fvtt-world-cli 1.0.0 → 1.1.1
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 +18 -15
- package/bin/fvtt-world-cli.js +12 -0
- package/dist/index.js +2479 -128
- package/docs/README.md +2 -2
- package/docs/architecture.md +43 -11
- package/docs/commands.md +198 -37
- package/docs/compatibility.md +4 -2
- package/docs/getting-started.md +19 -10
- package/docs/protocol.md +114 -23
- package/docs/security.md +155 -22
- package/docs/skill.md +12 -8
- package/package.json +5 -6
- package/skills/foundry-world-editor/SKILL.md +68 -29
- package/skills/foundry-world-editor/SKILL.md.sha256 +1 -1
- package/bin/sync-installed-skill.mjs +0 -89
package/README.md
CHANGED
|
@@ -2,24 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://foundryvtt.com)
|
|
4
4
|
|
|
5
|
-
Tell your AI agent what should change in your Foundry VTT world, and it happens
|
|
5
|
+
Tell your AI agent what should change in your Foundry VTT world, and it happens in the live
|
|
6
6
|
world, validated by Foundry, visible to your players immediately.
|
|
7
7
|
|
|
8
8
|
```text
|
|
9
|
-
You → agent:
|
|
10
|
-
|
|
9
|
+
You → agent: "The scimitar in Valeros's inventory should burn targets on hit.
|
|
10
|
+
Add a Flaming effect to it."
|
|
11
11
|
|
|
12
12
|
agent → fvtt-world-cli:
|
|
13
13
|
actor list --name Valeros find the character
|
|
14
14
|
actor item list --name scimitar find the sword in their inventory
|
|
15
15
|
actor item effect create … add the effect to it
|
|
16
16
|
|
|
17
|
-
agent → you:
|
|
17
|
+
agent → you: "Done. Flaming effect added, already live in the world."
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Modern agents already understand requests like that. What they have lacked is a safe way into
|
|
21
21
|
Foundry: driving the browser UI is brittle and token consuming, and editing world files on disk
|
|
22
|
-
bypasses everything Foundry does to keep a world consistent. fvtt-world-cli is the missing bridge
|
|
22
|
+
bypasses everything Foundry does to keep a world consistent. fvtt-world-cli is the missing bridge,
|
|
23
23
|
a command line wired into your open GM session that performs every change through Foundry's own
|
|
24
24
|
APIs, exactly as if a GM had made it in the UI.
|
|
25
25
|
|
|
@@ -34,18 +34,18 @@ scenes down to individual tokens, walls, and lights, and pull content in from co
|
|
|
34
34
|
|
|
35
35
|
A few asks it handles end to end:
|
|
36
36
|
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
the scene
|
|
37
|
+
- "Sort the journals into folders by location."
|
|
38
|
+
- "Dim every light in the tavern scene down to torchlight."
|
|
39
|
+
- "Find every NPC that still has no portrait and list them."
|
|
40
|
+
- "Turn the bestiary goblin into a flying one that throws dynamite for 2d6 damage, and add it to
|
|
41
|
+
the scene."
|
|
42
42
|
|
|
43
43
|
[Commands](docs/commands.md) maps the full surface.
|
|
44
44
|
|
|
45
45
|
## Setting up
|
|
46
46
|
|
|
47
47
|
The simplest setup is one step: point your AI agent at this repository and ask it to set
|
|
48
|
-
fvtt-world-cli up. Then follow its lead
|
|
48
|
+
fvtt-world-cli up. Then follow its lead. It will most likely ask you to install the module in
|
|
49
49
|
Foundry and click *Pair* there, and it handles the rest itself.
|
|
50
50
|
|
|
51
51
|
### Manual setup
|
|
@@ -75,14 +75,14 @@ agent works with the world.
|
|
|
75
75
|
|
|
76
76
|
4. Pair the browser: choose *Pair* in the module's Authorization window, reachable from the
|
|
77
77
|
*World CLI* group in the scene controls or from the module's settings. Then approve the request
|
|
78
|
-
from the terminal
|
|
78
|
+
from the terminal. It shows the requesting origin, world, GM, and browser, and asks for a yes
|
|
79
79
|
or no:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
fvtt-world-cli auth
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
5. Optionally, install the packaged skill into your AI agent
|
|
85
|
+
5. Optionally, install the packaged skill into your AI agent; the next section explains what it
|
|
86
86
|
does:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
@@ -100,8 +100,9 @@ The package ships `foundry-world-editor`, an installable [Agent Skill](docs/skil
|
|
|
100
100
|
an agent to drive the CLI safely. It follows the open Agent Skills standard, so the same skill
|
|
101
101
|
works in Claude Code, Codex, and other agents that read `SKILL.md` files.
|
|
102
102
|
|
|
103
|
-
An installed skill stays current on its own:
|
|
104
|
-
edited locally is never replaced automatically, so your own
|
|
103
|
+
An installed skill stays current on its own: after a package update, the bridge daemon refreshes
|
|
104
|
+
it at startup. A copy you have edited locally is never replaced automatically, so your own
|
|
105
|
+
instructions survive updates.
|
|
105
106
|
|
|
106
107
|
## Built to be trusted with a live world
|
|
107
108
|
|
|
@@ -116,6 +117,8 @@ authority; [Security](docs/security.md) covers the boundaries in full. In short:
|
|
|
116
117
|
the UI uses, under the GM's permissions.
|
|
117
118
|
- Any change can be previewed before it happens: a global `--dry-run` flag runs the same
|
|
118
119
|
validation and guards as a real call and stops before mutation.
|
|
120
|
+
- Every command has a permission in the active Foundry client: run, ask the GM, or refuse.
|
|
121
|
+
Destructive commands ask the GM by default.
|
|
119
122
|
- There is no arbitrary-code path: commands are typed and validated on both sides of the transport,
|
|
120
123
|
and executable content such as scripted region behaviors is blocked on every write route.
|
|
121
124
|
- File access is confined to the active world's managed assets and always excludes its manifest,
|
package/bin/fvtt-world-cli.js
CHANGED
|
@@ -31,7 +31,19 @@ if (existsSync(compiledEntry) && process.env.FVTT_WORLD_CLI_FORCE_SRC !== "1") {
|
|
|
31
31
|
stdio: "inherit"
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
/** @type {NodeJS.Signals[]} */
|
|
35
|
+
const relayedSignals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
36
|
+
for (const signal of relayedSignals) {
|
|
37
|
+
process.on(signal, () => {
|
|
38
|
+
child.kill(signal);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
child.on("exit", (code, signal) => {
|
|
43
|
+
for (const relayed of relayedSignals) {
|
|
44
|
+
process.removeAllListeners(relayed);
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
if (signal) {
|
|
36
48
|
process.kill(process.pid, signal);
|
|
37
49
|
return;
|