@xdevops/issue-auto-finish 1.0.87 → 1.0.89
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/{AIRunnerRegistry-II3WWSFN.js → AIRunnerRegistry-CFDNWSXC.js} +6 -3
- package/dist/{LockNote-Z2CLDZNN.js → LockNote-W2JNVMW7.js} +3 -3
- package/dist/PtyRunner-NYASBTRP.js +33 -0
- package/dist/SdkRunner-U2OTOMZU.js +9 -0
- package/dist/ai-runner/AIRunner.d.ts +19 -1
- package/dist/ai-runner/AIRunner.d.ts.map +1 -1
- package/dist/ai-runner/AIRunnerRegistry.d.ts +8 -0
- package/dist/ai-runner/AIRunnerRegistry.d.ts.map +1 -1
- package/dist/ai-runner/PlanFileResolver.d.ts +53 -0
- package/dist/ai-runner/PlanFileResolver.d.ts.map +1 -0
- package/dist/ai-runner/PtyRunner.d.ts +45 -4
- package/dist/ai-runner/PtyRunner.d.ts.map +1 -1
- package/dist/ai-runner/SdkRunner.d.ts +22 -0
- package/dist/ai-runner/SdkRunner.d.ts.map +1 -0
- package/dist/ai-runner/index.d.ts +5 -2
- package/dist/ai-runner/index.d.ts.map +1 -1
- package/dist/ai-runner/sdk/ClaudeCodeSDK.d.ts +37 -0
- package/dist/ai-runner/sdk/ClaudeCodeSDK.d.ts.map +1 -0
- package/dist/ai-runner/sdk/Stream.d.ts +22 -0
- package/dist/ai-runner/sdk/Stream.d.ts.map +1 -0
- package/dist/ai-runner/sdk/types.d.ts +146 -0
- package/dist/ai-runner/sdk/types.d.ts.map +1 -0
- package/dist/{ai-runner-HLA44WI6.js → ai-runner-TOHVJJ76.js} +14 -5
- package/dist/{analyze-ZIXNC5GN.js → analyze-DBH4K3J7.js} +8 -6
- package/dist/{analyze-ZIXNC5GN.js.map → analyze-DBH4K3J7.js.map} +1 -1
- package/dist/{braindump-56WAY2RD.js → braindump-RYI4BGMG.js} +11 -9
- package/dist/{braindump-56WAY2RD.js.map → braindump-RYI4BGMG.js.map} +1 -1
- package/dist/{chunk-AVGZH64A.js → chunk-2RWGZPNF.js} +4 -1
- package/dist/chunk-2RWGZPNF.js.map +1 -0
- package/dist/chunk-4XMYOXGZ.js +1153 -0
- package/dist/chunk-4XMYOXGZ.js.map +1 -0
- package/dist/{chunk-UBQLXQ7I.js → chunk-5JBADEKR.js} +7 -7
- package/dist/{chunk-M5C2WILQ.js → chunk-5M5SB6ZA.js} +7 -5
- package/dist/{chunk-M5C2WILQ.js.map → chunk-5M5SB6ZA.js.map} +1 -1
- package/dist/{chunk-HDFNMVRQ.js → chunk-DVNAH2GV.js} +2 -2
- package/dist/{chunk-GXFG4JU6.js → chunk-EU4XFZ2T.js} +2 -2
- package/dist/{chunk-NZHKAPU6.js → chunk-FJTZKAJA.js} +9 -3
- package/dist/chunk-FJTZKAJA.js.map +1 -0
- package/dist/chunk-G7QI5WDI.js +14 -0
- package/dist/chunk-G7QI5WDI.js.map +1 -0
- package/dist/{chunk-2YQHKXLL.js → chunk-GPZX4DSY.js} +22 -6
- package/dist/chunk-GPZX4DSY.js.map +1 -0
- package/dist/{chunk-IP3QTP5A.js → chunk-IWSMQXBL.js} +189 -48
- package/dist/chunk-IWSMQXBL.js.map +1 -0
- package/dist/{chunk-O3WEV5W3.js → chunk-JMACM7AJ.js} +47 -9
- package/dist/chunk-JMACM7AJ.js.map +1 -0
- package/dist/chunk-MSL7ROVK.js +1 -0
- package/dist/{chunk-YCYVNRLF.js → chunk-OBGEEGQ3.js} +61 -19
- package/dist/chunk-OBGEEGQ3.js.map +1 -0
- package/dist/chunk-R32Q3RGK.js +666 -0
- package/dist/chunk-R32Q3RGK.js.map +1 -0
- package/dist/{chunk-SAMTXC4A.js → chunk-TFEPHOVE.js} +12 -17
- package/dist/chunk-TFEPHOVE.js.map +1 -0
- package/dist/{chunk-QZZGIZWC.js → chunk-XSX3PGQW.js} +63 -20
- package/dist/chunk-XSX3PGQW.js.map +1 -0
- package/dist/{chunk-2MESXJEZ.js → chunk-YNRKPQLS.js} +3 -3
- package/dist/cli/setup/PreflightChecker.d.ts +1 -0
- package/dist/cli/setup/PreflightChecker.d.ts.map +1 -1
- package/dist/cli/setup/env-metadata.d.ts.map +1 -1
- package/dist/cli.js +10 -9
- package/dist/cli.js.map +1 -1
- package/dist/{config-WTRSZLOC.js → config-23TBYFP5.js} +5 -4
- package/dist/config-schema.d.ts +6 -0
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/{doctor-37JNBGDN.js → doctor-ZG3DO7J5.js} +3 -3
- package/dist/errors/AIExecutionError.d.ts +3 -0
- package/dist/errors/AIExecutionError.d.ts.map +1 -1
- package/dist/{errors-S3BWYA4I.js → errors-J3ZRP66W.js} +2 -2
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.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 +16 -14
- package/dist/{init-QQDXGTPB.js → init-37DLQ5AJ.js} +9 -8
- package/dist/{init-QQDXGTPB.js.map → init-37DLQ5AJ.js.map} +1 -1
- package/dist/lib.js +10 -8
- package/dist/lib.js.map +1 -1
- package/dist/orchestrator/PendingDialogStore.d.ts +12 -0
- package/dist/orchestrator/PendingDialogStore.d.ts.map +1 -0
- package/dist/orchestrator/steps/FailureHandler.d.ts.map +1 -1
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
- package/dist/persistence/PlanPersistence.d.ts +5 -0
- package/dist/persistence/PlanPersistence.d.ts.map +1 -1
- package/dist/persistence/TodolistExtractor.d.ts +31 -0
- package/dist/persistence/TodolistExtractor.d.ts.map +1 -0
- package/dist/phases/BasePhase.d.ts.map +1 -1
- package/dist/phases/PhaseOutcome.d.ts +2 -0
- package/dist/phases/PhaseOutcome.d.ts.map +1 -1
- package/dist/phases/PlanPhase.d.ts.map +1 -1
- package/dist/prompts/templates.d.ts +2 -2
- package/dist/prompts/templates.d.ts.map +1 -1
- package/dist/{restart-BMILTP5X.js → restart-C7QBXT44.js} +9 -8
- package/dist/{restart-BMILTP5X.js.map → restart-C7QBXT44.js.map} +1 -1
- package/dist/run.js +16 -14
- package/dist/run.js.map +1 -1
- package/dist/start-66JO56AW.js +16 -0
- package/dist/start-66JO56AW.js.map +1 -0
- package/dist/tracker/IssueTracker.d.ts +6 -0
- package/dist/tracker/IssueTracker.d.ts.map +1 -1
- package/dist/web/routes/api.d.ts.map +1 -1
- package/package.json +5 -1
- package/src/web/frontend/dist/assets/index-DJzC2saL.css +1 -0
- package/src/web/frontend/dist/assets/{index-D_oTMuJU.js → index-Mnu8M3ww.js} +57 -57
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/PtyRunner-6UGI5STW.js +0 -22
- package/dist/chunk-2YQHKXLL.js.map +0 -1
- package/dist/chunk-AVGZH64A.js.map +0 -1
- package/dist/chunk-IP3QTP5A.js.map +0 -1
- package/dist/chunk-NZHKAPU6.js.map +0 -1
- package/dist/chunk-O3WEV5W3.js.map +0 -1
- package/dist/chunk-QZZGIZWC.js.map +0 -1
- package/dist/chunk-SAMTXC4A.js.map +0 -1
- package/dist/chunk-U237JSLB.js +0 -1
- package/dist/chunk-U6GWFTKA.js +0 -657
- package/dist/chunk-U6GWFTKA.js.map +0 -1
- package/dist/chunk-YCYVNRLF.js.map +0 -1
- package/dist/start-6QRW6IJI.js +0 -15
- package/src/web/frontend/dist/assets/index-COYziOhv.css +0 -1
- /package/dist/{AIRunnerRegistry-II3WWSFN.js.map → AIRunnerRegistry-CFDNWSXC.js.map} +0 -0
- /package/dist/{LockNote-Z2CLDZNN.js.map → LockNote-W2JNVMW7.js.map} +0 -0
- /package/dist/{PtyRunner-6UGI5STW.js.map → PtyRunner-NYASBTRP.js.map} +0 -0
- /package/dist/{ai-runner-HLA44WI6.js.map → SdkRunner-U2OTOMZU.js.map} +0 -0
- /package/dist/{chunk-U237JSLB.js.map → ai-runner-TOHVJJ76.js.map} +0 -0
- /package/dist/{chunk-UBQLXQ7I.js.map → chunk-5JBADEKR.js.map} +0 -0
- /package/dist/{chunk-HDFNMVRQ.js.map → chunk-DVNAH2GV.js.map} +0 -0
- /package/dist/{chunk-GXFG4JU6.js.map → chunk-EU4XFZ2T.js.map} +0 -0
- /package/dist/{config-WTRSZLOC.js.map → chunk-MSL7ROVK.js.map} +0 -0
- /package/dist/{chunk-2MESXJEZ.js.map → chunk-YNRKPQLS.js.map} +0 -0
- /package/dist/{errors-S3BWYA4I.js.map → config-23TBYFP5.js.map} +0 -0
- /package/dist/{doctor-37JNBGDN.js.map → doctor-ZG3DO7J5.js.map} +0 -0
- /package/dist/{start-6QRW6IJI.js.map → errors-J3ZRP66W.js.map} +0 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isShuttingDown
|
|
3
|
+
} from "./chunk-G7QI5WDI.js";
|
|
4
|
+
import {
|
|
5
|
+
logger
|
|
6
|
+
} from "./chunk-GF2RRYHB.js";
|
|
7
|
+
|
|
8
|
+
// src/ai-runner/SdkRunner.ts
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
|
|
12
|
+
// src/ai-runner/sdk/ClaudeCodeSDK.ts
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
import { createInterface } from "readline";
|
|
15
|
+
|
|
16
|
+
// src/ai-runner/sdk/Stream.ts
|
|
17
|
+
var Stream = class {
|
|
18
|
+
queue = [];
|
|
19
|
+
readResolve;
|
|
20
|
+
readReject;
|
|
21
|
+
isDone = false;
|
|
22
|
+
hasError;
|
|
23
|
+
started = false;
|
|
24
|
+
[Symbol.asyncIterator]() {
|
|
25
|
+
if (this.started) {
|
|
26
|
+
throw new Error("Stream can only be iterated once");
|
|
27
|
+
}
|
|
28
|
+
this.started = true;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
async next() {
|
|
32
|
+
if (this.queue.length > 0) {
|
|
33
|
+
return { done: false, value: this.queue.shift() };
|
|
34
|
+
}
|
|
35
|
+
if (this.isDone) {
|
|
36
|
+
return { done: true, value: void 0 };
|
|
37
|
+
}
|
|
38
|
+
if (this.hasError) {
|
|
39
|
+
throw this.hasError;
|
|
40
|
+
}
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
this.readResolve = resolve;
|
|
43
|
+
this.readReject = reject;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
enqueue(value) {
|
|
47
|
+
if (this.readResolve) {
|
|
48
|
+
const resolve = this.readResolve;
|
|
49
|
+
this.readResolve = void 0;
|
|
50
|
+
this.readReject = void 0;
|
|
51
|
+
resolve({ done: false, value });
|
|
52
|
+
} else {
|
|
53
|
+
this.queue.push(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
done() {
|
|
57
|
+
this.isDone = true;
|
|
58
|
+
if (this.readResolve) {
|
|
59
|
+
const resolve = this.readResolve;
|
|
60
|
+
this.readResolve = void 0;
|
|
61
|
+
this.readReject = void 0;
|
|
62
|
+
resolve({ done: true, value: void 0 });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
error(error) {
|
|
66
|
+
this.hasError = error;
|
|
67
|
+
if (this.readReject) {
|
|
68
|
+
const reject = this.readReject;
|
|
69
|
+
this.readResolve = void 0;
|
|
70
|
+
this.readReject = void 0;
|
|
71
|
+
reject(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async return() {
|
|
75
|
+
this.isDone = true;
|
|
76
|
+
return { done: true, value: void 0 };
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/ai-runner/sdk/types.ts
|
|
81
|
+
var AbortError = class extends Error {
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = "AbortError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/ai-runner/sdk/ClaudeCodeSDK.ts
|
|
89
|
+
var logger2 = logger.child("ClaudeCodeSDK");
|
|
90
|
+
var Query = class {
|
|
91
|
+
constructor(child, childStdin, processExitPromise, canCallTool) {
|
|
92
|
+
this.childStdin = childStdin;
|
|
93
|
+
this.child = child;
|
|
94
|
+
this.canCallTool = canCallTool;
|
|
95
|
+
this.readMessages(processExitPromise);
|
|
96
|
+
this.sdkMessages = this.readSdkMessages();
|
|
97
|
+
}
|
|
98
|
+
pendingControlResponses = /* @__PURE__ */ new Map();
|
|
99
|
+
cancelControllers = /* @__PURE__ */ new Map();
|
|
100
|
+
sdkMessages;
|
|
101
|
+
inputStream = new Stream();
|
|
102
|
+
canCallTool;
|
|
103
|
+
/** The underlying child process — exposed for kill/abort by SdkRunner. */
|
|
104
|
+
child;
|
|
105
|
+
setError(error) {
|
|
106
|
+
this.inputStream.error(error);
|
|
107
|
+
}
|
|
108
|
+
// -- AsyncIterableIterator -------------------------------------------------
|
|
109
|
+
next() {
|
|
110
|
+
return this.sdkMessages.next();
|
|
111
|
+
}
|
|
112
|
+
return(value) {
|
|
113
|
+
if (this.sdkMessages.return) {
|
|
114
|
+
return this.sdkMessages.return(value);
|
|
115
|
+
}
|
|
116
|
+
return Promise.resolve({ done: true, value: void 0 });
|
|
117
|
+
}
|
|
118
|
+
throw(e) {
|
|
119
|
+
if (this.sdkMessages.throw) {
|
|
120
|
+
return this.sdkMessages.throw(e);
|
|
121
|
+
}
|
|
122
|
+
return Promise.reject(e);
|
|
123
|
+
}
|
|
124
|
+
[Symbol.asyncIterator]() {
|
|
125
|
+
return this.sdkMessages;
|
|
126
|
+
}
|
|
127
|
+
// -- Internal: read stdout JSON lines --------------------------------------
|
|
128
|
+
async readMessages(processExitPromise) {
|
|
129
|
+
const rl = createInterface({ input: this.child.stdout });
|
|
130
|
+
try {
|
|
131
|
+
for await (const line of rl) {
|
|
132
|
+
if (!line.trim()) continue;
|
|
133
|
+
try {
|
|
134
|
+
const message = JSON.parse(line);
|
|
135
|
+
if (message.type === "control_response") {
|
|
136
|
+
const controlResponse = message;
|
|
137
|
+
const handler = this.pendingControlResponses.get(
|
|
138
|
+
controlResponse.response.request_id
|
|
139
|
+
);
|
|
140
|
+
if (handler) {
|
|
141
|
+
handler(controlResponse.response);
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (message.type === "control_request") {
|
|
146
|
+
await this.handleControlRequest(
|
|
147
|
+
message
|
|
148
|
+
);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (message.type === "control_cancel_request") {
|
|
152
|
+
this.handleControlCancelRequest(
|
|
153
|
+
message
|
|
154
|
+
);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
this.inputStream.enqueue(message);
|
|
158
|
+
} catch {
|
|
159
|
+
logger2.debug("Non-JSON line from Claude stdout: " + line.slice(0, 200));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
await processExitPromise;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
this.inputStream.error(error);
|
|
165
|
+
} finally {
|
|
166
|
+
this.inputStream.done();
|
|
167
|
+
this.cleanupControllers();
|
|
168
|
+
rl.close();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async *readSdkMessages() {
|
|
172
|
+
for await (const message of this.inputStream) {
|
|
173
|
+
yield message;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// -- Control request handling (tool permissions) ---------------------------
|
|
177
|
+
async handleControlRequest(request) {
|
|
178
|
+
if (!this.childStdin) {
|
|
179
|
+
logger2.debug("Cannot handle control request \u2014 no stdin available");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const controller = new AbortController();
|
|
183
|
+
this.cancelControllers.set(request.request_id, controller);
|
|
184
|
+
try {
|
|
185
|
+
const response = await this.processControlRequest(
|
|
186
|
+
request,
|
|
187
|
+
controller.signal
|
|
188
|
+
);
|
|
189
|
+
const controlResponse = {
|
|
190
|
+
type: "control_response",
|
|
191
|
+
response: {
|
|
192
|
+
subtype: "success",
|
|
193
|
+
request_id: request.request_id,
|
|
194
|
+
response
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
this.childStdin.write(JSON.stringify(controlResponse) + "\n");
|
|
198
|
+
} catch (error) {
|
|
199
|
+
const controlErrorResponse = {
|
|
200
|
+
type: "control_response",
|
|
201
|
+
response: {
|
|
202
|
+
subtype: "error",
|
|
203
|
+
request_id: request.request_id,
|
|
204
|
+
error: error instanceof Error ? error.message : String(error)
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
this.childStdin.write(JSON.stringify(controlErrorResponse) + "\n");
|
|
208
|
+
} finally {
|
|
209
|
+
this.cancelControllers.delete(request.request_id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
handleControlCancelRequest(request) {
|
|
213
|
+
const controller = this.cancelControllers.get(request.request_id);
|
|
214
|
+
if (controller) {
|
|
215
|
+
controller.abort();
|
|
216
|
+
this.cancelControllers.delete(request.request_id);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async processControlRequest(request, signal) {
|
|
220
|
+
if (request.request.subtype === "can_use_tool") {
|
|
221
|
+
if (!this.canCallTool) {
|
|
222
|
+
throw new Error("canCallTool callback is not provided.");
|
|
223
|
+
}
|
|
224
|
+
return this.canCallTool(
|
|
225
|
+
request.request.tool_name,
|
|
226
|
+
request.request.input,
|
|
227
|
+
{ signal }
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
throw new Error(
|
|
231
|
+
"Unsupported control request subtype: " + request.request.subtype
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
cleanupControllers() {
|
|
235
|
+
for (const [, controller] of this.cancelControllers) {
|
|
236
|
+
controller.abort();
|
|
237
|
+
}
|
|
238
|
+
this.cancelControllers.clear();
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function query(config) {
|
|
242
|
+
const {
|
|
243
|
+
prompt,
|
|
244
|
+
binary,
|
|
245
|
+
options: {
|
|
246
|
+
allowedTools = [],
|
|
247
|
+
disallowedTools = [],
|
|
248
|
+
cwd,
|
|
249
|
+
model,
|
|
250
|
+
permissionMode = "default",
|
|
251
|
+
canCallTool,
|
|
252
|
+
resume,
|
|
253
|
+
continueConversation,
|
|
254
|
+
appendSystemPrompt,
|
|
255
|
+
maxTurns,
|
|
256
|
+
abort
|
|
257
|
+
} = {}
|
|
258
|
+
} = config;
|
|
259
|
+
const args = ["--output-format", "stream-json", "--verbose"];
|
|
260
|
+
if (appendSystemPrompt) args.push("--append-system-prompt", appendSystemPrompt);
|
|
261
|
+
if (maxTurns) args.push("--max-turns", maxTurns.toString());
|
|
262
|
+
if (model) args.push("--model", model);
|
|
263
|
+
if (continueConversation) args.push("--continue");
|
|
264
|
+
if (resume) args.push("--resume", resume);
|
|
265
|
+
if (allowedTools.length > 0) args.push("--allowedTools", allowedTools.join(","));
|
|
266
|
+
if (disallowedTools.length > 0) args.push("--disallowedTools", disallowedTools.join(","));
|
|
267
|
+
if (permissionMode) args.push("--permission-mode", permissionMode);
|
|
268
|
+
if (canCallTool) {
|
|
269
|
+
args.push("--permission-prompt-tool", "stdio");
|
|
270
|
+
args.push("--input-format", "stream-json");
|
|
271
|
+
}
|
|
272
|
+
if (!canCallTool) {
|
|
273
|
+
args.push("--print", prompt.trim());
|
|
274
|
+
}
|
|
275
|
+
const { CLAUDECODE: _, ...cleanEnv } = process.env;
|
|
276
|
+
logger2.info("Spawning Claude Code process", {
|
|
277
|
+
binary,
|
|
278
|
+
argCount: args.length,
|
|
279
|
+
cwd,
|
|
280
|
+
hasCanCallTool: !!canCallTool
|
|
281
|
+
});
|
|
282
|
+
logger2.debug("Claude Code args", { args });
|
|
283
|
+
const child = spawn(binary, args, {
|
|
284
|
+
cwd,
|
|
285
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
286
|
+
signal: abort,
|
|
287
|
+
env: cleanEnv,
|
|
288
|
+
windowsHide: true
|
|
289
|
+
});
|
|
290
|
+
let childStdin = null;
|
|
291
|
+
if (!canCallTool) {
|
|
292
|
+
child.stdin.end();
|
|
293
|
+
} else {
|
|
294
|
+
childStdin = child.stdin;
|
|
295
|
+
const userMessage = {
|
|
296
|
+
type: "user",
|
|
297
|
+
message: {
|
|
298
|
+
role: "user",
|
|
299
|
+
content: prompt.trim()
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
childStdin.write(JSON.stringify(userMessage) + "\n");
|
|
303
|
+
}
|
|
304
|
+
child.stderr.on("data", (data) => {
|
|
305
|
+
logger2.debug("Claude Code stderr: " + data.toString().trimEnd());
|
|
306
|
+
});
|
|
307
|
+
const cleanup = () => {
|
|
308
|
+
if (!child.killed) {
|
|
309
|
+
child.kill("SIGTERM");
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
abort?.addEventListener("abort", cleanup);
|
|
313
|
+
process.on("exit", cleanup);
|
|
314
|
+
const processExitPromise = new Promise((resolve, reject) => {
|
|
315
|
+
child.on("close", (code) => {
|
|
316
|
+
if (abort?.aborted) {
|
|
317
|
+
reject(new AbortError("Claude Code process aborted"));
|
|
318
|
+
} else if (code !== 0) {
|
|
319
|
+
reject(new Error(`Claude Code process exited with code ${code}`));
|
|
320
|
+
} else {
|
|
321
|
+
resolve();
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
const q = new Query(child, childStdin, processExitPromise, canCallTool);
|
|
326
|
+
child.on("error", (error) => {
|
|
327
|
+
if (abort?.aborted) {
|
|
328
|
+
q.setError(new AbortError("Claude Code process aborted"));
|
|
329
|
+
} else {
|
|
330
|
+
q.setError(
|
|
331
|
+
new Error(`Failed to spawn Claude Code process: ${error.message}`)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
processExitPromise.catch(() => {
|
|
336
|
+
}).finally(() => {
|
|
337
|
+
cleanup();
|
|
338
|
+
abort?.removeEventListener("abort", cleanup);
|
|
339
|
+
});
|
|
340
|
+
return q;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/ai-runner/SdkRunner.ts
|
|
344
|
+
var logger3 = logger.child("SdkRunner");
|
|
345
|
+
var SIGKILL_GRACE_MS = 1e4;
|
|
346
|
+
var IDLE_CHECK_INTERVAL_MS = 5e3;
|
|
347
|
+
var SdkRunner = class {
|
|
348
|
+
constructor(binary, nvmNodeVersion, model) {
|
|
349
|
+
this.binary = binary;
|
|
350
|
+
this.nvmNodeVersion = nvmNodeVersion;
|
|
351
|
+
this.model = model;
|
|
352
|
+
}
|
|
353
|
+
activeProcesses = /* @__PURE__ */ new Map();
|
|
354
|
+
// -- AIRunner interface ----------------------------------------------------
|
|
355
|
+
async run(options) {
|
|
356
|
+
if (isShuttingDown()) {
|
|
357
|
+
logger3.warn("SdkRunner skipped \u2014 service is shutting down");
|
|
358
|
+
return { success: false, output: "Service shutting down", exitCode: null };
|
|
359
|
+
}
|
|
360
|
+
const {
|
|
361
|
+
prompt,
|
|
362
|
+
workDir,
|
|
363
|
+
timeoutMs,
|
|
364
|
+
idleTimeoutMs,
|
|
365
|
+
onStreamEvent,
|
|
366
|
+
mode,
|
|
367
|
+
artifactPaths
|
|
368
|
+
} = options;
|
|
369
|
+
logger3.info("SdkRunner.run()", {
|
|
370
|
+
workDir,
|
|
371
|
+
timeoutMs,
|
|
372
|
+
mode,
|
|
373
|
+
phaseName: options.phaseName
|
|
374
|
+
});
|
|
375
|
+
const abortController = new AbortController();
|
|
376
|
+
const isPlanMode = mode === "plan";
|
|
377
|
+
let planContent;
|
|
378
|
+
const queryOptions = {
|
|
379
|
+
cwd: workDir,
|
|
380
|
+
model: this.model,
|
|
381
|
+
abort: abortController.signal
|
|
382
|
+
};
|
|
383
|
+
if (isPlanMode) {
|
|
384
|
+
queryOptions.permissionMode = "plan";
|
|
385
|
+
queryOptions.allowedTools = ["Read", "Grep", "Glob", "WebSearch"];
|
|
386
|
+
queryOptions.canCallTool = async (toolName, input, _opts) => {
|
|
387
|
+
if (toolName === "ExitPlanMode" || toolName === "exit_plan_mode") {
|
|
388
|
+
const planInput = input;
|
|
389
|
+
planContent = typeof planInput?.plan === "string" ? planInput.plan : void 0;
|
|
390
|
+
logger3.info("ExitPlanMode intercepted", {
|
|
391
|
+
hasPlan: !!planContent,
|
|
392
|
+
planLength: planContent?.length
|
|
393
|
+
});
|
|
394
|
+
if (planContent && artifactPaths && artifactPaths.length > 0) {
|
|
395
|
+
for (const targetPath of artifactPaths) {
|
|
396
|
+
const targetDir = path.dirname(targetPath);
|
|
397
|
+
if (!fs.existsSync(targetDir)) {
|
|
398
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
399
|
+
}
|
|
400
|
+
fs.writeFileSync(targetPath, planContent, "utf-8");
|
|
401
|
+
logger3.info("Plan written to artifact path", { target: targetPath });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return { behavior: "deny", message: "Plan captured by SdkRunner" };
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
behavior: "allow",
|
|
408
|
+
updatedInput: input && typeof input === "object" ? input : {}
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
} else {
|
|
412
|
+
queryOptions.permissionMode = "bypassPermissions";
|
|
413
|
+
}
|
|
414
|
+
if (options.continueSession && options.sessionId) {
|
|
415
|
+
queryOptions.resume = options.sessionId;
|
|
416
|
+
}
|
|
417
|
+
const q = query({
|
|
418
|
+
prompt,
|
|
419
|
+
binary: this.binary,
|
|
420
|
+
options: queryOptions
|
|
421
|
+
});
|
|
422
|
+
const entry = {
|
|
423
|
+
child: q.child,
|
|
424
|
+
workDir,
|
|
425
|
+
abort: abortController
|
|
426
|
+
};
|
|
427
|
+
this.activeProcesses.set(q.child, entry);
|
|
428
|
+
const outputParts = [];
|
|
429
|
+
const stderrParts = [];
|
|
430
|
+
let sessionId;
|
|
431
|
+
let resultMessage;
|
|
432
|
+
let caughtError;
|
|
433
|
+
let lastActivityTime = Date.now();
|
|
434
|
+
let timedOut = false;
|
|
435
|
+
let timeoutType;
|
|
436
|
+
let wasActiveAtTimeout = false;
|
|
437
|
+
q.child.stderr.on("data", (data) => {
|
|
438
|
+
stderrParts.push(data.toString());
|
|
439
|
+
});
|
|
440
|
+
const graceWindowMs = options.timeoutGraceMs ?? 6e4;
|
|
441
|
+
const extensionMs = options.timeoutExtensionMs ?? 6e5;
|
|
442
|
+
const maxExtensions = options.timeoutMaxExtensions ?? 3;
|
|
443
|
+
let extensions = 0;
|
|
444
|
+
const scheduleWallTimer = (delayMs) => {
|
|
445
|
+
return setTimeout(() => {
|
|
446
|
+
const recentMs = Date.now() - lastActivityTime;
|
|
447
|
+
const isActive = recentMs < graceWindowMs;
|
|
448
|
+
if (isActive && extensions < maxExtensions) {
|
|
449
|
+
extensions++;
|
|
450
|
+
logger3.info("Wall-clock timeout extended (agent still active)", {
|
|
451
|
+
extensions,
|
|
452
|
+
maxExtensions,
|
|
453
|
+
lastOutputAgoMs: recentMs
|
|
454
|
+
});
|
|
455
|
+
wallTimer = scheduleWallTimer(extensionMs);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
timedOut = true;
|
|
459
|
+
timeoutType = "wall-clock";
|
|
460
|
+
wasActiveAtTimeout = isActive;
|
|
461
|
+
abortController.abort();
|
|
462
|
+
}, delayMs);
|
|
463
|
+
};
|
|
464
|
+
let wallTimer = scheduleWallTimer(timeoutMs);
|
|
465
|
+
const effectiveIdleMs = idleTimeoutMs ?? 6e5;
|
|
466
|
+
const idleCheck = setInterval(() => {
|
|
467
|
+
const idleMs = Date.now() - lastActivityTime;
|
|
468
|
+
if (idleMs >= effectiveIdleMs) {
|
|
469
|
+
timedOut = true;
|
|
470
|
+
timeoutType = "idle";
|
|
471
|
+
wasActiveAtTimeout = false;
|
|
472
|
+
abortController.abort();
|
|
473
|
+
}
|
|
474
|
+
}, IDLE_CHECK_INTERVAL_MS);
|
|
475
|
+
try {
|
|
476
|
+
for await (const message of q) {
|
|
477
|
+
lastActivityTime = Date.now();
|
|
478
|
+
this.processMessage(
|
|
479
|
+
message,
|
|
480
|
+
outputParts,
|
|
481
|
+
onStreamEvent,
|
|
482
|
+
(sid) => {
|
|
483
|
+
sessionId = sid;
|
|
484
|
+
},
|
|
485
|
+
(result) => {
|
|
486
|
+
resultMessage = result;
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
} catch (error) {
|
|
491
|
+
caughtError = error;
|
|
492
|
+
if (error instanceof AbortError) {
|
|
493
|
+
logger3.info("Claude Code process aborted", { timedOut, timeoutType });
|
|
494
|
+
} else {
|
|
495
|
+
logger3.error("Unexpected error during SDK message iteration", {
|
|
496
|
+
error: error.message
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
} finally {
|
|
500
|
+
clearTimeout(wallTimer);
|
|
501
|
+
clearInterval(idleCheck);
|
|
502
|
+
this.activeProcesses.delete(q.child);
|
|
503
|
+
}
|
|
504
|
+
const output = outputParts.join("");
|
|
505
|
+
const success = !timedOut && resultMessage != null && !resultMessage.is_error;
|
|
506
|
+
let errorMessage;
|
|
507
|
+
if (!success) {
|
|
508
|
+
const parts = [];
|
|
509
|
+
if (timedOut) {
|
|
510
|
+
parts.push(
|
|
511
|
+
timeoutType === "idle" ? "AI \u957F\u65F6\u95F4\u65E0\u54CD\u5E94\uFF0C\u5DF2\u8D85\u65F6\u7EC8\u6B62" : "\u6267\u884C\u8D85\u65F6"
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
if (resultMessage?.is_error && resultMessage.result) {
|
|
515
|
+
parts.push(resultMessage.result);
|
|
516
|
+
}
|
|
517
|
+
if (!resultMessage && caughtError && !(caughtError instanceof AbortError)) {
|
|
518
|
+
parts.push(caughtError.message);
|
|
519
|
+
}
|
|
520
|
+
const stderr = stderrParts.join("").trim();
|
|
521
|
+
if (stderr && parts.length === 0) {
|
|
522
|
+
parts.push(stderr.slice(0, 500));
|
|
523
|
+
}
|
|
524
|
+
errorMessage = parts.join(" | ") || "Claude Code \u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF08\u65E0\u8F93\u51FA\uFF09";
|
|
525
|
+
}
|
|
526
|
+
logger3.info("SdkRunner completed", {
|
|
527
|
+
workDir,
|
|
528
|
+
success,
|
|
529
|
+
timedOut,
|
|
530
|
+
timeoutType,
|
|
531
|
+
outputLength: output.length,
|
|
532
|
+
sessionId,
|
|
533
|
+
resultSubtype: resultMessage?.subtype
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
success,
|
|
537
|
+
output: resultMessage?.result ?? output,
|
|
538
|
+
errorMessage,
|
|
539
|
+
sessionId: sessionId ?? resultMessage?.session_id,
|
|
540
|
+
exitCode: success ? 0 : timedOut ? null : 1,
|
|
541
|
+
timeoutType,
|
|
542
|
+
wasActiveAtTimeout
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
killAll() {
|
|
546
|
+
for (const [child, entry] of this.activeProcesses) {
|
|
547
|
+
entry.abort.abort();
|
|
548
|
+
this.forceKill(child);
|
|
549
|
+
}
|
|
550
|
+
logger3.info("SdkRunner: all active processes killed", {
|
|
551
|
+
count: this.activeProcesses.size
|
|
552
|
+
});
|
|
553
|
+
this.activeProcesses.clear();
|
|
554
|
+
}
|
|
555
|
+
killByWorkDir(targetWorkDir) {
|
|
556
|
+
let killed = 0;
|
|
557
|
+
for (const [child, entry] of this.activeProcesses) {
|
|
558
|
+
if (entry.workDir === targetWorkDir) {
|
|
559
|
+
entry.abort.abort();
|
|
560
|
+
this.forceKill(child);
|
|
561
|
+
this.activeProcesses.delete(child);
|
|
562
|
+
killed++;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (killed > 0) {
|
|
566
|
+
logger3.info("SdkRunner: killed processes by workDir", {
|
|
567
|
+
workDir: targetWorkDir,
|
|
568
|
+
killed
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return killed;
|
|
572
|
+
}
|
|
573
|
+
// -- Internal helpers ------------------------------------------------------
|
|
574
|
+
processMessage(message, outputParts, onStreamEvent, onSessionId, onResult) {
|
|
575
|
+
switch (message.type) {
|
|
576
|
+
case "system": {
|
|
577
|
+
const sys = message;
|
|
578
|
+
if (sys.session_id) onSessionId(sys.session_id);
|
|
579
|
+
if (onStreamEvent) {
|
|
580
|
+
onStreamEvent({
|
|
581
|
+
type: "system",
|
|
582
|
+
content: message,
|
|
583
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
case "assistant": {
|
|
589
|
+
const assistant = message;
|
|
590
|
+
if (assistant.message?.content) {
|
|
591
|
+
for (const block of assistant.message.content) {
|
|
592
|
+
if (block.type === "text" && block.text) {
|
|
593
|
+
outputParts.push(block.text);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (onStreamEvent) {
|
|
598
|
+
onStreamEvent({
|
|
599
|
+
type: "assistant",
|
|
600
|
+
content: message,
|
|
601
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
case "result": {
|
|
607
|
+
const result = message;
|
|
608
|
+
onResult(result);
|
|
609
|
+
if (result.session_id) onSessionId(result.session_id);
|
|
610
|
+
if (onStreamEvent) {
|
|
611
|
+
onStreamEvent({
|
|
612
|
+
type: "result",
|
|
613
|
+
content: message,
|
|
614
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case "user": {
|
|
620
|
+
if (onStreamEvent) {
|
|
621
|
+
onStreamEvent({
|
|
622
|
+
type: "user",
|
|
623
|
+
content: message,
|
|
624
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
case "log": {
|
|
630
|
+
const log = message;
|
|
631
|
+
if (log.log) {
|
|
632
|
+
logger3.debug(`Claude log [${log.log.level}]: ${log.log.message}`);
|
|
633
|
+
}
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
default: {
|
|
637
|
+
if (onStreamEvent) {
|
|
638
|
+
onStreamEvent({
|
|
639
|
+
type: message.type || "raw",
|
|
640
|
+
content: message,
|
|
641
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
forceKill(child) {
|
|
649
|
+
try {
|
|
650
|
+
if (child.exitCode === null) {
|
|
651
|
+
child.kill("SIGTERM");
|
|
652
|
+
setTimeout(() => {
|
|
653
|
+
if (child.exitCode === null) {
|
|
654
|
+
child.kill("SIGKILL");
|
|
655
|
+
}
|
|
656
|
+
}, SIGKILL_GRACE_MS);
|
|
657
|
+
}
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
export {
|
|
664
|
+
SdkRunner
|
|
665
|
+
};
|
|
666
|
+
//# sourceMappingURL=chunk-R32Q3RGK.js.map
|