@bongos/core 1.21.66 → 1.21.67

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.
Files changed (49) hide show
  1. package/.bongos-core.json +111 -41
  2. package/docs/copy-inventory.md +414 -386
  3. package/docs/copy-registry.json +815 -537
  4. package/docs/file-map.md +1 -0
  5. package/docs/module-api-changelog.md +2 -0
  6. package/docs/page-inventory.json +45 -12
  7. package/docs/page-readings.json +959 -907
  8. package/modules/platform-identity/routes/my-projects.js +1 -1
  9. package/modules/public-landing/public/account.html +1 -1
  10. package/modules/public-landing/public/assets/cosmos.css +12 -10
  11. package/modules/public-landing/public/contact.html +1 -1
  12. package/modules/public-landing/public/guild.html +1 -1
  13. package/modules/public-landing/public/index-cli.states.json +20 -0
  14. package/modules/public-landing/public/index.html +68 -1
  15. package/modules/public-landing/public/index.states.json +4 -4
  16. package/modules/public-landing/public/invitations-empty.states.json +56 -0
  17. package/modules/public-landing/public/invitations-picker.states.json +84 -0
  18. package/modules/public-landing/public/invitations.html +956 -0
  19. package/modules/public-landing/public/invitations.probes.json +430 -0
  20. package/modules/public-landing/public/invitations.states.json +473 -0
  21. package/modules/public-landing/public/privacy.html +1 -1
  22. package/modules/public-landing/public/projects.html +18 -543
  23. package/modules/public-landing/public/projects.probes.json +0 -29
  24. package/modules/public-landing/public/projects.states.json +0 -6
  25. package/modules/public-landing/public/terms.html +1 -1
  26. package/modules/ui-design/kit/fixtures/bongos-cli-latest-sample.json +10 -0
  27. package/modules/ui-design/kit/fixtures/featured-invitations.json +183 -0
  28. package/modules/ui-design/kit/fixtures/guilds-empty.json +1 -0
  29. package/modules/ui-design/kit/fixtures/me-guild-engagements-empty.json +1 -0
  30. package/modules/ui-design/kit/fixtures/me-guild-requests-empty.json +1 -0
  31. package/modules/ui-design/kit/fixtures/my-projects-applied.json +29 -0
  32. package/modules/ui-design/kit/fixtures/my-projects-invites-empty.json +1 -0
  33. package/modules/ui-design/kit/serve.js +5 -1
  34. package/package-lock.json +2 -2
  35. package/package.json +1 -1
  36. package/release-notes.json +10 -0
  37. package/src/module-api.js +1 -1
  38. package/src/platform-server.js +11 -0
  39. package/tests/hub_account_page.mjs +1 -1
  40. package/tests/hub_engagement_cards.mjs +44 -40
  41. package/tests/hub_guild_cards.mjs +24 -22
  42. package/tests/hub_invitations_page.mjs +414 -0
  43. package/tests/join_relay_outcome.mjs +1 -1
  44. package/tests/landing_legal_pages.mjs +8 -6
  45. package/tests/landing_page.mjs +125 -3
  46. package/tests/my_projects_surface.mjs +14 -6
  47. package/tests/public_landing_projects.mjs +12 -14
  48. package/tests/render_check.mjs +3 -2
  49. package/tests/ui_design_kit.mjs +3 -3
