- commit
- 7b39f32
- parent
- e91ac94
- author
- im_wower
- date
- 2026-03-29 03:18:46 +0800 CST
feat: expose recent session urls in describe
2 files changed,
+30,
-4
+17,
-0
1@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
2 import { randomUUID } from "node:crypto";
3 import { join } from "node:path";
4 import {
5+ buildArtifactRelativePath,
6 buildArtifactPublicUrl,
7 getArtifactContentType,
8 type ArtifactStore
9@@ -105,6 +106,8 @@ const SSE_RESPONSE_HEADERS = {
10 } as const;
11 const ALLOWED_ARTIFACT_SCOPES = new Set(["exec", "msg", "session"]);
12 const ARTIFACT_FILE_SEGMENT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
13+const RECENT_SESSIONS_TXT_ARTIFACT_PATH = buildArtifactRelativePath("session", "latest.txt");
14+const RECENT_SESSIONS_JSON_ARTIFACT_PATH = buildArtifactRelativePath("session", "latest.json");
15 const BROWSER_CLAUDE_PLATFORM = "claude";
16 const BROWSER_CLAUDE_ROOT_URL = "https://claude.ai/";
17 const BROWSER_CLAUDE_ORGANIZATIONS_PATH = "/api/organizations";
18@@ -1056,6 +1059,18 @@ function requireArtifactStore(artifactStore: ArtifactStore | null): ArtifactStor
19 return artifactStore;
20 }
21
22+function buildRecentSessionsArtifactUrls(artifactStore: ArtifactStore | null): {
23+ recent_sessions_json_url: string | null;
24+ recent_sessions_url: string | null;
25+} {
26+ const publicBaseUrl = artifactStore?.getPublicBaseUrl() ?? null;
27+
28+ return {
29+ recent_sessions_url: buildArtifactPublicUrl(publicBaseUrl, RECENT_SESSIONS_TXT_ARTIFACT_PATH),
30+ recent_sessions_json_url: buildArtifactPublicUrl(publicBaseUrl, RECENT_SESSIONS_JSON_ARTIFACT_PATH)
31+ };
32+}
33+
34 function summarizeTask(task: TaskRecord): JsonObject {
35 return {
36 task_id: task.taskId,
37@@ -4083,6 +4098,7 @@ async function handleDescribeRead(context: LocalApiRequestContext, version: stri
38 truth_source: "local sqlite control plane",
39 origin
40 },
41+ ...buildRecentSessionsArtifactUrls(context.artifactStore),
42 auth: buildHttpAuthData(snapshot),
43 system,
44 websocket: buildFirefoxWebSocketData(snapshot),
45@@ -4227,6 +4243,7 @@ async function handleScopedDescribeRead(
46 truth_source: "local sqlite control plane",
47 origin
48 },
49+ ...buildRecentSessionsArtifactUrls(context.artifactStore),
50 recommended_flow: [
51 "GET /describe/business",
52 "Optionally GET /v1/capabilities",
+13,
-4
1@@ -2,7 +2,7 @@
2
3 ## 状态
4
5-- 当前状态:`待开始`
6+- 当前状态:`已完成`
7 - 规模预估:`S`
8 - 依赖任务:无
9 - 建议执行者:`Codex` 或 `Claude`(改动小,边界清晰)
10@@ -69,19 +69,28 @@ URL 通过 `publicBaseUrl` 拼接,不硬编码域名。
11
12 ### 开始执行
13
14-- 执行者:
15-- 开始时间:
16+- 执行者:`Codex`
17+- 开始时间:`2026-03-29 03:13:59 CST`
18 - 状态变更:`待开始` → `进行中`
19
20 ### 完成摘要
21
22-- 完成时间:
23+- 完成时间:`2026-03-29 03:18:18 CST`
24 - 状态变更:`进行中` → `已完成`
25 - 修改了哪些文件:
26+ - `apps/conductor-daemon/src/local-api.ts`
27+ - `tasks/T-S047.md`
28 - 核心实现思路:
29+ - 在 `local-api.ts` 中新增 recent sessions URL helper,基于 `artifactStore.getPublicBaseUrl()` 和 session artifact 的固定相对路径生成 `recent_sessions_url` / `recent_sessions_json_url`
30+ - 将两个字段接入 `/describe` 与 `/describe/business` 响应,确保业务入口可直接暴露最近会话索引的可 fetch URL
31 - 跑了哪些测试:
32+ - `pnpm test`(`/Users/george/code/baa-conductor-describe-sessions-url/apps/conductor-daemon`)
33+ - `node --input-type=module -e '...'` 定向调用 `/describe`,确认返回 `https://conductor.makefile.so/artifact/session/latest.txt` 与 `https://conductor.makefile.so/artifact/session/latest.json`
34
35 ### 执行过程中遇到的问题
36
37+- 新建 worktree 初始缺少 `node_modules`,首次 `pnpm test` 因 `tsc` 不存在失败;执行 `pnpm install --frozen-lockfile` 后恢复正常验证流程
38+
39 ### 剩余风险
40
41+- 代码与本地验证已完成,但验收中的 `curl http://100.71.210.78:4317/describe` 仍依赖后续将本分支变更部署到目标实例