fvtt-world-cli 1.2.0 → 1.3.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/dist/index.js +645 -167
- package/docs/commands.md +18 -9
- package/docs/compatibility.md +2 -0
- package/docs/protocol.md +45 -15
- package/package.json +2 -2
- package/skills/foundry-world-editor/SKILL.md +25 -12
- package/skills/foundry-world-editor/SKILL.md.sha256 +1 -1
package/docs/commands.md
CHANGED
|
@@ -17,8 +17,9 @@ becomes `scene token get`. Nested commands require the parent IDs as well as the
|
|
|
17
17
|
|
|
18
18
|
With a bridge connected, discovery omits denied commands and marks approval requirements with
|
|
19
19
|
`approval: true`. If the daemon or bridge is unavailable, it returns the static registry with
|
|
20
|
-
`policy.applied: false
|
|
21
|
-
`schema` and `--help` always
|
|
20
|
+
`policy.applied: false`; `policy` sits at the top level of the response, beside `result`.
|
|
21
|
+
Authentication and protocol errors do not trigger that fallback. `schema` and `--help` always
|
|
22
|
+
describe the static registry, including denied commands.
|
|
22
23
|
|
|
23
24
|
## Capability map
|
|
24
25
|
|
|
@@ -27,13 +28,13 @@ Use these families to narrow your search, then check their help for supported op
|
|
|
27
28
|
| Task | Command families |
|
|
28
29
|
|---|---|
|
|
29
30
|
| Edit world content | `actor`, `item`, `journal`, `scene`, `macro`, `playlist`, `table`, `cards` |
|
|
30
|
-
| Edit embedded content | Nested families such as `actor.item`, `playlist.sound`, `table.result`, and `*.effect` |
|
|
31
|
+
| Edit embedded content | Nested families such as `actor.item`, `journal.page`, `playlist.sound`, `table.result`, and `*.effect` |
|
|
31
32
|
| Edit scene placeables | `scene.token`, `scene.wall`, `scene.region`, and related families |
|
|
32
33
|
| Edit scene levels (v14) | `scene.level` |
|
|
33
34
|
| Manage encounters | `combat`, `combat.combatant`, `combat.group` |
|
|
34
35
|
| Organize content and access | `folder`, `*.ownership.set`, `user` |
|
|
35
36
|
| Manage chat and settings | `chat`, `setting` |
|
|
36
|
-
| Find content or missing assets | `world.search`, `world.audit-files` |
|
|
37
|
+
| Find content, references, or missing assets | `world.search`, `world.references`, `world.audit-files` |
|
|
37
38
|
| Read packs or import world documents | `compendium`, supported `*.import-from-compendium` commands |
|
|
38
39
|
| Manage assets | `file` |
|
|
39
40
|
| Control playback and gameplay | Actions within `playlist`, `table`, `cards`, `combat`, `scene`, and `game` |
|
|
@@ -164,13 +165,15 @@ Use JSON flags such as `--data-json` and `--patch-json` for structured values. U
|
|
|
164
165
|
|
|
165
166
|
- Nested objects merge recursively.
|
|
166
167
|
- Ordinary arrays replace as a whole.
|
|
167
|
-
- Dotted paths target object properties where permitted; dotted
|
|
168
|
-
|
|
168
|
+
- Dotted paths target object properties where permitted; a top-level dotted write into an array
|
|
169
|
+
field, such as `c.0`, is refused.
|
|
170
|
+
- Foundry's operator keys work at any permitted nested level: `{"-=key": null}` deletes the key,
|
|
171
|
+
and `{"==key": value}` replaces the key's whole value instead of merging into it.
|
|
169
172
|
- Embedded collections follow their family's Foundry semantics.
|
|
170
173
|
|
|
171
174
|
Read arrays before editing and preserve unchanged values. For a wall's `c` field, send the complete
|
|
172
|
-
`[x1, y1, x2, y2]`, not `c.0`.
|
|
173
|
-
mutation, including in bulk writes and dry runs. Correct the fields identified by the error.
|
|
175
|
+
`[x1, y1, x2, y2]`, not `c.0`. Array writes that Foundry would discard return `INVALID_PARAMS`
|
|
176
|
+
before mutation, including in bulk writes and dry runs. Correct the fields identified by the error.
|
|
174
177
|
|
|
175
178
|
Prefer dedicated commands for creating embedded documents. A parent update that adds entries
|
|
176
179
|
without `_id`, such as new `behaviors` in `scene.region.update`, can create them but return an
|
|
@@ -190,6 +193,12 @@ A timeout or disconnect can happen after a write commits. An update error with `
|
|
|
190
193
|
or `details.indeterminate` can also mean that some changes persisted. Read affected state before
|
|
191
194
|
retrying and send only what remains to be done.
|
|
192
195
|
|
|
196
|
+
An update whose stored state differs from the request fails with `INTERNAL_ERROR`, and
|
|
197
|
+
`details.paths` lists the differing paths with the requested and the stored value. Foundry core, the
|
|
198
|
+
game system or a module may have changed the value; Foundry stores a raw `&` in rich text as
|
|
199
|
+
`&`, for example. The bridge cannot tell which, so the caller judges from the values. Sending
|
|
200
|
+
the same patch again stores the same result.
|
|
201
|
+
|
|
193
202
|
An idempotency key identifies one logical request and can prevent duplicate effects while its
|
|
194
203
|
cached state survives. Reuse it only for the same request; a corrected or remaining patch is a
|
|
195
204
|
new operation and needs a fresh key. Follow the [delivery rules](protocol.md#delivery-states-and-retries)
|
|
@@ -268,7 +277,7 @@ For scripts and JSON output, pass `--yes` to skip the listing and prompt.
|
|
|
268
277
|
## Local configuration
|
|
269
278
|
|
|
270
279
|
`config get` shows the config path and non-secret settings. `config set-upload-limit <size>`
|
|
271
|
-
changes the raw
|
|
280
|
+
changes the raw byte limit for uploads and file reads; it takes effect after a daemon restart.
|
|
272
281
|
|
|
273
282
|
If set, `XDG_CONFIG_HOME` places configuration under `$XDG_CONFIG_HOME/fvtt-world-cli`. Otherwise:
|
|
274
283
|
|
package/docs/compatibility.md
CHANGED
|
@@ -11,6 +11,8 @@ The bridge supports Foundry VTT v13 and v14. Check your connected version with
|
|
|
11
11
|
| Scene thumbnail rendering | Renders the scene | Renders the scene's initial level by default | On v14, expect the initial level in the thumbnail. Check the returned dimensions and image. |
|
|
12
12
|
| Scene thumbnail files | Uses a stable scene-based filename | Uses a filename based on the image content | Use the returned path. On v14, a changed image can leave an older thumbnail file behind. |
|
|
13
13
|
| User permission names | `TEMPLATE_CREATE` | `REGION_CREATE` | Use the permission keys reported by the connected Foundry version when calling `user.permissions.set`. |
|
|
14
|
+
| Relative content links | `@UUID[..<id>]` points at nothing | `@UUID[..<id>]` names a document one parent up | `world.references` reports a `..` link only on v14. |
|
|
15
|
+
| Legacy update operators `-=key` (deletion) and `==key` (forced replacement) | Work without a warning | Work, and the GM browser console logs a deprecation warning for each such key, stating that support will be removed in Version 16 | Nothing changes for v13 or v14 writes. The bridge passes the operator keys through without translating them to Foundry 14's operator objects, so they will stop working once Foundry removes them. |
|
|
14
16
|
|
|
15
17
|
Scene placeable fields and available region behavior types also differ. Read the target document
|
|
16
18
|
before preparing a patch; valid data from one version is not automatically valid on the other.
|
package/docs/protocol.md
CHANGED
|
@@ -27,12 +27,13 @@ a correlated control-request error.
|
|
|
27
27
|
|
|
28
28
|
## Size limits
|
|
29
29
|
|
|
30
|
-
The upload limit and the transport frame limit are distinct: the first bounds raw upload content
|
|
31
|
-
the second accounts for encoding and envelope overhead and is
|
|
32
|
-
large read responses require. The daemon advertises its effective
|
|
33
|
-
the bridge checks response size before sending, so an oversized
|
|
34
|
-
`PAYLOAD_TOO_LARGE` instead of destroying the
|
|
35
|
-
applied to the transport only after the daemon
|
|
30
|
+
The upload limit and the transport frame limit are distinct: the first bounds raw upload content
|
|
31
|
+
and the raw size of a `file.read`, the second accounts for encoding and envelope overhead and is
|
|
32
|
+
never lower than what legitimate large read responses require. The daemon advertises its effective
|
|
33
|
+
limits during the handshake, and the bridge checks response size before sending, so an oversized
|
|
34
|
+
response, or one the browser cannot serialize, returns `PAYLOAD_TOO_LARGE` instead of destroying the
|
|
35
|
+
shared session. A persisted upload-limit change is applied to the transport only after the daemon
|
|
36
|
+
restarts.
|
|
36
37
|
|
|
37
38
|
## Pairing
|
|
38
39
|
|
|
@@ -181,9 +182,17 @@ filters apply before pagination. Previewed documents have no persistent identity
|
|
|
181
182
|
observed during a preview must not be reused. Some result fields depend on the connected Foundry
|
|
182
183
|
version; [Foundry compatibility](compatibility.md) lists them.
|
|
183
184
|
|
|
185
|
+
World document reads carry `compendiumSource` and `duplicateSource`, hoisted from Foundry's
|
|
186
|
+
`_stats` and `null` when absent.
|
|
187
|
+
|
|
184
188
|
Broadcast actions report `dispatched` and target users rather than a confirmed player-side result.
|
|
185
189
|
The active/inactive split is included where knowable.
|
|
186
190
|
|
|
191
|
+
A setting value or macro return value node with no JSON form, such as a function, an own accessor
|
|
192
|
+
property or a circular reference, is replaced by `{"$unserializable": "<reason>"}` and listed with
|
|
193
|
+
its path in an `...Omissions` field. A value with omissions is incomplete; writing it back stores the
|
|
194
|
+
markers as data.
|
|
195
|
+
|
|
187
196
|
### Macro results
|
|
188
197
|
|
|
189
198
|
`macro.execute` returns the macro's value and observed chat messages. `chatCapture` describes the
|
|
@@ -196,10 +205,12 @@ world state when the return value does not establish the effect.
|
|
|
196
205
|
|
|
197
206
|
### Setting results
|
|
198
207
|
|
|
199
|
-
Setting writes return the observed `value` and its `previous` value
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
Setting writes return the observed `value` and its `previous` value, as `game.settings.get`
|
|
209
|
+
returns them after the registered type's cast. Confirmation compares the JSON Foundry stores with
|
|
210
|
+
the JSON of the validated request; a different stored value, for example one rewritten by an
|
|
211
|
+
`onChange` callback, is a mismatch error with a `paths` entry for `value`. Writing an
|
|
212
|
+
already-stored value succeeds as unchanged without calling Foundry. `requiresReload: true`
|
|
213
|
+
indicates that the GM client needs a reload.
|
|
203
214
|
|
|
204
215
|
## Write confirmation
|
|
205
216
|
|
|
@@ -207,11 +218,24 @@ Document writes report success only after confirmation. Foundry can resolve a ve
|
|
|
207
218
|
write without throwing, and hooks can remove or rewrite part of a patch. Single and bulk updates
|
|
208
219
|
pass Foundry a private copy, then compare stored state with the original request.
|
|
209
220
|
|
|
221
|
+
Confirmation compares each requested leaf path that survives Foundry's cleaning with the stored
|
|
222
|
+
source. Foundry drops a key the document's schema does not declare without an error before that
|
|
223
|
+
comparison, so a write made only of undeclared keys reports success with nothing stored. A deletion (`-=key` or
|
|
224
|
+
Foundry 14's `ForcedDeletion`) is confirmed when the key is absent. A forced replacement (`==key`
|
|
225
|
+
or Foundry 14's `ForcedReplacement`) is confirmed when the whole stored value at that path equals the requested
|
|
226
|
+
value after Foundry's cleaning.
|
|
227
|
+
|
|
210
228
|
A valid patch whose requested state is already stored remains a successful no-op. A validation
|
|
211
229
|
failure recovered after an unwritten update returns `INVALID_PARAMS`. Unconfirmed, partial, or
|
|
212
230
|
indeterminate updates can return `INTERNAL_ERROR` with these details:
|
|
213
231
|
|
|
214
|
-
- `fields`: requested fields whose state was not confirmed.
|
|
232
|
+
- `fields`: requested top-level fields whose state was not confirmed.
|
|
233
|
+
- `paths`: up to 20 entries `{ path, requested, stored }`, one per leaf path where the stored value
|
|
234
|
+
differs from the request. `absent: "stored"` replaces `stored` when the key is missing from the
|
|
235
|
+
stored source, and `absent: "requested"` replaces `requested` for a deletion that did not land.
|
|
236
|
+
A long string, or an object or array whose JSON is long, is shortened to a window around the
|
|
237
|
+
first differing character; `truncated` then carries `format` (`text` or `json`), `offset`, and
|
|
238
|
+
the full `requestedLength` and `storedLength`. `pathsTruncated` counts the entries beyond 20.
|
|
215
239
|
- `partial: true`: stored data changed in a requested field, but the full requested state was
|
|
216
240
|
not reached. A hook may have applied only part of a nested object or replaced a value.
|
|
217
241
|
- `changedFields`: requested top-level fields observed to change, including fields that changed
|
|
@@ -221,6 +245,12 @@ indeterminate updates can return `INTERNAL_ERROR` with these details:
|
|
|
221
245
|
- `indeterminate: true`: confirmation could not establish the outcome. Some or all of the write
|
|
222
246
|
may have persisted. `changedFields` is `null` when the before/after comparison was unavailable.
|
|
223
247
|
|
|
248
|
+
When stored differs from requested, the message says that the bridge cannot verify whether Foundry
|
|
249
|
+
core, the game system or a module normalized the value, or whether a hook rewrote, dropped or vetoed
|
|
250
|
+
the change. A blocking module is listed as one possible cause, never as the diagnosis. Resending the
|
|
251
|
+
same patch does not change the result. Every confirmed write that can report a mismatch carries the
|
|
252
|
+
same `paths`, and so does a `dropped` bulk outcome when the stored state was compared.
|
|
253
|
+
|
|
224
254
|
Parent patches that create embedded entries without `_id`, such as new `behaviors` in
|
|
225
255
|
`scene.region.update`, return an indeterminate error after writing because parent update results
|
|
226
256
|
cannot confirm those creations. A retry may duplicate them. Read the embedded collection first,
|
|
@@ -231,10 +261,10 @@ new operation with a fresh idempotency key if using one.
|
|
|
231
261
|
|
|
232
262
|
### Patch shape checks
|
|
233
263
|
|
|
234
|
-
Single updates, bulk updates, and dry runs reject
|
|
235
|
-
array
|
|
236
|
-
|
|
237
|
-
|
|
264
|
+
Single updates, bulk updates, and dry runs reject invalid or discarded array values, top-level
|
|
265
|
+
dotted writes into array fields, and writes to legacy fields the running core moved, with
|
|
266
|
+
`INVALID_PARAMS` before mutation. Send whole arrays; ordinary dotted object-property writes remain
|
|
267
|
+
supported where the schema permits them. Other key spellings reach Foundry as written.
|
|
238
268
|
|
|
239
269
|
Errors identify the `field` and may include `arrayField`, `requested`, `stored`, or migration
|
|
240
270
|
destination details. Bulk failures add the entry's `index` and `id`; single failures use document
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fvtt-world-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Safe local CLI and authenticated bridge for automating live Foundry VTT worlds.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foundry-vtt",
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"zod": "^3.24.3"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@fvtt-world-cli/protocol": "1.
|
|
59
|
+
"@fvtt-world-cli/protocol": "1.3.0"
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -19,10 +19,11 @@ fvtt-world-cli system info --json
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Proceed only when `.result.bridge.status` is `connected`, and verify the reported world and GM are
|
|
22
|
-
the intended targets. If
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`fvtt-world-cli` module
|
|
22
|
+
the intended targets. If `system info` answers at all, a daemon is running; use it. Start
|
|
23
|
+
`fvtt-world-cli bridge serve` only when the daemon is unreachable, as a persistent background
|
|
24
|
+
process (it does not daemonize itself), and leave it running for the whole session. If no bridge
|
|
25
|
+
connects, a GM must have the target world open in a browser with the `fvtt-world-cli` module
|
|
26
|
+
enabled and paired.
|
|
26
27
|
|
|
27
28
|
Pairing is a first-run trust decision that needs the human: ask the GM to open Module Settings →
|
|
28
29
|
Authorization, name the browser in the `Browser label` field, and choose Pair; run
|
|
@@ -80,6 +81,12 @@ level ids in a preview are provisional.
|
|
|
80
81
|
Deleting a level also deletes its tokens and every placeable only on that level, and the dry run
|
|
81
82
|
does not list them: read the scene's placeables (token `level`, others' `levels`) and tell the user
|
|
82
83
|
what the delete removes before running it.
|
|
84
|
+
An unlinked token keeps its own overrides, which edits to its world actor never reach. Before
|
|
85
|
+
editing an actor that has placed tokens, read its tokens' overrides
|
|
86
|
+
(`scene token get --include delta,prepared`) and tell the user which tokens the edit will not
|
|
87
|
+
change.
|
|
88
|
+
Before deleting or relinking a document, run `world references --uuid <uuid>` and tell the user
|
|
89
|
+
which stored references will point at nothing.
|
|
83
90
|
`setting.set` cannot touch this module's own namespace (`SETTING_PROTECTED` is final; never retry).
|
|
84
91
|
When a setting write returns `requiresReload: true`, the change needs a GM-client reload to take
|
|
85
92
|
effect; `system.reload` performs it, drops the bridge, and requires a reconnect wait before the
|
|
@@ -92,12 +99,13 @@ signal, not a failure — reconnect and continue rather than retrying the reload
|
|
|
92
99
|
Discover → inspect schema → locate → read → smallest patch → dry-run → commit → verify. Never
|
|
93
100
|
guess a command name or parameter.
|
|
94
101
|
|
|
95
|
-
1. Run `fvtt-world-cli commands --json`. Continue only when `policy.applied`
|
|
96
|
-
required command is present. A `false` value means no bridge
|
|
97
|
-
registry, not the client's permissions. Report an absent
|
|
98
|
-
to produce the same effect. Warn the user before a
|
|
99
|
-
one `*-many` command when it can replace several
|
|
100
|
-
CLI subcommands, such as `actor.item.update` to
|
|
102
|
+
1. Run `fvtt-world-cli commands --json`. Continue only when `policy.applied` (top level, beside
|
|
103
|
+
`result`) is `true` and the required command is present. A `false` value means no bridge
|
|
104
|
+
answered and the output is the static registry, not the client's permissions. Report an absent
|
|
105
|
+
command instead of seeking another way to produce the same effect. Warn the user before a
|
|
106
|
+
command marked `"approval": true`, and prefer one `*-many` command when it can replace several
|
|
107
|
+
approvals. Dotted protocol names map to spaced CLI subcommands, such as `actor.item.update` to
|
|
108
|
+
`actor item update`.
|
|
101
109
|
2. `fvtt-world-cli schema <command>` shows the exact request parameters, required fields, enums,
|
|
102
110
|
and whether unknown fields are accepted; `fvtt-world-cli <command path> --help` maps flags.
|
|
103
111
|
3. Locate targets with the narrowest query: a family `list --name <substring> --limit <n>` when
|
|
@@ -106,8 +114,9 @@ guess a command name or parameter.
|
|
|
106
114
|
never the basis of a write.
|
|
107
115
|
4. `get` the complete target before mutating and work from the returned ids.
|
|
108
116
|
5. Build the smallest valid patch. Nested objects merge recursively; plain arrays replace
|
|
109
|
-
wholesale; dotted paths reach permitted leaves;
|
|
110
|
-
`
|
|
117
|
+
wholesale; dotted paths reach permitted leaves; `{"-=key": null}` deletes a permitted key, and
|
|
118
|
+
`{"==key": value}` replaces its whole value instead of merging. A `get` result contains
|
|
119
|
+
read-only and derived fields, so build the patch from accepted fields only.
|
|
111
120
|
For arrays and extensible `system`/`flags` data: read, preserve everything that
|
|
112
121
|
stays, change only the intended part.
|
|
113
122
|
6. Dry-run every nontrivial mutation with the global `--dry-run` flag. It runs the same
|
|
@@ -171,6 +180,10 @@ Classify a failure before reacting:
|
|
|
171
180
|
observed to change, not fully applied nested values. An embedded creation without a requested
|
|
172
181
|
id may already exist despite an indeterminate error. Send only the remaining changes as a new
|
|
173
182
|
operation with a fresh idempotency key, if using one.
|
|
183
|
+
- An update `INTERNAL_ERROR` with `details.paths` is a stored-vs-requested mismatch. Compare
|
|
184
|
+
`requested` and `stored` for each path. A representation-only difference, such as HTML escaping (`&` stored as `&`) or
|
|
185
|
+
whitespace, can be accepted as done. Report any other difference to the user. The same patch
|
|
186
|
+
stores the same result, so the next request is either a different patch or none.
|
|
174
187
|
- Correct a structured Foundry rejection and submit the corrected content as a new operation.
|
|
175
188
|
|
|
176
189
|
## Bulk writes and actions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
07f46d03812a74d1643f7483eb3eb926244dd1837b7afb11280c3072bfa3d5bd
|