diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml index 3ffd6571..52feebd5 100644 --- a/deploy/deploy.yaml +++ b/deploy/deploy.yaml @@ -617,6 +617,8 @@ lanes: HWLAB_CLOUD_WEB_DISPLAY_TIME_ZONE: Asia/Shanghai HWLAB_CLOUD_WEB_DISPLAY_TIME_LOCALE: zh-CN HWLAB_CLOUD_WEB_DISPLAY_TIME_LABEL: 北京时间 + HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED: "true" + HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED: "false" HWLAB_CLOUD_WEB_OPENCODE_UPSTREAM_URL: http://opencode-server.hwlab-v03.svc.cluster.local:4096 HWLAB_CLOUD_WEB_OPENCODE_USERNAME: secretRef:hwlab-opencode-server-auth/username HWLAB_CLOUD_WEB_OPENCODE_PASSWORD: secretRef:hwlab-opencode-server-auth/password @@ -829,11 +831,19 @@ lanes: HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git HWLAB_KAFKA_ENABLED: "true" HWLAB_KAFKA_AGENTRUN_EVENT_CONSUME_ENABLED: "true" + HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED: "true" + HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED: "true" + HWLAB_KAFKA_TRANSACTIONAL_PROJECTOR_ENABLED: "false" + HWLAB_KAFKA_PROJECTION_OUTBOX_RELAY_ENABLED: "false" + HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED: "false" HWLAB_KAFKA_BOOTSTRAP_SERVERS: platform-infra-kafka-kafka-bootstrap.platform-infra.svc.cluster.local:9092 + HWLAB_KAFKA_STDIO_TOPIC: codex-stdio.raw.v1 HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC: agentrun.event.v1 HWLAB_KAFKA_EVENT_TOPIC: hwlab.event.v1 HWLAB_KAFKA_CLIENT_ID: hwlab-v03-cloud-api - HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID: hwlab-v03-agentrun-event-bridge + HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID: hwlab-v03-agentrun-event-direct-publish + HWLAB_KAFKA_PROJECTOR_GROUP_ID: hwlab-v03-agentrun-event-projector + HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID: hwlab-v03-workbench-live-sse HWLAB_KAFKA_PROJECTOR_HEARTBEAT_INTERVAL_MS: "2000" HWLAB_KAFKA_OUTBOX_RELAY_INTERVAL_MS: "250" HWLAB_KAFKA_OUTBOX_RELAY_BATCH_SIZE: "100" diff --git a/scripts/gitops-render.test.ts b/scripts/gitops-render.test.ts index fd07f93f..b1248245 100644 --- a/scripts/gitops-render.test.ts +++ b/scripts/gitops-render.test.ts @@ -635,11 +635,25 @@ test("v03 render keeps node identity as data instead of generated structure", as assert.equal(cloudApiEnv.get("HWLAB_CLOUD_DB_SSL_MODE"), "require"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_ENABLED"), "true"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_AGENTRUN_EVENT_CONSUME_ENABLED"), "true"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED"), "true"); + assert.equal(cloudApiEnv.get("HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED"), "true"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_TRANSACTIONAL_PROJECTOR_ENABLED"), "false"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_PROJECTION_OUTBOX_RELAY_ENABLED"), "false"); + assert.equal(cloudApiEnv.get("HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED"), "false"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_BOOTSTRAP_SERVERS"), "platform-infra-kafka-kafka-bootstrap.platform-infra.svc.cluster.local:9092"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_STDIO_TOPIC"), "codex-stdio.raw.v1"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC"), "agentrun.event.v1"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_EVENT_TOPIC"), "hwlab.event.v1"); assert.equal(cloudApiEnv.get("HWLAB_KAFKA_CLIENT_ID"), "hwlab-v03-cloud-api"); - assert.equal(cloudApiEnv.get("HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID"), "hwlab-v03-agentrun-event-bridge"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID"), "hwlab-v03-agentrun-event-direct-publish"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_PROJECTOR_GROUP_ID"), "hwlab-v03-agentrun-event-projector"); + assert.equal(cloudApiEnv.get("HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID"), "hwlab-v03-workbench-live-sse"); + assert.equal(cloudApiEnv.has("HWLAB_SESSION_COOKIE_DOMAIN"), false); + const cloudWeb = (workloadsJson.items ?? []).find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-cloud-web"); + const cloudWebContainer = collectContainersFromItem(cloudWeb).find((container) => container.name === "hwlab-cloud-web"); + const cloudWebEnv = new Map((cloudWebContainer?.env ?? []).map((entry) => [entry.name, entry.value])); + assert.equal(cloudWebEnv.get("HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED"), "true"); + assert.equal(cloudWebEnv.get("HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED"), "false"); assert.match(cloudApiEnv.get("HWLAB_ENVIRONMENT_IMAGE") ?? "", /hwlab-cloud-api-env:env-stale/u); assert.deepEqual(cloudApiEnvEntries.get("HWLAB_SECRET_PLANE_SMOKE")?.valueFrom?.secretKeyRef, { name: "hwlab-secret-plane-smoke", key: "password", optional: true }); assert.ok((cloudApi?.spec?.template?.spec?.volumes ?? []).some((volume) => volume.name === "hwpod-preinstalled-specs" && volume.configMap?.name === "hwlab-v03-hwpod-preinstalled-specs")); diff --git a/scripts/runtime-realtime-capabilities-config.test.mjs b/scripts/runtime-realtime-capabilities-config.test.mjs new file mode 100644 index 00000000..2a336105 --- /dev/null +++ b/scripts/runtime-realtime-capabilities-config.test.mjs @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { readStructuredFile } from "./src/structured-config.mjs"; + +const PRODUCT_CAPABILITIES = Object.freeze({ + HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED: "true", + HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED: "true", + HWLAB_KAFKA_TRANSACTIONAL_PROJECTOR_ENABLED: "false", + HWLAB_KAFKA_PROJECTION_OUTBOX_RELAY_ENABLED: "false", + HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED: "false" +}); + +const LIVE_KAFKA_CONTRACT = Object.freeze({ + HWLAB_KAFKA_STDIO_TOPIC: "codex-stdio.raw.v1", + HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC: "agentrun.event.v1", + HWLAB_KAFKA_EVENT_TOPIC: "hwlab.event.v1", + HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID: "hwlab-v03-agentrun-event-direct-publish", + HWLAB_KAFKA_PROJECTOR_GROUP_ID: "hwlab-v03-agentrun-event-projector", + HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID: "hwlab-v03-workbench-live-sse" +}); + +test("v03 YAML keeps pure Kafka realtime capabilities independently composable", async () => { + const deploy = await readStructuredFile(process.cwd(), "deploy/deploy.yaml"); + const cloudApi = deploy.lanes?.v03?.services?.find((service) => service.serviceId === "hwlab-cloud-api"); + const cloudWeb = deploy.lanes?.v03?.serviceDeclarations?.["hwlab-cloud-web"]; + assert.ok(cloudApi, "deploy.lanes.v03.services must declare hwlab-cloud-api"); + assert.ok(cloudWeb, "deploy.lanes.v03.serviceDeclarations must declare hwlab-cloud-web"); + + for (const [name, expected] of Object.entries(PRODUCT_CAPABILITIES)) { + assert.equal(cloudApi.env?.[name], expected, `${name} must be explicitly YAML-owned`); + } + for (const [name, expected] of Object.entries(LIVE_KAFKA_CONTRACT)) { + assert.equal(cloudApi.env?.[name], expected, `${name} must preserve the pure Kafka live contract`); + } + + assert.equal(cloudApi.env?.HWLAB_SESSION_COOKIE_DOMAIN, undefined, "internal and public origins require host-scoped session cookies"); + assert.notEqual(cloudApi.env?.HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID, "hwlab-v03-agentrun-event-bridge"); + assert.equal(cloudWeb.env?.HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED, "true"); + assert.equal(cloudWeb.env?.HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED, "false"); +});