@openclaw/plugin-inspector 0.3.18 → 0.3.20

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.20 - 2026-07-31
4
+
5
+ ### Added
6
+
7
+ - Resolve `latest`, `beta`, or exact OpenClaw npm versions for compatibility inspection, reuse prepared targets from cache, and report exact target/source metadata.
8
+
9
+ ### Fixed
10
+
11
+ - Treat version-derived API removals inside a plugin's declared OpenClaw range as author-facing errors while keeping out-of-range targets informational, including beta eligibility against the upcoming stable version.
12
+ - Accept tested minimum OpenClaw host versions below a plugin's build version without reporting package metadata drift.
13
+ - Ignore type-only OpenClaw SDK imports when classifying runtime compatibility against a target release.
14
+ - Accept `openclaw.runtimeSetupEntry` and other paired compiled runtime entrypoints as valid replacements for absent source entrypoints.
15
+
16
+ ## 0.3.19 - 2026-07-27
17
+
18
+ ### Highlights
19
+
20
+ - Restore silently-lost manifest contract findings after OpenClaw moved its public manifest types into `manifest-types.ts`.
21
+
22
+ ### Changed
23
+
24
+ - Refresh generated and repository GitHub Actions workflows to the current checkout, setup-node, upload-artifact, and pnpm/action-setup releases.
25
+
3
26
  ## 0.3.18 - 2026-07-21
4
27
 
5
28
  ### Highlights
package/README.md CHANGED
@@ -34,6 +34,12 @@ checkout. If an OpenClaw checkout is supplied with `--openclaw <path>`, the
34
34
  inspector only reads public compatibility surfaces such as compat records, SDK
35
35
  exports, hook names, manifest fields, and registrar metadata.
36
36
 
37
+ Pass `--openclaw-version latest`, `--openclaw-version beta`, or an exact
38
+ published version to resolve an official OpenClaw npm target. Reports keep the
39
+ exact resolved version and npm source metadata. Prepared targets are cached, so
40
+ batch and repeated inspections do not download the same OpenClaw package for
41
+ each plugin.
42
+
37
43
  ## Quick Start
38
44
 
39
45
  Run this from a plugin package root:
@@ -181,6 +187,7 @@ Common options:
181
187
  | `--config <path>` | Read a standalone config file. Required for fixture-suite `report`. |
182
188
  | `--out <dir>` | Write reports somewhere other than `reports/`. |
183
189
  | `--openclaw <path>` | Compare against a local OpenClaw checkout. |
190
+ | `--openclaw-version latest\|beta\|<exact>` | Resolve and compare against an official OpenClaw npm release. |
184
191
  | `--no-openclaw` | Disable OpenClaw checkout comparison. |
185
192
  | `--runtime` / `--capture` | Add opt-in runtime registration capture. |
186
193
  | `--no-runtime` / `--no-capture` | Disable runtime capture even when config enables it. |
@@ -256,14 +263,14 @@ jobs:
256
263
  check:
257
264
  runs-on: ubuntu-latest
258
265
  steps:
259
- - uses: actions/checkout@v5
260
- - uses: actions/setup-node@v5
266
+ - uses: actions/checkout@v7
267
+ - uses: actions/setup-node@v7
261
268
  with:
262
269
  node-version: 24
263
270
  cache: npm
264
271
  - run: npm ci
265
272
  - run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
266
- - uses: actions/upload-artifact@v5
273
+ - uses: actions/upload-artifact@v7
267
274
  if: always()
268
275
  with:
269
276
  name: plugin-inspector-reports
@@ -298,7 +305,7 @@ Important report sections:
298
305
  | --- | --- |
299
306
  | `status` | `pass` unless hard breakages exist. |
300
307
  | `summary` | Counts for fixtures, breakages, warnings, suggestions, issues, issue classes, and contract probes. |
301
- | `targetOpenClaw` | Status and public compatibility data read from the optional OpenClaw checkout. |
308
+ | `targetOpenClaw` | Exact resolved version, source metadata, cache state, and public compatibility data for the selected OpenClaw target. |
302
309
  | `fixtures` | Per-plugin metadata, hooks, registrations, manifest contracts, package data, SDK imports, and SDK deprecation evidence. |
303
310
  | `breakages` | Blocking compatibility failures. |
304
311
  | `warnings` / `suggestions` | Non-blocking compatibility findings. |
@@ -309,8 +316,9 @@ Important report sections:
309
316
 
310
317
  Default `check`, `ci`, and `batch` reports include both author-facing and
