@bongos/core 1.21.120 → 1.21.122
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 +54 -39
- package/.claude/skills/builder-ship/SKILL.md +2 -0
- package/clients/bongos-client/index.d.ts +2 -2
- package/docs/adr/0199-restart-an-unreachable-active-project.md +4 -0
- package/docs/adr/0369-ship-mode-decides-who-starts-a-ship-not-how-it-lands.md +1 -1
- package/docs/api/openapi.json +7 -3
- package/docs/copy-inventory.md +13 -11
- package/docs/copy-registry.json +30 -12
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +3 -3
- package/modules/government/protected-surfaces.json +2 -0
- package/modules/hall-ui/public/modules.js +29 -3
- package/modules/lifecycle/routes/project-settings.js +22 -1
- package/modules/provisioning/provisioning.js +5 -3
- package/modules/public-landing/public/projects.html +4 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/exec-path-guard.js +2 -2
- package/scripts/gds/provision.js +4 -2
- package/scripts/gds/ship-flow.js +22 -2
- package/scripts/gds/ship-mode-ask.js +138 -0
- package/scripts/gds/ship.js +6 -0
- package/src/bongos/module-scope-map.js +3 -1
- package/src/bongos/routes/modules.js +22 -3
- package/src/module-api.js +1 -1
- package/src/module-loader/loader.js +42 -2
- package/tests/government_protected_surfaces.mjs +9 -1
- package/tests/hall_catalog_world.mjs +1 -1
- package/tests/module_loader.mjs +31 -0
- package/tests/module_toggle_restart.mjs +73 -0
- package/tests/projects_hub_pre_uat.mjs +4 -1
- package/tests/provision_restart.mjs +3 -2
- package/tests/provisioning_restart.mjs +6 -3
- package/tests/provisioning_settings_apply.mjs +2 -2
- package/tests/ship_mode_first_ship.mjs +219 -0
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 2722,
|
|
7
|
+
"unique_ids": 2302,
|
|
8
|
+
"certain": 2722,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 240
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 143,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1914
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -5091,7 +5091,7 @@
|
|
|
5091
5091
|
"surface": "builders-hall",
|
|
5092
5092
|
"text": "Could not load modules.",
|
|
5093
5093
|
"file": "modules/hall-ui/public/modules.js",
|
|
5094
|
-
"line":
|
|
5094
|
+
"line": 361,
|
|
5095
5095
|
"origin": "js-notice",
|
|
5096
5096
|
"confidence": "certain"
|
|
5097
5097
|
},
|
|
@@ -5271,7 +5271,7 @@
|
|
|
5271
5271
|
"surface": "builders-hall",
|
|
5272
5272
|
"text": "Could not reach the submit endpoint.",
|
|
5273
5273
|
"file": "modules/hall-ui/public/modules.js",
|
|
5274
|
-
"line":
|
|
5274
|
+
"line": 340,
|
|
5275
5275
|
"origin": "js-notice",
|
|
5276
5276
|
"confidence": "certain"
|
|
5277
5277
|
},
|
|
@@ -9780,7 +9780,7 @@
|
|
|
9780
9780
|
"surface": "builders-hall",
|
|
9781
9781
|
"text": "Nothing to submit — every module here ships with the core.",
|
|
9782
9782
|
"file": "modules/hall-ui/public/modules.js",
|
|
9783
|
-
"line":
|
|
9783
|
+
"line": 301,
|
|
9784
9784
|
"origin": "js-text-assign",
|
|
9785
9785
|
"confidence": "certain"
|
|
9786
9786
|
},
|
|
@@ -17731,6 +17731,15 @@
|
|
|
17731
17731
|
"origin": "html-attr:placeholder",
|
|
17732
17732
|
"confidence": "certain"
|
|
17733
17733
|
},
|
|
17734
|
+
{
|
|
17735
|
+
"id": "299dd9d33f53",
|
|
17736
|
+
"surface": "builders-hall",
|
|
17737
|
+
"text": "restart it",
|
|
17738
|
+
"file": "modules/hall-ui/public/modules.js",
|
|
17739
|
+
"line": 129,
|
|
17740
|
+
"origin": "js-markup",
|
|
17741
|
+
"confidence": "certain"
|
|
17742
|
+
},
|
|
17734
17743
|
{
|
|
17735
17744
|
"id": "58b4e7a48d86",
|
|
17736
17745
|
"surface": "builders-hall",
|
|
@@ -17943,7 +17952,7 @@
|
|
|
17943
17952
|
"surface": "builders-hall",
|
|
17944
17953
|
"text": "submit for review",
|
|
17945
17954
|
"file": "modules/hall-ui/public/modules.js",
|
|
17946
|
-
"line":
|
|
17955
|
+
"line": 175,
|
|
17947
17956
|
"origin": "js-markup",
|
|
17948
17957
|
"confidence": "certain"
|
|
17949
17958
|
},
|
|
@@ -18550,6 +18559,15 @@
|
|
|
18550
18559
|
"origin": "js-markup",
|
|
18551
18560
|
"confidence": "certain"
|
|
18552
18561
|
},
|
|
18562
|
+
{
|
|
18563
|
+
"id": "354861d3c2ce",
|
|
18564
|
+
"surface": "builders-hall",
|
|
18565
|
+
"text": "waits for a restart",
|
|
18566
|
+
"file": "modules/hall-ui/public/modules.js",
|
|
18567
|
+
"line": 127,
|
|
18568
|
+
"origin": "js-markup",
|
|
18569
|
+
"confidence": "certain"
|
|
18570
|
+
},
|
|
18553
18571
|
{
|
|
18554
18572
|
"id": "35be9e0923da",
|
|
18555
18573
|
"surface": "builders-hall",
|
|
@@ -18861,7 +18879,7 @@
|
|
|
18861
18879
|
"surface": "builders-hall",
|
|
18862
18880
|
"text": "{…} and above can enable or disable a module",
|
|
18863
18881
|
"file": "modules/hall-ui/public/modules.js",
|
|
18864
|
-
"line":
|
|
18882
|
+
"line": 173,
|
|
18865
18883
|
"origin": "js-markup",
|
|
18866
18884
|
"confidence": "certain"
|
|
18867
18885
|
},
|
|
@@ -19203,7 +19221,7 @@
|
|
|
19203
19221
|
"surface": "builders-hall",
|
|
19204
19222
|
"text": "{…} {…} — how-to",
|
|
19205
19223
|
"file": "modules/hall-ui/public/modules.js",
|
|
19206
|
-
"line":
|
|
19224
|
+
"line": 416,
|
|
19207
19225
|
"origin": "js-text-assign",
|
|
19208
19226
|
"confidence": "certain"
|
|
19209
19227
|
},
|
|
@@ -19275,7 +19293,7 @@
|
|
|
19275
19293
|
"surface": "builders-hall",
|
|
19276
19294
|
"text": "{…} — how to use it",
|
|
19277
19295
|
"file": "modules/hall-ui/public/modules.js",
|
|
19278
|
-
"line":
|
|
19296
|
+
"line": 418,
|
|
19279
19297
|
"origin": "js-text-assign",
|
|
19280
19298
|
"confidence": "certain"
|
|
19281
19299
|
},
|
|
@@ -19410,7 +19428,7 @@
|
|
|
19410
19428
|
"surface": "builders-hall",
|
|
19411
19429
|
"text": "· version",
|
|
19412
19430
|
"file": "modules/hall-ui/public/modules.js",
|
|
19413
|
-
"line":
|
|
19431
|
+
"line": 421,
|
|
19414
19432
|
"origin": "js-markup",
|
|
19415
19433
|
"confidence": "certain"
|
|
19416
19434
|
},
|
|
@@ -3077,5 +3077,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3077
3077
|
landed since 1.21.118 with no explicit bump. run 38574092141. (task 1002620)
|
|
3078
3078
|
1.21.120 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3079
3079
|
landed since 1.21.119 with no explicit bump. run 38597828843. (task 1002620)
|
|
3080
|
+
1.21.121 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3081
|
+
landed since 1.21.120 with no explicit bump. run 38604777189. (task 1002620)
|
|
3082
|
+
1.21.122 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3083
|
+
landed since 1.21.121 with no explicit bump. run 38620147259. (task 1002620)
|
|
3080
3084
|
---------------------------------------------------------------------------
|
|
3081
3085
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -1875,7 +1875,7 @@
|
|
|
1875
1875
|
"surface": "builders",
|
|
1876
1876
|
"title": "Modules",
|
|
1877
1877
|
"states_read": ["browse","search-empty","lens","details","out"],
|
|
1878
|
-
"files_hash": "
|
|
1878
|
+
"files_hash": "c1d5725a6bf0afe2",
|
|
1879
1879
|
"reading_hash": "e37d27e205c7ba63",
|
|
1880
1880
|
"counts": {"lines":48,"placed":19,"shared":8,"unplaced":21},
|
|
1881
1881
|
"omitted": {"data":76},
|
|
@@ -1917,7 +1917,7 @@
|
|
|
1917
1917
|
{"key":"L0034","section":"Module catalog","text":"{…} file{…}","placement":"placed","file":"modules/hall-ui/public/gate.js","line":151,"string_id":"3efa67dc6e07"},
|
|
1918
1918
|
{"key":"L0035","section":"Module catalog","text":"sessions","placement":"placed","file":"modules/hall-ui/public/task.js","line":467,"string_id":"732b4c935e36"},
|
|
1919
1919
|
{"key":"L0036","section":"Module catalog","text":"Built by this project","placement":"placed","file":"modules/hall-ui/public/modules.html","line":70,"string_id":"b51edc826cbe"},
|
|
1920
|
-
{"key":"L0037","section":"Module catalog","text":"submit for review","placement":"placed","file":"modules/hall-ui/public/modules.js","line":
|
|
1920
|
+
{"key":"L0037","section":"Module catalog","text":"submit for review","placement":"placed","file":"modules/hall-ui/public/modules.js","line":175,"string_id":"0215b2cdca8c"},
|
|
1921
1921
|
{"key":"L0038","section":"Module catalog","text":"A module built here can be submitted upstream for review, so it can join the shared catalog with your name and this project credited.","placement":"placed","file":"modules/hall-ui/public/modules.html","line":74,"string_id":"2e17d7e236d9"},
|
|
1922
1922
|
{"key":"L0039","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":520,"string_id":"1883ad7f07fc"},
|
|
1923
1923
|
{"key":"L0040","section":"Module catalog","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":810,"string_id":"fd1903586e8e"},
|
|
@@ -5129,7 +5129,7 @@
|
|
|
5129
5129
|
"surface": "landing",
|
|
5130
5130
|
"title": "{{productName}} — projects",
|
|
5131
5131
|
"states_read": ["out-map","in-map","in-map-projects","in-map-profile","in-map-kbd","in-map-search","in-map-mine-on","in-table","in-table-by-builders","in-mine","in-manage","in-manage-address","in-manage-planet","in-manage-card","in-manage-look","in-manage-kind","in-manage-invite","in-manage-invite-handoff","in-manage-invite-guild","in-manage-app-live","in-manage-app-building","in-manage-app-failed","in-manage-app-retry","in-manage-app-offer","in-manage-app-connect","in-manage-snag","in-manage-gone","in-manage-missing","out-manage","in-new","in-new-named","in-new-ready","in-new-name-prefilled","in-new-demo","in-new-demo-used","in-new-checks-filled","in-new-checks-entity","in-new-row-code","in-new-row-address","in-new-row-team","in-new-row-extras","in-new-row-rewards","in-new-row-app","in-new-row-run","in-new-row-ship","in-new-look","in-new-look-custom","in-new-create-blocked","in-new-done","in-new-resume-offer","out-new","out-map-projects","card","card-noart","card-self","in-manage-demo","in-mine-dns-pending","in-manage-dns-pending","in-mine-empty","card-floor","card-backdrop","in-new-done-live","setup-just-created","setup-waiting-on-github","setup-installing","setup-stopped","setup-all-done","setup-cloud-hop-open"],
|
|
5132
|
-
"files_hash": "
|
|
5132
|
+
"files_hash": "6c567b2c13fd0fc6",
|
|
5133
5133
|
"reading_hash": "302909900601b80c",
|
|
5134
5134
|
"counts": {"lines":825,"placed":246,"shared":21,"unplaced":558},
|
|
5135
5135
|
"omitted": {"data":150},
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
{ "glob": "scripts/gds/ship-memory-sync.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
72
72
|
{ "glob": "scripts/gds/ship-merge-gate.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
73
73
|
{ "glob": "scripts/gds/ship-merge.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
74
|
+
{ "glob": "scripts/gds/ship-mode-ask.js", "group": "pipeline", "why": "task 1004630. Split out beside ship.js: the first-ship ask that decides whether a project ships in Auto or Manual mode, so an edit here changes who may start a ship." },
|
|
74
75
|
{ "glob": "scripts/gds/ship-preflight-steps.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
75
76
|
{ "glob": "scripts/gds/ship-regen.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
76
77
|
{ "glob": "scripts/gds/ship-resume.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
{ "glob": "scripts/gds/ship-state.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
79
80
|
{ "glob": "scripts/gds/ship-surprise.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
80
81
|
{ "glob": "scripts/gds/ship-visual.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
82
|
+
{ "glob": "scripts/gds/ship-mode-ask.js", "group": "pipeline", "why": "task 1004630. Split out of the protected scripts/gds/ship.js beside it: the ship-mode step every ship runs before it lands, so it is protected at ship.js's floor (the 1004024 carve tripwire)." },
|
|
81
83
|
{ "glob": "scripts/gds/confirm.js", "group": "pipeline", "why": "the confirm step" },
|
|
82
84
|
{ "glob": "scripts/gds/merge-mode.js", "group": "pipeline", "why": "the manual land fallback" },
|
|
83
85
|
{ "glob": "scripts/gds/ci-grade.js", "group": "pipeline", "why": "CI grade-gate driver — steers the grade check (ADR 0058)" },
|
|
@@ -113,6 +113,32 @@
|
|
|
113
113
|
return '<span class="fact-pill fact-pill--warn" title="Taken out of the store. Your copy keeps working, but it will get no more updates.">delisted · no more updates</span>';
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// A toggle this server has not acted on yet (task 1004463): routes mount and pollers
|
|
117
|
+
// start once, at boot, so the change lands at the next restart.
|
|
118
|
+
// The words are the server's (restartNote in src/bongos/routes/modules.js): 'routes' are
|
|
119
|
+
// its pages, 'pollers' its background jobs, and only what actually waits is named.
|
|
120
|
+
function restartFact(m) {
|
|
121
|
+
const waits = Array.isArray(m.restart_pending) ? m.restart_pending : [];
|
|
122
|
+
if (!waits.length) return '';
|
|
123
|
+
const parts = m.enabled
|
|
124
|
+
? [waits.includes('routes') && 'its pages do not open', waits.includes('pollers') && 'its background jobs do not run']
|
|
125
|
+
: [waits.includes('routes') && 'its pages still open', waits.includes('pollers') && 'its background jobs still run'];
|
|
126
|
+
const title = `It turns ${m.enabled ? 'on' : 'off'} at the next restart of this server. Until then ${parts.filter(Boolean).join(' and ')}.`;
|
|
127
|
+
const pill = `<span class="fact-pill fact-pill--warn" title="${escapeHtml(title)}">waits for a restart</span>`;
|
|
128
|
+
const door = restartDoor();
|
|
129
|
+
return door ? pill + `<a class="fact-pill" href="${escapeHtml(door)}">restart it</a>` : pill;
|
|
130
|
+
}
|
|
131
|
+
// Where this server's Restart button is: only the platform's own hall has one, on its
|
|
132
|
+
// project page on the hub (task 1004463). Elsewhere whoever runs the server restarts it.
|
|
133
|
+
function restartDoor() {
|
|
134
|
+
const hub = window.OTB && typeof window.OTB.hubOrigin === 'function' ? window.OTB.hubOrigin() : null;
|
|
135
|
+
if (!hub) return null;
|
|
136
|
+
let hubHost = '';
|
|
137
|
+
try { hubHost = new URL(hub).hostname; } catch (err) { return null; }
|
|
138
|
+
const here = String(window.location.hostname || '').replace(/^builders\./, '');
|
|
139
|
+
return here === hubHost ? `${hub}/projects?view=mine` : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
// The lines the row's details disclosure opens onto: the facts the payload
|
|
117
143
|
// carries that the card never rendered — what the module contributes, the
|
|
118
144
|
// core it needs, who maintains it and what it hands off to.
|
|
@@ -159,7 +185,7 @@
|
|
|
159
185
|
<div class="ov-row__main">
|
|
160
186
|
<span class="ov-row__title">${escapeHtml(m.title || m.key)}<span class="ov-mono">${escapeHtml(m.key)}</span></span>
|
|
161
187
|
${desc ? `<p class="ov-row__sub mod-row__desc">${escapeHtml(desc)}</p>` : ''}
|
|
162
|
-
<div class="ov-row__facts">${provenancePill(m)}${attributionFacts(m)}${upkeepFact(m)}${howtoFact(m)}${delistedFact(m)}</div>
|
|
188
|
+
<div class="ov-row__facts">${provenancePill(m)}${attributionFacts(m)}${upkeepFact(m)}${howtoFact(m)}${delistedFact(m)}${restartFact(m)}</div>
|
|
163
189
|
${lines ? `<details class="mod-more"${openKeys.has(m.key) ? ' open' : ''}><summary aria-label="Details for ${escapeHtml(m.title || m.key)}"></summary>${lines}</details>` : ''}
|
|
164
190
|
</div>
|
|
165
191
|
<div class="ov-row__right mod-row__right">${rightHtml(m)}</div>
|
|
@@ -289,13 +315,13 @@
|
|
|
289
315
|
const body = res.data || {};
|
|
290
316
|
if (!res.ok) throw new Error(body.message || `enable → ${res.status}`);
|
|
291
317
|
const m = allModules.find((x) => x.key === key);
|
|
292
|
-
if (m) m.enabled = next;
|
|
318
|
+
if (m) { m.enabled = next; if (Array.isArray(body.restart_pending)) m.restart_pending = body.restart_pending; }
|
|
293
319
|
btn.setAttribute('aria-pressed', next ? 'true' : 'false');
|
|
294
320
|
btn.textContent = next ? 'enabled' : 'disabled';
|
|
295
321
|
const figs = $('#mod-figs');
|
|
296
322
|
if (figs) figs.innerHTML = figsHTML();
|
|
297
323
|
renderRows();
|
|
298
|
-
toast(body.note || (next ? `${key} enabled.` : `${key} disabled.`), { duration: 3200 });
|
|
324
|
+
toast(body.note || (next ? `${key} enabled.` : `${key} disabled.`), { duration: body.restart_pending && body.restart_pending.length ? 7000 : 3200 });
|
|
299
325
|
} catch (err) {
|
|
300
326
|
console.error('[modules] toggle failed', err);
|
|
301
327
|
toast(err.message || 'Could not change that module.', { duration: 3200 });
|
|
@@ -58,6 +58,21 @@ function shipModeRow(resolved) {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// callerHolds(builderId, atom) → true | false | null. null means authority could
|
|
62
|
+
// not be established (government port missing or the resolver threw); a reader
|
|
63
|
+
// treats that as unknown, never as a grant.
|
|
64
|
+
async function callerHolds(builderId, atom) {
|
|
65
|
+
try {
|
|
66
|
+
const government = api.resolveOptional('government');
|
|
67
|
+
if (!government || typeof government.resolveBuilderPermissions !== 'function') return null;
|
|
68
|
+
const held = (await government.resolveBuilderPermissions(builderId)) || [];
|
|
69
|
+
return held.includes(atom);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
log.error(`ship-mode can_change: ${err && err.message}`);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
// --- the three rows that are READ, never written -----------------------------
|
|
62
77
|
//
|
|
63
78
|
// Each names the committed artefact it resolves from, because "you cannot edit this"
|
|
@@ -348,12 +363,18 @@ module.exports = function buildProjectSettingsRouter() {
|
|
|
348
363
|
// reward internals or file paths an agent could not already read, and every
|
|
349
364
|
// builder's AI session must know whether to ship on its own or stop and ask —
|
|
350
365
|
// gating it Metic+ would leave a newcomer's session guessing.
|
|
366
|
+
//
|
|
367
|
+
// can_change (task 1004630): whether THIS caller holds project.settings.manage,
|
|
368
|
+
// so the first-ship question is put only to someone who can answer it. null when
|
|
369
|
+
// authority could not be established; the CLI then asks, and a refused save lets
|
|
370
|
+
// the ship go on. Cosmetic only — the PATCH below keeps its own gate (ADR 0016).
|
|
351
371
|
router.get('/project-settings/ship-mode', api.requireBuilder, async (req, res) => {
|
|
352
372
|
try {
|
|
353
373
|
const resolved = await api.projectSettings.resolveShipMode();
|
|
354
374
|
res.set('Cache-Control', 'no-store');
|
|
355
375
|
const row = shipModeRow(resolved);
|
|
356
|
-
|
|
376
|
+
const canChange = req.builder ? await callerHolds(req.builder.id, 'project.settings.manage') : null;
|
|
377
|
+
res.json({ mode: resolved.mode, chosen: resolved.chosen, source: resolved.source, sentence: row.sentence, choices: row.choices, can_change: canChange });
|
|
357
378
|
} catch (err) {
|
|
358
379
|
log.error(`GET /project-settings/ship-mode: ${err && err.message}`);
|
|
359
380
|
res.fail('project_settings_read_failed', { status: 500, message: 'internal error' });
|
|
@@ -123,9 +123,11 @@ const SETTINGS_APPLY_SHAPES = new Set(['co-tenant', 'cloud-host', 'control-plane
|
|
|
123
123
|
// SETTINGS_APPLY_FROM: each action declaring its own gate is this module's
|
|
124
124
|
// convention, and the two answer different questions.
|
|
125
125
|
const RESTART_FROM = new Set(['active']);
|
|
126
|
-
// Same reachability rule as the settings push (the runner can only restart what it runs)
|
|
127
|
-
//
|
|
128
|
-
|
|
126
|
+
// Same reachability rule as the settings push (the runner can only restart what it runs).
|
|
127
|
+
// 'control-plane' joined in task 1004463: a module toggled in its hall takes effect only at
|
|
128
|
+
// the next restart, the guard already allows 'restart' on the self row, and the owner's only
|
|
129
|
+
// other way was a shell on the box.
|
|
130
|
+
const RESTART_SHAPES = new Set(['co-tenant', 'cloud-host', 'control-plane']);
|
|
129
131
|
|
|
130
132
|
// Per-project settings (task 1003040, goal 1000058): the known keys + their
|
|
131
133
|
// vocabularies, enforced at the API layer — the jsonb `settings` column
|
|
@@ -8541,9 +8541,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
8541
8541
|
last_verified_up === false is the verdict; null means never probed and is
|
|
8542
8542
|
deliberately NOT the same claim (publicInstance keeps the two apart), so a
|
|
8543
8543
|
brand-new project never grows a Restart button it has no use for. A door on
|
|
8544
|
-
every healthy card would only invite needless downtime.
|
|
8544
|
+
every healthy card would only invite needless downtime. The ONE exception is the
|
|
8545
|
+
platform's own row (task 1004463): a module switched on or off in its hall waits for
|
|
8546
|
+
a restart, so its owner always has the door. */
|
|
8545
8547
|
function canRestart(inst) {
|
|
8546
|
-
return inst.status === 'active' && inst.last_verified_up === false
|
|
8548
|
+
return inst.status === 'active' && !inst.open_intent && (inst.last_verified_up === false || inst.hosting_shape === 'control-plane');
|
|
8547
8549
|
}
|
|
8548
8550
|
/* keep watching while the platform is doing something to this instance */
|
|
8549
8551
|
function shouldWatch(inst) {
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.122",
|
|
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.122",
|
|
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.122",
|
|
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
|
@@ -9702,5 +9702,17 @@
|
|
|
9702
9702
|
"id": "1004410",
|
|
9703
9703
|
"text": "The new-project page no longer says GitHub sign-in is unfinished when it already works: the platform now checks the project itself and ticks the step."
|
|
9704
9704
|
}
|
|
9705
|
+
],
|
|
9706
|
+
"1.21.121": [
|
|
9707
|
+
{
|
|
9708
|
+
"id": "1004463",
|
|
9709
|
+
"text": "Turning a module on or off now says honestly when it takes effect at the next restart, and the platform's own server can be restarted from its project page instead of needing a login to the machine."
|
|
9710
|
+
}
|
|
9711
|
+
],
|
|
9712
|
+
"1.21.122": [
|
|
9713
|
+
{
|
|
9714
|
+
"id": "1004630",
|
|
9715
|
+
"text": "If a project never picked a ship mode, its next ship now pauses once and asks: Auto (recommended) or Manual. The answer is saved for the whole project and it never asks again. People who cannot change project settings, and una"
|
|
9716
|
+
}
|
|
9705
9717
|
]
|
|
9706
9718
|
}
|
|
@@ -91,7 +91,7 @@ const EXEMPTIONS = [
|
|
|
91
91
|
+ 're-opens for review.',
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
|
-
at: 'src/module-loader/loader.js:
|
|
94
|
+
at: 'src/module-loader/loader.js:198',
|
|
95
95
|
why: 'The plugin loader, and the second site the audit named (B50 "motivating sites"). It '
|
|
96
96
|
+ 'require()s a discovered module\'s route file, so the specifier is a runtime path BY '
|
|
97
97
|
+ 'DESIGN — a plugin system whose module paths are compile-time constants is not a plugin '
|
|
@@ -103,7 +103,7 @@ const EXEMPTIONS = [
|
|
|
103
103
|
+ 'absorbed into this check.',
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
|
-
at: 'src/module-loader/loader.js:
|
|
106
|
+
at: 'src/module-loader/loader.js:347',
|
|
107
107
|
why: 'The poller twin of the route require at :196 — same DEFAULT_ROOTS bound, same plugin-by-'
|
|
108
108
|
+ 'design rationale, same ADR 0107 / task 1003400 follow-on. Listed on its own line so a '
|
|
109
109
|
+ 'change to either call re-opens only that one.',
|
package/scripts/gds/provision.js
CHANGED
|
@@ -927,8 +927,10 @@ async function settingsApplyInstance(inst, deps) {
|
|
|
927
927
|
// ---------------------------------------------------------------------------
|
|
928
928
|
async function restartInstance(inst, deps) {
|
|
929
929
|
const { db, provisioning, exec, log, apply, privileged } = deps;
|
|
930
|
-
// Same box routing as settingsApplyInstance: a standalone runs control-plane-local
|
|
931
|
-
|
|
930
|
+
// Same box routing as settingsApplyInstance: a standalone runs control-plane-local, and the
|
|
931
|
+
// control plane's own row IS this box (task 1004463).
|
|
932
|
+
const onControlBox = inst.hosting_shape === 'cloud-host' || inst.hosting_shape === 'control-plane';
|
|
933
|
+
const boxExec = onControlBox ? (deps.controlExec || exec) : exec;
|
|
932
934
|
const sudoP = privileged ? 'sudo ' : '';
|
|
933
935
|
log(`restart ${inst.slug} (shape=${inst.hosting_shape}, status=${inst.status})`);
|
|
934
936
|
|
package/scripts/gds/ship-flow.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
-
const { cliClient, requireSession, arg, detectWorktreeName, cardFlagsFromMe } = require('./cli-lib');
|
|
11
|
+
const { cliClient, requireSession, arg, detectWorktreeName, cardFlagsFromMe, apiCall, cliExit } = require('./cli-lib');
|
|
12
|
+
// task 1004630: the first ship asks for the ship mode once (ADR 0369).
|
|
13
|
+
const { runFirstShipAsk } = require('./ship-mode-ask.js');
|
|
12
14
|
const { resolveProse, markShipProgress, smokeGateMessages, proseFlagPresent, regradeNotesRefusal, staleSummaryNotice } = require('./ship-honesty.js');
|
|
13
15
|
const { plainCardLines } = require('./plain-cards.js');
|
|
14
16
|
const { classifyEmptyShip } = require('../../modules/lifecycle/ship-preflight');
|
|
@@ -42,7 +44,7 @@ async function shipMain() {
|
|
|
42
44
|
const args = process.argv.slice(2);
|
|
43
45
|
const taskId = Number(args[0]);
|
|
44
46
|
if (!Number.isFinite(taskId)) {
|
|
45
|
-
console.error('usage: node scripts/gds/ship.js <task-id> (--notes-file <path|-> | --notes "...") (--summary-file <path|-> | --summary "...") [--visual <image>] [--visual-alt "caption"] [--surprise "text" | --surprise none | --no-surprise] [--no-merge] [--db-only] [--verified] [--allow-empty] [--allow-smoke-fail] [--base <ref>]');
|
|
47
|
+
console.error('usage: node scripts/gds/ship.js <task-id> (--notes-file <path|-> | --notes "...") (--summary-file <path|-> | --summary "...") [--visual <image>] [--visual-alt "caption"] [--surprise "text" | --surprise none | --no-surprise] [--no-merge] [--db-only] [--verified] [--allow-empty] [--allow-smoke-fail] [--base <ref>] [--ship-mode auto|manual]');
|
|
46
48
|
console.error(' prefer the -file forms for prose: a shell command-substitutes backticks in an inline value before node sees it (learning 1000197).');
|
|
47
49
|
process.exit(2);
|
|
48
50
|
}
|
|
@@ -157,6 +159,24 @@ async function shipMain() {
|
|
|
157
159
|
process.exit(1);
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
// ---- First ship asks for the ship mode, once (task 1004630, ADR 0369) ----
|
|
163
|
+
// A project that never chose Auto or Manual stops HERE, before anything resolves,
|
|
164
|
+
// so the AI can ask the person; the re-run carries --ship-mode and saves it. Never
|
|
165
|
+
// blocks a ship nobody can answer for (see ship-mode-ask.js).
|
|
166
|
+
const firstShip = await runFirstShipAsk({
|
|
167
|
+
args,
|
|
168
|
+
getShipMode: async () => {
|
|
169
|
+
const r = await apiCall('GET', '/api/bongos/project-settings/ship-mode');
|
|
170
|
+
return r.ok ? r.data : null;
|
|
171
|
+
},
|
|
172
|
+
saveShipMode: (mode) => apiCall('PATCH', '/api/bongos/project-settings/ship-mode', { mode }),
|
|
173
|
+
});
|
|
174
|
+
if (firstShip.stop) {
|
|
175
|
+
for (const line of firstShip.lines) console.log(line);
|
|
176
|
+
cliExit(firstShip.exit);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
160
180
|
// ---- Hard cap: refuse to ship over the monthly budget (V3.R84 #302, C3) ----
|
|
161
181
|
// Shipping runs the subagent grader, which spends API $. If this builder is at
|
|
162
182
|
// or over their monthly budget, refuse here — BEFORE the grader runs and BEFORE
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// scripts/gds/ship-mode-ask.js — the first-ship ship-mode question (task 1004630, ADR 0369).
|
|
2
|
+
//
|
|
3
|
+
// WHY. Ship mode says who starts a ship: Auto (the AI ships on its own) or Manual
|
|
4
|
+
// (a person approves each ship). The creation wizard asks for it (task 1004629),
|
|
5
|
+
// but a project made before that step, or by `bongos init`, never answered. Such a
|
|
6
|
+
// project quietly runs on the default, so its owner never learns there was a choice.
|
|
7
|
+
// This asks ONCE, at the next ship, and saves the answer. Once saved the mode reads
|
|
8
|
+
// as chosen and the question never comes back.
|
|
9
|
+
//
|
|
10
|
+
// HOW. The ship stops BEFORE anything resolves (the claim stays active) and prints
|
|
11
|
+
// an ask the AI turns into a two-option question for the person. The AI re-runs the
|
|
12
|
+
// same ship with `--ship-mode auto|manual`; that saves the answer and the ship goes on.
|
|
13
|
+
//
|
|
14
|
+
// It never blocks a ship it cannot get an answer for. The ship goes on with the
|
|
15
|
+
// default, and nobody is asked, when:
|
|
16
|
+
// - the mode could not be read (the resolver's own posture: a ship is never
|
|
17
|
+
// blocked by a settings read);
|
|
18
|
+
// - the caller cannot change project settings (only an owner-side builder can
|
|
19
|
+
// answer, so asking a teammate would stop every ship of theirs for nothing);
|
|
20
|
+
// - nobody is there to answer (an Autobongos worker or a subagent).
|
|
21
|
+
//
|
|
22
|
+
// The decision is pure (shipModeAskVerdict) so tests prove it with no network.
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
// The ship exits with this code when it stops to ask. Distinct from 1 (failure)
|
|
27
|
+
// and 2 (usage), and from the grade cap's 6, so a wrapper can tell "a question is
|
|
28
|
+
// waiting" from "something broke".
|
|
29
|
+
const ASK_EXIT = 5;
|
|
30
|
+
|
|
31
|
+
const ASK_MARKER = '[otb-ask-ship-mode]';
|
|
32
|
+
|
|
33
|
+
// unattended(env) — true when no person is there to answer.
|
|
34
|
+
// OTB_SUBAGENT is set on every Autobongos worker (scripts/gds/autobongos-run.js
|
|
35
|
+
// workerEnv); AUTOBONGOS_GRADE_LEDGER is set only by that supervisor.
|
|
36
|
+
function unattended(env = process.env) {
|
|
37
|
+
return env.OTB_SUBAGENT === '1' || Boolean(env.AUTOBONGOS_GRADE_LEDGER);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// shipModeFlag(args) — the --ship-mode answer, or null when absent.
|
|
41
|
+
// { mode } for auto|manual, { bad } for any other value (the ship refuses it).
|
|
42
|
+
function shipModeFlag(args) {
|
|
43
|
+
const i = args.indexOf('--ship-mode');
|
|
44
|
+
if (i === -1) return null;
|
|
45
|
+
const raw = String(args[i + 1] || '').trim().toLowerCase();
|
|
46
|
+
if (raw === 'auto' || raw === 'manual') return { mode: raw };
|
|
47
|
+
return { bad: raw || '(missing)' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// shipModeAskVerdict({ shipMode, flag, unattended }) →
|
|
51
|
+
// { action: 'proceed', why } — nothing to ask; ship on
|
|
52
|
+
// { action: 'save', mode } — the person answered; save it, then ship on
|
|
53
|
+
// { action: 'ask' } — stop and ask
|
|
54
|
+
// shipMode is the GET /project-settings/ship-mode body, or null when unreadable.
|
|
55
|
+
// can_change is absent on a server older than this task; absent means "unknown",
|
|
56
|
+
// and unknown asks (a refused save then lets the ship go on, see saveFailureLines).
|
|
57
|
+
function shipModeAskVerdict({ shipMode, flag, unattended: alone }) {
|
|
58
|
+
if (!shipMode) return { action: 'proceed', why: 'unreadable' };
|
|
59
|
+
if (shipMode.chosen) return { action: 'proceed', why: 'chosen' };
|
|
60
|
+
if (flag && flag.mode) return { action: 'save', mode: flag.mode };
|
|
61
|
+
if (shipMode.can_change === false) return { action: 'proceed', why: 'cannot_change' };
|
|
62
|
+
if (alone) return { action: 'proceed', why: 'unattended' };
|
|
63
|
+
return { action: 'ask' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// askLines(shipMode, argv) — what the ship prints when it stops to ask. The first
|
|
67
|
+
// lines are for the AI; the choices are the server's own words, so the question a
|
|
68
|
+
// person sees matches the settings page.
|
|
69
|
+
function askLines(shipMode, argv) {
|
|
70
|
+
const choices = (shipMode && Array.isArray(shipMode.choices) && shipMode.choices.length)
|
|
71
|
+
? shipMode.choices
|
|
72
|
+
: [
|
|
73
|
+
{ mode: 'auto', label: 'Auto (recommended)', sentence: 'When the AI finishes a task it ships and lands it. Nobody has to type a command.' },
|
|
74
|
+
{ mode: 'manual', label: 'Manual', sentence: 'The AI stops before each ship and asks a person to approve it.' },
|
|
75
|
+
];
|
|
76
|
+
const rerun = ['node', 'scripts/gds/ship.js', ...argv.filter((a, i) => a !== '--ship-mode' && argv[i - 1] !== '--ship-mode')]
|
|
77
|
+
.map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)).join(' ');
|
|
78
|
+
return [
|
|
79
|
+
`${ASK_MARKER} This project has never chosen a ship mode, so this first ship asks once.`,
|
|
80
|
+
'Ask the person this question with two options, Auto first, then re-run this ship with their answer:',
|
|
81
|
+
'',
|
|
82
|
+
' How should finished work ship on this project?',
|
|
83
|
+
...choices.map((c) => ` - ${c.label}: ${c.sentence}`),
|
|
84
|
+
'',
|
|
85
|
+
` Re-run: ${rerun} --ship-mode auto|manual`,
|
|
86
|
+
'',
|
|
87
|
+
'Nothing was resolved: the claim is still active. The answer is saved for the',
|
|
88
|
+
'whole project and can be changed later on the project settings page.',
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// saveFailureLines(status) — a refused or failed save never blocks the ship. The
|
|
93
|
+
// default (Auto) applies, and the person learns who can set it.
|
|
94
|
+
function saveFailureLines(status) {
|
|
95
|
+
const who = status === 403
|
|
96
|
+
? 'Only the project owner (or someone with project settings access) can set the ship mode,'
|
|
97
|
+
: `The ship mode could not be saved (${status || 'no response'}),`;
|
|
98
|
+
return [
|
|
99
|
+
`${who} so this ship goes on with the default, Auto.`,
|
|
100
|
+
'It can be set later on the project settings page, or with: node scripts/gds/shipping-mode.js auto|manual',
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// runFirstShipAsk({ args, getShipMode, saveShipMode, env, log }) — the side-effect
|
|
105
|
+
// half shipMain calls once it knows this is a real ship. Returns
|
|
106
|
+
// { stop: true, lines } when the ship must stop and ask, else { stop: false }.
|
|
107
|
+
// getShipMode() → body or null; saveShipMode(mode) → { ok, status, data }.
|
|
108
|
+
async function runFirstShipAsk({ args, getShipMode, saveShipMode, env = process.env, log = console.log }) {
|
|
109
|
+
const flag = shipModeFlag(args);
|
|
110
|
+
if (flag && flag.bad) {
|
|
111
|
+
return { stop: true, exit: 2, lines: [`--ship-mode must be auto or manual (got ${flag.bad}). Nothing was resolved: the claim is still active.`] };
|
|
112
|
+
}
|
|
113
|
+
let shipMode = null;
|
|
114
|
+
try { shipMode = await getShipMode(); } catch (_) { shipMode = null; }
|
|
115
|
+
const verdict = shipModeAskVerdict({ shipMode, flag, unattended: unattended(env) });
|
|
116
|
+
if (verdict.action === 'ask') return { stop: true, exit: ASK_EXIT, lines: askLines(shipMode, args) };
|
|
117
|
+
if (verdict.action === 'save') {
|
|
118
|
+
let r = null;
|
|
119
|
+
try { r = await saveShipMode(verdict.mode); } catch (_) { r = null; }
|
|
120
|
+
if (r && r.ok) {
|
|
121
|
+
log(`Ship mode saved: ${verdict.mode}. ${(r.data && r.data.sentence) || ''}`.trim());
|
|
122
|
+
} else {
|
|
123
|
+
for (const line of saveFailureLines(r && r.status)) log(line);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { stop: false };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
ASK_EXIT,
|
|
131
|
+
ASK_MARKER,
|
|
132
|
+
unattended,
|
|
133
|
+
shipModeFlag,
|
|
134
|
+
shipModeAskVerdict,
|
|
135
|
+
askLines,
|
|
136
|
+
saveFailureLines,
|
|
137
|
+
runFirstShipAsk,
|
|
138
|
+
};
|
package/scripts/gds/ship.js
CHANGED
|
@@ -50,6 +50,12 @@
|
|
|
50
50
|
// # game-surface diffs are otherwise staged to
|
|
51
51
|
// # the builder's own local preview (ADR 0044) for
|
|
52
52
|
// # a browser review BEFORE the claim resolves.
|
|
53
|
+
// node scripts/gds/ship.js <task-id> --ship-mode auto|manual
|
|
54
|
+
// # answer the first-ship question (task 1004630,
|
|
55
|
+
// # ADR 0369): a project that never chose a ship
|
|
56
|
+
// # mode stops once, before anything resolves, and
|
|
57
|
+
// # exits 5 with an ask; the re-run with the answer
|
|
58
|
+
// # saves it for the whole project and ships on.
|
|
53
59
|
// node scripts/gds/ship.js <task-id> --approved # record your sandbox sign-off (#1048): re-run with
|
|
54
60
|
// # this AFTER reviewing the staged change at the
|
|
55
61
|
// # sandbox URL. Persists a sandbox_review row on the
|
|
@@ -109,9 +109,11 @@ const MODULE_GLOBS = {
|
|
|
109
109
|
'scripts/gds/ship-finish.js', 'scripts/gds/ship-flow.js', 'scripts/gds/ship-git.js',
|
|
110
110
|
'scripts/gds/ship-grade-steps.js', 'scripts/gds/ship-grade.js', 'scripts/gds/ship-honesty.js',
|
|
111
111
|
'scripts/gds/ship-io.js', 'scripts/gds/ship-land.js', 'scripts/gds/ship-memory-sync.js',
|
|
112
|
-
'scripts/gds/ship-merge-gate.js', 'scripts/gds/ship-merge.js', 'scripts/gds/ship-
|
|
112
|
+
'scripts/gds/ship-merge-gate.js', 'scripts/gds/ship-merge.js', 'scripts/gds/ship-mode-ask.js',
|
|
113
|
+
'scripts/gds/ship-preflight-steps.js',
|
|
113
114
|
'scripts/gds/ship-regen.js', 'scripts/gds/ship-resume.js', 'scripts/gds/ship-session-upload.js',
|
|
114
115
|
'scripts/gds/ship-state.js', 'scripts/gds/ship-surprise.js', 'scripts/gds/ship-visual.js',
|
|
116
|
+
'scripts/gds/ship-mode-ask.js', // task 1004630
|
|
115
117
|
'scripts/gds/merge-mode.js', 'scripts/gds/confirm.js', 'scripts/gds/main-audit.js',
|
|
116
118
|
'scripts/gds/gate-review.js', 'scripts/gds/push-main.js', 'scripts/gds/ci-grade.js',
|
|
117
119
|
'scripts/gds/merge-lock.js', 'scripts/gds/version-close.js',
|