@thatopen/services 0.5.0 → 0.7.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.
- package/README.md +62 -62
- package/dist/built-in/index.d.ts +569 -0
- package/dist/cli.js +12 -23
- package/dist/index.cjs.js +2 -2
- package/dist/index.es.js +41 -49
- package/dist/vite-env.d.ts +1 -1
- package/docs/ai-quickstart.md +37 -0
- package/docs/builtin/paths.json +4 -0
- package/docs/cli/create.md +29 -29
- package/docs/cli/local-server.md +38 -38
- package/docs/cli/login.md +32 -32
- package/docs/cli/publish.md +31 -31
- package/docs/cli/run.md +26 -26
- package/docs/cli/serve.md +43 -43
- package/docs/cli/swap.md +30 -30
- package/docs/flow-architecture.md +389 -0
- package/docs/flow-plugin-guide.md +337 -0
- package/docs/platform-token.md +81 -0
- package/docs/revit-collab-quickstart.md +363 -173
- package/docs/rhino-interop-quickstart.md +391 -0
- package/package.json +1 -1
- package/resources/AGENTS.md +40 -40
- package/src/built-in/CDEManager/example.ts +214 -214
- package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
- package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
- package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
- package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
- package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
- package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
- package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
- package/src/built-in/UIManager/src/app/example.ts +97 -97
- package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
- package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
- package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
- package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
- package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
- package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
- package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
- package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
- package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
- package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
- package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -0
- package/src/built-in/UIManager/src/models-list/example.ts +122 -122
- package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
- package/src/built-in/index.ts +570 -0
- package/src/cli/templates/app/index.html +35 -35
- package/src/cli/templates/app/package.json +32 -32
- package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
- package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
- package/src/cli/templates/app/src/bim-components/index.ts +1 -1
- package/src/cli/templates/app/src/globals.ts +1 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
- package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
- package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
- package/src/cli/templates/app/src/ui-components/index.ts +2 -2
- package/src/cli/templates/app/tsconfig.json +16 -16
- package/src/cli/templates/app/vite.config.js +57 -57
- package/src/cli/templates/cloud-component/README.md +205 -205
- package/src/cli/templates/cloud-component/_thatopen +5 -5
- package/src/cli/templates/cloud-component/declarations.json +4 -4
- package/src/cli/templates/cloud-component/package.json +21 -21
- package/src/cli/templates/cloud-component/src/main.ts +70 -70
- package/src/cli/templates/cloud-component/tsconfig.json +16 -16
- package/src/cli/templates/cloud-component/vite.config.js +46 -46
- package/src/cli/templates/shared/AGENTS.md +8 -8
- package/src/cli/templates/shared/CLAUDE.md +1 -1
- package/src/cli/templates/shared/_gitignore +5 -5
|
@@ -1,256 +1,446 @@
|
|
|
1
1
|
# That Open — Revit Collaboration Quickstart (for AI assistants)
|
|
2
2
|
|
|
3
|
-
You are an AI assistant. A user pointed you at this guide so you can set up or join
|
|
4
|
-
|
|
5
|
-
order**. Ask the user for anything you don't already have.
|
|
6
|
-
yourself**
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
You are an AI assistant. A user pointed you at this guide so you can set up or join **real-time
|
|
4
|
+
Revit collaboration** through the That Open platform, and review the **proposals** other tools send
|
|
5
|
+
into their model. Follow the steps **in order**. Ask the user for anything you don't already have.
|
|
6
|
+
**You may run the setup commands yourself** — install the CLI, install the add-in, launch Revit —
|
|
7
|
+
*except* where a step says only the user can.
|
|
8
|
+
|
|
9
|
+
Needs add-in **1.2.17 or later**. `thatopen revit status` prints `addinVersion`; if it is older,
|
|
10
|
+
Step 2 updates it. Several things below did not exist or behaved differently before that.
|
|
9
11
|
|
|
10
12
|
## What this does
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
**
|
|
13
|
+
|
|
14
|
+
Two halves, and they are independent. A team can use the first without ever touching the second.
|
|
15
|
+
|
|
16
|
+
**Collaboration.** Several people work on **one shared Revit central** using Revit's own
|
|
17
|
+
worksharing. The central lives on the platform. Everyone has their own local; syncing sends their
|
|
18
|
+
changes up and brings the team's down. Syncs are **queued**, so the central never diverges.
|
|
19
|
+
|
|
20
|
+
**Proposals.** A change authored *outside* Revit arrives as a proposal: a list of adds, edits and
|
|
21
|
+
deletions naming Revit elements. Somebody in Revit reads it and decides. Nothing is applied behind
|
|
22
|
+
anyone's back, and applying it is an ordinary Revit edit that syncs like any other.
|
|
23
|
+
|
|
24
|
+
The proposal system is **open on the authoring side on purpose**. This package does not care what
|
|
25
|
+
wrote one — a Rhino plug-in, a cloud component, a script, a web app. What it defines is the format
|
|
26
|
+
and what Revit does when one arrives. Rhino is the example that exists today; it is an example, not
|
|
27
|
+
the point.
|
|
15
28
|
|
|
16
29
|
## Rules (follow throughout)
|
|
30
|
+
|
|
17
31
|
- **Never** print, echo, store, or commit the user's access token.
|
|
18
32
|
- **Ask, don't assume.** Missing a project id, file path, or central name? Ask first.
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
33
|
+
- Prefer the **`thatopen` CLI** for platform actions. For the things it does not cover — proposals,
|
|
34
|
+
and reading who holds what — talk to the **add-in's local API** as described in *Talking to the
|
|
35
|
+
add-in directly* below. Do not call the platform's own HTTP API by hand.
|
|
36
|
+
- You may **install software and launch Revit** on the user's machine. Say what you are about to
|
|
37
|
+
run, briefly, before you run it.
|
|
38
|
+
- After each command, **check the output** before moving on. A command that answered is not the
|
|
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.
|
|
23
44
|
|
|
24
45
|
---
|
|
25
46
|
|
|
26
47
|
## Step 0 — Ask what the user wants to do
|
|
27
|
-
|
|
48
|
+
|
|
28
49
|
> "What would you like to do?
|
|
29
50
|
> (A) **Share** a Revit model with my team for the first time.
|
|
30
51
|
> (B) **Connect** to a shared central a teammate already uploaded.
|
|
31
|
-
> (C) I **already have my local open** in Revit — just sync it.
|
|
52
|
+
> (C) I **already have my local open** in Revit — just sync it.
|
|
53
|
+
> (D) **Review a proposal** somebody sent into my model."
|
|
54
|
+
|
|
55
|
+
Then collect what that choice needs. Ask now; you will need it below.
|
|
56
|
+
|
|
57
|
+
- **(A) Share:** the **absolute path** to the `.rvt`, a short lowercase **name** for the shared
|
|
58
|
+
central (e.g. `tower-central`), and the platform **Project ID**. The file may or may not already
|
|
59
|
+
be a workshared central — Step 3 checks. Either way **the user's original file is never
|
|
60
|
+
modified**; a copy is uploaded.
|
|
32
61
|
|
|
33
|
-
|
|
62
|
+
**Sharing a new central means a new project. Always send them to make one, every time:**
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
**
|
|
37
|
-
|
|
38
|
-
in Step 3; either way **the user's original file is never modified** (a copy is uploaded).
|
|
64
|
+
> "A shared central lives inside a platform project, so let's make one for it. Open
|
|
65
|
+
> **https://platform.thatopen.com/dashboard/projects**, create a project, then paste me either its
|
|
66
|
+
> id or just the URL of the page you land on — I can take the id out of the URL."
|
|
39
67
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- the **local path** to the shared central on their PC, if they already have it
|
|
44
|
-
(looks like `C:\ThatOpenShared\<project>\<name>\<file>.rvt`).
|
|
68
|
+
Take the **URL** if that is easier for them; the id is the segment after `/projects/`. Asking
|
|
69
|
+
somebody to copy a hex string off a settings page is a worse experience than asking for the
|
|
70
|
+
address bar, and it is the same information.
|
|
45
71
|
|
|
46
|
-
|
|
72
|
+
**You cannot do this part for them.** The CLI has no command to list projects and none to create
|
|
73
|
+
one, so there is no menu to offer and nothing to pick from — which also means: do not guess an id,
|
|
74
|
+
and do not reuse one you saw in `status`. Every command after this needs it, and a wrong one fails
|
|
75
|
+
with a 403 that reads like a permissions problem rather than a typo.
|
|
47
76
|
|
|
48
|
-
- **(
|
|
49
|
-
the project
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
you don't need to ask the Project ID separately.
|
|
77
|
+
- **(B) Connect:** identify the existing central by **one** of:
|
|
78
|
+
- the **project folder id** of the central — the `revit-<name>` folder inside the project's
|
|
79
|
+
`bimterop` folder in the dashboard, or
|
|
80
|
+
- the **local path**, if they already have it: `C:\ThatOpenShared\<project>\<name>\<file>.rvt`.
|
|
53
81
|
|
|
54
|
-
- **(C):** nothing extra
|
|
82
|
+
- **(C)** and **(D):** nothing extra, but Revit must have their local open.
|
|
55
83
|
|
|
56
|
-
Do **not** ask for the access token yet
|
|
84
|
+
Do **not** ask for the access token yet. That is Step 1.
|
|
57
85
|
|
|
58
86
|
---
|
|
59
87
|
|
|
60
|
-
## Step 1 —
|
|
61
|
-
|
|
88
|
+
## Step 1 — CLI and login
|
|
89
|
+
|
|
90
|
+
1. **CLI:**
|
|
62
91
|
```
|
|
63
92
|
npm install -g @thatopen/services@latest
|
|
64
93
|
```
|
|
65
|
-
|
|
66
|
-
|
|
94
|
+
Check `thatopen --version` prints something. Needs Node.js 18+; if `node -v` fails, ask the user
|
|
95
|
+
to install Node.js first.
|
|
67
96
|
|
|
68
|
-
2. **
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- Errors **"The That Open Revit add‑in is not running"** → work through (a) then (b):
|
|
97
|
+
2. **Token.** Ask for it, and say exactly where to get it:
|
|
98
|
+
|
|
99
|
+
> "To log in I need a That Open Platform **access token**. You create it yourself:
|
|
100
|
+
> **https://platform.thatopen.com/dashboard/data → API Tokens → create → copy**, then paste it
|
|
101
|
+
> here."
|
|
74
102
|
|
|
75
|
-
**(a) Is Revit 2026 open?** If not, launch it (Windows):
|
|
76
103
|
```
|
|
77
|
-
|
|
104
|
+
thatopen login --token <TOKEN>
|
|
78
105
|
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- On the first launch after installing, Revit shows an **"unsigned add‑in"** prompt.
|
|
94
|
-
**Only the user can click it — ask them to choose "Always Load".**
|
|
95
|
-
- Re‑run `thatopen revit status` every ~10s until it prints `"loaded": true`.
|
|
106
|
+
|
|
107
|
+
That command is already right: **it logs in to production, which is where everybody is.** Do not
|
|
108
|
+
ask the user to choose an environment — there is a dev platform, it is for the people building
|
|
109
|
+
That Open Platform, and they will say so without being asked.
|
|
110
|
+
|
|
111
|
+
**Log in even if the machine is already logged in.** A saved login is whoever used this machine
|
|
112
|
+
last. Never print the token back.
|
|
113
|
+
|
|
114
|
+
[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
|
|
115
|
+
go smoothly: what to do if the machine turns out to be pointed at dev, and why `Unauthorized` and
|
|
116
|
+
`403` mean different things.
|
|
117
|
+
|
|
118
|
+
**Log in before Step 2**, not after: installing the add-in downloads a private package and needs
|
|
119
|
+
these credentials.
|
|
96
120
|
|
|
97
121
|
---
|
|
98
122
|
|
|
99
|
-
## Step 2 —
|
|
100
|
-
The user needs a personal **access token** from the That Open platform. Ask them for it and
|
|
101
|
-
tell them exactly where to get it:
|
|
123
|
+
## Step 2 — The add-in
|
|
102
124
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
> 2. Go to **Data → API Tokens**.
|
|
107
|
-
> 3. Click **Create a new token** and **copy** it.
|
|
108
|
-
> 4. Paste it here."
|
|
125
|
+
```
|
|
126
|
+
thatopen revit status
|
|
127
|
+
```
|
|
109
128
|
|
|
110
|
-
|
|
129
|
+
- Prints `"loaded": true` → the add-in is running. Check `addinVersion` is **1.2.17 or later**; if
|
|
130
|
+
not, update it with the same command as below. Then go to Step 3.
|
|
131
|
+
|
|
132
|
+
> **`status` also reports a `project`, a `doc` and a `central`. Do not use any of them.** They are
|
|
133
|
+
> whatever this machine did last, which on a colleague's laptop or a shared machine is somebody
|
|
134
|
+
> else's work. Say what you found and ask:
|
|
135
|
+
>
|
|
136
|
+
> > "This machine is still connected to project `<id>`, document `<doc>`. Do you want to keep
|
|
137
|
+
> > working on that one, connect to a different project, or share a new model?"
|
|
138
|
+
>
|
|
139
|
+
> Publishing into the wrong project succeeds, looks completely normal, and puts somebody's model
|
|
140
|
+
> somewhere their team cannot see it.
|
|
141
|
+
- Errors *"The That Open Revit add-in is not running"* → work through (a), then (b).
|
|
142
|
+
|
|
143
|
+
**(a) Is Revit 2026 open?** If not, launch it:
|
|
144
|
+
```
|
|
145
|
+
powershell -Command "Start-Process 'C:\Program Files\Autodesk\Revit 2026\Revit.exe'"
|
|
111
146
|
```
|
|
112
|
-
thatopen
|
|
147
|
+
A cold start takes a while. Re-run `thatopen revit status`.
|
|
148
|
+
|
|
149
|
+
**(b) Revit is open but status still errors → install the add-in.** Revit must be **closed** for
|
|
150
|
+
this, because it holds its add-in files open while it runs. Ask the user to close it, then:
|
|
113
151
|
```
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
152
|
+
thatopen revit install
|
|
153
|
+
```
|
|
154
|
+
This fetches the current package from the registry and runs the installer that ships inside it.
|
|
155
|
+
`--version <v>` pins a specific one; the default is the latest.
|
|
156
|
+
|
|
157
|
+
Then **launch Revit again**. On the first start after an install, Revit shows an **unsigned add-in**
|
|
158
|
+
prompt. **Only the user can answer it — ask them to choose "Always Load".** There is no way around
|
|
159
|
+
it from a script, and it comes back after every update until the add-in is signed.
|
|
160
|
+
|
|
161
|
+
Re-run `thatopen revit status` every ten seconds or so until `"loaded": true`.
|
|
119
162
|
|
|
120
163
|
---
|
|
121
164
|
|
|
122
165
|
## Step 3 — Do the action
|
|
123
166
|
|
|
124
167
|
### (A) Share a model — `inspect`, then `publish-central`
|
|
125
|
-
|
|
168
|
+
|
|
126
169
|
```
|
|
127
170
|
thatopen revit inspect --file "<FILE>"
|
|
128
171
|
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
172
|
+
|
|
173
|
+
- **`"isCentral": true`** → already a central. Tell the user you will upload a copy and their
|
|
174
|
+
original is untouched, then:
|
|
132
175
|
```
|
|
133
176
|
thatopen revit publish-central --project <PROJECT> --doc <DOC> --file "<FILE>"
|
|
134
177
|
```
|
|
135
|
-
- **`"isCentral": false`** →
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Only after the user agrees, publish **with `--convert`**:
|
|
178
|
+
- **`"isCentral": false`** → a plain model. **Ask for consent first:** *"This file isn't a shared
|
|
179
|
+
central yet. To share it I need to turn it into one. I'll do that on a COPY, so your original
|
|
180
|
+
stays exactly as it is. Shall I go ahead?"* Only then:
|
|
139
181
|
```
|
|
140
182
|
thatopen revit publish-central --project <PROJECT> --doc <DOC> --file "<FILE>" --convert
|
|
141
183
|
```
|
|
142
184
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
modified.` Tell teammates to connect with the **project folder id** shown in the dashboard,
|
|
147
|
-
or with `thatopen revit join --project <PROJECT> --doc <DOC>`.
|
|
148
|
-
|
|
149
|
-
> If you skip `inspect` and the file isn't a central, `publish-central` will refuse with a
|
|
150
|
-
> message telling you to re-run with `--convert` — still ask the user first.
|
|
185
|
+
Publishing uploads the central and opens **the user's own local** in Revit. Success ends with
|
|
186
|
+
`Published (…). Central: … (version N). Your original file was not modified.` Teammates connect with
|
|
187
|
+
the folder id from the dashboard, or with `--project` and `--doc`.
|
|
151
188
|
|
|
152
189
|
### (B) Connect to a central — `join`
|
|
153
|
-
|
|
190
|
+
|
|
154
191
|
```
|
|
155
|
-
thatopen revit join --folder-id <FOLDER_ID>
|
|
156
|
-
thatopen revit join --path "
|
|
192
|
+
thatopen revit join --folder-id <FOLDER_ID>
|
|
193
|
+
thatopen revit join --path "C:\ThatOpenShared\<project>\<name>\<file>.rvt"
|
|
157
194
|
```
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Success: `Joined. Your local was created and opened in Revit: …`.
|
|
195
|
+
`--project <PROJECT> --doc <DOC>` also works when the user knows both. This downloads the central,
|
|
196
|
+
creates the user's local, and opens it.
|
|
161
197
|
|
|
162
198
|
### (C) Already have a local open — `sync`
|
|
199
|
+
|
|
163
200
|
```
|
|
164
201
|
thatopen revit sync
|
|
165
202
|
```
|
|
166
|
-
|
|
203
|
+
|
|
204
|
+
### (D) Review a proposal
|
|
205
|
+
|
|
206
|
+
Go to **Step 6**.
|
|
167
207
|
|
|
168
208
|
---
|
|
169
209
|
|
|
170
210
|
## Step 4 — Work, then sync
|
|
171
|
-
|
|
172
|
-
|
|
211
|
+
|
|
212
|
+
Tell the user to **model normally**. To send their changes and pull the team's, they use **Revit's
|
|
213
|
+
own Synchronize with Central** on the Collaborate tab, exactly as on any worksharing job. The add-in
|
|
214
|
+
listens for it: it takes a turn in the team queue and brings the central up to date *before* Revit
|
|
215
|
+
merges, then publishes the result. A corner notice reports each step and ends with
|
|
216
|
+
`Synced. vN → vM.`
|
|
217
|
+
|
|
218
|
+
There is no "sync to team" button, on purpose. Two buttons that must mean the same thing are a
|
|
219
|
+
question nobody should have to answer twice a day.
|
|
220
|
+
|
|
221
|
+
For a script or an agent, the same round trip is `thatopen revit sync`.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Step 5 — Worksets, so two people don't edit the same thing
|
|
226
|
+
|
|
227
|
+
**Claiming one is Revit's own dialog:** Collaborate → Worksets, tick *Editable* on the row you want.
|
|
228
|
+
Same dialog, same rules and same messages as any Revit job. Ownership reaches everybody within
|
|
229
|
+
seconds, because the add-in replicates the worksharing files the central keeps beside it and each
|
|
230
|
+
Revit re-reads them by itself.
|
|
231
|
+
|
|
232
|
+
Only **one person at a time** owns a workset, and **Revit** enforces it: a teammate who tries to
|
|
233
|
+
take what you hold is told who holds it. That also means only *you* can give yours back — there is
|
|
234
|
+
no API that relinquishes on somebody else's behalf, so "ask them to release it" is the real answer
|
|
235
|
+
rather than a workaround.
|
|
236
|
+
|
|
237
|
+
**Release** by relinquishing in Revit, or simply by syncing, which relinquishes as part of the
|
|
238
|
+
dialog's terms.
|
|
239
|
+
|
|
240
|
+
From a script:
|
|
173
241
|
```
|
|
174
|
-
thatopen revit
|
|
242
|
+
thatopen revit worksets # who holds what
|
|
243
|
+
thatopen revit take --workset "<NAME>"
|
|
244
|
+
thatopen revit untake --workset "<NAME>"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
> Editing an element makes you its **borrower** automatically, element by element. Taking a workset
|
|
248
|
+
> makes you its **owner**. Revit only blocks editing worksets owned by **somebody else**; free ones
|
|
249
|
+
> are borrowed on demand.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Step 6 — Proposals
|
|
254
|
+
|
|
255
|
+
A proposal is a change authored **outside** this Revit: a set of `add`, `set` and `remove` entries
|
|
256
|
+
naming elements by their Revit UniqueId, carried in a `.frag` and queued against one document. It is
|
|
257
|
+
a request, not an edit. Until a person applies it, nothing in the model has moved.
|
|
258
|
+
|
|
259
|
+
**Who writes them is deliberately open.** A Rhino plug-in, a cloud component, a web app, a script:
|
|
260
|
+
anything that can produce the format can queue one. This package's job starts when one arrives,
|
|
261
|
+
which is why nothing below asks where it came from.
|
|
262
|
+
|
|
263
|
+
If the user asks how to send changes *from* their own tool — or you are being asked to write that
|
|
264
|
+
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
|
|
265
|
+
contract, and **[the architecture notes](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-architecture.md)** say why it is shaped that way.
|
|
266
|
+
Publishing a proposal is the smallest of the three jobs described there: it needs no ownership, no
|
|
267
|
+
history, and no collaboration layer.
|
|
268
|
+
|
|
269
|
+
If the tool is **Rhino**, the whole path is already written and worked through end to end:
|
|
270
|
+
**[Rhino → Revit, start to finish](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/rhino-interop-quickstart.md)**. Read that
|
|
271
|
+
instead of assembling it from the two documents above — it covers installing the plug-in, driving
|
|
272
|
+
Rhino without a keyboard, and the traps that cost a night.
|
|
273
|
+
|
|
274
|
+
**This is where you use the add-in's local API** — the CLI has no proposal commands yet.
|
|
275
|
+
|
|
276
|
+
**List what is waiting:**
|
|
277
|
+
```
|
|
278
|
+
POST /ops → { ok, ops: [ { OpId, Title, Author, State, BaseCommit, AffectedIds } ] }
|
|
175
279
|
```
|
|
176
|
-
|
|
177
|
-
|
|
280
|
+
`State` is `pending`, `applied` or `discarded`. Show the user the pending ones by title and author.
|
|
281
|
+
|
|
282
|
+
**Check one before touching anything.** This is read-only; no transaction is opened:
|
|
283
|
+
```
|
|
284
|
+
POST /op-check { "opId": "op-…" }
|
|
285
|
+
```
|
|
286
|
+
Read three things out of the answer, and tell the user all three:
|
|
287
|
+
|
|
288
|
+
- `plan.canApply` — whether it can be applied right now.
|
|
289
|
+
- `plan.blocker` — if not, why, phrased as the next thing to do. Read it out rather than paraphrase
|
|
290
|
+
it. It names the person to go and ask when a workset is held by somebody, and it points at Revit's
|
|
291
|
+
own dialog when the workset is merely free. "You need permissions" loses both.
|
|
292
|
+
- `authoredAgainst`, `youAreOn`, `behind` — the version the proposal was written against and the one
|
|
293
|
+
the model is on now. **A proposal does not expire and is not blocked for being old**: it says
|
|
294
|
+
*move this by two metres*, not *put it at this coordinate*, so it still means what it said. But
|
|
295
|
+
when `behind` is present, say so before the user approves. The model has moved underneath it.
|
|
296
|
+
|
|
297
|
+
**Expect this one, it is not a bug:** an `add` lands on the document's **active** workset, so
|
|
298
|
+
whoever applies has to hold *that* one — often `Workset1`, which nobody thinks of as theirs. If the
|
|
299
|
+
check blocks on a workset nobody seems to be using, that is why.
|
|
300
|
+
|
|
301
|
+
**Apply it,** once the user has said yes:
|
|
302
|
+
```
|
|
303
|
+
POST /apply-ops { "opId": "op-…" }
|
|
304
|
+
```
|
|
305
|
+
Everything in the proposal happens in **one** Revit transaction: one undo, one entry in the history.
|
|
306
|
+
Afterwards the entry's `State` is `applied` and `AffectedIds` lists what it touched, in the order the
|
|
307
|
+
proposal named them — which is how an authoring tool learns which Revit element corresponds to which
|
|
308
|
+
of its own objects. The change is in the local only; the user still syncs (Step 4) to publish it.
|
|
309
|
+
|
|
310
|
+
**Reject it:**
|
|
311
|
+
```
|
|
312
|
+
POST /op-discard { "opId": "op-…" }
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Verify with coordinates, not with the word "applied".** After applying, `POST /where` with
|
|
316
|
+
`{ "uids": [ … ] }` gives each element's position in metres. If a proposal said it moved four
|
|
317
|
+
things, check that four things moved. "Applied" and "applied to three of the four" read identically
|
|
318
|
+
in a status field and differently in a model.
|
|
178
319
|
|
|
179
320
|
---
|
|
180
321
|
|
|
181
|
-
## Step
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
322
|
+
## Step 7 — Offer them a viewer that shows the progress
|
|
323
|
+
|
|
324
|
+
**Offer this as soon as the central exists — right after Step 3, before they start working.** Not
|
|
325
|
+
at the end. Every sync from that moment on becomes a commit they can watch arrive, and setting it up
|
|
326
|
+
costs the same on day one as on day thirty. Left to the end it sounds like an extra; offered at the
|
|
327
|
+
start it is the thing that makes the rest visible.
|
|
328
|
+
|
|
329
|
+
Every sync is a commit, and the platform keeps the lot: who changed what, when, and the geometry as
|
|
330
|
+
it was at each version. Nothing in Revit shows that back, and it is the first thing people ask —
|
|
331
|
+
*what has changed since Friday?*
|
|
332
|
+
|
|
333
|
+
Say it plainly and wait:
|
|
334
|
+
|
|
335
|
+
> "Everything you sync is stored as a history. Would you like a viewer that shows it — pick any
|
|
336
|
+
> version and see what was created, modified and deleted, coloured in 3D?"
|
|
337
|
+
|
|
338
|
+
If they say yes, that is an **app**, and building one is
|
|
339
|
+
**[the platform AI quick start](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/ai-quickstart.md)** — scaffold with the `app` template and follow
|
|
340
|
+
it.
|
|
341
|
+
|
|
342
|
+
**Do not ask them how to add the panel.** Add a **new layout** called `History`, and leave the
|
|
343
|
+
scaffold's own layouts alone. It is a two-line change, it keeps the app they were given intact, and
|
|
344
|
+
it is the answer every time — asking hands somebody a UI decision about a screen they have not seen
|
|
345
|
+
yet.
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
app.elements = {
|
|
349
|
+
...app.elements,
|
|
350
|
+
history: () => html`<top-git-history></top-git-history>`,
|
|
351
|
+
};
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**For the layout, copy one of the scaffold's own panel layouts and swap its panel for `history`** —
|
|
355
|
+
same side, same width, same shape. Do not choose a side and do not pick a width: the app already
|
|
356
|
+
answered both, and the answer is written in `app.layouts` right there in `main.ts`.
|
|
357
|
+
|
|
358
|
+
A panel that opens on the opposite side from every other one, at a different size, is the first
|
|
359
|
+
thing anybody notices, and it reads as bolted on however good the contents are.
|
|
360
|
+
|
|
361
|
+
For the layout's `icon`, use **`at-icons:git-branch`** — That Open's own icon set, and the branch
|
|
362
|
+
mark is what the panel actually shows.
|
|
363
|
+
|
|
364
|
+
What makes it a history viewer rather than an empty viewer is a built-in component that already
|
|
365
|
+
exists, so nobody writes this part:
|
|
366
|
+
|
|
367
|
+
- **`GitHistoryManager`** — the headless half. It reads `revitflow_history.json` and the per-commit
|
|
368
|
+
`revitflow_frag_<N>.frag` files the add-in writes under `bimterop/revit-<docId>/`, loads the
|
|
369
|
+
model, and colours a commit's elements: **green created, blue modified, red deleted**. It has an
|
|
370
|
+
`isolateChanges` mode that ghosts everything else, which is the one people keep on.
|
|
371
|
+
- **`top-git-history`** — the panel, registered by `UIManager`. It only asks the manager for things
|
|
372
|
+
and listens; all the viewer work is on the manager.
|
|
373
|
+
|
|
374
|
+
Two things to say honestly when you offer it. The history only goes back as far as the team's first
|
|
375
|
+
sync through this add-in — it is not read out of the `.rvt`. And a commit's colours come from what
|
|
376
|
+
the add-in recorded at sync time, so a change somebody made and never synced is not in it, the same
|
|
377
|
+
way it is not in anybody else's model.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Talking to the add-in directly
|
|
382
|
+
|
|
383
|
+
The add-in serves a small HTTP API on **127.0.0.1**, and it is the same surface the CLI drives: a
|
|
384
|
+
person clicking and a script deciding are the same operation. Use it for what the CLI does not
|
|
385
|
+
cover. Do not use it to skip a CLI command that exists.
|
|
386
|
+
|
|
387
|
+
**Find it.** The port and the token change every launch, so read them rather than configuring them:
|
|
187
388
|
|
|
188
|
-
**See the worksets and who owns each:**
|
|
189
389
|
```
|
|
190
|
-
|
|
390
|
+
%APPDATA%\ThatOpen\revit-addin.json → { "port": …, "token": "…", "pid": … }
|
|
191
391
|
```
|
|
192
392
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
1. `thatopen revit take --workset "<NAME>"` (or the Take workset button)
|
|
209
|
-
2. `thatopen revit sync` ← you push the ownership
|
|
210
|
-
3. teammates `thatopen revit sync` ← now Revit blocks them from editing your workset
|
|
211
|
-
|
|
212
|
-
Until teammates have synced, they could still auto-borrow an element in that workset by
|
|
213
|
-
editing it directly. After the round, they'll see it as owned by you and be blocked.
|
|
214
|
-
|
|
215
|
-
**Release when you're done** so teammates can take it:
|
|
216
|
-
- **Ribbon:** click **"Release worksets"** (frees everything you own).
|
|
217
|
-
- **CLI:** `thatopen revit untake --workset "<NAME>"`
|
|
218
|
-
Then `sync` so the release propagates.
|
|
219
|
-
|
|
220
|
-
> Note: simply editing an element makes you its **borrower** automatically (element-level);
|
|
221
|
-
> **taking a workset** makes you the **owner** (workset-level). Revit only blocks editing
|
|
222
|
-
> worksets owned by **someone else** — so the Take→Sync→Sync round above is what creates the
|
|
223
|
-
> protection.
|
|
393
|
+
Send the token as the header **`X-RevitFlow-Token`**, POST JSON, read JSON back. The endpoints this
|
|
394
|
+
guide uses are `status`, `ops`, `op-check`, `apply-ops`, `op-discard`, `where` and `worksets`.
|
|
395
|
+
|
|
396
|
+
Three things that will otherwise mislead you:
|
|
397
|
+
|
|
398
|
+
- **`worksets` has `revitOwner` and `tocOwner`, and no field called `owner`.** Reading `owner`
|
|
399
|
+
returns nothing for every row, which looks exactly like "nobody owns anything" and is a lie.
|
|
400
|
+
`revitOwner` is Revit's answer, and Revit's answer is the one that decides.
|
|
401
|
+
- **The add-in serves one request at a time.** A machine in the middle of a sync answers nothing,
|
|
402
|
+
and from outside that is indistinguishable from a crash. Diagnose from
|
|
403
|
+
`%APPDATA%\ThatOpen\revitflow.log`, which keeps being written throughout. Never conclude anything
|
|
404
|
+
from a timeout alone.
|
|
405
|
+
- **A modal dialog in Revit stops everything.** The add-in does its work when Revit is idle, and a
|
|
406
|
+
dialog waiting for a click means Revit never is. If a call hangs, ask the user to look at Revit's
|
|
407
|
+
window before assuming something is broken.
|
|
224
408
|
|
|
225
409
|
---
|
|
226
410
|
|
|
227
411
|
## Troubleshooting
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
`--api-url
|
|
232
|
-
- **
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
-
|
|
237
|
-
|
|
238
|
-
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
-
|
|
246
|
-
|
|
247
|
-
- **A
|
|
248
|
-
|
|
249
|
-
|
|
412
|
+
|
|
413
|
+
- **`thatopen login` fails with "Unauthorized"** → the token is invalid, expired, or from the other
|
|
414
|
+
environment. Production tokens use no `--api-url`; dev tokens use
|
|
415
|
+
`--api-url https://dev.platform.thatopen.com`. Ask for a fresh one from the same environment.
|
|
416
|
+
- **HTTP 403 on `item/folder?projectId=…`** → login worked, but that account has no access to that
|
|
417
|
+
Project ID. Confirm the id and the environment with the user.
|
|
418
|
+
- **"add-in not running" while Revit is open** → it is not installed. Step 2(b).
|
|
419
|
+
- **`thatopen revit install` refuses** → Revit is still open. Close it.
|
|
420
|
+
- **`take` answers `taken: false` with `deniedBy`** → somebody else holds that workset. Expected,
|
|
421
|
+
not an error. Ask them to release and sync, or take another.
|
|
422
|
+
- **`take` answers `offline: true`** → the platform is unreachable. The take is **refused, not
|
|
423
|
+
queued**, and deliberately: the platform is what arbitrates the race, so with it gone nobody can
|
|
424
|
+
know whether a colleague already holds it. Nothing was changed. Retry when `status` says
|
|
425
|
+
`online: true`.
|
|
426
|
+
- **`locks` answers `offline: true` instead of a list** → the same outage. That is **not** an empty
|
|
427
|
+
list; claims still stand. `worksets` still answers from Revit with `registry: false`, and that
|
|
428
|
+
half needs no network at all — use it to see what the user themselves holds.
|
|
429
|
+
- **A teammate can still edit "my" workset** → ownership has not reached them yet. You must sync
|
|
430
|
+
after taking it, and they must sync afterwards.
|
|
431
|
+
- **A proposal blocks on a workset nobody seems to own** → it is probably the **active** workset,
|
|
432
|
+
needed by its `add` entries. See Step 6.
|
|
433
|
+
- **Everything hangs and nothing new is in the log** → a Revit dialog is waiting for a click.
|
|
434
|
+
|
|
435
|
+
---
|
|
250
436
|
|
|
251
437
|
## Rules recap (for the AI)
|
|
252
|
-
|
|
253
|
-
- Never
|
|
254
|
-
-
|
|
255
|
-
|
|
256
|
-
|
|
438
|
+
|
|
439
|
+
- Ask for every value you do not have. Never guess a project id, a file path or a central name.
|
|
440
|
+
- Never echo or store the token.
|
|
441
|
+
- You may install the CLI and the add-in and launch Revit. Only the "Always Load" prompt needs the
|
|
442
|
+
user — and so does every decision about applying a proposal.
|
|
443
|
+
- CLI first; the add-in's local API for proposals and for reading ownership; never the platform's
|
|
444
|
+
own HTTP API by hand.
|
|
445
|
+
- Read each answer before the next step, and prefer a measured value to a status word: coordinates
|
|
446
|
+
over "applied", `revitOwner` over "it should be fine".
|