fix: 配置上下文超限错误透传

This commit is contained in:
Codex
2026-07-14 20:40:10 +02:00
parent f3bbc6feaa
commit ae7093cc57
8 changed files with 372 additions and 3 deletions
@@ -86,6 +86,10 @@ bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --target PK01
bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete --target PK01 --account <name-or-id> --kind temp-unschedulable
bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --target PK01 --account <name-or-id> --kind model-mapping --model <client-model> --upstream-model <upstream-model>
bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete --target PK01 --account <name-or-id> --kind model-mapping --model <client-model>
bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough list --target PK01
bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough apply --target PK01 --template <template-id>
bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough apply --target PK01 --template <template-id> --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough delete --target PK01 --template <template-id> --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h
bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h --json
bun scripts/cli.ts platform-infra sub2api codex-pool profit --target PK01 --since 24h --accounts
@@ -98,6 +102,14 @@ bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-report --target D6
bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --target D601 --confirm
```
- `error-passthrough` 只管理 `runtime.errorPassthroughRules` 声明的官方全局错误透传规则:
- 使用 Sub2API 原生 `/api/v1/admin/error-passthrough-rules` CRUD,不修改 Sub2API 源码;
- 使用 YAML `id` 选择模板,使用唯一 `name` 与运行面规则对账;
- `apply` / `delete` 默认 dry-run,只有 `--confirm` 才写入,写后必须自动回读并显示 `synced` / `absent`
- 错误码与关键词需要同时约束时使用 `matchMode: all`,避免仅凭通用 400/502 扩大匹配;
- `passthroughBody: true` 可保留上游具体客户错误,`skipMonitoring: false` 保持原生运维事件可见;
- 规则是 Sub2API 全局事实,不按账号或分组复制,也不得用于把应当 failover 的真实非 auth 上游故障伪装成客户错误。
- `profit` 只读聚合 Sub2API 原生 `/api/v1/admin/usage`
- 默认窗口、盈利分组、自用分组、排除分组、人民币售价和账号采购成本来源由 `config/platform-infra/sub2api-codex-pool.yaml#profit` 声明;
- 原生 `actual_cost` 是标准 API 美元计费基数;`saleRateCnyPerApiUsd` 和账号名称末尾成本都表示每 1 美元标准 API 计费对应的人民币单价,不做汇率换算;
@@ -9,6 +9,21 @@ metadata:
- 340
runtime:
errorPassthroughRules:
- id: openai-context-window-client-error
name: UniDesk OpenAI context window client error
enabled: true
priority: 100
errorCodes: [400, 502]
keywords: [context_length_exceeded, input exceeds the context window]
matchMode: all
platforms: [openai]
passthroughCode: false
responseCode: 400
passthroughBody: true
customMessage: null
skipMonitoring: false
description: 将确定的 OpenAI 上下文超限错误作为客户请求错误返回,保留原始错误信息并继续记录运维事件。
templates:
- id: codex-upstream-failover
kind: temp-unschedulable
@@ -271,7 +271,59 @@ export function readRuntimeConfig(value: unknown): CodexRuntimeConfig {
if (templatesById[template.id] !== undefined) throw new Error(`${codexPoolConfigPath}.runtime.templates duplicates id ${template.id}`);
templatesById[template.id] = template;
}
return { templates, templatesById };
if (!Array.isArray(value.errorPassthroughRules)) {
throw new Error(`${codexPoolConfigPath}.runtime.errorPassthroughRules must be a YAML array`);
}
const errorPassthroughRules = value.errorPassthroughRules.map((item, index) => {
const path = `runtime.errorPassthroughRules[${index}]`;
if (!isRecord(item)) throw new Error(`${codexPoolConfigPath}.${path} must be a YAML object`);
const id = requiredStringConfigField(item, "id", path);
if (!/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u.test(id)) throw new Error(`${codexPoolConfigPath}.${path}.id has an unsupported format`);
const matchMode = requiredStringConfigField(item, "matchMode", path);
if (matchMode !== "any" && matchMode !== "all") throw new Error(`${codexPoolConfigPath}.${path}.matchMode must be any or all`);
const errorCodes = integerArrayConfigField(item, "errorCodes", path);
const keywords = readUniqueStringArray(item.keywords, `${path}.keywords`, true);
if (errorCodes.length === 0 && keywords.length === 0) throw new Error(`${codexPoolConfigPath}.${path} must declare errorCodes or keywords`);
const passthroughCode = item.passthroughCode;
const passthroughBody = item.passthroughBody;
const enabled = item.enabled;
const skipMonitoring = item.skipMonitoring;
if (typeof enabled !== "boolean") throw new Error(`${codexPoolConfigPath}.${path}.enabled must be boolean`);
if (typeof passthroughCode !== "boolean") throw new Error(`${codexPoolConfigPath}.${path}.passthroughCode must be boolean`);
if (typeof passthroughBody !== "boolean") throw new Error(`${codexPoolConfigPath}.${path}.passthroughBody must be boolean`);
if (typeof skipMonitoring !== "boolean") throw new Error(`${codexPoolConfigPath}.${path}.skipMonitoring must be boolean`);
const responseCode = item.responseCode === null ? null : integerConfigField(item, "responseCode", path);
if (!passthroughCode && (responseCode === null || responseCode < 100 || responseCode > 599)) {
throw new Error(`${codexPoolConfigPath}.${path}.responseCode must be an HTTP status when passthroughCode=false`);
}
const customMessage = item.customMessage === null || item.customMessage === undefined ? null : requiredStringConfigField(item, "customMessage", path);
if (!passthroughBody && customMessage === null) throw new Error(`${codexPoolConfigPath}.${path}.customMessage is required when passthroughBody=false`);
return {
id,
name: requiredStringConfigField(item, "name", path),
enabled,
priority: integerConfigField(item, "priority", path),
errorCodes,
keywords,
matchMode,
platforms: readUniqueStringArray(item.platforms, `${path}.platforms`, false),
passthroughCode,
responseCode,
passthroughBody,
customMessage,
skipMonitoring,
description: readRequiredDescription(item.description, `${path}.description`, 300),
};
});
const errorPassthroughRulesById: Record<string, (typeof errorPassthroughRules)[number]> = {};
const errorPassthroughRuleNames = new Set<string>();
for (const rule of errorPassthroughRules) {
if (errorPassthroughRulesById[rule.id] !== undefined) throw new Error(`${codexPoolConfigPath}.runtime.errorPassthroughRules duplicates id ${rule.id}`);
if (errorPassthroughRuleNames.has(rule.name)) throw new Error(`${codexPoolConfigPath}.runtime.errorPassthroughRules duplicates name ${rule.name}`);
errorPassthroughRulesById[rule.id] = rule;
errorPassthroughRuleNames.add(rule.name);
}
return { templates, templatesById, errorPassthroughRules, errorPassthroughRulesById };
}
export function readRuntimeTemplateRef(value: unknown, key: string, runtime: CodexRuntimeConfig): string {
@@ -0,0 +1,266 @@
import type { UniDeskConfig } from "../config";
import type { RenderedCliResult } from "../output";
import { readCodexPoolConfig } from "./config";
import { boolField, compactCapture, parseJsonOutput, runRemoteCodexPoolScript } from "./remote";
import { codexPoolRuntimeTarget, defaultCodexPoolRuntimeTargetId } from "./runtime-target";
import { codexPoolConfigPath, type CodexErrorPassthroughRule } from "./types";
type Action = "list" | "apply" | "delete";
interface Options {
action: Action;
template: string | null;
confirm: boolean;
json: boolean;
targetId: string;
}
export async function codexPoolErrorPassthrough(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
if (args.includes("--help")) return help();
const options = parseOptions(args);
const pool = readCodexPoolConfig();
const target = codexPoolRuntimeTarget(options.targetId);
const selected = options.template === null ? null : pool.runtime.errorPassthroughRulesById[options.template];
if (options.template !== null && selected === undefined) {
throw new Error(`unknown error-passthrough template ${options.template}; available: ${pool.runtime.errorPassthroughRules.map((item) => item.id).join(", ")}`);
}
const payload = {
action: options.action,
confirm: options.confirm,
selected: selected === null || selected === undefined ? null : apiRule(selected),
managed: pool.runtime.errorPassthroughRules.map((rule) => ({ id: rule.id, ...apiRule(rule) })),
};
const result = await runRemoteCodexPoolScript(config, "error-passthrough", remoteScript(payload, target), target);
const parsed = parseJsonOutput(result.stdout);
const ok = result.exitCode === 0 && boolField(parsed, "ok", false);
const response = {
ok,
action: `platform-infra-sub2api-codex-pool-error-passthrough-${options.action}`,
target: { id: target.id, route: target.route, runtimeMode: target.runtimeMode, endpoint: target.serviceDns },
source: { configPath: `${codexPoolConfigPath}#runtime.errorPassthroughRules`, api: "/api/v1/admin/error-passthrough-rules", official: true },
request: { template: options.template, confirm: options.confirm },
result: parsed,
remote: compactCapture(result, { full: parsed === null || result.exitCode !== 0 }),
valuesPrinted: false,
};
return options.json ? response : render(response);
}
function parseOptions(args: string[]): Options {
const [actionRaw = "list", ...rest] = args;
if (actionRaw !== "list" && actionRaw !== "apply" && actionRaw !== "delete") throw new Error("error-passthrough usage: list|apply|delete [--template <id>] [--confirm] [--target <id>] [--json]");
let template: string | null = null;
let confirm = false;
let json = false;
let targetId = defaultCodexPoolRuntimeTargetId();
for (let index = 0; index < rest.length; index += 1) {
const arg = rest[index]!;
const readValue = (name: string): string => {
const value = rest[index + 1];
if (!value || value.startsWith("--")) throw new Error(`${name} requires a value`);
index += 1;
return value;
};
if (arg === "--template") template = readValue("--template");
else if (arg.startsWith("--template=")) template = arg.slice("--template=".length);
else if (arg === "--target") targetId = readValue("--target");
else if (arg.startsWith("--target=")) targetId = arg.slice("--target=".length);
else if (arg === "--confirm") confirm = true;
else if (arg === "--json") json = true;
else throw new Error(`unsupported error-passthrough option: ${arg}`);
}
if ((actionRaw === "apply" || actionRaw === "delete") && template === null) throw new Error(`error-passthrough ${actionRaw} requires --template <id>`);
if (actionRaw === "list" && (template !== null || confirm)) throw new Error("error-passthrough list does not accept --template or --confirm");
return { action: actionRaw, template, confirm, json, targetId };
}
function apiRule(rule: CodexErrorPassthroughRule): Record<string, unknown> {
return {
name: rule.name,
enabled: rule.enabled,
priority: rule.priority,
error_codes: rule.errorCodes,
keywords: rule.keywords,
match_mode: rule.matchMode,
platforms: rule.platforms,
passthrough_code: rule.passthroughCode,
response_code: rule.responseCode,
passthrough_body: rule.passthroughBody,
custom_message: rule.customMessage,
skip_monitoring: rule.skipMonitoring,
description: rule.description,
};
}
function help(): RenderedCliResult {
return {
ok: true,
command: "platform-infra sub2api codex-pool error-passthrough --help",
renderedText: [
"SUB2API ERROR PASSTHROUGH",
"Usage:",
" bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough list [--target PK01] [--json]",
" bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough apply --template <id> [--target PK01] [--confirm] [--json]",
" bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough delete --template <id> [--target PK01] [--confirm] [--json]",
"Writes use the official Sub2API admin error-passthrough-rules API. Without --confirm, apply/delete are dry-run.",
].join("\n"),
contentType: "text/plain",
};
}
function render(response: Record<string, unknown>): RenderedCliResult {
const result = record(response.result);
const rows = Array.isArray(result.rules) ? result.rules.map(record) : [];
const lines = [
`SUB2API ERROR PASSTHROUGH ok=${response.ok === true ? "true" : "false"} mode=${text(result.mode)} mutation=${text(result.mutation)}`,
"TEMPLATE RULE_ID ENABLED MATCH ERROR_CODES RESPONSE PLATFORMS STATE",
];
for (const row of rows) {
lines.push([
text(row.templateId).padEnd(36),
text(row.ruleId).padEnd(7),
text(row.enabled).padEnd(7),
text(row.matchMode).padEnd(5),
text(row.errorCodes).padEnd(11),
text(row.responseCode).padEnd(8),
text(row.platforms).padEnd(9),
text(row.state),
].join(" ").trimEnd());
}
if (result.unmanagedCount !== undefined) lines.push(`UNMANAGED ${text(result.unmanagedCount)}`);
if (result.message) lines.push(`MESSAGE ${text(result.message)}`);
return { ok: response.ok === true, command: String(response.action), renderedText: lines.join("\n"), contentType: "text/plain", projection: response };
}
function record(value: unknown): Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
function text(value: unknown): string {
if (value === null || value === undefined || value === "") return "-";
if (Array.isArray(value)) return value.join(",");
if (typeof value === "boolean") return value ? "yes" : "no";
return String(value).replace(/[\r\n\t]+/gu, " ");
}
function remoteScript(payload: unknown, target: ReturnType<typeof codexPoolRuntimeTarget>): string {
const encoded = Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
return `set -u
python3 - <<'PY'
import base64, json, subprocess
from urllib.parse import urlencode
PAYLOAD = json.loads(base64.b64decode(${JSON.stringify(encoded)}).decode("utf-8"))
RUNTIME_MODE = ${JSON.stringify(target.runtimeMode)}
NAMESPACE = ${JSON.stringify(target.namespace)}
APP_SECRET_NAME = ${JSON.stringify(target.appSecretName)}
HOST_PORT = ${JSON.stringify(target.hostDockerAppPort)}
APP_POD = None
def run(cmd, body=None):
return subprocess.run(cmd, input=body, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def docker(args):
proc = run(["docker", *args])
return proc if proc.returncode == 0 else run(["sudo", "-n", "docker", *args])
def kubectl(args, body=None):
return run(["kubectl", *args], body)
def kube_json(args):
proc = kubectl([*args, "-o", "json"])
if proc.returncode != 0: raise RuntimeError("kubectl read failed")
return json.loads(proc.stdout.decode())
if RUNTIME_MODE != "host-docker":
pods = kube_json(["-n", NAMESPACE, "get", "pods", "-l", "app=sub2api"]).get("items") or []
running = [x for x in pods if x.get("status", {}).get("phase") == "Running"]
if not running: raise RuntimeError("sub2api app pod not found")
APP_POD = running[0]["metadata"]["name"]
def env_value(key, default=None):
if RUNTIME_MODE == "host-docker":
proc = docker(["inspect", "sub2api-app", "--format", "{{json .Config.Env}}"])
if proc.returncode != 0: return default
for item in json.loads(proc.stdout.decode()):
if item.startswith(key + "="): return item.split("=", 1)[1]
return default
cm = kube_json(["-n", NAMESPACE, "get", "configmap", "sub2api-config"]).get("data") or {}
return cm.get(key, default)
def secret_value(key):
if RUNTIME_MODE == "host-docker": return env_value(key)
data = kube_json(["-n", NAMESPACE, "get", "secret", APP_SECRET_NAME]).get("data") or {}
return base64.b64decode(data[key]).decode() if key in data else None
def api(method, path, token=None, payload=None):
body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode()
script = '''set -eu
method="$1"; url="$2"; token="$3"; tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT; cat >"$tmp"
args=""; [ -n "$token" ] && args="Authorization: Bearer $token"
if [ -n "$args" ] && [ -s "$tmp" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H "$args" -H 'Content-Type: application/json' --data-binary @"$tmp" "$url"
elif [ -n "$args" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H "$args" "$url"
elif [ -s "$tmp" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H 'Content-Type: application/json' --data-binary @"$tmp" "$url"
else curl -sS -w '\n__CODE__:%{http_code}' -X "$method" "$url"; fi'''
base = f"http://127.0.0.1:{HOST_PORT}" if RUNTIME_MODE == "host-docker" else "http://127.0.0.1:8080"
cmd = ["sh", "-c", script, "sh", method, base + path, token or ""]
proc = run(cmd, body) if RUNTIME_MODE == "host-docker" else kubectl(["-n", NAMESPACE, "exec", "-i", APP_POD, "--", *cmd], body)
out = proc.stdout.decode(errors="replace"); marker = "\\n__CODE__:"; pos = out.rfind(marker)
code = int(out[pos + len(marker):].strip()) if pos >= 0 else 0; raw = out[:pos] if pos >= 0 else out
parsed = json.loads(raw) if raw.strip() else None
if proc.returncode != 0 or not 200 <= code < 300 or (isinstance(parsed, dict) and parsed.get("code") not in (None, 0)):
raise RuntimeError(f"official admin API failed method={method} path={path} http={code}")
return parsed.get("data") if isinstance(parsed, dict) and "data" in parsed else parsed
def login():
password = secret_value("ADMIN_PASSWORD")
if not password: raise RuntimeError("ADMIN_PASSWORD missing")
data = api("POST", "/api/v1/auth/login", payload={"email": env_value("ADMIN_EMAIL", "admin@example.com"), "password": password})
token = data.get("access_token") or data.get("token") if isinstance(data, dict) else None
if not token: raise RuntimeError("admin login response has no token")
return token
FIELDS = ("name","enabled","priority","error_codes","keywords","match_mode","platforms","passthrough_code","response_code","passthrough_body","custom_message","skip_monitoring","description")
def normalized(rule): return {k: rule.get(k) for k in FIELDS}
def state(current, desired):
if current is None: return "missing"
return "synced" if normalized(current) == normalized(desired) else "drift"
def execute():
token = login(); current = api("GET", "/api/v1/admin/error-passthrough-rules", token=token) or []
if not isinstance(current, list): raise RuntimeError("official rule list is not an array")
managed = PAYLOAD.get("managed") or []; selected = PAYLOAD.get("selected")
action = PAYLOAD["action"]; confirm = bool(PAYLOAD.get("confirm")); mutation = False
if action in ("apply", "delete"):
matches = [r for r in current if r.get("name") == selected.get("name")]
if len(matches) > 1: raise RuntimeError("duplicate official rules with managed name")
existing = matches[0] if matches else None
if action == "apply" and state(existing, selected) != "synced" and confirm:
if existing is None: api("POST", "/api/v1/admin/error-passthrough-rules", token, selected)
else: api("PUT", f"/api/v1/admin/error-passthrough-rules/{existing['id']}", token, selected)
mutation = True
if action == "delete" and existing is not None and confirm:
api("DELETE", f"/api/v1/admin/error-passthrough-rules/{existing['id']}", token)
mutation = True
current = api("GET", "/api/v1/admin/error-passthrough-rules", token=token) or []
rows = []
names = {m["name"] for m in managed}
for desired in managed:
found = [r for r in current if r.get("name") == desired["name"]]
actual = found[0] if len(found) == 1 else None
row_state = "duplicate" if len(found) > 1 else state(actual, desired)
if action == "delete" and selected and desired["name"] == selected["name"] and actual is None: row_state = "absent"
rows.append({"templateId": desired["id"], "ruleId": actual.get("id") if actual else None, "enabled": actual.get("enabled") if actual else desired.get("enabled"), "matchMode": desired.get("match_mode"), "errorCodes": desired.get("error_codes"), "responseCode": desired.get("response_code"), "platforms": desired.get("platforms"), "state": row_state})
target_row = next((x for x in rows if selected and x["templateId"] == next((m["id"] for m in managed if m["name"] == selected["name"]), None)), None)
ok = all(x["state"] not in ("duplicate",) for x in rows)
if action == "apply" and confirm: ok = ok and target_row is not None and target_row["state"] == "synced"
if action == "delete" and confirm: ok = ok and target_row is not None and target_row["state"] == "absent"
return {"ok": ok, "mode": "confirm" if confirm else "dry-run" if action != "list" else "list", "mutation": mutation, "rules": rows, "unmanagedCount": sum(1 for r in current if r.get("name") not in names), "message": "official Sub2API error-passthrough-rules API"}
try: output = execute()
except Exception as exc: output = {"ok": False, "error": str(exc), "mutation": False, "rules": []}
print(json.dumps(output, ensure_ascii=False, indent=2))
raise SystemExit(0 if output.get("ok") else 1)
PY`;
}
@@ -15,3 +15,4 @@ export * from "./remote-python-sync";
export * from "./remote";
export * from "./feedback";
export * from "./profit";
export * from "./error-passthrough";
@@ -28,6 +28,7 @@ import { renderCodexPoolPlan } from "./render";
import { codexPoolRuntime } from "./runtime";
import { codexPoolFaults } from "./faults";
import { codexPoolProfit } from "./profit";
import { codexPoolErrorPassthrough } from "./error-passthrough";
import { defaultCodexPoolRuntimeTargetId } from "./runtime-target";
import { codexPoolHelp } from "./types";
@@ -43,6 +44,7 @@ export async function runCodexPoolCommand(config: UniDeskConfig, args: string[])
if (action === "runtime") return await codexPoolRuntime(config, args.slice(1));
if (action === "faults") return await codexPoolFaults(config, args.slice(1));
if (action === "profit") return await codexPoolProfit(config, args.slice(1));
if (action === "error-passthrough") return await codexPoolErrorPassthrough(config, args.slice(1));
if (action === "trace") return await codexPoolTrace(config, parseTraceOptions(args.slice(1)));
if (action === "feedback") return await codexPoolFeedback(config, parseFeedbackOptions(args.slice(1)));
if (action === "sentinel-image") return await codexPoolSentinelImage(config, parseSentinelImageOptions(args.slice(1)));
@@ -28,7 +28,7 @@ export async function capture(config: UniDeskConfig, target: string, args: strin
return await runSshCommandCapture(config, target, args, input);
}
export type RemoteCodexPoolMode = "sync" | "validate" | "runtime" | "faults" | "profit" | "sentinel-probe" | "sentinel-image-status" | "sentinel-image-build";
export type RemoteCodexPoolMode = "sync" | "validate" | "runtime" | "faults" | "profit" | "error-passthrough" | "sentinel-probe" | "sentinel-image-status" | "sentinel-image-build";
export async function runRemoteCodexPoolScript(config: UniDeskConfig, mode: RemoteCodexPoolMode, script: string, target = codexPoolRuntimeTarget()): Promise<SshCaptureResult> {
const jobName = `codex-pool-${mode}-${Date.now().toString(36)}`.slice(0, 63);
@@ -85,6 +85,7 @@ export function codexPoolModeCommand(mode: RemoteCodexPoolMode): string {
if (mode === "sync") return "bun scripts/cli.ts platform-infra sub2api codex-pool sync --confirm";
if (mode === "runtime") return "bun scripts/cli.ts platform-infra sub2api codex-pool runtime list";
if (mode === "profit") return "bun scripts/cli.ts platform-infra sub2api codex-pool profit --since 24h";
if (mode === "error-passthrough") return "bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough list";
if (mode === "sentinel-probe") return "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-probe --account <accountName> --confirm";
if (mode === "sentinel-image-status") return "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status";
if (mode === "sentinel-image-build") return "bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image build --confirm";
@@ -176,9 +176,28 @@ export interface CodexRuntimeTemplate {
tempUnschedulable: CodexTempUnschedulablePolicy;
}
export interface CodexErrorPassthroughRule {
id: string;
name: string;
enabled: boolean;
priority: number;
errorCodes: number[];
keywords: string[];
matchMode: "any" | "all";
platforms: string[];
passthroughCode: boolean;
responseCode: number | null;
passthroughBody: boolean;
customMessage: string | null;
skipMonitoring: boolean;
description: string;
}
export interface CodexRuntimeConfig {
templates: CodexRuntimeTemplate[];
templatesById: Record<string, CodexRuntimeTemplate>;
errorPassthroughRules: CodexErrorPassthroughRule[];
errorPassthroughRulesById: Record<string, CodexErrorPassthroughRule>;
}
export interface CodexPoolProfitConfig {
@@ -363,7 +382,7 @@ export function codexPoolHelp(): unknown {
const pool = readCodexPoolConfig();
const runtimeTarget = codexPoolRuntimeTarget();
return {
command: "platform-infra sub2api codex-pool plan|sync|validate|runtime|faults|profit|trace|feedback|sentinel-image|sentinel-probe|sentinel-report|cleanup-probes|expose|configure-local",
command: "platform-infra sub2api codex-pool plan|sync|validate|runtime|faults|profit|error-passthrough|trace|feedback|sentinel-image|sentinel-probe|sentinel-report|cleanup-probes|expose|configure-local",
output: "json, except trace, feedback, and sentinel-report default to low-noise text tables",
usage: [
"bun scripts/cli.ts platform-infra sub2api codex-pool plan",
@@ -375,6 +394,7 @@ export function codexPoolHelp(): unknown {
"bun scripts/cli.ts platform-infra sub2api codex-pool runtime errors [--group <name-or-id>|--all-groups] [--platform <name>] [--page-token <group-id>] [--account <name-or-id>] [--since 24h] [--tail 50000] [--target PK01] [--json|--full|--raw]",
"bun scripts/cli.ts platform-infra sub2api codex-pool faults [--level P0|P1|P2] [--group <name-or-id>] [--account <name-or-id>] [--model <model>] [--stream sync|stream] [--endpoint <path>] [--request-id <id>] [--page-token <token>] [--target PK01] [--json]",
"bun scripts/cli.ts platform-infra sub2api codex-pool profit [--since 24h] [--target PK01] [--accounts [--page-token <token>]|--missing-costs] [--json]",
"bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough list|apply|delete [--template <id>] [--target PK01] [--confirm] [--json]",
"bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --account <name-or-id> --template <id> [--target PK01] [--confirm]",
"bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply --accounts <name-or-id,...> --template <id> [--target PK01] [--confirm] [--full|--raw]",
"bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete --account <name-or-id> --kind temp-unschedulable [--target PK01] [--confirm]",