@linzumi/cli 0.0.58-beta → 0.0.59-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 +428 -15
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -20847,7 +20847,7 @@ async function resolvePendingPortForwardRequest(args, state, payloadContext, con
|
|
|
20847
20847
|
request.port,
|
|
20848
20848
|
approvedTargetFromRequest(request)
|
|
20849
20849
|
);
|
|
20850
|
-
await publishForwardPortResolvedEvent(args, request, {
|
|
20850
|
+
await publishForwardPortResolvedEvent(args, state, request, {
|
|
20851
20851
|
decision: "deny"
|
|
20852
20852
|
});
|
|
20853
20853
|
await publishMessageStateForPortForwardResult(
|
|
@@ -20874,7 +20874,7 @@ async function resolvePendingPortForwardRequest(args, state, payloadContext, con
|
|
|
20874
20874
|
async function expirePendingPortForwardRequest(args, state, request, reason) {
|
|
20875
20875
|
const failedReason = reason === "listener_exited" ? "port_forward_listener_exited" : "port_forward_listener_changed";
|
|
20876
20876
|
state.pendingPortForwardRequests.delete(request.requestId);
|
|
20877
|
-
await publishForwardPortResolvedEvent(args, request, {
|
|
20877
|
+
await publishForwardPortResolvedEvent(args, state, request, {
|
|
20878
20878
|
decision: "expired",
|
|
20879
20879
|
reason
|
|
20880
20880
|
});
|
|
@@ -20906,7 +20906,7 @@ async function approvePortForwardRequest(args, state, request, options) {
|
|
|
20906
20906
|
processName: processIdentity?.appName ?? null,
|
|
20907
20907
|
processIconKey: processIdentity?.iconKey ?? null
|
|
20908
20908
|
});
|
|
20909
|
-
await publishForwardPortResolvedEvent(args, request, {
|
|
20909
|
+
await publishForwardPortResolvedEvent(args, state, request, {
|
|
20910
20910
|
decision: "approve",
|
|
20911
20911
|
capabilities
|
|
20912
20912
|
});
|
|
@@ -21224,7 +21224,7 @@ async function publishForwardPortRequestedEvent(args, state, request, processIde
|
|
|
21224
21224
|
args.log
|
|
21225
21225
|
);
|
|
21226
21226
|
}
|
|
21227
|
-
async function publishForwardPortResolvedEvent(args, request, result) {
|
|
21227
|
+
async function publishForwardPortResolvedEvent(args, state, request, result) {
|
|
21228
21228
|
await pushOptional(
|
|
21229
21229
|
args.kandan,
|
|
21230
21230
|
args.topic,
|
|
@@ -21235,6 +21235,9 @@ async function publishForwardPortResolvedEvent(args, request, result) {
|
|
|
21235
21235
|
port: request.port,
|
|
21236
21236
|
pid: request.pid,
|
|
21237
21237
|
command: request.command,
|
|
21238
|
+
codexThreadId: state.codexThreadId ?? null,
|
|
21239
|
+
kandanThreadId: state.kandanThreadId ?? null,
|
|
21240
|
+
channelSlug: args.options.channelSession.channelSlug ?? null,
|
|
21238
21241
|
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
21239
21242
|
sourceSeq: request.sourceSeq,
|
|
21240
21243
|
decision: result.decision,
|
|
@@ -24718,6 +24721,7 @@ function linzumiMcpServerConfig(options) {
|
|
|
24718
24721
|
name: "linzumi",
|
|
24719
24722
|
command: options.command ?? "linzumi",
|
|
24720
24723
|
args: [
|
|
24724
|
+
...options.argsPrefix ?? [],
|
|
24721
24725
|
"mcp",
|
|
24722
24726
|
"server",
|
|
24723
24727
|
"--api-url",
|
|
@@ -24738,6 +24742,23 @@ function codexMcpConfigArgs(config2) {
|
|
|
24738
24742
|
`mcp_servers.${config2.name}.args=${tomlStringArray(config2.args)}`
|
|
24739
24743
|
];
|
|
24740
24744
|
}
|
|
24745
|
+
function linzumiMcpCommandForProcess(processExecPath, scriptPath) {
|
|
24746
|
+
const trimmedScriptPath = scriptPath?.trim() ?? "";
|
|
24747
|
+
switch (trimmedScriptPath === "") {
|
|
24748
|
+
case true:
|
|
24749
|
+
return { command: "linzumi", argsPrefix: [] };
|
|
24750
|
+
case false:
|
|
24751
|
+
switch (trimmedScriptPath.endsWith(".ts") || trimmedScriptPath.endsWith(".js")) {
|
|
24752
|
+
case true:
|
|
24753
|
+
return {
|
|
24754
|
+
command: processExecPath,
|
|
24755
|
+
argsPrefix: [trimmedScriptPath]
|
|
24756
|
+
};
|
|
24757
|
+
case false:
|
|
24758
|
+
return { command: trimmedScriptPath, argsPrefix: [] };
|
|
24759
|
+
}
|
|
24760
|
+
}
|
|
24761
|
+
}
|
|
24741
24762
|
function claudeCodeMcpConfigJson(config2) {
|
|
24742
24763
|
return `${JSON.stringify(
|
|
24743
24764
|
{
|
|
@@ -28273,7 +28294,7 @@ function realpathOrResolved(pathValue) {
|
|
|
28273
28294
|
}
|
|
28274
28295
|
|
|
28275
28296
|
// src/version.ts
|
|
28276
|
-
var linzumiCliVersion = "0.0.
|
|
28297
|
+
var linzumiCliVersion = "0.0.59-beta";
|
|
28277
28298
|
var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
28278
28299
|
|
|
28279
28300
|
// src/runnerLock.ts
|
|
@@ -28908,7 +28929,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
28908
28929
|
);
|
|
28909
28930
|
cleanup.actions.push(() => kandan.close());
|
|
28910
28931
|
const topic = `local_runner:${options.runnerId}`;
|
|
28911
|
-
const clientId = options.machineId ?? options.runnerId;
|
|
28932
|
+
const clientId = options.clientId ?? options.machineId ?? options.runnerId;
|
|
28912
28933
|
const runnerHost = hostname2();
|
|
28913
28934
|
const joinPayload = () => ({
|
|
28914
28935
|
clientName: "kandan-local-codex-runner",
|
|
@@ -29690,6 +29711,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29690
29711
|
launchTui: options.launchTui,
|
|
29691
29712
|
enablePortForwardWatch: true,
|
|
29692
29713
|
initialForwardPorts: allowedForwardPorts,
|
|
29714
|
+
portForwardWatcher: started?.process.pid === void 0 ? void 0 : { rootPid: started.process.pid },
|
|
29693
29715
|
suppressedForwardPorts,
|
|
29694
29716
|
onForwardPortApproved: (port, attribution) => {
|
|
29695
29717
|
liveForwardPorts.add(port);
|
|
@@ -29742,6 +29764,13 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29742
29764
|
)
|
|
29743
29765
|
);
|
|
29744
29766
|
});
|
|
29767
|
+
const threadRunnerProcesses = /* @__PURE__ */ new Map();
|
|
29768
|
+
cleanup.actions.push(async () => {
|
|
29769
|
+
await Promise.all(
|
|
29770
|
+
Array.from(threadRunnerProcesses.values(), closeThreadRunnerEntry)
|
|
29771
|
+
);
|
|
29772
|
+
threadRunnerProcesses.clear();
|
|
29773
|
+
});
|
|
29745
29774
|
const attachThreadSession = async (control, cwd, codexThreadId) => {
|
|
29746
29775
|
const workspaceSlug = optionalThreadControlField(control, "workspace");
|
|
29747
29776
|
const channelSlug = optionalThreadControlField(control, "channel");
|
|
@@ -29776,6 +29805,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29776
29805
|
launchTui: false,
|
|
29777
29806
|
enablePortForwardWatch: true,
|
|
29778
29807
|
initialForwardPorts: allowedForwardPorts,
|
|
29808
|
+
portForwardWatcher: started?.process.pid === void 0 ? void 0 : { rootPid: started.process.pid },
|
|
29779
29809
|
suppressedForwardPorts,
|
|
29780
29810
|
onForwardPortApproved: (port, attribution) => {
|
|
29781
29811
|
liveForwardPorts.add(port);
|
|
@@ -29810,6 +29840,78 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29810
29840
|
dynamicChannelSessions.set(kandanThreadId, session);
|
|
29811
29841
|
return session;
|
|
29812
29842
|
};
|
|
29843
|
+
const startThreadRunnerProcess = async (control, cwd) => {
|
|
29844
|
+
if (options.threadProcess?.role === "thread") {
|
|
29845
|
+
return void 0;
|
|
29846
|
+
}
|
|
29847
|
+
const workspaceSlug = optionalThreadControlField(control, "workspace");
|
|
29848
|
+
const channelSlug = optionalThreadControlField(control, "channel");
|
|
29849
|
+
const kandanThreadId = optionalThreadControlField(control, "threadId");
|
|
29850
|
+
if (workspaceSlug === void 0 || channelSlug === void 0 || kandanThreadId === void 0) {
|
|
29851
|
+
return void 0;
|
|
29852
|
+
}
|
|
29853
|
+
const existing = threadRunnerProcesses.get(kandanThreadId);
|
|
29854
|
+
if (existing !== void 0) {
|
|
29855
|
+
return {
|
|
29856
|
+
instanceId,
|
|
29857
|
+
controlType: control.type,
|
|
29858
|
+
ok: true,
|
|
29859
|
+
delegated: true,
|
|
29860
|
+
threadProcess: "already_started",
|
|
29861
|
+
kandanThreadId,
|
|
29862
|
+
cwd
|
|
29863
|
+
};
|
|
29864
|
+
}
|
|
29865
|
+
const spawnThreadRunner = options.spawnThreadRunner ?? spawnLocalThreadRunnerProcess;
|
|
29866
|
+
const startingEntry = {
|
|
29867
|
+
kind: "starting",
|
|
29868
|
+
promise: spawnThreadRunner(
|
|
29869
|
+
threadRunnerOptions({
|
|
29870
|
+
options,
|
|
29871
|
+
control,
|
|
29872
|
+
cwd,
|
|
29873
|
+
workspaceSlug,
|
|
29874
|
+
channelSlug,
|
|
29875
|
+
kandanThreadId
|
|
29876
|
+
})
|
|
29877
|
+
)
|
|
29878
|
+
};
|
|
29879
|
+
threadRunnerProcesses.set(kandanThreadId, startingEntry);
|
|
29880
|
+
let handle;
|
|
29881
|
+
try {
|
|
29882
|
+
handle = await startingEntry.promise;
|
|
29883
|
+
} catch (error51) {
|
|
29884
|
+
if (threadRunnerProcesses.get(kandanThreadId) === startingEntry) {
|
|
29885
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
29886
|
+
}
|
|
29887
|
+
throw error51;
|
|
29888
|
+
}
|
|
29889
|
+
const runningEntry = {
|
|
29890
|
+
kind: "running",
|
|
29891
|
+
handle
|
|
29892
|
+
};
|
|
29893
|
+
if (threadRunnerProcesses.get(kandanThreadId) === startingEntry) {
|
|
29894
|
+
threadRunnerProcesses.set(kandanThreadId, runningEntry);
|
|
29895
|
+
}
|
|
29896
|
+
handle.onExit?.(() => {
|
|
29897
|
+
if (threadRunnerProcesses.get(kandanThreadId) === runningEntry) {
|
|
29898
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
29899
|
+
}
|
|
29900
|
+
});
|
|
29901
|
+
log("runner.thread_process_started", {
|
|
29902
|
+
kandanThreadId,
|
|
29903
|
+
cwd
|
|
29904
|
+
});
|
|
29905
|
+
return {
|
|
29906
|
+
instanceId,
|
|
29907
|
+
controlType: control.type,
|
|
29908
|
+
ok: true,
|
|
29909
|
+
delegated: true,
|
|
29910
|
+
threadProcess: "started",
|
|
29911
|
+
kandanThreadId,
|
|
29912
|
+
cwd
|
|
29913
|
+
};
|
|
29914
|
+
};
|
|
29813
29915
|
const heartbeatPayload = () => ({
|
|
29814
29916
|
instanceId,
|
|
29815
29917
|
clientId,
|
|
@@ -29890,6 +29992,15 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29890
29992
|
});
|
|
29891
29993
|
const handleControl = (control) => {
|
|
29892
29994
|
log("kandan.control", { control });
|
|
29995
|
+
if (!threadProcessOwnsControl(options, control)) {
|
|
29996
|
+
log("kandan.control_ignored", {
|
|
29997
|
+
reason: "thread_process_mismatch",
|
|
29998
|
+
controlType: control.type,
|
|
29999
|
+
controlThreadId: controlThreadId(control) ?? null,
|
|
30000
|
+
threadProcessId: options.threadProcess?.kandanThreadId ?? null
|
|
30001
|
+
});
|
|
30002
|
+
return;
|
|
30003
|
+
}
|
|
29893
30004
|
if (control.type === "replace_runner") {
|
|
29894
30005
|
log("runner.replaced", {
|
|
29895
30006
|
runnerId: options.runnerId,
|
|
@@ -30099,7 +30210,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
30099
30210
|
disposeClaudeCodeForwardPortSession,
|
|
30100
30211
|
control,
|
|
30101
30212
|
log,
|
|
30102
|
-
attachThreadSession
|
|
30213
|
+
attachThreadSession,
|
|
30214
|
+
shouldUseThreadProcesses(options) ? startThreadRunnerProcess : void 0
|
|
30103
30215
|
);
|
|
30104
30216
|
}).then((response) => {
|
|
30105
30217
|
return kandan.push(topic, "codex_response", response);
|
|
@@ -30116,6 +30228,12 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
30116
30228
|
};
|
|
30117
30229
|
controlDispatcher.value = handleControl;
|
|
30118
30230
|
pendingControls.splice(0).forEach(handleControl);
|
|
30231
|
+
if (options.threadProcess?.role === "thread") {
|
|
30232
|
+
const initialControl = options.threadProcess.initialControl;
|
|
30233
|
+
if (initialControl !== void 0) {
|
|
30234
|
+
handleControl(initialControl);
|
|
30235
|
+
}
|
|
30236
|
+
}
|
|
30119
30237
|
return { instanceId, codexUrl, close };
|
|
30120
30238
|
}
|
|
30121
30239
|
function controlTargetsInstance(control, instanceId) {
|
|
@@ -30129,6 +30247,60 @@ function controlTargetsInstance(control, instanceId) {
|
|
|
30129
30247
|
function controlInstanceId(control) {
|
|
30130
30248
|
return "instanceId" in control ? control.instanceId : void 0;
|
|
30131
30249
|
}
|
|
30250
|
+
function shouldUseThreadProcesses(options) {
|
|
30251
|
+
if (options.threadProcess?.role === "thread") {
|
|
30252
|
+
return false;
|
|
30253
|
+
}
|
|
30254
|
+
return options.spawnThreadRunner !== void 0 || options.codexUrl === void 0;
|
|
30255
|
+
}
|
|
30256
|
+
function threadProcessOwnsControl(options, control) {
|
|
30257
|
+
const threadProcess = options.threadProcess;
|
|
30258
|
+
const threadId = controlThreadId(control);
|
|
30259
|
+
if (threadProcess?.role !== "thread") {
|
|
30260
|
+
if (shouldUseThreadProcesses(options) && threadId !== void 0 && threadChildOnlyControl(control)) {
|
|
30261
|
+
return false;
|
|
30262
|
+
}
|
|
30263
|
+
return true;
|
|
30264
|
+
}
|
|
30265
|
+
if (threadId !== void 0) {
|
|
30266
|
+
return threadId === threadProcess.kandanThreadId;
|
|
30267
|
+
}
|
|
30268
|
+
if ("instanceId" in control && control.instanceId !== void 0 && control.instanceId !== "") {
|
|
30269
|
+
return true;
|
|
30270
|
+
}
|
|
30271
|
+
switch (control.type) {
|
|
30272
|
+
case "replace_runner":
|
|
30273
|
+
return true;
|
|
30274
|
+
default:
|
|
30275
|
+
return false;
|
|
30276
|
+
}
|
|
30277
|
+
}
|
|
30278
|
+
function threadChildOnlyControl(control) {
|
|
30279
|
+
switch (control.type) {
|
|
30280
|
+
case "forward_http_request":
|
|
30281
|
+
case "forward_tcp_open":
|
|
30282
|
+
case "forward_tcp_send":
|
|
30283
|
+
case "forward_tcp_close":
|
|
30284
|
+
return true;
|
|
30285
|
+
default:
|
|
30286
|
+
return false;
|
|
30287
|
+
}
|
|
30288
|
+
}
|
|
30289
|
+
function controlThreadId(control) {
|
|
30290
|
+
switch (control.type) {
|
|
30291
|
+
case "start_instance":
|
|
30292
|
+
case "reconnect_thread":
|
|
30293
|
+
case "forward_http_request":
|
|
30294
|
+
case "forward_tcp_open":
|
|
30295
|
+
case "forward_tcp_send":
|
|
30296
|
+
case "forward_tcp_close": {
|
|
30297
|
+
const threadId = stringValue(control.threadId)?.trim();
|
|
30298
|
+
return threadId === void 0 || threadId === "" ? void 0 : threadId;
|
|
30299
|
+
}
|
|
30300
|
+
default:
|
|
30301
|
+
return void 0;
|
|
30302
|
+
}
|
|
30303
|
+
}
|
|
30132
30304
|
async function resolveSessionControl(channelSession, dynamicChannelSessions, control) {
|
|
30133
30305
|
const primaryHandled = await (channelSession?.handleControl(control) ?? Promise.resolve(void 0));
|
|
30134
30306
|
if (primaryHandled !== void 0) {
|
|
@@ -30349,7 +30521,7 @@ async function resumeCodexThreadForReconnect(codex, codexThreadId, resumeOverrid
|
|
|
30349
30521
|
);
|
|
30350
30522
|
}
|
|
30351
30523
|
}
|
|
30352
|
-
async function applyControl(codex, kandan, topic, instanceId, options, agentProviders, allowedCwds, activeClaudeCodeSessions, pendingClaudeCodeApprovals, ensureClaudeCodeForwardPortSession, disposeClaudeCodeForwardPortSession, control, log, onStartedThread) {
|
|
30524
|
+
async function applyControl(codex, kandan, topic, instanceId, options, agentProviders, allowedCwds, activeClaudeCodeSessions, pendingClaudeCodeApprovals, ensureClaudeCodeForwardPortSession, disposeClaudeCodeForwardPortSession, control, log, onStartedThread, onThreadProcessStart) {
|
|
30353
30525
|
switch (control.type) {
|
|
30354
30526
|
case "start_instance": {
|
|
30355
30527
|
const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
|
|
@@ -30427,6 +30599,10 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
30427
30599
|
});
|
|
30428
30600
|
}
|
|
30429
30601
|
try {
|
|
30602
|
+
const delegated = await onThreadProcessStart?.(control, cwd.cwd);
|
|
30603
|
+
if (delegated !== void 0) {
|
|
30604
|
+
return delegated;
|
|
30605
|
+
}
|
|
30430
30606
|
if (agentProvider === "claude-code") {
|
|
30431
30607
|
startupStage = "starting_claude_code_session";
|
|
30432
30608
|
const result2 = await startClaudeCodeProviderInstance({
|
|
@@ -30564,6 +30740,10 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
30564
30740
|
const workDescription = normalizedWorkDescription(
|
|
30565
30741
|
control.workDescription
|
|
30566
30742
|
);
|
|
30743
|
+
const delegated = await onThreadProcessStart?.(control, cwd.cwd);
|
|
30744
|
+
if (delegated !== void 0) {
|
|
30745
|
+
return delegated;
|
|
30746
|
+
}
|
|
30567
30747
|
if (agentProvider === "claude-code") {
|
|
30568
30748
|
const activeSession = activeClaudeCodeSessions.get(codexThreadId);
|
|
30569
30749
|
if (activeSession !== void 0 && workDescription !== void 0) {
|
|
@@ -32371,10 +32551,182 @@ function startInstanceRuntimeSettings(options, control) {
|
|
|
32371
32551
|
allowPortForwardingByDefault: control.allowPortForwardingByDefault ?? defaults.allowPortForwardingByDefault
|
|
32372
32552
|
};
|
|
32373
32553
|
}
|
|
32554
|
+
function threadRunnerOptions(args) {
|
|
32555
|
+
const runtimeSettings = startInstanceRuntimeSettings(
|
|
32556
|
+
args.options,
|
|
32557
|
+
args.control
|
|
32558
|
+
);
|
|
32559
|
+
return {
|
|
32560
|
+
...args.options,
|
|
32561
|
+
clientId: `${args.options.clientId ?? args.options.machineId ?? args.options.runnerId}:thread:${args.kandanThreadId}`,
|
|
32562
|
+
machineId: void 0,
|
|
32563
|
+
runnerLockConfigPath: void 0,
|
|
32564
|
+
workspaceSlug: args.workspaceSlug,
|
|
32565
|
+
cwd: args.cwd,
|
|
32566
|
+
codexUrl: void 0,
|
|
32567
|
+
launchTui: false,
|
|
32568
|
+
allowedCwds: Array.from(/* @__PURE__ */ new Set([args.cwd, ...args.options.allowedCwds])),
|
|
32569
|
+
codeServerBin: args.options.editorRuntime?.mode === "server_managed" ? void 0 : args.options.codeServerBin,
|
|
32570
|
+
editorRuntime: void 0,
|
|
32571
|
+
threadProcess: {
|
|
32572
|
+
role: "thread",
|
|
32573
|
+
kandanThreadId: args.kandanThreadId,
|
|
32574
|
+
initialControl: args.control
|
|
32575
|
+
},
|
|
32576
|
+
spawnThreadRunner: void 0,
|
|
32577
|
+
runtimeDefaults: {
|
|
32578
|
+
model: runtimeSettings.model,
|
|
32579
|
+
reasoningEffort: runtimeSettings.reasoningEffort,
|
|
32580
|
+
sandbox: runtimeSettings.sandbox,
|
|
32581
|
+
approvalPolicy: runtimeSettings.approvalPolicy,
|
|
32582
|
+
allowPortForwardingByDefault: runtimeSettings.allowPortForwardingByDefault
|
|
32583
|
+
},
|
|
32584
|
+
channelSession: void 0
|
|
32585
|
+
};
|
|
32586
|
+
}
|
|
32587
|
+
async function closeThreadRunnerEntry(entry) {
|
|
32588
|
+
switch (entry.kind) {
|
|
32589
|
+
case "running":
|
|
32590
|
+
await entry.handle.close();
|
|
32591
|
+
return;
|
|
32592
|
+
case "starting":
|
|
32593
|
+
try {
|
|
32594
|
+
const handle = await entry.promise;
|
|
32595
|
+
await handle.close();
|
|
32596
|
+
} catch (_error) {
|
|
32597
|
+
return;
|
|
32598
|
+
}
|
|
32599
|
+
return;
|
|
32600
|
+
}
|
|
32601
|
+
}
|
|
32602
|
+
async function spawnLocalThreadRunnerProcess(options) {
|
|
32603
|
+
const scriptPath = process.argv[1];
|
|
32604
|
+
if (scriptPath === void 0 || scriptPath.trim() === "") {
|
|
32605
|
+
throw new Error(
|
|
32606
|
+
"cannot fork thread runner without current CLI script path"
|
|
32607
|
+
);
|
|
32608
|
+
}
|
|
32609
|
+
const args = ["run", ...threadRunnerCliArgs(options)];
|
|
32610
|
+
const env = {
|
|
32611
|
+
...process.env,
|
|
32612
|
+
LINZUMI_THREAD_RUNNER_ROLE: "thread",
|
|
32613
|
+
LINZUMI_THREAD_RUNNER_TOKEN: options.token,
|
|
32614
|
+
LINZUMI_THREAD_RUNNER_CLIENT_ID: options.clientId ?? `${options.runnerId}:thread`,
|
|
32615
|
+
LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID: options.threadProcess?.kandanThreadId ?? "",
|
|
32616
|
+
...options.threadProcess?.initialControl === void 0 ? {} : {
|
|
32617
|
+
LINZUMI_THREAD_RUNNER_INITIAL_CONTROL: JSON.stringify(
|
|
32618
|
+
options.threadProcess.initialControl
|
|
32619
|
+
)
|
|
32620
|
+
}
|
|
32621
|
+
};
|
|
32622
|
+
writeCliAuditEvent("process.spawn", {
|
|
32623
|
+
command: process.execPath,
|
|
32624
|
+
args: [scriptPath, ...redactedThreadRunnerCliArgs(args)],
|
|
32625
|
+
cwd: options.cwd,
|
|
32626
|
+
purpose: "linzumi.thread_runner"
|
|
32627
|
+
});
|
|
32628
|
+
const child = spawn6(process.execPath, [scriptPath, ...args], {
|
|
32629
|
+
cwd: options.cwd,
|
|
32630
|
+
env,
|
|
32631
|
+
stdio: "inherit"
|
|
32632
|
+
});
|
|
32633
|
+
writeCliAuditEvent("process.spawned", {
|
|
32634
|
+
command: process.execPath,
|
|
32635
|
+
args: [scriptPath, ...redactedThreadRunnerCliArgs(args)],
|
|
32636
|
+
cwd: options.cwd,
|
|
32637
|
+
pid: child.pid,
|
|
32638
|
+
purpose: "linzumi.thread_runner"
|
|
32639
|
+
});
|
|
32640
|
+
const exitListeners = /* @__PURE__ */ new Set();
|
|
32641
|
+
const exitState = { exited: false };
|
|
32642
|
+
child.once("exit", (code, signal) => {
|
|
32643
|
+
writeCliAuditEvent("process.exit", {
|
|
32644
|
+
command: process.execPath,
|
|
32645
|
+
args: [scriptPath, ...redactedThreadRunnerCliArgs(args)],
|
|
32646
|
+
cwd: options.cwd,
|
|
32647
|
+
pid: child.pid,
|
|
32648
|
+
code,
|
|
32649
|
+
signal,
|
|
32650
|
+
purpose: "linzumi.thread_runner"
|
|
32651
|
+
});
|
|
32652
|
+
exitState.exited = true;
|
|
32653
|
+
for (const listener of exitListeners) {
|
|
32654
|
+
listener();
|
|
32655
|
+
}
|
|
32656
|
+
exitListeners.clear();
|
|
32657
|
+
});
|
|
32658
|
+
return {
|
|
32659
|
+
kandanThreadId: options.threadProcess?.kandanThreadId ?? "",
|
|
32660
|
+
onExit: (listener) => {
|
|
32661
|
+
if (exitState.exited) {
|
|
32662
|
+
queueMicrotask(listener);
|
|
32663
|
+
return;
|
|
32664
|
+
}
|
|
32665
|
+
exitListeners.add(listener);
|
|
32666
|
+
},
|
|
32667
|
+
close: () => new Promise((resolveClose) => {
|
|
32668
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
32669
|
+
resolveClose();
|
|
32670
|
+
return;
|
|
32671
|
+
}
|
|
32672
|
+
child.once("exit", () => resolveClose());
|
|
32673
|
+
child.kill("SIGINT");
|
|
32674
|
+
})
|
|
32675
|
+
};
|
|
32676
|
+
}
|
|
32677
|
+
function threadRunnerCliArgs(options) {
|
|
32678
|
+
return [
|
|
32679
|
+
"--api-url",
|
|
32680
|
+
options.kandanUrl,
|
|
32681
|
+
"--cwd",
|
|
32682
|
+
options.cwd,
|
|
32683
|
+
"--codex-bin",
|
|
32684
|
+
options.codexBin,
|
|
32685
|
+
...optionalCliValue("--workspace", options.workspaceSlug),
|
|
32686
|
+
...optionalCliValue("--log-file", options.logFile),
|
|
32687
|
+
...options.fast === true ? ["--fast"] : [],
|
|
32688
|
+
...optionalCliValue("--allowed-cwd", options.allowedCwds.join(",")),
|
|
32689
|
+
...optionalCliValue(
|
|
32690
|
+
"--forward-port",
|
|
32691
|
+
(options.allowedForwardPorts ?? []).join(",")
|
|
32692
|
+
),
|
|
32693
|
+
...optionalCliValue("--code-server-bin", options.codeServerBin),
|
|
32694
|
+
...optionalCliValue("--model", options.runtimeDefaults?.model),
|
|
32695
|
+
...optionalCliValue(
|
|
32696
|
+
"--reasoning-effort",
|
|
32697
|
+
options.runtimeDefaults?.reasoningEffort
|
|
32698
|
+
),
|
|
32699
|
+
...optionalCliValue("--sandbox", options.runtimeDefaults?.sandbox),
|
|
32700
|
+
...optionalCliValue(
|
|
32701
|
+
"--approval-policy",
|
|
32702
|
+
options.runtimeDefaults?.approvalPolicy
|
|
32703
|
+
),
|
|
32704
|
+
...options.runtimeDefaults?.allowPortForwardingByDefault === true ? ["--allow-port-forwarding-by-default"] : []
|
|
32705
|
+
];
|
|
32706
|
+
}
|
|
32707
|
+
function redactedThreadRunnerCliArgs(args) {
|
|
32708
|
+
const redacted = [];
|
|
32709
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
32710
|
+
const arg = args[index];
|
|
32711
|
+
if (arg === "--token") {
|
|
32712
|
+
redacted.push(arg, "<redacted>");
|
|
32713
|
+
index += 1;
|
|
32714
|
+
continue;
|
|
32715
|
+
}
|
|
32716
|
+
if (arg !== void 0) {
|
|
32717
|
+
redacted.push(arg);
|
|
32718
|
+
}
|
|
32719
|
+
}
|
|
32720
|
+
return redacted;
|
|
32721
|
+
}
|
|
32722
|
+
function optionalCliValue(flag, value) {
|
|
32723
|
+
return value === void 0 || value === "" ? [] : [flag, value];
|
|
32724
|
+
}
|
|
32374
32725
|
async function startOwnedCodexAppServer(options) {
|
|
32375
32726
|
ensureCodexProjectTrusted(options.cwd);
|
|
32376
32727
|
const defaults = runnerRuntimeDefaults(options);
|
|
32377
32728
|
const mcpAuth = writeEphemeralMcpAuthFile(options);
|
|
32729
|
+
const mcpCommand = currentLinzumiCommand();
|
|
32378
32730
|
try {
|
|
32379
32731
|
const started = await startCodexAppServer(options.codexBin, options.cwd, {
|
|
32380
32732
|
model: defaults.model,
|
|
@@ -32382,7 +32734,8 @@ async function startOwnedCodexAppServer(options) {
|
|
|
32382
32734
|
fast: options.fast,
|
|
32383
32735
|
mcpServers: [
|
|
32384
32736
|
linzumiMcpServerConfig({
|
|
32385
|
-
command:
|
|
32737
|
+
command: mcpCommand.command,
|
|
32738
|
+
argsPrefix: mcpCommand.argsPrefix,
|
|
32386
32739
|
kandanUrl: options.kandanUrl,
|
|
32387
32740
|
authFilePath: mcpAuth.path,
|
|
32388
32741
|
ownerUsername: mcpOwnerUsername(options)
|
|
@@ -32435,8 +32788,7 @@ function once(action) {
|
|
|
32435
32788
|
};
|
|
32436
32789
|
}
|
|
32437
32790
|
function currentLinzumiCommand() {
|
|
32438
|
-
|
|
32439
|
-
return scriptPath === void 0 || scriptPath.trim() === "" ? "linzumi" : scriptPath;
|
|
32791
|
+
return linzumiMcpCommandForProcess(process.execPath, process.argv[1]);
|
|
32440
32792
|
}
|
|
32441
32793
|
function runnerWorkspaceSlug(options) {
|
|
32442
32794
|
return options.channelSession?.workspaceSlug ?? options.workspaceSlug;
|
|
@@ -62781,18 +63133,24 @@ async function main(args) {
|
|
|
62781
63133
|
return;
|
|
62782
63134
|
case "agentRunner": {
|
|
62783
63135
|
const options = await parseAgentRunnerArgs(parsed.args);
|
|
62784
|
-
await runLocalCodexRunner(
|
|
63136
|
+
await runLocalCodexRunner(
|
|
63137
|
+
withLocalMachineId(withThreadRunnerEnv(options))
|
|
63138
|
+
);
|
|
62785
63139
|
return;
|
|
62786
63140
|
}
|
|
62787
63141
|
case "start": {
|
|
62788
63142
|
const options = await parseStartRunnerArgs(parsed.args);
|
|
62789
63143
|
addAllowedCwdForLinzumiUrl(options.cwd, options.kandanUrl);
|
|
62790
|
-
await runLocalCodexRunner(
|
|
63144
|
+
await runLocalCodexRunner(
|
|
63145
|
+
withLocalMachineId(withThreadRunnerEnv(options))
|
|
63146
|
+
);
|
|
62791
63147
|
return;
|
|
62792
63148
|
}
|
|
62793
63149
|
case "run": {
|
|
62794
63150
|
const options = await parseRunnerArgs(parsed.args);
|
|
62795
|
-
await runLocalCodexRunner(
|
|
63151
|
+
await runLocalCodexRunner(
|
|
63152
|
+
withLocalMachineId(withThreadRunnerEnv(options))
|
|
63153
|
+
);
|
|
62796
63154
|
return;
|
|
62797
63155
|
}
|
|
62798
63156
|
}
|
|
@@ -63421,7 +63779,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
63421
63779
|
) : [...localConfiguredAllowedCwds.allowedCwds];
|
|
63422
63780
|
const codexBin = stringValue5(values, "codex-bin") ?? "codex";
|
|
63423
63781
|
const customCodeServerBin = stringValue5(values, "code-server-bin");
|
|
63424
|
-
const explicitToken = stringValue5(values, "token");
|
|
63782
|
+
const explicitToken = stringValue5(values, "token") ?? threadRunnerTokenEnv();
|
|
63425
63783
|
const token = await deps.resolveToken({
|
|
63426
63784
|
kandanUrl,
|
|
63427
63785
|
explicitToken,
|
|
@@ -63657,11 +64015,66 @@ function parseChannelPath(channel) {
|
|
|
63657
64015
|
};
|
|
63658
64016
|
}
|
|
63659
64017
|
function withLocalMachineId(options) {
|
|
64018
|
+
if (options.threadProcess?.role === "thread") {
|
|
64019
|
+
return options;
|
|
64020
|
+
}
|
|
63660
64021
|
return {
|
|
63661
64022
|
...options,
|
|
63662
64023
|
machineId: localConfigScopeKey(options.kandanUrl) === localConfigScopeKey(defaultLinzumiWebSocketUrl) ? ensureLocalMachineId() : ensureLocalMachineIdForLinzumiUrl(options.kandanUrl)
|
|
63663
64024
|
};
|
|
63664
64025
|
}
|
|
64026
|
+
function withThreadRunnerEnv(options) {
|
|
64027
|
+
if (process.env.LINZUMI_THREAD_RUNNER_ROLE !== "thread") {
|
|
64028
|
+
return options;
|
|
64029
|
+
}
|
|
64030
|
+
const kandanThreadId = process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
|
|
64031
|
+
if (kandanThreadId === void 0 || kandanThreadId.trim() === "") {
|
|
64032
|
+
throw new Error(
|
|
64033
|
+
"thread runner is missing LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID"
|
|
64034
|
+
);
|
|
64035
|
+
}
|
|
64036
|
+
return {
|
|
64037
|
+
...options,
|
|
64038
|
+
clientId: process.env.LINZUMI_THREAD_RUNNER_CLIENT_ID,
|
|
64039
|
+
machineId: void 0,
|
|
64040
|
+
runnerLockConfigPath: void 0,
|
|
64041
|
+
codexUrl: void 0,
|
|
64042
|
+
launchTui: false,
|
|
64043
|
+
threadProcess: {
|
|
64044
|
+
role: "thread",
|
|
64045
|
+
kandanThreadId,
|
|
64046
|
+
initialControl: initialThreadControlFromEnv()
|
|
64047
|
+
},
|
|
64048
|
+
channelSession: void 0
|
|
64049
|
+
};
|
|
64050
|
+
}
|
|
64051
|
+
function threadRunnerTokenEnv() {
|
|
64052
|
+
if (process.env.LINZUMI_THREAD_RUNNER_ROLE !== "thread") {
|
|
64053
|
+
return void 0;
|
|
64054
|
+
}
|
|
64055
|
+
const token = process.env.LINZUMI_THREAD_RUNNER_TOKEN;
|
|
64056
|
+
if (token === void 0 || token.trim() === "") {
|
|
64057
|
+
throw new Error("thread runner is missing LINZUMI_THREAD_RUNNER_TOKEN");
|
|
64058
|
+
}
|
|
64059
|
+
return token;
|
|
64060
|
+
}
|
|
64061
|
+
function initialThreadControlFromEnv() {
|
|
64062
|
+
const raw = process.env.LINZUMI_THREAD_RUNNER_INITIAL_CONTROL;
|
|
64063
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
64064
|
+
return void 0;
|
|
64065
|
+
}
|
|
64066
|
+
const parsed = JSON.parse(raw);
|
|
64067
|
+
if (parsed === null || typeof parsed !== "object" || !("type" in parsed)) {
|
|
64068
|
+
throw new Error("thread runner initial control is invalid");
|
|
64069
|
+
}
|
|
64070
|
+
switch (parsed.type) {
|
|
64071
|
+
case "start_instance":
|
|
64072
|
+
case "reconnect_thread":
|
|
64073
|
+
return parsed;
|
|
64074
|
+
default:
|
|
64075
|
+
throw new Error("thread runner initial control type is invalid");
|
|
64076
|
+
}
|
|
64077
|
+
}
|
|
63665
64078
|
function required3(values, key) {
|
|
63666
64079
|
const value = stringValue5(values, key);
|
|
63667
64080
|
if (value === void 0) {
|
package/package.json
CHANGED