@xdevops/issue-auto-finish 1.0.2 → 1.0.3

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 (133) hide show
  1. package/dist/KnowledgeAnalyzer-EZSJT2MJ.js +13 -0
  2. package/dist/KnowledgeAnalyzer-EZSJT2MJ.js.map +1 -0
  3. package/dist/KnowledgeStore-4ROC6F56.js +10 -0
  4. package/dist/KnowledgeStore-4ROC6F56.js.map +1 -0
  5. package/dist/ai-runner/AIRunner.d.ts +2 -0
  6. package/dist/ai-runner/AIRunner.d.ts.map +1 -1
  7. package/dist/ai-runner/BaseAIRunner.d.ts +9 -0
  8. package/dist/ai-runner/BaseAIRunner.d.ts.map +1 -1
  9. package/dist/ai-runner-RGAJPOOW.js +16 -0
  10. package/dist/ai-runner-RGAJPOOW.js.map +1 -0
  11. package/dist/analyze-I7UOJB4F.js +72 -0
  12. package/dist/analyze-I7UOJB4F.js.map +1 -0
  13. package/dist/chunk-3JUHZGX5.js +171 -0
  14. package/dist/chunk-3JUHZGX5.js.map +1 -0
  15. package/dist/chunk-5JYCGAU3.js +318 -0
  16. package/dist/chunk-5JYCGAU3.js.map +1 -0
  17. package/dist/chunk-5VUB3UUK.js +643 -0
  18. package/dist/chunk-5VUB3UUK.js.map +1 -0
  19. package/dist/{chunk-IDUKWCC2.js → chunk-C6ZJVIPZ.js} +1151 -80
  20. package/dist/chunk-C6ZJVIPZ.js.map +1 -0
  21. package/dist/{chunk-OWVT3Z34.js → chunk-JFYAXNNS.js} +121 -31
  22. package/dist/chunk-JFYAXNNS.js.map +1 -0
  23. package/dist/chunk-KISVPNSV.js +188 -0
  24. package/dist/chunk-KISVPNSV.js.map +1 -0
  25. package/dist/{chunk-I3T573SU.js → chunk-LEQYGOMJ.js} +65 -2
  26. package/dist/chunk-LEQYGOMJ.js.map +1 -0
  27. package/dist/{chunk-TBIEB3JY.js → chunk-N5YK6YVI.js} +592 -767
  28. package/dist/chunk-N5YK6YVI.js.map +1 -0
  29. package/dist/{chunk-RIUI4ROA.js → chunk-PECYMYAK.js} +2 -2
  30. package/dist/chunk-SWG2Y7YX.js +410 -0
  31. package/dist/chunk-SWG2Y7YX.js.map +1 -0
  32. package/dist/chunk-TZ6C7HL5.js +59 -0
  33. package/dist/chunk-TZ6C7HL5.js.map +1 -0
  34. package/dist/cli/commands/analyze.d.ts +8 -0
  35. package/dist/cli/commands/analyze.d.ts.map +1 -0
  36. package/dist/cli.js +67 -3
  37. package/dist/cli.js.map +1 -1
  38. package/dist/clients/GongfengClient.d.ts +5 -0
  39. package/dist/clients/GongfengClient.d.ts.map +1 -1
  40. package/dist/config-RI7NLDXI.js +7 -0
  41. package/dist/config-RI7NLDXI.js.map +1 -0
  42. package/dist/config.d.ts +19 -0
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/{doctor-B26Q6JWI.js → doctor-ZPGIBA5N.js} +3 -3
  45. package/dist/events/EventBus.d.ts +1 -1
  46. package/dist/events/EventBus.d.ts.map +1 -1
  47. package/dist/git/GitOperations.d.ts +12 -0
  48. package/dist/git/GitOperations.d.ts.map +1 -1
  49. package/dist/i18n/locales/en.d.ts.map +1 -1
  50. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +11 -5
  53. package/dist/{init-L3VIWCOV.js → init-LZGCIHE7.js} +8 -4
  54. package/dist/{init-L3VIWCOV.js.map → init-LZGCIHE7.js.map} +1 -1
  55. package/dist/knowledge/KnowledgeAnalyzer.d.ts +31 -0
  56. package/dist/knowledge/KnowledgeAnalyzer.d.ts.map +1 -0
  57. package/dist/knowledge/KnowledgeDefaults.d.ts +7 -0
  58. package/dist/knowledge/KnowledgeDefaults.d.ts.map +1 -0
  59. package/dist/knowledge/KnowledgeEntry.d.ts +30 -0
  60. package/dist/knowledge/KnowledgeEntry.d.ts.map +1 -0
  61. package/dist/knowledge/KnowledgeLoader.d.ts +18 -0
  62. package/dist/knowledge/KnowledgeLoader.d.ts.map +1 -0
  63. package/dist/knowledge/KnowledgeStore.d.ts +35 -0
  64. package/dist/knowledge/KnowledgeStore.d.ts.map +1 -0
  65. package/dist/knowledge/ProjectKnowledge.d.ts +79 -0
  66. package/dist/knowledge/ProjectKnowledge.d.ts.map +1 -0
  67. package/dist/knowledge/analyze-prompt.d.ts +2 -0
  68. package/dist/knowledge/analyze-prompt.d.ts.map +1 -0
  69. package/dist/knowledge/importers/GongfengExtractor.d.ts +27 -0
  70. package/dist/knowledge/importers/GongfengExtractor.d.ts.map +1 -0
  71. package/dist/knowledge/importers/IwikiImporter.d.ts +21 -0
  72. package/dist/knowledge/importers/IwikiImporter.d.ts.map +1 -0
  73. package/dist/knowledge/index.d.ts +12 -0
  74. package/dist/knowledge/index.d.ts.map +1 -0
  75. package/dist/lib.js +19 -10
  76. package/dist/orchestrator/PipelineOrchestrator.d.ts +5 -1
  77. package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
  78. package/dist/phases/BasePhase.d.ts.map +1 -1
  79. package/dist/poller/IssuePoller.d.ts +5 -0
  80. package/dist/poller/IssuePoller.d.ts.map +1 -1
  81. package/dist/prompts/chat-templates.d.ts +4 -0
  82. package/dist/prompts/chat-templates.d.ts.map +1 -0
  83. package/dist/prompts/templates.d.ts +11 -0
  84. package/dist/prompts/templates.d.ts.map +1 -1
  85. package/dist/rules/RuleResolver.d.ts +4 -0
  86. package/dist/rules/RuleResolver.d.ts.map +1 -1
  87. package/dist/run.js +11 -5
  88. package/dist/run.js.map +1 -1
  89. package/dist/services/ChatService.d.ts +39 -0
  90. package/dist/services/ChatService.d.ts.map +1 -0
  91. package/dist/shutdown/ShutdownSignal.d.ts +3 -0
  92. package/dist/shutdown/ShutdownSignal.d.ts.map +1 -0
  93. package/dist/{start-TVN4SS6E.js → start-NMQHUKGF.js} +1 -1
  94. package/dist/tracker/IssueState.d.ts +1 -0
  95. package/dist/tracker/IssueState.d.ts.map +1 -1
  96. package/dist/tracker/IssueTracker.d.ts +2 -0
  97. package/dist/tracker/IssueTracker.d.ts.map +1 -1
  98. package/dist/updater/AutoUpdater.d.ts +33 -0
  99. package/dist/updater/AutoUpdater.d.ts.map +1 -0
  100. package/dist/updater/UpdateExecutor.d.ts +7 -0
  101. package/dist/updater/UpdateExecutor.d.ts.map +1 -0
  102. package/dist/updater/VersionChecker.d.ts +22 -0
  103. package/dist/updater/VersionChecker.d.ts.map +1 -0
  104. package/dist/web/WebServer.d.ts +4 -0
  105. package/dist/web/WebServer.d.ts.map +1 -1
  106. package/dist/web/routes/api.d.ts +4 -0
  107. package/dist/web/routes/api.d.ts.map +1 -1
  108. package/dist/web/routes/chat.d.ts +7 -0
  109. package/dist/web/routes/chat.d.ts.map +1 -0
  110. package/dist/web/routes/knowledge.d.ts +13 -0
  111. package/dist/web/routes/knowledge.d.ts.map +1 -0
  112. package/dist/web/routes/setup.d.ts.map +1 -1
  113. package/dist/webhook/CommandExecutor.d.ts +4 -0
  114. package/dist/webhook/CommandExecutor.d.ts.map +1 -1
  115. package/dist/webhook/CommandParser.d.ts +2 -2
  116. package/dist/webhook/CommandParser.d.ts.map +1 -1
  117. package/dist/webhook/WebhookHandler.d.ts +8 -0
  118. package/dist/webhook/WebhookHandler.d.ts.map +1 -1
  119. package/dist/webhook/WebhookServer.d.ts +2 -0
  120. package/dist/webhook/WebhookServer.d.ts.map +1 -1
  121. package/package.json +4 -2
  122. package/src/web/frontend/dist/assets/index-AcJ0lPIv.js +67 -0
  123. package/src/web/frontend/dist/assets/index-BbRt5BAr.css +1 -0
  124. package/src/web/frontend/dist/index.html +2 -2
  125. package/dist/chunk-I3T573SU.js.map +0 -1
  126. package/dist/chunk-IDUKWCC2.js.map +0 -1
  127. package/dist/chunk-OWVT3Z34.js.map +0 -1
  128. package/dist/chunk-TBIEB3JY.js.map +0 -1
  129. package/src/web/frontend/dist/assets/index-CQdlU9PE.js +0 -65
  130. package/src/web/frontend/dist/assets/index-CgMEkyZJ.css +0 -1
  131. /package/dist/{chunk-RIUI4ROA.js.map → chunk-PECYMYAK.js.map} +0 -0
  132. /package/dist/{doctor-B26Q6JWI.js.map → doctor-ZPGIBA5N.js.map} +0 -0
  133. /package/dist/{start-TVN4SS6E.js.map → start-NMQHUKGF.js.map} +0 -0
