@thatopen/services 0.6.0 → 0.8.0

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 (73) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +485 -100
  3. package/dist/cli/lib/revit-addin.d.ts +8 -0
  4. package/dist/cli.js +135 -24
  5. package/dist/index.cjs.js +2 -2
  6. package/dist/index.es.js +41 -50
  7. package/dist/vite-env.d.ts +1 -1
  8. package/docs/ai-quickstart.md +138 -4
  9. package/docs/cli/create.md +29 -29
  10. package/docs/cli/local-server.md +38 -38
  11. package/docs/cli/login.md +32 -32
  12. package/docs/cli/publish.md +31 -31
  13. package/docs/cli/run.md +26 -26
  14. package/docs/cli/serve.md +43 -43
  15. package/docs/cli/swap.md +30 -30
  16. package/docs/flow-architecture.md +1 -1
  17. package/docs/flow-plugin-guide.md +27 -1
  18. package/docs/platform-token.md +81 -0
  19. package/docs/revit-collab-quickstart.md +115 -35
  20. package/docs/rhino-interop-quickstart.md +458 -0
  21. package/package.json +1 -1
  22. package/resources/AGENTS.md +1 -1
  23. package/src/built-in/CDEManager/example.ts +214 -214
  24. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  25. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  26. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  27. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  28. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  30. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  31. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  32. package/src/built-in/UIManager/src/app/example.ts +97 -97
  33. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  34. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  35. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  36. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  37. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  38. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  39. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  40. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  41. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  42. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  43. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
  44. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  45. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  46. package/src/built-in/index.ts +485 -100
  47. package/src/cli/templates/app/index.html +35 -35
  48. package/src/cli/templates/app/package.json +32 -32
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  50. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  51. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  52. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  53. package/src/cli/templates/app/src/globals.ts +1 -1
  54. package/src/cli/templates/app/src/main.history.ts +191 -0
  55. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  56. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  57. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  58. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  59. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  60. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  61. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  62. package/src/cli/templates/app/tsconfig.json +16 -16
  63. package/src/cli/templates/app/vite.config.js +57 -57
  64. package/src/cli/templates/cloud-component/README.md +205 -205
  65. package/src/cli/templates/cloud-component/_thatopen +5 -5
  66. package/src/cli/templates/cloud-component/declarations.json +4 -4
  67. package/src/cli/templates/cloud-component/package.json +21 -21
  68. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  69. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  70. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  71. package/src/cli/templates/shared/AGENTS.md +8 -8
  72. package/src/cli/templates/shared/CLAUDE.md +1 -1
  73. package/src/cli/templates/shared/_gitignore +5 -5
