Merge main into durable runtime readiness PR

This commit is contained in:
root
2026-05-22 18:07:49 +00:00
21 changed files with 812 additions and 51 deletions
+29 -24
View File
@@ -6,11 +6,17 @@ import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
DEV_CODE_AGENT_PROVIDER_CONTRACT,
codeAgentSecretRefPlaceholder,
inspectCodeAgentProviderEnv
} from "./code-agent-contract.mjs";
const DEFAULT_CODE_AGENT_TIMEOUT_MS = 120000;
const DEFAULT_CODEX_COMMAND = "codex";
const DEFAULT_MODEL = "gpt-5.2";
const DEFAULT_MODEL = DEV_CODE_AGENT_PROVIDER_CONTRACT.model;
const DEFAULT_PROJECT_ID = "prj_hwlab-cloud-workbench";
const CODE_AGENT_PROVIDER_SECRET_REF = "hwlab-code-agent-provider/openai-api-key";
const CODE_AGENT_PROVIDER_SECRET_REF = codeAgentSecretRefPlaceholder().replace("secretRef:", "");
const CODE_AGENT_SYSTEM_PROMPT = [
"你是 HWLAB 云工作台的 Code Agent。",
"请用中文直接回答用户的工作台问题。",
@@ -127,13 +133,18 @@ export function validateCodeAgentChatSchema(payload) {
export function describeCodeAgentAvailability(env = process.env, options = {}) {
const providerPlan = resolveProviderPlan(env, options);
const missingEnv = [];
const providerContract = inspectCodeAgentProviderEnv(env);
const missingEnv = providerPlan.mode === "openai"
? providerContract.missingEnv
: [];
if (!env.OPENAI_API_KEY) {
if (providerPlan.mode !== "openai" && !env.OPENAI_API_KEY) {
missingEnv.push("OPENAI_API_KEY");
}
const blocked = missingEnv.length > 0;
const blocked = providerPlan.mode === "openai"
? !providerContract.ready
: missingEnv.length > 0;
return {
endpoint: "POST /v1/agent/chat",
provider: providerPlan.provider,
@@ -157,23 +168,15 @@ export function describeCodeAgentAvailability(env = process.env, options = {}) {
"availability.status"
],
status: blocked ? "blocked" : "available",
blocker: blocked ? "凭证缺口" : null,
blocker: blocked ? (providerPlan.mode === "openai" ? providerContract.blocker : "凭证缺口") : null,
reason: blocked ? "provider_unavailable" : null,
summary: blocked
? `真实后端已接入,但当前 DEV provider Secret ${CODE_AGENT_PROVIDER_SECRET_REF} 未注入,因此不能返回真实回复。`
: "真实后端已接入,发送会走真实 /v1/agent/chat;只有实际 completed 回复才标记 DEV-LIVE。",
? `真实后端已接入,但当前 DEV provider Secret ${CODE_AGENT_PROVIDER_SECRET_REF} 或 DEV egress/base-url contract 未满足,因此不能返回真实回复。`
: "真实后端已接入,发送会走 DEV egress/proxy 后的真实 /v1/agent/chat;只有实际 completed 回复才标记 DEV-LIVE。",
missingEnv,
secretRefs: blocked
? [
{
env: "OPENAI_API_KEY",
secretName: "hwlab-code-agent-provider",
secretKey: "openai-api-key",
present: false,
redacted: true
}
]
: [],
secretRefs: blocked ? providerContract.secretRefs : [],
egress: providerContract.egress,
safety: providerContract.safety,
ready: !blocked
};
}
@@ -233,16 +236,18 @@ async function callConfiguredProvider({
}
async function callOpenAiResponses({ providerPlan, message, conversationId, traceId, timeoutMs, env }) {
if (!env.OPENAI_API_KEY) {
throw providerUnavailable("OPENAI_API_KEY is not configured for openai-responses", {
missingEnv: ["OPENAI_API_KEY"],
const providerContract = inspectCodeAgentProviderEnv(env);
if (!providerContract.ready) {
throw providerUnavailable(providerContract.blocker, {
missingEnv: providerContract.missingEnv,
provider: providerPlan.provider,
model: providerPlan.model,
backend: providerPlan.backend
backend: providerPlan.backend,
egress: providerContract.egress
});
}
const endpoint = env.HWLAB_CODE_AGENT_OPENAI_BASE_URL || "https://api.openai.com/v1/responses";
const endpoint = env.HWLAB_CODE_AGENT_OPENAI_BASE_URL;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), effectiveTimeout(timeoutMs));
let response;
+137
View File
@@ -0,0 +1,137 @@
import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
export const DEV_CODE_AGENT_PROVIDER_CONTRACT = Object.freeze({
contractVersion: "v1",
environment: ENVIRONMENT_DEV,
provider: "openai",
runtimeProvider: "openai-responses",
backend: "hwlab-cloud-api/openai-responses",
model: "gpt-5.5",
requiredEnv: Object.freeze([
"OPENAI_API_KEY",
"HWLAB_CODE_AGENT_OPENAI_BASE_URL"
]),
secretRefs: Object.freeze([
Object.freeze({
env: "OPENAI_API_KEY",
secretName: "hwlab-code-agent-provider",
secretKey: "openai-api-key"
})
]),
egress: Object.freeze({
env: "HWLAB_CODE_AGENT_OPENAI_BASE_URL",
target: "dev-egress-proxy",
defaultBaseUrl: "http://172.26.26.227:17680/v1/responses",
forbiddenDirectBaseUrl: "https://api.openai.com/v1/responses",
reason: "DEV cloud-api pods must use the D601 egress/proxy path for OpenAI Responses."
}),
forbidden: Object.freeze({
prodAllowed: false,
secretPlaintextAllowed: false,
directPublicOpenAiFromDevPod: false
})
});
export function codeAgentSecretRefPlaceholder() {
const ref = DEV_CODE_AGENT_PROVIDER_CONTRACT.secretRefs[0];
return `secretRef:${ref.secretName}/${ref.secretKey}`;
}
export function buildCodeAgentProviderManifestPlaceholder() {
const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
return {
contractVersion: contract.contractVersion,
environment: contract.environment,
provider: contract.provider,
runtimeProvider: contract.runtimeProvider,
backend: contract.backend,
model: contract.model,
requiredEnv: [...contract.requiredEnv],
secretRefs: contract.secretRefs.map((item) => ({ ...item })),
egress: { ...contract.egress },
fixtureEvidence: {
providerConnected: false,
summary: "This manifest contract records provider env names, Secret reference names, and DEV egress policy only. It is not live provider evidence."
}
};
}
export function inspectCodeAgentProviderEnv(env = {}) {
const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
const missingEnv = contract.requiredEnv.filter((name) => !hasEnvValue(env, name));
const secretRef = contract.secretRefs[0];
const baseUrl = firstNonEmpty(env.HWLAB_CODE_AGENT_OPENAI_BASE_URL);
const directPublicOpenAi =
baseUrl === contract.egress.forbiddenDirectBaseUrl ||
/^https:\/\/api\.openai\.com\/v1\/responses\/?$/u.test(baseUrl);
const egressReady = Boolean(baseUrl) && !directPublicOpenAi;
const ready = missingEnv.length === 0 && egressReady;
return {
contractVersion: contract.contractVersion,
environment: contract.environment,
provider: contract.runtimeProvider,
model: firstNonEmpty(env.HWLAB_CODE_AGENT_MODEL, contract.model),
backend: contract.backend,
status: ready ? "available" : "blocked",
ready,
requiredEnv: contract.requiredEnv.map((name) => ({
name,
present: hasEnvValue(env, name),
redacted: name === secretRef.env,
source: name === secretRef.env ? "k8s-secret-ref" : "runtime-env"
})),
missingEnv,
secretRefs: [
{
env: secretRef.env,
secretName: secretRef.secretName,
secretKey: secretRef.secretKey,
present: hasEnvValue(env, secretRef.env),
envInjected: hasEnvValue(env, secretRef.env),
secretPresent: "not_observed_by_runtime",
secretKeyPresent: "not_observed_by_runtime",
redacted: true
}
],
egress: {
env: contract.egress.env,
present: hasEnvValue(env, contract.egress.env),
target: contract.egress.target,
baseUrlConfigured: Boolean(baseUrl),
directPublicOpenAi,
ready: egressReady,
valueRedacted: true
},
safety: {
devOnly: true,
prodAllowed: contract.forbidden.prodAllowed,
secretsRead: false,
secretMaterialRead: false,
valuesRedacted: true,
directPublicOpenAiFromDevPod: directPublicOpenAi
},
blocker: ready ? null : codeAgentProviderBlocker({ missingEnv, directPublicOpenAi, baseUrl })
};
}
function codeAgentProviderBlocker({ missingEnv, directPublicOpenAi, baseUrl }) {
if (directPublicOpenAi) {
return "DEV Code Agent OpenAI base URL points at public api.openai.com instead of the DEV egress/proxy path";
}
if (!baseUrl) {
return "DEV Code Agent OpenAI base URL is missing; provider calls must use the DEV egress/proxy path";
}
return `Code Agent provider runtime config is missing ${missingEnv.join(", ")}`;
}
function hasEnvValue(env, name) {
return typeof env?.[name] === "string" && env[name].trim().length > 0;
}
function firstNonEmpty(...values) {
for (const value of values) {
if (typeof value === "string" && value.trim()) return value.trim();
}
return "";
}
+59 -1
View File
@@ -23,6 +23,12 @@ export const DEV_DB_ENV_CONTRACT = Object.freeze({
port: 5432,
portName: "postgres"
}),
readinessLayers: Object.freeze([
"dns",
"tcp",
"auth",
"schema"
]),
nonSecretDefaults: Object.freeze({
HWLAB_CLOUD_DB_SSL_MODE: "require",
HWLAB_CLOUD_DB_SERVICE_NAME: "cloud-api-db",
@@ -33,7 +39,11 @@ export const DEV_DB_ENV_CONTRACT = Object.freeze({
forbidden: Object.freeze({
prodAllowed: false,
secretPlaintextAllowed: false,
liveDbEvidenceFromFixtures: false
liveDbEvidenceFromFixtures: false,
invalidRuntimeHosts: Object.freeze([
".invalid",
"hwlab-dev-db.invalid"
])
})
});
@@ -96,6 +106,7 @@ export function buildDbHealthContract(env = process.env) {
liveDbEvidence: connected,
liveDbConnectedEvidence: connected
},
readinessLayers: buildReadinessLayers(connection),
blocker: configReady ? "live DB connection has not been attempted by this health contract" : missingEnvBlocker(missingEnv),
evidence: configReady ? "env_presence_only_no_live_db" : "env_contract_blocked"
};
@@ -124,6 +135,8 @@ export function buildDbEnvManifestPlaceholder() {
requiredEnv: [...DEV_DB_ENV_CONTRACT.requiredEnv],
secretRefs: DEV_DB_ENV_CONTRACT.secretRefs.map((item) => ({ ...item })),
dns: { ...DEV_DB_ENV_CONTRACT.dns },
readinessLayers: [...DEV_DB_ENV_CONTRACT.readinessLayers],
forbiddenRuntimeHosts: [...DEV_DB_ENV_CONTRACT.forbidden.invalidRuntimeHosts],
nonSecretDefaults: { ...DEV_DB_ENV_CONTRACT.nonSecretDefaults },
fixtureEvidence: {
liveDbEvidence: false,
@@ -173,6 +186,7 @@ export function summarizeDbContract(db = buildDbHealthContract()) {
),
connection: summarizeConnection(db.connection),
redaction: summarizeRedaction(db.redaction),
readinessLayers: buildReadinessLayers(db.connection),
blocker: db.blocker ?? null,
fixtureEvidence: false
};
@@ -257,6 +271,14 @@ export function parseDbUrlContract(rawUrl) {
errorCode: "MISSING_HOST"
};
}
if (isForbiddenRuntimeHost(url.hostname)) {
return {
ok: false,
result: "forbidden_runtime_host",
classification: "db_url_forbidden_invalid_host",
errorCode: "FORBIDDEN_INVALID_HOST"
};
}
const port = url.port ? Number.parseInt(url.port, 10) : 5432;
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
return {
@@ -382,6 +404,7 @@ function applyConnectionResult(base, connection) {
liveDbEvidence: liveConnected,
liveDbConnectedEvidence: liveConnected
},
readinessLayers: buildReadinessLayers(connection),
blocker: liveConnected ? null : blockerForConnection(connection),
evidence: liveConnected ? "live_db_tcp_connection_ready" : "live_db_tcp_connection_blocked"
};
@@ -391,6 +414,9 @@ function blockerForConnection(connection) {
if (connection.result === "invalid_url") {
return "DB URL is injected but cannot be parsed as a supported postgres URL";
}
if (connection.result === "forbidden_runtime_host") {
return "DB URL is injected but points at a forbidden .invalid DEV runtime host";
}
if (connection.result === "unsupported_protocol") {
return "DB URL is injected but does not use a supported postgres protocol";
}
@@ -421,6 +447,38 @@ function normalizeTimeoutMs(value) {
return Math.min(parsed, 5000);
}
function isForbiddenRuntimeHost(hostname) {
const normalized = String(hostname ?? "").toLowerCase();
return DEV_DB_ENV_CONTRACT.forbidden.invalidRuntimeHosts.some((host) =>
host.startsWith(".") ? normalized.endsWith(host) : normalized === host
);
}
function buildReadinessLayers(connection = {}) {
const result = connection?.result ?? "unknown";
const connected = result === "connected";
const dnsReady = connected || ["refused", "timeout", "network_unreachable", "error"].includes(result);
const tcpReady = connected;
return {
dns: {
status: dnsReady ? "pass" : "not_proven",
result
},
tcp: {
status: tcpReady ? "pass" : "not_proven",
result
},
auth: {
status: "not_proven",
result: connected ? "tcp_connected_auth_not_checked" : result
},
schema: {
status: "not_proven",
result: connected ? "tcp_connected_schema_not_checked" : result
}
};
}
function summarizeConnection(connection) {
if (!connection) {
return {
+42 -6
View File
@@ -49,6 +49,8 @@ test("cloud api exposes /health, /health/live, and /live probes", async () => {
assert.equal(healthPayload.codeAgent.secretRefs[0].secretName, "hwlab-code-agent-provider");
assert.equal(healthPayload.codeAgent.secretRefs[0].secretKey, "openai-api-key");
assert.equal(healthPayload.codeAgent.secretRefs[0].redacted, true);
assert.equal(healthPayload.codeAgent.egress.directPublicOpenAi, false);
assert.equal(healthPayload.codeAgent.safety.secretMaterialRead, false);
assert.equal(JSON.stringify(healthPayload.codeAgent).includes("sk-"), false);
assert.deepEqual(healthPayload.db.missingEnv, ["HWLAB_CLOUD_DB_URL", "HWLAB_CLOUD_DB_SSL_MODE"]);
assert.equal(healthPayload.db.secretRefs[0].secretName, "hwlab-cloud-api-dev-db");
@@ -156,7 +158,7 @@ test("cloud api health does not treat DB env presence-only as live readiness", a
const originalProbeDisabled = process.env.HWLAB_CLOUD_DB_PROBE_DISABLED;
process.env.HWLAB_CLOUD_DB_URL = "postgres://user:password@db.example.invalid:5432/hwlab";
process.env.HWLAB_CLOUD_DB_SSL_MODE = "require";
process.env.HWLAB_CLOUD_DB_PROBE_DISABLED = "1";
delete process.env.HWLAB_CLOUD_DB_PROBE_DISABLED;
const server = createCloudApiServer();
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
@@ -172,9 +174,12 @@ test("cloud api health does not treat DB env presence-only as live readiness", a
assert.equal(payload.db.liveDbEvidence, false);
assert.equal(payload.db.safety.liveDbEvidence, false);
assert.equal(payload.db.ready, false);
assert.equal(payload.db.connectionAttempted, false);
assert.equal(payload.db.connectionResult, "not_attempted");
assert.equal(payload.db.evidence, "env_presence_only_probe_disabled");
assert.equal(payload.db.connectionAttempted, true);
assert.equal(payload.db.connectionResult, "forbidden_runtime_host");
assert.equal(payload.db.evidence, "live_db_tcp_connection_blocked");
assert.equal(payload.db.connection.networkAttempted, false);
assert.equal(payload.db.connection.classification, "db_url_forbidden_invalid_host");
assert.equal(payload.db.readinessLayers.dns.status, "not_proven");
assert.equal(JSON.stringify(payload.db).includes("password"), false);
assert.equal(JSON.stringify(payload.db).includes("db.example.invalid"), false);
} finally {
@@ -220,14 +225,17 @@ test("cloud api /v1 describes Code Agent provider blocker without leaking secret
assert.equal(payload.codeAgent.backend, "hwlab-cloud-api/openai-responses");
assert.equal(payload.codeAgent.mode, "openai");
assert.equal(payload.codeAgent.status, "blocked");
assert.equal(payload.codeAgent.blocker, "凭证缺口");
assert.match(payload.codeAgent.blocker, /DEV Code Agent OpenAI base URL is missing/u);
assert.equal(payload.codeAgent.reason, "provider_unavailable");
assert.deepEqual(payload.codeAgent.missingEnv, ["OPENAI_API_KEY"]);
assert.deepEqual(payload.codeAgent.missingEnv, ["OPENAI_API_KEY", "HWLAB_CODE_AGENT_OPENAI_BASE_URL"]);
assert.equal(payload.codeAgent.secretRefs[0].env, "OPENAI_API_KEY");
assert.equal(payload.codeAgent.secretRefs[0].secretName, "hwlab-code-agent-provider");
assert.equal(payload.codeAgent.secretRefs[0].secretKey, "openai-api-key");
assert.equal(payload.codeAgent.secretRefs[0].redacted, true);
assert.equal(payload.codeAgent.ready, false);
assert.equal(payload.codeAgent.egress.present, false);
assert.equal(payload.codeAgent.egress.valueRedacted, true);
assert.equal(payload.codeAgent.safety.secretMaterialRead, false);
assert.match(payload.codeAgent.summary, /真实后端已接入/u);
assert.match(payload.codeAgent.summary, /hwlab-code-agent-provider\/openai-api-key/u);
assert.equal(JSON.stringify(payload).includes("sk-"), false);
@@ -238,6 +246,34 @@ test("cloud api /v1 describes Code Agent provider blocker without leaking secret
}
});
test("cloud api /v1 describes Code Agent egress blocker without leaking API key", async () => {
const server = createCloudApiServer({
env: {
OPENAI_API_KEY: "test-openai-key-material",
HWLAB_CODE_AGENT_PROVIDER: "openai",
HWLAB_CODE_AGENT_MODEL: "gpt-test",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: "https://api.openai.com/v1/responses"
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const response = await fetch(`http://127.0.0.1:${port}/v1`);
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.codeAgent.status, "blocked");
assert.equal(payload.codeAgent.egress.directPublicOpenAi, true);
assert.match(payload.codeAgent.blocker, /public api\.openai\.com/u);
assert.equal(payload.codeAgent.safety.secretMaterialRead, false);
assert.equal(JSON.stringify(payload).includes("test-openai-key-material"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat returns structured completed Code Agent payload", async () => {
const server = createCloudApiServer({
callCodeAgentProvider: async ({ providerPlan, message, traceId }) => ({