refactor: unify GATEWAY_SECRET + DASHBOARD_API_KEY into WORKFLOW_DASHBOARD_SECRET
This commit is contained in:
+5
-2
@@ -14,7 +14,10 @@ WORKFLOW_CURSOR_MODEL=
|
|||||||
# Timeout in milliseconds for Cursor agent operations
|
# Timeout in milliseconds for Cursor agent operations
|
||||||
WORKFLOW_CURSOR_TIMEOUT=
|
WORKFLOW_CURSOR_TIMEOUT=
|
||||||
|
|
||||||
# ── Hermes Agent (used by workflow-template-solve-issue) ──
|
# ── Hermes Agent (used by develop tester/committer + solve-issue) ──
|
||||||
|
|
||||||
|
# CLI command to invoke the Hermes agent (absolute path required)
|
||||||
|
WORKFLOW_HERMES_COMMAND=
|
||||||
|
|
||||||
# Model override for Hermes agent
|
# Model override for Hermes agent
|
||||||
WORKFLOW_HERMES_MODEL=
|
WORKFLOW_HERMES_MODEL=
|
||||||
@@ -29,7 +32,7 @@ WORKFLOW_HERMES_TIMEOUT=
|
|||||||
WORKFLOW_STORAGE_ROOT=
|
WORKFLOW_STORAGE_ROOT=
|
||||||
|
|
||||||
# Gateway secret for the serve command
|
# Gateway secret for the serve command
|
||||||
WORKFLOW_GATEWAY_SECRET=
|
WORKFLOW_DASHBOARD_SECRET=
|
||||||
|
|
||||||
# ── Display ──
|
# ── Display ──
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function requireNextArg(argv: string[], i: number, flag: string): Result<string,
|
|||||||
function parseConnectArgv(argv: string[]): Result<ConnectOptions, string> {
|
function parseConnectArgv(argv: string[]): Result<ConnectOptions, string> {
|
||||||
let name = osHostname().split(".")[0].toLowerCase();
|
let name = osHostname().split(".")[0].toLowerCase();
|
||||||
let gatewayUrl = DEFAULT_GATEWAY_URL;
|
let gatewayUrl = DEFAULT_GATEWAY_URL;
|
||||||
const gatewaySecret = process.env.WORKFLOW_GATEWAY_SECRET ?? "";
|
const gatewaySecret = process.env.WORKFLOW_DASHBOARD_SECRET ?? "";
|
||||||
const stringFlags: Record<string, (v: string) => void> = {
|
const stringFlags: Record<string, (v: string) => void> = {
|
||||||
"--name": (v) => {
|
"--name": (v) => {
|
||||||
name = v;
|
name = v;
|
||||||
@@ -56,7 +56,7 @@ export async function dispatchConnect(storageRoot: string, argv: string[]): Prom
|
|||||||
const options = parsed.value;
|
const options = parsed.value;
|
||||||
|
|
||||||
if (options.gatewaySecret === "") {
|
if (options.gatewaySecret === "") {
|
||||||
printCliLine("error: WORKFLOW_GATEWAY_SECRET is required");
|
printCliLine("error: WORKFLOW_DASHBOARD_SECRET is required");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DurableObject } from "cloudflare:workers";
|
|||||||
import { parseWsRequestJson, parseWsResponseJson, type WsResponse } from "./ws-protocol.js";
|
import { parseWsRequestJson, parseWsResponseJson, type WsResponse } from "./ws-protocol.js";
|
||||||
|
|
||||||
type ClientSocketEnv = {
|
type ClientSocketEnv = {
|
||||||
GATEWAY_SECRET: string;
|
WORKFLOW_DASHBOARD_SECRET: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CLIENT_SOCKET_INTERNAL_STATUS_PATH = "/internal/client-socket/status";
|
export const CLIENT_SOCKET_INTERNAL_STATUS_PATH = "/internal/client-socket/status";
|
||||||
@@ -37,7 +37,7 @@ export class ClientSocket extends DurableObject<ClientSocketEnv> {
|
|||||||
|
|
||||||
private requireAuth(request: Request): Response | null {
|
private requireAuth(request: Request): Response | null {
|
||||||
const auth = request.headers.get("Authorization");
|
const auth = request.headers.get("Authorization");
|
||||||
if (auth !== `Bearer ${this.env.GATEWAY_SECRET}`) {
|
if (auth !== `Bearer ${this.env.WORKFLOW_DASHBOARD_SECRET}`) {
|
||||||
return jsonResponse(401, { error: "unauthorized" });
|
return jsonResponse(401, { error: "unauthorized" });
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ export { ClientSocket };
|
|||||||
type Env = {
|
type Env = {
|
||||||
Bindings: {
|
Bindings: {
|
||||||
ENDPOINTS: KVNamespace;
|
ENDPOINTS: KVNamespace;
|
||||||
GATEWAY_SECRET: string;
|
WORKFLOW_DASHBOARD_SECRET: string;
|
||||||
DASHBOARD_API_KEY: string;
|
|
||||||
CLIENT_SOCKET: DurableObjectNamespace<ClientSocket>;
|
CLIENT_SOCKET: DurableObjectNamespace<ClientSocket>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -40,7 +39,7 @@ function checkDashboardAuth(c: {
|
|||||||
const bearer = c.req.header("Authorization")?.replace("Bearer ", "");
|
const bearer = c.req.header("Authorization")?.replace("Bearer ", "");
|
||||||
const query = c.req.query("key");
|
const query = c.req.query("key");
|
||||||
const key = bearer ?? query;
|
const key = bearer ?? query;
|
||||||
return key === c.env.DASHBOARD_API_KEY;
|
return key === c.env.WORKFLOW_DASHBOARD_SECRET;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLocalClientUrl(url: string): boolean {
|
function isLocalClientUrl(url: string): boolean {
|
||||||
@@ -153,7 +152,7 @@ async function fetchClientSocketStatus(
|
|||||||
const resp = await stub.fetch(
|
const resp = await stub.fetch(
|
||||||
new Request(`https://do${CLIENT_SOCKET_INTERNAL_STATUS_PATH}`, {
|
new Request(`https://do${CLIENT_SOCKET_INTERNAL_STATUS_PATH}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { Authorization: `Bearer ${env.GATEWAY_SECRET}` },
|
headers: { Authorization: `Bearer ${env.WORKFLOW_DASHBOARD_SECRET}` },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
@@ -184,14 +183,14 @@ function endpointStatusFromKvAndDo(record: EndpointRecord, doConnected: boolean
|
|||||||
// ── Health ──────────────────────────────────────────────────────────
|
// ── Health ──────────────────────────────────────────────────────────
|
||||||
app.get("/healthz", (c) => c.json({ ok: true }));
|
app.get("/healthz", (c) => c.json({ ok: true }));
|
||||||
|
|
||||||
// ── Client reverse WebSocket (GATEWAY_SECRET query param) ────────────
|
// ── Client reverse WebSocket (WORKFLOW_DASHBOARD_SECRET query param) ────────────
|
||||||
app.get("/ws/connect", async (c) => {
|
app.get("/ws/connect", async (c) => {
|
||||||
const secret = c.req.query("secret");
|
const secret = c.req.query("secret");
|
||||||
const name = c.req.query("name");
|
const name = c.req.query("name");
|
||||||
if (name === undefined || name === "") {
|
if (name === undefined || name === "") {
|
||||||
return c.json({ error: "name required" }, 400);
|
return c.json({ error: "name required" }, 400);
|
||||||
}
|
}
|
||||||
if (secret !== c.env.GATEWAY_SECRET) {
|
if (secret !== c.env.WORKFLOW_DASHBOARD_SECRET) {
|
||||||
return c.json({ error: "unauthorized" }, 401);
|
return c.json({ error: "unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
if (c.req.header("Upgrade") !== "websocket") {
|
if (c.req.header("Upgrade") !== "websocket") {
|
||||||
@@ -202,7 +201,7 @@ app.get("/ws/connect", async (c) => {
|
|||||||
return stub.fetch(c.req.raw);
|
return stub.fetch(c.req.raw);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Gateway management (GATEWAY_SECRET auth) ────────────────────────
|
// ── Gateway management (WORKFLOW_DASHBOARD_SECRET auth) ────────────────────────
|
||||||
const gateway = new Hono<Env>();
|
const gateway = new Hono<Env>();
|
||||||
|
|
||||||
gateway.post("/register", async (c) => {
|
gateway.post("/register", async (c) => {
|
||||||
@@ -217,7 +216,7 @@ gateway.post("/register", async (c) => {
|
|||||||
if (!name || !url) {
|
if (!name || !url) {
|
||||||
return c.json({ error: "name and url required" }, 400);
|
return c.json({ error: "name and url required" }, 400);
|
||||||
}
|
}
|
||||||
if (secret !== c.env.GATEWAY_SECRET) {
|
if (secret !== c.env.WORKFLOW_DASHBOARD_SECRET) {
|
||||||
return c.json({ error: "unauthorized" }, 401);
|
return c.json({ error: "unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +241,7 @@ gateway.post("/register", async (c) => {
|
|||||||
|
|
||||||
gateway.delete("/register/:name", async (c) => {
|
gateway.delete("/register/:name", async (c) => {
|
||||||
const auth = c.req.header("Authorization");
|
const auth = c.req.header("Authorization");
|
||||||
if (auth !== `Bearer ${c.env.GATEWAY_SECRET}`) {
|
if (auth !== `Bearer ${c.env.WORKFLOW_DASHBOARD_SECRET}`) {
|
||||||
return c.json({ error: "unauthorized" }, 401);
|
return c.json({ error: "unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +307,7 @@ app.all("/api/clients/:client/*", async (c) => {
|
|||||||
const proxyResp = await fetchThroughClientSocket(
|
const proxyResp = await fetchThroughClientSocket(
|
||||||
c.env,
|
c.env,
|
||||||
client,
|
client,
|
||||||
c.env.GATEWAY_SECRET,
|
c.env.WORKFLOW_DASHBOARD_SECRET,
|
||||||
wsRequest,
|
wsRequest,
|
||||||
);
|
);
|
||||||
if (proxyResp.status !== 503) {
|
if (proxyResp.status !== 503) {
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ new_sqlite_classes = ["AgentSocket"]
|
|||||||
tag = "rename-agent-to-client"
|
tag = "rename-agent-to-client"
|
||||||
renamed_classes = [{ from = "AgentSocket", to = "ClientSocket" }]
|
renamed_classes = [{ from = "AgentSocket", to = "ClientSocket" }]
|
||||||
|
|
||||||
# GATEWAY_SECRET is set via `wrangler secret put`
|
# WORKFLOW_DASHBOARD_SECRET is set via `wrangler secret put`
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
/**
|
/**
|
||||||
* develop bundle entry — 小橘 🍊
|
* develop bundle entry — 小橘 🍊
|
||||||
*
|
*
|
||||||
* All roles use cursor-agent with workspace auto-extracted from context.
|
* planner/coder/reviewer → cursor-agent (needs code editing)
|
||||||
|
* tester/committer → hermes-agent (lightweight, no editing needed)
|
||||||
*/
|
*/
|
||||||
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
|
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
|
||||||
|
import { createHermesAgent } from "@uncaged/workflow-agent-hermes";
|
||||||
import { createWorkflow } from "@uncaged/workflow-runtime";
|
import { createWorkflow } from "@uncaged/workflow-runtime";
|
||||||
import { optionalEnv, requireEnv } from "@uncaged/workflow-util";
|
import { optionalEnv, requireEnv } from "@uncaged/workflow-util";
|
||||||
import { buildDevelopDescriptor, developWorkflowDefinition } from "./src/index.js";
|
import { buildDevelopDescriptor, developWorkflowDefinition } from "./src/index.js";
|
||||||
|
|
||||||
const adapter = createCursorAgent({
|
const cursorAdapter = createCursorAgent({
|
||||||
command: requireEnv("WORKFLOW_CURSOR_COMMAND", "set WORKFLOW_CURSOR_COMMAND (e.g. cursor-agent)"),
|
command: requireEnv("WORKFLOW_CURSOR_COMMAND", "set WORKFLOW_CURSOR_COMMAND (e.g. cursor-agent)"),
|
||||||
model: optionalEnv("WORKFLOW_CURSOR_MODEL"),
|
model: optionalEnv("WORKFLOW_CURSOR_MODEL"),
|
||||||
timeout: optionalEnv("WORKFLOW_CURSOR_TIMEOUT")
|
timeout: optionalEnv("WORKFLOW_CURSOR_TIMEOUT")
|
||||||
@@ -17,7 +19,21 @@ const adapter = createCursorAgent({
|
|||||||
workspace: null,
|
workspace: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const wf = createWorkflow(developWorkflowDefinition, { adapter, overrides: null });
|
const hermesAdapter = createHermesAgent({
|
||||||
|
command: requireEnv("WORKFLOW_HERMES_COMMAND", "set WORKFLOW_HERMES_COMMAND (absolute path to hermes CLI)"),
|
||||||
|
model: optionalEnv("WORKFLOW_HERMES_MODEL"),
|
||||||
|
timeout: optionalEnv("WORKFLOW_HERMES_TIMEOUT")
|
||||||
|
? Number(optionalEnv("WORKFLOW_HERMES_TIMEOUT"))
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wf = createWorkflow(developWorkflowDefinition, {
|
||||||
|
adapter: cursorAdapter,
|
||||||
|
overrides: {
|
||||||
|
tester: hermesAdapter,
|
||||||
|
committer: hermesAdapter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const descriptor = buildDevelopDescriptor();
|
export const descriptor = buildDevelopDescriptor();
|
||||||
export const run = wf;
|
export const run = wf;
|
||||||
|
|||||||
Reference in New Issue
Block a user