@bongos/core 1.21.47 → 1.21.49
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 +50 -60
- package/.claude/skills/bongos-feedback/SKILL.md +5 -4
- package/bin/bongos.js +0 -0
- package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +2 -2
- package/docs/copy-inventory.md +33 -31
- package/docs/copy-registry.json +103 -75
- package/docs/file-map.md +0 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/onboarding/primer.md +23 -16
- package/docs/page-readings.json +154 -145
- package/modules/hall-ui/public/commands-lib.js +1 -1
- package/modules/hall-ui/public/hall-render.js +13 -11
- package/modules/hall-ui/public/settings.html +4 -4
- package/modules/hall-ui/public/settings.js +1 -1
- package/modules/hall-ui/public/style.css +9 -0
- package/modules/onboarding/onboarding-state.js +5 -5
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +16 -0
- package/scripts/gds/claude-materialize.js +3 -0
- package/scripts/gds/pack-env-prefix.js +73 -0
- package/scripts/gds/provision-repo.js +13 -0
- package/scripts/gds/provision-units.js +6 -17
- package/scripts/gds/provision.js +8 -2
- package/src/bongos/routes/auth.js +6 -5
- package/src/instance-config.js +28 -1
- package/src/module-api.js +1 -1
- package/tests/bongos_cli.mjs +2 -1
- package/tests/cli_allowlist_permissions.mjs +4 -0
- package/tests/cli_token_reissue.mjs +5 -3
- package/tests/hall_onboarding_bongos_setup.mjs +101 -0
- package/tests/instance_config.mjs +53 -0
- package/tests/instance_feedback.mjs +12 -0
- package/tests/provision.mjs +103 -0
- package/tests/skill_menu_ruling.mjs +1 -1
- package/.claude/skills/feedback/SKILL.md +0 -39
- package/scripts/bongos-mcp.js +0 -28
- package/scripts/gds/feedback-latest.js +0 -224
- package/tests/feedback_latest.mjs +0 -122
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
{
|
|
25
25
|
key: 'find', label: 'Finding things out and working with others',
|
|
26
26
|
blurb: 'Looking up what the project already knows, asking for help, and passing work on.',
|
|
27
|
-
names: ['status', 'recall', 'ask-for-help', 'collab-review', 'session-handoff', 'read-session-export'
|
|
27
|
+
names: ['status', 'recall', 'ask-for-help', 'collab-review', 'session-handoff', 'read-session-export'],
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
key: 'owner', label: 'Owner routines',
|
|
@@ -808,11 +808,11 @@
|
|
|
808
808
|
? `Stuck on any step? <a href="${API}/auth/discord/start">Join our Discord</a> and ask in <strong>#support</strong>.`
|
|
809
809
|
: `Stuck on any step? See the project's README and docs.`;
|
|
810
810
|
const setupSteps = `
|
|
811
|
-
<p class="setup__intro">Build from your own machine —
|
|
811
|
+
<p class="setup__intro">Build from your own machine — get a copy of the project, sign in once, then work in Claude.</p>
|
|
812
812
|
<ol class="setup__steps">
|
|
813
813
|
<li><strong>Clone the repository</strong> and install dependencies (<code>npm install</code>).</li>
|
|
814
|
-
<li><strong
|
|
815
|
-
<li><strong>
|
|
814
|
+
<li><strong>Run <code>npx bongos setup</code></strong> in that folder to sign this machine in.</li>
|
|
815
|
+
<li><strong><a href="https://claude.com/download" target="_blank" rel="noopener noreferrer">Download Claude</a></strong> (or use the Claude Code CLI), open the project, and run <code>/builder-start</code> to claim your first task.</li>
|
|
816
816
|
</ol>
|
|
817
817
|
`;
|
|
818
818
|
// task 469: a "Set up your machine / get the code" door. The steps above
|
|
@@ -821,24 +821,26 @@
|
|
|
821
821
|
// how do I get it onto my own laptop?". It's the hall mirror of the primer's
|
|
822
822
|
// "Step 0 — get the project onto your machine". Collapsed by default so it
|
|
823
823
|
// doesn't crowd the recommended path; the clone block is click-to-copy
|
|
824
|
-
// (copy-on-click.js picks up .welcome__paste).
|
|
825
|
-
// scripts/setup
|
|
824
|
+
// (copy-on-click.js picks up .welcome__paste). Sign-in is `npx bongos setup`
|
|
825
|
+
// (bin/bongos.js → scripts/gds/setup.js), one line on every OS — bare
|
|
826
|
+
// `bongos` is not on a fresh clone's PATH, `npx` finds the repo's own bin.
|
|
826
827
|
// The clone commands come from the instance's own repo coords (loaded from
|
|
827
828
|
// GET /api/bongos/instance), not a hardcoded repo, so a fresh instance's newcomer
|
|
828
829
|
// clones ITS project (ADR 0062 §3, task 1705). Falls back to generic guidance
|
|
829
830
|
// until the coords load (or if they're unavailable).
|
|
830
831
|
const cloneBlock = ctx.instanceRepo
|
|
831
|
-
? `git clone ${escapeHtml(ctx.instanceRepo.url)}.git\ncd ${escapeHtml(ctx.instanceRepo.repo)}\
|
|
832
|
-
: `# clone your instance's repository, cd into it, then run:\
|
|
832
|
+
? `git clone ${escapeHtml(ctx.instanceRepo.url)}.git\ncd ${escapeHtml(ctx.instanceRepo.repo)}\nnpm install\nnpx bongos setup`
|
|
833
|
+
: `# clone your instance's repository, cd into it, then run:\nnpm install\nnpx bongos setup`;
|
|
833
834
|
const getCode = `
|
|
834
835
|
<details class="setup__getcode">
|
|
835
836
|
<summary class="setup__getcode-summary">Set up your machine / get the code</summary>
|
|
836
837
|
<div class="setup__getcode-body">
|
|
837
|
-
<p class="setup__hint">First install <a href="https://git-scm.com/downloads" target="_blank" rel="noopener noreferrer">Git</a
|
|
838
|
+
<p class="setup__hint">First install <a href="https://git-scm.com/downloads" target="_blank" rel="noopener noreferrer">Git</a>,
|
|
839
|
+
<a href="https://nodejs.org" target="_blank" rel="noopener noreferrer">Node</a> (version 22 or newer)
|
|
838
840
|
and <a href="https://claude.com/download" target="_blank" rel="noopener noreferrer">Claude Code</a> — that's all the tooling you need up front.</p>
|
|
839
|
-
<p class="setup__getcode-lede">Then copy the project onto your machine
|
|
841
|
+
<p class="setup__getcode-lede">Then copy the project onto your machine, install its dependencies, and sign in:</p>
|
|
840
842
|
<code class="welcome__paste">${cloneBlock}</code>
|
|
841
|
-
<p class="setup__hint">
|
|
843
|
+
<p class="setup__hint">The same commands work on Mac, Windows and Linux. New to the steps? The
|
|
842
844
|
<a href="/primer">primer</a> walks the whole path in plain language.</p>
|
|
843
845
|
</div>
|
|
844
846
|
</details>`;
|
|
@@ -898,7 +900,7 @@
|
|
|
898
900
|
<span class="onboarding-item__mark" aria-hidden="true">›</span>
|
|
899
901
|
<div class="onboarding-item__body">
|
|
900
902
|
<p class="onboarding-item__label">First — obtain the repo</p>
|
|
901
|
-
<p class="onboarding-item__hint">Clone the project to your machine. The build commands live inside the repo, so this comes first. The remaining steps below are tracked automatically.</p>
|
|
903
|
+
<p class="onboarding-item__hint">Clone the project to your machine and run <code>npm install</code> in it. The build commands live inside the repo, so this comes first. The remaining steps below are tracked automatically.</p>
|
|
902
904
|
</div>
|
|
903
905
|
</li>`;
|
|
904
906
|
|
|
@@ -140,13 +140,13 @@
|
|
|
140
140
|
<section class="scroll" id="cli-scroll" aria-labelledby="cli-h" data-group="access" hidden>
|
|
141
141
|
<h2 id="cli-h" class="scroll__h">CLI access</h2>
|
|
142
142
|
<p class="scroll__lede">
|
|
143
|
-
Mint a fresh CLI token using this browser session and paste
|
|
144
|
-
|
|
145
|
-
command says your session expired.
|
|
143
|
+
Mint a fresh CLI token using this browser session and paste the
|
|
144
|
+
command it gives you into a terminal in your project folder — the quick
|
|
145
|
+
way back in when a <code>/builder-*</code> command says your session expired.
|
|
146
146
|
</p>
|
|
147
147
|
<div class="settings-card">
|
|
148
148
|
<button type="button" class="settings-btn" id="cli-reissue-btn">Re-issue CLI token</button>
|
|
149
|
-
<span class="settings-card__hint">
|
|
149
|
+
<span class="settings-card__hint">no GitHub code to type in.</span>
|
|
150
150
|
<div id="cli-reissue-result" class="cli-result" hidden></div>
|
|
151
151
|
</div>
|
|
152
152
|
<p class="section-note">Tokens refresh on use; re-issuing never signs you out anywhere else.</p>
|
|
@@ -792,7 +792,7 @@
|
|
|
792
792
|
// three out of the copy registry, blinding the artist's queue to this panel.
|
|
793
793
|
const el = (tag, cls) => Object.assign(document.createElement(tag), { className: cls });
|
|
794
794
|
const say = el('p', 'cli-result__say');
|
|
795
|
-
say.textContent = 'Fresh CLI token issued. Paste
|
|
795
|
+
say.textContent = 'Fresh CLI token issued. Paste this into a terminal in your project folder:';
|
|
796
796
|
const eyebrow = el('p', 'quote__eyebrow');
|
|
797
797
|
eyebrow.textContent = 'paste command';
|
|
798
798
|
const pre = el('pre', 'quote__pre');
|
|
@@ -3206,6 +3206,15 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
|
|
|
3206
3206
|
padding: 8px 12px;
|
|
3207
3207
|
overflow-x: auto;
|
|
3208
3208
|
}
|
|
3209
|
+
/* The get-the-code block is several commands, one per line. Without `pre` the
|
|
3210
|
+
line breaks collapse on screen AND in copy-on-click (it copies innerText),
|
|
3211
|
+
so a builder pasted every command onto one line. Both axes named: see the
|
|
3212
|
+
one-axis-strip rule in this module's CLAUDE.md. */
|
|
3213
|
+
.setup__getcode .welcome__paste {
|
|
3214
|
+
display: block;
|
|
3215
|
+
white-space: pre;
|
|
3216
|
+
overflow-y: hidden;
|
|
3217
|
+
}
|
|
3209
3218
|
|
|
3210
3219
|
.setup { margin-top: 12px; }
|
|
3211
3220
|
.setup__intro { font-size: 13px; color: var(--ink-soft); }
|
|
@@ -92,7 +92,7 @@ const ALL_STAGES = [...STAGES, GRADUATED];
|
|
|
92
92
|
const PRE_AUTH_STEP = {
|
|
93
93
|
id: 'obtain_repo',
|
|
94
94
|
label: 'Obtain the repo',
|
|
95
|
-
hint: 'Clone the project to thy machine. The build commands live inside the repo, so this comes first.',
|
|
95
|
+
hint: 'Clone the project to thy machine and run npm install in it. The build commands live inside the repo, so this comes first.',
|
|
96
96
|
informational: true,
|
|
97
97
|
};
|
|
98
98
|
|
|
@@ -126,7 +126,7 @@ const STAGE_LABELS = {
|
|
|
126
126
|
// onboarding model never named that step, so the hint now points at it ahead of
|
|
127
127
|
// the sign-in itself. See PRE_AUTH_STEP below for the standalone representation.
|
|
128
128
|
const STAGE_HINTS = {
|
|
129
|
-
auth_complete: 'First obtain the repo — clone it — then sign in
|
|
129
|
+
auth_complete: 'First obtain the repo — clone it — then sign in with npx bongos setup in that folder.',
|
|
130
130
|
pick_disciplines: 'Pick the work that calls thee — engineer, artist, ideator, or any mix.',
|
|
131
131
|
first_start: 'Run /builder-start to see claimable works.',
|
|
132
132
|
first_claim: 'Claim a task with /builder-claim N — it is thine alone until thou shippest it.',
|
|
@@ -153,7 +153,7 @@ function isKnownStage(stage) {
|
|
|
153
153
|
// clears the step. `source: 'discord'`.
|
|
154
154
|
// 2. pick_disciplines — the real engine stage, cleared by the craft picker.
|
|
155
155
|
// 3. cli_setup — cleared once the builder has authenticated the CLI
|
|
156
|
-
// (ran
|
|
156
|
+
// (ran `npx bongos setup`, which mints a source='cli'
|
|
157
157
|
// session). Detected via db.hasCliSession; passed in as
|
|
158
158
|
// `cliAuthed`. `source: 'cli'`.
|
|
159
159
|
// 4. ship_three — the real engine stage. Auto, live "N of 3" counter,
|
|
@@ -178,14 +178,14 @@ const DISPLAY_STEPS = [
|
|
|
178
178
|
const DISPLAY_LABELS = {
|
|
179
179
|
join_discord: 'Join the Discord server',
|
|
180
180
|
pick_disciplines: 'Choose thy craft — engineer, artist, or ideator',
|
|
181
|
-
cli_setup: 'Run
|
|
181
|
+
cli_setup: 'Run npx bongos setup',
|
|
182
182
|
ship_three: 'Ship three works — and rise to Thetes',
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
const DISPLAY_HINTS = {
|
|
186
186
|
join_discord: 'Connect Discord — thou art auto-joined to the city\'s server and given thy rank.',
|
|
187
187
|
pick_disciplines: 'Pick the work that calls thee — engineer, artist, ideator, or any mix.',
|
|
188
|
-
cli_setup: '
|
|
188
|
+
cli_setup: 'In thy copy of the project, run npx bongos setup — this ticks itself once thou art signed in.',
|
|
189
189
|
ship_three: 'Carry three tasks through with /builder-ship — the third makes thee a Thetes.',
|
|
190
190
|
};
|
|
191
191
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.49",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.49",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.49",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -9046,5 +9046,21 @@
|
|
|
9046
9046
|
"id": "1004562",
|
|
9047
9047
|
"text": "Walked every building and floor desk of the Governor City in light, dark and on a phone, and fixed what did not match the design: number tiles now say what they count, a cleared floor no longer shows a stale count, counts read"
|
|
9048
9048
|
}
|
|
9049
|
+
],
|
|
9050
|
+
"1.21.48": [
|
|
9051
|
+
{
|
|
9052
|
+
"id": "1004576",
|
|
9053
|
+
"text": "Saying \"Bongos feedback: …\" or suggesting how Bongos should behave now reliably sends it to the Bongos team; an unused leftover skill that confused this was removed."
|
|
9054
|
+
},
|
|
9055
|
+
{
|
|
9056
|
+
"id": "1004573",
|
|
9057
|
+
"text": "A project brought in from an existing repo now always picks up the sign-in and settings the platform gives it, instead of silently ignoring them."
|
|
9058
|
+
}
|
|
9059
|
+
],
|
|
9060
|
+
"1.21.49": [
|
|
9061
|
+
{
|
|
9062
|
+
"id": "1004575",
|
|
9063
|
+
"text": "New builders get setup steps that actually work end to end: the sign-in commands in Settings and the primer now run from a fresh copy of the project, the primer's terminal commands display properly, and the setup no longer lea"
|
|
9064
|
+
}
|
|
9049
9065
|
]
|
|
9050
9066
|
}
|
|
@@ -346,6 +346,9 @@ const STARTER_COMMANDS = [
|
|
|
346
346
|
'bongos release:*',
|
|
347
347
|
'bongos reauth:*',
|
|
348
348
|
'bongos paste-token:*',
|
|
349
|
+
// What Settings → CLI access hands out since task 1004575: a clone has no
|
|
350
|
+
// bare `bongos` on PATH, so the copied command runs through npx.
|
|
351
|
+
'npx bongos paste-token:*',
|
|
349
352
|
'bongos exec scripts/gds/api.js:*',
|
|
350
353
|
// The read-only subset (task 1003992, ADR 0304). `git branch` and `git fetch`
|
|
351
354
|
// are exact forms on purpose: a flag turns either into a write or an exec. No
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// scripts/gds/pack-env-prefix.js — which env-var names a hosted project reads, and
|
|
4
|
+
// whether its own pack agrees with what the provisioning runner writes (task 1004573).
|
|
5
|
+
//
|
|
6
|
+
// WHY. The runner writes every hosted project's /etc/<slug>/web.env under ONE prefix
|
|
7
|
+
// (provisionedEnvPrefix). A project ADOPTED from an existing repo brings its own
|
|
8
|
+
// config/branding.json, and mercury's said envPrefix CLOUDBONGOS while web.env carried
|
|
9
|
+
// BONGOS_* — so the project read none of its sign-in credentials, its address or any
|
|
10
|
+
// setting, and the only symptom anyone saw was "did not report sign-in ready".
|
|
11
|
+
//
|
|
12
|
+
// Its own file because both provision-units.js (web.env) and provision-repo.js (the
|
|
13
|
+
// adopt scaffold) need it, and provision-units already requires provision-repo — a
|
|
14
|
+
// helper living in either would make a require cycle. Requires only provision-config.
|
|
15
|
+
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { REPO_ROOT } = require('./provision-config.js');
|
|
19
|
+
|
|
20
|
+
// The pack whose envPrefix a provisioned instance inherits unless its own pack says
|
|
21
|
+
// otherwise (see the long note on ENV_PREFIX_PACK's history in provision-units.js).
|
|
22
|
+
const ENV_PREFIX_PACK = 'config/branding.neutral.json';
|
|
23
|
+
|
|
24
|
+
// The env-var prefix a provisioned instance actually RESOLVES — i.e. the envPrefix
|
|
25
|
+
// of the pack it loads (ENV_PREFIX_PACK). src/instance-config.resolveEnv
|
|
26
|
+
// reads <envPrefix>_<SUFFIX>, and src/branding.resolveBranding applies
|
|
27
|
+
// <envPrefix>_{PUBLIC,BUILDERS,…}_ORIGIN overrides — so a web.env var only takes
|
|
28
|
+
// effect under THIS prefix. Read from the pack (fail-soft to the vanilla default)
|
|
29
|
+
// so it stays in lockstep instead of a magic string. (The prior slug-based prefix
|
|
30
|
+
// was a latent bug: it never matched the neutral pack's CLOUDBONGOS envPrefix, so
|
|
31
|
+
// the OAuth creds were silently unreadable — task 1972.)
|
|
32
|
+
function provisionedEnvPrefix() {
|
|
33
|
+
const { FALLBACK_ENV_PREFIX } = require('../../src/instance-config'); // eslint-disable-line global-require
|
|
34
|
+
try {
|
|
35
|
+
const pack = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, ENV_PREFIX_PACK), 'utf8'));
|
|
36
|
+
return pack.envPrefix || FALLBACK_ENV_PREFIX;
|
|
37
|
+
} catch { return FALLBACK_ENV_PREFIX; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The envPrefix a pack's text names, or null (no pack, not JSON, no envPrefix).
|
|
41
|
+
function packEnvPrefix(packText) {
|
|
42
|
+
try {
|
|
43
|
+
const pack = JSON.parse(String(packText || ''));
|
|
44
|
+
return pack && typeof pack.envPrefix === 'string' ? pack.envPrefix : null;
|
|
45
|
+
} catch { return null; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Does the instance's OWN pack read the names this runner writes? PURE: give it the
|
|
49
|
+
// pack's text (or null when it could not be read) and the prefix written; it returns
|
|
50
|
+
// a sentence naming the mismatch, or null when the names agree or the pack says nothing.
|
|
51
|
+
function envPrefixMismatch(packText, written = provisionedEnvPrefix()) {
|
|
52
|
+
const own = packEnvPrefix(packText);
|
|
53
|
+
if (!own || own === written) return null;
|
|
54
|
+
return `the project's config/branding.json reads settings named ${own}_*, but the platform wrote ${written}_* — set its envPrefix to "${written}"`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The adopt scaffold's half of the fix: rewrite ONLY the envPrefix value in an adopted
|
|
58
|
+
// repo's config/branding.json so the project reads the names this runner writes,
|
|
59
|
+
// leaving every other byte of the owner's file as they wrote it (a JSON round-trip
|
|
60
|
+
// would reflow it). PURE. Returns { text, from } when it changed something, or null
|
|
61
|
+
// when the names already agree, the pack names no prefix, or it is not JSON.
|
|
62
|
+
function normalizePackEnvPrefix(packText, written = provisionedEnvPrefix()) {
|
|
63
|
+
const raw = String(packText || '');
|
|
64
|
+
const own = packEnvPrefix(raw);
|
|
65
|
+
if (!own || own === written) return null;
|
|
66
|
+
const re = /("envPrefix"\s*:\s*)"[^"]*"/;
|
|
67
|
+
if (!re.test(raw)) return null;
|
|
68
|
+
const text = raw.replace(re, (_m, key) => `${key}"${written}"`);
|
|
69
|
+
if (packEnvPrefix(text) !== written) return null;
|
|
70
|
+
return { text, from: own };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { ENV_PREFIX_PACK, provisionedEnvPrefix, envPrefixMismatch, normalizePackEnvPrefix };
|
|
@@ -19,6 +19,7 @@ const { CONFIG, REPO_ROOT, coreVersionSafe, provisionerBotEmail, provisionerBotN
|
|
|
19
19
|
// upgrade.js can reach these three without pulling provision*.js into the merge
|
|
20
20
|
// resolver's executed closure; re-exported below so every existing caller is unchanged.
|
|
21
21
|
const { instanceRepoRemote, parseTargetRef, resolveOwnerGithubToken } = require('./provision-repo-auth.js');
|
|
22
|
+
const { normalizePackEnvPrefix } = require('./pack-env-prefix.js'); // an adopted pack's envPrefix (task 1004573)
|
|
22
23
|
const { CORE_PKG, compareSemver, isStable } = require('./update-channel.js');
|
|
23
24
|
|
|
24
25
|
function dbName(inst) { return inst.db_name || inst.slug; }
|
|
@@ -832,6 +833,18 @@ async function scaffoldStandaloneRepo(inst, deps) {
|
|
|
832
833
|
} else {
|
|
833
834
|
writeFile(giPath, INSTANCE_GITIGNORE);
|
|
834
835
|
}
|
|
836
|
+
// Adopt layers config only where ABSENT, so an owner's existing config/branding.json
|
|
837
|
+
// keeps whatever envPrefix it carried — and web.env is written under the platform's.
|
|
838
|
+
// Mercury's said CLOUDBONGOS: it read none of its credentials or settings (task 1004573).
|
|
839
|
+
// Rewrite just that one value, in this same commit, so the repo and web.env agree.
|
|
840
|
+
const packPath = path.join(root, 'config', 'branding.json');
|
|
841
|
+
if (mode === 'adopt' && fs.existsSync(packPath)) {
|
|
842
|
+
const fixed = normalizePackEnvPrefix(fs.readFileSync(packPath, 'utf8'));
|
|
843
|
+
if (fixed) {
|
|
844
|
+
writeFile(packPath, fixed.text);
|
|
845
|
+
log(` [scaffold] config/branding.json envPrefix ${fixed.from} → the platform's, so the project reads the settings it is given`);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
835
848
|
exec(`git -C ${root} config user.name "${provisionerBotName()}"`);
|
|
836
849
|
exec(`git -C ${root} config user.email "${provisionerBotEmail()}"`);
|
|
837
850
|
exec(`git -C ${root} add -A`);
|
|
@@ -162,23 +162,10 @@ WantedBy=timers.target
|
|
|
162
162
|
// branding-file env line is written at all. Reading the neutral pack HERE is still correct: it is
|
|
163
163
|
// the prefix floor an instance inherits unless its own pack overrides envPrefix, and every live
|
|
164
164
|
// co-tenant pack pins `envPrefix: "CLOUDBONGOS"` explicitly.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
// reads <envPrefix>_<SUFFIX>, and src/branding.resolveBranding applies
|
|
170
|
-
// <envPrefix>_{PUBLIC,BUILDERS,…}_ORIGIN overrides — so a web.env var only takes
|
|
171
|
-
// effect under THIS prefix. Read from the pack (fail-soft to the vanilla default)
|
|
172
|
-
// so it stays in lockstep instead of a magic string. (The prior slug-based prefix
|
|
173
|
-
// was a latent bug: it never matched the neutral pack's CLOUDBONGOS envPrefix, so
|
|
174
|
-
// the OAuth creds were silently unreadable — task 1972.)
|
|
175
|
-
function provisionedEnvPrefix() {
|
|
176
|
-
const { FALLBACK_ENV_PREFIX } = require('../../src/instance-config');
|
|
177
|
-
try {
|
|
178
|
-
const pack = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, ENV_PREFIX_PACK), 'utf8'));
|
|
179
|
-
return pack.envPrefix || FALLBACK_ENV_PREFIX;
|
|
180
|
-
} catch { return FALLBACK_ENV_PREFIX; }
|
|
181
|
-
}
|
|
165
|
+
// ENV_PREFIX_PACK + provisionedEnvPrefix (and the task-1004573 pack checks) live in
|
|
166
|
+
// pack-env-prefix.js — provision-repo.js needs them too, and it cannot require this file
|
|
167
|
+
// (this file requires it). Re-exported below so every existing caller is unchanged.
|
|
168
|
+
const { ENV_PREFIX_PACK, provisionedEnvPrefix, envPrefixMismatch, normalizePackEnvPrefix } = require('./pack-env-prefix.js');
|
|
182
169
|
|
|
183
170
|
// The per-instance secrets file body (the standup runbook's /etc/<inst>/web.env).
|
|
184
171
|
// The OAuth client id/secret are GUIDED/manual inputs (no GitHub create-API) — the
|
|
@@ -430,6 +417,8 @@ function renamedEnvCarry(body) {
|
|
|
430
417
|
|
|
431
418
|
module.exports = {
|
|
432
419
|
ENV_PREFIX_PACK,
|
|
420
|
+
envPrefixMismatch,
|
|
421
|
+
normalizePackEnvPrefix,
|
|
433
422
|
backupScriptPath,
|
|
434
423
|
backupService,
|
|
435
424
|
backupServicePath,
|
package/scripts/gds/provision.js
CHANGED
|
@@ -50,7 +50,7 @@ const crypto = require('node:crypto');
|
|
|
50
50
|
const retry = require('./intent-retry.js'); // when a failed intent is tried again (task 1004447)
|
|
51
51
|
const { CONFIG, MANIFEST_UA, softFailResult, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, MAX_INTENT_ATTEMPTS, REPO_ROOT, coreVersionSafe, hasFlag, loadDeps, oauthSecret, provisionerBotEmail } = require('./provision-config.js');
|
|
52
52
|
const { accountPreflight, appUser, alreadyScaffolded, buildCorePinRefreshCommit, coreCheckoutHasGit, dbCreateCmd, dbName, dbRoleCmd, dbRoleCmdShown, dbRoleGrantsCmd, ensureInstanceUserCmd, generateDbPassword, grantInstanceRepoReadCmd, instanceDbRole, instanceStateDir, instanceUser, deployKeyPath, deployKeyTitle, ensurePrivateRepoAccess, installedCorePackDir, installedCoreTarball, instanceInitSpec, instanceRepoRemote, migrateCmd, onboardMode, ownerLoginOf, parseTargetRef, planCorePinRefresh, refreshStandaloneCorePin, resolveOwnerGithubToken, resolveVendorableCoreTarball, safeVersionLabel, scaffoldStandaloneRepo, seedFirstVersionCmd, standaloneInstallCmd, standaloneMigrateCmd, standalonePullCmd, standaloneRegenDocsCmd, standaloneRoot } = require('./provision-repo.js');
|
|
53
|
-
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, renamedEnvCarry, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
53
|
+
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, envPrefixMismatch, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, renamedEnvCarry, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
54
54
|
const { caddyBlock, caddySnippetPath, checkCaddyWiring, checkDnsTokenScope, classifyZoneScope, dnsReleaseEnv, dnsUpsertEnv, federateInstance, federationHubOrigin, healthzCmd, identityCmd, identityVerdict, ingressPlaneGap, ingressPreflight, shouldFederate, zoneForDomain } = require('./provision-net.js');
|
|
55
55
|
const { resolveProcessManager } = require('./provision-process.js');
|
|
56
56
|
const { capacityPreflight, statusCapacityLine } = require('./provision-capacity.js');
|
|
@@ -1097,7 +1097,13 @@ async function placeManifestCreds(inst, mrow, deps) {
|
|
|
1097
1097
|
if (!authConfigured) {
|
|
1098
1098
|
// The App now EXISTS on the owner's account — keep its id + slug on the row so
|
|
1099
1099
|
// the wizard can name it and say why "try again" makes a second one (task 1004572).
|
|
1100
|
-
|
|
1100
|
+
// Say WHY when we can see it: an adopted repo's own pack may read other names (task 1004573).
|
|
1101
|
+
// A cloud-host checkout lives on the control plane (the scaffold reads it the same way), so
|
|
1102
|
+
// this is a plain file read — no shell. An unreadable or non-JSON pack names nothing.
|
|
1103
|
+
let packText = null;
|
|
1104
|
+
if (standalone) { try { packText = (deps.readFile || fs.readFileSync)(path.join(standaloneRoot(inst), 'config', 'branding.json'), 'utf8'); } catch { packText = null; } }
|
|
1105
|
+
const why = envPrefixMismatch(packText);
|
|
1106
|
+
return { ok: false, codeSpent: true, appId: creds.appId, appSlug: creds.appSlug, error: `the credentials are placed, but the project did not report sign-in ready in time${why ? ` — ${why}` : ''}` };
|
|
1101
1107
|
}
|
|
1102
1108
|
log(' [oauth-manifest] ✓ /healthz auth_configured=true — sign-in is live');
|
|
1103
1109
|
} else {
|
|
@@ -1306,17 +1306,18 @@ module.exports = function buildAuthRouter() {
|
|
|
1306
1306
|
// THERE — on an instance, where the core is an installed dependency and
|
|
1307
1307
|
// `scripts/gds/` does not exist, so the previous
|
|
1308
1308
|
// `node scripts/gds/paste-token.js <token>` named a file the builder did not
|
|
1309
|
-
// have and the copy button handed out a command that could not run.
|
|
1310
|
-
// is
|
|
1311
|
-
//
|
|
1309
|
+
// have and the copy button handed out a command that could not run. The verb
|
|
1310
|
+
// is run through `npx` (task 1004575): a builder's clone does not put bare
|
|
1311
|
+
// `bongos` on PATH, and `npx bongos` resolves the project's own bin there.
|
|
1312
|
+
// The hall tells them to paste it in their project folder.
|
|
1312
1313
|
//
|
|
1313
1314
|
// The --api-base flag carries the issuer above. Omitted when this instance
|
|
1314
1315
|
// cannot state its own origin, because a flag with an empty value would be
|
|
1315
1316
|
// worse than none: paste-token would take '' as an explicit instruction and
|
|
1316
1317
|
// stop falling back to the resolution that works today.
|
|
1317
1318
|
paste_command: issuer
|
|
1318
|
-
? `bongos paste-token ${token} --api-base ${issuer}`
|
|
1319
|
-
: `bongos paste-token ${token}`,
|
|
1319
|
+
? `npx bongos paste-token ${token} --api-base ${issuer}`
|
|
1320
|
+
: `npx bongos paste-token ${token}`,
|
|
1320
1321
|
});
|
|
1321
1322
|
} catch (err) {
|
|
1322
1323
|
log.error('[gds] POST /auth/cli-token/issue', err);
|
package/src/instance-config.js
CHANGED
|
@@ -190,7 +190,7 @@ function readConfigFileSync(name, { encoding = 'utf8', b = safeBrand() } = {}) {
|
|
|
190
190
|
// per process naming the old spelling, the new one, and the release the old
|
|
191
191
|
// stops working in; see warnLegacyEnv below. `warn` is injectable so a test can
|
|
192
192
|
// assert the warning fires rather than only that the value resolves.
|
|
193
|
-
function resolveEnv(suffix, { env = process.env, prefix = envPrefix(), legacy = LEGACY_ENV_PREFIXES, warn = warnLegacyEnv } = {}) {
|
|
193
|
+
function resolveEnv(suffix, { env = process.env, prefix = envPrefix(), legacy = LEGACY_ENV_PREFIXES, warn = warnLegacyEnv, warnPack = warnPackPrefix } = {}) {
|
|
194
194
|
const canonical = `${prefix}_${suffix}`;
|
|
195
195
|
const canonicalValue = prefix ? env[canonical] : undefined;
|
|
196
196
|
const canonicalSet = canonicalValue !== undefined && canonicalValue !== '';
|
|
@@ -200,6 +200,22 @@ function resolveEnv(suffix, { env = process.env, prefix = envPrefix(), legacy =
|
|
|
200
200
|
// on the winner would leave the "both set" operator with no notice at all.
|
|
201
201
|
const seen = new Set([prefix].filter(Boolean));
|
|
202
202
|
let winner = canonicalSet ? canonicalValue : undefined;
|
|
203
|
+
// THE PLATFORM'S SPELLING IS ALWAYS HEARD (task 1004573). The platform writes
|
|
204
|
+
// every hosted project's web.env as BONGOS_* whatever that project's own pack
|
|
205
|
+
// says. A project adopted from a repo whose config/branding.json still carried
|
|
206
|
+
// envPrefix CLOUDBONGOS read none of it — not its sign-in credentials, not its
|
|
207
|
+
// address, not one setting its owner chose — and nothing said so (mercury,
|
|
208
|
+
// 2026-10-04). So when the pack names another prefix, BONGOS_* is read right
|
|
209
|
+
// after it, ahead of the legacy list. The pack's own spelling still wins.
|
|
210
|
+
if (prefix && prefix !== FALLBACK_ENV_PREFIX) {
|
|
211
|
+
seen.add(FALLBACK_ENV_PREFIX);
|
|
212
|
+
const name = `${FALLBACK_ENV_PREFIX}_${suffix}`;
|
|
213
|
+
const v = env[name];
|
|
214
|
+
if (!canonicalSet && v !== undefined && v !== '') {
|
|
215
|
+
winner = v;
|
|
216
|
+
warnPack({ prefix, name });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
203
219
|
for (const pfx of legacy) {
|
|
204
220
|
if (!pfx || seen.has(pfx)) continue;
|
|
205
221
|
seen.add(pfx);
|
|
@@ -229,6 +245,17 @@ function warnLegacyEnv({ legacyName, canonicalName, shadowed, differs, log = con
|
|
|
229
245
|
return true;
|
|
230
246
|
}
|
|
231
247
|
|
|
248
|
+
// One line per process when a value had to come from the platform's BONGOS_*
|
|
249
|
+
// spelling because this project's pack names another prefix. Not a deprecation —
|
|
250
|
+
// BONGOS_ is the canonical name — so it says what to change instead: the pack.
|
|
251
|
+
let _packPrefixWarned = false;
|
|
252
|
+
function warnPackPrefix({ prefix, name, log = console } = {}) {
|
|
253
|
+
if (_packPrefixWarned) return false;
|
|
254
|
+
_packPrefixWarned = true;
|
|
255
|
+
log.warn(`[bongos] config/branding.json sets envPrefix "${prefix}", but ${name} (the platform's spelling) is what is set — reading it. Set envPrefix to "${FALLBACK_ENV_PREFIX}" to match.`);
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
232
259
|
// NOT exported, deliberately: warnLegacyEnv, LEGACY_ENV_SUNSET_RELEASE and the
|
|
233
260
|
// ledger are internals. resolveEnv's injectable `warn` is the whole test seam a
|
|
234
261
|
// caller needs, and a test of the DEFAULT path captures console.warn — which
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.49'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
package/tests/bongos_cli.mjs
CHANGED
|
@@ -75,7 +75,8 @@ test('the server emits exactly the verb the CLI registers', () => {
|
|
|
75
75
|
const cmds = [...tail.matchAll(/`([^`]*bongos paste-token[^`]*)`/g)].map((x) => x[1]);
|
|
76
76
|
assert.ok(cmds.length > 0, 'auth.js should emit at least one paste_command template literal');
|
|
77
77
|
for (const cmd of cmds) {
|
|
78
|
-
|
|
78
|
+
// `npx bongos <verb> …` since task 1004575 — a clone has no bare `bongos` on PATH.
|
|
79
|
+
const verb = cmd.replace(/^npx /, '').split(' ')[1];
|
|
79
80
|
assert.ok(cli.VERBS[verb], `server emits "bongos ${verb}" but the CLI has no such verb`);
|
|
80
81
|
}
|
|
81
82
|
});
|
|
@@ -210,6 +210,10 @@ const GIT_GH_COMMANDS = [
|
|
|
210
210
|
'git branch --list', 'git branch --show-current', 'git branch -a', 'git branch -vv',
|
|
211
211
|
'git fetch', 'git fetch origin', 'git fetch origin main',
|
|
212
212
|
'gh pr view:*', 'gh pr checks:*', 'gh pr list:*', 'gh run view:*', 'gh run list:*',
|
|
213
|
+
// Not git/gh, but the one non-script verb a session is handed to paste: Settings →
|
|
214
|
+
// CLI access gives `npx bongos paste-token <token>` (task 1004575), a blessed writer
|
|
215
|
+
// (see BLESSED_WRITERS) spelled the way a clone can run it.
|
|
216
|
+
'npx bongos paste-token:*',
|
|
213
217
|
];
|
|
214
218
|
// gds CLIs audited read-only under EVERY flag they take — a prefix rule allows them all.
|
|
215
219
|
// Audited and left OUT: backlog-review.js (POSTs from its default walk) and
|
|
@@ -176,8 +176,10 @@ t('paste_command names a CLI VERB, never a filesystem path', () => {
|
|
|
176
176
|
const cmds = [...tail.matchAll(/`([^`]*bongos paste-token[^`]*)`/g)].map((m) => m[1]);
|
|
177
177
|
assert.equal(cmds.length, 2, `expected both ternary branches, got ${cmds.length}`);
|
|
178
178
|
for (const cmd of cmds) {
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
// Through npx (task 1004575): a builder's clone does not put bare `bongos` on
|
|
180
|
+
// PATH, so a bare verb was "command not found" when pasted in the project folder.
|
|
181
|
+
assert.ok(/^npx bongos paste-token /.test(cmd),
|
|
182
|
+
`paste_command must start with the npx verb form, got: ${cmd}`);
|
|
181
183
|
// The invariant is "names no FILE ON THE RECIPIENT'S DISK", which the original
|
|
182
184
|
// spelling approximated as "contains no slash". That was too broad once the command
|
|
183
185
|
// had to carry the issuer URL (task 1003722) — `https://host` is not a filesystem
|
|
@@ -208,7 +210,7 @@ t('paste_command carries the issuing instance, so the CLI cannot guess wrong', (
|
|
|
208
210
|
t('an instance that cannot state its own origin omits the flag rather than sending an empty one', () => {
|
|
209
211
|
// `--api-base ''` would read at the far end as an explicit instruction and suppress
|
|
210
212
|
// the fallback that works today — strictly worse than sending no flag at all.
|
|
211
|
-
assert.ok(/:\s*`bongos paste-token \$\{token\}`/.test(issueBlock),
|
|
213
|
+
assert.ok(/:\s*`npx bongos paste-token \$\{token\}`/.test(issueBlock),
|
|
212
214
|
'there must be a flagless branch for the empty-issuer case');
|
|
213
215
|
});
|
|
214
216
|
|