mosse-agent-bridge 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ export interface CcAttachment {
2
+ filename: string;
3
+ mediaType: string;
4
+ /** 文件内容 base64。 */
5
+ dataBase64: string;
6
+ }
7
+ /**
8
+ * 把附件写到 `<cwd>/attachments/`,返回追加进 prompt 的引用文本(列出本地路径 + mediaType)。
9
+ * 无附件 → 返回 ""。CLI 据路径自行读取(图/文件就在它的 cwd 内)。
10
+ */
11
+ export declare function materializeAttachments(cwd: string, attachments?: CcAttachment[]): string;
@@ -0,0 +1,30 @@
1
+ // local:* 多模态(M-V5):把 server 下发的附件物化到 agent cwd,供 Claude Code CLI 按路径读。
2
+ // daemon 独立包(只依赖 node + ws),故附件类型在此自定义(与 @mosse/shared.Attachment 同形)。
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ /** 文件名消毒:只留字母数字 . _ -,折叠连续点(杜绝 .. 穿越),去开头的 ._-。 */
6
+ function safeName(name) {
7
+ const base = (name || "attachment")
8
+ .replace(/[^\w.-]/g, "_") // 分隔符等非安全字符 → _(无 / 即不可能逃出 attachments/)
9
+ .replace(/\.{2,}/g, "_") // 连续点(含 ..)→ _
10
+ .replace(/^[._-]+/, ""); // 去开头的 . _ -
11
+ return base || "attachment";
12
+ }
13
+ /**
14
+ * 把附件写到 `<cwd>/attachments/`,返回追加进 prompt 的引用文本(列出本地路径 + mediaType)。
15
+ * 无附件 → 返回 ""。CLI 据路径自行读取(图/文件就在它的 cwd 内)。
16
+ */
17
+ export function materializeAttachments(cwd, attachments) {
18
+ if (!attachments || attachments.length === 0)
19
+ return "";
20
+ const dir = join(cwd, "attachments");
21
+ mkdirSync(dir, { recursive: true });
22
+ const lines = [];
23
+ for (const a of attachments) {
24
+ const path = join(dir, safeName(a.filename));
25
+ writeFileSync(path, Buffer.from(a.dataBase64, "base64"));
26
+ lines.push(`- ${path} (${a.mediaType})`);
27
+ }
28
+ return `\n\n[Attached files have been saved locally for you to read from these paths]\n${lines.join("\n")}`;
29
+ }
30
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,mDAAmD;AACnD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,YAAY,CAAC;SAChC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,yCAAyC;SAClE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,eAAe;SACvC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;IACxC,OAAO,IAAI,IAAI,YAAY,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,WAA4B;IAC9E,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACrC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,kFAAkF,KAAK,CAAC,IAAI,CACjG,IAAI,CACL,EAAE,CAAC;AACN,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type BridgeChatMessage } from "./prompt.js";
2
+ export interface ClaudeTask {
3
+ systemPrompt: string;
4
+ messages: BridgeChatMessage[];
5
+ model?: string | null;
6
+ /** 注入到 CLI 进程环境的变量(ADVANCED · ENVIRONMENT VARIABLES)。 */
7
+ env?: Record<string, string>;
8
+ }
9
+ export declare function runClaude(task: ClaudeTask): Promise<string>;
package/dist/claude.js ADDED
@@ -0,0 +1,42 @@
1
+ // 调本机 Claude Code(print 模式)产出回复。用用户自己的 plan,不走 API。
2
+ // claude -p "<prompt>" --output-format json [--model X] [--append-system-prompt S]
3
+ import { execFile } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { buildPrompt } from "./prompt.js";
6
+ const execFileAsync = promisify(execFile);
7
+ export async function runClaude(task) {
8
+ const bin = process.env.MOSSE_CLAUDE_BIN ?? "claude";
9
+ const prompt = buildPrompt(task.messages);
10
+ const args = ["-p", prompt, "--output-format", "json"];
11
+ if (task.model)
12
+ args.push("--model", task.model);
13
+ if (task.systemPrompt)
14
+ args.push("--append-system-prompt", task.systemPrompt);
15
+ let stdout = "";
16
+ try {
17
+ ({ stdout } = await execFileAsync(bin, args, {
18
+ maxBuffer: 20 * 1024 * 1024,
19
+ env: { ...process.env, ...(task.env ?? {}) },
20
+ }));
21
+ }
22
+ catch (e) {
23
+ const err = e;
24
+ if (err.code === "ENOENT") {
25
+ throw new Error(`'${bin}' not found — is Claude Code installed on this machine?`);
26
+ }
27
+ // 退出码非 0 但可能仍在 stdout 打了 json
28
+ if (err.stdout)
29
+ stdout = err.stdout;
30
+ else
31
+ throw e;
32
+ }
33
+ // print --output-format json:取 .result(最终文本)。
34
+ try {
35
+ const parsed = JSON.parse(stdout);
36
+ return (parsed.result ?? parsed.text ?? "").toString().trim();
37
+ }
38
+ catch {
39
+ return stdout.trim(); // 兜底:直接当纯文本
40
+ }
41
+ }
42
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,qFAAqF;AACrF,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAA0B,WAAW,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAU1C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAgB;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,QAAQ,CAAC;IACrD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,IAAI,IAAI,CAAC,YAAY;QAAE,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAE9E,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC;QACH,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE;YAC3C,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;YAC3B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;SAC7C,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAuC,CAAC;QACpD,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,IAAI,GAAG,yDAAyD,CAAC,CAAC;QACpF,CAAC;QACD,8BAA8B;QAC9B,IAAI,GAAG,CAAC,MAAM;YAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;;YAC/B,MAAM,CAAC,CAAC;IACf,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAuC,CAAC;QACxE,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,YAAY;IACpC,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ export declare function credentialsPath(dataDir: string): string;
2
+ /**
3
+ * 凭证按 **server 分键**:token 是 server 专属的(各 server 自己签发、绑到它库里的 computer 行),
4
+ * 同一台机器来回连本地/线上不该互相覆盖。key = server 的 host(含端口)。
5
+ * agents/ 记忆与 cc-sessions 按全局唯一 agentId 存,跨 server 不撞、共享、不丢(故不分键)。
6
+ */
7
+ export declare function serverKey(serverUrl: string): string;
8
+ /** 读某个 server 的所有 per-attachment token(每条 = 一条挂载连接);无 → []。 */
9
+ export declare function loadTokens(dataDir: string, server: string): string[];
10
+ /** 读某个 server 的首条 token(单连接/兼容用);无 → null。 */
11
+ export declare function loadToken(dataDir: string, server: string): string | null;
12
+ /** 追加一条 token(去重);合并进表、不动其它 server,权限 600。 */
13
+ export declare function addToken(dataDir: string, server: string, token: string): void;
14
+ /** 移除某 server 下的**一条** token(该挂载 token 失效/解挂);其它连接/server 不受影响。 */
15
+ export declare function removeToken(dataDir: string, server: string, token: string): void;
16
+ /** 清掉某个 server 的**全部**凭证(整机从该 server 移除);其它 server 不受影响。 */
17
+ export declare function clearToken(dataDir: string, server: string): void;
18
+ /**
19
+ * 稳定机器标识:持久化在 <dataDir>/machine-id。
20
+ * 同一台机器、同一份安装 → 同一个 id(配对去重用)。删 dataDir 才会变(那时记忆也没了,算新机器)。
21
+ */
22
+ export declare function ensureMachineId(dataDir: string): string;
23
+ /** 从 ws daemon URL 推出 HTTP origin(配对接口用):ws→http、wss→https、去掉路径。 */
24
+ export declare function httpBaseFromWs(serverUrl: string): string;
@@ -0,0 +1,123 @@
1
+ // daemon 凭证(host token)管理:本地文件读写(600)+ 取值优先级 + 从 ws URL 推 http base。
2
+ // 设备登录流见 docs/agent-runtime-m3-device-login.md。
3
+ import { randomUUID } from "node:crypto";
4
+ import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ export function credentialsPath(dataDir) {
7
+ return join(dataDir, "credentials.json");
8
+ }
9
+ /**
10
+ * 凭证按 **server 分键**:token 是 server 专属的(各 server 自己签发、绑到它库里的 computer 行),
11
+ * 同一台机器来回连本地/线上不该互相覆盖。key = server 的 host(含端口)。
12
+ * agents/ 记忆与 cc-sessions 按全局唯一 agentId 存,跨 server 不撞、共享、不丢(故不分键)。
13
+ */
14
+ export function serverKey(serverUrl) {
15
+ try {
16
+ return new URL(serverUrl).host;
17
+ }
18
+ catch {
19
+ return serverUrl;
20
+ }
21
+ }
22
+ /** 读整个凭证表;旧的扁平格式 `{ token }`(无 server 分键)无法判定属于哪个 server → 忽略(触发各 server 重新配对一次)。 */
23
+ function readAll(dataDir) {
24
+ try {
25
+ const parsed = JSON.parse(readFileSync(credentialsPath(dataDir), "utf8"));
26
+ if (!parsed || typeof parsed !== "object")
27
+ return {};
28
+ // 旧格式:顶层就是 { token: "..." } → 不知归属哪个 server,丢弃。
29
+ if (typeof parsed.token === "string")
30
+ return {};
31
+ return parsed;
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ function writeAll(dataDir, all) {
38
+ const p = credentialsPath(dataDir);
39
+ mkdirSync(dirname(p), { recursive: true });
40
+ writeFileSync(p, `${JSON.stringify(all, null, 2)}\n`, { mode: 0o600 });
41
+ chmodSync(p, 0o600); // 确保已存在的旧文件也收紧
42
+ }
43
+ /** 某 server 条目的 token 列表(就地迁移 per-server 旧 `{ token }` 单条 → 列表)。 */
44
+ function entryTokens(entry) {
45
+ if (!entry)
46
+ return [];
47
+ if (Array.isArray(entry.tokens))
48
+ return entry.tokens.filter((t) => typeof t === "string" && t);
49
+ if (typeof entry.token === "string" && entry.token)
50
+ return [entry.token];
51
+ return [];
52
+ }
53
+ /** 读某个 server 的所有 per-attachment token(每条 = 一条挂载连接);无 → []。 */
54
+ export function loadTokens(dataDir, server) {
55
+ return entryTokens(readAll(dataDir)[serverKey(server)]);
56
+ }
57
+ /** 读某个 server 的首条 token(单连接/兼容用);无 → null。 */
58
+ export function loadToken(dataDir, server) {
59
+ return loadTokens(dataDir, server)[0] ?? null;
60
+ }
61
+ /** 追加一条 token(去重);合并进表、不动其它 server,权限 600。 */
62
+ export function addToken(dataDir, server, token) {
63
+ const all = readAll(dataDir);
64
+ const key = serverKey(server);
65
+ const tokens = entryTokens(all[key]);
66
+ if (!tokens.includes(token))
67
+ tokens.push(token);
68
+ all[key] = { tokens };
69
+ writeAll(dataDir, all);
70
+ }
71
+ /** 移除某 server 下的**一条** token(该挂载 token 失效/解挂);其它连接/server 不受影响。 */
72
+ export function removeToken(dataDir, server, token) {
73
+ const all = readAll(dataDir);
74
+ const key = serverKey(server);
75
+ if (!(key in all))
76
+ return;
77
+ const tokens = entryTokens(all[key]).filter((t) => t !== token);
78
+ if (tokens.length === 0)
79
+ delete all[key];
80
+ else
81
+ all[key] = { tokens };
82
+ if (Object.keys(all).length === 0)
83
+ rmSync(credentialsPath(dataDir), { force: true });
84
+ else
85
+ writeAll(dataDir, all);
86
+ }
87
+ /** 清掉某个 server 的**全部**凭证(整机从该 server 移除);其它 server 不受影响。 */
88
+ export function clearToken(dataDir, server) {
89
+ const all = readAll(dataDir);
90
+ if (!(serverKey(server) in all))
91
+ return;
92
+ delete all[serverKey(server)];
93
+ if (Object.keys(all).length === 0)
94
+ rmSync(credentialsPath(dataDir), { force: true });
95
+ else
96
+ writeAll(dataDir, all);
97
+ }
98
+ /**
99
+ * 稳定机器标识:持久化在 <dataDir>/machine-id。
100
+ * 同一台机器、同一份安装 → 同一个 id(配对去重用)。删 dataDir 才会变(那时记忆也没了,算新机器)。
101
+ */
102
+ export function ensureMachineId(dataDir) {
103
+ const p = join(dataDir, "machine-id");
104
+ try {
105
+ const id = readFileSync(p, "utf8").trim();
106
+ if (id)
107
+ return id;
108
+ }
109
+ catch {
110
+ // 不存在 / 损坏:生成新的。
111
+ }
112
+ const id = randomUUID();
113
+ mkdirSync(dirname(p), { recursive: true });
114
+ writeFileSync(p, `${id}\n`, { mode: 0o600 });
115
+ return id;
116
+ }
117
+ /** 从 ws daemon URL 推出 HTTP origin(配对接口用):ws→http、wss→https、去掉路径。 */
118
+ export function httpBaseFromWs(serverUrl) {
119
+ const u = new URL(serverUrl);
120
+ u.protocol = u.protocol === "wss:" ? "https:" : "http:";
121
+ return u.origin;
122
+ }
123
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { CliDriver } from "./types.js";
2
+ export declare const claudeDriver: CliDriver;
@@ -0,0 +1,120 @@
1
+ // Claude Code CLI driver:直驱 `claude` 的 stream-json 协议(取代 Agent SDK)。
2
+ // claude --print --input-format stream-json --output-format stream-json --verbose
3
+ // --include-partial-messages --permission-mode bypassPermissions
4
+ // [--model X] [--append-system-prompt S] [--resume <sid>]
5
+ // stdin 喂一帧 user 消息;stdout 逐行 stream-json → reduceMessage 归一为 CcEvent + sessionId。
6
+ import { spawn } from "node:child_process";
7
+ import { dirname } from "node:path";
8
+ import { createInterface } from "node:readline";
9
+ import { buildSpawnEnv } from "./env.js";
10
+ import { defaultResolveDeps, resolveClaudeBin } from "./resolve.js";
11
+ import { reduceMessage } from "./stream-json.js";
12
+ const CLAUDE_BIN = process.env.MOSSE_CLAUDE_BIN ?? "claude";
13
+ const NODE_DIR = dirname(process.execPath);
14
+ function errMsg(e) {
15
+ return e instanceof Error ? e.message : String(e);
16
+ }
17
+ /** 把 stderr 附到错误信息后(claude 的 "exited with code 1" 真因在 stderr)。 */
18
+ function withStderr(e, stderr) {
19
+ const s = stderr.trim();
20
+ return s ? new Error(`${e.message}\n${s.slice(-800)}`) : e;
21
+ }
22
+ function attempt(turn, resumeId, onEvent) {
23
+ return new Promise((resolve, reject) => {
24
+ // 剥离 daemon 机密 + 合并 agent 白名单 + 补全 PATH;再把 claude 解析成绝对路径(含 Desktop 兜底)。
25
+ const env = buildSpawnEnv({ base: process.env, inject: turn.env, nodeDir: NODE_DIR });
26
+ const bin = resolveClaudeBin(CLAUDE_BIN, defaultResolveDeps(env)) ?? CLAUDE_BIN;
27
+ const args = [
28
+ "--print",
29
+ "--input-format",
30
+ "stream-json",
31
+ "--output-format",
32
+ "stream-json",
33
+ "--verbose",
34
+ "--include-partial-messages",
35
+ "--permission-mode",
36
+ "bypassPermissions",
37
+ ];
38
+ if (turn.model)
39
+ args.push("--model", turn.model);
40
+ if (turn.systemPromptAppend)
41
+ args.push("--append-system-prompt", turn.systemPromptAppend);
42
+ // 知识 MCP(HTTP + bearer):加载我们的服务;bypassPermissions 下工具自动放行。
43
+ if (turn.mcpConfig)
44
+ args.push("--mcp-config", turn.mcpConfig);
45
+ if (resumeId)
46
+ args.push("--resume", resumeId);
47
+ const child = spawn(bin, args, {
48
+ cwd: turn.cwd,
49
+ env,
50
+ stdio: ["pipe", "pipe", "pipe"],
51
+ });
52
+ const state = { sessionId: resumeId, text: "" };
53
+ let stderr = "";
54
+ let failed = null;
55
+ child.stderr.on("data", (d) => {
56
+ const s = d.toString();
57
+ stderr += s;
58
+ process.stderr.write(`[claude stderr] ${s}`);
59
+ });
60
+ const rl = createInterface({ input: child.stdout });
61
+ rl.on("line", (line) => {
62
+ const t = line.trim();
63
+ if (!t)
64
+ return;
65
+ let m;
66
+ try {
67
+ m = JSON.parse(t);
68
+ }
69
+ catch {
70
+ return; // 忽略非 JSON 行(横幅/提示等)
71
+ }
72
+ try {
73
+ for (const ev of reduceMessage(m, state))
74
+ onEvent(ev);
75
+ }
76
+ catch (e) {
77
+ failed = e;
78
+ child.kill();
79
+ }
80
+ });
81
+ child.on("error", (e) => reject(new Error(`Failed to spawn ${CLAUDE_BIN} process: ${errMsg(e)}`)));
82
+ child.on("close", (code) => {
83
+ if (failed)
84
+ return reject(withStderr(failed, stderr));
85
+ if (code !== 0 && !state.text) {
86
+ return reject(withStderr(new Error(`claude exited with code ${code}`), stderr));
87
+ }
88
+ resolve({ text: state.text.trim(), sessionId: state.sessionId });
89
+ });
90
+ // 送一帧 user 消息后关 stdin。
91
+ const frame = JSON.stringify({
92
+ type: "user",
93
+ message: { role: "user", content: [{ type: "text", text: turn.prompt }] },
94
+ });
95
+ child.stdin.write(`${frame}\n`);
96
+ child.stdin.end();
97
+ });
98
+ }
99
+ export const claudeDriver = {
100
+ id: "claude-code", // harness 裸 id(见 @mosse/shared harness-catalog);旧 "local:claude-code" 已退役
101
+ async detect() {
102
+ const env = buildSpawnEnv({ base: process.env, nodeDir: NODE_DIR });
103
+ const bin = resolveClaudeBin(CLAUDE_BIN, defaultResolveDeps(env));
104
+ return bin ? { id: "claude-code", bin } : null;
105
+ },
106
+ async run(turn, onEvent) {
107
+ try {
108
+ return await attempt(turn, turn.sessionId ?? null, onEvent);
109
+ }
110
+ catch (e) {
111
+ // resume 失效(daemon 重启 / 会话与目录对不上等)→ 丢旧会话,新开一个重试一次。
112
+ if (turn.sessionId) {
113
+ console.error(`resume failed (${errMsg(e)}); retrying with a new session…`);
114
+ return attempt(turn, null, onEvent);
115
+ }
116
+ throw e;
117
+ }
118
+ },
119
+ };
120
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/drivers/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** daemon 机密 env 名单:spawn agent 前从子进程 env 删掉。新增 daemon 机密往这里加。
2
+ * 注意:agent 自己要用的密钥走文件(secrets.json)或 inject 白名单,不在此列。 */
3
+ export declare const ENV_DENYLIST: string[];
4
+ /** node 目录 + 常见 bin 前置到 PATH(去重)。让 claude / 其 shebang node / agent 内部命令都能找到。 */
5
+ export declare function augmentPath(basePath: string | undefined, nodeDir: string): string;
6
+ /** 构造 agent 子进程 env:base 去 denylist → 合并 inject 白名单 → 补全 PATH。 */
7
+ export declare function buildSpawnEnv(opts: {
8
+ base: NodeJS.ProcessEnv;
9
+ inject?: Record<string, string>;
10
+ nodeDir: string;
11
+ }): NodeJS.ProcessEnv;
@@ -0,0 +1,34 @@
1
+ // 给 agent 子进程构造环境:① 剥离 daemon 自己的机密(防漏进 agent 上下文,defense by exclusion);
2
+ // ② 合并 agent 注入的白名单;③ 补全 PATH(node 目录 + 常见 bin)。见 docs §9.3 / §7。
3
+ /** daemon 机密 env 名单:spawn agent 前从子进程 env 删掉。新增 daemon 机密往这里加。
4
+ * 注意:agent 自己要用的密钥走文件(secrets.json)或 inject 白名单,不在此列。 */
5
+ export const ENV_DENYLIST = ["MOSSE_DAEMON_TOKEN", "MOSSE_BRIDGE_TOKEN"];
6
+ /** node 目录 + 常见 bin 前置到 PATH(去重)。让 claude / 其 shebang node / agent 内部命令都能找到。 */
7
+ export function augmentPath(basePath, nodeDir) {
8
+ const seen = new Set();
9
+ const parts = [];
10
+ for (const p of [
11
+ nodeDir,
12
+ "/opt/homebrew/bin",
13
+ "/usr/local/bin",
14
+ ...(basePath?.split(":") ?? []),
15
+ "/usr/bin",
16
+ "/bin",
17
+ ]) {
18
+ if (p && !seen.has(p)) {
19
+ seen.add(p);
20
+ parts.push(p);
21
+ }
22
+ }
23
+ return parts.join(":");
24
+ }
25
+ /** 构造 agent 子进程 env:base 去 denylist → 合并 inject 白名单 → 补全 PATH。 */
26
+ export function buildSpawnEnv(opts) {
27
+ const out = { ...opts.base };
28
+ for (const k of ENV_DENYLIST)
29
+ delete out[k];
30
+ Object.assign(out, opts.inject ?? {});
31
+ out.PATH = augmentPath(opts.base.PATH, opts.nodeDir);
32
+ return out;
33
+ }
34
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/drivers/env.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,kEAAkE;AAElE;0DAC0D;AAC1D,MAAM,CAAC,MAAM,YAAY,GAAa,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;AAEnF,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,QAA4B,EAAE,OAAe;IACvE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI;QACd,OAAO;QACP,mBAAmB;QACnB,gBAAgB;QAChB,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC/B,UAAU;QACV,MAAM;KACP,EAAE,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,IAI7B;IACC,MAAM,GAAG,GAAsB,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,YAAY;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACtC,GAAG,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CliDriver } from "./types.js";
2
+ /** 取某 runtime 的 driver;未注册返回 null。 */
3
+ export declare function getDriver(id: string): CliDriver | null;
4
+ export type { AgentTurn, CcEvent, TurnResult, CliDriver } from "./types.js";
@@ -0,0 +1,12 @@
1
+ // 运行时分发:按 runtime id 选 driver。codex/gemini 之后在此注册(预留位)。
2
+ import { claudeDriver } from "./claude.js";
3
+ const DRIVERS = {
4
+ [claudeDriver.id]: claudeDriver,
5
+ // "local:codex": codexDriver, // 未来
6
+ // "local:gemini": geminiDriver, // 未来
7
+ };
8
+ /** 取某 runtime 的 driver;未注册返回 null。 */
9
+ export function getDriver(id) {
10
+ return DRIVERS[id] ?? null;
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drivers/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,OAAO,GAA8B;IACzC,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,YAAY;IAC/B,uCAAuC;IACvC,uCAAuC;CACxC,CAAC;AAEF,sCAAsC;AACtC,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ export interface ResolveDeps {
2
+ /** 在 PATH 上找命令,返回绝对路径或 null。 */
3
+ which: (cmd: string) => string | null;
4
+ existsSync: (p: string) => boolean;
5
+ platform: NodeJS.Platform;
6
+ home: string;
7
+ /** 登录 shell 兜底(`$SHELL -lc 'command -v cmd'`),拿用户完整 PATH;可选。 */
8
+ loginShellWhich?: (cmd: string) => string | null;
9
+ }
10
+ /** 用给定 env 在 PATH 上解析命令(`which`)。失败返回 null。 */
11
+ export declare function whichWith(env: NodeJS.ProcessEnv): (cmd: string) => string | null;
12
+ /** 登录 shell 兜底:`$SHELL -lc 'command -v <cmd>'`(固定命令名,白名单防注入,带超时)。 */
13
+ export declare function loginShellWhich(): (cmd: string) => string | null;
14
+ /** 解析 claude 绝对路径:PATH → 已知路径 → 登录 shell → macOS Claude Desktop。找不到返回 null。 */
15
+ export declare function resolveClaudeBin(cmd: string, deps: ResolveDeps): string | null;
16
+ /** 生产默认 deps(用补全过 PATH 的 env 跑 which + 登录 shell 兜底)。 */
17
+ export declare function defaultResolveDeps(env: NodeJS.ProcessEnv): ResolveDeps;
@@ -0,0 +1,81 @@
1
+ // 把 CLI 解析成绝对路径(不依赖子进程 PATH 行为,根治 spawn ENOENT)。
2
+ // 解析顺序(§D-5 确定性,GUI 进程 PATH 稀疏也能找到):
3
+ // 1) which(补全过的 PATH)2) 已知绝对路径候选 3) 登录 shell 兜底 4) macOS Claude Desktop。
4
+ import { execFileSync } from "node:child_process";
5
+ import { existsSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ const CLAUDE_DESKTOP_RELATIVE = "Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
9
+ const CLAUDE_DESKTOP_SYSTEM = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
10
+ /** 用给定 env 在 PATH 上解析命令(`which`)。失败返回 null。 */
11
+ export function whichWith(env) {
12
+ return (cmd) => {
13
+ try {
14
+ const out = execFileSync("which", [cmd], { env, encoding: "utf8" }).trim().split("\n")[0];
15
+ return out || null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ };
21
+ }
22
+ /** 登录 shell 兜底:`$SHELL -lc 'command -v <cmd>'`(固定命令名,白名单防注入,带超时)。 */
23
+ export function loginShellWhich() {
24
+ return (cmd) => {
25
+ if (!/^[a-zA-Z0-9_-]+$/.test(cmd))
26
+ return null;
27
+ const shell = process.env.SHELL || "/bin/zsh";
28
+ try {
29
+ const out = execFileSync(shell, ["-lc", `command -v ${cmd}`], {
30
+ encoding: "utf8",
31
+ timeout: 5000,
32
+ })
33
+ .trim()
34
+ .split("\n")
35
+ .pop();
36
+ return out?.startsWith("/") ? out : null;
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ };
42
+ }
43
+ /** 已知绝对路径候选(GUI 进程 PATH 稀疏时按此找)。 */
44
+ function knownPaths(cmd, home) {
45
+ return [
46
+ join(home, ".local/bin", cmd),
47
+ join(home, ".claude/local", cmd),
48
+ `/opt/homebrew/bin/${cmd}`,
49
+ `/usr/local/bin/${cmd}`,
50
+ join(home, ".bun/bin", cmd),
51
+ ];
52
+ }
53
+ /** 解析 claude 绝对路径:PATH → 已知路径 → 登录 shell → macOS Claude Desktop。找不到返回 null。 */
54
+ export function resolveClaudeBin(cmd, deps) {
55
+ const onPath = deps.which(cmd);
56
+ if (onPath)
57
+ return onPath;
58
+ const known = knownPaths(cmd, deps.home).find((p) => deps.existsSync(p));
59
+ if (known)
60
+ return known;
61
+ const viaShell = deps.loginShellWhich?.(cmd);
62
+ if (viaShell)
63
+ return viaShell;
64
+ if (deps.platform === "darwin") {
65
+ const desktop = [join(deps.home, CLAUDE_DESKTOP_RELATIVE), CLAUDE_DESKTOP_SYSTEM].find((p) => deps.existsSync(p));
66
+ if (desktop)
67
+ return desktop;
68
+ }
69
+ return null;
70
+ }
71
+ /** 生产默认 deps(用补全过 PATH 的 env 跑 which + 登录 shell 兜底)。 */
72
+ export function defaultResolveDeps(env) {
73
+ return {
74
+ which: whichWith(env),
75
+ existsSync,
76
+ platform: process.platform,
77
+ home: homedir(),
78
+ loginShellWhich: loginShellWhich(),
79
+ };
80
+ }
81
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/drivers/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { CcEvent } from "./types.js";
2
+ type SdkMessage = any;
3
+ /** 一轮累积状态:最新 session id + 最终文本。 */
4
+ export interface TurnState {
5
+ sessionId: string | null;
6
+ text: string;
7
+ }
8
+ /** 把工具调用压成一行人话摘要(读了哪个文件、跑了什么命令)。 */
9
+ export declare function summarizeTool(tool: string, input: unknown): string;
10
+ /**
11
+ * 消费一条 stream-json 消息:更新 state(sessionId / 最终文本),返回要发出的事件。
12
+ * - system/init → 记 session_id
13
+ * - stream_event/content_block_delta/text_delta → text 事件
14
+ * - assistant 的 tool_use 块 → activity 事件
15
+ * - result success → 最终文本;result is_error → 抛错
16
+ */
17
+ export declare function reduceMessage(m: SdkMessage, state: TurnState): CcEvent[];
18
+ export {};