@linzumi/cli 0.0.96-beta → 0.0.98-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 +611 -82
- 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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
146
|
-
let placesSearched =
|
|
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
|
+
}
|
|
174
204
|
continue;
|
|
175
205
|
}
|
|
176
|
-
const foundInRoot = discoverGitWorktreesInSourceRoot(
|
|
177
|
-
|
|
178
|
-
|
|
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) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
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
|
+
});
|
|
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 === "
|
|
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
|
|
|
@@ -14371,7 +14705,16 @@ function linzumiMcpServerConfig(options) {
|
|
|
14371
14705
|
...options.toolScope === void 0 || options.toolScope === "all" ? [] : ["--tool-scope", options.toolScope]
|
|
14372
14706
|
],
|
|
14373
14707
|
env: {
|
|
14374
|
-
...options.accessToken === void 0 ? {} : { LINZUMI_MCP_ACCESS_TOKEN: options.accessToken }
|
|
14708
|
+
...options.accessToken === void 0 ? {} : { LINZUMI_MCP_ACCESS_TOKEN: options.accessToken },
|
|
14709
|
+
// The Linzumi metadata MCP is launched by codex as a STDIO child whose
|
|
14710
|
+
// `command` may be the Electron app binary. That binary only behaves as a
|
|
14711
|
+
// plain Node process (and thus boots the MCP server instead of a GUI
|
|
14712
|
+
// shell) when ELECTRON_RUN_AS_NODE=1 is present in its env. Newer codex
|
|
14713
|
+
// sanitizes MCP-child env to an allowlist that drops ELECTRON_RUN_AS_NODE,
|
|
14714
|
+
// so we both (a) pin it here and (b) explicitly forward it via
|
|
14715
|
+
// codexMcpConfigArgs / codexMcpConfigToml below. For a real Node `command`
|
|
14716
|
+
// this flag is a harmless no-op, so we always include it.
|
|
14717
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
14375
14718
|
}
|
|
14376
14719
|
};
|
|
14377
14720
|
}
|
|
@@ -14380,7 +14723,16 @@ function codexMcpConfigArgs(config) {
|
|
|
14380
14723
|
"-c",
|
|
14381
14724
|
`mcp_servers.${config.name}.command=${JSON.stringify(config.command)}`,
|
|
14382
14725
|
"-c",
|
|
14383
|
-
`mcp_servers.${config.name}.args=${tomlStringArray(config.args)}
|
|
14726
|
+
`mcp_servers.${config.name}.args=${tomlStringArray(config.args)}`,
|
|
14727
|
+
// Explicitly forward the server env (ELECTRON_RUN_AS_NODE,
|
|
14728
|
+
// LINZUMI_MCP_ACCESS_TOKEN, …) so codex passes it to the stdio child
|
|
14729
|
+
// regardless of its env-forwarding/sanitization allowlist. Without this,
|
|
14730
|
+
// newer codex strips ELECTRON_RUN_AS_NODE and the Electron-binary `command`
|
|
14731
|
+
// boots a GUI shell that pollutes the MCP stdout JSON-RPC framing.
|
|
14732
|
+
...Object.entries(config.env).flatMap(([key, value]) => [
|
|
14733
|
+
"-c",
|
|
14734
|
+
`mcp_servers.${config.name}.env.${key}=${JSON.stringify(value)}`
|
|
14735
|
+
])
|
|
14384
14736
|
];
|
|
14385
14737
|
}
|
|
14386
14738
|
function linzumiMcpCommandForProcess(processExecPath, scriptPath, execArgv = []) {
|
|
@@ -19451,7 +19803,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
19451
19803
|
var init_version = __esm({
|
|
19452
19804
|
"src/version.ts"() {
|
|
19453
19805
|
"use strict";
|
|
19454
|
-
linzumiCliVersion = "0.0.
|
|
19806
|
+
linzumiCliVersion = "0.0.98-beta";
|
|
19455
19807
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19456
19808
|
}
|
|
19457
19809
|
});
|
|
@@ -23211,6 +23563,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23211
23563
|
const missingAllowedCwds = {
|
|
23212
23564
|
value: normalizeAllowedCwds(options.missingAllowedCwds ?? [])
|
|
23213
23565
|
};
|
|
23566
|
+
const projectDiscoveryCwdIsSelectedRootFromSyncedConfig = { value: false };
|
|
23214
23567
|
const applyRunnerConfigSync = (config, syncOptions) => {
|
|
23215
23568
|
const persistedAllowedCwds = replaceAllowedCwdsForLinzumiUrl(
|
|
23216
23569
|
config.allowedCwds,
|
|
@@ -23219,6 +23572,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23219
23572
|
const updatedAllowedCwds = configuredAllowedCwds(persistedAllowedCwds, {
|
|
23220
23573
|
createMissing: true
|
|
23221
23574
|
});
|
|
23575
|
+
projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value = projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value || allowedCwdsContainAny(updatedAllowedCwds.allowedCwds, [
|
|
23576
|
+
options.cwd,
|
|
23577
|
+
...startupAllowedCwds
|
|
23578
|
+
]);
|
|
23222
23579
|
allowedCwds.value = normalizeAllowedCwds([
|
|
23223
23580
|
...syncOptions.includeStartupAllowedCwds ? startupAllowedCwds : [],
|
|
23224
23581
|
...updatedAllowedCwds.allowedCwds
|
|
@@ -23305,6 +23662,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23305
23662
|
allowedCwds.value
|
|
23306
23663
|
),
|
|
23307
23664
|
allowedCwdProjects: allowedCwdProjects(allowedCwds.value),
|
|
23665
|
+
onboardingSelectedFolderScan: true,
|
|
23308
23666
|
portForwarding: liveForwardPorts.size > 0,
|
|
23309
23667
|
tcpForwarding: true,
|
|
23310
23668
|
linzumiMcp: true,
|
|
@@ -24386,6 +24744,59 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24386
24744
|
);
|
|
24387
24745
|
}
|
|
24388
24746
|
const runtimeSettings = startInstanceRuntimeSettings(options, control);
|
|
24747
|
+
const rebuildOnDeadCodexWorker = shouldUseThreadProcesses(options) ? async (rebuildArgs) => {
|
|
24748
|
+
await evictStaleDynamicChannelSession(rebuildArgs.codexThreadId);
|
|
24749
|
+
let rebuilt = false;
|
|
24750
|
+
for (const message of rebuildArgs.pendingMessages) {
|
|
24751
|
+
const reconnectControl = {
|
|
24752
|
+
type: "reconnect_thread",
|
|
24753
|
+
workspace: workspaceSlug,
|
|
24754
|
+
channel: channelSlug,
|
|
24755
|
+
threadId: rebuildArgs.kandanThreadId,
|
|
24756
|
+
codexThreadId: rebuildArgs.codexThreadId,
|
|
24757
|
+
cwd,
|
|
24758
|
+
// Carry the conversation context through the rebuild-replay so
|
|
24759
|
+
// the respawned worker's developer instructions match what the
|
|
24760
|
+
// server would have replayed (parity with the live reconnect
|
|
24761
|
+
// control built above at channelSession.linzumiContext).
|
|
24762
|
+
...control.linzumiContext === void 0 ? {} : { linzumiContext: control.linzumiContext },
|
|
24763
|
+
...integerValue(control.rootSeq) === void 0 ? {} : { rootSeq: integerValue(control.rootSeq) },
|
|
24764
|
+
sourceSeq: message.seq,
|
|
24765
|
+
workDescription: message.body,
|
|
24766
|
+
...runtimeSettings.model === void 0 ? {} : { model: runtimeSettings.model },
|
|
24767
|
+
...runtimeSettings.reasoningEffort === void 0 ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
|
|
24768
|
+
...runtimeSettings.sandbox === void 0 ? {} : { sandbox: runtimeSettings.sandbox },
|
|
24769
|
+
...runtimeSettings.approvalPolicy === void 0 ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
|
|
24770
|
+
...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
|
|
24771
|
+
};
|
|
24772
|
+
const liveSession = dynamicChannelSessions.get(
|
|
24773
|
+
rebuildArgs.codexThreadId
|
|
24774
|
+
);
|
|
24775
|
+
try {
|
|
24776
|
+
if (liveSession !== void 0 && rebuilt) {
|
|
24777
|
+
await liveSession.startThreadMessageTurn({
|
|
24778
|
+
seq: message.seq,
|
|
24779
|
+
body: message.body,
|
|
24780
|
+
actorSlug: message.actorSlug,
|
|
24781
|
+
actorUserId: message.actorUserId,
|
|
24782
|
+
boundStartTimeout: true
|
|
24783
|
+
});
|
|
24784
|
+
} else {
|
|
24785
|
+
await startThreadRunnerProcess(reconnectControl, cwd);
|
|
24786
|
+
}
|
|
24787
|
+
rebuilt = true;
|
|
24788
|
+
} catch (error) {
|
|
24789
|
+
log2("runner.thread_dead_worker_rebuild_failed", {
|
|
24790
|
+
codex_thread_id: rebuildArgs.codexThreadId,
|
|
24791
|
+
kandan_thread_id: rebuildArgs.kandanThreadId,
|
|
24792
|
+
seq: message.seq,
|
|
24793
|
+
message: error instanceof Error ? error.message : String(error)
|
|
24794
|
+
});
|
|
24795
|
+
return false;
|
|
24796
|
+
}
|
|
24797
|
+
}
|
|
24798
|
+
return rebuilt;
|
|
24799
|
+
} : void 0;
|
|
24389
24800
|
const session = await attachChannelSession({
|
|
24390
24801
|
kandan,
|
|
24391
24802
|
codex: sessionCodex,
|
|
@@ -24400,6 +24811,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24400
24811
|
fetch: options.fetch,
|
|
24401
24812
|
fast: control.fast ?? options.fast,
|
|
24402
24813
|
launchTui: false,
|
|
24814
|
+
rebuildOnDeadCodexWorker,
|
|
24403
24815
|
pipelinePersistDir: commanderOutboxPersistDir(),
|
|
24404
24816
|
enablePortForwardWatch: true,
|
|
24405
24817
|
initialForwardPorts: allowedForwardPorts,
|
|
@@ -25115,6 +25527,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25115
25527
|
}
|
|
25116
25528
|
);
|
|
25117
25529
|
}
|
|
25530
|
+
if (control.type === "scan_onboarding_projects") {
|
|
25531
|
+
const selectedScanRoots = onboardingProjectScanRoots(control.paths);
|
|
25532
|
+
return runLocalOnboardingProjectDiscovery(options, log2, {
|
|
25533
|
+
workspaceSlug: stringValue(control.workspace),
|
|
25534
|
+
selectedScanRoots,
|
|
25535
|
+
sourceRoots: selectedScanRoots,
|
|
25536
|
+
workerId: "projects-selected-folders",
|
|
25537
|
+
workerLabel: "Selected folders",
|
|
25538
|
+
startingMessage: "Checking selected folders",
|
|
25539
|
+
emptyMessage: "No Git projects found in selected folders",
|
|
25540
|
+
foundMessage: "Selected folder projects found",
|
|
25541
|
+
failedMessage: "Selected folder project scan failed",
|
|
25542
|
+
currentPlace: selectedScanRoots[0] ?? options.cwd,
|
|
25543
|
+
requireTerminalStatusWrite: true,
|
|
25544
|
+
includeRuntimeCwdHint: false
|
|
25545
|
+
}).catch((error) => {
|
|
25546
|
+
log2("onboarding.project_scan_failed", {
|
|
25547
|
+
runnerId: options.runnerId,
|
|
25548
|
+
message: error instanceof Error ? error.message : String(error)
|
|
25549
|
+
});
|
|
25550
|
+
throw error;
|
|
25551
|
+
});
|
|
25552
|
+
}
|
|
25118
25553
|
if (control.type === "start_instance") {
|
|
25119
25554
|
const staleAgeMs = staleStartInstanceAgeMs(control, Date.now());
|
|
25120
25555
|
if (staleAgeMs !== void 0) {
|
|
@@ -25219,7 +25654,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25219
25654
|
settleControlDispatch(control.type, () => handleControl(control), ack, log2);
|
|
25220
25655
|
};
|
|
25221
25656
|
finishRunnerStartup(
|
|
25222
|
-
|
|
25657
|
+
onboardingDiscoveryRunnerOptions(
|
|
25658
|
+
options,
|
|
25659
|
+
allowedCwds.value,
|
|
25660
|
+
missingAllowedCwds.value,
|
|
25661
|
+
projectDiscoveryCwdIsSelectedRootFromSyncedConfig.value
|
|
25662
|
+
),
|
|
25223
25663
|
log2,
|
|
25224
25664
|
pendingControls,
|
|
25225
25665
|
controlDispatcher,
|
|
@@ -26090,7 +26530,10 @@ async function resumeCodexThreadForReconnect(codex, codexThreadId, resumeOverrid
|
|
|
26090
26530
|
threadId: codexThreadId,
|
|
26091
26531
|
...resumeOverrides
|
|
26092
26532
|
};
|
|
26093
|
-
const resumeResponse = await
|
|
26533
|
+
const resumeResponse = await withCodexStartRequestTimeout(
|
|
26534
|
+
"thread/resume",
|
|
26535
|
+
codex.request("thread/resume", resumeParams)
|
|
26536
|
+
);
|
|
26094
26537
|
if (!("error" in resumeResponse)) {
|
|
26095
26538
|
return;
|
|
26096
26539
|
}
|
|
@@ -26108,7 +26551,10 @@ async function resumeCodexThreadForReconnect(codex, codexThreadId, resumeOverrid
|
|
|
26108
26551
|
`failed to prepare Codex thread before reconnect: ${injected.error.message}`
|
|
26109
26552
|
);
|
|
26110
26553
|
}
|
|
26111
|
-
const verified = await
|
|
26554
|
+
const verified = await withCodexStartRequestTimeout(
|
|
26555
|
+
"thread/resume",
|
|
26556
|
+
codex.request("thread/resume", resumeParams)
|
|
26557
|
+
);
|
|
26112
26558
|
if ("error" in verified) {
|
|
26113
26559
|
throw new Error(
|
|
26114
26560
|
`failed to verify Codex thread before reconnect: ${verified.error.message}`
|
|
@@ -26699,7 +27145,8 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
26699
27145
|
seq: sourceSeq,
|
|
26700
27146
|
body: workDescription,
|
|
26701
27147
|
actorSlug: identity.actorUsername,
|
|
26702
|
-
actorUserId: identity.actorUserId
|
|
27148
|
+
actorUserId: identity.actorUserId,
|
|
27149
|
+
boundStartTimeout: true
|
|
26703
27150
|
});
|
|
26704
27151
|
}
|
|
26705
27152
|
return {
|
|
@@ -29585,6 +30032,14 @@ function onboardingConversationImportSources(value) {
|
|
|
29585
30032
|
)
|
|
29586
30033
|
).sort();
|
|
29587
30034
|
}
|
|
30035
|
+
function onboardingProjectScanRoots(value) {
|
|
30036
|
+
return normalizeAllowedCwds(
|
|
30037
|
+
(arrayValue(value) ?? []).flatMap((path2) => {
|
|
30038
|
+
const normalizedPath = stringValue(path2)?.trim();
|
|
30039
|
+
return normalizedPath === void 0 || normalizedPath === "" ? [] : [normalizedPath];
|
|
30040
|
+
})
|
|
30041
|
+
);
|
|
30042
|
+
}
|
|
29588
30043
|
async function replayLocalOnboardingConversation(args) {
|
|
29589
30044
|
let replayedChunkCount = 0;
|
|
29590
30045
|
for (const message of args.messages) {
|
|
@@ -30011,27 +30466,42 @@ async function runLocalOnboardingConversationDiscovery(options, log2) {
|
|
|
30011
30466
|
);
|
|
30012
30467
|
}
|
|
30013
30468
|
}
|
|
30014
|
-
async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
30015
|
-
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
30469
|
+
async function runLocalOnboardingProjectDiscovery(options, log2, args = {}) {
|
|
30470
|
+
const workspaceSlug = args.workspaceSlug ?? runnerWorkspaceSlug(options);
|
|
30471
|
+
const selectedScanRoots = args.selectedScanRoots ?? selectedProjectDiscoveryScanRoots(options);
|
|
30016
30472
|
if (workspaceSlug === void 0) {
|
|
30017
30473
|
return;
|
|
30018
30474
|
}
|
|
30475
|
+
const workerId = args.workerId ?? "projects-local-filesystem";
|
|
30476
|
+
const workerLabel = args.workerLabel ?? "Local Git projects";
|
|
30477
|
+
const currentPlace = args.currentPlace ?? selectedScanRoots[0] ?? options.cwd;
|
|
30019
30478
|
await reportOnboardingDiscoveryStatus(options, log2, {
|
|
30020
30479
|
phase: "projects",
|
|
30021
30480
|
status: "starting",
|
|
30022
30481
|
placesSearched: 0,
|
|
30023
|
-
message: "
|
|
30024
|
-
currentPlace
|
|
30482
|
+
message: args.startingMessage ?? "Searching local Git projects",
|
|
30483
|
+
currentPlace,
|
|
30025
30484
|
currentSource: "git",
|
|
30485
|
+
workspaceSlug,
|
|
30026
30486
|
metadata: {
|
|
30027
|
-
worker_id:
|
|
30028
|
-
worker_label:
|
|
30487
|
+
worker_id: workerId,
|
|
30488
|
+
worker_label: workerLabel,
|
|
30489
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
|
|
30029
30490
|
}
|
|
30030
30491
|
});
|
|
30031
30492
|
let successfulPostStartWriteCount = 0;
|
|
30032
30493
|
try {
|
|
30033
|
-
const summary = await options.localProjectDiscovery?.(
|
|
30034
|
-
|
|
30494
|
+
const summary = await options.localProjectDiscovery?.({
|
|
30495
|
+
selectedScanRoots,
|
|
30496
|
+
sourceRoots: args.sourceRoots,
|
|
30497
|
+
includeRuntimeCwdHint: args.includeRuntimeCwdHint
|
|
30498
|
+
}) ?? await discoverCurrentGitProjectInChildProcess(
|
|
30499
|
+
{
|
|
30500
|
+
cwd: options.cwd,
|
|
30501
|
+
selectedScanRoots,
|
|
30502
|
+
sourceRoots: args.sourceRoots,
|
|
30503
|
+
includeRuntimeCwdHint: args.includeRuntimeCwdHint
|
|
30504
|
+
},
|
|
30035
30505
|
{ log: (event, payload) => log2(event, payload) }
|
|
30036
30506
|
);
|
|
30037
30507
|
const client = createLinzumiMcpApiClient({
|
|
@@ -30061,22 +30531,31 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30061
30531
|
phase: "projects",
|
|
30062
30532
|
status: "completed",
|
|
30063
30533
|
placesSearched: summary.placesSearched,
|
|
30064
|
-
message: summary.candidates.length === 0 ?
|
|
30065
|
-
currentPlace
|
|
30534
|
+
message: summary.candidates.length === 0 ? args.emptyMessage ?? "No local Git projects found" : args.foundMessage ?? "Local Git projects found",
|
|
30535
|
+
currentPlace,
|
|
30066
30536
|
currentSource: "git",
|
|
30537
|
+
workspaceSlug,
|
|
30067
30538
|
metadata: {
|
|
30068
|
-
worker_id:
|
|
30069
|
-
worker_label:
|
|
30539
|
+
worker_id: workerId,
|
|
30540
|
+
worker_label: workerLabel,
|
|
30070
30541
|
duration_ms: summary.durationMs,
|
|
30071
30542
|
reported_count: summary.candidates.length,
|
|
30072
30543
|
failed_report_count: failedReportCount,
|
|
30544
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots },
|
|
30073
30545
|
search_stats: summary.searchStats
|
|
30074
30546
|
}
|
|
30075
30547
|
};
|
|
30076
|
-
|
|
30077
|
-
|
|
30078
|
-
|
|
30548
|
+
const completedStatusWritten = await reportOnboardingDiscoveryStatus(
|
|
30549
|
+
options,
|
|
30550
|
+
log2,
|
|
30551
|
+
completedReport
|
|
30079
30552
|
);
|
|
30553
|
+
if (!completedStatusWritten && args.requireTerminalStatusWrite === true) {
|
|
30554
|
+
throw new Error(
|
|
30555
|
+
"onboarding project discovery terminal status write failed"
|
|
30556
|
+
);
|
|
30557
|
+
}
|
|
30558
|
+
successfulPostStartWriteCount = completedStatusWritten ? successfulPostStartWriteCount + 1 : successfulPostStartWriteCount;
|
|
30080
30559
|
log2("onboarding.local_project_discovery_completed", {
|
|
30081
30560
|
runnerId: options.runnerId,
|
|
30082
30561
|
workspace: workspaceSlug,
|
|
@@ -30094,19 +30573,26 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30094
30573
|
phase: "projects",
|
|
30095
30574
|
status: "failed",
|
|
30096
30575
|
placesSearched: 0,
|
|
30097
|
-
message: "
|
|
30098
|
-
currentPlace
|
|
30576
|
+
message: args.failedMessage ?? "Local Git project scan failed",
|
|
30577
|
+
currentPlace,
|
|
30099
30578
|
currentSource: "git",
|
|
30100
30579
|
lastError: message,
|
|
30580
|
+
workspaceSlug,
|
|
30101
30581
|
metadata: {
|
|
30102
|
-
worker_id:
|
|
30103
|
-
worker_label:
|
|
30582
|
+
worker_id: workerId,
|
|
30583
|
+
worker_label: workerLabel,
|
|
30584
|
+
...selectedScanRoots.length === 0 ? {} : { selected_scan_roots: selectedScanRoots }
|
|
30104
30585
|
}
|
|
30105
30586
|
};
|
|
30106
|
-
|
|
30107
|
-
|
|
30108
|
-
|
|
30587
|
+
const failedStatusWritten = await reportOnboardingDiscoveryStatus(
|
|
30588
|
+
options,
|
|
30589
|
+
log2,
|
|
30590
|
+
failedReport
|
|
30109
30591
|
);
|
|
30592
|
+
if (!failedStatusWritten && args.requireTerminalStatusWrite === true) {
|
|
30593
|
+
throw error;
|
|
30594
|
+
}
|
|
30595
|
+
successfulPostStartWriteCount = failedStatusWritten ? successfulPostStartWriteCount + 1 : successfulPostStartWriteCount;
|
|
30110
30596
|
log2("onboarding.local_project_discovery_failed", {
|
|
30111
30597
|
runnerId: options.runnerId,
|
|
30112
30598
|
workspace: workspaceSlug,
|
|
@@ -30118,11 +30604,45 @@ async function runLocalOnboardingProjectDiscovery(options, log2) {
|
|
|
30118
30604
|
);
|
|
30119
30605
|
}
|
|
30120
30606
|
}
|
|
30607
|
+
function selectedProjectDiscoveryScanRoots(options) {
|
|
30608
|
+
const cwd = options.cwd.trim();
|
|
30609
|
+
const implicitCwdAliases = new Set(implicitCwdStringAliases(cwd));
|
|
30610
|
+
for (const alias of options.projectDiscoveryImplicitCwdAliases ?? []) {
|
|
30611
|
+
implicitCwdAliases.add(alias);
|
|
30612
|
+
}
|
|
30613
|
+
const allowedCwds = options.projectDiscoveryCwdIsSelectedRoot === true ? options.allowedCwds : options.allowedCwds.filter(
|
|
30614
|
+
(allowedCwd) => !implicitCwdAliases.has(allowedCwd)
|
|
30615
|
+
);
|
|
30616
|
+
return normalizeAllowedCwds([
|
|
30617
|
+
...allowedCwds,
|
|
30618
|
+
...options.missingAllowedCwds ?? []
|
|
30619
|
+
]);
|
|
30620
|
+
}
|
|
30621
|
+
function implicitCwdStringAliases(cwd) {
|
|
30622
|
+
if (cwd === "") {
|
|
30623
|
+
return [];
|
|
30624
|
+
}
|
|
30625
|
+
return normalizeAllowedCwds([cwd, resolve8(expandUserPath(cwd))]);
|
|
30626
|
+
}
|
|
30627
|
+
function onboardingDiscoveryRunnerOptions(options, allowedCwds, missingAllowedCwds, projectDiscoveryCwdIsSelectedRootFromSyncedConfig) {
|
|
30628
|
+
return {
|
|
30629
|
+
...options,
|
|
30630
|
+
allowedCwds,
|
|
30631
|
+
missingAllowedCwds,
|
|
30632
|
+
projectDiscoveryCwdIsSelectedRoot: options.projectDiscoveryCwdIsSelectedRoot === true || projectDiscoveryCwdIsSelectedRootFromSyncedConfig
|
|
30633
|
+
};
|
|
30634
|
+
}
|
|
30635
|
+
function allowedCwdsContainAny(allowedCwds, candidates) {
|
|
30636
|
+
const allowed = new Set(normalizeAllowedCwds(allowedCwds));
|
|
30637
|
+
return normalizeAllowedCwds(candidates).some(
|
|
30638
|
+
(candidate) => allowed.has(candidate)
|
|
30639
|
+
);
|
|
30640
|
+
}
|
|
30121
30641
|
function shouldStartOnboardingDiscoveryAgents(options, workspaceSlug = runnerWorkspaceSlug(options)) {
|
|
30122
30642
|
return workspaceSlug !== void 0 && options.threadProcess === void 0 && options.onboardingDiscovery === "start";
|
|
30123
30643
|
}
|
|
30124
30644
|
async function reportOnboardingDiscoveryStatus(options, log2, args) {
|
|
30125
|
-
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
30645
|
+
const workspaceSlug = args.workspaceSlug ?? runnerWorkspaceSlug(options);
|
|
30126
30646
|
if (workspaceSlug === void 0) {
|
|
30127
30647
|
return false;
|
|
30128
30648
|
}
|
|
@@ -69207,6 +69727,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
69207
69727
|
fast: values.get("fast") === true,
|
|
69208
69728
|
logFile: stringValue8(values, "log-file"),
|
|
69209
69729
|
allowedCwds,
|
|
69730
|
+
projectDiscoveryCwdIsSelectedRoot: true,
|
|
69210
69731
|
allowedForwardPorts: parseAllowedPortList(
|
|
69211
69732
|
stringValue8(values, "forward-port")
|
|
69212
69733
|
),
|
|
@@ -69274,6 +69795,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
69274
69795
|
parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
|
|
69275
69796
|
) : requestedCwdValue === void 0 ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
|
|
69276
69797
|
const cwd = allowedCwds[0] ?? requestedCwd;
|
|
69798
|
+
const projectDiscoveryCwdIsSelectedRoot = values.has("allowed-cwd") || requestedCwdValue === void 0 && configuredAllowedCwds2.allowedCwds.length > 0;
|
|
69277
69799
|
const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
|
|
69278
69800
|
const customCodeServerBin = stringValue8(values, "code-server-bin");
|
|
69279
69801
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
@@ -69313,6 +69835,8 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
69313
69835
|
logFile: stringValue8(values, "log-file"),
|
|
69314
69836
|
allowedCwds,
|
|
69315
69837
|
missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
|
|
69838
|
+
projectDiscoveryCwdIsSelectedRoot,
|
|
69839
|
+
projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : allowedCwds,
|
|
69316
69840
|
allowedForwardPorts: parseAllowedPortList(
|
|
69317
69841
|
stringValue8(values, "forward-port")
|
|
69318
69842
|
),
|
|
@@ -69436,6 +69960,9 @@ async function parseRunnerArgs(args, deps = {
|
|
|
69436
69960
|
const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
|
|
69437
69961
|
parseAllowedCwdList(stringValue8(values, "allowed-cwd"))
|
|
69438
69962
|
) : [...localConfiguredAllowedCwds.allowedCwds];
|
|
69963
|
+
const projectDiscoveryCwdIsSelectedRoot = cwdAllowedCwds.some(
|
|
69964
|
+
(cwdAllowedCwd) => configuredAllowedCwds2.includes(cwdAllowedCwd)
|
|
69965
|
+
);
|
|
69439
69966
|
const requestedCodexBin = stringValue8(values, "codex-bin") ?? "codex";
|
|
69440
69967
|
const customCodeServerBin = stringValue8(values, "code-server-bin");
|
|
69441
69968
|
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
@@ -69490,6 +70017,8 @@ async function parseRunnerArgs(args, deps = {
|
|
|
69490
70017
|
/* @__PURE__ */ new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])
|
|
69491
70018
|
),
|
|
69492
70019
|
missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
|
|
70020
|
+
projectDiscoveryCwdIsSelectedRoot,
|
|
70021
|
+
projectDiscoveryImplicitCwdAliases: projectDiscoveryCwdIsSelectedRoot ? void 0 : cwdAllowedCwds,
|
|
69493
70022
|
allowedForwardPorts: parseAllowedPortList(
|
|
69494
70023
|
stringValue8(values, "forward-port")
|
|
69495
70024
|
),
|
package/package.json
CHANGED