@bongos/core 1.24.4 → 1.24.6

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.
Files changed (61) hide show
  1. package/.bongos-core.json +99 -59
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +6 -0
  4. package/clients/bongos-client/index.cjs +6 -0
  5. package/clients/bongos-client/index.d.ts +7 -0
  6. package/clients/bongos-client/index.mjs +6 -0
  7. package/docs/adr/0396-google-sign-in-a-stand-in-login-and-a-browser-approved-cli-sign-in.md +8 -0
  8. package/docs/api/openapi.json +110 -5
  9. package/docs/api-reference.md +6 -3
  10. package/docs/copy-inventory.md +47 -43
  11. package/docs/copy-registry.json +81 -45
  12. package/docs/file-map.md +3 -2
  13. package/docs/module-api-changelog.md +4 -2
  14. package/docs/page-inventory.json +2 -1
  15. package/docs/page-readings.json +64 -56
  16. package/migrations/core_286_credit_log_no_spend.sql +128 -0
  17. package/modules/copy-desk/auto-close.js +51 -11
  18. package/modules/copy-desk/page-status.js +18 -6
  19. package/modules/copy-desk/tests/copy_no_cms.mjs +8 -1
  20. package/modules/copy-desk/tests/copy_page_auto_close.mjs +68 -6
  21. package/modules/copy-desk/tests/copy_page_status.mjs +5 -1
  22. package/modules/hall-ui/public/tweak-editor.js +2 -1
  23. package/modules/lifecycle/db-claims.js +2 -2
  24. package/modules/lifecycle/db-ship.js +2 -2
  25. package/modules/lifecycle/routes/claims.js +7 -1
  26. package/modules/platform-identity/account-logins.js +131 -0
  27. package/modules/platform-identity/migrations/platform_identity_035_account_login_email_idx.sql +19 -0
  28. package/modules/platform-identity/routes/profile.js +29 -1
  29. package/modules/platform-identity/routes/sso.js +25 -3
  30. package/modules/public-landing/public/account.html +114 -0
  31. package/modules/public-landing/public/account.states.json +1 -0
  32. package/modules/ui-design/kit/serve.js +24 -0
  33. package/package-lock.json +2 -2
  34. package/package.json +1 -1
  35. package/release-notes.json +39 -1
  36. package/scripts/bongos/codemod-rename-scripts-gds.js +1 -3
  37. package/scripts/bongos/dev-box-guard.js +0 -1
  38. package/scripts/bongos/publish-manifest.js +1 -1
  39. package/scripts/bongos/run-unit-tests.js +4 -0
  40. package/src/bongos/agent-standing-rules.js +44 -0
  41. package/src/bongos/auth-github.js +24 -7
  42. package/src/bongos/module-scope-map.js +3 -2
  43. package/src/bongos/routes/auth-google.js +71 -5
  44. package/src/bongos/routes/auth-link.js +132 -0
  45. package/src/bongos/routes/me.js +4 -1
  46. package/src/bongos/routes.js +2 -0
  47. package/src/bongos/sign-in-pages.js +20 -1
  48. package/src/module-api.js +1 -1
  49. package/tests/account_linking.mjs +289 -0
  50. package/tests/account_logins_db.mjs +56 -0
  51. package/tests/agent_standing_rules.mjs +142 -0
  52. package/tests/claims_batch_routes.mjs +115 -25
  53. package/tests/copy_desk_page_release.mjs +20 -0
  54. package/tests/copy_stuck_page_tweaks.mjs +5 -3
  55. package/tests/credit_log_no_spend_db.mjs +160 -0
  56. package/tests/effective_visibility_predicate.mjs +8 -0
  57. package/tests/government_laws_session.mjs +2 -1
  58. package/tests/hall_tweak_editor.mjs +11 -0
  59. package/tests/host_topology_skips.mjs +2 -0
  60. package/tests/hub_account_page.mjs +91 -5
  61. package/tests/run_node_reroute.mjs +136 -0
