fix(hwlab): expose d518 code agent runtime config

This commit is contained in:
Codex
2026-06-27 16:04:20 +00:00
parent f8f0741073
commit 4227211544
5 changed files with 316 additions and 9 deletions
+74 -9
View File
@@ -36,7 +36,7 @@ import { runTransHostScript } from "./public-exposure";
import { compactRuntimeCommand, runNodeHostScriptAsync } from "./runtime-common";
import { compactNodeRuntimeGitMirrorStatus, nodeRuntimeGitMirrorStatus } from "./status";
import { keyValueLinesFromText, numericField, optionValue, record, shellQuote } from "./utils";
import { externalPostgresBridgeStatus, externalPostgresSecretStatus, getNodeRuntimePipelineRun, isLocalPostgresObject, nodeRuntimeRenderOverlay } from "./web-probe";
import { externalPostgresBridgeStatus, externalPostgresSecretStatus, getNodeRuntimePipelineRun, isLocalPostgresObject, nodeRuntimeCodeAgentRuntimeStatus, nodeRuntimeRenderOverlay } from "./web-probe";
import { webObserveShort, webObserveText } from "./web-probe-observe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
@@ -190,6 +190,7 @@ export function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNod
const workloadReadiness = parseNodeRuntimeWorkloadReadiness(workloadReadinessProbe?.stdout ?? "");
const bridge = externalPostgresBridgeStatus(spec, namespaceExists);
const secrets = externalPostgresSecretStatus(spec, namespaceExists);
const codeAgentRuntime = nodeRuntimeCodeAgentRuntimeStatus(spec, namespaceExists);
const publicProbes = nodeRuntimePublicProbeStatus(spec);
const gitMirror = nodeRuntimeGitMirrorStatus(scoped);
const gitMirrorCompact = compactNodeRuntimeGitMirrorStatus(gitMirror);
@@ -198,7 +199,21 @@ export function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNod
const workloadsReady = workloadReadiness.length > 0 && workloadReadiness.every((item) => item.ready);
const localPostgresExpectedAbsent = nodeRuntimeLocalPostgresExpectedAbsent(spec);
const localPostgresReady = localPostgresExpectedAbsent ? localPostgresObjects.length === 0 : localPostgresObjects.length > 0;
const runtimeReady = namespaceExists && localPostgresReady && workloadsReady && (activeExternalPostgres === undefined || (bridge.ready && secrets.ready));
const runtimeReady = namespaceExists && localPostgresReady && workloadsReady && (activeExternalPostgres === undefined || (bridge.ready && secrets.ready)) && codeAgentRuntime.ready === true;
const codeAgentRuntimeDegradedReason = typeof codeAgentRuntime.degradedReason === "string" ? codeAgentRuntime.degradedReason : null;
const runtimeDegradedReason = !namespaceExists
? "runtime-namespace-missing"
: !localPostgresReady
? "runtime-local-postgres-not-ready"
: !workloadsReady
? "runtime-workloads-not-ready"
: activeExternalPostgres !== undefined && bridge.ready !== true
? "external-postgres-bridge-not-ready"
: activeExternalPostgres !== undefined && secrets.ready !== true
? "external-postgres-secrets-not-ready"
: codeAgentRuntime.ready !== true
? codeAgentRuntimeDegradedReason ?? "code-agent-runtime-not-ready"
: "runtime-not-ready";
const argoReady = argo.exitCode === 0 && repoURL === spec.argoRepoUrl && targetRevision === spec.gitopsBranch && path === spec.runtimePath && syncStatus === "Synced" && health === "Healthy";
const pipelineRunReady = pipelineRunProbe !== null && pipelineRunProbe.status === "True";
const pipelineRunDegradedReason = typeof pipelineRunDiagnostics?.degradedReason === "string"
@@ -254,6 +269,7 @@ export function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNod
workloadResult: workloads === null ? null : compactRuntimeCommand(workloads),
externalPostgresBridge: bridge,
externalPostgresSecrets: secrets,
codeAgentRuntime,
},
publicProbes,
gitMirror: {
@@ -274,7 +290,7 @@ export function nodeRuntimeControlPlaneStatus(scoped: ReturnType<typeof parseNod
: "public-probe-not-ready"
: pipelineRunDegradedReason
: "argo-not-synced-healthy"
: namespaceExists ? "runtime-not-ready" : "runtime-namespace-missing"
: runtimeDegradedReason
: "control-plane-not-ready",
next: {
plan: `bun scripts/cli.ts hwlab nodes control-plane plan --node ${scoped.node} --lane ${scoped.lane}`,
@@ -516,6 +532,8 @@ export function summarizeNodeRuntimeControlPlaneStatus(status: Record<string, un
externalPostgresReady: runtime.externalPostgresBridge === undefined && runtime.externalPostgresSecrets === undefined
? null
: record(runtime.externalPostgresBridge).ready === true && record(runtime.externalPostgresSecrets).ready === true,
codeAgentRuntimeReady: record(runtime.codeAgentRuntime).required === true ? record(runtime.codeAgentRuntime).ready === true : null,
codeAgentRuntimeReason: typeof record(runtime.codeAgentRuntime).degradedReason === "string" ? record(runtime.codeAgentRuntime).degradedReason : null,
},
publicProbe: {
ready: publicProbes.ready === true,
@@ -1082,6 +1100,7 @@ export function renderNodeRuntimeControlPlaneOnNode(spec: HwlabRuntimeLaneSpec,
" envRecipe: { ...(lane.envRecipe || {}), downloadStack },",
"};",
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
"if (overlay.gitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.gitMirror;",
"fs.writeFileSync(path, YAML.stringify(doc));",
"NODE",
@@ -1166,6 +1185,7 @@ export function renderNodeRuntimeControlPlaneLocal(spec: HwlabRuntimeLaneSpec, s
" envRecipe: { ...(lane.envRecipe || {}), downloadStack },",
"};",
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
"if (overlay.gitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.gitMirror;",
"fs.writeFileSync(path, YAML.stringify(doc));",
"NODE",
@@ -1261,6 +1281,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" publicApiUrl: overlay.publicApiUrl,",
" externalPostgres: overlay.externalPostgres,",
" runtimeStore: overlay.runtimeStore,",
" codeAgentRuntime: overlay.codeAgentRuntime,",
" observability: overlay.observability,",
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
" dockerProxyHttp: overlay.dockerProxyHttp,",
@@ -1310,6 +1331,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" envRecipe: { ...envRecipe, downloadStack },",
"};",
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
"fs.writeFileSync(file, YAML.stringify(doc));",
"console.error(JSON.stringify({ event: 'unidesk-deploy-yaml-overlay', ok: true, lane: overlay.lane, httpProxy: overlay.dockerProxyHttp, noProxyCount: overlay.dockerNoProxyList.length }));",
"NODE_UNIDESK_DEPLOY_YAML_OVERLAY`;",
@@ -1348,6 +1370,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" runtimeRenderDir: overlay.runtimeRenderDir,",
" runtimeNamespace: overlay.runtimeNamespace,",
" externalPostgres: overlay.externalPostgres,",
" codeAgentRuntime: overlay.codeAgentRuntime,",
" publicExposure: overlay.publicExposure,",
" observability: overlay.observability,",
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
@@ -1526,18 +1549,30 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" return changed;",
"}",
"function patchRuntimeEnv(item, podSpec) {",
" if (!isObject(podSpec)) return { publicEndpointChanged: false, dbSslModeChanged: false };",
" if (!isObject(podSpec)) return { publicEndpointChanged: false, dbSslModeChanged: false, codeAgentRuntimeChanged: false };",
" let publicEndpointChanged = false;",
" let dbSslModeChanged = false;",
" let codeAgentRuntimeChanged = false;",
" const pg = overlay.externalPostgres;",
" const codeAgentRuntime = overlay.codeAgentRuntime;",
" for (const group of ['containers', 'initContainers']) {",
" for (const container of Array.isArray(podSpec[group]) ? podSpec[group] : []) {",
" if (!isObject(container)) continue;",
" if (envValue(container, 'HWLAB_PUBLIC_ENDPOINT') !== undefined) publicEndpointChanged = setEnvValue(container, 'HWLAB_PUBLIC_ENDPOINT', expectedPublicEndpoint(item)) || publicEndpointChanged;",
" if (pg && pg.sslmode && envValue(container, 'HWLAB_CLOUD_DB_SSL_MODE') !== undefined) dbSslModeChanged = setEnvValue(container, 'HWLAB_CLOUD_DB_SSL_MODE', pg.sslmode) || dbSslModeChanged;",
" if (codeAgentRuntime && codeAgentRuntime.enabled && workloadName(item) === 'hwlab-cloud-api' && container.name === 'hwlab-cloud-api') {",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_ADAPTER', String(codeAgentRuntime.adapter)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'AGENTRUN_MGR_URL', String(codeAgentRuntime.managerUrl)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvFromSecret(container, 'AGENTRUN_API_KEY', String(codeAgentRuntime.apiKeySecretName), String(codeAgentRuntime.apiKeySecretKey)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE', String(codeAgentRuntime.runnerNamespace)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE', String(codeAgentRuntime.secretNamespace)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_AGENTRUN_REPO_URL', String(codeAgentRuntime.repoUrl)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID', String(codeAgentRuntime.providerId)) || codeAgentRuntimeChanged;",
" codeAgentRuntimeChanged = setEnvValue(container, 'HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE', String(codeAgentRuntime.defaultProviderProfile)) || codeAgentRuntimeChanged;",
" }",
" }",
" }",
" return { publicEndpointChanged, dbSslModeChanged };",
" return { publicEndpointChanged, dbSslModeChanged, codeAgentRuntimeChanged };",
"}",
"function patchRuntimeWorkloads() {",
" let observabilityChanged = false;",
@@ -1546,6 +1581,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" let gitReadUrlChanged = false;",
" let publicEndpointChanged = false;",
" let dbSslModeChanged = false;",
" let codeAgentRuntimeChanged = false;",
" for (const file of yamlFiles(runtimePath)) {",
" if (path.basename(file) === 'kustomization.yaml') continue;",
" const docs = readYamlDocuments(file);",
@@ -1571,14 +1607,15 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" changed = gitUrlChanged || changed;",
" gitReadUrlChanged = gitReadUrlChanged || gitUrlChanged;",
" const envChanged = patchRuntimeEnv(item, podSpecFor(item));",
" changed = envChanged.publicEndpointChanged || envChanged.dbSslModeChanged || changed;",
" changed = envChanged.publicEndpointChanged || envChanged.dbSslModeChanged || envChanged.codeAgentRuntimeChanged || changed;",
" publicEndpointChanged = publicEndpointChanged || envChanged.publicEndpointChanged;",
" dbSslModeChanged = dbSslModeChanged || envChanged.dbSslModeChanged;",
" codeAgentRuntimeChanged = codeAgentRuntimeChanged || envChanged.codeAgentRuntimeChanged;",
" }",
" }",
" if (changed) writeYamlDocuments(file, docs);",
" }",
" return { observabilityChanged, startupProbeChanged, imageRewriteChanged, gitReadUrlChanged, publicEndpointChanged, dbSslModeChanged };",
" return { observabilityChanged, startupProbeChanged, imageRewriteChanged, gitReadUrlChanged, publicEndpointChanged, dbSslModeChanged, codeAgentRuntimeChanged };",
"}",
"function patchKustomization() {",
" const file = path.join(runtimePath, 'kustomization.yaml');",
@@ -1750,7 +1787,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
"const externalPostgresChanged = patchExternalPostgres();",
"const healthContractChanged = patchHealthContract();",
"const publicExposureChanged = patchPublicExposure();",
"console.error(JSON.stringify({ event: 'unidesk-runtime-gitops-postprocess', ok: true, runtimePath, sourcePath, pathRelocated: sourcePath !== runtimePath, observabilityPrometheusOperator: overlay.observability ? overlay.observability.prometheusOperator : null, runtimeImageRewriteCount: (overlay.runtimeImageRewrites || []).length, kustomizationChanged, observabilityWorkloadsChanged: runtimeWorkloadsChanged.observabilityChanged, startupProbeChanged: runtimeWorkloadsChanged.startupProbeChanged, runtimeImageRewriteChanged: runtimeWorkloadsChanged.imageRewriteChanged, gitReadUrlChanged: runtimeWorkloadsChanged.gitReadUrlChanged, publicEndpointChanged: runtimeWorkloadsChanged.publicEndpointChanged, dbSslModeChanged: runtimeWorkloadsChanged.dbSslModeChanged, externalPostgresChanged, healthContractChanged, publicExposureChanged }));",
"console.error(JSON.stringify({ event: 'unidesk-runtime-gitops-postprocess', ok: true, runtimePath, sourcePath, pathRelocated: sourcePath !== runtimePath, observabilityPrometheusOperator: overlay.observability ? overlay.observability.prometheusOperator : null, runtimeImageRewriteCount: (overlay.runtimeImageRewrites || []).length, kustomizationChanged, observabilityWorkloadsChanged: runtimeWorkloadsChanged.observabilityChanged, startupProbeChanged: runtimeWorkloadsChanged.startupProbeChanged, runtimeImageRewriteChanged: runtimeWorkloadsChanged.imageRewriteChanged, gitReadUrlChanged: runtimeWorkloadsChanged.gitReadUrlChanged, publicEndpointChanged: runtimeWorkloadsChanged.publicEndpointChanged, dbSslModeChanged: runtimeWorkloadsChanged.dbSslModeChanged, codeAgentRuntimeChanged: runtimeWorkloadsChanged.codeAgentRuntimeChanged, externalPostgresChanged, healthContractChanged, publicExposureChanged }));",
"NODE_UNIDESK_RUNTIME_GITOPS_POSTPROCESS`;",
"}",
"function runtimeGitopsVerifyScript() {",
@@ -1758,6 +1795,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" runtimePath: overlay.runtimePath,",
" runtimeNamespace: overlay.runtimeNamespace,",
" externalPostgres: overlay.externalPostgres,",
" codeAgentRuntime: overlay.codeAgentRuntime,",
" publicExposure: overlay.publicExposure,",
" observability: overlay.observability,",
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
@@ -1803,6 +1841,11 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" const item = container.env.find((env) => env && env.name === name);",
" return item ? item.value : undefined;",
"}",
"function envSecretRef(container, name) {",
" if (!isObject(container) || !Array.isArray(container.env)) return {};",
" const item = container.env.find((env) => env && env.name === name);",
" return item && item.valueFrom && item.valueFrom.secretKeyRef ? item.valueFrom.secretKeyRef : {};",
"}",
"function isEnvReuseContainer(container) { return envValue(container, 'HWLAB_RUNTIME_MODE') === 'env-reuse-git-mirror-checkout' || envValue(container, 'HWLAB_BOOT_SH') !== undefined || envValue(container, 'HWLAB_BOOT_COMMIT') !== undefined; }",
"function workloadName(item) { return item && item.metadata && item.metadata.labels && item.metadata.labels['app.kubernetes.io/name'] ? String(item.metadata.labels['app.kubernetes.io/name']) : String(item && item.metadata && item.metadata.name || ''); }",
"function expectedPublicEndpoint(item) { return workloadName(item) === 'hwlab-cloud-web' ? overlay.publicWebUrl : overlay.publicApiUrl; }",
@@ -1814,7 +1857,17 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" const staleGitReadUrls = [];",
" const wrongPublicEndpoints = [];",
" const wrongDbSslModes = [];",
" const wrongCodeAgentRuntimeEnvs = [];",
" const rewriteSources = new Set((overlay.runtimeImageRewrites || []).map((item) => item && item.source).filter(Boolean));",
" const codeAgentRuntime = overlay.codeAgentRuntime;",
" function checkCodeAgentRuntimeValue(item, file, container, envName, expected) {",
" const value = envValue(container, envName);",
" if (value !== expected) wrongCodeAgentRuntimeEnvs.push({ ...workloadRef(item, file, container), envName, kind: 'value', expected, value: value ?? null });",
" }",
" function checkCodeAgentRuntimeSecret(item, file, container, envName, expectedSecret, expectedKey) {",
" const secretRef = envSecretRef(container, envName);",
" if (secretRef.name !== expectedSecret || secretRef.key !== expectedKey) wrongCodeAgentRuntimeEnvs.push({ ...workloadRef(item, file, container), envName, kind: 'secretKeyRef', expectedSecret, expectedKey, secret: secretRef.name ?? null, key: secretRef.key ?? null });",
" }",
" for (const file of yamlFiles(runtimePath)) {",
" if (path.basename(file) === 'kustomization.yaml') continue;",
" for (const doc of readYamlDocuments(file)) {",
@@ -1831,12 +1884,22 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
" if (publicEndpoint !== undefined && publicEndpoint !== expectedPublicEndpoint(item)) wrongPublicEndpoints.push({ ...workloadRef(item, file, container), value: publicEndpoint, expected: expectedPublicEndpoint(item) });",
" const dbSslMode = envValue(container, 'HWLAB_CLOUD_DB_SSL_MODE');",
" if (overlay.externalPostgres && overlay.externalPostgres.sslmode && dbSslMode !== undefined && dbSslMode !== overlay.externalPostgres.sslmode) wrongDbSslModes.push({ ...workloadRef(item, file, container), value: dbSslMode, expected: overlay.externalPostgres.sslmode });",
" if (codeAgentRuntime && codeAgentRuntime.enabled && workloadName(item) === 'hwlab-cloud-api' && container.name === 'hwlab-cloud-api') {",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_ADAPTER', String(codeAgentRuntime.adapter));",
" checkCodeAgentRuntimeValue(item, file, container, 'AGENTRUN_MGR_URL', String(codeAgentRuntime.managerUrl));",
" checkCodeAgentRuntimeSecret(item, file, container, 'AGENTRUN_API_KEY', String(codeAgentRuntime.apiKeySecretName), String(codeAgentRuntime.apiKeySecretKey));",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE', String(codeAgentRuntime.runnerNamespace));",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE', String(codeAgentRuntime.secretNamespace));",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_AGENTRUN_REPO_URL', String(codeAgentRuntime.repoUrl));",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID', String(codeAgentRuntime.providerId));",
" checkCodeAgentRuntimeValue(item, file, container, 'HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE', String(codeAgentRuntime.defaultProviderProfile));",
" }",
" }",
" if (Array.isArray(podSpec.volumes) && podSpec.volumes.some((volume) => volume && volume.name === 'hwlab-metrics-sidecar')) metricsRefs.push(workloadRef(item, file, { name: 'volume/hwlab-metrics-sidecar' }));",
" }",
" }",
" }",
" return { metricsRefs, missingStartupProbes, publicRuntimeImages, staleGitReadUrls, wrongPublicEndpoints, wrongDbSslModes };",
" return { metricsRefs, missingStartupProbes, publicRuntimeImages, staleGitReadUrls, wrongPublicEndpoints, wrongDbSslModes, wrongCodeAgentRuntimeEnvs };",
"}",
"const checks = [];",
"const workloadCheck = workloadChecks();",
@@ -1858,6 +1921,8 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
"checks.push('runtime-public-endpoint');",
"if (workloadCheck.wrongDbSslModes.length > 0) fail('runtime-db-ssl-mode-mismatch', { refs: workloadCheck.wrongDbSslModes.slice(0, 12), count: workloadCheck.wrongDbSslModes.length });",
"if (overlay.externalPostgres && overlay.externalPostgres.sslmode) checks.push('runtime-db-ssl-mode');",
"if (workloadCheck.wrongCodeAgentRuntimeEnvs.length > 0) fail('code-agent-runtime-env-mismatch', { refs: workloadCheck.wrongCodeAgentRuntimeEnvs.slice(0, 12), count: workloadCheck.wrongCodeAgentRuntimeEnvs.length });",
"if (overlay.codeAgentRuntime && overlay.codeAgentRuntime.enabled) checks.push('code-agent-runtime-env');",
"const pg = overlay.externalPostgres;",
"if (pg && pg.serviceName) {",
" const access = pg.runtimeAccess || { endpointAddress: pg.endpointAddress, port: pg.port };",