team-anya-cli 0.1.8 → 1.0.1

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.
Files changed (168) hide show
  1. package/README.md +1 -37
  2. package/package.json +6 -37
  3. package/anya/prompts/execution-guides/git-delivery.md +0 -38
  4. package/anya/prompts/execution-guides/testing-and-self-heal.md +0 -28
  5. package/anya/prompts/protocols/brief-assembly.md +0 -55
  6. package/anya/prompts/protocols/report.md +0 -175
  7. package/anya/prompts/protocols/review.md +0 -90
  8. package/anya/prompts/task-claude-md.template.md +0 -32
  9. package/apps/server/dist/broker/cc-broker.js +0 -261
  10. package/apps/server/dist/cli.js +0 -296
  11. package/apps/server/dist/config.js +0 -76
  12. package/apps/server/dist/daemon.js +0 -51
  13. package/apps/server/dist/franky/context-builder.js +0 -160
  14. package/apps/server/dist/franky/franky-mcp-server.js +0 -107
  15. package/apps/server/dist/franky/franky-orchestrator.js +0 -450
  16. package/apps/server/dist/franky/index.js +0 -5
  17. package/apps/server/dist/franky/topic-router.js +0 -16
  18. package/apps/server/dist/gateway/chat-sync.js +0 -135
  19. package/apps/server/dist/gateway/command-router.js +0 -114
  20. package/apps/server/dist/gateway/commands/cancel.js +0 -32
  21. package/apps/server/dist/gateway/commands/help.js +0 -16
  22. package/apps/server/dist/gateway/commands/index.js +0 -26
  23. package/apps/server/dist/gateway/commands/restart.js +0 -34
  24. package/apps/server/dist/gateway/commands/status.js +0 -34
  25. package/apps/server/dist/gateway/commands/tasks.js +0 -33
  26. package/apps/server/dist/gateway/feishu-sender.js +0 -410
  27. package/apps/server/dist/gateway/feishu-ws.js +0 -256
  28. package/apps/server/dist/gateway/http.js +0 -1019
  29. package/apps/server/dist/gateway/media-downloader.js +0 -149
  30. package/apps/server/dist/gateway/message-events.js +0 -10
  31. package/apps/server/dist/gateway/message-intake.js +0 -67
  32. package/apps/server/dist/gateway/message-queue.js +0 -118
  33. package/apps/server/dist/gateway/session-reader.js +0 -142
  34. package/apps/server/dist/gateway/ws-push.js +0 -115
  35. package/apps/server/dist/loid/brain.js +0 -105
  36. package/apps/server/dist/loid/clarifier.js +0 -162
  37. package/apps/server/dist/loid/context-builder.js +0 -413
  38. package/apps/server/dist/loid/mcp-server.js +0 -106
  39. package/apps/server/dist/loid/memory-settler.js +0 -189
  40. package/apps/server/dist/loid/opportunity-manager.js +0 -148
  41. package/apps/server/dist/loid/profile-updater.js +0 -179
  42. package/apps/server/dist/loid/reporter.js +0 -148
  43. package/apps/server/dist/loid/schemas.js +0 -117
  44. package/apps/server/dist/loid/self-calibrator.js +0 -314
  45. package/apps/server/dist/loid/session-manager.js +0 -301
  46. package/apps/server/dist/loid/session.js +0 -271
  47. package/apps/server/dist/loid/worktree-manager.js +0 -191
  48. package/apps/server/dist/main.js +0 -393
  49. package/apps/server/dist/tracing/index.js +0 -2
  50. package/apps/server/dist/tracing/trace-context.js +0 -92
  51. package/apps/server/dist/types/message.js +0 -2
  52. package/apps/server/dist/yor/yor-mcp-server.js +0 -104
  53. package/apps/server/dist/yor/yor-orchestrator.js +0 -233
  54. package/apps/web/dist/assets/index-BiiEB0qZ.css +0 -1
  55. package/apps/web/dist/assets/index-D1AK5ZEE.js +0 -798
  56. package/apps/web/dist/index.html +0 -13
  57. package/packages/cc-client/dist/claude-code-backend.js +0 -664
  58. package/packages/cc-client/dist/index.js +0 -2
  59. package/packages/cc-client/package.json +0 -11
  60. package/packages/core/dist/constants.js +0 -59
  61. package/packages/core/dist/errors.js +0 -35
  62. package/packages/core/dist/index.js +0 -7
  63. package/packages/core/dist/office-init.js +0 -101
  64. package/packages/core/dist/scope/checker.js +0 -114
  65. package/packages/core/dist/scope/defaults.js +0 -55
  66. package/packages/core/dist/scope/index.js +0 -3
  67. package/packages/core/dist/state-machine.js +0 -85
  68. package/packages/core/dist/types/audit.js +0 -12
  69. package/packages/core/dist/types/backend.js +0 -2
  70. package/packages/core/dist/types/commitment.js +0 -17
  71. package/packages/core/dist/types/communication.js +0 -18
  72. package/packages/core/dist/types/index.js +0 -8
  73. package/packages/core/dist/types/opportunity.js +0 -27
  74. package/packages/core/dist/types/org.js +0 -26
  75. package/packages/core/dist/types/task.js +0 -46
  76. package/packages/core/package.json +0 -10
  77. package/packages/db/dist/client.js +0 -69
  78. package/packages/db/dist/index.js +0 -691
  79. package/packages/db/dist/schema/audit-events.js +0 -13
  80. package/packages/db/dist/schema/cc-sessions.js +0 -14
  81. package/packages/db/dist/schema/chats.js +0 -33
  82. package/packages/db/dist/schema/commitments.js +0 -18
  83. package/packages/db/dist/schema/communication-events.js +0 -14
  84. package/packages/db/dist/schema/index.js +0 -13
  85. package/packages/db/dist/schema/message-log.js +0 -20
  86. package/packages/db/dist/schema/opportunities.js +0 -23
  87. package/packages/db/dist/schema/org.js +0 -36
  88. package/packages/db/dist/schema/projects.js +0 -23
  89. package/packages/db/dist/schema/tasks.js +0 -48
  90. package/packages/db/dist/schema/topics.js +0 -20
  91. package/packages/db/dist/schema/trace-spans.js +0 -19
  92. package/packages/db/package.json +0 -12
  93. package/packages/db/src/migrations/0000_baseline.sql +0 -251
  94. package/packages/db/src/migrations/meta/_journal.json +0 -13
  95. package/packages/mcp-tools/dist/index.js +0 -41
  96. package/packages/mcp-tools/dist/layer1/audit-append.js +0 -38
  97. package/packages/mcp-tools/dist/layer1/audit-query.js +0 -51
  98. package/packages/mcp-tools/dist/layer1/memory-brief.js +0 -168
  99. package/packages/mcp-tools/dist/layer1/memory-context.js +0 -124
  100. package/packages/mcp-tools/dist/layer1/memory-digest.js +0 -126
  101. package/packages/mcp-tools/dist/layer1/memory-forget.js +0 -108
  102. package/packages/mcp-tools/dist/layer1/memory-learn.js +0 -63
  103. package/packages/mcp-tools/dist/layer1/memory-recall.js +0 -287
  104. package/packages/mcp-tools/dist/layer1/memory-reflect.js +0 -80
  105. package/packages/mcp-tools/dist/layer1/memory-remember.js +0 -119
  106. package/packages/mcp-tools/dist/layer1/memory-search.js +0 -263
  107. package/packages/mcp-tools/dist/layer1/memory-write.js +0 -21
  108. package/packages/mcp-tools/dist/layer1/org-lookup.js +0 -47
  109. package/packages/mcp-tools/dist/layer1/project-get.js +0 -28
  110. package/packages/mcp-tools/dist/layer1/project-list.js +0 -20
  111. package/packages/mcp-tools/dist/layer1/report-daily.js +0 -68
  112. package/packages/mcp-tools/dist/layer1/task-get.js +0 -29
  113. package/packages/mcp-tools/dist/layer1/task-update.js +0 -34
  114. package/packages/mcp-tools/dist/layer2/franky/topic-checkpoint.js +0 -43
  115. package/packages/mcp-tools/dist/layer2/franky/topic-escalate.js +0 -19
  116. package/packages/mcp-tools/dist/layer2/loid/decision-log.js +0 -15
  117. package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +0 -15
  118. package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +0 -30
  119. package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +0 -12
  120. package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +0 -77
  121. package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +0 -18
  122. package/packages/mcp-tools/dist/layer2/loid/project-remove.js +0 -16
  123. package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +0 -33
  124. package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +0 -196
  125. package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +0 -38
  126. package/packages/mcp-tools/dist/layer2/loid/topic-close.js +0 -22
  127. package/packages/mcp-tools/dist/layer2/loid/topic-create.js +0 -56
  128. package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +0 -8
  129. package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +0 -7
  130. package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +0 -7
  131. package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +0 -15
  132. package/packages/mcp-tools/dist/layer2/loid/yor-status.js +0 -8
  133. package/packages/mcp-tools/dist/layer2/yor/task-block.js +0 -11
  134. package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +0 -35
  135. package/packages/mcp-tools/dist/layer2/yor/task-progress.js +0 -21
  136. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +0 -192
  137. package/packages/mcp-tools/dist/layer3/adapters/types.js +0 -28
  138. package/packages/mcp-tools/dist/layer3/channel-receive.js +0 -11
  139. package/packages/mcp-tools/dist/layer3/channel-send.js +0 -91
  140. package/packages/mcp-tools/dist/layer3/file-upload.js +0 -44
  141. package/packages/mcp-tools/dist/registry.js +0 -871
  142. package/packages/mcp-tools/package.json +0 -13
  143. package/workspace/.claude/settings.local.json +0 -9
  144. package/workspace/.mcp.json +0 -12
  145. package/workspace/CHARTER.md +0 -76
  146. package/workspace/CLAUDE.md +0 -58
  147. package/workspace/PROTOCOL.md +0 -160
  148. package/workspace/TOOLS.md +0 -470
  149. package/workspace/audit/.gitkeep +0 -0
  150. package/workspace/franky/CLAUDE.md +0 -37
  151. package/workspace/franky/PLAYBOOK.md +0 -215
  152. package/workspace/franky/PROFILE.md +0 -80
  153. package/workspace/loid/CLAUDE.md +0 -12
  154. package/workspace/loid/PLAYBOOK.md +0 -198
  155. package/workspace/loid/PROFILE.md +0 -78
  156. package/workspace/memory/commitments/.gitkeep +0 -0
  157. package/workspace/memory/execution/.gitkeep +0 -0
  158. package/workspace/memory/people/.gitkeep +0 -0
  159. package/workspace/memory/projects/.gitkeep +0 -0
  160. package/workspace/memory/self/.gitkeep +0 -0
  161. package/workspace/reference/identity/.gitkeep +0 -0
  162. package/workspace/reference/org/escalation.yaml +0 -24
  163. package/workspace/reference/org/ownership.yaml +0 -28
  164. package/workspace/reports/.gitkeep +0 -0
  165. package/workspace/yor/CLAUDE.md +0 -22
  166. package/workspace/yor/PLAYBOOK.md +0 -73
  167. package/workspace/yor/PROFILE.md +0 -52
  168. package/workspace/yor/SELF-HEAL.md +0 -39
