@linzumi/cli 0.0.59-beta → 0.0.60-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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +339 -110
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,7 +62,7 @@ Install the CLI or run it with `npx`:
62
62
  ```bash
63
63
  npm install -g @linzumi/cli@latest
64
64
  npx -y @linzumi/cli@latest signup
65
- npx -y @linzumi/cli@0.0.59-beta --version
65
+ npx -y @linzumi/cli@0.0.60-beta --version
66
66
  linzumi --version
67
67
  ```
68
68
 
package/dist/index.js CHANGED
@@ -28294,7 +28294,7 @@ function realpathOrResolved(pathValue) {
28294
28294
  }
28295
28295
 
28296
28296
  // src/version.ts
28297
- var linzumiCliVersion = "0.0.59-beta";
28297
+ var linzumiCliVersion = "0.0.60-beta";
28298
28298
  var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
28299
28299
 
28300
28300
  // src/runnerLock.ts
@@ -28780,6 +28780,7 @@ function normalizeLocalRunnerCache(value) {
28780
28780
  }
28781
28781
 
28782
28782
  // src/runner.ts
28783
+ var THREAD_RUNNER_READY_TIMEOUT_MS = 3e4;
28783
28784
  async function runLocalCodexRunner(options) {
28784
28785
  const log = makeRunnerLogger(options);
28785
28786
  const cleanup = {
@@ -28817,6 +28818,29 @@ async function runLocalCodexRunner(options) {
28817
28818
  throw error51;
28818
28819
  }
28819
28820
  }
28821
+ async function runThreadCodexWorker(options) {
28822
+ if (options.threadProcess?.role !== "thread") {
28823
+ throw new Error("thread Codex worker requires thread process options");
28824
+ }
28825
+ if (process.send === void 0) {
28826
+ throw new Error("thread Codex worker requires an IPC channel");
28827
+ }
28828
+ const log = makeRunnerLogger(options);
28829
+ const started = await startOwnedCodexAppServer(options, {
28830
+ linzumiMcp: false
28831
+ });
28832
+ const stop = once(() => {
28833
+ started.stop();
28834
+ log.close();
28835
+ });
28836
+ process.send({
28837
+ type: "linzumi_thread_codex_worker_ready",
28838
+ kandanThreadId: options.threadProcess.kandanThreadId,
28839
+ codexUrl: started.url
28840
+ });
28841
+ await waitForThreadCodexWorkerStop(started.process);
28842
+ stop();
28843
+ }
28820
28844
  async function openLocalCodexRunner(options, log, cleanup, close) {
28821
28845
  const agentProviders = availableRunnerAgentProviders(options);
28822
28846
  const allowedForwardPorts = options.allowedForwardPorts ?? [];
@@ -29711,7 +29735,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29711
29735
  launchTui: options.launchTui,
29712
29736
  enablePortForwardWatch: true,
29713
29737
  initialForwardPorts: allowedForwardPorts,
29714
- portForwardWatcher: started?.process.pid === void 0 ? void 0 : { rootPid: started.process.pid },
29738
+ portForwardWatcher: channelSessionPortForwardWatcherOptions({
29739
+ rootPid: started?.process.pid,
29740
+ start: options.portForwardWatcher
29741
+ }),
29715
29742
  suppressedForwardPorts,
29716
29743
  onForwardPortApproved: (port, attribution) => {
29717
29744
  liveForwardPorts.add(port);
@@ -29771,7 +29798,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29771
29798
  );
29772
29799
  threadRunnerProcesses.clear();
29773
29800
  });