311
318
  internal findings. Pass `--author-facing` when producing plugin-author output;
312
- that filtered view includes only findings with `authorRemediation.summary` and
313
- `authorRemediation.docsUrl`.
319
+ that filtered view includes only findings with `authorRemediation.summary`.
320
+ Curated fixes include `authorRemediation.docsUrl`; dynamically discovered API
321
+ removals use generic remediation without inventing a documentation URL.
314
322
  Current author-facing deprecation warnings include deprecated SDK helpers such
315
323
  as `loadSessionStore(...)` when a plugin still depends on the legacy whole-store
316
324
  session shape.
@@ -13,8 +13,8 @@ jobs:
13
13
  check:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@v5
17
- - uses: actions/setup-node@v5
16
+ - uses: actions/checkout@v7
17
+ - uses: actions/setup-node@v7
18
18
  with:
19
19
  node-version: 24
20
20
  cache: npm
@@ -24,7 +24,7 @@ jobs:
24
24
  if: always()
25
25
  with:
26
26
  sarif_file: reports/plugin-inspector.sarif
27
- - uses: actions/upload-artifact@v5
27
+ - uses: actions/upload-artifact@v7
28
28
  if: always()
29
29
  with:
30
30
  name: plugin-inspector-reports
@@ -9,14 +9,14 @@ jobs:
9
9
  check:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
- - uses: actions/checkout@v5
13
- - uses: actions/setup-node@v5
12
+ - uses: actions/checkout@v7
13
+ - uses: actions/setup-node@v7
14
14
  with:
15
15
  node-version: 24
16
16
  cache: npm
17
17
  - run: npm ci
18
18
  - run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
19
- - uses: actions/upload-artifact@v5
19
+ - uses: actions/upload-artifact@v7
20
20
  if: always()
21
21
  with:
22
22
  name: plugin-inspector-reports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/plugin-inspector",
3
- "version": "0.3.18",
3
+ "version": "0.3.20",
4
4
  "private": false,
5
5
  "description": "Offline compatibility inspector for OpenClaw plugins.",
6
6
  "type": "module",
@@ -31,6 +31,7 @@
31
31
  "./execution-results": "./src/execution-results.js",
32
32
  "./import-loop-profile": "./src/import-loop-profile.js",
33
33
  "./openclaw-target": "./src/openclaw-target.js",
34
+ "./openclaw-version": "./src/openclaw-version.js",
34
35
  "./platform-probes": "./src/platform-probes.js",
35
36
  "./profile-diff": "./src/profile-diff.js",
36
37
  "./ref-diff": "./src/ref-diff.js",
@@ -69,5 +70,9 @@
69
70
  "plugin",
70
71
  "compatibility",
71
72
  "ci"
72
- ]
73
+ ],
74
+ "dependencies": {
75
+ "semver": "^7.8.5",
76
+ "tar": "^7.5.22"
77
+ }
73
78
  }
package/src/advanced.js CHANGED
@@ -116,6 +116,13 @@ export {
116
116
  parseTypeFields,
117
117
  readOpenClawTargetSurface,
118
118
  } from "./openclaw-target.js";
119
+ export {
120
+ openClawEligibilityVersion,
121
+ prepareOpenClawTarget,
122
+ resolveOpenClawTargetVersion,
123
+ satisfiesOpenClawCompatibilityRange,
124
+ satisfiesOpenClawVersionRange,
125
+ } from "./openclaw-version.js";
119
126
  export {
120
127
  captureEntrypoint,
121
128
  captureEntrypointWithMockSdk,
package/src/api.js CHANGED
@@ -46,6 +46,7 @@ export async function inspectPluginRoot(options = {}) {
46
46
  authorFacing: options.authorFacing,
47
47
  generatedAt: options.generatedAt,
48
48
  openclawPath: options.openclawPath,
49
+ openclawVersion: options.openclawVersion,
49
50
  executionResults: options.executionResults,
50
51
  targetOpenClaw: options.targetOpenClaw,
51
52
  });
@@ -62,6 +63,7 @@ export async function inspectCompatibilityFixtureSetConfig(options = {}) {
62
63
  authorFacing: options.authorFacing,
63
64
  generatedAt: options.generatedAt,
64
65
  openclawPath: options.openclawPath,
66
+ openclawVersion: options.openclawVersion,
65
67
  executionResults: options.executionResults,
66
68
  targetOpenClaw: options.targetOpenClaw,
67
69
  });
