fvtt-world-cli 1.1.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 +3 -2
- package/dist/index.js +10 -1
- package/docs/skill.md +7 -3
- package/package.json +5 -6
- package/bin/sync-installed-skill.mjs +0 -89
package/README.md
CHANGED
|
@@ -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
|
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync as readFileSync2 } from "node:fs";
|
|
|
10
10
|
import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
|
|
11
11
|
|
|
12
12
|
// ../protocol/src/constants.js
|
|
13
|
-
var PROTOCOL_VERSION = "1.1.
|
|
13
|
+
var PROTOCOL_VERSION = "1.1.1";
|
|
14
14
|
var PROTOCOL_COMPONENTS = Object.freeze({
|
|
15
15
|
MODULE: "module",
|
|
16
16
|
CLI_DAEMON: "cli-daemon",
|
|
@@ -14699,10 +14699,12 @@ function syncInstalledSkillCopies(dependencies) {
|
|
|
14699
14699
|
try {
|
|
14700
14700
|
const sourceDirectory = resolveSkillSourceDirectory();
|
|
14701
14701
|
if (!sourceDirectory) return;
|
|
14702
|
+
let installedAnywhere = false;
|
|
14702
14703
|
for (const root of knownSkillRoots(dependencies)) {
|
|
14703
14704
|
try {
|
|
14704
14705
|
const destination = join2(root, SKILL_NAME);
|
|
14705
14706
|
const state = inspectInstalledSkill(destination, sourceDirectory);
|
|
14707
|
+
if (state !== "missing") installedAnywhere = true;
|
|
14706
14708
|
if (state === "pristine") {
|
|
14707
14709
|
rmSync(destination, { recursive: true, force: true });
|
|
14708
14710
|
cpSync(sourceDirectory, destination, { recursive: true });
|
|
@@ -14722,6 +14724,13 @@ function syncInstalledSkillCopies(dependencies) {
|
|
|
14722
14724
|
continue;
|
|
14723
14725
|
}
|
|
14724
14726
|
}
|
|
14727
|
+
if (!installedAnywhere) {
|
|
14728
|
+
write(
|
|
14729
|
+
dependencies.stderr,
|
|
14730
|
+
`The ${SKILL_NAME} agent skill is not installed. If AI agents use this CLI, consider running \`fvtt-world-cli skill install\` to give them its operating manual.
|
|
14731
|
+
`
|
|
14732
|
+
);
|
|
14733
|
+
}
|
|
14725
14734
|
} catch {
|
|
14726
14735
|
return;
|
|
14727
14736
|
}
|
package/docs/skill.md
CHANGED
|
@@ -32,12 +32,16 @@ fvtt-world-cli skill install --to <skills-directory>
|
|
|
32
32
|
copy participates in updates later. `--link` symlinks instead of copying, which keeps the installed
|
|
33
33
|
skill permanently identical to the CLI it came from.
|
|
34
34
|
|
|
35
|
+
A bridge daemon started while no copy is installed anywhere prints a reminder with the
|
|
36
|
+
installation command, so a missing skill does not go unnoticed.
|
|
37
|
+
|
|
35
38
|
## Staying up to date
|
|
36
39
|
|
|
37
40
|
The skill is versioned together with the CLI, and the CLI keeps installed copies current on its
|
|
38
|
-
own:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
own: the bridge daemon checks every known copy at startup and refreshes outdated ones, so a package
|
|
42
|
+
update takes effect the next time the daemon starts. The check runs inside the CLI itself; nothing
|
|
43
|
+
about installation or updates relies on npm lifecycle scripts, which npm blocks by default. For
|
|
44
|
+
updates, a copy is one of two kinds:
|
|
41
45
|
|
|
42
46
|
- An **unmodified** copy, exactly what some version of the CLI shipped, is replaced silently with
|
|
43
47
|
the current version.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fvtt-world-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Safe local CLI and authenticated bridge for automating live Foundry VTT worlds.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "DroiD16",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"type": "module",
|
|
22
22
|
"bin": {
|
|
23
|
-
"fvtt-world-cli": "
|
|
24
|
-
"worldctl": "
|
|
23
|
+
"fvtt-world-cli": "bin/fvtt-world-cli.js",
|
|
24
|
+
"worldctl": "bin/fvtt-world-cli.js"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"bin/",
|
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --external:commander --external:pino --external:tsx --external:ws --external:zod --outfile=dist/index.js",
|
|
35
35
|
"typecheck": "tsc --project ../../tsconfig.json",
|
|
36
36
|
"prepack": "npm run build && node ./bin/prepare-package-assets.mjs",
|
|
37
|
-
"postpack": "node ./bin/prepare-package-assets.mjs --clean"
|
|
38
|
-
"postinstall": "node ./bin/sync-installed-skill.mjs"
|
|
37
|
+
"postpack": "node ./bin/prepare-package-assets.mjs --clean"
|
|
39
38
|
},
|
|
40
39
|
"dependencies": {
|
|
41
40
|
"commander": "^13.1.0",
|
|
@@ -45,6 +44,6 @@
|
|
|
45
44
|
"zod": "^3.24.3"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
|
-
"@fvtt-world-cli/protocol": "1.1.
|
|
47
|
+
"@fvtt-world-cli/protocol": "1.1.1"
|
|
49
48
|
}
|
|
50
49
|
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { cpSync, lstatSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
3
|
-
import { homedir, platform } from "node:os";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const SKILL_NAME = "foundry-world-editor";
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const binDirectory = dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const sourceDirectory = [resolve(binDirectory, "../skills"), resolve(binDirectory, "../../../skills")]
|
|
12
|
-
.map((candidate) => resolve(candidate, SKILL_NAME))
|
|
13
|
-
.find((candidate) => {
|
|
14
|
-
try {
|
|
15
|
-
return statSync(join(candidate, "SKILL.md")).isFile();
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
if (!sourceDirectory) process.exit(0);
|
|
21
|
-
const packaged = readFileSync(join(sourceDirectory, "SKILL.md"), "utf8");
|
|
22
|
-
|
|
23
|
-
const home = process.env.HOME?.trim() || homedir();
|
|
24
|
-
const xdgHome = process.env.XDG_CONFIG_HOME?.trim();
|
|
25
|
-
const configPath = xdgHome
|
|
26
|
-
? join(xdgHome, "fvtt-world-cli", "config.json")
|
|
27
|
-
: platform() === "darwin"
|
|
28
|
-
? join(home, "Library", "Application Support", "fvtt-world-cli", "config.json")
|
|
29
|
-
: platform() === "win32"
|
|
30
|
-
? join(
|
|
31
|
-
process.env.APPDATA?.trim() || join(home, "AppData", "Roaming"),
|
|
32
|
-
"fvtt-world-cli",
|
|
33
|
-
"config.json"
|
|
34
|
-
)
|
|
35
|
-
: join(home, ".config", "fvtt-world-cli", "config.json");
|
|
36
|
-
|
|
37
|
-
const roots = new Set([join(home, ".agents", "skills")]);
|
|
38
|
-
try {
|
|
39
|
-
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
40
|
-
if (Array.isArray(config?.skillInstalls)) {
|
|
41
|
-
for (const root of config.skillInstalls) {
|
|
42
|
-
if (typeof root === "string" && root.length > 0) roots.add(root);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// no or unreadable config: the standard location is still checked
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const root of roots) {
|
|
50
|
-
try {
|
|
51
|
-
const destination = join(root, SKILL_NAME);
|
|
52
|
-
const existing = (() => {
|
|
53
|
-
try {
|
|
54
|
-
return lstatSync(destination);
|
|
55
|
-
} catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
})();
|
|
59
|
-
if (!existing || existing.isSymbolicLink()) continue;
|
|
60
|
-
|
|
61
|
-
const installed = readFileSync(join(destination, "SKILL.md"), "utf8");
|
|
62
|
-
if (installed === packaged) continue;
|
|
63
|
-
|
|
64
|
-
const recorded = (() => {
|
|
65
|
-
try {
|
|
66
|
-
return readFileSync(join(destination, "SKILL.md.sha256"), "utf8").trim();
|
|
67
|
-
} catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
})();
|
|
71
|
-
|
|
72
|
-
if (recorded === createHash("sha256").update(installed).digest("hex")) {
|
|
73
|
-
rmSync(destination, { recursive: true, force: true });
|
|
74
|
-
cpSync(sourceDirectory, destination, { recursive: true });
|
|
75
|
-
console.error(
|
|
76
|
-
`fvtt-world-cli: updated the installed agent skill at ${destination} to the packaged version.`
|
|
77
|
-
);
|
|
78
|
-
} else {
|
|
79
|
-
console.error(
|
|
80
|
-
`fvtt-world-cli: the installed agent skill at ${destination} has local modifications and was NOT updated. Compare them, then run \`fvtt-world-cli skill update --force\` to replace it.`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|