baa-conductor

git clone 

commit
0516ea9
parent
cc93ae7
author
im_wower
date
2026-03-29 01:35:17 +0800 CST
fix: stabilize conductor local api test snapshots
3 files changed,  +44, -15
M apps/conductor-daemon/src/index.test.js
+9, -0
 1@@ -204,6 +204,9 @@ async function createLocalApiFixture(options = {}) {
 2   });
 3 
 4   const snapshot = {
 5+    claudeCoded: {
 6+      localApiBase: null
 7+    },
 8     codexd: {
 9       localApiBase: null
10     },
11@@ -2595,6 +2598,9 @@ test("parseConductorCliRequest rejects unlisted or non-Tailscale local API hosts
12 
13 test("handleConductorHttpRequest keeps degraded runtimes observable but not ready", async () => {
14   const snapshot = {
15+    claudeCoded: {
16+      localApiBase: null
17+    },
18     codexd: {
19       localApiBase: null
20     },
21@@ -4941,6 +4947,9 @@ test("persistent live ingest survives restart and /v1/browser restores recent hi
22       restartedControlPlane.repository,
23       "local-shared-token",
24       {
25+        claudeCoded: {
26+          localApiBase: null
27+        },
28         codexd: {
29           localApiBase: null
30         },
M apps/conductor-daemon/src/local-api.ts
+19, -11
 1@@ -677,6 +677,14 @@ function normalizeOptionalString(value: string | null | undefined): string | nul
 2   return normalized === "" ? null : normalized;
 3 }
 4 
 5+function getSnapshotClaudeCodedLocalApiBase(snapshot: ConductorRuntimeApiSnapshot): string | null {
 6+  return normalizeOptionalString(snapshot.claudeCoded?.localApiBase) ?? null;
 7+}
 8+
 9+function getSnapshotCodexdLocalApiBase(snapshot: ConductorRuntimeApiSnapshot): string | null {
10+  return normalizeOptionalString(snapshot.codexd?.localApiBase) ?? null;
11+}
12+
13 function buildSuccessEnvelope(
14   requestId: string,
15   status: number,
16@@ -3286,7 +3294,7 @@ function buildCodexRouteCatalog(): JsonObject[] {
17 }
18 
19 function buildCodexProxyNotes(snapshot: ConductorRuntimeApiSnapshot): string[] {
20-  if (snapshot.codexd.localApiBase == null) {
21+  if (getSnapshotCodexdLocalApiBase(snapshot) == null) {
22     return [
23       "Codex routes stay unavailable until independent codexd is configured.",
24       `Set ${CODEXD_LOCAL_API_ENV} to the codexd local HTTP base URL before using /v1/codex.`
25@@ -3300,13 +3308,14 @@ function buildCodexProxyNotes(snapshot: ConductorRuntimeApiSnapshot): string[] {
26 }
27 
28 function buildCodexProxyData(snapshot: ConductorRuntimeApiSnapshot): JsonObject {
29+  const codexdLocalApiBase = getSnapshotCodexdLocalApiBase(snapshot);
30   return {
31     auth_mode: "local_network_only",
32     backend: "independent_codexd",
33-    enabled: snapshot.codexd.localApiBase != null,
34+    enabled: codexdLocalApiBase != null,
35     route_prefix: "/v1/codex",
36     routes: buildCodexRouteCatalog(),
37-    target_base_url: snapshot.codexd.localApiBase,
38+    target_base_url: codexdLocalApiBase,
39     transport: "local_http",
40     notes: buildCodexProxyNotes(snapshot)
41   };
42@@ -3379,7 +3388,7 @@ async function requestCodexd(
43   }
44 ): Promise<{ data: JsonValue; status: number }> {
45   const codexdLocalApiBase =
46-    normalizeOptionalString(context.codexdLocalApiBase) ?? context.snapshotLoader().codexd.localApiBase;
47+    normalizeOptionalString(context.codexdLocalApiBase) ?? getSnapshotCodexdLocalApiBase(context.snapshotLoader());
48 
49   if (codexdLocalApiBase == null) {
50     throw new LocalApiHttpError(
51@@ -5727,10 +5736,7 @@ async function handleCodexStatusRead(context: LocalApiRequestContext): Promise<C
52   return buildSuccessEnvelope(
53     context.requestId,
54     200,
55-    buildCodexStatusData(
56-      result.data,
57-      normalizeOptionalString(context.codexdLocalApiBase) ?? context.snapshotLoader().codexd.localApiBase ?? ""
58-    )
59+    buildCodexStatusData(result.data, normalizeOptionalString(context.codexdLocalApiBase) ?? getSnapshotCodexdLocalApiBase(context.snapshotLoader()) ?? "")
60   );
61 }
62 
63@@ -5806,7 +5812,8 @@ async function requestClaudeCoded(
64   }
65 ): Promise<{ data: JsonValue; status: number }> {
66   const claudeCodedLocalApiBase =
67-    normalizeOptionalString(context.claudeCodedLocalApiBase) ?? context.snapshotLoader().claudeCoded.localApiBase;
68+    normalizeOptionalString(context.claudeCodedLocalApiBase) ??
69+    getSnapshotClaudeCodedLocalApiBase(context.snapshotLoader());
70 
71   if (claudeCodedLocalApiBase == null) {
72     throw new LocalApiHttpError(
73@@ -6465,9 +6472,10 @@ export async function handleConductorHttpRequest(
74         browserRequestPolicy: context.browserRequestPolicy ?? null,
75         browserStateLoader: context.browserStateLoader ?? (() => null),
76         claudeCodedLocalApiBase:
77-          normalizeOptionalString(context.claudeCodedLocalApiBase) ?? context.snapshotLoader().claudeCoded.localApiBase,
78+          normalizeOptionalString(context.claudeCodedLocalApiBase) ??
79+          getSnapshotClaudeCodedLocalApiBase(context.snapshotLoader()),
80         codexdLocalApiBase:
81-          normalizeOptionalString(context.codexdLocalApiBase) ?? context.snapshotLoader().codexd.localApiBase,
82+          normalizeOptionalString(context.codexdLocalApiBase) ?? getSnapshotCodexdLocalApiBase(context.snapshotLoader()),
83         fetchImpl: context.fetchImpl ?? globalThis.fetch,
84         now: context.now ?? (() => Math.floor(Date.now() / 1000)),
85         params: matchedRoute.params,
M tasks/T-BUG-026.md
+16, -4
 1@@ -2,7 +2,7 @@
 2 
 3 ## 状态
 4 
 5-- 当前状态:`待开始`
 6+- 当前状态:`已完成`
 7 - 规模预估:`S`
 8 - 依赖任务:无
 9 - 建议执行者:`Claude`(需要理解 conductor-daemon 测试初始化流程,定位 config 注入缺失)
10@@ -101,21 +101,33 @@
11 
12 ### 开始执行
13 
14-- 执行者:
15-- 开始时间:
16+- 执行者:`Codex`
17+- 开始时间:`2026-03-29 01:30:13 CST`
18 - 状态变更:`待开始` → `进行中`
19 
20 ### 完成摘要
21 
22-- 完成时间:
23+- 完成时间:`2026-03-29 01:34:40 CST`
24 - 状态变更:`进行中` → `已完成`
25 - 修改了哪些文件:
26+  - `apps/conductor-daemon/src/local-api.ts`
27+  - `apps/conductor-daemon/src/index.test.js`
28+  - `tasks/T-BUG-026.md`
29 - 核心实现思路:
30+  - 给 `handleConductorHttpRequest` 及 codexd/claude-coded 代理读取增加快照空值保护,避免缺失可选 backend snapshot 字段时直接抛出 `TypeError`
31+  - 补全 `index.test.js` 里的本地 API 测试夹具与持久化重启场景快照,显式提供 `claudeCoded.localApiBase: null`
32+  - 保持测试语义不变,只修正 context/snapshot 初始化缺失与运行时代码的默认值保护
33 - 跑了哪些测试:
34+  - `pnpm build`
35+  - `pnpm -F @baa-conductor/conductor-daemon test`
36+  - `pnpm test`
37 
38 ### 执行过程中遇到的问题
39 
40 > 记录执行过程中遇到的阻塞、环境问题、临时绕过方案等。合并时由合并者判断是否需要修复或建新任务。
41 
42+- 新 worktree 初始没有安装依赖,首次运行 `pnpm -F @baa-conductor/conductor-daemon test` 因 `pnpm exec tsc` 找不到命令失败;在 worktree 内执行 `pnpm install` 后完成复现与修复验证。
43+
44 ### 剩余风险
45 
46+- 当前修复覆盖了测试夹具缺失 `claudeCoded.localApiBase` 的场景,并为 local-api 增加了缺省保护;若后续引入新的最小快照夹具,仍建议保持与 `ConductorRuntimeApiSnapshot` 一致的结构。