@linzumi/cli 0.0.93-beta → 0.0.94-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 +184 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@0.0.93-beta --version
61
+ npx -y @linzumi/cli@0.0.94-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -19376,7 +19376,7 @@ var linzumiCliVersion, linzumiCliVersionText;
19376
19376
  var init_version = __esm({
19377
19377
  "src/version.ts"() {
19378
19378
  "use strict";
19379
- linzumiCliVersion = "0.0.93-beta";
19379
+ linzumiCliVersion = "0.0.94-beta";
19380
19380
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
19381
19381
  }
19382
19382
  });
@@ -19487,6 +19487,82 @@ function assessRunnerLockHolder(record, isPidAlive, now) {
19487
19487
  }
19488
19488
  return { state: "live" };
19489
19489
  }
19490
+ function shouldAutoTakeOverForUpdate(existing, myVersion, myLaunchSource, holderState) {
19491
+ if (myLaunchSource !== electronAutoConnectLaunchSource) {
19492
+ return false;
19493
+ }
19494
+ if (existing.launchSource !== electronAutoConnectLaunchSource) {
19495
+ return false;
19496
+ }
19497
+ if (holderState.state !== "live") {
19498
+ return true;
19499
+ }
19500
+ return compareCliVersions(myVersion, existing.cliVersion) > 0;
19501
+ }
19502
+ function compareCliVersions(a, b) {
19503
+ const pa = parseCliVersion(a);
19504
+ const pb = parseCliVersion(b);
19505
+ for (let i = 0; i < 3; i++) {
19506
+ if (pa.core[i] !== pb.core[i]) {
19507
+ return pa.core[i] > pb.core[i] ? 1 : -1;
19508
+ }
19509
+ }
19510
+ if (pa.prerelease.length === 0 && pb.prerelease.length === 0) {
19511
+ return 0;
19512
+ }
19513
+ if (pa.prerelease.length === 0) {
19514
+ return 1;
19515
+ }
19516
+ if (pb.prerelease.length === 0) {
19517
+ return -1;
19518
+ }
19519
+ return comparePrerelease(pa.prerelease, pb.prerelease);
19520
+ }
19521
+ function parseCliVersion(value) {
19522
+ const trimmed = value.trim().replace(/^v/i, "").split("+", 1)[0] ?? "";
19523
+ const hyphen = trimmed.indexOf("-");
19524
+ const coreText = hyphen === -1 ? trimmed : trimmed.slice(0, hyphen);
19525
+ const prereleaseText = hyphen === -1 ? "" : trimmed.slice(hyphen + 1);
19526
+ const parts = coreText.split(".");
19527
+ const core = [
19528
+ parseCoreNumber(parts[0]),
19529
+ parseCoreNumber(parts[1]),
19530
+ parseCoreNumber(parts[2])
19531
+ ];
19532
+ const prerelease = prereleaseText === "" ? [] : prereleaseText.split(".").filter((id) => id !== "");
19533
+ return { core, prerelease };
19534
+ }
19535
+ function parseCoreNumber(value) {
19536
+ const parsed = Number.parseInt(value ?? "", 10);
19537
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : 0;
19538
+ }
19539
+ function comparePrerelease(a, b) {
19540
+ const length = Math.max(a.length, b.length);
19541
+ for (let i = 0; i < length; i++) {
19542
+ if (i >= a.length) {
19543
+ return -1;
19544
+ }
19545
+ if (i >= b.length) {
19546
+ return 1;
19547
+ }
19548
+ const idA = a[i];
19549
+ const idB = b[i];
19550
+ const numA = Number.parseInt(idA, 10);
19551
+ const numB = Number.parseInt(idB, 10);
19552
+ const aIsNum = numA.toString() === idA;
19553
+ const bIsNum = numB.toString() === idB;
19554
+ if (aIsNum && bIsNum) {
19555
+ if (numA !== numB) {
19556
+ return numA > numB ? 1 : -1;
19557
+ }
19558
+ } else if (aIsNum !== bIsNum) {
19559
+ return aIsNum ? -1 : 1;
19560
+ } else if (idA !== idB) {
19561
+ return idA > idB ? 1 : -1;
19562
+ }
19563
+ }
19564
+ return 0;
19565
+ }
19490
19566
  function takeOverWedgedHolder(lockPath, holder, reason, killPid, onTakeover) {
19491
19567
  try {
19492
19568
  killPid(holder.pid);
@@ -19754,7 +19830,7 @@ function displayLinzumiUrl(linzumiUrl) {
19754
19830
  function isNodeErrorCode2(error, code) {
19755
19831
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
19756
19832
  }
19757
- var runnerLockHeartbeatIntervalMs, runnerLockTakeoverAfterMs, runnerLockHeldErrorName, RunnerLockHeldError;
19833
+ var runnerLockHeartbeatIntervalMs, runnerLockTakeoverAfterMs, runnerLockHeldErrorName, RunnerLockHeldError, electronAutoConnectLaunchSource;
19758
19834
  var init_runnerLock = __esm({
19759
19835
  "src/runnerLock.ts"() {
19760
19836
  "use strict";
@@ -19775,6 +19851,7 @@ var init_runnerLock = __esm({
19775
19851
  this.heldBy = heldBy;
19776
19852
  }
19777
19853
  };
19854
+ electronAutoConnectLaunchSource = "electron_auto_connect";
19778
19855
  }
19779
19856
  });
19780
19857
 
@@ -25632,6 +25709,43 @@ async function acquireRunnerLockWithConflictResolution(acquire, options, log2) {
25632
25709
  lockPath: error.lockPath
25633
25710
  });
25634
25711
  const lockTakeover = options.lockTakeover;
25712
+ const autoTakeoverDeps = resolveRunnerLockTakeoverDeps(
25713
+ error.lockPath,
25714
+ lockTakeover?.takeover,
25715
+ log2
25716
+ );
25717
+ const holderState = assessRunnerLockHolder(
25718
+ error.heldBy,
25719
+ autoTakeoverDeps.isPidAlive,
25720
+ () => new Date(autoTakeoverDeps.now())
25721
+ );
25722
+ if (shouldAutoTakeOverForUpdate(
25723
+ error.heldBy,
25724
+ linzumiCliVersion,
25725
+ options.launchSource,
25726
+ holderState
25727
+ )) {
25728
+ log2("runner.lock_auto_takeover_for_update", {
25729
+ runnerId: options.runnerId,
25730
+ heldByRunnerId: error.heldBy.runnerId,
25731
+ heldByPid: error.heldBy.pid,
25732
+ lockPath: error.lockPath,
25733
+ fromVersion: error.heldBy.cliVersion,
25734
+ toVersion: linzumiCliVersion,
25735
+ launchSource: electronAutoConnectLaunchSource,
25736
+ holderState: holderState.state
25737
+ });
25738
+ const autoPromptDeps = lockTakeover?.prompt ?? defaultRunnerLockTakeoverPromptDeps(lockTakeover);
25739
+ await resolveRunnerLockConflict({
25740
+ holder: error.heldBy,
25741
+ lockPath: error.lockPath,
25742
+ baseMessage: error.message,
25743
+ takeOverWithoutPrompt: true,
25744
+ prompt: autoPromptDeps,
25745
+ takeover: autoTakeoverDeps
25746
+ });
25747
+ return acquire();
25748
+ }
25635
25749
  if (!shouldResolveRunnerLockConflict(lockTakeover)) {
25636
25750
  throw error;
25637
25751
  }
@@ -25848,6 +25962,32 @@ async function resumeCodexThreadForReconnect(codex, codexThreadId, resumeOverrid
25848
25962
  );
25849
25963
  }
25850
25964
  }