29774
- const attachThreadSession = async (control, cwd, codexThreadId) => {
29801
+ const attachThreadSessionWithCodex = async (args) => {
29802
+ const { control, cwd, codexThreadId, sessionCodex } = args;
29803
+ const portForwardWatcherRootPid = args.portForwardWatcherRootPid;
29775
29804
  const workspaceSlug = optionalThreadControlField(control, "workspace");
29776
29805
  const channelSlug = optionalThreadControlField(control, "channel");
29777
29806
  const kandanThreadId = optionalThreadControlField(control, "threadId");
@@ -29791,7 +29820,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29791
29820
  const runtimeSettings = startInstanceRuntimeSettings(options, control);
29792
29821
  const session = await attachChannelSession({
29793
29822
  kandan,
29794
- codex,
29823
+ codex: sessionCodex,
29795
29824
  topic,
29796
29825
  instanceId,
29797
29826
  options: {
@@ -29805,7 +29834,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29805
29834
  launchTui: false,
29806
29835
  enablePortForwardWatch: true,
29807
29836
  initialForwardPorts: allowedForwardPorts,
29808
- portForwardWatcher: started?.process.pid === void 0 ? void 0 : { rootPid: started.process.pid },
29837
+ portForwardWatcher: channelSessionPortForwardWatcherOptions({
29838
+ rootPid: portForwardWatcherRootPid,
29839
+ start: options.portForwardWatcher
29840
+ }),
29809
29841
  suppressedForwardPorts,
29810
29842
  onForwardPortApproved: (port, attribution) => {
29811
29843
  liveForwardPorts.add(port);
@@ -29840,10 +29872,23 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29840
29872
  dynamicChannelSessions.set(kandanThreadId, session);
29841
29873
  return session;
29842
29874
  };
29875
+ const attachThreadSession = async (control, cwd, codexThreadId) => {
29876
+ return await attachThreadSessionWithCodex({
29877
+ control,
29878
+ cwd,
29879
+ codexThreadId,
29880
+ sessionCodex: codex,
29881
+ portForwardWatcherRootPid: started?.process.pid
29882
+ });
29883
+ };
29843
29884
  const startThreadRunnerProcess = async (control, cwd) => {
29844
29885
  if (options.threadProcess?.role === "thread") {
29845
29886
  return void 0;
29846
29887
  }
29888
+ const agentProviderResult = startInstanceAgentProvider(control);
29889
+ if (agentProviderResult.type !== "ok" || agentProviderResult.provider !== "codex") {
29890
+ return void 0;
29891
+ }
29847
29892
  const workspaceSlug = optionalThreadControlField(control, "workspace");
29848
29893
  const channelSlug = optionalThreadControlField(control, "channel");
29849
29894
  const kandanThreadId = optionalThreadControlField(control, "threadId");
@@ -29863,17 +29908,24 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29863
29908
  };
29864
29909
  }
29865
29910
  const spawnThreadRunner = options.spawnThreadRunner ?? spawnLocalThreadRunnerProcess;
29911
+ const readyTimeoutMs = threadRunnerReadyTimeoutMs(options);
29866
29912
  const startingEntry = {
29867
29913
  kind: "starting",
29868
- promise: spawnThreadRunner(
29869
- threadRunnerOptions({
29870
- options,
29871
- control,
29872
- cwd,
29873
- workspaceSlug,
29874
- channelSlug,
29875
- kandanThreadId
29876
- })
29914
+ promise: withThreadRunnerReadyTimeout(
29915
+ spawnThreadRunner(
29916
+ threadRunnerOptions({
29917
+ options,
29918
+ control,
29919
+ cwd,
29920
+ workspaceSlug,
29921
+ channelSlug,
29922
+ kandanThreadId
29923
+ })
29924
+ ),
29925
+ {
29926
+ kandanThreadId,
29927
+ timeoutMs: readyTimeoutMs
29928
+ }
29877
29929
  )
29878
29930
  };
29879
29931
  threadRunnerProcesses.set(kandanThreadId, startingEntry);
@@ -29886,31 +29938,84 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
29886
29938
  }
29887
29939
  throw error51;
29888
29940
  }
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) {
29941
+ try {
29942
+ const threadCodex = await connectCodexAppServer(handle.codexUrl);
29943
+ const closeThreadCodex = once(() => threadCodex.close());
29944
+ handle.onExit?.(closeThreadCodex);
29945
+ const runtimeHandle = {
29946
+ ...handle,
29947
+ close: async () => {
29948
+ closeThreadCodex();
29949
+ await handle.close();
29950
+ }
29951
+ };
29952
+ const runningEntry = {
29953
+ kind: "running",
29954
+ handle: runtimeHandle
29955
+ };
29956
+ if (threadRunnerProcesses.get(kandanThreadId) === startingEntry) {
29957
+ threadRunnerProcesses.set(kandanThreadId, runningEntry);
29958
+ }
29959
+ handle.onExit?.(() => {
29960
+ if (threadRunnerProcesses.get(kandanThreadId) === runningEntry) {
29961
+ threadRunnerProcesses.delete(kandanThreadId);
29962
+ }
29963
+ });
29964
+ log("runner.thread_process_started", {
29965
+ kandanThreadId,
29966
+ cwd
29967
+ });
29968
+ const onStartedThread = (startedControl, startedCwd, codexThreadId) => attachThreadSessionWithCodex({
29969
+ control: startedControl,
29970
+ cwd: startedCwd,
29971
+ codexThreadId,
29972
+ sessionCodex: threadCodex,
29973
+ portForwardWatcherRootPid: handle.processPid
29974
+ });
29975
+ switch (control.type) {
29976
+ case "start_instance": {
29977
+ const result = await startCodexProviderInstance({
29978
+ codex: threadCodex,
29979
+ kandan,
29980
+ topic,
29981
+ control,
29982
+ options,
29983
+ instanceId,
29984
+ cwd,
29985
+ matchedRoot: cwd,
29986
+ onStartedThread,
29987
+ setStartupStage: () => void 0,
29988
+ setStartedCodexThreadId: () => void 0
29989
+ });
29990
+ return result.controlResponse;
29991
+ }
29992
+ case "reconnect_thread":
29993
+ return await applyControl(
29994
+ threadCodex,
29995
+ kandan,
29996
+ topic,
29997
+ instanceId,
29998
+ options,
29999
+ agentProviders,
30000
+ allowedCwds.value,
30001
+ activeClaudeCodeSessions,
30002
+ pendingClaudeCodeApprovals,
30003
+ ensureClaudeCodeForwardPortSession,
30004
+ disposeClaudeCodeForwardPortSession,
30005
+ control,
30006
+ log,
30007
+ onStartedThread,
30008
+ void 0
30009
+ );
30010
+ }
30011
+ } catch (error51) {
30012
+ const entry = threadRunnerProcesses.get(kandanThreadId);
30013
+ if (entry === startingEntry || entry?.kind === "running") {
29898
30014
  threadRunnerProcesses.delete(kandanThreadId);
29899
30015
  }
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
- };
30016
+ await (entry?.kind === "running" ? entry.handle.close() : handle.close()).catch(() => void 0);
30017
+ throw error51;
30018
+ }
29914
30019
  };
29915
30020
  const heartbeatPayload = () => ({
29916
30021
  instanceId,
@@ -30228,12 +30333,6 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
30228
30333
  };
30229
30334
  controlDispatcher.value = handleControl;
30230
30335
  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
- }
30237
30336
  return { instanceId, codexUrl, close };
30238
30337
  }
