@bongos/core 1.19.726 → 1.19.728
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 +177 -492
- package/.devcontainer/Dockerfile +1 -1
- package/.devcontainer/devcontainer.json +2 -2
- package/.devcontainer/healthcheck.sh +1 -1
- package/.devcontainer/init-firewall.sh +1 -1
- package/.devcontainer/post-create.sh +2 -2
- package/.env.local.example +1 -1
- package/Dockerfile +1 -1
- package/docs/copy-inventory.md +1 -1
- package/docs/copy-registry.json +9 -9
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/devbox-app-release.md +4 -4
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +5 -5
- package/modules/dev-box/app/scripts/gen-brand.js +1 -1
- package/modules/dev-box/app/src/brand.js +1 -1
- package/modules/dev-box/app/test/unit.mjs +16 -16
- package/modules/dev-box/box-access.js +1 -1
- package/modules/dev-box/routes/box.js +1 -1
- package/modules/discord/discord-approvals.js +1 -1
- package/modules/grading/grader-model.js +1 -1
- package/modules/grading/grader-prompt.js +1 -1
- package/modules/grading/grader-workers/quality.js +1 -1
- package/modules/hall-ui/module.json +1 -1
- package/modules/hall-ui/public/brand-apply.js +1 -1
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/lifecycle/conflict-resolve.js +1 -1
- package/modules/lifecycle/db-analytics.js +1 -1
- package/modules/lifecycle/routes/analytics.js +1 -1
- package/modules/onboarding/approval-broadcast.js +1 -1
- package/modules/onboarding/onboarding.js +1 -1
- package/modules/provisioning/tests/liveness.mjs +1 -1
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects-floor.states.json +2 -2
- package/modules/public-landing/public/projects.probes.json +4 -4
- package/modules/public-landing/public/projects.states.json +2 -2
- package/modules/status-ui/module.json +1 -1
- package/modules/status-ui/public/status.js +4 -4
- package/modules/status-ui/public/style.css +1 -1
- package/modules/ui-design/kit/fixtures/cost-summary.json +1 -1
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
- package/modules/ui-design/kit/fixtures/featured.json +1 -1
- package/modules/ui-design/kit/fixtures/leaderboard.json +1 -1
- package/modules/ui-design/kit/fixtures/my-projects.json +1 -1
- package/modules/ui-design/kit/fixtures/progress.json +1 -1
- package/modules/ui-design/kit/fixtures/recent-shipped.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/cron/style_guide_audit.sh +1 -1
- package/scripts/discord/lib.js +1 -1
- package/scripts/discord/post.js +1 -1
- package/scripts/gds/audit-authorship.sh +1 -1
- package/scripts/gds/audit-replay.sh +29 -2
- package/scripts/gds/audit-ships.js +1 -1
- package/scripts/gds/backfill-sessions.js +2 -2
- package/scripts/gds/cf-tunnel.js +4 -4
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +6 -6
- package/scripts/gds/cutover-codemod.js +1 -1
- package/scripts/gds/dev-lib.js +2 -2
- package/scripts/gds/diagnose-achievements.js +2 -2
- package/scripts/gds/fitness-checks-identity.js +6 -4
- package/scripts/gds/gen-diagrams.js +1 -1
- package/scripts/gds/leak-scan-allowlist.js +293 -0
- package/scripts/gds/learning-capture.js +1 -1
- package/scripts/gds/linkify-refs.js +2 -2
- package/scripts/gds/local-preview-lib.js +1 -1
- package/scripts/gds/mirror-lineage-check.js +1 -1
- package/scripts/gds/mirror-redact.js +17 -2
- package/scripts/gds/newcomer-floor.templates.json +1 -1
- package/scripts/gds/package-core.js +18 -1
- package/scripts/gds/paste-token.js +1 -1
- package/scripts/gds/provision-units.js +2 -2
- package/scripts/gds/publish-manifest.js +15 -0
- package/scripts/gds/repo-metrics.js +1 -1
- package/scripts/gds/sandbox-stage.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +1 -1
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +1 -1
- package/scripts/gds/seed-bongos-tasks.js +2 -2
- package/scripts/gds/seed-gds-v4-tasks.js +1 -1
- package/scripts/gds/seed-medusa-tasks.js +10 -10
- package/scripts/gds/seed-module-upstreaming.js +1 -1
- package/scripts/gds/seed-productization-tasks.js +6 -6
- package/scripts/gds/seed-provisioning-tasks.js +2 -2
- package/scripts/gds/seed-staging.sh +19 -6
- package/scripts/gds/ship-deploy-target.js +1 -1
- package/scripts/gds/ship-flow.js +1 -1
- package/scripts/gds/smoke-gds.js +1 -1
- package/scripts/status-probe.mjs +2 -2
- package/src/bongos/auth-discord.js +2 -2
- package/src/bongos/live-channel.js +1 -1
- package/src/bongos/pool.js +1 -1
- package/src/bongos/routes/auth.js +11 -11
- package/src/bongos/routes/security.js +1 -1
- package/src/bongos/routes.js +1 -1
- package/src/branding.js +1 -1
- package/src/devbox-downloads.js +1 -1
- package/src/instance-config.js +2 -2
- package/src/module-api.js +1 -1
- package/src/module-loader/provenance.js +2 -2
- package/tests/art_stage.mjs +2 -2
- package/tests/auth_anthropic_identity.mjs +1 -1
- package/tests/auth_duplicate_cookie.mjs +1 -1
- package/tests/auth_error_brand.mjs +1 -1
- package/tests/auth_inactive_builder.mjs +1 -1
- package/tests/backup_cli.mjs +4 -4
- package/tests/bongos_box_connect.mjs +2 -2
- package/tests/bongos_downloads.mjs +2 -2
- package/tests/bongos_login.mjs +5 -5
- package/tests/box_connect_e2e.mjs +2 -2
- package/tests/box_dns_repoint.mjs +10 -10
- package/tests/box_host_keys.mjs +1 -1
- package/tests/box_onboard.mjs +9 -9
- package/tests/box_scope_session.mjs +1 -1
- package/tests/box_terminal_ssrf.mjs +7 -7
- package/tests/boxes.mjs +9 -9
- package/tests/branding.mjs +2 -2
- package/tests/bump_version.mjs +2 -2
- package/tests/cf_tunnel.mjs +40 -40
- package/tests/cli_package.mjs +40 -1
- package/tests/cli_token_reissue.mjs +7 -7
- package/tests/cloud_bongos_pack.mjs +1 -1
- package/tests/currency_label.mjs +8 -8
- package/tests/dev_lib.mjs +4 -4
- package/tests/devbox_dmg_backdrop.mjs +1 -1
- package/tests/devbox_downloads.mjs +2 -2
- package/tests/devbox_icons.mjs +1 -1
- package/tests/discord_approvals.mjs +1 -1
- package/tests/discord_oauth.mjs +2 -2
- package/tests/executed_closure.mjs +6 -1
- package/tests/first_admin_bootstrap.mjs +1 -1
- package/tests/gate_approvals.mjs +5 -5
- package/tests/grade_aggregates_unavailable.mjs +1 -1
- package/tests/hook_user_agents.mjs +2 -2
- package/tests/init.mjs +6 -6
- package/tests/instance_config.mjs +1 -1
- package/tests/instance_manifest.mjs +5 -5
- package/tests/instance_mismatch_guard.mjs +13 -13
- package/tests/leak_scan_allowlist.mjs +94 -0
- package/tests/migrate_root_resolution.mjs +4 -4
- package/tests/mirror_redact.mjs +28 -4
- package/tests/module_catalog.mjs +5 -5
- package/tests/module_submissions.mjs +3 -3
- package/tests/package_core.mjs +1 -1
- package/tests/parallel-load.mjs +6 -6
- package/tests/projects_hub.mjs +2 -2
- package/tests/provision.mjs +50 -50
- package/tests/provisioning_github_app_routes.mjs +1 -1
- package/tests/provisioning_onboard_mode.mjs +2 -2
- package/tests/publish_reconciler.mjs +4 -4
- package/tests/sandbox_stage.mjs +1 -1
- package/tests/scrubber_corpus.mjs +3 -3
- package/tests/search_isolation.mjs +1 -1
- package/tests/session_records.mjs +1 -1
- package/tests/ship_card.mjs +6 -6
- package/tests/ship_ci_deploy.mjs +1 -1
- package/tests/single_host_hall.mjs +9 -9
- package/tests/smoke_preflight.mjs +1 -1
- package/tests/smoke_target.mjs +1 -1
- package/tests/status-probe.mjs +1 -1
- package/tests/status_mirror_sync.mjs +1 -1
- package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +0 -45
- package/docs/design/mocks/_schemes.css +0 -604
- package/docs/design/mocks/component-sheet.html +0 -428
- package/docs/design/mocks/hall/_tokens.css +0 -370
- package/docs/design/mocks/hall/board/v1.html +0 -846
- package/docs/design/mocks/hall/board/v2.html +0 -858
- package/docs/design/mocks/hall/board/v3.html +0 -884
- package/docs/design/mocks/hall/goals/v1.html +0 -1212
- package/docs/design/mocks/hall/goals/v2.html +0 -1192
- package/docs/design/mocks/hall/goals/v3.html +0 -1205
- package/docs/design/mocks/hall/hall-sheet.html +0 -244
- package/docs/design/mocks/hall/reading/v1.html +0 -1125
- package/docs/design/mocks/hall/reading/v2.html +0 -1177
- package/docs/design/mocks/hall/reading/v3.html +0 -1150
- package/docs/design/mocks/hall/settings/v1.html +0 -1238
- package/docs/design/mocks/hall/settings/v2.html +0 -1235
- package/docs/design/mocks/hall/settings/v3.html +0 -1299
- package/docs/design/mocks/hall/task/v1.html +0 -633
- package/docs/design/mocks/hall/task/v2.html +0 -646
- package/docs/design/mocks/hall/task/v3.html +0 -647
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/hub-sheet.html +0 -192
- package/docs/design/mocks/hub/mine.html +0 -465
- package/docs/design/mocks/hub/picker.html +0 -421
- package/docs/design/mocks/hub/planets.html +0 -356
- package/docs/design/mocks/hub/v1.html +0 -1209
- package/docs/design/mocks/hub/v2.html +0 -1257
- package/docs/design/mocks/hub/v3.html +0 -1496
- package/docs/design/mocks/hub/wizard.html +0 -421
- package/docs/design/mocks/landing/landing-sheet.html +0 -131
- package/docs/design/mocks/landing/v1.html +0 -956
- package/docs/design/mocks/landing/v2.html +0 -708
- package/docs/design/mocks/landing/v3.html +0 -911
- package/docs/design/mocks/landing-page-mock.html +0 -687
- package/docs/design/mocks/landing-palettes.html +0 -103
- package/docs/design/mocks/projects-hub-mock.html +0 -991
- package/docs/design/mocks/projects-hub-variants.html +0 -146
- package/docs/design/mocks/projects-page-mock.html +0 -167
- package/docs/design/mocks/world/blue-hud/_tokens.css +0 -246
- package/docs/design/mocks/world/blue-hud/hall.html +0 -595
- package/docs/design/mocks/world/blue-hud/landing.html +0 -424
- package/docs/design/mocks/world/blue-hud/status.html +0 -410
- package/docs/design/mocks/world/chrome-blobs/_tokens.css +0 -479
- package/docs/design/mocks/world/chrome-blobs/hall.html +0 -553
- package/docs/design/mocks/world/chrome-blobs/landing-b.html +0 -581
- package/docs/design/mocks/world/chrome-blobs/landing.html +0 -328
- package/docs/design/mocks/world/chrome-blobs/status.html +0 -416
- package/docs/design/mocks/world/natgeo-editorial/_tokens.css +0 -377
- package/docs/design/mocks/world/natgeo-editorial/hall.html +0 -649
- package/docs/design/mocks/world/natgeo-editorial/landing-b.html +0 -743
- package/docs/design/mocks/world/natgeo-editorial/landing.html +0 -475
- package/docs/design/mocks/world/natgeo-editorial/status.html +0 -437
- package/docs/design/mocks/world/organic-cards/_tokens.css +0 -462
- package/docs/design/mocks/world/organic-cards/hall.html +0 -625
- package/docs/design/mocks/world/organic-cards/landing-b.html +0 -736
- package/docs/design/mocks/world/organic-cards/landing.html +0 -420
- package/docs/design/mocks/world/organic-cards/status.html +0 -458
- package/docs/design/mocks/world/world-sheet.html +0 -156
package/tests/cf_tunnel.mjs
CHANGED
|
@@ -77,9 +77,9 @@ await ta('live requests pass the IPv4 dispatcher through (token is IP-locked, #7
|
|
|
77
77
|
await ta('dry-run never touches the network and returns synthetic results', async () => {
|
|
78
78
|
let hit = false;
|
|
79
79
|
const cf = new CfTunnelApi({ accountId: ACCT, fetchImpl: () => { hit = true; throw new Error('should not fetch'); } });
|
|
80
|
-
const r = await cf.ensureNamedTunnel({ login: 'alice', zone: '
|
|
80
|
+
const r = await cf.ensureNamedTunnel({ login: 'alice', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
81
81
|
assert.equal(hit, false, 'fetch must not be called in dry-run');
|
|
82
|
-
assert.equal(r.hostname, 'term-alice.dev.
|
|
82
|
+
assert.equal(r.hostname, 'term-alice.dev.example.com');
|
|
83
83
|
assert.ok(String(r.token).startsWith('dryrun-token-'));
|
|
84
84
|
assert.ok(String(r.tunnelId).startsWith('dryrun-tunnel-'));
|
|
85
85
|
});
|
|
@@ -117,8 +117,8 @@ await ta('putConfiguration sends ttyd ingress + 404 catch-all', async () => {
|
|
|
117
117
|
'PUT /accounts/acct123/cfd_tunnel/tun1/configurations': ({ body }) => { seen = body; return { config: body.config }; },
|
|
118
118
|
});
|
|
119
119
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
120
|
-
await cf.putConfiguration('tun1', { hostname: 'term-alice.dev.
|
|
121
|
-
assert.deepEqual(seen.config.ingress[0], { hostname: 'term-alice.dev.
|
|
120
|
+
await cf.putConfiguration('tun1', { hostname: 'term-alice.dev.example.com' });
|
|
121
|
+
assert.deepEqual(seen.config.ingress[0], { hostname: 'term-alice.dev.example.com', service: 'http://127.0.0.1:7681' });
|
|
122
122
|
assert.deepEqual(seen.config.ingress[1], { service: 'http_status:404' });
|
|
123
123
|
});
|
|
124
124
|
|
|
@@ -139,7 +139,7 @@ await ta('upsertDnsRoute POSTs a proxied CNAME to cfargotunnel when absent', asy
|
|
|
139
139
|
'POST /zones/zone1/dns_records': ({ body }) => { posted = body; return { id: 'rec1', ...body }; },
|
|
140
140
|
});
|
|
141
141
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
142
|
-
await cf.upsertDnsRoute({ zoneId: 'zone1', name: 'term-alice.dev.
|
|
142
|
+
await cf.upsertDnsRoute({ zoneId: 'zone1', name: 'term-alice.dev.example.com', tunnelId: 'tun1' });
|
|
143
143
|
assert.equal(posted.type, 'CNAME');
|
|
144
144
|
assert.equal(posted.content, 'tun1.cfargotunnel.com');
|
|
145
145
|
assert.equal(posted.proxied, true);
|
|
@@ -148,11 +148,11 @@ await ta('upsertDnsRoute POSTs a proxied CNAME to cfargotunnel when absent', asy
|
|
|
148
148
|
await ta('upsertDnsRoute PUTs (updates) when the record already exists', async () => {
|
|
149
149
|
let method = null;
|
|
150
150
|
const { fetchImpl } = makeFetch({
|
|
151
|
-
'GET /zones/zone1/dns_records': () => [{ id: 'rec-existing', name: 'term-alice.dev.
|
|
151
|
+
'GET /zones/zone1/dns_records': () => [{ id: 'rec-existing', name: 'term-alice.dev.example.com' }],
|
|
152
152
|
'PUT /zones/zone1/dns_records/rec-existing': ({ body }) => { method = 'PUT'; return { id: 'rec-existing', ...body }; },
|
|
153
153
|
});
|
|
154
154
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
155
|
-
await cf.upsertDnsRoute({ zoneId: 'zone1', name: 'term-alice.dev.
|
|
155
|
+
await cf.upsertDnsRoute({ zoneId: 'zone1', name: 'term-alice.dev.example.com', tunnelId: 'tun1' });
|
|
156
156
|
assert.equal(method, 'PUT');
|
|
157
157
|
});
|
|
158
158
|
|
|
@@ -183,10 +183,10 @@ await ta('ensureNamedTunnel composes create→configure→token→zone→dns and
|
|
|
183
183
|
'POST /zones/zoneZ/dns_records': ({ body }) => ({ id: 'recZ', ...body }),
|
|
184
184
|
});
|
|
185
185
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
186
|
-
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: '
|
|
186
|
+
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
187
187
|
assert.equal(r.tunnelId, 'tunZ');
|
|
188
188
|
assert.equal(r.token, 'tokZ');
|
|
189
|
-
assert.equal(r.hostname, 'term-carol.dev.
|
|
189
|
+
assert.equal(r.hostname, 'term-carol.dev.example.com');
|
|
190
190
|
// the create used the deterministic per-builder name
|
|
191
191
|
assert.ok(calls.some((c) => c.method === 'POST' && c.path.endsWith('/cfd_tunnel') && c.body.name === 'otb-term-carol'));
|
|
192
192
|
});
|
|
@@ -194,14 +194,14 @@ await ta('ensureNamedTunnel composes create→configure→token→zone→dns and
|
|
|
194
194
|
await ta('teardownNamedTunnel deletes DNS + tunnel and never throws', async () => {
|
|
195
195
|
const { fetchImpl, calls } = makeFetch({
|
|
196
196
|
'GET /zones': () => [{ id: 'zoneZ' }],
|
|
197
|
-
'GET /zones/zoneZ/dns_records': () => [{ id: 'recZ', name: 'term-dan.dev.
|
|
197
|
+
'GET /zones/zoneZ/dns_records': () => [{ id: 'recZ', name: 'term-dan.dev.example.com' }],
|
|
198
198
|
'DELETE /zones/zoneZ/dns_records/recZ': () => ({ id: 'recZ' }),
|
|
199
199
|
'GET /accounts/acct123/cfd_tunnel': () => [{ id: 'tunZ', name: 'otb-term-dan' }],
|
|
200
200
|
'DELETE /accounts/acct123/cfd_tunnel/tunZ/connections': () => ({}),
|
|
201
201
|
'DELETE /accounts/acct123/cfd_tunnel/tunZ': () => ({ id: 'tunZ' }),
|
|
202
202
|
});
|
|
203
203
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
204
|
-
const out = await cf.teardownNamedTunnel({ login: 'dan', zone: '
|
|
204
|
+
const out = await cf.teardownNamedTunnel({ login: 'dan', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
205
205
|
assert.equal(out.dnsDeleted, true);
|
|
206
206
|
assert.equal(out.tunnelDeleted, true);
|
|
207
207
|
assert.deepEqual(out.errors, []);
|
|
@@ -215,7 +215,7 @@ await ta('teardownNamedTunnel on a missing tunnel/record is graceful (no throw,
|
|
|
215
215
|
'GET /accounts/acct123/cfd_tunnel': () => [],
|
|
216
216
|
});
|
|
217
217
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
218
|
-
const out = await cf.teardownNamedTunnel({ login: 'ghost', zone: '
|
|
218
|
+
const out = await cf.teardownNamedTunnel({ login: 'ghost', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
219
219
|
assert.equal(out.dnsDeleted, false);
|
|
220
220
|
assert.equal(out.tunnelDeleted, false);
|
|
221
221
|
assert.deepEqual(out.errors, []);
|
|
@@ -227,7 +227,7 @@ await ta('teardown collects errors per-leg without throwing', async () => {
|
|
|
227
227
|
'GET /accounts/acct123/cfd_tunnel': () => [{ id: 'tunZ', name: 'otb-term- err' }],
|
|
228
228
|
});
|
|
229
229
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
230
|
-
const out = await cf.teardownNamedTunnel({ login: 'err', zone: '
|
|
230
|
+
const out = await cf.teardownNamedTunnel({ login: 'err', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
231
231
|
assert.ok(out.errors.some((e) => /dns/.test(e)), 'dns failure recorded');
|
|
232
232
|
});
|
|
233
233
|
|
|
@@ -240,12 +240,12 @@ await ta('putConfiguration inserts extraRoutes between the primary host and the
|
|
|
240
240
|
});
|
|
241
241
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
242
242
|
await cf.putConfiguration('tun1', {
|
|
243
|
-
hostname: 'term-alice.dev.
|
|
244
|
-
extraRoutes: [{ hostname: 'sandbox-alice.dev.
|
|
243
|
+
hostname: 'term-alice.dev.example.com',
|
|
244
|
+
extraRoutes: [{ hostname: 'sandbox-alice.dev.example.com', service: GAME_SERVICE }],
|
|
245
245
|
});
|
|
246
246
|
assert.equal(seen.config.ingress.length, 3, 'ttyd + game + catch-all');
|
|
247
|
-
assert.deepEqual(seen.config.ingress[0], { hostname: 'term-alice.dev.
|
|
248
|
-
assert.deepEqual(seen.config.ingress[1], { hostname: 'sandbox-alice.dev.
|
|
247
|
+
assert.deepEqual(seen.config.ingress[0], { hostname: 'term-alice.dev.example.com', service: 'http://127.0.0.1:7681' });
|
|
248
|
+
assert.deepEqual(seen.config.ingress[1], { hostname: 'sandbox-alice.dev.example.com', service: 'http://127.0.0.1:3000' });
|
|
249
249
|
assert.deepEqual(seen.config.ingress[2], { service: 'http_status:404' }, 'catch-all stays last');
|
|
250
250
|
});
|
|
251
251
|
|
|
@@ -262,15 +262,15 @@ await ta('ensureNamedTunnel adds the game preview route + its own CNAME and retu
|
|
|
262
262
|
'POST /zones/zoneZ/dns_records': ({ body }) => { dnsPosts.push(body.name); return { id: `rec-${body.name}`, ...body }; },
|
|
263
263
|
});
|
|
264
264
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
265
|
-
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: '
|
|
265
|
+
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
266
266
|
// returns the deterministic preview host alongside the terminal host
|
|
267
|
-
assert.equal(r.previewHostname, 'sandbox-carol.dev.
|
|
268
|
-
assert.equal(r.hostname, 'term-carol.dev.
|
|
267
|
+
assert.equal(r.previewHostname, 'sandbox-carol.dev.example.com');
|
|
268
|
+
assert.equal(r.hostname, 'term-carol.dev.example.com');
|
|
269
269
|
// the ingress now carries the game route at 127.0.0.1:3000
|
|
270
|
-
assert.deepEqual(putBody.config.ingress[1], { hostname: 'sandbox-carol.dev.
|
|
270
|
+
assert.deepEqual(putBody.config.ingress[1], { hostname: 'sandbox-carol.dev.example.com', service: GAME_SERVICE });
|
|
271
271
|
// BOTH hostnames get a proxied CNAME
|
|
272
|
-
assert.ok(dnsPosts.includes('term-carol.dev.
|
|
273
|
-
assert.ok(dnsPosts.includes('sandbox-carol.dev.
|
|
272
|
+
assert.ok(dnsPosts.includes('term-carol.dev.example.com'), 'terminal CNAME upserted');
|
|
273
|
+
assert.ok(dnsPosts.includes('sandbox-carol.dev.example.com'), 'preview CNAME upserted');
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
await ta('teardownNamedTunnel also deletes the preview CNAME', async () => {
|
|
@@ -279,8 +279,8 @@ await ta('teardownNamedTunnel also deletes the preview CNAME', async () => {
|
|
|
279
279
|
'GET /zones': () => [{ id: 'zoneZ' }],
|
|
280
280
|
'GET /zones/zoneZ/dns_records': ({ query }) => {
|
|
281
281
|
const n = query.name;
|
|
282
|
-
if (n === 'term-dan.dev.
|
|
283
|
-
if (n === 'sandbox-dan.dev.
|
|
282
|
+
if (n === 'term-dan.dev.example.com') return [{ id: 'recTerm', name: n }];
|
|
283
|
+
if (n === 'sandbox-dan.dev.example.com') return [{ id: 'recGame', name: n }];
|
|
284
284
|
return [];
|
|
285
285
|
},
|
|
286
286
|
'DELETE /zones/zoneZ/dns_records/recTerm': () => { deletedDns.push('term'); return { id: 'recTerm' }; },
|
|
@@ -290,7 +290,7 @@ await ta('teardownNamedTunnel also deletes the preview CNAME', async () => {
|
|
|
290
290
|
'DELETE /accounts/acct123/cfd_tunnel/tunZ': () => ({ id: 'tunZ' }),
|
|
291
291
|
});
|
|
292
292
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
293
|
-
const out = await cf.teardownNamedTunnel({ login: 'dan', zone: '
|
|
293
|
+
const out = await cf.teardownNamedTunnel({ login: 'dan', zone: 'example.com', terminalZone: 'dev.example.com' });
|
|
294
294
|
assert.equal(out.dnsDeleted, true);
|
|
295
295
|
assert.equal(out.previewDnsDeleted, true, 'preview CNAME removed');
|
|
296
296
|
assert.equal(out.tunnelDeleted, true);
|
|
@@ -310,7 +310,7 @@ await ta('ensureCookieStripRule PUTs a remove-Cookie rule scoped to term-*/sandb
|
|
|
310
310
|
[`PUT ${RULESET_PATH}`]: ({ body }) => { put = body; return { id: 'rs1', rules: body.rules }; },
|
|
311
311
|
});
|
|
312
312
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
313
|
-
await cf.ensureCookieStripRule({ zone: '
|
|
313
|
+
await cf.ensureCookieStripRule({ zone: 'example.com' });
|
|
314
314
|
assert.equal(put.rules.length, 1, 'one rule on a previously-empty phase');
|
|
315
315
|
const rule = put.rules[0];
|
|
316
316
|
assert.equal(rule.action, 'rewrite');
|
|
@@ -330,7 +330,7 @@ await ta('ensureCookieStripRule preserves OTHER transform rules and is idempoten
|
|
|
330
330
|
[`PUT ${RULESET_PATH}`]: ({ body }) => { put = body; return { id: 'rs1', rules: body.rules }; },
|
|
331
331
|
});
|
|
332
332
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
333
|
-
await cf.ensureCookieStripRule({ zone: '
|
|
333
|
+
await cf.ensureCookieStripRule({ zone: 'example.com' });
|
|
334
334
|
// exactly two rules: the foreign one (kept) + ONE fresh copy of ours (no dupes)
|
|
335
335
|
assert.equal(put.rules.length, 2);
|
|
336
336
|
const ours = put.rules.filter((r) => /otb-919/.test(r.description));
|
|
@@ -351,7 +351,7 @@ await ta('ensureCookieStripRule creates the phase entrypoint when none exists (4
|
|
|
351
351
|
[`PUT ${RULESET_PATH}`]: ({ body }) => { put = body; return { id: 'rs-new', rules: body.rules }; },
|
|
352
352
|
});
|
|
353
353
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
354
|
-
await cf.ensureCookieStripRule({ zone: '
|
|
354
|
+
await cf.ensureCookieStripRule({ zone: 'example.com' });
|
|
355
355
|
assert.equal(put.rules.length, 1, 'a 404 entrypoint is treated as empty, our rule is created');
|
|
356
356
|
});
|
|
357
357
|
|
|
@@ -370,9 +370,9 @@ await ta('ensureCookieStripRule failure does NOT block ensureNamedTunnel (best-e
|
|
|
370
370
|
// ensureNamedTunnel's try/catch and is logged, not thrown.
|
|
371
371
|
});
|
|
372
372
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
373
|
-
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: '
|
|
373
|
+
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: 'example.com', terminalZone: 'example.com' });
|
|
374
374
|
assert.equal(r.token, 'tokZ', 'provisioning completes despite the cookie-strip rule failing');
|
|
375
|
-
assert.equal(r.hostname, 'term-carol.
|
|
375
|
+
assert.equal(r.hostname, 'term-carol.example.com');
|
|
376
376
|
// task 1002511: the failure is reported STRUCTURALLY, not only logged, so box.js
|
|
377
377
|
// can surface it in the provision summary instead of burying it mid-run.
|
|
378
378
|
assert.equal(r.edgeRules.cookieStrip.applied, false, 'a failed cookie-strip PUT must never report applied');
|
|
@@ -391,7 +391,7 @@ await ta('ensureCacheBypassRule PUTs a cache:false rule scoped to sandbox-* (not
|
|
|
391
391
|
[`PUT ${CACHE_PATH}`]: ({ body }) => { put = body; return { id: 'rs1', rules: body.rules }; },
|
|
392
392
|
});
|
|
393
393
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
394
|
-
await cf.ensureCacheBypassRule({ zone: '
|
|
394
|
+
await cf.ensureCacheBypassRule({ zone: 'example.com' });
|
|
395
395
|
assert.equal(put.rules.length, 1);
|
|
396
396
|
const rule = put.rules[0];
|
|
397
397
|
assert.equal(rule.action, 'set_cache_settings');
|
|
@@ -399,7 +399,7 @@ await ta('ensureCacheBypassRule PUTs a cache:false rule scoped to sandbox-* (not
|
|
|
399
399
|
assert.match(rule.expression, /sandbox-/);
|
|
400
400
|
assert.doesNotMatch(rule.expression, /term-/, 'ttyd hosts are not cache-ruled');
|
|
401
401
|
// prod-safety: the apex must NOT satisfy the expression
|
|
402
|
-
assert.ok(!'
|
|
402
|
+
assert.ok(!'example.com'.startsWith('sandbox-'), 'apex never matches sandbox-*');
|
|
403
403
|
assert.equal(rule.enabled, true);
|
|
404
404
|
});
|
|
405
405
|
|
|
@@ -413,7 +413,7 @@ await ta('ensureCacheBypassRule preserves OTHER cache rules and is idempotent on
|
|
|
413
413
|
[`PUT ${CACHE_PATH}`]: ({ body }) => { put = body; return { id: 'rs1', rules: body.rules }; },
|
|
414
414
|
});
|
|
415
415
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
416
|
-
await cf.ensureCacheBypassRule({ zone: '
|
|
416
|
+
await cf.ensureCacheBypassRule({ zone: 'example.com' });
|
|
417
417
|
assert.equal(put.rules.length, 2);
|
|
418
418
|
assert.equal(put.rules.filter((r) => r.description === CACHE_BYPASS_RULE_DESC).length, 1, 'our rule de-duplicated');
|
|
419
419
|
const kept = put.rules.find((r) => r.description === 'someone-elses-cache-rule');
|
|
@@ -431,7 +431,7 @@ await ta('ensureCacheBypassRule creates the cache phase entrypoint when none exi
|
|
|
431
431
|
[`PUT ${CACHE_PATH}`]: ({ body }) => { put = body; return { id: 'rs-new', rules: body.rules }; },
|
|
432
432
|
});
|
|
433
433
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
434
|
-
await cf.ensureCacheBypassRule({ zone: '
|
|
434
|
+
await cf.ensureCacheBypassRule({ zone: 'example.com' });
|
|
435
435
|
assert.equal(put.rules.length, 1);
|
|
436
436
|
});
|
|
437
437
|
|
|
@@ -448,7 +448,7 @@ await ta('ensureCacheBypassRule failure does NOT block ensureNamedTunnel (best-e
|
|
|
448
448
|
'POST /zones/zoneZ/dns_records': ({ body }) => ({ id: `rec-${body.name}`, ...body }),
|
|
449
449
|
});
|
|
450
450
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
451
|
-
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: '
|
|
451
|
+
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: 'example.com', terminalZone: 'example.com' });
|
|
452
452
|
assert.equal(r.token, 'tokZ', 'provisioning completes despite the cache rule failing');
|
|
453
453
|
assert.equal(r.edgeRules.cacheBypass.applied, false);
|
|
454
454
|
assert.ok(r.edgeRules.cacheBypass.error, 'the cache-rule failure reason is captured');
|
|
@@ -471,7 +471,7 @@ await ta('ensureNamedTunnel reports edgeRules.applied on the success path (both
|
|
|
471
471
|
[`PUT ${CS}`]: ({ body }) => ({ id: 'rs2', rules: body.rules }),
|
|
472
472
|
});
|
|
473
473
|
const cf = new CfTunnelApi({ dryRun: false, token: TOKEN, accountId: ACCT, fetchImpl });
|
|
474
|
-
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: '
|
|
474
|
+
const r = await cf.ensureNamedTunnel({ login: 'carol', zone: 'example.com', terminalZone: 'example.com' });
|
|
475
475
|
assert.equal(r.edgeRules.cookieStrip.applied, true);
|
|
476
476
|
assert.equal(r.edgeRules.cookieStrip.error, null);
|
|
477
477
|
assert.equal(r.edgeRules.cacheBypass.applied, true);
|
|
@@ -480,7 +480,7 @@ await ta('ensureNamedTunnel reports edgeRules.applied on the success path (both
|
|
|
480
480
|
|
|
481
481
|
await ta('ensureNamedTunnel in DRY-RUN reports applied:false (a plan never applied anything)', async () => {
|
|
482
482
|
const cf = new CfTunnelApi({ accountId: ACCT, fetchImpl: () => { throw new Error('should not fetch'); } });
|
|
483
|
-
const r = await cf.ensureNamedTunnel({ login: 'alice', zone: '
|
|
483
|
+
const r = await cf.ensureNamedTunnel({ login: 'alice', zone: 'example.com', terminalZone: 'example.com' });
|
|
484
484
|
assert.equal(r.edgeRules.cookieStrip.applied, false, 'dry-run must not read as "the security rule is in place"');
|
|
485
485
|
assert.equal(r.edgeRules.cacheBypass.applied, false);
|
|
486
486
|
});
|
|
@@ -488,8 +488,8 @@ await ta('ensureNamedTunnel in DRY-RUN reports applied:false (a plan never appli
|
|
|
488
488
|
console.log('name helpers:');
|
|
489
489
|
t('tunnelName / terminalHostname / previewHostname are deterministic', () => {
|
|
490
490
|
assert.equal(tunnelName('alice'), 'otb-term-alice');
|
|
491
|
-
assert.equal(terminalHostname('alice', 'dev.
|
|
492
|
-
assert.equal(previewHostname('alice', '
|
|
491
|
+
assert.equal(terminalHostname('alice', 'dev.example.com'), 'term-alice.dev.example.com');
|
|
492
|
+
assert.equal(previewHostname('alice', 'example.com'), 'sandbox-alice.example.com');
|
|
493
493
|
});
|
|
494
494
|
|
|
495
495
|
console.log(`\ncf-tunnel: ${passed} passed, ${failed} failed`);
|
package/tests/cli_package.mjs
CHANGED
|
@@ -185,7 +185,13 @@ test('the packed tarball installs and every verb runs with no repo present', (t)
|
|
|
185
185
|
// base or on missing arguments. Failing that way is fine; failing to find a file is not.
|
|
186
186
|
const LOAD_ARGS = {
|
|
187
187
|
login: ['login'],
|
|
188
|
-
|
|
188
|
+
// `--no-open` is load-bearing, not cosmetic (task 1003909). HOME is a throwaway here, so
|
|
189
|
+
// there is no session and reauth takes its no-session path — which launches the builder's
|
|
190
|
+
// default browser at the dead API base (`rundll32 url.dll,FileProtocolHandler` on Windows).
|
|
191
|
+
// Without the flag, every run of this suite pops a connection-refused tab on the machine
|
|
192
|
+
// under test. The flag skips only the spawn: the script still loads, still prints its
|
|
193
|
+
// guidance, still exits 10, so every assertion below is unchanged.
|
|
194
|
+
reauth: ['reauth', '--no-open'],
|
|
189
195
|
setup: ['setup'],
|
|
190
196
|
box: ['box'],
|
|
191
197
|
shell: ['shell'],
|
|
@@ -203,9 +209,42 @@ test('the packed tarball installs and every verb runs with no repo present', (t)
|
|
|
203
209
|
'every advertised verb needs an entry here, or it goes unexercised',
|
|
204
210
|
);
|
|
205
211
|
|
|
212
|
+
// No verb may launch a GUI on the machine running the suite (task 1003909). A script that can
|
|
213
|
+
// spawn a browser has two ways to be safe here, and it must pick one:
|
|
214
|
+
// * it offers an opt-out flag → the LOAD_ARGS entry above passes it, or
|
|
215
|
+
// * it stops short of the launch → STOPS_SHORT_OF_LAUNCH names a marker that must stay absent.
|
|
216
|
+
// Proving it, rather than assuming it, is the point: a verb that starts reaching its launcher
|
|
217
|
+
// fails here instead of on someone's desktop.
|
|
218
|
+
const LAUNCHES_BROWSER = /FileProtocolHandler|xdg-open/;
|
|
219
|
+
const OPT_OUT = '--no-open';
|
|
220
|
+
const STOPS_SHORT_OF_LAUNCH = {
|
|
221
|
+
// `bongos login` opens a browser from inside the device flow, several network steps past the
|
|
222
|
+
// instance-URL prompt — and with empty stdin that prompt is never answered, so the process
|
|
223
|
+
// ends right there. `▸ Connecting to` is the first thing printed once a URL is accepted, so
|
|
224
|
+
// its ABSENCE is the proof this run stopped well short of the launch.
|
|
225
|
+
login: /Connecting to/,
|
|
226
|
+
};
|
|
227
|
+
const mustStopShort = {};
|
|
228
|
+
for (const [verb, args] of Object.entries(LOAD_ARGS)) {
|
|
229
|
+
const src = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', VERBS[verb].script), 'utf8');
|
|
230
|
+
if (!LAUNCHES_BROWSER.test(src)) continue;
|
|
231
|
+
if (src.includes(OPT_OUT)) {
|
|
232
|
+
assert.ok(args.includes(OPT_OUT),
|
|
233
|
+
`bongos ${verb} can spawn a browser and accepts ${OPT_OUT} — pass it in LOAD_ARGS, or this suite opens a window on the builder's machine`);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
assert.ok(STOPS_SHORT_OF_LAUNCH[verb],
|
|
237
|
+
`bongos ${verb} can spawn a browser and has no ${OPT_OUT} — give it one, or add a STOPS_SHORT_OF_LAUNCH entry proving this run never reaches the launch`);
|
|
238
|
+
mustStopShort[verb] = STOPS_SHORT_OF_LAUNCH[verb];
|
|
239
|
+
}
|
|
240
|
+
|
|
206
241
|
for (const [verb, args] of Object.entries(LOAD_ARGS)) {
|
|
207
242
|
const res = runVerb(args);
|
|
208
243
|
const all = `${res.stdout}\n${res.stderr}`;
|
|
244
|
+
if (mustStopShort[verb]) {
|
|
245
|
+
assert.doesNotMatch(all, mustStopShort[verb],
|
|
246
|
+
`bongos ${verb} got past the point where it can spawn a browser on this machine:\n${all}`);
|
|
247
|
+
}
|
|
209
248
|
assert.ok(!/Cannot find module|MODULE_NOT_FOUND|ERR_MODULE_NOT_FOUND/.test(all),
|
|
210
249
|
`bongos ${verb} hit a missing file — the manifest is incomplete:\n${all}`);
|
|
211
250
|
assert.ok(!/ERR_REQUIRE_ESM|SyntaxError|ReferenceError/.test(all),
|
|
@@ -53,10 +53,10 @@ t('preserves gemini_api_key across token rotation', () => {
|
|
|
53
53
|
const existing = {
|
|
54
54
|
token: 'old-token',
|
|
55
55
|
builder: { id: '1', github_login: 'a' },
|
|
56
|
-
api_base: 'https://
|
|
56
|
+
api_base: 'https://example.com',
|
|
57
57
|
gemini_api_key: 'AIzaSy_TEST_KEY',
|
|
58
58
|
};
|
|
59
|
-
const merged = mergeSession(existing, { builder: { id: '1', github_login: 'a', rank: 'archon' } }, 'new-token-1234', 'https://
|
|
59
|
+
const merged = mergeSession(existing, { builder: { id: '1', github_login: 'a', rank: 'archon' } }, 'new-token-1234', 'https://example.com');
|
|
60
60
|
assert.equal(merged.gemini_api_key, 'AIzaSy_TEST_KEY');
|
|
61
61
|
assert.equal(merged.token, 'new-token-1234');
|
|
62
62
|
assert.equal(merged.builder.rank, 'archon', 'fresh builder rank must overwrite stale rank');
|
|
@@ -66,15 +66,15 @@ t('writes token even when existing has no gemini_api_key', () => {
|
|
|
66
66
|
const existing = {
|
|
67
67
|
token: 'old',
|
|
68
68
|
builder: { id: '1', github_login: 'a' },
|
|
69
|
-
api_base: 'https://
|
|
69
|
+
api_base: 'https://example.com',
|
|
70
70
|
};
|
|
71
|
-
const merged = mergeSession(existing, { builder: { id: '1', github_login: 'a' } }, 'fresh', 'https://
|
|
71
|
+
const merged = mergeSession(existing, { builder: { id: '1', github_login: 'a' } }, 'fresh', 'https://example.com');
|
|
72
72
|
assert.equal(merged.token, 'fresh');
|
|
73
73
|
assert.equal(merged.gemini_api_key, undefined);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
t('handles a completely empty existing session (first-time paste)', () => {
|
|
77
|
-
const merged = mergeSession({}, { builder: { id: '7', github_login: 'newbie' } }, 'first-token', 'https://
|
|
77
|
+
const merged = mergeSession({}, { builder: { id: '7', github_login: 'newbie' } }, 'first-token', 'https://example.com');
|
|
78
78
|
assert.equal(merged.token, 'first-token');
|
|
79
79
|
assert.equal(merged.builder.id, '7');
|
|
80
80
|
});
|
|
@@ -85,11 +85,11 @@ t('forward-compatible: an unknown field (future-proofing) survives the merge', (
|
|
|
85
85
|
const existing = {
|
|
86
86
|
token: 'old',
|
|
87
87
|
builder: { id: '1' },
|
|
88
|
-
api_base: 'https://
|
|
88
|
+
api_base: 'https://example.com',
|
|
89
89
|
gemini_api_key: 'k',
|
|
90
90
|
model_preference: 'opus-only', // hypothetical future field
|
|
91
91
|
};
|
|
92
|
-
const merged = mergeSession(existing, { builder: { id: '1' } }, 'new', 'https://
|
|
92
|
+
const merged = mergeSession(existing, { builder: { id: '1' } }, 'new', 'https://example.com');
|
|
93
93
|
assert.equal(merged.model_preference, 'opus-only');
|
|
94
94
|
});
|
|
95
95
|
|
|
@@ -95,7 +95,7 @@ test('customer-0: GDS_BRANDING_FILE selects the cloudbongos.com instance pack (t
|
|
|
95
95
|
assert.equal(b.domains.statusOrigin, 'https://status.cloudbongos.com');
|
|
96
96
|
assert.equal(b.domains.cookieDomain, '.cloudbongos.com');
|
|
97
97
|
assert.equal(b.db.database, 'cloudbongos');
|
|
98
|
-
assert.equal(b.firstAdmin, '
|
|
98
|
+
assert.equal(b.firstAdmin, 'example-owner');
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
test('customer-0: no GDS_BRANDING_FILE still resolves the instance pack (default unchanged)', (t) => {
|
package/tests/currency_label.mjs
CHANGED
|
@@ -13,7 +13,7 @@ const { clientBranding } = brandingMod;
|
|
|
13
13
|
const { injectBrandingGlobal, themeMarkup, injectModuleHallWidgets } = serveInternal;
|
|
14
14
|
|
|
15
15
|
const OTB_ISH = {
|
|
16
|
-
identity: { productName: '
|
|
16
|
+
identity: { productName: 'Example World', worldName: 'Example', company: { name: 'Hermeslines', site: 'x' } },
|
|
17
17
|
currency: { label: 'drachmae', symbol: '' },
|
|
18
18
|
theme: { palette: 'art/template/palette.json', mode: 'pixel-greek', ui: {
|
|
19
19
|
colors: { bg: '#ece0c2', ink: '#2a2110', accent: '#4a7d5c' },
|
|
@@ -21,7 +21,7 @@ const OTB_ISH = {
|
|
|
21
21
|
favicon: "data:image/svg+xml;utf8,<svg/>",
|
|
22
22
|
} },
|
|
23
23
|
rankLabels: { xenos: 'Xenos', archon: 'Archon' },
|
|
24
|
-
copy: { tagline: 'Goods straight
|
|
24
|
+
copy: { tagline: 'Goods straight example world.', hallName: "Builders' Hall", notFound: 'washed ashore' },
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
test('R56: clientBranding exposes the configurable currency label', () => {
|
|
@@ -31,13 +31,13 @@ test('R56: clientBranding exposes the configurable currency label', () => {
|
|
|
31
31
|
|
|
32
32
|
test('R57: clientBranding exposes identity + theme + rankLabels', () => {
|
|
33
33
|
const c = clientBranding(OTB_ISH);
|
|
34
|
-
assert.equal(c.identity.worldName, '
|
|
35
|
-
assert.equal(c.identity.productName, '
|
|
34
|
+
assert.equal(c.identity.worldName, 'Example');
|
|
35
|
+
assert.equal(c.identity.productName, 'Example World');
|
|
36
36
|
assert.equal(c.theme.colors.bg, '#ece0c2');
|
|
37
37
|
assert.equal(c.theme.fonts.display, '"Cinzel", serif');
|
|
38
38
|
assert.deepEqual(c.theme.fonts.links, ['https://fonts.example/css']);
|
|
39
39
|
assert.equal(c.rankLabels.archon, 'Archon');
|
|
40
|
-
assert.equal(c.copy.tagline, 'Goods straight
|
|
40
|
+
assert.equal(c.copy.tagline, 'Goods straight example world.');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('R57: clientBranding exposes theme.ui (web), NOT theme.palette (server-only game path)', () => {
|
|
@@ -65,9 +65,9 @@ test('clientBranding exposes project policy — the settings read-back (task 100
|
|
|
65
65
|
test('clientBranding is a SMALL allow-list — no server-only field leaks to the browser', () => {
|
|
66
66
|
const projected = clientBranding({
|
|
67
67
|
...OTB_ISH,
|
|
68
|
-
domains: { publicOrigin: 'https://
|
|
69
|
-
repo: { owner: '
|
|
70
|
-
db: { database: '
|
|
68
|
+
domains: { publicOrigin: 'https://example.com', cookieDomain: '.example.com' },
|
|
69
|
+
repo: { owner: 'example-owner', name: 'example' },
|
|
70
|
+
db: { database: 'example' },
|
|
71
71
|
envPrefix: 'OTB',
|
|
72
72
|
});
|
|
73
73
|
// `reward` joined the projection in task 1002469 (ADR 0146 follow-up) — the resolved
|
package/tests/dev_lib.mjs
CHANGED
|
@@ -70,19 +70,19 @@ t('deriveDbName: override > branding > repo dir > default', () => {
|
|
|
70
70
|
// ---------- branding facts (instance over neutral) ----------
|
|
71
71
|
t('loadBrandingFacts: instance pack wins, neutral fills gaps', () => {
|
|
72
72
|
const f = fakeFs({
|
|
73
|
-
'config/branding.json': JSON.stringify({ domains: { publicOrigin: 'http://localhost:3006' }, firstAdmin: '
|
|
73
|
+
'config/branding.json': JSON.stringify({ domains: { publicOrigin: 'http://localhost:3006' }, firstAdmin: 'example-owner', envPrefix: 'CLOUDBONGOS' }),
|
|
74
74
|
'config/branding.neutral.json': NEUTRAL,
|
|
75
75
|
});
|
|
76
76
|
const facts = lib.loadBrandingFacts({ instanceRoot: '/x/test4', coreRoot: '/core', f });
|
|
77
77
|
assert.equal(facts.publicOrigin, 'http://localhost:3006');
|
|
78
|
-
assert.equal(facts.firstAdmin, '
|
|
78
|
+
assert.equal(facts.firstAdmin, 'example-owner');
|
|
79
79
|
assert.equal(facts.configDir, 'cloudbongos');
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
// ---------- full run params ----------
|
|
83
83
|
t('resolveRunParams: port from branding, db from repo dir, TCP localhost, gdsOnly', () => {
|
|
84
84
|
const f = fakeFs({
|
|
85
|
-
'config/branding.json': JSON.stringify({ domains: { publicOrigin: 'http://localhost:3006' }, firstAdmin: '
|
|
85
|
+
'config/branding.json': JSON.stringify({ domains: { publicOrigin: 'http://localhost:3006' }, firstAdmin: 'example-owner' }),
|
|
86
86
|
'config/branding.neutral.json': NEUTRAL,
|
|
87
87
|
'config/modules.json': JSON.stringify({ modules: { game: false } }),
|
|
88
88
|
});
|
|
@@ -93,7 +93,7 @@ t('resolveRunParams: port from branding, db from repo dir, TCP localhost, gdsOnl
|
|
|
93
93
|
assert.equal(p.pgUser, 'lars');
|
|
94
94
|
assert.equal(p.gdsOnly, true); // game off -> skip OTB seeds
|
|
95
95
|
assert.equal(p.publicOrigin, 'http://localhost:3006');
|
|
96
|
-
assert.equal(p.firstAdmin, '
|
|
96
|
+
assert.equal(p.firstAdmin, 'example-owner');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
t('resolveRunParams: game module ON -> not GDS-only; env PGHOST/PGUSER honored', () => {
|
|
@@ -114,7 +114,7 @@ test('a pack filling the documented theme surface leaves NO neutral colour on th
|
|
|
114
114
|
test('the generator carries no host-brand copy or motif of its own', () => {
|
|
115
115
|
const code = codeOnly(source);
|
|
116
116
|
// The archaic install copy + the world motifs that shipped into the vanilla build.
|
|
117
|
-
for (const re of [/thine/i, /ashore/i, /
|
|
117
|
+
for (const re of [/thine/i, /ashore/i, /example/i, /example world/i, /meander/i, /greek/i, /marble/i]) {
|
|
118
118
|
assert.doesNotMatch(code, re, `host-brand literal ${re} must not appear in the generator's code`);
|
|
119
119
|
}
|
|
120
120
|
// Copy is derived from the pack (or overridden by it), never a fixed sentence.
|
|
@@ -23,7 +23,7 @@ function t(name, fn) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const CFG = {
|
|
26
|
-
releases_repo: '
|
|
26
|
+
releases_repo: 'example-owner/example-devbox',
|
|
27
27
|
version: '1.2.3',
|
|
28
28
|
assets: {
|
|
29
29
|
'mac-arm64': 'OTB-Dev-Box-1.2.3-arm64.dmg',
|
|
@@ -38,7 +38,7 @@ t('maps a known target to the versioned release-asset URL', () => {
|
|
|
38
38
|
const d = buildDevboxDownloads(CFG);
|
|
39
39
|
assert.equal(
|
|
40
40
|
d.redirectFor('mac-arm64'),
|
|
41
|
-
'https://github.com/
|
|
41
|
+
'https://github.com/example-owner/example-devbox/releases/download/v1.2.3/OTB-Dev-Box-1.2.3-arm64.dmg'
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
|
package/tests/devbox_icons.mjs
CHANGED
|
@@ -226,7 +226,7 @@ test('a pack filling the documented theme surface leaves NO neutral colour on th
|
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
test('the generator carries no host-brand motif of its own', () => {
|
|
229
|
-
for (const re of [/
|
|
229
|
+
for (const re of [/example world/i, /example/i, /shipping[- ]container/i, /greek/i, /meander/i, /parchment/i]) {
|
|
230
230
|
assert.doesNotMatch(code, re, `host-brand literal ${re} must not appear in the generator's code`);
|
|
231
231
|
}
|
|
232
232
|
});
|
|
@@ -74,7 +74,7 @@ await t('memberApprovalLine round-trips through parseApprovalMarker', () => {
|
|
|
74
74
|
|
|
75
75
|
// ---- recordApprovalReaction (orchestration via fakes) ----------------------
|
|
76
76
|
const GOOD_ID = '123456789012345678';
|
|
77
|
-
const archon = { id: 1, rank: 'archon', github_login: '
|
|
77
|
+
const archon = { id: 1, rank: 'archon', github_login: 'example-owner' };
|
|
78
78
|
|
|
79
79
|
function makeDeps(over = {}) {
|
|
80
80
|
const calls = { member: [], audit: [] };
|
package/tests/discord_oauth.mjs
CHANGED
|
@@ -27,7 +27,7 @@ function t(name, fn) {
|
|
|
27
27
|
console.log('Discord OAuth helpers (Phase 3 F2):');
|
|
28
28
|
|
|
29
29
|
await t('discordAuthorizeUrl carries client_id, identify+guilds.join scope, code response, state, redirect', () => {
|
|
30
|
-
const url = auth.discordAuthorizeUrl('123456789012345678', 'abc123state', 'https://
|
|
30
|
+
const url = auth.discordAuthorizeUrl('123456789012345678', 'abc123state', 'https://example.com/api/gds/auth/discord/callback');
|
|
31
31
|
const u = new URL(url);
|
|
32
32
|
assert.equal(u.origin + u.pathname, 'https://discord.com/api/oauth2/authorize');
|
|
33
33
|
assert.equal(u.searchParams.get('client_id'), '123456789012345678');
|
|
@@ -36,7 +36,7 @@ await t('discordAuthorizeUrl carries client_id, identify+guilds.join scope, code
|
|
|
36
36
|
assert.equal(u.searchParams.get('scope'), 'identify guilds.join');
|
|
37
37
|
assert.equal(u.searchParams.get('response_type'), 'code');
|
|
38
38
|
assert.equal(u.searchParams.get('state'), 'abc123state');
|
|
39
|
-
assert.equal(u.searchParams.get('redirect_uri'), 'https://
|
|
39
|
+
assert.equal(u.searchParams.get('redirect_uri'), 'https://example.com/api/gds/auth/discord/callback');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
await t('isValidDiscordUserId accepts a 17–20 digit snowflake', () => {
|
|
@@ -90,7 +90,12 @@ test('every computed require inside the closure is one we have accounted for', (
|
|
|
90
90
|
const ACCOUNTED = new Map([
|
|
91
91
|
['src/module-loader/loader.js', 2], // routes/<key>.js + pollerPath, from loadModules()
|
|
92
92
|
['scripts/gds/onboard.js', 3], // provision/oauth-secret/onboardPlan, inside the verb
|
|
93
|
-
|
|
93
|
+
// 6th added by task 1003869: leak-scan-allowlist.js. Unlike the others this one IS
|
|
94
|
+
// top-level, so it resolves whenever package-core is required — but it does not widen
|
|
95
|
+
// the closure: the module is pure data plus three predicates, does no I/O, dispatches
|
|
96
|
+
// nothing dynamically, and its single require (back to package-core, for the literal
|
|
97
|
+
// floor) is lazy and inside a function.
|
|
98
|
+
['scripts/gds/package-core.js', 6], // publish-manifest/mirror-redact/secret-scrub/leak-scan-allowlist/…
|
|
94
99
|
['scripts/gds/upgrade.js', 1], // a `node -e` child, not a require in this process
|
|
95
100
|
['scripts/gds/gen-api-docs.js', 1], // require(path.join(CORE_ROOT,'package.json')) — JSON
|
|
96
101
|
]);
|
|
@@ -35,7 +35,7 @@ test('firstAdminLogin: env override wins over branding (configured prefix)', ()
|
|
|
35
35
|
|
|
36
36
|
test('maybeSeatFirstAdmin: no-op when the builder is already an Archon (no DB call)', async () => {
|
|
37
37
|
clearEnv();
|
|
38
|
-
const b = { id: 1, github_login: '
|
|
38
|
+
const b = { id: 1, github_login: 'example-owner', rank: 'archon' };
|
|
39
39
|
const out = await maybeSeatFirstAdmin(b);
|
|
40
40
|
assert.equal(out.rank, 'archon');
|
|
41
41
|
});
|
package/tests/gate_approvals.mjs
CHANGED
|
@@ -56,7 +56,7 @@ function fakeRes() {
|
|
|
56
56
|
attachFail({}, res, () => {}); // install res.fail() as the router does (task 1991)
|
|
57
57
|
return res;
|
|
58
58
|
}
|
|
59
|
-
const ARCHON = { id: 1, rank: 'archon', github_login: '
|
|
59
|
+
const ARCHON = { id: 1, rank: 'archon', github_login: 'example-owner' };
|
|
60
60
|
|
|
61
61
|
// Snapshot/restore the github-push functions a route test stubs, so layers don't bleed.
|
|
62
62
|
function withStubs(stubs, fn) {
|
|
@@ -180,13 +180,13 @@ await t('getPullRequestFiles: rejects a non-positive PR number before any fetch'
|
|
|
180
180
|
await t('setGateOwnerApproved: POSTs a gate-owner-approved success status on the sha', async () => {
|
|
181
181
|
let captured = null;
|
|
182
182
|
const fetchImpl = async (url, opts) => { captured = { url, body: JSON.parse(opts.body) }; return { ok: true }; };
|
|
183
|
-
const ok = await gp.setGateOwnerApproved({ sha: SHA, login: '
|
|
183
|
+
const ok = await gp.setGateOwnerApproved({ sha: SHA, login: 'example-owner' }, { token: 'T', repoInfo: REPO, fetchImpl });
|
|
184
184
|
assert.equal(ok, true);
|
|
185
185
|
assert.ok(captured.url.endsWith(`/statuses/${SHA}`), captured.url);
|
|
186
186
|
assert.equal(captured.body.context, gp.GATE_OWNER_APPROVED_CONTEXT);
|
|
187
187
|
assert.equal(captured.body.context, 'gate-owner-approved');
|
|
188
188
|
assert.equal(captured.body.state, 'success');
|
|
189
|
-
assert.ok(/
|
|
189
|
+
assert.ok(/example-owner/.test(captured.body.description), 'records who approved');
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
await t('recheckPullRequest: PATCHes state=closed then state=open, then re-arms auto-merge', async () => {
|
|
@@ -332,7 +332,7 @@ await t('POST approve: sets the owner-approval status on the head sha + re-runs
|
|
|
332
332
|
assert.equal(res.statusCode, 200);
|
|
333
333
|
assert.equal(res.body.ok, true);
|
|
334
334
|
assert.equal(res.body.status_set, true);
|
|
335
|
-
assert.deepEqual(approved, { sha: SHA, login: '
|
|
335
|
+
assert.deepEqual(approved, { sha: SHA, login: 'example-owner' }, 'approval is sha-pinned to head + records the Archon');
|
|
336
336
|
assert.equal(rechecked, 201, 'the gate check is re-run for the PR');
|
|
337
337
|
});
|
|
338
338
|
});
|
|
@@ -353,7 +353,7 @@ await t('POST approve: still works when checks are unreadable (no gate-review ch
|
|
|
353
353
|
await handler({ builder: ARCHON, params: { pr: '167' }, body: {} }, res);
|
|
354
354
|
assert.equal(res.statusCode, 200);
|
|
355
355
|
assert.equal(res.body.status_set, true);
|
|
356
|
-
assert.deepEqual(approved, { sha: SHA, login: '
|
|
356
|
+
assert.deepEqual(approved, { sha: SHA, login: 'example-owner' });
|
|
357
357
|
assert.equal(rechecked, 167);
|
|
358
358
|
});
|
|
359
359
|
});
|
|
@@ -76,7 +76,7 @@ await test('gradeByBuilder: quality-filtered, and pass_rate is NULL (not 0.000)
|
|
|
76
76
|
const sql = (await captureSql(() => db.gradeByBuilder(30)))[0];
|
|
77
77
|
assertQualityFiltered(sql, 'gradeByBuilder');
|
|
78
78
|
// The old shape ROUND(COALESCE(AVG(...),0),3) fabricated a 0.000 fail-streak
|
|
79
|
-
// for an all-outage window — the
|
|
79
|
+
// for an all-outage window — the example-owner symptom. COALESCE must be gone from
|
|
80
80
|
// the pass_rate expression so it reads NULL ("no graded work") instead.
|
|
81
81
|
assert.ok(!sql.includes('ROUND(COALESCE(AVG'), 'pass_rate must not coalesce an empty window to 0');
|
|
82
82
|
assert.ok(/AVG\(CASE WHEN g\.passed THEN 1 ELSE 0 END\) FILTER/.test(sql), 'pass_rate must average quality rows only');
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// task 2122 (demo-audit fix B) — the scaffolded .claude/hooks must NOT hardcode a
|
|
4
4
|
// founder-instance brand in their outbound HTTP User-Agent. Those hooks are copied
|
|
5
5
|
// into a consumer instance by claude-materialize, whose rebrand maps domain HOSTS
|
|
6
|
-
// (
|
|
7
|
-
// like `
|
|
6
|
+
// (example.com), config dirs, and env prefixes — but NOT a bare brand token
|
|
7
|
+
// like `example-gds-cli` (no TLD). So a branded UA there leaks the founder's
|
|
8
8
|
// host-brand into every scaffolded instance (criterion gcarry-prod-vanilla).
|
|
9
9
|
//
|
|
10
10
|
// Invariant: every `'User-Agent': '<value>'` literal in .claude/hooks/*.js uses the
|