@coreplane/switchboard 1.201.0 → 1.202.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/README.md +1 -1
- package/bin/switchboard.js +26 -0
- package/dist/assets/deploy/cloudflare-resident/refresh.ts +238 -0
- package/dist/assets/deploy/cloudflare-resident/shared.ts +80 -0
- package/dist/assets/deploy/cloudflare-resident/tsconfig.json +1 -1
- package/dist/assets/deploy/cloudflare-resident/worker.ts +67 -285
- package/dist/assets/deploy/cloudflare-resident/wrangler.template.jsonc +2 -1
- package/dist/assets/package-lock.json +481 -4
- package/dist/assets/package.json +1 -1
- package/dist/assets/project.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/authz/policy.ts +3 -0
- package/dist/assets/src/execution/residentRefresh.ts +29 -0
- package/dist/assets/web/dist/.vite/manifest.json +43 -43
- package/dist/assets/web/dist/assets/{AppShell-CjDyTiip.js → AppShell-CJLPO_aI.js} +1 -1
- package/dist/assets/web/dist/assets/{CostsPage-CMAh3G74.js → CostsPage-C6ZvnAmH.js} +1 -1
- package/dist/assets/web/dist/assets/{NotFoundPage-CbEKm0tY.js → NotFoundPage-BdLcY60r.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentDetailPage-CcfmOAIP.js → ResidentDetailPage-hXWrr8T6.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-9Z5jcteM.js → ResidentsIndexPage-YLu4JwxJ.js} +1 -1
- package/dist/assets/web/dist/assets/{RunRoutePage-Cdimh4e0.js → RunRoutePage-BDhHRJVO.js} +1 -1
- package/dist/assets/web/dist/assets/{RunsIndexPage-CvgDXrO4.js → RunsIndexPage-D7zZ5a0l.js} +1 -1
- package/dist/assets/web/dist/assets/{RunsTabs-rEjVQG6H.js → RunsTabs-BOUlSa2W.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-C5bG9qvd.js → ScheduledPage-BciOQcC-.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-D_IPK3WP.js → StatusDot-CTDLBv92.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-JlZp3OVC.js → Tooltip-DD2v9Gxx.js} +1 -1
- package/dist/assets/web/dist/assets/{favicon-DZDjc8ab.js → favicon-C4Q8cRsP.js} +1 -1
- package/dist/assets/web/dist/assets/{main-BKKzFAnK.js → main-DrSlUcMg.js} +2 -2
- package/dist/cli.js +629 -207
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -544,6 +544,9 @@ var init_policy = __esm({
|
|
|
544
544
|
{ action: "deploy:write", resource: "command", when: [grant("deploy:write")] },
|
|
545
545
|
{ action: "env:write", resource: "command", when: [grant("env:write")] },
|
|
546
546
|
{ action: "setup:write", resource: "command", when: [grant("setup:write")] },
|
|
547
|
+
// `contract render` reads host paths the caller names: CLI-only, the read
|
|
548
|
+
// grant by name — no chat baseline holds it.
|
|
549
|
+
{ action: "contract:read", resource: "command", when: [grant("contract:read")] },
|
|
547
550
|
// ── mcp (external MCP servers live in the three config tiers) ────────────
|
|
548
551
|
{ action: "mcp:read", resource: "command", when: [grant("mcp:read")] },
|
|
549
552
|
{ action: "mcp:write", resource: "command", when: [grant("mcp:write")] },
|
|
@@ -1181,6 +1184,301 @@ var init_schedules = __esm({
|
|
|
1181
1184
|
}
|
|
1182
1185
|
});
|
|
1183
1186
|
|
|
1187
|
+
// ../../src/core/ship/contract.ts
|
|
1188
|
+
function parsePlanUnit(planMarkdown, unitId) {
|
|
1189
|
+
const lines = planMarkdown.split("\n");
|
|
1190
|
+
const start2 = lines.findIndex((l) => {
|
|
1191
|
+
const m = UNIT_HEADING.exec(l);
|
|
1192
|
+
return m !== null && m[1] === unitId;
|
|
1193
|
+
});
|
|
1194
|
+
if (start2 < 0) return void 0;
|
|
1195
|
+
let end = lines.findIndex((l, i) => i > start2 && ANY_HEADING.test(l));
|
|
1196
|
+
if (end < 0) end = lines.length;
|
|
1197
|
+
const body = lines.slice(start2, end);
|
|
1198
|
+
while (body.length > 1 && /^\s*$/.test(body[body.length - 1])) body.pop();
|
|
1199
|
+
if (body.length > 1 && /^---\s*$/.test(body[body.length - 1])) body.pop();
|
|
1200
|
+
while (body.length > 1 && /^\s*$/.test(body[body.length - 1])) body.pop();
|
|
1201
|
+
const heading = UNIT_HEADING.exec(body[0]);
|
|
1202
|
+
return { id: heading[1], title: heading[2], section: body.join("\n"), bullets: parseBullets(body.slice(1)) };
|
|
1203
|
+
}
|
|
1204
|
+
function planUnitIds(planMarkdown) {
|
|
1205
|
+
return planMarkdown.split("\n").map((l) => UNIT_HEADING.exec(l)?.[1]).filter((id) => id !== void 0);
|
|
1206
|
+
}
|
|
1207
|
+
function bulletSpans(sectionLines) {
|
|
1208
|
+
const spans = [];
|
|
1209
|
+
for (let i = 1; i < sectionLines.length; i++) {
|
|
1210
|
+
const m = BULLET.exec(sectionLines[i]);
|
|
1211
|
+
if (!m) continue;
|
|
1212
|
+
if (spans.length > 0) spans[spans.length - 1].end = i;
|
|
1213
|
+
spans.push({ key: m[1], start: i, end: sectionLines.length });
|
|
1214
|
+
}
|
|
1215
|
+
return spans;
|
|
1216
|
+
}
|
|
1217
|
+
function bulletBody(lines) {
|
|
1218
|
+
const first = BULLET.exec(lines[0]);
|
|
1219
|
+
const head = first[2] ?? "";
|
|
1220
|
+
const rest = lines.slice(1);
|
|
1221
|
+
while (rest.length > 0 && /^\s*$/.test(rest[rest.length - 1])) rest.pop();
|
|
1222
|
+
if (rest.length === 0) return head.trim();
|
|
1223
|
+
const indent = Math.min(...rest.filter((l) => l.trim() !== "").map((l) => /^\s*/.exec(l)[0].length));
|
|
1224
|
+
const dedented = rest.map((l) => l.slice(Math.min(indent, /^\s*/.exec(l)[0].length)));
|
|
1225
|
+
return [head.trim(), ...dedented].filter((l, i) => i > 0 || l !== "").join("\n");
|
|
1226
|
+
}
|
|
1227
|
+
function parseBullets(bodyLines) {
|
|
1228
|
+
const withHeading = ["", ...bodyLines];
|
|
1229
|
+
const out = {};
|
|
1230
|
+
for (const span of bulletSpans(withHeading)) out[span.key] = bulletBody(withHeading.slice(span.start, span.end));
|
|
1231
|
+
return out;
|
|
1232
|
+
}
|
|
1233
|
+
function expandItemList(list) {
|
|
1234
|
+
const out = [];
|
|
1235
|
+
const ranged = /(\d+)\s*(?:–|-|to)\s*(\d+)/g;
|
|
1236
|
+
let rest = list;
|
|
1237
|
+
for (const m of list.matchAll(ranged)) {
|
|
1238
|
+
const from = Number(m[1]);
|
|
1239
|
+
const to = Number(m[2]);
|
|
1240
|
+
if (to >= from && to - from <= 100) for (let n2 = from; n2 <= to; n2++) out.push(n2);
|
|
1241
|
+
rest = rest.replace(m[0], " ");
|
|
1242
|
+
}
|
|
1243
|
+
for (const m of rest.matchAll(/\d+/g)) out.push(Number(m[0]));
|
|
1244
|
+
return out;
|
|
1245
|
+
}
|
|
1246
|
+
function specNameOf(mention) {
|
|
1247
|
+
const m = /^(?:docs\/reference\/specs\/)?([a-z0-9-]+\.md)$/.exec(mention);
|
|
1248
|
+
return m?.[1];
|
|
1249
|
+
}
|
|
1250
|
+
function specItemRefs(section) {
|
|
1251
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1252
|
+
const refs = [];
|
|
1253
|
+
const add = (spec, item) => {
|
|
1254
|
+
const key = `${spec}#${item}`;
|
|
1255
|
+
if (seen.has(key)) return;
|
|
1256
|
+
seen.add(key);
|
|
1257
|
+
refs.push({ spec, item });
|
|
1258
|
+
};
|
|
1259
|
+
const named = [];
|
|
1260
|
+
for (const m of section.matchAll(NAMED_REF)) {
|
|
1261
|
+
const spec = m[1] ?? m[2] ?? m[3];
|
|
1262
|
+
named.push({ index: m.index, end: m.index + m[0].length, spec, items: expandItemList(m[4]) });
|
|
1263
|
+
}
|
|
1264
|
+
const mentions = [...section.matchAll(ANY_MD_MENTION)].map((m) => ({ index: m.index, spec: specNameOf(m[0]) }));
|
|
1265
|
+
const bare = [];
|
|
1266
|
+
for (const m of section.matchAll(ITEM_LIST)) {
|
|
1267
|
+
if (named.some((n2) => m.index >= n2.index && m.index < n2.end)) continue;
|
|
1268
|
+
bare.push({ index: m.index, items: expandItemList(m[1]) });
|
|
1269
|
+
}
|
|
1270
|
+
const all = [
|
|
1271
|
+
...named.map((n2) => ({ index: n2.index, spec: n2.spec, items: n2.items })),
|
|
1272
|
+
...bare.map((b) => ({
|
|
1273
|
+
index: b.index,
|
|
1274
|
+
spec: mentions.filter((x) => x.index < b.index).at(-1)?.spec,
|
|
1275
|
+
items: b.items
|
|
1276
|
+
}))
|
|
1277
|
+
].sort((a, b) => a.index - b.index);
|
|
1278
|
+
for (const ref of all) if (ref.spec !== void 0) for (const item of ref.items) add(ref.spec, item);
|
|
1279
|
+
return refs;
|
|
1280
|
+
}
|
|
1281
|
+
function parseSpecItem(specMarkdown, item) {
|
|
1282
|
+
const lines = specMarkdown.split("\n");
|
|
1283
|
+
const start2 = lines.findIndex((l) => Number(SPEC_ITEM.exec(l)?.[1]) === item);
|
|
1284
|
+
if (start2 < 0) return void 0;
|
|
1285
|
+
let end = lines.findIndex((l, i) => i > start2 && (SPEC_ITEM.test(l) || /^#{1,3} /.test(l)));
|
|
1286
|
+
if (end < 0) end = lines.length;
|
|
1287
|
+
const body = lines.slice(start2, end);
|
|
1288
|
+
while (body.length > 1 && /^\s*$/.test(body[body.length - 1])) body.pop();
|
|
1289
|
+
return body.join("\n");
|
|
1290
|
+
}
|
|
1291
|
+
function parseValidationRows(specMarkdown) {
|
|
1292
|
+
const rows = [];
|
|
1293
|
+
for (const line2 of specMarkdown.split("\n")) {
|
|
1294
|
+
if (!line2.startsWith("|")) continue;
|
|
1295
|
+
const cells = line2.trim().replace(/^\||\|$/g, "").split(/(?<!\\)\|/).map((c) => c.trim());
|
|
1296
|
+
if (cells.length < 2) continue;
|
|
1297
|
+
if (/^-+$/.test(cells[0]) || cells[0] === "Criterion" && cells[1] === "Proof") continue;
|
|
1298
|
+
rows.push({ criterion: cells[0], proof: cells.slice(1).join(" | ") });
|
|
1299
|
+
}
|
|
1300
|
+
return rows;
|
|
1301
|
+
}
|
|
1302
|
+
function itemNumbersNamed(criterion) {
|
|
1303
|
+
const out = /* @__PURE__ */ new Set();
|
|
1304
|
+
const lead = /^(\d+(?:\s*[,/]\s*\d+)*)\s*:/.exec(criterion);
|
|
1305
|
+
if (lead) for (const n2 of expandItemList(lead[1])) out.add(n2);
|
|
1306
|
+
for (const m of criterion.matchAll(ITEM_LIST)) for (const n2 of expandItemList(m[1])) out.add(n2);
|
|
1307
|
+
return out;
|
|
1308
|
+
}
|
|
1309
|
+
function resolveSpecRow(ref, specMarkdown) {
|
|
1310
|
+
if (specMarkdown === void 0) return { ...ref, specRead: false, text: void 0, validation: [] };
|
|
1311
|
+
return {
|
|
1312
|
+
...ref,
|
|
1313
|
+
specRead: true,
|
|
1314
|
+
text: parseSpecItem(specMarkdown, ref.item),
|
|
1315
|
+
validation: parseValidationRows(specMarkdown).filter((row) => itemNumbersNamed(row.criterion).has(ref.item))
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function contractFromPlan(input) {
|
|
1319
|
+
const unit = parsePlanUnit(input.planMarkdown, input.unitId);
|
|
1320
|
+
if (!unit) {
|
|
1321
|
+
const ids = planUnitIds(input.planMarkdown);
|
|
1322
|
+
throw new Error(
|
|
1323
|
+
`the plan has no unit ${input.unitId} (its units: ${ids.length > 0 ? ids.join(", ") : "none \u2014 no `### U<n>.` heading"})`
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
return {
|
|
1327
|
+
unit,
|
|
1328
|
+
specRows: specItemRefs(unit.section).map((ref) => resolveSpecRow(ref, input.readSpec(ref.spec))),
|
|
1329
|
+
agentRules: input.agentRules,
|
|
1330
|
+
guards: GUARDS,
|
|
1331
|
+
rebase: { branch: input.rebase?.branch, onto: input.rebase?.onto }
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
function unitBody(unit, dropApproach) {
|
|
1335
|
+
const lines = unit.section.split("\n");
|
|
1336
|
+
if (dropApproach) {
|
|
1337
|
+
const span = bulletSpans(lines).find((s) => s.key === "Approach");
|
|
1338
|
+
if (span) lines.splice(span.start, span.end - span.start, APPROACH_DROPPED);
|
|
1339
|
+
}
|
|
1340
|
+
return lines.slice(1).join("\n").trim();
|
|
1341
|
+
}
|
|
1342
|
+
function hasApproach(unit) {
|
|
1343
|
+
return "Approach" in unit.bullets;
|
|
1344
|
+
}
|
|
1345
|
+
function renderFirstInstruction(rebase) {
|
|
1346
|
+
const branch = rebase.branch ? `\`${rebase.branch}\`` : "the unit's branch";
|
|
1347
|
+
const onto = rebase.onto ? `\`${rebase.onto}\`` : "the merged parent";
|
|
1348
|
+
return `Rebase ${branch} onto ${onto} before any other work \u2014 the parent unit has merged and the base has moved; the only writes are your own on that branch. A conflict ends the unit: report it as the handoff and stop.`;
|
|
1349
|
+
}
|
|
1350
|
+
function renderSpecRow(row, dropText) {
|
|
1351
|
+
const head = `#### ${row.spec} item ${row.item}`;
|
|
1352
|
+
if (!row.specRead) return `${head}
|
|
1353
|
+
|
|
1354
|
+
(not found: no spec ${row.spec} was readable)`;
|
|
1355
|
+
if (row.text === void 0) return `${head}
|
|
1356
|
+
|
|
1357
|
+
(not found: ${row.spec} has no item ${row.item})`;
|
|
1358
|
+
const text = dropText ? `(text dropped \u2014 read item ${row.item} in the spec)` : row.text;
|
|
1359
|
+
const rows = row.validation.length === 0 ? `(no validation row names item ${row.item})` : ["| Criterion | Proof |", "|---|---|", ...row.validation.map((r) => `| ${r.criterion} | ${r.proof} |`)].join(
|
|
1360
|
+
"\n"
|
|
1361
|
+
);
|
|
1362
|
+
return `${head}
|
|
1363
|
+
|
|
1364
|
+
${text}
|
|
1365
|
+
|
|
1366
|
+
${rows}`;
|
|
1367
|
+
}
|
|
1368
|
+
function renderSpecRows(rows, dropped) {
|
|
1369
|
+
if (rows.length === 0)
|
|
1370
|
+
return "(none \u2014 the unit names no spec rows; until decided, a unit naming none receives none, not the covering specs' rows by path)";
|
|
1371
|
+
if (dropped.rows) return rows.map((r) => `- ${r.spec} item ${r.item}`).join("\n");
|
|
1372
|
+
return rows.map((r) => renderSpecRow(r, dropped.text)).join("\n\n");
|
|
1373
|
+
}
|
|
1374
|
+
function renderAgentRules(rules, dropped) {
|
|
1375
|
+
if (!rules) return "(none \u2014 the repository has neither AGENTS.md nor CLAUDE.md)";
|
|
1376
|
+
if (dropped)
|
|
1377
|
+
return `Source: ${rules.file}
|
|
1378
|
+
|
|
1379
|
+
(dropped to fit the contract's budget \u2014 read ${rules.file} at the head)`;
|
|
1380
|
+
return `Source: ${rules.file}
|
|
1381
|
+
|
|
1382
|
+
${rules.text.trim()}`;
|
|
1383
|
+
}
|
|
1384
|
+
function renderGuards(guards) {
|
|
1385
|
+
return [
|
|
1386
|
+
"Never weaken a guard: each refuses one class of change, and the review runs them over your range.",
|
|
1387
|
+
...guards.map((g) => `- \`${g.name}\` \u2014 ${g.refuses}`)
|
|
1388
|
+
].join("\n");
|
|
1389
|
+
}
|
|
1390
|
+
function renderContract(contract, opts) {
|
|
1391
|
+
const maxChars = opts.maxChars ?? DEFAULT_CONTRACT_MAX_CHARS;
|
|
1392
|
+
const hasSpecText = contract.specRows.some((r) => r.text !== void 0);
|
|
1393
|
+
const ladder = [
|
|
1394
|
+
{ part: "approach", present: hasApproach(contract.unit) },
|
|
1395
|
+
{ part: "agentRules", present: contract.agentRules !== void 0 },
|
|
1396
|
+
{ part: "specText", present: hasSpecText },
|
|
1397
|
+
{ part: "specRows", present: contract.specRows.length > 0 }
|
|
1398
|
+
];
|
|
1399
|
+
const compose = (dropped2) => {
|
|
1400
|
+
const has = (p) => dropped2.includes(p);
|
|
1401
|
+
const parts = [
|
|
1402
|
+
CONTRACT_HEADING,
|
|
1403
|
+
`${CONTRACT_SECTION_HEADINGS.firstInstruction}
|
|
1404
|
+
|
|
1405
|
+
${renderFirstInstruction(contract.rebase)}`,
|
|
1406
|
+
`${CONTRACT_SECTION_HEADINGS.unit} ${contract.unit.id} \u2014 ${contract.unit.title}
|
|
1407
|
+
|
|
1408
|
+
${unitBody(contract.unit, has("approach"))}`,
|
|
1409
|
+
`${CONTRACT_SECTION_HEADINGS.specRows}
|
|
1410
|
+
|
|
1411
|
+
${renderSpecRows(contract.specRows, { text: has("specText"), rows: has("specRows") })}`,
|
|
1412
|
+
`${CONTRACT_SECTION_HEADINGS.agentRules}
|
|
1413
|
+
|
|
1414
|
+
${renderAgentRules(contract.agentRules, has("agentRules"))}`,
|
|
1415
|
+
`${CONTRACT_SECTION_HEADINGS.guards}
|
|
1416
|
+
|
|
1417
|
+
${renderGuards(contract.guards)}`
|
|
1418
|
+
];
|
|
1419
|
+
if (dropped2.length > 0)
|
|
1420
|
+
parts.push(`Cut to fit ${maxChars} characters: ${dropped2.map((p) => DROP_NOTE[p]).join("; ")}.`);
|
|
1421
|
+
return parts.join("\n\n");
|
|
1422
|
+
};
|
|
1423
|
+
const dropped = [];
|
|
1424
|
+
let text = compose(dropped);
|
|
1425
|
+
for (const step of ladder) {
|
|
1426
|
+
if (text.length <= maxChars) break;
|
|
1427
|
+
if (!step.present) continue;
|
|
1428
|
+
dropped.push(step.part);
|
|
1429
|
+
text = compose(dropped);
|
|
1430
|
+
}
|
|
1431
|
+
return { text, chars: text.length, dropped, overBudget: text.length > maxChars };
|
|
1432
|
+
}
|
|
1433
|
+
var GUARDS, CONTRACT_HEADING, CONTRACT_SECTION_HEADINGS, DEFAULT_CONTRACT_MAX_CHARS, UNIT_HEADING, ANY_HEADING, BULLET, SPEC_MENTION, ITEM_LIST, NAMED_REF, ANY_MD_MENTION, SPEC_ITEM, APPROACH_DROPPED, DROP_NOTE;
|
|
1434
|
+
var init_contract = __esm({
|
|
1435
|
+
"../../src/core/ship/contract.ts"() {
|
|
1436
|
+
"use strict";
|
|
1437
|
+
GUARDS = [
|
|
1438
|
+
{
|
|
1439
|
+
name: "specs:check",
|
|
1440
|
+
refuses: "a spec whose `file::describe::it` proof names a test that does not exist, or whose Code/Tests header path is gone \u2014 a test cannot be renamed or deleted out from under its spec row"
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
name: "specs:coverage --test-guard",
|
|
1444
|
+
refuses: "a deleted test file, a removed test title or an added skip marker in a test file unless a spec covering that file changes in the same range; a lower assertion count or a rename is printed for the reviewer to dispose of"
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
name: "hygiene:check",
|
|
1448
|
+
refuses: "a new imprint in the public tree (a company, a person, a tracker reference, a plan id, a platform id, a date); the recorded list only shrinks"
|
|
1449
|
+
},
|
|
1450
|
+
{
|
|
1451
|
+
name: "decisions:check",
|
|
1452
|
+
refuses: "a record under docs/decisions or docs/plans without a valid status, a superseded one that names no successor, and any edit to an accepted record's body"
|
|
1453
|
+
}
|
|
1454
|
+
];
|
|
1455
|
+
CONTRACT_HEADING = "## Contract";
|
|
1456
|
+
CONTRACT_SECTION_HEADINGS = {
|
|
1457
|
+
firstInstruction: "### First instruction",
|
|
1458
|
+
unit: "### Unit",
|
|
1459
|
+
specRows: "### Spec rows",
|
|
1460
|
+
agentRules: "### Agent rules",
|
|
1461
|
+
guards: "### Guards"
|
|
1462
|
+
};
|
|
1463
|
+
DEFAULT_CONTRACT_MAX_CHARS = 48e3;
|
|
1464
|
+
UNIT_HEADING = /^### (U\d+)\. (.+?)\s*$/;
|
|
1465
|
+
ANY_HEADING = /^#{2,3} /;
|
|
1466
|
+
BULLET = /^- \*\*([^*]+)\*\*:(?:\s?(.*))?$/;
|
|
1467
|
+
SPEC_MENTION = /`(?:docs\/reference\/specs\/)?([a-z0-9-]+\.md)`|\[([a-z0-9-]+\.md)\]\([^)]*\)|(?<![\w/.-])(?:docs\/reference\/specs\/)?([a-z0-9-]+\.md)(?![\w/])/g;
|
|
1468
|
+
ITEM_LIST = /\b[Ii]tems?\s+(\d+(?:\s*(?:,|and|&|–|-|to|\/)\s*\d+)*)/g;
|
|
1469
|
+
NAMED_REF = new RegExp(`(?:${SPEC_MENTION.source})\\s+(?:\\([^)]*\\)\\s+)?${ITEM_LIST.source}`, "g");
|
|
1470
|
+
ANY_MD_MENTION = /(?<![\w/.-])(?:[\w.-]+\/)*([\w.-]+\.md)(?![\w/])/g;
|
|
1471
|
+
SPEC_ITEM = /^(\d+)\. /;
|
|
1472
|
+
APPROACH_DROPPED = "- **Approach**: (dropped to fit the contract's budget \u2014 read it in the plan)";
|
|
1473
|
+
DROP_NOTE = {
|
|
1474
|
+
approach: "the unit's Approach text was dropped (read it in the plan)",
|
|
1475
|
+
agentRules: "the agent rules were dropped (read the file at the head)",
|
|
1476
|
+
specText: "the spec items' text was dropped (read them in the specs; their proof rows stay)",
|
|
1477
|
+
specRows: "the spec rows' proof rows were dropped too (their names stay above)"
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1184
1482
|
// ../../src/agents/registry.ts
|
|
1185
1483
|
function getAgent(name) {
|
|
1186
1484
|
const a = AGENTS[name];
|
|
@@ -1189,10 +1487,11 @@ function getAgent(name) {
|
|
|
1189
1487
|
}
|
|
1190
1488
|
return a;
|
|
1191
1489
|
}
|
|
1192
|
-
var PR_DESCRIPTION_TEMPLATE, NEVER_MERGE, CODING_SYSTEM, CODING_SYSTEM_RESIDENT, REVIEW_VERDICT_INSTRUCTION, REVIEW_SPEC_CHECK, REVIEW_WHOLE_CHANGE, REVIEW_SYSTEM, REVIEW_SYSTEM_RESIDENT, RESEARCH_SYSTEM, GENERAL_SYSTEM, AGENTS;
|
|
1490
|
+
var PR_DESCRIPTION_TEMPLATE, NEVER_MERGE, CONTRACT_HEADINGS_LIST, UNIT_CONTRACT, CODING_SYSTEM, CODING_SYSTEM_RESIDENT, REVIEW_VERDICT_INSTRUCTION, REVIEW_SPEC_CHECK, REVIEW_UNIT_CONTRACT, REVIEW_WHOLE_CHANGE, REVIEW_SYSTEM, REVIEW_SYSTEM_RESIDENT, RESEARCH_SYSTEM, GENERAL_SYSTEM, AGENTS;
|
|
1193
1491
|
var init_registry = __esm({
|
|
1194
1492
|
"../../src/agents/registry.ts"() {
|
|
1195
1493
|
"use strict";
|
|
1494
|
+
init_contract();
|
|
1196
1495
|
PR_DESCRIPTION_TEMPLATE = `PR description \u2014 submit it with the submit_pr_description tool for EVERY PR (this is the default, not something to wait to be asked for). Switchboard renders the GitHub body from the object you submit, so never author PR-body markdown yourself. Content contract per field (each renders as its own section): prose is unwrapped \u2014 no hard line breaks inside a paragraph. Always hyperlink the triggering issue/request. Never fabricate validation \u2014 state exactly what you ran and the real result. Keep each field concise, not padded.
|
|
1197
1496
|
EVERY PR includes one that already exists when you push \u2014 opened by a person, by dependabot, or by an earlier run. After EVERY push to such a PR: read its current title and body (\`github_issue_get\` with the PR number works for pull requests; \`gh pr view\` where gh exists), judge them against the change as it now stands at the pushed head, and submit the object that describes the PR as it is NOW \u2014 carry forward what the existing body says that is still true (a dependency bump's release notes belong in whatWhy), add what you changed, and anchor the Tour at the new head. Switchboard replaces the PR's title and body with your rendering. A description that describes an earlier state of its branch is a bug; "it is someone else's PR" is never a reason to leave it.
|
|
1198
1497
|
- **title**: the PR title \u2014 one line naming the change, specific enough to pick out of a PR list.
|
|
@@ -1203,6 +1502,8 @@ EVERY PR includes one that already exists when you push \u2014 opened by a perso
|
|
|
1203
1502
|
- **Risks & implications** (\`risks\`): what could break, the blast radius, and any migration/rollout/compatibility concerns (or "none" \u2014 and why).
|
|
1204
1503
|
- **Validation** (\`validation\`): what you tested and the actual results as { criterion, proof } rows (commands run, pass/fail), plus how the reviewer can verify it themselves; the optional summary line carries the overall result.`;
|
|
1205
1504
|
NEVER_MERGE = `NEVER merge a pull request and NEVER approve one \u2014 no merge or approve command, no merge/approve API call, no pushing to the default branch. Your deliverable is the pushed branch plus the submitted description; Switchboard's own GitHub writes are the PR open/edit, never an approval or a merge \u2014 a human decides what merges.`;
|
|
1505
|
+
CONTRACT_HEADINGS_LIST = Object.values(CONTRACT_SECTION_HEADINGS).map((h) => `\`${h}\``).join(", ");
|
|
1506
|
+
UNIT_CONTRACT = `UNIT CONTRACT: when your first user turn carries a \`${CONTRACT_HEADING}\` block \u2014 its sub-headings, in this order: ${CONTRACT_HEADINGS_LIST} \u2014 it is the contract for one plan unit, rendered by Switchboard from the plan itself, and it outranks any free-text task beside it. Do its first instruction first: the rebase of the unit's branch onto the merged parent (a conflict ends the unit \u2014 report it and stop; never resolve it by force). Then implement the unit's section as written: every test scenario it lists is added as a test, every spec row it names is updated so its proof binding resolves, the agent rules are followed, and no guard it names is weakened. The review is handed the same block and checks the diff against it: a test scenario the unit listed and the diff did not add is a finding at minor severity \u2014 the same severity as a spec contradiction. Never edit the plan record itself; where the unit is wrong or a criterion could not be proven, say so in your final message.`;
|
|
1206
1507
|
CODING_SYSTEM = `You are Switchboard's coding agent, operating from a Slack request.
|
|
1207
1508
|
|
|
1208
1509
|
You work inside a dedicated workspace directory with bash, read_file, and write_file tools.
|
|
@@ -1224,6 +1525,8 @@ Workflow for shipping a PR:
|
|
|
1224
1525
|
|
|
1225
1526
|
${NEVER_MERGE}
|
|
1226
1527
|
|
|
1528
|
+
${UNIT_CONTRACT}
|
|
1529
|
+
|
|
1227
1530
|
${PR_DESCRIPTION_TEMPLATE}
|
|
1228
1531
|
|
|
1229
1532
|
Maintain the user-facing status card with the update_status tool: right after you decide your plan, post it as a checklist (\u25CB pending items), then update it whenever an item starts (\u2731) or finishes (\u2713). Items are short outcomes ("Clone repo and read the diff", "Run the test suite"), never commands. Mark an item \u2713 only after it has actually happened \u2014 never pre-mark reporting/posting steps. This is the only progress the user sees while you work.
|
|
@@ -1252,6 +1555,8 @@ Workflow for shipping a change:
|
|
|
1252
1555
|
|
|
1253
1556
|
${NEVER_MERGE}
|
|
1254
1557
|
|
|
1558
|
+
${UNIT_CONTRACT}
|
|
1559
|
+
|
|
1255
1560
|
${PR_DESCRIPTION_TEMPLATE}
|
|
1256
1561
|
|
|
1257
1562
|
Maintain the user-facing status card with the update_status tool: right after you decide your plan, post it as a checklist (\u25CB pending items), then update it whenever an item starts (\u2731) or finishes (\u2713). Items are short outcomes ("Implement the fix", "Run the test suite"), never commands. Mark an item \u2713 only after it has actually happened \u2014 never pre-mark reporting/posting steps. This is the only progress the user sees while you work.
|
|
@@ -1261,6 +1566,7 @@ Your final message is posted to Slack \u2014 keep it readable, lead with the out
|
|
|
1261
1566
|
REVIEW_VERDICT_INSTRUCTION = `VERDICT: before your final message, call the submit_verdict tool exactly once with \`approve\` (no blocking issues \u2014 nits alone are not blocking) or \`request_changes\`, a one-line summary, \`head\` = the output of \`git rev-parse HEAD\` in the checkout you reviewed, and \`findings\` \u2014 every issue you report as a structured entry with a stable id you assign in order (F1, F2, \u2026), a severity of exactly blocking|major|minor|nit, the file (plus line when it points at one), and a one-line title. The findings array is the index of your review: the full explanation of each finding stays in your prose, keyed by the same ids. Switchboard writes the verdict as the first line of the GitHub comment itself and lists the findings under it; a review with no submitted verdict is posted as not approving, so never skip it. An \`approve\` carrying a blocking finding is downgraded to \`request_changes\` \u2014 approve only when nothing blocking remains. Do not write "LGTM" in your own text \u2014 the verdict line carries it.`;
|
|
1262
1567
|
REVIEW_SPEC_CHECK = `3. SPEC CONTRADICTION CHECK, when the repository has \`docs/reference/specs/\`: list the specs the change touches \u2014 \`npm run --silent specs:coverage -- --changed origin/<base>...HEAD\` (\`<base>\` is the PR's base branch) when the repository's package.json has that script, otherwise match the changed paths against each spec's \`- **Code**:\` / \`- **Tests**:\` header lines (a header path covers itself and everything beneath it). If the command fails for any reason \u2014 dependencies not installed, tsx missing, a cold checkout \u2014 fall back to matching the header lines by hand; never install dependencies or build to make it run. Then read ONLY those specs, never the whole specs tree \u2014 fold the reads into your gather batch where you can. For each touched spec, judge whether the diff contradicts a numbered behavior statement or a validation criterion: code that now does what the spec says it does not, a criterion whose named test the diff removed or retitled, a behavior the diff deleted that the spec still promises. A contradiction is a finding of severity \`minor\` or higher titled \`Spec contradiction \u2014 <spec file> item <n>: <what the code now does vs what the spec says>\`; a spec updated in the same diff to match the code is not a finding. A repository with no \`docs/reference/specs/\` has nothing to check \u2014 skip this step silently.
|
|
1263
1568
|
3a. TEST GUARD, in the same repositories: run \`npm run --silent specs:coverage -- --changed origin/<base>...HEAD --test-guard\` (fold it into the same batch). It compares every test file the diff touches at the base and at the head and prints one line per thing lost, in two classes. A \`test-guard: <file> \u2014 removed: \u2026\` line is deterministic \u2014 the test file deleted, an it/test/describe title gone with no new title to pair with, a skip/only/todo marker (\`.skip(\`, \`.only(\`, \`xit(\`, \`xdescribe(\`, \`it.todo(\`, \`test.todo(\`) on a test the base ran \u2014 and each one is a finding of severity \`minor\` or higher titled \`Test removed \u2014 <file>: <what>\` whose explanation quotes the guard's line exactly as printed. A \`test-guard: <file> \u2014 check: \u2026\` line is a heuristic \u2014 fewer \`expect(\` calls in the file, a title gone while another arrived (a rename or a split) \u2014 and you dispose of every one of them explicitly in your review, never silently: either "weakened", which makes it a finding at \`minor\`, or "refactor, verification intact" with one clause saying why. A line ending \`\u2014 allowed by <spec>\` is licensed by a spec change in the same diff and is neither; \`test-guard ok\` is nothing to report. If the command fails for any reason, judge the same facts from the diff by hand \u2014 a deleted test file, a removed title, a new skip marker, fewer assertions \u2014 and file each one the diff does not license the same way; never install or build to make it run.`;
|
|
1569
|
+
REVIEW_UNIT_CONTRACT = ` 3b. UNIT CONTRACT, when this prompt carries a \`${CONTRACT_HEADING}\` block after the REVIEW TARGET block (its sub-headings, in order: ${CONTRACT_HEADINGS_LIST}): it is what the coding child was handed for this plan unit, rendered by Switchboard from the plan, and the diff is judged against it. Read the unit's Test scenarios and find each one in the diff: a test scenario the unit listed and the diff did not add is a finding of severity \`minor\` titled \`Contract \u2014 test scenario missing: <the scenario>\` \u2014 the same severity as a spec contradiction. For each spec row the block names, check its proof binding resolves in the diff; a named row the diff leaves untouched is disposed of out loud, as in 3a: one clause on why it needed no change, or a finding at \`minor\` titled \`Contract \u2014 spec row not updated: <spec> item <n>\`. A guard the block names that the diff weakens is the guard's own finding (3a). No \`${CONTRACT_HEADING}\` block in this prompt \u2192 nothing to check; skip this step silently.`;
|
|
1264
1570
|
REVIEW_WHOLE_CHANGE = ` - READ THE WHOLE CHANGE: the REVIEW TARGET block states the PR's size as GitHub reports it (files, +/\u2212) and \`diff_digest\` states the totals of what it covered \u2014 they must agree, and every file the digest lists must be in the diff you read. A tool output ending in \`...[truncated N chars]\` was cut short; when the digest or your diff shows fewer files or lines than the PR, read the rest file by file (\`git diff <base>...HEAD -- <path>\`) until every file is covered. Never judge from a partial diff: Switchboard does not post a verdict whose digest covered less than the PR.`;
|
|
1265
1571
|
REVIEW_SYSTEM = `You are Switchboard's code review agent, operating from a Slack request.
|
|
1266
1572
|
|
|
@@ -1276,6 +1582,7 @@ ${REVIEW_WHOLE_CHANGE}
|
|
|
1276
1582
|
- if the PR is enormous (>~6k changed lines), print the riskiest files in full (state mutation, auth, concurrency, data deletion, public APIs) and only the diff hunks for the rest \u2014 and say which files you skimmed
|
|
1277
1583
|
2. ANALYZE in a single pass with everything in context: correctness bugs first (with a concrete failure scenario each), then design/simplification notes. At most 2-3 targeted follow-up reads if a specific caller or callee is load-bearing \u2014 never a general exploration loop.
|
|
1278
1584
|
${REVIEW_SPEC_CHECK}
|
|
1585
|
+
${REVIEW_UNIT_CONTRACT}
|
|
1279
1586
|
4. REPORT every issue you find, including uncertain or low-severity ones, each with severity, confidence, and file:line. Order findings most-severe first. If the change looks correct, say so plainly \u2014 do not manufacture findings.
|
|
1280
1587
|
|
|
1281
1588
|
Do NOT post your review to GitHub yourself \u2014 no \`gh pr comment\`, no API call to create a comment. When the review is of a PR, Switchboard posts your final message to that PR automatically by default (as a comment \u2014 never an approval or a merge); just produce the review as your final message. If the request asks not to post (e.g. "don't post" / "slack only"), Switchboard handles that too \u2014 you still only write the review.
|
|
@@ -1301,6 +1608,7 @@ ${REVIEW_WHOLE_CHANGE}
|
|
|
1301
1608
|
- if the change is enormous (>~6k changed lines), print the riskiest files in full (state mutation, auth, concurrency, data deletion, public APIs) and only the diff hunks for the rest \u2014 and say which files you skimmed
|
|
1302
1609
|
2. ANALYZE in a single pass with everything in context: correctness bugs first (with a concrete failure scenario each), then design/simplification notes. At most 2-3 targeted follow-up reads if a specific caller or callee is load-bearing \u2014 never a general exploration loop.
|
|
1303
1610
|
${REVIEW_SPEC_CHECK}
|
|
1611
|
+
${REVIEW_UNIT_CONTRACT}
|
|
1304
1612
|
4. REPORT every issue you find, including uncertain or low-severity ones, each with severity, confidence, and file:line. Order findings most-severe first. If the change looks correct, say so plainly \u2014 do not manufacture findings.
|
|
1305
1613
|
|
|
1306
1614
|
Do NOT post your review to GitHub yourself \u2014 no API call to create a comment. When the review is of a PR, Switchboard posts your final message to that PR automatically by default (as a comment \u2014 never an approval or a merge); just produce the review as your final message. If the request asks not to post (e.g. "don't post" / "slack only"), Switchboard handles that too \u2014 you still only write the review.
|
|
@@ -10151,8 +10459,107 @@ ${quote(current ?? "")}`;
|
|
|
10151
10459
|
}
|
|
10152
10460
|
});
|
|
10153
10461
|
|
|
10154
|
-
// ../../src/core/commands/
|
|
10462
|
+
// ../../src/core/commands/contract.ts
|
|
10463
|
+
import { basename, dirname as dirname8, join as join10 } from "node:path";
|
|
10155
10464
|
import { z as z6 } from "zod";
|
|
10465
|
+
function repoRootOfPlan(planPath) {
|
|
10466
|
+
const plans = dirname8(planPath);
|
|
10467
|
+
const docs = dirname8(plans);
|
|
10468
|
+
if (basename(plans) === "plans" && basename(docs) === "docs") return dirname8(docs);
|
|
10469
|
+
return plans;
|
|
10470
|
+
}
|
|
10471
|
+
function renderOutput(output) {
|
|
10472
|
+
const o = output;
|
|
10473
|
+
const dropped = Array.isArray(o.dropped) && o.dropped.length > 0 ? o.dropped.map(String).join(", ") : "none";
|
|
10474
|
+
const over = o.overBudget === true ? " \u2014 still over the budget" : "";
|
|
10475
|
+
return `${String(o.text)}
|
|
10476
|
+
|
|
10477
|
+
(${String(o.chars)} characters; budget ${String(o.maxChars)}; dropped: ${dropped}${over})`;
|
|
10478
|
+
}
|
|
10479
|
+
function registerContractCommands(registry) {
|
|
10480
|
+
for (const cmd of contractCommands) registry.register(cmd);
|
|
10481
|
+
}
|
|
10482
|
+
var defineCommand3, SPECS_DIR, RULES_FILES, contractRender, contractCommands;
|
|
10483
|
+
var init_contract2 = __esm({
|
|
10484
|
+
"../../src/core/commands/contract.ts"() {
|
|
10485
|
+
"use strict";
|
|
10486
|
+
init_commandRegistry();
|
|
10487
|
+
init_contract();
|
|
10488
|
+
defineCommand3 = commandDefiner();
|
|
10489
|
+
SPECS_DIR = join10("docs", "reference", "specs");
|
|
10490
|
+
RULES_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
10491
|
+
contractRender = defineCommand3({
|
|
10492
|
+
id: "contract.render",
|
|
10493
|
+
options: z6.object({
|
|
10494
|
+
plan: z6.string().min(1).describe("path of the plan record (docs/plans/<file>.md)"),
|
|
10495
|
+
unit: z6.string().regex(/^U\d+$/, "expected a unit id like U<n>").describe("the unit to render, as its heading spells it (U<n>)"),
|
|
10496
|
+
root: z6.string().min(1).optional().describe("the repository root the specs and rules are read under (default: the root the plan path implies)"),
|
|
10497
|
+
branch: z6.string().min(1).optional().describe("the unit's branch, named in the first instruction"),
|
|
10498
|
+
onto: z6.string().min(1).optional().describe("the merged parent the branch is rebased onto"),
|
|
10499
|
+
maxChars: z6.coerce.number().int().positive().optional().describe(`the rendered block's budget in characters (default ${DEFAULT_CONTRACT_MAX_CHARS})`)
|
|
10500
|
+
}),
|
|
10501
|
+
action: "contract:read",
|
|
10502
|
+
effect: "read",
|
|
10503
|
+
surfaces: { chat: false, mcp: false, http: false },
|
|
10504
|
+
describe: "Render one plan unit's child contract \u2014 its section, the spec rows it names with their proof bindings, the repository's agent rules, the guards \u2014 as the `## Contract` block a coding prompt carries, and measure it.",
|
|
10505
|
+
render: renderOutput,
|
|
10506
|
+
handler: async ({ options, deps }) => {
|
|
10507
|
+
const planMarkdown = await deps.contract.readFile(options.plan);
|
|
10508
|
+
if (planMarkdown === void 0) throw new CommandError("not_found", `plan not found: ${options.plan}`);
|
|
10509
|
+
const root = options.root ?? repoRootOfPlan(options.plan);
|
|
10510
|
+
const unit = parsePlanUnit(planMarkdown, options.unit);
|
|
10511
|
+
const specs = /* @__PURE__ */ new Map();
|
|
10512
|
+
for (const spec of new Set(unit ? specItemRefs(unit.section).map((r) => r.spec) : []))
|
|
10513
|
+
specs.set(spec, await deps.contract.readFile(join10(root, SPECS_DIR, spec)));
|
|
10514
|
+
let agentRules;
|
|
10515
|
+
for (const file of RULES_FILES) {
|
|
10516
|
+
const text = await deps.contract.readFile(join10(root, file));
|
|
10517
|
+
if (text !== void 0) {
|
|
10518
|
+
agentRules = { file, text };
|
|
10519
|
+
break;
|
|
10520
|
+
}
|
|
10521
|
+
}
|
|
10522
|
+
let contract;
|
|
10523
|
+
try {
|
|
10524
|
+
contract = contractFromPlan({
|
|
10525
|
+
planMarkdown,
|
|
10526
|
+
unitId: options.unit,
|
|
10527
|
+
readSpec: (spec) => specs.get(spec),
|
|
10528
|
+
...agentRules ? { agentRules } : {},
|
|
10529
|
+
rebase: { branch: options.branch, onto: options.onto }
|
|
10530
|
+
});
|
|
10531
|
+
} catch (err2) {
|
|
10532
|
+
throw new CommandError("invalid_input", err2 instanceof Error ? err2.message : String(err2));
|
|
10533
|
+
}
|
|
10534
|
+
const maxChars = options.maxChars ?? DEFAULT_CONTRACT_MAX_CHARS;
|
|
10535
|
+
const rendered = renderContract(contract, { maxChars });
|
|
10536
|
+
return {
|
|
10537
|
+
plan: options.plan,
|
|
10538
|
+
root,
|
|
10539
|
+
unit: { id: contract.unit.id, title: contract.unit.title },
|
|
10540
|
+
specRows: contract.specRows.map((r) => ({
|
|
10541
|
+
spec: r.spec,
|
|
10542
|
+
item: r.item,
|
|
10543
|
+
found: r.text !== void 0,
|
|
10544
|
+
validationRows: r.validation.length
|
|
10545
|
+
})),
|
|
10546
|
+
agentRules: agentRules?.file ?? null,
|
|
10547
|
+
chars: rendered.chars,
|
|
10548
|
+
maxChars,
|
|
10549
|
+
dropped: rendered.dropped,
|
|
10550
|
+
overBudget: rendered.overBudget,
|
|
10551
|
+
text: rendered.text
|
|
10552
|
+
};
|
|
10553
|
+
}
|
|
10554
|
+
});
|
|
10555
|
+
contractCommands = [
|
|
10556
|
+
contractRender
|
|
10557
|
+
];
|
|
10558
|
+
}
|
|
10559
|
+
});
|
|
10560
|
+
|
|
10561
|
+
// ../../src/core/commands/deploy.ts
|
|
10562
|
+
import { z as z7 } from "zod";
|
|
10156
10563
|
function toOptions(o, dryRun, affected) {
|
|
10157
10564
|
return {
|
|
10158
10565
|
only: o.only,
|
|
@@ -10262,7 +10669,7 @@ async function copyImages(copies, account2, deps) {
|
|
|
10262
10669
|
function registerDeployCommands(registry) {
|
|
10263
10670
|
for (const cmd of deployCommands) registry.register(cmd);
|
|
10264
10671
|
}
|
|
10265
|
-
var
|
|
10672
|
+
var defineCommand4, workerList, positiveInt, gitRef, deployOptions, planJson, deployPlan, allOptions, copiedImage, listCopies, deployAll, restartOptions, deployRestart, initOptions, deployInit, configOptions, deployConfig, secretNames, secretsOptions, deploySecrets, imagesOptions, deployImages, deployCommands;
|
|
10266
10673
|
var init_deploy = __esm({
|
|
10267
10674
|
"../../src/core/commands/deploy.ts"() {
|
|
10268
10675
|
"use strict";
|
|
@@ -10275,16 +10682,16 @@ var init_deploy = __esm({
|
|
|
10275
10682
|
init_wranglerTemplate();
|
|
10276
10683
|
init_secrets();
|
|
10277
10684
|
init_commandRegistry();
|
|
10278
|
-
|
|
10279
|
-
workerList =
|
|
10685
|
+
defineCommand4 = commandDefiner();
|
|
10686
|
+
workerList = z7.string().transform(
|
|
10280
10687
|
(s) => s.split(",").map((x) => x.trim()).filter(Boolean)
|
|
10281
10688
|
).refine(
|
|
10282
10689
|
(names) => names.every((n2) => DEPLOY_ORDER.includes(n2)),
|
|
10283
10690
|
`expected a comma list of Workers (${DEPLOY_ORDER.join(", ")})`
|
|
10284
10691
|
).transform((names) => [...new Set(names)]);
|
|
10285
|
-
positiveInt =
|
|
10286
|
-
gitRef =
|
|
10287
|
-
deployOptions =
|
|
10692
|
+
positiveInt = z7.coerce.number().int().positive();
|
|
10693
|
+
gitRef = z7.string().regex(/^[A-Za-z0-9_./^~@{}][A-Za-z0-9_./^~@{}-]{0,199}$/, "expected a git revision (a sha, tag, branch, or HEAD^)");
|
|
10694
|
+
deployOptions = z7.object({
|
|
10288
10695
|
only: workerList.optional().describe(
|
|
10289
10696
|
`deploy only these Workers (comma list of ${DEPLOY_ORDER.join(", ")}); with --affected, only those of them that are affected`
|
|
10290
10697
|
),
|
|
@@ -10303,7 +10710,7 @@ var init_deploy = __esm({
|
|
|
10303
10710
|
poll: positiveInt.optional().describe("seconds between preflight retries (default 60)")
|
|
10304
10711
|
});
|
|
10305
10712
|
planJson = (plan) => plan;
|
|
10306
|
-
deployPlan =
|
|
10713
|
+
deployPlan = defineCommand4({
|
|
10307
10714
|
id: "deploy.plan",
|
|
10308
10715
|
options: deployOptions,
|
|
10309
10716
|
action: "deploy:read",
|
|
@@ -10319,7 +10726,7 @@ var init_deploy = __esm({
|
|
|
10319
10726
|
});
|
|
10320
10727
|
copiedImage = (c) => ({ kind: c.kind, source: c.source, target: c.target });
|
|
10321
10728
|
listCopies = (copies) => copies.map((c) => `${c.kind} \u2190 ${c.source}`).join(", ");
|
|
10322
|
-
deployAll =
|
|
10729
|
+
deployAll = defineCommand4({
|
|
10323
10730
|
id: "deploy.all",
|
|
10324
10731
|
options: allOptions,
|
|
10325
10732
|
action: "deploy:write",
|
|
@@ -10370,15 +10777,15 @@ ${formatDeployResults(result.results, result.notAttempted)}`
|
|
|
10370
10777
|
return output(result.results, copies, []);
|
|
10371
10778
|
}
|
|
10372
10779
|
});
|
|
10373
|
-
restartOptions =
|
|
10374
|
-
only:
|
|
10780
|
+
restartOptions = z7.object({
|
|
10781
|
+
only: z7.enum(["bot"]).optional().describe("the Worker to restart \u2014 only `bot` has a long-lived container (default bot)"),
|
|
10375
10782
|
force: flag.optional().describe(
|
|
10376
10783
|
"restart even when the bot cannot be consulted (no JSON on /healthz) \u2014 the fail-closed cases; runs in flight never refuse, they hand off"
|
|
10377
10784
|
),
|
|
10378
10785
|
waitMax: positiveInt.optional().describe("minutes to wait out a refusal (the bot not answering with JSON) before giving up (default 10)"),
|
|
10379
10786
|
poll: positiveInt.optional().describe("seconds between retries while refused (default 60)")
|
|
10380
10787
|
});
|
|
10381
|
-
deployRestart =
|
|
10788
|
+
deployRestart = defineCommand4({
|
|
10382
10789
|
id: "deploy.restart",
|
|
10383
10790
|
options: restartOptions,
|
|
10384
10791
|
action: "deploy:write",
|
|
@@ -10419,12 +10826,12 @@ ${formatDeployResults(result.results, result.notAttempted)}`
|
|
|
10419
10826
|
};
|
|
10420
10827
|
}
|
|
10421
10828
|
});
|
|
10422
|
-
initOptions =
|
|
10829
|
+
initOptions = z7.object({
|
|
10423
10830
|
check: flag.optional().describe(
|
|
10424
10831
|
"compare only: report each rendered file that differs from the one on disk and fail when any does (the `deploy:check` gate); nothing is written"
|
|
10425
10832
|
)
|
|
10426
10833
|
});
|
|
10427
|
-
deployInit =
|
|
10834
|
+
deployInit = defineCommand4({
|
|
10428
10835
|
id: "deploy.init",
|
|
10429
10836
|
options: initOptions,
|
|
10430
10837
|
action: "deploy:write",
|
|
@@ -10471,12 +10878,12 @@ ${formatDeployResults(result.results, result.notAttempted)}`
|
|
|
10471
10878
|
return output;
|
|
10472
10879
|
}
|
|
10473
10880
|
});
|
|
10474
|
-
configOptions =
|
|
10475
|
-
source:
|
|
10881
|
+
configOptions = z7.object({
|
|
10882
|
+
source: z7.string().min(1).optional().describe(
|
|
10476
10883
|
"read the config from this source instead of the profile's configSource (a path, github://owner/repo/path@ref, or op://Vault/Item/field)"
|
|
10477
10884
|
)
|
|
10478
10885
|
});
|
|
10479
|
-
deployConfig =
|
|
10886
|
+
deployConfig = defineCommand4({
|
|
10480
10887
|
id: "deploy.config",
|
|
10481
10888
|
options: configOptions,
|
|
10482
10889
|
action: "deploy:write",
|
|
@@ -10511,21 +10918,21 @@ the bot reads it on its next start: \`deploy restart\``;
|
|
|
10511
10918
|
return output;
|
|
10512
10919
|
}
|
|
10513
10920
|
});
|
|
10514
|
-
secretNames =
|
|
10921
|
+
secretNames = z7.string().transform(
|
|
10515
10922
|
(s) => s.split(",").map((x) => x.trim()).filter(Boolean)
|
|
10516
10923
|
).refine(
|
|
10517
10924
|
(names) => names.length > 0 && names.every((n2) => /^[A-Z][A-Z0-9_]*$/.test(n2)),
|
|
10518
10925
|
"expected a comma list of secret names (SCREAMING_SNAKE)"
|
|
10519
10926
|
).transform((names) => [...new Set(names)]);
|
|
10520
|
-
secretsOptions =
|
|
10927
|
+
secretsOptions = z7.object({
|
|
10521
10928
|
only: secretNames.optional().describe("put only these secrets (comma list of names from deploy/secrets.manifest.json)")
|
|
10522
10929
|
});
|
|
10523
|
-
deploySecrets =
|
|
10930
|
+
deploySecrets = defineCommand4({
|
|
10524
10931
|
id: "deploy.secrets",
|
|
10525
10932
|
args: [
|
|
10526
10933
|
{
|
|
10527
10934
|
name: "worker",
|
|
10528
|
-
schema:
|
|
10935
|
+
schema: z7.enum(DEPLOY_ORDER),
|
|
10529
10936
|
describe: `the Worker to provision (${DEPLOY_ORDER.join(", ")})`
|
|
10530
10937
|
}
|
|
10531
10938
|
],
|
|
@@ -10587,12 +10994,12 @@ the bot reads it on its next start: \`deploy restart\``;
|
|
|
10587
10994
|
return output;
|
|
10588
10995
|
}
|
|
10589
10996
|
});
|
|
10590
|
-
imagesOptions =
|
|
10997
|
+
imagesOptions = z7.object({
|
|
10591
10998
|
dryRun: flag.optional().describe(
|
|
10592
10999
|
"say which images the account registry already holds at the version and which would be copied; nothing is copied"
|
|
10593
11000
|
)
|
|
10594
11001
|
});
|
|
10595
|
-
deployImages =
|
|
11002
|
+
deployImages = defineCommand4({
|
|
10596
11003
|
id: "deploy.images",
|
|
10597
11004
|
options: imagesOptions,
|
|
10598
11005
|
action: "deploy:write",
|
|
@@ -10643,25 +11050,25 @@ the bot reads it on its next start: \`deploy restart\``;
|
|
|
10643
11050
|
});
|
|
10644
11051
|
|
|
10645
11052
|
// ../../src/core/commands/env.ts
|
|
10646
|
-
import { z as
|
|
11053
|
+
import { z as z8 } from "zod";
|
|
10647
11054
|
function registerEnvCommands(registry) {
|
|
10648
11055
|
for (const cmd of envCommands) registry.register(cmd);
|
|
10649
11056
|
}
|
|
10650
|
-
var
|
|
11057
|
+
var defineCommand5, envBootstrap, envCommands;
|
|
10651
11058
|
var init_env = __esm({
|
|
10652
11059
|
"../../src/core/commands/env.ts"() {
|
|
10653
11060
|
"use strict";
|
|
10654
11061
|
init_bootstrap();
|
|
10655
11062
|
init_commandRegistry();
|
|
10656
|
-
|
|
10657
|
-
envBootstrap =
|
|
11063
|
+
defineCommand5 = commandDefiner();
|
|
11064
|
+
envBootstrap = defineCommand5({
|
|
10658
11065
|
id: "env.bootstrap",
|
|
10659
|
-
options:
|
|
10660
|
-
env:
|
|
10661
|
-
service:
|
|
11066
|
+
options: z8.object({
|
|
11067
|
+
env: z8.string().min(1).describe("environment to materialize (UAT only; anything else is refused)"),
|
|
11068
|
+
service: z8.string().min(1).describe("which downstream service's toolchain env to fill"),
|
|
10662
11069
|
apply: flag.optional().describe("resolve the refs and WRITE the chmod-600 env file (default: dry-run)"),
|
|
10663
|
-
out:
|
|
10664
|
-
manifest:
|
|
11070
|
+
out: z8.string().min(1).optional().describe("env-file path (default .agent-env/<service>.<env>.env)"),
|
|
11071
|
+
manifest: z8.string().min(1).optional().describe(`manifest path (default ${DEFAULT_MANIFEST_PATH})`)
|
|
10665
11072
|
}),
|
|
10666
11073
|
action: "env:write",
|
|
10667
11074
|
effect: "write",
|
|
@@ -12260,7 +12667,7 @@ var init_selfImprovement = __esm({
|
|
|
12260
12667
|
});
|
|
12261
12668
|
|
|
12262
12669
|
// ../../src/core/commands/friction.ts
|
|
12263
|
-
import { z as
|
|
12670
|
+
import { z as z9 } from "zod";
|
|
12264
12671
|
async function ledgerOf(deps) {
|
|
12265
12672
|
let ledger;
|
|
12266
12673
|
try {
|
|
@@ -12288,7 +12695,7 @@ function inProgressHint(diagnosis, finished) {
|
|
|
12288
12695
|
function registerFrictionCommands(registry) {
|
|
12289
12696
|
for (const cmd of frictionCommands) registry.register(cmd);
|
|
12290
12697
|
}
|
|
12291
|
-
var
|
|
12698
|
+
var defineCommand6, positiveInt2, repoSlug, NO_LEDGER_MESSAGE, NO_REPO_MESSAGE, asJson, render, visibleRuns, frictionReport, frictionPropose, frictionAnalyze, frictionCommands;
|
|
12292
12699
|
var init_friction = __esm({
|
|
12293
12700
|
"../../src/core/commands/friction.ts"() {
|
|
12294
12701
|
"use strict";
|
|
@@ -12299,19 +12706,19 @@ var init_friction = __esm({
|
|
|
12299
12706
|
init_runEventLines();
|
|
12300
12707
|
init_runFriction();
|
|
12301
12708
|
init_selfImprovement();
|
|
12302
|
-
|
|
12303
|
-
positiveInt2 =
|
|
12304
|
-
repoSlug =
|
|
12709
|
+
defineCommand6 = commandDefiner();
|
|
12710
|
+
positiveInt2 = z9.coerce.number().int().positive();
|
|
12711
|
+
repoSlug = z9.string().refine((s) => /^[\w.-]+\/[\w.-]+$/.test(s), "expected an owner/name slug");
|
|
12305
12712
|
NO_LEDGER_MESSAGE = "Run history is not configured in this deployment (`runHistory`), so there are no recent runs to analyze.";
|
|
12306
12713
|
NO_REPO_MESSAGE = "Set `selfImprovement.repo` (an `owner/name`) in config.yaml to tell `friction propose` where to file issues.";
|
|
12307
12714
|
asJson = (r) => r;
|
|
12308
12715
|
render = (output) => formatSelfImprovementReport(output);
|
|
12309
12716
|
visibleRuns = (caller) => predicateFor(caller.actor, "runs:read", "run");
|
|
12310
|
-
frictionReport =
|
|
12717
|
+
frictionReport = defineCommand6({
|
|
12311
12718
|
id: "friction.report",
|
|
12312
12719
|
enabledWhen: (caps) => caps.runHistory,
|
|
12313
|
-
options:
|
|
12314
|
-
sinceMs:
|
|
12720
|
+
options: z9.object({
|
|
12721
|
+
sinceMs: z9.coerce.number().int().nonnegative().optional().describe("only runs finished at or after this epoch ms"),
|
|
12315
12722
|
limit: positiveInt2.optional().describe("newest n runs (default: the ledger's retained window)"),
|
|
12316
12723
|
minRuns: positiveInt2.optional().describe("distinct runs a pattern must recur in (default selfImprovement.minRuns, else 2)")
|
|
12317
12724
|
}),
|
|
@@ -12337,10 +12744,10 @@ var init_friction = __esm({
|
|
|
12337
12744
|
});
|
|
12338
12745
|
}
|
|
12339
12746
|
});
|
|
12340
|
-
frictionPropose =
|
|
12747
|
+
frictionPropose = defineCommand6({
|
|
12341
12748
|
id: "friction.propose",
|
|
12342
12749
|
enabledWhen: (caps) => caps.runHistory,
|
|
12343
|
-
options:
|
|
12750
|
+
options: z9.object({
|
|
12344
12751
|
dryRun: flag.optional().describe("compute and report everything, file nothing"),
|
|
12345
12752
|
top: positiveInt2.optional().describe("proposals filed per pass (default selfImprovement.top, else 3)"),
|
|
12346
12753
|
minRuns: positiveInt2.optional().describe("distinct runs a pattern must recur in (default selfImprovement.minRuns, else 2)"),
|
|
@@ -12372,17 +12779,17 @@ var init_friction = __esm({
|
|
|
12372
12779
|
}
|
|
12373
12780
|
}
|
|
12374
12781
|
});
|
|
12375
|
-
frictionAnalyze =
|
|
12782
|
+
frictionAnalyze = defineCommand6({
|
|
12376
12783
|
id: "friction.analyze",
|
|
12377
12784
|
args: [
|
|
12378
12785
|
{
|
|
12379
12786
|
name: "source",
|
|
12380
|
-
schema:
|
|
12787
|
+
schema: z9.string().optional(),
|
|
12381
12788
|
describe: "a saved run stream: JSON lines of run events or a curl'ed /runs/:id/events SSE capture (default: stdin)"
|
|
12382
12789
|
}
|
|
12383
12790
|
],
|
|
12384
|
-
options:
|
|
12385
|
-
slowMs:
|
|
12791
|
+
options: z9.object({
|
|
12792
|
+
slowMs: z9.coerce.number().nonnegative().optional().describe("a tool call slower than this many ms is a slow-tool finding"),
|
|
12386
12793
|
inProgress: flag.optional().describe("the capture was taken mid-run: a trailing call without a result is still executing, not a dead run")
|
|
12387
12794
|
}),
|
|
12388
12795
|
action: "friction:read",
|
|
@@ -12435,7 +12842,7 @@ var init_friction = __esm({
|
|
|
12435
12842
|
});
|
|
12436
12843
|
|
|
12437
12844
|
// ../../src/core/commandSurface.ts
|
|
12438
|
-
import { z as
|
|
12845
|
+
import { z as z10 } from "zod";
|
|
12439
12846
|
function camelToKebab(s) {
|
|
12440
12847
|
return s.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
12441
12848
|
}
|
|
@@ -12507,7 +12914,7 @@ function isBooleanSchema(schema) {
|
|
|
12507
12914
|
return false;
|
|
12508
12915
|
}
|
|
12509
12916
|
function jsonSchemaOf(schema) {
|
|
12510
|
-
const out =
|
|
12917
|
+
const out = z10.toJSONSchema(unwrap(schema), { io: "input" });
|
|
12511
12918
|
delete out.$schema;
|
|
12512
12919
|
return out;
|
|
12513
12920
|
}
|
|
@@ -12519,7 +12926,7 @@ function jsonSchemaFor(cmd) {
|
|
|
12519
12926
|
if (!acceptsUndefined(arg.schema)) required.push(arg.name);
|
|
12520
12927
|
}
|
|
12521
12928
|
if (cmd.options) {
|
|
12522
|
-
const opts =
|
|
12929
|
+
const opts = z10.toJSONSchema(cmd.options, { io: "input" });
|
|
12523
12930
|
Object.assign(properties, opts.properties ?? {});
|
|
12524
12931
|
required.push(...opts.required ?? []);
|
|
12525
12932
|
}
|
|
@@ -12731,15 +13138,15 @@ function chatCommandList(commands) {
|
|
|
12731
13138
|
function registerHelpCommands(registry) {
|
|
12732
13139
|
for (const cmd of helpCommands) registry.register(cmd);
|
|
12733
13140
|
}
|
|
12734
|
-
var
|
|
13141
|
+
var defineCommand7, DIRECTIVES_HELP, helpShow, helpCommands;
|
|
12735
13142
|
var init_help = __esm({
|
|
12736
13143
|
"../../src/core/commands/help.ts"() {
|
|
12737
13144
|
"use strict";
|
|
12738
13145
|
init_commandRegistry();
|
|
12739
13146
|
init_commandSurface();
|
|
12740
|
-
|
|
13147
|
+
defineCommand7 = commandDefiner();
|
|
12741
13148
|
DIRECTIVES_HELP = "`agent:review model:anthropic/claude-opus-5 effort:low look at PR 42` (effort: low | medium | high \u2014 lower = faster turns)";
|
|
12742
|
-
helpShow =
|
|
13149
|
+
helpShow = defineCommand7({
|
|
12743
13150
|
id: "help.show",
|
|
12744
13151
|
action: "help:read",
|
|
12745
13152
|
effect: "read",
|
|
@@ -14503,7 +14910,7 @@ var init_service = __esm({
|
|
|
14503
14910
|
});
|
|
14504
14911
|
|
|
14505
14912
|
// ../../src/core/commands/mcp.ts
|
|
14506
|
-
import { z as
|
|
14913
|
+
import { z as z11 } from "zod";
|
|
14507
14914
|
async function serviceOf(deps) {
|
|
14508
14915
|
const svc = await deps.mcp.service();
|
|
14509
14916
|
if ("unavailable" in svc) throw new CommandError("unavailable", svc.unavailable);
|
|
@@ -14597,7 +15004,7 @@ function renderShow(output) {
|
|
|
14597
15004
|
function registerMcpCommands(registry) {
|
|
14598
15005
|
for (const cmd of MCP_COMMANDS) registry.register(cmd);
|
|
14599
15006
|
}
|
|
14600
|
-
var
|
|
15007
|
+
var defineCommand8, serverName, serverUrl, scopeOption, channelOption2, nameArg, mcpList, mcpAdd, mcpConnect, mcpShow, mcpRemove, MCP_COMMANDS;
|
|
14601
15008
|
var init_mcp = __esm({
|
|
14602
15009
|
"../../src/core/commands/mcp.ts"() {
|
|
14603
15010
|
"use strict";
|
|
@@ -14605,18 +15012,18 @@ var init_mcp = __esm({
|
|
|
14605
15012
|
init_service();
|
|
14606
15013
|
init_authorize();
|
|
14607
15014
|
init_commandRegistry();
|
|
14608
|
-
|
|
14609
|
-
serverName =
|
|
14610
|
-
serverUrl =
|
|
14611
|
-
scopeOption =
|
|
15015
|
+
defineCommand8 = commandDefiner();
|
|
15016
|
+
serverName = z11.string().refine((s) => MCP_SERVER_NAME_PATTERN.test(s), "expected a slug: lowercase letters, digits, dashes (\u2264 32 chars)");
|
|
15017
|
+
serverUrl = z11.string().refine((s) => /^https?:\/\/\S+$/.test(s), "expected an http(s) URL");
|
|
15018
|
+
scopeOption = z11.enum(["me", "channel", "org"]).optional().describe(
|
|
14612
15019
|
"whose server: yours (`me`, default), this channel's (`channel`, channel-config rights), or org-wide (`org`, admins)"
|
|
14613
15020
|
);
|
|
14614
|
-
channelOption2 =
|
|
15021
|
+
channelOption2 = z11.string().optional().describe("target channel for `--scope channel` and for `list` (default: the channel you are speaking in)");
|
|
14615
15022
|
nameArg = { name: "name", schema: serverName, describe: "the server's name (see `mcp list`)" };
|
|
14616
|
-
mcpList =
|
|
15023
|
+
mcpList = defineCommand8({
|
|
14617
15024
|
id: "mcp.list",
|
|
14618
15025
|
enabledWhen: (caps) => caps.mcp,
|
|
14619
|
-
options:
|
|
15026
|
+
options: z11.object({ channel: channelOption2 }),
|
|
14620
15027
|
action: "mcp:read",
|
|
14621
15028
|
effect: "read",
|
|
14622
15029
|
describe: "External MCP servers your runs in this channel can use \u2014 org-wide, this channel's, and your own \u2014 with state and agents; never a credential.",
|
|
@@ -14628,7 +15035,7 @@ var init_mcp = __esm({
|
|
|
14628
15035
|
return { servers };
|
|
14629
15036
|
}
|
|
14630
15037
|
});
|
|
14631
|
-
mcpAdd =
|
|
15038
|
+
mcpAdd = defineCommand8({
|
|
14632
15039
|
id: "mcp.add",
|
|
14633
15040
|
enabledWhen: (caps) => caps.mcp,
|
|
14634
15041
|
args: [
|
|
@@ -14638,15 +15045,15 @@ var init_mcp = __esm({
|
|
|
14638
15045
|
describe: "a short slug for the server (becomes the tool prefix mcp__<name>__)"
|
|
14639
15046
|
}
|
|
14640
15047
|
],
|
|
14641
|
-
options:
|
|
15048
|
+
options: z11.object({
|
|
14642
15049
|
url: serverUrl.describe("the server's Streamable-HTTP endpoint (https://\u2026/mcp)"),
|
|
14643
15050
|
scope: scopeOption,
|
|
14644
|
-
agents:
|
|
15051
|
+
agents: z11.string().transform(
|
|
14645
15052
|
(s) => s.split(",").map((a) => a.trim()).filter(Boolean)
|
|
14646
15053
|
).optional().describe(
|
|
14647
15054
|
"comma-separated agents that may use it (default general,research; only org servers may name coding/review/ship)"
|
|
14648
15055
|
),
|
|
14649
|
-
auth:
|
|
15056
|
+
auth: z11.enum(["oauth", "bearer", "none"]).optional().describe(
|
|
14650
15057
|
"how the server authenticates: `oauth` (you sign in on a one-time link), `bearer` (you paste a token on a one-time link), or `none`; omit to detect it from the server"
|
|
14651
15058
|
),
|
|
14652
15059
|
channel: channelOption2
|
|
@@ -14671,11 +15078,11 @@ var init_mcp = __esm({
|
|
|
14671
15078
|
);
|
|
14672
15079
|
}
|
|
14673
15080
|
});
|
|
14674
|
-
mcpConnect =
|
|
15081
|
+
mcpConnect = defineCommand8({
|
|
14675
15082
|
id: "mcp.connect",
|
|
14676
15083
|
enabledWhen: (caps) => caps.mcp,
|
|
14677
15084
|
args: [nameArg],
|
|
14678
|
-
options:
|
|
15085
|
+
options: z11.object({ scope: scopeOption, channel: channelOption2 }),
|
|
14679
15086
|
action: "mcp:write",
|
|
14680
15087
|
effect: "write",
|
|
14681
15088
|
describe: "A fresh one-time link to sign in to an OAuth server or enter (or replace) a bearer server's token \u2014 only you can complete it; it expires in 10 minutes.",
|
|
@@ -14689,11 +15096,11 @@ var init_mcp = __esm({
|
|
|
14689
15096
|
return await via(() => svc.connect(actor, target, args.name));
|
|
14690
15097
|
}
|
|
14691
15098
|
});
|
|
14692
|
-
mcpShow =
|
|
15099
|
+
mcpShow = defineCommand8({
|
|
14693
15100
|
id: "mcp.show",
|
|
14694
15101
|
enabledWhen: (caps) => caps.mcp,
|
|
14695
15102
|
args: [nameArg],
|
|
14696
|
-
options:
|
|
15103
|
+
options: z11.object({ scope: scopeOption, channel: channelOption2 }),
|
|
14697
15104
|
action: "mcp:read",
|
|
14698
15105
|
effect: "read",
|
|
14699
15106
|
describe: "One MCP server's entry plus a live probe of the tools it offers (names, read-only flags); never a credential.",
|
|
@@ -14709,11 +15116,11 @@ var init_mcp = __esm({
|
|
|
14709
15116
|
return await via(() => svc.show(actor, target, args.name));
|
|
14710
15117
|
}
|
|
14711
15118
|
});
|
|
14712
|
-
mcpRemove =
|
|
15119
|
+
mcpRemove = defineCommand8({
|
|
14713
15120
|
id: "mcp.remove",
|
|
14714
15121
|
enabledWhen: (caps) => caps.mcp,
|
|
14715
15122
|
args: [nameArg],
|
|
14716
|
-
options:
|
|
15123
|
+
options: z11.object({ scope: scopeOption, channel: channelOption2 }),
|
|
14717
15124
|
action: "mcp:write",
|
|
14718
15125
|
effect: "write",
|
|
14719
15126
|
describe: "Remove an MCP server you added and its stored credential (yours freely; channel ones need channel-config rights, org-wide ones admin rights).",
|
|
@@ -15151,7 +15558,7 @@ var init_llmOutput = __esm({
|
|
|
15151
15558
|
});
|
|
15152
15559
|
|
|
15153
15560
|
// ../../src/core/memory/reflection.ts
|
|
15154
|
-
import { z as
|
|
15561
|
+
import { z as z12 } from "zod";
|
|
15155
15562
|
function shouldReflect(input) {
|
|
15156
15563
|
if (input.agentName !== void 0 && NO_REFLECT_AGENTS.has(input.agentName)) return false;
|
|
15157
15564
|
return input.toolCalls > 0 || input.historyTurns >= REFLECT_MIN_TURNS;
|
|
@@ -15370,7 +15777,7 @@ var init_reflection = __esm({
|
|
|
15370
15777
|
"Output raw JSON with no code fence and no prose."
|
|
15371
15778
|
].join("\n");
|
|
15372
15779
|
SUMMARY_INHERITANCE = ["user", "repo", "channel"];
|
|
15373
|
-
REFLECTION_ENVELOPE = jsonOutput(
|
|
15780
|
+
REFLECTION_ENVELOPE = jsonOutput(z12.object({ facts: z12.array(z12.unknown()), summary: z12.unknown().optional() }), {
|
|
15374
15781
|
name: "reflection-envelope"
|
|
15375
15782
|
});
|
|
15376
15783
|
SCOPE_KINDS = ["org", "user", "repo", "channel"];
|
|
@@ -15602,7 +16009,7 @@ var init_memory = __esm({
|
|
|
15602
16009
|
});
|
|
15603
16010
|
|
|
15604
16011
|
// ../../src/core/commands/memory.ts
|
|
15605
|
-
import { z as
|
|
16012
|
+
import { z as z13 } from "zod";
|
|
15606
16013
|
function scopeKeyOfMemoryId(id) {
|
|
15607
16014
|
const m = /^mem:(.+):\d+$/.exec(id);
|
|
15608
16015
|
return m ? m[1] : void 0;
|
|
@@ -15656,18 +16063,18 @@ _(limit ${limit} reached \u2014 there may be more; narrow with words or raise \`
|
|
|
15656
16063
|
function registerMemoryCommands(registry) {
|
|
15657
16064
|
for (const cmd of memoryCommands) registry.register(cmd);
|
|
15658
16065
|
}
|
|
15659
|
-
var
|
|
16066
|
+
var defineCommand9, MEMORY_LIST_LIMIT, MEMORY_LIST_MAX_LIMIT, MEMORY_OFF_MESSAGE, memoryId, repoScopeKey, channelScopeKey, isSharedScope, LIST_SCOPES, memoryList, memoryForget, memoryCommands;
|
|
15660
16067
|
var init_memory2 = __esm({
|
|
15661
16068
|
"../../src/core/commands/memory.ts"() {
|
|
15662
16069
|
"use strict";
|
|
15663
16070
|
init_authorize();
|
|
15664
16071
|
init_commandRegistry();
|
|
15665
16072
|
init_memory();
|
|
15666
|
-
|
|
16073
|
+
defineCommand9 = commandDefiner();
|
|
15667
16074
|
MEMORY_LIST_LIMIT = 20;
|
|
15668
16075
|
MEMORY_LIST_MAX_LIMIT = 50;
|
|
15669
16076
|
MEMORY_OFF_MESSAGE = "Memory is off in this deployment (`memory.enabled` is not set), so there is nothing to list or forget.";
|
|
15670
|
-
memoryId =
|
|
16077
|
+
memoryId = z13.string().refine(
|
|
15671
16078
|
(id) => scopeKeyOfMemoryId(id) !== void 0,
|
|
15672
16079
|
"expected a memory id like mem:<scope>:<n> (see `memory list`)"
|
|
15673
16080
|
);
|
|
@@ -15675,21 +16082,21 @@ var init_memory2 = __esm({
|
|
|
15675
16082
|
channelScopeKey = (channelId) => deriveScopeKey("channel", { channelId });
|
|
15676
16083
|
isSharedScope = (key, keys) => key === keys.org || key.startsWith("repo:") || key.startsWith("channel:");
|
|
15677
16084
|
LIST_SCOPES = ["me", "org", "repo", "channel", "all"];
|
|
15678
|
-
memoryList =
|
|
16085
|
+
memoryList = defineCommand9({
|
|
15679
16086
|
id: "memory.list",
|
|
15680
16087
|
enabledWhen: (caps) => caps.memory,
|
|
15681
16088
|
args: [
|
|
15682
16089
|
{
|
|
15683
16090
|
name: "query",
|
|
15684
|
-
schema:
|
|
16091
|
+
schema: z13.string().optional(),
|
|
15685
16092
|
describe: "words a record must mention (whole-token match)",
|
|
15686
16093
|
rest: true
|
|
15687
16094
|
}
|
|
15688
16095
|
],
|
|
15689
|
-
options:
|
|
15690
|
-
scope:
|
|
15691
|
-
limit:
|
|
15692
|
-
repo:
|
|
16096
|
+
options: z13.object({
|
|
16097
|
+
scope: z13.enum(LIST_SCOPES).optional().describe("which scopes to list (default all: yours, this repo's, this channel's, and the shared org scope)"),
|
|
16098
|
+
limit: z13.coerce.number().int().min(1).max(MEMORY_LIST_MAX_LIMIT).optional().describe(`records per scope (default ${MEMORY_LIST_LIMIT}, max ${MEMORY_LIST_MAX_LIMIT})`),
|
|
16099
|
+
repo: z13.string().refine((s) => /^[\w.-]+\/[\w.-]+$/.test(s), "expected an owner/name slug").optional().describe("the repo whose scope to list (default: the repo this thread is bound to)")
|
|
15693
16100
|
}),
|
|
15694
16101
|
action: "memory:read",
|
|
15695
16102
|
effect: "read",
|
|
@@ -15743,7 +16150,7 @@ var init_memory2 = __esm({
|
|
|
15743
16150
|
};
|
|
15744
16151
|
}
|
|
15745
16152
|
});
|
|
15746
|
-
memoryForget =
|
|
16153
|
+
memoryForget = defineCommand9({
|
|
15747
16154
|
id: "memory.forget",
|
|
15748
16155
|
enabledWhen: (caps) => caps.memory,
|
|
15749
16156
|
args: [{ name: "id", schema: memoryId, describe: "the record id (`mem:<scope>:<n>`, from `memory list`)" }],
|
|
@@ -15878,7 +16285,7 @@ var init_residentDiskBudget = __esm({
|
|
|
15878
16285
|
});
|
|
15879
16286
|
|
|
15880
16287
|
// ../../src/core/commands/repo.ts
|
|
15881
|
-
import { z as
|
|
16288
|
+
import { z as z14 } from "zod";
|
|
15882
16289
|
async function adminOf(deps, span) {
|
|
15883
16290
|
const api = await deps.repo.admin();
|
|
15884
16291
|
if ("unavailable" in api) throw new CommandError("unavailable", api.unavailable);
|
|
@@ -15986,7 +16393,7 @@ ${clipOpOutput(text)}
|
|
|
15986
16393
|
\`\`\`` : `${icon} ${str3(o.summary)}`;
|
|
15987
16394
|
}
|
|
15988
16395
|
function defineOp(op) {
|
|
15989
|
-
return
|
|
16396
|
+
return defineCommand10({
|
|
15990
16397
|
id: `repo.${op}`,
|
|
15991
16398
|
// The op needs a backend: the resident service, or the local workspace (`defaultOperations`).
|
|
15992
16399
|
enabledWhen: (caps) => caps.residents || caps.execution === "local",
|
|
@@ -16044,7 +16451,7 @@ function defineOp(op) {
|
|
|
16044
16451
|
function registerRepoCommands(registry) {
|
|
16045
16452
|
for (const cmd of repoCommands) registry.register(cmd);
|
|
16046
16453
|
}
|
|
16047
|
-
var
|
|
16454
|
+
var defineCommand10, DEFAULT_COMMANDS, DEFAULT_REF, SETTLE_POLL_MS, SETTLE_MAX_MS, repoSlug2, gitRef2, command, slugArg, n, obj, str3, repoList, repoOnboard, COMMAND_KEYS, dryRunOptions, repoOffboard, repoRebuild, repoReconfigure, NO_OPS_BACKEND_MESSAGE, repoTest, repoBuild, repoCommands;
|
|
16048
16455
|
var init_repo = __esm({
|
|
16049
16456
|
"../../src/core/commands/repo.ts"() {
|
|
16050
16457
|
"use strict";
|
|
@@ -16052,19 +16459,19 @@ var init_repo = __esm({
|
|
|
16052
16459
|
init_residentAdmin();
|
|
16053
16460
|
init_repoToolchain();
|
|
16054
16461
|
init_residentDiskBudget();
|
|
16055
|
-
|
|
16462
|
+
defineCommand10 = commandDefiner();
|
|
16056
16463
|
DEFAULT_COMMANDS = NPM_FALLBACK_COMMANDS;
|
|
16057
16464
|
DEFAULT_REF = "main";
|
|
16058
16465
|
SETTLE_POLL_MS = 1e4;
|
|
16059
16466
|
SETTLE_MAX_MS = 12 * 6e4;
|
|
16060
|
-
repoSlug2 =
|
|
16061
|
-
gitRef2 =
|
|
16062
|
-
command =
|
|
16467
|
+
repoSlug2 = z14.string().refine((s) => parseSlug(s) !== void 0, "expected a GitHub owner/name slug").transform((s) => parseSlug(s));
|
|
16468
|
+
gitRef2 = z14.string().refine((s) => validRef(s) !== void 0, "expected a plausible git branch ref (e.g. main)");
|
|
16469
|
+
command = z14.string().min(1);
|
|
16063
16470
|
slugArg = { name: "slug", schema: repoSlug2, describe: "GitHub owner/name of the repo" };
|
|
16064
16471
|
n = (v) => String(typeof v === "number" ? v : v ?? "?");
|
|
16065
16472
|
obj = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
|
|
16066
16473
|
str3 = (v) => typeof v === "string" ? v : String(v ?? "?");
|
|
16067
|
-
repoList =
|
|
16474
|
+
repoList = defineCommand10({
|
|
16068
16475
|
id: "repo.list",
|
|
16069
16476
|
enabledWhen: (caps) => caps.residents,
|
|
16070
16477
|
action: "repo:read",
|
|
@@ -16081,11 +16488,11 @@ var init_repo = __esm({
|
|
|
16081
16488
|
return res.data;
|
|
16082
16489
|
}
|
|
16083
16490
|
});
|
|
16084
|
-
repoOnboard =
|
|
16491
|
+
repoOnboard = defineCommand10({
|
|
16085
16492
|
id: "repo.onboard",
|
|
16086
16493
|
enabledWhen: (caps) => caps.residents,
|
|
16087
16494
|
args: [slugArg],
|
|
16088
|
-
options:
|
|
16495
|
+
options: z14.object({
|
|
16089
16496
|
ref: gitRef2.optional().describe(`default branch to keep warm (default ${DEFAULT_REF})`),
|
|
16090
16497
|
test: command.optional().describe(
|
|
16091
16498
|
"the repo's test command (default: detected from the repo root \u2014 `<pm> test`, or a no-op without a test script)"
|
|
@@ -16172,10 +16579,10 @@ var init_repo = __esm({
|
|
|
16172
16579
|
settle: (output, { deps }) => settleProvisioning(deps, str3(output.slug))
|
|
16173
16580
|
});
|
|
16174
16581
|
COMMAND_KEYS = ["install", "build", "test"];
|
|
16175
|
-
dryRunOptions =
|
|
16582
|
+
dryRunOptions = z14.object({
|
|
16176
16583
|
dryRun: flag.optional().describe("compute and return the itemized plan without executing")
|
|
16177
16584
|
});
|
|
16178
|
-
repoOffboard =
|
|
16585
|
+
repoOffboard = defineCommand10({
|
|
16179
16586
|
id: "repo.offboard",
|
|
16180
16587
|
enabledWhen: (caps) => caps.residents,
|
|
16181
16588
|
args: [slugArg],
|
|
@@ -16209,7 +16616,7 @@ var init_repo = __esm({
|
|
|
16209
16616
|
return { ...r.data, slug: args.slug, dryRun };
|
|
16210
16617
|
}
|
|
16211
16618
|
});
|
|
16212
|
-
repoRebuild =
|
|
16619
|
+
repoRebuild = defineCommand10({
|
|
16213
16620
|
id: "repo.rebuild",
|
|
16214
16621
|
enabledWhen: (caps) => caps.residents,
|
|
16215
16622
|
args: [slugArg],
|
|
@@ -16248,11 +16655,11 @@ var init_repo = __esm({
|
|
|
16248
16655
|
return { ...r.data, slug: args.slug, dryRun };
|
|
16249
16656
|
}
|
|
16250
16657
|
});
|
|
16251
|
-
repoReconfigure =
|
|
16658
|
+
repoReconfigure = defineCommand10({
|
|
16252
16659
|
id: "repo.reconfigure",
|
|
16253
16660
|
enabledWhen: (caps) => caps.residents,
|
|
16254
16661
|
args: [slugArg],
|
|
16255
|
-
options:
|
|
16662
|
+
options: z14.object({
|
|
16256
16663
|
ref: gitRef2.optional().describe("new default branch"),
|
|
16257
16664
|
test: command.optional().describe("new test command"),
|
|
16258
16665
|
build: command.optional().describe("new build command"),
|
|
@@ -17479,7 +17886,7 @@ var init_runsService = __esm({
|
|
|
17479
17886
|
});
|
|
17480
17887
|
|
|
17481
17888
|
// ../../src/core/commands/review.ts
|
|
17482
|
-
import { z as
|
|
17889
|
+
import { z as z15 } from "zod";
|
|
17483
17890
|
function artifactJson(a) {
|
|
17484
17891
|
return {
|
|
17485
17892
|
...a.model !== void 0 ? { model: a.model } : {},
|
|
@@ -17548,7 +17955,7 @@ function refused2(err2) {
|
|
|
17548
17955
|
function registerReviewCommands(registry) {
|
|
17549
17956
|
for (const cmd of reviewCommands) registry.register(cmd);
|
|
17550
17957
|
}
|
|
17551
|
-
var
|
|
17958
|
+
var defineCommand11, runId, ABRIDGE_OFF_MESSAGE, reviewAbridge, reviewCommands;
|
|
17552
17959
|
var init_review = __esm({
|
|
17553
17960
|
"../../src/core/commands/review.ts"() {
|
|
17554
17961
|
"use strict";
|
|
@@ -17557,17 +17964,17 @@ var init_review = __esm({
|
|
|
17557
17964
|
init_reviewAbridge();
|
|
17558
17965
|
init_runRecord();
|
|
17559
17966
|
init_runsService();
|
|
17560
|
-
|
|
17561
|
-
runId =
|
|
17967
|
+
defineCommand11 = commandDefiner();
|
|
17968
|
+
runId = z15.string().regex(RUN_ID_PATTERN);
|
|
17562
17969
|
ABRIDGE_OFF_MESSAGE = "The abridged reading diff is off in this deployment: it needs the `meat` binary on the bot host, the Anthropic provider's credential, `review.readingDiff.provider` not `off`, and run history to store it.";
|
|
17563
|
-
reviewAbridge =
|
|
17970
|
+
reviewAbridge = defineCommand11({
|
|
17564
17971
|
id: "review.abridge",
|
|
17565
17972
|
// Hidden unless the abridging can happen here (the binary, the credential,
|
|
17566
17973
|
// the switch) AND there is a record to append to.
|
|
17567
17974
|
enabledWhen: (caps) => caps.runHistory && caps.readingDiffAbridge,
|
|
17568
17975
|
args: [{ name: "id", schema: runId, describe: "run id of a finished PR review" }],
|
|
17569
|
-
options:
|
|
17570
|
-
model:
|
|
17976
|
+
options: z15.object({
|
|
17977
|
+
model: z15.string().min(1).optional().describe("meat's model (default review.readingDiff.meatModel, else claude-opus-5)"),
|
|
17571
17978
|
force: flag.optional().describe("recompute even when an abridged diff is already stored, or retry a failed one"),
|
|
17572
17979
|
wait: flag.optional().describe("block until the abridging finishes and answer the outcome instead of `running`")
|
|
17573
17980
|
}),
|
|
@@ -17599,7 +18006,7 @@ var init_review = __esm({
|
|
|
17599
18006
|
});
|
|
17600
18007
|
|
|
17601
18008
|
// ../../src/core/commands/runs.ts
|
|
17602
|
-
import { z as
|
|
18009
|
+
import { z as z16 } from "zod";
|
|
17603
18010
|
function unwrap2(res) {
|
|
17604
18011
|
if (res.ok) return res.value;
|
|
17605
18012
|
throw new CommandError(res.error, res.error === "not_found" ? "run not found" : "run already finished");
|
|
@@ -17640,7 +18047,7 @@ function wrapEvent(e) {
|
|
|
17640
18047
|
function registerRunsCommands(registry) {
|
|
17641
18048
|
for (const cmd of runsCommands) registry.register(cmd);
|
|
17642
18049
|
}
|
|
17643
|
-
var
|
|
18050
|
+
var defineCommand12, runId2, positiveInt3, idArg, logDenied, asJson2, runsList, runsGet, runsEvents, runsFriction, runsStop, runsCommands;
|
|
17644
18051
|
var init_runs = __esm({
|
|
17645
18052
|
"../../src/core/commands/runs.ts"() {
|
|
17646
18053
|
"use strict";
|
|
@@ -17649,21 +18056,21 @@ var init_runs = __esm({
|
|
|
17649
18056
|
init_commandRegistry();
|
|
17650
18057
|
init_runRecord();
|
|
17651
18058
|
init_runsService();
|
|
17652
|
-
|
|
17653
|
-
runId2 =
|
|
17654
|
-
positiveInt3 =
|
|
18059
|
+
defineCommand12 = commandDefiner();
|
|
18060
|
+
runId2 = z16.string().regex(RUN_ID_PATTERN);
|
|
18061
|
+
positiveInt3 = z16.coerce.number().int().positive();
|
|
17655
18062
|
idArg = { name: "id", schema: runId2, describe: "run id" };
|
|
17656
18063
|
logDenied = (entry) => console.log(JSON.stringify({ audit: "authz", ...entry }));
|
|
17657
18064
|
asJson2 = (v) => v;
|
|
17658
|
-
runsList =
|
|
18065
|
+
runsList = defineCommand12({
|
|
17659
18066
|
id: "runs.list",
|
|
17660
|
-
options:
|
|
17661
|
-
status:
|
|
17662
|
-
agent:
|
|
17663
|
-
channel:
|
|
17664
|
-
sinceMs:
|
|
18067
|
+
options: z16.object({
|
|
18068
|
+
status: z16.enum(["active", "finished", "all"]).default("active").describe("which runs: live (default), persisted, or both"),
|
|
18069
|
+
agent: z16.string().min(1).optional().describe("only runs of this agent"),
|
|
18070
|
+
channel: z16.string().min(1).optional().describe("only runs in this platform-namespaced channel (`slack:C0123`, `http:ops`)"),
|
|
18071
|
+
sinceMs: z16.coerce.number().int().nonnegative().optional().describe("only runs started at or after this epoch ms"),
|
|
17665
18072
|
limit: positiveInt3.max(RUN_LIST_MAX_LIMIT).optional().describe(`page size (max ${RUN_LIST_MAX_LIMIT})`),
|
|
17666
|
-
before:
|
|
18073
|
+
before: z16.coerce.number().int().nonnegative().optional().describe("page cursor: runs finished before this epoch ms"),
|
|
17667
18074
|
beforeId: runId2.optional().describe("page cursor tie-breaker: the last id of the previous page")
|
|
17668
18075
|
}),
|
|
17669
18076
|
action: "runs:read",
|
|
@@ -17674,11 +18081,11 @@ var init_runs = __esm({
|
|
|
17674
18081
|
return asJson2(await (await deps.runs()).listRuns({ ...options, visibleTo }));
|
|
17675
18082
|
}
|
|
17676
18083
|
});
|
|
17677
|
-
runsGet =
|
|
18084
|
+
runsGet = defineCommand12({
|
|
17678
18085
|
id: "runs.get",
|
|
17679
18086
|
args: [idArg],
|
|
17680
|
-
options:
|
|
17681
|
-
include:
|
|
18087
|
+
options: z16.object({
|
|
18088
|
+
include: z16.enum(["messages"]).optional().describe("add the run's events, free text wrapped as untrusted content")
|
|
17682
18089
|
}),
|
|
17683
18090
|
action: "runs:read",
|
|
17684
18091
|
effect: "read",
|
|
@@ -17698,11 +18105,11 @@ var init_runs = __esm({
|
|
|
17698
18105
|
return asJson2(view);
|
|
17699
18106
|
}
|
|
17700
18107
|
});
|
|
17701
|
-
runsEvents =
|
|
18108
|
+
runsEvents = defineCommand12({
|
|
17702
18109
|
id: "runs.events",
|
|
17703
18110
|
args: [idArg],
|
|
17704
|
-
options:
|
|
17705
|
-
afterSeq:
|
|
18111
|
+
options: z16.object({
|
|
18112
|
+
afterSeq: z16.coerce.number().int().nonnegative().optional().describe("events with seq greater than this"),
|
|
17706
18113
|
limit: positiveInt3.max(MAX_EVENTS_PAGE).optional().describe(`page size (max ${MAX_EVENTS_PAGE})`)
|
|
17707
18114
|
}),
|
|
17708
18115
|
action: "runs:read",
|
|
@@ -17716,7 +18123,7 @@ var init_runs = __esm({
|
|
|
17716
18123
|
return asJson2({ ...page2, events: page2.events.map(wrapEvent) });
|
|
17717
18124
|
}
|
|
17718
18125
|
});
|
|
17719
|
-
runsFriction =
|
|
18126
|
+
runsFriction = defineCommand12({
|
|
17720
18127
|
id: "runs.friction",
|
|
17721
18128
|
args: [idArg],
|
|
17722
18129
|
action: "runs:read",
|
|
@@ -17729,10 +18136,10 @@ var init_runs = __esm({
|
|
|
17729
18136
|
return asJson2(unwrap2(await runs.getRunFriction(args.id)));
|
|
17730
18137
|
}
|
|
17731
18138
|
});
|
|
17732
|
-
runsStop =
|
|
18139
|
+
runsStop = defineCommand12({
|
|
17733
18140
|
id: "runs.stop",
|
|
17734
18141
|
args: [idArg],
|
|
17735
|
-
options:
|
|
18142
|
+
options: z16.object({ mode: z16.enum(["soft", "hard"]).describe("soft = finish the current step; hard = abort now") }),
|
|
17736
18143
|
action: "runs:write",
|
|
17737
18144
|
effect: "write",
|
|
17738
18145
|
describe: "Request a live run to stop (`--mode soft` = finish the current step; `hard` = abort now). Records the caller as the actor.",
|
|
@@ -17756,16 +18163,16 @@ var init_runs = __esm({
|
|
|
17756
18163
|
function registerScheduleCommands(registry) {
|
|
17757
18164
|
for (const cmd of scheduleCommands) registry.register(cmd);
|
|
17758
18165
|
}
|
|
17759
|
-
var
|
|
18166
|
+
var defineCommand13, ACTION_LABEL, utc, scheduleList, scheduleCommands;
|
|
17760
18167
|
var init_schedule = __esm({
|
|
17761
18168
|
"../../src/core/commands/schedule.ts"() {
|
|
17762
18169
|
"use strict";
|
|
17763
18170
|
init_commandRegistry();
|
|
17764
18171
|
init_schedules();
|
|
17765
|
-
|
|
18172
|
+
defineCommand13 = commandDefiner();
|
|
17766
18173
|
ACTION_LABEL = { healthz: "keep-alive", watchdog: "resident watchdog" };
|
|
17767
18174
|
utc = (ms) => `${new Date(ms).toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
17768
|
-
scheduleList =
|
|
18175
|
+
scheduleList = defineCommand13({
|
|
17769
18176
|
id: "schedule.list",
|
|
17770
18177
|
enabledWhen: (caps) => caps.schedules,
|
|
17771
18178
|
action: "schedule:read",
|
|
@@ -18029,7 +18436,7 @@ var init_plan2 = __esm({
|
|
|
18029
18436
|
});
|
|
18030
18437
|
|
|
18031
18438
|
// ../../src/core/commands/setup.ts
|
|
18032
|
-
import { z as
|
|
18439
|
+
import { z as z17 } from "zod";
|
|
18033
18440
|
async function gatherAnswers(o, setup) {
|
|
18034
18441
|
const ask = setup.prompt;
|
|
18035
18442
|
const given = (v) => v !== void 0;
|
|
@@ -18099,33 +18506,33 @@ function capabilityLine(c) {
|
|
|
18099
18506
|
function registerSetupCommands(registry) {
|
|
18100
18507
|
for (const cmd of setupCommands) registry.register(cmd);
|
|
18101
18508
|
}
|
|
18102
|
-
var
|
|
18509
|
+
var defineCommand14, secret, digits, workerName, hostname2, httpUrl, account, initOptions2, DEFAULT_NAME, setupInit, ASK, setupCommands;
|
|
18103
18510
|
var init_setup = __esm({
|
|
18104
18511
|
"../../src/core/commands/setup.ts"() {
|
|
18105
18512
|
"use strict";
|
|
18106
18513
|
init_plan2();
|
|
18107
18514
|
init_commandRegistry();
|
|
18108
18515
|
init_deploy();
|
|
18109
|
-
|
|
18110
|
-
secret =
|
|
18111
|
-
digits =
|
|
18112
|
-
workerName =
|
|
18113
|
-
hostname2 =
|
|
18114
|
-
httpUrl =
|
|
18115
|
-
account =
|
|
18116
|
-
initOptions2 =
|
|
18117
|
-
organization:
|
|
18516
|
+
defineCommand14 = commandDefiner();
|
|
18517
|
+
secret = z17.string().min(1);
|
|
18518
|
+
digits = z17.string().regex(/^\d+$/, "expected a number");
|
|
18519
|
+
workerName = z17.string().regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "expected a Worker name: lowercase letters, digits, hyphens");
|
|
18520
|
+
hostname2 = z17.string().regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/, "expected a bare DNS name (example.com)");
|
|
18521
|
+
httpUrl = z17.string().regex(/^https?:\/\/\S+$/, "expected an http(s) URL");
|
|
18522
|
+
account = z17.string().regex(/^[0-9a-f]{32}$/, "expected a Cloudflare account id: 32 hex characters");
|
|
18523
|
+
initOptions2 = z17.object({
|
|
18524
|
+
organization: z17.string().min(1).optional().describe("the GitHub organization (or user) this installation serves \u2014 config.yaml's `organization`"),
|
|
18118
18525
|
anthropicKey: secret.optional().describe("an Anthropic API key \u2192 ANTHROPIC_API_KEY and the `anthropic` provider"),
|
|
18119
18526
|
openaiCompatible: httpUrl.optional().describe(
|
|
18120
18527
|
"an OpenAI-compatible endpoint's base URL (https://api.openai.com/v1, http://localhost:11434/v1) \u2192 the `openai` provider"
|
|
18121
18528
|
),
|
|
18122
|
-
model:
|
|
18529
|
+
model: z17.string().min(1).optional().describe("with --openai-compatible: the model every agent runs on at that endpoint (openai/<model>)"),
|
|
18123
18530
|
modelKey: secret.optional().describe("with --openai-compatible: the endpoint's key \u2192 OPENAI_API_KEY (a local endpoint needs none)"),
|
|
18124
18531
|
slackAppToken: secret.optional().describe("the Slack app-level token (xapp-\u2026) \u2192 SLACK_APP_TOKEN"),
|
|
18125
18532
|
slackBotToken: secret.optional().describe("the Slack bot token (xoxb-\u2026) \u2192 SLACK_BOT_TOKEN"),
|
|
18126
18533
|
githubAppId: digits.optional().describe("the GitHub App's id \u2192 GITHUB_APP_ID"),
|
|
18127
18534
|
githubInstallationId: digits.optional().describe("the GitHub App's installation id \u2192 GITHUB_APP_INSTALLATION_ID"),
|
|
18128
|
-
githubPrivateKeyFile:
|
|
18535
|
+
githubPrivateKeyFile: z17.string().min(1).optional().describe("path to the GitHub App's private key (PEM) \u2192 GITHUB_APP_PRIVATE_KEY, one quoted line"),
|
|
18129
18536
|
cloudflare: account.optional().describe(
|
|
18130
18537
|
"a Cloudflare account id: also write deploy/profile.json and render the Worker configs (needs --zone; from a checkout's root, or anywhere when this CLI is the published package)"
|
|
18131
18538
|
),
|
|
@@ -18135,7 +18542,7 @@ var init_setup = __esm({
|
|
|
18135
18542
|
dryRun: flag.optional().describe("write nothing; print each file as it would be written, secrets masked")
|
|
18136
18543
|
});
|
|
18137
18544
|
DEFAULT_NAME = "switchboard";
|
|
18138
|
-
setupInit =
|
|
18545
|
+
setupInit = defineCommand14({
|
|
18139
18546
|
id: "setup.init",
|
|
18140
18547
|
options: initOptions2,
|
|
18141
18548
|
action: "setup:write",
|
|
@@ -18235,13 +18642,13 @@ function renderStatus(output) {
|
|
|
18235
18642
|
function registerStatusCommands(registry) {
|
|
18236
18643
|
for (const cmd of statusCommands) registry.register(cmd);
|
|
18237
18644
|
}
|
|
18238
|
-
var
|
|
18645
|
+
var defineCommand15, statusShow, statusCommands;
|
|
18239
18646
|
var init_status = __esm({
|
|
18240
18647
|
"../../src/core/commands/status.ts"() {
|
|
18241
18648
|
"use strict";
|
|
18242
18649
|
init_commandRegistry();
|
|
18243
|
-
|
|
18244
|
-
statusShow =
|
|
18650
|
+
defineCommand15 = commandDefiner();
|
|
18651
|
+
statusShow = defineCommand15({
|
|
18245
18652
|
id: "status.show",
|
|
18246
18653
|
action: "status:read",
|
|
18247
18654
|
effect: "read",
|
|
@@ -18278,6 +18685,7 @@ function registerCoreCommands(registry) {
|
|
|
18278
18685
|
registerDeployCommands(registry);
|
|
18279
18686
|
registerEnvCommands(registry);
|
|
18280
18687
|
registerSetupCommands(registry);
|
|
18688
|
+
registerContractCommands(registry);
|
|
18281
18689
|
}
|
|
18282
18690
|
function coreCommandGroups() {
|
|
18283
18691
|
const registry = new CommandRegistry({ audit: () => {
|
|
@@ -18290,6 +18698,7 @@ var init_all = __esm({
|
|
|
18290
18698
|
"use strict";
|
|
18291
18699
|
init_commandRegistry();
|
|
18292
18700
|
init_config3();
|
|
18701
|
+
init_contract2();
|
|
18293
18702
|
init_deploy();
|
|
18294
18703
|
init_env();
|
|
18295
18704
|
init_friction();
|
|
@@ -18445,7 +18854,8 @@ var init_githubRepoInspect = __esm({
|
|
|
18445
18854
|
});
|
|
18446
18855
|
|
|
18447
18856
|
// ../../src/core/commandCatalogue.ts
|
|
18448
|
-
import { readFileSync as readFileSync12 } from "node:fs";
|
|
18857
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
18858
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
18449
18859
|
function unstampedStatus() {
|
|
18450
18860
|
let version2 = "unknown";
|
|
18451
18861
|
try {
|
|
@@ -18471,6 +18881,9 @@ function defaultOperations(config, secrets, caller) {
|
|
|
18471
18881
|
function readSource(source2) {
|
|
18472
18882
|
return Promise.resolve(readFileSync12(source2 === "-" ? 0 : source2, "utf8"));
|
|
18473
18883
|
}
|
|
18884
|
+
async function readOptionalFile(path) {
|
|
18885
|
+
return existsSync10(path) ? readFile2(path, "utf8") : void 0;
|
|
18886
|
+
}
|
|
18474
18887
|
function once(provided) {
|
|
18475
18888
|
if (typeof provided !== "function") {
|
|
18476
18889
|
const ready = Promise.resolve(provided);
|
|
@@ -18559,7 +18972,9 @@ function buildCoreCommands(config, store, wiring) {
|
|
|
18559
18972
|
env: { bootstrap: bootstrapOnHost },
|
|
18560
18973
|
status: { snapshot: () => (wiring.status ?? unstampedStatus)() },
|
|
18561
18974
|
// `setup init` writes the operator's working directory — the one the CLI runs in.
|
|
18562
|
-
setup: hostSetupIO()
|
|
18975
|
+
setup: hostSetupIO(),
|
|
18976
|
+
// `contract render` reads the paths the CLI caller names (CLI-only).
|
|
18977
|
+
contract: { readFile: readOptionalFile }
|
|
18563
18978
|
};
|
|
18564
18979
|
return bindCommands(registry, deps);
|
|
18565
18980
|
}
|
|
@@ -19535,7 +19950,7 @@ var init_tracingExecutor = __esm({
|
|
|
19535
19950
|
});
|
|
19536
19951
|
|
|
19537
19952
|
// ../../src/core/prDescription.ts
|
|
19538
|
-
import { z as
|
|
19953
|
+
import { z as z18 } from "zod";
|
|
19539
19954
|
function parsePrDescription(input) {
|
|
19540
19955
|
return PrDescriptionSchema.parse(input);
|
|
19541
19956
|
}
|
|
@@ -19823,34 +20238,34 @@ var init_prDescription = __esm({
|
|
|
19823
20238
|
"../../src/core/prDescription.ts"() {
|
|
19824
20239
|
"use strict";
|
|
19825
20240
|
init_redact();
|
|
19826
|
-
line =
|
|
19827
|
-
prose =
|
|
19828
|
-
TourAnchorSchema =
|
|
20241
|
+
line = z18.string().trim().min(1).refine((s) => !/[\r\n]/.test(s), "must be a single line (no embedded newlines)");
|
|
20242
|
+
prose = z18.string().trim().min(1);
|
|
20243
|
+
TourAnchorSchema = z18.object({
|
|
19829
20244
|
// Repo-relative: no leading `/`, no `..` SEGMENT (a filename like `a..b.ts` is fine).
|
|
19830
|
-
path:
|
|
19831
|
-
from:
|
|
19832
|
-
to:
|
|
20245
|
+
path: z18.string().trim().min(1).refine((p) => !p.startsWith("/") && !p.split("/").includes(".."), "path must be repo-relative"),
|
|
20246
|
+
from: z18.number().int().positive(),
|
|
20247
|
+
to: z18.number().int().positive()
|
|
19833
20248
|
}).refine((a) => a.to >= a.from, "to must be >= from");
|
|
19834
|
-
TourStepSchema =
|
|
20249
|
+
TourStepSchema = z18.object({
|
|
19835
20250
|
title: line,
|
|
19836
20251
|
description: prose,
|
|
19837
20252
|
lookFor: prose.optional(),
|
|
19838
20253
|
anchor: TourAnchorSchema
|
|
19839
20254
|
});
|
|
19840
|
-
PrDescriptionSchema =
|
|
20255
|
+
PrDescriptionSchema = z18.object({
|
|
19841
20256
|
/** The PR title's single source. Metadata for the PR's own title field —
|
|
19842
20257
|
* never rendered into the body (GitHub shows the title itself). */
|
|
19843
20258
|
title: line,
|
|
19844
20259
|
tldr: prose,
|
|
19845
20260
|
whatWhy: prose,
|
|
19846
|
-
tour:
|
|
20261
|
+
tour: z18.array(TourStepSchema).min(1),
|
|
19847
20262
|
/** Every touched file the Tour steps did not cover, one line each. */
|
|
19848
|
-
remaining:
|
|
19849
|
-
decisions:
|
|
20263
|
+
remaining: z18.array(z18.object({ path: line, note: prose })),
|
|
20264
|
+
decisions: z18.array(z18.object({ title: line, rationale: prose })).min(1),
|
|
19850
20265
|
risks: prose,
|
|
19851
|
-
validation:
|
|
20266
|
+
validation: z18.object({
|
|
19852
20267
|
summary: prose.optional(),
|
|
19853
|
-
criteria:
|
|
20268
|
+
criteria: z18.array(z18.object({ criterion: prose, proof: prose })).min(1)
|
|
19854
20269
|
})
|
|
19855
20270
|
});
|
|
19856
20271
|
REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
@@ -19994,7 +20409,7 @@ var init_reviewVerdict = __esm({
|
|
|
19994
20409
|
});
|
|
19995
20410
|
|
|
19996
20411
|
// ../../src/core/diffDigest.ts
|
|
19997
|
-
function
|
|
20412
|
+
function basename2(p) {
|
|
19998
20413
|
const i = p.lastIndexOf("/");
|
|
19999
20414
|
return i === -1 ? p : p.slice(i + 1);
|
|
20000
20415
|
}
|
|
@@ -20031,7 +20446,7 @@ function riskReasons(entry, paths) {
|
|
|
20031
20446
|
if (any(AUTH_RE)) reasons.push("auth/permission-sensitive");
|
|
20032
20447
|
if (any(INFRA_RE)) reasons.push("infra/deploy config");
|
|
20033
20448
|
if (entry.status === "deleted") reasons.push("whole-file deletion");
|
|
20034
|
-
if (candidates.some((p) => LOCKFILES.has(
|
|
20449
|
+
if (candidates.some((p) => LOCKFILES.has(basename2(p).toLowerCase()))) reasons.push("lockfile");
|
|
20035
20450
|
const churn = entry.adds + entry.dels;
|
|
20036
20451
|
if (churn >= LARGE_CHURN) reasons.push(`large change (${churn} lines)`);
|
|
20037
20452
|
return reasons;
|
|
@@ -20626,7 +21041,7 @@ ${skill.body}${footer}`;
|
|
|
20626
21041
|
});
|
|
20627
21042
|
|
|
20628
21043
|
// ../../src/tools/workspace.ts
|
|
20629
|
-
import { z as
|
|
21044
|
+
import { z as z19 } from "zod";
|
|
20630
21045
|
var bashTool, readFileTool, writeFileTool, NAME_STATUS_MARK, NAME_STATUS_MARK_LINE, OUTPUT_CUT_RE, diffDigestTool, submitVerdictTool, submitDispositionsTool, submitPrDescriptionTool, updateStatusTool, TOOLSETS;
|
|
20631
21046
|
var init_workspace = __esm({
|
|
20632
21047
|
"../../src/tools/workspace.ts"() {
|
|
@@ -20930,7 +21345,7 @@ ${raw.trim()}`;
|
|
|
20930
21345
|
try {
|
|
20931
21346
|
desc = parsePrDescription(input);
|
|
20932
21347
|
} catch (err2) {
|
|
20933
|
-
const detail = err2 instanceof
|
|
21348
|
+
const detail = err2 instanceof z19.ZodError ? err2.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ") : String(err2);
|
|
20934
21349
|
return `error: invalid PR description \u2014 ${detail}`;
|
|
20935
21350
|
}
|
|
20936
21351
|
ctx.onPrDescription?.(desc);
|
|
@@ -21791,14 +22206,9 @@ Target repository: ${input.repo}. ` + (workspace ? `Your shell starts in the wor
|
|
|
21791
22206
|
...head.verified ? { verifiedAtAttach: true } : {}
|
|
21792
22207
|
}) : void 0;
|
|
21793
22208
|
const agentSystem = (head) => {
|
|
21794
|
-
|
|
21795
|
-
const baseSystem = target ? `${residentSystem ?? agent.system}
|
|
21796
|
-
|
|
21797
|
-
${target}` : residentSystem;
|
|
21798
|
-
const trailing = [blocks.skills, blocks.mcp].filter((b) => Boolean(b));
|
|
21799
|
-
return trailing.length > 0 ? [baseSystem ?? agent.system, ...trailing].join("\n\n") : baseSystem;
|
|
22209
|
+
return [residentSystem ?? agent.system, targetBlock(head), input.contract, blocks.skills, blocks.mcp].filter((b) => Boolean(b)).join("\n\n");
|
|
21800
22210
|
};
|
|
21801
|
-
return (head) => [blocks.memory, blocks.config, blocks.about, blocks.instructions, agentSystem(head)
|
|
22211
|
+
return (head) => [blocks.memory, blocks.config, blocks.about, blocks.instructions, agentSystem(head)].filter((part) => Boolean(part)).join("\n\n");
|
|
21802
22212
|
}
|
|
21803
22213
|
async function settleReviewedHead(input) {
|
|
21804
22214
|
return input.span ? input.span.span("run.settle_reviewed_head", (span) => settle(input, span)) : settle(input, void 0);
|
|
@@ -24129,15 +24539,15 @@ var init_frontmatter = __esm({
|
|
|
24129
24539
|
});
|
|
24130
24540
|
|
|
24131
24541
|
// ../../src/skills/stores.ts
|
|
24132
|
-
import { existsSync as
|
|
24133
|
-
import { join as
|
|
24542
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13, readdirSync as readdirSync2 } from "node:fs";
|
|
24543
|
+
import { join as join11 } from "node:path";
|
|
24134
24544
|
function loadBundledSkills(dir) {
|
|
24135
|
-
if (!
|
|
24545
|
+
if (!existsSync11(dir)) return [];
|
|
24136
24546
|
const skills = [];
|
|
24137
24547
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
24138
24548
|
if (!entry.isDirectory()) continue;
|
|
24139
|
-
const file =
|
|
24140
|
-
if (!
|
|
24549
|
+
const file = join11(dir, entry.name, "SKILL.md");
|
|
24550
|
+
if (!existsSync11(file)) continue;
|
|
24141
24551
|
try {
|
|
24142
24552
|
skills.push(parseSkillMarkdown(readFileSync13(file, "utf8")));
|
|
24143
24553
|
} catch (err2) {
|
|
@@ -25746,9 +26156,18 @@ ${findings}
|
|
|
25746
26156
|
Review:
|
|
25747
26157
|
${input.review}`;
|
|
25748
26158
|
}
|
|
26159
|
+
function withContractInFirstUserTurn(messages, block) {
|
|
26160
|
+
const at = messages.findIndex((m) => m.role === "user");
|
|
26161
|
+
if (at < 0) return [...messages, { role: "user", content: [{ type: "text", text: block }] }];
|
|
26162
|
+
return messages.map((m, i) => i === at ? { ...m, content: [...m.content, { type: "text", text: block }] } : m);
|
|
26163
|
+
}
|
|
25749
26164
|
async function runShipCodingChild(input, ctx, opts, roundSpan) {
|
|
25750
26165
|
const { entry, clip: clip3, now } = ctx;
|
|
25751
26166
|
const { control, github, logKey } = input;
|
|
26167
|
+
const messages = opts.contract ? withContractInFirstUserTurn(
|
|
26168
|
+
opts.messages,
|
|
26169
|
+
renderContract(opts.contract, { maxChars: DEFAULT_CONTRACT_MAX_CHARS }).text
|
|
26170
|
+
) : opts.messages;
|
|
25752
26171
|
const spec = input.child("coding");
|
|
25753
26172
|
const ws = await attachRoundWorkspace({
|
|
25754
26173
|
factory: input.factory,
|
|
@@ -25828,7 +26247,7 @@ ${shipBranchContract(entry.branch)}`;
|
|
|
25828
26247
|
provider: spec.provider,
|
|
25829
26248
|
model: spec.model,
|
|
25830
26249
|
agent: clip3(spec.agent),
|
|
25831
|
-
messages
|
|
26250
|
+
messages,
|
|
25832
26251
|
...roundSpan ? { span: roundSpan } : {},
|
|
25833
26252
|
backend: ws.selection.backend,
|
|
25834
26253
|
system,
|
|
@@ -25850,7 +26269,7 @@ ${shipBranchContract(entry.branch)}`;
|
|
|
25850
26269
|
});
|
|
25851
26270
|
if (turnTarget) {
|
|
25852
26271
|
descriptionTurnRan = true;
|
|
25853
|
-
const turnMessages = [...
|
|
26272
|
+
const turnMessages = [...messages];
|
|
25854
26273
|
const run2 = (span) => runDescriptionTurn({
|
|
25855
26274
|
...span ? { span } : {},
|
|
25856
26275
|
target: turnTarget,
|
|
@@ -25920,6 +26339,7 @@ var init_codingChild = __esm({
|
|
|
25920
26339
|
init_reviewVerdict();
|
|
25921
26340
|
init_codingPrPostStep();
|
|
25922
26341
|
init_reviewRound();
|
|
26342
|
+
init_contract();
|
|
25923
26343
|
}
|
|
25924
26344
|
});
|
|
25925
26345
|
|
|
@@ -26003,6 +26423,7 @@ async function runShipReviewChild(input, ctx, round, roundSpan) {
|
|
|
26003
26423
|
repo: entry.repo,
|
|
26004
26424
|
workspace: binding?.workspace,
|
|
26005
26425
|
prTarget: { repo: entry.repo, pr: pr.number, ref: entry.branch, baseRef: entry.base },
|
|
26426
|
+
...round.contract ? { contract: renderContract(round.contract, { maxChars: DEFAULT_CONTRACT_MAX_CHARS }).text } : {},
|
|
26006
26427
|
blocks: input.blocks(spec)
|
|
26007
26428
|
});
|
|
26008
26429
|
const clipped = clip3(spec.agent);
|
|
@@ -26091,6 +26512,7 @@ var init_reviewChild = __esm({
|
|
|
26091
26512
|
init_reviewVerdict();
|
|
26092
26513
|
init_reviewedHead();
|
|
26093
26514
|
init_reviewRound();
|
|
26515
|
+
init_contract();
|
|
26094
26516
|
}
|
|
26095
26517
|
});
|
|
26096
26518
|
|
|
@@ -27775,8 +28197,8 @@ var init_runHistoryWriter = __esm({
|
|
|
27775
28197
|
});
|
|
27776
28198
|
|
|
27777
28199
|
// ../../src/core/runStore.ts
|
|
27778
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
27779
|
-
import { join as
|
|
28200
|
+
import { chmodSync as chmodSync2, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28201
|
+
import { join as join12, resolve as resolve9 } from "node:path";
|
|
27780
28202
|
function isValidRunId(id) {
|
|
27781
28203
|
return typeof id === "string" && RUN_ID_PATTERN.test(id);
|
|
27782
28204
|
}
|
|
@@ -27818,7 +28240,7 @@ function buildRunStore(cfg, secrets, deps) {
|
|
|
27818
28240
|
const now = deps.now ?? Date.now;
|
|
27819
28241
|
const policy = retentionPolicyOf(cfg);
|
|
27820
28242
|
if (cfg.store === "file") {
|
|
27821
|
-
const store = new FileRunStore(
|
|
28243
|
+
const store = new FileRunStore(join12(deps.dataDir, RUNS_DIR), { policy, now });
|
|
27822
28244
|
store.sweep();
|
|
27823
28245
|
const timer = (deps.setInterval ?? setInterval)(() => store.sweep(), SWEEP_INTERVAL_MS);
|
|
27824
28246
|
timer.unref?.();
|
|
@@ -27885,7 +28307,7 @@ var init_runStore = __esm({
|
|
|
27885
28307
|
this.now = opts.now ?? Date.now;
|
|
27886
28308
|
}
|
|
27887
28309
|
recordPath(id) {
|
|
27888
|
-
return
|
|
28310
|
+
return join12(this.dir, `${id}.json`);
|
|
27889
28311
|
}
|
|
27890
28312
|
ensureDir() {
|
|
27891
28313
|
mkdirSync8(this.dir, { recursive: true, mode: DIR_MODE });
|
|
@@ -27907,8 +28329,8 @@ var init_runStore = __esm({
|
|
|
27907
28329
|
}
|
|
27908
28330
|
/** Every valid index line, a repeated id resolved to its last line. */
|
|
27909
28331
|
readIndex() {
|
|
27910
|
-
const path =
|
|
27911
|
-
if (!
|
|
28332
|
+
const path = join12(this.dir, INDEX_FILE);
|
|
28333
|
+
if (!existsSync12(path)) return [];
|
|
27912
28334
|
const byId = /* @__PURE__ */ new Map();
|
|
27913
28335
|
for (const line2 of readFileSync14(path, "utf8").split("\n")) {
|
|
27914
28336
|
if (!line2.trim()) continue;
|
|
@@ -27939,7 +28361,7 @@ var init_runStore = __esm({
|
|
|
27939
28361
|
const keptIds = new Set(kept.map((r) => r.id));
|
|
27940
28362
|
for (const item of index) if (!keptIds.has(item.id)) rmSync3(this.recordPath(item.id), { force: true });
|
|
27941
28363
|
this.writeAtomic(
|
|
27942
|
-
|
|
28364
|
+
join12(this.dir, INDEX_FILE),
|
|
27943
28365
|
kept.map((r) => JSON.stringify(r)).join("\n") + (kept.length ? "\n" : "")
|
|
27944
28366
|
);
|
|
27945
28367
|
return kept;
|
|
@@ -27989,7 +28411,7 @@ var init_runStore = __esm({
|
|
|
27989
28411
|
async delete(id) {
|
|
27990
28412
|
if (!isValidRunId(id)) return;
|
|
27991
28413
|
const index = this.readIndex();
|
|
27992
|
-
if (!index.some((r) => r.id === id) && !
|
|
28414
|
+
if (!index.some((r) => r.id === id) && !existsSync12(this.recordPath(id))) return;
|
|
27993
28415
|
this.ensureDir();
|
|
27994
28416
|
rmSync3(this.recordPath(id), { force: true });
|
|
27995
28417
|
this.compact(index.filter((r) => r.id !== id));
|
|
@@ -27997,7 +28419,7 @@ var init_runStore = __esm({
|
|
|
27997
28419
|
/** Delete everything outside policy now (start-up and the 6 h timer). A
|
|
27998
28420
|
* missing directory means nothing to sweep. */
|
|
27999
28421
|
sweep() {
|
|
28000
|
-
if (!
|
|
28422
|
+
if (!existsSync12(this.dir)) return;
|
|
28001
28423
|
this.compact(this.readIndex());
|
|
28002
28424
|
}
|
|
28003
28425
|
};
|
|
@@ -28807,8 +29229,8 @@ var init_registry3 = __esm({
|
|
|
28807
29229
|
});
|
|
28808
29230
|
|
|
28809
29231
|
// ../../src/mcp/secretStore.ts
|
|
28810
|
-
import { existsSync as
|
|
28811
|
-
import { dirname as
|
|
29232
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
29233
|
+
import { dirname as dirname9, resolve as resolve10 } from "node:path";
|
|
28812
29234
|
var FileMcpSecretStore, MCP_SECRET_WORKER_TIMEOUT_MS, WorkerMcpSecretStore;
|
|
28813
29235
|
var init_secretStore = __esm({
|
|
28814
29236
|
"../../src/mcp/secretStore.ts"() {
|
|
@@ -28821,7 +29243,7 @@ var init_secretStore = __esm({
|
|
|
28821
29243
|
this.path = resolve10(path);
|
|
28822
29244
|
}
|
|
28823
29245
|
read() {
|
|
28824
|
-
if (!
|
|
29246
|
+
if (!existsSync13(this.path)) return { credentials: {}, tickets: {} };
|
|
28825
29247
|
const raw = JSON.parse(readFileSync15(this.path, "utf8"));
|
|
28826
29248
|
const credentials = {};
|
|
28827
29249
|
for (const [k, v] of Object.entries(raw.credentials ?? {})) if (isSealedCredential(v)) credentials[k] = v;
|
|
@@ -28830,7 +29252,7 @@ var init_secretStore = __esm({
|
|
|
28830
29252
|
return { credentials, tickets };
|
|
28831
29253
|
}
|
|
28832
29254
|
write(doc) {
|
|
28833
|
-
mkdirSync9(
|
|
29255
|
+
mkdirSync9(dirname9(this.path), { recursive: true });
|
|
28834
29256
|
writeFileSync8(this.path, JSON.stringify(doc, null, 2));
|
|
28835
29257
|
}
|
|
28836
29258
|
async putCredential(sealed) {
|
|
@@ -31582,18 +32004,18 @@ var init_liveView = __esm({
|
|
|
31582
32004
|
|
|
31583
32005
|
// ../../src/channels/webAssets.ts
|
|
31584
32006
|
import { readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync3 } from "node:fs";
|
|
31585
|
-
import { join as
|
|
32007
|
+
import { join as join13 } from "node:path";
|
|
31586
32008
|
function contentTypeFor(path) {
|
|
31587
32009
|
const dot = path.lastIndexOf(".");
|
|
31588
32010
|
return (dot === -1 ? void 0 : CONTENT_TYPES[path.slice(dot)]) ?? "application/octet-stream";
|
|
31589
32011
|
}
|
|
31590
32012
|
function webDistDir(env, packageRoot) {
|
|
31591
|
-
return env.SWITCHBOARD_WEB_DIST ??
|
|
32013
|
+
return env.SWITCHBOARD_WEB_DIST ?? join13(packageRoot, WEB_DIST_DIR);
|
|
31592
32014
|
}
|
|
31593
32015
|
function loadWebAssets(distDir) {
|
|
31594
32016
|
let manifest;
|
|
31595
32017
|
try {
|
|
31596
|
-
manifest = JSON.parse(readFileSync16(
|
|
32018
|
+
manifest = JSON.parse(readFileSync16(join13(distDir, ".vite", "manifest.json"), "utf8"));
|
|
31597
32019
|
} catch (err2) {
|
|
31598
32020
|
throw new Error(
|
|
31599
32021
|
`web app manifest not found under ${distDir} \u2014 build it first (npm run build in web/): ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
@@ -31607,9 +32029,9 @@ function loadWebAssets(distDir) {
|
|
|
31607
32029
|
css: (entryChunk.css ?? []).map((f) => `/${f}`)
|
|
31608
32030
|
};
|
|
31609
32031
|
const files = /* @__PURE__ */ new Map();
|
|
31610
|
-
const assetsDir =
|
|
32032
|
+
const assetsDir = join13(distDir, "assets");
|
|
31611
32033
|
for (const name of readdirSync3(assetsDir)) {
|
|
31612
|
-
const full =
|
|
32034
|
+
const full = join13(assetsDir, name);
|
|
31613
32035
|
if (statSync3(full).isFile()) files.set(`/assets/${name}`, readFileSync16(full));
|
|
31614
32036
|
}
|
|
31615
32037
|
const serve = (req, res) => {
|
|
@@ -33587,7 +34009,7 @@ __export(index_exports, {
|
|
|
33587
34009
|
runBot: () => runBot
|
|
33588
34010
|
});
|
|
33589
34011
|
import { createServer } from "node:http";
|
|
33590
|
-
import { join as
|
|
34012
|
+
import { join as join14 } from "node:path";
|
|
33591
34013
|
async function runBot() {
|
|
33592
34014
|
for (const v of ["SLACK_BOT_TOKEN", "SLACK_APP_TOKEN"]) {
|
|
33593
34015
|
if (!processSecrets.get(v)) {
|
|
@@ -34155,7 +34577,7 @@ var init_index = __esm({
|
|
|
34155
34577
|
init_secrets2();
|
|
34156
34578
|
DATA_DIR = installationPath(OPERATOR_ROOT, "data");
|
|
34157
34579
|
CONFIG_PATH2 = process.env.SWITCHBOARD_CONFIG ?? installationPath(OPERATOR_ROOT, "config/config.yaml");
|
|
34158
|
-
OVERRIDES_PATH = process.env.SWITCHBOARD_OVERRIDES ??
|
|
34580
|
+
OVERRIDES_PATH = process.env.SWITCHBOARD_OVERRIDES ?? join14(DATA_DIR, "overrides.json");
|
|
34159
34581
|
PROCESS_STARTED_AT = systemClock();
|
|
34160
34582
|
sampleProcessMetrics = startProcessMetrics();
|
|
34161
34583
|
INTERRUPTED_WRITE_BUDGET_MS = 1e4;
|
|
@@ -34199,8 +34621,8 @@ init_source();
|
|
|
34199
34621
|
init_invokedAsScript();
|
|
34200
34622
|
init_secrets2();
|
|
34201
34623
|
import { Console } from "node:console";
|
|
34202
|
-
import { existsSync as
|
|
34203
|
-
import { join as
|
|
34624
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
34625
|
+
import { join as join15 } from "node:path";
|
|
34204
34626
|
var CONFIG_PATH3 = process.env.SWITCHBOARD_CONFIG ?? installationPath(OPERATOR_ROOT, "config/config.yaml");
|
|
34205
34627
|
var DATA_DIR2 = installationPath(OPERATOR_ROOT, "data");
|
|
34206
34628
|
var CLI_CALLER = { kind: "cli", id: CLI_ACTOR.id, actor: CLI_ACTOR };
|
|
@@ -34387,7 +34809,7 @@ function missingBotConfig(configPath) {
|
|
|
34387
34809
|
);
|
|
34388
34810
|
}
|
|
34389
34811
|
async function loadBotConfig(configPath, overridesPath, opts = {}) {
|
|
34390
|
-
if (parseConfigLocation(configPath).kind === "file" && !(opts.exists ??
|
|
34812
|
+
if (parseConfigLocation(configPath).kind === "file" && !(opts.exists ?? existsSync14)(configPath))
|
|
34391
34813
|
throw missingBotConfig(configPath);
|
|
34392
34814
|
return openConfigStore(configPath, {
|
|
34393
34815
|
overridesPath,
|
|
@@ -34416,7 +34838,7 @@ function bindBotConfig(configPath, overridesPath, opts = {}) {
|
|
|
34416
34838
|
}
|
|
34417
34839
|
function cliCapabilities(configPath, env, secrets, opts = {}) {
|
|
34418
34840
|
if (parseConfigLocation(configPath).kind !== "file") return ALL_CAPABILITIES;
|
|
34419
|
-
if (!(opts.exists ??
|
|
34841
|
+
if (!(opts.exists ?? existsSync14)(configPath)) return ALL_CAPABILITIES;
|
|
34420
34842
|
try {
|
|
34421
34843
|
return capabilitiesFrom((opts.load ?? loadAppConfig)(configPath), env, secrets, { meatBinary: meatOnPath(env) });
|
|
34422
34844
|
} catch {
|
|
@@ -34425,7 +34847,7 @@ function cliCapabilities(configPath, env, secrets, opts = {}) {
|
|
|
34425
34847
|
}
|
|
34426
34848
|
function wireCli() {
|
|
34427
34849
|
const warn = (m) => console.error(m);
|
|
34428
|
-
const botConfig = bindBotConfig(CONFIG_PATH3,
|
|
34850
|
+
const botConfig = bindBotConfig(CONFIG_PATH3, join15(DATA_DIR2, "cli-overrides.json"));
|
|
34429
34851
|
let loaded;
|
|
34430
34852
|
const bot = () => loaded ??= botConfig().then((config) => ({
|
|
34431
34853
|
config,
|