fix: 持久化 Code Agent Codex app-server 主路径

DC-DCSN-P0-2026-003 / HWLAB #426
This commit is contained in:
Lyon
2026-05-24 14:53:14 +08:00
committed by GitHub
parent 3ca14d362e
commit d82b0c0ba3
12 changed files with 1281 additions and 271 deletions
+3 -3
View File
@@ -120,12 +120,12 @@ export function classifyCodexRunnerCapability(payload, { httpStatus = null } = {
const missing = [];
if (payload?.status !== "completed") missing.push("completed status");
if (!trustedRunnerProviders.has(provider)) missing.push("runner provider");
if (runnerKind !== "codex-mcp-stdio-runner") missing.push("runner.kind=codex-mcp-stdio-runner");
if (runnerKind !== "codex-app-server-stdio-runner") missing.push("runner.kind=codex-app-server-stdio-runner");
if (capabilityLevel !== "long-lived-codex-stdio-session") missing.push("capabilityLevel=long-lived-codex-stdio-session");
if (!workspace) missing.push("workspace");
if (sandbox !== "workspace-write") missing.push("sandbox=workspace-write");
if (sessionMode !== "codex-mcp-stdio-long-lived") missing.push("sessionMode=codex-mcp-stdio-long-lived");
if (implementationType !== "repo-owned-codex-mcp-stdio-session") missing.push("implementationType=repo-owned-codex-mcp-stdio-session");
if (sessionMode !== "codex-app-server-stdio-long-lived") missing.push("sessionMode=codex-app-server-stdio-long-lived");
if (implementationType !== "repo-owned-codex-app-server-stdio-session") missing.push("implementationType=repo-owned-codex-app-server-stdio-session");
if (!session) missing.push("session");
if (session && !["idle", "ready", "busy"].includes(session.status)) missing.push("session.status active/idle");
if (session && typeof session.idleTimeoutMs !== "number") missing.push("session.idleTimeoutMs");
+72
View File
@@ -401,6 +401,8 @@ function validateCloudApiCodeAgentSource(ctx, env) {
expectEqual(ctx, env.HWLAB_CODE_AGENT_CODEX_SANDBOX, "workspace-write", "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_CODEX_SANDBOX", "cloud API Codex stdio sandbox contract");
expectEqual(ctx, env.HWLAB_CODE_AGENT_CODEX_COMMAND, "/app/node_modules/.bin/codex", "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_CODEX_COMMAND", "cloud API Codex command contract");
expectEqual(ctx, env.CODEX_HOME, "/codex-home", "$.services.hwlab-cloud-api.env.CODEX_HOME", "cloud API CODEX_HOME contract");
expectNoProxyIncludes(ctx, env.NO_PROXY, contract.noProxyRequired, "$.services.hwlab-cloud-api.env.NO_PROXY");
expectNoProxyIncludes(ctx, env.no_proxy, contract.noProxyRequired, "$.services.hwlab-cloud-api.env.no_proxy");
expect(
ctx,
env.HWLAB_CODE_AGENT_OPENAI_BASE_URL !== contract.egress.forbiddenDirectBaseUrl,
@@ -570,6 +572,7 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) {
const secretRef = contract.secretRefs[0];
const container = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.containers?.[0];
const env = new Map((container?.env ?? []).map((entry) => [entry.name, entry]));
const volumes = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.volumes ?? [];
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_PROVIDER")?.value, contract.codeAgentProvider, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_PROVIDER", "cloud API workload Code Agent provider");
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_MODEL")?.value, contract.model, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_MODEL", "cloud API workload Code Agent model");
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_TIMEOUT_MS")?.value, codeAgentBackendTimeoutMs, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API workload Code Agent hard timeout budget");
@@ -594,6 +597,9 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) {
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_CODEX_SANDBOX")?.value, "workspace-write", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_CODEX_SANDBOX", "cloud API workload Codex stdio sandbox contract");
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_CODEX_COMMAND")?.value, "/app/node_modules/.bin/codex", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_CODEX_COMMAND", "cloud API workload Codex command contract");
expectEqual(ctx, env.get("CODEX_HOME")?.value, "/codex-home", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.CODEX_HOME", "cloud API workload CODEX_HOME contract");
expectNoProxyIncludes(ctx, env.get("NO_PROXY")?.value, contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.NO_PROXY");
expectNoProxyIncludes(ctx, env.get("no_proxy")?.value, contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.no_proxy");
validateCodexHomeMountArtifacts(ctx, container?.volumeMounts ?? [], volumes, contract);
expect(
ctx,
env.get("HWLAB_CODE_AGENT_OPENAI_BASE_URL")?.value !== contract.egress.forbiddenDirectBaseUrl,
@@ -606,6 +612,72 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) {
expectEqual(ctx, env.get(secretRef.env)?.valueFrom?.secretKeyRef?.key, secretRef.secretKey, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.OPENAI_API_KEY.secretKeyRef.key", "cloud API workload Code Agent Secret key");
}
function expectNoProxyIncludes(ctx, value, required, path, label = "NO_PROXY contract") {
const entries = new Set(String(value ?? "")
.split(/[,;\s]+/u)
.map((item) => item.trim().toLowerCase())
.filter(Boolean));
for (const item of required) {
expect(ctx, entries.has(String(item).toLowerCase()), "missing_no_proxy_entry", path, `${label} includes ${item}`, { expected: item, actual: value });
}
}
function validateCodexHomeMountArtifacts(ctx, volumeMounts, volumes, contract) {
expect(
ctx,
volumeMounts.some((entry) => entry.name === "hwlab-code-agent-codex-home" && entry.mountPath === contract.codexHome.path),
"codex_home_mount_missing",
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.volumeMounts.hwlab-code-agent-codex-home",
"cloud API workload CODEX_HOME writable mount"
);
expect(
ctx,
volumeMounts.some((entry) =>
entry.name === "hwlab-code-agent-codex-config" &&
entry.mountPath === `${contract.codexHome.path}/${contract.codexHome.configKey}` &&
entry.subPath === contract.codexHome.configKey &&
entry.readOnly === true
),
"codex_config_mount_missing",
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.volumeMounts.hwlab-code-agent-codex-config",
"cloud API workload Codex config.toml read-only mount"
);
expect(
ctx,
volumeMounts.some((entry) =>
entry.name === "hwlab-code-agent-codex-auth" &&
entry.mountPath === `${contract.codexHome.path}/${contract.codexHome.authKey}` &&
entry.subPath === contract.codexHome.authKey &&
entry.readOnly === true
),
"codex_auth_mount_missing",
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.volumeMounts.hwlab-code-agent-codex-auth",
"cloud API workload Codex auth.json read-only mount"
);
expect(
ctx,
volumes.some((entry) =>
entry.name === "hwlab-code-agent-codex-config" &&
entry.configMap?.name === contract.codexHome.configMapName &&
(entry.configMap.items ?? []).some((item) => item.key === contract.codexHome.configKey && item.path === contract.codexHome.configKey)
),
"codex_config_volume_missing",
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.volumes.hwlab-code-agent-codex-config",
"cloud API workload Codex config ConfigMap ref"
);
expect(
ctx,
volumes.some((entry) =>
entry.name === "hwlab-code-agent-codex-auth" &&
entry.secret?.secretName === contract.codexHome.secretName &&
(entry.secret.items ?? []).some((item) => item.key === contract.codexHome.authKey && item.path === contract.codexHome.authKey)
),
"codex_auth_volume_missing",
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.volumes.hwlab-code-agent-codex-auth",
"cloud API workload Codex auth Secret ref"
);
}
function validateCodeAgentProxyTimeoutArtifacts(ctx, workloads) {
const workloadItems = mapByServiceId(listItems(workloads));
const cloudWebEnv = new Map((workloadItems.get("hwlab-cloud-web")?.spec?.template?.spec?.containers?.[0]?.env ?? []).map((entry) => [entry.name, entry.value]));