25965
+ async function applyCodexUnavailableControl(kandan, topic, instanceId, control, log2) {
25966
+ const error = "agent_provider_unavailable:codex";
25967
+ log2("kandan.start_instance_codex_app_server_unavailable", {
25968
+ controlType: control.type,
25969
+ thread_id: controlThreadId(control) ?? null,
25970
+ instanceId
25971
+ });
25972
+ if (control.type === "start_instance" || control.type === "reconnect_thread") {
25973
+ try {
25974
+ await publishStartInstanceMessageState(
25975
+ kandan,
25976
+ topic,
25977
+ control,
25978
+ "failed",
25979
+ error,
25980
+ { instanceId }
25981
+ );
25982
+ } catch (publishError) {
25983
+ log2("kandan.start_instance_unavailable_state_push_failed", {
25984
+ agent_provider: "codex",
25985
+ message: publishError instanceof Error ? publishError.message : String(publishError)
25986
+ });
25987
+ }
25988
+ }
25989
+ throw new Error(error);
25990
+ }
25851
25991
  async function applyControl(codex, kandan, topic, instanceId, options, agentProviders, allowedCwds, remoteCodexSandboxRunner, activeRemoteCodexExecRequests, activeClaudeCodeSessions, pendingClaudeCodeApprovals, ensureClaudeCodeForwardPortSession, disposeClaudeCodeForwardPortSession, control, log2, onStartedThread, onThreadProcessStart) {
25852
25992
  if (codex === void 0) {
25853
25993
  switch (control.type) {
@@ -25861,6 +26001,43 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
25861
26001
  activeRemoteCodexExecRequests,
25862
26002
  log2
25863
26003
  );
26004
+ // PROD WEDGE (coding jobs down on the shipped CLI beta): a runner whose
26005
+ // cached dependencyStatus.codex.available was `false` at connect (the
26006
+ // `codex --version` probe timed out during the blocking onboarding scan
26007
+ // and was never re-probed) never started its SHARED codex app-server, so
26008
+ // this `codex` is undefined. But a start_instance / reconnect_thread runs
26009
+ // in its OWN thread worker that spawns its OWN codex app-server - it does
26010
+ // NOT need the shared client. The old `default` arm returned
26011
+ // `agent_provider_unavailable:codex` HERE, before delegating, and only as
26012
+ // a bare codex_response: no thread-worker spawn, no
26013
+ // `runner.thread_process_starting` log, no message_state - a SILENT no-op
26014
+ // that left the user's job stuck in `coding` (prod thread 99d5808e,
26015
+ // control_seq 13). When a thread worker IS available (onThreadProcessStart
26016
+ // wired), fall through to the normal switch so the control delegates to
26017
+ // the worker; the worker's own start surfaces any genuine engine failure.
26018
+ // When NO thread worker exists, codex genuinely cannot run the job, so
26019
+ // fail LOUD (log + failed message_state + throw -> codex_error) instead of
26020
+ // leaving it silently stuck. This loud path is SCOPED to start_instance /
26021
+ // reconnect_thread only.
26022
+ case "start_instance":
26023
+ case "reconnect_thread":
26024
+ if (onThreadProcessStart !== void 0) {
26025
+ break;
26026
+ }
26027
+ return await applyCodexUnavailableControl(
26028
+ kandan,
26029
+ topic,
26030
+ instanceId,
26031
+ control,
26032
+ log2
26033
+ );
26034
+ // Every OTHER control type that reaches here is a request/response control
26035
+ // that does NOT need the shared codex app-server to fail gracefully
26036
+ // (browse_directory, create_project, suggest_tasks,
26037
+ // list_claude_code_sessions, interrupt, cancel_turn, ...). Preserve the
26038
+ // original behavior: return a bare `agent_provider_unavailable:codex`
26039
+ // codex_response - NO throw, NO failed message_state, NO start_instance
26040
+ // log - so onboarding/UI features keep working while codex is unavailable.
25864
26041
  default:
25865
26042
  return {
25866
26043
  instanceId,
@@ -68863,7 +69040,7 @@ async function parseStartRunnerArgs(args, deps = {
68863
69040
  cwd,
68864
69041
  codexBin,
68865
69042
  codexUrl: stringValue8(values, "codex-url"),
68866
- launchSource: electronAutoConnectLaunchSource(),
69043
+ launchSource: electronAutoConnectLaunchSource2(),
68867
69044
  launchTui: values.get("launch-tui") === true,
68868
69045
  fast: values.get("fast") === true,
68869
69046
  logFile: stringValue8(values, "log-file"),
@@ -68968,7 +69145,7 @@ async function parseAgentRunnerArgs(args, deps = {
68968
69145
  cwd,
68969
69146
  codexBin,
68970
69147
  codexUrl: stringValue8(values, "codex-url"),
68971
- launchSource: electronAutoConnectLaunchSource(),
69148
+ launchSource: electronAutoConnectLaunchSource2(),
68972
69149
  launchTui: values.get("launch-tui") === true,
68973
69150
  fast: values.get("fast") === true,
68974
69151
  logFile: stringValue8(values, "log-file"),
@@ -69143,7 +69320,7 @@ async function parseRunnerArgs(args, deps = {
69143
69320
  cwd,
69144
69321
  codexBin,
69145
69322
  codexUrl: stringValue8(values, "codex-url"),
69146
- launchSource: electronAutoConnectLaunchSource(),
69323
+ launchSource: electronAutoConnectLaunchSource2(),
69147
69324
  launchTui: values.get("launch-tui") === true,
69148
69325
  fast: values.get("fast") === true,
69149
69326
  logFile: stringValue8(values, "log-file"),
@@ -69178,11 +69355,11 @@ function runnerRuntimeDefaultsFromValues(values) {
69178
69355
  allowPortForwardingByDefault: true
69179
69356
  };
69180
69357
  }
69181
- function electronAutoConnectLaunchSource() {
69358
+ function electronAutoConnectLaunchSource2() {
69182
69359
  return process.env.LINZUMI_ELECTRON_AUTO_CONNECT_RUNNER === "1" ? "electron_auto_connect" : void 0;
69183
69360
  }
69184
69361
  function connectLaunchSourceTelemetryValue() {
69185
- return electronAutoConnectLaunchSource() ?? "cli";
69362
+ return electronAutoConnectLaunchSource2() ?? "cli";
69186
69363
  }
69187
69364
  function workspaceTelemetryAttribute(workspace) {
69188
69365
  return workspace === void 0 ? {} : { workspace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.93-beta",
3
+ "version": "0.0.94-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": {