@dotobokuri/fleet-console 1.22.1 → 1.24.0
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/AGENTS.md +41 -255
- package/README.md +8 -0
- package/dist/cli.d.ts +11 -1
- package/dist/cli.mjs +1866 -515
- package/dist/client/assets/{_baseUniq-cap0P4FK.js → _baseUniq-yooYbTgN.js} +1 -1
- package/dist/client/assets/{arc-yBqa09aK.js → arc-D4mme5Ts.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-Bug4qdr-.js → architectureDiagram-Q4EWVU46-BAJOviEX.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-BkiIL2xt.js → blockDiagram-DXYQGD6D-DuObpxQ3.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-DYY200Al.js → c4Diagram-AHTNJAMY-BOT-SD34.js} +1 -1
- package/dist/client/assets/channel-TZYyAq1v.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DQKmhVxn.js → chunk-4BX2VUAB-BF_aMTes.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-DHpmA3Zl.js → chunk-4TB4RGXK-M4brBcnD.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DeRDuQXE.js → chunk-55IACEB6-CrG-4b2Z.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-CNpnhSJm.js → chunk-EDXVE4YY-BxDSygH1.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BXGVABL-.js → chunk-FMBD7UC4-Bh7MEX7E.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-WbJs4VwZ.js → chunk-OYMX7WX6-DBnBwxer.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-u3BLl6Sl.js → chunk-QZHKN3VN-bjAogQwW.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-BFESuhuJ.js → chunk-YZCP3GAM-l4FGlk_c.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-Dh6hZUl4.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-Dh6hZUl4.js +1 -0
- package/dist/client/assets/clone-DgWGnVWH.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-rouOsZjT.js → cose-bilkent-S5V4N54A-Bm5QA4zo.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-QCUeCVpM.js → dagre-KV5264BT-PzXQN-5Q.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-DqerlFb_.js → diagram-5BDNPKRD-KhaAKPtX.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-BS3a1YGN.js → diagram-G4DWMVQ6-DBbjfiR8.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-BeauTcSV.js → diagram-MMDJMWI5-QWro7PRX.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-Ci2wLUUE.js → diagram-TYMM5635-B9LqWrbF.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-9MnmiKch.js → erDiagram-SMLLAGMA-b0VkMMN4.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-CiV9p5EU.js → flowDiagram-DWJPFMVM-edxBAbe6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-D6oNoENl.js → ganttDiagram-T4ZO3ILL-ks2Gg1_V.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DESO385F.js → gitGraphDiagram-UUTBAWPF-DymUyYNl.js} +1 -1
- package/dist/client/assets/{graph-BZdl-RHy.js → graph-B-B_mqfh.js} +1 -1
- package/dist/client/assets/index-BgLzfg-v.css +1 -0
- package/dist/client/assets/index-pDnb-q6f.js +380 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-lc8vZnyi.js → infoDiagram-42DDH7IO-B5iLIYAc.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BfzvQXhn.js → ishikawaDiagram-UXIWVN3A-DnztLLn7.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BS_Dr0EV.js → journeyDiagram-VCZTEJTY-BKDRfH1Y.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-Cw7oTQJN.js → kanban-definition-6JOO6SKY-DPB3Phvr.js} +1 -1
- package/dist/client/assets/{layout-ChwcDQm8.js → layout-BEeRhYTG.js} +1 -1
- package/dist/client/assets/{linear-C7GLbmND.js → linear-QzmfWKSq.js} +1 -1
- package/dist/client/assets/{mermaid.core-D9phfpQo.js → mermaid.core-BVka5dgm.js} +4 -4
- package/dist/client/assets/{min-p7Whc_Wi.js → min-CfXk9kSN.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-fl_SJKrw.js → mindmap-definition-QFDTVHPH-tGrGhr6Z.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-Btw7ckA5.js → pieDiagram-DEJITSTG-ZRBqRiRK.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-DB4iJlTI.js → quadrantDiagram-34T5L4WZ-VCINZgct.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-hXwV5Nhy.js → requirementDiagram-MS252O5E-DFLzADh3.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-DC-fizCk.js → sankeyDiagram-XADWPNL6-BbouN8Ji.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-RrfrroR5.js → sequenceDiagram-FGHM5R23-B58_LF0R.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-q8FUPV_e.js → stateDiagram-FHFEXIEX-pk-OHHuL.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-xsN122JA.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-tegMd8Od.js → timeline-definition-GMOUNBTQ-4jQdCvaj.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-CXx5oN2X.js → vennDiagram-DHZGUBPP-CUr1fH6N.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-CxSJ3P_7.js → wardley-RL74JXVD-D0cfvvNA.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DN1P180Z.js → wardleyDiagram-NUSXRM2D-D0l_Rp_Z.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-Dcn9ukv5.js → xychartDiagram-5P7HB3ND-BdR8OEuJ.js} +1 -1
- package/dist/client/index.html +3 -3
- package/dist/desktop-protocol.d.ts +49 -0
- package/dist/desktop-protocol.mjs +99 -0
- package/dist/fleet-plugins/diff/routes.mjs +124 -225
- package/dist/fleet-plugins/skills/routes.mjs +218 -85
- package/dist/fleet-plugins/terminal/routes.mjs +573 -536
- package/package.json +7 -2
- package/dist/client/assets/channel-B3EqY8ya.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-BekEZLVJ.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-BekEZLVJ.js +0 -1
- package/dist/client/assets/clone-CsVi9-Ni.js +0 -1
- package/dist/client/assets/index-BKpCMFrm.js +0 -380
- package/dist/client/assets/index-cg8Uxys9.css +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BErzXGq7.js +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn, execFileSync,
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
2
|
+
import { execFile, spawn, execFileSync, execSync } from 'child_process';
|
|
3
|
+
import * as fs14 from 'fs';
|
|
4
|
+
import fs14__default, { realpathSync, accessSync, constants, existsSync, lstatSync } from 'fs';
|
|
5
|
+
import * as path27 from 'path';
|
|
6
|
+
import path27__default, { join, resolve, relative, sep, dirname } from 'path';
|
|
7
7
|
import process4 from 'process';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
9
9
|
import * as os4 from 'os';
|
|
@@ -12,9 +12,11 @@ import crypto5, { createHash, randomUUID } from 'crypto';
|
|
|
12
12
|
import { builtinModules, createRequire } from 'module';
|
|
13
13
|
import http2 from 'http';
|
|
14
14
|
import { EventEmitter } from 'events';
|
|
15
|
-
import { stat,
|
|
15
|
+
import { stat, mkdir, readdir, readFile, rm, realpath, mkdtemp, writeFile, appendFile, rename, access } from 'fs/promises';
|
|
16
16
|
import { Writable, Readable } from 'stream';
|
|
17
|
+
import fontList from 'font-list';
|
|
17
18
|
import net2 from 'net';
|
|
19
|
+
import { promisify } from 'util';
|
|
18
20
|
|
|
19
21
|
var __defProp = Object.defineProperty;
|
|
20
22
|
var __export = (target, all) => {
|
|
@@ -37,7 +39,7 @@ function findBinaryPath(command2, env, options = {}) {
|
|
|
37
39
|
return findOnPath(command2, pathValue, pathExts, platform);
|
|
38
40
|
}
|
|
39
41
|
function wrapWindowsShim(resolved, env, platform) {
|
|
40
|
-
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(
|
|
42
|
+
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(path27__default.extname(resolved).toLowerCase())) {
|
|
41
43
|
return { bin: resolved, prefixArgs: [] };
|
|
42
44
|
}
|
|
43
45
|
rejectCmdExpansionSensitiveShim(resolved);
|
|
@@ -55,13 +57,13 @@ function forceWindowsArgQuoting(value) {
|
|
|
55
57
|
return `${value} `;
|
|
56
58
|
}
|
|
57
59
|
function findOnPath(bin, pathValue, pathExts, platform) {
|
|
58
|
-
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR :
|
|
59
|
-
if (hasPathSeparator(bin, platform) ||
|
|
60
|
+
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR : path27__default.delimiter;
|
|
61
|
+
if (hasPathSeparator(bin, platform) || path27__default.isAbsolute(bin)) {
|
|
60
62
|
return resolveWithExtensions(bin, pathExts, platform);
|
|
61
63
|
}
|
|
62
64
|
for (const entry of pathValue.split(pathSeparator)) {
|
|
63
65
|
const searchDir = entry.length === 0 ? "." : entry;
|
|
64
|
-
const candidate = resolveWithExtensions(
|
|
66
|
+
const candidate = resolveWithExtensions(path27__default.join(searchDir, bin), pathExts, platform);
|
|
65
67
|
if (candidate) {
|
|
66
68
|
return candidate;
|
|
67
69
|
}
|
|
@@ -69,7 +71,7 @@ function findOnPath(bin, pathValue, pathExts, platform) {
|
|
|
69
71
|
return void 0;
|
|
70
72
|
}
|
|
71
73
|
function resolveWithExtensions(candidate, pathExts, platform) {
|
|
72
|
-
if (platform !== "win32" ||
|
|
74
|
+
if (platform !== "win32" || path27__default.extname(candidate).length > 0) {
|
|
73
75
|
return isAccessibleExecutable(candidate, platform) ? candidate : void 0;
|
|
74
76
|
}
|
|
75
77
|
for (const ext of pathExts) {
|
|
@@ -304,7 +306,7 @@ function isC1Control(code) {
|
|
|
304
306
|
var LOCK_DIR_MODE = 448;
|
|
305
307
|
var LOCK_FILE_MODE = 384;
|
|
306
308
|
function createConsoleLock(deps = {}) {
|
|
307
|
-
const fsImpl = deps.fs ??
|
|
309
|
+
const fsImpl = deps.fs ?? fs14__default;
|
|
308
310
|
const now = deps.now ?? Date.now;
|
|
309
311
|
const randomToken = deps.randomToken ?? (() => crypto5.randomBytes(32).toString("base64url"));
|
|
310
312
|
const hostname5 = deps.hostname ?? (() => "127.0.0.1");
|
|
@@ -333,9 +335,10 @@ function createConsoleLock(deps = {}) {
|
|
|
333
335
|
endpoint: input.endpoint,
|
|
334
336
|
startedAt: now(),
|
|
335
337
|
token: randomToken(),
|
|
336
|
-
version: input.version
|
|
338
|
+
version: input.version,
|
|
339
|
+
...input.owner ? { owner: input.owner } : {}
|
|
337
340
|
};
|
|
338
|
-
const fd = fsImpl.openSync(input.lockFile,
|
|
341
|
+
const fd = fsImpl.openSync(input.lockFile, fs14__default.constants.O_CREAT | fs14__default.constants.O_EXCL | fs14__default.constants.O_WRONLY, LOCK_FILE_MODE);
|
|
339
342
|
try {
|
|
340
343
|
fsImpl.writeFileSync(fd, `${JSON.stringify(payload, null, 2)}
|
|
341
344
|
`, "utf8");
|
|
@@ -361,7 +364,7 @@ function createConsoleLock(deps = {}) {
|
|
|
361
364
|
}
|
|
362
365
|
function assertLockModes(lockFile) {
|
|
363
366
|
if (typeof process.getuid !== "function") return;
|
|
364
|
-
const dir =
|
|
367
|
+
const dir = path27__default.dirname(lockFile);
|
|
365
368
|
const dirMode = fsImpl.statSync(dir).mode & 511;
|
|
366
369
|
const fileMode = fsImpl.statSync(lockFile).mode & 511;
|
|
367
370
|
if (dirMode !== LOCK_DIR_MODE) {
|
|
@@ -403,14 +406,14 @@ function createConsoleLock(deps = {}) {
|
|
|
403
406
|
}
|
|
404
407
|
var FLEET_DATA_DIR_NAME = ".fleet";
|
|
405
408
|
function getFleetDataDir() {
|
|
406
|
-
return
|
|
409
|
+
return path27.join(os4.homedir(), FLEET_DATA_DIR_NAME);
|
|
407
410
|
}
|
|
408
411
|
var SECURE_DIR_MODE = 448;
|
|
409
412
|
var SECURE_FILE_MODE = 384;
|
|
410
|
-
var NOFOLLOW_FLAG =
|
|
413
|
+
var NOFOLLOW_FLAG = fs14.constants.O_NOFOLLOW ?? 0;
|
|
411
414
|
function safeLstat(targetPath) {
|
|
412
415
|
try {
|
|
413
|
-
return
|
|
416
|
+
return fs14.lstatSync(targetPath);
|
|
414
417
|
} catch (error51) {
|
|
415
418
|
if (error51.code === "ENOENT") {
|
|
416
419
|
return null;
|
|
@@ -424,15 +427,15 @@ function ensureSafeDirectory(dirPath) {
|
|
|
424
427
|
if (!stat5.isDirectory() || stat5.isSymbolicLink()) {
|
|
425
428
|
throw new Error(`Unsafe Fleet directory: ${dirPath}`);
|
|
426
429
|
}
|
|
427
|
-
|
|
430
|
+
fs14.chmodSync(dirPath, SECURE_DIR_MODE);
|
|
428
431
|
return;
|
|
429
432
|
}
|
|
430
|
-
|
|
433
|
+
fs14.mkdirSync(dirPath, { recursive: true, mode: SECURE_DIR_MODE });
|
|
431
434
|
const created = safeLstat(dirPath);
|
|
432
435
|
if (!created?.isDirectory() || created.isSymbolicLink()) {
|
|
433
436
|
throw new Error(`Unsafe Fleet directory: ${dirPath}`);
|
|
434
437
|
}
|
|
435
|
-
|
|
438
|
+
fs14.chmodSync(dirPath, SECURE_DIR_MODE);
|
|
436
439
|
}
|
|
437
440
|
|
|
438
441
|
// ../../packages/core-infra/src/fs-store/atomic-write.ts
|
|
@@ -451,17 +454,17 @@ function writeAtomicSync(filePath, content, options = {}) {
|
|
|
451
454
|
const tempPath = buildTempPath(filePath);
|
|
452
455
|
let tempCreated = false;
|
|
453
456
|
try {
|
|
454
|
-
|
|
457
|
+
fs14.writeFileSync(tempPath, content, { encoding: "utf-8", flag: "wx", mode });
|
|
455
458
|
tempCreated = true;
|
|
456
459
|
if (doFsync) {
|
|
457
|
-
const fd =
|
|
460
|
+
const fd = fs14.openSync(tempPath, "r");
|
|
458
461
|
try {
|
|
459
462
|
fsyncSyncBestEffort(fd);
|
|
460
463
|
} finally {
|
|
461
|
-
|
|
464
|
+
fs14.closeSync(fd);
|
|
462
465
|
}
|
|
463
466
|
}
|
|
464
|
-
|
|
467
|
+
fs14.renameSync(tempPath, filePath);
|
|
465
468
|
return;
|
|
466
469
|
} catch (error51) {
|
|
467
470
|
const code = error51.code;
|
|
@@ -471,7 +474,7 @@ function writeAtomicSync(filePath, content, options = {}) {
|
|
|
471
474
|
}
|
|
472
475
|
if (tempCreated) {
|
|
473
476
|
try {
|
|
474
|
-
|
|
477
|
+
fs14.unlinkSync(tempPath);
|
|
475
478
|
} catch {
|
|
476
479
|
}
|
|
477
480
|
}
|
|
@@ -483,7 +486,7 @@ function writeAtomicSync(filePath, content, options = {}) {
|
|
|
483
486
|
function cleanupTempFiles(dir, prefix, minAgeMs, now) {
|
|
484
487
|
let entries;
|
|
485
488
|
try {
|
|
486
|
-
entries =
|
|
489
|
+
entries = fs14.readdirSync(dir);
|
|
487
490
|
} catch {
|
|
488
491
|
return;
|
|
489
492
|
}
|
|
@@ -491,11 +494,11 @@ function cleanupTempFiles(dir, prefix, minAgeMs, now) {
|
|
|
491
494
|
if (!entry.startsWith(prefix)) {
|
|
492
495
|
continue;
|
|
493
496
|
}
|
|
494
|
-
const tempPath =
|
|
497
|
+
const tempPath = path27.join(dir, entry);
|
|
495
498
|
try {
|
|
496
|
-
const stat5 =
|
|
499
|
+
const stat5 = fs14.lstatSync(tempPath);
|
|
497
500
|
if (stat5?.isFile() && !stat5.isSymbolicLink() && now - stat5.mtimeMs >= minAgeMs) {
|
|
498
|
-
|
|
501
|
+
fs14.unlinkSync(tempPath);
|
|
499
502
|
}
|
|
500
503
|
} catch {
|
|
501
504
|
}
|
|
@@ -507,7 +510,7 @@ function isIgnoredFsyncError(error51) {
|
|
|
507
510
|
}
|
|
508
511
|
function fsyncSyncBestEffort(fd) {
|
|
509
512
|
try {
|
|
510
|
-
|
|
513
|
+
fs14.fsyncSync(fd);
|
|
511
514
|
} catch (error51) {
|
|
512
515
|
if (!isIgnoredFsyncError(error51)) {
|
|
513
516
|
throw error51;
|
|
@@ -527,7 +530,7 @@ function withDirectoryLock(deps, operation) {
|
|
|
527
530
|
const startedAt = now();
|
|
528
531
|
while (true) {
|
|
529
532
|
try {
|
|
530
|
-
|
|
533
|
+
fs14.mkdirSync(deps.lockDir, { mode: 448 });
|
|
531
534
|
writeLockOwner(deps.lockDir, ownerFileName, now());
|
|
532
535
|
try {
|
|
533
536
|
return operation();
|
|
@@ -547,7 +550,7 @@ function withDirectoryLock(deps, operation) {
|
|
|
547
550
|
}
|
|
548
551
|
function releaseLock(lockDir) {
|
|
549
552
|
try {
|
|
550
|
-
|
|
553
|
+
fs14.rmSync(lockDir, { recursive: true, force: true });
|
|
551
554
|
} catch {
|
|
552
555
|
}
|
|
553
556
|
}
|
|
@@ -557,14 +560,14 @@ function recoverStaleLock(lockDir, ownerFileName, now, staleLockMs) {
|
|
|
557
560
|
if (!isRecoverableSnapshot(initialSnapshot, now, staleLockMs)) return;
|
|
558
561
|
const quarantineDir = buildQuarantinePath(lockDir);
|
|
559
562
|
try {
|
|
560
|
-
|
|
563
|
+
fs14.renameSync(lockDir, quarantineDir);
|
|
561
564
|
} catch {
|
|
562
565
|
return;
|
|
563
566
|
}
|
|
564
567
|
try {
|
|
565
568
|
const quarantinedSnapshot = readLockSnapshot(quarantineDir, ownerFileName);
|
|
566
569
|
if (sameLockSnapshot(initialSnapshot, quarantinedSnapshot) && isRecoverableSnapshot(quarantinedSnapshot, now, staleLockMs)) {
|
|
567
|
-
|
|
570
|
+
fs14.rmSync(quarantineDir, { recursive: true, force: true });
|
|
568
571
|
} else {
|
|
569
572
|
restoreQuarantinedLock(quarantineDir, lockDir);
|
|
570
573
|
}
|
|
@@ -578,7 +581,7 @@ function recoverStaleLock(lockDir, ownerFileName, now, staleLockMs) {
|
|
|
578
581
|
}
|
|
579
582
|
}
|
|
580
583
|
function readLockSnapshot(lockDir, ownerFileName) {
|
|
581
|
-
const stat5 =
|
|
584
|
+
const stat5 = fs14.lstatSync(lockDir);
|
|
582
585
|
if (stat5.isSymbolicLink()) {
|
|
583
586
|
return { owner: null, mtimeMs: 0 };
|
|
584
587
|
}
|
|
@@ -597,18 +600,18 @@ function writeLockOwner(lockDir, ownerFileName, startedAt) {
|
|
|
597
600
|
hostname: os4.hostname(),
|
|
598
601
|
startedAt
|
|
599
602
|
};
|
|
600
|
-
const ownerPath =
|
|
603
|
+
const ownerPath = path27.join(lockDir, ownerFileName);
|
|
601
604
|
try {
|
|
602
|
-
|
|
605
|
+
fs14.writeFileSync(ownerPath, JSON.stringify(owner), { encoding: "utf-8", mode: 384 });
|
|
603
606
|
} catch (error51) {
|
|
604
607
|
releaseLock(lockDir);
|
|
605
608
|
throw error51;
|
|
606
609
|
}
|
|
607
610
|
}
|
|
608
611
|
function readLockOwner(lockDir, ownerFileName) {
|
|
609
|
-
const ownerPath =
|
|
612
|
+
const ownerPath = path27.join(lockDir, ownerFileName);
|
|
610
613
|
try {
|
|
611
|
-
return sanitizeLockOwner(JSON.parse(
|
|
614
|
+
return sanitizeLockOwner(JSON.parse(fs14.readFileSync(ownerPath, "utf-8")));
|
|
612
615
|
} catch (error51) {
|
|
613
616
|
const code = error51.code;
|
|
614
617
|
if (code === "ENOENT") return null;
|
|
@@ -630,7 +633,7 @@ function sameLockSnapshot(a, b) {
|
|
|
630
633
|
}
|
|
631
634
|
function restoreQuarantinedLock(quarantineDir, lockDir) {
|
|
632
635
|
try {
|
|
633
|
-
|
|
636
|
+
fs14.renameSync(quarantineDir, lockDir);
|
|
634
637
|
} catch {
|
|
635
638
|
}
|
|
636
639
|
}
|
|
@@ -665,17 +668,17 @@ function sleepSync(ms) {
|
|
|
665
668
|
function readAuthStoreFile(filePath) {
|
|
666
669
|
let fd;
|
|
667
670
|
try {
|
|
668
|
-
fd =
|
|
669
|
-
if (!
|
|
671
|
+
fd = fs14.openSync(filePath, fs14.constants.O_RDONLY | NOFOLLOW_FLAG);
|
|
672
|
+
if (!fs14.fstatSync(fd).isFile()) {
|
|
670
673
|
return {};
|
|
671
674
|
}
|
|
672
|
-
return JSON.parse(
|
|
675
|
+
return JSON.parse(fs14.readFileSync(fd, "utf-8"));
|
|
673
676
|
} catch {
|
|
674
677
|
return {};
|
|
675
678
|
} finally {
|
|
676
679
|
if (fd !== void 0) {
|
|
677
680
|
try {
|
|
678
|
-
|
|
681
|
+
fs14.closeSync(fd);
|
|
679
682
|
} catch {
|
|
680
683
|
}
|
|
681
684
|
}
|
|
@@ -683,7 +686,7 @@ function readAuthStoreFile(filePath) {
|
|
|
683
686
|
}
|
|
684
687
|
|
|
685
688
|
// ../../packages/core-infra/src/auth/auth-storage.ts
|
|
686
|
-
var DEFAULT_AUTH_PATH =
|
|
689
|
+
var DEFAULT_AUTH_PATH = path27.join(getFleetDataDir(), "auth.json");
|
|
687
690
|
var AUTH_LOCK_OWNER_FILE_NAME = "owner.json";
|
|
688
691
|
var AUTH_LOCK_TIMEOUT_MS = 5e3;
|
|
689
692
|
function createAuthService(deps = {}) {
|
|
@@ -694,7 +697,7 @@ function createAuthService(deps = {}) {
|
|
|
694
697
|
async deleteApiKey(providerId) {
|
|
695
698
|
let deleted = false;
|
|
696
699
|
withAuthLock(authPath, lockDir, () => {
|
|
697
|
-
if (!
|
|
700
|
+
if (!fs14.existsSync(authPath)) return;
|
|
698
701
|
const data = readAuthStoreFile(authPath);
|
|
699
702
|
if (!Object.prototype.hasOwnProperty.call(data, providerId)) return;
|
|
700
703
|
delete data[providerId];
|
|
@@ -707,21 +710,21 @@ function createAuthService(deps = {}) {
|
|
|
707
710
|
async getApiKey(providerId) {
|
|
708
711
|
let fd;
|
|
709
712
|
try {
|
|
710
|
-
if (!
|
|
713
|
+
if (!fs14.existsSync(authPath)) {
|
|
711
714
|
return void 0;
|
|
712
715
|
}
|
|
713
|
-
fd =
|
|
714
|
-
if (!
|
|
716
|
+
fd = fs14.openSync(authPath, fs14.constants.O_RDONLY | NOFOLLOW_FLAG);
|
|
717
|
+
if (!fs14.fstatSync(fd).isFile()) {
|
|
715
718
|
return void 0;
|
|
716
719
|
}
|
|
717
|
-
const data = JSON.parse(
|
|
720
|
+
const data = JSON.parse(fs14.readFileSync(fd, "utf-8"));
|
|
718
721
|
return typeof data[providerId]?.key === "string" ? data[providerId].key : void 0;
|
|
719
722
|
} catch {
|
|
720
723
|
return void 0;
|
|
721
724
|
} finally {
|
|
722
725
|
if (fd !== void 0) {
|
|
723
726
|
try {
|
|
724
|
-
|
|
727
|
+
fs14.closeSync(fd);
|
|
725
728
|
} catch {
|
|
726
729
|
}
|
|
727
730
|
}
|
|
@@ -729,7 +732,7 @@ function createAuthService(deps = {}) {
|
|
|
729
732
|
},
|
|
730
733
|
// [HIGH #2] 읽기 경로: fd 기반 O_RDONLY|O_NOFOLLOW 통일
|
|
731
734
|
async listProviderIds() {
|
|
732
|
-
if (!
|
|
735
|
+
if (!fs14.existsSync(authPath)) {
|
|
733
736
|
return [];
|
|
734
737
|
}
|
|
735
738
|
try {
|
|
@@ -739,10 +742,10 @@ function createAuthService(deps = {}) {
|
|
|
739
742
|
}
|
|
740
743
|
},
|
|
741
744
|
async setApiKey(providerId, key) {
|
|
742
|
-
const dir =
|
|
745
|
+
const dir = path27.dirname(authPath);
|
|
743
746
|
ensureSafeDirectory(dir);
|
|
744
747
|
withAuthLock(authPath, lockDir, () => {
|
|
745
|
-
const data =
|
|
748
|
+
const data = fs14.existsSync(authPath) ? readAuthStoreFile(authPath) : {};
|
|
746
749
|
data[providerId] = {
|
|
747
750
|
...data[providerId] ?? {},
|
|
748
751
|
key
|
|
@@ -775,7 +778,7 @@ function createDurableJsonStore(deps) {
|
|
|
775
778
|
const sensitivity = deps.sensitivity;
|
|
776
779
|
const mode = sensitivity === "sensitive" ? SECURE_FILE_MODE : 420;
|
|
777
780
|
const minAgeMs = deps.tempCleanupMinAgeMs ?? DEFAULT_TEMP_FILE_MIN_AGE_MS;
|
|
778
|
-
const dir =
|
|
781
|
+
const dir = path27.dirname(deps.filePath);
|
|
779
782
|
return {
|
|
780
783
|
path: deps.filePath,
|
|
781
784
|
load() {
|
|
@@ -825,21 +828,21 @@ function createDurableJsonStore(deps) {
|
|
|
825
828
|
function readJsonFile(filePath, sanitize) {
|
|
826
829
|
let fd;
|
|
827
830
|
try {
|
|
828
|
-
const stat5 =
|
|
831
|
+
const stat5 = fs14.lstatSync(filePath);
|
|
829
832
|
if (!stat5 || !stat5.isFile() || stat5.isSymbolicLink()) {
|
|
830
833
|
return sanitize(void 0);
|
|
831
834
|
}
|
|
832
|
-
fd =
|
|
833
|
-
if (!
|
|
835
|
+
fd = fs14.openSync(filePath, fs14.constants.O_RDONLY | NOFOLLOW_FLAG);
|
|
836
|
+
if (!fs14.fstatSync(fd).isFile()) {
|
|
834
837
|
return sanitize(void 0);
|
|
835
838
|
}
|
|
836
|
-
return sanitize(JSON.parse(
|
|
839
|
+
return sanitize(JSON.parse(fs14.readFileSync(fd, "utf-8")));
|
|
837
840
|
} catch {
|
|
838
841
|
return sanitize(void 0);
|
|
839
842
|
} finally {
|
|
840
843
|
if (fd !== void 0) {
|
|
841
844
|
try {
|
|
842
|
-
|
|
845
|
+
fs14.closeSync(fd);
|
|
843
846
|
} catch {
|
|
844
847
|
}
|
|
845
848
|
}
|
|
@@ -849,7 +852,7 @@ function ensureSafeDirectoryIfSensitive(dir, sensitivity) {
|
|
|
849
852
|
if (sensitivity === "sensitive") {
|
|
850
853
|
ensureSafeDirectory(dir);
|
|
851
854
|
} else {
|
|
852
|
-
|
|
855
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
853
856
|
}
|
|
854
857
|
}
|
|
855
858
|
|
|
@@ -861,8 +864,8 @@ var LOCK_OWNER_FILE_NAME = "owner";
|
|
|
861
864
|
var TEMP_FILE_PREFIX = `.tmp-${GLOBAL_OPTIONS_FILE_NAME}-`;
|
|
862
865
|
function createGlobalOptionsStore(deps = {}) {
|
|
863
866
|
const dataDir = deps.dataDir ?? getFleetDataDir();
|
|
864
|
-
const optionsPath =
|
|
865
|
-
const lockDir =
|
|
867
|
+
const optionsPath = path27.join(dataDir, GLOBAL_OPTIONS_FILE_NAME);
|
|
868
|
+
const lockDir = path27.join(dataDir, LOCK_DIR_NAME);
|
|
866
869
|
const store2 = createDurableJsonStore({
|
|
867
870
|
filePath: optionsPath,
|
|
868
871
|
lockDir,
|
|
@@ -895,10 +898,11 @@ function sanitizeGlobalOptionsData(value) {
|
|
|
895
898
|
}
|
|
896
899
|
const data = {
|
|
897
900
|
version: GLOBAL_OPTIONS_VERSION,
|
|
898
|
-
...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {}
|
|
901
|
+
...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {},
|
|
902
|
+
...value.codexLaunchMode === "acp" || value.codexLaunchMode === "app-server" ? { codexLaunchMode: value.codexLaunchMode } : {}
|
|
899
903
|
};
|
|
900
|
-
const allowedKeys = /* @__PURE__ */ new Set(["version", "enableMetaphor"]);
|
|
901
|
-
const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean";
|
|
904
|
+
const allowedKeys = /* @__PURE__ */ new Set(["version", "enableMetaphor", "codexLaunchMode"]);
|
|
905
|
+
const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean" || "codexLaunchMode" in value && value.codexLaunchMode !== "acp" && value.codexLaunchMode !== "app-server";
|
|
902
906
|
return { data, changed };
|
|
903
907
|
}
|
|
904
908
|
function isRecord(value) {
|
|
@@ -930,16 +934,100 @@ function createInfraServices(deps = {}) {
|
|
|
930
934
|
globalOptionsService
|
|
931
935
|
};
|
|
932
936
|
}
|
|
933
|
-
|
|
937
|
+
var DESKTOP_PROTOCOL_VERSION = 1;
|
|
938
|
+
var DESKTOP_RESOURCE_ROOT_ENV = "FLEET_CONSOLE_RESOURCE_ROOT";
|
|
939
|
+
var DESKTOP_OWNER_ID_ENV = "FLEET_CONSOLE_OWNER_ID";
|
|
940
|
+
var DESKTOP_OWNER_KIND_ENV = "FLEET_CONSOLE_OWNER_KIND";
|
|
941
|
+
var DESKTOP_PROTOCOL_VERSION_ENV = "FLEET_CONSOLE_PROTOCOL_VERSION";
|
|
942
|
+
var DESKTOP_RESOURCE_ROOT_MARKER = ".fleet-console-resource-root";
|
|
943
|
+
var DESKTOP_DEVELOPMENT_ENV = "FLEET_CONSOLE_DESKTOP_DEVELOPMENT";
|
|
944
|
+
var LOCK_DIR_NAME2 = "fleet-console";
|
|
945
|
+
var LOCK_FILE_NAME = "console.lock";
|
|
946
|
+
var CONSOLE_DATA_DIR_NAME = "console";
|
|
947
|
+
var CONSOLE_STATE_FILE_NAME = "state.json";
|
|
948
|
+
var CONSOLE_SETTINGS_FILE_NAME = "settings.json";
|
|
949
|
+
var CONSOLE_CAPTURES_DIR_NAME = "captures";
|
|
950
|
+
function readDesktopProtocolEnvironment(env = process.env, deps = {}) {
|
|
951
|
+
const resourceRoot = env[DESKTOP_RESOURCE_ROOT_ENV];
|
|
952
|
+
const ownerId = env[DESKTOP_OWNER_ID_ENV];
|
|
953
|
+
const ownerKind = env[DESKTOP_OWNER_KIND_ENV];
|
|
954
|
+
const protocolVersion = env[DESKTOP_PROTOCOL_VERSION_ENV];
|
|
955
|
+
const values = [resourceRoot, ownerId, ownerKind, protocolVersion];
|
|
956
|
+
if (values.every((value) => value === void 0)) return null;
|
|
957
|
+
if (values.some((value) => !value)) throw new Error("desktop_protocol_environment_incomplete");
|
|
958
|
+
if (ownerKind !== "desktop") throw new Error("desktop_owner_kind_invalid");
|
|
959
|
+
if (!isSafeOwnerId(ownerId)) throw new Error("desktop_owner_id_invalid");
|
|
960
|
+
if (protocolVersion !== String(DESKTOP_PROTOCOL_VERSION)) throw new Error("desktop_protocol_version_unsupported");
|
|
961
|
+
const resolvedRoot = isDesktopDevelopmentEnvironment(env) ? validateDesktopDevelopmentResourceRoot(resourceRoot) : validateDesktopResourceRoot(resourceRoot, deps.expectedPackageRoot);
|
|
962
|
+
return { owner: { kind: "desktop", id: ownerId, protocolVersion: DESKTOP_PROTOCOL_VERSION }, resourceRoot: resolvedRoot };
|
|
963
|
+
}
|
|
964
|
+
function isDesktopDevelopmentEnvironment(env = process.env) {
|
|
965
|
+
return env[DESKTOP_DEVELOPMENT_ENV] === "1";
|
|
966
|
+
}
|
|
967
|
+
function resolveCanonicalStableConsolePaths(input) {
|
|
968
|
+
const dir = input.consoleDirOverride ?? path27__default.join(input.tmpDir, `${LOCK_DIR_NAME2}-${input.uid}-stable`);
|
|
969
|
+
const dataDir = input.consoleDirOverride ?? path27__default.join(input.fleetDataDir, CONSOLE_DATA_DIR_NAME);
|
|
970
|
+
return {
|
|
971
|
+
dir,
|
|
972
|
+
lockFile: path27__default.join(dir, LOCK_FILE_NAME),
|
|
973
|
+
dataDir,
|
|
974
|
+
stateFile: path27__default.join(dataDir, CONSOLE_STATE_FILE_NAME),
|
|
975
|
+
settingsFile: path27__default.join(dataDir, CONSOLE_SETTINGS_FILE_NAME),
|
|
976
|
+
capturesDir: path27__default.join(dataDir, CONSOLE_CAPTURES_DIR_NAME)
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function validateDesktopResourceRoot(root, expectedPackageRoot = resolveDesktopProtocolPackageRoot()) {
|
|
980
|
+
if (!path27__default.isAbsolute(root)) throw new Error("desktop_resource_root_not_absolute");
|
|
981
|
+
const resolvedRoot = fs14__default.realpathSync(root);
|
|
982
|
+
const expectedRoot = fs14__default.realpathSync(expectedPackageRoot);
|
|
983
|
+
if (resolvedRoot !== expectedRoot) throw new Error("desktop_resource_root_invalid");
|
|
984
|
+
const markerPath = path27__default.join(resolvedRoot, DESKTOP_RESOURCE_ROOT_MARKER);
|
|
985
|
+
const marker = fs14__default.readFileSync(markerPath, "utf8").trim();
|
|
986
|
+
if (marker !== String(DESKTOP_PROTOCOL_VERSION)) throw new Error("desktop_resource_root_marker_invalid");
|
|
987
|
+
return resolvedRoot;
|
|
988
|
+
}
|
|
989
|
+
function validateDesktopDevelopmentResourceRoot(root) {
|
|
990
|
+
if (!path27__default.isAbsolute(root)) throw new Error("desktop_resource_root_not_absolute");
|
|
991
|
+
let resolvedRoot;
|
|
992
|
+
try {
|
|
993
|
+
resolvedRoot = fs14__default.realpathSync(root);
|
|
994
|
+
} catch {
|
|
995
|
+
throw new Error("desktop_development_resource_root_invalid");
|
|
996
|
+
}
|
|
997
|
+
const expectedRoot = fs14__default.realpathSync(resolveDesktopProtocolPackageRoot());
|
|
998
|
+
if (resolvedRoot !== expectedRoot) throw new Error("desktop_development_resource_root_invalid");
|
|
999
|
+
return resolvedRoot;
|
|
1000
|
+
}
|
|
1001
|
+
function resolveDesktopProtocolPackageRoot() {
|
|
1002
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
1003
|
+
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
1004
|
+
try {
|
|
1005
|
+
return path27__default.dirname(requireFromHere.resolve(candidate));
|
|
1006
|
+
} catch (error51) {
|
|
1007
|
+
if (error51.code !== "MODULE_NOT_FOUND") throw error51;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
throw new Error("fleet_console_package_json_not_found");
|
|
1011
|
+
}
|
|
1012
|
+
function isSafeOwnerId(value) {
|
|
1013
|
+
return value.length > 0 && value.length <= 256 && /^[A-Za-z0-9._-]+$/.test(value);
|
|
1014
|
+
}
|
|
1015
|
+
function readFleetConsoleRelease(env = process.env) {
|
|
1016
|
+
const desktop = readDesktopProtocolEnvironment(env);
|
|
1017
|
+
if (desktop) return readReleaseAt(desktop.resourceRoot, isDesktopDevelopmentEnvironment(env) ? "local" : "desktop");
|
|
934
1018
|
const requireFromHere = createRequire(import.meta.url);
|
|
935
1019
|
const packageJsonPath = resolvePackageJsonPath(requireFromHere);
|
|
1020
|
+
const packageRoot = path27__default.dirname(packageJsonPath);
|
|
936
1021
|
const pkg = requireFromHere(packageJsonPath);
|
|
937
|
-
const packageRoot = path25__default.dirname(packageJsonPath);
|
|
938
|
-
const version2 = pkg.version ?? "";
|
|
939
1022
|
if (pkg.private === true) {
|
|
940
|
-
return
|
|
1023
|
+
return readReleaseAt(packageRoot, "local", requireFromHere);
|
|
941
1024
|
}
|
|
942
|
-
return
|
|
1025
|
+
return readReleaseAt(packageRoot, "stable", requireFromHere);
|
|
1026
|
+
}
|
|
1027
|
+
function readReleaseAt(packageRoot, channel, requireFromHere = createRequire(import.meta.url)) {
|
|
1028
|
+
const packageJsonPath = path27__default.join(packageRoot, "package.json");
|
|
1029
|
+
const pkg = requireFromHere(packageJsonPath);
|
|
1030
|
+
return { channel, version: pkg.version ?? "", packageRoot };
|
|
943
1031
|
}
|
|
944
1032
|
function resolvePackageJsonPath(requireFromHere) {
|
|
945
1033
|
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
@@ -953,25 +1041,23 @@ function resolvePackageJsonPath(requireFromHere) {
|
|
|
953
1041
|
}
|
|
954
1042
|
|
|
955
1043
|
// core/host/paths.ts
|
|
956
|
-
var LOCK_DIR_NAME2 = "fleet-console";
|
|
957
1044
|
var CONSOLE_RUNTIME_DIR_NAME = "console";
|
|
958
|
-
var
|
|
959
|
-
var
|
|
960
|
-
var
|
|
961
|
-
var
|
|
962
|
-
var CONSOLE_CAPTURES_DIR_NAME = "captures";
|
|
1045
|
+
var CONSOLE_DATA_DIR_NAME2 = "console";
|
|
1046
|
+
var CONSOLE_STATE_FILE_NAME2 = "state.json";
|
|
1047
|
+
var CONSOLE_SETTINGS_FILE_NAME2 = "settings.json";
|
|
1048
|
+
var CONSOLE_CAPTURES_DIR_NAME2 = "captures";
|
|
963
1049
|
function createConsolePaths(deps = {}) {
|
|
964
1050
|
const env = deps.env ?? process.env;
|
|
965
1051
|
const base = env.FLEET_CONSOLE_DIR ?? defaultConsoleBaseDir(deps);
|
|
966
|
-
return { dir: base, lockFile:
|
|
1052
|
+
return { dir: base, lockFile: path27__default.join(base, "console.lock") };
|
|
967
1053
|
}
|
|
968
1054
|
function createConsoleDataPaths(deps = {}) {
|
|
969
1055
|
const dir = defaultConsoleDataBaseDir(deps);
|
|
970
1056
|
return {
|
|
971
1057
|
dir,
|
|
972
|
-
stateFile:
|
|
973
|
-
settingsFile:
|
|
974
|
-
capturesDir:
|
|
1058
|
+
stateFile: path27__default.join(dir, CONSOLE_STATE_FILE_NAME2),
|
|
1059
|
+
settingsFile: path27__default.join(dir, CONSOLE_SETTINGS_FILE_NAME2),
|
|
1060
|
+
capturesDir: path27__default.join(dir, CONSOLE_CAPTURES_DIR_NAME2)
|
|
975
1061
|
};
|
|
976
1062
|
}
|
|
977
1063
|
function defaultConsoleBaseDir(deps) {
|
|
@@ -982,11 +1068,15 @@ function defaultConsoleBaseDir(deps) {
|
|
|
982
1068
|
return localConsoleDir(packageRoot);
|
|
983
1069
|
}
|
|
984
1070
|
const uid = deps.uid ?? (typeof process.getuid === "function" ? process.getuid() : 0);
|
|
985
|
-
return
|
|
1071
|
+
return resolveCanonicalStableConsolePaths({
|
|
1072
|
+
tmpDir: os4__default.tmpdir(),
|
|
1073
|
+
uid,
|
|
1074
|
+
fleetDataDir: getFleetDataDir()
|
|
1075
|
+
}).dir;
|
|
986
1076
|
}
|
|
987
1077
|
function defaultConsoleDataBaseDir(deps) {
|
|
988
1078
|
if (deps.fleetDataDir !== void 0) {
|
|
989
|
-
return
|
|
1079
|
+
return path27__default.join(deps.fleetDataDir, CONSOLE_DATA_DIR_NAME2);
|
|
990
1080
|
}
|
|
991
1081
|
const env = deps.env ?? process.env;
|
|
992
1082
|
if (env.FLEET_CONSOLE_DIR) {
|
|
@@ -998,10 +1088,14 @@ function defaultConsoleDataBaseDir(deps) {
|
|
|
998
1088
|
const packageRoot = deps.packageRoot ?? (release ??= readFleetConsoleRelease()).packageRoot;
|
|
999
1089
|
return localConsoleDir(packageRoot);
|
|
1000
1090
|
}
|
|
1001
|
-
return
|
|
1091
|
+
return resolveCanonicalStableConsolePaths({
|
|
1092
|
+
tmpDir: os4__default.tmpdir(),
|
|
1093
|
+
uid: typeof process.getuid === "function" ? process.getuid() : 0,
|
|
1094
|
+
fleetDataDir: getFleetDataDir()
|
|
1095
|
+
}).dataDir;
|
|
1002
1096
|
}
|
|
1003
1097
|
function localConsoleDir(packageRoot) {
|
|
1004
|
-
return
|
|
1098
|
+
return path27__default.join(path27__default.resolve(packageRoot, "..", ".."), ".fleet", CONSOLE_RUNTIME_DIR_NAME);
|
|
1005
1099
|
}
|
|
1006
1100
|
|
|
1007
1101
|
// ../../packages/fleet-carriers/src/dispatch/framework.ts
|
|
@@ -1837,10 +1931,10 @@ function mergeDefs(...defs) {
|
|
|
1837
1931
|
function cloneDef(schema) {
|
|
1838
1932
|
return mergeDefs(schema._zod.def);
|
|
1839
1933
|
}
|
|
1840
|
-
function getElementAtPath(obj,
|
|
1841
|
-
if (!
|
|
1934
|
+
function getElementAtPath(obj, path50) {
|
|
1935
|
+
if (!path50)
|
|
1842
1936
|
return obj;
|
|
1843
|
-
return
|
|
1937
|
+
return path50.reduce((acc, key) => acc?.[key], obj);
|
|
1844
1938
|
}
|
|
1845
1939
|
function promiseAllObject(promisesObj) {
|
|
1846
1940
|
const keys = Object.keys(promisesObj);
|
|
@@ -2249,11 +2343,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2249
2343
|
}
|
|
2250
2344
|
return false;
|
|
2251
2345
|
}
|
|
2252
|
-
function prefixIssues(
|
|
2346
|
+
function prefixIssues(path50, issues) {
|
|
2253
2347
|
return issues.map((iss) => {
|
|
2254
2348
|
var _a3;
|
|
2255
2349
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
2256
|
-
iss.path.unshift(
|
|
2350
|
+
iss.path.unshift(path50);
|
|
2257
2351
|
return iss;
|
|
2258
2352
|
});
|
|
2259
2353
|
}
|
|
@@ -2400,16 +2494,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2400
2494
|
}
|
|
2401
2495
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2402
2496
|
const fieldErrors = { _errors: [] };
|
|
2403
|
-
const processError = (error52,
|
|
2497
|
+
const processError = (error52, path50 = []) => {
|
|
2404
2498
|
for (const issue2 of error52.issues) {
|
|
2405
2499
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2406
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2500
|
+
issue2.errors.map((issues) => processError({ issues }, [...path50, ...issue2.path]));
|
|
2407
2501
|
} else if (issue2.code === "invalid_key") {
|
|
2408
|
-
processError({ issues: issue2.issues }, [...
|
|
2502
|
+
processError({ issues: issue2.issues }, [...path50, ...issue2.path]);
|
|
2409
2503
|
} else if (issue2.code === "invalid_element") {
|
|
2410
|
-
processError({ issues: issue2.issues }, [...
|
|
2504
|
+
processError({ issues: issue2.issues }, [...path50, ...issue2.path]);
|
|
2411
2505
|
} else {
|
|
2412
|
-
const fullpath = [...
|
|
2506
|
+
const fullpath = [...path50, ...issue2.path];
|
|
2413
2507
|
if (fullpath.length === 0) {
|
|
2414
2508
|
fieldErrors._errors.push(mapper(issue2));
|
|
2415
2509
|
} else {
|
|
@@ -2436,17 +2530,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2436
2530
|
}
|
|
2437
2531
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2438
2532
|
const result = { errors: [] };
|
|
2439
|
-
const processError = (error52,
|
|
2533
|
+
const processError = (error52, path50 = []) => {
|
|
2440
2534
|
var _a3, _b;
|
|
2441
2535
|
for (const issue2 of error52.issues) {
|
|
2442
2536
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2443
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2537
|
+
issue2.errors.map((issues) => processError({ issues }, [...path50, ...issue2.path]));
|
|
2444
2538
|
} else if (issue2.code === "invalid_key") {
|
|
2445
|
-
processError({ issues: issue2.issues }, [...
|
|
2539
|
+
processError({ issues: issue2.issues }, [...path50, ...issue2.path]);
|
|
2446
2540
|
} else if (issue2.code === "invalid_element") {
|
|
2447
|
-
processError({ issues: issue2.issues }, [...
|
|
2541
|
+
processError({ issues: issue2.issues }, [...path50, ...issue2.path]);
|
|
2448
2542
|
} else {
|
|
2449
|
-
const fullpath = [...
|
|
2543
|
+
const fullpath = [...path50, ...issue2.path];
|
|
2450
2544
|
if (fullpath.length === 0) {
|
|
2451
2545
|
result.errors.push(mapper(issue2));
|
|
2452
2546
|
continue;
|
|
@@ -2478,8 +2572,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2478
2572
|
}
|
|
2479
2573
|
function toDotPath(_path) {
|
|
2480
2574
|
const segs = [];
|
|
2481
|
-
const
|
|
2482
|
-
for (const seg of
|
|
2575
|
+
const path50 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2576
|
+
for (const seg of path50) {
|
|
2483
2577
|
if (typeof seg === "number")
|
|
2484
2578
|
segs.push(`[${seg}]`);
|
|
2485
2579
|
else if (typeof seg === "symbol")
|
|
@@ -15143,13 +15237,13 @@ function resolveRef(ref, ctx) {
|
|
|
15143
15237
|
if (!ref.startsWith("#")) {
|
|
15144
15238
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15145
15239
|
}
|
|
15146
|
-
const
|
|
15147
|
-
if (
|
|
15240
|
+
const path50 = ref.slice(1).split("/").filter(Boolean);
|
|
15241
|
+
if (path50.length === 0) {
|
|
15148
15242
|
return ctx.rootSchema;
|
|
15149
15243
|
}
|
|
15150
15244
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15151
|
-
if (
|
|
15152
|
-
const key =
|
|
15245
|
+
if (path50[0] === defsKey) {
|
|
15246
|
+
const key = path50[1];
|
|
15153
15247
|
if (!key || !ctx.defs[key]) {
|
|
15154
15248
|
throw new Error(`Reference not found: ${ref}`);
|
|
15155
15249
|
}
|
|
@@ -20652,11 +20746,24 @@ function getYoloModeId(cli) {
|
|
|
20652
20746
|
return "yolo";
|
|
20653
20747
|
}
|
|
20654
20748
|
}
|
|
20655
|
-
function
|
|
20656
|
-
|
|
20657
|
-
|
|
20749
|
+
function buildCodexConfigEnv(systemPrompt, configOverrides, baseConfigJson) {
|
|
20750
|
+
const map2 = parseBaseConfigMap(baseConfigJson);
|
|
20751
|
+
for (const override of configOverrides ?? []) {
|
|
20752
|
+
const eqIndex = override.indexOf("=");
|
|
20753
|
+
if (eqIndex < 0) {
|
|
20754
|
+
continue;
|
|
20755
|
+
}
|
|
20756
|
+
const key = override.slice(0, eqIndex);
|
|
20757
|
+
const rawValue = override.slice(eqIndex + 1);
|
|
20758
|
+
assignNestedKey(map2, key, parseOverrideValue(rawValue));
|
|
20759
|
+
}
|
|
20760
|
+
if (systemPrompt) {
|
|
20761
|
+
map2.developer_instructions = systemPrompt;
|
|
20762
|
+
}
|
|
20763
|
+
if (Object.keys(map2).length === 0) {
|
|
20764
|
+
return void 0;
|
|
20658
20765
|
}
|
|
20659
|
-
return
|
|
20766
|
+
return JSON.stringify(map2);
|
|
20660
20767
|
}
|
|
20661
20768
|
function buildConfigOverrideArgs(overrides) {
|
|
20662
20769
|
return overrides.flatMap((override) => ["-c", override]);
|
|
@@ -20684,27 +20791,44 @@ function mcpServerConfigsToAcp(servers) {
|
|
|
20684
20791
|
headers: server.headers ?? []
|
|
20685
20792
|
}));
|
|
20686
20793
|
}
|
|
20687
|
-
function
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
return "\\n";
|
|
20696
|
-
case "\f":
|
|
20697
|
-
return "\\f";
|
|
20698
|
-
case "\r":
|
|
20699
|
-
return "\\r";
|
|
20700
|
-
case '"':
|
|
20701
|
-
return '\\"';
|
|
20702
|
-
case "\\":
|
|
20703
|
-
return "\\\\";
|
|
20704
|
-
default:
|
|
20705
|
-
return `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
20794
|
+
function parseBaseConfigMap(baseConfigJson) {
|
|
20795
|
+
if (!baseConfigJson) {
|
|
20796
|
+
return {};
|
|
20797
|
+
}
|
|
20798
|
+
try {
|
|
20799
|
+
const parsed = JSON.parse(baseConfigJson);
|
|
20800
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
20801
|
+
return parsed;
|
|
20706
20802
|
}
|
|
20707
|
-
}
|
|
20803
|
+
} catch {
|
|
20804
|
+
}
|
|
20805
|
+
return {};
|
|
20806
|
+
}
|
|
20807
|
+
function parseOverrideValue(raw) {
|
|
20808
|
+
try {
|
|
20809
|
+
return JSON.parse(raw);
|
|
20810
|
+
} catch {
|
|
20811
|
+
if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
|
|
20812
|
+
return raw.slice(1, -1);
|
|
20813
|
+
}
|
|
20814
|
+
return raw;
|
|
20815
|
+
}
|
|
20816
|
+
}
|
|
20817
|
+
function assignNestedKey(target, dottedKey, value) {
|
|
20818
|
+
const segments = dottedKey.split(".");
|
|
20819
|
+
let cursor = target;
|
|
20820
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
20821
|
+
const segment = segments[i];
|
|
20822
|
+
const existing = cursor[segment];
|
|
20823
|
+
if (typeof existing === "object" && existing !== null && !Array.isArray(existing)) {
|
|
20824
|
+
cursor = existing;
|
|
20825
|
+
} else {
|
|
20826
|
+
const nested = {};
|
|
20827
|
+
cursor[segment] = nested;
|
|
20828
|
+
cursor = nested;
|
|
20829
|
+
}
|
|
20830
|
+
}
|
|
20831
|
+
cursor[segments[segments.length - 1]] = value;
|
|
20708
20832
|
}
|
|
20709
20833
|
|
|
20710
20834
|
// ../../packages/core-unified-agent/src/detector/CliDetector.ts
|
|
@@ -21965,12 +22089,12 @@ var CodexAppServerConnection = class extends BaseConnection {
|
|
|
21965
22089
|
);
|
|
21966
22090
|
}
|
|
21967
22091
|
findRolloutPathForThreadId(threadId) {
|
|
21968
|
-
const codexHome = this.codexHome ?? this.env.CODEX_HOME ??
|
|
22092
|
+
const codexHome = this.codexHome ?? this.env.CODEX_HOME ?? path27.join(this.env.HOME ?? "", ".codex");
|
|
21969
22093
|
if (!codexHome) {
|
|
21970
22094
|
return null;
|
|
21971
22095
|
}
|
|
21972
22096
|
for (const rootName of ["sessions", "archived_sessions"]) {
|
|
21973
|
-
const found = findRolloutPath(
|
|
22097
|
+
const found = findRolloutPath(path27.join(codexHome, rootName), threadId);
|
|
21974
22098
|
if (found) {
|
|
21975
22099
|
return found;
|
|
21976
22100
|
}
|
|
@@ -22077,11 +22201,11 @@ function isMissingRolloutError(error51, threadId) {
|
|
|
22077
22201
|
return error51 instanceof Error && error51.message.includes(`no rollout found for thread id ${threadId}`);
|
|
22078
22202
|
}
|
|
22079
22203
|
function findRolloutPath(root, threadId) {
|
|
22080
|
-
if (!
|
|
22204
|
+
if (!fs14.existsSync(root)) {
|
|
22081
22205
|
return null;
|
|
22082
22206
|
}
|
|
22083
|
-
for (const entry of
|
|
22084
|
-
const entryPath =
|
|
22207
|
+
for (const entry of fs14.readdirSync(root, { withFileTypes: true })) {
|
|
22208
|
+
const entryPath = path27.join(root, entry.name);
|
|
22085
22209
|
if (entry.isDirectory()) {
|
|
22086
22210
|
const found = findRolloutPath(entryPath, threadId);
|
|
22087
22211
|
if (found) {
|
|
@@ -22099,13 +22223,11 @@ function findRolloutPath(root, threadId) {
|
|
|
22099
22223
|
// ../../packages/core-unified-agent/src/client/UnifiedCodexAgentClient.ts
|
|
22100
22224
|
var CODEX_TURN_LEVEL_CONFIG_KEYS = /* @__PURE__ */ new Set(["effort", "model"]);
|
|
22101
22225
|
var CODEX_THREAD_POLICY_CONFIG_KEYS = /* @__PURE__ */ new Set(["approvalPolicy", "sandbox"]);
|
|
22102
|
-
var CODEX_ACP_PROMPT_FALLBACK = process.platform === "win32";
|
|
22103
22226
|
var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
22104
22227
|
connection = null;
|
|
22105
22228
|
sessionId = null;
|
|
22106
22229
|
sessionCwd = null;
|
|
22107
22230
|
currentSystemPrompt = null;
|
|
22108
|
-
firstPromptPending = null;
|
|
22109
22231
|
pendingOverrides = null;
|
|
22110
22232
|
detector = new CliDetector();
|
|
22111
22233
|
on(event, listener) {
|
|
@@ -22126,9 +22248,10 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22126
22248
|
throw new Error("UnifiedCodexAgentClient\uB294 codex CLI\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
|
|
22127
22249
|
}
|
|
22128
22250
|
this.validateModelEffort(options.model, options.effort);
|
|
22129
|
-
{
|
|
22251
|
+
if (shouldUseCodexAcp(options)) {
|
|
22130
22252
|
return this.connectAcp(options);
|
|
22131
22253
|
}
|
|
22254
|
+
return this.connectAppServer(options);
|
|
22132
22255
|
}
|
|
22133
22256
|
async connectAppServer(options) {
|
|
22134
22257
|
const backend = getBackendConfig("codex");
|
|
@@ -22197,22 +22320,17 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22197
22320
|
async connectAcp(options) {
|
|
22198
22321
|
const cleanEnv = cleanEnvironment(process.env, options.env);
|
|
22199
22322
|
const spawnConfig = createSpawnConfig("codex", options);
|
|
22200
|
-
const
|
|
22201
|
-
|
|
22202
|
-
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
void 0,
|
|
22206
|
-
void 0,
|
|
22207
|
-
developerInstructions
|
|
22208
|
-
)
|
|
22209
|
-
];
|
|
22323
|
+
const codexConfigEnv = buildCodexConfigEnv(
|
|
22324
|
+
options.systemPrompt,
|
|
22325
|
+
options.configOverrides,
|
|
22326
|
+
cleanEnv.CODEX_CONFIG
|
|
22327
|
+
);
|
|
22210
22328
|
const connection = new AcpConnection({
|
|
22211
22329
|
command: spawnConfig.command,
|
|
22212
|
-
args,
|
|
22330
|
+
args: spawnConfig.args,
|
|
22213
22331
|
cliType: "codex",
|
|
22214
22332
|
cwd: options.cwd,
|
|
22215
|
-
env: { ...cleanEnv },
|
|
22333
|
+
env: { ...cleanEnv, ...codexConfigEnv ? { CODEX_CONFIG: codexConfigEnv } : {} },
|
|
22216
22334
|
requestTimeout: options.timeout ?? 6e5,
|
|
22217
22335
|
initTimeout: options.timeout ?? 6e4,
|
|
22218
22336
|
promptIdleTimeout: options.promptIdleTimeout ?? 6e5,
|
|
@@ -22309,17 +22427,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22309
22427
|
if (!this.sessionId) {
|
|
22310
22428
|
throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
22311
22429
|
}
|
|
22312
|
-
|
|
22313
|
-
if (!systemPrompt) {
|
|
22314
|
-
return this.connection.sendPrompt(this.sessionId, content);
|
|
22315
|
-
}
|
|
22316
|
-
const userBlocks = typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
22317
|
-
const response = await this.connection.sendPrompt(this.sessionId, [
|
|
22318
|
-
{ type: "text", text: systemPrompt },
|
|
22319
|
-
...userBlocks
|
|
22320
|
-
]);
|
|
22321
|
-
this.firstPromptPending = null;
|
|
22322
|
-
return response;
|
|
22430
|
+
return this.connection.sendPrompt(this.sessionId, content);
|
|
22323
22431
|
}
|
|
22324
22432
|
this.applyPendingOverrides();
|
|
22325
22433
|
const input = typeof content === "string" ? [{ type: "text", text: content, text_elements: [] }] : content.map((block) => "text" in block ? { type: "text", text: block.text, text_elements: [] } : { type: "text", text: JSON.stringify(block), text_elements: [] });
|
|
@@ -22401,8 +22509,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22401
22509
|
mcpServers: this.resolveAcpMcpServers(mcpServers)
|
|
22402
22510
|
});
|
|
22403
22511
|
this.sessionId = sessionId;
|
|
22404
|
-
this.currentSystemPrompt = null;
|
|
22405
|
-
this.firstPromptPending = null;
|
|
22406
22512
|
return;
|
|
22407
22513
|
}
|
|
22408
22514
|
if (mcpServers?.length) {
|
|
@@ -22439,7 +22545,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22439
22545
|
const session = await this.connection.reconnectSession(targetCwd);
|
|
22440
22546
|
this.sessionId = session.sessionId;
|
|
22441
22547
|
this.sessionCwd = targetCwd;
|
|
22442
|
-
this.firstPromptPending = this.currentSystemPrompt;
|
|
22443
22548
|
return {
|
|
22444
22549
|
cli: "codex",
|
|
22445
22550
|
protocol: "acp",
|
|
@@ -22467,7 +22572,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22467
22572
|
this.sessionId = session.sessionId;
|
|
22468
22573
|
this.sessionCwd = options.cwd;
|
|
22469
22574
|
this.currentSystemPrompt = options.systemPrompt ?? null;
|
|
22470
|
-
this.firstPromptPending = CODEX_ACP_PROMPT_FALLBACK && !options.sessionId ? options.systemPrompt ?? null : null;
|
|
22471
22575
|
this.pendingOverrides = {
|
|
22472
22576
|
turnConfig: {},
|
|
22473
22577
|
threadConfig: {}
|
|
@@ -22485,7 +22589,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22485
22589
|
this.sessionId = null;
|
|
22486
22590
|
this.sessionCwd = null;
|
|
22487
22591
|
this.currentSystemPrompt = null;
|
|
22488
|
-
this.firstPromptPending = null;
|
|
22489
22592
|
this.pendingOverrides = null;
|
|
22490
22593
|
}
|
|
22491
22594
|
validateModelEffort(modelId, effort) {
|
|
@@ -22696,9 +22799,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22696
22799
|
return "read-only";
|
|
22697
22800
|
}
|
|
22698
22801
|
}
|
|
22699
|
-
buildStartupConfigArgs(overrides, servers, modeMapping
|
|
22802
|
+
buildStartupConfigArgs(overrides, servers, modeMapping) {
|
|
22700
22803
|
const configArgs = [
|
|
22701
|
-
...buildCodexDeveloperInstructionConfig(developerInstructions),
|
|
22702
22804
|
...modeMapping ? [
|
|
22703
22805
|
`approval_policy="${modeMapping.approvalPolicy}"`,
|
|
22704
22806
|
`sandbox_mode="${modeMapping.sandbox}"`
|
|
@@ -22787,6 +22889,11 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22787
22889
|
return patterns.some((pattern) => pattern.test(text));
|
|
22788
22890
|
}
|
|
22789
22891
|
};
|
|
22892
|
+
function shouldUseCodexAcp(options) {
|
|
22893
|
+
const value = options.env?.CODEX_USE_ACP ?? process.env.CODEX_USE_ACP;
|
|
22894
|
+
const normalized = value?.trim().toLowerCase();
|
|
22895
|
+
return normalized !== "false" && normalized !== "0";
|
|
22896
|
+
}
|
|
22790
22897
|
var UnifiedCursorAgentClient = class extends EventEmitter {
|
|
22791
22898
|
connection = null;
|
|
22792
22899
|
sessionId = null;
|
|
@@ -24912,7 +25019,7 @@ function formatInstallCommand(packageNames, command2, versionOrChannel) {
|
|
|
24912
25019
|
return `${command2} i -g ${packageNames.map((name) => `${name}@${versionOrChannel}`).join(" ")}`;
|
|
24913
25020
|
}
|
|
24914
25021
|
function isPathInside(deps, child, parent) {
|
|
24915
|
-
const separator = deps.platform === "win32" ? "\\" :
|
|
25022
|
+
const separator = deps.platform === "win32" ? "\\" : path27__default.sep;
|
|
24916
25023
|
return child === parent || child.startsWith(`${parent}${separator}`);
|
|
24917
25024
|
}
|
|
24918
25025
|
function normalizeExistingPath(deps, value) {
|
|
@@ -24927,10 +25034,10 @@ function normalizePath(deps, value) {
|
|
|
24927
25034
|
return deps.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
24928
25035
|
}
|
|
24929
25036
|
function resolvePathForPlatform(platform, value) {
|
|
24930
|
-
return platform === "win32" ?
|
|
25037
|
+
return platform === "win32" ? path27__default.win32.resolve(value) : path27__default.resolve(value);
|
|
24931
25038
|
}
|
|
24932
25039
|
function joinPathForPlatform(platform, ...parts) {
|
|
24933
|
-
return platform === "win32" ?
|
|
25040
|
+
return platform === "win32" ? path27__default.win32.join(...parts) : path27__default.join(...parts);
|
|
24934
25041
|
}
|
|
24935
25042
|
function defaultExecFile(file2, args) {
|
|
24936
25043
|
return execFileSync(file2, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -25039,7 +25146,7 @@ function readRawCarriersOrDefaultStore() {
|
|
|
25039
25146
|
return readCarriers();
|
|
25040
25147
|
}
|
|
25041
25148
|
function getCarriersFilePath() {
|
|
25042
|
-
return
|
|
25149
|
+
return path27.join(resolveStoreDir(), FILENAME);
|
|
25043
25150
|
}
|
|
25044
25151
|
function updateCarriers(mutator) {
|
|
25045
25152
|
withStoreLock(() => {
|
|
@@ -25053,7 +25160,7 @@ function updateCarriers(mutator) {
|
|
|
25053
25160
|
function withStoreLock(operation) {
|
|
25054
25161
|
const dir = resolveStoreDir();
|
|
25055
25162
|
ensureSafeDirectory(dir);
|
|
25056
|
-
const lockDir =
|
|
25163
|
+
const lockDir = path27.join(dir, LOCK_DIRNAME);
|
|
25057
25164
|
return withDirectoryLock(
|
|
25058
25165
|
{ lockDir, ownerFileName: LOCK_OWNER_FILENAME },
|
|
25059
25166
|
operation
|
|
@@ -25066,20 +25173,20 @@ function readCarriers() {
|
|
|
25066
25173
|
return readCarriersFromDir(resolveStoreDir());
|
|
25067
25174
|
}
|
|
25068
25175
|
function readCarriersFromDir(dir) {
|
|
25069
|
-
const filePath =
|
|
25176
|
+
const filePath = path27.join(dir, FILENAME);
|
|
25070
25177
|
let fd;
|
|
25071
25178
|
try {
|
|
25072
|
-
const stat5 =
|
|
25179
|
+
const stat5 = fs14.lstatSync(filePath);
|
|
25073
25180
|
if (!stat5.isFile() || stat5.isSymbolicLink()) return {};
|
|
25074
|
-
fd =
|
|
25075
|
-
if (!
|
|
25076
|
-
return sanitizeFleetCarriers(JSON.parse(
|
|
25181
|
+
fd = fs14.openSync(filePath, fs14.constants.O_RDONLY | NOFOLLOW_FLAG);
|
|
25182
|
+
if (!fs14.fstatSync(fd).isFile()) return {};
|
|
25183
|
+
return sanitizeFleetCarriers(JSON.parse(fs14.readFileSync(fd, "utf-8")));
|
|
25077
25184
|
} catch {
|
|
25078
25185
|
return {};
|
|
25079
25186
|
} finally {
|
|
25080
25187
|
if (fd !== void 0) {
|
|
25081
25188
|
try {
|
|
25082
|
-
|
|
25189
|
+
fs14.closeSync(fd);
|
|
25083
25190
|
} catch {
|
|
25084
25191
|
}
|
|
25085
25192
|
}
|
|
@@ -25087,7 +25194,7 @@ function readCarriersFromDir(dir) {
|
|
|
25087
25194
|
}
|
|
25088
25195
|
function recordLastLocalWriteFingerprint(filePath, generation) {
|
|
25089
25196
|
try {
|
|
25090
|
-
const st =
|
|
25197
|
+
const st = fs14.statSync(filePath);
|
|
25091
25198
|
runtimeState.lastLocalWriteFingerprint = { generation, mtimeMs: st.mtimeMs, size: st.size };
|
|
25092
25199
|
} catch {
|
|
25093
25200
|
runtimeState.lastLocalWriteFingerprint = { generation, mtimeMs: 0, size: 0 };
|
|
@@ -25095,7 +25202,7 @@ function recordLastLocalWriteFingerprint(filePath, generation) {
|
|
|
25095
25202
|
}
|
|
25096
25203
|
function writeCarriers(s) {
|
|
25097
25204
|
const dir = resolveStoreDir();
|
|
25098
|
-
const filePath =
|
|
25205
|
+
const filePath = path27.join(dir, FILENAME);
|
|
25099
25206
|
const next = serializeFleetCarriers(s);
|
|
25100
25207
|
writeAtomicSync(filePath, `${JSON.stringify(next, null, 2)}
|
|
25101
25208
|
`, { fsync: false, mode: 420 });
|
|
@@ -25120,7 +25227,7 @@ function sanitizeFleetCarriers(value) {
|
|
|
25120
25227
|
}
|
|
25121
25228
|
function unlinkStaleSubagentModeFile(dir) {
|
|
25122
25229
|
try {
|
|
25123
|
-
|
|
25230
|
+
fs14.unlinkSync(path27.join(dir, STALE_SUBAGENT_MODE_FILENAME));
|
|
25124
25231
|
} catch {
|
|
25125
25232
|
}
|
|
25126
25233
|
}
|
|
@@ -25649,7 +25756,7 @@ var CHRONICLE_DEFAULTS = {
|
|
|
25649
25756
|
};
|
|
25650
25757
|
var CARRIER_METADATA = {
|
|
25651
25758
|
// ── Tier 1: Routing ──
|
|
25652
|
-
title: "
|
|
25759
|
+
title: "Chief Knowledge Officer",
|
|
25653
25760
|
summary: "Documentation steward across two surfaces: codebase markdown (AGENTS.md/README/CHANGELOG/inline docs) and Fleet Wiki entries proposed via the wiki patch queue.",
|
|
25654
25761
|
category: "operations",
|
|
25655
25762
|
whenToUse: [
|
|
@@ -25662,7 +25769,7 @@ var CARRIER_METADATA = {
|
|
|
25662
25769
|
"before implementation and verification are complete",
|
|
25663
25770
|
"code modification (\u2192genesis) or code review (\u2192sentinel)",
|
|
25664
25771
|
"architectural judgment (\u2192nimitz) or release-scope planning decisions (\u2192kirov)",
|
|
25665
|
-
"Fleet Wiki patch approval \u2014
|
|
25772
|
+
"Fleet Wiki patch approval \u2014 host agent retains the wiki_patch_queue approve/reject gate"
|
|
25666
25773
|
],
|
|
25667
25774
|
requestBlocks: [
|
|
25668
25775
|
{
|
|
@@ -25698,18 +25805,18 @@ var CARRIER_METADATA = {
|
|
|
25698
25805
|
],
|
|
25699
25806
|
// ── Tier 2: Composition ──
|
|
25700
25807
|
permissions: [
|
|
25701
|
-
"CRITICAL: Fleet Wiki entries are governed by the Fleet Wiki workspace AGENTS.md doctrine \u2014 propose via wiki_ingest only, and NEVER edit wiki entries or system-managed indexes/logs by hand.
|
|
25808
|
+
"CRITICAL: Fleet Wiki entries are governed by the Fleet Wiki workspace AGENTS.md doctrine \u2014 propose via wiki_ingest only, and NEVER edit wiki entries or system-managed indexes/logs by hand. The host agent holds the wiki_patch_queue approve/reject gate.",
|
|
25702
25809
|
"CRITICAL: Owns codebase markdown \u2014 AGENTS.md, README, CHANGELOG, and inline doc files outside the Fleet Wiki workspace \u2014 and may write them directly. NEVER modify source code logic (report issues instead).",
|
|
25703
|
-
"MUST treat the
|
|
25704
|
-
"Owns expression details (tone, wording, paragraph flow within domain conventions) but MUST NOT silently change structural decisions specified by the
|
|
25810
|
+
"MUST treat the host agent's <doc_type>, <audience>, <scope>, and <change_scope> as binding contracts. NEVER redefine or expand documentation scope autonomously.",
|
|
25811
|
+
"Owns expression details (tone, wording, paragraph flow within domain conventions) but MUST NOT silently change structural decisions specified by the host agent (sections, items, target files).",
|
|
25705
25812
|
"MUST NOT make go/no-go, release timing, or release-scope decisions (escalate to Kirov/Nimitz). May detect and document breaking changes only."
|
|
25706
25813
|
],
|
|
25707
25814
|
outputFormat: `Deliver the documentation artifact directly.
|
|
25708
25815
|
[Codebase Doc] Write the .md/AGENTS.md/README/CHANGELOG file(s) directly via filesystem tools.
|
|
25709
|
-
[Fleet Wiki] Compose the entry body and enqueue via wiki_ingest. Report the patch_id; never edit wiki entries or system-managed files by hand.
|
|
25816
|
+
[Fleet Wiki] Compose the entry body and enqueue via wiki_ingest. Report the patch_id; never edit wiki entries or system-managed files by hand. The host agent approves via wiki_patch_queue.
|
|
25710
25817
|
After delivery, provide a brief completion report.
|
|
25711
25818
|
[Required] always include:
|
|
25712
|
-
**Workflow** \u2014 One of: 'Codebase Doc' or 'Fleet Wiki' (state which workflow this
|
|
25819
|
+
**Workflow** \u2014 One of: 'Codebase Doc' or 'Fleet Wiki' (state which workflow this task used).
|
|
25713
25820
|
**Documents written / Patches enqueued** \u2014 Each file created/modified (Codebase Doc) OR each patch_id enqueued (Fleet Wiki) with target path.
|
|
25714
25821
|
**Cascade .md audit** \u2014 (Codebase Doc only) Each .md inspected: path, status (updated / already consistent / not applicable), 1-line summary if updated.
|
|
25715
25822
|
[If applicable] omit if not relevant:
|
|
@@ -25718,10 +25825,10 @@ After delivery, provide a brief completion report.
|
|
|
25718
25825
|
Keep the completion report concise \u2014 the documentation/patch itself is the primary deliverable.`,
|
|
25719
25826
|
principles: [
|
|
25720
25827
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25721
|
-
"Two workflows, one carrier \u2014 never mix the two surfaces. (A) Codebase Doc: direct filesystem edits to .md/AGENTS.md/README/CHANGELOG outside the Fleet Wiki workspace. (B) Fleet Wiki: orient \u2192 consult \u2192 lint \u2192 wiki_ingest to enqueue \u2192
|
|
25722
|
-
"[Fleet Wiki] Read the Fleet Wiki workspace AGENTS.md before the first wiki operation in a
|
|
25828
|
+
"Two workflows, one carrier \u2014 never mix the two surfaces. (A) Codebase Doc: direct filesystem edits to .md/AGENTS.md/README/CHANGELOG outside the Fleet Wiki workspace. (B) Fleet Wiki: orient \u2192 consult \u2192 lint \u2192 wiki_ingest to enqueue \u2192 host agent approves via wiki_patch_queue. Decide the workflow from <doc_type>; if ambiguous, escalate before acting.",
|
|
25829
|
+
"[Fleet Wiki] Read the Fleet Wiki workspace AGENTS.md before the first wiki operation in a task \u2014 it is the authoritative doctrine for boundaries, roles, gates, schema reference, and escalation. The active entry schema may evolve; always consult the workspace doctrine and current schema each task rather than relying on prior memory.",
|
|
25723
25830
|
"[Fleet Wiki] Provide raw evidence (source) alongside every wiki_ingest call \u2014 synthesized entry body must cite raw substance without copying verbatim. The system writes raw, queue, and index automatically.",
|
|
25724
|
-
"[Codebase Doc] Every
|
|
25831
|
+
"[Codebase Doc] Every task must include a cascade .md audit \u2014 identify all .md files within the change scope, verify they reflect current state, and cross-reference parent/child AGENTS.md to prevent doctrinal conflicts.",
|
|
25725
25832
|
"[Codebase Doc] CRITICAL: README.md files must ONLY be updated where they already exist \u2014 NEVER create new README.md files. If a directory lacks a README.md, leave it as-is and note the absence in the audit report.",
|
|
25726
25833
|
"If additional documentation scope seems needed, MUST report it as a follow-up suggestion in the completion report. NEVER silently expand the audit/update scope.",
|
|
25727
25834
|
"Change-impact documentation must be factual and observable \u2014 never recommend whether a change should ship, be reverted, or be delayed."
|
|
@@ -25743,8 +25850,8 @@ var GENESIS_DEFAULTS = {
|
|
|
25743
25850
|
};
|
|
25744
25851
|
var CARRIER_METADATA2 = {
|
|
25745
25852
|
// ── Tier 1: Routing ──
|
|
25746
|
-
title: "
|
|
25747
|
-
summary: "Full-stack implementation workhorse \u2014 builds features, writes production-quality clean code, and maintains structural integrity throughout.
|
|
25853
|
+
title: "Chief Engineer",
|
|
25854
|
+
summary: "Full-stack implementation workhorse \u2014 builds features, writes production-quality clean code, and maintains structural integrity throughout.",
|
|
25748
25855
|
category: "operations",
|
|
25749
25856
|
whenToUse: [
|
|
25750
25857
|
"new features",
|
|
@@ -25771,14 +25878,14 @@ var CARRIER_METADATA2 = {
|
|
|
25771
25878
|
// ── Tier 2: Composition ──
|
|
25772
25879
|
permissions: [
|
|
25773
25880
|
"Full access to the codebase \u2014 read, write, and execute commands.",
|
|
25774
|
-
"Owns implementation details (internal helper structure, code organization, local naming) ONLY within the design boundaries set by the
|
|
25775
|
-
"MUST NOT substitute autonomous design judgment for the
|
|
25881
|
+
"Owns implementation details (internal helper structure, code organization, local naming) ONLY within the design boundaries set by the host agent's instructions.",
|
|
25882
|
+
"MUST NOT substitute autonomous design judgment for the host agent's explicit design decisions \u2014 interface unification vs separation, type/function names, directory structure, public surface shape, and any choice the host agent has specified are BINDING contracts, not suggestions.",
|
|
25776
25883
|
"MUST NOT silently re-plan, expand scope, invent alternative workflows, or shrink the assigned work beyond what the instructions specify.",
|
|
25777
25884
|
"MUST NOT silently absorb Kirov's planning role or Nimitz's architecture arbitration role when those inputs are clearly missing."
|
|
25778
25885
|
],
|
|
25779
25886
|
principles: [
|
|
25780
25887
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25781
|
-
"MUST treat the
|
|
25888
|
+
"MUST treat the host agent's <objective>, <scope>, <constraints>, and <references> as binding design contracts. Specific design decisions stated in the instructions MUST be implemented as-instructed, not as 'cleaner' or 'better' substitutions.",
|
|
25782
25889
|
"If an alternative design seems superior, MUST complete the assigned work AS-INSTRUCTED first, then report the alternative ONLY as a follow-up suggestion. NEVER substitute the alternative silently.",
|
|
25783
25890
|
"On ambiguity or apparent conflict in the instructions, MUST report back and request clarification instead of choosing autonomously.",
|
|
25784
25891
|
"Follow planning artifacts when provided \u2014 do not re-plan work that Kirov has already structured unless the input is clearly invalid.",
|
|
@@ -25812,8 +25919,8 @@ var KIROV_DEFAULTS = {
|
|
|
25812
25919
|
};
|
|
25813
25920
|
var CARRIER_METADATA3 = {
|
|
25814
25921
|
// ── Tier 1: Routing ──
|
|
25815
|
-
title: "
|
|
25816
|
-
summary: "Clarifies requirements, closes planning gaps, and writes executable .fleet/plans/*.md
|
|
25922
|
+
title: "Operational Planning",
|
|
25923
|
+
summary: "Clarifies requirements, closes planning gaps, and writes one executable .fleet/plans/*.md plan_file SSoT with explicit parallel lanes, ownership, dependencies, QA gates, acceptance criteria, documentation impacts, and escalation triggers.",
|
|
25817
25924
|
category: "planning",
|
|
25818
25925
|
whenToUse: [
|
|
25819
25926
|
"structured .fleet/plans/*.md plan_file requests, PRD decomposition, or Ohio-executable execution plans",
|
|
@@ -25821,14 +25928,14 @@ var CARRIER_METADATA3 = {
|
|
|
25821
25928
|
"medium/large refactors, migrations, cross-module work, or materially ambiguous requirements where planning must close gaps before execution"
|
|
25822
25929
|
],
|
|
25823
25930
|
whenNotToUse: [
|
|
25824
|
-
"single-Carrier tasks with \u22643 dependent steps and clear acceptance criteria (\
|
|
25931
|
+
"single-Carrier tasks with \u22643 dependent steps and clear acceptance criteria (\u2192host agent plans directly)",
|
|
25825
25932
|
"direct code implementation, single-shot (\u2192genesis) or plan-driven (\u2192ohio)",
|
|
25826
|
-
"final architecture arbitration (\u2192nimitz) or
|
|
25933
|
+
"final architecture arbitration (\u2192nimitz) or dispatches needing reconnaissance first (\u2192vanguard/tempest)"
|
|
25827
25934
|
],
|
|
25828
25935
|
requestBlocks: [
|
|
25829
25936
|
{ tag: "goal", hint: "What the user wants to build, fix, or achieve \u2014 specific feature, PRD, behavior, and any stated constraints.", required: true },
|
|
25830
25937
|
{ tag: "plan_file", hint: "If provided, exact repo-relative .fleet/plans/{name}.md path Kirov must create or update. Do not choose a different filename.", required: false },
|
|
25831
|
-
{ tag: "context", hint: "Relevant codebase context \u2014 files, modules, patterns, prior
|
|
25938
|
+
{ tag: "context", hint: "Relevant codebase context \u2014 files, modules, patterns, prior host-agent direction, or implementation realities the planner should respect.", required: false },
|
|
25832
25939
|
{ tag: "constraints", hint: "Business rules, tech stack requirements, scope boundaries, fixed decisions, or explicit exclusions the plan must respect.", required: false },
|
|
25833
25940
|
{ tag: "intent_type", hint: "If known: Refactoring | Build from Scratch | Mid-sized | Collaborative | Architecture Follow-through | Research-to-Plan.", required: false }
|
|
25834
25941
|
],
|
|
@@ -25836,27 +25943,31 @@ var CARRIER_METADATA3 = {
|
|
|
25836
25943
|
// ── Tier 2: Composition ──
|
|
25837
25944
|
permissions: [
|
|
25838
25945
|
"CRITICAL: Write access strictly limited to .fleet/plans/*.md and .fleet/drafts/*.md. NEVER modify source code, configs, or any non-markdown file.",
|
|
25839
|
-
"MUST honor exact provided .fleet/plans/*.md paths. Success means creating or updating
|
|
25840
|
-
"MUST return unresolved architecture choices, system-design trade-offs, and technical path decisions to the
|
|
25841
|
-
"MUST report Blockers or
|
|
25946
|
+
"MUST honor exact provided .fleet/plans/*.md paths. Keep one plan_file as the execution SSoT: Success means creating or updating that executable plan_file unless the host agent explicitly requests draft-only work. Do not create split plan files for parallel lanes.",
|
|
25947
|
+
"MUST return unresolved architecture choices, system-design trade-offs, and technical path decisions to the host agent for direction instead of silently deciding them.",
|
|
25948
|
+
"MUST report Blockers or Host Direction Needed instead of claiming completion when the plan file cannot be written or the schema cannot be filled."
|
|
25842
25949
|
],
|
|
25843
25950
|
outputFormat: `After completing the plan, provide a structured plan summary.
|
|
25844
25951
|
[Required] always include:
|
|
25845
25952
|
**Plan file** \u2014 Exact generated or updated .fleet/plans/{name}.md path.
|
|
25846
|
-
**Execution
|
|
25953
|
+
**Execution Topology** \u2014 Ordered waves, stable Wave/Lane IDs, parallel markers (e.g., "W1 \u2192 W2 || W3 \u2192 W4"), and critical dependencies.
|
|
25954
|
+
**Dispatch Manifest** \u2014 Each lane's exact write set, start condition, eligible concurrent lanes, integration gate, handoff, and rollback unit.
|
|
25847
25955
|
**Scope: IN** \u2014 What is explicitly included in the plan.
|
|
25848
25956
|
**Scope: OUT** \u2014 What is explicitly excluded.
|
|
25849
25957
|
**Next step** \u2014 Run \`/start-work {name}\` to execute the plan.
|
|
25850
25958
|
[If applicable] omit if not relevant:
|
|
25851
25959
|
**Blockers** \u2014 Why no plan file was written or why the schema cannot be filled.
|
|
25852
|
-
**
|
|
25960
|
+
**Host Direction Needed** \u2014 Architecture, trade-off, or path choices needing confirmation.
|
|
25853
25961
|
Keep the summary concise \u2014 bullets and short lines only. No narrative paragraphs.`,
|
|
25854
25962
|
principles: [
|
|
25855
25963
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25856
25964
|
"Clarify only to unlock planning \u2014 ask the minimum questions needed to produce a reliable execution plan.",
|
|
25857
25965
|
"Pre-plan gap analysis is mandatory internal input, never a substitute final output. May launch background explore/librarian sub-agents for context gathering. Use incremental write protocol: Write() skeleton first, then Edit() in 2-4 task batches.",
|
|
25858
|
-
'The .fleet/plans/*.md file MUST contain this exact default Markdown template unless the
|
|
25859
|
-
"
|
|
25966
|
+
'The .fleet/plans/*.md file MUST contain this exact default Markdown template unless the host agent provides a different template: # Objective, # File Ownership, # Execution Topology, - Execution mode: Sequential | Parallel, - Shared mutable resources:, # Waves, ## Wave N \u2014 <name>, ### Lane WN-X \u2014 <name>, - Exact write set:, - Read dependencies:, - Dependency/start condition:, - Eligible concurrent lanes: (use "none" for serialized work), - Integration gate:, - Handoff:, - Rollback unit:, - Implementation summary:, - Verification/static checks:, - Escalation triggers:, # Dispatch Manifest, - Full-plan Ohio invocation (execution_scope omitted or all): allowed sequentially only when Execution mode is Sequential or Execution Topology is absent; for Parallel, dispatch exact Lane IDs only and never combine a full-plan invocation with lane jobs, - Lane WN-X \u2014 <name>: exact write set, read dependencies, dependency/start condition, eligible concurrent lanes, integration gate, handoff, and rollback unit summary for dispatch, # QA Gates, # Acceptance Criteria, # Documentation Updates, # Final Review Loop. Required headings must not be renamed, reordered, or omitted; extra sections allowed only after them. Write headings into the plan_file itself, not just mentioned in the response. For tiny tasks, mark non-applicable fields "Not applicable" rather than deleting them.',
|
|
25967
|
+
"Execution Topology is mandatory for every plan. It MUST declare Execution mode: Sequential | Parallel, shared mutable resources, ordered waves, and stable Wave/Lane IDs; a lane may be marked parallel only when its exact non-overlapping write set and read dependencies prove it is safe to run concurrently.",
|
|
25968
|
+
"Dispatch Manifest is mandatory for every plan. For each parallel lane, declare: stable Wave/Lane ID; exact non-overlapping write set; read dependencies; dependency/start condition; eligible concurrent lanes; integration gate; handoff; and rollback unit. It MUST also state that full-plan Ohio invocation (execution_scope omitted or all) is allowed sequentially only for Sequential or absent Execution Topology; Parallel requires exact Lane IDs, makes full-plan invocation unavailable as an alternative dispatch path, and never combines it with lane jobs. If disjoint lanes cannot be proven safe, mark the work sequential rather than calling it parallel.",
|
|
25969
|
+
"Under each lane's '- Implementation summary:' (or the wave's when a wave declares no lanes), enumerate the concrete implementation steps as a Markdown checkbox task list \u2014 one '- [ ]' line per step, roughly 3-7 per lane; use '- [x]' only for work already completed at planning time. These checkboxes are the machine-readable progress contract consumed by the execution carrier and the Console Plans surface \u2014 do not encode task state in any other notation (no emoji, no status words, no strikethrough).",
|
|
25970
|
+
"Return unresolved architecture and deep trade-off decisions to the host agent for direction.",
|
|
25860
25971
|
"Optimize for direct execution from the resulting plan_file."
|
|
25861
25972
|
]
|
|
25862
25973
|
};
|
|
@@ -25876,8 +25987,8 @@ var NIMITZ_DEFAULTS = {
|
|
|
25876
25987
|
};
|
|
25877
25988
|
var CARRIER_METADATA4 = {
|
|
25878
25989
|
// ── Tier 1: Routing ──
|
|
25879
|
-
title: "
|
|
25880
|
-
summary: "Read-only strategic command \u2014 decides the technical path through doctrinal judgment, architecture decisions, deep analysis, and trade-off adjudication
|
|
25990
|
+
title: "Strategic Command & Judgment",
|
|
25991
|
+
summary: "Read-only strategic command \u2014 decides the technical path through doctrinal judgment, architecture decisions, deep analysis, and trade-off adjudication without entering the implementation path.",
|
|
25881
25992
|
category: "strategy",
|
|
25882
25993
|
whenToUse: [
|
|
25883
25994
|
"architecture and design decisions",
|
|
@@ -25900,7 +26011,7 @@ var CARRIER_METADATA4 = {
|
|
|
25900
26011
|
// ── Tier 2: Composition ──
|
|
25901
26012
|
permissions: [
|
|
25902
26013
|
"CRITICAL: Strictly read-only. NEVER delegate code modification or file editing to this carrier.",
|
|
25903
|
-
"CRITICAL: NEVER
|
|
26014
|
+
"CRITICAL: NEVER dispatch Nimitz without prior reconnaissance \u2014 if recon is needed, dispatch vanguard/tempest FIRST. Hard prerequisite, not a suggestion.",
|
|
25904
26015
|
"Full access to read the codebase and execute read-only commands for analysis.",
|
|
25905
26016
|
"MUST NOT decompose work into task waves, delivery schedules, or implementation checklists \u2014 handoff belongs to Kirov."
|
|
25906
26017
|
],
|
|
@@ -25941,8 +26052,8 @@ var OHIO_DEFAULTS = {
|
|
|
25941
26052
|
};
|
|
25942
26053
|
var CARRIER_METADATA5 = {
|
|
25943
26054
|
// ── Tier 1: Routing ──
|
|
25944
|
-
title: "
|
|
25945
|
-
summary: "Receives a Kirov-authored plan_file and executes it wave-by-wave to completion
|
|
26055
|
+
title: "Multi-Wave Execution",
|
|
26056
|
+
summary: "Receives a Kirov-authored plan_file and executes it wave-by-wave to completion, or one manifest-declared lane when explicitly scoped. Ohio is the sole carrier authorised to consume plan_file inputs.",
|
|
25946
26057
|
category: "planning",
|
|
25947
26058
|
whenToUse: [
|
|
25948
26059
|
"multi-wave builds driven by an explicit plan_file",
|
|
@@ -25951,13 +26062,14 @@ var CARRIER_METADATA5 = {
|
|
|
25951
26062
|
"cross-module coordinated changes following a Kirov plan"
|
|
25952
26063
|
],
|
|
25953
26064
|
whenNotToUse: [
|
|
25954
|
-
"single-file edits or
|
|
26065
|
+
"single-file edits or host-directed single-shot tasks (\u2192genesis)",
|
|
25955
26066
|
"architecture decisions (\u2192nimitz)",
|
|
25956
26067
|
"planning work itself (\u2192kirov)",
|
|
25957
26068
|
"reconnaissance before planning (\u2192vanguard/tempest)"
|
|
25958
26069
|
],
|
|
25959
26070
|
requestBlocks: [
|
|
25960
26071
|
{ tag: "plan_file", hint: "Required repo-relative path to a Markdown plan file under .fleet/plans/*.md only. Ohio reads this file and follows it as the authoritative execution plan.", required: true },
|
|
26072
|
+
{ tag: "execution_scope", hint: "Optional: for legacy plans without Execution Topology or plans marked Execution mode: Sequential, omitted or `all` executes the full plan sequentially. For Execution mode: Parallel, provide one exact Wave/Lane ID declared by the Dispatch Manifest; omitted or `all` is rejected. Never combine a full-plan invocation with scoped-lane Ohio invocation(s).", required: false },
|
|
25961
26073
|
{ tag: "objective", hint: "Optional brief restatement of the overarching goal for context anchoring.", required: false },
|
|
25962
26074
|
{ tag: "scope", hint: "Optional explicit scope boundaries if narrower than the plan_file's full coverage.", required: false },
|
|
25963
26075
|
{ tag: "constraints", hint: "Optional hard constraints, deadlines, or compatibility requirements that override or supplement the plan.", required: false }
|
|
@@ -25967,7 +26079,9 @@ var CARRIER_METADATA5 = {
|
|
|
25967
26079
|
permissions: [
|
|
25968
26080
|
"Full access to the codebase \u2014 read, write, and execute commands.",
|
|
25969
26081
|
"MUST consult plan_file as the authoritative execution contract \u2014 plan steps are not optional or negotiable.",
|
|
25970
|
-
"MUST
|
|
26082
|
+
"MUST read the plan's Execution Topology before resolving execution_scope. For legacy plans without Execution Topology or plans marked Execution mode: Sequential, omitted scope or `all` executes the full plan sequentially. For Execution mode: Parallel, require one exact Dispatch Manifest Wave/Lane ID and reject omitted or `all` scope rather than silently serializing available parallelism. A full-plan invocation (omitted or `all`) MUST NEVER be used alongside scoped-lane Ohio invocation(s).",
|
|
26083
|
+
"When executing a scoped lane, it MUST be exactly one Wave/Lane ID declared by the plan's Dispatch Manifest. A scoped Ohio may change only that lane's declared write set; it MUST NOT edit plan_file, execute unassigned lanes, or guess an ambiguous scope. Before execution, it MUST satisfy the lane's dependency/start condition and required predecessor integration gates; its own QA/integration gate occurs after execution and MUST be satisfied before Ohio reports the lane eligible to release downstream work.",
|
|
26084
|
+
"MUST treat the host agent's <objective>, <scope>, and <constraints> as binding ALONGSIDE the plan_file. Even if a step or constraint seems suboptimal, MUST NOT substitute autonomous design judgment.",
|
|
25971
26085
|
"MUST NOT silently re-plan, skip steps, invent new workflow paths, or expand scope beyond what the plan_file specifies.",
|
|
25972
26086
|
"On genuine blockers (ambiguous step, missing dependency, environmental failure), MUST report back and request re-direction instead of fabricating workarounds."
|
|
25973
26087
|
],
|
|
@@ -25975,13 +26089,17 @@ var CARRIER_METADATA5 = {
|
|
|
25975
26089
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25976
26090
|
"Read plan_file as the binding execution contract \u2014 do not deviate, re-plan, or skip steps.",
|
|
25977
26091
|
"Accept only repo-relative Markdown plan paths under .fleet/plans/*.md. If the path is missing, unreadable, outside .fleet/plans/, not repo-relative, or not a .md file, do not guess, do not silently re-plan, and do not invent a replacement workflow \u2014 report the problem back and ask for re-direction.",
|
|
26092
|
+
"Read Execution Topology before resolving execution_scope. For legacy plans without Execution Topology or Execution mode: Sequential, omitted scope or `all` retains full-plan sequential compatibility. For Execution mode: Parallel, require one exact manifest-declared Wave/Lane ID and reject omitted or `all` scope rather than silently serializing available parallelism. Never use a full-plan invocation alongside scoped-lane Ohio invocation(s).",
|
|
26093
|
+
"For a lane scope, change only that lane's declared write set. Never edit plan_file or execute another lane. Before execution, satisfy the lane's dependency/start condition and required predecessor integration gates; after execution, satisfy that lane's own QA/integration gate before reporting it eligible to release downstream work.",
|
|
25978
26094
|
"Execute waves in the declared order; preserve QA checkpoints between waves and do not collapse them.",
|
|
25979
|
-
"
|
|
26095
|
+
"Progress write-back applies to full-plan scope only (execution_scope omitted or `all`): immediately after a wave's QA checkpoint passes, update the plan_file by flipping that wave's completed task checkboxes from '- [ ]' to '- [x]'. This is a state-marker edit only \u2014 never reword, reorder, add, or delete any other plan content while doing it. Flip only steps actually completed; if a completed step has no matching checkbox, leave the text untouched and record the mismatch under Deviations in the completion report. A scoped-lane Ohio never edits plan_file (lane jobs may run concurrently) \u2014 instead it lists the lane's completed task checkboxes in its completion report so the integrator flips them after the lane's integration gate.",
|
|
26096
|
+
"If a wave or constraint should be redesigned, MUST escalate to the host agent via the completion report instead of silently altering the wave's intent.",
|
|
25980
26097
|
"Escalate genuine blockers (ambiguous step, missing dependency, environmental failure) instead of fabricating workarounds.",
|
|
25981
26098
|
"Do not absorb planning, architecture, or QA roles \u2014 if the plan demands a decision Ohio cannot make, escalate to the appropriate carrier (Kirov/Nimitz/Sentinel)."
|
|
25982
26099
|
],
|
|
25983
26100
|
outputFormat: `After completing the assigned wave(s), provide a structured wave-completion report.
|
|
25984
26101
|
[Required] always include:
|
|
26102
|
+
**Execution scope** \u2014 \`all\` or the exact Wave/Lane ID executed from the plan's Dispatch Manifest.
|
|
25985
26103
|
**Wave(s) executed** \u2014 Ordered list of wave/step IDs from the plan_file actually completed.
|
|
25986
26104
|
**Changes** \u2014 Every file created/modified/deleted with a 1-line summary each.
|
|
25987
26105
|
**QA results** \u2014 Outcome of each wave's QA checkpoint (pass/fail with detail).
|
|
@@ -26007,8 +26125,8 @@ var SENTINEL_DEFAULTS = {
|
|
|
26007
26125
|
};
|
|
26008
26126
|
var CARRIER_METADATA6 = {
|
|
26009
26127
|
// ── Tier 1: Routing ──
|
|
26010
|
-
title: "
|
|
26011
|
-
summary: "Bug hunter and security specialist \u2014 code review, defect detection, quality audits, vulnerability hunting, and penetration testing with ruthless precision.
|
|
26128
|
+
title: "QA & Security Lead",
|
|
26129
|
+
summary: "Bug hunter and security specialist \u2014 code review, defect detection, quality audits, vulnerability hunting, and penetration testing with ruthless precision.",
|
|
26012
26130
|
category: "strategy",
|
|
26013
26131
|
whenToUse: [
|
|
26014
26132
|
"code review, bug hunting, quality audits, test execution",
|
|
@@ -26064,8 +26182,8 @@ var TEMPEST_DEFAULTS = {
|
|
|
26064
26182
|
};
|
|
26065
26183
|
var CARRIER_METADATA7 = {
|
|
26066
26184
|
// ── Tier 1: Routing ──
|
|
26067
|
-
title: "
|
|
26068
|
-
summary: "GitHub intelligence \u2014 external repo investigation via API and clone-based deep analysis
|
|
26185
|
+
title: "External Intelligence",
|
|
26186
|
+
summary: "GitHub intelligence \u2014 external repo investigation via API and clone-based deep analysis of upstream repositories and unfamiliar codebases.",
|
|
26069
26187
|
category: "operations",
|
|
26070
26188
|
whenToUse: [
|
|
26071
26189
|
"external GitHub repo investigation (library internals, upstream dependencies)",
|
|
@@ -26120,8 +26238,8 @@ var VANGUARD_DEFAULTS = {
|
|
|
26120
26238
|
};
|
|
26121
26239
|
var CARRIER_METADATA8 = {
|
|
26122
26240
|
// ── Tier 1: Routing ──
|
|
26123
|
-
title: "
|
|
26124
|
-
summary: "Fast reconnaissance \u2014 codebase exploration, symbol tracing, web research
|
|
26241
|
+
title: "Reconnaissance Specialist",
|
|
26242
|
+
summary: "Fast reconnaissance \u2014 codebase exploration, symbol tracing, web research, and external knowledge gathering.",
|
|
26125
26243
|
category: "operations",
|
|
26126
26244
|
whenToUse: [
|
|
26127
26245
|
"codebase reconnaissance \u2014 exploration, multi-file scanning, symbol tracing",
|
|
@@ -31940,7 +32058,7 @@ function resolveDispatchCwd(rawCwd, fallbackCwd) {
|
|
|
31940
32058
|
if (rawCwd === void 0) return { ok: true, cwd: fallbackCwd };
|
|
31941
32059
|
const trimmed = rawCwd.trim();
|
|
31942
32060
|
if (trimmed.length === 0) return { ok: true, cwd: fallbackCwd };
|
|
31943
|
-
if (!
|
|
32061
|
+
if (!path27__default.isAbsolute(trimmed)) {
|
|
31944
32062
|
return {
|
|
31945
32063
|
ok: false,
|
|
31946
32064
|
error: `Invalid cwd "${rawCwd}": must be an absolute path. Provide an absolute directory or omit cwd to use the host session cwd.`
|
|
@@ -32415,10 +32533,103 @@ function requireCarrierConfig(registry3, carrierId) {
|
|
|
32415
32533
|
if (!config2) throw new Error(`Carrier not found: ${carrierId}`);
|
|
32416
32534
|
return config2;
|
|
32417
32535
|
}
|
|
32418
|
-
|
|
32419
|
-
|
|
32536
|
+
var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
32537
|
+
var UI_FONT_SIZE_RANGE = { min: 12, max: 18};
|
|
32538
|
+
var DEFAULT_UI_FONT_SETTINGS = { source: "builtin", id: "manrope", size: 14 };
|
|
32539
|
+
var SETTINGS_VERSION = 1;
|
|
32540
|
+
var SETTINGS_LOCK_DIR_NAME = "settings.lock";
|
|
32541
|
+
var SETTINGS_LOCK_OWNER_FILE_NAME = "owner.json";
|
|
32542
|
+
var SETTINGS_TEMP_PREFIX = ".settings.";
|
|
32420
32543
|
var MIN_CONSOLE_STATIC_PORT = 1024;
|
|
32421
32544
|
var MAX_CONSOLE_STATIC_PORT = 65535;
|
|
32545
|
+
function createConsoleSettingsStore(deps = {}) {
|
|
32546
|
+
const paths = deps.paths ?? createConsoleDataPaths();
|
|
32547
|
+
const createStore = deps.createStore ?? createDurableJsonStore;
|
|
32548
|
+
return createStore({
|
|
32549
|
+
filePath: paths.settingsFile,
|
|
32550
|
+
lockDir: path27__default.join(paths.dir, SETTINGS_LOCK_DIR_NAME),
|
|
32551
|
+
lockOwnerFileName: SETTINGS_LOCK_OWNER_FILE_NAME,
|
|
32552
|
+
now: deps.now,
|
|
32553
|
+
sanitize: sanitizeConsoleSettingsData,
|
|
32554
|
+
sensitivity: "sensitive",
|
|
32555
|
+
tempCleanupPrefix: SETTINGS_TEMP_PREFIX
|
|
32556
|
+
});
|
|
32557
|
+
}
|
|
32558
|
+
function sanitizeConsoleSettingsData(value) {
|
|
32559
|
+
if (!isRecord4(value)) return emptyConsoleSettingsData();
|
|
32560
|
+
if (value.version !== SETTINGS_VERSION) return emptyConsoleSettingsData();
|
|
32561
|
+
const general = readConsoleGeneralSettings(value.general);
|
|
32562
|
+
const plugins = readConsolePluginSettings(value.plugins);
|
|
32563
|
+
return {
|
|
32564
|
+
version: SETTINGS_VERSION,
|
|
32565
|
+
general: general ?? {},
|
|
32566
|
+
plugins: plugins ?? {}
|
|
32567
|
+
};
|
|
32568
|
+
}
|
|
32569
|
+
function emptyConsoleSettingsData() {
|
|
32570
|
+
return { version: 1, general: {}, plugins: {} };
|
|
32571
|
+
}
|
|
32572
|
+
function sanitizeUiFontSettings(value) {
|
|
32573
|
+
if (isConsoleUiFontId(value)) return { source: "builtin", id: value, size: DEFAULT_UI_FONT_SETTINGS.size };
|
|
32574
|
+
if (!isRecord4(value) || !isValidUiFontSize(value.size)) return void 0;
|
|
32575
|
+
if (value.source === "builtin" && isConsoleUiFontId(value.id)) {
|
|
32576
|
+
return { source: "builtin", id: value.id, size: value.size };
|
|
32577
|
+
}
|
|
32578
|
+
if (value.source === "system" && typeof value.familyName === "string") {
|
|
32579
|
+
const familyName = sanitizeSystemFontFamily(value.familyName);
|
|
32580
|
+
return familyName ? { source: "system", familyName, size: value.size } : void 0;
|
|
32581
|
+
}
|
|
32582
|
+
return void 0;
|
|
32583
|
+
}
|
|
32584
|
+
function isUiFontSettings(value) {
|
|
32585
|
+
if (!isRecord4(value) || !isValidUiFontSize(value.size)) return false;
|
|
32586
|
+
if (value.source === "builtin") return isConsoleUiFontId(value.id);
|
|
32587
|
+
return value.source === "system" && typeof value.familyName === "string" && value.familyName.length > 0 && value.familyName === sanitizeSystemFontFamily(value.familyName);
|
|
32588
|
+
}
|
|
32589
|
+
function readConsoleGeneralSettings(value) {
|
|
32590
|
+
if (!isRecord4(value)) return null;
|
|
32591
|
+
const consolePortMode = value.consolePortMode === "dynamic" || value.consolePortMode === "static" ? value.consolePortMode : void 0;
|
|
32592
|
+
const consoleStaticPort = isValidConsoleStaticPort(value.consoleStaticPort) ? value.consoleStaticPort : void 0;
|
|
32593
|
+
const language = value.language === "auto" || value.language === "en" || value.language === "ko" ? value.language : void 0;
|
|
32594
|
+
const theme = value.theme === "instrument" || value.theme === "maritime" || value.theme === "carbon" ? value.theme : void 0;
|
|
32595
|
+
const uiFont = sanitizeUiFontSettings(value.uiFont);
|
|
32596
|
+
return {
|
|
32597
|
+
...consolePortMode !== void 0 ? { consolePortMode } : {},
|
|
32598
|
+
...consoleStaticPort !== void 0 ? { consoleStaticPort } : {},
|
|
32599
|
+
...language !== void 0 ? { language } : {},
|
|
32600
|
+
...theme !== void 0 ? { theme } : {},
|
|
32601
|
+
...uiFont !== void 0 ? { uiFont } : {}
|
|
32602
|
+
};
|
|
32603
|
+
}
|
|
32604
|
+
function readConsolePluginSettings(value) {
|
|
32605
|
+
if (!isRecord4(value)) return null;
|
|
32606
|
+
const result = {};
|
|
32607
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
32608
|
+
if (!PLUGIN_ID_PATTERN.test(key)) continue;
|
|
32609
|
+
if (!isRecord4(entry)) continue;
|
|
32610
|
+
result[key] = entry;
|
|
32611
|
+
}
|
|
32612
|
+
return result;
|
|
32613
|
+
}
|
|
32614
|
+
function isRecord4(value) {
|
|
32615
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32616
|
+
}
|
|
32617
|
+
function isValidConsoleStaticPort(value) {
|
|
32618
|
+
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT && value <= MAX_CONSOLE_STATIC_PORT;
|
|
32619
|
+
}
|
|
32620
|
+
function isConsoleUiFontId(value) {
|
|
32621
|
+
return value === "manrope" || value === "jetbrains-mono" || value === "source-code-pro";
|
|
32622
|
+
}
|
|
32623
|
+
function isValidUiFontSize(value) {
|
|
32624
|
+
return typeof value === "number" && Number.isInteger(value) && value >= UI_FONT_SIZE_RANGE.min && value <= UI_FONT_SIZE_RANGE.max;
|
|
32625
|
+
}
|
|
32626
|
+
function sanitizeSystemFontFamily(value) {
|
|
32627
|
+
return value.replace(/[\x00-\x1F\x7F]/g, "").trim().slice(0, 128);
|
|
32628
|
+
}
|
|
32629
|
+
|
|
32630
|
+
// core/host/global-settings-routes.ts
|
|
32631
|
+
var MIN_CONSOLE_STATIC_PORT2 = 1024;
|
|
32632
|
+
var MAX_CONSOLE_STATIC_PORT2 = 65535;
|
|
32422
32633
|
var GLOBAL_SETTINGS_API_CATALOG = [
|
|
32423
32634
|
{
|
|
32424
32635
|
method: "GET",
|
|
@@ -32474,22 +32685,34 @@ async function mutateGlobalSettings(req, res, deps) {
|
|
|
32474
32685
|
deps.writeJson(res, 400, { error: "invalid_console_port_mode" });
|
|
32475
32686
|
return;
|
|
32476
32687
|
}
|
|
32477
|
-
if (body.consoleStaticPort !== void 0 && body.consoleStaticPort !== null && !
|
|
32688
|
+
if (body.consoleStaticPort !== void 0 && body.consoleStaticPort !== null && !isValidConsoleStaticPort2(body.consoleStaticPort)) {
|
|
32478
32689
|
deps.writeJson(res, 400, { error: "invalid_console_static_port" });
|
|
32479
32690
|
return;
|
|
32480
32691
|
}
|
|
32481
|
-
if (body.
|
|
32692
|
+
if (body.language !== void 0 && body.language !== "auto" && body.language !== "en" && body.language !== "ko") {
|
|
32693
|
+
deps.writeJson(res, 400, { error: "invalid_language" });
|
|
32694
|
+
return;
|
|
32695
|
+
}
|
|
32696
|
+
if (body.theme !== void 0 && body.theme !== "instrument" && body.theme !== "maritime" && body.theme !== "carbon") {
|
|
32482
32697
|
deps.writeJson(res, 400, { error: "invalid_theme" });
|
|
32483
32698
|
return;
|
|
32484
32699
|
}
|
|
32700
|
+
if (!isUiFontSettingsOrUndefined(body.uiFont)) {
|
|
32701
|
+
deps.writeJson(res, 400, { error: "invalid_ui_font" });
|
|
32702
|
+
return;
|
|
32703
|
+
}
|
|
32485
32704
|
const updated = deps.consoleSettingsStore.update((current) => ({
|
|
32705
|
+
...current,
|
|
32486
32706
|
version: 1,
|
|
32487
32707
|
general: {
|
|
32488
32708
|
...current.general,
|
|
32489
32709
|
...body.consolePortMode === "dynamic" || body.consolePortMode === "static" ? { consolePortMode: body.consolePortMode } : {},
|
|
32490
|
-
...
|
|
32491
|
-
...body.
|
|
32492
|
-
|
|
32710
|
+
...isValidConsoleStaticPort2(body.consoleStaticPort) ? { consoleStaticPort: body.consoleStaticPort } : {},
|
|
32711
|
+
...body.language === "auto" || body.language === "en" || body.language === "ko" ? { language: body.language } : {},
|
|
32712
|
+
...body.theme === "instrument" || body.theme === "maritime" || body.theme === "carbon" ? { theme: body.theme } : {},
|
|
32713
|
+
...isUiFontSettings(body.uiFont) ? { uiFont: body.uiFont } : {}
|
|
32714
|
+
},
|
|
32715
|
+
plugins: current.plugins
|
|
32493
32716
|
}));
|
|
32494
32717
|
const response = { state: toGlobalSettingsState(updated) };
|
|
32495
32718
|
deps.writeJson(res, 200, response);
|
|
@@ -32499,77 +32722,21 @@ function toGlobalSettingsState(data) {
|
|
|
32499
32722
|
return {
|
|
32500
32723
|
consolePortMode: general.consolePortMode ?? "dynamic",
|
|
32501
32724
|
consoleStaticPort: general.consoleStaticPort ?? null,
|
|
32502
|
-
|
|
32725
|
+
language: general.language ?? "auto",
|
|
32726
|
+
theme: general.theme ?? "instrument",
|
|
32727
|
+
uiFont: general.uiFont ?? DEFAULT_UI_FONT_SETTINGS
|
|
32503
32728
|
};
|
|
32504
32729
|
}
|
|
32505
32730
|
function isJsonRequest2(req) {
|
|
32506
32731
|
const contentType = req.headers["content-type"];
|
|
32507
32732
|
return typeof contentType === "string" && contentType.toLowerCase().split(";")[0]?.trim() === "application/json";
|
|
32508
32733
|
}
|
|
32509
|
-
function isValidConsoleStaticPort(value) {
|
|
32510
|
-
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT && value <= MAX_CONSOLE_STATIC_PORT;
|
|
32511
|
-
}
|
|
32512
|
-
var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
32513
|
-
var SETTINGS_VERSION = 1;
|
|
32514
|
-
var SETTINGS_LOCK_DIR_NAME = "settings.lock";
|
|
32515
|
-
var SETTINGS_LOCK_OWNER_FILE_NAME = "owner.json";
|
|
32516
|
-
var SETTINGS_TEMP_PREFIX = ".settings.";
|
|
32517
|
-
var MIN_CONSOLE_STATIC_PORT2 = 1024;
|
|
32518
|
-
var MAX_CONSOLE_STATIC_PORT2 = 65535;
|
|
32519
|
-
function createConsoleSettingsStore(deps = {}) {
|
|
32520
|
-
const paths = deps.paths ?? createConsoleDataPaths();
|
|
32521
|
-
const createStore = deps.createStore ?? createDurableJsonStore;
|
|
32522
|
-
return createStore({
|
|
32523
|
-
filePath: paths.settingsFile,
|
|
32524
|
-
lockDir: path25__default.join(paths.dir, SETTINGS_LOCK_DIR_NAME),
|
|
32525
|
-
lockOwnerFileName: SETTINGS_LOCK_OWNER_FILE_NAME,
|
|
32526
|
-
now: deps.now,
|
|
32527
|
-
sanitize: sanitizeConsoleSettingsData,
|
|
32528
|
-
sensitivity: "sensitive",
|
|
32529
|
-
tempCleanupPrefix: SETTINGS_TEMP_PREFIX
|
|
32530
|
-
});
|
|
32531
|
-
}
|
|
32532
|
-
function sanitizeConsoleSettingsData(value) {
|
|
32533
|
-
if (!isRecord4(value)) return emptyConsoleSettingsData();
|
|
32534
|
-
if (value.version !== SETTINGS_VERSION) return emptyConsoleSettingsData();
|
|
32535
|
-
const general = readConsoleGeneralSettings(value.general);
|
|
32536
|
-
const plugins = readConsolePluginSettings(value.plugins);
|
|
32537
|
-
return {
|
|
32538
|
-
version: SETTINGS_VERSION,
|
|
32539
|
-
general: general ?? {},
|
|
32540
|
-
plugins: plugins ?? {}
|
|
32541
|
-
};
|
|
32542
|
-
}
|
|
32543
|
-
function emptyConsoleSettingsData() {
|
|
32544
|
-
return { version: 1, general: {}, plugins: {} };
|
|
32545
|
-
}
|
|
32546
|
-
function readConsoleGeneralSettings(value) {
|
|
32547
|
-
if (!isRecord4(value)) return null;
|
|
32548
|
-
const consolePortMode = value.consolePortMode === "dynamic" || value.consolePortMode === "static" ? value.consolePortMode : void 0;
|
|
32549
|
-
const consoleStaticPort = isValidConsoleStaticPort2(value.consoleStaticPort) ? value.consoleStaticPort : void 0;
|
|
32550
|
-
const theme = value.theme === "maritime" || value.theme === "carbon" ? value.theme : void 0;
|
|
32551
|
-
return {
|
|
32552
|
-
...consolePortMode !== void 0 ? { consolePortMode } : {},
|
|
32553
|
-
...consoleStaticPort !== void 0 ? { consoleStaticPort } : {},
|
|
32554
|
-
...theme !== void 0 ? { theme } : {}
|
|
32555
|
-
};
|
|
32556
|
-
}
|
|
32557
|
-
function readConsolePluginSettings(value) {
|
|
32558
|
-
if (!isRecord4(value)) return null;
|
|
32559
|
-
const result = {};
|
|
32560
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
32561
|
-
if (!PLUGIN_ID_PATTERN.test(key)) continue;
|
|
32562
|
-
if (!isRecord4(entry)) continue;
|
|
32563
|
-
result[key] = entry;
|
|
32564
|
-
}
|
|
32565
|
-
return result;
|
|
32566
|
-
}
|
|
32567
|
-
function isRecord4(value) {
|
|
32568
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32569
|
-
}
|
|
32570
32734
|
function isValidConsoleStaticPort2(value) {
|
|
32571
32735
|
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT2 && value <= MAX_CONSOLE_STATIC_PORT2;
|
|
32572
32736
|
}
|
|
32737
|
+
function isUiFontSettingsOrUndefined(value) {
|
|
32738
|
+
return value === void 0 || isUiFontSettings(value);
|
|
32739
|
+
}
|
|
32573
32740
|
|
|
32574
32741
|
// core/host/plugin-settings-routes.ts
|
|
32575
32742
|
var PLUGIN_SETTINGS_PREFIX = "/api/v1/settings/plugins/";
|
|
@@ -32650,6 +32817,133 @@ function isJsonRequest3(req) {
|
|
|
32650
32817
|
const contentType = req.headers["content-type"];
|
|
32651
32818
|
return typeof contentType === "string" && contentType.toLowerCase().split(";")[0]?.trim() === "application/json";
|
|
32652
32819
|
}
|
|
32820
|
+
var MAX_FAMILY_LENGTH = 128;
|
|
32821
|
+
var SUCCESS_TTL_MS = 5 * 60 * 1e3;
|
|
32822
|
+
var FAILURE_TTL_MS = 30 * 1e3;
|
|
32823
|
+
var CONTROL_CHARACTER_PATTERN = /[\x00-\x1F\x7F]/g;
|
|
32824
|
+
var NORMAL_FACE_MARKERS = ["normal", "regular", "roman", "book"];
|
|
32825
|
+
var TEXT_FAMILY_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
32826
|
+
"arial",
|
|
32827
|
+
"arial nova",
|
|
32828
|
+
"avenir",
|
|
32829
|
+
"avenir next",
|
|
32830
|
+
"calibri",
|
|
32831
|
+
"candara",
|
|
32832
|
+
"helvetica",
|
|
32833
|
+
"helvetica neue",
|
|
32834
|
+
"inter",
|
|
32835
|
+
"manrope",
|
|
32836
|
+
"noto sans",
|
|
32837
|
+
"noto serif",
|
|
32838
|
+
"segoe ui",
|
|
32839
|
+
"sf pro text",
|
|
32840
|
+
"system ui",
|
|
32841
|
+
"times new roman",
|
|
32842
|
+
"verdana",
|
|
32843
|
+
"apple sd gothic neo",
|
|
32844
|
+
"hiragino sans",
|
|
32845
|
+
"hiragino kaku gothic pro",
|
|
32846
|
+
"malgun gothic",
|
|
32847
|
+
"meiryo",
|
|
32848
|
+
"microsoft yahei",
|
|
32849
|
+
"noto sans cjk",
|
|
32850
|
+
"noto serif cjk",
|
|
32851
|
+
"pingfang sc",
|
|
32852
|
+
"pingfang tc",
|
|
32853
|
+
"yu gothic"
|
|
32854
|
+
]);
|
|
32855
|
+
var TEXT_FAMILY_MARKERS = ["sans", "serif", "text", "grotesk", "gothic", "roman", "book", "humanist"];
|
|
32856
|
+
var DENY_FAMILY_MARKERS = ["hidden", "vertical", "symbol", "icon", "emoji", "dingbat", "ornament", "music", "math", "display", "decorative"];
|
|
32857
|
+
var EMPTY_SYSTEM_FONTS_ERROR = new Error("system font enumeration returned no usable families");
|
|
32858
|
+
function createSystemFontsService(deps = {}) {
|
|
32859
|
+
const loadFonts = deps.loadFonts ?? (() => fontList.getFonts2({ disableQuoting: true }));
|
|
32860
|
+
const now = deps.now ?? Date.now;
|
|
32861
|
+
const successTtlMs = deps.successTtlMs ?? SUCCESS_TTL_MS;
|
|
32862
|
+
const failureTtlMs = deps.failureTtlMs ?? FAILURE_TTL_MS;
|
|
32863
|
+
let cached2 = null;
|
|
32864
|
+
let cachedFailure = null;
|
|
32865
|
+
let inFlight = null;
|
|
32866
|
+
const getFonts = () => {
|
|
32867
|
+
if (cached2 && now() - cached2.cachedAt < successTtlMs) return Promise.resolve(cached2.fonts);
|
|
32868
|
+
if (cachedFailure && now() - cachedFailure.cachedAt < failureTtlMs) return Promise.reject(cachedFailure.error);
|
|
32869
|
+
if (inFlight) return inFlight;
|
|
32870
|
+
inFlight = loadFonts().then((fonts) => {
|
|
32871
|
+
const normalized = normalizeSystemFonts(fonts);
|
|
32872
|
+
if (normalized.length === 0) throw EMPTY_SYSTEM_FONTS_ERROR;
|
|
32873
|
+
cached2 = { fonts: normalized, cachedAt: now() };
|
|
32874
|
+
cachedFailure = null;
|
|
32875
|
+
return normalized;
|
|
32876
|
+
}).catch((error51) => {
|
|
32877
|
+
cachedFailure = { error: error51, cachedAt: now() };
|
|
32878
|
+
throw error51;
|
|
32879
|
+
}).finally(() => {
|
|
32880
|
+
inFlight = null;
|
|
32881
|
+
});
|
|
32882
|
+
return inFlight;
|
|
32883
|
+
};
|
|
32884
|
+
return { getFonts };
|
|
32885
|
+
}
|
|
32886
|
+
function normalizeSystemFonts(fonts) {
|
|
32887
|
+
const groups = /* @__PURE__ */ new Map();
|
|
32888
|
+
for (const font of fonts) {
|
|
32889
|
+
if (!isFontInfo(font)) continue;
|
|
32890
|
+
const family = sanitizeFamilyName(font.familyName);
|
|
32891
|
+
if (!family) continue;
|
|
32892
|
+
const key = family.toLocaleLowerCase();
|
|
32893
|
+
const group = groups.get(key) ?? { family, faces: [] };
|
|
32894
|
+
group.faces.push(font);
|
|
32895
|
+
groups.set(key, group);
|
|
32896
|
+
}
|
|
32897
|
+
return [...groups.values()].map(toSystemFontRecord).sort((left, right) => left.family.localeCompare(right.family, void 0, { sensitivity: "base" }) || left.family.localeCompare(right.family));
|
|
32898
|
+
}
|
|
32899
|
+
function buildSystemFontsResponse(fonts) {
|
|
32900
|
+
return { version: 1, fonts };
|
|
32901
|
+
}
|
|
32902
|
+
function isFontInfo(value) {
|
|
32903
|
+
return typeof value === "object" && value !== null && typeof value.familyName === "string" && typeof value.monospace === "boolean" && typeof value.style === "string";
|
|
32904
|
+
}
|
|
32905
|
+
function sanitizeFamilyName(value) {
|
|
32906
|
+
return value.replace(CONTROL_CHARACTER_PATTERN, "").trim().slice(0, MAX_FAMILY_LENGTH);
|
|
32907
|
+
}
|
|
32908
|
+
function toSystemFontRecord(group) {
|
|
32909
|
+
const normalizedFamily = group.family.toLocaleLowerCase();
|
|
32910
|
+
const monospace = group.faces.length > 0 && group.faces.every((face) => face.monospace);
|
|
32911
|
+
const hasNormalNonMonospaceFace = group.faces.some((face) => !face.monospace && isNormalFace(face));
|
|
32912
|
+
const denied = DENY_FAMILY_MARKERS.some((marker) => normalizedFamily.includes(marker));
|
|
32913
|
+
const textFamily = TEXT_FAMILY_ALLOWLIST.has(normalizedFamily) || TEXT_FAMILY_MARKERS.some((marker) => normalizedFamily.includes(marker));
|
|
32914
|
+
return { family: group.family, monospace, uiSuitable: !denied && hasNormalNonMonospaceFace && textFamily };
|
|
32915
|
+
}
|
|
32916
|
+
function isNormalFace(face) {
|
|
32917
|
+
const style = face.style.toLocaleLowerCase();
|
|
32918
|
+
return NORMAL_FACE_MARKERS.some((marker) => style.includes(marker));
|
|
32919
|
+
}
|
|
32920
|
+
|
|
32921
|
+
// core/host/system-fonts-routes.ts
|
|
32922
|
+
var SYSTEM_FONTS_PATH = "/api/v1/settings/fonts/system";
|
|
32923
|
+
var SYSTEM_FONTS_API_CATALOG = [
|
|
32924
|
+
{
|
|
32925
|
+
method: "GET",
|
|
32926
|
+
path: SYSTEM_FONTS_PATH,
|
|
32927
|
+
summary: "List sanitized system font families for built-in settings.",
|
|
32928
|
+
category: "Settings",
|
|
32929
|
+
gate: "loopback"
|
|
32930
|
+
}
|
|
32931
|
+
];
|
|
32932
|
+
function createSystemFontsRouter(deps) {
|
|
32933
|
+
return async function handleSystemFontsRoute(context) {
|
|
32934
|
+
if (context.pathname !== SYSTEM_FONTS_PATH) return false;
|
|
32935
|
+
if (context.req.method !== "GET") {
|
|
32936
|
+
deps.writeJson(context.res, 405, { error: "Method not allowed" });
|
|
32937
|
+
return true;
|
|
32938
|
+
}
|
|
32939
|
+
try {
|
|
32940
|
+
deps.writeJson(context.res, 200, buildSystemFontsResponse(await deps.systemFonts.getFonts()));
|
|
32941
|
+
} catch {
|
|
32942
|
+
deps.writeJson(context.res, 503, { error: "system_fonts_unavailable" });
|
|
32943
|
+
}
|
|
32944
|
+
return true;
|
|
32945
|
+
};
|
|
32946
|
+
}
|
|
32653
32947
|
|
|
32654
32948
|
// core/host/api-catalog.ts
|
|
32655
32949
|
var compareApiCatalogEntries = (left, right) => left.category.localeCompare(right.category) || left.path.localeCompare(right.path) || left.method.localeCompare(right.method);
|
|
@@ -32658,9 +32952,115 @@ function buildApiCatalog() {
|
|
|
32658
32952
|
...SERVER_API_CATALOG,
|
|
32659
32953
|
...CARRIER_SETTINGS_API_CATALOG,
|
|
32660
32954
|
...GLOBAL_SETTINGS_API_CATALOG,
|
|
32661
|
-
...PLUGIN_SETTINGS_API_CATALOG
|
|
32955
|
+
...PLUGIN_SETTINGS_API_CATALOG,
|
|
32956
|
+
...SYSTEM_FONTS_API_CATALOG
|
|
32662
32957
|
].slice().sort(compareApiCatalogEntries);
|
|
32663
32958
|
}
|
|
32959
|
+
var TheaterPathContextError = class extends Error {
|
|
32960
|
+
code;
|
|
32961
|
+
constructor(code) {
|
|
32962
|
+
super(code);
|
|
32963
|
+
this.code = code;
|
|
32964
|
+
}
|
|
32965
|
+
};
|
|
32966
|
+
var DIRECTORY_ENTRY_CAP = 500;
|
|
32967
|
+
async function resolveTheaterPathContext(theaterRoot, relPath) {
|
|
32968
|
+
if (relPath !== null && !isSafeRelativePath(relPath)) throw new TheaterPathContextError("invalid_path");
|
|
32969
|
+
const nominalRoot = path27__default.resolve(theaterRoot);
|
|
32970
|
+
const nominalTarget = relPath === null ? nominalRoot : path27__default.resolve(nominalRoot, ...relPath.split("/"));
|
|
32971
|
+
if (!isWithinRoot(nominalTarget, nominalRoot)) throw new TheaterPathContextError("forbidden");
|
|
32972
|
+
let realRoot;
|
|
32973
|
+
let realPath;
|
|
32974
|
+
try {
|
|
32975
|
+
[realRoot, realPath] = await Promise.all([fs14__default.promises.realpath(nominalRoot), fs14__default.promises.realpath(nominalTarget)]);
|
|
32976
|
+
} catch (error51) {
|
|
32977
|
+
throw mapFsError(error51);
|
|
32978
|
+
}
|
|
32979
|
+
if (!isWithinRoot(realPath, realRoot)) throw new TheaterPathContextError("forbidden");
|
|
32980
|
+
let stat5;
|
|
32981
|
+
try {
|
|
32982
|
+
stat5 = await fs14__default.promises.stat(realPath);
|
|
32983
|
+
} catch (error51) {
|
|
32984
|
+
throw mapFsError(error51);
|
|
32985
|
+
}
|
|
32986
|
+
if (!stat5.isDirectory()) throw new TheaterPathContextError("invalid_path");
|
|
32987
|
+
const canonicalRel = path27__default.relative(realRoot, realPath).split(path27__default.sep).join("/");
|
|
32988
|
+
return { realRoot, realPath, relPath: canonicalRel === "" ? null : canonicalRel, label: path27__default.basename(realPath) || path27__default.basename(realRoot) || "Theater" };
|
|
32989
|
+
}
|
|
32990
|
+
async function listTheaterDirectories(theaterRoot, parentRelPath) {
|
|
32991
|
+
const parent = await resolveTheaterPathContext(theaterRoot, parentRelPath);
|
|
32992
|
+
let entries;
|
|
32993
|
+
try {
|
|
32994
|
+
entries = await fs14__default.promises.readdir(parent.realPath, { withFileTypes: true });
|
|
32995
|
+
} catch (error51) {
|
|
32996
|
+
throw mapFsError(error51);
|
|
32997
|
+
}
|
|
32998
|
+
const result = [];
|
|
32999
|
+
for (const entry of entries) {
|
|
33000
|
+
if (result.length >= DIRECTORY_ENTRY_CAP) break;
|
|
33001
|
+
const candidate = path27__default.join(parent.realPath, entry.name);
|
|
33002
|
+
try {
|
|
33003
|
+
const realCandidate = await fs14__default.promises.realpath(candidate);
|
|
33004
|
+
if (!isWithinRoot(realCandidate, parent.realRoot)) continue;
|
|
33005
|
+
const stat5 = await fs14__default.promises.stat(realCandidate);
|
|
33006
|
+
if (!stat5.isDirectory()) continue;
|
|
33007
|
+
const relPath = path27__default.relative(parent.realRoot, realCandidate).split(path27__default.sep).join("/");
|
|
33008
|
+
if (relPath) result.push({ relPath, label: path27__default.basename(realCandidate) });
|
|
33009
|
+
} catch {
|
|
33010
|
+
}
|
|
33011
|
+
}
|
|
33012
|
+
return result.sort((left, right) => left.label.localeCompare(right.label));
|
|
33013
|
+
}
|
|
33014
|
+
function isSafeRelativePath(value) {
|
|
33015
|
+
return value.length > 0 && value.length <= 4096 && !value.includes("\0") && !value.includes("\\") && !value.startsWith("/") && !/^[A-Za-z]:/.test(value) && !value.split("/").some((segment) => segment === "" || segment === "." || segment === "..");
|
|
33016
|
+
}
|
|
33017
|
+
function isWithinRoot(candidate, root) {
|
|
33018
|
+
const normalizedRoot = root.endsWith(path27__default.sep) ? root : `${root}${path27__default.sep}`;
|
|
33019
|
+
return candidate === root || candidate.startsWith(normalizedRoot);
|
|
33020
|
+
}
|
|
33021
|
+
function mapFsError(error51) {
|
|
33022
|
+
return error51.code === "ENOENT" ? new TheaterPathContextError("not_found") : new TheaterPathContextError("invalid_path");
|
|
33023
|
+
}
|
|
33024
|
+
|
|
33025
|
+
// core/host/codex/context-routes.ts
|
|
33026
|
+
function createCodexWorkspaceContextRouter(deps) {
|
|
33027
|
+
return async function handleCodexWorkspaceContextRoute({ req, res, pathname }) {
|
|
33028
|
+
const match = pathname.match(/^\/api\/v1\/theaters\/([^/]+)\/codex-workspace$/u);
|
|
33029
|
+
if (!match) return false;
|
|
33030
|
+
const theater = deps.getTheater(decodeURIComponent(match[1] ?? ""));
|
|
33031
|
+
if (!theater) {
|
|
33032
|
+
deps.writeJson(res, 400, { error: "invalid_theater" });
|
|
33033
|
+
return true;
|
|
33034
|
+
}
|
|
33035
|
+
if (req.method !== "POST") {
|
|
33036
|
+
deps.writeJson(res, 405, { error: "method_not_allowed" });
|
|
33037
|
+
return true;
|
|
33038
|
+
}
|
|
33039
|
+
if (!deps.isAuthorized(req)) {
|
|
33040
|
+
deps.writeJson(res, 401, { error: "unauthorized" });
|
|
33041
|
+
return true;
|
|
33042
|
+
}
|
|
33043
|
+
const body = await deps.readJsonBody(req);
|
|
33044
|
+
if (!isResolveWorkspaceBody(body)) {
|
|
33045
|
+
deps.writeJson(res, 400, { error: "invalid_request" });
|
|
33046
|
+
return true;
|
|
33047
|
+
}
|
|
33048
|
+
try {
|
|
33049
|
+
deps.writeJson(res, 200, await deps.resolveWorkspace(theater.id, theater.realpath, body.relPath));
|
|
33050
|
+
} catch (error51) {
|
|
33051
|
+
writePathError(res, deps, error51);
|
|
33052
|
+
}
|
|
33053
|
+
return true;
|
|
33054
|
+
};
|
|
33055
|
+
}
|
|
33056
|
+
function isResolveWorkspaceBody(value) {
|
|
33057
|
+
return typeof value === "object" && value !== null && Object.keys(value).length === 1 && (value.relPath === null || typeof value.relPath === "string");
|
|
33058
|
+
}
|
|
33059
|
+
function writePathError(res, deps, error51) {
|
|
33060
|
+
if (!(error51 instanceof TheaterPathContextError)) throw error51;
|
|
33061
|
+
const status = error51.code === "not_found" ? 404 : error51.code === "forbidden" ? 403 : 400;
|
|
33062
|
+
deps.writeJson(res, status, { error: error51.code });
|
|
33063
|
+
}
|
|
32664
33064
|
|
|
32665
33065
|
// ../../packages/fleet-wiki/src/boundaries.ts
|
|
32666
33066
|
var FLEET_WIKI_ENTRY_BEGIN = "<<<FLEET_WIKI_ENTRY_BEGIN";
|
|
@@ -33068,18 +33468,18 @@ Always read \`schema/wiki-schema.md\` directly before composing or revising entr
|
|
|
33068
33468
|
`;
|
|
33069
33469
|
async function ensureWorkspaceSchema(paths) {
|
|
33070
33470
|
await mkdir(paths.schemaDir, { recursive: true });
|
|
33071
|
-
await writeDefaultFileIfMissing(
|
|
33072
|
-
await writeDefaultFileIfMissing(
|
|
33471
|
+
await writeDefaultFileIfMissing(path27__default.join(paths.schemaDir, WORKSPACE_SCHEMA_AGENTS_FILENAME), DEFAULT_WORKSPACE_SCHEMA_AGENTS);
|
|
33472
|
+
await writeDefaultFileIfMissing(path27__default.join(paths.schemaDir, WORKSPACE_SCHEMA_FILENAME), DEFAULT_WORKSPACE_WIKI_SCHEMA);
|
|
33073
33473
|
await writeDefaultFileIfMissing(buildTemplatePath(paths, "prd"), DEFAULT_TEMPLATE_PRD);
|
|
33074
33474
|
return readWorkspaceSchemaSummary(paths);
|
|
33075
33475
|
}
|
|
33076
33476
|
async function ensureWorkspaceDoctrine(paths) {
|
|
33077
33477
|
await mkdir(paths.root, { recursive: true });
|
|
33078
|
-
await writeDefaultFileIfMissing(
|
|
33478
|
+
await writeDefaultFileIfMissing(path27__default.join(paths.root, WORKSPACE_KNOWLEDGE_AGENTS_FILENAME), DEFAULT_WORKSPACE_KNOWLEDGE_AGENTS);
|
|
33079
33479
|
}
|
|
33080
33480
|
async function readWorkspaceSchemaSummary(paths) {
|
|
33081
|
-
const agentsPath =
|
|
33082
|
-
const wikiSchemaPath =
|
|
33481
|
+
const agentsPath = path27__default.join(paths.schemaDir, WORKSPACE_SCHEMA_AGENTS_FILENAME);
|
|
33482
|
+
const wikiSchemaPath = path27__default.join(paths.schemaDir, WORKSPACE_SCHEMA_FILENAME);
|
|
33083
33483
|
const wikiSchemaContent = await tryReadFile(wikiSchemaPath);
|
|
33084
33484
|
const templates = await scanTemplates(paths);
|
|
33085
33485
|
if (wikiSchemaContent === null) {
|
|
@@ -33118,7 +33518,7 @@ async function scanTemplates(paths) {
|
|
|
33118
33518
|
if (!entry.name.startsWith(WORKSPACE_TEMPLATE_PREFIX) || !entry.name.endsWith(WORKSPACE_TEMPLATE_SUFFIX)) continue;
|
|
33119
33519
|
const id = entry.name.slice(WORKSPACE_TEMPLATE_PREFIX.length, -WORKSPACE_TEMPLATE_SUFFIX.length);
|
|
33120
33520
|
if (!id) continue;
|
|
33121
|
-
const filePath =
|
|
33521
|
+
const filePath = path27__default.join(paths.schemaDir, entry.name);
|
|
33122
33522
|
try {
|
|
33123
33523
|
const parsed = parseTemplateMarkdown(await readFile(filePath, "utf8"));
|
|
33124
33524
|
templates.push({
|
|
@@ -33152,7 +33552,7 @@ async function validateTemplateCompliance(paths, templateId, body) {
|
|
|
33152
33552
|
}
|
|
33153
33553
|
}
|
|
33154
33554
|
function inferTemplateIdFromTarget(target, knownTemplateIds) {
|
|
33155
|
-
const basename =
|
|
33555
|
+
const basename = path27__default.basename(target, ".md");
|
|
33156
33556
|
const ids = knownTemplateIds ?? DEFAULT_TEMPLATE_IDS;
|
|
33157
33557
|
const sorted = [...ids].sort((a, b) => b.length - a.length);
|
|
33158
33558
|
for (const id of sorted) {
|
|
@@ -33181,7 +33581,7 @@ function extractSchemaSummary(content) {
|
|
|
33181
33581
|
return lines.find((line) => line.length > 0 && !line.startsWith("#")) ?? "Workspace schema is present.";
|
|
33182
33582
|
}
|
|
33183
33583
|
function buildTemplatePath(paths, id) {
|
|
33184
|
-
return
|
|
33584
|
+
return path27__default.join(paths.schemaDir, `${WORKSPACE_TEMPLATE_PREFIX}${id}${WORKSPACE_TEMPLATE_SUFFIX}`);
|
|
33185
33585
|
}
|
|
33186
33586
|
function parseTemplateMarkdown(content) {
|
|
33187
33587
|
const normalized = content.replace(/\r\n/g, "\n");
|
|
@@ -33208,16 +33608,16 @@ function isAlreadyExistsError(error51) {
|
|
|
33208
33608
|
|
|
33209
33609
|
// ../../packages/fleet-wiki/src/paths.ts
|
|
33210
33610
|
function resolveMemoryPaths(cwd) {
|
|
33211
|
-
const root =
|
|
33611
|
+
const root = path27__default.join(cwd, KNOWLEDGE_ROOT_DIRNAME);
|
|
33212
33612
|
return {
|
|
33213
33613
|
root,
|
|
33214
|
-
rawDir:
|
|
33215
|
-
wikiDir:
|
|
33216
|
-
schemaDir:
|
|
33217
|
-
queueDir:
|
|
33218
|
-
archiveDir:
|
|
33219
|
-
conflictsDir:
|
|
33220
|
-
indexFile:
|
|
33614
|
+
rawDir: path27__default.join(root, RAW_DIRNAME),
|
|
33615
|
+
wikiDir: path27__default.join(root, WIKI_DIRNAME),
|
|
33616
|
+
schemaDir: path27__default.join(root, SCHEMA_DIRNAME),
|
|
33617
|
+
queueDir: path27__default.join(root, QUEUE_DIRNAME),
|
|
33618
|
+
archiveDir: path27__default.join(root, ARCHIVE_DIRNAME),
|
|
33619
|
+
conflictsDir: path27__default.join(root, CONFLICTS_DIRNAME),
|
|
33620
|
+
indexFile: path27__default.join(root, INDEX_FILENAME)
|
|
33221
33621
|
};
|
|
33222
33622
|
}
|
|
33223
33623
|
async function ensureMemoryRoot(paths) {
|
|
@@ -33232,10 +33632,10 @@ async function ensureMemoryRoot(paths) {
|
|
|
33232
33632
|
await ensureWorkspaceDoctrine(paths);
|
|
33233
33633
|
}
|
|
33234
33634
|
function getIndexMarkdownFile(paths) {
|
|
33235
|
-
return
|
|
33635
|
+
return path27__default.join(paths.wikiDir, INDEX_MD_FILENAME);
|
|
33236
33636
|
}
|
|
33237
33637
|
function getLogFile(paths) {
|
|
33238
|
-
return
|
|
33638
|
+
return path27__default.join(paths.root, LOG_MD_FILENAME);
|
|
33239
33639
|
}
|
|
33240
33640
|
|
|
33241
33641
|
// ../../packages/fleet-wiki/src/log.ts
|
|
@@ -33301,12 +33701,12 @@ async function readWikiEntry(id, paths) {
|
|
|
33301
33701
|
const index = await loadIndex(paths);
|
|
33302
33702
|
const indexed = index[id];
|
|
33303
33703
|
if (indexed) {
|
|
33304
|
-
const resolved =
|
|
33305
|
-
const rel =
|
|
33306
|
-
if (!rel || rel.startsWith("..") ||
|
|
33704
|
+
const resolved = path27__default.resolve(paths.root, indexed.path);
|
|
33705
|
+
const rel = path27__default.relative(paths.wikiDir, resolved);
|
|
33706
|
+
if (!rel || rel.startsWith("..") || path27__default.isAbsolute(rel)) return null;
|
|
33307
33707
|
return readMarkdownFile(resolved);
|
|
33308
33708
|
}
|
|
33309
|
-
const fallbackPath =
|
|
33709
|
+
const fallbackPath = path27__default.join(paths.wikiDir, `${id}.md`);
|
|
33310
33710
|
if (await pathExists(fallbackPath)) {
|
|
33311
33711
|
return readMarkdownFile(fallbackPath);
|
|
33312
33712
|
}
|
|
@@ -33317,20 +33717,20 @@ async function readWikiEntry(id, paths) {
|
|
|
33317
33717
|
async function writeWikiEntryAtTarget(entry, target, paths) {
|
|
33318
33718
|
await ensureMemoryRoot(paths);
|
|
33319
33719
|
assertSafeEntryId(entry.id);
|
|
33320
|
-
const absoluteTarget =
|
|
33321
|
-
const relativeTarget =
|
|
33322
|
-
if (!relativeTarget || relativeTarget.startsWith("..") ||
|
|
33720
|
+
const absoluteTarget = path27__default.resolve(paths.root, target);
|
|
33721
|
+
const relativeTarget = path27__default.relative(paths.wikiDir, absoluteTarget);
|
|
33722
|
+
if (!relativeTarget || relativeTarget.startsWith("..") || path27__default.isAbsolute(relativeTarget)) {
|
|
33323
33723
|
throw new Error(`[fleet-wiki] wiki target escapes wiki/: ${target}`);
|
|
33324
33724
|
}
|
|
33325
33725
|
if (!absoluteTarget.endsWith(".md")) {
|
|
33326
33726
|
throw new Error(`[fleet-wiki] wiki target must end with .md: ${target}`);
|
|
33327
33727
|
}
|
|
33328
|
-
if (
|
|
33728
|
+
if (path27__default.basename(absoluteTarget, ".md") !== entry.id) {
|
|
33329
33729
|
throw new Error("wiki patch body id must match target filename");
|
|
33330
33730
|
}
|
|
33331
|
-
await mkdir(
|
|
33731
|
+
await mkdir(path27__default.dirname(absoluteTarget), { recursive: true });
|
|
33332
33732
|
await writeMarkdownAtomic(absoluteTarget, serializeWikiEntry(entry), paths);
|
|
33333
|
-
return
|
|
33733
|
+
return path27__default.join("wiki", relativeTarget).replaceAll(path27__default.sep, "/");
|
|
33334
33734
|
}
|
|
33335
33735
|
async function listWiki(paths) {
|
|
33336
33736
|
const entries = await listWikiRecords(paths);
|
|
@@ -33553,7 +33953,7 @@ function parseWikiEntry(content) {
|
|
|
33553
33953
|
async function listWikiRecords(paths) {
|
|
33554
33954
|
return listMarkdownEntriesRecursive(paths.wikiDir, paths, (content, filePath) => ({
|
|
33555
33955
|
entry: parseWikiEntry(content),
|
|
33556
|
-
path:
|
|
33956
|
+
path: path27__default.relative(paths.root, filePath).replaceAll(path27__default.sep, "/")
|
|
33557
33957
|
}));
|
|
33558
33958
|
}
|
|
33559
33959
|
async function listMarkdownEntriesRecursive(dirPath, paths, parser) {
|
|
@@ -33566,7 +33966,7 @@ async function listMarkdownEntriesRecursive(dirPath, paths, parser) {
|
|
|
33566
33966
|
}
|
|
33567
33967
|
const sortedEntries = entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
33568
33968
|
for (const entry of sortedEntries) {
|
|
33569
|
-
const filePath =
|
|
33969
|
+
const filePath = path27__default.join(dirPath, entry.name);
|
|
33570
33970
|
if (entry.isDirectory()) {
|
|
33571
33971
|
items.push(...await listMarkdownEntriesRecursive(filePath, paths, parser));
|
|
33572
33972
|
continue;
|
|
@@ -33704,8 +34104,8 @@ async function writeJsonAtomic(filePath, value, paths) {
|
|
|
33704
34104
|
}
|
|
33705
34105
|
async function writeAtomic(filePath, content, paths) {
|
|
33706
34106
|
await ensureMemoryRoot(paths);
|
|
33707
|
-
const tempPath =
|
|
33708
|
-
|
|
34107
|
+
const tempPath = path27__default.join(
|
|
34108
|
+
path27__default.dirname(filePath),
|
|
33709
34109
|
`.tmp-${process.pid}-${Date.now()}-${crypto5.randomUUID()}-${os4__default.hostname()}`
|
|
33710
34110
|
);
|
|
33711
34111
|
await writeFile(tempPath, content, "utf8");
|
|
@@ -33799,7 +34199,7 @@ function toRankedHit(entry, reason, detail, graphBoost, backlinksById) {
|
|
|
33799
34199
|
score: basePriority * 100 + statusPriority * 10 + graphScore + (detail.tagBoost ?? 0),
|
|
33800
34200
|
reason,
|
|
33801
34201
|
excerpt,
|
|
33802
|
-
path:
|
|
34202
|
+
path: path27__default.join("wiki", `${entry.id}.md`),
|
|
33803
34203
|
tags: entry.tags,
|
|
33804
34204
|
updated: entry.updated,
|
|
33805
34205
|
version: entry.version,
|
|
@@ -33909,15 +34309,15 @@ var PATCH_SET_DIRNAME = "_sets";
|
|
|
33909
34309
|
var PATCH_SET_META_FILENAME = PATCH_META_FILENAME;
|
|
33910
34310
|
function getPatchSetDir(paths, patchSetId) {
|
|
33911
34311
|
assertSafePatchSetId(patchSetId);
|
|
33912
|
-
const patchSetDir =
|
|
33913
|
-
const relative =
|
|
33914
|
-
if (!relative || relative.startsWith("..") ||
|
|
34312
|
+
const patchSetDir = path27__default.join(paths.queueDir, PATCH_SET_DIRNAME, patchSetId);
|
|
34313
|
+
const relative = path27__default.relative(path27__default.join(paths.queueDir, PATCH_SET_DIRNAME), patchSetDir);
|
|
34314
|
+
if (!relative || relative.startsWith("..") || path27__default.isAbsolute(relative)) {
|
|
33915
34315
|
throw new Error(`[fleet-wiki] patch set id escapes queue/_sets: ${patchSetId}`);
|
|
33916
34316
|
}
|
|
33917
34317
|
return patchSetDir;
|
|
33918
34318
|
}
|
|
33919
34319
|
function getPatchSetMetaFile(paths, patchSetId) {
|
|
33920
|
-
return
|
|
34320
|
+
return path27__default.join(getPatchSetDir(paths, patchSetId), PATCH_SET_META_FILENAME);
|
|
33921
34321
|
}
|
|
33922
34322
|
async function readPatchSet(paths, patchSetId) {
|
|
33923
34323
|
const patchSet = await readJsonFile2(getPatchSetMetaFile(paths, patchSetId));
|
|
@@ -33940,7 +34340,7 @@ async function createConflict(input, paths) {
|
|
|
33940
34340
|
await ensureMemoryRoot(paths);
|
|
33941
34341
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
33942
34342
|
const conflictId = buildConflictId(now, input.wikiId, input.reason, input.proposed);
|
|
33943
|
-
const conflictDir =
|
|
34343
|
+
const conflictDir = path27__default.join(paths.conflictsDir, conflictId);
|
|
33944
34344
|
await mkdir(conflictDir, { recursive: true });
|
|
33945
34345
|
const meta3 = {
|
|
33946
34346
|
id: conflictId,
|
|
@@ -33960,13 +34360,13 @@ async function createConflict(input, paths) {
|
|
|
33960
34360
|
currentHash: input.currentHash,
|
|
33961
34361
|
warnings: input.warnings
|
|
33962
34362
|
};
|
|
33963
|
-
await writeJsonFile(
|
|
33964
|
-
await writePatchFile(
|
|
34363
|
+
await writeJsonFile(path27__default.join(conflictDir, CONFLICT_META_FILENAME), meta3, paths);
|
|
34364
|
+
await writePatchFile(path27__default.join(conflictDir, CONFLICT_PROPOSED_FILENAME), input.proposed, paths);
|
|
33965
34365
|
if (input.current !== void 0) {
|
|
33966
|
-
await writePatchFile(
|
|
34366
|
+
await writePatchFile(path27__default.join(conflictDir, CONFLICT_CURRENT_FILENAME), input.current, paths);
|
|
33967
34367
|
}
|
|
33968
34368
|
if (input.rawSource !== void 0) {
|
|
33969
|
-
await writePatchFile(
|
|
34369
|
+
await writePatchFile(path27__default.join(conflictDir, CONFLICT_RAW_SOURCE_FILENAME), input.rawSource, paths);
|
|
33970
34370
|
}
|
|
33971
34371
|
return {
|
|
33972
34372
|
meta: meta3,
|
|
@@ -34016,11 +34416,11 @@ async function validatePatch(patch, paths) {
|
|
|
34016
34416
|
if (!target || !summary || !proposer || !created) throw new Error("patch frontmatter is incomplete");
|
|
34017
34417
|
if (summary.length > 120) throw new Error("patch summary exceeds 120 chars");
|
|
34018
34418
|
assertCanonicalPatchTarget(target);
|
|
34019
|
-
const absoluteTarget =
|
|
34020
|
-
if (!absoluteTarget.startsWith(`${paths.root}${
|
|
34419
|
+
const absoluteTarget = path27__default.resolve(paths.root, target);
|
|
34420
|
+
if (!absoluteTarget.startsWith(`${paths.root}${path27__default.sep}`) && absoluteTarget !== paths.root) {
|
|
34021
34421
|
throw new Error("patch target escapes wiki root");
|
|
34022
34422
|
}
|
|
34023
|
-
if (!absoluteTarget.startsWith(`${paths.wikiDir}${
|
|
34423
|
+
if (!absoluteTarget.startsWith(`${paths.wikiDir}${path27__default.sep}`)) throw new Error("wiki patch must target wiki/");
|
|
34024
34424
|
assertNoSymlinkPathComponents(target, paths);
|
|
34025
34425
|
if (op === "create_wiki" && await pathExists(absoluteTarget)) {
|
|
34026
34426
|
throw new Error(
|
|
@@ -34044,7 +34444,7 @@ async function listQueue(paths) {
|
|
|
34044
34444
|
for (const id of ids) {
|
|
34045
34445
|
if (id === "_sets") continue;
|
|
34046
34446
|
try {
|
|
34047
|
-
const meta3 = await readJsonFile2(
|
|
34447
|
+
const meta3 = await readJsonFile2(path27__default.join(paths.queueDir, id, PATCH_META_FILENAME));
|
|
34048
34448
|
results.push({ id, meta: meta3 });
|
|
34049
34449
|
} catch {
|
|
34050
34450
|
continue;
|
|
@@ -34069,9 +34469,9 @@ async function resolveQueueSelection(id, paths) {
|
|
|
34069
34469
|
}
|
|
34070
34470
|
async function showQueue(id, paths) {
|
|
34071
34471
|
const selection = await resolveQueueSelection(id, paths);
|
|
34072
|
-
const queueDir =
|
|
34073
|
-
const patch = await parsePatch(await readPatchFile(
|
|
34074
|
-
const meta3 = await readJsonFile2(
|
|
34472
|
+
const queueDir = path27__default.join(paths.queueDir, selection.id);
|
|
34473
|
+
const patch = await parsePatch(await readPatchFile(path27__default.join(queueDir, PATCH_FILENAME)));
|
|
34474
|
+
const meta3 = await readJsonFile2(path27__default.join(queueDir, PATCH_META_FILENAME));
|
|
34075
34475
|
return { patch, meta: meta3 };
|
|
34076
34476
|
}
|
|
34077
34477
|
async function approvePatch(id, paths) {
|
|
@@ -34094,8 +34494,8 @@ async function approvePatch(id, paths) {
|
|
|
34094
34494
|
rawSourceRef: meta3.rawSourceRef,
|
|
34095
34495
|
warnings: meta3.warnings
|
|
34096
34496
|
});
|
|
34097
|
-
const queueDir =
|
|
34098
|
-
await writeJsonFile(
|
|
34497
|
+
const queueDir = path27__default.join(paths.queueDir, id);
|
|
34498
|
+
await writeJsonFile(path27__default.join(queueDir, PATCH_META_FILENAME), {
|
|
34099
34499
|
...meta3,
|
|
34100
34500
|
conflictId
|
|
34101
34501
|
}, paths);
|
|
@@ -34148,7 +34548,7 @@ async function rejectPatch(id, reason, paths) {
|
|
|
34148
34548
|
}
|
|
34149
34549
|
async function normalizeWikiEntryPatch(entry, target, paths) {
|
|
34150
34550
|
assertSafeEntryId(entry.id);
|
|
34151
|
-
if (entry.id !==
|
|
34551
|
+
if (entry.id !== path27__default.basename(target, ".md")) {
|
|
34152
34552
|
throw new Error("wiki patch body id must match target filename");
|
|
34153
34553
|
}
|
|
34154
34554
|
const inlineRawSourceRef = extractInlineRawSourceRef(entry.body);
|
|
@@ -34186,9 +34586,9 @@ function extractInlineRawSourceRef(body) {
|
|
|
34186
34586
|
}
|
|
34187
34587
|
async function validatePatchBase(patch, meta3, paths) {
|
|
34188
34588
|
if (patch.frontmatter.op !== "update_wiki" || !meta3?.baseVersion && !meta3?.baseHash) return;
|
|
34189
|
-
const wikiId =
|
|
34589
|
+
const wikiId = path27__default.basename(patch.frontmatter.target, ".md");
|
|
34190
34590
|
const currentEntry = await readWikiEntry(wikiId, paths);
|
|
34191
|
-
const currentPath =
|
|
34591
|
+
const currentPath = path27__default.join(paths.root, patch.frontmatter.target);
|
|
34192
34592
|
const currentMarkdown = await pathExists(currentPath) ? await readPatchFile(currentPath) : void 0;
|
|
34193
34593
|
if (meta3.baseVersion !== void 0 && currentEntry?.version !== meta3.baseVersion) {
|
|
34194
34594
|
throw new Error(
|
|
@@ -34247,17 +34647,17 @@ function assertCanonicalPatchTarget(target) {
|
|
|
34247
34647
|
if (target.endsWith("/")) {
|
|
34248
34648
|
throw new Error("patch target must not end with a slash");
|
|
34249
34649
|
}
|
|
34250
|
-
if (target.startsWith("/") ||
|
|
34650
|
+
if (target.startsWith("/") || path27__default.posix.isAbsolute(target) || path27__default.win32.isAbsolute(target)) {
|
|
34251
34651
|
throw new Error("patch target must be relative");
|
|
34252
34652
|
}
|
|
34253
|
-
if (
|
|
34653
|
+
if (path27__default.posix.normalize(target) !== target) {
|
|
34254
34654
|
throw new Error("patch target must not contain dot segments or redundant separators");
|
|
34255
34655
|
}
|
|
34256
34656
|
}
|
|
34257
34657
|
function canonicalApprovalLockKey(paths, target) {
|
|
34258
34658
|
const wikiRoot = realpathSync(paths.wikiDir);
|
|
34259
|
-
const relativeTarget =
|
|
34260
|
-
const targetPath =
|
|
34659
|
+
const relativeTarget = path27__default.posix.relative("wiki", target);
|
|
34660
|
+
const targetPath = path27__default.join(wikiRoot, ...relativeTarget.split("/"));
|
|
34261
34661
|
return `${wikiRoot}\0${targetPath}`.toLowerCase();
|
|
34262
34662
|
}
|
|
34263
34663
|
function canonicalPatchEditLockKey(paths, patchId) {
|
|
@@ -34273,7 +34673,7 @@ function assertNoSymlinkPathComponents(target, paths) {
|
|
|
34273
34673
|
throw error51;
|
|
34274
34674
|
}
|
|
34275
34675
|
for (const part of parts) {
|
|
34276
|
-
current =
|
|
34676
|
+
current = path27__default.join(current, part);
|
|
34277
34677
|
try {
|
|
34278
34678
|
if (lstatSync(current).isSymbolicLink()) {
|
|
34279
34679
|
throw new Error("patch target must not include symlink path components");
|
|
@@ -34288,8 +34688,8 @@ function assertSafeRawSourceRef(rawSourceRef, paths) {
|
|
|
34288
34688
|
if (!rawSourceRef.startsWith("raw/")) {
|
|
34289
34689
|
throw new Error("raw source provenance must point into raw/");
|
|
34290
34690
|
}
|
|
34291
|
-
const absoluteRef =
|
|
34292
|
-
if (!absoluteRef.startsWith(`${paths.rawDir}${
|
|
34691
|
+
const absoluteRef = path27__default.resolve(paths.root, rawSourceRef);
|
|
34692
|
+
if (!absoluteRef.startsWith(`${paths.rawDir}${path27__default.sep}`)) {
|
|
34293
34693
|
throw new Error("raw source provenance must point into raw/");
|
|
34294
34694
|
}
|
|
34295
34695
|
}
|
|
@@ -34305,14 +34705,14 @@ function classifyPatchConflict(error51) {
|
|
|
34305
34705
|
}
|
|
34306
34706
|
async function recordPatchConflict(patch, paths, reason, options) {
|
|
34307
34707
|
const now = /* @__PURE__ */ new Date();
|
|
34308
|
-
const targetPath =
|
|
34708
|
+
const targetPath = path27__default.join(paths.root, patch.frontmatter.target);
|
|
34309
34709
|
const current = await pathExists(targetPath) ? await readPatchFile(targetPath) : void 0;
|
|
34310
34710
|
const currentEntry = current ? parseStoredWikiEntry(current) : void 0;
|
|
34311
34711
|
const proposedEntry = parsePatchBodyEntry(patch.body);
|
|
34312
34712
|
const record2 = await createConflict({
|
|
34313
34713
|
reason,
|
|
34314
34714
|
target: patch.frontmatter.target,
|
|
34315
|
-
wikiId: proposedEntry?.id ??
|
|
34715
|
+
wikiId: proposedEntry?.id ?? path27__default.basename(patch.frontmatter.target, ".md"),
|
|
34316
34716
|
title: proposedEntry?.title,
|
|
34317
34717
|
proposer: patch.frontmatter.proposer,
|
|
34318
34718
|
rawSourceRef: options?.rawSourceRef,
|
|
@@ -34408,11 +34808,11 @@ function parseInlineArray(value) {
|
|
|
34408
34808
|
return inner.split(",").map((item) => item.trim().replace(/^"(.*)"$/, "$1"));
|
|
34409
34809
|
}
|
|
34410
34810
|
async function archiveQueueEntry(id, paths, meta3) {
|
|
34411
|
-
const fromDir =
|
|
34412
|
-
const toDir =
|
|
34811
|
+
const fromDir = path27__default.join(paths.queueDir, id);
|
|
34812
|
+
const toDir = path27__default.join(paths.archiveDir, id);
|
|
34413
34813
|
await removePath(toDir);
|
|
34414
34814
|
await movePath(fromDir, toDir);
|
|
34415
|
-
await writeJsonFile(
|
|
34815
|
+
await writeJsonFile(path27__default.join(toDir, PATCH_META_FILENAME), meta3, paths);
|
|
34416
34816
|
}
|
|
34417
34817
|
function buildQueueIdHelp(prefix, availableIds) {
|
|
34418
34818
|
if (availableIds.length === 0) {
|
|
@@ -35134,15 +35534,15 @@ async function canonicalizeTheaterPath(cwd) {
|
|
|
35134
35534
|
return canonicalizeTheaterPathSync(cwd);
|
|
35135
35535
|
}
|
|
35136
35536
|
function canonicalizeTheaterPathSync(cwd) {
|
|
35137
|
-
const resolved =
|
|
35537
|
+
const resolved = path27__default.resolve(cwd);
|
|
35138
35538
|
try {
|
|
35139
|
-
return
|
|
35539
|
+
return fs14__default.realpathSync.native(resolved);
|
|
35140
35540
|
} catch {
|
|
35141
35541
|
return resolved;
|
|
35142
35542
|
}
|
|
35143
35543
|
}
|
|
35144
35544
|
function theaterLabel(cwd) {
|
|
35145
|
-
return
|
|
35545
|
+
return path27__default.basename(cwd) || cwd;
|
|
35146
35546
|
}
|
|
35147
35547
|
|
|
35148
35548
|
// core/host/codex/workspaces.ts
|
|
@@ -35150,7 +35550,7 @@ var WorkspaceRegistry = class {
|
|
|
35150
35550
|
#items = /* @__PURE__ */ new Map();
|
|
35151
35551
|
#mruId = null;
|
|
35152
35552
|
async register(cwdInput, lastOpenedAt) {
|
|
35153
|
-
const cwd =
|
|
35553
|
+
const cwd = path27__default.resolve(cwdInput);
|
|
35154
35554
|
const real = await canonicalizeTheaterPath(cwd);
|
|
35155
35555
|
const paths = resolveMemoryPaths(real);
|
|
35156
35556
|
if (!await directoryExists(paths.root)) {
|
|
@@ -35166,7 +35566,7 @@ var WorkspaceRegistry = class {
|
|
|
35166
35566
|
id,
|
|
35167
35567
|
cwd,
|
|
35168
35568
|
realpath: real,
|
|
35169
|
-
label:
|
|
35569
|
+
label: path27__default.basename(cwd),
|
|
35170
35570
|
paths,
|
|
35171
35571
|
registeredAt: existing?.registeredAt ?? now,
|
|
35172
35572
|
// 복원 경로는 durable lastOpenedAt을 그대로 보존해 재시작 후에도 워크스페이스 최근성
|
|
@@ -35232,6 +35632,7 @@ var LOOPBACK_ACCESS_HOSTS = ["127.0.0.1", "::1"];
|
|
|
35232
35632
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
35233
35633
|
function createCodexGateway(deps) {
|
|
35234
35634
|
const workspaces = new WorkspaceRegistry();
|
|
35635
|
+
const workspaceOwners = /* @__PURE__ */ new Map();
|
|
35235
35636
|
let accessSets = null;
|
|
35236
35637
|
let initialWorkspace = null;
|
|
35237
35638
|
let initialWorkspaceId = null;
|
|
@@ -35261,6 +35662,10 @@ function createCodexGateway(deps) {
|
|
|
35261
35662
|
redirect(response, `${CODEX_BASE}${selected.location}`);
|
|
35262
35663
|
return true;
|
|
35263
35664
|
}
|
|
35665
|
+
if (selected.kind === "missing-workspace") {
|
|
35666
|
+
sendJson2(response, 404, { error: "workspace_not_found" });
|
|
35667
|
+
return true;
|
|
35668
|
+
}
|
|
35264
35669
|
if (selected.kind === "no-workspace") {
|
|
35265
35670
|
if (isJsonRequest4(request)) {
|
|
35266
35671
|
sendJson2(response, 404, { error: "no_workspace_registered" });
|
|
@@ -35291,18 +35696,50 @@ function createCodexGateway(deps) {
|
|
|
35291
35696
|
initialWorkspaceId = workspace.id;
|
|
35292
35697
|
return workspace;
|
|
35293
35698
|
}
|
|
35699
|
+
async function registerWorkspace(cwd, lastOpenedAt, ownerTheaterId) {
|
|
35700
|
+
const workspace = await workspaces.register(cwd, lastOpenedAt);
|
|
35701
|
+
if (ownerTheaterId) {
|
|
35702
|
+
const owners = workspaceOwners.get(workspace.id) ?? /* @__PURE__ */ new Set();
|
|
35703
|
+
owners.add(ownerTheaterId);
|
|
35704
|
+
workspaceOwners.set(workspace.id, owners);
|
|
35705
|
+
}
|
|
35706
|
+
return workspace;
|
|
35707
|
+
}
|
|
35708
|
+
async function resolveWorkspaceForPath(theaterId, theaterRoot, relPath) {
|
|
35709
|
+
const pathContext = await resolveTheaterPathContext(theaterRoot, relPath);
|
|
35710
|
+
try {
|
|
35711
|
+
const workspace = await registerWorkspace(pathContext.realPath, void 0, theaterId);
|
|
35712
|
+
return { hasWiki: true, id: workspace.id };
|
|
35713
|
+
} catch (error51) {
|
|
35714
|
+
if (error51 instanceof Error && error51.message === "knowledge_root_missing") {
|
|
35715
|
+
return { hasWiki: false, id: null };
|
|
35716
|
+
}
|
|
35717
|
+
throw error51;
|
|
35718
|
+
}
|
|
35719
|
+
}
|
|
35720
|
+
function unregisterWorkspace(id) {
|
|
35721
|
+
if (initialWorkspaceId === id) {
|
|
35722
|
+
initialWorkspace = null;
|
|
35723
|
+
initialWorkspaceId = null;
|
|
35724
|
+
}
|
|
35725
|
+
return workspaces.remove(id);
|
|
35726
|
+
}
|
|
35727
|
+
function unregisterTheaterWorkspaces(theaterId) {
|
|
35728
|
+
for (const [workspaceId, owners] of workspaceOwners) {
|
|
35729
|
+
owners.delete(theaterId);
|
|
35730
|
+
if (owners.size === 0) {
|
|
35731
|
+
workspaceOwners.delete(workspaceId);
|
|
35732
|
+
unregisterWorkspace(workspaceId);
|
|
35733
|
+
}
|
|
35734
|
+
}
|
|
35735
|
+
}
|
|
35294
35736
|
return {
|
|
35295
35737
|
getWorkspace: (id) => workspaces.get(id),
|
|
35296
35738
|
handle,
|
|
35297
35739
|
listWorkspaceRegistrations: () => workspaces.listRegistrations(),
|
|
35298
|
-
registerWorkspace
|
|
35299
|
-
|
|
35300
|
-
|
|
35301
|
-
initialWorkspace = null;
|
|
35302
|
-
initialWorkspaceId = null;
|
|
35303
|
-
}
|
|
35304
|
-
return workspaces.remove(id);
|
|
35305
|
-
}
|
|
35740
|
+
registerWorkspace,
|
|
35741
|
+
resolveWorkspaceForPath,
|
|
35742
|
+
unregisterTheaterWorkspaces
|
|
35306
35743
|
};
|
|
35307
35744
|
}
|
|
35308
35745
|
function buildAllowedAccessSets(host, port, interfaces = os4__default.networkInterfaces()) {
|
|
@@ -35330,8 +35767,10 @@ function selectWorkspace(requestUrl, workspaces, cwd) {
|
|
|
35330
35767
|
const prefixed = url2.pathname.match(/^\/w\/([^/]+)(\/.*)?$/);
|
|
35331
35768
|
if (prefixed) {
|
|
35332
35769
|
const workspace = workspaces.get(decodeURIComponent(prefixed[1] ?? ""));
|
|
35333
|
-
if (!workspace) return { kind: "redirect", location: "/" };
|
|
35334
35770
|
const suffix = prefixed[2] ?? "/";
|
|
35771
|
+
if (!workspace) {
|
|
35772
|
+
return suffix.startsWith("/api/") ? { kind: "missing-workspace" } : { kind: "redirect", location: "/" };
|
|
35773
|
+
}
|
|
35335
35774
|
url2.pathname = suffix;
|
|
35336
35775
|
return { kind: "workspace", workspace, rewrittenUrl: `${url2.pathname}${url2.search}` };
|
|
35337
35776
|
}
|
|
@@ -35484,7 +35923,7 @@ function createConsoleDurableStateStore(deps = {}) {
|
|
|
35484
35923
|
const createStore = deps.createStore ?? createDurableJsonStore;
|
|
35485
35924
|
return createStore({
|
|
35486
35925
|
filePath: paths.stateFile,
|
|
35487
|
-
lockDir:
|
|
35926
|
+
lockDir: path27__default.join(paths.dir, STATE_LOCK_DIR_NAME),
|
|
35488
35927
|
lockOwnerFileName: STATE_LOCK_OWNER_FILE_NAME,
|
|
35489
35928
|
now: deps.now,
|
|
35490
35929
|
sanitize: sanitizeDurableConsoleState,
|
|
@@ -35526,7 +35965,7 @@ function migrateV1Operation(value) {
|
|
|
35526
35965
|
const cwd = readNonEmptyString(value.cwd);
|
|
35527
35966
|
const createdAt = readFiniteNumber(value.createdAt);
|
|
35528
35967
|
if (!sessionId || !theaterId || !cwd || createdAt === null) return null;
|
|
35529
|
-
const title = readOptionalString(value.label) ?? (
|
|
35968
|
+
const title = readOptionalString(value.label) ?? (path27__default.basename(cwd) || cwd);
|
|
35530
35969
|
const payload = { cwd };
|
|
35531
35970
|
const cliId = readOptionalString(value.cliId);
|
|
35532
35971
|
const cliLabel = readOptionalString(value.cliLabel);
|
|
@@ -35573,7 +36012,11 @@ function sanitizeTheaterRegistration(value) {
|
|
|
35573
36012
|
const registeredAt = readNonEmptyString(value.registeredAt);
|
|
35574
36013
|
const lastOpenedAt = readNonEmptyString(value.lastOpenedAt);
|
|
35575
36014
|
if (!id || !theaterPath || !realpath2 || !label || !registeredAt || !lastOpenedAt) return null;
|
|
35576
|
-
|
|
36015
|
+
const pathContext = typeof value.pathContext === "string" && isSafeDurableRelPath(value.pathContext) ? value.pathContext : null;
|
|
36016
|
+
return { id, path: theaterPath, realpath: realpath2, label, registeredAt, lastOpenedAt, pathContext };
|
|
36017
|
+
}
|
|
36018
|
+
function isSafeDurableRelPath(value) {
|
|
36019
|
+
return value.length > 0 && !value.includes("\0") && !value.startsWith("/") && !value.startsWith("\\") && !/^[A-Za-z]:/.test(value) && !value.split("/").some((part) => part === "" || part === "." || part === "..");
|
|
35577
36020
|
}
|
|
35578
36021
|
function sanitizeOperationNode(value) {
|
|
35579
36022
|
if (!isRecord6(value)) return null;
|
|
@@ -36110,6 +36553,359 @@ function sanitizeReplacementNodes(nextNodes) {
|
|
|
36110
36553
|
return result;
|
|
36111
36554
|
}
|
|
36112
36555
|
|
|
36556
|
+
// core/host/plans/plan-parse.ts
|
|
36557
|
+
var TITLE_PATTERN = /^#\s+(.+)$/;
|
|
36558
|
+
var EXECUTION_MODE_PATTERN = /^-\s*Execution mode:\s*(sequential|parallel)\b/i;
|
|
36559
|
+
var WAVE_PATTERN = /^##\s+wave\s+(\d+)\b/i;
|
|
36560
|
+
var LANE_PATTERN = /^###\s+lane\s+(.+)$/i;
|
|
36561
|
+
var LANE_ID_PATTERN = /^(W\d+-[A-Za-z0-9]+)\b/i;
|
|
36562
|
+
var LANE_BOUNDARY_PATTERN = /^#{1,3}\s/;
|
|
36563
|
+
var SECTION_HEADING_PATTERN = /^#{1,2}\s/;
|
|
36564
|
+
var CHECKBOX_PATTERN = /^\s*-\s*\[( |x|X)\]/;
|
|
36565
|
+
var FENCE_PATTERN = /^\s*(`{3,}|~{3,})/;
|
|
36566
|
+
var CLOSING_FENCE_PATTERN = /^\s*(`{3,}|~{3,})\s*$/;
|
|
36567
|
+
var TEMPLATE_SECTION_TITLES = /* @__PURE__ */ new Set([
|
|
36568
|
+
"objective",
|
|
36569
|
+
"file ownership",
|
|
36570
|
+
"waves",
|
|
36571
|
+
"qa gates",
|
|
36572
|
+
"acceptance criteria",
|
|
36573
|
+
"documentation updates",
|
|
36574
|
+
"final review loop"
|
|
36575
|
+
]);
|
|
36576
|
+
function parsePlan(content) {
|
|
36577
|
+
const lines = scanPlanLines(content.split(/\r?\n/));
|
|
36578
|
+
const title = findTitle(lines);
|
|
36579
|
+
const executionMode = findExecutionMode(lines);
|
|
36580
|
+
const waveStarts = findWaveStarts(lines);
|
|
36581
|
+
const waves = waveStarts.map(({ index, heading, lineIndex }, waveIndex) => {
|
|
36582
|
+
const nextWaveLineIndex = waveStarts[waveIndex + 1]?.lineIndex ?? lines.length;
|
|
36583
|
+
const endLineIndex = findSectionEnd(lines, lineIndex + 1, nextWaveLineIndex);
|
|
36584
|
+
const waveLines = lines.slice(lineIndex + 1, endLineIndex);
|
|
36585
|
+
const counts = countTasks(waveLines);
|
|
36586
|
+
return { index, heading, lanes: findLanes(waveLines), ...counts };
|
|
36587
|
+
});
|
|
36588
|
+
return { title, executionMode, waves, ...countTasks(lines) };
|
|
36589
|
+
}
|
|
36590
|
+
function scanPlanLines(lines) {
|
|
36591
|
+
let activeFence = null;
|
|
36592
|
+
return lines.map((text) => {
|
|
36593
|
+
if (activeFence !== null) {
|
|
36594
|
+
const closer = CLOSING_FENCE_PATTERN.exec(text)?.[1];
|
|
36595
|
+
if (closer !== void 0 && closer.startsWith(activeFence.char) && closer.length >= activeFence.length) {
|
|
36596
|
+
activeFence = null;
|
|
36597
|
+
}
|
|
36598
|
+
return { text, isFenced: true };
|
|
36599
|
+
}
|
|
36600
|
+
const marker = FENCE_PATTERN.exec(text)?.[1];
|
|
36601
|
+
if (marker !== void 0) {
|
|
36602
|
+
activeFence = { char: marker[0] ?? "`", length: marker.length };
|
|
36603
|
+
return { text, isFenced: true };
|
|
36604
|
+
}
|
|
36605
|
+
return { text, isFenced: false };
|
|
36606
|
+
});
|
|
36607
|
+
}
|
|
36608
|
+
function findTitle(lines) {
|
|
36609
|
+
for (const line of lines) {
|
|
36610
|
+
if (line.isFenced) continue;
|
|
36611
|
+
const match = TITLE_PATTERN.exec(line.text);
|
|
36612
|
+
if (!match?.[1]) continue;
|
|
36613
|
+
const heading = match[1].trim();
|
|
36614
|
+
if (TEMPLATE_SECTION_TITLES.has(heading.toLowerCase())) return null;
|
|
36615
|
+
return heading;
|
|
36616
|
+
}
|
|
36617
|
+
return null;
|
|
36618
|
+
}
|
|
36619
|
+
function findExecutionMode(lines) {
|
|
36620
|
+
for (const line of lines) {
|
|
36621
|
+
if (line.isFenced) continue;
|
|
36622
|
+
const match = EXECUTION_MODE_PATTERN.exec(line.text.trim());
|
|
36623
|
+
if (match?.[1]) return match[1].toLowerCase();
|
|
36624
|
+
}
|
|
36625
|
+
return null;
|
|
36626
|
+
}
|
|
36627
|
+
function findWaveStarts(lines) {
|
|
36628
|
+
const waves = [];
|
|
36629
|
+
for (const [lineIndex, line] of lines.entries()) {
|
|
36630
|
+
if (line.isFenced) continue;
|
|
36631
|
+
const match = WAVE_PATTERN.exec(line.text);
|
|
36632
|
+
if (!match?.[1]) continue;
|
|
36633
|
+
waves.push({
|
|
36634
|
+
index: Number(match[1]),
|
|
36635
|
+
heading: line.text.replace(/^##\s+/, ""),
|
|
36636
|
+
lineIndex
|
|
36637
|
+
});
|
|
36638
|
+
}
|
|
36639
|
+
return waves;
|
|
36640
|
+
}
|
|
36641
|
+
function findLanes(waveLines) {
|
|
36642
|
+
const laneStarts = [];
|
|
36643
|
+
for (const [lineIndex, line] of waveLines.entries()) {
|
|
36644
|
+
if (line.isFenced) continue;
|
|
36645
|
+
const match = LANE_PATTERN.exec(line.text);
|
|
36646
|
+
if (!match?.[1]) continue;
|
|
36647
|
+
const heading = match[1].trim();
|
|
36648
|
+
laneStarts.push({ heading, id: LANE_ID_PATTERN.exec(heading)?.[1]?.toUpperCase() ?? null, lineIndex });
|
|
36649
|
+
}
|
|
36650
|
+
return laneStarts.map(({ heading, id, lineIndex }, laneIndex) => {
|
|
36651
|
+
const nextLaneLineIndex = laneStarts[laneIndex + 1]?.lineIndex ?? waveLines.length;
|
|
36652
|
+
let endLineIndex = nextLaneLineIndex;
|
|
36653
|
+
for (let cursor = lineIndex + 1; cursor < nextLaneLineIndex; cursor++) {
|
|
36654
|
+
const line = waveLines[cursor];
|
|
36655
|
+
if (!line?.isFenced && LANE_BOUNDARY_PATTERN.test(line?.text ?? "")) {
|
|
36656
|
+
endLineIndex = cursor;
|
|
36657
|
+
break;
|
|
36658
|
+
}
|
|
36659
|
+
}
|
|
36660
|
+
return { id, heading, ...countTasks(waveLines.slice(lineIndex + 1, endLineIndex)) };
|
|
36661
|
+
});
|
|
36662
|
+
}
|
|
36663
|
+
function findSectionEnd(lines, startLineIndex, maxLineIndex) {
|
|
36664
|
+
for (let lineIndex = startLineIndex; lineIndex < maxLineIndex; lineIndex++) {
|
|
36665
|
+
const line = lines[lineIndex];
|
|
36666
|
+
if (!line?.isFenced && SECTION_HEADING_PATTERN.test(line?.text ?? "")) return lineIndex;
|
|
36667
|
+
}
|
|
36668
|
+
return maxLineIndex;
|
|
36669
|
+
}
|
|
36670
|
+
function countTasks(lines) {
|
|
36671
|
+
let tasksDone = 0;
|
|
36672
|
+
let tasksTotal = 0;
|
|
36673
|
+
for (const line of lines) {
|
|
36674
|
+
if (line.isFenced) continue;
|
|
36675
|
+
const match = CHECKBOX_PATTERN.exec(line.text);
|
|
36676
|
+
if (!match) continue;
|
|
36677
|
+
tasksTotal++;
|
|
36678
|
+
if (match[1] === "x" || match[1] === "X") tasksDone++;
|
|
36679
|
+
}
|
|
36680
|
+
return { tasksDone, tasksTotal };
|
|
36681
|
+
}
|
|
36682
|
+
|
|
36683
|
+
// core/host/plans/plan-store.ts
|
|
36684
|
+
var PLANS_DIRECTORY_SEGMENTS = [".fleet", "plans"];
|
|
36685
|
+
var MARKDOWN_EXTENSION = ".md";
|
|
36686
|
+
var PLAN_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/;
|
|
36687
|
+
var LIST_READ_CONCURRENCY = 16;
|
|
36688
|
+
var PLAN_READ_SIZE_CAP = 2 * 1024 * 1024;
|
|
36689
|
+
var PlanStoreError = class extends Error {
|
|
36690
|
+
code;
|
|
36691
|
+
constructor(code) {
|
|
36692
|
+
super(code);
|
|
36693
|
+
this.name = "PlanStoreError";
|
|
36694
|
+
this.code = code;
|
|
36695
|
+
}
|
|
36696
|
+
};
|
|
36697
|
+
async function listPlansForTheater(theaterPath) {
|
|
36698
|
+
const plansRoot = await resolvePlansRoot(theaterPath, true);
|
|
36699
|
+
if (plansRoot === null) return [];
|
|
36700
|
+
let entries;
|
|
36701
|
+
try {
|
|
36702
|
+
entries = await fs14__default.promises.readdir(plansRoot.realPlansPath, { withFileTypes: true });
|
|
36703
|
+
} catch (error51) {
|
|
36704
|
+
throw toPlanStoreError(error51);
|
|
36705
|
+
}
|
|
36706
|
+
const names = entries.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(MARKDOWN_EXTENSION) && isValidPlanName(entry.name)).map((entry) => entry.name);
|
|
36707
|
+
const plans = [];
|
|
36708
|
+
for (let startIndex = 0; startIndex < names.length; startIndex += LIST_READ_CONCURRENCY) {
|
|
36709
|
+
const chunk = names.slice(startIndex, startIndex + LIST_READ_CONCURRENCY);
|
|
36710
|
+
const results = await Promise.all(chunk.map((name) => toPlanListItem(plansRoot, name).catch(() => null)));
|
|
36711
|
+
plans.push(...results.filter((plan) => plan !== null));
|
|
36712
|
+
}
|
|
36713
|
+
return plans.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
36714
|
+
}
|
|
36715
|
+
function isValidPlanName(name) {
|
|
36716
|
+
return PLAN_NAME_PATTERN.test(name) && !name.includes("..");
|
|
36717
|
+
}
|
|
36718
|
+
async function readPlanForTheater(theaterPath, name) {
|
|
36719
|
+
const plansRoot = await resolvePlansRoot(theaterPath, false);
|
|
36720
|
+
if (plansRoot === null) throw new PlanStoreError("not_found");
|
|
36721
|
+
const file2 = await resolvePlanFile(plansRoot, name);
|
|
36722
|
+
if (file2.stat.size > PLAN_READ_SIZE_CAP) throw new PlanStoreError("too_large");
|
|
36723
|
+
let content;
|
|
36724
|
+
try {
|
|
36725
|
+
content = await fs14__default.promises.readFile(file2.realPath, "utf8");
|
|
36726
|
+
} catch (error51) {
|
|
36727
|
+
throw toPlanStoreError(error51);
|
|
36728
|
+
}
|
|
36729
|
+
const parsed = parsePlan(content);
|
|
36730
|
+
return {
|
|
36731
|
+
name,
|
|
36732
|
+
title: parsed.title ?? name,
|
|
36733
|
+
executionMode: parsed.executionMode,
|
|
36734
|
+
updatedAt: file2.stat.mtime.toISOString(),
|
|
36735
|
+
content,
|
|
36736
|
+
waves: parsed.waves,
|
|
36737
|
+
tasksDone: parsed.tasksDone,
|
|
36738
|
+
tasksTotal: parsed.tasksTotal
|
|
36739
|
+
};
|
|
36740
|
+
}
|
|
36741
|
+
async function toPlanListItem(plansRoot, name) {
|
|
36742
|
+
const file2 = await resolvePlanFile(plansRoot, name);
|
|
36743
|
+
if (file2.stat.size > PLAN_READ_SIZE_CAP) {
|
|
36744
|
+
return {
|
|
36745
|
+
name,
|
|
36746
|
+
title: name,
|
|
36747
|
+
executionMode: null,
|
|
36748
|
+
waveCount: 0,
|
|
36749
|
+
tasksDone: 0,
|
|
36750
|
+
tasksTotal: 0,
|
|
36751
|
+
updatedAt: file2.stat.mtime.toISOString(),
|
|
36752
|
+
sizeBytes: file2.stat.size
|
|
36753
|
+
};
|
|
36754
|
+
}
|
|
36755
|
+
const content = await fs14__default.promises.readFile(file2.realPath, "utf8").catch((error51) => {
|
|
36756
|
+
throw toPlanStoreError(error51);
|
|
36757
|
+
});
|
|
36758
|
+
const parsed = parsePlan(content);
|
|
36759
|
+
return {
|
|
36760
|
+
name,
|
|
36761
|
+
title: parsed.title ?? name,
|
|
36762
|
+
executionMode: parsed.executionMode,
|
|
36763
|
+
waveCount: parsed.waves.length,
|
|
36764
|
+
tasksDone: parsed.tasksDone,
|
|
36765
|
+
tasksTotal: parsed.tasksTotal,
|
|
36766
|
+
updatedAt: file2.stat.mtime.toISOString(),
|
|
36767
|
+
sizeBytes: file2.stat.size
|
|
36768
|
+
};
|
|
36769
|
+
}
|
|
36770
|
+
async function resolvePlansRoot(contextPath, allowMissing) {
|
|
36771
|
+
const nominalContextPath = path27__default.resolve(contextPath);
|
|
36772
|
+
const plansPath = path27__default.resolve(nominalContextPath, ...PLANS_DIRECTORY_SEGMENTS);
|
|
36773
|
+
if (!isWithinRoot2(plansPath, nominalContextPath)) throw new PlanStoreError("path_outside_theater");
|
|
36774
|
+
let realContextPath;
|
|
36775
|
+
let realPlansPath;
|
|
36776
|
+
try {
|
|
36777
|
+
[realContextPath, realPlansPath] = await Promise.all([
|
|
36778
|
+
fs14__default.promises.realpath(nominalContextPath),
|
|
36779
|
+
fs14__default.promises.realpath(plansPath)
|
|
36780
|
+
]);
|
|
36781
|
+
} catch (error51) {
|
|
36782
|
+
if (allowMissing && isNotFoundError(error51)) return null;
|
|
36783
|
+
throw toPlanStoreError(error51);
|
|
36784
|
+
}
|
|
36785
|
+
if (!isWithinRoot2(realPlansPath, realContextPath)) throw new PlanStoreError("path_outside_theater");
|
|
36786
|
+
return { realContextPath, realPlansPath };
|
|
36787
|
+
}
|
|
36788
|
+
async function resolvePlanFile(plansRoot, name) {
|
|
36789
|
+
const candidatePath = path27__default.resolve(plansRoot.realPlansPath, name);
|
|
36790
|
+
if (!isWithinRoot2(candidatePath, plansRoot.realPlansPath)) throw new PlanStoreError("path_outside_theater");
|
|
36791
|
+
let realPath;
|
|
36792
|
+
try {
|
|
36793
|
+
realPath = await fs14__default.promises.realpath(candidatePath);
|
|
36794
|
+
} catch (error51) {
|
|
36795
|
+
throw toPlanStoreError(error51);
|
|
36796
|
+
}
|
|
36797
|
+
if (!isWithinRoot2(realPath, plansRoot.realPlansPath) || !isWithinRoot2(realPath, plansRoot.realContextPath)) {
|
|
36798
|
+
throw new PlanStoreError("path_outside_theater");
|
|
36799
|
+
}
|
|
36800
|
+
let stat5;
|
|
36801
|
+
try {
|
|
36802
|
+
stat5 = await fs14__default.promises.stat(realPath);
|
|
36803
|
+
} catch (error51) {
|
|
36804
|
+
throw toPlanStoreError(error51);
|
|
36805
|
+
}
|
|
36806
|
+
if (!stat5.isFile()) throw new PlanStoreError("not_found");
|
|
36807
|
+
return { realPath, stat: stat5 };
|
|
36808
|
+
}
|
|
36809
|
+
function isWithinRoot2(resolvedPath, rootPath) {
|
|
36810
|
+
const normalizedRoot = rootPath.endsWith(path27__default.sep) ? rootPath : rootPath + path27__default.sep;
|
|
36811
|
+
return resolvedPath === rootPath || resolvedPath.startsWith(normalizedRoot);
|
|
36812
|
+
}
|
|
36813
|
+
function isNotFoundError(error51) {
|
|
36814
|
+
return error51.code === "ENOENT";
|
|
36815
|
+
}
|
|
36816
|
+
function toPlanStoreError(error51) {
|
|
36817
|
+
if (error51 instanceof PlanStoreError) return error51;
|
|
36818
|
+
return new PlanStoreError("not_found");
|
|
36819
|
+
}
|
|
36820
|
+
|
|
36821
|
+
// core/host/plans/routes.ts
|
|
36822
|
+
function createPlansRouter(deps) {
|
|
36823
|
+
return async function handlePlansRoute(context) {
|
|
36824
|
+
const { req, res, pathname } = context;
|
|
36825
|
+
if (pathname === "/api/v1/plans/list") {
|
|
36826
|
+
await handlePlansList(req, res, deps);
|
|
36827
|
+
return true;
|
|
36828
|
+
}
|
|
36829
|
+
if (pathname === "/api/v1/plans/read") {
|
|
36830
|
+
await handlePlansRead(req, res, deps);
|
|
36831
|
+
return true;
|
|
36832
|
+
}
|
|
36833
|
+
return false;
|
|
36834
|
+
};
|
|
36835
|
+
}
|
|
36836
|
+
async function handlePlansList(req, res, deps) {
|
|
36837
|
+
if (req.method !== "POST") {
|
|
36838
|
+
deps.writeJson(res, 405, { error: "Method not allowed" });
|
|
36839
|
+
return;
|
|
36840
|
+
}
|
|
36841
|
+
if (!deps.isAuthorized(req)) {
|
|
36842
|
+
deps.writeJson(res, 401, { error: "unauthorized" });
|
|
36843
|
+
return;
|
|
36844
|
+
}
|
|
36845
|
+
const body = await deps.readJsonBody(req);
|
|
36846
|
+
if (!isPlainObject2(body) || typeof body.theaterId !== "string" || !isValidRelPath(body.relPath)) {
|
|
36847
|
+
deps.writeJson(res, 400, { error: "invalid_request" });
|
|
36848
|
+
return;
|
|
36849
|
+
}
|
|
36850
|
+
const theaterPath = deps.resolveTheaterPath(body.theaterId);
|
|
36851
|
+
if (!theaterPath) {
|
|
36852
|
+
deps.writeJson(res, 404, { error: "theater_not_found" });
|
|
36853
|
+
return;
|
|
36854
|
+
}
|
|
36855
|
+
try {
|
|
36856
|
+
const context = await resolveTheaterPathContext(theaterPath, body.relPath);
|
|
36857
|
+
deps.writeJson(res, 200, { plans: await listPlansForTheater(context.realPath) });
|
|
36858
|
+
} catch (error51) {
|
|
36859
|
+
writePlanStoreError(res, deps, error51);
|
|
36860
|
+
}
|
|
36861
|
+
}
|
|
36862
|
+
async function handlePlansRead(req, res, deps) {
|
|
36863
|
+
if (req.method !== "POST") {
|
|
36864
|
+
deps.writeJson(res, 405, { error: "Method not allowed" });
|
|
36865
|
+
return;
|
|
36866
|
+
}
|
|
36867
|
+
if (!deps.isAuthorized(req)) {
|
|
36868
|
+
deps.writeJson(res, 401, { error: "unauthorized" });
|
|
36869
|
+
return;
|
|
36870
|
+
}
|
|
36871
|
+
const body = await deps.readJsonBody(req);
|
|
36872
|
+
if (!isPlainObject2(body) || typeof body.theaterId !== "string" || !isValidRelPath(body.relPath)) {
|
|
36873
|
+
deps.writeJson(res, 400, { error: "invalid_request" });
|
|
36874
|
+
return;
|
|
36875
|
+
}
|
|
36876
|
+
if (typeof body.name !== "string" || !isValidPlanName(body.name)) {
|
|
36877
|
+
deps.writeJson(res, 400, { error: "invalid_name" });
|
|
36878
|
+
return;
|
|
36879
|
+
}
|
|
36880
|
+
const theaterPath = deps.resolveTheaterPath(body.theaterId);
|
|
36881
|
+
if (!theaterPath) {
|
|
36882
|
+
deps.writeJson(res, 404, { error: "theater_not_found" });
|
|
36883
|
+
return;
|
|
36884
|
+
}
|
|
36885
|
+
try {
|
|
36886
|
+
const context = await resolveTheaterPathContext(theaterPath, body.relPath);
|
|
36887
|
+
deps.writeJson(res, 200, await readPlanForTheater(context.realPath, body.name));
|
|
36888
|
+
} catch (error51) {
|
|
36889
|
+
writePlanStoreError(res, deps, error51);
|
|
36890
|
+
}
|
|
36891
|
+
}
|
|
36892
|
+
function isPlainObject2(value) {
|
|
36893
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36894
|
+
}
|
|
36895
|
+
function isValidRelPath(value) {
|
|
36896
|
+
return value === null || typeof value === "string" && isSafeRelativePath(value);
|
|
36897
|
+
}
|
|
36898
|
+
function writePlanStoreError(res, deps, error51) {
|
|
36899
|
+
if (error51 instanceof TheaterPathContextError) {
|
|
36900
|
+
const httpStatus2 = error51.code === "forbidden" ? 403 : error51.code === "not_found" ? 404 : 400;
|
|
36901
|
+
deps.writeJson(res, httpStatus2, { error: error51.code });
|
|
36902
|
+
return;
|
|
36903
|
+
}
|
|
36904
|
+
if (!(error51 instanceof PlanStoreError)) throw error51;
|
|
36905
|
+
const httpStatus = error51.code === "path_outside_theater" ? 403 : error51.code === "too_large" ? 413 : 404;
|
|
36906
|
+
deps.writeJson(res, httpStatus, { error: error51.code });
|
|
36907
|
+
}
|
|
36908
|
+
|
|
36113
36909
|
// sdk/version.ts
|
|
36114
36910
|
var SDK_API_VERSION = 1;
|
|
36115
36911
|
|
|
@@ -36231,14 +37027,14 @@ function renderShim(definition) {
|
|
|
36231
37027
|
].join("\n");
|
|
36232
37028
|
}
|
|
36233
37029
|
function isSubpath(root, target) {
|
|
36234
|
-
const relative =
|
|
36235
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
37030
|
+
const relative = path27__default.relative(root, target);
|
|
37031
|
+
return relative === "" || !relative.startsWith("..") && !path27__default.isAbsolute(relative);
|
|
36236
37032
|
}
|
|
36237
37033
|
function discoverFleetPlugins(options = {}) {
|
|
36238
|
-
const builtInSourceRoot = options.builtInSourceRoot ?? (options.cwd ?
|
|
37034
|
+
const builtInSourceRoot = options.builtInSourceRoot ?? (options.cwd ? path27__default.resolve(options.cwd, "runtime/fleet-plugins") : null);
|
|
36239
37035
|
const roots = [
|
|
36240
37036
|
...builtInSourceRoot ? [{ root: builtInSourceRoot, builtInDistRoot: options.builtInDistRoot ?? null, external: false }] : [],
|
|
36241
|
-
{ root:
|
|
37037
|
+
{ root: path27__default.join(options.homeDir ?? os4__default.homedir(), ".fleet", "plugins"), builtInDistRoot: null, external: true }
|
|
36242
37038
|
];
|
|
36243
37039
|
const sourcePlugins = roots.flatMap((root) => discoverPluginRoot(root.root, root.builtInDistRoot, root.external));
|
|
36244
37040
|
const sourcePluginIds = new Set(sourcePlugins.map((plugin) => plugin.manifest.id));
|
|
@@ -36259,14 +37055,14 @@ function parseFleetPluginManifest(value) {
|
|
|
36259
37055
|
};
|
|
36260
37056
|
}
|
|
36261
37057
|
function discoverPluginRoot(root, builtInDistRoot, external) {
|
|
36262
|
-
if (!
|
|
36263
|
-
const entries =
|
|
37058
|
+
if (!fs14__default.existsSync(root)) return [];
|
|
37059
|
+
const entries = fs14__default.readdirSync(root, { withFileTypes: true });
|
|
36264
37060
|
const plugins = [];
|
|
36265
37061
|
for (const entry of entries) {
|
|
36266
37062
|
if (!entry.isDirectory() || entry.name === "shared") continue;
|
|
36267
|
-
const pluginRoot =
|
|
36268
|
-
const manifestPath =
|
|
36269
|
-
if (!
|
|
37063
|
+
const pluginRoot = path27__default.join(root, entry.name);
|
|
37064
|
+
const manifestPath = path27__default.join(pluginRoot, "plugin.json");
|
|
37065
|
+
if (!fs14__default.existsSync(manifestPath)) continue;
|
|
36270
37066
|
const manifest = readManifest(manifestPath);
|
|
36271
37067
|
if (!manifest) continue;
|
|
36272
37068
|
const clientEntry = resolveOptionalManifestEntry(pluginRoot, manifest.client, "client");
|
|
@@ -36278,14 +37074,14 @@ function discoverPluginRoot(root, builtInDistRoot, external) {
|
|
|
36278
37074
|
return plugins;
|
|
36279
37075
|
}
|
|
36280
37076
|
function discoverDistPluginRoot(root) {
|
|
36281
|
-
if (!
|
|
36282
|
-
const entries =
|
|
37077
|
+
if (!fs14__default.existsSync(root)) return [];
|
|
37078
|
+
const entries = fs14__default.readdirSync(root, { withFileTypes: true });
|
|
36283
37079
|
const plugins = [];
|
|
36284
37080
|
for (const entry of entries) {
|
|
36285
37081
|
if (!entry.isDirectory() || entry.name === "shared") continue;
|
|
36286
|
-
const pluginRoot =
|
|
36287
|
-
const manifestPath =
|
|
36288
|
-
const manifest =
|
|
37082
|
+
const pluginRoot = path27__default.join(root, entry.name);
|
|
37083
|
+
const manifestPath = path27__default.join(pluginRoot, "plugin.json");
|
|
37084
|
+
const manifest = fs14__default.existsSync(manifestPath) ? readManifest(manifestPath) : readDistPluginManifest(entry.name);
|
|
36289
37085
|
if (!manifest) continue;
|
|
36290
37086
|
const clientEntry = resolveOptionalManifestEntry(pluginRoot, manifest.client, "client");
|
|
36291
37087
|
if (clientEntry === false) continue;
|
|
@@ -36297,7 +37093,7 @@ function discoverDistPluginRoot(root) {
|
|
|
36297
37093
|
}
|
|
36298
37094
|
function readManifest(manifestPath) {
|
|
36299
37095
|
try {
|
|
36300
|
-
const manifest = parseFleetPluginManifest(JSON.parse(
|
|
37096
|
+
const manifest = parseFleetPluginManifest(JSON.parse(fs14__default.readFileSync(manifestPath, "utf8")));
|
|
36301
37097
|
if (!manifest) console.warn(`[fleet-console] Plugin manifest skipped: invalid manifest at ${manifestPath}`);
|
|
36302
37098
|
return manifest;
|
|
36303
37099
|
} catch (error51) {
|
|
@@ -36311,11 +37107,11 @@ function resolveRoutesEntry(pluginRoot, manifest, builtInDistRoot) {
|
|
|
36311
37107
|
if (manifestRoute2 === false) return false;
|
|
36312
37108
|
if (manifestRoute2) return manifestRoute2;
|
|
36313
37109
|
}
|
|
36314
|
-
const manifestRoute =
|
|
36315
|
-
if (
|
|
37110
|
+
const manifestRoute = path27__default.join(pluginRoot, "routes.ts");
|
|
37111
|
+
if (fs14__default.existsSync(manifestRoute)) return manifestRoute;
|
|
36316
37112
|
if (!builtInDistRoot) return null;
|
|
36317
|
-
const builtRoute =
|
|
36318
|
-
return
|
|
37113
|
+
const builtRoute = path27__default.join(builtInDistRoot, manifest.id, "routes.mjs");
|
|
37114
|
+
return fs14__default.existsSync(builtRoute) ? builtRoute : null;
|
|
36319
37115
|
}
|
|
36320
37116
|
function resolveDistRoutesEntry(pluginRoot, manifest) {
|
|
36321
37117
|
const routesEntry = resolveOptionalManifestEntry(pluginRoot, manifest.routes ?? "routes.mjs", "routes");
|
|
@@ -36328,10 +37124,10 @@ function resolveOptionalManifestEntry(pluginRoot, entryPath, label) {
|
|
|
36328
37124
|
console.warn(`[fleet-console] Plugin ${label} entry skipped: unsafe relative path ${entryPath}`);
|
|
36329
37125
|
return false;
|
|
36330
37126
|
}
|
|
36331
|
-
const candidate =
|
|
36332
|
-
if (!
|
|
36333
|
-
const rootRealpath =
|
|
36334
|
-
const entryRealpath =
|
|
37127
|
+
const candidate = path27__default.resolve(pluginRoot, entryPath);
|
|
37128
|
+
if (!fs14__default.existsSync(candidate)) return null;
|
|
37129
|
+
const rootRealpath = fs14__default.realpathSync.native(pluginRoot);
|
|
37130
|
+
const entryRealpath = fs14__default.realpathSync.native(candidate);
|
|
36335
37131
|
if (!isSubpath2(rootRealpath, entryRealpath)) {
|
|
36336
37132
|
console.warn(`[fleet-console] Plugin ${label} entry skipped: path escapes plugin root ${entryPath}`);
|
|
36337
37133
|
return false;
|
|
@@ -36346,12 +37142,12 @@ function readDistPluginManifest(id) {
|
|
|
36346
37142
|
return pluginId ? { id: pluginId, routes: "routes.mjs" } : null;
|
|
36347
37143
|
}
|
|
36348
37144
|
function isSafeRelativeEntry(entryPath) {
|
|
36349
|
-
if (
|
|
37145
|
+
if (path27__default.isAbsolute(entryPath)) return false;
|
|
36350
37146
|
return entryPath.split(/[\\/]+/u).every((segment) => segment !== "..");
|
|
36351
37147
|
}
|
|
36352
37148
|
function isSubpath2(root, candidate) {
|
|
36353
|
-
const relative =
|
|
36354
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
37149
|
+
const relative = path27__default.relative(root, candidate);
|
|
37150
|
+
return relative === "" || !relative.startsWith("..") && !path27__default.isAbsolute(relative);
|
|
36355
37151
|
}
|
|
36356
37152
|
function isRecord9(value) {
|
|
36357
37153
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -36369,17 +37165,22 @@ var PLUGIN_DEV_EXTERNALS = [
|
|
|
36369
37165
|
"child_process",
|
|
36370
37166
|
"node-pty",
|
|
36371
37167
|
"ws",
|
|
36372
|
-
"@fleet-console/sdk",
|
|
36373
|
-
"@fleet-console/sdk/*",
|
|
36374
|
-
"@dotobokuri/*",
|
|
36375
37168
|
"@fleet-plugins/*"
|
|
36376
37169
|
];
|
|
36377
|
-
var
|
|
37170
|
+
var PLUGIN_BUNDLE_DIR_PREFIX = "fleet-console-plugin-";
|
|
37171
|
+
var PLUGIN_BUNDLE_OWNER_FILE = ".fleet-console-plugin-owner.json";
|
|
37172
|
+
var PLUGIN_BUNDLE_PID_PATTERN = /^fleet-console-plugin-(\d+)-/u;
|
|
37173
|
+
var MAX_PLUGIN_BUNDLE_OWNER_PID = 2147483647;
|
|
37174
|
+
var __dirname$1 = path27__default.dirname(fileURLToPath(import.meta.url));
|
|
36378
37175
|
function createFleetPluginHost(deps) {
|
|
36379
37176
|
const plugins = filterDiscoveredPlugins(discoverFleetPlugins(deps));
|
|
36380
37177
|
const sensitiveFieldsByPluginId = new Map(plugins.map((plugin) => [plugin.manifest.id, plugin.manifest.sensitiveFields ?? []]));
|
|
36381
|
-
const
|
|
37178
|
+
const generatedBundleDirs = /* @__PURE__ */ new Set();
|
|
37179
|
+
const isProcessAlive2 = deps.isProcessAlive ?? isPluginBundleOwnerAlive;
|
|
37180
|
+
const removeBundleDir = deps.removeBundleDir ?? removePluginBundleDir;
|
|
37181
|
+
const importModule = deps.importModule ?? ((entry) => importPluginModule(entry, deps.bundleCacheDir, generatedBundleDirs));
|
|
36382
37182
|
async function boot() {
|
|
37183
|
+
await removeStalePluginBundleDirs(deps.bundleCacheDir, isProcessAlive2, removeBundleDir);
|
|
36383
37184
|
for (const plugin of plugins) {
|
|
36384
37185
|
if (!plugin.routesEntry) continue;
|
|
36385
37186
|
if (!plugin.external) {
|
|
@@ -36393,7 +37194,21 @@ function createFleetPluginHost(deps) {
|
|
|
36393
37194
|
}
|
|
36394
37195
|
}
|
|
36395
37196
|
}
|
|
36396
|
-
|
|
37197
|
+
async function cleanup() {
|
|
37198
|
+
const dirs = [...generatedBundleDirs];
|
|
37199
|
+
const cleanupResults = await Promise.allSettled(dirs.map((dir) => removeBundleDir(dir)));
|
|
37200
|
+
for (const [index, result] of cleanupResults.entries()) {
|
|
37201
|
+
const dir = dirs[index];
|
|
37202
|
+
if (result.status === "fulfilled") {
|
|
37203
|
+
generatedBundleDirs.delete(dir);
|
|
37204
|
+
continue;
|
|
37205
|
+
}
|
|
37206
|
+
if (result.status === "rejected") {
|
|
37207
|
+
console.warn(`[fleet-console] Plugin bundle cache cleanup failed: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
37208
|
+
}
|
|
37209
|
+
}
|
|
37210
|
+
}
|
|
37211
|
+
return { plugins, sensitiveFieldsByPluginId, boot, cleanup };
|
|
36397
37212
|
async function bootPluginRoutes(plugin) {
|
|
36398
37213
|
const mod = await importModule(plugin.routesEntry);
|
|
36399
37214
|
const register = resolveRegister(mod);
|
|
@@ -36427,7 +37242,7 @@ function filterDiscoveredPlugins(plugins) {
|
|
|
36427
37242
|
}
|
|
36428
37243
|
return accepted;
|
|
36429
37244
|
}
|
|
36430
|
-
async function importPluginModule(entry) {
|
|
37245
|
+
async function importPluginModule(entry, bundleCacheDir, generatedBundleDirs) {
|
|
36431
37246
|
if (entry.endsWith(".ts")) {
|
|
36432
37247
|
const { build } = await import('esbuild');
|
|
36433
37248
|
const output = await build({
|
|
@@ -36438,49 +37253,101 @@ async function importPluginModule(entry) {
|
|
|
36438
37253
|
target: "node20",
|
|
36439
37254
|
write: false,
|
|
36440
37255
|
sourcemap: false,
|
|
36441
|
-
external: PLUGIN_DEV_EXTERNALS
|
|
37256
|
+
external: PLUGIN_DEV_EXTERNALS,
|
|
37257
|
+
nodePaths: [resolveConsolePackageNodeModules()].filter((value) => value !== null)
|
|
36442
37258
|
});
|
|
36443
37259
|
const bundled = output.outputFiles[0]?.text;
|
|
36444
37260
|
if (!bundled) throw new Error("plugin_bundle_failed");
|
|
36445
|
-
const bundledPath = await writeTemporaryPluginBundle(entry, bundled);
|
|
37261
|
+
const bundledPath = await writeTemporaryPluginBundle(entry, bundled, bundleCacheDir, generatedBundleDirs);
|
|
36446
37262
|
return await import(pathToFileURL(bundledPath).href);
|
|
36447
37263
|
}
|
|
36448
37264
|
return await import(pathToFileURL(entry).href);
|
|
36449
37265
|
}
|
|
36450
|
-
async function writeTemporaryPluginBundle(entry, source) {
|
|
36451
|
-
const cacheRoot =
|
|
37266
|
+
async function writeTemporaryPluginBundle(entry, source, bundleCacheDir, generatedBundleDirs) {
|
|
37267
|
+
const cacheRoot = bundleCacheDir ?? path27__default.join(resolvePluginBundleNodeModules(entry), ".cache");
|
|
36452
37268
|
await mkdir(cacheRoot, { recursive: true });
|
|
36453
|
-
const dir = await mkdtemp(
|
|
36454
|
-
|
|
37269
|
+
const dir = await mkdtemp(path27__default.join(cacheRoot, `${PLUGIN_BUNDLE_DIR_PREFIX}${process.pid}-`));
|
|
37270
|
+
if (bundleCacheDir) {
|
|
37271
|
+
generatedBundleDirs.add(dir);
|
|
37272
|
+
await writePluginBundleOwner(dir, process.pid);
|
|
37273
|
+
}
|
|
37274
|
+
const file2 = path27__default.join(dir, "routes.mjs");
|
|
36455
37275
|
await writeFile(file2, source, "utf8");
|
|
36456
37276
|
return file2;
|
|
36457
37277
|
}
|
|
37278
|
+
async function removeStalePluginBundleDirs(bundleCacheDir, isProcessAlive2, removeBundleDir) {
|
|
37279
|
+
if (!bundleCacheDir) return;
|
|
37280
|
+
try {
|
|
37281
|
+
await mkdir(bundleCacheDir, { recursive: true });
|
|
37282
|
+
const entries = await readdir(bundleCacheDir, { withFileTypes: true });
|
|
37283
|
+
for (const entry of entries) {
|
|
37284
|
+
if (!entry.name.startsWith(PLUGIN_BUNDLE_DIR_PREFIX)) continue;
|
|
37285
|
+
const entryPath = path27__default.join(bundleCacheDir, entry.name);
|
|
37286
|
+
const ownerPid = await readPluginBundleOwnerPid(entryPath, entry.name);
|
|
37287
|
+
if (ownerPid !== null && isProcessAlive2(ownerPid)) continue;
|
|
37288
|
+
try {
|
|
37289
|
+
await removeBundleDir(entryPath);
|
|
37290
|
+
} catch (error51) {
|
|
37291
|
+
console.warn(`[fleet-console] Plugin bundle cache startup cleanup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
37292
|
+
}
|
|
37293
|
+
}
|
|
37294
|
+
} catch (error51) {
|
|
37295
|
+
console.warn(`[fleet-console] Plugin bundle cache startup cleanup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
37296
|
+
}
|
|
37297
|
+
}
|
|
37298
|
+
async function removePluginBundleDir(dir) {
|
|
37299
|
+
await rm(dir, { recursive: true, force: true });
|
|
37300
|
+
}
|
|
37301
|
+
async function writePluginBundleOwner(dir, pid) {
|
|
37302
|
+
await writeFile(path27__default.join(dir, PLUGIN_BUNDLE_OWNER_FILE), JSON.stringify({ pid }), "utf8");
|
|
37303
|
+
}
|
|
37304
|
+
async function readPluginBundleOwnerPid(dir, name) {
|
|
37305
|
+
try {
|
|
37306
|
+
const parsed = JSON.parse(await readFile(path27__default.join(dir, PLUGIN_BUNDLE_OWNER_FILE), "utf8"));
|
|
37307
|
+
if (isPluginBundleOwnerPid(parsed.pid)) return parsed.pid;
|
|
37308
|
+
} catch {
|
|
37309
|
+
}
|
|
37310
|
+
const pid = Number(PLUGIN_BUNDLE_PID_PATTERN.exec(name)?.[1]);
|
|
37311
|
+
return isPluginBundleOwnerPid(pid) ? pid : null;
|
|
37312
|
+
}
|
|
37313
|
+
function isPluginBundleOwnerAlive(pid) {
|
|
37314
|
+
if (!isPluginBundleOwnerPid(pid)) return false;
|
|
37315
|
+
try {
|
|
37316
|
+
process.kill(pid, 0);
|
|
37317
|
+
return true;
|
|
37318
|
+
} catch (error51) {
|
|
37319
|
+
return Boolean(error51 && typeof error51 === "object" && "code" in error51 && error51.code === "EPERM");
|
|
37320
|
+
}
|
|
37321
|
+
}
|
|
37322
|
+
function isPluginBundleOwnerPid(value) {
|
|
37323
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 && value <= MAX_PLUGIN_BUNDLE_OWNER_PID;
|
|
37324
|
+
}
|
|
36458
37325
|
function resolvePluginBundleNodeModules(entry) {
|
|
36459
37326
|
const packageNodeModules = resolveConsolePackageNodeModules();
|
|
36460
|
-
let dir =
|
|
37327
|
+
let dir = path27__default.dirname(entry);
|
|
36461
37328
|
let fallback = packageNodeModules;
|
|
36462
37329
|
while (true) {
|
|
36463
|
-
const workspaceConsoleNodeModules =
|
|
37330
|
+
const workspaceConsoleNodeModules = path27__default.join(dir, "runtime", "fleet-console", "node_modules");
|
|
36464
37331
|
if (hasFleetConsoleSdk(workspaceConsoleNodeModules)) return workspaceConsoleNodeModules;
|
|
36465
|
-
const candidate =
|
|
37332
|
+
const candidate = path27__default.join(dir, "node_modules");
|
|
36466
37333
|
if (hasFleetConsoleSdk(candidate)) return candidate;
|
|
36467
37334
|
if (fallback === null && existsSync(candidate)) fallback = candidate;
|
|
36468
|
-
const parent =
|
|
36469
|
-
if (parent === dir) return fallback ??
|
|
37335
|
+
const parent = path27__default.dirname(dir);
|
|
37336
|
+
if (parent === dir) return fallback ?? path27__default.join(path27__default.dirname(entry), "node_modules");
|
|
36470
37337
|
dir = parent;
|
|
36471
37338
|
}
|
|
36472
37339
|
}
|
|
36473
37340
|
function resolveConsolePackageNodeModules() {
|
|
36474
37341
|
for (const candidate of [
|
|
36475
|
-
|
|
36476
|
-
|
|
37342
|
+
path27__default.resolve(__dirname$1, "..", "node_modules"),
|
|
37343
|
+
path27__default.resolve(__dirname$1, "..", "..", "..", "node_modules")
|
|
36477
37344
|
]) {
|
|
36478
37345
|
if (hasFleetConsoleSdk(candidate)) return candidate;
|
|
36479
37346
|
}
|
|
36480
37347
|
return null;
|
|
36481
37348
|
}
|
|
36482
37349
|
function hasFleetConsoleSdk(nodeModules) {
|
|
36483
|
-
return existsSync(
|
|
37350
|
+
return existsSync(path27__default.join(nodeModules, "@fleet-console", "sdk"));
|
|
36484
37351
|
}
|
|
36485
37352
|
function resolveRegister(mod) {
|
|
36486
37353
|
if (mod.register) return mod.register;
|
|
@@ -36526,6 +37393,8 @@ function normalizePrefix(prefix) {
|
|
|
36526
37393
|
var RELEASE_NOTE_HEADINGS = ["Added", "Changed", "Fixed", "Removed", "Breaking Changes"];
|
|
36527
37394
|
var VERSION_HEADER_PATTERN = /^## \[([^\]]+)\](?: - ([0-9]{4}-[0-9]{2}-[0-9]{2}))?$/;
|
|
36528
37395
|
var SECTION_HEADER_PATTERN = /^### (Added|Changed|Fixed|Removed|Breaking Changes)$/;
|
|
37396
|
+
var PRODUCT_HEADER_PATTERN = /^### fleet-(cli|console|desktop|plugin|core)$/;
|
|
37397
|
+
var PRODUCT_SECTION_HEADER_PATTERN = /^#### (Added|Changed|Fixed|Removed|Breaking Changes)$/;
|
|
36529
37398
|
var BULLET_PATTERN = /^- (.+)$/;
|
|
36530
37399
|
var PACKAGE_TAG_PATTERN = /^\[([^\]]+)\]/;
|
|
36531
37400
|
function parseConsoleReleaseNotes(changelog) {
|
|
@@ -36546,24 +37415,55 @@ function parseConsoleReleaseNotes(changelog) {
|
|
|
36546
37415
|
return notes;
|
|
36547
37416
|
}
|
|
36548
37417
|
function collectSections(lines) {
|
|
36549
|
-
const
|
|
37418
|
+
const legacySections = /* @__PURE__ */ new Map();
|
|
37419
|
+
const productSections = /* @__PURE__ */ new Map();
|
|
37420
|
+
const legacyHeadings = /* @__PURE__ */ new Set();
|
|
36550
37421
|
let current = null;
|
|
37422
|
+
let isProductContext = false;
|
|
36551
37423
|
for (const line of lines) {
|
|
36552
37424
|
const sectionMatch = SECTION_HEADER_PATTERN.exec(line);
|
|
36553
37425
|
if (sectionMatch !== null) {
|
|
36554
|
-
|
|
36555
|
-
|
|
37426
|
+
const heading = sectionMatch[1];
|
|
37427
|
+
isProductContext = false;
|
|
37428
|
+
current = legacyHeadings.has(heading) ? null : getSection(legacySections, heading);
|
|
37429
|
+
legacyHeadings.add(heading);
|
|
37430
|
+
continue;
|
|
37431
|
+
}
|
|
37432
|
+
if (PRODUCT_HEADER_PATTERN.test(line)) {
|
|
37433
|
+
isProductContext = true;
|
|
37434
|
+
current = null;
|
|
36556
37435
|
continue;
|
|
36557
37436
|
}
|
|
36558
|
-
if (current === null) continue;
|
|
36559
37437
|
if (line.startsWith("### ")) {
|
|
37438
|
+
isProductContext = false;
|
|
37439
|
+
current = null;
|
|
37440
|
+
continue;
|
|
37441
|
+
}
|
|
37442
|
+
const productSectionMatch = PRODUCT_SECTION_HEADER_PATTERN.exec(line);
|
|
37443
|
+
if (productSectionMatch !== null) {
|
|
37444
|
+
current = isProductContext ? getSection(productSections, productSectionMatch[1]) : null;
|
|
37445
|
+
continue;
|
|
37446
|
+
}
|
|
37447
|
+
if (current === null) continue;
|
|
37448
|
+
if (line.startsWith("### ") || line.startsWith("#### ")) {
|
|
36560
37449
|
current = null;
|
|
36561
37450
|
continue;
|
|
36562
37451
|
}
|
|
36563
37452
|
const bulletMatch = BULLET_PATTERN.exec(line);
|
|
36564
37453
|
if (bulletMatch !== null) current.items.push(parseReleaseNoteItem(bulletMatch[1] ?? ""));
|
|
36565
37454
|
}
|
|
36566
|
-
return RELEASE_NOTE_HEADINGS.map((heading) =>
|
|
37455
|
+
return RELEASE_NOTE_HEADINGS.map((heading) => combineSections(heading, legacySections.get(heading), productSections.get(heading))).filter((section2) => Boolean(section2 && section2.items.length > 0));
|
|
37456
|
+
}
|
|
37457
|
+
function combineSections(heading, legacySection, productSection) {
|
|
37458
|
+
if (legacySection === void 0 && productSection === void 0) return void 0;
|
|
37459
|
+
return { heading, items: [...legacySection?.items ?? [], ...productSection?.items ?? []] };
|
|
37460
|
+
}
|
|
37461
|
+
function getSection(sections, heading) {
|
|
37462
|
+
const existing = sections.get(heading);
|
|
37463
|
+
if (existing !== void 0) return existing;
|
|
37464
|
+
const section2 = { heading, items: [] };
|
|
37465
|
+
sections.set(heading, section2);
|
|
37466
|
+
return section2;
|
|
36567
37467
|
}
|
|
36568
37468
|
function parseReleaseNoteItem(rawText) {
|
|
36569
37469
|
const packageTags = [];
|
|
@@ -36589,10 +37489,11 @@ var ConsoleReleaseNotesUnavailableError = class extends Error {
|
|
|
36589
37489
|
|
|
36590
37490
|
// core/host/release-notes/service.ts
|
|
36591
37491
|
var RAW_CHANGELOG_URL = "https://raw.githubusercontent.com/sbluemin/fleet-harness/main/CHANGELOG.md";
|
|
37492
|
+
var RAW_KOREAN_CHANGELOG_URL = "https://raw.githubusercontent.com/sbluemin/fleet-harness/main/CHANGELOG.ko.md";
|
|
36592
37493
|
var SOURCE_REF = "main";
|
|
36593
37494
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
36594
37495
|
var MAX_CHANGELOG_BYTES = 1024 * 1024;
|
|
36595
|
-
var
|
|
37496
|
+
var SUCCESS_TTL_MS2 = 60 * 60 * 1e3;
|
|
36596
37497
|
var NEGATIVE_TTL_MS = 30 * 1e3;
|
|
36597
37498
|
function createConsoleReleaseNotesService(deps = {}) {
|
|
36598
37499
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -36603,30 +37504,46 @@ function createConsoleReleaseNotesService(deps = {}) {
|
|
|
36603
37504
|
let lastFailureAt = 0;
|
|
36604
37505
|
let inFlight = null;
|
|
36605
37506
|
let forceInFlight = null;
|
|
37507
|
+
let koreanLastSuccess = null;
|
|
37508
|
+
let koreanLastFailureAt = 0;
|
|
37509
|
+
let koreanInFlight = null;
|
|
37510
|
+
let koreanForceInFlight = null;
|
|
37511
|
+
let koreanRequestGeneration = 0;
|
|
36606
37512
|
async function refresh(options = {}) {
|
|
37513
|
+
if (options.locale === "ko") return await refreshKoreanOverlay(options);
|
|
37514
|
+
return await refreshEnglish(options);
|
|
37515
|
+
}
|
|
37516
|
+
async function refreshEnglish(options) {
|
|
36607
37517
|
const currentTime = now();
|
|
36608
37518
|
if (options.force) {
|
|
36609
|
-
if (forceInFlight) return forceInFlight;
|
|
37519
|
+
if (forceInFlight) return await forceInFlight;
|
|
36610
37520
|
const pending2 = runFetch().finally(() => {
|
|
36611
37521
|
if (forceInFlight === pending2) forceInFlight = null;
|
|
36612
37522
|
});
|
|
36613
37523
|
forceInFlight = pending2;
|
|
36614
|
-
return pending2;
|
|
37524
|
+
return await pending2;
|
|
36615
37525
|
}
|
|
36616
|
-
if (lastSuccess && currentTime - lastSuccess.fetchedAt <
|
|
37526
|
+
if (lastSuccess && currentTime - lastSuccess.fetchedAt < SUCCESS_TTL_MS2) return lastSuccess;
|
|
36617
37527
|
if (lastFailureAt > 0 && currentTime - lastFailureAt < NEGATIVE_TTL_MS) {
|
|
36618
37528
|
if (lastSuccess) return { ...lastSuccess, stale: true };
|
|
36619
37529
|
throw new ConsoleReleaseNotesUnavailableError("negative_cache");
|
|
36620
37530
|
}
|
|
36621
|
-
if (inFlight) return inFlight;
|
|
37531
|
+
if (inFlight) return await inFlight;
|
|
36622
37532
|
const pending = runFetch().finally(() => {
|
|
36623
37533
|
if (inFlight === pending) inFlight = null;
|
|
36624
37534
|
});
|
|
36625
37535
|
inFlight = pending;
|
|
36626
|
-
return pending;
|
|
37536
|
+
return await pending;
|
|
37537
|
+
}
|
|
37538
|
+
async function refreshKoreanOverlay(options) {
|
|
37539
|
+
const english = refreshEnglish(options);
|
|
37540
|
+
const korean = refreshKorean(options).catch(() => null);
|
|
37541
|
+
const canonical = await english;
|
|
37542
|
+
const localized = await korean;
|
|
37543
|
+
return mergeKoreanOverlay(canonical, localized?.notes ?? null);
|
|
36627
37544
|
}
|
|
36628
37545
|
function runFetch() {
|
|
36629
|
-
return
|
|
37546
|
+
return fetchEnglishReleaseNotes().then((result) => {
|
|
36630
37547
|
lastSuccess = result;
|
|
36631
37548
|
lastFailureAt = 0;
|
|
36632
37549
|
return result;
|
|
@@ -36637,29 +37554,93 @@ function createConsoleReleaseNotesService(deps = {}) {
|
|
|
36637
37554
|
throw new ConsoleReleaseNotesUnavailableError("cold_unavailable");
|
|
36638
37555
|
});
|
|
36639
37556
|
}
|
|
36640
|
-
async function
|
|
37557
|
+
async function fetchEnglishReleaseNotes() {
|
|
37558
|
+
return {
|
|
37559
|
+
notes: await fetchAndParse(RAW_CHANGELOG_URL),
|
|
37560
|
+
sourceRef: SOURCE_REF,
|
|
37561
|
+
fetchedAt: now(),
|
|
37562
|
+
stale: false
|
|
37563
|
+
};
|
|
37564
|
+
}
|
|
37565
|
+
async function refreshKorean(options) {
|
|
37566
|
+
const currentTime = now();
|
|
37567
|
+
if (options.force) {
|
|
37568
|
+
if (koreanForceInFlight) return await koreanForceInFlight;
|
|
37569
|
+
const pending2 = runKoreanFetch(++koreanRequestGeneration).finally(() => {
|
|
37570
|
+
if (koreanForceInFlight === pending2) koreanForceInFlight = null;
|
|
37571
|
+
});
|
|
37572
|
+
koreanForceInFlight = pending2;
|
|
37573
|
+
return await pending2;
|
|
37574
|
+
}
|
|
37575
|
+
if (koreanLastSuccess && currentTime - koreanLastSuccess.fetchedAt < SUCCESS_TTL_MS2) return koreanLastSuccess;
|
|
37576
|
+
if (koreanLastFailureAt > 0 && currentTime - koreanLastFailureAt < NEGATIVE_TTL_MS) return null;
|
|
37577
|
+
if (koreanInFlight) return await koreanInFlight;
|
|
37578
|
+
const pending = runKoreanFetch(++koreanRequestGeneration).finally(() => {
|
|
37579
|
+
if (koreanInFlight === pending) koreanInFlight = null;
|
|
37580
|
+
});
|
|
37581
|
+
koreanInFlight = pending;
|
|
37582
|
+
return await pending;
|
|
37583
|
+
}
|
|
37584
|
+
function runKoreanFetch(requestGeneration) {
|
|
37585
|
+
return fetchAndParse(RAW_KOREAN_CHANGELOG_URL).then((notes) => {
|
|
37586
|
+
const result = { notes, fetchedAt: now() };
|
|
37587
|
+
if (requestGeneration === koreanRequestGeneration) {
|
|
37588
|
+
koreanLastSuccess = result;
|
|
37589
|
+
koreanLastFailureAt = 0;
|
|
37590
|
+
}
|
|
37591
|
+
return result;
|
|
37592
|
+
}).catch(() => {
|
|
37593
|
+
if (requestGeneration === koreanRequestGeneration) koreanLastFailureAt = now();
|
|
37594
|
+
return null;
|
|
37595
|
+
});
|
|
37596
|
+
}
|
|
37597
|
+
async function fetchAndParse(url2) {
|
|
36641
37598
|
const controller = new AbortController();
|
|
36642
37599
|
const timer = setTimer(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
36643
37600
|
timer.unref?.();
|
|
36644
37601
|
try {
|
|
36645
|
-
const response = await fetchImpl(
|
|
37602
|
+
const response = await fetchImpl(url2, {
|
|
36646
37603
|
headers: { Accept: "text/plain; charset=utf-8" },
|
|
36647
37604
|
signal: controller.signal
|
|
36648
37605
|
});
|
|
36649
37606
|
if (!response.ok) throw new ConsoleReleaseNotesUnavailableError("cold_unavailable");
|
|
36650
37607
|
const text = await readTextWithByteLimit(response, controller);
|
|
36651
|
-
return {
|
|
36652
|
-
notes: parseConsoleReleaseNotes(text),
|
|
36653
|
-
sourceRef: SOURCE_REF,
|
|
36654
|
-
fetchedAt: now(),
|
|
36655
|
-
stale: false
|
|
36656
|
-
};
|
|
37608
|
+
return parseConsoleReleaseNotes(text).map((note) => ({ ...note, localizationFallback: false }));
|
|
36657
37609
|
} finally {
|
|
36658
37610
|
clearTimer(timer);
|
|
36659
37611
|
}
|
|
36660
37612
|
}
|
|
36661
37613
|
return { refresh };
|
|
36662
37614
|
}
|
|
37615
|
+
function mergeKoreanOverlay(canonical, koreanNotes) {
|
|
37616
|
+
if (koreanNotes === null) return { ...canonical, notes: canonical.notes.map(withFallback) };
|
|
37617
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
37618
|
+
for (const note of koreanNotes) {
|
|
37619
|
+
const queue = occurrences.get(note.version) ?? [];
|
|
37620
|
+
queue.push(note);
|
|
37621
|
+
occurrences.set(note.version, queue);
|
|
37622
|
+
}
|
|
37623
|
+
return {
|
|
37624
|
+
...canonical,
|
|
37625
|
+
notes: canonical.notes.map((english) => {
|
|
37626
|
+
const korean = occurrences.get(english.version)?.shift();
|
|
37627
|
+
return korean && hasMatchingStructure(english, korean) ? { ...english, sections: korean.sections, localizationFallback: false } : withFallback(english);
|
|
37628
|
+
})
|
|
37629
|
+
};
|
|
37630
|
+
}
|
|
37631
|
+
function withFallback(note) {
|
|
37632
|
+
return { ...note, localizationFallback: true };
|
|
37633
|
+
}
|
|
37634
|
+
function hasMatchingStructure(english, korean) {
|
|
37635
|
+
return english.date === korean.date && english.sections.length === korean.sections.length && english.sections.every((section2, sectionIndex) => {
|
|
37636
|
+
const localizedSection = korean.sections[sectionIndex];
|
|
37637
|
+
return localizedSection?.heading === section2.heading && localizedSection.items.length === section2.items.length && section2.items.every((item, itemIndex) => sameTags(item.packageTags, localizedSection.items[itemIndex]?.packageTags));
|
|
37638
|
+
});
|
|
37639
|
+
}
|
|
37640
|
+
function sameTags(left, right) {
|
|
37641
|
+
if (right === void 0) return false;
|
|
37642
|
+
return left.length === right.length && left.every((tag, index) => tag === right[index]);
|
|
37643
|
+
}
|
|
36663
37644
|
async function readTextWithByteLimit(response, controller) {
|
|
36664
37645
|
const body = response.body;
|
|
36665
37646
|
if (body === null) return await response.text();
|
|
@@ -36756,7 +37737,7 @@ var MIME_TYPES = {
|
|
|
36756
37737
|
".woff2": "font/woff2"
|
|
36757
37738
|
};
|
|
36758
37739
|
function createStaticConsoleHandler(packageRoot) {
|
|
36759
|
-
const consoleRoot =
|
|
37740
|
+
const consoleRoot = path27__default.join(packageRoot, "dist", "client");
|
|
36760
37741
|
return (req, res, pathname) => tryServeStaticConsole(req, res, pathname, consoleRoot);
|
|
36761
37742
|
}
|
|
36762
37743
|
function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
@@ -36772,15 +37753,15 @@ function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
|
36772
37753
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
36773
37754
|
return true;
|
|
36774
37755
|
}
|
|
36775
|
-
const absolutePath =
|
|
37756
|
+
const absolutePath = path27__default.resolve(consoleRoot, relativePath2);
|
|
36776
37757
|
if (!absolutePath.startsWith(consoleRoot)) {
|
|
36777
37758
|
res.writeHead(404, withSecurityHeaders2({ "Content-Type": "application/json" }));
|
|
36778
37759
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
36779
37760
|
return true;
|
|
36780
37761
|
}
|
|
36781
37762
|
try {
|
|
36782
|
-
const data =
|
|
36783
|
-
const contentType = MIME_TYPES[
|
|
37763
|
+
const data = fs14__default.readFileSync(absolutePath);
|
|
37764
|
+
const contentType = MIME_TYPES[path27__default.extname(absolutePath)] ?? "application/octet-stream";
|
|
36784
37765
|
res.writeHead(200, withSecurityHeaders2({ "Content-Type": contentType }));
|
|
36785
37766
|
if (req.method === "HEAD") {
|
|
36786
37767
|
res.end();
|
|
@@ -36800,7 +37781,7 @@ function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
|
36800
37781
|
}
|
|
36801
37782
|
function serveFallbackIndex(req, res, consoleRoot) {
|
|
36802
37783
|
try {
|
|
36803
|
-
const data =
|
|
37784
|
+
const data = fs14__default.readFileSync(path27__default.join(consoleRoot, "index.html"));
|
|
36804
37785
|
res.writeHead(200, withSecurityHeaders2({ "Content-Type": MIME_TYPES[".html"] }));
|
|
36805
37786
|
if (req.method === "HEAD") {
|
|
36806
37787
|
res.end();
|
|
@@ -36826,7 +37807,7 @@ function resolveConsolePath(pathname) {
|
|
|
36826
37807
|
}
|
|
36827
37808
|
if (withoutPrefix === "") return "index.html";
|
|
36828
37809
|
if (withoutPrefix.includes("\0")) return null;
|
|
36829
|
-
if (!
|
|
37810
|
+
if (!path27__default.extname(withoutPrefix)) return "index.html";
|
|
36830
37811
|
return withoutPrefix;
|
|
36831
37812
|
}
|
|
36832
37813
|
var TheaterFolderListError = class extends Error {
|
|
@@ -36837,12 +37818,12 @@ var TheaterFolderListError = class extends Error {
|
|
|
36837
37818
|
this.code = code;
|
|
36838
37819
|
}
|
|
36839
37820
|
};
|
|
36840
|
-
var
|
|
37821
|
+
var DIRECTORY_ENTRY_CAP2 = 500;
|
|
36841
37822
|
var WINDOWS_DRIVE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
36842
37823
|
async function listTheaterFolders(requestedPath, deps = {}) {
|
|
36843
37824
|
const platform = deps.platform ?? process.platform;
|
|
36844
|
-
const stat5 = deps.stat ??
|
|
36845
|
-
const opendir = deps.opendir ??
|
|
37825
|
+
const stat5 = deps.stat ?? fs14__default.promises.stat;
|
|
37826
|
+
const opendir = deps.opendir ?? fs14__default.promises.opendir;
|
|
36846
37827
|
const targetPath = normalizeListPath(requestedPath, platform, deps);
|
|
36847
37828
|
const roots = await listRoots(platform, stat5);
|
|
36848
37829
|
const targetStat = await statDirectory(targetPath, stat5);
|
|
@@ -36860,14 +37841,14 @@ async function listTheaterFolders(requestedPath, deps = {}) {
|
|
|
36860
37841
|
}
|
|
36861
37842
|
function normalizeFolderBrowserPath(value, platform = process.platform) {
|
|
36862
37843
|
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) throw new TheaterFolderListError("invalid_path");
|
|
36863
|
-
if (isWindowsAmbiguousPath(value, platform) || !
|
|
36864
|
-
return
|
|
37844
|
+
if (isWindowsAmbiguousPath(value, platform) || !path27__default.isAbsolute(value)) throw new TheaterFolderListError("invalid_path");
|
|
37845
|
+
return path27__default.resolve(value);
|
|
36865
37846
|
}
|
|
36866
37847
|
function normalizeListPath(requestedPath, platform, deps) {
|
|
36867
37848
|
if (requestedPath === null || requestedPath === void 0) {
|
|
36868
37849
|
const home = deps.homedir?.() ?? os4__default.homedir();
|
|
36869
37850
|
const start = home || deps.cwd?.() || process.cwd();
|
|
36870
|
-
return
|
|
37851
|
+
return path27__default.resolve(start);
|
|
36871
37852
|
}
|
|
36872
37853
|
return normalizeFolderBrowserPath(requestedPath, platform);
|
|
36873
37854
|
}
|
|
@@ -36887,7 +37868,7 @@ async function statDirectory(targetPath, stat5) {
|
|
|
36887
37868
|
try {
|
|
36888
37869
|
return await stat5(targetPath);
|
|
36889
37870
|
} catch (error51) {
|
|
36890
|
-
throw
|
|
37871
|
+
throw mapFsError2(error51);
|
|
36891
37872
|
}
|
|
36892
37873
|
}
|
|
36893
37874
|
async function collectDirectoryEntries(targetPath, opendir, stat5, entries) {
|
|
@@ -36897,14 +37878,14 @@ async function collectDirectoryEntries(targetPath, opendir, stat5, entries) {
|
|
|
36897
37878
|
while (dirent !== null) {
|
|
36898
37879
|
const entry = await toTheaterFolderEntry(targetPath, dirent, stat5);
|
|
36899
37880
|
if (entry !== null) {
|
|
36900
|
-
if (entries.length >=
|
|
37881
|
+
if (entries.length >= DIRECTORY_ENTRY_CAP2) return true;
|
|
36901
37882
|
entries.push(entry);
|
|
36902
37883
|
}
|
|
36903
37884
|
dirent = await directory.read();
|
|
36904
37885
|
}
|
|
36905
37886
|
return false;
|
|
36906
37887
|
} catch (error51) {
|
|
36907
|
-
throw
|
|
37888
|
+
throw mapFsError2(error51);
|
|
36908
37889
|
} finally {
|
|
36909
37890
|
await directory.close();
|
|
36910
37891
|
}
|
|
@@ -36913,12 +37894,12 @@ async function openDirectory(targetPath, opendir) {
|
|
|
36913
37894
|
try {
|
|
36914
37895
|
return await opendir(targetPath);
|
|
36915
37896
|
} catch (error51) {
|
|
36916
|
-
throw
|
|
37897
|
+
throw mapFsError2(error51);
|
|
36917
37898
|
}
|
|
36918
37899
|
}
|
|
36919
37900
|
async function toTheaterFolderEntry(targetPath, dirent, stat5) {
|
|
36920
37901
|
if (!dirent.isDirectory() && !dirent.isSymbolicLink()) return null;
|
|
36921
|
-
const entryPath =
|
|
37902
|
+
const entryPath = path27__default.join(targetPath, dirent.name);
|
|
36922
37903
|
if (dirent.isDirectory()) return { name: dirent.name, path: entryPath, kind: "dir", accessible: true };
|
|
36923
37904
|
const accessible = await statSymlinkDirectory(entryPath, stat5);
|
|
36924
37905
|
if (accessible === null) return null;
|
|
@@ -36931,17 +37912,17 @@ async function statSymlinkDirectory(entryPath, stat5) {
|
|
|
36931
37912
|
return false;
|
|
36932
37913
|
}
|
|
36933
37914
|
}
|
|
36934
|
-
function
|
|
37915
|
+
function mapFsError2(error51) {
|
|
36935
37916
|
const code = error51.code;
|
|
36936
37917
|
if (code === "EACCES" || code === "EPERM") return new TheaterFolderListError("forbidden");
|
|
36937
37918
|
if (code === "ENOENT" || code === "ENOTDIR") return new TheaterFolderListError("not_found");
|
|
36938
37919
|
return new TheaterFolderListError("invalid_path");
|
|
36939
37920
|
}
|
|
36940
37921
|
function parentPath(targetPath, platform) {
|
|
36941
|
-
const parsed =
|
|
36942
|
-
const resolved =
|
|
36943
|
-
if (resolved ===
|
|
36944
|
-
const parent =
|
|
37922
|
+
const parsed = path27__default.parse(targetPath);
|
|
37923
|
+
const resolved = path27__default.resolve(targetPath);
|
|
37924
|
+
if (resolved === path27__default.resolve(parsed.root)) return null;
|
|
37925
|
+
const parent = path27__default.dirname(resolved);
|
|
36945
37926
|
if (platform === "win32" && parent === resolved) return null;
|
|
36946
37927
|
return parent;
|
|
36947
37928
|
}
|
|
@@ -36953,7 +37934,7 @@ var GRANT_ID_BYTES = 24;
|
|
|
36953
37934
|
var DEFAULT_GRANT_TTL_MS = 6e4;
|
|
36954
37935
|
function createFolderGrantStore(deps = {}) {
|
|
36955
37936
|
const randomId = deps.randomId ?? (() => crypto5.randomBytes(GRANT_ID_BYTES).toString("base64url"));
|
|
36956
|
-
const statSync2 = deps.statSync ??
|
|
37937
|
+
const statSync2 = deps.statSync ?? fs14__default.statSync;
|
|
36957
37938
|
const ttlMs = deps.ttlMs ?? DEFAULT_GRANT_TTL_MS;
|
|
36958
37939
|
const now = deps.now ?? Date.now;
|
|
36959
37940
|
const grants = /* @__PURE__ */ new Map();
|
|
@@ -36979,10 +37960,10 @@ function createFolderGrantStore(deps = {}) {
|
|
|
36979
37960
|
}
|
|
36980
37961
|
return { issue: issue2, consume };
|
|
36981
37962
|
}
|
|
36982
|
-
function validateAbsoluteDirectory(cwd, statSync2 =
|
|
37963
|
+
function validateAbsoluteDirectory(cwd, statSync2 = fs14__default.statSync) {
|
|
36983
37964
|
if (typeof cwd !== "string" || cwd.length === 0 || cwd.includes("\0")) throw new Error("invalid_folder");
|
|
36984
|
-
if (isWindowsAmbiguousPath2(cwd) || !
|
|
36985
|
-
const normalized =
|
|
37965
|
+
if (isWindowsAmbiguousPath2(cwd) || !path27__default.isAbsolute(cwd)) throw new Error("invalid_folder");
|
|
37966
|
+
const normalized = path27__default.resolve(cwd);
|
|
36986
37967
|
if (!statSync2(normalized).isDirectory()) throw new Error("invalid_folder");
|
|
36987
37968
|
return normalized;
|
|
36988
37969
|
}
|
|
@@ -36990,11 +37971,178 @@ function isWindowsAmbiguousPath2(value) {
|
|
|
36990
37971
|
if (process.platform !== "win32") return false;
|
|
36991
37972
|
return /^[a-zA-Z]:(?![\\/])/.test(value) || /^[\\/](?![\\/])/.test(value);
|
|
36992
37973
|
}
|
|
37974
|
+
var execFileAsync = promisify(execFile);
|
|
37975
|
+
var GIT_TIMEOUT_MS = 3e3;
|
|
37976
|
+
var GIT_MAX_BUFFER = 1e6;
|
|
37977
|
+
async function listTheaterWorktrees(theaterRoot, deps = {}) {
|
|
37978
|
+
const exec = deps.execFile ?? ((file2, args, options) => execFileAsync(file2, args, options));
|
|
37979
|
+
const realpath2 = deps.realpath ?? fs14__default.promises.realpath;
|
|
37980
|
+
let stdout;
|
|
37981
|
+
try {
|
|
37982
|
+
({ stdout } = await exec("git", ["worktree", "list", "--porcelain"], { cwd: theaterRoot, env: { ...process.env, LC_ALL: "C" }, shell: false, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER }));
|
|
37983
|
+
} catch (error51) {
|
|
37984
|
+
if (isNotGitError(error51)) return { isGitRepo: false, worktrees: [] };
|
|
37985
|
+
throw error51;
|
|
37986
|
+
}
|
|
37987
|
+
const realRoot = await realpath2(theaterRoot);
|
|
37988
|
+
const records = parseGitWorktreePorcelain(stdout);
|
|
37989
|
+
const worktrees = [];
|
|
37990
|
+
for (const record2 of records) {
|
|
37991
|
+
try {
|
|
37992
|
+
const real = await realpath2(record2.worktree);
|
|
37993
|
+
if (!isWithinRoot3(real, realRoot) || real === realRoot) continue;
|
|
37994
|
+
worktrees.push({ relPath: path27__default.relative(realRoot, real).split(path27__default.sep).join("/"), branch: record2.branch, isCurrent: real === realRoot });
|
|
37995
|
+
} catch {
|
|
37996
|
+
}
|
|
37997
|
+
}
|
|
37998
|
+
return { isGitRepo: true, worktrees };
|
|
37999
|
+
}
|
|
38000
|
+
function parseGitWorktreePorcelain(value) {
|
|
38001
|
+
const records = [];
|
|
38002
|
+
for (const block of value.split("\n\n")) {
|
|
38003
|
+
const fields = block.split("\n");
|
|
38004
|
+
const worktree = fields.find((field) => field.startsWith("worktree "))?.slice("worktree ".length);
|
|
38005
|
+
if (!worktree) continue;
|
|
38006
|
+
const branchRef = fields.find((field) => field.startsWith("branch "))?.slice("branch ".length) ?? null;
|
|
38007
|
+
records.push({ worktree, branch: branchRef?.replace(/^refs\/heads\//, "") ?? null, detached: fields.includes("detached") });
|
|
38008
|
+
}
|
|
38009
|
+
return records;
|
|
38010
|
+
}
|
|
38011
|
+
function isWithinRoot3(candidate, root) {
|
|
38012
|
+
const normalizedRoot = root.endsWith(path27__default.sep) ? root : `${root}${path27__default.sep}`;
|
|
38013
|
+
return candidate === root || candidate.startsWith(normalizedRoot);
|
|
38014
|
+
}
|
|
38015
|
+
function isNotGitError(error51) {
|
|
38016
|
+
const value = error51;
|
|
38017
|
+
const text = `${typeof value.stderr === "string" ? value.stderr : ""}
|
|
38018
|
+
${typeof value.message === "string" ? value.message : ""}`;
|
|
38019
|
+
return /not a git repository/i.test(text);
|
|
38020
|
+
}
|
|
38021
|
+
|
|
38022
|
+
// core/host/theater-path-context-routes.ts
|
|
38023
|
+
function createTheaterPathContextRouter(deps) {
|
|
38024
|
+
return async function handleTheaterPathContextRoute({ req, res, pathname }) {
|
|
38025
|
+
const match = pathname.match(/^\/api\/v1\/theaters\/([^/]+)\/path-context(?:\/(worktrees|directories))?$/u);
|
|
38026
|
+
if (!match) return false;
|
|
38027
|
+
const theater = deps.getTheater(decodeURIComponent(match[1] ?? ""));
|
|
38028
|
+
if (!theater) {
|
|
38029
|
+
deps.writeJson(res, 400, { error: "invalid_theater" });
|
|
38030
|
+
return true;
|
|
38031
|
+
}
|
|
38032
|
+
const action = match[2] ?? "context";
|
|
38033
|
+
if (action === "context") await handleContext(req, res, theater, deps);
|
|
38034
|
+
else if (action === "worktrees") await handleWorktrees(req, res, theater, deps);
|
|
38035
|
+
else await handleDirectories(req, res, theater, deps);
|
|
38036
|
+
return true;
|
|
38037
|
+
};
|
|
38038
|
+
}
|
|
38039
|
+
async function handleContext(req, res, theater, deps) {
|
|
38040
|
+
if (req.method === "GET") {
|
|
38041
|
+
await writeContext(res, theater, deps);
|
|
38042
|
+
return;
|
|
38043
|
+
}
|
|
38044
|
+
if (req.method !== "PUT") {
|
|
38045
|
+
deps.writeJson(res, 405, { error: "method_not_allowed" });
|
|
38046
|
+
return;
|
|
38047
|
+
}
|
|
38048
|
+
if (!deps.isAuthorized(req)) {
|
|
38049
|
+
deps.writeJson(res, 401, { error: "unauthorized" });
|
|
38050
|
+
return;
|
|
38051
|
+
}
|
|
38052
|
+
const body = await deps.readJsonBody(req);
|
|
38053
|
+
if (!isPlainObject3(body) || !(body.relPath === null || typeof body.relPath === "string") || Object.keys(body).length !== 1) {
|
|
38054
|
+
deps.writeJson(res, 400, { error: "invalid_request" });
|
|
38055
|
+
return;
|
|
38056
|
+
}
|
|
38057
|
+
try {
|
|
38058
|
+
const resolved = await resolveTheaterPathContext(theater.realpath, body.relPath);
|
|
38059
|
+
const updated = deps.setPathContext(theater.id, resolved.relPath);
|
|
38060
|
+
if (!updated) {
|
|
38061
|
+
deps.writeJson(res, 404, { error: "theater_not_found" });
|
|
38062
|
+
return;
|
|
38063
|
+
}
|
|
38064
|
+
deps.persist();
|
|
38065
|
+
deps.writeJson(res, 200, await toContextDto(updated, deps));
|
|
38066
|
+
} catch (error51) {
|
|
38067
|
+
writePathError2(res, deps, error51);
|
|
38068
|
+
}
|
|
38069
|
+
}
|
|
38070
|
+
async function handleWorktrees(req, res, theater, deps) {
|
|
38071
|
+
if (req.method !== "GET") {
|
|
38072
|
+
deps.writeJson(res, 405, { error: "method_not_allowed" });
|
|
38073
|
+
return;
|
|
38074
|
+
}
|
|
38075
|
+
try {
|
|
38076
|
+
deps.writeJson(res, 200, await listTheaterWorktrees(theater.realpath));
|
|
38077
|
+
} catch {
|
|
38078
|
+
deps.writeJson(res, 500, { error: "worktree_discovery_failed" });
|
|
38079
|
+
}
|
|
38080
|
+
}
|
|
38081
|
+
async function handleDirectories(req, res, theater, deps) {
|
|
38082
|
+
if (req.method !== "POST") {
|
|
38083
|
+
deps.writeJson(res, 405, { error: "method_not_allowed" });
|
|
38084
|
+
return;
|
|
38085
|
+
}
|
|
38086
|
+
if (!deps.isAuthorized(req)) {
|
|
38087
|
+
deps.writeJson(res, 401, { error: "unauthorized" });
|
|
38088
|
+
return;
|
|
38089
|
+
}
|
|
38090
|
+
const body = await deps.readJsonBody(req);
|
|
38091
|
+
if (!isPlainObject3(body) || !(body.relativePath === null || typeof body.relativePath === "string") || Object.keys(body).length !== 1) {
|
|
38092
|
+
deps.writeJson(res, 400, { error: "invalid_request" });
|
|
38093
|
+
return;
|
|
38094
|
+
}
|
|
38095
|
+
try {
|
|
38096
|
+
deps.writeJson(res, 200, { directories: await listTheaterDirectories(theater.realpath, body.relativePath) });
|
|
38097
|
+
} catch (error51) {
|
|
38098
|
+
writePathError2(res, deps, error51);
|
|
38099
|
+
}
|
|
38100
|
+
}
|
|
38101
|
+
async function writeContext(res, theater, deps) {
|
|
38102
|
+
try {
|
|
38103
|
+
deps.writeJson(res, 200, await toContextDto(theater, deps));
|
|
38104
|
+
} catch (error51) {
|
|
38105
|
+
if (error51 instanceof TheaterPathContextError && theater.pathContext !== null) {
|
|
38106
|
+
const healed = deps.setPathContext(theater.id, null);
|
|
38107
|
+
if (healed) {
|
|
38108
|
+
deps.persist();
|
|
38109
|
+
try {
|
|
38110
|
+
deps.writeJson(res, 200, await toContextDto(healed, deps));
|
|
38111
|
+
return;
|
|
38112
|
+
} catch (rootError) {
|
|
38113
|
+
writePathError2(res, deps, rootError);
|
|
38114
|
+
return;
|
|
38115
|
+
}
|
|
38116
|
+
}
|
|
38117
|
+
}
|
|
38118
|
+
writePathError2(res, deps, error51);
|
|
38119
|
+
}
|
|
38120
|
+
}
|
|
38121
|
+
async function toContextDto(theater, _deps) {
|
|
38122
|
+
const resolved = await resolveTheaterPathContext(theater.realpath, theater.pathContext);
|
|
38123
|
+
let kind = resolved.relPath === null ? "root" : "directory";
|
|
38124
|
+
if (resolved.relPath !== null) {
|
|
38125
|
+
try {
|
|
38126
|
+
const worktrees = await listTheaterWorktrees(theater.realpath);
|
|
38127
|
+
if (worktrees.worktrees.some((worktree) => worktree.relPath === resolved.relPath)) kind = "worktree";
|
|
38128
|
+
} catch {
|
|
38129
|
+
}
|
|
38130
|
+
}
|
|
38131
|
+
return { kind, relPath: resolved.relPath, label: resolved.relPath === null ? theater.label : resolved.label };
|
|
38132
|
+
}
|
|
38133
|
+
function writePathError2(res, deps, error51) {
|
|
38134
|
+
if (!(error51 instanceof TheaterPathContextError)) throw error51;
|
|
38135
|
+
const status = error51.code === "not_found" ? 404 : error51.code === "forbidden" ? 403 : 400;
|
|
38136
|
+
deps.writeJson(res, status, { error: error51.code });
|
|
38137
|
+
}
|
|
38138
|
+
function isPlainObject3(value) {
|
|
38139
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38140
|
+
}
|
|
36993
38141
|
var TheaterRegistry = class {
|
|
36994
38142
|
#items = /* @__PURE__ */ new Map();
|
|
36995
38143
|
#mruId = null;
|
|
36996
38144
|
async register(cwd) {
|
|
36997
|
-
const resolved =
|
|
38145
|
+
const resolved = path27__default.resolve(cwd);
|
|
36998
38146
|
const real = await canonicalizeTheaterPath(resolved);
|
|
36999
38147
|
const id = workspaceHash(real);
|
|
37000
38148
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -37008,7 +38156,8 @@ var TheaterRegistry = class {
|
|
|
37008
38156
|
realpath: real,
|
|
37009
38157
|
label: theaterLabel(resolved),
|
|
37010
38158
|
registeredAt: existing?.registeredAt ?? now,
|
|
37011
|
-
lastOpenedAt: now
|
|
38159
|
+
lastOpenedAt: now,
|
|
38160
|
+
pathContext: existing?.pathContext ?? null
|
|
37012
38161
|
};
|
|
37013
38162
|
this.#items.set(id, item);
|
|
37014
38163
|
this.#mruId = id;
|
|
@@ -37048,6 +38197,13 @@ var TheaterRegistry = class {
|
|
|
37048
38197
|
if (this.#mruId === id) this.#mruId = this.list()[0]?.id ?? null;
|
|
37049
38198
|
return removed;
|
|
37050
38199
|
}
|
|
38200
|
+
setPathContext(id, pathContext) {
|
|
38201
|
+
const current = this.#items.get(id);
|
|
38202
|
+
if (!current) return null;
|
|
38203
|
+
const next = { ...current, pathContext };
|
|
38204
|
+
this.#items.set(id, next);
|
|
38205
|
+
return next;
|
|
38206
|
+
}
|
|
37051
38207
|
};
|
|
37052
38208
|
var PACKAGE_NAMES = ["@dotobokuri/fleet-cli", "@dotobokuri/fleet-console"];
|
|
37053
38209
|
var WORKER_FILE_PREFIX = "fleet-console-update-";
|
|
@@ -37059,7 +38215,7 @@ function createConsoleUpdateApplyService(deps = {}) {
|
|
|
37059
38215
|
const env = deps.env ?? process4.env;
|
|
37060
38216
|
const execPath = deps.execPath ?? process4.execPath;
|
|
37061
38217
|
const makeDir = deps.makeDir ?? ((dirPath, options) => {
|
|
37062
|
-
|
|
38218
|
+
fs14__default.mkdirSync(dirPath, options);
|
|
37063
38219
|
});
|
|
37064
38220
|
const now = deps.now ?? Date.now;
|
|
37065
38221
|
const processPid = deps.processPid ?? process4.pid;
|
|
@@ -37068,15 +38224,15 @@ function createConsoleUpdateApplyService(deps = {}) {
|
|
|
37068
38224
|
const spawnWorker = deps.spawnWorker ?? defaultSpawnWorker;
|
|
37069
38225
|
const tmpDir = deps.tmpDir ?? os4__default.tmpdir();
|
|
37070
38226
|
const writeFile5 = deps.writeFile ?? ((filePath, content, options) => {
|
|
37071
|
-
|
|
38227
|
+
fs14__default.writeFileSync(filePath, content, { mode: options.mode });
|
|
37072
38228
|
});
|
|
37073
38229
|
async function start(request) {
|
|
37074
38230
|
const packageManager = await preflightInstall(request.currentPackageRoot);
|
|
37075
38231
|
const stamp = `${now()}-${processPid}`;
|
|
37076
|
-
const workerPath =
|
|
38232
|
+
const workerPath = path27__default.join(tmpDir, `${WORKER_FILE_PREFIX}${stamp}${WORKER_FILE_SUFFIX}`);
|
|
37077
38233
|
makeDir(request.dataDir, { recursive: true, mode: 448 });
|
|
37078
|
-
const statusFile =
|
|
37079
|
-
const logFile =
|
|
38234
|
+
const statusFile = path27__default.join(request.dataDir, `${WORKER_FILE_PREFIX}${stamp}${STATUS_FILE_SUFFIX}`);
|
|
38235
|
+
const logFile = path27__default.join(request.dataDir, `${WORKER_FILE_PREFIX}${stamp}${LOG_FILE_SUFFIX}`);
|
|
37080
38236
|
const script = emitConsoleUpdateWorkerScript({
|
|
37081
38237
|
currentEndpoint: request.currentEndpoint,
|
|
37082
38238
|
currentPackageRoot: request.currentPackageRoot,
|
|
@@ -37419,17 +38575,19 @@ function defaultSpawnWorker(execPath, args, options) {
|
|
|
37419
38575
|
}
|
|
37420
38576
|
function resolveDefaultServerModulePath() {
|
|
37421
38577
|
const builtPath = fileURLToPath(new URL("../dist/cli.mjs", import.meta.url));
|
|
37422
|
-
if (
|
|
38578
|
+
if (fs14__default.existsSync(builtPath)) return builtPath;
|
|
37423
38579
|
const sourcePath = fileURLToPath(import.meta.url);
|
|
37424
38580
|
if (sourcePath.endsWith(".ts")) {
|
|
37425
38581
|
return sourcePath.replace(/update-apply\.ts$/, "cli.ts");
|
|
37426
38582
|
}
|
|
37427
|
-
return
|
|
38583
|
+
return path27__default.join(path27__default.dirname(sourcePath), "cli.mjs");
|
|
37428
38584
|
}
|
|
37429
38585
|
|
|
37430
38586
|
// core/host/update-check.ts
|
|
37431
38587
|
var FLEET_CONSOLE_PACKAGE_NAME = "@dotobokuri/fleet-console";
|
|
37432
38588
|
var UPDATE_CHECK_TTL_MS = 60 * 60 * 1e3;
|
|
38589
|
+
var UPDATE_CHECK_ERROR_TTL_MS = 5 * 60 * 1e3;
|
|
38590
|
+
var UPDATE_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
37433
38591
|
var NO_UPDATE_STATUS = { updateAvailable: false };
|
|
37434
38592
|
function createConsoleUpdateCheckService(deps = {}) {
|
|
37435
38593
|
const readRelease = deps.readRelease ?? readFleetConsoleRelease;
|
|
@@ -37437,11 +38595,17 @@ function createConsoleUpdateCheckService(deps = {}) {
|
|
|
37437
38595
|
const isGreater = deps.isGreater ?? isVersionGreater;
|
|
37438
38596
|
const now = deps.now ?? Date.now;
|
|
37439
38597
|
const ttlMs = deps.ttlMs ?? UPDATE_CHECK_TTL_MS;
|
|
38598
|
+
const errorTtlMs = deps.errorTtlMs ?? UPDATE_CHECK_ERROR_TTL_MS;
|
|
38599
|
+
const intervalMs = deps.intervalMs ?? UPDATE_CHECK_INTERVAL_MS;
|
|
38600
|
+
const startInterval = deps.setInterval ?? ((callback, delayMs) => setInterval(callback, delayMs));
|
|
38601
|
+
const stopInterval = deps.clearInterval ?? ((interval2) => clearInterval(interval2));
|
|
37440
38602
|
let cached2 = null;
|
|
37441
38603
|
let inFlight = null;
|
|
38604
|
+
let interval = null;
|
|
38605
|
+
const changeListeners = /* @__PURE__ */ new Set();
|
|
37442
38606
|
const getStatus = () => {
|
|
37443
38607
|
const current = cached2;
|
|
37444
|
-
if (current && now() - current.checkedAt < ttlMs) {
|
|
38608
|
+
if (current && now() - current.checkedAt < current.ttlMs) {
|
|
37445
38609
|
return current.status;
|
|
37446
38610
|
}
|
|
37447
38611
|
void refresh();
|
|
@@ -37449,17 +38613,19 @@ function createConsoleUpdateCheckService(deps = {}) {
|
|
|
37449
38613
|
};
|
|
37450
38614
|
const refresh = async (options = {}) => {
|
|
37451
38615
|
const current = cached2;
|
|
37452
|
-
if (options.force !== true && current && now() - current.checkedAt < ttlMs) {
|
|
38616
|
+
if (options.force !== true && current && now() - current.checkedAt < current.ttlMs) {
|
|
37453
38617
|
return current.status;
|
|
37454
38618
|
}
|
|
37455
38619
|
if (inFlight) {
|
|
37456
38620
|
return inFlight;
|
|
37457
38621
|
}
|
|
37458
38622
|
inFlight = resolveUpdateStatus().then((status) => {
|
|
37459
|
-
|
|
38623
|
+
const previousStatus = cached2?.status ?? NO_UPDATE_STATUS;
|
|
38624
|
+
cached2 = { status, checkedAt: now(), ttlMs };
|
|
38625
|
+
notifyIfChanged(previousStatus, status);
|
|
37460
38626
|
return status;
|
|
37461
38627
|
}).catch(() => {
|
|
37462
|
-
cached2 = { status: NO_UPDATE_STATUS, checkedAt: now() };
|
|
38628
|
+
cached2 = { status: NO_UPDATE_STATUS, checkedAt: now(), ttlMs: errorTtlMs };
|
|
37463
38629
|
return NO_UPDATE_STATUS;
|
|
37464
38630
|
}).finally(() => {
|
|
37465
38631
|
inFlight = null;
|
|
@@ -37468,16 +38634,46 @@ function createConsoleUpdateCheckService(deps = {}) {
|
|
|
37468
38634
|
};
|
|
37469
38635
|
const resolveUpdateStatus = async () => {
|
|
37470
38636
|
const release = readRelease();
|
|
37471
|
-
if (release.channel === "local") {
|
|
38637
|
+
if (release.channel === "local" || release.channel === "desktop") {
|
|
37472
38638
|
return NO_UPDATE_STATUS;
|
|
37473
38639
|
}
|
|
37474
38640
|
const latestVersion = await fetchLatest(FLEET_CONSOLE_PACKAGE_NAME);
|
|
37475
|
-
if (
|
|
38641
|
+
if (latestVersion === void 0) {
|
|
38642
|
+
throw new Error("registry lookup failed");
|
|
38643
|
+
}
|
|
38644
|
+
if (!isGreater(latestVersion, release.version)) {
|
|
37476
38645
|
return NO_UPDATE_STATUS;
|
|
37477
38646
|
}
|
|
37478
38647
|
return { updateAvailable: true, latestVersion };
|
|
37479
38648
|
};
|
|
37480
|
-
|
|
38649
|
+
const start = () => {
|
|
38650
|
+
if (interval) return;
|
|
38651
|
+
interval = startInterval(() => {
|
|
38652
|
+
void refresh({ force: true });
|
|
38653
|
+
}, intervalMs);
|
|
38654
|
+
interval.unref?.();
|
|
38655
|
+
};
|
|
38656
|
+
const stop = () => {
|
|
38657
|
+
if (!interval) return;
|
|
38658
|
+
stopInterval(interval);
|
|
38659
|
+
interval = null;
|
|
38660
|
+
};
|
|
38661
|
+
const onChange = (listener) => {
|
|
38662
|
+
changeListeners.add(listener);
|
|
38663
|
+
return () => changeListeners.delete(listener);
|
|
38664
|
+
};
|
|
38665
|
+
function notifyIfChanged(previous, next) {
|
|
38666
|
+
const updateBecameAvailable = !previous.updateAvailable && next.updateAvailable;
|
|
38667
|
+
const latestVersionChanged = previous.latestVersion !== next.latestVersion;
|
|
38668
|
+
if (!updateBecameAvailable && !latestVersionChanged) return;
|
|
38669
|
+
for (const listener of changeListeners) {
|
|
38670
|
+
try {
|
|
38671
|
+
listener(next);
|
|
38672
|
+
} catch {
|
|
38673
|
+
}
|
|
38674
|
+
}
|
|
38675
|
+
}
|
|
38676
|
+
return { getStatus, refresh, start, stop, onChange };
|
|
37481
38677
|
}
|
|
37482
38678
|
|
|
37483
38679
|
// core/host/server.ts
|
|
@@ -37533,6 +38729,41 @@ var SERVER_API_CATALOG = [
|
|
|
37533
38729
|
category: "Observer",
|
|
37534
38730
|
gate: "origin-write"
|
|
37535
38731
|
},
|
|
38732
|
+
{
|
|
38733
|
+
method: "GET",
|
|
38734
|
+
path: "/api/v1/theaters/:theaterId/path-context",
|
|
38735
|
+
summary: "Get the selected Theater path context.",
|
|
38736
|
+
category: "Observer",
|
|
38737
|
+
gate: "loopback"
|
|
38738
|
+
},
|
|
38739
|
+
{
|
|
38740
|
+
method: "PUT",
|
|
38741
|
+
path: "/api/v1/theaters/:theaterId/path-context",
|
|
38742
|
+
summary: "Save the selected Theater path context.",
|
|
38743
|
+
category: "Observer",
|
|
38744
|
+
gate: "origin-write"
|
|
38745
|
+
},
|
|
38746
|
+
{
|
|
38747
|
+
method: "GET",
|
|
38748
|
+
path: "/api/v1/theaters/:theaterId/path-context/worktrees",
|
|
38749
|
+
summary: "List contained Git worktrees for a Theater.",
|
|
38750
|
+
category: "Observer",
|
|
38751
|
+
gate: "loopback"
|
|
38752
|
+
},
|
|
38753
|
+
{
|
|
38754
|
+
method: "POST",
|
|
38755
|
+
path: "/api/v1/theaters/:theaterId/path-context/directories",
|
|
38756
|
+
summary: "List contained directories for a Theater path context.",
|
|
38757
|
+
category: "Observer",
|
|
38758
|
+
gate: "origin-write"
|
|
38759
|
+
},
|
|
38760
|
+
{
|
|
38761
|
+
method: "POST",
|
|
38762
|
+
path: "/api/v1/theaters/:theaterId/codex-workspace",
|
|
38763
|
+
summary: "Resolve the Codex workspace for a Theater path context.",
|
|
38764
|
+
category: "Observer",
|
|
38765
|
+
gate: "origin-write"
|
|
38766
|
+
},
|
|
37536
38767
|
{
|
|
37537
38768
|
method: "POST",
|
|
37538
38769
|
path: "/plugins/terminal/shell/ticket",
|
|
@@ -37568,6 +38799,20 @@ var SERVER_API_CATALOG = [
|
|
|
37568
38799
|
category: "Observer",
|
|
37569
38800
|
gate: "origin-write"
|
|
37570
38801
|
},
|
|
38802
|
+
{
|
|
38803
|
+
method: "POST",
|
|
38804
|
+
path: "/api/v1/plans/list",
|
|
38805
|
+
summary: "Theater\uC758 \uC2E4\uD589 \uACC4\uD68D \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
38806
|
+
category: "Observer",
|
|
38807
|
+
gate: "origin-write"
|
|
38808
|
+
},
|
|
38809
|
+
{
|
|
38810
|
+
method: "POST",
|
|
38811
|
+
path: "/api/v1/plans/read",
|
|
38812
|
+
summary: "\uC2E4\uD589 \uACC4\uD68D \uBB38\uC11C\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
38813
|
+
category: "Observer",
|
|
38814
|
+
gate: "origin-write"
|
|
38815
|
+
},
|
|
37571
38816
|
{
|
|
37572
38817
|
method: "POST",
|
|
37573
38818
|
path: "/api/v1/updates/apply",
|
|
@@ -37587,6 +38832,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37587
38832
|
const host = deps.host ?? DEFAULT_HOST;
|
|
37588
38833
|
const port = deps.port ?? DEFAULT_PORT;
|
|
37589
38834
|
const release = deps.release ?? readFleetConsoleRelease();
|
|
38835
|
+
const desktop = readDesktopProtocolEnvironment();
|
|
37590
38836
|
const tryServeStaticConsole2 = createStaticConsoleHandler(release.packageRoot);
|
|
37591
38837
|
const version2 = deps.version ?? release.version;
|
|
37592
38838
|
const channel = release.channel;
|
|
@@ -37594,7 +38840,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37594
38840
|
registerDefaultCarriers(carrierRegistry);
|
|
37595
38841
|
const lock = createConsoleLock({ hostname: () => host });
|
|
37596
38842
|
const releaseNotes = deps.releaseNotes ?? createConsoleReleaseNotesService();
|
|
37597
|
-
const updateCheck = deps.updateCheck ?? createConsoleUpdateCheckService();
|
|
38843
|
+
const updateCheck = deps.updateCheck ?? createConsoleUpdateCheckService({ readRelease: () => release });
|
|
37598
38844
|
const updateApply = deps.updateApply ?? createConsoleUpdateApplyService();
|
|
37599
38845
|
const theaters = new TheaterRegistry();
|
|
37600
38846
|
const operations = createOperationStore();
|
|
@@ -37617,6 +38863,9 @@ function createConsoleServer(deps = {}) {
|
|
|
37617
38863
|
const pluginCleanupCallbacks = /* @__PURE__ */ new Set();
|
|
37618
38864
|
const pluginEventListeners = /* @__PURE__ */ new Map();
|
|
37619
38865
|
const operationSseSubscribers = /* @__PURE__ */ new Set();
|
|
38866
|
+
let unsubscribeUpdateCheckChanges = updateCheck.onChange?.(() => {
|
|
38867
|
+
broadcastUpdateAvailable();
|
|
38868
|
+
}) ?? null;
|
|
37620
38869
|
const pluginHostCapabilities = {
|
|
37621
38870
|
operations: {
|
|
37622
38871
|
list: () => operations.list(),
|
|
@@ -37687,8 +38936,8 @@ function createConsoleServer(deps = {}) {
|
|
|
37687
38936
|
},
|
|
37688
38937
|
paths: {
|
|
37689
38938
|
capturesDir: durablePaths.capturesDir,
|
|
37690
|
-
pluginDataDir: (pluginId) =>
|
|
37691
|
-
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.
|
|
38939
|
+
pluginDataDir: (pluginId) => path27__default.join(durablePaths.dir, "plugins", pluginId),
|
|
38940
|
+
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.realpath ?? null,
|
|
37692
38941
|
canonicalizeTheaterPath: canonicalizeTheaterPathSync,
|
|
37693
38942
|
workspaceHash
|
|
37694
38943
|
},
|
|
@@ -37712,8 +38961,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37712
38961
|
}
|
|
37713
38962
|
}
|
|
37714
38963
|
};
|
|
38964
|
+
process.env.FLEET_CONSOLE_PACKAGE_ROOT = release.packageRoot;
|
|
37715
38965
|
const pluginHost = createFleetPluginHost({
|
|
37716
38966
|
...resolveBuiltInPluginDiscoveryRoots(release.packageRoot),
|
|
38967
|
+
bundleCacheDir: path27__default.join(durablePaths.dir, "plugin-cache"),
|
|
37717
38968
|
routes: routeRegistry,
|
|
37718
38969
|
upgrades: upgradeRegistry,
|
|
37719
38970
|
host: pluginHostCapabilities
|
|
@@ -37779,6 +39030,31 @@ function createConsoleServer(deps = {}) {
|
|
|
37779
39030
|
readJsonBody,
|
|
37780
39031
|
writeJson
|
|
37781
39032
|
});
|
|
39033
|
+
const systemFontsRouter = createSystemFontsRouter({
|
|
39034
|
+
systemFonts: deps.systemFonts ?? createSystemFontsService(),
|
|
39035
|
+
writeJson
|
|
39036
|
+
});
|
|
39037
|
+
const plansRouter = createPlansRouter({
|
|
39038
|
+
isAuthorized: isTerminalAuthorized,
|
|
39039
|
+
readJsonBody,
|
|
39040
|
+
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.realpath ?? null,
|
|
39041
|
+
writeJson
|
|
39042
|
+
});
|
|
39043
|
+
const theaterPathContextRouter = createTheaterPathContextRouter({
|
|
39044
|
+
getTheater: (theaterId) => theaters.get(theaterId),
|
|
39045
|
+
isAuthorized: isTerminalAuthorized,
|
|
39046
|
+
persist: persistDurableState,
|
|
39047
|
+
readJsonBody,
|
|
39048
|
+
setPathContext: (theaterId, relPath) => theaters.setPathContext(theaterId, relPath),
|
|
39049
|
+
writeJson
|
|
39050
|
+
});
|
|
39051
|
+
const codexWorkspaceContextRouter = createCodexWorkspaceContextRouter({
|
|
39052
|
+
getTheater: (theaterId) => theaters.get(theaterId),
|
|
39053
|
+
isAuthorized: isTerminalAuthorized,
|
|
39054
|
+
readJsonBody,
|
|
39055
|
+
resolveWorkspace: (theaterId, theaterRoot, relPath) => codex.resolveWorkspaceForPath(theaterId, theaterRoot, relPath),
|
|
39056
|
+
writeJson
|
|
39057
|
+
});
|
|
37782
39058
|
const operationsRouter = createOperationsRouter({
|
|
37783
39059
|
store: operations,
|
|
37784
39060
|
isAuthorized: isTerminalAuthorized,
|
|
@@ -37807,6 +39083,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37807
39083
|
}
|
|
37808
39084
|
});
|
|
37809
39085
|
routeRegistry.register("/api/v1/operations", operationsRouter);
|
|
39086
|
+
routeRegistry.register("/api/v1/theaters", async (context) => {
|
|
39087
|
+
if (await theaterPathContextRouter(context)) return true;
|
|
39088
|
+
return codexWorkspaceContextRouter(context);
|
|
39089
|
+
});
|
|
37810
39090
|
routeRegistry.register("/api/v1/settings", async (ctx) => {
|
|
37811
39091
|
const { req, res, pathname } = ctx;
|
|
37812
39092
|
if (pathname === "/api/v1/settings/api-catalog") {
|
|
@@ -37815,6 +39095,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37815
39095
|
}
|
|
37816
39096
|
if (await carrierSettingsRouter(ctx)) return true;
|
|
37817
39097
|
if (await pluginSettingsRouter(ctx)) return true;
|
|
39098
|
+
if (await systemFontsRouter(ctx)) return true;
|
|
37818
39099
|
return globalSettingsRouter(ctx);
|
|
37819
39100
|
});
|
|
37820
39101
|
routeRegistry.register("/plugin-runtime", handlePluginRuntimeRoute);
|
|
@@ -37855,6 +39136,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37855
39136
|
runAsyncHandler(handleTheaterFolderGrants(req, res), res);
|
|
37856
39137
|
return;
|
|
37857
39138
|
}
|
|
39139
|
+
if (pathname === "/api/v1/plans/list" || pathname === "/api/v1/plans/read") {
|
|
39140
|
+
runAsyncBooleanHandler(plansRouter({ req, res, pathname }), res, () => false);
|
|
39141
|
+
return;
|
|
39142
|
+
}
|
|
37858
39143
|
const theaterItemMatch = pathname.match(/^\/api\/v1\/theaters\/([^/]+)$/);
|
|
37859
39144
|
if (theaterItemMatch) {
|
|
37860
39145
|
runAsyncHandler(handleObserverTheaterItem(req, res, decodeURIComponent(theaterItemMatch[1] ?? "")), res);
|
|
@@ -37919,6 +39204,8 @@ function createConsoleServer(deps = {}) {
|
|
|
37919
39204
|
endpoint: payload.endpoint,
|
|
37920
39205
|
startedAt: payload.startedAt,
|
|
37921
39206
|
version: payload.version,
|
|
39207
|
+
...channel === "desktop" ? { channel } : {},
|
|
39208
|
+
...payload.owner ? { owner: payload.owner } : {},
|
|
37922
39209
|
workspaceCount: operations.list().length
|
|
37923
39210
|
};
|
|
37924
39211
|
writeJson(res, 200, body);
|
|
@@ -37936,7 +39223,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37936
39223
|
return;
|
|
37937
39224
|
}
|
|
37938
39225
|
const body = await readJsonBody(req);
|
|
37939
|
-
if (!
|
|
39226
|
+
if (!isPlainObject4(body) || body.path !== void 0 && body.path !== null && typeof body.path !== "string") {
|
|
37940
39227
|
writeJson(res, 400, { error: "invalid_path" });
|
|
37941
39228
|
return;
|
|
37942
39229
|
}
|
|
@@ -37961,7 +39248,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37961
39248
|
return;
|
|
37962
39249
|
}
|
|
37963
39250
|
const body = await readJsonBody(req);
|
|
37964
|
-
if (!
|
|
39251
|
+
if (!isPlainObject4(body) || typeof body.path !== "string") {
|
|
37965
39252
|
writeJson(res, 400, { error: "invalid_folder" });
|
|
37966
39253
|
return;
|
|
37967
39254
|
}
|
|
@@ -37985,7 +39272,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37985
39272
|
return;
|
|
37986
39273
|
}
|
|
37987
39274
|
const body = await readJsonBody(req);
|
|
37988
|
-
if (!
|
|
39275
|
+
if (!isPlainObject4(body) || typeof body.folderGrantId !== "string") {
|
|
37989
39276
|
writeJson(res, 400, { error: "invalid_folder_grant" });
|
|
37990
39277
|
return;
|
|
37991
39278
|
}
|
|
@@ -37997,7 +39284,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37997
39284
|
const theater = await theaters.register(cwd);
|
|
37998
39285
|
let hasWiki = false;
|
|
37999
39286
|
try {
|
|
38000
|
-
await codex.registerWorkspace(
|
|
39287
|
+
await codex.registerWorkspace(theater.realpath, void 0, theater.id);
|
|
38001
39288
|
hasWiki = true;
|
|
38002
39289
|
} catch (error51) {
|
|
38003
39290
|
if (!(error51 instanceof Error && error51.message === "knowledge_root_missing")) {
|
|
@@ -38017,7 +39304,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38017
39304
|
return;
|
|
38018
39305
|
}
|
|
38019
39306
|
theaters.remove(theaterId);
|
|
38020
|
-
codex.
|
|
39307
|
+
codex.unregisterTheaterWorkspaces(theaterId);
|
|
38021
39308
|
for (const operation of operations.listByTheater(theaterId)) {
|
|
38022
39309
|
pluginHostCapabilities.events.publish(OPERATION_DELETED_EVENT_CHANNEL, {
|
|
38023
39310
|
operationId: operation.id,
|
|
@@ -38058,8 +39345,10 @@ function createConsoleServer(deps = {}) {
|
|
|
38058
39345
|
return;
|
|
38059
39346
|
}
|
|
38060
39347
|
try {
|
|
38061
|
-
const
|
|
38062
|
-
|
|
39348
|
+
const searchParams = readUrl(req).searchParams;
|
|
39349
|
+
const force = searchParams.get("force") === "true";
|
|
39350
|
+
const locale = searchParams.get("locale") === "ko" ? "ko" : "en";
|
|
39351
|
+
writeJson(res, 200, await releaseNotes.refresh({ force, locale }));
|
|
38063
39352
|
} catch (error51) {
|
|
38064
39353
|
if (error51 instanceof ConsoleReleaseNotesUnavailableError) {
|
|
38065
39354
|
writeJson(res, 503, { error: "release_notes_unavailable" });
|
|
@@ -38082,10 +39371,14 @@ function createConsoleServer(deps = {}) {
|
|
|
38082
39371
|
writeJson(res, 400, { error: "invalid_update_apply_body" });
|
|
38083
39372
|
return;
|
|
38084
39373
|
}
|
|
38085
|
-
if (body !== null && (!
|
|
39374
|
+
if (body !== null && (!isPlainObject4(body) || hasForbiddenUpdateApplyBodyKeys(body))) {
|
|
38086
39375
|
writeJson(res, 400, { error: "invalid_update_apply_body" });
|
|
38087
39376
|
return;
|
|
38088
39377
|
}
|
|
39378
|
+
if (channel === "desktop") {
|
|
39379
|
+
writeJson(res, 403, { error: "desktop_update_managed" });
|
|
39380
|
+
return;
|
|
39381
|
+
}
|
|
38089
39382
|
if (channel === "local") {
|
|
38090
39383
|
writeJson(res, 403, { error: "local_channel" });
|
|
38091
39384
|
return;
|
|
@@ -38180,6 +39473,9 @@ function createConsoleServer(deps = {}) {
|
|
|
38180
39473
|
await disposeConsoleResources(currentLock);
|
|
38181
39474
|
}
|
|
38182
39475
|
async function disposeConsoleResources(currentLock) {
|
|
39476
|
+
updateCheck.stop?.();
|
|
39477
|
+
unsubscribeUpdateCheckChanges?.();
|
|
39478
|
+
unsubscribeUpdateCheckChanges = null;
|
|
38183
39479
|
if (consoleResourcesDisposed) {
|
|
38184
39480
|
currentLock?.release();
|
|
38185
39481
|
return;
|
|
@@ -38191,6 +39487,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38191
39487
|
console.warn(`[fleet-console] Plugin cleanup failed: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
38192
39488
|
}
|
|
38193
39489
|
}
|
|
39490
|
+
await pluginHost.cleanup();
|
|
38194
39491
|
pluginCleanupCallbacks.clear();
|
|
38195
39492
|
pluginEventListeners.clear();
|
|
38196
39493
|
currentLock?.release();
|
|
@@ -38202,6 +39499,8 @@ function createConsoleServer(deps = {}) {
|
|
|
38202
39499
|
theaters.restore(state.theaters);
|
|
38203
39500
|
operations.replace(state.operations);
|
|
38204
39501
|
operations.replaceGroups(state.groups ?? []);
|
|
39502
|
+
const healed = await healRestoredPathContexts();
|
|
39503
|
+
if (healed) persistDurableState();
|
|
38205
39504
|
} catch (error51) {
|
|
38206
39505
|
console.warn(`[fleet-console] Durable state restore skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
38207
39506
|
state = emptyDurableConsoleState();
|
|
@@ -38211,11 +39510,28 @@ function createConsoleServer(deps = {}) {
|
|
|
38211
39510
|
}
|
|
38212
39511
|
await restoreCodexWorkspaces();
|
|
38213
39512
|
}
|
|
39513
|
+
async function healRestoredPathContexts() {
|
|
39514
|
+
let healed = false;
|
|
39515
|
+
for (const theater of theaters.list()) {
|
|
39516
|
+
if (theater.pathContext === null) continue;
|
|
39517
|
+
try {
|
|
39518
|
+
const resolved = await resolveTheaterPathContext(theater.realpath, theater.pathContext);
|
|
39519
|
+
if (resolved.relPath !== theater.pathContext) {
|
|
39520
|
+
theaters.setPathContext(theater.id, resolved.relPath);
|
|
39521
|
+
healed = true;
|
|
39522
|
+
}
|
|
39523
|
+
} catch {
|
|
39524
|
+
theaters.setPathContext(theater.id, null);
|
|
39525
|
+
healed = true;
|
|
39526
|
+
}
|
|
39527
|
+
}
|
|
39528
|
+
return healed;
|
|
39529
|
+
}
|
|
38214
39530
|
async function restoreCodexWorkspaces() {
|
|
38215
39531
|
const ordered = [...theaters.list()].sort((left, right) => left.lastOpenedAt.localeCompare(right.lastOpenedAt));
|
|
38216
39532
|
for (const theater of ordered) {
|
|
38217
39533
|
try {
|
|
38218
|
-
await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt);
|
|
39534
|
+
await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt, theater.id);
|
|
38219
39535
|
} catch (error51) {
|
|
38220
39536
|
if (!(error51 instanceof Error && error51.message === "knowledge_root_missing")) {
|
|
38221
39537
|
console.warn(`[fleet-console] Codex workspace restore skipped for Theater ${theater.id}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
@@ -38235,6 +39551,13 @@ function createConsoleServer(deps = {}) {
|
|
|
38235
39551
|
res.write(data);
|
|
38236
39552
|
}
|
|
38237
39553
|
}
|
|
39554
|
+
function broadcastUpdateAvailable() {
|
|
39555
|
+
if (operationSseSubscribers.size === 0) return;
|
|
39556
|
+
const data = encodeSseData("update:available", {});
|
|
39557
|
+
for (const res of operationSseSubscribers) {
|
|
39558
|
+
res.write(data);
|
|
39559
|
+
}
|
|
39560
|
+
}
|
|
38238
39561
|
function persistDurableState() {
|
|
38239
39562
|
try {
|
|
38240
39563
|
durableStateStore.save({
|
|
@@ -38279,7 +39602,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38279
39602
|
server = result.srv;
|
|
38280
39603
|
loopbackServer = result.localLoopbackServer;
|
|
38281
39604
|
portState = result.portState;
|
|
38282
|
-
lockHandle = lock.writeLock({ dir: lockPaths.dir, lockFile: lockPaths.lockFile, pid: process.pid, port: result.actualPort, endpoint: result.endpoint, version: version2 });
|
|
39605
|
+
lockHandle = lock.writeLock({ dir: lockPaths.dir, lockFile: lockPaths.lockFile, pid: process.pid, port: result.actualPort, endpoint: result.endpoint, version: version2, ...desktop ? { owner: desktop.owner } : {} });
|
|
38283
39606
|
activeLockFile = lockPaths.lockFile;
|
|
38284
39607
|
activeEndpoint = result.endpoint;
|
|
38285
39608
|
} catch (error51) {
|
|
@@ -38287,6 +39610,12 @@ function createConsoleServer(deps = {}) {
|
|
|
38287
39610
|
throw error51;
|
|
38288
39611
|
}
|
|
38289
39612
|
if (!activeEndpoint) throw new Error("Console endpoint unavailable");
|
|
39613
|
+
if (unsubscribeUpdateCheckChanges === null) {
|
|
39614
|
+
unsubscribeUpdateCheckChanges = updateCheck.onChange?.(() => {
|
|
39615
|
+
broadcastUpdateAvailable();
|
|
39616
|
+
}) ?? null;
|
|
39617
|
+
}
|
|
39618
|
+
updateCheck.start?.();
|
|
38290
39619
|
void updateCheck.refresh();
|
|
38291
39620
|
return activeEndpoint;
|
|
38292
39621
|
},
|
|
@@ -38307,6 +39636,14 @@ function createConsoleServer(deps = {}) {
|
|
|
38307
39636
|
allowFallback: false
|
|
38308
39637
|
};
|
|
38309
39638
|
}
|
|
39639
|
+
if (channel === "local") {
|
|
39640
|
+
return {
|
|
39641
|
+
port: DEFAULT_PORT,
|
|
39642
|
+
requestedPort: null,
|
|
39643
|
+
portMode: "dynamic",
|
|
39644
|
+
allowFallback: false
|
|
39645
|
+
};
|
|
39646
|
+
}
|
|
38310
39647
|
const options = consoleSettingsStore.load().general ?? {};
|
|
38311
39648
|
if (options.consolePortMode === "static" && isValidConsoleStaticPort3(options.consoleStaticPort)) {
|
|
38312
39649
|
return {
|
|
@@ -38373,11 +39710,11 @@ function createConsoleServer(deps = {}) {
|
|
|
38373
39710
|
return returnedServer;
|
|
38374
39711
|
}
|
|
38375
39712
|
function resolveBuiltInPluginDiscoveryRoots(packageRoot) {
|
|
38376
|
-
const packageRootRepo =
|
|
38377
|
-
const sourceRoot =
|
|
39713
|
+
const packageRootRepo = path27__default.resolve(packageRoot, "..", "..");
|
|
39714
|
+
const sourceRoot = path27__default.join(packageRootRepo, "runtime", "fleet-plugins");
|
|
38378
39715
|
return {
|
|
38379
|
-
...
|
|
38380
|
-
builtInDistRoot:
|
|
39716
|
+
...fs14__default.existsSync(sourceRoot) ? { builtInSourceRoot: sourceRoot } : {},
|
|
39717
|
+
builtInDistRoot: path27__default.join(packageRoot, "dist", "fleet-plugins")
|
|
38381
39718
|
};
|
|
38382
39719
|
}
|
|
38383
39720
|
function createHttpServer(handler, upgradeRegistry) {
|
|
@@ -38433,7 +39770,7 @@ function theaterFolderListStatus(error51) {
|
|
|
38433
39770
|
if (error51.code === "not_found") return 404;
|
|
38434
39771
|
return 400;
|
|
38435
39772
|
}
|
|
38436
|
-
function
|
|
39773
|
+
function isPlainObject4(value) {
|
|
38437
39774
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38438
39775
|
}
|
|
38439
39776
|
function hasForbiddenUpdateApplyBodyKeys(body) {
|
|
@@ -38443,7 +39780,7 @@ function isValidConsoleStaticPort3(value) {
|
|
|
38443
39780
|
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT3 && value <= MAX_CONSOLE_STATIC_PORT3;
|
|
38444
39781
|
}
|
|
38445
39782
|
function sanitizeLaunchKind(value) {
|
|
38446
|
-
if (!
|
|
39783
|
+
if (!isPlainObject4(value) || typeof value.id !== "string" || typeof value.type !== "string" || typeof value.title !== "string") return null;
|
|
38447
39784
|
return {
|
|
38448
39785
|
id: value.id,
|
|
38449
39786
|
type: value.type,
|
|
@@ -38522,7 +39859,7 @@ function isAllowedTerminalOrigin(req, expectedPort) {
|
|
|
38522
39859
|
async function readPluginStorageJson(dataDir, pluginId, key) {
|
|
38523
39860
|
const file2 = resolvePluginStorageFile(dataDir, pluginId, key);
|
|
38524
39861
|
try {
|
|
38525
|
-
return JSON.parse(await
|
|
39862
|
+
return JSON.parse(await fs14__default.promises.readFile(file2, "utf8"));
|
|
38526
39863
|
} catch (error51) {
|
|
38527
39864
|
if (isNodeError(error51) && error51.code === "ENOENT") return null;
|
|
38528
39865
|
throw error51;
|
|
@@ -38530,20 +39867,20 @@ async function readPluginStorageJson(dataDir, pluginId, key) {
|
|
|
38530
39867
|
}
|
|
38531
39868
|
async function writePluginStorageJson(dataDir, pluginId, key, value) {
|
|
38532
39869
|
const file2 = resolvePluginStorageFile(dataDir, pluginId, key);
|
|
38533
|
-
await
|
|
38534
|
-
await
|
|
39870
|
+
await fs14__default.promises.mkdir(path27__default.dirname(file2), { recursive: true });
|
|
39871
|
+
await fs14__default.promises.writeFile(file2, JSON.stringify(value), "utf8");
|
|
38535
39872
|
}
|
|
38536
39873
|
function resolvePluginStorageFile(dataDir, pluginId, key) {
|
|
38537
39874
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(pluginId) || !/^[a-z0-9][a-z0-9._-]*$/i.test(key)) {
|
|
38538
39875
|
throw new Error("invalid_plugin_storage_key");
|
|
38539
39876
|
}
|
|
38540
|
-
return
|
|
39877
|
+
return path27__default.join(dataDir, "plugins", pluginId, `${key}.json`);
|
|
38541
39878
|
}
|
|
38542
39879
|
function isNodeError(error51) {
|
|
38543
39880
|
return error51 instanceof Error && "code" in error51;
|
|
38544
39881
|
}
|
|
38545
39882
|
function createConsoleStalePolicy(deps = {}) {
|
|
38546
|
-
const fsImpl = deps.fs ??
|
|
39883
|
+
const fsImpl = deps.fs ?? fs14__default;
|
|
38547
39884
|
function isBuildStale(lock, buildFile) {
|
|
38548
39885
|
try {
|
|
38549
39886
|
return lock.startedAt < fsImpl.statSync(buildFile).mtimeMs;
|
|
@@ -38665,6 +40002,7 @@ function createConsoleDaemonLifecycle(deps = {}) {
|
|
|
38665
40002
|
async function stop() {
|
|
38666
40003
|
const payload = readTrustedLock();
|
|
38667
40004
|
if (!payload) return;
|
|
40005
|
+
if (isLockProcessAlive(payload.pid)) assertCliCanControlDaemon(payload);
|
|
38668
40006
|
try {
|
|
38669
40007
|
process4.kill(payload.pid, "SIGTERM");
|
|
38670
40008
|
} catch (err) {
|
|
@@ -38684,6 +40022,7 @@ function createConsoleDaemonLifecycle(deps = {}) {
|
|
|
38684
40022
|
const probeResult = await health.probe(current);
|
|
38685
40023
|
const isBuildStale = current ? stale.isBuildStale(current, serverModulePath) : false;
|
|
38686
40024
|
if (probeResult.healthy && current) {
|
|
40025
|
+
if (current.owner?.kind === "desktop") return current.endpoint;
|
|
38687
40026
|
if (!isBuildStale) return current.endpoint;
|
|
38688
40027
|
if (typeof probeResult.health?.workspaceCount === "number" && probeResult.health.workspaceCount > 0) return current.endpoint;
|
|
38689
40028
|
}
|
|
@@ -38736,11 +40075,12 @@ async function runConsoleStatus(deps = {}) {
|
|
|
38736
40075
|
const consoleUrl = `${status.lock.endpoint}console/`;
|
|
38737
40076
|
const workspaceCount = typeof status.health?.workspaceCount === "number" ? status.health.workspaceCount : 0;
|
|
38738
40077
|
const staleNote = status.buildStale ? " \xB7 build stale (restart recommended)" : "";
|
|
40078
|
+
const ownerNote = status.lock.owner?.kind === "desktop" ? " \xB7 owned by desktop" : "";
|
|
38739
40079
|
return [
|
|
38740
40080
|
`Fleet Console server: running (pid ${status.lock.pid})`,
|
|
38741
40081
|
` endpoint ${status.lock.endpoint}`,
|
|
38742
40082
|
` console ${consoleUrl}`,
|
|
38743
|
-
` workspaces ${workspaceCount}${staleNote}`
|
|
40083
|
+
` workspaces ${workspaceCount}${staleNote}${ownerNote}`
|
|
38744
40084
|
].join("\n");
|
|
38745
40085
|
}
|
|
38746
40086
|
async function runConsoleStop(deps = {}) {
|
|
@@ -38748,6 +40088,17 @@ async function runConsoleStop(deps = {}) {
|
|
|
38748
40088
|
await lifecycle.stop();
|
|
38749
40089
|
return "Fleet Console server stopped.";
|
|
38750
40090
|
}
|
|
40091
|
+
function assertCliCanControlDaemon(payload) {
|
|
40092
|
+
if (payload.owner?.kind === "desktop") throw new Error("Fleet Console is owned by the desktop app; quit it from the native menu.");
|
|
40093
|
+
}
|
|
40094
|
+
function isLockProcessAlive(pid) {
|
|
40095
|
+
try {
|
|
40096
|
+
process4.kill(pid, 0);
|
|
40097
|
+
return true;
|
|
40098
|
+
} catch (err) {
|
|
40099
|
+
return err.code !== "ESRCH";
|
|
40100
|
+
}
|
|
40101
|
+
}
|
|
38751
40102
|
async function runConsoleRestart(deps = {}) {
|
|
38752
40103
|
const lifecycle = deps.lifecycle ?? createConsoleDaemonLifecycle();
|
|
38753
40104
|
await lifecycle.stop();
|
|
@@ -38801,7 +40152,7 @@ async function main() {
|
|
|
38801
40152
|
}
|
|
38802
40153
|
function resolveDefaultServerModulePath2() {
|
|
38803
40154
|
const builtPath = new URL("../dist/cli.mjs", import.meta.url).pathname;
|
|
38804
|
-
if (
|
|
40155
|
+
if (fs14__default.existsSync(builtPath)) return builtPath;
|
|
38805
40156
|
return fileURLToPath(import.meta.url);
|
|
38806
40157
|
}
|
|
38807
40158
|
function readHookSessionId(env) {
|
|
@@ -38855,7 +40206,7 @@ function readStdinBestEffort() {
|
|
|
38855
40206
|
});
|
|
38856
40207
|
});
|
|
38857
40208
|
}
|
|
38858
|
-
var isDirectRun = process4.argv[1] &&
|
|
40209
|
+
var isDirectRun = process4.argv[1] && path27__default.resolve(process4.argv[1]) === fileURLToPath(import.meta.url);
|
|
38859
40210
|
if (isDirectRun) {
|
|
38860
40211
|
await main().catch((error51) => {
|
|
38861
40212
|
process4.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
|
|
@@ -38864,4 +40215,4 @@ if (isDirectRun) {
|
|
|
38864
40215
|
});
|
|
38865
40216
|
}
|
|
38866
40217
|
|
|
38867
|
-
export { buildConsoleHelpText, createConsoleDaemonLifecycle, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|
|
40218
|
+
export { assertCliCanControlDaemon, buildConsoleHelpText, createConsoleDaemonLifecycle, isLockProcessAlive, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|