@wairon/cli 5.1.1-dev.6 → 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 +1034 -870
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +404 -240
- 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;
|
|
@@ -16611,7 +16790,7 @@ var init_specs2 = __esm({
|
|
|
16611
16790
|
if (!occupantId) return;
|
|
16612
16791
|
if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
|
|
16613
16792
|
throw new Error(
|
|
16614
|
-
`Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${
|
|
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.`
|
|
16615
16794
|
);
|
|
16616
16795
|
}
|
|
16617
16796
|
/** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
|
|
@@ -16619,12 +16798,12 @@ var init_specs2 = __esm({
|
|
|
16619
16798
|
const notices = [];
|
|
16620
16799
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
16621
16800
|
this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
|
|
16622
|
-
ensureDir(
|
|
16801
|
+
ensureDir(path19.dirname(p));
|
|
16623
16802
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
16624
16803
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
16625
16804
|
if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
|
|
16626
16805
|
notices.push(
|
|
16627
|
-
`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.`
|
|
16628
16807
|
);
|
|
16629
16808
|
}
|
|
16630
16809
|
if (existing) {
|
|
@@ -16647,9 +16826,9 @@ var init_specs2 = __esm({
|
|
|
16647
16826
|
}
|
|
16648
16827
|
deleteInterfaceSpec(id) {
|
|
16649
16828
|
const p = this.getInterfacePath(id);
|
|
16650
|
-
if (!
|
|
16651
|
-
|
|
16652
|
-
cleanEmptyDirs(p,
|
|
16829
|
+
if (!fs14.existsSync(p)) return false;
|
|
16830
|
+
fs14.unlinkSync(p);
|
|
16831
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16653
16832
|
invalidateSpecCache();
|
|
16654
16833
|
return true;
|
|
16655
16834
|
}
|
|
@@ -16683,12 +16862,12 @@ var init_specs2 = __esm({
|
|
|
16683
16862
|
const notices = [];
|
|
16684
16863
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
16685
16864
|
this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
|
|
16686
|
-
ensureDir(
|
|
16865
|
+
ensureDir(path19.dirname(p));
|
|
16687
16866
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
16688
16867
|
const existing = this.loadImplementationSpec(spec.id);
|
|
16689
16868
|
if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
|
|
16690
16869
|
notices.push(
|
|
16691
|
-
`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.`
|
|
16692
16871
|
);
|
|
16693
16872
|
}
|
|
16694
16873
|
if (existing) {
|
|
@@ -16704,9 +16883,9 @@ var init_specs2 = __esm({
|
|
|
16704
16883
|
}
|
|
16705
16884
|
deleteImplementationSpec(id) {
|
|
16706
16885
|
const p = this.getImplementationPath(id);
|
|
16707
|
-
if (!
|
|
16708
|
-
|
|
16709
|
-
cleanEmptyDirs(p,
|
|
16886
|
+
if (!fs14.existsSync(p)) return false;
|
|
16887
|
+
fs14.unlinkSync(p);
|
|
16888
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16710
16889
|
invalidateSpecCache();
|
|
16711
16890
|
return true;
|
|
16712
16891
|
}
|
|
@@ -16731,11 +16910,11 @@ var init_specs2 = __esm({
|
|
|
16731
16910
|
const existing = this.loadTypeSpec(spec.id);
|
|
16732
16911
|
const group = spec.group || (existing ? existing.group : void 0);
|
|
16733
16912
|
const p = this.getTypePath(spec.id, spec.subsystem, group);
|
|
16734
|
-
ensureDir(
|
|
16913
|
+
ensureDir(path19.dirname(p));
|
|
16735
16914
|
const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
|
|
16736
16915
|
if (subsystemChanged) {
|
|
16737
16916
|
notices.push(
|
|
16738
|
-
`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)"}").`
|
|
16739
16918
|
);
|
|
16740
16919
|
}
|
|
16741
16920
|
if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
|
|
@@ -16758,9 +16937,9 @@ var init_specs2 = __esm({
|
|
|
16758
16937
|
deleteTypeSpec(id) {
|
|
16759
16938
|
const spec = this.loadTypeSpec(id);
|
|
16760
16939
|
const p = this.getTypePath(id, spec?.subsystem, spec?.group);
|
|
16761
|
-
if (!
|
|
16762
|
-
|
|
16763
|
-
cleanEmptyDirs(p,
|
|
16940
|
+
if (!fs14.existsSync(p)) return false;
|
|
16941
|
+
fs14.unlinkSync(p);
|
|
16942
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16764
16943
|
invalidateSpecCache();
|
|
16765
16944
|
return true;
|
|
16766
16945
|
}
|
|
@@ -16775,7 +16954,7 @@ var init_specs2 = __esm({
|
|
|
16775
16954
|
}
|
|
16776
16955
|
saveGroupSpec(spec) {
|
|
16777
16956
|
const p = this.getGroupPath(spec.id);
|
|
16778
|
-
ensureDir(
|
|
16957
|
+
ensureDir(path19.dirname(p));
|
|
16779
16958
|
const specToWrite = this.prepareGroupForWrite(spec);
|
|
16780
16959
|
const existing = this.loadGroupSpec(spec.id);
|
|
16781
16960
|
if (existing) {
|
|
@@ -16787,9 +16966,9 @@ var init_specs2 = __esm({
|
|
|
16787
16966
|
}
|
|
16788
16967
|
deleteGroupSpec(id) {
|
|
16789
16968
|
const p = this.getGroupPath(id);
|
|
16790
|
-
if (!
|
|
16791
|
-
|
|
16792
|
-
cleanEmptyDirs(p,
|
|
16969
|
+
if (!fs14.existsSync(p)) return false;
|
|
16970
|
+
fs14.unlinkSync(p);
|
|
16971
|
+
cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
|
|
16793
16972
|
invalidateSpecCache();
|
|
16794
16973
|
return true;
|
|
16795
16974
|
}
|
|
@@ -16837,6 +17016,50 @@ var init_specs2 = __esm({
|
|
|
16837
17016
|
}
|
|
16838
17017
|
return out;
|
|
16839
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
|
+
}
|
|
16840
17063
|
/** Set a single spec's status (bumps updatedAt). Caller invalidates the cache. */
|
|
16841
17064
|
applySpecStatus(kind, id, status2) {
|
|
16842
17065
|
switch (kind) {
|
|
@@ -16874,16 +17097,16 @@ var init_specs2 = __esm({
|
|
|
16874
17097
|
const files = /* @__PURE__ */ new Set();
|
|
16875
17098
|
const sysPath = this.paths.specsSystem();
|
|
16876
17099
|
if (pathExists(sysPath)) {
|
|
16877
|
-
files.add(
|
|
17100
|
+
files.add(path19.resolve(sysPath));
|
|
16878
17101
|
}
|
|
16879
17102
|
for (const group of Object.values(index.paths)) {
|
|
16880
17103
|
for (const file of Object.values(group)) {
|
|
16881
|
-
files.add(
|
|
17104
|
+
files.add(path19.resolve(file));
|
|
16882
17105
|
}
|
|
16883
17106
|
}
|
|
16884
17107
|
for (const file of files) {
|
|
16885
|
-
if (
|
|
16886
|
-
snapshot.set(file,
|
|
17108
|
+
if (fs14.existsSync(file)) {
|
|
17109
|
+
snapshot.set(file, fs14.readFileSync(file, "utf8"));
|
|
16887
17110
|
}
|
|
16888
17111
|
}
|
|
16889
17112
|
return snapshot;
|
|
@@ -16897,20 +17120,20 @@ var init_specs2 = __esm({
|
|
|
16897
17120
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
16898
17121
|
const legacy = [];
|
|
16899
17122
|
for (const f of files) {
|
|
16900
|
-
const base =
|
|
16901
|
-
const dir =
|
|
17123
|
+
const base = path19.basename(f);
|
|
17124
|
+
const dir = path19.dirname(f);
|
|
16902
17125
|
if (base === "system.yaml") {
|
|
16903
|
-
legacy.push({ path: f, expected:
|
|
17126
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16904
17127
|
} else if (base === "subsystem.yaml") {
|
|
16905
|
-
legacy.push({ path: f, expected:
|
|
17128
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16906
17129
|
} else if (base === "component.yaml") {
|
|
16907
|
-
legacy.push({ path: f, expected:
|
|
17130
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16908
17131
|
} else if (base === "group.yaml") {
|
|
16909
|
-
legacy.push({ path: f, expected:
|
|
17132
|
+
legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
|
|
16910
17133
|
} else if (base === "interface.yaml") {
|
|
16911
|
-
legacy.push({ path: f, expected:
|
|
17134
|
+
legacy.push({ path: f, expected: path19.join(dir, ".interface.yaml") });
|
|
16912
17135
|
} else if (base === "implementation.yaml") {
|
|
16913
|
-
legacy.push({ path: f, expected:
|
|
17136
|
+
legacy.push({ path: f, expected: path19.join(dir, ".implementation.yaml") });
|
|
16914
17137
|
}
|
|
16915
17138
|
}
|
|
16916
17139
|
return legacy;
|
|
@@ -17405,7 +17628,7 @@ function provisionProject(name) {
|
|
|
17405
17628
|
function ensureProjectInitialized(fallbackName) {
|
|
17406
17629
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17407
17630
|
const paths = aiPathsAt(getProjectRoot());
|
|
17408
|
-
const hasSystem =
|
|
17631
|
+
const hasSystem = fs15.existsSync(paths.specsSystem());
|
|
17409
17632
|
let name = fallbackName;
|
|
17410
17633
|
if (hasSystem) {
|
|
17411
17634
|
const existing = loadSystemSpec();
|
|
@@ -17413,7 +17636,7 @@ function ensureProjectInitialized(fallbackName) {
|
|
|
17413
17636
|
}
|
|
17414
17637
|
let wroteConfig = false;
|
|
17415
17638
|
let wroteSystem = false;
|
|
17416
|
-
if (!
|
|
17639
|
+
if (!fs15.existsSync(paths.projectConfig())) {
|
|
17417
17640
|
saveProjectConfig(defaultProjectConfig(name, now));
|
|
17418
17641
|
wroteConfig = true;
|
|
17419
17642
|
}
|
|
@@ -17442,11 +17665,11 @@ function promoteAllComplete() {
|
|
|
17442
17665
|
function walkChainedSubprojects(projectRoot2, onChild) {
|
|
17443
17666
|
const visited = /* @__PURE__ */ new Set();
|
|
17444
17667
|
const walk = (dir) => {
|
|
17445
|
-
const resolved =
|
|
17668
|
+
const resolved = path20.resolve(dir);
|
|
17446
17669
|
if (visited.has(resolved)) return;
|
|
17447
17670
|
visited.add(resolved);
|
|
17448
17671
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
17449
|
-
if (!
|
|
17672
|
+
if (!fs15.existsSync(specsDir)) return;
|
|
17450
17673
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17451
17674
|
let raw;
|
|
17452
17675
|
try {
|
|
@@ -17464,7 +17687,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
17464
17687
|
continue;
|
|
17465
17688
|
}
|
|
17466
17689
|
const id = raw.id;
|
|
17467
|
-
onChild(childDir, typeof id === "string" ? id :
|
|
17690
|
+
onChild(childDir, typeof id === "string" ? id : path20.basename(childDir));
|
|
17468
17691
|
walk(childDir);
|
|
17469
17692
|
}
|
|
17470
17693
|
};
|
|
@@ -17473,7 +17696,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
|
|
|
17473
17696
|
function listDirectChainedSubprojects(projectRoot2) {
|
|
17474
17697
|
const out = [];
|
|
17475
17698
|
const specsDir = aiPathsAt(projectRoot2).specsDir();
|
|
17476
|
-
if (!
|
|
17699
|
+
if (!fs15.existsSync(specsDir)) return out;
|
|
17477
17700
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17478
17701
|
let raw;
|
|
17479
17702
|
try {
|
|
@@ -17491,12 +17714,12 @@ function listDirectChainedSubprojects(projectRoot2) {
|
|
|
17491
17714
|
continue;
|
|
17492
17715
|
}
|
|
17493
17716
|
const id = raw.id;
|
|
17494
|
-
out.push({ dir, subsystemId: typeof id === "string" ? id :
|
|
17717
|
+
out.push({ dir, subsystemId: typeof id === "string" ? id : path20.basename(dir) });
|
|
17495
17718
|
}
|
|
17496
17719
|
return out;
|
|
17497
17720
|
}
|
|
17498
17721
|
function childHasSpecsButNoConfig(childDir) {
|
|
17499
|
-
return
|
|
17722
|
+
return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !fs15.existsSync(aiPathsAt(childDir).projectConfig());
|
|
17500
17723
|
}
|
|
17501
17724
|
function findChainingSubprojectsMissingConfig(projectRoot2) {
|
|
17502
17725
|
const missing = [];
|
|
@@ -17545,14 +17768,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
|
|
|
17545
17768
|
const oldDir = assertContainedProjectPath(root, sub.projectPath);
|
|
17546
17769
|
const newDir = assertContainedProjectPath(root, nextPath);
|
|
17547
17770
|
if (oldDir !== newDir) {
|
|
17548
|
-
if (!
|
|
17771
|
+
if (!fs15.existsSync(oldDir)) {
|
|
17549
17772
|
throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
|
|
17550
17773
|
}
|
|
17551
|
-
if (
|
|
17774
|
+
if (fs15.existsSync(newDir)) {
|
|
17552
17775
|
throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
17553
17776
|
}
|
|
17554
|
-
ensureDir(
|
|
17555
|
-
|
|
17777
|
+
ensureDir(path20.dirname(newDir));
|
|
17778
|
+
fs15.renameSync(oldDir, newDir);
|
|
17556
17779
|
}
|
|
17557
17780
|
saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17558
17781
|
invalidateSpecCache();
|
|
@@ -17561,9 +17784,9 @@ function toPosixPath(p) {
|
|
|
17561
17784
|
return p.replace(/\\/g, "/");
|
|
17562
17785
|
}
|
|
17563
17786
|
function isWithinDir(dir, file) {
|
|
17564
|
-
const d =
|
|
17565
|
-
const f =
|
|
17566
|
-
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);
|
|
17567
17790
|
}
|
|
17568
17791
|
function externalizeSubsystem(subsystemId, projectPath) {
|
|
17569
17792
|
if (subsystemId.includes("::")) {
|
|
@@ -17575,14 +17798,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
17575
17798
|
}
|
|
17576
17799
|
const parentRoot = getProjectRoot();
|
|
17577
17800
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
17578
|
-
const fooDir =
|
|
17579
|
-
if (!
|
|
17801
|
+
const fooDir = path20.join(parentSpecsDir, subsystemId);
|
|
17802
|
+
if (!fs15.existsSync(fooDir)) {
|
|
17580
17803
|
throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
|
|
17581
17804
|
}
|
|
17582
17805
|
const relPath = toPosixPath(projectPath);
|
|
17583
17806
|
const childDir = assertContainedProjectPath(parentRoot, relPath);
|
|
17584
|
-
const childFooDir =
|
|
17585
|
-
if (
|
|
17807
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
17808
|
+
if (fs15.existsSync(childFooDir)) {
|
|
17586
17809
|
throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
|
|
17587
17810
|
}
|
|
17588
17811
|
const renameMap = buildRenameMap(
|
|
@@ -17592,17 +17815,17 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
17592
17815
|
);
|
|
17593
17816
|
const childSystemName = foo.name || subsystemId;
|
|
17594
17817
|
runWithProjectRoot(childDir, () => {
|
|
17595
|
-
ensureDir(
|
|
17818
|
+
ensureDir(path20.join(childDir, ".wai", "specs"));
|
|
17596
17819
|
provisionProject(childSystemName);
|
|
17597
17820
|
});
|
|
17598
|
-
ensureDir(
|
|
17599
|
-
|
|
17600
|
-
patchSubsystemIndex(
|
|
17821
|
+
ensureDir(path20.dirname(childFooDir));
|
|
17822
|
+
fs15.renameSync(fooDir, childFooDir);
|
|
17823
|
+
patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
|
|
17601
17824
|
s.parentSystem = childSystemName;
|
|
17602
17825
|
delete s.projectPath;
|
|
17603
17826
|
});
|
|
17604
17827
|
ensureDir(fooDir);
|
|
17605
|
-
writeYamlFile(
|
|
17828
|
+
writeYamlFile(path20.join(fooDir, ".index.yaml"), {
|
|
17606
17829
|
id: subsystemId,
|
|
17607
17830
|
name: foo.name,
|
|
17608
17831
|
description: foo.description,
|
|
@@ -17628,9 +17851,9 @@ function internalizeSubsystem(subsystemId) {
|
|
|
17628
17851
|
const parentRoot = getProjectRoot();
|
|
17629
17852
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
17630
17853
|
const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
|
|
17631
|
-
const childWai =
|
|
17632
|
-
const childFooDir =
|
|
17633
|
-
if (!
|
|
17854
|
+
const childWai = path20.join(childDir, ".wai");
|
|
17855
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
17856
|
+
if (!fs15.existsSync(childFooDir)) {
|
|
17634
17857
|
throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
|
|
17635
17858
|
}
|
|
17636
17859
|
const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
|
|
@@ -17643,15 +17866,15 @@ function internalizeSubsystem(subsystemId) {
|
|
|
17643
17866
|
false
|
|
17644
17867
|
);
|
|
17645
17868
|
const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
|
|
17646
|
-
const fooDir =
|
|
17647
|
-
|
|
17648
|
-
ensureDir(
|
|
17649
|
-
|
|
17650
|
-
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) => {
|
|
17651
17874
|
s.parentSystem = parentSystemName;
|
|
17652
17875
|
delete s.projectPath;
|
|
17653
17876
|
});
|
|
17654
|
-
|
|
17877
|
+
fs15.rmSync(childWai, { recursive: true, force: true });
|
|
17655
17878
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
17656
17879
|
invalidateSpecCache();
|
|
17657
17880
|
}
|
|
@@ -17748,18 +17971,18 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
17748
17971
|
}
|
|
17749
17972
|
}
|
|
17750
17973
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
17751
|
-
if (!
|
|
17974
|
+
if (!fs15.existsSync(indexPath)) return;
|
|
17752
17975
|
const raw = readYamlFile(indexPath);
|
|
17753
17976
|
mutate(raw);
|
|
17754
17977
|
raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17755
17978
|
writeYamlFile(indexPath, raw);
|
|
17756
17979
|
}
|
|
17757
|
-
var
|
|
17980
|
+
var fs15, path20;
|
|
17758
17981
|
var init_provision = __esm({
|
|
17759
17982
|
"src/core/provision.ts"() {
|
|
17760
17983
|
"use strict";
|
|
17761
|
-
|
|
17762
|
-
|
|
17984
|
+
fs15 = __toESM(require("fs"));
|
|
17985
|
+
path20 = __toESM(require("path"));
|
|
17763
17986
|
init_specs2();
|
|
17764
17987
|
init_loader();
|
|
17765
17988
|
init_fs();
|
|
@@ -17800,18 +18023,18 @@ __export(ai_guide_exports, {
|
|
|
17800
18023
|
writeRootGuideDelegator: () => writeRootGuideDelegator
|
|
17801
18024
|
});
|
|
17802
18025
|
function globalGuideFilePath(targetType) {
|
|
17803
|
-
if (targetType === "claude") return
|
|
17804
|
-
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");
|
|
17805
18028
|
return null;
|
|
17806
18029
|
}
|
|
17807
18030
|
function localGuideFilePath(projectRoot2, targetType) {
|
|
17808
|
-
if (targetType === "claude") return
|
|
17809
|
-
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");
|
|
17810
18033
|
return null;
|
|
17811
18034
|
}
|
|
17812
18035
|
function hasWaironGuide(filePath) {
|
|
17813
|
-
if (!
|
|
17814
|
-
return
|
|
18036
|
+
if (!fs16.existsSync(filePath)) return false;
|
|
18037
|
+
return fs16.readFileSync(filePath, "utf-8").includes(GUIDE_MARKER_START);
|
|
17815
18038
|
}
|
|
17816
18039
|
function injectGuide(filePath, scope) {
|
|
17817
18040
|
const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
|
|
@@ -17822,11 +18045,11 @@ ${versionStamp()}
|
|
|
17822
18045
|
${body}
|
|
17823
18046
|
${GUIDE_MARKER_END}
|
|
17824
18047
|
`;
|
|
17825
|
-
const existing =
|
|
18048
|
+
const existing = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
|
|
17826
18049
|
const stripped = stripGuideSection(existing);
|
|
17827
18050
|
const newContent = stripped.trimEnd() + section;
|
|
17828
|
-
|
|
17829
|
-
|
|
18051
|
+
fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
18052
|
+
fs16.writeFileSync(filePath, newContent, "utf-8");
|
|
17830
18053
|
}
|
|
17831
18054
|
function stripGuideSection(content) {
|
|
17832
18055
|
const start = content.indexOf(GUIDE_MARKER_START);
|
|
@@ -17836,7 +18059,7 @@ function stripGuideSection(content) {
|
|
|
17836
18059
|
}
|
|
17837
18060
|
function writeRootGuideDelegator(projectRoot2, targetType) {
|
|
17838
18061
|
if (targetType === "claude") {
|
|
17839
|
-
const filePath =
|
|
18062
|
+
const filePath = path21.join(projectRoot2, "CLAUDE.md");
|
|
17840
18063
|
const content = `@.claude/CLAUDE.md
|
|
17841
18064
|
|
|
17842
18065
|
# Wairon SDD Project
|
|
@@ -17849,42 +18072,42 @@ To design or modify the system, invoke the **\`sdd-architect\`** skill
|
|
|
17849
18072
|
(in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
|
|
17850
18073
|
the \`wairon\` CLI is the human developer's tool, not yours.
|
|
17851
18074
|
`;
|
|
17852
|
-
|
|
18075
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17853
18076
|
} else if (targetType === "gemini" || targetType === "agy") {
|
|
17854
|
-
const filePath =
|
|
18077
|
+
const filePath = path21.join(projectRoot2, "GEMINI.md");
|
|
17855
18078
|
const content = `# Wairon SDD Project
|
|
17856
18079
|
${GUIDE_MARKER_START}
|
|
17857
18080
|
${versionStamp()}
|
|
17858
18081
|
${LOCAL_GUIDE_BODY}
|
|
17859
18082
|
${GUIDE_MARKER_END}
|
|
17860
18083
|
`;
|
|
17861
|
-
|
|
18084
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17862
18085
|
} else if (targetType === "cursor") {
|
|
17863
|
-
const filePath =
|
|
18086
|
+
const filePath = path21.join(projectRoot2, ".cursorrules");
|
|
17864
18087
|
const content = `# Wairon SDD Project
|
|
17865
18088
|
|
|
17866
18089
|
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
17867
18090
|
|
|
17868
18091
|
Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
17869
18092
|
`;
|
|
17870
|
-
|
|
18093
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17871
18094
|
} else if (targetType === "copilot") {
|
|
17872
|
-
const filePath =
|
|
17873
|
-
|
|
18095
|
+
const filePath = path21.join(projectRoot2, ".github", "copilot-instructions.md");
|
|
18096
|
+
fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
17874
18097
|
const content = `# Wairon SDD Project
|
|
17875
18098
|
|
|
17876
18099
|
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
17877
18100
|
|
|
17878
18101
|
Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
|
|
17879
18102
|
`;
|
|
17880
|
-
|
|
18103
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17881
18104
|
} else if (targetType === "codex") {
|
|
17882
|
-
const filePath =
|
|
18105
|
+
const filePath = path21.join(projectRoot2, ".codexrules");
|
|
17883
18106
|
const content = `# Wairon SDD Project
|
|
17884
18107
|
|
|
17885
18108
|
Refer to [.codex/agents/](.codex/agents/) for full instructions.
|
|
17886
18109
|
`;
|
|
17887
|
-
|
|
18110
|
+
fs16.writeFileSync(filePath, content, "utf-8");
|
|
17888
18111
|
}
|
|
17889
18112
|
}
|
|
17890
18113
|
function reinjectLocalGuides(projectRoot2, targetTypes) {
|
|
@@ -17900,13 +18123,13 @@ function reinjectLocalGuides(projectRoot2, targetTypes) {
|
|
|
17900
18123
|
}
|
|
17901
18124
|
return written;
|
|
17902
18125
|
}
|
|
17903
|
-
var
|
|
18126
|
+
var fs16, os6, path21, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
|
|
17904
18127
|
var init_ai_guide = __esm({
|
|
17905
18128
|
"src/utils/ai-guide.ts"() {
|
|
17906
18129
|
"use strict";
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
18130
|
+
fs16 = __toESM(require("fs"));
|
|
18131
|
+
os6 = __toESM(require("os"));
|
|
18132
|
+
path21 = __toESM(require("path"));
|
|
17910
18133
|
init_stamp();
|
|
17911
18134
|
GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
|
|
17912
18135
|
GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
|
|
@@ -17968,7 +18191,7 @@ __export(domains_exports, {
|
|
|
17968
18191
|
resolveDomains: () => resolveDomains
|
|
17969
18192
|
});
|
|
17970
18193
|
function rel(p) {
|
|
17971
|
-
return
|
|
18194
|
+
return path26.relative(process.cwd(), p).replace(/\\/g, "/");
|
|
17972
18195
|
}
|
|
17973
18196
|
function deriveSubsystemDomains() {
|
|
17974
18197
|
const subsystems = loadSubsystemSpecs();
|
|
@@ -18018,11 +18241,11 @@ function removeFreeStandingDomain(id) {
|
|
|
18018
18241
|
config.domains.splice(idx, 1);
|
|
18019
18242
|
saveTopologyConfig(config);
|
|
18020
18243
|
}
|
|
18021
|
-
var
|
|
18244
|
+
var path26;
|
|
18022
18245
|
var init_domains = __esm({
|
|
18023
18246
|
"src/core/domains.ts"() {
|
|
18024
18247
|
"use strict";
|
|
18025
|
-
|
|
18248
|
+
path26 = __toESM(require("path"));
|
|
18026
18249
|
init_loader();
|
|
18027
18250
|
init_specs2();
|
|
18028
18251
|
init_errors();
|
|
@@ -18178,16 +18401,16 @@ function packNamespace(pack) {
|
|
|
18178
18401
|
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
18179
18402
|
}
|
|
18180
18403
|
function extensionsFor(builtin, packSkills) {
|
|
18181
|
-
return packSkills.filter((s) => s.extends === builtin &&
|
|
18404
|
+
return packSkills.filter((s) => s.extends === builtin && fs18.existsSync(s.sourcePath));
|
|
18182
18405
|
}
|
|
18183
18406
|
function composeBuiltinSkill(name, packSkills) {
|
|
18184
18407
|
const srcPath = skillTemplatePath(name);
|
|
18185
|
-
const base =
|
|
18408
|
+
const base = fs18.existsSync(srcPath) ? fs18.readFileSync(srcPath, "utf-8") : "";
|
|
18186
18409
|
const sections = extensionsFor(name, packSkills);
|
|
18187
18410
|
if (sections.length === 0) return base;
|
|
18188
18411
|
const parts = [base.trimEnd()];
|
|
18189
18412
|
for (const section of sections) {
|
|
18190
|
-
const body =
|
|
18413
|
+
const body = fs18.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
18191
18414
|
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
18192
18415
|
}
|
|
18193
18416
|
return `${parts.join("\n\n")}
|
|
@@ -18203,16 +18426,16 @@ function readFrontmatter(raw, fallbackName) {
|
|
|
18203
18426
|
return { name: field("name") || fallbackName, description: field("description") };
|
|
18204
18427
|
}
|
|
18205
18428
|
function builtinSkillsDir() {
|
|
18206
|
-
return
|
|
18429
|
+
return path27.resolve(__dirname, "..", "templates", "skills");
|
|
18207
18430
|
}
|
|
18208
18431
|
function skillTemplatePath(name) {
|
|
18209
|
-
return
|
|
18432
|
+
return path27.join(builtinSkillsDir(), `${name}.md`);
|
|
18210
18433
|
}
|
|
18211
18434
|
function skillDestPath(type, destDir, name) {
|
|
18212
18435
|
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
18213
|
-
return
|
|
18436
|
+
return path27.join(destDir, name, "SKILL.md");
|
|
18214
18437
|
}
|
|
18215
|
-
return
|
|
18438
|
+
return path27.join(destDir, `${name}.md`);
|
|
18216
18439
|
}
|
|
18217
18440
|
function skillsDirForTarget(type) {
|
|
18218
18441
|
switch (type) {
|
|
@@ -18247,22 +18470,22 @@ function exportSddSkills(targetTypes) {
|
|
|
18247
18470
|
ensureDir(destDir);
|
|
18248
18471
|
destinations.push(destDir);
|
|
18249
18472
|
for (const name of SKILL_NAMES) {
|
|
18250
|
-
if (!
|
|
18473
|
+
if (!fs18.existsSync(skillTemplatePath(name))) continue;
|
|
18251
18474
|
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
18252
18475
|
const destPath = skillDestPath(type, destDir, name);
|
|
18253
|
-
ensureDir(
|
|
18254
|
-
|
|
18476
|
+
ensureDir(path27.dirname(destPath));
|
|
18477
|
+
fs18.writeFileSync(destPath, content, "utf-8");
|
|
18255
18478
|
fileCount++;
|
|
18256
18479
|
}
|
|
18257
18480
|
for (const skill of packSkills) {
|
|
18258
18481
|
if (skill.extends !== void 0) continue;
|
|
18259
18482
|
if (!skill.targets.includes(type)) continue;
|
|
18260
|
-
if (!
|
|
18483
|
+
if (!fs18.existsSync(skill.sourcePath)) continue;
|
|
18261
18484
|
const id = packSkillId(skill);
|
|
18262
|
-
const content =
|
|
18485
|
+
const content = fs18.readFileSync(skill.sourcePath, "utf-8");
|
|
18263
18486
|
const destPath = skillDestPath(type, destDir, id);
|
|
18264
|
-
ensureDir(
|
|
18265
|
-
|
|
18487
|
+
ensureDir(path27.dirname(destPath));
|
|
18488
|
+
fs18.writeFileSync(destPath, content, "utf-8");
|
|
18266
18489
|
fileCount++;
|
|
18267
18490
|
}
|
|
18268
18491
|
}
|
|
@@ -18275,12 +18498,12 @@ function checkSkillFreshness(type) {
|
|
|
18275
18498
|
const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
|
|
18276
18499
|
for (const name of SKILL_NAMES) {
|
|
18277
18500
|
const destPath = skillDestPath(type, dir, name);
|
|
18278
|
-
if (!
|
|
18501
|
+
if (!fs18.existsSync(destPath)) {
|
|
18279
18502
|
result.missing.push(name);
|
|
18280
18503
|
continue;
|
|
18281
18504
|
}
|
|
18282
18505
|
const want = composeBuiltinSkill(name, packSkills);
|
|
18283
|
-
const have =
|
|
18506
|
+
const have = fs18.readFileSync(destPath, "utf-8");
|
|
18284
18507
|
if (have === want) result.ok.push(name);
|
|
18285
18508
|
else result.stale.push(name);
|
|
18286
18509
|
}
|
|
@@ -18292,7 +18515,7 @@ function activeTargetTypes() {
|
|
|
18292
18515
|
return config.targets.filter((t) => !("enabled" in t) || t.enabled).map((t) => typeof t === "string" ? t : t.type);
|
|
18293
18516
|
}
|
|
18294
18517
|
function readSkillFrontmatter(name) {
|
|
18295
|
-
return readFrontmatter(
|
|
18518
|
+
return readFrontmatter(fs18.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
18296
18519
|
}
|
|
18297
18520
|
function listSkillResources() {
|
|
18298
18521
|
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
@@ -18306,9 +18529,9 @@ function listSkillResources() {
|
|
|
18306
18529
|
defaultForHostedMcp: true
|
|
18307
18530
|
};
|
|
18308
18531
|
});
|
|
18309
|
-
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) => {
|
|
18310
18533
|
const id = packSkillId(skill);
|
|
18311
|
-
const fm = readFrontmatter(
|
|
18534
|
+
const fm = readFrontmatter(fs18.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
18312
18535
|
return {
|
|
18313
18536
|
id,
|
|
18314
18537
|
name: fm.name,
|
|
@@ -18324,8 +18547,8 @@ function readSkillResource(resourceId) {
|
|
|
18324
18547
|
const packSkills = loadProjectExtensions2().skills;
|
|
18325
18548
|
if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
|
|
18326
18549
|
const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
|
|
18327
|
-
if (packSkill) return
|
|
18328
|
-
return
|
|
18550
|
+
if (packSkill) return fs18.readFileSync(packSkill.sourcePath, "utf-8");
|
|
18551
|
+
return fs18.readFileSync(skillTemplatePath(resourceId), "utf-8");
|
|
18329
18552
|
}
|
|
18330
18553
|
function listResources() {
|
|
18331
18554
|
return listSkillResources();
|
|
@@ -18338,12 +18561,12 @@ function readResource(resourceId) {
|
|
|
18338
18561
|
if (!known) throw new SkillResourceNotFoundError(resourceId);
|
|
18339
18562
|
return readSkillResource(resourceId);
|
|
18340
18563
|
}
|
|
18341
|
-
var
|
|
18564
|
+
var path27, fs18, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
|
|
18342
18565
|
var init_skills = __esm({
|
|
18343
18566
|
"src/core/skills.ts"() {
|
|
18344
18567
|
"use strict";
|
|
18345
|
-
|
|
18346
|
-
|
|
18568
|
+
path27 = __toESM(require("path"));
|
|
18569
|
+
fs18 = __toESM(require("fs"));
|
|
18347
18570
|
init_fs();
|
|
18348
18571
|
init_defaults();
|
|
18349
18572
|
init_extensions();
|
|
@@ -18570,23 +18793,34 @@ async function runStatus(options = {}) {
|
|
|
18570
18793
|
const lock = lockLine().trim();
|
|
18571
18794
|
if (lock) {
|
|
18572
18795
|
logger.blank();
|
|
18573
|
-
if (lock.includes("
|
|
18796
|
+
if (lock.includes("changed since approval")) logger.warn(lock);
|
|
18574
18797
|
else logger.info(lock);
|
|
18575
18798
|
}
|
|
18576
18799
|
logger.blank();
|
|
18577
18800
|
}
|
|
18578
18801
|
function lockLine() {
|
|
18579
18802
|
try {
|
|
18580
|
-
const
|
|
18581
|
-
if (
|
|
18582
|
-
|
|
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) {
|
|
18583
18810
|
return `
|
|
18584
|
-
|
|
18811
|
+
Approved: ${baseline.approvedAt} by ${baseline.approvedBy} \u2014 no spec has changed since.${childNote}
|
|
18585
18812
|
`;
|
|
18586
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;
|
|
18587
18820
|
return `
|
|
18588
|
-
|
|
18589
|
-
|
|
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";
|
|
18590
18824
|
} catch {
|
|
18591
18825
|
return "";
|
|
18592
18826
|
}
|
|
@@ -18713,6 +18947,7 @@ var init_status = __esm({
|
|
|
18713
18947
|
init_loader();
|
|
18714
18948
|
init_fs();
|
|
18715
18949
|
init_specs2();
|
|
18950
|
+
init_baseline();
|
|
18716
18951
|
}
|
|
18717
18952
|
});
|
|
18718
18953
|
|
|
@@ -18761,7 +18996,7 @@ function errText(message) {
|
|
|
18761
18996
|
}
|
|
18762
18997
|
function captureBuildStamp(entryPath) {
|
|
18763
18998
|
try {
|
|
18764
|
-
const s =
|
|
18999
|
+
const s = fs19.statSync(entryPath);
|
|
18765
19000
|
return { path: entryPath, mtimeMs: s.mtimeMs, size: s.size };
|
|
18766
19001
|
} catch {
|
|
18767
19002
|
return null;
|
|
@@ -18770,7 +19005,7 @@ function captureBuildStamp(entryPath) {
|
|
|
18770
19005
|
function isBuildStale(stamp) {
|
|
18771
19006
|
if (!stamp) return false;
|
|
18772
19007
|
try {
|
|
18773
|
-
const s =
|
|
19008
|
+
const s = fs19.statSync(stamp.path);
|
|
18774
19009
|
return s.mtimeMs !== stamp.mtimeMs || s.size !== stamp.size;
|
|
18775
19010
|
} catch {
|
|
18776
19011
|
return false;
|
|
@@ -19988,10 +20223,6 @@ NOTICE:
|
|
|
19988
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).",
|
|
19989
20224
|
inputSchema: {}
|
|
19990
20225
|
}, hostedStub);
|
|
19991
|
-
reg(server, "sdd_host_promote_project", {
|
|
19992
|
-
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.",
|
|
19993
|
-
inputSchema: {}
|
|
19994
|
-
}, hostedStub);
|
|
19995
20226
|
reg(server, "sdd_host_initialize_project", {
|
|
19996
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.",
|
|
19997
20228
|
inputSchema: {
|
|
@@ -20077,7 +20308,7 @@ async function scopeToClientWorkspace(server) {
|
|
|
20077
20308
|
} catch {
|
|
20078
20309
|
dir = null;
|
|
20079
20310
|
}
|
|
20080
|
-
if (dir && (
|
|
20311
|
+
if (dir && (fs19.existsSync(path29.join(dir, ".wai")) || fs19.existsSync(path29.join(dir, ".wairon")))) {
|
|
20081
20312
|
setProjectRoot(dir);
|
|
20082
20313
|
process.stderr.write(`[wairon mcp] scoped to client workspace root: ${dir}
|
|
20083
20314
|
`);
|
|
@@ -20101,7 +20332,7 @@ async function startMcpServer() {
|
|
|
20101
20332
|
} catch {
|
|
20102
20333
|
}
|
|
20103
20334
|
}
|
|
20104
|
-
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;
|
|
20105
20336
|
var init_server = __esm({
|
|
20106
20337
|
"src/mcp/server.ts"() {
|
|
20107
20338
|
"use strict";
|
|
@@ -20109,8 +20340,8 @@ var init_server = __esm({
|
|
|
20109
20340
|
import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
20110
20341
|
import_zod9 = require("zod");
|
|
20111
20342
|
import_types3 = require("@modelcontextprotocol/sdk/types.js");
|
|
20112
|
-
|
|
20113
|
-
|
|
20343
|
+
fs19 = __toESM(require("fs"));
|
|
20344
|
+
path29 = __toESM(require("path"));
|
|
20114
20345
|
import_url = require("url");
|
|
20115
20346
|
init_fs();
|
|
20116
20347
|
init_defaults();
|
|
@@ -20161,34 +20392,34 @@ __export(mcp_exports, {
|
|
|
20161
20392
|
validateConfigDir: () => validateConfigDir
|
|
20162
20393
|
});
|
|
20163
20394
|
function geminiGlobalDir(override) {
|
|
20164
|
-
return override || process.env["GEMINI_CONFIG_DIR"] ||
|
|
20395
|
+
return override || process.env["GEMINI_CONFIG_DIR"] || path30.join(os7.homedir(), ".gemini");
|
|
20165
20396
|
}
|
|
20166
20397
|
function claudeMcpConfigPath(useGlobal, override) {
|
|
20167
|
-
if (!useGlobal) return
|
|
20398
|
+
if (!useGlobal) return path30.join(process.cwd(), ".mcp.json");
|
|
20168
20399
|
const dir = override || process.env["CLAUDE_CONFIG_DIR"];
|
|
20169
|
-
return dir ?
|
|
20400
|
+
return dir ? path30.join(dir, ".claude.json") : path30.join(os7.homedir(), ".claude.json");
|
|
20170
20401
|
}
|
|
20171
20402
|
function validateConfigDir(dir, backend) {
|
|
20172
|
-
const resolved =
|
|
20403
|
+
const resolved = path30.resolve(dir);
|
|
20173
20404
|
const agent = backend === "claude" ? "Claude" : "Gemini/Antigravity";
|
|
20174
|
-
if (!
|
|
20175
|
-
const parent =
|
|
20176
|
-
if (!
|
|
20405
|
+
if (!fs20.existsSync(resolved)) {
|
|
20406
|
+
const parent = path30.dirname(resolved);
|
|
20407
|
+
if (!fs20.existsSync(parent)) {
|
|
20177
20408
|
throw new WaironError(`--config-dir "${dir}" does not exist and its parent is missing \u2014 check the path.`);
|
|
20178
20409
|
}
|
|
20179
20410
|
logger.warn(`Config dir "${resolved}" does not exist yet; it will be created.`);
|
|
20180
20411
|
return;
|
|
20181
20412
|
}
|
|
20182
|
-
if (!
|
|
20413
|
+
if (!fs20.statSync(resolved).isDirectory()) {
|
|
20183
20414
|
throw new WaironError(`--config-dir "${dir}" is not a directory.`);
|
|
20184
20415
|
}
|
|
20185
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"];
|
|
20186
|
-
const entries =
|
|
20417
|
+
const entries = fs20.readdirSync(resolved);
|
|
20187
20418
|
if (entries.length === 0) {
|
|
20188
20419
|
logger.warn(`Config dir "${resolved}" is empty; proceeding (treating it as a fresh ${agent} config dir).`);
|
|
20189
20420
|
return;
|
|
20190
20421
|
}
|
|
20191
|
-
if (!markers.some((m) =>
|
|
20422
|
+
if (!markers.some((m) => fs20.existsSync(path30.join(resolved, m)))) {
|
|
20192
20423
|
throw new WaironError(
|
|
20193
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.`
|
|
20194
20425
|
);
|
|
@@ -20196,12 +20427,12 @@ function validateConfigDir(dir, backend) {
|
|
|
20196
20427
|
}
|
|
20197
20428
|
async function runMcpServe() {
|
|
20198
20429
|
const { setProjectRoot: setProjectRoot2, getProjectRoot: getProjectRoot2, findProjectRoot: findProjectRoot2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
20199
|
-
const cwd =
|
|
20430
|
+
const cwd = path30.resolve(process.cwd());
|
|
20200
20431
|
const envDir = process.env["WAIRON_PROJECT_DIR"];
|
|
20201
20432
|
let resolved;
|
|
20202
20433
|
let how;
|
|
20203
|
-
if (envDir &&
|
|
20204
|
-
resolved =
|
|
20434
|
+
if (envDir && fs20.existsSync(path30.join(envDir, ".wai"))) {
|
|
20435
|
+
resolved = path30.resolve(envDir);
|
|
20205
20436
|
how = "WAIRON_PROJECT_DIR (pinned at install)";
|
|
20206
20437
|
} else {
|
|
20207
20438
|
const found = findProjectRoot2(cwd);
|
|
@@ -20264,20 +20495,20 @@ async function runMcpInstall(options = {}) {
|
|
|
20264
20495
|
let settingsPath;
|
|
20265
20496
|
if (backend === "gemini") {
|
|
20266
20497
|
if (useGlobal) {
|
|
20267
|
-
configBase =
|
|
20268
|
-
settingsPath =
|
|
20498
|
+
configBase = path30.join(geminiGlobalDir(options.configDir), "antigravity-cli");
|
|
20499
|
+
settingsPath = path30.join(configBase, "mcp_config.json");
|
|
20269
20500
|
} else {
|
|
20270
|
-
configBase =
|
|
20271
|
-
settingsPath =
|
|
20501
|
+
configBase = path30.join(process.cwd(), ".gemini");
|
|
20502
|
+
settingsPath = path30.join(configBase, "settings.json");
|
|
20272
20503
|
}
|
|
20273
20504
|
} else {
|
|
20274
20505
|
settingsPath = claudeMcpConfigPath(useGlobal, options.configDir);
|
|
20275
|
-
configBase =
|
|
20506
|
+
configBase = path30.dirname(settingsPath);
|
|
20276
20507
|
}
|
|
20277
20508
|
let settings = {};
|
|
20278
|
-
if (
|
|
20509
|
+
if (fs20.existsSync(settingsPath)) {
|
|
20279
20510
|
try {
|
|
20280
|
-
settings = JSON.parse(
|
|
20511
|
+
settings = JSON.parse(fs20.readFileSync(settingsPath, "utf8"));
|
|
20281
20512
|
} catch {
|
|
20282
20513
|
logger.warn(`Could not parse ${settingsPath} \u2014 starting fresh.`);
|
|
20283
20514
|
}
|
|
@@ -20285,7 +20516,7 @@ async function runMcpInstall(options = {}) {
|
|
|
20285
20516
|
const mcpServers = settings["mcpServers"] ?? {};
|
|
20286
20517
|
const agentLabel = backend === "gemini" ? "Antigravity" : "Claude";
|
|
20287
20518
|
const isPackaged = typeof process.pkg !== "undefined";
|
|
20288
|
-
const scriptPath = process.argv[1] ?
|
|
20519
|
+
const scriptPath = process.argv[1] ? path30.resolve(process.argv[1]).replace(/\\/g, "/") : null;
|
|
20289
20520
|
const useDirectNode = !isPackaged && scriptPath && (scriptPath.endsWith(".js") || scriptPath.endsWith(".ts"));
|
|
20290
20521
|
const env = useGlobal ? {} : { WAIRON_PROJECT_DIR: process.cwd().replace(/\\/g, "/") };
|
|
20291
20522
|
const desiredEntry = useDirectNode ? { command: "node", args: [scriptPath, "mcp", "serve"], env } : { command: "wairon", args: ["mcp", "serve"], env };
|
|
@@ -20297,8 +20528,8 @@ async function runMcpInstall(options = {}) {
|
|
|
20297
20528
|
const wasStale = !!existingEntry;
|
|
20298
20529
|
mcpServers["wairon"] = desiredEntry;
|
|
20299
20530
|
settings["mcpServers"] = mcpServers;
|
|
20300
|
-
if (!
|
|
20301
|
-
|
|
20531
|
+
if (!fs20.existsSync(configBase)) fs20.mkdirSync(configBase, { recursive: true });
|
|
20532
|
+
fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
20302
20533
|
logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk4.default.cyan(settingsPath)}.`);
|
|
20303
20534
|
logger.blank();
|
|
20304
20535
|
logger.info("AI tools using this config will have access to these wairon tools:");
|
|
@@ -20323,8 +20554,8 @@ async function runMcpStatus() {
|
|
|
20323
20554
|
const projectConfig = loadProjectConfig();
|
|
20324
20555
|
const claudeProject = claudeMcpConfigPath(false);
|
|
20325
20556
|
const claudeGlobal = claudeMcpConfigPath(true);
|
|
20326
|
-
const geminiProject =
|
|
20327
|
-
const geminiGlobal =
|
|
20557
|
+
const geminiProject = path30.join(process.cwd(), ".gemini", "settings.json");
|
|
20558
|
+
const geminiGlobal = path30.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
|
|
20328
20559
|
logger.blank();
|
|
20329
20560
|
logger.info(`${import_chalk4.default.bold("wairon MCP Server")}`);
|
|
20330
20561
|
logger.blank();
|
|
@@ -20335,12 +20566,12 @@ async function runMcpStatus() {
|
|
|
20335
20566
|
{ label: "Antigravity (global)", filePath: geminiGlobal, fallbackName: "mcp_config.json" }
|
|
20336
20567
|
];
|
|
20337
20568
|
for (const { label, filePath, fallbackName } of checks) {
|
|
20338
|
-
if (!
|
|
20569
|
+
if (!fs20.existsSync(filePath)) {
|
|
20339
20570
|
console.log(` ${label}: ${import_chalk4.default.gray(`${fallbackName} not found`)}`);
|
|
20340
20571
|
continue;
|
|
20341
20572
|
}
|
|
20342
20573
|
try {
|
|
20343
|
-
const s = JSON.parse(
|
|
20574
|
+
const s = JSON.parse(fs20.readFileSync(filePath, "utf8"));
|
|
20344
20575
|
const registered = !!s["mcpServers"]?.["wairon"];
|
|
20345
20576
|
const mark = registered ? import_chalk4.default.green("\u2713 registered") : import_chalk4.default.gray("not registered");
|
|
20346
20577
|
console.log(` ${label}: ${mark} ${import_chalk4.default.gray(filePath)}`);
|
|
@@ -20356,16 +20587,16 @@ async function runMcpStatus() {
|
|
|
20356
20587
|
logger.info(`To start manually: ${import_chalk4.default.bold("wairon mcp serve")}`);
|
|
20357
20588
|
logger.blank();
|
|
20358
20589
|
const mcpDir = aiDir("mcp");
|
|
20359
|
-
if (
|
|
20590
|
+
if (fs20.existsSync(mcpDir)) {
|
|
20360
20591
|
logger.info(`MCP state dir: ${import_chalk4.default.gray(mcpDir)}`);
|
|
20361
20592
|
}
|
|
20362
20593
|
}
|
|
20363
20594
|
function removeLegacyGlobalPlugin() {
|
|
20364
|
-
const home = process.env["USERPROFILE"] ?? process.env["HOME"] ??
|
|
20365
|
-
const pluginDir =
|
|
20595
|
+
const home = process.env["USERPROFILE"] ?? process.env["HOME"] ?? os7.homedir();
|
|
20596
|
+
const pluginDir = path30.join(home, ".gemini", "config", "plugins", "wairon");
|
|
20366
20597
|
try {
|
|
20367
|
-
if (
|
|
20368
|
-
|
|
20598
|
+
if (fs20.existsSync(pluginDir)) {
|
|
20599
|
+
fs20.rmSync(pluginDir, { recursive: true, force: true });
|
|
20369
20600
|
logger.info(`Removed legacy global Antigravity plugin at ${import_chalk4.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
|
|
20370
20601
|
return true;
|
|
20371
20602
|
}
|
|
@@ -20374,13 +20605,13 @@ function removeLegacyGlobalPlugin() {
|
|
|
20374
20605
|
}
|
|
20375
20606
|
return false;
|
|
20376
20607
|
}
|
|
20377
|
-
var
|
|
20608
|
+
var fs20, os7, path30, import_chalk4;
|
|
20378
20609
|
var init_mcp = __esm({
|
|
20379
20610
|
"src/commands/mcp.ts"() {
|
|
20380
20611
|
"use strict";
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
20612
|
+
fs20 = __toESM(require("fs"));
|
|
20613
|
+
os7 = __toESM(require("os"));
|
|
20614
|
+
path30 = __toESM(require("path"));
|
|
20384
20615
|
import_chalk4 = __toESM(require("chalk"));
|
|
20385
20616
|
init_logger();
|
|
20386
20617
|
init_loader();
|
|
@@ -23215,8 +23446,8 @@ var require_dist = __commonJS({
|
|
|
23215
23446
|
function slug(name) {
|
|
23216
23447
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23217
23448
|
}
|
|
23218
|
-
var
|
|
23219
|
-
var
|
|
23449
|
+
var fs54 = __toESM2(require("fs"));
|
|
23450
|
+
var path67 = __toESM2(require("path"));
|
|
23220
23451
|
var import_fflate = require_node();
|
|
23221
23452
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
|
|
23222
23453
|
function listEntries(archive) {
|
|
@@ -23254,9 +23485,9 @@ var require_dist = __commonJS({
|
|
|
23254
23485
|
}
|
|
23255
23486
|
function writeTree(destDir, files) {
|
|
23256
23487
|
for (const file of files) {
|
|
23257
|
-
const absolute =
|
|
23258
|
-
|
|
23259
|
-
|
|
23488
|
+
const absolute = path67.join(destDir, file.path);
|
|
23489
|
+
fs54.mkdirSync(path67.dirname(absolute), { recursive: true });
|
|
23490
|
+
fs54.writeFileSync(absolute, file.contents);
|
|
23260
23491
|
}
|
|
23261
23492
|
}
|
|
23262
23493
|
function classifyKind(name, symlinks) {
|
|
@@ -23265,14 +23496,14 @@ var require_dist = __commonJS({
|
|
|
23265
23496
|
return "file";
|
|
23266
23497
|
}
|
|
23267
23498
|
function walkPackDir(root, current2, out) {
|
|
23268
|
-
for (const entry of
|
|
23499
|
+
for (const entry of fs54.readdirSync(current2, { withFileTypes: true })) {
|
|
23269
23500
|
if (entry.isDirectory()) {
|
|
23270
23501
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
23271
|
-
walkPackDir(root,
|
|
23502
|
+
walkPackDir(root, path67.join(current2, entry.name), out);
|
|
23272
23503
|
} else if (entry.isFile()) {
|
|
23273
|
-
const absolute =
|
|
23274
|
-
const relative22 =
|
|
23275
|
-
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) });
|
|
23276
23507
|
}
|
|
23277
23508
|
}
|
|
23278
23509
|
}
|
|
@@ -23441,7 +23672,7 @@ var require_dist = __commonJS({
|
|
|
23441
23672
|
});
|
|
23442
23673
|
|
|
23443
23674
|
// src/cli/index.ts
|
|
23444
|
-
var
|
|
23675
|
+
var path66 = __toESM(require("path"));
|
|
23445
23676
|
var import_commander = require("commander");
|
|
23446
23677
|
init_defaults();
|
|
23447
23678
|
init_logger();
|
|
@@ -23668,7 +23899,7 @@ function isSupportedAlias(name) {
|
|
|
23668
23899
|
}
|
|
23669
23900
|
|
|
23670
23901
|
// src/commands/init.ts
|
|
23671
|
-
var
|
|
23902
|
+
var path31 = __toESM(require("path"));
|
|
23672
23903
|
var import_chalk5 = __toESM(require("chalk"));
|
|
23673
23904
|
var import_inquirer = __toESM(require("inquirer"));
|
|
23674
23905
|
init_logger();
|
|
@@ -23689,7 +23920,7 @@ var WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
|
23689
23920
|
var WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
|
|
23690
23921
|
|
|
23691
23922
|
// src/exporters/claude.ts
|
|
23692
|
-
var
|
|
23923
|
+
var path22 = __toESM(require("path"));
|
|
23693
23924
|
init_fs();
|
|
23694
23925
|
var ClaudeExporter = class {
|
|
23695
23926
|
constructor() {
|
|
@@ -23698,7 +23929,7 @@ var ClaudeExporter = class {
|
|
|
23698
23929
|
outputPath(ctx) {
|
|
23699
23930
|
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23700
23931
|
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
23701
|
-
return
|
|
23932
|
+
return path22.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23702
23933
|
}
|
|
23703
23934
|
export(ctx) {
|
|
23704
23935
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -23719,7 +23950,7 @@ var ClaudeExporter = class {
|
|
|
23719
23950
|
};
|
|
23720
23951
|
|
|
23721
23952
|
// src/exporters/custom.ts
|
|
23722
|
-
var
|
|
23953
|
+
var path23 = __toESM(require("path"));
|
|
23723
23954
|
init_fs();
|
|
23724
23955
|
var CustomExporter = class {
|
|
23725
23956
|
constructor() {
|
|
@@ -23730,7 +23961,7 @@ var CustomExporter = class {
|
|
|
23730
23961
|
if (!("outputDir" in target)) {
|
|
23731
23962
|
throw new Error("CustomExporter requires target.outputDir");
|
|
23732
23963
|
}
|
|
23733
|
-
return
|
|
23964
|
+
return path23.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
23734
23965
|
}
|
|
23735
23966
|
export(ctx) {
|
|
23736
23967
|
const { agent, target, renderedInstructions } = ctx;
|
|
@@ -23753,7 +23984,7 @@ var CustomExporter = class {
|
|
|
23753
23984
|
};
|
|
23754
23985
|
|
|
23755
23986
|
// src/exporters/gemini.ts
|
|
23756
|
-
var
|
|
23987
|
+
var path24 = __toESM(require("path"));
|
|
23757
23988
|
init_fs();
|
|
23758
23989
|
var GeminiExporter = class {
|
|
23759
23990
|
constructor() {
|
|
@@ -23762,7 +23993,7 @@ var GeminiExporter = class {
|
|
|
23762
23993
|
outputPath(ctx) {
|
|
23763
23994
|
const { agent, target, projectRoot: projectRoot2 } = ctx;
|
|
23764
23995
|
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
23765
|
-
return
|
|
23996
|
+
return path24.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
23766
23997
|
}
|
|
23767
23998
|
export(ctx) {
|
|
23768
23999
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -23787,11 +24018,11 @@ function yamlString(value) {
|
|
|
23787
24018
|
}
|
|
23788
24019
|
|
|
23789
24020
|
// src/exporters/generate.ts
|
|
23790
|
-
var
|
|
24021
|
+
var path28 = __toESM(require("path"));
|
|
23791
24022
|
|
|
23792
24023
|
// src/core/detection.ts
|
|
23793
|
-
var
|
|
23794
|
-
var
|
|
24024
|
+
var fs17 = __toESM(require("fs"));
|
|
24025
|
+
var path25 = __toESM(require("path"));
|
|
23795
24026
|
init_defaults();
|
|
23796
24027
|
var PACKAGE_MARKERS = [
|
|
23797
24028
|
"package.json",
|
|
@@ -23843,7 +24074,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
|
23843
24074
|
});
|
|
23844
24075
|
}
|
|
23845
24076
|
function parseGitmodules(filePath) {
|
|
23846
|
-
const content =
|
|
24077
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
23847
24078
|
const entries = [];
|
|
23848
24079
|
let current2 = {};
|
|
23849
24080
|
for (const line2 of content.split("\n")) {
|
|
@@ -23865,8 +24096,8 @@ function parseGitmodules(filePath) {
|
|
|
23865
24096
|
return entries;
|
|
23866
24097
|
}
|
|
23867
24098
|
function detectGitSubmodules(projectRoot2) {
|
|
23868
|
-
const gitmodulesPath =
|
|
23869
|
-
if (!
|
|
24099
|
+
const gitmodulesPath = path25.join(projectRoot2, ".gitmodules");
|
|
24100
|
+
if (!fs17.existsSync(gitmodulesPath)) return [];
|
|
23870
24101
|
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
23871
24102
|
suggestedId: pathToId(entry.path),
|
|
23872
24103
|
suggestedName: pathToName(entry.path),
|
|
@@ -23884,18 +24115,18 @@ function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
|
23884
24115
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
23885
24116
|
let entries;
|
|
23886
24117
|
try {
|
|
23887
|
-
entries =
|
|
24118
|
+
entries = fs17.readdirSync(currentDir, { withFileTypes: true });
|
|
23888
24119
|
} catch {
|
|
23889
24120
|
return;
|
|
23890
24121
|
}
|
|
23891
24122
|
for (const entry of entries) {
|
|
23892
24123
|
if (!entry.isDirectory()) continue;
|
|
23893
24124
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23894
|
-
const fullPath =
|
|
23895
|
-
const relPath = normalizePath3(
|
|
24125
|
+
const fullPath = path25.join(currentDir, entry.name);
|
|
24126
|
+
const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
|
|
23896
24127
|
if (relPath === "" || relPath === ".") continue;
|
|
23897
|
-
const gitPath =
|
|
23898
|
-
if (
|
|
24128
|
+
const gitPath = path25.join(fullPath, ".git");
|
|
24129
|
+
if (fs17.existsSync(gitPath)) {
|
|
23899
24130
|
results.push({
|
|
23900
24131
|
suggestedId: pathToId(relPath),
|
|
23901
24132
|
suggestedName: pathToName(relPath),
|
|
@@ -23917,17 +24148,17 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
|
23917
24148
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
23918
24149
|
let entries;
|
|
23919
24150
|
try {
|
|
23920
|
-
entries =
|
|
24151
|
+
entries = fs17.readdirSync(currentDir, { withFileTypes: true });
|
|
23921
24152
|
} catch {
|
|
23922
24153
|
return;
|
|
23923
24154
|
}
|
|
23924
24155
|
for (const entry of entries) {
|
|
23925
24156
|
if (!entry.isDirectory()) continue;
|
|
23926
24157
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23927
|
-
const fullPath =
|
|
23928
|
-
const relPath = normalizePath3(
|
|
24158
|
+
const fullPath = path25.join(currentDir, entry.name);
|
|
24159
|
+
const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
|
|
23929
24160
|
if (relPath === "" || relPath === ".") continue;
|
|
23930
|
-
const hasMarker = PACKAGE_MARKERS.some((m) =>
|
|
24161
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs17.existsSync(path25.join(fullPath, m)));
|
|
23931
24162
|
if (hasMarker) {
|
|
23932
24163
|
results.push({
|
|
23933
24164
|
suggestedId: pathToId(relPath),
|
|
@@ -23941,8 +24172,8 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
|
23941
24172
|
}
|
|
23942
24173
|
}
|
|
23943
24174
|
function pathToId(relPath) {
|
|
23944
|
-
const
|
|
23945
|
-
return
|
|
24175
|
+
const basename14 = path25.basename(relPath);
|
|
24176
|
+
return basename14.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23946
24177
|
}
|
|
23947
24178
|
function pathToName(relPath) {
|
|
23948
24179
|
const id = pathToId(relPath);
|
|
@@ -24169,7 +24400,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getPro
|
|
|
24169
24400
|
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
24170
24401
|
if (!targetConfig) continue;
|
|
24171
24402
|
const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
|
|
24172
|
-
expected.add(
|
|
24403
|
+
expected.add(path28.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
24173
24404
|
}
|
|
24174
24405
|
}
|
|
24175
24406
|
return expected;
|
|
@@ -24187,7 +24418,7 @@ async function runInit(options = {}) {
|
|
|
24187
24418
|
logger.header("wairon init");
|
|
24188
24419
|
const cwd = process.cwd();
|
|
24189
24420
|
const ancestorRoot = findSystemRoot(cwd);
|
|
24190
|
-
if (ancestorRoot &&
|
|
24421
|
+
if (ancestorRoot && path31.resolve(ancestorRoot) === path31.resolve(cwd)) {
|
|
24191
24422
|
logger.info("Project already initialized.");
|
|
24192
24423
|
logger.info("Design your spec tree with the SDD architect skill, then run `wairon generate`.");
|
|
24193
24424
|
return;
|
|
@@ -24203,8 +24434,8 @@ async function runInit(options = {}) {
|
|
|
24203
24434
|
await runInitInteractive();
|
|
24204
24435
|
}
|
|
24205
24436
|
async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
|
|
24206
|
-
const relPath =
|
|
24207
|
-
const defaultId =
|
|
24437
|
+
const relPath = path31.relative(parentRoot, cwd) || ".";
|
|
24438
|
+
const defaultId = path31.basename(cwd);
|
|
24208
24439
|
logger.info(`Detected a parent wairon project at ${parentRoot}`);
|
|
24209
24440
|
logger.info(`This directory ("${relPath}") is not yet a wairon project.`);
|
|
24210
24441
|
if (!options.yes) {
|
|
@@ -24264,7 +24495,7 @@ async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
|
|
|
24264
24495
|
}
|
|
24265
24496
|
async function runInitNonInteractive() {
|
|
24266
24497
|
const cwd = process.cwd();
|
|
24267
|
-
const projectName =
|
|
24498
|
+
const projectName = path31.basename(cwd);
|
|
24268
24499
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24269
24500
|
const targets = [defaultTargetConfig("claude"), defaultTargetConfig("agy")];
|
|
24270
24501
|
const projectConfig = buildProjectConfig(projectName, targets, now, "backend");
|
|
@@ -24286,7 +24517,7 @@ async function runInitNonInteractive() {
|
|
|
24286
24517
|
}
|
|
24287
24518
|
async function runInitInteractive() {
|
|
24288
24519
|
const cwd = process.cwd();
|
|
24289
|
-
const defaultProjectName =
|
|
24520
|
+
const defaultProjectName = path31.basename(cwd);
|
|
24290
24521
|
const { projectName } = await import_inquirer.default.prompt([
|
|
24291
24522
|
{
|
|
24292
24523
|
type: "input",
|
|
@@ -24446,7 +24677,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24446
24677
|
projectRoot: projectRoot2,
|
|
24447
24678
|
target: targetConfig
|
|
24448
24679
|
});
|
|
24449
|
-
logger.success(`Generated: ${
|
|
24680
|
+
logger.success(`Generated: ${path31.relative(cwd, result.outputPath)}`);
|
|
24450
24681
|
}
|
|
24451
24682
|
}
|
|
24452
24683
|
writeStarterDocs(projectName);
|
|
@@ -24461,7 +24692,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24461
24692
|
if (guidePlan.claudeLocal) {
|
|
24462
24693
|
const p = localGuideFilePath(projectRoot2, "claude");
|
|
24463
24694
|
injectGuide(p, "local");
|
|
24464
|
-
logger.success(`Injected wairon guide into ${
|
|
24695
|
+
logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
|
|
24465
24696
|
writeRootGuideDelegator(projectRoot2, "claude");
|
|
24466
24697
|
logger.success(`Created root CLAUDE.md delegator pointing to .claude/CLAUDE.md`);
|
|
24467
24698
|
}
|
|
@@ -24473,7 +24704,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
|
|
|
24473
24704
|
if (guidePlan.geminiLocal) {
|
|
24474
24705
|
const p = localGuideFilePath(projectRoot2, "gemini");
|
|
24475
24706
|
injectGuide(p, "local");
|
|
24476
|
-
logger.success(`Injected wairon guide into ${
|
|
24707
|
+
logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
|
|
24477
24708
|
writeRootGuideDelegator(projectRoot2, "gemini");
|
|
24478
24709
|
logger.success(`Created root GEMINI.md delegator pointing to .gemini/GEMINI.md`);
|
|
24479
24710
|
}
|
|
@@ -24706,8 +24937,8 @@ A new project initialized with Wairon.
|
|
|
24706
24937
|
}
|
|
24707
24938
|
|
|
24708
24939
|
// src/commands/generate.ts
|
|
24709
|
-
var
|
|
24710
|
-
var
|
|
24940
|
+
var fs21 = __toESM(require("fs"));
|
|
24941
|
+
var path32 = __toESM(require("path"));
|
|
24711
24942
|
init_logger();
|
|
24712
24943
|
init_loader();
|
|
24713
24944
|
init_fs();
|
|
@@ -24715,29 +24946,29 @@ init_provision();
|
|
|
24715
24946
|
init_specs2();
|
|
24716
24947
|
var WAIRON_AGENT_FILE = /-(owner|implementer|architect)\.md$/;
|
|
24717
24948
|
function pruneStaleAgents(expectedPaths, scanDirs) {
|
|
24718
|
-
const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) =>
|
|
24949
|
+
const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) => path32.dirname(p)));
|
|
24719
24950
|
let pruned = 0;
|
|
24720
24951
|
for (const dir of dirs) {
|
|
24721
24952
|
let entries;
|
|
24722
24953
|
try {
|
|
24723
|
-
entries =
|
|
24954
|
+
entries = fs21.readdirSync(dir);
|
|
24724
24955
|
} catch {
|
|
24725
24956
|
continue;
|
|
24726
24957
|
}
|
|
24727
24958
|
for (const name of entries) {
|
|
24728
24959
|
if (!name.endsWith(".md")) continue;
|
|
24729
|
-
const full =
|
|
24960
|
+
const full = path32.resolve(dir, name);
|
|
24730
24961
|
if (expectedPaths.has(full)) continue;
|
|
24731
24962
|
let owned = WAIRON_AGENT_FILE.test(name);
|
|
24732
24963
|
if (!owned) {
|
|
24733
24964
|
try {
|
|
24734
|
-
owned =
|
|
24965
|
+
owned = fs21.readFileSync(full, "utf8").includes(WAIRON_MANAGED_MARKER);
|
|
24735
24966
|
} catch {
|
|
24736
24967
|
owned = false;
|
|
24737
24968
|
}
|
|
24738
24969
|
}
|
|
24739
24970
|
if (!owned) continue;
|
|
24740
|
-
|
|
24971
|
+
fs21.unlinkSync(full);
|
|
24741
24972
|
pruned++;
|
|
24742
24973
|
logger.verbose(`Pruned stale: ${full}`);
|
|
24743
24974
|
}
|
|
@@ -24751,7 +24982,7 @@ async function runGenerate(options = {}) {
|
|
|
24751
24982
|
const children = listDirectChainedSubprojects(getProjectRoot());
|
|
24752
24983
|
for (const child of children) {
|
|
24753
24984
|
logger.blank();
|
|
24754
|
-
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) || "."}/`);
|
|
24755
24986
|
await runWithProjectRoot(child.dir, async () => {
|
|
24756
24987
|
ensureProjectInitialized(child.subsystemId);
|
|
24757
24988
|
invalidateSpecCache();
|
|
@@ -24772,7 +25003,7 @@ async function generateLayer(options = {}) {
|
|
|
24772
25003
|
} else if (options.dryRun) {
|
|
24773
25004
|
logger.info("Dry run \u2014 rules.materializeAgentFiles is off: no agent files are written; leftover managed files would be removed.");
|
|
24774
25005
|
} else if (options.prune !== false) {
|
|
24775
|
-
const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) =>
|
|
25006
|
+
const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) => path32.dirname(p));
|
|
24776
25007
|
const removed = pruneStaleAgents(/* @__PURE__ */ new Set(), candidateDirs);
|
|
24777
25008
|
if (removed > 0) {
|
|
24778
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.`);
|
|
@@ -24859,22 +25090,34 @@ function materializeAgentLayer(agents, projectConfig, options) {
|
|
|
24859
25090
|
}
|
|
24860
25091
|
|
|
24861
25092
|
// src/commands/lock.ts
|
|
24862
|
-
var
|
|
25093
|
+
var os8 = __toESM(require("os"));
|
|
24863
25094
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
24864
25095
|
init_logger();
|
|
25096
|
+
init_baseline();
|
|
24865
25097
|
init_defaults();
|
|
25098
|
+
var path33 = __toESM(require("path"));
|
|
25099
|
+
init_fs();
|
|
24866
25100
|
async function runLock(options = {}, gate) {
|
|
24867
|
-
const
|
|
24868
|
-
if (
|
|
24869
|
-
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.");
|
|
24870
25106
|
} else {
|
|
24871
|
-
logger.info(`${
|
|
24872
|
-
for (const p of
|
|
24873
|
-
|
|
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)"}`);
|
|
24874
25117
|
}
|
|
24875
25118
|
}
|
|
24876
25119
|
logger.blank();
|
|
24877
|
-
logger.warn("This
|
|
25120
|
+
logger.warn("This records the current design as approved and (re)generates the agent topology.");
|
|
24878
25121
|
if (!options.yes) {
|
|
24879
25122
|
if (!process.stdin.isTTY) {
|
|
24880
25123
|
logger.error("Non-interactive shell \u2014 re-run with --yes to confirm the lock.");
|
|
@@ -24884,24 +25127,15 @@ async function runLock(options = {}, gate) {
|
|
|
24884
25127
|
{
|
|
24885
25128
|
type: "confirm",
|
|
24886
25129
|
name: "confirmed",
|
|
24887
|
-
message: "
|
|
25130
|
+
message: "Approve this design and generate the agent topology?",
|
|
24888
25131
|
default: false
|
|
24889
25132
|
}
|
|
24890
25133
|
]);
|
|
24891
25134
|
if (!confirmed) return null;
|
|
24892
25135
|
}
|
|
24893
|
-
if (options.subsystem) {
|
|
24894
|
-
for (const p of promotable) applySpecStatus(p.kind, p.id, "complete");
|
|
24895
|
-
invalidateSpecCache();
|
|
24896
|
-
} else {
|
|
24897
|
-
promoteAllComplete();
|
|
24898
|
-
}
|
|
24899
|
-
if (promotable.length > 0) {
|
|
24900
|
-
logger.success(`Locked ${promotable.length} spec(s) as complete.`);
|
|
24901
|
-
}
|
|
24902
25136
|
let lockedBy = "local";
|
|
24903
25137
|
try {
|
|
24904
|
-
lockedBy = `local:${
|
|
25138
|
+
lockedBy = `local:${os8.userInfo().username}`;
|
|
24905
25139
|
} catch {
|
|
24906
25140
|
}
|
|
24907
25141
|
const record2 = {
|
|
@@ -24917,6 +25151,13 @@ async function runLock(options = {}, gate) {
|
|
|
24917
25151
|
status: "ready"
|
|
24918
25152
|
};
|
|
24919
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));
|
|
24920
25161
|
return record2;
|
|
24921
25162
|
}
|
|
24922
25163
|
|
|
@@ -25173,29 +25414,29 @@ init_mcp();
|
|
|
25173
25414
|
|
|
25174
25415
|
// src/commands/update.ts
|
|
25175
25416
|
var https2 = __toESM(require("https"));
|
|
25176
|
-
var
|
|
25177
|
-
var
|
|
25178
|
-
var
|
|
25179
|
-
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"));
|
|
25180
25421
|
var import_child_process2 = require("child_process");
|
|
25181
25422
|
init_logger();
|
|
25182
25423
|
init_defaults();
|
|
25183
25424
|
init_version();
|
|
25184
25425
|
|
|
25185
25426
|
// src/utils/download.ts
|
|
25186
|
-
var
|
|
25427
|
+
var fs22 = __toESM(require("fs"));
|
|
25187
25428
|
var http = __toESM(require("http"));
|
|
25188
25429
|
var https = __toESM(require("https"));
|
|
25189
25430
|
init_defaults();
|
|
25190
25431
|
function downloadFile(url, dest) {
|
|
25191
|
-
return new Promise((
|
|
25192
|
-
const file =
|
|
25432
|
+
return new Promise((resolve27, reject) => {
|
|
25433
|
+
const file = fs22.createWriteStream(dest);
|
|
25193
25434
|
const get4 = url.startsWith("https://") ? https.get : http.get;
|
|
25194
25435
|
get4(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
|
|
25195
25436
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
25196
25437
|
file.close();
|
|
25197
25438
|
res.destroy();
|
|
25198
|
-
downloadFile(res.headers.location, dest).then(
|
|
25439
|
+
downloadFile(res.headers.location, dest).then(resolve27).catch(reject);
|
|
25199
25440
|
return;
|
|
25200
25441
|
}
|
|
25201
25442
|
if (res.statusCode !== 200) {
|
|
@@ -25207,16 +25448,16 @@ function downloadFile(url, dest) {
|
|
|
25207
25448
|
res.pipe(file);
|
|
25208
25449
|
file.on("finish", () => {
|
|
25209
25450
|
res.destroy();
|
|
25210
|
-
file.close(() =>
|
|
25451
|
+
file.close(() => resolve27());
|
|
25211
25452
|
});
|
|
25212
25453
|
file.on("error", (err) => {
|
|
25213
25454
|
res.destroy();
|
|
25214
|
-
|
|
25455
|
+
fs22.unlink(dest, () => {
|
|
25215
25456
|
});
|
|
25216
25457
|
reject(err);
|
|
25217
25458
|
});
|
|
25218
25459
|
}).on("error", (err) => {
|
|
25219
|
-
|
|
25460
|
+
fs22.unlink(dest, () => {
|
|
25220
25461
|
});
|
|
25221
25462
|
reject(err);
|
|
25222
25463
|
});
|
|
@@ -25282,8 +25523,8 @@ async function runUpdate(options = {}) {
|
|
|
25282
25523
|
logger.info(`Download manually from: ${release.html_url}`);
|
|
25283
25524
|
process.exit(1);
|
|
25284
25525
|
}
|
|
25285
|
-
const tmpDir =
|
|
25286
|
-
const tmpFile =
|
|
25526
|
+
const tmpDir = os9.tmpdir();
|
|
25527
|
+
const tmpFile = path34.join(tmpDir, assetName);
|
|
25287
25528
|
logger.info(`Downloading ${assetName}...`);
|
|
25288
25529
|
try {
|
|
25289
25530
|
await downloadFile(asset.browser_download_url, tmpFile);
|
|
@@ -25300,16 +25541,16 @@ async function runUpdate(options = {}) {
|
|
|
25300
25541
|
const checksumAssetName = assetName + ".sha256";
|
|
25301
25542
|
const checksumAsset = release.assets.find((a) => a.name === checksumAssetName);
|
|
25302
25543
|
if (checksumAsset) {
|
|
25303
|
-
const tmpChecksum =
|
|
25544
|
+
const tmpChecksum = path34.join(tmpDir, checksumAssetName);
|
|
25304
25545
|
logger.info(`Verifying checksum...`);
|
|
25305
25546
|
try {
|
|
25306
25547
|
await downloadFile(checksumAsset.browser_download_url, tmpChecksum);
|
|
25307
25548
|
verifyChecksum(tmpFile, tmpChecksum, assetName);
|
|
25308
|
-
|
|
25549
|
+
fs23.unlinkSync(tmpChecksum);
|
|
25309
25550
|
} catch (err) {
|
|
25310
25551
|
logger.error(`Checksum verification failed: ${err.message}`);
|
|
25311
25552
|
try {
|
|
25312
|
-
|
|
25553
|
+
fs23.unlinkSync(tmpFile);
|
|
25313
25554
|
} catch {
|
|
25314
25555
|
}
|
|
25315
25556
|
process.exit(1);
|
|
@@ -25344,7 +25585,7 @@ function releaseChannelLabel(tag) {
|
|
|
25344
25585
|
return "stable";
|
|
25345
25586
|
}
|
|
25346
25587
|
function fetchReleases(repo) {
|
|
25347
|
-
return new Promise((
|
|
25588
|
+
return new Promise((resolve27, reject) => {
|
|
25348
25589
|
const url = `https://api.github.com/repos/${repo}/releases?per_page=20`;
|
|
25349
25590
|
const options = {
|
|
25350
25591
|
headers: {
|
|
@@ -25364,7 +25605,7 @@ function fetchReleases(repo) {
|
|
|
25364
25605
|
return;
|
|
25365
25606
|
}
|
|
25366
25607
|
try {
|
|
25367
|
-
|
|
25608
|
+
resolve27(JSON.parse(data));
|
|
25368
25609
|
} catch {
|
|
25369
25610
|
reject(new Error("Failed to parse GitHub API response"));
|
|
25370
25611
|
}
|
|
@@ -25374,10 +25615,10 @@ function fetchReleases(repo) {
|
|
|
25374
25615
|
});
|
|
25375
25616
|
}
|
|
25376
25617
|
function verifyChecksum(filePath, checksumFile, expectedFilename) {
|
|
25377
|
-
const checksumContent =
|
|
25618
|
+
const checksumContent = fs23.readFileSync(checksumFile, "utf-8").trim();
|
|
25378
25619
|
const expectedHash = checksumContent.split(/\s+/)[0].toLowerCase();
|
|
25379
|
-
const fileBuffer =
|
|
25380
|
-
const actualHash =
|
|
25620
|
+
const fileBuffer = fs23.readFileSync(filePath);
|
|
25621
|
+
const actualHash = crypto4.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase();
|
|
25381
25622
|
if (actualHash !== expectedHash) {
|
|
25382
25623
|
throw new Error(
|
|
25383
25624
|
`SHA-256 mismatch for ${expectedFilename}
|
|
@@ -25407,9 +25648,9 @@ function isPkgBinary2() {
|
|
|
25407
25648
|
function installBinary(tmpFile, destPath) {
|
|
25408
25649
|
const platform = process.platform;
|
|
25409
25650
|
const isZip = tmpFile.endsWith(".zip");
|
|
25410
|
-
const extractDir =
|
|
25411
|
-
if (
|
|
25412
|
-
|
|
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 });
|
|
25413
25654
|
if (isZip) {
|
|
25414
25655
|
(0, import_child_process2.execSync)(
|
|
25415
25656
|
`powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmpFile}' -DestinationPath '${extractDir}' -Force"`,
|
|
@@ -25419,18 +25660,18 @@ function installBinary(tmpFile, destPath) {
|
|
|
25419
25660
|
(0, import_child_process2.execSync)(`tar -xzf "${tmpFile}" -C "${extractDir}"`, { stdio: ["ignore", "pipe", "pipe"] });
|
|
25420
25661
|
}
|
|
25421
25662
|
const binaryName = platform === "win32" ? "wairon.exe" : "wairon";
|
|
25422
|
-
const extractedBinary =
|
|
25423
|
-
if (!
|
|
25663
|
+
const extractedBinary = path34.join(extractDir, binaryName);
|
|
25664
|
+
if (!fs23.existsSync(extractedBinary)) {
|
|
25424
25665
|
throw new Error(`Extracted binary not found at ${extractedBinary}`);
|
|
25425
25666
|
}
|
|
25426
25667
|
if (platform === "win32") {
|
|
25427
25668
|
const oldPath = destPath + ".old";
|
|
25428
25669
|
try {
|
|
25429
25670
|
cleanStaleBinary(oldPath);
|
|
25430
|
-
|
|
25431
|
-
|
|
25671
|
+
fs23.renameSync(destPath, oldPath);
|
|
25672
|
+
fs23.copyFileSync(extractedBinary, destPath);
|
|
25432
25673
|
try {
|
|
25433
|
-
|
|
25674
|
+
fs23.unlinkSync(oldPath);
|
|
25434
25675
|
} catch {
|
|
25435
25676
|
}
|
|
25436
25677
|
} catch (err) {
|
|
@@ -25444,25 +25685,25 @@ function installBinary(tmpFile, destPath) {
|
|
|
25444
25685
|
}
|
|
25445
25686
|
} else {
|
|
25446
25687
|
const tmpDest = destPath + ".new";
|
|
25447
|
-
|
|
25448
|
-
|
|
25449
|
-
|
|
25688
|
+
fs23.copyFileSync(extractedBinary, tmpDest);
|
|
25689
|
+
fs23.chmodSync(tmpDest, 493);
|
|
25690
|
+
fs23.renameSync(tmpDest, destPath);
|
|
25450
25691
|
}
|
|
25451
25692
|
try {
|
|
25452
|
-
|
|
25693
|
+
fs23.unlinkSync(tmpFile);
|
|
25453
25694
|
} catch {
|
|
25454
25695
|
}
|
|
25455
25696
|
try {
|
|
25456
|
-
|
|
25697
|
+
fs23.rmSync(extractDir, { recursive: true });
|
|
25457
25698
|
} catch {
|
|
25458
25699
|
}
|
|
25459
25700
|
}
|
|
25460
25701
|
function cleanStaleBinary(oldPath) {
|
|
25461
25702
|
const target = oldPath ?? (isPkgBinary2() ? process.execPath + ".old" : null);
|
|
25462
25703
|
if (!target) return;
|
|
25463
|
-
if (
|
|
25704
|
+
if (fs23.existsSync(target)) {
|
|
25464
25705
|
try {
|
|
25465
|
-
|
|
25706
|
+
fs23.unlinkSync(target);
|
|
25466
25707
|
} catch {
|
|
25467
25708
|
}
|
|
25468
25709
|
}
|
|
@@ -25539,7 +25780,7 @@ async function filteredCheckbox(config) {
|
|
|
25539
25780
|
32,
|
|
25540
25781
|
Math.max(...items.map((i) => i.label.length))
|
|
25541
25782
|
);
|
|
25542
|
-
return new Promise((
|
|
25783
|
+
return new Promise((resolve27) => {
|
|
25543
25784
|
const checked = /* @__PURE__ */ new Set();
|
|
25544
25785
|
let cursor = 0;
|
|
25545
25786
|
let filterIdx = 0;
|
|
@@ -25606,7 +25847,7 @@ async function filteredCheckbox(config) {
|
|
|
25606
25847
|
);
|
|
25607
25848
|
teardown();
|
|
25608
25849
|
const result = items.filter((_, idx) => checked.has(idx)).map((i) => i.value);
|
|
25609
|
-
|
|
25850
|
+
resolve27(result);
|
|
25610
25851
|
}
|
|
25611
25852
|
function abort() {
|
|
25612
25853
|
process.stdout.write("\n");
|
|
@@ -25878,9 +26119,9 @@ async function runSkillsInstall() {
|
|
|
25878
26119
|
}
|
|
25879
26120
|
|
|
25880
26121
|
// src/commands/doctor.ts
|
|
25881
|
-
var
|
|
25882
|
-
var
|
|
25883
|
-
var
|
|
26122
|
+
var fs24 = __toESM(require("fs"));
|
|
26123
|
+
var os10 = __toESM(require("os"));
|
|
26124
|
+
var path35 = __toESM(require("path"));
|
|
25884
26125
|
var import_chalk12 = __toESM(require("chalk"));
|
|
25885
26126
|
init_logger();
|
|
25886
26127
|
init_defaults();
|
|
@@ -25911,10 +26152,10 @@ function stampVerdict(content) {
|
|
|
25911
26152
|
return { mark: "warn", note: `v${v} \u2014 stale, installed is v${WAIRON_VERSION}` };
|
|
25912
26153
|
}
|
|
25913
26154
|
function mcpEntryHealth(settingsPath) {
|
|
25914
|
-
if (!
|
|
26155
|
+
if (!fs24.existsSync(settingsPath)) return { mark: "warn", note: "not registered" };
|
|
25915
26156
|
let entry;
|
|
25916
26157
|
try {
|
|
25917
|
-
const s = JSON.parse(
|
|
26158
|
+
const s = JSON.parse(fs24.readFileSync(settingsPath, "utf8"));
|
|
25918
26159
|
entry = s.mcpServers?.["wairon"];
|
|
25919
26160
|
} catch {
|
|
25920
26161
|
return { mark: "error", note: "parse error" };
|
|
@@ -25922,7 +26163,7 @@ function mcpEntryHealth(settingsPath) {
|
|
|
25922
26163
|
if (!entry) return { mark: "warn", note: "not registered" };
|
|
25923
26164
|
if (entry.command === "node" && Array.isArray(entry.args) && typeof entry.args[0] === "string") {
|
|
25924
26165
|
const scriptPath = entry.args[0];
|
|
25925
|
-
if (!
|
|
26166
|
+
if (!fs24.existsSync(scriptPath)) {
|
|
25926
26167
|
return { mark: "error", note: `registered but the server path is missing \u2014 ${scriptPath}` };
|
|
25927
26168
|
}
|
|
25928
26169
|
}
|
|
@@ -25974,7 +26215,7 @@ async function runDoctor(options = {}) {
|
|
|
25974
26215
|
const { findChainingSubprojectsMissingConfig: findChainingSubprojectsMissingConfig2 } = (init_provision(), __toCommonJS(provision_exports));
|
|
25975
26216
|
const missing = findChainingSubprojectsMissingConfig2(getProjectRoot());
|
|
25976
26217
|
if (missing.length > 0) {
|
|
25977
|
-
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.`);
|
|
25978
26219
|
}
|
|
25979
26220
|
} catch {
|
|
25980
26221
|
}
|
|
@@ -26012,7 +26253,7 @@ async function runDoctor(options = {}) {
|
|
|
26012
26253
|
const gp = localGuideFilePath(process.cwd(), t);
|
|
26013
26254
|
if (!gp || seenGuides.has(gp)) continue;
|
|
26014
26255
|
seenGuides.add(gp);
|
|
26015
|
-
const rel2 =
|
|
26256
|
+
const rel2 = path35.relative(process.cwd(), gp).replace(/\\/g, "/");
|
|
26016
26257
|
if (!pathExists(gp)) {
|
|
26017
26258
|
line(tally, "warn", `${rel2} guide \u2014 not injected (run \`wairon generate\`)`);
|
|
26018
26259
|
continue;
|
|
@@ -26094,17 +26335,17 @@ async function runDoctor(options = {}) {
|
|
|
26094
26335
|
line(tally, h.mark, `Claude (project .mcp.json): ${h.note}${h.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend claude`"}`);
|
|
26095
26336
|
}
|
|
26096
26337
|
if (wantGemini) {
|
|
26097
|
-
const globalCfg =
|
|
26338
|
+
const globalCfg = path35.join(os10.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
|
|
26098
26339
|
const hg = mcpEntryHealth(globalCfg);
|
|
26099
26340
|
line(tally, hg.mark, `Antigravity (global mcp_config.json): ${hg.note}${hg.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend gemini --global`"}`);
|
|
26100
26341
|
const projPath = fromProjectRoot(".gemini", "settings.json");
|
|
26101
|
-
if (
|
|
26342
|
+
if (fs24.existsSync(projPath)) {
|
|
26102
26343
|
const hp = mcpEntryHealth(projPath);
|
|
26103
26344
|
line(tally, hp.mark === "error" ? "error" : "ok", `Gemini CLI (project): ${hp.note} ${import_chalk12.default.gray("(Antigravity ignores this file)")}`);
|
|
26104
26345
|
}
|
|
26105
26346
|
}
|
|
26106
|
-
const pluginDir =
|
|
26107
|
-
if (
|
|
26347
|
+
const pluginDir = path35.join(os10.homedir(), ".gemini", "config", "plugins", "wairon");
|
|
26348
|
+
if (fs24.existsSync(pluginDir)) {
|
|
26108
26349
|
line(tally, "warn", `Legacy Antigravity plugin present (${pluginDir}) \u2014 it collides with the wairon MCP server. Remove it with \`wairon doctor --fix\`.`);
|
|
26109
26350
|
}
|
|
26110
26351
|
logger.blank();
|
|
@@ -26143,7 +26384,7 @@ async function applyFixes() {
|
|
|
26143
26384
|
const legacySpecs = findLegacySpecFiles();
|
|
26144
26385
|
if (legacySpecs.length > 0) {
|
|
26145
26386
|
for (const { path: oldPath, expected: newPath } of legacySpecs) {
|
|
26146
|
-
|
|
26387
|
+
fs24.renameSync(oldPath, newPath);
|
|
26147
26388
|
}
|
|
26148
26389
|
console.log(` ${icon("ok")} Migrated ${legacySpecs.length} legacy spec file(s) to the new dot-prefixed unified schema.`);
|
|
26149
26390
|
}
|
|
@@ -26197,8 +26438,8 @@ function printSummary(tally) {
|
|
|
26197
26438
|
}
|
|
26198
26439
|
|
|
26199
26440
|
// src/commands/diagram.ts
|
|
26200
|
-
var
|
|
26201
|
-
var
|
|
26441
|
+
var fs25 = __toESM(require("fs"));
|
|
26442
|
+
var path36 = __toESM(require("path"));
|
|
26202
26443
|
init_logger();
|
|
26203
26444
|
init_loader();
|
|
26204
26445
|
init_fs();
|
|
@@ -26233,71 +26474,71 @@ function collectIssues() {
|
|
|
26233
26474
|
}
|
|
26234
26475
|
function writeCanvas(dest) {
|
|
26235
26476
|
const model = buildCanvasModel(collectIssues());
|
|
26236
|
-
ensureDir(
|
|
26237
|
-
|
|
26477
|
+
ensureDir(path36.dirname(path36.resolve(dest)));
|
|
26478
|
+
fs25.writeFileSync(dest, renderCanvasHtml(model), "utf-8");
|
|
26238
26479
|
}
|
|
26239
26480
|
function parseSequenceRef(ref) {
|
|
26240
|
-
const
|
|
26241
|
-
if (
|
|
26481
|
+
const sep9 = ref.includes(":") ? ref.lastIndexOf(":") : ref.lastIndexOf(".");
|
|
26482
|
+
if (sep9 <= 0 || sep9 === ref.length - 1) {
|
|
26242
26483
|
throw new WaironError(
|
|
26243
26484
|
`Invalid --sequence reference "${ref}". Use <componentId>:<methodName> (e.g. billing-portal:authorize).`
|
|
26244
26485
|
);
|
|
26245
26486
|
}
|
|
26246
|
-
return { component: ref.slice(0,
|
|
26487
|
+
return { component: ref.slice(0, sep9), method: ref.slice(sep9 + 1) };
|
|
26247
26488
|
}
|
|
26248
26489
|
async function runDiagram(rawOptions = {}) {
|
|
26249
26490
|
assertProjectInitialized();
|
|
26250
26491
|
const options = applyFormat(rawOptions);
|
|
26251
26492
|
if (options.canvas && !options.all) {
|
|
26252
|
-
const dest2 = options.out ??
|
|
26493
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
26253
26494
|
writeCanvas(dest2);
|
|
26254
26495
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
26255
26496
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline).");
|
|
26256
26497
|
return;
|
|
26257
26498
|
}
|
|
26258
26499
|
if (options.drawio && !options.all) {
|
|
26259
|
-
const dest2 = options.out ??
|
|
26260
|
-
ensureDir(
|
|
26261
|
-
|
|
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");
|
|
26262
26503
|
logger.success(`draw.io diagram written to ${dest2}`);
|
|
26263
26504
|
logger.info("Open with draw.io / diagrams.net (or import into tools that accept the format).");
|
|
26264
26505
|
return;
|
|
26265
26506
|
}
|
|
26266
26507
|
if (options.excalidraw && !options.all) {
|
|
26267
|
-
const dest2 = options.out ??
|
|
26268
|
-
ensureDir(
|
|
26269
|
-
|
|
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");
|
|
26270
26511
|
logger.success(`Excalidraw scene written to ${dest2}`);
|
|
26271
26512
|
logger.info("Open with excalidraw.com or the VS Code extension.");
|
|
26272
26513
|
return;
|
|
26273
26514
|
}
|
|
26274
26515
|
const wantsMermaid = options.format?.toLowerCase().startsWith("mermaid") || !!options.subsystem || !!options.sequence;
|
|
26275
26516
|
if (!options.all && !options.sequence && !wantsMermaid) {
|
|
26276
|
-
const dest2 = options.out ??
|
|
26517
|
+
const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
26277
26518
|
writeCanvas(dest2);
|
|
26278
26519
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
26279
26520
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline). Other formats: --format mermaid|drawio|excalidraw.");
|
|
26280
26521
|
return;
|
|
26281
26522
|
}
|
|
26282
26523
|
if (options.all) {
|
|
26283
|
-
const outDir = options.out ??
|
|
26524
|
+
const outDir = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams");
|
|
26284
26525
|
const files = generateDiagramSet();
|
|
26285
26526
|
if (files.length === 0) {
|
|
26286
26527
|
logger.warn("No diagrams to generate \u2014 the spec tree has no components yet.");
|
|
26287
26528
|
return;
|
|
26288
26529
|
}
|
|
26289
26530
|
for (const file of files) {
|
|
26290
|
-
const dest2 =
|
|
26291
|
-
ensureDir(
|
|
26292
|
-
|
|
26531
|
+
const dest2 = path36.join(outDir, file.relPath);
|
|
26532
|
+
ensureDir(path36.dirname(dest2));
|
|
26533
|
+
fs25.writeFileSync(dest2, toMarkdown(file), "utf-8");
|
|
26293
26534
|
}
|
|
26294
|
-
writeCanvas(
|
|
26535
|
+
writeCanvas(path36.join(outDir, "canvas.html"));
|
|
26295
26536
|
const exportModel = buildCanvasModel();
|
|
26296
|
-
|
|
26297
|
-
|
|
26537
|
+
fs25.writeFileSync(path36.join(outDir, "architecture.drawio"), generateDrawioXml(exportModel), "utf-8");
|
|
26538
|
+
fs25.writeFileSync(path36.join(outDir, "architecture.excalidraw"), generateExcalidrawScene(exportModel), "utf-8");
|
|
26298
26539
|
const graph = loadSpecGraph();
|
|
26299
|
-
const indexPath =
|
|
26300
|
-
|
|
26540
|
+
const indexPath = path36.join(outDir, "README.md");
|
|
26541
|
+
fs25.writeFileSync(indexPath, diagramSetIndex(files, graph.systemName), "utf-8");
|
|
26301
26542
|
logger.success(`Generated ${files.length} diagram(s) + interactive canvas.html + index into ${outDir}`);
|
|
26302
26543
|
for (const file of files.slice(0, 12)) {
|
|
26303
26544
|
logger.info(` ${file.relPath}`);
|
|
@@ -26308,26 +26549,26 @@ async function runDiagram(rawOptions = {}) {
|
|
|
26308
26549
|
let mermaid;
|
|
26309
26550
|
let title;
|
|
26310
26551
|
let defaultDest;
|
|
26311
|
-
const diagramsDir =
|
|
26552
|
+
const diagramsDir = path36.join(AI_PATHS.docsDir(), "diagrams");
|
|
26312
26553
|
if (options.sequence) {
|
|
26313
26554
|
const { component, method: method2 } = parseSequenceRef(options.sequence);
|
|
26314
26555
|
mermaid = generateSequenceDiagram(component, method2, { depth: options.depth });
|
|
26315
26556
|
title = `${component}.${method2} \u2014 narrative sequence`;
|
|
26316
|
-
defaultDest =
|
|
26557
|
+
defaultDest = path36.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
|
|
26317
26558
|
} else if (options.subsystem) {
|
|
26318
26559
|
mermaid = generateComponentDiagram({ subsystem: options.subsystem });
|
|
26319
26560
|
title = `${options.subsystem} \u2014 components`;
|
|
26320
|
-
defaultDest =
|
|
26561
|
+
defaultDest = path36.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
|
|
26321
26562
|
} else {
|
|
26322
26563
|
mermaid = generateComponentDiagram();
|
|
26323
26564
|
title = "Component architecture";
|
|
26324
|
-
defaultDest =
|
|
26565
|
+
defaultDest = path36.join(diagramsDir, "system.md");
|
|
26325
26566
|
}
|
|
26326
26567
|
const dest = options.out ?? defaultDest;
|
|
26327
|
-
ensureDir(
|
|
26568
|
+
ensureDir(path36.dirname(path36.resolve(dest)));
|
|
26328
26569
|
const content = dest.endsWith(".mmd") ? `${mermaid}
|
|
26329
26570
|
` : toMarkdown({ relPath: dest, title, mermaid });
|
|
26330
|
-
|
|
26571
|
+
fs25.writeFileSync(dest, content, "utf-8");
|
|
26331
26572
|
logger.success(`Mermaid diagram written to ${dest}`);
|
|
26332
26573
|
logger.info("Renders on GitHub/IDE previews; use a .mmd --out path for raw Mermaid.");
|
|
26333
26574
|
}
|
|
@@ -26436,9 +26677,9 @@ Component variants (${variants.length})
|
|
|
26436
26677
|
}
|
|
26437
26678
|
|
|
26438
26679
|
// src/commands/packs.ts
|
|
26439
|
-
var
|
|
26440
|
-
var
|
|
26441
|
-
var
|
|
26680
|
+
var fs26 = __toESM(require("fs"));
|
|
26681
|
+
var os11 = __toESM(require("os"));
|
|
26682
|
+
var path37 = __toESM(require("path"));
|
|
26442
26683
|
var import_chalk16 = __toESM(require("chalk"));
|
|
26443
26684
|
var import_sdk = __toESM(require_dist());
|
|
26444
26685
|
init_logger();
|
|
@@ -26464,11 +26705,11 @@ function describe(probe2) {
|
|
|
26464
26705
|
return parts.join(", ");
|
|
26465
26706
|
}
|
|
26466
26707
|
function resolveSourceUnit(source) {
|
|
26467
|
-
const abs =
|
|
26468
|
-
if (!
|
|
26708
|
+
const abs = path37.resolve(source);
|
|
26709
|
+
if (!fs26.existsSync(abs)) {
|
|
26469
26710
|
throw new Error(`Pack source "${source}" does not exist.`);
|
|
26470
26711
|
}
|
|
26471
|
-
const isDir =
|
|
26712
|
+
const isDir = fs26.statSync(abs).isDirectory();
|
|
26472
26713
|
if (isDir && !packDirEntry(abs)) {
|
|
26473
26714
|
throw new Error(`"${source}" is a directory without a pack entry file (pack.yaml | pack.cjs | index.cjs | ...).`);
|
|
26474
26715
|
}
|
|
@@ -26484,7 +26725,7 @@ async function addPack(source, options = {}) {
|
|
|
26484
26725
|
}
|
|
26485
26726
|
const { abs } = resolveSourceUnit(source);
|
|
26486
26727
|
const scope = options.global ? "global" : "project";
|
|
26487
|
-
const probe2 = probePack(abs,
|
|
26728
|
+
const probe2 = probePack(abs, path37.dirname(abs), scope);
|
|
26488
26729
|
if (probe2.error) {
|
|
26489
26730
|
logger.error(probe2.error);
|
|
26490
26731
|
process.exitCode = 1;
|
|
@@ -26492,10 +26733,10 @@ async function addPack(source, options = {}) {
|
|
|
26492
26733
|
}
|
|
26493
26734
|
if (options.global) {
|
|
26494
26735
|
const destDir = globalPacksDir();
|
|
26495
|
-
const dest2 =
|
|
26496
|
-
if (
|
|
26497
|
-
|
|
26498
|
-
|
|
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 });
|
|
26499
26740
|
}
|
|
26500
26741
|
logger.success(`Installed pack "${probe2.name}" globally: ${dest2}`);
|
|
26501
26742
|
logger.info(`${describe(probe2)} \u2014 auto-loaded for every project on this machine (WAIRON_PACKS_DIR / ~/.wairon/packs).`);
|
|
@@ -26508,11 +26749,11 @@ async function addPack(source, options = {}) {
|
|
|
26508
26749
|
return;
|
|
26509
26750
|
}
|
|
26510
26751
|
const root = getProjectRoot();
|
|
26511
|
-
const relRef = `.wai/packs/${
|
|
26512
|
-
const dest =
|
|
26513
|
-
if (
|
|
26514
|
-
|
|
26515
|
-
|
|
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 });
|
|
26516
26757
|
}
|
|
26517
26758
|
const config = loadProjectConfig();
|
|
26518
26759
|
const packs = config.extensions?.packs ?? [];
|
|
@@ -26521,14 +26762,14 @@ async function addPack(source, options = {}) {
|
|
|
26521
26762
|
saveProjectConfig(config);
|
|
26522
26763
|
logger.success(`Vendored pack "${probe2.name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
26523
26764
|
} else {
|
|
26524
|
-
|
|
26765
|
+
fs26.cpSync(abs, dest, { recursive: true, force: true });
|
|
26525
26766
|
logger.success(`Pack "${probe2.name}" already registered \u2014 refreshed ${relRef} from the source.`);
|
|
26526
26767
|
}
|
|
26527
26768
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
26528
26769
|
}
|
|
26529
26770
|
async function addPackFromArchive(source, options) {
|
|
26530
|
-
const abs =
|
|
26531
|
-
if (!
|
|
26771
|
+
const abs = path37.resolve(source);
|
|
26772
|
+
if (!fs26.existsSync(abs) || !fs26.statSync(abs).isFile()) {
|
|
26532
26773
|
logger.error(`Pack archive "${source}" does not exist.`);
|
|
26533
26774
|
process.exitCode = 1;
|
|
26534
26775
|
return;
|
|
@@ -26543,27 +26784,27 @@ async function addPackFromArchive(source, options) {
|
|
|
26543
26784
|
process.exitCode = 1;
|
|
26544
26785
|
return;
|
|
26545
26786
|
}
|
|
26546
|
-
baseDir =
|
|
26787
|
+
baseDir = path37.join(getProjectRoot(), ".wai", "packs");
|
|
26547
26788
|
}
|
|
26548
|
-
const bytes =
|
|
26549
|
-
|
|
26550
|
-
const staging =
|
|
26789
|
+
const bytes = fs26.readFileSync(abs);
|
|
26790
|
+
fs26.mkdirSync(baseDir, { recursive: true });
|
|
26791
|
+
const staging = fs26.mkdtempSync(path37.join(baseDir, ".wpack-staging-"));
|
|
26551
26792
|
let result;
|
|
26552
26793
|
try {
|
|
26553
26794
|
result = (0, import_sdk.extractPack)(bytes, staging);
|
|
26554
26795
|
} catch (err) {
|
|
26555
|
-
|
|
26556
|
-
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)}`);
|
|
26557
26798
|
process.exitCode = 1;
|
|
26558
26799
|
return;
|
|
26559
26800
|
}
|
|
26560
26801
|
const name = result.name;
|
|
26561
|
-
const destDir =
|
|
26562
|
-
if (
|
|
26563
|
-
|
|
26564
|
-
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);
|
|
26565
26806
|
if (probe2.error) {
|
|
26566
|
-
|
|
26807
|
+
fs26.rmSync(destDir, { recursive: true, force: true });
|
|
26567
26808
|
logger.error(probe2.error);
|
|
26568
26809
|
process.exitCode = 1;
|
|
26569
26810
|
return;
|
|
@@ -26581,7 +26822,7 @@ async function addPackFromArchive(source, options) {
|
|
|
26581
26822
|
saveProjectConfig(config);
|
|
26582
26823
|
logger.success(`Installed pack "${probe2.name ?? name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
26583
26824
|
} else {
|
|
26584
|
-
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)}.`);
|
|
26585
26826
|
}
|
|
26586
26827
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
26587
26828
|
}
|
|
@@ -26602,7 +26843,7 @@ async function buildPack(source, options = {}) {
|
|
|
26602
26843
|
const sourceDir = source && source.length > 0 ? source : ".";
|
|
26603
26844
|
const result = (0, import_sdk.buildPack)(sourceDir);
|
|
26604
26845
|
const outPath = options.out ?? result.suggestedFileName;
|
|
26605
|
-
|
|
26846
|
+
fs26.writeFileSync(outPath, result.archive);
|
|
26606
26847
|
logger.success(`Built pack "${result.info.name}" v${result.info.version} \u2192 ${outPath} (${result.archive.byteLength} bytes)`);
|
|
26607
26848
|
logger.info(`Install it with \`wairon pack add ${outPath}\`, or upload it to a hosted instance.`);
|
|
26608
26849
|
}
|
|
@@ -26613,8 +26854,8 @@ function expandSource(source, version) {
|
|
|
26613
26854
|
return source.replace(/\{version\}/g, version ?? "").replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? "");
|
|
26614
26855
|
}
|
|
26615
26856
|
async function fetchArchive(url) {
|
|
26616
|
-
const dir =
|
|
26617
|
-
const dest =
|
|
26857
|
+
const dir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packdl-"));
|
|
26858
|
+
const dest = path37.join(dir, "pack.wpack");
|
|
26618
26859
|
await downloadFile(url, dest);
|
|
26619
26860
|
return dest;
|
|
26620
26861
|
}
|
|
@@ -26633,7 +26874,7 @@ async function installPack(source) {
|
|
|
26633
26874
|
}
|
|
26634
26875
|
const extracted = extractArchiveToTemp(archive);
|
|
26635
26876
|
try {
|
|
26636
|
-
|
|
26877
|
+
fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
|
|
26637
26878
|
} catch {
|
|
26638
26879
|
}
|
|
26639
26880
|
if (!extracted) return;
|
|
@@ -26645,7 +26886,7 @@ async function installPack(source) {
|
|
|
26645
26886
|
process.exitCode = 1;
|
|
26646
26887
|
} finally {
|
|
26647
26888
|
try {
|
|
26648
|
-
|
|
26889
|
+
fs26.rmSync(extracted.dir, { recursive: true, force: true });
|
|
26649
26890
|
} catch {
|
|
26650
26891
|
}
|
|
26651
26892
|
}
|
|
@@ -26656,7 +26897,7 @@ async function installPack(source) {
|
|
|
26656
26897
|
if (!extracted) return;
|
|
26657
26898
|
sourceDir = extracted.dir;
|
|
26658
26899
|
cleanup = extracted.dir;
|
|
26659
|
-
origin =
|
|
26900
|
+
origin = path37.resolve(source);
|
|
26660
26901
|
} else {
|
|
26661
26902
|
let unit;
|
|
26662
26903
|
try {
|
|
@@ -26682,7 +26923,7 @@ async function installPack(source) {
|
|
|
26682
26923
|
} finally {
|
|
26683
26924
|
if (cleanup) {
|
|
26684
26925
|
try {
|
|
26685
|
-
|
|
26926
|
+
fs26.rmSync(cleanup, { recursive: true, force: true });
|
|
26686
26927
|
} catch {
|
|
26687
26928
|
}
|
|
26688
26929
|
}
|
|
@@ -26699,20 +26940,20 @@ function reportInstalled(installed, origin) {
|
|
|
26699
26940
|
}
|
|
26700
26941
|
}
|
|
26701
26942
|
function extractArchiveToTemp(source) {
|
|
26702
|
-
const abs =
|
|
26703
|
-
if (!
|
|
26943
|
+
const abs = path37.resolve(source);
|
|
26944
|
+
if (!fs26.existsSync(abs)) {
|
|
26704
26945
|
logger.error(`Pack archive "${source}" does not exist.`);
|
|
26705
26946
|
process.exitCode = 1;
|
|
26706
26947
|
return null;
|
|
26707
26948
|
}
|
|
26708
|
-
const tempDir =
|
|
26949
|
+
const tempDir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packinstall-"));
|
|
26709
26950
|
try {
|
|
26710
|
-
const result = (0, import_sdk.extractPack)(new Uint8Array(
|
|
26951
|
+
const result = (0, import_sdk.extractPack)(new Uint8Array(fs26.readFileSync(abs)), tempDir);
|
|
26711
26952
|
void result;
|
|
26712
26953
|
return { dir: tempDir };
|
|
26713
26954
|
} catch (e) {
|
|
26714
26955
|
try {
|
|
26715
|
-
|
|
26956
|
+
fs26.rmSync(tempDir, { recursive: true, force: true });
|
|
26716
26957
|
} catch {
|
|
26717
26958
|
}
|
|
26718
26959
|
logger.error(`Could not extract "${source}": ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -26858,7 +27099,7 @@ async function fetchAndInstall(selection, url) {
|
|
|
26858
27099
|
const archive = await fetchArchive(url);
|
|
26859
27100
|
const extracted = extractArchiveToTemp(archive);
|
|
26860
27101
|
try {
|
|
26861
|
-
|
|
27102
|
+
fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
|
|
26862
27103
|
} catch {
|
|
26863
27104
|
}
|
|
26864
27105
|
if (!extracted) return null;
|
|
@@ -26870,7 +27111,7 @@ async function fetchAndInstall(selection, url) {
|
|
|
26870
27111
|
return pack;
|
|
26871
27112
|
} finally {
|
|
26872
27113
|
try {
|
|
26873
|
-
|
|
27114
|
+
fs26.rmSync(extracted.dir, { recursive: true, force: true });
|
|
26874
27115
|
} catch {
|
|
26875
27116
|
}
|
|
26876
27117
|
}
|
|
@@ -26881,10 +27122,10 @@ function bundleTargets(selections, name, all) {
|
|
|
26881
27122
|
return selections.filter((s) => s.bundle === true);
|
|
26882
27123
|
}
|
|
26883
27124
|
function writeBundle(root, resolved) {
|
|
26884
|
-
const dest =
|
|
26885
|
-
|
|
26886
|
-
|
|
26887
|
-
|
|
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 });
|
|
26888
27129
|
return dest;
|
|
26889
27130
|
}
|
|
26890
27131
|
async function bundlePack(name, options = {}) {
|
|
@@ -26915,7 +27156,7 @@ async function bundlePack(name, options = {}) {
|
|
|
26915
27156
|
selection.version = resolved.version;
|
|
26916
27157
|
selection.bundle = true;
|
|
26917
27158
|
bundled.push(`${resolved.name}@${resolved.version}`);
|
|
26918
|
-
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, "/"))}`);
|
|
26919
27160
|
}
|
|
26920
27161
|
if (bundled.length === 0) return;
|
|
26921
27162
|
config.extensions = { packs: entries, useGlobalPacks: globalPacksEnabled(config) };
|
|
@@ -26957,9 +27198,9 @@ async function listPacks() {
|
|
|
26957
27198
|
console.log(import_chalk16.default.bold.cyan(`\u25A0 Global (${globalPacksDir()})${useGlobal ? "" : import_chalk16.default.yellow(" [disabled: extensions.useGlobalPacks: false]")}`));
|
|
26958
27199
|
if (globalRefs.length === 0) console.log(import_chalk16.default.dim(" (none)"));
|
|
26959
27200
|
for (const ref of globalRefs) {
|
|
26960
|
-
const probe2 = probePack(ref,
|
|
26961
|
-
if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${
|
|
26962
|
-
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))}`);
|
|
26963
27204
|
}
|
|
26964
27205
|
console.log("");
|
|
26965
27206
|
if (!inProject) {
|
|
@@ -26987,9 +27228,9 @@ async function listPacks() {
|
|
|
26987
27228
|
async function removePack(name, options = {}) {
|
|
26988
27229
|
if (options.global) {
|
|
26989
27230
|
for (const ref of discoverPacks(globalPacksDir())) {
|
|
26990
|
-
const probe2 = probePack(ref,
|
|
26991
|
-
if (probe2.name === name ||
|
|
26992
|
-
|
|
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 });
|
|
26993
27234
|
logger.success(`Removed global pack "${probe2.name ?? name}" (${ref}).`);
|
|
26994
27235
|
return;
|
|
26995
27236
|
}
|
|
@@ -27010,16 +27251,16 @@ async function removePack(name, options = {}) {
|
|
|
27010
27251
|
if (typeof entry !== "string") continue;
|
|
27011
27252
|
const ref = entry;
|
|
27012
27253
|
const probe2 = probePack(ref, root, "project");
|
|
27013
|
-
if (probe2.name === name || ref === name ||
|
|
27254
|
+
if (probe2.name === name || ref === name || path37.basename(ref) === name) {
|
|
27014
27255
|
config.extensions = {
|
|
27015
27256
|
packs: packs.filter((p) => p !== ref),
|
|
27016
27257
|
useGlobalPacks: globalPacksEnabled(config)
|
|
27017
27258
|
};
|
|
27018
27259
|
saveProjectConfig(config);
|
|
27019
|
-
const resolved =
|
|
27020
|
-
const vendorDir =
|
|
27021
|
-
if (resolved.startsWith(vendorDir +
|
|
27022
|
-
|
|
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 });
|
|
27023
27264
|
logger.success(`Deregistered pack "${probe2.name ?? name}" and deleted ${ref}.`);
|
|
27024
27265
|
} else {
|
|
27025
27266
|
logger.success(`Deregistered pack "${probe2.name ?? name}" (files at ${ref} left in place).`);
|
|
@@ -27032,22 +27273,22 @@ async function removePack(name, options = {}) {
|
|
|
27032
27273
|
}
|
|
27033
27274
|
|
|
27034
27275
|
// src/commands/host.ts
|
|
27035
|
-
var
|
|
27036
|
-
var
|
|
27037
|
-
var
|
|
27038
|
-
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"));
|
|
27039
27280
|
var import_child_process5 = require("child_process");
|
|
27040
27281
|
var import_chalk17 = __toESM(require("chalk"));
|
|
27041
27282
|
init_logger();
|
|
27042
27283
|
init_errors();
|
|
27043
27284
|
|
|
27044
27285
|
// src/server/admin.ts
|
|
27045
|
-
var
|
|
27286
|
+
var crypto9 = __toESM(require("crypto"));
|
|
27046
27287
|
init_fs();
|
|
27047
27288
|
init_defaults();
|
|
27048
27289
|
|
|
27049
27290
|
// src/server/auth.ts
|
|
27050
|
-
var
|
|
27291
|
+
var crypto7 = __toESM(require("crypto"));
|
|
27051
27292
|
|
|
27052
27293
|
// src/server/types.ts
|
|
27053
27294
|
var SSO_ADMIN_ROLE_ID = "sso-admin";
|
|
@@ -27060,35 +27301,35 @@ var UNAUTHENTICATED = {
|
|
|
27060
27301
|
var WEB_SESSION_PREFIX = "ws_";
|
|
27061
27302
|
|
|
27062
27303
|
// src/server/credentials.ts
|
|
27063
|
-
var
|
|
27064
|
-
var
|
|
27065
|
-
var
|
|
27304
|
+
var fs27 = __toESM(require("fs"));
|
|
27305
|
+
var path38 = __toESM(require("path"));
|
|
27306
|
+
var crypto5 = __toESM(require("crypto"));
|
|
27066
27307
|
var HASH_NS = "wairon:token:v1";
|
|
27067
27308
|
function hashToken(token) {
|
|
27068
|
-
return
|
|
27309
|
+
return crypto5.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
|
|
27069
27310
|
}
|
|
27070
27311
|
function storePath(dataDir) {
|
|
27071
|
-
return
|
|
27312
|
+
return path38.join(dataDir, "auth", "credentials.json");
|
|
27072
27313
|
}
|
|
27073
27314
|
function load3(dataDir) {
|
|
27074
27315
|
try {
|
|
27075
|
-
return JSON.parse(
|
|
27316
|
+
return JSON.parse(fs27.readFileSync(storePath(dataDir), "utf8"));
|
|
27076
27317
|
} catch {
|
|
27077
27318
|
return [];
|
|
27078
27319
|
}
|
|
27079
27320
|
}
|
|
27080
27321
|
function save(dataDir, records) {
|
|
27081
27322
|
const p = storePath(dataDir);
|
|
27082
|
-
|
|
27323
|
+
fs27.mkdirSync(path38.dirname(p), { recursive: true });
|
|
27083
27324
|
const tmp = `${p}.tmp`;
|
|
27084
|
-
|
|
27085
|
-
|
|
27325
|
+
fs27.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
27326
|
+
fs27.renameSync(tmp, p);
|
|
27086
27327
|
}
|
|
27087
27328
|
function digestEquals(a, b) {
|
|
27088
27329
|
const ab = Buffer.from(a, "hex");
|
|
27089
27330
|
const bb = Buffer.from(b, "hex");
|
|
27090
27331
|
if (ab.length === 0 || ab.length !== bb.length) return false;
|
|
27091
|
-
return
|
|
27332
|
+
return crypto5.timingSafeEqual(ab, bb);
|
|
27092
27333
|
}
|
|
27093
27334
|
function findByTokenHash(dataDir, tokenHash) {
|
|
27094
27335
|
return load3(dataDir).find((r) => digestEquals(r.keyHash, tokenHash)) ?? null;
|
|
@@ -27127,17 +27368,17 @@ function listByOwner(dataDir, ownerUserId) {
|
|
|
27127
27368
|
}
|
|
27128
27369
|
|
|
27129
27370
|
// src/server/websessions.ts
|
|
27130
|
-
var
|
|
27131
|
-
var
|
|
27132
|
-
var
|
|
27371
|
+
var fs28 = __toESM(require("fs"));
|
|
27372
|
+
var path39 = __toESM(require("path"));
|
|
27373
|
+
var crypto6 = __toESM(require("crypto"));
|
|
27133
27374
|
function storePath2(dataDir) {
|
|
27134
|
-
return
|
|
27375
|
+
return path39.join(dataDir, "web-sessions.json");
|
|
27135
27376
|
}
|
|
27136
27377
|
function readSessions(dataDir) {
|
|
27137
27378
|
const p = storePath2(dataDir);
|
|
27138
27379
|
let raw;
|
|
27139
27380
|
try {
|
|
27140
|
-
raw =
|
|
27381
|
+
raw = fs28.readFileSync(p, "utf8");
|
|
27141
27382
|
} catch (e) {
|
|
27142
27383
|
if (e.code === "ENOENT") return [];
|
|
27143
27384
|
throw new Error(`Failed to read web session store at ${p}: ${e.message}`);
|
|
@@ -27152,13 +27393,13 @@ function readSessions(dataDir) {
|
|
|
27152
27393
|
}
|
|
27153
27394
|
function persistSessions(dataDir, sessions) {
|
|
27154
27395
|
const p = storePath2(dataDir);
|
|
27155
|
-
|
|
27396
|
+
fs28.mkdirSync(path39.dirname(p), { recursive: true });
|
|
27156
27397
|
const tmp = `${p}.tmp`;
|
|
27157
|
-
|
|
27158
|
-
|
|
27398
|
+
fs28.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
|
|
27399
|
+
fs28.renameSync(tmp, p);
|
|
27159
27400
|
}
|
|
27160
27401
|
function mintSessionId() {
|
|
27161
|
-
return `${WEB_SESSION_PREFIX}${
|
|
27402
|
+
return `${WEB_SESSION_PREFIX}${crypto6.randomBytes(24).toString("hex")}`;
|
|
27162
27403
|
}
|
|
27163
27404
|
var WebSessionStore = class {
|
|
27164
27405
|
constructor(dataDir) {
|
|
@@ -27316,17 +27557,17 @@ function listWebSessionsBySubject(dataDir, userId) {
|
|
|
27316
27557
|
}
|
|
27317
27558
|
|
|
27318
27559
|
// src/server/users.ts
|
|
27319
|
-
var
|
|
27320
|
-
var
|
|
27560
|
+
var fs29 = __toESM(require("fs"));
|
|
27561
|
+
var path40 = __toESM(require("path"));
|
|
27321
27562
|
var VALID_STATUSES = ["active", "inactive", "suspended", "deactivated", "disabled"];
|
|
27322
27563
|
function storePath3(dataDir) {
|
|
27323
|
-
return
|
|
27564
|
+
return path40.join(dataDir, "users.json");
|
|
27324
27565
|
}
|
|
27325
27566
|
function loadStore(dataDir) {
|
|
27326
27567
|
const p = storePath3(dataDir);
|
|
27327
27568
|
let raw;
|
|
27328
27569
|
try {
|
|
27329
|
-
raw =
|
|
27570
|
+
raw = fs29.readFileSync(p, "utf8");
|
|
27330
27571
|
} catch (err) {
|
|
27331
27572
|
if (err.code === "ENOENT") return [];
|
|
27332
27573
|
throw new Error(`Cannot read hosted-user store at ${p}: ${err.message}`);
|
|
@@ -27344,10 +27585,10 @@ function loadStore(dataDir) {
|
|
|
27344
27585
|
}
|
|
27345
27586
|
function replaceAll(dataDir, records) {
|
|
27346
27587
|
const p = storePath3(dataDir);
|
|
27347
|
-
|
|
27588
|
+
fs29.mkdirSync(path40.dirname(p), { recursive: true });
|
|
27348
27589
|
const tmp = `${p}.tmp`;
|
|
27349
|
-
|
|
27350
|
-
|
|
27590
|
+
fs29.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
27591
|
+
fs29.renameSync(tmp, p);
|
|
27351
27592
|
}
|
|
27352
27593
|
function registryUpsert(dataDir, record2) {
|
|
27353
27594
|
const records = loadStore(dataDir);
|
|
@@ -27449,11 +27690,11 @@ function remapUnitReferences(dataDir, remap, removedScopeIds) {
|
|
|
27449
27690
|
}
|
|
27450
27691
|
|
|
27451
27692
|
// src/server/instance.ts
|
|
27452
|
-
var
|
|
27453
|
-
var
|
|
27693
|
+
var fs30 = __toESM(require("fs"));
|
|
27694
|
+
var path41 = __toESM(require("path"));
|
|
27454
27695
|
var import_crypto = require("crypto");
|
|
27455
27696
|
function storePath4(dataDir) {
|
|
27456
|
-
return
|
|
27697
|
+
return path41.join(dataDir, "instance.json");
|
|
27457
27698
|
}
|
|
27458
27699
|
var InstanceIdentityStore = class {
|
|
27459
27700
|
constructor(dataDir) {
|
|
@@ -27470,7 +27711,7 @@ var InstanceIdentityStore = class {
|
|
|
27470
27711
|
const p = storePath4(this.dataDir);
|
|
27471
27712
|
let raw;
|
|
27472
27713
|
try {
|
|
27473
|
-
raw =
|
|
27714
|
+
raw = fs30.readFileSync(p, "utf8");
|
|
27474
27715
|
} catch (err) {
|
|
27475
27716
|
if (err.code === "ENOENT") return null;
|
|
27476
27717
|
throw new Error(`Cannot read instance identity at ${p}: ${err.message}`);
|
|
@@ -27493,10 +27734,10 @@ var InstanceIdentityStore = class {
|
|
|
27493
27734
|
* never truncates the file. Only called by the registry's create-once seed. */
|
|
27494
27735
|
replace(identity) {
|
|
27495
27736
|
const p = storePath4(this.dataDir);
|
|
27496
|
-
|
|
27737
|
+
fs30.mkdirSync(path41.dirname(p), { recursive: true });
|
|
27497
27738
|
const tmp = `${p}.tmp`;
|
|
27498
|
-
|
|
27499
|
-
|
|
27739
|
+
fs30.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
|
|
27740
|
+
fs30.renameSync(tmp, p);
|
|
27500
27741
|
}
|
|
27501
27742
|
};
|
|
27502
27743
|
var InstanceIdentityRegistry = class {
|
|
@@ -27552,8 +27793,8 @@ function getInstanceIdentity(dataDir) {
|
|
|
27552
27793
|
}
|
|
27553
27794
|
|
|
27554
27795
|
// src/utils/secrets.ts
|
|
27555
|
-
var
|
|
27556
|
-
var
|
|
27796
|
+
var fs31 = __toESM(require("fs"));
|
|
27797
|
+
var path42 = __toESM(require("path"));
|
|
27557
27798
|
var ENV_FALLBACK = {
|
|
27558
27799
|
"git-token": ["WAIRON_GIT_TOKEN"],
|
|
27559
27800
|
"notion-token": ["WAIRON_NOTION_TOKEN"],
|
|
@@ -27562,13 +27803,13 @@ var ENV_FALLBACK = {
|
|
|
27562
27803
|
};
|
|
27563
27804
|
function storePath5() {
|
|
27564
27805
|
const dataDir = process.env["WAIRON_DATA_DIR"];
|
|
27565
|
-
return dataDir ?
|
|
27806
|
+
return dataDir ? path42.join(dataDir, "auth", "secrets.json") : null;
|
|
27566
27807
|
}
|
|
27567
27808
|
function readStore() {
|
|
27568
27809
|
const p = storePath5();
|
|
27569
27810
|
if (!p) return {};
|
|
27570
27811
|
try {
|
|
27571
|
-
return JSON.parse(
|
|
27812
|
+
return JSON.parse(fs31.readFileSync(p, "utf8"));
|
|
27572
27813
|
} catch {
|
|
27573
27814
|
return {};
|
|
27574
27815
|
}
|
|
@@ -27593,10 +27834,10 @@ function setSecret(key, value) {
|
|
|
27593
27834
|
if (!p) throw new Error("WAIRON_DATA_DIR is not set \u2014 a running server needs it to store secrets.");
|
|
27594
27835
|
const store = readStore();
|
|
27595
27836
|
store[key] = value;
|
|
27596
|
-
|
|
27837
|
+
fs31.mkdirSync(path42.dirname(p), { recursive: true });
|
|
27597
27838
|
const tmp = `${p}.tmp`;
|
|
27598
|
-
|
|
27599
|
-
|
|
27839
|
+
fs31.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
|
|
27840
|
+
fs31.renameSync(tmp, p);
|
|
27600
27841
|
}
|
|
27601
27842
|
function listSecretKeys() {
|
|
27602
27843
|
return Object.keys(readStore());
|
|
@@ -27674,7 +27915,7 @@ function masterMatches(credential) {
|
|
|
27674
27915
|
if (!master || !credential) return false;
|
|
27675
27916
|
const a = Buffer.from(hashToken(credential), "hex");
|
|
27676
27917
|
const b = Buffer.from(hashToken(master), "hex");
|
|
27677
|
-
return a.length === b.length &&
|
|
27918
|
+
return a.length === b.length && crypto7.timingSafeEqual(a, b);
|
|
27678
27919
|
}
|
|
27679
27920
|
function bootstrapAdminPrincipal() {
|
|
27680
27921
|
const subject = {
|
|
@@ -27722,7 +27963,7 @@ function authenticateMaster(token) {
|
|
|
27722
27963
|
function hashedEquals(a, b) {
|
|
27723
27964
|
const ha = Buffer.from(hashToken(a), "hex");
|
|
27724
27965
|
const hb = Buffer.from(hashToken(b), "hex");
|
|
27725
|
-
return ha.length === hb.length &&
|
|
27966
|
+
return ha.length === hb.length && crypto7.timingSafeEqual(ha, hb);
|
|
27726
27967
|
}
|
|
27727
27968
|
function verifyBuiltinAdmin(cfg, user, password) {
|
|
27728
27969
|
const configuredUser = cfg.builtinAdminUser ?? "";
|
|
@@ -27771,16 +28012,16 @@ function signingKey() {
|
|
|
27771
28012
|
}
|
|
27772
28013
|
function signViewToken(project2, format) {
|
|
27773
28014
|
const body = Buffer.from(JSON.stringify({ project: project2, format, exp: Date.now() + VIEW_TTL_MS })).toString("base64url");
|
|
27774
|
-
const sig =
|
|
28015
|
+
const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27775
28016
|
return `${body}.${sig}`;
|
|
27776
28017
|
}
|
|
27777
28018
|
function verifyViewToken(token) {
|
|
27778
28019
|
const [body, sig] = String(token).split(".");
|
|
27779
28020
|
if (!body || !sig) throw new Error("invalid view token");
|
|
27780
|
-
const expected =
|
|
28021
|
+
const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27781
28022
|
const a = Buffer.from(sig);
|
|
27782
28023
|
const b = Buffer.from(expected);
|
|
27783
|
-
if (a.length !== b.length || !
|
|
28024
|
+
if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid view token");
|
|
27784
28025
|
const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
27785
28026
|
if (Date.now() > payload.exp) throw new Error("expired view token");
|
|
27786
28027
|
return { project: payload.project, format: payload.format, expiresAt: new Date(payload.exp).toISOString() };
|
|
@@ -27788,33 +28029,33 @@ function verifyViewToken(token) {
|
|
|
27788
28029
|
var SSO_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
27789
28030
|
function signSsoState(payload) {
|
|
27790
28031
|
const body = Buffer.from(JSON.stringify({ payload, exp: Date.now() + SSO_STATE_TTL_MS })).toString("base64url");
|
|
27791
|
-
const sig =
|
|
28032
|
+
const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27792
28033
|
return `${body}.${sig}`;
|
|
27793
28034
|
}
|
|
27794
28035
|
function verifySsoState(state) {
|
|
27795
28036
|
const [body, sig] = String(state).split(".");
|
|
27796
28037
|
if (!body || !sig) throw new Error("invalid SSO state");
|
|
27797
|
-
const expected =
|
|
28038
|
+
const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
|
|
27798
28039
|
const a = Buffer.from(sig);
|
|
27799
28040
|
const b = Buffer.from(expected);
|
|
27800
|
-
if (a.length !== b.length || !
|
|
28041
|
+
if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid SSO state");
|
|
27801
28042
|
const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
27802
28043
|
if (Date.now() > parsed.exp) throw new Error("expired SSO state");
|
|
27803
28044
|
return parsed.payload;
|
|
27804
28045
|
}
|
|
27805
28046
|
|
|
27806
28047
|
// src/server/organization.ts
|
|
27807
|
-
var
|
|
27808
|
-
var
|
|
27809
|
-
var
|
|
28048
|
+
var fs32 = __toESM(require("fs"));
|
|
28049
|
+
var path43 = __toESM(require("path"));
|
|
28050
|
+
var crypto8 = __toESM(require("crypto"));
|
|
27810
28051
|
function storePath6(dataDir) {
|
|
27811
|
-
return
|
|
28052
|
+
return path43.join(dataDir, "organization.json");
|
|
27812
28053
|
}
|
|
27813
28054
|
function readState(dataDir) {
|
|
27814
28055
|
const p = storePath6(dataDir);
|
|
27815
28056
|
let raw;
|
|
27816
28057
|
try {
|
|
27817
|
-
raw =
|
|
28058
|
+
raw = fs32.readFileSync(p, "utf8");
|
|
27818
28059
|
} catch (e) {
|
|
27819
28060
|
if (e.code === "ENOENT") return { units: [], placements: [] };
|
|
27820
28061
|
throw new Error(`Failed to read organization store at ${p}: ${e.message}`);
|
|
@@ -27831,10 +28072,10 @@ function readState(dataDir) {
|
|
|
27831
28072
|
}
|
|
27832
28073
|
function persistState(dataDir, state) {
|
|
27833
28074
|
const p = storePath6(dataDir);
|
|
27834
|
-
|
|
28075
|
+
fs32.mkdirSync(path43.dirname(p), { recursive: true });
|
|
27835
28076
|
const tmp = `${p}.tmp`;
|
|
27836
|
-
|
|
27837
|
-
|
|
28077
|
+
fs32.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
|
|
28078
|
+
fs32.renameSync(tmp, p);
|
|
27838
28079
|
}
|
|
27839
28080
|
var SLUG_PATTERN = /^[a-z0-9-]+$/;
|
|
27840
28081
|
var UNIT_KINDS = ["business_entity", "department", "team", "group"];
|
|
@@ -28129,7 +28370,7 @@ var OrganizationRegistry = class {
|
|
|
28129
28370
|
createdAt: placements[existingIdx].createdAt
|
|
28130
28371
|
} : {
|
|
28131
28372
|
...placement,
|
|
28132
|
-
id: placement.id ||
|
|
28373
|
+
id: placement.id || crypto8.randomUUID(),
|
|
28133
28374
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28134
28375
|
};
|
|
28135
28376
|
const next = isUpdate ? placements.map((p, i) => i === existingIdx ? stored : p) : [...placements, stored];
|
|
@@ -28210,11 +28451,11 @@ function getOrganizationUnit(dataDir, id) {
|
|
|
28210
28451
|
}
|
|
28211
28452
|
|
|
28212
28453
|
// src/server/permissions.ts
|
|
28213
|
-
var
|
|
28214
|
-
var
|
|
28454
|
+
var fs33 = __toESM(require("fs"));
|
|
28455
|
+
var path44 = __toESM(require("path"));
|
|
28215
28456
|
var import_crypto2 = require("crypto");
|
|
28216
28457
|
function storePath7(dataDir) {
|
|
28217
|
-
return
|
|
28458
|
+
return path44.join(dataDir, "permissions.json");
|
|
28218
28459
|
}
|
|
28219
28460
|
function assignmentKey(a) {
|
|
28220
28461
|
return [a.subjectKind, a.subjectId ?? "", a.scopeKind, a.scopeId ?? "", a.capability].join("|");
|
|
@@ -28223,7 +28464,7 @@ function load4(dataDir) {
|
|
|
28223
28464
|
const p = storePath7(dataDir);
|
|
28224
28465
|
let raw;
|
|
28225
28466
|
try {
|
|
28226
|
-
raw =
|
|
28467
|
+
raw = fs33.readFileSync(p, "utf8");
|
|
28227
28468
|
} catch (err) {
|
|
28228
28469
|
if (err.code === "ENOENT") return [];
|
|
28229
28470
|
throw new Error(`Cannot read permission store at ${p}: ${err.message}`);
|
|
@@ -28241,10 +28482,10 @@ function load4(dataDir) {
|
|
|
28241
28482
|
}
|
|
28242
28483
|
function replaceAll2(dataDir, assignments) {
|
|
28243
28484
|
const p = storePath7(dataDir);
|
|
28244
|
-
|
|
28485
|
+
fs33.mkdirSync(path44.dirname(p), { recursive: true });
|
|
28245
28486
|
const tmp = `${p}.tmp`;
|
|
28246
|
-
|
|
28247
|
-
|
|
28487
|
+
fs33.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
|
|
28488
|
+
fs33.renameSync(tmp, p);
|
|
28248
28489
|
}
|
|
28249
28490
|
function registrySet(dataDir, assignment) {
|
|
28250
28491
|
const assignments = load4(dataDir);
|
|
@@ -28330,8 +28571,8 @@ function getAssignment(dataDir, assignmentId) {
|
|
|
28330
28571
|
}
|
|
28331
28572
|
|
|
28332
28573
|
// src/server/roles.ts
|
|
28333
|
-
var
|
|
28334
|
-
var
|
|
28574
|
+
var fs34 = __toESM(require("fs"));
|
|
28575
|
+
var path45 = __toESM(require("path"));
|
|
28335
28576
|
var BUILTIN_ROLES = [
|
|
28336
28577
|
{
|
|
28337
28578
|
id: SSO_ADMIN_ROLE_ID,
|
|
@@ -28349,13 +28590,13 @@ function isBuiltinRoleId(roleId) {
|
|
|
28349
28590
|
return BUILTIN_ROLE_IDS.has(roleId);
|
|
28350
28591
|
}
|
|
28351
28592
|
function storePath8(dataDir) {
|
|
28352
|
-
return
|
|
28593
|
+
return path45.join(dataDir, "roles.json");
|
|
28353
28594
|
}
|
|
28354
28595
|
function load5(dataDir) {
|
|
28355
28596
|
const p = storePath8(dataDir);
|
|
28356
28597
|
let raw;
|
|
28357
28598
|
try {
|
|
28358
|
-
raw =
|
|
28599
|
+
raw = fs34.readFileSync(p, "utf8");
|
|
28359
28600
|
} catch (err) {
|
|
28360
28601
|
if (err.code === "ENOENT") return [];
|
|
28361
28602
|
throw new Error(`Cannot read role store at ${p}: ${err.message}`);
|
|
@@ -28373,10 +28614,10 @@ function load5(dataDir) {
|
|
|
28373
28614
|
}
|
|
28374
28615
|
function replaceAll3(dataDir, roles) {
|
|
28375
28616
|
const p = storePath8(dataDir);
|
|
28376
|
-
|
|
28617
|
+
fs34.mkdirSync(path45.dirname(p), { recursive: true });
|
|
28377
28618
|
const tmp = `${p}.tmp`;
|
|
28378
|
-
|
|
28379
|
-
|
|
28619
|
+
fs34.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
|
|
28620
|
+
fs34.renameSync(tmp, p);
|
|
28380
28621
|
}
|
|
28381
28622
|
function registryCreate(dataDir, role) {
|
|
28382
28623
|
if (isBuiltinRoleId(role.id)) {
|
|
@@ -28610,8 +28851,8 @@ function actionableUnitIds(scopes) {
|
|
|
28610
28851
|
}
|
|
28611
28852
|
|
|
28612
28853
|
// src/server/projects.ts
|
|
28613
|
-
var
|
|
28614
|
-
var
|
|
28854
|
+
var fs38 = __toESM(require("fs"));
|
|
28855
|
+
var path49 = __toESM(require("path"));
|
|
28615
28856
|
init_loader();
|
|
28616
28857
|
init_yaml();
|
|
28617
28858
|
init_fs();
|
|
@@ -28630,37 +28871,37 @@ init_types();
|
|
|
28630
28871
|
init_server();
|
|
28631
28872
|
|
|
28632
28873
|
// src/git/config.ts
|
|
28633
|
-
var
|
|
28634
|
-
var
|
|
28874
|
+
var fs35 = __toESM(require("fs"));
|
|
28875
|
+
var path46 = __toESM(require("path"));
|
|
28635
28876
|
init_fs();
|
|
28636
28877
|
function configPath() {
|
|
28637
28878
|
return aiDir("git.json");
|
|
28638
28879
|
}
|
|
28639
28880
|
function readGitConfig() {
|
|
28640
28881
|
try {
|
|
28641
|
-
return JSON.parse(
|
|
28882
|
+
return JSON.parse(fs35.readFileSync(configPath(), "utf8"));
|
|
28642
28883
|
} catch {
|
|
28643
28884
|
return null;
|
|
28644
28885
|
}
|
|
28645
28886
|
}
|
|
28646
28887
|
function writeGitConfig(config) {
|
|
28647
28888
|
const p = configPath();
|
|
28648
|
-
|
|
28889
|
+
fs35.mkdirSync(path46.dirname(p), { recursive: true });
|
|
28649
28890
|
const tmp = `${p}.tmp`;
|
|
28650
|
-
|
|
28651
|
-
|
|
28891
|
+
fs35.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
|
|
28892
|
+
fs35.renameSync(tmp, p);
|
|
28652
28893
|
}
|
|
28653
28894
|
function clearGitConfig() {
|
|
28654
28895
|
try {
|
|
28655
|
-
|
|
28896
|
+
fs35.rmSync(configPath(), { force: true });
|
|
28656
28897
|
} catch {
|
|
28657
28898
|
}
|
|
28658
28899
|
}
|
|
28659
28900
|
|
|
28660
28901
|
// src/git/adapter.ts
|
|
28661
28902
|
var import_child_process3 = require("child_process");
|
|
28662
|
-
var
|
|
28663
|
-
var
|
|
28903
|
+
var fs36 = __toESM(require("fs"));
|
|
28904
|
+
var path47 = __toESM(require("path"));
|
|
28664
28905
|
init_fs();
|
|
28665
28906
|
function git(args, cwd) {
|
|
28666
28907
|
return (0, import_child_process3.execFileSync)("git", args, {
|
|
@@ -28712,10 +28953,10 @@ function compareUrl(remote, defaultBranch, workingBranch) {
|
|
|
28712
28953
|
return `${web}/compare/${encodeURIComponent(defaultBranch)}...${encodeURIComponent(workingBranch)}`;
|
|
28713
28954
|
}
|
|
28714
28955
|
function excludeLocalFiles() {
|
|
28715
|
-
const excludePath =
|
|
28956
|
+
const excludePath = path47.join(getProjectRoot(), ".git", "info", "exclude");
|
|
28716
28957
|
try {
|
|
28717
|
-
|
|
28718
|
-
|
|
28958
|
+
fs36.mkdirSync(path47.dirname(excludePath), { recursive: true });
|
|
28959
|
+
fs36.appendFileSync(excludePath, "\n.wai/lock.json\n.wai/git.json\n");
|
|
28719
28960
|
} catch {
|
|
28720
28961
|
}
|
|
28721
28962
|
}
|
|
@@ -28780,25 +29021,25 @@ function configureSync(periodicSyncMinutes, skipIfClean) {
|
|
|
28780
29021
|
}
|
|
28781
29022
|
|
|
28782
29023
|
// src/producers/config.ts
|
|
28783
|
-
var
|
|
28784
|
-
var
|
|
29024
|
+
var fs37 = __toESM(require("fs"));
|
|
29025
|
+
var path48 = __toESM(require("path"));
|
|
28785
29026
|
init_fs();
|
|
28786
29027
|
function configPath2() {
|
|
28787
29028
|
return aiDir("producers.json");
|
|
28788
29029
|
}
|
|
28789
29030
|
function load6() {
|
|
28790
29031
|
try {
|
|
28791
|
-
return JSON.parse(
|
|
29032
|
+
return JSON.parse(fs37.readFileSync(configPath2(), "utf8"));
|
|
28792
29033
|
} catch {
|
|
28793
29034
|
return [];
|
|
28794
29035
|
}
|
|
28795
29036
|
}
|
|
28796
29037
|
function save2(configs) {
|
|
28797
29038
|
const p = configPath2();
|
|
28798
|
-
|
|
29039
|
+
fs37.mkdirSync(path48.dirname(p), { recursive: true });
|
|
28799
29040
|
const tmp = `${p}.tmp`;
|
|
28800
|
-
|
|
28801
|
-
|
|
29041
|
+
fs37.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
|
|
29042
|
+
fs37.renameSync(tmp, p);
|
|
28802
29043
|
}
|
|
28803
29044
|
function readProducerConfig(target) {
|
|
28804
29045
|
return load6().find((c) => c.target === target) ?? null;
|
|
@@ -29216,24 +29457,24 @@ function isValidProjectId(id) {
|
|
|
29216
29457
|
return typeof id === "string" && ID_RE.test(id);
|
|
29217
29458
|
}
|
|
29218
29459
|
function registryPath(dataDir) {
|
|
29219
|
-
return
|
|
29460
|
+
return path49.join(dataDir, "projects.json");
|
|
29220
29461
|
}
|
|
29221
29462
|
function load7(dataDir) {
|
|
29222
29463
|
try {
|
|
29223
|
-
return JSON.parse(
|
|
29464
|
+
return JSON.parse(fs38.readFileSync(registryPath(dataDir), "utf8"));
|
|
29224
29465
|
} catch {
|
|
29225
29466
|
return [];
|
|
29226
29467
|
}
|
|
29227
29468
|
}
|
|
29228
29469
|
function save3(dataDir, records) {
|
|
29229
29470
|
const p = registryPath(dataDir);
|
|
29230
|
-
|
|
29471
|
+
fs38.mkdirSync(path49.dirname(p), { recursive: true });
|
|
29231
29472
|
const tmp = `${p}.tmp`;
|
|
29232
|
-
|
|
29233
|
-
|
|
29473
|
+
fs38.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
29474
|
+
fs38.renameSync(tmp, p);
|
|
29234
29475
|
}
|
|
29235
29476
|
function projectRoot(dataDir, id) {
|
|
29236
|
-
return
|
|
29477
|
+
return path49.join(dataDir, "projects", id);
|
|
29237
29478
|
}
|
|
29238
29479
|
function existingProjectRoot(dataDir, id) {
|
|
29239
29480
|
if (!isValidProjectId(id)) return null;
|
|
@@ -29249,7 +29490,7 @@ function createProjectRecord(dataDir, id) {
|
|
|
29249
29490
|
throw new Error(`Project "${id}" already exists.`);
|
|
29250
29491
|
}
|
|
29251
29492
|
const root = projectRoot(dataDir, id);
|
|
29252
|
-
|
|
29493
|
+
fs38.mkdirSync(root, { recursive: true });
|
|
29253
29494
|
const record2 = {
|
|
29254
29495
|
id,
|
|
29255
29496
|
rootPath: root,
|
|
@@ -29284,7 +29525,7 @@ function removeProjectRecord(dataDir, id) {
|
|
|
29284
29525
|
const rec = records.find((r) => r.id === id);
|
|
29285
29526
|
if (rec) {
|
|
29286
29527
|
try {
|
|
29287
|
-
|
|
29528
|
+
fs38.rmSync(rec.rootPath, { recursive: true, force: true });
|
|
29288
29529
|
} catch {
|
|
29289
29530
|
}
|
|
29290
29531
|
}
|
|
@@ -29300,7 +29541,7 @@ function parseQualifiedSelector(value) {
|
|
|
29300
29541
|
}
|
|
29301
29542
|
function findSubsystemSpec(root, subsystemId) {
|
|
29302
29543
|
const specsDir = aiPathsAt(root).specsDir();
|
|
29303
|
-
if (!
|
|
29544
|
+
if (!fs38.existsSync(specsDir)) return null;
|
|
29304
29545
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
29305
29546
|
let raw;
|
|
29306
29547
|
try {
|
|
@@ -29478,9 +29719,9 @@ function mintKey(cfg, credential, project2, role) {
|
|
|
29478
29719
|
"instance-wide super-admin (*:*) keys cannot be minted \u2014 the built-in admin account (WAIRON_ADMIN_USER) is the only super-admin"
|
|
29479
29720
|
);
|
|
29480
29721
|
}
|
|
29481
|
-
const token = "wk_" +
|
|
29722
|
+
const token = "wk_" + crypto9.randomBytes(24).toString("hex");
|
|
29482
29723
|
const record2 = {
|
|
29483
|
-
id:
|
|
29724
|
+
id: crypto9.randomBytes(6).toString("hex"),
|
|
29484
29725
|
keyHash: hashToken(token),
|
|
29485
29726
|
role,
|
|
29486
29727
|
projects: project2 === "*" ? ["*"] : [project2],
|
|
@@ -29676,31 +29917,10 @@ function diagramViewLink(cfg, credential, project2) {
|
|
|
29676
29917
|
if (!existingProjectRoot(cfg.dataDir, project2)) throw new Error(`Unknown project "${project2}".`);
|
|
29677
29918
|
return `/view/diagram?token=${signViewToken(project2, "canvas")}`;
|
|
29678
29919
|
}
|
|
29679
|
-
function promoteProject(cfg, credential, project2) {
|
|
29680
|
-
const principal = requirePrincipal(cfg, credential);
|
|
29681
|
-
if (authorize(cfg.dataDir, principal, "project:write", "project", project2).value !== "yes") {
|
|
29682
|
-
throw new AdminAuthError("Forbidden \u2014 promoting a project requires project:write over it");
|
|
29683
|
-
}
|
|
29684
|
-
return executeApprovedPromote(cfg, project2);
|
|
29685
|
-
}
|
|
29686
|
-
function executeApprovedPromote(cfg, projectId, subproject) {
|
|
29687
|
-
const root = boundLifecycleRoot(cfg, projectId, subproject);
|
|
29688
|
-
return runWithProjectRoot(root, () => {
|
|
29689
|
-
const { state, record: lock, current: current2 } = hostCore.readLockState();
|
|
29690
|
-
if (state === "unlocked" || !lock) {
|
|
29691
|
-
return { status: "not-locked", message: "Project is not locked; run lock first." };
|
|
29692
|
-
}
|
|
29693
|
-
if (state === "stale") {
|
|
29694
|
-
return { status: "stale", stateId: current2, message: "Spec tree or governing doctrine changed since lock; re-lock required." };
|
|
29695
|
-
}
|
|
29696
|
-
hostCore.writeLockRecord({ ...lock, status: "promoted" });
|
|
29697
|
-
return { status: "ready", stateId: current2, message: "Locked state matches; change-set marked ready for promotion." };
|
|
29698
|
-
});
|
|
29699
|
-
}
|
|
29700
29920
|
|
|
29701
29921
|
// src/server/packs.ts
|
|
29702
|
-
var
|
|
29703
|
-
var
|
|
29922
|
+
var fs39 = __toESM(require("fs"));
|
|
29923
|
+
var path50 = __toESM(require("path"));
|
|
29704
29924
|
init_fs();
|
|
29705
29925
|
init_yaml();
|
|
29706
29926
|
init_loader();
|
|
@@ -29746,19 +29966,19 @@ function assertNotCodeArchive(info) {
|
|
|
29746
29966
|
function extractDeclarativeArchive(archive, destDir) {
|
|
29747
29967
|
const result = hostSdk.extractArchive(archive, destDir, HOSTED_STRICT_LIMITS);
|
|
29748
29968
|
try {
|
|
29749
|
-
assertDeclarative(
|
|
29969
|
+
assertDeclarative(fs39.readFileSync(path50.join(result.directory, result.entryPath), "utf8"));
|
|
29750
29970
|
} catch (e) {
|
|
29751
|
-
|
|
29971
|
+
fs39.rmSync(destDir, { recursive: true, force: true });
|
|
29752
29972
|
throw e;
|
|
29753
29973
|
}
|
|
29754
29974
|
}
|
|
29755
29975
|
function probe(loadRef, baseRoot, scope, displayRef) {
|
|
29756
29976
|
const loaded = hostCore.loadExtensionPacks([{ ref: loadRef, scope }], baseRoot);
|
|
29757
29977
|
if (loaded.errors.length) {
|
|
29758
|
-
return { name:
|
|
29978
|
+
return { name: path50.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
|
|
29759
29979
|
}
|
|
29760
29980
|
return {
|
|
29761
|
-
name: loaded.packNames[0] ??
|
|
29981
|
+
name: loaded.packNames[0] ?? path50.basename(displayRef),
|
|
29762
29982
|
scope,
|
|
29763
29983
|
ref: displayRef,
|
|
29764
29984
|
profiles: Object.keys(loaded.profiles).length,
|
|
@@ -29772,20 +29992,20 @@ function probe(loadRef, baseRoot, scope, displayRef) {
|
|
|
29772
29992
|
};
|
|
29773
29993
|
}
|
|
29774
29994
|
function writeFileAtomic(file, content) {
|
|
29775
|
-
|
|
29995
|
+
fs39.mkdirSync(path50.dirname(file), { recursive: true });
|
|
29776
29996
|
const tmp = `${file}.tmp`;
|
|
29777
|
-
|
|
29778
|
-
|
|
29997
|
+
fs39.writeFileSync(tmp, content);
|
|
29998
|
+
fs39.renameSync(tmp, file);
|
|
29779
29999
|
}
|
|
29780
30000
|
function stem(ref) {
|
|
29781
|
-
return
|
|
30001
|
+
return path50.basename(ref).replace(PACK_EXT_RE, "");
|
|
29782
30002
|
}
|
|
29783
30003
|
function imagePacksDir() {
|
|
29784
30004
|
return process.env.WAIRON_IMAGE_PACKS_DIR ?? "/opt/wairon/packs";
|
|
29785
30005
|
}
|
|
29786
30006
|
function probeTier(dir, tier) {
|
|
29787
30007
|
return hostCore.discoverPacks(dir).map((full) => {
|
|
29788
|
-
const d = probe(full,
|
|
30008
|
+
const d = probe(full, path50.dirname(full), "global", path50.basename(full));
|
|
29789
30009
|
d.tier = tier;
|
|
29790
30010
|
return d;
|
|
29791
30011
|
});
|
|
@@ -29803,9 +30023,9 @@ function scanGlobalPackProfiles() {
|
|
|
29803
30023
|
for (const dir of [hostCore.globalPacksDir(), imagePacksDir()]) {
|
|
29804
30024
|
for (const full of hostCore.discoverPacks(dir)) {
|
|
29805
30025
|
try {
|
|
29806
|
-
const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }],
|
|
30026
|
+
const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }], path50.dirname(full));
|
|
29807
30027
|
if (loaded.errors.length) continue;
|
|
29808
|
-
const source = loaded.packNames[0] ??
|
|
30028
|
+
const source = loaded.packNames[0] ?? path50.basename(full);
|
|
29809
30029
|
for (const [id, def] of Object.entries(loaded.profiles)) out.push({ id, source, family: def.family });
|
|
29810
30030
|
} catch {
|
|
29811
30031
|
}
|
|
@@ -29857,12 +30077,12 @@ function storeListProjectProfiles() {
|
|
|
29857
30077
|
return out;
|
|
29858
30078
|
}
|
|
29859
30079
|
function readPackContent(full) {
|
|
29860
|
-
const st =
|
|
29861
|
-
if (st.isFile()) return
|
|
30080
|
+
const st = fs39.statSync(full);
|
|
30081
|
+
if (st.isFile()) return fs39.readFileSync(full, "utf8");
|
|
29862
30082
|
if (st.isDirectory()) {
|
|
29863
30083
|
for (const entry of ["pack.yaml", "pack.yml"]) {
|
|
29864
|
-
const p =
|
|
29865
|
-
if (
|
|
30084
|
+
const p = path50.join(full, entry);
|
|
30085
|
+
if (fs39.existsSync(p) && fs39.statSync(p).isFile()) return fs39.readFileSync(p, "utf8");
|
|
29866
30086
|
}
|
|
29867
30087
|
}
|
|
29868
30088
|
return null;
|
|
@@ -29871,9 +30091,9 @@ function storeResolveGlobalPacks(names) {
|
|
|
29871
30091
|
const index = /* @__PURE__ */ new Map();
|
|
29872
30092
|
const indexTier = (dir, tier) => {
|
|
29873
30093
|
for (const full of hostCore.discoverPacks(dir)) {
|
|
29874
|
-
const manifestName = probe(full,
|
|
30094
|
+
const manifestName = probe(full, path50.dirname(full), "global", path50.basename(full)).name;
|
|
29875
30095
|
const candidate = { full, manifestName, tier };
|
|
29876
|
-
for (const key of [manifestName, stem(full),
|
|
30096
|
+
for (const key of [manifestName, stem(full), path50.basename(full)]) {
|
|
29877
30097
|
if (!index.has(key)) index.set(key, candidate);
|
|
29878
30098
|
}
|
|
29879
30099
|
}
|
|
@@ -29900,7 +30120,7 @@ function storeInstallGlobalPack(name, content) {
|
|
|
29900
30120
|
assertName(name);
|
|
29901
30121
|
assertDeclarative(content);
|
|
29902
30122
|
const dir = hostCore.globalPacksDir();
|
|
29903
|
-
const file =
|
|
30123
|
+
const file = path50.join(dir, `${name}.yaml`);
|
|
29904
30124
|
writeFileAtomic(file, content);
|
|
29905
30125
|
const descriptor = probe(file, dir, "global", `${name}.yaml`);
|
|
29906
30126
|
descriptor.tier = "instance";
|
|
@@ -29911,7 +30131,7 @@ function storeInstallGlobalPackArchive(archive, name) {
|
|
|
29911
30131
|
assertNotCodeArchive(info);
|
|
29912
30132
|
const packName = name ?? info.name;
|
|
29913
30133
|
assertName(packName);
|
|
29914
|
-
const dir =
|
|
30134
|
+
const dir = path50.join(hostCore.globalPacksDir(), packName);
|
|
29915
30135
|
extractDeclarativeArchive(archive, dir);
|
|
29916
30136
|
const descriptor = probe(dir, hostCore.globalPacksDir(), "global", packName);
|
|
29917
30137
|
descriptor.tier = "instance";
|
|
@@ -29920,16 +30140,16 @@ function storeInstallGlobalPackArchive(archive, name) {
|
|
|
29920
30140
|
function storeRemoveGlobalPack(name) {
|
|
29921
30141
|
assertName(name);
|
|
29922
30142
|
const dir = hostCore.globalPacksDir();
|
|
29923
|
-
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);
|
|
29924
30144
|
if (!match) {
|
|
29925
30145
|
const inImage = hostCore.discoverPacks(imagePacksDir()).some(
|
|
29926
|
-
(ref) => stem(ref) === name ||
|
|
30146
|
+
(ref) => stem(ref) === name || path50.basename(ref) === name
|
|
29927
30147
|
);
|
|
29928
30148
|
throw new Error(
|
|
29929
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}".`
|
|
29930
30150
|
);
|
|
29931
30151
|
}
|
|
29932
|
-
|
|
30152
|
+
fs39.rmSync(match, { recursive: true, force: true });
|
|
29933
30153
|
}
|
|
29934
30154
|
function storeListProjectPacks() {
|
|
29935
30155
|
const root = getProjectRoot();
|
|
@@ -29945,7 +30165,7 @@ function storeInstallProjectPack(name, content) {
|
|
|
29945
30165
|
assertDeclarative(content);
|
|
29946
30166
|
const root = getProjectRoot();
|
|
29947
30167
|
const relRef = `.wai/packs/${name}.yaml`;
|
|
29948
|
-
writeFileAtomic(
|
|
30168
|
+
writeFileAtomic(path50.join(root, ".wai", "packs", `${name}.yaml`), content);
|
|
29949
30169
|
const config = loadProjectConfig();
|
|
29950
30170
|
const packs = config.extensions?.packs ?? [];
|
|
29951
30171
|
if (!packs.includes(relRef)) {
|
|
@@ -29961,7 +30181,7 @@ function storeInstallProjectPackArchive(archive, name) {
|
|
|
29961
30181
|
assertName(packName);
|
|
29962
30182
|
const root = getProjectRoot();
|
|
29963
30183
|
const relRef = `.wai/packs/${packName}`;
|
|
29964
|
-
extractDeclarativeArchive(archive,
|
|
30184
|
+
extractDeclarativeArchive(archive, path50.join(root, ".wai", "packs", packName));
|
|
29965
30185
|
const config = loadProjectConfig();
|
|
29966
30186
|
const packs = config.extensions?.packs ?? [];
|
|
29967
30187
|
if (!packs.includes(relRef)) {
|
|
@@ -29976,18 +30196,18 @@ function storeRemoveProjectPack(name) {
|
|
|
29976
30196
|
const config = loadProjectConfig();
|
|
29977
30197
|
const packs = config.extensions?.packs ?? [];
|
|
29978
30198
|
const relRef = `.wai/packs/${name}.yaml`;
|
|
29979
|
-
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);
|
|
29980
30200
|
if (!match) throw new Error(`Project has no registered pack named "${name}".`);
|
|
29981
30201
|
config.extensions = { packs: packs.filter((entry) => entry !== match), useGlobalPacks: hostCore.globalPacksEnabled(config) };
|
|
29982
30202
|
saveProjectConfig(config);
|
|
29983
|
-
const resolved =
|
|
29984
|
-
const vendorDir =
|
|
29985
|
-
if (resolved.startsWith(vendorDir +
|
|
29986
|
-
|
|
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 });
|
|
29987
30207
|
}
|
|
29988
30208
|
}
|
|
29989
30209
|
function readProjectReferences(rootPath) {
|
|
29990
|
-
const projectId =
|
|
30210
|
+
const projectId = path50.basename(rootPath);
|
|
29991
30211
|
const empty = { projectId, packNames: [] };
|
|
29992
30212
|
try {
|
|
29993
30213
|
return runWithProjectRoot(rootPath, () => {
|
|
@@ -30110,9 +30330,9 @@ function installProjectPackArchive(cfg, credential, project2, archive, name) {
|
|
|
30110
30330
|
}
|
|
30111
30331
|
|
|
30112
30332
|
// src/server/audit.ts
|
|
30113
|
-
var
|
|
30114
|
-
var
|
|
30115
|
-
var
|
|
30333
|
+
var fs40 = __toESM(require("fs"));
|
|
30334
|
+
var path51 = __toESM(require("path"));
|
|
30335
|
+
var crypto10 = __toESM(require("crypto"));
|
|
30116
30336
|
var LEVEL_ORDER = {
|
|
30117
30337
|
debug: 0,
|
|
30118
30338
|
info: 1,
|
|
@@ -30134,13 +30354,13 @@ var DEFAULT_AUDIT_POLICY = {
|
|
|
30134
30354
|
metadataMode: "redacted"
|
|
30135
30355
|
};
|
|
30136
30356
|
function storePath9(dataDir) {
|
|
30137
|
-
return
|
|
30357
|
+
return path51.join(dataDir, "audit-events.json");
|
|
30138
30358
|
}
|
|
30139
30359
|
function readEvents(dataDir) {
|
|
30140
30360
|
const p = storePath9(dataDir);
|
|
30141
30361
|
let raw;
|
|
30142
30362
|
try {
|
|
30143
|
-
raw =
|
|
30363
|
+
raw = fs40.readFileSync(p, "utf8");
|
|
30144
30364
|
} catch (e) {
|
|
30145
30365
|
if (e.code === "ENOENT") return [];
|
|
30146
30366
|
throw new Error(`Failed to read audit store at ${p}: ${e.message}`);
|
|
@@ -30155,10 +30375,10 @@ function readEvents(dataDir) {
|
|
|
30155
30375
|
}
|
|
30156
30376
|
function persistEvents(dataDir, events) {
|
|
30157
30377
|
const p = storePath9(dataDir);
|
|
30158
|
-
|
|
30378
|
+
fs40.mkdirSync(path51.dirname(p), { recursive: true });
|
|
30159
30379
|
const tmp = `${p}.tmp`;
|
|
30160
|
-
|
|
30161
|
-
|
|
30380
|
+
fs40.writeFileSync(tmp, JSON.stringify(events, null, 2) + "\n");
|
|
30381
|
+
fs40.renameSync(tmp, p);
|
|
30162
30382
|
}
|
|
30163
30383
|
var SECRET_RE = /Bearer\s+\S/i;
|
|
30164
30384
|
var LONG_HEX_RE = /[0-9a-fA-F]{32,}/;
|
|
@@ -30216,7 +30436,7 @@ var AuditRegistry = class {
|
|
|
30216
30436
|
}
|
|
30217
30437
|
const stamped = {
|
|
30218
30438
|
...event,
|
|
30219
|
-
id: event.id && event.id.length > 0 ? event.id :
|
|
30439
|
+
id: event.id && event.id.length > 0 ? event.id : crypto10.randomUUID(),
|
|
30220
30440
|
timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : (/* @__PURE__ */ new Date()).toISOString(),
|
|
30221
30441
|
metadata
|
|
30222
30442
|
};
|
|
@@ -30483,10 +30703,10 @@ function unbindRole(cfg, credential, userId, roleId, scopeKind, scopeId) {
|
|
|
30483
30703
|
}
|
|
30484
30704
|
|
|
30485
30705
|
// src/server/identity.ts
|
|
30486
|
-
var
|
|
30706
|
+
var crypto12 = __toESM(require("crypto"));
|
|
30487
30707
|
|
|
30488
30708
|
// src/server/idp.ts
|
|
30489
|
-
var
|
|
30709
|
+
var crypto11 = __toESM(require("crypto"));
|
|
30490
30710
|
var REQUESTED_SCOPE = "openid email profile";
|
|
30491
30711
|
var discoveryCache = /* @__PURE__ */ new Map();
|
|
30492
30712
|
var jwksCache = /* @__PURE__ */ new Map();
|
|
@@ -30713,7 +30933,7 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
|
|
|
30713
30933
|
if (!digest) return false;
|
|
30714
30934
|
let keyObject;
|
|
30715
30935
|
try {
|
|
30716
|
-
keyObject =
|
|
30936
|
+
keyObject = crypto11.createPublicKey({ key: jwk, format: "jwk" });
|
|
30717
30937
|
} catch {
|
|
30718
30938
|
return false;
|
|
30719
30939
|
}
|
|
@@ -30721,18 +30941,18 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
|
|
|
30721
30941
|
const sig = Buffer.from(sigB64url, "base64url");
|
|
30722
30942
|
try {
|
|
30723
30943
|
if (alg.startsWith("RS")) {
|
|
30724
|
-
return
|
|
30944
|
+
return crypto11.verify(digest, data, keyObject, sig);
|
|
30725
30945
|
}
|
|
30726
30946
|
if (alg.startsWith("PS")) {
|
|
30727
|
-
return
|
|
30947
|
+
return crypto11.verify(
|
|
30728
30948
|
digest,
|
|
30729
30949
|
data,
|
|
30730
|
-
{ key: keyObject, padding:
|
|
30950
|
+
{ key: keyObject, padding: crypto11.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto11.constants.RSA_PSS_SALTLEN_DIGEST },
|
|
30731
30951
|
sig
|
|
30732
30952
|
);
|
|
30733
30953
|
}
|
|
30734
30954
|
if (alg.startsWith("ES")) {
|
|
30735
|
-
return
|
|
30955
|
+
return crypto11.verify(digest, data, { key: keyObject, dsaEncoding: "ieee-p1363" }, sig);
|
|
30736
30956
|
}
|
|
30737
30957
|
} catch {
|
|
30738
30958
|
return false;
|
|
@@ -30846,8 +31066,8 @@ function assertAllowedRedirectUri(provider, redirectUri) {
|
|
|
30846
31066
|
}
|
|
30847
31067
|
|
|
30848
31068
|
// src/server/policy.ts
|
|
30849
|
-
var
|
|
30850
|
-
var
|
|
31069
|
+
var fs41 = __toESM(require("fs"));
|
|
31070
|
+
var path52 = __toESM(require("path"));
|
|
30851
31071
|
init_fs();
|
|
30852
31072
|
init_yaml();
|
|
30853
31073
|
init_loader();
|
|
@@ -30866,13 +31086,13 @@ function sendJson(res, status2, body) {
|
|
|
30866
31086
|
|
|
30867
31087
|
// src/server/policy.ts
|
|
30868
31088
|
function packPolicyPath(dataDir) {
|
|
30869
|
-
return
|
|
31089
|
+
return path52.join(dataDir, "pack-policy.json");
|
|
30870
31090
|
}
|
|
30871
31091
|
function getPackPolicyRecord(dataDir) {
|
|
30872
31092
|
const p = packPolicyPath(dataDir);
|
|
30873
31093
|
let raw;
|
|
30874
31094
|
try {
|
|
30875
|
-
raw =
|
|
31095
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30876
31096
|
} catch (e) {
|
|
30877
31097
|
if (e.code === "ENOENT") return null;
|
|
30878
31098
|
throw new Error(`Failed to read pack policy store at ${p}: ${e.message}`);
|
|
@@ -30886,14 +31106,14 @@ function getPackPolicyRecord(dataDir) {
|
|
|
30886
31106
|
function setPackPolicyRecord(dataDir, policy) {
|
|
30887
31107
|
const stored = { ...policy, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
30888
31108
|
const p = packPolicyPath(dataDir);
|
|
30889
|
-
|
|
31109
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30890
31110
|
const tmp = `${p}.tmp`;
|
|
30891
|
-
|
|
30892
|
-
|
|
31111
|
+
fs41.writeFileSync(tmp, JSON.stringify(stored, null, 2) + "\n");
|
|
31112
|
+
fs41.renameSync(tmp, p);
|
|
30893
31113
|
return stored;
|
|
30894
31114
|
}
|
|
30895
31115
|
function exposurePolicyPath(dataDir) {
|
|
30896
|
-
return
|
|
31116
|
+
return path52.join(dataDir, "exposure-policy.json");
|
|
30897
31117
|
}
|
|
30898
31118
|
var COMPATIBLE_DEFAULT_EXPOSURE = {
|
|
30899
31119
|
adminApiMode: "local_only",
|
|
@@ -30910,7 +31130,7 @@ function getExposurePolicyRecord(dataDir) {
|
|
|
30910
31130
|
const p = exposurePolicyPath(dataDir);
|
|
30911
31131
|
let raw;
|
|
30912
31132
|
try {
|
|
30913
|
-
raw =
|
|
31133
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30914
31134
|
} catch (e) {
|
|
30915
31135
|
if (e.code === "ENOENT") return null;
|
|
30916
31136
|
throw new Error(`Failed to read exposure policy store at ${p}: ${e.message}`);
|
|
@@ -30923,10 +31143,10 @@ function getExposurePolicyRecord(dataDir) {
|
|
|
30923
31143
|
}
|
|
30924
31144
|
function setExposurePolicyRecord(dataDir, policy) {
|
|
30925
31145
|
const p = exposurePolicyPath(dataDir);
|
|
30926
|
-
|
|
31146
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30927
31147
|
const tmp = `${p}.tmp`;
|
|
30928
|
-
|
|
30929
|
-
|
|
31148
|
+
fs41.writeFileSync(tmp, JSON.stringify(policy, null, 2) + "\n");
|
|
31149
|
+
fs41.renameSync(tmp, p);
|
|
30930
31150
|
return policy;
|
|
30931
31151
|
}
|
|
30932
31152
|
var PERMISSIVE_DEFAULT_POLICY = {
|
|
@@ -30944,7 +31164,7 @@ function effectivePolicy(dataDir) {
|
|
|
30944
31164
|
return getPackPolicyRecord(dataDir) ?? PERMISSIVE_DEFAULT_POLICY;
|
|
30945
31165
|
}
|
|
30946
31166
|
function identityProvidersPath(dataDir) {
|
|
30947
|
-
return
|
|
31167
|
+
return path52.join(dataDir, "identity-providers.json");
|
|
30948
31168
|
}
|
|
30949
31169
|
function sanitizeIdentityProvider(config) {
|
|
30950
31170
|
const clean = {
|
|
@@ -30968,16 +31188,16 @@ function sanitizeIdentityProvider(config) {
|
|
|
30968
31188
|
}
|
|
30969
31189
|
function writeIdentityProviderRecords(dataDir, records) {
|
|
30970
31190
|
const p = identityProvidersPath(dataDir);
|
|
30971
|
-
|
|
31191
|
+
fs41.mkdirSync(path52.dirname(p), { recursive: true });
|
|
30972
31192
|
const tmp = `${p}.tmp`;
|
|
30973
|
-
|
|
30974
|
-
|
|
31193
|
+
fs41.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
31194
|
+
fs41.renameSync(tmp, p);
|
|
30975
31195
|
}
|
|
30976
31196
|
function listIdentityProviderRecords(dataDir) {
|
|
30977
31197
|
const p = identityProvidersPath(dataDir);
|
|
30978
31198
|
let raw;
|
|
30979
31199
|
try {
|
|
30980
|
-
raw =
|
|
31200
|
+
raw = fs41.readFileSync(p, "utf8");
|
|
30981
31201
|
} catch (e) {
|
|
30982
31202
|
if (e.code === "ENOENT") return [];
|
|
30983
31203
|
throw new Error(`Failed to read identity provider store at ${p}: ${e.message}`);
|
|
@@ -31604,9 +31824,9 @@ function mintToken(cfg, credential, request) {
|
|
|
31604
31824
|
if (owner && owner.status !== "active") {
|
|
31605
31825
|
throw new ForbiddenError("cannot mint a token for a deactivated user");
|
|
31606
31826
|
}
|
|
31607
|
-
const token = "wk_" +
|
|
31827
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31608
31828
|
const record2 = {
|
|
31609
|
-
id:
|
|
31829
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31610
31830
|
keyHash: hashToken(token),
|
|
31611
31831
|
projects,
|
|
31612
31832
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31650,10 +31870,10 @@ function mintSelfToken(cfg, credential, projectId, write) {
|
|
|
31650
31870
|
if (parsed.mounts.length > 0) {
|
|
31651
31871
|
assertMintableNarrowingEntry(cfg.dataDir, projectId);
|
|
31652
31872
|
}
|
|
31653
|
-
const token = "wk_" +
|
|
31873
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31654
31874
|
const owner = auditActor(principal);
|
|
31655
31875
|
const record2 = {
|
|
31656
|
-
id:
|
|
31876
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31657
31877
|
keyHash: hashToken(token),
|
|
31658
31878
|
projects: [projectId],
|
|
31659
31879
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31768,7 +31988,7 @@ function resolveEnabledProvider(cfg, providerId) {
|
|
|
31768
31988
|
}
|
|
31769
31989
|
async function startSsoLogin(cfg, providerId, redirectUri) {
|
|
31770
31990
|
const provider = resolveEnabledProvider(cfg, providerId);
|
|
31771
|
-
const nonce =
|
|
31991
|
+
const nonce = crypto12.randomBytes(16).toString("hex");
|
|
31772
31992
|
const payload = { providerId, nonce, redirectUri };
|
|
31773
31993
|
const state = signSsoState(JSON.stringify(payload));
|
|
31774
31994
|
const endpoints = await resolveEndpoints(provider);
|
|
@@ -31819,9 +32039,9 @@ async function completeSsoLogin(cfg, state, code) {
|
|
|
31819
32039
|
...subject.email ? { email: subject.email } : {}
|
|
31820
32040
|
});
|
|
31821
32041
|
}
|
|
31822
|
-
const token = "wk_" +
|
|
32042
|
+
const token = "wk_" + crypto12.randomBytes(24).toString("hex");
|
|
31823
32043
|
const record2 = {
|
|
31824
|
-
id:
|
|
32044
|
+
id: crypto12.randomBytes(6).toString("hex"),
|
|
31825
32045
|
keyHash: hashToken(token),
|
|
31826
32046
|
projects: ["*"],
|
|
31827
32047
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31953,17 +32173,17 @@ init_yaml();
|
|
|
31953
32173
|
init_loader();
|
|
31954
32174
|
|
|
31955
32175
|
// src/server/relations.ts
|
|
31956
|
-
var
|
|
31957
|
-
var
|
|
31958
|
-
var
|
|
32176
|
+
var fs42 = __toESM(require("fs"));
|
|
32177
|
+
var path53 = __toESM(require("path"));
|
|
32178
|
+
var crypto13 = __toESM(require("crypto"));
|
|
31959
32179
|
function storePath10(dataDir) {
|
|
31960
|
-
return
|
|
32180
|
+
return path53.join(dataDir, "relations.json");
|
|
31961
32181
|
}
|
|
31962
32182
|
function readRelations(dataDir) {
|
|
31963
32183
|
const p = storePath10(dataDir);
|
|
31964
32184
|
let raw;
|
|
31965
32185
|
try {
|
|
31966
|
-
raw =
|
|
32186
|
+
raw = fs42.readFileSync(p, "utf8");
|
|
31967
32187
|
} catch (e) {
|
|
31968
32188
|
if (e.code === "ENOENT") return [];
|
|
31969
32189
|
throw new Error(`Failed to read relation store at ${p}: ${e.message}`);
|
|
@@ -31978,10 +32198,10 @@ function readRelations(dataDir) {
|
|
|
31978
32198
|
}
|
|
31979
32199
|
function persistRelations(dataDir, relations) {
|
|
31980
32200
|
const p = storePath10(dataDir);
|
|
31981
|
-
|
|
32201
|
+
fs42.mkdirSync(path53.dirname(p), { recursive: true });
|
|
31982
32202
|
const tmp = `${p}.tmp`;
|
|
31983
|
-
|
|
31984
|
-
|
|
32203
|
+
fs42.writeFileSync(tmp, JSON.stringify(relations, null, 2) + "\n");
|
|
32204
|
+
fs42.renameSync(tmp, p);
|
|
31985
32205
|
}
|
|
31986
32206
|
var ProjectRelationStore = class {
|
|
31987
32207
|
constructor(dataDir) {
|
|
@@ -32024,7 +32244,7 @@ var ProjectRelationRegistry = class {
|
|
|
32024
32244
|
const hasStatus = typeof record2.status === "string" && record2.status.trim().length > 0;
|
|
32025
32245
|
const stored = {
|
|
32026
32246
|
...record2,
|
|
32027
|
-
id: hasId ? record2.id :
|
|
32247
|
+
id: hasId ? record2.id : crypto13.randomUUID(),
|
|
32028
32248
|
status: hasStatus ? record2.status : "active"
|
|
32029
32249
|
};
|
|
32030
32250
|
const relations = this.store.all();
|
|
@@ -32091,16 +32311,16 @@ function listProjectRelations(dataDir, sourceProjectId, targetProjectId, status2
|
|
|
32091
32311
|
}
|
|
32092
32312
|
|
|
32093
32313
|
// src/server/surfaces.ts
|
|
32094
|
-
var
|
|
32095
|
-
var
|
|
32314
|
+
var fs43 = __toESM(require("fs"));
|
|
32315
|
+
var path54 = __toESM(require("path"));
|
|
32096
32316
|
function storePath11(dataDir) {
|
|
32097
|
-
return
|
|
32317
|
+
return path54.join(dataDir, "public-surfaces.json");
|
|
32098
32318
|
}
|
|
32099
32319
|
function readSnapshots(dataDir) {
|
|
32100
32320
|
const p = storePath11(dataDir);
|
|
32101
32321
|
let raw;
|
|
32102
32322
|
try {
|
|
32103
|
-
raw =
|
|
32323
|
+
raw = fs43.readFileSync(p, "utf8");
|
|
32104
32324
|
} catch (e) {
|
|
32105
32325
|
if (e.code === "ENOENT") return [];
|
|
32106
32326
|
throw new Error(`Failed to read public surface store at ${p}: ${e.message}`);
|
|
@@ -32115,10 +32335,10 @@ function readSnapshots(dataDir) {
|
|
|
32115
32335
|
}
|
|
32116
32336
|
function persistSnapshots(dataDir, snapshots) {
|
|
32117
32337
|
const p = storePath11(dataDir);
|
|
32118
|
-
|
|
32338
|
+
fs43.mkdirSync(path54.dirname(p), { recursive: true });
|
|
32119
32339
|
const tmp = `${p}.tmp`;
|
|
32120
|
-
|
|
32121
|
-
|
|
32340
|
+
fs43.writeFileSync(tmp, JSON.stringify(snapshots, null, 2) + "\n");
|
|
32341
|
+
fs43.renameSync(tmp, p);
|
|
32122
32342
|
}
|
|
32123
32343
|
var PublicSurfaceStore = class {
|
|
32124
32344
|
constructor(dataDir) {
|
|
@@ -32261,12 +32481,12 @@ function resolveVisibility(observerProjectId, units, placements) {
|
|
|
32261
32481
|
const best = /* @__PURE__ */ new Map();
|
|
32262
32482
|
for (const placement of placements) {
|
|
32263
32483
|
if (placement.projectId === observerProjectId) continue;
|
|
32264
|
-
const
|
|
32265
|
-
if (!
|
|
32266
|
-
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));
|
|
32267
32487
|
if (!closedOk) continue;
|
|
32268
|
-
const crossTenant = !tenantRoots.has(
|
|
32269
|
-
if (crossTenant && !
|
|
32488
|
+
const crossTenant = !tenantRoots.has(path67[path67.length - 1].id);
|
|
32489
|
+
if (crossTenant && !path67.some(grantedTo)) continue;
|
|
32270
32490
|
if (crossTenant && directUnits.length === 0) continue;
|
|
32271
32491
|
const distance = crossTenant ? "partner" : sameBranch(placement.unitId) ? "department" : "instance";
|
|
32272
32492
|
const existing = best.get(placement.projectId);
|
|
@@ -32912,8 +33132,8 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
|
|
|
32912
33132
|
}
|
|
32913
33133
|
|
|
32914
33134
|
// src/server/migration.ts
|
|
32915
|
-
var
|
|
32916
|
-
var
|
|
33135
|
+
var fs44 = __toESM(require("fs"));
|
|
33136
|
+
var path55 = __toESM(require("path"));
|
|
32917
33137
|
var LEGACY_CAPABILITY_MAP = {
|
|
32918
33138
|
"mcp:read": "project:read",
|
|
32919
33139
|
"operations:read": "project:read",
|
|
@@ -32945,16 +33165,16 @@ function scopeOf(grant) {
|
|
|
32945
33165
|
}
|
|
32946
33166
|
function readRaw(file) {
|
|
32947
33167
|
try {
|
|
32948
|
-
return JSON.parse(
|
|
33168
|
+
return JSON.parse(fs44.readFileSync(file, "utf8"));
|
|
32949
33169
|
} catch {
|
|
32950
33170
|
return null;
|
|
32951
33171
|
}
|
|
32952
33172
|
}
|
|
32953
33173
|
function writeRaw(file, value) {
|
|
32954
|
-
|
|
33174
|
+
fs44.mkdirSync(path55.dirname(file), { recursive: true });
|
|
32955
33175
|
const tmp = `${file}.tmp`;
|
|
32956
|
-
|
|
32957
|
-
|
|
33176
|
+
fs44.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
|
|
33177
|
+
fs44.renameSync(tmp, file);
|
|
32958
33178
|
}
|
|
32959
33179
|
function slugify(name) {
|
|
32960
33180
|
return name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "unit";
|
|
@@ -32988,13 +33208,13 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
32988
33208
|
findings.push({ area, detail });
|
|
32989
33209
|
};
|
|
32990
33210
|
const grid = readRaw(
|
|
32991
|
-
|
|
33211
|
+
path55.join(dataDir, "permissions.json")
|
|
32992
33212
|
) ?? [];
|
|
32993
33213
|
if (!getInstanceIdentity(dataDir)) {
|
|
32994
33214
|
found("instance", "instance identity is not seeded \u2014 the built-in admin/dev subjects have no persisted UUIDs");
|
|
32995
33215
|
if (apply) ensureInstanceIdentity(dataDir);
|
|
32996
33216
|
}
|
|
32997
|
-
const orgPath =
|
|
33217
|
+
const orgPath = path55.join(dataDir, "organization.json");
|
|
32998
33218
|
const org = readRaw(orgPath);
|
|
32999
33219
|
if (org?.units?.length) {
|
|
33000
33220
|
const units = org.units;
|
|
@@ -33048,7 +33268,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33048
33268
|
found("units", `${remap.length} unit id(s) would be remapped across placements, exposeTo, assignments, and users`);
|
|
33049
33269
|
}
|
|
33050
33270
|
}
|
|
33051
|
-
const usersPath =
|
|
33271
|
+
const usersPath = path55.join(dataDir, "users.json");
|
|
33052
33272
|
const users = readRaw(usersPath);
|
|
33053
33273
|
if (users) {
|
|
33054
33274
|
let changed = false;
|
|
@@ -33075,7 +33295,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33075
33295
|
}
|
|
33076
33296
|
if (apply && changed) writeRaw(usersPath, users);
|
|
33077
33297
|
}
|
|
33078
|
-
const keysPath =
|
|
33298
|
+
const keysPath = path55.join(dataDir, "auth", "credentials.json");
|
|
33079
33299
|
const keys = readRaw(keysPath);
|
|
33080
33300
|
if (keys) {
|
|
33081
33301
|
let changed = false;
|
|
@@ -33125,7 +33345,7 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33125
33345
|
}
|
|
33126
33346
|
if (apply && changed) writeRaw(keysPath, keys);
|
|
33127
33347
|
}
|
|
33128
|
-
const sessionsPath =
|
|
33348
|
+
const sessionsPath = path55.join(dataDir, "web-sessions.json");
|
|
33129
33349
|
const sessions = readRaw(sessionsPath);
|
|
33130
33350
|
if (sessions) {
|
|
33131
33351
|
let changed = false;
|
|
@@ -33204,25 +33424,25 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
33204
33424
|
|
|
33205
33425
|
// src/server/http.ts
|
|
33206
33426
|
var http2 = __toESM(require("http"));
|
|
33207
|
-
var
|
|
33208
|
-
var
|
|
33427
|
+
var fs52 = __toESM(require("fs"));
|
|
33428
|
+
var path63 = __toESM(require("path"));
|
|
33209
33429
|
|
|
33210
33430
|
// src/server/request.ts
|
|
33211
33431
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
33212
33432
|
init_fs();
|
|
33213
33433
|
|
|
33214
33434
|
// src/server/approvals.ts
|
|
33215
|
-
var
|
|
33216
|
-
var
|
|
33217
|
-
var
|
|
33435
|
+
var fs45 = __toESM(require("fs"));
|
|
33436
|
+
var path56 = __toESM(require("path"));
|
|
33437
|
+
var crypto14 = __toESM(require("crypto"));
|
|
33218
33438
|
function storePath12(dataDir) {
|
|
33219
|
-
return
|
|
33439
|
+
return path56.join(dataDir, "approvals.json");
|
|
33220
33440
|
}
|
|
33221
33441
|
function readRequests(dataDir) {
|
|
33222
33442
|
const p = storePath12(dataDir);
|
|
33223
33443
|
let raw;
|
|
33224
33444
|
try {
|
|
33225
|
-
raw =
|
|
33445
|
+
raw = fs45.readFileSync(p, "utf8");
|
|
33226
33446
|
} catch (e) {
|
|
33227
33447
|
if (e.code === "ENOENT") return [];
|
|
33228
33448
|
throw new Error(`Failed to read approval store at ${p}: ${e.message}`);
|
|
@@ -33237,10 +33457,10 @@ function readRequests(dataDir) {
|
|
|
33237
33457
|
}
|
|
33238
33458
|
function persistRequests(dataDir, requests) {
|
|
33239
33459
|
const p = storePath12(dataDir);
|
|
33240
|
-
|
|
33460
|
+
fs45.mkdirSync(path56.dirname(p), { recursive: true });
|
|
33241
33461
|
const tmp = `${p}.tmp`;
|
|
33242
|
-
|
|
33243
|
-
|
|
33462
|
+
fs45.writeFileSync(tmp, JSON.stringify(requests, null, 2) + "\n");
|
|
33463
|
+
fs45.renameSync(tmp, p);
|
|
33244
33464
|
}
|
|
33245
33465
|
var SECRET_RE2 = /Bearer\s+\S/i;
|
|
33246
33466
|
var LONG_HEX_RE2 = /[0-9a-fA-F]{32,}/;
|
|
@@ -33288,7 +33508,7 @@ var ApprovalRegistry = class {
|
|
|
33288
33508
|
}
|
|
33289
33509
|
const stored = {
|
|
33290
33510
|
...request,
|
|
33291
|
-
id:
|
|
33511
|
+
id: crypto14.randomUUID(),
|
|
33292
33512
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33293
33513
|
status: "pending"
|
|
33294
33514
|
};
|
|
@@ -33597,23 +33817,6 @@ function lockProject2(cfg, credential, projectId, subproject) {
|
|
|
33597
33817
|
}
|
|
33598
33818
|
});
|
|
33599
33819
|
}
|
|
33600
|
-
function promoteProject2(cfg, credential, projectId, subproject) {
|
|
33601
|
-
return lifecycleAction(cfg, credential, projectId, {
|
|
33602
|
-
action: "project:promote",
|
|
33603
|
-
verb: "Promote",
|
|
33604
|
-
noun: "promotion",
|
|
33605
|
-
subproject,
|
|
33606
|
-
execute: () => {
|
|
33607
|
-
const promo = executeApprovedPromote(cfg, projectId, subproject);
|
|
33608
|
-
return {
|
|
33609
|
-
status: "completed",
|
|
33610
|
-
action: "project:promote",
|
|
33611
|
-
summary: `Promotion of project "${projectId}"${subprojectSuffix(subproject)}: ${promo.status} \u2014 ${promo.message}`,
|
|
33612
|
-
promote: promo
|
|
33613
|
-
};
|
|
33614
|
-
}
|
|
33615
|
-
});
|
|
33616
|
-
}
|
|
33617
33820
|
function subprojectSuffix(subproject) {
|
|
33618
33821
|
return subproject ? ` subproject "${subproject}"` : "";
|
|
33619
33822
|
}
|
|
@@ -33737,11 +33940,6 @@ function executeApproved(cfg, req) {
|
|
|
33737
33940
|
const lock = executeApprovedLock(cfg, req.projectId ?? "", scope);
|
|
33738
33941
|
return `Locked project "${req.projectId}"${subprojectSuffix(scope)} (status: ${lock.status}).`;
|
|
33739
33942
|
}
|
|
33740
|
-
case "project:promote": {
|
|
33741
|
-
const scope = readSubprojectScope(req);
|
|
33742
|
-
const promo = executeApprovedPromote(cfg, req.projectId ?? "", scope);
|
|
33743
|
-
return `Promotion of project "${req.projectId}"${subprojectSuffix(scope)}: ${promo.status} \u2014 ${promo.message}`;
|
|
33744
|
-
}
|
|
33745
33943
|
default:
|
|
33746
33944
|
throw new Error(`Unsupported approval kind "${req.kind}".`);
|
|
33747
33945
|
}
|
|
@@ -33783,7 +33981,7 @@ async function awaitApproval(cfg, credential, requestId, timeoutSeconds) {
|
|
|
33783
33981
|
let current2 = req;
|
|
33784
33982
|
while (current2.status === "pending" && Date.now() < deadline) {
|
|
33785
33983
|
const remaining = deadline - Date.now();
|
|
33786
|
-
await new Promise((
|
|
33984
|
+
await new Promise((resolve27) => setTimeout(resolve27, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
|
|
33787
33985
|
expirePendingApprovals(cfg.dataDir, (/* @__PURE__ */ new Date()).toISOString());
|
|
33788
33986
|
current2 = getApprovalRequestById(cfg.dataDir, requestId) ?? current2;
|
|
33789
33987
|
}
|
|
@@ -34051,12 +34249,12 @@ function handleOperationsRequest(cfg, credential, req, res, url) {
|
|
|
34051
34249
|
}
|
|
34052
34250
|
|
|
34053
34251
|
// src/server/gitbacking.ts
|
|
34054
|
-
var
|
|
34055
|
-
var
|
|
34056
|
-
var
|
|
34252
|
+
var fs46 = __toESM(require("fs"));
|
|
34253
|
+
var path57 = __toESM(require("path"));
|
|
34254
|
+
var crypto15 = __toESM(require("crypto"));
|
|
34057
34255
|
var import_child_process4 = require("child_process");
|
|
34058
34256
|
function storePath13(dataDir) {
|
|
34059
|
-
return
|
|
34257
|
+
return path57.join(dataDir, "git-backing.json");
|
|
34060
34258
|
}
|
|
34061
34259
|
var GitBackingStore = class {
|
|
34062
34260
|
constructor(dataDir) {
|
|
@@ -34068,7 +34266,7 @@ var GitBackingStore = class {
|
|
|
34068
34266
|
const p = storePath13(this.dataDir);
|
|
34069
34267
|
let raw;
|
|
34070
34268
|
try {
|
|
34071
|
-
raw =
|
|
34269
|
+
raw = fs46.readFileSync(p, "utf8");
|
|
34072
34270
|
} catch (err) {
|
|
34073
34271
|
if (err.code === "ENOENT") return [];
|
|
34074
34272
|
throw new Error(`Cannot read git-backing store at ${p}: ${err.message}`);
|
|
@@ -34087,10 +34285,10 @@ var GitBackingStore = class {
|
|
|
34087
34285
|
/** Swap the persisted collection wholesale via write-temp-then-rename. */
|
|
34088
34286
|
replaceAll(bindings) {
|
|
34089
34287
|
const p = storePath13(this.dataDir);
|
|
34090
|
-
|
|
34288
|
+
fs46.mkdirSync(path57.dirname(p), { recursive: true });
|
|
34091
34289
|
const tmp = `${p}.tmp`;
|
|
34092
|
-
|
|
34093
|
-
|
|
34290
|
+
fs46.writeFileSync(tmp, JSON.stringify(bindings, null, 2) + "\n");
|
|
34291
|
+
fs46.renameSync(tmp, p);
|
|
34094
34292
|
}
|
|
34095
34293
|
};
|
|
34096
34294
|
var GitBackingRegistry = class {
|
|
@@ -34109,7 +34307,7 @@ var GitBackingRegistry = class {
|
|
|
34109
34307
|
const bindings = this.store.load();
|
|
34110
34308
|
const stored = {
|
|
34111
34309
|
...binding,
|
|
34112
|
-
id: binding.id ||
|
|
34310
|
+
id: binding.id || crypto15.randomUUID(),
|
|
34113
34311
|
createdAt: binding.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
34114
34312
|
};
|
|
34115
34313
|
const sameScope = (b) => b.scopeKind === stored.scopeKind && (b.scopeId ?? "") === (stored.scopeId ?? "");
|
|
@@ -34195,8 +34393,8 @@ function checkoutBranch(workdir, branch) {
|
|
|
34195
34393
|
}
|
|
34196
34394
|
}
|
|
34197
34395
|
function cloneOrOpen(remote, branch, workdir, credentialRef) {
|
|
34198
|
-
if (!
|
|
34199
|
-
|
|
34396
|
+
if (!fs46.existsSync(path57.join(workdir, ".git"))) {
|
|
34397
|
+
fs46.mkdirSync(workdir, { recursive: true });
|
|
34200
34398
|
git2(["clone", authRemote2(remote, credentialRef), "."], workdir);
|
|
34201
34399
|
git2(["config", "user.name", process.env["WAIRON_GIT_NAME"] || "wairon-bot"], workdir);
|
|
34202
34400
|
git2(["config", "user.email", process.env["WAIRON_GIT_EMAIL"] || "wairon-bot@localhost"], workdir);
|
|
@@ -34208,11 +34406,11 @@ function cloneOrOpen(remote, branch, workdir, credentialRef) {
|
|
|
34208
34406
|
return workdir;
|
|
34209
34407
|
}
|
|
34210
34408
|
function mirrorTree(sourceDir, targetDir) {
|
|
34211
|
-
|
|
34212
|
-
const stat =
|
|
34409
|
+
fs46.rmSync(targetDir, { recursive: true, force: true });
|
|
34410
|
+
const stat = fs46.statSync(sourceDir, { throwIfNoEntry: false });
|
|
34213
34411
|
if (!stat) return;
|
|
34214
|
-
|
|
34215
|
-
|
|
34412
|
+
fs46.mkdirSync(path57.dirname(targetDir), { recursive: true });
|
|
34413
|
+
fs46.cpSync(sourceDir, targetDir, { recursive: true });
|
|
34216
34414
|
}
|
|
34217
34415
|
function commitAndPush(workdir, message) {
|
|
34218
34416
|
git2(["add", "-A"], workdir);
|
|
@@ -34334,7 +34532,7 @@ function unbindScope(cfg, credential, bindingId) {
|
|
|
34334
34532
|
tryAppendAudit7(cfg, buildAuditEvent6(principal, "git.backing.unbind", "security", bindingId));
|
|
34335
34533
|
}
|
|
34336
34534
|
function runMirrorSync(cfg, binding) {
|
|
34337
|
-
const workdir =
|
|
34535
|
+
const workdir = path57.join(cfg.dataDir, "git-backing", binding.id);
|
|
34338
34536
|
cloneOrOpen(binding.remote, binding.branch, workdir, binding.credentialRef);
|
|
34339
34537
|
if (binding.scopeKind === "unit") {
|
|
34340
34538
|
const units = listOrganizationUnits(cfg.dataDir);
|
|
@@ -34342,23 +34540,23 @@ function runMirrorSync(cfg, binding) {
|
|
|
34342
34540
|
const placedIds = new Set(
|
|
34343
34541
|
listProjectPlacements(cfg.dataDir).filter((p) => subtree.has(p.unitId)).map((p) => p.projectId)
|
|
34344
34542
|
);
|
|
34345
|
-
const projectsDir =
|
|
34346
|
-
|
|
34543
|
+
const projectsDir = path57.join(workdir, "projects");
|
|
34544
|
+
fs46.rmSync(projectsDir, { recursive: true, force: true });
|
|
34347
34545
|
for (const rec of listProjectRecords(cfg.dataDir)) {
|
|
34348
34546
|
if (!placedIds.has(rec.id)) continue;
|
|
34349
|
-
mirrorTree(
|
|
34547
|
+
mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
|
|
34350
34548
|
}
|
|
34351
34549
|
} else {
|
|
34352
|
-
const instanceDir =
|
|
34353
|
-
|
|
34550
|
+
const instanceDir = path57.join(workdir, "instance");
|
|
34551
|
+
fs46.rmSync(instanceDir, { recursive: true, force: true });
|
|
34354
34552
|
const files = binding.includeCredentials ? INSTANCE_STRUCTURE_FILES : INSTANCE_STRUCTURE_FILES.filter((rel2) => rel2 !== "auth/credentials.json");
|
|
34355
34553
|
for (const rel2 of files) {
|
|
34356
|
-
mirrorTree(
|
|
34554
|
+
mirrorTree(path57.join(cfg.dataDir, rel2), path57.join(instanceDir, rel2));
|
|
34357
34555
|
}
|
|
34358
|
-
const projectsDir =
|
|
34359
|
-
|
|
34556
|
+
const projectsDir = path57.join(workdir, "projects");
|
|
34557
|
+
fs46.rmSync(projectsDir, { recursive: true, force: true });
|
|
34360
34558
|
for (const rec of listProjectRecords(cfg.dataDir)) {
|
|
34361
|
-
mirrorTree(
|
|
34559
|
+
mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
|
|
34362
34560
|
}
|
|
34363
34561
|
}
|
|
34364
34562
|
const published = commitAndPush(
|
|
@@ -34504,7 +34702,7 @@ function setExposurePolicy2(cfg, credential, exposure) {
|
|
|
34504
34702
|
|
|
34505
34703
|
// src/server/websocket.ts
|
|
34506
34704
|
var import_events = require("events");
|
|
34507
|
-
var
|
|
34705
|
+
var crypto16 = __toESM(require("crypto"));
|
|
34508
34706
|
var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
34509
34707
|
var MAX_PAYLOAD = 1 << 20;
|
|
34510
34708
|
var OP_CONTINUATION = 0;
|
|
@@ -34518,7 +34716,7 @@ function isWebSocketUpgrade(req) {
|
|
|
34518
34716
|
function acceptWebSocket(req, socket) {
|
|
34519
34717
|
const key = req.headers["sec-websocket-key"];
|
|
34520
34718
|
if (!isWebSocketUpgrade(req) || typeof key !== "string") return null;
|
|
34521
|
-
const accept =
|
|
34719
|
+
const accept = crypto16.createHash("sha1").update(key + WS_GUID).digest("base64");
|
|
34522
34720
|
socket.write(
|
|
34523
34721
|
`HTTP/1.1 101 Switching Protocols\r
|
|
34524
34722
|
Upgrade: websocket\r
|
|
@@ -34668,9 +34866,9 @@ function encodeFrame(opcode, payload) {
|
|
|
34668
34866
|
}
|
|
34669
34867
|
|
|
34670
34868
|
// src/server/web.ts
|
|
34671
|
-
var
|
|
34672
|
-
var
|
|
34673
|
-
var
|
|
34869
|
+
var crypto21 = __toESM(require("crypto"));
|
|
34870
|
+
var fs51 = __toESM(require("fs"));
|
|
34871
|
+
var path62 = __toESM(require("path"));
|
|
34674
34872
|
|
|
34675
34873
|
// src/server/webadmin.ts
|
|
34676
34874
|
function listUsers3(cfg, sessionId, project2) {
|
|
@@ -34912,26 +35110,23 @@ function createProject2(cfg, sessionId, id, unitId, profileSelection) {
|
|
|
34912
35110
|
function lockProject3(cfg, sessionId, projectId) {
|
|
34913
35111
|
return lockProject(cfg, sessionId, projectId);
|
|
34914
35112
|
}
|
|
34915
|
-
function promoteProject3(cfg, sessionId, projectId) {
|
|
34916
|
-
return promoteProject(cfg, sessionId, projectId);
|
|
34917
|
-
}
|
|
34918
35113
|
function destroyProject2(cfg, sessionId, id) {
|
|
34919
35114
|
destroyProject(cfg, sessionId, id);
|
|
34920
35115
|
}
|
|
34921
35116
|
|
|
34922
35117
|
// src/server/shareadmin.ts
|
|
34923
|
-
var
|
|
35118
|
+
var crypto20 = __toESM(require("crypto"));
|
|
34924
35119
|
|
|
34925
35120
|
// src/server/sharesnapshots.ts
|
|
34926
|
-
var
|
|
34927
|
-
var
|
|
34928
|
-
var
|
|
35121
|
+
var fs47 = __toESM(require("fs"));
|
|
35122
|
+
var path58 = __toESM(require("path"));
|
|
35123
|
+
var crypto17 = __toESM(require("crypto"));
|
|
34929
35124
|
init_fs();
|
|
34930
35125
|
function snapshotDir(dataDir) {
|
|
34931
|
-
return
|
|
35126
|
+
return path58.join(dataDir, "share-snapshots");
|
|
34932
35127
|
}
|
|
34933
35128
|
function snapshotPath(dataDir, id) {
|
|
34934
|
-
return
|
|
35129
|
+
return path58.join(snapshotDir(dataDir), `${id}.json`);
|
|
34935
35130
|
}
|
|
34936
35131
|
var ShareSnapshotStore = class {
|
|
34937
35132
|
constructor(dataDir) {
|
|
@@ -34939,17 +35134,17 @@ var ShareSnapshotStore = class {
|
|
|
34939
35134
|
}
|
|
34940
35135
|
write(snapshot) {
|
|
34941
35136
|
const p = snapshotPath(this.dataDir, snapshot.id);
|
|
34942
|
-
if (
|
|
34943
|
-
|
|
35137
|
+
if (fs47.existsSync(p)) return;
|
|
35138
|
+
fs47.mkdirSync(path58.dirname(p), { recursive: true });
|
|
34944
35139
|
const tmp = `${p}.tmp`;
|
|
34945
|
-
|
|
34946
|
-
|
|
35140
|
+
fs47.writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n");
|
|
35141
|
+
fs47.renameSync(tmp, p);
|
|
34947
35142
|
}
|
|
34948
35143
|
read(snapshotId) {
|
|
34949
35144
|
const p = snapshotPath(this.dataDir, snapshotId);
|
|
34950
35145
|
let raw;
|
|
34951
35146
|
try {
|
|
34952
|
-
raw =
|
|
35147
|
+
raw = fs47.readFileSync(p, "utf8");
|
|
34953
35148
|
} catch (err) {
|
|
34954
35149
|
if (err.code === "ENOENT") return null;
|
|
34955
35150
|
throw new Error(`Cannot read share snapshot at ${p}: ${err.message}`);
|
|
@@ -34968,7 +35163,7 @@ var ShareSnapshotRegistry = class {
|
|
|
34968
35163
|
put(snapshot) {
|
|
34969
35164
|
const stored = {
|
|
34970
35165
|
...snapshot,
|
|
34971
|
-
id: snapshot.id ||
|
|
35166
|
+
id: snapshot.id || crypto17.randomUUID(),
|
|
34972
35167
|
capturedAt: snapshot.capturedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
34973
35168
|
};
|
|
34974
35169
|
this.store.write(stored);
|
|
@@ -35041,11 +35236,11 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
|
|
|
35041
35236
|
}
|
|
35042
35237
|
|
|
35043
35238
|
// src/server/sharelinks.ts
|
|
35044
|
-
var
|
|
35045
|
-
var
|
|
35046
|
-
var
|
|
35239
|
+
var fs48 = __toESM(require("fs"));
|
|
35240
|
+
var path59 = __toESM(require("path"));
|
|
35241
|
+
var crypto18 = __toESM(require("crypto"));
|
|
35047
35242
|
function storePath14(dataDir) {
|
|
35048
|
-
return
|
|
35243
|
+
return path59.join(dataDir, "share-links.json");
|
|
35049
35244
|
}
|
|
35050
35245
|
var ShareLinkStore = class {
|
|
35051
35246
|
constructor(dataDir) {
|
|
@@ -35055,7 +35250,7 @@ var ShareLinkStore = class {
|
|
|
35055
35250
|
const p = storePath14(this.dataDir);
|
|
35056
35251
|
let raw;
|
|
35057
35252
|
try {
|
|
35058
|
-
raw =
|
|
35253
|
+
raw = fs48.readFileSync(p, "utf8");
|
|
35059
35254
|
} catch (err) {
|
|
35060
35255
|
if (err.code === "ENOENT") return [];
|
|
35061
35256
|
throw new Error(`Cannot read share-link store at ${p}: ${err.message}`);
|
|
@@ -35073,10 +35268,10 @@ var ShareLinkStore = class {
|
|
|
35073
35268
|
}
|
|
35074
35269
|
replaceAll(links) {
|
|
35075
35270
|
const p = storePath14(this.dataDir);
|
|
35076
|
-
|
|
35271
|
+
fs48.mkdirSync(path59.dirname(p), { recursive: true });
|
|
35077
35272
|
const tmp = `${p}.tmp`;
|
|
35078
|
-
|
|
35079
|
-
|
|
35273
|
+
fs48.writeFileSync(tmp, JSON.stringify(links, null, 2) + "\n");
|
|
35274
|
+
fs48.renameSync(tmp, p);
|
|
35080
35275
|
}
|
|
35081
35276
|
};
|
|
35082
35277
|
var ShareLinkRegistry = class {
|
|
@@ -35087,7 +35282,7 @@ var ShareLinkRegistry = class {
|
|
|
35087
35282
|
const links = this.store.load();
|
|
35088
35283
|
const stored = {
|
|
35089
35284
|
...link,
|
|
35090
|
-
id: link.id ||
|
|
35285
|
+
id: link.id || crypto18.randomUUID(),
|
|
35091
35286
|
createdAt: link.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
35092
35287
|
};
|
|
35093
35288
|
this.store.replaceAll([...links, stored]);
|
|
@@ -35144,11 +35339,11 @@ function listProjectLinks(dataDir, projectId) {
|
|
|
35144
35339
|
}
|
|
35145
35340
|
|
|
35146
35341
|
// src/server/shareaccesslog.ts
|
|
35147
|
-
var
|
|
35148
|
-
var
|
|
35149
|
-
var
|
|
35342
|
+
var fs49 = __toESM(require("fs"));
|
|
35343
|
+
var path60 = __toESM(require("path"));
|
|
35344
|
+
var crypto19 = __toESM(require("crypto"));
|
|
35150
35345
|
function storePath15(dataDir) {
|
|
35151
|
-
return
|
|
35346
|
+
return path60.join(dataDir, "share-access.json");
|
|
35152
35347
|
}
|
|
35153
35348
|
var ShareAccessStore = class {
|
|
35154
35349
|
constructor(dataDir) {
|
|
@@ -35158,7 +35353,7 @@ var ShareAccessStore = class {
|
|
|
35158
35353
|
const p = storePath15(this.dataDir);
|
|
35159
35354
|
let raw;
|
|
35160
35355
|
try {
|
|
35161
|
-
raw =
|
|
35356
|
+
raw = fs49.readFileSync(p, "utf8");
|
|
35162
35357
|
} catch (err) {
|
|
35163
35358
|
if (err.code === "ENOENT") return [];
|
|
35164
35359
|
throw new Error(`Cannot read share-access store at ${p}: ${err.message}`);
|
|
@@ -35176,11 +35371,11 @@ var ShareAccessStore = class {
|
|
|
35176
35371
|
}
|
|
35177
35372
|
append(entry) {
|
|
35178
35373
|
const p = storePath15(this.dataDir);
|
|
35179
|
-
|
|
35374
|
+
fs49.mkdirSync(path60.dirname(p), { recursive: true });
|
|
35180
35375
|
const next = [...this.load(), entry];
|
|
35181
35376
|
const tmp = `${p}.tmp`;
|
|
35182
|
-
|
|
35183
|
-
|
|
35377
|
+
fs49.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
|
|
35378
|
+
fs49.renameSync(tmp, p);
|
|
35184
35379
|
}
|
|
35185
35380
|
};
|
|
35186
35381
|
var ShareAccessRegistry = class {
|
|
@@ -35190,7 +35385,7 @@ var ShareAccessRegistry = class {
|
|
|
35190
35385
|
append(entry) {
|
|
35191
35386
|
const stored = {
|
|
35192
35387
|
...entry,
|
|
35193
|
-
id: entry.id ||
|
|
35388
|
+
id: entry.id || crypto19.randomUUID(),
|
|
35194
35389
|
at: entry.at || (/* @__PURE__ */ new Date()).toISOString()
|
|
35195
35390
|
};
|
|
35196
35391
|
this.store.append(stored);
|
|
@@ -35254,7 +35449,7 @@ function createShareLink(cfg, sessionId, input) {
|
|
|
35254
35449
|
const wantsOpenapi = !!input.allowDownloadOpenapi || (input.artifacts ?? []).includes("openapi");
|
|
35255
35450
|
const artifacts = artifactsFor({ allowDownloadOpenapi: !!input.allowDownloadOpenapi }, wantsOpenapi);
|
|
35256
35451
|
const snapshot = putSnapshot(cfg.dataDir, captureSnapshot(cfg.dataDir, principal, input.projectId, input.view, artifacts));
|
|
35257
|
-
const token =
|
|
35452
|
+
const token = crypto20.randomBytes(32).toString("base64url");
|
|
35258
35453
|
const link = createLink(cfg.dataDir, {
|
|
35259
35454
|
id: "",
|
|
35260
35455
|
tokenHash: hashToken(token),
|
|
@@ -35331,16 +35526,16 @@ function getShareAccessLog(cfg, sessionId, linkId, limit) {
|
|
|
35331
35526
|
init_fs();
|
|
35332
35527
|
|
|
35333
35528
|
// src/server/swagger.ts
|
|
35334
|
-
var
|
|
35335
|
-
var
|
|
35529
|
+
var fs50 = __toESM(require("fs"));
|
|
35530
|
+
var path61 = __toESM(require("path"));
|
|
35336
35531
|
var cache;
|
|
35337
35532
|
function loadAssets() {
|
|
35338
35533
|
if (cache !== void 0) return cache;
|
|
35339
35534
|
try {
|
|
35340
|
-
const dir =
|
|
35535
|
+
const dir = path61.dirname(require.resolve("swagger-ui-dist/package.json"));
|
|
35341
35536
|
cache = {
|
|
35342
|
-
css:
|
|
35343
|
-
js:
|
|
35537
|
+
css: fs50.readFileSync(path61.join(dir, "swagger-ui.css"), "utf8"),
|
|
35538
|
+
js: fs50.readFileSync(path61.join(dir, "swagger-ui-bundle.js"), "utf8")
|
|
35344
35539
|
};
|
|
35345
35540
|
} catch {
|
|
35346
35541
|
cache = null;
|
|
@@ -35380,7 +35575,7 @@ function getLoginOptions(cfg) {
|
|
|
35380
35575
|
async function startSignIn(cfg, providerId, redirectUri) {
|
|
35381
35576
|
const provider = resolveEnabledProvider(cfg, providerId);
|
|
35382
35577
|
assertAllowedRedirectUri(provider, redirectUri);
|
|
35383
|
-
const nonce =
|
|
35578
|
+
const nonce = crypto21.randomBytes(16).toString("hex");
|
|
35384
35579
|
const payload = { providerId, nonce, redirectUri };
|
|
35385
35580
|
const state = signSsoState(JSON.stringify(payload));
|
|
35386
35581
|
const endpoints = await resolveEndpoints(provider);
|
|
@@ -35732,20 +35927,20 @@ function clearNonceCookie(secure) {
|
|
|
35732
35927
|
}
|
|
35733
35928
|
function nonceMatches(a, b) {
|
|
35734
35929
|
if (!a || !b || a.length !== b.length) return false;
|
|
35735
|
-
return
|
|
35930
|
+
return crypto21.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
35736
35931
|
}
|
|
35737
35932
|
function loadReactBundle() {
|
|
35738
35933
|
const candidates = [
|
|
35739
|
-
|
|
35934
|
+
path62.resolve(__dirname, "webapp.html"),
|
|
35740
35935
|
// dist/index.js -> dist/webapp.html
|
|
35741
|
-
|
|
35936
|
+
path62.resolve(__dirname, "..", "webapp.html"),
|
|
35742
35937
|
// dist/cli/index.js -> dist/webapp.html
|
|
35743
|
-
|
|
35938
|
+
path62.resolve(__dirname, "..", "..", "web", "dist", "index.html")
|
|
35744
35939
|
// tsx dev: src/server -> web/dist
|
|
35745
35940
|
];
|
|
35746
35941
|
for (const candidate of candidates) {
|
|
35747
35942
|
try {
|
|
35748
|
-
if (
|
|
35943
|
+
if (fs51.existsSync(candidate)) return fs51.readFileSync(candidate, "utf8");
|
|
35749
35944
|
} catch {
|
|
35750
35945
|
}
|
|
35751
35946
|
}
|
|
@@ -37432,9 +37627,6 @@ details.adv summary { cursor:pointer; color:var(--dim); font-size:12px; margin-b
|
|
|
37432
37627
|
Array.prototype.forEach.call(host.querySelectorAll('[data-lock]'), function (b) {
|
|
37433
37628
|
b.addEventListener('click', function () { projectAction('/web/projects/lock', { projectId: b.getAttribute('data-lock') }, b, 'Locking\u2026', 'Lock'); });
|
|
37434
37629
|
});
|
|
37435
|
-
Array.prototype.forEach.call(host.querySelectorAll('[data-promote]'), function (b) {
|
|
37436
|
-
b.addEventListener('click', function () { projectAction('/web/projects/promote', { projectId: b.getAttribute('data-promote') }, b, 'Promoting\u2026', 'Promote'); });
|
|
37437
|
-
});
|
|
37438
37630
|
Array.prototype.forEach.call(host.querySelectorAll('[data-destroy]'), function (b) {
|
|
37439
37631
|
b.addEventListener('click', function () {
|
|
37440
37632
|
if (!confirm('Destroy project "' + b.getAttribute('data-destroy') + '"? This removes its entire spec tree.')) return;
|
|
@@ -37738,9 +37930,6 @@ function projectCreate(cfg, sessionId, body, res) {
|
|
|
37738
37930
|
function projectLock(cfg, sessionId, body, res) {
|
|
37739
37931
|
sendJson(res, 200, lockProject3(cfg, sessionId, String(body?.projectId ?? "")));
|
|
37740
37932
|
}
|
|
37741
|
-
function projectPromote(cfg, sessionId, body, res) {
|
|
37742
|
-
sendJson(res, 200, promoteProject3(cfg, sessionId, String(body?.projectId ?? "")));
|
|
37743
|
-
}
|
|
37744
37933
|
function projectDestroy(cfg, sessionId, body, res) {
|
|
37745
37934
|
destroyProject2(cfg, sessionId, String(body?.id ?? ""));
|
|
37746
37935
|
sendJson(res, 200, { ok: true });
|
|
@@ -38020,9 +38209,6 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
|
|
|
38020
38209
|
if (req.method === "POST" && parts.length === 3 && parts[2] === "lock") {
|
|
38021
38210
|
return projectLock(cfg, sessionId, body, res);
|
|
38022
38211
|
}
|
|
38023
|
-
if (req.method === "POST" && parts.length === 3 && parts[2] === "promote") {
|
|
38024
|
-
return projectPromote(cfg, sessionId, body, res);
|
|
38025
|
-
}
|
|
38026
38212
|
if (req.method === "POST" && parts.length === 3 && parts[2] === "destroy") {
|
|
38027
38213
|
return projectDestroy(cfg, sessionId, body, res);
|
|
38028
38214
|
}
|
|
@@ -38277,8 +38463,8 @@ var RealtimeHub = class {
|
|
|
38277
38463
|
* complete the handshake, and register the connection. A bad path or session
|
|
38278
38464
|
* destroys the socket. */
|
|
38279
38465
|
handleUpgrade(cfg, req, socket) {
|
|
38280
|
-
const
|
|
38281
|
-
if (
|
|
38466
|
+
const path67 = (req.url ?? "/").split("?")[0];
|
|
38467
|
+
if (path67 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
|
|
38282
38468
|
socket.destroy();
|
|
38283
38469
|
return;
|
|
38284
38470
|
}
|
|
@@ -38455,7 +38641,6 @@ function auditToolCall(dataDir, principal, projectId, body, outcome, subproject)
|
|
|
38455
38641
|
var PROJECT_LIFECYCLE_TOOLS = /* @__PURE__ */ new Set([
|
|
38456
38642
|
"sdd_host_initialize_project",
|
|
38457
38643
|
"sdd_host_lock_project",
|
|
38458
|
-
"sdd_host_promote_project",
|
|
38459
38644
|
"sdd_host_get_approval_status",
|
|
38460
38645
|
"sdd_host_await_approval"
|
|
38461
38646
|
]);
|
|
@@ -38513,7 +38698,6 @@ function requiredDataPlaneCapability(toolName) {
|
|
|
38513
38698
|
var MUTATING_HOST_TOOLS = /* @__PURE__ */ new Set([
|
|
38514
38699
|
"sdd_host_initialize_project",
|
|
38515
38700
|
"sdd_host_lock_project",
|
|
38516
|
-
"sdd_host_promote_project",
|
|
38517
38701
|
"sdd_host_await_approval",
|
|
38518
38702
|
// a decided approval may have executed the action
|
|
38519
38703
|
"sdd_host_policy_reconcile"
|
|
@@ -38583,9 +38767,6 @@ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body, su
|
|
|
38583
38767
|
case "sdd_host_lock_project":
|
|
38584
38768
|
value = lockProject2(cfg, credential, projectId, subproject);
|
|
38585
38769
|
break;
|
|
38586
|
-
case "sdd_host_promote_project":
|
|
38587
|
-
value = promoteProject2(cfg, credential, projectId, subproject);
|
|
38588
|
-
break;
|
|
38589
38770
|
case "sdd_host_await_approval":
|
|
38590
38771
|
value = await awaitApproval(
|
|
38591
38772
|
cfg,
|
|
@@ -39051,7 +39232,6 @@ var WEB_MUTATION_PATHS = /* @__PURE__ */ new Set([
|
|
|
39051
39232
|
"/web/tokens/revoke",
|
|
39052
39233
|
"/web/projects",
|
|
39053
39234
|
"/web/projects/lock",
|
|
39054
|
-
"/web/projects/promote",
|
|
39055
39235
|
"/web/projects/destroy"
|
|
39056
39236
|
]);
|
|
39057
39237
|
function routeData(cfg, req, res) {
|
|
@@ -39063,7 +39243,7 @@ function routeData(cfg, req, res) {
|
|
|
39063
39243
|
if (req.method === "GET" && url.pathname === "/readyz") {
|
|
39064
39244
|
let ready = false;
|
|
39065
39245
|
try {
|
|
39066
|
-
|
|
39246
|
+
fs52.accessSync(cfg.dataDir, fs52.constants.W_OK);
|
|
39067
39247
|
ready = true;
|
|
39068
39248
|
} catch {
|
|
39069
39249
|
}
|
|
@@ -39163,7 +39343,7 @@ function routeData(cfg, req, res) {
|
|
|
39163
39343
|
}
|
|
39164
39344
|
function readExposurePolicyFile(dataDir) {
|
|
39165
39345
|
try {
|
|
39166
|
-
const raw =
|
|
39346
|
+
const raw = fs52.readFileSync(path63.join(dataDir, "exposure-policy.json"), "utf8");
|
|
39167
39347
|
const parsed = JSON.parse(raw);
|
|
39168
39348
|
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
39169
39349
|
} catch {
|
|
@@ -39212,7 +39392,6 @@ async function routeAdmin(cfg, req, res) {
|
|
|
39212
39392
|
return sendJson(res, 200, { ok: true });
|
|
39213
39393
|
}
|
|
39214
39394
|
if (req.method === "POST" && parts.length === 4 && parts[3] === "lock") return sendJson(res, 200, lockProject(cfg, cred, parts[2]));
|
|
39215
|
-
if (req.method === "POST" && parts.length === 4 && parts[3] === "promote") return sendJson(res, 200, promoteProject(cfg, cred, parts[2]));
|
|
39216
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));
|
|
39217
39396
|
if (req.method === "DELETE" && parts.length === 4 && parts[3] === "git") {
|
|
39218
39397
|
disableGit(cfg, cred, parts[2]);
|
|
@@ -39867,9 +40046,9 @@ function seedDemoTree() {
|
|
|
39867
40046
|
|
|
39868
40047
|
// src/commands/host.ts
|
|
39869
40048
|
function resolveHostConfig(options) {
|
|
39870
|
-
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");
|
|
39871
40050
|
if (!process.env["WAIRON_PACKS_DIR"]) {
|
|
39872
|
-
process.env["WAIRON_PACKS_DIR"] =
|
|
40051
|
+
process.env["WAIRON_PACKS_DIR"] = path64.join(dataDir, "packs");
|
|
39873
40052
|
}
|
|
39874
40053
|
const cfg = {
|
|
39875
40054
|
host: options.host || "0.0.0.0",
|
|
@@ -39970,11 +40149,11 @@ async function runServe(options = {}) {
|
|
|
39970
40149
|
logger.info(` data dir: ${import_chalk17.default.gray(cfg.dataDir)}`);
|
|
39971
40150
|
logger.blank();
|
|
39972
40151
|
logger.info("Press Ctrl+C to stop.");
|
|
39973
|
-
await new Promise((
|
|
40152
|
+
await new Promise((resolve27) => {
|
|
39974
40153
|
const shutdown = () => {
|
|
39975
40154
|
logger.info("Shutting down\u2026");
|
|
39976
40155
|
handle.close();
|
|
39977
|
-
|
|
40156
|
+
resolve27();
|
|
39978
40157
|
};
|
|
39979
40158
|
process.on("SIGINT", shutdown);
|
|
39980
40159
|
process.on("SIGTERM", shutdown);
|
|
@@ -39994,14 +40173,14 @@ function openBrowser(url) {
|
|
|
39994
40173
|
}
|
|
39995
40174
|
async function runDev(options = {}) {
|
|
39996
40175
|
const cwd = process.cwd();
|
|
39997
|
-
if (!
|
|
40176
|
+
if (!fs53.existsSync(path64.join(cwd, ".wai"))) {
|
|
39998
40177
|
throw new WaironError(
|
|
39999
40178
|
"No .wai/ found in the current directory. Run `wairon dev` from a wairon project root (or run `wairon init` first)."
|
|
40000
40179
|
);
|
|
40001
40180
|
}
|
|
40002
|
-
const hash =
|
|
40003
|
-
const dataDir =
|
|
40004
|
-
|
|
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 });
|
|
40005
40184
|
registerLocalDevProject(dataDir, "local", cwd);
|
|
40006
40185
|
const port = options.port ? Number(options.port) : 8080;
|
|
40007
40186
|
const exposurePolicy = {
|
|
@@ -40039,11 +40218,11 @@ async function runDev(options = {}) {
|
|
|
40039
40218
|
logger.blank();
|
|
40040
40219
|
logger.info("An agent edits specs; refresh the page to see the live graph. Press Ctrl+C to stop.");
|
|
40041
40220
|
if (options.open) openBrowser(url);
|
|
40042
|
-
await new Promise((
|
|
40221
|
+
await new Promise((resolve27) => {
|
|
40043
40222
|
const shutdown = () => {
|
|
40044
40223
|
logger.info("Shutting down\u2026");
|
|
40045
40224
|
handle.close();
|
|
40046
|
-
|
|
40225
|
+
resolve27();
|
|
40047
40226
|
};
|
|
40048
40227
|
process.on("SIGINT", shutdown);
|
|
40049
40228
|
process.on("SIGTERM", shutdown);
|
|
@@ -40313,18 +40492,6 @@ async function runHostLock(options = {}) {
|
|
|
40313
40492
|
throw mapAdminError(e);
|
|
40314
40493
|
}
|
|
40315
40494
|
}
|
|
40316
|
-
async function runHostPromote(options = {}) {
|
|
40317
|
-
const cfg = resolveHostConfig(options);
|
|
40318
|
-
if (!options.project) throw new WaironError("`--project <id>` is required for `host promote`.");
|
|
40319
|
-
try {
|
|
40320
|
-
const result = promoteProject(cfg, masterCredential(), options.project);
|
|
40321
|
-
const mark = result.status === "ready" ? import_chalk17.default.green("\u2713") : import_chalk17.default.yellow("\u2717");
|
|
40322
|
-
logger.info(`${mark} ${result.message}`);
|
|
40323
|
-
if (result.status !== "ready") process.exitCode = 1;
|
|
40324
|
-
} catch (e) {
|
|
40325
|
-
throw mapAdminError(e);
|
|
40326
|
-
}
|
|
40327
|
-
}
|
|
40328
40495
|
async function runHostProducer(action, options = {}) {
|
|
40329
40496
|
const cfg = resolveHostConfig(options);
|
|
40330
40497
|
const cred = masterCredential();
|
|
@@ -40409,8 +40576,8 @@ async function runHostPacks(action, options = {}) {
|
|
|
40409
40576
|
}
|
|
40410
40577
|
case "install": {
|
|
40411
40578
|
if (!options.file) throw new WaironError("`--file <path>` (a declarative pack YAML) is required for install.");
|
|
40412
|
-
const name = options.name ??
|
|
40413
|
-
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");
|
|
40414
40581
|
const desc = project2 ? installProjectPack(cfg, cred, project2, name, content) : installGlobalPack(cfg, cred, name, content);
|
|
40415
40582
|
logger.success(`Installed ${scope} pack "${desc.name}" (${desc.profiles} profile(s), ${desc.languages} language(s)).`);
|
|
40416
40583
|
if (project2) logger.info("Committed with the project \u2014 every clone and CI will enforce it.");
|
|
@@ -40636,7 +40803,7 @@ async function runSurface(action, options = {}) {
|
|
|
40636
40803
|
}
|
|
40637
40804
|
|
|
40638
40805
|
// src/commands/subsystem.ts
|
|
40639
|
-
var
|
|
40806
|
+
var path65 = __toESM(require("path"));
|
|
40640
40807
|
init_logger();
|
|
40641
40808
|
init_errors();
|
|
40642
40809
|
init_fs();
|
|
@@ -40676,9 +40843,9 @@ async function runSubsystemAdd(id, options = {}) {
|
|
|
40676
40843
|
updatedAt: now
|
|
40677
40844
|
};
|
|
40678
40845
|
createChainedSubsystem(subsystem, displayName);
|
|
40679
|
-
const childDir =
|
|
40846
|
+
const childDir = path65.resolve(getProjectRoot(), options.projectPath);
|
|
40680
40847
|
logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
40681
|
-
logger.info(`Scaffolded child project at ${
|
|
40848
|
+
logger.info(`Scaffolded child project at ${path65.relative(process.cwd(), childDir) || "."}`);
|
|
40682
40849
|
logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
|
|
40683
40850
|
}
|
|
40684
40851
|
async function runSubsystemMove(id, options = {}) {
|
|
@@ -40701,9 +40868,9 @@ async function runSubsystemExternalize(id, options = {}) {
|
|
|
40701
40868
|
throw new WaironError("--project-path (the subproject destination) is required.");
|
|
40702
40869
|
}
|
|
40703
40870
|
externalizeSubsystem(id, options.projectPath);
|
|
40704
|
-
const childDir =
|
|
40871
|
+
const childDir = path65.resolve(getProjectRoot(), options.projectPath);
|
|
40705
40872
|
logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
40706
|
-
logger.info(`Moved its specs into ${
|
|
40873
|
+
logger.info(`Moved its specs into ${path65.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
|
|
40707
40874
|
logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
|
|
40708
40875
|
}
|
|
40709
40876
|
async function runSubsystemInternalize(id) {
|
|
@@ -40947,7 +41114,7 @@ async function runAgent(action, id) {
|
|
|
40947
41114
|
}
|
|
40948
41115
|
case "customize": {
|
|
40949
41116
|
const brief = composeAgentBrief3(id);
|
|
40950
|
-
const guidancePath =
|
|
41117
|
+
const guidancePath = path66.join(AI_PATHS.root(), "agents", `${id}.md`);
|
|
40951
41118
|
const relPath = `.wai/agents/${id}.md`;
|
|
40952
41119
|
if (pathExists(guidancePath)) {
|
|
40953
41120
|
throw new GuidanceFileExistsError(relPath);
|
|
@@ -41050,9 +41217,6 @@ hostCmd.command("key <action>").description("mint | list | revoke an API key").o
|
|
|
41050
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) => {
|
|
41051
41218
|
await runHostLock({ project: opts.project, dataDir: opts.dataDir });
|
|
41052
41219
|
});
|
|
41053
|
-
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) => {
|
|
41054
|
-
await runHostPromote({ project: opts.project, dataDir: opts.dataDir });
|
|
41055
|
-
});
|
|
41056
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) => {
|
|
41057
41221
|
await runHostGit(action, {
|
|
41058
41222
|
project: opts.project,
|