@bongos/core 1.18.3 → 1.18.19
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 +1576 -286
- 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 +4 -2
- 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 +6 -1
- 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/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 +10 -0
- package/docs/api/openapi.json +843 -17
- package/docs/api-reference.md +23 -10
- 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 +8 -0
- 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/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/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/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/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/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/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 +51 -58
- 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 -27
- 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/login.js +16 -1
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +123 -12
- package/scripts/gds/publish-manifest.js +7 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +4 -2
- 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 +31 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +319 -29
- 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 +124 -2
- package/scripts/migrate.sh +99 -14
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +447 -102
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +39 -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 +219 -2
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +32 -0
- package/src/instance-config.js +30 -0
- package/src/module-api.js +124 -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 +12 -0
- 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/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 +93 -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/migrate_root_resolution.mjs +124 -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/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 +132 -7
- 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 +80 -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 +139 -0
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Compare what a live instance is SERVING against what is on main.
|
|
3
|
+
//
|
|
4
|
+
// Merging to main deploys nothing (ADR 0108/0137 — publishing is manual and
|
|
5
|
+
// owner-gated). That is by design, but every layer around it still reports
|
|
6
|
+
// success: /builder-ship prints success, the publish-reconciler flips
|
|
7
|
+
// confirmed → shipped, and credits pay out on "land". Nothing compared live
|
|
8
|
+
// against main, so between 2026-07-24 and 2026-08-03 a hundred and seventy-one
|
|
9
|
+
// commits — including a whole governance permission system — sat on main while
|
|
10
|
+
// cloudbongos.com served 1.18.17, and nobody noticed for ten days (blocker
|
|
11
|
+
// 1000097 / task 1002521). This script is the missing comparison.
|
|
12
|
+
//
|
|
13
|
+
// Two independent gaps, both reported:
|
|
14
|
+
// 1. UNRELEASED — commits on main since the last CORE_VERSION bump. No
|
|
15
|
+
// instance can take these at any pin, because no version carries them.
|
|
16
|
+
// 2. UNDEPLOYED — the live instance's coreVersion is behind the CORE_VERSION
|
|
17
|
+
// on main. A release exists; this instance has not moved its pin to it.
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// node scripts/gds/deploy-divergence.js # check the instance this checkout talks to
|
|
21
|
+
// node scripts/gds/deploy-divergence.js --url https://example.com
|
|
22
|
+
// node scripts/gds/deploy-divergence.js --quiet # only print on divergence
|
|
23
|
+
// node scripts/gds/deploy-divergence.js --warn-only # always exit 0 (for a non-blocking ship warning)
|
|
24
|
+
//
|
|
25
|
+
// Exit codes: 0 aligned (or --warn-only), 1 diverged, 2 could not determine.
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { execSync } = require('node:child_process');
|
|
30
|
+
|
|
31
|
+
const { API_BASE, arg, hasFlag } = require('./cli-lib');
|
|
32
|
+
|
|
33
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
34
|
+
const MODULE_API = path.join(REPO_ROOT, 'src', 'module-api.js');
|
|
35
|
+
|
|
36
|
+
// Read CORE_VERSION by regex rather than require() — this runs in checkouts
|
|
37
|
+
// (and CI) where loading the doorway would pull config/branding that may not
|
|
38
|
+
// resolve, and we only ever want the literal.
|
|
39
|
+
function coreVersionAt(ref) {
|
|
40
|
+
let src;
|
|
41
|
+
try {
|
|
42
|
+
src = ref
|
|
43
|
+
? execSync(`git show ${ref}:src/module-api.js`, { cwd: REPO_ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 * 1024 * 1024 })
|
|
44
|
+
: fs.readFileSync(MODULE_API, 'utf8');
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const m = src.match(/^const CORE_VERSION = '([^']+)'/m);
|
|
49
|
+
return m ? m[1] : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function git(cmd, fallback = null) {
|
|
53
|
+
try {
|
|
54
|
+
return execSync(`git ${cmd}`, { cwd: REPO_ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 * 1024 * 1024 }).trim();
|
|
55
|
+
} catch {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The commit that last changed the CORE_VERSION literal, and how many commits
|
|
61
|
+
// sit on top of it. Must be `-G` (any diff line matching the regex), NOT `-S`:
|
|
62
|
+
// `-S` fires only when the number of occurrences changes, and a bump rewrites
|
|
63
|
+
// the single existing line — so `-S` silently walks back to the commit that
|
|
64
|
+
// first introduced the constant and reports every commit since as unreleased.
|
|
65
|
+
// `--no-merges` is load-bearing, not a nicety: a bump lands on main VIA a merge,
|
|
66
|
+
// so counting merges means the moment any release lands there is always ≥1
|
|
67
|
+
// "unreleased" commit — the merge that landed the bump. This check exists
|
|
68
|
+
// because a real gap hid for ten days behind layers that all reported success;
|
|
69
|
+
// one that cries wolf after every single release earns exactly the same
|
|
70
|
+
// scrolling-past it was added to prevent. Merge commits carry no content of
|
|
71
|
+
// their own, so they are not unreleased work. (task 1002558)
|
|
72
|
+
function unreleasedSince(mainRef) {
|
|
73
|
+
const bump = git(`log ${mainRef} -1 --format=%H -G"^const CORE_VERSION = " -- src/module-api.js`);
|
|
74
|
+
if (!bump) return null;
|
|
75
|
+
const count = git(`rev-list --count --no-merges ${bump}..${mainRef}`);
|
|
76
|
+
const withMerges = git(`rev-list --count ${bump}..${mainRef}`);
|
|
77
|
+
return {
|
|
78
|
+
bump,
|
|
79
|
+
bumpShort: bump.slice(0, 8),
|
|
80
|
+
bumpSubject: git(`log -1 --format=%s ${bump}`, ''),
|
|
81
|
+
bumpDate: git(`log -1 --format=%cs ${bump}`, ''),
|
|
82
|
+
count: count === null ? null : Number(count),
|
|
83
|
+
countWithMerges: withMerges === null ? null : Number(withMerges),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function cmpSemver(a, b) {
|
|
88
|
+
const pa = String(a).split('.').map(Number);
|
|
89
|
+
const pb = String(b).split('.').map(Number);
|
|
90
|
+
for (let i = 0; i < 3; i += 1) {
|
|
91
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
92
|
+
if (d) return d < 0 ? -1 : 1;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function fetchLive(base) {
|
|
98
|
+
const url = `${String(base).replace(/\/+$/, '')}/version`;
|
|
99
|
+
const ctl = new AbortController();
|
|
100
|
+
const timer = setTimeout(() => ctl.abort(), 15000);
|
|
101
|
+
try {
|
|
102
|
+
const res = await fetch(url, { signal: ctl.signal, headers: { accept: 'application/json' } });
|
|
103
|
+
if (!res.ok) return { url, error: `HTTP ${res.status}` };
|
|
104
|
+
const body = await res.json();
|
|
105
|
+
if (!body || !body.coreVersion) return { url, error: 'no coreVersion field in /version' };
|
|
106
|
+
return { url, coreVersion: body.coreVersion, commitShort: body.commitShort || null };
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return { url, error: e.name === 'AbortError' ? 'timed out after 15s' : (e.message || String(e)) };
|
|
109
|
+
} finally {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function check({ base } = {}) {
|
|
115
|
+
const target = base || arg('--url') || API_BASE;
|
|
116
|
+
const local = coreVersionAt(null);
|
|
117
|
+
if (!local) return { ok: false, undetermined: true, reason: 'could not read CORE_VERSION from src/module-api.js', target };
|
|
118
|
+
|
|
119
|
+
// Prefer origin/main — the released trunk — but fall back to HEAD in a
|
|
120
|
+
// checkout with no remote (a box, a tarball) so the check still says
|
|
121
|
+
// something useful instead of bailing.
|
|
122
|
+
const mainRef = git('rev-parse --verify --quiet origin/main') ? 'origin/main' : 'HEAD';
|
|
123
|
+
const onMain = coreVersionAt(mainRef) || local;
|
|
124
|
+
const unreleased = unreleasedSince(mainRef);
|
|
125
|
+
const live = await fetchLive(target);
|
|
126
|
+
|
|
127
|
+
if (live.error) return { ok: false, undetermined: true, reason: `could not read ${live.url}: ${live.error}`, target, local, onMain, unreleased };
|
|
128
|
+
|
|
129
|
+
const behind = cmpSemver(live.coreVersion, onMain) < 0;
|
|
130
|
+
const ahead = cmpSemver(live.coreVersion, onMain) > 0;
|
|
131
|
+
const hasUnreleased = !!(unreleased && unreleased.count > 0);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
ok: !behind && !hasUnreleased,
|
|
135
|
+
target, local, onMain, mainRef, unreleased, live, behind, ahead, hasUnreleased,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function report(r, { quiet = false } = {}) {
|
|
140
|
+
const say = (...a) => console.log(...a);
|
|
141
|
+
|
|
142
|
+
if (r.undetermined) {
|
|
143
|
+
console.error(`deploy-divergence: undetermined — ${r.reason}`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (r.ok && quiet) return;
|
|
147
|
+
|
|
148
|
+
const host = String(r.target).replace(/^https?:\/\//, '');
|
|
149
|
+
say('');
|
|
150
|
+
say(` Deploy divergence · ${host}`);
|
|
151
|
+
say(' ──────────────────────────────────────────────────────────');
|
|
152
|
+
say(` live (serving) ${r.live.coreVersion}${r.live.commitShort ? ` (instance commit ${r.live.commitShort})` : ''}`);
|
|
153
|
+
say(` ${r.mainRef.padEnd(18)} ${r.onMain}`);
|
|
154
|
+
// Only worth a line when a bump is in flight in this tree but not yet landed.
|
|
155
|
+
if (r.local !== r.onMain) say(` working tree ${r.local} (not yet on ${r.mainRef})`);
|
|
156
|
+
if (r.unreleased) {
|
|
157
|
+
say(` last bump ${r.unreleased.bumpShort} ${r.unreleased.bumpDate} — ${r.unreleased.bumpSubject}`);
|
|
158
|
+
}
|
|
159
|
+
say('');
|
|
160
|
+
|
|
161
|
+
if (r.ok) {
|
|
162
|
+
say(' ✓ aligned — live matches main, and main carries no unreleased commits.');
|
|
163
|
+
say('');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (r.hasUnreleased) {
|
|
168
|
+
// Say which number this is: work commits and with-merges differ a lot on a
|
|
169
|
+
// big window (93 vs 171 for 1.18.18), and an unlabelled figure invites the
|
|
170
|
+
// "~100 vs 171" disagreement that muddied blocker 1000097.
|
|
171
|
+
const u = r.unreleased;
|
|
172
|
+
const merges = u.countWithMerges != null && u.countWithMerges !== u.count
|
|
173
|
+
? ` (${u.countWithMerges} counting merges)` : '';
|
|
174
|
+
say(` ⚠ UNRELEASED — ${u.count} work commit${u.count === 1 ? '' : 's'}${merges} on ${r.mainRef} since the last CORE_VERSION bump.`);
|
|
175
|
+
say(' No instance can take these at ANY pin: no published version contains them.');
|
|
176
|
+
say(` Fix: bump CORE_VERSION in src/module-api.js (+ a changelog line) and land it.`);
|
|
177
|
+
}
|
|
178
|
+
if (r.behind) {
|
|
179
|
+
say(` ⚠ UNDEPLOYED — live is ${r.live.coreVersion}, main declares ${r.onMain}.`);
|
|
180
|
+
say(' A release exists that this instance has not moved its pin to.');
|
|
181
|
+
say(' Fix: package-core, then move the instance pin (see docs/recipes/upgrading-the-core.md).');
|
|
182
|
+
}
|
|
183
|
+
if (r.ahead) {
|
|
184
|
+
say(` note: live (${r.live.coreVersion}) is AHEAD of ${r.mainRef} (${r.onMain}) — this checkout is stale; fetch.`);
|
|
185
|
+
}
|
|
186
|
+
say('');
|
|
187
|
+
say(' Merging to main deploys nothing by design. "shipped" means "merged" —');
|
|
188
|
+
say(' it does not mean users can see it.');
|
|
189
|
+
say('');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function main() {
|
|
193
|
+
const quiet = hasFlag('--quiet');
|
|
194
|
+
const warnOnly = hasFlag('--warn-only');
|
|
195
|
+
const r = await check();
|
|
196
|
+
report(r, { quiet });
|
|
197
|
+
if (warnOnly) return process.exit(0);
|
|
198
|
+
if (r.undetermined) return process.exit(2);
|
|
199
|
+
return process.exit(r.ok ? 0 : 1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (require.main === module) {
|
|
203
|
+
main().catch((e) => {
|
|
204
|
+
console.error('deploy-divergence: failed —', e.message || e);
|
|
205
|
+
process.exit(2);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { check, report, coreVersionAt, unreleasedSince, cmpSemver };
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// scripts/gds/dev-lib.js — pure + injectable core of `bongos dev`, the one-command
|
|
3
|
+
// LOCAL launcher for a standalone instance repo (ADR 0149).
|
|
4
|
+
//
|
|
5
|
+
// WHY. A project scaffolded by the creation flow ships a repo whose
|
|
6
|
+
// config/branding.json points at http://localhost:<port> — it is CONFIGURED to run
|
|
7
|
+
// on the owner's own machine — but the platform shipped no command that actually
|
|
8
|
+
// STARTS it. Standing it up by hand is a six-part assembly (install+start Postgres,
|
|
9
|
+
// createdb, migrate, set PORT/PGDATABASE/PGHOST/OAuth env, launch the server) with
|
|
10
|
+
// three silent mismatches: the server reads PORT from env and NOT from branding
|
|
11
|
+
// (platform-server.js:44); the server DB name collapses to the OS user while
|
|
12
|
+
// migrate.sh defaults to `amazonprimea` (pool.js:47 vs migrate.sh:52) unless
|
|
13
|
+
// PGDATABASE is set for BOTH; and node-pg defaults to the Linux peer socket
|
|
14
|
+
// /var/run/postgresql (pool.js:46), which does not exist under Homebrew Postgres on
|
|
15
|
+
// a Mac. `bongos dev` collapses all of that into one idempotent command.
|
|
16
|
+
//
|
|
17
|
+
// Everything here is a PURE transform or an INJECTABLE helper (deps.fs / deps.spawn
|
|
18
|
+
// / deps.kill / deps.fetchImpl / deps.env / deps.userInfo / deps.*Path), so
|
|
19
|
+
// tests/dev_lib.mjs exercises every decision with NO real spawn, DB, or network.
|
|
20
|
+
// The side-effecting orchestration (start Postgres, createdb, run migrate, hand off
|
|
21
|
+
// to login) lives in dev.js.
|
|
22
|
+
|
|
23
|
+
const { spawn } = require('node:child_process');
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const os = require('node:os');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
const ic = require('../../src/instance-config');
|
|
28
|
+
|
|
29
|
+
// The server binds loopback (platform-server.js HOST default). We probe the IP
|
|
30
|
+
// literal, never "localhost", to dodge any IPv6/hosts-file quirk while it binds
|
|
31
|
+
// 127.0.0.1 — the same choice local-preview-lib.js made.
|
|
32
|
+
const HOST = '127.0.0.1';
|
|
33
|
+
const DEFAULT_PORT = 3000;
|
|
34
|
+
|
|
35
|
+
// scripts/gds/ -> the core package root (holds src/platform-server.js + scripts/migrate.sh).
|
|
36
|
+
const CORE_ROOT = path.resolve(__dirname, '..', '..');
|
|
37
|
+
|
|
38
|
+
function readJsonSafe(p, f = fs) {
|
|
39
|
+
try { return JSON.parse(f.readFileSync(p, 'utf8')); }
|
|
40
|
+
catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Read the instance-identity fields the launcher needs, INSTANCE pack first
|
|
44
|
+
// (config/branding.json — host content, resolveInstanceRoot) then the CORE neutral
|
|
45
|
+
// starter (config/branding.neutral.json — ships with the package). Decoupled from
|
|
46
|
+
// the full branding() resolver on purpose (mirrors pool.js:instanceDbName): a
|
|
47
|
+
// branding validation error must never stop a local dev boot.
|
|
48
|
+
function loadBrandingFacts({ instanceRoot, coreRoot = CORE_ROOT, f = fs } = {}) {
|
|
49
|
+
const inst = readJsonSafe(path.join(instanceRoot, 'config', 'branding.json'), f) || {};
|
|
50
|
+
const neutral = readJsonSafe(path.join(coreRoot, 'config', 'branding.neutral.json'), f) || {};
|
|
51
|
+
const pick = (k, subk) => {
|
|
52
|
+
const from = (o) => (subk ? (o[k] && o[k][subk]) : o[k]);
|
|
53
|
+
const v = from(inst);
|
|
54
|
+
return (v === undefined || v === null || v === '') ? from(neutral) : v;
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
publicOrigin: pick('domains', 'publicOrigin') || `http://localhost:${DEFAULT_PORT}`,
|
|
58
|
+
firstAdmin: pick('firstAdmin') || '',
|
|
59
|
+
// db.database is null in the neutral starter and usually absent in a fresh
|
|
60
|
+
// instance pack — deriveDbName() falls back to the repo dir name.
|
|
61
|
+
brandingDbName: (inst.db && inst.db.database) || (neutral.db && neutral.db.database) || null,
|
|
62
|
+
envPrefix: pick('envPrefix') || ic.FALLBACK_ENV_PREFIX,
|
|
63
|
+
configDir: pick('configDir') || ic.FALLBACK_DIR,
|
|
64
|
+
gameEnabled: false, // overridden by resolveRunParams from config/modules.json
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Port the instance is configured for. The server reads process.env.PORT and
|
|
69
|
+
// ignores branding, so `bongos dev` MUST set PORT to this or the server would come
|
|
70
|
+
// up on 3000 while branding (and every printed link) says <port>.
|
|
71
|
+
function parsePort(origin, fallback = DEFAULT_PORT) {
|
|
72
|
+
try {
|
|
73
|
+
const p = new URL(String(origin)).port;
|
|
74
|
+
const n = Number(p);
|
|
75
|
+
return Number.isInteger(n) && n > 0 && n < 65536 ? n : fallback;
|
|
76
|
+
} catch { return fallback; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Coerce an arbitrary string into a legal, safe Postgres database identifier:
|
|
80
|
+
// lowercased, non [a-z0-9_] collapsed to '_', must begin with a letter or '_'
|
|
81
|
+
// (a leading digit is illegal unquoted), max 63 bytes. Returns null if nothing
|
|
82
|
+
// usable survives, so deriveDbName can fall through to its last-resort default.
|
|
83
|
+
function sanitizeDbName(s) {
|
|
84
|
+
if (!s) return null;
|
|
85
|
+
let out = String(s).toLowerCase().replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '');
|
|
86
|
+
if (!out) return null;
|
|
87
|
+
if (/^[0-9]/.test(out)) out = `db_${out}`;
|
|
88
|
+
return out.slice(0, 63);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The DB name BOTH migrate.sh and the server pool will use. Precedence:
|
|
92
|
+
// explicit --db / override -> branding db.database -> the repo dir name ->
|
|
93
|
+
// 'cloudbongos_dev'. Whatever we pick is exported as PGDATABASE for both sides,
|
|
94
|
+
// which is the fix for the pool.js(OS-user) vs migrate.sh(amazonprimea) split.
|
|
95
|
+
function deriveDbName({ brandingDbName, instanceRoot, override } = {}) {
|
|
96
|
+
return (
|
|
97
|
+
sanitizeDbName(override) ||
|
|
98
|
+
sanitizeDbName(brandingDbName) ||
|
|
99
|
+
sanitizeDbName(instanceRoot && path.basename(instanceRoot)) ||
|
|
100
|
+
'cloudbongos_dev'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Resolve every parameter a local run needs, from branding + modules + env + argv.
|
|
105
|
+
// Pure: all IO comes through injected deps (f/env/userInfo/argv). PGHOST defaults to
|
|
106
|
+
// TCP 'localhost' (NOT the Linux peer socket the pool defaults to) so a Homebrew Mac
|
|
107
|
+
// Postgres is reachable; an explicit PGHOST/PGUSER in the environment still wins.
|
|
108
|
+
function resolveRunParams({
|
|
109
|
+
instanceRoot,
|
|
110
|
+
coreRoot = CORE_ROOT,
|
|
111
|
+
f = fs,
|
|
112
|
+
env = process.env,
|
|
113
|
+
userInfo = os.userInfo,
|
|
114
|
+
argv = [],
|
|
115
|
+
} = {}) {
|
|
116
|
+
const facts = loadBrandingFacts({ instanceRoot, coreRoot, f });
|
|
117
|
+
const modules = readJsonSafe(path.join(instanceRoot, 'config', 'modules.json'), f) || {};
|
|
118
|
+
const gameEnabled = !!(modules.modules && modules.modules.game);
|
|
119
|
+
|
|
120
|
+
const portOverride = argFrom(argv, '--port');
|
|
121
|
+
const dbOverride = argFrom(argv, '--db');
|
|
122
|
+
|
|
123
|
+
const port = portOverride ? parsePort(`http://x:${portOverride}`, parsePort(facts.publicOrigin)) : parsePort(facts.publicOrigin);
|
|
124
|
+
const dbName = deriveDbName({ brandingDbName: facts.brandingDbName, instanceRoot, override: dbOverride });
|
|
125
|
+
|
|
126
|
+
let pgUser = env.PGUSER;
|
|
127
|
+
if (!pgUser) { try { pgUser = userInfo().username; } catch { pgUser = undefined; } }
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
instanceRoot,
|
|
131
|
+
coreRoot,
|
|
132
|
+
port,
|
|
133
|
+
host: HOST,
|
|
134
|
+
dbName,
|
|
135
|
+
pgHost: env.PGHOST || 'localhost',
|
|
136
|
+
pgUser,
|
|
137
|
+
pgPassword: env.PGPASSWORD || undefined,
|
|
138
|
+
publicOrigin: facts.publicOrigin.replace(/\/+$/, ''),
|
|
139
|
+
firstAdmin: facts.firstAdmin,
|
|
140
|
+
envPrefix: facts.envPrefix,
|
|
141
|
+
configDir: facts.configDir,
|
|
142
|
+
// A local dev instance never wants OTB's instance-seed migrations (they FK-crash
|
|
143
|
+
// a non-owning DB — migrate.sh:56). Game-less instances skip them via
|
|
144
|
+
// MEDUSA_GDS_ONLY=1; when the game module IS enabled we still skip instance
|
|
145
|
+
// seeds (default-off) but do not force GDS-only, so game schema still applies.
|
|
146
|
+
gdsOnly: !gameEnabled,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// The env a child process (migrate or server) runs with. Starts from baseEnv (so
|
|
151
|
+
// the child inherits any GitHub OAuth creds already in the shell) and overlays the
|
|
152
|
+
// resolved run parameters. role='server' adds PORT/HOST; both roles set the PG*
|
|
153
|
+
// trio + the instance-root pointer + the seed policy.
|
|
154
|
+
function buildEnv(params, { baseEnv = process.env, role = 'server' } = {}) {
|
|
155
|
+
const env = { ...baseEnv };
|
|
156
|
+
env.PGHOST = params.pgHost;
|
|
157
|
+
if (params.pgUser) env.PGUSER = params.pgUser;
|
|
158
|
+
if (params.pgPassword) env.PGPASSWORD = params.pgPassword;
|
|
159
|
+
env.PGDATABASE = params.dbName;
|
|
160
|
+
if (params.gdsOnly) env.MEDUSA_GDS_ONLY = '1';
|
|
161
|
+
// Point migrate + the server at the instance explicitly, so resolution never
|
|
162
|
+
// depends on a stray cwd (migrate.sh:26 gotcha).
|
|
163
|
+
env[`${params.envPrefix}_INSTANCE_ROOT`] = params.instanceRoot;
|
|
164
|
+
if (role === 'server') {
|
|
165
|
+
env.PORT = String(params.port);
|
|
166
|
+
env.HOST = params.host;
|
|
167
|
+
}
|
|
168
|
+
return env;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Does the machine have GitHub OAuth creds this instance's server will read?
|
|
172
|
+
// Mirrors auth.js:loadGithubCreds EXACTLY (env <PREFIX>_GITHUB_CLIENT_ID/SECRET
|
|
173
|
+
// with legacy fallbacks, else ~/.config/<configDir>/gds-github.json | pms-github.json)
|
|
174
|
+
// so `bongos dev` predicts sign-in correctly instead of guessing.
|
|
175
|
+
function authWillWork({ resolveEnv = ic.resolveEnv, readConfigFileSync = ic.readConfigFileSync } = {}) {
|
|
176
|
+
if (resolveEnv('GITHUB_CLIENT_ID') && resolveEnv('GITHUB_CLIENT_SECRET')) return true;
|
|
177
|
+
for (const name of ['gds-github.json', 'pms-github.json']) {
|
|
178
|
+
try {
|
|
179
|
+
const raw = readConfigFileSync(name);
|
|
180
|
+
if (raw) {
|
|
181
|
+
const c = JSON.parse(raw);
|
|
182
|
+
if (c && c.client_id && c.client_secret) return true;
|
|
183
|
+
}
|
|
184
|
+
} catch { /* try next */ }
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- managed background server (the local analogue of local-preview-lib) --------
|
|
190
|
+
// Keyed per (dbName, port) so two instances on one machine never collide on pid/log.
|
|
191
|
+
|
|
192
|
+
function slug(params) { return `${params.dbName}-${params.port}`.replace(/[^a-z0-9_.-]+/gi, '_'); }
|
|
193
|
+
function pidPath(params, deps = {}) {
|
|
194
|
+
return deps.pidPath || path.join((deps.tmpdir || os.tmpdir)(), `bongos-dev-${slug(params)}.pid`);
|
|
195
|
+
}
|
|
196
|
+
function logPath(params, deps = {}) {
|
|
197
|
+
return deps.logPath || path.join((deps.tmpdir || os.tmpdir)(), `bongos-dev-${slug(params)}.log`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function readPid(params, deps = {}) {
|
|
201
|
+
const f = deps.fs || fs;
|
|
202
|
+
try {
|
|
203
|
+
const pid = Number(String(f.readFileSync(pidPath(params, deps), 'utf8')).trim());
|
|
204
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
205
|
+
} catch { return null; }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// signal-0 existence probe: EPERM = alive but unsignalable, ESRCH = gone.
|
|
209
|
+
function pidAlive(pid, deps = {}) {
|
|
210
|
+
if (!pid) return false;
|
|
211
|
+
const kill = deps.kill || process.kill;
|
|
212
|
+
try { kill(pid, 0); return true; } catch (e) { return e.code === 'EPERM'; }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isRunning(params, deps = {}) { return pidAlive(readPid(params, deps), deps); }
|
|
216
|
+
|
|
217
|
+
function serverEntry(params) { return path.join(params.coreRoot, 'src', 'platform-server.js'); }
|
|
218
|
+
|
|
219
|
+
// Start the platform server DETACHED (background), idempotent. Returns
|
|
220
|
+
// { ok, already, pid, url, log }. The parent polls health, prints the managed-server
|
|
221
|
+
// notice + the `bongos dev stop` command, and exits — the server keeps running.
|
|
222
|
+
function startDetached(params, deps = {}) {
|
|
223
|
+
const f = deps.fs || fs;
|
|
224
|
+
const spawnImpl = deps.spawn || spawn;
|
|
225
|
+
if (isRunning(params, deps)) {
|
|
226
|
+
return { ok: true, already: true, pid: readPid(params, deps), url: params.publicOrigin, log: logPath(params, deps) };
|
|
227
|
+
}
|
|
228
|
+
const lp = logPath(params, deps);
|
|
229
|
+
let out = 'ignore';
|
|
230
|
+
try { out = f.openSync(lp, 'w'); } catch { /* fall back to ignore */ }
|
|
231
|
+
const child = spawnImpl(deps.nodePath || process.execPath, [serverEntry(params)], {
|
|
232
|
+
cwd: params.instanceRoot,
|
|
233
|
+
env: buildEnv(params, { baseEnv: deps.env || process.env, role: 'server' }),
|
|
234
|
+
detached: true,
|
|
235
|
+
stdio: ['ignore', out, out],
|
|
236
|
+
windowsHide: true,
|
|
237
|
+
});
|
|
238
|
+
child.unref();
|
|
239
|
+
if (child.pid) f.writeFileSync(pidPath(params, deps), String(child.pid) + '\n');
|
|
240
|
+
return { ok: true, already: false, pid: child.pid, url: params.publicOrigin, log: lp };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stop(params, deps = {}) {
|
|
244
|
+
const f = deps.fs || fs;
|
|
245
|
+
const kill = deps.kill || process.kill;
|
|
246
|
+
const pid = readPid(params, deps);
|
|
247
|
+
if (!pid || !pidAlive(pid, deps)) {
|
|
248
|
+
try { f.rmSync(pidPath(params, deps), { force: true }); } catch { /* best-effort */ }
|
|
249
|
+
return { ok: true, wasRunning: false };
|
|
250
|
+
}
|
|
251
|
+
try { kill(pid); } catch (e) { return { ok: false, reason: e.message, pid }; }
|
|
252
|
+
try { f.rmSync(pidPath(params, deps), { force: true }); } catch { /* best-effort */ }
|
|
253
|
+
return { ok: true, wasRunning: true, pid };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Probe the running server. Returns { up, authConfigured }. `up` = the plain
|
|
257
|
+
// /healthz (needs no DB); authConfigured comes from /api/gds/healthz.
|
|
258
|
+
async function health(params, deps = {}) {
|
|
259
|
+
const fetchImpl = deps.fetchImpl || fetch;
|
|
260
|
+
const base = `http://${HOST}:${params.port}`;
|
|
261
|
+
const timeout = deps.healthTimeoutMs || 4000;
|
|
262
|
+
let up = false;
|
|
263
|
+
try {
|
|
264
|
+
const r = await fetchImpl(`${base}/healthz`, { signal: AbortSignal.timeout(timeout) });
|
|
265
|
+
up = r.status < 500;
|
|
266
|
+
} catch { return { up: false, authConfigured: false }; }
|
|
267
|
+
let authConfigured = false;
|
|
268
|
+
try {
|
|
269
|
+
const r = await fetchImpl(`${base}/api/gds/healthz`, { signal: AbortSignal.timeout(timeout) });
|
|
270
|
+
const j = await r.json();
|
|
271
|
+
authConfigured = j && j.auth_configured === true;
|
|
272
|
+
} catch { /* leave false */ }
|
|
273
|
+
return { up, authConfigured };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// argv helper local to dev-lib (dev.js uses cli-lib's arg() for the real process
|
|
277
|
+
// argv; this variant is pure so resolveRunParams stays unit-testable). Accepts
|
|
278
|
+
// `--flag value` and `--flag=value`.
|
|
279
|
+
function argFrom(args, name) {
|
|
280
|
+
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
281
|
+
if (eq != null) return eq.slice(name.length + 1);
|
|
282
|
+
const i = args.indexOf(name);
|
|
283
|
+
return i >= 0 ? args[i + 1] : null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// depsInstallPlan (task 1002405) — decide whether `bongos dev` must install node
|
|
287
|
+
// deps before it can do anything useful, and with which command. A freshly-cloned
|
|
288
|
+
// instance has no node_modules, so migrate.sh (pg) and the server (pg/express)
|
|
289
|
+
// would crash; install FIRST — this makes `bongos dev` a true one-command bring-up
|
|
290
|
+
// even from a bare checkout (the case where a global/npx bongos, or `node
|
|
291
|
+
// <core>/scripts/gds/dev.js`, is reachable but deps are not). Prefer `npm ci` when
|
|
292
|
+
// a lockfile is present (reproducible + faster), else `npm install`. Pure:
|
|
293
|
+
// injectable fs.
|
|
294
|
+
function depsInstallPlan({ instanceRoot, f = fs } = {}) {
|
|
295
|
+
const present = f.existsSync(path.join(instanceRoot, 'node_modules'));
|
|
296
|
+
if (present) return { needed: false, cmd: null, args: [] };
|
|
297
|
+
const hasLock = f.existsSync(path.join(instanceRoot, 'package-lock.json'));
|
|
298
|
+
return hasLock
|
|
299
|
+
? { needed: true, cmd: 'ci', args: ['ci'] }
|
|
300
|
+
: { needed: true, cmd: 'install', args: ['install'] };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
module.exports = {
|
|
304
|
+
HOST, DEFAULT_PORT, CORE_ROOT,
|
|
305
|
+
readJsonSafe, loadBrandingFacts, parsePort, sanitizeDbName, deriveDbName,
|
|
306
|
+
resolveRunParams, buildEnv, authWillWork, depsInstallPlan,
|
|
307
|
+
slug, pidPath, logPath, readPid, pidAlive, isRunning, serverEntry,
|
|
308
|
+
startDetached, stop, health, argFrom,
|
|
309
|
+
};
|