@dotobokuri/fleet-console 1.23.0 → 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 -257
- package/README.md +8 -0
- package/dist/cli.d.ts +11 -1
- package/dist/cli.mjs +1738 -453
- package/dist/client/assets/{_baseUniq-BjOe5yYF.js → _baseUniq-yooYbTgN.js} +1 -1
- package/dist/client/assets/{arc-bFUuO3jf.js → arc-D4mme5Ts.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-Ba7nimQq.js → architectureDiagram-Q4EWVU46-BAJOviEX.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-CQGf47hl.js → blockDiagram-DXYQGD6D-DuObpxQ3.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-DZF_cAK4.js → c4Diagram-AHTNJAMY-BOT-SD34.js} +1 -1
- package/dist/client/assets/channel-TZYyAq1v.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DjUUHSQ5.js → chunk-4BX2VUAB-BF_aMTes.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Dvqti8bd.js → chunk-4TB4RGXK-M4brBcnD.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-2BxiBvkp.js → chunk-55IACEB6-CrG-4b2Z.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-CqCjol5O.js → chunk-EDXVE4YY-BxDSygH1.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-eoCfIbly.js → chunk-FMBD7UC4-Bh7MEX7E.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CoC86V_1.js → chunk-OYMX7WX6-DBnBwxer.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CJQA5mlQ.js → chunk-QZHKN3VN-bjAogQwW.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-C5C4kbLu.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-CahuunpL.js → cose-bilkent-S5V4N54A-Bm5QA4zo.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-Cbv5kSi5.js → dagre-KV5264BT-PzXQN-5Q.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-CNm40ZGB.js → diagram-5BDNPKRD-KhaAKPtX.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-DrlGNT3i.js → diagram-G4DWMVQ6-DBbjfiR8.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-r8Y2oqmw.js → diagram-MMDJMWI5-QWro7PRX.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-CMzEhXup.js → diagram-TYMM5635-B9LqWrbF.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-UFjUUMPz.js → erDiagram-SMLLAGMA-b0VkMMN4.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-Dd1STmYc.js → flowDiagram-DWJPFMVM-edxBAbe6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CHHU0hVd.js → ganttDiagram-T4ZO3ILL-ks2Gg1_V.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-vj2HukAF.js → gitGraphDiagram-UUTBAWPF-DymUyYNl.js} +1 -1
- package/dist/client/assets/{graph-DhjfEbEb.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-CC7AEgDm.js → infoDiagram-42DDH7IO-B5iLIYAc.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DlRw2B9G.js → ishikawaDiagram-UXIWVN3A-DnztLLn7.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BbRXANKQ.js → journeyDiagram-VCZTEJTY-BKDRfH1Y.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-DtvdCxGN.js → kanban-definition-6JOO6SKY-DPB3Phvr.js} +1 -1
- package/dist/client/assets/{layout-Dg5cvw5v.js → layout-BEeRhYTG.js} +1 -1
- package/dist/client/assets/{linear-BvVlQLqF.js → linear-QzmfWKSq.js} +1 -1
- package/dist/client/assets/{mermaid.core-DzrV8sC-.js → mermaid.core-BVka5dgm.js} +4 -4
- package/dist/client/assets/{min-DNGQ1Ciz.js → min-CfXk9kSN.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-DxJCNZbg.js → mindmap-definition-QFDTVHPH-tGrGhr6Z.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-B75XHG-s.js → pieDiagram-DEJITSTG-ZRBqRiRK.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CL4yoF0m.js → quadrantDiagram-34T5L4WZ-VCINZgct.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-kGV4vcr1.js → requirementDiagram-MS252O5E-DFLzADh3.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-DoA4u7wI.js → sankeyDiagram-XADWPNL6-BbouN8Ji.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-ZpNDf1bS.js → sequenceDiagram-FGHM5R23-B58_LF0R.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BzgKcVAG.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-B_DkUDsD.js → timeline-definition-GMOUNBTQ-4jQdCvaj.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-MCUnnsKb.js → vennDiagram-DHZGUBPP-CUr1fH6N.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-D_JAMSxj.js → wardley-RL74JXVD-D0cfvvNA.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BHCwZGLq.js → wardleyDiagram-NUSXRM2D-D0l_Rp_Z.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-FbGwbD3V.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 -82
- package/dist/fleet-plugins/terminal/routes.mjs +190 -176
- package/package.json +7 -2
- package/dist/client/assets/channel-D2BahVp1.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DsDAVLhS.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DsDAVLhS.js +0 -1
- package/dist/client/assets/clone-CdZy2Xzf.js +0 -1
- package/dist/client/assets/index-Dq9EKm2G.css +0 -1
- package/dist/client/assets/index-DtzbOdd7.js +0 -380
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-yRK5hzZ4.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
|
}
|
|
@@ -21995,12 +22089,12 @@ var CodexAppServerConnection = class extends BaseConnection {
|
|
|
21995
22089
|
);
|
|
21996
22090
|
}
|
|
21997
22091
|
findRolloutPathForThreadId(threadId) {
|
|
21998
|
-
const codexHome = this.codexHome ?? this.env.CODEX_HOME ??
|
|
22092
|
+
const codexHome = this.codexHome ?? this.env.CODEX_HOME ?? path27.join(this.env.HOME ?? "", ".codex");
|
|
21999
22093
|
if (!codexHome) {
|
|
22000
22094
|
return null;
|
|
22001
22095
|
}
|
|
22002
22096
|
for (const rootName of ["sessions", "archived_sessions"]) {
|
|
22003
|
-
const found = findRolloutPath(
|
|
22097
|
+
const found = findRolloutPath(path27.join(codexHome, rootName), threadId);
|
|
22004
22098
|
if (found) {
|
|
22005
22099
|
return found;
|
|
22006
22100
|
}
|
|
@@ -22107,11 +22201,11 @@ function isMissingRolloutError(error51, threadId) {
|
|
|
22107
22201
|
return error51 instanceof Error && error51.message.includes(`no rollout found for thread id ${threadId}`);
|
|
22108
22202
|
}
|
|
22109
22203
|
function findRolloutPath(root, threadId) {
|
|
22110
|
-
if (!
|
|
22204
|
+
if (!fs14.existsSync(root)) {
|
|
22111
22205
|
return null;
|
|
22112
22206
|
}
|
|
22113
|
-
for (const entry of
|
|
22114
|
-
const entryPath =
|
|
22207
|
+
for (const entry of fs14.readdirSync(root, { withFileTypes: true })) {
|
|
22208
|
+
const entryPath = path27.join(root, entry.name);
|
|
22115
22209
|
if (entry.isDirectory()) {
|
|
22116
22210
|
const found = findRolloutPath(entryPath, threadId);
|
|
22117
22211
|
if (found) {
|
|
@@ -22154,9 +22248,10 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22154
22248
|
throw new Error("UnifiedCodexAgentClient\uB294 codex CLI\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
|
|
22155
22249
|
}
|
|
22156
22250
|
this.validateModelEffort(options.model, options.effort);
|
|
22157
|
-
{
|
|
22251
|
+
if (shouldUseCodexAcp(options)) {
|
|
22158
22252
|
return this.connectAcp(options);
|
|
22159
22253
|
}
|
|
22254
|
+
return this.connectAppServer(options);
|
|
22160
22255
|
}
|
|
22161
22256
|
async connectAppServer(options) {
|
|
22162
22257
|
const backend = getBackendConfig("codex");
|
|
@@ -22794,6 +22889,11 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
|
|
|
22794
22889
|
return patterns.some((pattern) => pattern.test(text));
|
|
22795
22890
|
}
|
|
22796
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
|
+
}
|
|
22797
22897
|
var UnifiedCursorAgentClient = class extends EventEmitter {
|
|
22798
22898
|
connection = null;
|
|
22799
22899
|
sessionId = null;
|
|
@@ -24919,7 +25019,7 @@ function formatInstallCommand(packageNames, command2, versionOrChannel) {
|
|
|
24919
25019
|
return `${command2} i -g ${packageNames.map((name) => `${name}@${versionOrChannel}`).join(" ")}`;
|
|
24920
25020
|
}
|
|
24921
25021
|
function isPathInside(deps, child, parent) {
|
|
24922
|
-
const separator = deps.platform === "win32" ? "\\" :
|
|
25022
|
+
const separator = deps.platform === "win32" ? "\\" : path27__default.sep;
|
|
24923
25023
|
return child === parent || child.startsWith(`${parent}${separator}`);
|
|
24924
25024
|
}
|
|
24925
25025
|
function normalizeExistingPath(deps, value) {
|
|
@@ -24934,10 +25034,10 @@ function normalizePath(deps, value) {
|
|
|
24934
25034
|
return deps.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
24935
25035
|
}
|
|
24936
25036
|
function resolvePathForPlatform(platform, value) {
|
|
24937
|
-
return platform === "win32" ?
|
|
25037
|
+
return platform === "win32" ? path27__default.win32.resolve(value) : path27__default.resolve(value);
|
|
24938
25038
|
}
|
|
24939
25039
|
function joinPathForPlatform(platform, ...parts) {
|
|
24940
|
-
return platform === "win32" ?
|
|
25040
|
+
return platform === "win32" ? path27__default.win32.join(...parts) : path27__default.join(...parts);
|
|
24941
25041
|
}
|
|
24942
25042
|
function defaultExecFile(file2, args) {
|
|
24943
25043
|
return execFileSync(file2, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -25046,7 +25146,7 @@ function readRawCarriersOrDefaultStore() {
|
|
|
25046
25146
|
return readCarriers();
|
|
25047
25147
|
}
|
|
25048
25148
|
function getCarriersFilePath() {
|
|
25049
|
-
return
|
|
25149
|
+
return path27.join(resolveStoreDir(), FILENAME);
|
|
25050
25150
|
}
|
|
25051
25151
|
function updateCarriers(mutator) {
|
|
25052
25152
|
withStoreLock(() => {
|
|
@@ -25060,7 +25160,7 @@ function updateCarriers(mutator) {
|
|
|
25060
25160
|
function withStoreLock(operation) {
|
|
25061
25161
|
const dir = resolveStoreDir();
|
|
25062
25162
|
ensureSafeDirectory(dir);
|
|
25063
|
-
const lockDir =
|
|
25163
|
+
const lockDir = path27.join(dir, LOCK_DIRNAME);
|
|
25064
25164
|
return withDirectoryLock(
|
|
25065
25165
|
{ lockDir, ownerFileName: LOCK_OWNER_FILENAME },
|
|
25066
25166
|
operation
|
|
@@ -25073,20 +25173,20 @@ function readCarriers() {
|
|
|
25073
25173
|
return readCarriersFromDir(resolveStoreDir());
|
|
25074
25174
|
}
|
|
25075
25175
|
function readCarriersFromDir(dir) {
|
|
25076
|
-
const filePath =
|
|
25176
|
+
const filePath = path27.join(dir, FILENAME);
|
|
25077
25177
|
let fd;
|
|
25078
25178
|
try {
|
|
25079
|
-
const stat5 =
|
|
25179
|
+
const stat5 = fs14.lstatSync(filePath);
|
|
25080
25180
|
if (!stat5.isFile() || stat5.isSymbolicLink()) return {};
|
|
25081
|
-
fd =
|
|
25082
|
-
if (!
|
|
25083
|
-
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")));
|
|
25084
25184
|
} catch {
|
|
25085
25185
|
return {};
|
|
25086
25186
|
} finally {
|
|
25087
25187
|
if (fd !== void 0) {
|
|
25088
25188
|
try {
|
|
25089
|
-
|
|
25189
|
+
fs14.closeSync(fd);
|
|
25090
25190
|
} catch {
|
|
25091
25191
|
}
|
|
25092
25192
|
}
|
|
@@ -25094,7 +25194,7 @@ function readCarriersFromDir(dir) {
|
|
|
25094
25194
|
}
|
|
25095
25195
|
function recordLastLocalWriteFingerprint(filePath, generation) {
|
|
25096
25196
|
try {
|
|
25097
|
-
const st =
|
|
25197
|
+
const st = fs14.statSync(filePath);
|
|
25098
25198
|
runtimeState.lastLocalWriteFingerprint = { generation, mtimeMs: st.mtimeMs, size: st.size };
|
|
25099
25199
|
} catch {
|
|
25100
25200
|
runtimeState.lastLocalWriteFingerprint = { generation, mtimeMs: 0, size: 0 };
|
|
@@ -25102,7 +25202,7 @@ function recordLastLocalWriteFingerprint(filePath, generation) {
|
|
|
25102
25202
|
}
|
|
25103
25203
|
function writeCarriers(s) {
|
|
25104
25204
|
const dir = resolveStoreDir();
|
|
25105
|
-
const filePath =
|
|
25205
|
+
const filePath = path27.join(dir, FILENAME);
|
|
25106
25206
|
const next = serializeFleetCarriers(s);
|
|
25107
25207
|
writeAtomicSync(filePath, `${JSON.stringify(next, null, 2)}
|
|
25108
25208
|
`, { fsync: false, mode: 420 });
|
|
@@ -25127,7 +25227,7 @@ function sanitizeFleetCarriers(value) {
|
|
|
25127
25227
|
}
|
|
25128
25228
|
function unlinkStaleSubagentModeFile(dir) {
|
|
25129
25229
|
try {
|
|
25130
|
-
|
|
25230
|
+
fs14.unlinkSync(path27.join(dir, STALE_SUBAGENT_MODE_FILENAME));
|
|
25131
25231
|
} catch {
|
|
25132
25232
|
}
|
|
25133
25233
|
}
|
|
@@ -25656,7 +25756,7 @@ var CHRONICLE_DEFAULTS = {
|
|
|
25656
25756
|
};
|
|
25657
25757
|
var CARRIER_METADATA = {
|
|
25658
25758
|
// ── Tier 1: Routing ──
|
|
25659
|
-
title: "
|
|
25759
|
+
title: "Chief Knowledge Officer",
|
|
25660
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.",
|
|
25661
25761
|
category: "operations",
|
|
25662
25762
|
whenToUse: [
|
|
@@ -25669,7 +25769,7 @@ var CARRIER_METADATA = {
|
|
|
25669
25769
|
"before implementation and verification are complete",
|
|
25670
25770
|
"code modification (\u2192genesis) or code review (\u2192sentinel)",
|
|
25671
25771
|
"architectural judgment (\u2192nimitz) or release-scope planning decisions (\u2192kirov)",
|
|
25672
|
-
"Fleet Wiki patch approval \u2014
|
|
25772
|
+
"Fleet Wiki patch approval \u2014 host agent retains the wiki_patch_queue approve/reject gate"
|
|
25673
25773
|
],
|
|
25674
25774
|
requestBlocks: [
|
|
25675
25775
|
{
|
|
@@ -25705,18 +25805,18 @@ var CARRIER_METADATA = {
|
|
|
25705
25805
|
],
|
|
25706
25806
|
// ── Tier 2: Composition ──
|
|
25707
25807
|
permissions: [
|
|
25708
|
-
"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.",
|
|
25709
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).",
|
|
25710
|
-
"MUST treat the
|
|
25711
|
-
"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).",
|
|
25712
25812
|
"MUST NOT make go/no-go, release timing, or release-scope decisions (escalate to Kirov/Nimitz). May detect and document breaking changes only."
|
|
25713
25813
|
],
|
|
25714
25814
|
outputFormat: `Deliver the documentation artifact directly.
|
|
25715
25815
|
[Codebase Doc] Write the .md/AGENTS.md/README/CHANGELOG file(s) directly via filesystem tools.
|
|
25716
|
-
[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.
|
|
25717
25817
|
After delivery, provide a brief completion report.
|
|
25718
25818
|
[Required] always include:
|
|
25719
|
-
**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).
|
|
25720
25820
|
**Documents written / Patches enqueued** \u2014 Each file created/modified (Codebase Doc) OR each patch_id enqueued (Fleet Wiki) with target path.
|
|
25721
25821
|
**Cascade .md audit** \u2014 (Codebase Doc only) Each .md inspected: path, status (updated / already consistent / not applicable), 1-line summary if updated.
|
|
25722
25822
|
[If applicable] omit if not relevant:
|
|
@@ -25725,10 +25825,10 @@ After delivery, provide a brief completion report.
|
|
|
25725
25825
|
Keep the completion report concise \u2014 the documentation/patch itself is the primary deliverable.`,
|
|
25726
25826
|
principles: [
|
|
25727
25827
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25728
|
-
"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
|
|
25729
|
-
"[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.",
|
|
25730
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.",
|
|
25731
|
-
"[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.",
|
|
25732
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.",
|
|
25733
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.",
|
|
25734
25834
|
"Change-impact documentation must be factual and observable \u2014 never recommend whether a change should ship, be reverted, or be delayed."
|
|
@@ -25750,8 +25850,8 @@ var GENESIS_DEFAULTS = {
|
|
|
25750
25850
|
};
|
|
25751
25851
|
var CARRIER_METADATA2 = {
|
|
25752
25852
|
// ── Tier 1: Routing ──
|
|
25753
|
-
title: "
|
|
25754
|
-
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.",
|
|
25755
25855
|
category: "operations",
|
|
25756
25856
|
whenToUse: [
|
|
25757
25857
|
"new features",
|
|
@@ -25778,14 +25878,14 @@ var CARRIER_METADATA2 = {
|
|
|
25778
25878
|
// ── Tier 2: Composition ──
|
|
25779
25879
|
permissions: [
|
|
25780
25880
|
"Full access to the codebase \u2014 read, write, and execute commands.",
|
|
25781
|
-
"Owns implementation details (internal helper structure, code organization, local naming) ONLY within the design boundaries set by the
|
|
25782
|
-
"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.",
|
|
25783
25883
|
"MUST NOT silently re-plan, expand scope, invent alternative workflows, or shrink the assigned work beyond what the instructions specify.",
|
|
25784
25884
|
"MUST NOT silently absorb Kirov's planning role or Nimitz's architecture arbitration role when those inputs are clearly missing."
|
|
25785
25885
|
],
|
|
25786
25886
|
principles: [
|
|
25787
25887
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25788
|
-
"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.",
|
|
25789
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.",
|
|
25790
25890
|
"On ambiguity or apparent conflict in the instructions, MUST report back and request clarification instead of choosing autonomously.",
|
|
25791
25891
|
"Follow planning artifacts when provided \u2014 do not re-plan work that Kirov has already structured unless the input is clearly invalid.",
|
|
@@ -25819,8 +25919,8 @@ var KIROV_DEFAULTS = {
|
|
|
25819
25919
|
};
|
|
25820
25920
|
var CARRIER_METADATA3 = {
|
|
25821
25921
|
// ── Tier 1: Routing ──
|
|
25822
|
-
title: "
|
|
25823
|
-
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.",
|
|
25824
25924
|
category: "planning",
|
|
25825
25925
|
whenToUse: [
|
|
25826
25926
|
"structured .fleet/plans/*.md plan_file requests, PRD decomposition, or Ohio-executable execution plans",
|
|
@@ -25828,14 +25928,14 @@ var CARRIER_METADATA3 = {
|
|
|
25828
25928
|
"medium/large refactors, migrations, cross-module work, or materially ambiguous requirements where planning must close gaps before execution"
|
|
25829
25929
|
],
|
|
25830
25930
|
whenNotToUse: [
|
|
25831
|
-
"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)",
|
|
25832
25932
|
"direct code implementation, single-shot (\u2192genesis) or plan-driven (\u2192ohio)",
|
|
25833
|
-
"final architecture arbitration (\u2192nimitz) or
|
|
25933
|
+
"final architecture arbitration (\u2192nimitz) or dispatches needing reconnaissance first (\u2192vanguard/tempest)"
|
|
25834
25934
|
],
|
|
25835
25935
|
requestBlocks: [
|
|
25836
25936
|
{ tag: "goal", hint: "What the user wants to build, fix, or achieve \u2014 specific feature, PRD, behavior, and any stated constraints.", required: true },
|
|
25837
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 },
|
|
25838
|
-
{ 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 },
|
|
25839
25939
|
{ tag: "constraints", hint: "Business rules, tech stack requirements, scope boundaries, fixed decisions, or explicit exclusions the plan must respect.", required: false },
|
|
25840
25940
|
{ tag: "intent_type", hint: "If known: Refactoring | Build from Scratch | Mid-sized | Collaborative | Architecture Follow-through | Research-to-Plan.", required: false }
|
|
25841
25941
|
],
|
|
@@ -25843,27 +25943,31 @@ var CARRIER_METADATA3 = {
|
|
|
25843
25943
|
// ── Tier 2: Composition ──
|
|
25844
25944
|
permissions: [
|
|
25845
25945
|
"CRITICAL: Write access strictly limited to .fleet/plans/*.md and .fleet/drafts/*.md. NEVER modify source code, configs, or any non-markdown file.",
|
|
25846
|
-
"MUST honor exact provided .fleet/plans/*.md paths. Success means creating or updating
|
|
25847
|
-
"MUST return unresolved architecture choices, system-design trade-offs, and technical path decisions to the
|
|
25848
|
-
"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."
|
|
25849
25949
|
],
|
|
25850
25950
|
outputFormat: `After completing the plan, provide a structured plan summary.
|
|
25851
25951
|
[Required] always include:
|
|
25852
25952
|
**Plan file** \u2014 Exact generated or updated .fleet/plans/{name}.md path.
|
|
25853
|
-
**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.
|
|
25854
25955
|
**Scope: IN** \u2014 What is explicitly included in the plan.
|
|
25855
25956
|
**Scope: OUT** \u2014 What is explicitly excluded.
|
|
25856
25957
|
**Next step** \u2014 Run \`/start-work {name}\` to execute the plan.
|
|
25857
25958
|
[If applicable] omit if not relevant:
|
|
25858
25959
|
**Blockers** \u2014 Why no plan file was written or why the schema cannot be filled.
|
|
25859
|
-
**
|
|
25960
|
+
**Host Direction Needed** \u2014 Architecture, trade-off, or path choices needing confirmation.
|
|
25860
25961
|
Keep the summary concise \u2014 bullets and short lines only. No narrative paragraphs.`,
|
|
25861
25962
|
principles: [
|
|
25862
25963
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25863
25964
|
"Clarify only to unlock planning \u2014 ask the minimum questions needed to produce a reliable execution plan.",
|
|
25864
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.",
|
|
25865
|
-
'The .fleet/plans/*.md file MUST contain this exact default Markdown template unless the
|
|
25866
|
-
"
|
|
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.",
|
|
25867
25971
|
"Optimize for direct execution from the resulting plan_file."
|
|
25868
25972
|
]
|
|
25869
25973
|
};
|
|
@@ -25883,8 +25987,8 @@ var NIMITZ_DEFAULTS = {
|
|
|
25883
25987
|
};
|
|
25884
25988
|
var CARRIER_METADATA4 = {
|
|
25885
25989
|
// ── Tier 1: Routing ──
|
|
25886
|
-
title: "
|
|
25887
|
-
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.",
|
|
25888
25992
|
category: "strategy",
|
|
25889
25993
|
whenToUse: [
|
|
25890
25994
|
"architecture and design decisions",
|
|
@@ -25907,7 +26011,7 @@ var CARRIER_METADATA4 = {
|
|
|
25907
26011
|
// ── Tier 2: Composition ──
|
|
25908
26012
|
permissions: [
|
|
25909
26013
|
"CRITICAL: Strictly read-only. NEVER delegate code modification or file editing to this carrier.",
|
|
25910
|
-
"CRITICAL: NEVER
|
|
26014
|
+
"CRITICAL: NEVER dispatch Nimitz without prior reconnaissance \u2014 if recon is needed, dispatch vanguard/tempest FIRST. Hard prerequisite, not a suggestion.",
|
|
25911
26015
|
"Full access to read the codebase and execute read-only commands for analysis.",
|
|
25912
26016
|
"MUST NOT decompose work into task waves, delivery schedules, or implementation checklists \u2014 handoff belongs to Kirov."
|
|
25913
26017
|
],
|
|
@@ -25948,8 +26052,8 @@ var OHIO_DEFAULTS = {
|
|
|
25948
26052
|
};
|
|
25949
26053
|
var CARRIER_METADATA5 = {
|
|
25950
26054
|
// ── Tier 1: Routing ──
|
|
25951
|
-
title: "
|
|
25952
|
-
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.",
|
|
25953
26057
|
category: "planning",
|
|
25954
26058
|
whenToUse: [
|
|
25955
26059
|
"multi-wave builds driven by an explicit plan_file",
|
|
@@ -25958,13 +26062,14 @@ var CARRIER_METADATA5 = {
|
|
|
25958
26062
|
"cross-module coordinated changes following a Kirov plan"
|
|
25959
26063
|
],
|
|
25960
26064
|
whenNotToUse: [
|
|
25961
|
-
"single-file edits or
|
|
26065
|
+
"single-file edits or host-directed single-shot tasks (\u2192genesis)",
|
|
25962
26066
|
"architecture decisions (\u2192nimitz)",
|
|
25963
26067
|
"planning work itself (\u2192kirov)",
|
|
25964
26068
|
"reconnaissance before planning (\u2192vanguard/tempest)"
|
|
25965
26069
|
],
|
|
25966
26070
|
requestBlocks: [
|
|
25967
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 },
|
|
25968
26073
|
{ tag: "objective", hint: "Optional brief restatement of the overarching goal for context anchoring.", required: false },
|
|
25969
26074
|
{ tag: "scope", hint: "Optional explicit scope boundaries if narrower than the plan_file's full coverage.", required: false },
|
|
25970
26075
|
{ tag: "constraints", hint: "Optional hard constraints, deadlines, or compatibility requirements that override or supplement the plan.", required: false }
|
|
@@ -25974,7 +26079,9 @@ var CARRIER_METADATA5 = {
|
|
|
25974
26079
|
permissions: [
|
|
25975
26080
|
"Full access to the codebase \u2014 read, write, and execute commands.",
|
|
25976
26081
|
"MUST consult plan_file as the authoritative execution contract \u2014 plan steps are not optional or negotiable.",
|
|
25977
|
-
"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.",
|
|
25978
26085
|
"MUST NOT silently re-plan, skip steps, invent new workflow paths, or expand scope beyond what the plan_file specifies.",
|
|
25979
26086
|
"On genuine blockers (ambiguous step, missing dependency, environmental failure), MUST report back and request re-direction instead of fabricating workarounds."
|
|
25980
26087
|
],
|
|
@@ -25982,13 +26089,17 @@ var CARRIER_METADATA5 = {
|
|
|
25982
26089
|
CARRIER_JOBS_SELF_CALL_HINT,
|
|
25983
26090
|
"Read plan_file as the binding execution contract \u2014 do not deviate, re-plan, or skip steps.",
|
|
25984
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.",
|
|
25985
26094
|
"Execute waves in the declared order; preserve QA checkpoints between waves and do not collapse them.",
|
|
25986
|
-
"
|
|
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.",
|
|
25987
26097
|
"Escalate genuine blockers (ambiguous step, missing dependency, environmental failure) instead of fabricating workarounds.",
|
|
25988
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)."
|
|
25989
26099
|
],
|
|
25990
26100
|
outputFormat: `After completing the assigned wave(s), provide a structured wave-completion report.
|
|
25991
26101
|
[Required] always include:
|
|
26102
|
+
**Execution scope** \u2014 \`all\` or the exact Wave/Lane ID executed from the plan's Dispatch Manifest.
|
|
25992
26103
|
**Wave(s) executed** \u2014 Ordered list of wave/step IDs from the plan_file actually completed.
|
|
25993
26104
|
**Changes** \u2014 Every file created/modified/deleted with a 1-line summary each.
|
|
25994
26105
|
**QA results** \u2014 Outcome of each wave's QA checkpoint (pass/fail with detail).
|
|
@@ -26014,8 +26125,8 @@ var SENTINEL_DEFAULTS = {
|
|
|
26014
26125
|
};
|
|
26015
26126
|
var CARRIER_METADATA6 = {
|
|
26016
26127
|
// ── Tier 1: Routing ──
|
|
26017
|
-
title: "
|
|
26018
|
-
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.",
|
|
26019
26130
|
category: "strategy",
|
|
26020
26131
|
whenToUse: [
|
|
26021
26132
|
"code review, bug hunting, quality audits, test execution",
|
|
@@ -26071,8 +26182,8 @@ var TEMPEST_DEFAULTS = {
|
|
|
26071
26182
|
};
|
|
26072
26183
|
var CARRIER_METADATA7 = {
|
|
26073
26184
|
// ── Tier 1: Routing ──
|
|
26074
|
-
title: "
|
|
26075
|
-
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.",
|
|
26076
26187
|
category: "operations",
|
|
26077
26188
|
whenToUse: [
|
|
26078
26189
|
"external GitHub repo investigation (library internals, upstream dependencies)",
|
|
@@ -26127,8 +26238,8 @@ var VANGUARD_DEFAULTS = {
|
|
|
26127
26238
|
};
|
|
26128
26239
|
var CARRIER_METADATA8 = {
|
|
26129
26240
|
// ── Tier 1: Routing ──
|
|
26130
|
-
title: "
|
|
26131
|
-
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.",
|
|
26132
26243
|
category: "operations",
|
|
26133
26244
|
whenToUse: [
|
|
26134
26245
|
"codebase reconnaissance \u2014 exploration, multi-file scanning, symbol tracing",
|
|
@@ -31947,7 +32058,7 @@ function resolveDispatchCwd(rawCwd, fallbackCwd) {
|
|
|
31947
32058
|
if (rawCwd === void 0) return { ok: true, cwd: fallbackCwd };
|
|
31948
32059
|
const trimmed = rawCwd.trim();
|
|
31949
32060
|
if (trimmed.length === 0) return { ok: true, cwd: fallbackCwd };
|
|
31950
|
-
if (!
|
|
32061
|
+
if (!path27__default.isAbsolute(trimmed)) {
|
|
31951
32062
|
return {
|
|
31952
32063
|
ok: false,
|
|
31953
32064
|
error: `Invalid cwd "${rawCwd}": must be an absolute path. Provide an absolute directory or omit cwd to use the host session cwd.`
|
|
@@ -32422,10 +32533,103 @@ function requireCarrierConfig(registry3, carrierId) {
|
|
|
32422
32533
|
if (!config2) throw new Error(`Carrier not found: ${carrierId}`);
|
|
32423
32534
|
return config2;
|
|
32424
32535
|
}
|
|
32425
|
-
|
|
32426
|
-
|
|
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.";
|
|
32427
32543
|
var MIN_CONSOLE_STATIC_PORT = 1024;
|
|
32428
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;
|
|
32429
32633
|
var GLOBAL_SETTINGS_API_CATALOG = [
|
|
32430
32634
|
{
|
|
32431
32635
|
method: "GET",
|
|
@@ -32481,22 +32685,34 @@ async function mutateGlobalSettings(req, res, deps) {
|
|
|
32481
32685
|
deps.writeJson(res, 400, { error: "invalid_console_port_mode" });
|
|
32482
32686
|
return;
|
|
32483
32687
|
}
|
|
32484
|
-
if (body.consoleStaticPort !== void 0 && body.consoleStaticPort !== null && !
|
|
32688
|
+
if (body.consoleStaticPort !== void 0 && body.consoleStaticPort !== null && !isValidConsoleStaticPort2(body.consoleStaticPort)) {
|
|
32485
32689
|
deps.writeJson(res, 400, { error: "invalid_console_static_port" });
|
|
32486
32690
|
return;
|
|
32487
32691
|
}
|
|
32488
|
-
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") {
|
|
32489
32697
|
deps.writeJson(res, 400, { error: "invalid_theme" });
|
|
32490
32698
|
return;
|
|
32491
32699
|
}
|
|
32700
|
+
if (!isUiFontSettingsOrUndefined(body.uiFont)) {
|
|
32701
|
+
deps.writeJson(res, 400, { error: "invalid_ui_font" });
|
|
32702
|
+
return;
|
|
32703
|
+
}
|
|
32492
32704
|
const updated = deps.consoleSettingsStore.update((current) => ({
|
|
32705
|
+
...current,
|
|
32493
32706
|
version: 1,
|
|
32494
32707
|
general: {
|
|
32495
32708
|
...current.general,
|
|
32496
32709
|
...body.consolePortMode === "dynamic" || body.consolePortMode === "static" ? { consolePortMode: body.consolePortMode } : {},
|
|
32497
|
-
...
|
|
32498
|
-
...body.
|
|
32499
|
-
|
|
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
|
|
32500
32716
|
}));
|
|
32501
32717
|
const response = { state: toGlobalSettingsState(updated) };
|
|
32502
32718
|
deps.writeJson(res, 200, response);
|
|
@@ -32506,77 +32722,21 @@ function toGlobalSettingsState(data) {
|
|
|
32506
32722
|
return {
|
|
32507
32723
|
consolePortMode: general.consolePortMode ?? "dynamic",
|
|
32508
32724
|
consoleStaticPort: general.consoleStaticPort ?? null,
|
|
32509
|
-
|
|
32725
|
+
language: general.language ?? "auto",
|
|
32726
|
+
theme: general.theme ?? "instrument",
|
|
32727
|
+
uiFont: general.uiFont ?? DEFAULT_UI_FONT_SETTINGS
|
|
32510
32728
|
};
|
|
32511
32729
|
}
|
|
32512
32730
|
function isJsonRequest2(req) {
|
|
32513
32731
|
const contentType = req.headers["content-type"];
|
|
32514
32732
|
return typeof contentType === "string" && contentType.toLowerCase().split(";")[0]?.trim() === "application/json";
|
|
32515
32733
|
}
|
|
32516
|
-
function isValidConsoleStaticPort(value) {
|
|
32517
|
-
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT && value <= MAX_CONSOLE_STATIC_PORT;
|
|
32518
|
-
}
|
|
32519
|
-
var PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
32520
|
-
var SETTINGS_VERSION = 1;
|
|
32521
|
-
var SETTINGS_LOCK_DIR_NAME = "settings.lock";
|
|
32522
|
-
var SETTINGS_LOCK_OWNER_FILE_NAME = "owner.json";
|
|
32523
|
-
var SETTINGS_TEMP_PREFIX = ".settings.";
|
|
32524
|
-
var MIN_CONSOLE_STATIC_PORT2 = 1024;
|
|
32525
|
-
var MAX_CONSOLE_STATIC_PORT2 = 65535;
|
|
32526
|
-
function createConsoleSettingsStore(deps = {}) {
|
|
32527
|
-
const paths = deps.paths ?? createConsoleDataPaths();
|
|
32528
|
-
const createStore = deps.createStore ?? createDurableJsonStore;
|
|
32529
|
-
return createStore({
|
|
32530
|
-
filePath: paths.settingsFile,
|
|
32531
|
-
lockDir: path25__default.join(paths.dir, SETTINGS_LOCK_DIR_NAME),
|
|
32532
|
-
lockOwnerFileName: SETTINGS_LOCK_OWNER_FILE_NAME,
|
|
32533
|
-
now: deps.now,
|
|
32534
|
-
sanitize: sanitizeConsoleSettingsData,
|
|
32535
|
-
sensitivity: "sensitive",
|
|
32536
|
-
tempCleanupPrefix: SETTINGS_TEMP_PREFIX
|
|
32537
|
-
});
|
|
32538
|
-
}
|
|
32539
|
-
function sanitizeConsoleSettingsData(value) {
|
|
32540
|
-
if (!isRecord4(value)) return emptyConsoleSettingsData();
|
|
32541
|
-
if (value.version !== SETTINGS_VERSION) return emptyConsoleSettingsData();
|
|
32542
|
-
const general = readConsoleGeneralSettings(value.general);
|
|
32543
|
-
const plugins = readConsolePluginSettings(value.plugins);
|
|
32544
|
-
return {
|
|
32545
|
-
version: SETTINGS_VERSION,
|
|
32546
|
-
general: general ?? {},
|
|
32547
|
-
plugins: plugins ?? {}
|
|
32548
|
-
};
|
|
32549
|
-
}
|
|
32550
|
-
function emptyConsoleSettingsData() {
|
|
32551
|
-
return { version: 1, general: {}, plugins: {} };
|
|
32552
|
-
}
|
|
32553
|
-
function readConsoleGeneralSettings(value) {
|
|
32554
|
-
if (!isRecord4(value)) return null;
|
|
32555
|
-
const consolePortMode = value.consolePortMode === "dynamic" || value.consolePortMode === "static" ? value.consolePortMode : void 0;
|
|
32556
|
-
const consoleStaticPort = isValidConsoleStaticPort2(value.consoleStaticPort) ? value.consoleStaticPort : void 0;
|
|
32557
|
-
const theme = value.theme === "maritime" || value.theme === "carbon" ? value.theme : void 0;
|
|
32558
|
-
return {
|
|
32559
|
-
...consolePortMode !== void 0 ? { consolePortMode } : {},
|
|
32560
|
-
...consoleStaticPort !== void 0 ? { consoleStaticPort } : {},
|
|
32561
|
-
...theme !== void 0 ? { theme } : {}
|
|
32562
|
-
};
|
|
32563
|
-
}
|
|
32564
|
-
function readConsolePluginSettings(value) {
|
|
32565
|
-
if (!isRecord4(value)) return null;
|
|
32566
|
-
const result = {};
|
|
32567
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
32568
|
-
if (!PLUGIN_ID_PATTERN.test(key)) continue;
|
|
32569
|
-
if (!isRecord4(entry)) continue;
|
|
32570
|
-
result[key] = entry;
|
|
32571
|
-
}
|
|
32572
|
-
return result;
|
|
32573
|
-
}
|
|
32574
|
-
function isRecord4(value) {
|
|
32575
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32576
|
-
}
|
|
32577
32734
|
function isValidConsoleStaticPort2(value) {
|
|
32578
32735
|
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT2 && value <= MAX_CONSOLE_STATIC_PORT2;
|
|
32579
32736
|
}
|
|
32737
|
+
function isUiFontSettingsOrUndefined(value) {
|
|
32738
|
+
return value === void 0 || isUiFontSettings(value);
|
|
32739
|
+
}
|
|
32580
32740
|
|
|
32581
32741
|
// core/host/plugin-settings-routes.ts
|
|
32582
32742
|
var PLUGIN_SETTINGS_PREFIX = "/api/v1/settings/plugins/";
|
|
@@ -32657,6 +32817,133 @@ function isJsonRequest3(req) {
|
|
|
32657
32817
|
const contentType = req.headers["content-type"];
|
|
32658
32818
|
return typeof contentType === "string" && contentType.toLowerCase().split(";")[0]?.trim() === "application/json";
|
|
32659
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
|
+
}
|
|
32660
32947
|
|
|
32661
32948
|
// core/host/api-catalog.ts
|
|
32662
32949
|
var compareApiCatalogEntries = (left, right) => left.category.localeCompare(right.category) || left.path.localeCompare(right.path) || left.method.localeCompare(right.method);
|
|
@@ -32665,9 +32952,115 @@ function buildApiCatalog() {
|
|
|
32665
32952
|
...SERVER_API_CATALOG,
|
|
32666
32953
|
...CARRIER_SETTINGS_API_CATALOG,
|
|
32667
32954
|
...GLOBAL_SETTINGS_API_CATALOG,
|
|
32668
|
-
...PLUGIN_SETTINGS_API_CATALOG
|
|
32955
|
+
...PLUGIN_SETTINGS_API_CATALOG,
|
|
32956
|
+
...SYSTEM_FONTS_API_CATALOG
|
|
32669
32957
|
].slice().sort(compareApiCatalogEntries);
|
|
32670
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
|
+
}
|
|
32671
33064
|
|
|
32672
33065
|
// ../../packages/fleet-wiki/src/boundaries.ts
|
|
32673
33066
|
var FLEET_WIKI_ENTRY_BEGIN = "<<<FLEET_WIKI_ENTRY_BEGIN";
|
|
@@ -33075,18 +33468,18 @@ Always read \`schema/wiki-schema.md\` directly before composing or revising entr
|
|
|
33075
33468
|
`;
|
|
33076
33469
|
async function ensureWorkspaceSchema(paths) {
|
|
33077
33470
|
await mkdir(paths.schemaDir, { recursive: true });
|
|
33078
|
-
await writeDefaultFileIfMissing(
|
|
33079
|
-
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);
|
|
33080
33473
|
await writeDefaultFileIfMissing(buildTemplatePath(paths, "prd"), DEFAULT_TEMPLATE_PRD);
|
|
33081
33474
|
return readWorkspaceSchemaSummary(paths);
|
|
33082
33475
|
}
|
|
33083
33476
|
async function ensureWorkspaceDoctrine(paths) {
|
|
33084
33477
|
await mkdir(paths.root, { recursive: true });
|
|
33085
|
-
await writeDefaultFileIfMissing(
|
|
33478
|
+
await writeDefaultFileIfMissing(path27__default.join(paths.root, WORKSPACE_KNOWLEDGE_AGENTS_FILENAME), DEFAULT_WORKSPACE_KNOWLEDGE_AGENTS);
|
|
33086
33479
|
}
|
|
33087
33480
|
async function readWorkspaceSchemaSummary(paths) {
|
|
33088
|
-
const agentsPath =
|
|
33089
|
-
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);
|
|
33090
33483
|
const wikiSchemaContent = await tryReadFile(wikiSchemaPath);
|
|
33091
33484
|
const templates = await scanTemplates(paths);
|
|
33092
33485
|
if (wikiSchemaContent === null) {
|
|
@@ -33125,7 +33518,7 @@ async function scanTemplates(paths) {
|
|
|
33125
33518
|
if (!entry.name.startsWith(WORKSPACE_TEMPLATE_PREFIX) || !entry.name.endsWith(WORKSPACE_TEMPLATE_SUFFIX)) continue;
|
|
33126
33519
|
const id = entry.name.slice(WORKSPACE_TEMPLATE_PREFIX.length, -WORKSPACE_TEMPLATE_SUFFIX.length);
|
|
33127
33520
|
if (!id) continue;
|
|
33128
|
-
const filePath =
|
|
33521
|
+
const filePath = path27__default.join(paths.schemaDir, entry.name);
|
|
33129
33522
|
try {
|
|
33130
33523
|
const parsed = parseTemplateMarkdown(await readFile(filePath, "utf8"));
|
|
33131
33524
|
templates.push({
|
|
@@ -33159,7 +33552,7 @@ async function validateTemplateCompliance(paths, templateId, body) {
|
|
|
33159
33552
|
}
|
|
33160
33553
|
}
|
|
33161
33554
|
function inferTemplateIdFromTarget(target, knownTemplateIds) {
|
|
33162
|
-
const basename =
|
|
33555
|
+
const basename = path27__default.basename(target, ".md");
|
|
33163
33556
|
const ids = knownTemplateIds ?? DEFAULT_TEMPLATE_IDS;
|
|
33164
33557
|
const sorted = [...ids].sort((a, b) => b.length - a.length);
|
|
33165
33558
|
for (const id of sorted) {
|
|
@@ -33188,7 +33581,7 @@ function extractSchemaSummary(content) {
|
|
|
33188
33581
|
return lines.find((line) => line.length > 0 && !line.startsWith("#")) ?? "Workspace schema is present.";
|
|
33189
33582
|
}
|
|
33190
33583
|
function buildTemplatePath(paths, id) {
|
|
33191
|
-
return
|
|
33584
|
+
return path27__default.join(paths.schemaDir, `${WORKSPACE_TEMPLATE_PREFIX}${id}${WORKSPACE_TEMPLATE_SUFFIX}`);
|
|
33192
33585
|
}
|
|
33193
33586
|
function parseTemplateMarkdown(content) {
|
|
33194
33587
|
const normalized = content.replace(/\r\n/g, "\n");
|
|
@@ -33215,16 +33608,16 @@ function isAlreadyExistsError(error51) {
|
|
|
33215
33608
|
|
|
33216
33609
|
// ../../packages/fleet-wiki/src/paths.ts
|
|
33217
33610
|
function resolveMemoryPaths(cwd) {
|
|
33218
|
-
const root =
|
|
33611
|
+
const root = path27__default.join(cwd, KNOWLEDGE_ROOT_DIRNAME);
|
|
33219
33612
|
return {
|
|
33220
33613
|
root,
|
|
33221
|
-
rawDir:
|
|
33222
|
-
wikiDir:
|
|
33223
|
-
schemaDir:
|
|
33224
|
-
queueDir:
|
|
33225
|
-
archiveDir:
|
|
33226
|
-
conflictsDir:
|
|
33227
|
-
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)
|
|
33228
33621
|
};
|
|
33229
33622
|
}
|
|
33230
33623
|
async function ensureMemoryRoot(paths) {
|
|
@@ -33239,10 +33632,10 @@ async function ensureMemoryRoot(paths) {
|
|
|
33239
33632
|
await ensureWorkspaceDoctrine(paths);
|
|
33240
33633
|
}
|
|
33241
33634
|
function getIndexMarkdownFile(paths) {
|
|
33242
|
-
return
|
|
33635
|
+
return path27__default.join(paths.wikiDir, INDEX_MD_FILENAME);
|
|
33243
33636
|
}
|
|
33244
33637
|
function getLogFile(paths) {
|
|
33245
|
-
return
|
|
33638
|
+
return path27__default.join(paths.root, LOG_MD_FILENAME);
|
|
33246
33639
|
}
|
|
33247
33640
|
|
|
33248
33641
|
// ../../packages/fleet-wiki/src/log.ts
|
|
@@ -33308,12 +33701,12 @@ async function readWikiEntry(id, paths) {
|
|
|
33308
33701
|
const index = await loadIndex(paths);
|
|
33309
33702
|
const indexed = index[id];
|
|
33310
33703
|
if (indexed) {
|
|
33311
|
-
const resolved =
|
|
33312
|
-
const rel =
|
|
33313
|
-
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;
|
|
33314
33707
|
return readMarkdownFile(resolved);
|
|
33315
33708
|
}
|
|
33316
|
-
const fallbackPath =
|
|
33709
|
+
const fallbackPath = path27__default.join(paths.wikiDir, `${id}.md`);
|
|
33317
33710
|
if (await pathExists(fallbackPath)) {
|
|
33318
33711
|
return readMarkdownFile(fallbackPath);
|
|
33319
33712
|
}
|
|
@@ -33324,20 +33717,20 @@ async function readWikiEntry(id, paths) {
|
|
|
33324
33717
|
async function writeWikiEntryAtTarget(entry, target, paths) {
|
|
33325
33718
|
await ensureMemoryRoot(paths);
|
|
33326
33719
|
assertSafeEntryId(entry.id);
|
|
33327
|
-
const absoluteTarget =
|
|
33328
|
-
const relativeTarget =
|
|
33329
|
-
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)) {
|
|
33330
33723
|
throw new Error(`[fleet-wiki] wiki target escapes wiki/: ${target}`);
|
|
33331
33724
|
}
|
|
33332
33725
|
if (!absoluteTarget.endsWith(".md")) {
|
|
33333
33726
|
throw new Error(`[fleet-wiki] wiki target must end with .md: ${target}`);
|
|
33334
33727
|
}
|
|
33335
|
-
if (
|
|
33728
|
+
if (path27__default.basename(absoluteTarget, ".md") !== entry.id) {
|
|
33336
33729
|
throw new Error("wiki patch body id must match target filename");
|
|
33337
33730
|
}
|
|
33338
|
-
await mkdir(
|
|
33731
|
+
await mkdir(path27__default.dirname(absoluteTarget), { recursive: true });
|
|
33339
33732
|
await writeMarkdownAtomic(absoluteTarget, serializeWikiEntry(entry), paths);
|
|
33340
|
-
return
|
|
33733
|
+
return path27__default.join("wiki", relativeTarget).replaceAll(path27__default.sep, "/");
|
|
33341
33734
|
}
|
|
33342
33735
|
async function listWiki(paths) {
|
|
33343
33736
|
const entries = await listWikiRecords(paths);
|
|
@@ -33560,7 +33953,7 @@ function parseWikiEntry(content) {
|
|
|
33560
33953
|
async function listWikiRecords(paths) {
|
|
33561
33954
|
return listMarkdownEntriesRecursive(paths.wikiDir, paths, (content, filePath) => ({
|
|
33562
33955
|
entry: parseWikiEntry(content),
|
|
33563
|
-
path:
|
|
33956
|
+
path: path27__default.relative(paths.root, filePath).replaceAll(path27__default.sep, "/")
|
|
33564
33957
|
}));
|
|
33565
33958
|
}
|
|
33566
33959
|
async function listMarkdownEntriesRecursive(dirPath, paths, parser) {
|
|
@@ -33573,7 +33966,7 @@ async function listMarkdownEntriesRecursive(dirPath, paths, parser) {
|
|
|
33573
33966
|
}
|
|
33574
33967
|
const sortedEntries = entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
33575
33968
|
for (const entry of sortedEntries) {
|
|
33576
|
-
const filePath =
|
|
33969
|
+
const filePath = path27__default.join(dirPath, entry.name);
|
|
33577
33970
|
if (entry.isDirectory()) {
|
|
33578
33971
|
items.push(...await listMarkdownEntriesRecursive(filePath, paths, parser));
|
|
33579
33972
|
continue;
|
|
@@ -33711,8 +34104,8 @@ async function writeJsonAtomic(filePath, value, paths) {
|
|
|
33711
34104
|
}
|
|
33712
34105
|
async function writeAtomic(filePath, content, paths) {
|
|
33713
34106
|
await ensureMemoryRoot(paths);
|
|
33714
|
-
const tempPath =
|
|
33715
|
-
|
|
34107
|
+
const tempPath = path27__default.join(
|
|
34108
|
+
path27__default.dirname(filePath),
|
|
33716
34109
|
`.tmp-${process.pid}-${Date.now()}-${crypto5.randomUUID()}-${os4__default.hostname()}`
|
|
33717
34110
|
);
|
|
33718
34111
|
await writeFile(tempPath, content, "utf8");
|
|
@@ -33806,7 +34199,7 @@ function toRankedHit(entry, reason, detail, graphBoost, backlinksById) {
|
|
|
33806
34199
|
score: basePriority * 100 + statusPriority * 10 + graphScore + (detail.tagBoost ?? 0),
|
|
33807
34200
|
reason,
|
|
33808
34201
|
excerpt,
|
|
33809
|
-
path:
|
|
34202
|
+
path: path27__default.join("wiki", `${entry.id}.md`),
|
|
33810
34203
|
tags: entry.tags,
|
|
33811
34204
|
updated: entry.updated,
|
|
33812
34205
|
version: entry.version,
|
|
@@ -33916,15 +34309,15 @@ var PATCH_SET_DIRNAME = "_sets";
|
|
|
33916
34309
|
var PATCH_SET_META_FILENAME = PATCH_META_FILENAME;
|
|
33917
34310
|
function getPatchSetDir(paths, patchSetId) {
|
|
33918
34311
|
assertSafePatchSetId(patchSetId);
|
|
33919
|
-
const patchSetDir =
|
|
33920
|
-
const relative =
|
|
33921
|
-
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)) {
|
|
33922
34315
|
throw new Error(`[fleet-wiki] patch set id escapes queue/_sets: ${patchSetId}`);
|
|
33923
34316
|
}
|
|
33924
34317
|
return patchSetDir;
|
|
33925
34318
|
}
|
|
33926
34319
|
function getPatchSetMetaFile(paths, patchSetId) {
|
|
33927
|
-
return
|
|
34320
|
+
return path27__default.join(getPatchSetDir(paths, patchSetId), PATCH_SET_META_FILENAME);
|
|
33928
34321
|
}
|
|
33929
34322
|
async function readPatchSet(paths, patchSetId) {
|
|
33930
34323
|
const patchSet = await readJsonFile2(getPatchSetMetaFile(paths, patchSetId));
|
|
@@ -33947,7 +34340,7 @@ async function createConflict(input, paths) {
|
|
|
33947
34340
|
await ensureMemoryRoot(paths);
|
|
33948
34341
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
33949
34342
|
const conflictId = buildConflictId(now, input.wikiId, input.reason, input.proposed);
|
|
33950
|
-
const conflictDir =
|
|
34343
|
+
const conflictDir = path27__default.join(paths.conflictsDir, conflictId);
|
|
33951
34344
|
await mkdir(conflictDir, { recursive: true });
|
|
33952
34345
|
const meta3 = {
|
|
33953
34346
|
id: conflictId,
|
|
@@ -33967,13 +34360,13 @@ async function createConflict(input, paths) {
|
|
|
33967
34360
|
currentHash: input.currentHash,
|
|
33968
34361
|
warnings: input.warnings
|
|
33969
34362
|
};
|
|
33970
|
-
await writeJsonFile(
|
|
33971
|
-
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);
|
|
33972
34365
|
if (input.current !== void 0) {
|
|
33973
|
-
await writePatchFile(
|
|
34366
|
+
await writePatchFile(path27__default.join(conflictDir, CONFLICT_CURRENT_FILENAME), input.current, paths);
|
|
33974
34367
|
}
|
|
33975
34368
|
if (input.rawSource !== void 0) {
|
|
33976
|
-
await writePatchFile(
|
|
34369
|
+
await writePatchFile(path27__default.join(conflictDir, CONFLICT_RAW_SOURCE_FILENAME), input.rawSource, paths);
|
|
33977
34370
|
}
|
|
33978
34371
|
return {
|
|
33979
34372
|
meta: meta3,
|
|
@@ -34023,11 +34416,11 @@ async function validatePatch(patch, paths) {
|
|
|
34023
34416
|
if (!target || !summary || !proposer || !created) throw new Error("patch frontmatter is incomplete");
|
|
34024
34417
|
if (summary.length > 120) throw new Error("patch summary exceeds 120 chars");
|
|
34025
34418
|
assertCanonicalPatchTarget(target);
|
|
34026
|
-
const absoluteTarget =
|
|
34027
|
-
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) {
|
|
34028
34421
|
throw new Error("patch target escapes wiki root");
|
|
34029
34422
|
}
|
|
34030
|
-
if (!absoluteTarget.startsWith(`${paths.wikiDir}${
|
|
34423
|
+
if (!absoluteTarget.startsWith(`${paths.wikiDir}${path27__default.sep}`)) throw new Error("wiki patch must target wiki/");
|
|
34031
34424
|
assertNoSymlinkPathComponents(target, paths);
|
|
34032
34425
|
if (op === "create_wiki" && await pathExists(absoluteTarget)) {
|
|
34033
34426
|
throw new Error(
|
|
@@ -34051,7 +34444,7 @@ async function listQueue(paths) {
|
|
|
34051
34444
|
for (const id of ids) {
|
|
34052
34445
|
if (id === "_sets") continue;
|
|
34053
34446
|
try {
|
|
34054
|
-
const meta3 = await readJsonFile2(
|
|
34447
|
+
const meta3 = await readJsonFile2(path27__default.join(paths.queueDir, id, PATCH_META_FILENAME));
|
|
34055
34448
|
results.push({ id, meta: meta3 });
|
|
34056
34449
|
} catch {
|
|
34057
34450
|
continue;
|
|
@@ -34076,9 +34469,9 @@ async function resolveQueueSelection(id, paths) {
|
|
|
34076
34469
|
}
|
|
34077
34470
|
async function showQueue(id, paths) {
|
|
34078
34471
|
const selection = await resolveQueueSelection(id, paths);
|
|
34079
|
-
const queueDir =
|
|
34080
|
-
const patch = await parsePatch(await readPatchFile(
|
|
34081
|
-
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));
|
|
34082
34475
|
return { patch, meta: meta3 };
|
|
34083
34476
|
}
|
|
34084
34477
|
async function approvePatch(id, paths) {
|
|
@@ -34101,8 +34494,8 @@ async function approvePatch(id, paths) {
|
|
|
34101
34494
|
rawSourceRef: meta3.rawSourceRef,
|
|
34102
34495
|
warnings: meta3.warnings
|
|
34103
34496
|
});
|
|
34104
|
-
const queueDir =
|
|
34105
|
-
await writeJsonFile(
|
|
34497
|
+
const queueDir = path27__default.join(paths.queueDir, id);
|
|
34498
|
+
await writeJsonFile(path27__default.join(queueDir, PATCH_META_FILENAME), {
|
|
34106
34499
|
...meta3,
|
|
34107
34500
|
conflictId
|
|
34108
34501
|
}, paths);
|
|
@@ -34155,7 +34548,7 @@ async function rejectPatch(id, reason, paths) {
|
|
|
34155
34548
|
}
|
|
34156
34549
|
async function normalizeWikiEntryPatch(entry, target, paths) {
|
|
34157
34550
|
assertSafeEntryId(entry.id);
|
|
34158
|
-
if (entry.id !==
|
|
34551
|
+
if (entry.id !== path27__default.basename(target, ".md")) {
|
|
34159
34552
|
throw new Error("wiki patch body id must match target filename");
|
|
34160
34553
|
}
|
|
34161
34554
|
const inlineRawSourceRef = extractInlineRawSourceRef(entry.body);
|
|
@@ -34193,9 +34586,9 @@ function extractInlineRawSourceRef(body) {
|
|
|
34193
34586
|
}
|
|
34194
34587
|
async function validatePatchBase(patch, meta3, paths) {
|
|
34195
34588
|
if (patch.frontmatter.op !== "update_wiki" || !meta3?.baseVersion && !meta3?.baseHash) return;
|
|
34196
|
-
const wikiId =
|
|
34589
|
+
const wikiId = path27__default.basename(patch.frontmatter.target, ".md");
|
|
34197
34590
|
const currentEntry = await readWikiEntry(wikiId, paths);
|
|
34198
|
-
const currentPath =
|
|
34591
|
+
const currentPath = path27__default.join(paths.root, patch.frontmatter.target);
|
|
34199
34592
|
const currentMarkdown = await pathExists(currentPath) ? await readPatchFile(currentPath) : void 0;
|
|
34200
34593
|
if (meta3.baseVersion !== void 0 && currentEntry?.version !== meta3.baseVersion) {
|
|
34201
34594
|
throw new Error(
|
|
@@ -34254,17 +34647,17 @@ function assertCanonicalPatchTarget(target) {
|
|
|
34254
34647
|
if (target.endsWith("/")) {
|
|
34255
34648
|
throw new Error("patch target must not end with a slash");
|
|
34256
34649
|
}
|
|
34257
|
-
if (target.startsWith("/") ||
|
|
34650
|
+
if (target.startsWith("/") || path27__default.posix.isAbsolute(target) || path27__default.win32.isAbsolute(target)) {
|
|
34258
34651
|
throw new Error("patch target must be relative");
|
|
34259
34652
|
}
|
|
34260
|
-
if (
|
|
34653
|
+
if (path27__default.posix.normalize(target) !== target) {
|
|
34261
34654
|
throw new Error("patch target must not contain dot segments or redundant separators");
|
|
34262
34655
|
}
|
|
34263
34656
|
}
|
|
34264
34657
|
function canonicalApprovalLockKey(paths, target) {
|
|
34265
34658
|
const wikiRoot = realpathSync(paths.wikiDir);
|
|
34266
|
-
const relativeTarget =
|
|
34267
|
-
const targetPath =
|
|
34659
|
+
const relativeTarget = path27__default.posix.relative("wiki", target);
|
|
34660
|
+
const targetPath = path27__default.join(wikiRoot, ...relativeTarget.split("/"));
|
|
34268
34661
|
return `${wikiRoot}\0${targetPath}`.toLowerCase();
|
|
34269
34662
|
}
|
|
34270
34663
|
function canonicalPatchEditLockKey(paths, patchId) {
|
|
@@ -34280,7 +34673,7 @@ function assertNoSymlinkPathComponents(target, paths) {
|
|
|
34280
34673
|
throw error51;
|
|
34281
34674
|
}
|
|
34282
34675
|
for (const part of parts) {
|
|
34283
|
-
current =
|
|
34676
|
+
current = path27__default.join(current, part);
|
|
34284
34677
|
try {
|
|
34285
34678
|
if (lstatSync(current).isSymbolicLink()) {
|
|
34286
34679
|
throw new Error("patch target must not include symlink path components");
|
|
@@ -34295,8 +34688,8 @@ function assertSafeRawSourceRef(rawSourceRef, paths) {
|
|
|
34295
34688
|
if (!rawSourceRef.startsWith("raw/")) {
|
|
34296
34689
|
throw new Error("raw source provenance must point into raw/");
|
|
34297
34690
|
}
|
|
34298
|
-
const absoluteRef =
|
|
34299
|
-
if (!absoluteRef.startsWith(`${paths.rawDir}${
|
|
34691
|
+
const absoluteRef = path27__default.resolve(paths.root, rawSourceRef);
|
|
34692
|
+
if (!absoluteRef.startsWith(`${paths.rawDir}${path27__default.sep}`)) {
|
|
34300
34693
|
throw new Error("raw source provenance must point into raw/");
|
|
34301
34694
|
}
|
|
34302
34695
|
}
|
|
@@ -34312,14 +34705,14 @@ function classifyPatchConflict(error51) {
|
|
|
34312
34705
|
}
|
|
34313
34706
|
async function recordPatchConflict(patch, paths, reason, options) {
|
|
34314
34707
|
const now = /* @__PURE__ */ new Date();
|
|
34315
|
-
const targetPath =
|
|
34708
|
+
const targetPath = path27__default.join(paths.root, patch.frontmatter.target);
|
|
34316
34709
|
const current = await pathExists(targetPath) ? await readPatchFile(targetPath) : void 0;
|
|
34317
34710
|
const currentEntry = current ? parseStoredWikiEntry(current) : void 0;
|
|
34318
34711
|
const proposedEntry = parsePatchBodyEntry(patch.body);
|
|
34319
34712
|
const record2 = await createConflict({
|
|
34320
34713
|
reason,
|
|
34321
34714
|
target: patch.frontmatter.target,
|
|
34322
|
-
wikiId: proposedEntry?.id ??
|
|
34715
|
+
wikiId: proposedEntry?.id ?? path27__default.basename(patch.frontmatter.target, ".md"),
|
|
34323
34716
|
title: proposedEntry?.title,
|
|
34324
34717
|
proposer: patch.frontmatter.proposer,
|
|
34325
34718
|
rawSourceRef: options?.rawSourceRef,
|
|
@@ -34415,11 +34808,11 @@ function parseInlineArray(value) {
|
|
|
34415
34808
|
return inner.split(",").map((item) => item.trim().replace(/^"(.*)"$/, "$1"));
|
|
34416
34809
|
}
|
|
34417
34810
|
async function archiveQueueEntry(id, paths, meta3) {
|
|
34418
|
-
const fromDir =
|
|
34419
|
-
const toDir =
|
|
34811
|
+
const fromDir = path27__default.join(paths.queueDir, id);
|
|
34812
|
+
const toDir = path27__default.join(paths.archiveDir, id);
|
|
34420
34813
|
await removePath(toDir);
|
|
34421
34814
|
await movePath(fromDir, toDir);
|
|
34422
|
-
await writeJsonFile(
|
|
34815
|
+
await writeJsonFile(path27__default.join(toDir, PATCH_META_FILENAME), meta3, paths);
|
|
34423
34816
|
}
|
|
34424
34817
|
function buildQueueIdHelp(prefix, availableIds) {
|
|
34425
34818
|
if (availableIds.length === 0) {
|
|
@@ -35141,15 +35534,15 @@ async function canonicalizeTheaterPath(cwd) {
|
|
|
35141
35534
|
return canonicalizeTheaterPathSync(cwd);
|
|
35142
35535
|
}
|
|
35143
35536
|
function canonicalizeTheaterPathSync(cwd) {
|
|
35144
|
-
const resolved =
|
|
35537
|
+
const resolved = path27__default.resolve(cwd);
|
|
35145
35538
|
try {
|
|
35146
|
-
return
|
|
35539
|
+
return fs14__default.realpathSync.native(resolved);
|
|
35147
35540
|
} catch {
|
|
35148
35541
|
return resolved;
|
|
35149
35542
|
}
|
|
35150
35543
|
}
|
|
35151
35544
|
function theaterLabel(cwd) {
|
|
35152
|
-
return
|
|
35545
|
+
return path27__default.basename(cwd) || cwd;
|
|
35153
35546
|
}
|
|
35154
35547
|
|
|
35155
35548
|
// core/host/codex/workspaces.ts
|
|
@@ -35157,7 +35550,7 @@ var WorkspaceRegistry = class {
|
|
|
35157
35550
|
#items = /* @__PURE__ */ new Map();
|
|
35158
35551
|
#mruId = null;
|
|
35159
35552
|
async register(cwdInput, lastOpenedAt) {
|
|
35160
|
-
const cwd =
|
|
35553
|
+
const cwd = path27__default.resolve(cwdInput);
|
|
35161
35554
|
const real = await canonicalizeTheaterPath(cwd);
|
|
35162
35555
|
const paths = resolveMemoryPaths(real);
|
|
35163
35556
|
if (!await directoryExists(paths.root)) {
|
|
@@ -35173,7 +35566,7 @@ var WorkspaceRegistry = class {
|
|
|
35173
35566
|
id,
|
|
35174
35567
|
cwd,
|
|
35175
35568
|
realpath: real,
|
|
35176
|
-
label:
|
|
35569
|
+
label: path27__default.basename(cwd),
|
|
35177
35570
|
paths,
|
|
35178
35571
|
registeredAt: existing?.registeredAt ?? now,
|
|
35179
35572
|
// 복원 경로는 durable lastOpenedAt을 그대로 보존해 재시작 후에도 워크스페이스 최근성
|
|
@@ -35239,6 +35632,7 @@ var LOOPBACK_ACCESS_HOSTS = ["127.0.0.1", "::1"];
|
|
|
35239
35632
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
35240
35633
|
function createCodexGateway(deps) {
|
|
35241
35634
|
const workspaces = new WorkspaceRegistry();
|
|
35635
|
+
const workspaceOwners = /* @__PURE__ */ new Map();
|
|
35242
35636
|
let accessSets = null;
|
|
35243
35637
|
let initialWorkspace = null;
|
|
35244
35638
|
let initialWorkspaceId = null;
|
|
@@ -35268,6 +35662,10 @@ function createCodexGateway(deps) {
|
|
|
35268
35662
|
redirect(response, `${CODEX_BASE}${selected.location}`);
|
|
35269
35663
|
return true;
|
|
35270
35664
|
}
|
|
35665
|
+
if (selected.kind === "missing-workspace") {
|
|
35666
|
+
sendJson2(response, 404, { error: "workspace_not_found" });
|
|
35667
|
+
return true;
|
|
35668
|
+
}
|
|
35271
35669
|
if (selected.kind === "no-workspace") {
|
|
35272
35670
|
if (isJsonRequest4(request)) {
|
|
35273
35671
|
sendJson2(response, 404, { error: "no_workspace_registered" });
|
|
@@ -35298,18 +35696,50 @@ function createCodexGateway(deps) {
|
|
|
35298
35696
|
initialWorkspaceId = workspace.id;
|
|
35299
35697
|
return workspace;
|
|
35300
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
|
+
}
|
|
35301
35736
|
return {
|
|
35302
35737
|
getWorkspace: (id) => workspaces.get(id),
|
|
35303
35738
|
handle,
|
|
35304
35739
|
listWorkspaceRegistrations: () => workspaces.listRegistrations(),
|
|
35305
|
-
registerWorkspace
|
|
35306
|
-
|
|
35307
|
-
|
|
35308
|
-
initialWorkspace = null;
|
|
35309
|
-
initialWorkspaceId = null;
|
|
35310
|
-
}
|
|
35311
|
-
return workspaces.remove(id);
|
|
35312
|
-
}
|
|
35740
|
+
registerWorkspace,
|
|
35741
|
+
resolveWorkspaceForPath,
|
|
35742
|
+
unregisterTheaterWorkspaces
|
|
35313
35743
|
};
|
|
35314
35744
|
}
|
|
35315
35745
|
function buildAllowedAccessSets(host, port, interfaces = os4__default.networkInterfaces()) {
|
|
@@ -35337,8 +35767,10 @@ function selectWorkspace(requestUrl, workspaces, cwd) {
|
|
|
35337
35767
|
const prefixed = url2.pathname.match(/^\/w\/([^/]+)(\/.*)?$/);
|
|
35338
35768
|
if (prefixed) {
|
|
35339
35769
|
const workspace = workspaces.get(decodeURIComponent(prefixed[1] ?? ""));
|
|
35340
|
-
if (!workspace) return { kind: "redirect", location: "/" };
|
|
35341
35770
|
const suffix = prefixed[2] ?? "/";
|
|
35771
|
+
if (!workspace) {
|
|
35772
|
+
return suffix.startsWith("/api/") ? { kind: "missing-workspace" } : { kind: "redirect", location: "/" };
|
|
35773
|
+
}
|
|
35342
35774
|
url2.pathname = suffix;
|
|
35343
35775
|
return { kind: "workspace", workspace, rewrittenUrl: `${url2.pathname}${url2.search}` };
|
|
35344
35776
|
}
|
|
@@ -35491,7 +35923,7 @@ function createConsoleDurableStateStore(deps = {}) {
|
|
|
35491
35923
|
const createStore = deps.createStore ?? createDurableJsonStore;
|
|
35492
35924
|
return createStore({
|
|
35493
35925
|
filePath: paths.stateFile,
|
|
35494
|
-
lockDir:
|
|
35926
|
+
lockDir: path27__default.join(paths.dir, STATE_LOCK_DIR_NAME),
|
|
35495
35927
|
lockOwnerFileName: STATE_LOCK_OWNER_FILE_NAME,
|
|
35496
35928
|
now: deps.now,
|
|
35497
35929
|
sanitize: sanitizeDurableConsoleState,
|
|
@@ -35533,7 +35965,7 @@ function migrateV1Operation(value) {
|
|
|
35533
35965
|
const cwd = readNonEmptyString(value.cwd);
|
|
35534
35966
|
const createdAt = readFiniteNumber(value.createdAt);
|
|
35535
35967
|
if (!sessionId || !theaterId || !cwd || createdAt === null) return null;
|
|
35536
|
-
const title = readOptionalString(value.label) ?? (
|
|
35968
|
+
const title = readOptionalString(value.label) ?? (path27__default.basename(cwd) || cwd);
|
|
35537
35969
|
const payload = { cwd };
|
|
35538
35970
|
const cliId = readOptionalString(value.cliId);
|
|
35539
35971
|
const cliLabel = readOptionalString(value.cliLabel);
|
|
@@ -35580,7 +36012,11 @@ function sanitizeTheaterRegistration(value) {
|
|
|
35580
36012
|
const registeredAt = readNonEmptyString(value.registeredAt);
|
|
35581
36013
|
const lastOpenedAt = readNonEmptyString(value.lastOpenedAt);
|
|
35582
36014
|
if (!id || !theaterPath || !realpath2 || !label || !registeredAt || !lastOpenedAt) return null;
|
|
35583
|
-
|
|
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 === "..");
|
|
35584
36020
|
}
|
|
35585
36021
|
function sanitizeOperationNode(value) {
|
|
35586
36022
|
if (!isRecord6(value)) return null;
|
|
@@ -36117,6 +36553,359 @@ function sanitizeReplacementNodes(nextNodes) {
|
|
|
36117
36553
|
return result;
|
|
36118
36554
|
}
|
|
36119
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
|
+
|
|
36120
36909
|
// sdk/version.ts
|
|
36121
36910
|
var SDK_API_VERSION = 1;
|
|
36122
36911
|
|
|
@@ -36238,14 +37027,14 @@ function renderShim(definition) {
|
|
|
36238
37027
|
].join("\n");
|
|
36239
37028
|
}
|
|
36240
37029
|
function isSubpath(root, target) {
|
|
36241
|
-
const relative =
|
|
36242
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
37030
|
+
const relative = path27__default.relative(root, target);
|
|
37031
|
+
return relative === "" || !relative.startsWith("..") && !path27__default.isAbsolute(relative);
|
|
36243
37032
|
}
|
|
36244
37033
|
function discoverFleetPlugins(options = {}) {
|
|
36245
|
-
const builtInSourceRoot = options.builtInSourceRoot ?? (options.cwd ?
|
|
37034
|
+
const builtInSourceRoot = options.builtInSourceRoot ?? (options.cwd ? path27__default.resolve(options.cwd, "runtime/fleet-plugins") : null);
|
|
36246
37035
|
const roots = [
|
|
36247
37036
|
...builtInSourceRoot ? [{ root: builtInSourceRoot, builtInDistRoot: options.builtInDistRoot ?? null, external: false }] : [],
|
|
36248
|
-
{ root:
|
|
37037
|
+
{ root: path27__default.join(options.homeDir ?? os4__default.homedir(), ".fleet", "plugins"), builtInDistRoot: null, external: true }
|
|
36249
37038
|
];
|
|
36250
37039
|
const sourcePlugins = roots.flatMap((root) => discoverPluginRoot(root.root, root.builtInDistRoot, root.external));
|
|
36251
37040
|
const sourcePluginIds = new Set(sourcePlugins.map((plugin) => plugin.manifest.id));
|
|
@@ -36266,14 +37055,14 @@ function parseFleetPluginManifest(value) {
|
|
|
36266
37055
|
};
|
|
36267
37056
|
}
|
|
36268
37057
|
function discoverPluginRoot(root, builtInDistRoot, external) {
|
|
36269
|
-
if (!
|
|
36270
|
-
const entries =
|
|
37058
|
+
if (!fs14__default.existsSync(root)) return [];
|
|
37059
|
+
const entries = fs14__default.readdirSync(root, { withFileTypes: true });
|
|
36271
37060
|
const plugins = [];
|
|
36272
37061
|
for (const entry of entries) {
|
|
36273
37062
|
if (!entry.isDirectory() || entry.name === "shared") continue;
|
|
36274
|
-
const pluginRoot =
|
|
36275
|
-
const manifestPath =
|
|
36276
|
-
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;
|
|
36277
37066
|
const manifest = readManifest(manifestPath);
|
|
36278
37067
|
if (!manifest) continue;
|
|
36279
37068
|
const clientEntry = resolveOptionalManifestEntry(pluginRoot, manifest.client, "client");
|
|
@@ -36285,14 +37074,14 @@ function discoverPluginRoot(root, builtInDistRoot, external) {
|
|
|
36285
37074
|
return plugins;
|
|
36286
37075
|
}
|
|
36287
37076
|
function discoverDistPluginRoot(root) {
|
|
36288
|
-
if (!
|
|
36289
|
-
const entries =
|
|
37077
|
+
if (!fs14__default.existsSync(root)) return [];
|
|
37078
|
+
const entries = fs14__default.readdirSync(root, { withFileTypes: true });
|
|
36290
37079
|
const plugins = [];
|
|
36291
37080
|
for (const entry of entries) {
|
|
36292
37081
|
if (!entry.isDirectory() || entry.name === "shared") continue;
|
|
36293
|
-
const pluginRoot =
|
|
36294
|
-
const manifestPath =
|
|
36295
|
-
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);
|
|
36296
37085
|
if (!manifest) continue;
|
|
36297
37086
|
const clientEntry = resolveOptionalManifestEntry(pluginRoot, manifest.client, "client");
|
|
36298
37087
|
if (clientEntry === false) continue;
|
|
@@ -36304,7 +37093,7 @@ function discoverDistPluginRoot(root) {
|
|
|
36304
37093
|
}
|
|
36305
37094
|
function readManifest(manifestPath) {
|
|
36306
37095
|
try {
|
|
36307
|
-
const manifest = parseFleetPluginManifest(JSON.parse(
|
|
37096
|
+
const manifest = parseFleetPluginManifest(JSON.parse(fs14__default.readFileSync(manifestPath, "utf8")));
|
|
36308
37097
|
if (!manifest) console.warn(`[fleet-console] Plugin manifest skipped: invalid manifest at ${manifestPath}`);
|
|
36309
37098
|
return manifest;
|
|
36310
37099
|
} catch (error51) {
|
|
@@ -36318,11 +37107,11 @@ function resolveRoutesEntry(pluginRoot, manifest, builtInDistRoot) {
|
|
|
36318
37107
|
if (manifestRoute2 === false) return false;
|
|
36319
37108
|
if (manifestRoute2) return manifestRoute2;
|
|
36320
37109
|
}
|
|
36321
|
-
const manifestRoute =
|
|
36322
|
-
if (
|
|
37110
|
+
const manifestRoute = path27__default.join(pluginRoot, "routes.ts");
|
|
37111
|
+
if (fs14__default.existsSync(manifestRoute)) return manifestRoute;
|
|
36323
37112
|
if (!builtInDistRoot) return null;
|
|
36324
|
-
const builtRoute =
|
|
36325
|
-
return
|
|
37113
|
+
const builtRoute = path27__default.join(builtInDistRoot, manifest.id, "routes.mjs");
|
|
37114
|
+
return fs14__default.existsSync(builtRoute) ? builtRoute : null;
|
|
36326
37115
|
}
|
|
36327
37116
|
function resolveDistRoutesEntry(pluginRoot, manifest) {
|
|
36328
37117
|
const routesEntry = resolveOptionalManifestEntry(pluginRoot, manifest.routes ?? "routes.mjs", "routes");
|
|
@@ -36335,10 +37124,10 @@ function resolveOptionalManifestEntry(pluginRoot, entryPath, label) {
|
|
|
36335
37124
|
console.warn(`[fleet-console] Plugin ${label} entry skipped: unsafe relative path ${entryPath}`);
|
|
36336
37125
|
return false;
|
|
36337
37126
|
}
|
|
36338
|
-
const candidate =
|
|
36339
|
-
if (!
|
|
36340
|
-
const rootRealpath =
|
|
36341
|
-
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);
|
|
36342
37131
|
if (!isSubpath2(rootRealpath, entryRealpath)) {
|
|
36343
37132
|
console.warn(`[fleet-console] Plugin ${label} entry skipped: path escapes plugin root ${entryPath}`);
|
|
36344
37133
|
return false;
|
|
@@ -36353,12 +37142,12 @@ function readDistPluginManifest(id) {
|
|
|
36353
37142
|
return pluginId ? { id: pluginId, routes: "routes.mjs" } : null;
|
|
36354
37143
|
}
|
|
36355
37144
|
function isSafeRelativeEntry(entryPath) {
|
|
36356
|
-
if (
|
|
37145
|
+
if (path27__default.isAbsolute(entryPath)) return false;
|
|
36357
37146
|
return entryPath.split(/[\\/]+/u).every((segment) => segment !== "..");
|
|
36358
37147
|
}
|
|
36359
37148
|
function isSubpath2(root, candidate) {
|
|
36360
|
-
const relative =
|
|
36361
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
37149
|
+
const relative = path27__default.relative(root, candidate);
|
|
37150
|
+
return relative === "" || !relative.startsWith("..") && !path27__default.isAbsolute(relative);
|
|
36362
37151
|
}
|
|
36363
37152
|
function isRecord9(value) {
|
|
36364
37153
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -36376,17 +37165,22 @@ var PLUGIN_DEV_EXTERNALS = [
|
|
|
36376
37165
|
"child_process",
|
|
36377
37166
|
"node-pty",
|
|
36378
37167
|
"ws",
|
|
36379
|
-
"@fleet-console/sdk",
|
|
36380
|
-
"@fleet-console/sdk/*",
|
|
36381
|
-
"@dotobokuri/*",
|
|
36382
37168
|
"@fleet-plugins/*"
|
|
36383
37169
|
];
|
|
36384
|
-
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));
|
|
36385
37175
|
function createFleetPluginHost(deps) {
|
|
36386
37176
|
const plugins = filterDiscoveredPlugins(discoverFleetPlugins(deps));
|
|
36387
37177
|
const sensitiveFieldsByPluginId = new Map(plugins.map((plugin) => [plugin.manifest.id, plugin.manifest.sensitiveFields ?? []]));
|
|
36388
|
-
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));
|
|
36389
37182
|
async function boot() {
|
|
37183
|
+
await removeStalePluginBundleDirs(deps.bundleCacheDir, isProcessAlive2, removeBundleDir);
|
|
36390
37184
|
for (const plugin of plugins) {
|
|
36391
37185
|
if (!plugin.routesEntry) continue;
|
|
36392
37186
|
if (!plugin.external) {
|
|
@@ -36400,7 +37194,21 @@ function createFleetPluginHost(deps) {
|
|
|
36400
37194
|
}
|
|
36401
37195
|
}
|
|
36402
37196
|
}
|
|
36403
|
-
|
|
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 };
|
|
36404
37212
|
async function bootPluginRoutes(plugin) {
|
|
36405
37213
|
const mod = await importModule(plugin.routesEntry);
|
|
36406
37214
|
const register = resolveRegister(mod);
|
|
@@ -36434,7 +37242,7 @@ function filterDiscoveredPlugins(plugins) {
|
|
|
36434
37242
|
}
|
|
36435
37243
|
return accepted;
|
|
36436
37244
|
}
|
|
36437
|
-
async function importPluginModule(entry) {
|
|
37245
|
+
async function importPluginModule(entry, bundleCacheDir, generatedBundleDirs) {
|
|
36438
37246
|
if (entry.endsWith(".ts")) {
|
|
36439
37247
|
const { build } = await import('esbuild');
|
|
36440
37248
|
const output = await build({
|
|
@@ -36445,49 +37253,101 @@ async function importPluginModule(entry) {
|
|
|
36445
37253
|
target: "node20",
|
|
36446
37254
|
write: false,
|
|
36447
37255
|
sourcemap: false,
|
|
36448
|
-
external: PLUGIN_DEV_EXTERNALS
|
|
37256
|
+
external: PLUGIN_DEV_EXTERNALS,
|
|
37257
|
+
nodePaths: [resolveConsolePackageNodeModules()].filter((value) => value !== null)
|
|
36449
37258
|
});
|
|
36450
37259
|
const bundled = output.outputFiles[0]?.text;
|
|
36451
37260
|
if (!bundled) throw new Error("plugin_bundle_failed");
|
|
36452
|
-
const bundledPath = await writeTemporaryPluginBundle(entry, bundled);
|
|
37261
|
+
const bundledPath = await writeTemporaryPluginBundle(entry, bundled, bundleCacheDir, generatedBundleDirs);
|
|
36453
37262
|
return await import(pathToFileURL(bundledPath).href);
|
|
36454
37263
|
}
|
|
36455
37264
|
return await import(pathToFileURL(entry).href);
|
|
36456
37265
|
}
|
|
36457
|
-
async function writeTemporaryPluginBundle(entry, source) {
|
|
36458
|
-
const cacheRoot =
|
|
37266
|
+
async function writeTemporaryPluginBundle(entry, source, bundleCacheDir, generatedBundleDirs) {
|
|
37267
|
+
const cacheRoot = bundleCacheDir ?? path27__default.join(resolvePluginBundleNodeModules(entry), ".cache");
|
|
36459
37268
|
await mkdir(cacheRoot, { recursive: true });
|
|
36460
|
-
const dir = await mkdtemp(
|
|
36461
|
-
|
|
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");
|
|
36462
37275
|
await writeFile(file2, source, "utf8");
|
|
36463
37276
|
return file2;
|
|
36464
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
|
+
}
|
|
36465
37325
|
function resolvePluginBundleNodeModules(entry) {
|
|
36466
37326
|
const packageNodeModules = resolveConsolePackageNodeModules();
|
|
36467
|
-
let dir =
|
|
37327
|
+
let dir = path27__default.dirname(entry);
|
|
36468
37328
|
let fallback = packageNodeModules;
|
|
36469
37329
|
while (true) {
|
|
36470
|
-
const workspaceConsoleNodeModules =
|
|
37330
|
+
const workspaceConsoleNodeModules = path27__default.join(dir, "runtime", "fleet-console", "node_modules");
|
|
36471
37331
|
if (hasFleetConsoleSdk(workspaceConsoleNodeModules)) return workspaceConsoleNodeModules;
|
|
36472
|
-
const candidate =
|
|
37332
|
+
const candidate = path27__default.join(dir, "node_modules");
|
|
36473
37333
|
if (hasFleetConsoleSdk(candidate)) return candidate;
|
|
36474
37334
|
if (fallback === null && existsSync(candidate)) fallback = candidate;
|
|
36475
|
-
const parent =
|
|
36476
|
-
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");
|
|
36477
37337
|
dir = parent;
|
|
36478
37338
|
}
|
|
36479
37339
|
}
|
|
36480
37340
|
function resolveConsolePackageNodeModules() {
|
|
36481
37341
|
for (const candidate of [
|
|
36482
|
-
|
|
36483
|
-
|
|
37342
|
+
path27__default.resolve(__dirname$1, "..", "node_modules"),
|
|
37343
|
+
path27__default.resolve(__dirname$1, "..", "..", "..", "node_modules")
|
|
36484
37344
|
]) {
|
|
36485
37345
|
if (hasFleetConsoleSdk(candidate)) return candidate;
|
|
36486
37346
|
}
|
|
36487
37347
|
return null;
|
|
36488
37348
|
}
|
|
36489
37349
|
function hasFleetConsoleSdk(nodeModules) {
|
|
36490
|
-
return existsSync(
|
|
37350
|
+
return existsSync(path27__default.join(nodeModules, "@fleet-console", "sdk"));
|
|
36491
37351
|
}
|
|
36492
37352
|
function resolveRegister(mod) {
|
|
36493
37353
|
if (mod.register) return mod.register;
|
|
@@ -36533,6 +37393,8 @@ function normalizePrefix(prefix) {
|
|
|
36533
37393
|
var RELEASE_NOTE_HEADINGS = ["Added", "Changed", "Fixed", "Removed", "Breaking Changes"];
|
|
36534
37394
|
var VERSION_HEADER_PATTERN = /^## \[([^\]]+)\](?: - ([0-9]{4}-[0-9]{2}-[0-9]{2}))?$/;
|
|
36535
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)$/;
|
|
36536
37398
|
var BULLET_PATTERN = /^- (.+)$/;
|
|
36537
37399
|
var PACKAGE_TAG_PATTERN = /^\[([^\]]+)\]/;
|
|
36538
37400
|
function parseConsoleReleaseNotes(changelog) {
|
|
@@ -36553,24 +37415,55 @@ function parseConsoleReleaseNotes(changelog) {
|
|
|
36553
37415
|
return notes;
|
|
36554
37416
|
}
|
|
36555
37417
|
function collectSections(lines) {
|
|
36556
|
-
const
|
|
37418
|
+
const legacySections = /* @__PURE__ */ new Map();
|
|
37419
|
+
const productSections = /* @__PURE__ */ new Map();
|
|
37420
|
+
const legacyHeadings = /* @__PURE__ */ new Set();
|
|
36557
37421
|
let current = null;
|
|
37422
|
+
let isProductContext = false;
|
|
36558
37423
|
for (const line of lines) {
|
|
36559
37424
|
const sectionMatch = SECTION_HEADER_PATTERN.exec(line);
|
|
36560
37425
|
if (sectionMatch !== null) {
|
|
36561
|
-
|
|
36562
|
-
|
|
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;
|
|
36563
37435
|
continue;
|
|
36564
37436
|
}
|
|
36565
|
-
if (current === null) continue;
|
|
36566
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("#### ")) {
|
|
36567
37449
|
current = null;
|
|
36568
37450
|
continue;
|
|
36569
37451
|
}
|
|
36570
37452
|
const bulletMatch = BULLET_PATTERN.exec(line);
|
|
36571
37453
|
if (bulletMatch !== null) current.items.push(parseReleaseNoteItem(bulletMatch[1] ?? ""));
|
|
36572
37454
|
}
|
|
36573
|
-
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;
|
|
36574
37467
|
}
|
|
36575
37468
|
function parseReleaseNoteItem(rawText) {
|
|
36576
37469
|
const packageTags = [];
|
|
@@ -36596,10 +37489,11 @@ var ConsoleReleaseNotesUnavailableError = class extends Error {
|
|
|
36596
37489
|
|
|
36597
37490
|
// core/host/release-notes/service.ts
|
|
36598
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";
|
|
36599
37493
|
var SOURCE_REF = "main";
|
|
36600
37494
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
36601
37495
|
var MAX_CHANGELOG_BYTES = 1024 * 1024;
|
|
36602
|
-
var
|
|
37496
|
+
var SUCCESS_TTL_MS2 = 60 * 60 * 1e3;
|
|
36603
37497
|
var NEGATIVE_TTL_MS = 30 * 1e3;
|
|
36604
37498
|
function createConsoleReleaseNotesService(deps = {}) {
|
|
36605
37499
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -36610,30 +37504,46 @@ function createConsoleReleaseNotesService(deps = {}) {
|
|
|
36610
37504
|
let lastFailureAt = 0;
|
|
36611
37505
|
let inFlight = null;
|
|
36612
37506
|
let forceInFlight = null;
|
|
37507
|
+
let koreanLastSuccess = null;
|
|
37508
|
+
let koreanLastFailureAt = 0;
|
|
37509
|
+
let koreanInFlight = null;
|
|
37510
|
+
let koreanForceInFlight = null;
|
|
37511
|
+
let koreanRequestGeneration = 0;
|
|
36613
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) {
|
|
36614
37517
|
const currentTime = now();
|
|
36615
37518
|
if (options.force) {
|
|
36616
|
-
if (forceInFlight) return forceInFlight;
|
|
37519
|
+
if (forceInFlight) return await forceInFlight;
|
|
36617
37520
|
const pending2 = runFetch().finally(() => {
|
|
36618
37521
|
if (forceInFlight === pending2) forceInFlight = null;
|
|
36619
37522
|
});
|
|
36620
37523
|
forceInFlight = pending2;
|
|
36621
|
-
return pending2;
|
|
37524
|
+
return await pending2;
|
|
36622
37525
|
}
|
|
36623
|
-
if (lastSuccess && currentTime - lastSuccess.fetchedAt <
|
|
37526
|
+
if (lastSuccess && currentTime - lastSuccess.fetchedAt < SUCCESS_TTL_MS2) return lastSuccess;
|
|
36624
37527
|
if (lastFailureAt > 0 && currentTime - lastFailureAt < NEGATIVE_TTL_MS) {
|
|
36625
37528
|
if (lastSuccess) return { ...lastSuccess, stale: true };
|
|
36626
37529
|
throw new ConsoleReleaseNotesUnavailableError("negative_cache");
|
|
36627
37530
|
}
|
|
36628
|
-
if (inFlight) return inFlight;
|
|
37531
|
+
if (inFlight) return await inFlight;
|
|
36629
37532
|
const pending = runFetch().finally(() => {
|
|
36630
37533
|
if (inFlight === pending) inFlight = null;
|
|
36631
37534
|
});
|
|
36632
37535
|
inFlight = pending;
|
|
36633
|
-
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);
|
|
36634
37544
|
}
|
|
36635
37545
|
function runFetch() {
|
|
36636
|
-
return
|
|
37546
|
+
return fetchEnglishReleaseNotes().then((result) => {
|
|
36637
37547
|
lastSuccess = result;
|
|
36638
37548
|
lastFailureAt = 0;
|
|
36639
37549
|
return result;
|
|
@@ -36644,29 +37554,93 @@ function createConsoleReleaseNotesService(deps = {}) {
|
|
|
36644
37554
|
throw new ConsoleReleaseNotesUnavailableError("cold_unavailable");
|
|
36645
37555
|
});
|
|
36646
37556
|
}
|
|
36647
|
-
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) {
|
|
36648
37598
|
const controller = new AbortController();
|
|
36649
37599
|
const timer = setTimer(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
36650
37600
|
timer.unref?.();
|
|
36651
37601
|
try {
|
|
36652
|
-
const response = await fetchImpl(
|
|
37602
|
+
const response = await fetchImpl(url2, {
|
|
36653
37603
|
headers: { Accept: "text/plain; charset=utf-8" },
|
|
36654
37604
|
signal: controller.signal
|
|
36655
37605
|
});
|
|
36656
37606
|
if (!response.ok) throw new ConsoleReleaseNotesUnavailableError("cold_unavailable");
|
|
36657
37607
|
const text = await readTextWithByteLimit(response, controller);
|
|
36658
|
-
return {
|
|
36659
|
-
notes: parseConsoleReleaseNotes(text),
|
|
36660
|
-
sourceRef: SOURCE_REF,
|
|
36661
|
-
fetchedAt: now(),
|
|
36662
|
-
stale: false
|
|
36663
|
-
};
|
|
37608
|
+
return parseConsoleReleaseNotes(text).map((note) => ({ ...note, localizationFallback: false }));
|
|
36664
37609
|
} finally {
|
|
36665
37610
|
clearTimer(timer);
|
|
36666
37611
|
}
|
|
36667
37612
|
}
|
|
36668
37613
|
return { refresh };
|
|
36669
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
|
+
}
|
|
36670
37644
|
async function readTextWithByteLimit(response, controller) {
|
|
36671
37645
|
const body = response.body;
|
|
36672
37646
|
if (body === null) return await response.text();
|
|
@@ -36763,7 +37737,7 @@ var MIME_TYPES = {
|
|
|
36763
37737
|
".woff2": "font/woff2"
|
|
36764
37738
|
};
|
|
36765
37739
|
function createStaticConsoleHandler(packageRoot) {
|
|
36766
|
-
const consoleRoot =
|
|
37740
|
+
const consoleRoot = path27__default.join(packageRoot, "dist", "client");
|
|
36767
37741
|
return (req, res, pathname) => tryServeStaticConsole(req, res, pathname, consoleRoot);
|
|
36768
37742
|
}
|
|
36769
37743
|
function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
@@ -36779,15 +37753,15 @@ function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
|
36779
37753
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
36780
37754
|
return true;
|
|
36781
37755
|
}
|
|
36782
|
-
const absolutePath =
|
|
37756
|
+
const absolutePath = path27__default.resolve(consoleRoot, relativePath2);
|
|
36783
37757
|
if (!absolutePath.startsWith(consoleRoot)) {
|
|
36784
37758
|
res.writeHead(404, withSecurityHeaders2({ "Content-Type": "application/json" }));
|
|
36785
37759
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
36786
37760
|
return true;
|
|
36787
37761
|
}
|
|
36788
37762
|
try {
|
|
36789
|
-
const data =
|
|
36790
|
-
const contentType = MIME_TYPES[
|
|
37763
|
+
const data = fs14__default.readFileSync(absolutePath);
|
|
37764
|
+
const contentType = MIME_TYPES[path27__default.extname(absolutePath)] ?? "application/octet-stream";
|
|
36791
37765
|
res.writeHead(200, withSecurityHeaders2({ "Content-Type": contentType }));
|
|
36792
37766
|
if (req.method === "HEAD") {
|
|
36793
37767
|
res.end();
|
|
@@ -36807,7 +37781,7 @@ function tryServeStaticConsole(req, res, pathname, consoleRoot) {
|
|
|
36807
37781
|
}
|
|
36808
37782
|
function serveFallbackIndex(req, res, consoleRoot) {
|
|
36809
37783
|
try {
|
|
36810
|
-
const data =
|
|
37784
|
+
const data = fs14__default.readFileSync(path27__default.join(consoleRoot, "index.html"));
|
|
36811
37785
|
res.writeHead(200, withSecurityHeaders2({ "Content-Type": MIME_TYPES[".html"] }));
|
|
36812
37786
|
if (req.method === "HEAD") {
|
|
36813
37787
|
res.end();
|
|
@@ -36833,7 +37807,7 @@ function resolveConsolePath(pathname) {
|
|
|
36833
37807
|
}
|
|
36834
37808
|
if (withoutPrefix === "") return "index.html";
|
|
36835
37809
|
if (withoutPrefix.includes("\0")) return null;
|
|
36836
|
-
if (!
|
|
37810
|
+
if (!path27__default.extname(withoutPrefix)) return "index.html";
|
|
36837
37811
|
return withoutPrefix;
|
|
36838
37812
|
}
|
|
36839
37813
|
var TheaterFolderListError = class extends Error {
|
|
@@ -36844,12 +37818,12 @@ var TheaterFolderListError = class extends Error {
|
|
|
36844
37818
|
this.code = code;
|
|
36845
37819
|
}
|
|
36846
37820
|
};
|
|
36847
|
-
var
|
|
37821
|
+
var DIRECTORY_ENTRY_CAP2 = 500;
|
|
36848
37822
|
var WINDOWS_DRIVE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
36849
37823
|
async function listTheaterFolders(requestedPath, deps = {}) {
|
|
36850
37824
|
const platform = deps.platform ?? process.platform;
|
|
36851
|
-
const stat5 = deps.stat ??
|
|
36852
|
-
const opendir = deps.opendir ??
|
|
37825
|
+
const stat5 = deps.stat ?? fs14__default.promises.stat;
|
|
37826
|
+
const opendir = deps.opendir ?? fs14__default.promises.opendir;
|
|
36853
37827
|
const targetPath = normalizeListPath(requestedPath, platform, deps);
|
|
36854
37828
|
const roots = await listRoots(platform, stat5);
|
|
36855
37829
|
const targetStat = await statDirectory(targetPath, stat5);
|
|
@@ -36867,14 +37841,14 @@ async function listTheaterFolders(requestedPath, deps = {}) {
|
|
|
36867
37841
|
}
|
|
36868
37842
|
function normalizeFolderBrowserPath(value, platform = process.platform) {
|
|
36869
37843
|
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) throw new TheaterFolderListError("invalid_path");
|
|
36870
|
-
if (isWindowsAmbiguousPath(value, platform) || !
|
|
36871
|
-
return
|
|
37844
|
+
if (isWindowsAmbiguousPath(value, platform) || !path27__default.isAbsolute(value)) throw new TheaterFolderListError("invalid_path");
|
|
37845
|
+
return path27__default.resolve(value);
|
|
36872
37846
|
}
|
|
36873
37847
|
function normalizeListPath(requestedPath, platform, deps) {
|
|
36874
37848
|
if (requestedPath === null || requestedPath === void 0) {
|
|
36875
37849
|
const home = deps.homedir?.() ?? os4__default.homedir();
|
|
36876
37850
|
const start = home || deps.cwd?.() || process.cwd();
|
|
36877
|
-
return
|
|
37851
|
+
return path27__default.resolve(start);
|
|
36878
37852
|
}
|
|
36879
37853
|
return normalizeFolderBrowserPath(requestedPath, platform);
|
|
36880
37854
|
}
|
|
@@ -36894,7 +37868,7 @@ async function statDirectory(targetPath, stat5) {
|
|
|
36894
37868
|
try {
|
|
36895
37869
|
return await stat5(targetPath);
|
|
36896
37870
|
} catch (error51) {
|
|
36897
|
-
throw
|
|
37871
|
+
throw mapFsError2(error51);
|
|
36898
37872
|
}
|
|
36899
37873
|
}
|
|
36900
37874
|
async function collectDirectoryEntries(targetPath, opendir, stat5, entries) {
|
|
@@ -36904,14 +37878,14 @@ async function collectDirectoryEntries(targetPath, opendir, stat5, entries) {
|
|
|
36904
37878
|
while (dirent !== null) {
|
|
36905
37879
|
const entry = await toTheaterFolderEntry(targetPath, dirent, stat5);
|
|
36906
37880
|
if (entry !== null) {
|
|
36907
|
-
if (entries.length >=
|
|
37881
|
+
if (entries.length >= DIRECTORY_ENTRY_CAP2) return true;
|
|
36908
37882
|
entries.push(entry);
|
|
36909
37883
|
}
|
|
36910
37884
|
dirent = await directory.read();
|
|
36911
37885
|
}
|
|
36912
37886
|
return false;
|
|
36913
37887
|
} catch (error51) {
|
|
36914
|
-
throw
|
|
37888
|
+
throw mapFsError2(error51);
|
|
36915
37889
|
} finally {
|
|
36916
37890
|
await directory.close();
|
|
36917
37891
|
}
|
|
@@ -36920,12 +37894,12 @@ async function openDirectory(targetPath, opendir) {
|
|
|
36920
37894
|
try {
|
|
36921
37895
|
return await opendir(targetPath);
|
|
36922
37896
|
} catch (error51) {
|
|
36923
|
-
throw
|
|
37897
|
+
throw mapFsError2(error51);
|
|
36924
37898
|
}
|
|
36925
37899
|
}
|
|
36926
37900
|
async function toTheaterFolderEntry(targetPath, dirent, stat5) {
|
|
36927
37901
|
if (!dirent.isDirectory() && !dirent.isSymbolicLink()) return null;
|
|
36928
|
-
const entryPath =
|
|
37902
|
+
const entryPath = path27__default.join(targetPath, dirent.name);
|
|
36929
37903
|
if (dirent.isDirectory()) return { name: dirent.name, path: entryPath, kind: "dir", accessible: true };
|
|
36930
37904
|
const accessible = await statSymlinkDirectory(entryPath, stat5);
|
|
36931
37905
|
if (accessible === null) return null;
|
|
@@ -36938,17 +37912,17 @@ async function statSymlinkDirectory(entryPath, stat5) {
|
|
|
36938
37912
|
return false;
|
|
36939
37913
|
}
|
|
36940
37914
|
}
|
|
36941
|
-
function
|
|
37915
|
+
function mapFsError2(error51) {
|
|
36942
37916
|
const code = error51.code;
|
|
36943
37917
|
if (code === "EACCES" || code === "EPERM") return new TheaterFolderListError("forbidden");
|
|
36944
37918
|
if (code === "ENOENT" || code === "ENOTDIR") return new TheaterFolderListError("not_found");
|
|
36945
37919
|
return new TheaterFolderListError("invalid_path");
|
|
36946
37920
|
}
|
|
36947
37921
|
function parentPath(targetPath, platform) {
|
|
36948
|
-
const parsed =
|
|
36949
|
-
const resolved =
|
|
36950
|
-
if (resolved ===
|
|
36951
|
-
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);
|
|
36952
37926
|
if (platform === "win32" && parent === resolved) return null;
|
|
36953
37927
|
return parent;
|
|
36954
37928
|
}
|
|
@@ -36960,7 +37934,7 @@ var GRANT_ID_BYTES = 24;
|
|
|
36960
37934
|
var DEFAULT_GRANT_TTL_MS = 6e4;
|
|
36961
37935
|
function createFolderGrantStore(deps = {}) {
|
|
36962
37936
|
const randomId = deps.randomId ?? (() => crypto5.randomBytes(GRANT_ID_BYTES).toString("base64url"));
|
|
36963
|
-
const statSync2 = deps.statSync ??
|
|
37937
|
+
const statSync2 = deps.statSync ?? fs14__default.statSync;
|
|
36964
37938
|
const ttlMs = deps.ttlMs ?? DEFAULT_GRANT_TTL_MS;
|
|
36965
37939
|
const now = deps.now ?? Date.now;
|
|
36966
37940
|
const grants = /* @__PURE__ */ new Map();
|
|
@@ -36986,10 +37960,10 @@ function createFolderGrantStore(deps = {}) {
|
|
|
36986
37960
|
}
|
|
36987
37961
|
return { issue: issue2, consume };
|
|
36988
37962
|
}
|
|
36989
|
-
function validateAbsoluteDirectory(cwd, statSync2 =
|
|
37963
|
+
function validateAbsoluteDirectory(cwd, statSync2 = fs14__default.statSync) {
|
|
36990
37964
|
if (typeof cwd !== "string" || cwd.length === 0 || cwd.includes("\0")) throw new Error("invalid_folder");
|
|
36991
|
-
if (isWindowsAmbiguousPath2(cwd) || !
|
|
36992
|
-
const normalized =
|
|
37965
|
+
if (isWindowsAmbiguousPath2(cwd) || !path27__default.isAbsolute(cwd)) throw new Error("invalid_folder");
|
|
37966
|
+
const normalized = path27__default.resolve(cwd);
|
|
36993
37967
|
if (!statSync2(normalized).isDirectory()) throw new Error("invalid_folder");
|
|
36994
37968
|
return normalized;
|
|
36995
37969
|
}
|
|
@@ -36997,11 +37971,178 @@ function isWindowsAmbiguousPath2(value) {
|
|
|
36997
37971
|
if (process.platform !== "win32") return false;
|
|
36998
37972
|
return /^[a-zA-Z]:(?![\\/])/.test(value) || /^[\\/](?![\\/])/.test(value);
|
|
36999
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
|
+
}
|
|
37000
38141
|
var TheaterRegistry = class {
|
|
37001
38142
|
#items = /* @__PURE__ */ new Map();
|
|
37002
38143
|
#mruId = null;
|
|
37003
38144
|
async register(cwd) {
|
|
37004
|
-
const resolved =
|
|
38145
|
+
const resolved = path27__default.resolve(cwd);
|
|
37005
38146
|
const real = await canonicalizeTheaterPath(resolved);
|
|
37006
38147
|
const id = workspaceHash(real);
|
|
37007
38148
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -37015,7 +38156,8 @@ var TheaterRegistry = class {
|
|
|
37015
38156
|
realpath: real,
|
|
37016
38157
|
label: theaterLabel(resolved),
|
|
37017
38158
|
registeredAt: existing?.registeredAt ?? now,
|
|
37018
|
-
lastOpenedAt: now
|
|
38159
|
+
lastOpenedAt: now,
|
|
38160
|
+
pathContext: existing?.pathContext ?? null
|
|
37019
38161
|
};
|
|
37020
38162
|
this.#items.set(id, item);
|
|
37021
38163
|
this.#mruId = id;
|
|
@@ -37055,6 +38197,13 @@ var TheaterRegistry = class {
|
|
|
37055
38197
|
if (this.#mruId === id) this.#mruId = this.list()[0]?.id ?? null;
|
|
37056
38198
|
return removed;
|
|
37057
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
|
+
}
|
|
37058
38207
|
};
|
|
37059
38208
|
var PACKAGE_NAMES = ["@dotobokuri/fleet-cli", "@dotobokuri/fleet-console"];
|
|
37060
38209
|
var WORKER_FILE_PREFIX = "fleet-console-update-";
|
|
@@ -37066,7 +38215,7 @@ function createConsoleUpdateApplyService(deps = {}) {
|
|
|
37066
38215
|
const env = deps.env ?? process4.env;
|
|
37067
38216
|
const execPath = deps.execPath ?? process4.execPath;
|
|
37068
38217
|
const makeDir = deps.makeDir ?? ((dirPath, options) => {
|
|
37069
|
-
|
|
38218
|
+
fs14__default.mkdirSync(dirPath, options);
|
|
37070
38219
|
});
|
|
37071
38220
|
const now = deps.now ?? Date.now;
|
|
37072
38221
|
const processPid = deps.processPid ?? process4.pid;
|
|
@@ -37075,15 +38224,15 @@ function createConsoleUpdateApplyService(deps = {}) {
|
|
|
37075
38224
|
const spawnWorker = deps.spawnWorker ?? defaultSpawnWorker;
|
|
37076
38225
|
const tmpDir = deps.tmpDir ?? os4__default.tmpdir();
|
|
37077
38226
|
const writeFile5 = deps.writeFile ?? ((filePath, content, options) => {
|
|
37078
|
-
|
|
38227
|
+
fs14__default.writeFileSync(filePath, content, { mode: options.mode });
|
|
37079
38228
|
});
|
|
37080
38229
|
async function start(request) {
|
|
37081
38230
|
const packageManager = await preflightInstall(request.currentPackageRoot);
|
|
37082
38231
|
const stamp = `${now()}-${processPid}`;
|
|
37083
|
-
const workerPath =
|
|
38232
|
+
const workerPath = path27__default.join(tmpDir, `${WORKER_FILE_PREFIX}${stamp}${WORKER_FILE_SUFFIX}`);
|
|
37084
38233
|
makeDir(request.dataDir, { recursive: true, mode: 448 });
|
|
37085
|
-
const statusFile =
|
|
37086
|
-
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}`);
|
|
37087
38236
|
const script = emitConsoleUpdateWorkerScript({
|
|
37088
38237
|
currentEndpoint: request.currentEndpoint,
|
|
37089
38238
|
currentPackageRoot: request.currentPackageRoot,
|
|
@@ -37426,12 +38575,12 @@ function defaultSpawnWorker(execPath, args, options) {
|
|
|
37426
38575
|
}
|
|
37427
38576
|
function resolveDefaultServerModulePath() {
|
|
37428
38577
|
const builtPath = fileURLToPath(new URL("../dist/cli.mjs", import.meta.url));
|
|
37429
|
-
if (
|
|
38578
|
+
if (fs14__default.existsSync(builtPath)) return builtPath;
|
|
37430
38579
|
const sourcePath = fileURLToPath(import.meta.url);
|
|
37431
38580
|
if (sourcePath.endsWith(".ts")) {
|
|
37432
38581
|
return sourcePath.replace(/update-apply\.ts$/, "cli.ts");
|
|
37433
38582
|
}
|
|
37434
|
-
return
|
|
38583
|
+
return path27__default.join(path27__default.dirname(sourcePath), "cli.mjs");
|
|
37435
38584
|
}
|
|
37436
38585
|
|
|
37437
38586
|
// core/host/update-check.ts
|
|
@@ -37485,7 +38634,7 @@ function createConsoleUpdateCheckService(deps = {}) {
|
|
|
37485
38634
|
};
|
|
37486
38635
|
const resolveUpdateStatus = async () => {
|
|
37487
38636
|
const release = readRelease();
|
|
37488
|
-
if (release.channel === "local") {
|
|
38637
|
+
if (release.channel === "local" || release.channel === "desktop") {
|
|
37489
38638
|
return NO_UPDATE_STATUS;
|
|
37490
38639
|
}
|
|
37491
38640
|
const latestVersion = await fetchLatest(FLEET_CONSOLE_PACKAGE_NAME);
|
|
@@ -37580,6 +38729,41 @@ var SERVER_API_CATALOG = [
|
|
|
37580
38729
|
category: "Observer",
|
|
37581
38730
|
gate: "origin-write"
|
|
37582
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
|
+
},
|
|
37583
38767
|
{
|
|
37584
38768
|
method: "POST",
|
|
37585
38769
|
path: "/plugins/terminal/shell/ticket",
|
|
@@ -37615,6 +38799,20 @@ var SERVER_API_CATALOG = [
|
|
|
37615
38799
|
category: "Observer",
|
|
37616
38800
|
gate: "origin-write"
|
|
37617
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
|
+
},
|
|
37618
38816
|
{
|
|
37619
38817
|
method: "POST",
|
|
37620
38818
|
path: "/api/v1/updates/apply",
|
|
@@ -37634,6 +38832,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37634
38832
|
const host = deps.host ?? DEFAULT_HOST;
|
|
37635
38833
|
const port = deps.port ?? DEFAULT_PORT;
|
|
37636
38834
|
const release = deps.release ?? readFleetConsoleRelease();
|
|
38835
|
+
const desktop = readDesktopProtocolEnvironment();
|
|
37637
38836
|
const tryServeStaticConsole2 = createStaticConsoleHandler(release.packageRoot);
|
|
37638
38837
|
const version2 = deps.version ?? release.version;
|
|
37639
38838
|
const channel = release.channel;
|
|
@@ -37641,7 +38840,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37641
38840
|
registerDefaultCarriers(carrierRegistry);
|
|
37642
38841
|
const lock = createConsoleLock({ hostname: () => host });
|
|
37643
38842
|
const releaseNotes = deps.releaseNotes ?? createConsoleReleaseNotesService();
|
|
37644
|
-
const updateCheck = deps.updateCheck ?? createConsoleUpdateCheckService();
|
|
38843
|
+
const updateCheck = deps.updateCheck ?? createConsoleUpdateCheckService({ readRelease: () => release });
|
|
37645
38844
|
const updateApply = deps.updateApply ?? createConsoleUpdateApplyService();
|
|
37646
38845
|
const theaters = new TheaterRegistry();
|
|
37647
38846
|
const operations = createOperationStore();
|
|
@@ -37737,8 +38936,8 @@ function createConsoleServer(deps = {}) {
|
|
|
37737
38936
|
},
|
|
37738
38937
|
paths: {
|
|
37739
38938
|
capturesDir: durablePaths.capturesDir,
|
|
37740
|
-
pluginDataDir: (pluginId) =>
|
|
37741
|
-
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.
|
|
38939
|
+
pluginDataDir: (pluginId) => path27__default.join(durablePaths.dir, "plugins", pluginId),
|
|
38940
|
+
resolveTheaterPath: (theaterId) => theaters.get(theaterId)?.realpath ?? null,
|
|
37742
38941
|
canonicalizeTheaterPath: canonicalizeTheaterPathSync,
|
|
37743
38942
|
workspaceHash
|
|
37744
38943
|
},
|
|
@@ -37762,8 +38961,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37762
38961
|
}
|
|
37763
38962
|
}
|
|
37764
38963
|
};
|
|
38964
|
+
process.env.FLEET_CONSOLE_PACKAGE_ROOT = release.packageRoot;
|
|
37765
38965
|
const pluginHost = createFleetPluginHost({
|
|
37766
38966
|
...resolveBuiltInPluginDiscoveryRoots(release.packageRoot),
|
|
38967
|
+
bundleCacheDir: path27__default.join(durablePaths.dir, "plugin-cache"),
|
|
37767
38968
|
routes: routeRegistry,
|
|
37768
38969
|
upgrades: upgradeRegistry,
|
|
37769
38970
|
host: pluginHostCapabilities
|
|
@@ -37829,6 +39030,31 @@ function createConsoleServer(deps = {}) {
|
|
|
37829
39030
|
readJsonBody,
|
|
37830
39031
|
writeJson
|
|
37831
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
|
+
});
|
|
37832
39058
|
const operationsRouter = createOperationsRouter({
|
|
37833
39059
|
store: operations,
|
|
37834
39060
|
isAuthorized: isTerminalAuthorized,
|
|
@@ -37857,6 +39083,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37857
39083
|
}
|
|
37858
39084
|
});
|
|
37859
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
|
+
});
|
|
37860
39090
|
routeRegistry.register("/api/v1/settings", async (ctx) => {
|
|
37861
39091
|
const { req, res, pathname } = ctx;
|
|
37862
39092
|
if (pathname === "/api/v1/settings/api-catalog") {
|
|
@@ -37865,6 +39095,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37865
39095
|
}
|
|
37866
39096
|
if (await carrierSettingsRouter(ctx)) return true;
|
|
37867
39097
|
if (await pluginSettingsRouter(ctx)) return true;
|
|
39098
|
+
if (await systemFontsRouter(ctx)) return true;
|
|
37868
39099
|
return globalSettingsRouter(ctx);
|
|
37869
39100
|
});
|
|
37870
39101
|
routeRegistry.register("/plugin-runtime", handlePluginRuntimeRoute);
|
|
@@ -37905,6 +39136,10 @@ function createConsoleServer(deps = {}) {
|
|
|
37905
39136
|
runAsyncHandler(handleTheaterFolderGrants(req, res), res);
|
|
37906
39137
|
return;
|
|
37907
39138
|
}
|
|
39139
|
+
if (pathname === "/api/v1/plans/list" || pathname === "/api/v1/plans/read") {
|
|
39140
|
+
runAsyncBooleanHandler(plansRouter({ req, res, pathname }), res, () => false);
|
|
39141
|
+
return;
|
|
39142
|
+
}
|
|
37908
39143
|
const theaterItemMatch = pathname.match(/^\/api\/v1\/theaters\/([^/]+)$/);
|
|
37909
39144
|
if (theaterItemMatch) {
|
|
37910
39145
|
runAsyncHandler(handleObserverTheaterItem(req, res, decodeURIComponent(theaterItemMatch[1] ?? "")), res);
|
|
@@ -37969,6 +39204,8 @@ function createConsoleServer(deps = {}) {
|
|
|
37969
39204
|
endpoint: payload.endpoint,
|
|
37970
39205
|
startedAt: payload.startedAt,
|
|
37971
39206
|
version: payload.version,
|
|
39207
|
+
...channel === "desktop" ? { channel } : {},
|
|
39208
|
+
...payload.owner ? { owner: payload.owner } : {},
|
|
37972
39209
|
workspaceCount: operations.list().length
|
|
37973
39210
|
};
|
|
37974
39211
|
writeJson(res, 200, body);
|
|
@@ -37986,7 +39223,7 @@ function createConsoleServer(deps = {}) {
|
|
|
37986
39223
|
return;
|
|
37987
39224
|
}
|
|
37988
39225
|
const body = await readJsonBody(req);
|
|
37989
|
-
if (!
|
|
39226
|
+
if (!isPlainObject4(body) || body.path !== void 0 && body.path !== null && typeof body.path !== "string") {
|
|
37990
39227
|
writeJson(res, 400, { error: "invalid_path" });
|
|
37991
39228
|
return;
|
|
37992
39229
|
}
|
|
@@ -38011,7 +39248,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38011
39248
|
return;
|
|
38012
39249
|
}
|
|
38013
39250
|
const body = await readJsonBody(req);
|
|
38014
|
-
if (!
|
|
39251
|
+
if (!isPlainObject4(body) || typeof body.path !== "string") {
|
|
38015
39252
|
writeJson(res, 400, { error: "invalid_folder" });
|
|
38016
39253
|
return;
|
|
38017
39254
|
}
|
|
@@ -38035,7 +39272,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38035
39272
|
return;
|
|
38036
39273
|
}
|
|
38037
39274
|
const body = await readJsonBody(req);
|
|
38038
|
-
if (!
|
|
39275
|
+
if (!isPlainObject4(body) || typeof body.folderGrantId !== "string") {
|
|
38039
39276
|
writeJson(res, 400, { error: "invalid_folder_grant" });
|
|
38040
39277
|
return;
|
|
38041
39278
|
}
|
|
@@ -38047,7 +39284,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38047
39284
|
const theater = await theaters.register(cwd);
|
|
38048
39285
|
let hasWiki = false;
|
|
38049
39286
|
try {
|
|
38050
|
-
await codex.registerWorkspace(
|
|
39287
|
+
await codex.registerWorkspace(theater.realpath, void 0, theater.id);
|
|
38051
39288
|
hasWiki = true;
|
|
38052
39289
|
} catch (error51) {
|
|
38053
39290
|
if (!(error51 instanceof Error && error51.message === "knowledge_root_missing")) {
|
|
@@ -38067,7 +39304,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38067
39304
|
return;
|
|
38068
39305
|
}
|
|
38069
39306
|
theaters.remove(theaterId);
|
|
38070
|
-
codex.
|
|
39307
|
+
codex.unregisterTheaterWorkspaces(theaterId);
|
|
38071
39308
|
for (const operation of operations.listByTheater(theaterId)) {
|
|
38072
39309
|
pluginHostCapabilities.events.publish(OPERATION_DELETED_EVENT_CHANNEL, {
|
|
38073
39310
|
operationId: operation.id,
|
|
@@ -38108,8 +39345,10 @@ function createConsoleServer(deps = {}) {
|
|
|
38108
39345
|
return;
|
|
38109
39346
|
}
|
|
38110
39347
|
try {
|
|
38111
|
-
const
|
|
38112
|
-
|
|
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 }));
|
|
38113
39352
|
} catch (error51) {
|
|
38114
39353
|
if (error51 instanceof ConsoleReleaseNotesUnavailableError) {
|
|
38115
39354
|
writeJson(res, 503, { error: "release_notes_unavailable" });
|
|
@@ -38132,10 +39371,14 @@ function createConsoleServer(deps = {}) {
|
|
|
38132
39371
|
writeJson(res, 400, { error: "invalid_update_apply_body" });
|
|
38133
39372
|
return;
|
|
38134
39373
|
}
|
|
38135
|
-
if (body !== null && (!
|
|
39374
|
+
if (body !== null && (!isPlainObject4(body) || hasForbiddenUpdateApplyBodyKeys(body))) {
|
|
38136
39375
|
writeJson(res, 400, { error: "invalid_update_apply_body" });
|
|
38137
39376
|
return;
|
|
38138
39377
|
}
|
|
39378
|
+
if (channel === "desktop") {
|
|
39379
|
+
writeJson(res, 403, { error: "desktop_update_managed" });
|
|
39380
|
+
return;
|
|
39381
|
+
}
|
|
38139
39382
|
if (channel === "local") {
|
|
38140
39383
|
writeJson(res, 403, { error: "local_channel" });
|
|
38141
39384
|
return;
|
|
@@ -38244,6 +39487,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38244
39487
|
console.warn(`[fleet-console] Plugin cleanup failed: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
38245
39488
|
}
|
|
38246
39489
|
}
|
|
39490
|
+
await pluginHost.cleanup();
|
|
38247
39491
|
pluginCleanupCallbacks.clear();
|
|
38248
39492
|
pluginEventListeners.clear();
|
|
38249
39493
|
currentLock?.release();
|
|
@@ -38255,6 +39499,8 @@ function createConsoleServer(deps = {}) {
|
|
|
38255
39499
|
theaters.restore(state.theaters);
|
|
38256
39500
|
operations.replace(state.operations);
|
|
38257
39501
|
operations.replaceGroups(state.groups ?? []);
|
|
39502
|
+
const healed = await healRestoredPathContexts();
|
|
39503
|
+
if (healed) persistDurableState();
|
|
38258
39504
|
} catch (error51) {
|
|
38259
39505
|
console.warn(`[fleet-console] Durable state restore skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
38260
39506
|
state = emptyDurableConsoleState();
|
|
@@ -38264,11 +39510,28 @@ function createConsoleServer(deps = {}) {
|
|
|
38264
39510
|
}
|
|
38265
39511
|
await restoreCodexWorkspaces();
|
|
38266
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
|
+
}
|
|
38267
39530
|
async function restoreCodexWorkspaces() {
|
|
38268
39531
|
const ordered = [...theaters.list()].sort((left, right) => left.lastOpenedAt.localeCompare(right.lastOpenedAt));
|
|
38269
39532
|
for (const theater of ordered) {
|
|
38270
39533
|
try {
|
|
38271
|
-
await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt);
|
|
39534
|
+
await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt, theater.id);
|
|
38272
39535
|
} catch (error51) {
|
|
38273
39536
|
if (!(error51 instanceof Error && error51.message === "knowledge_root_missing")) {
|
|
38274
39537
|
console.warn(`[fleet-console] Codex workspace restore skipped for Theater ${theater.id}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
@@ -38339,7 +39602,7 @@ function createConsoleServer(deps = {}) {
|
|
|
38339
39602
|
server = result.srv;
|
|
38340
39603
|
loopbackServer = result.localLoopbackServer;
|
|
38341
39604
|
portState = result.portState;
|
|
38342
|
-
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 } : {} });
|
|
38343
39606
|
activeLockFile = lockPaths.lockFile;
|
|
38344
39607
|
activeEndpoint = result.endpoint;
|
|
38345
39608
|
} catch (error51) {
|
|
@@ -38373,6 +39636,14 @@ function createConsoleServer(deps = {}) {
|
|
|
38373
39636
|
allowFallback: false
|
|
38374
39637
|
};
|
|
38375
39638
|
}
|
|
39639
|
+
if (channel === "local") {
|
|
39640
|
+
return {
|
|
39641
|
+
port: DEFAULT_PORT,
|
|
39642
|
+
requestedPort: null,
|
|
39643
|
+
portMode: "dynamic",
|
|
39644
|
+
allowFallback: false
|
|
39645
|
+
};
|
|
39646
|
+
}
|
|
38376
39647
|
const options = consoleSettingsStore.load().general ?? {};
|
|
38377
39648
|
if (options.consolePortMode === "static" && isValidConsoleStaticPort3(options.consoleStaticPort)) {
|
|
38378
39649
|
return {
|
|
@@ -38439,11 +39710,11 @@ function createConsoleServer(deps = {}) {
|
|
|
38439
39710
|
return returnedServer;
|
|
38440
39711
|
}
|
|
38441
39712
|
function resolveBuiltInPluginDiscoveryRoots(packageRoot) {
|
|
38442
|
-
const packageRootRepo =
|
|
38443
|
-
const sourceRoot =
|
|
39713
|
+
const packageRootRepo = path27__default.resolve(packageRoot, "..", "..");
|
|
39714
|
+
const sourceRoot = path27__default.join(packageRootRepo, "runtime", "fleet-plugins");
|
|
38444
39715
|
return {
|
|
38445
|
-
...
|
|
38446
|
-
builtInDistRoot:
|
|
39716
|
+
...fs14__default.existsSync(sourceRoot) ? { builtInSourceRoot: sourceRoot } : {},
|
|
39717
|
+
builtInDistRoot: path27__default.join(packageRoot, "dist", "fleet-plugins")
|
|
38447
39718
|
};
|
|
38448
39719
|
}
|
|
38449
39720
|
function createHttpServer(handler, upgradeRegistry) {
|
|
@@ -38499,7 +39770,7 @@ function theaterFolderListStatus(error51) {
|
|
|
38499
39770
|
if (error51.code === "not_found") return 404;
|
|
38500
39771
|
return 400;
|
|
38501
39772
|
}
|
|
38502
|
-
function
|
|
39773
|
+
function isPlainObject4(value) {
|
|
38503
39774
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38504
39775
|
}
|
|
38505
39776
|
function hasForbiddenUpdateApplyBodyKeys(body) {
|
|
@@ -38509,7 +39780,7 @@ function isValidConsoleStaticPort3(value) {
|
|
|
38509
39780
|
return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT3 && value <= MAX_CONSOLE_STATIC_PORT3;
|
|
38510
39781
|
}
|
|
38511
39782
|
function sanitizeLaunchKind(value) {
|
|
38512
|
-
if (!
|
|
39783
|
+
if (!isPlainObject4(value) || typeof value.id !== "string" || typeof value.type !== "string" || typeof value.title !== "string") return null;
|
|
38513
39784
|
return {
|
|
38514
39785
|
id: value.id,
|
|
38515
39786
|
type: value.type,
|
|
@@ -38588,7 +39859,7 @@ function isAllowedTerminalOrigin(req, expectedPort) {
|
|
|
38588
39859
|
async function readPluginStorageJson(dataDir, pluginId, key) {
|
|
38589
39860
|
const file2 = resolvePluginStorageFile(dataDir, pluginId, key);
|
|
38590
39861
|
try {
|
|
38591
|
-
return JSON.parse(await
|
|
39862
|
+
return JSON.parse(await fs14__default.promises.readFile(file2, "utf8"));
|
|
38592
39863
|
} catch (error51) {
|
|
38593
39864
|
if (isNodeError(error51) && error51.code === "ENOENT") return null;
|
|
38594
39865
|
throw error51;
|
|
@@ -38596,20 +39867,20 @@ async function readPluginStorageJson(dataDir, pluginId, key) {
|
|
|
38596
39867
|
}
|
|
38597
39868
|
async function writePluginStorageJson(dataDir, pluginId, key, value) {
|
|
38598
39869
|
const file2 = resolvePluginStorageFile(dataDir, pluginId, key);
|
|
38599
|
-
await
|
|
38600
|
-
await
|
|
39870
|
+
await fs14__default.promises.mkdir(path27__default.dirname(file2), { recursive: true });
|
|
39871
|
+
await fs14__default.promises.writeFile(file2, JSON.stringify(value), "utf8");
|
|
38601
39872
|
}
|
|
38602
39873
|
function resolvePluginStorageFile(dataDir, pluginId, key) {
|
|
38603
39874
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(pluginId) || !/^[a-z0-9][a-z0-9._-]*$/i.test(key)) {
|
|
38604
39875
|
throw new Error("invalid_plugin_storage_key");
|
|
38605
39876
|
}
|
|
38606
|
-
return
|
|
39877
|
+
return path27__default.join(dataDir, "plugins", pluginId, `${key}.json`);
|
|
38607
39878
|
}
|
|
38608
39879
|
function isNodeError(error51) {
|
|
38609
39880
|
return error51 instanceof Error && "code" in error51;
|
|
38610
39881
|
}
|
|
38611
39882
|
function createConsoleStalePolicy(deps = {}) {
|
|
38612
|
-
const fsImpl = deps.fs ??
|
|
39883
|
+
const fsImpl = deps.fs ?? fs14__default;
|
|
38613
39884
|
function isBuildStale(lock, buildFile) {
|
|
38614
39885
|
try {
|
|
38615
39886
|
return lock.startedAt < fsImpl.statSync(buildFile).mtimeMs;
|
|
@@ -38731,6 +40002,7 @@ function createConsoleDaemonLifecycle(deps = {}) {
|
|
|
38731
40002
|
async function stop() {
|
|
38732
40003
|
const payload = readTrustedLock();
|
|
38733
40004
|
if (!payload) return;
|
|
40005
|
+
if (isLockProcessAlive(payload.pid)) assertCliCanControlDaemon(payload);
|
|
38734
40006
|
try {
|
|
38735
40007
|
process4.kill(payload.pid, "SIGTERM");
|
|
38736
40008
|
} catch (err) {
|
|
@@ -38750,6 +40022,7 @@ function createConsoleDaemonLifecycle(deps = {}) {
|
|
|
38750
40022
|
const probeResult = await health.probe(current);
|
|
38751
40023
|
const isBuildStale = current ? stale.isBuildStale(current, serverModulePath) : false;
|
|
38752
40024
|
if (probeResult.healthy && current) {
|
|
40025
|
+
if (current.owner?.kind === "desktop") return current.endpoint;
|
|
38753
40026
|
if (!isBuildStale) return current.endpoint;
|
|
38754
40027
|
if (typeof probeResult.health?.workspaceCount === "number" && probeResult.health.workspaceCount > 0) return current.endpoint;
|
|
38755
40028
|
}
|
|
@@ -38802,11 +40075,12 @@ async function runConsoleStatus(deps = {}) {
|
|
|
38802
40075
|
const consoleUrl = `${status.lock.endpoint}console/`;
|
|
38803
40076
|
const workspaceCount = typeof status.health?.workspaceCount === "number" ? status.health.workspaceCount : 0;
|
|
38804
40077
|
const staleNote = status.buildStale ? " \xB7 build stale (restart recommended)" : "";
|
|
40078
|
+
const ownerNote = status.lock.owner?.kind === "desktop" ? " \xB7 owned by desktop" : "";
|
|
38805
40079
|
return [
|
|
38806
40080
|
`Fleet Console server: running (pid ${status.lock.pid})`,
|
|
38807
40081
|
` endpoint ${status.lock.endpoint}`,
|
|
38808
40082
|
` console ${consoleUrl}`,
|
|
38809
|
-
` workspaces ${workspaceCount}${staleNote}`
|
|
40083
|
+
` workspaces ${workspaceCount}${staleNote}${ownerNote}`
|
|
38810
40084
|
].join("\n");
|
|
38811
40085
|
}
|
|
38812
40086
|
async function runConsoleStop(deps = {}) {
|
|
@@ -38814,6 +40088,17 @@ async function runConsoleStop(deps = {}) {
|
|
|
38814
40088
|
await lifecycle.stop();
|
|
38815
40089
|
return "Fleet Console server stopped.";
|
|
38816
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
|
+
}
|
|
38817
40102
|
async function runConsoleRestart(deps = {}) {
|
|
38818
40103
|
const lifecycle = deps.lifecycle ?? createConsoleDaemonLifecycle();
|
|
38819
40104
|
await lifecycle.stop();
|
|
@@ -38867,7 +40152,7 @@ async function main() {
|
|
|
38867
40152
|
}
|
|
38868
40153
|
function resolveDefaultServerModulePath2() {
|
|
38869
40154
|
const builtPath = new URL("../dist/cli.mjs", import.meta.url).pathname;
|
|
38870
|
-
if (
|
|
40155
|
+
if (fs14__default.existsSync(builtPath)) return builtPath;
|
|
38871
40156
|
return fileURLToPath(import.meta.url);
|
|
38872
40157
|
}
|
|
38873
40158
|
function readHookSessionId(env) {
|
|
@@ -38921,7 +40206,7 @@ function readStdinBestEffort() {
|
|
|
38921
40206
|
});
|
|
38922
40207
|
});
|
|
38923
40208
|
}
|
|
38924
|
-
var isDirectRun = process4.argv[1] &&
|
|
40209
|
+
var isDirectRun = process4.argv[1] && path27__default.resolve(process4.argv[1]) === fileURLToPath(import.meta.url);
|
|
38925
40210
|
if (isDirectRun) {
|
|
38926
40211
|
await main().catch((error51) => {
|
|
38927
40212
|
process4.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
|
|
@@ -38930,4 +40215,4 @@ if (isDirectRun) {
|
|
|
38930
40215
|
});
|
|
38931
40216
|
}
|
|
38932
40217
|
|
|
38933
|
-
export { buildConsoleHelpText, createConsoleDaemonLifecycle, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|
|
40218
|
+
export { assertCliCanControlDaemon, buildConsoleHelpText, createConsoleDaemonLifecycle, isLockProcessAlive, main, openFleetConsole, parseConsoleCliMode, parseConsoleHookCommand, runConsoleRestart, runConsoleStatus, runConsoleStop };
|