@bongos/core 1.19.1032 → 1.19.1034
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/.bongos-core.json +322 -117
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/grade-audit/SKILL.md +1 -1
- package/.claude/skills/grade-recover/SKILL.md +2 -1
- package/.claude/skills/grader-health/SKILL.md +5 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/docs/adr/0089-modular-multi-model-adversarial-grader.md +15 -0
- package/docs/adr/0175-the-board-room-full-idea-ratification.md +3 -2
- package/docs/adr/0178-the-copy-desk-flag-and-queue.md +3 -3
- package/docs/adr/0188-patrol-runs-on-ci-not-the-server.md +2 -2
- package/docs/adr/0189-baseline-floors-and-the-freeze-detector.md +1 -1
- package/docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md +1 -1
- package/docs/adr/0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md +11 -3
- package/docs/adr/README.md +4 -4
- package/docs/api/openapi.json +1 -1
- package/docs/architecture.md +4 -3
- package/docs/canonical-permissions.md +2 -2
- package/docs/copy-inventory.md +34 -30
- package/docs/copy-registry.json +69 -33
- package/docs/file-map.md +4 -3
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/autobongos-windows-host.md +3 -1
- package/docs/recipes/local-dev.md +45 -3
- package/docs/recipes/packaging-the-core.md +3 -1
- package/docs/recipes/redteam-patrol.md +2 -2
- package/docs/recipes/upgrading-the-core.md +1 -1
- package/modules/autonomy/cadence.js +52 -0
- package/modules/autonomy/gauge.js +57 -9
- package/modules/government/protected-surfaces.json +1 -0
- package/modules/grading/grade-engine.js +267 -0
- package/modules/grading/grader-subagent.js +4 -3
- package/modules/grading/grader-workers/index.js +67 -11
- package/modules/grading/grader-workers/worker-verdict.js +32 -2
- package/modules/grading/grader.js +109 -88
- package/modules/hall-ui/public/deploy.js +35 -0
- package/modules/hall-ui/public/project-settings.css +7 -0
- package/modules/hall-ui/records/builders-roster.md +10 -0
- package/modules/hall-ui/records/catalog.md +10 -0
- package/modules/hall-ui/records/collaboration.md +14 -0
- package/modules/hall-ui/records/goal-dependency-forest.md +11 -0
- package/modules/hall-ui/records/oversight-family.md +19 -0
- package/modules/hall-ui/records/panel-family.md +19 -0
- package/modules/hall-ui/records/reading-rooms.md +12 -0
- package/modules/hall-ui/records/record-family.md +13 -0
- package/modules/hall-ui/records/reference-room.md +9 -0
- package/modules/hall-ui/records/roster-and-ledger.md +9 -0
- package/modules/hall-ui/records/sky.md +23 -0
- package/modules/lifecycle/dependency-advisory.js +2 -1
- package/modules/lifecycle/routes/tasks.js +19 -4
- package/modules/lifecycle/ship-preflight.js +91 -0
- package/modules/provisioning/control-plane-guard.js +14 -13
- package/modules/provisioning/core-upgrade.js +30 -17
- package/modules/provisioning/public-refusal.js +44 -0
- package/modules/provisioning/routes/disconnect.js +2 -1
- package/modules/provisioning/routes/env-manifest.js +5 -4
- package/modules/provisioning/routes/provisioning.js +16 -15
- package/modules/provisioning/routes/repo-private.js +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +7481 -0
- package/scripts/gds/address-park-sweep.sh +3 -1
- package/scripts/gds/autobongos-run.js +91 -3
- package/scripts/gds/backfill-cost.js +20 -9
- package/scripts/gds/baseline-staleness.js +1 -1
- package/scripts/gds/claim.js +2 -2
- package/scripts/gds/credential-presence.js +281 -0
- package/scripts/gds/db-backup-nightly.sh +9 -4
- package/scripts/gds/db-backup-offsite.sh +4 -2
- package/scripts/gds/deploy-main-audit-gate.sh +11 -4
- package/scripts/gds/dev-lib.js +212 -5
- package/scripts/gds/dev.js +102 -50
- package/scripts/gds/doctor.js +3 -2
- package/scripts/gds/grade-correlation-audit.js +280 -0
- package/scripts/gds/grade-replay.js +237 -0
- package/scripts/gds/hooks/pre-push.js +7 -3
- package/scripts/gds/main-audit.js +19 -10
- package/scripts/gds/memory-map.js +3 -1
- package/scripts/gds/onboarding-config.js +3 -2
- package/scripts/gds/package-core.js +72 -1
- package/scripts/gds/plain-cards.js +4 -0
- package/scripts/gds/preflight.js +12 -4
- package/scripts/gds/provision-core-upgrade.js +282 -80
- package/scripts/gds/provision-repo-auth.js +15 -0
- package/scripts/gds/provision.js +1 -1
- package/scripts/gds/publish-credential-check.js +3 -0
- package/scripts/gds/push-main.js +14 -3
- package/scripts/gds/push-path-brief.js +3 -2
- package/scripts/gds/redteam-patrol.js +4 -4
- package/scripts/gds/release-notes.js +271 -0
- package/scripts/gds/run-routine.js +18 -5
- package/scripts/gds/run-unit-tests.js +14 -3
- package/scripts/gds/seed-board-room-tasks.js +1 -1
- package/scripts/gds/session-cost.js +12 -3
- package/scripts/gds/ship-deploy-target.js +11 -6
- package/scripts/gds/ship-flow.js +5 -3
- package/scripts/gds/ship-grade-steps.js +33 -0
- package/scripts/gds/ship-land.js +241 -16
- package/scripts/gds/ship-merge.js +3 -2
- package/scripts/gds/ship-preflight-steps.js +86 -11
- package/scripts/gds/ship.js +1 -1
- package/scripts/gds/stray-unit-sweep.sh +3 -1
- package/scripts/gds/upgrade-migrate.js +67 -0
- package/scripts/gds/upgrade.js +9 -7
- package/scripts/hall-preview/README.md +20 -3
- package/scripts/hall-preview/live-identity.js +1 -1
- package/scripts/hall-preview/refresh-fixtures.js +71 -11
- package/scripts/hall-preview/scrub-identity.js +296 -0
- package/src/bongos/routes/security.js +3 -3
- package/src/build-info.js +4 -2
- package/src/module-api.js +1 -1
- package/tests/adr_0089_scope.mjs +70 -0
- package/tests/adr_0162_citation.mjs +184 -0
- package/tests/autobongos_cadence.mjs +74 -0
- package/tests/autobongos_loop.mjs +114 -0
- package/tests/autonomy_gauge.mjs +56 -0
- package/tests/consumer_layout_boot.mjs +363 -31
- package/tests/core_upgrade_door.mjs +40 -6
- package/tests/core_upgrade_runner.mjs +329 -5
- package/tests/credential_presence.mjs +153 -0
- package/tests/db_backup_nightly.mjs +21 -3
- package/tests/db_backup_offsite.mjs +81 -0
- package/tests/deploy_audit_gate.mjs +5 -2
- package/tests/deploy_page_projects.mjs +63 -2
- package/tests/dev_lib.mjs +2 -1
- package/tests/dev_windows.mjs +324 -0
- package/tests/env_knobs_neutral_prefix.mjs +203 -0
- package/tests/fixtures/grade-replay/recorded-panel-grades.json +27 -0
- package/tests/government_protected_surfaces.mjs +10 -0
- package/tests/grade_correlation_audit.mjs +159 -0
- package/tests/grade_engine.mjs +249 -0
- package/tests/grade_engine_replay.mjs +99 -0
- package/tests/grader_prompt_window_bound.mjs +132 -0
- package/tests/grader_unbacked_score.mjs +194 -0
- package/tests/hall_preview_scrub.mjs +214 -0
- package/tests/hall_records.mjs +85 -0
- package/tests/helpers.mjs +25 -0
- package/tests/instance_db_migrated_on_code_move.mjs +8 -1
- package/tests/integration_parked_ratchet.mjs +24 -1
- package/tests/plain_cards.mjs +1 -1
- package/tests/provisioning_public_refusal.mjs +195 -0
- package/tests/publish_status_branch.mjs +128 -0
- package/tests/redteam_patrol.mjs +1 -1
- package/tests/release_notes.mjs +327 -0
- package/tests/shell_stat_portability.mjs +49 -0
- package/tests/ship_land_conflict.mjs +181 -0
- package/tests/ship_land_red_pr.mjs +236 -0
- package/tests/ship_preflight.mjs +226 -3
- package/tests/start_env_guard.mjs +16 -0
- package/tests/sweep_app_account.mjs +15 -2
- package/tests/upgrade.mjs +20 -1
- package/tests/upgrade_migrate.mjs +195 -0
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
// `node scripts/gds/audit-deps.js --min high` as one more housekeeping
|
|
22
22
|
// step (idea 1000276's half — see .claude/skills/goal-review/SKILL.md).
|
|
23
23
|
//
|
|
24
|
-
// HARD CONSTRAINT, same as goal-advisory.js
|
|
24
|
+
// HARD CONSTRAINT, same as goal-advisory.js: advisory only (a finding addressed
|
|
25
|
+
// to a human never acts by itself — ADR 0158 §2). This
|
|
25
26
|
// must never refuse a POST, and `buildDependencyAdvisory` swallows its own
|
|
26
27
|
// failures for the same reason goal-advisory.js's does — the task row is
|
|
27
28
|
// ALREADY COMMITTED by the time this runs, so a throw here would turn a
|
|
@@ -53,6 +53,11 @@ const githubPush = require('../github-push');
|
|
|
53
53
|
const prAssign = require('../pr-assign');
|
|
54
54
|
const shipAssurance = require('../ship-assurance');
|
|
55
55
|
const RECONCILE_PROOF = Object.freeze({ kind: 'proof', reason: 'publish-status reconcile: land proven' });
|
|
56
|
+
// task 1003764: publish-status with no ?branch= and no PR titled for the task — the shape
|
|
57
|
+
// getPublishStatus gives a branch with no PR, plus how to ask about one. Never a 400.
|
|
58
|
+
const NO_PR_STATUS = Object.freeze({
|
|
59
|
+
pr_state: 'none', merged: false, merge_sha: null, deploy_status: 'none', pr_url: null, pr_number: null, pr_title: null, pr_body: null,
|
|
60
|
+
branch_hint: 'No pull request titled for this task is among the 100 most recent. Pass ?branch=<name> to ask about a specific branch.' });
|
|
56
61
|
const seams = api;
|
|
57
62
|
const { classifyTaskKind, VALID_KINDS, VALID_DISCIPLINES, VOTABLE_TASK_STATUSES, TASK_FIELD_BOUNDS } = require('../task-classifier');
|
|
58
63
|
const { isModuleKey, moduleKeys } = api.moduleScopeMap;
|
|
@@ -1250,15 +1255,25 @@ module.exports = function buildTasksRouter() {
|
|
|
1250
1255
|
// GET /tasks/:id/publish-status?branch=<branch> — poll the PR + prod-deploy
|
|
1251
1256
|
// state for a server-mediated publish so ship.js can confirm the land WITHOUT
|
|
1252
1257
|
// gh on the box. Ownership-gated like the POST. Derives state live from GitHub.
|
|
1258
|
+
// `branch` is optional (task 1003764): without it the server finds the task's own PR
|
|
1259
|
+
// (both publish paths title it "task <id>: …") and the answer names the `branch` it
|
|
1260
|
+
// used and its `branch_source`, so the check agents are sent to instead of git probes
|
|
1261
|
+
// never refuses a live publish for want of a branch name.
|
|
1253
1262
|
router.get('/tasks/:id/publish-status', auth.requireBuilder, async (req, res) => {
|
|
1254
1263
|
const id = parseId(req, res, { code: 'bad_task_id' });
|
|
1255
1264
|
if (id === null) return;
|
|
1256
1265
|
if (!(await gateTaskOwnership(req, res, id))) return;
|
|
1257
1266
|
if (!githubPush.isConfigured()) return res.fail('push_unconfigured', { status: 503, message: PUSH_UNCONFIGURED_MSG });
|
|
1258
|
-
|
|
1259
|
-
|
|
1267
|
+
// A fully-qualified ref names the same branch, so it is accepted rather than refused.
|
|
1268
|
+
const asked = typeof req.query.branch === 'string' ? req.query.branch.replace(/^refs\/heads\//, '') : req.query.branch;
|
|
1269
|
+
if (asked != null && asked !== '' && !githubPush.isSafeBranch(asked)) {
|
|
1270
|
+
return res.fail('bad_branch', { status: 400, message: 'branch must be a plain branch name such as task-123, or left out so the server finds the task\'s own PR.' });
|
|
1271
|
+
}
|
|
1260
1272
|
try {
|
|
1261
|
-
const
|
|
1273
|
+
const found = asked ? null : await githubPush.findTaskPullRequest({ taskId: id });
|
|
1274
|
+
const branch = asked || (found && found.branch) || null;
|
|
1275
|
+
const branch_source = asked ? 'query' : (branch ? 'task_pr' : null);
|
|
1276
|
+
const status = branch ? await githubPush.getPublishStatus({ branch }) : NO_PR_STATUS;
|
|
1262
1277
|
// task 1049 (B — server-side reconcile-on-read): when GitHub shows the PR
|
|
1263
1278
|
// MERGED and deploy-prod SUCCEEDED, finish the task right here so a
|
|
1264
1279
|
// confirmed-but-unshipped task never strands waiting on a human to re-poll
|
|
@@ -1318,7 +1333,7 @@ module.exports = function buildTasksRouter() {
|
|
|
1318
1333
|
log.error('[gds] publish-status merge-driver (non-blocking):', e && e.message);
|
|
1319
1334
|
}
|
|
1320
1335
|
}
|
|
1321
|
-
return res.json({ ok: true, ...status, reconciled, reconcile_blocked, merge_driver });
|
|
1336
|
+
return res.json({ ok: true, ...status, branch, branch_source, reconciled, reconcile_blocked, merge_driver });
|
|
1322
1337
|
} catch (e) {
|
|
1323
1338
|
if (e.code === 'NO_REPO_INFO') return res.fail('no_repo_info', 500);
|
|
1324
1339
|
if (e.code === 'APP_NOT_INSTALLED') return res.fail('push_unconfigured', { status: 503, message: appNotInstalledMessage(e) });
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
// touches[] was empty, leaving empty-touches tasks unprotected — with this
|
|
16
16
|
// plain clean-tree check. Strictly stronger; untracked / .gitignored files
|
|
17
17
|
// are ignored so autonomous sessions with scratch files aren't refused.)
|
|
18
|
+
// Ignored, but no longer UNMENTIONED: untrackedLeftBehind +
|
|
19
|
+
// untrackedWarningLines (task 1001436) name the untracked files a ship
|
|
20
|
+
// with committed work is about to leave behind, as a warning only.
|
|
18
21
|
//
|
|
19
22
|
// 2. cleanupMacFinderDupRefs — find + delete `.git/refs/.../FOO 2` files
|
|
20
23
|
// that macOS Finder / iCloud creates when it duplicates a regular file
|
|
@@ -96,6 +99,91 @@ function classifyEmptyShip(porcelainOut, { allowEmpty = false } = {}) {
|
|
|
96
99
|
return { refuse: true, reason: 'nothing-to-ship', uncommitted: [] };
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
// ---- The RESIDUAL phantom ship (task 1001436) --------------------------------
|
|
103
|
+
//
|
|
104
|
+
// classifyEmptyShip closes the case where NOTHING is committed and the
|
|
105
|
+
// deliverable sits uncommitted. It cannot see the weaker case beside it: the
|
|
106
|
+
// builder commits real work, so the branch is ahead of main and the ship goes
|
|
107
|
+
// ahead, but one more NEW file was never `git add`ed. The merge carries only
|
|
108
|
+
// committed history, so that file silently does not ship — the task reads
|
|
109
|
+
// "shipped" with part of its artifact missing, and nothing said so.
|
|
110
|
+
//
|
|
111
|
+
// The answer is a loud WARNING, never a refusal. trackedDirty ignores untracked
|
|
112
|
+
// files on purpose: autonomous sessions leave scratch in the tree, and refusing
|
|
113
|
+
// on it would false-refuse real ships. So this only names the files — before the
|
|
114
|
+
// grade and the merge, while stopping is still free.
|
|
115
|
+
//
|
|
116
|
+
// SCOPE. The filing suggested narrowing the list to touches[] or to docs/ src/
|
|
117
|
+
// scripts/. Neither is used, because each hides exactly the file this exists to
|
|
118
|
+
// catch: touches[] is advisory and often empty, and a forgotten file is by
|
|
119
|
+
// definition one nobody thought to declare; a directory allowlist misses tests/,
|
|
120
|
+
// modules/ and migrations/, where a forgotten new file most often lives (a new
|
|
121
|
+
// test beside a committed fix). The one SYSTEMATIC source of noise is the ship's
|
|
122
|
+
// own input — the builder-ship skill writes notes.md + summary.txt into the tree
|
|
123
|
+
// and passes them as --notes-file / --summary-file — so those are excluded
|
|
124
|
+
// exactly, by path, via `inputs`. Surveyed before choosing (2026-09-25, 16 live
|
|
125
|
+
// worktrees): 12 untracked entries, none of them root-level scratch, and 5 under
|
|
126
|
+
// tests/ or modules/ — where a docs/ src/ scripts/ allowlist would have hidden them.
|
|
127
|
+
|
|
128
|
+
// Pure: the untracked ('??') paths in raw `git status --porcelain` output, in
|
|
129
|
+
// output order. .gitignored files never appear without --ignored, so this is
|
|
130
|
+
// exactly "untracked and not ignored". Quoted paths are unquoted the same way
|
|
131
|
+
// parsePorcelain does it.
|
|
132
|
+
function untrackedPaths(porcelainOut) {
|
|
133
|
+
if (!porcelainOut) return [];
|
|
134
|
+
const files = [];
|
|
135
|
+
for (const raw of porcelainOut.split('\n')) {
|
|
136
|
+
if (raw.slice(0, 2) !== '??') continue;
|
|
137
|
+
const file = raw.slice(3).replace(/^"|"$/g, '');
|
|
138
|
+
if (file) files.push(file);
|
|
139
|
+
}
|
|
140
|
+
return files;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// One spelling for a repo-relative path, so an input given as `./notes.md` or
|
|
144
|
+
// with Windows separators still matches porcelain's `notes.md`.
|
|
145
|
+
function normalizeRelPath(p) {
|
|
146
|
+
return String(p || '').replace(/\\/g, '/').replace(/^(\.\/)+/, '');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Pure: which untracked files will be LEFT BEHIND by this ship.
|
|
150
|
+
// committedFileCount — files in the branch's committed diff vs the grader
|
|
151
|
+
// baseline. Zero returns [] on purpose: with nothing committed,
|
|
152
|
+
// classifyEmptyShip already REFUSES and lists these same files, and a second
|
|
153
|
+
// message about them would only compete with the refusal.
|
|
154
|
+
// inputs — repo-relative paths the ship itself was handed (notes, summary,
|
|
155
|
+
// response, visual). They are inputs to the ship, not deliverables.
|
|
156
|
+
function untrackedLeftBehind(porcelainOut, { committedFileCount = 0, inputs = [] } = {}) {
|
|
157
|
+
if (!(Number(committedFileCount) > 0)) return [];
|
|
158
|
+
const skip = new Set((inputs || []).map(normalizeRelPath).filter(Boolean));
|
|
159
|
+
return untrackedPaths(porcelainOut).filter((f) => !skip.has(normalizeRelPath(f)));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Pure: the warning block for untrackedLeftBehind's result; [] when there is
|
|
163
|
+
// nothing to say. `cap` bounds the listing so a tree full of scratch cannot bury
|
|
164
|
+
// the rest of the ship output. The wording holds on all three preflight callers
|
|
165
|
+
// (first ship, --regrade, the confirmed-task resume): each runs this before it
|
|
166
|
+
// resolves, grades or merges anything.
|
|
167
|
+
function untrackedWarningLines(files, { taskId = null, cap = 15 } = {}) {
|
|
168
|
+
if (!files || files.length === 0) return [];
|
|
169
|
+
const n = files.length;
|
|
170
|
+
const task = taskId != null && taskId !== '' ? ` with task ${taskId}` : '';
|
|
171
|
+
const lines = [
|
|
172
|
+
'',
|
|
173
|
+
`⚠ Untracked-file warning: ${n} untracked file(s) in this worktree will NOT ship${task}:`,
|
|
174
|
+
];
|
|
175
|
+
for (const f of files.slice(0, cap)) lines.push(` - ${f}`);
|
|
176
|
+
if (n > cap) lines.push(` … and ${n - cap} more`);
|
|
177
|
+
lines.push(
|
|
178
|
+
' The merge carries committed work only, so these stay behind.',
|
|
179
|
+
' If one of them belongs to this task: stop now (Ctrl-C — nothing has been resolved at this step),',
|
|
180
|
+
' commit it, and re-run. If they are scratch, ignore this.',
|
|
181
|
+
' Warning only — not blocking; the ship proceeds.',
|
|
182
|
+
'',
|
|
183
|
+
);
|
|
184
|
+
return lines;
|
|
185
|
+
}
|
|
186
|
+
|
|
99
187
|
function gitDirFor(cwd) {
|
|
100
188
|
// Resolve the *actual* refs directory for a given worktree. In a linked
|
|
101
189
|
// worktree, `.git` is a file containing `gitdir: <path>` that points at
|
|
@@ -218,6 +306,9 @@ module.exports = {
|
|
|
218
306
|
parsePorcelain,
|
|
219
307
|
trackedDirty,
|
|
220
308
|
classifyEmptyShip,
|
|
309
|
+
untrackedPaths,
|
|
310
|
+
untrackedLeftBehind,
|
|
311
|
+
untrackedWarningLines,
|
|
221
312
|
cleanupMacFinderDupRefs,
|
|
222
313
|
gitDirFor,
|
|
223
314
|
findMainWorktree,
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
// that file sits on the oversized-file ratchet; a guard whose whole value is being
|
|
25
25
|
// read before it is edited should not be the thing that reds the build either.
|
|
26
26
|
|
|
27
|
+
const { PublicRefusal } = require('./public-refusal');
|
|
28
|
+
|
|
27
29
|
// The only acts that cannot destroy the box they run on.
|
|
28
30
|
//
|
|
29
31
|
// core-upgrade — moves the pin, health-gated, auto-rollback on failure. This is
|
|
@@ -86,25 +88,24 @@ function intentAllowedForShape(action, hostingShape) {
|
|
|
86
88
|
* @param {{query: Function}} db
|
|
87
89
|
* @param {number|string} instanceId
|
|
88
90
|
* @param {string} action an INTENT_ACTIONS member
|
|
89
|
-
* @throws {
|
|
91
|
+
* @throws {PublicRefusal} code 'control_plane_protected', status 409
|
|
90
92
|
*/
|
|
91
93
|
async function assertIntentAllowed(db, instanceId, action) {
|
|
92
94
|
const { rows } = await db.query(
|
|
93
95
|
'SELECT hosting_shape FROM provisioning_instances WHERE id = $1', [instanceId]);
|
|
94
96
|
const shape = rows[0] && rows[0].hosting_shape;
|
|
95
97
|
if (!shape || intentAllowedForShape(action, shape)) return;
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
// a route forgot
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
throw err;
|
|
98
|
+
// A PublicRefusal, so every route answers it as a 409 in the owner's words (task
|
|
99
|
+
// 1004126). This used to rest on "every action that can reach this throw has its own
|
|
100
|
+
// earlier refusal", and that did not hold: teardown, force-teardown and disconnect reach
|
|
101
|
+
// it with none, and the owner read "internal error". Each route still logs the stack, so
|
|
102
|
+
// a route that forgot its own check stays as visible to us as it was.
|
|
103
|
+
throw new PublicRefusal({
|
|
104
|
+
code: 'control_plane_protected',
|
|
105
|
+
status: 409,
|
|
106
|
+
publicMessage: `That action cannot be aimed at the machine the platform itself runs on. Only ${CONTROL_PLANE_ACTIONS.join(' and ')} are allowed there.`,
|
|
107
|
+
message: `'${action}' cannot be aimed at the control plane — only ${CONTROL_PLANE_ACTIONS.join(' and ')} may run against the box the platform itself runs on`,
|
|
108
|
+
});
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
module.exports = { CONTROL_PLANE_ACTIONS, intentAllowedForShape, assertIntentAllowed };
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
// AT the 1500-line fitness ratchet, and at that ceiling a new function costs more
|
|
30
30
|
// than the build will give back.
|
|
31
31
|
|
|
32
|
+
const { failFrom } = require('./public-refusal');
|
|
32
33
|
|
|
33
34
|
// Exactly the states in which there is a running instance whose pin can move.
|
|
34
35
|
// Narrower than RESTART_FROM on purpose: an instance mid-standup has no settled pin
|
|
@@ -209,18 +210,10 @@ function makeCoreUpgradeHandler({ provisioning, pool, parseId, validateOrRespond
|
|
|
209
210
|
});
|
|
210
211
|
} catch (err) {
|
|
211
212
|
log.error('[provisioning] POST /provisioning/instances/:id/core-upgrade', err);
|
|
212
|
-
// A REFUSAL IS NOT AN OUTAGE
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
// in the repo honoured `publicMessage`; it was assigned once and read never
|
|
217
|
-
// (task 1004125). Only an error that has already declared itself public is
|
|
218
|
-
// forwarded — anything else still gets the opaque 500, because an unexpected
|
|
219
|
-
// throw may carry anything.
|
|
220
|
-
if (err && err.publicMessage && err.code && err.status) {
|
|
221
|
-
return res.fail(err.code, { status: err.status, message: err.publicMessage });
|
|
222
|
-
}
|
|
223
|
-
return res.fail('core_upgrade_failed', { status: 500, message: 'internal error' });
|
|
213
|
+
// A REFUSAL IS NOT AN OUTAGE (task 1004125): a guard refusal reaches the owner as
|
|
214
|
+
// its own sentence, and an unexpected throw, which may carry anything, stays the
|
|
215
|
+
// opaque 500. The one definition of "public" is public-refusal.js (task 1004126).
|
|
216
|
+
return failFrom(res, err, 'core_upgrade_failed');
|
|
224
217
|
}
|
|
225
218
|
};
|
|
226
219
|
}
|
|
@@ -256,6 +249,23 @@ function lastMoveSuperseded(target, served, recommended) {
|
|
|
256
249
|
return !!(recommended && String(recommended) !== String(target));
|
|
257
250
|
}
|
|
258
251
|
|
|
252
|
+
/**
|
|
253
|
+
* The note a DONE move carries when the upgrade landed but its pin never reached the
|
|
254
|
+
* project's GitHub repo (task 1004065) — `{ code, text }`, or null. PURE.
|
|
255
|
+
*
|
|
256
|
+
* The runner writes it into the intent's last_error as `repo_not_updated:<code>: <text>`
|
|
257
|
+
* (scripts/gds/provision-core-upgrade.js repoNote; re-stated here because a module may
|
|
258
|
+
* not require a script, and round-tripped by tests/core_upgrade_runner.mjs). ONLY that
|
|
259
|
+
* shape is read: any other text on a done row is not turned into a warning, because
|
|
260
|
+
* inventing one would tell an owner their repo is stale on no evidence.
|
|
261
|
+
*/
|
|
262
|
+
const REPO_NOTE_RE = /^repo_not_updated:([a-z_]+): ([\s\S]+)$/;
|
|
263
|
+
function parseRepoNote(text) {
|
|
264
|
+
const m = REPO_NOTE_RE.exec(String(text || ''));
|
|
265
|
+
// Bounded because it reaches a DOM, like the error beside it.
|
|
266
|
+
return m ? { code: m[1], text: m[2].slice(0, 600) } : null;
|
|
267
|
+
}
|
|
268
|
+
|
|
259
269
|
/**
|
|
260
270
|
* The cached dry-run verdict, IF it is about the move now on offer. PURE (task 1004173).
|
|
261
271
|
*
|
|
@@ -344,8 +354,11 @@ function previewFromSnapshot(inst, snap, openIntent, lastMove) {
|
|
|
344
354
|
// versions, and this is a version question wearing a different hat.
|
|
345
355
|
superseded: lastMoveSuperseded(lastMove.target_version, served, recommended),
|
|
346
356
|
// Truncated because it reaches a DOM and upgrade.js's refusals can carry a whole
|
|
347
|
-
// `git status` (the dirty-tree one lists every file).
|
|
348
|
-
|
|
357
|
+
// `git status` (the dirty-tree one lists every file). Never set on a DONE move:
|
|
358
|
+
// a done row's last_error is the note below, and a move that worked is not an error.
|
|
359
|
+
error: lastMove.state !== 'done' && lastMove.last_error ? String(lastMove.last_error).slice(0, 600) : null,
|
|
360
|
+
// It LANDED, but GitHub did not get the new pin (task 1004065) — parseRepoNote.
|
|
361
|
+
warning: lastMove.state === 'done' ? parseRepoNote(lastMove.last_error) : null,
|
|
349
362
|
} : null,
|
|
350
363
|
};
|
|
351
364
|
}
|
|
@@ -379,7 +392,7 @@ function makeCoreUpgradePreviewHandler({ provisioning, pool, parseId, log }) {
|
|
|
379
392
|
return res.json({ ok: true, preview: await loadPreview(pool, provisioning, inst) });
|
|
380
393
|
} catch (err) {
|
|
381
394
|
log.error('[provisioning] GET /provisioning/instances/:id/core-upgrade', err);
|
|
382
|
-
return res
|
|
395
|
+
return failFrom(res, err, 'core_upgrade_preview_failed');
|
|
383
396
|
}
|
|
384
397
|
};
|
|
385
398
|
}
|
|
@@ -410,10 +423,10 @@ function makeControlPlaneHandler({ provisioning, pool, log }) {
|
|
|
410
423
|
return res.json({ ok: true, preview: await loadPreview(pool, provisioning, rows[0]) });
|
|
411
424
|
} catch (err) {
|
|
412
425
|
log.error('[provisioning] GET /provisioning/control-plane', err);
|
|
413
|
-
return res
|
|
426
|
+
return failFrom(res, err, 'control_plane_read_failed');
|
|
414
427
|
}
|
|
415
428
|
};
|
|
416
429
|
}
|
|
417
430
|
|
|
418
|
-
module.exports = { CORE_UPGRADE_FROM, lastCoreMove, lastMoveSuperseded, preflightForOffer, UPGRADE_SHAPES, VERSION_RE, isCoreVersion, coreUpgradeFault, enqueueCoreUpgrade, makeCoreUpgradeHandler, previewFromSnapshot, makeCoreUpgradePreviewHandler, makeControlPlaneHandler };
|
|
431
|
+
module.exports = { CORE_UPGRADE_FROM, lastCoreMove, lastMoveSuperseded, parseRepoNote, preflightForOffer, UPGRADE_SHAPES, VERSION_RE, isCoreVersion, coreUpgradeFault, enqueueCoreUpgrade, makeCoreUpgradeHandler, previewFromSnapshot, makeCoreUpgradePreviewHandler, makeControlPlaneHandler };
|
|
419
432
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// modules/provisioning/public-refusal.js — the one definition of an error written for the
|
|
3
|
+
// person who asked, and the one way a catch in this module answers (task 1004126).
|
|
4
|
+
//
|
|
5
|
+
// WHY. A refusal such as the control-plane guard's carries a code, a 4xx and a sentence
|
|
6
|
+
// written for the owner. Every catch in this module used to answer a blanket 500
|
|
7
|
+
// "internal error" instead, so the owner never read that sentence; it went only to the
|
|
8
|
+
// journal. Forwarding every thrown error would be worse, because a Postgres error names
|
|
9
|
+
// tables. So an error has to DECLARE itself public by being a PublicRefusal, and
|
|
10
|
+
// everything else still gets today's opaque 500.
|
|
11
|
+
//
|
|
12
|
+
// A CLASS, NOT A DUCK TYPE. Task 1004125 fixed one catch by testing
|
|
13
|
+
// `code && status && publicMessage`, which anything can carry by accident. The class is
|
|
14
|
+
// the single definition of "public"; failFrom is the single place that honours it.
|
|
15
|
+
|
|
16
|
+
// A refusal a route may repeat to the person verbatim. `publicMessage` is theirs;
|
|
17
|
+
// `message` stays in the journal (it defaults to the public sentence). A refusal is never
|
|
18
|
+
// an outage, so the status must be a 4xx.
|
|
19
|
+
class PublicRefusal extends Error {
|
|
20
|
+
constructor({ code, status, publicMessage, message } = {}) {
|
|
21
|
+
if (!(Number.isInteger(status) && status >= 400 && status < 500)) {
|
|
22
|
+
throw new TypeError(`a PublicRefusal is a 4xx, got ${status}: an outage is not a refusal`);
|
|
23
|
+
}
|
|
24
|
+
if (!code || !publicMessage) throw new TypeError('a PublicRefusal needs a code and a publicMessage');
|
|
25
|
+
super(message || publicMessage);
|
|
26
|
+
this.name = 'PublicRefusal';
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.publicMessage = publicMessage;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isPublicRefusal(err) {
|
|
34
|
+
return err instanceof PublicRefusal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// failFrom — answer a caught error: a PublicRefusal as its own code, status and public
|
|
38
|
+
// sentence; anything else as the opaque 500 under the route's own fallbackCode.
|
|
39
|
+
function failFrom(res, err, fallbackCode) {
|
|
40
|
+
if (isPublicRefusal(err)) return res.fail(err.code, { status: err.status, message: err.publicMessage });
|
|
41
|
+
return res.fail(fallbackCode, { status: 500, message: 'internal error' });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { PublicRefusal, isPublicRefusal, failFrom };
|
|
@@ -11,6 +11,7 @@ const api = require('../../../src/module-api');
|
|
|
11
11
|
const { pool, validateOrRespond, parseId } = api;
|
|
12
12
|
const provisioning = require('../provisioning');
|
|
13
13
|
const disconnect = require('../disconnect');
|
|
14
|
+
const { failFrom } = require('../public-refusal');
|
|
14
15
|
|
|
15
16
|
const log = api.logger('provisioning');
|
|
16
17
|
|
|
@@ -55,7 +56,7 @@ module.exports = function buildDisconnectRouter() {
|
|
|
55
56
|
: 'A disconnect is already queued for this project.' });
|
|
56
57
|
} catch (err) {
|
|
57
58
|
log.error('[provisioning] POST /provisioning/instances/:id/disconnect', err);
|
|
58
|
-
res
|
|
59
|
+
failFrom(res, err, 'disconnect_failed');
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
62
|
|
|
@@ -24,6 +24,7 @@ const api = require('../../../src/module-api');
|
|
|
24
24
|
const { pool, validateOrRespond, parseId } = api;
|
|
25
25
|
const provisioning = require('../provisioning');
|
|
26
26
|
const envManifest = require('../env-manifest');
|
|
27
|
+
const { failFrom } = require('../public-refusal');
|
|
27
28
|
|
|
28
29
|
const log = api.logger('provisioning');
|
|
29
30
|
|
|
@@ -119,7 +120,7 @@ module.exports = function buildEnvManifestRouter() {
|
|
|
119
120
|
});
|
|
120
121
|
} catch (err) {
|
|
121
122
|
log.error('[provisioning] GET /provisioning/instances/:id/env-manifest', err);
|
|
122
|
-
res
|
|
123
|
+
failFrom(res, err, 'env_manifest_failed');
|
|
123
124
|
}
|
|
124
125
|
});
|
|
125
126
|
|
|
@@ -147,7 +148,7 @@ module.exports = function buildEnvManifestRouter() {
|
|
|
147
148
|
res.status(created ? 201 : 200).json({ ok: true, created, env_var: row, values_held: false });
|
|
148
149
|
} catch (err) {
|
|
149
150
|
log.error('[provisioning] POST /provisioning/instances/:id/env-manifest', err);
|
|
150
|
-
res
|
|
151
|
+
failFrom(res, err, 'env_manifest_failed');
|
|
151
152
|
}
|
|
152
153
|
});
|
|
153
154
|
|
|
@@ -174,7 +175,7 @@ module.exports = function buildEnvManifestRouter() {
|
|
|
174
175
|
res.json({ ok: true, env_var: row, values_held: false });
|
|
175
176
|
} catch (err) {
|
|
176
177
|
log.error('[provisioning] PATCH /provisioning/instances/:id/env-manifest/:varId', err);
|
|
177
|
-
res
|
|
178
|
+
failFrom(res, err, 'env_manifest_failed');
|
|
178
179
|
}
|
|
179
180
|
});
|
|
180
181
|
|
|
@@ -193,7 +194,7 @@ module.exports = function buildEnvManifestRouter() {
|
|
|
193
194
|
message: 'Declaration removed. The variable\'s value, wherever you keep it, is untouched — this platform never had it.' });
|
|
194
195
|
} catch (err) {
|
|
195
196
|
log.error('[provisioning] DELETE /provisioning/instances/:id/env-manifest/:varId', err);
|
|
196
|
-
res
|
|
197
|
+
failFrom(res, err, 'env_manifest_failed');
|
|
197
198
|
}
|
|
198
199
|
});
|
|
199
200
|
|
|
@@ -62,6 +62,7 @@ const capacity = require('../capacity'); // the box's slot ceiling (ADR 0285,
|
|
|
62
62
|
const { callbackPage } = require('./callback-page');
|
|
63
63
|
// the LOUD body validators (task 1003504) — lifted out when this file hit the size ratchet
|
|
64
64
|
const { badProjectDetail, badModuleSelection, badProjectType } = require('./body-validators');
|
|
65
|
+
const { failFrom } = require('../public-refusal'); // every catch: a declared refusal reaches the owner, anything else stays opaque (task 1004126)
|
|
65
66
|
// the kernel ports this module provides (moved out at the size ratchet, task 1003578)
|
|
66
67
|
const { registerProvisioningSeams } = require('../seams');
|
|
67
68
|
// task 1003208: structured logging (pino via the doorway) — was console.*.
|
|
@@ -211,7 +212,7 @@ module.exports = function provisioningRoutes() {
|
|
|
211
212
|
res.json({ instances: rows.map(fleetInstance), capacity: await capacity.fleetCapacity(pool).catch(() => null) });
|
|
212
213
|
} catch (err) {
|
|
213
214
|
log.error('[provisioning] GET /provisioning/fleet', err);
|
|
214
|
-
res
|
|
215
|
+
failFrom(res, err, 'fleet_failed');
|
|
215
216
|
}
|
|
216
217
|
});
|
|
217
218
|
|
|
@@ -226,7 +227,7 @@ module.exports = function provisioningRoutes() {
|
|
|
226
227
|
res.json(out);
|
|
227
228
|
} catch (err) {
|
|
228
229
|
log.error('[provisioning] GET /provisioning/fleet/cost-ledger', err);
|
|
229
|
-
res
|
|
230
|
+
failFrom(res, err, 'cost_ledger_failed');
|
|
230
231
|
}
|
|
231
232
|
});
|
|
232
233
|
|
|
@@ -279,7 +280,7 @@ module.exports = function provisioningRoutes() {
|
|
|
279
280
|
message: r.created ? 'The instance will be torn down shortly.' : 'A teardown is already queued for that instance.' });
|
|
280
281
|
} catch (err) {
|
|
281
282
|
log.error('[provisioning] POST /provisioning/instances/:id/force-teardown', err);
|
|
282
|
-
res
|
|
283
|
+
failFrom(res, err, 'teardown_failed');
|
|
283
284
|
}
|
|
284
285
|
});
|
|
285
286
|
|
|
@@ -518,7 +519,7 @@ module.exports = function provisioningRoutes() {
|
|
|
518
519
|
// Another instance took the address between the check above and the INSERT (task 1002901).
|
|
519
520
|
if (provisioning.isDomainTaken(err)) return res.fail('domain_taken', { status: 409, message: 'That address is already held by another instance.' });
|
|
520
521
|
log.error('[provisioning] POST /provisioning/instances', err);
|
|
521
|
-
res
|
|
522
|
+
failFrom(res, err, 'request_failed');
|
|
522
523
|
}
|
|
523
524
|
});
|
|
524
525
|
|
|
@@ -566,7 +567,7 @@ module.exports = function provisioningRoutes() {
|
|
|
566
567
|
res.json({ slug, available: reason === null, reason, address: address || null });
|
|
567
568
|
} catch (err) {
|
|
568
569
|
log.error('[provisioning] GET /provisioning/slug-available', err);
|
|
569
|
-
res
|
|
570
|
+
failFrom(res, err, 'check_failed');
|
|
570
571
|
}
|
|
571
572
|
});
|
|
572
573
|
|
|
@@ -586,7 +587,7 @@ module.exports = function provisioningRoutes() {
|
|
|
586
587
|
res.json({ instances });
|
|
587
588
|
} catch (err) {
|
|
588
589
|
log.error('[provisioning] GET /provisioning/instances', err);
|
|
589
|
-
res
|
|
590
|
+
failFrom(res, err, 'list_failed');
|
|
590
591
|
}
|
|
591
592
|
});
|
|
592
593
|
|
|
@@ -630,7 +631,7 @@ module.exports = function provisioningRoutes() {
|
|
|
630
631
|
});
|
|
631
632
|
} catch (err) {
|
|
632
633
|
log.error('[provisioning] GET /provisioning/instances/:id', err);
|
|
633
|
-
res
|
|
634
|
+
failFrom(res, err, 'read_failed');
|
|
634
635
|
}
|
|
635
636
|
});
|
|
636
637
|
|
|
@@ -750,7 +751,7 @@ module.exports = function provisioningRoutes() {
|
|
|
750
751
|
});
|
|
751
752
|
} catch (err) {
|
|
752
753
|
log.error('[provisioning] PATCH /provisioning/instances/:id/detail', err);
|
|
753
|
-
res
|
|
754
|
+
failFrom(res, err, 'detail_update_failed');
|
|
754
755
|
}
|
|
755
756
|
});
|
|
756
757
|
|
|
@@ -805,7 +806,7 @@ module.exports = function provisioningRoutes() {
|
|
|
805
806
|
res.json({ suggested, seeking: invites.seeking, reason: invites.reason });
|
|
806
807
|
} catch (err) {
|
|
807
808
|
log.error('[provisioning] GET /provisioning/instances/:id/invite-suggestions', err);
|
|
808
|
-
res
|
|
809
|
+
failFrom(res, err, 'invite_suggestions_failed');
|
|
809
810
|
}
|
|
810
811
|
});
|
|
811
812
|
|
|
@@ -830,7 +831,7 @@ module.exports = function provisioningRoutes() {
|
|
|
830
831
|
options: { ...provisioning.SETTINGS_VOCAB, ...provisioning.PLANET_VOCAB } });
|
|
831
832
|
} catch (err) {
|
|
832
833
|
log.error('[provisioning] GET /provisioning/instances/:id/settings', err);
|
|
833
|
-
res
|
|
834
|
+
failFrom(res, err, 'settings_read_failed');
|
|
834
835
|
}
|
|
835
836
|
});
|
|
836
837
|
|
|
@@ -983,7 +984,7 @@ module.exports = function provisioningRoutes() {
|
|
|
983
984
|
res.json({ ok: true, saved: provisioning.effectiveSettings(updated), planet: provisioning.planetChoice(updated), push });
|
|
984
985
|
} catch (err) {
|
|
985
986
|
log.error('[provisioning] PATCH /provisioning/instances/:id/settings', err);
|
|
986
|
-
res
|
|
987
|
+
failFrom(res, err, 'settings_update_failed');
|
|
987
988
|
}
|
|
988
989
|
});
|
|
989
990
|
|
|
@@ -1020,7 +1021,7 @@ module.exports = function provisioningRoutes() {
|
|
|
1020
1021
|
message: r.created ? 'Your instance will be torn down shortly.' : 'A teardown is already queued for that instance.' });
|
|
1021
1022
|
} catch (err) {
|
|
1022
1023
|
log.error('[provisioning] POST /provisioning/instances/:id/teardown', err);
|
|
1023
|
-
res
|
|
1024
|
+
failFrom(res, err, 'teardown_failed');
|
|
1024
1025
|
}
|
|
1025
1026
|
});
|
|
1026
1027
|
|
|
@@ -1079,7 +1080,7 @@ module.exports = function provisioningRoutes() {
|
|
|
1079
1080
|
: 'A restart is already under way for this project.' });
|
|
1080
1081
|
} catch (err) {
|
|
1081
1082
|
log.error('[provisioning] POST /provisioning/instances/:id/restart', err);
|
|
1082
|
-
res
|
|
1083
|
+
failFrom(res, err, 'restart_failed');
|
|
1083
1084
|
}
|
|
1084
1085
|
});
|
|
1085
1086
|
|
|
@@ -1224,7 +1225,7 @@ module.exports = function provisioningRoutes() {
|
|
|
1224
1225
|
// Same race on the attach side: the index refuses the UPDATE, so nothing is recorded and nothing is enqueued.
|
|
1225
1226
|
if (provisioning.isDomainTaken(err)) return res.fail('domain_taken', { status: 409, message: 'That address is already in use by another instance.' });
|
|
1226
1227
|
log.error('[provisioning] POST /provisioning/instances/:id/domain', err);
|
|
1227
|
-
res
|
|
1228
|
+
failFrom(res, err, 'domain_attach_failed');
|
|
1228
1229
|
}
|
|
1229
1230
|
});
|
|
1230
1231
|
|
|
@@ -1282,7 +1283,7 @@ module.exports = function provisioningRoutes() {
|
|
|
1282
1283
|
});
|
|
1283
1284
|
} catch (err) {
|
|
1284
1285
|
log.error('[provisioning] POST /provisioning/instances/:id/github-app', err);
|
|
1285
|
-
res
|
|
1286
|
+
failFrom(res, err, 'github_app_manifest_failed');
|
|
1286
1287
|
}
|
|
1287
1288
|
});
|
|
1288
1289
|
|
|
@@ -16,6 +16,7 @@ const api = require('../../../src/module-api');
|
|
|
16
16
|
const { pool, validateOrRespond, parseId } = api;
|
|
17
17
|
const provisioning = require('../provisioning');
|
|
18
18
|
const repoPrivate = require('../repo-private');
|
|
19
|
+
const { failFrom } = require('../public-refusal');
|
|
19
20
|
|
|
20
21
|
const log = api.logger('provisioning');
|
|
21
22
|
|
|
@@ -74,7 +75,7 @@ module.exports = function buildRepoPrivateRouter() {
|
|
|
74
75
|
: 'Making the repository private is already under way for this project.' });
|
|
75
76
|
} catch (err) {
|
|
76
77
|
log.error('[provisioning] POST /provisioning/instances/:id/repo-private', err);
|
|
77
|
-
res
|
|
78
|
+
failFrom(res, err, 'repo_private_failed');
|
|
78
79
|
}
|
|
79
80
|
});
|
|
80
81
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1034",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.1034",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1034",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|