@linzumi/cli 0.0.65-beta → 0.0.67-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 -5
- package/dist/index.js +399 -177
- package/package.json +1 -2
- package/docs/images/signup-mission-control.png +0 -0
- package/docs/images/signup-terminal.png +0 -0
package/README.md
CHANGED
|
@@ -33,10 +33,6 @@ Paste this into your terminal:
|
|
|
33
33
|
npx -y @linzumi/cli@latest signup
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-

|
|
37
|
-
|
|
38
|
-

|
|
39
|
-
|
|
40
36
|
Linzumi will ask for your email, send you a sign-in code, help you pick
|
|
41
37
|
the repos where Codex may work, start useful first tasks, and open
|
|
42
38
|
Mission Control in your browser with the work already running.
|
|
@@ -62,7 +58,7 @@ Install the CLI or run it with `npx`:
|
|
|
62
58
|
```bash
|
|
63
59
|
npm install -g @linzumi/cli@latest
|
|
64
60
|
npx -y @linzumi/cli@latest signup
|
|
65
|
-
npx -y @linzumi/cli@0.0.
|
|
61
|
+
npx -y @linzumi/cli@0.0.67-beta --version
|
|
66
62
|
linzumi --version
|
|
67
63
|
```
|
|
68
64
|
|
package/dist/index.js
CHANGED
|
@@ -2175,6 +2175,7 @@ function startPortForwardWatcher(options) {
|
|
|
2175
2175
|
const scanListenSockets = options.scanListenSockets ?? readListenSocketRows;
|
|
2176
2176
|
const scanProcessCwds = options.scanProcessCwds ?? readProcessCwdRows;
|
|
2177
2177
|
const nowMs = options.nowMs ?? Date.now;
|
|
2178
|
+
const commanderBoundPids = validPidSet(options.commanderBoundPids ?? []);
|
|
2178
2179
|
const candidateStabilityByPort = /* @__PURE__ */ new Map();
|
|
2179
2180
|
const emittedByPort = /* @__PURE__ */ new Map();
|
|
2180
2181
|
const missingByPort = /* @__PURE__ */ new Map();
|
|
@@ -2187,11 +2188,13 @@ function startPortForwardWatcher(options) {
|
|
|
2187
2188
|
void Promise.resolve().then(async () => {
|
|
2188
2189
|
const descendants = descendantPidSet(scanProcesses(), rootPid);
|
|
2189
2190
|
const sockets = scanListenSockets();
|
|
2190
|
-
const
|
|
2191
|
+
const observedPids = /* @__PURE__ */ new Set([...descendants, ...commanderBoundPids]);
|
|
2192
|
+
const candidatePids = sockets.filter((socket) => observedPids.has(socket.pid)).map((socket) => socket.pid);
|
|
2191
2193
|
const candidates = detectedForwardCandidates(
|
|
2192
2194
|
sockets,
|
|
2193
|
-
|
|
2194
|
-
scanProcessCwds(candidatePids)
|
|
2195
|
+
observedPids,
|
|
2196
|
+
scanProcessCwds(candidatePids),
|
|
2197
|
+
commanderBoundPids
|
|
2195
2198
|
);
|
|
2196
2199
|
const scanTimeMs = nowMs();
|
|
2197
2200
|
const stable = stableForwardCandidates(
|
|
@@ -2289,7 +2292,7 @@ function stableForwardCandidates(previousObservedByPort, candidates, nowMs, debo
|
|
|
2289
2292
|
return { nextObservedByPort, stableCandidates };
|
|
2290
2293
|
}
|
|
2291
2294
|
function sameForwardCandidate(left, right) {
|
|
2292
|
-
return left.port === right.port && left.pid === right.pid;
|
|
2295
|
+
return left.port === right.port && left.pid === right.pid && normalizedPortKind(left.portKind) === normalizedPortKind(right.portKind);
|
|
2293
2296
|
}
|
|
2294
2297
|
function descendantPidSet(rows, rootPid) {
|
|
2295
2298
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
@@ -2309,17 +2312,25 @@ function descendantPidSet(rows, rootPid) {
|
|
|
2309
2312
|
}
|
|
2310
2313
|
return descendants;
|
|
2311
2314
|
}
|
|
2312
|
-
function detectedForwardCandidates(sockets, descendantPids, processCwds = /* @__PURE__ */ new Map()) {
|
|
2315
|
+
function detectedForwardCandidates(sockets, descendantPids, processCwds = /* @__PURE__ */ new Map(), commanderBoundPids = /* @__PURE__ */ new Set()) {
|
|
2313
2316
|
return sockets.filter((socket) => descendantPids.has(socket.pid)).filter((socket) => socket.port > 0 && socket.port < 65536).sort((left, right) => left.port - right.port).map((socket) => {
|
|
2314
2317
|
const cwd = processCwds.get(socket.pid);
|
|
2318
|
+
const portKind = commanderBoundPids.size === 0 ? void 0 : commanderBoundPids.has(socket.pid) ? "commander_bound" : "descendant";
|
|
2315
2319
|
return {
|
|
2316
2320
|
port: socket.port,
|
|
2317
2321
|
pid: socket.pid,
|
|
2318
2322
|
command: socket.command,
|
|
2323
|
+
...portKind === void 0 ? {} : { portKind },
|
|
2319
2324
|
...cwd === void 0 ? {} : { cwd }
|
|
2320
2325
|
};
|
|
2321
2326
|
});
|
|
2322
2327
|
}
|
|
2328
|
+
function validPidSet(pids) {
|
|
2329
|
+
return new Set(pids.filter((pid) => Number.isInteger(pid) && pid > 0));
|
|
2330
|
+
}
|
|
2331
|
+
function normalizedPortKind(portKind) {
|
|
2332
|
+
return portKind ?? "descendant";
|
|
2333
|
+
}
|
|
2323
2334
|
function parseProcessRows(output) {
|
|
2324
2335
|
return output.split("\n").map((line) => line.trim()).filter((line) => line !== "").map((line) => {
|
|
2325
2336
|
const match = line.match(/^(\d+)\s+(\d+)\s+(.*)$/);
|
|
@@ -2453,7 +2464,7 @@ var defaultIntervalMs, defaultDebounceMs;
|
|
|
2453
2464
|
var init_portForwardWatcher = __esm({
|
|
2454
2465
|
"src/portForwardWatcher.ts"() {
|
|
2455
2466
|
"use strict";
|
|
2456
|
-
defaultIntervalMs =
|
|
2467
|
+
defaultIntervalMs = 1e3;
|
|
2457
2468
|
defaultDebounceMs = 750;
|
|
2458
2469
|
}
|
|
2459
2470
|
});
|
|
@@ -2503,6 +2514,7 @@ function pendingRequestFromCandidate(options) {
|
|
|
2503
2514
|
port: options.candidate.port,
|
|
2504
2515
|
pid: options.candidate.pid,
|
|
2505
2516
|
command: options.candidate.command,
|
|
2517
|
+
...options.candidate.portKind === void 0 ? {} : { portKind: options.candidate.portKind },
|
|
2506
2518
|
...options.candidate.cwd === void 0 ? {} : { cwd: options.candidate.cwd },
|
|
2507
2519
|
sourceSeq: options.sourceSeq
|
|
2508
2520
|
};
|
|
@@ -2512,6 +2524,7 @@ function approvedTargetFromRequest(request) {
|
|
|
2512
2524
|
port: request.port,
|
|
2513
2525
|
pid: request.pid,
|
|
2514
2526
|
command: request.command,
|
|
2527
|
+
...request.portKind === void 0 ? {} : { portKind: request.portKind },
|
|
2515
2528
|
...request.cwd === void 0 ? {} : { cwd: request.cwd }
|
|
2516
2529
|
};
|
|
2517
2530
|
}
|
|
@@ -3726,13 +3739,7 @@ async function updateSessionSettings(args, state, payloadContext, control) {
|
|
|
3726
3739
|
fast: state.runtimeSettings.fast ?? null,
|
|
3727
3740
|
allow_port_forwarding_by_default: state.runtimeSettings.allowPortForwardingByDefault ?? null
|
|
3728
3741
|
});
|
|
3729
|
-
|
|
3730
|
-
await approvePendingPortForwardRequestsByDefault(
|
|
3731
|
-
args,
|
|
3732
|
-
state,
|
|
3733
|
-
payloadContext
|
|
3734
|
-
);
|
|
3735
|
-
}
|
|
3742
|
+
await approvePendingPortForwardRequestsByDefault(args, state, payloadContext);
|
|
3736
3743
|
await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
|
|
3737
3744
|
return {
|
|
3738
3745
|
instanceId: args.instanceId,
|
|
@@ -3964,7 +3971,8 @@ async function approvePortForwardRequest(args, state, request, options) {
|
|
|
3964
3971
|
codexThreadId: state.codexThreadId ?? null,
|
|
3965
3972
|
channelSlug: args.options.channelSession.channelSlug ?? null,
|
|
3966
3973
|
processName: processIdentity?.appName ?? null,
|
|
3967
|
-
processIconKey: processIdentity?.iconKey ?? null
|
|
3974
|
+
processIconKey: processIdentity?.iconKey ?? null,
|
|
3975
|
+
linzumiCliManaged: request.portKind === "commander_bound"
|
|
3968
3976
|
});
|
|
3969
3977
|
await publishForwardPortResolvedEvent(args, state, request, {
|
|
3970
3978
|
decision: "approve",
|
|
@@ -4015,7 +4023,7 @@ async function publishPortForwardPrompt(args, state, payloadContext, candidate)
|
|
|
4015
4023
|
dismissedTargets: state.dismissedForwardTargets,
|
|
4016
4024
|
pendingRequests: Array.from(state.pendingPortForwardRequests.values())
|
|
4017
4025
|
});
|
|
4018
|
-
const allowByDefault =
|
|
4026
|
+
const allowByDefault = true;
|
|
4019
4027
|
switch (review.type) {
|
|
4020
4028
|
case "skip":
|
|
4021
4029
|
return;
|
|
@@ -6969,7 +6977,7 @@ function mergeRuntimeSettings(current, update) {
|
|
|
6969
6977
|
),
|
|
6970
6978
|
sandbox,
|
|
6971
6979
|
fast: update.fast ?? current.fast,
|
|
6972
|
-
allowPortForwardingByDefault:
|
|
6980
|
+
allowPortForwardingByDefault: true
|
|
6973
6981
|
};
|
|
6974
6982
|
}
|
|
6975
6983
|
function mergeOptionalApprovalPolicyRuntimeSetting(current, update, sandbox) {
|
|
@@ -7989,7 +7997,9 @@ function linzumiMcpServerConfig(options) {
|
|
|
7989
7997
|
options.kandanUrl,
|
|
7990
7998
|
...options.authFilePath === void 0 ? [] : ["--auth-file", options.authFilePath],
|
|
7991
7999
|
...options.delegationAuthFilePath === void 0 ? [] : ["--delegation-auth-file", options.delegationAuthFilePath],
|
|
7992
|
-
...options.ownerUsername === void 0 ? [] : ["--owner-username", options.ownerUsername]
|
|
8000
|
+
...options.ownerUsername === void 0 ? [] : ["--owner-username", options.ownerUsername],
|
|
8001
|
+
"--mode",
|
|
8002
|
+
options.operatingMode ?? "text"
|
|
7993
8003
|
],
|
|
7994
8004
|
env: {
|
|
7995
8005
|
...options.accessToken === void 0 ? {} : { LINZUMI_MCP_ACCESS_TOKEN: options.accessToken }
|
|
@@ -11685,7 +11695,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
11685
11695
|
var init_version = __esm({
|
|
11686
11696
|
"src/version.ts"() {
|
|
11687
11697
|
"use strict";
|
|
11688
|
-
linzumiCliVersion = "0.0.
|
|
11698
|
+
linzumiCliVersion = "0.0.67-beta";
|
|
11689
11699
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
11690
11700
|
}
|
|
11691
11701
|
});
|
|
@@ -12819,6 +12829,7 @@ import {
|
|
|
12819
12829
|
rmSync as rmSync3,
|
|
12820
12830
|
statSync
|
|
12821
12831
|
} from "node:fs";
|
|
12832
|
+
import { createServer as createServer3 } from "node:http";
|
|
12822
12833
|
import { homedir as homedir8, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
|
|
12823
12834
|
import { dirname as dirname7, join as join11, resolve as resolve6 } from "node:path";
|
|
12824
12835
|
async function runLocalCodexRunner(options) {
|
|
@@ -12876,7 +12887,8 @@ async function runThreadCodexWorker(options) {
|
|
|
12876
12887
|
bindThreadCodexWorkerIpc(codex);
|
|
12877
12888
|
process.send({
|
|
12878
12889
|
type: "linzumi_thread_codex_worker_ready",
|
|
12879
|
-
kandanThreadId: options.threadProcess.kandanThreadId
|
|
12890
|
+
kandanThreadId: options.threadProcess.kandanThreadId,
|
|
12891
|
+
commanderManagedPorts: commanderManagedPortsForStartedCodex(started)
|
|
12880
12892
|
});
|
|
12881
12893
|
await waitForThreadCodexWorkerStop(started.process);
|
|
12882
12894
|
stop();
|
|
@@ -12888,6 +12900,12 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
12888
12900
|
const managedForwardPorts = /* @__PURE__ */ new Set();
|
|
12889
12901
|
const kandanControlPort = explicitUrlPort(options.kandanUrl);
|
|
12890
12902
|
const suppressedForwardPorts = () => suppressedForwardPortsForRunner(kandanControlPort, managedForwardPorts);
|
|
12903
|
+
const commanderBoundE2eHttpProbe = shouldStartCommanderBoundForwardWatcher(
|
|
12904
|
+
options
|
|
12905
|
+
) ? await startCommanderBoundE2eHttpProbe(log) : void 0;
|
|
12906
|
+
if (commanderBoundE2eHttpProbe !== void 0) {
|
|
12907
|
+
cleanup.actions.push(() => commanderBoundE2eHttpProbe.close());
|
|
12908
|
+
}
|
|
12891
12909
|
const forwardPortAttributions = /* @__PURE__ */ new Map();
|
|
12892
12910
|
const setForwardPortAttribution = (port, attribution) => {
|
|
12893
12911
|
forwardPortAttributions.set(port, {
|
|
@@ -12897,12 +12915,30 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
12897
12915
|
processName: attribution.processName ?? null,
|
|
12898
12916
|
processIconKey: attribution.processIconKey ?? null,
|
|
12899
12917
|
localEditor: attribution.localEditor === true,
|
|
12900
|
-
agentProvider: attribution.agentProvider ?? null
|
|
12918
|
+
agentProvider: attribution.agentProvider ?? null,
|
|
12919
|
+
linzumiCliManaged: attribution.linzumiCliManaged === true
|
|
12901
12920
|
});
|
|
12902
12921
|
};
|
|
12903
12922
|
const clearForwardPortAttribution = (port) => {
|
|
12904
12923
|
forwardPortAttributions.delete(port);
|
|
12905
12924
|
};
|
|
12925
|
+
const markCommanderManagedForwardPort = (port, attribution = {}) => {
|
|
12926
|
+
liveForwardPorts.add(port);
|
|
12927
|
+
managedForwardPorts.add(port);
|
|
12928
|
+
setForwardPortAttribution(port, {
|
|
12929
|
+
processName: attribution.processName ?? null,
|
|
12930
|
+
processIconKey: attribution.processIconKey ?? null,
|
|
12931
|
+
agentProvider: attribution.agentProvider ?? null,
|
|
12932
|
+
linzumiCliManaged: true
|
|
12933
|
+
});
|
|
12934
|
+
};
|
|
12935
|
+
const clearCommanderManagedForwardPort = (port) => {
|
|
12936
|
+
managedForwardPorts.delete(port);
|
|
12937
|
+
clearForwardPortAttribution(port);
|
|
12938
|
+
if (!allowedForwardPorts.includes(port)) {
|
|
12939
|
+
liveForwardPorts.delete(port);
|
|
12940
|
+
}
|
|
12941
|
+
};
|
|
12906
12942
|
const buildForwardPortAttributionPayload = () => Array.from(liveForwardPorts).sort((left, right) => left - right).map((port) => {
|
|
12907
12943
|
const attribution = forwardPortAttributions.get(port);
|
|
12908
12944
|
return {
|
|
@@ -12913,7 +12949,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
12913
12949
|
processName: attribution?.processName ?? null,
|
|
12914
12950
|
processIconKey: attribution?.processIconKey ?? null,
|
|
12915
12951
|
localEditor: attribution?.localEditor === true,
|
|
12916
|
-
agentProvider: attribution?.agentProvider ?? null
|
|
12952
|
+
agentProvider: attribution?.agentProvider ?? null,
|
|
12953
|
+
linzumiCliManaged: attribution?.linzumiCliManaged === true || managedForwardPorts.has(port)
|
|
12917
12954
|
};
|
|
12918
12955
|
});
|
|
12919
12956
|
const startupAllowedCwds = normalizeAllowedCwds(options.allowedCwds);
|
|
@@ -13034,6 +13071,16 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13034
13071
|
if (codexUrl === void 0) {
|
|
13035
13072
|
throw new Error("missing codex app-server websocket URL");
|
|
13036
13073
|
}
|
|
13074
|
+
if (started !== void 0) {
|
|
13075
|
+
const appServerPort = explicitUrlPort(codexUrl);
|
|
13076
|
+
if (appServerPort !== void 0) {
|
|
13077
|
+
markCommanderManagedForwardPort(appServerPort, {
|
|
13078
|
+
processName: "Codex app-server",
|
|
13079
|
+
processIconKey: "codex",
|
|
13080
|
+
agentProvider: "codex"
|
|
13081
|
+
});
|
|
13082
|
+
}
|
|
13083
|
+
}
|
|
13037
13084
|
const instanceId = `codex-${randomUUID3()}`;
|
|
13038
13085
|
const publishLocalEditorStatus = (payload) => {
|
|
13039
13086
|
void kandan.push(topic, "local_editor_status", payload).catch((error) => {
|
|
@@ -13049,6 +13096,18 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13049
13096
|
});
|
|
13050
13097
|
});
|
|
13051
13098
|
};
|
|
13099
|
+
const commanderBoundForwardWatcher = shouldStartCommanderBoundForwardWatcher(
|
|
13100
|
+
options
|
|
13101
|
+
) ? startCommanderBoundForwardWatcher({
|
|
13102
|
+
log,
|
|
13103
|
+
instanceId,
|
|
13104
|
+
markCommanderManagedForwardPort,
|
|
13105
|
+
clearCommanderManagedForwardPort,
|
|
13106
|
+
managedForwardPorts,
|
|
13107
|
+
capabilitiesPayload,
|
|
13108
|
+
pushForwardPortEvent: pushEditorPortForwardEvent
|
|
13109
|
+
}) : void 0;
|
|
13110
|
+
cleanup.actions.push(() => commanderBoundForwardWatcher?.close());
|
|
13052
13111
|
const revokeEditorForwardPort = (port, reason) => {
|
|
13053
13112
|
const target = localEditorForwardState.approvedTargets.get(port);
|
|
13054
13113
|
localEditorForwardState.approvedPorts.delete(port);
|
|
@@ -13095,6 +13154,37 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13095
13154
|
drainQueuedEditorPortForwardPrompt();
|
|
13096
13155
|
}
|
|
13097
13156
|
};
|
|
13157
|
+
const approveEditorPortForwardCandidate = (candidate) => {
|
|
13158
|
+
const request = pendingRequestFromCandidate({
|
|
13159
|
+
requestId: `editor-port-forward-auto-${randomUUID3()}`,
|
|
13160
|
+
sourceSeq: 0,
|
|
13161
|
+
candidate
|
|
13162
|
+
});
|
|
13163
|
+
localEditorForwardState.approvedPorts.add(request.port);
|
|
13164
|
+
localEditorForwardState.approvedTargets.set(
|
|
13165
|
+
request.port,
|
|
13166
|
+
approvedTargetFromRequest(request)
|
|
13167
|
+
);
|
|
13168
|
+
liveForwardPorts.add(request.port);
|
|
13169
|
+
const processIdentity = guessCanonicalProcessFromCommand(request.command);
|
|
13170
|
+
setForwardPortAttribution(request.port, {
|
|
13171
|
+
localEditor: true,
|
|
13172
|
+
processName: processIdentity?.appName ?? null,
|
|
13173
|
+
processIconKey: processIdentity?.iconKey ?? null,
|
|
13174
|
+
linzumiCliManaged: request.portKind === "commander_bound"
|
|
13175
|
+
});
|
|
13176
|
+
pushEditorPortForwardEvent("forward_port_resolved", {
|
|
13177
|
+
instanceId,
|
|
13178
|
+
requestId: request.requestId,
|
|
13179
|
+
source: "local_editor",
|
|
13180
|
+
port: request.port,
|
|
13181
|
+
pid: request.pid,
|
|
13182
|
+
command: request.command,
|
|
13183
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
13184
|
+
decision: "approve",
|
|
13185
|
+
capabilities: capabilitiesPayload()
|
|
13186
|
+
});
|
|
13187
|
+
};
|
|
13098
13188
|
const publishEditorPortForwardPrompt = (candidate) => {
|
|
13099
13189
|
const review = reviewPortForwardCandidate({
|
|
13100
13190
|
candidate,
|
|
@@ -13117,15 +13207,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13117
13207
|
);
|
|
13118
13208
|
return;
|
|
13119
13209
|
case "revoke_and_prompt":
|
|
13120
|
-
if (localEditorForwardState.pendingRequests.size > 0) {
|
|
13121
|
-
localEditorForwardState.queuedCandidates.set(
|
|
13122
|
-
candidate.port,
|
|
13123
|
-
candidate
|
|
13124
|
-
);
|
|
13125
|
-
return;
|
|
13126
|
-
}
|
|
13127
13210
|
revokeEditorForwardPort(review.revoked.port, review.reason);
|
|
13128
|
-
|
|
13211
|
+
approveEditorPortForwardCandidate(candidate);
|
|
13212
|
+
return;
|
|
13129
13213
|
case "replace_pending":
|
|
13130
13214
|
localEditorForwardState.pendingRequests.delete(review.expired.port);
|
|
13131
13215
|
pushEditorPortForwardEvent("forward_port_resolved", {
|
|
@@ -13140,44 +13224,12 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13140
13224
|
reason: review.reason,
|
|
13141
13225
|
capabilities: capabilitiesPayload()
|
|
13142
13226
|
});
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
review.candidate.port,
|
|
13146
|
-
review.candidate
|
|
13147
|
-
);
|
|
13148
|
-
return;
|
|
13149
|
-
}
|
|
13150
|
-
break;
|
|
13227
|
+
approveEditorPortForwardCandidate(review.candidate);
|
|
13228
|
+
return;
|
|
13151
13229
|
case "prompt":
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
candidate.port,
|
|
13155
|
-
candidate
|
|
13156
|
-
);
|
|
13157
|
-
return;
|
|
13158
|
-
}
|
|
13159
|
-
break;
|
|
13230
|
+
approveEditorPortForwardCandidate(candidate);
|
|
13231
|
+
return;
|
|
13160
13232
|
}
|
|
13161
|
-
const requestId = `editor-port-forward-${randomUUID3()}`;
|
|
13162
|
-
const request = pendingRequestFromCandidate({
|
|
13163
|
-
requestId,
|
|
13164
|
-
sourceSeq: 0,
|
|
13165
|
-
candidate
|
|
13166
|
-
});
|
|
13167
|
-
localEditorForwardState.pendingRequests.set(request.port, request);
|
|
13168
|
-
const processIdentity = guessCanonicalProcessFromCommand(candidate.command);
|
|
13169
|
-
pushEditorPortForwardEvent("forward_port_requested", {
|
|
13170
|
-
instanceId,
|
|
13171
|
-
requestId,
|
|
13172
|
-
source: "local_editor",
|
|
13173
|
-
port: request.port,
|
|
13174
|
-
pid: request.pid,
|
|
13175
|
-
command: request.command,
|
|
13176
|
-
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
13177
|
-
...processIdentity?.appName === void 0 ? {} : { processName: processIdentity.appName },
|
|
13178
|
-
...processIdentity?.iconKey === void 0 ? {} : { processIconKey: processIdentity.iconKey },
|
|
13179
|
-
capabilities: capabilitiesPayload()
|
|
13180
|
-
});
|
|
13181
13233
|
};
|
|
13182
13234
|
const expireLostEditorPortForwardCandidate = (candidate) => {
|
|
13183
13235
|
const queuedCandidate = localEditorForwardState.queuedCandidates.get(
|
|
@@ -13248,7 +13300,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13248
13300
|
setForwardPortAttribution(request.port, {
|
|
13249
13301
|
localEditor: true,
|
|
13250
13302
|
processName: processIdentity?.appName ?? null,
|
|
13251
|
-
processIconKey: processIdentity?.iconKey ?? null
|
|
13303
|
+
processIconKey: processIdentity?.iconKey ?? null,
|
|
13304
|
+
linzumiCliManaged: request.portKind === "commander_bound"
|
|
13252
13305
|
});
|
|
13253
13306
|
pushEditorPortForwardEvent("forward_port_resolved", {
|
|
13254
13307
|
instanceId,
|
|
@@ -13320,6 +13373,50 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13320
13373
|
capabilities: revocationCapabilities(capabilitiesPayload(), port)
|
|
13321
13374
|
});
|
|
13322
13375
|
};
|
|
13376
|
+
const approveClaudeCodeForwardCandidate = async (session, candidate) => {
|
|
13377
|
+
const sourceSeq = session.currentSourceSeq() ?? session.rootSeq;
|
|
13378
|
+
if (sourceSeq === void 0) {
|
|
13379
|
+
log("port_forward.claude_code_auto_approval_skipped", {
|
|
13380
|
+
reason: "source_seq_missing",
|
|
13381
|
+
port: candidate.port,
|
|
13382
|
+
pid: candidate.pid
|
|
13383
|
+
});
|
|
13384
|
+
return;
|
|
13385
|
+
}
|
|
13386
|
+
const request = pendingRequestFromCandidate({
|
|
13387
|
+
requestId: `claude-port-forward-auto-${randomUUID3()}`,
|
|
13388
|
+
sourceSeq,
|
|
13389
|
+
candidate
|
|
13390
|
+
});
|
|
13391
|
+
session.approvedPorts.add(request.port);
|
|
13392
|
+
session.approvedTargets.set(
|
|
13393
|
+
request.port,
|
|
13394
|
+
approvedTargetFromRequest(request)
|
|
13395
|
+
);
|
|
13396
|
+
liveForwardPorts.add(request.port);
|
|
13397
|
+
const processIdentity = guessCanonicalProcessFromCommand(request.command);
|
|
13398
|
+
setForwardPortAttribution(request.port, {
|
|
13399
|
+
kandanThreadId: session.threadId,
|
|
13400
|
+
codexThreadId: session.sessionId,
|
|
13401
|
+
channelSlug: session.channel,
|
|
13402
|
+
processName: processIdentity?.appName ?? null,
|
|
13403
|
+
processIconKey: processIdentity?.iconKey ?? null,
|
|
13404
|
+
agentProvider: "claude-code",
|
|
13405
|
+
linzumiCliManaged: request.portKind === "commander_bound"
|
|
13406
|
+
});
|
|
13407
|
+
await kandan.push(topic, "forward_port_resolved", {
|
|
13408
|
+
instanceId,
|
|
13409
|
+
requestId: request.requestId,
|
|
13410
|
+
port: request.port,
|
|
13411
|
+
pid: request.pid,
|
|
13412
|
+
command: request.command,
|
|
13413
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
13414
|
+
sourceSeq: request.sourceSeq,
|
|
13415
|
+
decision: "approve",
|
|
13416
|
+
agentProvider: "claude-code",
|
|
13417
|
+
capabilities: capabilitiesPayload()
|
|
13418
|
+
});
|
|
13419
|
+
};
|
|
13323
13420
|
const publishClaudeCodeForwardPortPrompt = async (session, candidate) => {
|
|
13324
13421
|
const review = reviewPortForwardCandidate({
|
|
13325
13422
|
candidate,
|
|
@@ -13337,16 +13434,13 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13337
13434
|
session.approvedTargets.set(review.target.port, review.target);
|
|
13338
13435
|
return;
|
|
13339
13436
|
case "revoke_and_prompt":
|
|
13340
|
-
if (session.pendingRequests.size > 0) {
|
|
13341
|
-
session.queuedCandidates.set(candidate.port, candidate);
|
|
13342
|
-
return;
|
|
13343
|
-
}
|
|
13344
13437
|
await revokeClaudeCodeForwardPort(
|
|
13345
13438
|
session,
|
|
13346
13439
|
review.revoked.port,
|
|
13347
13440
|
review.reason
|
|
13348
13441
|
);
|
|
13349
|
-
|
|
13442
|
+
await approveClaudeCodeForwardCandidate(session, candidate);
|
|
13443
|
+
return;
|
|
13350
13444
|
case "replace_pending":
|
|
13351
13445
|
session.pendingRequests.delete(review.expired.requestId);
|
|
13352
13446
|
await kandan.push(topic, "forward_port_resolved", {
|
|
@@ -13371,83 +13465,12 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13371
13465
|
reason: "port_forward_listener_changed",
|
|
13372
13466
|
sessionId: session.sessionId
|
|
13373
13467
|
});
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
return;
|
|
13377
|
-
}
|
|
13378
|
-
break;
|
|
13468
|
+
await approveClaudeCodeForwardCandidate(session, review.candidate);
|
|
13469
|
+
return;
|
|
13379
13470
|
case "prompt":
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
return;
|
|
13383
|
-
}
|
|
13384
|
-
break;
|
|
13385
|
-
}
|
|
13386
|
-
const sourceSeq = session.currentSourceSeq() ?? session.rootSeq;
|
|
13387
|
-
if (sourceSeq === void 0) {
|
|
13388
|
-
log("port_forward.claude_code_prompt_skipped", {
|
|
13389
|
-
reason: "source_seq_missing",
|
|
13390
|
-
port: candidate.port,
|
|
13391
|
-
pid: candidate.pid
|
|
13392
|
-
});
|
|
13393
|
-
return;
|
|
13471
|
+
await approveClaudeCodeForwardCandidate(session, candidate);
|
|
13472
|
+
return;
|
|
13394
13473
|
}
|
|
13395
|
-
const requestId = `claude-port-forward-${randomUUID3()}`;
|
|
13396
|
-
const request = pendingRequestFromCandidate({
|
|
13397
|
-
requestId,
|
|
13398
|
-
sourceSeq,
|
|
13399
|
-
candidate
|
|
13400
|
-
});
|
|
13401
|
-
session.pendingRequests.set(requestId, request);
|
|
13402
|
-
const processIdentity = guessCanonicalProcessFromCommand(candidate.command);
|
|
13403
|
-
const processName = processIdentity?.appName ?? portForwardPromptLabel(candidate);
|
|
13404
|
-
await kandan.push(topic, "message_state", {
|
|
13405
|
-
workspace: session.workspace,
|
|
13406
|
-
channel: session.channel,
|
|
13407
|
-
thread_id: session.threadId,
|
|
13408
|
-
seq: sourceSeq,
|
|
13409
|
-
status: "processing",
|
|
13410
|
-
reason: "awaiting approval",
|
|
13411
|
-
codex_thread_id: session.sessionId,
|
|
13412
|
-
agent_provider: "claude-code",
|
|
13413
|
-
approval_request_id: requestId,
|
|
13414
|
-
approval_kind: "local_runner_port_forward",
|
|
13415
|
-
approval_summary: `Make ${processName} on port ${candidate.port} accessible on Linzumi?`,
|
|
13416
|
-
approval_reason: portForwardPromptReason(candidate),
|
|
13417
|
-
approval_port: candidate.port,
|
|
13418
|
-
approval_pid: candidate.pid,
|
|
13419
|
-
approval_command: candidate.command,
|
|
13420
|
-
...candidate.cwd === void 0 ? {} : { approval_cwd: candidate.cwd },
|
|
13421
|
-
...processIdentity?.appName === void 0 ? {} : { approval_process_name: processIdentity.appName },
|
|
13422
|
-
...processIdentity?.iconKey === void 0 ? {} : { approval_process_icon_key: processIdentity.iconKey },
|
|
13423
|
-
approval_choices: [
|
|
13424
|
-
{
|
|
13425
|
-
decision: "approve",
|
|
13426
|
-
label: "Enable",
|
|
13427
|
-
description: `Allow Linzumi to proxy Claude Code preview port ${candidate.port}.`
|
|
13428
|
-
},
|
|
13429
|
-
{
|
|
13430
|
-
decision: "deny",
|
|
13431
|
-
label: "Deny",
|
|
13432
|
-
description: `Keep Claude Code preview port ${candidate.port} private.`
|
|
13433
|
-
}
|
|
13434
|
-
]
|
|
13435
|
-
});
|
|
13436
|
-
await kandan.push(topic, "forward_port_requested", {
|
|
13437
|
-
instanceId,
|
|
13438
|
-
requestId,
|
|
13439
|
-
sourceSeq,
|
|
13440
|
-
port: candidate.port,
|
|
13441
|
-
pid: candidate.pid,
|
|
13442
|
-
command: candidate.command,
|
|
13443
|
-
codexThreadId: session.sessionId,
|
|
13444
|
-
kandanThreadId: session.threadId,
|
|
13445
|
-
channelSlug: session.channel,
|
|
13446
|
-
agentProvider: "claude-code",
|
|
13447
|
-
...candidate.cwd === void 0 ? {} : { cwd: candidate.cwd },
|
|
13448
|
-
...processIdentity?.appName === void 0 ? {} : { processName: processIdentity.appName },
|
|
13449
|
-
...processIdentity?.iconKey === void 0 ? {} : { processIconKey: processIdentity.iconKey }
|
|
13450
|
-
});
|
|
13451
13474
|
};
|
|
13452
13475
|
const expireLostClaudeCodeForwardCandidate = (session, candidate) => {
|
|
13453
13476
|
const queuedCandidate = session.queuedCandidates.get(candidate.port);
|
|
@@ -13570,7 +13593,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13570
13593
|
channelSlug: session.channel,
|
|
13571
13594
|
processName: processIdentity?.appName ?? null,
|
|
13572
13595
|
processIconKey: processIdentity?.iconKey ?? null,
|
|
13573
|
-
agentProvider: "claude-code"
|
|
13596
|
+
agentProvider: "claude-code",
|
|
13597
|
+
linzumiCliManaged: request.portKind === "commander_bound"
|
|
13574
13598
|
});
|
|
13575
13599
|
void Promise.all([
|
|
13576
13600
|
kandan.push(topic, "forward_port_resolved", {
|
|
@@ -13885,7 +13909,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13885
13909
|
initialForwardPorts: allowedForwardPorts,
|
|
13886
13910
|
portForwardWatcher: channelSessionPortForwardWatcherOptions({
|
|
13887
13911
|
rootPid: portForwardWatcherRootPid,
|
|
13888
|
-
start: options.portForwardWatcher
|
|
13912
|
+
start: options.portForwardWatcher,
|
|
13913
|
+
commanderBoundPids: args.commanderBoundPids
|
|
13889
13914
|
}),
|
|
13890
13915
|
suppressedForwardPorts,
|
|
13891
13916
|
onForwardPortApproved: (port, attribution) => {
|
|
@@ -13999,6 +14024,13 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
13999
14024
|
}
|
|
14000
14025
|
throw error;
|
|
14001
14026
|
}
|
|
14027
|
+
for (const port of handle.commanderManagedPorts ?? []) {
|
|
14028
|
+
markCommanderManagedForwardPort(port.port, {
|
|
14029
|
+
processName: port.processName,
|
|
14030
|
+
processIconKey: port.processIconKey,
|
|
14031
|
+
agentProvider: port.agentProvider
|
|
14032
|
+
});
|
|
14033
|
+
}
|
|
14002
14034
|
try {
|
|
14003
14035
|
const threadCodex = handle.codex;
|
|
14004
14036
|
const closeThreadCodex = once(() => threadCodex.close());
|
|
@@ -14031,7 +14063,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
14031
14063
|
cwd: startedCwd,
|
|
14032
14064
|
codexThreadId,
|
|
14033
14065
|
sessionCodex: threadCodex,
|
|
14034
|
-
portForwardWatcherRootPid: handle.processPid
|
|
14066
|
+
portForwardWatcherRootPid: handle.processPid,
|
|
14067
|
+
commanderBoundPids: (handle.commanderManagedPorts ?? []).flatMap(
|
|
14068
|
+
(port) => port.pid === void 0 ? [] : [port.pid]
|
|
14069
|
+
)
|
|
14035
14070
|
});
|
|
14036
14071
|
switch (control.type) {
|
|
14037
14072
|
case "start_instance": {
|
|
@@ -16742,18 +16777,138 @@ function startInstanceRuntimeSettings(options, control) {
|
|
|
16742
16777
|
),
|
|
16743
16778
|
sandbox,
|
|
16744
16779
|
fast: control.fast ?? options.fast,
|
|
16745
|
-
allowPortForwardingByDefault:
|
|
16780
|
+
allowPortForwardingByDefault: true
|
|
16746
16781
|
};
|
|
16747
16782
|
}
|
|
16748
16783
|
function channelSessionPortForwardWatcherOptions(args) {
|
|
16749
16784
|
if (args.rootPid === void 0 && args.start === void 0) {
|
|
16750
16785
|
return void 0;
|
|
16751
16786
|
}
|
|
16787
|
+
const commanderBoundPids = [
|
|
16788
|
+
process.pid,
|
|
16789
|
+
...args.rootPid === void 0 ? [] : [args.rootPid],
|
|
16790
|
+
...args.commanderBoundPids ?? []
|
|
16791
|
+
];
|
|
16752
16792
|
return {
|
|
16753
16793
|
...args.rootPid === void 0 ? {} : { rootPid: args.rootPid },
|
|
16794
|
+
...commanderBoundPids.length === 0 ? {} : { commanderBoundPids },
|
|
16754
16795
|
...args.start === void 0 ? {} : { start: args.start }
|
|
16755
16796
|
};
|
|
16756
16797
|
}
|
|
16798
|
+
function shouldStartCommanderBoundForwardWatcher(options) {
|
|
16799
|
+
return options.threadProcess?.role !== "thread" && options.codexUrl === void 0;
|
|
16800
|
+
}
|
|
16801
|
+
async function startCommanderBoundE2eHttpProbe(log) {
|
|
16802
|
+
const rawPort = process.env.LINZUMI_E2E_COMMANDER_BOUND_HTTP_PORT;
|
|
16803
|
+
if (rawPort === void 0 || rawPort.trim() === "") {
|
|
16804
|
+
return void 0;
|
|
16805
|
+
}
|
|
16806
|
+
const port = strictTcpPortFromEnv(
|
|
16807
|
+
rawPort,
|
|
16808
|
+
"LINZUMI_E2E_COMMANDER_BOUND_HTTP_PORT"
|
|
16809
|
+
);
|
|
16810
|
+
const server = createServer3((_request, response) => {
|
|
16811
|
+
response.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
|
|
16812
|
+
response.end("linzumi commander-bound probe\n");
|
|
16813
|
+
});
|
|
16814
|
+
await listenOnLoopback(server, port);
|
|
16815
|
+
log("runner.e2e_commander_bound_http_probe_started", { port });
|
|
16816
|
+
return {
|
|
16817
|
+
port,
|
|
16818
|
+
close: () => closeHttpServer(server)
|
|
16819
|
+
};
|
|
16820
|
+
}
|
|
16821
|
+
function strictTcpPortFromEnv(rawValue, envName) {
|
|
16822
|
+
const trimmed = rawValue.trim();
|
|
16823
|
+
const port = Number.parseInt(trimmed, 10);
|
|
16824
|
+
if (!/^\d+$/u.test(trimmed) || !Number.isInteger(port)) {
|
|
16825
|
+
throw new Error(`${envName} must be an integer TCP port`);
|
|
16826
|
+
}
|
|
16827
|
+
if (port <= 0 || port > 65535) {
|
|
16828
|
+
throw new Error(`${envName} must be between 1 and 65535`);
|
|
16829
|
+
}
|
|
16830
|
+
return port;
|
|
16831
|
+
}
|
|
16832
|
+
function listenOnLoopback(server, port) {
|
|
16833
|
+
return new Promise((resolve11, reject) => {
|
|
16834
|
+
const onError = (error) => {
|
|
16835
|
+
server.off("listening", onListening);
|
|
16836
|
+
reject(error);
|
|
16837
|
+
};
|
|
16838
|
+
const onListening = () => {
|
|
16839
|
+
server.off("error", onError);
|
|
16840
|
+
resolve11();
|
|
16841
|
+
};
|
|
16842
|
+
server.once("error", onError);
|
|
16843
|
+
server.once("listening", onListening);
|
|
16844
|
+
server.listen(port, "127.0.0.1");
|
|
16845
|
+
});
|
|
16846
|
+
}
|
|
16847
|
+
function closeHttpServer(server) {
|
|
16848
|
+
if (!server.listening) {
|
|
16849
|
+
return Promise.resolve();
|
|
16850
|
+
}
|
|
16851
|
+
return new Promise((resolve11, reject) => {
|
|
16852
|
+
server.close((error) => {
|
|
16853
|
+
if (error !== void 0) {
|
|
16854
|
+
reject(error);
|
|
16855
|
+
return;
|
|
16856
|
+
}
|
|
16857
|
+
resolve11();
|
|
16858
|
+
});
|
|
16859
|
+
});
|
|
16860
|
+
}
|
|
16861
|
+
function startCommanderBoundForwardWatcher(args) {
|
|
16862
|
+
return startPortForwardWatcher({
|
|
16863
|
+
rootPid: process.pid,
|
|
16864
|
+
commanderBoundPids: [process.pid],
|
|
16865
|
+
onCandidate: (candidate) => {
|
|
16866
|
+
if (candidate.portKind !== "commander_bound") {
|
|
16867
|
+
return;
|
|
16868
|
+
}
|
|
16869
|
+
if (args.managedForwardPorts.has(candidate.port)) {
|
|
16870
|
+
return;
|
|
16871
|
+
}
|
|
16872
|
+
const processIdentity = guessCanonicalProcessFromCommand(
|
|
16873
|
+
candidate.command
|
|
16874
|
+
);
|
|
16875
|
+
args.markCommanderManagedForwardPort(candidate.port, {
|
|
16876
|
+
processName: processIdentity?.appName ?? portForwardPromptLabel(candidate),
|
|
16877
|
+
processIconKey: processIdentity?.iconKey ?? "terminal"
|
|
16878
|
+
});
|
|
16879
|
+
args.pushForwardPortEvent("forward_port_resolved", {
|
|
16880
|
+
instanceId: args.instanceId,
|
|
16881
|
+
requestId: `commander-bound-${candidate.pid}-${candidate.port}`,
|
|
16882
|
+
port: candidate.port,
|
|
16883
|
+
decision: "approve",
|
|
16884
|
+
capabilities: args.capabilitiesPayload()
|
|
16885
|
+
});
|
|
16886
|
+
},
|
|
16887
|
+
onCandidateLost: (candidate) => {
|
|
16888
|
+
if (candidate.portKind !== "commander_bound") {
|
|
16889
|
+
return;
|
|
16890
|
+
}
|
|
16891
|
+
if (!args.managedForwardPorts.has(candidate.port)) {
|
|
16892
|
+
return;
|
|
16893
|
+
}
|
|
16894
|
+
args.clearCommanderManagedForwardPort(candidate.port);
|
|
16895
|
+
args.pushForwardPortEvent("forward_port_revoked", {
|
|
16896
|
+
instanceId: args.instanceId,
|
|
16897
|
+
requestId: `commander-bound-${candidate.pid}-${candidate.port}`,
|
|
16898
|
+
port: candidate.port,
|
|
16899
|
+
decision: "revoked",
|
|
16900
|
+
reason: "listener_exited",
|
|
16901
|
+
capabilities: revocationCapabilities(
|
|
16902
|
+
args.capabilitiesPayload(),
|
|
16903
|
+
candidate.port
|
|
16904
|
+
)
|
|
16905
|
+
});
|
|
16906
|
+
},
|
|
16907
|
+
onError: (error) => args.log("port_forward.commander_bound_watch_failed", {
|
|
16908
|
+
message: error.message
|
|
16909
|
+
})
|
|
16910
|
+
});
|
|
16911
|
+
}
|
|
16757
16912
|
function threadRunnerOptions(args) {
|
|
16758
16913
|
const runtimeSettings = startInstanceRuntimeSettings(
|
|
16759
16914
|
args.options,
|
|
@@ -16916,6 +17071,7 @@ async function spawnLocalThreadRunnerProcess(options) {
|
|
|
16916
17071
|
kandanThreadId: ready2.kandanThreadId,
|
|
16917
17072
|
codex: connectThreadCodexWorkerIpc(child),
|
|
16918
17073
|
processPid: child.pid,
|
|
17074
|
+
commanderManagedPorts: ready2.commanderManagedPorts,
|
|
16919
17075
|
onExit: (listener) => {
|
|
16920
17076
|
if (exitState.exited) {
|
|
16921
17077
|
queueMicrotask(listener);
|
|
@@ -16982,7 +17138,49 @@ function threadRunnerReadyMessage(message, expectedKandanThreadId) {
|
|
|
16982
17138
|
if (kandanThreadId !== expectedKandanThreadId) {
|
|
16983
17139
|
throw new Error("thread Codex worker reported invalid ready payload");
|
|
16984
17140
|
}
|
|
16985
|
-
return {
|
|
17141
|
+
return {
|
|
17142
|
+
kandanThreadId,
|
|
17143
|
+
commanderManagedPorts: commanderManagedPortRecords(
|
|
17144
|
+
message.commanderManagedPorts
|
|
17145
|
+
)
|
|
17146
|
+
};
|
|
17147
|
+
}
|
|
17148
|
+
function commanderManagedPortRecords(value) {
|
|
17149
|
+
if (!Array.isArray(value)) {
|
|
17150
|
+
return [];
|
|
17151
|
+
}
|
|
17152
|
+
return value.flatMap((entry) => {
|
|
17153
|
+
if (!isJsonObject(entry)) {
|
|
17154
|
+
return [];
|
|
17155
|
+
}
|
|
17156
|
+
const port = integerValue(entry.port);
|
|
17157
|
+
if (port === void 0 || port < 1 || port > 65535) {
|
|
17158
|
+
return [];
|
|
17159
|
+
}
|
|
17160
|
+
const pid = integerValue(entry.pid);
|
|
17161
|
+
const processName = stringValue(entry.processName);
|
|
17162
|
+
const processIconKey = stringValue(entry.processIconKey);
|
|
17163
|
+
const agentProvider = providerKindValue(entry.agentProvider);
|
|
17164
|
+
return [
|
|
17165
|
+
{
|
|
17166
|
+
port,
|
|
17167
|
+
...pid === void 0 ? {} : { pid },
|
|
17168
|
+
...processName === void 0 ? {} : { processName },
|
|
17169
|
+
...processIconKey === void 0 ? {} : { processIconKey },
|
|
17170
|
+
...agentProvider === void 0 ? {} : { agentProvider }
|
|
17171
|
+
}
|
|
17172
|
+
];
|
|
17173
|
+
});
|
|
17174
|
+
}
|
|
17175
|
+
function providerKindValue(value) {
|
|
17176
|
+
switch (stringValue(value)) {
|
|
17177
|
+
case "codex":
|
|
17178
|
+
return "codex";
|
|
17179
|
+
case "claude-code":
|
|
17180
|
+
return "claude-code";
|
|
17181
|
+
default:
|
|
17182
|
+
return void 0;
|
|
17183
|
+
}
|
|
16986
17184
|
}
|
|
16987
17185
|
function waitForThreadCodexWorkerStop(codexProcess) {
|
|
16988
17186
|
return new Promise((resolve11) => {
|
|
@@ -17070,7 +17268,7 @@ function threadRunnerCliArgs(options) {
|
|
|
17070
17268
|
"--approval-policy",
|
|
17071
17269
|
options.runtimeDefaults?.approvalPolicy
|
|
17072
17270
|
),
|
|
17073
|
-
|
|
17271
|
+
"--allow-port-forwarding-by-default"
|
|
17074
17272
|
];
|
|
17075
17273
|
}
|
|
17076
17274
|
function redactedThreadRunnerCliArgs(args) {
|
|
@@ -17107,7 +17305,8 @@ async function startOwnedCodexAppServer(options, args = { linzumiMcp: true }) {
|
|
|
17107
17305
|
argsPrefix: mcpCommand.argsPrefix,
|
|
17108
17306
|
kandanUrl: options.kandanUrl,
|
|
17109
17307
|
authFilePath: mcpAuth.path,
|
|
17110
|
-
ownerUsername: mcpOwnerUsername(options)
|
|
17308
|
+
ownerUsername: mcpOwnerUsername(options),
|
|
17309
|
+
operatingMode: "text"
|
|
17111
17310
|
})
|
|
17112
17311
|
]
|
|
17113
17312
|
});
|
|
@@ -17125,6 +17324,21 @@ async function startOwnedCodexAppServer(options, args = { linzumiMcp: true }) {
|
|
|
17125
17324
|
throw error;
|
|
17126
17325
|
}
|
|
17127
17326
|
}
|
|
17327
|
+
function commanderManagedPortsForStartedCodex(started) {
|
|
17328
|
+
const port = explicitUrlPort(started.url);
|
|
17329
|
+
if (port === void 0) {
|
|
17330
|
+
return [];
|
|
17331
|
+
}
|
|
17332
|
+
return [
|
|
17333
|
+
{
|
|
17334
|
+
port,
|
|
17335
|
+
...started.process.pid === void 0 ? {} : { pid: started.process.pid },
|
|
17336
|
+
processName: "Codex app-server",
|
|
17337
|
+
processIconKey: "codex",
|
|
17338
|
+
agentProvider: "codex"
|
|
17339
|
+
}
|
|
17340
|
+
];
|
|
17341
|
+
}
|
|
17128
17342
|
function mcpOwnerUsername(options) {
|
|
17129
17343
|
return options.channelSession?.listenUser ?? identityFromAccessToken(options.token).actorUsername;
|
|
17130
17344
|
}
|
|
@@ -54468,14 +54682,19 @@ function createLinzumiMcpApiClient(options) {
|
|
|
54468
54682
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
54469
54683
|
const baseUrl = kandanHttpBaseUrl(options.kandanUrl);
|
|
54470
54684
|
const apiPrefix = options.authMode === "personal-agent-delegation" ? "/api/v2/personal-agent-mcp" : "/api/v2/local-runner-mcp";
|
|
54685
|
+
const operatingMode = options.operatingMode ?? "text";
|
|
54471
54686
|
const request = async (method, path2, params) => {
|
|
54472
54687
|
const url = new URL(path2, baseUrl);
|
|
54688
|
+
const paramsWithMode = {
|
|
54689
|
+
...params,
|
|
54690
|
+
operating_mode: operatingMode
|
|
54691
|
+
};
|
|
54473
54692
|
const requestInit = {
|
|
54474
54693
|
method,
|
|
54475
54694
|
headers: { authorization: `Bearer ${options.accessToken}` }
|
|
54476
54695
|
};
|
|
54477
54696
|
if (method === "GET") {
|
|
54478
|
-
for (const [key, value] of Object.entries(
|
|
54697
|
+
for (const [key, value] of Object.entries(paramsWithMode)) {
|
|
54479
54698
|
if (value !== void 0 && value !== null) {
|
|
54480
54699
|
url.searchParams.set(key, String(value));
|
|
54481
54700
|
}
|
|
@@ -54485,7 +54704,7 @@ function createLinzumiMcpApiClient(options) {
|
|
|
54485
54704
|
...requestInit.headers,
|
|
54486
54705
|
"content-type": "application/json"
|
|
54487
54706
|
};
|
|
54488
|
-
requestInit.body = JSON.stringify(
|
|
54707
|
+
requestInit.body = JSON.stringify(paramsWithMode);
|
|
54489
54708
|
}
|
|
54490
54709
|
const response = await fetchImpl(url, requestInit);
|
|
54491
54710
|
const parsed = await response.json();
|
|
@@ -54522,6 +54741,7 @@ var mcpFlagDefinitions = /* @__PURE__ */ new Map([
|
|
|
54522
54741
|
["workspace", { kind: "value" }],
|
|
54523
54742
|
["channel", { kind: "value" }],
|
|
54524
54743
|
["owner-username", { kind: "value" }],
|
|
54744
|
+
["mode", { kind: "value" }],
|
|
54525
54745
|
["format", { kind: "value" }],
|
|
54526
54746
|
["command", { kind: "value" }],
|
|
54527
54747
|
["include-token", { kind: "boolean" }],
|
|
@@ -54552,8 +54772,8 @@ function mcpHelpText() {
|
|
|
54552
54772
|
return `Linzumi MCP
|
|
54553
54773
|
|
|
54554
54774
|
Usage:
|
|
54555
|
-
linzumi mcp server --api-url <url> [--workspace <slug> --channel <slug>]
|
|
54556
|
-
linzumi mcp server --api-url <url> --delegation-auth-file <path>
|
|
54775
|
+
linzumi mcp server --api-url <url> [--mode voice|text] [--workspace <slug> --channel <slug>]
|
|
54776
|
+
linzumi mcp server --api-url <url> --delegation-auth-file <path> [--mode voice|text]
|
|
54557
54777
|
linzumi mcp config --api-url <url> --format codex|claude-code [--include-token]
|
|
54558
54778
|
linzumi mcp doctor --api-url <url> [--workspace <slug> --channel <slug>]
|
|
54559
54779
|
|
|
@@ -54589,10 +54809,12 @@ async function runMcpServer(args) {
|
|
|
54589
54809
|
channelSlug: stringValue3(values, "channel")
|
|
54590
54810
|
});
|
|
54591
54811
|
const ownerUsername = stringValue3(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME;
|
|
54812
|
+
const operatingMode = mcpOperatingMode(stringValue3(values, "mode"));
|
|
54592
54813
|
const client = createLinzumiMcpApiClient({
|
|
54593
54814
|
kandanUrl,
|
|
54594
54815
|
accessToken: auth.accessToken,
|
|
54595
|
-
authMode: auth.mode
|
|
54816
|
+
authMode: auth.mode,
|
|
54817
|
+
operatingMode
|
|
54596
54818
|
});
|
|
54597
54819
|
const server = new McpServer({
|
|
54598
54820
|
name: "linzumi",
|
|
@@ -54709,6 +54931,7 @@ async function runMcpConfig(args) {
|
|
|
54709
54931
|
const values = strictFlagValues(args);
|
|
54710
54932
|
const kandanUrl = required(values, "api-url");
|
|
54711
54933
|
const format = stringValue3(values, "format") ?? "codex";
|
|
54934
|
+
const operatingMode = mcpOperatingMode(stringValue3(values, "mode"));
|
|
54712
54935
|
const token = values.get("include-token") === true ? (await resolveMcpAuth({
|
|
54713
54936
|
kandanUrl,
|
|
54714
54937
|
explicitToken: stringValue3(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
|
|
@@ -54722,7 +54945,8 @@ async function runMcpConfig(args) {
|
|
|
54722
54945
|
kandanUrl,
|
|
54723
54946
|
accessToken: token,
|
|
54724
54947
|
delegationAuthFilePath: stringValue3(values, "delegation-auth-file"),
|
|
54725
|
-
ownerUsername: stringValue3(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME
|
|
54948
|
+
ownerUsername: stringValue3(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME,
|
|
54949
|
+
operatingMode
|
|
54726
54950
|
});
|
|
54727
54951
|
switch (format) {
|
|
54728
54952
|
case "codex":
|
|
@@ -54763,7 +54987,8 @@ async function runMcpDoctor(args) {
|
|
|
54763
54987
|
const client = createLinzumiMcpApiClient({
|
|
54764
54988
|
kandanUrl,
|
|
54765
54989
|
accessToken: auth.accessToken,
|
|
54766
|
-
authMode: auth.mode
|
|
54990
|
+
authMode: auth.mode,
|
|
54991
|
+
operatingMode: mcpOperatingMode(stringValue3(values, "mode"))
|
|
54767
54992
|
});
|
|
54768
54993
|
await client.validateAuth();
|
|
54769
54994
|
break;
|
|
@@ -54838,6 +55063,17 @@ function stringValue3(values, key) {
|
|
|
54838
55063
|
const value = values.get(key);
|
|
54839
55064
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
54840
55065
|
}
|
|
55066
|
+
function mcpOperatingMode(value) {
|
|
55067
|
+
switch (value) {
|
|
55068
|
+
case void 0:
|
|
55069
|
+
return "text";
|
|
55070
|
+
case "voice":
|
|
55071
|
+
case "text":
|
|
55072
|
+
return value;
|
|
55073
|
+
default:
|
|
55074
|
+
throw new Error("--mode must be voice or text");
|
|
55075
|
+
}
|
|
55076
|
+
}
|
|
54841
55077
|
function required(values, key) {
|
|
54842
55078
|
const value = stringValue3(values, key);
|
|
54843
55079
|
if (value === void 0) {
|
|
@@ -55406,10 +55642,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
55406
55642
|
reasoningEffort: stringValue5(values, "reasoning-effort"),
|
|
55407
55643
|
sandbox: stringValue5(values, "sandbox"),
|
|
55408
55644
|
approvalPolicy: stringValue5(values, "approval-policy"),
|
|
55409
|
-
allowPortForwardingByDefault:
|
|
55410
|
-
values,
|
|
55411
|
-
"allow-port-forwarding-by-default"
|
|
55412
|
-
),
|
|
55645
|
+
allowPortForwardingByDefault: true,
|
|
55413
55646
|
streamFlushMs: positiveIntegerValue2(values, "stream-flush-ms")
|
|
55414
55647
|
}
|
|
55415
55648
|
};
|
|
@@ -55446,10 +55679,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
55446
55679
|
reasoningEffort: stringValue5(values, "reasoning-effort"),
|
|
55447
55680
|
sandbox: stringValue5(values, "sandbox"),
|
|
55448
55681
|
approvalPolicy: stringValue5(values, "approval-policy"),
|
|
55449
|
-
allowPortForwardingByDefault:
|
|
55450
|
-
values,
|
|
55451
|
-
"allow-port-forwarding-by-default"
|
|
55452
|
-
),
|
|
55682
|
+
allowPortForwardingByDefault: true,
|
|
55453
55683
|
streamFlushMs: positiveIntegerValue2(values, "stream-flush-ms")
|
|
55454
55684
|
};
|
|
55455
55685
|
const kandanUrl = kandanUrlValue(values) ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
|
|
@@ -55689,10 +55919,7 @@ function runnerRuntimeDefaultsFromValues(values) {
|
|
|
55689
55919
|
reasoningEffort: stringValue5(values, "reasoning-effort"),
|
|
55690
55920
|
approvalPolicy: stringValue5(values, "approval-policy"),
|
|
55691
55921
|
sandbox: stringValue5(values, "sandbox"),
|
|
55692
|
-
allowPortForwardingByDefault:
|
|
55693
|
-
values,
|
|
55694
|
-
"allow-port-forwarding-by-default"
|
|
55695
|
-
)
|
|
55922
|
+
allowPortForwardingByDefault: true
|
|
55696
55923
|
};
|
|
55697
55924
|
}
|
|
55698
55925
|
function kandanUrlValue(values) {
|
|
@@ -55710,9 +55937,6 @@ function requiredKandanUrl(values) {
|
|
|
55710
55937
|
}
|
|
55711
55938
|
return value;
|
|
55712
55939
|
}
|
|
55713
|
-
function booleanFlagValue(values, key) {
|
|
55714
|
-
return values.get(key) === true ? true : void 0;
|
|
55715
|
-
}
|
|
55716
55940
|
function strictFlagValues2(args, definitions = flagDefinitions) {
|
|
55717
55941
|
const values = /* @__PURE__ */ new Map();
|
|
55718
55942
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -56006,8 +56230,6 @@ Codex:
|
|
|
56006
56230
|
--reasoning-effort <value> Reasoning effort requested for Codex and shown in Linzumi
|
|
56007
56231
|
--sandbox <value> Sandbox metadata shown in Linzumi
|
|
56008
56232
|
--approval-policy <value> Approval-policy metadata shown in Linzumi
|
|
56009
|
-
--allow-port-forwarding-by-default
|
|
56010
|
-
Auto-approve detected port-forward candidates for started sessions
|
|
56011
56233
|
--stream-flush-ms <ms> Batch live Codex deltas before Linzumi persistence, default 150
|
|
56012
56234
|
--fast Mark this runner as low-latency/fast in the availability message
|
|
56013
56235
|
--log-file <path> JSONL event log path, default ~/.linzumi/logs/runner-events.jsonl
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linzumi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.67-beta",
|
|
4
4
|
"description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"README.md",
|
|
11
11
|
"bin",
|
|
12
12
|
"dist",
|
|
13
|
-
"docs/images",
|
|
14
13
|
"scripts"
|
|
15
14
|
],
|
|
16
15
|
"scripts": {
|
|
Binary file
|
|
Binary file
|