@bongos/core 1.17.0
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 +5727 -0
- package/.claude/skills/blocker-review/SKILL.md +91 -0
- package/.claude/skills/blocker-solve/SKILL.md +96 -0
- package/.claude/skills/builder-backup/SKILL.md +54 -0
- package/.claude/skills/builder-box/SKILL.md +45 -0
- package/.claude/skills/builder-claim/SKILL.md +76 -0
- package/.claude/skills/builder-connect/SKILL.md +59 -0
- package/.claude/skills/builder-cost/SKILL.md +47 -0
- package/.claude/skills/builder-end/SKILL.md +81 -0
- package/.claude/skills/builder-exit/SKILL.md +88 -0
- package/.claude/skills/builder-key/SKILL.md +40 -0
- package/.claude/skills/builder-reauth/SKILL.md +50 -0
- package/.claude/skills/builder-redteam/SKILL.md +109 -0
- package/.claude/skills/builder-release/SKILL.md +41 -0
- package/.claude/skills/builder-setup/SKILL.md +62 -0
- package/.claude/skills/builder-ship/SKILL.md +79 -0
- package/.claude/skills/builder-stage/SKILL.md +51 -0
- package/.claude/skills/builder-start/SKILL.md +46 -0
- package/.claude/skills/close-box/SKILL.md +43 -0
- package/.claude/skills/design-sync/SKILL.md +69 -0
- package/.claude/skills/dev/SKILL.md +49 -0
- package/.claude/skills/feedback/SKILL.md +32 -0
- package/.claude/skills/figma-design-sync/SKILL.md +74 -0
- package/.claude/skills/goal-review/SKILL.md +89 -0
- package/.claude/skills/idea-triage/SKILL.md +144 -0
- package/.claude/skills/ideate/SKILL.md +82 -0
- package/.claude/skills/merge-mode/SKILL.md +169 -0
- package/.claude/skills/new-project/SKILL.md +108 -0
- package/.claude/skills/otb-character-review/SKILL.md +71 -0
- package/.claude/skills/otb-design-review/SKILL.md +47 -0
- package/.claude/skills/otb-feedback-capture/SKILL.md +45 -0
- package/.claude/skills/otb-figma-sync/SKILL.md +54 -0
- package/.claude/skills/otb-tile-generate/SKILL.md +45 -0
- package/.claude/skills/paint/SKILL.md +70 -0
- package/.claude/skills/planning-session/SKILL.md +299 -0
- package/.claude/skills/priority-session/SKILL.md +134 -0
- package/.claude/skills/read-session-export/SKILL.md +58 -0
- package/.claude/skills/recall/SKILL.md +52 -0
- package/.claude/skills/session-handoff/SKILL.md +46 -0
- package/.claude/skills/status/SKILL.md +33 -0
- package/.devcontainer/Dockerfile +69 -0
- package/.devcontainer/README.md +73 -0
- package/.devcontainer/devcontainer-lock.json +19 -0
- package/.devcontainer/devcontainer.json +93 -0
- package/.devcontainer/healthcheck.sh +84 -0
- package/.devcontainer/init-firewall.sh +198 -0
- package/.devcontainer/post-create.sh +153 -0
- package/.dockerignore +36 -0
- package/.env.local.example +32 -0
- package/.gitattributes +83 -0
- package/.gitignore +91 -0
- package/CLAUDE.md +242 -0
- package/CODE_OF_CONDUCT.md +46 -0
- package/CONTRIBUTING.md +51 -0
- package/Dockerfile +46 -0
- package/GOVERNANCE.md +44 -0
- package/LICENSE +661 -0
- package/README.md +88 -0
- package/bin/bongos.js +222 -0
- package/clients/bongos-client/README.md +48 -0
- package/clients/bongos-client/bongos-client.global.js +617 -0
- package/clients/bongos-client/examples/hello-world.mjs +62 -0
- package/clients/bongos-client/index.cjs +619 -0
- package/clients/bongos-client/index.d.ts +793 -0
- package/clients/bongos-client/index.mjs +614 -0
- package/clients/bongos-client/package.json +32 -0
- package/config/branding.neutral.json +60 -0
- package/config/design-tokens.neutral.json +51 -0
- package/config/design-tokens.schema.json +96 -0
- package/config/modules.neutral.json +9 -0
- package/docker-compose.yml +52 -0
- package/docker-entrypoint.sh +30 -0
- package/docs/adr/0001-stack-choice-single-vps.md +45 -0
- package/docs/adr/0002-digitalocean-over-hetzner.md +27 -0
- package/docs/adr/0003-cloudflare-origin-cert-over-letsencrypt.md +35 -0
- package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +45 -0
- package/docs/adr/0005-invisible-other-players-in-v1.md +37 -0
- package/docs/adr/0006-singleton-worldroom-plus-queueroom.md +30 -0
- package/docs/adr/0007-persistent-identity-via-localstorage-uuid.md +31 -0
- package/docs/adr/0008-family-based-tile-generation.md +86 -0
- package/docs/adr/0008-google-chat-oauth-user-auth.md +37 -0
- package/docs/adr/0009-autotile-architecture.md +181 -0
- package/docs/adr/0010-pms-architecture.md +70 -0
- package/docs/adr/0011-terrain-id-rendering-model.md +174 -0
- package/docs/adr/0012-ground-object-cell-split.md +208 -0
- package/docs/adr/0013-paths-as-object-overlays-with-autotile-dispatch.md +158 -0
- package/docs/adr/0014-families-canonical-over-vocabulary.md +103 -0
- package/docs/adr/0015-task-dependencies-and-auto-promotion.md +125 -0
- package/docs/adr/0016-trust-boundary-server-enforced-permissions.md +132 -0
- package/docs/adr/0017-code-simplification-and-repo-cleanliness.md +74 -0
- package/docs/adr/0018-three-rank-model-goes-live.md +97 -0
- package/docs/adr/0019-red-team-bounty-payout-table.md +52 -0
- package/docs/adr/0020-security-model-public-stub.md +70 -0
- package/docs/adr/0021-per-builder-skill-model-preferences.md +99 -0
- package/docs/adr/0022-secrets-policy.md +124 -0
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +118 -0
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +277 -0
- package/docs/adr/0024-multi-agent-system-architecture.md +125 -0
- package/docs/adr/0025-offsite-backup-vendor-digitalocean-spaces.md +59 -0
- package/docs/adr/0025-structured-criterion-task-link.md +44 -0
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +117 -0
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +170 -0
- package/docs/adr/0028-honest-llm-spend-accounting.md +109 -0
- package/docs/adr/0029-status-page-outage-resilience.md +59 -0
- package/docs/adr/0030-builders-hall-live-updates.md +59 -0
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +222 -0
- package/docs/adr/0032-discord-mirror-of-gds.md +50 -0
- package/docs/adr/0033-discord-bot-service-principal.md +82 -0
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +53 -0
- package/docs/adr/0035-builder-onboarding-three-paths.md +128 -0
- package/docs/adr/0036-builders-subdomain-and-ui-reorg.md +40 -0
- package/docs/adr/0037-discord-channel-management.md +51 -0
- package/docs/adr/0038-chromebook-ttyd-cloudflare-tunnel.md +111 -0
- package/docs/adr/0039-setup-first-onboarding-ux.md +49 -0
- package/docs/adr/0040-remote-control-default-browser-onramp.md +47 -0
- package/docs/adr/0041-temporary-grader-bypass-killswitch.md +40 -0
- package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +82 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +74 -0
- package/docs/adr/0044-mediterranean-palette-replacement.md +73 -0
- package/docs/adr/0044-per-box-live-game-preview.md +57 -0
- package/docs/adr/0045-devbox-desktop-app.md +133 -0
- package/docs/adr/0046-sandbox-first-review-gate.md +72 -0
- package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +36 -0
- package/docs/adr/0048-works-in-progress-ia-split.md +32 -0
- package/docs/adr/0049-split-parallel-safety-contract.md +46 -0
- package/docs/adr/0050-device-flow-admission-invite-gated-by-default.md +43 -0
- package/docs/adr/0051-full-session-transcript-corpus.md +57 -0
- package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +70 -0
- package/docs/adr/0053-scoped-dev-box-session.md +55 -0
- package/docs/adr/0054-session-token-reward-cost-plus.md +63 -0
- package/docs/adr/0055-server-mediated-branch-publish.md +47 -0
- package/docs/adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md +62 -0
- package/docs/adr/0057-container-cost-ledger.md +28 -0
- package/docs/adr/0057-discord-archon-approval-channels.md +64 -0
- package/docs/adr/0058-automatic-gate-surface-gatekeeper.md +64 -0
- package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +37 -0
- package/docs/adr/0060-gds-retrieval-layer.md +97 -0
- package/docs/adr/0061-context-layer-decomposition.md +61 -0
- package/docs/adr/0062-medusa-instance-model-decoupling.md +107 -0
- package/docs/adr/0063-generated-repo-map-symbol-skeleton.md +44 -0
- package/docs/adr/0064-rename-medusa-to-cloud-bongos.md +49 -0
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +69 -0
- package/docs/adr/0066-generated-file-map-sections.md +51 -0
- package/docs/adr/0067-session-start-fast-forward-pull.md +43 -0
- package/docs/adr/0068-shared-art-key-for-newcomers.md +38 -0
- package/docs/adr/0069-hall-live-channel-sse-decouple.md +35 -0
- package/docs/adr/0070-deterministic-session-card-delivery.md +44 -0
- package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +59 -0
- package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +35 -0
- package/docs/adr/0072-dev-box-code-staleness-visibility.md +39 -0
- package/docs/adr/0073-builder-needs-signal-and-byok-gemini-key.md +64 -0
- package/docs/adr/0073-secrets-scan-exclude-uri-detector.md +37 -0
- package/docs/adr/0074-memory-map-stays-light.md +32 -0
- package/docs/adr/0075-per-system-cost-attribution.md +37 -0
- package/docs/adr/0076-in-session-deterministic-compaction.md +58 -0
- package/docs/adr/0077-content-addressed-llm-cache.md +32 -0
- package/docs/adr/0078-deterministic-first-cron.md +48 -0
- package/docs/adr/0079-value-gated-autonomous-engagement.md +39 -0
- package/docs/adr/0080-llm-cache-api-transport-and-trust-model.md +46 -0
- package/docs/adr/0081-tool-agnostic-design-layer.md +62 -0
- package/docs/adr/0082-productized-key-storage-provisioning.md +32 -0
- package/docs/adr/0082-server-side-merge-conflict-auto-resolution.md +55 -0
- package/docs/adr/0083-modular-architecture-strangler-build-plan.md +143 -0
- package/docs/adr/0084-auto-derived-per-task-rank-floor.md +86 -0
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +39 -0
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +123 -0
- package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +49 -0
- package/docs/adr/0087-compete-on-governance-not-tooling-cursor.md +65 -0
- package/docs/adr/0088-consistent-character-generative-animation.md +169 -0
- package/docs/adr/0089-modular-multi-model-adversarial-grader.md +179 -0
- package/docs/adr/0090-metic-task-authoring.md +73 -0
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +106 -0
- package/docs/adr/0092-hosting-module-and-on-prem-target.md +117 -0
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +72 -0
- package/docs/adr/0094-repo-atlas-self-drawing-codebase-map.md +49 -0
- package/docs/adr/0095-borrowed-memory-and-retrieval-concepts.md +65 -0
- package/docs/adr/0095-cross-agent-context-management.md +58 -0
- package/docs/adr/0096-require-reward-before-workable.md +209 -0
- package/docs/adr/0097-one-active-claim-per-session-and-worktree-binding.md +46 -0
- package/docs/adr/0097-retroactive-reward-backfill.md +124 -0
- package/docs/adr/0098-public-mirror-publish-manifest.md +92 -0
- package/docs/adr/0099-delayed-redacted-mirror-export.md +55 -0
- package/docs/adr/0100-two-repo-instance-model.md +60 -0
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +90 -0
- package/docs/adr/0102-finish-bounding-the-kernel.md +36 -0
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +70 -0
- package/docs/adr/0103-gdsv4-bongos-consolidation.md +28 -0
- package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +42 -0
- package/docs/adr/0105-instance-seed-migrations-out-of-core.md +47 -0
- package/docs/adr/0106-goal-owner-manager-authority.md +84 -0
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +83 -0
- package/docs/adr/0108-instance-composition-configurable-root.md +87 -0
- package/docs/adr/0109-self-describing-openapi-and-hosted-docs.md +44 -0
- package/docs/adr/0110-untrack-whole-file-generated-nav-docs.md +49 -0
- package/docs/adr/0111-instance-hosting-provisioning-module.md +135 -0
- package/docs/adr/0112-private-goals-invitations-join-requests.md +72 -0
- package/docs/adr/0113-sandbox-studio-descope-superseded-by-0044-0052.md +41 -0
- package/docs/adr/0114-instance-docs-discovery-contract.md +32 -0
- package/docs/adr/0115-scheduled-routines-portability.md +31 -0
- package/docs/adr/0116-api-error-envelope-and-code-taxonomy.md +62 -0
- package/docs/adr/0117-api-versioning-and-deprecation-policy.md +50 -0
- package/docs/adr/0118-api-contract-source-of-truth-code-first.md +47 -0
- package/docs/adr/0119-api-pagination-contract.md +34 -0
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +108 -0
- package/docs/adr/0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md +97 -0
- package/docs/adr/0122-onboarding-diagram-renders-deploy-rendered-off-git.md +43 -0
- package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +36 -0
- package/docs/adr/0124-typed-api-response-bodies.md +37 -0
- package/docs/adr/0125-control-plane-scaffolds-standalone-instance-repo.md +36 -0
- package/docs/adr/0126-dedicated-cloudbongos-control-plane-droplet.md +30 -0
- package/docs/adr/0127-cross-box-fleet-observability.md +64 -0
- package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +46 -0
- package/docs/adr/0129-cloudbongos-control-plane-pull-deploy.md +34 -0
- package/docs/adr/0130-provision-remote-exec-over-ssh.md +61 -0
- package/docs/adr/0131-rank-scoped-skill-visibility.md +47 -0
- package/docs/adr/0132-cohost-fleet-control-plane-migration.md +54 -0
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +42 -0
- package/docs/adr/0134-private-first-npm-distribution.md +38 -0
- package/docs/adr/README.md +208 -0
- package/docs/api/openapi.json +13720 -0
- package/docs/api-reference.md +437 -0
- package/docs/architecture.md +244 -0
- package/docs/branding-contract.md +66 -0
- package/docs/branding-fill-prompt.md +25 -0
- package/docs/canonical-permissions.md +241 -0
- package/docs/design/container-exterior-tiles.md +49 -0
- package/docs/design/container-exterior.md +81 -0
- package/docs/design/container-interior.md +77 -0
- package/docs/design/modular-architecture/00-research-report.md +107 -0
- package/docs/design/modular-architecture/01-architecture.md +125 -0
- package/docs/design/modular-architecture/02-module-map.md +116 -0
- package/docs/design/modular-architecture/03-builder-flows.md +104 -0
- package/docs/design/modular-architecture/04-module-lifecycle.md +62 -0
- package/docs/design/modular-architecture/README.md +27 -0
- package/docs/design/vanilla-hall-ui-redesign-scope.md +284 -0
- package/docs/design/world-map-scroll.md +41 -0
- package/docs/design-contract.md +320 -0
- package/docs/file-map.md +418 -0
- package/docs/handoff-template.md +92 -0
- package/docs/modules-contract.md +293 -0
- package/docs/onboarding/browser-terminal-guide.md +73 -0
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +28 -0
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +54 -0
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +37 -0
- package/docs/onboarding/diagrams/04-architecture.mmd +51 -0
- package/docs/onboarding/diagrams/05-versioning.mmd +29 -0
- package/docs/onboarding/diagrams/README.md +94 -0
- package/docs/onboarding/diagrams/assertions.json +170 -0
- package/docs/onboarding/diagrams/puppeteer-config.json +1 -0
- package/docs/onboarding/diagrams/render-config.json +9 -0
- package/docs/onboarding/drachmae-and-karma.md +178 -0
- package/docs/onboarding/primer.md +202 -0
- package/docs/project-context.template.md +67 -0
- package/docs/recipes/README.md +35 -0
- package/docs/recipes/audit-replay.md +100 -0
- package/docs/recipes/bongos-cli-release.md +66 -0
- package/docs/recipes/builder-box-lifecycle.md +152 -0
- package/docs/recipes/claude-design-loop.md +143 -0
- package/docs/recipes/claude-gemini-bongos-doors.md +155 -0
- package/docs/recipes/cloudbongos-staging-gate.md +139 -0
- package/docs/recipes/cloudbongos-standup.md +111 -0
- package/docs/recipes/devbox-app-release.md +93 -0
- package/docs/recipes/devbox-app-windows-test.md +128 -0
- package/docs/recipes/failed-ship-recovery-proof.md +102 -0
- package/docs/recipes/figma-design-loop.md +129 -0
- package/docs/recipes/gds-db-backup.md +255 -0
- package/docs/recipes/instance-deploy.md +37 -0
- package/docs/recipes/managed-settings-remote-control.md +416 -0
- package/docs/recipes/memory-backup.md +71 -0
- package/docs/recipes/memory-sync-conflicts.md +98 -0
- package/docs/recipes/multi-builder-merge.md +78 -0
- package/docs/recipes/ops-gotchas.md +212 -0
- package/docs/recipes/overnight-builder.md +53 -0
- package/docs/recipes/packaging-the-core.md +49 -0
- package/docs/recipes/parallel-load-results-2026-05-30.md +63 -0
- package/docs/recipes/parallel-load-test.md +32 -0
- package/docs/recipes/pixel-art-pipeline.md +411 -0
- package/docs/recipes/private-npm-distribution.md +79 -0
- package/docs/recipes/search-index-ingestion.md +67 -0
- package/docs/recipes/self-host.md +60 -0
- package/docs/recipes/ship-and-regrade.md +72 -0
- package/docs/recipes/staging-data.md +50 -0
- package/docs/recipes/staging-environment.md +215 -0
- package/docs/recipes/upgrading-the-core.md +58 -0
- package/docs/research/cursor-competitive-analysis.md +255 -0
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +227 -0
- package/migrations/003_pms.sql +284 -0
- package/migrations/004_pms_cost_idempotency.sql +13 -0
- package/migrations/005_pms_smoke_filter.sql +44 -0
- package/migrations/006_pms_v2.sql +378 -0
- package/migrations/007_pms_v2_hierarchy_trigger.sql +59 -0
- package/migrations/008_pms_v2_migration_allocator.sql +27 -0
- package/migrations/009_pms_v2_blockers_table.sql +68 -0
- package/migrations/010_uptime.sql +29 -0
- package/migrations/011_backfill_v2_v3_done_when.sql +20 -0
- package/migrations/012_pms_v2_task_lifecycle_states.sql +82 -0
- package/migrations/013_claim_locking.sql +22 -0
- package/migrations/014_pms_task_discipline.sql +86 -0
- package/migrations/015_pms_estimation_calibration.sql +140 -0
- package/migrations/016_pms_task_grades.sql +60 -0
- package/migrations/017_version_progress_exclude_abandoned.sql +49 -0
- package/migrations/019_rename_pms_v3_to_gds_v3.sql +65 -0
- package/migrations/020_task_dependencies.sql +99 -0
- package/migrations/021_v3_renumber_and_deps.sql +224 -0
- package/migrations/022_v3_reconcile_dep_gated_status.sql +41 -0
- package/migrations/023_pms_rank.sql +30 -0
- package/migrations/025_builder_exit_status.sql +15 -0
- package/migrations/027_builders_preferred_disciplines.sql +46 -0
- package/migrations/028_tasks_parallel_safe.sql +71 -0
- package/migrations/029_rank_three_live.sql +32 -0
- package/migrations/030_vulnerability_reports.sql +61 -0
- package/migrations/031_reward_simplification.sql +57 -0
- package/migrations/032_repo_health_snapshots.sql +56 -0
- package/migrations/033_audit_log.sql +64 -0
- package/migrations/034_lifecycle_breakdown_leaf_only.sql +62 -0
- package/migrations/035_auth_token_hygiene.sql +66 -0
- package/migrations/037_builder_skill_model_prefs.sql +44 -0
- package/migrations/038_task_override_requests.sql +51 -0
- package/migrations/039_newcomer_friendly_column.sql +124 -0
- package/migrations/040_builder_karma.sql +58 -0
- package/migrations/041_kind_multipliers_and_idea_bonus.sql +42 -0
- package/migrations/042_criterion_proposal_workflow.sql +78 -0
- package/migrations/043_builder_onboarding_observability.sql +66 -0
- package/migrations/044_tasks_requires_rank.sql +97 -0
- package/migrations/045_builder_onboarding_state.sql +147 -0
- package/migrations/046_cost_log_builder_attribution.sql +42 -0
- package/migrations/054_builder_memory.sql +108 -0
- package/migrations/055_task_criteria.sql +90 -0
- package/migrations/058_peer_votes.sql +41 -0
- package/migrations/059_onboarding_pick_disciplines.sql +59 -0
- package/migrations/060_access_requests.sql +46 -0
- package/migrations/061_backfill_override_ship_attribution.sql +29 -0
- package/migrations/062_session_records.sql +34 -0
- package/migrations/063_session_records_tool_errors_idx.sql +11 -0
- package/migrations/064_grade_attempts.sql +18 -0
- package/migrations/065_bfg_principal.sql +58 -0
- package/migrations/066_live_notify_triggers.sql +69 -0
- package/migrations/067_builder_boxes.sql +117 -0
- package/migrations/067_discord_bot_foundation.sql +69 -0
- package/migrations/068_thetes_requires_rank.sql +27 -0
- package/migrations/069_builder_ssh_keys.sql +66 -0
- package/migrations/070_box_intents.sql +101 -0
- package/migrations/071_box_first_provision_approval.sql +46 -0
- package/migrations/072_onboarding_ship_three_remove_primer.sql +80 -0
- package/migrations/073_box_terminal_access.sql +46 -0
- package/migrations/075_builder_sound_prefs.sql +28 -0
- package/migrations/078_box_claude_active.sql +22 -0
- package/migrations/079_box_first_connected_at.sql +20 -0
- package/migrations/081_tasks_needs_migration.sql +34 -0
- package/migrations/084_tasks_security_sensitive.sql +24 -0
- package/migrations/085_security_engagement_docs.sql +34 -0
- package/migrations/086_rename_disciplines.sql +125 -0
- package/migrations/088_builders_anthropic_link.sql +28 -0
- package/migrations/089_builders_monthly_budget.sql +19 -0
- package/migrations/090_box_scoped_sessions.sql +38 -0
- package/migrations/091_session_token_reward.sql +44 -0
- package/migrations/093_tasks_sandbox_review.sql +22 -0
- package/migrations/102_remove_box_first_provision_approval.sql +58 -0
- package/migrations/103_search_chunks.sql +90 -0
- package/migrations/104_builder_box_blocked.sql +34 -0
- package/migrations/105_box_host_keys.sql +35 -0
- package/migrations/111_medusa_customer0_framing.sql +35 -0
- package/migrations/112_drop_track_check.sql +25 -0
- package/migrations/113_merge_lease.sql +16 -0
- package/migrations/114_cloudbongos_rename_live_strings.sql +32 -0
- package/migrations/118_currency_label_decoupling.sql +23 -0
- package/migrations/119_builder_behavior_prefs.sql +31 -0
- package/migrations/121_box_code_version.sql +37 -0
- package/migrations/122_builder_gemini_key.sql +30 -0
- package/migrations/123_cost_log_tiers.sql +33 -0
- package/migrations/124_cost_baseline.sql +45 -0
- package/migrations/125_llm_cache.sql +54 -0
- package/migrations/126_autonomy_runs.sql +33 -0
- package/migrations/143_cost_calibration.sql +63 -0
- package/migrations/145_backfill_task_requires_rank.sql +84 -0
- package/migrations/160_goal_tier.sql +88 -0
- package/migrations/163_polymorphic_dependencies.sql +187 -0
- package/migrations/164_event_sound_prefs.sql +20 -0
- package/migrations/165_task_module_key.sql +15 -0
- package/migrations/171_claims_published_head_sha.sql +24 -0
- package/migrations/174_close_orphaned_claims_on_terminal_tasks.sql +30 -0
- package/migrations/175_task_grades_diff_hash.sql +9 -0
- package/migrations/176_promotion_respects_open_blockers.sql +58 -0
- package/migrations/176_ui_discipline.sql +40 -0
- package/migrations/177_require_reward_before_workable.sql +76 -0
- package/migrations/178_reward_gate_auto_assign_revert_autopromote.sql +61 -0
- package/migrations/179_search_chunk_usefulness.sql +33 -0
- package/migrations/180_one_active_claim_per_session.sql +54 -0
- package/migrations/181_cost_log_skill_name.sql +33 -0
- package/migrations/184_claim_release_requested.sql +25 -0
- package/migrations/185_module_upstreaming_goal_wiring.sql +59 -0
- package/migrations/187_goal_sort_order.sql +30 -0
- package/migrations/187_tasks_kind_check_add_verify.sql +21 -0
- package/migrations/README.md +88 -0
- package/migrations/core_188_goal_visibility_and_membership_requests.sql +71 -0
- package/migrations/core_189_core_upgrades_ledger.sql +37 -0
- package/migrations/core_189_credit_log_task_id_index.sql +19 -0
- package/migrations/core_190_rebuild_claimable_task_views.sql +71 -0
- package/migrations/instance/018_pms_v3_planning_seed.sql +131 -0
- package/migrations/instance/080_gds_v4_planning_seed.sql +75 -0
- package/migrations/instance/110_medusa_planning_seed.sql +70 -0
- package/migrations/instance/120_bongos_v1_planning_seed.sql +69 -0
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +26 -0
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +29 -0
- package/migrations/instance/172_v2_world_planning_seed.sql +163 -0
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +33 -0
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +132 -0
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +116 -0
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +69 -0
- package/modules/autonomy/CLAUDE.md +49 -0
- package/modules/autonomy/db.js +50 -0
- package/modules/autonomy/module.json +13 -0
- package/modules/autonomy/pollers/routine-timers.js +146 -0
- package/modules/autonomy/routes/autonomy.js +37 -0
- package/modules/builder-settings/CLAUDE.md +58 -0
- package/modules/builder-settings/builder-needs.js +115 -0
- package/modules/builder-settings/db.js +168 -0
- package/modules/builder-settings/display-name.js +84 -0
- package/modules/builder-settings/event-sound-prefs.js +169 -0
- package/modules/builder-settings/interaction-prefs.js +201 -0
- package/modules/builder-settings/model-alloc-prefs.js +147 -0
- package/modules/builder-settings/module.json +10 -0
- package/modules/builder-settings/render-prefs.js +103 -0
- package/modules/builder-settings/routes/settings.js +417 -0
- package/modules/builder-settings/settings.js +52 -0
- package/modules/builder-settings/skill-prefs.js +196 -0
- package/modules/builder-settings/sound-prefs.js +178 -0
- package/modules/builder-settings/wandering-prefs.js +282 -0
- package/modules/dev-box/CLAUDE.md +38 -0
- package/modules/dev-box/box-access.js +275 -0
- package/modules/dev-box/box-credential.js +94 -0
- package/modules/dev-box/box-onboard.js +486 -0
- package/modules/dev-box/boxes.js +842 -0
- package/modules/dev-box/db.js +39 -0
- package/modules/dev-box/module.json +14 -0
- package/modules/dev-box/routes/box.js +977 -0
- package/modules/discord/CLAUDE.md +50 -0
- package/modules/discord/auth.js +54 -0
- package/modules/discord/bug-attachments.js +210 -0
- package/modules/discord/db.js +96 -0
- package/modules/discord/discord-approvals.js +145 -0
- package/modules/discord/discord-audit.js +29 -0
- package/modules/discord/discord-bot.js +545 -0
- package/modules/discord/discord-bugs.js +257 -0
- package/modules/discord/discord-channels.js +412 -0
- package/modules/discord/discord-inbound.js +147 -0
- package/modules/discord/discord-message-buffer.js +54 -0
- package/modules/discord/discord-reaction.js +186 -0
- package/modules/discord/discord-role-sync.js +318 -0
- package/modules/discord/module.json +14 -0
- package/modules/discord/pollers/discord-bot.js +19 -0
- package/modules/discord/routes/bug-attachments.js +44 -0
- package/modules/discord/routes/discord.js +102 -0
- package/modules/discord/ship-broadcast.js +78 -0
- package/modules/discord/webhook.js +74 -0
- package/modules/economy/CLAUDE.md +48 -0
- package/modules/economy/achievements.js +594 -0
- package/modules/economy/cost-classify.js +331 -0
- package/modules/economy/cost-summary-shape.js +90 -0
- package/modules/economy/cost.js +438 -0
- package/modules/economy/credits.js +430 -0
- package/modules/economy/migrations/economy_001_fix_gds_shipper_description.sql +91 -0
- package/modules/economy/module.json +10 -0
- package/modules/economy/reward.js +61 -0
- package/modules/economy/routes/cost.js +97 -0
- package/modules/economy/routes/leaderboard.js +26 -0
- package/modules/economy/routes/reward.js +42 -0
- package/modules/economy/streak.js +285 -0
- package/modules/grading/CLAUDE.md +42 -0
- package/modules/grading/db.js +39 -0
- package/modules/grading/grader-rubric.json +151 -0
- package/modules/grading/grader-workers/efficiency.js +213 -0
- package/modules/grading/grader-workers/extract-json.js +80 -0
- package/modules/grading/grader-workers/fence.js +100 -0
- package/modules/grading/grader-workers/hacker.js +239 -0
- package/modules/grading/grader-workers/index.js +183 -0
- package/modules/grading/grader-workers/narc.js +207 -0
- package/modules/grading/grader-workers/quality.js +198 -0
- package/modules/grading/grader-workers/rubric.js +20 -0
- package/modules/grading/grader-workers/worker-prompt.js +89 -0
- package/modules/grading/grader-workers/worker-verdict.js +50 -0
- package/modules/grading/grader.js +1790 -0
- package/modules/grading/module.json +12 -0
- package/modules/grading/routes/grade.js +51 -0
- package/modules/hall-ui/CLAUDE.md +61 -0
- package/modules/hall-ui/module.json +14 -0
- package/modules/hall-ui/public/atlas.html +180 -0
- package/modules/hall-ui/public/atlas.js +336 -0
- package/modules/hall-ui/public/box-panel.js +423 -0
- package/modules/hall-ui/public/brand-apply.js +117 -0
- package/modules/hall-ui/public/builders.js +3028 -0
- package/modules/hall-ui/public/chime.wav +0 -0
- package/modules/hall-ui/public/copy-on-click.js +174 -0
- package/modules/hall-ui/public/cursors/arrow.png +0 -0
- package/modules/hall-ui/public/cursors/hand-pointer.png +0 -0
- package/modules/hall-ui/public/cursors/magnifier-out.png +0 -0
- package/modules/hall-ui/public/cursors/magnifier.png +0 -0
- package/modules/hall-ui/public/diagram-viewer.css +125 -0
- package/modules/hall-ui/public/diagram-viewer.js +330 -0
- package/modules/hall-ui/public/diagrams.html +113 -0
- package/modules/hall-ui/public/dom-utils.js +212 -0
- package/modules/hall-ui/public/drachmae.html +68 -0
- package/modules/hall-ui/public/drachmae.js +136 -0
- package/modules/hall-ui/public/gate.html +143 -0
- package/modules/hall-ui/public/gate.js +228 -0
- package/modules/hall-ui/public/goals-page.js +1297 -0
- package/modules/hall-ui/public/goals.html +376 -0
- package/modules/hall-ui/public/goals.js +119 -0
- package/modules/hall-ui/public/hall-widgets.js +189 -0
- package/modules/hall-ui/public/harbor.html +167 -0
- package/modules/hall-ui/public/harbor.js +335 -0
- package/modules/hall-ui/public/index.html +116 -0
- package/modules/hall-ui/public/instances-panel.js +172 -0
- package/modules/hall-ui/public/md-reader.js +234 -0
- package/modules/hall-ui/public/modules.html +119 -0
- package/modules/hall-ui/public/modules.js +154 -0
- package/modules/hall-ui/public/new-project-wizard.js +186 -0
- package/modules/hall-ui/public/not-ready.html +48 -0
- package/modules/hall-ui/public/pair.html +119 -0
- package/modules/hall-ui/public/pair.js +132 -0
- package/modules/hall-ui/public/primer.css +404 -0
- package/modules/hall-ui/public/primer.html +85 -0
- package/modules/hall-ui/public/primer.js +641 -0
- package/modules/hall-ui/public/profile.html +85 -0
- package/modules/hall-ui/public/profile.js +236 -0
- package/modules/hall-ui/public/ranks.html +69 -0
- package/modules/hall-ui/public/ranks.js +206 -0
- package/modules/hall-ui/public/roadmap.html +209 -0
- package/modules/hall-ui/public/roadmap.js +375 -0
- package/modules/hall-ui/public/sessions.html +92 -0
- package/modules/hall-ui/public/sessions.js +222 -0
- package/modules/hall-ui/public/settings.html +640 -0
- package/modules/hall-ui/public/settings.js +1219 -0
- package/modules/hall-ui/public/shell.js +495 -0
- package/modules/hall-ui/public/style.css +1748 -0
- package/modules/hall-ui/public/task-detail.js +253 -0
- package/modules/hall-ui/public/task.html +83 -0
- package/modules/hall-ui/public/task.js +326 -0
- package/modules/hall-ui/public/watch.html +333 -0
- package/modules/hall-ui/public/watch.js +859 -0
- package/modules/hall-ui/public/work.html +469 -0
- package/modules/hall-ui/public/work.js +1292 -0
- package/modules/ideas/CLAUDE.md +37 -0
- package/modules/ideas/blockers.js +100 -0
- package/modules/ideas/inbox.js +459 -0
- package/modules/ideas/module.json +10 -0
- package/modules/ideas/routes/blockers.js +120 -0
- package/modules/ideas/routes/inbox.js +230 -0
- package/modules/lifecycle/CLAUDE.md +63 -0
- package/modules/lifecycle/conflict-resolve.js +436 -0
- package/modules/lifecycle/db.js +4970 -0
- package/modules/lifecycle/done-when.js +355 -0
- package/modules/lifecycle/github-push.js +969 -0
- package/modules/lifecycle/goal-conflicts.js +133 -0
- package/modules/lifecycle/hierarchy.js +104 -0
- package/modules/lifecycle/lifecycle.js +58 -0
- package/modules/lifecycle/merge-lock.js +261 -0
- package/modules/lifecycle/migrations/lifecycle_001_goal_first_leads.sql +28 -0
- package/modules/lifecycle/migrations/lifecycle_002_backfill_orphan_task_goals.sql +36 -0
- package/modules/lifecycle/module.json +13 -0
- package/modules/lifecycle/pollers/publish-reconciler.js +20 -0
- package/modules/lifecycle/publish-reconciler.js +418 -0
- package/modules/lifecycle/routes/analytics.js +63 -0
- package/modules/lifecycle/routes/claims.js +506 -0
- package/modules/lifecycle/routes/dependencies.js +82 -0
- package/modules/lifecycle/routes/done-when.js +136 -0
- package/modules/lifecycle/routes/gate-approvals.js +197 -0
- package/modules/lifecycle/routes/goals.js +1419 -0
- package/modules/lifecycle/routes/lifecycle.js +43 -0
- package/modules/lifecycle/routes/tasks.js +1703 -0
- package/modules/lifecycle/routes/versions.js +128 -0
- package/modules/lifecycle/ship-card.js +505 -0
- package/modules/lifecycle/ship-preflight.js +225 -0
- package/modules/lifecycle/task-classifier.js +512 -0
- package/modules/lifecycle/version-close.js +106 -0
- package/modules/memory/CLAUDE.md +40 -0
- package/modules/memory/learnings.js +275 -0
- package/modules/memory/memory.js +572 -0
- package/modules/memory/module.json +12 -0
- package/modules/memory/routes/learnings.js +90 -0
- package/modules/memory/routes/memory.js +916 -0
- package/modules/memory/routes/search.js +188 -0
- package/modules/memory/search-chunker.js +252 -0
- package/modules/memory/search.js +320 -0
- package/modules/onboarding/CLAUDE.md +55 -0
- package/modules/onboarding/approval-broadcast.js +59 -0
- package/modules/onboarding/db.js +54 -0
- package/modules/onboarding/module.json +10 -0
- package/modules/onboarding/newcomer-restock.js +212 -0
- package/modules/onboarding/onboarding-state.js +532 -0
- package/modules/onboarding/onboarding.js +508 -0
- package/modules/onboarding/port.js +63 -0
- package/modules/onboarding/routes/access-requests.js +208 -0
- package/modules/onboarding/routes/onboarding.js +54 -0
- package/modules/security/CLAUDE.md +44 -0
- package/modules/security/module.json +10 -0
- package/modules/security/routes/reports.js +251 -0
- package/modules/security/secret-scrub.js +201 -0
- package/modules/security/security.js +294 -0
- package/modules/sessions/CLAUDE.md +39 -0
- package/modules/sessions/bfg.js +357 -0
- package/modules/sessions/db.js +326 -0
- package/modules/sessions/module.json +10 -0
- package/modules/sessions/pulse.js +149 -0
- package/modules/sessions/routes/sessions.js +331 -0
- package/modules/status-ui/CLAUDE.md +47 -0
- package/modules/status-ui/module.json +14 -0
- package/modules/status-ui/public/README.md +29 -0
- package/modules/status-ui/public/cursors/arrow.png +0 -0
- package/modules/status-ui/public/cursors/hand-pointer.png +0 -0
- package/modules/status-ui/public/index.html +363 -0
- package/modules/status-ui/public/snapshots/.gitkeep +0 -0
- package/modules/status-ui/public/status.js +1375 -0
- package/modules/status-ui/public/style.css +1342 -0
- package/package-lock.json +975 -0
- package/package.json +28 -0
- package/public-docs/index.html +35 -0
- package/public-docs/redoc.standalone.js +1838 -0
- package/public-docs/redoc.standalone.js.LICENSE.txt +102 -0
- package/public-landing/buddha.png +0 -0
- package/public-landing/create.html +868 -0
- package/public-landing/index.html +168 -0
- package/scripts/bongos-mcp.js +28 -0
- package/scripts/cron/style_guide_audit.sh +134 -0
- package/scripts/discord/README.md +52 -0
- package/scripts/discord/lib.js +80 -0
- package/scripts/discord/post.js +51 -0
- package/scripts/gds/CLAUDE.md +144 -0
- package/scripts/gds/abandon.js +58 -0
- package/scripts/gds/adopt-detect.js +123 -0
- package/scripts/gds/adopt-import.js +111 -0
- package/scripts/gds/adopt-preflight.js +121 -0
- package/scripts/gds/api.js +78 -0
- package/scripts/gds/architect-audit.js +288 -0
- package/scripts/gds/art-stage.js +152 -0
- package/scripts/gds/audit-authorship.sh +243 -0
- package/scripts/gds/audit-deps.js +121 -0
- package/scripts/gds/audit-exports.js +128 -0
- package/scripts/gds/audit-replay.sh +161 -0
- package/scripts/gds/audit-ships.js +251 -0
- package/scripts/gds/autonomy-gate.js +135 -0
- package/scripts/gds/backfill-cost.js +165 -0
- package/scripts/gds/backfill-sessions.js +351 -0
- package/scripts/gds/backfill-task-rewards.js +408 -0
- package/scripts/gds/backup.js +94 -0
- package/scripts/gds/bfg-principal-token.js +65 -0
- package/scripts/gds/box-auth-check.js +160 -0
- package/scripts/gds/box-connect-lib.js +453 -0
- package/scripts/gds/box.js +1258 -0
- package/scripts/gds/build-bongos.js +94 -0
- package/scripts/gds/capture.js +123 -0
- package/scripts/gds/cf-tunnel.js +419 -0
- package/scripts/gds/ci-grade.js +82 -0
- package/scripts/gds/claim-recall.js +112 -0
- package/scripts/gds/claim.js +333 -0
- package/scripts/gds/classify-discipline.js +153 -0
- package/scripts/gds/claude-materialize.js +176 -0
- package/scripts/gds/cleanup-orphans.js +74 -0
- package/scripts/gds/cli-lib.js +518 -0
- package/scripts/gds/code.js +101 -0
- package/scripts/gds/codemod-rename-src-gds.js +145 -0
- package/scripts/gds/conductor-dispatch.js +426 -0
- package/scripts/gds/context-pack.js +228 -0
- package/scripts/gds/coreB-carve-proof.sh +716 -0
- package/scripts/gds/cost.js +47 -0
- package/scripts/gds/db-backup-nightly.sh +160 -0
- package/scripts/gds/db-backup-offsite.sh +152 -0
- package/scripts/gds/demote.js +40 -0
- package/scripts/gds/design-sync.js +62 -0
- package/scripts/gds/diagnose-achievements.js +118 -0
- package/scripts/gds/diagram-facts.js +319 -0
- package/scripts/gds/discipline-modes.json +25 -0
- package/scripts/gds/discord-channels.js +66 -0
- package/scripts/gds/do-api.js +176 -0
- package/scripts/gds/docs-entropy.js +403 -0
- package/scripts/gds/doctor.js +389 -0
- package/scripts/gds/feedback-latest.js +94 -0
- package/scripts/gds/fetch-art-key.js +106 -0
- package/scripts/gds/fetch-sound-prefs.js +88 -0
- package/scripts/gds/figma-design-sync.js +61 -0
- package/scripts/gds/fitness.js +1446 -0
- package/scripts/gds/gate-review.js +322 -0
- package/scripts/gds/gating.js +325 -0
- package/scripts/gds/gds-db-backup.service +24 -0
- package/scripts/gds/gds-db-backup.timer +20 -0
- package/scripts/gds/gds-db-offsite.service +29 -0
- package/scripts/gds/gds-db-offsite.timer +18 -0
- package/scripts/gds/gen-api-client.js +508 -0
- package/scripts/gds/gen-api-docs.js +1037 -0
- package/scripts/gds/gen-atlas.js +366 -0
- package/scripts/gds/gen-diagrams.js +608 -0
- package/scripts/gds/gen-file-map.js +196 -0
- package/scripts/gds/gen-repo-map.js +519 -0
- package/scripts/gds/gen-session-index.js +210 -0
- package/scripts/gds/git-merge-regen.js +147 -0
- package/scripts/gds/grade-smoke.js +205 -0
- package/scripts/gds/hooks/pre-push.js +169 -0
- package/scripts/gds/http-api-client.js +88 -0
- package/scripts/gds/init.js +903 -0
- package/scripts/gds/install-git-hooks.js +114 -0
- package/scripts/gds/key-card.js +213 -0
- package/scripts/gds/key-status.js +126 -0
- package/scripts/gds/land-watch.js +168 -0
- package/scripts/gds/learning-capture.js +145 -0
- package/scripts/gds/lib.sh +44 -0
- package/scripts/gds/linkify-refs.js +473 -0
- package/scripts/gds/local-preview-lib.js +159 -0
- package/scripts/gds/local-preview.js +95 -0
- package/scripts/gds/login.js +225 -0
- package/scripts/gds/main-audit.js +279 -0
- package/scripts/gds/me.js +88 -0
- package/scripts/gds/memory-map.js +174 -0
- package/scripts/gds/merge-lock.js +71 -0
- package/scripts/gds/migration-namespace.js +200 -0
- package/scripts/gds/mirror-lineage-check.js +319 -0
- package/scripts/gds/mirror-redact.js +305 -0
- package/scripts/gds/module.js +482 -0
- package/scripts/gds/newcomer-floor.config.json +13 -0
- package/scripts/gds/newcomer-floor.js +415 -0
- package/scripts/gds/newcomer-floor.templates.json +86 -0
- package/scripts/gds/newcomer-floor.test.js +235 -0
- package/scripts/gds/next-number.js +163 -0
- package/scripts/gds/oauth-secret.js +197 -0
- package/scripts/gds/onboard.js +217 -0
- package/scripts/gds/onboarding-analyze.js +226 -0
- package/scripts/gds/onboarding-config.js +178 -0
- package/scripts/gds/onboarding-ship.js +92 -0
- package/scripts/gds/package-core.js +684 -0
- package/scripts/gds/paste-token.js +131 -0
- package/scripts/gds/portability-smoke-art.sh +85 -0
- package/scripts/gds/portability-smoke-memory.sh +147 -0
- package/scripts/gds/provision.js +1324 -0
- package/scripts/gds/publish-manifest.js +307 -0
- package/scripts/gds/pulse.js +309 -0
- package/scripts/gds/push-main.js +45 -0
- package/scripts/gds/read-session-export.js +359 -0
- package/scripts/gds/reauth.js +149 -0
- package/scripts/gds/recall.js +135 -0
- package/scripts/gds/release.js +71 -0
- package/scripts/gds/render-ideas.js +173 -0
- package/scripts/gds/repo-metrics.js +363 -0
- package/scripts/gds/resolve-skill-model.js +57 -0
- package/scripts/gds/run-routine.js +197 -0
- package/scripts/gds/run-unit-tests.js +178 -0
- package/scripts/gds/sandbox-stage.js +653 -0
- package/scripts/gds/search-ingest-db.js +209 -0
- package/scripts/gds/search-ingest-md.js +225 -0
- package/scripts/gds/security-baseline-check.sh +180 -0
- package/scripts/gds/seed-adopt-tasks.js +75 -0
- package/scripts/gds/seed-api-contract-goal-tasks.js +141 -0
- package/scripts/gds/seed-bongos-app-tasks.js +294 -0
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +81 -0
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +100 -0
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +307 -0
- package/scripts/gds/seed-bongos-module-system-tasks.js +346 -0
- package/scripts/gds/seed-bongos-tasks.js +214 -0
- package/scripts/gds/seed-gds-v4-tasks.js +624 -0
- package/scripts/gds/seed-governance-tasks.js +340 -0
- package/scripts/gds/seed-medusa-tasks.js +440 -0
- package/scripts/gds/seed-module-upstreaming.js +219 -0
- package/scripts/gds/seed-productization-tasks.js +235 -0
- package/scripts/gds/seed-provisioning-tasks.js +83 -0
- package/scripts/gds/seed-staging.sh +104 -0
- package/scripts/gds/seed-v2-world-tasks.js +283 -0
- package/scripts/gds/seed-v3-tasks.js +1981 -0
- package/scripts/gds/session-cost.js +393 -0
- package/scripts/gds/session-digest.js +179 -0
- package/scripts/gds/session-eval.js +576 -0
- package/scripts/gds/session.js +404 -0
- package/scripts/gds/setup.js +504 -0
- package/scripts/gds/shell.js +100 -0
- package/scripts/gds/ship.js +3618 -0
- package/scripts/gds/smoke-batch-claim.sh +213 -0
- package/scripts/gds/smoke-batch-release.sh +296 -0
- package/scripts/gds/smoke-box.sh +150 -0
- package/scripts/gds/smoke-claim-consistency.sh +212 -0
- package/scripts/gds/smoke-dependencies.sh +84 -0
- package/scripts/gds/smoke-gds.js +125 -0
- package/scripts/gds/smoke-write.sh +168 -0
- package/scripts/gds/start.js +782 -0
- package/scripts/gds/status.js +365 -0
- package/scripts/gds/triage.js +366 -0
- package/scripts/gds/upgrade.js +421 -0
- package/scripts/gds/validate-design.js +254 -0
- package/scripts/gds/version-close.js +549 -0
- package/scripts/migrate.sh +183 -0
- package/scripts/migrate_cost_ledger.sh +178 -0
- package/scripts/public-mirror-export.js +291 -0
- package/scripts/render-diagrams.sh +88 -0
- package/scripts/security/dep-audit.js +276 -0
- package/scripts/setup-builder.js +274 -0
- package/scripts/setup-builder.sh +28 -0
- package/scripts/snapshots/backfill-history.mjs +292 -0
- package/scripts/snapshots/maybe-render-on-deploy.sh +66 -0
- package/scripts/snapshots/render-pre-art-eras.mjs +329 -0
- package/scripts/snapshots/render-world.mjs +322 -0
- package/scripts/status-mirror-sync.js +192 -0
- package/scripts/status-probe.mjs +185 -0
- package/server.js +212 -0
- package/src/asset-version.js +71 -0
- package/src/bongos/CLAUDE.md +64 -0
- package/src/bongos/api-errors.js +281 -0
- package/src/bongos/api-path-404.js +67 -0
- package/src/bongos/api-prefix.js +38 -0
- package/src/bongos/app-pair.js +172 -0
- package/src/bongos/auth.js +979 -0
- package/src/bongos/cascade-dispatch.js +123 -0
- package/src/bongos/db-kernel.js +252 -0
- package/src/bongos/db.js +911 -0
- package/src/bongos/goal-scope-check.js +120 -0
- package/src/bongos/hierarchy-config.js +189 -0
- package/src/bongos/live-channel.js +145 -0
- package/src/bongos/llm-cache.js +260 -0
- package/src/bongos/llm-pricing.js +220 -0
- package/src/bongos/metrics.js +286 -0
- package/src/bongos/middleware/audit.js +274 -0
- package/src/bongos/middleware/error-envelope.js +93 -0
- package/src/bongos/middleware/rate-limit.js +195 -0
- package/src/bongos/module-scope-map.js +271 -0
- package/src/bongos/optimizer.js +166 -0
- package/src/bongos/path-match.js +47 -0
- package/src/bongos/permission-path-check.js +248 -0
- package/src/bongos/pool.js +55 -0
- package/src/bongos/repo-info.js +91 -0
- package/src/bongos/route-rank-check.js +420 -0
- package/src/bongos/routes/CLAUDE.md +43 -0
- package/src/bongos/routes/_helpers.js +369 -0
- package/src/bongos/routes/audit-log.js +59 -0
- package/src/bongos/routes/auth.js +755 -0
- package/src/bongos/routes/backup.js +104 -0
- package/src/bongos/routes/builders.js +526 -0
- package/src/bongos/routes/healthz.js +16 -0
- package/src/bongos/routes/instance.js +129 -0
- package/src/bongos/routes/live.js +125 -0
- package/src/bongos/routes/llm-cache.js +119 -0
- package/src/bongos/routes/me.js +380 -0
- package/src/bongos/routes/modules.js +131 -0
- package/src/bongos/routes/optimizer.js +54 -0
- package/src/bongos/routes/public.js +476 -0
- package/src/bongos/routes/security.js +207 -0
- package/src/bongos/routes.js +166 -0
- package/src/bongos/secret-box.js +177 -0
- package/src/bongos/sensitive-surfaces.js +30 -0
- package/src/bongos/serve-internal.js +889 -0
- package/src/bongos/uptime-poller.js +90 -0
- package/src/bongos-downloads.js +70 -0
- package/src/branding.js +182 -0
- package/src/build-info.js +77 -0
- package/src/devbox-downloads.js +67 -0
- package/src/instance-config.js +200 -0
- package/src/module-api.js +366 -0
- package/src/module-loader/loader.js +268 -0
- package/src/module-loader/manifest-schema.js +140 -0
- package/src/module-loader/provenance.js +28 -0
- package/src/module-loader/semver.js +100 -0
- package/src/module-seams.js +291 -0
- package/src/modules.js +254 -0
- package/src/platform-server.js +135 -0
- package/src/ui/adapters/claude-design/index.js +333 -0
- package/src/ui/adapters/figma/index.js +328 -0
- package/tests/abandon_task.mjs +138 -0
- package/tests/achievements_reconcile.mjs +167 -0
- package/tests/adopt_detect.mjs +141 -0
- package/tests/adopt_import.mjs +132 -0
- package/tests/adopt_preflight.mjs +105 -0
- package/tests/anti_grid.mjs +89 -0
- package/tests/api_client.mjs +231 -0
- package/tests/api_docs.mjs +283 -0
- package/tests/api_error_envelope_buildcheck.mjs +56 -0
- package/tests/api_error_envelope_middleware.mjs +99 -0
- package/tests/api_error_registry.mjs +108 -0
- package/tests/api_pagination_contract.mjs +51 -0
- package/tests/api_path_404.mjs +96 -0
- package/tests/app_pair.mjs +168 -0
- package/tests/architect.mjs +140 -0
- package/tests/art_key.mjs +121 -0
- package/tests/art_stage.mjs +121 -0
- package/tests/asset_version.mjs +94 -0
- package/tests/atlas.mjs +69 -0
- package/tests/audit_deps.mjs +74 -0
- package/tests/audit_middleware.mjs +385 -0
- package/tests/auth_admission_gate.mjs +164 -0
- package/tests/auth_anthropic_identity.mjs +95 -0
- package/tests/auth_duplicate_cookie.mjs +56 -0
- package/tests/auth_error_brand.mjs +77 -0
- package/tests/auth_flow_slice1.mjs +223 -0
- package/tests/auth_inactive_builder.mjs +94 -0
- package/tests/auth_page_gate.mjs +92 -0
- package/tests/auth_session_ttl.mjs +155 -0
- package/tests/autonomous_fit_protected_paths.mjs +115 -0
- package/tests/autonomy_gate.mjs +85 -0
- package/tests/autotile_variants.mjs +256 -0
- package/tests/autotile_xverify.mjs +133 -0
- package/tests/backfill_sessions.mjs +101 -0
- package/tests/backfill_task_rewards.mjs +152 -0
- package/tests/backup_cli.mjs +104 -0
- package/tests/bfg.mjs +327 -0
- package/tests/bfg_write.mjs +423 -0
- package/tests/bongos_box_connect.mjs +89 -0
- package/tests/bongos_dispatch.mjs +74 -0
- package/tests/bongos_downloads.mjs +118 -0
- package/tests/bongos_login.mjs +34 -0
- package/tests/box_access.mjs +186 -0
- package/tests/box_auth_check.mjs +114 -0
- package/tests/box_code_staleness.mjs +116 -0
- package/tests/box_connect_e2e.mjs +196 -0
- package/tests/box_cost.mjs +70 -0
- package/tests/box_dns_repoint.mjs +151 -0
- package/tests/box_host_keys.mjs +102 -0
- package/tests/box_onboard.mjs +317 -0
- package/tests/box_scope_session.mjs +115 -0
- package/tests/box_ship_permissions.mjs +71 -0
- package/tests/box_terminal_ssrf.mjs +90 -0
- package/tests/boxes.mjs +1255 -0
- package/tests/branding.mjs +88 -0
- package/tests/bug_attachments.mjs +189 -0
- package/tests/build_info.mjs +88 -0
- package/tests/builder_needs.mjs +99 -0
- package/tests/cap_newcomer_tasks.mjs +114 -0
- package/tests/cascade-dispatch.mjs +95 -0
- package/tests/cf_tunnel.mjs +459 -0
- package/tests/claim_recall.mjs +150 -0
- package/tests/claims_bind_session.mjs +47 -0
- package/tests/claude_materialize.mjs +148 -0
- package/tests/cli_box_scope.mjs +52 -0
- package/tests/cli_lib_formatters.mjs +87 -0
- package/tests/cli_token_reissue.mjs +147 -0
- package/tests/client_bundle_route.mjs +47 -0
- package/tests/cloud_bongos_pack.mjs +85 -0
- package/tests/conductor.mjs +496 -0
- package/tests/confirm_attribution.mjs +156 -0
- package/tests/conflict_resolve.mjs +462 -0
- package/tests/context_pack.mjs +240 -0
- package/tests/cost.mjs +94 -0
- package/tests/cost_audit.mjs +122 -0
- package/tests/cost_budget_status.mjs +78 -0
- package/tests/cost_reconcile.mjs +102 -0
- package/tests/cost_summary_shape.mjs +94 -0
- package/tests/credit_grant.mjs +376 -0
- package/tests/criterion_progress.mjs +220 -0
- package/tests/criterion_proposal.mjs +109 -0
- package/tests/criterion_ref_parse.mjs +53 -0
- package/tests/currency_label.mjs +119 -0
- package/tests/day_cycle.mjs +197 -0
- package/tests/dependencies_migration.mjs +77 -0
- package/tests/dependencies_polymorphic.mjs +212 -0
- package/tests/deploy_instance.mjs +78 -0
- package/tests/devbox_downloads.mjs +122 -0
- package/tests/diff_derived_signals.mjs +115 -0
- package/tests/discord_approvals.mjs +174 -0
- package/tests/discord_autojoin.mjs +158 -0
- package/tests/discord_bot.mjs +94 -0
- package/tests/discord_bugs.mjs +297 -0
- package/tests/discord_channels.mjs +337 -0
- package/tests/discord_foundation.mjs +76 -0
- package/tests/discord_inbound.mjs +190 -0
- package/tests/discord_message_buffer.mjs +87 -0
- package/tests/discord_oauth.mjs +55 -0
- package/tests/discord_reaction.mjs +170 -0
- package/tests/discord_role_sync.mjs +151 -0
- package/tests/display_name.mjs +123 -0
- package/tests/dns_preflight.mjs +74 -0
- package/tests/event_sound_prefs.mjs +194 -0
- package/tests/eviction_policy.mjs +279 -0
- package/tests/figma_adapter.mjs +199 -0
- package/tests/file_map.mjs +91 -0
- package/tests/first_admin_bootstrap.mjs +40 -0
- package/tests/fitness.mjs +435 -0
- package/tests/fitness_two_root.mjs +104 -0
- package/tests/fixtures/session-506-digest.json +34 -0
- package/tests/gate_approvals.mjs +451 -0
- package/tests/gate_review.mjs +152 -0
- package/tests/git_merge_regen.mjs +151 -0
- package/tests/github_push_ancestry.mjs +119 -0
- package/tests/github_push_land_proof.mjs +127 -0
- package/tests/github_push_merge_driver.mjs +175 -0
- package/tests/goal_abuse_matrix.mjs +187 -0
- package/tests/goal_achievement.mjs +146 -0
- package/tests/goal_conflicts.mjs +305 -0
- package/tests/goal_dependencies_read.mjs +73 -0
- package/tests/goal_member_roles.mjs +420 -0
- package/tests/goal_membership_requests.mjs +332 -0
- package/tests/goal_routes.mjs +203 -0
- package/tests/goal_scope_check.mjs +167 -0
- package/tests/goal_tier.mjs +312 -0
- package/tests/goal_wall_proof.mjs +112 -0
- package/tests/grade_attempts.mjs +73 -0
- package/tests/grade_attribution.mjs +117 -0
- package/tests/grade_diff_hash.mjs +191 -0
- package/tests/grade_server_authoritative.mjs +305 -0
- package/tests/grade_unavailable_display.mjs +66 -0
- package/tests/grader.mjs +1960 -0
- package/tests/grader_bypass.mjs +57 -0
- package/tests/grader_panel.mjs +252 -0
- package/tests/grader_prompt_fencing.mjs +192 -0
- package/tests/helpers.mjs +52 -0
- package/tests/hierarchy-config.mjs +81 -0
- package/tests/init.mjs +463 -0
- package/tests/instance_config.mjs +76 -0
- package/tests/instance_manifest.mjs +110 -0
- package/tests/interaction_prefs.mjs +72 -0
- package/tests/key-card.mjs +110 -0
- package/tests/key-status.mjs +45 -0
- package/tests/land_watch.mjs +115 -0
- package/tests/lifecycle-card.mjs +145 -0
- package/tests/lifecycle_breakdown_view.mjs +236 -0
- package/tests/linkify_refs.mjs +264 -0
- package/tests/live_channel.mjs +128 -0
- package/tests/live_sse.mjs +122 -0
- package/tests/llm-cache-route.mjs +110 -0
- package/tests/llm-cache.mjs +140 -0
- package/tests/llm-pricing.mjs +152 -0
- package/tests/local_preview.mjs +136 -0
- package/tests/main_audit.mjs +137 -0
- package/tests/memory_isolation.mjs +431 -0
- package/tests/memory_map.mjs +107 -0
- package/tests/memory_pull.mjs +429 -0
- package/tests/merge_lease.mjs +100 -0
- package/tests/merge_lock.mjs +312 -0
- package/tests/metrics.mjs +147 -0
- package/tests/migration_allocation.mjs +146 -0
- package/tests/migration_namespace.mjs +185 -0
- package/tests/migrations_fresh_replay_guard.mjs +38 -0
- package/tests/mirror_lineage_fulltree.mjs +45 -0
- package/tests/mirror_lineage_proof.mjs +72 -0
- package/tests/mirror_redact.mjs +193 -0
- package/tests/model_alloc_prefs.mjs +63 -0
- package/tests/module-scope-map.mjs +88 -0
- package/tests/module_api.mjs +110 -0
- package/tests/module_cli.mjs +292 -0
- package/tests/module_contributions.mjs +148 -0
- package/tests/module_loader.mjs +286 -0
- package/tests/module_manifest.mjs +194 -0
- package/tests/module_owned_tests.mjs +105 -0
- package/tests/module_provenance.mjs +34 -0
- package/tests/module_route_rank.mjs +147 -0
- package/tests/module_seams.mjs +68 -0
- package/tests/modules.mjs +156 -0
- package/tests/modules_route.mjs +38 -0
- package/tests/newcomer_floor.mjs +17 -0
- package/tests/newcomer_restock.mjs +80 -0
- package/tests/next_number.mjs +154 -0
- package/tests/oauth_secret.mjs +60 -0
- package/tests/onboard.mjs +70 -0
- package/tests/onboarding_gate.mjs +267 -0
- package/tests/onboarding_observability.mjs +344 -0
- package/tests/onboarding_route_signals.mjs +175 -0
- package/tests/onboarding_state.mjs +346 -0
- package/tests/one_active_claim_per_session.mjs +111 -0
- package/tests/orphaned_claim_ship.mjs +135 -0
- package/tests/override_attribution.mjs +142 -0
- package/tests/override_request_ownership.mjs +235 -0
- package/tests/package_core.mjs +435 -0
- package/tests/parallel-load.mjs +402 -0
- package/tests/path_match.mjs +62 -0
- package/tests/permission_path.mjs +151 -0
- package/tests/platform_boot.mjs +167 -0
- package/tests/pre_compact.mjs +60 -0
- package/tests/preview_server.mjs +127 -0
- package/tests/profile_route.mjs +197 -0
- package/tests/provision.mjs +805 -0
- package/tests/provisioning_github_app_routes.mjs +98 -0
- package/tests/public_mirror_export.mjs +124 -0
- package/tests/public_task_resolver.mjs +112 -0
- package/tests/publish_branch_route.mjs +422 -0
- package/tests/publish_manifest.mjs +184 -0
- package/tests/publish_reconciler.mjs +728 -0
- package/tests/rank_gate.mjs +463 -0
- package/tests/rate_limit_public.mjs +217 -0
- package/tests/read_session_export.mjs +89 -0
- package/tests/recall_cli.mjs +82 -0
- package/tests/recall_hints.mjs +119 -0
- package/tests/recent_shipped_window.mjs +80 -0
- package/tests/regrade_eligibility.mjs +202 -0
- package/tests/render_prefs.mjs +122 -0
- package/tests/repo_map.mjs +92 -0
- package/tests/reward_gate.mjs +284 -0
- package/tests/route_rank_check.mjs +243 -0
- package/tests/route_validation.mjs +490 -0
- package/tests/routine_timers.mjs +72 -0
- package/tests/rubric_fixtures.mjs +140 -0
- package/tests/run_routine.mjs +69 -0
- package/tests/sandbox_stage.mjs +582 -0
- package/tests/scrubber_corpus.mjs +257 -0
- package/tests/search_chunker.mjs +209 -0
- package/tests/search_ingest.mjs +96 -0
- package/tests/search_ingest_db.mjs +88 -0
- package/tests/search_isolation.mjs +204 -0
- package/tests/search_route.mjs +95 -0
- package/tests/secret_box.mjs +189 -0
- package/tests/security_docs.mjs +186 -0
- package/tests/security_route.mjs +108 -0
- package/tests/served_paths_shipped.mjs +118 -0
- package/tests/session-cards.mjs +142 -0
- package/tests/session_506_regression.mjs +69 -0
- package/tests/session_caps.mjs +238 -0
- package/tests/session_cost.mjs +133 -0
- package/tests/session_digest.mjs +73 -0
- package/tests/session_eval.mjs +386 -0
- package/tests/session_index.mjs +105 -0
- package/tests/session_records.mjs +369 -0
- package/tests/session_search.mjs +78 -0
- package/tests/session_start_freshness.mjs +123 -0
- package/tests/setup_smoke.mjs +154 -0
- package/tests/ship_autolinkify.mjs +60 -0
- package/tests/ship_automerge_injection.mjs +174 -0
- package/tests/ship_broadcast_routing.mjs +125 -0
- package/tests/ship_broadcast_server.mjs +175 -0
- package/tests/ship_card.mjs +280 -0
- package/tests/ship_ci_deploy.mjs +374 -0
- package/tests/ship_preflight.mjs +368 -0
- package/tests/ship_premerge.mjs +75 -0
- package/tests/ship_resume.mjs +129 -0
- package/tests/ship_upload_skip.mjs +89 -0
- package/tests/ship_zero_commit.mjs +60 -0
- package/tests/single_host_hall.mjs +98 -0
- package/tests/skill_prefs.mjs +150 -0
- package/tests/smoke_preflight.mjs +66 -0
- package/tests/snapshot_picker.mjs +233 -0
- package/tests/sound_prefs.mjs +198 -0
- package/tests/sprite_facing.mjs +35 -0
- package/tests/status-probe.mjs +89 -0
- package/tests/status_backfill.mjs +94 -0
- package/tests/stop_refcheck.mjs +127 -0
- package/tests/streak.mjs +175 -0
- package/tests/subdomain_auth.mjs +58 -0
- package/tests/task_classifier_vocab.mjs +41 -0
- package/tests/task_create_idempotency.mjs +96 -0
- package/tests/task_goal_link.mjs +93 -0
- package/tests/task_ownership_gate.mjs +125 -0
- package/tests/terrain_picker.mjs +298 -0
- package/tests/touches_overlap.mjs +75 -0
- package/tests/triage_development.mjs +140 -0
- package/tests/turn_in_place.mjs +280 -0
- package/tests/upgrade.mjs +357 -0
- package/tests/verify_task_guard.mjs +64 -0
- package/tests/version_close.mjs +76 -0
- package/tests/versions_route.mjs +93 -0
- package/tests/walkability.mjs +188 -0
- package/tests/wandering_prefs.mjs +176 -0
- package/tests/worktree_disambiguation.mjs +105 -0
- package/tests/worktree_guard.mjs +90 -0
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
// src/bongos/serve-internal.js — the shared INTERNAL web surface (ADR 0062 R51, task 1190).
|
|
2
|
+
//
|
|
3
|
+
// Mounts every non-game surface the platform serves onto an Express app:
|
|
4
|
+
// * /healthz + /version — liveness + deployed-build info
|
|
5
|
+
// * /api/gds/* — the GDS API (buildGdsRouter)
|
|
6
|
+
// * /downloads/devbox/* — Dev Box installer redirect surface (#904)
|
|
7
|
+
// * generic module web-surface — each enabled module's contributes.webSurfaces
|
|
8
|
+
// hosts served on its Host prefix by ONE generic static
|
|
9
|
+
// loop (the status dashboard at status.<apex>,
|
|
10
|
+
// BV1.R84). The hall declares a webSurface too
|
|
11
|
+
// but is partitioned OUT of this loop — it has
|
|
12
|
+
// the dedicated richer serving below.
|
|
13
|
+
// * the builders' hall host — modules/hall-ui/public/ (builders.<apex>, the
|
|
14
|
+
// hall-ui module's web surface carved in BV1.R85)
|
|
15
|
+
// + the apex /builders/* tree, with the subdomain
|
|
16
|
+
// shim, the Archon/citizen page gates, the
|
|
17
|
+
// asset-stamped HTML, and the primer/diagram
|
|
18
|
+
// static mounts.
|
|
19
|
+
//
|
|
20
|
+
// WHY THIS MODULE EXISTS: the game boot (server.js) and the GDS-only boot
|
|
21
|
+
// (src/platform-server.js, R51) must serve these surfaces IDENTICALLY. Rather
|
|
22
|
+
// than copy-paste the ~330 lines of host routing into both — which would drift
|
|
23
|
+
// the moment one entrypoint changed — both call mountInternalSurfaces(app). This
|
|
24
|
+
// mirrors the registerGameRooms() pattern that keeps server.js and
|
|
25
|
+
// preview-server.js from drifting on the game-room list (ADR 0052).
|
|
26
|
+
//
|
|
27
|
+
// CORE-SAFE: this file lives under src/bongos/ and so is held to the core↔host
|
|
28
|
+
// import boundary (scripts/gds/fitness.js §1) — it imports nothing from the host
|
|
29
|
+
// game runtime (no src/db.js, no src/rooms/, no src/world/, no Colyseus). It
|
|
30
|
+
// serves only static files + the GDS router, so a non-game Cloud Bongos instance
|
|
31
|
+
// runs it unchanged. (The /downloads/devbox/* surface is an instance feature that
|
|
32
|
+
// R54's module system, task 1193, will gate per-instance; it is benign on a
|
|
33
|
+
// vanilla boot — it only redirects to whatever config/devbox-app.json names.)
|
|
34
|
+
|
|
35
|
+
const path = require('path');
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const express = require('express');
|
|
38
|
+
|
|
39
|
+
const { buildGdsRouter } = require('./routes');
|
|
40
|
+
const { attachFail } = require('./middleware/error-envelope');
|
|
41
|
+
const metrics = require('./metrics');
|
|
42
|
+
const gdsAuth = require('./auth');
|
|
43
|
+
const { buildStampedHtml } = require('../asset-version');
|
|
44
|
+
const { buildInfo } = require('../build-info');
|
|
45
|
+
const { buildDevboxDownloads } = require('../devbox-downloads');
|
|
46
|
+
const { buildBongosDownloads } = require('../bongos-downloads');
|
|
47
|
+
const { clientBranding, branding } = require('../branding');
|
|
48
|
+
const { clientModules, isModuleEnabled } = require('../modules');
|
|
49
|
+
const { hallWidgetScripts, moduleWebSurfaces } = require('../module-loader/loader');
|
|
50
|
+
const { resolveCoreRoot } = require('../instance-config');
|
|
51
|
+
const { API_PREFIX, LEGACY_API_PREFIXES, API_VERSION, VERSIONED_API_PREFIX, ALL_API_PREFIXES } = require('./api-prefix');
|
|
52
|
+
|
|
53
|
+
// ADR 0108 §1: every use of ROOT below (infra/ installers, modules/, docs/) is
|
|
54
|
+
// core package content. resolveCoreRoot() returns the repo root today, so this
|
|
55
|
+
// is byte-identical for the live instance.
|
|
56
|
+
const ROOT = resolveCoreRoot();
|
|
57
|
+
|
|
58
|
+
// Instance-only RELEASE config (config/devbox-app.json, config/bongos-cli.json) is
|
|
59
|
+
// boundary-excluded from the portable core (publish-manifest INSTANCE_EXCLUDE), so a
|
|
60
|
+
// vanilla self-host boot ships without it. Requiring it hard used to CRASH the boot
|
|
61
|
+
// (task 1824). Guard the require so an absent config makes its download surface simply
|
|
62
|
+
// not mount (fail-to-vanilla, like branding/modules) rather than take the server down.
|
|
63
|
+
// A malformed config (JSON parse error, etc.) still throws — only a MISSING file is
|
|
64
|
+
// tolerated. A branded instance that ships the config gets the routes exactly as before.
|
|
65
|
+
function tryRequireConfig(relPath) {
|
|
66
|
+
try { return require(relPath); }
|
|
67
|
+
catch (e) { if (e && e.code === 'MODULE_NOT_FOUND') return null; throw e; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Map the pack's camelCase theme color keys → the kebab-case CSS custom
|
|
71
|
+
// properties the hall/status stylesheets read (public-{builders,status}/style.css
|
|
72
|
+
// :root). R57 (task 1196).
|
|
73
|
+
const COLOR_VARS = {
|
|
74
|
+
bg: '--bg', bgCard: '--bg-card', bgDeep: '--bg-deep', bgFrame: '--bg-frame',
|
|
75
|
+
ink: '--ink', inkSoft: '--ink-soft', inkFaint: '--ink-faint',
|
|
76
|
+
rule: '--rule', ruleSoft: '--rule-soft',
|
|
77
|
+
accent: '--accent', accentDeep: '--accent-deep', sea: '--sea', terracotta: '--terracotta',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Build the THEME markup for a served page from the client-safe pack's theme:
|
|
81
|
+
// a <style> overriding the :root palette + font-family vars, the web-font <link>s,
|
|
82
|
+
// and the favicon <link>. Returns '' when the pack carries no theme (so a pack
|
|
83
|
+
// without a `theme.ui` block is a no-op and the stylesheet's own defaults stand).
|
|
84
|
+
// The <style> is emitted so it lands AFTER the page's own stylesheet <link>
|
|
85
|
+
// (which sits earlier in <head>) — equal specificity, later wins — making the
|
|
86
|
+
// pack authoritative over style.css. For OTB the values equal style.css's
|
|
87
|
+
// parchment defaults, so the render is byte-identical; a neutral instance gets
|
|
88
|
+
// its plain palette + system fonts. R57 (task 1196, ADR 0062 §3).
|
|
89
|
+
function themeMarkup(theme) {
|
|
90
|
+
if (!theme) return '';
|
|
91
|
+
const decls = [];
|
|
92
|
+
for (const [key, cssVar] of Object.entries(COLOR_VARS)) {
|
|
93
|
+
const v = theme.colors?.[key];
|
|
94
|
+
if (v) decls.push(`${cssVar}:${v};`);
|
|
95
|
+
}
|
|
96
|
+
if (theme.fonts?.display) decls.push(`--font-display:${theme.fonts.display};`);
|
|
97
|
+
if (theme.fonts?.body) decls.push(`--font-body:${theme.fonts.body};`);
|
|
98
|
+
let out = '';
|
|
99
|
+
for (const href of theme.fonts?.links || []) {
|
|
100
|
+
out += `<link rel="stylesheet" href="${href}">`;
|
|
101
|
+
}
|
|
102
|
+
// `backdrop` paints the page background (e.g. the Cloud Bongos sunrise gradient,
|
|
103
|
+
// ADR 0064 / R74) over the flat var(--bg); omitted packs (OTB) keep --bg.
|
|
104
|
+
const css = [];
|
|
105
|
+
if (decls.length) css.push(`:root{${decls.join('')}}`);
|
|
106
|
+
if (theme.backdrop) css.push(`body{background:${theme.backdrop};}`);
|
|
107
|
+
if (css.length) out += `<style>${css.join('')}</style>`;
|
|
108
|
+
if (theme.favicon) out += `<link rel="icon" href="${theme.favicon}">`;
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Inject the client-safe branding pack into a served HTML document (R56 task 1195
|
|
113
|
+
// + R57 task 1196, ADR 0062 §3):
|
|
114
|
+
// * `window.__BRANDING__` — a synchronous global so every hall/status script
|
|
115
|
+
// reads instance strings (currency, identity, copy, rank labels) from config
|
|
116
|
+
// with NO fetch, NO race, NO flash-of-default.
|
|
117
|
+
// * the THEME markup — a :root palette/font override <style>, the web-font
|
|
118
|
+
// <link>s, and the favicon <link>, so the LOOK comes from config too.
|
|
119
|
+
// Everything lands at the END of <head>: the script before any deferred page
|
|
120
|
+
// script runs, and the theme <style>/favicon AFTER the page's own stylesheet so
|
|
121
|
+
// the pack wins. Applied to the boot-time stamped HTML maps (the pack is a
|
|
122
|
+
// boot-time singleton, computed once). Off-box surfaces that can't be
|
|
123
|
+
// server-injected (the GitHub-Pages status page) read the same projection from
|
|
124
|
+
// GET /api/gds/public/branding. Pure + exported for the unit test.
|
|
125
|
+
function injectBrandingGlobal(html, brand = clientBranding(), mods = clientModules()) {
|
|
126
|
+
// window.__MODULES__ — the enabled feature-module set (R54 task 1193, ADR 0062
|
|
127
|
+
// §4), injected the same way as __BRANDING__ so hall/status scripts can hide
|
|
128
|
+
// UI sections for off modules synchronously (no fetch, no race). R60 wires the
|
|
129
|
+
// actual section-hiding; this exposes the data.
|
|
130
|
+
const stamped = applyBrandTokens(html, brand);
|
|
131
|
+
const tag = `${clientBundleScriptTag()}<script>window.__BRANDING__=${JSON.stringify(brand)};window.__MODULES__=${JSON.stringify(mods)};</script>${themeMarkup(brand.theme)}`;
|
|
132
|
+
return stamped.includes('</head>') ? stamped.replace('</head>', `${tag}</head>`) : tag + stamped;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Server-side stamp of instance-identity TOKENS into served HTML (task 1705, ADR
|
|
136
|
+
// 0062 §3). A page's static markup carries neutral placeholders instead of a
|
|
137
|
+
// hardcoded host brand, and this replaces them at serve time with the instance's
|
|
138
|
+
// own strings — so a non-OTB instance's <title>/copy render its own name, the
|
|
139
|
+
// no-JS path still works (server-rendered, not client-stamped), and the source
|
|
140
|
+
// stays brand-free (the fitness identity gate). Runs BEFORE the __BRANDING__
|
|
141
|
+
// script is inserted; the injected JSON holds resolved values (never tokens), so
|
|
142
|
+
// order is immaterial. Tokens: {{worldName}} {{productName}} {{currency}}
|
|
143
|
+
// (lowercase label) {{Currency}} (Capitalized) {{publicOrigin}} (the apex origin,
|
|
144
|
+
// for the one functional cross-instance link — sourced from the FULL pack's
|
|
145
|
+
// domains, which clientBranding deliberately omits). Pure + exported for the test;
|
|
146
|
+
// `origin` is injectable so the test drives it with zero I/O.
|
|
147
|
+
function applyBrandTokens(html, brand = clientBranding(), origin = safePublicOrigin()) {
|
|
148
|
+
const world = (brand.identity && brand.identity.worldName) || '';
|
|
149
|
+
const product = (brand.identity && brand.identity.productName) || '';
|
|
150
|
+
const cur = (brand.currency && brand.currency.label) || 'credits';
|
|
151
|
+
const Cur = cur ? cur.charAt(0).toUpperCase() + cur.slice(1) : cur;
|
|
152
|
+
return String(html == null ? '' : html)
|
|
153
|
+
.split('{{worldName}}').join(world)
|
|
154
|
+
.split('{{productName}}').join(product)
|
|
155
|
+
.split('{{Currency}}').join(Cur)
|
|
156
|
+
.split('{{currency}}').join(cur)
|
|
157
|
+
.split('{{publicOrigin}}').join(origin || '');
|
|
158
|
+
}
|
|
159
|
+
// The instance apex origin from the full branding pack (domains is server-only,
|
|
160
|
+
// so it never rides clientBranding). Best-effort so a branding hiccup can't crash
|
|
161
|
+
// serving.
|
|
162
|
+
function safePublicOrigin() {
|
|
163
|
+
try { return branding().domains.publicOrigin || ''; } catch { return ''; }
|
|
164
|
+
}
|
|
165
|
+
// Instance product/world name for the API-discovery index (task 1918). Best-effort:
|
|
166
|
+
// a branding hiccup must not break the JSON response.
|
|
167
|
+
function safeProductName() {
|
|
168
|
+
try {
|
|
169
|
+
const b = branding();
|
|
170
|
+
return (b.identity && (b.identity.productName || b.identity.worldName)) || 'Cloud Bongos';
|
|
171
|
+
} catch { return 'Cloud Bongos'; }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Apply injectBrandingGlobal across a whole stamped-HTML map ({ basename: html }).
|
|
175
|
+
function injectBrandingInto(stamped, brand = clientBranding()) {
|
|
176
|
+
const out = {};
|
|
177
|
+
for (const [name, html] of Object.entries(stamped)) out[name] = injectBrandingGlobal(html, brand);
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// The rendered API-docs page (task 1918) — public-docs/index.html wrapped with the
|
|
182
|
+
// instance branding (title/theme/favicon), memoized once at boot like the apex
|
|
183
|
+
// landing. '' when the instance ships no docs page (then /docs falls through to 404).
|
|
184
|
+
let _docsHtml;
|
|
185
|
+
function docsIndexHtml() {
|
|
186
|
+
if (_docsHtml === undefined) {
|
|
187
|
+
try {
|
|
188
|
+
_docsHtml = injectBrandingGlobal(fs.readFileSync(path.join(ROOT, 'public-docs/index.html'), 'utf8'));
|
|
189
|
+
} catch {
|
|
190
|
+
_docsHtml = '';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return _docsHtml;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// The browser-global build of the generated typed client (task 1996 / R09),
|
|
197
|
+
// served at /lib/bongos-client.js so classic <script> web pages can consume the
|
|
198
|
+
// same generated client the CLI uses (window.BongosClient). Read once at boot
|
|
199
|
+
// from clients/bongos-client/bongos-client.global.js — the SAME committed file
|
|
200
|
+
// the freshness test guards, so the browser can never drift from the spec. ''
|
|
201
|
+
// (→ 404) if the build is somehow absent, so a missing file fails loudly per page.
|
|
202
|
+
let _clientBundle;
|
|
203
|
+
function clientBundleJs() {
|
|
204
|
+
if (_clientBundle === undefined) {
|
|
205
|
+
try {
|
|
206
|
+
_clientBundle = fs.readFileSync(path.join(ROOT, 'clients/bongos-client/bongos-client.global.js'), 'utf8');
|
|
207
|
+
} catch {
|
|
208
|
+
_clientBundle = '';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return _clientBundle;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The <script> tag that loads the generated client on every internal page (task
|
|
215
|
+
// 1996 / R09), content-hashed so a client change busts the browser/Cloudflare
|
|
216
|
+
// cache exactly like the ?v= stamps on the hall's own scripts. Injected into
|
|
217
|
+
// <head> by injectBrandingGlobal so window.BongosClient is defined before any
|
|
218
|
+
// body-end page script runs. '' when the build is absent (pages then have no
|
|
219
|
+
// client and fail loudly on first use, same as a missing script anywhere).
|
|
220
|
+
let _clientBundleTag;
|
|
221
|
+
function clientBundleScriptTag() {
|
|
222
|
+
if (_clientBundleTag === undefined) {
|
|
223
|
+
const js = clientBundleJs();
|
|
224
|
+
_clientBundleTag = js
|
|
225
|
+
? `<script src="/lib/bongos-client.js?v=${require('node:crypto').createHash('sha256').update(js).digest('hex').slice(0, 8)}"></script>`
|
|
226
|
+
: '';
|
|
227
|
+
}
|
|
228
|
+
return _clientBundleTag;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Inject each ENABLED module's hall-widget client <script> into the hall index HTML
|
|
232
|
+
// (BV1.R75, ADR 0062 §4). The script must load BEFORE builders.js (which mounts the
|
|
233
|
+
// registered widgets) but AFTER hall-widgets.js/dom-utils.js (which define the
|
|
234
|
+
// registry) — so we insert just before the existing builders.js <script>. A disabled
|
|
235
|
+
// module's widget is never injected (it vanishes); contributeHallWidget is the second
|
|
236
|
+
// gate (it no-ops a widget whose module is off). `scripts` is the loader projection
|
|
237
|
+
// [{ key, src }]; default reads the live enabled set. Pure + exported for the test.
|
|
238
|
+
function injectModuleHallWidgets(html, scripts = hallWidgetScripts({ isEnabled: isModuleEnabled })) {
|
|
239
|
+
if (!Array.isArray(scripts) || scripts.length === 0) return html;
|
|
240
|
+
const tags = scripts
|
|
241
|
+
.map((s) => `<script src="${s.src}" defer></script>`)
|
|
242
|
+
.join('');
|
|
243
|
+
// Anchor on builders.js's <script> regardless of its ?v= stamp so the module
|
|
244
|
+
// widgets always land immediately before the consumer that mounts them.
|
|
245
|
+
const anchor = html.match(/<script src="\/builders\/builders\.js[^"]*"[^>]*><\/script>/);
|
|
246
|
+
if (anchor) return html.replace(anchor[0], tags + anchor[0]);
|
|
247
|
+
return html.includes('</body>') ? html.replace('</body>', `${tags}</body>`) : html + tags;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Host-based routing. status.<apex> gets the public dashboard (served by the
|
|
251
|
+
// status-ui MODULE's web surface, BV1.R84 — the generic webSurfaces loop below);
|
|
252
|
+
// builders.<apex> gets the builder surface (served by the hall-ui MODULE's web
|
|
253
|
+
// surface, BV1.R85 — its DEDICATED block below); the apex host gets the game
|
|
254
|
+
// (server.js) or — on a game-less instance — a plain 404 (platform-server.js). All
|
|
255
|
+
// share the same process + the same /api/gds/* surface (mounted host-agnostically
|
|
256
|
+
// below). The prefix checks are apex-agnostic (any domain), so no brand string lives
|
|
257
|
+
// here. isStatusHost + isBuildersHost stay the literal "status."/"builders."
|
|
258
|
+
// predicates the entrypoints (server.js/platform-server.js) use to keep their apex
|
|
259
|
+
// tail off those hosts; inside mountInternalSurfaces the GENERIC isModuleSurfaceHost
|
|
260
|
+
// (any enabled GENERIC web-surface host) is used to skip the status-style surfaces,
|
|
261
|
+
// while the builders host keeps its own isBuildersHost guard (it's served by the
|
|
262
|
+
// richer dedicated block, not the generic loop).
|
|
263
|
+
function isStatusHost(req) {
|
|
264
|
+
const host = (req.headers.host || '').toLowerCase().split(':')[0];
|
|
265
|
+
return host.startsWith('status.');
|
|
266
|
+
}
|
|
267
|
+
// A single-host instance — one with no dedicated builders.<apex> subdomain, e.g. a
|
|
268
|
+
// co-located instance on a one-level subdomain like emersonian-circles.cloudbongos.com
|
|
269
|
+
// (where free-plan TLS can't cover a two-level builders.<sub>.<apex>) — serves the hall
|
|
270
|
+
// on its OWN configured buildersOrigin host. Treating that configured host as a builders
|
|
271
|
+
// host makes the hall, not the apex landing, its front door (so /builders no longer
|
|
272
|
+
// 301s to a builders.<host> that was never provisioned). Back-compat: an instance whose
|
|
273
|
+
// buildersOrigin is the conventional builders.<apex> already matches the
|
|
274
|
+
// startsWith('builders.') branch, so its routing is byte-identical; the second branch
|
|
275
|
+
// only ever fires for an instance that deliberately points buildersOrigin at a
|
|
276
|
+
// non-"builders." host (the configurable-root / hosting-module direction — ADR 0108,
|
|
277
|
+
// tasks 1917/1485).
|
|
278
|
+
function configuredBuildersHost() {
|
|
279
|
+
try {
|
|
280
|
+
const bo = (branding().domains || {}).buildersOrigin || '';
|
|
281
|
+
return bo ? new URL(bo).host.toLowerCase().split(':')[0] : '';
|
|
282
|
+
} catch { return ''; }
|
|
283
|
+
}
|
|
284
|
+
function isBuildersHost(req) {
|
|
285
|
+
const host = (req.headers.host || '').toLowerCase().split(':')[0];
|
|
286
|
+
if (host.startsWith('builders.')) return true;
|
|
287
|
+
const cfg = configuredBuildersHost();
|
|
288
|
+
return cfg !== '' && host === cfg;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Mount the internal web surfaces onto `app`, in the exact order server.js used.
|
|
292
|
+
// Call this ONCE, right after app setup (trust proxy etc.) and BEFORE any
|
|
293
|
+
// entrypoint-specific tail (the game static + themed 404 in server.js; the plain
|
|
294
|
+
// 404 in platform-server.js).
|
|
295
|
+
function mountInternalSurfaces(app) {
|
|
296
|
+
// Prometheus instrumentation (task #1680, V4.R28a). The counting middleware
|
|
297
|
+
// mounts FIRST so it wraps every surface registered below (and, since
|
|
298
|
+
// mountInternalSurfaces runs before the game host blocks in server.js, the
|
|
299
|
+
// game routes too) — it records on res 'finish' regardless of which handler
|
|
300
|
+
// answers. The /metrics scrape endpoint is access-gated (loopback Prometheus
|
|
301
|
+
// or Bearer METRICS_TOKEN; see metrics.authorizeScrape).
|
|
302
|
+
app.use(metrics.httpMiddleware());
|
|
303
|
+
app.get('/metrics', metrics.metricsHandler());
|
|
304
|
+
|
|
305
|
+
// res.fail() decorator at the APP level (BV1 R04 / task 1991 / ADR 0116). The
|
|
306
|
+
// GDS router mounts its own attachFail, but a few app-level routes here (the
|
|
307
|
+
// /downloads/* redirect surface) also emit the error envelope via res.fail, and
|
|
308
|
+
// they run OUTSIDE that router — so decorate every response here too. Idempotent
|
|
309
|
+
// with the router's copy (it just re-installs the same method).
|
|
310
|
+
app.use(attachFail);
|
|
311
|
+
|
|
312
|
+
app.get('/healthz', (_req, res) => {
|
|
313
|
+
res.type('text/plain').send('ok');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Deployed build info (#858, ADR 0042). Public + read-only — exposes the git
|
|
317
|
+
// commit the running process was deployed from, so ship.js's ci-mode deploy
|
|
318
|
+
// confirmation can commit-PIN (deployedCommit === mergeCommit) rather than only
|
|
319
|
+
// checking liveness via /healthz. Low-sensitivity (a public commit sha).
|
|
320
|
+
app.get('/version', (_req, res) => {
|
|
321
|
+
res.json(buildInfo());
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Generated typed client for classic <script> web pages (task 1996 / R09).
|
|
325
|
+
// Host-agnostic and registered early so it wins over the host-routed surface
|
|
326
|
+
// middleware; every internal surface (hall, status) can load window.BongosClient
|
|
327
|
+
// from this one path. A short max-age + the page-side ?v= query bust the cache
|
|
328
|
+
// on client updates (the bytes are content-stable per deploy).
|
|
329
|
+
app.get('/lib/bongos-client.js', (_req, res) => {
|
|
330
|
+
const js = clientBundleJs();
|
|
331
|
+
if (!js) return res.status(404).type('text/plain').send('// bongos-client bundle not found on the server');
|
|
332
|
+
res.type('application/javascript');
|
|
333
|
+
res.set('Cache-Control', 'public, max-age=300');
|
|
334
|
+
res.send(js);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// GDS API (game development system; renamed from PMS in V3.R01). See
|
|
338
|
+
// docs/adr/0010-pms-architecture.md and limitations/pms-v1-shipped.md for
|
|
339
|
+
// the historical context (when the surface was still /api/pms).
|
|
340
|
+
//
|
|
341
|
+
// The transitional /api/pms/* → /api/gds/* redirect surface was removed in
|
|
342
|
+
// V3.R01-followup (#308, 2026-05-13) after a repo audit confirmed no caller
|
|
343
|
+
// still hits the legacy path. Read-side back-compat for the `pms_session`
|
|
344
|
+
// cookie, the `PMS_GITHUB_*` env vars, and the `~/.config/otb/pms-session.json`
|
|
345
|
+
// file path continues to live in src/bongos/auth.js + scripts/gds/cli-lib.js —
|
|
346
|
+
// those are independent of the URL surface and protect existing local
|
|
347
|
+
// sessions during the rollout.
|
|
348
|
+
// Machine-discovery index (task 1918): a bare GET on the API root answers with a
|
|
349
|
+
// small JSON map to the contract (the rendered docs + the OpenAPI spec) instead
|
|
350
|
+
// of a 404 — so an AI agent or a curious client that hits the base path finds the
|
|
351
|
+
// reference on its own. CORS-open + no auth (it names only public locations).
|
|
352
|
+
//
|
|
353
|
+
// Dual-mount (task 1919): the router + this discovery index are mounted at the
|
|
354
|
+
// canonical API_PREFIX (/api/bongos) AND every LEGACY_API_PREFIXES entry
|
|
355
|
+
// (/api/gds) — the legacy alias stays FOREVER so shipped Dev Box binaries, the
|
|
356
|
+
// cached status mirror, and live boxes keep working (src/bongos/api-prefix.js). The
|
|
357
|
+
// SAME router instance is mounted at each path. Each discovery GET is registered
|
|
358
|
+
// BEFORE its router mount so the exact base-path GET wins; every other
|
|
359
|
+
// method/subpath falls through to the router.
|
|
360
|
+
const gdsRouter = buildGdsRouter();
|
|
361
|
+
const discoveryIndex = (req, res) => {
|
|
362
|
+
res.set('Access-Control-Allow-Origin', '*');
|
|
363
|
+
res.set('Cache-Control', 'public, max-age=300');
|
|
364
|
+
// Docs-discovery contract (ADR 0114): advertise the instance's doc surfaces —
|
|
365
|
+
// the hall (canonical doc-reading surface), the status page (outage fallback),
|
|
366
|
+
// and the public origin — sourced from branding().domains, so a second instance
|
|
367
|
+
// resolves its own surfaces with no hard-coded host. null when unset.
|
|
368
|
+
const domains = (() => { try { return branding().domains || {}; } catch { return {}; } })();
|
|
369
|
+
res.json({
|
|
370
|
+
name: `${safeProductName()} — Bongos API`,
|
|
371
|
+
api_base: API_PREFIX, // unversioned canonical alias — always resolves to the current version, never breaks
|
|
372
|
+
api_version: API_VERSION, // the explicit CONTRACT version (ADR 0117)
|
|
373
|
+
api_base_versioned: VERSIONED_API_PREFIX, // '/api/bongos/v1' — pin here for a stable contract
|
|
374
|
+
docs: '/docs',
|
|
375
|
+
openapi: '/docs/openapi.json',
|
|
376
|
+
reference: 'docs/api-reference.md',
|
|
377
|
+
hall: domains.buildersOrigin || null,
|
|
378
|
+
status: domains.statusOrigin || null,
|
|
379
|
+
public: domains.publicOrigin || null,
|
|
380
|
+
ranks: ['public', 'any-builder', 'metic+archon', 'archon'],
|
|
381
|
+
note: 'Ranks are enforced server-side per request (ADR 0016). Open /docs for the full reference. The unversioned base + /api/gds are permanent aliases pinned to the current version (ADR 0117).',
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
// Mount the router + discovery index at EVERY prefix, most-specific first
|
|
385
|
+
// (ALL_API_PREFIXES: /api/bongos/v1, then /api/bongos, then /api/gds) so the
|
|
386
|
+
// versioned path is matched before the bare one (BV1 R06 / ADR 0117). The SAME
|
|
387
|
+
// router serves all three — the unversioned + legacy paths are permanent aliases
|
|
388
|
+
// pinned to v1.
|
|
389
|
+
for (const prefix of ALL_API_PREFIXES) {
|
|
390
|
+
app.get(prefix, discoveryIndex);
|
|
391
|
+
app.use(prefix, gdsRouter);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Dev Box desktop app downloads (#904, ADR 0045). Branded redirect surface:
|
|
395
|
+
// the installers live as release assets on the public releases repo (they
|
|
396
|
+
// can't ride the git deploy — see src/devbox-downloads.js), and these routes
|
|
397
|
+
// keep every builder-facing URL on the apex. Mounted right after the API and
|
|
398
|
+
// BEFORE the host shims so the same paths work on the apex AND on
|
|
399
|
+
// builders.<apex> (the builders rewrite shim below only prefixes non-/api
|
|
400
|
+
// paths, so without this placement /downloads/* would be rewritten to
|
|
401
|
+
// /builders/downloads/* and 404).
|
|
402
|
+
const devboxCfg = tryRequireConfig('../../config/devbox-app.json');
|
|
403
|
+
if (devboxCfg) {
|
|
404
|
+
const devboxDownloads = buildDevboxDownloads(devboxCfg);
|
|
405
|
+
app.get('/downloads/devbox/latest.json', (_req, res) => {
|
|
406
|
+
// Short TTL: a release bump (config change + deploy) is visible within a
|
|
407
|
+
// minute, same posture as the hall's HTML.
|
|
408
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
409
|
+
res.json(devboxDownloads.latestJson());
|
|
410
|
+
});
|
|
411
|
+
// #924: the lightweight update-check endpoint the Dev Box app polls + the hall
|
|
412
|
+
// /settings courtesy line reads. MUST be registered before the `:target` route
|
|
413
|
+
// below, or Express would treat "version" as a download target and 404 it.
|
|
414
|
+
app.get('/downloads/devbox/version', (_req, res) => {
|
|
415
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
416
|
+
res.json(devboxDownloads.versionJson());
|
|
417
|
+
});
|
|
418
|
+
app.get('/downloads/devbox/:target', (req, res) => {
|
|
419
|
+
const url = devboxDownloads.redirectFor(req.params.target);
|
|
420
|
+
if (!url) return res.fail('unknown_target', 404, { targets: Object.keys(devboxDownloads.latestJson().assets) });
|
|
421
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
422
|
+
res.redirect(302, url);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Standalone `bongos` CLI downloads + the one-line installer (task 1248,
|
|
427
|
+
// V4.R81). Same off-repo release pattern as the Dev Box block above: the
|
|
428
|
+
// compiled per-platform binaries live as release assets on the public releases
|
|
429
|
+
// repo (config/bongos-cli.json → src/bongos-downloads.js), and these routes keep
|
|
430
|
+
// every user-facing URL on the apex. The installer scripts are served with the
|
|
431
|
+
// server's PINNED origin substituted for __GDS_API_BASE__ — never a request
|
|
432
|
+
// header, so a spoofed Host can't redirect a `curl | sh` (the box-connect.sh rule).
|
|
433
|
+
const bongosCfg = tryRequireConfig('../../config/bongos-cli.json');
|
|
434
|
+
if (bongosCfg) {
|
|
435
|
+
const bongosDownloads = buildBongosDownloads(bongosCfg);
|
|
436
|
+
// Pinned public origin for the served installer scripts (never a request header —
|
|
437
|
+
// a spoofed Host can't redirect a `curl | sh`). The fallback comes from the
|
|
438
|
+
// branding pack, not a hardcoded host brand (ADR 0062 §3).
|
|
439
|
+
const BONGOS_PUBLIC_ORIGIN = process.env.GDS_PUBLIC_ORIGIN || branding().domains.publicOrigin;
|
|
440
|
+
const readBongosInstallScript = (name) => {
|
|
441
|
+
try {
|
|
442
|
+
return fs.readFileSync(path.join(ROOT, 'infra', name), 'utf8').split('__GDS_API_BASE__').join(BONGOS_PUBLIC_ORIGIN);
|
|
443
|
+
} catch { return null; }
|
|
444
|
+
};
|
|
445
|
+
app.get('/downloads/bongos/latest.json', (_req, res) => {
|
|
446
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
447
|
+
res.json(bongosDownloads.latestJson());
|
|
448
|
+
});
|
|
449
|
+
app.get('/downloads/bongos/version', (_req, res) => {
|
|
450
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
451
|
+
res.json(bongosDownloads.versionJson());
|
|
452
|
+
});
|
|
453
|
+
// The one-line installers — text/plain so `curl | sh` and `irm | iex` receive
|
|
454
|
+
// raw script bytes. Registered BEFORE the :target catch-all so their literal
|
|
455
|
+
// paths win (Express matches in registration order).
|
|
456
|
+
app.get('/downloads/bongos/install.sh', (_req, res) => {
|
|
457
|
+
const script = readBongosInstallScript('bongos-install.sh');
|
|
458
|
+
if (script == null) return res.status(404).type('text/plain').send('# bongos-install.sh not found on the server');
|
|
459
|
+
res.type('text/plain').send(script);
|
|
460
|
+
});
|
|
461
|
+
app.get('/downloads/bongos/install.ps1', (_req, res) => {
|
|
462
|
+
const script = readBongosInstallScript('bongos-install.ps1');
|
|
463
|
+
if (script == null) return res.status(404).type('text/plain').send('# bongos-install.ps1 not found on the server');
|
|
464
|
+
res.type('text/plain').send(script);
|
|
465
|
+
});
|
|
466
|
+
app.get('/downloads/bongos/:target', (req, res) => {
|
|
467
|
+
const url = bongosDownloads.redirectFor(req.params.target);
|
|
468
|
+
if (!url) return res.fail('unknown_target', 404, { targets: Object.keys(bongosDownloads.latestJson().assets) });
|
|
469
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
470
|
+
res.redirect(302, url);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Module-contributed HOST-ROUTED web surfaces (BV1.R84, ADR 0093 §1 /
|
|
475
|
+
// 02-module-map §6). A module that owns a web surface declares
|
|
476
|
+
// contributes.webSurfaces:[{ host, dir }] and ships the static dir at
|
|
477
|
+
// modules/<key>/<dir>/; the loader's moduleWebSurfaces() resolves the enabled set
|
|
478
|
+
// to [{ key, host, dir(abs) }]. This GENERALIZES the former hardcoded status-host
|
|
479
|
+
// block (the surface that moved to modules/status-ui/ in BV1.R84): serve-internal
|
|
480
|
+
// serves each surface on its Host prefix with the SAME machinery the status page
|
|
481
|
+
// always used — the in-memory content-hash-
|
|
482
|
+
// stamped index (#521, defeats Cloudflare's 4h TTL) + the per-instance branding/
|
|
483
|
+
// theme injection — and falls through to an on-disk static handler for assets.
|
|
484
|
+
// The status dashboard (modules/status-ui/, host "status.") is served here; a
|
|
485
|
+
// disabled module's surface is never built here, so its host routes nowhere
|
|
486
|
+
// (server.js/platform-server.js then 404 it on the apex tail). No module key is
|
|
487
|
+
// named — the kernel stays module-agnostic (ADR 0083 §Decision #4).
|
|
488
|
+
//
|
|
489
|
+
// The HALL (modules/hall-ui/, host "builders.", BV1.R85) is itself a declared
|
|
490
|
+
// web-surface module, but it is NOT served by this GENERIC static loop — it has
|
|
491
|
+
// far richer serving (the apex /builders/* shim, the canonical-URL redirects,
|
|
492
|
+
// the citizen/Archon page gates, asset-stamping per page, and the module
|
|
493
|
+
// hall-widget injection) in its DEDICATED block below. So we PARTITION the
|
|
494
|
+
// resolved set on the builders-host predicate: the generic loop owns every
|
|
495
|
+
// surface EXCEPT the hall; the dedicated block owns the hall. The filter is on
|
|
496
|
+
// the host (isBuildersHost), not the "hall-ui" key, so the kernel still names no
|
|
497
|
+
// module key — a future surface that wants the generic treatment just declares a
|
|
498
|
+
// non-builders host. (The webSurfaces manifest declaration still makes the hall a
|
|
499
|
+
// loader-DISCOVERED module — it feeds the scope map, the fitness keys, docs, and
|
|
500
|
+
// window.__MODULES__; it is the dedicated serving below that reads the bytes.)
|
|
501
|
+
const buildersHostReq = (host) => ({ headers: { host } });
|
|
502
|
+
const allWebSurfaces = moduleWebSurfaces({ isEnabled: isModuleEnabled });
|
|
503
|
+
const webSurfaces = allWebSurfaces
|
|
504
|
+
.filter((s) => !isBuildersHost(buildersHostReq(s.host))) // hall served by the dedicated block, not here
|
|
505
|
+
.map((s) => ({
|
|
506
|
+
key: s.key,
|
|
507
|
+
host: s.host,
|
|
508
|
+
static: express.static(s.dir, { extensions: ['html'], maxAge: '5m' }),
|
|
509
|
+
// Stamp + brand the index in memory at boot (a boot-time singleton, like the
|
|
510
|
+
// hall's). urlPrefix '/' because the surface is served at the host root.
|
|
511
|
+
html: injectBrandingInto(buildStampedHtml(s.dir, '/')),
|
|
512
|
+
}));
|
|
513
|
+
// True iff this request's Host matches a GENERIC module web surface (status-style,
|
|
514
|
+
// served by the loop below) — replaces the literal isStatusHost() guard the
|
|
515
|
+
// /builders middleware used, so those guards skip every generic surface host with
|
|
516
|
+
// zero hardcoded host string. The hall's own host is deliberately EXCLUDED (it was
|
|
517
|
+
// partitioned out above), so the dedicated /builders blocks below do NOT self-skip
|
|
518
|
+
// the builders host — they are what serves it.
|
|
519
|
+
function isModuleSurfaceHost(req) {
|
|
520
|
+
const host = (req.headers.host || '').toLowerCase().split(':')[0];
|
|
521
|
+
return webSurfaces.some((s) => host.startsWith(s.host));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
app.use((req, res, next) => {
|
|
525
|
+
const host = (req.headers.host || '').toLowerCase().split(':')[0];
|
|
526
|
+
const surface = webSurfaces.find((s) => host.startsWith(s.host));
|
|
527
|
+
if (!surface) return next();
|
|
528
|
+
// /api/* already handled above; everything else on a surface host serves from
|
|
529
|
+
// the module's static dir. The index page comes from the stamped in-memory copy
|
|
530
|
+
// (asset ?v= = content hash); everything else falls through to the on-disk
|
|
531
|
+
// static handler.
|
|
532
|
+
if ((req.method === 'GET' || req.method === 'HEAD') &&
|
|
533
|
+
(req.path === '/' || req.path === '/index.html') &&
|
|
534
|
+
surface.html['index.html']) {
|
|
535
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
536
|
+
res.type('html');
|
|
537
|
+
return res.send(surface.html['index.html']);
|
|
538
|
+
}
|
|
539
|
+
return surface.static(req, res, next);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// builders.<apex> canonical-URL redirect (#744). On the subdomain the
|
|
543
|
+
// /builders prefix is redundant — the page lives at the root. Old links/bookmarks
|
|
544
|
+
// (and the legacy in-page hrefs) point at /builders[/<page>]; 301 them to the
|
|
545
|
+
// clean path so the address bar never shows ".../builders". PAGE paths only —
|
|
546
|
+
// the slug is [a-z0-9-]+ with no dot, so asset URLs the pages emit
|
|
547
|
+
// (/builders/style.css, /builders/*.js, /builders/diagrams/*.png) and the API
|
|
548
|
+
// (/api/gds/builders/*) never match and pass through to the shim below.
|
|
549
|
+
const BUILDERS_CANONICAL_RE = /^\/builders(?:\/([a-z0-9-]+))?\/?$/i;
|
|
550
|
+
app.use((req, res, next) => {
|
|
551
|
+
if (!isBuildersHost(req)) return next();
|
|
552
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
553
|
+
const m = req.path.match(BUILDERS_CANONICAL_RE);
|
|
554
|
+
if (!m) return next();
|
|
555
|
+
const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
|
|
556
|
+
return res.redirect(301, `/${m[1] || ''}${qs}`); // /builders → /, /builders/watch → /watch
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// builders.<apex> rewrite shim (#726). The dedicated builder subdomain serves
|
|
560
|
+
// the SAME surface as the main host's /builders/* tree, just rooted at "/".
|
|
561
|
+
// Rather than duplicate the serving + gating + asset-stamping logic below, this
|
|
562
|
+
// shim rewrites every non-API path on the builders host onto the existing
|
|
563
|
+
// /builders/* path so all of that logic is reused verbatim:
|
|
564
|
+
// GET https://builders.<apex>/ → /builders/ (hall)
|
|
565
|
+
// GET https://builders.<apex>/watch → /builders/watch (Archon)
|
|
566
|
+
// GET https://builders.<apex>/settings → /builders/settings
|
|
567
|
+
// GET https://builders.<apex>/builders/style.css → unchanged
|
|
568
|
+
// Paths already under /builders pass through untouched, so absolute asset URLs
|
|
569
|
+
// the pages emit (/builders/style.css, /builders/*.js) resolve on this host too.
|
|
570
|
+
app.use((req, res, next) => {
|
|
571
|
+
if (!isBuildersHost(req)) return next();
|
|
572
|
+
if (req.path.startsWith('/api/') || req.path === '/healthz') return next();
|
|
573
|
+
if (req.path === '/builders' || req.path.startsWith('/builders/')) return next();
|
|
574
|
+
// Prefix with /builders so the handlers below match. req.url carries the
|
|
575
|
+
// query string; preserve it. '/' → '/builders/', '/watch' → '/builders/watch'.
|
|
576
|
+
req.url = '/builders/' + req.url.replace(/^\/+/, '');
|
|
577
|
+
next();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// #739 (ADR 0036 cutover): the builder surface's canonical home is now the
|
|
581
|
+
// subdomain. On the apex/game host, redirect /builders and /builders/* to
|
|
582
|
+
// builders.<apex>/<rest> so old links keep working while the subdomain becomes
|
|
583
|
+
// the front door. Runs ONLY on the apex (skipped on the status + builders
|
|
584
|
+
// hosts; /api is host-agnostic and mounted earlier, and never starts /builders).
|
|
585
|
+
// 301 (permanent) — the cutover is proven live + verified end-to-end (#741;
|
|
586
|
+
// started as a 302 in #739). The URL fragment (#/task/N deep-links) is preserved
|
|
587
|
+
// by the browser across the redirect; the query string is carried explicitly.
|
|
588
|
+
// The redirect target apex is read from the request host (strip a leading
|
|
589
|
+
// "builders."/"status." if present) so it stays brand-agnostic.
|
|
590
|
+
app.use((req, res, next) => {
|
|
591
|
+
if (isModuleSurfaceHost(req) || isBuildersHost(req)) return next();
|
|
592
|
+
if (req.path !== '/builders' && !req.path.startsWith('/builders/')) return next();
|
|
593
|
+
const apex = (req.headers.host || '').toLowerCase().split(':')[0];
|
|
594
|
+
const rest = req.path.replace(/^\/builders\/?/, ''); // '/builders'→'', '/builders/watch'→'watch'
|
|
595
|
+
const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
|
|
596
|
+
return res.redirect(301, `https://builders.${apex}/${rest}${qs}`);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// The /watch monitoring page and the /harbor dev-box management page (idea 257)
|
|
600
|
+
// and the /gate page (task 1179) are all Archon-only. Same server-side page
|
|
601
|
+
// gate, same posture: a non-Archon never receives the shell (requireArchonPage
|
|
602
|
+
// redirects a signed-in non-Archon to the hall, an unauthenticated visitor into
|
|
603
|
+
// sign-in). The subdomain shim above has already rewritten the bare
|
|
604
|
+
// /watch + /harbor + /gate onto /builders/*. (Defense in depth: every data
|
|
605
|
+
// endpoint these pages call is independently requireRank('archon').)
|
|
606
|
+
const WATCH_PAGE_RE = /^\/builders\/watch(?:\.html)?\/?$/;
|
|
607
|
+
const HARBOR_PAGE_RE = /^\/builders\/harbor(?:\.html)?\/?$/;
|
|
608
|
+
const GATE_PAGE_RE = /^\/builders\/gate(?:\.html)?\/?$/;
|
|
609
|
+
app.use((req, res, next) => {
|
|
610
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
611
|
+
if (!req.path.startsWith('/builders')) return next();
|
|
612
|
+
const norm = path.posix.normalize(req.path);
|
|
613
|
+
if (WATCH_PAGE_RE.test(norm) || HARBOR_PAGE_RE.test(norm) || GATE_PAGE_RE.test(norm)) {
|
|
614
|
+
return gdsAuth.requireArchonPage(req, res, next);
|
|
615
|
+
}
|
|
616
|
+
next();
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// The /builders surface — authenticated single-page UI for active builders.
|
|
620
|
+
// Served from the hall-ui module's public/ dir (modules/hall-ui/public/, carved
|
|
621
|
+
// out of the repo-root public-builders/ in BV1.R85). The Caddy reverse-proxy
|
|
622
|
+
// passes the request through; this handler short-circuits before any entrypoint
|
|
623
|
+
// tail so /builders/* does not fall into the game's public/.
|
|
624
|
+
//
|
|
625
|
+
// This is the hall's DEDICATED serving — the richer twin of the generic
|
|
626
|
+
// web-surface loop above (which the hall is partitioned out of): the apex
|
|
627
|
+
// /builders/* shim, the canonical-URL redirects, the citizen/Archon page gates,
|
|
628
|
+
// per-page asset-stamping, and the module hall-widget injection all live here and
|
|
629
|
+
// read from BUILDERS_DIR. The module key is NOT named — the path is the
|
|
630
|
+
// hall-ui module's public dir, resolved once here.
|
|
631
|
+
//
|
|
632
|
+
// Auth is handled client-side: the page calls /api/gds/me; on 401 it redirects
|
|
633
|
+
// the browser to /api/gds/auth/web/start, which (per src/bongos/routes/auth.js)
|
|
634
|
+
// loops back to /builders with a gds_session cookie set.
|
|
635
|
+
const BUILDERS_DIR = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
636
|
+
const buildersStatic = express.static(BUILDERS_DIR, {
|
|
637
|
+
extensions: ['html'],
|
|
638
|
+
setHeaders: (res, filePath) => {
|
|
639
|
+
if (/\.(html|js|json)$/.test(filePath)) {
|
|
640
|
+
res.setHeader('Cache-Control', 'public, max-age=60');
|
|
641
|
+
} else {
|
|
642
|
+
res.setHeader('Cache-Control', 'public, max-age=300');
|
|
643
|
+
}
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// Task #521: content-hash the hall HTML pages' asset ?v= queries at boot, in
|
|
648
|
+
// memory. This is the durable fix for the stale-asset bug — a changed
|
|
649
|
+
// builders.js/style.css gets a new ?v= automatically, so browsers + Cloudflare
|
|
650
|
+
// fetch it fresh instead of serving the pre-deploy bytes for hours. Keyed by
|
|
651
|
+
// basename; the page-serving middleware below maps a request path to a file.
|
|
652
|
+
const buildersHtml = injectBrandingInto(buildStampedHtml(BUILDERS_DIR, '/builders/'));
|
|
653
|
+
// BV1.R75 (ADR 0062 §4): inject each enabled module's hall-widget client script
|
|
654
|
+
// into the hall index page so a module's contributed UI section appears. No-op when
|
|
655
|
+
// no enabled module ships one (the default today). Only the index hall carries the
|
|
656
|
+
// registry-driven <main>, so this targets index.html alone.
|
|
657
|
+
if (buildersHtml['index.html']) {
|
|
658
|
+
buildersHtml['index.html'] = injectModuleHallWidgets(buildersHtml['index.html']);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Serve enabled modules' browser assets at /modules/<key>/public/* — the URL the
|
|
662
|
+
// loader's hallWidgetScripts projection points at. Mounted BEFORE the /builders
|
|
663
|
+
// static handler. Gated per-request on the live enabled set so a disabled module's
|
|
664
|
+
// assets 404 (defense in depth behind the no-injection above). Same Cache-Control
|
|
665
|
+
// posture as the hall's own scripts.
|
|
666
|
+
const MODULE_ASSET_RE = /^\/modules\/([a-z][a-z0-9-]*)\/public\/(.+)$/;
|
|
667
|
+
const MODULES_ROOT = path.join(ROOT, 'modules');
|
|
668
|
+
app.get(MODULE_ASSET_RE, (req, res, next) => {
|
|
669
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
670
|
+
const m = req.path.match(MODULE_ASSET_RE);
|
|
671
|
+
const key = m[1];
|
|
672
|
+
let enabled = false;
|
|
673
|
+
try { enabled = isModuleEnabled(key); } catch { enabled = false; } // unknown key → not enabled
|
|
674
|
+
if (!enabled) return res.status(404).type('text/plain').send('not found');
|
|
675
|
+
// Normalize the asset sub-path to keep it inside modules/<key>/public.
|
|
676
|
+
const rel = path.posix.normalize(m[2]);
|
|
677
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) return res.status(404).type('text/plain').send('not found');
|
|
678
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
679
|
+
return res.sendFile(path.join(MODULES_ROOT, key, 'public', rel), (err) => {
|
|
680
|
+
if (err) { if (!res.headersSent) res.status(404).type('text/plain').send('not found'); }
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// Resolve a /builders/* request path to a stamped HTML basename, mirroring
|
|
685
|
+
// express.static's `extensions: ['html']` resolution. Returns null for any
|
|
686
|
+
// path that isn't one of our HTML pages (assets, unknown paths) so it falls
|
|
687
|
+
// through to the static handler.
|
|
688
|
+
const BUILDERS_PAGE_RE = /^\/builders\/([a-z0-9-]+)(?:\.html)?\/?$/i;
|
|
689
|
+
// task 751: the task-detail page is the one hall page addressed by a SECOND
|
|
690
|
+
// path segment (an id) rather than a bare slug — /builders/task/:id — so it
|
|
691
|
+
// needs its own two-segment match ahead of the single-segment resolution
|
|
692
|
+
// below. Same gate tier as Work/Roadmap/Goals (SIGNEDIN_BUILDERS_PAGE_RE).
|
|
693
|
+
const TASK_DETAIL_PAGE_RE = /^\/builders\/task\/(\d+)\/?$/;
|
|
694
|
+
function buildersHtmlName(reqPath) {
|
|
695
|
+
if (reqPath === '/builders' || reqPath === '/builders/') return 'index.html';
|
|
696
|
+
if (TASK_DETAIL_PAGE_RE.test(reqPath)) return buildersHtml['task.html'] ? 'task.html' : null;
|
|
697
|
+
// e.g. /builders/ranks or /builders/ranks.html → ranks.html
|
|
698
|
+
const m = reqPath.match(BUILDERS_PAGE_RE);
|
|
699
|
+
if (m) {
|
|
700
|
+
const name = `${m[1].toLowerCase()}.html`;
|
|
701
|
+
if (buildersHtml[name]) return name;
|
|
702
|
+
}
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// #488: server-side sign-in gate for the citizen primer + the system-maps.
|
|
707
|
+
// These pages — and their content assets (the primer markdown + the diagram
|
|
708
|
+
// PNGs) — are citizen-only: an unauthenticated request must NOT be served the
|
|
709
|
+
// file. Mounted BEFORE every /builders static handler below so the gate runs
|
|
710
|
+
// first; gdsAuth.requireBuilderPage redirects an unauthenticated request into
|
|
711
|
+
// the web sign-in flow instead of returning the content. This is the real
|
|
712
|
+
// access control (curl-proof / no-JS-proof) — the link-hiding in builders.js
|
|
713
|
+
// is only cosmetic.
|
|
714
|
+
//
|
|
715
|
+
// Scope is deliberately narrow: the hall index (/builders), the shared assets
|
|
716
|
+
// (style.css, *.js, fonts, cursors), the not-ready notice, and the sign-in flow
|
|
717
|
+
// itself stay PUBLIC so the landing card + GitHub button render for a stranger.
|
|
718
|
+
//
|
|
719
|
+
// Two tiers of gate (#770):
|
|
720
|
+
// * NON-XENOS (Thetes+) — the orientation reading rooms (primer, diagrams).
|
|
721
|
+
// A Xenos's path is action-only; an unauth visitor is sent to sign-in, a
|
|
722
|
+
// Xenos to the friendly /builders/not-ready notice.
|
|
723
|
+
// * ANY SIGNED-IN BUILDER — the Work Board, Roadmap, the Goals page, the
|
|
724
|
+
// task-detail page, the Repo Atlas, the Dev Box pair page,
|
|
725
|
+
// and Settings (#748, #904, task 1293, task 1757, task 751): a Xenos IS
|
|
726
|
+
// allowed, but an unauthenticated visitor is bounced into sign-in rather
|
|
727
|
+
// than served the page shell. Settings sits in THIS tier (not the
|
|
728
|
+
// reading-room tier) because a newcomer needs it for first-time setup —
|
|
729
|
+
// CLI re-auth (the "CLI Access" section) and choosing their craft both
|
|
730
|
+
// live there (task 1291). It must still stay behind sign-in: it shows
|
|
731
|
+
// personal prefs and mints CLI tokens.
|
|
732
|
+
// The link-hiding in builders.js is only cosmetic; these are the real gates.
|
|
733
|
+
const NONXENOS_BUILDERS_PAGE_RE =
|
|
734
|
+
/^\/builders\/(?:primer(?:\.md|\.html)?|diagrams(?:\.html)?)\/?$/;
|
|
735
|
+
const NONXENOS_BUILDERS_PREFIX_RE = /^\/builders\/diagrams\//;
|
|
736
|
+
const SIGNEDIN_BUILDERS_PAGE_RE = /^\/builders\/(?:work|pair|settings|atlas|modules|goals|roadmap)(?:\.html)?\/?$/;
|
|
737
|
+
app.use((req, res, next) => {
|
|
738
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
739
|
+
// Cheap early-out: only /builders/* paths can ever be gated, so skip the
|
|
740
|
+
// work entirely for game / API / asset requests.
|
|
741
|
+
if (!req.path.startsWith('/builders')) return next();
|
|
742
|
+
// Match the NORMALIZED path. express.static (send) calls path.normalize on
|
|
743
|
+
// the sub-path before resolving the file, so '/builders//primer' and
|
|
744
|
+
// '/builders/./primer' both serve primer.html — but those raw forms slip
|
|
745
|
+
// past anchored regexes. Normalizing here (collapse '//', resolve './..')
|
|
746
|
+
// closes that bypass so the gate holds at the application layer, not just
|
|
747
|
+
// behind the proxy's path normalization.
|
|
748
|
+
const norm = path.posix.normalize(req.path);
|
|
749
|
+
if (NONXENOS_BUILDERS_PAGE_RE.test(norm) || NONXENOS_BUILDERS_PREFIX_RE.test(norm)) {
|
|
750
|
+
return gdsAuth.requireNonXenosPage(req, res, next);
|
|
751
|
+
}
|
|
752
|
+
if (SIGNEDIN_BUILDERS_PAGE_RE.test(norm) || TASK_DETAIL_PAGE_RE.test(norm)) {
|
|
753
|
+
return gdsAuth.requireBuilderPage(req, res, next);
|
|
754
|
+
}
|
|
755
|
+
next();
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// V3.R13-followup-2 (#411): /builders/diagrams/ (with trailing slash) used to
|
|
759
|
+
// fall through to the game's catch-all because express.static can't resolve a
|
|
760
|
+
// directory request without an index. Redirect to /builders/diagrams (no slash)
|
|
761
|
+
// so the diagrams.html page actually renders. The no-slash form already works
|
|
762
|
+
// via the extensions: ['html'] resolution on buildersStatic.
|
|
763
|
+
//
|
|
764
|
+
// Implementation notes:
|
|
765
|
+
// - REGEX route, not a string. With Express's default non-strict routing,
|
|
766
|
+
// the string form '/builders/diagrams/' matches both '/.../diagrams' AND
|
|
767
|
+
// '/.../diagrams/', which would cause a redirect loop. Anchoring with
|
|
768
|
+
// ^…$ + a literal trailing slash gates the match to the with-slash form.
|
|
769
|
+
// - Registered BEFORE the diagramsStatic wrapper below — that wrapper
|
|
770
|
+
// mutates req.url for /builders/diagrams/* paths, which would prevent
|
|
771
|
+
// this exact-path redirect from matching if it ran first.
|
|
772
|
+
app.get(/^\/builders\/diagrams\/$/, (req, res, next) => {
|
|
773
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
774
|
+
res.redirect(301, '/builders/diagrams');
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
// task 1965: the read-only Task Board (/board, task 751) was consolidated into
|
|
778
|
+
// the Work page (/work) — the hall keeps one task-listing surface. 301 old
|
|
779
|
+
// bookmarks + deep-links (the apex /builders/board and the bare subdomain
|
|
780
|
+
// /board form, with or without a .html suffix) to the surviving Work page,
|
|
781
|
+
// preserving the /builders prefix and any query string.
|
|
782
|
+
app.get(/^(?:\/builders)?\/board(?:\.html)?\/?$/, (req, res, next) => {
|
|
783
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
784
|
+
const prefix = req.path.startsWith('/builders') ? '/builders' : '';
|
|
785
|
+
const qi = req.url.indexOf('?');
|
|
786
|
+
const qs = qi >= 0 ? req.url.slice(qi) : '';
|
|
787
|
+
res.redirect(301, `${prefix}/work${qs}`);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// Onboarding diagram PNGs (V3.R76 #295) live in docs/onboarding/diagrams as the
|
|
791
|
+
// single source of truth alongside their Mermaid sources. Serve them here so the
|
|
792
|
+
// builders'-hall diagrams page can embed them without duplicating the binaries.
|
|
793
|
+
// Mounted before buildersStatic so /builders/diagrams/*.png resolves to docs/.
|
|
794
|
+
const diagramsStatic = express.static(path.join(ROOT, 'docs/onboarding/diagrams'), {
|
|
795
|
+
setHeaders: (res) => res.setHeader('Cache-Control', 'public, max-age=300'),
|
|
796
|
+
});
|
|
797
|
+
app.use((req, res, next) => {
|
|
798
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
799
|
+
if (req.path.startsWith('/builders/diagrams/')) {
|
|
800
|
+
req.url = req.url.replace(/^\/builders\/diagrams\/?/, '/') || '/';
|
|
801
|
+
return diagramsStatic(req, res, next);
|
|
802
|
+
}
|
|
803
|
+
next();
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// Citizen's primer (V3.R13-followup #406) — the long-form reading source for
|
|
807
|
+
// the /builders/primer page. The canonical markdown lives at
|
|
808
|
+
// docs/onboarding/primer.md (single source of truth, also linked from the CLI
|
|
809
|
+
// nudges + the repo). Mounted as a single static file so primer.js can fetch
|
|
810
|
+
// and render it client-side without a build step. Same mount pattern as
|
|
811
|
+
// diagramsStatic above.
|
|
812
|
+
app.get('/builders/primer.md', (req, res, next) => {
|
|
813
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
814
|
+
res.set('Cache-Control', 'public, max-age=300');
|
|
815
|
+
res.set('Content-Type', 'text/markdown; charset=utf-8');
|
|
816
|
+
res.sendFile(path.join(ROOT, 'docs/onboarding/primer.md'));
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// Drachmae & karma reader (task 1472) — the builder-facing "how you get paid"
|
|
820
|
+
// doc. The canonical markdown lives at docs/onboarding/drachmae-and-karma.md
|
|
821
|
+
// (single source of truth, also linked from the primer + the repo); the
|
|
822
|
+
// /builders/drachmae page (drachmae.js + md-reader.js) fetches + renders it
|
|
823
|
+
// client-side. Same static-file mount pattern as primer.md above; public
|
|
824
|
+
// like the /ranks page — economy transparency, no sign-in gate.
|
|
825
|
+
app.get('/builders/drachmae-and-karma.md', (req, res, next) => {
|
|
826
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
827
|
+
res.set('Cache-Control', 'public, max-age=300');
|
|
828
|
+
res.set('Content-Type', 'text/markdown; charset=utf-8');
|
|
829
|
+
res.sendFile(path.join(ROOT, 'docs/onboarding/drachmae-and-karma.md'));
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// Rendered API docs (task 1918, BONGOS-V1). A self-describing OpenAPI 3.1 site
|
|
833
|
+
// at /docs — cloudbongos.com/docs is the advertised canonical URL, and it works
|
|
834
|
+
// on the apex of every instance (mounted in the SHARED surface, so a self-hosted
|
|
835
|
+
// Cloud Bongos is self-documenting for free). Served on the apex host; the
|
|
836
|
+
// status./builders. module hosts skip it (isModuleSurfaceHost). The renderer
|
|
837
|
+
// (Redoc) + the generated spec are vendored/generated static assets — no CDN, no
|
|
838
|
+
// external fetch (matches the /vendor/* self-hosting convention; CSP-safe).
|
|
839
|
+
// GET /docs → the Redoc host page (branding-injected)
|
|
840
|
+
// GET /docs/redoc.standalone.js → the vendored renderer (long-cached)
|
|
841
|
+
// GET /docs/openapi.json → the generated spec (CORS-open so Postman/Swagger/AI can fetch it)
|
|
842
|
+
app.get('/docs', (req, res, next) => {
|
|
843
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
844
|
+
const html = docsIndexHtml();
|
|
845
|
+
if (!html) return next(); // instance ships no docs page → fall through to 404
|
|
846
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
847
|
+
res.type('html');
|
|
848
|
+
res.send(html);
|
|
849
|
+
});
|
|
850
|
+
app.get('/docs/redoc.standalone.js', (req, res, next) => {
|
|
851
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
852
|
+
res.set('Cache-Control', 'public, max-age=86400');
|
|
853
|
+
res.type('application/javascript');
|
|
854
|
+
res.sendFile(path.join(ROOT, 'public-docs/redoc.standalone.js'));
|
|
855
|
+
});
|
|
856
|
+
app.get('/docs/openapi.json', (req, res, next) => {
|
|
857
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
858
|
+
res.set('Access-Control-Allow-Origin', '*'); // read-only spec — tools fetch it cross-origin
|
|
859
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
860
|
+
res.type('application/json');
|
|
861
|
+
res.sendFile(path.join(ROOT, 'docs/api/openapi.json'));
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
app.use((req, res, next) => {
|
|
865
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
866
|
+
if (req.path === '/builders' || req.path.startsWith('/builders/')) {
|
|
867
|
+
// Serve HTML pages from the stamped in-memory copy (asset ?v= = content
|
|
868
|
+
// hash). Gated pages (primer, diagrams) have already passed the sign-in
|
|
869
|
+
// gate above before reaching here. Everything else (js/css/png/json,
|
|
870
|
+
// unknown paths) falls through to the on-disk static handler.
|
|
871
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
872
|
+
const htmlName = buildersHtmlName(req.path);
|
|
873
|
+
if (htmlName) {
|
|
874
|
+
res.set('Cache-Control', 'public, max-age=60');
|
|
875
|
+
res.type('html');
|
|
876
|
+
return res.send(buildersHtml[htmlName]);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
// Strip the /builders prefix so index.html resolves at the root.
|
|
880
|
+
req.url = req.url.replace(/^\/builders\/?/, '/') || '/';
|
|
881
|
+
return buildersStatic(req, res, next);
|
|
882
|
+
}
|
|
883
|
+
next();
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
return app;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
module.exports = { mountInternalSurfaces, isStatusHost, isBuildersHost, injectBrandingGlobal, injectBrandingInto, injectModuleHallWidgets, themeMarkup, applyBrandTokens };
|