30239
30338
  function controlTargetsInstance(control, instanceId) {
@@ -32551,6 +32650,15 @@ function startInstanceRuntimeSettings(options, control) {
32551
32650
  allowPortForwardingByDefault: control.allowPortForwardingByDefault ?? defaults.allowPortForwardingByDefault
32552
32651
  };
32553
32652
  }
32653
+ function channelSessionPortForwardWatcherOptions(args) {
32654
+ if (args.rootPid === void 0 && args.start === void 0) {
32655
+ return void 0;
32656
+ }
32657
+ return {
32658
+ ...args.rootPid === void 0 ? {} : { rootPid: args.rootPid },
32659
+ ...args.start === void 0 ? {} : { start: args.start }
32660
+ };
32661
+ }
32554
32662
  function threadRunnerOptions(args) {
32555
32663
  const runtimeSettings = startInstanceRuntimeSettings(
32556
32664
  args.options,
@@ -32558,7 +32666,7 @@ function threadRunnerOptions(args) {
32558
32666
  );
32559
32667
  return {
32560
32668
  ...args.options,
32561
- clientId: `${args.options.clientId ?? args.options.machineId ?? args.options.runnerId}:thread:${args.kandanThreadId}`,
32669
+ clientId: void 0,
32562
32670
  machineId: void 0,
32563
32671
  runnerLockConfigPath: void 0,
32564
32672
  workspaceSlug: args.workspaceSlug,
@@ -32570,10 +32678,10 @@ function threadRunnerOptions(args) {
32570
32678
  editorRuntime: void 0,
32571
32679
  threadProcess: {
32572
32680
  role: "thread",
32573
- kandanThreadId: args.kandanThreadId,
32574
- initialControl: args.control
32681
+ kandanThreadId: args.kandanThreadId
32575
32682
  },
32576
32683
  spawnThreadRunner: void 0,
32684
+ threadRunnerReadyTimeoutMs: args.options.threadRunnerReadyTimeoutMs,
32577
32685
  runtimeDefaults: {
32578
32686
  model: runtimeSettings.model,
32579
32687
  reasoningEffort: runtimeSettings.reasoningEffort,
@@ -32601,6 +32709,7 @@ async function closeThreadRunnerEntry(entry) {
32601
32709
  }
32602
32710
  async function spawnLocalThreadRunnerProcess(options) {
32603
32711
  const scriptPath = process.argv[1];
32712
+ const kandanThreadId = options.threadProcess?.kandanThreadId ?? "";
32604
32713
  if (scriptPath === void 0 || scriptPath.trim() === "") {
32605
32714
  throw new Error(
32606
32715
  "cannot fork thread runner without current CLI script path"
@@ -32610,14 +32719,7 @@ async function spawnLocalThreadRunnerProcess(options) {
32610
32719
  const env = {
32611
32720
  ...process.env,
32612
32721
  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
- }
32722
+ LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID: kandanThreadId
32621
32723
  };
32622
32724
  writeCliAuditEvent("process.spawn", {
32623
32725
  command: process.execPath,
@@ -32628,7 +32730,7 @@ async function spawnLocalThreadRunnerProcess(options) {
32628
32730
  const child = spawn6(process.execPath, [scriptPath, ...args], {
32629
32731
  cwd: options.cwd,
32630
32732
  env,
32631
- stdio: "inherit"
32733
+ stdio: ["inherit", "inherit", "inherit", "ipc"]
32632
32734
  });
32633
32735
  writeCliAuditEvent("process.spawned", {
32634
32736
  command: process.execPath,
@@ -32655,8 +32757,64 @@ async function spawnLocalThreadRunnerProcess(options) {
32655
32757
  }
32656
32758
  exitListeners.clear();
32657
32759
  });
32760
+ const ready2 = await new Promise((resolve11, reject) => {
32761
+ let settled = false;
32762
+ let timeout;
32763
+ const cleanupReadyListeners = () => {
32764
+ if (timeout !== void 0) {
32765
+ clearTimeout(timeout);
32766
+ }
32767
+ child.off("message", onMessage);
32768
+ child.off("exit", onExitBeforeReady);
32769
+ };
32770
+ const settle = (action) => {
32771
+ if (settled) {
32772
+ return;
32773
+ }
32774
+ settled = true;
32775
+ cleanupReadyListeners();
32776
+ action();
32777
+ };
32778
+ const onMessage = (message) => {
32779
+ const parsed = (() => {
32780
+ try {
32781
+ return threadRunnerReadyMessage(message, kandanThreadId);
32782
+ } catch (error51) {
32783
+ settle(
32784
+ () => reject(error51 instanceof Error ? error51 : new Error(String(error51)))
32785
+ );
32786
+ return void 0;
32787
+ }
32788
+ })();
32789
+ if (parsed === void 0) {
32790
+ return;
32791
+ }
32792
+ settle(() => resolve11(parsed));
32793
+ };
32794
+ const onExitBeforeReady = (code, signal) => {
32795
+ settle(
32796
+ () => reject(
32797
+ new Error(
32798
+ `thread Codex worker exited before ready: code=${code ?? "null"} signal=${signal ?? "null"}`
32799
+ )
32800
+ )
32801
+ );
32802
+ };
32803
+ const onReadyTimeout = () => {
32804
+ settle(() => {
32805
+ child.kill("SIGINT");
32806
+ reject(threadRunnerReadyTimeoutError(kandanThreadId, readyTimeoutMs));
32807
+ });
32808
+ };
32809
+ child.on("message", onMessage);
32810
+ child.once("exit", onExitBeforeReady);
32811
+ const readyTimeoutMs = threadRunnerReadyTimeoutMs(options);
32812
+ timeout = setTimeout(onReadyTimeout, readyTimeoutMs);
32813
+ });
32658
32814
  return {
32659
- kandanThreadId: options.threadProcess?.kandanThreadId ?? "",
32815
+ kandanThreadId: ready2.kandanThreadId,
32816
+ codexUrl: ready2.codexUrl,
32817
+ processPid: child.pid,
32660
32818
  onExit: (listener) => {
32661
32819
  if (exitState.exited) {
32662
32820
  queueMicrotask(listener);
@@ -32674,6 +32832,79 @@ async function spawnLocalThreadRunnerProcess(options) {
32674
32832
  })
32675
32833
  };
32676
32834
  }
32835
+ function threadRunnerReadyMessage(message, expectedKandanThreadId) {
32836
+ if (!isJsonObject(message)) {
32837
+ return void 0;
32838
+ }
32839
+ if (message.type !== "linzumi_thread_codex_worker_ready") {
32840
+ return void 0;
32841
+ }
32842
+ const kandanThreadId = stringValue(message.kandanThreadId);
32843
+ const codexUrl = stringValue(message.codexUrl);
32844
+ if (kandanThreadId !== expectedKandanThreadId || codexUrl === void 0) {
32845
+ throw new Error("thread Codex worker reported invalid ready payload");
32846
+ }
32847
+ return { kandanThreadId, codexUrl };
32848
+ }
32849
+ function waitForThreadCodexWorkerStop(codexProcess) {
32850
+ return new Promise((resolve11) => {
32851
+ const stop = once(() => {
32852
+ process.off("disconnect", stop);
32853
+ process.off("SIGINT", stop);
32854
+ process.off("SIGTERM", stop);
32855
+ codexProcess.off("exit", stop);
32856
+ resolve11();
32857
+ });
32858
+ process.once("disconnect", stop);
32859
+ process.once("SIGINT", stop);
32860
+ process.once("SIGTERM", stop);
32861
+ codexProcess.once("exit", stop);
32862
+ });
32863
+ }
32864
+ function withThreadRunnerReadyTimeout(promise2, args) {
32865
+ return new Promise((resolve11, reject) => {
32866
+ let settled = false;
32867
+ const timeout = setTimeout(() => {
32868
+ if (settled) {
32869
+ return;
32870
+ }
32871
+ settled = true;
32872
+ reject(
32873
+ threadRunnerReadyTimeoutError(args.kandanThreadId, args.timeoutMs)
32874
+ );
32875
+ }, args.timeoutMs);
32876
+ promise2.then(
32877
+ (handle) => {
32878
+ if (settled) {
32879
+ return;
32880
+ }
32881
+ settled = true;
32882
+ clearTimeout(timeout);
32883
+ resolve11(handle);
32884
+ },
32885
+ (error51) => {
32886
+ if (settled) {
32887
+ return;
32888
+ }
32889
+ settled = true;
32890
+ clearTimeout(timeout);
32891
+ reject(error51);
32892
+ }
32893
+ );
32894
+ });
32895
+ }
32896
+ function threadRunnerReadyTimeoutError(kandanThreadId, timeoutMs) {
32897
+ return new Error(
32898
+ `thread Codex worker did not report ready within ${timeoutMs}ms for Kandan thread ${kandanThreadId}`
32899
+ );
32900
+ }
32901
+ function threadRunnerReadyTimeoutMs(options) {
32902
+ const timeoutMs = options.threadRunnerReadyTimeoutMs ?? THREAD_RUNNER_READY_TIMEOUT_MS;
32903
+ if (Number.isFinite(timeoutMs) && timeoutMs >= 0) {
32904
+ return timeoutMs;
32905
+ }
32906
+ throw new Error("thread runner ready timeout must be a non-negative number");
32907
+ }
32677
32908
  function threadRunnerCliArgs(options) {
32678
32909
  return [
32679
32910
  "--api-url",
@@ -32722,17 +32953,17 @@ function redactedThreadRunnerCliArgs(args) {
32722
32953
  function optionalCliValue(flag, value) {
32723
32954
  return value === void 0 || value === "" ? [] : [flag, value];
32724
32955
  }
32725
- async function startOwnedCodexAppServer(options) {
32956
+ async function startOwnedCodexAppServer(options, args = { linzumiMcp: true }) {
32726
32957
  ensureCodexProjectTrusted(options.cwd);
32727
32958
  const defaults = runnerRuntimeDefaults(options);
32728
- const mcpAuth = writeEphemeralMcpAuthFile(options);
32959
+ const mcpAuth = args.linzumiMcp === true ? writeEphemeralMcpAuthFile(options) : void 0;
32729
32960
  const mcpCommand = currentLinzumiCommand();
32730
32961
  try {
32731
32962
  const started = await startCodexAppServer(options.codexBin, options.cwd, {
32732
32963
  model: defaults.model,
32733
32964
  reasoningEffort: defaults.reasoningEffort,
32734
32965
  fast: options.fast,
32735
- mcpServers: [
32966
+ mcpServers: mcpAuth === void 0 ? [] : [
32736
32967
  linzumiMcpServerConfig({
32737
32968
  command: mcpCommand.command,
32738
32969
  argsPrefix: mcpCommand.argsPrefix,
@@ -32742,7 +32973,7 @@ async function startOwnedCodexAppServer(options) {
32742
32973
  })
32743
32974
  ]
32744
32975
  });
32745
- const cleanup = once(mcpAuth.cleanup);
32976
+ const cleanup = once(() => mcpAuth?.cleanup());
32746
32977
  started.process.once("exit", cleanup);
32747
32978
  return {
32748
32979
  ...started,
@@ -32752,7 +32983,7 @@ async function startOwnedCodexAppServer(options) {
32752
32983
  }
32753
32984
  };
32754
32985
  } catch (error51) {
32755
- mcpAuth.cleanup();
32986
+ mcpAuth?.cleanup();
32756
32987
  throw error51;
32757
32988
  }
32758
32989
  }
@@ -63133,24 +63364,22 @@ async function main(args) {
63133
63364
  return;
63134
63365
  case "agentRunner": {
63135
63366
  const options = await parseAgentRunnerArgs(parsed.args);
63136
- await runLocalCodexRunner(
63137
- withLocalMachineId(withThreadRunnerEnv(options))
63138
- );
63367
+ await runLocalCodexRunner(withLocalMachineId(options));
63139
63368
  return;
63140
63369
  }
63141
63370
  case "start": {
63142
63371
  const options = await parseStartRunnerArgs(parsed.args);
63143
63372
  addAllowedCwdForLinzumiUrl(options.cwd, options.kandanUrl);
63144
- await runLocalCodexRunner(
63145
- withLocalMachineId(withThreadRunnerEnv(options))
63146
- );
63373
+ await runLocalCodexRunner(withLocalMachineId(options));
63147
63374
  return;
63148
63375
  }
63149
63376
  case "run": {
63377
+ if (process.env.LINZUMI_THREAD_RUNNER_ROLE === "thread") {
63378
+ await runThreadCodexWorker(parseThreadCodexWorkerArgs(parsed.args));
63379
+ return;
63380
+ }
63150
63381
  const options = await parseRunnerArgs(parsed.args);
63151
- await runLocalCodexRunner(
63152
- withLocalMachineId(withThreadRunnerEnv(options))
63153
- );
63382
+ await runLocalCodexRunner(withLocalMachineId(options));
63154
63383
  return;
63155
63384
  }
63156
63385
  }
@@ -63779,7 +64008,13 @@ async function parseRunnerArgs(args, deps = {
63779
64008
  ) : [...localConfiguredAllowedCwds.allowedCwds];
63780
64009
  const codexBin = stringValue5(values, "codex-bin") ?? "codex";
63781
64010
  const customCodeServerBin = stringValue5(values, "code-server-bin");
63782
- const explicitToken = stringValue5(values, "token") ?? threadRunnerTokenEnv();
64011
+ const initialDependencyStatus = await deps.buildDependencyStatus({
64012
+ cwd,
64013
+ codexBin,
64014
+ codeServerBin: customCodeServerBin
64015
+ });
64016
+ assertStartDependencies(initialDependencyStatus);
64017
+ const explicitToken = stringValue5(values, "token");
63783
64018
  const token = await deps.resolveToken({
63784
64019
  kandanUrl,
63785
64020
  explicitToken,
@@ -64023,57 +64258,51 @@ function withLocalMachineId(options) {
64023
64258
  machineId: localConfigScopeKey(options.kandanUrl) === localConfigScopeKey(defaultLinzumiWebSocketUrl) ? ensureLocalMachineId() : ensureLocalMachineIdForLinzumiUrl(options.kandanUrl)
64024
64259
  };
64025
64260
  }
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
- }
64261
+ function parseThreadCodexWorkerArgs(args) {
64262
+ const values = strictFlagValues2(args);
64263
+ const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
64264
+ const cwd = stringValue5(values, "cwd") ?? process.cwd();
64265
+ const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
64266
+ parseAllowedCwdList(stringValue5(values, "allowed-cwd"))
64267
+ ) : assertConfiguredAllowedCwds([cwd]);
64268
+ const kandanThreadId = requiredThreadRunnerKandanThreadId();
64036
64269
  return {
64037
- ...options,
64038
- clientId: process.env.LINZUMI_THREAD_RUNNER_CLIENT_ID,
64270
+ kandanUrl,
64271
+ token: "",
64272
+ runnerId: `thread-codex-worker:${kandanThreadId}`,
64039
64273
  machineId: void 0,
64040
64274
  runnerLockConfigPath: void 0,
64275
+ workspaceSlug: stringValue5(values, "workspace"),
64276
+ cwd,
64277
+ codexBin: stringValue5(values, "codex-bin") ?? "codex",
64041
64278
  codexUrl: void 0,
64042
64279
  launchTui: false,
64280
+ fast: values.get("fast") === true ? true : void 0,
64281
+ logFile: stringValue5(values, "log-file"),
64282
+ allowedCwds: configuredAllowedCwds2.allowedCwds,
64283
+ missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
64284
+ allowedForwardPorts: parseAllowedPortList(
64285
+ stringValue5(values, "forward-port")
64286
+ ),
64287
+ codeServerBin: void 0,
64288
+ editorRuntime: void 0,
64289
+ socketFactory: void 0,
64290
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
64043
64291
  threadProcess: {
64044
64292
  role: "thread",
64045
- kandanThreadId,
64046
- initialControl: initialThreadControlFromEnv()
64293
+ kandanThreadId
64047
64294
  },
64048
64295
  channelSession: void 0
64049
64296
  };
64050
64297
  }
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");
64298
+ function requiredThreadRunnerKandanThreadId() {
64299
+ const kandanThreadId = process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
64300
+ if (kandanThreadId === void 0 || kandanThreadId.trim() === "") {
64301
+ throw new Error(
64302
+ "thread runner is missing LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID"
64303
+ );
64076
64304
  }
64305
+ return kandanThreadId;
64077
64306
  }
64078
64307
  function required3(values, key) {
64079
64308
  const value = stringValue5(values, key);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.59-beta",
3
+ "version": "0.0.60-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": {