@@ -0,0 +1,410 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-TZ6C7HL5.js";
4
+
5
+ // src/ai-runner/BaseAIRunner.ts
6
+ import { spawn } from "child_process";
7
+
8
+ // src/shutdown/ShutdownSignal.ts
9
+ var _shuttingDown = false;
10
+ function isShuttingDown() {
11
+ return _shuttingDown;
12
+ }
13
+ function setShuttingDown() {
14
+ _shuttingDown = true;
15
+ }
16
+
17
+ // src/ai-runner/BaseAIRunner.ts
18
+ var logger2 = logger.child("AIRunner");
19
+ var SIGKILL_GRACE_MS = 1e4;
20
+ var BaseAIRunner = class {
21
+ activeChildren = /* @__PURE__ */ new Map();
22
+ async run(options) {
23
+ if (isShuttingDown()) {
24
+ logger2.warn("AI runner skipped \u2014 service is shutting down");
25
+ return { success: false, output: "Service shutting down", exitCode: null };
26
+ }
27
+ const { prompt, workDir, timeoutMs, sessionId, continueSession, onStreamEvent } = options;
28
+ logger2.info("Running AI runner", {
29
+ workDir,
30
+ timeoutMs,
31
+ continueSession: !!continueSession,
32
+ sessionId
33
+ });
34
+ return new Promise((resolve) => {
35
+ const chunks = [];
36
+ const stderrChunks = [];
37
+ let timedOut = false;
38
+ let lineBuffer = "";
39
+ let killTimer;
40
+ const binary = this.getBinary();
41
+ const args = this.buildArgs(options);
42
+ const spawnOpts = this.getSpawnOptions(options);
43
+ const child = spawn(binary, args, {
44
+ ...spawnOpts,
45
+ stdio: ["pipe", "pipe", "pipe"]
46
+ });
47
+ this.activeChildren.set(child, { child, workDir });
48
+ const timer = setTimeout(() => {
49
+ timedOut = true;
50
+ child.kill("SIGTERM");
51
+ logger2.warn("AI runner timed out", { timeoutMs });
52
+ killTimer = setTimeout(() => {
53
+ if (child.exitCode === null) {
54
+ child.kill("SIGKILL");
55
+ logger2.warn("AI runner force-killed (SIGKILL fallback)", { workDir });
56
+ }
57
+ }, SIGKILL_GRACE_MS);
58
+ }, timeoutMs);
59
+ const flushInterval = onStreamEvent ? setInterval(() => {
60
+ if (lineBuffer.trim()) {
61
+ onStreamEvent({ type: "partial", content: lineBuffer.trim(), timestamp: (/* @__PURE__ */ new Date()).toISOString() });
62
+ }
63
+ }, 200) : void 0;
64
+ child.stdout.on("data", (chunk) => {
65
+ chunks.push(chunk);
66
+ if (onStreamEvent) {
67
+ lineBuffer += chunk.toString();
68
+ let newlineIdx;
69
+ while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
70
+ const line = lineBuffer.slice(0, newlineIdx).trim();
71
+ lineBuffer = lineBuffer.slice(newlineIdx + 1);
72
+ if (!line) continue;
73
+ this.emitStreamLine(line, onStreamEvent);
74
+ }
75
+ }
76
+ });
77
+ child.stderr.on("data", (chunk) => {
78
+ stderrChunks.push(chunk);
79
+ const text = chunk.toString();
80
+ logger2.debug("AI runner stderr: " + text);
81
+ if (onStreamEvent) {
82
+ onStreamEvent({ type: "stderr", content: text, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
83
+ }
84
+ });
85
+ child.on("close", (code) => {
86
+ this.activeChildren.delete(child);
87
+ clearTimeout(timer);
88
+ if (killTimer) clearTimeout(killTimer);
89
+ if (flushInterval) clearInterval(flushInterval);
90
+ if (onStreamEvent && lineBuffer.trim()) {
91
+ this.emitStreamLine(lineBuffer.trim(), onStreamEvent);
92
+ }
93
+ const rawOutput = Buffer.concat(chunks).toString("utf-8");
94
+ const { output, resolvedSessionId } = this.parseOutput(rawOutput, sessionId);
95
+ const success = code === 0 && !timedOut;
96
+ logger2.info("AI runner finished", { exitCode: code, success, timedOut });
97
+ const stderrText = stderrChunks.length > 0 ? Buffer.concat(stderrChunks).toString("utf-8").trim() : "";
98
+ const finalOutput = !success && !output.trim() && stderrText ? stderrText : output;
99
+ resolve({
100
+ success,
101
+ output: finalOutput,
102
+ sessionId: resolvedSessionId ?? sessionId,
103
+ exitCode: code
104
+ });
105
+ });
106
+ child.on("error", (err) => {
107
+ this.activeChildren.delete(child);
108
+ clearTimeout(timer);
109
+ if (killTimer) clearTimeout(killTimer);
110
+ if (flushInterval) clearInterval(flushInterval);
111
+ logger2.error("AI runner spawn error", { error: err.message });
112
+ resolve({
113
+ success: false,
114
+ output: err.message,
115
+ exitCode: null
116
+ });
117
+ });
118
+ child.stdin.write(prompt);
119
+ child.stdin.end();
120
+ });
121
+ }
122
+ killAll() {
123
+ for (const [child] of this.activeChildren) {
124
+ this.forceKillChild(child);
125
+ }
126
+ logger2.info("Killed all active AI runner children", { count: this.activeChildren.size });
127
+ }
128
+ killByWorkDir(targetWorkDir) {
129
+ let killed = 0;
130
+ for (const [child, entry] of this.activeChildren) {
131
+ if (entry.workDir === targetWorkDir) {
132
+ this.forceKillChild(child);
133
+ killed++;
134
+ }
135
+ }
136
+ if (killed > 0) {
137
+ logger2.info("Killed AI runner children by workDir", { workDir: targetWorkDir, killed });
138
+ }
139
+ return killed;
140
+ }
141
+ forceKillChild(child) {
142
+ try {
143
+ if (child.exitCode === null) {
144
+ child.kill("SIGTERM");
145
+ setTimeout(() => {
146
+ if (child.exitCode === null) {
147
+ child.kill("SIGKILL");
148
+ }
149
+ }, SIGKILL_GRACE_MS);
150
+ }
151
+ } catch {
152
+ }
153
+ }
154
+ emitStreamLine(line, onStreamEvent) {
155
+ try {
156
+ const parsed = JSON.parse(line);
157
+ onStreamEvent({
158
+ type: typeof parsed.type === "string" ? parsed.type : "raw",
159
+ content: parsed,
160
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
161
+ });
162
+ } catch {
163
+ onStreamEvent({ type: "raw", content: line, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
164
+ }
165
+ }
166
+ /**
167
+ * Handles both stream-json (one JSON object per line) and legacy single-JSON formats.
168
+ */
169
+ parseOutput(rawOutput, _sessionId) {
170
+ let output = rawOutput;
171
+ let resolvedSessionId;
172
+ const events = this.tryParseStreamJson(rawOutput);
173
+ if (events) {
174
+ output = events.filter((item) => item.type === "result").map((item) => item.result ?? "").join("\n");
175
+ const sessionItem = events.find(
176
+ (item) => item.session_id != null
177
+ );
178
+ if (sessionItem) {
179
+ resolvedSessionId = sessionItem.session_id;
180
+ }
181
+ if (!output) {
182
+ output = this.extractAssistantText(events);
183
+ }
184
+ if (!output) {
185
+ output = this.summarizeStreamEvents(events);
186
+ }
187
+ return { output, resolvedSessionId };
188
+ }
189
+ try {
190
+ const parsed = JSON.parse(rawOutput);
191
+ if (Array.isArray(parsed)) {
192
+ output = parsed.filter((item) => item.type === "result").map((item) => item.result ?? "").join("\n");
193
+ const sessionItem = parsed.find(
194
+ (item) => item.session_id != null
195
+ );
196
+ if (sessionItem) {
197
+ resolvedSessionId = sessionItem.session_id;
198
+ }
199
+ if (!output) {
200
+ output = this.extractAssistantText(parsed);
201
+ }
202
+ } else if (parsed.result != null) {
203
+ output = parsed.result;
204
+ resolvedSessionId = parsed.session_id;
205
+ }
206
+ } catch {
207
+ }
208
+ return { output, resolvedSessionId };
209
+ }
210
+ /**
211
+ * Extract plain text from assistant events as fallback when result.result is empty.
212
+ * Looks for message.content[].text in assistant-type events.
213
+ */
214
+ extractAssistantText(events) {
215
+ const texts = [];
216
+ for (const evt of events) {
217
+ if (evt.type !== "assistant") continue;
218
+ const message = evt.message;
219
+ if (!message?.content || !Array.isArray(message.content)) continue;
220
+ for (const block of message.content) {
221
+ if (block.type === "text" && block.text) {
222
+ texts.push(block.text);
223
+ }
224
+ }
225
+ }
226
+ return texts.join("");
227
+ }
228
+ summarizeStreamEvents(events) {
229
+ const errorEvents = events.filter(
230
+ (e) => e.type === "error" || e.subtype === "error"
231
+ );
232
+ if (errorEvents.length > 0) {
233
+ const messages = errorEvents.map(
234
+ (e) => String(e.message ?? e.error ?? JSON.stringify(e))
235
+ );
236
+ return `AI runner \u9519\u8BEF: ${messages.join("; ")}`;
237
+ }
238
+ const types = events.map((e) => String(e.type ?? "unknown"));
239
+ const typeCounts = {};
240
+ for (const t of types) {
241
+ typeCounts[t] = (typeCounts[t] ?? 0) + 1;
242
+ }
243
+ const summary = Object.entries(typeCounts).map(([t, c]) => `${t}(${c})`).join(", ");
244
+ return `AI runner \u672A\u8FD4\u56DE\u7ED3\u679C (\u6536\u5230 ${events.length} \u4E2A\u4E8B\u4EF6: ${summary})`;
245
+ }
246
+ tryParseStreamJson(raw) {
247
+ const lines = raw.split("\n").filter((l) => l.trim());
248
+ if (lines.length < 2) return null;
249
+ const objects = [];
250
+ for (const line of lines) {
251
+ try {
252
+ objects.push(JSON.parse(line));
253
+ } catch {
254
+ return null;
255
+ }
256
+ }
257
+ return objects;
258
+ }
259
+ };
260
+
261
+ // src/ai-runner/ClaudeInternalRunner.ts
262
+ var ClaudeInternalRunner = class extends BaseAIRunner {
263
+ binary;
264
+ nvmNodeVersion;
265
+ model;
266
+ constructor(binary, nvmNodeVersion, model) {
267
+ super();
268
+ this.binary = binary;
269
+ this.nvmNodeVersion = nvmNodeVersion;
270
+ this.model = model;
271
+ }
272
+ getBinary() {
273
+ return this.binary;
274
+ }
275
+ buildArgs(options) {
276
+ const args = ["-p", "-", "--output-format", "stream-json", "--verbose"];
277
+ if (options.mode === "plan") {
278
+ args.push("--permission-mode", "plan", "--allowedTools", "Read,Grep,Glob,WebSearch");
279
+ } else {
280
+ args.push("--dangerously-skip-permissions");
281
+ }
282
+ if (this.model) {
283
+ args.push("--model", this.model);
284
+ }
285
+ if (options.continueSession && options.sessionId) {
286
+ args.push("--resume", options.sessionId);
287
+ }
288
+ return args;
289
+ }
290
+ getSpawnOptions(options) {
291
+ const { CLAUDECODE, ...env } = process.env;
292
+ return {
293
+ cwd: options.workDir,
294
+ env: {
295
+ ...env,
296
+ NODE_VERSION: this.nvmNodeVersion
297
+ }
298
+ };
299
+ }
300
+ };
301
+
302
+ // src/ai-runner/CodebuddyRunner.ts
303
+ var CodebuddyRunner = class extends BaseAIRunner {
304
+ binary;
305
+ nvmNodeVersion;
306
+ model;
307
+ constructor(binary, nvmNodeVersion, model) {
308
+ super();
309
+ this.binary = binary;
310
+ this.nvmNodeVersion = nvmNodeVersion;
311
+ this.model = model;
312
+ }
313
+ getBinary() {
314
+ return this.binary;
315
+ }
316
+ buildArgs(options) {
317
+ const args = ["-p", "--output-format", "stream-json", "--verbose"];
318
+ if (options.mode === "plan") {
319
+ args.push("--permission-mode", "plan", "--allowedTools", "Read,Grep,Glob,WebSearch");
320
+ } else {
321
+ args.push("-y");
322
+ }
323
+ if (this.model) {
324
+ args.push("--model", this.model);
325
+ }
326
+ if (options.continueSession && options.sessionId) {
327
+ args.push("--resume", options.sessionId);
328
+ }
329
+ return args;
330
+ }
331
+ getSpawnOptions(options) {
332
+ return {
333
+ cwd: options.workDir,
334
+ env: {
335
+ ...process.env,
336
+ NODE_VERSION: this.nvmNodeVersion
337
+ }
338
+ };
339
+ }
340
+ };
341
+
342
+ // src/ai-runner/CursorAgentRunner.ts
343
+ var CursorAgentRunner = class extends BaseAIRunner {
344
+ binary;
345
+ nvmNodeVersion;
346
+ model;
347
+ constructor(binary, nvmNodeVersion, model) {
348
+ super();
349
+ this.binary = binary;
350
+ this.nvmNodeVersion = nvmNodeVersion;
351
+ this.model = model;
352
+ }
353
+ getBinary() {
354
+ return this.binary;
355
+ }
356
+ buildArgs(options) {
357
+ const args = [
358
+ "agent",
359
+ "-p",
360
+ "--force",
361
+ "--trust",
362
+ "--output-format",
363
+ "stream-json",
364
+ "--workspace",
365
+ options.workDir
366
+ ];
367
+ if (options.mode === "plan") {
368
+ args.push("--mode", "plan");
369
+ }
370
+ if (this.model) {
371
+ args.push("--model", this.model);
372
+ }
373
+ if (options.continueSession && options.sessionId) {
374
+ args.push("--resume", options.sessionId);
375
+ }
376
+ return args;
377
+ }
378
+ getSpawnOptions(_options) {
379
+ return {
380
+ cwd: process.cwd(),
381
+ env: {
382
+ ...process.env,
383
+ NODE_VERSION: this.nvmNodeVersion
384
+ }
385
+ };
386
+ }
387
+ };
388
+
389
+ // src/ai-runner/index.ts
390
+ function createAIRunner(ai) {
391
+ switch (ai.mode) {
392
+ case "cursor-agent":
393
+ return new CursorAgentRunner(ai.binary, ai.nvmNodeVersion, ai.model);
394
+ case "codebuddy":
395
+ return new CodebuddyRunner(ai.binary, ai.nvmNodeVersion, ai.model);
396
+ default:
397
+ return new ClaudeInternalRunner(ai.binary, ai.nvmNodeVersion, ai.model);
398
+ }
399
+ }
400
+
401
+ export {
402
+ isShuttingDown,
403
+ setShuttingDown,
404
+ BaseAIRunner,
405
+ ClaudeInternalRunner,
406
+ CodebuddyRunner,
407
+ CursorAgentRunner,
408
+ createAIRunner
409
+ };
410
+ //# sourceMappingURL=chunk-SWG2Y7YX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai-runner/BaseAIRunner.ts","../src/shutdown/ShutdownSignal.ts","../src/ai-runner/ClaudeInternalRunner.ts","../src/ai-runner/CodebuddyRunner.ts","../src/ai-runner/CursorAgentRunner.ts","../src/ai-runner/index.ts"],"sourcesContent":["import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\nimport { logger as rootLogger } from '../logger.js';\nimport { isShuttingDown } from '../shutdown/ShutdownSignal.js';\nimport type { RunOptions, RunResult, StreamEvent } from './AIRunner.js';\n\nconst logger = rootLogger.child('AIRunner');\n\nconst SIGKILL_GRACE_MS = 10_000;\n\ninterface ActiveChild {\n child: ChildProcess;\n workDir: string;\n}\n\nexport abstract class BaseAIRunner {\n protected abstract getBinary(): string;\n protected abstract buildArgs(options: RunOptions): string[];\n protected abstract getSpawnOptions(options: RunOptions): SpawnOptions;\n\n private activeChildren = new Map<ChildProcess, ActiveChild>();\n\n async run(options: RunOptions): Promise<RunResult> {\n if (isShuttingDown()) {\n logger.warn('AI runner skipped — service is shutting down');\n return { success: false, output: 'Service shutting down', exitCode: null };\n }\n\n const { prompt, workDir, timeoutMs, sessionId, continueSession, onStreamEvent } = options;\n\n logger.info('Running AI runner', {\n workDir,\n timeoutMs,\n continueSession: !!continueSession,\n sessionId,\n });\n\n return new Promise<RunResult>((resolve) => {\n const chunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let timedOut = false;\n let lineBuffer = '';\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n\n const binary = this.getBinary();\n const args = this.buildArgs(options);\n const spawnOpts = this.getSpawnOptions(options);\n\n const child = spawn(binary, args, {\n ...spawnOpts,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n this.activeChildren.set(child, { child, workDir });\n\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill('SIGTERM');\n logger.warn('AI runner timed out', { timeoutMs });\n\n killTimer = setTimeout(() => {\n if (child.exitCode === null) {\n child.kill('SIGKILL');\n logger.warn('AI runner force-killed (SIGKILL fallback)', { workDir });\n }\n }, SIGKILL_GRACE_MS);\n }, timeoutMs);\n\n const flushInterval = onStreamEvent\n ? setInterval(() => {\n if (lineBuffer.trim()) {\n onStreamEvent({ type: 'partial', content: lineBuffer.trim(), timestamp: new Date().toISOString() });\n }\n }, 200)\n : undefined;\n\n child.stdout.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n\n if (onStreamEvent) {\n lineBuffer += chunk.toString();\n let newlineIdx: number;\n while ((newlineIdx = lineBuffer.indexOf('\\n')) !== -1) {\n const line = lineBuffer.slice(0, newlineIdx).trim();\n lineBuffer = lineBuffer.slice(newlineIdx + 1);\n if (!line) continue;\n this.emitStreamLine(line, onStreamEvent);\n }\n }\n });\n\n child.stderr.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n const text = chunk.toString();\n logger.debug('AI runner stderr: ' + text);\n if (onStreamEvent) {\n onStreamEvent({ type: 'stderr', content: text, timestamp: new Date().toISOString() });\n }\n });\n\n child.on('close', (code) => {\n this.activeChildren.delete(child);\n clearTimeout(timer);\n if (killTimer) clearTimeout(killTimer);\n if (flushInterval) clearInterval(flushInterval);\n\n if (onStreamEvent && lineBuffer.trim()) {\n this.emitStreamLine(lineBuffer.trim(), onStreamEvent);\n }\n\n const rawOutput = Buffer.concat(chunks as unknown as Uint8Array[]).toString('utf-8');\n const { output, resolvedSessionId } = this.parseOutput(rawOutput, sessionId);\n\n const success = code === 0 && !timedOut;\n logger.info('AI runner finished', { exitCode: code, success, timedOut });\n\n const stderrText = stderrChunks.length > 0 ? Buffer.concat(stderrChunks as unknown as Uint8Array[]).toString('utf-8').trim() : '';\n const finalOutput = (!success && !output.trim() && stderrText) ? stderrText : output;\n\n resolve({\n success,\n output: finalOutput,\n sessionId: resolvedSessionId ?? sessionId,\n exitCode: code,\n });\n });\n\n child.on('error', (err) => {\n this.activeChildren.delete(child);\n clearTimeout(timer);\n if (killTimer) clearTimeout(killTimer);\n if (flushInterval) clearInterval(flushInterval);\n logger.error('AI runner spawn error', { error: err.message });\n resolve({\n success: false,\n output: err.message,\n exitCode: null,\n });\n });\n\n child.stdin.write(prompt);\n child.stdin.end();\n });\n }\n\n killAll(): void {\n for (const [child] of this.activeChildren) {\n this.forceKillChild(child);\n }\n logger.info('Killed all active AI runner children', { count: this.activeChildren.size });\n }\n\n killByWorkDir(targetWorkDir: string): number {\n let killed = 0;\n for (const [child, entry] of this.activeChildren) {\n if (entry.workDir === targetWorkDir) {\n this.forceKillChild(child);\n killed++;\n }\n }\n if (killed > 0) {\n logger.info('Killed AI runner children by workDir', { workDir: targetWorkDir, killed });\n }\n return killed;\n }\n\n private forceKillChild(child: ChildProcess): void {\n try {\n if (child.exitCode === null) {\n child.kill('SIGTERM');\n setTimeout(() => {\n if (child.exitCode === null) {\n child.kill('SIGKILL');\n }\n }, SIGKILL_GRACE_MS);\n }\n } catch {\n // process may have already exited\n }\n }\n\n private emitStreamLine(line: string, onStreamEvent: (event: StreamEvent) => void): void {\n try {\n const parsed = JSON.parse(line) as Record<string, unknown>;\n onStreamEvent({\n type: (typeof parsed.type === 'string' ? parsed.type : 'raw'),\n content: parsed,\n timestamp: new Date().toISOString(),\n });\n } catch {\n onStreamEvent({ type: 'raw', content: line, timestamp: new Date().toISOString() });\n }\n }\n\n /**\n * Handles both stream-json (one JSON object per line) and legacy single-JSON formats.\n */\n parseOutput(rawOutput: string, _sessionId?: string): { output: string; resolvedSessionId?: string } {\n let output = rawOutput;\n let resolvedSessionId: string | undefined;\n\n const events = this.tryParseStreamJson(rawOutput);\n if (events) {\n output = events\n .filter((item: { type?: string }) => item.type === 'result')\n .map((item: { result?: string }) => item.result ?? '')\n .join('\\n');\n const sessionItem = events.find(\n (item: { type?: string; session_id?: string }) => item.session_id != null,\n );\n if (sessionItem) {\n resolvedSessionId = (sessionItem as { session_id: string }).session_id;\n }\n if (!output) {\n output = this.extractAssistantText(events);\n }\n if (!output) {\n output = this.summarizeStreamEvents(events);\n }\n return { output, resolvedSessionId };\n }\n\n try {\n const parsed = JSON.parse(rawOutput);\n if (Array.isArray(parsed)) {\n output = parsed\n .filter((item: { type?: string }) => item.type === 'result')\n .map((item: { result?: string }) => item.result ?? '')\n .join('\\n');\n const sessionItem = parsed.find(\n (item: { type?: string; session_id?: string }) => item.session_id != null,\n );\n if (sessionItem) {\n resolvedSessionId = sessionItem.session_id;\n }\n if (!output) {\n output = this.extractAssistantText(parsed);\n }\n } else if (parsed.result != null) {\n output = parsed.result;\n resolvedSessionId = parsed.session_id;\n }\n } catch {\n // raw text output\n }\n\n return { output, resolvedSessionId };\n }\n\n /**\n * Extract plain text from assistant events as fallback when result.result is empty.\n * Looks for message.content[].text in assistant-type events.\n */\n private extractAssistantText(events: Record<string, unknown>[]): string {\n const texts: string[] = [];\n for (const evt of events) {\n if (evt.type !== 'assistant') continue;\n const message = evt.message as { content?: Array<{ type?: string; text?: string }> } | undefined;\n if (!message?.content || !Array.isArray(message.content)) continue;\n for (const block of message.content) {\n if (block.type === 'text' && block.text) {\n texts.push(block.text);\n }\n }\n }\n return texts.join('');\n }\n\n private summarizeStreamEvents(events: Record<string, unknown>[]): string {\n const errorEvents = events.filter(\n (e) => e.type === 'error' || e.subtype === 'error',\n );\n if (errorEvents.length > 0) {\n const messages = errorEvents.map(\n (e) => String(e.message ?? e.error ?? JSON.stringify(e)),\n );\n return `AI runner 错误: ${messages.join('; ')}`;\n }\n\n const types = events.map((e) => String(e.type ?? 'unknown'));\n const typeCounts: Record<string, number> = {};\n for (const t of types) {\n typeCounts[t] = (typeCounts[t] ?? 0) + 1;\n }\n const summary = Object.entries(typeCounts)\n .map(([t, c]) => `${t}(${c})`)\n .join(', ');\n return `AI runner 未返回结果 (收到 ${events.length} 个事件: ${summary})`;\n }\n\n private tryParseStreamJson(raw: string): Record<string, unknown>[] | null {\n const lines = raw.split('\\n').filter((l) => l.trim());\n if (lines.length < 2) return null;\n\n const objects: Record<string, unknown>[] = [];\n for (const line of lines) {\n try {\n objects.push(JSON.parse(line) as Record<string, unknown>);\n } catch {\n return null;\n }\n }\n return objects;\n }\n}\n","let _shuttingDown = false;\n\nexport function isShuttingDown(): boolean {\n return _shuttingDown;\n}\n\nexport function setShuttingDown(): void {\n _shuttingDown = true;\n}\n","import type { SpawnOptions } from 'node:child_process';\nimport { BaseAIRunner } from './BaseAIRunner.js';\nimport type { RunOptions } from './AIRunner.js';\n\nexport class ClaudeInternalRunner extends BaseAIRunner {\n private readonly binary: string;\n private readonly nvmNodeVersion: string;\n private readonly model?: string;\n\n constructor(binary: string, nvmNodeVersion: string, model?: string) {\n super();\n this.binary = binary;\n this.nvmNodeVersion = nvmNodeVersion;\n this.model = model;\n }\n\n protected getBinary(): string {\n return this.binary;\n }\n\n protected buildArgs(options: RunOptions): string[] {\n const args = ['-p', '-', '--output-format', 'stream-json', '--verbose'];\n if (options.mode === 'plan') {\n args.push('--permission-mode', 'plan', '--allowedTools', 'Read,Grep,Glob,WebSearch');\n } else {\n args.push('--dangerously-skip-permissions');\n }\n if (this.model) {\n args.push('--model', this.model);\n }\n if (options.continueSession && options.sessionId) {\n args.push('--resume', options.sessionId);\n }\n return args;\n }\n\n protected getSpawnOptions(options: RunOptions): SpawnOptions {\n const { CLAUDECODE, ...env } = process.env;\n return {\n cwd: options.workDir,\n env: {\n ...env,\n NODE_VERSION: this.nvmNodeVersion,\n },\n };\n }\n}\n","import type { SpawnOptions } from 'node:child_process';\nimport { BaseAIRunner } from './BaseAIRunner.js';\nimport type { RunOptions } from './AIRunner.js';\n\nexport class CodebuddyRunner extends BaseAIRunner {\n private readonly binary: string;\n private readonly nvmNodeVersion: string;\n private readonly model?: string;\n\n constructor(binary: string, nvmNodeVersion: string, model?: string) {\n super();\n this.binary = binary;\n this.nvmNodeVersion = nvmNodeVersion;\n this.model = model;\n }\n\n protected getBinary(): string {\n return this.binary;\n }\n\n protected buildArgs(options: RunOptions): string[] {\n const args = ['-p', '--output-format', 'stream-json', '--verbose'];\n if (options.mode === 'plan') {\n args.push('--permission-mode', 'plan', '--allowedTools', 'Read,Grep,Glob,WebSearch');\n } else {\n args.push('-y');\n }\n if (this.model) {\n args.push('--model', this.model);\n }\n if (options.continueSession && options.sessionId) {\n args.push('--resume', options.sessionId);\n }\n return args;\n }\n\n protected getSpawnOptions(options: RunOptions): SpawnOptions {\n return {\n cwd: options.workDir,\n env: {\n ...process.env,\n NODE_VERSION: this.nvmNodeVersion,\n },\n };\n }\n}\n","import type { SpawnOptions } from 'node:child_process';\nimport { BaseAIRunner } from './BaseAIRunner.js';\nimport type { RunOptions } from './AIRunner.js';\n\nexport class CursorAgentRunner extends BaseAIRunner {\n private readonly binary: string;\n private readonly nvmNodeVersion: string;\n private readonly model?: string;\n\n constructor(binary: string, nvmNodeVersion: string, model?: string) {\n super();\n this.binary = binary;\n this.nvmNodeVersion = nvmNodeVersion;\n this.model = model;\n }\n\n protected getBinary(): string {\n return this.binary;\n }\n\n protected buildArgs(options: RunOptions): string[] {\n const args = [\n 'agent',\n '-p',\n '--force',\n '--trust',\n '--output-format',\n 'stream-json',\n '--workspace',\n options.workDir,\n ];\n if (options.mode === 'plan') {\n args.push('--mode', 'plan');\n }\n if (this.model) {\n args.push('--model', this.model);\n }\n if (options.continueSession && options.sessionId) {\n args.push('--resume', options.sessionId);\n }\n return args;\n }\n\n protected getSpawnOptions(_options: RunOptions): SpawnOptions {\n return {\n cwd: process.cwd(),\n env: {\n ...process.env,\n NODE_VERSION: this.nvmNodeVersion,\n },\n };\n }\n}\n","import type { AIRunner } from './AIRunner.js';\nimport { ClaudeInternalRunner } from './ClaudeInternalRunner.js';\nimport { CodebuddyRunner } from './CodebuddyRunner.js';\nimport { CursorAgentRunner } from './CursorAgentRunner.js';\n\nexport type { AIRunner, RunOptions, RunResult, StreamEvent } from './AIRunner.js';\nexport { BaseAIRunner } from './BaseAIRunner.js';\nexport { ClaudeInternalRunner } from './ClaudeInternalRunner.js';\nexport { CodebuddyRunner } from './CodebuddyRunner.js';\nexport { CursorAgentRunner } from './CursorAgentRunner.js';\n\nexport interface AIConfig {\n mode: 'claude-internal' | 'cursor-agent' | 'codebuddy';\n binary: string;\n phaseTimeoutMs: number;\n nvmNodeVersion: string;\n model?: string;\n}\n\nexport function createAIRunner(ai: AIConfig): AIRunner {\n switch (ai.mode) {\n case 'cursor-agent':\n return new CursorAgentRunner(ai.binary, ai.nvmNodeVersion, ai.model);\n case 'codebuddy':\n return new CodebuddyRunner(ai.binary, ai.nvmNodeVersion, ai.model);\n default:\n return new ClaudeInternalRunner(ai.binary, ai.nvmNodeVersion, ai.model);\n }\n}\n"],"mappings":";;;;;AAAA,SAAS,aAAmD;;;ACA5D,IAAI,gBAAgB;AAEb,SAAS,iBAA0B;AACxC,SAAO;AACT;AAEO,SAAS,kBAAwB;AACtC,kBAAgB;AAClB;;;ADHA,IAAMA,UAAS,OAAW,MAAM,UAAU;AAE1C,IAAM,mBAAmB;AAOlB,IAAe,eAAf,MAA4B;AAAA,EAKzB,iBAAiB,oBAAI,IAA+B;AAAA,EAE5D,MAAM,IAAI,SAAyC;AACjD,QAAI,eAAe,GAAG;AACpB,MAAAA,QAAO,KAAK,mDAA8C;AAC1D,aAAO,EAAE,SAAS,OAAO,QAAQ,yBAAyB,UAAU,KAAK;AAAA,IAC3E;AAEA,UAAM,EAAE,QAAQ,SAAS,WAAW,WAAW,iBAAiB,cAAc,IAAI;AAElF,IAAAA,QAAO,KAAK,qBAAqB;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,iBAAiB,CAAC,CAAC;AAAA,MACnB;AAAA,IACF,CAAC;AAED,WAAO,IAAI,QAAmB,CAAC,YAAY;AACzC,YAAM,SAAmB,CAAC;AAC1B,YAAM,eAAyB,CAAC;AAChC,UAAI,WAAW;AACf,UAAI,aAAa;AACjB,UAAI;AAEJ,YAAM,SAAS,KAAK,UAAU;AAC9B,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,YAAM,YAAY,KAAK,gBAAgB,OAAO;AAE9C,YAAM,QAAQ,MAAM,QAAQ,MAAM;AAAA,QAChC,GAAG;AAAA,QACH,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAChC,CAAC;AAED,WAAK,eAAe,IAAI,OAAO,EAAE,OAAO,QAAQ,CAAC;AAEjD,YAAM,QAAQ,WAAW,MAAM;AAC7B,mBAAW;AACX,cAAM,KAAK,SAAS;AACpB,QAAAA,QAAO,KAAK,uBAAuB,EAAE,UAAU,CAAC;AAEhD,oBAAY,WAAW,MAAM;AAC3B,cAAI,MAAM,aAAa,MAAM;AAC3B,kBAAM,KAAK,SAAS;AACpB,YAAAA,QAAO,KAAK,6CAA6C,EAAE,QAAQ,CAAC;AAAA,UACtE;AAAA,QACF,GAAG,gBAAgB;AAAA,MACrB,GAAG,SAAS;AAEZ,YAAM,gBAAgB,gBAClB,YAAY,MAAM;AAChB,YAAI,WAAW,KAAK,GAAG;AACrB,wBAAc,EAAE,MAAM,WAAW,SAAS,WAAW,KAAK,GAAG,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAAA,QACpG;AAAA,MACF,GAAG,GAAG,IACN;AAEJ,YAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,eAAO,KAAK,KAAK;AAEjB,YAAI,eAAe;AACjB,wBAAc,MAAM,SAAS;AAC7B,cAAI;AACJ,kBAAQ,aAAa,WAAW,QAAQ,IAAI,OAAO,IAAI;AACrD,kBAAM,OAAO,WAAW,MAAM,GAAG,UAAU,EAAE,KAAK;AAClD,yBAAa,WAAW,MAAM,aAAa,CAAC;AAC5C,gBAAI,CAAC,KAAM;AACX,iBAAK,eAAe,MAAM,aAAa;AAAA,UACzC;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,qBAAa,KAAK,KAAK;AACvB,cAAM,OAAO,MAAM,SAAS;AAC5B,QAAAA,QAAO,MAAM,uBAAuB,IAAI;AACxC,YAAI,eAAe;AACjB,wBAAc,EAAE,MAAM,UAAU,SAAS,MAAM,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAAA,QACtF;AAAA,MACF,CAAC;AAED,YAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,aAAK,eAAe,OAAO,KAAK;AAChC,qBAAa,KAAK;AAClB,YAAI,UAAW,cAAa,SAAS;AACrC,YAAI,cAAe,eAAc,aAAa;AAE9C,YAAI,iBAAiB,WAAW,KAAK,GAAG;AACtC,eAAK,eAAe,WAAW,KAAK,GAAG,aAAa;AAAA,QACtD;AAEA,cAAM,YAAY,OAAO,OAAO,MAAiC,EAAE,SAAS,OAAO;AACnF,cAAM,EAAE,QAAQ,kBAAkB,IAAI,KAAK,YAAY,WAAW,SAAS;AAE3E,cAAM,UAAU,SAAS,KAAK,CAAC;AAC/B,QAAAA,QAAO,KAAK,sBAAsB,EAAE,UAAU,MAAM,SAAS,SAAS,CAAC;AAEvE,cAAM,aAAa,aAAa,SAAS,IAAI,OAAO,OAAO,YAAuC,EAAE,SAAS,OAAO,EAAE,KAAK,IAAI;AAC/H,cAAM,cAAe,CAAC,WAAW,CAAC,OAAO,KAAK,KAAK,aAAc,aAAa;AAE9E,gBAAQ;AAAA,UACN;AAAA,UACA,QAAQ;AAAA,UACR,WAAW,qBAAqB;AAAA,UAChC,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,CAAC;AAED,YAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,aAAK,eAAe,OAAO,KAAK;AAChC,qBAAa,KAAK;AAClB,YAAI,UAAW,cAAa,SAAS;AACrC,YAAI,cAAe,eAAc,aAAa;AAC9C,QAAAA,QAAO,MAAM,yBAAyB,EAAE,OAAO,IAAI,QAAQ,CAAC;AAC5D,gBAAQ;AAAA,UACN,SAAS;AAAA,UACT,QAAQ,IAAI;AAAA,UACZ,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,CAAC;AAED,YAAM,MAAM,MAAM,MAAM;AACxB,YAAM,MAAM,IAAI;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EAEA,UAAgB;AACd,eAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB;AACzC,WAAK,eAAe,KAAK;AAAA,IAC3B;AACA,IAAAA,QAAO,KAAK,wCAAwC,EAAE,OAAO,KAAK,eAAe,KAAK,CAAC;AAAA,EACzF;AAAA,EAEA,cAAc,eAA+B;AAC3C,QAAI,SAAS;AACb,eAAW,CAAC,OAAO,KAAK,KAAK,KAAK,gBAAgB;AAChD,UAAI,MAAM,YAAY,eAAe;AACnC,aAAK,eAAe,KAAK;AACzB;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,GAAG;AACd,MAAAA,QAAO,KAAK,wCAAwC,EAAE,SAAS,eAAe,OAAO,CAAC;AAAA,IACxF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,OAA2B;AAChD,QAAI;AACF,UAAI,MAAM,aAAa,MAAM;AAC3B,cAAM,KAAK,SAAS;AACpB,mBAAW,MAAM;AACf,cAAI,MAAM,aAAa,MAAM;AAC3B,kBAAM,KAAK,SAAS;AAAA,UACtB;AAAA,QACF,GAAG,gBAAgB;AAAA,MACrB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,eAAe,MAAc,eAAmD;AACtF,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,oBAAc;AAAA,QACZ,MAAO,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,QACvD,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAAA,IACH,QAAQ;AACN,oBAAc,EAAE,MAAM,OAAO,SAAS,MAAM,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAAA,IACnF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,WAAmB,YAAqE;AAClG,QAAI,SAAS;AACb,QAAI;AAEJ,UAAM,SAAS,KAAK,mBAAmB,SAAS;AAChD,QAAI,QAAQ;AACV,eAAS,OACN,OAAO,CAAC,SAA4B,KAAK,SAAS,QAAQ,EAC1D,IAAI,CAAC,SAA8B,KAAK,UAAU,EAAE,EACpD,KAAK,IAAI;AACZ,YAAM,cAAc,OAAO;AAAA,QACzB,CAAC,SAAiD,KAAK,cAAc;AAAA,MACvE;AACA,UAAI,aAAa;AACf,4BAAqB,YAAuC;AAAA,MAC9D;AACA,UAAI,CAAC,QAAQ;AACX,iBAAS,KAAK,qBAAqB,MAAM;AAAA,MAC3C;AACA,UAAI,CAAC,QAAQ;AACX,iBAAS,KAAK,sBAAsB,MAAM;AAAA,MAC5C;AACA,aAAO,EAAE,QAAQ,kBAAkB;AAAA,IACrC;AAEA,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,SAAS;AACnC,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,iBAAS,OACN,OAAO,CAAC,SAA4B,KAAK,SAAS,QAAQ,EAC1D,IAAI,CAAC,SAA8B,KAAK,UAAU,EAAE,EACpD,KAAK,IAAI;AACZ,cAAM,cAAc,OAAO;AAAA,UACzB,CAAC,SAAiD,KAAK,cAAc;AAAA,QACvE;AACA,YAAI,aAAa;AACf,8BAAoB,YAAY;AAAA,QAClC;AACA,YAAI,CAAC,QAAQ;AACX,mBAAS,KAAK,qBAAqB,MAAM;AAAA,QAC3C;AAAA,MACF,WAAW,OAAO,UAAU,MAAM;AAChC,iBAAS,OAAO;AAChB,4BAAoB,OAAO;AAAA,MAC7B;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,WAAO,EAAE,QAAQ,kBAAkB;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,QAA2C;AACtE,UAAM,QAAkB,CAAC;AACzB,eAAW,OAAO,QAAQ;AACxB,UAAI,IAAI,SAAS,YAAa;AAC9B,YAAM,UAAU,IAAI;AACpB,UAAI,CAAC,SAAS,WAAW,CAAC,MAAM,QAAQ,QAAQ,OAAO,EAAG;AAC1D,iBAAW,SAAS,QAAQ,SAAS;AACnC,YAAI,MAAM,SAAS,UAAU,MAAM,MAAM;AACvC,gBAAM,KAAK,MAAM,IAAI;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,WAAO,MAAM,KAAK,EAAE;AAAA,EACtB;AAAA,EAEQ,sBAAsB,QAA2C;AACvE,UAAM,cAAc,OAAO;AAAA,MACzB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,YAAY;AAAA,IAC7C;AACA,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,WAAW,YAAY;AAAA,QAC3B,CAAC,MAAM,OAAO,EAAE,WAAW,EAAE,SAAS,KAAK,UAAU,CAAC,CAAC;AAAA,MACzD;AACA,aAAO,2BAAiB,SAAS,KAAK,IAAI,CAAC;AAAA,IAC7C;AAEA,UAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,OAAO,EAAE,QAAQ,SAAS,CAAC;AAC3D,UAAM,aAAqC,CAAC;AAC5C,eAAW,KAAK,OAAO;AACrB,iBAAW,CAAC,KAAK,WAAW,CAAC,KAAK,KAAK;AAAA,IACzC;AACA,UAAM,UAAU,OAAO,QAAQ,UAAU,EACtC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,EAC5B,KAAK,IAAI;AACZ,WAAO,0DAAuB,OAAO,MAAM,wBAAS,OAAO;AAAA,EAC7D;AAAA,EAEQ,mBAAmB,KAA+C;AACxE,UAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AACpD,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,UAAqC,CAAC;AAC5C,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,gBAAQ,KAAK,KAAK,MAAM,IAAI,CAA4B;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AE3SO,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgB,gBAAwB,OAAgB;AAClE,UAAM;AACN,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEU,YAAoB;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,UAAU,SAA+B;AACjD,UAAM,OAAO,CAAC,MAAM,KAAK,mBAAmB,eAAe,WAAW;AACtE,QAAI,QAAQ,SAAS,QAAQ;AAC3B,WAAK,KAAK,qBAAqB,QAAQ,kBAAkB,0BAA0B;AAAA,IACrF,OAAO;AACL,WAAK,KAAK,gCAAgC;AAAA,IAC5C;AACA,QAAI,KAAK,OAAO;AACd,WAAK,KAAK,WAAW,KAAK,KAAK;AAAA,IACjC;AACA,QAAI,QAAQ,mBAAmB,QAAQ,WAAW;AAChD,WAAK,KAAK,YAAY,QAAQ,SAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEU,gBAAgB,SAAmC;AAC3D,UAAM,EAAE,YAAY,GAAG,IAAI,IAAI,QAAQ;AACvC,WAAO;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK;AAAA,QACH,GAAG;AAAA,QACH,cAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;;;AC1CO,IAAM,kBAAN,cAA8B,aAAa;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgB,gBAAwB,OAAgB;AAClE,UAAM;AACN,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEU,YAAoB;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,UAAU,SAA+B;AACjD,UAAM,OAAO,CAAC,MAAM,mBAAmB,eAAe,WAAW;AACjE,QAAI,QAAQ,SAAS,QAAQ;AAC3B,WAAK,KAAK,qBAAqB,QAAQ,kBAAkB,0BAA0B;AAAA,IACrF,OAAO;AACL,WAAK,KAAK,IAAI;AAAA,IAChB;AACA,QAAI,KAAK,OAAO;AACd,WAAK,KAAK,WAAW,KAAK,KAAK;AAAA,IACjC;AACA,QAAI,QAAQ,mBAAmB,QAAQ,WAAW;AAChD,WAAK,KAAK,YAAY,QAAQ,SAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEU,gBAAgB,SAAmC;AAC3D,WAAO;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,cAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;;;ACzCO,IAAM,oBAAN,cAAgC,aAAa;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgB,gBAAwB,OAAgB;AAClE,UAAM;AACN,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEU,YAAoB;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,UAAU,SAA+B;AACjD,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AACA,QAAI,QAAQ,SAAS,QAAQ;AAC3B,WAAK,KAAK,UAAU,MAAM;AAAA,IAC5B;AACA,QAAI,KAAK,OAAO;AACd,WAAK,KAAK,WAAW,KAAK,KAAK;AAAA,IACjC;AACA,QAAI,QAAQ,mBAAmB,QAAQ,WAAW;AAChD,WAAK,KAAK,YAAY,QAAQ,SAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEU,gBAAgB,UAAoC;AAC5D,WAAO;AAAA,MACL,KAAK,QAAQ,IAAI;AAAA,MACjB,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,cAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;;;ACjCO,SAAS,eAAe,IAAwB;AACrD,UAAQ,GAAG,MAAM;AAAA,IACf,KAAK;AACH,aAAO,IAAI,kBAAkB,GAAG,QAAQ,GAAG,gBAAgB,GAAG,KAAK;AAAA,IACrE,KAAK;AACH,aAAO,IAAI,gBAAgB,GAAG,QAAQ,GAAG,gBAAgB,GAAG,KAAK;AAAA,IACnE;AACE,aAAO,IAAI,qBAAqB,GAAG,QAAQ,GAAG,gBAAgB,GAAG,KAAK;AAAA,EAC1E;AACF;","names":["logger"]}
@@ -0,0 +1,59 @@
1
+ // src/logger.ts
2
+ var LOG_LEVELS = {
3
+ debug: 0,
4
+ info: 1,
5
+ warn: 2,
6
+ error: 3
7
+ };
8
+ var Logger = class _Logger {
9
+ level = "info";
10
+ context;
11
+ constructor(context) {
12
+ this.context = context;
13
+ const envLevel = process.env.LOG_LEVEL;
14
+ if (envLevel && envLevel in LOG_LEVELS) {
15
+ this.level = envLevel;
16
+ }
17
+ }
18
+ child(context) {
19
+ const child = new _Logger(this.context ? `${this.context}:${context}` : context);
20
+ child.level = this.level;
21
+ return child;
22
+ }
23
+ format(level, message, meta) {
24
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
25
+ const prefix = this.context ? `[${this.context}]` : "";
26
+ const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
27
+ return `${ts} ${level.toUpperCase().padEnd(5)} ${prefix} ${message}${metaStr}`;
28
+ }
29
+ log(level, message, meta) {
30
+ if (LOG_LEVELS[level] < LOG_LEVELS[this.level]) return;
31
+ const line = this.format(level, message, meta);
32
+ if (level === "error") {
33
+ console.error(line);
34
+ } else if (level === "warn") {
35
+ console.warn(line);
36
+ } else {
37
+ console.log(line);
38
+ }
39
+ }
40
+ debug(message, meta) {
41
+ this.log("debug", message, meta);
42
+ }
43
+ info(message, meta) {
44
+ this.log("info", message, meta);
45
+ }
46
+ warn(message, meta) {
47
+ this.log("warn", message, meta);
48
+ }
49
+ error(message, meta) {
50
+ this.log("error", message, meta);
51
+ }
52
+ };
53
+ var logger = new Logger();
54
+
55
+ export {
56
+ Logger,
57
+ logger
58
+ };
59
+ //# sourceMappingURL=chunk-TZ6C7HL5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logger.ts"],"sourcesContent":["type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nclass Logger {\n private level: LogLevel = 'info';\n private context?: string;\n\n constructor(context?: string) {\n this.context = context;\n const envLevel = process.env.LOG_LEVEL as LogLevel | undefined;\n if (envLevel && envLevel in LOG_LEVELS) {\n this.level = envLevel;\n }\n }\n\n child(context: string): Logger {\n const child = new Logger(this.context ? `${this.context}:${context}` : context);\n child.level = this.level;\n return child;\n }\n\n private format(level: LogLevel, message: string, meta?: Record<string, unknown>): string {\n const ts = new Date().toISOString();\n const prefix = this.context ? `[${this.context}]` : '';\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${ts} ${level.toUpperCase().padEnd(5)} ${prefix} ${message}${metaStr}`;\n }\n\n private log(level: LogLevel, message: string, meta?: Record<string, unknown>): void {\n if (LOG_LEVELS[level] < LOG_LEVELS[this.level]) return;\n const line = this.format(level, message, meta);\n if (level === 'error') {\n console.error(line);\n } else if (level === 'warn') {\n console.warn(line);\n } else {\n console.log(line);\n }\n }\n\n debug(message: string, meta?: Record<string, unknown>): void {\n this.log('debug', message, meta);\n }\n\n info(message: string, meta?: Record<string, unknown>): void {\n this.log('info', message, meta);\n }\n\n warn(message: string, meta?: Record<string, unknown>): void {\n this.log('warn', message, meta);\n }\n\n error(message: string, meta?: Record<string, unknown>): void {\n this.log('error', message, meta);\n }\n}\n\nexport const logger = new Logger();\nexport { Logger };\n"],"mappings":";AAEA,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,SAAN,MAAM,QAAO;AAAA,EACH,QAAkB;AAAA,EAClB;AAAA,EAER,YAAY,SAAkB;AAC5B,SAAK,UAAU;AACf,UAAM,WAAW,QAAQ,IAAI;AAC7B,QAAI,YAAY,YAAY,YAAY;AACtC,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAM,SAAyB;AAC7B,UAAM,QAAQ,IAAI,QAAO,KAAK,UAAU,GAAG,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO;AAC9E,UAAM,QAAQ,KAAK;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,OAAO,OAAiB,SAAiB,MAAwC;AACvF,UAAM,MAAK,oBAAI,KAAK,GAAE,YAAY;AAClC,UAAM,SAAS,KAAK,UAAU,IAAI,KAAK,OAAO,MAAM;AACpD,UAAM,UAAU,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AACpD,WAAO,GAAG,EAAE,IAAI,MAAM,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM,IAAI,OAAO,GAAG,OAAO;AAAA,EAC9E;AAAA,EAEQ,IAAI,OAAiB,SAAiB,MAAsC;AAClF,QAAI,WAAW,KAAK,IAAI,WAAW,KAAK,KAAK,EAAG;AAChD,UAAM,OAAO,KAAK,OAAO,OAAO,SAAS,IAAI;AAC7C,QAAI,UAAU,SAAS;AACrB,cAAQ,MAAM,IAAI;AAAA,IACpB,WAAW,UAAU,QAAQ;AAC3B,cAAQ,KAAK,IAAI;AAAA,IACnB,OAAO;AACL,cAAQ,IAAI,IAAI;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,MAAsC;AAC3D,SAAK,IAAI,SAAS,SAAS,IAAI;AAAA,EACjC;AAAA,EAEA,KAAK,SAAiB,MAAsC;AAC1D,SAAK,IAAI,QAAQ,SAAS,IAAI;AAAA,EAChC;AAAA,EAEA,KAAK,SAAiB,MAAsC;AAC1D,SAAK,IAAI,QAAQ,SAAS,IAAI;AAAA,EAChC;AAAA,EAEA,MAAM,SAAiB,MAAsC;AAC3D,SAAK,IAAI,SAAS,SAAS,IAAI;AAAA,EACjC;AACF;AAEO,IAAM,SAAS,IAAI,OAAO;","names":[]}
@@ -0,0 +1,8 @@
1
+ interface AnalyzeCommandOptions {
2
+ dir?: string;
3
+ output?: string;
4
+ force?: boolean;
5
+ }
6
+ export declare function analyzeCommand(opts: AnalyzeCommandOptions): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/analyze.ts"],"names":[],"mappings":"AAOA,UAAU,qBAAqB;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAsBD,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC/E"}
package/dist/cli.js CHANGED
@@ -23,17 +23,21 @@ var pkg = JSON.parse(readFileSync(findPackageJson(), "utf-8"));
23
23
  var program = new Command();
24
24
  program.name("issue-auto-finish").description("Issue Auto-Finish: AI-powered issue resolution daemon").version(pkg.version);
25
25
  program.command("init").description("Launch the interactive web setup wizard").option("-p, --port <port>", "Port for the setup wizard", "3456").option("-c, --config <path>", "Config file path to generate").action(async (opts) => {
26
- const { initCommand } = await import("./init-L3VIWCOV.js");
26
+ const { initCommand } = await import("./init-LZGCIHE7.js");
27
27
  await initCommand({ port: parseInt(opts.port, 10), config: opts.config });
28
28
  });
29
29
  program.command("start").description("Start the issue-auto-finish daemon service").option("-c, --config <path>", "Path to .env config file").action(async (opts) => {
30
- const { startCommand } = await import("./start-TVN4SS6E.js");
30
+ const { startCommand } = await import("./start-NMQHUKGF.js");
31
31
  await startCommand({ config: opts.config });
32
32
  });
33
33
  program.command("doctor").description("Check environment dependencies").action(async () => {
34
- const { doctorCommand } = await import("./doctor-B26Q6JWI.js");
34
+ const { doctorCommand } = await import("./doctor-ZPGIBA5N.js");
35
35
  await doctorCommand();
36
36
  });
37
+ program.command("analyze").description("Analyze target repository and generate knowledge.json").option("-d, --dir <path>", "Project directory to analyze (defaults to PROJECT_WORK_DIR)").option("-o, --output <path>", "Output path for knowledge.json").option("-f, --force", "Overwrite existing knowledge.json").action(async (opts) => {
38
+ const { analyzeCommand } = await import("./analyze-I7UOJB4F.js");
39
+ await analyzeCommand({ dir: opts.dir, output: opts.output, force: opts.force });
40
+ });
37
41
  program.command("status").description("Show service status").option("-p, --port <port>", "Web UI port to query", "3000").action(async (opts) => {
38
42
  const port = parseInt(opts.port, 10);
39
43
  try {
@@ -69,5 +73,65 @@ function formatUptime(ms) {
69
73
  if (m > 0) return `${m}m ${s % 60}s`;
70
74
  return `${s}s`;
71
75
  }
76
+ program.command("update").description("Check for updates or trigger an update").option("--check", "Only check for a new version without updating").option("-p, --port <port>", "Web UI port to query", "3000").action(async (opts) => {
77
+ const port = parseInt(opts.port, 10);
78
+ const checkOnly = !!opts.check;
79
+ try {
80
+ if (checkOnly) {
81
+ const resp = await fetch(`http://localhost:${port}/api/system/update-check`, {
82
+ method: "POST",
83
+ signal: AbortSignal.timeout(2e4)
84
+ });
85
+ if (!resp.ok) {
86
+ const body = await resp.json();
87
+ console.error(` Check failed: ${body.error ?? resp.statusText}`);
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ const result = await resp.json();
92
+ console.log(`
93
+ Current version: ${result.currentVersion}`);
94
+ console.log(` Latest version: ${result.latestVersion}`);
95
+ console.log(` Update available: ${result.hasUpdate ? "Yes" : "No"}
96
+ `);
97
+ } else {
98
+ const checkResp = await fetch(`http://localhost:${port}/api/system/update-check`, {
99
+ method: "POST",
100
+ signal: AbortSignal.timeout(2e4)
101
+ });
102
+ if (!checkResp.ok) {
103
+ const body = await checkResp.json();
104
+ console.error(` Check failed: ${body.error ?? checkResp.statusText}`);
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+ const result = await checkResp.json();
109
+ if (!result.hasUpdate) {
110
+ console.log(`
111
+ Already up to date (v${result.currentVersion})
112
+ `);
113
+ return;
114
+ }
115
+ console.log(`
116
+ Updating from v${result.currentVersion} to v${result.latestVersion}...`);
117
+ const updateResp = await fetch(`http://localhost:${port}/api/system/update`, {
118
+ method: "POST",
119
+ signal: AbortSignal.timeout(1e4)
120
+ });
121
+ if (!updateResp.ok) {
122
+ const body = await updateResp.json();
123
+ console.error(` Update trigger failed: ${body.error ?? updateResp.statusText}`);
124
+ process.exitCode = 1;
125
+ return;
126
+ }
127
+ console.log(" Update triggered. The service will restart automatically via PM2.\n");
128
+ }
129
+ } catch {
130
+ console.error(`
131
+ Cannot connect to service at localhost:${port}.`);
132
+ console.error(" Is the service running? Start with: issue-auto-finish start\n");
133
+ process.exitCode = 1;
134
+ }
135
+ });
72
136
  program.parse();
73
137
  //# sourceMappingURL=cli.js.map