@@ -0,0 +1,136 @@
1
+ // tests/run_node_reroute.mjs — the hook resolver runs the SESSION's copy of a hook
2
+ // (.claude/hooks/run-node.sh, task 1005015). Claude Code sets $CLAUDE_PROJECT_DIR to the
3
+ // main checkout even in a worktree session, so every hook ran from there; on a stale main
4
+ // checkout that hid the human-task markers (task 1004922) entirely. Each case builds fake
5
+ // checkouts, SPAWNS the real resolver with the real settings.json argument shape, and
6
+ // lets a probe hook report which file ran and what CLAUDE_PROJECT_DIR it saw.
7
+ //
8
+ // The guard hooks run through the resolver too, so the decoy cases matter as much as
9
+ // the happy path: only a linked git worktree OF THIS PROJECT may have its copy run.
10
+ //
11
+ // .claude/hooks/ is package-excluded, so a clean core checkout skips this file
12
+ // (tests/host_topology_skips.mjs carries the allowlist entry).
13
+ //
14
+ // Run: node tests/run_node_reroute.mjs
15
+
16
+ import { strict as assert } from 'node:assert';
17
+ import { spawnSync } from 'node:child_process';
18
+ import fs from 'node:fs';
19
+ import os from 'node:os';
20
+ import path from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ import { skipUnlessPresent } from './helpers.mjs';
23
+
24
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
25
+ const RESOLVER = path.join(ROOT, '.claude', 'hooks', 'run-node.sh');
26
+
27
+ let passed = 0;
28
+ let failed = 0;
29
+ function t(name, fn) {
30
+ try { fn(); passed++; console.log(` PASS ${name}`); } catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
31
+ }
32
+
33
+ skipUnlessPresent(['.claude/hooks/run-node.sh']);
34
+
35
+ const PROBE = "process.stdout.write(JSON.stringify({ file: __filename, pd: process.env.CLAUDE_PROJECT_DIR }))\n";
36
+
37
+ // A fake Bongos checkout. `linkTo` makes it a linked worktree of that main checkout the
38
+ // way git does: a .git FILE naming <main>/.git/worktrees/<name>, whose gitdir file points
39
+ // back (git writes forward slashes on every platform). `backTo` forges a wrong back-pointer.
40
+ const fwd = (p) => p.split(path.sep).join('/');
41
+ function checkout(dir, { linkTo = null, backTo = null, core = true, probe = true } = {}) {
42
+ fs.mkdirSync(path.join(dir, '.claude', 'hooks'), { recursive: true });
43
+ fs.copyFileSync(RESOLVER, path.join(dir, '.claude', 'hooks', 'run-node.sh'));
44
+ if (probe) fs.writeFileSync(path.join(dir, '.claude', 'hooks', 'probe.js'), PROBE);
45
+ if (linkTo) {
46
+ const admin = path.join(linkTo, '.git', 'worktrees', path.basename(dir));
47
+ fs.mkdirSync(admin, { recursive: true });
48
+ fs.writeFileSync(path.join(admin, 'gitdir'), `${fwd(path.join(backTo || dir, '.git'))}\n`);
49
+ fs.writeFileSync(path.join(dir, '.git'), `gitdir: ${fwd(admin)}\n`);
50
+ } else {
51
+ fs.mkdirSync(path.join(dir, '.git'), { recursive: true });
52
+ }
53
+ if (core) fs.mkdirSync(path.join(dir, 'src', 'bongos'), { recursive: true });
54
+ return dir;
55
+ }
56
+
57
+ // The argument shape settings.json uses: "$CLAUDE_PROJECT_DIR/.claude/hooks/<hook>.js".
58
+ function run({ main, cwd, env = {} }) {
59
+ const r = spawnSync('sh', [path.join(main, '.claude', 'hooks', 'run-node.sh'), `${main}/.claude/hooks/probe.js`], {
60
+ cwd,
61
+ env: { ...process.env, CLAUDE_PROJECT_DIR: main, OTB_NODE_BIN: process.execPath, OTB_HOOKS_FROM_PROJECT_DIR: '', ...env },
62
+ encoding: 'utf8',
63
+ });
64
+ if (r.error) throw r.error;
65
+ assert.equal(r.status, 0, `resolver exited ${r.status}: ${r.stderr}`);
66
+ return JSON.parse(r.stdout);
67
+ }
68
+
69
+ // Windows paths are case-insensitive, and the resolver hands node a lower-case drive.
70
+ const canon = (p) => (process.platform === 'win32' ? fs.realpathSync.native(p).toLowerCase() : fs.realpathSync(p));
71
+ const same = (a, b) => canon(a) === canon(b);
72
+
73
+ if (spawnSync('sh', ['-c', 'exit 0']).status !== 0) {
74
+ console.log('SKIP run_node_reroute: no `sh` on PATH');
75
+ process.exit(0);
76
+ }
77
+
78
+ // realpath: on Windows os.tmpdir() can be an 8.3 short name (GMK1_P~1), which git never
79
+ // writes and the shell's $PWD never shows, so the fixture's paths would not compare.
80
+ const tmp = fs.realpathSync.native(fs.mkdtempSync(path.join(os.tmpdir(), 'run-node-reroute-')));
81
+ const main = checkout(path.join(tmp, 'main'));
82
+ const MAIN_PROBE = path.join(main, '.claude', 'hooks', 'probe.js');
83
+ const wt = checkout(path.join(main, '.claude', 'worktrees', 'wt'), { linkTo: main });
84
+ fs.mkdirSync(path.join(wt, 'deep', 'er'), { recursive: true });
85
+ const ranMain = (out) => assert.ok(same(out.file, MAIN_PROBE), `ran ${out.file}`);
86
+
87
+ t('a hook fired from inside a worktree runs the worktree\'s copy, with CLAUDE_PROJECT_DIR pointed at it', () => {
88
+ const out = run({ main, cwd: path.join(wt, 'deep', 'er') });
89
+ assert.ok(same(out.file, path.join(wt, '.claude', 'hooks', 'probe.js')), `ran ${out.file}`);
90
+ assert.ok(same(out.pd, wt), `CLAUDE_PROJECT_DIR was ${out.pd}`);
91
+ });
92
+
93
+ t('a linked worktree outside the main folder reroutes too', () => {
94
+ const linked = checkout(path.join(tmp, 'linked'), { linkTo: main });
95
+ const out = run({ main, cwd: linked });
96
+ assert.ok(same(out.file, path.join(linked, '.claude', 'hooks', 'probe.js')), `ran ${out.file}`);
97
+ });
98
+
99
+ t('a hook fired from the main checkout itself runs unchanged', () => {
100
+ const out = run({ main, cwd: main });
101
+ ranMain(out);
102
+ assert.equal(out.pd, main);
103
+ });
104
+
105
+ t('OTB_HOOKS_FROM_PROJECT_DIR=1 turns the reroute off', () => {
106
+ ranMain(run({ main, cwd: wt, env: { OTB_HOOKS_FROM_PROJECT_DIR: '1' } }));
107
+ });
108
+
109
+ t('a worktree that cannot resolve the core (no src/bongos, no node_modules) keeps the main copy', () => {
110
+ ranMain(run({ main, cwd: checkout(path.join(tmp, 'bare'), { linkTo: main, core: false }) }));
111
+ });
112
+
113
+ t('a worktree whose branch lacks the hook keeps the main copy', () => {
114
+ ranMain(run({ main, cwd: checkout(path.join(tmp, 'old'), { linkTo: main, probe: false }) }));
115
+ });
116
+
117
+ t('a folder outside any checkout keeps the main copy', () => {
118
+ ranMain(run({ main, cwd: fs.mkdtempSync(path.join(os.tmpdir(), 'run-node-loose-')) }));
119
+ });
120
+
121
+ t('a look-alike with a .git dir and its own hooks (not a worktree) keeps the main copy', () => {
122
+ ranMain(run({ main, cwd: checkout(path.join(tmp, 'decoy')) }));
123
+ });
124
+
125
+ t('a decoy whose .git file names a gitdir outside the project keeps the main copy', () => {
126
+ const other = checkout(path.join(tmp, 'other-main'));
127
+ ranMain(run({ main, cwd: checkout(path.join(tmp, 'decoy2'), { linkTo: other }) }));
128
+ });
129
+
130
+ t('a decoy pointing into the project whose back-pointer names another folder keeps the main copy', () => {
131
+ ranMain(run({ main, cwd: checkout(path.join(tmp, 'decoy3'), { linkTo: main, backTo: wt }) }));
132
+ });
133
+
134
+ fs.rmSync(tmp, { recursive: true, force: true });
135
+ console.log(`\n${passed} passed, ${failed} failed`);
136
+ process.exitCode = failed ? 1 : 0;