@bongos/core 1.18.0 → 1.18.18
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 +1740 -640
- package/.claude/skills/builder-box/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +97 -0
- package/.claude/skills/builder-ship/SKILL.md +8 -2
- package/.claude/skills/builder-start/SKILL.md +11 -0
- package/.claude/skills/builder-sync/SKILL.md +41 -0
- package/.claude/skills/fix-task/SKILL.md +64 -0
- package/.claude/skills/goal-create/SKILL.md +158 -0
- package/.claude/skills/goal-review/SKILL.md +3 -1
- package/.claude/skills/idea-triage/SKILL.md +14 -8
- package/.claude/skills/merge-mode/SKILL.md +2 -2
- package/.claude/skills/new-project/SKILL.md +1 -0
- package/.claude/skills/planning-session/SKILL.md +45 -18
- package/.env.local.example +4 -3
- package/README.md +5 -3
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +82 -18
- package/clients/bongos-client/index.cjs +82 -18
- package/clients/bongos-client/index.d.ts +131 -29
- package/clients/bongos-client/index.mjs +82 -18
- package/config/branding.neutral.json +11 -2
- package/config/governance.neutral.json +21 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
- package/docs/adr/0090-metic-task-authoring.md +1 -1
- package/docs/adr/0142-project-context-is-prose-only.md +41 -0
- package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +35 -0
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
- package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
- package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
- package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
- package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
- package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
- package/docs/adr/0152-metic-task-abandonment.md +67 -0
- package/docs/adr/0153-sequential-chain-runner.md +51 -0
- package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
- package/docs/adr/README.md +15 -2
- package/docs/api/openapi.json +2648 -483
- package/docs/api-reference.md +73 -13
- package/docs/architecture.md +4 -2
- package/docs/branding-contract.md +3 -1
- package/docs/canonical-permissions.md +21 -8
- package/docs/file-map.md +15 -7
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
- package/docs/onboarding/diagrams/assertions.json +4 -7
- package/docs/onboarding/drachmae-and-karma.md +3 -3
- package/docs/onboarding/primer.md +17 -16
- package/docs/onboarding/slash-commands.md +124 -0
- package/docs/recipes/builder-box-lifecycle.md +23 -0
- package/docs/recipes/devbox-app-release.md +14 -4
- package/docs/recipes/instance-first-run.md +16 -0
- package/docs/recipes/local-dev.md +81 -0
- package/docs/recipes/ops-gotchas.md +19 -1
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/packaging-the-core.md +13 -0
- package/docs/recipes/parallel-goal-waves.md +174 -0
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +48 -0
- package/docs/recipes/upgrading-the-core.md +5 -2
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
- package/migrations/003_pms.sql +6 -7
- package/migrations/006_pms_v2.sql +3 -31
- package/migrations/039_newcomer_friendly_column.sql +5 -0
- package/migrations/102_remove_box_first_provision_approval.sql +4 -0
- package/migrations/core_192_github_oauth_tokens.sql +33 -0
- package/migrations/core_193_task_needs_rebase.sql +28 -0
- package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
- package/migrations/core_195_idea_triage_provenance.sql +57 -0
- package/migrations/core_196_session_reward_topup.sql +69 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -8
- package/modules/builder-settings/wandering-prefs.js +61 -18
- package/modules/dev-box/app/README.md +73 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
- package/modules/dev-box/app/electron-builder.yml +95 -0
- package/modules/dev-box/app/package-lock.json +5291 -0
- package/modules/dev-box/app/package.json +30 -0
- package/modules/dev-box/app/renderer/app.js +319 -0
- package/modules/dev-box/app/renderer/index.html +45 -0
- package/modules/dev-box/app/renderer/styles.css +246 -0
- package/modules/dev-box/app/renderer/theme.generated.css +16 -0
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
- package/modules/dev-box/app/scripts/gen-brand.js +138 -0
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
- package/modules/dev-box/app/scripts/gen-icons.js +286 -0
- package/modules/dev-box/app/scripts/vendor-client.js +23 -0
- package/modules/dev-box/app/src/box-controller.js +287 -0
- package/modules/dev-box/app/src/brand.generated.json +13 -0
- package/modules/dev-box/app/src/brand.js +16 -0
- package/modules/dev-box/app/src/config.js +19 -0
- package/modules/dev-box/app/src/gds-client.js +90 -0
- package/modules/dev-box/app/src/main.js +610 -0
- package/modules/dev-box/app/src/pairing.js +57 -0
- package/modules/dev-box/app/src/preload.js +29 -0
- package/modules/dev-box/app/src/session-store.js +58 -0
- package/modules/dev-box/app/src/ssh-setup.js +569 -0
- package/modules/dev-box/app/src/task-picker.js +70 -0
- package/modules/dev-box/app/src/tray.js +99 -0
- package/modules/dev-box/app/src/updater.js +55 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
- package/modules/dev-box/app/test/unit.mjs +526 -0
- package/modules/dev-box/box-access.js +121 -4
- package/modules/dev-box/boxes.js +20 -0
- package/modules/dev-box/module.json +1 -1
- package/modules/dev-box/routes/box.js +87 -3
- package/modules/economy/credits.js +116 -25
- package/modules/economy/reward.js +8 -0
- package/modules/economy/streak.js +2 -1
- package/modules/governance/CLAUDE.md +15 -0
- package/modules/governance/catalog.js +419 -0
- package/modules/governance/config.js +174 -0
- package/modules/governance/db.js +125 -0
- package/modules/governance/migrations/governance_001_tables.sql +81 -0
- package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
- package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
- package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
- package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
- package/modules/governance/module.json +10 -0
- package/modules/governance/protected-surfaces.js +88 -0
- package/modules/governance/protected-surfaces.json +80 -0
- package/modules/governance/rank-roles.js +83 -0
- package/modules/governance/resolver.js +221 -0
- package/modules/governance/routes/governance.js +89 -0
- package/modules/governance/routes/role-sync.js +49 -0
- package/modules/governance/session-scopes.js +127 -0
- package/modules/governance/session-scopes.json +38 -0
- package/modules/hall-ui/public/atlas.css +117 -0
- package/modules/hall-ui/public/atlas.html +3 -114
- package/modules/hall-ui/public/brand-apply.js +34 -6
- package/modules/hall-ui/public/builders.js +187 -219
- package/modules/hall-ui/public/diagrams.html +2 -0
- package/modules/hall-ui/public/dom-utils.js +27 -0
- package/modules/hall-ui/public/drachmae.html +2 -0
- package/modules/hall-ui/public/drachmae.js +54 -1
- package/modules/hall-ui/public/gate.css +98 -0
- package/modules/hall-ui/public/gate.html +3 -95
- package/modules/hall-ui/public/goal-inbox.js +4 -1
- package/modules/hall-ui/public/goals-page.js +626 -114
- package/modules/hall-ui/public/goals.css +79 -1
- package/modules/hall-ui/public/goals.html +38 -11
- package/modules/hall-ui/public/hall-widgets.js +62 -52
- package/modules/hall-ui/public/harbor.css +123 -0
- package/modules/hall-ui/public/harbor.html +3 -120
- package/modules/hall-ui/public/index.html +11 -4
- package/modules/hall-ui/public/instances-panel.js +4 -1
- package/modules/hall-ui/public/modules.css +63 -0
- package/modules/hall-ui/public/modules.html +3 -60
- package/modules/hall-ui/public/my-projects.html +78 -0
- package/modules/hall-ui/public/my-projects.js +142 -0
- package/modules/hall-ui/public/new-project-wizard.js +7 -3
- package/modules/hall-ui/public/not-ready.html +2 -0
- package/modules/hall-ui/public/pair.css +73 -0
- package/modules/hall-ui/public/pair.html +3 -70
- package/modules/hall-ui/public/palette.js +302 -0
- package/modules/hall-ui/public/primer.css +13 -0
- package/modules/hall-ui/public/primer.html +2 -0
- package/modules/hall-ui/public/profile.html +2 -0
- package/modules/hall-ui/public/ranks.html +2 -0
- package/modules/hall-ui/public/roadmap.css +179 -0
- package/modules/hall-ui/public/roadmap.html +9 -172
- package/modules/hall-ui/public/roadmap.js +329 -316
- package/modules/hall-ui/public/scouting.html +45 -0
- package/modules/hall-ui/public/scouting.js +73 -0
- package/modules/hall-ui/public/sessions.css +16 -0
- package/modules/hall-ui/public/sessions.html +3 -13
- package/modules/hall-ui/public/settings.html +2 -0
- package/modules/hall-ui/public/settings.js +4 -4
- package/modules/hall-ui/public/shell.js +143 -18
- package/modules/hall-ui/public/style.css +231 -39
- package/modules/hall-ui/public/task-detail.js +347 -26
- package/modules/hall-ui/public/task.html +6 -1
- package/modules/hall-ui/public/watch.html +2 -0
- package/modules/hall-ui/public/work.html +8 -6
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/ideas/inbox.js +61 -11
- package/modules/ideas/routes/inbox.js +23 -0
- package/modules/lifecycle/claim-eligibility.js +72 -0
- package/modules/lifecycle/db.js +519 -139
- package/modules/lifecycle/done-when.js +18 -3
- package/modules/lifecycle/github-push.js +19 -2
- package/modules/lifecycle/goal-advisory.js +114 -0
- package/modules/lifecycle/goal-authz.js +62 -0
- package/modules/lifecycle/lifecycle.js +9 -0
- package/modules/lifecycle/publish-reconciler.js +109 -6
- package/modules/lifecycle/routes/claims.js +68 -23
- package/modules/lifecycle/routes/done-when.js +110 -15
- package/modules/lifecycle/routes/goals.js +8 -38
- package/modules/lifecycle/routes/tasks.js +38 -6
- package/modules/lifecycle/ship-card.js +6 -0
- package/modules/onboarding/onboarding-state.js +1 -1
- package/modules/platform-identity/CLAUDE.md +15 -0
- package/modules/platform-identity/hub-device.js +147 -0
- package/modules/platform-identity/hub-keys.js +151 -0
- package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
- package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
- package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
- package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
- package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
- package/modules/platform-identity/module.json +18 -0
- package/modules/platform-identity/platform-identity.js +531 -0
- package/modules/platform-identity/pollers/code-sweep.js +43 -0
- package/modules/platform-identity/routes/my-projects.js +140 -0
- package/modules/platform-identity/routes/projects.js +107 -0
- package/modules/platform-identity/routes/scouting.js +35 -0
- package/modules/platform-identity/routes/sso.js +402 -0
- package/modules/platform-identity/tests/platform-identity.mjs +388 -0
- package/modules/platform-identity/ui/.gitkeep +0 -0
- package/modules/security/routes/reports.js +10 -0
- package/modules/sessions/db.js +166 -21
- package/modules/sessions/module.json +1 -1
- package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
- package/modules/sessions/routes/sessions.js +33 -0
- package/modules/status-ui/public/explore.html +54 -0
- package/modules/status-ui/public/explore.js +67 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/anim/idle/frame_00.webp +0 -0
- package/public-landing/assets/anim/idle/frame_01.webp +0 -0
- package/public-landing/assets/anim/idle/frame_02.webp +0 -0
- package/public-landing/assets/anim/idle/frame_03.webp +0 -0
- package/public-landing/assets/anim/idle/frame_04.webp +0 -0
- package/public-landing/assets/anim/idle/frame_05.webp +0 -0
- package/public-landing/assets/anim/idle/frame_06.webp +0 -0
- package/public-landing/assets/anim/idle/frame_07.webp +0 -0
- package/public-landing/assets/anim/idle/frame_08.webp +0 -0
- package/public-landing/assets/anim/idle/frame_09.webp +0 -0
- package/public-landing/assets/anim/idle/frame_10.webp +0 -0
- package/public-landing/assets/anim/idle/frame_11.webp +0 -0
- package/public-landing/assets/anim/idle/frame_12.webp +0 -0
- package/public-landing/assets/anim/idle/frame_13.webp +0 -0
- package/public-landing/assets/anim/idle/frame_14.webp +0 -0
- package/public-landing/assets/anim/idle/frame_15.webp +0 -0
- package/public-landing/assets/anim/idle/frame_16.webp +0 -0
- package/public-landing/assets/anim/idle/frame_17.webp +0 -0
- package/public-landing/assets/anim/idle/frame_18.webp +0 -0
- package/public-landing/assets/anim/idle/frame_19.webp +0 -0
- package/public-landing/assets/anim/idle/frame_20.webp +0 -0
- package/public-landing/assets/anim/idle/frame_21.webp +0 -0
- package/public-landing/assets/anim/idle/frame_22.webp +0 -0
- package/public-landing/assets/anim/idle/frame_23.webp +0 -0
- package/public-landing/assets/anim/idle/frame_24.webp +0 -0
- package/public-landing/assets/anim/idle/frame_25.webp +0 -0
- package/public-landing/assets/anim/idle/frame_26.webp +0 -0
- package/public-landing/assets/anim/idle/frame_27.webp +0 -0
- package/public-landing/assets/anim/idle/frame_28.webp +0 -0
- package/public-landing/assets/anim/idle/frame_29.webp +0 -0
- package/public-landing/assets/anim/idle/frame_30.webp +0 -0
- package/public-landing/assets/anim/idle/frame_31.webp +0 -0
- package/public-landing/assets/anim/idle/frame_32.webp +0 -0
- package/public-landing/assets/anim/idle/frame_33.webp +0 -0
- package/public-landing/assets/anim/idle/frame_34.webp +0 -0
- package/public-landing/assets/anim/idle/frame_35.webp +0 -0
- package/public-landing/assets/anim/idle/frame_36.webp +0 -0
- package/public-landing/assets/anim/idle/frame_37.webp +0 -0
- package/public-landing/assets/anim/idle/frame_38.webp +0 -0
- package/public-landing/assets/anim/idle/frame_39.webp +0 -0
- package/public-landing/assets/anim/idle/frame_40.webp +0 -0
- package/public-landing/assets/anim/idle/frame_41.webp +0 -0
- package/public-landing/assets/anim/idle/frame_42.webp +0 -0
- package/public-landing/assets/anim/idle/frame_43.webp +0 -0
- package/public-landing/create.html +252 -62
- package/public-landing/gate/README.md +137 -0
- package/public-landing/gate/dispmaps/bar.png +0 -0
- package/public-landing/gate/dispmaps/flat.png +0 -0
- package/public-landing/gate/dispmaps/key.png +0 -0
- package/public-landing/gate/index.html +587 -0
- package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
- package/public-landing/gate/previews/off-the-boats.jpg +0 -0
- package/public-landing/gate/space.css +799 -0
- package/public-landing/gate/space.js +1826 -0
- package/public-landing/gate/sprites/enter.png +0 -0
- package/public-landing/gate/sprites/search.png +0 -0
- package/public-landing/gate/sprites/terminal-blank.png +0 -0
- package/public-landing/gate/sprites/tile.png +0 -0
- package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
- package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
- package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
- package/public-landing/world/assets/plates/scene-establishing.png +0 -0
- package/scripts/gds/abandon.js +6 -4
- package/scripts/gds/api.js +10 -5
- package/scripts/gds/api.ps1 +26 -0
- package/scripts/gds/api.sh +6 -0
- package/scripts/gds/audit-ships.js +7 -1
- package/scripts/gds/backfill-sessions.js +78 -18
- package/scripts/gds/backup.js +23 -1
- package/scripts/gds/box-connect-lib.js +124 -31
- package/scripts/gds/box-sync.js +66 -0
- package/scripts/gds/box.js +225 -53
- package/scripts/gds/cf-tunnel.js +145 -6
- package/scripts/gds/claim.js +34 -3
- package/scripts/gds/cli-lib.js +161 -21
- package/scripts/gds/code.js +6 -2
- package/scripts/gds/deploy-divergence.js +209 -0
- package/scripts/gds/dev-lib.js +309 -0
- package/scripts/gds/dev.js +322 -0
- package/scripts/gds/doctor.js +90 -61
- package/scripts/gds/fetch-art-key.js +1 -1
- package/scripts/gds/fetch-sound-prefs.js +2 -2
- package/scripts/gds/fitness.js +228 -12
- package/scripts/gds/gds-db-backup.service +16 -1
- package/scripts/gds/gds-db-offsite.service +9 -1
- package/scripts/gds/gen-api-client.js +1 -1
- package/scripts/gds/gen-api-docs.js +36 -14
- package/scripts/gds/gen-diagrams.js +45 -28
- package/scripts/gds/gen-file-map.js +2 -1
- package/scripts/gds/gen-freshness.js +32 -0
- package/scripts/gds/gen-repo-map.js +3 -2
- package/scripts/gds/gen-session-index.js +14 -5
- package/scripts/gds/hooks/pre-push.js +2 -2
- package/scripts/gds/init.js +60 -3
- package/scripts/gds/install-git-hooks.js +1 -1
- package/scripts/gds/key-card.js +15 -6
- package/scripts/gds/key-status.js +1 -1
- package/scripts/gds/linkify-refs.js +35 -9
- package/scripts/gds/lint-migrations.js +102 -0
- package/scripts/gds/login.js +58 -11
- package/scripts/gds/mirror-redact.js +5 -0
- package/scripts/gds/package-core.js +263 -4
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +268 -19
- package/scripts/gds/publish-manifest.js +22 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +42 -16
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
- package/scripts/gds/sequence.js +425 -0
- package/scripts/gds/session-cost.js +10 -1
- package/scripts/gds/setup.js +72 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +433 -59
- package/scripts/gds/smoke-gds.js +139 -8
- package/scripts/gds/start.js +171 -22
- package/scripts/gds/status.js +1 -1
- package/scripts/gds/triage.js +10 -9
- package/scripts/gds/upgrade.js +271 -7
- package/scripts/migrate.sh +10 -0
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +809 -107
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +105 -0
- package/src/bongos/github-repos.js +93 -0
- package/src/bongos/module-scope-map.js +27 -1
- package/src/bongos/permission-path-check.js +136 -102
- package/src/bongos/repo-info.js +47 -11
- package/src/bongos/route-rank-check.js +429 -46
- package/src/bongos/routes/_helpers.js +12 -1
- package/src/bongos/routes/auth.js +312 -13
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/github.js +90 -0
- package/src/bongos/routes/instance.js +15 -3
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/routes.js +2 -0
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +49 -2
- package/src/instance-config.js +46 -0
- package/src/module-api.js +140 -1
- package/src/platform-server.js +86 -9
- package/tests/abandon_task.mjs +59 -3
- package/tests/api_cli_flags.mjs +53 -0
- package/tests/auth_page_gate.mjs +13 -0
- package/tests/backfill_sessions.mjs +127 -1
- package/tests/backup_cli.mjs +54 -0
- package/tests/bongos_box_connect.mjs +77 -0
- package/tests/box_access.mjs +130 -1
- package/tests/box_credential.mjs +125 -0
- package/tests/box_host_keys.mjs +24 -0
- package/tests/box_infra_resolve.mjs +149 -0
- package/tests/box_scope_session.mjs +6 -2
- package/tests/box_sync.mjs +55 -0
- package/tests/box_task_scope.mjs +99 -0
- package/tests/branding.mjs +41 -23
- package/tests/cf_ruleset_preflight.mjs +142 -0
- package/tests/cf_tunnel.mjs +37 -0
- package/tests/claim_gate_rebase.mjs +98 -0
- package/tests/clawback_confirmed_backlog.mjs +103 -0
- package/tests/cli_lib_formatters.mjs +20 -0
- package/tests/confirm_attribution.mjs +30 -46
- package/tests/credit_grant.mjs +198 -10
- package/tests/criterion_authoring_gate.mjs +272 -0
- package/tests/criterion_progress.mjs +33 -1
- package/tests/dev_lib.mjs +253 -0
- package/tests/devbox_artifact_gate.mjs +75 -0
- package/tests/devbox_dmg_backdrop.mjs +137 -0
- package/tests/devbox_icons.mjs +304 -0
- package/tests/devbox_installer_brand.mjs +232 -0
- package/tests/devbox_key_register.mjs +234 -0
- package/tests/docs_openapi_route.mjs +110 -0
- package/tests/doctor_hooks.mjs +141 -0
- package/tests/extract_json.mjs +140 -0
- package/tests/fitness.mjs +89 -0
- package/tests/gen_freshness.mjs +96 -0
- package/tests/github_repos_helpers.mjs +91 -0
- package/tests/github_token_store.mjs +121 -0
- package/tests/goal_advisory.mjs +251 -0
- package/tests/goal_criterion_confirm_ui.mjs +269 -0
- package/tests/goal_routes.mjs +59 -4
- package/tests/goal_tier.mjs +12 -3
- package/tests/governance_catalog.mjs +129 -0
- package/tests/governance_claim_eligibility.mjs +262 -0
- package/tests/governance_config.mjs +187 -0
- package/tests/governance_ownership_scope.mjs +410 -0
- package/tests/governance_page_gates.mjs +324 -0
- package/tests/governance_parity.mjs +336 -0
- package/tests/governance_principals.mjs +232 -0
- package/tests/governance_protected_surfaces.mjs +208 -0
- package/tests/governance_require_permission.mjs +223 -0
- package/tests/governance_resolver.mjs +84 -0
- package/tests/governance_role_sync.mjs +231 -0
- package/tests/governance_seed.mjs +127 -0
- package/tests/governance_session_scope.mjs +434 -0
- package/tests/governance_vanilla_boot.mjs +203 -0
- package/tests/grade_attribution.mjs +7 -7
- package/tests/hall_audit.mjs +223 -0
- package/tests/hall_nav.mjs +191 -0
- package/tests/hall_palette.mjs +206 -0
- package/tests/hall_signout.mjs +124 -0
- package/tests/hook_user_agents.mjs +10 -0
- package/tests/idea_triage.mjs +162 -0
- package/tests/idp_device_exchange.mjs +198 -0
- package/tests/idp_federation.mjs +100 -0
- package/tests/init.mjs +51 -0
- package/tests/instance_config.mjs +65 -0
- package/tests/instance_mismatch_guard.mjs +23 -6
- package/tests/instance_release_config.mjs +104 -0
- package/tests/linkify_refs.mjs +15 -2
- package/tests/migrations_forward_safe.mjs +86 -0
- package/tests/module_api.mjs +1 -1
- package/tests/module_contributions.mjs +55 -0
- package/tests/module_loader.mjs +10 -5
- package/tests/module_scope_active_claims.mjs +94 -0
- package/tests/needs_rebase.mjs +92 -0
- package/tests/one_active_claim_per_session.mjs +76 -0
- package/tests/override_attribution.mjs +10 -8
- package/tests/override_request_ownership.mjs +8 -4
- package/tests/package_core.mjs +246 -1
- package/tests/parse_cookie_decode.mjs +42 -0
- package/tests/pay_on_land.mjs +129 -0
- package/tests/platform_boot.mjs +100 -1
- package/tests/post_login_landing.mjs +42 -0
- package/tests/preflight_checks.mjs +72 -0
- package/tests/provision.mjs +289 -7
- package/tests/pruned_lockfile_integrity.mjs +79 -0
- package/tests/publish_branch_route.mjs +53 -0
- package/tests/publish_reconciler.mjs +187 -2
- package/tests/repo_info_binding.mjs +248 -0
- package/tests/reward_gate.mjs +11 -2
- package/tests/reward_mode_cost_plus_only.mjs +116 -0
- package/tests/reward_mode_surfacing.mjs +168 -0
- package/tests/route_permission_check.mjs +301 -0
- package/tests/route_rank_check.mjs +254 -12
- package/tests/route_validation.mjs +30 -0
- package/tests/sequence.mjs +267 -0
- package/tests/session_digest.mjs +44 -0
- package/tests/session_index.mjs +18 -6
- package/tests/session_records.mjs +39 -1
- package/tests/session_reward_on_land.mjs +381 -0
- package/tests/session_reward_reconcile.mjs +229 -0
- package/tests/ship_api_client_pathspec.mjs +100 -0
- package/tests/ship_base_flag.mjs +93 -0
- package/tests/ship_ci_deploy.mjs +140 -3
- package/tests/ship_upstream_deletion.mjs +57 -0
- package/tests/smoke_target.mjs +233 -0
- package/tests/start_env_guard.mjs +87 -0
- package/tests/start_rebase_warning.mjs +103 -0
- package/tests/status_mirror_sync.mjs +122 -0
- package/tests/stop_refcheck.mjs +19 -1
- package/tests/task_goal_link.mjs +15 -6
- package/tests/upgrade.mjs +393 -5
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
- package/.gitignore +0 -91
- package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +0 -94
- package/docs/adr/0140-cloudbongos-standalone-reprovision.md +0 -43
- package/scripts/hall-preview/fixtures/access-requests.json +0 -20
- package/scripts/hall-preview/fixtures/achievements.json +0 -76
- package/scripts/hall-preview/fixtures/analytics__builder.json +0 -251
- package/scripts/hall-preview/fixtures/audit-log.json +0 -68
- package/scripts/hall-preview/fixtures/blockers__detail.json +0 -12
- package/scripts/hall-preview/fixtures/box__me.json +0 -5
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +0 -69
- package/scripts/hall-preview/fixtures/builders__active-claims.json +0 -1
- package/scripts/hall-preview/fixtures/builders__directory.json +0 -81
- package/scripts/hall-preview/fixtures/builders__roster.json +0 -48
- package/scripts/hall-preview/fixtures/goals.json +0 -502
- package/scripts/hall-preview/fixtures/goals__25.json +0 -96
- package/scripts/hall-preview/fixtures/goals__26.json +0 -67
- package/scripts/hall-preview/fixtures/goals__3.json +0 -103
- package/scripts/hall-preview/fixtures/goals__35.json +0 -49
- package/scripts/hall-preview/fixtures/goals__conflicts.json +0 -154
- package/scripts/hall-preview/fixtures/goals__detail.json +0 -96
- package/scripts/hall-preview/fixtures/goals__inbox.json +0 -5
- package/scripts/hall-preview/fixtures/goals__requests.json +0 -3
- package/scripts/hall-preview/fixtures/grades__by-builder.json +0 -32
- package/scripts/hall-preview/fixtures/inbox.json +0 -330
- package/scripts/hall-preview/fixtures/inbox__detail.json +0 -15
- package/scripts/hall-preview/fixtures/instance.json +0 -124
- package/scripts/hall-preview/fixtures/leaderboard.json +0 -184
- package/scripts/hall-preview/fixtures/me.json +0 -476
- package/scripts/hall-preview/fixtures/me__art-key__own.json +0 -6
- package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +0 -209
- package/scripts/hall-preview/fixtures/me__recent-ships.json +0 -304
- package/scripts/hall-preview/fixtures/me__render.json +0 -5
- package/scripts/hall-preview/fixtures/me__skill-prefs.json +0 -143
- package/scripts/hall-preview/fixtures/me__sound-prefs.json +0 -67
- package/scripts/hall-preview/fixtures/me__wandering.json +0 -43
- package/scripts/hall-preview/fixtures/override-requests.json +0 -15
- package/scripts/hall-preview/fixtures/provisioning__fleet.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__instances.json +0 -1
- package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +0 -516
- package/scripts/hall-preview/fixtures/public__bounty-table.json +0 -37
- package/scripts/hall-preview/fixtures/public__cost-summary.json +0 -742
- package/scripts/hall-preview/fixtures/public__estimation-drift.json +0 -41
- package/scripts/hall-preview/fixtures/public__grades.json +0 -50
- package/scripts/hall-preview/fixtures/public__progress.json +0 -124
- package/scripts/hall-preview/fixtures/public__recent-shipped.json +0 -426
- package/scripts/hall-preview/fixtures/public__repo-health.json +0 -160
- package/scripts/hall-preview/fixtures/public__repo-info.json +0 -5
- package/scripts/hall-preview/fixtures/security__docs.json +0 -10
- package/scripts/hall-preview/fixtures/security__reports.json +0 -31
- package/scripts/hall-preview/fixtures/sessions__search.json +0 -9
- package/scripts/hall-preview/fixtures/tasks.json +0 -2420
- package/scripts/hall-preview/fixtures/tasks__claimable.json +0 -2555
- package/scripts/hall-preview/fixtures/tasks__detail.json +0 -111
- package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +0 -330
- package/scripts/hall-preview/fixtures/versions.json +0 -89
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/gds/preflight.js — the onboarding ENVIRONMENT PREFLIGHT (task 1002401,
|
|
3
|
+
// onboarding-audit area 4).
|
|
4
|
+
//
|
|
5
|
+
// One shared, PURE check of the three things that used to bite a brand-new
|
|
6
|
+
// builder one reactive crash at a time:
|
|
7
|
+
// 1. Node >= 22 — the very first command (`bongos start`) shells to node;
|
|
8
|
+
// on a bare machine it died with `node: command not found`.
|
|
9
|
+
// 2. deps installed — a missing node_modules is why a ship later blew up with
|
|
10
|
+
// `Cannot find module 'pg'`.
|
|
11
|
+
// 3. `claude` launchable — the grader spawns it per worker; without it every ship
|
|
12
|
+
// failed grading with SUBAGENT_SPAWN_FAILED.
|
|
13
|
+
// Each finding carries a copy-paste fix.
|
|
14
|
+
//
|
|
15
|
+
// Before this, those checks lived only inside `bongos doctor` (doctor.js) and were
|
|
16
|
+
// never run at setup/start time — so newcomers hit the failures reactively, deep
|
|
17
|
+
// in the session. This module is the front-loadable, testable source of truth:
|
|
18
|
+
// - `setup.js` runs it at the top of `/builder-setup` (catch a bare machine up front)
|
|
19
|
+
// - `doctor.js` `checkToolchain()` delegates to it (one source, no drift)
|
|
20
|
+
// - `node scripts/gds/preflight.js` runs it standalone (a quick `bongos`-less check)
|
|
21
|
+
//
|
|
22
|
+
// Pure by construction: every OS touch (node version, fs, child_process) is an
|
|
23
|
+
// injectable dep, so the unit test drives it with zero real I/O.
|
|
24
|
+
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
const nodeFs = require('node:fs');
|
|
28
|
+
const nodePath = require('node:path');
|
|
29
|
+
const { spawnSync } = require('node:child_process');
|
|
30
|
+
|
|
31
|
+
const REPO_ROOT = nodePath.resolve(__dirname, '..', '..');
|
|
32
|
+
const MIN_NODE_MAJOR = 22;
|
|
33
|
+
|
|
34
|
+
// The node_modules-present check, factored out (task 1002403) so a HOT-PATH
|
|
35
|
+
// caller — /builder-start runs on every session open — can reuse THIS exact
|
|
36
|
+
// finding without paying for the `claude --version` spawn the full
|
|
37
|
+
// preflightChecks() does. Pure: injectable fs + repoRoot.
|
|
38
|
+
function depsFinding({ repoRoot = REPO_ROOT, fs = nodeFs } = {}) {
|
|
39
|
+
let hasDeps = false;
|
|
40
|
+
try { hasDeps = fs.existsSync(nodePath.join(repoRoot, 'node_modules')); } catch (_) { /* treat as missing */ }
|
|
41
|
+
if (hasDeps) {
|
|
42
|
+
return { level: 'ok', title: 'Dependencies', detail: 'node_modules present.' };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
level: 'warn',
|
|
46
|
+
title: 'Dependencies',
|
|
47
|
+
detail: 'node_modules is missing — scripts that need pg/express (a ship, the server) will fail until installed.',
|
|
48
|
+
fix: 'Run `npm ci` in the repo root.',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// preflightChecks(deps) → [{ level:'ok'|'warn'|'fail', title, detail, fix? }]
|
|
53
|
+
// No printing, no process.exit — callers (setup, doctor, the test) format it.
|
|
54
|
+
function preflightChecks({
|
|
55
|
+
repoRoot = REPO_ROOT,
|
|
56
|
+
nodeVersion = process.version,
|
|
57
|
+
env = process.env,
|
|
58
|
+
fs = nodeFs,
|
|
59
|
+
spawn = spawnSync,
|
|
60
|
+
platform = process.platform,
|
|
61
|
+
} = {}) {
|
|
62
|
+
const out = [];
|
|
63
|
+
|
|
64
|
+
// 1. Node >= 22.
|
|
65
|
+
const major = Number(String(nodeVersion).replace(/^v/, '').split('.')[0]) || 0;
|
|
66
|
+
if (major >= MIN_NODE_MAJOR) {
|
|
67
|
+
out.push({ level: 'ok', title: 'Node', detail: `${nodeVersion} (>= ${MIN_NODE_MAJOR}).` });
|
|
68
|
+
} else {
|
|
69
|
+
out.push({
|
|
70
|
+
level: 'fail',
|
|
71
|
+
title: 'Node',
|
|
72
|
+
detail: `${nodeVersion} is below the required v${MIN_NODE_MAJOR}.`,
|
|
73
|
+
fix: 'Install Node >= 22 — e.g. `brew install node`, `nvm install 22`, or https://nodejs.org.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 2. Dependencies installed (node_modules present) — shared with the hot-path
|
|
78
|
+
// /builder-start caller via depsFinding() (task 1002403).
|
|
79
|
+
out.push(depsFinding({ repoRoot, fs }));
|
|
80
|
+
|
|
81
|
+
// 3. `claude` CLI launchable — same resolution order the grader uses.
|
|
82
|
+
const bin = env.OTB_CLAUDE_BIN || env.CLAUDE_BIN || 'claude';
|
|
83
|
+
const via = env.OTB_CLAUDE_BIN ? ' (via OTB_CLAUDE_BIN)' : env.CLAUDE_BIN ? ' (via CLAUDE_BIN)' : '';
|
|
84
|
+
let r;
|
|
85
|
+
try {
|
|
86
|
+
r = spawn(bin, ['--version'], { encoding: 'utf8', shell: platform === 'win32', windowsHide: true, timeout: 15000 });
|
|
87
|
+
} catch (err) { r = { error: err }; }
|
|
88
|
+
if (r && !r.error && r.status === 0) {
|
|
89
|
+
const ver = String(r.stdout || '').trim().split('\n')[0] || 'present';
|
|
90
|
+
out.push({ level: 'ok', title: 'claude CLI', detail: `launches${via}: ${ver} — the grader can spawn its workers.` });
|
|
91
|
+
} else {
|
|
92
|
+
const why = r && r.error ? (r.error.code || r.error.message) : `exited ${r ? r.status : '?'}`;
|
|
93
|
+
out.push({
|
|
94
|
+
level: 'fail',
|
|
95
|
+
title: 'claude CLI',
|
|
96
|
+
detail: `not launchable (${bin}: ${why}) — the grader spawns it per worker, so ships fail grading with SUBAGENT_SPAWN_FAILED.`,
|
|
97
|
+
fix: 'Run `npm i -g @anthropic-ai/claude-code` (puts a real claude on PATH), or set OTB_CLAUDE_BIN to its full path.',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const GLYPH = { ok: '✓', warn: '!', fail: '✖' };
|
|
105
|
+
const ANSI = { ok: '\x1b[32m', warn: '\x1b[33m', fail: '\x1b[31m', dim: '\x1b[2m', reset: '\x1b[0m' };
|
|
106
|
+
|
|
107
|
+
// formatPreflight(results, opts) → a printable multi-line string.
|
|
108
|
+
function formatPreflight(results, { color = false, heading = false } = {}) {
|
|
109
|
+
const c = (k, s) => (color ? `${ANSI[k]}${s}${ANSI.reset}` : s);
|
|
110
|
+
const lines = [];
|
|
111
|
+
if (heading) lines.push('Checking your environment…');
|
|
112
|
+
for (const r of results) {
|
|
113
|
+
lines.push(` ${c(r.level, GLYPH[r.level])} ${r.title}: ${r.detail}`);
|
|
114
|
+
if (r.fix) lines.push(` ${c('dim', '→')} ${r.fix}`);
|
|
115
|
+
}
|
|
116
|
+
return lines.join('\n');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { preflightChecks, depsFinding, formatPreflight, REPO_ROOT, MIN_NODE_MAJOR };
|
|
120
|
+
|
|
121
|
+
// Standalone: `node scripts/gds/preflight.js` — exit 1 if any hard fail.
|
|
122
|
+
if (require.main === module) {
|
|
123
|
+
const results = preflightChecks();
|
|
124
|
+
console.log(formatPreflight(results, { color: process.stdout.isTTY, heading: true }));
|
|
125
|
+
process.exit(results.some((r) => r.level === 'fail') ? 1 : 0);
|
|
126
|
+
}
|
package/scripts/gds/provision.js
CHANGED
|
@@ -47,6 +47,7 @@ const fs = require('node:fs');
|
|
|
47
47
|
const os = require('node:os');
|
|
48
48
|
const path = require('node:path');
|
|
49
49
|
const cp = require('node:child_process');
|
|
50
|
+
const crypto = require('node:crypto');
|
|
50
51
|
|
|
51
52
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
52
53
|
|
|
@@ -180,7 +181,23 @@ function loadDeps() {
|
|
|
180
181
|
const provisioning = require(path.join(REPO_ROOT, 'modules/provisioning/provisioning'));
|
|
181
182
|
// eslint-disable-next-line global-require
|
|
182
183
|
const { DoApi } = require(path.join(REPO_ROOT, 'scripts/gds/do-api'));
|
|
183
|
-
|
|
184
|
+
// Cross-site identity (ADR 0141 §4, task #1002109). Resolve the hub-side module by a
|
|
185
|
+
// VARIABLE key, gated on isModuleEnabled — the module-loader's own dynamic-dispatch
|
|
186
|
+
// pattern (the run-unit-tests.js precedent), NOT a static 'modules/platform-identity/'
|
|
187
|
+
// literal that would couple this publishable core file to an instance-EXCLUDED module
|
|
188
|
+
// (fitness Check 16b / task 2135). A vanilla core that neither ships nor enables the
|
|
189
|
+
// module never resolves it → no MODULE_NOT_FOUND on a fresh boot. Null here → federation
|
|
190
|
+
// dormant (shouldFederate skips) → the instance owns its auth, byte-identical to today.
|
|
191
|
+
let platformIdentity = null, hubKeys = null;
|
|
192
|
+
const PI_KEY = 'platform-identity';
|
|
193
|
+
let piEnabled = false;
|
|
194
|
+
try { piEnabled = require(path.join(REPO_ROOT, 'src/modules')).isModuleEnabled(PI_KEY); } catch { piEnabled = false; }
|
|
195
|
+
if (piEnabled) {
|
|
196
|
+
const piBase = path.join(REPO_ROOT, 'modules', PI_KEY);
|
|
197
|
+
try { platformIdentity = require(path.join(piBase, 'platform-identity')); hubKeys = require(path.join(piBase, 'hub-keys')); }
|
|
198
|
+
catch { platformIdentity = null; hubKeys = null; }
|
|
199
|
+
}
|
|
200
|
+
_deps = { pool, provisioning, DoApi, platformIdentity, hubKeys };
|
|
184
201
|
return _deps;
|
|
185
202
|
}
|
|
186
203
|
|
|
@@ -279,6 +296,14 @@ function standaloneMigrateCmd(inst, { privileged = false } = {}) {
|
|
|
279
296
|
? `sudo -u postgres env PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`
|
|
280
297
|
: `PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`;
|
|
281
298
|
}
|
|
299
|
+
// Regenerate the instance's whole-file navigation docs (session-log index) from the pinned core,
|
|
300
|
+
// AFTER the pull+install+migrate — the standalone analogue of the monorepo's deploy-prod.sh regen
|
|
301
|
+
// block (ADR 0110 §2, task 1002314). Best-effort + LOUD (the script always exits 0 + prints
|
|
302
|
+
// ::ERROR:: on a generator failure) so a stale/absent nav doc never fails a deploy. Run with
|
|
303
|
+
// cwd = standaloneRoot so resolveInstanceRoot() writes into the instance repo. Without this a
|
|
304
|
+
// standalone instance's docs never refresh on deploy — the gap the owner hit ("docs didn't
|
|
305
|
+
// transfer from primea"): its bespoke pull-deploy regenerated nothing.
|
|
306
|
+
function standaloneRegenDocsCmd() { return `node ${CORE_PKG_DIR}/scripts/gds/regen-instance-docs.js`; }
|
|
282
307
|
|
|
283
308
|
// ── STANDALONE scaffold (ADR 0108 / 0125, task 2055) ──────────────────────────
|
|
284
309
|
// The MISSING first leg of the greenfield "one guided flow": provision.js used to
|
|
@@ -325,7 +350,54 @@ function instanceInitSpec(inst, { ownerLogin } = {}) {
|
|
|
325
350
|
// dist/ + secrets OUT of the commit, while the vendored core tarball (vendor/*.tgz)
|
|
326
351
|
// and package-lock.json stay IN — `npm ci` on deploy resolves the pinned core from
|
|
327
352
|
// the committed tarball (ADR 0108), so they MUST be tracked.
|
|
328
|
-
|
|
353
|
+
//
|
|
354
|
+
// docs/session-log-index.md is a WHOLE-FILE generated navigation doc (ADR 0110): every byte is
|
|
355
|
+
// machine-owned, regenerated at deploy by regen-instance-docs.js (task 1002314) into the served
|
|
356
|
+
// tree. Untracked so `git reset --hard origin/main` never wipes it and no branch can diff it —
|
|
357
|
+
// the same reasoning the core's own .gitignore applies to it. (docs/repo-map.md is listed for
|
|
358
|
+
// parity/forward-compat; it is core-scoped today so a standalone instance doesn't generate its
|
|
359
|
+
// own — see regenerateNavDocs() in upgrade.js — but ignoring it costs nothing and prevents a
|
|
360
|
+
// stray future write from entering the tree.)
|
|
361
|
+
const INSTANCE_GITIGNORE = [
|
|
362
|
+
'node_modules/', 'dist/', '.env', '.env.local', '*.log',
|
|
363
|
+
'# Whole-file generated navigation docs — regenerated at deploy (ADR 0110, task 1002314).',
|
|
364
|
+
'docs/session-log-index.md', 'docs/repo-map.md', '',
|
|
365
|
+
].join('\n');
|
|
366
|
+
|
|
367
|
+
// A PRE-BUILT @bongos/core tarball this control plane can vendor into a NEW instance
|
|
368
|
+
// WITHOUT rebuilding from source — the consumer-control-plane fix (task 1002275).
|
|
369
|
+
//
|
|
370
|
+
// The scaffold's default core-install path rebuilds the tarball with package-core.js,
|
|
371
|
+
// which enumerates GIT-TRACKED files (`git -C REPO_ROOT ls-tree`). That works from a core
|
|
372
|
+
// SOURCE checkout, but NOT when this runner is ITSELF a consumer instance whose core is an
|
|
373
|
+
// installed npm dependency: provision.js then runs out of node_modules/@bongos/core, so
|
|
374
|
+
// REPO_ROOT points INSIDE gitignored node_modules, git sees ZERO tracked files there, and
|
|
375
|
+
// package-core aborts ("the core set is EMPTY") — the scaffold never pushes and the freshly
|
|
376
|
+
// created repo is left EMPTY (seen live: trial/test on cloudbongos.com).
|
|
377
|
+
//
|
|
378
|
+
// But the exact pinned tarball this instance installed FROM is on disk, named by its own
|
|
379
|
+
// package.json ("@bongos/core": "file:vendor/bongos-core-<v>.tgz"). Reuse it: byte-identical,
|
|
380
|
+
// already proven, and the child pins the SAME core the hub runs. Returns an absolute .tgz
|
|
381
|
+
// path, or null on a source checkout (no `file:` core pin) — where package-core.js is the
|
|
382
|
+
// right, unchanged path. `instanceRoot` defaults to $BONGOS_INSTANCE_ROOT (what
|
|
383
|
+
// infra/core-node.sh resolves against) else process.cwd() (the runner's WorkingDirectory).
|
|
384
|
+
// fs injectable for tests. PURE-ish (fs reads only, no mutation → dry-run safe).
|
|
385
|
+
function installedCoreTarball({ instanceRoot, fsImpl = fs } = {}) {
|
|
386
|
+
const root = instanceRoot || process.env.BONGOS_INSTANCE_ROOT || process.cwd();
|
|
387
|
+
let pkg;
|
|
388
|
+
try { pkg = JSON.parse(fsImpl.readFileSync(path.join(root, 'package.json'), 'utf8')); }
|
|
389
|
+
catch { return null; } // no/unreadable package.json → treat as a source checkout
|
|
390
|
+
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
|
|
391
|
+
// Both scope aliases the loader accepts (infra/core-node.sh checks the same two).
|
|
392
|
+
for (const name of ['@bongos/core', '@cloudbongos/core']) {
|
|
393
|
+
const m = /^file:(.+\.tgz)$/.exec(String(deps[name] || '').trim());
|
|
394
|
+
if (m) {
|
|
395
|
+
const abs = path.resolve(root, m[1]);
|
|
396
|
+
if (fsImpl.existsSync(abs)) return abs; // a dangling pin (tarball pruned) → fall through to null
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
329
401
|
|
|
330
402
|
// Clone the (empty) target_ref → standaloneRoot → materialize config/ + .claude/ + a
|
|
331
403
|
// file:-pinned @bongos/core + lockfile (via `bongos init --vendor-core`, task 2053)
|
|
@@ -366,11 +438,19 @@ async function scaffoldStandaloneRepo(inst, deps) {
|
|
|
366
438
|
} catch { /* fall back to the repo owner */ }
|
|
367
439
|
}
|
|
368
440
|
|
|
369
|
-
// 1.
|
|
370
|
-
//
|
|
441
|
+
// 1. Materialize the working tree + set the initial branch to `main`. A freshly-created
|
|
442
|
+
// instance repo is EMPTY (github-repos.createUserRepo, auto_init:false), so there is
|
|
443
|
+
// NOTHING to clone — and a git DEPLOY KEY is per-repo, so the shared PROVISION_GIT_SSH_ALIAS
|
|
444
|
+
// key (e.g. `github-instances`, a deploy key scoped to ONE repo) can't even READ a
|
|
445
|
+
// consumer's brand-new repo under their own account: `git clone` 500s with "Repository not
|
|
446
|
+
// found." This is the clone-side twin of the task-1002317 push bug. So `git init` locally
|
|
447
|
+
// instead of cloning, and set `origin` to the SSH remote for the no-token fallback push
|
|
448
|
+
// below; the OAuth-preferred push (its token CREATED the repo, so it can write) handles the
|
|
449
|
+
// upload for consumer repos. A re-run after a partial init reuses the existing dir.
|
|
371
450
|
if (!fs.existsSync(path.join(root, '.git'))) {
|
|
372
|
-
exec(`mkdir -p ${
|
|
373
|
-
exec(`git
|
|
451
|
+
exec(`mkdir -p ${root}`);
|
|
452
|
+
exec(`git -C ${root} init -q`);
|
|
453
|
+
exec(`git -C ${root} remote add origin ${remote}`);
|
|
374
454
|
}
|
|
375
455
|
exec(`git -C ${root} checkout -B main`);
|
|
376
456
|
|
|
@@ -400,9 +480,19 @@ async function scaffoldStandaloneRepo(inst, deps) {
|
|
|
400
480
|
log(` [scaffold] core install: registry — PRIVATE @bongos/core@${coreV} via env-fed NPM_TOKEN (ADR 0134)`);
|
|
401
481
|
exec(`node scripts/gds/init.js --from ${specPath} --dir ${root} --no-seed --force --registry-core`);
|
|
402
482
|
} else {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
483
|
+
// Vendored tarball. PREFER a pre-built pinned tarball this control plane already has
|
|
484
|
+
// (a consumer instance's own `file:` core pin) — rebuilding via package-core.js aborts
|
|
485
|
+
// with an EMPTY core set when this runner IS a consumer instance (REPO_ROOT is inside
|
|
486
|
+
// gitignored node_modules), which left new repos empty (task 1002275). Only rebuild
|
|
487
|
+
// from source when there is no such pin (a genuine core source checkout).
|
|
488
|
+
let tgz = installedCoreTarball({ instanceRoot: deps.instanceRoot });
|
|
489
|
+
if (tgz) {
|
|
490
|
+
log(` [scaffold] core install: reusing the pinned core tarball this control plane runs (${path.basename(tgz)}) — no source rebuild (task 1002275)`);
|
|
491
|
+
} else {
|
|
492
|
+
log(` [scaffold] core install: building a fresh vendored tarball from source (package-core.js)`);
|
|
493
|
+
exec(`node scripts/gds/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
|
|
494
|
+
tgz = path.join(REPO_ROOT, 'dist', `bongos-core-${coreV}.tgz`);
|
|
495
|
+
}
|
|
406
496
|
exec(`node scripts/gds/init.js --from ${specPath} --dir ${root} --no-seed --force --vendor-core ${tgz}`);
|
|
407
497
|
}
|
|
408
498
|
|
|
@@ -412,8 +502,38 @@ async function scaffoldStandaloneRepo(inst, deps) {
|
|
|
412
502
|
exec(`git -C ${root} config user.email "bot@cloudbongos.com"`);
|
|
413
503
|
exec(`git -C ${root} add -A`);
|
|
414
504
|
exec(`git -C ${root} commit -m "chore: scaffold ${inst.slug} on a pinned @bongos/core ${coreV} (provision.js, ADR 0108)"`, { allowFail: true });
|
|
415
|
-
|
|
416
|
-
|
|
505
|
+
// Push. A GitHub DEPLOY KEY is per-repo, so the box's shared key can't write to a NEW
|
|
506
|
+
// project repo — the second empty-repo cause (task 1002317). PREFER the instance owner's
|
|
507
|
+
// OAuth token (it CREATED the repo via github-repos.createUserRepo, so `public_repo`
|
|
508
|
+
// already grants write). The token rides a chmod-600 git credential-store file — never
|
|
509
|
+
// the logged command or makeExec's env (both are surfaced) — and is removed right after
|
|
510
|
+
// (finally, so a failed push can't leave it on disk). No token (org-owned repo, or
|
|
511
|
+
// secret-box off) → fall back to the SSH `origin` push (unchanged; e.g. an instance whose
|
|
512
|
+
// matching deploy key was cloned into origin). `deps.getGithubToken` is injectable for
|
|
513
|
+
// tests; production resolves it lazily against the core db slice with the runner's pool.
|
|
514
|
+
let ownerToken = null;
|
|
515
|
+
if (inst.owner_builder_id) {
|
|
516
|
+
try {
|
|
517
|
+
const fetchTok = deps.getGithubToken
|
|
518
|
+
|| ((bid) => require(path.join(REPO_ROOT, 'src/bongos/db')).getGithubToken({ builderId: bid, p: deps.db }));
|
|
519
|
+
const r = await fetchTok(inst.owner_builder_id);
|
|
520
|
+
ownerToken = r && r.token ? r.token : null;
|
|
521
|
+
} catch (e) { log(` [scaffold] owner OAuth token lookup failed (${e && e.message}) — using the SSH remote`); }
|
|
522
|
+
}
|
|
523
|
+
if (ownerToken) {
|
|
524
|
+
const credFile = path.join(os.tmpdir(), `bongos-gitcred-${inst.slug}-${process.pid}`);
|
|
525
|
+
writeFile(credFile, `https://x-access-token:${ownerToken}@github.com\n`, { mode: 0o600 });
|
|
526
|
+
const httpsUrl = `https://x-access-token@github.com/${repo.owner}/${repo.name}.git`;
|
|
527
|
+
try {
|
|
528
|
+
exec(`git -C ${root} -c credential.helper='store --file=${credFile}' push ${httpsUrl} HEAD:main`);
|
|
529
|
+
log(` [scaffold] pushed to ${repo.owner}/${repo.name}@main via the owner's GitHub token (task 1002317)`);
|
|
530
|
+
} finally {
|
|
531
|
+
exec(`rm -f ${credFile}`, { allowFail: true });
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
exec(`git -C ${root} push -u origin main`);
|
|
535
|
+
log(` [scaffold] pushed initial scaffold to ${repo.owner}/${repo.name}@main (SSH origin)`);
|
|
536
|
+
}
|
|
417
537
|
return { ok: true, scaffolded: true };
|
|
418
538
|
}
|
|
419
539
|
|
|
@@ -562,7 +682,7 @@ function provisionedEnvPrefix() {
|
|
|
562
682
|
// the hall on this host + OAuth uses the real callback origin — without them the pack
|
|
563
683
|
// stays at neutral localhost and the hall never loads (task 1972). No domain yet
|
|
564
684
|
// (bring-your-own deferred, ADR 0111 §4) ⇒ origins stay neutral until one is set.
|
|
565
|
-
function webEnvBody(inst, { clientId = '', clientSecret = '' } = {}) {
|
|
685
|
+
function webEnvBody(inst, { clientId = '', clientSecret = '', federation = null } = {}) {
|
|
566
686
|
const prefix = provisionedEnvPrefix();
|
|
567
687
|
const origin = inst.domain ? `https://${inst.domain}` : '';
|
|
568
688
|
const originLines = origin
|
|
@@ -583,7 +703,11 @@ ${prefix}_OAUTH_ORIGIN=${origin}
|
|
|
583
703
|
// Manifest flow (placeManifestCreds fetches them control-plane-side + re-writes this
|
|
584
704
|
// file with clientId/clientSecret set), or the manual oauth-secret.js placement. On
|
|
585
705
|
// the first provision write they are blank placeholders the flow fills later.
|
|
586
|
-
const credComment =
|
|
706
|
+
const credComment = federation
|
|
707
|
+
? `# Provisioned by scripts/gds/provision.js (ADR 0111). This instance FEDERATES sign-in
|
|
708
|
+
# through the Cloud Bongos hub (ADR 0141 §4) — it needs no GitHub app of its own, so the
|
|
709
|
+
# GitHub client id/secret below stay blank; the ${prefix}_HUB_* block drives sign-in.`
|
|
710
|
+
: clientId || clientSecret
|
|
587
711
|
? `# Provisioned by scripts/gds/provision.js (ADR 0111). OAuth app creds placed via the
|
|
588
712
|
# GitHub App Manifest flow (ADR 0133) — the owner clicked one link; no secret was pasted.`
|
|
589
713
|
: `# Provisioned by scripts/gds/provision.js (ADR 0111). Fill the OAuth app creds via the
|
|
@@ -602,6 +726,20 @@ ${prefix}_OAUTH_ORIGIN=${origin}
|
|
|
602
726
|
const firstAdminLine = ownerRepo && ownerRepo.owner
|
|
603
727
|
? `${prefix}_FIRST_ADMIN=${ownerRepo.owner}\n`
|
|
604
728
|
: '';
|
|
729
|
+
// Cross-site identity federation block (ADR 0141 §4, task #1002109). Present only for
|
|
730
|
+
// a HOSTED (federated) instance: flips auth.idp ON via the env override branding.js
|
|
731
|
+
// reads (idpEnabled), and carries the hub origin, this instance's per-client secret
|
|
732
|
+
// (authenticates it TO the hub, server-to-server), and the hub's PUBLIC key (verifies
|
|
733
|
+
// the CLI's hub-signed assertion offline). All written straight to this chmod-600
|
|
734
|
+
// file — never through a browser or chat (the ADR 0133 secret-placement posture).
|
|
735
|
+
const fedLines = federation
|
|
736
|
+
? `${prefix}_AUTH_IDP=true
|
|
737
|
+
${prefix}_HUB_ORIGIN=${federation.hubOrigin}
|
|
738
|
+
${prefix}_HUB_CLIENT_ID=${federation.clientId}
|
|
739
|
+
${prefix}_HUB_CLIENT_SECRET=${federation.clientSecret}
|
|
740
|
+
${prefix}_HUB_PUBLIC_KEY=${federation.hubPublicKey}
|
|
741
|
+
`
|
|
742
|
+
: '';
|
|
605
743
|
return `# /etc/${inst.slug}/web.env — per-instance secrets (chmod 600, OUTSIDE the repo).
|
|
606
744
|
${credComment}
|
|
607
745
|
${prefix}_GITHUB_CLIENT_ID=${clientId || ''}
|
|
@@ -609,12 +747,88 @@ ${prefix}_GITHUB_CLIENT_SECRET=${clientSecret || ''}
|
|
|
609
747
|
${firstAdminLine}GDS_BRANDING_FILE=${PROVISIONED_BRANDING_FILE}
|
|
610
748
|
${originLines}PORT=${inst.port}
|
|
611
749
|
PGDATABASE=${dbName(inst)}
|
|
612
|
-
`;
|
|
750
|
+
${fedLines}`;
|
|
613
751
|
}
|
|
614
752
|
function webEnvPath(inst, { etcBase = CONFIG.etcBase } = {}) {
|
|
615
753
|
return path.posix.join(etcBase, inst.slug, 'web.env');
|
|
616
754
|
}
|
|
617
755
|
|
|
756
|
+
// ── Cross-site identity: federate a hosted instance's sign-in through the hub ───
|
|
757
|
+
// (ADR 0141 §4, task #1002109). The control plane IS the hub (cloudbongos.com), so
|
|
758
|
+
// registration is a direct in-process call against the hub pool — no HTTP hop.
|
|
759
|
+
|
|
760
|
+
// The hub's own public origin, written into each federated instance as
|
|
761
|
+
// <PREFIX>_HUB_ORIGIN (what auth.js loadIdpConfig reads). Explicit PROVISION_HUB_ORIGIN
|
|
762
|
+
// wins; otherwise the control plane's OWN public origin (<PREFIX>_PUBLIC_ORIGIN, the
|
|
763
|
+
// env the hub already runs under). Null → not hub-configured → shouldFederate is false.
|
|
764
|
+
function federationHubOrigin() {
|
|
765
|
+
const explicit = process.env.PROVISION_HUB_ORIGIN;
|
|
766
|
+
if (explicit) return String(explicit).replace(/\/+$/, '');
|
|
767
|
+
const own = process.env[`${provisionedEnvPrefix()}_PUBLIC_ORIGIN`];
|
|
768
|
+
return own ? String(own).replace(/\/+$/, '') : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Whether provisioning should federate this instance's sign-in through the hub.
|
|
772
|
+
// Dormant-by-default: true ONLY when the control plane is hub-configured (the
|
|
773
|
+
// platform-identity module is present, a signing key is loadable, AND a hub origin
|
|
774
|
+
// resolves), the instance is a CO-TENANT (the mothership-hosted shape whose web.env is
|
|
775
|
+
// written locally — a `standalone` is owner-hosted and keeps its own GitHub app, the
|
|
776
|
+
// self-hosted path; a `dedicated` droplet has no local web.env write yet, same gap the
|
|
777
|
+
// ADR 0133 manifest flow leaves — see placeManifestCreds), and it has a public domain
|
|
778
|
+
// (the origin the hub binds the CLI mint audience to — the #1002108 account-takeover
|
|
779
|
+
// fix). Returns { federate, reason } so the caller can log WHY it skipped. PURE-ish
|
|
780
|
+
// (reads env + the injected module handles; no DB, no I/O).
|
|
781
|
+
function shouldFederate(inst, deps) {
|
|
782
|
+
const { platformIdentity, hubKeys } = deps;
|
|
783
|
+
if (!platformIdentity || !hubKeys) return { federate: false, reason: 'platform-identity module not present (control plane is not the hub)' };
|
|
784
|
+
if (!hubKeys.hubSigningConfigured()) return { federate: false, reason: 'hub signing key not configured (<PREFIX>_HUB_SIGNING_KEY unset)' };
|
|
785
|
+
if (!federationHubOrigin()) return { federate: false, reason: 'hub origin not resolvable (set PROVISION_HUB_ORIGIN or <PREFIX>_PUBLIC_ORIGIN)' };
|
|
786
|
+
if (inst.hosting_shape !== 'co-tenant') return { federate: false, reason: `${inst.hosting_shape} keeps its own GitHub app (not the co-tenant hosted path)` };
|
|
787
|
+
if (!inst.domain) return { federate: false, reason: 'no domain yet — federation needs the instance public origin' };
|
|
788
|
+
return { federate: true, reason: null };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Register this hosted instance as an sso_clients row on the HUB (fresh per-client
|
|
792
|
+
// secret) and return the federation block to bake into its web.env (ADR 0141 §4).
|
|
793
|
+
// SECURITY: `origin` MUST be the instance's EXACT public origin — the hub binds the
|
|
794
|
+
// CLI-federation mint audience to it (the #1002108 fix), so a mismatch would break
|
|
795
|
+
// device sign-in (and a wrong origin would be a takeover vector). client_id is the
|
|
796
|
+
// (unique, DNS-safe) slug, so a re-provision rotates the SAME row's secret via
|
|
797
|
+
// registerClient's ON CONFLICT rather than orphaning a client. redirect_uri lists both
|
|
798
|
+
// callback prefixes the core may emit (/api/gds is what auth.js builds today) so the
|
|
799
|
+
// hub's exact-match allow-list accepts the browser leg. --apply gates the real hub
|
|
800
|
+
// write; dry-run returns a placeholder block (so webEnvBody can still be composed) + logs.
|
|
801
|
+
async function federateInstance(inst, deps) {
|
|
802
|
+
const { db, platformIdentity, hubKeys, log, apply } = deps;
|
|
803
|
+
const origin = `https://${inst.domain}`;
|
|
804
|
+
const clientId = inst.slug;
|
|
805
|
+
const hubOrigin = federationHubOrigin();
|
|
806
|
+
const hubPublicKey = hubKeys.hubPublicKeyB64();
|
|
807
|
+
if (!hubPublicKey) throw new Error('hub signing key is set but unreadable — cannot derive HUB_PUBLIC_KEY');
|
|
808
|
+
const redirectUris = [
|
|
809
|
+
`${origin}/api/gds/auth/web/callback`,
|
|
810
|
+
`${origin}/api/bongos/auth/web/callback`,
|
|
811
|
+
];
|
|
812
|
+
if (!apply) {
|
|
813
|
+
log(` [federate] would register client '${clientId}' on the hub (origin ${origin}) + write ${provisionedEnvPrefix()}_HUB_* + AUTH_IDP into web.env`);
|
|
814
|
+
return { hubOrigin, clientId, clientSecret: '0'.repeat(43), hubPublicKey };
|
|
815
|
+
}
|
|
816
|
+
const clientSecret = crypto.randomBytes(32).toString('base64url');
|
|
817
|
+
// Best-effort owner github_id — nice for the hub catalog's ownership link, but
|
|
818
|
+
// nullable in registerClient, so a lookup miss must never fail the provision.
|
|
819
|
+
let ownerGithubId = null;
|
|
820
|
+
try {
|
|
821
|
+
const { rows } = await db.query('SELECT github_id FROM builders WHERE id = $1', [inst.owner_builder_id]);
|
|
822
|
+
ownerGithubId = rows[0] ? rows[0].github_id : null;
|
|
823
|
+
} catch { /* non-fatal */ }
|
|
824
|
+
await platformIdentity.registerClient(
|
|
825
|
+
{ clientId, secret: clientSecret, name: inst.slug, redirectUris, origin, ownerGithubId },
|
|
826
|
+
{ pool: db },
|
|
827
|
+
);
|
|
828
|
+
log(` [federate] registered client '${clientId}' on the hub (origin ${origin})`);
|
|
829
|
+
return { hubOrigin, clientId, clientSecret, hubPublicKey };
|
|
830
|
+
}
|
|
831
|
+
|
|
618
832
|
// The Cloudflare zone a hostname's A record lives in. Cloudflare zones are
|
|
619
833
|
// registrable domains, which for the common case is the last two DNS labels
|
|
620
834
|
// (mercury.example.com → example.com; apex example.com → example.com). Multi-part
|
|
@@ -925,6 +1139,28 @@ async function provisionInstance(inst, deps) {
|
|
|
925
1139
|
if (apply) await provisioning.setInstanceStatus(db, inst.id, 'provisioning');
|
|
926
1140
|
const patch = {};
|
|
927
1141
|
|
|
1142
|
+
// ── cross-site identity: register hub federation (ADR 0141 §4, task #1002109) ──
|
|
1143
|
+
// Computed BEFORE the shape steps so the co-tenant web.env write (step 3) bakes it
|
|
1144
|
+
// in — the instance comes up already federated, no post-hoc restart. Dormant unless
|
|
1145
|
+
// the control plane is hub-configured AND the shape/domain qualify (shouldFederate);
|
|
1146
|
+
// otherwise the instance owns its auth (direct GitHub), byte-identically to today.
|
|
1147
|
+
let federation = null;
|
|
1148
|
+
{
|
|
1149
|
+
const fed = shouldFederate(inst, deps);
|
|
1150
|
+
if (fed.federate) {
|
|
1151
|
+
federation = await federateInstance(inst, deps);
|
|
1152
|
+
log(` [federate] ${inst.slug} → hub sign-in (auth.idp on) — no GitHub app of its own`);
|
|
1153
|
+
if (apply) {
|
|
1154
|
+
await provisioning.recordEvent(db, {
|
|
1155
|
+
instanceId: inst.id, ownerBuilderId: inst.owner_builder_id, event: 'federated',
|
|
1156
|
+
detail: `sign-in federated to the hub (client ${inst.slug}, origin https://${inst.domain})`, actor: 'runner',
|
|
1157
|
+
}).catch(() => {});
|
|
1158
|
+
}
|
|
1159
|
+
} else {
|
|
1160
|
+
log(` [federate] skipped (${fed.reason}) — instance owns its auth (direct GitHub)`);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
928
1164
|
// ── step 1: server ───────────────────────────────────────────────────────
|
|
929
1165
|
// The A-record target for step 5's DNS UPSERT:
|
|
930
1166
|
// • co-tenant → PROVISION_PUBLIC_IP (the box it runs on — under remote-exec that
|
|
@@ -1018,11 +1254,19 @@ async function provisionInstance(inst, deps) {
|
|
|
1018
1254
|
// here should surface. Dedicated bakes the same step into cloud-init (dedicatedUserData).
|
|
1019
1255
|
log(' [seed] first version (only if versions is empty)');
|
|
1020
1256
|
boxExec(seedFirstVersionCmd(inst, { privileged }));
|
|
1257
|
+
// Regenerate the whole-file navigation docs from the pinned core so a fresh/redeployed
|
|
1258
|
+
// standalone instance's docs/ stays live (ADR 0110 §2, task 1002314) — the deploy-regen the
|
|
1259
|
+
// bespoke pull scripts were missing. Best-effort: regen-instance-docs.js exits 0 even on a
|
|
1260
|
+
// generator hiccup (LOUD ::ERROR::), so a stale nav doc never fails a provision.
|
|
1261
|
+
if (standalone) {
|
|
1262
|
+
log(' [docs] regenerate whole-file navigation docs (session-log index)');
|
|
1263
|
+
boxExec(standaloneRegenDocsCmd(), { cwd: standaloneRoot(inst), allowFail: true });
|
|
1264
|
+
}
|
|
1021
1265
|
} else { log(' (db create + migrate + first-version seed run via cloud-init on the droplet)'); }
|
|
1022
1266
|
|
|
1023
1267
|
// ── step 3: config + per-instance secrets ─────────────────────────────────
|
|
1024
1268
|
log(' [config] per-instance web.env');
|
|
1025
|
-
if (local) boxWriteFile(webEnvPath(inst), webEnvBody(inst), { mode: 0o600, sudo: true });
|
|
1269
|
+
if (local) boxWriteFile(webEnvPath(inst), webEnvBody(inst, { federation }), { mode: 0o600, sudo: true });
|
|
1026
1270
|
else log(' (baked into cloud-init)');
|
|
1027
1271
|
|
|
1028
1272
|
// ── step 4: systemd unit + enable ─────────────────────────────────────────
|
|
@@ -1476,8 +1720,8 @@ async function main() {
|
|
|
1476
1720
|
const slug = args[1] && !args[1].startsWith('--') ? args[1] : null;
|
|
1477
1721
|
if ((cmd === 'provision' || cmd === 'teardown') && !slug) { console.error(`${cmd} needs a <slug>`); process.exit(2); }
|
|
1478
1722
|
|
|
1479
|
-
let pool, provisioning, DoApi;
|
|
1480
|
-
try { ({ pool, provisioning, DoApi } = loadDeps()); }
|
|
1723
|
+
let pool, provisioning, DoApi, platformIdentity, hubKeys;
|
|
1724
|
+
try { ({ pool, provisioning, DoApi, platformIdentity, hubKeys } = loadDeps()); }
|
|
1481
1725
|
catch (e) { console.error(`cannot load DB layer (run this on the control plane): ${e.message}`); process.exit(1); }
|
|
1482
1726
|
|
|
1483
1727
|
// Box-target executor (remote over SSH when PROVISION_REMOTE_HOST is set, else
|
|
@@ -1492,6 +1736,9 @@ async function main() {
|
|
|
1492
1736
|
writeFile: makeWriteFile({ apply, privileged: CONFIG.privileged, exec, remoteHost: CONFIG.remoteHost }),
|
|
1493
1737
|
controlWriteFile: makeWriteFile({ apply, privileged: CONFIG.privileged, exec: controlExec }),
|
|
1494
1738
|
doApi: DoApi ? new DoApi({ dryRun: !apply, token: CONFIG.doToken, log: (m) => console.log(' ' + m) }) : null,
|
|
1739
|
+
// Cross-site identity (ADR 0141 §4). Null on a non-hub control plane → federation
|
|
1740
|
+
// dormant; shouldFederate() then skips and the instance owns its auth (as today).
|
|
1741
|
+
platformIdentity, hubKeys,
|
|
1495
1742
|
};
|
|
1496
1743
|
|
|
1497
1744
|
let result;
|
|
@@ -1518,8 +1765,8 @@ module.exports = {
|
|
|
1518
1765
|
dbName, dbCreateCmd, migrateCmd, seedFirstVersionCmd, serviceUnit, serviceUnitPath, webEnvBody, webEnvPath,
|
|
1519
1766
|
backupUnitName, backupServicePath, backupTimerPath, backupScriptPath, backupService, backupTimer,
|
|
1520
1767
|
standaloneRoot, standalonePullCmd, standaloneInstallCmd, standaloneMigrateCmd,
|
|
1521
|
-
// standalone scaffold (ADR 0108 / 0125, task 2055)
|
|
1522
|
-
parseTargetRef, instanceRepoRemote, instanceInitSpec, scaffoldStandaloneRepo, coreVersionSafe,
|
|
1768
|
+
// standalone scaffold (ADR 0108 / 0125, task 2055; consumer-core fix task 1002275)
|
|
1769
|
+
parseTargetRef, instanceRepoRemote, instanceInitSpec, installedCoreTarball, scaffoldStandaloneRepo, coreVersionSafe,
|
|
1523
1770
|
zoneForDomain, dnsUpsertEnv, classifyZoneScope, checkDnsTokenScope,
|
|
1524
1771
|
caddyBlock, caddySnippetPath, healthzCmd, dedicatedUserData, nextFreePort,
|
|
1525
1772
|
// executors + orchestration (injectable deps for tests)
|
|
@@ -1527,4 +1774,6 @@ module.exports = {
|
|
|
1527
1774
|
cmdRunIntents, cmdReconcile, cmdStatus,
|
|
1528
1775
|
// GitHub App Manifest flow (task 2080, ADR 0133)
|
|
1529
1776
|
convertManifestCode, placeManifestCreds, drainOAuthManifests, MANIFEST_UA,
|
|
1777
|
+
// cross-site identity federation (ADR 0141 §4, task #1002109)
|
|
1778
|
+
federationHubOrigin, shouldFederate, federateInstance,
|
|
1530
1779
|
};
|
|
@@ -61,17 +61,20 @@ const PUBLISH_ALLOWLIST = [
|
|
|
61
61
|
'modules/sessions/',
|
|
62
62
|
'modules/autonomy/',
|
|
63
63
|
'modules/security/',
|
|
64
|
+
'modules/governance/', // configurable roles & permissions (BV1 goal 1000030, ADR 0151) — the rank/permission machinery publishes AS the open-source methodology (ADR 0065 §4 / ADR 0098); it is in PROTECTED_GLOBS for write-protection, NOT publish-exclusion. default:true.
|
|
64
65
|
'modules/hall-ui/',
|
|
65
66
|
'modules/status-ui/',
|
|
66
67
|
'modules/dev-box/',
|
|
67
68
|
'modules/discord/',
|
|
68
69
|
'modules/provisioning/', // instance-hosting control plane — CORE like its sibling dev-box (same do-api/cf-tunnel toolchain, all core). Reclassified IN so the core CLI scripts/gds/provision.js resolves it from the core package post-split, not a MODULE_NOT_FOUND (task 2135). default:false — shipped ≠ enabled.
|
|
70
|
+
'modules/platform-identity/', // hub identity provider (ADR 0141) — ships in core, enabled ONLY on the hub (cloudbongos.com). MUST be here or the hub's enabledModules() throws "unknown module" and provision.js's federation load MODULE_NOT_FOUNDs (task 1002235, same rationale as provisioning above). default:false — shipped ≠ enabled; a vanilla/self-hosted instance never turns it on.
|
|
69
71
|
// --- neutral (customer-0) config starters + their schemas (the filled-in *.json twins are denied) ---
|
|
70
72
|
'config/branding.neutral.json',
|
|
71
73
|
'config/modules.neutral.json',
|
|
72
74
|
'config/design-tokens.neutral.json',
|
|
73
75
|
'config/design-tokens.schema.json', // the design-token contract (generic schema, no instance values)
|
|
74
76
|
'config/hierarchy.neutral.json',
|
|
77
|
+
'config/governance.neutral.json', // the starting role templates a fresh instance inherits (BV1.R96)
|
|
75
78
|
// --- the portable methodology core, governance, and sanitized docs ---
|
|
76
79
|
'CLAUDE.md',
|
|
77
80
|
'README.md',
|
|
@@ -86,7 +89,9 @@ const PUBLISH_ALLOWLIST = [
|
|
|
86
89
|
'docs/architecture.md',
|
|
87
90
|
// Served to the hall / advertised by /api/bongos — a standalone that omits these
|
|
88
91
|
// renders broken onboarding + API-reference pages (task 2078). Redacted like the
|
|
89
|
-
// rest of this docs section
|
|
92
|
+
// rest of this docs section. Only the diagram .mmd SOURCES ship: the .png/.svg are
|
|
93
|
+
// gitignored (ADR 0122) so the tarball cannot carry them, and `bongos upgrade`
|
|
94
|
+
// regenerates them on the box via healDocAssets() (upgrade.js, task 1002441).
|
|
90
95
|
'docs/onboarding/', // primer.md + drachmae-and-karma.md + diagram sources (/builders/primer · /drachmae · /diagrams)
|
|
91
96
|
// API artifacts (classification settled, task 2141): CORE-shipped STARTERS so a fresh
|
|
92
97
|
// hall/docs render before first regen — but per-instance GENERATED. init + `bongos
|
|
@@ -206,6 +211,7 @@ const INSTANCE_EXCLUDE = [
|
|
|
206
211
|
'config/modules.json',
|
|
207
212
|
'config/devbox-app.json',
|
|
208
213
|
'config/hierarchy.json',
|
|
214
|
+
'config/governance.json',
|
|
209
215
|
'CODEOWNERS', // names the owner (a protected surface, excluded as identity)
|
|
210
216
|
// instance SEED migrations — this host's scope-truth SQL (planning seeds, world data).
|
|
211
217
|
// ADR 0105: a consumer's core must not carry them; migrate.sh reads instance seeds from
|
|
@@ -250,6 +256,21 @@ const INSTANCE_EXCLUDE = [
|
|
|
250
256
|
'docs/design/container-exterior-tiles.md',
|
|
251
257
|
'docs/design/container-interior.md',
|
|
252
258
|
'docs/design/world-map-scroll.md',
|
|
259
|
+
// instance-HISTORY ADRs (task 1002219 / audit H5). These record THIS platform-family's own
|
|
260
|
+
// migration + identity history (the amazonprimea→cloudbongos lift, the standalone reprovision,
|
|
261
|
+
// the cloudbongos identity hub) — they are inherently full of the prior instance's name/owner and
|
|
262
|
+
// genericize to nothing (unlike a portable methodology ADR). docs/adr/ is allowlisted wholesale for
|
|
263
|
+
// the portable decision records; these three stay in this repo and never ship in the core, mirroring
|
|
264
|
+
// the instance-recipe / game-design carve-outs above. (The portable ADRs + the README index still publish.)
|
|
265
|
+
'docs/adr/0139-platform-history-migration-to-cloudbongos.md',
|
|
266
|
+
'docs/adr/0140-cloudbongos-standalone-reprovision.md',
|
|
267
|
+
'docs/adr/0141-cross-site-identity-federated-hub-idp.md',
|
|
268
|
+
// hall-preview FIXTURES (task 1002219 / audit H5) — the local hall-UI preview harness's sample
|
|
269
|
+
// data is a snapshot of THIS instance's builders/goals/tasks (owner login, world name, prior
|
|
270
|
+
// instance name), pure instance identity. The harness code stays portable; only the OTB data is
|
|
271
|
+
// carved out, so a consumer core never ships customer-zero's directory. (Fixtures remain in-repo
|
|
272
|
+
// for local dev — this only affects what package-core publishes.)
|
|
273
|
+
'scripts/hall-preview/fixtures/',
|
|
253
274
|
'blockers/',
|
|
254
275
|
'limitations/',
|
|
255
276
|
'generative-ideas.md',
|
package/scripts/gds/reauth.js
CHANGED
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
const { spawn } = require('node:child_process');
|
|
40
40
|
const lib = require('./cli-lib');
|
|
41
41
|
|
|
42
|
+
// lib.API_BASE is session-aware (task 1002427), so both URLs name the instance the
|
|
43
|
+
// builder is actually signed into — not this checkout's branding default, which is
|
|
44
|
+
// what sent a cloudbongos.com builder to a localhost:3000 click-path.
|
|
42
45
|
const HALL_URL = `${lib.API_BASE}/builders`;
|
|
43
46
|
// The "Re-issue CLI token" button lives on the Settings page (under "CLI
|
|
44
47
|
// Access"), not the hall profile — open the builder right where the button is.
|
|
@@ -56,9 +59,10 @@ function openInBrowser(url) {
|
|
|
56
59
|
case 'win32':
|
|
57
60
|
// rundll32's FileProtocolHandler opens a URL without going through a
|
|
58
61
|
// shell, so (unlike `cmd /c start`) shell metacharacters in the URL are
|
|
59
|
-
// never interpreted.
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
+
// never interpreted. This is now load-bearing, not hypothetical: since
|
|
63
|
+
// task 1002427 the URL derives from the session file's api_base (via the
|
|
64
|
+
// session-aware lib.API_BASE), not from a bare env/branding constant. That
|
|
65
|
+
// file is the builder's own 0600 config, but it IS file-derived input.
|
|
62
66
|
cmd = 'rundll32';
|
|
63
67
|
args = ['url.dll,FileProtocolHandler', url];
|
|
64
68
|
break;
|