clawvault 3.5.0 → 3.5.2
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 +33 -25
- package/bin/command-registration.test.js +179 -0
- package/bin/command-runtime.test.js +154 -0
- package/bin/help-contract.test.js +55 -0
- package/bin/register-config-route-commands.test.js +121 -0
- package/bin/register-core-commands.test.js +80 -0
- package/bin/register-kanban-commands.test.js +83 -0
- package/bin/register-project-commands.test.js +206 -0
- package/bin/register-query-commands.test.js +80 -0
- package/bin/register-resilience-commands.test.js +81 -0
- package/bin/register-task-commands.test.js +69 -0
- package/bin/register-template-commands.test.js +87 -0
- package/bin/test-helpers/cli-command-fixtures.js +120 -0
- package/dashboard/lib/graph-diff.test.js +75 -0
- package/dashboard/lib/vault-parser.test.js +254 -0
- package/dist/{chunk-DCF4KMFD.js → chunk-DPK6P6BO.js} +3 -3
- package/dist/{chunk-BLQXXX7Q.js → chunk-FNFP7N6A.js} +2 -2
- package/dist/{chunk-JI7VUQV7.js → chunk-IFUHSHN3.js} +146 -118
- package/dist/{chunk-QFWERBDP.js → chunk-J6DW6HBX.js} +1 -1
- package/dist/{chunk-VXAGOLDP.js → chunk-LCBHM3D6.js} +1 -1
- package/dist/{chunk-HGDDW24U.js → chunk-NTQD55S3.js} +3 -3
- package/dist/{chunk-QUFQBAHP.js → chunk-P35SHNAU.js} +93 -147
- package/dist/cli/index.js +8 -8
- package/dist/commands/compat.js +1 -1
- package/dist/commands/inject.js +2 -2
- package/dist/commands/maintain.js +2 -2
- package/dist/commands/observe.js +4 -4
- package/dist/commands/rebuild.js +3 -3
- package/dist/commands/replay.js +4 -4
- package/dist/commands/sleep.js +5 -5
- package/dist/commands/status.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +17 -17
- package/dist/{openclaw-plugin--gqA2BZw.d.ts → openclaw-plugin-9M9qCZgl.d.ts} +2 -2
- package/dist/openclaw-plugin.d.ts +1 -1
- package/dist/openclaw-plugin.js +6 -1
- package/package.json +4 -26
- package/CHANGELOG.md +0 -543
- package/SKILL.md +0 -369
- package/docs/clawhub-security-release-playbook.md +0 -75
- package/docs/getting-started/installation.md +0 -99
- package/docs/openclaw-plugin-usage.md +0 -152
- package/dist/{chunk-7SWP5FKU.js → chunk-FSYISBTU.js} +4 -4
- package/dist/{chunk-D5U3Q4N5.js → chunk-IOKLQR4W.js} +4 -4
- package/dist/{chunk-OFOCU2V4.js → chunk-QL34TMGN.js} +3 -3
|
@@ -4,18 +4,8 @@ import * as path from "path";
|
|
|
4
4
|
import matter from "gray-matter";
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var LEGACY_HOOK_DIR = "hooks/clawvault";
|
|
10
|
-
var FORBIDDEN_PACKAGE_FILE_ENTRIES = [
|
|
11
|
-
"hooks",
|
|
12
|
-
"src",
|
|
13
|
-
"tests",
|
|
14
|
-
"testdata",
|
|
15
|
-
"benchmarks",
|
|
16
|
-
"eval",
|
|
17
|
-
"autoresearch"
|
|
18
|
-
];
|
|
7
|
+
var REQUIRED_HOOK_EVENTS = ["gateway:startup", "command:new", "session:start"];
|
|
8
|
+
var REQUIRED_HOOK_BIN = "clawvault";
|
|
19
9
|
function readOptionalFile(filePath) {
|
|
20
10
|
try {
|
|
21
11
|
if (!fs.existsSync(filePath)) return null;
|
|
@@ -44,17 +34,6 @@ function resolveProjectFile(relativePath, baseDir) {
|
|
|
44
34
|
}
|
|
45
35
|
return path.resolve(findPackageRoot(), relativePath);
|
|
46
36
|
}
|
|
47
|
-
function loadPackageJson(options) {
|
|
48
|
-
const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
|
|
49
|
-
if (!packageRaw) {
|
|
50
|
-
return { parsed: null, error: "package.json not found" };
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
return { parsed: JSON.parse(packageRaw) };
|
|
54
|
-
} catch (err) {
|
|
55
|
-
return { parsed: null, error: err?.message || "Unable to parse package.json" };
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
37
|
function checkOpenClawCli() {
|
|
59
38
|
const result = spawnSync("openclaw", ["--version"], { stdio: "ignore" });
|
|
60
39
|
if (result.error) {
|
|
@@ -62,7 +41,7 @@ function checkOpenClawCli() {
|
|
|
62
41
|
label: "openclaw CLI available",
|
|
63
42
|
status: "warn",
|
|
64
43
|
detail: "openclaw binary not found",
|
|
65
|
-
hint: "Install OpenClaw CLI to enable runtime validation."
|
|
44
|
+
hint: "Install OpenClaw CLI to enable hook runtime validation."
|
|
66
45
|
};
|
|
67
46
|
}
|
|
68
47
|
if (typeof result.status === "number" && result.status !== 0) {
|
|
@@ -83,69 +62,166 @@ function checkOpenClawCli() {
|
|
|
83
62
|
}
|
|
84
63
|
return { label: "openclaw CLI available", status: "ok" };
|
|
85
64
|
}
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
65
|
+
function checkPackageHookRegistration(options) {
|
|
66
|
+
const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
|
|
67
|
+
if (!packageRaw) {
|
|
89
68
|
return {
|
|
90
|
-
label: "package
|
|
69
|
+
label: "package hook registration",
|
|
91
70
|
status: "error",
|
|
92
|
-
detail:
|
|
71
|
+
detail: "package.json not found"
|
|
93
72
|
};
|
|
94
73
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(packageRaw);
|
|
76
|
+
const registeredHooks = parsed.openclaw?.hooks ?? [];
|
|
77
|
+
if (registeredHooks.includes("./hooks/clawvault")) {
|
|
78
|
+
return {
|
|
79
|
+
label: "package hook registration",
|
|
80
|
+
status: "ok",
|
|
81
|
+
detail: "./hooks/clawvault"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
98
84
|
return {
|
|
99
|
-
label: "package
|
|
100
|
-
status: "
|
|
101
|
-
detail:
|
|
85
|
+
label: "package hook registration",
|
|
86
|
+
status: "error",
|
|
87
|
+
detail: "Missing ./hooks/clawvault in package openclaw.hooks"
|
|
88
|
+
};
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
label: "package hook registration",
|
|
92
|
+
status: "error",
|
|
93
|
+
detail: err?.message || "Unable to parse package.json"
|
|
102
94
|
};
|
|
103
95
|
}
|
|
104
|
-
return {
|
|
105
|
-
label: "package plugin registration",
|
|
106
|
-
status: "error",
|
|
107
|
-
detail: `Missing openclaw.plugin=${REQUIRED_OPENCLAW_PLUGIN_PATH}`,
|
|
108
|
-
hint: `Set package.json openclaw.plugin to "${REQUIRED_OPENCLAW_PLUGIN_PATH}".`
|
|
109
|
-
};
|
|
110
96
|
}
|
|
111
|
-
function
|
|
112
|
-
const
|
|
113
|
-
if (!
|
|
97
|
+
function checkHookManifest(options) {
|
|
98
|
+
const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
|
|
99
|
+
if (!hookRaw) {
|
|
100
|
+
return {
|
|
101
|
+
label: "hook manifest",
|
|
102
|
+
status: "error",
|
|
103
|
+
detail: "HOOK.md not found"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const parsed = matter(hookRaw);
|
|
108
|
+
const openclaw = parsed.data?.metadata?.openclaw;
|
|
109
|
+
const events = Array.isArray(openclaw?.events) ? openclaw?.events ?? [] : [];
|
|
110
|
+
const missingEvents = REQUIRED_HOOK_EVENTS.filter((event) => !events.includes(event));
|
|
111
|
+
if (missingEvents.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
label: "hook manifest events",
|
|
114
|
+
status: "ok",
|
|
115
|
+
detail: events.join(", ")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
label: "hook manifest events",
|
|
120
|
+
status: "error",
|
|
121
|
+
detail: `Missing events: ${missingEvents.join(", ")}`
|
|
122
|
+
};
|
|
123
|
+
} catch (err) {
|
|
114
124
|
return {
|
|
115
|
-
label: "
|
|
125
|
+
label: "hook manifest events",
|
|
116
126
|
status: "error",
|
|
117
|
-
detail:
|
|
127
|
+
detail: err?.message || "Unable to parse HOOK.md frontmatter"
|
|
118
128
|
};
|
|
119
129
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
}
|
|
131
|
+
function checkHookManifestRequirements(options) {
|
|
132
|
+
const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
|
|
133
|
+
if (!hookRaw) {
|
|
123
134
|
return {
|
|
124
|
-
label: "
|
|
125
|
-
status: "
|
|
126
|
-
detail:
|
|
135
|
+
label: "hook manifest requirements",
|
|
136
|
+
status: "error",
|
|
137
|
+
detail: "HOOK.md not found"
|
|
127
138
|
};
|
|
128
139
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
try {
|
|
141
|
+
const parsed = matter(hookRaw);
|
|
142
|
+
const requiresBins = parsed.data?.metadata?.openclaw?.requires?.bins;
|
|
143
|
+
const bins = Array.isArray(requiresBins) ? requiresBins : [];
|
|
144
|
+
if (bins.includes(REQUIRED_HOOK_BIN)) {
|
|
145
|
+
return {
|
|
146
|
+
label: "hook manifest requirements",
|
|
147
|
+
status: "ok",
|
|
148
|
+
detail: `bins: ${bins.join(", ")}`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
label: "hook manifest requirements",
|
|
153
|
+
status: "warn",
|
|
154
|
+
detail: `Missing required hook bin "${REQUIRED_HOOK_BIN}"`,
|
|
155
|
+
hint: 'Add metadata.openclaw.requires.bins: ["clawvault"] to hooks/clawvault/HOOK.md.'
|
|
156
|
+
};
|
|
157
|
+
} catch (err) {
|
|
158
|
+
return {
|
|
159
|
+
label: "hook manifest requirements",
|
|
160
|
+
status: "error",
|
|
161
|
+
detail: err?.message || "Unable to parse HOOK.md frontmatter"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function checkHookHandlerSafety(options) {
|
|
166
|
+
const handlerRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/handler.js", options.baseDir));
|
|
167
|
+
if (!handlerRaw) {
|
|
168
|
+
return {
|
|
169
|
+
label: "hook handler script",
|
|
170
|
+
status: "error",
|
|
171
|
+
detail: "handler.js not found"
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const usesExecFileSync = handlerRaw.includes("execFileSync");
|
|
175
|
+
const usesExecSync = /\bexecSync\b/.test(handlerRaw);
|
|
176
|
+
const enablesShell = /\bshell\s*:\s*true\b/.test(handlerRaw);
|
|
177
|
+
const delegatesAutoProfile = /['"]--profile['"]\s*,\s*['"]auto['"]/.test(handlerRaw);
|
|
178
|
+
const violations = [];
|
|
179
|
+
if (!usesExecFileSync || usesExecSync) {
|
|
180
|
+
violations.push("execFileSync-only execution path");
|
|
181
|
+
}
|
|
182
|
+
if (enablesShell) {
|
|
183
|
+
violations.push("shell:false execution option");
|
|
184
|
+
}
|
|
185
|
+
if (!delegatesAutoProfile) {
|
|
186
|
+
violations.push("shared context profile delegation (--profile auto)");
|
|
187
|
+
}
|
|
188
|
+
if (violations.length > 0) {
|
|
189
|
+
return {
|
|
190
|
+
label: "hook handler safety",
|
|
191
|
+
status: "warn",
|
|
192
|
+
detail: `Missing conventions: ${violations.join(", ")}`,
|
|
193
|
+
hint: "Use execFileSync (no shell), avoid execSync, and delegate profile inference via --profile auto."
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { label: "hook handler safety", status: "ok" };
|
|
197
|
+
}
|
|
198
|
+
function resolvePluginManifestPath(options) {
|
|
199
|
+
const packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
|
|
200
|
+
if (packageRaw) {
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(packageRaw);
|
|
203
|
+
if (typeof parsed.openclaw?.plugin === "string") {
|
|
204
|
+
return parsed.openclaw.plugin.replace(/^\.\//, "");
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return "openclaw.plugin.json";
|
|
135
210
|
}
|
|
136
211
|
function checkPluginManifest(options) {
|
|
137
|
-
const
|
|
212
|
+
const manifestRelPath = resolvePluginManifestPath(options);
|
|
213
|
+
const manifestRaw = readOptionalFile(resolveProjectFile(manifestRelPath, options.baseDir));
|
|
138
214
|
if (!manifestRaw) {
|
|
139
215
|
return {
|
|
140
216
|
label: "plugin manifest",
|
|
141
217
|
status: "error",
|
|
142
|
-
detail:
|
|
143
|
-
hint: "
|
|
218
|
+
detail: `${manifestRelPath} not found`,
|
|
219
|
+
hint: "Ensure openclaw.plugin.json exists at the path declared in package.json openclaw.plugin."
|
|
144
220
|
};
|
|
145
221
|
}
|
|
146
222
|
try {
|
|
147
223
|
const parsed = JSON.parse(manifestRaw);
|
|
148
|
-
if (parsed.id !== "clawvault") {
|
|
224
|
+
if (!parsed.id || parsed.id !== "clawvault") {
|
|
149
225
|
return {
|
|
150
226
|
label: "plugin manifest",
|
|
151
227
|
status: "error",
|
|
@@ -156,10 +232,12 @@ function checkPluginManifest(options) {
|
|
|
156
232
|
return {
|
|
157
233
|
label: "plugin manifest",
|
|
158
234
|
status: "error",
|
|
159
|
-
detail: "Missing configSchema in plugin manifest"
|
|
235
|
+
detail: "Missing configSchema in plugin manifest",
|
|
236
|
+
hint: "Add configSchema to openclaw.plugin.json for config validation."
|
|
160
237
|
};
|
|
161
238
|
}
|
|
162
|
-
|
|
239
|
+
const hasVaultPath = Boolean(parsed.configSchema.properties?.vaultPath);
|
|
240
|
+
if (!hasVaultPath) {
|
|
163
241
|
return {
|
|
164
242
|
label: "plugin manifest",
|
|
165
243
|
status: "warn",
|
|
@@ -180,56 +258,6 @@ function checkPluginManifest(options) {
|
|
|
180
258
|
};
|
|
181
259
|
}
|
|
182
260
|
}
|
|
183
|
-
function checkPackageFilesHygiene(options) {
|
|
184
|
-
const packageJson = loadPackageJson(options);
|
|
185
|
-
if (!packageJson.parsed) {
|
|
186
|
-
return {
|
|
187
|
-
label: "package files hygiene",
|
|
188
|
-
status: "error",
|
|
189
|
-
detail: packageJson.error
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
const files = Array.isArray(packageJson.parsed.files) ? packageJson.parsed.files : [];
|
|
193
|
-
if (files.length === 0) {
|
|
194
|
-
return {
|
|
195
|
-
label: "package files hygiene",
|
|
196
|
-
status: "warn",
|
|
197
|
-
detail: "package.json files allowlist is missing",
|
|
198
|
-
hint: "Define package.json files to avoid publishing non-runtime directories."
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
const normalized = files.map((entry) => String(entry).replace(/\\/g, "/").replace(/\/+$/, ""));
|
|
202
|
-
const forbidden = normalized.filter(
|
|
203
|
-
(entry) => FORBIDDEN_PACKAGE_FILE_ENTRIES.some((name) => entry === name || entry.startsWith(`${name}/`))
|
|
204
|
-
);
|
|
205
|
-
if (forbidden.length > 0) {
|
|
206
|
-
return {
|
|
207
|
-
label: "package files hygiene",
|
|
208
|
-
status: "warn",
|
|
209
|
-
detail: `Forbidden publish entries: ${forbidden.join(", ")}`,
|
|
210
|
-
hint: "Remove legacy/test/research directories from package.json files."
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
label: "package files hygiene",
|
|
215
|
-
status: "ok"
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
function checkLegacyHooksRemoved(options) {
|
|
219
|
-
const hooksPath = resolveProjectFile(LEGACY_HOOK_DIR, options.baseDir);
|
|
220
|
-
if (fs.existsSync(hooksPath)) {
|
|
221
|
-
return {
|
|
222
|
-
label: "legacy hooks removed",
|
|
223
|
-
status: "warn",
|
|
224
|
-
detail: `${LEGACY_HOOK_DIR} still exists`,
|
|
225
|
-
hint: "Remove hooks/clawvault legacy handler artifacts in plugin-first architecture."
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
label: "legacy hooks removed",
|
|
230
|
-
status: "ok"
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
261
|
function checkSkillMetadata(options) {
|
|
234
262
|
const skillRaw = readOptionalFile(resolveProjectFile("SKILL.md", options.baseDir));
|
|
235
263
|
if (!skillRaw) {
|
|
@@ -270,11 +298,11 @@ function checkSkillMetadata(options) {
|
|
|
270
298
|
function checkOpenClawCompatibility(options = {}) {
|
|
271
299
|
const checks = [
|
|
272
300
|
checkOpenClawCli(),
|
|
273
|
-
|
|
274
|
-
checkPackageExtensionRegistration(options),
|
|
301
|
+
checkPackageHookRegistration(options),
|
|
275
302
|
checkPluginManifest(options),
|
|
276
|
-
|
|
277
|
-
|
|
303
|
+
checkHookManifest(options),
|
|
304
|
+
checkHookManifestRequirements(options),
|
|
305
|
+
checkHookHandlerSafety(options),
|
|
278
306
|
checkSkillMetadata(options)
|
|
279
307
|
];
|
|
280
308
|
const warnings = checks.filter((check) => check.status === "warn").length;
|