@anthropic-ai/claude-code 1.0.84 → 1.0.86
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/cli.js +1262 -1210
- package/package.json +1 -1
- package/sdk-tools.d.ts +4 -1
- package/sdk.d.ts +105 -11
- package/sdk.mjs +595 -181
- package/vendor/claude-code.vsix +0 -0
package/sdk.mjs
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// (c) Anthropic PBC. All rights reserved. Use is subject to Anthropic's Commercial Terms of Service (https://www.anthropic.com/legal/commercial-terms).
|
|
4
4
|
|
|
5
|
-
// Version: 1.0.
|
|
5
|
+
// Version: 1.0.86
|
|
6
6
|
|
|
7
7
|
// Want to see the unminified source? We're hiring!
|
|
8
8
|
// https://job-boards.greenhouse.io/anthropic/jobs/4816199008
|
|
9
9
|
|
|
10
10
|
// src/entrypoints/sdk.ts
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { fileURLToPath } from "url";
|
|
14
|
-
import { createInterface } from "readline";
|
|
15
|
-
import { existsSync } from "fs";
|
|
11
|
+
import { join as join2 } from "path";
|
|
12
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16
13
|
|
|
17
14
|
// src/utils/stream.ts
|
|
18
15
|
class Stream {
|
|
@@ -97,36 +94,478 @@ function createAbortController(maxListeners = DEFAULT_MAX_LISTENERS) {
|
|
|
97
94
|
return controller;
|
|
98
95
|
}
|
|
99
96
|
|
|
97
|
+
// src/transport/ProcessTransport.ts
|
|
98
|
+
import { spawn } from "child_process";
|
|
99
|
+
import { join } from "path";
|
|
100
|
+
import { fileURLToPath } from "url";
|
|
101
|
+
import { createInterface } from "readline";
|
|
102
|
+
|
|
103
|
+
// src/utils/fsOperations.ts
|
|
104
|
+
import * as fs from "fs";
|
|
105
|
+
import { stat as statPromise } from "fs/promises";
|
|
106
|
+
var NodeFsOperations = {
|
|
107
|
+
accessSync(fsPath, mode) {
|
|
108
|
+
fs.accessSync(fsPath, mode);
|
|
109
|
+
},
|
|
110
|
+
cwd() {
|
|
111
|
+
return process.cwd();
|
|
112
|
+
},
|
|
113
|
+
chmodSync(fsPath, mode) {
|
|
114
|
+
fs.chmodSync(fsPath, mode);
|
|
115
|
+
},
|
|
116
|
+
existsSync(fsPath) {
|
|
117
|
+
return fs.existsSync(fsPath);
|
|
118
|
+
},
|
|
119
|
+
async stat(fsPath) {
|
|
120
|
+
return statPromise(fsPath);
|
|
121
|
+
},
|
|
122
|
+
statSync(fsPath) {
|
|
123
|
+
return fs.statSync(fsPath);
|
|
124
|
+
},
|
|
125
|
+
readFileSync(fsPath, options) {
|
|
126
|
+
return fs.readFileSync(fsPath, { encoding: options.encoding });
|
|
127
|
+
},
|
|
128
|
+
readFileBytesSync(fsPath) {
|
|
129
|
+
return fs.readFileSync(fsPath);
|
|
130
|
+
},
|
|
131
|
+
readSync(fsPath, options) {
|
|
132
|
+
let fd = undefined;
|
|
133
|
+
try {
|
|
134
|
+
fd = fs.openSync(fsPath, "r");
|
|
135
|
+
const buffer = Buffer.alloc(options.length);
|
|
136
|
+
const bytesRead = fs.readSync(fd, buffer, 0, options.length, 0);
|
|
137
|
+
return { buffer, bytesRead };
|
|
138
|
+
} finally {
|
|
139
|
+
if (fd)
|
|
140
|
+
fs.closeSync(fd);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
writeFileSync(fsPath, data, options) {
|
|
144
|
+
if (!options.flush) {
|
|
145
|
+
fs.writeFileSync(fsPath, data, { encoding: options.encoding });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
let fd;
|
|
149
|
+
try {
|
|
150
|
+
fd = fs.openSync(fsPath, "w");
|
|
151
|
+
fs.writeFileSync(fd, data, { encoding: options.encoding });
|
|
152
|
+
fs.fsyncSync(fd);
|
|
153
|
+
} finally {
|
|
154
|
+
if (fd) {
|
|
155
|
+
fs.closeSync(fd);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
appendFileSync(path, data) {
|
|
160
|
+
fs.appendFileSync(path, data);
|
|
161
|
+
},
|
|
162
|
+
copyFileSync(src, dest) {
|
|
163
|
+
fs.copyFileSync(src, dest);
|
|
164
|
+
},
|
|
165
|
+
unlinkSync(path) {
|
|
166
|
+
fs.unlinkSync(path);
|
|
167
|
+
},
|
|
168
|
+
renameSync(oldPath, newPath) {
|
|
169
|
+
fs.renameSync(oldPath, newPath);
|
|
170
|
+
},
|
|
171
|
+
symlinkSync(target, path) {
|
|
172
|
+
fs.symlinkSync(target, path);
|
|
173
|
+
},
|
|
174
|
+
readlinkSync(path) {
|
|
175
|
+
return fs.readlinkSync(path);
|
|
176
|
+
},
|
|
177
|
+
realpathSync(path) {
|
|
178
|
+
return fs.realpathSync(path);
|
|
179
|
+
},
|
|
180
|
+
mkdirSync(dirPath) {
|
|
181
|
+
if (!fs.existsSync(dirPath)) {
|
|
182
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
readdirSync(dirPath) {
|
|
186
|
+
return fs.readdirSync(dirPath, { withFileTypes: true });
|
|
187
|
+
},
|
|
188
|
+
readdirStringSync(dirPath) {
|
|
189
|
+
return fs.readdirSync(dirPath);
|
|
190
|
+
},
|
|
191
|
+
isDirEmptySync(dirPath) {
|
|
192
|
+
const files = this.readdirSync(dirPath);
|
|
193
|
+
return files.length === 0;
|
|
194
|
+
},
|
|
195
|
+
rmdirSync(dirPath) {
|
|
196
|
+
fs.rmdirSync(dirPath);
|
|
197
|
+
},
|
|
198
|
+
rmSync(path, options) {
|
|
199
|
+
fs.rmSync(path, options);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var activeFs = NodeFsOperations;
|
|
203
|
+
function getFsImplementation() {
|
|
204
|
+
return activeFs;
|
|
205
|
+
}
|
|
206
|
+
|
|
100
207
|
// src/entrypoints/sdkTypes.ts
|
|
101
208
|
class AbortError extends Error {
|
|
102
209
|
}
|
|
103
210
|
|
|
211
|
+
// src/transport/ProcessTransport.ts
|
|
212
|
+
class ProcessTransport {
|
|
213
|
+
options;
|
|
214
|
+
child;
|
|
215
|
+
childStdin;
|
|
216
|
+
childStdout;
|
|
217
|
+
ready = false;
|
|
218
|
+
abortController;
|
|
219
|
+
exitError;
|
|
220
|
+
exitListeners = [];
|
|
221
|
+
processExitHandler;
|
|
222
|
+
abortHandler;
|
|
223
|
+
isStreaming;
|
|
224
|
+
constructor(options) {
|
|
225
|
+
this.options = options;
|
|
226
|
+
this.abortController = options.abortController || createAbortController();
|
|
227
|
+
this.isStreaming = typeof options.prompt !== "string";
|
|
228
|
+
this.initialize();
|
|
229
|
+
}
|
|
230
|
+
initialize() {
|
|
231
|
+
try {
|
|
232
|
+
const {
|
|
233
|
+
prompt,
|
|
234
|
+
additionalDirectories = [],
|
|
235
|
+
cwd,
|
|
236
|
+
executable = this.isRunningWithBun() ? "bun" : "node",
|
|
237
|
+
executableArgs = [],
|
|
238
|
+
pathToClaudeCodeExecutable,
|
|
239
|
+
env = { ...process.env },
|
|
240
|
+
stderr,
|
|
241
|
+
customSystemPrompt,
|
|
242
|
+
appendSystemPrompt,
|
|
243
|
+
maxTurns,
|
|
244
|
+
model,
|
|
245
|
+
fallbackModel,
|
|
246
|
+
permissionMode,
|
|
247
|
+
permissionPromptToolName,
|
|
248
|
+
continueConversation,
|
|
249
|
+
resume,
|
|
250
|
+
allowedTools = [],
|
|
251
|
+
disallowedTools = [],
|
|
252
|
+
mcpServers,
|
|
253
|
+
strictMcpConfig,
|
|
254
|
+
canUseTool
|
|
255
|
+
} = this.options;
|
|
256
|
+
const args = ["--output-format", "stream-json", "--verbose"];
|
|
257
|
+
if (customSystemPrompt)
|
|
258
|
+
args.push("--system-prompt", customSystemPrompt);
|
|
259
|
+
if (appendSystemPrompt)
|
|
260
|
+
args.push("--append-system-prompt", appendSystemPrompt);
|
|
261
|
+
if (maxTurns)
|
|
262
|
+
args.push("--max-turns", maxTurns.toString());
|
|
263
|
+
if (model)
|
|
264
|
+
args.push("--model", model);
|
|
265
|
+
if (env.DEBUG)
|
|
266
|
+
args.push("--debug-to-stderr");
|
|
267
|
+
if (canUseTool) {
|
|
268
|
+
if (typeof prompt === "string") {
|
|
269
|
+
throw new Error("canUseTool callback requires --input-format stream-json. Please set prompt as an AsyncIterable.");
|
|
270
|
+
}
|
|
271
|
+
if (permissionPromptToolName) {
|
|
272
|
+
throw new Error("canUseTool callback cannot be used with permissionPromptToolName. Please use one or the other.");
|
|
273
|
+
}
|
|
274
|
+
args.push("--permission-prompt-tool", "stdio");
|
|
275
|
+
} else if (permissionPromptToolName) {
|
|
276
|
+
args.push("--permission-prompt-tool", permissionPromptToolName);
|
|
277
|
+
}
|
|
278
|
+
if (continueConversation)
|
|
279
|
+
args.push("--continue");
|
|
280
|
+
if (resume)
|
|
281
|
+
args.push("--resume", resume);
|
|
282
|
+
if (allowedTools.length > 0) {
|
|
283
|
+
args.push("--allowedTools", allowedTools.join(","));
|
|
284
|
+
}
|
|
285
|
+
if (disallowedTools.length > 0) {
|
|
286
|
+
args.push("--disallowedTools", disallowedTools.join(","));
|
|
287
|
+
}
|
|
288
|
+
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
289
|
+
args.push("--mcp-config", JSON.stringify({ mcpServers }));
|
|
290
|
+
}
|
|
291
|
+
if (strictMcpConfig) {
|
|
292
|
+
args.push("--strict-mcp-config");
|
|
293
|
+
}
|
|
294
|
+
if (permissionMode && permissionMode !== "default") {
|
|
295
|
+
args.push("--permission-mode", permissionMode);
|
|
296
|
+
}
|
|
297
|
+
if (fallbackModel) {
|
|
298
|
+
if (model && fallbackModel === model) {
|
|
299
|
+
throw new Error("Fallback model cannot be the same as the main model. Please specify a different model for fallbackModel option.");
|
|
300
|
+
}
|
|
301
|
+
args.push("--fallback-model", fallbackModel);
|
|
302
|
+
}
|
|
303
|
+
if (typeof prompt === "string") {
|
|
304
|
+
args.push("--print");
|
|
305
|
+
args.push("--", prompt.trim());
|
|
306
|
+
} else {
|
|
307
|
+
args.push("--input-format", "stream-json");
|
|
308
|
+
}
|
|
309
|
+
for (const dir of additionalDirectories) {
|
|
310
|
+
args.push("--add-dir", dir);
|
|
311
|
+
}
|
|
312
|
+
if (!env.CLAUDE_CODE_ENTRYPOINT) {
|
|
313
|
+
env.CLAUDE_CODE_ENTRYPOINT = "sdk-ts";
|
|
314
|
+
}
|
|
315
|
+
const claudeCodePath = pathToClaudeCodeExecutable || this.getDefaultExecutablePath();
|
|
316
|
+
const fs2 = getFsImplementation();
|
|
317
|
+
if (!fs2.existsSync(claudeCodePath)) {
|
|
318
|
+
throw new Error(`Claude Code executable not found at ${claudeCodePath}. Is options.pathToClaudeCodeExecutable set?`);
|
|
319
|
+
}
|
|
320
|
+
this.logDebug(`Spawning Claude Code process: ${executable} ${[...executableArgs, claudeCodePath, ...args].join(" ")}`);
|
|
321
|
+
const stderrMode = env.DEBUG || stderr ? "pipe" : "ignore";
|
|
322
|
+
this.child = spawn(executable, [...executableArgs, claudeCodePath, ...args], {
|
|
323
|
+
cwd,
|
|
324
|
+
stdio: ["pipe", "pipe", stderrMode],
|
|
325
|
+
signal: this.abortController.signal,
|
|
326
|
+
env
|
|
327
|
+
});
|
|
328
|
+
this.childStdin = this.child.stdin;
|
|
329
|
+
this.childStdout = this.child.stdout;
|
|
330
|
+
if (typeof prompt === "string") {
|
|
331
|
+
this.childStdin.end();
|
|
332
|
+
this.childStdin = undefined;
|
|
333
|
+
}
|
|
334
|
+
if (env.DEBUG || stderr) {
|
|
335
|
+
this.child.stderr.on("data", (data) => {
|
|
336
|
+
this.logDebug(`Claude Code stderr: ${data.toString()}`);
|
|
337
|
+
if (stderr) {
|
|
338
|
+
stderr(data.toString());
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
const cleanup = () => {
|
|
343
|
+
if (this.child && !this.child.killed) {
|
|
344
|
+
this.child.kill("SIGTERM");
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
this.processExitHandler = cleanup;
|
|
348
|
+
this.abortHandler = cleanup;
|
|
349
|
+
process.on("exit", this.processExitHandler);
|
|
350
|
+
this.abortController.signal.addEventListener("abort", this.abortHandler);
|
|
351
|
+
this.child.on("error", (error) => {
|
|
352
|
+
this.ready = false;
|
|
353
|
+
if (this.abortController.signal.aborted) {
|
|
354
|
+
this.exitError = new AbortError("Claude Code process aborted by user");
|
|
355
|
+
} else {
|
|
356
|
+
this.exitError = new Error(`Failed to spawn Claude Code process: ${error.message}`);
|
|
357
|
+
this.logDebug(this.exitError.message);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
this.child.on("close", (code, signal) => {
|
|
361
|
+
this.ready = false;
|
|
362
|
+
if (this.abortController.signal.aborted) {
|
|
363
|
+
this.exitError = new AbortError("Claude Code process aborted by user");
|
|
364
|
+
} else {
|
|
365
|
+
const error = this.getProcessExitError(code, signal);
|
|
366
|
+
if (error) {
|
|
367
|
+
this.exitError = error;
|
|
368
|
+
this.logDebug(error.message);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
this.ready = true;
|
|
373
|
+
} catch (error) {
|
|
374
|
+
this.ready = false;
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
getProcessExitError(code, signal) {
|
|
379
|
+
if (code !== 0 && code !== null) {
|
|
380
|
+
return new Error(`Claude Code process exited with code ${code}`);
|
|
381
|
+
} else if (signal) {
|
|
382
|
+
return new Error(`Claude Code process terminated by signal ${signal}`);
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
getDefaultExecutablePath() {
|
|
387
|
+
const filename = fileURLToPath(import.meta.url);
|
|
388
|
+
const dirname = join(filename, "..", "..");
|
|
389
|
+
return join(dirname, "entrypoints", "cli.js");
|
|
390
|
+
}
|
|
391
|
+
isRunningWithBun() {
|
|
392
|
+
return process.versions.bun !== undefined || process.env.BUN_INSTALL !== undefined;
|
|
393
|
+
}
|
|
394
|
+
logDebug(message) {
|
|
395
|
+
if (process.env.DEBUG) {
|
|
396
|
+
process.stderr.write(`${message}
|
|
397
|
+
`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
write(data) {
|
|
401
|
+
if (this.abortController.signal.aborted) {
|
|
402
|
+
throw new AbortError("Operation aborted");
|
|
403
|
+
}
|
|
404
|
+
if (!this.ready || !this.childStdin) {
|
|
405
|
+
throw new Error("ProcessTransport is not ready for writing");
|
|
406
|
+
}
|
|
407
|
+
if (this.child?.killed || this.child?.exitCode !== null) {
|
|
408
|
+
throw new Error("Cannot write to terminated process");
|
|
409
|
+
}
|
|
410
|
+
if (this.exitError) {
|
|
411
|
+
throw new Error(`Cannot write to process that exited with error: ${this.exitError.message}`);
|
|
412
|
+
}
|
|
413
|
+
if (process.env.DEBUG_SDK) {
|
|
414
|
+
process.stderr.write(`[ProcessTransport] Writing to stdin: ${data.substring(0, 100)}
|
|
415
|
+
`);
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const written = this.childStdin.write(data);
|
|
419
|
+
if (!written && process.env.DEBUG_SDK) {
|
|
420
|
+
console.warn("[ProcessTransport] Write buffer full, data queued");
|
|
421
|
+
}
|
|
422
|
+
} catch (error) {
|
|
423
|
+
this.ready = false;
|
|
424
|
+
throw new Error(`Failed to write to process stdin: ${error.message}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
close() {
|
|
428
|
+
if (this.childStdin) {
|
|
429
|
+
this.childStdin.end();
|
|
430
|
+
this.childStdin = undefined;
|
|
431
|
+
}
|
|
432
|
+
if (this.processExitHandler) {
|
|
433
|
+
process.off("exit", this.processExitHandler);
|
|
434
|
+
this.processExitHandler = undefined;
|
|
435
|
+
}
|
|
436
|
+
if (this.abortHandler) {
|
|
437
|
+
this.abortController.signal.removeEventListener("abort", this.abortHandler);
|
|
438
|
+
this.abortHandler = undefined;
|
|
439
|
+
}
|
|
440
|
+
for (const { handler } of this.exitListeners) {
|
|
441
|
+
this.child?.off("exit", handler);
|
|
442
|
+
}
|
|
443
|
+
this.exitListeners = [];
|
|
444
|
+
if (this.child && !this.child.killed) {
|
|
445
|
+
this.child.kill("SIGTERM");
|
|
446
|
+
setTimeout(() => {
|
|
447
|
+
if (this.child && !this.child.killed) {
|
|
448
|
+
this.child.kill("SIGKILL");
|
|
449
|
+
}
|
|
450
|
+
}, 5000);
|
|
451
|
+
}
|
|
452
|
+
this.ready = false;
|
|
453
|
+
}
|
|
454
|
+
isReady() {
|
|
455
|
+
return this.ready;
|
|
456
|
+
}
|
|
457
|
+
async* readMessages() {
|
|
458
|
+
if (!this.childStdout) {
|
|
459
|
+
throw new Error("ProcessTransport output stream not available");
|
|
460
|
+
}
|
|
461
|
+
const rl = createInterface({ input: this.childStdout });
|
|
462
|
+
try {
|
|
463
|
+
for await (const line of rl) {
|
|
464
|
+
if (line.trim()) {
|
|
465
|
+
const message = JSON.parse(line);
|
|
466
|
+
yield message;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
await this.waitForExit();
|
|
470
|
+
} catch (error) {
|
|
471
|
+
throw error;
|
|
472
|
+
} finally {
|
|
473
|
+
rl.close();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
endInput() {
|
|
477
|
+
if (this.childStdin) {
|
|
478
|
+
this.childStdin.end();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
getInputStream() {
|
|
482
|
+
return this.childStdin;
|
|
483
|
+
}
|
|
484
|
+
onExit(callback) {
|
|
485
|
+
if (!this.child)
|
|
486
|
+
return () => {};
|
|
487
|
+
const handler = (code, signal) => {
|
|
488
|
+
const error = this.getProcessExitError(code, signal);
|
|
489
|
+
callback(error);
|
|
490
|
+
};
|
|
491
|
+
this.child.on("exit", handler);
|
|
492
|
+
this.exitListeners.push({ callback, handler });
|
|
493
|
+
return () => {
|
|
494
|
+
if (this.child) {
|
|
495
|
+
this.child.off("exit", handler);
|
|
496
|
+
}
|
|
497
|
+
const index = this.exitListeners.findIndex((l) => l.handler === handler);
|
|
498
|
+
if (index !== -1) {
|
|
499
|
+
this.exitListeners.splice(index, 1);
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
async waitForExit() {
|
|
504
|
+
if (!this.child) {
|
|
505
|
+
if (this.exitError) {
|
|
506
|
+
throw this.exitError;
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (this.child.exitCode !== null || this.child.killed) {
|
|
511
|
+
if (this.exitError) {
|
|
512
|
+
throw this.exitError;
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
return new Promise((resolve, reject) => {
|
|
517
|
+
const exitHandler = (code, signal) => {
|
|
518
|
+
if (this.abortController.signal.aborted) {
|
|
519
|
+
reject(new AbortError("Operation aborted"));
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const error = this.getProcessExitError(code, signal);
|
|
523
|
+
if (error) {
|
|
524
|
+
reject(error);
|
|
525
|
+
} else {
|
|
526
|
+
resolve();
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
this.child.once("exit", exitHandler);
|
|
530
|
+
const errorHandler = (error) => {
|
|
531
|
+
this.child.off("exit", exitHandler);
|
|
532
|
+
reject(error);
|
|
533
|
+
};
|
|
534
|
+
this.child.once("error", errorHandler);
|
|
535
|
+
this.child.once("exit", () => {
|
|
536
|
+
this.child.off("error", errorHandler);
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
104
542
|
// src/entrypoints/sdk.ts
|
|
105
543
|
function query({
|
|
106
544
|
prompt,
|
|
107
545
|
options: {
|
|
108
546
|
abortController = createAbortController(),
|
|
547
|
+
additionalDirectories = [],
|
|
109
548
|
allowedTools = [],
|
|
110
549
|
appendSystemPrompt,
|
|
550
|
+
canUseTool,
|
|
551
|
+
continue: continueConversation,
|
|
111
552
|
customSystemPrompt,
|
|
112
553
|
cwd,
|
|
113
554
|
disallowedTools = [],
|
|
555
|
+
env,
|
|
114
556
|
executable = isRunningWithBun() ? "bun" : "node",
|
|
115
557
|
executableArgs = [],
|
|
558
|
+
fallbackModel,
|
|
559
|
+
hooks,
|
|
116
560
|
maxTurns,
|
|
117
561
|
mcpServers,
|
|
562
|
+
model,
|
|
118
563
|
pathToClaudeCodeExecutable,
|
|
119
564
|
permissionMode = "default",
|
|
120
565
|
permissionPromptToolName,
|
|
121
|
-
canUseTool,
|
|
122
|
-
continue: continueConversation,
|
|
123
566
|
resume,
|
|
124
|
-
model,
|
|
125
|
-
fallbackModel,
|
|
126
|
-
strictMcpConfig,
|
|
127
567
|
stderr,
|
|
128
|
-
|
|
129
|
-
additionalDirectories = []
|
|
568
|
+
strictMcpConfig
|
|
130
569
|
} = {}
|
|
131
570
|
}) {
|
|
132
571
|
if (!env) {
|
|
@@ -136,151 +575,87 @@ function query({
|
|
|
136
575
|
env.CLAUDE_CODE_ENTRYPOINT = "sdk-ts";
|
|
137
576
|
}
|
|
138
577
|
if (pathToClaudeCodeExecutable === undefined) {
|
|
139
|
-
const filename =
|
|
140
|
-
const dirname =
|
|
141
|
-
pathToClaudeCodeExecutable =
|
|
142
|
-
}
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (maxTurns)
|
|
149
|
-
args.push("--max-turns", maxTurns.toString());
|
|
150
|
-
if (model)
|
|
151
|
-
args.push("--model", model);
|
|
152
|
-
if (canUseTool) {
|
|
153
|
-
if (typeof prompt === "string") {
|
|
154
|
-
throw new Error("canUseTool callback requires --input-format stream-json. Please set prompt as an AsyncIterable.");
|
|
155
|
-
}
|
|
156
|
-
if (permissionPromptToolName) {
|
|
157
|
-
throw new Error("canUseTool callback cannot be used with permissionPromptToolName. Please use one or the other.");
|
|
158
|
-
}
|
|
159
|
-
permissionPromptToolName = "stdio";
|
|
160
|
-
}
|
|
161
|
-
if (permissionPromptToolName) {
|
|
162
|
-
args.push("--permission-prompt-tool", permissionPromptToolName);
|
|
163
|
-
}
|
|
164
|
-
if (continueConversation)
|
|
165
|
-
args.push("--continue");
|
|
166
|
-
if (resume)
|
|
167
|
-
args.push("--resume", resume);
|
|
168
|
-
if (allowedTools.length > 0) {
|
|
169
|
-
args.push("--allowedTools", allowedTools.join(","));
|
|
170
|
-
}
|
|
171
|
-
if (disallowedTools.length > 0) {
|
|
172
|
-
args.push("--disallowedTools", disallowedTools.join(","));
|
|
173
|
-
}
|
|
174
|
-
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
175
|
-
args.push("--mcp-config", JSON.stringify({ mcpServers }));
|
|
176
|
-
}
|
|
177
|
-
if (strictMcpConfig) {
|
|
178
|
-
args.push("--strict-mcp-config");
|
|
179
|
-
}
|
|
180
|
-
if (permissionMode !== "default") {
|
|
181
|
-
args.push("--permission-mode", permissionMode);
|
|
182
|
-
}
|
|
183
|
-
if (fallbackModel) {
|
|
184
|
-
if (model && fallbackModel === model) {
|
|
185
|
-
throw new Error("Fallback model cannot be the same as the main model. Please specify a different model for fallbackModel option.");
|
|
186
|
-
}
|
|
187
|
-
args.push("--fallback-model", fallbackModel);
|
|
188
|
-
}
|
|
189
|
-
if (typeof prompt === "string") {
|
|
190
|
-
args.push("--print");
|
|
191
|
-
args.push("--", prompt.trim());
|
|
192
|
-
} else {
|
|
193
|
-
args.push("--input-format", "stream-json");
|
|
194
|
-
}
|
|
195
|
-
for (const dir of additionalDirectories) {
|
|
196
|
-
args.push("--add-dir", dir);
|
|
197
|
-
}
|
|
198
|
-
if (!existsSync(pathToClaudeCodeExecutable)) {
|
|
199
|
-
throw new ReferenceError(`Claude Code executable not found at ${pathToClaudeCodeExecutable}. Is options.pathToClaudeCodeExecutable set?`);
|
|
200
|
-
}
|
|
201
|
-
logDebug(`Spawning Claude Code process: ${executable} ${[...executableArgs, pathToClaudeCodeExecutable, ...args].join(" ")}`);
|
|
202
|
-
const stderrMode = env.DEBUG || stderr ? "pipe" : "ignore";
|
|
203
|
-
const child = spawn(executable, [...executableArgs, pathToClaudeCodeExecutable, ...args], {
|
|
578
|
+
const filename = fileURLToPath2(import.meta.url);
|
|
579
|
+
const dirname = join2(filename, "..");
|
|
580
|
+
pathToClaudeCodeExecutable = join2(dirname, "cli.js");
|
|
581
|
+
}
|
|
582
|
+
const isStreamingMode = typeof prompt !== "string";
|
|
583
|
+
const transport = new ProcessTransport({
|
|
584
|
+
prompt,
|
|
585
|
+
abortController,
|
|
586
|
+
additionalDirectories,
|
|
204
587
|
cwd,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
588
|
+
executable,
|
|
589
|
+
executableArgs,
|
|
590
|
+
pathToClaudeCodeExecutable,
|
|
591
|
+
env,
|
|
592
|
+
stderr,
|
|
593
|
+
customSystemPrompt,
|
|
594
|
+
appendSystemPrompt,
|
|
595
|
+
maxTurns,
|
|
596
|
+
model,
|
|
597
|
+
fallbackModel,
|
|
598
|
+
permissionMode,
|
|
599
|
+
permissionPromptToolName,
|
|
600
|
+
continueConversation,
|
|
601
|
+
resume,
|
|
602
|
+
allowedTools,
|
|
603
|
+
disallowedTools,
|
|
604
|
+
mcpServers,
|
|
605
|
+
strictMcpConfig,
|
|
606
|
+
canUseTool: !!canUseTool,
|
|
607
|
+
hooks: !!hooks
|
|
208
608
|
});
|
|
209
|
-
|
|
210
|
-
if (typeof prompt
|
|
211
|
-
|
|
212
|
-
} else {
|
|
213
|
-
streamToStdin(prompt, child.stdin, abortController);
|
|
214
|
-
childStdin = child.stdin;
|
|
215
|
-
}
|
|
216
|
-
if (env.DEBUG || stderr) {
|
|
217
|
-
child.stderr.on("data", (data) => {
|
|
218
|
-
if (env.DEBUG) {
|
|
219
|
-
console.error("Claude Code stderr:", data.toString());
|
|
220
|
-
}
|
|
221
|
-
if (stderr) {
|
|
222
|
-
stderr(data.toString());
|
|
223
|
-
}
|
|
224
|
-
});
|
|
609
|
+
const query2 = new Query(transport, isStreamingMode, canUseTool, hooks, abortController);
|
|
610
|
+
if (typeof prompt !== "string") {
|
|
611
|
+
query2.streamInput(prompt);
|
|
225
612
|
}
|
|
226
|
-
const cleanup = () => {
|
|
227
|
-
if (!child.killed) {
|
|
228
|
-
child.kill("SIGTERM");
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
abortController.signal.addEventListener("abort", cleanup);
|
|
232
|
-
process.on("exit", cleanup);
|
|
233
|
-
const processExitPromise = new Promise((resolve) => {
|
|
234
|
-
child.on("close", (code) => {
|
|
235
|
-
if (abortController.signal.aborted) {
|
|
236
|
-
query2.setError(new AbortError("Claude Code process aborted by user"));
|
|
237
|
-
}
|
|
238
|
-
if (code !== 0) {
|
|
239
|
-
query2.setError(new Error(`Claude Code process exited with code ${code}`));
|
|
240
|
-
} else {
|
|
241
|
-
resolve();
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
const query2 = new Query(childStdin, child.stdout, processExitPromise, canUseTool);
|
|
246
|
-
child.on("error", (error) => {
|
|
247
|
-
if (abortController.signal.aborted) {
|
|
248
|
-
query2.setError(new AbortError("Claude Code process aborted by user"));
|
|
249
|
-
} else {
|
|
250
|
-
query2.setError(new Error(`Failed to spawn Claude Code process: ${error.message}`));
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
processExitPromise.finally(() => {
|
|
254
|
-
cleanup();
|
|
255
|
-
abortController.signal.removeEventListener("abort", cleanup);
|
|
256
|
-
});
|
|
257
613
|
return query2;
|
|
258
614
|
}
|
|
259
615
|
|
|
260
616
|
class Query {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
processExitPromise;
|
|
617
|
+
transport;
|
|
618
|
+
isStreamingMode;
|
|
264
619
|
canUseTool;
|
|
620
|
+
hooks;
|
|
621
|
+
abortController;
|
|
265
622
|
pendingControlResponses = new Map;
|
|
623
|
+
cleanupPerformed = false;
|
|
266
624
|
sdkMessages;
|
|
267
625
|
inputStream = new Stream;
|
|
268
626
|
intialization;
|
|
269
627
|
cancelControllers = new Map;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.
|
|
628
|
+
hookCallbacks = new Map;
|
|
629
|
+
nextCallbackId = 0;
|
|
630
|
+
constructor(transport, isStreamingMode, canUseTool, hooks, abortController) {
|
|
631
|
+
this.transport = transport;
|
|
632
|
+
this.isStreamingMode = isStreamingMode;
|
|
274
633
|
this.canUseTool = canUseTool;
|
|
275
|
-
this.
|
|
634
|
+
this.hooks = hooks;
|
|
635
|
+
this.abortController = abortController;
|
|
276
636
|
this.sdkMessages = this.readSdkMessages();
|
|
277
|
-
|
|
637
|
+
this.readMessages();
|
|
638
|
+
if (this.isStreamingMode) {
|
|
278
639
|
this.intialization = this.initialize();
|
|
279
640
|
}
|
|
280
641
|
}
|
|
281
642
|
setError(error) {
|
|
282
643
|
this.inputStream.error(error);
|
|
283
644
|
}
|
|
645
|
+
cleanup(error) {
|
|
646
|
+
if (this.cleanupPerformed)
|
|
647
|
+
return;
|
|
648
|
+
this.cleanupPerformed = true;
|
|
649
|
+
try {
|
|
650
|
+
this.transport.close();
|
|
651
|
+
this.pendingControlResponses.clear();
|
|
652
|
+
if (error) {
|
|
653
|
+
this.inputStream.error(error);
|
|
654
|
+
} else {
|
|
655
|
+
this.inputStream.done();
|
|
656
|
+
}
|
|
657
|
+
} catch (_error) {}
|
|
658
|
+
}
|
|
284
659
|
next(...[value]) {
|
|
285
660
|
return this.sdkMessages.next(...[value]);
|
|
286
661
|
}
|
|
@@ -297,33 +672,28 @@ class Query {
|
|
|
297
672
|
return this.sdkMessages[Symbol.asyncDispose]();
|
|
298
673
|
}
|
|
299
674
|
async readMessages() {
|
|
300
|
-
const rl = createInterface({ input: this.childStdout });
|
|
301
675
|
try {
|
|
302
|
-
for await (const
|
|
303
|
-
if (
|
|
304
|
-
const
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
if (handler) {
|
|
308
|
-
handler(message.response);
|
|
309
|
-
}
|
|
310
|
-
continue;
|
|
311
|
-
} else if (message.type === "control_request") {
|
|
312
|
-
this.handleControlRequest(message);
|
|
313
|
-
continue;
|
|
314
|
-
} else if (message.type === "control_cancel_request") {
|
|
315
|
-
this.handleControlCancelRequest(message);
|
|
316
|
-
continue;
|
|
676
|
+
for await (const message of this.transport.readMessages()) {
|
|
677
|
+
if (message.type === "control_response") {
|
|
678
|
+
const handler = this.pendingControlResponses.get(message.response.request_id);
|
|
679
|
+
if (handler) {
|
|
680
|
+
handler(message.response);
|
|
317
681
|
}
|
|
318
|
-
|
|
682
|
+
continue;
|
|
683
|
+
} else if (message.type === "control_request") {
|
|
684
|
+
this.handleControlRequest(message);
|
|
685
|
+
continue;
|
|
686
|
+
} else if (message.type === "control_cancel_request") {
|
|
687
|
+
this.handleControlCancelRequest(message);
|
|
688
|
+
continue;
|
|
319
689
|
}
|
|
690
|
+
this.inputStream.enqueue(message);
|
|
320
691
|
}
|
|
321
|
-
|
|
692
|
+
this.inputStream.done();
|
|
693
|
+
this.cleanup();
|
|
322
694
|
} catch (error) {
|
|
323
695
|
this.inputStream.error(error);
|
|
324
|
-
|
|
325
|
-
this.inputStream.done();
|
|
326
|
-
rl.close();
|
|
696
|
+
this.cleanup(error);
|
|
327
697
|
}
|
|
328
698
|
}
|
|
329
699
|
async handleControlRequest(request) {
|
|
@@ -339,7 +709,7 @@ class Query {
|
|
|
339
709
|
response
|
|
340
710
|
}
|
|
341
711
|
};
|
|
342
|
-
this.
|
|
712
|
+
this.transport.write(JSON.stringify(controlResponse) + `
|
|
343
713
|
`);
|
|
344
714
|
} catch (error) {
|
|
345
715
|
const controlErrorResponse = {
|
|
@@ -350,7 +720,7 @@ class Query {
|
|
|
350
720
|
error: error.message || String(error)
|
|
351
721
|
}
|
|
352
722
|
};
|
|
353
|
-
this.
|
|
723
|
+
this.transport.write(JSON.stringify(controlErrorResponse) + `
|
|
354
724
|
`);
|
|
355
725
|
} finally {
|
|
356
726
|
this.cancelControllers.delete(request.request_id);
|
|
@@ -371,6 +741,9 @@ class Query {
|
|
|
371
741
|
return this.canUseTool(request.request.tool_name, request.request.input, {
|
|
372
742
|
signal
|
|
373
743
|
});
|
|
744
|
+
} else if (request.request.subtype === "hook_callback") {
|
|
745
|
+
const result = await this.handleHookCallbacks(request.request.callback_id, request.request.input, request.request.tool_use_id, signal);
|
|
746
|
+
return result;
|
|
374
747
|
}
|
|
375
748
|
throw new Error("Unsupported control request subtype: " + request.request.subtype);
|
|
376
749
|
}
|
|
@@ -380,24 +753,51 @@ class Query {
|
|
|
380
753
|
}
|
|
381
754
|
}
|
|
382
755
|
async initialize() {
|
|
383
|
-
|
|
384
|
-
|
|
756
|
+
let hooks;
|
|
757
|
+
if (this.hooks) {
|
|
758
|
+
hooks = {};
|
|
759
|
+
for (const [event, matchers] of Object.entries(this.hooks)) {
|
|
760
|
+
if (matchers.length > 0) {
|
|
761
|
+
hooks[event] = matchers.map((matcher) => {
|
|
762
|
+
const callbackIds = [];
|
|
763
|
+
for (const callback of matcher.hooks) {
|
|
764
|
+
const callbackId = `hook_${this.nextCallbackId++}`;
|
|
765
|
+
this.hookCallbacks.set(callbackId, callback);
|
|
766
|
+
callbackIds.push(callbackId);
|
|
767
|
+
}
|
|
768
|
+
return {
|
|
769
|
+
matcher: matcher.matcher,
|
|
770
|
+
hookCallbackIds: callbackIds
|
|
771
|
+
};
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
385
775
|
}
|
|
386
776
|
const initRequest = {
|
|
387
|
-
subtype: "initialize"
|
|
777
|
+
subtype: "initialize",
|
|
778
|
+
hooks
|
|
388
779
|
};
|
|
389
|
-
const response = await this.request(initRequest
|
|
780
|
+
const response = await this.request(initRequest);
|
|
390
781
|
return response.response;
|
|
391
782
|
}
|
|
392
783
|
async interrupt() {
|
|
393
|
-
if (!this.
|
|
784
|
+
if (!this.isStreamingMode) {
|
|
394
785
|
throw new Error("Interrupt requires --input-format stream-json");
|
|
395
786
|
}
|
|
396
787
|
await this.request({
|
|
397
788
|
subtype: "interrupt"
|
|
398
|
-
}
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
async setPermissionMode(mode) {
|
|
792
|
+
if (!this.isStreamingMode) {
|
|
793
|
+
throw new Error("setPermissionMode requires --input-format stream-json");
|
|
794
|
+
}
|
|
795
|
+
await this.request({
|
|
796
|
+
subtype: "set_permission_mode",
|
|
797
|
+
mode
|
|
798
|
+
});
|
|
399
799
|
}
|
|
400
|
-
request(request
|
|
800
|
+
request(request) {
|
|
401
801
|
const requestId = Math.random().toString(36).substring(2, 15);
|
|
402
802
|
const sdkRequest = {
|
|
403
803
|
request_id: requestId,
|
|
@@ -412,29 +812,42 @@ class Query {
|
|
|
412
812
|
reject(new Error(response.error));
|
|
413
813
|
}
|
|
414
814
|
});
|
|
415
|
-
|
|
815
|
+
this.transport.write(JSON.stringify(sdkRequest) + `
|
|
416
816
|
`);
|
|
417
817
|
});
|
|
418
818
|
}
|
|
419
819
|
async supportedCommands() {
|
|
820
|
+
if (!this.isStreamingMode) {
|
|
821
|
+
throw new Error("supportedCommands requires --input-format stream-json");
|
|
822
|
+
}
|
|
420
823
|
if (!this.intialization) {
|
|
421
|
-
throw new Error("
|
|
824
|
+
throw new Error("supportedCommands requires transport with bidirectional communication");
|
|
422
825
|
}
|
|
423
826
|
return (await this.intialization).commands;
|
|
424
827
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
828
|
+
async streamInput(stream) {
|
|
829
|
+
try {
|
|
830
|
+
for await (const message of stream) {
|
|
831
|
+
if (this.abortController?.signal.aborted)
|
|
832
|
+
break;
|
|
833
|
+
this.transport.write(JSON.stringify(message) + `
|
|
431
834
|
`);
|
|
835
|
+
}
|
|
836
|
+
this.transport.endInput();
|
|
837
|
+
} catch (error) {
|
|
838
|
+
if (!(error instanceof AbortError)) {
|
|
839
|
+
throw error;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
432
842
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
843
|
+
handleHookCallbacks(callbackId, input, toolUseID, abortSignal) {
|
|
844
|
+
const callback = this.hookCallbacks.get(callbackId);
|
|
845
|
+
if (!callback) {
|
|
846
|
+
throw new Error(`No hook callback found for ID: ${callbackId}`);
|
|
847
|
+
}
|
|
848
|
+
return callback(input, toolUseID, {
|
|
849
|
+
signal: abortSignal
|
|
850
|
+
});
|
|
438
851
|
}
|
|
439
852
|
}
|
|
440
853
|
function isRunningWithBun() {
|
|
@@ -442,5 +855,6 @@ function isRunningWithBun() {
|
|
|
442
855
|
}
|
|
443
856
|
export {
|
|
444
857
|
query,
|
|
445
|
-
Query
|
|
858
|
+
Query,
|
|
859
|
+
AbortError
|
|
446
860
|
};
|