@linzumi/cli 0.0.96-beta → 0.0.97-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 +575 -77
  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.96-beta --version
61
+ npx -y @linzumi/cli@0.0.97-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -125,25 +125,31 @@ function discoverCurrentGitProject(args) {
125
125
  const responsiveness = args.placeResponsiveness;
126
126
  const candidates = /* @__PURE__ */ new Map();
127
127
  const searchStats = [];
128
- const cwdResponsive = isPlaceResponsive(args.cwd, responsiveness);
129
- const gitStartedAtMs = Date.now();
130
- const topLevel = cwdResponsive ? gitOutput(args.cwd, ["rev-parse", "--show-toplevel"]) : void 0;
131
- const gitDurationMs = Date.now() - gitStartedAtMs;
132
- if (topLevel !== void 0) {
133
- mergeCandidate(
134
- candidates,
135
- projectCandidateFromWorktree(topLevel, "local_current_git", topLevel)
136
- );
137
- }
138
- searchStats.push({
139
- place: args.cwd,
140
- source: "git",
141
- duration_ms: gitDurationMs,
142
- result_count: topLevel === void 0 ? 0 : 1,
143
- skipped_unresponsive: cwdResponsive ? void 0 : true
128
+ const selectedScanRoots = uniqueStrings(args.selectedScanRoots ?? []);
129
+ const selectedScanRootSet = new Set(selectedScanRoots);
130
+ const sourceRoots = args.sourceRoots ?? defaultLocalProjectSourceRoots({
131
+ cwd: args.cwd,
132
+ homeDir: args.homeDir,
133
+ selectedScanRoots
134
+ });
135
+ const sourceRootMaxDepth = normalizeSourceRootMaxDepth(
136
+ args.sourceRootMaxDepth
137
+ );
138
+ const sourceRootScanDeadlineMs = Date.now() + normalizeSourceRootScanBudgetMs(args.sourceRootScanBudgetMs);
139
+ const createSourceRootScanBudget = () => ({
140
+ deadlineMs: sourceRootScanDeadlineMs,
141
+ maxDirectoryVisits: normalizeSourceRootMaxDirectoryVisits(
142
+ args.sourceRootMaxDirectoryVisits
143
+ ),
144
+ maxProjectEnrichments: normalizeSourceRootMaxProjectEnrichments(
145
+ args.sourceRootMaxProjectEnrichments
146
+ ),
147
+ directoryVisits: 0,
148
+ projectEnrichments: 0,
149
+ exhausted: false
144
150
  });
145
- const sourceRoots = args.sourceRoots ?? defaultLocalProjectSourceRoots(args.cwd);
146
- let placesSearched = 1;
151
+ const defaultSourceRootScanBudget = createSourceRootScanBudget();
152
+ let placesSearched = 0;
147
153
  const scanBudgetMs = projectScanBudgetMs();
148
154
  for (const sourceRoot of sourceRoots) {
149
155
  if (Date.now() - startedAtMs >= scanBudgetMs) {
@@ -158,6 +164,19 @@ function discoverCurrentGitProject(args) {
158
164
  continue;
159
165
  }
160
166
  const sourceRootStartedAtMs = Date.now();
167
+ const isSelectedScanRoot = selectedScanRootSet.has(sourceRoot);
168
+ const sourceRootScanBudget = isSelectedScanRoot ? createSourceRootScanBudget() : defaultSourceRootScanBudget;
169
+ if (isSourceRootScanBudgetExhausted(sourceRootScanBudget)) {
170
+ searchStats.push({
171
+ place: sourceRoot,
172
+ source: "git",
173
+ duration_ms: Date.now() - sourceRootStartedAtMs,
174
+ result_count: 0,
175
+ scan_budget_exhausted: true
176
+ });
177
+ placesSearched += 1;
178
+ continue;
179
+ }
161
180
  const rootResponsive = isPlaceResponsive(sourceRoot, responsiveness);
162
181
  if (!rootResponsive) {
163
182
  searchStats.push({
@@ -170,25 +189,60 @@ function discoverCurrentGitProject(args) {
170
189
  placesSearched += 1;
171
190
  continue;
172
191
  }
173
- if (!isDirectory(sourceRoot)) {
192
+ const directoryExists2 = isDirectory(sourceRoot);
193
+ if (!directoryExists2) {
194
+ if (isSelectedScanRoot) {
195
+ searchStats.push({
196
+ place: sourceRoot,
197
+ source: "git",
198
+ duration_ms: Date.now() - sourceRootStartedAtMs,
199
+ result_count: 0,
200
+ missing_directory: true
201
+ });
202
+ placesSearched += 1;
203
+ }
204
+ continue;
205
+ }
206
+ const foundInRoot = discoverGitWorktreesInSourceRoot(
207
+ sourceRoot,
208
+ sourceRootMaxDepth,
209
+ responsiveness,
210
+ sourceRootScanBudget
211
+ );
212
+ const enrichedProjectCount = enrichSourceRootProjects(
213
+ candidates,
214
+ foundInRoot,
215
+ sourceRootScanBudget
216
+ );
217
+ searchStats.push({
218
+ place: sourceRoot,
219
+ source: "git",
220
+ duration_ms: Date.now() - sourceRootStartedAtMs,
221
+ result_count: enrichedProjectCount,
222
+ scan_budget_exhausted: sourceRootScanBudget.exhausted ? true : void 0
223
+ });
224
+ placesSearched += 1;
225
+ if (sourceRootScanBudget.exhausted) {
174
226
  continue;
175
227
  }
176
- const foundInRoot = discoverGitWorktreesInSourceRoot(sourceRoot);
177
- for (const worktreePath of foundInRoot) {
178
- mergeCandidate(
228
+ }
229
+ if (args.includeRuntimeCwdHint !== false) {
230
+ const cwdResponsive = isPlaceResponsive(args.cwd, responsiveness);
231
+ const gitStartedAtMs = Date.now();
232
+ const topLevel = cwdResponsive ? gitOutput(args.cwd, ["rev-parse", "--show-toplevel"]) : void 0;
233
+ const gitDurationMs = Date.now() - gitStartedAtMs;
234
+ if (topLevel !== void 0) {
235
+ mergeRuntimeCwdCandidate(
179
236
  candidates,
180
- projectCandidateFromWorktree(
181
- worktreePath,
182
- "local_source_root",
183
- topLevel
184
- )
237
+ projectCandidateFromWorktree(topLevel, "runtime_cwd_hint", topLevel)
185
238
  );
186
239
  }
187
240
  searchStats.push({
188
- place: sourceRoot,
241
+ place: args.cwd,
189
242
  source: "git",
190
- duration_ms: Date.now() - sourceRootStartedAtMs,
191
- result_count: foundInRoot.length
243
+ duration_ms: gitDurationMs,
244
+ result_count: topLevel === void 0 ? 0 : 1,
245
+ skipped_unresponsive: cwdResponsive ? void 0 : true
192
246
  });
193
247
  placesSearched += 1;
194
248
  }
@@ -261,16 +315,60 @@ function mergeCandidate(candidates, candidate) {
261
315
  const existing = candidates.get(candidate.importKey);
262
316
  if (existing === void 0 || shouldReplaceCandidate(existing, candidate)) {
263
317
  candidates.set(candidate.importKey, candidate);
318
+ return;
319
+ }
320
+ const currentWorktreePath = candidate.metadata.current_worktree_path;
321
+ if (existing.metadata.current_worktree_path === void 0 && typeof currentWorktreePath === "string") {
322
+ candidates.set(candidate.importKey, {
323
+ ...existing,
324
+ metadata: {
325
+ ...existing.metadata,
326
+ current_worktree_path: currentWorktreePath
327
+ }
328
+ });
329
+ }
330
+ }
331
+ function mergeRuntimeCwdCandidate(candidates, candidate) {
332
+ if (candidate === void 0) {
333
+ return;
334
+ }
335
+ const existing = candidates.get(candidate.importKey);
336
+ if (existing !== void 0 && existing.metadata.discovery_method === "local_source_root" && existing.path !== candidate.path) {
337
+ candidates.set(candidate.importKey, {
338
+ ...candidate,
339
+ metadata: {
340
+ ...candidate.metadata,
341
+ discovery_method: "local_source_root"
342
+ }
343
+ });
344
+ return;
264
345
  }
346
+ mergeCandidate(candidates, candidate);
347
+ }
348
+ function enrichSourceRootProjects(candidates, worktreePaths, scanBudget) {
349
+ return worktreePaths.reduce((enrichedCount, worktreePath) => {
350
+ if (!consumeSourceRootProjectEnrichmentBudget(scanBudget)) {
351
+ return enrichedCount;
352
+ }
353
+ const candidate = projectCandidateFromWorktree(
354
+ worktreePath,
355
+ "local_source_root"
356
+ );
357
+ if (candidate === void 0) {
358
+ return enrichedCount;
359
+ }
360
+ mergeCandidate(candidates, candidate);
361
+ return enrichedCount + 1;
362
+ }, 0);
265
363
  }
266
364
  function shouldReplaceCandidate(existing, candidate) {
267
365
  const existingMethod = existing.metadata.discovery_method;
268
366
  const candidateMethod = candidate.metadata.discovery_method;
269
- if (existingMethod === "local_current_git") {
270
- return false;
367
+ if (existingMethod === "runtime_cwd_hint") {
368
+ return candidateMethod !== "runtime_cwd_hint";
271
369
  }
272
- if (candidateMethod === "local_current_git") {
273
- return true;
370
+ if (candidateMethod === "runtime_cwd_hint") {
371
+ return false;
274
372
  }
275
373
  const existingActivity = Date.parse(existing.activityAt ?? "");
276
374
  const candidateActivity = Date.parse(candidate.activityAt ?? "");
@@ -296,32 +394,91 @@ function compareProjectCandidates(left, right) {
296
394
  }
297
395
  return left.displayName.localeCompare(right.displayName);
298
396
  }
299
- function discoverGitWorktreesInSourceRoot(sourceRoot) {
397
+ function discoverGitWorktreesInSourceRoot(sourceRoot, maxDepth, responsiveness, scanBudget) {
300
398
  if (!isDirectory(sourceRoot)) {
301
399
  return [];
302
400
  }
401
+ return uniqueStrings(
402
+ discoverGitWorktreesBelow(sourceRoot, maxDepth, responsiveness, scanBudget)
403
+ ).sort();
404
+ }
405
+ function discoverGitWorktreesBelow(directory, depthRemaining, responsiveness, scanBudget) {
406
+ if (!consumeSourceRootScanBudget(scanBudget)) {
407
+ return [];
408
+ }
409
+ if (depthRemaining < 0) {
410
+ return [];
411
+ }
412
+ if (isGitWorktreeRoot(directory)) {
413
+ return [directory];
414
+ }
415
+ if (depthRemaining === 0) {
416
+ return [];
417
+ }
418
+ let entries;
303
419
  try {
304
- return readdirSync(sourceRoot, { withFileTypes: true }).flatMap((entry) => {
305
- if (!entry.isDirectory()) {
306
- return [];
307
- }
308
- const candidatePath = join(sourceRoot, entry.name);
309
- return isGitWorktreeRoot(candidatePath) ? [candidatePath] : [];
310
- }).sort();
420
+ entries = readdirSync(directory, { withFileTypes: true });
311
421
  } catch (_error) {
312
422
  return [];
313
423
  }
424
+ return entries.flatMap((entry) => {
425
+ if (!entry.isDirectory()) {
426
+ return [];
427
+ }
428
+ if (ignoredSourceRootDirectoryNames.has(entry.name)) {
429
+ return [];
430
+ }
431
+ const childPath = join(directory, entry.name);
432
+ if (isSourceRootScanBudgetExhausted(scanBudget)) {
433
+ return [];
434
+ }
435
+ if (!isPlaceResponsive(childPath, responsiveness)) {
436
+ return [];
437
+ }
438
+ return discoverGitWorktreesBelow(
439
+ childPath,
440
+ depthRemaining - 1,
441
+ responsiveness,
442
+ scanBudget
443
+ );
444
+ });
445
+ }
446
+ function consumeSourceRootScanBudget(scanBudget) {
447
+ if (isSourceRootScanBudgetExhausted(scanBudget)) {
448
+ return false;
449
+ }
450
+ scanBudget.directoryVisits += 1;
451
+ return true;
452
+ }
453
+ function consumeSourceRootProjectEnrichmentBudget(scanBudget) {
454
+ if (Date.now() >= scanBudget.deadlineMs || scanBudget.projectEnrichments >= scanBudget.maxProjectEnrichments) {
455
+ scanBudget.exhausted = true;
456
+ return false;
457
+ }
458
+ scanBudget.projectEnrichments += 1;
459
+ return true;
460
+ }
461
+ function isSourceRootScanBudgetExhausted(scanBudget) {
462
+ if (scanBudget.exhausted) {
463
+ return true;
464
+ }
465
+ if (Date.now() >= scanBudget.deadlineMs || scanBudget.directoryVisits >= scanBudget.maxDirectoryVisits || scanBudget.projectEnrichments >= scanBudget.maxProjectEnrichments) {
466
+ scanBudget.exhausted = true;
467
+ return true;
468
+ }
469
+ return false;
314
470
  }
315
471
  function isGitWorktreeRoot(path2) {
316
472
  return existsSync(join(path2, ".git"));
317
473
  }
318
- function defaultLocalProjectSourceRoots(cwd) {
319
- if (cwd === dirname(cwd)) {
474
+ function defaultLocalProjectSourceRoots(args) {
475
+ if (args.cwd === dirname(args.cwd)) {
320
476
  return [];
321
477
  }
322
478
  return uniqueStrings([
323
- ...ancestorSourceRoots(cwd),
324
- ...homeSourceRoots(),
479
+ ...homeSourceRoots(args.homeDir),
480
+ ...args.selectedScanRoots ?? [],
481
+ ...ancestorSourceRoots(args.cwd),
325
482
  ...externalVolumeSourceRoots()
326
483
  ]);
327
484
  }
@@ -344,8 +501,8 @@ function ancestorSourceRoots(cwd) {
344
501
  }
345
502
  return roots;
346
503
  }
347
- function homeSourceRoots() {
348
- const home = homedir();
504
+ function homeSourceRoots(homeDir) {
505
+ const home = homeDir ?? homedir();
349
506
  return [
350
507
  "code",
351
508
  "src",
@@ -385,6 +542,42 @@ function isDirectory(path2) {
385
542
  return false;
386
543
  }
387
544
  }
545
+ function normalizeSourceRootMaxDepth(value) {
546
+ if (value === void 0) {
547
+ return defaultSourceRootMaxDepth;
548
+ }
549
+ if (!Number.isFinite(value) || value < 0) {
550
+ return defaultSourceRootMaxDepth;
551
+ }
552
+ return Math.floor(value);
553
+ }
554
+ function normalizeSourceRootScanBudgetMs(value) {
555
+ if (value === void 0) {
556
+ return defaultSourceRootScanBudgetMs;
557
+ }
558
+ if (!Number.isFinite(value) || value <= 0) {
559
+ return defaultSourceRootScanBudgetMs;
560
+ }
561
+ return Math.floor(value);
562
+ }
563
+ function normalizeSourceRootMaxDirectoryVisits(value) {
564
+ if (value === void 0) {
565
+ return defaultSourceRootMaxDirectoryVisits;
566
+ }
567
+ if (!Number.isFinite(value) || value <= 0) {
568
+ return defaultSourceRootMaxDirectoryVisits;
569
+ }
570
+ return Math.floor(value);
571
+ }
572
+ function normalizeSourceRootMaxProjectEnrichments(value) {
573
+ if (value === void 0) {
574
+ return defaultSourceRootMaxProjectEnrichments;
575
+ }
576
+ if (!Number.isFinite(value) || value <= 0) {
577
+ return defaultSourceRootMaxProjectEnrichments;
578
+ }
579
+ return Math.floor(value);
580
+ }
388
581
  function uniqueStrings(values) {
389
582
  return Array.from(new Set(values));
390
583
  }
@@ -432,13 +625,29 @@ function compactJsonObject(value) {
432
625
  })
433
626
  );
434
627
  }
435
- var worktreePathSampleLimit, gitSpawnTimeoutMs, defaultProjectScanBudgetMs;
628
+ var worktreePathSampleLimit, gitSpawnTimeoutMs, defaultSourceRootMaxDepth, defaultSourceRootScanBudgetMs, defaultSourceRootMaxDirectoryVisits, defaultSourceRootMaxProjectEnrichments, ignoredSourceRootDirectoryNames, defaultProjectScanBudgetMs;
436
629
  var init_onboardingProjectDiscovery = __esm({
437
630
  "src/onboardingProjectDiscovery.ts"() {
438
631
  "use strict";
439
632
  init_onboardingPlaceResponsiveness();
440
633
  worktreePathSampleLimit = 25;
441
634
  gitSpawnTimeoutMs = 4e3;
635
+ defaultSourceRootMaxDepth = 4;
636
+ defaultSourceRootScanBudgetMs = 2e4;
637
+ defaultSourceRootMaxDirectoryVisits = 5e3;
638
+ defaultSourceRootMaxProjectEnrichments = 100;
639
+ ignoredSourceRootDirectoryNames = /* @__PURE__ */ new Set([
640
+ ".cache",
641
+ ".git",
642
+ ".next",
643
+ ".pnpm-store",
644
+ ".turbo",
645
+ "build",
646
+ "dist",
647
+ "Library",
648
+ "node_modules",
649
+ "target"
650
+ ]);
442
651
  defaultProjectScanBudgetMs = 2e4;
443
652
  }
444
653
  });
@@ -1115,7 +1324,27 @@ async function runOnboardingDiscoveryChildRole(kind, stdin, stdout) {
1115
1324
  if (kind === "projects") {
1116
1325
  const { discoverCurrentGitProject: discoverCurrentGitProject2 } = await Promise.resolve().then(() => (init_onboardingProjectDiscovery(), onboardingProjectDiscovery_exports));
1117
1326
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
1118
- stdout.write(JSON.stringify(discoverCurrentGitProject2({ cwd })));
1327
+ const homeDir = typeof args.homeDir === "string" ? args.homeDir : void 0;
1328
+ const sourceRoots = Array.isArray(args.sourceRoots) ? args.sourceRoots.filter(
1329
+ (path2) => typeof path2 === "string"
1330
+ ) : void 0;
1331
+ const selectedScanRoots = Array.isArray(args.selectedScanRoots) ? args.selectedScanRoots.filter(
1332
+ (path2) => typeof path2 === "string"
1333
+ ) : void 0;
1334
+ const sourceRootMaxDepth = typeof args.sourceRootMaxDepth === "number" ? args.sourceRootMaxDepth : void 0;
1335
+ const includeRuntimeCwdHint = typeof args.includeRuntimeCwdHint === "boolean" ? args.includeRuntimeCwdHint : void 0;
1336
+ stdout.write(
1337
+ JSON.stringify(
1338
+ discoverCurrentGitProject2({
1339
+ cwd,
1340
+ homeDir,
1341
+ sourceRoots,
1342
+ selectedScanRoots,
1343
+ includeRuntimeCwdHint,
1344
+ sourceRootMaxDepth
1345
+ })
1346
+ )
1347
+ );
1119
1348
  return 0;
1120
1349
  }
1121
1350
  if (kind === "conversations") {
@@ -7269,6 +7498,18 @@ function dequeuePendingKandanMessage(queue) {
7269
7498
  compactPendingKandanMessageQueue(queue);
7270
7499
  return message;
7271
7500
  }
7501
+ function drainAllPendingKandanMessages(queue) {
7502
+ const drained = [];
7503
+ for (let index = queue.head; index < queue.items.length; index += 1) {
7504
+ const message = queue.items[index];
7505
+ if (message !== void 0) {
7506
+ drained.push(message);
7507
+ }
7508
+ }
7509
+ queue.items = [];
7510
+ queue.head = 0;
7511
+ return drained;
7512
+ }
7272
7513
  function requeuePendingKandanMessageFront(queue, message) {
7273
7514
  if (queue.head > 0) {
7274
7515
  queue.head = queue.head - 1;
@@ -7278,7 +7519,10 @@ function requeuePendingKandanMessageFront(queue, message) {
7278
7519
  queue.items.unshift(message);
7279
7520
  }
7280
7521
  function interruptPendingKandanMessages(queue, throughSeq) {
7281
- const result = interruptQueuedMessages(pendingKandanMessages(queue), throughSeq);
7522
+ const result = interruptQueuedMessages(
7523
+ pendingKandanMessages(queue),
7524
+ throughSeq
7525
+ );
7282
7526
  if (result.ok) {
7283
7527
  replacePendingKandanMessages(queue, result.queue);
7284
7528
  }
@@ -8997,6 +9241,7 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
8997
9241
  failedSeqs: /* @__PURE__ */ new Map(),
8998
9242
  drainInFlight: false,
8999
9243
  drainRequested: false,
9244
+ deadWorkerRebuildAttempts: 0,
9000
9245
  boundStartTimeout: false,
9001
9246
  fusedQueuedSeqs: /* @__PURE__ */ new Map(),
9002
9247
  reconnectRetryTimer: void 0,
@@ -10568,6 +10813,9 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
10568
10813
  }
10569
10814
  }
10570
10815
  async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10816
+ if (await rebuildIfCodexWorkerDead(args, state)) {
10817
+ return;
10818
+ }
10571
10819
  if (drainKandanMessageQueueBlocked(args, state)) {
10572
10820
  return;
10573
10821
  }
@@ -10649,6 +10897,14 @@ async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10649
10897
  await drainKandanMessageQueue(args, state, payloadContext);
10650
10898
  }
10651
10899
  } catch (error) {
10900
+ if (codexWorkerIsDead(args)) {
10901
+ await stopCodexTyping(args, state);
10902
+ requeuePendingKandanMessageFront(state.queue, next);
10903
+ state.turn = { status: "idle" };
10904
+ clearActiveProcessingState(state, next.seq);
10905
+ await rebuildIfCodexWorkerDead(args, state);
10906
+ return;
10907
+ }
10652
10908
  if (isRecoverableCodexThreadError(error) && state.kandanThreadId !== void 0) {
10653
10909
  const oldCodexThreadId = state.codexThreadId;
10654
10910
  try {
@@ -10709,6 +10965,83 @@ async function drainKandanMessageQueueOnce(args, state, payloadContext) {
10709
10965
  }
10710
10966
  }
10711
10967
  }
10968
+ function codexWorkerIsDead(args) {
10969
+ return args.codex.isClosed?.() === true;
10970
+ }
10971
+ async function rebuildIfCodexWorkerDead(args, state) {
10972
+ if (!codexWorkerIsDead(args)) {
10973
+ return false;
10974
+ }
10975
+ const pending = drainAllPendingKandanMessages(state.queue);
10976
+ if (pending.length === 0) {
10977
+ return false;
10978
+ }
10979
+ const { codexThreadId, kandanThreadId } = state;
10980
+ const rebuild = args.options.rebuildOnDeadCodexWorker;
10981
+ args.log("kandan.queue_drain_dead_worker_rebuild", {
10982
+ codex_thread_id: codexThreadId ?? null,
10983
+ kandan_thread_id: kandanThreadId ?? null,
10984
+ pending_count: pending.length,
10985
+ attempt: state.deadWorkerRebuildAttempts + 1,
10986
+ rebuild_available: rebuild !== void 0
10987
+ });
10988
+ const failPending = async (reason) => {
10989
+ for (const message of pending) {
10990
+ try {
10991
+ await publishQueuedMessageState(args, state, message, {
10992
+ status: "failed",
10993
+ reason
10994
+ });
10995
+ } catch (error) {
10996
+ args.log("kandan.queue_drain_dead_worker_fail_publish_failed", {
10997
+ codex_thread_id: codexThreadId ?? null,
10998
+ seq: message.seq,
10999
+ message: error instanceof Error ? error.message : String(error)
11000
+ });
11001
+ }
11002
+ }
11003
+ };
11004
+ if (rebuild === void 0 || codexThreadId === void 0 || kandanThreadId === void 0) {
11005
+ await failPending(
11006
+ "Codex worker for this thread exited and could not be respawned to resume the conversation."
11007
+ );
11008
+ return true;
11009
+ }
11010
+ if (state.deadWorkerRebuildAttempts >= maxDeadWorkerRebuildAttempts) {
11011
+ args.log("kandan.queue_drain_dead_worker_rebuild_exhausted", {
11012
+ codex_thread_id: codexThreadId,
11013
+ kandan_thread_id: kandanThreadId,
11014
+ attempts: state.deadWorkerRebuildAttempts
11015
+ });
11016
+ await failPending(
11017
+ "Codex worker for this thread kept exiting on resume; giving up after several respawn attempts."
11018
+ );
11019
+ return true;
11020
+ }
11021
+ state.deadWorkerRebuildAttempts += 1;
11022
+ state.closed = true;
11023
+ let rebuilt = false;
11024
+ try {
11025
+ rebuilt = await rebuild({
11026
+ codexThreadId,
11027
+ kandanThreadId,
11028
+ pendingMessages: pending
11029
+ });
11030
+ } catch (error) {
11031
+ args.log("kandan.queue_drain_dead_worker_rebuild_failed", {
11032
+ codex_thread_id: codexThreadId,
11033
+ kandan_thread_id: kandanThreadId,
11034
+ message: error instanceof Error ? error.message : String(error)
11035
+ });
11036
+ rebuilt = false;
11037
+ }
11038
+ if (!rebuilt) {
11039
+ await failPending(
11040
+ "Codex worker for this thread exited and could not be respawned to resume the conversation."
11041
+ );
11042
+ }
11043
+ return true;
11044
+ }
10712
11045
  function drainKandanMessageQueueBlocked(args, state) {
10713
11046
  if (state.closed) {
10714
11047
  logQueuedDrainBlocked(args, state, "session_closed");
@@ -11720,7 +12053,7 @@ async function pushOptional(kandan, topic, event, payload, log2) {
11720
12053
  });
11721
12054
  }
11722
12055
  }
11723
- var codexTypingHeartbeatMs, CODEX_START_REQUEST_TIMEOUT_MS, CodexStartRequestTimeoutError, maxTrackedFailedChatEventSeqs, maxChatEventFailureAttempts, maxForwardedTurnIds, maxClaimedKandanMessageKeys, claimedKandanMessageKeys, nextChatJoinRegistrationSerial, defaultReconnectHandlingRetryDelaysMs, defaultTuiInputMirrorRetryDelaysMs, pipelineCloseTimeoutMs;
12056
+ var codexTypingHeartbeatMs, CODEX_START_REQUEST_TIMEOUT_MS, CodexStartRequestTimeoutError, maxTrackedFailedChatEventSeqs, maxChatEventFailureAttempts, maxForwardedTurnIds, maxClaimedKandanMessageKeys, claimedKandanMessageKeys, nextChatJoinRegistrationSerial, defaultReconnectHandlingRetryDelaysMs, defaultTuiInputMirrorRetryDelaysMs, pipelineCloseTimeoutMs, maxDeadWorkerRebuildAttempts;
11724
12057
  var init_channelSession = __esm({
11725
12058
  "src/channelSession.ts"() {
11726
12059
  "use strict";
@@ -11771,6 +12104,7 @@ var init_channelSession = __esm({
11771
12104
  15e3
11772
12105
  ];
11773
12106
  pipelineCloseTimeoutMs = 1e4;
12107
+ maxDeadWorkerRebuildAttempts = 3;
11774
12108
  }
11775
12109
  });
11776
12110
 
@@ -19451,7 +19785,7 @@ var linzumiCliVersion, linzumiCliVersionText;
19451
19785
  var init_version = __esm({
19452
19786
  "src/version.ts"() {
19453
19787
  "use strict";
19454
- linzumiCliVersion = "0.0.96-beta";
19788
+ linzumiCliVersion = "0.0.97-beta";
19455
19789
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
19456
19790
  }
19457
19791
  });
@@ -23211,6 +23545,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
23211
23545
  const missingAllowedCwds = {
23212
23546
  value: normalizeAllowedCwds(options.missingAllowedCwds ?? [])
23213
23547
  };
23548
+ const projectDiscoveryCwdIsSelectedRootFromSyncedConfig = { value: false };
23214
23549
  const applyRunnerConfigSync = (config, syncOptions) => {
23215
23550
  const persistedAllowedCwds = replaceAllowedCwdsForLinzumiUrl(
23216
23551
  config.allowedCwds,
@@ -23219,6 +23554,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
23219
23554
  const updatedAllowedCwds = configuredAllowedCwds(persistedAllowedCwds, {
23220
23555
  createMissing: true
23221
23556
  });
23557
+ projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value = projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value || allowedCwdsContainAny(updatedAllowedCwds.allowedCwds, [
23558
+ options.cwd,
23559
+ ...startupAllowedCwds
23560
+ ]);
23222
23561
  allowedCwds.value = normalizeAllowedCwds([
23223
23562
  ...syncOptions.includeStartupAllowedCwds ? startupAllowedCwds : [],
23224
23563
  ...updatedAllowedCwds.allowedCwds
@@ -23305,6 +23644,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
23305
23644
  allowedCwds.value
23306
23645
  ),
23307
23646
  allowedCwdProjects: allowedCwdProjects(allowedCwds.value),
23647
+ onboardingSelectedFolderScan: true,
23308
23648
  portForwarding: liveForwardPorts.size > 0,
23309
23649
  tcpForwarding: true,
23310
23650
  linzumiMcp: true,
@@ -24386,6 +24726,53 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
24386
24726
  );
24387
24727
  }
24388
24728
  const runtimeSettings = startInstanceRuntimeSettings(options, control);
24729
+ const rebuildOnDeadCodexWorker = shouldUseThreadProcesses(options) ? async (rebuildArgs) => {
24730
+ await evictStaleDynamicChannelSession(rebuildArgs.codexThreadId);
24731
+ let rebuilt = false;
24732
+ for (const message of rebuildArgs.pendingMessages) {
24733
+ const reconnectControl = {
24734
+ type: "reconnect_thread",
24735
+ workspace: workspaceSlug,
24736
+ channel: channelSlug,
24737
+ threadId: rebuildArgs.kandanThreadId,
24738
+ codexThreadId: rebuildArgs.codexThreadId,
24739
+ cwd,
24740
+ ...integerValue(control.rootSeq) === void 0 ? {} : { rootSeq: integerValue(control.rootSeq) },
24741
+ sourceSeq: message.seq,
24742
+ workDescription: message.body,
24743
+ ...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
24744
+ ...runtimeSettings.reasoningEffort === void 0 ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
24745
+ ...runtimeSettings.sandbox === void 0 ? {} : { sandbox: runtimeSettings.sandbox },
24746
+ ...runtimeSettings.approvalPolicy === void 0 ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
24747
+ ...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
24748
+ };
24749
+ const liveSession = dynamicChannelSessions.get(
24750
+ rebuildArgs.codexThreadId
24751
+ );
24752
+ try {
24753
+ if (liveSession !== void 0 && rebuilt) {
24754
+ await liveSession.startThreadMessageTurn({
24755
+ seq: message.seq,
24756
+ body: message.body,
24757
+ actorSlug: message.actorSlug,
24758
+ actorUserId: message.actorUserId
24759
+ });
24760
+ } else {
24761
+ await startThreadRunnerProcess(reconnectControl, cwd);
24762
+ }
24763
+ rebuilt = true;
24764
+ } catch (error) {
24765
+ log2("runner.thread_dead_worker_rebuild_failed", {
24766
+ codex_thread_id: rebuildArgs.codexThreadId,
24767
+ kandan_thread_id: rebuildArgs.kandanThreadId,
24768
+ seq: message.seq,
24769
+ message: error instanceof Error ? error.message : String(error)
24770
+ });
24771
+ return false;
24772
+ }
24773
+ }
24774
+ return rebuilt;
24775
+ } : void 0;
24389
24776
  const session = await attachChannelSession({
24390
24777
  kandan,
24391
24778
  codex: sessionCodex,
@@ -24400,6 +24787,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
24400
24787
  fetch: options.fetch,
24401
24788
  fast: control.fast ?? options.fast,
24402
24789
  launchTui: false,
24790
+ rebuildOnDeadCodexWorker,
24403
24791
  pipelinePersistDir: commanderOutboxPersistDir(),
24404
24792
  enablePortForwardWatch: true,
24405
24793
  initialForwardPorts: allowedForwardPorts,
@@ -25115,6 +25503,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25115
25503
  }
25116
25504
  );
25117
25505
  }
25506
+ if (control.type === "scan_onboarding_projects") {
25507
+ const selectedScanRoots = onboardingProjectScanRoots(control.paths);
25508
+ return runLocalOnboardingProjectDiscovery(options, log2, {
25509
+ workspaceSlug: stringValue(control.workspace),
25510
+ selectedScanRoots,
25511
+ sourceRoots: selectedScanRoots,
25512
+ workerId: "projects-selected-folders",
25513
+ workerLabel: "Selected folders",
25514
+ startingMessage: "Checking selected folders",
25515
+ emptyMessage: "No Git projects found in selected folders",
25516
+ foundMessage: "Selected folder projects found",
25517
+ failedMessage: "Selected folder project scan failed",
25518
+ currentPlace: selectedScanRoots[0] ?? options.cwd,
25519
+ requireTerminalStatusWrite: true,
25520
+ includeRuntimeCwdHint: false
25521
+ }).catch((error) => {
25522
+ log2("onboarding.project_scan_failed", {
25523
+ runnerId: options.runnerId,
25524
+ message: error instanceof Error ? error.message : String(error)
25525
+ });
25526
+ throw error;
25527
+ });
25528
+ }
25118
25529
  if (control.type === "start_instance") {
25119
25530
  const staleAgeMs = staleStartInstanceAgeMs(control, Date.now());
25120
25531
  if (staleAgeMs !== void 0) {
@@ -25219,7 +25630,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25219
25630
  settleControlDispatch(control.type, () => handleControl(control), ack, log2);
25220
25631
  };
25221
25632
  finishRunnerStartup(
25222
- options,
25633
+ onboardingDiscoveryRunnerOptions(
25634
+ options,
25635
+ allowedCwds.value,
25636
+ missingAllowedCwds.value,
25637
+ projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value
25638
+ ),
25223
25639
  log2,
25224
25640
  pendingControls,
25225
25641
  controlDispatcher,
@@ -29585,6 +30001,14 @@ function onboardingConversationImportSources(value) {
29585
30001
  )
29586
30002
  ).sort();
29587
30003
  }
30004
+ function onboardingProjectScanRoots(value) {
30005
+ return normalizeAllowedCwds(
30006
+ (arrayValue(value) ?? []).flatMap((path2) => {
30007
+ const normalizedPath = stringValue(path2)?.trim();
30008
+ return normalizedPath === void 0 || normalizedPath === "" ? [] : [normalizedPath];
30009
+ })
30010
+ );
30011
+ }
29588
30012
  async function replayLocalOnboardingConversation(args) {
29589
30013
  let replayedChunkCount = 0;
29590
30014
  for (const message of args.messages) {
@@ -30011,27 +30435,42 @@ async function runLocalOnboardingConversationDiscovery(options, log2) {
30011
30435
  );
30012
30436
  }
30013
30437
  }
30014
- async function runLocalOnboardingProjectDiscovery(options, log2) {
30015
- const workspaceSlug = runnerWorkspaceSlug(options);
30438
+ async function runLocalOnboardingProjectDiscovery(options, log2, args = {}) {
30439
+ const workspaceSlug = args.workspaceSlug ?? runnerWorkspaceSlug(options);
30440
+ const selectedScanRoots = args.selectedScanRoots ?? selectedProjectDiscoveryScanRoots(options);
30016
30441
  if (workspaceSlug === void 0) {
30017
30442
  return;
30018
30443
  }
30444
+ const workerId = args.workerId ?? "projects-local-filesystem";
30445
+ const workerLabel = args.workerLabel ?? "Local Git projects";
30446
+ const currentPlace = args.currentPlace ?? selectedScanRoots[0] ?? options.cwd;
30019
30447
  await reportOnboardingDiscoveryStatus(options, log2, {
30020
30448
  phase: "projects",
30021
30449
  status: "starting",
30022
30450
  placesSearched: 0,
30023
- message: "Checking current Git project",
30024
- currentPlace: options.cwd,
30451
+ message: args.startingMessage ?? "Searching local Git projects",
30452
+ currentPlace,
30025
30453
  currentSource: "git",
30454
+ workspaceSlug,
30026
30455
  metadata: {
30027
- worker_id: "projects-local-current",
30028
- worker_label: "Current Git project"
30456
+ worker_id: workerId,
30457
+ worker_label: workerLabel,
30458
+ ...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
30029
30459
  }
30030
30460
  });
30031
30461
  let successfulPostStartWriteCount = 0;
30032
30462
  try {
30033
- const summary = await options.localProjectDiscovery?.() ?? await discoverCurrentGitProjectInChildProcess(
30034
- { cwd: options.cwd },
30463
+ const summary = await options.localProjectDiscovery?.({
30464
+ selectedScanRoots,
30465
+ sourceRoots: args.sourceRoots,
30466
+ includeRuntimeCwdHint: args.includeRuntimeCwdHint
30467
+ }) ?? await discoverCurrentGitProjectInChildProcess(
30468
+ {
30469
+ cwd: options.cwd,
30470
+ selectedScanRoots,
30471
+ sourceRoots: args.sourceRoots,
30472
+ includeRuntimeCwdHint: args.includeRuntimeCwdHint
30473
+ },
30035
30474
  { log: (event, payload) => log2(event, payload) }
30036
30475
  );
30037
30476
  const client = createLinzumiMcpApiClient({
@@ -30061,22 +30500,31 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
30061
30500
  phase: "projects",
30062
30501
  status: "completed",
30063
30502
  placesSearched: summary.placesSearched,
30064
- message: summary.candidates.length === 0 ? "Current folder is not a Git project" : "Current Git project found",
30065
- currentPlace: options.cwd,
30503
+ message: summary.candidates.length === 0 ? args.emptyMessage ?? "No local Git projects found" : args.foundMessage ?? "Local Git projects found",
30504
+ currentPlace,
30066
30505
  currentSource: "git",
30506
+ workspaceSlug,
30067
30507
  metadata: {
30068
- worker_id: "projects-local-current",
30069
- worker_label: "Current Git project",
30508
+ worker_id: workerId,
30509
+ worker_label: workerLabel,
30070
30510
  duration_ms: summary.durationMs,
30071
30511
  reported_count: summary.candidates.length,
30072
30512
  failed_report_count: failedReportCount,
30513
+ ...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots },
30073
30514
  search_stats: summary.searchStats
30074
30515
  }
30075
30516
  };
30076
- successfulPostStartWriteCount = await addStatusWriteResult(
30077
- successfulPostStartWriteCount,
30078
- reportOnboardingDiscoveryStatus(options, log2, completedReport)
30517
+ const completedStatusWritten = await reportOnboardingDiscoveryStatus(
30518
+ options,
30519
+ log2,
30520
+ completedReport
30079
30521
  );
30522
+ if (!completedStatusWritten && args.requireTerminalStatusWrite === true) {
30523
+ throw new Error(
30524
+ "onboarding project discovery terminal status write failed"
30525
+ );
30526
+ }
30527
+ successfulPostStartWriteCount = completedStatusWritten ? successfulPostStartWriteCount + 1 : successfulPostStartWriteCount;
30080
30528
  log2("onboarding.local_project_discovery_completed", {
30081
30529
  runnerId: options.runnerId,
30082
30530
  workspace: workspaceSlug,
@@ -30094,19 +30542,26 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
30094
30542
  phase: "projects",
30095
30543
  status: "failed",
30096
30544
  placesSearched: 0,
30097
- message: "Current Git project scan failed",
30098
- currentPlace: options.cwd,
30545
+ message: args.failedMessage ?? "Local Git project scan failed",
30546
+ currentPlace,
30099
30547
  currentSource: "git",
30100
30548
  lastError: message,
30549
+ workspaceSlug,
30101
30550
  metadata: {
30102
- worker_id: "projects-local-current",
30103
- worker_label: "Current Git project"
30551
+ worker_id: workerId,
30552
+ worker_label: workerLabel,
30553
+ ...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
30104
30554
  }
30105
30555
  };
30106
- successfulPostStartWriteCount = await addStatusWriteResult(
30107
- successfulPostStartWriteCount,
30108
- reportOnboardingDiscoveryStatus(options, log2, failedReport)
30556
+ const failedStatusWritten = await reportOnboardingDiscoveryStatus(
30557
+ options,
30558
+ log2,
30559
+ failedReport
30109
30560
  );
30561
+ if (!failedStatusWritten && args.requireTerminalStatusWrite === true) {
30562
+ throw error;
30563
+ }
30564
+ successfulPostStartWriteCount = failedStatusWritten ? successfulPostStartWriteCount + 1 : successfulPostStartWriteCount;
30110
30565
  log2("onboarding.local_project_discovery_failed", {
30111
30566
  runnerId: options.runnerId,
30112
30567
  workspace: workspaceSlug,
@@ -30118,11 +30573,45 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
30118
30573
  );
30119
30574
  }
30120
30575
  }
30576
+ function selectedProjectDiscoveryScanRoots(options) {
30577
+ const cwd = options.cwd.trim();
30578
+ const implicitCwdAliases = new Set(implicitCwdStringAliases(cwd));
30579
+ for (const alias of options.projectDiscoveryImplicitCwdAliases ?? []) {
30580
+ implicitCwdAliases.add(alias);
30581
+ }
30582
+ const allowedCwds = options.projectDiscoveryCwdIsSelectedRoot === true ? options.allowedCwds : options.allowedCwds.filter(
30583
+ (allowedCwd) => !implicitCwdAliases.has(allowedCwd)
30584
+ );
30585
+ return normalizeAllowedCwds([
30586
+ ...allowedCwds,
30587
+ ...options.missingAllowedCwds ?? []
30588
+ ]);
30589
+ }
30590
+ function implicitCwdStringAliases(cwd) {
30591
+ if (cwd === "") {
30592
+ return [];
30593
+ }
30594
+ return normalizeAllowedCwds([cwd, resolve8(expandUserPath(cwd))]);
30595
+ }
30596
+ function onboardingDiscoveryRunnerOptions(options, allowedCwds, missingAllowedCwds, projectDiscoveryCwdIsSelectedRootFromSyncedConfig) {
30597
+ return {
30598
+ ...options,
30599
+ allowedCwds,
30600
+ missingAllowedCwds,
30601
+ projectDiscoveryCwdIsSelectedRoot: options.projectDiscoveryCwdIsSelectedRoot === true || projectDiscoveryCwdIsSelectedRootFromSyncedConfig
30602
+ };
30603
+ }
30604
+ function allowedCwdsContainAny(allowedCwds, candidates) {
30605
+ const allowed = new Set(normalizeAllowedCwds(allowedCwds));
30606
+ return normalizeAllowedCwds(candidates).some(
30607
+ (candidate) => allowed.has(candidate)
30608
+ );
30609
+ }
30121
30610
  function shouldStartOnboardingDiscoveryAgents(options, workspaceSlug = runnerWorkspaceSlug(options)) {
30122
30611
  return workspaceSlug !== void 0 && options.threadProcess === void 0 && options.onboardingDiscovery === "start";
30123
30612
  }
30124
30613
  async function reportOnboardingDiscoveryStatus(options, log2, args) {
30125
- const workspaceSlug = runnerWorkspaceSlug(options);
30614
+ const workspaceSlug = args.workspaceSlug ?? runnerWorkspaceSlug(options);
30126
30615
  if (workspaceSlug === void 0) {
30127
30616
  return false;
30128
30617
  }
@@ -69207,6 +69696,7 @@ async function parseStartRunnerArgs(args, deps = {
69207
69696
  fast: values.get("fast") === true,
69208
69697
  logFile: stringValue8(values, "log-file"),
69209
69698
  allowedCwds,
69699
+ projectDiscoveryCwdIsSelectedRoot: true,
69210
69700
  allowedForwardPorts: parseAllowedPortList(
69211
69701
  stringValue8(values, "forward-port")
69212
69702
  ),
@@ -69274,6 +69764,7 @@ async function parseAgentRunnerArgs(args, deps = {
69274
69764
  parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
69275
69765
  ) : requestedCwdValue === void 0 ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
69276
69766
  const cwd = allowedCwds[0] ?? requestedCwd;
69767
+ const projectDiscoveryCwdIsSelectedRoot = values.has("allowed-cwd") || requestedCwdValue === void 0 && configuredAllowedCwds2.allowedCwds.length > 0;
69277
69768
  const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
69278
69769
  const customCodeServerBin = stringValue8(values, "code-server-bin");
69279
69770
  const initialDependencyStatus = await deps.buildDependencyStatus({
@@ -69313,6 +69804,8 @@ async function parseAgentRunnerArgs(args, deps = {
69313
69804
  logFile: stringValue8(values, "log-file"),
69314
69805
  allowedCwds,
69315
69806
  missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
69807
+ projectDiscoveryCwdIsSelectedRoot,
69808
+ projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : allowedCwds,
69316
69809
  allowedForwardPorts: parseAllowedPortList(
69317
69810
  stringValue8(values, "forward-port")
69318
69811
  ),
@@ -69436,6 +69929,9 @@ async function parseRunnerArgs(args, deps = {
69436
69929
  const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
69437
69930
  parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
69438
69931
  ) : [...localConfiguredAllowedCwds.allowedCwds];
69932
+ const projectDiscoveryCwdIsSelectedRoot = cwdAllowedCwds.some(
69933
+ (cwdAllowedCwd) => configuredAllowedCwds2.includes(cwdAllowedCwd)
69934
+ );
69439
69935
  const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
69440
69936
  const customCodeServerBin = stringValue8(values, "code-server-bin");
69441
69937
  const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
@@ -69490,6 +69986,8 @@ async function parseRunnerArgs(args, deps = {
69490
69986
  /* @__PURE__ */ new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])
69491
69987
  ),
69492
69988
  missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
69989
+ projectDiscoveryCwdIsSelectedRoot,
69990
+ projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : cwdAllowedCwds,
69493
69991
  allowedForwardPorts: parseAllowedPortList(
69494
69992
  stringValue8(values, "forward-port")
69495
69993
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.96-beta",
3
+ "version": "0.0.97-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": {