@linzumi/cli 0.0.99-beta → 0.0.101-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 +145 -71
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -3361,7 +3361,8 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3361
3361
|
resolveReady: void 0,
|
|
3362
3362
|
rejectReady: void 0,
|
|
3363
3363
|
reconnectTimer: void 0,
|
|
3364
|
-
reconnectAttempts: 0
|
|
3364
|
+
reconnectAttempts: 0,
|
|
3365
|
+
heartbeatTimer: void 0
|
|
3365
3366
|
};
|
|
3366
3367
|
const pushTimeoutMs = options.pushTimeoutMs ?? defaultPushTimeoutMs;
|
|
3367
3368
|
const controlCursorStore = options.controlCursorStore;
|
|
@@ -3751,6 +3752,7 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3751
3752
|
state.connected = false;
|
|
3752
3753
|
const websocket = state.websocket;
|
|
3753
3754
|
state.websocket = void 0;
|
|
3755
|
+
stopHeartbeat();
|
|
3754
3756
|
if (websocket !== void 0 && (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN)) {
|
|
3755
3757
|
try {
|
|
3756
3758
|
websocket.close();
|
|
@@ -3768,6 +3770,29 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3768
3770
|
});
|
|
3769
3771
|
}
|
|
3770
3772
|
};
|
|
3773
|
+
const HEARTBEAT_INTERVAL_MS = 3e4;
|
|
3774
|
+
const stopHeartbeat = () => {
|
|
3775
|
+
if (state.heartbeatTimer !== void 0) {
|
|
3776
|
+
clearInterval(state.heartbeatTimer);
|
|
3777
|
+
state.heartbeatTimer = void 0;
|
|
3778
|
+
}
|
|
3779
|
+
};
|
|
3780
|
+
const startHeartbeat = () => {
|
|
3781
|
+
stopHeartbeat();
|
|
3782
|
+
state.heartbeatTimer = setInterval(() => {
|
|
3783
|
+
const websocket = state.websocket;
|
|
3784
|
+
if (websocket === void 0 || websocket.readyState !== WebSocket.OPEN) {
|
|
3785
|
+
return;
|
|
3786
|
+
}
|
|
3787
|
+
const ref = String(state.nextRef);
|
|
3788
|
+
state.nextRef += 1;
|
|
3789
|
+
try {
|
|
3790
|
+
websocket.send(JSON.stringify([null, ref, "phoenix", "heartbeat", {}]));
|
|
3791
|
+
} catch {
|
|
3792
|
+
}
|
|
3793
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
3794
|
+
state.heartbeatTimer.unref?.();
|
|
3795
|
+
};
|
|
3771
3796
|
const openSocket = async () => {
|
|
3772
3797
|
if (state.closed) {
|
|
3773
3798
|
return;
|
|
@@ -3803,6 +3828,7 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protoc
|
|
|
3803
3828
|
state.connected = true;
|
|
3804
3829
|
state.hasEverConnected = true;
|
|
3805
3830
|
state.reconnectAttempts = 0;
|
|
3831
|
+
startHeartbeat();
|
|
3806
3832
|
markReady();
|
|
3807
3833
|
if (state.connectionGeneration > 1) {
|
|
3808
3834
|
reconnectCallbacks.forEach((callback) => {
|
|
@@ -19803,7 +19829,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
19803
19829
|
var init_version = __esm({
|
|
19804
19830
|
"src/version.ts"() {
|
|
19805
19831
|
"use strict";
|
|
19806
|
-
linzumiCliVersion = "0.0.
|
|
19832
|
+
linzumiCliVersion = "0.0.101-beta";
|
|
19807
19833
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19808
19834
|
}
|
|
19809
19835
|
});
|
|
@@ -22098,6 +22124,37 @@ var init_runnerConsoleReporter = __esm({
|
|
|
22098
22124
|
}
|
|
22099
22125
|
});
|
|
22100
22126
|
|
|
22127
|
+
// src/threadRebuildSerializer.ts
|
|
22128
|
+
function createThreadRebuildSerializer() {
|
|
22129
|
+
const chains = /* @__PURE__ */ new Map();
|
|
22130
|
+
return {
|
|
22131
|
+
async run(key, fn) {
|
|
22132
|
+
const prior = chains.get(key) ?? Promise.resolve();
|
|
22133
|
+
const run = prior.then(fn, fn);
|
|
22134
|
+
const tail = run.then(
|
|
22135
|
+
() => void 0,
|
|
22136
|
+
() => void 0
|
|
22137
|
+
);
|
|
22138
|
+
chains.set(key, tail);
|
|
22139
|
+
try {
|
|
22140
|
+
return await run;
|
|
22141
|
+
} finally {
|
|
22142
|
+
if (chains.get(key) === tail) {
|
|
22143
|
+
chains.delete(key);
|
|
22144
|
+
}
|
|
22145
|
+
}
|
|
22146
|
+
},
|
|
22147
|
+
activeKeyCount() {
|
|
22148
|
+
return chains.size;
|
|
22149
|
+
}
|
|
22150
|
+
};
|
|
22151
|
+
}
|
|
22152
|
+
var init_threadRebuildSerializer = __esm({
|
|
22153
|
+
"src/threadRebuildSerializer.ts"() {
|
|
22154
|
+
"use strict";
|
|
22155
|
+
}
|
|
22156
|
+
});
|
|
22157
|
+
|
|
22101
22158
|
// src/telemetry.ts
|
|
22102
22159
|
import { mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
22103
22160
|
import { dirname as dirname11, basename as basename7, join as join18 } from "node:path";
|
|
@@ -24699,6 +24756,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24699
24756
|
);
|
|
24700
24757
|
threadRunnerProcesses.clear();
|
|
24701
24758
|
});
|
|
24759
|
+
const threadRebuildSerializer = createThreadRebuildSerializer();
|
|
24702
24760
|
const evictStaleDynamicChannelSession = async (codexThreadId, expectClient) => {
|
|
24703
24761
|
const session = dynamicChannelSessions.get(codexThreadId);
|
|
24704
24762
|
const boundClient = dynamicChannelSessionCodexClients.get(codexThreadId);
|
|
@@ -24778,81 +24836,96 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24778
24836
|
);
|
|
24779
24837
|
}
|
|
24780
24838
|
const runtimeSettings = startInstanceRuntimeSettings(options, control);
|
|
24781
|
-
const rebuildOnDeadCodexWorker = shouldUseThreadProcesses(options) ? async (rebuildArgs) =>
|
|
24782
|
-
|
|
24783
|
-
|
|
24784
|
-
|
|
24785
|
-
|
|
24786
|
-
|
|
24787
|
-
|
|
24788
|
-
|
|
24789
|
-
|
|
24790
|
-
|
|
24791
|
-
|
|
24792
|
-
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
|
|
24800
|
-
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
|
|
24808
|
-
|
|
24809
|
-
|
|
24810
|
-
|
|
24811
|
-
|
|
24812
|
-
|
|
24813
|
-
|
|
24814
|
-
|
|
24815
|
-
|
|
24816
|
-
|
|
24817
|
-
|
|
24818
|
-
|
|
24819
|
-
seq: message.seq,
|
|
24820
|
-
body: message.body,
|
|
24821
|
-
actorSlug: message.actorSlug,
|
|
24822
|
-
actorUserId: message.actorUserId,
|
|
24823
|
-
boundStartTimeout: true
|
|
24824
|
-
});
|
|
24825
|
-
};
|
|
24826
|
-
try {
|
|
24827
|
-
if (rebuilt) {
|
|
24828
|
-
await replayOntoLiveSession({ waitForSiblingSpawn: false });
|
|
24829
|
-
} else {
|
|
24830
|
-
const startResult = await startThreadRunnerProcess(
|
|
24831
|
-
reconnectControl,
|
|
24832
|
-
cwd
|
|
24839
|
+
const rebuildOnDeadCodexWorker = shouldUseThreadProcesses(options) ? async (rebuildArgs) => threadRebuildSerializer.run(
|
|
24840
|
+
rebuildArgs.kandanThreadId,
|
|
24841
|
+
async () => {
|
|
24842
|
+
await evictStaleDynamicChannelSession(
|
|
24843
|
+
rebuildArgs.codexThreadId
|
|
24844
|
+
);
|
|
24845
|
+
let rebuilt = false;
|
|
24846
|
+
for (const message of rebuildArgs.pendingMessages) {
|
|
24847
|
+
const reconnectControl = {
|
|
24848
|
+
type: "reconnect_thread",
|
|
24849
|
+
workspace: workspaceSlug,
|
|
24850
|
+
channel: channelSlug,
|
|
24851
|
+
threadId: rebuildArgs.kandanThreadId,
|
|
24852
|
+
codexThreadId: rebuildArgs.codexThreadId,
|
|
24853
|
+
cwd,
|
|
24854
|
+
// Carry the conversation context through the rebuild-replay so
|
|
24855
|
+
// the respawned worker's developer instructions match what the
|
|
24856
|
+
// server would have replayed (parity with the live reconnect
|
|
24857
|
+
// control built above at channelSession.linzumiContext).
|
|
24858
|
+
...control.linzumiContext === void 0 ? {} : { linzumiContext: control.linzumiContext },
|
|
24859
|
+
...integerValue(control.rootSeq) === void 0 ? {} : { rootSeq: integerValue(control.rootSeq) },
|
|
24860
|
+
sourceSeq: message.seq,
|
|
24861
|
+
workDescription: message.body,
|
|
24862
|
+
...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
|
|
24863
|
+
...runtimeSettings.reasoningEffort === void 0 ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
|
|
24864
|
+
...runtimeSettings.sandbox === void 0 ? {} : { sandbox: runtimeSettings.sandbox },
|
|
24865
|
+
...runtimeSettings.approvalPolicy === void 0 ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
|
|
24866
|
+
...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
|
|
24867
|
+
};
|
|
24868
|
+
const replayOntoLiveSession = async (options2) => {
|
|
24869
|
+
const liveSession = await withCodexStartRequestTimeout(
|
|
24870
|
+
"thread/attach",
|
|
24871
|
+
resolveLiveDynamicChannelSession(
|
|
24872
|
+
rebuildArgs.codexThreadId,
|
|
24873
|
+
{
|
|
24874
|
+
waitForSiblingSpawn: options2.waitForSiblingSpawn
|
|
24875
|
+
}
|
|
24876
|
+
)
|
|
24833
24877
|
);
|
|
24834
|
-
if (
|
|
24835
|
-
|
|
24836
|
-
|
|
24837
|
-
|
|
24838
|
-
|
|
24878
|
+
if (liveSession === void 0) {
|
|
24879
|
+
throw new Error(
|
|
24880
|
+
`dead-worker rebuild could not resolve a live session for codex thread ${rebuildArgs.codexThreadId}`
|
|
24881
|
+
);
|
|
24882
|
+
}
|
|
24883
|
+
await liveSession.startThreadMessageTurn({
|
|
24884
|
+
seq: message.seq,
|
|
24885
|
+
body: message.body,
|
|
24886
|
+
actorSlug: message.actorSlug,
|
|
24887
|
+
actorUserId: message.actorUserId,
|
|
24888
|
+
boundStartTimeout: true
|
|
24889
|
+
});
|
|
24890
|
+
};
|
|
24891
|
+
try {
|
|
24892
|
+
if (rebuilt) {
|
|
24893
|
+
await replayOntoLiveSession({
|
|
24894
|
+
waitForSiblingSpawn: false
|
|
24839
24895
|
});
|
|
24840
|
-
|
|
24896
|
+
} else {
|
|
24897
|
+
const startResult = await startThreadRunnerProcess(
|
|
24898
|
+
reconnectControl,
|
|
24899
|
+
cwd
|
|
24900
|
+
);
|
|
24901
|
+
if (threadStartDelegatedToSibling(startResult)) {
|
|
24902
|
+
log2(
|
|
24903
|
+
"runner.thread_dead_worker_rebuild_replay_on_sibling",
|
|
24904
|
+
{
|
|
24905
|
+
codex_thread_id: rebuildArgs.codexThreadId,
|
|
24906
|
+
kandan_thread_id: rebuildArgs.kandanThreadId,
|
|
24907
|
+
seq: message.seq
|
|
24908
|
+
}
|
|
24909
|
+
);
|
|
24910
|
+
await replayOntoLiveSession({
|
|
24911
|
+
waitForSiblingSpawn: true
|
|
24912
|
+
});
|
|
24913
|
+
}
|
|
24841
24914
|
}
|
|
24915
|
+
rebuilt = true;
|
|
24916
|
+
} catch (error) {
|
|
24917
|
+
log2("runner.thread_dead_worker_rebuild_failed", {
|
|
24918
|
+
codex_thread_id: rebuildArgs.codexThreadId,
|
|
24919
|
+
kandan_thread_id: rebuildArgs.kandanThreadId,
|
|
24920
|
+
seq: message.seq,
|
|
24921
|
+
message: error instanceof Error ? error.message : String(error)
|
|
24922
|
+
});
|
|
24923
|
+
return false;
|
|
24842
24924
|
}
|
|
24843
|
-
rebuilt = true;
|
|
24844
|
-
} catch (error) {
|
|
24845
|
-
log2("runner.thread_dead_worker_rebuild_failed", {
|
|
24846
|
-
codex_thread_id: rebuildArgs.codexThreadId,
|
|
24847
|
-
kandan_thread_id: rebuildArgs.kandanThreadId,
|
|
24848
|
-
seq: message.seq,
|
|
24849
|
-
message: error instanceof Error ? error.message : String(error)
|
|
24850
|
-
});
|
|
24851
|
-
return false;
|
|
24852
24925
|
}
|
|
24926
|
+
return rebuilt;
|
|
24853
24927
|
}
|
|
24854
|
-
|
|
24855
|
-
} : void 0;
|
|
24928
|
+
) : void 0;
|
|
24856
24929
|
const session = await attachChannelSession({
|
|
24857
24930
|
kandan,
|
|
24858
24931
|
codex: sessionCodex,
|
|
@@ -31192,6 +31265,7 @@ var init_runner = __esm({
|
|
|
31192
31265
|
init_runnerLockTakeover();
|
|
31193
31266
|
init_runnerConsoleReporter();
|
|
31194
31267
|
init_version();
|
|
31268
|
+
init_threadRebuildSerializer();
|
|
31195
31269
|
init_telemetry();
|
|
31196
31270
|
init_mcpConfig();
|
|
31197
31271
|
init_linzumiApiClient();
|
package/package.json
CHANGED