@bongos/core 1.24.78 → 1.24.80
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 +498 -353
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/design/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +1 -1
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0274-one-kernel-three-role-packs.md +2 -2
- package/docs/adr/0399-the-visor-is-glass-over-the-scene-and-owns-no-camera.md +91 -0
- package/docs/adr/0400-a-chain-reserves-work-and-each-sub-quest-is-still-claimed-alone.md +109 -0
- package/docs/adr/0401-one-options-menu-opens-every-settings-screen-in-a-frame.md +53 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +195 -3
- package/docs/api-reference.md +8 -4
- package/docs/architecture.md +7 -3
- package/docs/copy-inventory.md +390 -370
- package/docs/copy-registry.json +719 -518
- package/docs/design/characters/character-sprites-source.webp +0 -0
- package/docs/file-map.md +7 -8
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +5 -0
- package/docs/onboarding/slash-commands.md +1 -1
- package/docs/packs/{engineer.md → artisan.md} +5 -5
- package/docs/packs/artist.md +50 -4
- package/docs/page-inventory.json +15 -4
- package/docs/page-readings.json +3182 -3336
- package/docs/{packs/ideator.md → recipes/ideation-playbook.md} +5 -48
- package/migrations/core_296_crafts_artisan_artist.sql +72 -0
- package/migrations/core_297_builder_character.sql +30 -0
- package/modules/copy-desk/proposals.js +1 -1
- package/modules/economy/credits-by-craft.js +29 -15
- package/modules/hall-ui/public/ai-studio.html +4 -4
- package/modules/hall-ui/public/atlas.html +5 -5
- package/modules/hall-ui/public/atlas.js +3 -1
- package/modules/hall-ui/public/blockers.html +5 -5
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/characters/artisan.png +0 -0
- package/modules/hall-ui/public/characters/artist.png +0 -0
- package/modules/hall-ui/public/characters/patron.png +0 -0
- package/modules/hall-ui/public/city.css +2 -1
- package/modules/hall-ui/public/city.html +9 -6
- package/modules/hall-ui/public/city.js +7 -1
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/craft-mode.js +258 -64
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/diagram-viewer.js +2 -1
- package/modules/hall-ui/public/dom-utils.js +1 -1
- package/modules/hall-ui/public/goal-map.html +9 -6
- package/modules/hall-ui/public/goals.html +5 -5
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/hall-render.js +3 -1
- package/modules/hall-ui/public/home-city-draw.js +3 -3
- package/modules/hall-ui/public/home-city.css +4 -0
- package/modules/hall-ui/public/home-city.js +17 -9
- package/modules/hall-ui/public/home-gallery.js +8 -1
- package/modules/hall-ui/public/home-idea-box.css +2 -2
- package/modules/hall-ui/public/home-idea-box.js +3 -3
- package/modules/hall-ui/public/idea.html +5 -5
- package/modules/hall-ui/public/ideas.html +5 -5
- package/modules/hall-ui/public/index.html +11 -7
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch.html +6 -9
- package/modules/hall-ui/public/market-watch.js +22 -45
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/options-menu.js +409 -0
- package/modules/hall-ui/public/paintbrush.js +3 -3
- package/modules/hall-ui/public/palette.js +11 -13
- package/modules/hall-ui/public/primer.html +5 -5
- package/modules/hall-ui/public/profile-crafts.js +1 -1
- package/modules/hall-ui/public/profile-roles.js +23 -19
- package/modules/hall-ui/public/profile.css +1 -1
- package/modules/hall-ui/public/profile.html +19 -18
- package/modules/hall-ui/public/profile.js +19 -18
- package/modules/hall-ui/public/profile.states.json +4 -4
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/roster.html +4 -4
- package/modules/hall-ui/public/roster.js +3 -3
- package/modules/hall-ui/public/settings.css +24 -4
- package/modules/hall-ui/public/settings.html +21 -17
- package/modules/hall-ui/public/settings.js +44 -44
- package/modules/hall-ui/public/shell-game.css +65 -0
- package/modules/hall-ui/public/shell-game.js +283 -3
- package/modules/hall-ui/public/shell.js +7 -26
- package/modules/hall-ui/public/sky-interaction.js +7 -0
- package/modules/hall-ui/public/speciality-tools.js +1 -2
- package/modules/hall-ui/public/street-inside.js +3 -4
- package/modules/hall-ui/public/street.html +4 -4
- package/modules/hall-ui/public/studio.html +5 -5
- package/modules/hall-ui/public/style.css +73 -47
- package/modules/hall-ui/public/task-detail.js +5 -4
- package/modules/hall-ui/public/task.html +5 -5
- package/modules/hall-ui/public/thinking.css +10 -0
- package/modules/hall-ui/public/thinking.html +9 -6
- package/modules/hall-ui/public/tweak-editor.html +5 -5
- package/modules/hall-ui/public/tweak-editor.js +5 -1
- package/modules/hall-ui/public/update-pill.js +91 -2
- package/modules/hall-ui/public/visor.css +80 -0
- package/modules/hall-ui/public/visor.js +316 -0
- package/modules/hall-ui/public/watch-repo-invite.js +159 -0
- package/modules/hall-ui/public/watch.html +6 -5
- package/modules/hall-ui/public/watch.js +17 -2
- package/modules/hall-ui/public/welcome-boot.js +15 -6
- package/modules/hall-ui/public/work.html +5 -5
- package/modules/hall-ui/records/builders-roster.md +2 -2
- package/modules/hall-ui/records/craft-mode.md +13 -11
- package/modules/hall-ui/records/game-mode.md +2 -2
- package/modules/hall-ui/records/governor-street.md +1 -1
- package/modules/hall-ui/records/home-city.md +3 -1
- package/modules/hall-ui/records/market-watch-settings.md +2 -0
- package/modules/hall-ui/records/options-menu.md +15 -0
- package/modules/hall-ui/records/profile-roles.md +11 -11
- package/modules/hall-ui/records/sky.md +3 -3
- package/modules/hall-ui/records/start-screen.md +21 -0
- package/modules/hall-ui/records/tweak-editor.md +1 -1
- package/modules/hall-ui/records/update-pill.md +12 -0
- package/modules/hall-ui/records/visor.md +23 -0
- package/modules/hall-ui/records/welcome-tower.md +5 -5
- package/modules/ideas/ideator-stats.js +3 -1
- package/modules/ideas/inbox.js +2 -2
- package/modules/lifecycle/activity.js +1 -1
- package/modules/lifecycle/cascade.js +4 -4
- package/modules/lifecycle/goal-spec-task.js +1 -1
- package/modules/lifecycle/help-requests.js +19 -15
- package/modules/lifecycle/kickoff-checklist.js +3 -3
- package/modules/lifecycle/kickoff-seed.js +1 -1
- package/modules/lifecycle/migrations/lifecycle_021_crafts_artisan_artist.sql +29 -0
- package/modules/lifecycle/mingle.js +4 -4
- package/modules/lifecycle/page-tweak-write.js +1 -1
- package/modules/lifecycle/routes/task-write-routes.js +3 -1
- package/modules/lifecycle/routes/tasks.js +3 -3
- package/modules/lifecycle/ship-card.js +2 -3
- package/modules/lifecycle/task-classifier.js +39 -29
- package/modules/lifecycle/tests/cascade.mjs +7 -7
- package/modules/onboarding/founding-mode.js +3 -0
- package/modules/onboarding/genesis-home.js +21 -1
- package/modules/onboarding/routes/onboarding.js +2 -0
- package/modules/page-notes/notes.js +3 -2
- package/modules/page-notes/split.js +1 -1
- package/modules/platform-identity/craft-rollup.js +35 -7
- package/modules/platform-identity/migrations/platform_identity_036_crafts_artisan_artist.sql +45 -0
- package/modules/platform-identity/platform-identity.js +2 -2
- package/modules/platform-identity/routes/community-leaderboard.js +2 -1
- package/modules/platform-identity/routes/community-search.js +2 -1
- package/modules/platform-identity/routes/sso.js +2 -1
- package/modules/platform-identity/tests/platform-identity.mjs +6 -6
- package/modules/public-landing/public/community.html +3 -5
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects.html +1 -1
- package/modules/specialities/migrations/specialities_004_crafts_artisan_artist.sql +30 -0
- package/modules/specialities/routes/specialities.js +2 -2
- package/modules/status-ui/public/status.js +1 -1
- package/modules/ui-design/kit/fixtures/community-leaderboard.json +10 -10
- package/modules/ui-design/kit/fixtures/community-search.json +2 -2
- package/modules/ui-design/kit/fixtures/scouting-recruiter.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +76 -0
- package/scripts/bongos/autobongos-host.js +19 -4
- package/scripts/bongos/autobongos-loop.js +1 -1
- package/scripts/bongos/claude-materialize.js +20 -1
- package/scripts/bongos/discipline-modes.json +7 -12
- package/scripts/bongos/fitness.js +1 -1
- package/scripts/bongos/gen-hall-sitemap.js +7 -7
- package/scripts/bongos/grade-smoke.js +2 -2
- package/scripts/bongos/init.js +2 -2
- package/scripts/bongos/newcomer-floor.config.json +1 -2
- package/scripts/bongos/newcomer-floor.js +4 -3
- package/scripts/bongos/newcomer-floor.templates.json +10 -12
- package/scripts/bongos/newcomer-floor.test.js +47 -38
- package/scripts/bongos/role-pack-guard.js +5 -4
- package/scripts/bongos/seed-tasks.example.json +3 -3
- package/scripts/bongos/setup.js +20 -107
- package/scripts/bongos/start.js +2 -2
- package/scripts/bongos/task.js +5 -4
- package/scripts/bongos/triage.js +1 -1
- package/scripts/bongos/worktree.js +16 -4
- package/scripts/setup-builder.js +6 -46
- package/src/bongos/auth-admission.js +8 -8
- package/src/bongos/craft-mode.js +2 -0
- package/src/bongos/db.js +39 -1
- package/src/bongos/github-repos.js +78 -0
- package/src/bongos/module-scope-map.js +22 -0
- package/src/bongos/role-stats.js +27 -11
- package/src/bongos/routes/github.js +100 -2
- package/src/bongos/routes/me.js +33 -0
- package/src/bongos/serve-internal.js +26 -9
- package/src/crafts.js +66 -0
- package/src/module-api.js +6 -1
- package/src/module-loader/manifest-schema.js +16 -0
- package/src/modules.js +2 -2
- package/src/role-responsibilities.js +6 -5
- package/tests/acclaim_feed_evidence_db.mjs +1 -1
- package/tests/activity_rollup_as_of.mjs +1 -1
- package/tests/api_alias_caller_ratchet.mjs +3 -3
- package/tests/application_lifecycle.mjs +1 -1
- package/tests/artist_review.mjs +1 -1
- package/tests/artist_review_goal.mjs +1 -1
- package/tests/autobongos_host.mjs +19 -1
- package/tests/blocker_hall_live_proof.mjs +1 -1
- package/tests/blocker_tasks_named_db.mjs +2 -2
- package/tests/canonical_profile_url.mjs +1 -1
- package/tests/cap_newcomer_tasks.mjs +13 -13
- package/tests/character_creation.mjs +294 -0
- package/tests/claim_gate_open_blocker.mjs +2 -2
- package/tests/claim_gate_rebase.mjs +6 -6
- package/tests/claude_materialize.mjs +75 -1
- package/tests/clawback_confirmed_backlog.mjs +3 -3
- package/tests/collab_page.mjs +8 -9
- package/tests/collab_self_settling.mjs +2 -2
- package/tests/community_leaderboard_route.mjs +13 -5
- package/tests/community_proof_db.mjs +3 -0
- package/tests/community_search_route.mjs +7 -6
- package/tests/conductor.mjs +1 -1
- package/tests/conductor_main_e2e.mjs +8 -8
- package/tests/copy_desk_flags_db.mjs +1 -1
- package/tests/copy_desk_page_approve.mjs +2 -2
- package/tests/copy_desk_page_claim.mjs +2 -2
- package/tests/copy_desk_page_next.mjs +1 -1
- package/tests/copy_desk_page_release.mjs +1 -1
- package/tests/copy_desk_page_submit.mjs +2 -2
- package/tests/core_258_box_session_claims_db.mjs +1 -1
- package/tests/craft_mode.mjs +48 -34
- package/tests/craft_mode_home.mjs +20 -20
- package/tests/craft_mode_looks.mjs +16 -15
- package/tests/craft_mode_nav.mjs +31 -29
- package/tests/craft_review_mode.mjs +46 -41
- package/tests/crafts_vocabulary.mjs +70 -0
- package/tests/cross_discipline_priority_direction.mjs +5 -5
- package/tests/fitness.mjs +7 -11
- package/tests/funded_project_pays_proof_db.mjs +1 -1
- package/tests/genesis_home.mjs +21 -1
- package/tests/github_chip.mjs +3 -3
- package/tests/github_repo_invite.mjs +379 -0
- package/tests/goal_criterion_confirm_ui.mjs +1 -1
- package/tests/goal_invite_consent.mjs +1 -1
- package/tests/goal_map_page.mjs +2 -2
- package/tests/goal_sort_staleness.mjs +1 -1
- package/tests/goal_spec_task.mjs +1 -1
- package/tests/goal_spec_task_db.mjs +3 -3
- package/tests/grade_engine.mjs +2 -2
- package/tests/grader_no_artifact.mjs +1 -1
- package/tests/grader_prompt_fencing.mjs +1 -1
- package/tests/grader_prompt_window_bound.mjs +1 -1
- package/tests/grader_reader_lens.mjs +2 -2
- package/tests/grader_severity_gate.mjs +1 -1
- package/tests/grader_unbacked_score.mjs +1 -1
- package/tests/hall_approval_queue.mjs +1 -1
- package/tests/hall_board_world.mjs +2 -2
- package/tests/hall_builders_page_pass.mjs +1 -1
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_fullscreen.mjs +27 -3
- package/tests/hall_kit_charts.mjs +1 -1
- package/tests/hall_landing_boot.mjs +1 -1
- package/tests/hall_my_agents.mjs +16 -16
- package/tests/hall_nav.mjs +22 -15
- package/tests/hall_options_menu.mjs +247 -0
- package/tests/hall_oversight_world.mjs +1 -1
- package/tests/hall_palette.mjs +8 -9
- package/tests/hall_profile_world.mjs +3 -3
- package/tests/hall_record_world.mjs +2 -2
- package/tests/hall_settings_world.mjs +2 -1
- package/tests/hall_sitemap.mjs +6 -5
- package/tests/hall_start_screen.mjs +244 -0
- package/tests/hall_street.mjs +2 -2
- package/tests/hall_street_inside.mjs +17 -9
- package/tests/hall_studio_world.mjs +3 -3
- package/tests/hall_visor.mjs +224 -0
- package/tests/help_requests.mjs +47 -15
- package/tests/helpers.mjs +4 -4
- package/tests/home_city.mjs +6 -4
- package/tests/home_city_widget.mjs +33 -14
- package/tests/home_gallery_widget.mjs +4 -4
- package/tests/home_idea_box.mjs +6 -6
- package/tests/hub_community_page.mjs +3 -3
- package/tests/hub_craft_rollup.mjs +75 -36
- package/tests/hub_recent_ships.mjs +3 -2
- package/tests/idea_cascade.mjs +2 -2
- package/tests/idea_credit_streams_db.mjs +1 -1
- package/tests/idea_detail_page.mjs +1 -1
- package/tests/idea_ideate_full.mjs +5 -2
- package/tests/idea_spark_hall.mjs +1 -1
- package/tests/init.mjs +3 -3
- package/tests/instance_manifest.mjs +2 -2
- package/tests/kickoff_checklist.mjs +1 -1
- package/tests/leaderboard_roles.mjs +34 -25
- package/tests/lifecycle_claim_ladder_refs.mjs +55 -0
- package/tests/lightbulb.mjs +14 -10
- package/tests/main_discipline.mjs +8 -8
- package/tests/markdown_line_endings.mjs +1 -1
- package/tests/market_watch_pages.mjs +52 -74
- package/tests/me_streak_leaderboard_queries.mjs +3 -3
- package/tests/migration_allocation.mjs +1 -1
- package/tests/mingle.mjs +36 -21
- package/tests/module-scope-map.mjs +22 -2
- package/tests/module_api.mjs +1 -0
- package/tests/module_contributions.mjs +13 -13
- package/tests/module_manifest.mjs +29 -0
- package/tests/module_skills_hook.mjs +2 -2
- package/tests/modules.mjs +14 -7
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/needs_rebase.mjs +1 -1
- package/tests/newcomer_restock.mjs +13 -13
- package/tests/page_notes_panel.mjs +22 -22
- package/tests/page_tweak_no_block.mjs +1 -1
- package/tests/paintbrush.mjs +7 -7
- package/tests/privacy_matrix.mjs +1 -0
- package/tests/profile_activity.mjs +20 -23
- package/tests/profile_earnings.mjs +1 -1
- package/tests/profile_nudge_prefs.mjs +4 -4
- package/tests/profile_role_headline.mjs +73 -46
- package/tests/profile_role_stats.mjs +55 -45
- package/tests/profile_rollup_consent.mjs +19 -6
- package/tests/profile_route.mjs +12 -12
- package/tests/profile_ui_cross_project.mjs +4 -4
- package/tests/project_invite_suggestions.mjs +2 -2
- package/tests/promote_goal_id.mjs +1 -1
- package/tests/publish_manifest.mjs +1 -1
- package/tests/rank_floor_wiring.mjs +1 -1
- package/tests/regrade_actual_minutes.mjs +1 -1
- package/tests/removal_proposal.mjs +2 -2
- package/tests/reward_gate.mjs +4 -4
- package/tests/reward_mode_cost_plus_only.mjs +1 -1
- package/tests/role_responsibilities.mjs +18 -12
- package/tests/role_stats_db.mjs +17 -16
- package/tests/scouting_rollup_fanout.mjs +5 -5
- package/tests/seed_tasks.mjs +5 -2
- package/tests/session_earnings_mirror_db.mjs +1 -1
- package/tests/session_reward_on_land_db.mjs +1 -1
- package/tests/session_reward_reconcile_db.mjs +2 -2
- package/tests/sessions_kit_pager.mjs +1 -1
- package/tests/settings_art_key_absent.mjs +2 -2
- package/tests/settings_main_role.mjs +21 -20
- package/tests/setup_flow.mjs +3 -3
- package/tests/setup_smoke.mjs +20 -0
- package/tests/ship_card.mjs +6 -6
- package/tests/specialities.mjs +16 -4
- package/tests/speciality_db_shape.mjs +6 -6
- package/tests/speciality_injection.mjs +17 -17
- package/tests/speciality_offer_ui.mjs +5 -5
- package/tests/speciality_offered.mjs +6 -5
- package/tests/speciality_session_skills.mjs +7 -7
- package/tests/speciality_skills.mjs +11 -11
- package/tests/speciality_walkthrough_ui.mjs +2 -2
- package/tests/sso_checkin_identity_spoof.mjs +2 -1
- package/tests/task_classifier_vocab.mjs +10 -0
- package/tests/task_cli.mjs +11 -3
- package/tests/task_criteria_unlink_db.mjs +2 -2
- package/tests/task_discipline_required.mjs +38 -16
- package/tests/task_goal_required.mjs +4 -4
- package/tests/task_needs_db.mjs +1 -1
- package/tests/tweak_replaces_artist_review.mjs +1 -1
- package/tests/update_pill.mjs +153 -2
- package/tests/upkeep_home.mjs +1 -1
- package/tests/v3_c1_options_proof.mjs +145 -0
- package/tests/visual_review.mjs +3 -3
- package/tests/watch_applications_queue.mjs +8 -5
- package/tests/watch_sealed_floor.mjs +3 -1
- package/tests/welcome_boot.mjs +12 -9
- package/tests/welcome_boot_browser.mjs +13 -13
- package/tests/worktree_helper.mjs +16 -1
package/scripts/bongos/setup.js
CHANGED
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
// Usage:
|
|
5
5
|
// node scripts/bongos/setup.js run interactively, perform the auth
|
|
6
6
|
// node scripts/bongos/setup.js --force re-auth even if a valid session exists
|
|
7
|
-
// node scripts/bongos/setup.js --skip-keys
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// node scripts/bongos/setup.js --dry-run print every step that *would* run, without
|
|
7
|
+
// node scripts/bongos/setup.js --skip-keys accepted and ignored: setup asks for no
|
|
8
|
+
// API key any more (task 1005189)
|
|
9
|
+
// node scripts/bongos/setup.js --dry-run print every step that *would* run, without
|
|
11
10
|
// contacting the API, writing files, or
|
|
12
11
|
// prompting. Useful for newcomers who want
|
|
13
12
|
// a preview before running it for real.
|
|
@@ -21,24 +20,18 @@
|
|
|
21
20
|
const readline = require('readline');
|
|
22
21
|
const { cliClient, errCode, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE, hallBase, surfaceAction, isTerminalSurface } = require('./cli-lib');
|
|
23
22
|
|
|
24
|
-
// The instance's status dashboard, from branding — never a hardcoded host, so a
|
|
25
|
-
// non-OTB instance points its builders at ITS OWN dashboard (task 1002428).
|
|
26
|
-
// Falls back to the resolved API base when branding is unreadable.
|
|
27
|
-
function statusOrigin() {
|
|
28
|
-
try {
|
|
29
|
-
const d = require('../../src/branding').branding().domains || {};
|
|
30
|
-
if (d.statusOrigin) return d.statusOrigin;
|
|
31
|
-
} catch (_) { /* fall through */ }
|
|
32
|
-
return API_BASE;
|
|
33
|
-
}
|
|
34
23
|
const onboardingConfig = require('./onboarding-config');
|
|
35
24
|
const { sleep } = require('../../src/sleep'); // the one shared sleep (task 1004893)
|
|
36
25
|
|
|
37
26
|
const DRY_RUN = process.argv.includes('--dry-run');
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
27
|
+
// Setup asks for NO API key (task 1005189). It used to stop every new builder,
|
|
28
|
+
// on every project, at a Google AI Studio key question — including projects
|
|
29
|
+
// with no art pipeline, and including agent-run setups where nobody can type
|
|
30
|
+
// an answer. An optional key is noise at sign-in: a builder adds it in hall
|
|
31
|
+
// Settings (#art-key) when art actually needs it, and the art pipeline's own
|
|
32
|
+
// "no key found" message is the one place that says so. The server-stored key
|
|
33
|
+
// still reaches this machine through fetch-art-key.js. --skip-keys is still
|
|
34
|
+
// accepted (setup-builder passes it) and does nothing.
|
|
42
35
|
|
|
43
36
|
// Tiny helper so the dry-run output reads as a clear plan rather than a wall
|
|
44
37
|
// of identical-looking log lines.
|
|
@@ -46,77 +39,6 @@ function dryRunStep(msg) {
|
|
|
46
39
|
console.log(`[dry-run] ${msg}`);
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
// Ask the new builder for their own Google AI Studio key — used by the
|
|
50
|
-
// pixel-art pipeline (gen_api.py). V3.R11 (#91): each builder pays their own
|
|
51
|
-
// Gemini bill, so spend is attributable per-builder on the public dashboard.
|
|
52
|
-
//
|
|
53
|
-
// Stored as gemini_api_key in ~/.config/otb/bongos-session.json (mode 600, same
|
|
54
|
-
// file as the GitHub session token). gen_api.py reads this location before
|
|
55
|
-
// falling back to ~/.config/otb/env or .env.local.
|
|
56
|
-
//
|
|
57
|
-
// Behavior:
|
|
58
|
-
// - Non-TTY shells: skip silently. Builder can edit the session file later.
|
|
59
|
-
// - --skip-keys flag: skip silently. CI/batch onboarding.
|
|
60
|
-
// - Existing key on file: show last-4, ask "overwrite? [y/N]". Default no.
|
|
61
|
-
// - Empty input: leave whatever was there. Don't blank a working key by
|
|
62
|
-
// accident.
|
|
63
|
-
async function promptGeminiKey() {
|
|
64
|
-
if (SKIP_KEYS) return;
|
|
65
|
-
if (!process.stdin.isTTY) return;
|
|
66
|
-
|
|
67
|
-
const fs = require('node:fs/promises');
|
|
68
|
-
let session;
|
|
69
|
-
try {
|
|
70
|
-
session = JSON.parse(await fs.readFile(SESSION_PATH, 'utf8'));
|
|
71
|
-
} catch (_) {
|
|
72
|
-
// saveSession just wrote it; if it's not readable something's badly wrong.
|
|
73
|
-
// Better to bail than to silently lose a key the builder is about to type.
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const existing = session.gemini_api_key;
|
|
78
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
79
|
-
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
80
|
-
try {
|
|
81
|
-
console.log('');
|
|
82
|
-
console.log('Google AI Studio key for the pixel-art pipeline (per-builder, optional).');
|
|
83
|
-
console.log(' Get one at https://aistudio.google.com/app/apikey');
|
|
84
|
-
console.log(' (Or skip this and add it later with browser clicks: hall Settings →');
|
|
85
|
-
console.log(` ${hallBase()}/settings#art-key — stored encrypted, ADR 0073.)`);
|
|
86
|
-
console.log(' Each builder pays their own Gemini bill so spend is attributed to them');
|
|
87
|
-
console.log(` on ${statusOrigin()}.`);
|
|
88
|
-
let prompt;
|
|
89
|
-
if (existing && typeof existing === 'string' && existing.length >= 4) {
|
|
90
|
-
const last4 = existing.slice(-4);
|
|
91
|
-
const overwrite = (await ask(` Key on file ending …${last4}. Overwrite? [y/N] `)).trim().toLowerCase();
|
|
92
|
-
if (!(overwrite === 'y' || overwrite === 'yes')) {
|
|
93
|
-
console.log(' Kept the existing key.');
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
prompt = ' New GOOGLE_AI_STUDIO_KEY (press Enter to clear): ';
|
|
97
|
-
} else {
|
|
98
|
-
prompt = ' GOOGLE_AI_STUDIO_KEY (press Enter to skip): ';
|
|
99
|
-
}
|
|
100
|
-
const input = (await ask(prompt)).trim();
|
|
101
|
-
if (!input) {
|
|
102
|
-
if (existing) {
|
|
103
|
-
// The user asked to overwrite, then entered nothing → clear it.
|
|
104
|
-
delete session.gemini_api_key;
|
|
105
|
-
await fs.writeFile(SESSION_PATH, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
106
|
-
console.log(' Cleared the key.');
|
|
107
|
-
} else {
|
|
108
|
-
console.log(' Skipped — set it later by re-running setup.js or editing the session file.');
|
|
109
|
-
}
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
session.gemini_api_key = input;
|
|
113
|
-
await fs.writeFile(SESSION_PATH, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
114
|
-
console.log(` Saved Gemini key to ${SESSION_PATH} (mode 600).`);
|
|
115
|
-
} finally {
|
|
116
|
-
rl.close();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
42
|
// V3.R10 (#235), criterion C3 — link the builder's Anthropic account.
|
|
121
43
|
//
|
|
122
44
|
// Claude Code is the trusted agent on the builder's machine: it writes its own
|
|
@@ -213,15 +135,15 @@ async function promptDisciplines() {
|
|
|
213
135
|
try {
|
|
214
136
|
console.log('');
|
|
215
137
|
console.log('Which kind of work do you want surfaced first?');
|
|
216
|
-
console.log(' 1)
|
|
217
|
-
console.log(' 2) artist
|
|
218
|
-
console.log(' 3)
|
|
219
|
-
console.log(' 4)
|
|
220
|
-
|
|
221
|
-
|
|
138
|
+
console.log(' 1) artisan — code, gameplay mechanics, infra, Bongos itself, perf, bugs');
|
|
139
|
+
console.log(' 2) artist — how the project looks and reads, and its ideas: art, copy, scoping, lore');
|
|
140
|
+
console.log(' 3) both of them');
|
|
141
|
+
console.log(' 4) skip for now (you can set this later on /builders)');
|
|
142
|
+
const choice = (await ask('Choose [1-4]: ')).trim();
|
|
143
|
+
// Two task crafts since task 1005141 (the ideator folded into the artist).
|
|
222
144
|
const map = {
|
|
223
|
-
1: ['
|
|
224
|
-
|
|
145
|
+
1: ['artisan'], 2: ['artist'],
|
|
146
|
+
3: ['artisan', 'artist'], 4: null,
|
|
225
147
|
};
|
|
226
148
|
const disciplines = map[choice];
|
|
227
149
|
if (disciplines === undefined) {
|
|
@@ -327,12 +249,9 @@ async function finishSetup(builder) {
|
|
|
327
249
|
} catch (e) {
|
|
328
250
|
console.log(`Note: could not activate git hooks (${e.message}); run: git config core.hooksPath .husky`);
|
|
329
251
|
}
|
|
330
|
-
await promptGeminiKey();
|
|
331
252
|
// #1281: deliver the GDS shared art key to entitled newcomers right now, so
|
|
332
253
|
// a brand-new Xenos's first art task works without waiting for the next
|
|
333
|
-
// session start. Best-effort, bounded — never block setup.
|
|
334
|
-
// make the promptGeminiKey copy above rank-aware — "we cover your first art
|
|
335
|
-
// tasks; add your own key when you reach Thetes".)
|
|
254
|
+
// session start. Best-effort, bounded, silent — never block setup.
|
|
336
255
|
try {
|
|
337
256
|
const { spawnSync } = require('node:child_process');
|
|
338
257
|
const path = require('node:path');
|
|
@@ -405,11 +324,6 @@ async function main() {
|
|
|
405
324
|
dryRunStep('If GitHub refuses with device_flow_disabled (a one-click App whose checkbox is unticked, task 1003051): would print the web sign-in guidance (hall Settings → CLI Access) and prompt for a pasted CLI token instead of dead-ending.');
|
|
406
325
|
dryRunStep('Would print the verification_uri and user_code, then poll /api/bongos/auth/device/poll every few seconds until the user approves or the code expires.');
|
|
407
326
|
dryRunStep(`On approval, would write the bearer token + builder profile to ${SESSION_PATH} (chmod 600).`);
|
|
408
|
-
if (SKIP_KEYS) {
|
|
409
|
-
dryRunStep('Would skip the Gemini-key prompt (--skip-keys set).');
|
|
410
|
-
} else {
|
|
411
|
-
dryRunStep(`Would prompt for a per-builder GOOGLE_AI_STUDIO_KEY on TTY shells and store it as gemini_api_key in ${SESSION_PATH} (mode 600). Used by modules/art-pipeline/pipeline/gen_api.py for per-builder cost attribution.`);
|
|
412
|
-
}
|
|
413
327
|
dryRunStep('Would read ~/.claude.json oauthAccount and, on a TTY, prompt to confirm the Anthropic email; on a match would POST /api/bongos/me/anthropic-link and store anthropic_user_id in the session (kept as cosmetic attribution; the identity pin was removed — task 1039 / idea 258). On mismatch would REFUSE to link.');
|
|
414
328
|
dryRunStep('Would prompt for preferred disciplines (creative / engineering / both / skip) and PATCH /api/bongos/me/disciplines on TTY shells.');
|
|
415
329
|
dryRunStep(`Would prompt for onboarding-friction consent (Y/n) and record the decision to ${onboardingConfig.CONSENT_PATH} on TTY shells.`);
|
|
@@ -531,7 +445,6 @@ async function main() {
|
|
|
531
445
|
const { activate } = require('./install-git-hooks');
|
|
532
446
|
activate();
|
|
533
447
|
} catch (_) { /* hook activation is best-effort */ }
|
|
534
|
-
await promptGeminiKey();
|
|
535
448
|
await promptAnthropicLink();
|
|
536
449
|
await promptDisciplines();
|
|
537
450
|
await promptOnboardingConsent();
|
package/scripts/bongos/start.js
CHANGED
|
@@ -220,9 +220,9 @@ function roleLabel(t) {
|
|
|
220
220
|
// that read in both light and dark. Colours the Type column so the work lane
|
|
221
221
|
// reads at a glance (task 1273 follow-up: "spice up the view").
|
|
222
222
|
function disciplineColor(d) {
|
|
223
|
-
if (d === '
|
|
223
|
+
if (d === 'artisan') return '#1D9E75'; // sea-teal
|
|
224
224
|
if (d === 'artist') return '#D85A30'; // terracotta
|
|
225
|
-
if (d === '
|
|
225
|
+
if (d === 'artist') return '#639922'; // olive
|
|
226
226
|
return '#888780'; // unclassified — neutral gray
|
|
227
227
|
}
|
|
228
228
|
function whatItIs(t) {
|
package/scripts/bongos/task.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// the first-class front door.
|
|
8
8
|
//
|
|
9
9
|
// Usage:
|
|
10
|
-
// bongos task new --title "…" --description "…" [--kind feature] [--discipline
|
|
10
|
+
// bongos task new --title "…" --description "…" [--kind feature] [--discipline artisan]
|
|
11
11
|
// [--priority 3] [--version <id>] [--goal <id>] [--criteria C1,C2]
|
|
12
12
|
// [--touches a/,b/] [--est-minutes N] [--credits N] [--ready] [--json]
|
|
13
13
|
// bongos task show <id> [--json]
|
|
@@ -29,7 +29,7 @@ const { UPKEEP_KIND } = require('../../modules/lifecycle/upkeep-home');
|
|
|
29
29
|
// Valid enums (mirrors the server's task-classifier vocab; a friendly client-side check
|
|
30
30
|
// so an obvious typo fails before the round-trip — the server stays authoritative).
|
|
31
31
|
const VALID_KINDS = ['feature', 'bug', 'infra', 'refactor', 'spike', 'learning-capture', 'cleanup', 'decision', 'blocker-resolution', 'verify'];
|
|
32
|
-
const VALID_DISCIPLINES = ['
|
|
32
|
+
const VALID_DISCIPLINES = ['artisan', 'artist', 'ui'];
|
|
33
33
|
|
|
34
34
|
function csv(v) {
|
|
35
35
|
return String(v || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
@@ -46,7 +46,8 @@ function buildTaskBody(args) {
|
|
|
46
46
|
|
|
47
47
|
const kind = arg('--kind', args) || 'feature';
|
|
48
48
|
if (!VALID_KINDS.includes(kind)) errors.push(`--kind must be one of: ${VALID_KINDS.join(', ')}`);
|
|
49
|
-
|
|
49
|
+
// Old spellings still work (task 1005141): engineer → artisan, ideator → artist.
|
|
50
|
+
const discipline = ({ engineer: 'artisan', ideator: 'artist' })[arg('--discipline', args)] || arg('--discipline', args) || 'artisan';
|
|
50
51
|
if (!VALID_DISCIPLINES.includes(discipline)) errors.push(`--discipline must be one of: ${VALID_DISCIPLINES.join(', ')}`);
|
|
51
52
|
|
|
52
53
|
const num = (flag) => { const v = arg(flag, args); return v == null ? undefined : Number(v); };
|
|
@@ -82,7 +83,7 @@ function buildTaskBody(args) {
|
|
|
82
83
|
|
|
83
84
|
function usage() {
|
|
84
85
|
console.error('usage:');
|
|
85
|
-
console.error(' bongos task new --title "…" --description "…" [--kind feature] [--discipline
|
|
86
|
+
console.error(' bongos task new --title "…" --description "…" [--kind feature] [--discipline artisan]');
|
|
86
87
|
console.error(' [--priority 3] [--version <id>] [--goal <id>] [--criteria C1,C2]');
|
|
87
88
|
console.error(' [--touches a/,b/] [--est-minutes N] [--credits N] [--ready] [--json]');
|
|
88
89
|
console.error(' bongos task show <id> [--json]');
|
package/scripts/bongos/triage.js
CHANGED
|
@@ -282,7 +282,7 @@ async function createTask({ versionId, title, description, priority, sourceRef,
|
|
|
282
282
|
// promotion they just decided on. So resolve it here with the SAME function the
|
|
283
283
|
// server uses, and name a fallback rather than letting the loop break — the
|
|
284
284
|
// identical trade modules/ideas/inbox.js makes for capture.
|
|
285
|
-
discipline: resolveWriteDiscipline({ title, description }).discipline || '
|
|
285
|
+
discipline: resolveWriteDiscipline({ title, description }).discipline || 'artisan',
|
|
286
286
|
};
|
|
287
287
|
if (goalId == null) {
|
|
288
288
|
throw new Error('createTask: goal_id is required — every task belongs to a goal (task 1003598 deleted the catch-all fallback).');
|
|
@@ -291,9 +291,21 @@ function cmdAdd(root, argv) {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
console.log('');
|
|
294
|
-
console.log(
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
for (const line of nextStepLines(wtPath)) console.log(line);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// task 1005185 - feedback bug scripts: the "Next:" hint names the claim command that
|
|
298
|
+
// exists in the new tree. A project that installs the core from npm has no scripts/ of
|
|
299
|
+
// its own — scripts is a git-ignored link into node_modules/@bongos/core, so a fresh
|
|
300
|
+
// worktree has none and `node scripts/...` dies MODULE_NOT_FOUND. There the CLI is
|
|
301
|
+
// `npx bongos <verb>` (npx walks up to the main checkout's node_modules). Same check
|
|
302
|
+
// claim.js's finishHintLines uses (task 1005113).
|
|
303
|
+
function nextStepLines(wtPath, { installed } = {}) {
|
|
304
|
+
if (installed === undefined) {
|
|
305
|
+
try { installed = require('./fitness-lib').isInstalledCore(); } catch { installed = false; }
|
|
306
|
+
}
|
|
307
|
+
const claim = installed ? 'npx bongos claim <task-id>' : 'node scripts/bongos/claim.js <task-id>';
|
|
308
|
+
return ['Next:', ` cd "${wtPath}"`, ` ${claim} (worktree auto-detected)`];
|
|
297
309
|
}
|
|
298
310
|
|
|
299
311
|
function cmdRemove(root, argv) {
|
|
@@ -445,5 +457,5 @@ if (require.main === module) main();
|
|
|
445
457
|
|
|
446
458
|
module.exports = {
|
|
447
459
|
worktreeNameFor, needsJunction, removalPlan, nodeModulesState, shadowingNodeModules, huskDisposition,
|
|
448
|
-
unlinkNodeModulesLink,
|
|
460
|
+
unlinkNodeModulesLink, nextStepLines,
|
|
449
461
|
};
|
package/scripts/setup-builder.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// bootstrap a new builder runs right after cloning:
|
|
8
8
|
//
|
|
9
9
|
// node scripts/setup-builder.js
|
|
10
|
-
// node scripts/setup-builder.js --skip-keys #
|
|
10
|
+
// node scripts/setup-builder.js --skip-keys # accepted and ignored (no key is asked for)
|
|
11
11
|
// node scripts/setup-builder.js --no-auth # skip the GDS Device Flow step
|
|
12
12
|
// node scripts/setup-builder.js --help
|
|
13
13
|
//
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
// "already done" rather than clobbering. What it does, in order:
|
|
16
16
|
// 1. Verify Node >= 22 and Python >= 3.10 (the two required toolchains).
|
|
17
17
|
// 2. `npm ci` to install JS dependencies.
|
|
18
|
-
// 3. Copy .env.local.example -> .env.local (if absent)
|
|
19
|
-
// for
|
|
18
|
+
// 3. Copy .env.local.example -> .env.local (if absent). No API key is asked
|
|
19
|
+
// for (task 1005189): a builder adds one in hall Settings when art needs it.
|
|
20
20
|
// 4. `node scripts/bongos/setup.js` — GitHub Device Flow auth (--no-auth skips).
|
|
21
21
|
// 5. Print the "ready" banner pointing at /builder-start.
|
|
22
22
|
//
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
|
|
26
26
|
const fs = require('node:fs');
|
|
27
27
|
const path = require('node:path');
|
|
28
|
-
const readline = require('node:readline');
|
|
29
28
|
const { spawnSync } = require('node:child_process');
|
|
30
29
|
|
|
31
30
|
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
@@ -33,17 +32,15 @@ const IS_WIN = process.platform === 'win32';
|
|
|
33
32
|
|
|
34
33
|
// ---- arg parsing ----
|
|
35
34
|
const argv = process.argv.slice(2);
|
|
36
|
-
let SKIP_KEYS = false;
|
|
37
35
|
let NO_AUTH = false;
|
|
38
36
|
for (const a of argv) {
|
|
39
|
-
if (a === '--skip-keys')
|
|
37
|
+
if (a === '--skip-keys') { /* accepted for old callers; setup asks for no key (task 1005189) */ }
|
|
40
38
|
else if (a === '--no-auth') NO_AUTH = true;
|
|
41
39
|
else if (a === '-h' || a === '--help') {
|
|
42
40
|
console.log(`setup-builder.js — one-command bootstrap for a new OTB builder.
|
|
43
41
|
|
|
44
42
|
Usage:
|
|
45
43
|
node scripts/setup-builder.js
|
|
46
|
-
node scripts/setup-builder.js --skip-keys # don't prompt for API keys
|
|
47
44
|
node scripts/setup-builder.js --no-auth # skip the GDS Device Flow step
|
|
48
45
|
node scripts/setup-builder.js --help
|
|
49
46
|
|
|
@@ -162,13 +159,6 @@ function npmCi() {
|
|
|
162
159
|
// ---------------------------------------------------------------------------
|
|
163
160
|
// 3. .env.local
|
|
164
161
|
// ---------------------------------------------------------------------------
|
|
165
|
-
function ask(question) {
|
|
166
|
-
return new Promise((resolve) => {
|
|
167
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
168
|
-
rl.question(question, (answer) => { rl.close(); resolve(answer); });
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
162
|
async function envLocal() {
|
|
173
163
|
step('3/5 Local environment (.env.local)');
|
|
174
164
|
const example = path.join(REPO_ROOT, '.env.local.example');
|
|
@@ -186,37 +176,8 @@ async function envLocal() {
|
|
|
186
176
|
fs.copyFileSync(example, target);
|
|
187
177
|
ok('Created .env.local from .env.local.example');
|
|
188
178
|
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
// ~/.config/otb/env, so it just means art-pipeline calls raise the standard
|
|
192
|
-
// "GOOGLE_AI_STUDIO_KEY not found" error until set.
|
|
193
|
-
if (SKIP_KEYS) {
|
|
194
|
-
skip('--skip-keys set; leaving GOOGLE_AI_STUDIO_KEY blank in .env.local');
|
|
195
|
-
skip('you can edit .env.local later, or set it in ~/.config/otb/env');
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (!process.stdin.isTTY) {
|
|
199
|
-
skip('no TTY; leaving GOOGLE_AI_STUDIO_KEY blank in .env.local');
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
console.log('');
|
|
203
|
-
console.log(` ${C.dim}Google AI Studio key for the pixel-art pipeline.`);
|
|
204
|
-
console.log(' Get one at https://aistudio.google.com/app/apikey .');
|
|
205
|
-
console.log(` Press Enter to skip — you can always edit .env.local later.${C.reset}`);
|
|
206
|
-
// Strip CR/LF so a pasted value with a stray newline can't inject extra
|
|
207
|
-
// lines into the builder's .env.local; trim handles surrounding whitespace.
|
|
208
|
-
const input = (await ask(' GOOGLE_AI_STUDIO_KEY: ')).replace(/[\r\n]+/g, '').trim();
|
|
209
|
-
if (input) {
|
|
210
|
-
const contents = fs.readFileSync(target, 'utf8');
|
|
211
|
-
// Function replacement (not a string): String.replace treats $&, $1, $$ etc.
|
|
212
|
-
// in a replacement STRING as special, which would silently corrupt a key
|
|
213
|
-
// containing a bare `$`. A function return value is spliced verbatim.
|
|
214
|
-
const next = contents.replace(/^GOOGLE_AI_STUDIO_KEY=.*$/m, () => `GOOGLE_AI_STUDIO_KEY=${input}`);
|
|
215
|
-
fs.writeFileSync(target, next);
|
|
216
|
-
ok('Saved GOOGLE_AI_STUDIO_KEY to .env.local');
|
|
217
|
-
} else {
|
|
218
|
-
skip('left GOOGLE_AI_STUDIO_KEY blank — edit .env.local when you have one');
|
|
219
|
-
}
|
|
179
|
+
// No key is asked for here (task 1005189): an optional key at sign-in is noise.
|
|
180
|
+
// The art pipeline says so itself, when art actually needs one.
|
|
220
181
|
}
|
|
221
182
|
|
|
222
183
|
// ---------------------------------------------------------------------------
|
|
@@ -234,7 +195,6 @@ function gdsAuth() {
|
|
|
234
195
|
console.log(' Handing off to scripts/bongos/setup.js ...');
|
|
235
196
|
console.log('');
|
|
236
197
|
const setupArgs = [path.join(REPO_ROOT, 'scripts', 'bongos', 'setup.js')];
|
|
237
|
-
if (SKIP_KEYS) setupArgs.push('--skip-keys');
|
|
238
198
|
const status = run(process.execPath, setupArgs); // shell defaults off
|
|
239
199
|
if (status === 0) {
|
|
240
200
|
ok('GDS session ready');
|
|
@@ -149,7 +149,7 @@ async function recentShipsForRollup(builderId) {
|
|
|
149
149
|
// preference, exactly as before. A port that cannot be asked leaves its numbers
|
|
150
150
|
// null ("could not ask"), never 0. Never throws: a failed read drops its craft's
|
|
151
151
|
// numbers, not the report.
|
|
152
|
-
const ROLLUP_CRAFTS = Object.freeze(['
|
|
152
|
+
const ROLLUP_CRAFTS = Object.freeze(['artisan', 'artist']);
|
|
153
153
|
const countOrNull = (v) => (Number.isInteger(v) && v >= 0 ? v : null);
|
|
154
154
|
async function readCraftRollup(builderId, { shipped = 0, preferred = [] } = {}) {
|
|
155
155
|
const fallback = { counts: null, disciplines: Array.isArray(preferred) ? preferred : [] };
|
|
@@ -172,20 +172,20 @@ async function readCraftRollup(builderId, { shipped = 0, preferred = [] } = {})
|
|
|
172
172
|
};
|
|
173
173
|
const [artist, ideator] = await Promise.all([
|
|
174
174
|
earned.includes('artist') ? ask('copy-desk.artistStats') : null,
|
|
175
|
-
earned.includes('
|
|
175
|
+
earned.includes('artist') ? ask('ideas.ideatorStats') : null,
|
|
176
176
|
]);
|
|
177
177
|
const credits = (c) => countOrNull(Math.round(Number(byCraft[c])));
|
|
178
178
|
const counts = {};
|
|
179
|
-
if (earned.includes('
|
|
179
|
+
if (earned.includes('artisan')) counts.artisan = { shipped: countOrNull(shipped), credits: credits('artisan') };
|
|
180
|
+
// One artist block since the ideator folded in (task 1005141): the page count
|
|
181
|
+
// and the idea counts side by side, under the craft's one credit split.
|
|
180
182
|
if (earned.includes('artist')) {
|
|
181
|
-
counts.artist = {
|
|
182
|
-
|
|
183
|
-
if (earned.includes('ideator')) {
|
|
184
|
-
counts.ideator = {
|
|
183
|
+
counts.artist = {
|
|
184
|
+
pages_approved: countOrNull(artist && artist.pages_approved),
|
|
185
185
|
filed: countOrNull(ideator && ideator.filed),
|
|
186
186
|
full_ideas: countOrNull(ideator && ideator.full_ideas),
|
|
187
187
|
ratified: countOrNull(ideator && ideator.ratified),
|
|
188
|
-
credits: credits('
|
|
188
|
+
credits: credits('artist'),
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
return { counts, disciplines: earned };
|
package/src/bongos/craft-mode.js
CHANGED
|
@@ -77,6 +77,8 @@ function roleLine(mode) {
|
|
|
77
77
|
// so this adds no prompt-injection surface to the session.
|
|
78
78
|
function describeCraftMode(discipline, modes) {
|
|
79
79
|
if (!discipline || !modes) return null;
|
|
80
|
+
// A row a rolled-back release wrote may still say engineer/ideator (task 1005141).
|
|
81
|
+
discipline = require('../crafts').normalizeCraft(discipline);
|
|
80
82
|
const mode = modes[discipline];
|
|
81
83
|
if (!mode || typeof mode !== 'object' || !mode.directive) return null;
|
|
82
84
|
const parts = [`${roleLine(mode)} (the craft of the task claimed in this worktree).`, String(mode.directive).trim()];
|
package/src/bongos/db.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
// grade-count SQL runs on both sides of the kernel↔module line — keep in sync.
|
|
26
26
|
|
|
27
27
|
const { pool } = require('./pool');
|
|
28
|
+
const { normalizeCraft: normalizeCraftName } = require('../crafts'); // task 1005147: setBuilderCharacter
|
|
28
29
|
const secretBox = require('./secret-box');
|
|
29
30
|
// Module seam (ADR 0083). The builder-exit path EMITS `builder.secrets-scrub-needed`
|
|
30
31
|
// AFTER COMMIT (best-effort) so a module that stores a builder-keyed secret can
|
|
@@ -172,7 +173,10 @@ const UPDATE_BUILDER_DISCIPLINES_SQL =
|
|
|
172
173
|
async function updateBuilderDisciplines(builderId, disciplines, { mainDiscipline } = {}) {
|
|
173
174
|
const { enabledDisciplines } = require('../modules');
|
|
174
175
|
const offered = enabledDisciplines();
|
|
175
|
-
|
|
176
|
+
// An old spelling (engineer, ideator) is today's craft, not an invalid one (task 1005141).
|
|
177
|
+
const { normalizeCraft, normalizeCrafts } = require('../crafts');
|
|
178
|
+
const list = Array.isArray(disciplines) ? normalizeCrafts(disciplines) : [];
|
|
179
|
+
if (typeof mainDiscipline === 'string') mainDiscipline = normalizeCraft(mainDiscipline);
|
|
176
180
|
const bad = list.filter((d) => !offered.includes(d));
|
|
177
181
|
if (bad.length) {
|
|
178
182
|
const err = new Error(`invalid discipline(s): ${bad.join(', ')}`);
|
|
@@ -207,6 +211,38 @@ async function getBuilderRoleChoice(builderId) {
|
|
|
207
211
|
return rows[0] ?? null;
|
|
208
212
|
}
|
|
209
213
|
|
|
214
|
+
// The character a builder holds (task 1005147, core_297): 'artisan' | 'artist' |
|
|
215
|
+
// 'patron', or null while they have not picked one. Identity and view only — no
|
|
216
|
+
// gate reads it (criterion character-is-identity-not-authority). Its own read, not a
|
|
217
|
+
// column on getBuilderById, so the kernel's hot identity read never depends on a
|
|
218
|
+
// migration that may not have run yet.
|
|
219
|
+
const CHARACTERS = Object.freeze(['artisan', 'artist', 'patron']);
|
|
220
|
+
async function getBuilderCharacter(builderId) {
|
|
221
|
+
const { rows } = await pool.query(
|
|
222
|
+
`SELECT game_character FROM builders WHERE id = $1`,
|
|
223
|
+
[builderId]
|
|
224
|
+
);
|
|
225
|
+
return rows[0] ? rows[0].game_character : null;
|
|
226
|
+
}
|
|
227
|
+
// Set (a CHARACTERS word, old spellings normalized) or clear (null) the builder's own
|
|
228
|
+
// character. Throws { code: 'invalid_character' } for anything else.
|
|
229
|
+
async function setBuilderCharacter(builderId, character) {
|
|
230
|
+
let value = null;
|
|
231
|
+
if (character !== null) {
|
|
232
|
+
value = normalizeCraftName(character);
|
|
233
|
+
if (!CHARACTERS.includes(value)) {
|
|
234
|
+
const err = new Error(`invalid character: ${character}`);
|
|
235
|
+
err.code = 'invalid_character';
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const { rows } = await pool.query(
|
|
240
|
+
`UPDATE builders SET game_character = $2 WHERE id = $1 RETURNING game_character`,
|
|
241
|
+
[builderId, value]
|
|
242
|
+
);
|
|
243
|
+
return rows[0] ? rows[0].game_character : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
210
246
|
// The same two facts for a whole list of builders, in ONE statement (task
|
|
211
247
|
// 1004437): the public leaderboard names every row's roles. Map<idString, row>.
|
|
212
248
|
async function getBuilderRoleChoices(builderIds) {
|
|
@@ -1142,6 +1178,8 @@ module.exports = {
|
|
|
1142
1178
|
UPDATE_BUILDER_DISCIPLINES_SQL,
|
|
1143
1179
|
getBuilderRoleChoice,
|
|
1144
1180
|
getBuilderRoleChoices,
|
|
1181
|
+
getBuilderCharacter,
|
|
1182
|
+
setBuilderCharacter,
|
|
1145
1183
|
RANKS,
|
|
1146
1184
|
setBuilderRank,
|
|
1147
1185
|
setBuilderTester,
|
|
@@ -378,7 +378,85 @@ async function deleteDeployKey(token, { owner, name, keyId, fetchImpl = fetch }
|
|
|
378
378
|
throw new GithubApiError(`GitHub deploy-key delete failed (${res.status})`, { status: res.status, code: 'github_error' });
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
// ---- repo collaborators: inviting a person to the repo itself ----
|
|
382
|
+
// A GitHub invite is a SEPARATE door from a Bongos invite. Bongos admission is a row
|
|
383
|
+
// in the project's own database; this puts the person on the repo's collaborator list
|
|
384
|
+
// so they can see (and, only when asked, push to) the code on their own GitHub.
|
|
385
|
+
// ONE permission is offered: 'pull' (read). A builder ships through the server's own
|
|
386
|
+
// publish and needs no write access, so Bongos never grants it (owner decision,
|
|
387
|
+
// 2026-11-04): 'push', 'admin', 'maintain' and 'triage' are all refused. An owner who
|
|
388
|
+
// wants someone to have write access gives it on GitHub directly.
|
|
389
|
+
const REPO_INVITE_PERMISSIONS = Object.freeze(['pull']);
|
|
390
|
+
|
|
391
|
+
function repoPath(owner, name) {
|
|
392
|
+
return `${GH_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// PUT /collaborators/{login}. 201 = a pending invitation was created, 204 = they
|
|
396
|
+
// already have access. Returns { state: 'invited' | 'already_collaborator', ... }.
|
|
397
|
+
async function inviteRepoCollaborator(token, { owner, name, login, permission = 'pull', fetchImpl = fetch } = {}) {
|
|
398
|
+
if (!REPO_INVITE_PERMISSIONS.includes(permission)) {
|
|
399
|
+
throw new GithubApiError('Only read-only access can be offered', { status: 400, code: 'bad_permission' });
|
|
400
|
+
}
|
|
401
|
+
const res = await fetchImpl(`${repoPath(owner, name)}/collaborators/${encodeURIComponent(login)}`, {
|
|
402
|
+
method: 'PUT',
|
|
403
|
+
headers: ghHeaders(token, { 'Content-Type': 'application/json' }),
|
|
404
|
+
body: JSON.stringify({ permission }),
|
|
405
|
+
});
|
|
406
|
+
if (res.status === 201) {
|
|
407
|
+
const inv = await res.json().catch(() => ({}));
|
|
408
|
+
return { state: 'invited', invitation_id: inv.id || null, permission };
|
|
409
|
+
}
|
|
410
|
+
if (res.status === 204) return { state: 'already_collaborator', permission };
|
|
411
|
+
if (res.status === 401) {
|
|
412
|
+
throw new GithubApiError('GitHub rejected the stored token', { status: 401, code: 'github_unauthorized' });
|
|
413
|
+
}
|
|
414
|
+
if (res.status === 403) {
|
|
415
|
+
throw new GithubApiError('GitHub refused — adding people needs repository admin', { status: 403, code: 'not_repo_admin' });
|
|
416
|
+
}
|
|
417
|
+
if (res.status === 404) {
|
|
418
|
+
throw new GithubApiError('GitHub answered 404 for that repository or user', { status: 404, code: 'repo_not_found' });
|
|
419
|
+
}
|
|
420
|
+
if (res.status === 422) {
|
|
421
|
+
throw new GithubApiError('GitHub would not invite that account', { status: 422, code: 'cannot_invite' });
|
|
422
|
+
}
|
|
423
|
+
throw new GithubApiError(`GitHub collaborator invite failed (${res.status})`, { status: res.status, code: 'github_error' });
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Where one person stands on the repo: 'collaborator' (accepted), 'pending' (the
|
|
427
|
+
// invitation is out and not yet accepted), or 'none' (never invited, declined or
|
|
428
|
+
// expired). Two reads: the collaborator check, then the open invitations.
|
|
429
|
+
async function repoInviteState(token, { owner, name, login, fetchImpl = fetch } = {}) {
|
|
430
|
+
const who = encodeURIComponent(login);
|
|
431
|
+
const c = await fetchImpl(`${repoPath(owner, name)}/collaborators/${who}`, { headers: ghHeaders(token) });
|
|
432
|
+
if (c.status === 204) return { state: 'collaborator' };
|
|
433
|
+
if (c.status === 401) {
|
|
434
|
+
throw new GithubApiError('GitHub rejected the stored token', { status: 401, code: 'github_unauthorized' });
|
|
435
|
+
}
|
|
436
|
+
if (c.status === 403) {
|
|
437
|
+
throw new GithubApiError('GitHub refused — reading collaborators needs repository admin', { status: 403, code: 'not_repo_admin' });
|
|
438
|
+
}
|
|
439
|
+
if (c.status !== 404) {
|
|
440
|
+
throw new GithubApiError(`GitHub collaborator read failed (${c.status})`, { status: c.status, code: 'github_error' });
|
|
441
|
+
}
|
|
442
|
+
const i = await fetchImpl(`${repoPath(owner, name)}/invitations?per_page=${PER_PAGE}`, { headers: ghHeaders(token) });
|
|
443
|
+
if (i.status === 404) {
|
|
444
|
+
throw new GithubApiError('GitHub answered 404 for that repository', { status: 404, code: 'repo_not_found' });
|
|
445
|
+
}
|
|
446
|
+
if (!i.ok) {
|
|
447
|
+
throw new GithubApiError(`GitHub invitations read failed (${i.status})`, { status: i.status, code: 'github_error' });
|
|
448
|
+
}
|
|
449
|
+
const rows = await i.json();
|
|
450
|
+
const mine = (Array.isArray(rows) ? rows : []).find(
|
|
451
|
+
(r) => r && r.invitee && String(r.invitee.login || '').toLowerCase() === String(login).toLowerCase(),
|
|
452
|
+
);
|
|
453
|
+
return mine
|
|
454
|
+
? { state: 'pending', invitation_id: mine.id, permission: mine.permissions || null }
|
|
455
|
+
: { state: 'none' };
|
|
456
|
+
}
|
|
457
|
+
|
|
381
458
|
module.exports = {
|
|
459
|
+
inviteRepoCollaborator, repoInviteState, REPO_INVITE_PERMISSIONS,
|
|
382
460
|
listUserRepos, createUserRepo, getUserRepo, makeRepoPublic, makeRepoPrivate,
|
|
383
461
|
listUserOrgs, getOrgAccess, createOrgRepo, validGithubLogin,
|
|
384
462
|
listDeployKeys, addDeployKey, deleteDeployKey,
|
|
@@ -366,6 +366,28 @@ const MODULE_GLOBS = {
|
|
|
366
366
|
agents: [
|
|
367
367
|
'modules/agents/', '.claude/agents/', 'scripts/bongos/agents-sync.js',
|
|
368
368
|
],
|
|
369
|
+
// --- the MODULE STORE (ADR 0338, task 1004793): publishing, listing, entitlements,
|
|
370
|
+
// testing channel, promotion, usage reports, payouts and the assessment CLIs.
|
|
371
|
+
// It is core code — it lives in src/bongos/, not modules/ — so it has no
|
|
372
|
+
// module.json and this key is a scope like kernel or devsecops. Before this
|
|
373
|
+
// key NO key resolved these files, so no goal could own a change to the store
|
|
374
|
+
// (the 1002690 / 1002736 precedent). Its HTTP surface, src/bongos/routes/modules.js,
|
|
375
|
+
// stays kernel's with the rest of routes/. module-scope-map.js is kernel's too:
|
|
376
|
+
// the name prefix is shared, the job is not. ---
|
|
377
|
+
'module-store': [
|
|
378
|
+
'src/bongos/module-store.js', 'src/bongos/module-listing.js',
|
|
379
|
+
'src/bongos/module-entitlements.js', 'src/bongos/module-overrides.js',
|
|
380
|
+
'src/bongos/module-payouts.js', 'src/bongos/module-promotion.js',
|
|
381
|
+
'src/bongos/module-rechecks.js', 'src/bongos/module-subcategories.js',
|
|
382
|
+
'src/bongos/module-submissions.js', 'src/bongos/module-testing.js',
|
|
383
|
+
'src/bongos/module-usage-reports.js',
|
|
384
|
+
'scripts/bongos/module-store-cli.js', 'scripts/bongos/module-artifact.js',
|
|
385
|
+
'scripts/bongos/module-assess-docs.js', 'scripts/bongos/module-assess-installs.js',
|
|
386
|
+
'scripts/bongos/module-assess-score.js', 'scripts/bongos/module-assess-security.js',
|
|
387
|
+
'scripts/bongos/module-assess-tests.js', 'scripts/bongos/module-assess-version.js',
|
|
388
|
+
'scripts/bongos/module-error-report.js', 'scripts/bongos/module-remove.js',
|
|
389
|
+
'scripts/bongos/module-test-runner.js',
|
|
390
|
+
],
|
|
369
391
|
// --- the hub identity provider + the PROJECTS CATALOG it owns (ADR 0141): platform
|
|
370
392
|
// accounts, the SSO client registry, project memberships/invites, and the
|
|
371
393
|
// platform_identity_projects rows the Explore feed reads. Carved already, so the
|