@xdevops/issue-auto-finish 1.0.2 → 1.0.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.
- package/dist/KnowledgeAnalyzer-EZSJT2MJ.js +13 -0
- package/dist/KnowledgeAnalyzer-EZSJT2MJ.js.map +1 -0
- package/dist/KnowledgeStore-4ROC6F56.js +10 -0
- package/dist/KnowledgeStore-4ROC6F56.js.map +1 -0
- package/dist/ai-runner/AIRunner.d.ts +2 -0
- package/dist/ai-runner/AIRunner.d.ts.map +1 -1
- package/dist/ai-runner/BaseAIRunner.d.ts +9 -0
- package/dist/ai-runner/BaseAIRunner.d.ts.map +1 -1
- package/dist/ai-runner-RGAJPOOW.js +16 -0
- package/dist/ai-runner-RGAJPOOW.js.map +1 -0
- package/dist/analyze-ONQDTYCN.js +72 -0
- package/dist/analyze-ONQDTYCN.js.map +1 -0
- package/dist/chunk-3JUHZGX5.js +171 -0
- package/dist/chunk-3JUHZGX5.js.map +1 -0
- package/dist/chunk-5JYCGAU3.js +318 -0
- package/dist/chunk-5JYCGAU3.js.map +1 -0
- package/dist/chunk-5VUB3UUK.js +643 -0
- package/dist/chunk-5VUB3UUK.js.map +1 -0
- package/dist/{chunk-OWVT3Z34.js → chunk-JFYAXNNS.js} +121 -31
- package/dist/chunk-JFYAXNNS.js.map +1 -0
- package/dist/chunk-MH6LHFPB.js +188 -0
- package/dist/chunk-MH6LHFPB.js.map +1 -0
- package/dist/{chunk-TBIEB3JY.js → chunk-N5YK6YVI.js} +592 -767
- package/dist/chunk-N5YK6YVI.js.map +1 -0
- package/dist/{chunk-RIUI4ROA.js → chunk-PECYMYAK.js} +2 -2
- package/dist/{chunk-I3T573SU.js → chunk-PTIL5AY2.js} +65 -2
- package/dist/chunk-PTIL5AY2.js.map +1 -0
- package/dist/chunk-SWG2Y7YX.js +410 -0
- package/dist/chunk-SWG2Y7YX.js.map +1 -0
- package/dist/chunk-TZ6C7HL5.js +59 -0
- package/dist/chunk-TZ6C7HL5.js.map +1 -0
- package/dist/{chunk-IDUKWCC2.js → chunk-VFQYIC6L.js} +1151 -80
- package/dist/chunk-VFQYIC6L.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +8 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli.js +67 -3
- package/dist/cli.js.map +1 -1
- package/dist/clients/GongfengClient.d.ts +5 -0
- package/dist/clients/GongfengClient.d.ts.map +1 -1
- package/dist/config-6GFBDMGD.js +7 -0
- package/dist/config-6GFBDMGD.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/{doctor-B26Q6JWI.js → doctor-ZPGIBA5N.js} +3 -3
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/git/GitOperations.d.ts +12 -0
- package/dist/git/GitOperations.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -5
- package/dist/{init-L3VIWCOV.js → init-QDTII4SP.js} +10 -5
- package/dist/init-QDTII4SP.js.map +1 -0
- package/dist/knowledge/KnowledgeAnalyzer.d.ts +31 -0
- package/dist/knowledge/KnowledgeAnalyzer.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeDefaults.d.ts +7 -0
- package/dist/knowledge/KnowledgeDefaults.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeEntry.d.ts +30 -0
- package/dist/knowledge/KnowledgeEntry.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeLoader.d.ts +18 -0
- package/dist/knowledge/KnowledgeLoader.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeStore.d.ts +35 -0
- package/dist/knowledge/KnowledgeStore.d.ts.map +1 -0
- package/dist/knowledge/ProjectKnowledge.d.ts +79 -0
- package/dist/knowledge/ProjectKnowledge.d.ts.map +1 -0
- package/dist/knowledge/analyze-prompt.d.ts +2 -0
- package/dist/knowledge/analyze-prompt.d.ts.map +1 -0
- package/dist/knowledge/importers/GongfengExtractor.d.ts +27 -0
- package/dist/knowledge/importers/GongfengExtractor.d.ts.map +1 -0
- package/dist/knowledge/importers/IwikiImporter.d.ts +21 -0
- package/dist/knowledge/importers/IwikiImporter.d.ts.map +1 -0
- package/dist/knowledge/index.d.ts +12 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/lib.js +19 -10
- package/dist/orchestrator/PipelineOrchestrator.d.ts +5 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
- package/dist/phases/BasePhase.d.ts.map +1 -1
- package/dist/poller/IssuePoller.d.ts +5 -0
- package/dist/poller/IssuePoller.d.ts.map +1 -1
- package/dist/prompts/chat-templates.d.ts +4 -0
- package/dist/prompts/chat-templates.d.ts.map +1 -0
- package/dist/prompts/templates.d.ts +11 -0
- package/dist/prompts/templates.d.ts.map +1 -1
- package/dist/rules/RuleResolver.d.ts +4 -0
- package/dist/rules/RuleResolver.d.ts.map +1 -1
- package/dist/run.js +11 -5
- package/dist/run.js.map +1 -1
- package/dist/services/ChatService.d.ts +39 -0
- package/dist/services/ChatService.d.ts.map +1 -0
- package/dist/shutdown/ShutdownSignal.d.ts +3 -0
- package/dist/shutdown/ShutdownSignal.d.ts.map +1 -0
- package/dist/{start-TVN4SS6E.js → start-EDOZC5WL.js} +1 -1
- package/dist/tracker/IssueState.d.ts +1 -0
- package/dist/tracker/IssueState.d.ts.map +1 -1
- package/dist/tracker/IssueTracker.d.ts +2 -0
- package/dist/tracker/IssueTracker.d.ts.map +1 -1
- package/dist/updater/AutoUpdater.d.ts +33 -0
- package/dist/updater/AutoUpdater.d.ts.map +1 -0
- package/dist/updater/UpdateExecutor.d.ts +7 -0
- package/dist/updater/UpdateExecutor.d.ts.map +1 -0
- package/dist/updater/VersionChecker.d.ts +22 -0
- package/dist/updater/VersionChecker.d.ts.map +1 -0
- package/dist/web/WebServer.d.ts +4 -0
- package/dist/web/WebServer.d.ts.map +1 -1
- package/dist/web/routes/api.d.ts +4 -0
- package/dist/web/routes/api.d.ts.map +1 -1
- package/dist/web/routes/chat.d.ts +7 -0
- package/dist/web/routes/chat.d.ts.map +1 -0
- package/dist/web/routes/knowledge.d.ts +13 -0
- package/dist/web/routes/knowledge.d.ts.map +1 -0
- package/dist/web/routes/setup.d.ts.map +1 -1
- package/dist/webhook/CommandExecutor.d.ts +4 -0
- package/dist/webhook/CommandExecutor.d.ts.map +1 -1
- package/dist/webhook/CommandParser.d.ts +2 -2
- package/dist/webhook/CommandParser.d.ts.map +1 -1
- package/dist/webhook/WebhookHandler.d.ts +8 -0
- package/dist/webhook/WebhookHandler.d.ts.map +1 -1
- package/dist/webhook/WebhookServer.d.ts +2 -0
- package/dist/webhook/WebhookServer.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/web/frontend/dist/assets/index-AcJ0lPIv.js +67 -0
- package/src/web/frontend/dist/assets/index-BbRt5BAr.css +1 -0
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/chunk-I3T573SU.js.map +0 -1
- package/dist/chunk-IDUKWCC2.js.map +0 -1
- package/dist/chunk-OWVT3Z34.js.map +0 -1
- package/dist/chunk-TBIEB3JY.js.map +0 -1
- package/dist/init-L3VIWCOV.js.map +0 -1
- package/src/web/frontend/dist/assets/index-CQdlU9PE.js +0 -65
- package/src/web/frontend/dist/assets/index-CgMEkyZJ.css +0 -1
- /package/dist/{chunk-RIUI4ROA.js.map → chunk-PECYMYAK.js.map} +0 -0
- /package/dist/{doctor-B26Q6JWI.js.map → doctor-ZPGIBA5N.js.map} +0 -0
- /package/dist/{start-TVN4SS6E.js.map → start-EDOZC5WL.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web/routes/setup.ts"],"sourcesContent":["import { Router, Request, Response } from 'express';\nimport fs from 'node:fs';\nimport { DependencyChecker } from '../../cli/setup/DependencyChecker.js';\nimport { ConfigGenerator, SetupConfig } from '../../cli/setup/ConfigGenerator.js';\nimport { collectStaticInfo, analyze } from '../../knowledge/KnowledgeAnalyzer.js';\nimport { getProjectKnowledge } from '../../knowledge/index.js';\n\nexport interface SetupRouterDeps {\n configPath?: string;\n onComplete?: (configPath: string) => void;\n serviceMode?: boolean;\n}\n\nexport function createSetupRouter(deps: SetupRouterDeps = {}): Router {\n const router = Router();\n const checker = new DependencyChecker();\n\n router.get('/api/setup/status', (_req: Request, res: Response) => {\n const initialized = deps.serviceMode || ConfigGenerator.isInitialized(deps.configPath);\n res.json({\n initialized,\n configPath: deps.configPath ?? ConfigGenerator.getGlobalConfigPath(),\n globalConfigDir: ConfigGenerator.getGlobalConfigDir(),\n });\n });\n\n router.get('/api/setup/check-env', async (_req: Request, res: Response) => {\n try {\n const aiMode = ((_req.query.aiMode as string) || undefined);\n const results = await checker.checkAll(aiMode);\n res.json({ dependencies: results });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/validate-token', async (req: Request, res: Response) => {\n const { apiUrl, token } = req.body as { apiUrl?: string; token?: string };\n if (!apiUrl || !token) {\n res.status(400).json({ error: 'apiUrl and token are required' });\n return;\n }\n try {\n const url = `${apiUrl.replace(/\\/$/, '')}/api/v3/user`;\n const resp = await fetch(url, {\n headers: { 'PRIVATE-TOKEN': token },\n signal: AbortSignal.timeout(10000),\n });\n if (resp.ok) {\n const user = (await resp.json()) as { username?: string; name?: string };\n res.json({ valid: true, user: { username: user.username, name: user.name } });\n } else {\n res.json({ valid: false, error: `HTTP ${resp.status}: ${resp.statusText}` });\n }\n } catch (err) {\n res.json({ valid: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/validate-repo', async (req: Request, res: Response) => {\n const { apiUrl, token, projectPath } = req.body as {\n apiUrl?: string; token?: string; projectPath?: string;\n };\n if (!apiUrl || !token || !projectPath) {\n res.status(400).json({ error: 'apiUrl, token, and projectPath are required' });\n return;\n }\n try {\n const encoded = encodeURIComponent(projectPath);\n const url = `${apiUrl.replace(/\\/$/, '')}/api/v3/projects/${encoded}`;\n const resp = await fetch(url, {\n headers: { 'PRIVATE-TOKEN': token },\n signal: AbortSignal.timeout(10000),\n });\n if (resp.ok) {\n const project = (await resp.json()) as {\n id?: number; name?: string; path_with_namespace?: string;\n };\n res.json({ valid: true, project });\n } else {\n res.json({ valid: false, error: `HTTP ${resp.status}: ${resp.statusText}` });\n }\n } catch (err) {\n res.json({ valid: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/install-dep', (req: Request, res: Response) => {\n const { name } = req.body as { name?: string };\n if (!name) {\n res.status(400).json({ error: 'name is required' });\n return;\n }\n\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.flushHeaders();\n\n (async () => {\n try {\n for await (const line of checker.install(name)) {\n res.write(`data: ${JSON.stringify({ line })}\\n\\n`);\n }\n res.write(`data: ${JSON.stringify({ done: true })}\\n\\n`);\n } catch (err) {\n res.write(`data: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n\n req.on('close', () => {\n res.end();\n });\n });\n\n router.post('/api/setup/generate-preview', (req: Request, res: Response) => {\n const config = req.body as SetupConfig;\n try {\n const preview = ConfigGenerator.generate(config);\n res.json({ preview });\n } catch (err) {\n res.status(400).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/complete', (req: Request, res: Response) => {\n const { config, outputPath } = req.body as {\n config?: SetupConfig;\n outputPath?: string;\n };\n if (!config) {\n res.status(400).json({ error: 'config is required' });\n return;\n }\n try {\n const targetPath = outputPath ?? deps.configPath;\n const savedPath = ConfigGenerator.write(config, targetPath);\n res.json({ success: true, configPath: savedPath });\n deps.onComplete?.(savedPath);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/check-path', (req: Request, res: Response) => {\n const { path: dirPath } = req.body as { path?: string };\n if (!dirPath) {\n res.status(400).json({ error: 'path is required' });\n return;\n }\n try {\n const exists = fs.existsSync(dirPath);\n const isDirectory = exists && fs.statSync(dirPath).isDirectory();\n const isGitRepo = exists && fs.existsSync(dirPath + '/.git');\n res.json({ exists, isDirectory, isGitRepo });\n } catch (err) {\n res.json({ exists: false, isDirectory: false, isGitRepo: false, error: (err as Error).message });\n }\n });\n\n router.post('/api/setup/analyze-repo', (req: Request, res: Response) => {\n const { workDir, aiRunner } = req.body as { workDir?: string; aiRunner?: unknown };\n if (!workDir) {\n res.status(400).json({ error: 'workDir is required' });\n return;\n }\n\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.flushHeaders();\n\n (async () => {\n try {\n res.write(`data: ${JSON.stringify({ step: 'collecting', message: 'Collecting project info...' })}\\n\\n`);\n const staticInfo = await collectStaticInfo(workDir);\n res.write(`data: ${JSON.stringify({ step: 'collected', message: 'Static info collected' })}\\n\\n`);\n\n // AI analysis requires an AI runner; for setup wizard we attempt if runner deps available\n res.write(`data: ${JSON.stringify({ step: 'analyzing', message: 'Running AI analysis...' })}\\n\\n`);\n\n try {\n const { loadConfig } = await import('../../config.js');\n const { createAIRunner } = await import('../../ai-runner/index.js');\n const config = loadConfig();\n const runner = createAIRunner(config.ai);\n const knowledge = await analyze({ workDir, aiRunner: runner });\n res.write(`data: ${JSON.stringify({ step: 'done', knowledge })}\\n\\n`);\n } catch (aiErr) {\n // AI not available, return static info only\n res.write(`data: ${JSON.stringify({ step: 'ai_unavailable', message: (aiErr as Error).message, staticInfo })}\\n\\n`);\n }\n } catch (err) {\n res.write(`data: ${JSON.stringify({ step: 'error', error: (err as Error).message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n\n req.on('close', () => {\n res.end();\n });\n });\n\n router.get('/api/setup/knowledge-preview', (_req: Request, res: Response) => {\n const knowledge = getProjectKnowledge();\n if (knowledge) {\n res.json({ exists: true, knowledge });\n } else {\n res.json({ exists: false, knowledge: null });\n }\n });\n\n return router;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -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":"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","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":[]}
|