@k-leumas/queque-cli 0.5.2 → 0.6.0
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/README.md +1 -0
- package/dist/cli/app.js +1046 -199
- package/dist/cli/app.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/app.js
CHANGED
|
@@ -83,7 +83,7 @@ var init_uuid = __esm({
|
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
// node_modules/.pnpm/@anthropic-ai+sdk@0.92.0_zod@4.1.5/node_modules/@anthropic-ai/sdk/internal/errors.mjs
|
|
86
|
-
function
|
|
86
|
+
function isAbortError2(err) {
|
|
87
87
|
return typeof err === "object" && err !== null && // Spec-compliant fetch implementations
|
|
88
88
|
("name" in err && err.name === "AbortError" || // Expo fetch
|
|
89
89
|
"message" in err && String(err.message).includes("FetchRequestCanceledException"));
|
|
@@ -841,7 +841,7 @@ var init_streaming = __esm({
|
|
|
841
841
|
}
|
|
842
842
|
done = true;
|
|
843
843
|
} catch (e) {
|
|
844
|
-
if (
|
|
844
|
+
if (isAbortError2(e))
|
|
845
845
|
return;
|
|
846
846
|
throw e;
|
|
847
847
|
} finally {
|
|
@@ -884,7 +884,7 @@ var init_streaming = __esm({
|
|
|
884
884
|
}
|
|
885
885
|
done = true;
|
|
886
886
|
} catch (e) {
|
|
887
|
-
if (
|
|
887
|
+
if (isAbortError2(e))
|
|
888
888
|
return;
|
|
889
889
|
throw e;
|
|
890
890
|
} finally {
|
|
@@ -3225,7 +3225,7 @@ var init_BetaMessageStream = __esm({
|
|
|
3225
3225
|
_BetaMessageStream_logger.set(this, void 0);
|
|
3226
3226
|
_BetaMessageStream_handleError.set(this, (error43) => {
|
|
3227
3227
|
__classPrivateFieldSet(this, _BetaMessageStream_errored, true, "f");
|
|
3228
|
-
if (
|
|
3228
|
+
if (isAbortError2(error43)) {
|
|
3229
3229
|
error43 = new APIUserAbortError();
|
|
3230
3230
|
}
|
|
3231
3231
|
if (error43 instanceof APIUserAbortError) {
|
|
@@ -5424,7 +5424,7 @@ var init_MessageStream = __esm({
|
|
|
5424
5424
|
_MessageStream_logger.set(this, void 0);
|
|
5425
5425
|
_MessageStream_handleError.set(this, (error43) => {
|
|
5426
5426
|
__classPrivateFieldSet(this, _MessageStream_errored, true, "f");
|
|
5427
|
-
if (
|
|
5427
|
+
if (isAbortError2(error43)) {
|
|
5428
5428
|
error43 = new APIUserAbortError();
|
|
5429
5429
|
}
|
|
5430
5430
|
if (error43 instanceof APIUserAbortError) {
|
|
@@ -6559,7 +6559,7 @@ var init_client = __esm({
|
|
|
6559
6559
|
if (options.signal?.aborted) {
|
|
6560
6560
|
throw new APIUserAbortError();
|
|
6561
6561
|
}
|
|
6562
|
-
const isTimeout =
|
|
6562
|
+
const isTimeout = isAbortError2(response) || /timed? ?out/i.test(String(response) + ("cause" in response ? String(response.cause) : ""));
|
|
6563
6563
|
if (retriesRemaining) {
|
|
6564
6564
|
loggerFor(this).info(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} - ${retryMessage}`);
|
|
6565
6565
|
loggerFor(this).debug(`[${requestLogID}] connection ${isTimeout ? "timed out" : "failed"} (${retryMessage})`, formatRequestDetails({
|
|
@@ -33239,7 +33239,7 @@ var require_websocket = __commonJS({
|
|
|
33239
33239
|
var http = __require("http");
|
|
33240
33240
|
var net4 = __require("net");
|
|
33241
33241
|
var tls = __require("tls");
|
|
33242
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
33242
|
+
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
33243
33243
|
var { Duplex, Readable } = __require("stream");
|
|
33244
33244
|
var { URL: URL2 } = __require("url");
|
|
33245
33245
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -33899,7 +33899,7 @@ var require_websocket = __commonJS({
|
|
|
33899
33899
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
33900
33900
|
return;
|
|
33901
33901
|
}
|
|
33902
|
-
const digest =
|
|
33902
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
33903
33903
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
33904
33904
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
33905
33905
|
return;
|
|
@@ -34266,7 +34266,7 @@ var require_websocket_server = __commonJS({
|
|
|
34266
34266
|
var EventEmitter3 = __require("events");
|
|
34267
34267
|
var http = __require("http");
|
|
34268
34268
|
var { Duplex } = __require("stream");
|
|
34269
|
-
var { createHash } = __require("crypto");
|
|
34269
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
34270
34270
|
var extension2 = require_extension();
|
|
34271
34271
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
34272
34272
|
var subprotocol2 = require_subprotocol();
|
|
@@ -34567,7 +34567,7 @@ var require_websocket_server = __commonJS({
|
|
|
34567
34567
|
);
|
|
34568
34568
|
}
|
|
34569
34569
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
34570
|
-
const digest =
|
|
34570
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
34571
34571
|
const headers = [
|
|
34572
34572
|
"HTTP/1.1 101 Switching Protocols",
|
|
34573
34573
|
"Upgrade: websocket",
|
|
@@ -48088,7 +48088,14 @@ var privacyConfigSchema = external_exports.object({
|
|
|
48088
48088
|
var safetyConfigSchema = external_exports.object({
|
|
48089
48089
|
destructiveCommandPatterns: external_exports.array(external_exports.string()).optional()
|
|
48090
48090
|
}).optional();
|
|
48091
|
+
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
48092
|
+
var DEFAULT_EFFORT = "low";
|
|
48093
|
+
var effortSchema = external_exports.preprocess(
|
|
48094
|
+
(value) => typeof value === "string" ? value.trim().toLowerCase() : value,
|
|
48095
|
+
external_exports.enum(EFFORT_LEVELS)
|
|
48096
|
+
).optional().catch(void 0);
|
|
48091
48097
|
var qqConfigFileSchema = external_exports.object({
|
|
48098
|
+
effort: effortSchema,
|
|
48092
48099
|
privacy: privacyConfigSchema,
|
|
48093
48100
|
safety: safetyConfigSchema
|
|
48094
48101
|
});
|
|
@@ -48153,10 +48160,14 @@ function loadQqConfig() {
|
|
|
48153
48160
|
sensitivePathRegexes: compilePatterns(sensitivePatterns),
|
|
48154
48161
|
redactLogKeys: new Set(redactKeys),
|
|
48155
48162
|
allowFileRead,
|
|
48156
|
-
destructiveCommandRegexes: compilePatterns(destructivePatterns)
|
|
48163
|
+
destructiveCommandRegexes: compilePatterns(destructivePatterns),
|
|
48164
|
+
effort: file2.effort ?? DEFAULT_EFFORT
|
|
48157
48165
|
};
|
|
48158
48166
|
return cachedConfig;
|
|
48159
48167
|
}
|
|
48168
|
+
function resolveEffort() {
|
|
48169
|
+
return loadQqConfig().effort;
|
|
48170
|
+
}
|
|
48160
48171
|
|
|
48161
48172
|
// src/shared/privacy-filter.ts
|
|
48162
48173
|
function isSensitivePath(path5) {
|
|
@@ -48411,6 +48422,113 @@ function readEnvValueFromDotEnvLocal(key, startDir = process.cwd()) {
|
|
|
48411
48422
|
return result;
|
|
48412
48423
|
}
|
|
48413
48424
|
|
|
48425
|
+
// src/providers/candidate-stream.ts
|
|
48426
|
+
var MAX_STREAMED_CANDIDATES = 5;
|
|
48427
|
+
function createCandidateScanner(onCandidate) {
|
|
48428
|
+
let started = false;
|
|
48429
|
+
let finished = false;
|
|
48430
|
+
let depth = 0;
|
|
48431
|
+
let inString = false;
|
|
48432
|
+
let escaped = false;
|
|
48433
|
+
let inObject = false;
|
|
48434
|
+
let objectText = "";
|
|
48435
|
+
let emitted = 0;
|
|
48436
|
+
const emit = (text) => {
|
|
48437
|
+
let parsed;
|
|
48438
|
+
try {
|
|
48439
|
+
parsed = JSON.parse(text);
|
|
48440
|
+
} catch {
|
|
48441
|
+
return;
|
|
48442
|
+
}
|
|
48443
|
+
const candidate = commandCandidateSchema.safeParse(parsed);
|
|
48444
|
+
if (!candidate.success) {
|
|
48445
|
+
return;
|
|
48446
|
+
}
|
|
48447
|
+
emitted++;
|
|
48448
|
+
onCandidate(candidate.data);
|
|
48449
|
+
if (emitted >= MAX_STREAMED_CANDIDATES) {
|
|
48450
|
+
finished = true;
|
|
48451
|
+
}
|
|
48452
|
+
};
|
|
48453
|
+
return (text) => {
|
|
48454
|
+
for (const ch of text) {
|
|
48455
|
+
if (finished) {
|
|
48456
|
+
return;
|
|
48457
|
+
}
|
|
48458
|
+
if (!started) {
|
|
48459
|
+
if (ch === "[") {
|
|
48460
|
+
started = true;
|
|
48461
|
+
depth = 1;
|
|
48462
|
+
}
|
|
48463
|
+
continue;
|
|
48464
|
+
}
|
|
48465
|
+
if (inObject) {
|
|
48466
|
+
objectText += ch;
|
|
48467
|
+
}
|
|
48468
|
+
if (inString) {
|
|
48469
|
+
if (escaped) {
|
|
48470
|
+
escaped = false;
|
|
48471
|
+
} else if (ch === "\\") {
|
|
48472
|
+
escaped = true;
|
|
48473
|
+
} else if (ch === '"') {
|
|
48474
|
+
inString = false;
|
|
48475
|
+
}
|
|
48476
|
+
continue;
|
|
48477
|
+
}
|
|
48478
|
+
if (ch === '"') {
|
|
48479
|
+
inString = true;
|
|
48480
|
+
} else if (ch === "[" || ch === "{") {
|
|
48481
|
+
depth++;
|
|
48482
|
+
if (ch === "{" && depth === 2) {
|
|
48483
|
+
inObject = true;
|
|
48484
|
+
objectText = ch;
|
|
48485
|
+
}
|
|
48486
|
+
} else if (ch === "]" || ch === "}") {
|
|
48487
|
+
depth--;
|
|
48488
|
+
if (ch === "}" && depth === 1 && inObject) {
|
|
48489
|
+
const closed = objectText;
|
|
48490
|
+
inObject = false;
|
|
48491
|
+
objectText = "";
|
|
48492
|
+
emit(closed);
|
|
48493
|
+
} else if (depth <= 0) {
|
|
48494
|
+
finished = true;
|
|
48495
|
+
}
|
|
48496
|
+
}
|
|
48497
|
+
}
|
|
48498
|
+
};
|
|
48499
|
+
}
|
|
48500
|
+
function stripJsonCodeFence(raw) {
|
|
48501
|
+
const match = raw.trim().match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```\s*$/);
|
|
48502
|
+
return match ? match[1].trim() : raw.trim();
|
|
48503
|
+
}
|
|
48504
|
+
function parseStrictCandidateList(text) {
|
|
48505
|
+
let parsed;
|
|
48506
|
+
try {
|
|
48507
|
+
parsed = JSON.parse(stripJsonCodeFence(text));
|
|
48508
|
+
} catch {
|
|
48509
|
+
return void 0;
|
|
48510
|
+
}
|
|
48511
|
+
const list = candidateListSchema.safeParse(parsed);
|
|
48512
|
+
return list.success ? list.data : void 0;
|
|
48513
|
+
}
|
|
48514
|
+
|
|
48515
|
+
// src/providers/provider.ts
|
|
48516
|
+
function abortError() {
|
|
48517
|
+
const error43 = new Error("QueQue: request aborted");
|
|
48518
|
+
error43.name = "AbortError";
|
|
48519
|
+
return error43;
|
|
48520
|
+
}
|
|
48521
|
+
function isNormalStopReason(stopReason) {
|
|
48522
|
+
return stopReason === "end_turn" || stopReason === "stop_sequence";
|
|
48523
|
+
}
|
|
48524
|
+
function isAbortError(error43) {
|
|
48525
|
+
if (error43 === null || typeof error43 !== "object") {
|
|
48526
|
+
return false;
|
|
48527
|
+
}
|
|
48528
|
+
const name = error43.name;
|
|
48529
|
+
return name === "AbortError" || name === "APIUserAbortError";
|
|
48530
|
+
}
|
|
48531
|
+
|
|
48414
48532
|
// src/shared/tool-catalog.ts
|
|
48415
48533
|
var CATALOG = Object.freeze(
|
|
48416
48534
|
`
|
|
@@ -48480,6 +48598,8 @@ ${buildSteeringText(chunk.payload.tools)}`;
|
|
|
48480
48598
|
|
|
48481
48599
|
// src/providers/claude.ts
|
|
48482
48600
|
var DEFAULT_MODEL = "claude-sonnet-5";
|
|
48601
|
+
var SDK_MAX_TOKENS = 16e3;
|
|
48602
|
+
var TRUNCATED_RESPONSE_MESSAGE = "QueQue: Claude response was truncated";
|
|
48483
48603
|
var cachedAnthropic;
|
|
48484
48604
|
async function getAnthropicClient(apiKey) {
|
|
48485
48605
|
if (cachedAnthropic?.apiKey === apiKey) {
|
|
@@ -48492,12 +48612,26 @@ async function getAnthropicClient(apiKey) {
|
|
|
48492
48612
|
cachedAnthropic = { apiKey, client: new AnthropicSdk({ apiKey }) };
|
|
48493
48613
|
return cachedAnthropic.client;
|
|
48494
48614
|
}
|
|
48615
|
+
var EFFORT_UNSUPPORTED_MODEL_RE = /haiku|claude-3|sonnet-4-5(?!\d)|(?:sonnet|opus)-4-(?:0|1|2025\d*)(?!\d)/i;
|
|
48616
|
+
function sdkOutputConfig(model, effort) {
|
|
48617
|
+
if (EFFORT_UNSUPPORTED_MODEL_RE.test(model)) {
|
|
48618
|
+
return void 0;
|
|
48619
|
+
}
|
|
48620
|
+
return { effort };
|
|
48621
|
+
}
|
|
48495
48622
|
function resolveModel() {
|
|
48496
48623
|
return process.env.QQ_MODEL ?? readEnvValueFromDotEnvLocal("QQ_MODEL") ?? DEFAULT_MODEL;
|
|
48497
48624
|
}
|
|
48498
48625
|
function extractText(content) {
|
|
48499
48626
|
return content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text ?? "").join("").trim();
|
|
48500
48627
|
}
|
|
48628
|
+
function completeCandidates(text) {
|
|
48629
|
+
const rows = [];
|
|
48630
|
+
createCandidateScanner((candidate) => {
|
|
48631
|
+
rows.push(candidate);
|
|
48632
|
+
})(text);
|
|
48633
|
+
return rows;
|
|
48634
|
+
}
|
|
48501
48635
|
function stripCodeFence(raw) {
|
|
48502
48636
|
const m = raw.trim().match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```\s*$/);
|
|
48503
48637
|
return m ? m[1].trim() : raw.trim();
|
|
@@ -48516,6 +48650,24 @@ function parseCandidates(text) {
|
|
|
48516
48650
|
return [{ command: 'echo ""', explanation: "QueQue: unexpected response format" }];
|
|
48517
48651
|
}
|
|
48518
48652
|
}
|
|
48653
|
+
function finalCandidateList(text, streamed, finishedNormally, logFields = {}) {
|
|
48654
|
+
const strict = parseStrictCandidateList(text);
|
|
48655
|
+
if (strict) {
|
|
48656
|
+
return strict;
|
|
48657
|
+
}
|
|
48658
|
+
if (streamed.length > 0) {
|
|
48659
|
+
return streamed;
|
|
48660
|
+
}
|
|
48661
|
+
if (!finishedNormally) {
|
|
48662
|
+
const complete = completeCandidates(text);
|
|
48663
|
+
if (complete.length === 0) {
|
|
48664
|
+
void appendDebugLog("provider", "response truncated", logFields);
|
|
48665
|
+
throw new Error(TRUNCATED_RESPONSE_MESSAGE);
|
|
48666
|
+
}
|
|
48667
|
+
return complete;
|
|
48668
|
+
}
|
|
48669
|
+
return parseCandidates(text);
|
|
48670
|
+
}
|
|
48519
48671
|
function shouldForceSelector() {
|
|
48520
48672
|
const configured = process.env.QQ_FORCE_SELECTOR ?? readEnvValueFromDotEnvLocal("QQ_FORCE_SELECTOR");
|
|
48521
48673
|
return configured === "1" || configured === "true";
|
|
@@ -48559,7 +48711,7 @@ function buildPrompt(envelope) {
|
|
|
48559
48711
|
)
|
|
48560
48712
|
].join("\n");
|
|
48561
48713
|
}
|
|
48562
|
-
async function fetchCandidates(envelope, rbuffer = "", timeoutMs = 25e3) {
|
|
48714
|
+
async function fetchCandidates(envelope, rbuffer = "", timeoutMs = 25e3, options = {}) {
|
|
48563
48715
|
const apiKey = usableSecret(process.env.ANTHROPIC_API_KEY) ?? usableSecret(readEnvValueFromDotEnvLocal("ANTHROPIC_API_KEY"));
|
|
48564
48716
|
if (!apiKey) {
|
|
48565
48717
|
throw new Error("ANTHROPIC_API_KEY is required in the environment or .env.local");
|
|
@@ -48567,33 +48719,57 @@ async function fetchCandidates(envelope, rbuffer = "", timeoutMs = 25e3) {
|
|
|
48567
48719
|
const client = await getAnthropicClient(apiKey);
|
|
48568
48720
|
const prompt = buildPrompt(envelope);
|
|
48569
48721
|
const model = resolveModel();
|
|
48722
|
+
const outputConfig = sdkOutputConfig(model, resolveEffort());
|
|
48570
48723
|
void appendDebugLog("provider", "request start", {
|
|
48571
48724
|
model,
|
|
48725
|
+
effort: outputConfig?.effort ?? "none",
|
|
48572
48726
|
cwd: envelope.base.cwd,
|
|
48573
48727
|
rbufferLength: rbuffer.length,
|
|
48574
48728
|
extras: envelope.extras.map((chunk) => chunk.kind)
|
|
48575
48729
|
});
|
|
48730
|
+
const { onCandidate, signal } = options;
|
|
48731
|
+
const streamed = [];
|
|
48576
48732
|
try {
|
|
48577
|
-
const
|
|
48733
|
+
const stream = client.messages.stream(
|
|
48578
48734
|
{
|
|
48579
48735
|
model,
|
|
48580
|
-
max_tokens:
|
|
48736
|
+
max_tokens: SDK_MAX_TOKENS,
|
|
48581
48737
|
system: buildSystemPrompt(envelope),
|
|
48582
|
-
messages: [{ role: "user", content: prompt }]
|
|
48738
|
+
messages: [{ role: "user", content: prompt }],
|
|
48739
|
+
...outputConfig ? { output_config: outputConfig } : {}
|
|
48583
48740
|
},
|
|
48584
|
-
{
|
|
48585
|
-
timeout: timeoutMs
|
|
48586
|
-
}
|
|
48741
|
+
{ timeout: timeoutMs, signal: options.signal }
|
|
48587
48742
|
);
|
|
48588
|
-
|
|
48589
|
-
|
|
48743
|
+
if (onCandidate) {
|
|
48744
|
+
const scan = createCandidateScanner((candidate) => {
|
|
48745
|
+
streamed.push(candidate);
|
|
48746
|
+
if (!signal?.aborted) {
|
|
48747
|
+
onCandidate(candidate);
|
|
48748
|
+
}
|
|
48749
|
+
});
|
|
48750
|
+
stream.on("text", (textDelta) => {
|
|
48751
|
+
scan(textDelta);
|
|
48752
|
+
});
|
|
48753
|
+
}
|
|
48754
|
+
const message = await stream.finalMessage();
|
|
48755
|
+
const text = extractText(message.content);
|
|
48756
|
+
const finalList = finalCandidateList(text, streamed, isNormalStopReason(message.stop_reason), {
|
|
48757
|
+
model,
|
|
48758
|
+
stopReason: message.stop_reason
|
|
48759
|
+
});
|
|
48760
|
+
const candidates = ensureSelectableCandidates(finalList);
|
|
48590
48761
|
void appendDebugLog("provider", "response parsed", {
|
|
48591
48762
|
model,
|
|
48592
48763
|
candidateCount: candidates.length,
|
|
48764
|
+
streamedCount: streamed.length,
|
|
48593
48765
|
forceSelector: shouldForceSelector()
|
|
48594
48766
|
});
|
|
48595
48767
|
return candidates;
|
|
48596
48768
|
} catch (error43) {
|
|
48769
|
+
if (isAbortError(error43) || signal?.aborted) {
|
|
48770
|
+
void appendDebugLog("provider", "request aborted", { model });
|
|
48771
|
+
throw abortError();
|
|
48772
|
+
}
|
|
48597
48773
|
void appendDebugLog("provider", "request failed", {
|
|
48598
48774
|
model,
|
|
48599
48775
|
message: error43 instanceof Error ? error43.message : String(error43)
|
|
@@ -48602,158 +48778,636 @@ async function fetchCandidates(envelope, rbuffer = "", timeoutMs = 25e3) {
|
|
|
48602
48778
|
}
|
|
48603
48779
|
}
|
|
48604
48780
|
var claudeAdapter = {
|
|
48605
|
-
fetchCandidates
|
|
48781
|
+
fetchCandidates: (envelope, options) => fetchCandidates(envelope, "", 25e3, options)
|
|
48606
48782
|
};
|
|
48607
48783
|
|
|
48608
|
-
// src/providers/claude-
|
|
48784
|
+
// src/providers/claude-prewarm.ts
|
|
48785
|
+
import { createHash } from "crypto";
|
|
48786
|
+
|
|
48787
|
+
// src/providers/claude-process.ts
|
|
48609
48788
|
import { spawn } from "child_process";
|
|
48610
|
-
|
|
48611
|
-
|
|
48612
|
-
|
|
48613
|
-
|
|
48614
|
-
|
|
48615
|
-
|
|
48616
|
-
|
|
48617
|
-
|
|
48618
|
-
|
|
48619
|
-
|
|
48620
|
-
|
|
48621
|
-
|
|
48622
|
-
|
|
48623
|
-
|
|
48624
|
-
|
|
48789
|
+
import { StringDecoder } from "string_decoder";
|
|
48790
|
+
|
|
48791
|
+
// src/providers/stream-json.ts
|
|
48792
|
+
var STREAM_JSON_CARRY_CAP_BYTES = 1024 * 1024;
|
|
48793
|
+
var streamJsonResultSchema = external_exports.object({
|
|
48794
|
+
type: external_exports.literal("result"),
|
|
48795
|
+
is_error: external_exports.boolean(),
|
|
48796
|
+
result: external_exports.string().optional(),
|
|
48797
|
+
subtype: external_exports.string().optional(),
|
|
48798
|
+
duration_api_ms: external_exports.number().optional(),
|
|
48799
|
+
/**
|
|
48800
|
+
* Claude Code 2.1.285 records it on `result` (Probe 2, `"end_turn"`); older builds and the
|
|
48801
|
+
* Probe 3 auth result leave it out. Null is kept so the runner can fail closed on it.
|
|
48802
|
+
*/
|
|
48803
|
+
stop_reason: external_exports.string().nullable().optional()
|
|
48804
|
+
});
|
|
48805
|
+
function isRecord(value) {
|
|
48806
|
+
return value !== null && typeof value === "object";
|
|
48807
|
+
}
|
|
48808
|
+
function createLineParser(onEvent, options = {}) {
|
|
48809
|
+
const maxCarryBytes = options.maxCarryBytes ?? STREAM_JSON_CARRY_CAP_BYTES;
|
|
48810
|
+
let carry = "";
|
|
48811
|
+
let carryBytes = 0;
|
|
48812
|
+
let overflowed = false;
|
|
48813
|
+
return (chunk) => {
|
|
48814
|
+
if (overflowed) {
|
|
48815
|
+
return;
|
|
48816
|
+
}
|
|
48817
|
+
carry += chunk;
|
|
48818
|
+
let nl = carry.indexOf("\n");
|
|
48819
|
+
if (nl === -1) {
|
|
48820
|
+
carryBytes += Buffer.byteLength(chunk, "utf8");
|
|
48821
|
+
} else {
|
|
48822
|
+
while (nl !== -1) {
|
|
48823
|
+
const line = carry.slice(0, nl).trim();
|
|
48824
|
+
carry = carry.slice(nl + 1);
|
|
48825
|
+
nl = carry.indexOf("\n");
|
|
48826
|
+
if (!line) {
|
|
48827
|
+
continue;
|
|
48828
|
+
}
|
|
48829
|
+
let event;
|
|
48830
|
+
try {
|
|
48831
|
+
event = JSON.parse(line);
|
|
48832
|
+
} catch {
|
|
48833
|
+
continue;
|
|
48834
|
+
}
|
|
48835
|
+
onEvent(event);
|
|
48836
|
+
}
|
|
48837
|
+
carryBytes = Buffer.byteLength(carry, "utf8");
|
|
48838
|
+
}
|
|
48839
|
+
if (carryBytes > maxCarryBytes) {
|
|
48840
|
+
overflowed = true;
|
|
48841
|
+
carry = "";
|
|
48842
|
+
carryBytes = 0;
|
|
48843
|
+
options.onOverflow?.();
|
|
48844
|
+
}
|
|
48845
|
+
};
|
|
48846
|
+
}
|
|
48847
|
+
function textDeltaOf(event) {
|
|
48848
|
+
if (!isRecord(event) || event.type !== "stream_event") {
|
|
48849
|
+
return void 0;
|
|
48850
|
+
}
|
|
48851
|
+
const inner = event.event;
|
|
48852
|
+
if (!isRecord(inner) || inner.type !== "content_block_delta") {
|
|
48853
|
+
return void 0;
|
|
48854
|
+
}
|
|
48855
|
+
const delta = inner.delta;
|
|
48856
|
+
if (!isRecord(delta) || delta.type !== "text_delta" || typeof delta.text !== "string") {
|
|
48857
|
+
return void 0;
|
|
48858
|
+
}
|
|
48859
|
+
return delta.text;
|
|
48860
|
+
}
|
|
48861
|
+
function isInitEvent(event) {
|
|
48862
|
+
return isRecord(event) && event.type === "system" && event.subtype === "init";
|
|
48863
|
+
}
|
|
48864
|
+
function buildStreamJsonUserLine(prompt) {
|
|
48865
|
+
return `${JSON.stringify({ type: "user", message: { role: "user", content: prompt } })}
|
|
48866
|
+
`;
|
|
48867
|
+
}
|
|
48868
|
+
|
|
48869
|
+
// src/providers/claude-process.ts
|
|
48870
|
+
var SIGKILL_GRACE_MS = 1500;
|
|
48871
|
+
var POST_RESULT_BACKSTOP_MS = 2e3;
|
|
48872
|
+
var STDERR_CAP_BYTES = 64 * 1024;
|
|
48873
|
+
function turnFailure(message, reason, sawInit, fields) {
|
|
48874
|
+
return Object.assign(new Error(message), { ...fields, reason, sawInit });
|
|
48875
|
+
}
|
|
48876
|
+
function timeoutFailure(stderr, sawInit) {
|
|
48877
|
+
return turnFailure("Command timed out: claude", "timeout", sawInit, {
|
|
48878
|
+
killed: true,
|
|
48879
|
+
signal: "SIGTERM",
|
|
48880
|
+
code: null,
|
|
48881
|
+
stdout: "",
|
|
48882
|
+
stderr
|
|
48883
|
+
});
|
|
48884
|
+
}
|
|
48885
|
+
function isRunning(child) {
|
|
48886
|
+
return child.pid !== void 0 && child.exitCode === null && child.signalCode === null;
|
|
48887
|
+
}
|
|
48888
|
+
function isRecord2(value) {
|
|
48889
|
+
return value !== null && typeof value === "object";
|
|
48890
|
+
}
|
|
48891
|
+
function spawnClaudeChild(command, argv, cwd2, env3) {
|
|
48892
|
+
return spawn(command, [...argv], {
|
|
48893
|
+
cwd: cwd2,
|
|
48894
|
+
env: env3,
|
|
48895
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
48896
|
+
shell: false,
|
|
48897
|
+
windowsHide: true
|
|
48898
|
+
});
|
|
48899
|
+
}
|
|
48900
|
+
function terminateChild(child, graceMs = SIGKILL_GRACE_MS) {
|
|
48901
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
48902
|
+
return;
|
|
48903
|
+
}
|
|
48904
|
+
child.kill("SIGTERM");
|
|
48905
|
+
const killTimer = setTimeout(() => {
|
|
48906
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
48907
|
+
child.kill("SIGKILL");
|
|
48908
|
+
}
|
|
48909
|
+
}, graceMs);
|
|
48910
|
+
killTimer.unref();
|
|
48911
|
+
child.once("exit", () => {
|
|
48912
|
+
clearTimeout(killTimer);
|
|
48913
|
+
});
|
|
48914
|
+
}
|
|
48915
|
+
function runClaudeTurn(options) {
|
|
48916
|
+
const { command, argv, cwd: cwd2, env: env3, prompt, budgetMs, signal, onTextDelta, onStage } = options;
|
|
48917
|
+
const backstopMs = options.backstopMs ?? POST_RESULT_BACKSTOP_MS;
|
|
48918
|
+
const killGraceMs = options.killGraceMs ?? SIGKILL_GRACE_MS;
|
|
48919
|
+
const prewarmed = options.child;
|
|
48920
|
+
if (signal?.aborted) {
|
|
48921
|
+
if (prewarmed) {
|
|
48922
|
+
terminateChild(prewarmed, killGraceMs);
|
|
48923
|
+
}
|
|
48924
|
+
return Promise.reject(abortError());
|
|
48925
|
+
}
|
|
48926
|
+
if (budgetMs <= 0) {
|
|
48927
|
+
if (prewarmed) {
|
|
48928
|
+
terminateChild(prewarmed, killGraceMs);
|
|
48929
|
+
}
|
|
48930
|
+
return Promise.reject(timeoutFailure("", false));
|
|
48931
|
+
}
|
|
48932
|
+
if (prewarmed && !isRunning(prewarmed)) {
|
|
48933
|
+
return Promise.reject(
|
|
48934
|
+
turnFailure("Command failed: claude", "exit-without-result", false, {
|
|
48935
|
+
code: prewarmed.exitCode,
|
|
48936
|
+
killed: false,
|
|
48937
|
+
signal: prewarmed.signalCode,
|
|
48938
|
+
stdout: "",
|
|
48939
|
+
stderr: ""
|
|
48940
|
+
})
|
|
48941
|
+
);
|
|
48625
48942
|
}
|
|
48626
48943
|
return new Promise((resolve3, reject) => {
|
|
48627
48944
|
let settled = false;
|
|
48628
|
-
|
|
48945
|
+
let sawInit = false;
|
|
48946
|
+
let sawDelta = false;
|
|
48947
|
+
let stderr = "";
|
|
48948
|
+
let stderrBytes = 0;
|
|
48949
|
+
let budgetTimer;
|
|
48950
|
+
let initTimer;
|
|
48951
|
+
let backstopArmed = false;
|
|
48952
|
+
const child = prewarmed ?? spawnClaudeChild(command, argv, cwd2, env3);
|
|
48953
|
+
const onAbort = () => {
|
|
48954
|
+
if (settled) {
|
|
48955
|
+
return;
|
|
48956
|
+
}
|
|
48957
|
+
onStage?.("aborted");
|
|
48958
|
+
terminateChild(child, killGraceMs);
|
|
48959
|
+
finish(() => reject(abortError()));
|
|
48960
|
+
};
|
|
48961
|
+
const collectStderr = (chunk) => {
|
|
48962
|
+
if (stderrBytes >= STDERR_CAP_BYTES) {
|
|
48963
|
+
return;
|
|
48964
|
+
}
|
|
48965
|
+
const bytes = Buffer.byteLength(chunk, "utf8");
|
|
48966
|
+
if (stderrBytes + bytes <= STDERR_CAP_BYTES) {
|
|
48967
|
+
stderr += chunk;
|
|
48968
|
+
stderrBytes += bytes;
|
|
48969
|
+
return;
|
|
48970
|
+
}
|
|
48971
|
+
const room = STDERR_CAP_BYTES - stderrBytes;
|
|
48972
|
+
stderr += new StringDecoder("utf8").write(Buffer.from(chunk, "utf8").subarray(0, room));
|
|
48973
|
+
stderrBytes = STDERR_CAP_BYTES;
|
|
48974
|
+
};
|
|
48975
|
+
const armBackstop = () => {
|
|
48976
|
+
if (backstopArmed) {
|
|
48977
|
+
return;
|
|
48978
|
+
}
|
|
48979
|
+
backstopArmed = true;
|
|
48980
|
+
const backstopTimer = setTimeout(() => {
|
|
48981
|
+
terminateChild(child, killGraceMs);
|
|
48982
|
+
}, backstopMs);
|
|
48983
|
+
backstopTimer.unref();
|
|
48984
|
+
child.once("exit", () => {
|
|
48985
|
+
clearTimeout(backstopTimer);
|
|
48986
|
+
});
|
|
48987
|
+
};
|
|
48988
|
+
const onOverflow = () => {
|
|
48989
|
+
if (settled) {
|
|
48990
|
+
return;
|
|
48991
|
+
}
|
|
48992
|
+
terminateChild(child, killGraceMs);
|
|
48993
|
+
finish(
|
|
48994
|
+
() => reject(
|
|
48995
|
+
turnFailure("Command failed: claude", "invalid-result", sawInit, {
|
|
48996
|
+
code: null,
|
|
48997
|
+
killed: false,
|
|
48998
|
+
signal: null,
|
|
48999
|
+
stdout: "",
|
|
49000
|
+
stderr
|
|
49001
|
+
})
|
|
49002
|
+
)
|
|
49003
|
+
);
|
|
49004
|
+
};
|
|
49005
|
+
function finish(callback) {
|
|
48629
49006
|
if (settled) {
|
|
48630
49007
|
return;
|
|
48631
49008
|
}
|
|
48632
49009
|
settled = true;
|
|
49010
|
+
if (budgetTimer) {
|
|
49011
|
+
clearTimeout(budgetTimer);
|
|
49012
|
+
}
|
|
49013
|
+
if (initTimer) {
|
|
49014
|
+
clearTimeout(initTimer);
|
|
49015
|
+
}
|
|
49016
|
+
signal?.removeEventListener("abort", onAbort);
|
|
48633
49017
|
callback();
|
|
48634
|
-
};
|
|
48635
|
-
const child = spawn(file2, [...args], {
|
|
48636
|
-
cwd: options.cwd,
|
|
48637
|
-
env: options.env,
|
|
48638
|
-
stdio,
|
|
48639
|
-
windowsHide: options.windowsHide ?? true,
|
|
48640
|
-
shell: options.shell ?? false
|
|
48641
|
-
});
|
|
48642
|
-
const stdoutChunks = [];
|
|
48643
|
-
const stderrChunks = [];
|
|
48644
|
-
let exceededBuffer = false;
|
|
48645
|
-
let timedOut = false;
|
|
48646
|
-
let timer;
|
|
48647
|
-
function combined(chunks) {
|
|
48648
|
-
return Buffer.concat(chunks).toString(encoding);
|
|
48649
49018
|
}
|
|
48650
|
-
|
|
48651
|
-
|
|
48652
|
-
|
|
48653
|
-
exceededBuffer = true;
|
|
48654
|
-
child.kill(killSignal);
|
|
49019
|
+
const handleEvent = (event) => {
|
|
49020
|
+
if (settled) {
|
|
49021
|
+
return;
|
|
48655
49022
|
}
|
|
48656
|
-
|
|
48657
|
-
|
|
48658
|
-
|
|
48659
|
-
|
|
48660
|
-
|
|
48661
|
-
|
|
48662
|
-
|
|
48663
|
-
|
|
48664
|
-
|
|
48665
|
-
});
|
|
48666
|
-
child.stderr?.on("data", (chunk) => {
|
|
48667
|
-
pushChunk(stderrChunks, chunk);
|
|
48668
|
-
});
|
|
48669
|
-
child.on("error", (error43) => {
|
|
48670
|
-
if (timer) {
|
|
48671
|
-
clearTimeout(timer);
|
|
49023
|
+
if (isInitEvent(event)) {
|
|
49024
|
+
if (!sawInit) {
|
|
49025
|
+
sawInit = true;
|
|
49026
|
+
if (initTimer) {
|
|
49027
|
+
clearTimeout(initTimer);
|
|
49028
|
+
}
|
|
49029
|
+
onStage?.("init");
|
|
49030
|
+
}
|
|
49031
|
+
return;
|
|
48672
49032
|
}
|
|
48673
|
-
|
|
48674
|
-
|
|
48675
|
-
|
|
48676
|
-
|
|
48677
|
-
|
|
48678
|
-
|
|
48679
|
-
|
|
49033
|
+
const text = textDeltaOf(event);
|
|
49034
|
+
if (text !== void 0) {
|
|
49035
|
+
if (!sawDelta) {
|
|
49036
|
+
sawDelta = true;
|
|
49037
|
+
onStage?.("first-delta");
|
|
49038
|
+
}
|
|
49039
|
+
if (!settled) {
|
|
49040
|
+
onTextDelta?.(text);
|
|
49041
|
+
}
|
|
49042
|
+
return;
|
|
48680
49043
|
}
|
|
48681
|
-
|
|
48682
|
-
const stderr = combined(stderrChunks);
|
|
48683
|
-
if (exceededBuffer || timedOut) {
|
|
48684
|
-
const error43 = Object.assign(new Error(`Command timed out: ${file2}`), {
|
|
48685
|
-
killed: true,
|
|
48686
|
-
signal: signal ?? killSignal,
|
|
48687
|
-
code: null,
|
|
48688
|
-
stdout,
|
|
48689
|
-
stderr
|
|
48690
|
-
});
|
|
48691
|
-
finish(() => {
|
|
48692
|
-
reject(error43);
|
|
48693
|
-
});
|
|
49044
|
+
if (!isRecord2(event) || event.type !== "result") {
|
|
48694
49045
|
return;
|
|
48695
49046
|
}
|
|
48696
|
-
|
|
48697
|
-
|
|
48698
|
-
|
|
48699
|
-
|
|
48700
|
-
|
|
48701
|
-
|
|
48702
|
-
|
|
48703
|
-
|
|
48704
|
-
|
|
48705
|
-
|
|
48706
|
-
|
|
49047
|
+
armBackstop();
|
|
49048
|
+
const parsed = streamJsonResultSchema.safeParse(event);
|
|
49049
|
+
if (!parsed.success) {
|
|
49050
|
+
finish(
|
|
49051
|
+
() => reject(
|
|
49052
|
+
turnFailure("Command failed: claude", "invalid-result", sawInit, {
|
|
49053
|
+
code: null,
|
|
49054
|
+
killed: false,
|
|
49055
|
+
signal: null,
|
|
49056
|
+
stdout: "",
|
|
49057
|
+
stderr
|
|
49058
|
+
})
|
|
49059
|
+
)
|
|
49060
|
+
);
|
|
48707
49061
|
return;
|
|
48708
49062
|
}
|
|
48709
|
-
if (
|
|
48710
|
-
|
|
48711
|
-
|
|
48712
|
-
|
|
48713
|
-
|
|
48714
|
-
|
|
48715
|
-
|
|
48716
|
-
|
|
48717
|
-
|
|
48718
|
-
|
|
48719
|
-
|
|
49063
|
+
if (parsed.data.is_error) {
|
|
49064
|
+
finish(
|
|
49065
|
+
() => reject(
|
|
49066
|
+
turnFailure("Command failed: claude", "is-error", sawInit, {
|
|
49067
|
+
code: 1,
|
|
49068
|
+
killed: false,
|
|
49069
|
+
signal: null,
|
|
49070
|
+
stdout: parsed.data.result ?? "",
|
|
49071
|
+
stderr
|
|
49072
|
+
})
|
|
49073
|
+
)
|
|
49074
|
+
);
|
|
48720
49075
|
return;
|
|
48721
49076
|
}
|
|
48722
|
-
|
|
48723
|
-
|
|
48724
|
-
|
|
49077
|
+
const stopReason = parsed.data.stop_reason;
|
|
49078
|
+
if (stopReason !== void 0 && !isNormalStopReason(parsed.data.stop_reason)) {
|
|
49079
|
+
finish(
|
|
49080
|
+
() => reject(
|
|
49081
|
+
turnFailure("Command failed: claude", "truncated", sawInit, {
|
|
49082
|
+
code: null,
|
|
49083
|
+
killed: false,
|
|
49084
|
+
signal: null,
|
|
49085
|
+
stdout: parsed.data.result ?? "",
|
|
49086
|
+
stderr,
|
|
49087
|
+
stopReason
|
|
49088
|
+
})
|
|
49089
|
+
)
|
|
49090
|
+
);
|
|
49091
|
+
return;
|
|
49092
|
+
}
|
|
49093
|
+
onStage?.("result");
|
|
49094
|
+
finish(() => resolve3(parsed.data.result ?? ""));
|
|
49095
|
+
};
|
|
49096
|
+
if (!prewarmed && child.pid !== void 0) {
|
|
49097
|
+
onStage?.("spawned", { pid: child.pid });
|
|
49098
|
+
}
|
|
49099
|
+
child.stdout.setEncoding("utf8");
|
|
49100
|
+
child.stdout.on("data", createLineParser(handleEvent, { onOverflow }));
|
|
49101
|
+
child.stderr.setEncoding("utf8");
|
|
49102
|
+
child.stderr.on("data", collectStderr);
|
|
49103
|
+
child.on("error", (error43) => {
|
|
49104
|
+
finish(() => reject(Object.assign(error43, { reason: "spawn-error", sawInit })));
|
|
49105
|
+
});
|
|
49106
|
+
child.on("exit", (code, exitSignal) => {
|
|
49107
|
+
onStage?.("exit", { code, signal: exitSignal });
|
|
48725
49108
|
});
|
|
49109
|
+
child.on("close", (code, closeSignal) => {
|
|
49110
|
+
finish(
|
|
49111
|
+
() => reject(
|
|
49112
|
+
turnFailure("Command failed: claude", "exit-without-result", sawInit, {
|
|
49113
|
+
code,
|
|
49114
|
+
killed: false,
|
|
49115
|
+
signal: closeSignal,
|
|
49116
|
+
stdout: "",
|
|
49117
|
+
stderr
|
|
49118
|
+
})
|
|
49119
|
+
)
|
|
49120
|
+
);
|
|
49121
|
+
});
|
|
49122
|
+
budgetTimer = setTimeout(() => {
|
|
49123
|
+
if (settled) {
|
|
49124
|
+
return;
|
|
49125
|
+
}
|
|
49126
|
+
onStage?.("timed-out");
|
|
49127
|
+
terminateChild(child, killGraceMs);
|
|
49128
|
+
finish(() => reject(timeoutFailure(stderr, sawInit)));
|
|
49129
|
+
}, budgetMs);
|
|
49130
|
+
budgetTimer.unref();
|
|
49131
|
+
if (options.initDeadlineMs !== void 0) {
|
|
49132
|
+
const initDeadlineMs = options.initDeadlineMs;
|
|
49133
|
+
initTimer = setTimeout(() => {
|
|
49134
|
+
if (settled || sawInit) {
|
|
49135
|
+
return;
|
|
49136
|
+
}
|
|
49137
|
+
onStage?.("init-timed-out");
|
|
49138
|
+
terminateChild(child, killGraceMs);
|
|
49139
|
+
finish(
|
|
49140
|
+
() => reject(
|
|
49141
|
+
turnFailure("Command failed: claude", "stale-prewarm", false, {
|
|
49142
|
+
code: null,
|
|
49143
|
+
killed: false,
|
|
49144
|
+
signal: null,
|
|
49145
|
+
stdout: "",
|
|
49146
|
+
stderr
|
|
49147
|
+
})
|
|
49148
|
+
)
|
|
49149
|
+
);
|
|
49150
|
+
}, initDeadlineMs);
|
|
49151
|
+
initTimer.unref();
|
|
49152
|
+
}
|
|
49153
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
49154
|
+
child.stdin.on("error", () => {
|
|
49155
|
+
});
|
|
49156
|
+
child.stdin.write(buildStreamJsonUserLine(prompt));
|
|
49157
|
+
child.stdin.end();
|
|
48726
49158
|
});
|
|
48727
49159
|
}
|
|
48728
49160
|
|
|
49161
|
+
// src/providers/claude-prewarm.ts
|
|
49162
|
+
var enabled = false;
|
|
49163
|
+
var slot;
|
|
49164
|
+
var diedIdle = false;
|
|
49165
|
+
function prewarmKey(spec) {
|
|
49166
|
+
return createHash("sha256").update(JSON.stringify({ command: spec.command, cwd: spec.cwd, argv: spec.argv })).digest("hex");
|
|
49167
|
+
}
|
|
49168
|
+
function enablePrewarm() {
|
|
49169
|
+
enabled = true;
|
|
49170
|
+
}
|
|
49171
|
+
function disablePrewarm() {
|
|
49172
|
+
retirePrewarmed();
|
|
49173
|
+
enabled = false;
|
|
49174
|
+
diedIdle = false;
|
|
49175
|
+
}
|
|
49176
|
+
function retireChild(child, graceMs) {
|
|
49177
|
+
if (!isRunning(child)) {
|
|
49178
|
+
return;
|
|
49179
|
+
}
|
|
49180
|
+
child.stdin.end();
|
|
49181
|
+
const killTimer = setTimeout(() => {
|
|
49182
|
+
if (isRunning(child)) {
|
|
49183
|
+
child.kill("SIGKILL");
|
|
49184
|
+
}
|
|
49185
|
+
}, graceMs);
|
|
49186
|
+
killTimer.unref();
|
|
49187
|
+
child.once("exit", () => {
|
|
49188
|
+
clearTimeout(killTimer);
|
|
49189
|
+
});
|
|
49190
|
+
}
|
|
49191
|
+
function retireSlot(graceMs) {
|
|
49192
|
+
const entry = slot;
|
|
49193
|
+
if (!entry) {
|
|
49194
|
+
return;
|
|
49195
|
+
}
|
|
49196
|
+
slot = void 0;
|
|
49197
|
+
entry.state = "retired";
|
|
49198
|
+
void appendDebugLog("provider", "prewarm retired", { pid: entry.child.pid ?? null });
|
|
49199
|
+
retireChild(entry.child, graceMs);
|
|
49200
|
+
}
|
|
49201
|
+
function schedulePrewarm(spec) {
|
|
49202
|
+
if (!enabled) {
|
|
49203
|
+
return;
|
|
49204
|
+
}
|
|
49205
|
+
const key = prewarmKey(spec);
|
|
49206
|
+
if (slot && slot.key === key && isRunning(slot.child)) {
|
|
49207
|
+
return;
|
|
49208
|
+
}
|
|
49209
|
+
retireSlot(SIGKILL_GRACE_MS);
|
|
49210
|
+
diedIdle = false;
|
|
49211
|
+
let child;
|
|
49212
|
+
try {
|
|
49213
|
+
child = spawnClaudeChild(spec.command, spec.argv, spec.cwd, process.env);
|
|
49214
|
+
} catch (error43) {
|
|
49215
|
+
void appendDebugLog("provider", "prewarm spawn failed", {
|
|
49216
|
+
code: error43.code ?? null
|
|
49217
|
+
});
|
|
49218
|
+
return;
|
|
49219
|
+
}
|
|
49220
|
+
const entry = { child, cwd: spec.cwd, key, state: "idle", spawnedAt: Date.now() };
|
|
49221
|
+
slot = entry;
|
|
49222
|
+
const clearIfIdle = () => {
|
|
49223
|
+
if (slot === entry) {
|
|
49224
|
+
slot = void 0;
|
|
49225
|
+
diedIdle = true;
|
|
49226
|
+
}
|
|
49227
|
+
};
|
|
49228
|
+
child.on("error", (error43) => {
|
|
49229
|
+
const wasIdle = entry.state === "idle";
|
|
49230
|
+
clearIfIdle();
|
|
49231
|
+
if (wasIdle) {
|
|
49232
|
+
void appendDebugLog("provider", "prewarm error", { code: error43.code ?? null });
|
|
49233
|
+
}
|
|
49234
|
+
});
|
|
49235
|
+
child.stdin.on("error", () => {
|
|
49236
|
+
});
|
|
49237
|
+
child.on("exit", (code, signal) => {
|
|
49238
|
+
clearIfIdle();
|
|
49239
|
+
if (entry.state !== "taken") {
|
|
49240
|
+
void appendDebugLog("provider", "prewarm exit", {
|
|
49241
|
+
pid: child.pid ?? null,
|
|
49242
|
+
code,
|
|
49243
|
+
signal,
|
|
49244
|
+
retired: entry.state === "retired"
|
|
49245
|
+
});
|
|
49246
|
+
}
|
|
49247
|
+
});
|
|
49248
|
+
if (child.pid !== void 0) {
|
|
49249
|
+
void appendDebugLog("provider", "prewarm spawned", { pid: child.pid });
|
|
49250
|
+
}
|
|
49251
|
+
}
|
|
49252
|
+
function takePrewarmed(spec) {
|
|
49253
|
+
if (!enabled) {
|
|
49254
|
+
return { miss: "disabled" };
|
|
49255
|
+
}
|
|
49256
|
+
const entry = slot;
|
|
49257
|
+
if (!entry) {
|
|
49258
|
+
const reason = diedIdle ? "dead" : "empty";
|
|
49259
|
+
diedIdle = false;
|
|
49260
|
+
return { miss: reason };
|
|
49261
|
+
}
|
|
49262
|
+
if (!isRunning(entry.child)) {
|
|
49263
|
+
slot = void 0;
|
|
49264
|
+
entry.state = "retired";
|
|
49265
|
+
diedIdle = false;
|
|
49266
|
+
return { miss: "dead" };
|
|
49267
|
+
}
|
|
49268
|
+
if (entry.cwd !== spec.cwd) {
|
|
49269
|
+
retireSlot(SIGKILL_GRACE_MS);
|
|
49270
|
+
return { miss: "cwd" };
|
|
49271
|
+
}
|
|
49272
|
+
if (entry.key !== prewarmKey(spec)) {
|
|
49273
|
+
retireSlot(SIGKILL_GRACE_MS);
|
|
49274
|
+
return { miss: "prompt" };
|
|
49275
|
+
}
|
|
49276
|
+
slot = void 0;
|
|
49277
|
+
entry.state = "taken";
|
|
49278
|
+
return { child: entry.child, spawnedAt: entry.spawnedAt };
|
|
49279
|
+
}
|
|
49280
|
+
function retirePrewarmed(graceMs = SIGKILL_GRACE_MS) {
|
|
49281
|
+
retireSlot(graceMs);
|
|
49282
|
+
}
|
|
49283
|
+
|
|
48729
49284
|
// src/providers/claude-cli.ts
|
|
48730
|
-
|
|
48731
|
-
|
|
48732
|
-
|
|
49285
|
+
var WARM_INIT_DEADLINE_MS = 1500;
|
|
49286
|
+
var PREWARM_BOOT_ALLOWANCE_MS = 3e3;
|
|
49287
|
+
function warmInitDeadlineMs(spawnedAt, now = Date.now()) {
|
|
49288
|
+
const ageMs = Math.max(0, now - spawnedAt);
|
|
49289
|
+
return WARM_INIT_DEADLINE_MS + Math.max(0, PREWARM_BOOT_ALLOWANCE_MS - ageMs);
|
|
49290
|
+
}
|
|
49291
|
+
var CLAUDE_COMMAND = "claude";
|
|
49292
|
+
function buildClaudeCliArgv(envelope) {
|
|
49293
|
+
return [
|
|
48733
49294
|
"-p",
|
|
49295
|
+
"--input-format",
|
|
49296
|
+
"stream-json",
|
|
48734
49297
|
"--output-format",
|
|
48735
|
-
"
|
|
49298
|
+
"stream-json",
|
|
49299
|
+
"--verbose",
|
|
49300
|
+
"--include-partial-messages",
|
|
49301
|
+
"--effort",
|
|
49302
|
+
resolveEffort(),
|
|
48736
49303
|
"--safe-mode",
|
|
48737
49304
|
"--tools",
|
|
48738
49305
|
"",
|
|
48739
49306
|
"--no-session-persistence",
|
|
48740
49307
|
"--system-prompt",
|
|
48741
|
-
buildSystemPrompt(envelope)
|
|
48742
|
-
prompt
|
|
49308
|
+
buildSystemPrompt(envelope)
|
|
48743
49309
|
];
|
|
48744
|
-
|
|
48745
|
-
|
|
48746
|
-
|
|
48747
|
-
|
|
48748
|
-
|
|
48749
|
-
|
|
48750
|
-
|
|
48751
|
-
|
|
48752
|
-
|
|
48753
|
-
|
|
49310
|
+
}
|
|
49311
|
+
async function fetchClaudeCliCandidates(envelope, remainingBudgetMs, options = {}) {
|
|
49312
|
+
const startedAt = Date.now();
|
|
49313
|
+
const deadline = startedAt + remainingBudgetMs;
|
|
49314
|
+
const streamed = [];
|
|
49315
|
+
const { onCandidate, signal } = options;
|
|
49316
|
+
const spec = {
|
|
49317
|
+
command: CLAUDE_COMMAND,
|
|
49318
|
+
argv: buildClaudeCliArgv(envelope),
|
|
49319
|
+
cwd: envelope.base.cwd
|
|
48754
49320
|
};
|
|
48755
|
-
const
|
|
48756
|
-
|
|
49321
|
+
const prompt = buildPrompt(envelope);
|
|
49322
|
+
const createTextDeltaHandler = () => onCandidate ? createCandidateScanner((candidate) => {
|
|
49323
|
+
streamed.push(candidate);
|
|
49324
|
+
if (streamed.length === 1) {
|
|
49325
|
+
void appendDebugLog("provider", "cli first candidate", { ms: Date.now() - startedAt });
|
|
49326
|
+
}
|
|
49327
|
+
if (!signal?.aborted) {
|
|
49328
|
+
onCandidate(candidate);
|
|
49329
|
+
}
|
|
49330
|
+
}) : void 0;
|
|
49331
|
+
const runTurn = (child, budgetMs, initDeadlineMs) => runClaudeTurn({
|
|
49332
|
+
command: spec.command,
|
|
49333
|
+
argv: spec.argv,
|
|
49334
|
+
cwd: spec.cwd,
|
|
49335
|
+
env: process.env,
|
|
49336
|
+
prompt,
|
|
49337
|
+
budgetMs,
|
|
49338
|
+
signal,
|
|
49339
|
+
onTextDelta: createTextDeltaHandler(),
|
|
49340
|
+
onStage: (stage, detail) => {
|
|
49341
|
+
logTurnStage(stage, detail, Date.now() - startedAt, child !== void 0);
|
|
49342
|
+
},
|
|
49343
|
+
...child ? { child } : {},
|
|
49344
|
+
...initDeadlineMs === void 0 ? {} : { initDeadlineMs }
|
|
49345
|
+
});
|
|
49346
|
+
try {
|
|
49347
|
+
let text;
|
|
49348
|
+
let finishedNormally = true;
|
|
49349
|
+
let stopReason;
|
|
49350
|
+
try {
|
|
49351
|
+
const take = takePrewarmed(spec);
|
|
49352
|
+
if ("child" in take) {
|
|
49353
|
+
void appendDebugLog("provider", "prewarm hit");
|
|
49354
|
+
try {
|
|
49355
|
+
text = await runTurn(take.child, remainingBudgetMs, warmInitDeadlineMs(take.spawnedAt));
|
|
49356
|
+
} catch (error43) {
|
|
49357
|
+
const leftMs = deadline - Date.now();
|
|
49358
|
+
if (!isStalePrewarmFailure(error43) || streamed.length > 0 || signal?.aborted || leftMs <= 0) {
|
|
49359
|
+
throw error43;
|
|
49360
|
+
}
|
|
49361
|
+
void appendDebugLog("provider", "prewarm retry cold", { ms: Date.now() - startedAt });
|
|
49362
|
+
text = await runTurn(void 0, leftMs);
|
|
49363
|
+
}
|
|
49364
|
+
} else {
|
|
49365
|
+
void appendDebugLog("provider", "prewarm miss", { reason: take.miss });
|
|
49366
|
+
text = await runTurn(void 0, remainingBudgetMs);
|
|
49367
|
+
}
|
|
49368
|
+
} catch (error43) {
|
|
49369
|
+
if (!isTruncatedFailure(error43)) {
|
|
49370
|
+
throw error43;
|
|
49371
|
+
}
|
|
49372
|
+
const stdout = error43.stdout;
|
|
49373
|
+
text = typeof stdout === "string" ? stdout : "";
|
|
49374
|
+
finishedNormally = false;
|
|
49375
|
+
stopReason = error43.stopReason;
|
|
49376
|
+
}
|
|
49377
|
+
return ensureSelectableCandidates(
|
|
49378
|
+
finalCandidateList(text, streamed, finishedNormally, { stopReason })
|
|
49379
|
+
);
|
|
49380
|
+
} finally {
|
|
49381
|
+
schedulePrewarm(spec);
|
|
49382
|
+
}
|
|
49383
|
+
}
|
|
49384
|
+
function isStalePrewarmFailure(error43) {
|
|
49385
|
+
if (error43 === null || typeof error43 !== "object") {
|
|
49386
|
+
return false;
|
|
49387
|
+
}
|
|
49388
|
+
const reason = error43.reason;
|
|
49389
|
+
return reason === "exit-without-result" || reason === "stale-prewarm";
|
|
49390
|
+
}
|
|
49391
|
+
function isTruncatedFailure(error43) {
|
|
49392
|
+
if (error43 === null || typeof error43 !== "object") {
|
|
49393
|
+
return false;
|
|
49394
|
+
}
|
|
49395
|
+
const reason = error43.reason;
|
|
49396
|
+
return reason === "truncated";
|
|
49397
|
+
}
|
|
49398
|
+
var STAGE_LOG_NAMES = {
|
|
49399
|
+
spawned: "cli spawned",
|
|
49400
|
+
init: "cli init",
|
|
49401
|
+
"first-delta": "cli first delta",
|
|
49402
|
+
result: "cli result",
|
|
49403
|
+
exit: "cli exit",
|
|
49404
|
+
aborted: "cli aborted",
|
|
49405
|
+
"timed-out": "cli timed out",
|
|
49406
|
+
"init-timed-out": "cli init timed out"
|
|
49407
|
+
};
|
|
49408
|
+
function logTurnStage(stage, detail, ms, warm) {
|
|
49409
|
+
const fields = stage === "exit" ? { ms, warm, code: detail?.code ?? null, signal: detail?.signal ?? null } : { ms, warm };
|
|
49410
|
+
void appendDebugLog("provider", STAGE_LOG_NAMES[stage], fields);
|
|
48757
49411
|
}
|
|
48758
49412
|
|
|
48759
49413
|
// src/providers/claude-default.ts
|
|
@@ -48796,6 +49450,9 @@ function lookupApiKey() {
|
|
|
48796
49450
|
return usableSecret(process.env.ANTHROPIC_API_KEY) ?? usableSecret(readEnvValueFromDotEnvLocal("ANTHROPIC_API_KEY"));
|
|
48797
49451
|
}
|
|
48798
49452
|
function hardError(error43) {
|
|
49453
|
+
if (error43 instanceof Error && error43.message === TRUNCATED_RESPONSE_MESSAGE) {
|
|
49454
|
+
return error43;
|
|
49455
|
+
}
|
|
48799
49456
|
if (isEnoent(error43)) {
|
|
48800
49457
|
return new Error(ENOENT_MESSAGE);
|
|
48801
49458
|
}
|
|
@@ -48808,18 +49465,22 @@ function hardError(error43) {
|
|
|
48808
49465
|
return new Error(D13_MESSAGE);
|
|
48809
49466
|
}
|
|
48810
49467
|
var claudeDefaultAdapter = {
|
|
48811
|
-
async fetchCandidates(envelope) {
|
|
49468
|
+
async fetchCandidates(envelope, options) {
|
|
48812
49469
|
const deadline = Date.now() + TOTAL_BUDGET_MS;
|
|
48813
49470
|
const apiKey = lookupApiKey();
|
|
48814
49471
|
if (apiKey) {
|
|
48815
49472
|
void appendDebugLog("provider", "sdk path", {
|
|
48816
49473
|
path: "sdk"
|
|
48817
49474
|
});
|
|
48818
|
-
return fetchCandidates(envelope, "", remainingMs(deadline));
|
|
49475
|
+
return fetchCandidates(envelope, "", remainingMs(deadline), options);
|
|
48819
49476
|
}
|
|
48820
49477
|
try {
|
|
48821
|
-
return await fetchClaudeCliCandidates(envelope, remainingMs(deadline));
|
|
49478
|
+
return await fetchClaudeCliCandidates(envelope, remainingMs(deadline), options);
|
|
48822
49479
|
} catch (error43) {
|
|
49480
|
+
if (isAbortError(error43)) {
|
|
49481
|
+
void appendDebugLog("provider", "claude-cli aborted");
|
|
49482
|
+
throw error43;
|
|
49483
|
+
}
|
|
48823
49484
|
void appendDebugLog("provider", "claude-cli path", {
|
|
48824
49485
|
path: "claude-cli",
|
|
48825
49486
|
exitCode: exitCodeDetail(error43),
|
|
@@ -57261,8 +57922,8 @@ var Ink = class {
|
|
|
57261
57922
|
};
|
|
57262
57923
|
return { resume, [Symbol.asyncDispose]: resume };
|
|
57263
57924
|
}
|
|
57264
|
-
setAlternateScreen(
|
|
57265
|
-
this.alternateScreen = this.resolveAlternateScreenOption(
|
|
57925
|
+
setAlternateScreen(enabled2) {
|
|
57926
|
+
this.alternateScreen = this.resolveAlternateScreenOption(enabled2, this.interactive);
|
|
57266
57927
|
if (this.alternateScreen) {
|
|
57267
57928
|
this.writeBestEffort(this.options.stdout, base_exports.enterAlternativeScreen);
|
|
57268
57929
|
this.writeBestEffort(this.options.stdout, hideCursorEscape);
|
|
@@ -58334,7 +58995,10 @@ var ipcRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
|
58334
58995
|
}),
|
|
58335
58996
|
external_exports.object({
|
|
58336
58997
|
kind: external_exports.literal("fetch-candidates"),
|
|
58337
|
-
envelope: contextEnvelopeSchema
|
|
58998
|
+
envelope: contextEnvelopeSchema,
|
|
58999
|
+
// The client wants one `candidate` message per parsed row before the final
|
|
59000
|
+
// `candidates` message (Phase 09.1). Old daemons strip the key and reply once.
|
|
59001
|
+
stream: external_exports.literal(true).optional()
|
|
58338
59002
|
})
|
|
58339
59003
|
]);
|
|
58340
59004
|
var ipcResponseSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -58342,7 +59006,9 @@ var ipcResponseSchema = external_exports.discriminatedUnion("kind", [
|
|
|
58342
59006
|
kind: external_exports.literal("pong"),
|
|
58343
59007
|
fetchCandidates: external_exports.literal(true).optional(),
|
|
58344
59008
|
// Daemon accepts envelopes carrying the `installed-tools` chunk (Phase 9).
|
|
58345
|
-
installedTools: external_exports.literal(true).optional()
|
|
59009
|
+
installedTools: external_exports.literal(true).optional(),
|
|
59010
|
+
// Daemon streams `candidate` messages to a `stream: true` request (Phase 09.1).
|
|
59011
|
+
streamCandidates: external_exports.literal(true).optional()
|
|
58346
59012
|
}),
|
|
58347
59013
|
external_exports.object({
|
|
58348
59014
|
kind: external_exports.literal("session-ready"),
|
|
@@ -58355,6 +59021,12 @@ var ipcResponseSchema = external_exports.discriminatedUnion("kind", [
|
|
|
58355
59021
|
external_exports.object({
|
|
58356
59022
|
kind: external_exports.literal("fetch-accepted")
|
|
58357
59023
|
}),
|
|
59024
|
+
// One streamed row, sent only when the request carried `stream: true`.
|
|
59025
|
+
external_exports.object({
|
|
59026
|
+
kind: external_exports.literal("candidate"),
|
|
59027
|
+
candidate: commandCandidateSchema
|
|
59028
|
+
}),
|
|
59029
|
+
// The done signal and the authoritative final list, for every request.
|
|
58358
59030
|
external_exports.object({
|
|
58359
59031
|
kind: external_exports.literal("candidates"),
|
|
58360
59032
|
candidates: candidateListSchema
|
|
@@ -58447,7 +59119,7 @@ function pingDaemonFetchCap(socketPath2) {
|
|
|
58447
59119
|
done("unknown");
|
|
58448
59120
|
return;
|
|
58449
59121
|
}
|
|
58450
|
-
const current2 = response.data.fetchCandidates === true && response.data.installedTools === true;
|
|
59122
|
+
const current2 = response.data.fetchCandidates === true && response.data.installedTools === true && response.data.streamCandidates === true;
|
|
58451
59123
|
done(current2 ? "yes" : "no");
|
|
58452
59124
|
});
|
|
58453
59125
|
client.on("error", () => {
|
|
@@ -58568,12 +59240,19 @@ var IpcTransportError = class extends Error {
|
|
|
58568
59240
|
this.name = "IpcTransportError";
|
|
58569
59241
|
}
|
|
58570
59242
|
};
|
|
58571
|
-
function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
59243
|
+
function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS, options = {}) {
|
|
59244
|
+
const { signal } = options;
|
|
59245
|
+
if (signal?.aborted) {
|
|
59246
|
+
return Promise.reject(abortError());
|
|
59247
|
+
}
|
|
58572
59248
|
return new Promise((resolve3, reject) => {
|
|
58573
59249
|
const client = net2.createConnection(socketPath2);
|
|
58574
59250
|
let buf = "";
|
|
58575
59251
|
let settled = false;
|
|
58576
59252
|
let accepted = false;
|
|
59253
|
+
const onAbort = () => {
|
|
59254
|
+
finish(abortError());
|
|
59255
|
+
};
|
|
58577
59256
|
const finish = (error43, candidates) => {
|
|
58578
59257
|
if (settled) {
|
|
58579
59258
|
return;
|
|
@@ -58581,6 +59260,7 @@ function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS)
|
|
|
58581
59260
|
settled = true;
|
|
58582
59261
|
clearTimeout(ackTimer);
|
|
58583
59262
|
clearTimeout(timer);
|
|
59263
|
+
signal?.removeEventListener("abort", onAbort);
|
|
58584
59264
|
client.destroy();
|
|
58585
59265
|
if (error43) {
|
|
58586
59266
|
reject(error43);
|
|
@@ -58596,6 +59276,7 @@ function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS)
|
|
|
58596
59276
|
const timer = setTimeout(() => {
|
|
58597
59277
|
finish(new IpcTransportError("daemon fetch timed out"));
|
|
58598
59278
|
}, timeoutMs);
|
|
59279
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
58599
59280
|
const handleResponse = (parsed) => {
|
|
58600
59281
|
const response = ipcResponseSchema.safeParse(parsed);
|
|
58601
59282
|
if (!response.success) {
|
|
@@ -58607,6 +59288,12 @@ function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS)
|
|
|
58607
59288
|
clearTimeout(ackTimer);
|
|
58608
59289
|
return;
|
|
58609
59290
|
}
|
|
59291
|
+
if (response.data.kind === "candidate") {
|
|
59292
|
+
accepted = true;
|
|
59293
|
+
clearTimeout(ackTimer);
|
|
59294
|
+
options.onCandidate?.(response.data.candidate);
|
|
59295
|
+
return;
|
|
59296
|
+
}
|
|
58610
59297
|
if (response.data.kind === "candidates") {
|
|
58611
59298
|
accepted = true;
|
|
58612
59299
|
finish(void 0, response.data.candidates);
|
|
@@ -58620,11 +59307,12 @@ function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS)
|
|
|
58620
59307
|
finish(new IpcTransportError("unexpected daemon fetch response"));
|
|
58621
59308
|
};
|
|
58622
59309
|
client.on("connect", () => {
|
|
58623
|
-
client.write(`${JSON.stringify({ kind: "fetch-candidates", envelope })}
|
|
59310
|
+
client.write(`${JSON.stringify({ kind: "fetch-candidates", envelope, stream: true })}
|
|
58624
59311
|
`);
|
|
58625
59312
|
});
|
|
59313
|
+
client.setEncoding("utf8");
|
|
58626
59314
|
client.on("data", (chunk) => {
|
|
58627
|
-
buf += chunk
|
|
59315
|
+
buf += String(chunk);
|
|
58628
59316
|
let nl = buf.indexOf("\n");
|
|
58629
59317
|
while (nl !== -1) {
|
|
58630
59318
|
const line = buf.slice(0, nl).trim();
|
|
@@ -58649,19 +59337,36 @@ function requestDaemonFetch(socketPath2, envelope, timeoutMs = FETCH_TIMEOUT_MS)
|
|
|
58649
59337
|
client.on("error", (error43) => {
|
|
58650
59338
|
finish(new IpcTransportError(error43.message));
|
|
58651
59339
|
});
|
|
59340
|
+
client.on("close", () => {
|
|
59341
|
+
finish(new IpcTransportError("daemon connection closed"));
|
|
59342
|
+
});
|
|
58652
59343
|
});
|
|
58653
59344
|
}
|
|
58654
|
-
async function fetchCandidatesForUi(socketPath2, envelope) {
|
|
59345
|
+
async function fetchCandidatesForUi(socketPath2, envelope, options = {}) {
|
|
59346
|
+
let forwarded = 0;
|
|
59347
|
+
const daemonOptions = {
|
|
59348
|
+
signal: options.signal,
|
|
59349
|
+
onCandidate: (candidate) => {
|
|
59350
|
+
forwarded++;
|
|
59351
|
+
options.onCandidate?.(candidate);
|
|
59352
|
+
}
|
|
59353
|
+
};
|
|
58655
59354
|
try {
|
|
58656
|
-
const candidates = await requestDaemonFetch(socketPath2, envelope);
|
|
59355
|
+
const candidates = await requestDaemonFetch(socketPath2, envelope, void 0, daemonOptions);
|
|
58657
59356
|
void appendDebugLog("client", "candidates from daemon", { count: candidates.length });
|
|
58658
59357
|
return candidates;
|
|
58659
59358
|
} catch (error43) {
|
|
58660
59359
|
if (!(error43 instanceof IpcTransportError)) {
|
|
58661
59360
|
throw error43;
|
|
58662
59361
|
}
|
|
59362
|
+
if (options.signal?.aborted) {
|
|
59363
|
+
throw abortError();
|
|
59364
|
+
}
|
|
59365
|
+
if (forwarded > 0) {
|
|
59366
|
+
throw error43;
|
|
59367
|
+
}
|
|
58663
59368
|
void appendDebugLog("client", "daemon fetch fallback", { message: error43.message });
|
|
58664
|
-
return resolveClaudeDefaultAdapter().fetchCandidates(envelope);
|
|
59369
|
+
return resolveClaudeDefaultAdapter().fetchCandidates(envelope, options);
|
|
58665
59370
|
}
|
|
58666
59371
|
}
|
|
58667
59372
|
|
|
@@ -59421,6 +60126,8 @@ function filterCandidates(candidates, query) {
|
|
|
59421
60126
|
);
|
|
59422
60127
|
}
|
|
59423
60128
|
var MODAL_VIEWPORT_LINES = 18;
|
|
60129
|
+
var INCOMPLETE_LIST_NOTE = "list may be incomplete";
|
|
60130
|
+
var LIST_STATUS_ROW_LINES = 1;
|
|
59424
60131
|
var MARK_COLUMNS = 2;
|
|
59425
60132
|
function estimateCandidateSelectLines(input) {
|
|
59426
60133
|
let lines = 0;
|
|
@@ -59451,6 +60158,9 @@ function estimateCandidateSelectLines(input) {
|
|
|
59451
60158
|
lines += 1;
|
|
59452
60159
|
}
|
|
59453
60160
|
}
|
|
60161
|
+
if (input.listState === "arriving" || input.listState === "partial") {
|
|
60162
|
+
lines += LIST_STATUS_ROW_LINES;
|
|
60163
|
+
}
|
|
59454
60164
|
}
|
|
59455
60165
|
const visible = input.candidates !== null && input.error !== true ? filterCandidates(input.candidates, input.query) : [];
|
|
59456
60166
|
const selectedCandidate = visible[input.selectedIndex];
|
|
@@ -59510,30 +60220,42 @@ function CandidateSelect({
|
|
|
59510
60220
|
onSelect,
|
|
59511
60221
|
onCancel,
|
|
59512
60222
|
error: error43,
|
|
59513
|
-
onLayoutLinesChange
|
|
60223
|
+
onLayoutLinesChange,
|
|
60224
|
+
listState = "complete"
|
|
59514
60225
|
}) {
|
|
59515
60226
|
const { isRawModeSupported } = use_stdin_default();
|
|
59516
60227
|
const { stdout } = use_stdout_default();
|
|
59517
60228
|
const modalWidth = Math.min(MODAL_MAX_WIDTH, stdout.columns || MODAL_MAX_WIDTH);
|
|
59518
60229
|
const contentWidth = modalWidth - MODAL_PADDING_X - ROW_GUTTER.length;
|
|
59519
60230
|
const [selectedIndex, setSelectedIndex] = (0, import_react35.useState)(0);
|
|
59520
|
-
const [query, setQuery] = (0, import_react35.useState)(
|
|
60231
|
+
const [query, setQuery] = (0, import_react35.useState)("");
|
|
59521
60232
|
const [installToggled, setInstallToggled] = (0, import_react35.useState)(/* @__PURE__ */ new Set());
|
|
59522
60233
|
const [prevCandidates, setPrevCandidates] = (0, import_react35.useState)(null);
|
|
59523
60234
|
const paneHold = (0, import_react35.useRef)({
|
|
59524
60235
|
candidates: null,
|
|
59525
60236
|
lines: 0
|
|
59526
60237
|
});
|
|
60238
|
+
const pendingInput = (0, import_react35.useRef)(null);
|
|
60239
|
+
const renderToken = {};
|
|
59527
60240
|
if (candidates !== prevCandidates) {
|
|
59528
60241
|
setPrevCandidates(candidates);
|
|
59529
|
-
if (
|
|
59530
|
-
|
|
59531
|
-
|
|
59532
|
-
|
|
59533
|
-
|
|
59534
|
-
|
|
59535
|
-
|
|
59536
|
-
|
|
60242
|
+
if (prevCandidates === null) {
|
|
60243
|
+
if (selectedIndex !== 0) {
|
|
60244
|
+
setSelectedIndex(0);
|
|
60245
|
+
}
|
|
60246
|
+
if (installToggled.size > 0) {
|
|
60247
|
+
setInstallToggled(/* @__PURE__ */ new Set());
|
|
60248
|
+
}
|
|
60249
|
+
} else if (candidates !== null) {
|
|
60250
|
+
const highlighted = filterCandidates(prevCandidates, query)[selectedIndex];
|
|
60251
|
+
const visible = filterCandidates(candidates, query);
|
|
60252
|
+
const highlightedKey = highlighted === void 0 ? void 0 : candidateKey(highlighted);
|
|
60253
|
+
const kept = highlightedKey === void 0 ? -1 : visible.findIndex((candidate) => candidateKey(candidate) === highlightedKey);
|
|
60254
|
+
const current2 = Number.isFinite(selectedIndex) && selectedIndex >= 0 ? selectedIndex : 0;
|
|
60255
|
+
const nextIndex = kept >= 0 ? kept : Math.max(0, Math.min(current2, visible.length - 1));
|
|
60256
|
+
if (nextIndex !== selectedIndex) {
|
|
60257
|
+
setSelectedIndex(nextIndex);
|
|
60258
|
+
}
|
|
59537
60259
|
}
|
|
59538
60260
|
}
|
|
59539
60261
|
(0, import_react35.useEffect)(() => {
|
|
@@ -59541,9 +60263,18 @@ function CandidateSelect({
|
|
|
59541
60263
|
onCancel();
|
|
59542
60264
|
}
|
|
59543
60265
|
}, [isRawModeSupported, onCancel]);
|
|
59544
|
-
(
|
|
59545
|
-
|
|
59546
|
-
|
|
60266
|
+
const applyInput = (live, next) => {
|
|
60267
|
+
pendingInput.current = next;
|
|
60268
|
+
if (next.query !== live.query) {
|
|
60269
|
+
setQuery(next.query);
|
|
60270
|
+
}
|
|
60271
|
+
if (next.selectedIndex !== live.selectedIndex) {
|
|
60272
|
+
setSelectedIndex(next.selectedIndex);
|
|
60273
|
+
}
|
|
60274
|
+
if (next.installToggled !== live.installToggled) {
|
|
60275
|
+
setInstallToggled(next.installToggled);
|
|
60276
|
+
}
|
|
60277
|
+
};
|
|
59547
60278
|
const includeDevFooter = process.env.QQ_DEV_ROOT != null;
|
|
59548
60279
|
(0, import_react35.useEffect)(() => {
|
|
59549
60280
|
if (!onLayoutLinesChange) {
|
|
@@ -59552,6 +60283,9 @@ function CandidateSelect({
|
|
|
59552
60283
|
if (candidates === null && error43 !== true) {
|
|
59553
60284
|
return;
|
|
59554
60285
|
}
|
|
60286
|
+
if (listState === "arriving" && error43 !== true) {
|
|
60287
|
+
return;
|
|
60288
|
+
}
|
|
59555
60289
|
const layout = {
|
|
59556
60290
|
candidates,
|
|
59557
60291
|
initialQuery,
|
|
@@ -59560,7 +60294,8 @@ function CandidateSelect({
|
|
|
59560
60294
|
error: error43,
|
|
59561
60295
|
contentWidth,
|
|
59562
60296
|
includeDevFooter,
|
|
59563
|
-
installToggled
|
|
60297
|
+
installToggled,
|
|
60298
|
+
listState
|
|
59564
60299
|
};
|
|
59565
60300
|
const previousHeld = paneHold.current.candidates === candidates ? paneHold.current.lines : 0;
|
|
59566
60301
|
const lines = heldPaneLines(previousHeld, layout);
|
|
@@ -59575,57 +60310,63 @@ function CandidateSelect({
|
|
|
59575
60310
|
contentWidth,
|
|
59576
60311
|
includeDevFooter,
|
|
59577
60312
|
installToggled,
|
|
60313
|
+
listState,
|
|
59578
60314
|
onLayoutLinesChange
|
|
59579
60315
|
]);
|
|
59580
60316
|
use_input_default(
|
|
59581
60317
|
(input, key) => {
|
|
60318
|
+
const live = pendingInput.current?.render === renderToken ? pendingInput.current : { render: renderToken, query, selectedIndex, installToggled };
|
|
59582
60319
|
if (key.escape) {
|
|
59583
60320
|
onCancel();
|
|
59584
60321
|
return;
|
|
59585
60322
|
}
|
|
59586
60323
|
if (key.backspace) {
|
|
59587
|
-
|
|
60324
|
+
if (live.query === "") {
|
|
60325
|
+
return;
|
|
60326
|
+
}
|
|
60327
|
+
applyInput(live, { ...live, query: live.query.slice(0, -1), selectedIndex: 0 });
|
|
59588
60328
|
return;
|
|
59589
60329
|
}
|
|
59590
60330
|
if (key.tab) {
|
|
59591
60331
|
if (!candidates) return;
|
|
59592
|
-
const visible = filterCandidates(candidates, query);
|
|
59593
|
-
const selected = visible[selectedIndex] ?? visible[0];
|
|
60332
|
+
const visible = filterCandidates(candidates, live.query);
|
|
60333
|
+
const selected = visible[live.selectedIndex] ?? visible[0];
|
|
59594
60334
|
if (selected?.install?.form == null) return;
|
|
59595
60335
|
const id = candidateKey(selected);
|
|
59596
|
-
|
|
59597
|
-
|
|
59598
|
-
|
|
59599
|
-
|
|
59600
|
-
|
|
59601
|
-
|
|
59602
|
-
|
|
59603
|
-
return next;
|
|
59604
|
-
});
|
|
60336
|
+
const toggled = new Set(live.installToggled);
|
|
60337
|
+
if (toggled.has(id)) {
|
|
60338
|
+
toggled.delete(id);
|
|
60339
|
+
} else {
|
|
60340
|
+
toggled.add(id);
|
|
60341
|
+
}
|
|
60342
|
+
applyInput(live, { ...live, installToggled: toggled });
|
|
59605
60343
|
return;
|
|
59606
60344
|
}
|
|
59607
60345
|
if (key.return && candidates) {
|
|
59608
|
-
const visible = filterCandidates(candidates, query);
|
|
60346
|
+
const visible = filterCandidates(candidates, live.query);
|
|
59609
60347
|
if (visible.length === 0) return;
|
|
59610
|
-
const selected = visible[selectedIndex] ?? visible[0];
|
|
59611
|
-
const command = displayedCommand(selected, installToggled);
|
|
60348
|
+
const selected = visible[live.selectedIndex] ?? visible[0];
|
|
60349
|
+
const command = displayedCommand(selected, live.installToggled);
|
|
59612
60350
|
const original = normalizeCommand2(selected.command);
|
|
59613
60351
|
const explanation = selected.explanation || `see man ${original.split(" ")[0]}`;
|
|
59614
60352
|
onSelect(command, explanation);
|
|
59615
60353
|
return;
|
|
59616
60354
|
}
|
|
59617
60355
|
if (input && !key.ctrl && !key.meta) {
|
|
59618
|
-
|
|
60356
|
+
applyInput(live, { ...live, query: live.query + input, selectedIndex: 0 });
|
|
59619
60357
|
return;
|
|
59620
60358
|
}
|
|
59621
60359
|
if (key.upArrow && candidates) {
|
|
59622
|
-
const visible = filterCandidates(candidates, query);
|
|
59623
|
-
|
|
60360
|
+
const visible = filterCandidates(candidates, live.query);
|
|
60361
|
+
if (visible.length === 0) return;
|
|
60362
|
+
const index = live.selectedIndex;
|
|
60363
|
+
applyInput(live, { ...live, selectedIndex: index <= 0 ? visible.length - 1 : index - 1 });
|
|
59624
60364
|
return;
|
|
59625
60365
|
}
|
|
59626
60366
|
if (key.downArrow && candidates) {
|
|
59627
|
-
const visible = filterCandidates(candidates, query);
|
|
59628
|
-
|
|
60367
|
+
const visible = filterCandidates(candidates, live.query);
|
|
60368
|
+
if (visible.length === 0) return;
|
|
60369
|
+
applyInput(live, { ...live, selectedIndex: (live.selectedIndex + 1) % visible.length });
|
|
59629
60370
|
return;
|
|
59630
60371
|
}
|
|
59631
60372
|
},
|
|
@@ -59684,13 +60425,15 @@ function CandidateSelect({
|
|
|
59684
60425
|
initialQuery && initialQuery.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexGrow: 0, height: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, wrap: "truncate", children: `queque \u203A ${initialQuery}` }) }),
|
|
59685
60426
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchInput, { query }),
|
|
59686
60427
|
content,
|
|
60428
|
+
candidates !== null && error43 !== true && listState === "arriving" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoadingSpinner, {}),
|
|
60429
|
+
candidates !== null && error43 !== true && listState === "partial" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: INCOMPLETE_LIST_NOTE }),
|
|
59687
60430
|
showDestructiveWarning && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", children: DESTRUCTIVE_WARNING_TEXT }) }),
|
|
59688
60431
|
legend !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { marginTop: 1, children: [
|
|
59689
60432
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", children: MISSING_TOOL_MARK }),
|
|
59690
60433
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: legend.slice(MISSING_TOOL_MARK.length) })
|
|
59691
60434
|
] }),
|
|
59692
60435
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ControlsLine, {}),
|
|
59693
|
-
process.env.QQ_DEV_ROOT != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", children: `dev v${"0.
|
|
60436
|
+
process.env.QQ_DEV_ROOT != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", children: `dev v${"0.6.0"} ${"3d6ffbb"}` }) })
|
|
59694
60437
|
] });
|
|
59695
60438
|
}
|
|
59696
60439
|
|
|
@@ -60040,13 +60783,17 @@ async function runForegroundClient(args) {
|
|
|
60040
60783
|
await new Promise((resolve3) => {
|
|
60041
60784
|
let unmount;
|
|
60042
60785
|
let resolved = false;
|
|
60043
|
-
const
|
|
60786
|
+
const fetchAbort = new AbortController();
|
|
60787
|
+
const streamed = [];
|
|
60788
|
+
const buildCandidateElement = (candidates, errorState, listState = "complete") => import_react36.default.createElement(CandidateSelect, {
|
|
60044
60789
|
candidates,
|
|
60045
60790
|
initialQuery: request.lbuffer,
|
|
60046
60791
|
error: errorState,
|
|
60792
|
+
listState,
|
|
60047
60793
|
onSelect: async (command, explanation) => {
|
|
60048
60794
|
if (resolved) return;
|
|
60049
60795
|
resolved = true;
|
|
60796
|
+
fetchAbort.abort();
|
|
60050
60797
|
const { lbuffer, rbuffer } = buildShellBuffers(command, explanation);
|
|
60051
60798
|
await writeShellResult(resultFile, {
|
|
60052
60799
|
kind: "replace-buffer",
|
|
@@ -60059,6 +60806,7 @@ async function runForegroundClient(args) {
|
|
|
60059
60806
|
onCancel: async () => {
|
|
60060
60807
|
if (resolved) return;
|
|
60061
60808
|
resolved = true;
|
|
60809
|
+
fetchAbort.abort();
|
|
60062
60810
|
await writeShellResult(resultFile, { kind: "cancel" });
|
|
60063
60811
|
unmount?.();
|
|
60064
60812
|
},
|
|
@@ -60075,6 +60823,7 @@ async function runForegroundClient(args) {
|
|
|
60075
60823
|
}
|
|
60076
60824
|
};
|
|
60077
60825
|
const cleanupOnSignal = () => {
|
|
60826
|
+
fetchAbort.abort();
|
|
60078
60827
|
app.unmount();
|
|
60079
60828
|
clearScrollReserve();
|
|
60080
60829
|
process.exit(0);
|
|
@@ -60088,17 +60837,50 @@ async function runForegroundClient(args) {
|
|
|
60088
60837
|
if (!skipClear) clearScrollReserve();
|
|
60089
60838
|
resolve3();
|
|
60090
60839
|
};
|
|
60091
|
-
|
|
60840
|
+
const onCandidate = (candidate) => {
|
|
60841
|
+
if (resolved) return;
|
|
60842
|
+
streamed.push(candidate);
|
|
60843
|
+
if (streamed.length === 1) {
|
|
60844
|
+
void appendDebugLog("client", "first candidate received");
|
|
60845
|
+
}
|
|
60846
|
+
app.rerender(
|
|
60847
|
+
buildCandidateElement(
|
|
60848
|
+
annotateCandidates([...streamed], toolInventory),
|
|
60849
|
+
void 0,
|
|
60850
|
+
"arriving"
|
|
60851
|
+
)
|
|
60852
|
+
);
|
|
60853
|
+
};
|
|
60854
|
+
fetchCandidatesForUi(socketPath2, envelope, { signal: fetchAbort.signal, onCandidate }).then((candidates) => {
|
|
60855
|
+
if (resolved) return;
|
|
60092
60856
|
const displayCandidates = annotateCandidates(candidates, toolInventory);
|
|
60093
60857
|
void appendDebugLog("client", "candidates received", {
|
|
60094
60858
|
count: candidates.length,
|
|
60859
|
+
streamed: streamed.length,
|
|
60095
60860
|
flagged: displayCandidates.filter((c) => c.missingTools !== void 0).length
|
|
60096
60861
|
});
|
|
60097
|
-
app.rerender(buildCandidateElement(displayCandidates));
|
|
60862
|
+
app.rerender(buildCandidateElement(displayCandidates, void 0, "complete"));
|
|
60098
60863
|
}).catch(async (err) => {
|
|
60864
|
+
if (isAbortError(err)) {
|
|
60865
|
+
void appendDebugLog("client", "fetch aborted");
|
|
60866
|
+
return;
|
|
60867
|
+
}
|
|
60099
60868
|
const message = err instanceof Error ? err.message : String(err);
|
|
60100
60869
|
void appendDebugLog("client", "llm request failed", { message });
|
|
60101
60870
|
if (resolved) return;
|
|
60871
|
+
if (streamed.length > 0) {
|
|
60872
|
+
void appendDebugLog("client", "llm request failed after rows", {
|
|
60873
|
+
count: streamed.length
|
|
60874
|
+
});
|
|
60875
|
+
app.rerender(
|
|
60876
|
+
buildCandidateElement(
|
|
60877
|
+
annotateCandidates([...streamed], toolInventory),
|
|
60878
|
+
void 0,
|
|
60879
|
+
"partial"
|
|
60880
|
+
)
|
|
60881
|
+
);
|
|
60882
|
+
return;
|
|
60883
|
+
}
|
|
60102
60884
|
resolved = true;
|
|
60103
60885
|
takeover?.clearNow();
|
|
60104
60886
|
const errorMsg = formatFifoError(message);
|
|
@@ -60152,6 +60934,8 @@ function startDaemonServer(socketPath2) {
|
|
|
60152
60934
|
const MAX_BUF_BYTES = 256 * 1024;
|
|
60153
60935
|
const server = net3.createServer((socket) => {
|
|
60154
60936
|
let buf = "";
|
|
60937
|
+
const connectionAbort = new AbortController();
|
|
60938
|
+
socket.setEncoding("utf8");
|
|
60155
60939
|
void appendDebugLog("daemon", "socket connected", { socketPath: socketPath2 });
|
|
60156
60940
|
const writeJson = (payload) => {
|
|
60157
60941
|
if (socket.destroyed) {
|
|
@@ -60161,11 +60945,11 @@ function startDaemonServer(socketPath2) {
|
|
|
60161
60945
|
`);
|
|
60162
60946
|
};
|
|
60163
60947
|
socket.on("data", (chunk) => {
|
|
60164
|
-
if (buf
|
|
60948
|
+
if (Buffer.byteLength(buf) + Buffer.byteLength(chunk) > MAX_BUF_BYTES) {
|
|
60165
60949
|
socket.destroy(new Error("IPC message too large"));
|
|
60166
60950
|
return;
|
|
60167
60951
|
}
|
|
60168
|
-
buf += chunk
|
|
60952
|
+
buf += chunk;
|
|
60169
60953
|
let nl = buf.indexOf("\n");
|
|
60170
60954
|
while (nl !== -1) {
|
|
60171
60955
|
const line = buf.slice(0, nl).trim();
|
|
@@ -60187,7 +60971,12 @@ function startDaemonServer(socketPath2) {
|
|
|
60187
60971
|
void appendDebugLog("daemon", "request received", { kind: req.kind });
|
|
60188
60972
|
switch (req.kind) {
|
|
60189
60973
|
case "ping": {
|
|
60190
|
-
writeJson({
|
|
60974
|
+
writeJson({
|
|
60975
|
+
kind: "pong",
|
|
60976
|
+
fetchCandidates: true,
|
|
60977
|
+
installedTools: true,
|
|
60978
|
+
streamCandidates: true
|
|
60979
|
+
});
|
|
60191
60980
|
void appendDebugLog("daemon", "replied pong");
|
|
60192
60981
|
break;
|
|
60193
60982
|
}
|
|
@@ -60204,14 +60993,42 @@ function startDaemonServer(socketPath2) {
|
|
|
60204
60993
|
}
|
|
60205
60994
|
case "fetch-candidates": {
|
|
60206
60995
|
writeJson({ kind: "fetch-accepted" });
|
|
60207
|
-
|
|
60208
|
-
|
|
60209
|
-
|
|
60996
|
+
const envelope = req.envelope;
|
|
60997
|
+
let streamedIndex = 0;
|
|
60998
|
+
const onCandidate = req.stream === true ? (candidate) => {
|
|
60999
|
+
const index = streamedIndex++;
|
|
61000
|
+
if (connectionAbort.signal.aborted) {
|
|
61001
|
+
void appendDebugLog("daemon", "candidate dropped", { index });
|
|
61002
|
+
return;
|
|
61003
|
+
}
|
|
61004
|
+
const row = commandCandidateSchema.safeParse(candidate);
|
|
61005
|
+
if (!row.success) {
|
|
61006
|
+
return;
|
|
61007
|
+
}
|
|
61008
|
+
writeJson({ kind: "candidate", candidate: row.data });
|
|
61009
|
+
void appendDebugLog("daemon", "replied candidate", { index });
|
|
61010
|
+
} : void 0;
|
|
61011
|
+
void Promise.resolve().then(() => {
|
|
61012
|
+
bootstrapBuiltins();
|
|
61013
|
+
const adapter = resolveClaudeDefaultAdapter();
|
|
61014
|
+
return adapter.fetchCandidates(envelope, {
|
|
61015
|
+
signal: connectionAbort.signal,
|
|
61016
|
+
onCandidate
|
|
61017
|
+
});
|
|
61018
|
+
}).then((candidates) => {
|
|
61019
|
+
if (connectionAbort.signal.aborted) {
|
|
61020
|
+
void appendDebugLog("daemon", "fetch aborted");
|
|
61021
|
+
return;
|
|
61022
|
+
}
|
|
60210
61023
|
writeJson({ kind: "candidates", candidates });
|
|
60211
61024
|
void appendDebugLog("daemon", "replied candidates", {
|
|
60212
61025
|
count: candidates.length
|
|
60213
61026
|
});
|
|
60214
61027
|
}).catch((error43) => {
|
|
61028
|
+
if (connectionAbort.signal.aborted) {
|
|
61029
|
+
void appendDebugLog("daemon", "fetch aborted");
|
|
61030
|
+
return;
|
|
61031
|
+
}
|
|
60215
61032
|
const message = error43 instanceof Error ? error43.message : String(error43);
|
|
60216
61033
|
writeJson({ kind: "fetch-error", message });
|
|
60217
61034
|
void appendDebugLog("daemon", "replied fetch-error", { message });
|
|
@@ -60223,6 +61040,9 @@ function startDaemonServer(socketPath2) {
|
|
|
60223
61040
|
});
|
|
60224
61041
|
socket.on("error", () => {
|
|
60225
61042
|
});
|
|
61043
|
+
socket.on("close", () => {
|
|
61044
|
+
connectionAbort.abort();
|
|
61045
|
+
});
|
|
60226
61046
|
});
|
|
60227
61047
|
server.on("error", reject);
|
|
60228
61048
|
server.listen(socketPath2, () => {
|
|
@@ -60233,6 +61053,32 @@ function startDaemonServer(socketPath2) {
|
|
|
60233
61053
|
}
|
|
60234
61054
|
|
|
60235
61055
|
// src/cli/commands/daemon.ts
|
|
61056
|
+
function trackConnections(server) {
|
|
61057
|
+
const connections = /* @__PURE__ */ new Set();
|
|
61058
|
+
server.on("connection", (socket) => {
|
|
61059
|
+
connections.add(socket);
|
|
61060
|
+
socket.once("close", () => {
|
|
61061
|
+
connections.delete(socket);
|
|
61062
|
+
});
|
|
61063
|
+
});
|
|
61064
|
+
return connections;
|
|
61065
|
+
}
|
|
61066
|
+
function shutdownDaemon(server, connections, options = {}) {
|
|
61067
|
+
const exit = options.exit ?? (() => process.exit(0));
|
|
61068
|
+
const graceMs = options.graceMs ?? SIGKILL_GRACE_MS + 100;
|
|
61069
|
+
disablePrewarm();
|
|
61070
|
+
const inFlight = connections.size;
|
|
61071
|
+
server.close(() => {
|
|
61072
|
+
if (inFlight === 0) {
|
|
61073
|
+
exit();
|
|
61074
|
+
return;
|
|
61075
|
+
}
|
|
61076
|
+
setTimeout(exit, graceMs);
|
|
61077
|
+
});
|
|
61078
|
+
for (const socket of connections) {
|
|
61079
|
+
socket.destroy();
|
|
61080
|
+
}
|
|
61081
|
+
}
|
|
60236
61082
|
async function daemonCommand(options) {
|
|
60237
61083
|
const sock = options.socket ?? socketPath();
|
|
60238
61084
|
void appendDebugLog("daemon", "command start", { socket: sock, ensure: options.ensure });
|
|
@@ -60242,12 +61088,12 @@ async function daemonCommand(options) {
|
|
|
60242
61088
|
return;
|
|
60243
61089
|
}
|
|
60244
61090
|
const server = await startDaemonServer(sock);
|
|
61091
|
+
const connections = trackConnections(server);
|
|
60245
61092
|
void appendDebugLog("daemon", "server started", { socket: sock });
|
|
61093
|
+
enablePrewarm();
|
|
60246
61094
|
const shutdown = () => {
|
|
60247
|
-
void appendDebugLog("daemon", "shutdown", { socket: sock });
|
|
60248
|
-
server
|
|
60249
|
-
process.exit(0);
|
|
60250
|
-
});
|
|
61095
|
+
void appendDebugLog("daemon", "shutdown", { socket: sock, inFlight: connections.size });
|
|
61096
|
+
shutdownDaemon(server, connections);
|
|
60251
61097
|
};
|
|
60252
61098
|
process.once("SIGTERM", shutdown);
|
|
60253
61099
|
process.once("SIGINT", shutdown);
|
|
@@ -60336,6 +61182,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
60336
61182
|
const requestFile = requiredOption("--request-file", options.requestFile);
|
|
60337
61183
|
const resultFile = requiredOption("--result-file", options.resultFile);
|
|
60338
61184
|
await clientCommand({ requestFile, resultFile, resultMode: options.resultMode });
|
|
61185
|
+
process.exit(process.exitCode ?? 0);
|
|
60339
61186
|
});
|
|
60340
61187
|
cli.command(
|
|
60341
61188
|
"init <shell>",
|