@@ -1,31 +1,31 @@
1
- ---
2
- description: "Build and upload the project to the platform. Automatically detects app vs cloud component from the local .thatopen config. On first publish, saves the assigned item ID back to .thatopen so subsequent runs update the same item instead of creating a new one."
3
- ---
4
-
5
- ## thatopen publish
6
-
7
- Builds the project and uploads it to the platform. Works for both apps and cloud components — the project type is read from `.thatopen` (`itemType: APP | COMPONENT`).
8
-
9
- First publish creates the item and auto-saves its ID to `.thatopen`. Subsequent publishes update that same item.
10
-
11
- **Usage:**
12
- ```bash
13
- thatopen publish [flags]
14
- ```
15
-
16
- **Flags:**
17
-
18
- - `--name <name>` — Item name on the platform. Defaults to `name` in `package.json`.
19
- - `--version-tag <tag>` — Version tag. Defaults to `version` in `package.json`.
20
- - `--app-id <id>` — Target an existing app by ID (overrides `.thatopen`).
21
- - `--component-id <id>` — Target an existing cloud component by ID (overrides `.thatopen`).
22
- - `--skip-build` — Skip the build step and upload the existing `dist/bundle.js`.
23
- - `--icon <path>` — Path to an icon file (PNG, WebP, or ICO, max 512 KB). Saved to `.thatopen` for future publishes.
24
-
25
- **Examples:**
26
- ```bash
27
- thatopen publish
28
- thatopen publish --version-tag 2.0.0
29
- thatopen publish --skip-build
30
- thatopen publish --icon assets/icon.png
31
- ```
1
+ ---
2
+ description: "Build and upload the project to the platform. Automatically detects app vs cloud component from the local .thatopen config. On first publish, saves the assigned item ID back to .thatopen so subsequent runs update the same item instead of creating a new one."
3
+ ---
4
+
5
+ ## thatopen publish
6
+
7
+ Builds the project and uploads it to the platform. Works for both apps and cloud components — the project type is read from `.thatopen` (`itemType: APP | COMPONENT`).
8
+
9
+ First publish creates the item and auto-saves its ID to `.thatopen`. Subsequent publishes update that same item.
10
+
11
+ **Usage:**
12
+ ```bash
13
+ thatopen publish [flags]
14
+ ```
15
+
16
+ **Flags:**
17
+
18
+ - `--name <name>` — Item name on the platform. Defaults to `name` in `package.json`.
19
+ - `--version-tag <tag>` — Version tag. Defaults to `version` in `package.json`.
20
+ - `--app-id <id>` — Target an existing app by ID (overrides `.thatopen`).
21
+ - `--component-id <id>` — Target an existing cloud component by ID (overrides `.thatopen`).
22
+ - `--skip-build` — Skip the build step and upload the existing `dist/bundle.js`.
23
+ - `--icon <path>` — Path to an icon file (PNG, WebP, or ICO, max 512 KB). Saved to `.thatopen` for future publishes.
24
+
25
+ **Examples:**
26
+ ```bash
27
+ thatopen publish
28
+ thatopen publish --version-tag 2.0.0
29
+ thatopen publish --skip-build
30
+ thatopen publish --icon assets/icon.png
31
+ ```
package/docs/cli/run.md CHANGED
@@ -1,26 +1,26 @@
1
- ---
2
- description: "Build and run a cloud component locally as a child process, mirroring the backend execution environment. Accepts execution parameters as JSON via --params, validates them against declarations.json, and streams progress output to the terminal."
3
- ---
4
-
5
- ## thatopen run
6
-
7
- Builds and executes a cloud component locally, replicating what the platform's cloud engine does. Useful for testing component logic and parameters before publishing.
8
-
9
- Execution output (`MESSAGE`, `PROGRESS`, `SUCCESS`, `FAIL`) is streamed to the terminal with elapsed time.
10
-
11
- **Usage:**
12
- ```bash
13
- thatopen run [flags]
14
- ```
15
-
16
- **Flags:**
17
-
18
- - `--params <json>` — Execution parameters as a JSON string. Default: `'{}'`. Validated against `declarations.json` if present (warns, does not fail).
19
- - `--skip-build` — Skip the build step and run the existing `dist/bundle.js`.
20
-
21
- **Examples:**
22
- ```bash
23
- thatopen run
24
- thatopen run --params '{"fileId": "abc123"}'
25
- thatopen run --skip-build
26
- ```
1
+ ---
2
+ description: "Build and run a cloud component locally as a child process, mirroring the backend execution environment. Accepts execution parameters as JSON via --params, validates them against declarations.json, and streams progress output to the terminal."
3
+ ---
4
+
5
+ ## thatopen run
6
+
7
+ Builds and executes a cloud component locally, replicating what the platform's cloud engine does. Useful for testing component logic and parameters before publishing.
8
+
9
+ Execution output (`MESSAGE`, `PROGRESS`, `SUCCESS`, `FAIL`) is streamed to the terminal with elapsed time.
10
+
11
+ **Usage:**
12
+ ```bash
13
+ thatopen run [flags]
14
+ ```
15
+
16
+ **Flags:**
17
+
18
+ - `--params <json>` — Execution parameters as a JSON string. Default: `'{}'`. Validated against `declarations.json` if present (warns, does not fail).
19
+ - `--skip-build` — Skip the build step and run the existing `dist/bundle.js`.
20
+
21
+ **Examples:**
22
+ ```bash
23
+ thatopen run
24
+ thatopen run --params '{"fileId": "abc123"}'
25
+ thatopen run --skip-build
26
+ ```
package/docs/cli/serve.md CHANGED
@@ -1,43 +1,43 @@
1
- ---
2
- description: "Start a local dev server for a browser app. Uses esbuild in watch mode and serves the IIFE bundle on port 4000 with SSE live reload. To connect: open the project on platform.thatopen.com, click the Local App icon, then Get Started."
3
- ---
4
-
5
- ## thatopen serve
6
-
7
- Builds the app in watch mode and serves the IIFE bundle locally. On every save, esbuild rebuilds and the platform reloads the bundle automatically via SSE.
8
-
9
- Does **not** open a browser tab — the app runs inside the platform iframe. Open your project on the platform, then click the debug button to connect it to the local server.
10
-
11
- **Usage:**
12
- ```bash
13
- thatopen serve [flags]
14
- ```
15
-
16
- **Flags:**
17
-
18
- - `--port <port>` — Port to serve the bundle on. Default: `4000`.
19
-
20
- **Endpoints served:**
21
-
22
- - `GET /bundle.js` — The compiled IIFE bundle.
23
- - `GET /bundle.js.map` — Source map.
24
- - `GET /events` — SSE stream for live reload.
25
-
26
- **Example:**
27
- ```bash
28
- thatopen serve
29
- thatopen serve --port 4321
30
- ```
31
-
32
- **Connecting the platform to your local server:**
33
-
34
- 1. Go to [https://platform.thatopen.com](https://platform.thatopen.com) and open a project (create one first if needed).
35
- 2. In the top right corner, click the **Local App** icon.
36
- 3. Click **Get Started**.
37
-
38
- The platform loads the bundle from port 4000 and renders the app inside the project context, with full access to its models, data, and users. The dev server must be running before opening this URL — if nothing is served on port 4000, the platform will fail to load the app.
39
-
40
- The resulting URL follows this pattern:
41
- ```
42
- https://platform.thatopen.com/dashboard/projects/{projectId}/apps/local-app
43
- ```
1
+ ---
2
+ description: "Start a local dev server for a browser app. Uses esbuild in watch mode and serves the IIFE bundle on port 4000 with SSE live reload. To connect: open the project on platform.thatopen.com, click the Local App icon, then Get Started."
3
+ ---
4
+
5
+ ## thatopen serve
6
+
7
+ Builds the app in watch mode and serves the IIFE bundle locally. On every save, esbuild rebuilds and the platform reloads the bundle automatically via SSE.
8
+
9
+ Does **not** open a browser tab — the app runs inside the platform iframe. Open your project on the platform, then click the debug button to connect it to the local server.
10
+
11
+ **Usage:**
12
+ ```bash
13
+ thatopen serve [flags]
14
+ ```
15
+
16
+ **Flags:**
17
+
18
+ - `--port <port>` — Port to serve the bundle on. Default: `4000`.
19
+
20
+ **Endpoints served:**
21
+
22
+ - `GET /bundle.js` — The compiled IIFE bundle.
23
+ - `GET /bundle.js.map` — Source map.
24
+ - `GET /events` — SSE stream for live reload.
25
+
26
+ **Example:**
27
+ ```bash
28
+ thatopen serve
29
+ thatopen serve --port 4321
30
+ ```
31
+
32
+ **Connecting the platform to your local server:**
33
+
34
+ 1. Go to [https://platform.thatopen.com](https://platform.thatopen.com) and open a project (create one first if needed).
35
+ 2. In the top right corner, click the **Local App** icon.
36
+ 3. Click **Get Started**.
37
+
38
+ The platform loads the bundle from port 4000 and renders the app inside the project context, with full access to its models, data, and users. The dev server must be running before opening this URL — if nothing is served on port 4000, the platform will fail to load the app.
39
+
40
+ The resulting URL follows this pattern:
41
+ ```
42
+ https://platform.thatopen.com/dashboard/projects/{projectId}/apps/local-app
43
+ ```
package/docs/cli/swap.md CHANGED
@@ -1,30 +1,30 @@
1
- ---
2
- description: "Toggle or explicitly set the engine library mode between stable public packages (@thatopen/*) and private beta packages (@thatopen-platform/*-beta). Updates package.json and .thatopen, then runs npm install. Source code never changes — aliases handle resolution."
3
- ---
4
-
5
- ## thatopen swap
6
-
7
- Switches the project between stable and beta engine libraries. Without flags, it toggles from the current state. Updates `package.json` dependencies and sets `beta` in `.thatopen` so `thatopen serve`, `thatopen local-server`, and `vite build` all pick up the change automatically.
8
-
9
- Source code stays unchanged — `@thatopen/components` imports resolve to the correct package via esbuild/vite aliases based on the `beta` flag in `.thatopen`.
10
-
11
- **Usage:**
12
- ```bash
13
- thatopen swap # toggle: stable → beta or beta → stable
14
- thatopen swap --beta # force beta
15
- thatopen swap --stable # force stable
16
- ```
17
-
18
- **Flags:**
19
-
20
- - `--beta` — Switch to beta libraries (`@thatopen-platform/*-beta`).
21
- - `--stable` — Switch back to stable public libraries (`@thatopen/*`).
22
-
23
- **Examples:**
24
- ```bash
25
- thatopen swap
26
- thatopen swap --beta
27
- thatopen swap --stable
28
- ```
29
-
30
- > Beta packages are private and the access must be explicitly granted by That Open Company.
1
+ ---
2
+ description: "Toggle or explicitly set the engine library mode between stable public packages (@thatopen/*) and private beta packages (@thatopen-platform/*-beta). Updates package.json and .thatopen, then runs npm install. Source code never changes — aliases handle resolution."
3
+ ---
4
+
5
+ ## thatopen swap
6
+
7
+ Switches the project between stable and beta engine libraries. Without flags, it toggles from the current state. Updates `package.json` dependencies and sets `beta` in `.thatopen` so `thatopen serve`, `thatopen local-server`, and `vite build` all pick up the change automatically.
8
+
9
+ Source code stays unchanged — `@thatopen/components` imports resolve to the correct package via esbuild/vite aliases based on the `beta` flag in `.thatopen`.
10
+
11
+ **Usage:**
12
+ ```bash
13
+ thatopen swap # toggle: stable → beta or beta → stable
14
+ thatopen swap --beta # force beta
15
+ thatopen swap --stable # force stable
16
+ ```
17
+
18
+ **Flags:**
19
+
20
+ - `--beta` — Switch to beta libraries (`@thatopen-platform/*-beta`).
21
+ - `--stable` — Switch back to stable public libraries (`@thatopen/*`).
22
+
23
+ **Examples:**
24
+ ```bash
25
+ thatopen swap
26
+ thatopen swap --beta
27
+ thatopen swap --stable
28
+ ```
29
+
30
+ > Beta packages are private and the access must be explicitly granted by That Open Company.
@@ -386,4 +386,4 @@ Rhino kernel, so a converter can run server-side while real modelling operations
386
386
 
387
387
  The concrete wire formats — where each file lives on the platform, what the proposal envelope
388
388
  contains, how the queue and the ownership registry work — are in
389
- [the plugin guide](./flow-plugin-guide.md), which is what to read if you are writing a plugin.
389
+ [the plugin guide](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-plugin-guide.md), which is what to read if you are writing a plugin.
@@ -1,6 +1,6 @@
1
1
  # Writing a flow plugin
2
2
 
3
- The concrete contract. [The architecture notes](./flow-architecture.md) say why any of this is
3
+ The concrete contract. [The architecture notes](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-architecture.md) say why any of this is
4
4
  shaped the way it is; this says what the bytes look like.
5
5
 
6
6
  Nothing here is aspirational. Every rule below is what the Revit and Rhino plugins already do, and
@@ -152,6 +152,32 @@ made every proposal declare its units, which looked careful and was a bug waitin
152
152
  an author state a unit while writing coordinates in an axis convention the target does not use, a
153
153
  mistake no declared unit can catch.
154
154
 
155
+ ### Who wrote it
156
+
157
+ `author` is free text for a timeline to print. `userId` is the one that has to be right: it is what
158
+ the platform's own member component resolves into a real name and face. Get it wrong and the
159
+ proposal shows up under somebody else's avatar, which is worse than showing none.
160
+
161
+ **In a browser app, take it from `getProjectData`.** It answers with `currentUser` already resolved
162
+ server-side, and the members it lists are keyed by exactly the id the history panel indexes:
163
+
164
+ ```ts
165
+ const { currentUser } = await client.getProjectData(projectId);
166
+ const userId = currentUser?.user._id; // what the avatar resolves against
167
+ const author = currentUser?.user.fullName; // what the timeline prints
168
+ ```
169
+
170
+ **Do not look for it on the client's context.** `client.context` is the four fields the platform
171
+ injects into the iframe: `appId`, `projectId`, `accessToken`, `apiUrl`. Reading `context.userId` or
172
+ `context.user.name` yields `undefined`, and both failures are silent: the envelope simply ships
173
+ without a `userId`, and the panel falls back to passing `author` as the email, so the member
174
+ component renders whatever it makes of a string that names nobody. A converter did exactly this, and
175
+ it looked fine until somebody noticed the proposal was wearing another person's face.
176
+
177
+ **In a native plug-in there is no such call**, so read the `u` claim out of the API key's JWT payload
178
+ (base64url, no signature check, since this only decides which name to display). That is what the
179
+ Revit add-in does.
180
+
155
181
  ### The verbs
156
182
 
157
183
  One per item, in the attribute `flow:op`:
@@ -0,0 +1,81 @@
1
+ # Logging in to That Open Platform
2
+
3
+ Everything else in these guides needs this, and it is the same three lines every time, so it lives
4
+ here rather than in each of them.
5
+
6
+ ## Get the token
7
+
8
+ Say this to the user, and wait:
9
+
10
+ > "To log in I need a That Open Platform **access token**. You create it yourself:
11
+ > **https://platform.thatopen.com/dashboard/data → API Tokens → create → copy**, then paste it
12
+ > here."
13
+
14
+ **Never print it back, never write it into a file, never commit it.** Not into a script, not into a
15
+ `.env` you are about to show, not into a chat message quoting the command you ran.
16
+
17
+ ## Log in
18
+
19
+ ```bash
20
+ thatopen login --token <TOKEN>
21
+ ```
22
+
23
+ Credentials go to `~/.thatopen/config.json`, as `accessToken` and `apiUrl`. `--local` writes them to
24
+ a `.thatopen` file in the project instead, which is what a scaffolded project uses.
25
+
26
+ You do not need an npm account or an npm token for the private packages. The CLI derives access to
27
+ them from this one.
28
+
29
+ ## Everyone is on production
30
+
31
+ **Do not ask the user which environment they want.** Production is the answer, it is the default,
32
+ and the plain command above is already correct. Asking turns a decision nobody has into a question
33
+ they cannot answer, at the worst possible moment — the first thing you say to them.
34
+
35
+ There is a `dev` platform, and it exists for the people building That Open Platform itself. If the
36
+ user is one of them they already know, and they will tell you without being asked.
37
+
38
+ Everything below this line is for that case only. If nothing has mentioned dev, skip it.
39
+
40
+ <details>
41
+ <summary>Dev, for the platform team</summary>
42
+
43
+ Two platforms, not two views of one. Separate accounts, separate tokens, separate projects. A token
44
+ from one is refused by the other, and a model published to the wrong one lands somewhere the user's
45
+ team cannot see — while every command reports success.
46
+
47
+ ```bash
48
+ thatopen login --token <TOKEN> --api-url https://dev.platform.thatopen.com
49
+ ```
50
+
51
+ Its dashboard is the same page on that host: `https://dev.platform.thatopen.com/dashboard/data`.
52
+
53
+ </details>
54
+
55
+ ## Check, do not ask
56
+
57
+ **`login` prints "Logged in successfully" and does not name the environment**, so nothing on screen
58
+ would contradict a wrong assumption. Read `apiUrl` from `~/.thatopen/config.json` yourself.
59
+
60
+ If it says production — which it will — say nothing about it and carry on. There is no decision to
61
+ report.
62
+
63
+ **Only if it says dev**, and the user has not told you they work there, is there anything to say:
64
+
65
+ > "This machine is pointed at the dev platform, which is the one the That Open team builds on. I'll
66
+ > move it to production — that will need a token from **https://platform.thatopen.com/dashboard/data**,
67
+ > since dev tokens are not accepted there."
68
+
69
+ Check this **even when the machine is already logged in**, and especially then. A saved login is
70
+ whoever used this machine last, not an answer to a question you have asked.
71
+
72
+ ## When it fails
73
+
74
+ - **`Unauthorized`** — the token is invalid, expired, or from the other environment. Production
75
+ tokens use no `--api-url`; dev tokens need it. Ask for a fresh one from the same environment as
76
+ the URL you are using.
77
+ - **HTTP 403 on `item/folder?projectId=…`** — login worked. The account simply has no access to that
78
+ project, or the project is in the other environment. Confirm the project id with the user before
79
+ assuming anything is broken.
80
+ - **A private package fails to install** — you are not logged in, or logged in to the environment
81
+ that does not have it. Log in again and retry; there is no separate npm login to do.
@@ -37,11 +37,21 @@ the point.
37
37
  run, briefly, before you run it.
38
38
  - After each command, **check the output** before moving on. A command that answered is not the
39
39
  same as a command that did what you wanted.
40
+ - **Whatever you find already set up on this machine is somebody's previous session, not this
41
+ user's choice.** A saved login, an environment, a joined project, a model already open in Revit:
42
+ none of it is an answer to a question you have not asked. Report what you found and ask. This is
43
+ the single most likely way to get the whole thing wrong while every command succeeds.
40
44
 
41
45
  ---
42
46
 
43
47
  ## Step 0 — Ask what the user wants to do
44
48
 
49
+ **Only if they have not already told you.** "I want to start a new collaborative project in Revit
50
+ and here is my file" is (A), said plainly. Reading it back as a four-way menu costs the user a
51
+ whole turn to answer something they just said, and that is the single most common way this guide
52
+ wastes somebody's time. Work out which one it is, say which one you took it to be in one line, and
53
+ carry on. Ask only for what is genuinely missing.
54
+
45
55
  > "What would you like to do?
46
56
  > (A) **Share** a Revit model with my team for the first time.
47
57
  > (B) **Connect** to a shared central a teammate already uploaded.
@@ -51,9 +61,24 @@ the point.
51
61
  Then collect what that choice needs. Ask now; you will need it below.
52
62
 
53
63
  - **(A) Share:** the **absolute path** to the `.rvt`, a short lowercase **name** for the shared
54
- central (e.g. `tower-central`), and the platform **Project ID** (in the project's settings and in
55
- its URL in the dashboard). The file may or may not already be a workshared central — Step 3
56
- checks. Either way **the user's original file is never modified**; a copy is uploaded.
64
+ central (e.g. `tower-central`), and the platform **Project ID**. The file may or may not already
65
+ be a workshared central — Step 3 checks. Either way **the user's original file is never
66
+ modified**; a copy is uploaded.
67
+
68
+ **Sharing a new central means a new project. Always send them to make one, every time:**
69
+
70
+ > "A shared central lives inside a platform project, so let's make one for it. Open
71
+ > **https://platform.thatopen.com/dashboard/projects**, create a project, then paste me either its
72
+ > id or just the URL of the page you land on — I can take the id out of the URL."
73
+
74
+ Take the **URL** if that is easier for them; the id is the segment after `/projects/`. Asking
75
+ somebody to copy a hex string off a settings page is a worse experience than asking for the
76
+ address bar, and it is the same information.
77
+
78
+ **You cannot do this part for them.** The CLI has no command to list projects and none to create
79
+ one, so there is no menu to offer and nothing to pick from — which also means: do not guess an id,
80
+ and do not reuse one you saw in `status`. Every command after this needs it, and a wrong one fails
81
+ with a 403 that reads like a permissions problem rather than a typo.
57
82
 
58
83
  - **(B) Connect:** identify the existing central by **one** of:
59
84
  - the **project folder id** of the central — the `revit-<name>` folder inside the project's
@@ -77,16 +102,24 @@ Do **not** ask for the access token yet. That is Step 1.
77
102
 
78
103
  2. **Token.** Ask for it, and say exactly where to get it:
79
104
 
80
- > "To log in I need your That Open **access token**:
81
- > 1. Open your dashboard — **production:** https://platform.thatopen.com ·
82
- > **dev:** https://dev.platform.thatopen.com (use the one that matches your team).
83
- > 2. Go to **Data → API Tokens**.
84
- > 3. **Create a new token**, copy it, and paste it here."
105
+ > "To log in I need a That Open Platform **access token**. You create it yourself:
106
+ > **https://platform.thatopen.com/dashboard/data → API Tokens → create → copy**, then paste it
107
+ > here."
85
108
 
86
109
  ```
87
110
  thatopen login --token <TOKEN>
88
111
  ```
89
- Production is the default. For dev, add `--api-url https://dev.platform.thatopen.com`.
112
+
113
+ That command is already right: **it logs in to production, which is where everybody is.** Do not
114
+ ask the user to choose an environment — there is a dev platform, it is for the people building
115
+ That Open Platform, and they will say so without being asked.
116
+
117
+ **Log in even if the machine is already logged in.** A saved login is whoever used this machine
118
+ last. Never print the token back.
119
+
120
+ [Logging in to That Open Platform](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/platform-token.md) is the short page for when this does not
121
+ go smoothly: what to do if the machine turns out to be pointed at dev, and why `Unauthorized` and
122
+ `403` mean different things.
90
123
 
91
124
  **Log in before Step 2**, not after: installing the add-in downloads a private package and needs
92
125
  these credentials.
@@ -101,6 +134,16 @@ thatopen revit status
101
134
 
102
135
  - Prints `"loaded": true` → the add-in is running. Check `addinVersion` is **1.2.17 or later**; if
103
136
  not, update it with the same command as below. Then go to Step 3.
137
+
138
+ > **`status` also reports a `project`, a `doc` and a `central`. Do not use any of them.** They are
139
+ > whatever this machine did last, which on a colleague's laptop or a shared machine is somebody
140
+ > else's work. Say what you found and ask:
141
+ >
142
+ > > "This machine is still connected to project `<id>`, document `<doc>`. Do you want to keep
143
+ > > working on that one, connect to a different project, or share a new model?"
144
+ >
145
+ > Publishing into the wrong project succeeds, looks completely normal, and puts somebody's model
146
+ > somewhere their team cannot see it.
104
147
  - Errors *"The That Open Revit add-in is not running"* → work through (a), then (b).
105
148
 
106
149
  **(a) Is Revit 2026 open?** If not, launch it:
@@ -127,27 +170,30 @@ Re-run `thatopen revit status` every ten seconds or so until `"loaded": true`.
127
170
 
128
171
  ## Step 3 — Do the action
129
172
 
130
- ### (A) Share a model — `inspect`, then `publish-central`
173
+ ### (A) Share a model — one command
131
174
 
132
175
  ```
133
- thatopen revit inspect --file "<FILE>"
176
+ thatopen revit share --file "<FILE>" --project <PROJECT>
134
177
  ```
135
178
 
136
- - **`"isCentral": true`** → already a central. Tell the user you will upload a copy and their
137
- original is untouched, then:
138
- ```
139
- thatopen revit publish-central --project <PROJECT> --doc <DOC> --file "<FILE>"
140
- ```
141
- - **`"isCentral": false`** → a plain model. **Ask for consent first:** *"This file isn't a shared
142
- central yet. To share it I need to turn it into one. I'll do that on a COPY, so your original
143
- stays exactly as it is. Shall I go ahead?"* Only then:
144
- ```
145
- thatopen revit publish-central --project <PROJECT> --doc <DOC> --file "<FILE>" --convert
146
- ```
147
-
148
- Publishing uploads the central and opens **the user's own local** in Revit. Success ends with
149
- `Published (…). Central: … (version N). Your original file was not modified.` Teammates connect with
150
- the folder id from the dashboard, or with `--project` and `--doc`.
179
+ That is the whole of it. `share` installs the add-in if nothing is listening, starts Revit and
180
+ waits for it, checks whether the file is already a central, publishes it, and opens the user's own
181
+ local. It prints each of the five steps as it reaches them, so a long silence is always a step you
182
+ can name.
183
+
184
+ - **`--doc <name>`** is optional. Left out, it comes from the file name, kebab-cased.
185
+ - **`--project`** takes the dashboard URL as happily as the id.
186
+ - **The original file is never modified.** Everything happens on a copy, which is why `share` does
187
+ not need to ask before converting: there is nothing to lose. Say so rather than asking.
188
+ - **It joins at the end on purpose.** Sharing a model and then carrying on in the original file is
189
+ the one reliable way to end up outside your own team.
190
+
191
+ Steps 1 and 2 (CLI, login, add-in) still have to have happened: `share` installs the add-in but it
192
+ cannot log in for somebody.
193
+
194
+ If you need the pieces separately — a central that already exists, a different convert decision,
195
+ scripting around it — they are still there: `thatopen revit inspect --file`, then
196
+ `thatopen revit publish-central --project --doc --file [--convert]`, then `thatopen revit join`.
151
197
 
152
198
  ### (B) Connect to a central — `join`
153
199
 
@@ -224,11 +270,16 @@ anything that can produce the format can queue one. This package's job starts wh
224
270
  which is why nothing below asks where it came from.
225
271
 
226
272
  If the user asks how to send changes *from* their own tool — or you are being asked to write that
227
- tool — the format is documented: **[Writing a flow plugin](./flow-plugin-guide.md)** is the byte
228
- contract, and **[the architecture notes](./flow-architecture.md)** say why it is shaped that way.
273
+ tool — the format is documented: **[Writing a flow plugin](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-plugin-guide.md)** is the byte
274
+ contract, and **[the architecture notes](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-architecture.md)** say why it is shaped that way.
229
275
  Publishing a proposal is the smallest of the three jobs described there: it needs no ownership, no
230
276
  history, and no collaboration layer.
231
277
 
278
+ If the tool is **Rhino**, the whole path is already written and worked through end to end:
279
+ **[Rhino → Revit, start to finish](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/rhino-interop-quickstart.md)**. Read that
280
+ instead of assembling it from the two documents above — it covers installing the plug-in, driving
281
+ Rhino without a keyboard, and the traps that cost a night.
282
+
232
283
  **This is where you use the add-in's local API** — the CLI has no proposal commands yet.
233
284
 
234
285
  **List what is waiting:**
@@ -279,18 +330,47 @@ in a status field and differently in a model.
279
330
 
280
331
  ## Step 7 — Offer them a viewer that shows the progress
281
332
 
282
- Once a team has been syncing for a few days, every sync is a commit and the platform is holding the
283
- whole history: who changed what, when, and the geometry as it was at each version. Nothing in Revit
284
- shows that back to them, and it is the question people ask first — *what has changed since Friday?*
333
+ **Offer this as soon as the central exists — right after Step 3, before they start working.** Not
334
+ at the end. Every sync from that moment on becomes a commit they can watch arrive, and setting it up
335
+ costs the same on day one as on day thirty. Left to the end it sounds like an extra; offered at the
336
+ start it is the thing that makes the rest visible.
285
337
 
286
- So offer it, rather than waiting to be asked:
338
+ Every sync is a commit, and the platform keeps the lot: who changed what, when, and the geometry as
339
+ it was at each version. Nothing in Revit shows that back, and it is the first thing people ask —
340
+ *what has changed since Friday?*
287
341
 
288
- > "Everything you've synced is stored as a history. Would you like a viewer that shows it — pick any
342
+ Say it plainly and wait:
343
+
344
+ > "Everything you sync is stored as a history. Would you like a viewer that shows it — pick any
289
345
  > version and see what was created, modified and deleted, coloured in 3D?"
290
346
 
291
347
  If they say yes, that is an **app**, and building one is
292
- **[the platform AI quick start](./ai-quickstart.md)** — scaffold with the `app` template and follow
293
- it. What makes it a history viewer rather than an empty viewer is a built-in component that already
348
+ **[the platform AI quick start](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/ai-quickstart.md)** — scaffold with the `app` template and follow
349
+ it.
350
+
351
+ **Do not ask them how to add the panel.** Add a **new layout** called `History`, and leave the
352
+ scaffold's own layouts alone. It is a two-line change, it keeps the app they were given intact, and
353
+ it is the answer every time — asking hands somebody a UI decision about a screen they have not seen
354
+ yet.
355
+
356
+ ```ts
357
+ app.elements = {
358
+ ...app.elements,
359
+ history: () => html`<top-git-history></top-git-history>`,
360
+ };
361
+ ```
362
+
363
+ **For the layout, copy one of the scaffold's own panel layouts and swap its panel for `history`** —
364
+ same side, same width, same shape. Do not choose a side and do not pick a width: the app already
365
+ answered both, and the answer is written in `app.layouts` right there in `main.ts`.
366
+
367
+ A panel that opens on the opposite side from every other one, at a different size, is the first
368
+ thing anybody notices, and it reads as bolted on however good the contents are.
369
+
370
+ For the layout's `icon`, use **`at-icons:git-branch`** — That Open's own icon set, and the branch
371
+ mark is what the panel actually shows.
372
+
373
+ What makes it a history viewer rather than an empty viewer is a built-in component that already
294
374
  exists, so nobody writes this part:
295
375
 
296
376
  - **`GitHistoryManager`** — the headless half. It reads `revitflow_history.json` and the per-commit