@bongos/core 1.19.884 → 1.19.886
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 +20 -285
- package/docs/architecture.md +0 -1
- package/docs/file-map.md +0 -1
- package/docs/module-api-changelog.md +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/gen-session-index.js +8 -1
- package/src/bongos/serve-internal.js +10 -56
- package/src/module-api.js +1 -1
- package/src/platform-server.js +0 -1
- package/tests/host_topology_skips.mjs +0 -1
- package/tests/instance_release_config.mjs +27 -26
- package/tests/session_rename_fallback.mjs +8 -90
- package/modules/dev-box/app/README.md +0 -74
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +0 -95
- package/modules/dev-box/app/electron-builder.yml +0 -95
- package/modules/dev-box/app/package-lock.json +0 -5291
- package/modules/dev-box/app/package.json +0 -30
- package/modules/dev-box/app/renderer/app.js +0 -319
- package/modules/dev-box/app/renderer/index.html +0 -45
- package/modules/dev-box/app/renderer/styles.css +0 -248
- package/modules/dev-box/app/renderer/theme.generated.css +0 -16
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +0 -82
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +0 -39
- package/modules/dev-box/app/scripts/gen-brand.js +0 -138
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +0 -323
- package/modules/dev-box/app/scripts/gen-icons.js +0 -286
- package/modules/dev-box/app/scripts/vendor-client.js +0 -23
- package/modules/dev-box/app/src/box-controller.js +0 -287
- package/modules/dev-box/app/src/brand.generated.json +0 -13
- package/modules/dev-box/app/src/brand.js +0 -16
- package/modules/dev-box/app/src/config.js +0 -31
- package/modules/dev-box/app/src/gds-client.js +0 -96
- package/modules/dev-box/app/src/main.js +0 -610
- package/modules/dev-box/app/src/pairing.js +0 -57
- package/modules/dev-box/app/src/preload.js +0 -29
- package/modules/dev-box/app/src/session-store.js +0 -82
- package/modules/dev-box/app/src/ssh-setup.js +0 -569
- package/modules/dev-box/app/src/task-picker.js +0 -70
- package/modules/dev-box/app/src/tray.js +0 -99
- package/modules/dev-box/app/src/updater.js +0 -55
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +0 -1014
- package/modules/dev-box/app/test/unit.mjs +0 -526
- package/src/devbox-downloads.js +0 -67
- package/tests/devbox_artifact_gate.mjs +0 -75
- package/tests/devbox_dmg_backdrop.mjs +0 -137
- package/tests/devbox_downloads.mjs +0 -140
- package/tests/devbox_icons.mjs +0 -304
- package/tests/devbox_installer_brand.mjs +0 -232
- package/tests/devbox_key_register.mjs +0 -234
- package/tests/devbox_update_path.mjs +0 -84
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// tests/instance_release_config.mjs
|
|
2
2
|
//
|
|
3
|
-
// serve-internal mounts /downloads/
|
|
4
|
-
//
|
|
5
|
-
// config/bongos-cli.json). Those configs are boundary-EXCLUDED from the packaged core,
|
|
3
|
+
// serve-internal mounts /downloads/bongos/* only when the instance ships the matching
|
|
4
|
+
// RELEASE config (config/bongos-cli.json). That config is boundary-EXCLUDED from the packaged core,
|
|
6
5
|
// so they live at the INSTANCE root — which on a vendored instance (@bongos/core in
|
|
7
6
|
// node_modules, ADR 0108) is a DIFFERENT directory from the core package dir
|
|
8
7
|
// serve-internal.js sits in. The old core-relative `require('../../config/…')` looked
|
|
9
|
-
// only in node_modules/@bongos/core/config/, so a vendored instance's
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
8
|
+
// only in node_modules/@bongos/core/config/, so a vendored instance's download routes
|
|
9
|
+
// never mounted (task 1002371; ADR 0145 papered over it by copying the config into
|
|
10
|
+
// node_modules — lost on the next npm ci). Same class as ADR 0144's vendored infra/
|
|
11
|
+
// path bug. This test was written against the /downloads/devbox/* twin, which task
|
|
12
|
+
// 1003891 deleted with the Dev Box app; it now proves the SAME ladder against
|
|
13
|
+
// /downloads/bongos/*, the surviving surface that still depends on it.
|
|
13
14
|
//
|
|
14
15
|
// The first test is THE regression: a config present ONLY at the instance root must be
|
|
15
16
|
// found. It fails against the old resolution (which could not see the instance root at
|
|
@@ -29,12 +30,12 @@ import path from 'node:path';
|
|
|
29
30
|
|
|
30
31
|
const require = createRequire(import.meta.url);
|
|
31
32
|
|
|
32
|
-
// A minimal but VALID release config —
|
|
33
|
+
// A minimal but VALID release config — buildBongosDownloads rejects a bad
|
|
33
34
|
// releases_repo/version at boot, so the end-to-end mount below proves real parsing.
|
|
34
|
-
const
|
|
35
|
-
releases_repo: 'example-owner/example-
|
|
35
|
+
const CLI_CFG = {
|
|
36
|
+
releases_repo: 'example-owner/example-cli',
|
|
36
37
|
version: '9.9.9',
|
|
37
|
-
assets: { 'mac-arm64': '
|
|
38
|
+
assets: { 'mac-arm64': 'bongos-9.9.9-macos-arm64' },
|
|
38
39
|
aliases: { mac: 'mac-arm64' },
|
|
39
40
|
};
|
|
40
41
|
|
|
@@ -50,23 +51,23 @@ function makeRoot(name, contents) {
|
|
|
50
51
|
|
|
51
52
|
// Point the process at a temp INSTANCE root BEFORE serve-internal loads, so the default
|
|
52
53
|
// (uninjected) resolution runs the vendored shape: config at the instance root, absent
|
|
53
|
-
// from the core package dir. This checkout ships no config/
|
|
54
|
+
// from the core package dir. This checkout ships no config/bongos-cli.json — it is
|
|
54
55
|
// instance-only — so the core tier genuinely has nothing to find, exactly like a
|
|
55
56
|
// freshly-vendored box.
|
|
56
|
-
const INSTANCE_ROOT = makeRoot('
|
|
57
|
+
const INSTANCE_ROOT = makeRoot('bongos-cli.json', JSON.stringify(CLI_CFG));
|
|
57
58
|
process.env.CLOUDBONGOS_INSTANCE_ROOT = INSTANCE_ROOT;
|
|
58
59
|
const { tryLoadInstanceConfig, mountInternalSurfaces } = require('../src/bongos/serve-internal.js');
|
|
59
60
|
|
|
60
61
|
test('finds a release config that exists ONLY at the instance root (the vendored bug)', () => {
|
|
61
|
-
const instance = makeRoot('
|
|
62
|
+
const instance = makeRoot('bongos-cli.json', '{"version":"1.0.0"}');
|
|
62
63
|
const core = makeRoot(null);
|
|
63
|
-
assert.deepEqual(tryLoadInstanceConfig('
|
|
64
|
+
assert.deepEqual(tryLoadInstanceConfig('bongos-cli.json', [instance, core]), { version: '1.0.0' });
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
test('the instance root wins over the core package dir when both carry a copy', () => {
|
|
67
|
-
const instance = makeRoot('
|
|
68
|
-
const core = makeRoot('
|
|
69
|
-
assert.equal(tryLoadInstanceConfig('
|
|
68
|
+
const instance = makeRoot('bongos-cli.json', '{"from":"instance"}');
|
|
69
|
+
const core = makeRoot('bongos-cli.json', '{"from":"core"}');
|
|
70
|
+
assert.equal(tryLoadInstanceConfig('bongos-cli.json', [instance, core]).from, 'instance');
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
test('falls back to the core package dir (single checkout + the copied-in workaround)', () => {
|
|
@@ -76,16 +77,16 @@ test('falls back to the core package dir (single checkout + the copied-in workar
|
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
test('absent from every root → null, so the download surface just does not mount', () => {
|
|
79
|
-
assert.equal(tryLoadInstanceConfig('
|
|
80
|
-
assert.equal(tryLoadInstanceConfig('
|
|
80
|
+
assert.equal(tryLoadInstanceConfig('bongos-cli.json', [makeRoot(null), makeRoot(null)]), null);
|
|
81
|
+
assert.equal(tryLoadInstanceConfig('bongos-cli.json', []), null);
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
test('a MALFORMED config still throws — only a MISSING file is tolerated', () => {
|
|
84
|
-
const bad = makeRoot('
|
|
85
|
-
assert.throws(() => tryLoadInstanceConfig('
|
|
85
|
+
const bad = makeRoot('bongos-cli.json', '{ this is not valid json ');
|
|
86
|
+
assert.throws(() => tryLoadInstanceConfig('bongos-cli.json', [bad]), SyntaxError);
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
test('/downloads/
|
|
89
|
+
test('/downloads/bongos/version mounts from an INSTANCE-root config (end to end)', async () => {
|
|
89
90
|
const express = require('express');
|
|
90
91
|
const app = express();
|
|
91
92
|
mountInternalSurfaces(app);
|
|
@@ -93,11 +94,11 @@ test('/downloads/devbox/version mounts from an INSTANCE-root config (end to end)
|
|
|
93
94
|
const s = app.listen(0, () => resolve(s));
|
|
94
95
|
});
|
|
95
96
|
try {
|
|
96
|
-
const res = await fetch(`http://127.0.0.1:${server.address().port}/downloads/
|
|
97
|
+
const res = await fetch(`http://127.0.0.1:${server.address().port}/downloads/bongos/version`);
|
|
97
98
|
assert.equal(res.status, 200, 'the route must mount when only the INSTANCE root has the config');
|
|
98
99
|
const body = await res.json();
|
|
99
|
-
assert.equal(body.version,
|
|
100
|
-
assert.match(body.assets['mac-arm64'], /^https:\/\/github\.com\/example-owner\/example-
|
|
100
|
+
assert.equal(body.version, CLI_CFG.version);
|
|
101
|
+
assert.match(body.assets['mac-arm64'], /^https:\/\/github\.com\/example-owner\/example-cli\//);
|
|
101
102
|
} finally {
|
|
102
103
|
await new Promise((r) => server.close(r));
|
|
103
104
|
}
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
// So these tests assert the FALLBACK path fires — a happy-path test on the new name alone would
|
|
13
13
|
// pass just as well with the back-compat deleted, which is exactly the regression that matters.
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
// the canonical one (dependency-free middleware,
|
|
17
|
-
//
|
|
15
|
+
// Some of the read-lists are DUPLICATED rather than imported, because their modules cannot reach
|
|
16
|
+
// the canonical one (dependency-free middleware, static browser surfaces). Those copies are
|
|
17
|
+
// pinned here against the real list by parsing the source — an unpinned copy is how a rename
|
|
18
18
|
// half-lands.
|
|
19
19
|
|
|
20
20
|
import test from 'node:test';
|
|
@@ -235,6 +235,11 @@ test('routes/auth.js sets ONLY the head name and derives the clear-list from the
|
|
|
235
235
|
});
|
|
236
236
|
|
|
237
237
|
// ── 5. the duplicated copies are pinned to the canonical lists ──────────────────────────────
|
|
238
|
+
//
|
|
239
|
+
// There used to be a third copy here and a whole section 7 exercising it: the Dev Box Electron
|
|
240
|
+
// app shipped as its own bundle, so it could not import instance-config and carried its own
|
|
241
|
+
// read-list. Task 1003891 deleted that app, so the copy it was pinned against no longer exists
|
|
242
|
+
// and the cases went with it — there is nothing left to drift.
|
|
238
243
|
|
|
239
244
|
test('rate-limit.js — its dependency-free copy of the cookie names matches the real list', () => {
|
|
240
245
|
const src = read('src/bongos/middleware/rate-limit.js');
|
|
@@ -245,19 +250,6 @@ test('rate-limit.js — its dependency-free copy of the cookie names matches the
|
|
|
245
250
|
'the limiter’s copy drifted from src/bongos/auth.js SESSION_COOKIE_NAMES');
|
|
246
251
|
});
|
|
247
252
|
|
|
248
|
-
test('dev-box session-store — its bundled copy of the filenames matches the real list', () => {
|
|
249
|
-
const src = read('modules/dev-box/app/src/session-store.js');
|
|
250
|
-
const head = /const FILE = path\.join\(DIR, '([^']+)'\)/.exec(src);
|
|
251
|
-
assert.ok(head, 'FILE not found in the dev-box session store');
|
|
252
|
-
const legacy = /const FILES_LEGACY = \[([^\]]*)\]/.exec(src);
|
|
253
|
-
assert.ok(legacy, 'FILES_LEGACY not found — the app would stop reading un-migrated sessions');
|
|
254
|
-
const names = [head[1], ...[...legacy[1].matchAll(/'([^']+\.json)'/g)].map((x) => x[1])];
|
|
255
|
-
assert.deepEqual(names, ic.SESSION_FILENAMES,
|
|
256
|
-
'the dev-box app’s copy drifted from src/instance-config.js SESSION_FILENAMES');
|
|
257
|
-
// It must WRITE only the head name.
|
|
258
|
-
assert.doesNotMatch(src, /writeFileSync\(\s*FILES_LEGACY/, 'the app must never write a legacy name');
|
|
259
|
-
});
|
|
260
|
-
|
|
261
253
|
test('the browser logout clears every session-cookie name', () => {
|
|
262
254
|
// Three static surfaces expire the cookie client-side. One that misses the current name leaves a
|
|
263
255
|
// live cookie behind after "sign out".
|
|
@@ -288,80 +280,6 @@ test('the baked box scripts scan BOTH session filenames', () => {
|
|
|
288
280
|
}
|
|
289
281
|
});
|
|
290
282
|
|
|
291
|
-
// ── 7. the dev-box app's own copy of the fallback, and its sign-out ─────────────────────────
|
|
292
|
-
//
|
|
293
|
-
// The Electron app shares the CLI's session file on purpose (ADR 0045). It ships as its own
|
|
294
|
-
// bundle, so it cannot import instance-config and carries its own read-list — which means its
|
|
295
|
-
// fallback needs exercising, not just pinning.
|
|
296
|
-
function inDevBoxSandbox(body) {
|
|
297
|
-
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'bongos-devbox-'));
|
|
298
|
-
try {
|
|
299
|
-
const code = `const R=${JSON.stringify(REPO_ROOT)};
|
|
300
|
-
const SANDBOX=${JSON.stringify(home)};
|
|
301
|
-
const os=require('node:os');
|
|
302
|
-
if (os.homedir() !== SANDBOX) {
|
|
303
|
-
console.error('ERR sandbox-escape: os.homedir()=' + os.homedir() + ' but the sandbox is ' + SANDBOX);
|
|
304
|
-
process.exit(2);
|
|
305
|
-
}
|
|
306
|
-
const fs=require('node:fs'), path=require('node:path');
|
|
307
|
-
const brand=require(R+'/modules/dev-box/app/src/brand.js');
|
|
308
|
-
const CFG=path.join(os.homedir(), '.config', brand.CONFIG_DIR);
|
|
309
|
-
const store=require(R+'/modules/dev-box/app/src/session-store.js');
|
|
310
|
-
const w=(name,obj)=>{fs.mkdirSync(CFG,{recursive:true});fs.writeFileSync(path.join(CFG,name),JSON.stringify(obj,null,2));};
|
|
311
|
-
const has=(name)=>fs.existsSync(path.join(CFG,name));
|
|
312
|
-
const j=(name)=>JSON.parse(fs.readFileSync(path.join(CFG,name),'utf8'));
|
|
313
|
-
(async()=>{ ${body} })().catch((e)=>{ console.error('ERR '+e.message); process.exit(1); });`;
|
|
314
|
-
const r = spawnSync(process.execPath, ['-e', code], { encoding: 'utf8', env: sandboxEnv(home) });
|
|
315
|
-
return { out: `${r.stdout || ''}${r.stderr || ''}`, status: r.status };
|
|
316
|
-
} finally {
|
|
317
|
-
fs.rmSync(home, { recursive: true, force: true });
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
test('dev-box: reads an old-name session, and writes migrate to the new name', () => {
|
|
322
|
-
const { out, status } = inDevBoxSandbox(`
|
|
323
|
-
w('gds-session.json', { token:'TOK-OLD', api_base:'https://x.example.com', gemini_api_key:'KEEP' });
|
|
324
|
-
const got = store.read();
|
|
325
|
-
store.write({ token:'TOK-NEW', apiBase:'https://x.example.com' });
|
|
326
|
-
console.log(JSON.stringify({
|
|
327
|
-
readOld: got && got.token,
|
|
328
|
-
newFile: has('bongos-session.json') && j('bongos-session.json').token,
|
|
329
|
-
keptExtra: has('bongos-session.json') && j('bongos-session.json').gemini_api_key,
|
|
330
|
-
oldKept: has('gds-session.json'),
|
|
331
|
-
}));`);
|
|
332
|
-
assert.equal(status, 0, out);
|
|
333
|
-
const r = JSON.parse(out.trim().split('\n').pop());
|
|
334
|
-
assert.equal(r.readOld, 'TOK-OLD', 'the app must still read an un-migrated session');
|
|
335
|
-
assert.equal(r.newFile, 'TOK-NEW', 'a write must land on the new name');
|
|
336
|
-
assert.equal(r.keptExtra, 'KEEP', 'merge-preserving write must carry unknown fields across the rename');
|
|
337
|
-
assert.equal(r.oldKept, true, 'the app must not delete the old file either');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test('dev-box: sign-out strips the credential from EVERY accepted filename', () => {
|
|
341
|
-
// THE BUG THIS PINS. clear() rewrote only the CURRENT filename, but read() falls through to an
|
|
342
|
-
// older name when the current file carries no token — so sign-out left a live token in the
|
|
343
|
-
// superseded file and the very next read signed the builder straight back in. Harmless while
|
|
344
|
-
// the only legacy name was the long-dead pms-session.json; the common case the moment
|
|
345
|
-
// gds-session.json joined the read-list (task 1003704).
|
|
346
|
-
const { out, status } = inDevBoxSandbox(`
|
|
347
|
-
w('bongos-session.json', { token:'TOK-NEW', api_base:'https://x.example.com' });
|
|
348
|
-
w('gds-session.json', { token:'TOK-OLD', api_base:'https://x.example.com', gemini_api_key:'KEEP' });
|
|
349
|
-
store.clear();
|
|
350
|
-
console.log(JSON.stringify({
|
|
351
|
-
stillSignedIn: !!store.read(),
|
|
352
|
-
newToken: j('bongos-session.json').token ?? null,
|
|
353
|
-
oldToken: j('gds-session.json').token ?? null,
|
|
354
|
-
keptExtra: j('gds-session.json').gemini_api_key,
|
|
355
|
-
}));`);
|
|
356
|
-
assert.equal(status, 0, out);
|
|
357
|
-
const r = JSON.parse(out.trim().split('\n').pop());
|
|
358
|
-
assert.equal(r.stillSignedIn, false, 'sign-out must not leave a readable session under ANY name');
|
|
359
|
-
assert.equal(r.newToken, null, 'the current file must lose its token');
|
|
360
|
-
assert.equal(r.oldToken, null, 'the superseded file must lose its token too — this is the bug');
|
|
361
|
-
// Sign-out drops credentials only; CLI-adjacent config must survive in every file it touches.
|
|
362
|
-
assert.equal(r.keptExtra, 'KEEP', 'clear() must preserve non-credential fields');
|
|
363
|
-
});
|
|
364
|
-
|
|
365
283
|
// ── 8. the credential detector follows the rename ───────────────────────────────────────────
|
|
366
284
|
|
|
367
285
|
test('the artifact-scan credential detector flags a read of EVERY session filename', () => {
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# OTB Dev Box — the desktop switch for a builder's hosted box
|
|
2
|
-
|
|
3
|
-
A menu-bar (macOS) / system-tray (Windows) app that turns a builder's hosted
|
|
4
|
-
dev box on and off with one switch. Built for the newcomer who should never
|
|
5
|
-
see a terminal: sign in with browser clicks (app pairing, ADR 0045), flip the
|
|
6
|
-
switch, and connect from Claude Desktop or the browser terminal.
|
|
7
|
-
|
|
8
|
-
What it does beyond the switch — each fixing a failure observed in the
|
|
9
|
-
2026-06-10 onboarding sessions that killed the old `curl | bash` flow:
|
|
10
|
-
|
|
11
|
-
- **Pairing sign-in** — opens the builders' hall, one Approve click, token
|
|
12
|
-
delivered over the app's own secret channel (`/api/bongos/auth/app-pair/*`).
|
|
13
|
-
The session is written to `~/.config/otb/gds-session.json`, the same file
|
|
14
|
-
the `/builder-*` CLI uses — pairing the app signs in the CLI too.
|
|
15
|
-
- **SSH key done right** — generates `~/.ssh/otb_builder` (ed25519) with real
|
|
16
|
-
600 perms (icacls ACL on Windows) and registers it via `POST /box/ssh-key`.
|
|
17
|
-
- **known_hosts kept fresh** — Claude Desktop's embedded ssh2 reads ONLY
|
|
18
|
-
`~/.ssh/known_hosts`, and host keys change on every re-provision; the app
|
|
19
|
-
re-scans and replaces entries each time the box turns active.
|
|
20
|
-
- **Claude Desktop connection file** — installs the managed-settings file to
|
|
21
|
-
the system path with a native elevation prompt (osascript / UAC), the step
|
|
22
|
-
the agent-driven flow could never finish.
|
|
23
|
-
- **Native notifications** — box ready, rested while idle.
|
|
24
|
-
- **No heartbeats** — the app never sends `claude_active`; the droplet's idle
|
|
25
|
-
sweep stays authoritative so an unused box still reclaims itself (~10 min).
|
|
26
|
-
|
|
27
|
-
## Layout
|
|
28
|
-
|
|
29
|
-
- `src/main.js` — Electron main: tray, window, IPC, wiring.
|
|
30
|
-
- `src/brand.js` — every brand string (ADR 0004: the civ name lives here only).
|
|
31
|
-
- `src/{pairing,box-controller,gds-client,session-store,ssh-setup,updater}.js` — the
|
|
32
|
-
logic, Electron-free and unit-tested in `test/unit.mjs`.
|
|
33
|
-
- `src/updater.js` — in-app update check (#924): semver compare + platform→target
|
|
34
|
-
resolution. `main.js` polls `GET /downloads/devbox/version` on open + every 12h
|
|
35
|
-
and, when the published release is strictly newer, shows the Update banner; the
|
|
36
|
-
Update button downloads the installer and hands it to the OS (browser-download
|
|
37
|
-
fallback on any error). **Bumping `config/devbox-app.json` version is what makes
|
|
38
|
-
the banner appear in the field.**
|
|
39
|
-
- `renderer/` — the panel UI (sandboxed, contextIsolated; preload exposes
|
|
40
|
-
named verbs only).
|
|
41
|
-
- `scripts/gen-icons.js` — regenerates all icons from pixel grids (`npm run
|
|
42
|
-
icons`; borrows `sharp` from the repo root's node_modules).
|
|
43
|
-
|
|
44
|
-
## Develop
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
cd devbox-app
|
|
48
|
-
npm install
|
|
49
|
-
npm test # hermetic unit tests, no Electron needed
|
|
50
|
-
npm start # run against prod (or OTB_DEVBOX_API_BASE=http://… npm start)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Dev/test env hooks: `OTB_DEVBOX_API_BASE` (point at a stub/staging GDS),
|
|
54
|
-
`OTB_DEVBOX_NO_ELEVATE=1` (skip the managed-settings elevation prompt),
|
|
55
|
-
`OTB_DEVBOX_SCREENSHOT_DIR` (write a PNG of the panel on every state change —
|
|
56
|
-
how CI/verification captures proof).
|
|
57
|
-
|
|
58
|
-
## Build & release
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
npm run dist:mac # dmg, arm64 + x64 (unsigned → ad-hoc)
|
|
62
|
-
npm run dist:win # NSIS one-click installer, x64 + arm64
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
CI builds both on every push that touches `devbox-app/**`
|
|
66
|
-
(`.github/workflows/build-devbox-app.yml`) and uploads them as artifacts.
|
|
67
|
-
Publishing = attach the artifacts to a release on the PUBLIC releases repo
|
|
68
|
-
named in `config/devbox-app.json`, then bump that config and ship — the
|
|
69
|
-
branded `/downloads/devbox/*` redirects follow the config. (The long-form
|
|
70
|
-
release recipe was retired with the other published dev-box recipes —
|
|
71
|
-
task 1004013, ADR 0305.)
|
|
72
|
-
|
|
73
|
-
This subproject is self-contained on purpose: its `package.json` never feeds
|
|
74
|
-
the droplet's `npm ci`, and nothing under `devbox-app/` runs server-side.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# TEMPLATE — rendered to electron-builder.yml by scripts/gen-brand.js from
|
|
2
|
-
# config/branding.json (@@TOKENS@@ are substituted; ${version}/${arch}/${ext}
|
|
3
|
-
# are electron-builder's own and stay literal). Edit THIS file, never the
|
|
4
|
-
# generated electron-builder.yml.
|
|
5
|
-
#
|
|
6
|
-
# Brand strings (appId, productName, copyright, artifact slug) come from the
|
|
7
|
-
# instance branding so one source tree builds any instance's app (ADR 0004,
|
|
8
|
-
# generalised). Signing rides the instance's own CI (main-ref-gated Apple
|
|
9
|
-
# Developer ID; ADR 0072) — an unarmed build ad-hoc-signs the mac app, which
|
|
10
|
-
# arm64 requires to launch at all.
|
|
11
|
-
#
|
|
12
|
-
# Artifact names are pinned (no spaces, no world name inside — ADR 0004) and
|
|
13
|
-
# MUST match config/devbox-app.json's assets map; tests/devbox_downloads.mjs
|
|
14
|
-
# cross-checks the config side.
|
|
15
|
-
|
|
16
|
-
appId: @@APP_ID@@
|
|
17
|
-
productName: @@PRODUCT_NAME@@
|
|
18
|
-
copyright: @@COPYRIGHT@@
|
|
19
|
-
|
|
20
|
-
# buildResources is named build-resources (not electron-builder's default
|
|
21
|
-
# `build`) because the repo root .gitignore ignores build/ everywhere and the
|
|
22
|
-
# app icon in here is COMMITTED source, not output.
|
|
23
|
-
directories:
|
|
24
|
-
buildResources: build-resources
|
|
25
|
-
output: dist
|
|
26
|
-
|
|
27
|
-
files:
|
|
28
|
-
- src/**/*
|
|
29
|
-
- renderer/**/*
|
|
30
|
-
- assets/**/*
|
|
31
|
-
- package.json
|
|
32
|
-
|
|
33
|
-
asar: true
|
|
34
|
-
|
|
35
|
-
mac:
|
|
36
|
-
category: public.app-category.developer-tools
|
|
37
|
-
icon: build-resources/icon.png
|
|
38
|
-
target:
|
|
39
|
-
- target: dmg
|
|
40
|
-
arch: [arm64, x64]
|
|
41
|
-
artifactName: @@PRODUCT_SLUG@@-${version}-${arch}.${ext}
|
|
42
|
-
|
|
43
|
-
dmg:
|
|
44
|
-
artifactName: @@PRODUCT_SLUG@@-${version}-${arch}.${ext}
|
|
45
|
-
# On-brand install window (task 912). The backdrop
|
|
46
|
-
# (build-resources/dmg-background.png + @2x, drawn by
|
|
47
|
-
# scripts/gen-dmg-backdrop.py) guides the drag from the container (the app)
|
|
48
|
-
# into Applications. iconSize + window + the contents coords MUST match
|
|
49
|
-
# the icon-well centers the backdrop draws (LEFT 180,198 / RIGHT 480,198) — if
|
|
50
|
-
# you move one, re-run the generator. The volume icon is the app icon (the
|
|
51
|
-
# container). electron-builder auto-picks dmg-background@2x.png on Retina.
|
|
52
|
-
#
|
|
53
|
-
# task 1235: this electron-builder DMG is only an INTERMEDIATE. Its window
|
|
54
|
-
# backdrop does NOT render on macOS >= Sonoma — electron-builder writes a
|
|
55
|
-
# `pBBk` bookmark Finder can't resolve, so the install window shows up white.
|
|
56
|
-
# The CI step "Apply install-window backdrop (dmgbuild)" re-packages the
|
|
57
|
-
# signed+notarized .app out of this DMG into the FINAL DMG via dmgbuild
|
|
58
|
-
# (scripts/apply-dmg-backdrop.sh + scripts/dmgbuild-settings.py), which writes
|
|
59
|
-
# an alias-only .DS_Store that DOES render. Keep the coords below in sync with
|
|
60
|
-
# dmgbuild-settings.py (currently identical: 180,198 / 480,198, 660x400, size 100).
|
|
61
|
-
icon: build-resources/icon.png
|
|
62
|
-
background: build-resources/dmg-background.png
|
|
63
|
-
iconSize: 100
|
|
64
|
-
window:
|
|
65
|
-
width: 660
|
|
66
|
-
height: 400
|
|
67
|
-
contents:
|
|
68
|
-
- x: 180
|
|
69
|
-
y: 198
|
|
70
|
-
type: file
|
|
71
|
-
- x: 480
|
|
72
|
-
y: 198
|
|
73
|
-
type: link
|
|
74
|
-
path: /Applications
|
|
75
|
-
|
|
76
|
-
win:
|
|
77
|
-
icon: build-resources/icon.png
|
|
78
|
-
target:
|
|
79
|
-
- target: nsis
|
|
80
|
-
# x64 covers Intel/AMD PCs; arm64 is native for ARM Windows (Surface,
|
|
81
|
-
# Snapdragon laptops, Parallels VMs on Apple-silicon Macs). x64 also
|
|
82
|
-
# RUNS on arm64 via Windows emulation, so x64 stays the safe default
|
|
83
|
-
# link; arm64 is the first-class build for those machines (#908).
|
|
84
|
-
arch: [x64, arm64]
|
|
85
|
-
|
|
86
|
-
nsis:
|
|
87
|
-
oneClick: true
|
|
88
|
-
perMachine: false
|
|
89
|
-
runAfterFinish: true
|
|
90
|
-
deleteAppDataOnUninstall: false
|
|
91
|
-
# ${arch} suffix added in #908 when arm64 joined — two arches can't share
|
|
92
|
-
# one filename. Must stay in lockstep with config/devbox-app.json assets.
|
|
93
|
-
artifactName: @@PRODUCT_SLUG@@-Setup-${version}-${arch}.${ext}
|
|
94
|
-
|
|
95
|
-
publish: null
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# TEMPLATE — rendered to electron-builder.yml by scripts/gen-brand.js from
|
|
2
|
-
# config/branding.json (@@TOKENS@@ are substituted; ${version}/${arch}/${ext}
|
|
3
|
-
# are electron-builder's own and stay literal). Edit THIS file, never the
|
|
4
|
-
# generated electron-builder.yml.
|
|
5
|
-
#
|
|
6
|
-
# Brand strings (appId, productName, copyright, artifact slug) come from the
|
|
7
|
-
# instance branding so one source tree builds any instance's app (ADR 0004,
|
|
8
|
-
# generalised). Signing rides the instance's own CI (main-ref-gated Apple
|
|
9
|
-
# Developer ID; ADR 0072) — an unarmed build ad-hoc-signs the mac app, which
|
|
10
|
-
# arm64 requires to launch at all.
|
|
11
|
-
#
|
|
12
|
-
# Artifact names are pinned (no spaces, no world name inside — ADR 0004) and
|
|
13
|
-
# MUST match config/devbox-app.json's assets map; tests/devbox_downloads.mjs
|
|
14
|
-
# cross-checks the config side.
|
|
15
|
-
|
|
16
|
-
appId: com.cloudbongos.devbox
|
|
17
|
-
productName: Cloud Bongos Dev Box
|
|
18
|
-
copyright: © Cloud Bongos
|
|
19
|
-
|
|
20
|
-
# buildResources is named build-resources (not electron-builder's default
|
|
21
|
-
# `build`) because the repo root .gitignore ignores build/ everywhere and the
|
|
22
|
-
# app icon in here is COMMITTED source, not output.
|
|
23
|
-
directories:
|
|
24
|
-
buildResources: build-resources
|
|
25
|
-
output: dist
|
|
26
|
-
|
|
27
|
-
files:
|
|
28
|
-
- src/**/*
|
|
29
|
-
- renderer/**/*
|
|
30
|
-
- assets/**/*
|
|
31
|
-
- package.json
|
|
32
|
-
|
|
33
|
-
asar: true
|
|
34
|
-
|
|
35
|
-
mac:
|
|
36
|
-
category: public.app-category.developer-tools
|
|
37
|
-
icon: build-resources/icon.png
|
|
38
|
-
target:
|
|
39
|
-
- target: dmg
|
|
40
|
-
arch: [arm64, x64]
|
|
41
|
-
artifactName: Cloud-Bongos-Dev-Box-${version}-${arch}.${ext}
|
|
42
|
-
|
|
43
|
-
dmg:
|
|
44
|
-
artifactName: Cloud-Bongos-Dev-Box-${version}-${arch}.${ext}
|
|
45
|
-
# On-brand install window (task 912). The backdrop
|
|
46
|
-
# (build-resources/dmg-background.png + @2x, drawn by
|
|
47
|
-
# scripts/gen-dmg-backdrop.py) guides the drag from the container (the app)
|
|
48
|
-
# into Applications. iconSize + window + the contents coords MUST match
|
|
49
|
-
# the icon-well centers the backdrop draws (LEFT 180,198 / RIGHT 480,198) — if
|
|
50
|
-
# you move one, re-run the generator. The volume icon is the app icon (the
|
|
51
|
-
# container). electron-builder auto-picks dmg-background@2x.png on Retina.
|
|
52
|
-
#
|
|
53
|
-
# task 1235: this electron-builder DMG is only an INTERMEDIATE. Its window
|
|
54
|
-
# backdrop does NOT render on macOS >= Sonoma — electron-builder writes a
|
|
55
|
-
# `pBBk` bookmark Finder can't resolve, so the install window shows up white.
|
|
56
|
-
# The CI step "Apply install-window backdrop (dmgbuild)" re-packages the
|
|
57
|
-
# signed+notarized .app out of this DMG into the FINAL DMG via dmgbuild
|
|
58
|
-
# (scripts/apply-dmg-backdrop.sh + scripts/dmgbuild-settings.py), which writes
|
|
59
|
-
# an alias-only .DS_Store that DOES render. Keep the coords below in sync with
|
|
60
|
-
# dmgbuild-settings.py (currently identical: 180,198 / 480,198, 660x400, size 100).
|
|
61
|
-
icon: build-resources/icon.png
|
|
62
|
-
background: build-resources/dmg-background.png
|
|
63
|
-
iconSize: 100
|
|
64
|
-
window:
|
|
65
|
-
width: 660
|
|
66
|
-
height: 400
|
|
67
|
-
contents:
|
|
68
|
-
- x: 180
|
|
69
|
-
y: 198
|
|
70
|
-
type: file
|
|
71
|
-
- x: 480
|
|
72
|
-
y: 198
|
|
73
|
-
type: link
|
|
74
|
-
path: /Applications
|
|
75
|
-
|
|
76
|
-
win:
|
|
77
|
-
icon: build-resources/icon.png
|
|
78
|
-
target:
|
|
79
|
-
- target: nsis
|
|
80
|
-
# x64 covers Intel/AMD PCs; arm64 is native for ARM Windows (Surface,
|
|
81
|
-
# Snapdragon laptops, Parallels VMs on Apple-silicon Macs). x64 also
|
|
82
|
-
# RUNS on arm64 via Windows emulation, so x64 stays the safe default
|
|
83
|
-
# link; arm64 is the first-class build for those machines (#908).
|
|
84
|
-
arch: [x64, arm64]
|
|
85
|
-
|
|
86
|
-
nsis:
|
|
87
|
-
oneClick: true
|
|
88
|
-
perMachine: false
|
|
89
|
-
runAfterFinish: true
|
|
90
|
-
deleteAppDataOnUninstall: false
|
|
91
|
-
# ${arch} suffix added in #908 when arm64 joined — two arches can't share
|
|
92
|
-
# one filename. Must stay in lockstep with config/devbox-app.json assets.
|
|
93
|
-
artifactName: Cloud-Bongos-Dev-Box-Setup-${version}-${arch}.${ext}
|
|
94
|
-
|
|
95
|
-
publish: null
|