@wairon/cli 5.1.1-dev.5 → 5.1.1-dev.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/index.js +1118 -880
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +488 -250
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.7";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -3976,9 +3976,9 @@ var init_surfaces = __esm({
|
|
|
3976
3976
|
// src/core/rules/namespace.ts
|
|
3977
3977
|
function isExternalNamespaceRef(ctx, ref) {
|
|
3978
3978
|
if (ref.startsWith("::") || ref.startsWith("super::")) return true;
|
|
3979
|
-
const
|
|
3980
|
-
if (
|
|
3981
|
-
return !ctx.subsystemIds.has(ref.slice(0,
|
|
3979
|
+
const sep9 = ref.indexOf("::");
|
|
3980
|
+
if (sep9 === -1) return false;
|
|
3981
|
+
return !ctx.subsystemIds.has(ref.slice(0, sep9));
|
|
3982
3982
|
}
|
|
3983
3983
|
function resolveSurfaceRef(ctx, ref) {
|
|
3984
3984
|
const local = ref.split("::").filter((seg) => seg && seg !== "super").pop();
|
|
@@ -4315,10 +4315,10 @@ function stepGraph(steps) {
|
|
|
4315
4315
|
const s = byNum.get(n);
|
|
4316
4316
|
if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
|
|
4317
4317
|
const entries = s.branches.map((b) => b.step).sort((a, b) => a - b);
|
|
4318
|
-
const
|
|
4318
|
+
const join50 = fallNext(s.endStep);
|
|
4319
4319
|
for (let i = 0; i < entries.length; i++) {
|
|
4320
4320
|
const armEnd = i + 1 < entries.length ? prevOf(entries[i + 1]) : s.endStep;
|
|
4321
|
-
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd,
|
|
4321
|
+
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join50);
|
|
4322
4322
|
}
|
|
4323
4323
|
}
|
|
4324
4324
|
const successorsOf = (n) => {
|
|
@@ -4392,7 +4392,8 @@ var init_narrative_flow = __esm({
|
|
|
4392
4392
|
{ code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" },
|
|
4393
4393
|
{ code: "JUMP_INTO_REGION", defaultSeverity: "warning", summary: "Jump lands in the middle of a loop/try body from outside \u2014 regions are entered through their header" },
|
|
4394
4394
|
{ code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" },
|
|
4395
|
-
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" }
|
|
4395
|
+
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" },
|
|
4396
|
+
{ code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
|
|
4396
4397
|
],
|
|
4397
4398
|
check(ctx) {
|
|
4398
4399
|
for (const impl of ctx.implementations) {
|
|
@@ -4401,6 +4402,23 @@ var init_narrative_flow = __esm({
|
|
|
4401
4402
|
const steps = implMethod.narrative;
|
|
4402
4403
|
if (!steps.length) continue;
|
|
4403
4404
|
const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
|
|
4405
|
+
const labelled = /* @__PURE__ */ new Map();
|
|
4406
|
+
for (const step of steps) {
|
|
4407
|
+
const label = step.label;
|
|
4408
|
+
if (!label) continue;
|
|
4409
|
+
const first = labelled.get(label);
|
|
4410
|
+
if (first !== void 0) {
|
|
4411
|
+
ctx.addIssue(
|
|
4412
|
+
"error",
|
|
4413
|
+
"DUPLICATE_STEP_LABEL",
|
|
4414
|
+
`${where}steps ${first} and ${step.stepNumber} share the label "${label}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`,
|
|
4415
|
+
impl.id,
|
|
4416
|
+
isDraftCtx
|
|
4417
|
+
);
|
|
4418
|
+
} else {
|
|
4419
|
+
labelled.set(label, step.stepNumber);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4404
4422
|
let sound = true;
|
|
4405
4423
|
const malformed = (msg) => {
|
|
4406
4424
|
sound = false;
|
|
@@ -7626,11 +7644,11 @@ var init_narrative_antipatterns = __esm({
|
|
|
7626
7644
|
const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
|
|
7627
7645
|
if (memberEdges.length === 0) continue;
|
|
7628
7646
|
const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
|
|
7629
|
-
const
|
|
7647
|
+
const path67 = [...keys].sort().join(" \u2192 ");
|
|
7630
7648
|
ctx.addIssue(
|
|
7631
7649
|
"warning",
|
|
7632
7650
|
"UNCONDITIONAL_CALL_CYCLE",
|
|
7633
|
-
`Call cycle with no guard: ${
|
|
7651
|
+
`Call cycle with no guard: ${path67} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
|
|
7634
7652
|
anchor.impl.id,
|
|
7635
7653
|
memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
|
|
7636
7654
|
);
|
|
@@ -14403,6 +14421,137 @@ var init_repository = __esm({
|
|
|
14403
14421
|
}
|
|
14404
14422
|
});
|
|
14405
14423
|
|
|
14424
|
+
// src/core/baseline.ts
|
|
14425
|
+
function diffSize(d) {
|
|
14426
|
+
return d.added.length + d.changed.length + d.removed.length;
|
|
14427
|
+
}
|
|
14428
|
+
function baselineDir() {
|
|
14429
|
+
return process.env["WAIRON_BASELINE_DIR"] || path17.join(os5.homedir(), ".wairon", "baselines");
|
|
14430
|
+
}
|
|
14431
|
+
function keyFor(root) {
|
|
14432
|
+
const normalized = path17.resolve(root).replace(/\\/g, "/").toLowerCase();
|
|
14433
|
+
return crypto3.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
14434
|
+
}
|
|
14435
|
+
function baselinePath(root) {
|
|
14436
|
+
return path17.join(baselineDir(), `${keyFor(root)}.json`);
|
|
14437
|
+
}
|
|
14438
|
+
function readBaseline(root = getProjectRoot()) {
|
|
14439
|
+
const p = baselinePath(root);
|
|
14440
|
+
if (!pathExists(p)) return null;
|
|
14441
|
+
try {
|
|
14442
|
+
return JSON.parse(fs13.readFileSync(p, "utf8"));
|
|
14443
|
+
} catch {
|
|
14444
|
+
return null;
|
|
14445
|
+
}
|
|
14446
|
+
}
|
|
14447
|
+
function writeBaseline(record2) {
|
|
14448
|
+
const p = baselinePath(record2.projectRoot);
|
|
14449
|
+
ensureDir(path17.dirname(p));
|
|
14450
|
+
fs13.writeFileSync(p, `${JSON.stringify(record2, null, 2)}
|
|
14451
|
+
`);
|
|
14452
|
+
return p;
|
|
14453
|
+
}
|
|
14454
|
+
function currentSpecs(root) {
|
|
14455
|
+
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path17.resolve(root, s.projectPath).split(path17.sep).join("/")}/`);
|
|
14456
|
+
const out = {};
|
|
14457
|
+
for (const [abs, content] of snapshotSpecFiles()) {
|
|
14458
|
+
const normalized = path17.resolve(abs).split(path17.sep).join("/");
|
|
14459
|
+
if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
|
|
14460
|
+
out[path17.relative(root, abs).split(path17.sep).join("/")] = content;
|
|
14461
|
+
}
|
|
14462
|
+
return out;
|
|
14463
|
+
}
|
|
14464
|
+
function captureBaseline(approvedBy, children = {}, root = getProjectRoot(), scope) {
|
|
14465
|
+
const system = loadSystemSpec();
|
|
14466
|
+
const current2 = currentSpecs(root);
|
|
14467
|
+
let specs = current2;
|
|
14468
|
+
if (scope) {
|
|
14469
|
+
const previous = readBaseline(root)?.specs ?? {};
|
|
14470
|
+
specs = { ...previous };
|
|
14471
|
+
for (const rel2 of Object.keys(previous)) {
|
|
14472
|
+
if (scope.paths.has(rel2) && current2[rel2] === void 0) delete specs[rel2];
|
|
14473
|
+
}
|
|
14474
|
+
for (const rel2 of scope.paths) {
|
|
14475
|
+
if (current2[rel2] !== void 0) specs[rel2] = current2[rel2];
|
|
14476
|
+
}
|
|
14477
|
+
}
|
|
14478
|
+
return {
|
|
14479
|
+
schemaVersion: SCHEMA_VERSION,
|
|
14480
|
+
projectRoot: path17.resolve(root),
|
|
14481
|
+
systemName: system?.name ?? path17.basename(root),
|
|
14482
|
+
approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14483
|
+
approvedBy,
|
|
14484
|
+
stateId: computeGateStateId(),
|
|
14485
|
+
specs,
|
|
14486
|
+
children
|
|
14487
|
+
};
|
|
14488
|
+
}
|
|
14489
|
+
function diffAgainstBaseline(root = getProjectRoot()) {
|
|
14490
|
+
const baseline = readBaseline(root);
|
|
14491
|
+
if (!baseline) return null;
|
|
14492
|
+
const current2 = currentSpecs(root);
|
|
14493
|
+
const added = [];
|
|
14494
|
+
const changed = [];
|
|
14495
|
+
const removed = [];
|
|
14496
|
+
const unchangedPaths = [];
|
|
14497
|
+
for (const [rel2, content] of Object.entries(current2)) {
|
|
14498
|
+
const before = baseline.specs[rel2];
|
|
14499
|
+
if (before === void 0) added.push(rel2);
|
|
14500
|
+
else if (before !== content) changed.push(rel2);
|
|
14501
|
+
else unchangedPaths.push(rel2);
|
|
14502
|
+
}
|
|
14503
|
+
for (const rel2 of Object.keys(baseline.specs)) {
|
|
14504
|
+
if (current2[rel2] === void 0) removed.push(rel2);
|
|
14505
|
+
}
|
|
14506
|
+
return {
|
|
14507
|
+
added: added.sort(),
|
|
14508
|
+
changed: changed.sort(),
|
|
14509
|
+
removed: removed.sort(),
|
|
14510
|
+
unchangedPaths: unchangedPaths.sort()
|
|
14511
|
+
};
|
|
14512
|
+
}
|
|
14513
|
+
function settledSpecPaths(root = getProjectRoot()) {
|
|
14514
|
+
const diff = diffAgainstBaseline(root);
|
|
14515
|
+
return diff ? new Set(diff.unchangedPaths) : null;
|
|
14516
|
+
}
|
|
14517
|
+
function pinOf(stateId) {
|
|
14518
|
+
return `${stateId.algorithm}:${stateId.digest}`;
|
|
14519
|
+
}
|
|
14520
|
+
function currentChildPins(mounts, root = getProjectRoot()) {
|
|
14521
|
+
const pins = {};
|
|
14522
|
+
for (const mount of mounts) {
|
|
14523
|
+
if (!mount.projectPath || mount.id.includes("::")) continue;
|
|
14524
|
+
const childRoot = path17.resolve(root, mount.projectPath);
|
|
14525
|
+
const child = readBaseline(childRoot);
|
|
14526
|
+
if (child) pins[mount.id] = pinOf(child.stateId);
|
|
14527
|
+
}
|
|
14528
|
+
return pins;
|
|
14529
|
+
}
|
|
14530
|
+
function movedChildren(mounts, root = getProjectRoot()) {
|
|
14531
|
+
const baseline = readBaseline(root);
|
|
14532
|
+
if (!baseline) return [];
|
|
14533
|
+
const now = currentChildPins(mounts, root);
|
|
14534
|
+
const moved = [];
|
|
14535
|
+
for (const [id, pinned] of Object.entries(baseline.children)) {
|
|
14536
|
+
const current2 = now[id] ?? null;
|
|
14537
|
+
if (current2 !== pinned) moved.push({ id, pinned, now: current2 });
|
|
14538
|
+
}
|
|
14539
|
+
return moved;
|
|
14540
|
+
}
|
|
14541
|
+
var fs13, os5, path17, crypto3, SCHEMA_VERSION;
|
|
14542
|
+
var init_baseline = __esm({
|
|
14543
|
+
"src/core/baseline.ts"() {
|
|
14544
|
+
"use strict";
|
|
14545
|
+
fs13 = __toESM(require("fs"));
|
|
14546
|
+
os5 = __toESM(require("os"));
|
|
14547
|
+
path17 = __toESM(require("path"));
|
|
14548
|
+
crypto3 = __toESM(require("crypto"));
|
|
14549
|
+
init_fs();
|
|
14550
|
+
init_specs2();
|
|
14551
|
+
SCHEMA_VERSION = "1.0.0";
|
|
14552
|
+
}
|
|
14553
|
+
});
|
|
14554
|
+
|
|
14406
14555
|
// src/core/validation.ts
|
|
14407
14556
|
var validation_exports = {};
|
|
14408
14557
|
__export(validation_exports, {
|
|
@@ -14533,7 +14682,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14533
14682
|
const types = loadTypeSpecs();
|
|
14534
14683
|
const surfaceSnapshots = loadSurfaceSnapshots();
|
|
14535
14684
|
const codeModel = buildCodeModel(implementations, getProjectRoot());
|
|
14536
|
-
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] :
|
|
14685
|
+
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
|
|
14537
14686
|
const statusSnapshot = statusBearing.map((s) => s.status);
|
|
14538
14687
|
for (const s of statusBearing) s.status = "complete";
|
|
14539
14688
|
try {
|
|
@@ -14655,10 +14804,34 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14655
14804
|
});
|
|
14656
14805
|
}
|
|
14657
14806
|
}
|
|
14807
|
+
function settledStatusBearing(loaded) {
|
|
14808
|
+
let settled;
|
|
14809
|
+
try {
|
|
14810
|
+
settled = settledSpecPaths();
|
|
14811
|
+
} catch {
|
|
14812
|
+
return [];
|
|
14813
|
+
}
|
|
14814
|
+
if (!settled || settled.size === 0) return [];
|
|
14815
|
+
const root = getProjectRoot();
|
|
14816
|
+
const index = scanAllSpecs();
|
|
14817
|
+
const rel2 = (abs) => path18.relative(root, abs).split(path18.sep).join("/");
|
|
14818
|
+
const out = [];
|
|
14819
|
+
const take = (specs, paths) => {
|
|
14820
|
+
for (const spec of specs) {
|
|
14821
|
+
const p = paths[spec.id];
|
|
14822
|
+
if (p && settled.has(rel2(p))) out.push(spec);
|
|
14823
|
+
}
|
|
14824
|
+
};
|
|
14825
|
+
take(loaded.subsystems, index.paths.subsystem);
|
|
14826
|
+
take(loaded.components, index.paths.component);
|
|
14827
|
+
take(loaded.interfaces, index.paths.interface);
|
|
14828
|
+
take(loaded.implementations, index.paths.implementation);
|
|
14829
|
+
return out;
|
|
14830
|
+
}
|
|
14658
14831
|
function validateAsComplete(options) {
|
|
14659
14832
|
return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
|
|
14660
14833
|
}
|
|
14661
|
-
var SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
|
|
14834
|
+
var path18, SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
|
|
14662
14835
|
var init_validation = __esm({
|
|
14663
14836
|
"src/core/validation.ts"() {
|
|
14664
14837
|
"use strict";
|
|
@@ -14672,6 +14845,8 @@ var init_validation = __esm({
|
|
|
14672
14845
|
init_source_analysis();
|
|
14673
14846
|
init_specs2();
|
|
14674
14847
|
init_fs();
|
|
14848
|
+
path18 = __toESM(require("path"));
|
|
14849
|
+
init_baseline();
|
|
14675
14850
|
SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
|
|
14676
14851
|
"UNDEFINED_TYPE_REFERENCE",
|
|
14677
14852
|
"INVALID_DEPENDENCY_REFERENCE",
|
|
@@ -15001,8 +15176,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
15001
15176
|
const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
15002
15177
|
for (const step of steps) {
|
|
15003
15178
|
for (const par of parallelArms) {
|
|
15004
|
-
const
|
|
15005
|
-
if (
|
|
15179
|
+
const sep9 = par.sepByStep.get(step.stepNumber);
|
|
15180
|
+
if (sep9 !== void 0) lines.push(` and ${escapeLabel(sep9)}`);
|
|
15006
15181
|
}
|
|
15007
15182
|
switch (step.type) {
|
|
15008
15183
|
case "local":
|
|
@@ -15282,6 +15457,7 @@ __export(specs_exports, {
|
|
|
15282
15457
|
saveTypeSpec: () => saveTypeSpec,
|
|
15283
15458
|
scanAllSpecs: () => scanAllSpecs,
|
|
15284
15459
|
snapshotSpecFiles: () => snapshotSpecFiles,
|
|
15460
|
+
specPathsInScope: () => specPathsInScope,
|
|
15285
15461
|
splitNamespace: () => splitNamespace,
|
|
15286
15462
|
updateSpec: () => updateSpec,
|
|
15287
15463
|
workspaceFor: () => workspaceFor
|
|
@@ -15355,16 +15531,16 @@ function relativizeId(id, prefix) {
|
|
|
15355
15531
|
return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
|
|
15356
15532
|
}
|
|
15357
15533
|
function isWithin(dir, file) {
|
|
15358
|
-
const d =
|
|
15359
|
-
const f =
|
|
15360
|
-
return f === d || f.startsWith(d +
|
|
15534
|
+
const d = path19.resolve(dir);
|
|
15535
|
+
const f = path19.resolve(file);
|
|
15536
|
+
return f === d || f.startsWith(d + path19.sep);
|
|
15361
15537
|
}
|
|
15362
15538
|
function projectPathEscapesRoot(projectRoot2, projectPath, resolvedChildDir) {
|
|
15363
|
-
return
|
|
15539
|
+
return path19.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir);
|
|
15364
15540
|
}
|
|
15365
15541
|
function assertContainedProjectPath(projectRoot2, projectPath) {
|
|
15366
|
-
const root =
|
|
15367
|
-
const resolved =
|
|
15542
|
+
const root = path19.resolve(projectRoot2);
|
|
15543
|
+
const resolved = path19.resolve(root, projectPath);
|
|
15368
15544
|
if (projectPathEscapesRoot(root, projectPath, resolved)) {
|
|
15369
15545
|
throw new Error(
|
|
15370
15546
|
`projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute paths and ../-escaping paths are rejected so a chained subproject is always contained by its parent.`
|
|
@@ -15375,11 +15551,11 @@ function assertContainedProjectPath(projectRoot2, projectPath) {
|
|
|
15375
15551
|
function findChainingParent(childRoot) {
|
|
15376
15552
|
let childResolved;
|
|
15377
15553
|
try {
|
|
15378
|
-
childResolved =
|
|
15554
|
+
childResolved = path19.resolve(childRoot);
|
|
15379
15555
|
} catch {
|
|
15380
15556
|
return null;
|
|
15381
15557
|
}
|
|
15382
|
-
let dir =
|
|
15558
|
+
let dir = path19.dirname(childResolved);
|
|
15383
15559
|
for (let hops = 0; hops < 32; hops++) {
|
|
15384
15560
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
15385
15561
|
if (pathExists(specsDir)) {
|
|
@@ -15394,7 +15570,7 @@ function findChainingParent(childRoot) {
|
|
|
15394
15570
|
const projectPath = raw.projectPath;
|
|
15395
15571
|
if (typeof projectPath === "string" && projectPath.trim() !== "") {
|
|
15396
15572
|
try {
|
|
15397
|
-
if (
|
|
15573
|
+
if (path19.resolve(dir, projectPath) === childResolved) {
|
|
15398
15574
|
const id = raw.id;
|
|
15399
15575
|
return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
|
|
15400
15576
|
}
|
|
@@ -15404,7 +15580,7 @@ function findChainingParent(childRoot) {
|
|
|
15404
15580
|
}
|
|
15405
15581
|
}
|
|
15406
15582
|
}
|
|
15407
|
-
const up =
|
|
15583
|
+
const up = path19.dirname(dir);
|
|
15408
15584
|
if (up === dir) break;
|
|
15409
15585
|
dir = up;
|
|
15410
15586
|
}
|
|
@@ -15509,18 +15685,18 @@ function findOwner(id, components) {
|
|
|
15509
15685
|
}) ?? null;
|
|
15510
15686
|
}
|
|
15511
15687
|
function moveComponentFolder(fromDir, toDir) {
|
|
15512
|
-
if (
|
|
15513
|
-
if (!
|
|
15514
|
-
ensureDir(
|
|
15515
|
-
|
|
15688
|
+
if (path19.normalize(fromDir) === path19.normalize(toDir)) return false;
|
|
15689
|
+
if (!fs14.existsSync(fromDir) || fs14.existsSync(toDir)) return false;
|
|
15690
|
+
ensureDir(path19.dirname(toDir));
|
|
15691
|
+
fs14.renameSync(fromDir, toDir);
|
|
15516
15692
|
return true;
|
|
15517
15693
|
}
|
|
15518
15694
|
function cleanEmptyDirs(filePath, specsRoot) {
|
|
15519
|
-
let dir =
|
|
15695
|
+
let dir = path19.dirname(filePath);
|
|
15520
15696
|
while (dir !== specsRoot && dir.startsWith(specsRoot)) {
|
|
15521
|
-
if (
|
|
15522
|
-
|
|
15523
|
-
dir =
|
|
15697
|
+
if (fs14.existsSync(dir) && fs14.readdirSync(dir).length === 0) {
|
|
15698
|
+
fs14.rmdirSync(dir);
|
|
15699
|
+
dir = path19.dirname(dir);
|
|
15524
15700
|
} else {
|
|
15525
15701
|
break;
|
|
15526
15702
|
}
|
|
@@ -15539,13 +15715,13 @@ function parseOrThrow(schema, value, kind, id) {
|
|
|
15539
15715
|
function computeSpecTreeSignature(dirs) {
|
|
15540
15716
|
const parts = [];
|
|
15541
15717
|
for (const dir of dirs) {
|
|
15542
|
-
if (!
|
|
15718
|
+
if (!fs14.existsSync(dir)) {
|
|
15543
15719
|
parts.push(`${dir}:missing`);
|
|
15544
15720
|
continue;
|
|
15545
15721
|
}
|
|
15546
15722
|
for (const f of listFilesRecursive(dir, ".yaml")) {
|
|
15547
15723
|
try {
|
|
15548
|
-
const st =
|
|
15724
|
+
const st = fs14.statSync(f);
|
|
15549
15725
|
parts.push(`${f}:${st.mtimeMs}:${st.size}`);
|
|
15550
15726
|
} catch {
|
|
15551
15727
|
parts.push(`${f}:gone`);
|
|
@@ -15555,7 +15731,7 @@ function computeSpecTreeSignature(dirs) {
|
|
|
15555
15731
|
return parts.join("|");
|
|
15556
15732
|
}
|
|
15557
15733
|
function workspaceFor(rootDir) {
|
|
15558
|
-
const key =
|
|
15734
|
+
const key = path19.resolve(rootDir);
|
|
15559
15735
|
let ws = workspaces.get(key);
|
|
15560
15736
|
if (!ws) {
|
|
15561
15737
|
ws = new SpecWorkspace(key);
|
|
@@ -15694,7 +15870,7 @@ function readLockState() {
|
|
|
15694
15870
|
return { state: stateIdEquals(record2.stateId, current2) ? "locked" : "stale", record: record2, current: current2 };
|
|
15695
15871
|
}
|
|
15696
15872
|
function computeStateIdAt(root) {
|
|
15697
|
-
const resolved =
|
|
15873
|
+
const resolved = path19.resolve(root);
|
|
15698
15874
|
return runWithProjectRoot(resolved, () => {
|
|
15699
15875
|
workspaceFor(resolved).invalidate();
|
|
15700
15876
|
const system = loadSystemSpec();
|
|
@@ -15724,12 +15900,15 @@ function collectPromotableSpecs(scopeSubsystem) {
|
|
|
15724
15900
|
function applySpecStatus(kind, id, status2) {
|
|
15725
15901
|
current().applySpecStatus(kind, id, status2);
|
|
15726
15902
|
}
|
|
15903
|
+
function specPathsInScope(scopeSubsystem) {
|
|
15904
|
+
return current().specPathsInScope(scopeSubsystem);
|
|
15905
|
+
}
|
|
15727
15906
|
function snapshotSpecFiles() {
|
|
15728
15907
|
return current().snapshotSpecFiles();
|
|
15729
15908
|
}
|
|
15730
15909
|
function restoreSpecFiles(snapshot) {
|
|
15731
15910
|
for (const [file, content] of snapshot) {
|
|
15732
|
-
|
|
15911
|
+
fs14.writeFileSync(file, content);
|
|
15733
15912
|
}
|
|
15734
15913
|
}
|
|
15735
15914
|
function findLegacySpecFiles() {
|
|
@@ -15738,12 +15917,12 @@ function findLegacySpecFiles() {
|
|
|
15738
15917
|
function updateSpec(kind, id, delta, hooks) {
|
|
15739
15918
|
return current().updateSpec(kind, id, delta, hooks);
|
|
15740
15919
|
}
|
|
15741
|
-
var
|
|
15920
|
+
var fs14, path19, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
15742
15921
|
var init_specs2 = __esm({
|
|
15743
15922
|
"src/core/specs.ts"() {
|
|
15744
15923
|
"use strict";
|
|
15745
|
-
|
|
15746
|
-
|
|
15924
|
+
fs14 = __toESM(require("fs"));
|
|
15925
|
+
path19 = __toESM(require("path"));
|
|
15747
15926
|
init_loader();
|
|
15748
15927
|
init_fs();
|
|
15749
15928
|
init_statehash();
|
|
@@ -15764,7 +15943,7 @@ var init_specs2 = __esm({
|
|
|
15764
15943
|
this.rootSubsystems = /* @__PURE__ */ new Set();
|
|
15765
15944
|
this.scanVisitedSpecDirs = [];
|
|
15766
15945
|
this.loaderIssues = [];
|
|
15767
|
-
this.rootDir =
|
|
15946
|
+
this.rootDir = path19.resolve(rootDir);
|
|
15768
15947
|
this.paths = aiPathsAt(this.rootDir);
|
|
15769
15948
|
}
|
|
15770
15949
|
invalidate() {
|
|
@@ -15791,7 +15970,7 @@ var init_specs2 = __esm({
|
|
|
15791
15970
|
this.rootSubsystems.clear();
|
|
15792
15971
|
this.cachedRecursive = recursive;
|
|
15793
15972
|
this.scanVisitedSpecDirs = [];
|
|
15794
|
-
const visited = /* @__PURE__ */ new Set([
|
|
15973
|
+
const visited = /* @__PURE__ */ new Set([path19.resolve(this.rootDir)]);
|
|
15795
15974
|
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
15796
15975
|
this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
|
|
15797
15976
|
this.cachedSpecDirs = this.scanVisitedSpecDirs;
|
|
@@ -15806,10 +15985,10 @@ var init_specs2 = __esm({
|
|
|
15806
15985
|
this.scanVisitedSpecDirs.push(specsDir);
|
|
15807
15986
|
if (!pathExists(specsDir)) return index;
|
|
15808
15987
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
15809
|
-
const systemYaml =
|
|
15988
|
+
const systemYaml = path19.normalize(projectPaths.specsSystem());
|
|
15810
15989
|
const localSubprojects = [];
|
|
15811
15990
|
for (const file of files) {
|
|
15812
|
-
const normFile =
|
|
15991
|
+
const normFile = path19.normalize(file);
|
|
15813
15992
|
if (normFile === systemYaml) continue;
|
|
15814
15993
|
let detectedType = "spec";
|
|
15815
15994
|
try {
|
|
@@ -15819,7 +15998,7 @@ var init_specs2 = __esm({
|
|
|
15819
15998
|
severity: "error",
|
|
15820
15999
|
code: "INVALID_YAML",
|
|
15821
16000
|
message: `Spec file "${file}" is not a valid YAML object or is empty.`,
|
|
15822
|
-
specId:
|
|
16001
|
+
specId: path19.basename(file, ".yaml")
|
|
15823
16002
|
});
|
|
15824
16003
|
continue;
|
|
15825
16004
|
}
|
|
@@ -15851,8 +16030,8 @@ var init_specs2 = __esm({
|
|
|
15851
16030
|
detectedType = "implementation";
|
|
15852
16031
|
const parsed = ImplementationSpecSchema.parse(raw);
|
|
15853
16032
|
if (parsed.sourcePath) {
|
|
15854
|
-
const absSourcePath =
|
|
15855
|
-
parsed.sourcePath =
|
|
16033
|
+
const absSourcePath = path19.resolve(projectDir, parsed.sourcePath);
|
|
16034
|
+
parsed.sourcePath = path19.relative(projectDir, absSourcePath).replace(/\\/g, "/");
|
|
15856
16035
|
}
|
|
15857
16036
|
index.implementations.push(parsed);
|
|
15858
16037
|
index.paths.implementation[parsed.id] = file;
|
|
@@ -15874,11 +16053,11 @@ var init_specs2 = __esm({
|
|
|
15874
16053
|
severity: "error",
|
|
15875
16054
|
code: "UNKNOWN_SPEC_TYPE",
|
|
15876
16055
|
message: `Spec file "${file}" does not match any recognized L1-L4 schema structure.`,
|
|
15877
|
-
specId:
|
|
16056
|
+
specId: path19.basename(file, ".yaml")
|
|
15878
16057
|
});
|
|
15879
16058
|
}
|
|
15880
16059
|
} catch (e) {
|
|
15881
|
-
const filename =
|
|
16060
|
+
const filename = path19.basename(file, ".yaml");
|
|
15882
16061
|
this.loaderIssues.push({
|
|
15883
16062
|
severity: "error",
|
|
15884
16063
|
code: "SCHEMA_VALIDATION_ERROR",
|
|
@@ -15976,7 +16155,7 @@ var init_specs2 = __esm({
|
|
|
15976
16155
|
}
|
|
15977
16156
|
if (currentDepth < maxDepth) {
|
|
15978
16157
|
for (const subproj of localSubprojects) {
|
|
15979
|
-
const childDir =
|
|
16158
|
+
const childDir = path19.resolve(projectDir, subproj.projectPath);
|
|
15980
16159
|
if (projectPathEscapesRoot(this.rootDir, subproj.projectPath, childDir)) {
|
|
15981
16160
|
this.loaderIssues.push({
|
|
15982
16161
|
severity: "error",
|
|
@@ -15995,7 +16174,7 @@ var init_specs2 = __esm({
|
|
|
15995
16174
|
});
|
|
15996
16175
|
continue;
|
|
15997
16176
|
}
|
|
15998
|
-
if (!
|
|
16177
|
+
if (!fs14.existsSync(childDir)) {
|
|
15999
16178
|
this.loaderIssues.push({
|
|
16000
16179
|
severity: "error",
|
|
16001
16180
|
code: "SUBPROJECT_NOT_FOUND",
|
|
@@ -16038,7 +16217,7 @@ var init_specs2 = __esm({
|
|
|
16038
16217
|
const index = this.scanAll();
|
|
16039
16218
|
const sub = index.subsystems.find((s) => s.id === currentPrefix);
|
|
16040
16219
|
if (sub && sub.projectPath) {
|
|
16041
|
-
const nextDir =
|
|
16220
|
+
const nextDir = path19.resolve(currentDir, sub.projectPath);
|
|
16042
16221
|
if (projectPathEscapesRoot(this.rootDir, sub.projectPath, nextDir)) {
|
|
16043
16222
|
this.loaderIssues.push({
|
|
16044
16223
|
severity: "error",
|
|
@@ -16090,9 +16269,9 @@ var init_specs2 = __esm({
|
|
|
16090
16269
|
return index.paths.subsystem[matches2[0]];
|
|
16091
16270
|
}
|
|
16092
16271
|
if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
|
|
16093
|
-
return
|
|
16272
|
+
return path19.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
|
|
16094
16273
|
}
|
|
16095
|
-
return
|
|
16274
|
+
return path19.join(this.paths.specsDir(), id, ".index.yaml");
|
|
16096
16275
|
}
|
|
16097
16276
|
getComponentPath(id, subsystemId) {
|
|
16098
16277
|
const index = this.scanAll();
|
|
@@ -16112,21 +16291,21 @@ var init_specs2 = __esm({
|
|
|
16112
16291
|
if (owner) {
|
|
16113
16292
|
const ownerPath = index.paths.component[owner.id];
|
|
16114
16293
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
16115
|
-
return
|
|
16294
|
+
return path19.join(path19.dirname(ownerPath), id, ".index.yaml");
|
|
16116
16295
|
}
|
|
16117
16296
|
}
|
|
16118
16297
|
if (subsystemId) {
|
|
16119
16298
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
16120
|
-
const subDir =
|
|
16299
|
+
const subDir = path19.dirname(subPath);
|
|
16121
16300
|
if (subPath.endsWith(".index.yaml")) {
|
|
16122
|
-
return
|
|
16301
|
+
return path19.join(subDir, id, ".index.yaml");
|
|
16123
16302
|
}
|
|
16124
16303
|
}
|
|
16125
16304
|
if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
|
|
16126
|
-
return
|
|
16305
|
+
return path19.join(this.paths.specsComponentsDir(), `${id}.yaml`);
|
|
16127
16306
|
}
|
|
16128
16307
|
const targetSubsystem = subsystemId || "default";
|
|
16129
|
-
return
|
|
16308
|
+
return path19.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
|
|
16130
16309
|
}
|
|
16131
16310
|
getInterfacePath(id, componentId) {
|
|
16132
16311
|
const index = this.scanAll();
|
|
@@ -16152,16 +16331,16 @@ var init_specs2 = __esm({
|
|
|
16152
16331
|
}
|
|
16153
16332
|
if (componentId) {
|
|
16154
16333
|
const compPath = this.getComponentPath(componentId);
|
|
16155
|
-
const compDir =
|
|
16334
|
+
const compDir = path19.dirname(compPath);
|
|
16156
16335
|
if (compPath.endsWith(".index.yaml")) {
|
|
16157
|
-
return
|
|
16336
|
+
return path19.join(compDir, ".interface.yaml");
|
|
16158
16337
|
}
|
|
16159
16338
|
}
|
|
16160
16339
|
if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
|
|
16161
|
-
return
|
|
16340
|
+
return path19.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
|
|
16162
16341
|
}
|
|
16163
16342
|
const targetComponent = componentId || "default";
|
|
16164
|
-
return
|
|
16343
|
+
return path19.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
|
|
16165
16344
|
}
|
|
16166
16345
|
getImplementationPath(id, contractId) {
|
|
16167
16346
|
const index = this.scanAll();
|
|
@@ -16187,16 +16366,16 @@ var init_specs2 = __esm({
|
|
|
16187
16366
|
}
|
|
16188
16367
|
if (contractId) {
|
|
16189
16368
|
const intfPath = this.getInterfacePath(contractId);
|
|
16190
|
-
const intfDir =
|
|
16369
|
+
const intfDir = path19.dirname(intfPath);
|
|
16191
16370
|
if (intfPath.endsWith(".interface.yaml")) {
|
|
16192
|
-
return
|
|
16371
|
+
return path19.join(intfDir, ".implementation.yaml");
|
|
16193
16372
|
}
|
|
16194
16373
|
}
|
|
16195
16374
|
if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
|
|
16196
|
-
return
|
|
16375
|
+
return path19.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
|
|
16197
16376
|
}
|
|
16198
16377
|
const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
|
|
16199
|
-
return
|
|
16378
|
+
return path19.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
|
|
16200
16379
|
}
|
|
16201
16380
|
getTypePath(id, subsystemId, group) {
|
|
16202
16381
|
const index = this.scanAll();
|
|
@@ -16227,7 +16406,7 @@ var init_specs2 = __esm({
|
|
|
16227
16406
|
const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
|
|
16228
16407
|
if (groupPath) {
|
|
16229
16408
|
const localId2 = id.split("::").pop();
|
|
16230
|
-
return
|
|
16409
|
+
return path19.join(path19.dirname(groupPath), `${localId2}.yaml`);
|
|
16231
16410
|
}
|
|
16232
16411
|
}
|
|
16233
16412
|
let localId = id;
|
|
@@ -16240,12 +16419,12 @@ var init_specs2 = __esm({
|
|
|
16240
16419
|
}
|
|
16241
16420
|
if (subsystemId) {
|
|
16242
16421
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
16243
|
-
const subDir =
|
|
16422
|
+
const subDir = path19.dirname(subPath);
|
|
16244
16423
|
if (subPath.endsWith(".index.yaml")) {
|
|
16245
|
-
return
|
|
16424
|
+
return path19.join(subDir, "types", `${localId}.yaml`);
|
|
16246
16425
|
}
|
|
16247
16426
|
}
|
|
16248
|
-
return
|
|
16427
|
+
return path19.join(this.paths.specsTypesDir(), `${localId}.yaml`);
|
|
16249
16428
|
}
|
|
16250
16429
|
getGroupPath(id, subsystemId) {
|
|
16251
16430
|
const index = this.scanAll();
|
|
@@ -16279,12 +16458,12 @@ var init_specs2 = __esm({
|
|
|
16279
16458
|
}
|
|
16280
16459
|
if (subsystemId) {
|
|
16281
16460
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
16282
|
-
const subDir =
|
|
16461
|
+
const subDir = path19.dirname(subPath);
|
|
16283
16462
|
if (subPath.endsWith(".index.yaml")) {
|
|
16284
|
-
return
|
|
16463
|
+
return path19.join(subDir, "types", localId, ".index.yaml");
|
|
16285
16464
|
}
|
|
16286
16465
|
}
|
|
16287
|
-
return
|
|
16466
|
+
return path19.join(this.paths.specsTypesDir(), localId, ".index.yaml");
|
|
16288
16467
|
}
|
|
16289
16468
|
// -------------------------------------------------------------------------
|
|
16290
16469
|
// Level 0: System
|
|
@@ -16307,7 +16486,7 @@ var init_specs2 = __esm({
|
|
|
16307
16486
|
}
|
|
16308
16487
|
saveSystemSpec(spec) {
|
|
16309
16488
|
const p = this.paths.specsSystem();
|
|
16310
|
-
ensureDir(
|
|
16489
|
+
ensureDir(path19.dirname(p));
|
|
16311
16490
|
writeYamlFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
|
|
16312
16491
|
invalidateSpecCache();
|
|
16313
16492
|
}
|
|
@@ -16425,7 +16604,7 @@ var init_specs2 = __esm({
|
|
|
16425
16604
|
}
|
|
16426
16605
|
saveSubsystemSpec(spec) {
|
|
16427
16606
|
const p = this.getSubsystemPath(spec.id);
|
|
16428
|
-
ensureDir(
|
|
16607
|
+
ensureDir(path19.dirname(p));
|
|
16429
16608
|
const { prefix } = splitNamespace(spec.id);
|
|
16430
16609
|
if (!prefix && spec.projectPath && spec.projectPath.trim() !== "") {
|
|
16431
16610
|
assertContainedProjectPath(this.rootDir, spec.projectPath);
|
|
@@ -16456,9 +16635,9 @@ var init_specs2 = __esm({
|
|
|
16456
16635
|
}
|
|
16457
16636
|
deleteSubsystemSpec(id) {
|
|
16458
16637
|
const p = this.getSubsystemPath(id);
|
|
16459
|
-
if (!
|
|
16460
|
-
|
|
16461
|
-
cleanEmptyDirs(p,
|
|
16638
|
+
if (!fs14.existsSync(p)) return false;
|
|
16639
|
+
fs14.unlinkSync(p);
|
|
16640
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16462
16641
|
invalidateSpecCache();
|
|
16463
16642
|
return true;
|
|
16464
16643
|
}
|
|
@@ -16491,7 +16670,7 @@ var init_specs2 = __esm({
|
|
|
16491
16670
|
saveComponentSpec(spec, opts) {
|
|
16492
16671
|
const notices = [];
|
|
16493
16672
|
const p = this.getComponentPath(spec.id, spec.subsystem);
|
|
16494
|
-
ensureDir(
|
|
16673
|
+
ensureDir(path19.dirname(p));
|
|
16495
16674
|
const specToWrite = this.prepareComponentForWrite(spec);
|
|
16496
16675
|
const existing = this.loadComponentSpec(spec.id);
|
|
16497
16676
|
if (existing) {
|
|
@@ -16508,7 +16687,7 @@ var init_specs2 = __esm({
|
|
|
16508
16687
|
const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
|
|
16509
16688
|
if (subsystemChanged && !p.endsWith(".index.yaml")) {
|
|
16510
16689
|
notices.push(
|
|
16511
|
-
`component "${spec.id}" already exists at ${
|
|
16690
|
+
`component "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
|
|
16512
16691
|
);
|
|
16513
16692
|
}
|
|
16514
16693
|
specToWrite.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -16519,9 +16698,9 @@ var init_specs2 = __esm({
|
|
|
16519
16698
|
}
|
|
16520
16699
|
deleteComponentSpec(id) {
|
|
16521
16700
|
const p = this.getComponentPath(id);
|
|
16522
|
-
if (!
|
|
16523
|
-
|
|
16524
|
-
cleanEmptyDirs(p,
|
|
16701
|
+
if (!fs14.existsSync(p)) return false;
|
|
16702
|
+
fs14.unlinkSync(p);
|
|
16703
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16525
16704
|
invalidateSpecCache();
|
|
16526
16705
|
return true;
|
|
16527
16706
|
}
|
|
@@ -16534,12 +16713,12 @@ var init_specs2 = __esm({
|
|
|
16534
16713
|
if (owner) {
|
|
16535
16714
|
const ownerPath = index.paths.component[owner.id];
|
|
16536
16715
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
16537
|
-
const ownerSubDir =
|
|
16538
|
-
return
|
|
16716
|
+
const ownerSubDir = path19.dirname(this.getSubsystemPath(owner.subsystem));
|
|
16717
|
+
return path19.join(ownerSubDir, owner.id, comp.id);
|
|
16539
16718
|
}
|
|
16540
16719
|
}
|
|
16541
|
-
const subDir =
|
|
16542
|
-
return
|
|
16720
|
+
const subDir = path19.dirname(this.getSubsystemPath(comp.subsystem));
|
|
16721
|
+
return path19.join(subDir, comp.id);
|
|
16543
16722
|
}
|
|
16544
16723
|
/**
|
|
16545
16724
|
* Move component folders so the physical tree mirrors ownership: each owned
|
|
@@ -16555,7 +16734,7 @@ var init_specs2 = __esm({
|
|
|
16555
16734
|
if (!currentPath) continue;
|
|
16556
16735
|
const desiredDir = this.desiredComponentDir(comp, index);
|
|
16557
16736
|
if (!desiredDir) continue;
|
|
16558
|
-
if (moveComponentFolder(
|
|
16737
|
+
if (moveComponentFolder(path19.dirname(currentPath), desiredDir)) moved.push(comp.id);
|
|
16559
16738
|
}
|
|
16560
16739
|
if (moved.length) invalidateSpecCache();
|
|
16561
16740
|
return moved;
|
|
@@ -16585,16 +16764,46 @@ var init_specs2 = __esm({
|
|
|
16585
16764
|
return null;
|
|
16586
16765
|
}
|
|
16587
16766
|
}
|
|
16767
|
+
/**
|
|
16768
|
+
* Refuse a write that would land on a file already holding a DIFFERENT spec.
|
|
16769
|
+
*
|
|
16770
|
+
* In the nested layout a spec's path is derived from its parent — a component
|
|
16771
|
+
* for an interface, a contract for an implementation — and the spec's own id
|
|
16772
|
+
* is not part of it (see getInterfacePath / getImplementationPath, which take
|
|
16773
|
+
* the id but ignore it once the parent resolves). So a second id bound to the
|
|
16774
|
+
* same parent resolves to the SAME file.
|
|
16775
|
+
*
|
|
16776
|
+
* Left unguarded that is silent data loss, and doubly invisible: the caller's
|
|
16777
|
+
* `existing` lookup is by the NEW id, finds nothing, and every re-author
|
|
16778
|
+
* notice ("REMOVED …", the carry-forward seam) stays quiet. An agent renaming
|
|
16779
|
+
* an interface by defining a new one destroys the old contract, its
|
|
16780
|
+
* narratives and its lint.allows, and is told "Successfully defined".
|
|
16781
|
+
*/
|
|
16782
|
+
assertPathHoldsNoOtherSpec(p, id, kind, parentLabel) {
|
|
16783
|
+
if (!pathExists(p)) return;
|
|
16784
|
+
let occupantId;
|
|
16785
|
+
try {
|
|
16786
|
+
occupantId = readYamlFile(p)?.id;
|
|
16787
|
+
} catch {
|
|
16788
|
+
return;
|
|
16789
|
+
}
|
|
16790
|
+
if (!occupantId) return;
|
|
16791
|
+
if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
|
|
16792
|
+
throw new Error(
|
|
16793
|
+
`Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path19.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
|
|
16794
|
+
);
|
|
16795
|
+
}
|
|
16588
16796
|
/** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
|
|
16589
16797
|
saveInterfaceSpec(spec, opts) {
|
|
16590
16798
|
const notices = [];
|
|
16591
16799
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
16592
|
-
|
|
16800
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
|
|
16801
|
+
ensureDir(path19.dirname(p));
|
|
16593
16802
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
16594
16803
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
16595
16804
|
if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
|
|
16596
16805
|
notices.push(
|
|
16597
|
-
`interface "${spec.id}" already exists at ${
|
|
16806
|
+
`interface "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
|
|
16598
16807
|
);
|
|
16599
16808
|
}
|
|
16600
16809
|
if (existing) {
|
|
@@ -16617,9 +16826,9 @@ var init_specs2 = __esm({
|
|
|
16617
16826
|
}
|
|
16618
16827
|
deleteInterfaceSpec(id) {
|
|
16619
16828
|
const p = this.getInterfacePath(id);
|
|
16620
|
-
if (!
|
|
16621
|
-
|
|
16622
|
-
cleanEmptyDirs(p,
|
|
16829
|
+
if (!fs14.existsSync(p)) return false;
|
|
16830
|
+
fs14.unlinkSync(p);
|
|
16831
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16623
16832
|
invalidateSpecCache();
|
|
16624
16833
|
return true;
|
|
16625
16834
|
}
|
|
@@ -16652,12 +16861,13 @@ var init_specs2 = __esm({
|
|
|
16652
16861
|
saveImplementationSpec(spec, opts) {
|
|
16653
16862
|
const notices = [];
|
|
16654
16863
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
16655
|
-
|
|
16864
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
|
|
16865
|
+
ensureDir(path19.dirname(p));
|
|
16656
16866
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
16657
16867
|
const existing = this.loadImplementationSpec(spec.id);
|
|
16658
16868
|
if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
|
|
16659
16869
|
notices.push(
|
|
16660
|
-
`implementation "${spec.id}" already exists at ${
|
|
16870
|
+
`implementation "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
|
|
16661
16871
|
);
|
|
16662
16872
|
}
|
|
16663
16873
|
if (existing) {
|
|
@@ -16673,9 +16883,9 @@ var init_specs2 = __esm({
|
|
|
16673
16883
|
}
|
|
16674
16884
|
deleteImplementationSpec(id) {
|
|
16675
16885
|
const p = this.getImplementationPath(id);
|
|
16676
|
-
if (!
|
|
16677
|
-
|
|
16678
|
-
cleanEmptyDirs(p,
|
|
16886
|
+
if (!fs14.existsSync(p)) return false;
|
|
16887
|
+
fs14.unlinkSync(p);
|
|
16888
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16679
16889
|
invalidateSpecCache();
|
|
16680
16890
|
return true;
|
|
16681
16891
|
}
|
|
@@ -16700,11 +16910,11 @@ var init_specs2 = __esm({
|
|
|
16700
16910
|
const existing = this.loadTypeSpec(spec.id);
|
|
16701
16911
|
const group = spec.group || (existing ? existing.group : void 0);
|
|
16702
16912
|
const p = this.getTypePath(spec.id, spec.subsystem, group);
|
|
16703
|
-
ensureDir(
|
|
16913
|
+
ensureDir(path19.dirname(p));
|
|
16704
16914
|
const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
|
|
16705
16915
|
if (subsystemChanged) {
|
|
16706
16916
|
notices.push(
|
|
16707
|
-
`type "${spec.id}" already exists at ${
|
|
16917
|
+
`type "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
|
|
16708
16918
|
);
|
|
16709
16919
|
}
|
|
16710
16920
|
if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
|
|
@@ -16727,9 +16937,9 @@ var init_specs2 = __esm({
|
|
|
16727
16937
|
deleteTypeSpec(id) {
|
|
16728
16938
|
const spec = this.loadTypeSpec(id);
|
|
16729
16939
|
const p = this.getTypePath(id, spec?.subsystem, spec?.group);
|
|
16730
|
-
if (!
|
|
16731
|
-
|
|
16732
|
-
cleanEmptyDirs(p,
|
|
16940
|
+
if (!fs14.existsSync(p)) return false;
|
|
16941
|
+
fs14.unlinkSync(p);
|
|
16942
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16733
16943
|
invalidateSpecCache();
|
|
16734
16944
|
return true;
|
|
16735
16945
|
}
|
|
@@ -16744,7 +16954,7 @@ var init_specs2 = __esm({
|
|
|
16744
16954
|
}
|
|
16745
16955
|
saveGroupSpec(spec) {
|
|
16746
16956
|
const p = this.getGroupPath(spec.id);
|
|
16747
|
-
ensureDir(
|
|
16957
|
+
ensureDir(path19.dirname(p));
|
|
16748
16958
|
const specToWrite = this.prepareGroupForWrite(spec);
|
|
16749
16959
|
const existing = this.loadGroupSpec(spec.id);
|
|
16750
16960
|
if (existing) {
|
|
@@ -16756,9 +16966,9 @@ var init_specs2 = __esm({
|
|
|
16756
16966
|
}
|
|
16757
16967
|
deleteGroupSpec(id) {
|
|
16758
16968
|
const p = this.getGroupPath(id);
|
|
16759
|
-
if (!
|
|
16760
|
-
|
|
16761
|
-
cleanEmptyDirs(p,
|
|
16969
|
+
if (!fs14.existsSync(p)) return false;
|
|
16970
|
+
fs14.unlinkSync(p);
|
|
16971
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16762
16972
|
invalidateSpecCache();
|
|
16763
16973
|
return true;
|
|
16764
16974
|
}
|
|
@@ -16806,6 +17016,50 @@ var init_specs2 = __esm({
|
|
|
16806
17016
|
}
|
|
16807
17017
|
return out;
|
|
16808
17018
|
}
|
|
17019
|
+
/**
|
|
17020
|
+
* Every spec FILE inside a subsystem scope (absolute paths), regardless of
|
|
17021
|
+
* status. `collectPromotableSpecs` answers a different question — which specs
|
|
17022
|
+
* are not yet complete — so it cannot stand in for this: a scoped approval
|
|
17023
|
+
* must cover the specs it approves whether or not they were already settled.
|
|
17024
|
+
*/
|
|
17025
|
+
specPathsInScope(scopeSubsystem) {
|
|
17026
|
+
const index = this.scanAll();
|
|
17027
|
+
const components = this.loadComponentSpecs();
|
|
17028
|
+
const interfaces = this.loadInterfaceSpecs();
|
|
17029
|
+
const implementations = this.loadImplementationSpecs();
|
|
17030
|
+
const inScope = (specSubsystem) => {
|
|
17031
|
+
if (!scopeSubsystem) return true;
|
|
17032
|
+
if (!specSubsystem) return false;
|
|
17033
|
+
return specSubsystem === scopeSubsystem || specSubsystem.startsWith(`${scopeSubsystem}::`);
|
|
17034
|
+
};
|
|
17035
|
+
const out = /* @__PURE__ */ new Set();
|
|
17036
|
+
const add = (p) => {
|
|
17037
|
+
if (p) out.add(path19.resolve(p));
|
|
17038
|
+
};
|
|
17039
|
+
for (const s of this.loadSubsystemSpecs()) {
|
|
17040
|
+
if (!scopeSubsystem || s.id === scopeSubsystem || s.id.startsWith(`${scopeSubsystem}::`)) {
|
|
17041
|
+
add(index.paths.subsystem[s.id]);
|
|
17042
|
+
}
|
|
17043
|
+
}
|
|
17044
|
+
for (const c of components) {
|
|
17045
|
+
if (inScope(c.subsystem)) add(index.paths.component[c.id]);
|
|
17046
|
+
}
|
|
17047
|
+
for (const i of interfaces) {
|
|
17048
|
+
const comp = components.find((c) => c.id === i.component);
|
|
17049
|
+
if (comp && inScope(comp.subsystem)) add(index.paths.interface[i.id]);
|
|
17050
|
+
}
|
|
17051
|
+
for (const m of implementations) {
|
|
17052
|
+
const intf = interfaces.find((i) => i.id === m.contract);
|
|
17053
|
+
const comp = intf ? components.find((c) => c.id === intf.component) : null;
|
|
17054
|
+
if (comp && inScope(comp.subsystem)) add(index.paths.implementation[m.id]);
|
|
17055
|
+
}
|
|
17056
|
+
if (!scopeSubsystem) {
|
|
17057
|
+
add(this.paths.specsSystem());
|
|
17058
|
+
for (const p of Object.values(index.paths.type)) add(p);
|
|
17059
|
+
for (const p of Object.values(index.paths.group)) add(p);
|
|
17060
|
+
}
|
|
17061
|
+
return [...out];
|
|
17062
|
+
}
|
|
16809
17063
|
/** Set a single spec's status (bumps updatedAt). Caller invalidates the cache. */
|
|
16810
17064
|
applySpecStatus(kind, id, status2) {
|
|
16811
17065
|
switch (kind) {
|
|
@@ -16843,16 +17097,16 @@ var init_specs2 = __esm({
|
|
|
16843
17097
|
const files = /* @__PURE__ */ new Set();
|
|
16844
17098
|
const sysPath = this.paths.specsSystem();
|
|
16845
17099
|
if (pathExists(sysPath)) {
|
|
16846
|
-
files.add(
|
|
17100
|
+
files.add(path19.resolve(sysPath));
|
|
16847
17101
|
}
|
|
16848
17102
|
for (const group of Object.values(index.paths)) {
|
|
16849
17103
|
for (const file of Object.values(group)) {
|
|
16850
|
-
files.add(
|
|
17104
|
+
files.add(path19.resolve(file));
|
|
16851
17105
|
}
|
|
16852
17106
|
}
|
|
16853
17107
|
for (const file of files) {
|
|
16854
|
-
if (
|
|
16855
|
-
snapshot.set(file,
|
|
17108
|
+
if (fs14.existsSync(file)) {
|
|
17109
|
+
snapshot.set(file, fs14.readFileSync(file, "utf8"));
|
|
16856
17110
|
}
|
|
16857
17111
|
}
|
|
16858
17112
|
return snapshot;
|
|
@@ -16866,20 +17120,20 @@ var init_specs2 = __esm({
|
|
|
16866
17120
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
16867
17121
|
const legacy = [];
|
|
16868
17122
|
for (const f of files) {
|
|
16869
|
-
const base =
|
|
16870
|
-
const dir =
|
|
17123
|
+
const base = path19.basename(f);
|
|
17124
|
+
const dir = path19.dirname(f);
|
|
16871
17125
|
if (base === "system.yaml") {
|
|
16872
|
-
legacy.push({ path: f, expected:
|
|
17126
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16873
17127
|
} else if (base === "subsystem.yaml") {
|
|
16874
|
-
legacy.push({ path: f, expected:
|
|
17128
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16875
17129
|
} else if (base === "component.yaml") {
|
|
16876
|
-
legacy.push({ path: f, expected:
|
|
17130
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16877
17131
|
} else if (base === "group.yaml") {
|
|
16878
|
-
legacy.push({ path: f, expected:
|
|
17132
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16879
17133
|
} else if (base === "interface.yaml") {
|
|
16880
|
-
legacy.push({ path: f, expected:
|
|
17134
|
+
legacy.push({ path: f, expected: path19.join(dir, ".interface.yaml") });
|
|
16881
17135
|
} else if (base === "implementation.yaml") {
|
|
16882
|
-
legacy.push({ path: f, expected:
|
|
17136
|
+
legacy.push({ path: f, expected: path19.join(dir, ".implementation.yaml") });
|
|
16883
17137
|
}
|
|
16884
17138
|
}
|
|
16885
17139
|
return legacy;
|
|
@@ -16946,6 +17200,35 @@ var init_specs2 = __esm({
|
|
|
16946
17200
|
}
|
|
16947
17201
|
return refs;
|
|
16948
17202
|
};
|
|
17203
|
+
const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
|
|
17204
|
+
const assertDeltaMarkers = (item, label) => {
|
|
17205
|
+
if (!item || typeof item !== "object") return;
|
|
17206
|
+
if ("remove" in item && typeof item.remove !== "boolean") {
|
|
17207
|
+
throw new Error(
|
|
17208
|
+
`Refusing to update ${label}: "remove" must be the boolean true, got ${JSON.stringify(item.remove)}. A non-boolean is ignored, which would leave the element in place while reporting success.`
|
|
17209
|
+
);
|
|
17210
|
+
}
|
|
17211
|
+
if ("action" in item && item.action !== "add" && item.action !== "delete") {
|
|
17212
|
+
throw new Error(
|
|
17213
|
+
`Refusing to update ${label}: unknown action ${JSON.stringify(item.action)} \u2014 expected "add" or "delete". An unknown verb is ignored, which would leave the element in place while reporting success.`
|
|
17214
|
+
);
|
|
17215
|
+
}
|
|
17216
|
+
};
|
|
17217
|
+
const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label) => {
|
|
17218
|
+
if (deltaItem?.remove === true || deltaItem?.action === "delete") {
|
|
17219
|
+
throw new Error(
|
|
17220
|
+
`Refusing to delete ${label} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
|
|
17221
|
+
);
|
|
17222
|
+
}
|
|
17223
|
+
const near = existingKeys.find(
|
|
17224
|
+
(k) => String(k) !== String(key) && normalizeIdentity(k) === normalizeIdentity(key)
|
|
17225
|
+
);
|
|
17226
|
+
if (near !== void 0) {
|
|
17227
|
+
throw new Error(
|
|
17228
|
+
`Refusing to add ${label} "${String(key)}": "${String(near)}" already exists and differs only in case or separators. Use the existing identity to edit it, or pick a name that is not a near-duplicate.`
|
|
17229
|
+
);
|
|
17230
|
+
}
|
|
17231
|
+
};
|
|
16949
17232
|
const mergeNarrative = (existingSteps, deltaSteps) => {
|
|
16950
17233
|
let steps = [...existingSteps];
|
|
16951
17234
|
const sortedDeltas = [...deltaSteps].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
@@ -17014,6 +17297,7 @@ var init_specs2 = __esm({
|
|
|
17014
17297
|
const mergeMethods = (existingMethods, deltaMethods) => {
|
|
17015
17298
|
const merged = [...existingMethods];
|
|
17016
17299
|
for (const deltaMethod of deltaMethods) {
|
|
17300
|
+
assertDeltaMarkers(deltaMethod, `method "${deltaMethod?.name}"`);
|
|
17017
17301
|
const idx = merged.findIndex((m) => m.name === deltaMethod.name);
|
|
17018
17302
|
if (idx !== -1) {
|
|
17019
17303
|
if (deltaMethod.remove === true || deltaMethod.action === "delete") {
|
|
@@ -17033,9 +17317,8 @@ var init_specs2 = __esm({
|
|
|
17033
17317
|
};
|
|
17034
17318
|
}
|
|
17035
17319
|
} else {
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
}
|
|
17320
|
+
assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
|
|
17321
|
+
merged.push(deltaMethod);
|
|
17039
17322
|
}
|
|
17040
17323
|
}
|
|
17041
17324
|
return merged;
|
|
@@ -17043,6 +17326,7 @@ var init_specs2 = __esm({
|
|
|
17043
17326
|
const mergeNamedArray = (existing, delta2) => {
|
|
17044
17327
|
const merged = [...existing];
|
|
17045
17328
|
for (const deltaItem of delta2) {
|
|
17329
|
+
assertDeltaMarkers(deltaItem, `entry "${deltaItem?.name}"`);
|
|
17046
17330
|
const idx = merged.findIndex((item) => item.name === deltaItem.name);
|
|
17047
17331
|
if (idx !== -1) {
|
|
17048
17332
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17055,9 +17339,8 @@ var init_specs2 = __esm({
|
|
|
17055
17339
|
};
|
|
17056
17340
|
}
|
|
17057
17341
|
} else {
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
}
|
|
17342
|
+
assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
|
|
17343
|
+
merged.push(deltaItem);
|
|
17061
17344
|
}
|
|
17062
17345
|
}
|
|
17063
17346
|
return merged;
|
|
@@ -17065,6 +17348,7 @@ var init_specs2 = __esm({
|
|
|
17065
17348
|
const mergeKeyedArray = (existing, delta2, keyOf) => {
|
|
17066
17349
|
const merged = [...existing];
|
|
17067
17350
|
for (const deltaItem of delta2) {
|
|
17351
|
+
assertDeltaMarkers(deltaItem, `entry "${keyOf(deltaItem)}"`);
|
|
17068
17352
|
const idx = merged.findIndex((item) => keyOf(item) === keyOf(deltaItem));
|
|
17069
17353
|
if (idx !== -1) {
|
|
17070
17354
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17072,7 +17356,8 @@ var init_specs2 = __esm({
|
|
|
17072
17356
|
} else {
|
|
17073
17357
|
merged[idx] = { ...merged[idx], ...deltaItem };
|
|
17074
17358
|
}
|
|
17075
|
-
} else
|
|
17359
|
+
} else {
|
|
17360
|
+
assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
|
|
17076
17361
|
merged.push(deltaItem);
|
|
17077
17362
|
}
|
|
17078
17363
|
}
|
|
@@ -17081,6 +17366,8 @@ var init_specs2 = __esm({
|
|
|
17081
17366
|
const mergePublicInterfaces = (existing, delta2) => {
|
|
17082
17367
|
const merged = [...existing];
|
|
17083
17368
|
for (const deltaItem of delta2) {
|
|
17369
|
+
const piKey = (i) => `${i?.component}.${i?.interface}`;
|
|
17370
|
+
assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
|
|
17084
17371
|
const idx = merged.findIndex((item) => item.component === deltaItem.component && item.interface === deltaItem.interface);
|
|
17085
17372
|
if (idx !== -1) {
|
|
17086
17373
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17092,9 +17379,8 @@ var init_specs2 = __esm({
|
|
|
17092
17379
|
};
|
|
17093
17380
|
}
|
|
17094
17381
|
} else {
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
}
|
|
17382
|
+
assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
|
|
17383
|
+
merged.push(deltaItem);
|
|
17098
17384
|
}
|
|
17099
17385
|
}
|
|
17100
17386
|
return merged;
|
|
@@ -17135,11 +17421,22 @@ var init_specs2 = __esm({
|
|
|
17135
17421
|
for (const deltaItem of delta2) {
|
|
17136
17422
|
const key = identityKeyOf(field, deltaItem);
|
|
17137
17423
|
const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf(field, item) === key);
|
|
17424
|
+
assertDeltaMarkers(deltaItem, `${field} entry "${String(key)}"`);
|
|
17138
17425
|
const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
|
|
17139
17426
|
if (idx !== -1) {
|
|
17140
17427
|
if (isDelete) merged.splice(idx, 1);
|
|
17141
17428
|
else merged[idx] = { ...merged[idx], ...deltaItem };
|
|
17142
|
-
} else
|
|
17429
|
+
} else {
|
|
17430
|
+
if (key !== null) {
|
|
17431
|
+
assertUnmatchedIsAnAddition(
|
|
17432
|
+
deltaItem,
|
|
17433
|
+
key,
|
|
17434
|
+
merged.map((i) => identityKeyOf(field, i)).filter((k) => k !== null),
|
|
17435
|
+
field
|
|
17436
|
+
);
|
|
17437
|
+
} else if (isDelete) {
|
|
17438
|
+
throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
|
|
17439
|
+
}
|
|
17143
17440
|
merged.push(deltaItem);
|
|
17144
17441
|
}
|
|
17145
17442
|
}
|
|
@@ -17331,7 +17628,7 @@ function provisionProject(name) {
|
|
|
17331
17628
|
function ensureProjectInitialized(fallbackName) {
|
|
17332
17629
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17333
17630
|
const paths = aiPathsAt(getProjectRoot());
|
|
17334
|
-
const hasSystem =
|
|
17631
|
+
const hasSystem = fs15.existsSync(paths.specsSystem());
|
|
17335
17632
|
let name = fallbackName;
|
|
17336
17633
|
if (hasSystem) {
|
|
17337
17634
|
const existing = loadSystemSpec();
|
|
@@ -17339,7 +17636,7 @@ function ensureProjectInitialized(fallbackName) {
|
|
|
17339
17636
|
}
|
|
17340
17637
|
let wroteConfig = false;
|
|
17341
17638
|
let wroteSystem = false;
|
|
17342
|
-
if (!
|
|
17639
|
+
if (!fs15.existsSync(paths.projectConfig())) {
|
|
17343
17640
|
saveProjectConfig(defaultProjectConfig(name, now));
|
|
17344
17641
|
wroteConfig = true;
|
|
17345
17642
|
}
|
|
@@ -17368,11 +17665,11 @@ function promoteAllComplete() {
|
|
|
17368
17665
|
function walkChainedSubprojects(projectRoot2, onChild) {
|
|
17369
17666
|
const visited = /* @__PURE__ */ new Set();
|
|
17370
17667
|
const walk = (dir) => {
|
|
17371
|
-
const resolved =
|
|
17668
|
+
const resolved = path20.resolve(dir);
|
|
17372
17669
|
if (visited.has(resolved)) return;
|
|
17373
17670
|
visited.add(resolved);
|
|
17374
17671
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
17375
|
-
if (!
|
|
17672
|
+
if (!fs15.existsSync(specsDir)) return;
|
|
17376
17673
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17377
17674
|
let raw;
|
|
17378
17675
|
try {
|
|
@@ -17390,7 +17687,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
17390
17687
|
continue;
|
|
17391
17688
|
}
|
|
17392
17689
|
const id = raw.id;
|
|
17393
|
-
onChild(childDir, typeof id === "string" ? id :
|
|
17690
|
+
onChild(childDir, typeof id === "string" ? id : path20.basename(childDir));
|
|
17394
17691
|
walk(childDir);
|
|
17395
17692
|
}
|
|
17396
17693
|
};
|
|
@@ -17399,7 +17696,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
17399
17696
|
function listDirectChainedSubprojects(projectRoot2) {
|
|
17400
17697
|
const out = [];
|
|
17401
17698
|
const specsDir = aiPathsAt(projectRoot2).specsDir();
|
|
17402
|
-
if (!
|
|
17699
|
+
if (!fs15.existsSync(specsDir)) return out;
|
|
17403
17700
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17404
17701
|
let raw;
|
|
17405
17702
|
try {
|
|
@@ -17417,12 +17714,12 @@ function listDirectChainedSubprojects(projectRoot2) {
|
|
|
17417
17714
|
continue;
|
|
17418
17715
|
}
|
|
17419
17716
|
const id = raw.id;
|
|
17420
|
-
out.push({ dir, subsystemId: typeof id === "string" ? id :
|
|
17717
|
+
out.push({ dir, subsystemId: typeof id === "string" ? id : path20.basename(dir) });
|
|
17421
17718
|
}
|
|
17422
17719
|
return out;
|
|
17423
17720
|
}
|
|
17424
17721
|
function childHasSpecsButNoConfig(childDir) {
|
|
17425
|
-
return
|
|
17722
|
+
return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !fs15.existsSync(aiPathsAt(childDir).projectConfig());
|
|
17426
17723
|
}
|
|
17427
17724
|
function findChainingSubprojectsMissingConfig(projectRoot2) {
|
|
17428
17725
|
const missing = [];
|
|
@@ -17471,14 +17768,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
|
|
|
17471
17768
|
const oldDir = assertContainedProjectPath(root, sub.projectPath);
|
|
17472
17769
|
const newDir = assertContainedProjectPath(root, nextPath);
|
|
17473
17770
|
if (oldDir !== newDir) {
|
|
17474
|
-
if (!
|
|
17771
|
+
if (!fs15.existsSync(oldDir)) {
|
|
17475
17772
|
throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
|
|
17476
17773
|
}
|
|
17477
|
-
if (
|
|
17774
|
+
if (fs15.existsSync(newDir)) {
|
|
17478
17775
|
throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
17479
17776
|
}
|
|
17480
|
-
ensureDir(
|
|
17481
|
-
|
|
17777
|
+
ensureDir(path20.dirname(newDir));
|
|
17778
|
+
fs15.renameSync(oldDir, newDir);
|
|
17482
17779
|
}
|
|
17483
17780
|
saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17484
17781
|
invalidateSpecCache();
|
|
@@ -17487,9 +17784,9 @@ function toPosixPath(p) {
|
|
|
17487
17784
|
return p.replace(/\\/g, "/");
|
|
17488
17785
|
}
|
|
17489
17786
|
function isWithinDir(dir, file) {
|
|
17490
|
-
const d =
|
|
17491
|
-
const f =
|
|
17492
|
-
return f === d || f.startsWith(d +
|
|
17787
|
+
const d = path20.resolve(dir);
|
|
17788
|
+
const f = path20.resolve(file);
|
|
17789
|
+
return f === d || f.startsWith(d + path20.sep);
|
|
17493
17790
|
}
|
|
17494
17791
|
function externalizeSubsystem(subsystemId, projectPath) {
|
|
17495
17792
|
if (subsystemId.includes("::")) {
|
|
@@ -17501,14 +17798,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
17501
17798
|
}
|
|
17502
17799
|
const parentRoot = getProjectRoot();
|
|
17503
17800
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
17504
|
-
const fooDir =
|
|
17505
|
-
if (!
|
|
17801
|
+
const fooDir = path20.join(parentSpecsDir, subsystemId);
|
|
17802
|
+
if (!fs15.existsSync(fooDir)) {
|
|
17506
17803
|
throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
|
|
17507
17804
|
}
|
|
17508
17805
|
const relPath = toPosixPath(projectPath);
|
|
17509
17806
|
const childDir = assertContainedProjectPath(parentRoot, relPath);
|
|
17510
|
-
const childFooDir =
|
|
17511
|
-
if (
|
|
17807
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
17808
|
+
if (fs15.existsSync(childFooDir)) {
|
|
17512
17809
|
throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
|
|
17513
17810
|
}
|
|
17514
17811
|
const renameMap = buildRenameMap(
|
|
@@ -17518,17 +17815,17 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
17518
17815
|
);
|
|
17519
17816
|
const childSystemName = foo.name || subsystemId;
|
|
17520
17817
|
runWithProjectRoot(childDir, () => {
|
|
17521
|
-
ensureDir(
|
|
17818
|
+
ensureDir(path20.join(childDir, ".wai", "specs"));
|
|
17522
17819
|
provisionProject(childSystemName);
|
|
17523
17820
|
});
|
|
17524
|
-
ensureDir(
|
|
17525
|
-
|
|
17526
|
-
patchSubsystemIndex(
|
|
17821
|
+
ensureDir(path20.dirname(childFooDir));
|
|
17822
|
+
fs15.renameSync(fooDir, childFooDir);
|
|
17823
|
+
patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
|
|
17527
17824
|
s.parentSystem = childSystemName;
|
|
17528
17825
|
delete s.projectPath;
|
|
17529
17826
|
});
|
|
17530
17827
|
ensureDir(fooDir);
|
|
17531
|
-
writeYamlFile(
|
|
17828
|
+
writeYamlFile(path20.join(fooDir, ".index.yaml"), {
|
|
17532
17829
|
id: subsystemId,
|
|
17533
17830
|
name: foo.name,
|
|
17534
17831
|
description: foo.description,
|
|
@@ -17554,9 +17851,9 @@ function internalizeSubsystem(subsystemId) {
|
|
|
17554
17851
|
const parentRoot = getProjectRoot();
|
|
17555
17852
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
17556
17853
|
const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
|
|
17557
|
-
const childWai =
|
|
17558
|
-
const childFooDir =
|
|
17559
|
-
if (!
|
|
17854
|
+
const childWai = path20.join(childDir, ".wai");
|
|
17855
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
17856
|
+
if (!fs15.existsSync(childFooDir)) {
|
|
17560
17857
|
throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
|
|
17561
17858
|
}
|
|
17562
17859
|
const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
|
|
@@ -17569,15 +17866,15 @@ function internalizeSubsystem(subsystemId) {
|
|
|
17569
17866
|
false
|
|
17570
17867
|
);
|
|
17571
17868
|
const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
|
|
17572
|
-
const fooDir =
|
|
17573
|
-
|
|
17574
|
-
ensureDir(
|
|
17575
|
-
|
|
17576
|
-
patchSubsystemIndex(
|
|
17869
|
+
const fooDir = path20.join(parentSpecsDir, subsystemId);
|
|
17870
|
+
fs15.rmSync(fooDir, { recursive: true, force: true });
|
|
17871
|
+
ensureDir(path20.dirname(fooDir));
|
|
17872
|
+
fs15.renameSync(childFooDir, fooDir);
|
|
17873
|
+
patchSubsystemIndex(path20.join(fooDir, ".index.yaml"), (s) => {
|
|
17577
17874
|
s.parentSystem = parentSystemName;
|
|
17578
17875
|
delete s.projectPath;
|
|
17579
17876
|
});
|
|
17580
|
-
|
|
17877
|
+
fs15.rmSync(childWai, { recursive: true, force: true });
|
|
17581
17878
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
17582
17879
|
invalidateSpecCache();
|
|
17583
17880
|
}
|
|
@@ -17674,18 +17971,18 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
17674
17971
|
}
|
|
17675
17972
|
}
|
|
17676
17973
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
17677
|
-
if (!
|
|
17974
|
+
if (!fs15.existsSync(indexPath)) return;
|
|
17678
17975
|
const raw = readYamlFile(indexPath);
|
|
17679
17976
|
mutate(raw);
|
|
17680
17977
|
raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17681
17978
|
writeYamlFile(indexPath, raw);
|
|
17682
17979
|
}
|
|
17683
|
-
var
|
|
17980
|
+
var fs15, path20;
|
|
17684
17981
|
var init_provision = __esm({
|
|
17685
17982
|
"src/core/provision.ts"() {
|
|
17686
17983
|
"use strict";
|
|
17687
|
-
|
|
17688
|
-
|
|
17984
|
+
fs15 = __toESM(require("fs"));
|
|
17985
|
+
path20 = __toESM(require("path"));
|
|
17689
17986
|
init_specs2();
|
|
17690
17987
|
init_loader();
|
|
17691
17988
|
init_fs();
|
|
@@ -17726,18 +18023,18 @@ __export(ai_guide_exports, {
|
|
|
17726
18023
|
writeRootGuideDelegator: () => writeRootGuideDelegator
|
|
17727
18024
|
});
|
|
17728
18025
|
function globalGuideFilePath(targetType) {
|
|
17729
|
-
if (targetType === "claude") return
|
|
17730
|
-
if (targetType === "gemini") return
|
|
18026
|
+
if (targetType === "claude") return path21.join(process.env["CLAUDE_CONFIG_DIR"] || path21.join(os6.homedir(), ".claude"), "CLAUDE.md");
|
|
18027
|
+
if (targetType === "gemini") return path21.join(process.env["GEMINI_CONFIG_DIR"] || path21.join(os6.homedir(), ".gemini"), "GEMINI.md");
|
|
17731
18028
|
return null;
|
|
17732
18029
|
}
|
|
17733
18030
|
function localGuideFilePath(projectRoot2, targetType) {
|
|
17734
|
-
if (targetType === "claude") return
|
|
17735
|
-
if (targetType === "gemini" || targetType === "agy") return
|
|
18031
|
+
if (targetType === "claude") return path21.join(projectRoot2, ".claude", "CLAUDE.md");
|
|
18032
|
+
if (targetType === "gemini" || targetType === "agy") return path21.join(projectRoot2, ".gemini", "GEMINI.md");
|
|
17736
18033
|
return null;
|
|
17737
18034
|
}
|
|
17738
18035
|
function hasWaironGuide(filePath) {
|
|
17739
|
-
if (!
|
|
17740
|
-
return
|
|
18036
|
+
if (!fs16.existsSync(filePath)) return false;
|
|
18037
|
+
return fs16.readFileSync(filePath, "utf-8").includes(GUIDE_MARKER_START);
|
|
17741
18038
|
}
|
|
17742
18039
|
function injectGuide(filePath, scope) {
|
|
17743
18040
|
const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
|
|
@@ -17748,11 +18045,11 @@ ${versionStamp()}
|
|
|
17748
18045
|
${body}
|
|
17749
18046
|
${GUIDE_MARKER_END}
|
|
17750
18047
|
`;
|
|
17751
|
-
const existing =
|
|
18048
|
+
const existing = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
|
|
17752
18049
|
const stripped = stripGuideSection(existing);
|
|
17753
18050
|
const newContent = stripped.trimEnd() + section;
|
|
17754
|
-
|
|
17755
|
-
|
|
18051
|
+
fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
18052
|
+
fs16.writeFileSync(filePath, newContent, "utf-8");
|
|
17756
18053
|
}
|
|
17757
18054
|
function stripGuideSection(content) {
|
|
17758
18055
|
const start = content.indexOf(GUIDE_MARKER_START);
|
|
@@ -17762,7 +18059,7 @@ function stripGuideSection(content) {
|
|
|
17762
18059
|
}
|
|
17763
18060
|
function writeRootGuideDelegator(projectRoot2, targetType) {
|
|
17764
18061
|
if (targetType === "claude") {
|
|
17765
|
-
const filePath =
|
|
18062
|
+
const filePath = path21.join(projectRoot2, "CLAUDE.md");
|
|
17766
18063
|
const content = `@.claude/CLAUDE.md
|
|
17767
18064
|
|
|
17768
18065
|
# Wairon SDD Project
|
|
@@ -17775,42 +18072,42 @@ To design or modify the system, invoke the **\`sdd-architect\`** skill
|
|
|
17775
18072
|
(in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
|
|
17776
18073
|
the \`wairon\` CLI is the human developer's tool, not yours.
|
|
17777
18074
|
`;
|
|
17778
|
-
|
|
18075
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17779
18076
|
} else if (targetType === "gemini" || targetType === "agy") {
|
|
17780
|
-
const filePath =
|
|
18077
|
+
const filePath = path21.join(projectRoot2, "GEMINI.md");
|
|
17781
18078
|
const content = `# Wairon SDD Project
|
|
17782
18079
|
${GUIDE_MARKER_START}
|
|
17783
18080
|
${versionStamp()}
|
|
17784
18081
|
${LOCAL_GUIDE_BODY}
|
|
17785
18082
|
${GUIDE_MARKER_END}
|
|
17786
18083
|
`;
|
|
17787
|
-
|
|
18084
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17788
18085
|
} else if (targetType === "cursor") {
|
|
17789
|
-
const filePath =
|
|
18086
|
+
const filePath = path21.join(projectRoot2, ".cursorrules");
|
|
17790
18087
|
const content = `# Wairon SDD Project
|
|
17791
18088
|
|
|
17792
18089
|
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
17793
18090
|
|
|
17794
18091
|
Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
17795
18092
|
`;
|
|
17796
|
-
|
|
18093
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17797
18094
|
} else if (targetType === "copilot") {
|
|
17798
|
-
const filePath =
|
|
17799
|
-
|
|
18095
|
+
const filePath = path21.join(projectRoot2, ".github", "copilot-instructions.md");
|
|
18096
|
+
fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
17800
18097
|
const content = `# Wairon SDD Project
|
|
17801
18098
|
|
|
17802
18099
|
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
17803
18100
|
|
|
17804
18101
|
Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
|
|
17805
18102
|
`;
|
|
17806
|
-
|
|
18103
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17807
18104
|
} else if (targetType === "codex") {
|
|
17808
|
-
const filePath =
|
|
18105
|
+
const filePath = path21.join(projectRoot2, ".codexrules");
|
|
17809
18106
|
const content = `# Wairon SDD Project
|
|
17810
18107
|
|
|
17811
18108
|
Refer to [.codex/agents/](.codex/agents/) for full instructions.
|
|
17812
18109
|
`;
|
|
17813
|
-
|
|
18110
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17814
18111
|
}
|
|
17815
18112
|
}
|
|
17816
18113
|
function reinjectLocalGuides(projectRoot2, targetTypes) {
|
|
@@ -17826,13 +18123,13 @@ function reinjectLocalGuides(projectRoot2, targetTypes) {
|
|
|
17826
18123
|
}
|
|
17827
18124
|
return written;
|
|
17828
18125
|
}
|
|
17829
|
-
var
|
|
18126
|
+
var fs16, os6, path21, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
|
|
17830
18127
|
var init_ai_guide = __esm({
|
|
17831
18128
|
"src/utils/ai-guide.ts"() {
|
|
17832
18129
|
"use strict";
|
|
17833
|
-
|
|
17834
|
-
|
|
17835
|
-
|
|
18130
|
+
fs16 = __toESM(require("fs"));
|
|
18131
|
+
os6 = __toESM(require("os"));
|
|
18132
|
+
path21 = __toESM(require("path"));
|
|
17836
18133
|
init_stamp();
|
|
17837
18134
|
GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
|
|
17838
18135
|
GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
|
|
@@ -17894,7 +18191,7 @@ __export(domains_exports, {
|
|
|
17894
18191
|
resolveDomains: () => resolveDomains
|
|
17895
18192
|
});
|
|
17896
18193
|
function rel(p) {
|
|
17897
|
-
return
|
|
18194
|
+
return path26.relative(process.cwd(), p).replace(/\\/g, "/");
|
|
17898
18195
|
}
|
|
17899
18196
|
function deriveSubsystemDomains() {
|
|
17900
18197
|
const subsystems = loadSubsystemSpecs();
|
|
@@ -17944,11 +18241,11 @@ function removeFreeStandingDomain(id) {
|
|
|
17944
18241
|
config.domains.splice(idx, 1);
|
|
17945
18242
|
saveTopologyConfig(config);
|
|
17946
18243
|
}
|
|
17947
|
-
var
|
|
18244
|
+
var path26;
|
|
17948
18245
|
var init_domains = __esm({
|
|
17949
18246
|
"src/core/domains.ts"() {
|
|
17950
18247
|
"use strict";
|
|
17951
|
-
|
|
18248
|
+
path26 = __toESM(require("path"));
|
|
17952
18249
|
init_loader();
|
|
17953
18250
|
init_specs2();
|
|
17954
18251
|
init_errors();
|
|
@@ -18104,16 +18401,16 @@ function packNamespace(pack) {
|
|
|
18104
18401
|
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
18105
18402
|
}
|
|
18106
18403
|
function extensionsFor(builtin, packSkills) {
|
|
18107
|
-
return packSkills.filter((s) => s.extends === builtin &&
|
|
18404
|
+
return packSkills.filter((s) => s.extends === builtin && fs18.existsSync(s.sourcePath));
|
|
18108
18405
|
}
|
|
18109
18406
|
function composeBuiltinSkill(name, packSkills) {
|
|
18110
18407
|
const srcPath = skillTemplatePath(name);
|
|
18111
|
-
const base =
|
|
18408
|
+
const base = fs18.existsSync(srcPath) ? fs18.readFileSync(srcPath, "utf-8") : "";
|
|
18112
18409
|
const sections = extensionsFor(name, packSkills);
|
|
18113
18410
|
if (sections.length === 0) return base;
|
|
18114
18411
|
const parts = [base.trimEnd()];
|
|
18115
18412
|
for (const section of sections) {
|
|
18116
|
-
const body =
|
|
18413
|
+
const body = fs18.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
18117
18414
|
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
18118
18415
|
}
|
|
18119
18416
|
return `${parts.join("\n\n")}
|
|
@@ -18129,16 +18426,16 @@ function readFrontmatter(raw, fallbackName) {
|
|
|
18129
18426
|
return { name: field("name") || fallbackName, description: field("description") };
|
|
18130
18427
|
}
|
|
18131
18428
|
function builtinSkillsDir() {
|
|
18132
|
-
return
|
|
18429
|
+
return path27.resolve(__dirname, "..", "templates", "skills");
|
|
18133
18430
|
}
|
|
18134
18431
|
function skillTemplatePath(name) {
|
|
18135
|
-
return
|
|
18432
|
+
return path27.join(builtinSkillsDir(), `${name}.md`);
|
|
18136
18433
|
}
|
|
18137
18434
|
function skillDestPath(type, destDir, name) {
|
|
18138
18435
|
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
18139
|
-
return
|
|
18436
|
+
return path27.join(destDir, name, "SKILL.md");
|
|
18140
18437
|
}
|
|
18141
|
-
return
|
|
18438
|
+
return path27.join(destDir, `${name}.md`);
|
|
18142
18439
|
}
|
|
18143
18440
|
function skillsDirForTarget(type) {
|
|
18144
18441
|
switch (type) {
|
|
@@ -18173,22 +18470,22 @@ function exportSddSkills(targetTypes) {
|
|
|
18173
18470
|
ensureDir(destDir);
|
|
18174
18471
|
destinations.push(destDir);
|
|
18175
18472
|
for (const name of SKILL_NAMES) {
|
|
18176
|
-
if (!
|
|
18473
|
+
if (!fs18.existsSync(skillTemplatePath(name))) continue;
|
|
18177
18474
|
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
18178
18475
|
const destPath = skillDestPath(type, destDir, name);
|
|
18179
|
-
ensureDir(
|
|
18180
|
-
|
|
18476
|
+
ensureDir(path27.dirname(destPath));
|
|
18477
|
+
fs18.writeFileSync(destPath, content, "utf-8");
|
|
18181
18478
|
fileCount++;
|
|
18182
18479
|
}
|
|
18183
18480
|
for (const skill of packSkills) {
|
|
18184
18481
|
if (skill.extends !== void 0) continue;
|
|
18185
18482
|
if (!skill.targets.includes(type)) continue;
|
|
18186
|
-
if (!
|
|
18483
|
+
if (!fs18.existsSync(skill.sourcePath)) continue;
|
|
18187
18484
|
const id = packSkillId(skill);
|
|
18188
|
-
const content =
|
|
18485
|
+
const content = fs18.readFileSync(skill.sourcePath, "utf-8");
|
|
18189
18486
|
const destPath = skillDestPath(type, destDir, id);
|
|
18190
|
-
ensureDir(
|
|
18191
|
-
|
|
18487
|
+
ensureDir(path27.dirname(destPath));
|
|
18488
|
+
fs18.writeFileSync(destPath, content, "utf-8");
|
|
18192
18489
|
fileCount++;
|
|
18193
18490
|
}
|
|
18194
18491
|
}
|
|
@@ -18201,12 +18498,12 @@ function checkSkillFreshness(type) {
|
|
|
18201
18498
|
const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
|
|
18202
18499
|
for (const name of SKILL_NAMES) {
|
|
18203
18500
|
const destPath = skillDestPath(type, dir, name);
|
|
18204
|
-
if (!
|
|
18501
|
+
if (!fs18.existsSync(destPath)) {
|
|
18205
18502
|
result.missing.push(name);
|
|
18206
18503
|
continue;
|
|
18207
18504
|
}
|
|
18208
18505
|
const want = composeBuiltinSkill(name, packSkills);
|
|
18209
|
-
const have =
|
|
18506
|
+
const have = fs18.readFileSync(destPath, "utf-8");
|
|
18210
18507
|
if (have === want) result.ok.push(name);
|
|
18211
18508
|
else result.stale.push(name);
|
|
18212
18509
|
}
|
|
@@ -18218,7 +18515,7 @@ function activeTargetTypes() {
|
|
|
18218
18515
|
return config.targets.filter((t) => !("enabled" in t) || t.enabled).map((t) => typeof t === "string" ? t : t.type);
|
|
18219
18516
|
}
|
|
18220
18517
|
function readSkillFrontmatter(name) {
|
|
18221
|
-
return readFrontmatter(
|
|
18518
|
+
return readFrontmatter(fs18.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
18222
18519
|
}
|
|
18223
18520
|
function listSkillResources() {
|
|
18224
18521
|
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
@@ -18232,9 +18529,9 @@ function listSkillResources() {
|
|
|
18232
18529
|
defaultForHostedMcp: true
|
|
18233
18530
|
};
|
|
18234
18531
|
});
|
|
18235
|
-
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 &&
|
|
18532
|
+
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs18.existsSync(s.sourcePath)).map((skill) => {
|
|
18236
18533
|
const id = packSkillId(skill);
|
|
18237
|
-
const fm = readFrontmatter(
|
|
18534
|
+
const fm = readFrontmatter(fs18.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
18238
18535
|
return {
|
|
18239
18536
|
id,
|
|
18240
18537
|
name: fm.name,
|
|
@@ -18250,8 +18547,8 @@ function readSkillResource(resourceId) {
|
|
|
18250
18547
|
const packSkills = loadProjectExtensions2().skills;
|
|
18251
18548
|
if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
|
|
18252
18549
|
const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
|
|
18253
|
-
if (packSkill) return
|
|
18254
|
-
return
|
|
18550
|
+
if (packSkill) return fs18.readFileSync(packSkill.sourcePath, "utf-8");
|
|
18551
|
+
return fs18.readFileSync(skillTemplatePath(resourceId), "utf-8");
|
|
18255
18552
|
}
|
|
18256
18553
|
function listResources() {
|
|
18257
18554
|
return listSkillResources();
|
|
@@ -18264,12 +18561,12 @@ function readResource(resourceId) {
|
|
|
18264
18561
|
if (!known) throw new SkillResourceNotFoundError(resourceId);
|
|
18265
18562
|
return readSkillResource(resourceId);
|
|
18266
18563
|
}
|
|
18267
|
-
var
|
|
18564
|
+
var path27, fs18, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
|
|
18268
18565
|
var init_skills = __esm({
|
|
18269
18566
|
"src/core/skills.ts"() {
|
|
18270
18567
|
"use strict";
|
|
18271
|
-
|
|
18272
|
-
|
|
18568
|
+
path27 = __toESM(require("path"));
|
|
18569
|
+
fs18 = __toESM(require("fs"));
|
|
18273
18570
|
init_fs();
|
|
18274
18571
|
init_defaults();
|
|
18275
18572
|
init_extensions();
|
|
@@ -18496,23 +18793,34 @@ async function runStatus(options = {}) {
|
|
|
18496
18793
|
const lock = lockLine().trim();
|
|
18497
18794
|
if (lock) {
|
|
18498
18795
|
logger.blank();
|
|
18499
|
-
if (lock.includes("
|
|
18796
|
+
if (lock.includes("changed since approval")) logger.warn(lock);
|
|
18500
18797
|
else logger.info(lock);
|
|
18501
18798
|
}
|
|
18502
18799
|
logger.blank();
|
|
18503
18800
|
}
|
|
18504
18801
|
function lockLine() {
|
|
18505
18802
|
try {
|
|
18506
|
-
const
|
|
18507
|
-
if (
|
|
18508
|
-
|
|
18803
|
+
const baseline = readBaseline();
|
|
18804
|
+
if (!baseline) return "";
|
|
18805
|
+
const moved = movedChildren(loadSubsystemSpecs());
|
|
18806
|
+
const childNote = moved.length ? `
|
|
18807
|
+
${moved.length} chained child project(s) moved since approval: ${moved.map((m) => m.id).join(", ")}.` : "";
|
|
18808
|
+
const diff = diffAgainstBaseline();
|
|
18809
|
+
if (!diff || diffSize(diff) === 0) {
|
|
18509
18810
|
return `
|
|
18510
|
-
|
|
18811
|
+
Approved: ${baseline.approvedAt} by ${baseline.approvedBy} \u2014 no spec has changed since.${childNote}
|
|
18511
18812
|
`;
|
|
18512
18813
|
}
|
|
18814
|
+
const parts = [];
|
|
18815
|
+
if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
|
|
18816
|
+
if (diff.added.length) parts.push(`${diff.added.length} added`);
|
|
18817
|
+
if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
|
|
18818
|
+
const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
|
|
18819
|
+
const rest = diffSize(diff) - named.length;
|
|
18513
18820
|
return `
|
|
18514
|
-
|
|
18515
|
-
|
|
18821
|
+
${diffSize(diff)} spec(s) changed since approval (${parts.join(", ")}) \u2014 approved ${baseline.approvedAt} by ${baseline.approvedBy}:
|
|
18822
|
+
` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
|
|
18823
|
+
\u2026 and ${rest} more` : "") + childNote + "\n";
|
|
18516
18824
|
} catch {
|
|
18517
18825
|
return "";
|
|
18518
18826
|
}
|
|
@@ -18639,6 +18947,7 @@ var init_status = __esm({
|
|
|
18639
18947
|
init_loader();
|
|
18640
18948
|
init_fs();
|
|
18641
18949
|
init_specs2();
|
|
18950
|
+
init_baseline();
|
|
18642
18951
|
}
|
|
18643
18952
|
});
|
|
18644
18953
|
|
|
@@ -18687,7 +18996,7 @@ function errText(message) {
|
|
|
18687
18996
|
}
|
|
18688
18997
|
function captureBuildStamp(entryPath) {
|
|
18689
18998
|
try {
|
|
18690
|
-
const s =
|
|
18999
|
+
const s = fs19.statSync(entryPath);
|
|
18691
19000
|
return { path: entryPath, mtimeMs: s.mtimeMs, size: s.size };
|
|
18692
19001
|
} catch {
|
|
18693
19002
|
return null;
|
|
@@ -18696,7 +19005,7 @@ function captureBuildStamp(entryPath) {
|
|
|
18696
19005
|
function isBuildStale(stamp) {
|
|
18697
19006
|
if (!stamp) return false;
|
|
18698
19007
|
try {
|
|
18699
|
-
const s =
|
|
19008
|
+
const s = fs19.statSync(stamp.path);
|
|
18700
19009
|
return s.mtimeMs !== stamp.mtimeMs || s.size !== stamp.size;
|
|
18701
19010
|
} catch {
|
|
18702
19011
|
return false;
|
|
@@ -19914,10 +20223,6 @@ NOTICE:
|
|
|
19914
20223
|
description: "Hosted project lifecycle (execute-primary): LOCK the bound project (validate-as-complete gate + commit-scoped lock record). Executes directly when your resolved permission is yes and returns the completed outcome; when it is approval, a pending approval request is created instead (await it with sdd_host_await_approval).",
|
|
19915
20224
|
inputSchema: {}
|
|
19916
20225
|
}, hostedStub);
|
|
19917
|
-
reg(server, "sdd_host_promote_project", {
|
|
19918
|
-
description: "Hosted project lifecycle (execute-primary): PROMOTE the bound, locked project after a StateId re-check. Executes directly when your resolved permission is yes; when it is approval, a pending approval request is created instead.",
|
|
19919
|
-
inputSchema: {}
|
|
19920
|
-
}, hostedStub);
|
|
19921
20226
|
reg(server, "sdd_host_initialize_project", {
|
|
19922
20227
|
description: "Hosted project lifecycle (execute-primary): initialize a new hosted project into its REQUIRED owner organization unit (with an optional profile selection). Executes directly when your resolved permission is yes; when it is approval, a pending approval request is created instead.",
|
|
19923
20228
|
inputSchema: {
|
|
@@ -20003,7 +20308,7 @@ async function scopeToClientWorkspace(server) {
|
|
|
20003
20308
|
} catch {
|
|
20004
20309
|
dir = null;
|
|
20005
20310
|
}
|
|
20006
|
-
if (dir && (
|
|
20311
|
+
if (dir && (fs19.existsSync(path29.join(dir, ".wai")) || fs19.existsSync(path29.join(dir, ".wairon")))) {
|
|
20007
20312
|
setProjectRoot(dir);
|
|
20008
20313
|
process.stderr.write(`[wairon mcp] scoped to client workspace root: ${dir}
|
|
20009
20314
|
`);
|
|
@@ -20027,7 +20332,7 @@ async function startMcpServer() {
|
|
|
20027
20332
|
} catch {
|
|
20028
20333
|
}
|
|
20029
20334
|
}
|
|
20030
|
-
var import_mcp, import_stdio, import_zod9, import_types3,
|
|
20335
|
+
var import_mcp, import_stdio, import_zod9, import_types3, fs19, path29, import_url, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, STORE_MANAGED_FIELDS, ALWAYS_CARRIED_FIELDS, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
|
|
20031
20336
|
var init_server = __esm({
|
|
20032
20337
|
"src/mcp/server.ts"() {
|
|
20033
20338
|
"use strict";
|
|
@@ -20035,8 +20340,8 @@ var init_server = __esm({
|
|
|
20035
20340
|
import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
20036
20341
|
import_zod9 = require("zod");
|
|
20037
20342
|
import_types3 = require("@modelcontextprotocol/sdk/types.js");
|
|
20038
|
-
|
|
20039
|
-
|
|
20343
|
+
fs19 = __toESM(require("fs"));
|
|
20344
|
+
path29 = __toESM(require("path"));
|
|
20040
20345
|
import_url = require("url");
|
|
20041
20346
|
init_fs();
|
|
20042
20347
|
init_defaults();
|
|
@@ -20087,34 +20392,34 @@ __export(mcp_exports, {
|
|
|
20087
20392
|
validateConfigDir: () => validateConfigDir
|
|
20088
20393
|
});
|
|
20089
20394
|
function geminiGlobalDir(override) {
|
|
20090
|
-
return override || process.env["GEMINI_CONFIG_DIR"] ||
|
|
20395
|
+
return override || process.env["GEMINI_CONFIG_DIR"] || path30.join(os7.homedir(), ".gemini");
|
|
20091
20396
|
}
|
|
20092
20397
|
function claudeMcpConfigPath(useGlobal, override) {
|
|
20093
|
-
if (!useGlobal) return
|
|
20398
|
+
if (!useGlobal) return path30.join(process.cwd(), ".mcp.json");
|
|
20094
20399
|
const dir = override || process.env["CLAUDE_CONFIG_DIR"];
|
|
20095
|
-
return dir ?
|
|
20400
|
+
return dir ? path30.join(dir, ".claude.json") : path30.join(os7.homedir(), ".claude.json");
|
|
20096
20401
|
}
|
|
20097
20402
|
function validateConfigDir(dir, backend) {
|
|
20098
|
-
const resolved =
|
|
20403
|
+
const resolved = path30.resolve(dir);
|
|
20099
20404
|
const agent = backend === "claude" ? "Claude" : "Gemini/Antigravity";
|
|
20100
|
-
if (!
|
|
20101
|
-
const parent =
|
|
20102
|
-
if (!
|
|
20405
|
+
if (!fs20.existsSync(resolved)) {
|
|
20406
|
+
const parent = path30.dirname(resolved);
|
|
20407
|
+
if (!fs20.existsSync(parent)) {
|
|
20103
20408
|
throw new WaironError(`--config-dir "${dir}" does not exist and its parent is missing \u2014 check the path.`);
|
|
20104
20409
|
}
|
|
20105
20410
|
logger.warn(`Config dir "${resolved}" does not exist yet; it will be created.`);
|
|
20106
20411
|
return;
|
|
20107
20412
|
}
|
|
20108
|
-
if (!
|
|
20413
|
+
if (!fs20.statSync(resolved).isDirectory()) {
|
|
20109
20414
|
throw new WaironError(`--config-dir "${dir}" is not a directory.`);
|
|
20110
20415
|
}
|
|
20111
20416
|
const markers = backend === "claude" ? ["settings.json", "settings.local.json", ".credentials.json", "projects", "statsig", "todos", "shell-snapshots", "CLAUDE.md"] : ["settings.json", "GEMINI.md", "oauth_creds.json", "antigravity-cli", "tmp"];
|
|
20112
|
-
const entries =
|
|
20417
|
+
const entries = fs20.readdirSync(resolved);
|
|
20113
20418
|
if (entries.length === 0) {
|
|
20114
20419
|
logger.warn(`Config dir "${resolved}" is empty; proceeding (treating it as a fresh ${agent} config dir).`);
|
|
20115
20420
|
return;
|
|
20116
20421
|
}
|
|
20117
|
-
if (!markers.some((m) =>
|
|
20422
|
+
if (!markers.some((m) => fs20.existsSync(path30.join(resolved, m)))) {
|
|
20118
20423
|
throw new WaironError(
|
|
20119
20424
|
`"${dir}" does not look like a ${agent} config directory (none of ${markers.slice(0, 4).join(", ")} found). Point --config-dir at the agent's config directory.`
|
|
20120
20425
|
);
|
|
@@ -20122,12 +20427,12 @@ function validateConfigDir(dir, backend) {
|
|
|
20122
20427
|
}
|
|
20123
20428
|
async function runMcpServe() {
|
|
20124
20429
|
const { setProjectRoot: setProjectRoot2, getProjectRoot: getProjectRoot2, findProjectRoot: findProjectRoot2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
20125
|
-
const cwd =
|
|
20430
|
+
const cwd = path30.resolve(process.cwd());
|
|
20126
20431
|
const envDir = process.env["WAIRON_PROJECT_DIR"];
|
|
20127
20432
|
let resolved;
|
|
20128
20433
|
let how;
|
|
20129
|
-
if (envDir &&
|
|
20130
|
-
resolved =
|
|
20434
|
+
if (envDir && fs20.existsSync(path30.join(envDir, ".wai"))) {
|
|
20435
|
+
resolved = path30.resolve(envDir);
|
|
20131
20436
|
how = "WAIRON_PROJECT_DIR (pinned at install)";
|
|
20132
20437
|
} else {
|
|
20133
20438
|
const found = findProjectRoot2(cwd);
|
|
@@ -20190,20 +20495,20 @@ async function runMcpInstall(options = {}) {
|
|
|
20190
20495
|
let settingsPath;
|
|
20191
20496
|
if (backend === "gemini") {
|
|
20192
20497
|
if (useGlobal) {
|
|
20193
|
-
configBase =
|
|
20194
|
-
settingsPath =
|
|
20498
|
+
configBase = path30.join(geminiGlobalDir(options.configDir), "antigravity-cli");
|
|
20499
|
+
settingsPath = path30.join(configBase, "mcp_config.json");
|
|
20195
20500
|
} else {
|
|
20196
|
-
configBase =
|
|
20197
|
-
settingsPath =
|
|
20501
|
+
configBase = path30.join(process.cwd(), ".gemini");
|
|
20502
|
+
settingsPath = path30.join(configBase, "settings.json");
|
|
20198
20503
|
}
|
|
20199
20504
|
} else {
|
|
20200
20505
|
settingsPath = claudeMcpConfigPath(useGlobal, options.configDir);
|
|
20201
|
-
configBase =
|
|
20506
|
+
configBase = path30.dirname(settingsPath);
|
|
20202
20507
|
}
|
|
20203
20508
|
let settings = {};
|
|
20204
|
-
if (
|
|
20509
|
+
if (fs20.existsSync(settingsPath)) {
|
|
20205
20510
|
try {
|
|
20206
|
-
settings = JSON.parse(
|
|
20511
|
+
settings = JSON.parse(fs20.readFileSync(settingsPath, "utf8"));
|
|
20207
20512
|
} catch {
|
|
20208
20513
|
logger.warn(`Could not parse ${settingsPath} \u2014 starting fresh.`);
|
|
20209
20514
|
}
|
|
@@ -20211,7 +20516,7 @@ async function runMcpInstall(options = {}) {
|
|
|
20211
20516
|
const mcpServers = settings["mcpServers"] ?? {};
|
|
20212
20517
|
const agentLabel = backend === "gemini" ? "Antigravity" : "Claude";
|
|
20213
20518
|
const isPackaged = typeof process.pkg !== "undefined";
|
|
20214
|
-
const scriptPath = process.argv[1] ?
|
|
20519
|
+
const scriptPath = process.argv[1] ? path30.resolve(process.argv[1]).replace(/\\/g, "/") : null;
|
|
20215
20520
|
const useDirectNode = !isPackaged && scriptPath && (scriptPath.endsWith(".js") || scriptPath.endsWith(".ts"));
|
|
20216
20521
|
const env = useGlobal ? {} : { WAIRON_PROJECT_DIR: process.cwd().replace(/\\/g, "/") };
|
|
20217
20522
|
const desiredEntry = useDirectNode ? { command: "node", args: [scriptPath, "mcp", "serve"], env } : { command: "wairon", args: ["mcp", "serve"], env };
|
|
@@ -20223,8 +20528,8 @@ async function runMcpInstall(options = {}) {
|
|
|
20223
20528
|
const wasStale = !!existingEntry;
|
|
20224
20529
|
mcpServers["wairon"] = desiredEntry;
|
|
20225
20530
|
settings["mcpServers"] = mcpServers;
|
|
20226
|
-
if (!
|
|
20227
|
-
|
|
20531
|
+
if (!fs20.existsSync(configBase)) fs20.mkdirSync(configBase, { recursive: true });
|
|
20532
|
+
fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
20228
20533
|
logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk4.default.cyan(settingsPath)}.`);
|
|
20229
20534
|
logger.blank();
|
|
20230
20535
|
logger.info("AI tools using this config will have access to these wairon tools:");
|
|
@@ -20249,8 +20554,8 @@ async function runMcpStatus() {
|
|
|
20249
20554
|
const projectConfig = loadProjectConfig();
|
|
20250
20555
|
const claudeProject = claudeMcpConfigPath(false);
|
|
20251
20556
|
const claudeGlobal = claudeMcpConfigPath(true);
|
|
20252
|
-
const geminiProject =
|
|
20253
|
-
const geminiGlobal =
|
|
20557
|
+
const geminiProject = path30.join(process.cwd(), ".gemini", "settings.json");
|
|
20558
|
+
const geminiGlobal = path30.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
|
|
20254
20559
|
logger.blank();
|
|
20255
20560
|
logger.info(`${import_chalk4.default.bold("wairon MCP Server")}`);
|
|
20256
20561
|
logger.blank();
|
|
@@ -20261,12 +20566,12 @@ async function runMcpStatus() {
|
|
|
20261
20566
|
{ label: "Antigravity (global)", filePath: geminiGlobal, fallbackName: "mcp_config.json" }
|
|
20262
20567
|
];
|
|
20263
20568
|
for (const { label, filePath, fallbackName } of checks) {
|
|
20264
|
-
if (!
|
|
20569
|
+
if (!fs20.existsSync(filePath)) {
|
|
20265
20570
|
console.log(` ${label}: ${import_chalk4.default.gray(`${fallbackName} not found`)}`);
|
|
20266
20571
|
continue;
|
|
20267
20572
|
}
|
|
20268
20573
|
try {
|
|
20269
|
-
const s = JSON.parse(
|
|
20574
|
+
const s = JSON.parse(fs20.readFileSync(filePath, "utf8"));
|
|
20270
20575
|
const registered = !!s["mcpServers"]?.["wairon"];
|
|
20271
20576
|
const mark = registered ? import_chalk4.default.green("\u2713 registered") : import_chalk4.default.gray("not registered");
|
|
20272
20577
|
console.log(` ${label}: ${mark} ${import_chalk4.default.gray(filePath)}`);
|
|
@@ -20282,16 +20587,16 @@ async function runMcpStatus() {
|
|
|
20282
20587
|
logger.info(`To start manually: ${import_chalk4.default.bold("wairon mcp serve")}`);
|
|
20283
20588
|
logger.blank();
|
|
20284
20589
|
const mcpDir = aiDir("mcp");
|
|
20285
|
-
if (
|
|
20590
|
+
if (fs20.existsSync(mcpDir)) {
|
|
20286
20591
|
logger.info(`MCP state dir: ${import_chalk4.default.gray(mcpDir)}`);
|
|
20287
20592
|
}
|
|
20288
20593
|
}
|
|
20289
20594
|
function removeLegacyGlobalPlugin() {
|
|
20290
|
-
const home = process.env["USERPROFILE"] ?? process.env["HOME"] ??
|
|
20291
|
-
const pluginDir =
|
|
20595
|
+
const home = process.env["USERPROFILE"] ?? process.env["HOME"] ?? os7.homedir();
|
|
20596
|
+
const pluginDir = path30.join(home, ".gemini", "config", "plugins", "wairon");
|
|
20292
20597
|
try {
|
|
20293
|
-
if (
|
|
20294
|
-
|
|
20598
|
+
if (fs20.existsSync(pluginDir)) {
|
|
20599
|
+
fs20.rmSync(pluginDir, { recursive: true, force: true });
|
|
20295
20600
|
logger.info(`Removed legacy global Antigravity plugin at ${import_chalk4.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
|
|
20296
20601
|
return true;
|
|
20297
20602
|
}
|
|
@@ -20300,13 +20605,13 @@ function removeLegacyGlobalPlugin() {
|
|
|
20300
20605
|
}
|
|
20301
20606
|
return false;
|
|
20302
20607
|
}
|
|
20303
|
-
var
|
|
20608
|
+
var fs20, os7, path30, import_chalk4;
|
|
20304
20609
|
var init_mcp = __esm({
|
|
20305
20610
|
"src/commands/mcp.ts"() {
|
|
20306
20611
|
"use strict";
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20612
|
+
fs20 = __toESM(require("fs"));
|
|
20613
|
+
os7 = __toESM(require("os"));
|
|
20614
|
+
path30 = __toESM(require("path"));
|
|
20310
20615
|
import_chalk4 = __toESM(require("chalk"));
|
|
20311
20616
|
init_logger();
|
|
20312
20617
|
init_loader();
|
|
@@ -23141,8 +23446,8 @@ var require_dist = __commonJS({
|
|
|
23141
23446
|
function slug(name) {
|
|
23142
23447
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23143
23448
|
}
|
|
23144
|
-
var
|
|
23145
|
-
var
|
|
23449
|
+
var fs54 = __toESM2(require("fs"));
|
|
23450
|
+
var path67 = __toESM2(require("path"));
|
|
23146
23451
|
var import_fflate = require_node();
|
|
23147
23452
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
|
|
23148
23453
|
function listEntries(archive) {
|
|
@@ -23180,9 +23485,9 @@ var require_dist = __commonJS({
|
|
|
23180
23485
|
}
|
|
23181
23486
|
function writeTree(destDir, files) {
|
|
23182
23487
|
for (const file of files) {
|
|
23183
|
-
const absolute =
|
|
23184
|
-
|
|
23185
|
-
|
|
23488
|
+
const absolute = path67.join(destDir, file.path);
|
|
23489
|
+
fs54.mkdirSync(path67.dirname(absolute), { recursive: true });
|
|
23490
|
+
fs54.writeFileSync(absolute, file.contents);
|
|
23186
23491
|
}
|
|
23187
23492
|
}
|
|
23188
23493
|
function classifyKind(name, symlinks) {
|
|
@@ -23191,14 +23496,14 @@ var require_dist = __commonJS({
|
|
|
23191
23496
|
return "file";
|
|
23192
23497
|
}
|
|
23193
23498
|
function walkPackDir(root, current2, out) {
|
|
23194
|
-
for (const entry of
|
|
23499
|
+
for (const entry of fs54.readdirSync(current2, { withFileTypes: true })) {
|
|
23195
23500
|
if (entry.isDirectory()) {
|
|
23196
23501
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
23197
|
-
walkPackDir(root,
|
|
23502
|
+
walkPackDir(root, path67.join(current2, entry.name), out);
|
|
23198
23503
|
} else if (entry.isFile()) {
|
|
23199
|
-
const absolute =
|
|
23200
|
-
const relative22 =
|
|
23201
|
-
out.push({ path: relative22, contents:
|
|
23504
|
+
const absolute = path67.join(current2, entry.name);
|
|
23505
|
+
const relative22 = path67.relative(root, absolute).split(path67.sep).join("/");
|
|
23506
|
+
out.push({ path: relative22, contents: fs54.readFileSync(absolute) });
|
|
23202
23507
|
}
|
|
23203
23508
|
}
|
|
23204
23509
|
}
|
|
@@ -23367,7 +23672,7 @@ var require_dist = __commonJS({
|
|
|
23367
23672
|
});
|
|
23368
23673
|
|
|
23369
23674
|
// src/cli/index.ts
|
|
23370
|
-
var
|
|
23675
|
+
var path66 = __toESM(require("path"));
|
|
23371
23676
|
var import_commander = require("commander");
|
|
23372
23677
|
init_defaults();
|
|
23373
23678
|
init_logger();
|
|
@@ -23594,7 +23899,7 @@ function isSupportedAlias(name) {
|
|
|
23594
23899
|
}
|
|
23595
23900
|
|
|
23596
23901
|
// src/commands/init.ts
|
|
23597
|
-
var
|
|
23902
|
+
var path31 = __toESM(require("path"));
|
|
23598
23903
|
var import_chalk5 = __toESM(require("chalk"));
|
|
23599
23904
|
var import_inquirer = __toESM(require("inquirer"));
|
|
23600
23905
|
init_logger();
|
|
@@ -23615,7 +23920,7 @@ var WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
|
23615
23920
|
var WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
|
|
23616
23921
|
|
|
23617
23922
|
// src/exporters/claude.ts
|
|
23618
|
-
var
|
|
23923
|
+
var path22 = __toESM(require("path"));
|
|
23619
23924
|
init_fs();
|
|
23620
23925
|
var ClaudeExporter = class {
|
|
23621
23926
|
constructor() {
|
|
@@ -23624,7 +23929,7 @@ var ClaudeExporter = class {
|
|
|
23624
23929
|
outputPath(ctx) {
|
|
23625
23930
|
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23626
23931
|
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
23627
|
-
return
|
|
23932
|
+
return path22.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23628
23933
|
}
|
|
23629
23934
|
export(ctx) {
|
|
23630
23935
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -23645,7 +23950,7 @@ var ClaudeExporter = class {
|
|
|
23645
23950
|
};
|
|
23646
23951
|
|
|
23647
23952
|
// src/exporters/custom.ts
|
|
23648
|
-
var
|
|
23953
|
+
var path23 = __toESM(require("path"));
|
|
23649
23954
|
init_fs();
|
|
23650
23955
|
var CustomExporter = class {
|
|
23651
23956
|
constructor() {
|
|
@@ -23656,7 +23961,7 @@ var CustomExporter = class {
|
|
|
23656
23961
|
if (!("outputDir" in target)) {
|
|
23657
23962
|
throw new Error("CustomExporter requires target.outputDir");
|
|
23658
23963
|
}
|
|
23659
|
-
return
|
|
23964
|
+
return path23.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23660
23965
|
}
|
|
23661
23966
|
export(ctx) {
|
|
23662
23967
|
const { agent, target, renderedInstructions } = ctx;
|
|
@@ -23679,7 +23984,7 @@ var CustomExporter = class {
|
|
|
23679
23984
|
};
|
|
23680
23985
|
|
|
23681
23986
|
// src/exporters/gemini.ts
|
|
23682
|
-
var
|
|
23987
|
+
var path24 = __toESM(require("path"));
|
|
23683
23988
|
init_fs();
|
|
23684
23989
|
var GeminiExporter = class {
|
|
23685
23990
|
constructor() {
|
|
@@ -23688,7 +23993,7 @@ var GeminiExporter = class {
|
|
|
23688
23993
|
outputPath(ctx) {
|
|
23689
23994
|
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23690
23995
|
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
23691
|
-
return
|
|
23996
|
+
return path24.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
23692
23997
|
}
|
|
23693
23998
|
export(ctx) {
|
|
23694
23999
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -23713,11 +24018,11 @@ function yamlString(value) {
|
|
|
23713
24018
|
}
|
|
23714
24019
|
|
|
23715
24020
|
// src/exporters/generate.ts
|
|
23716
|
-
var
|
|
24021
|
+
var path28 = __toESM(require("path"));
|
|
23717
24022
|
|
|
23718
24023
|
// src/core/detection.ts
|
|
23719
|
-
var
|
|
23720
|
-
var
|
|
24024
|
+
var fs17 = __toESM(require("fs"));
|
|
24025
|
+
var path25 = __toESM(require("path"));
|
|
23721
24026
|
init_defaults();
|
|
23722
24027
|
var PACKAGE_MARKERS = [
|
|
23723
24028
|
"package.json",
|
|
@@ -23769,7 +24074,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
|
23769
24074
|
});
|
|
23770
24075
|
}
|
|
23771
24076
|
function parseGitmodules(filePath) {
|
|
23772
|
-
const content =
|
|
24077
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
23773
24078
|
const entries = [];
|
|
23774
24079
|
let current2 = {};
|
|
23775
24080
|
for (const line2 of content.split("\n")) {
|
|
@@ -23791,8 +24096,8 @@ function parseGitmodules(filePath) {
|
|
|
23791
24096
|
return entries;
|
|
23792
24097
|
}
|
|
23793
24098
|
function detectGitSubmodules(projectRoot2) {
|
|
23794
|
-
const gitmodulesPath =
|
|
23795
|
-
if (!
|
|
24099
|
+
const gitmodulesPath = path25.join(projectRoot2, ".gitmodules");
|
|
24100
|
+
if (!fs17.existsSync(gitmodulesPath)) return [];
|
|
23796
24101
|
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
23797
24102
|
suggestedId: pathToId(entry.path),
|
|
23798
24103
|
suggestedName: pathToName(entry.path),
|
|
@@ -23810,18 +24115,18 @@ function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
|
23810
24115
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
23811
24116
|
let entries;
|
|
23812
24117
|
try {
|
|
23813
|
-
entries =
|
|
24118
|
+
entries = fs17.readdirSync(currentDir, { withFileTypes: true });
|
|
23814
24119
|
} catch {
|
|
23815
24120
|
return;
|
|
23816
24121
|
}
|
|
23817
24122
|
for (const entry of entries) {
|
|
23818
24123
|
if (!entry.isDirectory()) continue;
|
|
23819
24124
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23820
|
-
const fullPath =
|
|
23821
|
-
const relPath = normalizePath3(
|
|
24125
|
+
const fullPath = path25.join(currentDir, entry.name);
|
|
24126
|
+
const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
|
|
23822
24127
|
if (relPath === "" || relPath === ".") continue;
|
|
23823
|
-
const gitPath =
|
|
23824
|
-
if (
|
|
24128
|
+
const gitPath = path25.join(fullPath, ".git");
|
|
24129
|
+
if (fs17.existsSync(gitPath)) {
|
|
23825
24130
|
results.push({
|
|
23826
24131
|
suggestedId: pathToId(relPath),
|
|
23827
24132
|
suggestedName: pathToName(relPath),
|
|
@@ -23843,17 +24148,17 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
|
23843
24148
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
23844
24149
|
let entries;
|
|
23845
24150
|
try {
|
|
23846
|
-
entries =
|
|
24151
|
+
entries = fs17.readdirSync(currentDir, { withFileTypes: true });
|
|
23847
24152
|
} catch {
|
|
23848
24153
|
return;
|
|
23849
24154
|
}
|
|
23850
24155
|
for (const entry of entries) {
|
|
23851
24156
|
if (!entry.isDirectory()) continue;
|
|
23852
24157
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23853
|
-
const fullPath =
|
|
23854
|
-
const relPath = normalizePath3(
|
|
24158
|
+
const fullPath = path25.join(currentDir, entry.name);
|
|
24159
|
+
const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
|
|
23855
24160
|
if (relPath === "" || relPath === ".") continue;
|
|
23856
|
-
const hasMarker = PACKAGE_MARKERS.some((m) =>
|
|
24161
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs17.existsSync(path25.join(fullPath, m)));
|
|
23857
24162
|
if (hasMarker) {
|
|
23858
24163
|
results.push({
|
|
23859
24164
|
suggestedId: pathToId(relPath),
|
|
@@ -23867,8 +24172,8 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
|
23867
24172
|
}
|
|
23868
24173
|
}
|
|
23869
24174
|
function pathToId(relPath) {
|
|
23870
|
-
const
|
|
23871
|
-
return
|
|
24175
|
+
const basename14 = path25.basename(relPath);
|
|
24176
|
+
return basename14.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23872
24177
|
}
|
|
23873
24178
|
function pathToName(relPath) {
|
|
23874
24179
|
const id = pathToId(relPath);
|
|
@@ -24095,7 +24400,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getPro
|
|
|
24095
24400
|
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
24096
24401
|
if (!targetConfig) continue;
|
|
24097
24402
|
const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
|
|
24098
|
-
expected.add(
|
|
24403
|
+
expected.add(path28.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
24099
24404
|
}
|
|
24100
24405
|
}
|
|
24101
24406
|
return expected;
|
|
@@ -24113,7 +24418,7 @@ async function runInit(options = {}) {
|
|
|
24113
24418
|
logger.header("wairon init");
|
|
24114
24419
|
const cwd = process.cwd();
|
|
24115
24420
|
const ancestorRoot = findSystemRoot(cwd);
|
|
24116
|
-
if (ancestorRoot &&
|
|
24421
|
+
if (ancestorRoot && path31.resolve(ancestorRoot) === path31.resolve(cwd)) {
|
|
24117
24422
|
logger.info("Project already initialized.");
|
|
24118
24423
|
logger.info("Design your spec tree with the SDD architect skill, then run `wairon generate`.");
|
|
24119
24424
|
return;
|
|
@@ -24129,8 +24434,8 @@ async function runInit(options = {}) {
|
|
|
24129
24434
|
await runInitInteractive();
|
|
24130
24435
|
}
|
|
24131
24436
|
async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
|
|
24132
|
-
const relPath =
|
|
24133
|
-
const defaultId =
|
|
24437
|
+
const relPath = path31.relative(parentRoot, cwd) || ".";
|
|
24438
|
+
const defaultId = path31.basename(cwd);
|
|
24134
24439
|
logger.info(`Detected a parent wairon project at ${parentRoot}`);
|
|
24135
24440
|
logger.info(`This directory ("${relPath}") is not yet a wairon project.`);
|
|
24136
24441
|
if (!options.yes) {
|
|
@@ -24190,7 +24495,7 @@ async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
|
|
|
24190
24495
|
}
|
|
24191
24496
|
async function runInitNonInteractive() {
|
|
24192
24497
|
const cwd = process.cwd();
|
|
24193
|
-
const projectName =
|
|
24498
|
+
const projectName = path31.basename(cwd);
|
|
24194
24499
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24195
24500
|
const targets = [defaultTargetConfig("claude"), defaultTargetConfig("agy")];
|
|
24196
24501
|
const projectConfig = buildProjectConfig(projectName, targets, now, "backend");
|
|
@@ -24212,7 +24517,7 @@ async function runInitNonInteractive() {
|
|
|
24212
24517
|
}
|
|
24213
24518
|
async function runInitInteractive() {
|
|
24214
24519
|
const cwd = process.cwd();
|
|
24215
|
-
const defaultProjectName =
|
|
24520
|
+
const defaultProjectName = path31.basename(cwd);
|
|
24216
24521
|
const { projectName } = await import_inquirer.default.prompt([
|
|
24217
24522
|
{
|
|
24218
24523
|
type: "input",
|
|
@@ -24372,7 +24677,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24372
24677
|
projectRoot: projectRoot2,
|
|
24373
24678
|
target: targetConfig
|
|
24374
24679
|
});
|
|
24375
|
-
logger.success(`Generated: ${
|
|
24680
|
+
logger.success(`Generated: ${path31.relative(cwd, result.outputPath)}`);
|
|
24376
24681
|
}
|
|
24377
24682
|
}
|
|
24378
24683
|
writeStarterDocs(projectName);
|
|
@@ -24387,7 +24692,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24387
24692
|
if (guidePlan.claudeLocal) {
|
|
24388
24693
|
const p = localGuideFilePath(projectRoot2, "claude");
|
|
24389
24694
|
injectGuide(p, "local");
|
|
24390
|
-
logger.success(`Injected wairon guide into ${
|
|
24695
|
+
logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
|
|
24391
24696
|
writeRootGuideDelegator(projectRoot2, "claude");
|
|
24392
24697
|
logger.success(`Created root CLAUDE.md delegator pointing to .claude/CLAUDE.md`);
|
|
24393
24698
|
}
|
|
@@ -24399,7 +24704,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24399
24704
|
if (guidePlan.geminiLocal) {
|
|
24400
24705
|
const p = localGuideFilePath(projectRoot2, "gemini");
|
|
24401
24706
|
injectGuide(p, "local");
|
|
24402
|
-
logger.success(`Injected wairon guide into ${
|
|
24707
|
+
logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
|
|
24403
24708
|
writeRootGuideDelegator(projectRoot2, "gemini");
|
|
24404
24709
|
logger.success(`Created root GEMINI.md delegator pointing to .gemini/GEMINI.md`);
|
|
24405
24710
|
}
|
|
@@ -24632,8 +24937,8 @@ A new project initialized with Wairon.
|
|
|
24632
24937
|
}
|
|
24633
24938
|
|
|
24634
24939
|
// src/commands/generate.ts
|
|
24635
|
-
var
|
|
24636
|
-
var
|
|
24940
|
+
var fs21 = __toESM(require("fs"));
|
|
24941
|
+
var path32 = __toESM(require("path"));
|
|
24637
24942
|
init_logger();
|
|
24638
24943
|
init_loader();
|
|
24639
24944
|
init_fs();
|
|
@@ -24641,29 +24946,29 @@ init_provision();
|
|
|
24641
24946
|
init_specs2();
|
|
24642
24947
|
var WAIRON_AGENT_FILE = /-(owner|implementer|architect)\.md$/;
|
|
24643
24948
|
function pruneStaleAgents(expectedPaths, scanDirs) {
|
|
24644
|
-
const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) =>
|
|
24949
|
+
const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) => path32.dirname(p)));
|
|
24645
24950
|
let pruned = 0;
|
|
24646
24951
|
for (const dir of dirs) {
|
|
24647
24952
|
let entries;
|
|
24648
24953
|
try {
|
|
24649
|
-
entries =
|
|
24954
|
+
entries = fs21.readdirSync(dir);
|
|
24650
24955
|
} catch {
|
|
24651
24956
|
continue;
|
|
24652
24957
|
}
|
|
24653
24958
|
for (const name of entries) {
|
|
24654
24959
|
if (!name.endsWith(".md")) continue;
|
|
24655
|
-
const full =
|
|
24960
|
+
const full = path32.resolve(dir, name);
|
|
24656
24961
|
if (expectedPaths.has(full)) continue;
|
|
24657
24962
|
let owned = WAIRON_AGENT_FILE.test(name);
|
|
24658
24963
|
if (!owned) {
|
|
24659
24964
|
try {
|
|
24660
|
-
owned =
|
|
24965
|
+
owned = fs21.readFileSync(full, "utf8").includes(WAIRON_MANAGED_MARKER);
|
|
24661
24966
|
} catch {
|
|
24662
24967
|
owned = false;
|
|
24663
24968
|
}
|
|
24664
24969
|
}
|
|
24665
24970
|
if (!owned) continue;
|
|
24666
|
-
|
|
24971
|
+
fs21.unlinkSync(full);
|
|
24667
24972
|
pruned++;
|
|
24668
24973
|
logger.verbose(`Pruned stale: ${full}`);
|
|
24669
24974
|
}
|
|
@@ -24677,7 +24982,7 @@ async function runGenerate(options = {}) {
|
|
|
24677
24982
|
const children = listDirectChainedSubprojects(getProjectRoot());
|
|
24678
24983
|
for (const child of children) {
|
|
24679
24984
|
logger.blank();
|
|
24680
|
-
logger.info(`\u21B3 Chained subproject "${child.subsystemId}" \u2014 generating its layer in ${
|
|
24985
|
+
logger.info(`\u21B3 Chained subproject "${child.subsystemId}" \u2014 generating its layer in ${path32.relative(getProjectRoot(), child.dir) || "."}/`);
|
|
24681
24986
|
await runWithProjectRoot(child.dir, async () => {
|
|
24682
24987
|
ensureProjectInitialized(child.subsystemId);
|
|
24683
24988
|
invalidateSpecCache();
|
|
@@ -24698,7 +25003,7 @@ async function generateLayer(options = {}) {
|
|
|
24698
25003
|
} else if (options.dryRun) {
|
|
24699
25004
|
logger.info("Dry run \u2014 rules.materializeAgentFiles is off: no agent files are written; leftover managed files would be removed.");
|
|
24700
25005
|
} else if (options.prune !== false) {
|
|
24701
|
-
const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) =>
|
|
25006
|
+
const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) => path32.dirname(p));
|
|
24702
25007
|
const removed = pruneStaleAgents(/* @__PURE__ */ new Set(), candidateDirs);
|
|
24703
25008
|
if (removed > 0) {
|
|
24704
25009
|
logger.info(`Removed ${removed} previously materialized agent file(s) \u2014 agents are served as live briefs (sdd_get_agent_brief); opt back in with rules.materializeAgentFiles: true.`);
|
|
@@ -24785,22 +25090,34 @@ function materializeAgentLayer(agents, projectConfig, options) {
|
|
|
24785
25090
|
}
|
|
24786
25091
|
|
|
24787
25092
|
// src/commands/lock.ts
|
|
24788
|
-
var
|
|
25093
|
+
var os8 = __toESM(require("os"));
|
|
24789
25094
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
24790
25095
|
init_logger();
|
|
25096
|
+
init_baseline();
|
|
24791
25097
|
init_defaults();
|
|
25098
|
+
var path33 = __toESM(require("path"));
|
|
25099
|
+
init_fs();
|
|
24792
25100
|
async function runLock(options = {}, gate) {
|
|
24793
|
-
const
|
|
24794
|
-
if (
|
|
24795
|
-
logger.info("
|
|
25101
|
+
const diff = diffAgainstBaseline();
|
|
25102
|
+
if (!diff) {
|
|
25103
|
+
logger.info("First approval of this tree \u2014 the whole spec tree becomes the approved baseline.");
|
|
25104
|
+
} else if (diffSize(diff) === 0) {
|
|
25105
|
+
logger.info("Nothing has changed since the last approval \u2014 this will re-validate and regenerate the agent topology.");
|
|
24796
25106
|
} else {
|
|
24797
|
-
logger.info(`${
|
|
24798
|
-
for (const p of
|
|
24799
|
-
|
|
25107
|
+
logger.info(`${diffSize(diff)} spec(s) changed since the last approval:`);
|
|
25108
|
+
for (const p of diff.changed) logger.info(` ~ ${p}`);
|
|
25109
|
+
for (const p of diff.added) logger.info(` + ${p}`);
|
|
25110
|
+
for (const p of diff.removed) logger.info(` - ${p}`);
|
|
25111
|
+
}
|
|
25112
|
+
const moved = movedChildren(loadSubsystemSpecs());
|
|
25113
|
+
if (moved.length > 0) {
|
|
25114
|
+
logger.info(`${moved.length} chained child project(s) moved since the last approval:`);
|
|
25115
|
+
for (const m of moved) {
|
|
25116
|
+
logger.info(` ${m.id}: ${m.pinned.slice(0, 19)}\u2026 \u2192 ${m.now ? `${m.now.slice(0, 19)}\u2026` : "(no approval)"}`);
|
|
24800
25117
|
}
|
|
24801
25118
|
}
|
|
24802
25119
|
logger.blank();
|
|
24803
|
-
logger.warn("This
|
|
25120
|
+
logger.warn("This records the current design as approved and (re)generates the agent topology.");
|
|
24804
25121
|
if (!options.yes) {
|
|
24805
25122
|
if (!process.stdin.isTTY) {
|
|
24806
25123
|
logger.error("Non-interactive shell \u2014 re-run with --yes to confirm the lock.");
|
|
@@ -24810,24 +25127,15 @@ async function runLock(options = {}, gate) {
|
|
|
24810
25127
|
{
|
|
24811
25128
|
type: "confirm",
|
|
24812
25129
|
name: "confirmed",
|
|
24813
|
-
message: "
|
|
25130
|
+
message: "Approve this design and generate the agent topology?",
|
|
24814
25131
|
default: false
|
|
24815
25132
|
}
|
|
24816
25133
|
]);
|
|
24817
25134
|
if (!confirmed) return null;
|
|
24818
25135
|
}
|
|
24819
|
-
if (options.subsystem) {
|
|
24820
|
-
for (const p of promotable) applySpecStatus(p.kind, p.id, "complete");
|
|
24821
|
-
invalidateSpecCache();
|
|
24822
|
-
} else {
|
|
24823
|
-
promoteAllComplete();
|
|
24824
|
-
}
|
|
24825
|
-
if (promotable.length > 0) {
|
|
24826
|
-
logger.success(`Locked ${promotable.length} spec(s) as complete.`);
|
|
24827
|
-
}
|
|
24828
25136
|
let lockedBy = "local";
|
|
24829
25137
|
try {
|
|
24830
|
-
lockedBy = `local:${
|
|
25138
|
+
lockedBy = `local:${os8.userInfo().username}`;
|
|
24831
25139
|
} catch {
|
|
24832
25140
|
}
|
|
24833
25141
|
const record2 = {
|
|
@@ -24843,6 +25151,13 @@ async function runLock(options = {}, gate) {
|
|
|
24843
25151
|
status: "ready"
|
|
24844
25152
|
};
|
|
24845
25153
|
writeLockRecord(record2);
|
|
25154
|
+
const root = getProjectRoot();
|
|
25155
|
+
const scope = options.subsystem ? {
|
|
25156
|
+
paths: new Set(
|
|
25157
|
+
specPathsInScope(options.subsystem).map((p) => path33.relative(root, p).split(path33.sep).join("/"))
|
|
25158
|
+
)
|
|
25159
|
+
} : void 0;
|
|
25160
|
+
writeBaseline(captureBaseline(lockedBy, currentChildPins(loadSubsystemSpecs(), root), root, scope));
|
|
24846
25161
|
return record2;
|
|
24847
25162
|
}
|
|
24848
25163
|
|
|
@@ -25099,29 +25414,29 @@ init_mcp();
|
|
|
25099
25414
|
|
|
25100
25415
|
// src/commands/update.ts
|
|
25101
25416
|
var https2 = __toESM(require("https"));
|
|
25102
|
-
var
|
|
25103
|
-
var
|
|
25104
|
-
var
|
|
25105
|
-
var
|
|
25417
|
+
var fs23 = __toESM(require("fs"));
|
|
25418
|
+
var path34 = __toESM(require("path"));
|
|
25419
|
+
var os9 = __toESM(require("os"));
|
|
25420
|
+
var crypto4 = __toESM(require("crypto"));
|
|
25106
25421
|
var import_child_process2 = require("child_process");
|
|
25107
25422
|
init_logger();
|
|
25108
25423
|
init_defaults();
|
|
25109
25424
|
init_version();
|
|
25110
25425
|
|
|
25111
25426
|
// src/utils/download.ts
|
|
25112
|
-
var
|
|
25427
|
+
var fs22 = __toESM(require("fs"));
|
|
25113
25428
|
var http = __toESM(require("http"));
|
|
25114
25429
|
var https = __toESM(require("https"));
|
|
25115
25430
|
init_defaults();
|
|
25116
25431
|
function downloadFile(url, dest) {
|
|
25117
|
-
return new Promise((
|
|
25118
|
-
const file =
|
|
25432
|
+
return new Promise((resolve27, reject) => {
|
|
25433
|
+
const file = fs22.createWriteStream(dest);
|
|
25119
25434
|
const get4 = url.startsWith("https://") ? https.get : http.get;
|
|
25120
25435
|
get4(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
|
|
25121
25436
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
25122
25437
|
file.close();
|
|
25123
25438
|
res.destroy();
|
|
25124
|
-
downloadFile(res.headers.location, dest).then(
|
|
25439
|
+
downloadFile(res.headers.location, dest).then(resolve27).catch(reject);
|
|
25125
25440
|
return;
|
|
25126
25441
|
}
|
|
25127
25442
|
if (res.statusCode !== 200) {
|
|
@@ -25133,16 +25448,16 @@ function downloadFile(url, dest) {
|
|
|
25133
25448
|
res.pipe(file);
|
|
25134
25449
|
file.on("finish", () => {
|
|
25135
25450
|
res.destroy();
|
|
25136
|
-
file.close(() =>
|
|
25451
|
+
file.close(() => resolve27());
|
|
25137
25452
|
});
|
|
25138
25453
|
file.on("error", (err) => {
|
|
25139
25454
|
res.destroy();
|
|
25140
|
-
|
|
25455
|
+
fs22.unlink(dest, () => {
|
|
25141
25456
|
});
|
|
25142
25457
|
reject(err);
|
|
25143
25458
|
});
|
|
25144
25459
|
}).on("error", (err) => {
|
|
25145
|
-
|
|
25460
|
+
fs22.unlink(dest, () => {
|
|
25146
25461
|
});
|
|
25147
25462
|
reject(err);
|
|
25148
25463
|
});
|
|
@@ -25208,8 +25523,8 @@ async function runUpdate(options = {}) {
|
|
|
25208
25523
|
logger.info(`Download manually from: ${release.html_url}`);
|
|
25209
25524
|
process.exit(1);
|
|
25210
25525
|
}
|
|
25211
|
-
const tmpDir =
|
|
25212
|
-
const tmpFile =
|
|
25526
|
+
const tmpDir = os9.tmpdir();
|
|
25527
|
+
const tmpFile = path34.join(tmpDir, assetName);
|
|
25213
25528
|
logger.info(`Downloading ${assetName}...`);
|
|
25214
25529
|
try {
|
|
25215
25530
|
await downloadFile(asset.browser_download_url, tmpFile);
|
|
@@ -25226,16 +25541,16 @@ async function runUpdate(options = {}) {
|
|
|
25226
25541
|
const checksumAssetName = assetName + ".sha256";
|
|
25227
25542
|
const checksumAsset = release.assets.find((a) => a.name === checksumAssetName);
|
|
25228
25543
|
if (checksumAsset) {
|
|
25229
|
-
const tmpChecksum =
|
|
25544
|
+
const tmpChecksum = path34.join(tmpDir, checksumAssetName);
|
|
25230
25545
|
logger.info(`Verifying checksum...`);
|
|
25231
25546
|
try {
|
|
25232
25547
|
await downloadFile(checksumAsset.browser_download_url, tmpChecksum);
|
|
25233
25548
|
verifyChecksum(tmpFile, tmpChecksum, assetName);
|
|
25234
|
-
|
|
25549
|
+
fs23.unlinkSync(tmpChecksum);
|
|
25235
25550
|
} catch (err) {
|
|
25236
25551
|
logger.error(`Checksum verification failed: ${err.message}`);
|
|
25237
25552
|
try {
|
|
25238
|
-
|
|
25553
|
+
fs23.unlinkSync(tmpFile);
|
|
25239
25554
|
} catch {
|
|
25240
25555
|
}
|
|
25241
25556
|
process.exit(1);
|
|
@@ -25270,7 +25585,7 @@ function releaseChannelLabel(tag) {
|
|
|
25270
25585
|
return "stable";
|
|
25271
25586
|
}
|
|
25272
25587
|
function fetchReleases(repo) {
|
|
25273
|
-
return new Promise((
|
|
25588
|
+
return new Promise((resolve27, reject) => {
|
|
25274
25589
|
const url = `https://api.github.com/repos/${repo}/releases?per_page=20`;
|
|
25275
25590
|
const options = {
|
|
25276
25591
|
headers: {
|
|
@@ -25290,7 +25605,7 @@ function fetchReleases(repo) {
|
|
|
25290
25605
|
return;
|
|
25291
25606
|
}
|
|
25292
25607
|
try {
|
|
25293
|
-
|
|
25608
|
+
resolve27(JSON.parse(data));
|
|
25294
25609
|
} catch {
|
|
25295
25610
|
reject(new Error("Failed to parse GitHub API response"));
|
|
25296
25611
|
}
|
|
@@ -25300,10 +25615,10 @@ function fetchReleases(repo) {
|
|
|
25300
25615
|
});
|
|
25301
25616
|
}
|
|
25302
25617
|
function verifyChecksum(filePath, checksumFile, expectedFilename) {
|
|
25303
|
-
const checksumContent =
|
|
25618
|
+
const checksumContent = fs23.readFileSync(checksumFile, "utf-8").trim();
|
|
25304
25619
|
const expectedHash = checksumContent.split(/\s+/)[0].toLowerCase();
|
|
25305
|
-
const fileBuffer =
|
|
25306
|
-
const actualHash =
|
|
25620
|
+
const fileBuffer = fs23.readFileSync(filePath);
|
|
25621
|
+
const actualHash = crypto4.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase();
|
|
25307
25622
|
if (actualHash !== expectedHash) {
|
|
25308
25623
|
throw new Error(
|
|
25309
25624
|
`SHA-256 mismatch for ${expectedFilename}
|
|
@@ -25333,9 +25648,9 @@ function isPkgBinary2() {
|
|
|
25333
25648
|
function installBinary(tmpFile, destPath) {
|
|
25334
25649
|
const platform = process.platform;
|
|
25335
25650
|
const isZip = tmpFile.endsWith(".zip");
|
|
25336
|
-
const extractDir =
|
|
25337
|
-
if (
|
|
25338
|
-
|
|
25651
|
+
const extractDir = path34.join(os9.tmpdir(), "wairon-extract");
|
|
25652
|
+
if (fs23.existsSync(extractDir)) fs23.rmSync(extractDir, { recursive: true });
|
|
25653
|
+
fs23.mkdirSync(extractDir, { recursive: true });
|
|
25339
25654
|
if (isZip) {
|
|
25340
25655
|
(0, import_child_process2.execSync)(
|
|
25341
25656
|
`powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmpFile}' -DestinationPath '${extractDir}' -Force"`,
|
|
@@ -25345,18 +25660,18 @@ function installBinary(tmpFile, destPath) {
|
|
|
25345
25660
|
(0, import_child_process2.execSync)(`tar -xzf "${tmpFile}" -C "${extractDir}"`, { stdio: ["ignore", "pipe", "pipe"] });
|
|
25346
25661
|
}
|
|
25347
25662
|
const binaryName = platform === "win32" ? "wairon.exe" : "wairon";
|
|
25348
|
-
const extractedBinary =
|
|
25349
|
-
if (!
|
|
25663
|
+
const extractedBinary = path34.join(extractDir, binaryName);
|
|
25664
|
+
if (!fs23.existsSync(extractedBinary)) {
|
|
25350
25665
|
throw new Error(`Extracted binary not found at ${extractedBinary}`);
|
|
25351
25666
|
}
|
|
25352
25667
|
if (platform === "win32") {
|
|
25353
25668
|
const oldPath = destPath + ".old";
|
|
25354
25669
|
try {
|
|
25355
25670
|
cleanStaleBinary(oldPath);
|
|
25356
|
-
|
|
25357
|
-
|
|
25671
|
+
fs23.renameSync(destPath, oldPath);
|
|
25672
|
+
fs23.copyFileSync(extractedBinary, destPath);
|
|
25358
25673
|
try {
|
|
25359
|
-
|
|
25674
|
+
fs23.unlinkSync(oldPath);
|
|
25360
25675
|
} catch {
|
|
25361
25676
|
}
|
|
25362
25677
|
} catch (err) {
|
|
@@ -25370,25 +25685,25 @@ function installBinary(tmpFile, destPath) {
|
|
|
25370
25685
|
}
|
|
25371
25686
|
} else {
|
|
25372
25687
|
const tmpDest = destPath + ".new";
|
|
25373
|
-
|
|
25374
|
-
|
|
25375
|
-
|
|
25688
|
+
fs23.copyFileSync(extractedBinary, tmpDest);
|
|
25689
|
+
fs23.chmodSync(tmpDest, 493);
|
|
25690
|
+
fs23.renameSync(tmpDest, destPath);
|
|
25376
25691
|
}
|
|
25377
25692
|
try {
|
|
25378
|
-
|
|
25693
|
+
fs23.unlinkSync(tmpFile);
|
|
25379
25694
|
} catch {
|
|
25380
25695
|
}
|
|
25381
25696
|
try {
|
|
25382
|
-
|
|
25697
|
+
fs23.rmSync(extractDir, { recursive: true });
|
|
25383
25698
|
} catch {
|
|
25384
25699
|
}
|
|
25385
25700
|
}
|
|
25386
25701
|
function cleanStaleBinary(oldPath) {
|
|
25387
25702
|
const target = oldPath ?? (isPkgBinary2() ? process.execPath + ".old" : null);
|
|
25388
25703
|
if (!target) return;
|
|
25389
|
-
if (
|
|
25704
|
+
if (fs23.existsSync(target)) {
|
|
25390
25705
|
try {
|
|
25391
|
-
|
|
25706
|
+
fs23.unlinkSync(target);
|
|
25392
25707
|
} catch {
|
|
25393
25708
|
}
|
|
25394
25709
|
}
|
|
@@ -25465,7 +25780,7 @@ async function filteredCheckbox(config) {
|
|
|
25465
25780
|
32,
|
|
25466
25781
|
Math.max(...items.map((i) => i.label.length))
|
|
25467
25782
|
);
|
|
25468
|
-
return new Promise((
|
|
25783
|
+
return new Promise((resolve27) => {
|
|
25469
25784
|
const checked = /* @__PURE__ */ new Set();
|
|
25470
25785
|
let cursor = 0;
|
|
25471
25786
|
let filterIdx = 0;
|
|
@@ -25532,7 +25847,7 @@ async function filteredCheckbox(config) {
|
|
|
25532
25847
|
);
|
|
25533
25848
|
teardown();
|
|
25534
25849
|
const result = items.filter((_, idx) => checked.has(idx)).map((i) => i.value);
|
|
25535
|
-
|
|
25850
|
+
resolve27(result);
|
|
25536
25851
|
}
|
|
25537
25852
|
function abort() {
|
|
25538
25853
|
process.stdout.write("\n");
|
|
@@ -25804,9 +26119,9 @@ async function runSkillsInstall() {
|
|
|
25804
26119
|
}
|
|
25805
26120
|
|
|
25806
26121
|
// src/commands/doctor.ts
|
|
25807
|
-
var
|
|
25808
|
-
var
|
|
25809
|
-
var
|
|
26122
|
+
var fs24 = __toESM(require("fs"));
|
|
26123
|
+
var os10 = __toESM(require("os"));
|
|
26124
|
+
var path35 = __toESM(require("path"));
|
|
25810
26125
|
var import_chalk12 = __toESM(require("chalk"));
|
|
25811
26126
|
init_logger();
|
|
25812
26127
|
init_defaults();
|
|
@@ -25837,10 +26152,10 @@ function stampVerdict(content) {
|
|
|
25837
26152
|
return { mark: "warn", note: `v${v} \u2014 stale, installed is v${WAIRON_VERSION}` };
|
|
25838
26153
|
}
|
|
25839
26154
|
function mcpEntryHealth(settingsPath) {
|
|
25840
|
-
if (!
|
|
26155
|
+
if (!fs24.existsSync(settingsPath)) return { mark: "warn", note: "not registered" };
|
|
25841
26156
|
let entry;
|
|
25842
26157
|
try {
|
|
25843
|
-
const s = JSON.parse(
|
|
26158
|
+
const s = JSON.parse(fs24.readFileSync(settingsPath, "utf8"));
|
|
25844
26159
|
entry = s.mcpServers?.["wairon"];
|
|
25845
26160
|
} catch {
|
|
25846
26161
|
return { mark: "error", note: "parse error" };
|
|
@@ -25848,7 +26163,7 @@ function mcpEntryHealth(settingsPath) {
|
|
|
25848
26163
|
if (!entry) return { mark: "warn", note: "not registered" };
|
|
25849
26164
|
if (entry.command === "node" && Array.isArray(entry.args) && typeof entry.args[0] === "string") {
|
|
25850
26165
|
const scriptPath = entry.args[0];
|
|
25851
|
-
if (!
|
|
26166
|
+
if (!fs24.existsSync(scriptPath)) {
|
|
25852
26167
|
return { mark: "error", note: `registered but the server path is missing \u2014 ${scriptPath}` };
|
|
25853
26168
|
}
|
|
25854
26169
|
}
|
|
@@ -25900,7 +26215,7 @@ async function runDoctor(options = {}) {
|
|
|
25900
26215
|
const { findChainingSubprojectsMissingConfig: findChainingSubprojectsMissingConfig2 } = (init_provision(), __toCommonJS(provision_exports));
|
|
25901
26216
|
const missing = findChainingSubprojectsMissingConfig2(getProjectRoot());
|
|
25902
26217
|
if (missing.length > 0) {
|
|
25903
|
-
line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) =>
|
|
26218
|
+
line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path35.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
|
|
25904
26219
|
}
|
|
25905
26220
|
} catch {
|
|
25906
26221
|
}
|
|
@@ -25938,7 +26253,7 @@ async function runDoctor(options = {}) {
|
|
|
25938
26253
|
const gp = localGuideFilePath(process.cwd(), t);
|
|
25939
26254
|
if (!gp || seenGuides.has(gp)) continue;
|
|
25940
26255
|
seenGuides.add(gp);
|
|
25941
|
-
const rel2 =
|
|
26256
|
+
const rel2 = path35.relative(process.cwd(), gp).replace(/\\/g, "/");
|
|
25942
26257
|
if (!pathExists(gp)) {
|
|
25943
26258
|
line(tally, "warn", `${rel2} guide \u2014 not injected (run \`wairon generate\`)`);
|
|
25944
26259
|
continue;
|
|
@@ -26020,17 +26335,17 @@ async function runDoctor(options = {}) {
|
|
|
26020
26335
|
line(tally, h.mark, `Claude (project .mcp.json): ${h.note}${h.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend claude`"}`);
|
|
26021
26336
|
}
|
|
26022
26337
|
if (wantGemini) {
|
|
26023
|
-
const globalCfg =
|
|
26338
|
+
const globalCfg = path35.join(os10.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
|
|
26024
26339
|
const hg = mcpEntryHealth(globalCfg);
|
|
26025
26340
|
line(tally, hg.mark, `Antigravity (global mcp_config.json): ${hg.note}${hg.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend gemini --global`"}`);
|
|
26026
26341
|
const projPath = fromProjectRoot(".gemini", "settings.json");
|
|
26027
|
-
if (
|
|
26342
|
+
if (fs24.existsSync(projPath)) {
|
|
26028
26343
|
const hp = mcpEntryHealth(projPath);
|
|
26029
26344
|
line(tally, hp.mark === "error" ? "error" : "ok", `Gemini CLI (project): ${hp.note} ${import_chalk12.default.gray("(Antigravity ignores this file)")}`);
|
|
26030
26345
|
}
|
|
26031
26346
|
}
|
|
26032
|
-
const pluginDir =
|
|
26033
|
-
if (
|
|
26347
|
+
const pluginDir = path35.join(os10.homedir(), ".gemini", "config", "plugins", "wairon");
|
|
26348
|
+
if (fs24.existsSync(pluginDir)) {
|
|
26034
26349
|
line(tally, "warn", `Legacy Antigravity plugin present (${pluginDir}) \u2014 it collides with the wairon MCP server. Remove it with \`wairon doctor --fix\`.`);
|
|
26035
26350
|
}
|
|
26036
26351
|
logger.blank();
|
|
@@ -26069,7 +26384,7 @@ async function applyFixes() {
|
|
|
26069
26384
|
const legacySpecs = findLegacySpecFiles();
|
|
26070
26385
|
if (legacySpecs.length > 0) {
|
|
26071
26386
|
for (const { path: oldPath, expected: newPath } of legacySpecs) {
|
|
26072
|
-
|
|
26387
|
+
fs24.renameSync(oldPath, newPath);
|
|
26073
26388
|
}
|
|
26074
26389
|
console.log(` ${icon("ok")} Migrated ${legacySpecs.length} legacy spec file(s) to the new dot-prefixed unified schema.`);
|
|
26075
26390
|
}
|
|
@@ -26123,8 +26438,8 @@ function printSummary(tally) {
|
|
|
26123
26438
|
}
|
|
26124
26439
|
|
|
26125
26440
|
// src/commands/diagram.ts
|
|
26126
|
-
var
|
|
26127
|
-
var
|
|
26441
|
+
var fs25 = __toESM(require("fs"));
|
|
26442
|
+
var path36 = __toESM(require("path"));
|
|
26128
26443
|
init_logger();
|
|
26129
26444
|
init_loader();
|
|
26130
26445
|
init_fs();
|
|
@@ -26159,71 +26474,71 @@ function collectIssues() {
|
|
|
26159
26474
|
}
|
|
26160
26475
|
function writeCanvas(dest) {
|
|
26161
26476
|
const model = buildCanvasModel(collectIssues());
|
|
26162
|
-
ensureDir(
|
|
26163
|
-
|
|
26477
|
+
ensureDir(path36.dirname(path36.resolve(dest)));
|
|
26478
|
+
fs25.writeFileSync(dest, renderCanvasHtml(model), "utf-8");
|
|
26164
26479
|
}
|
|
26165
26480
|
function parseSequenceRef(ref) {
|
|
26166
|
-
const
|
|
26167
|
-
if (
|
|
26481
|
+
const sep9 = ref.includes(":") ? ref.lastIndexOf(":") : ref.lastIndexOf(".");
|
|
26482
|
+
if (sep9 <= 0 || sep9 === ref.length - 1) {
|
|
26168
26483
|
throw new WaironError(
|
|
26169
26484
|
`Invalid --sequence reference "${ref}". Use <componentId>:<methodName> (e.g. billing-portal:authorize).`
|
|
26170
26485
|
);
|
|
26171
26486
|
}
|
|
26172
|
-
return { component: ref.slice(0,
|
|
26487
|
+
return { component: ref.slice(0, sep9), method: ref.slice(sep9 + 1) };
|
|
26173
26488
|
}
|
|
26174
26489
|
async function runDiagram(rawOptions = {}) {
|
|
26175
26490
|
assertProjectInitialized();
|
|
26176
26491
|
const options = applyFormat(rawOptions);
|
|
26177
26492
|
if (options.canvas && !options.all) {
|
|
26178
|
-
const dest2 = options.out ??
|
|
26493
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
26179
26494
|
writeCanvas(dest2);
|
|
26180
26495
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
26181
26496
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline).");
|
|
26182
26497
|
return;
|
|
26183
26498
|
}
|
|
26184
26499
|
if (options.drawio && !options.all) {
|
|
26185
|
-
const dest2 = options.out ??
|
|
26186
|
-
ensureDir(
|
|
26187
|
-
|
|
26500
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
|
|
26501
|
+
ensureDir(path36.dirname(path36.resolve(dest2)));
|
|
26502
|
+
fs25.writeFileSync(dest2, generateDrawioXml(buildCanvasModel()), "utf-8");
|
|
26188
26503
|
logger.success(`draw.io diagram written to ${dest2}`);
|
|
26189
26504
|
logger.info("Open with draw.io / diagrams.net (or import into tools that accept the format).");
|
|
26190
26505
|
return;
|
|
26191
26506
|
}
|
|
26192
26507
|
if (options.excalidraw && !options.all) {
|
|
26193
|
-
const dest2 = options.out ??
|
|
26194
|
-
ensureDir(
|
|
26195
|
-
|
|
26508
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
|
|
26509
|
+
ensureDir(path36.dirname(path36.resolve(dest2)));
|
|
26510
|
+
fs25.writeFileSync(dest2, generateExcalidrawScene(buildCanvasModel()), "utf-8");
|
|
26196
26511
|
logger.success(`Excalidraw scene written to ${dest2}`);
|
|
26197
26512
|
logger.info("Open with excalidraw.com or the VS Code extension.");
|
|
26198
26513
|
return;
|
|
26199
26514
|
}
|
|
26200
26515
|
const wantsMermaid = options.format?.toLowerCase().startsWith("mermaid") || !!options.subsystem || !!options.sequence;
|
|
26201
26516
|
if (!options.all && !options.sequence && !wantsMermaid) {
|
|
26202
|
-
const dest2 = options.out ??
|
|
26517
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
26203
26518
|
writeCanvas(dest2);
|
|
26204
26519
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
26205
26520
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline). Other formats: --format mermaid|drawio|excalidraw.");
|
|
26206
26521
|
return;
|
|
26207
26522
|
}
|
|
26208
26523
|
if (options.all) {
|
|
26209
|
-
const outDir = options.out ??
|
|
26524
|
+
const outDir = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams");
|
|
26210
26525
|
const files = generateDiagramSet();
|
|
26211
26526
|
if (files.length === 0) {
|
|
26212
26527
|
logger.warn("No diagrams to generate \u2014 the spec tree has no components yet.");
|
|
26213
26528
|
return;
|
|
26214
26529
|
}
|
|
26215
26530
|
for (const file of files) {
|
|
26216
|
-
const dest2 =
|
|
26217
|
-
ensureDir(
|
|
26218
|
-
|
|
26531
|
+
const dest2 = path36.join(outDir, file.relPath);
|
|
26532
|
+
ensureDir(path36.dirname(dest2));
|
|
26533
|
+
fs25.writeFileSync(dest2, toMarkdown(file), "utf-8");
|
|
26219
26534
|
}
|
|
26220
|
-
writeCanvas(
|
|
26535
|
+
writeCanvas(path36.join(outDir, "canvas.html"));
|
|
26221
26536
|
const exportModel = buildCanvasModel();
|
|
26222
|
-
|
|
26223
|
-
|
|
26537
|
+
fs25.writeFileSync(path36.join(outDir, "architecture.drawio"), generateDrawioXml(exportModel), "utf-8");
|
|
26538
|
+
fs25.writeFileSync(path36.join(outDir, "architecture.excalidraw"), generateExcalidrawScene(exportModel), "utf-8");
|
|
26224
26539
|
const graph = loadSpecGraph();
|
|
26225
|
-
const indexPath =
|
|
26226
|
-
|
|
26540
|
+
const indexPath = path36.join(outDir, "README.md");
|
|
26541
|
+
fs25.writeFileSync(indexPath, diagramSetIndex(files, graph.systemName), "utf-8");
|
|
26227
26542
|
logger.success(`Generated ${files.length} diagram(s) + interactive canvas.html + index into ${outDir}`);
|
|
26228
26543
|
for (const file of files.slice(0, 12)) {
|
|
26229
26544
|
logger.info(` ${file.relPath}`);
|
|
@@ -26234,26 +26549,26 @@ async function runDiagram(rawOptions = {}) {
|
|
|
26234
26549
|
let mermaid;
|
|
26235
26550
|
let title;
|
|
26236
26551
|
let defaultDest;
|
|
26237
|
-
const diagramsDir =
|
|
26552
|
+
const diagramsDir = path36.join(AI_PATHS.docsDir(), "diagrams");
|
|
26238
26553
|
if (options.sequence) {
|
|
26239
26554
|
const { component, method: method2 } = parseSequenceRef(options.sequence);
|
|
26240
26555
|
mermaid = generateSequenceDiagram(component, method2, { depth: options.depth });
|
|
26241
26556
|
title = `${component}.${method2} \u2014 narrative sequence`;
|
|
26242
|
-
defaultDest =
|
|
26557
|
+
defaultDest = path36.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
|
|
26243
26558
|
} else if (options.subsystem) {
|
|
26244
26559
|
mermaid = generateComponentDiagram({ subsystem: options.subsystem });
|
|
26245
26560
|
title = `${options.subsystem} \u2014 components`;
|
|
26246
|
-
defaultDest =
|
|
26561
|
+
defaultDest = path36.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
|
|
26247
26562
|
} else {
|
|
26248
26563
|
mermaid = generateComponentDiagram();
|
|
26249
26564
|
title = "Component architecture";
|
|
26250
|
-
defaultDest =
|
|
26565
|
+
defaultDest = path36.join(diagramsDir, "system.md");
|
|
26251
26566
|
}
|
|
26252
26567
|
const dest = options.out ?? defaultDest;
|
|
26253
|
-
ensureDir(
|
|
26568
|
+
ensureDir(path36.dirname(path36.resolve(dest)));
|
|
26254
26569
|
const content = dest.endsWith(".mmd") ? `${mermaid}
|
|
26255
26570
|
` : toMarkdown({ relPath: dest, title, mermaid });
|
|
26256
|
-
|
|
26571
|
+
fs25.writeFileSync(dest, content, "utf-8");
|
|
26257
26572
|
logger.success(`Mermaid diagram written to ${dest}`);
|
|
26258
26573
|
logger.info("Renders on GitHub/IDE previews; use a .mmd --out path for raw Mermaid.");
|
|
26259
26574
|
}
|
|
@@ -26362,9 +26677,9 @@ Component variants (${variants.length})
|
|
|
26362
26677
|
}
|
|
26363
26678
|
|
|
26364
26679
|
// src/commands/packs.ts
|
|
26365
|
-
var
|
|
26366
|
-
var
|
|
26367
|
-
var
|
|
26680
|
+
var fs26 = __toESM(require("fs"));
|
|
26681
|
+
var os11 = __toESM(require("os"));
|
|
26682
|
+
var path37 = __toESM(require("path"));
|
|
26368
26683
|
var import_chalk16 = __toESM(require("chalk"));
|
|
26369
26684
|
var import_sdk = __toESM(require_dist());
|
|
26370
26685
|
init_logger();
|
|
@@ -26390,11 +26705,11 @@ function describe(probe2) {
|
|
|
26390
26705
|
return parts.join(", ");
|
|
26391
26706
|
}
|
|
26392
26707
|
function resolveSourceUnit(source) {
|
|
26393
|
-
const abs =
|
|
26394
|
-
if (!
|
|
26708
|
+
const abs = path37.resolve(source);
|
|
26709
|
+
if (!fs26.existsSync(abs)) {
|
|
26395
26710
|
throw new Error(`Pack source "${source}" does not exist.`);
|
|
26396
26711
|
}
|
|
26397
|
-
const isDir =
|
|
26712
|
+
const isDir = fs26.statSync(abs).isDirectory();
|
|
26398
26713
|
if (isDir && !packDirEntry(abs)) {
|
|
26399
26714
|
throw new Error(`"${source}" is a directory without a pack entry file (pack.yaml | pack.cjs | index.cjs | ...).`);
|
|
26400
26715
|
}
|
|
@@ -26410,7 +26725,7 @@ async function addPack(source, options = {}) {
|
|
|
26410
26725
|
}
|
|
26411
26726
|
const { abs } = resolveSourceUnit(source);
|
|
26412
26727
|
const scope = options.global ? "global" : "project";
|
|
26413
|
-
const probe2 = probePack(abs,
|
|
26728
|
+
const probe2 = probePack(abs, path37.dirname(abs), scope);
|
|
26414
26729
|
if (probe2.error) {
|
|
26415
26730
|
logger.error(probe2.error);
|
|
26416
26731
|
process.exitCode = 1;
|
|
@@ -26418,10 +26733,10 @@ async function addPack(source, options = {}) {
|
|
|
26418
26733
|
}
|
|
26419
26734
|
if (options.global) {
|
|
26420
26735
|
const destDir = globalPacksDir();
|
|
26421
|
-
const dest2 =
|
|
26422
|
-
if (
|
|
26423
|
-
|
|
26424
|
-
|
|
26736
|
+
const dest2 = path37.join(destDir, path37.basename(abs));
|
|
26737
|
+
if (path37.resolve(dest2) !== abs) {
|
|
26738
|
+
fs26.mkdirSync(destDir, { recursive: true });
|
|
26739
|
+
fs26.cpSync(abs, dest2, { recursive: true, force: true });
|
|
26425
26740
|
}
|
|
26426
26741
|
logger.success(`Installed pack "${probe2.name}" globally: ${dest2}`);
|
|
26427
26742
|
logger.info(`${describe(probe2)} \u2014 auto-loaded for every project on this machine (WAIRON_PACKS_DIR / ~/.wairon/packs).`);
|
|
@@ -26434,11 +26749,11 @@ async function addPack(source, options = {}) {
|
|
|
26434
26749
|
return;
|
|
26435
26750
|
}
|
|
26436
26751
|
const root = getProjectRoot();
|
|
26437
|
-
const relRef = `.wai/packs/${
|
|
26438
|
-
const dest =
|
|
26439
|
-
if (
|
|
26440
|
-
|
|
26441
|
-
|
|
26752
|
+
const relRef = `.wai/packs/${path37.basename(abs)}`;
|
|
26753
|
+
const dest = path37.join(root, ".wai", "packs", path37.basename(abs));
|
|
26754
|
+
if (path37.resolve(dest) !== abs) {
|
|
26755
|
+
fs26.mkdirSync(path37.dirname(dest), { recursive: true });
|
|
26756
|
+
fs26.cpSync(abs, dest, { recursive: true, force: true });
|
|
26442
26757
|
}
|
|
26443
26758
|
const config = loadProjectConfig();
|
|
26444
26759
|
const packs = config.extensions?.packs ?? [];
|
|
@@ -26447,14 +26762,14 @@ async function addPack(source, options = {}) {
|
|
|
26447
26762
|
saveProjectConfig(config);
|
|
26448
26763
|
logger.success(`Vendored pack "${probe2.name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
26449
26764
|
} else {
|
|
26450
|
-
|
|
26765
|
+
fs26.cpSync(abs, dest, { recursive: true, force: true });
|
|
26451
26766
|
logger.success(`Pack "${probe2.name}" already registered \u2014 refreshed ${relRef} from the source.`);
|
|
26452
26767
|
}
|
|
26453
26768
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
26454
26769
|
}
|
|
26455
26770
|
async function addPackFromArchive(source, options) {
|
|
26456
|
-
const abs =
|
|
26457
|
-
if (!
|
|
26771
|
+
const abs = path37.resolve(source);
|
|
26772
|
+
if (!fs26.existsSync(abs) || !fs26.statSync(abs).isFile()) {
|
|
26458
26773
|
logger.error(`Pack archive "${source}" does not exist.`);
|
|
26459
26774
|
process.exitCode = 1;
|
|
26460
26775
|
return;
|
|
@@ -26469,27 +26784,27 @@ async function addPackFromArchive(source, options) {
|
|
|
26469
26784
|
process.exitCode = 1;
|
|
26470
26785
|
return;
|
|
26471
26786
|
}
|
|
26472
|
-
baseDir =
|
|
26787
|
+
baseDir = path37.join(getProjectRoot(), ".wai", "packs");
|
|
26473
26788
|
}
|
|
26474
|
-
const bytes =
|
|
26475
|
-
|
|
26476
|
-
const staging =
|
|
26789
|
+
const bytes = fs26.readFileSync(abs);
|
|
26790
|
+
fs26.mkdirSync(baseDir, { recursive: true });
|
|
26791
|
+
const staging = fs26.mkdtempSync(path37.join(baseDir, ".wpack-staging-"));
|
|
26477
26792
|
let result;
|
|
26478
26793
|
try {
|
|
26479
26794
|
result = (0, import_sdk.extractPack)(bytes, staging);
|
|
26480
26795
|
} catch (err) {
|
|
26481
|
-
|
|
26482
|
-
logger.error(`Failed to extract pack archive "${
|
|
26796
|
+
fs26.rmSync(staging, { recursive: true, force: true });
|
|
26797
|
+
logger.error(`Failed to extract pack archive "${path37.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
|
|
26483
26798
|
process.exitCode = 1;
|
|
26484
26799
|
return;
|
|
26485
26800
|
}
|
|
26486
26801
|
const name = result.name;
|
|
26487
|
-
const destDir =
|
|
26488
|
-
if (
|
|
26489
|
-
|
|
26490
|
-
const probe2 = probePack(destDir,
|
|
26802
|
+
const destDir = path37.join(baseDir, name);
|
|
26803
|
+
if (fs26.existsSync(destDir)) fs26.rmSync(destDir, { recursive: true, force: true });
|
|
26804
|
+
fs26.renameSync(staging, destDir);
|
|
26805
|
+
const probe2 = probePack(destDir, path37.dirname(destDir), scope);
|
|
26491
26806
|
if (probe2.error) {
|
|
26492
|
-
|
|
26807
|
+
fs26.rmSync(destDir, { recursive: true, force: true });
|
|
26493
26808
|
logger.error(probe2.error);
|
|
26494
26809
|
process.exitCode = 1;
|
|
26495
26810
|
return;
|
|
@@ -26507,7 +26822,7 @@ async function addPackFromArchive(source, options) {
|
|
|
26507
26822
|
saveProjectConfig(config);
|
|
26508
26823
|
logger.success(`Installed pack "${probe2.name ?? name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
26509
26824
|
} else {
|
|
26510
|
-
logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${
|
|
26825
|
+
logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path37.basename(abs)}.`);
|
|
26511
26826
|
}
|
|
26512
26827
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
26513
26828
|
}
|
|
@@ -26528,7 +26843,7 @@ async function buildPack(source, options = {}) {
|
|
|
26528
26843
|
const sourceDir = source && source.length > 0 ? source : ".";
|
|
26529
26844
|
const result = (0, import_sdk.buildPack)(sourceDir);
|
|
26530
26845
|
const outPath = options.out ?? result.suggestedFileName;
|
|
26531
|
-
|
|
26846
|
+
fs26.writeFileSync(outPath, result.archive);
|
|
26532
26847
|
logger.success(`Built pack "${result.info.name}" v${result.info.version} \u2192 ${outPath} (${result.archive.byteLength} bytes)`);
|
|
26533
26848
|
logger.info(`Install it with \`wairon pack add ${outPath}\`, or upload it to a hosted instance.`);
|
|
26534
26849
|
}
|
|
@@ -26539,8 +26854,8 @@ function expandSource(source, version) {
|
|
|
26539
26854
|
return source.replace(/\{version\}/g, version ?? "").replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? "");
|
|
26540
26855
|
}
|
|
26541
26856
|
async function fetchArchive(url) {
|
|
26542
|
-
const dir =
|
|
26543
|
-
const dest =
|
|
26857
|
+
const dir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packdl-"));
|
|
26858
|
+
const dest = path37.join(dir, "pack.wpack");
|
|
26544
26859
|
await downloadFile(url, dest);
|
|
26545
26860
|
return dest;
|
|
26546
26861
|
}
|
|
@@ -26559,7 +26874,7 @@ async function installPack(source) {
|
|
|
26559
26874
|
}
|
|
26560
26875
|
const extracted = extractArchiveToTemp(archive);
|
|
26561
26876
|
try {
|
|
26562
|
-
|
|
26877
|
+
fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
|
|
26563
26878
|
} catch {
|
|
26564
26879
|
}
|
|
26565
26880
|
if (!extracted) return;
|
|
@@ -26571,7 +26886,7 @@ async function installPack(source) {
|
|
|
26571
26886
|
process.exitCode = 1;
|
|
26572
26887
|
} finally {
|
|
26573
26888
|
try {
|
|
26574
|
-
|
|
26889
|
+
fs26.rmSync(extracted.dir, { recursive: true, force: true });
|
|
26575
26890
|
} catch {
|
|
26576
26891
|
}
|
|
26577
26892
|
}
|
|
@@ -26582,7 +26897,7 @@ async function installPack(source) {
|
|
|
26582
26897
|
if (!extracted) return;
|
|
26583
26898
|
sourceDir = extracted.dir;
|
|
26584
26899
|
cleanup = extracted.dir;
|
|
26585
|
-
origin =
|
|
26900
|
+
origin = path37.resolve(source);
|
|
26586
26901
|
} else {
|
|
26587
26902
|
let unit;
|
|
26588
26903
|
try {
|
|
@@ -26608,7 +26923,7 @@ async function installPack(source) {
|
|
|
26608
26923
|
} finally {
|
|
26609
26924
|
if (cleanup) {
|
|
26610
26925
|
try {
|
|
26611
|
-
|
|
26926
|
+
fs26.rmSync(cleanup, { recursive: true, force: true });
|
|
26612
26927
|
} catch {
|
|
26613
26928
|
}
|
|
26614
26929
|
}
|
|
@@ -26625,20 +26940,20 @@ function reportInstalled(installed, origin) {
|
|
|
26625
26940
|
}
|
|
26626
26941
|
}
|
|
26627
26942
|
function extractArchiveToTemp(source) {
|
|
26628
|
-
const abs =
|
|
26629
|
-
if (!
|
|
26943
|
+
const abs = path37.resolve(source);
|
|
26944
|
+
if (!fs26.existsSync(abs)) {
|
|
26630
26945
|
logger.error(`Pack archive "${source}" does not exist.`);
|
|
26631
26946
|
process.exitCode = 1;
|
|
26632
26947
|
return null;
|
|
26633
26948
|
}
|
|
26634
|
-
const tempDir =
|
|
26949
|
+
const tempDir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packinstall-"));
|
|
26635
26950
|
try {
|
|
26636
|
-
const result = (0, import_sdk.extractPack)(new Uint8Array(
|
|
26951
|
+
const result = (0, import_sdk.extractPack)(new Uint8Array(fs26.readFileSync(abs)), tempDir);
|
|
26637
26952
|
void result;
|
|
26638
26953
|
return { dir: tempDir };
|
|
26639
26954
|
} catch (e) {
|
|
26640
26955
|
try {
|
|
26641
|
-
|
|
26956
|
+
fs26.rmSync(tempDir, { recursive: true, force: true });
|
|
26642
26957
|
} catch {
|
|
26643
26958
|
}
|
|
26644
26959
|
logger.error(`Could not extract "${source}": ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -26784,7 +27099,7 @@ async function fetchAndInstall(selection, url) {
|
|
|
26784
27099
|
const archive = await fetchArchive(url);
|
|
26785
27100
|
const extracted = extractArchiveToTemp(archive);
|
|
26786
27101
|
try {
|
|
26787
|
-
|
|
27102
|
+
fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
|
|
26788
27103
|
} catch {
|
|
26789
27104
|
}
|
|
26790
27105
|
if (!extracted) return null;
|
|
@@ -26796,7 +27111,7 @@ async function fetchAndInstall(selection, url) {
|
|
|
26796
27111
|
return pack;
|
|
26797
27112
|
} finally {
|
|
26798
27113
|
try {
|
|
26799
|
-
|
|
27114
|
+
fs26.rmSync(extracted.dir, { recursive: true, force: true });
|
|
26800
27115
|
} catch {
|
|
26801
27116
|
}
|
|
26802
27117
|
}
|
|
@@ -26807,10 +27122,10 @@ function bundleTargets(selections, name, all) {
|
|
|
26807
27122
|
return selections.filter((s) => s.bundle === true);
|
|
26808
27123
|
}
|
|
26809
27124
|
function writeBundle(root, resolved) {
|
|
26810
|
-
const dest =
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
27125
|
+
const dest = path37.join(root, ".wai", "packs", resolved.name, resolved.version);
|
|
27126
|
+
fs26.rmSync(dest, { recursive: true, force: true });
|
|
27127
|
+
fs26.mkdirSync(path37.dirname(dest), { recursive: true });
|
|
27128
|
+
fs26.cpSync(resolved.path, dest, { recursive: true });
|
|
26814
27129
|
return dest;
|
|
26815
27130
|
}
|
|
26816
27131
|
async function bundlePack(name, options = {}) {
|
|
@@ -26841,7 +27156,7 @@ async function bundlePack(name, options = {}) {
|
|
|
26841
27156
|
selection.version = resolved.version;
|
|
26842
27157
|
selection.bundle = true;
|
|
26843
27158
|
bundled.push(`${resolved.name}@${resolved.version}`);
|
|
26844
|
-
console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(
|
|
27159
|
+
console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(path37.relative(root, dest).replace(/\\/g, "/"))}`);
|
|
26845
27160
|
}
|
|
26846
27161
|
if (bundled.length === 0) return;
|
|
26847
27162
|
config.extensions = { packs: entries, useGlobalPacks: globalPacksEnabled(config) };
|
|
@@ -26883,9 +27198,9 @@ async function listPacks() {
|
|
|
26883
27198
|
console.log(import_chalk16.default.bold.cyan(`\u25A0 Global (${globalPacksDir()})${useGlobal ? "" : import_chalk16.default.yellow(" [disabled: extensions.useGlobalPacks: false]")}`));
|
|
26884
27199
|
if (globalRefs.length === 0) console.log(import_chalk16.default.dim(" (none)"));
|
|
26885
27200
|
for (const ref of globalRefs) {
|
|
26886
|
-
const probe2 = probePack(ref,
|
|
26887
|
-
if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${
|
|
26888
|
-
else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ??
|
|
27201
|
+
const probe2 = probePack(ref, path37.dirname(ref), "global");
|
|
27202
|
+
if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path37.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
|
|
27203
|
+
else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path37.basename(ref))} ${import_chalk16.default.dim(describe(probe2))}`);
|
|
26889
27204
|
}
|
|
26890
27205
|
console.log("");
|
|
26891
27206
|
if (!inProject) {
|
|
@@ -26913,9 +27228,9 @@ async function listPacks() {
|
|
|
26913
27228
|
async function removePack(name, options = {}) {
|
|
26914
27229
|
if (options.global) {
|
|
26915
27230
|
for (const ref of discoverPacks(globalPacksDir())) {
|
|
26916
|
-
const probe2 = probePack(ref,
|
|
26917
|
-
if (probe2.name === name ||
|
|
26918
|
-
|
|
27231
|
+
const probe2 = probePack(ref, path37.dirname(ref), "global");
|
|
27232
|
+
if (probe2.name === name || path37.basename(ref) === name) {
|
|
27233
|
+
fs26.rmSync(ref, { recursive: true, force: true });
|
|
26919
27234
|
logger.success(`Removed global pack "${probe2.name ?? name}" (${ref}).`);
|
|
26920
27235
|
return;
|
|
26921
27236
|
}
|
|
@@ -26936,16 +27251,16 @@ async function removePack(name, options = {}) {
|
|
|
26936
27251
|
if (typeof entry !== "string") continue;
|
|
26937
27252
|
const ref = entry;
|
|
26938
27253
|
const probe2 = probePack(ref, root, "project");
|
|
26939
|
-
if (probe2.name === name || ref === name ||
|
|
27254
|
+
if (probe2.name === name || ref === name || path37.basename(ref) === name) {
|
|
26940
27255
|
config.extensions = {
|
|
26941
27256
|
packs: packs.filter((p) => p !== ref),
|
|
26942
27257
|
useGlobalPacks: globalPacksEnabled(config)
|
|
26943
27258
|
};
|
|
26944
27259
|
saveProjectConfig(config);
|
|
26945
|
-
const resolved =
|
|
26946
|
-
const vendorDir =
|
|
26947
|
-
if (resolved.startsWith(vendorDir +
|
|
26948
|
-
|
|
27260
|
+
const resolved = path37.resolve(root, ref);
|
|
27261
|
+
const vendorDir = path37.resolve(root, ".wai", "packs");
|
|
27262
|
+
if (resolved.startsWith(vendorDir + path37.sep)) {
|
|
27263
|
+
fs26.rmSync(resolved, { recursive: true, force: true });
|
|
26949
27264
|
logger.success(`Deregistered pack "${probe2.name ?? name}" and deleted ${ref}.`);
|
|
26950
27265
|
} else {
|
|
26951
27266
|
logger.success(`Deregistered pack "${probe2.name ?? name}" (files at ${ref} left in place).`);
|
|
@@ -26958,22 +27273,22 @@ async function removePack(name, options = {}) {
|
|
|
26958
27273
|
}
|
|
26959
27274
|
|
|
26960
27275
|
// src/commands/host.ts
|
|
26961
|
-
var
|
|
26962
|
-
var
|
|
26963
|
-
var
|
|
26964
|
-
var
|
|
27276
|
+
var fs53 = __toESM(require("fs"));
|
|
27277
|
+
var path64 = __toESM(require("path"));
|
|
27278
|
+
var os12 = __toESM(require("os"));
|
|
27279
|
+
var crypto22 = __toESM(require("crypto"));
|
|
26965
27280
|
var import_child_process5 = require("child_process");
|
|
26966
27281
|
var import_chalk17 = __toESM(require("chalk"));
|
|
26967
27282
|
init_logger();
|
|
26968
27283
|
init_errors();
|
|
26969
27284
|
|
|
26970
27285
|
// src/server/admin.ts
|
|
26971
|
-
var
|
|
27286
|
+
var crypto9 = __toESM(require("crypto"));
|
|
26972
27287
|
init_fs();
|
|
26973
27288
|
init_defaults();
|
|
26974
27289
|
|
|
26975
27290
|
// src/server/auth.ts
|
|
26976
|
-
var
|
|
27291
|
+
var crypto7 = __toESM(require("crypto"));
|
|
26977
27292
|
|
|
26978
27293
|
// src/server/types.ts
|
|
26979
27294
|
var SSO_ADMIN_ROLE_ID = "sso-admin";
|
|
@@ -26986,35 +27301,35 @@ var UNAUTHENTICATED = {
|
|
|
26986
27301
|
var WEB_SESSION_PREFIX = "ws_";
|
|
26987
27302
|
|
|
26988
27303
|
// src/server/credentials.ts
|
|
26989
|
-
var
|
|
26990
|
-
var
|
|
26991
|
-
var
|
|
27304
|
+
var fs27 = __toESM(require("fs"));
|
|
27305
|
+
var path38 = __toESM(require("path"));
|
|
27306
|
+
var crypto5 = __toESM(require("crypto"));
|
|
26992
27307
|
var HASH_NS = "wairon:token:v1";
|
|
26993
27308
|
function hashToken(token) {
|
|
26994
|
-
return
|
|
27309
|
+
return crypto5.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
|
|
26995
27310
|
}
|
|
26996
27311
|
function storePath(dataDir) {
|
|
26997
|
-
return
|
|
27312
|
+
return path38.join(dataDir, "auth", "credentials.json");
|
|
26998
27313
|
}
|
|
26999
27314
|
function load3(dataDir) {
|
|
27000
27315
|
try {
|
|
27001
|
-
return JSON.parse(
|
|
27316
|
+
return JSON.parse(fs27.readFileSync(storePath(dataDir), "utf8"));
|
|
27002
27317
|
} catch {
|
|
27003
27318
|
return [];
|
|
27004
27319
|
}
|
|
27005
27320
|
}
|
|
27006
27321
|
function save(dataDir, records) {
|
|
27007
27322
|
const p = storePath(dataDir);
|
|
27008
|
-
|
|
27323
|
+
fs27.mkdirSync(path38.dirname(p), { recursive: true });
|
|
27009
27324
|
const tmp = `${p}.tmp`;
|
|
27010
|
-
|
|
27011
|
-
|
|
27325
|
+
fs27.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
27326
|
+
fs27.renameSync(tmp, p);
|
|
27012
27327
|
}
|
|
27013
27328
|
function digestEquals(a, b) {
|
|
27014
27329
|
const ab = Buffer.from(a, "hex");
|
|
27015
27330
|
const bb = Buffer.from(b, "hex");
|
|
27016
27331
|
if (ab.length === 0 || ab.length !== bb.length) return false;
|
|
27017
|
-
return
|
|
27332
|
+
return crypto5.timingSafeEqual(ab, bb);
|
|
27018
27333
|
}
|
|
27019
27334
|
function findByTokenHash(dataDir, tokenHash) {
|
|
27020
27335
|
return load3(dataDir).find((r) => digestEquals(r.keyHash, tokenHash)) ?? null;
|
|
@@ -27053,17 +27368,17 @@ function listByOwner(dataDir, ownerUserId) {
|
|
|
27053
27368
|
}
|
|
27054
27369
|
|
|
27055
27370
|
// src/server/websessions.ts
|
|
27056
|
-
var
|
|
27057
|
-
var
|
|
27058
|
-
var
|
|
27371
|
+
var fs28 = __toESM(require("fs"));
|
|
27372
|
+
var path39 = __toESM(require("path"));
|
|
27373
|
+
var crypto6 = __toESM(require("crypto"));
|
|
27059
27374
|
function storePath2(dataDir) {
|
|
27060
|
-
return
|
|
27375
|
+
return path39.join(dataDir, "web-sessions.json");
|
|
27061
27376
|
}
|
|
27062
27377
|
function readSessions(dataDir) {
|
|
27063
27378
|
const p = storePath2(dataDir);
|
|
27064
27379
|
let raw;
|
|
27065
27380
|
try {
|
|
27066
|
-
raw =
|
|
27381
|
+
raw = fs28.readFileSync(p, "utf8");
|
|
27067
27382
|
} catch (e) {
|
|
27068
27383
|
if (e.code === "ENOENT") return [];
|
|
27069
27384
|
throw new Error(`Failed to read web session store at ${p}: ${e.message}`);
|
|
@@ -27078,13 +27393,13 @@ function readSessions(dataDir) {
|
|
|
27078
27393
|
}
|
|
27079
27394
|
function persistSessions(dataDir, sessions) {
|
|
27080
27395
|
const p = storePath2(dataDir);
|
|
27081
|
-
|
|
27396
|
+
fs28.mkdirSync(path39.dirname(p), { recursive: true });
|
|
27082
27397
|
const tmp = `${p}.tmp`;
|
|
27083
|
-
|
|
27084
|
-
|
|
27398
|
+
fs28.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
|
|
27399
|
+
fs28.renameSync(tmp, p);
|
|
27085
27400
|
}
|
|
27086
27401
|
function mintSessionId() {
|
|
27087
|
-
return `${WEB_SESSION_PREFIX}${
|
|
27402
|
+
return `${WEB_SESSION_PREFIX}${crypto6.randomBytes(24).toString("hex")}`;
|
|
27088
27403
|
}
|
|
27089
27404
|
var WebSessionStore = class {
|
|
27090
27405
|
constructor(dataDir) {
|
|
@@ -27242,17 +27557,17 @@ function listWebSessionsBySubject(dataDir, userId) {
|
|
|
27242
27557
|
}
|
|
27243
27558
|
|
|
27244
27559
|
// src/server/users.ts
|
|
27245
|
-
var
|
|
27246
|
-
var
|
|
27560
|
+
var fs29 = __toESM(require("fs"));
|
|
27561
|
+
var path40 = __toESM(require("path"));
|
|
27247
27562
|
var VALID_STATUSES = ["active", "inactive", "suspended", "deactivated", "disabled"];
|
|
27248
27563
|
function storePath3(dataDir) {
|
|
27249
|
-
return
|
|
27564
|
+
return path40.join(dataDir, "users.json");
|
|
27250
27565
|
}
|
|
27251
27566
|
function loadStore(dataDir) {
|
|
27252
27567
|
const p = storePath3(dataDir);
|
|
27253
27568
|
let raw;
|
|
27254
27569
|
try {
|
|
27255
|
-
raw =
|
|
27570
|
+
raw = fs29.readFileSync(p, "utf8");
|
|
27256
27571
|
} catch (err) {
|
|
27257
27572
|
if (err.code === "ENOENT") return [];
|
|
27258
27573
|
throw new Error(`Cannot read hosted-user store at ${p}: ${err.message}`);
|
|
@@ -27270,10 +27585,10 @@ function loadStore(dataDir) {
|
|
|
27270
27585
|
}
|
|
27271
27586
|
function replaceAll(dataDir, records) {
|
|
27272
27587
|
const p = storePath3(dataDir);
|
|
27273
|
-
|
|
27588
|
+
fs29.mkdirSync(path40.dirname(p), { recursive: true });
|
|
27274
27589
|
const tmp = `${p}.tmp`;
|
|
27275
|
-
|
|
27276
|
-
|
|
27590
|
+
fs29.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
27591
|
+
fs29.renameSync(tmp, p);
|
|
27277
27592
|
}
|
|
27278
27593
|
function registryUpsert(dataDir, record2) {
|
|
27279
27594
|
const records = loadStore(dataDir);
|
|
@@ -27375,11 +27690,11 @@ function remapUnitReferences(dataDir, remap, removedScopeIds) {
|
|
|
27375
27690
|
}
|
|
27376
27691
|
|
|
27377
27692
|
// src/server/instance.ts
|
|
27378
|
-
var
|
|
27379
|
-
var
|
|
27693
|
+
var fs30 = __toESM(require("fs"));
|
|
27694
|
+
var path41 = __toESM(require("path"));
|
|
27380
27695
|
var import_crypto = require("crypto");
|
|
27381
27696
|
function storePath4(dataDir) {
|
|
27382
|
-
return
|
|
27697
|
+
return path41.join(dataDir, "instance.json");
|
|
27383
27698
|
}
|
|
27384
27699
|
var InstanceIdentityStore = class {
|
|
27385
27700
|
constructor(dataDir) {
|
|
@@ -27396,7 +27711,7 @@ var InstanceIdentityStore = class {
|
|
|
27396
27711
|
const p = storePath4(this.dataDir);
|
|
27397
27712
|
let raw;
|
|
27398
27713
|
try {
|
|
27399
|
-
raw =
|
|
27714
|
+
raw = fs30.readFileSync(p, "utf8");
|
|
27400
27715
|
} catch (err) {
|
|
27401
27716
|
if (err.code === "ENOENT") return null;
|
|
27402
27717
|
throw new Error(`Cannot read instance identity at ${p}: ${err.message}`);
|
|
@@ -27419,10 +27734,10 @@ var InstanceIdentityStore = class {
|
|
|
27419
27734
|
* never truncates the file. Only called by the registry's create-once seed. */
|
|
27420
27735
|
replace(identity) {
|
|
27421
27736
|
const p = storePath4(this.dataDir);
|
|
27422
|
-
|
|
27737
|
+
fs30.mkdirSync(path41.dirname(p), { recursive: true });
|
|
27423
27738
|
const tmp = `${p}.tmp`;
|
|
27424
|
-
|
|
27425
|
-
|
|
27739
|
+
fs30.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
|
|
27740
|
+
fs30.renameSync(tmp, p);
|
|
27426
27741
|
}
|
|
27427
27742
|
};
|
|
27428
27743
|
var InstanceIdentityRegistry = class {
|
|
@@ -27478,8 +27793,8 @@ function getInstanceIdentity(dataDir) {
|
|
|
27478
27793
|
}
|
|
27479
27794
|
|
|
27480
27795
|
// src/utils/secrets.ts
|
|
27481
|
-
var
|
|
27482
|
-
var
|
|
27796
|
+
var fs31 = __toESM(require("fs"));
|
|
27797
|
+
var path42 = __toESM(require("path"));
|
|
27483
27798
|
var ENV_FALLBACK = {
|
|
27484
27799
|
"git-token": ["WAIRON_GIT_TOKEN"],
|
|
27485
27800
|
"notion-token": ["WAIRON_NOTION_TOKEN"],
|
|
@@ -27488,13 +27803,13 @@ var ENV_FALLBACK = {
|
|
|
27488
27803
|
};
|
|
27489
27804
|
function storePath5() {
|
|
27490
27805
|
const dataDir = process.env["WAIRON_DATA_DIR"];
|
|
27491
|
-
return dataDir ?
|
|
27806
|
+
return dataDir ? path42.join(dataDir, "auth", "secrets.json") : null;
|
|
27492
27807
|
}
|
|
27493
27808
|
function readStore() {
|
|
27494
27809
|
const p = storePath5();
|
|
27495
27810
|
if (!p) return {};
|
|
27496
27811
|
try {
|
|
27497
|
-
return JSON.parse(
|
|
27812
|
+
return JSON.parse(fs31.readFileSync(p, "utf8"));
|
|
27498
27813
|
} catch {
|
|
27499
27814
|
return {};
|
|
27500
27815
|
}
|
|
@@ -27519,10 +27834,10 @@ function setSecret(key, value) {
|
|
|
27519
27834
|
if (!p) throw new Error("WAIRON_DATA_DIR is not set \u2014 a running server needs it to store secrets.");
|
|
27520
27835
|
const store = readStore();
|
|
27521
27836
|
store[key] = value;
|
|
27522
|
-
|
|
27837
|
+
fs31.mkdirSync(path42.dirname(p), { recursive: true });
|
|
27523
27838
|
const tmp = `${p}.tmp`;
|
|
27524
|
-
|
|
27525
|
-
|
|
27839
|
+
fs31.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
|
|
27840
|
+
fs31.renameSync(tmp, p);
|
|
27526
27841
|
}
|
|
27527
27842
|
function listSecretKeys() {
|
|
27528
27843
|
return Object.keys(readStore());
|
|
@@ -27600,7 +27915,7 @@ function masterMatches(credential) {
|
|
|
27600
27915
|
if (!master || !credential) return false;
|
|
27601
27916
|
const a = Buffer.from(hashToken(credential), "hex");
|
|
27602
27917
|
const b = Buffer.from(hashToken(master), "hex");
|
|
27603
|
-
return a.length === b.length &&
|
|
27918
|
+
return a.length === b.length && crypto7.timingSafeEqual(a, b);
|
|
27604
27919
|
}
|
|
27605
27920
|
function bootstrapAdminPrincipal() {
|
|
27606
27921
|
const subject = {
|
|
@@ -27648,7 +27963,7 @@ function authenticateMaster(token) {
|
|
|
27648
27963
|
function hashedEquals(a, b) {
|
|
27649
27964
|
const ha = Buffer.from(hashToken(a), "hex");
|
|
27650
27965
|
const hb = Buffer.from(hashToken(b), "hex");
|
|
27651
|
-
return ha.length === hb.length &&
|
|
27966
|
+
return ha.length === hb.length && crypto7.timingSafeEqual(ha, hb);
|
|
27652
27967
|
}
|
|
27653
27968
|
function verifyBuiltinAdmin(cfg, user, password) {
|
|
27654
27969
|
const configuredUser = cfg.builtinAdminUser ?? "";
|
|
@@ -27697,16 +28012,16 @@ function signingKey() {
|
|
|
27697
28012
|
}
|
|
27698
28013
|
function signViewToken(project2, format) {
|
|
27699
28014
|
const body = Buffer.from(JSON.stringify({ project: project2, format, exp: Date.now() + VIEW_TTL_MS })).toString("base64url");
|
|
27700
|
-
const sig =
|
|
28015
|
+
const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27701
28016
|
return `${body}.${sig}`;
|
|
27702
28017
|
}
|
|
27703
28018
|
function verifyViewToken(token) {
|
|
27704
28019
|
const [body, sig] = String(token).split(".");
|
|
27705
28020
|
if (!body || !sig) throw new Error("invalid view token");
|
|
27706
|
-
const expected =
|
|
28021
|
+
const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27707
28022
|
const a = Buffer.from(sig);
|
|
27708
28023
|
const b = Buffer.from(expected);
|
|
27709
|
-
if (a.length !== b.length || !
|
|
28024
|
+
if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid view token");
|
|
27710
28025
|
const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
27711
28026
|
if (Date.now() > payload.exp) throw new Error("expired view token");
|
|
27712
28027
|
return { project: payload.project, format: payload.format, expiresAt: new Date(payload.exp).toISOString() };
|
|
@@ -27714,33 +28029,33 @@ function verifyViewToken(token) {
|
|
|
27714
28029
|
var SSO_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
27715
28030
|
function signSsoState(payload) {
|
|
27716
28031
|
const body = Buffer.from(JSON.stringify({ payload, exp: Date.now() + SSO_STATE_TTL_MS })).toString("base64url");
|
|
27717
|
-
const sig =
|
|
28032
|
+
const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27718
28033
|
return `${body}.${sig}`;
|
|
27719
28034
|
}
|
|
27720
28035
|
function verifySsoState(state) {
|
|
27721
28036
|
const [body, sig] = String(state).split(".");
|
|
27722
28037
|
if (!body || !sig) throw new Error("invalid SSO state");
|
|
27723
|
-
const expected =
|
|
28038
|
+
const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27724
28039
|
const a = Buffer.from(sig);
|
|
27725
28040
|
const b = Buffer.from(expected);
|
|
27726
|
-
if (a.length !== b.length || !
|
|
28041
|
+
if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid SSO state");
|
|
27727
28042
|
const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
27728
28043
|
if (Date.now() > parsed.exp) throw new Error("expired SSO state");
|
|
27729
28044
|
return parsed.payload;
|
|
27730
28045
|
}
|
|
27731
28046
|
|
|
27732
28047
|
// src/server/organization.ts
|
|
27733
|
-
var
|
|
27734
|
-
var
|
|
27735
|
-
var
|
|
28048
|
+
var fs32 = __toESM(require("fs"));
|
|
28049
|
+
var path43 = __toESM(require("path"));
|
|
28050
|
+
var crypto8 = __toESM(require("crypto"));
|
|
27736
28051
|
function storePath6(dataDir) {
|
|
27737
|
-
return
|
|
28052
|
+
return path43.join(dataDir, "organization.json");
|
|
27738
28053
|
}
|
|
27739
28054
|
function readState(dataDir) {
|
|
27740
28055
|
const p = storePath6(dataDir);
|
|
27741
28056
|
let raw;
|
|
27742
28057
|
try {
|
|
27743
|
-
raw =
|
|
28058
|
+
raw = fs32.readFileSync(p, "utf8");
|
|
27744
28059
|
} catch (e) {
|
|
27745
28060
|
if (e.code === "ENOENT") return { units: [], placements: [] };
|
|
27746
28061
|
throw new Error(`Failed to read organization store at ${p}: ${e.message}`);
|
|
@@ -27757,10 +28072,10 @@ function readState(dataDir) {
|
|
|
27757
28072
|
}
|
|
27758
28073
|
function persistState(dataDir, state) {
|
|
27759
28074
|
const p = storePath6(dataDir);
|
|
27760
|
-
|
|
28075
|
+
fs32.mkdirSync(path43.dirname(p), { recursive: true });
|
|
27761
28076
|
const tmp = `${p}.tmp`;
|
|
27762
|
-
|
|
27763
|
-
|
|
28077
|
+
fs32.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
|
|
28078
|
+
fs32.renameSync(tmp, p);
|
|
27764
28079
|
}
|
|
27765
28080
|
var SLUG_PATTERN = /^[a-z0-9-]+$/;
|
|
27766
28081
|
var UNIT_KINDS = ["business_entity", "department", "team", "group"];
|
|
@@ -28055,7 +28370,7 @@ var OrganizationRegistry = class {
|
|
|
28055
28370
|
createdAt: placements[existingIdx].createdAt
|
|
28056
28371
|
} : {
|
|
28057
28372
|
...placement,
|
|
28058
|
-
id: placement.id ||
|
|
28373
|
+
id: placement.id || crypto8.randomUUID(),
|
|
28059
28374
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28060
28375
|
};
|
|
28061
28376
|
const next = isUpdate ? placements.map((p, i) => i === existingIdx ? stored : p) : [...placements, stored];
|
|
@@ -28136,11 +28451,11 @@ function getOrganizationUnit(dataDir, id) {
|
|
|
28136
28451
|
}
|
|
28137
28452
|
|
|
28138
28453
|
// src/server/permissions.ts
|
|
28139
|
-
var
|
|
28140
|
-
var
|
|
28454
|
+
var fs33 = __toESM(require("fs"));
|
|
28455
|
+
var path44 = __toESM(require("path"));
|
|
28141
28456
|
var import_crypto2 = require("crypto");
|
|
28142
28457
|
function storePath7(dataDir) {
|
|
28143
|
-
return
|
|
28458
|
+
return path44.join(dataDir, "permissions.json");
|
|
28144
28459
|
}
|
|
28145
28460
|
function assignmentKey(a) {
|
|
28146
28461
|
return [a.subjectKind, a.subjectId ?? "", a.scopeKind, a.scopeId ?? "", a.capability].join("|");
|
|
@@ -28149,7 +28464,7 @@ function load4(dataDir) {
|
|
|
28149
28464
|
const p = storePath7(dataDir);
|
|
28150
28465
|
let raw;
|
|
28151
28466
|
try {
|
|
28152
|
-
raw =
|
|
28467
|
+
raw = fs33.readFileSync(p, "utf8");
|
|
28153
28468
|
} catch (err) {
|
|
28154
28469
|
if (err.code === "ENOENT") return [];
|
|
28155
28470
|
throw new Error(`Cannot read permission store at ${p}: ${err.message}`);
|
|
@@ -28167,10 +28482,10 @@ function load4(dataDir) {
|
|
|
28167
28482
|
}
|
|
28168
28483
|
function replaceAll2(dataDir, assignments) {
|
|
28169
28484
|
const p = storePath7(dataDir);
|
|
28170
|
-
|
|
28485
|
+
fs33.mkdirSync(path44.dirname(p), { recursive: true });
|
|
28171
28486
|
const tmp = `${p}.tmp`;
|
|
28172
|
-
|
|
28173
|
-
|
|
28487
|
+
fs33.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
|
|
28488
|
+
fs33.renameSync(tmp, p);
|
|
28174
28489
|
}
|
|
28175
28490
|
function registrySet(dataDir, assignment) {
|
|
28176
28491
|
const assignments = load4(dataDir);
|
|
@@ -28256,8 +28571,8 @@ function getAssignment(dataDir, assignmentId) {
|
|
|
28256
28571
|
}
|
|
28257
28572
|
|
|
28258
28573
|
// src/server/roles.ts
|
|
28259
|
-
var
|
|
28260
|
-
var
|
|
28574
|
+
var fs34 = __toESM(require("fs"));
|
|
28575
|
+
var path45 = __toESM(require("path"));
|
|
28261
28576
|
var BUILTIN_ROLES = [
|
|
28262
28577
|
{
|
|
28263
28578
|
id: SSO_ADMIN_ROLE_ID,
|
|
@@ -28275,13 +28590,13 @@ function isBuiltinRoleId(roleId) {
|
|
|
28275
28590
|
return BUILTIN_ROLE_IDS.has(roleId);
|
|
28276
28591
|
}
|
|
28277
28592
|
function storePath8(dataDir) {
|
|
28278
|
-
return
|
|
28593
|
+
return path45.join(dataDir, "roles.json");
|
|
28279
28594
|
}
|
|
28280
28595
|
function load5(dataDir) {
|
|
28281
28596
|
const p = storePath8(dataDir);
|
|
28282
28597
|
let raw;
|
|
28283
28598
|
try {
|
|
28284
|
-
raw =
|
|
28599
|
+
raw = fs34.readFileSync(p, "utf8");
|
|
28285
28600
|
} catch (err) {
|
|
28286
28601
|
if (err.code === "ENOENT") return [];
|
|
28287
28602
|
throw new Error(`Cannot read role store at ${p}: ${err.message}`);
|
|
@@ -28299,10 +28614,10 @@ function load5(dataDir) {
|
|
|
28299
28614
|
}
|
|
28300
28615
|
function replaceAll3(dataDir, roles) {
|
|
28301
28616
|
const p = storePath8(dataDir);
|
|
28302
|
-
|
|
28617
|
+
fs34.mkdirSync(path45.dirname(p), { recursive: true });
|
|
28303
28618
|
const tmp = `${p}.tmp`;
|
|
28304
|
-
|
|
28305
|
-
|
|
28619
|
+
fs34.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
|
|
28620
|
+
fs34.renameSync(tmp, p);
|
|
28306
28621
|
}
|
|
28307
28622
|
function registryCreate(dataDir, role) {
|
|
28308
28623
|
if (isBuiltinRoleId(role.id)) {
|
|
@@ -28536,8 +28851,8 @@ function actionableUnitIds(scopes) {
|
|
|
28536
28851
|
}
|
|
28537
28852
|
|
|
28538
28853
|
// src/server/projects.ts
|
|
28539
|
-
var
|
|
28540
|
-
var
|
|
28854
|
+
var fs38 = __toESM(require("fs"));
|
|
28855
|
+
var path49 = __toESM(require("path"));
|
|
28541
28856
|
init_loader();
|
|
28542
28857
|
init_yaml();
|
|
28543
28858
|
init_fs();
|
|
@@ -28556,37 +28871,37 @@ init_types();
|
|
|
28556
28871
|
init_server();
|
|
28557
28872
|
|
|
28558
28873
|
// src/git/config.ts
|
|
28559
|
-
var
|
|
28560
|
-
var
|
|
28874
|
+
var fs35 = __toESM(require("fs"));
|
|
28875
|
+
var path46 = __toESM(require("path"));
|
|
28561
28876
|
init_fs();
|
|
28562
28877
|
function configPath() {
|
|
28563
28878
|
return aiDir("git.json");
|
|
28564
28879
|
}
|
|
28565
28880
|
function readGitConfig() {
|
|
28566
28881
|
try {
|
|
28567
|
-
return JSON.parse(
|
|
28882
|
+
return JSON.parse(fs35.readFileSync(configPath(), "utf8"));
|
|
28568
28883
|
} catch {
|
|
28569
28884
|
return null;
|
|
28570
28885
|
}
|
|
28571
28886
|
}
|
|
28572
28887
|
function writeGitConfig(config) {
|
|
28573
28888
|
const p = configPath();
|
|
28574
|
-
|
|
28889
|
+
fs35.mkdirSync(path46.dirname(p), { recursive: true });
|
|
28575
28890
|
const tmp = `${p}.tmp`;
|
|
28576
|
-
|
|
28577
|
-
|
|
28891
|
+
fs35.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
|
|
28892
|
+
fs35.renameSync(tmp, p);
|
|
28578
28893
|
}
|
|
28579
28894
|
function clearGitConfig() {
|
|
28580
28895
|
try {
|
|
28581
|
-
|
|
28896
|
+
fs35.rmSync(configPath(), { force: true });
|
|
28582
28897
|
} catch {
|
|
28583
28898
|
}
|
|
28584
28899
|
}
|
|
28585
28900
|
|
|
28586
28901
|
// src/git/adapter.ts
|
|
28587
28902
|
var import_child_process3 = require("child_process");
|
|
28588
|
-
var
|
|
28589
|
-
var
|
|
28903
|
+
var fs36 = __toESM(require("fs"));
|
|
28904
|
+
var path47 = __toESM(require("path"));
|
|
28590
28905
|
init_fs();
|
|
28591
28906
|
function git(args, cwd) {
|
|
28592
28907
|
return (0, import_child_process3.execFileSync)("git", args, {
|
|
@@ -28638,10 +28953,10 @@ function compareUrl(remote, defaultBranch, workingBranch) {
|
|
|
28638
28953
|
return `${web}/compare/${encodeURIComponent(defaultBranch)}...${encodeURIComponent(workingBranch)}`;
|
|
28639
28954
|
}
|
|
28640
28955
|
function excludeLocalFiles() {
|
|
28641
|
-
const excludePath =
|
|
28956
|
+
const excludePath = path47.join(getProjectRoot(), ".git", "info", "exclude");
|
|
28642
28957
|
try {
|
|
28643
|
-
|
|
28644
|
-
|
|
28958
|
+
fs36.mkdirSync(path47.dirname(excludePath), { recursive: true });
|
|
28959
|
+
fs36.appendFileSync(excludePath, "\n.wai/lock.json\n.wai/git.json\n");
|
|
28645
28960
|
} catch {
|
|
28646
28961
|
}
|
|
28647
28962
|
}
|
|
@@ -28706,25 +29021,25 @@ function configureSync(periodicSyncMinutes, skipIfClean) {
|
|
|
28706
29021
|
}
|
|
28707
29022
|
|
|
28708
29023
|
// src/producers/config.ts
|
|
28709
|
-
var
|
|
28710
|
-
var
|
|
29024
|
+
var fs37 = __toESM(require("fs"));
|
|
29025
|
+
var path48 = __toESM(require("path"));
|
|
28711
29026
|
init_fs();
|
|
28712
29027
|
function configPath2() {
|
|
28713
29028
|
return aiDir("producers.json");
|
|
28714
29029
|
}
|
|
28715
29030
|
function load6() {
|
|
28716
29031
|
try {
|
|
28717
|
-
return JSON.parse(
|
|
29032
|
+
return JSON.parse(fs37.readFileSync(configPath2(), "utf8"));
|
|
28718
29033
|
} catch {
|
|
28719
29034
|
return [];
|
|
28720
29035
|
}
|
|
28721
29036
|
}
|
|
28722
29037
|
function save2(configs) {
|
|
28723
29038
|
const p = configPath2();
|
|
28724
|
-
|
|
29039
|
+
fs37.mkdirSync(path48.dirname(p), { recursive: true });
|
|
28725
29040
|
const tmp = `${p}.tmp`;
|
|
28726
|
-
|
|
28727
|
-
|
|
29041
|
+
fs37.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
|
|
29042
|
+
fs37.renameSync(tmp, p);
|
|
28728
29043
|
}
|
|
28729
29044
|
function readProducerConfig(target) {
|
|
28730
29045
|
return load6().find((c) => c.target === target) ?? null;
|
|
@@ -29142,24 +29457,24 @@ function isValidProjectId(id) {
|
|
|
29142
29457
|
return typeof id === "string" && ID_RE.test(id);
|
|
29143
29458
|
}
|
|
29144
29459
|
function registryPath(dataDir) {
|
|
29145
|
-
return
|
|
29460
|
+
return path49.join(dataDir, "projects.json");
|
|
29146
29461
|
}
|
|
29147
29462
|
function load7(dataDir) {
|
|
29148
29463
|
try {
|
|
29149
|
-
return JSON.parse(
|
|
29464
|
+
return JSON.parse(fs38.readFileSync(registryPath(dataDir), "utf8"));
|
|
29150
29465
|
} catch {
|
|
29151
29466
|
return [];
|
|
29152
29467
|
}
|
|
29153
29468
|
}
|
|
29154
29469
|
function save3(dataDir, records) {
|
|
29155
29470
|
const p = registryPath(dataDir);
|
|
29156
|
-
|
|
29471
|
+
fs38.mkdirSync(path49.dirname(p), { recursive: true });
|
|
29157
29472
|
const tmp = `${p}.tmp`;
|
|
29158
|
-
|
|
29159
|
-
|
|
29473
|
+
fs38.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
29474
|
+
fs38.renameSync(tmp, p);
|
|
29160
29475
|
}
|
|
29161
29476
|
function projectRoot(dataDir, id) {
|
|
29162
|
-
return
|
|
29477
|
+
return path49.join(dataDir, "projects", id);
|
|
29163
29478
|
}
|
|
29164
29479
|
function existingProjectRoot(dataDir, id) {
|
|
29165
29480
|
if (!isValidProjectId(id)) return null;
|
|
@@ -29175,7 +29490,7 @@ function createProjectRecord(dataDir, id) {
|
|
|
29175
29490
|
throw new Error(`Project "${id}" already exists.`);
|
|
29176
29491
|
}
|
|
29177
29492
|
const root = projectRoot(dataDir, id);
|
|
29178
|
-
|
|
29493
|
+
fs38.mkdirSync(root, { recursive: true });
|
|
29179
29494
|
const record2 = {
|
|
29180
29495
|
id,
|
|
29181
29496
|
rootPath: root,
|
|
@@ -29210,7 +29525,7 @@ function removeProjectRecord(dataDir, id) {
|
|
|
29210
29525
|
const rec = records.find((r) => r.id === id);
|
|
29211
29526
|
if (rec) {
|
|
29212
29527
|
try {
|
|
29213
|
-
|
|
29528
|
+
fs38.rmSync(rec.rootPath, { recursive: true, force: true });
|
|
29214
29529
|
} catch {
|
|
29215
29530
|
}
|
|
29216
29531
|
}
|
|
@@ -29226,7 +29541,7 @@ function parseQualifiedSelector(value) {
|
|
|
29226
29541
|
}
|
|
29227
29542
|
function findSubsystemSpec(root, subsystemId) {
|
|
29228
29543
|
const specsDir = aiPathsAt(root).specsDir();
|
|
29229
|
-
if (!
|
|
29544
|
+
if (!fs38.existsSync(specsDir)) return null;
|
|
29230
29545
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
29231
29546
|
let raw;
|
|
29232
29547
|
try {
|
|
@@ -29404,9 +29719,9 @@ function mintKey(cfg, credential, project2, role) {
|
|
|
29404
29719
|
"instance-wide super-admin (*:*) keys cannot be minted \u2014 the built-in admin account (WAIRON_ADMIN_USER) is the only super-admin"
|
|
29405
29720
|
);
|
|
29406
29721
|
}
|
|
29407
|
-
const token = "wk_" +
|
|
29722
|
+
const token = "wk_" + crypto9.randomBytes(24).toString("hex");
|
|
29408
29723
|
const record2 = {
|
|
29409
|
-
id:
|
|
29724
|
+
id: crypto9.randomBytes(6).toString("hex"),
|
|
29410
29725
|
keyHash: hashToken(token),
|
|
29411
29726
|
role,
|
|
29412
29727
|
projects: project2 === "*" ? ["*"] : [project2],
|
|
@@ -29602,31 +29917,10 @@ function diagramViewLink(cfg, credential, project2) {
|
|
|
29602
29917
|
if (!existingProjectRoot(cfg.dataDir, project2)) throw new Error(`Unknown project "${project2}".`);
|
|
29603
29918
|
return `/view/diagram?token=${signViewToken(project2, "canvas")}`;
|
|
29604
29919
|
}
|
|
29605
|
-
function promoteProject(cfg, credential, project2) {
|
|
29606
|
-
const principal = requirePrincipal(cfg, credential);
|
|
29607
|
-
if (authorize(cfg.dataDir, principal, "project:write", "project", project2).value !== "yes") {
|
|
29608
|
-
throw new AdminAuthError("Forbidden \u2014 promoting a project requires project:write over it");
|
|
29609
|
-
}
|
|
29610
|
-
return executeApprovedPromote(cfg, project2);
|
|
29611
|
-
}
|
|
29612
|
-
function executeApprovedPromote(cfg, projectId, subproject) {
|
|
29613
|
-
const root = boundLifecycleRoot(cfg, projectId, subproject);
|
|
29614
|
-
return runWithProjectRoot(root, () => {
|
|
29615
|
-
const { state, record: lock, current: current2 } = hostCore.readLockState();
|
|
29616
|
-
if (state === "unlocked" || !lock) {
|
|
29617
|
-
return { status: "not-locked", message: "Project is not locked; run lock first." };
|
|
29618
|
-
}
|
|
29619
|
-
if (state === "stale") {
|
|
29620
|
-
return { status: "stale", stateId: current2, message: "Spec tree or governing doctrine changed since lock; re-lock required." };
|
|
29621
|
-
}
|
|
29622
|
-
hostCore.writeLockRecord({ ...lock, status: "promoted" });
|
|
29623
|
-
return { status: "ready", stateId: current2, message: "Locked state matches; change-set marked ready for promotion." };
|
|
29624
|
-
});
|
|
29625
|
-
}
|
|
29626
29920
|
|
|
29627
29921
|
// src/server/packs.ts
|
|
29628
|
-
var
|
|
29629
|
-
var
|
|
29922
|
+
var fs39 = __toESM(require("fs"));
|
|
29923
|
+
var path50 = __toESM(require("path"));
|
|
29630
29924
|
init_fs();
|
|
29631
29925
|
init_yaml();
|
|
29632
29926
|
init_loader();
|
|
@@ -29672,19 +29966,19 @@ function assertNotCodeArchive(info) {
|
|
|
29672
29966
|
function extractDeclarativeArchive(archive, destDir) {
|
|
29673
29967
|
const result = hostSdk.extractArchive(archive, destDir, HOSTED_STRICT_LIMITS);
|
|
29674
29968
|
try {
|
|
29675
|
-
assertDeclarative(
|
|
29969
|
+
assertDeclarative(fs39.readFileSync(path50.join(result.directory, result.entryPath), "utf8"));
|
|
29676
29970
|
} catch (e) {
|
|
29677
|
-
|
|
29971
|
+
fs39.rmSync(destDir, { recursive: true, force: true });
|
|
29678
29972
|
throw e;
|
|
29679
29973
|
}
|
|
29680
29974
|
}
|
|
29681
29975
|
function probe(loadRef, baseRoot, scope, displayRef) {
|
|
29682
29976
|
const loaded = hostCore.loadExtensionPacks([{ ref: loadRef, scope }], baseRoot);
|
|
29683
29977
|
if (loaded.errors.length) {
|
|
29684
|
-
return { name:
|
|
29978
|
+
return { name: path50.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
|
|
29685
29979
|
}
|
|
29686
29980
|
return {
|
|
29687
|
-
name: loaded.packNames[0] ??
|
|
29981
|
+
name: loaded.packNames[0] ?? path50.basename(displayRef),
|
|
29688
29982
|
scope,
|
|
29689
29983
|
ref: displayRef,
|
|
29690
29984
|
profiles: Object.keys(loaded.profiles).length,
|
|
@@ -29698,20 +29992,20 @@ function probe(loadRef, baseRoot, scope, displayRef) {
|
|
|
29698
29992
|
};
|
|
29699
29993
|
}
|
|
29700
29994
|
function writeFileAtomic(file, content) {
|
|
29701
|
-
|
|
29995
|
+
fs39.mkdirSync(path50.dirname(file), { recursive: true });
|
|
29702
29996
|
const tmp = `${file}.tmp`;
|
|
29703
|
-
|
|
29704
|
-
|
|
29997
|
+
fs39.writeFileSync(tmp, content);
|
|
29998
|
+
fs39.renameSync(tmp, file);
|
|
29705
29999
|
}
|
|
29706
30000
|
function stem(ref) {
|
|
29707
|
-
return
|
|
30001
|
+
return path50.basename(ref).replace(PACK_EXT_RE, "");
|
|
29708
30002
|
}
|
|
29709
30003
|
function imagePacksDir() {
|
|
29710
30004
|
return process.env.WAIRON_IMAGE_PACKS_DIR ?? "/opt/wairon/packs";
|
|
29711
30005
|
}
|
|
29712
30006
|
function probeTier(dir, tier) {
|
|
29713
30007
|
return hostCore.discoverPacks(dir).map((full) => {
|
|
29714
|
-
const d = probe(full,
|
|
30008
|
+
const d = probe(full, path50.dirname(full), "global", path50.basename(full));
|
|
29715
30009
|
d.tier = tier;
|
|
29716
30010
|
return d;
|
|
29717
30011
|
});
|
|
@@ -29729,9 +30023,9 @@ function scanGlobalPackProfiles() {
|
|
|
29729
30023
|
for (const dir of [hostCore.globalPacksDir(), imagePacksDir()]) {
|
|
29730
30024
|
for (const full of hostCore.discoverPacks(dir)) {
|
|
29731
30025
|
try {
|
|
29732
|
-
const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }],
|
|
30026
|
+
const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }], path50.dirname(full));
|
|
29733
30027
|
if (loaded.errors.length) continue;
|
|
29734
|
-
const source = loaded.packNames[0] ??
|
|
30028
|
+
const source = loaded.packNames[0] ?? path50.basename(full);
|
|
29735
30029
|
for (const [id, def] of Object.entries(loaded.profiles)) out.push({ id, source, family: def.family });
|
|
29736
30030
|
} catch {
|
|
29737
30031
|
}
|
|
@@ -29783,12 +30077,12 @@ function storeListProjectProfiles() {
|
|
|
29783
30077
|
return out;
|
|
29784
30078
|
}
|
|
29785
30079
|
function readPackContent(full) {
|
|
29786
|
-
const st =
|
|
29787
|
-
if (st.isFile()) return
|
|
30080
|
+
const st = fs39.statSync(full);
|
|
30081
|
+
if (st.isFile()) return fs39.readFileSync(full, "utf8");
|
|
29788
30082
|
if (st.isDirectory()) {
|
|
29789
30083
|
for (const entry of ["pack.yaml", "pack.yml"]) {
|
|
29790
|
-
const p =
|
|
29791
|
-
if (
|
|
30084
|
+
const p = path50.join(full, entry);
|
|
30085
|
+
if (fs39.existsSync(p) && fs39.statSync(p).isFile()) return fs39.readFileSync(p, "utf8");
|
|
29792
30086
|
}
|
|
29793
30087
|
}
|
|
29794
30088
|
return null;
|
|
@@ -29797,9 +30091,9 @@ function storeResolveGlobalPacks(names) {
|
|
|
29797
30091
|
const index = /* @__PURE__ */ new Map();
|
|
29798
30092
|
const indexTier = (dir, tier) => {
|
|
29799
30093
|
for (const full of hostCore.discoverPacks(dir)) {
|
|
29800
|
-
const manifestName = probe(full,
|
|
30094
|
+
const manifestName = probe(full, path50.dirname(full), "global", path50.basename(full)).name;
|
|
29801
30095
|
const candidate = { full, manifestName, tier };
|
|
29802
|
-
for (const key of [manifestName, stem(full),
|
|
30096
|
+
for (const key of [manifestName, stem(full), path50.basename(full)]) {
|
|
29803
30097
|
if (!index.has(key)) index.set(key, candidate);
|
|
29804
30098
|
}
|
|
29805
30099
|
}
|
|
@@ -29826,7 +30120,7 @@ function storeInstallGlobalPack(name, content) {
|
|
|
29826
30120
|
assertName(name);
|
|
29827
30121
|
assertDeclarative(content);
|
|
29828
30122
|
const dir = hostCore.globalPacksDir();
|
|
29829
|
-
const file =
|
|
30123
|
+
const file = path50.join(dir, `${name}.yaml`);
|
|
29830
30124
|
writeFileAtomic(file, content);
|
|
29831
30125
|
const descriptor = probe(file, dir, "global", `${name}.yaml`);
|
|
29832
30126
|
descriptor.tier = "instance";
|
|
@@ -29837,7 +30131,7 @@ function storeInstallGlobalPackArchive(archive, name) {
|
|
|
29837
30131
|
assertNotCodeArchive(info);
|
|
29838
30132
|
const packName = name ?? info.name;
|
|
29839
30133
|
assertName(packName);
|
|
29840
|
-
const dir =
|
|
30134
|
+
const dir = path50.join(hostCore.globalPacksDir(), packName);
|
|
29841
30135
|
extractDeclarativeArchive(archive, dir);
|
|
29842
30136
|
const descriptor = probe(dir, hostCore.globalPacksDir(), "global", packName);
|
|
29843
30137
|
descriptor.tier = "instance";
|
|
@@ -29846,16 +30140,16 @@ function storeInstallGlobalPackArchive(archive, name) {
|
|
|
29846
30140
|
function storeRemoveGlobalPack(name) {
|
|
29847
30141
|
assertName(name);
|
|
29848
30142
|
const dir = hostCore.globalPacksDir();
|
|
29849
|
-
const match = hostCore.discoverPacks(dir).find((ref) => stem(ref) === name ||
|
|
30143
|
+
const match = hostCore.discoverPacks(dir).find((ref) => stem(ref) === name || path50.basename(ref) === name);
|
|
29850
30144
|
if (!match) {
|
|
29851
30145
|
const inImage = hostCore.discoverPacks(imagePacksDir()).some(
|
|
29852
|
-
(ref) => stem(ref) === name ||
|
|
30146
|
+
(ref) => stem(ref) === name || path50.basename(ref) === name
|
|
29853
30147
|
);
|
|
29854
30148
|
throw new Error(
|
|
29855
30149
|
inImage ? `Pack "${name}" is an immutable image-layer pack (WAIRON_IMAGE_PACKS_DIR) and cannot be removed at runtime; rebuild the extension image without it.` : `No server-global instance pack named "${name}".`
|
|
29856
30150
|
);
|
|
29857
30151
|
}
|
|
29858
|
-
|
|
30152
|
+
fs39.rmSync(match, { recursive: true, force: true });
|
|
29859
30153
|
}
|
|
29860
30154
|
function storeListProjectPacks() {
|
|
29861
30155
|
const root = getProjectRoot();
|
|
@@ -29871,7 +30165,7 @@ function storeInstallProjectPack(name, content) {
|
|
|
29871
30165
|
assertDeclarative(content);
|
|
29872
30166
|
const root = getProjectRoot();
|
|
29873
30167
|
const relRef = `.wai/packs/${name}.yaml`;
|
|
29874
|
-
writeFileAtomic(
|
|
30168
|
+
writeFileAtomic(path50.join(root, ".wai", "packs", `${name}.yaml`), content);
|
|
29875
30169
|
const config = loadProjectConfig();
|
|
29876
30170
|
const packs = config.extensions?.packs ?? [];
|
|
29877
30171
|
if (!packs.includes(relRef)) {
|
|
@@ -29887,7 +30181,7 @@ function storeInstallProjectPackArchive(archive, name) {
|
|
|
29887
30181
|
assertName(packName);
|
|
29888
30182
|
const root = getProjectRoot();
|
|
29889
30183
|
const relRef = `.wai/packs/${packName}`;
|
|
29890
|
-
extractDeclarativeArchive(archive,
|
|
30184
|
+
extractDeclarativeArchive(archive, path50.join(root, ".wai", "packs", packName));
|
|
29891
30185
|
const config = loadProjectConfig();
|
|
29892
30186
|
const packs = config.extensions?.packs ?? [];
|
|
29893
30187
|
if (!packs.includes(relRef)) {
|
|
@@ -29902,18 +30196,18 @@ function storeRemoveProjectPack(name) {
|
|
|
29902
30196
|
const config = loadProjectConfig();
|
|
29903
30197
|
const packs = config.extensions?.packs ?? [];
|
|
29904
30198
|
const relRef = `.wai/packs/${name}.yaml`;
|
|
29905
|
-
const match = packs.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name ||
|
|
30199
|
+
const match = packs.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name || path50.basename(entry) === name : entry.name === name);
|
|
29906
30200
|
if (!match) throw new Error(`Project has no registered pack named "${name}".`);
|
|
29907
30201
|
config.extensions = { packs: packs.filter((entry) => entry !== match), useGlobalPacks: hostCore.globalPacksEnabled(config) };
|
|
29908
30202
|
saveProjectConfig(config);
|
|
29909
|
-
const resolved =
|
|
29910
|
-
const vendorDir =
|
|
29911
|
-
if (resolved.startsWith(vendorDir +
|
|
29912
|
-
|
|
30203
|
+
const resolved = path50.resolve(root, typeof match === "string" ? match : path50.join(".wai", "packs", match.name));
|
|
30204
|
+
const vendorDir = path50.resolve(root, ".wai", "packs");
|
|
30205
|
+
if (resolved.startsWith(vendorDir + path50.sep)) {
|
|
30206
|
+
fs39.rmSync(resolved, { recursive: true, force: true });
|
|
29913
30207
|
}
|
|
29914
30208
|
}
|
|
29915
30209
|
function readProjectReferences(rootPath) {
|
|
29916
|
-
const projectId =
|
|
30210
|
+
const projectId = path50.basename(rootPath);
|
|
29917
30211
|
const empty = { projectId, packNames: [] };
|
|
29918
30212
|
try {
|
|
29919
30213
|
return runWithProjectRoot(rootPath, () => {
|
|
@@ -30036,9 +30330,9 @@ function installProjectPackArchive(cfg, credential, project2, archive, name) {
|
|
|
30036
30330
|
}
|
|
30037
30331
|
|
|
30038
30332
|
// src/server/audit.ts
|
|
30039
|
-
var
|
|
30040
|
-
var
|
|
30041
|
-
var
|
|
30333
|
+
var fs40 = __toESM(require("fs"));
|
|
30334
|
+
var path51 = __toESM(require("path"));
|
|
30335
|
+
var crypto10 = __toESM(require("crypto"));
|
|
30042
30336
|
var LEVEL_ORDER = {
|
|
30043
30337
|
debug: 0,
|
|
30044
30338
|
info: 1,
|
|
@@ -30060,13 +30354,13 @@ var DEFAULT_AUDIT_POLICY = {
|
|
|
30060
30354
|
metadataMode: "redacted"
|
|
30061
30355
|
};
|
|
30062
30356
|
function storePath9(dataDir) {
|
|
30063
|
-
return
|
|
30357
|
+
return path51.join(dataDir, "audit-events.json");
|
|
30064
30358
|
}
|
|
30065
30359
|
function readEvents(dataDir) {
|
|
30066
30360
|
const p = storePath9(dataDir);
|
|
30067
30361
|
let raw;
|
|
30068
30362
|
try {
|
|
30069
|
-
raw =
|
|
30363
|
+
raw = fs40.readFileSync(p, "utf8");
|
|
30070
30364
|
} catch (e) {
|
|
30071
30365
|
if (e.code === "ENOENT") return [];
|
|
30072
30366
|
throw new Error(`Failed to read audit store at ${p}: ${e.message}`);
|
|
@@ -30081,10 +30375,10 @@ function readEvents(dataDir) {
|
|
|
30081
30375
|
}
|
|
30082
30376
|
function persistEvents(dataDir, events) {
|
|
30083
30377
|
const p = storePath9(dataDir);
|
|
30084
|
-
|
|
30378
|
+
fs40.mkdirSync(path51.dirname(p), { recursive: true });
|
|
30085
30379
|
const tmp = `${p}.tmp`;
|
|
30086
|
-
|
|
30087
|
-
|
|
30380
|
+
fs40.writeFileSync(tmp, JSON.stringify(events, null, 2) + "\n");
|
|
30381
|
+
fs40.renameSync(tmp, p);
|
|
30088
30382
|
}
|
|
30089
30383
|
var SECRET_RE = /Bearer\s+\S/i;
|
|
30090
30384
|
var LONG_HEX_RE = /[0-9a-fA-F]{32,}/;
|
|
@@ -30142,7 +30436,7 @@ var AuditRegistry = class {
|
|
|
30142
30436
|
}
|
|
30143
30437
|
const stamped = {
|
|
30144
30438
|
...event,
|
|
30145
|
-
id: event.id && event.id.length > 0 ? event.id :
|
|
30439
|
+
id: event.id && event.id.length > 0 ? event.id : crypto10.randomUUID(),
|
|
30146
30440
|
timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : (/* @__PURE__ */ new Date()).toISOString(),
|
|
30147
30441
|
metadata
|
|
30148
30442
|
};
|
|
@@ -30409,10 +30703,10 @@ function unbindRole(cfg, credential, userId, roleId, scopeKind, scopeId) {
|
|
|
30409
30703
|
}
|
|
30410
30704
|
|
|
30411
30705
|
// src/server/identity.ts
|
|
30412
|
-
var
|
|
30706
|
+
var crypto12 = __toESM(require("crypto"));
|
|
30413
30707
|
|
|
30414
30708
|
// src/server/idp.ts
|
|
30415
|
-
var
|
|
30709
|
+
var crypto11 = __toESM(require("crypto"));
|
|
30416
30710
|
var REQUESTED_SCOPE = "openid email profile";
|
|
30417
30711
|
var discoveryCache = /* @__PURE__ */ new Map();
|
|
30418
30712
|
var jwksCache = /* @__PURE__ */ new Map();
|
|
@@ -30639,7 +30933,7 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
|
|
|
30639
30933
|
if (!digest) return false;
|
|
30640
30934
|
let keyObject;
|
|
30641
30935
|
try {
|
|
30642
|
-
keyObject =
|
|
30936
|
+
keyObject = crypto11.createPublicKey({ key: jwk, format: "jwk" });
|
|
30643
30937
|
} catch {
|
|
30644
30938
|
return false;
|
|
30645
30939
|
}
|
|
@@ -30647,18 +30941,18 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
|
|
|
30647
30941
|
const sig = Buffer.from(sigB64url, "base64url");
|
|
30648
30942
|
try {
|
|
30649
30943
|
if (alg.startsWith("RS")) {
|
|
30650
|
-
return
|
|
30944
|
+
return crypto11.verify(digest, data, keyObject, sig);
|
|
30651
30945
|
}
|
|
30652
30946
|
if (alg.startsWith("PS")) {
|
|
30653
|
-
return
|
|
30947
|
+
return crypto11.verify(
|
|
30654
30948
|
digest,
|
|
30655
30949
|
data,
|
|
30656
|
-
{ key: keyObject, padding:
|
|
30950
|
+
{ key: keyObject, padding: crypto11.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto11.constants.RSA_PSS_SALTLEN_DIGEST },
|
|
30657
30951
|
sig
|
|
30658
30952
|
);
|
|
30659
30953
|
}
|
|
30660
30954
|
if (alg.startsWith("ES")) {
|
|
30661
|
-
return
|
|
30955
|
+
return crypto11.verify(digest, data, { key: keyObject, dsaEncoding: "ieee-p1363" }, sig);
|
|
30662
30956
|
}
|
|
30663
30957
|
} catch {
|
|
30664
30958
|
return false;
|
|
@@ -30772,8 +31066,8 @@ function assertAllowedRedirectUri(provider, redirectUri) {
|
|
|
30772
31066
|
}
|
|
30773
31067
|
|
|
30774
31068
|
// src/server/policy.ts
|
|
30775
|
-
var
|
|
30776
|
-
var
|
|
31069
|
+
var fs41 = __toESM(require("fs"));
|
|
31070
|
+
var path52 = __toESM(require("path"));
|
|
30777
31071
|
init_fs();
|
|
30778
31072
|
init_yaml();
|
|
30779
31073
|
init_loader();
|
|
@@ -30792,13 +31086,13 @@ function sendJson(res, status2, body) {
|
|
|
30792
31086
|
|
|
30793
31087
|
// src/server/policy.ts
|
|
30794
31088
|
function packPolicyPath(dataDir) {
|
|
30795
|
-
return
|
|
31089
|
+
return path52.join(dataDir, "pack-policy.json");
|
|
30796
31090
|
}
|
|
30797
31091
|
function getPackPolicyRecord(dataDir) {
|
|
30798
31092
|
const p = packPolicyPath(dataDir);
|
|
30799
31093
|
let raw;
|
|
30800
31094
|
try {
|
|
30801
|
-
raw =
|
|
31095
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30802
31096
|
} catch (e) {
|
|
30803
31097
|
if (e.code === "ENOENT") return null;
|
|
30804
31098
|
throw new Error(`Failed to read pack policy store at ${p}: ${e.message}`);
|
|
@@ -30812,14 +31106,14 @@ function getPackPolicyRecord(dataDir) {
|
|
|
30812
31106
|
function setPackPolicyRecord(dataDir, policy) {
|
|
30813
31107
|
const stored = { ...policy, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
30814
31108
|
const p = packPolicyPath(dataDir);
|
|
30815
|
-
|
|
31109
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30816
31110
|
const tmp = `${p}.tmp`;
|
|
30817
|
-
|
|
30818
|
-
|
|
31111
|
+
fs41.writeFileSync(tmp, JSON.stringify(stored, null, 2) + "\n");
|
|
31112
|
+
fs41.renameSync(tmp, p);
|
|
30819
31113
|
return stored;
|
|
30820
31114
|
}
|
|
30821
31115
|
function exposurePolicyPath(dataDir) {
|
|
30822
|
-
return
|
|
31116
|
+
return path52.join(dataDir, "exposure-policy.json");
|
|
30823
31117
|
}
|
|
30824
31118
|
var COMPATIBLE_DEFAULT_EXPOSURE = {
|
|
30825
31119
|
adminApiMode: "local_only",
|
|
@@ -30836,7 +31130,7 @@ function getExposurePolicyRecord(dataDir) {
|
|
|
30836
31130
|
const p = exposurePolicyPath(dataDir);
|
|
30837
31131
|
let raw;
|
|
30838
31132
|
try {
|
|
30839
|
-
raw =
|
|
31133
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30840
31134
|
} catch (e) {
|
|
30841
31135
|
if (e.code === "ENOENT") return null;
|
|
30842
31136
|
throw new Error(`Failed to read exposure policy store at ${p}: ${e.message}`);
|
|
@@ -30849,10 +31143,10 @@ function getExposurePolicyRecord(dataDir) {
|
|
|
30849
31143
|
}
|
|
30850
31144
|
function setExposurePolicyRecord(dataDir, policy) {
|
|
30851
31145
|
const p = exposurePolicyPath(dataDir);
|
|
30852
|
-
|
|
31146
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30853
31147
|
const tmp = `${p}.tmp`;
|
|
30854
|
-
|
|
30855
|
-
|
|
31148
|
+
fs41.writeFileSync(tmp, JSON.stringify(policy, null, 2) + "\n");
|
|
31149
|
+
fs41.renameSync(tmp, p);
|
|
30856
31150
|
return policy;
|
|
30857
31151
|
}
|
|
30858
31152
|
var PERMISSIVE_DEFAULT_POLICY = {
|
|
@@ -30870,7 +31164,7 @@ function effectivePolicy(dataDir) {
|
|
|
30870
31164
|
return getPackPolicyRecord(dataDir) ?? PERMISSIVE_DEFAULT_POLICY;
|
|
30871
31165
|
}
|
|
30872
31166
|
function identityProvidersPath(dataDir) {
|
|
30873
|
-
return
|
|
31167
|
+
return path52.join(dataDir, "identity-providers.json");
|
|
30874
31168
|
}
|
|
30875
31169
|
function sanitizeIdentityProvider(config) {
|
|
30876
31170
|
const clean = {
|
|
@@ -30894,16 +31188,16 @@ function sanitizeIdentityProvider(config) {
|
|
|
30894
31188
|
}
|
|
30895
31189
|
function writeIdentityProviderRecords(dataDir, records) {
|
|
30896
31190
|
const p = identityProvidersPath(dataDir);
|
|
30897
|
-
|
|
31191
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30898
31192
|
const tmp = `${p}.tmp`;
|
|
30899
|
-
|
|
30900
|
-
|
|
31193
|
+
fs41.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
31194
|
+
fs41.renameSync(tmp, p);
|
|
30901
31195
|
}
|
|
30902
31196
|
function listIdentityProviderRecords(dataDir) {
|
|
30903
31197
|
const p = identityProvidersPath(dataDir);
|
|
30904
31198
|
let raw;
|
|
30905
31199
|
try {
|
|
30906
|
-
raw =
|
|
31200
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30907
31201
|
} catch (e) {
|
|
30908
31202
|
if (e.code === "ENOENT") return [];
|
|
30909
31203
|
throw new Error(`Failed to read identity provider store at ${p}: ${e.message}`);
|
|
@@ -31530,9 +31824,9 @@ function mintToken(cfg, credential, request) {
|
|
|
31530
31824
|
if (owner && owner.status !== "active") {
|
|
31531
31825
|
throw new ForbiddenError("cannot mint a token for a deactivated user");
|
|
31532
31826
|
}
|
|
31533
|
-
const token = "wk_" +
|
|
31827
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31534
31828
|
const record2 = {
|
|
31535
|
-
id:
|
|
31829
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31536
31830
|
keyHash: hashToken(token),
|
|
31537
31831
|
projects,
|
|
31538
31832
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31576,10 +31870,10 @@ function mintSelfToken(cfg, credential, projectId, write) {
|
|
|
31576
31870
|
if (parsed.mounts.length > 0) {
|
|
31577
31871
|
assertMintableNarrowingEntry(cfg.dataDir, projectId);
|
|
31578
31872
|
}
|
|
31579
|
-
const token = "wk_" +
|
|
31873
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31580
31874
|
const owner = auditActor(principal);
|
|
31581
31875
|
const record2 = {
|
|
31582
|
-
id:
|
|
31876
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31583
31877
|
keyHash: hashToken(token),
|
|
31584
31878
|
projects: [projectId],
|
|
31585
31879
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31694,7 +31988,7 @@ function resolveEnabledProvider(cfg, providerId) {
|
|
|
31694
31988
|
}
|
|
31695
31989
|
async function startSsoLogin(cfg, providerId, redirectUri) {
|
|
31696
31990
|
const provider = resolveEnabledProvider(cfg, providerId);
|
|
31697
|
-
const nonce =
|
|
31991
|
+
const nonce = crypto12.randomBytes(16).toString("hex");
|
|
31698
31992
|
const payload = { providerId, nonce, redirectUri };
|
|
31699
31993
|
const state = signSsoState(JSON.stringify(payload));
|
|
31700
31994
|
const endpoints = await resolveEndpoints(provider);
|
|
@@ -31745,9 +32039,9 @@ async function completeSsoLogin(cfg, state, code) {
|
|
|
31745
32039
|
...subject.email ? { email: subject.email } : {}
|
|
31746
32040
|
});
|
|
31747
32041
|
}
|
|
31748
|
-
const token = "wk_" +
|
|
32042
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31749
32043
|
const record2 = {
|
|
31750
|
-
id:
|
|
32044
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31751
32045
|
keyHash: hashToken(token),
|
|
31752
32046
|
projects: ["*"],
|
|
31753
32047
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31879,17 +32173,17 @@ init_yaml();
|
|
|
31879
32173
|
init_loader();
|
|
31880
32174
|
|
|
31881
32175
|
// src/server/relations.ts
|
|
31882
|
-
var
|
|
31883
|
-
var
|
|
31884
|
-
var
|
|
32176
|
+
var fs42 = __toESM(require("fs"));
|
|
32177
|
+
var path53 = __toESM(require("path"));
|
|
32178
|
+
var crypto13 = __toESM(require("crypto"));
|
|
31885
32179
|
function storePath10(dataDir) {
|
|
31886
|
-
return
|
|
32180
|
+
return path53.join(dataDir, "relations.json");
|
|
31887
32181
|
}
|
|
31888
32182
|
function readRelations(dataDir) {
|
|
31889
32183
|
const p = storePath10(dataDir);
|
|
31890
32184
|
let raw;
|
|
31891
32185
|
try {
|
|
31892
|
-
raw =
|
|
32186
|
+
raw = fs42.readFileSync(p, "utf8");
|
|
31893
32187
|
} catch (e) {
|
|
31894
32188
|
if (e.code === "ENOENT") return [];
|
|
31895
32189
|
throw new Error(`Failed to read relation store at ${p}: ${e.message}`);
|
|
@@ -31904,10 +32198,10 @@ function readRelations(dataDir) {
|
|
|
31904
32198
|
}
|
|
31905
32199
|
function persistRelations(dataDir, relations) {
|
|
31906
32200
|
const p = storePath10(dataDir);
|
|
31907
|
-
|
|
32201
|
+
fs42.mkdirSync(path53.dirname(p), { recursive: true });
|
|
31908
32202
|
const tmp = `${p}.tmp`;
|
|
31909
|
-
|
|
31910
|
-
|
|
32203
|
+
fs42.writeFileSync(tmp, JSON.stringify(relations, null, 2) + "\n");
|
|
32204
|
+
fs42.renameSync(tmp, p);
|
|
31911
32205
|
}
|
|
31912
32206
|
var ProjectRelationStore = class {
|
|
31913
32207
|
constructor(dataDir) {
|
|
@@ -31950,7 +32244,7 @@ var ProjectRelationRegistry = class {
|
|
|
31950
32244
|
const hasStatus = typeof record2.status === "string" && record2.status.trim().length > 0;
|
|
31951
32245
|
const stored = {
|
|
31952
32246
|
...record2,
|
|
31953
|
-
id: hasId ? record2.id :
|
|
32247
|
+
id: hasId ? record2.id : crypto13.randomUUID(),
|
|
31954
32248
|
status: hasStatus ? record2.status : "active"
|
|
31955
32249
|
};
|
|
31956
32250
|
const relations = this.store.all();
|
|
@@ -32017,16 +32311,16 @@ function listProjectRelations(dataDir, sourceProjectId, targetProjectId, status2
|
|
|
32017
32311
|
}
|
|
32018
32312
|
|
|
32019
32313
|
// src/server/surfaces.ts
|
|
32020
|
-
var
|
|
32021
|
-
var
|
|
32314
|
+
var fs43 = __toESM(require("fs"));
|
|
32315
|
+
var path54 = __toESM(require("path"));
|
|
32022
32316
|
function storePath11(dataDir) {
|
|
32023
|
-
return
|
|
32317
|
+
return path54.join(dataDir, "public-surfaces.json");
|
|
32024
32318
|
}
|
|
32025
32319
|
function readSnapshots(dataDir) {
|
|
32026
32320
|
const p = storePath11(dataDir);
|
|
32027
32321
|
let raw;
|
|
32028
32322
|
try {
|
|
32029
|
-
raw =
|
|
32323
|
+
raw = fs43.readFileSync(p, "utf8");
|
|
32030
32324
|
} catch (e) {
|
|
32031
32325
|
if (e.code === "ENOENT") return [];
|
|
32032
32326
|
throw new Error(`Failed to read public surface store at ${p}: ${e.message}`);
|
|
@@ -32041,10 +32335,10 @@ function readSnapshots(dataDir) {
|
|
|
32041
32335
|
}
|
|
32042
32336
|
function persistSnapshots(dataDir, snapshots) {
|
|
32043
32337
|
const p = storePath11(dataDir);
|
|
32044
|
-
|
|
32338
|
+
fs43.mkdirSync(path54.dirname(p), { recursive: true });
|
|
32045
32339
|
const tmp = `${p}.tmp`;
|
|
32046
|
-
|
|
32047
|
-
|
|
32340
|
+
fs43.writeFileSync(tmp, JSON.stringify(snapshots, null, 2) + "\n");
|
|
32341
|
+
fs43.renameSync(tmp, p);
|
|
32048
32342
|
}
|
|
32049
32343
|
var PublicSurfaceStore = class {
|
|
32050
32344
|
constructor(dataDir) {
|
|
@@ -32187,12 +32481,12 @@ function resolveVisibility(observerProjectId, units, placements) {
|
|
|
32187
32481
|
const best = /* @__PURE__ */ new Map();
|
|
32188
32482
|
for (const placement of placements) {
|
|
32189
32483
|
if (placement.projectId === observerProjectId) continue;
|
|
32190
|
-
const
|
|
32191
|
-
if (!
|
|
32192
|
-
const closedOk =
|
|
32484
|
+
const path67 = chainOf(placement.unitId, unitById);
|
|
32485
|
+
if (!path67.length) continue;
|
|
32486
|
+
const closedOk = path67.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
|
|
32193
32487
|
if (!closedOk) continue;
|
|
32194
|
-
const crossTenant = !tenantRoots.has(
|
|
32195
|
-
if (crossTenant && !
|
|
32488
|
+
const crossTenant = !tenantRoots.has(path67[path67.length - 1].id);
|
|
32489
|
+
if (crossTenant && !path67.some(grantedTo)) continue;
|
|
32196
32490
|
if (crossTenant && directUnits.length === 0) continue;
|
|
32197
32491
|
const distance = crossTenant ? "partner" : sameBranch(placement.unitId) ? "department" : "instance";
|
|
32198
32492
|
const existing = best.get(placement.projectId);
|
|
@@ -32838,8 +33132,8 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
|
|
|
32838
33132
|
}
|
|
32839
33133
|
|
|
32840
33134
|
// src/server/migration.ts
|
|
32841
|
-
var
|
|
32842
|
-
var
|
|
33135
|
+
var fs44 = __toESM(require("fs"));
|
|
33136
|
+
var path55 = __toESM(require("path"));
|
|
32843
33137
|
var LEGACY_CAPABILITY_MAP = {
|
|
32844
33138
|
"mcp:read": "project:read",
|
|
32845
33139
|
"operations:read": "project:read",
|
|
@@ -32871,16 +33165,16 @@ function scopeOf(grant) {
|
|
|
32871
33165
|
}
|
|
32872
33166
|
function readRaw(file) {
|
|
32873
33167
|
try {
|
|
32874
|
-
return JSON.parse(
|
|
33168
|
+
return JSON.parse(fs44.readFileSync(file, "utf8"));
|
|
32875
33169
|
} catch {
|
|
32876
33170
|
return null;
|
|
32877
33171
|
}
|
|
32878
33172
|
}
|
|
32879
33173
|
function writeRaw(file, value) {
|
|
32880
|
-
|
|
33174
|
+
fs44.mkdirSync(path55.dirname(file), { recursive: true });
|
|
32881
33175
|
const tmp = `${file}.tmp`;
|
|
32882
|
-
|
|
32883
|
-
|
|
33176
|
+
fs44.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
|
|
33177
|
+
fs44.renameSync(tmp, file);
|
|
32884
33178
|
}
|
|
32885
33179
|
function slugify(name) {
|
|
32886
33180
|
return name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "unit";
|
|
@@ -32914,13 +33208,13 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
32914
33208
|
findings.push({ area, detail });
|
|
32915
33209
|
};
|
|
32916
33210
|
const grid = readRaw(
|
|
32917
|
-
|
|
33211
|
+
path55.join(dataDir, "permissions.json")
|
|
32918
33212
|
) ?? [];
|
|
32919
33213
|
if (!getInstanceIdentity(dataDir)) {
|
|
32920
33214
|
found("instance", "instance identity is not seeded \u2014 the built-in admin/dev subjects have no persisted UUIDs");
|
|
32921
33215
|
if (apply) ensureInstanceIdentity(dataDir);
|
|
32922
33216
|
}
|
|
32923
|
-
const orgPath =
|
|
33217
|
+
const orgPath = path55.join(dataDir, "organization.json");
|
|
32924
33218
|
const org = readRaw(orgPath);
|
|
32925
33219
|
if (org?.units?.length) {
|
|
32926
33220
|
const units = org.units;
|
|
@@ -32974,7 +33268,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
32974
33268
|
found("units", `${remap.length} unit id(s) would be remapped across placements, exposeTo, assignments, and users`);
|
|
32975
33269
|
}
|
|
32976
33270
|
}
|
|
32977
|
-
const usersPath =
|
|
33271
|
+
const usersPath = path55.join(dataDir, "users.json");
|
|
32978
33272
|
const users = readRaw(usersPath);
|
|
32979
33273
|
if (users) {
|
|
32980
33274
|
let changed = false;
|
|
@@ -33001,7 +33295,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33001
33295
|
}
|
|
33002
33296
|
if (apply && changed) writeRaw(usersPath, users);
|
|
33003
33297
|
}
|
|
33004
|
-
const keysPath =
|
|
33298
|
+
const keysPath = path55.join(dataDir, "auth", "credentials.json");
|
|
33005
33299
|
const keys = readRaw(keysPath);
|
|
33006
33300
|
if (keys) {
|
|
33007
33301
|
let changed = false;
|
|
@@ -33051,7 +33345,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33051
33345
|
}
|
|
33052
33346
|
if (apply && changed) writeRaw(keysPath, keys);
|
|
33053
33347
|
}
|
|
33054
|
-
const sessionsPath =
|
|
33348
|
+
const sessionsPath = path55.join(dataDir, "web-sessions.json");
|
|
33055
33349
|
const sessions = readRaw(sessionsPath);
|
|
33056
33350
|
if (sessions) {
|
|
33057
33351
|
let changed = false;
|
|
@@ -33130,25 +33424,25 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33130
33424
|
|
|
33131
33425
|
// src/server/http.ts
|
|
33132
33426
|
var http2 = __toESM(require("http"));
|
|
33133
|
-
var
|
|
33134
|
-
var
|
|
33427
|
+
var fs52 = __toESM(require("fs"));
|
|
33428
|
+
var path63 = __toESM(require("path"));
|
|
33135
33429
|
|
|
33136
33430
|
// src/server/request.ts
|
|
33137
33431
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
33138
33432
|
init_fs();
|
|
33139
33433
|
|
|
33140
33434
|
// src/server/approvals.ts
|
|
33141
|
-
var
|
|
33142
|
-
var
|
|
33143
|
-
var
|
|
33435
|
+
var fs45 = __toESM(require("fs"));
|
|
33436
|
+
var path56 = __toESM(require("path"));
|
|
33437
|
+
var crypto14 = __toESM(require("crypto"));
|
|
33144
33438
|
function storePath12(dataDir) {
|
|
33145
|
-
return
|
|
33439
|
+
return path56.join(dataDir, "approvals.json");
|
|
33146
33440
|
}
|
|
33147
33441
|
function readRequests(dataDir) {
|
|
33148
33442
|
const p = storePath12(dataDir);
|
|
33149
33443
|
let raw;
|
|
33150
33444
|
try {
|
|
33151
|
-
raw =
|
|
33445
|
+
raw = fs45.readFileSync(p, "utf8");
|
|
33152
33446
|
} catch (e) {
|
|
33153
33447
|
if (e.code === "ENOENT") return [];
|
|
33154
33448
|
throw new Error(`Failed to read approval store at ${p}: ${e.message}`);
|
|
@@ -33163,10 +33457,10 @@ function readRequests(dataDir) {
|
|
|
33163
33457
|
}
|
|
33164
33458
|
function persistRequests(dataDir, requests) {
|
|
33165
33459
|
const p = storePath12(dataDir);
|
|
33166
|
-
|
|
33460
|
+
fs45.mkdirSync(path56.dirname(p), { recursive: true });
|
|
33167
33461
|
const tmp = `${p}.tmp`;
|
|
33168
|
-
|
|
33169
|
-
|
|
33462
|
+
fs45.writeFileSync(tmp, JSON.stringify(requests, null, 2) + "\n");
|
|
33463
|
+
fs45.renameSync(tmp, p);
|
|
33170
33464
|
}
|
|
33171
33465
|
var SECRET_RE2 = /Bearer\s+\S/i;
|
|
33172
33466
|
var LONG_HEX_RE2 = /[0-9a-fA-F]{32,}/;
|
|
@@ -33214,7 +33508,7 @@ var ApprovalRegistry = class {
|
|
|
33214
33508
|
}
|
|
33215
33509
|
const stored = {
|
|
33216
33510
|
...request,
|
|
33217
|
-
id:
|
|
33511
|
+
id: crypto14.randomUUID(),
|
|
33218
33512
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33219
33513
|
status: "pending"
|
|
33220
33514
|
};
|
|
@@ -33523,23 +33817,6 @@ function lockProject2(cfg, credential, projectId, subproject) {
|
|
|
33523
33817
|
}
|
|
33524
33818
|
});
|
|
33525
33819
|
}
|
|
33526
|
-
function promoteProject2(cfg, credential, projectId, subproject) {
|
|
33527
|
-
return lifecycleAction(cfg, credential, projectId, {
|
|
33528
|
-
action: "project:promote",
|
|
33529
|
-
verb: "Promote",
|
|
33530
|
-
noun: "promotion",
|
|
33531
|
-
subproject,
|
|
33532
|
-
execute: () => {
|
|
33533
|
-
const promo = executeApprovedPromote(cfg, projectId, subproject);
|
|
33534
|
-
return {
|
|
33535
|
-
status: "completed",
|
|
33536
|
-
action: "project:promote",
|
|
33537
|
-
summary: `Promotion of project "${projectId}"${subprojectSuffix(subproject)}: ${promo.status} \u2014 ${promo.message}`,
|
|
33538
|
-
promote: promo
|
|
33539
|
-
};
|
|
33540
|
-
}
|
|
33541
|
-
});
|
|
33542
|
-
}
|
|
33543
33820
|
function subprojectSuffix(subproject) {
|
|
33544
33821
|
return subproject ? ` subproject "${subproject}"` : "";
|
|
33545
33822
|
}
|
|
@@ -33663,11 +33940,6 @@ function executeApproved(cfg, req) {
|
|
|
33663
33940
|
const lock = executeApprovedLock(cfg, req.projectId ?? "", scope);
|
|
33664
33941
|
return `Locked project "${req.projectId}"${subprojectSuffix(scope)} (status: ${lock.status}).`;
|
|
33665
33942
|
}
|
|
33666
|
-
case "project:promote": {
|
|
33667
|
-
const scope = readSubprojectScope(req);
|
|
33668
|
-
const promo = executeApprovedPromote(cfg, req.projectId ?? "", scope);
|
|
33669
|
-
return `Promotion of project "${req.projectId}"${subprojectSuffix(scope)}: ${promo.status} \u2014 ${promo.message}`;
|
|
33670
|
-
}
|
|
33671
33943
|
default:
|
|
33672
33944
|
throw new Error(`Unsupported approval kind "${req.kind}".`);
|
|
33673
33945
|
}
|
|
@@ -33709,7 +33981,7 @@ async function awaitApproval(cfg, credential, requestId, timeoutSeconds) {
|
|
|
33709
33981
|
let current2 = req;
|
|
33710
33982
|
while (current2.status === "pending" && Date.now() < deadline) {
|
|
33711
33983
|
const remaining = deadline - Date.now();
|
|
33712
|
-
await new Promise((
|
|
33984
|
+
await new Promise((resolve27) => setTimeout(resolve27, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
|
|
33713
33985
|
expirePendingApprovals(cfg.dataDir, (/* @__PURE__ */ new Date()).toISOString());
|
|
33714
33986
|
current2 = getApprovalRequestById(cfg.dataDir, requestId) ?? current2;
|
|
33715
33987
|
}
|
|
@@ -33977,12 +34249,12 @@ function handleOperationsRequest(cfg, credential, req, res, url) {
|
|
|
33977
34249
|
}
|
|
33978
34250
|
|
|
33979
34251
|
// src/server/gitbacking.ts
|
|
33980
|
-
var
|
|
33981
|
-
var
|
|
33982
|
-
var
|
|
34252
|
+
var fs46 = __toESM(require("fs"));
|
|
34253
|
+
var path57 = __toESM(require("path"));
|
|
34254
|
+
var crypto15 = __toESM(require("crypto"));
|
|
33983
34255
|
var import_child_process4 = require("child_process");
|
|
33984
34256
|
function storePath13(dataDir) {
|
|
33985
|
-
return
|
|
34257
|
+
return path57.join(dataDir, "git-backing.json");
|
|
33986
34258
|
}
|
|
33987
34259
|
var GitBackingStore = class {
|
|
33988
34260
|
constructor(dataDir) {
|
|
@@ -33994,7 +34266,7 @@ var GitBackingStore = class {
|
|
|
33994
34266
|
const p = storePath13(this.dataDir);
|
|
33995
34267
|
let raw;
|
|
33996
34268
|
try {
|
|
33997
|
-
raw =
|
|
34269
|
+
raw = fs46.readFileSync(p, "utf8");
|
|
33998
34270
|
} catch (err) {
|
|
33999
34271
|
if (err.code === "ENOENT") return [];
|
|
34000
34272
|
throw new Error(`Cannot read git-backing store at ${p}: ${err.message}`);
|
|
@@ -34013,10 +34285,10 @@ var GitBackingStore = class {
|
|
|
34013
34285
|
/** Swap the persisted collection wholesale via write-temp-then-rename. */
|
|
34014
34286
|
replaceAll(bindings) {
|
|
34015
34287
|
const p = storePath13(this.dataDir);
|
|
34016
|
-
|
|
34288
|
+
fs46.mkdirSync(path57.dirname(p), { recursive: true });
|
|
34017
34289
|
const tmp = `${p}.tmp`;
|
|
34018
|
-
|
|
34019
|
-
|
|
34290
|
+
fs46.writeFileSync(tmp, JSON.stringify(bindings, null, 2) + "\n");
|
|
34291
|
+
fs46.renameSync(tmp, p);
|
|
34020
34292
|
}
|
|
34021
34293
|
};
|
|
34022
34294
|
var GitBackingRegistry = class {
|
|
@@ -34035,7 +34307,7 @@ var GitBackingRegistry = class {
|
|
|
34035
34307
|
const bindings = this.store.load();
|
|
34036
34308
|
const stored = {
|
|
34037
34309
|
...binding,
|
|
34038
|
-
id: binding.id ||
|
|
34310
|
+
id: binding.id || crypto15.randomUUID(),
|
|
34039
34311
|
createdAt: binding.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
34040
34312
|
};
|
|
34041
34313
|
const sameScope = (b) => b.scopeKind === stored.scopeKind && (b.scopeId ?? "") === (stored.scopeId ?? "");
|
|
@@ -34121,8 +34393,8 @@ function checkoutBranch(workdir, branch) {
|
|
|
34121
34393
|
}
|
|
34122
34394
|
}
|
|
34123
34395
|
function cloneOrOpen(remote, branch, workdir, credentialRef) {
|
|
34124
|
-
if (!
|
|
34125
|
-
|
|
34396
|
+
if (!fs46.existsSync(path57.join(workdir, ".git"))) {
|
|
34397
|
+
fs46.mkdirSync(workdir, { recursive: true });
|
|
34126
34398
|
git2(["clone", authRemote2(remote, credentialRef), "."], workdir);
|
|
34127
34399
|
git2(["config", "user.name", process.env["WAIRON_GIT_NAME"] || "wairon-bot"], workdir);
|
|
34128
34400
|
git2(["config", "user.email", process.env["WAIRON_GIT_EMAIL"] || "wairon-bot@localhost"], workdir);
|
|
@@ -34134,11 +34406,11 @@ function cloneOrOpen(remote, branch, workdir, credentialRef) {
|
|
|
34134
34406
|
return workdir;
|
|
34135
34407
|
}
|
|
34136
34408
|
function mirrorTree(sourceDir, targetDir) {
|
|
34137
|
-
|
|
34138
|
-
const stat =
|
|
34409
|
+
fs46.rmSync(targetDir, { recursive: true, force: true });
|
|
34410
|
+
const stat = fs46.statSync(sourceDir, { throwIfNoEntry: false });
|
|
34139
34411
|
if (!stat) return;
|
|
34140
|
-
|
|
34141
|
-
|
|
34412
|
+
fs46.mkdirSync(path57.dirname(targetDir), { recursive: true });
|
|
34413
|
+
fs46.cpSync(sourceDir, targetDir, { recursive: true });
|
|
34142
34414
|
}
|
|
34143
34415
|
function commitAndPush(workdir, message) {
|
|
34144
34416
|
git2(["add", "-A"], workdir);
|
|
@@ -34260,7 +34532,7 @@ function unbindScope(cfg, credential, bindingId) {
|
|
|
34260
34532
|
tryAppendAudit7(cfg, buildAuditEvent6(principal, "git.backing.unbind", "security", bindingId));
|
|
34261
34533
|
}
|
|
34262
34534
|
function runMirrorSync(cfg, binding) {
|
|
34263
|
-
const workdir =
|
|
34535
|
+
const workdir = path57.join(cfg.dataDir, "git-backing", binding.id);
|
|
34264
34536
|
cloneOrOpen(binding.remote, binding.branch, workdir, binding.credentialRef);
|
|
34265
34537
|
if (binding.scopeKind === "unit") {
|
|
34266
34538
|
const units = listOrganizationUnits(cfg.dataDir);
|
|
@@ -34268,23 +34540,23 @@ function runMirrorSync(cfg, binding) {
|
|
|
34268
34540
|
const placedIds = new Set(
|
|
34269
34541
|
listProjectPlacements(cfg.dataDir).filter((p) => subtree.has(p.unitId)).map((p) => p.projectId)
|
|
34270
34542
|
);
|
|
34271
|
-
const projectsDir =
|
|
34272
|
-
|
|
34543
|
+
const projectsDir = path57.join(workdir, "projects");
|
|
34544
|
+
fs46.rmSync(projectsDir, { recursive: true, force: true });
|
|
34273
34545
|
for (const rec of listProjectRecords(cfg.dataDir)) {
|
|
34274
34546
|
if (!placedIds.has(rec.id)) continue;
|
|
34275
|
-
mirrorTree(
|
|
34547
|
+
mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
|
|
34276
34548
|
}
|
|
34277
34549
|
} else {
|
|
34278
|
-
const instanceDir =
|
|
34279
|
-
|
|
34550
|
+
const instanceDir = path57.join(workdir, "instance");
|
|
34551
|
+
fs46.rmSync(instanceDir, { recursive: true, force: true });
|
|
34280
34552
|
const files = binding.includeCredentials ? INSTANCE_STRUCTURE_FILES : INSTANCE_STRUCTURE_FILES.filter((rel2) => rel2 !== "auth/credentials.json");
|
|
34281
34553
|
for (const rel2 of files) {
|
|
34282
|
-
mirrorTree(
|
|
34554
|
+
mirrorTree(path57.join(cfg.dataDir, rel2), path57.join(instanceDir, rel2));
|
|
34283
34555
|
}
|
|
34284
|
-
const projectsDir =
|
|
34285
|
-
|
|
34556
|
+
const projectsDir = path57.join(workdir, "projects");
|
|
34557
|
+
fs46.rmSync(projectsDir, { recursive: true, force: true });
|
|
34286
34558
|
for (const rec of listProjectRecords(cfg.dataDir)) {
|
|
34287
|
-
mirrorTree(
|
|
34559
|
+
mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
|
|
34288
34560
|
}
|
|
34289
34561
|
}
|
|
34290
34562
|
const published = commitAndPush(
|
|
@@ -34430,7 +34702,7 @@ function setExposurePolicy2(cfg, credential, exposure) {
|
|
|
34430
34702
|
|
|
34431
34703
|
// src/server/websocket.ts
|
|
34432
34704
|
var import_events = require("events");
|
|
34433
|
-
var
|
|
34705
|
+
var crypto16 = __toESM(require("crypto"));
|
|
34434
34706
|
var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
34435
34707
|
var MAX_PAYLOAD = 1 << 20;
|
|
34436
34708
|
var OP_CONTINUATION = 0;
|
|
@@ -34444,7 +34716,7 @@ function isWebSocketUpgrade(req) {
|
|
|
34444
34716
|
function acceptWebSocket(req, socket) {
|
|
34445
34717
|
const key = req.headers["sec-websocket-key"];
|
|
34446
34718
|
if (!isWebSocketUpgrade(req) || typeof key !== "string") return null;
|
|
34447
|
-
const accept =
|
|
34719
|
+
const accept = crypto16.createHash("sha1").update(key + WS_GUID).digest("base64");
|
|
34448
34720
|
socket.write(
|
|
34449
34721
|
`HTTP/1.1 101 Switching Protocols\r
|
|
34450
34722
|
Upgrade: websocket\r
|
|
@@ -34594,9 +34866,9 @@ function encodeFrame(opcode, payload) {
|
|
|
34594
34866
|
}
|
|
34595
34867
|
|
|
34596
34868
|
// src/server/web.ts
|
|
34597
|
-
var
|
|
34598
|
-
var
|
|
34599
|
-
var
|
|
34869
|
+
var crypto21 = __toESM(require("crypto"));
|
|
34870
|
+
var fs51 = __toESM(require("fs"));
|
|
34871
|
+
var path62 = __toESM(require("path"));
|
|
34600
34872
|
|
|
34601
34873
|
// src/server/webadmin.ts
|
|
34602
34874
|
function listUsers3(cfg, sessionId, project2) {
|
|
@@ -34838,26 +35110,23 @@ function createProject2(cfg, sessionId, id, unitId, profileSelection) {
|
|
|
34838
35110
|
function lockProject3(cfg, sessionId, projectId) {
|
|
34839
35111
|
return lockProject(cfg, sessionId, projectId);
|
|
34840
35112
|
}
|
|
34841
|
-
function promoteProject3(cfg, sessionId, projectId) {
|
|
34842
|
-
return promoteProject(cfg, sessionId, projectId);
|
|
34843
|
-
}
|
|
34844
35113
|
function destroyProject2(cfg, sessionId, id) {
|
|
34845
35114
|
destroyProject(cfg, sessionId, id);
|
|
34846
35115
|
}
|
|
34847
35116
|
|
|
34848
35117
|
// src/server/shareadmin.ts
|
|
34849
|
-
var
|
|
35118
|
+
var crypto20 = __toESM(require("crypto"));
|
|
34850
35119
|
|
|
34851
35120
|
// src/server/sharesnapshots.ts
|
|
34852
|
-
var
|
|
34853
|
-
var
|
|
34854
|
-
var
|
|
35121
|
+
var fs47 = __toESM(require("fs"));
|
|
35122
|
+
var path58 = __toESM(require("path"));
|
|
35123
|
+
var crypto17 = __toESM(require("crypto"));
|
|
34855
35124
|
init_fs();
|
|
34856
35125
|
function snapshotDir(dataDir) {
|
|
34857
|
-
return
|
|
35126
|
+
return path58.join(dataDir, "share-snapshots");
|
|
34858
35127
|
}
|
|
34859
35128
|
function snapshotPath(dataDir, id) {
|
|
34860
|
-
return
|
|
35129
|
+
return path58.join(snapshotDir(dataDir), `${id}.json`);
|
|
34861
35130
|
}
|
|
34862
35131
|
var ShareSnapshotStore = class {
|
|
34863
35132
|
constructor(dataDir) {
|
|
@@ -34865,17 +35134,17 @@ var ShareSnapshotStore = class {
|
|
|
34865
35134
|
}
|
|
34866
35135
|
write(snapshot) {
|
|
34867
35136
|
const p = snapshotPath(this.dataDir, snapshot.id);
|
|
34868
|
-
if (
|
|
34869
|
-
|
|
35137
|
+
if (fs47.existsSync(p)) return;
|
|
35138
|
+
fs47.mkdirSync(path58.dirname(p), { recursive: true });
|
|
34870
35139
|
const tmp = `${p}.tmp`;
|
|
34871
|
-
|
|
34872
|
-
|
|
35140
|
+
fs47.writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n");
|
|
35141
|
+
fs47.renameSync(tmp, p);
|
|
34873
35142
|
}
|
|
34874
35143
|
read(snapshotId) {
|
|
34875
35144
|
const p = snapshotPath(this.dataDir, snapshotId);
|
|
34876
35145
|
let raw;
|
|
34877
35146
|
try {
|
|
34878
|
-
raw =
|
|
35147
|
+
raw = fs47.readFileSync(p, "utf8");
|
|
34879
35148
|
} catch (err) {
|
|
34880
35149
|
if (err.code === "ENOENT") return null;
|
|
34881
35150
|
throw new Error(`Cannot read share snapshot at ${p}: ${err.message}`);
|
|
@@ -34894,7 +35163,7 @@ var ShareSnapshotRegistry = class {
|
|
|
34894
35163
|
put(snapshot) {
|
|
34895
35164
|
const stored = {
|
|
34896
35165
|
...snapshot,
|
|
34897
|
-
id: snapshot.id ||
|
|
35166
|
+
id: snapshot.id || crypto17.randomUUID(),
|
|
34898
35167
|
capturedAt: snapshot.capturedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
34899
35168
|
};
|
|
34900
35169
|
this.store.write(stored);
|
|
@@ -34967,11 +35236,11 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
|
|
|
34967
35236
|
}
|
|
34968
35237
|
|
|
34969
35238
|
// src/server/sharelinks.ts
|
|
34970
|
-
var
|
|
34971
|
-
var
|
|
34972
|
-
var
|
|
35239
|
+
var fs48 = __toESM(require("fs"));
|
|
35240
|
+
var path59 = __toESM(require("path"));
|
|
35241
|
+
var crypto18 = __toESM(require("crypto"));
|
|
34973
35242
|
function storePath14(dataDir) {
|
|
34974
|
-
return
|
|
35243
|
+
return path59.join(dataDir, "share-links.json");
|
|
34975
35244
|
}
|
|
34976
35245
|
var ShareLinkStore = class {
|
|
34977
35246
|
constructor(dataDir) {
|
|
@@ -34981,7 +35250,7 @@ var ShareLinkStore = class {
|
|
|
34981
35250
|
const p = storePath14(this.dataDir);
|
|
34982
35251
|
let raw;
|
|
34983
35252
|
try {
|
|
34984
|
-
raw =
|
|
35253
|
+
raw = fs48.readFileSync(p, "utf8");
|
|
34985
35254
|
} catch (err) {
|
|
34986
35255
|
if (err.code === "ENOENT") return [];
|
|
34987
35256
|
throw new Error(`Cannot read share-link store at ${p}: ${err.message}`);
|
|
@@ -34999,10 +35268,10 @@ var ShareLinkStore = class {
|
|
|
34999
35268
|
}
|
|
35000
35269
|
replaceAll(links) {
|
|
35001
35270
|
const p = storePath14(this.dataDir);
|
|
35002
|
-
|
|
35271
|
+
fs48.mkdirSync(path59.dirname(p), { recursive: true });
|
|
35003
35272
|
const tmp = `${p}.tmp`;
|
|
35004
|
-
|
|
35005
|
-
|
|
35273
|
+
fs48.writeFileSync(tmp, JSON.stringify(links, null, 2) + "\n");
|
|
35274
|
+
fs48.renameSync(tmp, p);
|
|
35006
35275
|
}
|
|
35007
35276
|
};
|
|
35008
35277
|
var ShareLinkRegistry = class {
|
|
@@ -35013,7 +35282,7 @@ var ShareLinkRegistry = class {
|
|
|
35013
35282
|
const links = this.store.load();
|
|
35014
35283
|
const stored = {
|
|
35015
35284
|
...link,
|
|
35016
|
-
id: link.id ||
|
|
35285
|
+
id: link.id || crypto18.randomUUID(),
|
|
35017
35286
|
createdAt: link.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
35018
35287
|
};
|
|
35019
35288
|
this.store.replaceAll([...links, stored]);
|
|
@@ -35070,11 +35339,11 @@ function listProjectLinks(dataDir, projectId) {
|
|
|
35070
35339
|
}
|
|
35071
35340
|
|
|
35072
35341
|
// src/server/shareaccesslog.ts
|
|
35073
|
-
var
|
|
35074
|
-
var
|
|
35075
|
-
var
|
|
35342
|
+
var fs49 = __toESM(require("fs"));
|
|
35343
|
+
var path60 = __toESM(require("path"));
|
|
35344
|
+
var crypto19 = __toESM(require("crypto"));
|
|
35076
35345
|
function storePath15(dataDir) {
|
|
35077
|
-
return
|
|
35346
|
+
return path60.join(dataDir, "share-access.json");
|
|
35078
35347
|
}
|
|
35079
35348
|
var ShareAccessStore = class {
|
|
35080
35349
|
constructor(dataDir) {
|
|
@@ -35084,7 +35353,7 @@ var ShareAccessStore = class {
|
|
|
35084
35353
|
const p = storePath15(this.dataDir);
|
|
35085
35354
|
let raw;
|
|
35086
35355
|
try {
|
|
35087
|
-
raw =
|
|
35356
|
+
raw = fs49.readFileSync(p, "utf8");
|
|
35088
35357
|
} catch (err) {
|
|
35089
35358
|
if (err.code === "ENOENT") return [];
|
|
35090
35359
|
throw new Error(`Cannot read share-access store at ${p}: ${err.message}`);
|
|
@@ -35102,11 +35371,11 @@ var ShareAccessStore = class {
|
|
|
35102
35371
|
}
|
|
35103
35372
|
append(entry) {
|
|
35104
35373
|
const p = storePath15(this.dataDir);
|
|
35105
|
-
|
|
35374
|
+
fs49.mkdirSync(path60.dirname(p), { recursive: true });
|
|
35106
35375
|
const next = [...this.load(), entry];
|
|
35107
35376
|
const tmp = `${p}.tmp`;
|
|
35108
|
-
|
|
35109
|
-
|
|
35377
|
+
fs49.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
|
|
35378
|
+
fs49.renameSync(tmp, p);
|
|
35110
35379
|
}
|
|
35111
35380
|
};
|
|
35112
35381
|
var ShareAccessRegistry = class {
|
|
@@ -35116,7 +35385,7 @@ var ShareAccessRegistry = class {
|
|
|
35116
35385
|
append(entry) {
|
|
35117
35386
|
const stored = {
|
|
35118
35387
|
...entry,
|
|
35119
|
-
id: entry.id ||
|
|
35388
|
+
id: entry.id || crypto19.randomUUID(),
|
|
35120
35389
|
at: entry.at || (/* @__PURE__ */ new Date()).toISOString()
|
|
35121
35390
|
};
|
|
35122
35391
|
this.store.append(stored);
|
|
@@ -35180,7 +35449,7 @@ function createShareLink(cfg, sessionId, input) {
|
|
|
35180
35449
|
const wantsOpenapi = !!input.allowDownloadOpenapi || (input.artifacts ?? []).includes("openapi");
|
|
35181
35450
|
const artifacts = artifactsFor({ allowDownloadOpenapi: !!input.allowDownloadOpenapi }, wantsOpenapi);
|
|
35182
35451
|
const snapshot = putSnapshot(cfg.dataDir, captureSnapshot(cfg.dataDir, principal, input.projectId, input.view, artifacts));
|
|
35183
|
-
const token =
|
|
35452
|
+
const token = crypto20.randomBytes(32).toString("base64url");
|
|
35184
35453
|
const link = createLink(cfg.dataDir, {
|
|
35185
35454
|
id: "",
|
|
35186
35455
|
tokenHash: hashToken(token),
|
|
@@ -35257,16 +35526,16 @@ function getShareAccessLog(cfg, sessionId, linkId, limit) {
|
|
|
35257
35526
|
init_fs();
|
|
35258
35527
|
|
|
35259
35528
|
// src/server/swagger.ts
|
|
35260
|
-
var
|
|
35261
|
-
var
|
|
35529
|
+
var fs50 = __toESM(require("fs"));
|
|
35530
|
+
var path61 = __toESM(require("path"));
|
|
35262
35531
|
var cache;
|
|
35263
35532
|
function loadAssets() {
|
|
35264
35533
|
if (cache !== void 0) return cache;
|
|
35265
35534
|
try {
|
|
35266
|
-
const dir =
|
|
35535
|
+
const dir = path61.dirname(require.resolve("swagger-ui-dist/package.json"));
|
|
35267
35536
|
cache = {
|
|
35268
|
-
css:
|
|
35269
|
-
js:
|
|
35537
|
+
css: fs50.readFileSync(path61.join(dir, "swagger-ui.css"), "utf8"),
|
|
35538
|
+
js: fs50.readFileSync(path61.join(dir, "swagger-ui-bundle.js"), "utf8")
|
|
35270
35539
|
};
|
|
35271
35540
|
} catch {
|
|
35272
35541
|
cache = null;
|
|
@@ -35306,7 +35575,7 @@ function getLoginOptions(cfg) {
|
|
|
35306
35575
|
async function startSignIn(cfg, providerId, redirectUri) {
|
|
35307
35576
|
const provider = resolveEnabledProvider(cfg, providerId);
|
|
35308
35577
|
assertAllowedRedirectUri(provider, redirectUri);
|
|
35309
|
-
const nonce =
|
|
35578
|
+
const nonce = crypto21.randomBytes(16).toString("hex");
|
|
35310
35579
|
const payload = { providerId, nonce, redirectUri };
|
|
35311
35580
|
const state = signSsoState(JSON.stringify(payload));
|
|
35312
35581
|
const endpoints = await resolveEndpoints(provider);
|
|
@@ -35658,20 +35927,20 @@ function clearNonceCookie(secure) {
|
|
|
35658
35927
|
}
|
|
35659
35928
|
function nonceMatches(a, b) {
|
|
35660
35929
|
if (!a || !b || a.length !== b.length) return false;
|
|
35661
|
-
return
|
|
35930
|
+
return crypto21.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
35662
35931
|
}
|
|
35663
35932
|
function loadReactBundle() {
|
|
35664
35933
|
const candidates = [
|
|
35665
|
-
|
|
35934
|
+
path62.resolve(__dirname, "webapp.html"),
|
|
35666
35935
|
// dist/index.js -> dist/webapp.html
|
|
35667
|
-
|
|
35936
|
+
path62.resolve(__dirname, "..", "webapp.html"),
|
|
35668
35937
|
// dist/cli/index.js -> dist/webapp.html
|
|
35669
|
-
|
|
35938
|
+
path62.resolve(__dirname, "..", "..", "web", "dist", "index.html")
|
|
35670
35939
|
// tsx dev: src/server -> web/dist
|
|
35671
35940
|
];
|
|
35672
35941
|
for (const candidate of candidates) {
|
|
35673
35942
|
try {
|
|
35674
|
-
if (
|
|
35943
|
+
if (fs51.existsSync(candidate)) return fs51.readFileSync(candidate, "utf8");
|
|
35675
35944
|
} catch {
|
|
35676
35945
|
}
|
|
35677
35946
|
}
|
|
@@ -37358,9 +37627,6 @@ details.adv summary { cursor:pointer; color:var(--dim); font-size:12px; margin-b
|
|
|
37358
37627
|
Array.prototype.forEach.call(host.querySelectorAll('[data-lock]'), function (b) {
|
|
37359
37628
|
b.addEventListener('click', function () { projectAction('/web/projects/lock', { projectId: b.getAttribute('data-lock') }, b, 'Locking\u2026', 'Lock'); });
|
|
37360
37629
|
});
|
|
37361
|
-
Array.prototype.forEach.call(host.querySelectorAll('[data-promote]'), function (b) {
|
|
37362
|
-
b.addEventListener('click', function () { projectAction('/web/projects/promote', { projectId: b.getAttribute('data-promote') }, b, 'Promoting\u2026', 'Promote'); });
|
|
37363
|
-
});
|
|
37364
37630
|
Array.prototype.forEach.call(host.querySelectorAll('[data-destroy]'), function (b) {
|
|
37365
37631
|
b.addEventListener('click', function () {
|
|
37366
37632
|
if (!confirm('Destroy project "' + b.getAttribute('data-destroy') + '"? This removes its entire spec tree.')) return;
|
|
@@ -37664,9 +37930,6 @@ function projectCreate(cfg, sessionId, body, res) {
|
|
|
37664
37930
|
function projectLock(cfg, sessionId, body, res) {
|
|
37665
37931
|
sendJson(res, 200, lockProject3(cfg, sessionId, String(body?.projectId ?? "")));
|
|
37666
37932
|
}
|
|
37667
|
-
function projectPromote(cfg, sessionId, body, res) {
|
|
37668
|
-
sendJson(res, 200, promoteProject3(cfg, sessionId, String(body?.projectId ?? "")));
|
|
37669
|
-
}
|
|
37670
37933
|
function projectDestroy(cfg, sessionId, body, res) {
|
|
37671
37934
|
destroyProject2(cfg, sessionId, String(body?.id ?? ""));
|
|
37672
37935
|
sendJson(res, 200, { ok: true });
|
|
@@ -37946,9 +38209,6 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
|
|
|
37946
38209
|
if (req.method === "POST" && parts.length === 3 && parts[2] === "lock") {
|
|
37947
38210
|
return projectLock(cfg, sessionId, body, res);
|
|
37948
38211
|
}
|
|
37949
|
-
if (req.method === "POST" && parts.length === 3 && parts[2] === "promote") {
|
|
37950
|
-
return projectPromote(cfg, sessionId, body, res);
|
|
37951
|
-
}
|
|
37952
38212
|
if (req.method === "POST" && parts.length === 3 && parts[2] === "destroy") {
|
|
37953
38213
|
return projectDestroy(cfg, sessionId, body, res);
|
|
37954
38214
|
}
|
|
@@ -38203,8 +38463,8 @@ var RealtimeHub = class {
|
|
|
38203
38463
|
* complete the handshake, and register the connection. A bad path or session
|
|
38204
38464
|
* destroys the socket. */
|
|
38205
38465
|
handleUpgrade(cfg, req, socket) {
|
|
38206
|
-
const
|
|
38207
|
-
if (
|
|
38466
|
+
const path67 = (req.url ?? "/").split("?")[0];
|
|
38467
|
+
if (path67 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
|
|
38208
38468
|
socket.destroy();
|
|
38209
38469
|
return;
|
|
38210
38470
|
}
|
|
@@ -38381,7 +38641,6 @@ function auditToolCall(dataDir, principal, projectId, body, outcome, subproject)
|
|
|
38381
38641
|
var PROJECT_LIFECYCLE_TOOLS = /* @__PURE__ */ new Set([
|
|
38382
38642
|
"sdd_host_initialize_project",
|
|
38383
38643
|
"sdd_host_lock_project",
|
|
38384
|
-
"sdd_host_promote_project",
|
|
38385
38644
|
"sdd_host_get_approval_status",
|
|
38386
38645
|
"sdd_host_await_approval"
|
|
38387
38646
|
]);
|
|
@@ -38439,7 +38698,6 @@ function requiredDataPlaneCapability(toolName) {
|
|
|
38439
38698
|
var MUTATING_HOST_TOOLS = /* @__PURE__ */ new Set([
|
|
38440
38699
|
"sdd_host_initialize_project",
|
|
38441
38700
|
"sdd_host_lock_project",
|
|
38442
|
-
"sdd_host_promote_project",
|
|
38443
38701
|
"sdd_host_await_approval",
|
|
38444
38702
|
// a decided approval may have executed the action
|
|
38445
38703
|
"sdd_host_policy_reconcile"
|
|
@@ -38509,9 +38767,6 @@ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body, su
|
|
|
38509
38767
|
case "sdd_host_lock_project":
|
|
38510
38768
|
value = lockProject2(cfg, credential, projectId, subproject);
|
|
38511
38769
|
break;
|
|
38512
|
-
case "sdd_host_promote_project":
|
|
38513
|
-
value = promoteProject2(cfg, credential, projectId, subproject);
|
|
38514
|
-
break;
|
|
38515
38770
|
case "sdd_host_await_approval":
|
|
38516
38771
|
value = await awaitApproval(
|
|
38517
38772
|
cfg,
|
|
@@ -38977,7 +39232,6 @@ var WEB_MUTATION_PATHS = /* @__PURE__ */ new Set([
|
|
|
38977
39232
|
"/web/tokens/revoke",
|
|
38978
39233
|
"/web/projects",
|
|
38979
39234
|
"/web/projects/lock",
|
|
38980
|
-
"/web/projects/promote",
|
|
38981
39235
|
"/web/projects/destroy"
|
|
38982
39236
|
]);
|
|
38983
39237
|
function routeData(cfg, req, res) {
|
|
@@ -38989,7 +39243,7 @@ function routeData(cfg, req, res) {
|
|
|
38989
39243
|
if (req.method === "GET" && url.pathname === "/readyz") {
|
|
38990
39244
|
let ready = false;
|
|
38991
39245
|
try {
|
|
38992
|
-
|
|
39246
|
+
fs52.accessSync(cfg.dataDir, fs52.constants.W_OK);
|
|
38993
39247
|
ready = true;
|
|
38994
39248
|
} catch {
|
|
38995
39249
|
}
|
|
@@ -39089,7 +39343,7 @@ function routeData(cfg, req, res) {
|
|
|
39089
39343
|
}
|
|
39090
39344
|
function readExposurePolicyFile(dataDir) {
|
|
39091
39345
|
try {
|
|
39092
|
-
const raw =
|
|
39346
|
+
const raw = fs52.readFileSync(path63.join(dataDir, "exposure-policy.json"), "utf8");
|
|
39093
39347
|
const parsed = JSON.parse(raw);
|
|
39094
39348
|
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
39095
39349
|
} catch {
|
|
@@ -39138,7 +39392,6 @@ async function routeAdmin(cfg, req, res) {
|
|
|
39138
39392
|
return sendJson(res, 200, { ok: true });
|
|
39139
39393
|
}
|
|
39140
39394
|
if (req.method === "POST" && parts.length === 4 && parts[3] === "lock") return sendJson(res, 200, lockProject(cfg, cred, parts[2]));
|
|
39141
|
-
if (req.method === "POST" && parts.length === 4 && parts[3] === "promote") return sendJson(res, 200, promoteProject(cfg, cred, parts[2]));
|
|
39142
39395
|
if (req.method === "POST" && parts.length === 4 && parts[3] === "git") return sendJson(res, 201, enableGit(cfg, cred, parts[2], body.remote, body.branch ?? "main", body.pat));
|
|
39143
39396
|
if (req.method === "DELETE" && parts.length === 4 && parts[3] === "git") {
|
|
39144
39397
|
disableGit(cfg, cred, parts[2]);
|
|
@@ -39793,9 +40046,9 @@ function seedDemoTree() {
|
|
|
39793
40046
|
|
|
39794
40047
|
// src/commands/host.ts
|
|
39795
40048
|
function resolveHostConfig(options) {
|
|
39796
|
-
const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] ||
|
|
40049
|
+
const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path64.join(os12.homedir(), ".wairon", "data");
|
|
39797
40050
|
if (!process.env["WAIRON_PACKS_DIR"]) {
|
|
39798
|
-
process.env["WAIRON_PACKS_DIR"] =
|
|
40051
|
+
process.env["WAIRON_PACKS_DIR"] = path64.join(dataDir, "packs");
|
|
39799
40052
|
}
|
|
39800
40053
|
const cfg = {
|
|
39801
40054
|
host: options.host || "0.0.0.0",
|
|
@@ -39896,11 +40149,11 @@ async function runServe(options = {}) {
|
|
|
39896
40149
|
logger.info(` data dir: ${import_chalk17.default.gray(cfg.dataDir)}`);
|
|
39897
40150
|
logger.blank();
|
|
39898
40151
|
logger.info("Press Ctrl+C to stop.");
|
|
39899
|
-
await new Promise((
|
|
40152
|
+
await new Promise((resolve27) => {
|
|
39900
40153
|
const shutdown = () => {
|
|
39901
40154
|
logger.info("Shutting down\u2026");
|
|
39902
40155
|
handle.close();
|
|
39903
|
-
|
|
40156
|
+
resolve27();
|
|
39904
40157
|
};
|
|
39905
40158
|
process.on("SIGINT", shutdown);
|
|
39906
40159
|
process.on("SIGTERM", shutdown);
|
|
@@ -39920,14 +40173,14 @@ function openBrowser(url) {
|
|
|
39920
40173
|
}
|
|
39921
40174
|
async function runDev(options = {}) {
|
|
39922
40175
|
const cwd = process.cwd();
|
|
39923
|
-
if (!
|
|
40176
|
+
if (!fs53.existsSync(path64.join(cwd, ".wai"))) {
|
|
39924
40177
|
throw new WaironError(
|
|
39925
40178
|
"No .wai/ found in the current directory. Run `wairon dev` from a wairon project root (or run `wairon init` first)."
|
|
39926
40179
|
);
|
|
39927
40180
|
}
|
|
39928
|
-
const hash =
|
|
39929
|
-
const dataDir =
|
|
39930
|
-
|
|
40181
|
+
const hash = crypto22.createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
40182
|
+
const dataDir = path64.join(os12.tmpdir(), "wairon-dev", hash);
|
|
40183
|
+
fs53.mkdirSync(dataDir, { recursive: true });
|
|
39931
40184
|
registerLocalDevProject(dataDir, "local", cwd);
|
|
39932
40185
|
const port = options.port ? Number(options.port) : 8080;
|
|
39933
40186
|
const exposurePolicy = {
|
|
@@ -39965,11 +40218,11 @@ async function runDev(options = {}) {
|
|
|
39965
40218
|
logger.blank();
|
|
39966
40219
|
logger.info("An agent edits specs; refresh the page to see the live graph. Press Ctrl+C to stop.");
|
|
39967
40220
|
if (options.open) openBrowser(url);
|
|
39968
|
-
await new Promise((
|
|
40221
|
+
await new Promise((resolve27) => {
|
|
39969
40222
|
const shutdown = () => {
|
|
39970
40223
|
logger.info("Shutting down\u2026");
|
|
39971
40224
|
handle.close();
|
|
39972
|
-
|
|
40225
|
+
resolve27();
|
|
39973
40226
|
};
|
|
39974
40227
|
process.on("SIGINT", shutdown);
|
|
39975
40228
|
process.on("SIGTERM", shutdown);
|
|
@@ -40239,18 +40492,6 @@ async function runHostLock(options = {}) {
|
|
|
40239
40492
|
throw mapAdminError(e);
|
|
40240
40493
|
}
|
|
40241
40494
|
}
|
|
40242
|
-
async function runHostPromote(options = {}) {
|
|
40243
|
-
const cfg = resolveHostConfig(options);
|
|
40244
|
-
if (!options.project) throw new WaironError("`--project <id>` is required for `host promote`.");
|
|
40245
|
-
try {
|
|
40246
|
-
const result = promoteProject(cfg, masterCredential(), options.project);
|
|
40247
|
-
const mark = result.status === "ready" ? import_chalk17.default.green("\u2713") : import_chalk17.default.yellow("\u2717");
|
|
40248
|
-
logger.info(`${mark} ${result.message}`);
|
|
40249
|
-
if (result.status !== "ready") process.exitCode = 1;
|
|
40250
|
-
} catch (e) {
|
|
40251
|
-
throw mapAdminError(e);
|
|
40252
|
-
}
|
|
40253
|
-
}
|
|
40254
40495
|
async function runHostProducer(action, options = {}) {
|
|
40255
40496
|
const cfg = resolveHostConfig(options);
|
|
40256
40497
|
const cred = masterCredential();
|
|
@@ -40335,8 +40576,8 @@ async function runHostPacks(action, options = {}) {
|
|
|
40335
40576
|
}
|
|
40336
40577
|
case "install": {
|
|
40337
40578
|
if (!options.file) throw new WaironError("`--file <path>` (a declarative pack YAML) is required for install.");
|
|
40338
|
-
const name = options.name ??
|
|
40339
|
-
const content =
|
|
40579
|
+
const name = options.name ?? path64.basename(options.file).replace(/\.(ya?ml)$/i, "");
|
|
40580
|
+
const content = fs53.readFileSync(path64.resolve(options.file), "utf8");
|
|
40340
40581
|
const desc = project2 ? installProjectPack(cfg, cred, project2, name, content) : installGlobalPack(cfg, cred, name, content);
|
|
40341
40582
|
logger.success(`Installed ${scope} pack "${desc.name}" (${desc.profiles} profile(s), ${desc.languages} language(s)).`);
|
|
40342
40583
|
if (project2) logger.info("Committed with the project \u2014 every clone and CI will enforce it.");
|
|
@@ -40562,7 +40803,7 @@ async function runSurface(action, options = {}) {
|
|
|
40562
40803
|
}
|
|
40563
40804
|
|
|
40564
40805
|
// src/commands/subsystem.ts
|
|
40565
|
-
var
|
|
40806
|
+
var path65 = __toESM(require("path"));
|
|
40566
40807
|
init_logger();
|
|
40567
40808
|
init_errors();
|
|
40568
40809
|
init_fs();
|
|
@@ -40602,9 +40843,9 @@ async function runSubsystemAdd(id, options = {}) {
|
|
|
40602
40843
|
updatedAt: now
|
|
40603
40844
|
};
|
|
40604
40845
|
createChainedSubsystem(subsystem, displayName);
|
|
40605
|
-
const childDir =
|
|
40846
|
+
const childDir = path65.resolve(getProjectRoot(), options.projectPath);
|
|
40606
40847
|
logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
40607
|
-
logger.info(`Scaffolded child project at ${
|
|
40848
|
+
logger.info(`Scaffolded child project at ${path65.relative(process.cwd(), childDir) || "."}`);
|
|
40608
40849
|
logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
|
|
40609
40850
|
}
|
|
40610
40851
|
async function runSubsystemMove(id, options = {}) {
|
|
@@ -40627,9 +40868,9 @@ async function runSubsystemExternalize(id, options = {}) {
|
|
|
40627
40868
|
throw new WaironError("--project-path (the subproject destination) is required.");
|
|
40628
40869
|
}
|
|
40629
40870
|
externalizeSubsystem(id, options.projectPath);
|
|
40630
|
-
const childDir =
|
|
40871
|
+
const childDir = path65.resolve(getProjectRoot(), options.projectPath);
|
|
40631
40872
|
logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
40632
|
-
logger.info(`Moved its specs into ${
|
|
40873
|
+
logger.info(`Moved its specs into ${path65.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
|
|
40633
40874
|
logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
|
|
40634
40875
|
}
|
|
40635
40876
|
async function runSubsystemInternalize(id) {
|
|
@@ -40873,7 +41114,7 @@ async function runAgent(action, id) {
|
|
|
40873
41114
|
}
|
|
40874
41115
|
case "customize": {
|
|
40875
41116
|
const brief = composeAgentBrief3(id);
|
|
40876
|
-
const guidancePath =
|
|
41117
|
+
const guidancePath = path66.join(AI_PATHS.root(), "agents", `${id}.md`);
|
|
40877
41118
|
const relPath = `.wai/agents/${id}.md`;
|
|
40878
41119
|
if (pathExists(guidancePath)) {
|
|
40879
41120
|
throw new GuidanceFileExistsError(relPath);
|
|
@@ -40976,9 +41217,6 @@ hostCmd.command("key <action>").description("mint | list | revoke an API key").o
|
|
|
40976
41217
|
hostCmd.command("lock").description("validate-as-complete and write the state-scoped lock record for a hosted project").requiredOption("--project <id>", "project id").option("--data-dir <path>", "data root").action(async (opts) => {
|
|
40977
41218
|
await runHostLock({ project: opts.project, dataDir: opts.dataDir });
|
|
40978
41219
|
});
|
|
40979
|
-
hostCmd.command("promote").description("promote a locked project after re-checking its StateId (never merges to production)").requiredOption("--project <id>", "project id").option("--data-dir <path>", "data root").action(async (opts) => {
|
|
40980
|
-
await runHostPromote({ project: opts.project, dataDir: opts.dataDir });
|
|
40981
|
-
});
|
|
40982
41220
|
hostCmd.command("git <action>").description("enable | disable | sync | commit | status | sync-config \u2014 bind a project to its REAL repo (wairon commits ONLY .wai/)").option("--project <id>", "project id").option("--remote <url>", "git remote URL (for enable)").option("--branch <name>", "default branch PRs target (for enable)", "main").option("--subsystem <id>", "narrow a commit to .wai/specs/<subsystem>/ (staging convenience \u2014 history stays per-repo)").option("-m, --message <message>", "commit message (for commit)").option("--interval <minutes>", "periodic-sync interval in minutes (for sync-config; omit to disable)").option("--no-skip-if-clean", "commit on every periodic tick even when the scoped path is clean (for sync-config)").option("--data-dir <path>", "data root").action(async (action, opts) => {
|
|
40983
41221
|
await runHostGit(action, {
|
|
40984
41222
|
project: opts.project,
|