@bongos/core 1.21.70 → 1.21.72
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 +123 -63
- package/.claude/skills/goal-create/SKILL.md +21 -3
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +5 -3
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0368-red-and-blue-teams-practice-on-a-range-never-on-live.md +99 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +46 -5
- package/docs/api-reference.md +4 -3
- package/docs/copy-inventory.md +388 -377
- package/docs/copy-registry.json +567 -448
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +912 -931
- package/docs/recipes/core-release-pipeline.md +6 -0
- package/docs/recipes/upgrading-the-core.md +2 -0
- package/modules/hall-ui/public/settings-software-update.js +77 -2
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/lifecycle/db-tasks.js +12 -0
- package/modules/lifecycle/goal-spec-task.js +129 -0
- package/modules/lifecycle/routes/goals.js +7 -9
- package/modules/lifecycle/routes/task-write-routes.js +11 -1
- package/modules/lifecycle/upkeep-home.js +55 -0
- package/modules/public-landing/public/projects-hall-live.states.json +7 -1
- package/modules/public-landing/public/projects.html +581 -0
- package/modules/public-landing/public/projects.probes.json +2 -2
- package/modules/public-landing/public/projects.states.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +43 -0
- package/scripts/gds/claude-materialize.js +125 -31
- package/scripts/gds/claude-md-template.js +47 -10
- package/scripts/gds/cli-lib.js +4 -4
- package/scripts/gds/feedback-send.js +3 -3
- package/scripts/gds/paste-token.js +1 -1
- package/scripts/gds/provision-net.js +1 -8
- package/scripts/gds/provision-units.js +28 -2
- package/scripts/gds/reauth.js +1 -1
- package/scripts/gds/run-unit-tests.js +3 -0
- package/scripts/gds/setup.js +1 -1
- package/scripts/gds/start.js +4 -4
- package/scripts/gds/surface.js +15 -3
- package/scripts/gds/task.js +32 -5
- package/scripts/gds/upgrade-operator.js +73 -0
- package/scripts/gds/upgrade.js +7 -2
- package/scripts/hall-preview/server.js +3 -0
- package/src/bongos/routes/core-update.js +98 -4
- package/src/bongos/routes/feedback.js +11 -1
- package/src/bongos/upgrade-request.js +158 -0
- package/src/module-api.js +1 -1
- package/tests/claude_materialize.mjs +195 -18
- package/tests/cli_allowlist_permissions.mjs +6 -2
- package/tests/cli_surface.mjs +2 -2
- package/tests/fixtures/page-ledger.mjs +5 -0
- package/tests/goal_admission_override.mjs +1 -1
- package/tests/goal_category.mjs +1 -1
- package/tests/goal_create_spec_entry.mjs +61 -0
- package/tests/goal_spec_task.mjs +197 -0
- package/tests/goal_spec_task_db.mjs +95 -0
- package/tests/projects_hub_cloud_hop.mjs +303 -0
- package/tests/provision_settings_apply.mjs +25 -1
- package/tests/software_update.mjs +18 -1
- package/tests/start_env_guard.mjs +2 -2
- package/tests/upgrade_operator.mjs +105 -0
- package/tests/upgrade_request.mjs +348 -0
- package/tests/upkeep_home.mjs +156 -0
|
@@ -167,9 +167,9 @@
|
|
|
167
167
|
{ "name": "the review (tasks 1004504, 1004550): Edit opens a row in place, Done confirms it, and Continue is the one confirmation for the rest", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
168
168
|
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",200],["click","[data-phys-solo=\"many\"]"],["wait",600],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",200]],
|
|
169
169
|
"expect": {"visible":["#panel-review","#reviewRows .setRow[data-row=\"rewards\"]","#reviewRows .setRow.done[data-row=\"team\"]"]} },
|
|
170
|
-
{ "name": "the wizard driver (tasks 1003346 and 1004504): the fork, what, the checks, every review row confirmed (the stub grants repo access, so the new repository is named from the project; the dotless stub host takes a domain the founder owns), the look, then the filing — the done panel with the
|
|
170
|
+
{ "name": "the wizard driver (tasks 1003346 and 1004504): the fork, what, the checks, every review row confirmed (the stub grants repo access, so the new repository is named from the project; the dotless stub host takes a domain the founder owns), the look, then the filing — the done panel waits with the cloud hop and its status line (task 1004578), the strip and the locked checklist standing down", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
171
171
|
"steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],
|
|
172
|
-
"expect": {"visible":["#panel-done","#
|
|
172
|
+
"expect": {"visible":["#panel-done","#hop","#hopCanvas","#hopStatus","#hopJump"],"hidden":["#panel-look","#panel-review","#liveProgress","#humanSteps"],"text":[["#done-h","Setting up Mercury"],["#hopStatus","Raising the server, database, and address — nothing needed from you."]]} },
|
|
173
173
|
{ "name": "the front door at 390: the fork's two doors stack inside one pill", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
174
174
|
"steps": [["wait",600],["click","#termsAck"],["wait",100]],
|
|
175
175
|
"expect": {"visible":["#panel-fork",".doorPill","#wizDemo","#wizStart"]} },
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"in-new-review-run": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-chg]"],["wait",300],["click","[data-phys=\"framing\"][data-val=\"hybrid\"]"],["wait",600]],"expect":{"visible":["#physSuite"]}},
|
|
58
58
|
"in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200]],"expect":{"visible":["#reviewGo","#reviewRows .setRow.done"],"text":[["#reviewGo","Looks right, continue"]]}},
|
|
59
59
|
"in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500]],"expect":{"visible":["#panel-look","#wizLook","#wizRecap","#doCreate"],"hidden":["#panel-review"],"attr":[["#wizRoom","data-screen","look"]]}},
|
|
60
|
-
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#
|
|
60
|
+
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#hop","#hopCanvas","#hopStatus","#hopJump"],"hidden":["#panel-look","#qWrap","#wizBack","#wizInvite","#liveProgress","#humanSteps","#wizInviteLater","#stepPager"]}},
|
|
61
61
|
"in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-fork","#wizResume","#wizResumeGo"],"hidden":["#panel-done"],"text":[["#wizResumeName","Mercury"]]}},
|
|
62
62
|
"in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#panel-look","#wizAccent","#wizAccentNote"],"hidden":["#panel-review"]}},
|
|
63
63
|
"out-new": {"auth":false,"url":"/projects?view=new","expect":{"visible":["#panel-gate","#wizSignin"],"hidden":["#qWrap","#panel-fork"]}},
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.72",
|
|
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.72",
|
|
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.72",
|
|
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
|
@@ -9199,5 +9199,48 @@
|
|
|
9199
9199
|
{
|
|
9200
9200
|
"text": "Reporting a problem with Bongos now works from every project, including ones like Will's that could not send feedback before. It takes a second, needs no extra sign-in, and the Bongos team can see who really sent each report."
|
|
9201
9201
|
}
|
|
9202
|
+
],
|
|
9203
|
+
"1.21.71": [
|
|
9204
|
+
{
|
|
9205
|
+
"id": "1004595",
|
|
9206
|
+
"text": "A hosted project's Software update page no longer stops at \"Update ready\". When the people who run its server do the upgrade, the owner now sees who that is and an \"Ask for this upgrade\" button; one press reaches the Cloud Bon"
|
|
9207
|
+
},
|
|
9208
|
+
{
|
|
9209
|
+
"id": "1004621",
|
|
9210
|
+
"text": "The assistant in a Bongos project no longer starts every session with a failed command. Its instructions now say `npx bongos …`, which works as soon as the project is installed, instead of a bare `bongos` that the computer cou"
|
|
9211
|
+
},
|
|
9212
|
+
{
|
|
9213
|
+
"id": "1004586",
|
|
9214
|
+
"text": "The goal-planning assistant can now finish a goal that was started in the hall: it works out the goal's scope, its 'done when' checks and its first tasks, then releases the goal's work."
|
|
9215
|
+
},
|
|
9216
|
+
{
|
|
9217
|
+
"id": "1004622",
|
|
9218
|
+
"text": "Fixed a broken test that was stopping every new Bongos release from being published."
|
|
9219
|
+
},
|
|
9220
|
+
{
|
|
9221
|
+
"id": "1004596",
|
|
9222
|
+
"text": "When someone on a hosted project (like Will's) asks to upgrade from their own computer, Bongos now tells them plainly that the project's operator does upgrades, and how to ask for one — from the Software update page or with on"
|
|
9223
|
+
},
|
|
9224
|
+
{
|
|
9225
|
+
"id": "1004619",
|
|
9226
|
+
"text": "Bongos now has a written plan for red and blue security teams: attacks happen on a separate practice server, never on the live site, and every release gets checked there."
|
|
9227
|
+
},
|
|
9228
|
+
{
|
|
9229
|
+
"id": "1004597",
|
|
9230
|
+
"text": "Recording routine Bongos upkeep, such as a core upgrade, no longer gets refused just because none of a project's goals fit. Filed as upkeep with no goal, it now goes into the version's standing maintenance goal. Any other task"
|
|
9231
|
+
},
|
|
9232
|
+
{
|
|
9233
|
+
"id": "1004585",
|
|
9234
|
+
"text": "A goal created from the hall now arrives with a 'Spec goal N' task. Nothing else in that goal can be picked up until someone finishes it, so a goal never sits half-defined."
|
|
9235
|
+
},
|
|
9236
|
+
{
|
|
9237
|
+
"text": "Hosted projects like Will's now get a working Update button in Settings, instead of being told to run a command on a server they don't have access to."
|
|
9238
|
+
}
|
|
9239
|
+
],
|
|
9240
|
+
"1.21.72": [
|
|
9241
|
+
{
|
|
9242
|
+
"id": "1004578",
|
|
9243
|
+
"text": "While a new project is being set up, founders now get a little cloud-hopping game: each cloud is a public project on the platform, in its own planet colour. When the project goes live, clouds and stars fill the screen and fade"
|
|
9244
|
+
}
|
|
9202
9245
|
]
|
|
9203
9246
|
}
|
|
@@ -33,7 +33,7 @@ const { spawnSync } = require('node:child_process');
|
|
|
33
33
|
const { VERBS } = require('../../bin/bongos.js'); // side-effect-free require (guarded main)
|
|
34
34
|
const ic = require('../../src/instance-config'); // neutral configDir/envPrefix fallbacks (never a founder literal)
|
|
35
35
|
const { isCorePackageName, CORE_PACKAGE_NAMES } = require('../../src/module-loader/catalog'); // one definition of "this checkout IS the core"
|
|
36
|
-
const { renderInstanceClaudeMd } = require('./claude-md-template'); // task 2104 — the starter CLAUDE.md
|
|
36
|
+
const { renderInstanceClaudeMd, refreshStarterCliLines } = require('./claude-md-template'); // task 2104 — the starter CLAUDE.md
|
|
37
37
|
|
|
38
38
|
// script basename (e.g. "ship.js") → dedicated bongos verb, for verbs that map to a bare script with
|
|
39
39
|
// NO prefixArgs. Verbs whose script is driven by prefixArgs (box → api.js) are EXCLUDED so a generic
|
|
@@ -61,16 +61,40 @@ const GDS_INVOCATION_RE = new RegExp(LAUNCHER + String.raw`\s+"?(?:\$CLAUDE_PROJ
|
|
|
61
61
|
// A non-gds core script under scripts/ (rare in skills, but keep the transform total).
|
|
62
62
|
const OTHER_SCRIPT_RE = new RegExp(LAUNCHER + String.raw`\s+"?(?:\$CLAUDE_PROJECT_DIR\/|\.\/)?scripts\/((?!gds\/)[a-zA-Z0-9_./-]+\.js)"?`, 'g');
|
|
63
63
|
|
|
64
|
-
//
|
|
65
|
-
//
|
|
64
|
+
// How a project runs the CLI (task 1004621). The `bongos` bin ships inside the @bongos/core
|
|
65
|
+
// dependency (node_modules/.bin/bongos), which is NOT on PATH, so a bare `bongos ship` written
|
|
66
|
+
// into a project's skills failed with `command not found` on every session's first call and the
|
|
67
|
+
// assistant had to recover by retrying through npx. `npx` finds the project's own bin right after
|
|
68
|
+
// `npm install`, with no setup, on every OS and shell — so that is the spelling a project reads.
|
|
69
|
+
const PROJECT_CLI = 'npx bongos';
|
|
70
|
+
|
|
71
|
+
// A hand-written bare `bongos <verb>` in skill prose (e.g. "`bongos paste-token <token>`"), so it
|
|
72
|
+
// takes the npx spelling too. Case-sensitive on purpose ("Bongos status" is a product noun), only a
|
|
73
|
+
// KNOWN verb, and never when already prefixed by `npx ` or glued to a path/name (`@bongos/core`,
|
|
74
|
+
// `bongos-feedback`, `bongos: command not found` all stay). The `npx ` lookbehind is what keeps a
|
|
75
|
+
// second run from writing `npx npx bongos`.
|
|
76
|
+
function bareVerbRe(verbs) {
|
|
77
|
+
const names = Object.keys(verbs || {}).filter((v) => /^[a-z][a-z-]*$/.test(v));
|
|
78
|
+
if (!names.length) return null;
|
|
79
|
+
names.sort((a, b) => b.length - a.length); // `paste-token` before a shorter prefix of it
|
|
80
|
+
return new RegExp(String.raw`(?<![\w@/.\-])(?<!npx )bongos (${names.join('|')})(?![\w-])`, 'g');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Pure: rewrite core-script invocations in a skill/settings command string to the project's CLI
|
|
84
|
+
// spelling (`npx bongos <verb>`, else `npx bongos exec scripts/gds/<x>.js`), and give any bare
|
|
85
|
+
// `bongos <verb>` the same npx prefix. Idempotent: its own output contains neither a
|
|
86
|
+
// `node scripts/…` invocation nor an un-prefixed `bongos <verb>`, so a second run changes nothing.
|
|
66
87
|
function bongosify(text, verbs = VERBS) {
|
|
67
88
|
const map = scriptToVerb(verbs);
|
|
68
|
-
|
|
89
|
+
const bare = bareVerbRe(verbs);
|
|
90
|
+
let out = String(text == null ? '' : text);
|
|
91
|
+
if (bare) out = out.replace(bare, (_whole, verb) => `${PROJECT_CLI} ${verb}`);
|
|
92
|
+
return out
|
|
69
93
|
.replace(GDS_INVOCATION_RE, (_whole, script) => {
|
|
70
94
|
const verb = map[`${script}.js`];
|
|
71
|
-
return verb ?
|
|
95
|
+
return verb ? `${PROJECT_CLI} ${verb}` : `${PROJECT_CLI} exec scripts/gds/${script}.js`;
|
|
72
96
|
})
|
|
73
|
-
.replace(OTHER_SCRIPT_RE, (_whole, rel) =>
|
|
97
|
+
.replace(OTHER_SCRIPT_RE, (_whole, rel) => `${PROJECT_CLI} exec scripts/${rel}`);
|
|
74
98
|
}
|
|
75
99
|
|
|
76
100
|
// ---- instance rebrand (task 2103) -----------------------------------------
|
|
@@ -340,34 +364,38 @@ function copyTreeTransformed(srcDir, destDir, { dryRun, onFile, overwrite = true
|
|
|
340
364
|
// path and is the agent's escape hatch, so allow-listing it would be a blanket
|
|
341
365
|
// grant wearing a narrow rule's clothes. The one script the core's own list
|
|
342
366
|
// blesses (`api.js`) is named in full instead.
|
|
367
|
+
//
|
|
368
|
+
// EVERY RULE IS THE `npx bongos` SPELLING (task 1004621): a project has no bare
|
|
369
|
+
// `bongos` on PATH, so its skills, its CLAUDE.md and Settings → CLI access
|
|
370
|
+
// (task 1004575) all say `npx bongos …`, and a bare-`bongos` rule would allow a
|
|
371
|
+
// command nothing runs. An instance that took the older bare starter gains the
|
|
372
|
+
// npx twin of each bare rule it still holds on its next upgrade
|
|
373
|
+
// (topUpNpxAllowTwins below).
|
|
343
374
|
const STARTER_COMMANDS = [
|
|
344
|
-
'bongos claim:*',
|
|
345
|
-
'bongos ship:*',
|
|
346
|
-
'bongos release:*',
|
|
347
|
-
'bongos reauth:*',
|
|
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.
|
|
375
|
+
'npx bongos claim:*',
|
|
376
|
+
'npx bongos ship:*',
|
|
377
|
+
'npx bongos release:*',
|
|
378
|
+
'npx bongos reauth:*',
|
|
351
379
|
'npx bongos paste-token:*',
|
|
352
|
-
'bongos exec scripts/gds/api.js:*',
|
|
380
|
+
'npx bongos exec scripts/gds/api.js:*',
|
|
353
381
|
// The read-only subset (task 1003992, ADR 0304). `git branch` and `git fetch`
|
|
354
382
|
// are exact forms on purpose: a flag turns either into a write or an exec. No
|
|
355
383
|
// `git diff|log|show` rule, also on purpose: the harness already runs their
|
|
356
384
|
// read-only forms unprompted, and a prefix rule would switch off its flag check
|
|
357
385
|
// (`--output=<file>` writes anywhere, `diff --no-index` reads any file).
|
|
358
386
|
// tests/cli_allowlist_permissions.mjs pins this list to the core's, rule for rule.
|
|
359
|
-
'bongos start:*',
|
|
360
|
-
'bongos status:*',
|
|
361
|
-
'bongos recall:*',
|
|
362
|
-
'bongos exec scripts/gds/me.js:*',
|
|
363
|
-
'bongos doctor:*',
|
|
364
|
-
'bongos exec scripts/gds/preflight.js:*',
|
|
365
|
-
'bongos exec scripts/gds/skill-preflight.js:*',
|
|
366
|
-
'bongos exec scripts/gds/ship-check.js:*',
|
|
367
|
-
'bongos exec scripts/gds/fitness.js:*',
|
|
368
|
-
'bongos exec scripts/gds/strand-watch.js:*',
|
|
369
|
-
'bongos exec scripts/gds/stale-worktrees.js:*',
|
|
370
|
-
'bongos exec scripts/gds/read-session-export.js:*',
|
|
387
|
+
'npx bongos start:*',
|
|
388
|
+
'npx bongos status:*',
|
|
389
|
+
'npx bongos recall:*',
|
|
390
|
+
'npx bongos exec scripts/gds/me.js:*',
|
|
391
|
+
'npx bongos doctor:*',
|
|
392
|
+
'npx bongos exec scripts/gds/preflight.js:*',
|
|
393
|
+
'npx bongos exec scripts/gds/skill-preflight.js:*',
|
|
394
|
+
'npx bongos exec scripts/gds/ship-check.js:*',
|
|
395
|
+
'npx bongos exec scripts/gds/fitness.js:*',
|
|
396
|
+
'npx bongos exec scripts/gds/strand-watch.js:*',
|
|
397
|
+
'npx bongos exec scripts/gds/stale-worktrees.js:*',
|
|
398
|
+
'npx bongos exec scripts/gds/read-session-export.js:*',
|
|
371
399
|
'git status:*',
|
|
372
400
|
'git branch --list',
|
|
373
401
|
'git branch --show-current',
|
|
@@ -473,6 +501,37 @@ function topUpStarterDeny(existingText, wanted) {
|
|
|
473
501
|
return { text: `${JSON.stringify(next, null, 2)}\n`, added, reason: null };
|
|
474
502
|
}
|
|
475
503
|
|
|
504
|
+
// topUpNpxAllowTwins — the `npx bongos` twin of every bare-`bongos` allow rule an instance's OWN
|
|
505
|
+
// settings.json holds (task 1004621). The skills an upgrade lands now say `npx bongos ship`, and a
|
|
506
|
+
// `Bash(bongos ship:*)` rule does not match that, so without the twin the upgrade would trade a
|
|
507
|
+
// `command not found` for a permission prompt on every blessed command. This is NOT the shell-rule
|
|
508
|
+
// top-up task 1004158 ruled out: nothing new is offered. Each twin is a rule the owner already
|
|
509
|
+
// holds, respelled for the one launcher that reaches the same project-local bin — a rule they
|
|
510
|
+
// removed gets no twin, and nothing they wrote is removed or rewritten. Same { text, added, reason }
|
|
511
|
+
// contract as topUpStarterDeny; an unreadable shape is left alone and reported.
|
|
512
|
+
const BARE_BONGOS_RULE_RE = /^(\w+)\(bongos (.+)\)$/;
|
|
513
|
+
function topUpNpxAllowTwins(existingText) {
|
|
514
|
+
const isObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
515
|
+
const cannotCheck = (reason) => ({ text: null, added: [], reason });
|
|
516
|
+
let doc;
|
|
517
|
+
try { doc = JSON.parse(existingText); } catch (err) { return cannotCheck(`not valid JSON (${err.message})`); }
|
|
518
|
+
if (!isObject(doc)) return cannotCheck('its top level is not a JSON object');
|
|
519
|
+
const perms = doc.permissions === undefined ? {} : doc.permissions;
|
|
520
|
+
if (!isObject(perms)) return cannotCheck('its "permissions" is not an object');
|
|
521
|
+
const allow = perms.allow === undefined ? [] : perms.allow;
|
|
522
|
+
if (!Array.isArray(allow)) return cannotCheck('its "permissions.allow" is not an array');
|
|
523
|
+
const have = new Set(allow);
|
|
524
|
+
const added = [];
|
|
525
|
+
for (const rule of allow) {
|
|
526
|
+
const m = typeof rule === 'string' && rule.match(BARE_BONGOS_RULE_RE);
|
|
527
|
+
const twin = m && `${m[1]}(${PROJECT_CLI} ${m[2]})`;
|
|
528
|
+
if (twin && !have.has(twin)) { have.add(twin); added.push(twin); }
|
|
529
|
+
}
|
|
530
|
+
if (!added.length) return { text: null, added: [], reason: null };
|
|
531
|
+
const next = { ...doc, permissions: { ...perms, allow: [...allow, ...added] } };
|
|
532
|
+
return { text: `${JSON.stringify(next, null, 2)}\n`, added, reason: null };
|
|
533
|
+
}
|
|
534
|
+
|
|
476
535
|
// The starter an instance gets when the core root carries no settings.json — a
|
|
477
536
|
// package-only install. Permissions ONLY: no hooks (they are topology), no env.
|
|
478
537
|
function renderStarterSettings(branding = {}) {
|
|
@@ -778,9 +837,26 @@ function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite =
|
|
|
778
837
|
// The read is guarded: existsSync passing does not mean readable (a directory at that path,
|
|
779
838
|
// a locked or root-owned file), and this runs AFTER the pin moved and the migrations ran, so
|
|
780
839
|
// a throw here would leave a half-applied upgrade. It fails soft, like its sibling above.
|
|
840
|
+
// The npx twins of the owner's own bare-`bongos` allow rules ride the same single read and
|
|
841
|
+
// write (task 1004621): applied first, so the deny top-up below works on — and writes — the
|
|
842
|
+
// text that already carries them. An unparseable file trips both the same way and is
|
|
843
|
+
// reported once, by the deny half.
|
|
781
844
|
let topUp;
|
|
782
|
-
|
|
783
|
-
|
|
845
|
+
let twins = { text: null, added: [], reason: null };
|
|
846
|
+
try {
|
|
847
|
+
const raw = fs.readFileSync(settingsDest, 'utf8');
|
|
848
|
+
twins = topUpNpxAllowTwins(raw);
|
|
849
|
+
topUp = topUpStarterDeny(twins.text || raw, credentialDeny(toBranding));
|
|
850
|
+
} catch (err) { topUp = { text: null, added: [], reason: `it could not be read (${err.code || err.message})` }; }
|
|
851
|
+
if (twins.added.length) {
|
|
852
|
+
summary.settingsAllowTwinsAdded = twins.added;
|
|
853
|
+
summary.notes.push(`.claude/settings.json kept every rule it already had and GAINED the \`npx bongos\` twin of ${twins.added.length} bare-\`bongos\` allow rule(s) it held — the skills now say \`npx bongos …\`, which a bare rule does not match: ${twins.added.join(', ')}`);
|
|
854
|
+
log(` ${dryRun ? '[dry-run] would add' : '✓ added'} the npx twin of ${twins.added.length} bare-bongos allow rule(s) to .claude/settings.json (skills now run \`npx bongos …\`; every rule you wrote is kept): ${twins.added.join(', ')}`);
|
|
855
|
+
if (!topUp.text && !topUp.reason) {
|
|
856
|
+
if (!dryRun) fs.writeFileSync(settingsDest, twins.text);
|
|
857
|
+
if (!dryRun) addWritten('.claude/settings.json'); // real run only — the same reasoning as the deny write below
|
|
858
|
+
}
|
|
859
|
+
}
|
|
784
860
|
if (topUp.text) {
|
|
785
861
|
if (!dryRun) fs.writeFileSync(settingsDest, topUp.text);
|
|
786
862
|
// Reported as written only on the REAL run, deliberately. writtenPaths doubles as the set
|
|
@@ -803,7 +879,7 @@ function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite =
|
|
|
803
879
|
summary.settingsDenyUncheckable = topUp.reason;
|
|
804
880
|
summary.notes.push(`.claude/settings.json could not be checked for missing deny rules — ${topUp.reason}. It was left exactly as it is, so if it predates task 1003966 its local agent can still read keys and session tokens; compare its permissions.deny against the starter by hand, or fix the file and upgrade again.`);
|
|
805
881
|
log(` ! .claude/settings.json could NOT be checked for missing deny rules — ${topUp.reason}. Left untouched; its deny half may still be missing.`);
|
|
806
|
-
} else {
|
|
882
|
+
} else if (!twins.added.length) {
|
|
807
883
|
summary.skippedFiles.push('settings.json');
|
|
808
884
|
log(' • .claude/settings.json already present — left untouched (the instance owns it; it already carries every credential deny rule. The starter\'s shelf rules are not offered here — they guard a workflow, not a secret, and are starter-only: task 1004158).');
|
|
809
885
|
}
|
|
@@ -859,9 +935,27 @@ function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite =
|
|
|
859
935
|
// branding. Written only when ABSENT — never clobber a CLAUDE.md the instance has
|
|
860
936
|
// grown (safe for init, adopt, and upgrade alike; the self-copy guard above already
|
|
861
937
|
// keeps this from ever touching the core/OTB checkout's own CLAUDE.md).
|
|
938
|
+
// One narrow exception (task 1004621): the starter's OWN sentences that told the assistant to
|
|
939
|
+
// type a bare `bongos …` — not on PATH in a project — are swapped for their `npx bongos …` form
|
|
940
|
+
// where they still read exactly as the starter wrote them (refreshStarterCliLines). Not in
|
|
941
|
+
// adopt mode, which never touches what the owner already had.
|
|
862
942
|
const claudeMdPath = path.join(instanceDir, 'CLAUDE.md');
|
|
863
943
|
if (fs.existsSync(claudeMdPath)) {
|
|
864
|
-
|
|
944
|
+
let refreshed = { changed: 0 };
|
|
945
|
+
if (overwrite) {
|
|
946
|
+
try { refreshed = refreshStarterCliLines(fs.readFileSync(claudeMdPath, 'utf8')); } catch { /* unreadable: leave it, as before */ }
|
|
947
|
+
}
|
|
948
|
+
if (refreshed.changed) {
|
|
949
|
+
if (!dryRun) fs.writeFileSync(claudeMdPath, refreshed.text);
|
|
950
|
+
// Real run only, like the settings top-up: it preserves content, so the upgrade pre-flight's
|
|
951
|
+
// "materialize overwrites this anyway" waiver must not cover an owner's uncommitted edit.
|
|
952
|
+
if (!dryRun) addWritten('CLAUDE.md');
|
|
953
|
+
summary.claudeMdCliLines = refreshed.changed;
|
|
954
|
+
summary.notes.push(`CLAUDE.md kept as the instance's own, except ${refreshed.changed} unedited starter sentence(s) now say \`npx bongos …\` — the CLI is not on PATH in a project, so a bare \`bongos\` failed with command not found.`);
|
|
955
|
+
log(` ${dryRun ? '[dry-run] would update' : '✓ updated'} ${refreshed.changed} unedited starter sentence(s) in CLAUDE.md to \`npx bongos …\` (everything else untouched)`);
|
|
956
|
+
} else {
|
|
957
|
+
summary.notes.push('CLAUDE.md already present — left untouched (the instance owns it).');
|
|
958
|
+
}
|
|
865
959
|
} else {
|
|
866
960
|
if (!dryRun) {
|
|
867
961
|
fs.mkdirSync(instanceDir, { recursive: true });
|
|
@@ -1018,4 +1112,4 @@ if (require.main === module) {
|
|
|
1018
1112
|
process.exit(s.refused.length ? 1 : 0);
|
|
1019
1113
|
}
|
|
1020
1114
|
|
|
1021
|
-
module.exports = { scriptToVerb, bongosify, transformSettings, readSettingsTransform, renderStarterSettings, STARTER_ALLOW, copyTreeTransformed, materializeClaude, materializeWillRewrite, materializeModuleSkillsIntoCore, isCoreCheckoutRoot, readModuleSkillsManifest, MODULE_SKILLS_MANIFEST, readInstanceSkillsManifest, INSTANCE_SKILLS_MANIFEST, GDS_INVOCATION_RE, buildRebrandRules, rebrandText, disabledModuleSkills, moduleSkillRoster, moduleSkillSources, vendoredSkillGate, SKILL_NAME_RE, VENDOR_DEFAULT_POLICY };
|
|
1115
|
+
module.exports = { scriptToVerb, bongosify, PROJECT_CLI, topUpNpxAllowTwins, transformSettings, readSettingsTransform, renderStarterSettings, STARTER_ALLOW, copyTreeTransformed, materializeClaude, materializeWillRewrite, materializeModuleSkillsIntoCore, isCoreCheckoutRoot, readModuleSkillsManifest, MODULE_SKILLS_MANIFEST, readInstanceSkillsManifest, INSTANCE_SKILLS_MANIFEST, GDS_INVOCATION_RE, buildRebrandRules, rebrandText, disabledModuleSkills, moduleSkillRoster, moduleSkillSources, vendoredSkillGate, SKILL_NAME_RE, VENDOR_DEFAULT_POLICY };
|
|
@@ -56,15 +56,15 @@ function renderInstanceClaudeMd(branding = {}) {
|
|
|
56
56
|
|
|
57
57
|
Read this at the start of every session. Keep it lean — it loads in full every time.
|
|
58
58
|
|
|
59
|
-
## First run — before any \`bongos\` command
|
|
59
|
+
## First run — before any \`npx bongos\` command
|
|
60
60
|
|
|
61
|
-
**If \`node_modules/@bongos/core\` is missing, run \`npm install\` first.** The \`bongos\` CLI ships *inside* that dependency
|
|
61
|
+
**If \`node_modules/@bongos/core\` is missing, run \`npm install\` first.** The \`bongos\` CLI ships *inside* that dependency and is **not on your PATH**, so it always runs through npx — \`npx bongos <command>\`, never a bare \`bongos\` (that fails with \`command not found\`). Until the install completes, every \`npx bongos\` instruction below fails too:
|
|
62
62
|
|
|
63
63
|
\`\`\`bash
|
|
64
64
|
npm install
|
|
65
65
|
\`\`\`
|
|
66
66
|
|
|
67
|
-
Once per machine, per clone — it is not committed (\`node_modules/\` is ignored on purpose), so a fresh \`git clone\` always needs it. If
|
|
67
|
+
Once per machine, per clone — it is not committed (\`node_modules/\` is ignored on purpose), so a fresh \`git clone\` always needs it. If an \`npx bongos\` command then still misbehaves, \`npx bongos doctor\` checks the toolchain and session and prints the fix.
|
|
68
68
|
|
|
69
69
|
## What this is
|
|
70
70
|
|
|
@@ -74,17 +74,17 @@ ${ident}
|
|
|
74
74
|
|
|
75
75
|
**Every change is backed by a claimed Bongos task. No exceptions.** Code, docs, migrations, config — if you are about to change project state and do not hold an active claim covering it, stop: create or find the task, claim it, then make the change. There is no "tiny fix" or "just a doc" loophole; the database is the complete ledger of what happened to this project.
|
|
76
76
|
|
|
77
|
-
- **Start every session with \`bongos start\`** (or \`/builder-start\`) — it lists the tasks you can claim right now, parallel-safe against everyone else's active claims.
|
|
78
|
-
- **\`bongos claim <id>\`** locks a task to your session. If it refuses (already claimed, not ready, dependencies not shipped), pick another.
|
|
79
|
-
- **\`bongos ship\`** runs the smoke + grade, then merges and deploys; **\`bongos release <id>\`** drops a claim with no credit. Ship or release before you sign off.
|
|
77
|
+
- **Start every session with \`npx bongos start\`** (or \`/builder-start\`) — it lists the tasks you can claim right now, parallel-safe against everyone else's active claims.
|
|
78
|
+
- **\`npx bongos claim <id>\`** locks a task to your session. If it refuses (already claimed, not ready, dependencies not shipped), pick another.
|
|
79
|
+
- **\`npx bongos ship\`** runs the smoke + grade, then merges and deploys; **\`npx bongos release <id>\`** drops a claim with no credit. Ship or release before you sign off.
|
|
80
80
|
- **Dependencies gate everything** — a task becomes claimable only once every task it depends on has shipped.
|
|
81
81
|
- **Code you write or touch carries a doc-comment** — each module a header, each public function/class a line or two, saying *what* it does and *why* it exists (never how), in the words someone would search for. Agents find code by searching text, so meaning written into the source is what makes it findable.
|
|
82
82
|
|
|
83
83
|
## Where things live
|
|
84
84
|
|
|
85
|
-
- **Live work state** (tasks, versions, ranks, ${currency}): the Bongos DB — \`bongos start\` / \`bongos status\`, or ${hallLine}.${statusLine}
|
|
85
|
+
- **Live work state** (tasks, versions, ranks, ${currency}): the Bongos DB — \`npx bongos start\` / \`npx bongos status\`, or ${hallLine}.${statusLine}
|
|
86
86
|
- **This instance's identity** (product, world, brand, addresses): \`config/branding.json\`. **Which features are on**: \`config/modules.json\`.
|
|
87
|
-
- **The platform you stand on** (rank ladder, the ship/grade/deploy pipeline, permissions, migrations) is vendored **Cloud Bongos core** — build the product, not the platform under it. Landing code is Bongos's job: claim a task and run \`bongos ship\`, never a raw \`git push\`/deploy.
|
|
87
|
+
- **The platform you stand on** (rank ladder, the ship/grade/deploy pipeline, permissions, migrations) is vendored **Cloud Bongos core** — build the product, not the platform under it. Landing code is Bongos's job: claim a task and run \`npx bongos ship\`, never a raw \`git push\`/deploy.
|
|
88
88
|
|
|
89
89
|
## Permissions
|
|
90
90
|
|
|
@@ -96,8 +96,45 @@ Builders are rewarded in **${currency}** for shipped work.
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
-
*This is a starter, authored by \`bongos init\`. Grow it as the project grows — add your architecture, versioning methodology, and hard-won gotchas — but keep it lean: every line spends context budget in every session.*
|
|
99
|
+
*This is a starter, authored by \`npx bongos init\`. Grow it as the project grows — add your architecture, versioning methodology, and hard-won gotchas — but keep it lean: every line spends context budget in every session.*
|
|
100
100
|
`;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
// The starter's own sentences as an earlier `bongos init` wrote them, each beside its `npx bongos`
|
|
104
|
+
// form (task 1004621). Every pair is branding-independent text this template authored, so an exact
|
|
105
|
+
// match can only be a line the owner never changed.
|
|
106
|
+
const LEGACY_CLI_LINES = [
|
|
107
|
+
['## First run — before any `bongos` command\n\n**If `node_modules/@bongos/core` is missing, run `npm install` first.** The `bongos` CLI ships *inside* that dependency, so until the install completes there is no `bongos` command and every instruction below fails with `command not found`:',
|
|
108
|
+
'## First run — before any `npx bongos` command\n\n**If `node_modules/@bongos/core` is missing, run `npm install` first.** The `bongos` CLI ships *inside* that dependency and is **not on your PATH**, so it always runs through npx — `npx bongos <command>`, never a bare `bongos` (that fails with `command not found`). Until the install completes, every `npx bongos` instruction below fails too:'],
|
|
109
|
+
['If a `bongos` command then still misbehaves, `bongos doctor` checks', 'If an `npx bongos` command then still misbehaves, `npx bongos doctor` checks'],
|
|
110
|
+
['- **Start every session with `bongos start`** (or `/builder-start`)', '- **Start every session with `npx bongos start`** (or `/builder-start`)'],
|
|
111
|
+
['- **`bongos claim <id>`** locks a task to your session.', '- **`npx bongos claim <id>`** locks a task to your session.'],
|
|
112
|
+
['- **`bongos ship`** runs the smoke + grade, then merges and deploys; **`bongos release <id>`** drops', '- **`npx bongos ship`** runs the smoke + grade, then merges and deploys; **`npx bongos release <id>`** drops'],
|
|
113
|
+
['the Bongos DB — `bongos start` / `bongos status`, or', 'the Bongos DB — `npx bongos start` / `npx bongos status`, or'],
|
|
114
|
+
['claim a task and run `bongos ship`, never', 'claim a task and run `npx bongos ship`, never'],
|
|
115
|
+
['*This is a starter, authored by `bongos init`.', '*This is a starter, authored by `npx bongos init`.'],
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
// refreshStarterCliLines — bring an EXISTING project's CLAUDE.md onto the `npx bongos` spelling
|
|
119
|
+
// (task 1004621). The starter is written only when CLAUDE.md is absent, so every project scaffolded
|
|
120
|
+
// before this kept telling its assistant to type a bare `bongos start` — a command that is not on
|
|
121
|
+
// PATH — in the one file loaded every session. The instance owns the file, so this replaces ONLY
|
|
122
|
+
// the starter's own sentences, matched exactly: a line the owner reworded is left alone, nothing
|
|
123
|
+
// is added or removed, and a file grown from the starter keeps everything else byte for byte.
|
|
124
|
+
// Pure: returns { text, changed } where `changed` counts the sentences replaced.
|
|
125
|
+
function refreshStarterCliLines(text) {
|
|
126
|
+
let out = String(text == null ? '' : text);
|
|
127
|
+
let changed = 0;
|
|
128
|
+
// A Windows checkout (git autocrlf) holds the file with CRLF endings; match it in its own.
|
|
129
|
+
const eol = out.includes('\r\n') ? (s) => s.replace(/\n/g, '\r\n') : (s) => s;
|
|
130
|
+
for (const [legacyLf, currentLf] of LEGACY_CLI_LINES) {
|
|
131
|
+
const legacy = eol(legacyLf);
|
|
132
|
+
const current = eol(currentLf);
|
|
133
|
+
if (!out.includes(legacy)) continue;
|
|
134
|
+
out = out.split(legacy).join(current);
|
|
135
|
+
changed += 1;
|
|
136
|
+
}
|
|
137
|
+
return { text: out, changed };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { renderInstanceClaudeMd, refreshStarterCliLines, LEGACY_CLI_LINES };
|
package/scripts/gds/cli-lib.js
CHANGED
|
@@ -512,7 +512,7 @@ function printUnauthGuidanceAndExit() {
|
|
|
512
512
|
console.error(` Recover the fast (UI-first) way — run ${surfaceAction('reauth')}`);
|
|
513
513
|
console.error(' (opens your Settings page; under "CLI Access" click "Re-issue CLI');
|
|
514
514
|
console.error(' token", copy the one-liner, paste it back — no terminal Device Flow.)');
|
|
515
|
-
console.error(' Fallback (browser sign-in): bongos login <instance-url>');
|
|
515
|
+
console.error(' Fallback (browser sign-in): npx bongos login <instance-url>');
|
|
516
516
|
} else {
|
|
517
517
|
console.error(' Recover the fast (UI-first) way — run /builder-reauth, or:');
|
|
518
518
|
console.error(' node scripts/gds/reauth.js');
|
|
@@ -609,7 +609,7 @@ function printInstanceMismatchAndExit({ expected, actual }) {
|
|
|
609
609
|
console.error(` Your session is signed in to: ${actual}`);
|
|
610
610
|
console.error('');
|
|
611
611
|
console.error(' Sign in to THIS instance, then re-run your command:');
|
|
612
|
-
console.error(` bongos login ${expected}`);
|
|
612
|
+
console.error(` npx bongos login ${expected}`);
|
|
613
613
|
console.error(` (or, from a checkout without the bongos bin: node scripts/gds/login.js ${expected})`);
|
|
614
614
|
console.error('');
|
|
615
615
|
console.error(' Intentionally targeting another instance? Set the base explicitly, e.g.');
|
|
@@ -764,8 +764,8 @@ async function requireSession() {
|
|
|
764
764
|
if (isTerminalSurface()) {
|
|
765
765
|
// A terminal reader may have no instance configured at all — naming one is the whole point
|
|
766
766
|
// of `login`, and `/builder-*` does not exist outside a Claude Code session (task 1003730).
|
|
767
|
-
console.error(` • First time here → run bongos login <instance-url> (sign in with a browser click)`);
|
|
768
|
-
console.error(` • Signed in before / expired → run bongos reauth (re-issue your CLI token)`);
|
|
767
|
+
console.error(` • First time here → run npx bongos login <instance-url> (sign in with a browser click)`);
|
|
768
|
+
console.error(` • Signed in before / expired → run npx bongos reauth (re-issue your CLI token)`);
|
|
769
769
|
} else {
|
|
770
770
|
console.error(` • First time on the project → run /builder-setup (one-time GitHub sign-in that also registers you)`);
|
|
771
771
|
console.error(` • Signed in before / expired → run /builder-reauth (fast UI re-issue of your CLI token)`);
|
|
@@ -29,9 +29,9 @@ const ic = require('../../src/instance-config');
|
|
|
29
29
|
|
|
30
30
|
const USAGE = [
|
|
31
31
|
'usage:',
|
|
32
|
-
' bongos feedback "what went wrong" report a problem with Bongos',
|
|
33
|
-
' bongos feedback --idea "what you wish for" suggest an improvement',
|
|
34
|
-
' bongos feedback --message-file notes.md send a longer note from a file (- for stdin)',
|
|
32
|
+
' npx bongos feedback "what went wrong" report a problem with Bongos',
|
|
33
|
+
' npx bongos feedback --idea "what you wish for" suggest an improvement',
|
|
34
|
+
' npx bongos feedback --message-file notes.md send a longer note from a file (- for stdin)',
|
|
35
35
|
'',
|
|
36
36
|
'Goes to the Cloud Bongos maintainers. Only the text you type and the Bongos',
|
|
37
37
|
'versions you run are sent — no code, chat or files from your project.',
|
|
@@ -86,7 +86,7 @@ const API_BASE = RESOLVED.base;
|
|
|
86
86
|
function usage(extra) {
|
|
87
87
|
if (extra) console.error(extra);
|
|
88
88
|
console.error('');
|
|
89
|
-
console.error('Usage: bongos paste-token <token> [--api-base <url>]');
|
|
89
|
+
console.error('Usage: npx bongos paste-token <token> [--api-base <url>]');
|
|
90
90
|
console.error(' (inside a core checkout: node scripts/gds/paste-token.js <token>)');
|
|
91
91
|
console.error('');
|
|
92
92
|
// Fall back to the inferred base for this hint when --api-base was the thing that
|
|
@@ -12,14 +12,7 @@
|
|
|
12
12
|
const path = require('node:path');
|
|
13
13
|
const crypto = require('node:crypto');
|
|
14
14
|
const { CONFIG } = require('./provision-config.js');
|
|
15
|
-
const { provisionedEnvPrefix } = require('./provision-units.js');
|
|
16
|
-
|
|
17
|
-
function federationHubOrigin() {
|
|
18
|
-
const explicit = process.env.PROVISION_HUB_ORIGIN;
|
|
19
|
-
if (explicit) return String(explicit).replace(/\/+$/, '');
|
|
20
|
-
const own = process.env[`${provisionedEnvPrefix()}_PUBLIC_ORIGIN`];
|
|
21
|
-
return own ? String(own).replace(/\/+$/, '') : null;
|
|
22
|
-
}
|
|
15
|
+
const { federationHubOrigin, provisionedEnvPrefix } = require('./provision-units.js'); // lives beside the hub-upgrade address it also feeds (task 1004608)
|
|
23
16
|
|
|
24
17
|
// Whether provisioning should federate this instance's sign-in through the hub.
|
|
25
18
|
//
|
|
@@ -258,13 +258,15 @@ GITHUB_APP_PRIVATE_KEY=${String(appPem).replace(/\r?\n/g, '\\n')}
|
|
|
258
258
|
// Postgres with every sibling and the control plane, so its pool may not take pg's
|
|
259
259
|
// default 10 of the server's 97. src/bongos/pool.js reads it bare.
|
|
260
260
|
const poolLine = `PGPOOL_MAX=${CONFIG.instancePoolMax}\n`;
|
|
261
|
+
// Where this project's Update button goes (task 1004608) — see hubUpgradeEnvVarsFor.
|
|
262
|
+
const hubUpgradeLines = Object.entries(hubUpgradeEnvVarsFor(inst)).map(([k, v]) => `${k}=${v}\n`).join('');
|
|
261
263
|
return `# /etc/${inst.slug}/web.env — per-instance secrets (chmod 600, OUTSIDE the repo).
|
|
262
264
|
${credComment}
|
|
263
265
|
${prefix}_GITHUB_CLIENT_ID=${clientId || ''}
|
|
264
266
|
${prefix}_GITHUB_CLIENT_SECRET=${clientSecret || ''}
|
|
265
267
|
${firstAdminLine}${originLines}${appLines}PORT=${inst.port}
|
|
266
268
|
PGDATABASE=${dbName(inst)}
|
|
267
|
-
${poolLine}${dbLines}${fedLines}${settingsEnvLines(inst, prefix)}`;
|
|
269
|
+
${poolLine}${dbLines}${fedLines}${hubUpgradeLines}${settingsEnvLines(inst, prefix)}`;
|
|
268
270
|
}
|
|
269
271
|
function webEnvPath(inst, { etcBase = CONFIG.etcBase } = {}) {
|
|
270
272
|
return path.posix.join(etcBase, inst.slug, 'web.env');
|
|
@@ -295,7 +297,7 @@ function originEnvVarsFor(inst) {
|
|
|
295
297
|
function settingsEnvVarsFor(inst, envBody) {
|
|
296
298
|
const { settingsEnvVars } = require(path.join(REPO_ROOT, 'modules/provisioning/provisioning')); // eslint-disable-line global-require
|
|
297
299
|
const prefix = inst && inst.hosting_shape === 'control-plane' ? selfEnvPrefix({ envBody }) : provisionedEnvPrefix();
|
|
298
|
-
return settingsEnvVars(inst, prefix);
|
|
300
|
+
return { ...settingsEnvVars(inst, prefix), ...hubUpgradeEnvVarsFor(inst) };
|
|
299
301
|
}
|
|
300
302
|
|
|
301
303
|
// The env-var prefix the CONTROL PLANE's own web process resolves (task 1004334). The
|
|
@@ -414,8 +416,32 @@ function renamedEnvCarry(body) {
|
|
|
414
416
|
// <PREFIX>_HUB_ORIGIN (what auth.js loadIdpConfig reads). Explicit PROVISION_HUB_ORIGIN
|
|
415
417
|
// wins; otherwise the control plane's OWN public origin (<PREFIX>_PUBLIC_ORIGIN, the
|
|
416
418
|
// env the hub already runs under). Null → not hub-configured → shouldFederate is false.
|
|
419
|
+
function federationHubOrigin() {
|
|
420
|
+
const explicit = process.env.PROVISION_HUB_ORIGIN;
|
|
421
|
+
if (explicit) return String(explicit).replace(/\/+$/, '');
|
|
422
|
+
const own = process.env[`${provisionedEnvPrefix()}_PUBLIC_ORIGIN`];
|
|
423
|
+
return own ? String(own).replace(/\/+$/, '') : null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// WHERE A HOSTED PROJECT'S UPDATE BUTTON GOES (task 1004608): the hub's deploy door for
|
|
427
|
+
// THIS project, written as <PREFIX>_HUB_UPGRADE_URL. The hall used to learn the hub only
|
|
428
|
+
// from the sign-in federation block, which only a co-tenant gets, so every cloud-host
|
|
429
|
+
// project (and any that keeps its own GitHub app) told its owner to run `bongos upgrade`
|
|
430
|
+
// on a server the platform runs. The door admits the owner of both tenant shapes
|
|
431
|
+
// (modules/provisioning/core-upgrade-authz.js), so both get the address. Not a secret and
|
|
432
|
+
// grants nothing: the door re-checks every request. Empty for the tenant shapes when no hub
|
|
433
|
+
// origin resolves (a key that came and went would leave a stale line behind); absent for
|
|
434
|
+
// every other shape, which has no door on this hub.
|
|
435
|
+
const HUB_UPGRADE_SHAPES = new Set(['co-tenant', 'cloud-host']);
|
|
436
|
+
function hubUpgradeEnvVarsFor(inst) {
|
|
437
|
+
if (!inst || !HUB_UPGRADE_SHAPES.has(inst.hosting_shape) || !inst.slug) return {};
|
|
438
|
+
const origin = federationHubOrigin();
|
|
439
|
+
return { [`${provisionedEnvPrefix()}_HUB_UPGRADE_URL`]: origin ? `${origin}/deploy?project=${encodeURIComponent(String(inst.slug))}` : '' };
|
|
440
|
+
}
|
|
417
441
|
|
|
418
442
|
module.exports = {
|
|
443
|
+
federationHubOrigin,
|
|
444
|
+
hubUpgradeEnvVarsFor,
|
|
419
445
|
ENV_PREFIX_PACK,
|
|
420
446
|
envPrefixMismatch,
|
|
421
447
|
normalizePackEnvPrefix,
|
package/scripts/gds/reauth.js
CHANGED
|
@@ -96,7 +96,7 @@ function printGuidance({ opened }) {
|
|
|
96
96
|
console.log(" you're already signed in there via your browser cookie)");
|
|
97
97
|
console.log(' 2. Under the "CLI Access" section, click "Re-issue CLI token".');
|
|
98
98
|
console.log(' 3. Click "Copy" — it copies a one-line command that looks like:');
|
|
99
|
-
console.log(' bongos paste-token <token>');
|
|
99
|
+
console.log(' npx bongos paste-token <token>');
|
|
100
100
|
console.log(' 4. Paste that one line back to Claude. It will run it for you,');
|
|
101
101
|
console.log(' verify the token, and write your new session.');
|
|
102
102
|
console.log('');
|
|
@@ -106,6 +106,9 @@ const INTEGRATION = new Set([
|
|
|
106
106
|
// never was a dedicated consumer-layout-boot workflow, whatever older notes say.
|
|
107
107
|
'consumer_layout_boot',
|
|
108
108
|
'claim_gate_open_blocker', 'claim_gate_rebase',
|
|
109
|
+
// task 1004585: a hall-made goal's spec task gates its other work — the edge, the
|
|
110
|
+
// claim gate's unsatisfiedDeps read, and the ship-time auto-promotion, on real Postgres.
|
|
111
|
+
'goal_spec_task_db',
|
|
109
112
|
// task 1003457: proves the blocker-resolve auto-promotion trigger (migration
|
|
110
113
|
// 009, tightened by core_223) fires on the HALL'S OWN write path — the real
|
|
111
114
|
// buildGdsRouter() HTTP routes, not a static SQL-text pin (blocker_resolve_guard.mjs,
|
package/scripts/gds/setup.js
CHANGED
|
@@ -356,7 +356,7 @@ async function finishSetup(builder) {
|
|
|
356
356
|
console.log(` 3. Claim + ship: ${surfaceAction('claim', 'N')} → ${surfaceAction('ship')}`);
|
|
357
357
|
// Claiming writes code, so it needs a checkout. In a terminal the public CLI answers
|
|
358
358
|
// `bongos claim` with where to get one; say so here rather than let step 3 look self-serve.
|
|
359
|
-
if (isTerminalSurface()) console.log(' (claim + ship run in a checkout — `bongos claim` shows how to get one)');
|
|
359
|
+
if (isTerminalSurface()) console.log(' (claim + ship run in a checkout — `npx bongos claim` shows how to get one)');
|
|
360
360
|
console.log(' 4. Ship three works and you rise to Thetes — the whole queue opens.');
|
|
361
361
|
}
|
|
362
362
|
} catch (_) {
|