fix(code-queue): harden codex submit prompt input

Host commander merge after read-only audit and post-#72 mergeability refresh. PR #71 is CLEAN/MERGEABLE and limited to UniDesk Code Queue CLI prompt input hardening, help/check docs, and submit prompt contract test. It does not touch HWLAB business code.
This commit is contained in:
Lyon
2026-05-23 00:36:45 +08:00
committed by GitHub
parent 2a8e9a5cd3
commit 77b577a2cc
6 changed files with 184 additions and 4 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ CLI 可以从 `master` 快速演进,但必须兼容 `deploy.json` 固定的 CI
- `ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs` 管理 D601 原生 k3s 上的 Tekton CI。`run` 手动创建每 commit 检查和 Code Queue 只读性能门禁;`publish-backend-core``publish-user-service` 从 pushed Git commit 构建并发布 `127.0.0.1:5000/unidesk/<service>:<commit>` commit-pinned artifacts,输出 `artifactSummary`(含 `serviceId``sourceCommit``sourceRepo``dockerfile``imageRef``tag``digest``digestRef`),但不部署生产;`run-dev-e2e` 的 Git 控制 runner、短 launcher、host fetch 边界、临时 smoke namespace 和 no-CD 规则只在 `docs/reference/dev-ci-runner.md` 定义;Tekton CI 通用规则见 `docs/reference/ci.md`
- `schedule list|get|runs|run|retry-run|delete|upsert-pgdata-backup` 管理 backend-core 定时任务和运行历史。`schedule list``schedule get``schedule runs --limit N``schedule runs <scheduleId> --limit N` 是只读观察入口;`schedule run``schedule retry-run``schedule delete``schedule upsert-pgdata-backup` 会触发运行或写入配置,生产恢复时必须有明确授权。`schedule runs --limit N` 是全局历史视图,返回 `scope=global``scheduleId=null``schedule runs <scheduleId> --limit N` 是指定 schedule 历史视图,返回 `scope=schedule` 和对应 `scheduleId`。CLI 必须拒绝 `schedule runs 50` 这类纯数字位置参数,并提示使用 `schedule runs --limit 50`,避免把空数组误判成“没有历史 run”。`schedule run <id> --wait-ms N` 触发同一 schedule,并且即使 wait 超时也必须返回 `newRunId``observeCommand``schedule retry-run <failedRunId>` 只接受 failed run,从原 run 反查 `scheduleId` 后重触发同一 schedule,并输出 `originalRunId``scheduleId``newRunId``observeCommand`。当 backend-core 目标容器缺失或只观察到 verify-only 容器时,schedule/microservice 命令必须以非零退出并返回 `failureKind=target-stack-not-running``runnerDisposition=infra-blocked``readOnlyCommands``authorizationRequiredForRecovery`,不得把 Docker 的 `No such container` 当成成功的空历史。
- `codex deploy <commitId>` 是旧 Code Queue 兼容部署入口,已禁用以防止维护通道直连 D601 部署 Code Queue;当前 dev 自动化只做 `ci run-dev-e2e` smoke,不提供 Code Queue CD,详细规则见 `docs/reference/codex-deploy.md`
- `codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]` 通过 backend-core 私有代理向稳定 `code-queue` 用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,`--dry-run` 只返回结构化请求且不实际入队。dry-run 会额外输出 `routingRecommendation`,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/运行态或 release 禁止/证据要求/中等复杂度候选等 guard 状态;同时输出 `policyContract`,固定暴露 GPT-5.5、DeepSeek、MiniMax 的风险分层、并发上限和外部 provider 429 退避处置。该建议只用于指挥官 preflight,不会改写 payload,不改变 runtime admission,也不假设生产 MiniMax 或 DeepSeek 可用。提交确认和 dry-run 必须返回完整 prompt、字符数和 `truncated=false`,不能套用任务详情的预览截断策略,否则长任务 prompt 无法被人工验收。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或 `code-queue-mgr` 控制面打抖;返回值会附带 `submitConcurrencyGuard` 说明本次提交的锁与等待信息。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQLD601 scheduler 只轮询并执行已入库任务。
- `codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]` 通过 backend-core 私有代理向稳定 `code-queue` 用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,`--dry-run` 只返回结构化请求且不实际入队。长 prompt、多行 prompt、含引号/反引号/Markdown 表格/JSON/反斜杠的 prompt 必须优先用 `--prompt-stdin``--prompt-file`,不要拼进 shell 单个参数;位置参数只适合短单行 smoke prompt。stdin 推荐用 quoted heredoc`cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue <id> --dry-run`,文件路径推荐 `bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run`,确认 dry-run 后移除 `--dry-run` 提交同一 payload。dry-run 会额外输出 `routingRecommendation`,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/运行态或 release 禁止/证据要求/中等复杂度候选等 guard 状态;同时输出 `policyContract`,固定暴露 GPT-5.5、DeepSeek、MiniMax 的风险分层、并发上限和外部 provider 429 退避处置。该建议只用于指挥官 preflight,不会改写 payload,不改变 runtime admission,也不假设生产 MiniMax 或 DeepSeek 可用。提交确认和 dry-run 必须返回完整 prompt、字符数和 `truncated=false`,不能套用任务详情的预览截断策略,否则长任务 prompt 无法被人工验收。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或 `code-queue-mgr` 控制面打抖;返回值会附带 `submitConcurrencyGuard` 说明本次提交的锁与等待信息。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQLD601 scheduler 只轮询并执行已入库任务。
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、Auth Broker source/capability/nextAction、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测、可选 push dry-run,以及可选 PR body/create dry-run guard;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。当 auth-broker 配置存在时,`tokenCoverage.source="auth-broker"``credentialSource="broker-issued-token"` 且 runner env token 不是成功前提;当仅 env token 存在时,`credentialSource="env-token"``preflight.authBroker.nextAction="use-env-token-until-auth-broker-live"`;两者都缺失时顶层 `ok=false``runnerDisposition=infra-blocked``degradedReason=auth-broker-needed``tokenCoverage.missing` 同时列出 `GH_TOKEN``GITHUB_TOKEN`,并输出 `preflight.authBroker.source="broker/auth-broker-needed"``capability.source="missing-token"`。该 `auth-missing` 的 scope 是 `scheduler-runner-env`,不能简化成“当前 active runner/dev container 不能创建 PR”;输出必须带 `scopeBoundary``activeRunnerDevContainer`,要求调用方另跑 `bun scripts/cli.ts gh auth status --repo pikasTech/unidesk` 与 PR dry-run 来确认当前 dev container 能力。`preflight.prCapabilityContract` 是 runner-facing 合同摘要,必须包含目标分支、token/auth 来源、`systemGhBinaryRequiredForWrites=false`、UniDesk REST `bun scripts/cli.ts gh` 可用性、push dry-run/PR create dry-run 的 `writesRemote=false`、expected PR handoff、真实 PR 创建需要 commander 授权和 `gh pr merge``unsupported-command` 边界;系统 `gh` binary 缺失只进入 `tools.systemGhBinary`,不得误判为 UniDesk REST `gh` CLI 不可用。`--remote` 在 runner-like 环境里不再依赖本地 `unidesk-backend-core``unidesk-database``baidu-netdisk-backend` 容器存在;这些缺失只作为本地观测证据。若远程控制面可达,则继续走远程控制面结果;若远程控制面不可达,则结构化返回 `failureKind=control-plane-missing` / `degradedReason=remote-control-plane-unreachable`,而不是把本地 `backend-core-container-missing` 当作最终阻塞。`--pr-create-dry-run` 不 POST GitHub,只证明 runner 内 PR body 生成、`scripts/cli.ts gh pr create --dry-run` 和 branch 参数形态可用;服务端创建权限仍以 token/auth broker、repo/issue/PR read、push dry-run 和最终授权后的真实 PR 创建结果为准。
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化审阅摘要;默认只返回任务身份、执行 Provider、工作目录、attempt 计数、原始 prompt、最终 response、最后错误和渐进披露命令,适合指挥官审阅完成未读任务且避免上下文爆炸。需要旧式详细摘要时显式加 `--detail`;需要完整 prompt/response 文本时加 `--full`;需要工具调用、judge、attempt 全量摘要时使用 `--detail --full --tool-limit N`。该摘要读取默认由主 server `code-queue-mgr` 从 PostgreSQL 返回,不依赖 D601 `code-queue-read` Service 可用。
- `codex tasks [--view supervisor|full] [--queue id] [--status succeeded|running|queued|failed|canceled|judging|retry_wait[,..]] [--unread|--unread-only] [--limit N] [--before-id id]` 通过同一私有代理输出渐进式披露视图。默认 `supervisor` 是低噪声指挥官视图,只返回 `running``completedUnread``recentCompleted``queued``executionDiagnostics` 的紧凑行;prompt/body 只给短预览和原始字符数,`recentCompleted` 默认最多返回 5 条且不重复 `completedUnread` 未读终态,不嵌入完整 Trace、final response 或全量 overview。每个条目只保留 `commands.show/detail/trace/output/full/read` 作为精确展开入口,并带 `classification` 标记直接推进、部署修复、验证/报告噪声等类别,帮助指挥官按 #131 聚焦真实推进而不是被 Gate/报告/审查任务牵引。`--unread``--unread-only` 的别名,必须只保留未读终态;`--status` 必须真实过滤支持的状态,未知参数或未知状态必须结构化失败,不能静默忽略。需要更详细当前页任务行时显式使用 `--view full``--full`,仍受 `--limit``--before-id` 分页约束。
+1 -1
View File
@@ -45,7 +45,7 @@
Code Queue 派单模型按成本、可信度和 blast radius 分层:GPT-5.5/Codex 处理高风险和复杂任务,DeepSeek/OpenCode 处理中等复杂度且边界清晰的任务,MiniMax/OpenCode 处理简单、低权限、可复核任务,生产重启、密钥、数据库手工写入和运行中任务控制保留给指挥官或人工。
当前提交合同由 `bun scripts/cli.ts codex submit` 暴露:prompt 必须来自位置参数、`--prompt-file``--prompt-stdin`;可选字段包括 `--queue/--queue-id``--provider-id/--provider``--cwd/--workdir``--model``--reasoning-effort``--execution-mode/--mode``--max-attempts``--reference-task-id/--reference/--ref`。这些字段写入任务 payload 后由 `code-queue-mgr` 入 PostgreSQL,核心任务字段包括 `queue_id``provider_id``execution_mode``model``cwd``prompt/base_prompt``reference_task_ids``reasoning_effort``max_attempts``task_json`;队列记录至少有 `id/name/created_at/updated_at`。模型治理应优先看任务 payload 和数据库字段,不靠 worker final response 自报。
当前提交合同由 `bun scripts/cli.ts codex submit` 暴露:prompt 必须来自位置参数、`--prompt-file``--prompt-stdin`;可选字段包括 `--queue/--queue-id``--provider-id/--provider``--cwd/--workdir``--model``--reasoning-effort``--execution-mode/--mode``--max-attempts``--reference-task-id/--reference/--ref`长 prompt、多行 prompt、含引号/反引号/Markdown 表格/JSON/反斜杠的 prompt 应使用 `--prompt-stdin``--prompt-file`,例如 `cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue <id> --dry-run``bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run`;位置参数只适合短单行 smoke prompt。提交前先用 `--dry-run` 检查完整 payload,确认后移除 `--dry-run`这些字段写入任务 payload 后由 `code-queue-mgr` 入 PostgreSQL,核心任务字段包括 `queue_id``provider_id``execution_mode``model``cwd``prompt/base_prompt``reference_task_ids``reasoning_effort``max_attempts``task_json`;队列记录至少有 `id/name/created_at/updated_at`。模型治理应优先看任务 payload 和数据库字段,不靠 worker final response 自报。
运行态默认模型仍是 `gpt-5.5``CODE_QUEUE_MODELS` 当前长期合同至少包含 GPT-5.5、GPT-5.4、GPT-5.4 Mini、DeepSeek Chat 和 MiniMax M2.7`deepseek`/`deepseek-chat``minimax-m2.7` 会走 OpenCode port,其余模型走 Codex port。只有当执行面 `/health` 或等价配置已经显示 DeepSeek 模型可用、并完成轻量 runner smoke 后,才允许真实提交 `--model deepseek-chat`
+37 -1
View File
@@ -30,11 +30,47 @@ const commandName = displayCommandName(args);
function displayCommandName(parts: string[]): string {
if (parts.length === 0) return "help";
if (parts[0] === "codex" && (parts[1] === "submit" || parts[1] === "enqueue")) {
const shown = ["codex", parts[1]];
const shownValueOptions = new Set([
"--prompt-file",
"--file",
"--queue",
"--queue-id",
"--provider",
"--provider-id",
"--cwd",
"--workdir",
"--model",
"--reasoning-effort",
"--execution-mode",
"--mode",
"--max-attempts",
"--reference-task-id",
"--reference",
"--ref",
]);
const hasPromptFile = parts.includes("--prompt-file") || parts.includes("--file");
const hasPromptStdin = parts.includes("--prompt-stdin") || parts.includes("--stdin");
const hasHelp = parts.slice(2).some(isHelpToken);
if (!hasPromptFile && !hasPromptStdin && !hasHelp) shown.push("<prompt:redacted>");
for (let index = 2; index < parts.length; index += 1) {
const part = parts[index] ?? "";
if (!part.startsWith("--")) continue;
shown.push(part);
if (shownValueOptions.has(part)) {
shown.push(parts[index + 1] ?? "<missing>");
index += 1;
}
}
return shown.join(" ");
}
if (parts[0] === "codex" && parts[1] === "steer" && parts[2] !== undefined) {
const shown = ["codex", "steer", parts[2]];
const hasPromptFile = parts.includes("--prompt-file") || parts.includes("--file");
const hasPromptStdin = parts.includes("--prompt-stdin") || parts.includes("--stdin");
if (!hasPromptFile && !hasPromptStdin) shown.push("<prompt:redacted>");
const hasHelp = parts.slice(3).some(isHelpToken);
if (!hasPromptFile && !hasPromptStdin && !hasHelp) shown.push("<prompt:redacted>");
for (let index = 3; index < parts.length; index += 1) {
const part = parts[index] ?? "";
if (!part.startsWith("--")) continue;
@@ -0,0 +1,123 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
type JsonRecord = Record<string, unknown>;
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
function runCli(args: string[], stdin?: string): { status: number | null; stdout: string; stderr: string; json: JsonRecord | null } {
const result = spawnSync("bun", ["scripts/cli.ts", ...args], {
cwd: process.cwd(),
input: stdin,
encoding: "utf8",
});
const stdout = String(result.stdout || "");
let json: JsonRecord | null = null;
try {
json = JSON.parse(stdout) as JsonRecord;
} catch {
json = null;
}
return {
status: result.status,
stdout,
stderr: String(result.stderr || ""),
json,
};
}
function nestedRecord(value: unknown, path: string[]): JsonRecord {
let current: unknown = value;
for (const key of path) {
assertCondition(current !== null && typeof current === "object" && !Array.isArray(current), "expected object while traversing JSON", { path, key, current });
current = (current as JsonRecord)[key];
}
assertCondition(current !== null && typeof current === "object" && !Array.isArray(current), "expected nested object", { path, current });
return current as JsonRecord;
}
function stringArray(value: unknown): string[] {
return Array.isArray(value) ? value.map((item) => String(item)) : [];
}
function assertDryRunPrompt(response: JsonRecord, expectedText: string): void {
assertCondition(response.ok === true, "submit dry-run should succeed", response);
const data = nestedRecord(response.data, []);
assertCondition(data.dryRun === true, "submit dry-run should expose dryRun=true", data);
const request = nestedRecord(response.data, ["request"]);
const prompt = nestedRecord(request, ["prompt"]);
assertCondition(prompt.text === expectedText, "submit dry-run prompt text mismatch", prompt);
assertCondition(prompt.chars === expectedText.length, "submit dry-run prompt char count mismatch", prompt);
assertCondition(prompt.truncated === false, "submit dry-run prompt must expose the full prompt", prompt);
}
export function runCodeQueueCliSubmitPromptContract(): JsonRecord {
const multilinePrompt = [
"Goal: verify stdin prompt path",
"JSON: {\"quote\":\"'single' and \\\"double\\\"\"}",
"Markdown: `code` | table | value",
"Backslash: C:\\tmp\\prompt",
"",
].join("\n");
const stdin = runCli(["codex", "submit", "--prompt-stdin", "--queue", "prompt-contract", "--dry-run"], multilinePrompt);
assertDryRunPrompt(stdin.json ?? {}, multilinePrompt);
assertCondition(String(stdin.json?.command || "") === "codex submit --prompt-stdin --queue prompt-contract --dry-run", "stdin command should list flags without echoing prompt", stdin.json ?? {});
const tmp = mkdtempSync(join(tmpdir(), "unidesk-code-queue-submit-"));
const promptFile = join(tmp, "prompt.md");
const filePrompt = `${multilinePrompt}file prompt tail\n`;
writeFileSync(promptFile, filePrompt, "utf8");
try {
const fromFile = runCli(["codex", "submit", "--prompt-file", promptFile, "--queue", "prompt-contract", "--dry-run"]);
assertDryRunPrompt(fromFile.json ?? {}, filePrompt);
assertCondition(String(fromFile.json?.command || "").includes(`--prompt-file ${promptFile}`), "prompt-file command should retain file path for review", fromFile.json ?? {});
assertCondition(!String(fromFile.json?.command || "").includes("file prompt tail"), "prompt-file command must not echo file prompt text", fromFile.json ?? {});
} finally {
rmSync(tmp, { recursive: true, force: true });
}
const positional = runCli(["codex", "submit", "short smoke prompt", "--dry-run"]);
assertDryRunPrompt(positional.json ?? {}, "short smoke prompt");
assertCondition(String(positional.json?.command || "").includes("<prompt:redacted>"), "outer command should redact positional submit prompt", positional.json ?? {});
assertCondition(!String(positional.json?.command || "").includes("short smoke prompt"), "outer command must not echo positional submit prompt", positional.json ?? {});
const duplicateSource = runCli(["codex", "submit", "positional", "--prompt-stdin", "--dry-run"], "stdin\n");
assertCondition(duplicateSource.status !== 0, "duplicate prompt source should fail", duplicateSource.json ?? { stdout: duplicateSource.stdout });
const duplicateMessage = String(nestedRecord(duplicateSource.json, ["error"]).message || "");
assertCondition(duplicateMessage.includes("exactly one prompt source"), "duplicate prompt source error should be explicit", { duplicateMessage });
const help = runCli(["codex", "submit", "--help"]);
assertCondition(help.status === 0 && help.json?.ok === true, "codex submit help should succeed", help.json ?? { stdout: help.stdout });
const data = nestedRecord(help.json?.data, []);
const usage = stringArray(data.usage);
const promptInput = nestedRecord(data, ["promptInput"]);
const recommended = stringArray(promptInput.recommended);
const examples = nestedRecord(data, ["examples"]);
assertCondition(usage.some((line) => line.includes("--prompt-stdin")), "help usage should include --prompt-stdin", { usage });
assertCondition(usage.some((line) => line.includes("--prompt-file")), "help usage should include --prompt-file", { usage });
assertCondition(usage.some((line) => line.includes("cat <<'PROMPT'")), "help usage should include a quoted heredoc example", { usage });
assertCondition(recommended.includes("--prompt-stdin") && recommended.includes("--prompt-file"), "help should recommend stdin and file prompt sources", promptInput);
assertCondition(String(promptInput.sourceRule || "").includes("Exactly one prompt source"), "help should document exact prompt source rule", promptInput);
assertCondition(stringArray(examples.stdin).some((line) => line.includes("--prompt-stdin")), "help examples should include stdin command", examples);
assertCondition(String(examples.file || "").includes("--prompt-file"), "help examples should include file command", examples);
return {
ok: true,
checks: [
"submit --prompt-stdin preserves multiline quotes and newlines",
"submit --prompt-file preserves reviewed file contents",
"submit positional prompt is redacted from the outer command envelope",
"duplicate submit prompt source fails explicitly",
"codex submit help documents stdin/file recommendations and copyable examples",
],
};
}
if (import.meta.main) {
process.stdout.write(`${JSON.stringify(runCodeQueueCliSubmitPromptContract(), null, 2)}\n`);
}
+3
View File
@@ -30,6 +30,7 @@ const syntaxFiles = [
"scripts/host-codex-commander-no-daemon-smoke-contract-test.ts",
"scripts/host-codex-commander-skeleton-contract-test.ts",
"scripts/auth-broker-contract-test.ts",
"scripts/code-queue-cli-submit-prompt-contract-test.ts",
"scripts/code-queue-supervisor-disclosure-contract-test.ts",
"src/components/frontend/src/index.ts",
"src/components/frontend/src/app.tsx",
@@ -341,6 +342,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(commandItem("code-queue:trace-summary-contract", ["bun", "scripts/code-queue-trace-summary-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:pr-preflight-contract", ["bun", "scripts/code-queue-pr-preflight-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:runner-skills-contract", ["bun", "scripts/code-queue-runner-skills-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:submit-prompt-contract", ["bun", "scripts/code-queue-cli-submit-prompt-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:submit-routing-contract", ["bun", "scripts/code-queue-submit-routing-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:supervisor-disclosure-contract", ["bun", "scripts/code-queue-supervisor-disclosure-contract-test.ts"], 30_000));
items.push(commandItem("host-codex-commander:skeleton-contract", ["bun", "scripts/host-codex-commander-skeleton-contract-test.ts"], 30_000));
@@ -367,6 +369,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(skippedItem("code-queue:trace-summary-contract", "Code Queue trace summary contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:pr-preflight-contract", "Code Queue PR preflight contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:runner-skills-contract", "Code Queue runner skill availability contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:submit-prompt-contract", "Code Queue submit prompt contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:submit-routing-contract", "Code Queue submit routing contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:supervisor-disclosure-contract", "Code Queue supervisor disclosure contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("host-codex-commander:skeleton-contract", "host Codex commander skeleton contract is opt-in with script checks", "--scripts-typecheck or --full"));
+19 -1
View File
@@ -248,7 +248,9 @@ function codexHelp(): unknown {
output: "json",
usage: [
"bun scripts/cli.ts codex deploy <commitId> # disabled legacy deployment entry",
"bun scripts/cli.ts codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue id] [--dry-run]",
"bun scripts/cli.ts codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue id] [--model model] [--dry-run]",
"cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue <id> --dry-run",
"bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run",
"bun scripts/cli.ts codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]",
"bun scripts/cli.ts codex tasks [--view supervisor|full] [--queue id] [--status succeeded,running] [--unread|--unread-only] [--limit N] [--before-id id]",
"bun scripts/cli.ts codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]",
@@ -260,6 +262,22 @@ function codexHelp(): unknown {
"bun scripts/cli.ts codex interrupt|cancel <taskId>",
"bun scripts/cli.ts codex queues [--full|--all] | queue create <queueId> | queue merge <sourceQueueId> --into <targetQueueId> | move <taskId> --queue <queueId>",
],
promptInput: {
recommended: ["--prompt-stdin", "--prompt-file"],
stdin: "Use a quoted heredoc or pipe when the prompt contains newlines, quotes, backticks, JSON, Markdown tables, or shell-sensitive text.",
file: "Use --prompt-file for reviewed or reusable dispatch prompts; --file is an alias.",
positional: "Keep positional prompt usage to short one-line smoke prompts only.",
sourceRule: "Exactly one prompt source is accepted: positional prompt, --prompt-file, or --prompt-stdin.",
},
examples: {
stdin: [
"cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue <id> --dry-run",
"<multi-line prompt body>",
"PROMPT",
],
file: "bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run",
dryRunThenSubmit: "Run with --dry-run first; remove --dry-run to submit exactly the same payload.",
},
description: "Operate Code Queue through the stable backend-core private proxy path.",
};
}