@cloudgrid-io/mcp 0.20.54 → 0.20.56

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 (68) hide show
  1. package/README.md +2 -1
  2. package/package.json +4 -2
  3. package/src/corpus/playbook.md +1 -1
  4. package/src/corpus/templates/_cloudgrid.yaml.reference +1 -1
  5. package/src/corpus/templates/admin-dashboard/cloudgrid.yaml +1 -1
  6. package/src/corpus/templates/ai-app/cloudgrid.yaml +1 -1
  7. package/src/corpus/templates/ai-knowledge-base/cloudgrid.yaml +1 -1
  8. package/src/corpus/templates/analytics-dashboard/cloudgrid.yaml +1 -1
  9. package/src/corpus/templates/api-dashboard/cloudgrid.yaml +1 -1
  10. package/src/corpus/templates/api-docs/cloudgrid.yaml +1 -1
  11. package/src/corpus/templates/api-service/cloudgrid.yaml +1 -1
  12. package/src/corpus/templates/app-with-data/cloudgrid.yaml +1 -1
  13. package/src/corpus/templates/appointment-booking/cloudgrid.yaml +1 -1
  14. package/src/corpus/templates/approval-workflow/cloudgrid.yaml +1 -1
  15. package/src/corpus/templates/billing-dashboard/cloudgrid.yaml +1 -1
  16. package/src/corpus/templates/blog-cms/cloudgrid.yaml +1 -1
  17. package/src/corpus/templates/booking-system/cloudgrid.yaml +1 -1
  18. package/src/corpus/templates/calendar-scheduler/cloudgrid.yaml +1 -1
  19. package/src/corpus/templates/changelog/cloudgrid.yaml +1 -1
  20. package/src/corpus/templates/company-website/cloudgrid.yaml +1 -1
  21. package/src/corpus/templates/course-platform/cloudgrid.yaml +1 -1
  22. package/src/corpus/templates/crm/cloudgrid.yaml +1 -1
  23. package/src/corpus/templates/dashboard/cloudgrid.yaml +1 -1
  24. package/src/corpus/templates/deck/cloudgrid.yaml +1 -1
  25. package/src/corpus/templates/directory/cloudgrid.yaml +1 -1
  26. package/src/corpus/templates/docs-site/cloudgrid.yaml +1 -1
  27. package/src/corpus/templates/erp/cloudgrid.yaml +1 -1
  28. package/src/corpus/templates/event-board/cloudgrid.yaml +1 -1
  29. package/src/corpus/templates/expense-tracker/cloudgrid.yaml +1 -1
  30. package/src/corpus/templates/feature-request-board/cloudgrid.yaml +1 -1
  31. package/src/corpus/templates/financial-dashboard/cloudgrid.yaml +1 -1
  32. package/src/corpus/templates/forum/cloudgrid.yaml +1 -1
  33. package/src/corpus/templates/hr-portal/cloudgrid.yaml +1 -1
  34. package/src/corpus/templates/internal-tools-portal/cloudgrid.yaml +1 -1
  35. package/src/corpus/templates/inventory/cloudgrid.yaml +1 -1
  36. package/src/corpus/templates/invoice/cloudgrid.yaml +1 -1
  37. package/src/corpus/templates/job-board/cloudgrid.yaml +1 -1
  38. package/src/corpus/templates/kanban/cloudgrid.yaml +1 -1
  39. package/src/corpus/templates/landing-page/cloudgrid.yaml +1 -1
  40. package/src/corpus/templates/lms/cloudgrid.yaml +1 -1
  41. package/src/corpus/templates/marketplace/cloudgrid.yaml +1 -1
  42. package/src/corpus/templates/membership-site/cloudgrid.yaml +1 -1
  43. package/src/corpus/templates/monitoring-dashboard/cloudgrid.yaml +1 -1
  44. package/src/corpus/templates/multi-service/cloudgrid.yaml +1 -1
  45. package/src/corpus/templates/online-store/cloudgrid.yaml +1 -1
  46. package/src/corpus/templates/portfolio/cloudgrid.yaml +1 -1
  47. package/src/corpus/templates/product-catalog/cloudgrid.yaml +1 -1
  48. package/src/corpus/templates/product-launch/cloudgrid.yaml +1 -1
  49. package/src/corpus/templates/project-management/cloudgrid.yaml +1 -1
  50. package/src/corpus/templates/property-listings/cloudgrid.yaml +1 -1
  51. package/src/corpus/templates/quiz-platform/cloudgrid.yaml +1 -1
  52. package/src/corpus/templates/report/cloudgrid.yaml +1 -1
  53. package/src/corpus/templates/restaurant-reservations/cloudgrid.yaml +1 -1
  54. package/src/corpus/templates/revenue-dashboard/cloudgrid.yaml +1 -1
  55. package/src/corpus/templates/saas-marketing/cloudgrid.yaml +1 -1
  56. package/src/corpus/templates/semantic-search/cloudgrid.yaml +1 -1
  57. package/src/corpus/templates/simple-semantic-search/cloudgrid.yaml +1 -1
  58. package/src/corpus/templates/status-page/cloudgrid.yaml +1 -1
  59. package/src/corpus/templates/subscription-management/cloudgrid.yaml +1 -1
  60. package/src/corpus/templates/task-manager/cloudgrid.yaml +1 -1
  61. package/src/corpus/templates/ticket-system/cloudgrid.yaml +1 -1
  62. package/src/corpus/templates/time-tracking/cloudgrid.yaml +1 -1
  63. package/src/corpus/templates/travel-booking/cloudgrid.yaml +1 -1
  64. package/src/corpus/templates/waitlist/cloudgrid.yaml +1 -1
  65. package/src/corpus/templates/web-app/cloudgrid.yaml +1 -1
  66. package/src/tools/deploy.js +172 -11
  67. package/src/tools/register.js +42 -4
  68. package/src/tools.js +1 -0
