@openclaw/plugin-inspector 0.3.5 → 0.3.7
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 +18 -1
- package/package.json +1 -1
- package/src/advanced.js +5 -0
- package/src/api.js +4 -0
- package/src/artifacts.js +1 -1
- package/src/ci-policy.js +3 -1
- package/src/ci-summary.js +16 -3
- package/src/compatibility-report.js +39 -3
- package/src/config.js +35 -5
- package/src/contract-probes.js +25 -1
- package/src/fixture-summary.js +406 -0
- package/src/import-loop-profile.js +227 -5
- package/src/index.js +7 -0
- package/src/inspector.js +35 -4
- package/src/issues.js +68 -3
- package/src/openclaw-target.js +79 -2
- package/src/platform-probes.js +29 -6
- package/src/prune-workspace-dev-deps-cli.js +23 -0
- package/src/report.js +19 -1
- package/src/runtime-reconciliation.js +124 -0
- package/src/sdk-mock.js +64 -6
- package/src/synthetic-probes.js +45 -0
- package/src/workspace-plan.js +19 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 0.3.7 - 2026-05-03
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Downgrade `registration-capture-gap` to advisory severity so missing capture evidence no longer reports as a P1 plugin contract risk.
|
|
10
|
+
|
|
11
|
+
## 0.3.6 - 2026-05-03
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Report import-loop RSS and CPU as baseline-adjusted plugin deltas alongside raw subprocess metrics so Crabpot dashboards do not treat harness import cost as plugin runtime cost.
|
|
16
|
+
- Include optional OpenClaw loader lifecycle timings for import and full activation when a capture runner provides them.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Accept plugin install minimum-host floors as supported package metadata.
|
|
21
|
+
- Flag unsupported legacy OpenClaw bundle metadata and advertised npm pack blockers.
|
|
22
|
+
- Reconcile runtime capture evidence and harden mocked capture paths for downstream fixture reports.
|
|
6
23
|
|
|
7
24
|
## 0.3.5 - 2026-04-29
|
|
8
25
|
|
package/package.json
CHANGED
package/src/advanced.js
CHANGED
|
@@ -95,6 +95,7 @@ export {
|
|
|
95
95
|
classifyTargetOpenClawCoverage,
|
|
96
96
|
readPackageSummaries,
|
|
97
97
|
readPluginManifests,
|
|
98
|
+
readSecurityManifests,
|
|
98
99
|
summarizePackage,
|
|
99
100
|
} from "./fixture-summary.js";
|
|
100
101
|
export {
|
|
@@ -183,6 +184,10 @@ export {
|
|
|
183
184
|
validateRuntimeProfile,
|
|
184
185
|
writeRuntimeProfile,
|
|
185
186
|
} from "./runtime-profile.js";
|
|
187
|
+
export {
|
|
188
|
+
applyRuntimeExecutionCoverage,
|
|
189
|
+
buildRuntimeExecutionCoverage,
|
|
190
|
+
} from "./runtime-reconciliation.js";
|
|
186
191
|
export {
|
|
187
192
|
buildRuntimeCaptureReport,
|
|
188
193
|
renderRuntimeCaptureMarkdown,
|
package/src/api.js
CHANGED
|
@@ -45,6 +45,7 @@ export async function inspectPluginRoot(options = {}) {
|
|
|
45
45
|
return inspectCompatibilityFixtureSet(config, {
|
|
46
46
|
generatedAt: options.generatedAt,
|
|
47
47
|
openclawPath: options.openclawPath,
|
|
48
|
+
executionResults: options.executionResults,
|
|
48
49
|
targetOpenClaw: options.targetOpenClaw,
|
|
49
50
|
});
|
|
50
51
|
}
|
|
@@ -59,6 +60,7 @@ export async function inspectCompatibilityFixtureSetConfig(options = {}) {
|
|
|
59
60
|
return inspectCompatibilityFixtureSet(config, {
|
|
60
61
|
generatedAt: options.generatedAt,
|
|
61
62
|
openclawPath: options.openclawPath,
|
|
63
|
+
executionResults: options.executionResults,
|
|
62
64
|
targetOpenClaw: options.targetOpenClaw,
|
|
63
65
|
});
|
|
64
66
|
}
|
|
@@ -112,6 +114,7 @@ export async function buildFixtureSetColdImportReadiness(options = {}) {
|
|
|
112
114
|
(await inspectCompatibilityFixtureSet(config, {
|
|
113
115
|
generatedAt: options.generatedAt,
|
|
114
116
|
openclawPath: options.openclawPath,
|
|
117
|
+
executionResults: options.executionResults,
|
|
115
118
|
targetOpenClaw: options.targetOpenClaw,
|
|
116
119
|
}));
|
|
117
120
|
|
|
@@ -143,6 +146,7 @@ export async function buildFixtureSetWorkspacePlan(options = {}) {
|
|
|
143
146
|
(await inspectCompatibilityFixtureSet(config, {
|
|
144
147
|
generatedAt: options.generatedAt,
|
|
145
148
|
openclawPath: options.openclawPath,
|
|
149
|
+
executionResults: options.executionResults,
|
|
146
150
|
targetOpenClaw: options.targetOpenClaw,
|
|
147
151
|
}));
|
|
148
152
|
const rootDir = options.rootDir ?? config?.rootDir ?? options.cwd;
|
package/src/artifacts.js
CHANGED
|
@@ -95,7 +95,7 @@ export function renderPaddedMarkdownTable(rows, headers, options = {}) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
export function escapeMarkdownTableCell(value) {
|
|
98
|
-
return value.replace(/\|/g, "\\|").replace(/\n/g, "<br>");
|
|
98
|
+
return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, "<br>");
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
async function assertFileMatches(filePath, expected) {
|
package/src/ci-policy.js
CHANGED
|
@@ -241,7 +241,9 @@ function executionChecks(executionResults, policy, options) {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
function findPolicyMatch(rules, item) {
|
|
244
|
-
return rules.find(
|
|
244
|
+
return rules.find(
|
|
245
|
+
(rule) => (rule.seam === "*" || item.seam === rule.seam) && item.reason?.includes(rule.reasonIncludes),
|
|
246
|
+
);
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
function failedExecutionEvidence(executionResults) {
|
package/src/ci-summary.js
CHANGED
|
@@ -56,8 +56,12 @@ export async function buildCiSummary(options = {}) {
|
|
|
56
56
|
loaderJitiCandidates: reports.platform?.summary?.jitiAlternativeCount ?? 0,
|
|
57
57
|
importLoopP50Ms: reports.importLoop?.summary?.p50WallMs ?? 0,
|
|
58
58
|
importLoopP95Ms: reports.importLoop?.summary?.p95WallMs ?? 0,
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
importLoopOpenClawLifecycleCount: reports.importLoop?.summary?.openClawLifecycleCount ?? 0,
|
|
60
|
+
importLoopOpenClawImportP50Ms: reports.importLoop?.summary?.p50OpenClawImportMs ?? 0,
|
|
61
|
+
importLoopOpenClawActivationP50Ms: reports.importLoop?.summary?.p50OpenClawActivationMs ?? 0,
|
|
62
|
+
importLoopMetricBasis: reports.importLoop?.summary?.maxPluginPeakRssDeltaMb === undefined ? "raw" : "baseline-adjusted",
|
|
63
|
+
importLoopMaxRssMb: reports.importLoop?.summary?.maxPluginPeakRssDeltaMb ?? reports.importLoop?.summary?.maxPeakRssMb ?? 0,
|
|
64
|
+
importLoopMaxCpuMs: reports.importLoop?.summary?.maxPluginCpuDeltaMsEstimate ?? reports.importLoop?.summary?.maxCpuMsEstimate ?? 0,
|
|
61
65
|
importLoopRssSampleCount: metricSampleCount(reports.importLoop, "rss", "maxPeakRssMb"),
|
|
62
66
|
importLoopCpuSampleCount: metricSampleCount(reports.importLoop, "cpu", "maxCpuMsEstimate"),
|
|
63
67
|
},
|
|
@@ -150,7 +154,7 @@ export function renderCiSummaryMarkdown(summary) {
|
|
|
150
154
|
["Jiti loader candidates", summary.summary.loaderJitiCandidates],
|
|
151
155
|
[
|
|
152
156
|
"Import loop",
|
|
153
|
-
|
|
157
|
+
importLoopSummaryLabel(summary.summary),
|
|
154
158
|
],
|
|
155
159
|
],
|
|
156
160
|
["Metric", "Value"],
|
|
@@ -249,6 +253,15 @@ function inferSampleCount(samples = [], kind) {
|
|
|
249
253
|
}, 0);
|
|
250
254
|
}
|
|
251
255
|
|
|
256
|
+
function importLoopSummaryLabel(summary) {
|
|
257
|
+
const metricLabel = summary.importLoopMetricBasis === "baseline-adjusted" ? "plugin delta" : "raw";
|
|
258
|
+
const lifecycle =
|
|
259
|
+
summary.importLoopOpenClawLifecycleCount > 0
|
|
260
|
+
? ` / OpenClaw import ${summary.importLoopOpenClawImportP50Ms} ms / activate ${summary.importLoopOpenClawActivationP50Ms} ms`
|
|
261
|
+
: "";
|
|
262
|
+
return `p50 ${summary.importLoopP50Ms} ms / p95 ${summary.importLoopP95Ms} ms / ${metricLabel} RSS ${formatSampledMetric(summary.importLoopMaxRssMb, summary.importLoopRssSampleCount)} / ${metricLabel} CPU ${formatSampledMetric(summary.importLoopMaxCpuMs, summary.importLoopCpuSampleCount, "ms")}${lifecycle}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
252
265
|
function formatSampledMetric(value, count, unit = "MB") {
|
|
253
266
|
if ((count ?? 0) <= 0) {
|
|
254
267
|
return "n/a";
|
|
@@ -26,13 +26,20 @@ export function renderCompatibilityMarkdownReport(report, options = {}) {
|
|
|
26
26
|
["Warnings", report.summary.warningCount],
|
|
27
27
|
["Compatibility suggestions", report.summary.suggestionCount],
|
|
28
28
|
["Issue findings", report.summary.issueCount],
|
|
29
|
+
["Open issue findings", report.summary.openIssueCount ?? report.summary.issueCount],
|
|
30
|
+
["Runtime-covered findings", report.summary.runtimeCoveredIssueCount ?? 0],
|
|
31
|
+
["Runtime-partial findings", report.summary.runtimePartiallyCoveredIssueCount ?? 0],
|
|
29
32
|
["P0 issues", report.summary.p0IssueCount],
|
|
30
33
|
["P1 issues", report.summary.p1IssueCount],
|
|
34
|
+
["Open P0 issues", report.summary.openP0IssueCount ?? report.summary.p0IssueCount],
|
|
35
|
+
["Open P1 issues", report.summary.openP1IssueCount ?? report.summary.p1IssueCount],
|
|
31
36
|
["Live issues", report.summary.liveIssueCount],
|
|
32
37
|
["Live P0 issues", report.summary.liveP0IssueCount],
|
|
33
38
|
["Compat gaps", report.summary.compatGapCount],
|
|
34
39
|
["Deprecation warnings", report.summary.deprecationWarningCount],
|
|
35
40
|
["Inspector gaps", report.summary.inspectorGapCount],
|
|
41
|
+
["Open inspector gaps", report.summary.openInspectorGapCount ?? report.summary.inspectorGapCount],
|
|
42
|
+
["Runtime coverage artifacts", report.summary.runtimeCoverageArtifactCount ?? 0],
|
|
36
43
|
["Upstream metadata", report.summary.upstreamIssueCount],
|
|
37
44
|
["Contract probes", report.summary.contractProbeCount],
|
|
38
45
|
["Decision rows", report.summary.decisionCount],
|
|
@@ -65,7 +72,11 @@ export function renderCompatibilityMarkdownReport(report, options = {}) {
|
|
|
65
72
|
"",
|
|
66
73
|
"## Inspector Proof Gaps",
|
|
67
74
|
"",
|
|
68
|
-
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap"), options),
|
|
75
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap" && issue.status !== "runtime-covered"), options),
|
|
76
|
+
"",
|
|
77
|
+
"## Runtime-Covered Inspector Gaps",
|
|
78
|
+
"",
|
|
79
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap" && issue.status === "runtime-covered"), options),
|
|
69
80
|
"",
|
|
70
81
|
"## Upstream Metadata Issues",
|
|
71
82
|
"",
|
|
@@ -141,13 +152,20 @@ export function renderCompatibilityIssuesReport(report, options = {}) {
|
|
|
141
152
|
markdownTable(
|
|
142
153
|
[
|
|
143
154
|
["Issue findings", report.summary.issueCount],
|
|
155
|
+
["Open issue findings", report.summary.openIssueCount ?? report.summary.issueCount],
|
|
156
|
+
["Runtime-covered findings", report.summary.runtimeCoveredIssueCount ?? 0],
|
|
157
|
+
["Runtime-partial findings", report.summary.runtimePartiallyCoveredIssueCount ?? 0],
|
|
144
158
|
[severityLabel("P0", options), report.summary.p0IssueCount],
|
|
145
159
|
[severityLabel("P1", options), report.summary.p1IssueCount],
|
|
160
|
+
[`Open ${severityLabel("P0", options)}`, report.summary.openP0IssueCount ?? report.summary.p0IssueCount],
|
|
161
|
+
[`Open ${severityLabel("P1", options)}`, report.summary.openP1IssueCount ?? report.summary.p1IssueCount],
|
|
146
162
|
["Live issues", report.summary.liveIssueCount],
|
|
147
163
|
["Live P0 issues", report.summary.liveP0IssueCount],
|
|
148
164
|
["Compat gaps", report.summary.compatGapCount],
|
|
149
165
|
["Deprecation warnings", report.summary.deprecationWarningCount],
|
|
150
166
|
["Inspector gaps", report.summary.inspectorGapCount],
|
|
167
|
+
["Open inspector gaps", report.summary.openInspectorGapCount ?? report.summary.inspectorGapCount],
|
|
168
|
+
["Runtime coverage artifacts", report.summary.runtimeCoverageArtifactCount ?? 0],
|
|
151
169
|
["Upstream metadata", report.summary.upstreamIssueCount],
|
|
152
170
|
["Contract probes", report.summary.contractProbeCount],
|
|
153
171
|
],
|
|
@@ -179,7 +197,11 @@ export function renderCompatibilityIssuesReport(report, options = {}) {
|
|
|
179
197
|
"",
|
|
180
198
|
"## Inspector Proof Gaps",
|
|
181
199
|
"",
|
|
182
|
-
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap"), options),
|
|
200
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap" && issue.status !== "runtime-covered"), options),
|
|
201
|
+
"",
|
|
202
|
+
"## Runtime-Covered Inspector Gaps",
|
|
203
|
+
"",
|
|
204
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "inspector-gap" && issue.status === "runtime-covered"), options),
|
|
183
205
|
"",
|
|
184
206
|
"## Upstream Metadata Issues",
|
|
185
207
|
"",
|
|
@@ -228,6 +250,7 @@ function issueBlock(issue, options) {
|
|
|
228
250
|
` - state: ${issueState(issue)}`,
|
|
229
251
|
" - evidence:",
|
|
230
252
|
...evidenceList(issue.evidence, options).map((item) => ` - ${item}`),
|
|
253
|
+
...runtimeCoverageList(issue, options),
|
|
231
254
|
].join("\n");
|
|
232
255
|
}
|
|
233
256
|
|
|
@@ -235,6 +258,7 @@ function issueState(issue) {
|
|
|
235
258
|
const flags = [
|
|
236
259
|
issue.status,
|
|
237
260
|
`compat:${issue.compatStatus ?? "none"}`,
|
|
261
|
+
issue.runtimeCoverage?.status ? `runtime:${issue.runtimeCoverage.status}` : null,
|
|
238
262
|
issue.live ? "live" : null,
|
|
239
263
|
issue.deprecated ? "deprecated" : null,
|
|
240
264
|
].filter(Boolean);
|
|
@@ -266,7 +290,7 @@ function triageOverview(report) {
|
|
|
266
290
|
"inspector-gap",
|
|
267
291
|
report.summary.inspectorGapCount,
|
|
268
292
|
"-",
|
|
269
|
-
"Plugin Inspector needs stronger capture/probe evidence before making contract judgments.",
|
|
293
|
+
"Plugin Inspector needs stronger capture/probe evidence before making contract judgments. Runtime-covered rows are proof-backed and not open report work.",
|
|
270
294
|
],
|
|
271
295
|
[
|
|
272
296
|
"upstream-metadata",
|
|
@@ -357,3 +381,15 @@ function evidenceList(evidence, options) {
|
|
|
357
381
|
const formatEvidence = options.formatEvidence ?? ((item) => item);
|
|
358
382
|
return items.map((item) => formatEvidence(item));
|
|
359
383
|
}
|
|
384
|
+
|
|
385
|
+
function runtimeCoverageList(issue, options) {
|
|
386
|
+
const runtimeCoverage = issue.runtimeCoverage;
|
|
387
|
+
if (!runtimeCoverage) {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
return [
|
|
391
|
+
" - runtime coverage:",
|
|
392
|
+
...evidenceList(runtimeCoverage.captured, options).map((item) => ` - captured ${item}`),
|
|
393
|
+
...evidenceList(runtimeCoverage.artifacts, options).map((item) => ` - ${item}`),
|
|
394
|
+
];
|
|
395
|
+
}
|
package/src/config.js
CHANGED
|
@@ -202,13 +202,43 @@ export function packageId(packageName) {
|
|
|
202
202
|
if (!packageName) {
|
|
203
203
|
return null;
|
|
204
204
|
}
|
|
205
|
-
|
|
205
|
+
const packageBase = packageName
|
|
206
206
|
.split("/")
|
|
207
207
|
.pop()
|
|
208
|
-
.replace(/^openclaw-/, "")
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
.replace(/^openclaw-/, "");
|
|
209
|
+
return trimHyphenEdges(collapsePackageIdSeparators(packageBase)).toLowerCase();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function collapsePackageIdSeparators(value) {
|
|
213
|
+
let result = "";
|
|
214
|
+
let previousWasHyphen = false;
|
|
215
|
+
for (const char of value) {
|
|
216
|
+
if (isAsciiAlphaNumeric(char)) {
|
|
217
|
+
result += char;
|
|
218
|
+
previousWasHyphen = false;
|
|
219
|
+
} else if (!previousWasHyphen) {
|
|
220
|
+
result += "-";
|
|
221
|
+
previousWasHyphen = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function isAsciiAlphaNumeric(char) {
|
|
228
|
+
const code = char.charCodeAt(0);
|
|
229
|
+
return (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function trimHyphenEdges(value) {
|
|
233
|
+
let start = 0;
|
|
234
|
+
let end = value.length;
|
|
235
|
+
while (start < end && value[start] === "-") {
|
|
236
|
+
start += 1;
|
|
237
|
+
}
|
|
238
|
+
while (end > start && value[end - 1] === "-") {
|
|
239
|
+
end -= 1;
|
|
240
|
+
}
|
|
241
|
+
return value.slice(start, end);
|
|
212
242
|
}
|
|
213
243
|
|
|
214
244
|
export function inferPluginSeams(pluginManifest, packageJson) {
|
package/src/contract-probes.js
CHANGED
|
@@ -74,6 +74,31 @@ export const contractProbeRules = {
|
|
|
74
74
|
contract: "OpenClaw package entrypoints resolve to files in the published or built plugin package.",
|
|
75
75
|
target: "package-loader",
|
|
76
76
|
},
|
|
77
|
+
"package-install-metadata-incomplete": {
|
|
78
|
+
id: "package.metadata.install-release",
|
|
79
|
+
contract: "Release publishing metadata declares canonical ClawHub and npm install specs.",
|
|
80
|
+
target: "package-loader",
|
|
81
|
+
},
|
|
82
|
+
"package-min-host-version-drift": {
|
|
83
|
+
id: "package.metadata.min-host-version",
|
|
84
|
+
contract: "Install minimum host version matches the OpenClaw package surface targeted by the plugin.",
|
|
85
|
+
target: "package-loader",
|
|
86
|
+
},
|
|
87
|
+
"package-npm-pack-entrypoint-missing": {
|
|
88
|
+
id: "package.npm-pack.entrypoints",
|
|
89
|
+
contract: "Advertised npm artifacts include every declared OpenClaw package entrypoint.",
|
|
90
|
+
target: "package-loader",
|
|
91
|
+
},
|
|
92
|
+
"package-npm-pack-metadata-missing": {
|
|
93
|
+
id: "package.npm-pack.metadata",
|
|
94
|
+
contract: "Advertised npm artifacts include OpenClaw manifest and package metadata.",
|
|
95
|
+
target: "package-loader",
|
|
96
|
+
},
|
|
97
|
+
"package-npm-pack-unavailable": {
|
|
98
|
+
id: "package.npm-pack.available",
|
|
99
|
+
contract: "Packages that advertise npm install support can produce an npm pack artifact.",
|
|
100
|
+
target: "package-loader",
|
|
101
|
+
},
|
|
77
102
|
"package-openclaw-entry-missing": {
|
|
78
103
|
id: "package.entrypoint.openclaw-metadata",
|
|
79
104
|
contract: "OpenClaw package metadata declares entrypoints for cold import and registration capture.",
|
|
@@ -150,7 +175,6 @@ export function probePriority(code, fixturePriority) {
|
|
|
150
175
|
"before-tool-call-probe",
|
|
151
176
|
"conversation-access-hook",
|
|
152
177
|
"missing-compat-record",
|
|
153
|
-
"registration-capture-gap",
|
|
154
178
|
"sdk-export-missing",
|
|
155
179
|
].includes(code)
|
|
156
180
|
) {
|