@bongos/core 1.19.877 → 1.19.879
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 +54 -84
- package/docs/copy-inventory.md +195 -283
- package/docs/copy-registry.json +254 -1141
- package/docs/file-map.md +2 -2
- package/docs/module-api-changelog.md +5 -1
- package/docs/recipes/autobongos-windows-host.md +26 -1
- package/modules/autonomy/digest.js +344 -0
- package/modules/hall-ui/module.json +1 -1
- package/modules/hall-ui/public/builders.js +1 -13
- package/modules/hall-ui/public/dom-utils.js +4 -4
- package/modules/hall-ui/public/hall-render.js +2 -18
- package/modules/hall-ui/public/index.html +0 -3
- package/modules/hall-ui/public/panel.css +8 -9
- package/modules/hall-ui/public/settings.css +0 -5
- package/modules/hall-ui/public/settings.html +0 -29
- package/modules/hall-ui/public/settings.js +1 -26
- package/modules/hall-ui/public/shell.js +0 -1
- package/modules/hall-ui/public/style.css +0 -44
- package/modules/hall-ui/public/watch.html +0 -7
- package/modules/hall-ui/public/watch.js +1 -37
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/autobongos-digest.js +123 -0
- package/scripts/gds/autobongos-run.js +7 -0
- package/scripts/gds/autobongos-service.ps1 +7 -1
- package/scripts/gds/provision-core-upgrade.js +23 -3
- package/src/bongos/serve-internal.js +11 -11
- package/src/module-api.js +1 -1
- package/tests/autonomy_digest.mjs +302 -0
- package/tests/core_upgrade_runner.mjs +43 -1
- package/tests/government_page_gates.mjs +1 -1
- package/tests/hall_landing_boot.mjs +1 -1
- package/tests/hall_nav.mjs +1 -1
- package/tests/hall_oversight_world.mjs +5 -5
- package/tests/hall_page_gate_map.mjs +11 -2
- package/tests/hall_settings_world.mjs +21 -18
- package/modules/hall-ui/public/box-panel.js +0 -456
- package/modules/hall-ui/public/harbor.css +0 -9
- package/modules/hall-ui/public/harbor.html +0 -69
- package/modules/hall-ui/public/harbor.js +0 -367
- package/modules/hall-ui/public/harbor.states.json +0 -77
- package/modules/hall-ui/public/pair.css +0 -23
- package/modules/hall-ui/public/pair.html +0 -58
- package/modules/hall-ui/public/pair.js +0 -132
- package/modules/hall-ui/public/pair.states.json +0 -14
|
@@ -157,11 +157,6 @@ select.voices__select { min-width: 140px; }
|
|
|
157
157
|
.settings-btn--discord { background: #5865F2; border-color: #4853c4; color: #ffffff; }
|
|
158
158
|
.settings-btn--discord:hover:not(:disabled) { background: #4853c4; border-color: #4853c4; color: #ffffff; }
|
|
159
159
|
|
|
160
|
-
/* ---- dev box ------------------------------------------------------------- */
|
|
161
|
-
/* the box panel is the shell's (.setup__box, box-panel.js); inside a settings
|
|
162
|
-
panel it sits on the glass rather than a second card */
|
|
163
|
-
.settings-groups .setup__box { margin: var(--sp-sm) 0 0; background: var(--glass); border-color: var(--glass-edge); border-radius: var(--radius-sm); }
|
|
164
|
-
|
|
165
160
|
/* Specialities panel (task 1003855, ADR 0296). A speciality is a named bundle of
|
|
166
161
|
knowledge and terms under a craft; this styles the list, the train form and the
|
|
167
162
|
create form. Tokens only — no literal colours, so light/dark follow the page. */
|
|
@@ -195,20 +195,6 @@
|
|
|
195
195
|
|
|
196
196
|
<!-- ============ group: devbox ============ -->
|
|
197
197
|
|
|
198
|
-
<!-- DEV BOX — full box management (#807, ADR 0040). Request/wake, open
|
|
199
|
-
the online terminal, see SSH details, and close the box. Settings
|
|
200
|
-
shows BOTH the online terminal AND the SSH IP + command. The
|
|
201
|
-
OTBBoxPanel module (box-panel.js) renders + polls inside #box-mount. -->
|
|
202
|
-
<section class="scroll" id="box-scroll" aria-labelledby="box-h" data-group="devbox" hidden>
|
|
203
|
-
<h2 id="box-h" class="scroll__h">Your dev box</h2>
|
|
204
|
-
<p class="scroll__lede">
|
|
205
|
-
The hosted machine your work runs on. Open it in your browser (the
|
|
206
|
-
online terminal) or connect over SSH — whichever you prefer.
|
|
207
|
-
</p>
|
|
208
|
-
<div class="setup__box" id="box-mount"></div>
|
|
209
|
-
<p class="section-note">Actions apply at once. Closing stops and deletes the box; you can start a fresh one anytime.</p>
|
|
210
|
-
</section>
|
|
211
|
-
|
|
212
198
|
<!-- DEV BOX APP — the desktop switch (#904, ADR 0044). Static download
|
|
213
199
|
links; the /downloads/devbox/* redirects resolve to the current
|
|
214
200
|
release (config/devbox-app.json). -->
|
|
@@ -236,19 +222,6 @@
|
|
|
236
222
|
</p>
|
|
237
223
|
</section>
|
|
238
224
|
|
|
239
|
-
<!-- PAIRING — link out to the dedicated pair page (task 2172): keeps
|
|
240
|
-
the whole dev-box story reachable from this group. -->
|
|
241
|
-
<section class="scroll" id="pairing-scroll" aria-labelledby="pairing-h" data-group="devbox" hidden>
|
|
242
|
-
<h2 id="pairing-h" class="scroll__h">Pair Claude Desktop</h2>
|
|
243
|
-
<p class="scroll__lede">
|
|
244
|
-
Connect your Claude Desktop to your dev box with a one-time pairing
|
|
245
|
-
code — the guided flow lives on its own page.
|
|
246
|
-
</p>
|
|
247
|
-
<div class="settings-card">
|
|
248
|
-
<a class="settings-btn" href="/pair">Open the pairing page</a>
|
|
249
|
-
</div>
|
|
250
|
-
</section>
|
|
251
|
-
|
|
252
225
|
<!-- ============ group: preferences ============ -->
|
|
253
226
|
|
|
254
227
|
<!-- INTERACTION PROFILE — how replies are pitched for you (task 2010,
|
|
@@ -453,8 +426,6 @@
|
|
|
453
426
|
<!-- shared record overlay (#/idea|blocker/N) — every hall page carries it; this
|
|
454
427
|
page was the one gap (task 2172). -->
|
|
455
428
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
|
|
456
|
-
<!-- #807: shared dev-box panel — loaded before settings.js so OTBBoxPanel exists. -->
|
|
457
|
-
<script src="/builders/box-panel.js?v=2026-06-14-domutils"></script>
|
|
458
429
|
<script src="/builders/settings.js?v=2026-09-02-panel"></script>
|
|
459
430
|
<!-- task 1003341: the Active sessions panel. Its own file rather than more of
|
|
460
431
|
settings.js, which sits close to the 1500-line size ratchet. -->
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
//
|
|
7
7
|
// 1. VOICES — per-skill model picker (GET/PATCH /api/bongos/me/skill-prefs).
|
|
8
8
|
// 2. CRAFT — preferred disciplines (GET /api/bongos/me, PATCH /api/bongos/me/disciplines).
|
|
9
|
-
// 3. DEV BOX — full box management via the shared OTBBoxPanel (#807, ADR 0040):
|
|
10
|
-
// request/open the online terminal, SSH details, and close.
|
|
11
9
|
// 5. SOUNDS — per-sound on/off toggles (GET/PATCH /api/bongos/me/sound-prefs, #786).
|
|
12
10
|
//
|
|
13
11
|
// On a 401 it redirects into the web sign-in flow, like the other hall pages.
|
|
@@ -813,24 +811,6 @@
|
|
|
813
811
|
$('#display-name-scroll').hidden = false;
|
|
814
812
|
}
|
|
815
813
|
|
|
816
|
-
// ---- section 1b: DEV BOX (full management, #807 ADR 0040) ----------------
|
|
817
|
-
//
|
|
818
|
-
// Mounts the shared OTBBoxPanel (box-panel.js) with showSsh:true so a builder
|
|
819
|
-
// sees BOTH the online terminal and the SSH IP + command, and can request,
|
|
820
|
-
// open, or close their box. The module owns its own polling + state painting;
|
|
821
|
-
// we just hand it the mount element. Hidden if the module failed to load.
|
|
822
|
-
function setupBox() {
|
|
823
|
-
// R60 (task 1199): dev boxes are a feature module. On a vanilla instance the
|
|
824
|
-
// module is off — there is no box to manage, so the whole section stays
|
|
825
|
-
// hidden (its #box-scroll defaults to hidden in the markup).
|
|
826
|
-
if (!window.OTB.moduleOn('dev-box')) return;
|
|
827
|
-
const mount = $('#box-mount');
|
|
828
|
-
if (!mount || !window.OTBBoxPanel) return;
|
|
829
|
-
const panel = window.OTBBoxPanel.create({ api: API, showSsh: true, toast });
|
|
830
|
-
$('#box-scroll').hidden = false;
|
|
831
|
-
panel.mount(mount);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
814
|
// ---- section 3: CONNECTIONS (Discord link/unlink, F7 ADR 0033) -----------
|
|
835
815
|
//
|
|
836
816
|
// The /me payload carries `discord: { configured, linked }`. The section is
|
|
@@ -1243,9 +1223,7 @@
|
|
|
1243
1223
|
'disciplines': 'craft-scroll',
|
|
1244
1224
|
'cli': 'cli-scroll',
|
|
1245
1225
|
'art-key': 'art-key',
|
|
1246
|
-
'dev-box': 'box-scroll',
|
|
1247
1226
|
'dev-box-app': 'boxapp-scroll',
|
|
1248
|
-
'pairing': 'pairing-scroll',
|
|
1249
1227
|
'interaction': 'interaction-scroll',
|
|
1250
1228
|
'wandering': 'wandering-scroll',
|
|
1251
1229
|
'rendering': 'render-scroll',
|
|
@@ -1358,14 +1336,11 @@
|
|
|
1358
1336
|
// DISPLAY NAME — the label beside your work (task 1669); seeded from /me.
|
|
1359
1337
|
setupDisplayName(builder);
|
|
1360
1338
|
|
|
1361
|
-
// DEV BOX —
|
|
1362
|
-
setupBox();
|
|
1363
|
-
// DEV BOX APP + PAIRING — revealed once we know the viewer is a real builder,
|
|
1339
|
+
// DEV BOX APP — revealed once we know the viewer is a real builder,
|
|
1364
1340
|
// and only where the dev-box module runs (the group's rail entry is gated
|
|
1365
1341
|
// the same way above — task 2172).
|
|
1366
1342
|
if (window.OTB.moduleOn('dev-box')) {
|
|
1367
1343
|
$('#boxapp-scroll').hidden = false;
|
|
1368
|
-
$('#pairing-scroll').hidden = false;
|
|
1369
1344
|
loadDevboxLatestVersion(); // #924: best-effort "latest version: X" courtesy line
|
|
1370
1345
|
}
|
|
1371
1346
|
// CONNECTIONS — Discord (hidden unless the server has Discord configured).
|
|
@@ -314,7 +314,6 @@
|
|
|
314
314
|
// Metic+ oversight could not honestly hold it. Everything below IS a rank
|
|
315
315
|
// surface, so this group's gate and audience are unchanged.
|
|
316
316
|
{ id: 'watch', label: 'Watch', icon: 'admin', href: '/watch', gate: 'government' },
|
|
317
|
-
{ id: 'harbor', label: 'Harbor', icon: 'admin', href: '/harbor', gate: 'government', module: 'dev-box' },
|
|
318
317
|
{ id: 'gate', label: 'Gate', icon: 'admin', href: '/gate', gate: 'government' },
|
|
319
318
|
{ id: 'sessions', label: 'Sessions', icon: 'admin', href: '/sessions', gate: 'government' },
|
|
320
319
|
// The project's own fundamentals (task 1003280, goal 1000072). Mirrors the
|
|
@@ -2421,50 +2421,6 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
|
|
|
2421
2421
|
}
|
|
2422
2422
|
.setup__btn:hover:not(:disabled) { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; text-decoration: none; }
|
|
2423
2423
|
.setup__btn:disabled, .setup__btn--open[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
|
|
2424
|
-
.setup__btn--box { background: var(--surface); border-color: var(--border-strong); color: var(--ink); }
|
|
2425
|
-
.setup__btn--box:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
|
|
2426
|
-
.setup__btn--danger { background: var(--surface); border-color: color-mix(in srgb, var(--danger) 45%, transparent); color: var(--danger); }
|
|
2427
|
-
.setup__btn--danger:hover:not(:disabled) { background: var(--danger-weak); }
|
|
2428
|
-
|
|
2429
|
-
.setup__box { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 14px; margin: 10px 0; background: var(--surface-2); }
|
|
2430
|
-
.setup__box-btns { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
|
|
2431
|
-
.setup__box-msg { font-size: 13px; margin: 6px 0; }
|
|
2432
|
-
.setup__box-msg--ok { color: var(--ok); }
|
|
2433
|
-
.setup__box-msg--warn { color: var(--warn); }
|
|
2434
|
-
.setup__box-msg--err { color: var(--danger); }
|
|
2435
|
-
.setup__box-out {
|
|
2436
|
-
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
2437
|
-
font-size: 12px;
|
|
2438
|
-
background: var(--surface);
|
|
2439
|
-
border: 1px solid var(--border);
|
|
2440
|
-
border-radius: var(--radius-sm);
|
|
2441
|
-
padding: 8px 10px;
|
|
2442
|
-
max-height: 180px;
|
|
2443
|
-
overflow: auto;
|
|
2444
|
-
}
|
|
2445
|
-
.setup__box-close { margin-left: auto; }
|
|
2446
|
-
.setup__box-close-hint { font-size: 12px; color: var(--ink-faint); }
|
|
2447
|
-
.setup__cred-row { display: flex; align-items: center; gap: 8px; margin: 4px 0; font-size: 13px; flex-wrap: wrap; }
|
|
2448
|
-
.setup__cred-k { color: var(--ink-faint); min-width: 90px; }
|
|
2449
|
-
.setup__cred-v {
|
|
2450
|
-
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
2451
|
-
font-size: 12px;
|
|
2452
|
-
background: var(--surface-2);
|
|
2453
|
-
border: 1px solid var(--border);
|
|
2454
|
-
border-radius: 4px;
|
|
2455
|
-
padding: 2px 8px;
|
|
2456
|
-
}
|
|
2457
|
-
.setup__copy {
|
|
2458
|
-
border: 1px solid var(--border-strong);
|
|
2459
|
-
background: var(--surface);
|
|
2460
|
-
color: var(--ink-soft);
|
|
2461
|
-
border-radius: var(--radius-sm);
|
|
2462
|
-
font-size: 12px;
|
|
2463
|
-
padding: 3px 10px;
|
|
2464
|
-
cursor: pointer;
|
|
2465
|
-
}
|
|
2466
|
-
.setup__copy:hover { background: var(--surface-2); }
|
|
2467
|
-
.setup__copy--copied { background: var(--ok-weak); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
|
|
2468
2424
|
.setup__getcode { margin: 10px 0; }
|
|
2469
2425
|
/* A <summary> is a control, so it owes the 24px floor like any other; it
|
|
2470
2426
|
measured 20px tall (task 1003304). The height is bought with padding rather
|
|
@@ -89,13 +89,6 @@
|
|
|
89
89
|
<!-- ============ tab: Operations ============ -->
|
|
90
90
|
<div id="pane-operations" role="tabpanel" aria-labelledby="tab-operations" hidden>
|
|
91
91
|
|
|
92
|
-
<!-- 5. BOX COSTS (#602) — per-builder dev-box cost ledger + totals. -->
|
|
93
|
-
<section class="scroll" id="box-costs-scroll" aria-labelledby="box-costs-h" hidden>
|
|
94
|
-
<h2 id="box-costs-h" class="scroll__h">Dev-box costs</h2>
|
|
95
|
-
<p class="scroll__lede">What each builder's dev box has cost — spin-up, compute, and storage. Tracked for oversight; not charged in the early stage.</p>
|
|
96
|
-
<div id="box-costs-body"><span class="kit-skel"></span></div>
|
|
97
|
-
</section>
|
|
98
|
-
|
|
99
92
|
<!-- 6. SESSIONS — the tally, and the five most recent sessions the page
|
|
100
93
|
always fetched and used to throw away (task 1003469). -->
|
|
101
94
|
<section class="scroll" id="sessions-scroll" aria-labelledby="sessions-h" hidden>
|
|
@@ -897,41 +897,6 @@
|
|
|
897
897
|
});
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
-
// ---- Box costs (#602): per-builder container-cost totals + recent ledger ----
|
|
901
|
-
function renderBoxCosts(data, target) {
|
|
902
|
-
const totals = Array.isArray(data.totals_by_builder) ? data.totals_by_builder : [];
|
|
903
|
-
const recent = Array.isArray(data.ledger) ? data.ledger : [];
|
|
904
|
-
const usd = (n) => `$${(Number(n) || 0).toFixed(2)}`;
|
|
905
|
-
target.innerHTML = '';
|
|
906
|
-
if (!totals.length) {
|
|
907
|
-
target.innerHTML = emptyStateHtml('No dev-box costs accrued yet.');
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
ledger(group(target, 'Totals by builder', totals.length), {
|
|
911
|
-
columns: ['Builder', { label: 'Total', class: 'num' }],
|
|
912
|
-
rowHtml: (tb) => `<tr><td>${escapeHtml(tb.builder_login || ('#' + (tb.builder_id ?? '?')))}</td><td class="num">${usd(tb.total_usd)}</td></tr>`,
|
|
913
|
-
}).render(totals);
|
|
914
|
-
ledger(group(target, 'Recent ledger', recent.length), {
|
|
915
|
-
columns: ['When', 'Builder', 'Kind', { label: 'Amount', class: 'num' }, 'Detail'],
|
|
916
|
-
pager: makePager('watch-boxcosts', 'otb.watch.boxCostsPerPage'),
|
|
917
|
-
emptyMsg: 'No ledger rows yet.',
|
|
918
|
-
rowHtml: (r) => `<tr><td>${escapeHtml(String(r.recorded_at || '').slice(0, 16).replace('T', ' '))}</td>` +
|
|
919
|
-
`<td>${escapeHtml(r.builder_login || ('#' + (r.builder_id ?? '?')))}</td>` +
|
|
920
|
-
`<td><span class="ov-mono">${escapeHtml((r.source || '').replace('box-', ''))}</span></td>` +
|
|
921
|
-
`<td class="num">${usd(r.amount_usd)}</td>` +
|
|
922
|
-
`<td>${escapeHtml(r.description || '')}</td></tr>`,
|
|
923
|
-
}).render(recent);
|
|
924
|
-
target.insertAdjacentHTML('beforeend', foot('Container cost is tracked for oversight — not charged in the early stage.'));
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function loadBoxCosts() {
|
|
928
|
-
return section($('#box-costs-body'), () => fetchJson('/boxes/cost-ledger?limit=100'), renderBoxCosts, {
|
|
929
|
-
sealedMsg: 'The cost ledger is not visible at your rank.',
|
|
930
|
-
errMsg: 'Could not load the dev-box cost ledger.',
|
|
931
|
-
name: 'box costs',
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
|
|
935
900
|
// =========================================================================
|
|
936
901
|
// 4. MARKS — reviews by builder (per-builder grade breakdown)
|
|
937
902
|
// NOTE: built in parallel — code defensively (404 / [] / missing fields).
|
|
@@ -1316,7 +1281,7 @@
|
|
|
1316
1281
|
|
|
1317
1282
|
function revealSections() {
|
|
1318
1283
|
[
|
|
1319
|
-
'watch-attention', 'watch-tabs', 'gate-scroll', 'watch-scroll', 'roster-scroll',
|
|
1284
|
+
'watch-attention', 'watch-tabs', 'gate-scroll', 'watch-scroll', 'roster-scroll',
|
|
1320
1285
|
'marks-scroll', 'mason-marks-scroll', 'surveyor-scroll', 'ledger-scroll', 'sessions-scroll',
|
|
1321
1286
|
].forEach((id) => {
|
|
1322
1287
|
const el = document.getElementById(id);
|
|
@@ -1376,7 +1341,6 @@
|
|
|
1376
1341
|
loadOverrides();
|
|
1377
1342
|
loadAudit();
|
|
1378
1343
|
loadRoster();
|
|
1379
|
-
loadBoxCosts();
|
|
1380
1344
|
loadMarks();
|
|
1381
1345
|
loadMasonMarks();
|
|
1382
1346
|
loadSurveyor();
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.879",
|
|
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.879",
|
|
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.879",
|
|
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",
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// scripts/gds/autobongos-digest.js — read the night without opening a log
|
|
5
|
+
// (task 1003906, ADR 0307).
|
|
6
|
+
//
|
|
7
|
+
// node scripts/gds/autobongos-digest.js # since midnight, local
|
|
8
|
+
// node scripts/gds/autobongos-digest.js --since 24h # a rolling window
|
|
9
|
+
// node scripts/gds/autobongos-digest.js --json # the structured shape
|
|
10
|
+
// node scripts/gds/autobongos-digest.js --all # the whole log
|
|
11
|
+
//
|
|
12
|
+
// The decision logic is modules/autonomy/digest.js and has no I/O; this half
|
|
13
|
+
// finds the two files it reads and prints the result. Exit 0 always, including on
|
|
14
|
+
// an empty night: a digest is a report, and a non-zero exit would make a cron
|
|
15
|
+
// wrapper treat "nothing happened" as a failure.
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
|
|
20
|
+
const digest = require('../../modules/autonomy/digest.js');
|
|
21
|
+
|
|
22
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
23
|
+
|
|
24
|
+
function arg(flag, argv) {
|
|
25
|
+
const i = argv.indexOf(flag);
|
|
26
|
+
return i >= 0 ? argv[i + 1] : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logPath() {
|
|
30
|
+
if (process.env.AUTOBONGOS_LOG_FILE) return process.env.AUTOBONGOS_LOG_FILE;
|
|
31
|
+
try { return require('../../src/instance-config.js').configPath('autobongos-runs.jsonl'); }
|
|
32
|
+
catch (_) { return path.join(REPO_ROOT, 'autobongos-runs.jsonl'); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function gaugePath() {
|
|
36
|
+
if (process.env.AUTOBONGOS_GAUGE_FILE) return process.env.AUTOBONGOS_GAUGE_FILE;
|
|
37
|
+
try { return require('../../src/instance-config.js').configPath('autobongos-gauge.json'); }
|
|
38
|
+
catch (_) { return path.join(REPO_ROOT, 'autobongos-gauge.json'); }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// readRows — an append-only JSONL, and a single unparseable line must not cost
|
|
42
|
+
// the whole night. A JSONL written by a process that can be killed mid-write has
|
|
43
|
+
// a torn last line more often than never.
|
|
44
|
+
function readRows(file) {
|
|
45
|
+
let text;
|
|
46
|
+
try { text = fs.readFileSync(file, 'utf8'); }
|
|
47
|
+
catch (_) { return { rows: [], missing: true, skipped: 0 }; }
|
|
48
|
+
const rows = [];
|
|
49
|
+
let skipped = 0;
|
|
50
|
+
for (const line of text.split('\n')) {
|
|
51
|
+
const t = line.trim();
|
|
52
|
+
if (!t) continue;
|
|
53
|
+
try { rows.push(JSON.parse(t)); } catch (_) { skipped += 1; }
|
|
54
|
+
}
|
|
55
|
+
return { rows, missing: false, skipped };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readGaugeFile(file) {
|
|
59
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
60
|
+
catch (_) { return null; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// sinceMs — `--since 24h|90m|3d`, or midnight LOCAL by default.
|
|
64
|
+
//
|
|
65
|
+
// Local rather than UTC because the question is "what happened last night" and
|
|
66
|
+
// night is a local idea; a UTC midnight cut would split an owner's evening in
|
|
67
|
+
// half depending on where they live.
|
|
68
|
+
function sinceMs(spec, now = new Date()) {
|
|
69
|
+
if (!spec) {
|
|
70
|
+
const d = new Date(now);
|
|
71
|
+
d.setHours(0, 0, 0, 0);
|
|
72
|
+
return d.getTime();
|
|
73
|
+
}
|
|
74
|
+
const m = /^(\d+(?:\.\d+)?)\s*([hmd])$/i.exec(String(spec).trim());
|
|
75
|
+
if (!m) return null;
|
|
76
|
+
const mult = { h: 3600e3, m: 60e3, d: 86400e3 }[m[2].toLowerCase()];
|
|
77
|
+
return now.getTime() - Number(m[1]) * mult;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function main(argv) {
|
|
81
|
+
const all = argv.includes('--all');
|
|
82
|
+
const asJson = argv.includes('--json');
|
|
83
|
+
const now = new Date();
|
|
84
|
+
const from = all ? null : sinceMs(arg('--since', argv), now);
|
|
85
|
+
if (!all && from === null) {
|
|
86
|
+
console.error('autobongos-digest: --since wants a span like 24h, 90m or 3d.');
|
|
87
|
+
return 2;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lf = logPath();
|
|
91
|
+
const { rows, missing, skipped } = readRows(lf);
|
|
92
|
+
const inWindow = all ? rows : rows.filter((r) => {
|
|
93
|
+
const t = Date.parse(r && r.at);
|
|
94
|
+
return Number.isFinite(t) ? t >= from : true; // an undated row is kept, not silently dropped
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const d = digest.buildDigest(inWindow, {
|
|
98
|
+
gauge: readGaugeFile(gaugePath()),
|
|
99
|
+
nowEpochS: Math.floor(now.getTime() / 1000),
|
|
100
|
+
sinceMs: all ? null : from,
|
|
101
|
+
untilMs: now.getTime(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (asJson) { console.log(JSON.stringify(d, null, 2)); return 0; }
|
|
105
|
+
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(digest.renderDigest(d));
|
|
108
|
+
// Both of these are about the DIGEST's own reliability, so they sit outside the
|
|
109
|
+
// five questions rather than being folded into them.
|
|
110
|
+
if (missing) {
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log(` No run log at ${lf} — the runner has not written one here yet.`);
|
|
113
|
+
}
|
|
114
|
+
if (skipped) console.log(` (${skipped} unreadable line(s) in the log were skipped.)`);
|
|
115
|
+
console.log('');
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (require.main === module) {
|
|
120
|
+
process.exit(main(process.argv.slice(2)));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { main, sinceMs, readRows, logPath, gaugePath };
|
|
@@ -381,6 +381,13 @@ async function iteration(opts, deps = {}) {
|
|
|
381
381
|
// What the LEDGER says, kept separate from what the worker said, so the run
|
|
382
382
|
// log can be read afterwards without having to trust either one alone.
|
|
383
383
|
ledger_status: ledger.status, ledger_shape: ledger.shape, verified: !!ledger.verified,
|
|
384
|
+
// The LEDGER's own explanation, kept beside the worker's. `reason` above is
|
|
385
|
+
// verdict.reason — what the WORKER said happened — and it was the only reason
|
|
386
|
+
// in the row, so anything reading the log afterwards was quoting the thing
|
|
387
|
+
// being checked rather than the check. The morning digest did exactly that
|
|
388
|
+
// until the grader caught it (task 1003906).
|
|
389
|
+
ledger_reason: ledger.reason || null,
|
|
390
|
+
ledger_remedy: ledger.remedy || null,
|
|
384
391
|
artifact_on_main: ledger.artifact && ledger.artifact.checked ? !!ledger.artifact.onMain : null,
|
|
385
392
|
claim_released: !!ledger.released,
|
|
386
393
|
blocker_id: ledger.blocker && ledger.blocker.blockerId ? ledger.blocker.blockerId : null,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# powershell -ExecutionPolicy Bypass -File scripts\gds\autobongos-service.ps1 install
|
|
5
5
|
# powershell -ExecutionPolicy Bypass -File scripts\gds\autobongos-service.ps1 status
|
|
6
|
+
# powershell -ExecutionPolicy Bypass -File scripts\gds\autobongos-service.ps1 digest
|
|
6
7
|
# powershell -ExecutionPolicy Bypass -File scripts\gds\autobongos-service.ps1 start|stop|uninstall
|
|
7
8
|
#
|
|
8
9
|
# WHY TASK SCHEDULER AND NOT A REAL SERVICE. Windows services speak a protocol
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
[CmdletBinding()]
|
|
30
31
|
param(
|
|
31
32
|
[Parameter(Position = 0)]
|
|
32
|
-
[ValidateSet('install', 'uninstall', 'start', 'stop', 'status')]
|
|
33
|
+
[ValidateSet('install', 'uninstall', 'start', 'stop', 'status', 'digest')]
|
|
33
34
|
[string]$Action = 'status',
|
|
34
35
|
|
|
35
36
|
# The checkout to run from. Defaults to the repo this script lives in, so the
|
|
@@ -118,6 +119,11 @@ switch ($Action) {
|
|
|
118
119
|
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
|
|
119
120
|
Write-Host "removed '$TaskName'. The log at $LogFile is left in place."
|
|
120
121
|
}
|
|
122
|
+
'digest' {
|
|
123
|
+
# The night in one page (task 1003906). On the box for convenience; the same
|
|
124
|
+
# five answers are what the Gate page and a mailed digest would carry.
|
|
125
|
+
& node (Join-Path $RepoRoot 'scripts\gds\autobongos-digest.js') @args
|
|
126
|
+
}
|
|
121
127
|
'start' { Start-ScheduledTask -TaskName $TaskName; Write-Host "started '$TaskName'" }
|
|
122
128
|
'stop' {
|
|
123
129
|
Stop-ScheduledTask -TaskName $TaskName
|
|
@@ -187,6 +187,25 @@ async function coreUpgradeInstance(inst, deps, intent) {
|
|
|
187
187
|
const healthUrl = `http://127.0.0.1:${inst.port}/healthz`;
|
|
188
188
|
const versionUrl = `http://127.0.0.1:${inst.port}/version`;
|
|
189
189
|
const sudoP = privileged ? 'sudo ' : '';
|
|
190
|
+
// WHERE THE CLI LIVES AND WHERE THE TARGET LIVES ARE DIFFERENT PLACES (task 1004053).
|
|
191
|
+
//
|
|
192
|
+
// `scripts/gds/upgrade.js` ships in the CORE checkout. A provisioned instance root holds
|
|
193
|
+
// no scripts/ directory at all — its core sits under node_modules/@bongos/core — so
|
|
194
|
+
// running the CLI with cwd = the instance root resolved nothing, and EVERY standalone
|
|
195
|
+
// upgrade died before it began:
|
|
196
|
+
//
|
|
197
|
+
// Error: Cannot find module '/srv/<base>/<slug>/scripts/gds/upgrade.js'
|
|
198
|
+
//
|
|
199
|
+
// It worked only where instanceDir HAPPENED to be this checkout — the control-plane self
|
|
200
|
+
// row and a co-tenant — which is why it survived to production: the shapes that are
|
|
201
|
+
// upgraded by hand are the shapes the relative path fits. Every ACTIVE tenant is
|
|
202
|
+
// standalone, so the deploy door could not move a single one of them.
|
|
203
|
+
//
|
|
204
|
+
// upgrade.js's own header states the contract this restores: "Runs from the consumer
|
|
205
|
+
// instance root (default) or `--instance <dir>`. This script ships IN the core … against
|
|
206
|
+
// `--instance <consumer>`." So: run it where it exists, and NAME the target.
|
|
207
|
+
const runDir = REPO_ROOT;
|
|
208
|
+
const targetArg = instanceDir === REPO_ROOT ? '' : `--instance ${instanceDir}`;
|
|
190
209
|
const cli = path.posix.join('scripts', 'gds', 'upgrade.js');
|
|
191
210
|
const requestedBy = intent.requested_by && /^api:archon:(\d+)$/.test(intent.requested_by)
|
|
192
211
|
? intent.requested_by.replace(/^api:archon:/, '')
|
|
@@ -195,6 +214,7 @@ async function coreUpgradeInstance(inst, deps, intent) {
|
|
|
195
214
|
`${sudoP}node ${cli}`,
|
|
196
215
|
'--registry',
|
|
197
216
|
`--to ${to}`,
|
|
217
|
+
targetArg,
|
|
198
218
|
`--service ${inst.slug}`,
|
|
199
219
|
`--health-url ${healthUrl}`,
|
|
200
220
|
`--version-url ${versionUrl}`,
|
|
@@ -204,13 +224,13 @@ async function coreUpgradeInstance(inst, deps, intent) {
|
|
|
204
224
|
].filter(Boolean).join(' ');
|
|
205
225
|
|
|
206
226
|
if (!apply) {
|
|
207
|
-
log(` ✓ would run: ${cmd} (dry-run — nothing ran)`);
|
|
227
|
+
log(` ✓ would run (in ${runDir}): ${cmd} (dry-run — nothing ran)`);
|
|
208
228
|
log(` ✓ would then read ${versionUrl} on the box and require it to say ${to}`);
|
|
209
229
|
return { ok: true, served: null };
|
|
210
230
|
}
|
|
211
231
|
try {
|
|
212
|
-
log(` [upgrade] ${cmd}`);
|
|
213
|
-
boxExec(cmd, { cwd:
|
|
232
|
+
log(` [upgrade] (in ${runDir}) ${cmd}`);
|
|
233
|
+
boxExec(cmd, { cwd: runDir });
|
|
214
234
|
} catch (e) {
|
|
215
235
|
// upgrade.js rolls itself back on a failed health check or an unchanged served
|
|
216
236
|
// version, so by the time this throws the instance is back on the core it had.
|
|
@@ -927,16 +927,16 @@ function mountInternalSurfaces(app) {
|
|
|
927
927
|
return res.redirect(301, `https://builders.${apex}/${rest}${qs}`);
|
|
928
928
|
});
|
|
929
929
|
|
|
930
|
-
// The /watch monitoring page and the /
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
//
|
|
934
|
-
//
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
//
|
|
930
|
+
// The /watch monitoring page and the /gate page (task 1179) both sit behind
|
|
931
|
+
// `page.view.government` — Metic and up since ADR 0157. Same server-side page
|
|
932
|
+
// gate, same posture: a builder without the permission never receives the shell
|
|
933
|
+
// (requireGovernmentPage — named requireArchonPage before ADR 0157 — redirects
|
|
934
|
+
// them to the hall, and an unauthenticated visitor into sign-in). The subdomain
|
|
935
|
+
// shim above has already rewritten the bare /watch + /gate onto /builders/*.
|
|
936
|
+
// (Defense in depth: every data endpoint these pages call carries its own
|
|
937
|
+
// server-side gate.) /harbor sat here too until task 1003890 removed the dev
|
|
938
|
+
// box: its page is gone, and a gate in front of nothing is a dead guard.
|
|
938
939
|
const WATCH_PAGE_RE = /^\/builders\/watch(?:\.html)?\/?$/;
|
|
939
|
-
const HARBOR_PAGE_RE = /^\/builders\/harbor(?:\.html)?\/?$/;
|
|
940
940
|
const GATE_PAGE_RE = /^\/builders\/gate(?:\.html)?\/?$/;
|
|
941
941
|
// /sessions (the session-log ledger) belongs to that same Governance group and
|
|
942
942
|
// its data route (`session.search`) carries the same metic floor since ADR 0157
|
|
@@ -1020,7 +1020,7 @@ function mountInternalSurfaces(app) {
|
|
|
1020
1020
|
if (isModuleSurfaceHost(req)) return next();
|
|
1021
1021
|
if (!req.path.startsWith('/builders')) return next();
|
|
1022
1022
|
const norm = path.posix.normalize(req.path);
|
|
1023
|
-
if (WATCH_PAGE_RE.test(norm) ||
|
|
1023
|
+
if (WATCH_PAGE_RE.test(norm) || GATE_PAGE_RE.test(norm)
|
|
1024
1024
|
|| SESSIONS_PAGE_RE.test(norm) || PROJECT_SETTINGS_PAGE_RE.test(norm)) {
|
|
1025
1025
|
return gdsAuth.requireGovernmentPage(req, res, next);
|
|
1026
1026
|
}
|
|
@@ -1201,7 +1201,7 @@ function mountInternalSurfaces(app) {
|
|
|
1201
1201
|
// read behind it is gated server-side, so this closed an inconsistency rather
|
|
1202
1202
|
// than an exposure; the point is that one URL cannot be two tiers at once.
|
|
1203
1203
|
const SIGNEDIN_BUILDERS_PAGE_RE =
|
|
1204
|
-
/^\/builders\/(?:work|
|
|
1204
|
+
/^\/builders\/(?:work|settings|atlas|modules|goals|roadmap|copy-desk|studio|ideas|collab|task|idea|primer(?:\.md)?|diagrams)(?:\.html)?\/?$/;
|
|
1205
1205
|
// The diagram image assets under /builders/diagrams/ ride the same tier as the
|
|
1206
1206
|
// page that embeds them.
|
|
1207
1207
|
const SIGNEDIN_BUILDERS_PREFIX_RE = /^\/builders\/diagrams\//;
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.879'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|