@bongos/core 1.19.948 → 1.19.949

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.
@@ -1,534 +0,0 @@
1
- #!/usr/bin/env node
2
- // scripts/gds/build-cli-package.js — generate the public, client-only `@cloudbongos/cli`
3
- // npm package from this repo (task 1003679, goal 1000054).
4
- //
5
- // WHY THIS EXISTS
6
- // The core ships as `@bongos/core`, which is PRIVATE. That makes the front door unusable for
7
- // anyone without a credential: a newcomer cannot install anything, so "take on a task" has no
8
- // terminal path at all and the web hall is the only way in. This script cuts the CLI *surface*
9
- // out of the core and emits a small, public, server-free package, so `npx @cloudbongos/cli
10
- // login <instance>` works from a bare laptop.
11
- //
12
- // WHAT IT IS NOT
13
- // It does NOT publish the core. No server, no routes, no auth middleware, no pool/db, no
14
- // provisioning. Publishing the core would bypass the ADR 0099 redaction pipeline; this package
15
- // carries a file list narrow enough to actually audit, and REDACTION_PATTERNS below fails the
16
- // build if instance identity leaks into it.
17
- //
18
- // WHY THE FILE LIST IS DECLARED, NOT COMPUTED
19
- // A static require-closure is unusable here. `src/module-api.js` is the published module doorway
20
- // (ADR 0083) and by design *names* every kernel capability, so any static walk that reaches a
21
- // module file appears to reach the whole server — even though the getters are lazy and never
22
- // resolve. The honest instrument is behaviour: FILES is declared, and tests/cli_package.mjs packs
23
- // the tarball, installs it into an empty directory with no repo present, and RUNS every verb.
24
- // If a real code path needs a file that isn't here, that test fails with MODULE_NOT_FOUND.
25
- //
26
- // Usage:
27
- // node scripts/gds/build-cli-package.js [--out dist/cloudbongos-cli] [--version 0.1.0] [--quiet]
28
-
29
- 'use strict';
30
-
31
- const fs = require('node:fs');
32
- const path = require('node:path');
33
-
34
- const REPO_ROOT = path.join(__dirname, '..', '..');
35
-
36
- // The package's OWN version — deliberately independent of the core's. The core moves many times
37
- // a day (CI auto-patch, ADR 0161); the CLI's public contract should not.
38
- const PACKAGE_VERSION = '0.1.3';
39
- const PACKAGE_NAME = '@cloudbongos/cli';
40
-
41
- // ── What the package carries ────────────────────────────────────────────────────────────────
42
- // Every entry is repo-relative and copied verbatim. Grouped by why it's here, because the
43
- // grouping is the audit: anything that doesn't fit a group below does not belong in a public
44
- // client package.
45
- const FILES = [
46
- // The shared CLI plumbing every verb goes through: session file, HTTP, error shapes.
47
- 'scripts/gds/cli-lib.js',
48
- 'scripts/gds/api.js',
49
-
50
- // Account + sign-in. login.js is the whole point — GitHub device flow, no paste, no credential.
51
- 'scripts/gds/login.js',
52
- 'scripts/gds/reauth.js',
53
- 'scripts/gds/setup.js',
54
- 'scripts/gds/device-flow-help.js', // shared leaf: login.js + setup.js both require it (task 1003881)
55
- 'scripts/gds/onboarding-config.js', // lazy dep of setup.js
56
- 'scripts/gds/install-git-hooks.js', // lazy dep of setup.js
57
- 'scripts/gds/preflight.js',
58
- 'scripts/gds/surface.js', // who the output is for: agent vs terminal (task 1003680)
59
- 'scripts/gds/plain-cards.js', // required by cli-lib + release; prints nothing on the terminal surface (task 1003989)
60
- // The partial-checkout preflight (task 1003098). start/claim/ship require it
61
- // ABOVE their first module require, so an absent file here breaks every verb in
62
- // the published package -- which is exactly how tests/cli_package.mjs caught it.
63
- // Pure: node builtins only, no repo, no network.
64
- 'scripts/gds/tree-preflight.js',
65
-
66
- // Read the board and move your own work along.
67
- 'scripts/gds/start.js',
68
- 'scripts/gds/context-pack.js', // start.js renders through this
69
- 'scripts/gds/status.js',
70
- // status.js resolves its default version at runtime instead of naming one
71
- // (task 1004071) -- so the packaged CLI needs this too, or `bongos status`
72
- // dies on a missing module. tests/cli_package.mjs caught exactly that.
73
- 'scripts/gds/version-select.js',
74
- 'scripts/gds/task.js',
75
- 'scripts/gds/recall.js',
76
- 'scripts/gds/cost.js',
77
- 'scripts/gds/release.js',
78
- 'scripts/gds/worktree-claim-guard.js', // release.js warns through it; pure, zero requires
79
-
80
- // Module files the above touch. Each imports nothing from the kernel at load time.
81
- 'modules/lifecycle/task-classifier.js',
82
- 'modules/lifecycle/task-visuals.js',
83
- // The criterion-ref vocabulary status.js resolves a `Cn` or a slug through
84
- // (task 1004069). It is in modules/ because the WRITE path uses the same
85
- // definition; it requires nothing, which is the whole reason it could be
86
- // carved out of db-deps-criteria.js (that file needs a pool) and shipped here.
87
- 'modules/lifecycle/criterion-ref.js',
88
- 'modules/economy/reward-policy.js',
89
-
90
- // The only core files in the package: config/branding readers and one constant table.
91
- // src/module-api.js is here because task-classifier.js reads branding through the doorway
92
- // (ADR 0083 forbids a module importing src/branding directly). Its `branding` getter resolves
93
- // against src/branding.js, which ships; the other getters point at server files that do not,
94
- // and no client path touches them — the smoke test is what proves that.
95
- 'src/branding.js',
96
- 'src/instance-config.js',
97
- 'src/module-seams.js',
98
- 'src/module-api.js',
99
- 'src/bongos/api-prefix.js',
100
-
101
- // The generated typed API client (ADR 0118), which cli-lib.js reaches by a dynamic
102
- // `import()` of this exact path. Vendored as a single file rather than depended on: it is not
103
- // published to npm at all (task 1003742 — it was private, had zero consumers, and was removed),
104
- // so there is nothing to declare a dependency ON. Vendoring was already the right call while it
105
- // was merely private, since a public package cannot depend on a private one. index.mjs is
106
- // self-contained and imports nothing; the sibling package.json is deliberately NOT shipped —
107
- // a nested manifest only confuses packing.
108
- 'clients/bongos-client/index.mjs',
109
-
110
- // Neutral branding defaults, so the CLI has sane strings with no instance checkout present.
111
- 'config/branding.neutral.json',
112
- ];
113
-
114
- // Paths that must NEVER appear in the generated tree, whatever FILES says. A belt-and-braces
115
- // assertion against a future edit quietly widening the package back into the server.
116
- const FORBIDDEN_PREFIXES = [
117
- 'src/bongos/routes/',
118
- 'src/server',
119
- 'migrations/',
120
- 'infra/',
121
- 'public/',
122
- 'modules/hall-ui/',
123
- '.github/',
124
- ];
125
- const FORBIDDEN_BASENAMES = [
126
- 'pool.js', 'auth.js', 'db.js', 'server.js', 'serve-internal.js',
127
- 'logger.js', 'project-door.js', 'project-settings.js',
128
- ];
129
- // src/bongos/ is the kernel. Exactly one file from it is allowed, and it imports nothing.
130
- const ALLOWED_SRC_BONGOS = new Set(['src/bongos/api-prefix.js']);
131
-
132
- // ── The verbs the package advertises ────────────────────────────────────────────────────────
133
- // `script` is resolved inside the package. Keep in lockstep with FILES.
134
- const VERBS = {
135
- login: { script: 'login.js', group: 'account', summary: 'Sign in to an instance (bongos login <url> — browser click, no paste)' },
136
- reauth: { script: 'reauth.js', group: 'account', summary: 'Refresh your session' },
137
- setup: { script: 'setup.js', group: 'account', summary: 'Finish builder setup on this instance (disciplines, consent)' },
138
-
139
- start: { script: 'start.js', group: 'work', summary: 'List tasks you can claim right now' },
140
- status: { script: 'status.js', group: 'work', summary: 'Version / criterion progress (bongos status C8)', selfHelp: true },
141
- task: { script: 'task.js', group: 'work', summary: 'Create or show a task', selfHelp: true },
142
- recall: { script: 'recall.js', group: 'work', summary: 'Search the docs + project knowledge (bongos recall "x")' },
143
- cost: { script: 'cost.js', group: 'work', summary: 'Log a cost entry' },
144
- release: { script: 'release.js', group: 'work', summary: 'Cancel a claim — task returns to ready' },
145
-
146
- api: { script: 'api.js', group: 'work', summary: 'Call any instance endpoint (bongos api GET /api/bongos/me)' },
147
- };
148
-
149
- // Verbs that exist in the full core CLI and are deliberately absent here. The dispatcher prints
150
- // the reason and the actual next step, because a newcomer hitting a bare "unknown command" learns
151
- // nothing — that silence is the exact failure this whole task is fixing.
152
- //
153
- // The next step used to be CONDITIONAL prose offering a cloud dev box first. The dev box was
154
- // retired (task 1003889), so the one unconditional route — a checkout of the project repo — is
155
- // now the only branch. ADR 0258's exclusion of claim/ship is UNCHANGED and deliberate: this is
156
- // the PRE-checkout CLI, and a claim that cannot materialise into a checkout is worse than one
157
- // that says where to go.
158
- const CHECKOUT_ONLY = {
159
- claim: 'Claiming a task writes code, so it needs a real checkout and a worktree.',
160
- ship: 'Shipping grades, merges and deploys from a real checkout.',
161
- dev: 'Runs an instance locally — needs the instance repo.',
162
- serve: 'Runs an instance on a host — needs the instance repo.',
163
- module: 'Scaffolds a module into an instance repo.',
164
- upgrade: 'Moves an instance to a new core version.',
165
- onboard: 'Provisions new infrastructure — owner tooling, not in the public CLI.',
166
- doctor: 'Checks a local checkout toolchain.',
167
- exec: 'Runs a core script from inside the core package.',
168
- 'package-core': 'Packages the core — owner tooling.',
169
- };
170
-
171
- // ── Redaction gate ──────────────────────────────────────────────────────────────────────────
172
- // Generic shapes only. Instance-specific needles are discovered at build time from the host's
173
- // own config (below) so the core itself carries no instance identity — the ADR 0062 §7 split.
174
- const REDACTION_PATTERNS = [
175
- { name: 'non-loopback IPv4', re: /\b(?!127\.|0\.0\.0\.0|255\.)(?:\d{1,3}\.){3}\d{1,3}\b/g },
176
- { name: 'github token', re: /\bgh[pousr]_[A-Za-z0-9]{16,}/g },
177
- { name: 'github fine-grained token', re: /\bgithub_pat_[A-Za-z0-9_]{20,}/g },
178
- { name: 'npm token', re: /\bnpm_[A-Za-z0-9]{30,}/g },
179
- { name: 'private key block', re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g },
180
- { name: 'aws access key', re: /\bAKIA[0-9A-Z]{16}\b/g },
181
- ];
182
-
183
- // Version strings (1.19.576) and dotted identifiers look like IPv4 to a loose regex; a real
184
- // address has four all-numeric octets each <= 255 and is not a semver.
185
- //
186
- // The documentation ranges (RFC 5737) and link-local are exempt: they can never name real
187
- // infrastructure, so they are the CORRECT thing to write in an example. Nothing else is
188
- // exempt — private ranges included, because an internal address still leaks topology and
189
- // deserves a human look before it goes public.
190
- const DOC_IPV4 = [/^192\.0\.2\./, /^198\.51\.100\./, /^203\.0\.113\./, /^169\.254\./];
191
-
192
- function isRealIPv4(s) {
193
- const parts = s.split('.');
194
- if (parts.length !== 4) return false;
195
- if (!parts.every((p) => /^\d{1,3}$/.test(p) && Number(p) <= 255)) return false;
196
- return !DOC_IPV4.some((re) => re.test(s));
197
- }
198
-
199
- // The project's own public identity. `cloudbongos.com` is the flagship instance and the CLI's
200
- // documented default — the way `registry.npmjs.org` is baked into npm — so it is published on
201
- // purpose and must not trip the gate. Anything NOT on this list that comes out of an instance's
202
- // branding is treated as private identity and blocks the build.
203
- const PUBLIC_BY_DESIGN = new Set(['cloudbongos.com']);
204
-
205
- // Instance identity to refuse: the host's own domains and owner login, read from the instance's
206
- // committed branding if this checkout has one. Absent (a neutral core), the gate still runs the
207
- // generic patterns above.
208
- function instanceNeedles() {
209
- const needles = new Set();
210
- const add = (v) => {
211
- if (typeof v !== 'string') return;
212
- const s = v.trim().toLowerCase();
213
- if (s.length > 3 && !PUBLIC_BY_DESIGN.has(s)) needles.add(s);
214
- };
215
- try {
216
- const b = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'config', 'branding.json'), 'utf8'));
217
- for (const v of Object.values(b.domains || {})) add(String(v).replace(/^https?:\/\//, '').replace(/\/.*$/, ''));
218
- add(b.identity && b.identity.ownerLogin);
219
- add(b.repo && b.repo.owner);
220
- } catch (_) { /* neutral core: no instance branding, nothing to redact */ }
221
- for (const extra of String(process.env.CLI_PACKAGE_FORBIDDEN || '').split(',')) add(extra);
222
- return [...needles];
223
- }
224
-
225
- function scanRedaction(outDir, files) {
226
- const needles = instanceNeedles();
227
- const hits = [];
228
- for (const rel of files) {
229
- const abs = path.join(outDir, rel);
230
- let text;
231
- try { text = fs.readFileSync(abs, 'utf8'); } catch { continue; }
232
- for (const { name, re } of REDACTION_PATTERNS) {
233
- for (const m of text.matchAll(re)) {
234
- if (name === 'non-loopback IPv4' && !isRealIPv4(m[0])) continue;
235
- const line = text.slice(0, m.index).split('\n').length;
236
- hits.push(`${rel}:${line} ${name}: ${m[0]}`);
237
- }
238
- }
239
- const lower = text.toLowerCase();
240
- for (const n of needles) {
241
- let i = lower.indexOf(n);
242
- while (i !== -1) {
243
- hits.push(`${rel}:${text.slice(0, i).split('\n').length} instance identity: ${n}`);
244
- i = lower.indexOf(n, i + n.length);
245
- }
246
- }
247
- }
248
- return hits;
249
- }
250
-
251
- // ── Generated files ─────────────────────────────────────────────────────────────────────────
252
-
253
- function dispatcherSource() {
254
- const verbs = JSON.stringify(VERBS, null, 2).replace(/\n/g, '\n');
255
- const checkout = JSON.stringify(CHECKOUT_ONLY, null, 2).replace(/\n/g, '\n');
256
- return `#!/usr/bin/env node
257
- // bin/bongos.js — the public Cloud Bongos CLI.
258
- //
259
- // GENERATED by scripts/gds/build-cli-package.js in the Cloud Bongos core. Do not edit by hand:
260
- // edit the generator and rebuild.
261
- //
262
- // A thin dispatcher over the client scripts in this package. It spawns each with process.execPath
263
- // and forwards argv + the exit code; no auth or API logic lives here (that is cli-lib.js).
264
-
265
- 'use strict';
266
-
267
- const path = require('node:path');
268
- const { spawnSync } = require('node:child_process');
269
-
270
- const PKG_ROOT = path.join(__dirname, '..');
271
- const SCRIPTS_DIR = path.join(PKG_ROOT, 'scripts', 'gds');
272
- const VERSION = require('../package.json').version;
273
-
274
- const VERBS = ${verbs};
275
-
276
- const CHECKOUT_ONLY = ${checkout};
277
-
278
- const GROUPS = [['account', 'Account & environment'], ['work', 'Work']];
279
-
280
- function helpText() {
281
- const lines = ['bongos — the Cloud Bongos CLI', '', 'Usage: bongos <command> [args]'];
282
- lines.push('', 'First time here?');
283
- lines.push(' bongos login https://your-instance.com sign in with a browser click');
284
- lines.push(' bongos start see what you can pick up');
285
- for (const [key, label] of GROUPS) {
286
- lines.push('', label + ':');
287
- for (const [verb, def] of Object.entries(VERBS)) {
288
- if (def.group !== key) continue;
289
- lines.push(' ' + verb.padEnd(10) + ' ' + def.summary);
290
- }
291
- }
292
- lines.push('', ' help Show this help', ' version Show the bongos version');
293
- lines.push('');
294
- lines.push('Writing code (claim, ship) happens in a checkout of the project repo,');
295
- lines.push('where the full CLI is already installed.');
296
- return lines.join('\\n');
297
- }
298
-
299
- function verbHelpText(verb, def) {
300
- return [
301
- 'bongos ' + verb + ' — ' + def.summary,
302
- '',
303
- 'Forwards to scripts/gds/' + def.script + '; extra arguments are passed through.',
304
- 'Run \`bongos help\` to see every command.',
305
- ].join('\\n');
306
- }
307
-
308
- // Returns the exit code (does not call process.exit, so it stays testable).
309
- function main(argv) {
310
- const args = argv.slice(2);
311
- const verb = args[0];
312
-
313
- if (!verb || verb === 'help' || verb === '--help' || verb === '-h') {
314
- process.stdout.write(helpText() + '\\n');
315
- return 0;
316
- }
317
- if (verb === 'version' || verb === '--version' || verb === '-v') {
318
- process.stdout.write('bongos ' + VERSION + '\\n');
319
- return 0;
320
- }
321
-
322
- if (CHECKOUT_ONLY[verb]) {
323
- process.stderr.write(
324
- 'bongos: "' + verb + '" is not in the public CLI.\\n ' + CHECKOUT_ONLY[verb] + '\\n\\n' +
325
- 'The route is a checkout of the project repo, where the full CLI\\n' +
326
- '(including ' + verb + ') is installed. Ask the project owner for repo access.\\n'
327
- );
328
- return 2;
329
- }
330
-
331
- const def = VERBS[verb];
332
- if (!def) {
333
- process.stderr.write('bongos: unknown command "' + verb + '"\\n\\n' + helpText() + '\\n');
334
- return 2;
335
- }
336
-
337
- if (!def.selfHelp && args.slice(1).some((a) => a === '--help' || a === '-h')) {
338
- process.stdout.write(verbHelpText(verb, def) + '\\n');
339
- return 0;
340
- }
341
-
342
- const target = path.join(SCRIPTS_DIR, def.script);
343
- const spawnArgs = [target, ...(def.prefixArgs || []), ...args.slice(1)];
344
- // Everyone reaching this package is a person at a terminal, so say so: scripts/gds/surface.js
345
- // switches off the [otb-card] model directives and the /builder-* slash commands (task 1003680).
346
- const res = spawnSync(process.execPath, spawnArgs, {
347
- stdio: 'inherit',
348
- env: { ...process.env, BONGOS_SURFACE: 'terminal' },
349
- });
350
- if (res.error) {
351
- process.stderr.write('bongos: could not run ' + def.script + ': ' + res.error.message + '\\n');
352
- return 1;
353
- }
354
- return res.status == null ? 1 : res.status;
355
- }
356
-
357
- if (require.main === module) process.exit(main(process.argv));
358
- module.exports = { main, helpText, VERBS, CHECKOUT_ONLY };
359
- `;
360
- }
361
-
362
- function packageJsonSource(version) {
363
- // DERIVED from the manifest, never hand-listed. A hand-written `files` array silently drops
364
- // whatever it forgets: the first build of this package copied clients/bongos-client/index.mjs
365
- // correctly and then `npm pack` left it out, so the tarball installed and died on first use.
366
- const roots = [...new Set(FILES.map((f) => f.split('/')[0] + '/'))].sort();
367
- return JSON.stringify({
368
- name: PACKAGE_NAME,
369
- version,
370
- description: 'The Cloud Bongos CLI — sign in to an instance and see what you can build. Client only: no server.',
371
- bin: { bongos: 'bin/bongos.js' },
372
- files: ['bin/', ...roots, 'README.md'],
373
- engines: { node: '>=20' },
374
- dependencies: { undici: '^6.19.8' },
375
- keywords: ['cloud-bongos', 'cli', 'ai-agents', 'build-platform'],
376
- license: 'AGPL-3.0-or-later',
377
- // No `repository` field on purpose. The core repo is private today, so a repository URL
378
- // would 404 for every user of a public package AND put the owner's login in it for nothing.
379
- // Point it at the redacted public mirror once that lands (ADR 0099).
380
- homepage: 'https://cloudbongos.com',
381
- bugs: { url: 'https://cloudbongos.com/builders' },
382
- publishConfig: { access: 'public' },
383
- }, null, 2) + '\n';
384
- }
385
-
386
- function readmeSource(version) {
387
- return `# ${PACKAGE_NAME}
388
-
389
- The [Cloud Bongos](https://cloudbongos.com) CLI. Sign in to an instance and see what you can
390
- pick up — from a terminal, with nothing installed.
391
-
392
- ## Start here
393
-
394
- \`\`\`sh
395
- npx ${PACKAGE_NAME} login https://your-instance.com
396
- npx ${PACKAGE_NAME} start
397
- \`\`\`
398
-
399
- \`login\` opens your browser, you click once, and the session is written to
400
- \`~/.config/cloudbongos/\`. There is no token to copy and no password to set. If the instance
401
- gates admission, \`login\` files your access request and the owner approves it.
402
-
403
- Install it properly once you're past the first run:
404
-
405
- \`\`\`sh
406
- npm install -g ${PACKAGE_NAME}
407
- bongos start
408
- \`\`\`
409
-
410
- ## Commands
411
-
412
- Run \`bongos help\` for the current list. In short: \`login\`, \`reauth\`, \`setup\` for your
413
- account; \`start\`, \`status\`, \`task\`, \`recall\`, \`cost\`, \`release\`, \`api\` for the work.
414
-
415
- \`bongos api\` is the escape hatch — it calls any endpoint on the instance as you, so anything
416
- the web hall can do is reachable from the terminal:
417
-
418
- \`\`\`sh
419
- bongos api GET /api/bongos/me
420
- \`\`\`
421
-
422
- ## Writing code
423
-
424
- \`claim\` and \`ship\` need a real checkout, so they aren't in this package. Ask the project's
425
- owner for repo access and clone it — the full CLI is installed there, and every command in
426
- this package works the same way from inside it.
427
-
428
- ## What this package is
429
-
430
- The client surface of the Cloud Bongos core, and nothing else — no server, no routes, no
431
- database, no provisioning. It is generated from the core by
432
- \`scripts/gds/build-cli-package.js\` and verified by installing the packed tarball into an empty
433
- directory and running every command.
434
-
435
- Version ${version} · AGPL-3.0-or-later
436
- `;
437
- }
438
-
439
- // ── Build ───────────────────────────────────────────────────────────────────────────────────
440
-
441
- function build(opts = {}) {
442
- const outDir = path.resolve(REPO_ROOT, opts.out || 'dist/cloudbongos-cli');
443
- const version = opts.version || PACKAGE_VERSION;
444
- const log = opts.quiet ? () => {} : (m) => process.stdout.write(m + '\n');
445
-
446
- // Refuse a file list that contradicts the package's whole premise, before copying anything.
447
- const violations = [];
448
- for (const rel of FILES) {
449
- if (rel.startsWith('src/bongos/') && !ALLOWED_SRC_BONGOS.has(rel)) {
450
- violations.push(`${rel} — src/bongos/ is the kernel; only ${[...ALLOWED_SRC_BONGOS].join(', ')} may ship`);
451
- }
452
- if (FORBIDDEN_PREFIXES.some((p) => rel.startsWith(p))) violations.push(`${rel} — forbidden path prefix`);
453
- if (FORBIDDEN_BASENAMES.includes(path.basename(rel)) && !ALLOWED_SRC_BONGOS.has(rel)) {
454
- violations.push(`${rel} — forbidden filename (server surface)`);
455
- }
456
- if (!fs.existsSync(path.join(REPO_ROOT, rel))) violations.push(`${rel} — missing from the repo`);
457
- }
458
- if (violations.length) {
459
- const err = new Error('cli package manifest rejected:\n ' + violations.join('\n '));
460
- err.violations = violations;
461
- throw err;
462
- }
463
-
464
- // Dynamic `import()` of a relative path is invisible to any require()-based reasoning, and
465
- // that is not hypothetical: cli-lib.js reaches the generated API client that way, and the
466
- // first build shipped without it. Every relative import target in a shipped file must itself
467
- // be shipped, or the package installs fine and dies on first use.
468
- const unshipped = [];
469
- for (const rel of FILES) {
470
- if (!/\.(js|mjs|cjs)$/.test(rel)) continue;
471
- const text = fs.readFileSync(path.join(REPO_ROOT, rel), 'utf8');
472
- for (const m of text.matchAll(/\bimport\(\s*['"](\.[^'"]+)['"]\s*\)/g)) {
473
- // FILES holds POSIX repo-relative paths, so the resolved target is spelled the
474
- // same way before the lookup — path.relative emits 'clients\bongos-client\…' on
475
- // win32, which matched nothing and failed every build on Windows (task 1003529).
476
- const target = path.relative(REPO_ROOT, path.resolve(path.dirname(path.join(REPO_ROOT, rel)), m[1]))
477
- .split(path.sep).join('/');
478
- if (!FILES.includes(target)) unshipped.push(`${rel} dynamically imports ${target}, which the manifest does not ship`);
479
- }
480
- }
481
- if (unshipped.length) {
482
- const err = new Error('cli package manifest incomplete:\n ' + unshipped.join('\n '));
483
- err.violations = unshipped;
484
- throw err;
485
- }
486
-
487
- fs.rmSync(outDir, { recursive: true, force: true });
488
- fs.mkdirSync(outDir, { recursive: true });
489
-
490
- for (const rel of FILES) {
491
- const dest = path.join(outDir, rel);
492
- fs.mkdirSync(path.dirname(dest), { recursive: true });
493
- fs.copyFileSync(path.join(REPO_ROOT, rel), dest);
494
- }
495
-
496
- fs.mkdirSync(path.join(outDir, 'bin'), { recursive: true });
497
- fs.writeFileSync(path.join(outDir, 'bin', 'bongos.js'), dispatcherSource(), { mode: 0o755 });
498
- fs.writeFileSync(path.join(outDir, 'package.json'), packageJsonSource(version));
499
- fs.writeFileSync(path.join(outDir, 'README.md'), readmeSource(version));
500
-
501
- const generated = [...FILES, 'bin/bongos.js', 'package.json', 'README.md'];
502
- const leaks = scanRedaction(outDir, generated);
503
- if (leaks.length) {
504
- const err = new Error(
505
- 'cli package redaction gate FAILED — refusing to emit:\n ' + leaks.join('\n ') +
506
- '\n\nThis package is PUBLIC. Build it from the neutral core, not from an instance checkout' +
507
- '\n(an instance\'s config/branding.json names its own domains and owner).'
508
- );
509
- err.leaks = leaks;
510
- throw err;
511
- }
512
-
513
- const bytes = generated.reduce((n, rel) => {
514
- try { return n + fs.statSync(path.join(outDir, rel)).size; } catch { return n; }
515
- }, 0);
516
-
517
- log(`${PACKAGE_NAME}@${version} → ${path.relative(REPO_ROOT, outDir)}`);
518
- log(` ${generated.length} files · ${(bytes / 1024).toFixed(0)} KB · ${Object.keys(VERBS).length} verbs · 1 dependency (undici)`);
519
- log(` redaction gate: clean (${instanceNeedles().length} instance needles checked)`);
520
- return { outDir, version, files: generated, verbs: Object.keys(VERBS) };
521
- }
522
-
523
- module.exports = { build, FILES, VERBS, CHECKOUT_ONLY, FORBIDDEN_PREFIXES, FORBIDDEN_BASENAMES, ALLOWED_SRC_BONGOS, PACKAGE_NAME, PACKAGE_VERSION, scanRedaction, instanceNeedles };
524
-
525
- if (require.main === module) {
526
- const argv = process.argv.slice(2);
527
- const flag = (name) => { const i = argv.indexOf(name); return i === -1 ? null : argv[i + 1]; };
528
- try {
529
- build({ out: flag('--out'), version: flag('--version'), quiet: argv.includes('--quiet') });
530
- } catch (err) {
531
- process.stderr.write(String(err.message) + '\n');
532
- process.exit(1);
533
- }
534
- }