@wairon/cli 5.0.2-dev.13 → 5.0.2-dev.14
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/dist/cli/index.js +981 -674
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +249 -71
- 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.0.2-dev.
|
|
68
|
+
WAIRON_VERSION = "5.0.2-dev.14";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -1914,6 +1914,47 @@ var init_loader = __esm({
|
|
|
1914
1914
|
}
|
|
1915
1915
|
});
|
|
1916
1916
|
|
|
1917
|
+
// src/core/statehash.ts
|
|
1918
|
+
function computeStateId() {
|
|
1919
|
+
const tree = {
|
|
1920
|
+
system: loadSystemSpec(),
|
|
1921
|
+
subsystems: loadSubsystemSpecs(),
|
|
1922
|
+
components: loadComponentSpecs(),
|
|
1923
|
+
interfaces: loadInterfaceSpecs(),
|
|
1924
|
+
implementations: loadImplementationSpecs(),
|
|
1925
|
+
types: loadTypeSpecs()
|
|
1926
|
+
};
|
|
1927
|
+
const digest = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
|
|
1928
|
+
return { algorithm: "sha256", digest };
|
|
1929
|
+
}
|
|
1930
|
+
function stateIdEquals(a, b) {
|
|
1931
|
+
return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
|
|
1932
|
+
}
|
|
1933
|
+
function canonicalize(value) {
|
|
1934
|
+
return JSON.stringify(sortKeys(value));
|
|
1935
|
+
}
|
|
1936
|
+
function sortKeys(v) {
|
|
1937
|
+
if (Array.isArray(v)) return v.map(sortKeys);
|
|
1938
|
+
if (v && typeof v === "object") {
|
|
1939
|
+
const src = v;
|
|
1940
|
+
const out = {};
|
|
1941
|
+
for (const k of Object.keys(src).sort()) {
|
|
1942
|
+
if (k === "createdAt" || k === "updatedAt") continue;
|
|
1943
|
+
out[k] = sortKeys(src[k]);
|
|
1944
|
+
}
|
|
1945
|
+
return out;
|
|
1946
|
+
}
|
|
1947
|
+
return v;
|
|
1948
|
+
}
|
|
1949
|
+
var crypto;
|
|
1950
|
+
var init_statehash = __esm({
|
|
1951
|
+
"src/core/statehash.ts"() {
|
|
1952
|
+
"use strict";
|
|
1953
|
+
crypto = __toESM(require("crypto"));
|
|
1954
|
+
init_specs2();
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1917
1958
|
// src/core/narrative-labels.ts
|
|
1918
1959
|
function resolveNarrativeLabels(methodName, steps) {
|
|
1919
1960
|
const errors = [];
|
|
@@ -7475,47 +7516,6 @@ var init_filenames = __esm({
|
|
|
7475
7516
|
}
|
|
7476
7517
|
});
|
|
7477
7518
|
|
|
7478
|
-
// src/core/statehash.ts
|
|
7479
|
-
function computeStateId() {
|
|
7480
|
-
const tree = {
|
|
7481
|
-
system: loadSystemSpec(),
|
|
7482
|
-
subsystems: loadSubsystemSpecs(),
|
|
7483
|
-
components: loadComponentSpecs(),
|
|
7484
|
-
interfaces: loadInterfaceSpecs(),
|
|
7485
|
-
implementations: loadImplementationSpecs(),
|
|
7486
|
-
types: loadTypeSpecs()
|
|
7487
|
-
};
|
|
7488
|
-
const digest = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
|
|
7489
|
-
return { algorithm: "sha256", digest };
|
|
7490
|
-
}
|
|
7491
|
-
function stateIdEquals(a, b) {
|
|
7492
|
-
return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
|
|
7493
|
-
}
|
|
7494
|
-
function canonicalize(value) {
|
|
7495
|
-
return JSON.stringify(sortKeys(value));
|
|
7496
|
-
}
|
|
7497
|
-
function sortKeys(v) {
|
|
7498
|
-
if (Array.isArray(v)) return v.map(sortKeys);
|
|
7499
|
-
if (v && typeof v === "object") {
|
|
7500
|
-
const src = v;
|
|
7501
|
-
const out = {};
|
|
7502
|
-
for (const k of Object.keys(src).sort()) {
|
|
7503
|
-
if (k === "createdAt" || k === "updatedAt") continue;
|
|
7504
|
-
out[k] = sortKeys(src[k]);
|
|
7505
|
-
}
|
|
7506
|
-
return out;
|
|
7507
|
-
}
|
|
7508
|
-
return v;
|
|
7509
|
-
}
|
|
7510
|
-
var crypto;
|
|
7511
|
-
var init_statehash = __esm({
|
|
7512
|
-
"src/core/statehash.ts"() {
|
|
7513
|
-
"use strict";
|
|
7514
|
-
crypto = __toESM(require("crypto"));
|
|
7515
|
-
init_specs2();
|
|
7516
|
-
}
|
|
7517
|
-
});
|
|
7518
|
-
|
|
7519
7519
|
// src/core/openapi.ts
|
|
7520
7520
|
function schemaFor(typeRef, closureIds) {
|
|
7521
7521
|
const trimmed = typeRef.trim().replace(/^promise\s*<(.+)>$/i, "$1").trim();
|
|
@@ -7969,6 +7969,57 @@ function projectOwnSurface(maxAudience) {
|
|
|
7969
7969
|
function projectChildSurface() {
|
|
7970
7970
|
return projectOwnSurface("project");
|
|
7971
7971
|
}
|
|
7972
|
+
function localName(id) {
|
|
7973
|
+
return id.split("::").pop();
|
|
7974
|
+
}
|
|
7975
|
+
function projectSubsystemSurface(subsystemId) {
|
|
7976
|
+
const system = loadSystemSpec();
|
|
7977
|
+
if (!system) {
|
|
7978
|
+
throw new Error("Cannot project a subsystem surface: the L0 system spec is missing.");
|
|
7979
|
+
}
|
|
7980
|
+
const subsystems = loadSubsystemSpecs();
|
|
7981
|
+
const target = subsystems.find((s) => s.id === subsystemId);
|
|
7982
|
+
if (!target) {
|
|
7983
|
+
throw new Error(`Cannot project a subsystem surface: subsystem "${subsystemId}" does not exist.`);
|
|
7984
|
+
}
|
|
7985
|
+
const components = loadComponentSpecs();
|
|
7986
|
+
const interfaces = loadInterfaceSpecs();
|
|
7987
|
+
const types = loadTypeSpecs();
|
|
7988
|
+
const entries = [];
|
|
7989
|
+
for (const pub of target.publicInterfaces ?? []) {
|
|
7990
|
+
if (!pub.component) continue;
|
|
7991
|
+
const comp = components.find((c) => c.id === pub.component || c.id === `${subsystemId}::${pub.component}`);
|
|
7992
|
+
if (!comp) continue;
|
|
7993
|
+
if (comp.componentType !== "Portal") continue;
|
|
7994
|
+
const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!pub.interface || i.id === pub.interface || i.id === `${subsystemId}::${pub.interface}`));
|
|
7995
|
+
const methods = compInterfaces.flatMap((i) => i.methods);
|
|
7996
|
+
entries.push({
|
|
7997
|
+
id: localName(pub.interface ?? comp.id),
|
|
7998
|
+
name: comp.name,
|
|
7999
|
+
// Family ceiling: a sibling surface is consumable by the system family only.
|
|
8000
|
+
audience: "project",
|
|
8001
|
+
type: pub.type ?? "Custom",
|
|
8002
|
+
// The snapshot carries the LOCAL portal name — consumers resolve cross-tree
|
|
8003
|
+
// refs by their final segment.
|
|
8004
|
+
component: localName(comp.id),
|
|
8005
|
+
methods,
|
|
8006
|
+
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
8007
|
+
// Project the backing Portal's auth + basePath so the codec can emit
|
|
8008
|
+
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
8009
|
+
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
8010
|
+
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
8011
|
+
details: pub.details ?? ""
|
|
8012
|
+
});
|
|
8013
|
+
}
|
|
8014
|
+
return SurfaceSnapshotSchema.parse({
|
|
8015
|
+
projectName: `${system.name}::${subsystemId}`,
|
|
8016
|
+
origin: "generated",
|
|
8017
|
+
stateId: stateIdString(),
|
|
8018
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8019
|
+
interfaces: entries,
|
|
8020
|
+
types: computeTypeClosure(entries, types)
|
|
8021
|
+
});
|
|
8022
|
+
}
|
|
7972
8023
|
function listSnapshots(rootDir = getProjectRoot()) {
|
|
7973
8024
|
const dir = surfacesDir(rootDir);
|
|
7974
8025
|
if (!fs9.existsSync(dir)) return [];
|
|
@@ -7985,10 +8036,13 @@ function listSnapshots(rootDir = getProjectRoot()) {
|
|
|
7985
8036
|
function getSnapshot(projectName, rootDir = getProjectRoot()) {
|
|
7986
8037
|
return listSnapshots(rootDir).find((s) => s.projectName === projectName) ?? null;
|
|
7987
8038
|
}
|
|
8039
|
+
function snapshotFilename(projectName) {
|
|
8040
|
+
return `${safeFilenamePart(projectName)}.yaml`;
|
|
8041
|
+
}
|
|
7988
8042
|
function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
|
|
7989
8043
|
const dir = surfacesDir(rootDir);
|
|
7990
8044
|
fs9.mkdirSync(dir, { recursive: true });
|
|
7991
|
-
const p = path10.join(dir,
|
|
8045
|
+
const p = path10.join(dir, snapshotFilename(snapshot.projectName));
|
|
7992
8046
|
writeYamlFile(p, SurfaceSnapshotSchema.parse(snapshot));
|
|
7993
8047
|
return p;
|
|
7994
8048
|
}
|
|
@@ -8061,17 +8115,54 @@ function importSurface(sourcePath, origin) {
|
|
|
8061
8115
|
return snapshot;
|
|
8062
8116
|
}
|
|
8063
8117
|
function generateChildSnapshots(rootDir = getProjectRoot()) {
|
|
8064
|
-
const
|
|
8118
|
+
const topLevel = loadSubsystemSpecs().filter((s) => !s.id.includes("::"));
|
|
8119
|
+
const children = topLevel.filter((s) => s.projectPath);
|
|
8065
8120
|
if (!children.length) return [];
|
|
8066
|
-
const
|
|
8121
|
+
const familySnapshot = projectChildSurface();
|
|
8122
|
+
const siblingSnapshots = /* @__PURE__ */ new Map();
|
|
8123
|
+
const siblingSurface = (subsystemId) => {
|
|
8124
|
+
let snap = siblingSnapshots.get(subsystemId);
|
|
8125
|
+
if (!snap) {
|
|
8126
|
+
snap = projectSubsystemSurface(subsystemId);
|
|
8127
|
+
siblingSnapshots.set(subsystemId, snap);
|
|
8128
|
+
}
|
|
8129
|
+
return snap;
|
|
8130
|
+
};
|
|
8067
8131
|
const written = [];
|
|
8068
8132
|
for (const child of children) {
|
|
8069
8133
|
const childDir = path10.resolve(rootDir, child.projectPath);
|
|
8070
8134
|
if (!fs9.existsSync(childDir)) continue;
|
|
8071
|
-
written.push(saveSnapshot(
|
|
8135
|
+
written.push(saveSnapshot(familySnapshot, childDir));
|
|
8136
|
+
for (const sibling of topLevel) {
|
|
8137
|
+
if (sibling.id === child.id) continue;
|
|
8138
|
+
written.push(saveSnapshot(siblingSurface(sibling.id), childDir));
|
|
8139
|
+
}
|
|
8072
8140
|
}
|
|
8073
8141
|
return written;
|
|
8074
8142
|
}
|
|
8143
|
+
function computeParentStateId(parentRoot) {
|
|
8144
|
+
return computeStateIdAt(parentRoot);
|
|
8145
|
+
}
|
|
8146
|
+
function listExternalInterfaces() {
|
|
8147
|
+
const snapshots = listSnapshots();
|
|
8148
|
+
const chainingParent = resolveChainingParent();
|
|
8149
|
+
const parentStateId = chainingParent ? computeParentStateId(chainingParent.parentRoot) : null;
|
|
8150
|
+
return snapshots.map((snapshot) => {
|
|
8151
|
+
const generated = snapshot.origin === "generated";
|
|
8152
|
+
const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
|
|
8153
|
+
const freshness = generated && parentStateId ? snapshot.stateId === parentStateId ? "fresh" : "stale" : "unverifiable";
|
|
8154
|
+
return {
|
|
8155
|
+
projectName: snapshot.projectName,
|
|
8156
|
+
origin: snapshot.origin,
|
|
8157
|
+
sourceKind,
|
|
8158
|
+
generatedAt: snapshot.generatedAt,
|
|
8159
|
+
...snapshot.stateId ? { stateId: snapshot.stateId } : {},
|
|
8160
|
+
...snapshot.version ? { version: snapshot.version } : {},
|
|
8161
|
+
freshness,
|
|
8162
|
+
interfaceIds: snapshot.interfaces.map((e) => e.id)
|
|
8163
|
+
};
|
|
8164
|
+
});
|
|
8165
|
+
}
|
|
8075
8166
|
function surfaceContentKey(snapshot) {
|
|
8076
8167
|
const { stateId, generatedAt, origin, ...content } = snapshot;
|
|
8077
8168
|
return JSON.stringify(content);
|
|
@@ -8280,7 +8371,8 @@ var init_contracts = __esm({
|
|
|
8280
8371
|
"SURFACE_REF_NOT_EXPOSED",
|
|
8281
8372
|
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches capability "${step.capability}" through cross-tree portal "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not serve that capability on "${resolved.entry.id}".`,
|
|
8282
8373
|
impl.id,
|
|
8283
|
-
isDraftCtx
|
|
8374
|
+
isDraftCtx,
|
|
8375
|
+
true
|
|
8284
8376
|
);
|
|
8285
8377
|
}
|
|
8286
8378
|
continue;
|
|
@@ -8337,7 +8429,8 @@ var init_contracts = __esm({
|
|
|
8337
8429
|
"SURFACE_REF_NOT_EXPOSED",
|
|
8338
8430
|
`Method "${implMethod.name}" in implementation "${impl.id}" calls "${step.targetMethod}" on cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
|
|
8339
8431
|
impl.id,
|
|
8340
|
-
isDraftCtx
|
|
8432
|
+
isDraftCtx,
|
|
8433
|
+
true
|
|
8341
8434
|
);
|
|
8342
8435
|
} else if (step.assertsGuarantees) {
|
|
8343
8436
|
const declared = new Set(surfaceMethod.guarantees ?? []);
|
|
@@ -8348,7 +8441,8 @@ var init_contracts = __esm({
|
|
|
8348
8441
|
"NARRATIVE_SEMANTIC_UNBACKED",
|
|
8349
8442
|
`Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" asserts guarantee "${g}", but the surface snapshot of "${resolved.snapshot.projectName}" does not declare it on "${resolved.entry.id}.${step.targetMethod}".`,
|
|
8350
8443
|
impl.id,
|
|
8351
|
-
isDraftCtx
|
|
8444
|
+
isDraftCtx,
|
|
8445
|
+
true
|
|
8352
8446
|
);
|
|
8353
8447
|
}
|
|
8354
8448
|
}
|
|
@@ -9836,7 +9930,8 @@ var init_stereotype_deps = __esm({
|
|
|
9836
9930
|
"CROSS_SUBSYSTEM_NON_ADAPTER",
|
|
9837
9931
|
`Boundary violation: ${comp.componentType} "${comp.id}" depends directly on "${depId}", a surface of project "${resolved.snapshot.projectName}". Only a local client Adapter may cross a project boundary \u2014 route this hop through an Adapter.`,
|
|
9838
9932
|
comp.id,
|
|
9839
|
-
isDraftCtx
|
|
9933
|
+
isDraftCtx,
|
|
9934
|
+
true
|
|
9840
9935
|
);
|
|
9841
9936
|
}
|
|
9842
9937
|
continue;
|
|
@@ -11699,11 +11794,11 @@ var init_narrative_antipatterns = __esm({
|
|
|
11699
11794
|
const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
|
|
11700
11795
|
if (memberEdges.length === 0) continue;
|
|
11701
11796
|
const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
|
|
11702
|
-
const
|
|
11797
|
+
const path61 = [...keys].sort().join(" \u2192 ");
|
|
11703
11798
|
ctx.addIssue(
|
|
11704
11799
|
"warning",
|
|
11705
11800
|
"UNCONDITIONAL_CALL_CYCLE",
|
|
11706
|
-
`Call cycle with no guard: ${
|
|
11801
|
+
`Call cycle with no guard: ${path61} \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.`,
|
|
11707
11802
|
anchor.impl.id,
|
|
11708
11803
|
memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
|
|
11709
11804
|
);
|
|
@@ -13090,9 +13185,15 @@ function buildRuleContext(opts) {
|
|
|
13090
13185
|
...[...SDD_RULES, ...extensions.rules].flatMap((r) => r.codes.map((c) => c.code)),
|
|
13091
13186
|
// Declarative assertions bring their own namespaced codes — lint.allow
|
|
13092
13187
|
// and severity overrides treat them exactly like builtins.
|
|
13093
|
-
...extensions.assertions.map((a) => a.fullCode)
|
|
13188
|
+
...extensions.assertions.map((a) => a.fullCode),
|
|
13189
|
+
// Entry-point emitted codes: validateSddTree's chained-subproject pass
|
|
13190
|
+
// raises these AFTER the rule run (it post-processes the aggregated issue
|
|
13191
|
+
// list), so no registered rule declares them — but lint.allow validation
|
|
13192
|
+
// must still recognize them as real codes.
|
|
13193
|
+
"CHAINED_SUBPROJECT_CONTEXT",
|
|
13194
|
+
"UNVERIFIED_EXTERNAL_REF"
|
|
13094
13195
|
]);
|
|
13095
|
-
const addIssue = (defaultSeverity, code, message, specId, isDraftContext) => {
|
|
13196
|
+
const addIssue = (defaultSeverity, code, message, specId, isDraftContext, surfaceResolved) => {
|
|
13096
13197
|
if (scopeSubsystem && specId && !isSpecInScope(specId)) {
|
|
13097
13198
|
return;
|
|
13098
13199
|
}
|
|
@@ -13110,7 +13211,14 @@ function buildRuleContext(opts) {
|
|
|
13110
13211
|
if (severity === "warning") return;
|
|
13111
13212
|
}
|
|
13112
13213
|
}
|
|
13113
|
-
issues.push({
|
|
13214
|
+
issues.push({
|
|
13215
|
+
severity,
|
|
13216
|
+
code,
|
|
13217
|
+
message,
|
|
13218
|
+
specId,
|
|
13219
|
+
...isDraftContext ? { draftContext: true } : {},
|
|
13220
|
+
...surfaceResolved ? { surfaceResolved: true } : {}
|
|
13221
|
+
});
|
|
13114
13222
|
};
|
|
13115
13223
|
return {
|
|
13116
13224
|
system,
|
|
@@ -13535,24 +13643,54 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
13535
13643
|
for (const rule of ruleSequence()) {
|
|
13536
13644
|
rule.check(ctx);
|
|
13537
13645
|
}
|
|
13538
|
-
const hasCrossTreeSuspects = issues.some(
|
|
13646
|
+
const hasCrossTreeSuspects = issues.some(
|
|
13647
|
+
(i) => SUBPROJECT_REFERENCE_CODES.has(i.code) || SUBPROJECT_CONFORMANCE_CODES.has(i.code)
|
|
13648
|
+
);
|
|
13539
13649
|
const chainingParent = hasCrossTreeSuspects ? findChainingParent(getProjectRoot()) : null;
|
|
13540
13650
|
if (chainingParent) {
|
|
13651
|
+
let unverified = 0;
|
|
13541
13652
|
let downgraded = 0;
|
|
13542
|
-
for (
|
|
13543
|
-
|
|
13544
|
-
if (iss.
|
|
13545
|
-
|
|
13546
|
-
|
|
13653
|
+
for (let at = 0; at < issues.length; at++) {
|
|
13654
|
+
const iss = issues[at];
|
|
13655
|
+
if (SUBPROJECT_REFERENCE_CODES.has(iss.code) && !iss.surfaceResolved) {
|
|
13656
|
+
issues[at] = {
|
|
13657
|
+
severity: "warning",
|
|
13658
|
+
code: "UNVERIFIED_EXTERNAL_REF",
|
|
13659
|
+
crossTreeContext: true,
|
|
13660
|
+
// --ci waives it (parent root is authoritative)
|
|
13661
|
+
specId: iss.specId,
|
|
13662
|
+
...iss.agentId ? { agentId: iss.agentId } : {},
|
|
13663
|
+
...iss.draftContext ? { draftContext: true } : {},
|
|
13664
|
+
message: `Unverified external reference (${iss.code}): ${iss.message} No vendored surface snapshot covers this reference, so it cannot be verified from this chained subproject standalone \u2014 re-lock the parent so fresh family/sibling snapshots ship, or inspect what this project can consume via \`wairon surface externals\` / sdd_list_external_interfaces.`
|
|
13665
|
+
};
|
|
13666
|
+
unverified++;
|
|
13667
|
+
continue;
|
|
13668
|
+
}
|
|
13669
|
+
if (SUBPROJECT_CONFORMANCE_CODES.has(iss.code)) {
|
|
13670
|
+
if (iss.severity === "error") {
|
|
13671
|
+
iss.severity = "warning";
|
|
13672
|
+
downgraded++;
|
|
13673
|
+
}
|
|
13674
|
+
iss.crossTreeContext = true;
|
|
13547
13675
|
}
|
|
13548
|
-
iss.crossTreeContext = true;
|
|
13549
13676
|
}
|
|
13550
|
-
if (downgraded > 0) {
|
|
13677
|
+
if (unverified > 0 || downgraded > 0) {
|
|
13678
|
+
const notes = [];
|
|
13679
|
+
if (unverified > 0) {
|
|
13680
|
+
notes.push(
|
|
13681
|
+
`${unverified} cross-tree reference(s) have no vendored surface snapshot covering them and were reported as UNVERIFIED_EXTERNAL_REF warnings \u2014 re-lock the parent so fresh family/sibling snapshots ship, or inspect via \`wairon surface externals\` / sdd_list_external_interfaces.`
|
|
13682
|
+
);
|
|
13683
|
+
}
|
|
13684
|
+
if (downgraded > 0) {
|
|
13685
|
+
notes.push(
|
|
13686
|
+
`${downgraded} code\u2194spec conformance finding(s) (parent-root-relative source paths) were downgraded to warnings.`
|
|
13687
|
+
);
|
|
13688
|
+
}
|
|
13551
13689
|
issues.unshift({
|
|
13552
13690
|
severity: "warning",
|
|
13553
13691
|
code: "CHAINED_SUBPROJECT_CONTEXT",
|
|
13554
13692
|
crossTreeContext: true,
|
|
13555
|
-
message: `This project is a chained subproject ("${chainingParent.subsystemId}") of the parent project at "${chainingParent.parentRoot}". ${
|
|
13693
|
+
message: `This project is a chained subproject ("${chainingParent.subsystemId}") of the parent project at "${chainingParent.parentRoot}". ${notes.join(" ")} Full cross-tree verification runs from the parent root.`
|
|
13556
13694
|
});
|
|
13557
13695
|
}
|
|
13558
13696
|
}
|
|
@@ -13569,7 +13707,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
13569
13707
|
function validateAsComplete(options) {
|
|
13570
13708
|
return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
|
|
13571
13709
|
}
|
|
13572
|
-
var
|
|
13710
|
+
var SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
|
|
13573
13711
|
var init_validation = __esm({
|
|
13574
13712
|
"src/core/validation.ts"() {
|
|
13575
13713
|
"use strict";
|
|
@@ -13582,8 +13720,7 @@ var init_validation = __esm({
|
|
|
13582
13720
|
init_source_analysis();
|
|
13583
13721
|
init_specs2();
|
|
13584
13722
|
init_fs();
|
|
13585
|
-
|
|
13586
|
-
// reference resolution
|
|
13723
|
+
SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
|
|
13587
13724
|
"UNDEFINED_TYPE_REFERENCE",
|
|
13588
13725
|
"INVALID_DEPENDENCY_REFERENCE",
|
|
13589
13726
|
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
@@ -13591,8 +13728,9 @@ var init_validation = __esm({
|
|
|
13591
13728
|
"UNDECLARED_DEPENDENCY_CALL",
|
|
13592
13729
|
"INVALID_TRUSTED_LINK",
|
|
13593
13730
|
"CROSS_SUBSYSTEM_NON_ADAPTER",
|
|
13594
|
-
"CROSS_TREE_REF_UNRESOLVED"
|
|
13595
|
-
|
|
13731
|
+
"CROSS_TREE_REF_UNRESOLVED"
|
|
13732
|
+
]);
|
|
13733
|
+
SUBPROJECT_CONFORMANCE_CODES = /* @__PURE__ */ new Set([
|
|
13596
13734
|
"MISSING_SOURCE_FILE",
|
|
13597
13735
|
"SOURCE_PATH_ESCAPES_ROOT",
|
|
13598
13736
|
"MISSING_SOURCE_PATH",
|
|
@@ -14134,6 +14272,7 @@ __export(specs_exports, {
|
|
|
14134
14272
|
buildProjectGraph: () => buildProjectGraph,
|
|
14135
14273
|
clearLoaderIssues: () => clearLoaderIssues,
|
|
14136
14274
|
collectPromotableSpecs: () => collectPromotableSpecs,
|
|
14275
|
+
computeStateIdAt: () => computeStateIdAt,
|
|
14137
14276
|
deleteComponentSpec: () => deleteComponentSpec,
|
|
14138
14277
|
deleteGroupSpec: () => deleteGroupSpec,
|
|
14139
14278
|
deleteImplementationSpec: () => deleteImplementationSpec,
|
|
@@ -14166,6 +14305,7 @@ __export(specs_exports, {
|
|
|
14166
14305
|
loadTypeSpec: () => loadTypeSpec,
|
|
14167
14306
|
loadTypeSpecs: () => loadTypeSpecs,
|
|
14168
14307
|
normalizeComponentLayout: () => normalizeComponentLayout,
|
|
14308
|
+
resolveChainingParent: () => resolveChainingParent,
|
|
14169
14309
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
14170
14310
|
restoreSpecFiles: () => restoreSpecFiles,
|
|
14171
14311
|
saveComponentSpec: () => saveComponentSpec,
|
|
@@ -14570,6 +14710,19 @@ function dryRunSerializeSpecs(include) {
|
|
|
14570
14710
|
function buildProjectGraph(level) {
|
|
14571
14711
|
return buildGraphModel(level);
|
|
14572
14712
|
}
|
|
14713
|
+
function resolveChainingParent() {
|
|
14714
|
+
return findChainingParent(getProjectRoot());
|
|
14715
|
+
}
|
|
14716
|
+
function computeStateIdAt(root) {
|
|
14717
|
+
const resolved = path14.resolve(root);
|
|
14718
|
+
return runWithProjectRoot(resolved, () => {
|
|
14719
|
+
workspaceFor(resolved).invalidate();
|
|
14720
|
+
const system = loadSystemSpec();
|
|
14721
|
+
if (!system) return null;
|
|
14722
|
+
const s = computeStateId();
|
|
14723
|
+
return `${s.algorithm}:${s.digest}`;
|
|
14724
|
+
});
|
|
14725
|
+
}
|
|
14573
14726
|
function deleteTypeSpec(id) {
|
|
14574
14727
|
return current().deleteTypeSpec(id);
|
|
14575
14728
|
}
|
|
@@ -14613,6 +14766,7 @@ var init_specs2 = __esm({
|
|
|
14613
14766
|
path14 = __toESM(require("path"));
|
|
14614
14767
|
init_loader();
|
|
14615
14768
|
init_fs();
|
|
14769
|
+
init_statehash();
|
|
14616
14770
|
init_yaml();
|
|
14617
14771
|
init_models();
|
|
14618
14772
|
init_narrative_labels();
|
|
@@ -17206,6 +17360,9 @@ function requireSpecs() {
|
|
|
17206
17360
|
function requireProvision() {
|
|
17207
17361
|
return init_provision(), __toCommonJS(provision_exports);
|
|
17208
17362
|
}
|
|
17363
|
+
function listExternalInterfaces2() {
|
|
17364
|
+
return listExternalInterfaces();
|
|
17365
|
+
}
|
|
17209
17366
|
function text(content) {
|
|
17210
17367
|
return { content: [{ type: "text", text: content }] };
|
|
17211
17368
|
}
|
|
@@ -18172,7 +18329,36 @@ NOTICE:
|
|
|
18172
18329
|
}
|
|
18173
18330
|
}
|
|
18174
18331
|
);
|
|
18332
|
+
reg(
|
|
18333
|
+
server,
|
|
18334
|
+
"sdd_list_external_interfaces",
|
|
18335
|
+
{
|
|
18336
|
+
description: "List the bound project's consumable external surfaces (parent family, siblings, foreign imports) as discovery entries with origin, provenance, and freshness \u2014 the tool an agent inside a subproject uses to SEE its outward world instead of discovering it by failed reference resolution. Full contracts stay in the vendored snapshots (.wai/surfaces/); each entry summarizes the interface ids it exposes."
|
|
18337
|
+
},
|
|
18338
|
+
() => {
|
|
18339
|
+
try {
|
|
18340
|
+
return json(listExternalInterfaces2());
|
|
18341
|
+
} catch (e) {
|
|
18342
|
+
return errText(String(e));
|
|
18343
|
+
}
|
|
18344
|
+
}
|
|
18345
|
+
);
|
|
18175
18346
|
registerSkillResources(server);
|
|
18347
|
+
try {
|
|
18348
|
+
const chainingParent = resolveChainingParent();
|
|
18349
|
+
if (chainingParent) {
|
|
18350
|
+
let externalSurfaceCount = 0;
|
|
18351
|
+
try {
|
|
18352
|
+
externalSurfaceCount = listExternalInterfaces2().length;
|
|
18353
|
+
} catch {
|
|
18354
|
+
}
|
|
18355
|
+
process.stderr.write(
|
|
18356
|
+
`[wairon mcp] chained subproject: this root is mounted as subsystem "${chainingParent.subsystemId}" of the parent project at ${chainingParent.parentRoot} \u2014 ${externalSurfaceCount} vendored external surface(s) discoverable via sdd_list_external_interfaces
|
|
18357
|
+
`
|
|
18358
|
+
);
|
|
18359
|
+
}
|
|
18360
|
+
} catch {
|
|
18361
|
+
}
|
|
18176
18362
|
if (options.hostedTools) {
|
|
18177
18363
|
const hostedStub = () => errText("This hosted tool is dispatched by the hosting data plane before reaching the MCP server; it is unavailable outside a hosted request.");
|
|
18178
18364
|
reg(server, "sdd_host_lock_project", {
|
|
@@ -18308,6 +18494,8 @@ var init_server = __esm({
|
|
|
18308
18494
|
init_narrative_labels();
|
|
18309
18495
|
init_specs();
|
|
18310
18496
|
init_skills();
|
|
18497
|
+
init_specs2();
|
|
18498
|
+
init_surfaces();
|
|
18311
18499
|
SERVER_BUILD_STAMP = captureBuildStamp(__filename);
|
|
18312
18500
|
STALE_SERVER_WARNING = "\n\n\u26A0 STALE SERVER: the wairon build on disk changed after this MCP server started. Restart the MCP session (e.g. /mcp reconnect) before further spec edits \u2014 writes through a stale server can silently drop fields introduced by newer schemas.";
|
|
18313
18501
|
SKILL_RESOURCE_MIME = "text/markdown";
|
|
@@ -21381,7 +21569,7 @@ var require_dist = __commonJS({
|
|
|
21381
21569
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
21382
21570
|
}
|
|
21383
21571
|
var fs51 = __toESM2(require("fs"));
|
|
21384
|
-
var
|
|
21572
|
+
var path61 = __toESM2(require("path"));
|
|
21385
21573
|
var import_fflate = require_node();
|
|
21386
21574
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
|
|
21387
21575
|
function listEntries(archive) {
|
|
@@ -21419,8 +21607,8 @@ var require_dist = __commonJS({
|
|
|
21419
21607
|
}
|
|
21420
21608
|
function writeTree(destDir, files) {
|
|
21421
21609
|
for (const file of files) {
|
|
21422
|
-
const absolute =
|
|
21423
|
-
fs51.mkdirSync(
|
|
21610
|
+
const absolute = path61.join(destDir, file.path);
|
|
21611
|
+
fs51.mkdirSync(path61.dirname(absolute), { recursive: true });
|
|
21424
21612
|
fs51.writeFileSync(absolute, file.contents);
|
|
21425
21613
|
}
|
|
21426
21614
|
}
|
|
@@ -21433,10 +21621,10 @@ var require_dist = __commonJS({
|
|
|
21433
21621
|
for (const entry of fs51.readdirSync(current2, { withFileTypes: true })) {
|
|
21434
21622
|
if (entry.isDirectory()) {
|
|
21435
21623
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
21436
|
-
walkPackDir(root,
|
|
21624
|
+
walkPackDir(root, path61.join(current2, entry.name), out);
|
|
21437
21625
|
} else if (entry.isFile()) {
|
|
21438
|
-
const absolute =
|
|
21439
|
-
const relative22 =
|
|
21626
|
+
const absolute = path61.join(current2, entry.name);
|
|
21627
|
+
const relative22 = path61.relative(root, absolute).split(path61.sep).join("/");
|
|
21440
21628
|
out.push({ path: relative22, contents: fs51.readFileSync(absolute) });
|
|
21441
21629
|
}
|
|
21442
21630
|
}
|
|
@@ -22847,86 +23035,216 @@ async function generateLayer(options = {}) {
|
|
|
22847
23035
|
}
|
|
22848
23036
|
|
|
22849
23037
|
// src/commands/lock.ts
|
|
23038
|
+
var os7 = __toESM(require("os"));
|
|
22850
23039
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
22851
23040
|
init_logger();
|
|
22852
|
-
|
|
22853
|
-
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
|
|
22860
|
-
|
|
23041
|
+
init_defaults();
|
|
23042
|
+
|
|
23043
|
+
// src/core/detection.ts
|
|
23044
|
+
var fs18 = __toESM(require("fs"));
|
|
23045
|
+
var path27 = __toESM(require("path"));
|
|
23046
|
+
init_defaults();
|
|
23047
|
+
var PACKAGE_MARKERS = [
|
|
23048
|
+
"package.json",
|
|
23049
|
+
"pyproject.toml",
|
|
23050
|
+
"Cargo.toml",
|
|
23051
|
+
"go.mod",
|
|
23052
|
+
"build.gradle",
|
|
23053
|
+
"build.gradle.kts",
|
|
23054
|
+
"pom.xml"
|
|
23055
|
+
];
|
|
23056
|
+
var MAX_SCAN_DEPTH = 5;
|
|
23057
|
+
function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
|
|
23058
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
23059
|
+
for (const c of detectGitSubmodules(projectRoot2)) {
|
|
23060
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22861
23061
|
}
|
|
22862
|
-
const
|
|
22863
|
-
|
|
22864
|
-
|
|
22865
|
-
const promotable = collectPromotableSpecs(options.subsystem);
|
|
22866
|
-
const originalStatuses = /* @__PURE__ */ new Map();
|
|
22867
|
-
const isSpecInSubsystemScope = (specSubsystem) => {
|
|
22868
|
-
if (!options.subsystem) return true;
|
|
22869
|
-
if (!specSubsystem) return false;
|
|
22870
|
-
return specSubsystem === options.subsystem || specSubsystem.startsWith(`${options.subsystem}::`);
|
|
22871
|
-
};
|
|
22872
|
-
for (const s of index.subsystems) {
|
|
22873
|
-
if (!options.subsystem || s.id === options.subsystem || s.id.startsWith(`${options.subsystem}::`)) {
|
|
22874
|
-
originalStatuses.set(s, s.status);
|
|
22875
|
-
s.status = "complete";
|
|
23062
|
+
for (const c of detectNestedGitRepos(projectRoot2)) {
|
|
23063
|
+
if (!candidates.has(c.path)) {
|
|
23064
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22876
23065
|
}
|
|
22877
23066
|
}
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
23067
|
+
const gitPaths = new Set(
|
|
23068
|
+
Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
|
|
23069
|
+
);
|
|
23070
|
+
for (const c of detectPackageRoots(projectRoot2)) {
|
|
23071
|
+
if (candidates.has(c.path)) continue;
|
|
23072
|
+
const insideGit = Array.from(gitPaths).some(
|
|
23073
|
+
(gp) => c.path === gp || c.path.startsWith(gp + "/")
|
|
23074
|
+
);
|
|
23075
|
+
if (insideGit) continue;
|
|
23076
|
+
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
22883
23077
|
}
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
23078
|
+
const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
23079
|
+
return deduplicateIds(sorted, alreadyTrackedIds);
|
|
23080
|
+
}
|
|
23081
|
+
function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
23082
|
+
const idCount = /* @__PURE__ */ new Map();
|
|
23083
|
+
for (const id of existingIds) {
|
|
23084
|
+
idCount.set(id, (idCount.get(id) ?? 0) + 1);
|
|
22890
23085
|
}
|
|
22891
|
-
for (const
|
|
22892
|
-
|
|
22893
|
-
const comp = intf ? index.components.find((c) => c.id === intf.component) : null;
|
|
22894
|
-
if (comp && isSpecInSubsystemScope(comp.subsystem)) {
|
|
22895
|
-
originalStatuses.set(m, m.status);
|
|
22896
|
-
m.status = "complete";
|
|
22897
|
-
}
|
|
23086
|
+
for (const c of candidates) {
|
|
23087
|
+
idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
|
|
22898
23088
|
}
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
23089
|
+
return candidates.map((c) => {
|
|
23090
|
+
if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
|
|
23091
|
+
const parts = c.path.split("/");
|
|
23092
|
+
const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
|
|
23093
|
+
return { ...c, suggestedId: qualifiedId2 };
|
|
22904
23094
|
});
|
|
22905
|
-
|
|
22906
|
-
|
|
23095
|
+
}
|
|
23096
|
+
function parseGitmodules(filePath) {
|
|
23097
|
+
const content = fs18.readFileSync(filePath, "utf-8");
|
|
23098
|
+
const entries = [];
|
|
23099
|
+
let current2 = {};
|
|
23100
|
+
for (const line2 of content.split("\n")) {
|
|
23101
|
+
const trimmed = line2.trim();
|
|
23102
|
+
const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
|
|
23103
|
+
if (headerMatch) {
|
|
23104
|
+
if (current2.path) entries.push(current2);
|
|
23105
|
+
current2 = { name: headerMatch[1] };
|
|
23106
|
+
continue;
|
|
23107
|
+
}
|
|
23108
|
+
const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
23109
|
+
if (keyVal) {
|
|
23110
|
+
const [, key, value] = keyVal;
|
|
23111
|
+
if (key === "path") current2.path = value.trim();
|
|
23112
|
+
if (key === "url") current2.url = value.trim();
|
|
23113
|
+
}
|
|
22907
23114
|
}
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22919
|
-
|
|
22920
|
-
|
|
23115
|
+
if (current2.path) entries.push(current2);
|
|
23116
|
+
return entries;
|
|
23117
|
+
}
|
|
23118
|
+
function detectGitSubmodules(projectRoot2) {
|
|
23119
|
+
const gitmodulesPath = path27.join(projectRoot2, ".gitmodules");
|
|
23120
|
+
if (!fs18.existsSync(gitmodulesPath)) return [];
|
|
23121
|
+
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
23122
|
+
suggestedId: pathToId(entry.path),
|
|
23123
|
+
suggestedName: pathToName(entry.path),
|
|
23124
|
+
path: normalizePath3(entry.path),
|
|
23125
|
+
type: "git-submodule",
|
|
23126
|
+
alreadyTracked: false
|
|
23127
|
+
}));
|
|
23128
|
+
}
|
|
23129
|
+
function detectNestedGitRepos(projectRoot2) {
|
|
23130
|
+
const results = [];
|
|
23131
|
+
walkForGit(projectRoot2, projectRoot2, 0, results);
|
|
23132
|
+
return results;
|
|
23133
|
+
}
|
|
23134
|
+
function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
23135
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
23136
|
+
let entries;
|
|
23137
|
+
try {
|
|
23138
|
+
entries = fs18.readdirSync(currentDir, { withFileTypes: true });
|
|
23139
|
+
} catch {
|
|
23140
|
+
return;
|
|
23141
|
+
}
|
|
23142
|
+
for (const entry of entries) {
|
|
23143
|
+
if (!entry.isDirectory()) continue;
|
|
23144
|
+
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23145
|
+
const fullPath = path27.join(currentDir, entry.name);
|
|
23146
|
+
const relPath = normalizePath3(path27.relative(projectRoot2, fullPath));
|
|
23147
|
+
if (relPath === "" || relPath === ".") continue;
|
|
23148
|
+
const gitPath = path27.join(fullPath, ".git");
|
|
23149
|
+
if (fs18.existsSync(gitPath)) {
|
|
23150
|
+
results.push({
|
|
23151
|
+
suggestedId: pathToId(relPath),
|
|
23152
|
+
suggestedName: pathToName(relPath),
|
|
23153
|
+
path: relPath,
|
|
23154
|
+
type: "git-repo",
|
|
23155
|
+
alreadyTracked: false
|
|
23156
|
+
});
|
|
23157
|
+
continue;
|
|
22921
23158
|
}
|
|
22922
|
-
|
|
22923
|
-
|
|
23159
|
+
walkForGit(projectRoot2, fullPath, depth + 1, results);
|
|
23160
|
+
}
|
|
23161
|
+
}
|
|
23162
|
+
function detectPackageRoots(projectRoot2) {
|
|
23163
|
+
const results = [];
|
|
23164
|
+
walkForPackages(projectRoot2, projectRoot2, 0, results);
|
|
23165
|
+
return results;
|
|
23166
|
+
}
|
|
23167
|
+
function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
23168
|
+
if (depth > MAX_SCAN_DEPTH) return;
|
|
23169
|
+
let entries;
|
|
23170
|
+
try {
|
|
23171
|
+
entries = fs18.readdirSync(currentDir, { withFileTypes: true });
|
|
23172
|
+
} catch {
|
|
23173
|
+
return;
|
|
23174
|
+
}
|
|
23175
|
+
for (const entry of entries) {
|
|
23176
|
+
if (!entry.isDirectory()) continue;
|
|
23177
|
+
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23178
|
+
const fullPath = path27.join(currentDir, entry.name);
|
|
23179
|
+
const relPath = normalizePath3(path27.relative(projectRoot2, fullPath));
|
|
23180
|
+
if (relPath === "" || relPath === ".") continue;
|
|
23181
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs18.existsSync(path27.join(fullPath, m)));
|
|
23182
|
+
if (hasMarker) {
|
|
23183
|
+
results.push({
|
|
23184
|
+
suggestedId: pathToId(relPath),
|
|
23185
|
+
suggestedName: pathToName(relPath),
|
|
23186
|
+
path: relPath,
|
|
23187
|
+
type: "package-root",
|
|
23188
|
+
alreadyTracked: false
|
|
23189
|
+
});
|
|
22924
23190
|
}
|
|
22925
|
-
|
|
22926
|
-
logger.info("Fix the errors above, then run `wairon lock` again. Nothing was changed.");
|
|
22927
|
-
process.exit(1);
|
|
23191
|
+
walkForPackages(projectRoot2, fullPath, depth + 1, results);
|
|
22928
23192
|
}
|
|
22929
|
-
|
|
23193
|
+
}
|
|
23194
|
+
function pathToId(relPath) {
|
|
23195
|
+
const basename11 = path27.basename(relPath);
|
|
23196
|
+
return basename11.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23197
|
+
}
|
|
23198
|
+
function pathToName(relPath) {
|
|
23199
|
+
const id = pathToId(relPath);
|
|
23200
|
+
return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
23201
|
+
}
|
|
23202
|
+
function normalizePath3(p) {
|
|
23203
|
+
return p.replace(/\\/g, "/");
|
|
23204
|
+
}
|
|
23205
|
+
|
|
23206
|
+
// src/core/index.ts
|
|
23207
|
+
init_domains();
|
|
23208
|
+
init_validation();
|
|
23209
|
+
init_extensions();
|
|
23210
|
+
init_variants();
|
|
23211
|
+
init_rules();
|
|
23212
|
+
init_specs2();
|
|
23213
|
+
init_provision();
|
|
23214
|
+
init_diagram();
|
|
23215
|
+
|
|
23216
|
+
// src/core/lockfile.ts
|
|
23217
|
+
var fs19 = __toESM(require("fs"));
|
|
23218
|
+
var path28 = __toESM(require("path"));
|
|
23219
|
+
init_fs();
|
|
23220
|
+
function lockPath() {
|
|
23221
|
+
return aiDir("lock.json");
|
|
23222
|
+
}
|
|
23223
|
+
function readLockRecord() {
|
|
23224
|
+
try {
|
|
23225
|
+
return JSON.parse(fs19.readFileSync(lockPath(), "utf8"));
|
|
23226
|
+
} catch {
|
|
23227
|
+
return null;
|
|
23228
|
+
}
|
|
23229
|
+
}
|
|
23230
|
+
function writeLockRecord(record2) {
|
|
23231
|
+
const p = lockPath();
|
|
23232
|
+
fs19.mkdirSync(path28.dirname(p), { recursive: true });
|
|
23233
|
+
const tmp = `${p}.tmp`;
|
|
23234
|
+
fs19.writeFileSync(tmp, JSON.stringify(record2, null, 2) + "\n");
|
|
23235
|
+
fs19.renameSync(tmp, p);
|
|
23236
|
+
}
|
|
23237
|
+
|
|
23238
|
+
// src/core/index.ts
|
|
23239
|
+
init_statehash();
|
|
23240
|
+
init_agent_resolver();
|
|
23241
|
+
init_skills();
|
|
23242
|
+
init_surfaces();
|
|
23243
|
+
init_openapi();
|
|
23244
|
+
|
|
23245
|
+
// src/commands/lock.ts
|
|
23246
|
+
async function runLock(options = {}, gate) {
|
|
23247
|
+
const promotable = collectPromotableSpecs(options.subsystem);
|
|
22930
23248
|
if (promotable.length === 0) {
|
|
22931
23249
|
logger.info("All specs are already complete \u2014 this will re-validate and regenerate the agent topology.");
|
|
22932
23250
|
} else {
|
|
@@ -22950,23 +23268,36 @@ async function runLock(options = {}) {
|
|
|
22950
23268
|
default: false
|
|
22951
23269
|
}
|
|
22952
23270
|
]);
|
|
22953
|
-
if (!confirmed)
|
|
22954
|
-
|
|
22955
|
-
|
|
22956
|
-
|
|
23271
|
+
if (!confirmed) return null;
|
|
23272
|
+
}
|
|
23273
|
+
if (options.subsystem) {
|
|
23274
|
+
for (const p of promotable) applySpecStatus(p.kind, p.id, "complete");
|
|
23275
|
+
invalidateSpecCache();
|
|
23276
|
+
} else {
|
|
23277
|
+
promoteAllComplete();
|
|
22957
23278
|
}
|
|
22958
|
-
for (const p of promotable) applySpecStatus(p.kind, p.id, "complete");
|
|
22959
|
-
invalidateSpecCache();
|
|
22960
23279
|
if (promotable.length > 0) {
|
|
22961
23280
|
logger.success(`Locked ${promotable.length} spec(s) as complete.`);
|
|
22962
23281
|
}
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
|
|
22966
|
-
|
|
22967
|
-
|
|
22968
|
-
|
|
22969
|
-
|
|
23282
|
+
let lockedBy = "local";
|
|
23283
|
+
try {
|
|
23284
|
+
lockedBy = `local:${os7.userInfo().username}`;
|
|
23285
|
+
} catch {
|
|
23286
|
+
}
|
|
23287
|
+
const record2 = {
|
|
23288
|
+
stateId: computeStateId(),
|
|
23289
|
+
lockedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23290
|
+
lockedBy,
|
|
23291
|
+
validatorVersion: WAIRON_VERSION,
|
|
23292
|
+
validationResult: {
|
|
23293
|
+
valid: true,
|
|
23294
|
+
errors: 0,
|
|
23295
|
+
warnings: gate ? gate.issues.filter((i) => i.severity === "warning").length : 0
|
|
23296
|
+
},
|
|
23297
|
+
status: "ready"
|
|
23298
|
+
};
|
|
23299
|
+
writeLockRecord(record2);
|
|
23300
|
+
return record2;
|
|
22970
23301
|
}
|
|
22971
23302
|
|
|
22972
23303
|
// src/commands/validate.ts
|
|
@@ -23105,6 +23436,10 @@ async function runValidate(options = {}) {
|
|
|
23105
23436
|
}
|
|
23106
23437
|
}
|
|
23107
23438
|
|
|
23439
|
+
// src/cli/index.ts
|
|
23440
|
+
init_loader();
|
|
23441
|
+
init_fs();
|
|
23442
|
+
|
|
23108
23443
|
// src/commands/list.ts
|
|
23109
23444
|
var import_chalk7 = __toESM(require("chalk"));
|
|
23110
23445
|
init_logger();
|
|
@@ -23219,9 +23554,9 @@ init_mcp();
|
|
|
23219
23554
|
// src/commands/update.ts
|
|
23220
23555
|
var https = __toESM(require("https"));
|
|
23221
23556
|
var http = __toESM(require("http"));
|
|
23222
|
-
var
|
|
23223
|
-
var
|
|
23224
|
-
var
|
|
23557
|
+
var fs20 = __toESM(require("fs"));
|
|
23558
|
+
var path29 = __toESM(require("path"));
|
|
23559
|
+
var os8 = __toESM(require("os"));
|
|
23225
23560
|
var crypto2 = __toESM(require("crypto"));
|
|
23226
23561
|
var import_child_process2 = require("child_process");
|
|
23227
23562
|
init_logger();
|
|
@@ -23284,8 +23619,8 @@ async function runUpdate(options = {}) {
|
|
|
23284
23619
|
logger.info(`Download manually from: ${release.html_url}`);
|
|
23285
23620
|
process.exit(1);
|
|
23286
23621
|
}
|
|
23287
|
-
const tmpDir =
|
|
23288
|
-
const tmpFile =
|
|
23622
|
+
const tmpDir = os8.tmpdir();
|
|
23623
|
+
const tmpFile = path29.join(tmpDir, assetName);
|
|
23289
23624
|
logger.info(`Downloading ${assetName}...`);
|
|
23290
23625
|
try {
|
|
23291
23626
|
await downloadFile(asset.browser_download_url, tmpFile);
|
|
@@ -23302,16 +23637,16 @@ async function runUpdate(options = {}) {
|
|
|
23302
23637
|
const checksumAssetName = assetName + ".sha256";
|
|
23303
23638
|
const checksumAsset = release.assets.find((a) => a.name === checksumAssetName);
|
|
23304
23639
|
if (checksumAsset) {
|
|
23305
|
-
const tmpChecksum =
|
|
23640
|
+
const tmpChecksum = path29.join(tmpDir, checksumAssetName);
|
|
23306
23641
|
logger.info(`Verifying checksum...`);
|
|
23307
23642
|
try {
|
|
23308
23643
|
await downloadFile(checksumAsset.browser_download_url, tmpChecksum);
|
|
23309
23644
|
verifyChecksum(tmpFile, tmpChecksum, assetName);
|
|
23310
|
-
|
|
23645
|
+
fs20.unlinkSync(tmpChecksum);
|
|
23311
23646
|
} catch (err) {
|
|
23312
23647
|
logger.error(`Checksum verification failed: ${err.message}`);
|
|
23313
23648
|
try {
|
|
23314
|
-
|
|
23649
|
+
fs20.unlinkSync(tmpFile);
|
|
23315
23650
|
} catch {
|
|
23316
23651
|
}
|
|
23317
23652
|
process.exit(1);
|
|
@@ -23377,7 +23712,7 @@ function fetchReleases(repo) {
|
|
|
23377
23712
|
}
|
|
23378
23713
|
function downloadFile(url, dest) {
|
|
23379
23714
|
return new Promise((resolve24, reject) => {
|
|
23380
|
-
const file =
|
|
23715
|
+
const file = fs20.createWriteStream(dest);
|
|
23381
23716
|
const get3 = url.startsWith("https://") ? https.get : http.get;
|
|
23382
23717
|
get3(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
|
|
23383
23718
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
@@ -23399,21 +23734,21 @@ function downloadFile(url, dest) {
|
|
|
23399
23734
|
});
|
|
23400
23735
|
file.on("error", (err) => {
|
|
23401
23736
|
res.destroy();
|
|
23402
|
-
|
|
23737
|
+
fs20.unlink(dest, () => {
|
|
23403
23738
|
});
|
|
23404
23739
|
reject(err);
|
|
23405
23740
|
});
|
|
23406
23741
|
}).on("error", (err) => {
|
|
23407
|
-
|
|
23742
|
+
fs20.unlink(dest, () => {
|
|
23408
23743
|
});
|
|
23409
23744
|
reject(err);
|
|
23410
23745
|
});
|
|
23411
23746
|
});
|
|
23412
23747
|
}
|
|
23413
23748
|
function verifyChecksum(filePath, checksumFile, expectedFilename) {
|
|
23414
|
-
const checksumContent =
|
|
23749
|
+
const checksumContent = fs20.readFileSync(checksumFile, "utf-8").trim();
|
|
23415
23750
|
const expectedHash = checksumContent.split(/\s+/)[0].toLowerCase();
|
|
23416
|
-
const fileBuffer =
|
|
23751
|
+
const fileBuffer = fs20.readFileSync(filePath);
|
|
23417
23752
|
const actualHash = crypto2.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase();
|
|
23418
23753
|
if (actualHash !== expectedHash) {
|
|
23419
23754
|
throw new Error(
|
|
@@ -23444,9 +23779,9 @@ function isPkgBinary2() {
|
|
|
23444
23779
|
function installBinary(tmpFile, destPath) {
|
|
23445
23780
|
const platform = process.platform;
|
|
23446
23781
|
const isZip = tmpFile.endsWith(".zip");
|
|
23447
|
-
const extractDir =
|
|
23448
|
-
if (
|
|
23449
|
-
|
|
23782
|
+
const extractDir = path29.join(os8.tmpdir(), "wairon-extract");
|
|
23783
|
+
if (fs20.existsSync(extractDir)) fs20.rmSync(extractDir, { recursive: true });
|
|
23784
|
+
fs20.mkdirSync(extractDir, { recursive: true });
|
|
23450
23785
|
if (isZip) {
|
|
23451
23786
|
(0, import_child_process2.execSync)(
|
|
23452
23787
|
`powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmpFile}' -DestinationPath '${extractDir}' -Force"`,
|
|
@@ -23456,18 +23791,18 @@ function installBinary(tmpFile, destPath) {
|
|
|
23456
23791
|
(0, import_child_process2.execSync)(`tar -xzf "${tmpFile}" -C "${extractDir}"`, { stdio: ["ignore", "pipe", "pipe"] });
|
|
23457
23792
|
}
|
|
23458
23793
|
const binaryName = platform === "win32" ? "wairon.exe" : "wairon";
|
|
23459
|
-
const extractedBinary =
|
|
23460
|
-
if (!
|
|
23794
|
+
const extractedBinary = path29.join(extractDir, binaryName);
|
|
23795
|
+
if (!fs20.existsSync(extractedBinary)) {
|
|
23461
23796
|
throw new Error(`Extracted binary not found at ${extractedBinary}`);
|
|
23462
23797
|
}
|
|
23463
23798
|
if (platform === "win32") {
|
|
23464
23799
|
const oldPath = destPath + ".old";
|
|
23465
23800
|
try {
|
|
23466
23801
|
cleanStaleBinary(oldPath);
|
|
23467
|
-
|
|
23468
|
-
|
|
23802
|
+
fs20.renameSync(destPath, oldPath);
|
|
23803
|
+
fs20.copyFileSync(extractedBinary, destPath);
|
|
23469
23804
|
try {
|
|
23470
|
-
|
|
23805
|
+
fs20.unlinkSync(oldPath);
|
|
23471
23806
|
} catch {
|
|
23472
23807
|
}
|
|
23473
23808
|
} catch (err) {
|
|
@@ -23481,25 +23816,25 @@ function installBinary(tmpFile, destPath) {
|
|
|
23481
23816
|
}
|
|
23482
23817
|
} else {
|
|
23483
23818
|
const tmpDest = destPath + ".new";
|
|
23484
|
-
|
|
23485
|
-
|
|
23486
|
-
|
|
23819
|
+
fs20.copyFileSync(extractedBinary, tmpDest);
|
|
23820
|
+
fs20.chmodSync(tmpDest, 493);
|
|
23821
|
+
fs20.renameSync(tmpDest, destPath);
|
|
23487
23822
|
}
|
|
23488
23823
|
try {
|
|
23489
|
-
|
|
23824
|
+
fs20.unlinkSync(tmpFile);
|
|
23490
23825
|
} catch {
|
|
23491
23826
|
}
|
|
23492
23827
|
try {
|
|
23493
|
-
|
|
23828
|
+
fs20.rmSync(extractDir, { recursive: true });
|
|
23494
23829
|
} catch {
|
|
23495
23830
|
}
|
|
23496
23831
|
}
|
|
23497
23832
|
function cleanStaleBinary(oldPath) {
|
|
23498
23833
|
const target = oldPath ?? (isPkgBinary2() ? process.execPath + ".old" : null);
|
|
23499
23834
|
if (!target) return;
|
|
23500
|
-
if (
|
|
23835
|
+
if (fs20.existsSync(target)) {
|
|
23501
23836
|
try {
|
|
23502
|
-
|
|
23837
|
+
fs20.unlinkSync(target);
|
|
23503
23838
|
} catch {
|
|
23504
23839
|
}
|
|
23505
23840
|
}
|
|
@@ -23745,171 +24080,6 @@ async function filteredCheckbox(config) {
|
|
|
23745
24080
|
|
|
23746
24081
|
// src/commands/domains.ts
|
|
23747
24082
|
init_loader();
|
|
23748
|
-
|
|
23749
|
-
// src/core/detection.ts
|
|
23750
|
-
var fs19 = __toESM(require("fs"));
|
|
23751
|
-
var path28 = __toESM(require("path"));
|
|
23752
|
-
init_defaults();
|
|
23753
|
-
var PACKAGE_MARKERS = [
|
|
23754
|
-
"package.json",
|
|
23755
|
-
"pyproject.toml",
|
|
23756
|
-
"Cargo.toml",
|
|
23757
|
-
"go.mod",
|
|
23758
|
-
"build.gradle",
|
|
23759
|
-
"build.gradle.kts",
|
|
23760
|
-
"pom.xml"
|
|
23761
|
-
];
|
|
23762
|
-
var MAX_SCAN_DEPTH = 5;
|
|
23763
|
-
function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
|
|
23764
|
-
const candidates = /* @__PURE__ */ new Map();
|
|
23765
|
-
for (const c of detectGitSubmodules(projectRoot2)) {
|
|
23766
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
23767
|
-
}
|
|
23768
|
-
for (const c of detectNestedGitRepos(projectRoot2)) {
|
|
23769
|
-
if (!candidates.has(c.path)) {
|
|
23770
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
23771
|
-
}
|
|
23772
|
-
}
|
|
23773
|
-
const gitPaths = new Set(
|
|
23774
|
-
Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
|
|
23775
|
-
);
|
|
23776
|
-
for (const c of detectPackageRoots(projectRoot2)) {
|
|
23777
|
-
if (candidates.has(c.path)) continue;
|
|
23778
|
-
const insideGit = Array.from(gitPaths).some(
|
|
23779
|
-
(gp) => c.path === gp || c.path.startsWith(gp + "/")
|
|
23780
|
-
);
|
|
23781
|
-
if (insideGit) continue;
|
|
23782
|
-
candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
|
|
23783
|
-
}
|
|
23784
|
-
const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
23785
|
-
return deduplicateIds(sorted, alreadyTrackedIds);
|
|
23786
|
-
}
|
|
23787
|
-
function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
23788
|
-
const idCount = /* @__PURE__ */ new Map();
|
|
23789
|
-
for (const id of existingIds) {
|
|
23790
|
-
idCount.set(id, (idCount.get(id) ?? 0) + 1);
|
|
23791
|
-
}
|
|
23792
|
-
for (const c of candidates) {
|
|
23793
|
-
idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
|
|
23794
|
-
}
|
|
23795
|
-
return candidates.map((c) => {
|
|
23796
|
-
if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
|
|
23797
|
-
const parts = c.path.split("/");
|
|
23798
|
-
const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
|
|
23799
|
-
return { ...c, suggestedId: qualifiedId2 };
|
|
23800
|
-
});
|
|
23801
|
-
}
|
|
23802
|
-
function parseGitmodules(filePath) {
|
|
23803
|
-
const content = fs19.readFileSync(filePath, "utf-8");
|
|
23804
|
-
const entries = [];
|
|
23805
|
-
let current2 = {};
|
|
23806
|
-
for (const line2 of content.split("\n")) {
|
|
23807
|
-
const trimmed = line2.trim();
|
|
23808
|
-
const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
|
|
23809
|
-
if (headerMatch) {
|
|
23810
|
-
if (current2.path) entries.push(current2);
|
|
23811
|
-
current2 = { name: headerMatch[1] };
|
|
23812
|
-
continue;
|
|
23813
|
-
}
|
|
23814
|
-
const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
23815
|
-
if (keyVal) {
|
|
23816
|
-
const [, key, value] = keyVal;
|
|
23817
|
-
if (key === "path") current2.path = value.trim();
|
|
23818
|
-
if (key === "url") current2.url = value.trim();
|
|
23819
|
-
}
|
|
23820
|
-
}
|
|
23821
|
-
if (current2.path) entries.push(current2);
|
|
23822
|
-
return entries;
|
|
23823
|
-
}
|
|
23824
|
-
function detectGitSubmodules(projectRoot2) {
|
|
23825
|
-
const gitmodulesPath = path28.join(projectRoot2, ".gitmodules");
|
|
23826
|
-
if (!fs19.existsSync(gitmodulesPath)) return [];
|
|
23827
|
-
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
23828
|
-
suggestedId: pathToId(entry.path),
|
|
23829
|
-
suggestedName: pathToName(entry.path),
|
|
23830
|
-
path: normalizePath3(entry.path),
|
|
23831
|
-
type: "git-submodule",
|
|
23832
|
-
alreadyTracked: false
|
|
23833
|
-
}));
|
|
23834
|
-
}
|
|
23835
|
-
function detectNestedGitRepos(projectRoot2) {
|
|
23836
|
-
const results = [];
|
|
23837
|
-
walkForGit(projectRoot2, projectRoot2, 0, results);
|
|
23838
|
-
return results;
|
|
23839
|
-
}
|
|
23840
|
-
function walkForGit(projectRoot2, currentDir, depth, results) {
|
|
23841
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
23842
|
-
let entries;
|
|
23843
|
-
try {
|
|
23844
|
-
entries = fs19.readdirSync(currentDir, { withFileTypes: true });
|
|
23845
|
-
} catch {
|
|
23846
|
-
return;
|
|
23847
|
-
}
|
|
23848
|
-
for (const entry of entries) {
|
|
23849
|
-
if (!entry.isDirectory()) continue;
|
|
23850
|
-
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23851
|
-
const fullPath = path28.join(currentDir, entry.name);
|
|
23852
|
-
const relPath = normalizePath3(path28.relative(projectRoot2, fullPath));
|
|
23853
|
-
if (relPath === "" || relPath === ".") continue;
|
|
23854
|
-
const gitPath = path28.join(fullPath, ".git");
|
|
23855
|
-
if (fs19.existsSync(gitPath)) {
|
|
23856
|
-
results.push({
|
|
23857
|
-
suggestedId: pathToId(relPath),
|
|
23858
|
-
suggestedName: pathToName(relPath),
|
|
23859
|
-
path: relPath,
|
|
23860
|
-
type: "git-repo",
|
|
23861
|
-
alreadyTracked: false
|
|
23862
|
-
});
|
|
23863
|
-
continue;
|
|
23864
|
-
}
|
|
23865
|
-
walkForGit(projectRoot2, fullPath, depth + 1, results);
|
|
23866
|
-
}
|
|
23867
|
-
}
|
|
23868
|
-
function detectPackageRoots(projectRoot2) {
|
|
23869
|
-
const results = [];
|
|
23870
|
-
walkForPackages(projectRoot2, projectRoot2, 0, results);
|
|
23871
|
-
return results;
|
|
23872
|
-
}
|
|
23873
|
-
function walkForPackages(projectRoot2, currentDir, depth, results) {
|
|
23874
|
-
if (depth > MAX_SCAN_DEPTH) return;
|
|
23875
|
-
let entries;
|
|
23876
|
-
try {
|
|
23877
|
-
entries = fs19.readdirSync(currentDir, { withFileTypes: true });
|
|
23878
|
-
} catch {
|
|
23879
|
-
return;
|
|
23880
|
-
}
|
|
23881
|
-
for (const entry of entries) {
|
|
23882
|
-
if (!entry.isDirectory()) continue;
|
|
23883
|
-
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
23884
|
-
const fullPath = path28.join(currentDir, entry.name);
|
|
23885
|
-
const relPath = normalizePath3(path28.relative(projectRoot2, fullPath));
|
|
23886
|
-
if (relPath === "" || relPath === ".") continue;
|
|
23887
|
-
const hasMarker = PACKAGE_MARKERS.some((m) => fs19.existsSync(path28.join(fullPath, m)));
|
|
23888
|
-
if (hasMarker) {
|
|
23889
|
-
results.push({
|
|
23890
|
-
suggestedId: pathToId(relPath),
|
|
23891
|
-
suggestedName: pathToName(relPath),
|
|
23892
|
-
path: relPath,
|
|
23893
|
-
type: "package-root",
|
|
23894
|
-
alreadyTracked: false
|
|
23895
|
-
});
|
|
23896
|
-
}
|
|
23897
|
-
walkForPackages(projectRoot2, fullPath, depth + 1, results);
|
|
23898
|
-
}
|
|
23899
|
-
}
|
|
23900
|
-
function pathToId(relPath) {
|
|
23901
|
-
const basename12 = path28.basename(relPath);
|
|
23902
|
-
return basename12.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23903
|
-
}
|
|
23904
|
-
function pathToName(relPath) {
|
|
23905
|
-
const id = pathToId(relPath);
|
|
23906
|
-
return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
23907
|
-
}
|
|
23908
|
-
function normalizePath3(p) {
|
|
23909
|
-
return p.replace(/\\/g, "/");
|
|
23910
|
-
}
|
|
23911
|
-
|
|
23912
|
-
// src/commands/domains.ts
|
|
23913
24083
|
init_domains();
|
|
23914
24084
|
init_domain();
|
|
23915
24085
|
async function runDomainsList() {
|
|
@@ -24099,9 +24269,9 @@ async function runSkillsInstall() {
|
|
|
24099
24269
|
}
|
|
24100
24270
|
|
|
24101
24271
|
// src/commands/doctor.ts
|
|
24102
|
-
var
|
|
24103
|
-
var
|
|
24104
|
-
var
|
|
24272
|
+
var fs21 = __toESM(require("fs"));
|
|
24273
|
+
var os9 = __toESM(require("os"));
|
|
24274
|
+
var path30 = __toESM(require("path"));
|
|
24105
24275
|
var import_chalk12 = __toESM(require("chalk"));
|
|
24106
24276
|
init_logger();
|
|
24107
24277
|
init_defaults();
|
|
@@ -24131,10 +24301,10 @@ function stampVerdict(content) {
|
|
|
24131
24301
|
return { mark: "warn", note: `v${v} \u2014 stale, installed is v${WAIRON_VERSION}` };
|
|
24132
24302
|
}
|
|
24133
24303
|
function mcpEntryHealth(settingsPath) {
|
|
24134
|
-
if (!
|
|
24304
|
+
if (!fs21.existsSync(settingsPath)) return { mark: "warn", note: "not registered" };
|
|
24135
24305
|
let entry;
|
|
24136
24306
|
try {
|
|
24137
|
-
const s = JSON.parse(
|
|
24307
|
+
const s = JSON.parse(fs21.readFileSync(settingsPath, "utf8"));
|
|
24138
24308
|
entry = s.mcpServers?.["wairon"];
|
|
24139
24309
|
} catch {
|
|
24140
24310
|
return { mark: "error", note: "parse error" };
|
|
@@ -24142,7 +24312,7 @@ function mcpEntryHealth(settingsPath) {
|
|
|
24142
24312
|
if (!entry) return { mark: "warn", note: "not registered" };
|
|
24143
24313
|
if (entry.command === "node" && Array.isArray(entry.args) && typeof entry.args[0] === "string") {
|
|
24144
24314
|
const scriptPath = entry.args[0];
|
|
24145
|
-
if (!
|
|
24315
|
+
if (!fs21.existsSync(scriptPath)) {
|
|
24146
24316
|
return { mark: "error", note: `registered but the server path is missing \u2014 ${scriptPath}` };
|
|
24147
24317
|
}
|
|
24148
24318
|
}
|
|
@@ -24194,7 +24364,7 @@ async function runDoctor(options = {}) {
|
|
|
24194
24364
|
const { findChainingSubprojectsMissingConfig: findChainingSubprojectsMissingConfig2 } = (init_provision(), __toCommonJS(provision_exports));
|
|
24195
24365
|
const missing = findChainingSubprojectsMissingConfig2(getProjectRoot());
|
|
24196
24366
|
if (missing.length > 0) {
|
|
24197
|
-
line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) =>
|
|
24367
|
+
line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path30.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
|
|
24198
24368
|
}
|
|
24199
24369
|
} catch {
|
|
24200
24370
|
}
|
|
@@ -24232,7 +24402,7 @@ async function runDoctor(options = {}) {
|
|
|
24232
24402
|
const gp = localGuideFilePath(process.cwd(), t);
|
|
24233
24403
|
if (!gp || seenGuides.has(gp)) continue;
|
|
24234
24404
|
seenGuides.add(gp);
|
|
24235
|
-
const rel2 =
|
|
24405
|
+
const rel2 = path30.relative(process.cwd(), gp).replace(/\\/g, "/");
|
|
24236
24406
|
if (!pathExists(gp)) {
|
|
24237
24407
|
line(tally, "warn", `${rel2} guide \u2014 not injected (run \`wairon generate\`)`);
|
|
24238
24408
|
continue;
|
|
@@ -24266,17 +24436,17 @@ async function runDoctor(options = {}) {
|
|
|
24266
24436
|
line(tally, h.mark, `Claude (project .mcp.json): ${h.note}${h.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend claude`"}`);
|
|
24267
24437
|
}
|
|
24268
24438
|
if (wantGemini) {
|
|
24269
|
-
const globalCfg =
|
|
24439
|
+
const globalCfg = path30.join(os9.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
|
|
24270
24440
|
const hg = mcpEntryHealth(globalCfg);
|
|
24271
24441
|
line(tally, hg.mark, `Antigravity (global mcp_config.json): ${hg.note}${hg.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend gemini --global`"}`);
|
|
24272
24442
|
const projPath = fromProjectRoot(".gemini", "settings.json");
|
|
24273
|
-
if (
|
|
24443
|
+
if (fs21.existsSync(projPath)) {
|
|
24274
24444
|
const hp = mcpEntryHealth(projPath);
|
|
24275
24445
|
line(tally, hp.mark === "error" ? "error" : "ok", `Gemini CLI (project): ${hp.note} ${import_chalk12.default.gray("(Antigravity ignores this file)")}`);
|
|
24276
24446
|
}
|
|
24277
24447
|
}
|
|
24278
|
-
const pluginDir =
|
|
24279
|
-
if (
|
|
24448
|
+
const pluginDir = path30.join(os9.homedir(), ".gemini", "config", "plugins", "wairon");
|
|
24449
|
+
if (fs21.existsSync(pluginDir)) {
|
|
24280
24450
|
line(tally, "warn", `Legacy Antigravity plugin present (${pluginDir}) \u2014 it collides with the wairon MCP server. Remove it with \`wairon doctor --fix\`.`);
|
|
24281
24451
|
}
|
|
24282
24452
|
logger.blank();
|
|
@@ -24307,7 +24477,7 @@ async function applyFixes() {
|
|
|
24307
24477
|
const legacySpecs = findLegacySpecFiles();
|
|
24308
24478
|
if (legacySpecs.length > 0) {
|
|
24309
24479
|
for (const { path: oldPath, expected: newPath } of legacySpecs) {
|
|
24310
|
-
|
|
24480
|
+
fs21.renameSync(oldPath, newPath);
|
|
24311
24481
|
}
|
|
24312
24482
|
console.log(` ${icon("ok")} Migrated ${legacySpecs.length} legacy spec file(s) to the new dot-prefixed unified schema.`);
|
|
24313
24483
|
}
|
|
@@ -24361,8 +24531,8 @@ function printSummary(tally) {
|
|
|
24361
24531
|
}
|
|
24362
24532
|
|
|
24363
24533
|
// src/commands/diagram.ts
|
|
24364
|
-
var
|
|
24365
|
-
var
|
|
24534
|
+
var fs22 = __toESM(require("fs"));
|
|
24535
|
+
var path31 = __toESM(require("path"));
|
|
24366
24536
|
init_logger();
|
|
24367
24537
|
init_loader();
|
|
24368
24538
|
init_fs();
|
|
@@ -24397,8 +24567,8 @@ function collectIssues() {
|
|
|
24397
24567
|
}
|
|
24398
24568
|
function writeCanvas(dest) {
|
|
24399
24569
|
const model = buildCanvasModel(collectIssues());
|
|
24400
|
-
ensureDir(
|
|
24401
|
-
|
|
24570
|
+
ensureDir(path31.dirname(path31.resolve(dest)));
|
|
24571
|
+
fs22.writeFileSync(dest, renderCanvasHtml(model), "utf-8");
|
|
24402
24572
|
}
|
|
24403
24573
|
function parseSequenceRef(ref) {
|
|
24404
24574
|
const sep6 = ref.includes(":") ? ref.lastIndexOf(":") : ref.lastIndexOf(".");
|
|
@@ -24413,55 +24583,55 @@ async function runDiagram(rawOptions = {}) {
|
|
|
24413
24583
|
assertProjectInitialized();
|
|
24414
24584
|
const options = applyFormat(rawOptions);
|
|
24415
24585
|
if (options.canvas && !options.all) {
|
|
24416
|
-
const dest2 = options.out ??
|
|
24586
|
+
const dest2 = options.out ?? path31.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
24417
24587
|
writeCanvas(dest2);
|
|
24418
24588
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
24419
24589
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline).");
|
|
24420
24590
|
return;
|
|
24421
24591
|
}
|
|
24422
24592
|
if (options.drawio && !options.all) {
|
|
24423
|
-
const dest2 = options.out ??
|
|
24424
|
-
ensureDir(
|
|
24425
|
-
|
|
24593
|
+
const dest2 = options.out ?? path31.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
|
|
24594
|
+
ensureDir(path31.dirname(path31.resolve(dest2)));
|
|
24595
|
+
fs22.writeFileSync(dest2, generateDrawioXml(buildCanvasModel()), "utf-8");
|
|
24426
24596
|
logger.success(`draw.io diagram written to ${dest2}`);
|
|
24427
24597
|
logger.info("Open with draw.io / diagrams.net (or import into tools that accept the format).");
|
|
24428
24598
|
return;
|
|
24429
24599
|
}
|
|
24430
24600
|
if (options.excalidraw && !options.all) {
|
|
24431
|
-
const dest2 = options.out ??
|
|
24432
|
-
ensureDir(
|
|
24433
|
-
|
|
24601
|
+
const dest2 = options.out ?? path31.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
|
|
24602
|
+
ensureDir(path31.dirname(path31.resolve(dest2)));
|
|
24603
|
+
fs22.writeFileSync(dest2, generateExcalidrawScene(buildCanvasModel()), "utf-8");
|
|
24434
24604
|
logger.success(`Excalidraw scene written to ${dest2}`);
|
|
24435
24605
|
logger.info("Open with excalidraw.com or the VS Code extension.");
|
|
24436
24606
|
return;
|
|
24437
24607
|
}
|
|
24438
24608
|
const wantsMermaid = options.format?.toLowerCase().startsWith("mermaid") || !!options.subsystem || !!options.sequence;
|
|
24439
24609
|
if (!options.all && !options.sequence && !wantsMermaid) {
|
|
24440
|
-
const dest2 = options.out ??
|
|
24610
|
+
const dest2 = options.out ?? path31.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
|
|
24441
24611
|
writeCanvas(dest2);
|
|
24442
24612
|
logger.success(`Interactive canvas written to ${dest2}`);
|
|
24443
24613
|
logger.info("Open it in a browser \u2014 fully self-contained (works offline). Other formats: --format mermaid|drawio|excalidraw.");
|
|
24444
24614
|
return;
|
|
24445
24615
|
}
|
|
24446
24616
|
if (options.all) {
|
|
24447
|
-
const outDir = options.out ??
|
|
24617
|
+
const outDir = options.out ?? path31.join(AI_PATHS.docsDir(), "diagrams");
|
|
24448
24618
|
const files = generateDiagramSet();
|
|
24449
24619
|
if (files.length === 0) {
|
|
24450
24620
|
logger.warn("No diagrams to generate \u2014 the spec tree has no components yet.");
|
|
24451
24621
|
return;
|
|
24452
24622
|
}
|
|
24453
24623
|
for (const file of files) {
|
|
24454
|
-
const dest2 =
|
|
24455
|
-
ensureDir(
|
|
24456
|
-
|
|
24624
|
+
const dest2 = path31.join(outDir, file.relPath);
|
|
24625
|
+
ensureDir(path31.dirname(dest2));
|
|
24626
|
+
fs22.writeFileSync(dest2, toMarkdown(file), "utf-8");
|
|
24457
24627
|
}
|
|
24458
|
-
writeCanvas(
|
|
24628
|
+
writeCanvas(path31.join(outDir, "canvas.html"));
|
|
24459
24629
|
const exportModel = buildCanvasModel();
|
|
24460
|
-
|
|
24461
|
-
|
|
24630
|
+
fs22.writeFileSync(path31.join(outDir, "architecture.drawio"), generateDrawioXml(exportModel), "utf-8");
|
|
24631
|
+
fs22.writeFileSync(path31.join(outDir, "architecture.excalidraw"), generateExcalidrawScene(exportModel), "utf-8");
|
|
24462
24632
|
const graph = loadSpecGraph();
|
|
24463
|
-
const indexPath =
|
|
24464
|
-
|
|
24633
|
+
const indexPath = path31.join(outDir, "README.md");
|
|
24634
|
+
fs22.writeFileSync(indexPath, diagramSetIndex(files, graph.systemName), "utf-8");
|
|
24465
24635
|
logger.success(`Generated ${files.length} diagram(s) + interactive canvas.html + index into ${outDir}`);
|
|
24466
24636
|
for (const file of files.slice(0, 12)) {
|
|
24467
24637
|
logger.info(` ${file.relPath}`);
|
|
@@ -24472,26 +24642,26 @@ async function runDiagram(rawOptions = {}) {
|
|
|
24472
24642
|
let mermaid;
|
|
24473
24643
|
let title;
|
|
24474
24644
|
let defaultDest;
|
|
24475
|
-
const diagramsDir =
|
|
24645
|
+
const diagramsDir = path31.join(AI_PATHS.docsDir(), "diagrams");
|
|
24476
24646
|
if (options.sequence) {
|
|
24477
24647
|
const { component, method: method2 } = parseSequenceRef(options.sequence);
|
|
24478
24648
|
mermaid = generateSequenceDiagram(component, method2, { depth: options.depth });
|
|
24479
24649
|
title = `${component}.${method2} \u2014 narrative sequence`;
|
|
24480
|
-
defaultDest =
|
|
24650
|
+
defaultDest = path31.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
|
|
24481
24651
|
} else if (options.subsystem) {
|
|
24482
24652
|
mermaid = generateComponentDiagram({ subsystem: options.subsystem });
|
|
24483
24653
|
title = `${options.subsystem} \u2014 components`;
|
|
24484
|
-
defaultDest =
|
|
24654
|
+
defaultDest = path31.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
|
|
24485
24655
|
} else {
|
|
24486
24656
|
mermaid = generateComponentDiagram();
|
|
24487
24657
|
title = "Component architecture";
|
|
24488
|
-
defaultDest =
|
|
24658
|
+
defaultDest = path31.join(diagramsDir, "system.md");
|
|
24489
24659
|
}
|
|
24490
24660
|
const dest = options.out ?? defaultDest;
|
|
24491
|
-
ensureDir(
|
|
24661
|
+
ensureDir(path31.dirname(path31.resolve(dest)));
|
|
24492
24662
|
const content = dest.endsWith(".mmd") ? `${mermaid}
|
|
24493
24663
|
` : toMarkdown({ relPath: dest, title, mermaid });
|
|
24494
|
-
|
|
24664
|
+
fs22.writeFileSync(dest, content, "utf-8");
|
|
24495
24665
|
logger.success(`Mermaid diagram written to ${dest}`);
|
|
24496
24666
|
logger.info("Renders on GitHub/IDE previews; use a .mmd --out path for raw Mermaid.");
|
|
24497
24667
|
}
|
|
@@ -24600,8 +24770,8 @@ Component variants (${variants.length})
|
|
|
24600
24770
|
}
|
|
24601
24771
|
|
|
24602
24772
|
// src/commands/packs.ts
|
|
24603
|
-
var
|
|
24604
|
-
var
|
|
24773
|
+
var fs23 = __toESM(require("fs"));
|
|
24774
|
+
var path32 = __toESM(require("path"));
|
|
24605
24775
|
var import_chalk16 = __toESM(require("chalk"));
|
|
24606
24776
|
var import_sdk = __toESM(require_dist());
|
|
24607
24777
|
init_logger();
|
|
@@ -24627,11 +24797,11 @@ function describe(probe2) {
|
|
|
24627
24797
|
return parts.join(", ");
|
|
24628
24798
|
}
|
|
24629
24799
|
function resolveSourceUnit(source) {
|
|
24630
|
-
const abs =
|
|
24631
|
-
if (!
|
|
24800
|
+
const abs = path32.resolve(source);
|
|
24801
|
+
if (!fs23.existsSync(abs)) {
|
|
24632
24802
|
throw new Error(`Pack source "${source}" does not exist.`);
|
|
24633
24803
|
}
|
|
24634
|
-
const isDir =
|
|
24804
|
+
const isDir = fs23.statSync(abs).isDirectory();
|
|
24635
24805
|
if (isDir && !packDirEntry(abs)) {
|
|
24636
24806
|
throw new Error(`"${source}" is a directory without a pack entry file (pack.yaml | pack.cjs | index.cjs | ...).`);
|
|
24637
24807
|
}
|
|
@@ -24647,7 +24817,7 @@ async function addPack(source, options = {}) {
|
|
|
24647
24817
|
}
|
|
24648
24818
|
const { abs } = resolveSourceUnit(source);
|
|
24649
24819
|
const scope = options.global ? "global" : "project";
|
|
24650
|
-
const probe2 = probePack(abs,
|
|
24820
|
+
const probe2 = probePack(abs, path32.dirname(abs), scope);
|
|
24651
24821
|
if (probe2.error) {
|
|
24652
24822
|
logger.error(probe2.error);
|
|
24653
24823
|
process.exitCode = 1;
|
|
@@ -24655,10 +24825,10 @@ async function addPack(source, options = {}) {
|
|
|
24655
24825
|
}
|
|
24656
24826
|
if (options.global) {
|
|
24657
24827
|
const destDir = globalPacksDir();
|
|
24658
|
-
const dest2 =
|
|
24659
|
-
if (
|
|
24660
|
-
|
|
24661
|
-
|
|
24828
|
+
const dest2 = path32.join(destDir, path32.basename(abs));
|
|
24829
|
+
if (path32.resolve(dest2) !== abs) {
|
|
24830
|
+
fs23.mkdirSync(destDir, { recursive: true });
|
|
24831
|
+
fs23.cpSync(abs, dest2, { recursive: true, force: true });
|
|
24662
24832
|
}
|
|
24663
24833
|
logger.success(`Installed pack "${probe2.name}" globally: ${dest2}`);
|
|
24664
24834
|
logger.info(`${describe(probe2)} \u2014 auto-loaded for every project on this machine (WAIRON_PACKS_DIR / ~/.wairon/packs).`);
|
|
@@ -24671,11 +24841,11 @@ async function addPack(source, options = {}) {
|
|
|
24671
24841
|
return;
|
|
24672
24842
|
}
|
|
24673
24843
|
const root = getProjectRoot();
|
|
24674
|
-
const relRef = `.wai/packs/${
|
|
24675
|
-
const dest =
|
|
24676
|
-
if (
|
|
24677
|
-
|
|
24678
|
-
|
|
24844
|
+
const relRef = `.wai/packs/${path32.basename(abs)}`;
|
|
24845
|
+
const dest = path32.join(root, ".wai", "packs", path32.basename(abs));
|
|
24846
|
+
if (path32.resolve(dest) !== abs) {
|
|
24847
|
+
fs23.mkdirSync(path32.dirname(dest), { recursive: true });
|
|
24848
|
+
fs23.cpSync(abs, dest, { recursive: true, force: true });
|
|
24679
24849
|
}
|
|
24680
24850
|
const config = loadProjectConfig();
|
|
24681
24851
|
const packs = config.extensions?.packs ?? [];
|
|
@@ -24684,14 +24854,14 @@ async function addPack(source, options = {}) {
|
|
|
24684
24854
|
saveProjectConfig(config);
|
|
24685
24855
|
logger.success(`Vendored pack "${probe2.name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
24686
24856
|
} else {
|
|
24687
|
-
|
|
24857
|
+
fs23.cpSync(abs, dest, { recursive: true, force: true });
|
|
24688
24858
|
logger.success(`Pack "${probe2.name}" already registered \u2014 refreshed ${relRef} from the source.`);
|
|
24689
24859
|
}
|
|
24690
24860
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
24691
24861
|
}
|
|
24692
24862
|
async function addPackFromArchive(source, options) {
|
|
24693
|
-
const abs =
|
|
24694
|
-
if (!
|
|
24863
|
+
const abs = path32.resolve(source);
|
|
24864
|
+
if (!fs23.existsSync(abs) || !fs23.statSync(abs).isFile()) {
|
|
24695
24865
|
logger.error(`Pack archive "${source}" does not exist.`);
|
|
24696
24866
|
process.exitCode = 1;
|
|
24697
24867
|
return;
|
|
@@ -24706,27 +24876,27 @@ async function addPackFromArchive(source, options) {
|
|
|
24706
24876
|
process.exitCode = 1;
|
|
24707
24877
|
return;
|
|
24708
24878
|
}
|
|
24709
|
-
baseDir =
|
|
24879
|
+
baseDir = path32.join(getProjectRoot(), ".wai", "packs");
|
|
24710
24880
|
}
|
|
24711
|
-
const bytes =
|
|
24712
|
-
|
|
24713
|
-
const staging =
|
|
24881
|
+
const bytes = fs23.readFileSync(abs);
|
|
24882
|
+
fs23.mkdirSync(baseDir, { recursive: true });
|
|
24883
|
+
const staging = fs23.mkdtempSync(path32.join(baseDir, ".wpack-staging-"));
|
|
24714
24884
|
let result;
|
|
24715
24885
|
try {
|
|
24716
24886
|
result = (0, import_sdk.extractPack)(bytes, staging);
|
|
24717
24887
|
} catch (err) {
|
|
24718
|
-
|
|
24719
|
-
logger.error(`Failed to extract pack archive "${
|
|
24888
|
+
fs23.rmSync(staging, { recursive: true, force: true });
|
|
24889
|
+
logger.error(`Failed to extract pack archive "${path32.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
|
|
24720
24890
|
process.exitCode = 1;
|
|
24721
24891
|
return;
|
|
24722
24892
|
}
|
|
24723
24893
|
const name = result.name;
|
|
24724
|
-
const destDir =
|
|
24725
|
-
if (
|
|
24726
|
-
|
|
24727
|
-
const probe2 = probePack(destDir,
|
|
24894
|
+
const destDir = path32.join(baseDir, name);
|
|
24895
|
+
if (fs23.existsSync(destDir)) fs23.rmSync(destDir, { recursive: true, force: true });
|
|
24896
|
+
fs23.renameSync(staging, destDir);
|
|
24897
|
+
const probe2 = probePack(destDir, path32.dirname(destDir), scope);
|
|
24728
24898
|
if (probe2.error) {
|
|
24729
|
-
|
|
24899
|
+
fs23.rmSync(destDir, { recursive: true, force: true });
|
|
24730
24900
|
logger.error(probe2.error);
|
|
24731
24901
|
process.exitCode = 1;
|
|
24732
24902
|
return;
|
|
@@ -24744,7 +24914,7 @@ async function addPackFromArchive(source, options) {
|
|
|
24744
24914
|
saveProjectConfig(config);
|
|
24745
24915
|
logger.success(`Installed pack "${probe2.name ?? name}" into ${relRef} and registered it in .wai/project.yaml.`);
|
|
24746
24916
|
} else {
|
|
24747
|
-
logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${
|
|
24917
|
+
logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path32.basename(abs)}.`);
|
|
24748
24918
|
}
|
|
24749
24919
|
logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
|
|
24750
24920
|
}
|
|
@@ -24765,7 +24935,7 @@ async function buildPack(source, options = {}) {
|
|
|
24765
24935
|
const sourceDir = source && source.length > 0 ? source : ".";
|
|
24766
24936
|
const result = (0, import_sdk.buildPack)(sourceDir);
|
|
24767
24937
|
const outPath = options.out ?? result.suggestedFileName;
|
|
24768
|
-
|
|
24938
|
+
fs23.writeFileSync(outPath, result.archive);
|
|
24769
24939
|
logger.success(`Built pack "${result.info.name}" v${result.info.version} \u2192 ${outPath} (${result.archive.byteLength} bytes)`);
|
|
24770
24940
|
logger.info(`Install it with \`wairon pack add ${outPath}\`, or upload it to a hosted instance.`);
|
|
24771
24941
|
}
|
|
@@ -24779,9 +24949,9 @@ async function listPacks() {
|
|
|
24779
24949
|
console.log(import_chalk16.default.bold.cyan(`\u25A0 Global (${globalPacksDir()})${useGlobal ? "" : import_chalk16.default.yellow(" [disabled: extensions.useGlobalPacks: false]")}`));
|
|
24780
24950
|
if (globalRefs.length === 0) console.log(import_chalk16.default.dim(" (none)"));
|
|
24781
24951
|
for (const ref of globalRefs) {
|
|
24782
|
-
const probe2 = probePack(ref,
|
|
24783
|
-
if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${
|
|
24784
|
-
else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ??
|
|
24952
|
+
const probe2 = probePack(ref, path32.dirname(ref), "global");
|
|
24953
|
+
if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path32.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
|
|
24954
|
+
else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path32.basename(ref))} ${import_chalk16.default.dim(describe(probe2))}`);
|
|
24785
24955
|
}
|
|
24786
24956
|
console.log("");
|
|
24787
24957
|
if (!inProject) {
|
|
@@ -24802,9 +24972,9 @@ async function listPacks() {
|
|
|
24802
24972
|
async function removePack(name, options = {}) {
|
|
24803
24973
|
if (options.global) {
|
|
24804
24974
|
for (const ref of discoverPacks(globalPacksDir())) {
|
|
24805
|
-
const probe2 = probePack(ref,
|
|
24806
|
-
if (probe2.name === name ||
|
|
24807
|
-
|
|
24975
|
+
const probe2 = probePack(ref, path32.dirname(ref), "global");
|
|
24976
|
+
if (probe2.name === name || path32.basename(ref) === name) {
|
|
24977
|
+
fs23.rmSync(ref, { recursive: true, force: true });
|
|
24808
24978
|
logger.success(`Removed global pack "${probe2.name ?? name}" (${ref}).`);
|
|
24809
24979
|
return;
|
|
24810
24980
|
}
|
|
@@ -24823,16 +24993,16 @@ async function removePack(name, options = {}) {
|
|
|
24823
24993
|
const packs = config.extensions?.packs ?? [];
|
|
24824
24994
|
for (const ref of packs) {
|
|
24825
24995
|
const probe2 = probePack(ref, root, "project");
|
|
24826
|
-
if (probe2.name === name || ref === name ||
|
|
24996
|
+
if (probe2.name === name || ref === name || path32.basename(ref) === name) {
|
|
24827
24997
|
config.extensions = {
|
|
24828
24998
|
packs: packs.filter((p) => p !== ref),
|
|
24829
24999
|
useGlobalPacks: config.extensions?.useGlobalPacks ?? true
|
|
24830
25000
|
};
|
|
24831
25001
|
saveProjectConfig(config);
|
|
24832
|
-
const resolved =
|
|
24833
|
-
const vendorDir =
|
|
24834
|
-
if (resolved.startsWith(vendorDir +
|
|
24835
|
-
|
|
25002
|
+
const resolved = path32.resolve(root, ref);
|
|
25003
|
+
const vendorDir = path32.resolve(root, ".wai", "packs");
|
|
25004
|
+
if (resolved.startsWith(vendorDir + path32.sep)) {
|
|
25005
|
+
fs23.rmSync(resolved, { recursive: true, force: true });
|
|
24836
25006
|
logger.success(`Deregistered pack "${probe2.name ?? name}" and deleted ${ref}.`);
|
|
24837
25007
|
} else {
|
|
24838
25008
|
logger.success(`Deregistered pack "${probe2.name ?? name}" (files at ${ref} left in place).`);
|
|
@@ -24846,8 +25016,8 @@ async function removePack(name, options = {}) {
|
|
|
24846
25016
|
|
|
24847
25017
|
// src/commands/host.ts
|
|
24848
25018
|
var fs50 = __toESM(require("fs"));
|
|
24849
|
-
var
|
|
24850
|
-
var
|
|
25019
|
+
var path59 = __toESM(require("path"));
|
|
25020
|
+
var os10 = __toESM(require("os"));
|
|
24851
25021
|
var crypto20 = __toESM(require("crypto"));
|
|
24852
25022
|
var import_child_process5 = require("child_process");
|
|
24853
25023
|
var import_chalk17 = __toESM(require("chalk"));
|
|
@@ -24874,29 +25044,29 @@ var UNAUTHENTICATED = {
|
|
|
24874
25044
|
var WEB_SESSION_PREFIX = "ws_";
|
|
24875
25045
|
|
|
24876
25046
|
// src/server/credentials.ts
|
|
24877
|
-
var
|
|
24878
|
-
var
|
|
25047
|
+
var fs24 = __toESM(require("fs"));
|
|
25048
|
+
var path33 = __toESM(require("path"));
|
|
24879
25049
|
var crypto3 = __toESM(require("crypto"));
|
|
24880
25050
|
var HASH_NS = "wairon:token:v1";
|
|
24881
25051
|
function hashToken(token) {
|
|
24882
25052
|
return crypto3.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
|
|
24883
25053
|
}
|
|
24884
25054
|
function storePath(dataDir) {
|
|
24885
|
-
return
|
|
25055
|
+
return path33.join(dataDir, "auth", "credentials.json");
|
|
24886
25056
|
}
|
|
24887
25057
|
function load3(dataDir) {
|
|
24888
25058
|
try {
|
|
24889
|
-
return JSON.parse(
|
|
25059
|
+
return JSON.parse(fs24.readFileSync(storePath(dataDir), "utf8"));
|
|
24890
25060
|
} catch {
|
|
24891
25061
|
return [];
|
|
24892
25062
|
}
|
|
24893
25063
|
}
|
|
24894
25064
|
function save(dataDir, records) {
|
|
24895
25065
|
const p = storePath(dataDir);
|
|
24896
|
-
|
|
25066
|
+
fs24.mkdirSync(path33.dirname(p), { recursive: true });
|
|
24897
25067
|
const tmp = `${p}.tmp`;
|
|
24898
|
-
|
|
24899
|
-
|
|
25068
|
+
fs24.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
25069
|
+
fs24.renameSync(tmp, p);
|
|
24900
25070
|
}
|
|
24901
25071
|
function digestEquals(a, b) {
|
|
24902
25072
|
const ab = Buffer.from(a, "hex");
|
|
@@ -24941,17 +25111,17 @@ function listByOwner(dataDir, ownerUserId) {
|
|
|
24941
25111
|
}
|
|
24942
25112
|
|
|
24943
25113
|
// src/server/websessions.ts
|
|
24944
|
-
var
|
|
24945
|
-
var
|
|
25114
|
+
var fs25 = __toESM(require("fs"));
|
|
25115
|
+
var path34 = __toESM(require("path"));
|
|
24946
25116
|
var crypto4 = __toESM(require("crypto"));
|
|
24947
25117
|
function storePath2(dataDir) {
|
|
24948
|
-
return
|
|
25118
|
+
return path34.join(dataDir, "web-sessions.json");
|
|
24949
25119
|
}
|
|
24950
25120
|
function readSessions(dataDir) {
|
|
24951
25121
|
const p = storePath2(dataDir);
|
|
24952
25122
|
let raw;
|
|
24953
25123
|
try {
|
|
24954
|
-
raw =
|
|
25124
|
+
raw = fs25.readFileSync(p, "utf8");
|
|
24955
25125
|
} catch (e) {
|
|
24956
25126
|
if (e.code === "ENOENT") return [];
|
|
24957
25127
|
throw new Error(`Failed to read web session store at ${p}: ${e.message}`);
|
|
@@ -24966,10 +25136,10 @@ function readSessions(dataDir) {
|
|
|
24966
25136
|
}
|
|
24967
25137
|
function persistSessions(dataDir, sessions) {
|
|
24968
25138
|
const p = storePath2(dataDir);
|
|
24969
|
-
|
|
25139
|
+
fs25.mkdirSync(path34.dirname(p), { recursive: true });
|
|
24970
25140
|
const tmp = `${p}.tmp`;
|
|
24971
|
-
|
|
24972
|
-
|
|
25141
|
+
fs25.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
|
|
25142
|
+
fs25.renameSync(tmp, p);
|
|
24973
25143
|
}
|
|
24974
25144
|
function mintSessionId() {
|
|
24975
25145
|
return `${WEB_SESSION_PREFIX}${crypto4.randomBytes(24).toString("hex")}`;
|
|
@@ -25130,17 +25300,17 @@ function listWebSessionsBySubject(dataDir, userId) {
|
|
|
25130
25300
|
}
|
|
25131
25301
|
|
|
25132
25302
|
// src/server/users.ts
|
|
25133
|
-
var
|
|
25134
|
-
var
|
|
25303
|
+
var fs26 = __toESM(require("fs"));
|
|
25304
|
+
var path35 = __toESM(require("path"));
|
|
25135
25305
|
var VALID_STATUSES = ["active", "inactive", "suspended", "deactivated", "disabled"];
|
|
25136
25306
|
function storePath3(dataDir) {
|
|
25137
|
-
return
|
|
25307
|
+
return path35.join(dataDir, "users.json");
|
|
25138
25308
|
}
|
|
25139
25309
|
function loadStore(dataDir) {
|
|
25140
25310
|
const p = storePath3(dataDir);
|
|
25141
25311
|
let raw;
|
|
25142
25312
|
try {
|
|
25143
|
-
raw =
|
|
25313
|
+
raw = fs26.readFileSync(p, "utf8");
|
|
25144
25314
|
} catch (err) {
|
|
25145
25315
|
if (err.code === "ENOENT") return [];
|
|
25146
25316
|
throw new Error(`Cannot read hosted-user store at ${p}: ${err.message}`);
|
|
@@ -25158,10 +25328,10 @@ function loadStore(dataDir) {
|
|
|
25158
25328
|
}
|
|
25159
25329
|
function replaceAll(dataDir, records) {
|
|
25160
25330
|
const p = storePath3(dataDir);
|
|
25161
|
-
|
|
25331
|
+
fs26.mkdirSync(path35.dirname(p), { recursive: true });
|
|
25162
25332
|
const tmp = `${p}.tmp`;
|
|
25163
|
-
|
|
25164
|
-
|
|
25333
|
+
fs26.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
25334
|
+
fs26.renameSync(tmp, p);
|
|
25165
25335
|
}
|
|
25166
25336
|
function registryUpsert(dataDir, record2) {
|
|
25167
25337
|
const records = loadStore(dataDir);
|
|
@@ -25263,11 +25433,11 @@ function remapUnitReferences(dataDir, remap, removedScopeIds) {
|
|
|
25263
25433
|
}
|
|
25264
25434
|
|
|
25265
25435
|
// src/server/instance.ts
|
|
25266
|
-
var
|
|
25267
|
-
var
|
|
25436
|
+
var fs27 = __toESM(require("fs"));
|
|
25437
|
+
var path36 = __toESM(require("path"));
|
|
25268
25438
|
var import_crypto = require("crypto");
|
|
25269
25439
|
function storePath4(dataDir) {
|
|
25270
|
-
return
|
|
25440
|
+
return path36.join(dataDir, "instance.json");
|
|
25271
25441
|
}
|
|
25272
25442
|
var InstanceIdentityStore = class {
|
|
25273
25443
|
constructor(dataDir) {
|
|
@@ -25284,7 +25454,7 @@ var InstanceIdentityStore = class {
|
|
|
25284
25454
|
const p = storePath4(this.dataDir);
|
|
25285
25455
|
let raw;
|
|
25286
25456
|
try {
|
|
25287
|
-
raw =
|
|
25457
|
+
raw = fs27.readFileSync(p, "utf8");
|
|
25288
25458
|
} catch (err) {
|
|
25289
25459
|
if (err.code === "ENOENT") return null;
|
|
25290
25460
|
throw new Error(`Cannot read instance identity at ${p}: ${err.message}`);
|
|
@@ -25307,10 +25477,10 @@ var InstanceIdentityStore = class {
|
|
|
25307
25477
|
* never truncates the file. Only called by the registry's create-once seed. */
|
|
25308
25478
|
replace(identity) {
|
|
25309
25479
|
const p = storePath4(this.dataDir);
|
|
25310
|
-
|
|
25480
|
+
fs27.mkdirSync(path36.dirname(p), { recursive: true });
|
|
25311
25481
|
const tmp = `${p}.tmp`;
|
|
25312
|
-
|
|
25313
|
-
|
|
25482
|
+
fs27.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
|
|
25483
|
+
fs27.renameSync(tmp, p);
|
|
25314
25484
|
}
|
|
25315
25485
|
};
|
|
25316
25486
|
var InstanceIdentityRegistry = class {
|
|
@@ -25366,8 +25536,8 @@ function getInstanceIdentity(dataDir) {
|
|
|
25366
25536
|
}
|
|
25367
25537
|
|
|
25368
25538
|
// src/utils/secrets.ts
|
|
25369
|
-
var
|
|
25370
|
-
var
|
|
25539
|
+
var fs28 = __toESM(require("fs"));
|
|
25540
|
+
var path37 = __toESM(require("path"));
|
|
25371
25541
|
var ENV_FALLBACK = {
|
|
25372
25542
|
"git-token": ["WAIRON_GIT_TOKEN"],
|
|
25373
25543
|
"notion-token": ["WAIRON_NOTION_TOKEN"],
|
|
@@ -25376,13 +25546,13 @@ var ENV_FALLBACK = {
|
|
|
25376
25546
|
};
|
|
25377
25547
|
function storePath5() {
|
|
25378
25548
|
const dataDir = process.env["WAIRON_DATA_DIR"];
|
|
25379
|
-
return dataDir ?
|
|
25549
|
+
return dataDir ? path37.join(dataDir, "auth", "secrets.json") : null;
|
|
25380
25550
|
}
|
|
25381
25551
|
function readStore() {
|
|
25382
25552
|
const p = storePath5();
|
|
25383
25553
|
if (!p) return {};
|
|
25384
25554
|
try {
|
|
25385
|
-
return JSON.parse(
|
|
25555
|
+
return JSON.parse(fs28.readFileSync(p, "utf8"));
|
|
25386
25556
|
} catch {
|
|
25387
25557
|
return {};
|
|
25388
25558
|
}
|
|
@@ -25407,10 +25577,10 @@ function setSecret(key, value) {
|
|
|
25407
25577
|
if (!p) throw new Error("WAIRON_DATA_DIR is not set \u2014 a running server needs it to store secrets.");
|
|
25408
25578
|
const store = readStore();
|
|
25409
25579
|
store[key] = value;
|
|
25410
|
-
|
|
25580
|
+
fs28.mkdirSync(path37.dirname(p), { recursive: true });
|
|
25411
25581
|
const tmp = `${p}.tmp`;
|
|
25412
|
-
|
|
25413
|
-
|
|
25582
|
+
fs28.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
|
|
25583
|
+
fs28.renameSync(tmp, p);
|
|
25414
25584
|
}
|
|
25415
25585
|
function listSecretKeys() {
|
|
25416
25586
|
return Object.keys(readStore());
|
|
@@ -25618,17 +25788,17 @@ function verifySsoState(state) {
|
|
|
25618
25788
|
}
|
|
25619
25789
|
|
|
25620
25790
|
// src/server/organization.ts
|
|
25621
|
-
var
|
|
25622
|
-
var
|
|
25791
|
+
var fs29 = __toESM(require("fs"));
|
|
25792
|
+
var path38 = __toESM(require("path"));
|
|
25623
25793
|
var crypto6 = __toESM(require("crypto"));
|
|
25624
25794
|
function storePath6(dataDir) {
|
|
25625
|
-
return
|
|
25795
|
+
return path38.join(dataDir, "organization.json");
|
|
25626
25796
|
}
|
|
25627
25797
|
function readState(dataDir) {
|
|
25628
25798
|
const p = storePath6(dataDir);
|
|
25629
25799
|
let raw;
|
|
25630
25800
|
try {
|
|
25631
|
-
raw =
|
|
25801
|
+
raw = fs29.readFileSync(p, "utf8");
|
|
25632
25802
|
} catch (e) {
|
|
25633
25803
|
if (e.code === "ENOENT") return { units: [], placements: [] };
|
|
25634
25804
|
throw new Error(`Failed to read organization store at ${p}: ${e.message}`);
|
|
@@ -25645,10 +25815,10 @@ function readState(dataDir) {
|
|
|
25645
25815
|
}
|
|
25646
25816
|
function persistState(dataDir, state) {
|
|
25647
25817
|
const p = storePath6(dataDir);
|
|
25648
|
-
|
|
25818
|
+
fs29.mkdirSync(path38.dirname(p), { recursive: true });
|
|
25649
25819
|
const tmp = `${p}.tmp`;
|
|
25650
|
-
|
|
25651
|
-
|
|
25820
|
+
fs29.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
|
|
25821
|
+
fs29.renameSync(tmp, p);
|
|
25652
25822
|
}
|
|
25653
25823
|
var SLUG_PATTERN = /^[a-z0-9-]+$/;
|
|
25654
25824
|
var UNIT_KINDS = ["business_entity", "department", "team", "group"];
|
|
@@ -26017,11 +26187,11 @@ function getOrganizationUnit(dataDir, id) {
|
|
|
26017
26187
|
}
|
|
26018
26188
|
|
|
26019
26189
|
// src/server/permissions.ts
|
|
26020
|
-
var
|
|
26021
|
-
var
|
|
26190
|
+
var fs30 = __toESM(require("fs"));
|
|
26191
|
+
var path39 = __toESM(require("path"));
|
|
26022
26192
|
var import_crypto2 = require("crypto");
|
|
26023
26193
|
function storePath7(dataDir) {
|
|
26024
|
-
return
|
|
26194
|
+
return path39.join(dataDir, "permissions.json");
|
|
26025
26195
|
}
|
|
26026
26196
|
function assignmentKey(a) {
|
|
26027
26197
|
return [a.subjectKind, a.subjectId ?? "", a.scopeKind, a.scopeId ?? "", a.capability].join("|");
|
|
@@ -26030,7 +26200,7 @@ function load4(dataDir) {
|
|
|
26030
26200
|
const p = storePath7(dataDir);
|
|
26031
26201
|
let raw;
|
|
26032
26202
|
try {
|
|
26033
|
-
raw =
|
|
26203
|
+
raw = fs30.readFileSync(p, "utf8");
|
|
26034
26204
|
} catch (err) {
|
|
26035
26205
|
if (err.code === "ENOENT") return [];
|
|
26036
26206
|
throw new Error(`Cannot read permission store at ${p}: ${err.message}`);
|
|
@@ -26048,10 +26218,10 @@ function load4(dataDir) {
|
|
|
26048
26218
|
}
|
|
26049
26219
|
function replaceAll2(dataDir, assignments) {
|
|
26050
26220
|
const p = storePath7(dataDir);
|
|
26051
|
-
|
|
26221
|
+
fs30.mkdirSync(path39.dirname(p), { recursive: true });
|
|
26052
26222
|
const tmp = `${p}.tmp`;
|
|
26053
|
-
|
|
26054
|
-
|
|
26223
|
+
fs30.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
|
|
26224
|
+
fs30.renameSync(tmp, p);
|
|
26055
26225
|
}
|
|
26056
26226
|
function registrySet(dataDir, assignment) {
|
|
26057
26227
|
const assignments = load4(dataDir);
|
|
@@ -26137,8 +26307,8 @@ function getAssignment(dataDir, assignmentId) {
|
|
|
26137
26307
|
}
|
|
26138
26308
|
|
|
26139
26309
|
// src/server/roles.ts
|
|
26140
|
-
var
|
|
26141
|
-
var
|
|
26310
|
+
var fs31 = __toESM(require("fs"));
|
|
26311
|
+
var path40 = __toESM(require("path"));
|
|
26142
26312
|
var BUILTIN_ROLES = [
|
|
26143
26313
|
{
|
|
26144
26314
|
id: SSO_ADMIN_ROLE_ID,
|
|
@@ -26156,13 +26326,13 @@ function isBuiltinRoleId(roleId) {
|
|
|
26156
26326
|
return BUILTIN_ROLE_IDS.has(roleId);
|
|
26157
26327
|
}
|
|
26158
26328
|
function storePath8(dataDir) {
|
|
26159
|
-
return
|
|
26329
|
+
return path40.join(dataDir, "roles.json");
|
|
26160
26330
|
}
|
|
26161
26331
|
function load5(dataDir) {
|
|
26162
26332
|
const p = storePath8(dataDir);
|
|
26163
26333
|
let raw;
|
|
26164
26334
|
try {
|
|
26165
|
-
raw =
|
|
26335
|
+
raw = fs31.readFileSync(p, "utf8");
|
|
26166
26336
|
} catch (err) {
|
|
26167
26337
|
if (err.code === "ENOENT") return [];
|
|
26168
26338
|
throw new Error(`Cannot read role store at ${p}: ${err.message}`);
|
|
@@ -26180,10 +26350,10 @@ function load5(dataDir) {
|
|
|
26180
26350
|
}
|
|
26181
26351
|
function replaceAll3(dataDir, roles) {
|
|
26182
26352
|
const p = storePath8(dataDir);
|
|
26183
|
-
|
|
26353
|
+
fs31.mkdirSync(path40.dirname(p), { recursive: true });
|
|
26184
26354
|
const tmp = `${p}.tmp`;
|
|
26185
|
-
|
|
26186
|
-
|
|
26355
|
+
fs31.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
|
|
26356
|
+
fs31.renameSync(tmp, p);
|
|
26187
26357
|
}
|
|
26188
26358
|
function registryCreate(dataDir, role) {
|
|
26189
26359
|
if (isBuiltinRoleId(role.id)) {
|
|
@@ -26417,131 +26587,14 @@ function actionableUnitIds(scopes) {
|
|
|
26417
26587
|
}
|
|
26418
26588
|
|
|
26419
26589
|
// src/server/projects.ts
|
|
26420
|
-
var
|
|
26421
|
-
var
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
return typeof id === "string" && ID_RE.test(id);
|
|
26425
|
-
}
|
|
26426
|
-
function registryPath(dataDir) {
|
|
26427
|
-
return path40.join(dataDir, "projects.json");
|
|
26428
|
-
}
|
|
26429
|
-
function load6(dataDir) {
|
|
26430
|
-
try {
|
|
26431
|
-
return JSON.parse(fs31.readFileSync(registryPath(dataDir), "utf8"));
|
|
26432
|
-
} catch {
|
|
26433
|
-
return [];
|
|
26434
|
-
}
|
|
26435
|
-
}
|
|
26436
|
-
function save2(dataDir, records) {
|
|
26437
|
-
const p = registryPath(dataDir);
|
|
26438
|
-
fs31.mkdirSync(path40.dirname(p), { recursive: true });
|
|
26439
|
-
const tmp = `${p}.tmp`;
|
|
26440
|
-
fs31.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
26441
|
-
fs31.renameSync(tmp, p);
|
|
26442
|
-
}
|
|
26443
|
-
function projectRoot(dataDir, id) {
|
|
26444
|
-
return path40.join(dataDir, "projects", id);
|
|
26445
|
-
}
|
|
26446
|
-
function existingProjectRoot(dataDir, id) {
|
|
26447
|
-
if (!isValidProjectId(id)) return null;
|
|
26448
|
-
const rec = load6(dataDir).find((r) => r.id === id);
|
|
26449
|
-
return rec ? rec.rootPath : null;
|
|
26450
|
-
}
|
|
26451
|
-
function createProjectRecord(dataDir, id) {
|
|
26452
|
-
if (!isValidProjectId(id)) {
|
|
26453
|
-
throw new Error(`Invalid project id "${id}" (allowed: lowercase letters, digits, hyphen).`);
|
|
26454
|
-
}
|
|
26455
|
-
const records = load6(dataDir);
|
|
26456
|
-
if (records.some((r) => r.id === id)) {
|
|
26457
|
-
throw new Error(`Project "${id}" already exists.`);
|
|
26458
|
-
}
|
|
26459
|
-
const root = projectRoot(dataDir, id);
|
|
26460
|
-
fs31.mkdirSync(root, { recursive: true });
|
|
26461
|
-
const record2 = {
|
|
26462
|
-
id,
|
|
26463
|
-
rootPath: root,
|
|
26464
|
-
status: "active",
|
|
26465
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26466
|
-
};
|
|
26467
|
-
records.push(record2);
|
|
26468
|
-
save2(dataDir, records);
|
|
26469
|
-
return record2;
|
|
26470
|
-
}
|
|
26471
|
-
function registerLocalDevProject(dataDir, id, rootPath) {
|
|
26472
|
-
if (!isValidProjectId(id)) {
|
|
26473
|
-
throw new Error(`Invalid project id "${id}" (allowed: lowercase letters, digits, hyphen).`);
|
|
26474
|
-
}
|
|
26475
|
-
const records = load6(dataDir);
|
|
26476
|
-
const existing = records.find((r) => r.id === id);
|
|
26477
|
-
const record2 = {
|
|
26478
|
-
id,
|
|
26479
|
-
rootPath,
|
|
26480
|
-
status: "active",
|
|
26481
|
-
createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
26482
|
-
};
|
|
26483
|
-
const next = existing ? records.map((r) => r.id === id ? record2 : r) : [...records, record2];
|
|
26484
|
-
save2(dataDir, next);
|
|
26485
|
-
return record2;
|
|
26486
|
-
}
|
|
26487
|
-
function listProjectRecords(dataDir) {
|
|
26488
|
-
return load6(dataDir);
|
|
26489
|
-
}
|
|
26490
|
-
function removeProjectRecord(dataDir, id) {
|
|
26491
|
-
const records = load6(dataDir);
|
|
26492
|
-
const rec = records.find((r) => r.id === id);
|
|
26493
|
-
if (rec) {
|
|
26494
|
-
try {
|
|
26495
|
-
fs31.rmSync(rec.rootPath, { recursive: true, force: true });
|
|
26496
|
-
} catch {
|
|
26497
|
-
}
|
|
26498
|
-
}
|
|
26499
|
-
save2(dataDir, records.filter((r) => r.id !== id));
|
|
26500
|
-
}
|
|
26501
|
-
function resolveProjectRoot(dataDir, principal, selector) {
|
|
26502
|
-
const authorized = principal.projects;
|
|
26503
|
-
const wildcard = authorized.includes("*");
|
|
26504
|
-
let target;
|
|
26505
|
-
if (selector) {
|
|
26506
|
-
if (!wildcard && !authorized.includes(selector)) return null;
|
|
26507
|
-
target = selector;
|
|
26508
|
-
} else if (!wildcard && authorized.length === 1) {
|
|
26509
|
-
target = authorized[0];
|
|
26510
|
-
} else {
|
|
26511
|
-
return null;
|
|
26512
|
-
}
|
|
26513
|
-
if (!isValidProjectId(target)) return null;
|
|
26514
|
-
const rec = load6(dataDir).find((r) => r.id === target);
|
|
26515
|
-
if (!rec || rec.status !== "active") return null;
|
|
26516
|
-
return rec.rootPath;
|
|
26517
|
-
}
|
|
26518
|
-
|
|
26519
|
-
// src/server/adapters.ts
|
|
26520
|
-
init_statehash();
|
|
26521
|
-
|
|
26522
|
-
// src/core/lockfile.ts
|
|
26523
|
-
var fs32 = __toESM(require("fs"));
|
|
26524
|
-
var path41 = __toESM(require("path"));
|
|
26590
|
+
var fs35 = __toESM(require("fs"));
|
|
26591
|
+
var path44 = __toESM(require("path"));
|
|
26592
|
+
init_loader();
|
|
26593
|
+
init_yaml();
|
|
26525
26594
|
init_fs();
|
|
26526
|
-
function lockPath() {
|
|
26527
|
-
return aiDir("lock.json");
|
|
26528
|
-
}
|
|
26529
|
-
function readLockRecord() {
|
|
26530
|
-
try {
|
|
26531
|
-
return JSON.parse(fs32.readFileSync(lockPath(), "utf8"));
|
|
26532
|
-
} catch {
|
|
26533
|
-
return null;
|
|
26534
|
-
}
|
|
26535
|
-
}
|
|
26536
|
-
function writeLockRecord(record2) {
|
|
26537
|
-
const p = lockPath();
|
|
26538
|
-
fs32.mkdirSync(path41.dirname(p), { recursive: true });
|
|
26539
|
-
const tmp = `${p}.tmp`;
|
|
26540
|
-
fs32.writeFileSync(tmp, JSON.stringify(record2, null, 2) + "\n");
|
|
26541
|
-
fs32.renameSync(tmp, p);
|
|
26542
|
-
}
|
|
26543
26595
|
|
|
26544
26596
|
// src/server/adapters.ts
|
|
26597
|
+
init_statehash();
|
|
26545
26598
|
init_specs2();
|
|
26546
26599
|
init_provision();
|
|
26547
26600
|
init_validation();
|
|
@@ -26552,37 +26605,37 @@ init_types();
|
|
|
26552
26605
|
init_server();
|
|
26553
26606
|
|
|
26554
26607
|
// src/git/config.ts
|
|
26555
|
-
var
|
|
26556
|
-
var
|
|
26608
|
+
var fs32 = __toESM(require("fs"));
|
|
26609
|
+
var path41 = __toESM(require("path"));
|
|
26557
26610
|
init_fs();
|
|
26558
26611
|
function configPath() {
|
|
26559
26612
|
return aiDir("git.json");
|
|
26560
26613
|
}
|
|
26561
26614
|
function readGitConfig() {
|
|
26562
26615
|
try {
|
|
26563
|
-
return JSON.parse(
|
|
26616
|
+
return JSON.parse(fs32.readFileSync(configPath(), "utf8"));
|
|
26564
26617
|
} catch {
|
|
26565
26618
|
return null;
|
|
26566
26619
|
}
|
|
26567
26620
|
}
|
|
26568
26621
|
function writeGitConfig(config) {
|
|
26569
26622
|
const p = configPath();
|
|
26570
|
-
|
|
26623
|
+
fs32.mkdirSync(path41.dirname(p), { recursive: true });
|
|
26571
26624
|
const tmp = `${p}.tmp`;
|
|
26572
|
-
|
|
26573
|
-
|
|
26625
|
+
fs32.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
|
|
26626
|
+
fs32.renameSync(tmp, p);
|
|
26574
26627
|
}
|
|
26575
26628
|
function clearGitConfig() {
|
|
26576
26629
|
try {
|
|
26577
|
-
|
|
26630
|
+
fs32.rmSync(configPath(), { force: true });
|
|
26578
26631
|
} catch {
|
|
26579
26632
|
}
|
|
26580
26633
|
}
|
|
26581
26634
|
|
|
26582
26635
|
// src/git/adapter.ts
|
|
26583
26636
|
var import_child_process3 = require("child_process");
|
|
26584
|
-
var
|
|
26585
|
-
var
|
|
26637
|
+
var fs33 = __toESM(require("fs"));
|
|
26638
|
+
var path42 = __toESM(require("path"));
|
|
26586
26639
|
init_fs();
|
|
26587
26640
|
function git(args, cwd) {
|
|
26588
26641
|
return (0, import_child_process3.execFileSync)("git", args, {
|
|
@@ -26634,10 +26687,10 @@ function compareUrl(remote, defaultBranch, workingBranch) {
|
|
|
26634
26687
|
return `${web}/compare/${encodeURIComponent(defaultBranch)}...${encodeURIComponent(workingBranch)}`;
|
|
26635
26688
|
}
|
|
26636
26689
|
function excludeLocalFiles() {
|
|
26637
|
-
const excludePath =
|
|
26690
|
+
const excludePath = path42.join(getProjectRoot(), ".git", "info", "exclude");
|
|
26638
26691
|
try {
|
|
26639
|
-
|
|
26640
|
-
|
|
26692
|
+
fs33.mkdirSync(path42.dirname(excludePath), { recursive: true });
|
|
26693
|
+
fs33.appendFileSync(excludePath, "\n.wai/lock.json\n.wai/git.json\n");
|
|
26641
26694
|
} catch {
|
|
26642
26695
|
}
|
|
26643
26696
|
}
|
|
@@ -26702,39 +26755,39 @@ function configureSync(periodicSyncMinutes, skipIfClean) {
|
|
|
26702
26755
|
}
|
|
26703
26756
|
|
|
26704
26757
|
// src/producers/config.ts
|
|
26705
|
-
var
|
|
26706
|
-
var
|
|
26758
|
+
var fs34 = __toESM(require("fs"));
|
|
26759
|
+
var path43 = __toESM(require("path"));
|
|
26707
26760
|
init_fs();
|
|
26708
26761
|
function configPath2() {
|
|
26709
26762
|
return aiDir("producers.json");
|
|
26710
26763
|
}
|
|
26711
|
-
function
|
|
26764
|
+
function load6() {
|
|
26712
26765
|
try {
|
|
26713
|
-
return JSON.parse(
|
|
26766
|
+
return JSON.parse(fs34.readFileSync(configPath2(), "utf8"));
|
|
26714
26767
|
} catch {
|
|
26715
26768
|
return [];
|
|
26716
26769
|
}
|
|
26717
26770
|
}
|
|
26718
|
-
function
|
|
26771
|
+
function save2(configs) {
|
|
26719
26772
|
const p = configPath2();
|
|
26720
|
-
|
|
26773
|
+
fs34.mkdirSync(path43.dirname(p), { recursive: true });
|
|
26721
26774
|
const tmp = `${p}.tmp`;
|
|
26722
|
-
|
|
26723
|
-
|
|
26775
|
+
fs34.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
|
|
26776
|
+
fs34.renameSync(tmp, p);
|
|
26724
26777
|
}
|
|
26725
26778
|
function readProducerConfig(target) {
|
|
26726
|
-
return
|
|
26779
|
+
return load6().find((c) => c.target === target) ?? null;
|
|
26727
26780
|
}
|
|
26728
26781
|
function writeProducerConfig(config) {
|
|
26729
|
-
const configs =
|
|
26782
|
+
const configs = load6().filter((c) => c.target !== config.target);
|
|
26730
26783
|
configs.push(config);
|
|
26731
|
-
|
|
26784
|
+
save2(configs);
|
|
26732
26785
|
}
|
|
26733
26786
|
function clearProducerConfig(target) {
|
|
26734
|
-
|
|
26787
|
+
save2(load6().filter((c) => c.target !== target));
|
|
26735
26788
|
}
|
|
26736
26789
|
function listProducerConfigs() {
|
|
26737
|
-
return
|
|
26790
|
+
return load6();
|
|
26738
26791
|
}
|
|
26739
26792
|
|
|
26740
26793
|
// src/producers/core-adapter.ts
|
|
@@ -27075,6 +27128,9 @@ var hostCore = {
|
|
|
27075
27128
|
* read of a bundled constant. */
|
|
27076
27129
|
builtinProfileIds: () => [...BUILTIN_PROFILES]
|
|
27077
27130
|
};
|
|
27131
|
+
function resolveContainedProjectPath(projectRoot2, projectPath) {
|
|
27132
|
+
return assertContainedProjectPath(projectRoot2, projectPath);
|
|
27133
|
+
}
|
|
27078
27134
|
function validateProjectAsComplete() {
|
|
27079
27135
|
const config = loadProjectConfig();
|
|
27080
27136
|
return validateAsComplete({ rules: config.rules, projectType: config.projectType });
|
|
@@ -27106,6 +27162,180 @@ var hostSdk = {
|
|
|
27106
27162
|
extractArchive: (archive, destDir, limits) => sdkPortal.extractPack(archive, destDir, limits)
|
|
27107
27163
|
};
|
|
27108
27164
|
|
|
27165
|
+
// src/server/projects.ts
|
|
27166
|
+
var ID_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
27167
|
+
function isValidProjectId(id) {
|
|
27168
|
+
return typeof id === "string" && ID_RE.test(id);
|
|
27169
|
+
}
|
|
27170
|
+
function registryPath(dataDir) {
|
|
27171
|
+
return path44.join(dataDir, "projects.json");
|
|
27172
|
+
}
|
|
27173
|
+
function load7(dataDir) {
|
|
27174
|
+
try {
|
|
27175
|
+
return JSON.parse(fs35.readFileSync(registryPath(dataDir), "utf8"));
|
|
27176
|
+
} catch {
|
|
27177
|
+
return [];
|
|
27178
|
+
}
|
|
27179
|
+
}
|
|
27180
|
+
function save3(dataDir, records) {
|
|
27181
|
+
const p = registryPath(dataDir);
|
|
27182
|
+
fs35.mkdirSync(path44.dirname(p), { recursive: true });
|
|
27183
|
+
const tmp = `${p}.tmp`;
|
|
27184
|
+
fs35.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
|
|
27185
|
+
fs35.renameSync(tmp, p);
|
|
27186
|
+
}
|
|
27187
|
+
function projectRoot(dataDir, id) {
|
|
27188
|
+
return path44.join(dataDir, "projects", id);
|
|
27189
|
+
}
|
|
27190
|
+
function existingProjectRoot(dataDir, id) {
|
|
27191
|
+
if (!isValidProjectId(id)) return null;
|
|
27192
|
+
const rec = load7(dataDir).find((r) => r.id === id);
|
|
27193
|
+
return rec ? rec.rootPath : null;
|
|
27194
|
+
}
|
|
27195
|
+
function createProjectRecord(dataDir, id) {
|
|
27196
|
+
if (!isValidProjectId(id)) {
|
|
27197
|
+
throw new Error(`Invalid project id "${id}" (allowed: lowercase letters, digits, hyphen).`);
|
|
27198
|
+
}
|
|
27199
|
+
const records = load7(dataDir);
|
|
27200
|
+
if (records.some((r) => r.id === id)) {
|
|
27201
|
+
throw new Error(`Project "${id}" already exists.`);
|
|
27202
|
+
}
|
|
27203
|
+
const root = projectRoot(dataDir, id);
|
|
27204
|
+
fs35.mkdirSync(root, { recursive: true });
|
|
27205
|
+
const record2 = {
|
|
27206
|
+
id,
|
|
27207
|
+
rootPath: root,
|
|
27208
|
+
status: "active",
|
|
27209
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27210
|
+
};
|
|
27211
|
+
records.push(record2);
|
|
27212
|
+
save3(dataDir, records);
|
|
27213
|
+
return record2;
|
|
27214
|
+
}
|
|
27215
|
+
function registerLocalDevProject(dataDir, id, rootPath) {
|
|
27216
|
+
if (!isValidProjectId(id)) {
|
|
27217
|
+
throw new Error(`Invalid project id "${id}" (allowed: lowercase letters, digits, hyphen).`);
|
|
27218
|
+
}
|
|
27219
|
+
const records = load7(dataDir);
|
|
27220
|
+
const existing = records.find((r) => r.id === id);
|
|
27221
|
+
const record2 = {
|
|
27222
|
+
id,
|
|
27223
|
+
rootPath,
|
|
27224
|
+
status: "active",
|
|
27225
|
+
createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
27226
|
+
};
|
|
27227
|
+
const next = existing ? records.map((r) => r.id === id ? record2 : r) : [...records, record2];
|
|
27228
|
+
save3(dataDir, next);
|
|
27229
|
+
return record2;
|
|
27230
|
+
}
|
|
27231
|
+
function listProjectRecords(dataDir) {
|
|
27232
|
+
return load7(dataDir);
|
|
27233
|
+
}
|
|
27234
|
+
function removeProjectRecord(dataDir, id) {
|
|
27235
|
+
const records = load7(dataDir);
|
|
27236
|
+
const rec = records.find((r) => r.id === id);
|
|
27237
|
+
if (rec) {
|
|
27238
|
+
try {
|
|
27239
|
+
fs35.rmSync(rec.rootPath, { recursive: true, force: true });
|
|
27240
|
+
} catch {
|
|
27241
|
+
}
|
|
27242
|
+
}
|
|
27243
|
+
save3(dataDir, records.filter((r) => r.id !== id));
|
|
27244
|
+
}
|
|
27245
|
+
var SUBPROJECT_SEPARATOR = "::";
|
|
27246
|
+
function parseQualifiedSelector(value) {
|
|
27247
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
27248
|
+
const [projectId, ...mounts] = value.split(SUBPROJECT_SEPARATOR);
|
|
27249
|
+
if (!isValidProjectId(projectId)) return null;
|
|
27250
|
+
if (mounts.some((m) => m.trim() === "")) return null;
|
|
27251
|
+
return { projectId, mounts };
|
|
27252
|
+
}
|
|
27253
|
+
function findSubsystemSpec(root, subsystemId) {
|
|
27254
|
+
const specsDir = aiPathsAt(root).specsDir();
|
|
27255
|
+
if (!fs35.existsSync(specsDir)) return null;
|
|
27256
|
+
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
27257
|
+
let raw;
|
|
27258
|
+
try {
|
|
27259
|
+
raw = readYamlFile(file);
|
|
27260
|
+
} catch {
|
|
27261
|
+
continue;
|
|
27262
|
+
}
|
|
27263
|
+
if (!raw || typeof raw !== "object" || !("parentSystem" in raw)) continue;
|
|
27264
|
+
if (raw.id !== subsystemId) continue;
|
|
27265
|
+
const pp = raw.projectPath;
|
|
27266
|
+
return typeof pp === "string" && pp.trim() !== "" ? { projectPath: pp } : {};
|
|
27267
|
+
}
|
|
27268
|
+
return null;
|
|
27269
|
+
}
|
|
27270
|
+
function resolveSubprojectMounts(projectId, projectRoot2, mounts) {
|
|
27271
|
+
let root = projectRoot2;
|
|
27272
|
+
let at = projectId;
|
|
27273
|
+
for (const mount of mounts) {
|
|
27274
|
+
const sub = findSubsystemSpec(root, mount);
|
|
27275
|
+
if (!sub) {
|
|
27276
|
+
throw new Error(
|
|
27277
|
+
`unknown subproject mount "${mount}" on "${at}" \u2014 no subsystem with that id exists in its spec tree`
|
|
27278
|
+
);
|
|
27279
|
+
}
|
|
27280
|
+
if (!sub.projectPath) {
|
|
27281
|
+
throw new Error(
|
|
27282
|
+
`subsystem "${mount}" on "${at}" is not a chained subproject (it carries no projectPath) \u2014 only a subsystem mounted via projectPath can be bound as a subproject`
|
|
27283
|
+
);
|
|
27284
|
+
}
|
|
27285
|
+
root = resolveContainedProjectPath(root, sub.projectPath);
|
|
27286
|
+
at = `${at}${SUBPROJECT_SEPARATOR}${mount}`;
|
|
27287
|
+
}
|
|
27288
|
+
return root;
|
|
27289
|
+
}
|
|
27290
|
+
function assertMintableNarrowingEntry(dataDir, entry) {
|
|
27291
|
+
if (entry === "*") return;
|
|
27292
|
+
const parsed = parseQualifiedSelector(entry);
|
|
27293
|
+
if (!parsed) {
|
|
27294
|
+
throw new Error(
|
|
27295
|
+
`invalid project narrowing entry "${entry}" (expected a project id, optionally subproject-qualified as projectId::subsystemId)`
|
|
27296
|
+
);
|
|
27297
|
+
}
|
|
27298
|
+
const rec = load7(dataDir).find((r) => r.id === parsed.projectId);
|
|
27299
|
+
if (!rec) throw new Error(`unknown project "${parsed.projectId}"`);
|
|
27300
|
+
if (parsed.mounts.length > 0) {
|
|
27301
|
+
resolveSubprojectMounts(parsed.projectId, rec.rootPath, parsed.mounts);
|
|
27302
|
+
}
|
|
27303
|
+
}
|
|
27304
|
+
function narrowingCovers(entry, target) {
|
|
27305
|
+
return target === entry || target.startsWith(entry + SUBPROJECT_SEPARATOR);
|
|
27306
|
+
}
|
|
27307
|
+
function resolveProjectBinding(dataDir, principal, selector) {
|
|
27308
|
+
const authorized = principal.projects;
|
|
27309
|
+
const wildcard = authorized.includes("*");
|
|
27310
|
+
let target;
|
|
27311
|
+
if (selector) {
|
|
27312
|
+
if (!wildcard && !authorized.some((e) => e !== "*" && narrowingCovers(e, selector))) return null;
|
|
27313
|
+
target = selector;
|
|
27314
|
+
} else if (!wildcard && authorized.length === 1) {
|
|
27315
|
+
target = authorized[0];
|
|
27316
|
+
} else {
|
|
27317
|
+
return null;
|
|
27318
|
+
}
|
|
27319
|
+
const parsed = parseQualifiedSelector(target);
|
|
27320
|
+
if (!parsed) return null;
|
|
27321
|
+
const rec = load7(dataDir).find((r) => r.id === parsed.projectId);
|
|
27322
|
+
if (!rec || rec.status !== "active") return null;
|
|
27323
|
+
let rootPath = rec.rootPath;
|
|
27324
|
+
if (parsed.mounts.length > 0) {
|
|
27325
|
+
try {
|
|
27326
|
+
rootPath = resolveSubprojectMounts(parsed.projectId, rec.rootPath, parsed.mounts);
|
|
27327
|
+
} catch {
|
|
27328
|
+
return null;
|
|
27329
|
+
}
|
|
27330
|
+
}
|
|
27331
|
+
const binding = { rootPath, projectId: parsed.projectId };
|
|
27332
|
+
if (parsed.mounts.length > 0) binding.subproject = parsed.mounts.join(SUBPROJECT_SEPARATOR);
|
|
27333
|
+
return binding;
|
|
27334
|
+
}
|
|
27335
|
+
function resolveProjectRoot(dataDir, principal, selector) {
|
|
27336
|
+
return resolveProjectBinding(dataDir, principal, selector)?.rootPath ?? null;
|
|
27337
|
+
}
|
|
27338
|
+
|
|
27109
27339
|
// src/server/errors.ts
|
|
27110
27340
|
var UnauthenticatedError = class extends Error {
|
|
27111
27341
|
constructor() {
|
|
@@ -29131,11 +29361,8 @@ function mintToken(cfg, credential, request) {
|
|
|
29131
29361
|
}
|
|
29132
29362
|
assertNotReservedSubjectId(cfg, [request.ownerUserId]);
|
|
29133
29363
|
const projects = request.projects?.length ? request.projects : ["*"];
|
|
29134
|
-
const knownProjects = new Set(listProjectRecords(cfg.dataDir).map((p) => p.id));
|
|
29135
29364
|
for (const p of projects) {
|
|
29136
|
-
|
|
29137
|
-
throw new Error(`unknown project "${p}"`);
|
|
29138
|
-
}
|
|
29365
|
+
assertMintableNarrowingEntry(cfg.dataDir, p);
|
|
29139
29366
|
}
|
|
29140
29367
|
const owner = findUserByRecordOrSubjectId(cfg.dataDir, request.ownerUserId);
|
|
29141
29368
|
if (owner && owner.status !== "active") {
|
|
@@ -29172,12 +29399,21 @@ function revokeToken(cfg, credential, tokenId) {
|
|
|
29172
29399
|
}
|
|
29173
29400
|
function mintSelfToken(cfg, credential, projectId, write) {
|
|
29174
29401
|
const principal = requirePrincipal5(cfg, credential);
|
|
29175
|
-
|
|
29402
|
+
const parsed = parseQualifiedSelector(projectId);
|
|
29403
|
+
if (!parsed) {
|
|
29404
|
+
throw new Error(
|
|
29405
|
+
`invalid project id "${projectId}" (expected a project id, optionally subproject-qualified as projectId::subsystemId)`
|
|
29406
|
+
);
|
|
29407
|
+
}
|
|
29408
|
+
if (authorize(cfg.dataDir, principal, PROJECT_READ_CAPABILITY2, "project", parsed.projectId).value !== "yes") {
|
|
29176
29409
|
throw new ForbiddenError("caller lacks project:read on the requested project");
|
|
29177
29410
|
}
|
|
29178
|
-
if (write && authorize(cfg.dataDir, principal, PROJECT_WRITE_CAPABILITY2, "project", projectId).value !== "yes") {
|
|
29411
|
+
if (write && authorize(cfg.dataDir, principal, PROJECT_WRITE_CAPABILITY2, "project", parsed.projectId).value !== "yes") {
|
|
29179
29412
|
throw new ForbiddenError("caller lacks project:write on the requested project");
|
|
29180
29413
|
}
|
|
29414
|
+
if (parsed.mounts.length > 0) {
|
|
29415
|
+
assertMintableNarrowingEntry(cfg.dataDir, projectId);
|
|
29416
|
+
}
|
|
29181
29417
|
const token = "wk_" + crypto10.randomBytes(24).toString("hex");
|
|
29182
29418
|
const owner = auditActor(principal);
|
|
29183
29419
|
const record2 = {
|
|
@@ -29789,12 +30025,12 @@ function resolveVisibility(observerProjectId, units, placements) {
|
|
|
29789
30025
|
const best = /* @__PURE__ */ new Map();
|
|
29790
30026
|
for (const placement of placements) {
|
|
29791
30027
|
if (placement.projectId === observerProjectId) continue;
|
|
29792
|
-
const
|
|
29793
|
-
if (!
|
|
29794
|
-
const closedOk =
|
|
30028
|
+
const path61 = chainOf(placement.unitId, unitById);
|
|
30029
|
+
if (!path61.length) continue;
|
|
30030
|
+
const closedOk = path61.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
|
|
29795
30031
|
if (!closedOk) continue;
|
|
29796
|
-
const crossTenant = !tenantRoots.has(
|
|
29797
|
-
if (crossTenant && !
|
|
30032
|
+
const crossTenant = !tenantRoots.has(path61[path61.length - 1].id);
|
|
30033
|
+
if (crossTenant && !path61.some(grantedTo)) continue;
|
|
29798
30034
|
if (crossTenant && directUnits.length === 0) continue;
|
|
29799
30035
|
const distance = crossTenant ? "partner" : sameBranch(placement.unitId) ? "department" : "instance";
|
|
29800
30036
|
const existing = best.get(placement.projectId);
|
|
@@ -30733,10 +30969,9 @@ function migratePermissionModel(dataDir, apply) {
|
|
|
30733
30969
|
// src/server/http.ts
|
|
30734
30970
|
var http2 = __toESM(require("http"));
|
|
30735
30971
|
var fs49 = __toESM(require("fs"));
|
|
30736
|
-
var
|
|
30972
|
+
var path58 = __toESM(require("path"));
|
|
30737
30973
|
|
|
30738
30974
|
// src/server/request.ts
|
|
30739
|
-
var path58 = __toESM(require("path"));
|
|
30740
30975
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
30741
30976
|
init_fs();
|
|
30742
30977
|
|
|
@@ -35774,8 +36009,8 @@ var RealtimeHub = class {
|
|
|
35774
36009
|
* complete the handshake, and register the connection. A bad path or session
|
|
35775
36010
|
* destroys the socket. */
|
|
35776
36011
|
handleUpgrade(cfg, req, socket) {
|
|
35777
|
-
const
|
|
35778
|
-
if (
|
|
36012
|
+
const path61 = (req.url ?? "/").split("?")[0];
|
|
36013
|
+
if (path61 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
|
|
35779
36014
|
socket.destroy();
|
|
35780
36015
|
return;
|
|
35781
36016
|
}
|
|
@@ -35920,7 +36155,7 @@ function deriveMcpOutcome(response) {
|
|
|
35920
36155
|
if (r.result && typeof r.result === "object" && r.result.isError) return "failed";
|
|
35921
36156
|
return "success";
|
|
35922
36157
|
}
|
|
35923
|
-
function auditToolCall(dataDir, principal, projectId, body, outcome) {
|
|
36158
|
+
function auditToolCall(dataDir, principal, projectId, body, outcome, subproject) {
|
|
35924
36159
|
const target = mcpToolTarget(body);
|
|
35925
36160
|
if (!target) return;
|
|
35926
36161
|
const actor = principal.subject ?? {
|
|
@@ -35938,7 +36173,8 @@ function auditToolCall(dataDir, principal, projectId, body, outcome) {
|
|
|
35938
36173
|
actor,
|
|
35939
36174
|
tokenId: principal.tokenId,
|
|
35940
36175
|
projectId,
|
|
35941
|
-
target
|
|
36176
|
+
target,
|
|
36177
|
+
...subproject ? { metadata: JSON.stringify({ subproject }) } : {}
|
|
35942
36178
|
};
|
|
35943
36179
|
try {
|
|
35944
36180
|
appendAuditEvent(dataDir, event, DEFAULT_AUDIT_POLICY);
|
|
@@ -36142,8 +36378,8 @@ async function handleMcpRequest(cfg, req, res, body, credential) {
|
|
|
36142
36378
|
permissionSubject: { subjectId: "anonymous", roleBindings: [], instanceAdmin: true }
|
|
36143
36379
|
};
|
|
36144
36380
|
}
|
|
36145
|
-
const
|
|
36146
|
-
if (!
|
|
36381
|
+
const binding = resolveProjectBinding(cfg.dataDir, principal, projectSelector(req));
|
|
36382
|
+
if (!binding) {
|
|
36147
36383
|
sendJson(res, 403, { error: "project not authorized, unknown, or not specified" });
|
|
36148
36384
|
return;
|
|
36149
36385
|
}
|
|
@@ -36158,19 +36394,20 @@ async function handleMcpRequest(cfg, req, res, body, credential) {
|
|
|
36158
36394
|
});
|
|
36159
36395
|
return;
|
|
36160
36396
|
}
|
|
36161
|
-
await runWithProjectRoot(
|
|
36162
|
-
const projectId =
|
|
36397
|
+
await runWithProjectRoot(binding.rootPath, async () => {
|
|
36398
|
+
const projectId = binding.projectId;
|
|
36399
|
+
const subproject = binding.subproject;
|
|
36163
36400
|
const dispatchedResponse = await dispatchProjectLifecycleTool(cfg, cred, projectId, body);
|
|
36164
36401
|
if (dispatchedResponse !== void 0) {
|
|
36165
36402
|
sendJson(res, 200, dispatchedResponse);
|
|
36166
|
-
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(dispatchedResponse));
|
|
36403
|
+
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(dispatchedResponse), subproject);
|
|
36167
36404
|
for (const ch of mcpChangeChannels(body, projectId, dispatchedResponse)) publishChange(ch);
|
|
36168
36405
|
return;
|
|
36169
36406
|
}
|
|
36170
36407
|
const permissionError = dataPlanePermissionError(cfg, principal, projectId, body);
|
|
36171
36408
|
if (permissionError !== void 0) {
|
|
36172
36409
|
sendJson(res, 200, permissionError);
|
|
36173
|
-
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(permissionError));
|
|
36410
|
+
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(permissionError), subproject);
|
|
36174
36411
|
return;
|
|
36175
36412
|
}
|
|
36176
36413
|
const server = createScopedServer();
|
|
@@ -36191,7 +36428,7 @@ async function handleMcpRequest(cfg, req, res, body, credential) {
|
|
|
36191
36428
|
};
|
|
36192
36429
|
await server.connect(transport);
|
|
36193
36430
|
await transport.handleRequest(req, res, body);
|
|
36194
|
-
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(response));
|
|
36431
|
+
auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(response), subproject);
|
|
36195
36432
|
for (const ch of mcpChangeChannels(body, projectId, response)) publishChange(ch);
|
|
36196
36433
|
});
|
|
36197
36434
|
}
|
|
@@ -36626,7 +36863,7 @@ function routeData(cfg, req, res) {
|
|
|
36626
36863
|
}
|
|
36627
36864
|
function readExposurePolicyFile(dataDir) {
|
|
36628
36865
|
try {
|
|
36629
|
-
const raw = fs49.readFileSync(
|
|
36866
|
+
const raw = fs49.readFileSync(path58.join(dataDir, "exposure-policy.json"), "utf8");
|
|
36630
36867
|
const parsed = JSON.parse(raw);
|
|
36631
36868
|
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
36632
36869
|
} catch {
|
|
@@ -37311,9 +37548,9 @@ function seedDemoTree() {
|
|
|
37311
37548
|
|
|
37312
37549
|
// src/commands/host.ts
|
|
37313
37550
|
function resolveHostConfig(options) {
|
|
37314
|
-
const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] ||
|
|
37551
|
+
const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path59.join(os10.homedir(), ".wairon", "data");
|
|
37315
37552
|
if (!process.env["WAIRON_PACKS_DIR"]) {
|
|
37316
|
-
process.env["WAIRON_PACKS_DIR"] =
|
|
37553
|
+
process.env["WAIRON_PACKS_DIR"] = path59.join(dataDir, "packs");
|
|
37317
37554
|
}
|
|
37318
37555
|
const cfg = {
|
|
37319
37556
|
host: options.host || "0.0.0.0",
|
|
@@ -37438,13 +37675,13 @@ function openBrowser(url) {
|
|
|
37438
37675
|
}
|
|
37439
37676
|
async function runDev(options = {}) {
|
|
37440
37677
|
const cwd = process.cwd();
|
|
37441
|
-
if (!fs50.existsSync(
|
|
37678
|
+
if (!fs50.existsSync(path59.join(cwd, ".wai"))) {
|
|
37442
37679
|
throw new WaironError(
|
|
37443
37680
|
"No .wai/ found in the current directory. Run `wairon dev` from a wairon project root (or run `wairon init` first)."
|
|
37444
37681
|
);
|
|
37445
37682
|
}
|
|
37446
37683
|
const hash = crypto20.createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
37447
|
-
const dataDir =
|
|
37684
|
+
const dataDir = path59.join(os10.tmpdir(), "wairon-dev", hash);
|
|
37448
37685
|
fs50.mkdirSync(dataDir, { recursive: true });
|
|
37449
37686
|
registerLocalDevProject(dataDir, "local", cwd);
|
|
37450
37687
|
const port = options.port ? Number(options.port) : 8080;
|
|
@@ -37853,8 +38090,8 @@ async function runHostPacks(action, options = {}) {
|
|
|
37853
38090
|
}
|
|
37854
38091
|
case "install": {
|
|
37855
38092
|
if (!options.file) throw new WaironError("`--file <path>` (a declarative pack YAML) is required for install.");
|
|
37856
|
-
const name = options.name ??
|
|
37857
|
-
const content = fs50.readFileSync(
|
|
38093
|
+
const name = options.name ?? path59.basename(options.file).replace(/\.(ya?ml)$/i, "");
|
|
38094
|
+
const content = fs50.readFileSync(path59.resolve(options.file), "utf8");
|
|
37858
38095
|
const desc = project2 ? installProjectPack(cfg, cred, project2, name, content) : installGlobalPack(cfg, cred, name, content);
|
|
37859
38096
|
logger.success(`Installed ${scope} pack "${desc.name}" (${desc.profiles} profile(s), ${desc.languages} language(s)).`);
|
|
37860
38097
|
if (project2) logger.info("Committed with the project \u2014 every clone and CI will enforce it.");
|
|
@@ -38060,13 +38297,27 @@ async function runSurface(action, options = {}) {
|
|
|
38060
38297
|
for (const p of written) logger.info(` ${p}`);
|
|
38061
38298
|
return;
|
|
38062
38299
|
}
|
|
38300
|
+
case "externals": {
|
|
38301
|
+
const entries = listExternalInterfaces();
|
|
38302
|
+
if (!entries.length) {
|
|
38303
|
+
logger.info("No external surfaces available (.wai/surfaces/ holds no snapshots).");
|
|
38304
|
+
return;
|
|
38305
|
+
}
|
|
38306
|
+
const freshness = (f) => f === "fresh" ? import_chalk19.default.green(f) : f === "stale" ? import_chalk19.default.yellow(f) : import_chalk19.default.gray(f);
|
|
38307
|
+
for (const e of entries) {
|
|
38308
|
+
logger.info(
|
|
38309
|
+
`${e.sourceKind.padEnd(8)} ${import_chalk19.default.cyan(e.projectName)} [${e.origin}] ${freshness(e.freshness)} \u2014 ${e.interfaceIds.length ? e.interfaceIds.join(", ") : "(no interfaces)"}`
|
|
38310
|
+
);
|
|
38311
|
+
}
|
|
38312
|
+
return;
|
|
38313
|
+
}
|
|
38063
38314
|
default:
|
|
38064
|
-
throw new WaironError(`Unknown surface action "${action}" (supported: export, import, list, generate-children).`);
|
|
38315
|
+
throw new WaironError(`Unknown surface action "${action}" (supported: export, import, list, generate-children, externals).`);
|
|
38065
38316
|
}
|
|
38066
38317
|
}
|
|
38067
38318
|
|
|
38068
38319
|
// src/commands/subsystem.ts
|
|
38069
|
-
var
|
|
38320
|
+
var path60 = __toESM(require("path"));
|
|
38070
38321
|
init_logger();
|
|
38071
38322
|
init_errors();
|
|
38072
38323
|
init_fs();
|
|
@@ -38102,9 +38353,9 @@ async function runSubsystemAdd(id, options = {}) {
|
|
|
38102
38353
|
updatedAt: now
|
|
38103
38354
|
};
|
|
38104
38355
|
createChainedSubsystem(subsystem, displayName);
|
|
38105
|
-
const childDir =
|
|
38356
|
+
const childDir = path60.resolve(getProjectRoot(), options.projectPath);
|
|
38106
38357
|
logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
38107
|
-
logger.info(`Scaffolded child project at ${
|
|
38358
|
+
logger.info(`Scaffolded child project at ${path60.relative(process.cwd(), childDir) || "."}`);
|
|
38108
38359
|
logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
|
|
38109
38360
|
}
|
|
38110
38361
|
async function runSubsystemMove(id, options = {}) {
|
|
@@ -38127,9 +38378,9 @@ async function runSubsystemExternalize(id, options = {}) {
|
|
|
38127
38378
|
throw new WaironError("--project-path (the subproject destination) is required.");
|
|
38128
38379
|
}
|
|
38129
38380
|
externalizeSubsystem(id, options.projectPath);
|
|
38130
|
-
const childDir =
|
|
38381
|
+
const childDir = path60.resolve(getProjectRoot(), options.projectPath);
|
|
38131
38382
|
logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
38132
|
-
logger.info(`Moved its specs into ${
|
|
38383
|
+
logger.info(`Moved its specs into ${path60.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
|
|
38133
38384
|
logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
|
|
38134
38385
|
}
|
|
38135
38386
|
async function runSubsystemInternalize(id) {
|
|
@@ -38168,8 +38419,64 @@ program.command("generate").description("Generate agent output files from the sp
|
|
|
38168
38419
|
dryRun: opts.dryRun
|
|
38169
38420
|
});
|
|
38170
38421
|
});
|
|
38422
|
+
async function runLock2(options) {
|
|
38423
|
+
assertProjectInitialized();
|
|
38424
|
+
if (!pathExists(AI_PATHS.specsSystem())) {
|
|
38425
|
+
logger.error("No SDD spec tree found (.wai/specs). Nothing to lock.");
|
|
38426
|
+
process.exit(1);
|
|
38427
|
+
}
|
|
38428
|
+
const projectConfig = loadProjectConfig();
|
|
38429
|
+
logger.info("Analyzing and validating specifications in-memory...");
|
|
38430
|
+
const dry = validateAsComplete({
|
|
38431
|
+
rules: projectConfig.rules,
|
|
38432
|
+
projectType: projectConfig.projectType,
|
|
38433
|
+
scopeSubsystem: options.subsystem,
|
|
38434
|
+
recursive: options.recursive ?? true
|
|
38435
|
+
});
|
|
38436
|
+
const errors = dry.issues.filter((i) => i.severity === "error");
|
|
38437
|
+
if (errors.length > 0) {
|
|
38438
|
+
logger.header("Cannot lock \u2014 the spec tree does not validate as complete");
|
|
38439
|
+
let errorCount = 0;
|
|
38440
|
+
const MAX_PRINT = 100;
|
|
38441
|
+
let skippedErrors = 0;
|
|
38442
|
+
for (const i of errors) {
|
|
38443
|
+
if (errorCount < MAX_PRINT) {
|
|
38444
|
+
logger.error(`${i.specId ? `[${i.specId}] ` : ""}[${i.code}] ${i.message}`);
|
|
38445
|
+
errorCount++;
|
|
38446
|
+
} else {
|
|
38447
|
+
skippedErrors++;
|
|
38448
|
+
}
|
|
38449
|
+
}
|
|
38450
|
+
if (skippedErrors > 0) {
|
|
38451
|
+
logger.error(`... and ${skippedErrors} more error(s) omitted.`);
|
|
38452
|
+
}
|
|
38453
|
+
logger.blank();
|
|
38454
|
+
logger.info("Fix the errors above, then run `wairon lock` again. Nothing was changed.");
|
|
38455
|
+
process.exit(1);
|
|
38456
|
+
}
|
|
38457
|
+
logger.header("Lock SDD specs");
|
|
38458
|
+
const record2 = await runLock(options, dry);
|
|
38459
|
+
if (!record2) {
|
|
38460
|
+
logger.info("Cancelled. Nothing was changed.");
|
|
38461
|
+
return;
|
|
38462
|
+
}
|
|
38463
|
+
const childPaths = generateChildSnapshots();
|
|
38464
|
+
if (childPaths.length > 0) {
|
|
38465
|
+
logger.blank();
|
|
38466
|
+
logger.success(`Regenerated the family/sibling surfaces into ${childPaths.length} chained child snapshot(s):`);
|
|
38467
|
+
for (const p of childPaths) logger.info(` ${p}`);
|
|
38468
|
+
}
|
|
38469
|
+
logger.blank();
|
|
38470
|
+
await runGenerate({ domain: options.subsystem });
|
|
38471
|
+
logger.blank();
|
|
38472
|
+
logger.success("Specs locked and agent topology generated.");
|
|
38473
|
+
logger.info(`Lock record written (.wai/lock.json): stateId ${record2.stateId.algorithm}:${record2.stateId.digest} \u2014 status ${record2.status}.`);
|
|
38474
|
+
logger.warn(
|
|
38475
|
+
"Restart any running AI agent sessions (Claude Code / Antigravity / Codex) so the newly generated implementer agents load \u2014 they are not picked up mid-session."
|
|
38476
|
+
);
|
|
38477
|
+
}
|
|
38171
38478
|
program.command("lock").description("Final check before implementation: validate the spec tree as complete, freeze all specs to complete, and (re)generate the agent topology \u2014 only if it validates").option("-y, --yes", "skip the confirmation prompt (for scripts / CI)").option("--subsystem <id>", "only lock specs in the specified subsystem").option("--no-recursive", "do not recursively validate subprojects").action(async (opts) => {
|
|
38172
|
-
await
|
|
38479
|
+
await runLock2({ yes: opts.yes, subsystem: opts.subsystem, recursive: opts.recursive });
|
|
38173
38480
|
});
|
|
38174
38481
|
program.command("validate").description("Validate the project configuration and the SDD Spec Tree").option("--ci", "treat warnings as errors for CI pipelines").option("--subsystem <id>", "only validate the specified subsystem (granular)").option("--no-recursive", "do not recursively validate subprojects").action(async (opts) => {
|
|
38175
38482
|
await runValidate({ ci: opts.ci, subsystem: opts.subsystem, recursive: opts.recursive });
|
|
@@ -38285,7 +38592,7 @@ mcpCmd.command("status").description("Show whether the wairon MCP server is regi
|
|
|
38285
38592
|
program.command("produce <target>").description("project the local project's specs to a producer target (notion | miro)").option("--page <id>", "parent page/board id in the target").option("--token <token>", "integration token (else env, else interactive prompt)").action(async (target, opts) => {
|
|
38286
38593
|
await runProduce(target, { page: opts.page, token: opts.token });
|
|
38287
38594
|
});
|
|
38288
|
-
program.command("surface <action>").description("public surface exchange: export | import | list | generate-children").option("--audience <level>", "export ceiling: project | department | instance | partner | external (default instance)").option("--format <fmt>", "export format: native | openapi (default native)").option("--out <path>", "export output path (else print)").option("--portal <id>", "export: select one portal's OpenAPI spec (a multi-portal project renders one document per portal)").option("--source <path>", "import: the surface document (native snapshot YAML or OpenAPI)").option("--origin <origin>", "import provenance: exchanged | authored (default authored)").action(async (action, opts) => {
|
|
38595
|
+
program.command("surface <action>").description("public surface exchange: export | import | list | generate-children | externals").option("--audience <level>", "export ceiling: project | department | instance | partner | external (default instance)").option("--format <fmt>", "export format: native | openapi (default native)").option("--out <path>", "export output path (else print)").option("--portal <id>", "export: select one portal's OpenAPI spec (a multi-portal project renders one document per portal)").option("--source <path>", "import: the surface document (native snapshot YAML or OpenAPI)").option("--origin <origin>", "import provenance: exchanged | authored (default authored)").action(async (action, opts) => {
|
|
38289
38596
|
await runSurface(action, {
|
|
38290
38597
|
audience: opts.audience,
|
|
38291
38598
|
format: opts.format,
|