package/README.md CHANGED
@@ -84,7 +84,7 @@ posted). `CLOUDGRID_QA_IDLE_MS` overrides the abandoned-idle window (default
84
84
  <!-- gen:tools -->
85
85
  ## Tools
86
86
 
87
- 36 tools registered (14 shared across both editions, 22 local-only). No deprecated aliases. MIN_CLI_VERSION: 0.15.26.
87
+ 37 tools registered (15 shared across both editions, 22 local-only). No deprecated aliases. MIN_CLI_VERSION: 0.15.26.
88
88
 
89
89
  ### Direct-API tools (both editions)
90
90
 
@@ -92,6 +92,7 @@ posted). `CLOUDGRID_QA_IDLE_MS` overrides the abandoned-idle window (default
92
92
  |---|---|---|
93
93
  | `grid_pickup` | API | Pick up an app: make your OWN COPY of any app you can see (like a git fork) into a grid you can build in. |
94
94
  | `grid_pull` | API | Pull an app to continue/edit it IN PLACE — like `git clone` of the SAME entity: your next grid_plug (with its target_... |
95
+ | `grid_collab` | API | Collab: GET PUSH ACCESS to the SAME live entity that someone else owns — you become a collaborator on THAT entity, no... |
95
96
  | `grid_create_grid` | API | Create a new grid (workspace) for the signed-in user — they become its admin. |
96
97
  | `grid_note` | API | Optionally leave a one-paragraph summary of what you built this session and why. |
97
98
  | `grid_plug` | API | Plug an app, website, game, or single HTML page into CloudGrid — the live runtime that runs it and provides its infra... |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.20.54",
3
+ "version": "0.20.56",
4
4
  "description": "MCP access to CloudGrid — the live runtime environment where the user's apps run WITH the infrastructure they need out of the box: managed MongoDB, Redis (cache/queues/pub-sub), a pgvector store, persistent disk, and built-in AI with no API keys, in any language or stack, no Docker or Kubernetes to manage. Brainstorm, build, plug, and improve apps, games, dashboards, and tools; instant single-page publishes and full runtime-app deploys with a live URL — for developers and non-technical users. Three editions: local stdio (full toolset), hosted web (light, CLI-free), and a public docs-search server.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,7 +58,9 @@
58
58
  "test:proxy": "node test/proxy.test.mjs",
59
59
  "test:system-ca": "node test/system-ca.test.mjs",
60
60
  "test:tool-titles-and-vocab": "node test/tool-titles-and-vocab.test.mjs",
61
- "test:visibility-mapping": "node test/visibility-mapping.test.mjs"
61
+ "test:visibility-mapping": "node test/visibility-mapping.test.mjs",
62
+ "test:verb-distinction": "node test/verb-distinction.test.mjs",
63
+ "test:collab": "node test/collab.test.mjs"
62
64
  },
63
65
  "dependencies": {
64
66
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -39,7 +39,7 @@ Operating rules:
39
39
  15. Editing an existing thing from just its URL (a fresh chat, no prior context — e.g. "change the background to green here <url>"): call grid_get_app_source(url) first. It resolves the entity_id and returns the current HTML plus its kind, single_html, capabilities, and replug_handle — read those to pick the branch:
40
40
  - Single-HTML inspiration you can re-plug (single_html: true and capabilities.replug: true): edit the returned HTML and call grid_plug with target_entity_id (or grid+slug — the replug_handle) to update the SAME URL in place. This works on every edition, including hosted.
41
41
  - Multi-file app or agent (kind is app or agent, or single_html: false): do NOT try to edit it as one inline HTML file. Tell the user it is a multi-file <kind>, give them the entity_id and the source (source_download_url), and explain that rebuilding it needs the local edition (Claude Desktop/Code) or the CLI — the hosted server cannot rebuild a multi-file app.
42
- - Not yours (capabilities.replug: false, reason not_owner): do NOT attempt a re-plug. Offer to fork it into the user's own grid with grid_pickup and edit the copy instead.
42
+ - Not yours (capabilities.replug: false, reason not_owner): do NOT attempt a re-plug. If the user wants their OWN copy, offer to fork it into their grid with grid_pickup and edit the copy. If the user wants to COLLABORATE on the SAME entity, use grid_collab. Three distinct verbs, do not conflate them: grid_pickup = make your OWN COPY (a fork, a NEW entity); grid_pull = continue the SAME entity in place (needs push access — you own it or are a collaborator); grid_collab = GET push access to that same entity (grants permission only and fetches nothing — run grid_pull afterwards to get the code, and if the owner gates access grid_collab sends them a request to approve). So if the user asks to "collab", do NOT call grid_pickup; call grid_collab — distinct from pickup's fork.
43
43
  16. Help CloudGrid QA. When you plug, fill BOTH of grid_plug's user_request (a brief version of the user's ask) AND session_note (one short paragraph on what you built and why) — include both by default; omit only if the user objects. A successful plug posts the QA log on that same call, so do NOT call grid_note after plugging — session_note is the narrative path. Use grid_note only BEFORE a plug, or in a session that ends without one. Everything here is recorded for quality review and never changes the plug.
44
44
  17. Auth check before an owned plug, with a guest fallback — ALWAYS ask first. grid_start reports signed_in. If the user wants to publish and is NOT signed in (or the plug returns a create-time 401), do NOT silently fail, do NOT invent an auth flow, and NEVER pass anon: true on your own — ask them plainly: "Sign in and plug it to your grid, or plug it as a guest right now (live immediately at a guest link that expires in 7 days unless claimed)?" Only if they choose guest, re-call grid_plug with anon: true and surface the claim_url + owner_token. grid_plug enforces this server-side: the first unauthenticated create in a session returns needs_auth (the ask) even when anon: true was passed — relay it to the user and stop; do not answer it yourself. This is distinct from rule 7: rule 7 forbids the anon fallback on a SERVER error (5xx) for a signed-in user (it burns quota and downgrades ownership); THIS rule is about not being authenticated at all, where guest is a legitimate choice the USER makes.
45
45
  18. Using the CLI (runtime apps, `grid dev`, or any shell step): the verb is `grid`, but ALWAYS invoke it as `npx -y @cloudgrid-io/cli@latest <command>` — never a bare `grid` or `cloudgrid`. A `grid` already on the machine is usually a stale global that the API rejects on its version floor, so a bare command fails with a version error and wastes turns; `npx …@latest` needs no install and is always current. In a local edition, prefer the MCP `grid_*` tools — they resolve a current CLI for you — and drop to `npx …@latest` only for shell-only steps like `grid dev`.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-app
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-admin
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-chatbot
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-knowledge-base
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: analytics-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-api-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: api-docs
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-api
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-app
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: appointment-booking
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: approval-workflow
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: billing-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: blog-cms
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: booking-system
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-calendar
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: changelog
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -24,7 +24,7 @@
24
24
 
25
25
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
26
26
  # Must start and end with a letter or digit.
27
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
27
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
28
28
  name: company-website
29
29
 
30
30
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: course-platform
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-crm
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: dashboard
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: deck
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-directory
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: docs-site
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-erp
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: event-board
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: expense-tracker
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: feature-request-board
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: financial-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: forum
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: hr-portal
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: internal-tools-portal
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-inventory
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-invoices
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-jobs
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-board
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: landing-page
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: lms
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-marketplace
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: membership-site
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: monitoring-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-app
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: online-store
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: portfolio
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: product-catalog
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: product-launch
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-project-board
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: property-listings
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: quiz-platform
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: report
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: restaurant-reservations
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: revenue-dashboard
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: saas-marketing
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: semantic-search
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: simple-semantic-search
24
24
  description: Semantic search over a bundled document, backed by pgvector.
25
25
  icon: "🔎"
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: status-page
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: subscription-management
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-tasks
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: my-tickets
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: time-tracking
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: travel-booking
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -19,7 +19,7 @@
19
19
 
20
20
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
21
  # Must start and end with a letter or digit.
22
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
22
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
23
23
  name: waitlist
24
24
 
25
25
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -25,7 +25,7 @@
25
25
 
26
26
  # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
27
27
  # Must start and end with a letter or digit.
28
- # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
28
+ # Becomes part of your public URL: <name>-<4hex>--<org>.cloudgrid.io
29
29
  name: web-app
30
30
 
31
31
  # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
@@ -334,13 +334,17 @@ const GUEST_ORG_SLUG = "guest";
334
334
  // path (create + edit, anon + authed) — flat-arch-aware per grid, matching the
335
335
  // host that actually serves. ALWAYS prefer `data.url` (see resolvePlugUrl);
336
336
  // this client-side derivation exists only for the rare response where `url`
337
- // came back empty (the server composes it best-effort). It mirrors the legacy
338
- // `entityUrl()` rules and is WRONG on flat-arch grids (which serve
339
- // `<slug>--<grid>.cloudgrid.io`), so it must never be the primary source:
337
+ // came back empty (the server composes it best-effort). It mirrors the canonical
338
+ // `entityUrl()` rules (the flat-only platform host), but only the server knows
339
+ // the per-entity nuances (custom domains, guest-org placement), so it must never
340
+ // be the primary source:
340
341
  // - inspiration (HTML drops): path-based at the org apex
341
342
  // https://<grid>.cloudgrid.io/<slug>
342
- // - runtime (app/agent): subdomain
343
- // https://<slug>.<grid>.cloudgrid.io
343
+ // - runtime (app/agent): flat platform host
344
+ // https://<slug>--<grid>.cloudgrid.io
345
+ // The old nested `<slug>.<grid>.cloudgrid.io` host is retired — it falls outside
346
+ // the single `*.cloudgrid.io` platform wildcard, so it has no cert and no ingress
347
+ // (matches `composeFlatEntityHost`).
344
348
  // Anonymous drops are grid-less in the response (`grid: null`); they live under
345
349
  // the Guest Org, so the apex slug is the constant `guest`.
346
350
  function composePlugUrl(data) {
@@ -349,7 +353,7 @@ function composePlugUrl(data) {
349
353
  const grid = data?.grid || GUEST_ORG_SLUG;
350
354
  const kind = data?.detection?.kind;
351
355
  if (kind === "app" || kind === "agent") {
352
- return `https://${slug}.${grid}.cloudgrid.io`;
356
+ return `https://${slug}--${grid}.cloudgrid.io`;
353
357
  }
354
358
  // inspiration (and any unknown/static kind) — path-based at the org apex.
355
359
  return `https://${grid}.cloudgrid.io/${slug}`;
@@ -630,8 +634,9 @@ export async function runReport(
630
634
  // grid_pull — continue/edit an existing entity IN PLACE (POST /entities/:id/pickup).
631
635
  // Like `git clone` of the SAME entity: your next grid_plug (target_entity_id)
632
636
  // updates it. Needs push access — you must OWN it or be a COLLABORATOR. A
633
- // view-only caller is told they can't edit/plug it (make a copy with grid_pickup,
634
- // or request collaborator access with the CLI `grid collab`). A claim_token also
637
+ // view-only caller is told they can't edit/plug it (fork a copy with grid_pickup,
638
+ // or GET collaborator push access with grid_collab — grants permission
639
+ // only, pull again once granted). A claim_token also
635
640
  // claims an anonymous drop into your account (ownership transfer).
636
641
  export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
637
642
  const token = await ctx.getToken();
@@ -724,8 +729,9 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
724
729
  return {
725
730
  text:
726
731
  `You don't have push access to this entity, so you can't edit or plug it. Two options: ` +
727
- `make your own separate copy with grid_pickup, or ask the owner for collaborator access with the CLI ` +
728
- `\`grid collab <entity>\` (rolling out soon) and pull again once granted.`,
732
+ `make your own separate copy (a fork) with grid_pickup, or GET collaborator push access to the SAME ` +
733
+ `entity with grid_collab (the CLI equivalent is \`grid collab <entity>\`) — that grants permission only and fetches nothing, so ` +
734
+ `pull again once it is granted (if the owner gates access, grid_collab becomes a request they approve).`,
729
735
  structured: { can_edit: false, owner_is_you: false, access: "view_only" },
730
736
  };
731
737
  }
@@ -752,7 +758,7 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
752
758
  head = `You have collaborator push access to ${slug}${where} — your next grid_plug (target_entity_id) updates the SHARED entity; the team sees the new version and can roll it back.`;
753
759
  canEdit = true;
754
760
  } else {
755
- head = `You can view ${slug}${where} but do NOT have push access — you can't edit or plug it. Make your own copy with grid_pickup, or request collaborator access with the CLI \`grid collab ${slug}\` (rolling out soon), then pull again.`;
761
+ head = `You can view ${slug}${where} but do NOT have push access — you can't edit or plug it. Make your own separate copy (a fork) with grid_pickup, or GET collaborator push access to the SAME entity with grid_collab (CLI equivalent: \`grid collab ${slug}\`) — it grants permission only and fetches nothing, so pull again once granted.`;
756
762
  canEdit = false;
757
763
  }
758
764
  const lines = [head];
@@ -774,6 +780,161 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
774
780
  }
775
781
 
776
782
 
783
+ // grid_collab — GET PUSH ACCESS to the SAME live entity you do NOT own (issue
784
+ // #253). This is the third adopt/access verb, distinct from the other two:
785
+ // grid_pickup = make your OWN COPY (a fork: new entity, forked_from lineage)
786
+ // grid_pull = continue an entity you ALREADY have access to (fetch the code)
787
+ // grid_collab = grant yourself push access to someone else's SAME entity
788
+ // (permission only — fetches nothing; run grid_pull afterwards)
789
+ //
790
+ // Mirrors the CLI reference (packages/cli/src/commands/collab.ts). It records the
791
+ // grant server-side via the CANONICAL join path POST /api/v2/entities/:id/collab
792
+ // — the SAME entity, NOT a fork. The fork route is POST /runtimes/:id/remix
793
+ // (runPickup); collab never touches it, so a collab can never silently become a
794
+ // pickup (the #242 defect). On a POLICY denial (NOT_ALLOWLISTED / PICKUP_DISABLED
795
+ // — the owner gated who may join) it does NOT dead-end on the 403: it turns the
796
+ // denial into a REQUEST the owner approves, via POST /:id/collab-requests, exactly
797
+ // as the CLI does (collab.ts:129). Other 403s (NOT_A_GRID_MEMBER / FORBIDDEN_ROLE)
798
+ // are grid-boundary problems, not policy denials, so they are surfaced, never
799
+ // converted to a request. Pure authenticated API calls, no CLI or filesystem —
800
+ // so, like grid_pickup / grid_pull, it ships on BOTH editions.
801
+ export async function runCollab(ctx, { entity_id, grid } = {}) {
802
+ const token = await ctx.getToken();
803
+ if (!token) {
804
+ throw new Error("You are not signed in. Run grid_login first, then collab.");
805
+ }
806
+ const target = entity_id;
807
+ if (!target) {
808
+ throw new Error("`entity_id` is required (a canonical UUID or <grid-slug>/<entity-slug>).");
809
+ }
810
+
811
+ const orgSlug = grid || (await ctx.getActiveGrid());
812
+ const headers = {
813
+ Authorization: `Bearer ${token}`,
814
+ "Content-Type": "application/json",
815
+ };
816
+ if (orgSlug) {
817
+ // Grid-native header + X-CloudGrid-Org alias (same slug) during the soak, so
818
+ // a bare-slug target resolves in the intended grid (mirrors runPull).
819
+ headers["X-CloudGrid-Grid"] = orgSlug;
820
+ headers["X-CloudGrid-Org"] = orgSlug;
821
+ }
822
+
823
+ const collabUrl = `${API_BASE}/api/v2/entities/${encodeURIComponent(target)}/collab`;
824
+ let res;
825
+ try {
826
+ res = await fetch(collabUrl, { method: "POST", headers, body: JSON.stringify({}) });
827
+ } catch (err) {
828
+ throw new Error(`Could not reach CloudGrid at ${API_BASE}: ${err.message}`);
829
+ }
830
+
831
+ const raw = await res.text();
832
+ let data = null;
833
+ try { data = JSON.parse(raw); } catch { /* handled below */ }
834
+
835
+ if (!res.ok) {
836
+ const code = data?.error?.code || null;
837
+ // ── Policy denial → REQUEST ACCESS (the whole point of the tool). Scoped to
838
+ // the two policy codes ONLY: a grid-boundary 403 (NOT_A_GRID_MEMBER /
839
+ // FORBIDDEN_ROLE) is a different problem and must NOT spam the owner. ──────
840
+ if (res.status === 403 && (code === "NOT_ALLOWLISTED" || code === "PICKUP_DISABLED")) {
841
+ return await requestCollabAccess(ctx, target, headers);
842
+ }
843
+ if (res.status === 404 || code === "RUNTIME_NOT_FOUND" || code === "NOT_FOUND" || (typeof code === "string" && code.endsWith("_NOT_FOUND"))) {
844
+ throw new Error(`No entity matched '${target}'. Check the id or grid/slug (a canonical UUID or <grid-slug>/<entity-slug>).`);
845
+ }
846
+ if (res.status === 403 && code === "NOT_A_GRID_MEMBER") {
847
+ throw new Error(data?.error?.message || `You're not a member of the grid that owns '${target}'. Ask its owner to invite you to the grid first, then collab.`);
848
+ }
849
+ if (res.status === 403 && code === "FORBIDDEN_ROLE") {
850
+ throw new Error(data?.error?.message || `Collaborating on '${target}' needs builder or admin role in its grid.`);
851
+ }
852
+ if (res.status === 400 && code === "NOT_A_RUNTIME") {
853
+ throw new Error(data?.error?.message || `'${target}' is an Inspiration, not an app or agent — it has no collaborators. Fork it with grid_pickup instead.`);
854
+ }
855
+ const msg = data?.error?.message || data?.message || raw || `HTTP ${res.status}`;
856
+ throw new Error(`Collab failed (HTTP ${res.status}): ${msg}`);
857
+ }
858
+
859
+ // ── Grant recorded on the SAME entity (permission only — no code fetched) ────
860
+ // Interpret the unified §4 pickup contract, exactly as runPull does, but frame
861
+ // it as an access grant and point at grid_pull for the code (founder split
862
+ // 2026-07-23: collab = permission, pull = code).
863
+ const slug = data?.slug || target;
864
+ const where = data?.grid ? ` in ${data.grid}` : "";
865
+ const ownerIsYou = data?.owner?.is_you === true;
866
+ const canReplug = data?.capabilities?.replug === true;
867
+ let head, canEdit;
868
+ if (ownerIsYou) {
869
+ head = `${slug}${where} is yours — nothing to grant. Use grid_pull to get the code, then grid_plug (target_entity_id) to update it.`;
870
+ canEdit = true;
871
+ } else if (canReplug) {
872
+ head = `You're now a collaborator on ${slug}${where} — you have PUSH ACCESS to the SAME entity (not a copy). This granted permission only; use grid_pull to get the code, then grid_plug (target_entity_id) updates the shared entity — the team sees the new version and can roll it back.`;
873
+ canEdit = true;
874
+ } else {
875
+ head = `You can view ${slug}${where}, but the grant did not give you push access. Make your own separate copy (a fork) with grid_pickup instead.`;
876
+ canEdit = false;
877
+ }
878
+ const lines = [head];
879
+ if (data?.url) lines.push(`URL: ${data.url}`);
880
+ return {
881
+ text: lines.join("\n"),
882
+ structured: {
883
+ ...(data?.entity_id ? { entity_id: data.entity_id } : {}),
884
+ ...(slug ? { slug } : {}),
885
+ grid: data?.grid ?? null,
886
+ ...(data?.url ? { url: data.url } : {}),
887
+ owner_is_you: ownerIsYou,
888
+ can_edit: canEdit,
889
+ access_requested: false,
890
+ request_pending: false,
891
+ },
892
+ };
893
+ }
894
+
895
+ // Turn a policy 403 into a request: POST /:id/collab-requests and tell the user
896
+ // what happens next (mirrors CLI collab-requests.ts requestCollabAccess). A
897
+ // request IS a success outcome, so this returns a normal result, never throws for
898
+ // the expected 409s (a pending ask, or an access race the owner just opened).
899
+ async function requestCollabAccess(ctx, target, headers) {
900
+ const url = `${API_BASE}/api/v2/entities/${encodeURIComponent(target)}/collab-requests`;
901
+ let res;
902
+ try {
903
+ res = await fetch(url, { method: "POST", headers, body: JSON.stringify({}) });
904
+ } catch (err) {
905
+ throw new Error(`Could not reach CloudGrid at ${API_BASE}: ${err.message}`);
906
+ }
907
+ const raw = await res.text();
908
+ let data = null;
909
+ try { data = JSON.parse(raw); } catch { /* handled below */ }
910
+ const code = data?.error?.code || null;
911
+
912
+ if (res.ok) {
913
+ return {
914
+ text:
915
+ `The owner gates who can collaborate on '${target}', so I asked them for push access on your behalf. ` +
916
+ `They've been notified — once they approve, run grid_collab again to join, then grid_pull to get the code.`,
917
+ structured: { access_requested: true, request_pending: true, owner_is_you: false, can_edit: false },
918
+ };
919
+ }
920
+ if (res.status === 409 && code === "COLLAB_REQUEST_EXISTS") {
921
+ return {
922
+ text: `You already asked for access to '${target}'. The owner hasn't decided yet — you'll be able to join once they approve.`,
923
+ structured: { access_requested: true, request_pending: true, owner_is_you: false, can_edit: false },
924
+ };
925
+ }
926
+ if (res.status === 409 && code === "COLLAB_ALREADY_ALLOWED") {
927
+ // Race: the owner opened access between the join 403 and this request.
928
+ return {
929
+ text: `You already have access to '${target}'. Run grid_collab again to join, then grid_pull to get the code.`,
930
+ structured: { access_requested: false, request_pending: false, owner_is_you: false, can_edit: false },
931
+ };
932
+ }
933
+ const msg = data?.error?.message || data?.message || raw || `HTTP ${res.status}`;
934
+ throw new Error(`Couldn't request access to '${target}' (HTTP ${res.status}): ${msg}`);
935
+ }
936
+
937
+
777
938
  // ── grid_plug — the unified create/re-plug verb (spec v2 §3) ──────────────
778
939
 
779
940
  // Total upload budget mirrors the server's multipart cap (100 MB).
@@ -21,6 +21,7 @@ import {
21
21
  detectSourceManifest,
22
22
  runReport,
23
23
  runPull,
24
+ runCollab,
24
25
  runPlug,
25
26
  runPickup,
26
27
  runCreateGrid,
@@ -132,7 +133,7 @@ export function registerTools(server, ctx) {
132
133
  "grid_pickup",
133
134
  {
134
135
  title: "Copy an app into your grid",
135
- description: "Pick up an app: make your OWN COPY of any app you can see (like a git fork) into a grid you can build in. It mints a NEW entity with lineage back to the source and WITHOUT the source's secrets (set your own before you plug). Plugging your copy creates/updates YOUR entity — the original is never touched. Requires sign-in. To edit the ORIGINAL entity in place (as its owner or a collaborator), use grid_pull instead.",
136
+ description: "Pick up an app: make your OWN COPY of any app you can see (like a git fork) into a grid you can build in. It mints a NEW entity with lineage back to the source and WITHOUT the source's secrets (set your own before you plug). Plugging your copy creates/updates YOUR entity — the original is never touched. Requires sign-in. This is a FORK, not a \"collab\": it never grants you access to anyone else's live entity. Getting PUSH ACCESS to the SAME entity is a different operation — the grid_collab tool (or the CLI `grid collab <entity>`) — so do NOT reach for grid_pickup when the user asks to collab; reach for grid_collab. To edit the ORIGINAL entity in place (as its owner or a collaborator), use grid_pull instead.",
136
137
  inputSchema: {
137
138
  entity_id: z.string().describe("The source app to copy: a canonical UUID or <grid-slug>/<entity-slug>."),
138
139
  grid: z.string().optional().describe("Grid to create your copy in. Required only when you belong to more than one grid."),
@@ -172,7 +173,7 @@ export function registerTools(server, ctx) {
172
173
  "grid_pull",
173
174
  {
174
175
  title: "Pull an app to edit in place",
175
- description: "Pull an app to continue/edit it IN PLACE — like `git clone` of the SAME entity: your next grid_plug (with its target_entity_id) updates that entity, and the team sees the new version. Requires PUSH ACCESS: you must own it or be a collaborator. If you can only view it, you CANNOT edit or plug it — say so and tell the user they can make their own copy with grid_pickup, or request collaborator access with the CLI `grid collab <entity>` (rolling out soon). Passing an anonymous drop's `claim_token` claims it into your account. Requires sign-in. Calls the API directly (both editions).",
176
+ description: "Pull an app to continue/edit it IN PLACE — like `git clone` of the SAME entity: your next grid_plug (with its target_entity_id) updates that entity, and the team sees the new version. Requires PUSH ACCESS: you must own it or be a collaborator. If you can only view it, you CANNOT edit or plug it — say so, and offer the two real options: (1) make your own separate copy (a fork) with grid_pickup, or (2) GET push access to the SAME entity with grid_collab (or the CLI `grid collab <entity>`) — \"collab\" is a distinct access-control operation that grants permission only and fetches nothing, so once it is granted you run grid_pull again to get the code (and if the owner gates access, grid_collab becomes a request they approve). Passing an anonymous drop's `claim_token` claims it into your account. Requires sign-in. Calls the API directly (both editions).",
176
177
  inputSchema: {
177
178
  entity_id: z.string().optional().describe("The entity id to pull. Defaults to this session's last anonymous drop."),
178
179
  claim_token: z.string().optional().describe("Anonymous drop only: its owner token — claims that drop into your account (ownership transfer)."),
@@ -197,6 +198,42 @@ export function registerTools(server, ctx) {
197
198
  },
198
199
  );
199
200
 
201
+ // grid_collab — GET PUSH ACCESS to the SAME live entity you do NOT own (#253).
202
+ // The THIRD adopt/access verb, distinct from grid_pickup (fork) and grid_pull
203
+ // (continue). Pure API (POST /entities/:id/collab; a policy 403 → a request via
204
+ // POST /:id/collab-requests), no CLI or filesystem — so it ships on BOTH
205
+ // editions, exactly like grid_pickup/grid_pull. Registered BEFORE the
206
+ // edition!=="local" gate for that reason.
207
+ reg(
208
+ "grid_collab",
209
+ {
210
+ title: "Get push access to an app you don't own",
211
+ description: "Collab: GET PUSH ACCESS to the SAME live entity that someone else owns — you become a collaborator on THAT entity, not a copy. This grants PERMISSION ONLY and fetches nothing: after it succeeds, run grid_pull to get the code, then grid_plug (with its target_entity_id) updates the SHARED entity in place (the team sees the new version and can roll it back). This is NOT a fork — it never mints a new entity and never carries forked_from lineage; if you want your OWN separate copy instead, that is grid_pickup. If the owner GATES who may join, this does NOT dead-end: it sends the owner a request for access on your behalf, and once they approve you run grid_collab again to join. Use this — not grid_pickup — whenever the user asks to \"collab\" on, \"join\", or \"get push/write access to\" an app they don't own. Requires sign-in. Calls the API directly (both editions).",
212
+ inputSchema: {
213
+ entity_id: z.string().describe("The app to get push access to: a canonical UUID or <grid-slug>/<entity-slug>."),
214
+ grid: z.string().optional().describe("Grid to resolve a bare slug in. Required only when a bare slug is ambiguous across grids you belong to."),
215
+ },
216
+ outputSchema: {
217
+ entity_id: z.string().optional().describe("The SAME entity you now collaborate on — pass it to grid_pull, then to grid_plug as target_entity_id. Never a new/forked entity."),
218
+ slug: z.string().optional().describe("Its slug."),
219
+ grid: z.string().nullable().optional().describe("Its grid."),
220
+ url: z.string().optional().describe("Its public URL."),
221
+ owner_is_you: z.boolean().optional().describe("True if you already own it (nothing to grant)."),
222
+ can_edit: z.boolean().optional().describe("True once you have push access (owner or collaborator). False = still view-only."),
223
+ access_requested: z.boolean().optional().describe("True when the owner gates access and a request was sent on your behalf."),
224
+ request_pending: z.boolean().optional().describe("True when a request is awaiting the owner's decision — join with grid_collab again once approved."),
225
+ },
226
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
227
+ },
228
+ async (input) => {
229
+ try {
230
+ return okResult(await runCollab(ctx, input || {}));
231
+ } catch (err) {
232
+ return fail(err.message);
233
+ }
234
+ },
235
+ );
236
+
200
237
  // grid_create_grid — create a grid for the signed-in user (POST /api/v2/grids,
201
238
  // the same call as the CLI `grid create grid <slug>`). Exists so a first-time
202
239
  // user with NO grid is never sent to the console: grid_plug returns
@@ -1206,11 +1243,12 @@ export function registerTools(server, ctx) {
1206
1243
  // source + cloudgrid.yaml and links the folder, so the next `grid plug` updates
1207
1244
  // the SAME entity in place. Wraps `grid pull` (NOT `grid pickup` — pickup now
1208
1245
  // makes a separate copy). Push access required; a view-only entity can't be
1209
- // pulled — use grid_pickup to copy it, or `grid collab` to request access.
1246
+ // pulled — use grid_pickup to fork a copy, or grid_collab to GET push access
1247
+ // to the same entity (grants permission only — pull again once granted).
1210
1248
  regTool(
1211
1249
  "grid_edit_existing_app",
1212
1250
  "Pull app source to edit locally",
1213
- "Continue/edit an EXISTING entity locally: download its source + cloudgrid.yaml and link the folder so your next `grid plug` updates it IN PLACE. Requires push access (owner or collaborator). Wraps `grid pull`. To make your own separate copy instead, use grid_pickup.",
1251
+ "Continue/edit an EXISTING entity locally: download its source + cloudgrid.yaml and link the folder so your next `grid plug` updates it IN PLACE. Requires push access (owner or collaborator). Wraps `grid pull`. To make your own separate copy (a fork) instead, use grid_pickup. To GET push access to an entity you can currently only view, that is a different operation — grid_collab (CLI equivalent: `grid collab <entity>`), which grants permission only; run the pull again once it is granted.",
1214
1252
  {
1215
1253
  name: z.string().describe("Entity slug or id to pull."),
1216
1254
  target_dir: z.string().optional().describe("Directory to pull into (relative to cwd). Defaults to the entity name."),
package/src/tools.js CHANGED
@@ -35,6 +35,7 @@ export {
35
35
  runPlug,
36
36
  runPickup,
37
37
  runPull,
38
+ runCollab,
38
39
  runCreateGrid,
39
40
  scanInlineSecrets,
40
41
  secretBlockMessage,