@bongos/core 1.18.3 → 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 +1575 -285
- 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/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 +112 -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/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,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
//
|
|
4
|
+
// scripts/gds/dev.js — `bongos dev`: stand up THIS instance locally, in one command.
|
|
5
|
+
//
|
|
6
|
+
// The gap it closes (ADR 0149): a project the creation flow scaffolds ships a repo
|
|
7
|
+
// configured for http://localhost:<port> but no way to START it locally. `bongos
|
|
8
|
+
// onboard` provisions a CLOUD server (domain + DO/Cloudflare); Docker serves a
|
|
9
|
+
// vanilla brand on :3000. Running THIS repo's brand on its OWN port meant a manual
|
|
10
|
+
// six-part Postgres+migrate+env+server assembly with three silent mismatches. This
|
|
11
|
+
// command does the whole thing, idempotently, and hands off to sign-in.
|
|
12
|
+
//
|
|
13
|
+
// bongos dev up: Postgres -> createdb -> migrate -> server -> login
|
|
14
|
+
// bongos dev stop stop the background server this launched
|
|
15
|
+
// bongos dev status is it up? what URL? is sign-in configured?
|
|
16
|
+
//
|
|
17
|
+
// Flags: --foreground (run attached; Ctrl-C stops), --no-login (don't auto sign in),
|
|
18
|
+
// --skip-migrate, --port <n>, --db <name>.
|
|
19
|
+
//
|
|
20
|
+
// Design notes:
|
|
21
|
+
// * The server runs DETACHED but MANAGED (pid/log keyed per instance) — the fix for
|
|
22
|
+
// the orphaned-background-server confusion (a stale server on :3005 answering a
|
|
23
|
+
// new project's login). `bongos dev status`/`stop` make it visible + killable, and
|
|
24
|
+
// a re-run is a no-op if it's already up. --foreground is there for those who want
|
|
25
|
+
// Ctrl-C lifecycle.
|
|
26
|
+
// * PORT is forced to branding's port (the server ignores branding — platform-server.js:44).
|
|
27
|
+
// * PGDATABASE is set for BOTH migrate.sh and the server (fixes the pool.js/migrate.sh
|
|
28
|
+
// default split); PGHOST defaults to TCP localhost (not the Linux peer socket).
|
|
29
|
+
|
|
30
|
+
const cp = require('node:child_process');
|
|
31
|
+
const fs = require('node:fs');
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
const dev = require('./dev-lib');
|
|
34
|
+
const { arg, hasFlag, loadSessionSync } = require('./cli-lib');
|
|
35
|
+
|
|
36
|
+
// ---- small terminal helpers (ascii-first; a builder's private surface) ---------
|
|
37
|
+
const ok = (m) => console.log(` ✓ ${m}`);
|
|
38
|
+
const info = (m) => console.log(`▸ ${m}`);
|
|
39
|
+
const warn = (m) => console.log(` ⚠ ${m}`);
|
|
40
|
+
const fail = (m) => console.error(`✖ ${m}`);
|
|
41
|
+
const blank = () => console.log('');
|
|
42
|
+
|
|
43
|
+
function run(cmd, args, opts = {}) {
|
|
44
|
+
const r = cp.spawnSync(cmd, args, { encoding: 'utf8', ...opts });
|
|
45
|
+
return { status: r.status, stdout: r.stdout || '', stderr: r.stderr || '', error: r.error };
|
|
46
|
+
}
|
|
47
|
+
function has(bin) {
|
|
48
|
+
// `command -v` via the binary's own --version is unreliable across tools; probe PATH directly.
|
|
49
|
+
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
50
|
+
return run(which, [bin]).status === 0;
|
|
51
|
+
}
|
|
52
|
+
function tail(s, n = 6) {
|
|
53
|
+
return String(s || '').trimEnd().split('\n').slice(-n).join('\n');
|
|
54
|
+
}
|
|
55
|
+
// Portable synchronous pause (no subprocess, no foreground-sleep dependency).
|
|
56
|
+
function sleepSync(ms) { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); }
|
|
57
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
58
|
+
|
|
59
|
+
function usage() {
|
|
60
|
+
console.log(`bongos dev — run THIS instance locally, in one command.
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
bongos dev [--foreground] [--no-login] [--skip-migrate] [--port <n>] [--db <name>]
|
|
64
|
+
bongos dev stop Stop the local server this launched
|
|
65
|
+
bongos dev status Show whether it's running, its URL, and sign-in state
|
|
66
|
+
|
|
67
|
+
What it does (idempotent — safe to re-run):
|
|
68
|
+
1. Make sure Postgres is running (starts it via Homebrew if it isn't)
|
|
69
|
+
2. Create this instance's database if it doesn't exist
|
|
70
|
+
3. Build/refresh its tables (migrations)
|
|
71
|
+
4. Start the server on the port in config/branding.json
|
|
72
|
+
5. Sign you in to this instance (unless --no-login)
|
|
73
|
+
|
|
74
|
+
Needs Postgres + Node >= 22 installed. macOS/Linux (uses bash for migrations;
|
|
75
|
+
on Windows use WSL or the Docker path in docs/recipes/self-host.md).`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// psql/createdb take -h/-U only when we have them; libpq fills the rest.
|
|
79
|
+
function pgArgs(params) {
|
|
80
|
+
const a = ['-h', params.pgHost];
|
|
81
|
+
if (params.pgUser) a.push('-U', params.pgUser);
|
|
82
|
+
return a;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function pgReady(params) {
|
|
86
|
+
return run('pg_isready', [...pgArgs(params), '-q']).status === 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Best-effort: start a Homebrew-managed Postgres, then poll until it accepts
|
|
90
|
+
// connections. Returns true if ready, false if we couldn't bring it up.
|
|
91
|
+
function ensurePostgres(params) {
|
|
92
|
+
if (pgReady(params)) { ok('Postgres is running'); return true; }
|
|
93
|
+
info('Postgres is not running — trying to start it…');
|
|
94
|
+
if (has('brew')) {
|
|
95
|
+
const formulae = run('brew', ['list', '--formula']).stdout
|
|
96
|
+
.split(/\s+/).filter((x) => /^postgresql(@\d+)?$/.test(x))
|
|
97
|
+
.sort().reverse();
|
|
98
|
+
const target = formulae[0] || 'postgresql@16';
|
|
99
|
+
run('brew', ['services', 'start', target], { stdio: 'ignore' });
|
|
100
|
+
} else if (has('pg_ctl') && process.env.PGDATA) {
|
|
101
|
+
run('pg_ctl', ['start', '-w', '-D', process.env.PGDATA], { stdio: 'ignore' });
|
|
102
|
+
}
|
|
103
|
+
// Poll for readiness (~15s). pg_isready is cheap; pause between probes.
|
|
104
|
+
for (let i = 0; i < 15; i++) {
|
|
105
|
+
if (pgReady(params)) { ok('Postgres started'); return true; }
|
|
106
|
+
sleepSync(1000);
|
|
107
|
+
}
|
|
108
|
+
return pgReady(params);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function dbExists(params) {
|
|
112
|
+
const r = run('psql', [...pgArgs(params), '-d', 'postgres', '-tAc',
|
|
113
|
+
`SELECT 1 FROM pg_database WHERE datname='${params.dbName}'`]);
|
|
114
|
+
return r.status === 0 && r.stdout.trim() === '1';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createDb(params) {
|
|
118
|
+
if (dbExists(params)) { ok(`database "${params.dbName}" exists`); return true; }
|
|
119
|
+
const r = run('createdb', [...pgArgs(params), params.dbName]);
|
|
120
|
+
if (r.status === 0) { ok(`created database "${params.dbName}"`); return true; }
|
|
121
|
+
// A race or prior partial run may have created it; treat "already exists" as fine.
|
|
122
|
+
if (/already exists/i.test(r.stderr)) { ok(`database "${params.dbName}" exists`); return true; }
|
|
123
|
+
fail(`could not create database "${params.dbName}": ${tail(r.stderr, 2) || r.error}`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function migrate(params) {
|
|
128
|
+
const migrateSh = path.join(params.coreRoot, 'scripts', 'migrate.sh');
|
|
129
|
+
const env = dev.buildEnv(params, { role: 'migrate' });
|
|
130
|
+
const r = run('bash', [migrateSh], { cwd: params.instanceRoot, env });
|
|
131
|
+
if (r.status !== 0) {
|
|
132
|
+
fail('migrations failed:');
|
|
133
|
+
console.error(tail(r.stderr || r.stdout, 12));
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const applied = (r.stdout.match(/^apply /gm) || []).length;
|
|
137
|
+
ok(applied ? `schema migrated (${applied} migration${applied === 1 ? '' : 's'} applied)` : 'schema up to date');
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Guidance for the ONE step that can require the user: a GitHub OAuth App. Only
|
|
142
|
+
// printed when no creds are resolvable — most machines that already ran another
|
|
143
|
+
// instance carry usable creds and skip this entirely.
|
|
144
|
+
function printOAuthGuidance(params) {
|
|
145
|
+
const cb = `${params.publicOrigin}/api/gds/auth/web/callback`;
|
|
146
|
+
const credsPath = path.join(require('node:os').homedir(), '.config', params.configDir, 'gds-github.json');
|
|
147
|
+
blank();
|
|
148
|
+
warn('Sign-in is not configured yet (no GitHub OAuth credentials found).');
|
|
149
|
+
console.log(' The server will run, but `bongos login` needs a GitHub OAuth App first:');
|
|
150
|
+
console.log(' 1. github.com → Settings → Developer settings → OAuth Apps → New OAuth App');
|
|
151
|
+
console.log(` Homepage URL: ${params.publicOrigin}`);
|
|
152
|
+
console.log(` Authorization callback URL: ${cb}`);
|
|
153
|
+
console.log(' Then tick "Enable Device Flow" and Save (a separate save).');
|
|
154
|
+
console.log(' 2. Copy the Client ID; generate a client secret.');
|
|
155
|
+
console.log(` 3. Save both into ${credsPath} as:`);
|
|
156
|
+
console.log(' { "client_id": "…", "client_secret": "…" }');
|
|
157
|
+
console.log(' 4. Re-run `bongos dev`.');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function signedInToThisInstance(params) {
|
|
161
|
+
try {
|
|
162
|
+
const s = loadSessionSync();
|
|
163
|
+
if (!s || !s.token || !s.api_base) return false;
|
|
164
|
+
return new URL(s.api_base).host === new URL(params.publicOrigin).host;
|
|
165
|
+
} catch { return false; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function waitHealthy(params, ms = 20000) {
|
|
169
|
+
const start = Date.now();
|
|
170
|
+
while (Date.now() - start < ms) {
|
|
171
|
+
const h = await dev.health(params);
|
|
172
|
+
if (h.up) return h;
|
|
173
|
+
await sleep(1000);
|
|
174
|
+
}
|
|
175
|
+
return dev.health(params);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function loginNow(params) {
|
|
179
|
+
const loginJs = path.join(params.coreRoot, 'scripts', 'gds', 'login.js');
|
|
180
|
+
info(`Signing in to ${params.publicOrigin}${params.firstAdmin ? ` (sign in as ${params.firstAdmin})` : ''}…`);
|
|
181
|
+
const r = cp.spawnSync(process.execPath, [loginJs, params.publicOrigin], { stdio: 'inherit' });
|
|
182
|
+
return r.status === 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---- subcommands ---------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function cmdStop(params) {
|
|
188
|
+
const res = dev.stop(params);
|
|
189
|
+
if (res.wasRunning) ok(`stopped the local ${params.dbName} server (pid ${res.pid})`);
|
|
190
|
+
else console.log(` (no running ${params.dbName} server to stop)`);
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function cmdStatus(params) {
|
|
195
|
+
const running = dev.isRunning(params);
|
|
196
|
+
console.log(`instance: ${path.basename(params.instanceRoot)} (${params.publicOrigin})`);
|
|
197
|
+
console.log(`database: ${params.dbName} on ${params.pgHost}`);
|
|
198
|
+
if (!running) { console.log('server: not running — start it with `bongos dev`'); return 0; }
|
|
199
|
+
const h = await dev.health(params);
|
|
200
|
+
console.log(`server: running (pid ${dev.readPid(params)}), health ${h.up ? 'OK' : 'starting/unreachable'}`);
|
|
201
|
+
console.log(`sign-in: ${h.authConfigured ? 'configured' : 'NOT configured (see `bongos dev`)'}`);
|
|
202
|
+
console.log(`logs: ${dev.logPath(params)}`);
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function cmdUp(params, { foreground, noLogin, skipMigrate }) {
|
|
207
|
+
info(`Standing up ${path.basename(params.instanceRoot)} on ${params.publicOrigin}`);
|
|
208
|
+
|
|
209
|
+
// Step 0: dependencies. A freshly-cloned instance has no node_modules, so
|
|
210
|
+
// migrate.sh (pg) and the server (pg/express) would crash. Install FIRST, so
|
|
211
|
+
// `bongos dev` is a true one-command bring-up even from a bare checkout (task
|
|
212
|
+
// 1002405 — pairs with the root START-HERE that names `npm install` for the
|
|
213
|
+
// truly-bare case where not even the `bongos` bin exists yet).
|
|
214
|
+
const depsPlan = dev.depsInstallPlan({ instanceRoot: params.instanceRoot });
|
|
215
|
+
if (depsPlan.needed) {
|
|
216
|
+
if (!has('npm')) {
|
|
217
|
+
fail('node_modules is missing and `npm` is not on PATH — install Node (it bundles npm), then re-run `bongos dev`.');
|
|
218
|
+
return 1;
|
|
219
|
+
}
|
|
220
|
+
info(`installing dependencies (npm ${depsPlan.cmd}) — first run in this checkout, this can take a minute…`);
|
|
221
|
+
const r = run('npm', depsPlan.args, { cwd: params.instanceRoot, stdio: 'inherit' });
|
|
222
|
+
if (r.status !== 0) {
|
|
223
|
+
fail(`\`npm ${depsPlan.cmd}\` failed (exit ${r.status ?? '?'}). Fix the error above, then re-run \`bongos dev\`.`);
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
ok('dependencies installed');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Preflight: DB tooling + bash for migrations.
|
|
230
|
+
const missing = ['psql', 'createdb', 'pg_isready'].filter((b) => !has(b));
|
|
231
|
+
if (missing.length) {
|
|
232
|
+
fail(`Postgres client tools missing: ${missing.join(', ')}`);
|
|
233
|
+
console.error(' Install Postgres, then re-run `bongos dev`:');
|
|
234
|
+
console.error(process.platform === 'darwin'
|
|
235
|
+
? ' brew install postgresql@16 && brew services start postgresql@16'
|
|
236
|
+
: ' (Debian/Ubuntu) sudo apt-get install -y postgresql (or your distro’s package)');
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
if (!skipMigrate && !has('bash')) {
|
|
240
|
+
fail('`bash` is required to run migrations (used by scripts/migrate.sh).');
|
|
241
|
+
console.error(' On Windows, use WSL, or the Docker path in docs/recipes/self-host.md.');
|
|
242
|
+
return 1;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!ensurePostgres(params)) {
|
|
246
|
+
fail('Postgres is installed but not reachable, and I could not start it.');
|
|
247
|
+
console.error(` Start it yourself, then re-run \`bongos dev\`:`);
|
|
248
|
+
console.error(process.platform === 'darwin' ? ' brew services start postgresql@16' : ' sudo service postgresql start');
|
|
249
|
+
return 1;
|
|
250
|
+
}
|
|
251
|
+
if (!createDb(params)) return 1;
|
|
252
|
+
if (skipMigrate) warn('skipping migrations (--skip-migrate)');
|
|
253
|
+
else if (!migrate(params)) return 1;
|
|
254
|
+
|
|
255
|
+
const authReady = dev.authWillWork();
|
|
256
|
+
if (authReady) ok('sign-in configured');
|
|
257
|
+
|
|
258
|
+
// Foreground mode: run the server attached; Ctrl-C stops it. Print the login hint
|
|
259
|
+
// up front (the server owns the terminal once it starts).
|
|
260
|
+
if (foreground) {
|
|
261
|
+
blank();
|
|
262
|
+
info(`Starting ${params.dbName} on ${params.publicOrigin} (Ctrl-C to stop)`);
|
|
263
|
+
if (authReady && !signedInToThisInstance(params)) {
|
|
264
|
+
console.log(' Once it says "listening", open a SECOND terminal and run:');
|
|
265
|
+
console.log(` bongos login ${params.publicOrigin}${params.firstAdmin ? ` (sign in as ${params.firstAdmin})` : ''}`);
|
|
266
|
+
} else if (!authReady) printOAuthGuidance(params);
|
|
267
|
+
blank();
|
|
268
|
+
const env = dev.buildEnv(params, { role: 'server' });
|
|
269
|
+
const r = cp.spawnSync(process.execPath, [dev.serverEntry(params)], { cwd: params.instanceRoot, env, stdio: 'inherit' });
|
|
270
|
+
return r.status == null ? 1 : r.status;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Default: managed background server.
|
|
274
|
+
const started = dev.startDetached(params);
|
|
275
|
+
if (started.already) ok(`server already running (pid ${started.pid})`);
|
|
276
|
+
const h = await waitHealthy(params);
|
|
277
|
+
if (!h.up) {
|
|
278
|
+
fail('the server did not become healthy in time. Recent log:');
|
|
279
|
+
try { console.error(tail(fs.readFileSync(started.log, 'utf8'), 12)); } catch { /* no log */ }
|
|
280
|
+
dev.stop(params); // don't leave a broken detached server behind
|
|
281
|
+
return 1;
|
|
282
|
+
}
|
|
283
|
+
if (!started.already) ok(`server running on ${params.publicOrigin} (pid ${started.pid})`);
|
|
284
|
+
blank();
|
|
285
|
+
console.log(` → ${params.publicOrigin}`);
|
|
286
|
+
console.log(` status: bongos dev status logs: ${started.log}`);
|
|
287
|
+
console.log(` stop: bongos dev stop`);
|
|
288
|
+
blank();
|
|
289
|
+
|
|
290
|
+
if (!authReady) { printOAuthGuidance(params); return 0; }
|
|
291
|
+
if (noLogin) { console.log(' Sign in when ready: bongos login ' + params.publicOrigin); return 0; }
|
|
292
|
+
if (signedInToThisInstance(params)) { ok('already signed in to this instance'); return 0; }
|
|
293
|
+
const signedIn = loginNow(params);
|
|
294
|
+
blank();
|
|
295
|
+
if (signedIn) ok('all set — you’re signed in. Try: bongos start');
|
|
296
|
+
else console.log(' Sign-in didn’t finish — re-run: bongos login ' + params.publicOrigin);
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function main() {
|
|
301
|
+
const argv = process.argv.slice(2);
|
|
302
|
+
if (hasFlag('--help') || hasFlag('-h') || argv[0] === 'help') { usage(); return 0; }
|
|
303
|
+
|
|
304
|
+
const params = dev.resolveRunParams({ instanceRoot: process.cwd(), argv });
|
|
305
|
+
const sub = argv[0] && !argv[0].startsWith('-') ? argv[0] : 'up';
|
|
306
|
+
|
|
307
|
+
if (sub === 'stop') return cmdStop(params);
|
|
308
|
+
if (sub === 'status') return cmdStatus(params);
|
|
309
|
+
if (sub !== 'up') { fail(`unknown subcommand "${sub}"`); usage(); return 2; }
|
|
310
|
+
|
|
311
|
+
return cmdUp(params, {
|
|
312
|
+
foreground: hasFlag('--foreground'),
|
|
313
|
+
noLogin: hasFlag('--no-login'),
|
|
314
|
+
skipMigrate: hasFlag('--skip-migrate'),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (require.main === module) {
|
|
319
|
+
main().then((code) => process.exit(code || 0)).catch((e) => { fail(e && e.message ? e.message : String(e)); process.exit(1); });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
module.exports = { usage, pgArgs };
|
package/scripts/gds/doctor.js
CHANGED
|
@@ -28,6 +28,7 @@ const path = require('node:path');
|
|
|
28
28
|
const os = require('node:os');
|
|
29
29
|
const { execSync, spawnSync } = require('node:child_process');
|
|
30
30
|
const { cliClient, SESSION_PATH, loadSession } = require('./cli-lib');
|
|
31
|
+
const { preflightChecks } = require('./preflight');
|
|
31
32
|
|
|
32
33
|
// ANSI helpers (only if stdout is a TTY; otherwise emit plain text)
|
|
33
34
|
const TTY = process.stdout.isTTY;
|
|
@@ -130,77 +131,101 @@ async function checkSession() {
|
|
|
130
131
|
function checkToolchain() {
|
|
131
132
|
section('Toolchain');
|
|
132
133
|
|
|
133
|
-
// Node
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
// Node + deps + `claude` come from the shared onboarding preflight (preflight.js)
|
|
135
|
+
// — the single source of truth also run up front at /builder-setup (task 1002401),
|
|
136
|
+
// so doctor and setup can never drift. (This also gives doctor the node_modules
|
|
137
|
+
// check it previously lacked.)
|
|
138
|
+
const results = preflightChecks();
|
|
139
|
+
for (const r of results) {
|
|
140
|
+
if (r.level === 'ok') {
|
|
141
|
+
ok('toolchain', `${r.title}: ${r.detail}`);
|
|
142
|
+
} else {
|
|
143
|
+
const emit = r.level === 'warn' ? warn : fail;
|
|
144
|
+
emit('toolchain', `${r.title}: ${r.detail}`);
|
|
145
|
+
if (r.fix) emit('toolchain', `Fix: ${r.fix}`);
|
|
146
|
+
}
|
|
139
147
|
}
|
|
140
148
|
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
windowsHide: true,
|
|
154
|
-
timeout: 15000,
|
|
155
|
-
});
|
|
156
|
-
} catch (err) {
|
|
157
|
-
r = { error: err };
|
|
158
|
-
}
|
|
159
|
-
if (r && !r.error && r.status === 0) {
|
|
160
|
-
const ver = (r.stdout || '').trim().split('\n')[0] || 'present';
|
|
161
|
-
ok('toolchain', `claude CLI launches${fromEnv}: ${ver} — the grader can spawn its workers.`);
|
|
162
|
-
// Windows-only: warn if the resolved binary is the desktop "App execution
|
|
163
|
-
// alias" stub. It can pass --version yet misbehave under the grader's
|
|
164
|
-
// stdio redirection (SUBAGENT_NONZERO_EXIT). Learning #42.
|
|
165
|
-
if (process.platform === 'win32' && !process.env.OTB_CLAUDE_BIN && !process.env.CLAUDE_BIN) {
|
|
166
|
-
try {
|
|
167
|
-
const where = spawnSync('where', ['claude'], { encoding: 'utf8', shell: true, timeout: 8000 });
|
|
168
|
-
if (where.status === 0 && /WindowsApps/i.test(where.stdout || '')) {
|
|
169
|
-
warn('toolchain', `claude resolves to the WindowsApps desktop alias, which can fail headless grading (SUBAGENT_NONZERO_EXIT). Prefer a real CLI: npm i -g @anthropic-ai/claude-code (learning #42).`);
|
|
170
|
-
}
|
|
171
|
-
} catch (_) { /* best-effort */ }
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
const why = r && r.error ? (r.error.code || r.error.message) : `exited ${r ? r.status : '?'}`;
|
|
175
|
-
fail('toolchain', `claude CLI not launchable (${bin}: ${why}). The grader spawns it per worker — without it, ships fail grading with SUBAGENT_SPAWN_FAILED.`);
|
|
176
|
-
fail('toolchain', `Fix: npm i -g @anthropic-ai/claude-code (puts a real claude on PATH). If it lives elsewhere, set OTB_CLAUDE_BIN to its full path and re-run.`);
|
|
149
|
+
// Windows-only extra (beyond the shared preflight): even when `claude` launches,
|
|
150
|
+
// warn if it resolves to the WindowsApps desktop "App execution alias" stub — it
|
|
151
|
+
// can pass --version yet misbehave under the grader's stdio redirection
|
|
152
|
+
// (SUBAGENT_NONZERO_EXIT). Learning #42.
|
|
153
|
+
const claudeOk = results.some((r) => r.title === 'claude CLI' && r.level === 'ok');
|
|
154
|
+
if (claudeOk && process.platform === 'win32' && !process.env.OTB_CLAUDE_BIN && !process.env.CLAUDE_BIN) {
|
|
155
|
+
try {
|
|
156
|
+
const where = spawnSync('where', ['claude'], { encoding: 'utf8', shell: true, timeout: 8000 });
|
|
157
|
+
if (where.status === 0 && /WindowsApps/i.test(where.stdout || '')) {
|
|
158
|
+
warn('toolchain', `claude resolves to the WindowsApps desktop alias, which can fail headless grading (SUBAGENT_NONZERO_EXIT). Prefer a real CLI: npm i -g @anthropic-ai/claude-code (learning #42).`);
|
|
159
|
+
}
|
|
160
|
+
} catch (_) { /* best-effort */ }
|
|
177
161
|
}
|
|
178
162
|
}
|
|
179
163
|
|
|
180
164
|
// --------------------------------------------------------------------------
|
|
181
165
|
// Check 3 — Git plumbing
|
|
182
166
|
// --------------------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
167
|
+
// Pure verdict for the hooks gate, extracted so the truth table is testable
|
|
168
|
+
// (tests/doctor_hooks.mjs) — the box-auth-check `decideBoxAuth` precedent.
|
|
169
|
+
//
|
|
170
|
+
// `.husky/` is INSTANCE topology, not shipped in the portable core (README
|
|
171
|
+
// "Secrets scanning"). So its absence is not a builder error: it means ADR 0043
|
|
172
|
+
// layer 1 has nothing to activate here, and CI + the post-push main audit are
|
|
173
|
+
// the enforcing layers. Recommending `.husky` regardless is what produced the
|
|
174
|
+
// unbreakable red loop this replaces — doctor demanded a dir that wasn't there,
|
|
175
|
+
// then failed on the dangling path it had just told you to set.
|
|
176
|
+
function classifyHooksState({ hooksPath, worktreeRoot, exists = fs.existsSync }) {
|
|
177
|
+
const huskyDir = worktreeRoot ? path.join(worktreeRoot, '.husky') : '.husky';
|
|
178
|
+
const huskyPresent = exists(huskyDir);
|
|
179
|
+
const installer = 'node scripts/gds/install-git-hooks.js';
|
|
180
|
+
|
|
186
181
|
if (!hooksPath) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
182
|
+
if (!huskyPresent) {
|
|
183
|
+
return {
|
|
184
|
+
status: 'warn',
|
|
185
|
+
code: 'no-hooks-dir',
|
|
186
|
+
resolved: null,
|
|
187
|
+
message:
|
|
188
|
+
'No hooks dir in this checkout (.husky/ is instance topology, not shipped in the portable core), '
|
|
189
|
+
+ 'so the local pre-commit/pre-push gates are inactive. CI secrets-scan and the post-push main audit still enforce.',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
status: 'fail',
|
|
194
|
+
code: 'inactive',
|
|
195
|
+
resolved: null,
|
|
196
|
+
message: `.husky/ is present but core.hooksPath is unset — the secrets + permission gates are not running. Activate with: ${installer}`,
|
|
197
|
+
};
|
|
196
198
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
|
|
200
|
+
// Relative hooksPath resolves against the worktree root (git's behavior);
|
|
201
|
+
// absolute resolves as-is.
|
|
202
|
+
const resolved = path.isAbsolute(hooksPath) || !worktreeRoot
|
|
203
|
+
? hooksPath
|
|
204
|
+
: path.join(worktreeRoot, hooksPath);
|
|
205
|
+
|
|
206
|
+
if (!exists(resolved)) {
|
|
207
|
+
return {
|
|
208
|
+
status: 'fail',
|
|
209
|
+
code: 'dangling',
|
|
210
|
+
resolved,
|
|
211
|
+
message: huskyPresent
|
|
212
|
+
? `core.hooksPath="${hooksPath}" resolves to a non-existent path: ${resolved}. Repoint it with: ${installer}`
|
|
213
|
+
: `core.hooksPath="${hooksPath}" resolves to a non-existent path: ${resolved}, and this checkout has no .husky/ to point at. Clear it with: git config --unset core.hooksPath`,
|
|
214
|
+
};
|
|
201
215
|
}
|
|
202
|
-
ok
|
|
203
|
-
|
|
216
|
+
return { status: 'ok', code: 'active', resolved, message: `core.hooksPath="${hooksPath}" → ${resolved} (exists)` };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function checkGitPlumbing() {
|
|
220
|
+
section('Git plumbing');
|
|
221
|
+
const verdict = classifyHooksState({
|
|
222
|
+
hooksPath: gitOut('git config --get core.hooksPath'),
|
|
223
|
+
worktreeRoot: gitOut('git rev-parse --show-toplevel'),
|
|
224
|
+
});
|
|
225
|
+
const report = { ok, warn, fail }[verdict.status];
|
|
226
|
+
report('git', verdict.message);
|
|
227
|
+
if (verdict.status !== 'ok') return;
|
|
228
|
+
const preCommit = path.join(verdict.resolved, 'pre-commit');
|
|
204
229
|
if (!fs.existsSync(preCommit)) {
|
|
205
230
|
warn('git', `No pre-commit file at ${preCommit} — secrets-guard hook will not fire.`);
|
|
206
231
|
} else {
|
|
@@ -353,7 +378,11 @@ function checkDeployTooling() {
|
|
|
353
378
|
// --------------------------------------------------------------------------
|
|
354
379
|
// main
|
|
355
380
|
// --------------------------------------------------------------------------
|
|
356
|
-
|
|
381
|
+
// Guarded so requiring this file for its pure helpers (tests/doctor_hooks.mjs)
|
|
382
|
+
// does not run the checks, which shell out to git and hit the network.
|
|
383
|
+
module.exports = { classifyHooksState };
|
|
384
|
+
|
|
385
|
+
if (require.main === module) (async () => {
|
|
357
386
|
console.log(`${C.bold}GDS Doctor${C.reset} — local-machine health check`);
|
|
358
387
|
await checkSession();
|
|
359
388
|
checkToolchain();
|
|
@@ -46,7 +46,7 @@ async function main() {
|
|
|
46
46
|
const res = await fetch(`${base}/api/gds/me/art-key`, {
|
|
47
47
|
headers: {
|
|
48
48
|
Authorization: `Bearer ${session.token}`,
|
|
49
|
-
'User-Agent': '
|
|
49
|
+
'User-Agent': require('../../src/branding').userAgent(),
|
|
50
50
|
},
|
|
51
51
|
signal: ctrl.signal,
|
|
52
52
|
});
|
|
@@ -49,11 +49,11 @@ async function main() {
|
|
|
49
49
|
// existing cached state (fail-open).
|
|
50
50
|
const [soundRes, eventRes] = await Promise.all([
|
|
51
51
|
fetch(`${sess.apiBase}/api/gds/me/sound-prefs`, {
|
|
52
|
-
headers: { Authorization: `Bearer ${sess.token}`, 'User-Agent': '
|
|
52
|
+
headers: { Authorization: `Bearer ${sess.token}`, 'User-Agent': require('../../src/branding').userAgent() },
|
|
53
53
|
signal: ctrl.signal,
|
|
54
54
|
}),
|
|
55
55
|
fetch(`${sess.apiBase}/api/gds/me/event-sound-prefs`, {
|
|
56
|
-
headers: { Authorization: `Bearer ${sess.token}`, 'User-Agent': '
|
|
56
|
+
headers: { Authorization: `Bearer ${sess.token}`, 'User-Agent': require('../../src/branding').userAgent() },
|
|
57
57
|
signal: ctrl.signal,
|
|
58
58
|
}),
|
|
59
59
|
]);
|