@bongos/core 1.18.0 → 1.18.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +1740 -640
- package/.claude/skills/builder-box/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +97 -0
- package/.claude/skills/builder-ship/SKILL.md +8 -2
- package/.claude/skills/builder-start/SKILL.md +11 -0
- package/.claude/skills/builder-sync/SKILL.md +41 -0
- package/.claude/skills/fix-task/SKILL.md +64 -0
- package/.claude/skills/goal-create/SKILL.md +158 -0
- package/.claude/skills/goal-review/SKILL.md +3 -1
- package/.claude/skills/idea-triage/SKILL.md +14 -8
- package/.claude/skills/merge-mode/SKILL.md +2 -2
- package/.claude/skills/new-project/SKILL.md +1 -0
- package/.claude/skills/planning-session/SKILL.md +45 -18
- package/.env.local.example +4 -3
- package/README.md +5 -3
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +82 -18
- package/clients/bongos-client/index.cjs +82 -18
- package/clients/bongos-client/index.d.ts +131 -29
- package/clients/bongos-client/index.mjs +82 -18
- package/config/branding.neutral.json +11 -2
- package/config/governance.neutral.json +21 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
- package/docs/adr/0090-metic-task-authoring.md +1 -1
- package/docs/adr/0142-project-context-is-prose-only.md +41 -0
- package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +35 -0
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
- package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
- package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
- package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
- package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
- package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
- package/docs/adr/0152-metic-task-abandonment.md +67 -0
- package/docs/adr/0153-sequential-chain-runner.md +51 -0
- package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
- package/docs/adr/README.md +15 -2
- package/docs/api/openapi.json +2648 -483
- package/docs/api-reference.md +73 -13
- package/docs/architecture.md +4 -2
- package/docs/branding-contract.md +3 -1
- package/docs/canonical-permissions.md +21 -8
- package/docs/file-map.md +15 -7
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
- package/docs/onboarding/diagrams/assertions.json +4 -7
- package/docs/onboarding/drachmae-and-karma.md +3 -3
- package/docs/onboarding/primer.md +17 -16
- package/docs/onboarding/slash-commands.md +124 -0
- package/docs/recipes/builder-box-lifecycle.md +23 -0
- package/docs/recipes/devbox-app-release.md +14 -4
- package/docs/recipes/instance-first-run.md +16 -0
- package/docs/recipes/local-dev.md +81 -0
- package/docs/recipes/ops-gotchas.md +19 -1
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/packaging-the-core.md +13 -0
- package/docs/recipes/parallel-goal-waves.md +174 -0
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +48 -0
- package/docs/recipes/upgrading-the-core.md +5 -2
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
- package/migrations/003_pms.sql +6 -7
- package/migrations/006_pms_v2.sql +3 -31
- package/migrations/039_newcomer_friendly_column.sql +5 -0
- package/migrations/102_remove_box_first_provision_approval.sql +4 -0
- package/migrations/core_192_github_oauth_tokens.sql +33 -0
- package/migrations/core_193_task_needs_rebase.sql +28 -0
- package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
- package/migrations/core_195_idea_triage_provenance.sql +57 -0
- package/migrations/core_196_session_reward_topup.sql +69 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -8
- package/modules/builder-settings/wandering-prefs.js +61 -18
- package/modules/dev-box/app/README.md +73 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
- package/modules/dev-box/app/electron-builder.yml +95 -0
- package/modules/dev-box/app/package-lock.json +5291 -0
- package/modules/dev-box/app/package.json +30 -0
- package/modules/dev-box/app/renderer/app.js +319 -0
- package/modules/dev-box/app/renderer/index.html +45 -0
- package/modules/dev-box/app/renderer/styles.css +246 -0
- package/modules/dev-box/app/renderer/theme.generated.css +16 -0
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
- package/modules/dev-box/app/scripts/gen-brand.js +138 -0
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
- package/modules/dev-box/app/scripts/gen-icons.js +286 -0
- package/modules/dev-box/app/scripts/vendor-client.js +23 -0
- package/modules/dev-box/app/src/box-controller.js +287 -0
- package/modules/dev-box/app/src/brand.generated.json +13 -0
- package/modules/dev-box/app/src/brand.js +16 -0
- package/modules/dev-box/app/src/config.js +19 -0
- package/modules/dev-box/app/src/gds-client.js +90 -0
- package/modules/dev-box/app/src/main.js +610 -0
- package/modules/dev-box/app/src/pairing.js +57 -0
- package/modules/dev-box/app/src/preload.js +29 -0
- package/modules/dev-box/app/src/session-store.js +58 -0
- package/modules/dev-box/app/src/ssh-setup.js +569 -0
- package/modules/dev-box/app/src/task-picker.js +70 -0
- package/modules/dev-box/app/src/tray.js +99 -0
- package/modules/dev-box/app/src/updater.js +55 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
- package/modules/dev-box/app/test/unit.mjs +526 -0
- package/modules/dev-box/box-access.js +121 -4
- package/modules/dev-box/boxes.js +20 -0
- package/modules/dev-box/module.json +1 -1
- package/modules/dev-box/routes/box.js +87 -3
- package/modules/economy/credits.js +116 -25
- package/modules/economy/reward.js +8 -0
- package/modules/economy/streak.js +2 -1
- package/modules/governance/CLAUDE.md +15 -0
- package/modules/governance/catalog.js +419 -0
- package/modules/governance/config.js +174 -0
- package/modules/governance/db.js +125 -0
- package/modules/governance/migrations/governance_001_tables.sql +81 -0
- package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
- package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
- package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
- package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
- package/modules/governance/module.json +10 -0
- package/modules/governance/protected-surfaces.js +88 -0
- package/modules/governance/protected-surfaces.json +80 -0
- package/modules/governance/rank-roles.js +83 -0
- package/modules/governance/resolver.js +221 -0
- package/modules/governance/routes/governance.js +89 -0
- package/modules/governance/routes/role-sync.js +49 -0
- package/modules/governance/session-scopes.js +127 -0
- package/modules/governance/session-scopes.json +38 -0
- package/modules/hall-ui/public/atlas.css +117 -0
- package/modules/hall-ui/public/atlas.html +3 -114
- package/modules/hall-ui/public/brand-apply.js +34 -6
- package/modules/hall-ui/public/builders.js +187 -219
- package/modules/hall-ui/public/diagrams.html +2 -0
- package/modules/hall-ui/public/dom-utils.js +27 -0
- package/modules/hall-ui/public/drachmae.html +2 -0
- package/modules/hall-ui/public/drachmae.js +54 -1
- package/modules/hall-ui/public/gate.css +98 -0
- package/modules/hall-ui/public/gate.html +3 -95
- package/modules/hall-ui/public/goal-inbox.js +4 -1
- package/modules/hall-ui/public/goals-page.js +626 -114
- package/modules/hall-ui/public/goals.css +79 -1
- package/modules/hall-ui/public/goals.html +38 -11
- package/modules/hall-ui/public/hall-widgets.js +62 -52
- package/modules/hall-ui/public/harbor.css +123 -0
- package/modules/hall-ui/public/harbor.html +3 -120
- package/modules/hall-ui/public/index.html +11 -4
- package/modules/hall-ui/public/instances-panel.js +4 -1
- package/modules/hall-ui/public/modules.css +63 -0
- package/modules/hall-ui/public/modules.html +3 -60
- package/modules/hall-ui/public/my-projects.html +78 -0
- package/modules/hall-ui/public/my-projects.js +142 -0
- package/modules/hall-ui/public/new-project-wizard.js +7 -3
- package/modules/hall-ui/public/not-ready.html +2 -0
- package/modules/hall-ui/public/pair.css +73 -0
- package/modules/hall-ui/public/pair.html +3 -70
- package/modules/hall-ui/public/palette.js +302 -0
- package/modules/hall-ui/public/primer.css +13 -0
- package/modules/hall-ui/public/primer.html +2 -0
- package/modules/hall-ui/public/profile.html +2 -0
- package/modules/hall-ui/public/ranks.html +2 -0
- package/modules/hall-ui/public/roadmap.css +179 -0
- package/modules/hall-ui/public/roadmap.html +9 -172
- package/modules/hall-ui/public/roadmap.js +329 -316
- package/modules/hall-ui/public/scouting.html +45 -0
- package/modules/hall-ui/public/scouting.js +73 -0
- package/modules/hall-ui/public/sessions.css +16 -0
- package/modules/hall-ui/public/sessions.html +3 -13
- package/modules/hall-ui/public/settings.html +2 -0
- package/modules/hall-ui/public/settings.js +4 -4
- package/modules/hall-ui/public/shell.js +143 -18
- package/modules/hall-ui/public/style.css +231 -39
- package/modules/hall-ui/public/task-detail.js +347 -26
- package/modules/hall-ui/public/task.html +6 -1
- package/modules/hall-ui/public/watch.html +2 -0
- package/modules/hall-ui/public/work.html +8 -6
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/ideas/inbox.js +61 -11
- package/modules/ideas/routes/inbox.js +23 -0
- package/modules/lifecycle/claim-eligibility.js +72 -0
- package/modules/lifecycle/db.js +519 -139
- package/modules/lifecycle/done-when.js +18 -3
- package/modules/lifecycle/github-push.js +19 -2
- package/modules/lifecycle/goal-advisory.js +114 -0
- package/modules/lifecycle/goal-authz.js +62 -0
- package/modules/lifecycle/lifecycle.js +9 -0
- package/modules/lifecycle/publish-reconciler.js +109 -6
- package/modules/lifecycle/routes/claims.js +68 -23
- package/modules/lifecycle/routes/done-when.js +110 -15
- package/modules/lifecycle/routes/goals.js +8 -38
- package/modules/lifecycle/routes/tasks.js +38 -6
- package/modules/lifecycle/ship-card.js +6 -0
- package/modules/onboarding/onboarding-state.js +1 -1
- package/modules/platform-identity/CLAUDE.md +15 -0
- package/modules/platform-identity/hub-device.js +147 -0
- package/modules/platform-identity/hub-keys.js +151 -0
- package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
- package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
- package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
- package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
- package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
- package/modules/platform-identity/module.json +18 -0
- package/modules/platform-identity/platform-identity.js +531 -0
- package/modules/platform-identity/pollers/code-sweep.js +43 -0
- package/modules/platform-identity/routes/my-projects.js +140 -0
- package/modules/platform-identity/routes/projects.js +107 -0
- package/modules/platform-identity/routes/scouting.js +35 -0
- package/modules/platform-identity/routes/sso.js +402 -0
- package/modules/platform-identity/tests/platform-identity.mjs +388 -0
- package/modules/platform-identity/ui/.gitkeep +0 -0
- package/modules/security/routes/reports.js +10 -0
- package/modules/sessions/db.js +166 -21
- package/modules/sessions/module.json +1 -1
- package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
- package/modules/sessions/routes/sessions.js +33 -0
- package/modules/status-ui/public/explore.html +54 -0
- package/modules/status-ui/public/explore.js +67 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/anim/idle/frame_00.webp +0 -0
- package/public-landing/assets/anim/idle/frame_01.webp +0 -0
- package/public-landing/assets/anim/idle/frame_02.webp +0 -0
- package/public-landing/assets/anim/idle/frame_03.webp +0 -0
- package/public-landing/assets/anim/idle/frame_04.webp +0 -0
- package/public-landing/assets/anim/idle/frame_05.webp +0 -0
- package/public-landing/assets/anim/idle/frame_06.webp +0 -0
- package/public-landing/assets/anim/idle/frame_07.webp +0 -0
- package/public-landing/assets/anim/idle/frame_08.webp +0 -0
- package/public-landing/assets/anim/idle/frame_09.webp +0 -0
- package/public-landing/assets/anim/idle/frame_10.webp +0 -0
- package/public-landing/assets/anim/idle/frame_11.webp +0 -0
- package/public-landing/assets/anim/idle/frame_12.webp +0 -0
- package/public-landing/assets/anim/idle/frame_13.webp +0 -0
- package/public-landing/assets/anim/idle/frame_14.webp +0 -0
- package/public-landing/assets/anim/idle/frame_15.webp +0 -0
- package/public-landing/assets/anim/idle/frame_16.webp +0 -0
- package/public-landing/assets/anim/idle/frame_17.webp +0 -0
- package/public-landing/assets/anim/idle/frame_18.webp +0 -0
- package/public-landing/assets/anim/idle/frame_19.webp +0 -0
- package/public-landing/assets/anim/idle/frame_20.webp +0 -0
- package/public-landing/assets/anim/idle/frame_21.webp +0 -0
- package/public-landing/assets/anim/idle/frame_22.webp +0 -0
- package/public-landing/assets/anim/idle/frame_23.webp +0 -0
- package/public-landing/assets/anim/idle/frame_24.webp +0 -0
- package/public-landing/assets/anim/idle/frame_25.webp +0 -0
- package/public-landing/assets/anim/idle/frame_26.webp +0 -0
- package/public-landing/assets/anim/idle/frame_27.webp +0 -0
- package/public-landing/assets/anim/idle/frame_28.webp +0 -0
- package/public-landing/assets/anim/idle/frame_29.webp +0 -0
- package/public-landing/assets/anim/idle/frame_30.webp +0 -0
- package/public-landing/assets/anim/idle/frame_31.webp +0 -0
- package/public-landing/assets/anim/idle/frame_32.webp +0 -0
- package/public-landing/assets/anim/idle/frame_33.webp +0 -0
- package/public-landing/assets/anim/idle/frame_34.webp +0 -0
- package/public-landing/assets/anim/idle/frame_35.webp +0 -0
- package/public-landing/assets/anim/idle/frame_36.webp +0 -0
- package/public-landing/assets/anim/idle/frame_37.webp +0 -0
- package/public-landing/assets/anim/idle/frame_38.webp +0 -0
- package/public-landing/assets/anim/idle/frame_39.webp +0 -0
- package/public-landing/assets/anim/idle/frame_40.webp +0 -0
- package/public-landing/assets/anim/idle/frame_41.webp +0 -0
- package/public-landing/assets/anim/idle/frame_42.webp +0 -0
- package/public-landing/assets/anim/idle/frame_43.webp +0 -0
- package/public-landing/create.html +252 -62
- package/public-landing/gate/README.md +137 -0
- package/public-landing/gate/dispmaps/bar.png +0 -0
- package/public-landing/gate/dispmaps/flat.png +0 -0
- package/public-landing/gate/dispmaps/key.png +0 -0
- package/public-landing/gate/index.html +587 -0
- package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
- package/public-landing/gate/previews/off-the-boats.jpg +0 -0
- package/public-landing/gate/space.css +799 -0
- package/public-landing/gate/space.js +1826 -0
- package/public-landing/gate/sprites/enter.png +0 -0
- package/public-landing/gate/sprites/search.png +0 -0
- package/public-landing/gate/sprites/terminal-blank.png +0 -0
- package/public-landing/gate/sprites/tile.png +0 -0
- package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
- package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
- package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
- package/public-landing/world/assets/plates/scene-establishing.png +0 -0
- package/scripts/gds/abandon.js +6 -4
- package/scripts/gds/api.js +10 -5
- package/scripts/gds/api.ps1 +26 -0
- package/scripts/gds/api.sh +6 -0
- package/scripts/gds/audit-ships.js +7 -1
- package/scripts/gds/backfill-sessions.js +78 -18
- package/scripts/gds/backup.js +23 -1
- package/scripts/gds/box-connect-lib.js +124 -31
- package/scripts/gds/box-sync.js +66 -0
- package/scripts/gds/box.js +225 -53
- package/scripts/gds/cf-tunnel.js +145 -6
- package/scripts/gds/claim.js +34 -3
- package/scripts/gds/cli-lib.js +161 -21
- package/scripts/gds/code.js +6 -2
- package/scripts/gds/deploy-divergence.js +209 -0
- package/scripts/gds/dev-lib.js +309 -0
- package/scripts/gds/dev.js +322 -0
- package/scripts/gds/doctor.js +90 -61
- package/scripts/gds/fetch-art-key.js +1 -1
- package/scripts/gds/fetch-sound-prefs.js +2 -2
- package/scripts/gds/fitness.js +228 -12
- package/scripts/gds/gds-db-backup.service +16 -1
- package/scripts/gds/gds-db-offsite.service +9 -1
- package/scripts/gds/gen-api-client.js +1 -1
- package/scripts/gds/gen-api-docs.js +36 -14
- package/scripts/gds/gen-diagrams.js +45 -28
- package/scripts/gds/gen-file-map.js +2 -1
- package/scripts/gds/gen-freshness.js +32 -0
- package/scripts/gds/gen-repo-map.js +3 -2
- package/scripts/gds/gen-session-index.js +14 -5
- package/scripts/gds/hooks/pre-push.js +2 -2
- package/scripts/gds/init.js +60 -3
- package/scripts/gds/install-git-hooks.js +1 -1
- package/scripts/gds/key-card.js +15 -6
- package/scripts/gds/key-status.js +1 -1
- package/scripts/gds/linkify-refs.js +35 -9
- package/scripts/gds/lint-migrations.js +102 -0
- package/scripts/gds/login.js +58 -11
- package/scripts/gds/mirror-redact.js +5 -0
- package/scripts/gds/package-core.js +263 -4
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +268 -19
- package/scripts/gds/publish-manifest.js +22 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +42 -16
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
- package/scripts/gds/sequence.js +425 -0
- package/scripts/gds/session-cost.js +10 -1
- package/scripts/gds/setup.js +72 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +433 -59
- package/scripts/gds/smoke-gds.js +139 -8
- package/scripts/gds/start.js +171 -22
- package/scripts/gds/status.js +1 -1
- package/scripts/gds/triage.js +10 -9
- package/scripts/gds/upgrade.js +271 -7
- package/scripts/migrate.sh +10 -0
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +809 -107
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +105 -0
- package/src/bongos/github-repos.js +93 -0
- package/src/bongos/module-scope-map.js +27 -1
- package/src/bongos/permission-path-check.js +136 -102
- package/src/bongos/repo-info.js +47 -11
- package/src/bongos/route-rank-check.js +429 -46
- package/src/bongos/routes/_helpers.js +12 -1
- package/src/bongos/routes/auth.js +312 -13
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/github.js +90 -0
- package/src/bongos/routes/instance.js +15 -3
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/routes.js +2 -0
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +49 -2
- package/src/instance-config.js +46 -0
- package/src/module-api.js +140 -1
- package/src/platform-server.js +86 -9
- package/tests/abandon_task.mjs +59 -3
- package/tests/api_cli_flags.mjs +53 -0
- package/tests/auth_page_gate.mjs +13 -0
- package/tests/backfill_sessions.mjs +127 -1
- package/tests/backup_cli.mjs +54 -0
- package/tests/bongos_box_connect.mjs +77 -0
- package/tests/box_access.mjs +130 -1
- package/tests/box_credential.mjs +125 -0
- package/tests/box_host_keys.mjs +24 -0
- package/tests/box_infra_resolve.mjs +149 -0
- package/tests/box_scope_session.mjs +6 -2
- package/tests/box_sync.mjs +55 -0
- package/tests/box_task_scope.mjs +99 -0
- package/tests/branding.mjs +41 -23
- package/tests/cf_ruleset_preflight.mjs +142 -0
- package/tests/cf_tunnel.mjs +37 -0
- package/tests/claim_gate_rebase.mjs +98 -0
- package/tests/clawback_confirmed_backlog.mjs +103 -0
- package/tests/cli_lib_formatters.mjs +20 -0
- package/tests/confirm_attribution.mjs +30 -46
- package/tests/credit_grant.mjs +198 -10
- package/tests/criterion_authoring_gate.mjs +272 -0
- package/tests/criterion_progress.mjs +33 -1
- package/tests/dev_lib.mjs +253 -0
- package/tests/devbox_artifact_gate.mjs +75 -0
- package/tests/devbox_dmg_backdrop.mjs +137 -0
- package/tests/devbox_icons.mjs +304 -0
- package/tests/devbox_installer_brand.mjs +232 -0
- package/tests/devbox_key_register.mjs +234 -0
- package/tests/docs_openapi_route.mjs +110 -0
- package/tests/doctor_hooks.mjs +141 -0
- package/tests/extract_json.mjs +140 -0
- package/tests/fitness.mjs +89 -0
- package/tests/gen_freshness.mjs +96 -0
- package/tests/github_repos_helpers.mjs +91 -0
- package/tests/github_token_store.mjs +121 -0
- package/tests/goal_advisory.mjs +251 -0
- package/tests/goal_criterion_confirm_ui.mjs +269 -0
- package/tests/goal_routes.mjs +59 -4
- package/tests/goal_tier.mjs +12 -3
- package/tests/governance_catalog.mjs +129 -0
- package/tests/governance_claim_eligibility.mjs +262 -0
- package/tests/governance_config.mjs +187 -0
- package/tests/governance_ownership_scope.mjs +410 -0
- package/tests/governance_page_gates.mjs +324 -0
- package/tests/governance_parity.mjs +336 -0
- package/tests/governance_principals.mjs +232 -0
- package/tests/governance_protected_surfaces.mjs +208 -0
- package/tests/governance_require_permission.mjs +223 -0
- package/tests/governance_resolver.mjs +84 -0
- package/tests/governance_role_sync.mjs +231 -0
- package/tests/governance_seed.mjs +127 -0
- package/tests/governance_session_scope.mjs +434 -0
- package/tests/governance_vanilla_boot.mjs +203 -0
- package/tests/grade_attribution.mjs +7 -7
- package/tests/hall_audit.mjs +223 -0
- package/tests/hall_nav.mjs +191 -0
- package/tests/hall_palette.mjs +206 -0
- package/tests/hall_signout.mjs +124 -0
- package/tests/hook_user_agents.mjs +10 -0
- package/tests/idea_triage.mjs +162 -0
- package/tests/idp_device_exchange.mjs +198 -0
- package/tests/idp_federation.mjs +100 -0
- package/tests/init.mjs +51 -0
- package/tests/instance_config.mjs +65 -0
- package/tests/instance_mismatch_guard.mjs +23 -6
- package/tests/instance_release_config.mjs +104 -0
- package/tests/linkify_refs.mjs +15 -2
- package/tests/migrations_forward_safe.mjs +86 -0
- package/tests/module_api.mjs +1 -1
- package/tests/module_contributions.mjs +55 -0
- package/tests/module_loader.mjs +10 -5
- package/tests/module_scope_active_claims.mjs +94 -0
- package/tests/needs_rebase.mjs +92 -0
- package/tests/one_active_claim_per_session.mjs +76 -0
- package/tests/override_attribution.mjs +10 -8
- package/tests/override_request_ownership.mjs +8 -4
- package/tests/package_core.mjs +246 -1
- package/tests/parse_cookie_decode.mjs +42 -0
- package/tests/pay_on_land.mjs +129 -0
- package/tests/platform_boot.mjs +100 -1
- package/tests/post_login_landing.mjs +42 -0
- package/tests/preflight_checks.mjs +72 -0
- package/tests/provision.mjs +289 -7
- package/tests/pruned_lockfile_integrity.mjs +79 -0
- package/tests/publish_branch_route.mjs +53 -0
- package/tests/publish_reconciler.mjs +187 -2
- package/tests/repo_info_binding.mjs +248 -0
- package/tests/reward_gate.mjs +11 -2
- package/tests/reward_mode_cost_plus_only.mjs +116 -0
- package/tests/reward_mode_surfacing.mjs +168 -0
- package/tests/route_permission_check.mjs +301 -0
- package/tests/route_rank_check.mjs +254 -12
- package/tests/route_validation.mjs +30 -0
- package/tests/sequence.mjs +267 -0
- package/tests/session_digest.mjs +44 -0
- package/tests/session_index.mjs +18 -6
- package/tests/session_records.mjs +39 -1
- package/tests/session_reward_on_land.mjs +381 -0
- package/tests/session_reward_reconcile.mjs +229 -0
- package/tests/ship_api_client_pathspec.mjs +100 -0
- package/tests/ship_base_flag.mjs +93 -0
- package/tests/ship_ci_deploy.mjs +140 -3
- package/tests/ship_upstream_deletion.mjs +57 -0
- package/tests/smoke_target.mjs +233 -0
- package/tests/start_env_guard.mjs +87 -0
- package/tests/start_rebase_warning.mjs +103 -0
- package/tests/status_mirror_sync.mjs +122 -0
- package/tests/stop_refcheck.mjs +19 -1
- package/tests/task_goal_link.mjs +15 -6
- package/tests/upgrade.mjs +393 -5
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
- package/.gitignore +0 -91
- package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +0 -94
- package/docs/adr/0140-cloudbongos-standalone-reprovision.md +0 -43
- package/scripts/hall-preview/fixtures/access-requests.json +0 -20
- package/scripts/hall-preview/fixtures/achievements.json +0 -76
- package/scripts/hall-preview/fixtures/analytics__builder.json +0 -251
- package/scripts/hall-preview/fixtures/audit-log.json +0 -68
- package/scripts/hall-preview/fixtures/blockers__detail.json +0 -12
- package/scripts/hall-preview/fixtures/box__me.json +0 -5
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +0 -69
- package/scripts/hall-preview/fixtures/builders__active-claims.json +0 -1
- package/scripts/hall-preview/fixtures/builders__directory.json +0 -81
- package/scripts/hall-preview/fixtures/builders__roster.json +0 -48
- package/scripts/hall-preview/fixtures/goals.json +0 -502
- package/scripts/hall-preview/fixtures/goals__25.json +0 -96
- package/scripts/hall-preview/fixtures/goals__26.json +0 -67
- package/scripts/hall-preview/fixtures/goals__3.json +0 -103
- package/scripts/hall-preview/fixtures/goals__35.json +0 -49
- package/scripts/hall-preview/fixtures/goals__conflicts.json +0 -154
- package/scripts/hall-preview/fixtures/goals__detail.json +0 -96
- package/scripts/hall-preview/fixtures/goals__inbox.json +0 -5
- package/scripts/hall-preview/fixtures/goals__requests.json +0 -3
- package/scripts/hall-preview/fixtures/grades__by-builder.json +0 -32
- package/scripts/hall-preview/fixtures/inbox.json +0 -330
- package/scripts/hall-preview/fixtures/inbox__detail.json +0 -15
- package/scripts/hall-preview/fixtures/instance.json +0 -124
- package/scripts/hall-preview/fixtures/leaderboard.json +0 -184
- package/scripts/hall-preview/fixtures/me.json +0 -476
- package/scripts/hall-preview/fixtures/me__art-key__own.json +0 -6
- package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +0 -209
- package/scripts/hall-preview/fixtures/me__recent-ships.json +0 -304
- package/scripts/hall-preview/fixtures/me__render.json +0 -5
- package/scripts/hall-preview/fixtures/me__skill-prefs.json +0 -143
- package/scripts/hall-preview/fixtures/me__sound-prefs.json +0 -67
- package/scripts/hall-preview/fixtures/me__wandering.json +0 -43
- package/scripts/hall-preview/fixtures/override-requests.json +0 -15
- package/scripts/hall-preview/fixtures/provisioning__fleet.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__instances.json +0 -1
- package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +0 -516
- package/scripts/hall-preview/fixtures/public__bounty-table.json +0 -37
- package/scripts/hall-preview/fixtures/public__cost-summary.json +0 -742
- package/scripts/hall-preview/fixtures/public__estimation-drift.json +0 -41
- package/scripts/hall-preview/fixtures/public__grades.json +0 -50
- package/scripts/hall-preview/fixtures/public__progress.json +0 -124
- package/scripts/hall-preview/fixtures/public__recent-shipped.json +0 -426
- package/scripts/hall-preview/fixtures/public__repo-health.json +0 -160
- package/scripts/hall-preview/fixtures/public__repo-info.json +0 -5
- package/scripts/hall-preview/fixtures/security__docs.json +0 -10
- package/scripts/hall-preview/fixtures/security__reports.json +0 -31
- package/scripts/hall-preview/fixtures/sessions__search.json +0 -9
- package/scripts/hall-preview/fixtures/tasks.json +0 -2420
- package/scripts/hall-preview/fixtures/tasks__claimable.json +0 -2555
- package/scripts/hall-preview/fixtures/tasks__detail.json +0 -111
- package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +0 -330
- package/scripts/hall-preview/fixtures/versions.json +0 -89
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// modules/dev-box/app/scripts/gen-icons.js — generate the app + tray icons from
|
|
2
|
+
// pixel grids (no binary sources to maintain; re-run after edits: npm run icons).
|
|
3
|
+
//
|
|
4
|
+
// The MOTIF is a bongo pair — a large drum and a small one, joined, the emblem
|
|
5
|
+
// the neutral pack already draws in its favicon and the DMG backdrop. It is
|
|
6
|
+
// brand-NEUTRAL geometry: it says "this is the Dev Box", not "this is one
|
|
7
|
+
// particular host's world". (It replaces a shipping-container + wave-footer motif
|
|
8
|
+
// that belonged to one instance's lore and shipped into every build.)
|
|
9
|
+
//
|
|
10
|
+
// The COLOURS are a build input, not baked in — the posture gen-brand.js holds for
|
|
11
|
+
// the app's strings and gen-dmg-backdrop.py holds for the install screen (ADR 0145).
|
|
12
|
+
// The palette comes from the resolved pack's `theme.ui.colors`, so ONE source tree
|
|
13
|
+
// renders the vanilla Cloud Bongos icons here and a host instance's own icons there.
|
|
14
|
+
//
|
|
15
|
+
// ONE source, resolved exactly as gen-brand.js resolves it (docs/branding-contract.md):
|
|
16
|
+
// BONGOS_BRANDING_FILE -> config/branding.json -> config/branding.neutral.json
|
|
17
|
+
//
|
|
18
|
+
// Uses sharp from the REPO ROOT's node_modules (the art pipeline already
|
|
19
|
+
// depends on it) so this subproject keeps zero runtime deps of its own.
|
|
20
|
+
//
|
|
21
|
+
// Outputs (committed):
|
|
22
|
+
// build-resources/icon.png 1024×1024 app icon (electron-builder
|
|
23
|
+
// derives .icns/.ico from it)
|
|
24
|
+
// assets/tray/mac-<state>Template.png 16px mono template (+@2x 32px)
|
|
25
|
+
// assets/tray/win-<state>.png 32px colored
|
|
26
|
+
// States: off | on | busy | alert
|
|
27
|
+
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
|
|
31
|
+
// sharp is a repo-root devDep (the art pipeline already needs it), not a dep
|
|
32
|
+
// of this subproject — so resolve it from a few candidate roots rather than a
|
|
33
|
+
// single brittle relative path: normal resolution (NODE_PATH / a local
|
|
34
|
+
// install) first, then the repo root walked up from here. Fail with an
|
|
35
|
+
// actionable message instead of a raw MODULE_NOT_FOUND.
|
|
36
|
+
function resolveSharp() {
|
|
37
|
+
const candidates = [
|
|
38
|
+
'sharp',
|
|
39
|
+
path.join(__dirname, '..', 'node_modules', 'sharp'),
|
|
40
|
+
path.join(__dirname, '..', '..', 'node_modules', 'sharp'),
|
|
41
|
+
];
|
|
42
|
+
for (const c of candidates) {
|
|
43
|
+
try { return require(c); } catch { /* try the next root */ }
|
|
44
|
+
}
|
|
45
|
+
throw new Error(
|
|
46
|
+
"gen-icons: cannot find 'sharp'. Run `npm install` at the repo root " +
|
|
47
|
+
'(sharp is a root devDep shared with the art pipeline), then retry.'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const sharp = resolveSharp();
|
|
51
|
+
|
|
52
|
+
const APP_DIR = path.resolve(__dirname, '..'); // modules/dev-box/app
|
|
53
|
+
const REPO_ROOT = path.resolve(APP_DIR, '..', '..', '..'); // repo root
|
|
54
|
+
const OUT_TRAY = path.join(APP_DIR, 'assets', 'tray');
|
|
55
|
+
const OUT_BUILD = path.join(APP_DIR, 'build-resources');
|
|
56
|
+
|
|
57
|
+
// theme.ui.colors key → the value used when a pack omits it. Every fallback here
|
|
58
|
+
// is the NEUTRAL pack's own colour, so a literal in this file is never a brand.
|
|
59
|
+
//
|
|
60
|
+
// Every role MUST be one gen-brand.js's buildThemeCss emits into the app's :root —
|
|
61
|
+
// that CSS block is the surface a host pack author actually fills. A role outside
|
|
62
|
+
// it has nothing prompting a host to define it, so its "fallback" would paint a
|
|
63
|
+
// Cloud Bongos colour into a fully-specified host build.
|
|
64
|
+
const COLOR_ROLES = {
|
|
65
|
+
bgCard: '#ffffff', bgDeep: '#ffedd5', ink: '#2a1f17',
|
|
66
|
+
rule: '#fdba74', ruleSoft: '#fed7aa',
|
|
67
|
+
accent: '#f97316', accentDeep: '#ea580c', terracotta: '#fb7185',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// The pack, resolved exactly as gen-brand.js resolves it (env → instance → neutral).
|
|
71
|
+
//
|
|
72
|
+
// Deliberately NOT read from src/brand.generated.json, which also carries colours:
|
|
73
|
+
// that artifact is TRACKED holding the neutral values and nothing on the icon path
|
|
74
|
+
// regenerates it, so preferring it would let a stale checked-in file outrank the
|
|
75
|
+
// host's own pack. The pack decides, every time.
|
|
76
|
+
function readBranding() {
|
|
77
|
+
const candidates = [
|
|
78
|
+
process.env.BONGOS_BRANDING_FILE,
|
|
79
|
+
path.join(REPO_ROOT, 'config', 'branding.json'),
|
|
80
|
+
path.join(REPO_ROOT, 'config', 'branding.neutral.json'),
|
|
81
|
+
].filter(Boolean);
|
|
82
|
+
for (const f of candidates) {
|
|
83
|
+
try { return { file: f, branding: JSON.parse(fs.readFileSync(f, 'utf8')) }; } catch { /* try next */ }
|
|
84
|
+
}
|
|
85
|
+
throw new Error('gen-icons: no branding file found (looked for config/branding.json, config/branding.neutral.json)');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// A pack colour, or the neutral fallback when the pack omits it or writes junk —
|
|
89
|
+
// one malformed hex must not abort a build over an icon.
|
|
90
|
+
function palette(branding) {
|
|
91
|
+
const colors = (((branding.theme || {}).ui) || {}).colors || {};
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const [role, fallback] of Object.entries(COLOR_ROLES)) {
|
|
94
|
+
const v = String(colors[role] || '').trim();
|
|
95
|
+
out[role] = /^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?$/.test(v) ? v : fallback;
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// 16×16 tray grids. Legend: '.' transparent · '#' rim/shell · 'h' head · 'f' shell
|
|
102
|
+
// fill · 'm' state mark
|
|
103
|
+
//
|
|
104
|
+
// The bongo pair, three-quarter view: a large drum left, the small one joined at
|
|
105
|
+
// its right with its head sitting two rows lower. Two things make it read as DRUMS
|
|
106
|
+
// at 16px rather than boxes: the head is an oval that OVERHANGS the narrower shell
|
|
107
|
+
// (a flat top of the same size reads as a crate), and the profile is SQUAT — a
|
|
108
|
+
// bongo is about as wide as it is tall, so a tall shell reads as a jar.
|
|
109
|
+
//
|
|
110
|
+
// Every state keeps that silhouette and differs in SHAPE as well as colour,
|
|
111
|
+
// because the macOS template icons are monochrome: hollow (off) · filled (on) ·
|
|
112
|
+
// struck heads + beats overhead (busy) · a corner badge (alert).
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
const GRIDS = {
|
|
115
|
+
off: [
|
|
116
|
+
'................',
|
|
117
|
+
'................',
|
|
118
|
+
'................',
|
|
119
|
+
'................',
|
|
120
|
+
'..#######.......',
|
|
121
|
+
'.#.......#......',
|
|
122
|
+
'..#######.......',
|
|
123
|
+
'...#...#.#####..',
|
|
124
|
+
'...#...##.....#.',
|
|
125
|
+
'...#...#.#####..',
|
|
126
|
+
'...#...#.#...#..',
|
|
127
|
+
'...#...#.#...#..',
|
|
128
|
+
'...#####.#####..',
|
|
129
|
+
'................',
|
|
130
|
+
'................',
|
|
131
|
+
'................',
|
|
132
|
+
],
|
|
133
|
+
on: [
|
|
134
|
+
'................',
|
|
135
|
+
'................',
|
|
136
|
+
'................',
|
|
137
|
+
'................',
|
|
138
|
+
'..#######.......',
|
|
139
|
+
'.#hhhhhhh#......',
|
|
140
|
+
'..#######.......',
|
|
141
|
+
'...#fff#.#####..',
|
|
142
|
+
'...#fff##hhhhh#.',
|
|
143
|
+
'...#fff#.#####..',
|
|
144
|
+
'...#fff#.#fff#..',
|
|
145
|
+
'...#fff#.#fff#..',
|
|
146
|
+
'...#####.#####..',
|
|
147
|
+
'................',
|
|
148
|
+
'................',
|
|
149
|
+
'................',
|
|
150
|
+
],
|
|
151
|
+
busy: [
|
|
152
|
+
'................',
|
|
153
|
+
'................',
|
|
154
|
+
'....m...m...m...',
|
|
155
|
+
'................',
|
|
156
|
+
'..#######.......',
|
|
157
|
+
'.#hhhhhhh#......',
|
|
158
|
+
'..#######.......',
|
|
159
|
+
'...#...#.#####..',
|
|
160
|
+
'...#...##hhhhh#.',
|
|
161
|
+
'...#...#.#####..',
|
|
162
|
+
'...#...#.#...#..',
|
|
163
|
+
'...#...#.#...#..',
|
|
164
|
+
'...#####.#####..',
|
|
165
|
+
'................',
|
|
166
|
+
'................',
|
|
167
|
+
'................',
|
|
168
|
+
],
|
|
169
|
+
alert: [
|
|
170
|
+
'............mmm.',
|
|
171
|
+
'............mmm.',
|
|
172
|
+
'............mmm.',
|
|
173
|
+
'................',
|
|
174
|
+
'..#######.......',
|
|
175
|
+
'.#.......#......',
|
|
176
|
+
'..#######.......',
|
|
177
|
+
'...#...#.#####..',
|
|
178
|
+
'...#...##.....#.',
|
|
179
|
+
'...#...#.#####..',
|
|
180
|
+
'...#...#.#...#..',
|
|
181
|
+
'...#...#.#...#..',
|
|
182
|
+
'...#####.#####..',
|
|
183
|
+
'................',
|
|
184
|
+
'................',
|
|
185
|
+
'................',
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
function gridToSvg(grid, pal, cell = 1) {
|
|
190
|
+
const h = grid.length;
|
|
191
|
+
const w = grid[0].length;
|
|
192
|
+
const rects = [];
|
|
193
|
+
for (let y = 0; y < h; y++) {
|
|
194
|
+
for (let x = 0; x < w; x++) {
|
|
195
|
+
const color = pal[grid[y][x]];
|
|
196
|
+
if (!color) continue;
|
|
197
|
+
rects.push(`<rect x="${x * cell}" y="${y * cell}" width="${cell}" height="${cell}" fill="${color}"/>`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w * cell}" height="${h * cell}" shape-rendering="crispEdges">${rects.join('')}</svg>`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function renderPng(svg, size, file) {
|
|
204
|
+
await sharp(Buffer.from(svg)).resize(size, size, { kernel: 'nearest' }).png().toFile(file);
|
|
205
|
+
console.log('wrote', path.relative(process.cwd(), file));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// The app icon: the pack's field + border, the bongo pair, a beat line at the foot.
|
|
209
|
+
// Built as a 32×32 grid then nearest-upscaled to 1024 so it keeps the pixel look at
|
|
210
|
+
// every size electron-builder derives.
|
|
211
|
+
function buildAppGrid() {
|
|
212
|
+
const rows = [];
|
|
213
|
+
for (let y = 0; y < 32; y++) rows.push('.'.repeat(32).split(''));
|
|
214
|
+
const put = (x, y, c) => { if (rows[y] && rows[y][x] !== undefined) rows[y][x] = c; };
|
|
215
|
+
// Radii are nudged by half a cell so the boundary row/column lands ON the grid
|
|
216
|
+
// instead of being clipped away — otherwise a 3-row oval renders as 1.
|
|
217
|
+
const oval = (cx, cy, rx, ry, c) => {
|
|
218
|
+
for (let y = cy - ry; y <= cy + ry; y++) for (let x = cx - rx; x <= cx + rx; x++) {
|
|
219
|
+
const dx = (x - cx) / (rx + 0.5);
|
|
220
|
+
const dy = (y - cy) / (ry + 0.5);
|
|
221
|
+
if (dx * dx + dy * dy <= 1) put(x, y, c);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
for (let y = 2; y < 30; y++) for (let x = 2; x < 30; x++) put(x, y, 'p');
|
|
226
|
+
for (let x = 2; x < 30; x++) { put(x, 2, 'b'); put(x, 29, 'b'); }
|
|
227
|
+
for (let y = 2; y < 30; y++) { put(2, y, 'b'); put(29, y, 'b'); }
|
|
228
|
+
|
|
229
|
+
// One drum: a shell hanging from the head's centre line, then the head oval
|
|
230
|
+
// drawn over it — so the head overhangs the shell, the cue that reads as a drum.
|
|
231
|
+
const drum = (cx, cy, rx, ry, shellRx, yBase, shell) => {
|
|
232
|
+
for (let y = cy; y <= yBase; y++) for (let x = cx - shellRx; x <= cx + shellRx; x++) put(x, y, 'k');
|
|
233
|
+
for (let y = cy; y < yBase; y++) for (let x = cx - shellRx + 1; x < cx + shellRx; x++) put(x, y, shell);
|
|
234
|
+
oval(cx, cy, rx, ry, 'k');
|
|
235
|
+
oval(cx, cy, rx - 1, ry - 1, 'h');
|
|
236
|
+
};
|
|
237
|
+
drum(11, 12, 7, 3, 5, 24, 't'); // the large drum
|
|
238
|
+
drum(22, 15, 5, 3, 3, 24, 's'); // the small one, drawn second so it sits in front
|
|
239
|
+
|
|
240
|
+
// The beat: four even strokes across the foot — rhythm without a world-specific
|
|
241
|
+
// motif, echoing the DMG backdrop's beat line. Spanning the full width (rather
|
|
242
|
+
// than sitting under each shell) keeps them reading as a strip, not as legs.
|
|
243
|
+
for (let x = 5; x <= 23; x += 6) for (let dx = 0; dx < 3; dx++) { put(x + dx, 26, 'd'); put(x + dx, 27, 'd'); }
|
|
244
|
+
|
|
245
|
+
return rows.map((r) => r.join(''));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function main() {
|
|
249
|
+
fs.mkdirSync(OUT_TRAY, { recursive: true });
|
|
250
|
+
fs.mkdirSync(OUT_BUILD, { recursive: true });
|
|
251
|
+
|
|
252
|
+
const { file, branding } = readBranding();
|
|
253
|
+
const p = palette(branding);
|
|
254
|
+
|
|
255
|
+
// macOS template: pure black + alpha, for EVERY state. This is an OS
|
|
256
|
+
// requirement, not a brand choice — the system recolours a *Template.png itself
|
|
257
|
+
// (menu-bar light/dark, inverted when selected), and any colour in the source
|
|
258
|
+
// is discarded. Branding one of these would be a bug, not a feature.
|
|
259
|
+
const MAC = { '#': '#000000', f: '#000000', h: '#000000', m: '#000000' };
|
|
260
|
+
// Windows: state via colour on a taskbar we don't control — it may be light OR
|
|
261
|
+
// dark, so every state outlines in the pack's DEEP accent, the one tone that
|
|
262
|
+
// holds contrast against both. The pale tints only ever appear as fill inside
|
|
263
|
+
// that outline, where the shape is already legible.
|
|
264
|
+
const WIN = {
|
|
265
|
+
off: { '#': p.accentDeep },
|
|
266
|
+
on: { '#': p.accentDeep, f: p.accent, h: p.bgDeep },
|
|
267
|
+
busy: { '#': p.accentDeep, h: p.accent, m: p.accent },
|
|
268
|
+
alert: { '#': p.accentDeep, m: p.terracotta },
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
for (const state of Object.keys(GRIDS)) {
|
|
272
|
+
const macSvg = gridToSvg(GRIDS[state], MAC);
|
|
273
|
+
await renderPng(macSvg, 16, path.join(OUT_TRAY, `mac-${state}Template.png`));
|
|
274
|
+
await renderPng(macSvg, 32, path.join(OUT_TRAY, `mac-${state}Template@2x.png`));
|
|
275
|
+
await renderPng(gridToSvg(GRIDS[state], WIN[state]), 32, path.join(OUT_TRAY, `win-${state}.png`));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const APP_PALETTE = {
|
|
279
|
+
p: p.bgCard, b: p.rule, k: p.ink,
|
|
280
|
+
t: p.accent, s: p.terracotta, h: p.ruleSoft, d: p.accentDeep,
|
|
281
|
+
};
|
|
282
|
+
await renderPng(gridToSvg(buildAppGrid(), APP_PALETTE), 1024, path.join(OUT_BUILD, 'icon.png'));
|
|
283
|
+
console.log(`gen-icons: palette ← ${path.relative(REPO_ROOT, file)}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// devbox-app/scripts/vendor-client.js — vendor the generated CJS API client
|
|
2
|
+
// into src/vendor/ so it ships INSIDE the packaged asar.
|
|
3
|
+
//
|
|
4
|
+
// The devbox app is a self-contained, zero-dependency subproject (ADR 0045): it
|
|
5
|
+
// is never `npm install`ed and electron-builder packages `src/**` only, so it
|
|
6
|
+
// cannot require() the repo's clients/bongos-client/ at runtime. This copies the
|
|
7
|
+
// committed CJS build in verbatim (byte-for-byte) so gds-client.js can require
|
|
8
|
+
// './vendor/bongos-client.cjs'. Vendoring a generated FILE (not an npm dep) keeps
|
|
9
|
+
// the subproject dependency-free. The main repo's api_client test asserts this
|
|
10
|
+
// copy stays byte-identical to the canonical build, so drift fails CI.
|
|
11
|
+
//
|
|
12
|
+
// Run: npm run vendor (also invoked before `start`, `test`, and `dist:*`).
|
|
13
|
+
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
|
|
17
|
+
const SRC = path.resolve(__dirname, '../../../../clients/bongos-client/index.cjs');
|
|
18
|
+
const DEST = path.resolve(__dirname, '../src/vendor/bongos-client.cjs');
|
|
19
|
+
|
|
20
|
+
const bytes = fs.readFileSync(SRC);
|
|
21
|
+
fs.mkdirSync(path.dirname(DEST), { recursive: true });
|
|
22
|
+
fs.writeFileSync(DEST, bytes);
|
|
23
|
+
console.log(`vendor-client: ${path.relative(process.cwd(), DEST)} (${bytes.length} bytes) ← clients/bongos-client/index.cjs`);
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// devbox-app/src/box-controller.js — the app's state machine.
|
|
2
|
+
//
|
|
3
|
+
// Owns: the polled view of GET /box/me (+ /box/terminal), the on/off actions,
|
|
4
|
+
// poll cadence, and transition events (for notifications + tray updates). The
|
|
5
|
+
// pure decision helpers are exported separately so test/unit.mjs can pin them
|
|
6
|
+
// without Electron.
|
|
7
|
+
//
|
|
8
|
+
// Status vocabulary the UI/tray consume (derived, NOT the raw box.state):
|
|
9
|
+
// signed_out | loading | none | provisioning | active
|
|
10
|
+
// | closing | error
|
|
11
|
+
//
|
|
12
|
+
// Cost posture (deliberate, with one narrow exception): the app NEVER sends
|
|
13
|
+
// claude_active heartbeats, so the droplet's idle sweep stays authoritative — a
|
|
14
|
+
// box nobody is using is reclaimed in ~10–15 min and the app just reflects that.
|
|
15
|
+
// The exception (leg 3): while the app is ACTIVELY pointed at the box (panel open,
|
|
16
|
+
// or within HEARTBEAT_WINDOW_MS of a connect action) it sends BODY-LESS heartbeats
|
|
17
|
+
// that bump last_activity_at only — never claude_active. That stops the box reaping
|
|
18
|
+
// out from under a builder who is mid-connect (the vicious cycle where a failing
|
|
19
|
+
// connect lets the box rebuild with fresh host keys) WITHOUT keeping a genuinely
|
|
20
|
+
// idle box (app closed, no recent connect) alive. See shouldHeartbeat().
|
|
21
|
+
|
|
22
|
+
// Derive the UI status from a GET /box/me payload.
|
|
23
|
+
function deriveStatus(payload) {
|
|
24
|
+
if (!payload || !payload.box) return 'none';
|
|
25
|
+
const state = payload.box.state || 'none';
|
|
26
|
+
const intent = payload.open_intent || null;
|
|
27
|
+
if (intent && intent.action === 'deprovision') return 'closing';
|
|
28
|
+
if (intent && (intent.action === 'provision' || intent.action === 'wake')) return 'provisioning';
|
|
29
|
+
if (state === 'active') return 'active';
|
|
30
|
+
if (state === 'provisioning' || state === 'waking') return 'provisioning';
|
|
31
|
+
if (state === 'parking') return 'closing';
|
|
32
|
+
// parked = off-but-resumable; surfaced as plain "off" — the switch wakes it.
|
|
33
|
+
if (state === 'parked' || state === 'none' || state === 'destroyed') return 'none';
|
|
34
|
+
if (state === 'error') return 'error';
|
|
35
|
+
return 'none';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Poll cadence by status (ms). Transitions poll fast; settled states slow.
|
|
39
|
+
function pollDelayMs(status) {
|
|
40
|
+
switch (status) {
|
|
41
|
+
case 'provisioning':
|
|
42
|
+
case 'closing': return 5_000;
|
|
43
|
+
case 'active': return 60_000;
|
|
44
|
+
case 'error': return 60_000;
|
|
45
|
+
default: return 120_000; // none / signed_out idle tick
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Leg 3 — connect-window keep-alive. The heartbeat timer ticks at this cadence,
|
|
50
|
+
// well under the box-side cron (5 min) and the idle threshold (10 min), so even a
|
|
51
|
+
// short connect window defers a reap.
|
|
52
|
+
const HEARTBEAT_TICK_MS = 120_000; // 2 min
|
|
53
|
+
// Keep beating for this long after a deliberate connect action (box on / terminal
|
|
54
|
+
// open) so retries during a flaky connect stay covered even if the panel is hidden.
|
|
55
|
+
const HEARTBEAT_WINDOW_MS = 600_000; // 10 min
|
|
56
|
+
|
|
57
|
+
// PURE (unit-tested): should the app send a keep-alive heartbeat right now? Only
|
|
58
|
+
// when the box is active AND the app is actively pointed at it — panel visible, or
|
|
59
|
+
// within HEARTBEAT_WINDOW_MS of a connect action. An idle box (app closed, no
|
|
60
|
+
// recent connect) returns false, so it still reaps on schedule (cost posture).
|
|
61
|
+
function shouldHeartbeat({ status, panelVisible, lastConnectActionAt, now }) {
|
|
62
|
+
if (status !== 'active') return false;
|
|
63
|
+
if (panelVisible) return true;
|
|
64
|
+
return Boolean(lastConnectActionAt) && (now - lastConnectActionAt) < HEARTBEAT_WINDOW_MS;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Map a status transition to a user-facing notification (or null).
|
|
68
|
+
// `ctx.envName` = the Claude Desktop environment name.
|
|
69
|
+
function transitionNotification(prev, next, ctx = {}) {
|
|
70
|
+
if (prev === next) return null;
|
|
71
|
+
if (next === 'active') {
|
|
72
|
+
return {
|
|
73
|
+
title: 'Your dev box is ready',
|
|
74
|
+
body: ctx.settingsInstalled
|
|
75
|
+
? `Restart Claude Desktop, then Code → Environment → “${ctx.envName}”. The browser terminal works too.`
|
|
76
|
+
: 'Connect from the app panel — browser terminal or SSH.',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (prev === 'active' && next === 'none') {
|
|
80
|
+
return {
|
|
81
|
+
title: 'Your dev box closed',
|
|
82
|
+
body: 'It shut down (boxes rest after ~10 idle minutes, or when closed). Flip the switch to raise it again.',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (next === 'error') {
|
|
86
|
+
return {
|
|
87
|
+
title: 'Your dev box hit an error',
|
|
88
|
+
body: 'Flip the switch to try again, or ask in #support on Discord.',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// createBoxController({ client, signedIn, onChange, onNotify, sleep? })
|
|
95
|
+
// onChange(snapshot) fires on every refresh; onNotify({title, body}) on
|
|
96
|
+
// meaningful transitions. Snapshot shape is what ipc.js ships to the renderer.
|
|
97
|
+
function createBoxController({ client, signedIn, onChange, onNotify, envName }) {
|
|
98
|
+
let status = 'loading';
|
|
99
|
+
let box = null;
|
|
100
|
+
let terminal = null; // { url, credential } when active+ready
|
|
101
|
+
let lastError = null;
|
|
102
|
+
let settingsInstalled = false; // set by main.js after ssh-setup runs
|
|
103
|
+
let hostKeys = null; // { ok, reason?, attempt? } — #909 pinning status
|
|
104
|
+
let keyRegistered = null; // { ok, reason, fingerprint, attempts } — task 1002509
|
|
105
|
+
let busy = false; // an ensure/close request is in flight
|
|
106
|
+
let timer = null;
|
|
107
|
+
let stopped = false;
|
|
108
|
+
let panelVisible = false; // leg 3: main.js pushes BrowserWindow show/hide
|
|
109
|
+
let lastConnectActionAt = 0; // leg 3: stamped on box-on / terminal-open
|
|
110
|
+
let hbTimer = null; // leg 3: keep-alive timer (separate from the poll)
|
|
111
|
+
|
|
112
|
+
function snapshot() {
|
|
113
|
+
return {
|
|
114
|
+
status: signedIn() ? status : 'signed_out',
|
|
115
|
+
busy,
|
|
116
|
+
box: box && {
|
|
117
|
+
state: box.state,
|
|
118
|
+
hostname: box.hostname,
|
|
119
|
+
size: box.size_slug,
|
|
120
|
+
provisionedAt: box.provisioned_at,
|
|
121
|
+
// task 1187 / idea 235: the box's GDS-reported PUBLIC host keys
|
|
122
|
+
// (`<keytype> <base64>` per line). main.js pins known_hosts from these
|
|
123
|
+
// (authoritative — no keyscan/TOFU) when present, else falls back to keyscan.
|
|
124
|
+
hostKeysText: box.host_keys || null,
|
|
125
|
+
// When those keys were reported, and when the CURRENT droplet came up. A
|
|
126
|
+
// report older than the activation belongs to a box that no longer exists,
|
|
127
|
+
// so main.js keyscans instead of pinning a dead identity (task 1002509).
|
|
128
|
+
hostKeysAt: box.host_keys_at || null,
|
|
129
|
+
activeSince: box.active_since || null,
|
|
130
|
+
},
|
|
131
|
+
terminal,
|
|
132
|
+
lastError,
|
|
133
|
+
settingsInstalled,
|
|
134
|
+
hostKeys,
|
|
135
|
+
keyRegistered,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function emit() { onChange(snapshot()); }
|
|
140
|
+
|
|
141
|
+
function schedule(ms) {
|
|
142
|
+
if (stopped) return;
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
timer = setTimeout(() => refresh().catch(() => {}), ms ?? pollDelayMs(status));
|
|
145
|
+
// Don't hold the process open for a poll timer.
|
|
146
|
+
if (timer.unref) timer.unref();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Leg 3: body-less keep-alive while the app is actively pointed at the box (see
|
|
150
|
+
// the cost-posture note up top). Best-effort — a failed beat just retries next
|
|
151
|
+
// tick; it never blocks or surfaces an error to the user.
|
|
152
|
+
function heartbeatTick() {
|
|
153
|
+
if (stopped || !signedIn()) return;
|
|
154
|
+
if (!shouldHeartbeat({ status, panelVisible, lastConnectActionAt, now: Date.now() })) return;
|
|
155
|
+
Promise.resolve(client.boxHeartbeat()).catch(() => {});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function refresh() {
|
|
159
|
+
if (stopped) return snapshot();
|
|
160
|
+
if (!signedIn()) {
|
|
161
|
+
status = 'signed_out';
|
|
162
|
+
box = null; terminal = null;
|
|
163
|
+
emit();
|
|
164
|
+
return snapshot();
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const payload = await client.boxMe();
|
|
168
|
+
const prev = status;
|
|
169
|
+
box = payload.box && payload.box.state !== 'none' ? payload.box : null;
|
|
170
|
+
status = deriveStatus(payload);
|
|
171
|
+
lastError = null;
|
|
172
|
+
// Terminal link only matters when active; warming_up → keep polling.
|
|
173
|
+
if (status === 'active') {
|
|
174
|
+
try {
|
|
175
|
+
const t = await client.boxTerminal();
|
|
176
|
+
terminal = t && t.url ? { url: t.url, credential: t.credential } : null;
|
|
177
|
+
} catch { terminal = null; }
|
|
178
|
+
} else {
|
|
179
|
+
terminal = null;
|
|
180
|
+
hostKeys = null; // re-pinned on next activation (#909)
|
|
181
|
+
}
|
|
182
|
+
const note = transitionNotification(prev, status, { envName, settingsInstalled });
|
|
183
|
+
if (note && prev !== 'loading') {
|
|
184
|
+
onNotify(note);
|
|
185
|
+
}
|
|
186
|
+
if (note && status === 'active') {
|
|
187
|
+
// 'ready' fires even from a cold start (prev === 'loading') if the
|
|
188
|
+
// box JUST became active from this app's own ensure — handled by main.
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (err && err.status === 401) {
|
|
192
|
+
status = 'signed_out';
|
|
193
|
+
} else {
|
|
194
|
+
lastError = err.message || 'unreachable';
|
|
195
|
+
if (status === 'loading') status = 'error';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
emit();
|
|
199
|
+
schedule();
|
|
200
|
+
return snapshot();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
snapshot,
|
|
205
|
+
refresh,
|
|
206
|
+
setSettingsInstalled(v) { settingsInstalled = v; emit(); },
|
|
207
|
+
setHostKeys(v) { hostKeys = v; emit(); },
|
|
208
|
+
// Registration outcome from ssh-setup.registerKeyWithVerify. Unlike hostKeys it
|
|
209
|
+
// is NOT cleared when the box goes inactive: the key lives on the builder's
|
|
210
|
+
// account, so a failure stays visible (and retryable) until it verifies.
|
|
211
|
+
setKeyRegistered(v) { keyRegistered = v; emit(); },
|
|
212
|
+
// Leg 3: main.js pushes panel visibility; connect actions stamp a window.
|
|
213
|
+
setPanelVisible(v) { panelVisible = !!v; },
|
|
214
|
+
markConnectAttempt() { lastConnectActionAt = Date.now(); },
|
|
215
|
+
|
|
216
|
+
// Flip ON: POST /box/ensure, then poll fast. Returns the server message.
|
|
217
|
+
async turnOn() {
|
|
218
|
+
lastConnectActionAt = Date.now(); // leg 3: turning the box on is a connect action
|
|
219
|
+
busy = true; lastError = null; emit();
|
|
220
|
+
try {
|
|
221
|
+
return await client.boxEnsure();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
lastError = err.body && err.body.error === 'BOX_PROVISION_DENIED'
|
|
224
|
+
? `Not allowed: ${err.body.reason || 'provision denied'}`
|
|
225
|
+
: (err.message || 'request failed');
|
|
226
|
+
throw err;
|
|
227
|
+
} finally {
|
|
228
|
+
busy = false;
|
|
229
|
+
await refresh();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// Flip OFF: POST /box/close (active only — the API 409s otherwise).
|
|
234
|
+
async turnOff() {
|
|
235
|
+
busy = true; lastError = null; emit();
|
|
236
|
+
try {
|
|
237
|
+
return await client.boxClose();
|
|
238
|
+
} catch (err) {
|
|
239
|
+
lastError = err.body && err.body.error === 'box_not_active'
|
|
240
|
+
? 'The box is mid-change — wait for it to settle, then try again.'
|
|
241
|
+
: (err.message || 'request failed');
|
|
242
|
+
throw err;
|
|
243
|
+
} finally {
|
|
244
|
+
busy = false;
|
|
245
|
+
await refresh();
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
start() {
|
|
250
|
+
stopped = false;
|
|
251
|
+
refresh().catch(() => {});
|
|
252
|
+
// leg 3: a separate, unref'd keep-alive timer (gated by shouldHeartbeat).
|
|
253
|
+
if (!hbTimer) { hbTimer = setInterval(heartbeatTick, HEARTBEAT_TICK_MS); if (hbTimer.unref) hbTimer.unref(); }
|
|
254
|
+
},
|
|
255
|
+
stop() {
|
|
256
|
+
stopped = true;
|
|
257
|
+
clearTimeout(timer);
|
|
258
|
+
if (hbTimer) { clearInterval(hbTimer); hbTimer = null; }
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ttyd's HTTP-basic username is the fixed `otb` user (box-terminal.service:
|
|
264
|
+
// --credential "otb:<pass>"); the password is the long hex `credential`.
|
|
265
|
+
const TERMINAL_USER = 'otb';
|
|
266
|
+
|
|
267
|
+
// Browser-terminal URL with the basic-auth userinfo embedded, so opening it
|
|
268
|
+
// lands signed-in (browsers still honor user:pass@host for top-level
|
|
269
|
+
// navigation; ttyd auth is plain HTTP basic). Falls back to the bare URL if
|
|
270
|
+
// there's no credential or the URL doesn't parse.
|
|
271
|
+
function terminalAuthUrl(terminal) {
|
|
272
|
+
if (!terminal || !terminal.url) return null;
|
|
273
|
+
if (!terminal.credential) return terminal.url;
|
|
274
|
+
try {
|
|
275
|
+
const u = new URL(terminal.url);
|
|
276
|
+
u.username = TERMINAL_USER;
|
|
277
|
+
u.password = terminal.credential;
|
|
278
|
+
return u.toString();
|
|
279
|
+
} catch {
|
|
280
|
+
return terminal.url;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = {
|
|
285
|
+
createBoxController, deriveStatus, pollDelayMs, transitionNotification,
|
|
286
|
+
terminalAuthUrl, TERMINAL_USER, shouldHeartbeat,
|
|
287
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"CIV_NAME": "Cloud Bongos",
|
|
3
|
+
"PRODUCT_NAME": "Cloud Bongos Dev Box",
|
|
4
|
+
"DESKTOP_ENV_NAME": "Cloud Bongos Dev Box",
|
|
5
|
+
"PUBLIC_ORIGIN": "http://localhost:3000",
|
|
6
|
+
"BUILDERS_ORIGIN": "http://localhost:3000",
|
|
7
|
+
"PRODUCT_SLUG": "Cloud-Bongos-Dev-Box",
|
|
8
|
+
"APP_ID": "com.cloudbongos.devbox",
|
|
9
|
+
"COPYRIGHT": "© Cloud Bongos",
|
|
10
|
+
"CONFIG_DIR": "cloudbongos",
|
|
11
|
+
"TAGLINE": "Building software is laying down a beat.",
|
|
12
|
+
"BG_COLOR": "#fff7ed"
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// modules/dev-box/app/src/brand.js — the app's brand strings, in ONE place.
|
|
2
|
+
//
|
|
3
|
+
// GENERATED, not hardcoded: scripts/gen-brand.js writes src/brand.generated.json
|
|
4
|
+
// from the instance's config/branding.json at build time (prestart / pretest /
|
|
5
|
+
// predist), so the SAME source tree renders a "Cloud Bongos Dev Box" here and an
|
|
6
|
+
// "Amazonprimea Dev Box" on the OTB instance — no instance name is baked into
|
|
7
|
+
// code (the ADR 0004 posture, generalised to every instance). The generated file
|
|
8
|
+
// is committed (the Cloud Bongos default) so a fresh checkout runs without a
|
|
9
|
+
// generate step; every build regenerates it from the local branding.
|
|
10
|
+
//
|
|
11
|
+
// Keys: CIV_NAME (world/instance name, runtime display only), PRODUCT_NAME,
|
|
12
|
+
// DESKTOP_ENV_NAME, PUBLIC_ORIGIN, BUILDERS_ORIGIN, PRODUCT_SLUG (artifact
|
|
13
|
+
// filenames), APP_ID, COPYRIGHT, CONFIG_DIR (matches the CLI session dir),
|
|
14
|
+
// BG_COLOR (pre-CSS window bg), TAGLINE (panel epigraph).
|
|
15
|
+
|
|
16
|
+
module.exports = require('./brand.generated.json');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// devbox-app/src/config.js — runtime configuration resolution.
|
|
2
|
+
|
|
3
|
+
const brand = require('./brand');
|
|
4
|
+
|
|
5
|
+
function apiBase(sessionApiBase) {
|
|
6
|
+
// Priority: explicit env (dev/staging) → the base recorded in the session
|
|
7
|
+
// file at sign-in → production default. Trailing slashes are stripped so
|
|
8
|
+
// path concatenation stays uniform.
|
|
9
|
+
const raw = process.env.OTB_DEVBOX_API_BASE || sessionApiBase || brand.PUBLIC_ORIGIN;
|
|
10
|
+
return String(raw).replace(/\/+$/, '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function appVersion() {
|
|
14
|
+
// package.json is inside the asar in production; require() resolves it both
|
|
15
|
+
// packed and in dev.
|
|
16
|
+
return require('../package.json').version;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { apiBase, appVersion };
|