@linzumi/cli 0.0.95-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.
- package/README.md +1 -1
- package/dist/index.js +625 -78
- package/package.json +1 -1
package/README.md
CHANGED
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
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
144
134
|
});
|
|
145
|
-
const
|
|
146
|
-
|
|
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
|
|
150
|
+
});
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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:
|
|
241
|
+
place: args.cwd,
|
|
189
242
|
source: "git",
|
|
190
|
-
duration_ms:
|
|
191
|
-
result_count:
|
|
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
|
+
});
|
|
264
329
|
}
|
|
265
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;
|
|
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);
|
|
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 === "
|
|
270
|
-
return
|
|
367
|
+
if (existingMethod === "runtime_cwd_hint") {
|
|
368
|
+
return candidateMethod !== "runtime_cwd_hint";
|
|
271
369
|
}
|
|
272
|
-
if (candidateMethod === "
|
|
273
|
-
return
|
|
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
|
-
|
|
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(
|
|
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
|
-
...
|
|
324
|
-
...
|
|
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
|
-
|
|
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(
|
|
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.
|
|
19788
|
+
linzumiCliVersion = "0.0.97-beta";
|
|
19455
19789
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19456
19790
|
}
|
|
19457
19791
|
});
|
|
@@ -22340,6 +22674,11 @@ function connectThreadCodexWorkerIpc(child) {
|
|
|
22340
22674
|
onRequest: (callback) => {
|
|
22341
22675
|
requestCallbacks.add(callback);
|
|
22342
22676
|
},
|
|
22677
|
+
// The worker child exited/disconnected (onClosed) or close() was called.
|
|
22678
|
+
// A reconnect uses this to detect a DEAD cached client and respawn a fresh
|
|
22679
|
+
// worker instead of send()-ing on a closed channel (which throws "thread
|
|
22680
|
+
// Codex worker IPC client is closed" and strands the resume).
|
|
22681
|
+
isClosed: () => state.closed || child.connected === false,
|
|
22343
22682
|
close: () => {
|
|
22344
22683
|
state.closed = true;
|
|
22345
22684
|
child.off("message", onMessage);
|
|
@@ -23206,6 +23545,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23206
23545
|
const missingAllowedCwds = {
|
|
23207
23546
|
value: normalizeAllowedCwds(options.missingAllowedCwds ?? [])
|
|
23208
23547
|
};
|
|
23548
|
+
const projectDiscoveryCwdIsSelectedRootFromSyncedConfig = { value: false };
|
|
23209
23549
|
const applyRunnerConfigSync = (config, syncOptions) => {
|
|
23210
23550
|
const persistedAllowedCwds = replaceAllowedCwdsForLinzumiUrl(
|
|
23211
23551
|
config.allowedCwds,
|
|
@@ -23214,6 +23554,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23214
23554
|
const updatedAllowedCwds = configuredAllowedCwds(persistedAllowedCwds, {
|
|
23215
23555
|
createMissing: true
|
|
23216
23556
|
});
|
|
23557
|
+
projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value = projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value || allowedCwdsContainAny(updatedAllowedCwds.allowedCwds, [
|
|
23558
|
+
options.cwd,
|
|
23559
|
+
...startupAllowedCwds
|
|
23560
|
+
]);
|
|
23217
23561
|
allowedCwds.value = normalizeAllowedCwds([
|
|
23218
23562
|
...syncOptions.includeStartupAllowedCwds ? startupAllowedCwds : [],
|
|
23219
23563
|
...updatedAllowedCwds.allowedCwds
|
|
@@ -23300,6 +23644,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23300
23644
|
allowedCwds.value
|
|
23301
23645
|
),
|
|
23302
23646
|
allowedCwdProjects: allowedCwdProjects(allowedCwds.value),
|
|
23647
|
+
onboardingSelectedFolderScan: true,
|
|
23303
23648
|
portForwarding: liveForwardPorts.size > 0,
|
|
23304
23649
|
tcpForwarding: true,
|
|
23305
23650
|
linzumiMcp: true,
|
|
@@ -24324,6 +24669,35 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24324
24669
|
);
|
|
24325
24670
|
threadRunnerProcesses.clear();
|
|
24326
24671
|
});
|
|
24672
|
+
const evictStaleDynamicChannelSession = async (codexThreadId, expectClient) => {
|
|
24673
|
+
const session = dynamicChannelSessions.get(codexThreadId);
|
|
24674
|
+
const boundClient = dynamicChannelSessionCodexClients.get(codexThreadId);
|
|
24675
|
+
if (session === void 0) {
|
|
24676
|
+
return false;
|
|
24677
|
+
}
|
|
24678
|
+
if (expectClient !== void 0 && boundClient !== expectClient) {
|
|
24679
|
+
return false;
|
|
24680
|
+
}
|
|
24681
|
+
if (boundClient?.isClosed?.() !== true) {
|
|
24682
|
+
return false;
|
|
24683
|
+
}
|
|
24684
|
+
dynamicChannelSessions.delete(codexThreadId);
|
|
24685
|
+
dynamicChannelSessionCodexClients.delete(codexThreadId);
|
|
24686
|
+
dynamicChannelSessionAttachInFlight.delete(codexThreadId);
|
|
24687
|
+
log2("runner.thread_session_evicted_dead_worker", {
|
|
24688
|
+
codex_thread_id: codexThreadId,
|
|
24689
|
+
kandan_thread_id: session.currentKandanThreadId() ?? null
|
|
24690
|
+
});
|
|
24691
|
+
try {
|
|
24692
|
+
await session.close();
|
|
24693
|
+
} catch (error) {
|
|
24694
|
+
log2("runner.thread_session_evict_close_failed", {
|
|
24695
|
+
codex_thread_id: codexThreadId,
|
|
24696
|
+
message: error instanceof Error ? error.message : String(error)
|
|
24697
|
+
});
|
|
24698
|
+
}
|
|
24699
|
+
return true;
|
|
24700
|
+
};
|
|
24327
24701
|
const attachThreadSessionWithCodex = async (args) => {
|
|
24328
24702
|
const { control, cwd, codexThreadId, sessionCodex } = args;
|
|
24329
24703
|
const portForwardWatcherRootPid = args.portForwardWatcherRootPid;
|
|
@@ -24335,7 +24709,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24335
24709
|
}
|
|
24336
24710
|
const existingSession = dynamicChannelSessions.get(codexThreadId);
|
|
24337
24711
|
if (existingSession !== void 0) {
|
|
24338
|
-
|
|
24712
|
+
const evicted = await evictStaleDynamicChannelSession(codexThreadId);
|
|
24713
|
+
if (!evicted) {
|
|
24714
|
+
return existingSession;
|
|
24715
|
+
}
|
|
24339
24716
|
}
|
|
24340
24717
|
const alreadyAttaching = dynamicChannelSessionAttachInFlight.get(codexThreadId);
|
|
24341
24718
|
if (alreadyAttaching !== void 0) {
|
|
@@ -24349,6 +24726,53 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24349
24726
|
);
|
|
24350
24727
|
}
|
|
24351
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;
|
|
24352
24776
|
const session = await attachChannelSession({
|
|
24353
24777
|
kandan,
|
|
24354
24778
|
codex: sessionCodex,
|
|
@@ -24363,6 +24787,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24363
24787
|
fetch: options.fetch,
|
|
24364
24788
|
fast: control.fast ?? options.fast,
|
|
24365
24789
|
launchTui: false,
|
|
24790
|
+
rebuildOnDeadCodexWorker,
|
|
24366
24791
|
pipelinePersistDir: commanderOutboxPersistDir(),
|
|
24367
24792
|
enablePortForwardWatch: true,
|
|
24368
24793
|
initialForwardPorts: allowedForwardPorts,
|
|
@@ -24641,6 +25066,18 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24641
25066
|
threadRunnerProcesses.delete(kandanThreadId);
|
|
24642
25067
|
}
|
|
24643
25068
|
});
|
|
25069
|
+
handle.onExit?.(() => {
|
|
25070
|
+
if (cleanup.closePromise !== void 0) {
|
|
25071
|
+
return;
|
|
25072
|
+
}
|
|
25073
|
+
for (const [codexThreadId, client] of Array.from(
|
|
25074
|
+
dynamicChannelSessionCodexClients.entries()
|
|
25075
|
+
)) {
|
|
25076
|
+
if (client === threadCodex) {
|
|
25077
|
+
void evictStaleDynamicChannelSession(codexThreadId, threadCodex);
|
|
25078
|
+
}
|
|
25079
|
+
}
|
|
25080
|
+
});
|
|
24644
25081
|
log2("runner.thread_process_started", {
|
|
24645
25082
|
kandanThreadId,
|
|
24646
25083
|
cwd
|
|
@@ -25066,6 +25503,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25066
25503
|
}
|
|
25067
25504
|
);
|
|
25068
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
|
+
}
|
|
25069
25529
|
if (control.type === "start_instance") {
|
|
25070
25530
|
const staleAgeMs = staleStartInstanceAgeMs(control, Date.now());
|
|
25071
25531
|
if (staleAgeMs !== void 0) {
|
|
@@ -25170,7 +25630,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25170
25630
|
settleControlDispatch(control.type, () => handleControl(control), ack, log2);
|
|
25171
25631
|
};
|
|
25172
25632
|
finishRunnerStartup(
|
|
25173
|
-
|
|
25633
|
+
onboardingDiscoveryRunnerOptions(
|
|
25634
|
+
options,
|
|
25635
|
+
allowedCwds.value,
|
|
25636
|
+
missingAllowedCwds.value,
|
|
25637
|
+
projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value
|
|
25638
|
+
),
|
|
25174
25639
|
log2,
|
|
25175
25640
|
pendingControls,
|
|
25176
25641
|
controlDispatcher,
|
|
@@ -29536,6 +30001,14 @@ function onboardingConversationImportSources(value) {
|
|
|
29536
30001
|
)
|
|
29537
30002
|
).sort();
|
|
29538
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
|
+
}
|
|
29539
30012
|
async function replayLocalOnboardingConversation(args) {
|
|
29540
30013
|
let replayedChunkCount = 0;
|
|
29541
30014
|
for (const message of args.messages) {
|
|
@@ -29962,27 +30435,42 @@ async function runLocalOnboardingConversationDiscovery(options, log2) {
|
|
|
29962
30435
|
);
|
|
29963
30436
|
}
|
|
29964
30437
|
}
|
|
29965
|
-
async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
29966
|
-
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);
|
|
29967
30441
|
if (workspaceSlug === void 0) {
|
|
29968
30442
|
return;
|
|
29969
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;
|
|
29970
30447
|
await reportOnboardingDiscoveryStatus(options, log2, {
|
|
29971
30448
|
phase: "projects",
|
|
29972
30449
|
status: "starting",
|
|
29973
30450
|
placesSearched: 0,
|
|
29974
|
-
message: "
|
|
29975
|
-
currentPlace
|
|
30451
|
+
message: args.startingMessage ?? "Searching local Git projects",
|
|
30452
|
+
currentPlace,
|
|
29976
30453
|
currentSource: "git",
|
|
30454
|
+
workspaceSlug,
|
|
29977
30455
|
metadata: {
|
|
29978
|
-
worker_id:
|
|
29979
|
-
worker_label:
|
|
30456
|
+
worker_id: workerId,
|
|
30457
|
+
worker_label: workerLabel,
|
|
30458
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
|
|
29980
30459
|
}
|
|
29981
30460
|
});
|
|
29982
30461
|
let successfulPostStartWriteCount = 0;
|
|
29983
30462
|
try {
|
|
29984
|
-
const summary = await options.localProjectDiscovery?.(
|
|
29985
|
-
|
|
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
|
+
},
|
|
29986
30474
|
{ log: (event, payload) => log2(event, payload) }
|
|
29987
30475
|
);
|
|
29988
30476
|
const client = createLinzumiMcpApiClient({
|
|
@@ -30012,22 +30500,31 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30012
30500
|
phase: "projects",
|
|
30013
30501
|
status: "completed",
|
|
30014
30502
|
placesSearched: summary.placesSearched,
|
|
30015
|
-
message: summary.candidates.length === 0 ?
|
|
30016
|
-
currentPlace
|
|
30503
|
+
message: summary.candidates.length === 0 ? args.emptyMessage ?? "No local Git projects found" : args.foundMessage ?? "Local Git projects found",
|
|
30504
|
+
currentPlace,
|
|
30017
30505
|
currentSource: "git",
|
|
30506
|
+
workspaceSlug,
|
|
30018
30507
|
metadata: {
|
|
30019
|
-
worker_id:
|
|
30020
|
-
worker_label:
|
|
30508
|
+
worker_id: workerId,
|
|
30509
|
+
worker_label: workerLabel,
|
|
30021
30510
|
duration_ms: summary.durationMs,
|
|
30022
30511
|
reported_count: summary.candidates.length,
|
|
30023
30512
|
failed_report_count: failedReportCount,
|
|
30513
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots },
|
|
30024
30514
|
search_stats: summary.searchStats
|
|
30025
30515
|
}
|
|
30026
30516
|
};
|
|
30027
|
-
|
|
30028
|
-
|
|
30029
|
-
|
|
30517
|
+
const completedStatusWritten = await reportOnboardingDiscoveryStatus(
|
|
30518
|
+
options,
|
|
30519
|
+
log2,
|
|
30520
|
+
completedReport
|
|
30030
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;
|
|
30031
30528
|
log2("onboarding.local_project_discovery_completed", {
|
|
30032
30529
|
runnerId: options.runnerId,
|
|
30033
30530
|
workspace: workspaceSlug,
|
|
@@ -30045,19 +30542,26 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30045
30542
|
phase: "projects",
|
|
30046
30543
|
status: "failed",
|
|
30047
30544
|
placesSearched: 0,
|
|
30048
|
-
message: "
|
|
30049
|
-
currentPlace
|
|
30545
|
+
message: args.failedMessage ?? "Local Git project scan failed",
|
|
30546
|
+
currentPlace,
|
|
30050
30547
|
currentSource: "git",
|
|
30051
30548
|
lastError: message,
|
|
30549
|
+
workspaceSlug,
|
|
30052
30550
|
metadata: {
|
|
30053
|
-
worker_id:
|
|
30054
|
-
worker_label:
|
|
30551
|
+
worker_id: workerId,
|
|
30552
|
+
worker_label: workerLabel,
|
|
30553
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
|
|
30055
30554
|
}
|
|
30056
30555
|
};
|
|
30057
|
-
|
|
30058
|
-
|
|
30059
|
-
|
|
30556
|
+
const failedStatusWritten = await reportOnboardingDiscoveryStatus(
|
|
30557
|
+
options,
|
|
30558
|
+
log2,
|
|
30559
|
+
failedReport
|
|
30060
30560
|
);
|
|
30561
|
+
if (!failedStatusWritten && args.requireTerminalStatusWrite === true) {
|
|
30562
|
+
throw error;
|
|
30563
|
+
}
|
|
30564
|
+
successfulPostStartWriteCount = failedStatusWritten ? successfulPostStartWriteCount + 1 : successfulPostStartWriteCount;
|
|
30061
30565
|
log2("onboarding.local_project_discovery_failed", {
|
|
30062
30566
|
runnerId: options.runnerId,
|
|
30063
30567
|
workspace: workspaceSlug,
|
|
@@ -30069,11 +30573,45 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30069
30573
|
);
|
|
30070
30574
|
}
|
|
30071
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
|
+
}
|
|
30072
30610
|
function shouldStartOnboardingDiscoveryAgents(options, workspaceSlug = runnerWorkspaceSlug(options)) {
|
|
30073
30611
|
return workspaceSlug !== void 0 && options.threadProcess === void 0 && options.onboardingDiscovery === "start";
|
|
30074
30612
|
}
|
|
30075
30613
|
async function reportOnboardingDiscoveryStatus(options, log2, args) {
|
|
30076
|
-
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
30614
|
+
const workspaceSlug = args.workspaceSlug ?? runnerWorkspaceSlug(options);
|
|
30077
30615
|
if (workspaceSlug === void 0) {
|
|
30078
30616
|
return false;
|
|
30079
30617
|
}
|
|
@@ -69158,6 +69696,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
69158
69696
|
fast: values.get("fast") === true,
|
|
69159
69697
|
logFile: stringValue8(values, "log-file"),
|
|
69160
69698
|
allowedCwds,
|
|
69699
|
+
projectDiscoveryCwdIsSelectedRoot: true,
|
|
69161
69700
|
allowedForwardPorts: parseAllowedPortList(
|
|
69162
69701
|
stringValue8(values, "forward-port")
|
|
69163
69702
|
),
|
|
@@ -69225,6 +69764,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
69225
69764
|
parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
|
|
69226
69765
|
) : requestedCwdValue === void 0 ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
|
|
69227
69766
|
const cwd = allowedCwds[0] ?? requestedCwd;
|
|
69767
|
+
const projectDiscoveryCwdIsSelectedRoot = values.has("allowed-cwd") || requestedCwdValue === void 0 && configuredAllowedCwds2.allowedCwds.length > 0;
|
|
69228
69768
|
const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
|
|
69229
69769
|
const customCodeServerBin = stringValue8(values, "code-server-bin");
|
|
69230
69770
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
@@ -69264,6 +69804,8 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
69264
69804
|
logFile: stringValue8(values, "log-file"),
|
|
69265
69805
|
allowedCwds,
|
|
69266
69806
|
missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
|
|
69807
|
+
projectDiscoveryCwdIsSelectedRoot,
|
|
69808
|
+
projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : allowedCwds,
|
|
69267
69809
|
allowedForwardPorts: parseAllowedPortList(
|
|
69268
69810
|
stringValue8(values, "forward-port")
|
|
69269
69811
|
),
|
|
@@ -69387,6 +69929,9 @@ async function parseRunnerArgs(args, deps = {
|
|
|
69387
69929
|
const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
|
|
69388
69930
|
parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
|
|
69389
69931
|
) : [...localConfiguredAllowedCwds.allowedCwds];
|
|
69932
|
+
const projectDiscoveryCwdIsSelectedRoot = cwdAllowedCwds.some(
|
|
69933
|
+
(cwdAllowedCwd) => configuredAllowedCwds2.includes(cwdAllowedCwd)
|
|
69934
|
+
);
|
|
69390
69935
|
const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
|
|
69391
69936
|
const customCodeServerBin = stringValue8(values, "code-server-bin");
|
|
69392
69937
|
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
@@ -69441,6 +69986,8 @@ async function parseRunnerArgs(args, deps = {
|
|
|
69441
69986
|
/* @__PURE__ */ new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])
|
|
69442
69987
|
),
|
|
69443
69988
|
missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
|
|
69989
|
+
projectDiscoveryCwdIsSelectedRoot,
|
|
69990
|
+
projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : cwdAllowedCwds,
|
|
69444
69991
|
allowedForwardPorts: parseAllowedPortList(
|
|
69445
69992
|
stringValue8(values, "forward-port")
|
|
69446
69993
|
),
|
package/package.json
CHANGED