@linzumi/cli 0.0.94-beta → 0.0.95-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 +143 -30
  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.94-beta --version
61
+ npx -y @linzumi/cli@0.0.95-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -112,6 +112,14 @@ import { spawnSync } from "node:child_process";
112
112
  import { homedir } from "node:os";
113
113
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
114
114
  import { basename, dirname, join } from "node:path";
115
+ function projectScanBudgetMs() {
116
+ const raw = process.env.LINZUMI_ONBOARDING_PROJECT_SCAN_BUDGET_MS?.trim();
117
+ if (raw === void 0 || raw === "") {
118
+ return defaultProjectScanBudgetMs;
119
+ }
120
+ const parsed = Number.parseInt(raw, 10);
121
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultProjectScanBudgetMs;
122
+ }
115
123
  function discoverCurrentGitProject(args) {
116
124
  const startedAtMs = args.nowMs ?? Date.now();
117
125
  const responsiveness = args.placeResponsiveness;
@@ -136,7 +144,19 @@ function discoverCurrentGitProject(args) {
136
144
  });
137
145
  const sourceRoots = args.sourceRoots ?? defaultLocalProjectSourceRoots(args.cwd);
138
146
  let placesSearched = 1;
147
+ const scanBudgetMs = projectScanBudgetMs();
139
148
  for (const sourceRoot of sourceRoots) {
149
+ if (Date.now() - startedAtMs >= scanBudgetMs) {
150
+ searchStats.push({
151
+ place: sourceRoot,
152
+ source: "git",
153
+ duration_ms: 0,
154
+ result_count: 0,
155
+ skipped_scan_budget_exceeded: true
156
+ });
157
+ placesSearched += 1;
158
+ continue;
159
+ }
140
160
  const sourceRootStartedAtMs = Date.now();
141
161
  const rootResponsive = isPlaceResponsive(sourceRoot, responsiveness);
142
162
  if (!rootResponsive) {
@@ -296,6 +316,9 @@ function isGitWorktreeRoot(path2) {
296
316
  return existsSync(join(path2, ".git"));
297
317
  }
298
318
  function defaultLocalProjectSourceRoots(cwd) {
319
+ if (cwd === dirname(cwd)) {
320
+ return [];
321
+ }
299
322
  return uniqueStrings([
300
323
  ...ancestorSourceRoots(cwd),
301
324
  ...homeSourceRoots(),
@@ -409,13 +432,14 @@ function compactJsonObject(value) {
409
432
  })
410
433
  );
411
434
  }
412
- var worktreePathSampleLimit, gitSpawnTimeoutMs;
435
+ var worktreePathSampleLimit, gitSpawnTimeoutMs, defaultProjectScanBudgetMs;
413
436
  var init_onboardingProjectDiscovery = __esm({
414
437
  "src/onboardingProjectDiscovery.ts"() {
415
438
  "use strict";
416
439
  init_onboardingPlaceResponsiveness();
417
440
  worktreePathSampleLimit = 25;
418
441
  gitSpawnTimeoutMs = 4e3;
442
+ defaultProjectScanBudgetMs = 2e4;
419
443
  }
420
444
  });
421
445
 
@@ -8437,6 +8461,38 @@ var init_userFacingErrors = __esm({
8437
8461
 
8438
8462
  // src/channelSession.ts
8439
8463
  import { createHash as createHash3, randomUUID } from "node:crypto";
8464
+ function codexStartRequestTimeoutMs() {
8465
+ const raw = process.env.LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS?.trim();
8466
+ if (raw === void 0 || raw === "") {
8467
+ return CODEX_START_REQUEST_TIMEOUT_MS;
8468
+ }
8469
+ const parsed = Number.parseInt(raw, 10);
8470
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : CODEX_START_REQUEST_TIMEOUT_MS;
8471
+ }
8472
+ function isCodexStartRequestTimeoutError(error) {
8473
+ return error instanceof CodexStartRequestTimeoutError;
8474
+ }
8475
+ function withCodexStartRequestTimeout(method, request, timeoutMs = codexStartRequestTimeoutMs()) {
8476
+ if (!(timeoutMs > 0)) {
8477
+ return request;
8478
+ }
8479
+ return new Promise((resolve12, reject) => {
8480
+ const timer = setTimeout(() => {
8481
+ reject(new CodexStartRequestTimeoutError(method, timeoutMs));
8482
+ }, timeoutMs);
8483
+ timer.unref?.();
8484
+ request.then(
8485
+ (value) => {
8486
+ clearTimeout(timer);
8487
+ resolve12(value);
8488
+ },
8489
+ (error) => {
8490
+ clearTimeout(timer);
8491
+ reject(error);
8492
+ }
8493
+ );
8494
+ });
8495
+ }
8440
8496
  async function attachChannelSession(args) {
8441
8497
  const session = args.options.channelSession;
8442
8498
  const chatTopic = `chat:${session.workspaceSlug}:${session.channelSlug}`;
@@ -8941,6 +8997,7 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
8941
8997
  failedSeqs: /* @__PURE__ */ new Map(),
8942
8998
  drainInFlight: false,
8943
8999
  drainRequested: false,
9000
+ boundStartTimeout: false,
8944
9001
  fusedQueuedSeqs: /* @__PURE__ */ new Map(),
8945
9002
  reconnectRetryTimer: void 0,
8946
9003
  reconnectRetryAttempts: 0,
@@ -10295,6 +10352,9 @@ async function startThreadMessageTurn(args, state, payloadContext, message) {
10295
10352
  if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
10296
10353
  throw new Error("cannot start a local Codex turn before thread binding");
10297
10354
  }
10355
+ if (message.boundStartTimeout === true) {
10356
+ state.boundStartTimeout = true;
10357
+ }
10298
10358
  const queued = {
10299
10359
  seq: message.seq,
10300
10360
  actorSlug: message.actorSlug,
@@ -10558,7 +10618,9 @@ async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10558
10618
  if (state.kandanThreadId !== void 0) {
10559
10619
  startCodexTypingHeartbeat(args, state, state.kandanThreadId);
10560
10620
  }
10561
- const started = await args.codex.request("turn/start", {
10621
+ const boundStartTimeout = state.boundStartTimeout;
10622
+ state.boundStartTimeout = false;
10623
+ const turnStartRequest = args.codex.request("turn/start", {
10562
10624
  threadId: codexThreadId,
10563
10625
  input: await codexInputItemsForQueuedKandanMessage(
10564
10626
  args,
@@ -10569,6 +10631,7 @@ async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10569
10631
  runtimeOptionsForSettings(args.options, state.runtimeSettings)
10570
10632
  )
10571
10633
  });
10634
+ const started = boundStartTimeout ? await withCodexStartRequestTimeout("turn/start", turnStartRequest) : await turnStartRequest;
10572
10635
  const turnId = extractTurnIdFromResponse(started);
10573
10636
  state.pendingReconnectContextInjection = void 0;
10574
10637
  const interruptAfterStart = state.turn.status === "starting" && state.turn.interruptAfterStart;
@@ -10641,6 +10704,9 @@ async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10641
10704
  queued_seq: next.seq,
10642
10705
  message: error instanceof Error ? error.message : String(error)
10643
10706
  });
10707
+ if (isCodexStartRequestTimeoutError(error)) {
10708
+ throw error;
10709
+ }
10644
10710
  }
10645
10711
  }
10646
10712
  function drainKandanMessageQueueBlocked(args, state) {
@@ -11654,7 +11720,7 @@ async function pushOptional(kandan, topic, event, payload, log2) {
11654
11720
  });
11655
11721
  }
11656
11722
  }
11657
- var codexTypingHeartbeatMs, maxTrackedFailedChatEventSeqs, maxChatEventFailureAttempts, maxForwardedTurnIds, maxClaimedKandanMessageKeys, claimedKandanMessageKeys, nextChatJoinRegistrationSerial, defaultReconnectHandlingRetryDelaysMs, defaultTuiInputMirrorRetryDelaysMs, pipelineCloseTimeoutMs;
11723
+ var codexTypingHeartbeatMs, CODEX_START_REQUEST_TIMEOUT_MS, CodexStartRequestTimeoutError, maxTrackedFailedChatEventSeqs, maxChatEventFailureAttempts, maxForwardedTurnIds, maxClaimedKandanMessageKeys, claimedKandanMessageKeys, nextChatJoinRegistrationSerial, defaultReconnectHandlingRetryDelaysMs, defaultTuiInputMirrorRetryDelaysMs, pipelineCloseTimeoutMs;
11658
11724
  var init_channelSession = __esm({
11659
11725
  "src/channelSession.ts"() {
11660
11726
  "use strict";
@@ -11679,6 +11745,15 @@ var init_channelSession = __esm({
11679
11745
  init_processNameCatalog();
11680
11746
  init_userFacingErrors();
11681
11747
  codexTypingHeartbeatMs = 5e3;
11748
+ CODEX_START_REQUEST_TIMEOUT_MS = 12e4;
11749
+ CodexStartRequestTimeoutError = class extends Error {
11750
+ constructor(method, timeoutMs) {
11751
+ super(
11752
+ `Codex ${method} did not reply within ${timeoutMs}ms (start request timed out)`
11753
+ );
11754
+ this.name = "CodexStartRequestTimeoutError";
11755
+ }
11756
+ };
11682
11757
  maxTrackedFailedChatEventSeqs = 256;
11683
11758
  maxChatEventFailureAttempts = 5;
11684
11759
  maxForwardedTurnIds = 64;
@@ -19376,7 +19451,7 @@ var linzumiCliVersion, linzumiCliVersionText;
19376
19451
  var init_version = __esm({
19377
19452
  "src/version.ts"() {
19378
19453
  "use strict";
19379
- linzumiCliVersion = "0.0.94-beta";
19454
+ linzumiCliVersion = "0.0.95-beta";
19380
19455
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
19381
19456
  }
19382
19457
  });
@@ -23035,6 +23110,34 @@ async function runThreadCodexWorker(options) {
23035
23110
  async function openLocalCodexRunner(options, log2, cleanup, close) {
23036
23111
  const agentProviders = availableRunnerAgentProviders(options);
23037
23112
  const localCodexAppServerAvailable = hasLocalCodexAppServerCapability(options);
23113
+ const dependencyStatusRef = {
23114
+ value: options.dependencyStatus
23115
+ };
23116
+ const refreshDependencyStatus = async () => {
23117
+ if (dependencyStatusRef.value === void 0) {
23118
+ return;
23119
+ }
23120
+ try {
23121
+ const refreshed = await buildRunnerDependencyStatus({
23122
+ cwd: options.cwd,
23123
+ codexBin: options.codexBin,
23124
+ codeServerBin: options.codeServerBin,
23125
+ editorRuntime: dependencyStatusRef.value?.editorRuntime
23126
+ });
23127
+ const previousCodexAvailable = dependencyStatusRef.value?.codex.available;
23128
+ dependencyStatusRef.value = refreshed;
23129
+ log2("runner.dependency_status_reprobed", {
23130
+ runnerId: options.runnerId,
23131
+ codex_available: refreshed.codex.available,
23132
+ codex_available_changed: previousCodexAvailable !== refreshed.codex.available
23133
+ });
23134
+ } catch (error) {
23135
+ log2("runner.dependency_status_reprobe_failed", {
23136
+ runnerId: options.runnerId,
23137
+ message: error instanceof Error ? error.message : String(error)
23138
+ });
23139
+ }
23140
+ };
23038
23141
  const allowedForwardPorts = options.allowedForwardPorts ?? [];
23039
23142
  const liveForwardPorts = new Set(allowedForwardPorts);
23040
23143
  const managedForwardPorts = /* @__PURE__ */ new Set();
@@ -23207,8 +23310,8 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
23207
23310
  ),
23208
23311
  forwardedPortAttributions: buildForwardPortAttributionPayload(),
23209
23312
  pendingEditorForwardPortRequests: pendingEditorForwardPortRequests(),
23210
- toolStatus: options.dependencyStatus === void 0 ? null : dependencyStatusPayload(options.dependencyStatus),
23211
- editorRuntime: options.dependencyStatus?.editorRuntime === void 0 ? null : dependencyStatusPayload(options.dependencyStatus).editorRuntime,
23313
+ toolStatus: dependencyStatusRef.value === void 0 ? null : dependencyStatusPayload(dependencyStatusRef.value),
23314
+ editorRuntime: dependencyStatusRef.value?.editorRuntime === void 0 ? null : dependencyStatusPayload(dependencyStatusRef.value).editorRuntime,
23212
23315
  ...localEditorCapabilities(
23213
23316
  options.editorRuntime,
23214
23317
  allowedCwds.value,
@@ -25071,14 +25174,15 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25071
25174
  log2,
25072
25175
  pendingControls,
25073
25176
  controlDispatcher,
25074
- dispatchControl
25177
+ dispatchControl,
25178
+ refreshDependencyStatus
25075
25179
  );
25076
25180
  return { instanceId, codexUrl, close };
25077
25181
  }
25078
- function finishRunnerStartup(options, log2, pendingControls, controlDispatcher, dispatchControl) {
25182
+ function finishRunnerStartup(options, log2, pendingControls, controlDispatcher, dispatchControl, onOnboardingDiscoveryComplete) {
25079
25183
  controlDispatcher.value = dispatchControl;
25080
25184
  pendingControls.splice(0).forEach(({ control, ack }) => dispatchControl(control, ack));
25081
- startOnboardingDiscoveryAgents(options, log2);
25185
+ startOnboardingDiscoveryAgents(options, log2, onOnboardingDiscoveryComplete);
25082
25186
  }
25083
25187
  function commanderOutboxPersistDir() {
25084
25188
  const override = process.env.LINZUMI_COMMANDER_OUTBOX_DIR?.trim();
@@ -27175,22 +27279,25 @@ async function startCodexProviderInstance(args) {
27175
27279
  args.options,
27176
27280
  args.control
27177
27281
  );
27178
- const response = await args.codex.request("thread/start", {
27179
- cwd: args.cwd,
27180
- serviceName: "kandan-local-runner",
27181
- personality: "pragmatic",
27182
- developerInstructions: commanderDeveloperInstructions({
27282
+ const response = await withCodexStartRequestTimeout(
27283
+ "thread/start",
27284
+ args.codex.request("thread/start", {
27183
27285
  cwd: args.cwd,
27184
- planTool: "linzumi-mcp",
27185
- developerPrompt,
27186
- linzumiContext
27187
- }),
27188
- ...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
27189
- ...runtimeSettings.reasoningEffort === void 0 ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
27190
- ...runtimeSettings.approvalPolicy === void 0 ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
27191
- ...runtimeSettings.sandbox === void 0 ? {} : { sandbox: runtimeSettings.sandbox },
27192
- ...runtimeSettings.fast === true ? { serviceTier: "fast" } : {}
27193
- });
27286
+ serviceName: "kandan-local-runner",
27287
+ personality: "pragmatic",
27288
+ developerInstructions: commanderDeveloperInstructions({
27289
+ cwd: args.cwd,
27290
+ planTool: "linzumi-mcp",
27291
+ developerPrompt,
27292
+ linzumiContext
27293
+ }),
27294
+ ...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
27295
+ ...runtimeSettings.reasoningEffort === void 0 ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
27296
+ ...runtimeSettings.approvalPolicy === void 0 ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
27297
+ ...runtimeSettings.sandbox === void 0 ? {} : { sandbox: runtimeSettings.sandbox },
27298
+ ...runtimeSettings.fast === true ? { serviceTier: "fast" } : {}
27299
+ })
27300
+ );
27194
27301
  const codexThreadId = extractStartedThreadId(response);
27195
27302
  args.setStartedCodexThreadId(codexThreadId);
27196
27303
  const workDescription = normalizedWorkDescription(
@@ -27224,13 +27331,17 @@ async function startCodexProviderInstance(args) {
27224
27331
  seq: sourceSeq,
27225
27332
  body: workDescription,
27226
27333
  actorSlug: identity.actorUsername,
27227
- actorUserId: identity.actorUserId
27334
+ actorUserId: identity.actorUserId,
27335
+ boundStartTimeout: true
27228
27336
  });
27229
27337
  } else {
27230
- await args.codex.request("turn/start", {
27231
- threadId: codexThreadId,
27232
- input: [{ type: "text", text: workDescription }]
27233
- });
27338
+ await withCodexStartRequestTimeout(
27339
+ "turn/start",
27340
+ args.codex.request("turn/start", {
27341
+ threadId: codexThreadId,
27342
+ input: [{ type: "text", text: workDescription }]
27343
+ })
27344
+ );
27234
27345
  }
27235
27346
  }
27236
27347
  return {
@@ -29130,9 +29241,10 @@ function redactedThreadRunnerCliArgs(args) {
29130
29241
  function optionalCliValue(flag, value) {
29131
29242
  return value === void 0 || value === "" ? [] : [flag, value];
29132
29243
  }
29133
- function startOnboardingDiscoveryAgents(options, log2) {
29244
+ function startOnboardingDiscoveryAgents(options, log2, onOnboardingDiscoveryComplete) {
29134
29245
  const workspaceSlug = runnerWorkspaceSlug(options);
29135
29246
  if (!shouldStartOnboardingDiscoveryAgents(options, workspaceSlug)) {
29247
+ void onOnboardingDiscoveryComplete?.();
29136
29248
  return;
29137
29249
  }
29138
29250
  const startKey = onboardingDiscoveryAgentStartKey(options, workspaceSlug);
@@ -29190,6 +29302,7 @@ function startOnboardingDiscoveryAgents(options, log2) {
29190
29302
  workers_completed: 2,
29191
29303
  successful_write_count: successfulWriteCount
29192
29304
  });
29305
+ await onOnboardingDiscoveryComplete?.();
29193
29306
  });
29194
29307
  }
29195
29308
  function withSuccessfulWriteCount(report, successfulPostStartWriteCount) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.94-beta",
3
+ "version": "0.0.95-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": {