@spec-engine/spec-engine 0.5.0 → 0.6.1
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/docs/architecture/index.html +11 -3
- package/dist/docs/commands/index.html +9 -6
- package/dist/docs/pagefind/fragment/en_aa3ebfe.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_dfaa776.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_edd950c.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_a63e8e2.pf_index +0 -0
- package/dist/docs/pagefind/pagefind-entry.json +1 -1
- package/dist/docs/pagefind/pagefind.en_2ed26d23fb.pf_meta +0 -0
- package/dist/docs/platform-mapping/index.html +4 -3
- package/dist/impl.js +478 -102
- package/package.json +1 -1
- package/dist/docs/pagefind/fragment/en_48f1b3a.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_73bb90d.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_a7dfb2d.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_6c7635b.pf_index +0 -0
- package/dist/docs/pagefind/pagefind.en_20f81448c5.pf_meta +0 -0
package/dist/impl.js
CHANGED
|
@@ -20,7 +20,7 @@ var package_default;
|
|
|
20
20
|
var init_package = __esm(() => {
|
|
21
21
|
package_default = {
|
|
22
22
|
name: "@spec-engine/spec-engine",
|
|
23
|
-
version: "0.
|
|
23
|
+
version: "0.6.1",
|
|
24
24
|
description: "Cross-repo spec engine \u2014 durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
25
25
|
license: "MIT",
|
|
26
26
|
type: "module",
|
|
@@ -373,10 +373,10 @@ var init_flags = __esm(() => {
|
|
|
373
373
|
FEATURE_KEYS = Object.keys(FEATURES);
|
|
374
374
|
});
|
|
375
375
|
// ../shared/src/net.ts
|
|
376
|
-
function
|
|
377
|
-
return LOOPBACK_HOSTNAMES.has(hostname);
|
|
376
|
+
function isServedHostname(hostname, bindHost = DEFAULT_BIND_HOST) {
|
|
377
|
+
return LOOPBACK_HOSTNAMES.has(hostname) || hostname === bindHost;
|
|
378
378
|
}
|
|
379
|
-
var LOOPBACK_HOSTNAMES;
|
|
379
|
+
var DEFAULT_BIND_HOST = "127.0.0.1", LOOPBACK_HOSTNAMES;
|
|
380
380
|
var init_net = __esm(() => {
|
|
381
381
|
LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
382
382
|
});
|
|
@@ -1120,6 +1120,9 @@ function readPlatformMap(dir) {
|
|
|
1120
1120
|
return null;
|
|
1121
1121
|
return { mapped: map(abs), located };
|
|
1122
1122
|
}
|
|
1123
|
+
function platformName(platformDir) {
|
|
1124
|
+
return readPlatformMap(platformDir)?.mapped.name ?? basename(resolve2(platformDir));
|
|
1125
|
+
}
|
|
1123
1126
|
function platformMode(platformDir) {
|
|
1124
1127
|
return readPlatformMap(platformDir)?.mapped.mode ?? "single-repo";
|
|
1125
1128
|
}
|
|
@@ -1274,6 +1277,37 @@ async function packageColumns(repoPath, prefix, repo, parentPin) {
|
|
|
1274
1277
|
}
|
|
1275
1278
|
return out;
|
|
1276
1279
|
}
|
|
1280
|
+
function packageDirs(repo) {
|
|
1281
|
+
const dirs = repo.packages.map((p) => p.path);
|
|
1282
|
+
return dirs.some((d) => d === "" || d === ".") ? null : dirs;
|
|
1283
|
+
}
|
|
1284
|
+
function loneRootColumn(absPlatform, repo, platformVersion) {
|
|
1285
|
+
const dirs = packageDirs(repo);
|
|
1286
|
+
const { dependency_depth: _depth, ...self } = selfMember(absPlatform, platformVersion);
|
|
1287
|
+
if (dirs === null || self.name === CANONICAL_SPECS_DIR)
|
|
1288
|
+
return [];
|
|
1289
|
+
return [{ repo: { ...self, ignore: dirs }, node: repo }];
|
|
1290
|
+
}
|
|
1291
|
+
async function loneMembers(repo, absPlatform, platformVersion) {
|
|
1292
|
+
if (repo === undefined || repo.mode !== "monorepo" || repo.packages.length === 0) {
|
|
1293
|
+
return [selfMember(absPlatform, platformVersion)];
|
|
1294
|
+
}
|
|
1295
|
+
return toRepos([
|
|
1296
|
+
...await packageColumns(absPlatform, "", repo, platformVersion),
|
|
1297
|
+
...loneRootColumn(absPlatform, repo, platformVersion)
|
|
1298
|
+
]);
|
|
1299
|
+
}
|
|
1300
|
+
async function memberColumns(repo, path, cfg) {
|
|
1301
|
+
const pin = extractPin(cfg.specs);
|
|
1302
|
+
if (repo.mode !== "monorepo" || repo.packages.length === 0) {
|
|
1303
|
+
return [column(repo.name, path, pin, repo, cfg.ignore)];
|
|
1304
|
+
}
|
|
1305
|
+
const packages = await packageColumns(path, `${repo.name}/`, repo, pin);
|
|
1306
|
+
const dirs = packageDirs(repo);
|
|
1307
|
+
if (dirs === null)
|
|
1308
|
+
return packages;
|
|
1309
|
+
return [...packages, column(repo.name, path, pin, repo, [...cfg.ignore ?? [], ...dirs])];
|
|
1310
|
+
}
|
|
1277
1311
|
function describe(d, mapped) {
|
|
1278
1312
|
const s = d.subject;
|
|
1279
1313
|
switch (d.code) {
|
|
@@ -1306,8 +1340,7 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1306
1340
|
const { mapped, located } = reading;
|
|
1307
1341
|
const diagnostics2 = surfaced(mapped);
|
|
1308
1342
|
if (mapped.mode !== "multi-repo") {
|
|
1309
|
-
const
|
|
1310
|
-
const members = repo !== undefined && repo.mode === "monorepo" && repo.packages.length > 0 ? toRepos(await packageColumns(absPlatform, "", repo, platformVersion)) : [selfMember(absPlatform, platformVersion)];
|
|
1343
|
+
const members = await loneMembers(mapped.repos[0], absPlatform, platformVersion);
|
|
1311
1344
|
return { mode: mapped.mode, members, unpinned: [], undeclared: [], diagnostics: diagnostics2 };
|
|
1312
1345
|
}
|
|
1313
1346
|
if (!mapped.declared) {
|
|
@@ -1325,13 +1358,7 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1325
1358
|
unpinned.push({ name: repo.name, path });
|
|
1326
1359
|
continue;
|
|
1327
1360
|
}
|
|
1328
|
-
|
|
1329
|
-
const pin = extractPin(cfg.specs);
|
|
1330
|
-
if (repo.mode === "monorepo" && repo.packages.length > 0) {
|
|
1331
|
-
columns.push(...await packageColumns(path, `${repo.name}/`, repo, pin));
|
|
1332
|
-
} else {
|
|
1333
|
-
columns.push(column(repo.name, path, pin, repo, cfg.ignore));
|
|
1334
|
-
}
|
|
1361
|
+
columns.push(...await memberColumns(repo, path, await readRepoConfig(configPath)));
|
|
1335
1362
|
}
|
|
1336
1363
|
const undeclared = mapped.diagnostics.filter((d) => d.code === "UNLISTED_REPO").map((d) => ({ name: d.subject, path: join3(absPlatform, d.subject) }));
|
|
1337
1364
|
return { mode: "multi-repo", members: toRepos(columns), unpinned, undeclared, diagnostics: diagnostics2 };
|
|
@@ -3314,7 +3341,24 @@ import { resolve as resolve6 } from "path";
|
|
|
3314
3341
|
function resolvePlatformDir(args) {
|
|
3315
3342
|
return resolve6(args.platformDir ?? process.cwd());
|
|
3316
3343
|
}
|
|
3317
|
-
|
|
3344
|
+
function listFlag(rawArgs, name, parsed) {
|
|
3345
|
+
const flag = `--${name}`;
|
|
3346
|
+
const raw = [];
|
|
3347
|
+
for (let i = 0;i < rawArgs.length; i++) {
|
|
3348
|
+
const arg = rawArgs[i];
|
|
3349
|
+
if (arg === flag && i + 1 < rawArgs.length)
|
|
3350
|
+
raw.push(rawArgs[++i]);
|
|
3351
|
+
else if (arg.startsWith(`${flag}=`))
|
|
3352
|
+
raw.push(arg.slice(flag.length + 1));
|
|
3353
|
+
}
|
|
3354
|
+
if (raw.length === 0) {
|
|
3355
|
+
if (parsed === undefined)
|
|
3356
|
+
return;
|
|
3357
|
+
raw.push(parsed);
|
|
3358
|
+
}
|
|
3359
|
+
return raw.flatMap((v) => v.split(",")).map((v) => v.trim()).filter((v) => v !== "");
|
|
3360
|
+
}
|
|
3361
|
+
var platformDirArg, outArg, jsonArg, freshArg, noPromptArg, livesArg;
|
|
3318
3362
|
var init__args = __esm(() => {
|
|
3319
3363
|
init_constants();
|
|
3320
3364
|
platformDirArg = {
|
|
@@ -3335,6 +3379,10 @@ var init__args = __esm(() => {
|
|
|
3335
3379
|
type: "boolean",
|
|
3336
3380
|
description: "Suppress the interactive onboarding prompt for siblings missing spec-engine.member.json (they surface as NO_SPEC_CONFIG warnings)"
|
|
3337
3381
|
};
|
|
3382
|
+
livesArg = {
|
|
3383
|
+
type: "string",
|
|
3384
|
+
description: "Lives in (livesIn) paths: repeat the flag or comma-separate; an empty value clears the list"
|
|
3385
|
+
};
|
|
3338
3386
|
});
|
|
3339
3387
|
|
|
3340
3388
|
// src/commands/index.ts
|
|
@@ -6448,24 +6496,27 @@ __export(exports_req, {
|
|
|
6448
6496
|
});
|
|
6449
6497
|
import { createInterface as createInterface2 } from "readline";
|
|
6450
6498
|
import { defineCommand as defineCommand12 } from "citty";
|
|
6451
|
-
async function mintFromFlags(platformDir, key, textFlag, args) {
|
|
6499
|
+
async function mintFromFlags(platformDir, key, textFlag, args, rawArgs) {
|
|
6452
6500
|
const statement = textFlag.trim();
|
|
6453
6501
|
if (statement === "") {
|
|
6454
6502
|
console.error("spec req: --text must be a non-empty Requirement");
|
|
6455
6503
|
process.exit(EXIT.USAGE);
|
|
6456
6504
|
}
|
|
6457
6505
|
await printResolvedCharter(platformDir, key);
|
|
6458
|
-
const lives = (args.lives ?? "").trim();
|
|
6459
6506
|
await mintAndReport(platformDir, key, {
|
|
6460
6507
|
statement,
|
|
6461
6508
|
why: (args.why ?? "").trim(),
|
|
6462
|
-
livesIn: lives
|
|
6509
|
+
livesIn: listFlag(rawArgs, "lives", args.lives) ?? [],
|
|
6463
6510
|
issue: (args.issue ?? "").trim() || undefined,
|
|
6511
|
+
status: statusOf(args),
|
|
6464
6512
|
json: Boolean(args.json)
|
|
6465
6513
|
});
|
|
6466
6514
|
}
|
|
6467
|
-
|
|
6468
|
-
|
|
6515
|
+
function statusOf(args) {
|
|
6516
|
+
return args.draft ? "draft" : "active";
|
|
6517
|
+
}
|
|
6518
|
+
async function mintInteractively(platformDir, key, id, status) {
|
|
6519
|
+
console.error(`Authoring ${id} \u2014 ${status === "draft" ? "Draft" : "Active"}`);
|
|
6469
6520
|
await printResolvedCharter(platformDir, key);
|
|
6470
6521
|
const requirement = (await askLine("Requirement: ")).trim();
|
|
6471
6522
|
if (requirement === "") {
|
|
@@ -6474,12 +6525,13 @@ async function mintInteractively(platformDir, key, id) {
|
|
|
6474
6525
|
return;
|
|
6475
6526
|
}
|
|
6476
6527
|
const why = (await askLine("Why it matters: ")).trim();
|
|
6477
|
-
const
|
|
6528
|
+
const livesIn = listFlag([], "lives", await askLine("Lives in: ")) ?? [];
|
|
6478
6529
|
await mintAndReport(platformDir, key, {
|
|
6479
6530
|
statement: requirement,
|
|
6480
6531
|
why,
|
|
6481
|
-
livesIn
|
|
6532
|
+
livesIn,
|
|
6482
6533
|
issue: undefined,
|
|
6534
|
+
status,
|
|
6483
6535
|
json: false
|
|
6484
6536
|
});
|
|
6485
6537
|
}
|
|
@@ -6532,10 +6584,14 @@ var init_req = __esm(() => {
|
|
|
6532
6584
|
description: "Non-interactive authoring: the Requirement field. When set, the entry appends with zero prompts."
|
|
6533
6585
|
},
|
|
6534
6586
|
why: { type: "string", description: "Why it matters field (only with --text)" },
|
|
6535
|
-
lives:
|
|
6587
|
+
lives: livesArg,
|
|
6536
6588
|
issue: {
|
|
6537
6589
|
type: "string",
|
|
6538
6590
|
description: "Originating ticket id, recorded as created-provenance on the entry (only with --text; opaque \u2014 never a requirement id)"
|
|
6591
|
+
},
|
|
6592
|
+
draft: {
|
|
6593
|
+
type: "boolean",
|
|
6594
|
+
description: "Author the entry as Draft instead of Active (promote it later with spec accept)"
|
|
6539
6595
|
}
|
|
6540
6596
|
};
|
|
6541
6597
|
reqCommand = defineCommand12({
|
|
@@ -6544,7 +6600,7 @@ var init_req = __esm(() => {
|
|
|
6544
6600
|
description: "Author a new requirement interactively (TTY) or print the next unused id (non-TTY)"
|
|
6545
6601
|
},
|
|
6546
6602
|
args: reqArgs,
|
|
6547
|
-
async run({ args }) {
|
|
6603
|
+
async run({ args, rawArgs }) {
|
|
6548
6604
|
const input = args.domainPrefix;
|
|
6549
6605
|
const platformDir = resolvePlatformDir(args);
|
|
6550
6606
|
try {
|
|
@@ -6558,7 +6614,7 @@ var init_req = __esm(() => {
|
|
|
6558
6614
|
const { key, nextId: id } = allocated;
|
|
6559
6615
|
const textFlag = args.text;
|
|
6560
6616
|
if (textFlag !== undefined) {
|
|
6561
|
-
await mintFromFlags(platformDir, key, textFlag, args);
|
|
6617
|
+
await mintFromFlags(platformDir, key, textFlag, args, rawArgs);
|
|
6562
6618
|
return;
|
|
6563
6619
|
}
|
|
6564
6620
|
const hasFieldFlags = typeof args.why === "string" || typeof args.lives === "string" || typeof args.issue === "string";
|
|
@@ -6567,6 +6623,11 @@ var init_req = __esm(() => {
|
|
|
6567
6623
|
process.exit(EXIT.USAGE);
|
|
6568
6624
|
return;
|
|
6569
6625
|
}
|
|
6626
|
+
if (args.draft && (args.json || !process.stdin.isTTY)) {
|
|
6627
|
+
console.error("spec req: --draft authors an entry \u2014 pass --text, or run on a TTY");
|
|
6628
|
+
process.exit(EXIT.USAGE);
|
|
6629
|
+
return;
|
|
6630
|
+
}
|
|
6570
6631
|
if (args.json) {
|
|
6571
6632
|
console.log(JSON.stringify({ domain: key, next_id: id }));
|
|
6572
6633
|
return;
|
|
@@ -6575,7 +6636,7 @@ var init_req = __esm(() => {
|
|
|
6575
6636
|
console.log(id);
|
|
6576
6637
|
return;
|
|
6577
6638
|
}
|
|
6578
|
-
await mintInteractively(platformDir, key, id);
|
|
6639
|
+
await mintInteractively(platformDir, key, id, statusOf(args));
|
|
6579
6640
|
}
|
|
6580
6641
|
});
|
|
6581
6642
|
});
|
|
@@ -7122,14 +7183,13 @@ async function resolveSuccessorText(args, platformDir, id) {
|
|
|
7122
7183
|
function splitAliases(raw) {
|
|
7123
7184
|
return raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
7124
7185
|
}
|
|
7125
|
-
function inputFromArgs(args, platformDir, id, statement) {
|
|
7186
|
+
function inputFromArgs(args, rawArgs, platformDir, id, statement) {
|
|
7126
7187
|
const input = { platformDir, id, statement, noBump: Boolean(args.noBump) };
|
|
7127
7188
|
if (typeof args.why === "string")
|
|
7128
7189
|
input.why = args.why;
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
input.livesIn =
|
|
7132
|
-
}
|
|
7190
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7191
|
+
if (livesIn !== undefined)
|
|
7192
|
+
input.livesIn = livesIn;
|
|
7133
7193
|
if (typeof args.term === "string")
|
|
7134
7194
|
input.term = args.term;
|
|
7135
7195
|
if (typeof args.aliases === "string")
|
|
@@ -7166,7 +7226,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7166
7226
|
},
|
|
7167
7227
|
lives: {
|
|
7168
7228
|
type: "string",
|
|
7169
|
-
description: "Successor's Lives in (default: copied from the old entry)"
|
|
7229
|
+
description: "Successor's Lives in: repeat or comma-separate; empty clears (default: copied from the old entry)"
|
|
7170
7230
|
},
|
|
7171
7231
|
term: {
|
|
7172
7232
|
type: "string",
|
|
@@ -7195,7 +7255,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7195
7255
|
description: "Supersede a shipped requirement: flip it to superseded, mint the successor, bump specVersion, and emit the retag worklist."
|
|
7196
7256
|
},
|
|
7197
7257
|
args: supersedeArgs,
|
|
7198
|
-
async run({ args }) {
|
|
7258
|
+
async run({ args, rawArgs }) {
|
|
7199
7259
|
const id = args.id;
|
|
7200
7260
|
if (!ID_RE.test(id)) {
|
|
7201
7261
|
console.error(`spec supersede: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
@@ -7208,7 +7268,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7208
7268
|
handleNotAPlatform(e);
|
|
7209
7269
|
}
|
|
7210
7270
|
const statement = await resolveSuccessorText(args, platformDir, id);
|
|
7211
|
-
const result = await supersede(inputFromArgs(args, platformDir, id, statement), coldFreshTags(platformDir));
|
|
7271
|
+
const result = await supersede(inputFromArgs(args, rawArgs, platformDir, id, statement), coldFreshTags(platformDir));
|
|
7212
7272
|
if (!result.ok)
|
|
7213
7273
|
exitOnFailure("spec supersede", result);
|
|
7214
7274
|
printWarnings("spec supersede", result.warnings);
|
|
@@ -7454,16 +7514,15 @@ __export(exports_move, {
|
|
|
7454
7514
|
moveCommand: () => moveCommand
|
|
7455
7515
|
});
|
|
7456
7516
|
import { defineCommand as defineCommand17 } from "citty";
|
|
7457
|
-
function inputFromArgs2(args, platformDir, id, targetKey) {
|
|
7517
|
+
function inputFromArgs2(args, rawArgs, platformDir, id, targetKey) {
|
|
7458
7518
|
const input = { platformDir, id, targetKey, noBump: Boolean(args.noBump) };
|
|
7459
7519
|
if (typeof args.text === "string")
|
|
7460
7520
|
input.statement = args.text;
|
|
7461
7521
|
if (typeof args.why === "string")
|
|
7462
7522
|
input.why = args.why;
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
input.livesIn =
|
|
7466
|
-
}
|
|
7523
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7524
|
+
if (livesIn !== undefined)
|
|
7525
|
+
input.livesIn = livesIn;
|
|
7467
7526
|
return input;
|
|
7468
7527
|
}
|
|
7469
7528
|
var moveArgs, moveCommand;
|
|
@@ -7498,7 +7557,7 @@ var init_move2 = __esm(() => {
|
|
|
7498
7557
|
},
|
|
7499
7558
|
lives: {
|
|
7500
7559
|
type: "string",
|
|
7501
|
-
description: "Rewrite the successor's Lives in (default: copied from the source)"
|
|
7560
|
+
description: "Rewrite the successor's Lives in: repeat or comma-separate; empty clears (default: copied from the source)"
|
|
7502
7561
|
},
|
|
7503
7562
|
noBump: {
|
|
7504
7563
|
type: "boolean",
|
|
@@ -7515,7 +7574,7 @@ var init_move2 = __esm(() => {
|
|
|
7515
7574
|
description: "Move a requirement to another domain: mint the successor in <NEW-DOMAIN> carrying the source's fields, mark the source superseded, bump both specVersions, and emit the retag worklist."
|
|
7516
7575
|
},
|
|
7517
7576
|
args: moveArgs,
|
|
7518
|
-
async run({ args }) {
|
|
7577
|
+
async run({ args, rawArgs }) {
|
|
7519
7578
|
const id = args.id;
|
|
7520
7579
|
const rawTargetKey = args.newDomain;
|
|
7521
7580
|
const platformDir = resolvePlatformDir(args);
|
|
@@ -7533,7 +7592,7 @@ var init_move2 = __esm(() => {
|
|
|
7533
7592
|
} catch (e) {
|
|
7534
7593
|
handleNotAPlatform(e);
|
|
7535
7594
|
}
|
|
7536
|
-
const result = await move(inputFromArgs2(args, platformDir, id, targetKey), coldFreshTags(platformDir));
|
|
7595
|
+
const result = await move(inputFromArgs2(args, rawArgs, platformDir, id, targetKey), coldFreshTags(platformDir));
|
|
7537
7596
|
if (!result.ok)
|
|
7538
7597
|
exitOnFailure("spec move", result);
|
|
7539
7598
|
printWarnings("spec move", result.warnings);
|
|
@@ -7679,7 +7738,7 @@ function assertAmendFlags(args) {
|
|
|
7679
7738
|
function splitAliases2(raw) {
|
|
7680
7739
|
return raw === "" ? [] : raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
7681
7740
|
}
|
|
7682
|
-
function fieldsFromArgs(args) {
|
|
7741
|
+
function fieldsFromArgs(args, rawArgs) {
|
|
7683
7742
|
assertAmendFlags(args);
|
|
7684
7743
|
const fields = {};
|
|
7685
7744
|
if (args.text !== undefined)
|
|
@@ -7688,10 +7747,9 @@ function fieldsFromArgs(args) {
|
|
|
7688
7747
|
const v = args.why.trim();
|
|
7689
7748
|
fields.why = v === "" ? null : v;
|
|
7690
7749
|
}
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
fields.livesIn =
|
|
7694
|
-
}
|
|
7750
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7751
|
+
if (livesIn !== undefined)
|
|
7752
|
+
fields.livesIn = livesIn;
|
|
7695
7753
|
if (args.term !== undefined)
|
|
7696
7754
|
fields.term = args.term.trim();
|
|
7697
7755
|
if (args.issue !== undefined && args.issue.trim() !== "")
|
|
@@ -7718,7 +7776,7 @@ var init_amend2 = __esm(() => {
|
|
|
7718
7776
|
platformDir: platformDirArg,
|
|
7719
7777
|
text: { type: "string", description: "New Requirement (statement) field value" },
|
|
7720
7778
|
why: { type: "string", description: "New Why it matters field value" },
|
|
7721
|
-
lives:
|
|
7779
|
+
lives: livesArg,
|
|
7722
7780
|
issue: {
|
|
7723
7781
|
type: "string",
|
|
7724
7782
|
description: "Ticket that motivated this amendment \u2014 recorded as amends-via provenance (opaque, never a requirement id)"
|
|
@@ -7736,7 +7794,7 @@ var init_amend2 = __esm(() => {
|
|
|
7736
7794
|
description: "Revise an unshipped requirement's fields in place (same id, no specVersion bump). Supersede shipped truth instead."
|
|
7737
7795
|
},
|
|
7738
7796
|
args: amendArgs,
|
|
7739
|
-
async run({ args }) {
|
|
7797
|
+
async run({ args, rawArgs }) {
|
|
7740
7798
|
const id = args.id;
|
|
7741
7799
|
if (!ID_RE.test(id)) {
|
|
7742
7800
|
console.error(`spec amend: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
@@ -7749,7 +7807,7 @@ var init_amend2 = __esm(() => {
|
|
|
7749
7807
|
} catch (e) {
|
|
7750
7808
|
handleNotAPlatform(e);
|
|
7751
7809
|
}
|
|
7752
|
-
const fields = fieldsFromArgs(args);
|
|
7810
|
+
const fields = fieldsFromArgs(args, rawArgs);
|
|
7753
7811
|
const result = await amend({ platformDir, id, fields }, coldFreshTags(platformDir));
|
|
7754
7812
|
if (!result.ok)
|
|
7755
7813
|
exitOnFailure("spec amend", result);
|
|
@@ -7763,6 +7821,86 @@ var init_amend2 = __esm(() => {
|
|
|
7763
7821
|
});
|
|
7764
7822
|
});
|
|
7765
7823
|
|
|
7824
|
+
// src/operations/accept.ts
|
|
7825
|
+
async function accept(input) {
|
|
7826
|
+
const { platformDir, id } = input;
|
|
7827
|
+
const located = await locateEntry(platformDir, id);
|
|
7828
|
+
if (!located.ok)
|
|
7829
|
+
return located;
|
|
7830
|
+
const { specPath, relFile, domain: domain2, req } = located;
|
|
7831
|
+
if (req.status.toLowerCase() !== "draft") {
|
|
7832
|
+
return fail("conflict", `${id} is ${displayStatus(req.status)} \u2014 only a Draft entry accepts`);
|
|
7833
|
+
}
|
|
7834
|
+
req.status = "active";
|
|
7835
|
+
domain2.updated = localToday();
|
|
7836
|
+
const res = await validateAndWrite(specPath, domain2, relFile);
|
|
7837
|
+
if (!res.ok) {
|
|
7838
|
+
return fail("invalid_domain_file", res.diagnostics.map((d) => d.detail).join(`
|
|
7839
|
+
`), {
|
|
7840
|
+
diagnostics: res.diagnostics
|
|
7841
|
+
});
|
|
7842
|
+
}
|
|
7843
|
+
return { ok: true, id, file: relFile };
|
|
7844
|
+
}
|
|
7845
|
+
var init_accept = __esm(() => {
|
|
7846
|
+
init_src();
|
|
7847
|
+
init__envelope();
|
|
7848
|
+
init__result();
|
|
7849
|
+
});
|
|
7850
|
+
|
|
7851
|
+
// src/commands/accept.ts
|
|
7852
|
+
var exports_accept = {};
|
|
7853
|
+
__export(exports_accept, {
|
|
7854
|
+
acceptCommand: () => acceptCommand
|
|
7855
|
+
});
|
|
7856
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
7857
|
+
var acceptCommand;
|
|
7858
|
+
var init_accept2 = __esm(() => {
|
|
7859
|
+
init_constants();
|
|
7860
|
+
init_discover();
|
|
7861
|
+
init_accept();
|
|
7862
|
+
init_grammar3();
|
|
7863
|
+
init__args();
|
|
7864
|
+
init__shared();
|
|
7865
|
+
acceptCommand = defineCommand19({
|
|
7866
|
+
meta: {
|
|
7867
|
+
name: "accept",
|
|
7868
|
+
description: "Promote a Draft requirement to Active (same id, fields untouched)"
|
|
7869
|
+
},
|
|
7870
|
+
args: {
|
|
7871
|
+
id: {
|
|
7872
|
+
type: "positional",
|
|
7873
|
+
required: true,
|
|
7874
|
+
description: "Draft requirement id to accept (KEY-NNN)"
|
|
7875
|
+
},
|
|
7876
|
+
platformDir: platformDirArg,
|
|
7877
|
+
json: jsonArg
|
|
7878
|
+
},
|
|
7879
|
+
async run({ args }) {
|
|
7880
|
+
const id = args.id;
|
|
7881
|
+
if (!ID_RE.test(id)) {
|
|
7882
|
+
console.error(`spec accept: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
7883
|
+
process.exit(EXIT.USAGE);
|
|
7884
|
+
return;
|
|
7885
|
+
}
|
|
7886
|
+
const platformDir = resolvePlatformDir(args);
|
|
7887
|
+
try {
|
|
7888
|
+
assertSpecPlatform(platformDir);
|
|
7889
|
+
} catch (e) {
|
|
7890
|
+
handleNotAPlatform(e);
|
|
7891
|
+
}
|
|
7892
|
+
const result = await accept({ platformDir, id });
|
|
7893
|
+
if (!result.ok)
|
|
7894
|
+
exitOnFailure("spec accept", result);
|
|
7895
|
+
if (args.json) {
|
|
7896
|
+
console.log(JSON.stringify({ id, file: result.file }));
|
|
7897
|
+
return;
|
|
7898
|
+
}
|
|
7899
|
+
console.log(`accepted ${id} in ${result.file} \u2014 now Active`);
|
|
7900
|
+
}
|
|
7901
|
+
});
|
|
7902
|
+
});
|
|
7903
|
+
|
|
7766
7904
|
// src/server/authorPrompt.ts
|
|
7767
7905
|
function renderAuthorPrompt(opts) {
|
|
7768
7906
|
const charterBlock = opts.charter ? `
|
|
@@ -7800,7 +7938,7 @@ function errorResult(message) {
|
|
|
7800
7938
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
7801
7939
|
}
|
|
7802
7940
|
function buildMcpServer(platformDir) {
|
|
7803
|
-
const server = new McpServer({ name: "spec", version:
|
|
7941
|
+
const server = new McpServer({ name: "spec", version: package_default.version });
|
|
7804
7942
|
const fresh = (fn) => withIndex({ platformDir, build: "fresh" }, (h) => fn(h.storage));
|
|
7805
7943
|
server.registerTool("spec_query", {
|
|
7806
7944
|
title: "Full-text requirement retrieval",
|
|
@@ -7938,6 +8076,19 @@ function buildMcpServer(platformDir) {
|
|
|
7938
8076
|
const r = await deprecate({ platformDir, id: req_id, reason: trimmed }, coldFreshTags(platformDir));
|
|
7939
8077
|
return r.ok ? jsonResult({ id: r.id, file: r.file, reason: r.reason, sites: r.sites }) : errorResult(r.detail);
|
|
7940
8078
|
});
|
|
8079
|
+
server.registerTool("spec_accept", {
|
|
8080
|
+
title: "Accept a draft requirement",
|
|
8081
|
+
description: "Promote a Draft requirement to Active, leaving every other field untouched. The same JSON as `spec accept --json`.",
|
|
8082
|
+
inputSchema: {
|
|
8083
|
+
req_id: z3.string().describe("The Draft requirement id to accept (KEY-NNN)")
|
|
8084
|
+
}
|
|
8085
|
+
}, async ({ req_id }) => {
|
|
8086
|
+
if (!ID_RE.test(req_id)) {
|
|
8087
|
+
return errorResult(`req_id must be a requirement id (KEY-NNN); got ${req_id}`);
|
|
8088
|
+
}
|
|
8089
|
+
const r = await accept({ platformDir, id: req_id });
|
|
8090
|
+
return r.ok ? jsonResult({ id: r.id, file: r.file }) : errorResult(r.detail);
|
|
8091
|
+
});
|
|
7941
8092
|
server.registerPrompt("author_requirements", {
|
|
7942
8093
|
title: "Author requirements from a brief",
|
|
7943
8094
|
description: "Turn a vague brief/ticket into well-formed Spec Engine requirements: one requirement per testable promise, placed by domain charter, deduped via spec query, drafted to the GUARD template + cold-read rubric, minted with spec req. The engine returns a static playbook template \u2014 your model runs it.",
|
|
@@ -7970,8 +8121,10 @@ function buildMcpServer(platformDir) {
|
|
|
7970
8121
|
}
|
|
7971
8122
|
var init_mcp = __esm(() => {
|
|
7972
8123
|
init_src();
|
|
8124
|
+
init_package();
|
|
7973
8125
|
init_domains();
|
|
7974
8126
|
init__index();
|
|
8127
|
+
init_accept();
|
|
7975
8128
|
init_check();
|
|
7976
8129
|
init_deprecate();
|
|
7977
8130
|
init_nextId();
|
|
@@ -7987,14 +8140,14 @@ __export(exports_mcp, {
|
|
|
7987
8140
|
mcpCommand: () => mcpCommand
|
|
7988
8141
|
});
|
|
7989
8142
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7990
|
-
import { defineCommand as
|
|
8143
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
7991
8144
|
var mcpCommand;
|
|
7992
8145
|
var init_mcp2 = __esm(() => {
|
|
7993
8146
|
init_discover();
|
|
7994
8147
|
init_mcp();
|
|
7995
8148
|
init__args();
|
|
7996
8149
|
init__shared();
|
|
7997
|
-
mcpCommand =
|
|
8150
|
+
mcpCommand = defineCommand20({
|
|
7998
8151
|
meta: {
|
|
7999
8152
|
name: "mcp",
|
|
8000
8153
|
description: "Serve Spec Engine as an MCP (Model Context Protocol) server over stdio \u2014 agent-native query/resolve/check/report tools."
|
|
@@ -8010,7 +8163,7 @@ var init_mcp2 = __esm(() => {
|
|
|
8010
8163
|
handleNotAPlatform(e);
|
|
8011
8164
|
}
|
|
8012
8165
|
const server = buildMcpServer(platformDir);
|
|
8013
|
-
console.error(`spec mcp: serving ${platformDir} over stdio`);
|
|
8166
|
+
console.error(`spec mcp: serving ${platformName(platformDir)} over stdio`);
|
|
8014
8167
|
await server.connect(new StdioServerTransport);
|
|
8015
8168
|
}
|
|
8016
8169
|
});
|
|
@@ -8023,7 +8176,7 @@ __export(exports_resolve, {
|
|
|
8023
8176
|
});
|
|
8024
8177
|
import { statSync as statSync5 } from "fs";
|
|
8025
8178
|
import { join as join12, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
|
|
8026
|
-
import { defineCommand as
|
|
8179
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
8027
8180
|
function classifyArg(a) {
|
|
8028
8181
|
if (a.startsWith("--")) {
|
|
8029
8182
|
const eq = a.indexOf("=");
|
|
@@ -8209,7 +8362,7 @@ var init_resolve2 = __esm(() => {
|
|
|
8209
8362
|
description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
|
|
8210
8363
|
}
|
|
8211
8364
|
};
|
|
8212
|
-
resolveCommand =
|
|
8365
|
+
resolveCommand = defineCommand21({
|
|
8213
8366
|
meta: {
|
|
8214
8367
|
name: "resolve",
|
|
8215
8368
|
description: "Return the requirements tagged in the given files (RSLV-01). Accepts multiple positional file paths (e.g. `spec resolve api/src/renew.ts api/src/charge.ts`) and a comma-split fallback inside a single positional (`spec resolve a.ts,b.ts`). Paths may be platform-relative or absolute under the platform tree."
|
|
@@ -8374,7 +8527,7 @@ __export(exports_gate, {
|
|
|
8374
8527
|
});
|
|
8375
8528
|
import { existsSync as existsSync12 } from "fs";
|
|
8376
8529
|
import { join as join13 } from "path";
|
|
8377
|
-
import { defineCommand as
|
|
8530
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
8378
8531
|
function isEpipe(e) {
|
|
8379
8532
|
if (e === null || typeof e !== "object" || !("code" in e))
|
|
8380
8533
|
return false;
|
|
@@ -8437,7 +8590,7 @@ var init_gate2 = __esm(() => {
|
|
|
8437
8590
|
description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
|
|
8438
8591
|
}
|
|
8439
8592
|
};
|
|
8440
|
-
gateCommand =
|
|
8593
|
+
gateCommand = defineCommand22({
|
|
8441
8594
|
meta: {
|
|
8442
8595
|
name: "gate",
|
|
8443
8596
|
description: "Rung-3 approval primitive: passes iff <reqId> is Active and <repo>'s pinned spec_version covers its changed_at_version. Exits 0 PASS / 1 any gate failure / 2 crash or bad args."
|
|
@@ -8759,14 +8912,14 @@ var exports_guard = {};
|
|
|
8759
8912
|
__export(exports_guard, {
|
|
8760
8913
|
guardCommand: () => guardCommand
|
|
8761
8914
|
});
|
|
8762
|
-
import { defineCommand as
|
|
8915
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
8763
8916
|
var guardCommand;
|
|
8764
8917
|
var init_guard2 = __esm(() => {
|
|
8765
8918
|
init_constants();
|
|
8766
8919
|
init_guard();
|
|
8767
8920
|
init__args();
|
|
8768
8921
|
init__shared();
|
|
8769
|
-
guardCommand =
|
|
8922
|
+
guardCommand = defineCommand23({
|
|
8770
8923
|
meta: {
|
|
8771
8924
|
name: "guard",
|
|
8772
8925
|
description: "Loss detection: diff the requirement derivation at a git ref (default HEAD) against the working tree and block requirements about to be steamrolled."
|
|
@@ -9099,6 +9252,18 @@ code {
|
|
|
9099
9252
|
display: block;
|
|
9100
9253
|
}
|
|
9101
9254
|
|
|
9255
|
+
.sidebar-platform {
|
|
9256
|
+
margin-left: auto;
|
|
9257
|
+
min-width: 0;
|
|
9258
|
+
overflow: hidden;
|
|
9259
|
+
text-overflow: ellipsis;
|
|
9260
|
+
white-space: nowrap;
|
|
9261
|
+
font-family: var(--font-mono);
|
|
9262
|
+
font-weight: 500;
|
|
9263
|
+
font-size: 0.75rem;
|
|
9264
|
+
color: var(--fg-dim);
|
|
9265
|
+
}
|
|
9266
|
+
|
|
9102
9267
|
.sidebar-scroll {
|
|
9103
9268
|
flex: 1;
|
|
9104
9269
|
overflow-y: auto;
|
|
@@ -10705,9 +10870,9 @@ var init_coverage = __esm(() => {
|
|
|
10705
10870
|
// ../webapp/src/pages/editor.ts
|
|
10706
10871
|
import { bodyLimit } from "hono/body-limit";
|
|
10707
10872
|
import { html as html5, raw as raw4 } from "hono/html";
|
|
10708
|
-
function crossOriginRejected(c) {
|
|
10873
|
+
function crossOriginRejected(c, bindHost) {
|
|
10709
10874
|
const selfUrl = new URL(c.req.url);
|
|
10710
|
-
if (!
|
|
10875
|
+
if (!isServedHostname(selfUrl.hostname, bindHost))
|
|
10711
10876
|
return true;
|
|
10712
10877
|
const origin = c.req.header("origin");
|
|
10713
10878
|
if (origin === undefined)
|
|
@@ -10765,7 +10930,7 @@ async function renderResult(res) {
|
|
|
10765
10930
|
<p><a href="/editor">Back to editor</a></p>
|
|
10766
10931
|
`);
|
|
10767
10932
|
}
|
|
10768
|
-
function mountEditor(app) {
|
|
10933
|
+
function mountEditor(app, bindHost = DEFAULT_BIND_HOST) {
|
|
10769
10934
|
app.get("/editor", (c) => {
|
|
10770
10935
|
if (!featureEnabled("editor"))
|
|
10771
10936
|
return c.html(comingSoonDoc("editor", "Editor"));
|
|
@@ -10825,7 +10990,7 @@ function mountEditor(app) {
|
|
|
10825
10990
|
app.post("/editor/create", editorBodyLimit, async (c) => {
|
|
10826
10991
|
if (!featureEnabled("editor"))
|
|
10827
10992
|
return c.html(rejectPage(featureDisabledMessage("editor")), 404);
|
|
10828
|
-
if (crossOriginRejected(c))
|
|
10993
|
+
if (crossOriginRejected(c, bindHost))
|
|
10829
10994
|
return c.html(rejectPage("cross-origin request rejected"), 403);
|
|
10830
10995
|
const form = await c.req.parseBody();
|
|
10831
10996
|
const statement = field(form, "statement");
|
|
@@ -10849,7 +11014,7 @@ function mountEditor(app) {
|
|
|
10849
11014
|
app.post("/editor/amend", editorBodyLimit, async (c) => {
|
|
10850
11015
|
if (!featureEnabled("editor"))
|
|
10851
11016
|
return c.html(rejectPage(featureDisabledMessage("editor")), 404);
|
|
10852
|
-
if (crossOriginRejected(c))
|
|
11017
|
+
if (crossOriginRejected(c, bindHost))
|
|
10853
11018
|
return c.html(rejectPage("cross-origin request rejected"), 403);
|
|
10854
11019
|
const form = await c.req.parseBody();
|
|
10855
11020
|
const id = field(form, "id");
|
|
@@ -11603,12 +11768,27 @@ var init_setup = __esm(() => {
|
|
|
11603
11768
|
|
|
11604
11769
|
// ../webapp/src/server.ts
|
|
11605
11770
|
import { Hono } from "hono";
|
|
11771
|
+
import { html as htmlTemplate } from "hono/html";
|
|
11606
11772
|
function createApp() {
|
|
11607
11773
|
const app = new Hono;
|
|
11608
11774
|
app.get("/", (c) => c.html(index_html_default));
|
|
11609
11775
|
return app;
|
|
11610
11776
|
}
|
|
11611
|
-
function
|
|
11777
|
+
function labelPlatform(app, platformName2) {
|
|
11778
|
+
const name = htmlTemplate`${platformName2}`.toString();
|
|
11779
|
+
app.use("*", async (c, next) => {
|
|
11780
|
+
await next();
|
|
11781
|
+
if (!c.res.headers.get("content-type")?.startsWith("text/html"))
|
|
11782
|
+
return;
|
|
11783
|
+
const body = (await c.res.text()).replace("<title>Spec Engine", `<title>${name} \xB7 Spec Engine`).replace("<span>Spec Engine</span></a>", `<span>Spec Engine</span><span class="sidebar-platform">${name}</span></a>`);
|
|
11784
|
+
const headers = new Headers(c.res.headers);
|
|
11785
|
+
headers.delete("content-length");
|
|
11786
|
+
c.res = new Response(body, { status: c.res.status, headers });
|
|
11787
|
+
});
|
|
11788
|
+
}
|
|
11789
|
+
function mountWebapp(app, bindHost = DEFAULT_BIND_HOST, platformName2) {
|
|
11790
|
+
if (platformName2 !== undefined)
|
|
11791
|
+
labelPlatform(app, platformName2);
|
|
11612
11792
|
registerErrorBoundary(app);
|
|
11613
11793
|
mountCoverage(app);
|
|
11614
11794
|
mountRequirements(app);
|
|
@@ -11616,13 +11796,14 @@ function mountWebapp(app) {
|
|
|
11616
11796
|
mountQuery(app);
|
|
11617
11797
|
mountRelations(app);
|
|
11618
11798
|
mountProvenance(app);
|
|
11619
|
-
mountEditor(app);
|
|
11799
|
+
mountEditor(app, bindHost);
|
|
11620
11800
|
mountSetup(app);
|
|
11621
11801
|
mountGlossary(app);
|
|
11622
11802
|
mountLogs(app);
|
|
11623
11803
|
return app;
|
|
11624
11804
|
}
|
|
11625
11805
|
var init_server = __esm(() => {
|
|
11806
|
+
init_src();
|
|
11626
11807
|
init_index_html();
|
|
11627
11808
|
init_coverage();
|
|
11628
11809
|
init_data();
|
|
@@ -11672,9 +11853,9 @@ function withWriteLock(fn) {
|
|
|
11672
11853
|
writeChain = run.catch(() => {});
|
|
11673
11854
|
return run;
|
|
11674
11855
|
}
|
|
11675
|
-
function rejectCrossOrigin(c) {
|
|
11856
|
+
function rejectCrossOrigin(c, bindHost) {
|
|
11676
11857
|
const selfUrl = new URL(c.req.url);
|
|
11677
|
-
if (!
|
|
11858
|
+
if (!isServedHostname(selfUrl.hostname, bindHost)) {
|
|
11678
11859
|
return c.json({ error: "cross-origin request rejected" }, 403);
|
|
11679
11860
|
}
|
|
11680
11861
|
const origin = c.req.header("origin");
|
|
@@ -11730,6 +11911,15 @@ function toLivesIn(v) {
|
|
|
11730
11911
|
return [v];
|
|
11731
11912
|
return [];
|
|
11732
11913
|
}
|
|
11914
|
+
function mintFieldsFromBody(body) {
|
|
11915
|
+
return {
|
|
11916
|
+
statement: typeof body.statement === "string" ? body.statement : "",
|
|
11917
|
+
why: typeof body.why === "string" ? body.why : "",
|
|
11918
|
+
livesIn: toLivesIn(body.livesIn),
|
|
11919
|
+
issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined,
|
|
11920
|
+
status: body.status === "draft" ? "draft" : "active"
|
|
11921
|
+
};
|
|
11922
|
+
}
|
|
11733
11923
|
function amendFieldsFromBody(body) {
|
|
11734
11924
|
const fields = {};
|
|
11735
11925
|
if (typeof body.statement === "string")
|
|
@@ -11768,7 +11958,7 @@ function parseQueryLimit2(c, rawLimit) {
|
|
|
11768
11958
|
}
|
|
11769
11959
|
return { ok: true, limit };
|
|
11770
11960
|
}
|
|
11771
|
-
function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
11961
|
+
function mountApi(app, storage2, platformDir = process.cwd(), bindHost = DEFAULT_BIND_HOST) {
|
|
11772
11962
|
app.use("/api/*", async (c, next) => {
|
|
11773
11963
|
c.header("Cache-Control", "no-store");
|
|
11774
11964
|
await next();
|
|
@@ -11777,7 +11967,12 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11777
11967
|
app.get("/api/repos", guarded((c) => c.json(storage2.listRepos())));
|
|
11778
11968
|
app.get("/api/platform", async (c) => {
|
|
11779
11969
|
const version = await derivePlatformVersion(platformDir);
|
|
11780
|
-
const info = {
|
|
11970
|
+
const info = {
|
|
11971
|
+
version,
|
|
11972
|
+
name: platformName(platformDir),
|
|
11973
|
+
source: "derived",
|
|
11974
|
+
mode: platformMode(platformDir)
|
|
11975
|
+
};
|
|
11781
11976
|
return c.json(info);
|
|
11782
11977
|
});
|
|
11783
11978
|
app.get("/api/report", guarded((c) => c.json(coverageReport(storage2).rows)));
|
|
@@ -11799,7 +11994,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11799
11994
|
if (!featureEnabled("editor")) {
|
|
11800
11995
|
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
11801
11996
|
}
|
|
11802
|
-
const originReject = rejectCrossOrigin(c);
|
|
11997
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
11803
11998
|
if (originReject)
|
|
11804
11999
|
return originReject;
|
|
11805
12000
|
const headerReject = rejectBadWriteHeaders(c);
|
|
@@ -11814,14 +12009,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11814
12009
|
return c.json({ error: "unknown domain key" }, 400);
|
|
11815
12010
|
}
|
|
11816
12011
|
return withWriteLock(async () => {
|
|
11817
|
-
const r = await mint({
|
|
11818
|
-
platformDir,
|
|
11819
|
-
key,
|
|
11820
|
-
statement: typeof body.statement === "string" ? body.statement : "",
|
|
11821
|
-
why: typeof body.why === "string" ? body.why : "",
|
|
11822
|
-
livesIn: toLivesIn(body.livesIn),
|
|
11823
|
-
issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined
|
|
11824
|
-
});
|
|
12012
|
+
const r = await mint({ platformDir, key, ...mintFieldsFromBody(body) });
|
|
11825
12013
|
if (!r.ok)
|
|
11826
12014
|
return failureResponse(c, r);
|
|
11827
12015
|
await runIndex({ platformDir, storage: storage2 });
|
|
@@ -11832,7 +12020,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11832
12020
|
if (!featureEnabled("editor")) {
|
|
11833
12021
|
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
11834
12022
|
}
|
|
11835
|
-
const originReject = rejectCrossOrigin(c);
|
|
12023
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
11836
12024
|
if (originReject)
|
|
11837
12025
|
return originReject;
|
|
11838
12026
|
const headerReject = rejectBadWriteHeaders(c);
|
|
@@ -11862,7 +12050,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11862
12050
|
if (!featureEnabled("editor")) {
|
|
11863
12051
|
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
11864
12052
|
}
|
|
11865
|
-
const originReject = rejectCrossOrigin(c);
|
|
12053
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
11866
12054
|
if (originReject)
|
|
11867
12055
|
return originReject;
|
|
11868
12056
|
const headerReject = rejectBadWriteHeaders(c);
|
|
@@ -11906,7 +12094,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11906
12094
|
if (!featureEnabled("editor")) {
|
|
11907
12095
|
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
11908
12096
|
}
|
|
11909
|
-
const originReject = rejectCrossOrigin(c);
|
|
12097
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
11910
12098
|
if (originReject)
|
|
11911
12099
|
return originReject;
|
|
11912
12100
|
const headerReject = rejectBadWriteHeaders(c);
|
|
@@ -11932,6 +12120,30 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11932
12120
|
return c.json({ ok: true, id: r.id, file: r.file, reason: r.reason, sites: r.sites }, 200);
|
|
11933
12121
|
});
|
|
11934
12122
|
}));
|
|
12123
|
+
app.post("/api/requirements/:id/accept", guarded(async function acceptRequirement(c) {
|
|
12124
|
+
if (!featureEnabled("editor")) {
|
|
12125
|
+
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
12126
|
+
}
|
|
12127
|
+
const originReject = rejectCrossOrigin(c, bindHost);
|
|
12128
|
+
if (originReject)
|
|
12129
|
+
return originReject;
|
|
12130
|
+
const headerReject = rejectBadWriteHeaders(c);
|
|
12131
|
+
if (headerReject)
|
|
12132
|
+
return headerReject;
|
|
12133
|
+
const parsed = await readJsonBody(c);
|
|
12134
|
+
if (!parsed.ok)
|
|
12135
|
+
return parsed.res;
|
|
12136
|
+
const id = c.req.param("id") ?? "";
|
|
12137
|
+
if (!ID_RE.test(id))
|
|
12138
|
+
return c.json({ error: "id must be a requirement id (KEY-NNN)" }, 400);
|
|
12139
|
+
return withWriteLock(async () => {
|
|
12140
|
+
const r = await accept({ platformDir, id });
|
|
12141
|
+
if (!r.ok)
|
|
12142
|
+
return failureResponse(c, r);
|
|
12143
|
+
await runIndex({ platformDir, storage: storage2 });
|
|
12144
|
+
return c.json({ ok: true, id: r.id, file: r.file }, 200);
|
|
12145
|
+
});
|
|
12146
|
+
}));
|
|
11935
12147
|
app.get("/api/propagation/:id", guarded((c) => c.json(propagation2(storage2, c.req.param("id") ?? "").rows)));
|
|
11936
12148
|
app.get("/api/query", guarded((c) => {
|
|
11937
12149
|
if (!featureEnabled("query")) {
|
|
@@ -12012,6 +12224,7 @@ var init_api = __esm(() => {
|
|
|
12012
12224
|
init_discover();
|
|
12013
12225
|
init_pipeline();
|
|
12014
12226
|
init__result();
|
|
12227
|
+
init_accept();
|
|
12015
12228
|
init_amend();
|
|
12016
12229
|
init_deprecate();
|
|
12017
12230
|
init_mint();
|
|
@@ -12025,22 +12238,162 @@ var init_api = __esm(() => {
|
|
|
12025
12238
|
writeChain = Promise.resolve();
|
|
12026
12239
|
});
|
|
12027
12240
|
|
|
12241
|
+
// src/server/lan.ts
|
|
12242
|
+
import { createHash as createHash2, randomBytes, timingSafeEqual } from "crypto";
|
|
12243
|
+
import { isIP } from "net";
|
|
12244
|
+
function ipv4Prefix(ip) {
|
|
12245
|
+
const parts = ip.split(".");
|
|
12246
|
+
if (parts.length !== 4 || parts.some((p) => String(Number(p)) !== p))
|
|
12247
|
+
return null;
|
|
12248
|
+
return [Number(parts[0]), Number(parts[1])];
|
|
12249
|
+
}
|
|
12250
|
+
function isLoopbackAddress(ip) {
|
|
12251
|
+
if (ip === "::1")
|
|
12252
|
+
return true;
|
|
12253
|
+
return ipv4Prefix(ip)?.[0] === 127;
|
|
12254
|
+
}
|
|
12255
|
+
function isPrivateIPv4([a, b]) {
|
|
12256
|
+
if (a === 127 || a === 10)
|
|
12257
|
+
return true;
|
|
12258
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
12259
|
+
return true;
|
|
12260
|
+
if (a === 192 && b === 168)
|
|
12261
|
+
return true;
|
|
12262
|
+
return a === 100 && b >= 64 && b <= 127;
|
|
12263
|
+
}
|
|
12264
|
+
function bindHostRefusal(raw11) {
|
|
12265
|
+
const kind = isIP(raw11);
|
|
12266
|
+
if (kind === 0)
|
|
12267
|
+
return `--host must be a literal IP address, not "${raw11}"`;
|
|
12268
|
+
if (kind === 6) {
|
|
12269
|
+
return raw11 === "::1" ? null : `--host ${raw11}: the only IPv6 address accepted is ::1`;
|
|
12270
|
+
}
|
|
12271
|
+
const prefix = ipv4Prefix(raw11);
|
|
12272
|
+
if (prefix === null || !isPrivateIPv4(prefix)) {
|
|
12273
|
+
return `--host ${raw11} is not a loopback, RFC 1918, or 100.64.0.0/10 address`;
|
|
12274
|
+
}
|
|
12275
|
+
return null;
|
|
12276
|
+
}
|
|
12277
|
+
function urlHost(ip, port) {
|
|
12278
|
+
return `${isIP(ip) === 6 ? `[${ip}]` : ip}:${port}`;
|
|
12279
|
+
}
|
|
12280
|
+
function generateToken() {
|
|
12281
|
+
return randomBytes(32).toString("hex");
|
|
12282
|
+
}
|
|
12283
|
+
function pinnedTokenRefusal(token) {
|
|
12284
|
+
if (!/^[A-Za-z0-9_-]+$/.test(token))
|
|
12285
|
+
return "the access token may only contain A-Z a-z 0-9 _ -";
|
|
12286
|
+
if (token.length < MIN_PINNED_TOKEN_LENGTH) {
|
|
12287
|
+
return `the access token must be at least ${MIN_PINNED_TOKEN_LENGTH} characters`;
|
|
12288
|
+
}
|
|
12289
|
+
return null;
|
|
12290
|
+
}
|
|
12291
|
+
function digest(value) {
|
|
12292
|
+
return createHash2("sha256").update(value).digest();
|
|
12293
|
+
}
|
|
12294
|
+
function tokenMatches(candidate, token) {
|
|
12295
|
+
if (!candidate)
|
|
12296
|
+
return false;
|
|
12297
|
+
return timingSafeEqual(digest(candidate), digest(token));
|
|
12298
|
+
}
|
|
12299
|
+
function cookieValue(header, name) {
|
|
12300
|
+
for (const pair of (header ?? "").split(";")) {
|
|
12301
|
+
const eq = pair.indexOf("=");
|
|
12302
|
+
if (eq !== -1 && pair.slice(0, eq).trim() === name)
|
|
12303
|
+
return pair.slice(eq + 1).trim();
|
|
12304
|
+
}
|
|
12305
|
+
return null;
|
|
12306
|
+
}
|
|
12307
|
+
function isWrite(req, url) {
|
|
12308
|
+
if (req.method !== "GET" && req.method !== "HEAD")
|
|
12309
|
+
return true;
|
|
12310
|
+
if (url.pathname === "/provenance")
|
|
12311
|
+
return true;
|
|
12312
|
+
return url.pathname === "/api/provenance" && url.searchParams.has("resolve");
|
|
12313
|
+
}
|
|
12314
|
+
function plain(status, body, headers = {}) {
|
|
12315
|
+
return new Response(`${body}
|
|
12316
|
+
`, {
|
|
12317
|
+
status,
|
|
12318
|
+
headers: {
|
|
12319
|
+
"content-type": "text/plain; charset=utf-8",
|
|
12320
|
+
"cache-control": "no-store",
|
|
12321
|
+
...headers
|
|
12322
|
+
}
|
|
12323
|
+
});
|
|
12324
|
+
}
|
|
12325
|
+
function lanGate(fetch2, { bindHost, token, allowWrites }) {
|
|
12326
|
+
return (req) => {
|
|
12327
|
+
const url = new URL(req.url);
|
|
12328
|
+
if (!isServedHostname(url.hostname, bindHost))
|
|
12329
|
+
return plain(403, "host rejected");
|
|
12330
|
+
const queryToken = url.searchParams.get("token");
|
|
12331
|
+
if (queryToken !== null) {
|
|
12332
|
+
if (!tokenMatches(queryToken, token))
|
|
12333
|
+
return plain(401, "invalid access token");
|
|
12334
|
+
url.searchParams.delete("token");
|
|
12335
|
+
return plain(302, "authenticated", {
|
|
12336
|
+
location: `${url.pathname}${url.search}`,
|
|
12337
|
+
"set-cookie": `${TOKEN_COOKIE}=${token}; Path=/; HttpOnly; SameSite=Strict`
|
|
12338
|
+
});
|
|
12339
|
+
}
|
|
12340
|
+
if (!tokenMatches(cookieValue(req.headers.get("cookie"), TOKEN_COOKIE), token)) {
|
|
12341
|
+
return plain(401, "access token required: open the tokenized URL spec serve printed");
|
|
12342
|
+
}
|
|
12343
|
+
if (!allowWrites && isWrite(req, url)) {
|
|
12344
|
+
return plain(403, "read-only: restart spec serve with --allow-writes to permit changes");
|
|
12345
|
+
}
|
|
12346
|
+
return fetch2(req);
|
|
12347
|
+
};
|
|
12348
|
+
}
|
|
12349
|
+
var TOKEN_COOKIE = "spec_token", MIN_PINNED_TOKEN_LENGTH = 16;
|
|
12350
|
+
var init_lan = __esm(() => {
|
|
12351
|
+
init_src();
|
|
12352
|
+
});
|
|
12353
|
+
|
|
12028
12354
|
// src/commands/serve.ts
|
|
12029
12355
|
var exports_serve = {};
|
|
12030
12356
|
__export(exports_serve, {
|
|
12031
12357
|
serveCommand: () => serveCommand,
|
|
12358
|
+
resolveBind: () => resolveBind,
|
|
12032
12359
|
composeServeApp: () => composeServeApp
|
|
12033
12360
|
});
|
|
12034
12361
|
import { existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
12035
12362
|
import { dirname as dirname8 } from "path";
|
|
12036
|
-
import { defineCommand as
|
|
12363
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
12037
12364
|
import { Hono as Hono2 } from "hono";
|
|
12038
|
-
function composeServeApp(storage2, platformDir = process.cwd()) {
|
|
12365
|
+
function composeServeApp(storage2, platformDir = process.cwd(), bindHost = DEFAULT_BIND_HOST) {
|
|
12039
12366
|
const app = new Hono2;
|
|
12040
|
-
mountApi(app, storage2, platformDir);
|
|
12041
|
-
mountWebapp(app);
|
|
12367
|
+
mountApi(app, storage2, platformDir, bindHost);
|
|
12368
|
+
mountWebapp(app, bindHost, platformName(platformDir));
|
|
12042
12369
|
return app;
|
|
12043
12370
|
}
|
|
12371
|
+
function usageError(message) {
|
|
12372
|
+
console.error(`spec serve: ${message}`);
|
|
12373
|
+
process.exit(EXIT.USAGE);
|
|
12374
|
+
}
|
|
12375
|
+
function resolveBind(args) {
|
|
12376
|
+
const host = args.host ?? DEFAULT_BIND_HOST;
|
|
12377
|
+
const hostRefusal = bindHostRefusal(host);
|
|
12378
|
+
if (hostRefusal !== null)
|
|
12379
|
+
usageError(hostRefusal);
|
|
12380
|
+
if (isLoopbackAddress(host))
|
|
12381
|
+
return { host, lan: null };
|
|
12382
|
+
const pinned = args.token ?? process.env.SPEC_SERVE_TOKEN;
|
|
12383
|
+
if (pinned !== undefined) {
|
|
12384
|
+
const tokenRefusal = pinnedTokenRefusal(pinned);
|
|
12385
|
+
if (tokenRefusal !== null)
|
|
12386
|
+
usageError(tokenRefusal);
|
|
12387
|
+
}
|
|
12388
|
+
return {
|
|
12389
|
+
host,
|
|
12390
|
+
lan: {
|
|
12391
|
+
bindHost: host,
|
|
12392
|
+
token: pinned ?? generateToken(),
|
|
12393
|
+
allowWrites: args.allowWrites === true
|
|
12394
|
+
}
|
|
12395
|
+
};
|
|
12396
|
+
}
|
|
12044
12397
|
async function runProbe() {
|
|
12045
12398
|
const app = createApp();
|
|
12046
12399
|
const server = Bun.serve({
|
|
@@ -12073,26 +12426,41 @@ async function runProbe() {
|
|
|
12073
12426
|
}
|
|
12074
12427
|
var serveCommand;
|
|
12075
12428
|
var init_serve = __esm(() => {
|
|
12429
|
+
init_src();
|
|
12076
12430
|
init_server();
|
|
12077
12431
|
init_constants();
|
|
12078
12432
|
init_discover();
|
|
12079
12433
|
init_pipeline();
|
|
12080
12434
|
init_prompt();
|
|
12081
12435
|
init_api();
|
|
12436
|
+
init_lan();
|
|
12082
12437
|
init_sqlite();
|
|
12083
12438
|
init__args();
|
|
12084
12439
|
init__shared();
|
|
12085
|
-
serveCommand =
|
|
12440
|
+
serveCommand = defineCommand24({
|
|
12086
12441
|
meta: {
|
|
12087
12442
|
name: "serve",
|
|
12088
|
-
description: "Run the local webapp. Binds 127.0.0.1 on --port
|
|
12443
|
+
description: "Run the local webapp. Binds 127.0.0.1 on --port, or one private --host address behind an access token. --probe is the SERV-04 asset-embedding smoke."
|
|
12089
12444
|
},
|
|
12090
12445
|
args: {
|
|
12091
12446
|
platformDir: platformDirArg,
|
|
12092
12447
|
port: {
|
|
12093
12448
|
type: "string",
|
|
12094
12449
|
default: "4000",
|
|
12095
|
-
description: "Port (default 4000; 0 = ephemeral).
|
|
12450
|
+
description: "Port (default 4000; 0 = ephemeral)."
|
|
12451
|
+
},
|
|
12452
|
+
host: {
|
|
12453
|
+
type: "string",
|
|
12454
|
+
default: DEFAULT_BIND_HOST,
|
|
12455
|
+
description: "Address to bind (default 127.0.0.1). Only a literal loopback, RFC 1918, or 100.64.0.0/10 IP; a non-loopback address requires the printed access token and is read-only."
|
|
12456
|
+
},
|
|
12457
|
+
token: {
|
|
12458
|
+
type: "string",
|
|
12459
|
+
description: "Pin the access token for a non-loopback --host (else SPEC_SERVE_TOKEN, else random). At least 16 of A-Z a-z 0-9 _ -."
|
|
12460
|
+
},
|
|
12461
|
+
allowWrites: {
|
|
12462
|
+
type: "boolean",
|
|
12463
|
+
description: "On a non-loopback --host, permit editor writes and tracker resolution."
|
|
12096
12464
|
},
|
|
12097
12465
|
out: {
|
|
12098
12466
|
type: "string",
|
|
@@ -12100,7 +12468,7 @@ var init_serve = __esm(() => {
|
|
|
12100
12468
|
},
|
|
12101
12469
|
probe: {
|
|
12102
12470
|
type: "boolean",
|
|
12103
|
-
description: "Smoke test: bind ephemeral port on 127.0.0.1, GET /, assert body contains placeholder, exit."
|
|
12471
|
+
description: "Smoke test: bind ephemeral port on 127.0.0.1 (ignores --host), GET /, assert body contains placeholder, exit."
|
|
12104
12472
|
},
|
|
12105
12473
|
noPrompt: {
|
|
12106
12474
|
type: "boolean",
|
|
@@ -12124,6 +12492,7 @@ var init_serve = __esm(() => {
|
|
|
12124
12492
|
process.exit(EXIT.USAGE);
|
|
12125
12493
|
return;
|
|
12126
12494
|
}
|
|
12495
|
+
const { host, lan } = resolveBind(args);
|
|
12127
12496
|
const platformDir = resolvePlatformDir(args);
|
|
12128
12497
|
const outArg2 = args.out;
|
|
12129
12498
|
const dbPath = resolveDbPath(platformDir, outArg2);
|
|
@@ -12154,20 +12523,26 @@ var init_serve = __esm(() => {
|
|
|
12154
12523
|
if (needsIndex || storage2.listRepos().length === 0) {
|
|
12155
12524
|
await runIndex({ platformDir, storage: storage2 });
|
|
12156
12525
|
}
|
|
12157
|
-
const app = composeServeApp(storage2, platformDir);
|
|
12526
|
+
const app = composeServeApp(storage2, platformDir, host);
|
|
12158
12527
|
server = Bun.serve({
|
|
12159
12528
|
port,
|
|
12160
|
-
hostname:
|
|
12161
|
-
fetch: app.fetch
|
|
12529
|
+
hostname: host,
|
|
12530
|
+
fetch: lan === null ? app.fetch : lanGate(app.fetch, lan)
|
|
12162
12531
|
});
|
|
12163
12532
|
} catch (err) {
|
|
12164
12533
|
storage2.close();
|
|
12165
12534
|
handleStorageUnavailable(err, dbPath);
|
|
12166
|
-
console.error(`spec serve: failed to start on
|
|
12535
|
+
console.error(`spec serve: failed to start on ${urlHost(host, port)}:`, err);
|
|
12167
12536
|
process.exit(EXIT.FAILURE);
|
|
12168
12537
|
return;
|
|
12169
12538
|
}
|
|
12170
|
-
|
|
12539
|
+
const serving = `spec: serving ${platformName(platformDir)} on ${server.url.origin}`;
|
|
12540
|
+
if (lan === null) {
|
|
12541
|
+
console.log(serving);
|
|
12542
|
+
return;
|
|
12543
|
+
}
|
|
12544
|
+
console.log(`${serving}/?token=${lan.token}`);
|
|
12545
|
+
console.log(lan.allowWrites ? "spec: writes ALLOWED (--allow-writes)" : "spec: read-only");
|
|
12171
12546
|
}
|
|
12172
12547
|
});
|
|
12173
12548
|
});
|
|
@@ -12185,7 +12560,7 @@ __export(exports_docs, {
|
|
|
12185
12560
|
import { existsSync as existsSync14, statSync as statSync6 } from "fs";
|
|
12186
12561
|
import { join as join15, normalize, sep as sep4 } from "path";
|
|
12187
12562
|
import { fileURLToPath } from "url";
|
|
12188
|
-
import { defineCommand as
|
|
12563
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
12189
12564
|
function resolveDocsRootFrom(candidates) {
|
|
12190
12565
|
for (const candidate of candidates) {
|
|
12191
12566
|
if (existsSync14(join15(candidate, "index.html")))
|
|
@@ -12263,7 +12638,7 @@ function exitNoDocsRoot() {
|
|
|
12263
12638
|
var docsCommand;
|
|
12264
12639
|
var init_docs2 = __esm(() => {
|
|
12265
12640
|
init_constants();
|
|
12266
|
-
docsCommand =
|
|
12641
|
+
docsCommand = defineCommand25({
|
|
12267
12642
|
meta: {
|
|
12268
12643
|
name: "docs",
|
|
12269
12644
|
description: "Serve the bundled documentation site offline. Binds 127.0.0.1 on --port (loopback only \u2014 no --host). --probe is the boot-and-fetch smoke."
|
|
@@ -12324,10 +12699,10 @@ __export(exports___smoke, {
|
|
|
12324
12699
|
schemaSmokeCommand: () => schemaSmokeCommand,
|
|
12325
12700
|
schemaMismatchSmokeCommand: () => schemaMismatchSmokeCommand
|
|
12326
12701
|
});
|
|
12327
|
-
import { defineCommand as
|
|
12702
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
12328
12703
|
var schemaSmokeCommand, schemaMismatchSmokeCommand;
|
|
12329
12704
|
var init___smoke = __esm(() => {
|
|
12330
|
-
schemaSmokeCommand =
|
|
12705
|
+
schemaSmokeCommand = defineCommand26({
|
|
12331
12706
|
meta: {
|
|
12332
12707
|
name: "__schema-smoke",
|
|
12333
12708
|
description: "(hidden CI) open a fresh DB, exec DDL, assert the schema shape",
|
|
@@ -12370,7 +12745,7 @@ var init___smoke = __esm(() => {
|
|
|
12370
12745
|
console.log("__schema-smoke OK");
|
|
12371
12746
|
}
|
|
12372
12747
|
});
|
|
12373
|
-
schemaMismatchSmokeCommand =
|
|
12748
|
+
schemaMismatchSmokeCommand = defineCommand26({
|
|
12374
12749
|
meta: {
|
|
12375
12750
|
name: "__schema-mismatch-smoke",
|
|
12376
12751
|
description: "(hidden CI) corrupt _schema_version, re-open, assert silent rebuild",
|
|
@@ -12396,8 +12771,8 @@ var init___smoke = __esm(() => {
|
|
|
12396
12771
|
|
|
12397
12772
|
// src/cli.ts
|
|
12398
12773
|
init_package();
|
|
12399
|
-
import { defineCommand as
|
|
12400
|
-
var main =
|
|
12774
|
+
import { defineCommand as defineCommand27, runMain, showUsage } from "citty";
|
|
12775
|
+
var main = defineCommand27({
|
|
12401
12776
|
meta: {
|
|
12402
12777
|
name: "spec",
|
|
12403
12778
|
version: package_default.version,
|
|
@@ -12422,6 +12797,7 @@ var main = defineCommand26({
|
|
|
12422
12797
|
deprecate: () => Promise.resolve().then(() => (init_deprecate2(), exports_deprecate)).then((m) => m.deprecateCommand),
|
|
12423
12798
|
move: () => Promise.resolve().then(() => (init_move2(), exports_move)).then((m) => m.moveCommand),
|
|
12424
12799
|
amend: () => Promise.resolve().then(() => (init_amend2(), exports_amend)).then((m) => m.amendCommand),
|
|
12800
|
+
accept: () => Promise.resolve().then(() => (init_accept2(), exports_accept)).then((m) => m.acceptCommand),
|
|
12425
12801
|
mcp: () => Promise.resolve().then(() => (init_mcp2(), exports_mcp)).then((m) => m.mcpCommand),
|
|
12426
12802
|
resolve: () => Promise.resolve().then(() => (init_resolve2(), exports_resolve)).then((m) => m.resolveCommand),
|
|
12427
12803
|
gate: () => Promise.resolve().then(() => (init_gate2(), exports_gate)).then((m) => m.gateCommand),
|