@@ -117,6 +119,7 @@ export async function buildFixtureSetColdImportReadiness(options = {}) {
117
119
  authorFacing: options.authorFacing,
118
120
  generatedAt: options.generatedAt,
119
121
  openclawPath: options.openclawPath,
122
+ openclawVersion: options.openclawVersion,
120
123
  executionResults: options.executionResults,
121
124
  targetOpenClaw: options.targetOpenClaw,
122
125
  }));
@@ -150,6 +153,7 @@ export async function buildFixtureSetWorkspacePlan(options = {}) {
150
153
  authorFacing: options.authorFacing,
151
154
  generatedAt: options.generatedAt,
152
155
  openclawPath: options.openclawPath,
156
+ openclawVersion: options.openclawVersion,
153
157
  executionResults: options.executionResults,
154
158
  targetOpenClaw: options.targetOpenClaw,
155
159
  }));
package/src/batch.js CHANGED
@@ -4,6 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { renderMarkdownTable, writeJsonMarkdownArtifacts } from "./artifacts.js";
6
6
  import { runPluginCheck } from "./api.js";
7
+ import { prepareOpenClawTarget, resolveOpenClawTargetVersion } from "./openclaw-version.js";
7
8
 
8
9
  const ignoredDirs = new Set([
9
10
  ".git",
@@ -22,6 +23,11 @@ export async function runBatchAnalysis(options = {}) {
22
23
  const outRoot = path.resolve(rootDir, outDir);
23
24
  const concurrency = Math.max(1, Math.min(Math.round(options.concurrency ?? 4), 32));
24
25
  const keepPluginReports = options.keepPluginReports === true;
26
+ const targetOpenClaw =
27
+ options.targetOpenClaw ??
28
+ (options.openclawVersion
29
+ ? await prepareOpenClawTarget(await resolveOpenClawTargetVersion(options.openclawVersion, options), options)
30
+ : undefined);
25
31
  const pluginRoots = await discoverPluginRoots(rootDir);
26
32
  const tempRoot = keepPluginReports ? null : await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-batch-"));
27
33
  const entries = [];
@@ -37,6 +43,7 @@ export async function runBatchAnalysis(options = {}) {
37
43
  rootDir,
38
44
  outDir: reportsRoot,
39
45
  openclawPath: options.openclawPath,
46
+ targetOpenClaw,
40
47
  }),
41
48
  );
42
49
  });
@@ -113,6 +120,7 @@ async function inspectBatchPlugin(pluginRoot, options) {
113
120
  configPath: options.configPath,
114
121
  mockSdk: options.mockSdk,
115
122
  openclawPath: options.openclawPath,
123
+ targetOpenClaw: options.targetOpenClaw,
116
124
  outDir: options.outDir,
117
125
  pluginRoot,
118
126
  });
package/src/cli.js CHANGED
@@ -57,9 +57,10 @@ try {
57
57
  }
58
58
 
