@bongos/core 1.23.21 → 1.24.1
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 +983 -428
- package/.claude/skills/blocker-review/SKILL.md +9 -1
- package/.claude/skills/blocker-solve/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +1 -1
- package/.claude/skills/builder-reauth/SKILL.md +1 -1
- package/.claude/skills/builder-redteam/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/builder-setup/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -1
- package/.claude/skills/collab-review/SKILL.md +5 -2
- package/.claude/skills/design/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +6 -3
- package/.claude/skills/grade-recover/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/owner-review/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +11 -11
- package/.claude/skills/read-session-export/SKILL.md +1 -1
- package/.claude/skills/recall/SKILL.md +1 -1
- package/.claude/skills/scan-before-install/SKILL.md +1 -1
- package/.claude/skills/session-handoff/SKILL.md +1 -1
- package/.claude/skills/strand-fix/SKILL.md +13 -0
- package/.claude/skills/worktree-clean/SKILL.md +1 -1
- package/AGENTS.md +15 -0
- package/GEMINI.md +5 -0
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +94 -0
- package/clients/bongos-client/index.cjs +94 -0
- package/clients/bongos-client/index.d.ts +154 -4
- package/clients/bongos-client/index.mjs +94 -0
- package/docs/adr/0054-session-token-reward-cost-plus.md +3 -1
- package/docs/adr/0055-server-mediated-branch-publish.md +1 -1
- package/docs/adr/0385-money-lives-with-a-processor-credits-stay-a-project-meter.md +87 -0
- package/docs/adr/0386-the-funding-wall-is-a-per-project-rule-claim-time-hold-by-default.md +37 -0
- package/docs/adr/0387-two-ledgers-credits-measure-work-money-stays-with-the-processor.md +39 -0
- package/docs/adr/0388-a-module-sale-is-split-by-the-project-the-buyer-pays-the-platform-fee.md +42 -0
- package/docs/adr/0392-ai-generation-runs-on-a-processor-held-balance-and-the-builders-own-keys.md +33 -0
- package/docs/adr/0393-a-platform-account-is-a-person-and-github-is-one-way-in.md +37 -0
- package/docs/adr/0394-market-watch-searches-through-the-messages-api-on-the-projects-key-and-runs-from-its-own-poller.md +102 -0
- package/docs/adr/0395-a-task-says-what-it-needs-and-lists-show-only-what-you-can-do.md +36 -0
- package/docs/adr/0396-google-sign-in-a-stand-in-login-and-a-browser-approved-cli-sign-in.md +41 -0
- package/docs/adr/README.md +10 -1
- package/docs/api/openapi.json +8619 -5885
- package/docs/api-reference.md +68 -9
- package/docs/architecture.md +37 -1
- package/docs/canonical-permissions.md +1 -1
- package/docs/copy-inventory.md +420 -337
- package/docs/copy-registry.json +1321 -500
- package/docs/file-map.md +4 -0
- package/docs/module-api-changelog.md +7 -0
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +3 -3
- package/docs/onboarding/diagrams/README.md +1 -1
- package/docs/onboarding/drachmae-and-karma.md +15 -15
- package/docs/onboarding/primer.md +2 -2
- package/docs/page-inventory.json +55 -6
- package/docs/page-readings.json +1185 -1011
- package/docs/recipes/google-sign-in.md +60 -0
- package/docs/recipes/ops-gotchas.md +1 -1
- package/migrations/core_282_builders_lower_login_idx.sql +33 -0
- package/migrations/core_283_claimable_ignores_page_markers.sql +53 -0
- package/migrations/core_283_payout_approvals.sql +183 -0
- package/migrations/core_284_payout_gates.sql +179 -0
- package/migrations/core_285_task_needs.sql +74 -0
- package/modules/ai-studio/CLAUDE.md +15 -0
- package/modules/ai-studio/billing.js +67 -0
- package/modules/ai-studio/context.js +29 -0
- package/modules/ai-studio/db.js +172 -0
- package/modules/ai-studio/gemini.js +75 -0
- package/modules/ai-studio/generate.js +119 -0
- package/modules/ai-studio/keys.js +73 -0
- package/modules/ai-studio/migrations/ai_studio_001_keys.sql +22 -0
- package/modules/ai-studio/migrations/ai_studio_002_ledger.sql +48 -0
- package/modules/ai-studio/migrations/ai_studio_003_requests.sql +55 -0
- package/modules/ai-studio/module.json +26 -0
- package/modules/ai-studio/openrouter.js +90 -0
- package/modules/ai-studio/payments.js +105 -0
- package/modules/ai-studio/pricing.js +92 -0
- package/modules/ai-studio/routes/ai-studio.js +252 -0
- package/modules/ai-studio/studio.js +121 -0
- package/modules/autonomy/fence.js +64 -1
- package/modules/builder-settings/builder-needs.js +27 -1
- package/modules/copy-desk/routes/copy-desk.js +5 -0
- package/modules/economy/achievements.js +22 -10
- package/modules/economy/cost-classify.js +9 -0
- package/modules/economy/cost-write-guard.js +52 -0
- package/modules/economy/cost.js +2 -1
- package/modules/economy/credits.js +18 -15
- package/modules/economy/goal-reward.js +161 -0
- package/modules/economy/reward.js +4 -0
- package/modules/economy/routes/cost.js +33 -7
- package/modules/government/catalog.js +6 -0
- package/modules/government/db.js +108 -0
- package/modules/government/docket.js +36 -0
- package/modules/government/migrations/government_035_payout_approve.sql +29 -0
- package/modules/government/pollers/board-expiry.js +108 -20
- package/modules/government/protected-surfaces.json +1 -0
- package/modules/government/routes/government.js +50 -18
- package/modules/government/routing.js +39 -5
- package/modules/government/waiting.js +74 -0
- package/modules/grading/grader-subagent.js +30 -11
- package/modules/hall-ui/public/ai-studio-lib.js +268 -0
- package/modules/hall-ui/public/ai-studio.css +193 -0
- package/modules/hall-ui/public/ai-studio.html +98 -0
- package/modules/hall-ui/public/ai-studio.js +556 -0
- package/modules/hall-ui/public/ai-studio.states.json +94 -0
- package/modules/hall-ui/public/blockers-by-person.js +156 -0
- package/modules/hall-ui/public/blockers-lib.js +15 -1
- package/modules/hall-ui/public/blockers.html +8 -0
- package/modules/hall-ui/public/blockers.js +1 -1
- package/modules/hall-ui/public/city.html +15 -15
- package/modules/hall-ui/public/claim-action.js +3 -0
- package/modules/hall-ui/public/collab.html +12 -0
- package/modules/hall-ui/public/craft-mode.js +4 -1
- package/modules/hall-ui/public/criterion-uat-ui.js +4 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/genesis/r5-life-1-primordial-sea.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-2-first-cells-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-2-first-cells.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-3-genome-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-3-genome.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-4-membrane-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-4-membrane.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-5-nervous-system-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-5-nervous-system.jpg +0 -0
- package/modules/hall-ui/public/goal-inbox.js +1 -1
- package/modules/hall-ui/public/goals-render.js +4 -0
- package/modules/hall-ui/public/goals.html +8 -0
- package/modules/hall-ui/public/gov-living.js +6 -2
- package/modules/hall-ui/public/hall-ask.css +5 -0
- package/modules/hall-ui/public/hall-ask.js +39 -3
- package/modules/hall-ui/public/notes-panel.css +20 -0
- package/modules/hall-ui/public/notes-panel.js +146 -0
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/project-settings.js +7 -5
- package/modules/hall-ui/public/settings-ai-keys.js +124 -0
- package/modules/hall-ui/public/settings.html +15 -0
- package/modules/hall-ui/public/street-deck.js +12 -3
- package/modules/hall-ui/public/studio.css +90 -8
- package/modules/hall-ui/public/studio.html +39 -10
- package/modules/hall-ui/public/studio.js +148 -18
- package/modules/hall-ui/public/task.html +2 -0
- package/modules/hall-ui/public/task.js +4 -0
- package/modules/hall-ui/public/tweak-editor.js +12 -3
- package/modules/hall-ui/public/welcome-boot.css +42 -6
- package/modules/hall-ui/public/welcome-boot.js +20 -6
- package/modules/hall-ui/public/work.js +20 -0
- package/modules/hall-ui/records/ai-studio.md +36 -0
- package/modules/hall-ui/records/blockers-people.md +24 -0
- package/modules/lifecycle/db-analytics.js +10 -3
- package/modules/lifecycle/db-claims.js +32 -4
- package/modules/lifecycle/db-goals.js +48 -18
- package/modules/lifecycle/db-shared.js +9 -0
- package/modules/lifecycle/db-tasks.js +15 -6
- package/modules/lifecycle/db-versions.js +21 -8
- package/modules/lifecycle/done-when.js +23 -1
- package/modules/lifecycle/lifecycle.js +5 -0
- package/modules/lifecycle/migrations/lifecycle_017_notes.sql +46 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/notes.js +219 -0
- package/modules/lifecycle/page-notice.js +124 -0
- package/modules/lifecycle/page-tweak-artist.js +18 -1
- package/modules/lifecycle/publish-reconciler.js +67 -20
- package/modules/lifecycle/routes/claims.js +5 -2
- package/modules/lifecycle/routes/goals.js +3 -1
- package/modules/lifecycle/routes/notes.js +91 -0
- package/modules/lifecycle/routes/project-settings.js +43 -35
- package/modules/lifecycle/routes/task-route-helpers.js +21 -0
- package/modules/lifecycle/routes/task-write-routes.js +21 -3
- package/modules/lifecycle/routes/tasks.js +12 -12
- package/modules/lifecycle/ship-assurance.js +4 -1
- package/modules/lifecycle/ship-card.js +8 -2
- package/modules/lifecycle/task-needs-gate.js +70 -0
- package/modules/market-watch/CLAUDE.md +15 -0
- package/modules/market-watch/db.js +340 -0
- package/modules/market-watch/key.js +58 -0
- package/modules/market-watch/migrations/market_watch_001_tables.sql +249 -0
- package/modules/market-watch/migrations/market_watch_002_run.sql +46 -0
- package/modules/market-watch/module.json +26 -0
- package/modules/market-watch/pollers/hub-seed.js +54 -0
- package/modules/market-watch/pollers/weekly-run.js +100 -0
- package/modules/market-watch/research.js +350 -0
- package/modules/market-watch/run.js +245 -0
- package/modules/market-watch/seed/market-data.json +38 -0
- package/modules/market-watch/seed/partners-data.json +497 -0
- package/modules/market-watch/seed.js +252 -0
- package/modules/market-watch/store.js +230 -0
- package/modules/memory/routes/memory.js +14 -2
- package/modules/memory/routes/search.js +8 -2
- package/modules/onboarding/howto-moments.js +8 -1
- package/modules/platform-identity/account-logins.js +192 -0
- package/modules/platform-identity/migrations/platform_identity_034_account_logins.sql +96 -0
- package/modules/platform-identity/platform-identity.js +5 -4
- package/modules/platform-identity/routes/my-projects.js +6 -0
- package/modules/platform-identity/routes/profile.js +18 -0
- package/modules/platform-identity/routes/public-profile.js +10 -1
- package/modules/platform-identity/routes/sso.js +26 -1
- package/modules/platform-identity/tests/platform-identity.mjs +2 -1
- package/modules/provisioning/credential-preflight.js +13 -3
- package/modules/provisioning/pollers/app-liveness.js +25 -5
- package/modules/provisioning/routes/provisioning.js +1 -1
- package/modules/provisioning/starter-bundles.js +8 -2
- package/modules/public-landing/public/account.html +10 -2
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-space-nebula.jpg +0 -0
- package/modules/public-landing/public/assets/world/r3-space-nebula.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg.json +1 -1
- package/modules/public-landing/public/community.html +1 -1
- package/modules/public-landing/public/connections.html +2 -2
- package/modules/public-landing/public/guild.html +1 -1
- package/modules/public-landing/public/invitations.html +2 -2
- package/modules/public-landing/public/projects.html +5 -5
- package/modules/raise/views/nda.html +2 -0
- package/modules/specialities/db.js +32 -1
- package/modules/specialities/routes/specialities.js +10 -6
- package/modules/status-ui/public/index.html +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +286 -0
- package/scripts/bongos/autobongos-tweak-lane.js +46 -1
- package/scripts/bongos/backfill-subagent-rewards.js +2 -2
- package/scripts/bongos/claim.js +22 -1
- package/scripts/bongos/db-backup-nightly.sh +29 -0
- package/scripts/bongos/deploy-divergence.js +7 -0
- package/scripts/bongos/doc-cli-guard.js +6 -0
- package/scripts/bongos/fitness.js +3 -2
- package/scripts/bongos/gen-diagrams.js +3 -3
- package/scripts/bongos/lib.sh +4 -5
- package/scripts/bongos/migration-namespace.js +13 -1
- package/scripts/bongos/plain-cards.js +4 -0
- package/scripts/bongos/provision-github-setup.js +3 -1
- package/scripts/bongos/provision-units.js +6 -1
- package/scripts/bongos/publish-manifest.js +4 -0
- package/scripts/bongos/release.js +2 -0
- package/scripts/bongos/run-unit-tests.js +268 -82
- package/scripts/bongos/seed-tasks.example.json +56 -0
- package/scripts/bongos/seed-tasks.js +222 -0
- package/scripts/bongos/seed-verify-criteria.js +1 -2
- package/scripts/bongos/ship-finish.js +2 -0
- package/scripts/bongos/ship-resume.js +2 -0
- package/scripts/bongos/strand-lease.js +250 -0
- package/scripts/bongos/strand-watch.js +129 -12
- package/scripts/bongos/version-literal-guard.js +2 -2
- package/scripts/discord/post.js +4 -3
- package/scripts/gds/seed-tasks.js +7 -0
- package/scripts/gds/{build-bongos.js → strand-lease.js} +3 -3
- package/scripts/hall-preview/server.js +21 -0
- package/src/bongos/auth-config.js +29 -1
- package/src/bongos/auth-discord.js +3 -3
- package/src/bongos/auth-github.js +3 -6
- package/src/bongos/auth-google.js +217 -0
- package/src/bongos/device-approval.js +140 -0
- package/src/bongos/llm-pricing.js +17 -1
- package/src/bongos/metrics.js +48 -2
- package/src/bongos/middleware/rate-limit.js +30 -11
- package/src/bongos/module-scope-map.js +16 -1
- package/src/bongos/optimizer.js +15 -1
- package/src/bongos/payout-approvals.js +342 -0
- package/src/bongos/payout-gates.js +364 -0
- package/src/bongos/prelaunch-gate.js +7 -9
- package/src/bongos/prelaunch-switch.js +20 -0
- package/src/bongos/route-rank-check.js +3 -0
- package/src/bongos/routes/auth-google.js +246 -0
- package/src/bongos/routes/auth.js +38 -1
- package/src/bongos/routes/backup.js +33 -9
- package/src/bongos/routes/builders.js +57 -30
- package/src/bongos/routes/github.js +9 -0
- package/src/bongos/routes/me.js +18 -0
- package/src/bongos/routes/my-payouts.js +133 -0
- package/src/bongos/routes/payout-approvals.js +312 -0
- package/src/bongos/routes/payout-gates.js +179 -0
- package/src/bongos/routes.js +15 -1
- package/src/bongos/secret-box.js +18 -3
- package/src/bongos/serve-internal.js +47 -19
- package/src/bongos/sign-in-pages.js +138 -0
- package/src/bongos/task-needs.js +146 -0
- package/src/bongos-downloads.js +18 -1
- package/src/instance-config.js +28 -0
- package/src/module-api.js +14 -1
- package/src/platform-server.js +27 -1
- package/tests/account_logins.mjs +326 -0
- package/tests/account_logins_db.mjs +161 -0
- package/tests/ai_studio_billing.mjs +253 -0
- package/tests/ai_studio_generate.mjs +282 -0
- package/tests/ai_studio_keys.mjs +159 -0
- package/tests/ai_studio_requests.mjs +319 -0
- package/tests/auth_backchannel_logout_route.mjs +201 -0
- package/tests/auto_satisfy_criteria.mjs +10 -1
- package/tests/autobongos_tweak_lane.mjs +144 -0
- package/tests/autobongos_tweak_pin_identity.mjs +137 -0
- package/tests/backfill_subagent_rewards.mjs +17 -1
- package/tests/backup_trigger_lock.mjs +119 -0
- package/tests/batched_writes_parity.mjs +340 -0
- package/tests/blockers_people.mjs +198 -0
- package/tests/bounded_rate_maps.mjs +184 -0
- package/tests/claimable_blocker_status.mjs +1 -1
- package/tests/claims_batch_routes.mjs +421 -0
- package/tests/conductor_main_e2e.mjs +4 -0
- package/tests/config_read_cache.mjs +171 -0
- package/tests/copy_desk_page_editor.mjs +12 -0
- package/tests/cost_write_guard.mjs +200 -0
- package/tests/craft_achievements.mjs +33 -10
- package/tests/criterion_goal_attach.mjs +1 -1
- package/tests/db_backup_nightly.mjs +73 -1
- package/tests/deploy_divergence_line.mjs +7 -0
- package/tests/effective_visibility_predicate.mjs +12 -0
- package/tests/fitness.mjs +19 -0
- package/tests/fixtures/per-row-loops-legacy.cjs +245 -0
- package/tests/fixtures/seed-tasks/bongos-v1-idea-routing.json +256 -0
- package/tests/fixtures/seed-tasks/bv1-profiles.json +592 -0
- package/tests/fixtures/seed-tasks/bv2-ideator-surface.json +373 -0
- package/tests/fixtures/seed-tasks/fake-api.mjs +80 -0
- package/tests/github_one_trip.mjs +34 -0
- package/tests/github_repo_visibility.mjs +20 -0
- package/tests/goal_achievement.mjs +30 -1
- package/tests/goal_achievement_reward.mjs +205 -0
- package/tests/goal_archive_disposition.mjs +5 -5
- package/tests/goal_sort_staleness.mjs +2 -1
- package/tests/goal_tier.mjs +11 -1
- package/tests/google_fonts_nonblocking.mjs +84 -0
- package/tests/google_sign_in.mjs +401 -0
- package/tests/government_board_expiry.mjs +94 -0
- package/tests/government_protected_surfaces.mjs +1 -1
- package/tests/government_routes.mjs +146 -23
- package/tests/grader.mjs +31 -0
- package/tests/hall_ai_studio.mjs +347 -0
- package/tests/hall_city.mjs +8 -0
- package/tests/hall_page_gate_map.mjs +3 -0
- package/tests/hall_studio_home.mjs +61 -2
- package/tests/home_attention_cards.mjs +1 -1
- package/tests/hook_shell_tools.mjs +8 -0
- package/tests/hooks_drain.mjs +1 -1
- package/tests/host_topology_skips.mjs +2 -0
- package/tests/howto_moments.mjs +17 -0
- package/tests/human_signal.mjs +141 -0
- package/tests/install_script_reader.mjs +51 -0
- package/tests/integration_fixture_ids.mjs +82 -0
- package/tests/invite_expiry_db.mjs +13 -1
- package/tests/lib_sh_resolution.mjs +0 -4
- package/tests/market_watch_data.mjs +244 -0
- package/tests/market_watch_data_db.mjs +308 -0
- package/tests/market_watch_module.mjs +114 -0
- package/tests/market_watch_run.mjs +503 -0
- package/tests/metrics.mjs +124 -0
- package/tests/migration_namespace.mjs +14 -0
- package/tests/module_api.mjs +2 -0
- package/tests/module_loader.mjs +1 -1
- package/tests/module_skills_hook.mjs +69 -0
- package/tests/notes.mjs +182 -0
- package/tests/own_scope_guard.mjs +5 -0
- package/tests/page_tweak_marker_sql.mjs +76 -0
- package/tests/page_tweak_no_block.mjs +135 -0
- package/tests/page_tweak_notice.mjs +103 -0
- package/tests/payout_approvals.mjs +253 -0
- package/tests/payout_approvals_db.mjs +106 -0
- package/tests/payout_gates.mjs +322 -0
- package/tests/payout_gates_db.mjs +113 -0
- package/tests/planning_session_skill.mjs +13 -3
- package/tests/project_settings_page.mjs +29 -5
- package/tests/project_settings_spend_cap_route.mjs +235 -0
- package/tests/promotion_sweep.mjs +29 -1
- package/tests/provision.mjs +3 -2
- package/tests/provisioning_app_status.mjs +18 -0
- package/tests/provisioning_credential_preflight.mjs +53 -0
- package/tests/provisioning_env_manifest_edit_delete.mjs +223 -0
- package/tests/public_landing_projects.mjs +37 -0
- package/tests/publish_reconciler_overlap.mjs +60 -0
- package/tests/rank_change_broadcast_port.mjs +151 -0
- package/tests/seed_tasks.mjs +176 -0
- package/tests/seed_verify_criteria.mjs +2 -6
- package/tests/session_start_freshness.mjs +66 -1
- package/tests/shell_hooks.mjs +209 -0
- package/tests/ship_assurance.mjs +28 -0
- package/tests/ship_card.mjs +20 -5
- package/tests/site_classified_once.mjs +142 -0
- package/tests/speciality_db_shape.mjs +23 -0
- package/tests/speciality_session_skills.mjs +48 -2
- package/tests/strand_lease.mjs +249 -0
- package/tests/strand_watch.mjs +58 -0
- package/tests/task_criteria_routes.mjs +275 -0
- package/tests/task_criteria_unlink_db.mjs +123 -0
- package/tests/task_needs.mjs +233 -0
- package/tests/task_needs_db.mjs +110 -0
- package/tests/unit_runner_serial.mjs +266 -43
- package/tests/version_close_route.mjs +1 -1
- package/tests/version_literals.mjs +5 -7
- package/tests/welcome_boot.mjs +16 -5
- package/tests/welcome_boot_browser.mjs +36 -4
- package/docs/recipes/bongos-cli-release.md +0 -66
- package/scripts/bongos/backfill-disciplines.js +0 -144
- package/scripts/bongos/backfill-session-earnings.js +0 -123
- package/scripts/bongos/backfill-session-types.js +0 -87
- package/scripts/bongos/build-bongos.js +0 -94
- package/scripts/bongos/cleanup-orphans.js +0 -74
- package/scripts/bongos/seed-bongos-v1-idea-routing-tasks.js +0 -152
- package/scripts/bongos/seed-bv1-profiles-tasks.js +0 -135
- package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +0 -390
- package/scripts/bongos/smoke-batch-claim.sh +0 -238
- package/scripts/bongos/smoke-batch-release.sh +0 -316
- package/scripts/bongos/smoke-claim-consistency.sh +0 -222
- package/scripts/bongos/smoke-dependencies.sh +0 -143
- package/scripts/gds/backfill-disciplines.js +0 -7
- package/scripts/gds/backfill-session-earnings.js +0 -7
- package/scripts/gds/backfill-session-types.js +0 -7
- package/scripts/gds/cleanup-orphans.js +0 -7
- package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +0 -6
- package/scripts/gds/seed-bv1-profiles-tasks.js +0 -6
- package/scripts/gds/seed-bv2-ideator-surface-tasks.js +0 -6
- package/scripts/gds/smoke-batch-claim.sh +0 -8
- package/scripts/gds/smoke-batch-release.sh +0 -8
- package/scripts/gds/smoke-claim-consistency.sh +0 -8
- package/scripts/gds/smoke-dependencies.sh +0 -8
- package/tests/smoke_dependencies_witness.mjs +0 -46
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// modules/hall-ui/public/notes-panel.js — the Notes panel on a task or goal page
|
|
2
|
+
// (task 1004745, goal 1000095).
|
|
3
|
+
//
|
|
4
|
+
// Notes are information that helps scope a task or goal or helps finish it. They are
|
|
5
|
+
// shown GROUPED BY PURPOSE (scope, decisions, open questions, risks, progress, links)
|
|
6
|
+
// with the newest few open in each group and the rest folded behind "Show more", so a
|
|
7
|
+
// long history stays scannable instead of becoming one endless list. The grouping is
|
|
8
|
+
// the server's (GET /notes); this file only draws it and posts new notes.
|
|
9
|
+
//
|
|
10
|
+
// OTBNotes.mount(element, { task_id: 12 }) // or { goal_id: 3 }
|
|
11
|
+
//
|
|
12
|
+
// Dependency-free apart from window.BongosClient and window.OTB.escapeHtml, so both
|
|
13
|
+
// the task page and the goals page can load it.
|
|
14
|
+
|
|
15
|
+
(function () {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const API = '/api/bongos';
|
|
19
|
+
const esc = (s) => window.OTB.escapeHtml(s == null ? '' : String(s));
|
|
20
|
+
const client = () => window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
|
|
21
|
+
|
|
22
|
+
// A builder's words are quoted data: a long dash inside them is normalised the way the
|
|
23
|
+
// public feed does (cleanTitle, goal-inbox.js), then escaped.
|
|
24
|
+
const clean = (raw) => esc(String(raw == null ? '' : raw).replace(/\s*[—–]\s*/g, ', ').replace(/,\s*,/g, ',').trim());
|
|
25
|
+
|
|
26
|
+
function rel(iso) {
|
|
27
|
+
if (!iso) return '';
|
|
28
|
+
const sec = (Date.now() - new Date(iso).getTime()) / 1000;
|
|
29
|
+
if (sec < 3600) return Math.max(1, Math.floor(sec / 60)) + 'm ago';
|
|
30
|
+
if (sec < 86400) return Math.floor(sec / 3600) + 'h ago';
|
|
31
|
+
return Math.floor(sec / 86400) + 'd ago';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function noteHtml(n, kinds) {
|
|
35
|
+
const who = n.author_name || n.author_login || 'Someone';
|
|
36
|
+
const moves = kinds
|
|
37
|
+
.filter((k) => k.key !== n.kind)
|
|
38
|
+
.map((k) => `<option value="${esc(k.key)}">${esc(k.label)}</option>`).join('');
|
|
39
|
+
return `
|
|
40
|
+
<li class="np-note" data-note="${esc(n.id)}">
|
|
41
|
+
<p class="np-note__body">${clean(n.body)}</p>
|
|
42
|
+
<p class="np-note__meta">
|
|
43
|
+
<span>${esc(who)} · ${esc(rel(n.created_at))}${n.kind_source === 'auto' ? ' · sorted automatically' : ''}</span>
|
|
44
|
+
<label class="np-move">Move to
|
|
45
|
+
<select data-move="${esc(n.id)}" aria-label="Move this note to another group"><option value="">…</option>${moves}</select>
|
|
46
|
+
</label>
|
|
47
|
+
<button type="button" class="np-link" data-del="${esc(n.id)}">Delete</button>
|
|
48
|
+
</p>
|
|
49
|
+
</li>`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function groupHtml(g, kinds, expanded) {
|
|
53
|
+
const shown = expanded.has(g.kind) ? g.open.concat(g.folded) : g.open;
|
|
54
|
+
const more = expanded.has(g.kind) || !g.folded.length
|
|
55
|
+
? ''
|
|
56
|
+
: `<button type="button" class="np-link" data-more="${esc(g.kind)}">Show ${esc(g.folded.length)} more</button>`;
|
|
57
|
+
return `
|
|
58
|
+
<section class="np-group" data-kind="${esc(g.kind)}">
|
|
59
|
+
<h3 class="np-group__h">${esc(g.label)} <span class="np-count">${esc(g.count)}</span>
|
|
60
|
+
<span class="np-group__hint">${esc(g.hint)}</span></h3>
|
|
61
|
+
<ul class="np-list">${shown.map((n) => noteHtml(n, kinds)).join('')}</ul>
|
|
62
|
+
${more}
|
|
63
|
+
</section>`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function formHtml(kinds) {
|
|
67
|
+
const opts = kinds.map((k) => `<option value="${esc(k.key)}">${esc(k.label)}</option>`).join('');
|
|
68
|
+
return `
|
|
69
|
+
<form class="np-form" autocomplete="off">
|
|
70
|
+
<label class="np-field"><span>What would help someone scope this or finish it?</span>
|
|
71
|
+
<textarea name="body" rows="3" maxlength="4000" required
|
|
72
|
+
placeholder="The mobile layout can wait until the desktop one ships."></textarea>
|
|
73
|
+
</label>
|
|
74
|
+
<div class="np-actions">
|
|
75
|
+
<label class="np-field np-field--inline"><span>Group</span>
|
|
76
|
+
<select name="kind"><option value="">Sort it for me</option>${opts}</select>
|
|
77
|
+
</label>
|
|
78
|
+
<button type="submit" class="td-btn">Add note</button>
|
|
79
|
+
</div>
|
|
80
|
+
</form>`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function mount(el, subject) {
|
|
84
|
+
if (!el || !subject) return;
|
|
85
|
+
const qs = subject.task_id != null ? `task_id=${encodeURIComponent(subject.task_id)}` : `goal_id=${encodeURIComponent(subject.goal_id)}`;
|
|
86
|
+
// Mounting again on the same element (the goals page reuses one slot across goals)
|
|
87
|
+
// must not stack a second set of listeners: the previous set is dropped first.
|
|
88
|
+
if (el.npAbort) el.npAbort.abort();
|
|
89
|
+
const ctl = new AbortController();
|
|
90
|
+
el.npAbort = ctl;
|
|
91
|
+
const on = (type, fn) => el.addEventListener(type, fn, { signal: ctl.signal });
|
|
92
|
+
const expanded = new Set();
|
|
93
|
+
let data = null;
|
|
94
|
+
|
|
95
|
+
async function load() {
|
|
96
|
+
const res = await client().request('GET', `${API}/notes?${qs}`);
|
|
97
|
+
data = res.ok ? res.data : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function draw() {
|
|
101
|
+
if (!data) { el.innerHTML = '<p class="np-empty">Notes could not be loaded.</p>'; return; }
|
|
102
|
+
const body = data.groups.length
|
|
103
|
+
? data.groups.map((g) => groupHtml(g, data.kinds, expanded)).join('')
|
|
104
|
+
: '<p class="np-empty">No notes yet. Add what helps scope this or get it done.</p>';
|
|
105
|
+
el.innerHTML = `<div class="np">${body}${formHtml(data.kinds)}</div>`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// A read that lands after the panel was re-mounted for another task or goal is
|
|
109
|
+
// dropped, so one subject's notes never paint over the next one's.
|
|
110
|
+
async function refresh() { await load(); if (!ctl.signal.aborted) draw(); }
|
|
111
|
+
|
|
112
|
+
on('click', async (ev) => {
|
|
113
|
+
const t = ev.target;
|
|
114
|
+
if (!(t instanceof Element)) return;
|
|
115
|
+
if (t.dataset.more) { expanded.add(t.dataset.more); draw(); return; }
|
|
116
|
+
if (t.dataset.del) {
|
|
117
|
+
const res = await client().request('DELETE', `${API}/notes/${encodeURIComponent(t.dataset.del)}`);
|
|
118
|
+
if (res.status === 403) window.OTB.toast('Only the author or a Metic+ can delete that note.');
|
|
119
|
+
await refresh();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
on('change', async (ev) => {
|
|
123
|
+
const t = ev.target;
|
|
124
|
+
if (!(t instanceof HTMLSelectElement) || !t.dataset.move || !t.value) return;
|
|
125
|
+
const res = await client().request('PATCH', `${API}/notes/${encodeURIComponent(t.dataset.move)}`, { body: { kind: t.value } });
|
|
126
|
+
if (res.status === 403) window.OTB.toast('Only the author or a Metic+ can move that note.');
|
|
127
|
+
await refresh();
|
|
128
|
+
});
|
|
129
|
+
on('submit', async (ev) => {
|
|
130
|
+
ev.preventDefault();
|
|
131
|
+
const form = ev.target;
|
|
132
|
+
if (!(form instanceof HTMLFormElement)) return;
|
|
133
|
+
const text = form.elements.body.value.trim();
|
|
134
|
+
if (!text) return;
|
|
135
|
+
const body = Object.assign({ body: text }, subject);
|
|
136
|
+
if (form.elements.kind.value) body.kind = form.elements.kind.value;
|
|
137
|
+
const res = await client().request('POST', `${API}/notes`, { body });
|
|
138
|
+
if (!res.ok) { window.OTB.toast('That note could not be added.'); return; }
|
|
139
|
+
await refresh();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await refresh();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
window.OTBNotes = { mount };
|
|
146
|
+
})();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Project settings — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="The fundamentals of this project in one place — how tasks ship, the stale timer, the category set, how credits are calculated,
|
|
7
|
+
<meta name="description" content="The fundamentals of this project in one place — how tasks ship, the stale timer, the category set, how credits are calculated, why credits are not equity, and whether a credit-weighted pool can pay out.">
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
// WHAT THIS PAGE IS FOR. Goal 1000072's thesis: the project is legible to Claude and
|
|
5
5
|
// illegible to a person. Answering "how long before work is called stale", "what
|
|
6
|
-
// categories do we use", "how is a credit calculated", "
|
|
7
|
-
//
|
|
6
|
+
// categories do we use", "how is a credit calculated", "are credits equity" or "can
|
|
7
|
+
// a credit-weighted pool pay out" each meant reading source. This page is the
|
|
8
8
|
// translation layer — it invents no value and stores nothing of its own.
|
|
9
9
|
//
|
|
10
10
|
// THE RULE THAT SHAPES EVERY ROW: the SERVER decides what each row says and where it
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
// describes, one release later and on the screen the owner trusts most.
|
|
15
15
|
//
|
|
16
16
|
// A ROW THE READER CANNOT TYPE INTO MUST LOOK UNWRITABLE AND SAY WHY. Three of the
|
|
17
|
-
// five are read-only, and two of those are read-only for different reasons —
|
|
18
|
-
//
|
|
19
|
-
//
|
|
17
|
+
// five are read-only, and two of those are read-only for different reasons — credits
|
|
18
|
+
// are a settled owner decision that they are not equity (ADR 0387), and the
|
|
19
|
+
// credit-weighted pool is a pay type gated shut (ADR 0385). Both render as themselves
|
|
20
|
+
// rather than as a plausible zero.
|
|
20
21
|
(() => {
|
|
21
22
|
'use strict';
|
|
22
23
|
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
'read-only': ['ps-pill--readonly', 'committed config'],
|
|
70
71
|
'not-yet-real': ['ps-pill--readonly', 'not yet real'],
|
|
71
72
|
absent: ['ps-pill--absent', 'not configured'],
|
|
73
|
+
gated: ['ps-pill--absent', 'not available (gated)'],
|
|
72
74
|
unavailable: ['ps-pill--absent', 'unavailable'],
|
|
73
75
|
};
|
|
74
76
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// modules/hall-ui/public/settings-ai-keys.js — the "Your AI keys" panel on
|
|
2
|
+
// /builders/settings (task 1004919, goal 1000136, ADR 0392). A builder links
|
|
3
|
+
// their own OpenRouter or Gemini key here so AI studio generations run on their
|
|
4
|
+
// account and cost them nothing with us. The ONLY place a key is ever typed.
|
|
5
|
+
//
|
|
6
|
+
// Write-only: save, replace, remove. The server never sends a key back, only
|
|
7
|
+
// its last four characters, so once saved the full key is gone from the page.
|
|
8
|
+
// The panel stays hidden when the AI studio is not switched on (the read 404s).
|
|
9
|
+
(() => {
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const API = '/api/bongos';
|
|
13
|
+
const { escapeHtml, $, fmtDate } = window.OTB;
|
|
14
|
+
const toast = (msg) => window.OTB.toast(msg);
|
|
15
|
+
const api = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
|
|
16
|
+
const toAuth = () => window.location.assign(`${API}/auth/web/start`);
|
|
17
|
+
|
|
18
|
+
let storageConfigured = true;
|
|
19
|
+
|
|
20
|
+
function entryHtml(k) {
|
|
21
|
+
return `<div class="artkey-entry-row">
|
|
22
|
+
<input type="password" class="artkey-input aikeys-input" autocomplete="off" spellcheck="false"
|
|
23
|
+
aria-label="Your ${escapeHtml(k.label)} key" placeholder="Paste your ${escapeHtml(k.label)} key">
|
|
24
|
+
<button type="button" class="settings-btn craft-save aikeys-save">Save key</button>
|
|
25
|
+
</div>`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function rowHtml(k) {
|
|
29
|
+
const get = `<a href="${escapeHtml(k.learn_more_url)}" target="_blank" rel="noopener">Get a key ↗</a>`;
|
|
30
|
+
const head = `<strong>${escapeHtml(k.label)}</strong>`;
|
|
31
|
+
if (!storageConfigured) {
|
|
32
|
+
return `<div class="aikeys-row">${head}<p class="settings-card__hint">Key storage is not set up on this project yet.</p></div>`;
|
|
33
|
+
}
|
|
34
|
+
if (k.set) {
|
|
35
|
+
const added = k.set_at ? ` · added ${escapeHtml(fmtDate(k.set_at))}` : '';
|
|
36
|
+
return `<div class="aikeys-row" data-provider="${escapeHtml(k.provider)}">${head}
|
|
37
|
+
<p class="settings-card__ok">Key on file ending …${escapeHtml(k.last4 || '????')}${added}. Your generations run on it and we charge nothing.</p>
|
|
38
|
+
<div class="artkey-actions">
|
|
39
|
+
<button type="button" class="settings-btn aikeys-replace">Replace</button>
|
|
40
|
+
<button type="button" class="settings-btn settings-btn--quiet aikeys-remove">Remove</button>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="aikeys-entry" hidden></div>
|
|
43
|
+
</div>`;
|
|
44
|
+
}
|
|
45
|
+
return `<div class="aikeys-row" data-provider="${escapeHtml(k.provider)}">${head}
|
|
46
|
+
${entryHtml(k)}
|
|
47
|
+
<p class="settings-card__hint">${get} · Optional. Without one, generations run on ours and are charged to your balance.</p>
|
|
48
|
+
</div>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function wire(keys) {
|
|
52
|
+
for (const row of document.querySelectorAll('#aikeys-list .aikeys-row[data-provider]')) {
|
|
53
|
+
const provider = row.getAttribute('data-provider');
|
|
54
|
+
const k = keys.find((x) => x.provider === provider);
|
|
55
|
+
const bindEntry = (scope) => {
|
|
56
|
+
const input = scope.querySelector('.aikeys-input');
|
|
57
|
+
const save = scope.querySelector('.aikeys-save');
|
|
58
|
+
if (!input || !save) return;
|
|
59
|
+
save.addEventListener('click', () => saveKey(provider, input, save));
|
|
60
|
+
input.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); saveKey(provider, input, save); } });
|
|
61
|
+
};
|
|
62
|
+
bindEntry(row);
|
|
63
|
+
const replace = row.querySelector('.aikeys-replace');
|
|
64
|
+
if (replace) replace.addEventListener('click', () => {
|
|
65
|
+
const entry = row.querySelector('.aikeys-entry');
|
|
66
|
+
entry.innerHTML = entryHtml(k);
|
|
67
|
+
entry.hidden = false;
|
|
68
|
+
bindEntry(entry);
|
|
69
|
+
const i = entry.querySelector('.aikeys-input'); if (i) i.focus();
|
|
70
|
+
});
|
|
71
|
+
const remove = row.querySelector('.aikeys-remove');
|
|
72
|
+
if (remove) remove.addEventListener('click', () => removeKey(provider, k.label));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function saveKey(provider, input, btn) {
|
|
77
|
+
const key = (input.value || '').trim();
|
|
78
|
+
if (!key) { toast('Paste your key first.'); return; }
|
|
79
|
+
btn.disabled = true;
|
|
80
|
+
try {
|
|
81
|
+
const res = await api.request('PUT', `${API}/ai-studio/keys/${encodeURIComponent(provider)}`, { body: { key } });
|
|
82
|
+
input.value = '';
|
|
83
|
+
if (res.status === 401) { toAuth(); return; }
|
|
84
|
+
if (!res.ok) { toast((res.data && res.data.message) || `Could not save (HTTP ${res.status}).`); btn.disabled = false; return; }
|
|
85
|
+
toast('Key saved.');
|
|
86
|
+
await render();
|
|
87
|
+
} catch (err) {
|
|
88
|
+
toast('Could not save your key. Try again.');
|
|
89
|
+
btn.disabled = false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function removeKey(provider, label) {
|
|
94
|
+
if (!window.confirm(`Remove your ${label} key? Generations will run on ours and be charged to your balance.`)) return;
|
|
95
|
+
const res = await api.request('DELETE', `${API}/ai-studio/keys/${encodeURIComponent(provider)}`);
|
|
96
|
+
if (res.status === 401) { toAuth(); return; }
|
|
97
|
+
if (!res.ok) { toast('Could not remove your key. Try again.'); return; }
|
|
98
|
+
toast('Key removed.');
|
|
99
|
+
await render();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function render() {
|
|
103
|
+
const res = await api.request('GET', `${API}/ai-studio/keys`);
|
|
104
|
+
if (res.status === 401) { toAuth(); return false; }
|
|
105
|
+
if (res.status === 404) return false; // the AI studio is not switched on here
|
|
106
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
107
|
+
storageConfigured = res.data.storage_configured !== false;
|
|
108
|
+
const keys = res.data.keys || [];
|
|
109
|
+
$('#aikeys-list').innerHTML = keys.map(rowHtml).join('');
|
|
110
|
+
wire(keys);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function setup() {
|
|
115
|
+
try {
|
|
116
|
+
if (await render()) $('#ai-keys').hidden = false;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
$('#ai-keys').hidden = false;
|
|
119
|
+
$('#aikeys-list').innerHTML = '<p class="voices-empty">Your AI keys could not be loaded. Reload the page to try again.</p>';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setup();
|
|
124
|
+
})();
|
|
@@ -226,6 +226,20 @@
|
|
|
226
226
|
<p class="section-note">Stored encrypted; never shown again after saving — only its last four digits.</p>
|
|
227
227
|
</section>
|
|
228
228
|
|
|
229
|
+
<!-- YOUR AI KEYS — your own OpenRouter / Gemini keys for the AI studio
|
|
230
|
+
(task 1004919, ADR 0392). Rendered by settings-ai-keys.js against
|
|
231
|
+
/api/bongos/ai-studio/keys. Write-only: a key is never sent back,
|
|
232
|
+
only its last four characters. Hidden where the AI studio is off. -->
|
|
233
|
+
<section class="scroll" id="ai-keys" aria-labelledby="aikeys-h" data-group="access" hidden>
|
|
234
|
+
<h2 id="aikeys-h" class="scroll__h">Your AI keys</h2>
|
|
235
|
+
<p class="scroll__lede">
|
|
236
|
+
Link your own AI account so studio generations run on it and we charge
|
|
237
|
+
you nothing. Without a key, generations run on ours and come out of your balance.
|
|
238
|
+
</p>
|
|
239
|
+
<div class="settings-card" id="aikeys-list" aria-live="polite"></div>
|
|
240
|
+
<p class="section-note">Stored encrypted; never shown again after saving, only its last four characters.</p>
|
|
241
|
+
</section>
|
|
242
|
+
|
|
229
243
|
<!-- ============ group: preferences ============ -->
|
|
230
244
|
|
|
231
245
|
<!-- INTERACTION PROFILE — how replies are pitched for you (task 2010,
|
|
@@ -487,6 +501,7 @@
|
|
|
487
501
|
settings.js, which sits close to the 1500-line size ratchet. -->
|
|
488
502
|
<script src="/builders/settings-sessions.js?v=2026-08-29-sessions"></script>
|
|
489
503
|
<script src="/builders/settings-passkeys.js?v=2026-10-15-passkeys"></script>
|
|
504
|
+
<script src="/builders/settings-ai-keys.js?v=2026-10-28-ai-keys"></script>
|
|
490
505
|
<!-- task 1002514: the interaction-profile panel ("How Claude talks to you"), on
|
|
491
506
|
the same precedent and for the same reason — settings.js is against the
|
|
492
507
|
1500-line ratchet, and this panel shares nothing with the rest of the page. -->
|
|
@@ -123,12 +123,21 @@
|
|
|
123
123
|
// ---------- one card ----------
|
|
124
124
|
// A seat the server routed (task 1004705, `waits_on.real`) is named; an ask
|
|
125
125
|
// the docket did not route waits on you, or on others, unnamed.
|
|
126
|
+
// Who sits in the seat, by name (task 1004999): "Lars, the owner", "Ana and Ben, the
|
|
127
|
+
// board". Falls back to the seat alone when the server named nobody.
|
|
128
|
+
function whoWords(w) {
|
|
129
|
+
const label = str(w && w.label) || 'others';
|
|
130
|
+
const names = (Array.isArray(w && w.people) ? w.people : []).map((p) => str(p && (p.name || p.login))).filter(Boolean);
|
|
131
|
+
if (!names.length) return label;
|
|
132
|
+
const who = names.length === 1 ? names[0] : names.length === 2 ? names[0] + ' and ' + names[1] : names[0] + ' and ' + (names.length - 1) + ' others';
|
|
133
|
+
return who + ', ' + label;
|
|
134
|
+
}
|
|
126
135
|
function seatWords(ask) {
|
|
127
136
|
const w = ask.waits_on || {};
|
|
128
137
|
if (!w.real) return ask.needs_you ? 'waits on you' : 'waits on others';
|
|
129
138
|
const label = str(w.label) || 'someone';
|
|
130
139
|
if (ask.needs_you) return label === 'you' || label === 'the owner' || /^the .* Lead$/.test(label) ? 'waits on you' : 'waits on you · ' + label;
|
|
131
|
-
return 'waits on ' +
|
|
140
|
+
return 'waits on ' + whoWords(w);
|
|
132
141
|
}
|
|
133
142
|
// ---------- the words, in the asker's own voice (task 1004702) ----------
|
|
134
143
|
// The owner's pick (2026-10-13): a card speaks as "I", the agent that asked.
|
|
@@ -159,7 +168,7 @@
|
|
|
159
168
|
else if (/^(board|committee):/.test(seat) && w.real && str(w.label)) you = upperFirst(w.label) + ' answers ' + (ask.batch ? 'these' : 'this') + ', and you sit in it.';
|
|
160
169
|
else you = 'Only you can answer ' + (ask.batch ? 'these' : 'this') + '.';
|
|
161
170
|
} else if (ask) {
|
|
162
|
-
rest = 'It waits on ' + (str(w.label) || 'others') + '.';
|
|
171
|
+
rest = 'It waits on ' + (w.real ? whoWords(w) : (str(w.label) || 'others')) + '.';
|
|
163
172
|
}
|
|
164
173
|
const left = str(silent).trim() ? 'Left alone, ' + lowerFirst(stop(silent)) : '';
|
|
165
174
|
return { you: you, rest: [rest, left].filter(Boolean).join(' ') };
|
|
@@ -363,7 +372,7 @@
|
|
|
363
372
|
|
|
364
373
|
const model = {
|
|
365
374
|
actFor: actFor, optionPlan: optionPlan, cardView: cardView, queueOf: queueOf, scopesOf: scopesOf, likesOf: likesOf, sessionCounts: sessionCounts,
|
|
366
|
-
batchAsk: batchAsk, seatsFor: seatsFor, deckModel: deckModel, saidOf: saidOf, footOf: footOf, holdWords: holdWords, waitWords: waitWords,
|
|
375
|
+
batchAsk: batchAsk, seatsFor: seatsFor, deckModel: deckModel, saidOf: saidOf, footOf: footOf, whoWords: whoWords, holdWords: holdWords, waitWords: waitWords,
|
|
367
376
|
BATCH: BATCH, ALIAS: ALIAS,
|
|
368
377
|
};
|
|
369
378
|
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
1003117), which still answers the reviews filed before Tweak Mode (TW14) in
|
|
31
31
|
its own quiet dialog, in the hall's own tokens as it always was. */
|
|
32
32
|
|
|
33
|
+
body[data-page="ai-studio"],
|
|
33
34
|
body[data-page="studio"] {
|
|
34
35
|
--st-room: #07080d;
|
|
35
36
|
--st-frame: #14151b;
|
|
@@ -85,6 +86,7 @@ body[data-page="studio"] {
|
|
|
85
86
|
inks are set darker than the mockup's so they hold AA on the glass over a
|
|
86
87
|
light wall (records/studio.md): the link, and the credits figure and focus
|
|
87
88
|
ring, which by night are the accent. */
|
|
89
|
+
body[data-page="ai-studio"][data-sky="day"],
|
|
88
90
|
body[data-page="studio"][data-sky="day"] {
|
|
89
91
|
--st-room: #efe7dc;
|
|
90
92
|
--st-frame: #d6c9b7;
|
|
@@ -136,6 +138,7 @@ body[data-page="studio"][data-sky="day"] .st-overlay {
|
|
|
136
138
|
--st-scrim: rgba(239, 231, 220, .9);
|
|
137
139
|
color-scheme: light;
|
|
138
140
|
}
|
|
141
|
+
body[data-page="ai-studio"][data-sky="day"] .studio,
|
|
139
142
|
body[data-page="studio"][data-sky="day"] .studio { color-scheme: light; }
|
|
140
143
|
/* The Approval queue (approval-queue.css) is night glass on its own root; over
|
|
141
144
|
the day room it wears the same day glass as the studio's own panels. Its
|
|
@@ -163,7 +166,9 @@ body[data-page="studio"][data-sky="day"] .aq-overlay :focus-visible { outline-co
|
|
|
163
166
|
|
|
164
167
|
/* The stage fills the content box: the hall's content column is a reading
|
|
165
168
|
width, and the artboard is the whole window. */
|
|
169
|
+
body[data-page="ai-studio"] .app-content,
|
|
166
170
|
body[data-page="studio"] .app-content { max-width: none; padding: 0; }
|
|
171
|
+
body[data-page="ai-studio"] .app-foot,
|
|
167
172
|
body[data-page="studio"] .app-foot { display: none; }
|
|
168
173
|
|
|
169
174
|
.studio {
|
|
@@ -184,6 +189,7 @@ html[data-fullscreen="on"] .studio { min-height: 100vh; }
|
|
|
184
189
|
|
|
185
190
|
/* In full screen the shell's one button floats over the night room in either
|
|
186
191
|
theme, so on this page it wears the room's glass, not the hall's ink. */
|
|
192
|
+
html[data-fullscreen="on"] body[data-page="ai-studio"] .fullscreen-toggle,
|
|
187
193
|
html[data-fullscreen="on"] body[data-page="studio"] .fullscreen-toggle {
|
|
188
194
|
color: var(--st-ink);
|
|
189
195
|
background: var(--st-glass);
|
|
@@ -451,36 +457,112 @@ body[data-sky="day"] .st-sunpatch { opacity: .7; }
|
|
|
451
457
|
flex-direction: column;
|
|
452
458
|
gap: 12px;
|
|
453
459
|
}
|
|
460
|
+
/* The things to do (Resume and the two doors): the accent tint and an arrow, so
|
|
461
|
+
they read as buttons to press. The overview below (.st-glance) stays quiet. */
|
|
454
462
|
.st-resume,
|
|
455
463
|
.st-action {
|
|
464
|
+
position: relative;
|
|
456
465
|
display: flex;
|
|
457
466
|
flex-direction: column;
|
|
458
467
|
align-items: flex-start;
|
|
459
468
|
justify-content: center;
|
|
460
469
|
gap: 3px;
|
|
461
470
|
min-height: 72px;
|
|
462
|
-
padding: 10px 24px;
|
|
471
|
+
padding: 10px 52px 10px 24px;
|
|
463
472
|
box-sizing: border-box;
|
|
464
473
|
border-radius: 22px;
|
|
465
474
|
text-align: left;
|
|
475
|
+
background: var(--st-resume);
|
|
476
|
+
border: 1px solid var(--st-resume-edge);
|
|
466
477
|
backdrop-filter: blur(20px);
|
|
467
478
|
-webkit-backdrop-filter: blur(20px);
|
|
468
479
|
}
|
|
469
|
-
.st-resume
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
480
|
+
.st-resume::after,
|
|
481
|
+
.st-action::after {
|
|
482
|
+
content: "\2192";
|
|
483
|
+
position: absolute;
|
|
484
|
+
right: 22px;
|
|
485
|
+
top: 50%;
|
|
486
|
+
transform: translateY(-50%);
|
|
487
|
+
font-size: 20px;
|
|
488
|
+
color: var(--st-link);
|
|
473
489
|
}
|
|
490
|
+
.st-resume { text-decoration: none; }
|
|
474
491
|
.st-resume[hidden] { display: none; }
|
|
492
|
+
/* The AI door (task 1004921) is a link, not a button: same row, no underline. */
|
|
493
|
+
a.st-action { text-decoration: none; }
|
|
494
|
+
a.st-action[hidden], a.st-glance__row[hidden] { display: none; }
|
|
495
|
+
a.st-glance__row { text-decoration: none; }
|
|
475
496
|
.st-action {
|
|
476
497
|
cursor: pointer;
|
|
477
|
-
background: var(--st-glass);
|
|
478
|
-
border: 1px solid var(--st-glass-edge);
|
|
479
498
|
color: var(--st-ink);
|
|
480
499
|
}
|
|
481
|
-
.st-
|
|
500
|
+
.st-resume:hover,
|
|
501
|
+
.st-action:hover { border-color: var(--st-focus); background: var(--st-close); }
|
|
482
502
|
.st-resume__label,
|
|
483
503
|
.st-action__label { font-size: 17px; color: var(--st-ink); }
|
|
504
|
+
|
|
505
|
+
/* The full-page reader: in place of the room, never over it. */
|
|
506
|
+
html.st-reading .st-words,
|
|
507
|
+
html.st-reading .st-actions,
|
|
508
|
+
html.st-reading .st-glance { display: none; }
|
|
509
|
+
.st-doc { position: relative; z-index: 1; width: min(720px, 100%); margin-top: 32px; }
|
|
510
|
+
.st-doc[hidden] { display: none; }
|
|
511
|
+
.st-doc__back { display: inline-block; min-height: 24px; font-size: 14px; color: var(--st-link); }
|
|
512
|
+
.st-doc__title { margin: 16px 0 8px; font-size: 28px; font-weight: 400; color: var(--st-ink); }
|
|
513
|
+
.st-doc__meta { margin: 0 0 20px; font-size: 13px; color: var(--st-ink-faint); }
|
|
514
|
+
.st-doc__text { font-size: 17px; line-height: 1.65; color: var(--st-ink); }
|
|
515
|
+
.st-doc__text h3, .st-doc__h { margin: 28px 0 8px; font-size: 19px; font-weight: 500; color: var(--st-ink); }
|
|
516
|
+
.st-doc__text p, .st-doc__text ul { margin: 0 0 14px; }
|
|
517
|
+
.st-doc__text ul, .st-doc__ideas { padding-left: 22px; }
|
|
518
|
+
.st-doc__ideas a { color: var(--st-link); }
|
|
519
|
+
.st-doc__img { display: block; max-width: 100%; margin: 12px 0; border-radius: 12px; }
|
|
520
|
+
.st-doc-list { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
|
|
521
|
+
.st-doc-list__row {
|
|
522
|
+
display: flex; flex-direction: column; gap: 4px; padding: 16px 20px; border-radius: 18px;
|
|
523
|
+
background: var(--st-glass); border: 1px solid var(--st-glass-edge); text-decoration: none; color: var(--st-ink);
|
|
524
|
+
}
|
|
525
|
+
.st-doc-list__row:hover { border-color: var(--st-rule); }
|
|
526
|
+
.st-doc-list__page { font-size: 17px; }
|
|
527
|
+
.st-doc-list__meta { font-size: 12px; color: var(--st-ink-faint); }
|
|
528
|
+
.st-doc-list__text { font-size: 14px; color: var(--st-ink-faint); }
|
|
529
|
+
.st-doc__back:focus-visible, .st-doc-list__row:focus-visible { outline: 2px solid var(--st-focus); outline-offset: 3px; }
|
|
530
|
+
|
|
531
|
+
/* Overview: small text rows, no glass, set apart under the actions. */
|
|
532
|
+
.st-glance {
|
|
533
|
+
margin-top: 40px;
|
|
534
|
+
width: min(420px, 100%);
|
|
535
|
+
display: flex;
|
|
536
|
+
flex-direction: column;
|
|
537
|
+
gap: 2px;
|
|
538
|
+
padding-top: 16px;
|
|
539
|
+
border-top: 1px solid var(--st-glass-edge);
|
|
540
|
+
}
|
|
541
|
+
.st-glance__head {
|
|
542
|
+
font-family: var(--st-mono);
|
|
543
|
+
font-size: 11px;
|
|
544
|
+
letter-spacing: .08em;
|
|
545
|
+
text-transform: uppercase;
|
|
546
|
+
color: var(--st-ink-faint);
|
|
547
|
+
margin-bottom: 4px;
|
|
548
|
+
}
|
|
549
|
+
.st-glance__row {
|
|
550
|
+
display: flex;
|
|
551
|
+
flex-wrap: wrap;
|
|
552
|
+
align-items: baseline;
|
|
553
|
+
gap: 2px 12px;
|
|
554
|
+
min-height: 32px;
|
|
555
|
+
padding: 4px 0;
|
|
556
|
+
border: 0;
|
|
557
|
+
background: none;
|
|
558
|
+
cursor: pointer;
|
|
559
|
+
text-align: left;
|
|
560
|
+
font: inherit;
|
|
561
|
+
}
|
|
562
|
+
.st-glance__label { font-size: 14px; color: var(--st-ink-faint); text-decoration: underline; text-underline-offset: 3px; }
|
|
563
|
+
.st-glance__detail { font-size: 12px; color: var(--st-ink-faint); }
|
|
564
|
+
.st-glance__row:hover .st-glance__label { color: var(--st-ink); }
|
|
565
|
+
.st-glance__row:focus-visible { outline: 2px solid var(--st-focus); outline-offset: 3px; }
|
|
484
566
|
.st-resume__detail { font-size: 13px; color: var(--st-link); }
|
|
485
567
|
.st-action__detail { font-size: 13px; color: var(--st-ink-faint); }
|
|
486
568
|
.st-resume:focus-visible,
|
|
@@ -127,16 +127,13 @@
|
|
|
127
127
|
<span class="st-action__label">Approve and confirm tweaks</span>
|
|
128
128
|
<span class="st-action__detail" id="studio-approvals-detail">tweaks waiting for your sign-off</span>
|
|
129
129
|
</button>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<span class="st-action__label">From the live site</span>
|
|
138
|
-
<span class="st-action__detail" id="st-site-detail">reviews written on the live site</span>
|
|
139
|
-
</button>
|
|
130
|
+
<!-- Task 1004921 (goal 1000136): the door to the studio's AI pages,
|
|
131
|
+
shown only where the ai-studio module is on. Its line points at
|
|
132
|
+
the newest request while it is being made or just finished. -->
|
|
133
|
+
<a class="st-action" id="st-ai" href="/builders/ai-studio" hidden>
|
|
134
|
+
<span class="st-action__label">Make something with AI</span>
|
|
135
|
+
<span class="st-action__detail" id="st-ai-detail">sketches in, ideas and pictures out</span>
|
|
136
|
+
</a>
|
|
140
137
|
<!-- The retired review queue (task 1004325 / TW14), a quiet door that
|
|
141
138
|
shows only while reviews filed before Tweak Mode still wait. -->
|
|
142
139
|
<button type="button" class="st-older" id="st-older" aria-haspopup="dialog" hidden>
|
|
@@ -144,6 +141,37 @@
|
|
|
144
141
|
</button>
|
|
145
142
|
</div>
|
|
146
143
|
|
|
144
|
+
<!-- Overview, not actions: where things stand. Quiet text rows under the
|
|
145
|
+
actions so the three above read as the things to do. -->
|
|
146
|
+
<div class="st-glance" role="group" aria-label="Overview">
|
|
147
|
+
<span class="st-glance__head">Overview</span>
|
|
148
|
+
<button type="button" class="st-glance__row" id="st-status" aria-haspopup="dialog">
|
|
149
|
+
<span class="st-glance__label">Needs to be reviewed</span>
|
|
150
|
+
<span class="st-glance__detail" id="st-status-detail">pages waiting on you, and how far each surface has come</span>
|
|
151
|
+
</button>
|
|
152
|
+
<!-- Task 1004499: the reviews builders wrote on the public site with
|
|
153
|
+
its Artist review button. Each opens here, in the palette's panel. -->
|
|
154
|
+
<button type="button" class="st-glance__row" id="st-site" aria-haspopup="dialog">
|
|
155
|
+
<span class="st-glance__label">From the live site</span>
|
|
156
|
+
<span class="st-glance__detail" id="st-site-detail">reviews written on the live site</span>
|
|
157
|
+
</button>
|
|
158
|
+
<!-- Task 1004921: what is left of the AI balance, where paying from a
|
|
159
|
+
balance is set up. -->
|
|
160
|
+
<a class="st-glance__row" id="st-ai-balance" href="/builders/ai-studio?view=balance" hidden>
|
|
161
|
+
<span class="st-glance__label">AI balance</span>
|
|
162
|
+
<span class="st-glance__detail" id="st-ai-balance-detail"></span>
|
|
163
|
+
</a>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- The full-page reader (not a dialog): the live site's reviews as a list
|
|
167
|
+
(?view=site) or one doc (?note=N). Shown in place of the room's
|
|
168
|
+
greeting and doors while html.st-reading is set. -->
|
|
169
|
+
<section class="st-doc" id="st-doc" aria-labelledby="st-doc-title" hidden>
|
|
170
|
+
<a class="st-doc__back" href="/builders/studio">← Back to the studio</a>
|
|
171
|
+
<h2 class="st-doc__title" id="st-doc-title" tabindex="-1">Review</h2>
|
|
172
|
+
<div class="st-doc__body" id="st-doc-body"></div>
|
|
173
|
+
</section>
|
|
174
|
+
|
|
147
175
|
<!-- The two small glass panels: the Tweak/CopyWrite Flow and Artist
|
|
148
176
|
Review Status. The Approval queue is its own component
|
|
149
177
|
(approval-queue.js), mounted by studio.js. -->
|
|
@@ -206,6 +234,7 @@
|
|
|
206
234
|
the studio's own. -->
|
|
207
235
|
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-tw23"></script>
|
|
208
236
|
<script src="/builders/studio-lib.js?v=2026-10-09-day"></script>
|
|
237
|
+
<script src="/builders/ai-studio-lib.js?v=2026-10-29-ais1"></script>
|
|
209
238
|
<script src="/builders/studio.js?v=2026-10-09-day"></script>
|
|
210
239
|
</body>
|
|
211
240
|
</html>
|