@@ -1,13 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Anya Gateway Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-D1AK5ZEE.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BiiEB0qZ.css">
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- </body>
13
- </html>
@@ -1,664 +0,0 @@
1
- import { spawn, execSync } from "node:child_process";
2
- import { randomUUID } from "node:crypto";
3
- import { readFileSync, writeFileSync, appendFileSync, unlinkSync, existsSync, mkdirSync, } from "node:fs";
4
- import { createServer } from "node:http";
5
- import { join } from "node:path";
6
- import { WebSocketServer, WebSocket } from "ws";
7
- import { ConnectionState } from "@team-anya/core";
8
- export class ClaudeCodeBackend {
9
- proc = null;
10
- ws = null;
11
- wsServer = null;
12
- httpServer = null;
13
- cliSessionId = null;
14
- config = null;
15
- verbose;
16
- // 事件回调
17
- toolRequestHandler = null;
18
- resultHandler = null;
19
- progressHandler = null;
20
- stateChangeHandler = null;
21
- sessionIdHandler = null;
22
- // system/init 信息(异步更新,供外部查询)
23
- initMessage = null;
24
- // 消息队列:CLI WS 未连接时暂存待发消息
25
- pendingMessages = [];
26
- // .mcp.json 管理:记录原始内容用于恢复
27
- mcpJsonPath = null;
28
- mcpJsonBackup = null; // null = 文件原本不存在
29
- mcpJsonCreated = false;
30
- // 原始对话日志:收集每次 prompt 执行期间的所有 NDJSON 消息
31
- rawConversationLog = [];
32
- // 日志文件路径
33
- logFile = null;
34
- constructor(options) {
35
- this.verbose = options?.verbose ?? false;
36
- this.logFile = options?.logFile ?? null;
37
- }
38
- /**
39
- * 日志输出:
40
- * - error: 始终输出到控制台 stderr
41
- * - warn/info: 如果配置了 logFile 则写文件,否则输出到控制台
42
- */
43
- log(level, ...args) {
44
- if (level === "info" && !this.verbose)
45
- return;
46
- const ts = new Date().toISOString();
47
- const prefix = `[cc-backend ${ts}]`;
48
- if (level === "error") {
49
- console.error(prefix, ...args);
50
- this.appendToLogFile(prefix, ...args);
51
- }
52
- else if (this.logFile) {
53
- // warn/info 日志写入文件,不输出到控制台
54
- this.appendToLogFile(prefix, ...args);
55
- }
56
- else if (level === "warn") {
57
- console.warn(prefix, ...args);
58
- }
59
- else {
60
- console.log(prefix, ...args);
61
- }
62
- }
63
- appendToLogFile(...parts) {
64
- if (!this.logFile)
65
- return;
66
- try {
67
- const line = parts.map(p => typeof p === 'string' ? p : JSON.stringify(p)).join(' ');
68
- appendFileSync(this.logFile, line + '\n');
69
- }
70
- catch {
71
- // 日志写入失败不影响主流程
72
- }
73
- }
74
- async connect(config) {
75
- this.config = config;
76
- this.initMessage = null;
77
- this.cliSessionId = null;
78
- this.pendingMessages = [];
79
- this.stateChangeHandler?.(ConnectionState.CONNECTING);
80
- this.log("info", "开始连接, binary:", config.binary ?? "claude", "workingDir:", config.workingDir);
81
- const port = await this.findFreePort();
82
- this.log("info", `WS 服务监听端口: ${port}`);
83
- // 1. 启动 WebSocket Server,等待 CLI 反连
84
- const wsReady = new Promise((resolve, reject) => {
85
- this.httpServer = createServer();
86
- this.wsServer = new WebSocketServer({ server: this.httpServer });
87
- this.wsServer.on("connection", (ws, req) => {
88
- this.log("warn", `CLI WebSocket 已连接, url=${req.url}, headers=${JSON.stringify(req.headers)}`);
89
- this.ws = ws;
90
- this.setupMessageHandler(ws);
91
- resolve(ws);
92
- });
93
- this.httpServer.on("listening", () => {
94
- this.log("warn", `HTTP server 已启动, 监听 127.0.0.1:${port}`);
95
- });
96
- this.httpServer.on("request", (req) => {
97
- this.log("warn", `[HTTP] 收到请求: ${req.method} ${req.url}`);
98
- });
99
- this.httpServer.on("upgrade", (req) => {
100
- this.log("warn", `[HTTP] 收到 upgrade 请求: ${req.url}, headers=${JSON.stringify(req.headers)}`);
101
- });
102
- this.httpServer.listen(port, "127.0.0.1");
103
- this.httpServer.on("error", (err) => {
104
- this.log("error", "HTTP server 错误:", err.message);
105
- reject(err);
106
- });
107
- });
108
- // 2. 启动 Claude Code CLI 子进程
109
- const sessionId = randomUUID();
110
- const sdkUrl = `ws://127.0.0.1:${port}/ws/cli/${sessionId}`;
111
- const args = [
112
- "--sdk-url",
113
- sdkUrl,
114
- "--print",
115
- "--output-format",
116
- "stream-json",
117
- "--input-format",
118
- "stream-json",
119
- "--verbose",
120
- ];
121
- if (config.model)
122
- args.push("--model", config.model);
123
- if (config.resumeSessionId)
124
- args.push("--resume", config.resumeSessionId);
125
- if (config.systemPrompt)
126
- args.push("--system-prompt", config.systemPrompt);
127
- if (config.maxTurns)
128
- args.push("--max-turns", String(config.maxTurns));
129
- if (config.dangerouslySkipPermissions) {
130
- args.push("--dangerously-skip-permissions");
131
- }
132
- else if (config.permissionMode) {
133
- args.push("--permission-mode", config.permissionMode);
134
- }
135
- args.push("-p", ""); // headless 模式占位(参照 companion)
136
- // MCP servers:写入 .mcp.json 到 workingDir(CLI 从 cwd 读取项目级配置)
137
- if (config.mcpServers && config.mcpServers.length > 0) {
138
- this.writeMcpJson(config.workingDir, config.mcpServers);
139
- }
140
- // 参照 companion: 用 which 解析 CLI 绝对路径
141
- let binary = config.binary ?? "claude";
142
- if (!binary.startsWith("/")) {
143
- try {
144
- binary = execSync(`which ${binary}`, { encoding: "utf-8" }).trim();
145
- this.log("info", `解析 CLI 路径: ${binary}`);
146
- }
147
- catch {
148
- this.log("warn", `无法解析 ${binary} 绝对路径,使用原值`);
149
- }
150
- }
151
- this.log("warn", `启动 CLI: ${binary}`, "sdkUrl:", sdkUrl);
152
- // 参照 companion (cli-launcher.ts:285-289):
153
- // companion 设置 CLAUDECODE=1 标识 SDK 模式,但它是独立进程。
154
- // 当从 Claude Code 会话内启动时,需要清理所有 CLAUDECODE/CLAUDE_CODE_*
155
- // 环境变量避免嵌套检测。--sdk-url 本身已足够标识 SDK 模式。
156
- const cleanEnv = {};
157
- for (const [key, val] of Object.entries(process.env)) {
158
- if (key === "CLAUDECODE" || key.startsWith("CLAUDE_CODE_"))
159
- continue;
160
- cleanEnv[key] = val;
161
- }
162
- // 二次清洗:config.env 可能把 CLAUDECODE/CLAUDE_CODE_* 又注入回来
163
- const mergedEnv = {
164
- ...cleanEnv,
165
- ...config.env,
166
- };
167
- for (const key of Object.keys(mergedEnv)) {
168
- if (key === "CLAUDECODE" || key.startsWith("CLAUDE_CODE_")) {
169
- delete mergedEnv[key];
170
- }
171
- }
172
- this.proc = spawn(binary, args, {
173
- cwd: config.workingDir,
174
- env: mergedEnv,
175
- stdio: ["pipe", "pipe", "pipe"],
176
- });
177
- // 收集 stdout/stderr 用于超时诊断
178
- const stdoutChunks = [];
179
- const stderrChunks = [];
180
- this.proc.stdout?.on("data", (data) => {
181
- const text = data.toString().trim();
182
- if (text)
183
- stdoutChunks.push(text);
184
- });
185
- this.proc.stderr?.on("data", (data) => {
186
- const text = data.toString().trim();
187
- if (text)
188
- stderrChunks.push(text);
189
- this.log("warn", "CLI stderr:", text);
190
- });
191
- this.proc.on("error", (err) => {
192
- this.log("error", "CLI 进程错误:", err.message);
193
- this.stateChangeHandler?.(ConnectionState.ERROR);
194
- });
195
- // 进程提前退出时,让 wsReady 立即 reject 而非等到超时
196
- this.proc.on("exit", (code, signal) => {
197
- this.log("info", `CLI 进程退出, code=${code}, signal=${signal}`);
198
- });
199
- const earlyExit = new Promise((_, reject) => {
200
- this.proc.on("exit", (code, signal) => {
201
- const stdout = stdoutChunks.join("\n");
202
- const stderr = stderrChunks.join("\n");
203
- reject(new Error(`CLI 进程在连接前退出 (code=${code}, signal=${signal})` +
204
- (stdout ? `\nstdout:\n${stdout}` : "") +
205
- (stderr ? `\nstderr:\n${stderr}` : "")));
206
- });
207
- });
208
- // 3. 等待 CLI 反连(不等待 system/init,参照 companion 非阻塞模式)
209
- const connectTimeout = config.timeout?.connect ?? 30_000;
210
- this.log("warn", `等待 CLI 反连, 超时: ${connectTimeout}ms`);
211
- // 超时时附带进程状态和 stdout/stderr 便于诊断
212
- const timeoutWithDiag = new Promise((_, reject) => {
213
- setTimeout(() => {
214
- const alive = this.proc && this.proc.exitCode === null && !this.proc.killed;
215
- const stdout = stdoutChunks.join("\n");
216
- const stderr = stderrChunks.join("\n");
217
- const diag = [
218
- `CLI 连接超时 (${connectTimeout}ms)`,
219
- `进程状态: ${alive ? "运行中" : `已退出 (code=${this.proc?.exitCode}, signal=${this.proc?.signalCode})`}`,
220
- stdout ? `stdout (最后 500 字符):\n${stdout.slice(-500)}` : "stdout: (空)",
221
- stderr ? `stderr:\n${stderr}` : "stderr: (空)",
222
- ].join("\n");
223
- reject(new Error(diag));
224
- }, connectTimeout);
225
- });
226
- this.ws = await Promise.race([wsReady, earlyExit, timeoutWithDiag]);
227
- // system/init 到达时会异步更新 cliSessionId(在 routeMessage 中处理)
228
- this.log("warn", `连接就绪, sessionId=${sessionId} (system/init 将异步更新)`);
229
- this.stateChangeHandler?.(ConnectionState.CONNECTED);
230
- return {
231
- sessionId,
232
- backendType: "claude-code",
233
- model: config.model ?? "unknown",
234
- tools: [],
235
- capabilities: {
236
- supportsResume: true,
237
- supportsStreaming: true,
238
- supportsToolApproval: true,
239
- supportsModelSwitch: true,
240
- },
241
- };
242
- }
243
- async sendPrompt(prompt, _options) {
244
- this.stateChangeHandler?.(ConnectionState.EXECUTING);
245
- // 重置原始对话日志(每次 sendPrompt 开始新的收集周期)
246
- this.rawConversationLog = [];
247
- // 参照 companion (ws-bridge.ts:848-854)
248
- const msg = {
249
- type: "user",
250
- message: { role: "user", content: prompt },
251
- parent_tool_use_id: null,
252
- session_id: this.cliSessionId ?? "",
253
- };
254
- const ndjson = JSON.stringify(msg);
255
- // 记录发出的消息
256
- this.rawConversationLog.push({
257
- direction: "sent",
258
- timestamp: new Date().toISOString(),
259
- message: msg,
260
- });
261
- // this.log(
262
- // "info",
263
- // `[WS 发] sendPrompt: ${ndjson.slice(0, 500)}${ndjson.length > 500 ? "...(截断)" : ""}`,
264
- // );
265
- this.sendToCLI(ndjson);
266
- }
267
- async interrupt() {
268
- // 参照 companion (ws-bridge.ts:899-905)
269
- const msg = {
270
- type: "control_request",
271
- request_id: randomUUID(),
272
- request: { subtype: "interrupt" },
273
- };
274
- const ndjson = JSON.stringify(msg);
275
- this.rawConversationLog.push({
276
- direction: "sent",
277
- timestamp: new Date().toISOString(),
278
- message: msg,
279
- });
280
- // this.log("info", `[WS 发] interrupt: ${ndjson}`);
281
- this.sendToCLI(ndjson);
282
- }
283
- async resume(sessionId) {
284
- await this.dispose();
285
- if (!this.config)
286
- throw new Error("No config available for resume");
287
- // 参照 companion (cli-launcher.ts:219-224): 用 cliSessionId 恢复会话
288
- return this.connect({
289
- ...this.config,
290
- resumeSessionId: sessionId,
291
- });
292
- }
293
- getCapabilities() {
294
- return {
295
- supportsResume: true,
296
- supportsStreaming: true,
297
- supportsToolApproval: true,
298
- supportsModelSwitch: true,
299
- };
300
- }
301
- async dispose() {
302
- // 参照 companion (cli-launcher.ts:507-532): SIGTERM → 等待 → SIGKILL
303
- if (this.proc) {
304
- const proc = this.proc;
305
- const alreadyExited = proc.exitCode !== null || proc.signalCode !== null;
306
- if (!alreadyExited) {
307
- proc.kill("SIGTERM");
308
- await new Promise((resolve) => {
309
- const timeout = setTimeout(() => {
310
- proc.kill("SIGKILL");
311
- resolve();
312
- }, 5_000);
313
- proc.on("exit", () => {
314
- clearTimeout(timeout);
315
- resolve();
316
- });
317
- });
318
- }
319
- this.proc = null;
320
- }
321
- this.ws?.close();
322
- this.ws = null;
323
- this.wsServer?.close();
324
- this.wsServer = null;
325
- this.httpServer?.close();
326
- this.httpServer = null;
327
- this.restoreMcpJson();
328
- this.stateChangeHandler?.(ConnectionState.DISCONNECTED);
329
- }
330
- onToolRequest(handler) {
331
- this.toolRequestHandler = handler;
332
- }
333
- onResult(handler) {
334
- this.resultHandler = handler;
335
- }
336
- onProgress(handler) {
337
- this.progressHandler = handler;
338
- }
339
- onStateChange(handler) {
340
- this.stateChangeHandler = handler;
341
- }
342
- onSessionId(handler) {
343
- this.sessionIdHandler = handler;
344
- }
345
- getCliSessionId() {
346
- return this.cliSessionId;
347
- }
348
- // ── 私有方法 ──
349
- /**
350
- * 统一发送入口:WS 未就绪时消息入队,就绪时直接发送。
351
- * 参照 companion 非阻塞队列模式。
352
- */
353
- sendToCLI(ndjson) {
354
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
355
- this.log("warn", `[WS] CLI 未就绪 (ws=${this.ws ? `readyState=${this.ws.readyState}` : "null"}),消息入队 (queue=${this.pendingMessages.length + 1})`);
356
- this.pendingMessages.push(ndjson);
357
- return;
358
- }
359
- // this.log(
360
- // "warn",
361
- // `[WS 发] ${ndjson.slice(0, 500)}${ndjson.length > 500 ? "...(截断)" : ""}`,
362
- // );
363
- this.ws.send(ndjson + "\n");
364
- }
365
- setupMessageHandler(ws) {
366
- // WS 连接建立后立即刷新排队消息
367
- if (this.pendingMessages.length > 0) {
368
- this.log("info", `[WS] 刷新 ${this.pendingMessages.length} 条排队消息`);
369
- const queued = this.pendingMessages.splice(0);
370
- for (const ndjson of queued) {
371
- this.sendToCLI(ndjson);
372
- }
373
- }
374
- let buffer = "";
375
- ws.on("message", (data) => {
376
- const raw = data.toString();
377
- // this.log('warn', `[WS 收] ${raw.slice(0, 500)}${raw.length > 500 ? '...(截断)' : ''}`);
378
- buffer += raw;
379
- const lines = buffer.split("\n");
380
- buffer = lines.pop() ?? "";
381
- for (const line of lines) {
382
- if (!line.trim())
383
- continue;
384
- try {
385
- const msg = JSON.parse(line);
386
- this.routeMessage(msg);
387
- }
388
- catch (err) {
389
- this.log("warn", `[WS 收] JSON 解析失败: ${err.message}, raw=${line.slice(0, 200)}`);
390
- }
391
- }
392
- });
393
- ws.on("close", (code, reason) => {
394
- this.log("warn", `[WS] 连接关闭, code=${code}, reason=${reason.toString()}`);
395
- });
396
- ws.on("error", (err) => {
397
- this.log("error", `[WS] 连接错误:`, err.message);
398
- });
399
- ws.on("ping", () => {
400
- // this.log("warn", "[WS] 收到 ping");
401
- });
402
- ws.on("pong", () => {
403
- // this.log("warn", "[WS] 收到 pong");
404
- });
405
- }
406
- /**
407
- * 参照 companion (ws-bridge.ts:550-591): 按 type 路由 CLI 消息
408
- */
409
- routeMessage(msg) {
410
- // 收到任何消息即证明连接存活,通知 BackendManager 重置心跳计数
411
- this.progressHandler?.({ type: "heartbeat", timestamp: Date.now() });
412
- // 记录收到的原始消息到对话日志
413
- this.rawConversationLog.push({
414
- direction: "received",
415
- timestamp: new Date().toISOString(),
416
- message: msg,
417
- });
418
- const msgType = `${msg.type}${msg.subtype ? "/" + msg.subtype : ""}`;
419
- this.log("info", `[路由] type=${msgType}${msg.session_id ? ` sid=${msg.session_id}` : ""}${msg.hook_name ? ` hook=${msg.hook_name}` : ""}`);
420
- switch (msg.type) {
421
- case "system":
422
- // 参照 companion (ws-bridge.ts:594-641): 只有 subtype=init 才是初始化消息
423
- // 异步更新 cliSessionId 和 initMessage,不阻塞连接流程
424
- if (msg.subtype === "init") {
425
- this.log("info", `[路由] system/init 详情: model=${msg.model}, tools=${JSON.stringify(msg.tools ?? [])}`);
426
- this.initMessage = msg;
427
- this.cliSessionId = msg.session_id ?? this.cliSessionId;
428
- if (this.cliSessionId) {
429
- this.sessionIdHandler?.(this.cliSessionId);
430
- }
431
- }
432
- break;
433
- case "assistant":
434
- this.log("info", `[路由] assistant 内容块数: ${msg.message?.content?.length ?? 0}`);
435
- this.handleAssistant(msg);
436
- break;
437
- case "stream_event":
438
- this.handleStreamEvent(msg.event);
439
- break;
440
- case "result": {
441
- const preview = typeof msg.result === "string"
442
- ? msg.result.slice(0, 200)
443
- : JSON.stringify(msg.result ?? null).slice(0, 200);
444
- this.log("info", `[路由] result: is_error=${msg.is_error}, turns=${msg.num_turns}, preview=${preview}`);
445
- this.handleResult(msg);
446
- break;
447
- }
448
- case "control_request":
449
- this.log("info", `[路由] control_request: subtype=${msg.request?.subtype}, tool=${msg.request?.tool_name ?? "N/A"}`);
450
- if (msg.request?.subtype === "can_use_tool") {
451
- this.handleToolApproval(msg);
452
- }
453
- break;
454
- case "tool_progress":
455
- this.progressHandler?.({
456
- type: "tool_progress",
457
- toolName: msg.tool_name ?? "unknown",
458
- output: msg.output ?? "",
459
- });
460
- break;
461
- case "keep_alive":
462
- case "user":
463
- // 静默消费(user 是 CLI 回传的 tool_use_result)
464
- break;
465
- default:
466
- this.log("warn", `[路由] 未知消息类型: ${msg.type}, keys=${Object.keys(msg).join(",")}`);
467
- break;
468
- }
469
- }
470
- handleAssistant(msg) {
471
- if (msg.message?.content) {
472
- for (const block of msg.message.content) {
473
- if (block.type === "text") {
474
- this.progressHandler?.({ type: "text_chunk", text: block.text });
475
- }
476
- else if (block.type === "thinking") {
477
- this.progressHandler?.({ type: "thinking", text: block.thinking });
478
- }
479
- }
480
- }
481
- }
482
- handleStreamEvent(event) {
483
- if (!event)
484
- return;
485
- if (event.type === "content_block_delta") {
486
- if (event.delta?.type === "text_delta") {
487
- this.progressHandler?.({ type: "text_chunk", text: event.delta.text });
488
- }
489
- }
490
- }
491
- handleResult(msg) {
492
- const result = {
493
- status: msg.is_error ? "error" : "success",
494
- resultText: msg.result,
495
- errors: msg.errors,
496
- metrics: {
497
- durationMs: msg.duration_ms ?? 0,
498
- numTurns: msg.num_turns ?? 0,
499
- totalCostUsd: msg.total_cost_usd,
500
- linesAdded: msg.total_lines_added,
501
- linesRemoved: msg.total_lines_removed,
502
- },
503
- // 附加完整的原始对话日志
504
- rawConversation: [...this.rawConversationLog],
505
- };
506
- // 特殊处理 max_turns
507
- if (msg.subtype === "error_max_turns") {
508
- result.status = "max_turns";
509
- }
510
- this.resultHandler?.(result);
511
- this.stateChangeHandler?.(ConnectionState.CONNECTED);
512
- }
513
- /**
514
- * 参照 companion (ws-bridge.ts:714-733 + 858-896): 工具审批流
515
- */
516
- async handleToolApproval(msg) {
517
- if (!this.toolRequestHandler)
518
- return;
519
- const req = {
520
- requestId: msg.request_id,
521
- toolName: msg.request.tool_name ?? "unknown",
522
- input: msg.request.input ?? {},
523
- description: msg.request.description,
524
- toolUseId: msg.request.tool_use_id ?? "",
525
- };
526
- const response = await this.toolRequestHandler(req);
527
- const reply = JSON.stringify({
528
- type: "control_response",
529
- response: {
530
- subtype: "success",
531
- request_id: req.requestId,
532
- response: {
533
- behavior: response.behavior,
534
- ...(response.behavior === "allow" && response.updatedInput
535
- ? { updatedInput: response.updatedInput }
536
- : {}),
537
- },
538
- },
539
- });
540
- // 记录发出的 tool approval 响应
541
- this.rawConversationLog.push({
542
- direction: "sent",
543
- timestamp: new Date().toISOString(),
544
- message: JSON.parse(reply),
545
- });
546
- // this.log(
547
- // "info",
548
- // `[WS 发] toolApproval: tool=${req.toolName}, behavior=${response.behavior}`,
549
- // );
550
- this.sendToCLI(reply);
551
- }
552
- findFreePort() {
553
- return new Promise((resolve, reject) => {
554
- const server = createServer();
555
- server.listen(0, "127.0.0.1", () => {
556
- const addr = server.address();
557
- if (addr && typeof addr === "object") {
558
- const port = addr.port;
559
- server.close(() => resolve(port));
560
- }
561
- else {
562
- reject(new Error("Failed to get port"));
563
- }
564
- });
565
- });
566
- }
567
- withTimeout(promise, ms, message) {
568
- return new Promise((resolve, reject) => {
569
- const timer = setTimeout(() => reject(new Error(message)), ms);
570
- promise.then((val) => {
571
- clearTimeout(timer);
572
- resolve(val);
573
- }, (err) => {
574
- clearTimeout(timer);
575
- reject(err);
576
- });
577
- });
578
- }
579
- /**
580
- * 从 workingDir 向上查找 git root
581
- */
582
- /**
583
- * 将 MCP server 配置写入目标目录的 .mcp.json
584
- * 如果文件已存在,合并我们的 server 条目并备份原始内容
585
- */
586
- writeMcpJson(workingDir, mcpServers) {
587
- // 确保目录存在
588
- if (!existsSync(workingDir)) {
589
- mkdirSync(workingDir, { recursive: true });
590
- }
591
- this.mcpJsonPath = join(workingDir, ".mcp.json");
592
- // 构建我们的 server 条目
593
- const ourServers = {};
594
- for (const s of mcpServers) {
595
- if (s.url) {
596
- ourServers[s.name] = { type: "http", url: s.url };
597
- }
598
- else if (s.command) {
599
- ourServers[s.name] = {
600
- type: "stdio",
601
- command: s.command,
602
- ...(s.args ? { args: s.args } : {}),
603
- };
604
- }
605
- }
606
- // 读取已有文件(如有)
607
- let existing = {};
608
- if (existsSync(this.mcpJsonPath)) {
609
- try {
610
- const raw = readFileSync(this.mcpJsonPath, "utf-8");
611
- existing = JSON.parse(raw);
612
- this.mcpJsonBackup = raw;
613
- this.mcpJsonCreated = false;
614
- this.log("info", `.mcp.json 已存在,合并 ${Object.keys(ourServers).length} 个 server`);
615
- }
616
- catch {
617
- this.mcpJsonBackup = null;
618
- this.mcpJsonCreated = true;
619
- }
620
- }
621
- else {
622
- this.mcpJsonBackup = null;
623
- this.mcpJsonCreated = true;
624
- }
625
- // 合并写入
626
- const merged = {
627
- ...existing,
628
- mcpServers: {
629
- ...(existing.mcpServers ?? {}),
630
- ...ourServers,
631
- },
632
- };
633
- writeFileSync(this.mcpJsonPath, JSON.stringify(merged, null, 2), "utf-8");
634
- this.log("info", `.mcp.json 已写入: ${this.mcpJsonPath}, servers: ${Object.keys(ourServers).join(", ")}`);
635
- }
636
- /**
637
- * 恢复或删除 .mcp.json
638
- */
639
- restoreMcpJson() {
640
- if (!this.mcpJsonPath)
641
- return;
642
- try {
643
- if (this.mcpJsonCreated) {
644
- // 文件是我们创建的 → 直接删除
645
- if (existsSync(this.mcpJsonPath)) {
646
- unlinkSync(this.mcpJsonPath);
647
- this.log("info", `.mcp.json 已删除: ${this.mcpJsonPath}`);
648
- }
649
- }
650
- else if (this.mcpJsonBackup !== null) {
651
- // 文件原本存在 → 恢复原始内容
652
- writeFileSync(this.mcpJsonPath, this.mcpJsonBackup, "utf-8");
653
- this.log("info", `.mcp.json 已恢复: ${this.mcpJsonPath}`);
654
- }
655
- }
656
- catch (err) {
657
- this.log("warn", `.mcp.json 清理失败:`, err.message);
658
- }
659
- this.mcpJsonPath = null;
660
- this.mcpJsonBackup = null;
661
- this.mcpJsonCreated = false;
662
- }
663
- }
664
- //# sourceMappingURL=claude-code-backend.js.map
@@ -1,2 +0,0 @@
1
- export { ClaudeCodeBackend } from './claude-code-backend.js';
2
- //# sourceMappingURL=index.js.map
@@ -1,11 +0,0 @@
1
- {
2
- "name": "@team-anya/cc-client",
3
- "version": "0.1.0",
4
- "type": "module",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "dependencies": {
8
- "@team-anya/core": "file:../core",
9
- "ws": "^8.18.0"
10
- }
11
- }