59
59
  async function runBatch(commandArgs) {
60
- const inputDir = readFirstPositional(commandArgs, new Set(["--out", "--openclaw", "--concurrency"]));
60
+ const inputDir = readFirstPositional(commandArgs, new Set(["--out", "--openclaw", "--openclaw-version", "--concurrency"]));
61
61
  const outDir = readFlag(commandArgs, "--out") ?? "reports";
62
62
  const openclawPath = commandArgs.includes("--no-openclaw") ? false : readFlag(commandArgs, "--openclaw");
63
+ const openclawVersion = readOpenClawVersion(commandArgs);
63
64
  const concurrency = Number(readFlag(commandArgs, "--concurrency") ?? "4");
64
65
  const json = commandArgs.includes("--json");
65
66
  const check = commandArgs.includes("--check");
@@ -72,6 +73,7 @@ async function runBatch(commandArgs) {
72
73
  rootDir: inputDir,
73
74
  outDir,
74
75
  openclawPath,
76
+ openclawVersion,
75
77
  concurrency,
76
78
  authorFacing,
77
79
  keepPluginReports,
@@ -105,6 +107,7 @@ async function runCheck(commandArgs, options = {}) {
105
107
  const pluginRoot = readFlag(commandArgs, "--plugin-root") ?? readFlag(commandArgs, "--root");
106
108
  const outDir = readFlag(commandArgs, "--out") ?? "reports";
107
109
  const openclawPath = commandArgs.includes("--no-openclaw") ? false : readFlag(commandArgs, "--openclaw");
110
+ const openclawVersion = readOpenClawVersion(commandArgs);
108
111
  const json = commandArgs.includes("--json");
109
112
  const capture = readRuntimeFlag(commandArgs);
110
113
  const mockSdk = readMockSdkFlag(commandArgs);
@@ -118,6 +121,7 @@ async function runCheck(commandArgs, options = {}) {
118
121
  configPath,
119
122
  mockSdk,
120
123
  openclawPath,
124
+ openclawVersion,
121
125
  outDir,
122
126
  pluginRoot,
123
127
  });
@@ -169,15 +173,17 @@ async function runReport(command, commandArgs) {
169
173
  const configPath = readFlag(commandArgs, "--config");
170
174
  const outDir = readFlag(commandArgs, "--out") ?? "reports";
171
175
  const openclawPath = commandArgs.includes("--no-openclaw") ? false : readFlag(commandArgs, "--openclaw");
176
+ const openclawVersion = readOpenClawVersion(commandArgs);
172
177
  const check = commandArgs.includes("--check") || command === "ci";
173
178
  const json = commandArgs.includes("--json");
174
179
  const ciOutputs = readCiOutputFlags(commandArgs);
175
180
  const authorFacing = readAuthorFacingFlag(commandArgs);
176
181
  const config = await loadInspectorConfig(configPath);
177
- const report = authorFacing
178
- ? await inspectCompatibilityFixtureSet(config, { authorFacing, openclawPath })
182
+ const compatibilityInspection = authorFacing || typeof openclawPath === "string" || openclawVersion !== null;
183
+ const report = compatibilityInspection
184
+ ? await inspectCompatibilityFixtureSet(config, { authorFacing, openclawPath, openclawVersion })
179
185
  : await inspectFixtureSet(config);
180
- const paths = authorFacing
186
+ const paths = compatibilityInspection
181
187
  ? await writeCompatibilityReport(report, { cwd: config.rootDir, outDir })
182
188
  : await writeReport(report, { outDir });
183
189
  await writeCiOutputArtifacts(report, {
@@ -202,6 +208,7 @@ async function runCi(commandArgs) {
202
208
  const pluginRoot = readFlag(commandArgs, "--plugin-root") ?? readFlag(commandArgs, "--root");
203
209
  const outDir = readFlag(commandArgs, "--out") ?? "reports";
204
210
  const openclawPath = commandArgs.includes("--no-openclaw") ? false : readFlag(commandArgs, "--openclaw");
211
+ const openclawVersion = readOpenClawVersion(commandArgs);
205
212
  const json = commandArgs.includes("--json");
206
213
  const capture = readRuntimeFlag(commandArgs);
207
214
  const mockSdk = readMockSdkFlag(commandArgs);
@@ -215,6 +222,7 @@ async function runCi(commandArgs) {
215
222
  configPath,
216
223
  mockSdk,
217
224
  openclawPath,
225
+ openclawVersion,
218
226
  outDir,
219
227
  pluginRoot,
220
228
  });
@@ -256,12 +264,13 @@ async function runCiCompatibilityReport({
256
264
  configPath,
257
265
  mockSdk,
258
266
  openclawPath,
267
+ openclawVersion,
259
268
  outDir,
260
269
  pluginRoot,
261
270
  }) {
262
271
  if (configPath) {
263
272
  const config = await loadInspectorConfig(configPath, { cwd: pluginRoot });
264
- const report = await inspectCompatibilityFixtureSet(config, { authorFacing, openclawPath });
273
+ const report = await inspectCompatibilityFixtureSet(config, { authorFacing, openclawPath, openclawVersion });
265
274
  await writeCompatibilityReport(report, { cwd: config.rootDir, outDir });
266
275
  return {
267
276
  report,
@@ -275,6 +284,7 @@ async function runCiCompatibilityReport({
275
284
  capture,
276
285
  mockSdk,
277
286
  openclawPath,
287
+ openclawVersion,
278
288
  outDir,
279
289
  pluginRoot,
280
290
  });
@@ -314,6 +324,19 @@ function readFlag(commandArgs, name) {
314
324
  return commandArgs[index + 1] ?? null;
315
325
  }
316
326
 
327
+ function readOpenClawVersion(commandArgs) {
328
+ const index = commandArgs.indexOf("--openclaw-version");
329
+ if (index === -1) return null;
330
+ const version = commandArgs[index + 1];
331
+ if (!version || version.startsWith("-")) {
332
+ throw new Error("--openclaw-version requires a value");
333
+ }
334
+ if (version && (commandArgs.includes("--no-openclaw") || commandArgs.includes("--openclaw"))) {
335
+ throw new Error("--openclaw-version cannot be combined with --openclaw or --no-openclaw");
336
+ }
337
+ return version;
338
+ }
339
+
317
340
  function findCaptureEntrypoint(commandArgs) {
318
341
  const flagsWithValues = new Set(["--output", "--plugin-root", "--sdk"]);
319
342
  const consumedIndexes = new Set();
@@ -452,13 +475,13 @@ function printHelp() {
452
475
 
453
476
  Usage:
454
477
  plugin-inspector
455
- plugin-inspector check [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--author-facing] [--json]
478
+ plugin-inspector check [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path> | --openclaw-version latest|beta|<exact> | --no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--author-facing] [--json]
456
479
  plugin-inspector config [--plugin-root <path>] [--config <path>] [--json]
457
480
  plugin-inspector init [--plugin-root <path>] [--config <path>] [--ci] [--scripts] [--package-manager npm|pnpm|yarn|bun] [--dry-run] [--json] [--force]
458
- plugin-inspector report --config <path> [--out <dir>] [--openclaw <path>] [--no-openclaw] [--author-facing] [--check] [--json]
459
- plugin-inspector batch <folder> [--out <dir>] [--openclaw <path>] [--no-openclaw] [--concurrency <n>] [--keep-plugin-reports] [--author-facing] [--check] [--json]
460
- plugin-inspector inspect [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--author-facing] [--check] [--json] [--sarif [path]] [--junit [path]] [--allow-execute]
461
- plugin-inspector ci [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--author-facing] [--json] [--no-sarif] [--no-junit]
481
+ plugin-inspector report --config <path> [--out <dir>] [--openclaw <path> | --openclaw-version latest|beta|<exact> | --no-openclaw] [--author-facing] [--check] [--json]
482
+ plugin-inspector batch <folder> [--out <dir>] [--openclaw <path> | --openclaw-version latest|beta|<exact> | --no-openclaw] [--concurrency <n>] [--keep-plugin-reports] [--author-facing] [--check] [--json]
483
+ plugin-inspector inspect [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path> | --openclaw-version latest|beta|<exact> | --no-openclaw] [--author-facing] [--check] [--json] [--sarif [path]] [--junit [path]] [--allow-execute]
484
+ plugin-inspector ci [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path> | --openclaw-version latest|beta|<exact> | --no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--author-facing] [--json] [--no-sarif] [--no-junit]
462
485
  plugin-inspector capture <entrypoint> [--mock-sdk|--real-sdk] [--allow-execute] [--plugin-root <path>] [--output <path>]
463
486
 
464
487
  Default check runs from the current plugin root and writes reports/ unless --out is set.
@@ -351,6 +351,12 @@ function targetOpenClawTable(targetOpenClaw = {}) {
351
351
  [
352
352
  ["Configured path", targetOpenClaw.configuredPath ?? "-"],
353
353
  ["Status", targetOpenClaw.status],
354
+ ["Requested version", targetOpenClaw.requestedVersion ?? "-"],
355
+ ["Resolved version", targetOpenClaw.version ?? "-"],
356
+ ["Range eligibility version", targetOpenClaw.eligibilityVersion ?? "-"],
357
+ ["Source", targetOpenClaw.source ? `${targetOpenClaw.source.type}:${targetOpenClaw.source.package}` : "-"],
358
+ ["NPM dist-tag", targetOpenClaw.source?.distTag ?? "-"],
359
+ ["Prepared cache", targetOpenClaw.cache ? (targetOpenClaw.cache.hit ? "hit" : "miss") : "-"],
354
360
  ["Compat registry", targetOpenClaw.compatRegistryPath ?? "-"],
355
361
  ["Compat records", targetOpenClaw.compatRecordCount ?? 0],
356
362
  ["Compat status counts", Object.entries(statusCounts).map(([status, count]) => `${status}:${count}`).join(", ") || "-"],
package/src/config.js CHANGED
@@ -195,7 +195,8 @@ async function readJsonIfExists(filePath) {
195
195
  if (!existsSync(filePath)) {
196
196
  return null;
197
197
  }
198
- return JSON.parse(await readFile(filePath, "utf8"));
198
+ const contents = await readFile(filePath, "utf8");
199
+ return JSON.parse(contents.startsWith("\uFEFF") ? contents.slice(1) : contents);
199
200
  }
200
201
 
201
202
  export function packageId(packageName) {