@wairon/cli 5.1.1-dev.32 → 5.1.1-dev.34
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 +155 -63
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +145 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.34";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -23143,6 +23143,7 @@ __export(specs_exports, {
|
|
|
23143
23143
|
setExecutionTier: () => setExecutionTier,
|
|
23144
23144
|
setProjectType: () => setProjectType,
|
|
23145
23145
|
snapshotSpecFiles: () => snapshotSpecFiles,
|
|
23146
|
+
specChanges: () => specChanges,
|
|
23146
23147
|
specPathsInScope: () => specPathsInScope,
|
|
23147
23148
|
updateSpec: () => updateSpec,
|
|
23148
23149
|
upsertPackSelection: () => upsertPackSelection,
|
|
@@ -23526,6 +23527,92 @@ function computeSpecTreeSignature(dirs) {
|
|
|
23526
23527
|
}
|
|
23527
23528
|
return parts.join("|");
|
|
23528
23529
|
}
|
|
23530
|
+
function summarizeValue(value) {
|
|
23531
|
+
if (value === void 0) return "";
|
|
23532
|
+
const text2 = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
|
|
23533
|
+
return text2.length > 120 ? `${text2.slice(0, 117)}...` : text2;
|
|
23534
|
+
}
|
|
23535
|
+
function identityKeyOf(field, item) {
|
|
23536
|
+
if (item === null || typeof item !== "object") return null;
|
|
23537
|
+
const o = item;
|
|
23538
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
23539
|
+
switch (field) {
|
|
23540
|
+
case "dispatch":
|
|
23541
|
+
return str(o.capability);
|
|
23542
|
+
case "lifecycle":
|
|
23543
|
+
return `${String(o.phase)} ${String(o.component)} ${String(o.method)}`;
|
|
23544
|
+
case "emits":
|
|
23545
|
+
case "subscribesTo":
|
|
23546
|
+
return `${String(o.topic)} ${o.event === void 0 ? "" : String(o.event)}`;
|
|
23547
|
+
case "trustedLinks":
|
|
23548
|
+
return str(o.subsystem);
|
|
23549
|
+
case "allow":
|
|
23550
|
+
return str(o.code);
|
|
23551
|
+
// lint.allow
|
|
23552
|
+
case "findings":
|
|
23553
|
+
return str(o.code);
|
|
23554
|
+
// an interface method's findings
|
|
23555
|
+
case "invariants":
|
|
23556
|
+
return str(o.id);
|
|
23557
|
+
case "patterns":
|
|
23558
|
+
return str(o.id);
|
|
23559
|
+
case "boundaries":
|
|
23560
|
+
return str(o.name);
|
|
23561
|
+
case "globalRequirements":
|
|
23562
|
+
return str(o.description);
|
|
23563
|
+
case "databases":
|
|
23564
|
+
return str(o.id) ?? str(o.name);
|
|
23565
|
+
default:
|
|
23566
|
+
return str(o.name) ?? str(o.id);
|
|
23567
|
+
}
|
|
23568
|
+
}
|
|
23569
|
+
function reportIdentityOf(field, item) {
|
|
23570
|
+
if (item !== null && typeof item === "object") {
|
|
23571
|
+
const step = item.stepNumber;
|
|
23572
|
+
if (field === "narrative" && typeof step === "number") return `step ${step}`;
|
|
23573
|
+
}
|
|
23574
|
+
return identityKeyOf(field, item);
|
|
23575
|
+
}
|
|
23576
|
+
function specChanges(before, after, prefix = "") {
|
|
23577
|
+
const at = (key) => prefix ? `${prefix}.${key}` : key;
|
|
23578
|
+
const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
23579
|
+
if (Array.isArray(before) && Array.isArray(after)) {
|
|
23580
|
+
if (after.length === 0 && before.length > 0) {
|
|
23581
|
+
return [{ path: prefix, change: "cleared", before: summarizeValue(before) }];
|
|
23582
|
+
}
|
|
23583
|
+
const identified = [...before, ...after].every((i) => reportIdentityOf(prefix.split(".").pop() ?? prefix, i) !== null);
|
|
23584
|
+
if (!identified) {
|
|
23585
|
+
return JSON.stringify(before) === JSON.stringify(after) ? [] : [{ path: prefix, change: "set", before: summarizeValue(before), after: summarizeValue(after) }];
|
|
23586
|
+
}
|
|
23587
|
+
const field = prefix.split(".").pop() ?? prefix;
|
|
23588
|
+
const keyOf = (i) => String(reportIdentityOf(field, i));
|
|
23589
|
+
const changes = [];
|
|
23590
|
+
for (const item of before) {
|
|
23591
|
+
const match = after.find((a) => keyOf(a) === keyOf(item));
|
|
23592
|
+
if (match === void 0) changes.push({ path: at(keyOf(item)), change: "removed", before: summarizeValue(item) });
|
|
23593
|
+
else changes.push(...specChanges(item, match, at(keyOf(item))));
|
|
23594
|
+
}
|
|
23595
|
+
for (const item of after) {
|
|
23596
|
+
if (!before.some((b) => keyOf(b) === keyOf(item))) {
|
|
23597
|
+
changes.push({ path: at(keyOf(item)), change: "added", after: summarizeValue(item) });
|
|
23598
|
+
}
|
|
23599
|
+
}
|
|
23600
|
+
return changes;
|
|
23601
|
+
}
|
|
23602
|
+
if (isPlain(before) && isPlain(after)) {
|
|
23603
|
+
const changes = [];
|
|
23604
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
|
|
23605
|
+
const b = before[key];
|
|
23606
|
+
const a = after[key];
|
|
23607
|
+
if (b === void 0 && a !== void 0) changes.push({ path: at(key), change: "added", after: summarizeValue(a) });
|
|
23608
|
+
else if (b !== void 0 && a === void 0) changes.push({ path: at(key), change: "removed", before: summarizeValue(b) });
|
|
23609
|
+
else changes.push(...specChanges(b, a, at(key)));
|
|
23610
|
+
}
|
|
23611
|
+
return changes;
|
|
23612
|
+
}
|
|
23613
|
+
if (JSON.stringify(before) === JSON.stringify(after)) return [];
|
|
23614
|
+
return [{ path: prefix, change: "set", before: summarizeValue(before), after: summarizeValue(after) }];
|
|
23615
|
+
}
|
|
23529
23616
|
function workspaceFor(rootDir) {
|
|
23530
23617
|
const key = path23.resolve(rootDir);
|
|
23531
23618
|
let ws = workspaces.get(key);
|
|
@@ -25089,6 +25176,14 @@ var init_specs2 = __esm({
|
|
|
25089
25176
|
return refs;
|
|
25090
25177
|
};
|
|
25091
25178
|
const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
|
|
25179
|
+
const unsetFieldsOf = (item) => Array.isArray(item?.unset) ? item.unset.filter((f) => typeof f === "string") : [];
|
|
25180
|
+
const withUnset = (merged, deltaItem) => {
|
|
25181
|
+
const fields = unsetFieldsOf(deltaItem);
|
|
25182
|
+
const out = { ...merged };
|
|
25183
|
+
delete out.unset;
|
|
25184
|
+
for (const field of fields) delete out[field];
|
|
25185
|
+
return out;
|
|
25186
|
+
};
|
|
25092
25187
|
const assertDeltaMarkers = (item, label) => {
|
|
25093
25188
|
if (!item || typeof item !== "object") return;
|
|
25094
25189
|
if ("remove" in item && typeof item.remove !== "boolean") {
|
|
@@ -25155,18 +25250,18 @@ var init_specs2 = __esm({
|
|
|
25155
25250
|
capture
|
|
25156
25251
|
));
|
|
25157
25252
|
const { action, remove: remove2, captureJumps, ...cleanStep } = deltaStep;
|
|
25158
|
-
steps.push(cleanStep);
|
|
25253
|
+
steps.push(withUnset(cleanStep, deltaStep));
|
|
25159
25254
|
} else {
|
|
25160
25255
|
const idx = steps.findIndex((s) => s.stepNumber === stepNum);
|
|
25161
25256
|
if (idx !== -1) {
|
|
25162
25257
|
const { action, remove: remove2, ...cleanStep } = deltaStep;
|
|
25163
|
-
steps[idx] = {
|
|
25258
|
+
steps[idx] = withUnset({
|
|
25164
25259
|
...steps[idx],
|
|
25165
25260
|
...cleanStep
|
|
25166
|
-
};
|
|
25261
|
+
}, deltaStep);
|
|
25167
25262
|
} else {
|
|
25168
25263
|
const { action, remove: remove2, ...cleanStep } = deltaStep;
|
|
25169
|
-
steps.push(cleanStep);
|
|
25264
|
+
steps.push(withUnset(cleanStep, deltaStep));
|
|
25170
25265
|
}
|
|
25171
25266
|
}
|
|
25172
25267
|
}
|
|
@@ -25193,20 +25288,20 @@ var init_specs2 = __esm({
|
|
|
25193
25288
|
} else {
|
|
25194
25289
|
const existingMethod = merged[idx];
|
|
25195
25290
|
let narrative = existingMethod.narrative ? [...existingMethod.narrative] : [];
|
|
25196
|
-
if (
|
|
25197
|
-
narrative = mergeNarrative(narrative, deltaMethod.narrative);
|
|
25291
|
+
if (Array.isArray(deltaMethod.narrative)) {
|
|
25292
|
+
narrative = deltaMethod.narrative.length === 0 ? [] : mergeNarrative(narrative, deltaMethod.narrative);
|
|
25198
25293
|
}
|
|
25199
25294
|
const { narrative: _, ...cleanMethod } = deltaMethod;
|
|
25200
|
-
merged[idx] = {
|
|
25295
|
+
merged[idx] = withUnset({
|
|
25201
25296
|
...existingMethod,
|
|
25202
25297
|
...cleanMethod,
|
|
25203
25298
|
...deltaMethod.ext !== void 0 ? { ext: mergeExt(existingMethod.ext, deltaMethod.ext) } : {},
|
|
25204
25299
|
narrative
|
|
25205
|
-
};
|
|
25300
|
+
}, deltaMethod);
|
|
25206
25301
|
}
|
|
25207
25302
|
} else {
|
|
25208
25303
|
assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
|
|
25209
|
-
merged.push(deltaMethod);
|
|
25304
|
+
merged.push(withUnset(deltaMethod, deltaMethod));
|
|
25210
25305
|
}
|
|
25211
25306
|
}
|
|
25212
25307
|
return merged;
|
|
@@ -25220,18 +25315,18 @@ var init_specs2 = __esm({
|
|
|
25220
25315
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
25221
25316
|
merged.splice(idx, 1);
|
|
25222
25317
|
} else {
|
|
25223
|
-
merged[idx] = {
|
|
25318
|
+
merged[idx] = withUnset({
|
|
25224
25319
|
...merged[idx],
|
|
25225
25320
|
...deltaItem,
|
|
25226
25321
|
...deltaItem.ext !== void 0 ? { ext: mergeExt(merged[idx].ext, deltaItem.ext) } : {},
|
|
25227
25322
|
// An interface method's findings upsert and delete by code, like
|
|
25228
25323
|
// lint.allow; an empty list still clears them outright.
|
|
25229
25324
|
...Array.isArray(deltaItem.findings) && deltaItem.findings.length > 0 ? { findings: mergeIdentifiedArray("findings", merged[idx].findings ?? [], deltaItem.findings) } : {}
|
|
25230
|
-
};
|
|
25325
|
+
}, deltaItem);
|
|
25231
25326
|
}
|
|
25232
25327
|
} else {
|
|
25233
25328
|
assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
|
|
25234
|
-
merged.push(deltaItem);
|
|
25329
|
+
merged.push(withUnset(deltaItem, deltaItem));
|
|
25235
25330
|
}
|
|
25236
25331
|
}
|
|
25237
25332
|
return merged;
|
|
@@ -25245,11 +25340,11 @@ var init_specs2 = __esm({
|
|
|
25245
25340
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
25246
25341
|
merged.splice(idx, 1);
|
|
25247
25342
|
} else {
|
|
25248
|
-
merged[idx] = { ...merged[idx], ...deltaItem };
|
|
25343
|
+
merged[idx] = withUnset({ ...merged[idx], ...deltaItem }, deltaItem);
|
|
25249
25344
|
}
|
|
25250
25345
|
} else {
|
|
25251
25346
|
assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
|
|
25252
|
-
merged.push(deltaItem);
|
|
25347
|
+
merged.push(withUnset(deltaItem, deltaItem));
|
|
25253
25348
|
}
|
|
25254
25349
|
}
|
|
25255
25350
|
return merged.map(({ action, remove: remove2, ...item }) => item);
|
|
@@ -25264,52 +25359,18 @@ var init_specs2 = __esm({
|
|
|
25264
25359
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
25265
25360
|
merged.splice(idx, 1);
|
|
25266
25361
|
} else {
|
|
25267
|
-
merged[idx] = {
|
|
25362
|
+
merged[idx] = withUnset({
|
|
25268
25363
|
...merged[idx],
|
|
25269
25364
|
...deltaItem
|
|
25270
|
-
};
|
|
25365
|
+
}, deltaItem);
|
|
25271
25366
|
}
|
|
25272
25367
|
} else {
|
|
25273
25368
|
assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
|
|
25274
|
-
merged.push(deltaItem);
|
|
25369
|
+
merged.push(withUnset(deltaItem, deltaItem));
|
|
25275
25370
|
}
|
|
25276
25371
|
}
|
|
25277
25372
|
return merged;
|
|
25278
25373
|
};
|
|
25279
|
-
const identityKeyOf = (field, item) => {
|
|
25280
|
-
if (item === null || typeof item !== "object") return null;
|
|
25281
|
-
const o = item;
|
|
25282
|
-
const str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
25283
|
-
switch (field) {
|
|
25284
|
-
case "dispatch":
|
|
25285
|
-
return str(o.capability);
|
|
25286
|
-
case "lifecycle":
|
|
25287
|
-
return `${String(o.phase)} ${String(o.component)} ${String(o.method)}`;
|
|
25288
|
-
case "emits":
|
|
25289
|
-
case "subscribesTo":
|
|
25290
|
-
return `${String(o.topic)} ${o.event === void 0 ? "" : String(o.event)}`;
|
|
25291
|
-
case "trustedLinks":
|
|
25292
|
-
return str(o.subsystem);
|
|
25293
|
-
case "allow":
|
|
25294
|
-
return str(o.code);
|
|
25295
|
-
// lint.allow
|
|
25296
|
-
case "findings":
|
|
25297
|
-
return str(o.code);
|
|
25298
|
-
// an interface method's findings
|
|
25299
|
-
case "invariants":
|
|
25300
|
-
return str(o.id);
|
|
25301
|
-
case "patterns":
|
|
25302
|
-
return str(o.id);
|
|
25303
|
-
case "boundaries":
|
|
25304
|
-
return str(o.name);
|
|
25305
|
-
case "globalRequirements":
|
|
25306
|
-
return str(o.description);
|
|
25307
|
-
case "databases":
|
|
25308
|
-
return str(o.id) ?? str(o.name);
|
|
25309
|
-
default:
|
|
25310
|
-
return str(o.name) ?? str(o.id);
|
|
25311
|
-
}
|
|
25312
|
-
};
|
|
25313
25374
|
const mergeIdentifiedArray = (field, existing, delta2) => {
|
|
25314
25375
|
const merged = [...existing];
|
|
25315
25376
|
for (const deltaItem of delta2) {
|
|
@@ -25319,7 +25380,7 @@ var init_specs2 = __esm({
|
|
|
25319
25380
|
const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
|
|
25320
25381
|
if (idx !== -1) {
|
|
25321
25382
|
if (isDelete) merged.splice(idx, 1);
|
|
25322
|
-
else merged[idx] = { ...merged[idx], ...deltaItem };
|
|
25383
|
+
else merged[idx] = withUnset({ ...merged[idx], ...deltaItem }, deltaItem);
|
|
25323
25384
|
} else {
|
|
25324
25385
|
if (key !== null) {
|
|
25325
25386
|
assertUnmatchedIsAnAddition(
|
|
@@ -25331,7 +25392,7 @@ var init_specs2 = __esm({
|
|
|
25331
25392
|
} else if (isDelete) {
|
|
25332
25393
|
throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
|
|
25333
25394
|
}
|
|
25334
|
-
merged.push(deltaItem);
|
|
25395
|
+
merged.push(withUnset(deltaItem, deltaItem));
|
|
25335
25396
|
}
|
|
25336
25397
|
}
|
|
25337
25398
|
return merged.map((item) => item && typeof item === "object" ? (({ action, remove: remove2, ...rest }) => rest)(item) : item);
|
|
@@ -25340,6 +25401,7 @@ var init_specs2 = __esm({
|
|
|
25340
25401
|
const mergeDelta = (existing, delta2) => {
|
|
25341
25402
|
const res = { ...existing };
|
|
25342
25403
|
for (const [key, value] of Object.entries(delta2)) {
|
|
25404
|
+
if (key === "unset") continue;
|
|
25343
25405
|
if (value === void 0 || value === null) {
|
|
25344
25406
|
continue;
|
|
25345
25407
|
}
|
|
@@ -25367,7 +25429,7 @@ var init_specs2 = __esm({
|
|
|
25367
25429
|
res[key] = value;
|
|
25368
25430
|
}
|
|
25369
25431
|
}
|
|
25370
|
-
return res;
|
|
25432
|
+
return withUnset(res, delta2);
|
|
25371
25433
|
};
|
|
25372
25434
|
const qualifyDeltaRefs = (d) => {
|
|
25373
25435
|
if (kind === "system") return d;
|
|
@@ -25421,9 +25483,9 @@ var init_specs2 = __esm({
|
|
|
25421
25483
|
};
|
|
25422
25484
|
const unsetFields = Array.isArray(delta.unset) ? delta.unset.filter((f) => typeof f === "string") : [];
|
|
25423
25485
|
const { unset: _unset, ...mergeableDelta } = delta;
|
|
25486
|
+
const storedBefore = JSON.parse(JSON.stringify(result));
|
|
25424
25487
|
const mergedResult = mergeDelta(result, qualifyDeltaRefs(mergeableDelta));
|
|
25425
25488
|
for (const field of unsetFields) delete mergedResult[field];
|
|
25426
|
-
mergedResult.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25427
25489
|
if (kind === "implementation" && Array.isArray(mergedResult.methods)) {
|
|
25428
25490
|
const labelErrors = [];
|
|
25429
25491
|
for (const m of mergedResult.methods) {
|
|
@@ -25434,6 +25496,24 @@ var init_specs2 = __esm({
|
|
|
25434
25496
|
- ${labelErrors.join("\n- ")}`);
|
|
25435
25497
|
}
|
|
25436
25498
|
}
|
|
25499
|
+
const canonical = (spec) => {
|
|
25500
|
+
const parsed = deltaSchema.safeParse(spec);
|
|
25501
|
+
const value = { ...parsed.success ? parsed.data : spec };
|
|
25502
|
+
delete value.updatedAt;
|
|
25503
|
+
return value;
|
|
25504
|
+
};
|
|
25505
|
+
const changes = specChanges(canonical(storedBefore), canonical(mergedResult));
|
|
25506
|
+
if (changes.length === 0) {
|
|
25507
|
+
return {
|
|
25508
|
+
kind,
|
|
25509
|
+
id,
|
|
25510
|
+
written: false,
|
|
25511
|
+
changes,
|
|
25512
|
+
notices,
|
|
25513
|
+
summary: `No change to ${kind} "${id}" \u2014 the delta matches what is stored, so nothing was written.`
|
|
25514
|
+
};
|
|
25515
|
+
}
|
|
25516
|
+
mergedResult.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25437
25517
|
const gateNotices = hooks?.gate?.(kind, mergedResult);
|
|
25438
25518
|
if (gateNotices?.length) notices.push(...gateNotices);
|
|
25439
25519
|
const opts = {
|
|
@@ -25459,7 +25539,14 @@ var init_specs2 = __esm({
|
|
|
25459
25539
|
notices.push(...this.saveTypeSpec(mergedResult));
|
|
25460
25540
|
break;
|
|
25461
25541
|
}
|
|
25462
|
-
return
|
|
25542
|
+
return {
|
|
25543
|
+
kind,
|
|
25544
|
+
id,
|
|
25545
|
+
written: true,
|
|
25546
|
+
changes,
|
|
25547
|
+
notices,
|
|
25548
|
+
summary: `Updated ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
|
|
25549
|
+
};
|
|
25463
25550
|
}
|
|
25464
25551
|
};
|
|
25465
25552
|
workspaces = /* @__PURE__ */ new Map();
|
|
@@ -26425,6 +26512,16 @@ function json(value) {
|
|
|
26425
26512
|
function errText(message) {
|
|
26426
26513
|
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
26427
26514
|
}
|
|
26515
|
+
function renderChangeReport(report2) {
|
|
26516
|
+
const lines = [report2.summary];
|
|
26517
|
+
for (const change of report2.changes) {
|
|
26518
|
+
const to = change.after === void 0 ? "" : `: ${JSON.stringify(change.after)}`;
|
|
26519
|
+
const from = change.before === void 0 ? "" : ` (was ${JSON.stringify(change.before)})`;
|
|
26520
|
+
lines.push(`- ${change.path} ${change.change}${to}${from}`);
|
|
26521
|
+
}
|
|
26522
|
+
if (report2.notices.length) lines.push("", "NOTICE:", ...report2.notices.map((n) => `- ${n}`));
|
|
26523
|
+
return lines.join("\n");
|
|
26524
|
+
}
|
|
26428
26525
|
function captureBuildStamp(entryPath) {
|
|
26429
26526
|
try {
|
|
26430
26527
|
const s = fs20.statSync(entryPath);
|
|
@@ -27626,12 +27723,7 @@ NOTICE:
|
|
|
27626
27723
|
},
|
|
27627
27724
|
({ kind, id, delta }) => {
|
|
27628
27725
|
try {
|
|
27629
|
-
|
|
27630
|
-
const noticeBlock = notices.length ? `
|
|
27631
|
-
|
|
27632
|
-
NOTICE:
|
|
27633
|
-
- ${notices.join("\n- ")}` : "";
|
|
27634
|
-
return text(`Successfully updated ${kind} spec "${id}".${noticeBlock}`);
|
|
27726
|
+
return text(renderChangeReport(updateSpecGated(kind, id, delta)));
|
|
27635
27727
|
} catch (e) {
|
|
27636
27728
|
return errText(String(e));
|
|
27637
27729
|
}
|