@bongos/core 1.19.873 → 1.19.875
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 +56 -31
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +18 -0
- package/docs/api/openapi.json +218 -3
- package/docs/api-reference.md +6 -2
- package/docs/copy-inventory.md +48 -39
- package/docs/copy-registry.json +152 -71
- package/docs/module-api-changelog.md +4 -0
- package/modules/autonomy/db.js +74 -0
- package/modules/autonomy/fence.js +172 -0
- package/modules/autonomy/migrations/autonomy_001_autobongos_fence.sql +71 -0
- package/modules/autonomy/routes/autonomy.js +91 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
- package/modules/government/catalog.js +11 -0
- package/modules/government/migrations/government_016_autonomy_fence_manage.sql +37 -0
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/deploy.js +189 -58
- package/modules/hall-ui/public/gate.html +14 -1
- package/modules/hall-ui/public/gate.js +117 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/autobongos-run.js +90 -15
- package/src/module-api.js +1 -1
- package/tests/autobongos_fence.mjs +262 -0
- package/tests/autobongos_loop.mjs +120 -1
- package/tests/deploy_page_projects.mjs +193 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// tests/deploy_page_projects.mjs — the deploy page now drives every hosted project,
|
|
2
|
+
// not only the control plane's own row (task 1004046).
|
|
3
|
+
//
|
|
4
|
+
// These EXECUTE deploy.js in a DOM stub and read what it painted. The sibling suite
|
|
5
|
+
// (core_upgrade_door.mjs) asserts the page's invariants against its SOURCE, which is
|
|
6
|
+
// right for "never computes a version comparison" but cannot see the thing that broke
|
|
7
|
+
// when this page went multi-box: whether a click on one box arms that box and not
|
|
8
|
+
// another. That is behaviour, so it is tested as behaviour.
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { test } from 'node:test';
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import vm from 'node:vm';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const SRC = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/deploy.js'), 'utf8');
|
|
18
|
+
|
|
19
|
+
const preview = (id, slug, over = {}) => ({
|
|
20
|
+
instance_id: id, slug, hosting_shape: 'standalone',
|
|
21
|
+
served_version: '1.19.800', recommended_version: '1.19.871',
|
|
22
|
+
behind: true, reading: 'ok', observed_at: new Date().toISOString(), open_intent: null,
|
|
23
|
+
...over,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// The fleet as the control plane really reports it: mostly torn down, a couple live.
|
|
27
|
+
const FLEET = [
|
|
28
|
+
{ id: '31', slug: 'mercury', status: 'active', hosting_shape: 'standalone' },
|
|
29
|
+
{ id: '32', slug: 'hoodify-finance', status: 'active', hosting_shape: 'co-tenant' },
|
|
30
|
+
{ id: '33', slug: 'walk-lantern', status: 'torn_down', hosting_shape: 'standalone' },
|
|
31
|
+
{ id: '34', slug: 'testccc', status: 'torn_down', hosting_shape: 'standalone' },
|
|
32
|
+
{ id: '35', slug: 'unreadable-one', status: 'active', hosting_shape: 'standalone' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function boot({ controlPlane = preview('1', 'cloudbongos'), controlPlaneStatus = 200, fleet = FLEET, fleetStatus = 200 } = {}) {
|
|
36
|
+
const els = new Map();
|
|
37
|
+
const posts = [];
|
|
38
|
+
const gets = [];
|
|
39
|
+
const makeEl = () => ({
|
|
40
|
+
_on: {}, _html: '', hidden: false, textContent: '', disabled: false,
|
|
41
|
+
addEventListener(ev, fn) { this._on[ev] = fn; },
|
|
42
|
+
set innerHTML(v) { this._html = String(v); }, get innerHTML() { return this._html; },
|
|
43
|
+
});
|
|
44
|
+
const documentObj = {
|
|
45
|
+
getElementById(id) { if (!els.has(id)) els.set(id, makeEl()); return els.get(id); },
|
|
46
|
+
addEventListener(ev, fn) { if (ev === 'DOMContentLoaded') this._ready = fn; },
|
|
47
|
+
};
|
|
48
|
+
const request = async (method, url, opts) => {
|
|
49
|
+
const p = url.replace('/api/bongos', '');
|
|
50
|
+
if (method === 'GET') gets.push(p);
|
|
51
|
+
if (method === 'POST') { posts.push({ path: p, body: opts && opts.body }); return { ok: true, status: 200, data: { message: 'Queued.' } }; }
|
|
52
|
+
if (p === '/provisioning/control-plane') {
|
|
53
|
+
if (controlPlaneStatus !== 200) {
|
|
54
|
+
return { ok: false, status: controlPlaneStatus, data: { error: { code: 'control_plane_not_enrolled', message: 'This platform has no deploy door yet. Run enroll-control-plane.js once.' } } };
|
|
55
|
+
}
|
|
56
|
+
return { ok: true, status: 200, data: { preview: controlPlane } };
|
|
57
|
+
}
|
|
58
|
+
if (p === '/provisioning/fleet') {
|
|
59
|
+
if (fleetStatus !== 200) return { ok: false, status: fleetStatus, data: { error: { code: 'forbidden' } } };
|
|
60
|
+
return { ok: true, status: 200, data: { instances: fleet } };
|
|
61
|
+
}
|
|
62
|
+
if (p === '/provisioning/instances') return { ok: true, status: 200, data: { instances: fleet } };
|
|
63
|
+
const m = /^\/provisioning\/instances\/(\w+)\/core-upgrade$/.exec(p);
|
|
64
|
+
if (m) {
|
|
65
|
+
const inst = fleet.find((f) => f.id === m[1]);
|
|
66
|
+
if (!inst || inst.slug === 'unreadable-one') {
|
|
67
|
+
return { ok: false, status: 500, data: { error: { code: 'core_upgrade_preview_failed', message: 'internal error' } } };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, status: 200, data: { preview: preview(inst.id, inst.slug, { hosting_shape: inst.hosting_shape }) } };
|
|
70
|
+
}
|
|
71
|
+
return { ok: false, status: 404, data: { error: { code: 'not_found' } } };
|
|
72
|
+
};
|
|
73
|
+
const windowObj = {
|
|
74
|
+
BongosClient: { createClient: () => ({ request }) },
|
|
75
|
+
OTB: { escapeHtml: (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])), toast() {} },
|
|
76
|
+
location: { assign() {} },
|
|
77
|
+
};
|
|
78
|
+
const sandbox = { window: windowObj, document: documentObj, console, setTimeout, clearTimeout, queueMicrotask, Date, Set, Map, Promise };
|
|
79
|
+
sandbox.globalThis = sandbox;
|
|
80
|
+
vm.createContext(sandbox);
|
|
81
|
+
vm.runInContext(SRC, sandbox, { filename: 'deploy.js' });
|
|
82
|
+
documentObj._ready();
|
|
83
|
+
const settle = async () => { for (let i = 0; i < 12; i++) await new Promise((r) => setTimeout(r, 0)); };
|
|
84
|
+
return { els, posts, gets, settle, panel: () => els.get('dp-panel').innerHTML, sub: () => els.get('dp-sub').textContent };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
test('the platform AND every live hosted project get a row', async () => {
|
|
88
|
+
const b = boot();
|
|
89
|
+
await b.settle();
|
|
90
|
+
const html = b.panel();
|
|
91
|
+
assert.match(html, /cloudbongos/, 'the platform is still here');
|
|
92
|
+
assert.match(html, /mercury/, 'a standalone project');
|
|
93
|
+
assert.match(html, /hoodify-finance/, 'a co-tenant project');
|
|
94
|
+
assert.match(b.sub(), /3 hosted projects/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('torn-down projects never appear — the page is about what is live', async () => {
|
|
98
|
+
const b = boot();
|
|
99
|
+
await b.settle();
|
|
100
|
+
const html = b.panel();
|
|
101
|
+
assert.doesNotMatch(html, /walk-lantern/);
|
|
102
|
+
assert.doesNotMatch(html, /testccc/);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('a project whose state cannot be read is LISTED, not silently dropped', async () => {
|
|
106
|
+
// A box missing from a deploy page reads as a box that needs no deploying.
|
|
107
|
+
const b = boot();
|
|
108
|
+
await b.settle();
|
|
109
|
+
assert.match(b.panel(), /unreadable-one/);
|
|
110
|
+
assert.match(b.panel(), /Could not read this project/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('opening the confirm on one project does not arm another', async () => {
|
|
114
|
+
const b = boot();
|
|
115
|
+
await b.settle();
|
|
116
|
+
b.els.get('dp-preview-31')._on.click();
|
|
117
|
+
const html = b.panel();
|
|
118
|
+
assert.match(html, /id="dp-confirm-31"/, 'mercury is armed');
|
|
119
|
+
assert.doesNotMatch(html, /id="dp-confirm-32"/, 'hoodify-finance is NOT');
|
|
120
|
+
assert.match(html, /id="dp-preview-32"/, 'and is still at step one');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('confirming sends THAT project’s id and the version it was shown', async () => {
|
|
124
|
+
const b = boot();
|
|
125
|
+
await b.settle();
|
|
126
|
+
b.els.get('dp-preview-32')._on.click();
|
|
127
|
+
b.els.get('dp-confirm-32')._on.click();
|
|
128
|
+
await b.settle();
|
|
129
|
+
assert.equal(b.posts.length, 1);
|
|
130
|
+
assert.equal(b.posts[0].path, '/provisioning/instances/32/core-upgrade');
|
|
131
|
+
// Field-wise, not deepEqual: the body is built inside the vm realm, so its
|
|
132
|
+
// prototype is not this realm's Object and deepStrictEqual rejects it on identity
|
|
133
|
+
// alone — a harness artifact that says nothing about the payload.
|
|
134
|
+
assert.equal(b.posts[0].body.to, '1.19.871');
|
|
135
|
+
assert.deepEqual(Object.keys(b.posts[0].body), ['to'], 'and nothing else is sent');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('a box with nothing newer offers no button', async () => {
|
|
139
|
+
const b = boot({ controlPlane: preview('1', 'cloudbongos', { behind: false, recommended_version: null }) });
|
|
140
|
+
await b.settle();
|
|
141
|
+
assert.match(b.panel(), /There is nothing to deploy/);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('a box that did not answer is never rendered as up to date', async () => {
|
|
145
|
+
const b = boot({ controlPlane: preview('1', 'cloudbongos', { reading: 'no_answer', served_version: null, behind: false }) });
|
|
146
|
+
await b.settle();
|
|
147
|
+
const html = b.panel();
|
|
148
|
+
assert.match(html, /unknown rather than unchanged/);
|
|
149
|
+
assert.doesNotMatch(html, /id="dp-preview-1"/, 'and is offered no move');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('an unenrolled control plane still lets the projects through', async () => {
|
|
153
|
+
// The regression this pins: the platform's own missing row used to be the whole
|
|
154
|
+
// page, so one un-run setup command would hide every project too.
|
|
155
|
+
const b = boot({ controlPlaneStatus: 404 });
|
|
156
|
+
await b.settle();
|
|
157
|
+
const html = b.panel();
|
|
158
|
+
assert.match(html, /has no deploy door yet/, 'the server’s own sentence, naming the fix');
|
|
159
|
+
assert.match(html, /mercury/, 'and the projects are still listed');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('a viewer without the fleet atom falls back to their own projects', async () => {
|
|
163
|
+
const b = boot({ fleetStatus: 403 });
|
|
164
|
+
await b.settle();
|
|
165
|
+
assert.match(b.panel(), /mercury/);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('a move re-reads ONLY the box it moved, not every project again', async () => {
|
|
169
|
+
// The grader's finding on the first round: refreshing one row by re-running the
|
|
170
|
+
// whole page load fans a probe out at every hosted project, including ones the
|
|
171
|
+
// owner never touched.
|
|
172
|
+
const b = boot();
|
|
173
|
+
await b.settle();
|
|
174
|
+
b.gets.length = 0;
|
|
175
|
+
b.els.get('dp-preview-32')._on.click();
|
|
176
|
+
b.els.get('dp-confirm-32')._on.click();
|
|
177
|
+
await b.settle();
|
|
178
|
+
const previewReads = b.gets.filter((g) => g.endsWith('/core-upgrade'));
|
|
179
|
+
assert.deepEqual(previewReads, ['/provisioning/instances/32/core-upgrade'],
|
|
180
|
+
'exactly one preview re-read, and it is the moved box');
|
|
181
|
+
assert.ok(!b.gets.includes('/provisioning/fleet'), 'the fleet is not re-listed');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('a move on the platform row re-reads the control plane, not a tenant preview', async () => {
|
|
185
|
+
const b = boot();
|
|
186
|
+
await b.settle();
|
|
187
|
+
b.gets.length = 0;
|
|
188
|
+
b.els.get('dp-preview-1')._on.click();
|
|
189
|
+
b.els.get('dp-confirm-1')._on.click();
|
|
190
|
+
await b.settle();
|
|
191
|
+
assert.ok(b.gets.includes('/provisioning/control-plane'), 'the self row is read from its own endpoint');
|
|
192
|
+
assert.ok(!b.gets.some((g) => /instances\/3\d\/core-upgrade/.test(g)), 'and no tenant is re-probed');
|
|
193
|
+
});
|