feat: 实现 Queue Q2 受控 dispatch

This commit is contained in:
Codex
2026-06-01 22:44:27 +08:00
parent db886617dc
commit b19143ad85
9 changed files with 337 additions and 3 deletions
+27
View File
@@ -40,6 +40,8 @@ async function dispatch(args: ParsedArgs): Promise<JsonValue> {
if (group === "queue" && command === "commander") return client(args).get(`/api/v1/queue/commander${queueQuery(args)}`);
if (group === "queue" && command === "read" && id) return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(id)}/read`, { readerId: optionalFlag(args, "reader-id") ?? "cli" });
if (group === "queue" && command === "cancel" && id) return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(id)}/cancel`, cancelBody(args));
if (group === "queue" && command === "dispatch" && id) return dispatchQueueTask(args, id);
if (group === "queue" && command === "refresh" && id) return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(id)}/refresh`, {});
if (group === "runs" && command === "create") return client(args).post("/api/v1/runs", await jsonFile(args));
if (group === "runs" && command === "show" && id) return client(args).get(`/api/v1/runs/${encodeURIComponent(id)}`);
if (group === "runs" && command === "events" && id) return client(args).get(`/api/v1/runs/${encodeURIComponent(id)}/events?afterSeq=${flag(args, "after-seq", "0")}&limit=${flag(args, "limit", "100")}`);
@@ -132,6 +134,23 @@ function queueQuery(args: ParsedArgs): string {
return queue ? `?queue=${encodeURIComponent(queue)}` : "";
}
async function dispatchQueueTask(args: ParsedArgs, taskId: string): Promise<JsonValue> {
const body = await optionalJsonFile(args);
const copy = (flagName: string, key = flagName.replace(/-([a-z])/gu, (_, letter: string) => letter.toUpperCase())): void => {
const value = optionalFlag(args, flagName);
if (value) body[key] = value;
};
copy("idempotency-key", "idempotencyKey");
copy("image");
copy("namespace");
copy("attempt-id", "attemptId");
copy("runner-id", "runnerId");
copy("source-commit", "sourceCommit");
copy("runner-manager-url", "managerUrl");
copy("service-account-name", "serviceAccountName");
return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(taskId)}/dispatch`, body);
}
async function showRunnerJobStatus(args: ParsedArgs): Promise<JsonValue> {
const runId = flag(args, "run-id", "");
if (!runId) throw new AgentRunError("schema-invalid", "runner job-status requires --run-id", { httpStatus: 2 });
@@ -228,6 +247,12 @@ async function jsonFile(args: ParsedArgs): Promise<JsonRecord> {
throw new AgentRunError("schema-invalid", "json file must contain an object", { httpStatus: 2 });
}
async function optionalJsonFile(args: ParsedArgs): Promise<JsonRecord> {
const file = optionalFlag(args, "json-file");
if (!file) return {};
return jsonFile(args);
}
function parseArgs(argv: string[]): ParsedArgs {
const positional: string[] = [];
const flags = new Map<string, string | boolean>();
@@ -287,6 +312,8 @@ function help(): JsonRecord {
"queue commander [--queue <queue>]",
"queue read <taskId> [--reader-id <reader>]",
"queue cancel <taskId> [--reason <text>]",
"queue dispatch <taskId> [--json-file <dispatch.json>] [--idempotency-key <key>] [--image <image>] [--namespace <namespace>]",
"queue refresh <taskId>",
"secrets codex render --dry-run [--profile codex|deepseek] [--codex-home <dir>] [--namespace agentrun-v01] [--secret-name <name>]",
"backends list",
"server start|status",