@@ -176,35 +176,6 @@
176
176
  { "name": "the wizard driver at 390: the review rows stack and the walk still reaches Create", "auth": true, "width": 390, "url": "/projects?view=new",
177
177
  "steps": [["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500]],
178
178
  "expect": {"visible":["#panel-look","#wizLook","#doCreate"],"hidden":["#panel-review"],"text":[["#look-h","Give it a look."]]} },
179
- { "name": "guild cards (task 1002303, R27): the mine view lists what your guilds have going, each card with its own Apply", "auth": true, "width": 1440, "url": "/projects?view=mine",
180
- "steps": [["wait",900]],
181
- "expect": { "visible": ["#mineInvites", "#engageList"], "count": [["#engageList .engageRow", 2], ["#engageList .engageRow .mineJoinNote:not([hidden])", 0]],
182
- "text": [["#engageList .engageRow:nth-child(1) .mineRow__name", "Forge Crew is applying to Tidepool — apply with them?"], ["#engageList .engageRow:nth-child(2) .mineRow__name", "Lantern Works invited Forge Crew"]] } },
183
- { "name": "guild cards: Add a note opens the card's own note; Apply sends it with the guild (the stub refuses a guild with no engagement) and the card says it went through", "continue": true,
184
- "steps": [["click","#engageList .engageRow:first-child .linklike"],["fill","#engageList .engageRow:first-child .mineJoinNote","Happy to take the migrations."],["click","#engageList .engageRow:first-child .btn"],["wait",400]],
185
- "expect": { "hidden": ["#engageList .engageRow:first-child .mineJoinNote"], "hasClass": [["#engageList .engageRow:first-child .projMsg", "ok"]],
186
- "text": [["#engageList .engageRow:first-child .btn", "Applied"], ["#engageList .engageRow:first-child .projMsg", "Requested access. The project’s owner admits new builders; once approved, sign in to enter."]] } },
187
- { "name": "guild cards: the invited card applies on its own, with no note", "continue": true,
188
- "steps": [["click","#engageList .engageRow:nth-child(2) .btn"],["wait",400]],
189
- "expect": { "text": [["#engageList .engageRow:nth-child(2) .btn", "Applied"]], "hasClass": [["#engageList .engageRow:nth-child(2) .projMsg", "ok"]] } },
190
- {"name": "guild invite cards (task 1004583): each says what joining shows and, before accept, that your numbers count — with the switch right there, set to count", "auth": true, "width": 1440, "url": "/projects?view=mine",
191
- "steps": [["wait",900]],
192
- "expect": {"visible": ["#guildInviteList", "#guildInviteList .guildRow:first-child .guildCount"], "count": [["#guildInviteList .guildRow", 2], ["#guildInviteList .guildRow:first-child a.linklike:not([hidden])", 1], ["#guildInviteList .guildRow:nth-child(2) a.linklike:not([hidden])", 0]], "text": [["#guildInviteList .guildRow:first-child .mineRow__name", "You’re invited to join Open Hands"], ["#guildInviteList .guildRow:first-child .guildCount__what", "Your numbers count toward this guild’s rounded totals — even while your record is hidden or your account is private. They are never shown on their own: only inside totals rounded down to a band (10+, 20+, 50+ …) and counted once a week. Leave them out of any guild you’d rather not share them with."]], "checked": "#guildInviteList .guildRow:first-child input[value=\"on\"]"} },
193
- {"name": "guild invite: Leave them out says what that means before anything is sent", "continue": true,
194
- "steps": [["click","#guildInviteList .guildRow:first-child .guildCount__opt:nth-child(2)"],["wait",150]],
195
- "expect": {"checked": "#guildInviteList .guildRow:first-child input[value=\"off\"]", "text": [["#guildInviteList .guildRow:first-child .guildCount__what", "Your numbers are left out of this guild’s totals, from its next weekly count."]]} },
196
- {"name": "guild invite: Accept sends the card's visibility and the switch (the stub refuses an accept without the card's visibility) and the card says where your numbers went", "continue": true,
197
- "steps": [["click","#guildInviteList .guildRow:first-child .btn"],["wait",400]],
198
- "expect": {"hasClass": [["#guildInviteList .guildRow:first-child .projMsg", "ok"], ["#guildInviteList .guildRow:first-child .btn", "ghost"]], "text": [["#guildInviteList .guildRow:first-child .btn", "Joined"], ["#guildInviteList .guildRow:first-child .projMsg", "You’ve joined Open Hands, and your numbers are left out of its totals."]]} },
199
- {"name": "guild invite: Decline drops the card, silently", "continue": true,
200
- "steps": [["click","#guildInviteList .guildRow:nth-child(2) .btn.ghost"],["wait",400]],
201
- "expect": {"count": [["#guildInviteList .guildRow", 1]]} },
202
- {"name": "join request (task 1004583): the owner of Night Shift accepts one and declines the other, from the hub", "continue": true,
203
- "steps": [["click","#guildRequestList .guildRow:first-child .btn"],["wait",400],["click","#guildRequestList .guildRow:nth-child(2) .btn.ghost"],["wait",400]],
204
- "expect": {"count": [["#guildRequestList .guildRow", 1]], "hasClass": [["#guildRequestList .guildRow:first-child .projMsg", "ok"]], "text": [["#guildRequestList .guildRow:first-child .btn", "Accepted"], ["#guildRequestList .guildRow:first-child .projMsg", "Tomás Ek is in Night Shift now."]]} },
205
- {"name": "guild cards at 320: the switch stacks and nothing scrolls sideways", "auth": true, "width": 320, "url": "/projects?view=mine",
206
- "steps": [["wait",900]],
207
- "expect": {"noOverflow": true, "visible": ["#guildInviteList .guildRow:first-child .guildCount"]} },
208
179
  { "name": "invite a guild (task 1002303, R27): a /g/ address gives one pending line per member on its public page, each a link to the hall", "auth": true, "width": 1440, "url": "/projects?view=manage&id=101",
209
180
  "steps": [["wait",900],["fill","#mInviteGuild","/g/forge-crew"],["click","#mInvite [data-invite-guild]"],["wait",400]],
210
181
  "expect": { "count": [["#mInviteList li", 4]], "value": [["#mInviteGuild", ""]],
@@ -27,12 +27,6 @@
27
27
  "in-manage-invite": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mInvite", "#mInvite h2", "#mInviteWho", "#mInvite [data-invite-send]"] } },
28
28
  "in-manage-invite-handoff": { "auth": true, "url": "/projects?view=manage&id=101", "actions": [["wait",900],["fill","#mInviteWho","octo-builder"],["click","#mInvite [data-invite-send]"],["wait",400]], "expect": {"visible":["#mInviteList a","#mInviteMsg"],"hidden":[]} },
29
29
  "in-manage-invite-guild": { "auth": true, "url": "/projects?view=manage&id=101", "actions": [["wait",900],["fill","#mInviteGuild","https://cloudbongos.com/g/forge-crew"],["click","#mInvite [data-invite-guild]"],["wait",400]], "expect": { "visible": ["#mInviteGuild", "#mInviteList a", "#mInviteGuildMsg"], "count": [["#mInviteList li", 4]], "value": [["#mInviteGuild", ""]], "hasClass": [["#mInviteGuildMsg", "ok"]], "text": [["#mInviteList li:first-child b", "Mara Quinn"], ["#mInviteGuildMsg", "Forge Crew: 4 members to invite. Send each invite from your project’s hall — every line above opens it with their name filled in."]] } },
30
- "in-mine-engagements": { "auth": true, "url": "/projects?view=mine", "actions": [["wait",900],["focus","#engageList .engageRow:first-child .btn"],["wait",200]], "expect": { "visible": ["#mineInvites", "#invitesList", "#engageList"], "count": [["#engageList .engageRow", 2], ["#engageList .engageRow .mineJoinNote:not([hidden])", 0]], "text": [["#engageList .engageRow:nth-child(1) .mineRow__name", "Forge Crew is applying to Tidepool — apply with them?"], ["#engageList .engageRow:nth-child(1) .mineRow__host", "tidepool.cloudbongos.com"], ["#engageList .engageRow:nth-child(2) .mineRow__name", "Lantern Works invited Forge Crew"], ["#engageList .engageRow:nth-child(1) .mineRow__hint", "You apply as yourself — add a note for the owner if you like."], ["#engageList .engageRow:nth-child(1) .btn", "Apply"]] } },
31
- "in-mine-engagement-note": { "auth": true, "url": "/projects?view=mine", "actions": [["wait",900],["click","#engageList .engageRow:first-child .linklike"],["fill","#engageList .engageRow:first-child .mineJoinNote","I like the boring parts too: migrations and the deploy path."],["wait",150]], "expect": { "visible": ["#engageList .engageRow:first-child .mineJoinNote"], "hidden": ["#engageList .engageRow:first-child .linklike"], "value": [["#engageList .engageRow:first-child .mineJoinNote", "I like the boring parts too: migrations and the deploy path."]] } },
32
- "in-mine-engagement-applied": { "auth": true, "url": "/projects?view=mine", "actions": [["wait",900],["click","#engageList .engageRow:first-child .linklike"],["fill","#engageList .engageRow:first-child .mineJoinNote","I like the boring parts too: migrations and the deploy path."],["click","#engageList .engageRow:first-child .btn"],["wait",400]], "expect": { "hidden": ["#engageList .engageRow:first-child .mineJoinNote", "#engageList .engageRow:first-child .linklike"], "hasClass": [["#engageList .engageRow:first-child .projMsg", "ok"], ["#engageList .engageRow:first-child .btn", "ghost"]], "text": [["#engageList .engageRow:first-child .btn", "Applied"], ["#engageList .engageRow:first-child .projMsg", "Requested access. The project’s owner admits new builders; once approved, sign in to enter."], ["#engageList .engageRow:nth-child(2) .btn", "Apply"]] } },
33
- "in-mine-guild-cards": {"auth": true, "url": "/projects?view=mine", "actions": [["wait", 900], ["focus", "#guildInviteList .guildRow:first-child .btn"], ["wait", 200]], "expect": {"visible": ["#mineInvites", "#guildInviteList", "#guildRequestList", "#guildInviteList .guildRow:first-child .guildCount"], "count": [["#guildInviteList .guildRow", 2], ["#guildRequestList .guildRow", 2], ["#guildInviteList .guildRow .guildCount", 2], ["#guildRequestList .guildRow .guildCount", 0]], "text": [["#guildInviteList .guildRow:nth-child(1) .mineRow__name", "You’re invited to join Open Hands"], ["#guildInviteList .guildRow:nth-child(2) .mineRow__name", "You’re invited to join Quiet Room"], ["#guildInviteList .guildRow:nth-child(1) .guildCount__what", "Your numbers count toward this guild’s rounded totals — even while your record is hidden or your account is private. They are never shown on their own: only inside totals rounded down to a band (10+, 20+, 50+ …) and counted once a week. Leave them out of any guild you’d rather not share them with."], ["#guildRequestList .guildRow:nth-child(1) .mineRow__name", "Tomás Ek asked to join Night Shift"], ["#guildRequestList .guildRow:nth-child(2) .mineRow__name", "@kai-lowe asked to join Night Shift"]], "checked": "#guildInviteList .guildRow:first-child input[value=\"on\"]"}},
34
- "in-mine-guild-count-off": {"auth": true, "url": "/projects?view=mine", "actions": [["wait", 900], ["click", "#guildInviteList .guildRow:first-child .guildCount__opt:nth-child(2)"], ["wait", 150]], "expect": {"checked": "#guildInviteList .guildRow:first-child input[value=\"off\"]", "text": [["#guildInviteList .guildRow:first-child .guildCount__what", "Your numbers are left out of this guild’s totals, from its next weekly count."], ["#guildInviteList .guildRow:first-child .btn", "Accept"]]}},
35
- "in-mine-guild-joined": {"auth": true, "url": "/projects?view=mine", "actions": [["wait", 900], ["click", "#guildInviteList .guildRow:first-child .guildCount__opt:nth-child(2)"], ["click", "#guildInviteList .guildRow:first-child .btn"], ["wait", 400]], "expect": {"hidden": ["#guildInviteList .guildRow:first-child .btn.ghost + .btn"], "hasClass": [["#guildInviteList .guildRow:first-child .projMsg", "ok"], ["#guildInviteList .guildRow:first-child .btn", "ghost"]], "text": [["#guildInviteList .guildRow:first-child .btn", "Joined"], ["#guildInviteList .guildRow:first-child .projMsg", "You’ve joined Open Hands, and your numbers are left out of its totals."]]}},
36
30
  "in-manage-app-live": { "auth": true, "url": "/projects?view=manage&id=101", "actions": [["wait",900],["focus","#mApp a"]], "expect": {"visible":["#mAppCard","#mApp .appStat","#mApp .appVars"],"hidden":[]} },
37
31
  "in-manage-app-building": { "auth": true, "url": "/projects?view=manage&id=102", "actions": [["wait",900],["focus","#mApp a"]], "expect": {"visible":["#mAppCard","#mApp .appStages"],"hidden":[]} },
38
32
  "in-manage-app-failed": { "auth": true, "url": "/projects?view=manage&id=105", "actions": [["wait",900],["focus","#mApp .appRetryBtn"]], "expect": {"visible":["#mAppCard","#mApp p.err","#mApp .appRetryBtn"],"hidden":["#mApp .appRetry"]} },
@@ -84,7 +84,7 @@
84
84
  <li class="bandCell"><a class="bandT" href="/projects?view=mine">My projects</a></li>
85
85
  <!-- apex-only because /account is: it is served only where platform-identity
86
86
  runs, so a child instance loses the item whole (as on projects.html) -->
87
- <!--apex-only:start--><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
87
+ <!--apex-only:start--><li class="bandCell"><a class="bandT" href="/invitations">Invitations</a></li><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
88
88
  <!-- the honest hover target; assets/hub-bar.js upgrades the click to the POST -->
89
89
  <li class="bandCell"><a class="bandT signout" href="/api/bongos/auth/logout">Sign out</a></li>
90
90
  </ul>
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": "0.4.2",
3
+ "assets": {
4
+ "mac-arm64": { "file": "bongos-0.4.2-darwin-arm64", "url": "https://github.com/example-owner/example-bongos/releases/download/v0.4.2/bongos-0.4.2-darwin-arm64" },
5
+ "mac-x64": { "file": "bongos-0.4.2-darwin-x64", "url": "https://github.com/example-owner/example-bongos/releases/download/v0.4.2/bongos-0.4.2-darwin-x64" },
6
+ "linux-x64": { "file": "bongos-0.4.2-linux-x64", "url": "https://github.com/example-owner/example-bongos/releases/download/v0.4.2/bongos-0.4.2-linux-x64" },
7
+ "linux-arm64": { "file": "bongos-0.4.2-linux-arm64", "url": "https://github.com/example-owner/example-bongos/releases/download/v0.4.2/bongos-0.4.2-linux-arm64" },
8
+ "win-x64": { "file": "bongos-0.4.2-windows-x64.exe", "url": "https://github.com/example-owner/example-bongos/releases/download/v0.4.2/bongos-0.4.2-windows-x64.exe" }
9
+ }
10
+ }
@@ -0,0 +1,183 @@
1
+ {
2
+ "projects": [
3
+ {
4
+ "origin": "https://example.com",
5
+ "name": "Example World",
6
+ "tagline": "A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.",
7
+ "art_url": "/assets/world/r2-chrome-blob-one.jpg",
8
+ "type": "game",
9
+ "template": "bead-wash",
10
+ "planet_template": null,
11
+ "planet_accent": null,
12
+ "created_at": "2026-07-15T22:12:57.732Z",
13
+ "builders": 11,
14
+ "tags": [
15
+ "art-pipeline",
16
+ "character-anim",
17
+ "demo",
18
+ "game"
19
+ ],
20
+ "owner": "example-owner",
21
+ "owner_avatar": "https://avatars.githubusercontent.com/u/283020391?v=4",
22
+ "faces": [
23
+ "https://avatars.githubusercontent.com/u/283020391?v=4",
24
+ "https://avatars.githubusercontent.com/u/64553469?v=4",
25
+ "https://avatars.githubusercontent.com/u/290184380?v=4",
26
+ "https://avatars.githubusercontent.com/u/295990394?v=4",
27
+ "https://avatars.githubusercontent.com/u/35337514?v=4"
28
+ ],
29
+ "shipped_30d": 0,
30
+ "shipped_30d_capped": false,
31
+ "hall_origin": "https://builders.example.com",
32
+ "state": "public",
33
+ "orb": "planet"
34
+ },
35
+ {
36
+ "origin": "https://emersonian-circles.cloudbongos.com",
37
+ "name": "Emersonian Circles",
38
+ "tagline": "An independent project built on Cloud Bongos.",
39
+ "art_url": null,
40
+ "type": null,
41
+ "template": null,
42
+ "planet_template": null,
43
+ "planet_accent": null,
44
+ "created_at": "2026-07-25T02:21:55.641Z",
45
+ "builders": 1,
46
+ "tags": [],
47
+ "owner": "example-owner",
48
+ "owner_avatar": "https://avatars.githubusercontent.com/u/283020391?v=4",
49
+ "faces": [],
50
+ "shipped_30d": 30,
51
+ "shipped_30d_capped": true,
52
+ "hall_origin": null,
53
+ "state": "public",
54
+ "orb": "planet"
55
+ },
56
+ {
57
+ "origin": "https://demo.cloudbongos.com",
58
+ "name": "Demo",
59
+ "tagline": null,
60
+ "art_url": null,
61
+ "type": null,
62
+ "template": "pair-gold-wash",
63
+ "planet_template": "pair-obsidian-tint",
64
+ "planet_accent": "sea",
65
+ "created_at": "2026-07-14T19:16:38.160Z",
66
+ "builders": null,
67
+ "tags": null,
68
+ "owner": null,
69
+ "owner_avatar": null,
70
+ "faces": null,
71
+ "shipped_30d": null,
72
+ "shipped_30d_capped": null,
73
+ "hall_origin": null,
74
+ "state": "public",
75
+ "orb": "planet"
76
+ },
77
+ {
78
+ "origin": "https://grove.cloudbongos.com",
79
+ "name": "The Grove",
80
+ "tagline": null,
81
+ "art_url": null,
82
+ "type": null,
83
+ "template": "pair-gold-wash",
84
+ "planet_template": "pair-obsidian-tint",
85
+ "planet_accent": "sea",
86
+ "created_at": "2026-07-14T19:16:38.160Z",
87
+ "builders": null,
88
+ "tags": null,
89
+ "owner": null,
90
+ "owner_avatar": null,
91
+ "faces": null,
92
+ "shipped_30d": null,
93
+ "shipped_30d_capped": null,
94
+ "hall_origin": null,
95
+ "state": "public",
96
+ "orb": "planet"
97
+ },
98
+ {
99
+ "origin": "https://tidepool.cloudbongos.com",
100
+ "name": "Tidepool",
101
+ "tagline": null,
102
+ "art_url": null,
103
+ "type": null,
104
+ "template": "pair-gold-wash",
105
+ "planet_template": "pair-obsidian-tint",
106
+ "planet_accent": "sea",
107
+ "created_at": "2026-07-14T19:16:38.160Z",
108
+ "builders": null,
109
+ "tags": null,
110
+ "owner": null,
111
+ "owner_avatar": null,
112
+ "faces": null,
113
+ "shipped_30d": null,
114
+ "shipped_30d_capped": null,
115
+ "hall_origin": null,
116
+ "state": "public",
117
+ "orb": "planet"
118
+ },
119
+ {
120
+ "origin": "https://lantern-works.cloudbongos.com",
121
+ "name": "Lantern Works",
122
+ "tagline": null,
123
+ "art_url": null,
124
+ "type": null,
125
+ "template": "pair-gold-wash",
126
+ "planet_template": "pair-obsidian-tint",
127
+ "planet_accent": "sea",
128
+ "created_at": "2026-07-14T19:16:38.160Z",
129
+ "builders": null,
130
+ "tags": null,
131
+ "owner": null,
132
+ "owner_avatar": null,
133
+ "faces": null,
134
+ "shipped_30d": null,
135
+ "shipped_30d_capped": null,
136
+ "hall_origin": null,
137
+ "state": "public",
138
+ "orb": "planet"
139
+ },
140
+ {
141
+ "origin": "https://northwind.example.org",
142
+ "name": "Northwind Atlas",
143
+ "tagline": null,
144
+ "art_url": null,
145
+ "type": null,
146
+ "template": "pair-gold-wash",
147
+ "planet_template": "pair-obsidian-tint",
148
+ "planet_accent": "sea",
149
+ "created_at": "2026-07-14T19:16:38.160Z",
150
+ "builders": null,
151
+ "tags": null,
152
+ "owner": null,
153
+ "owner_avatar": null,
154
+ "faces": null,
155
+ "shipped_30d": null,
156
+ "shipped_30d_capped": null,
157
+ "hall_origin": null,
158
+ "state": "public",
159
+ "orb": "planet"
160
+ },
161
+ {
162
+ "origin": null,
163
+ "name": null,
164
+ "tagline": null,
165
+ "art_url": null,
166
+ "type": null,
167
+ "template": null,
168
+ "planet_template": null,
169
+ "planet_accent": null,
170
+ "created_at": null,
171
+ "builders": null,
172
+ "tags": null,
173
+ "owner": null,
174
+ "owner_avatar": null,
175
+ "faces": null,
176
+ "shipped_30d": null,
177
+ "shipped_30d_capped": null,
178
+ "hall_origin": null,
179
+ "state": "stealth",
180
+ "orb": "black-hole"
181
+ }
182
+ ]
183
+ }
@@ -0,0 +1 @@
1
+ {"engagements":[],"count":0}
@@ -0,0 +1 @@
1
+ {"incoming":[],"outgoing":[],"incoming_count":0,"outgoing_count":0}
@@ -0,0 +1,29 @@
1
+ {
2
+ "owned": [],
3
+ "building": [
4
+ {
5
+ "client_id": "otb",
6
+ "name": "Example World",
7
+ "origin": "https://example.com",
8
+ "hall_origin": "https://builders.example.com",
9
+ "membership_kind": "member",
10
+ "joined_at": "2026-07-20T10:00:00.000Z",
11
+ "last_active": "2026-08-27T18:00:00.000Z"
12
+ },
13
+ {
14
+ "client_id": "emersonian",
15
+ "name": "Emersonian Circles",
16
+ "origin": "https://emersonian-circles.cloudbongos.com",
17
+ "hall_origin": null,
18
+ "membership_kind": "member",
19
+ "joined_at": "2026-07-26T10:00:00.000Z",
20
+ "last_active": "2026-08-20T18:00:00.000Z"
21
+ }
22
+ ],
23
+ "applied": [
24
+ {
25
+ "name": "Tidepool",
26
+ "origin": "https://tidepool.cloudbongos.com"
27
+ }
28
+ ]
29
+ }
@@ -561,6 +561,10 @@ const server = http.createServer((req, res) => {
561
561
  const p = u.pathname;
562
562
  const origin = `http://127.0.0.1:${server.address().port}`;
563
563
  if (p === '/__kit/avatar.svg') { res.writeHead(200, { 'content-type': MIME['.svg'], 'cache-control': 'no-store' }); return res.end(AVATAR_SVG); }
564
+ // the CLI release feed (task 1004257): mounted on an instance only when config/bongos-cli.json
565
+ // exists, so by default it 404s as an apex with no release does; a variant states file names
566
+ // a release with stub.fixtures ("bongos-cli-latest": "bongos-cli-latest-sample")
567
+ if (p === '/downloads/bongos/latest.json') return fixture('bongos-cli-latest', res, origin);
564
568
  // ── the API: /api/gds/* and /api/bongos/* are one surface on the instance ──
565
569
  const apiMatch = p.match(/^\/api\/(gds|bongos)(\/.*)$/);
566
570
  if (apiMatch) {
@@ -623,7 +627,7 @@ const server = http.createServer((req, res) => {
623
627
  if (body.guild !== undefined && !guildEngagementLive(body.guild, body.origin)) return refuse(res, 409, 'guild_engagement_gone', 'that guild engagement has ended');
624
628
  return json(res, 200, {
625
629
  ok: true, status: 'requested', project: { name: body.origin || 'a project', origin: body.origin || null },
626
- message: 'Requested access. The project’s owner admits new builders; once approved, sign in to enter.',
630
+ message: 'Applied. The project’s owner admits new builders; once approved, sign in to enter.',
627
631
  });
628
632
  }) : json(res, 401, UNAUTH);
629
633
  }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.66",
3
+ "version": "1.21.67",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.66",
9
+ "version": "1.21.67",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.66",
3
+ "version": "1.21.67",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -9172,5 +9172,15 @@
9172
9172
  "id": "1003451",
9173
9173
  "text": "The builder profile page no longer downloads a script that could never run; nothing on the page changes for anyone."
9174
9174
  }
9175
+ ],
9176
+ "1.21.67": [
9177
+ {
9178
+ "id": "1004276",
9179
+ "text": "Invitations now have their own page on the hub, cloudbongos.com/invitations: project and guild invites, guild requests, and a \"join a project\" field where you can pick a project from a list instead of typing its address. The \""
9180
+ },
9181
+ {
9182
+ "id": "1004257",
9183
+ "text": "The cloudbongos.com landing page now has a \"Download the CLI\" button in its closing section. It picks the right download for the visitor's computer and lists the others. It stays hidden until a CLI release is published, so not"
9184
+ }
9175
9185
  ]
9176
9186
  }
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.66'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.67'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -305,6 +305,17 @@ function buildPlatformApp() {
305
305
  if (!html) return next();
306
306
  res.type('html').send(html);
307
307
  });
308
+ // THE INVITATIONS PAGE (task 1004276, ADR 0337 D5): what asks you in — project
309
+ // and guild invites, a guild's engagements, the join requests to a guild you
310
+ // own — and applying to a project by its address, moved off /projects?view=mine
311
+ // onto a page of its own. Hub-only for the /account reason: every read and
312
+ // write it makes is a platform-identity route.
313
+ app.get('/invitations', (req, res, next) => {
314
+ if (!isApexHost(req) || !isModuleEnabled('platform-identity')) return next();
315
+ const html = landingPage('invitations.html');
316
+ if (!html) return next();
317
+ res.type('html').send(html);
318
+ });
308
319
  // THE GUILD PAGE (task 1002301, R25; ADR 0336 D4/D6): /g/<slug>, the namespace
309
320
  // D6 reserved. Hub-only for the /account reason — every read it makes is a
310
321
  // platform-identity route. ONE shell for every slug: the page reads the public
@@ -134,7 +134,7 @@ test('the bands say what the hub’s say and lead into /projects; Account is the
134
134
  ['Your projects', '/projects?view=mine'], ['New project', '/projects?view=new'],
135
135
  ], 'this page has no views of its own, so every item names /projects in full');
136
136
  assert.deepEqual(bandLinks(bandOf(PAGE, 'menuProfile')), [
137
- ['My projects', '/projects?view=mine'], ['Account', '/account'], ['Sign out', '/api/bongos/auth/logout'],
137
+ ['My projects', '/projects?view=mine'], ['Invitations', '/invitations'], ['Account', '/account'], ['Sign out', '/api/bongos/auth/logout'],
138
138
  ]);
139
139
  assert.deepEqual(PAGE.match(/<a [^>]*aria-current="page"[^>]*>[^<]+/g), ['<a class="bandT" href="/account" aria-current="page">Account']);
140
140
  });