@linzumi/cli 0.0.105-beta → 0.0.107-beta
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 -1
- package/dist/index.js +156 -27
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -15076,6 +15076,15 @@ import { createServer } from "node:net";
|
|
|
15076
15076
|
import { homedir as homedir8 } from "node:os";
|
|
15077
15077
|
import { join as join10 } from "node:path";
|
|
15078
15078
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
15079
|
+
function pingWebSocketIfSupported(websocket) {
|
|
15080
|
+
const candidate = websocket;
|
|
15081
|
+
if (typeof candidate.ping === "function" && candidate.readyState === WebSocket.OPEN) {
|
|
15082
|
+
try {
|
|
15083
|
+
candidate.ping();
|
|
15084
|
+
} catch {
|
|
15085
|
+
}
|
|
15086
|
+
}
|
|
15087
|
+
}
|
|
15079
15088
|
async function chooseLoopbackPort() {
|
|
15080
15089
|
return new Promise((resolve12, reject) => {
|
|
15081
15090
|
const server = createServer();
|
|
@@ -15363,7 +15372,13 @@ async function connectCodexAppServer(websocketUrl, socketFactory = (url) => new
|
|
|
15363
15372
|
pending.forEach((pendingRequest) => pendingRequest.reject(error));
|
|
15364
15373
|
pending.clear();
|
|
15365
15374
|
};
|
|
15375
|
+
const keepalive = setInterval(
|
|
15376
|
+
() => pingWebSocketIfSupported(websocket),
|
|
15377
|
+
codexAppServerKeepaliveIntervalMs
|
|
15378
|
+
);
|
|
15379
|
+
keepalive.unref?.();
|
|
15366
15380
|
const handleConnectionClosed = (message) => {
|
|
15381
|
+
clearInterval(keepalive);
|
|
15367
15382
|
rejectPending(message);
|
|
15368
15383
|
if (state.closeError !== void 0) {
|
|
15369
15384
|
return;
|
|
@@ -15443,7 +15458,10 @@ async function connectCodexAppServer(websocketUrl, socketFactory = (url) => new
|
|
|
15443
15458
|
}
|
|
15444
15459
|
closeCallbacks.add(callback);
|
|
15445
15460
|
},
|
|
15446
|
-
close: () =>
|
|
15461
|
+
close: () => {
|
|
15462
|
+
clearInterval(keepalive);
|
|
15463
|
+
websocket.close();
|
|
15464
|
+
}
|
|
15447
15465
|
};
|
|
15448
15466
|
}
|
|
15449
15467
|
function parseCodexAppServerFrame(data) {
|
|
@@ -15653,7 +15671,7 @@ function readyzUrlForWebsocket(websocketUrl) {
|
|
|
15653
15671
|
parsed.hash = "";
|
|
15654
15672
|
return parsed.toString();
|
|
15655
15673
|
}
|
|
15656
|
-
var codexAppServerWatchdogPollMs, blockedCodexAppServerEnvKeys, codexDisableAppsConfigArgs;
|
|
15674
|
+
var codexAppServerWatchdogPollMs, codexAppServerKeepaliveIntervalMs, blockedCodexAppServerEnvKeys, codexDisableAppsConfigArgs;
|
|
15657
15675
|
var init_codexAppServer = __esm({
|
|
15658
15676
|
"src/codexAppServer.ts"() {
|
|
15659
15677
|
"use strict";
|
|
@@ -15663,6 +15681,14 @@ var init_codexAppServer = __esm({
|
|
|
15663
15681
|
init_engineChildReaper();
|
|
15664
15682
|
init_engineParentDeathWatchdog();
|
|
15665
15683
|
codexAppServerWatchdogPollMs = 2e3;
|
|
15684
|
+
codexAppServerKeepaliveIntervalMs = (() => {
|
|
15685
|
+
const raw = process.env.LINZUMI_CODEX_WS_KEEPALIVE_MS;
|
|
15686
|
+
if (raw === void 0) {
|
|
15687
|
+
return 2e4;
|
|
15688
|
+
}
|
|
15689
|
+
const parsed = Number.parseInt(raw, 10);
|
|
15690
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 2e4;
|
|
15691
|
+
})();
|
|
15666
15692
|
blockedCodexAppServerEnvKeys = [
|
|
15667
15693
|
"LINZUMI_MCP_ACCESS_TOKEN",
|
|
15668
15694
|
"LINZUMI_MCP_OWNER_USERNAME"
|
|
@@ -19972,7 +19998,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
19972
19998
|
var init_version = __esm({
|
|
19973
19999
|
"src/version.ts"() {
|
|
19974
20000
|
"use strict";
|
|
19975
|
-
linzumiCliVersion = "0.0.
|
|
20001
|
+
linzumiCliVersion = "0.0.107-beta";
|
|
19976
20002
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19977
20003
|
}
|
|
19978
20004
|
});
|
|
@@ -22702,6 +22728,9 @@ var init_authCache = __esm({
|
|
|
22702
22728
|
});
|
|
22703
22729
|
|
|
22704
22730
|
// src/threadCodexWorkerIpc.ts
|
|
22731
|
+
function isThreadCodexWorkerSessionClosedMessage(message) {
|
|
22732
|
+
return isJsonObject(message) && message.type === "linzumi_thread_codex_worker_session_closed";
|
|
22733
|
+
}
|
|
22705
22734
|
function bindThreadCodexWorkerIpc(codex, ipc = process) {
|
|
22706
22735
|
const pendingServerRequests = /* @__PURE__ */ new Map();
|
|
22707
22736
|
const state = { nextServerRequestId: 1, sendDropLogged: false };
|
|
@@ -23654,15 +23683,33 @@ async function runThreadCodexWorker(options) {
|
|
|
23654
23683
|
const log2 = makeRunnerLogger(options);
|
|
23655
23684
|
const started = await startOwnedCodexAppServer(options);
|
|
23656
23685
|
const codex = await connectCodexAppServer(started.url);
|
|
23686
|
+
const kandanThreadId = options.threadProcess.kandanThreadId;
|
|
23657
23687
|
const stop = once(() => {
|
|
23658
23688
|
codex.close();
|
|
23659
23689
|
started.stop();
|
|
23660
23690
|
log2.close();
|
|
23661
23691
|
});
|
|
23662
23692
|
bindThreadCodexWorkerIpc(codex);
|
|
23693
|
+
codex.onClose?.((error) => {
|
|
23694
|
+
log2("runner.thread_codex_worker_session_closed", {
|
|
23695
|
+
kandanThreadId,
|
|
23696
|
+
reason: error.message
|
|
23697
|
+
});
|
|
23698
|
+
if (process.send !== void 0 && process.connected !== false) {
|
|
23699
|
+
try {
|
|
23700
|
+
process.send({
|
|
23701
|
+
type: "linzumi_thread_codex_worker_session_closed",
|
|
23702
|
+
kandanThreadId,
|
|
23703
|
+
reason: error.message
|
|
23704
|
+
});
|
|
23705
|
+
} catch {
|
|
23706
|
+
}
|
|
23707
|
+
}
|
|
23708
|
+
stop();
|
|
23709
|
+
});
|
|
23663
23710
|
process.send({
|
|
23664
23711
|
type: "linzumi_thread_codex_worker_ready",
|
|
23665
|
-
kandanThreadId
|
|
23712
|
+
kandanThreadId,
|
|
23666
23713
|
commanderManagedPorts: commanderManagedPortsForStartedCodex(started)
|
|
23667
23714
|
});
|
|
23668
23715
|
await waitForThreadCodexWorkerStop(started.process);
|
|
@@ -25472,6 +25519,17 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25472
25519
|
(port) => port.port
|
|
25473
25520
|
)
|
|
25474
25521
|
});
|
|
25522
|
+
const evictOnFailedResult = async (result) => {
|
|
25523
|
+
if (result !== void 0 && result.ok !== false) {
|
|
25524
|
+
return result;
|
|
25525
|
+
}
|
|
25526
|
+
const entry = threadRunnerProcesses.get(kandanThreadId);
|
|
25527
|
+
if (entry === startingEntry || entry?.kind === "running") {
|
|
25528
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
25529
|
+
}
|
|
25530
|
+
await (entry?.kind === "running" ? entry.handle.close() : handle.close()).catch(() => void 0);
|
|
25531
|
+
return result;
|
|
25532
|
+
};
|
|
25475
25533
|
switch (control.type) {
|
|
25476
25534
|
case "start_instance": {
|
|
25477
25535
|
const result = await startCodexProviderInstance({
|
|
@@ -25487,27 +25545,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25487
25545
|
setStartupStage: () => void 0,
|
|
25488
25546
|
setStartedCodexThreadId: () => void 0
|
|
25489
25547
|
});
|
|
25490
|
-
return result.controlResponse;
|
|
25548
|
+
return await evictOnFailedResult(result.controlResponse);
|
|
25491
25549
|
}
|
|
25492
25550
|
case "reconnect_thread":
|
|
25493
|
-
return await
|
|
25494
|
-
|
|
25495
|
-
|
|
25496
|
-
|
|
25497
|
-
|
|
25498
|
-
|
|
25499
|
-
|
|
25500
|
-
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25510
|
-
|
|
25551
|
+
return await evictOnFailedResult(
|
|
25552
|
+
await applyControl(
|
|
25553
|
+
threadCodex,
|
|
25554
|
+
kandan,
|
|
25555
|
+
topic,
|
|
25556
|
+
instanceId,
|
|
25557
|
+
options,
|
|
25558
|
+
agentProviders,
|
|
25559
|
+
allowedCwds,
|
|
25560
|
+
remoteCodexSandboxRunner,
|
|
25561
|
+
activeRemoteCodexExecRequests,
|
|
25562
|
+
activeClaudeCodeSessions,
|
|
25563
|
+
pendingClaudeCodeApprovals,
|
|
25564
|
+
ensureClaudeCodeForwardPortSession,
|
|
25565
|
+
disposeClaudeCodeForwardPortSession,
|
|
25566
|
+
control,
|
|
25567
|
+
log2,
|
|
25568
|
+
onStartedThread,
|
|
25569
|
+
void 0
|
|
25570
|
+
)
|
|
25511
25571
|
);
|
|
25512
25572
|
}
|
|
25513
25573
|
} catch (error) {
|
|
@@ -25520,6 +25580,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25520
25580
|
}
|
|
25521
25581
|
};
|
|
25522
25582
|
const spawnOnMissInFlight = /* @__PURE__ */ new Map();
|
|
25583
|
+
const spawnOnMissHandledMessages = /* @__PURE__ */ new Map();
|
|
25523
25584
|
const replaySpawnOnMissMessageOnLiveSession = async (event, record, reconnectControl) => {
|
|
25524
25585
|
try {
|
|
25525
25586
|
log2("runner.spawn_on_miss_replay_on_sibling", {
|
|
@@ -25564,13 +25625,24 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25564
25625
|
}
|
|
25565
25626
|
};
|
|
25566
25627
|
const handleUnroutedThreadMessage = async (event) => {
|
|
25567
|
-
if (!shouldUseThreadProcesses(options)) {
|
|
25568
|
-
return "ignored";
|
|
25569
|
-
}
|
|
25570
25628
|
const kandanThreadId = event.threadId;
|
|
25571
|
-
if (kandanThreadId === void 0) {
|
|
25629
|
+
if (!shouldUseThreadProcesses(options) || kandanThreadId === void 0) {
|
|
25572
25630
|
return "ignored";
|
|
25573
25631
|
}
|
|
25632
|
+
const messageKey = `${kandanThreadId}:${event.seq}`;
|
|
25633
|
+
const inFlightMessage = spawnOnMissHandledMessages.get(messageKey);
|
|
25634
|
+
if (inFlightMessage !== void 0) {
|
|
25635
|
+
return await inFlightMessage;
|
|
25636
|
+
}
|
|
25637
|
+
const run = handleUnroutedThreadMessageOnce(event, kandanThreadId);
|
|
25638
|
+
spawnOnMissHandledMessages.set(messageKey, run);
|
|
25639
|
+
try {
|
|
25640
|
+
return await run;
|
|
25641
|
+
} finally {
|
|
25642
|
+
spawnOnMissHandledMessages.delete(messageKey);
|
|
25643
|
+
}
|
|
25644
|
+
};
|
|
25645
|
+
const handleUnroutedThreadMessageOnce = async (event, kandanThreadId) => {
|
|
25574
25646
|
const existing = threadRunnerProcesses.get(kandanThreadId);
|
|
25575
25647
|
const existingLiveness = existing === void 0 ? void 0 : threadRunnerEntryLiveness(existing);
|
|
25576
25648
|
if (existingLiveness === "live") {
|
|
@@ -25586,6 +25658,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25586
25658
|
...baseControl,
|
|
25587
25659
|
sourceSeq: event.seq,
|
|
25588
25660
|
workDescription: event.body,
|
|
25661
|
+
// Carry the user's attachments on the reconstructed control (Codex P2) so
|
|
25662
|
+
// the FIRST spawn-on-miss message - the one that owns the respawn and runs
|
|
25663
|
+
// its turn inside startThreadRunnerProcess, not via the replay helper -
|
|
25664
|
+
// still resumes with its files/images. Re-serialized to the wire shape the
|
|
25665
|
+
// resume turn parses (parseKandanChatAttachments).
|
|
25666
|
+
...event.attachments.length === 0 ? {} : { attachments: kandanChatAttachmentsToWire(event.attachments) },
|
|
25589
25667
|
...providerBinding?.modelProvider === void 0 ? {} : { modelProvider: providerBinding.modelProvider },
|
|
25590
25668
|
...providerBinding?.llmProxy === void 0 ? {} : { llmProxy: providerBinding.llmProxy }
|
|
25591
25669
|
};
|
|
@@ -25658,6 +25736,23 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25658
25736
|
);
|
|
25659
25737
|
return "spawned";
|
|
25660
25738
|
}
|
|
25739
|
+
if (startResult === void 0 || startResult.ok === false) {
|
|
25740
|
+
spawnSettle("no_session");
|
|
25741
|
+
const startError = startResult !== void 0 && typeof startResult.error === "string" ? startResult.error : "the thread could not be resumed (no resumable codex session)";
|
|
25742
|
+
log2("runner.spawn_on_miss_failed", {
|
|
25743
|
+
kandanThreadId,
|
|
25744
|
+
codex_thread_id: record.codexThreadId,
|
|
25745
|
+
seq: event.seq,
|
|
25746
|
+
message: startError
|
|
25747
|
+
});
|
|
25748
|
+
await publishSpawnOnMissMessageState(
|
|
25749
|
+
reconnectControl,
|
|
25750
|
+
"failed",
|
|
25751
|
+
`could not resume the thread's coding session: ${startError}`,
|
|
25752
|
+
record.codexThreadId
|
|
25753
|
+
);
|
|
25754
|
+
return "spawned";
|
|
25755
|
+
}
|
|
25661
25756
|
spawnSettle("spawned");
|
|
25662
25757
|
return "spawned";
|
|
25663
25758
|
} catch (error) {
|
|
@@ -27717,11 +27812,15 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
27717
27812
|
})
|
|
27718
27813
|
);
|
|
27719
27814
|
const identity = identityFromAccessToken(options.token);
|
|
27815
|
+
const reconnectAttachments = parseKandanChatAttachments(
|
|
27816
|
+
arrayValue(control.attachments) ?? []
|
|
27817
|
+
);
|
|
27720
27818
|
await startedThreadSession.startThreadMessageTurn({
|
|
27721
27819
|
seq: sourceSeq,
|
|
27722
27820
|
body: workDescription,
|
|
27723
27821
|
actorSlug: identity.actorUsername,
|
|
27724
27822
|
actorUserId: identity.actorUserId,
|
|
27823
|
+
attachments: reconnectAttachments,
|
|
27725
27824
|
boundStartTimeout: true
|
|
27726
27825
|
});
|
|
27727
27826
|
}
|
|
@@ -28396,6 +28495,9 @@ async function startCodexProviderInstance(args) {
|
|
|
28396
28495
|
args.setStartupStage("starting_first_turn");
|
|
28397
28496
|
const rootSeq = integerValue(args.control.rootSeq);
|
|
28398
28497
|
const sourceSeq = integerValue(args.control.sourceSeq) ?? rootSeq;
|
|
28498
|
+
const firstTurnAttachments = parseKandanChatAttachments(
|
|
28499
|
+
arrayValue(args.control.attachments) ?? []
|
|
28500
|
+
);
|
|
28399
28501
|
if (startedThreadSession !== void 0 && sourceSeq !== void 0) {
|
|
28400
28502
|
const identity = identityFromAccessToken(args.options.token);
|
|
28401
28503
|
await startedThreadSession.startThreadMessageTurn({
|
|
@@ -28403,6 +28505,7 @@ async function startCodexProviderInstance(args) {
|
|
|
28403
28505
|
body: workDescription,
|
|
28404
28506
|
actorSlug: identity.actorUsername,
|
|
28405
28507
|
actorUserId: identity.actorUserId,
|
|
28508
|
+
attachments: firstTurnAttachments,
|
|
28406
28509
|
boundStartTimeout: true
|
|
28407
28510
|
});
|
|
28408
28511
|
} else {
|
|
@@ -29761,6 +29864,16 @@ function rehydrationControlSnapshot(args) {
|
|
|
29761
29864
|
...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
|
|
29762
29865
|
};
|
|
29763
29866
|
}
|
|
29867
|
+
function kandanChatAttachmentsToWire(attachments) {
|
|
29868
|
+
return attachments.map((attachment) => ({
|
|
29869
|
+
...attachment.id === void 0 ? {} : { id: attachment.id },
|
|
29870
|
+
...attachment.kind === void 0 ? {} : { kind: attachment.kind },
|
|
29871
|
+
...attachment.fileName === void 0 ? {} : { file_name: attachment.fileName },
|
|
29872
|
+
...attachment.contentType === void 0 ? {} : { content_type: attachment.contentType },
|
|
29873
|
+
...attachment.sizeBytes === void 0 ? {} : { size_bytes: attachment.sizeBytes },
|
|
29874
|
+
...attachment.url === void 0 ? {} : { url: attachment.url }
|
|
29875
|
+
}));
|
|
29876
|
+
}
|
|
29764
29877
|
function rehydrationReconnectControl(record) {
|
|
29765
29878
|
const snapshot = record.control;
|
|
29766
29879
|
const rootSeq = integerValue(snapshot.rootSeq);
|
|
@@ -29972,6 +30085,9 @@ function threadRunnerEntryLiveness(entry) {
|
|
|
29972
30085
|
if (ipcClosed) {
|
|
29973
30086
|
return "dead";
|
|
29974
30087
|
}
|
|
30088
|
+
if (handle.sessionClosed?.() === true) {
|
|
30089
|
+
return "dead";
|
|
30090
|
+
}
|
|
29975
30091
|
return processIsAlive2(handle.processPid) ? "live" : "dead";
|
|
29976
30092
|
}
|
|
29977
30093
|
async function closeThreadRunnerEntry(entry) {
|
|
@@ -30029,6 +30145,18 @@ async function spawnLocalThreadRunnerProcess(options) {
|
|
|
30029
30145
|
});
|
|
30030
30146
|
const exitListeners = /* @__PURE__ */ new Set();
|
|
30031
30147
|
const exitState = { exited: false };
|
|
30148
|
+
const sessionState = { closed: false };
|
|
30149
|
+
child.on("message", (message) => {
|
|
30150
|
+
if (isThreadCodexWorkerSessionClosedMessage(message)) {
|
|
30151
|
+
sessionState.closed = true;
|
|
30152
|
+
writeCliAuditEvent("runner.thread_runner_session_detached", {
|
|
30153
|
+
purpose: "linzumi.thread_runner",
|
|
30154
|
+
kandanThreadId: message.kandanThreadId,
|
|
30155
|
+
reason: message.reason,
|
|
30156
|
+
pid: child.pid
|
|
30157
|
+
});
|
|
30158
|
+
}
|
|
30159
|
+
});
|
|
30032
30160
|
child.once("exit", (code, signal) => {
|
|
30033
30161
|
writeCliAuditEvent("process.exit", {
|
|
30034
30162
|
command: process.execPath,
|
|
@@ -30104,6 +30232,7 @@ async function spawnLocalThreadRunnerProcess(options) {
|
|
|
30104
30232
|
codex: connectThreadCodexWorkerIpc(child),
|
|
30105
30233
|
processPid: child.pid,
|
|
30106
30234
|
commanderManagedPorts: ready2.commanderManagedPorts,
|
|
30235
|
+
sessionClosed: () => sessionState.closed,
|
|
30107
30236
|
onExit: (listener) => {
|
|
30108
30237
|
if (exitState.exited) {
|
|
30109
30238
|
queueMicrotask(listener);
|
package/package.json
CHANGED