@openclaw/plugin-inspector 0.3.2 → 0.3.4
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 +15 -0
- package/package.json +1 -1
- package/src/advanced.js +2 -0
- package/src/cli.js +2 -1
- package/src/cold-import-readiness.js +3 -1
- package/src/compatibility-report.js +3 -0
- package/src/contract-probes.js +14 -0
- package/src/fixture-summary.js +7 -1
- package/src/index.js +4 -2
- package/src/issues.js +1 -1
- package/src/platform-probes.js +56 -11
- package/src/report-sanitizer.js +42 -0
- package/src/report.js +10 -5
- package/src/synthetic-probes.js +140 -0
- package/src/workspace-plan.js +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.4 - 2026-04-29
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Separate executor-covered platform portability findings from residual findings so downstream structured runners can keep reports blocking only on unhandled risks.
|
|
10
|
+
- Sanitize absolute target OpenClaw paths from generated report artifacts and JSON CLI output.
|
|
11
|
+
- Normalize the dependency-install inspector finding title to use isolated-workspace wording.
|
|
12
|
+
- Treat `openclaw` package dependencies as host-linked workspace inputs instead of isolated dependency-install blockers.
|
|
13
|
+
|
|
14
|
+
## 0.3.3 - 2026-04-28
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Classify generated kitchen-sink public registrar coverage in synthetic probe plans so new API-surface fixtures do not fail as unknown execution profiles.
|
|
19
|
+
|
|
5
20
|
## 0.3.2 - 2026-04-28
|
|
6
21
|
|
|
7
22
|
### Fixed
|
package/package.json
CHANGED
package/src/advanced.js
CHANGED
|
@@ -43,6 +43,7 @@ export {
|
|
|
43
43
|
} from "./ci-outputs.js";
|
|
44
44
|
export {
|
|
45
45
|
buildContractProbes,
|
|
46
|
+
compatRecordForIssueCode,
|
|
46
47
|
contractProbeRules,
|
|
47
48
|
probePriority,
|
|
48
49
|
} from "./contract-probes.js";
|
|
@@ -170,6 +171,7 @@ export {
|
|
|
170
171
|
classifyCompatRecordCoverage,
|
|
171
172
|
renderMarkdownReport,
|
|
172
173
|
renderTextSummary,
|
|
174
|
+
sanitizeReportArtifact,
|
|
173
175
|
writeCompatibilityReport,
|
|
174
176
|
writeReport,
|
|
175
177
|
} from "./report.js";
|
package/src/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import {
|
|
4
4
|
loadPluginConfig,
|
|
5
5
|
renderTextSummary,
|
|
6
|
+
sanitizeReportArtifact,
|
|
6
7
|
runPluginCheck,
|
|
7
8
|
} from "./index.js";
|
|
8
9
|
import {
|
|
@@ -90,7 +91,7 @@ async function runCheck(commandArgs) {
|
|
|
90
91
|
});
|
|
91
92
|
|
|
92
93
|
if (json) {
|
|
93
|
-
console.log(JSON.stringify(report, null, 2));
|
|
94
|
+
console.log(JSON.stringify(sanitizeReportArtifact(report), null, 2));
|
|
94
95
|
} else {
|
|
95
96
|
console.log(renderTextSummary(report, { artifacts: paths }));
|
|
96
97
|
}
|
|
@@ -3,6 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import { renderPaddedMarkdownTable, writeJsonMarkdownArtifacts } from "./artifacts.js";
|
|
4
4
|
import { slugForArtifact } from "./path-utils.js";
|
|
5
5
|
|
|
6
|
+
const hostLinkedRuntimeDependencies = new Set(["openclaw"]);
|
|
7
|
+
|
|
6
8
|
export function buildColdImportReadiness(options = {}) {
|
|
7
9
|
const report = options.report;
|
|
8
10
|
if (!report) {
|
|
@@ -182,7 +184,7 @@ function classifyEntrypointReadiness({ fixture, packageSummary, entrypoint, root
|
|
|
182
184
|
...(packageSummary.dependencies ?? []),
|
|
183
185
|
...(packageSummary.peerDependencies ?? []),
|
|
184
186
|
...(packageSummary.optionalDependencies ?? []),
|
|
185
|
-
]);
|
|
187
|
+
]).filter((dependency) => !hostLinkedRuntimeDependencies.has(dependency));
|
|
186
188
|
if (entrypoint.exists && runtimeDependencies.length > 0) {
|
|
187
189
|
blockers.push({
|
|
188
190
|
code: "dependency-install-required",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { renderPaddedMarkdownTable } from "./artifacts.js";
|
|
2
|
+
import { sanitizeReportArtifact } from "./report-sanitizer.js";
|
|
2
3
|
|
|
3
4
|
const defaultSeverityLabels = {
|
|
4
5
|
P0: "P0",
|
|
@@ -8,6 +9,7 @@ const defaultSeverityLabels = {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export function renderCompatibilityMarkdownReport(report, options = {}) {
|
|
12
|
+
report = sanitizeReportArtifact(report, options);
|
|
11
13
|
return [
|
|
12
14
|
`# ${options.title ?? "OpenClaw Plugin Compatibility Report"}`,
|
|
13
15
|
"",
|
|
@@ -127,6 +129,7 @@ export function renderCompatibilityMarkdownReport(report, options = {}) {
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
export function renderCompatibilityIssuesReport(report, options = {}) {
|
|
132
|
+
report = sanitizeReportArtifact(report, options);
|
|
130
133
|
return [
|
|
131
134
|
`# ${options.title ?? "OpenClaw Plugin Issue Findings"}`,
|
|
132
135
|
"",
|
package/src/contract-probes.js
CHANGED
|
@@ -106,6 +106,20 @@ export const contractProbeRules = {
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
const openClawOwnedProbeIssueCodes = new Set([
|
|
110
|
+
"before-tool-call-probe",
|
|
111
|
+
"channel-contract-probe",
|
|
112
|
+
"conversation-access-hook",
|
|
113
|
+
"registration-capture-gap",
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
export function compatRecordForIssueCode(code) {
|
|
117
|
+
if (!openClawOwnedProbeIssueCodes.has(code)) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
return contractProbeRules[code]?.id;
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
export function buildContractProbes({ warnings = [], suggestions = [], fixtures = [] }) {
|
|
110
124
|
const fixtureById = new Map(fixtures.map((fixture) => [fixture.id, fixture]));
|
|
111
125
|
const probes = [];
|
package/src/fixture-summary.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { readdir } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { compatRecordForIssueCode } from "./contract-probes.js";
|
|
4
5
|
import { readJsonFile } from "./json-file.js";
|
|
5
6
|
|
|
6
7
|
const conversationAccessHooks = new Set(["agent_end", "llm_input", "llm_output"]);
|
|
@@ -17,6 +18,7 @@ const channelRegistrations = new Set([
|
|
|
17
18
|
"defineChannelPluginEntry",
|
|
18
19
|
"registerChannel",
|
|
19
20
|
]);
|
|
21
|
+
const hostLinkedRuntimeDependencies = new Set(["openclaw"]);
|
|
20
22
|
|
|
21
23
|
export async function buildCompatibilityFixtureReport({ fixture, inspection, checkoutPath, sourceRoot, rootDir = process.cwd() }) {
|
|
22
24
|
const pluginManifests = await readPluginManifests({ checkoutPath, sourceRoot, rootDir });
|
|
@@ -278,7 +280,7 @@ export function classifyPackageContracts({ fixture, inspection, fixtureReport })
|
|
|
278
280
|
...packageSummary.dependencies,
|
|
279
281
|
...packageSummary.peerDependencies,
|
|
280
282
|
...packageSummary.optionalDependencies,
|
|
281
|
-
]);
|
|
283
|
+
]).filter((dependency) => !hostLinkedRuntimeDependencies.has(dependency));
|
|
282
284
|
if (packageSummary.openclaw?.entrypoints.length > 0 && runtimeDependencies.length > 0) {
|
|
283
285
|
suggestions.push({
|
|
284
286
|
fixture: fixture.id,
|
|
@@ -399,6 +401,7 @@ export function classifyCompatibilityFixture({ fixture, inspection, fixtureRepor
|
|
|
399
401
|
level: "warning",
|
|
400
402
|
message: "fixture observes raw model or conversation content and needs privacy-boundary contract probes",
|
|
401
403
|
evidence: detailEvidence(conversationHookDetails),
|
|
404
|
+
compatRecord: compatRecordForIssueCode("conversation-access-hook"),
|
|
402
405
|
});
|
|
403
406
|
decisions.push({
|
|
404
407
|
fixture: fixture.id,
|
|
@@ -459,6 +462,7 @@ export function classifyCompatibilityFixture({ fixture, inspection, fixtureRepor
|
|
|
459
462
|
level: "suggestion",
|
|
460
463
|
message: "future inspector capture API should record lifecycle, route, gateway, command, and interactive registrations",
|
|
461
464
|
evidence: detailEvidence(captureGapRegistrationDetails),
|
|
465
|
+
compatRecord: compatRecordForIssueCode("registration-capture-gap"),
|
|
462
466
|
});
|
|
463
467
|
decisions.push({
|
|
464
468
|
fixture: fixture.id,
|
|
@@ -477,6 +481,7 @@ export function classifyCompatibilityFixture({ fixture, inspection, fixtureRepor
|
|
|
477
481
|
level: "suggestion",
|
|
478
482
|
message: "add contract probes for before_tool_call terminal, block, and approval semantics",
|
|
479
483
|
evidence: detailEvidence(hookDetails),
|
|
484
|
+
compatRecord: compatRecordForIssueCode("before-tool-call-probe"),
|
|
480
485
|
});
|
|
481
486
|
decisions.push({
|
|
482
487
|
fixture: fixture.id,
|
|
@@ -500,6 +505,7 @@ export function classifyCompatibilityFixture({ fixture, inspection, fixtureRepor
|
|
|
500
505
|
level: "suggestion",
|
|
501
506
|
message: "add channel envelope, config-schema, and runtime metadata probes",
|
|
502
507
|
evidence: detailEvidence(channelRegistrationDetails),
|
|
508
|
+
compatRecord: compatRecordForIssueCode("channel-contract-probe"),
|
|
503
509
|
});
|
|
504
510
|
decisions.push({
|
|
505
511
|
fixture: fixture.id,
|
package/src/index.js
CHANGED
|
@@ -53,11 +53,13 @@ export const staticInspection = Object.freeze({
|
|
|
53
53
|
export const reports = Object.freeze({
|
|
54
54
|
renderMarkdown: reportApi.renderMarkdownReport,
|
|
55
55
|
renderTextSummary: pluginApi.renderTextSummary,
|
|
56
|
+
sanitizeArtifact: reportApi.sanitizeReportArtifact,
|
|
56
57
|
write: reportApi.writeReport,
|
|
57
58
|
issueId: issuesApi.issueId,
|
|
58
59
|
classifyIssueFinding: issuesApi.classifyIssueFinding,
|
|
59
60
|
knownIssueCodes: issuesApi.knownIssueCodes,
|
|
60
61
|
openClawTargetPathCandidates: openClawTargetApi.openClawTargetPathCandidates,
|
|
62
|
+
readOpenClawTargetSurface: openClawTargetApi.readOpenClawTargetSurface,
|
|
61
63
|
});
|
|
62
64
|
|
|
63
65
|
export const contracts = Object.freeze({
|
|
@@ -200,7 +202,7 @@ export {
|
|
|
200
202
|
} from "./import-loop-profile.js";
|
|
201
203
|
export { classifyIssueFinding, issueId, knownIssueCodes } from "./issues.js";
|
|
202
204
|
export { inspectFixtureSet, inspectPlugin, inspectSourceText } from "./inspector.js";
|
|
203
|
-
export { openClawTargetPathCandidates } from "./openclaw-target.js";
|
|
205
|
+
export { openClawTargetPathCandidates, readOpenClawTargetSurface } from "./openclaw-target.js";
|
|
204
206
|
export {
|
|
205
207
|
buildProfileDiff,
|
|
206
208
|
defaultProfileDiffOptions,
|
|
@@ -216,7 +218,7 @@ export {
|
|
|
216
218
|
validateRefDiff,
|
|
217
219
|
writeRefDiff,
|
|
218
220
|
} from "./ref-diff.js";
|
|
219
|
-
export { renderMarkdownReport, writeReport } from "./report.js";
|
|
221
|
+
export { renderMarkdownReport, sanitizeReportArtifact, writeReport } from "./report.js";
|
|
220
222
|
export {
|
|
221
223
|
buildRuntimeProfile,
|
|
222
224
|
defaultRuntimeProfileCommands,
|
package/src/issues.js
CHANGED
|
@@ -119,7 +119,7 @@ export const issueMetadataByCode = {
|
|
|
119
119
|
severity: "P2",
|
|
120
120
|
owner: "inspector",
|
|
121
121
|
decision: "inspector-follow-up",
|
|
122
|
-
title: "cold import requires
|
|
122
|
+
title: "cold import requires dependency installation in an isolated workspace",
|
|
123
123
|
},
|
|
124
124
|
"package-entrypoint-missing": {
|
|
125
125
|
severity: "P1",
|
package/src/platform-probes.js
CHANGED
|
@@ -12,13 +12,11 @@ export function buildPlatformProbes(options = {}) {
|
|
|
12
12
|
const entrypoints = plan.fixtures.flatMap((fixture) =>
|
|
13
13
|
fixture.entrypoints.map((entrypoint) => summarizeEntrypoint(fixture.id, entrypoint)),
|
|
14
14
|
);
|
|
15
|
-
const
|
|
16
|
-
fixture.entrypoints.flatMap((entrypoint) =>
|
|
17
|
-
entrypoint.steps
|
|
18
|
-
.map((step) => summarizeStep(fixture.id, entrypoint, step))
|
|
19
|
-
.filter((finding) => finding.riskCodes.length > 0),
|
|
20
|
-
),
|
|
15
|
+
const stepFindings = plan.fixtures.flatMap((fixture) =>
|
|
16
|
+
fixture.entrypoints.flatMap((entrypoint) => entrypoint.steps.map((step) => summarizeStep(fixture.id, entrypoint, step, options.stepCoverage))),
|
|
21
17
|
);
|
|
18
|
+
const portabilityFindings = stepFindings.flatMap((finding) => (finding.residual ? [finding.residual] : []));
|
|
19
|
+
const coveredPortabilityFindings = stepFindings.flatMap((finding) => (finding.covered ? [finding.covered] : []));
|
|
22
20
|
|
|
23
21
|
return {
|
|
24
22
|
generatedAt: plan.generatedAt,
|
|
@@ -31,6 +29,7 @@ export function buildPlatformProbes(options = {}) {
|
|
|
31
29
|
jitiAlternativeCount: entrypoints.filter((entrypoint) => entrypoint.loaderAlternatives.includes("jiti")).length,
|
|
32
30
|
lazyImportProbeCount: entrypoints.filter((entrypoint) => entrypoint.capturePlanned && entrypoint.syntheticProbePlanned).length,
|
|
33
31
|
portabilityFindingCount: portabilityFindings.length,
|
|
32
|
+
coveredPortabilityFindingCount: coveredPortabilityFindings.length,
|
|
34
33
|
windowsRiskStepCount: portabilityFindings.filter((finding) => finding.platforms.includes("windows")).length,
|
|
35
34
|
macosRiskStepCount: portabilityFindings.filter((finding) => finding.platforms.includes("macos")).length,
|
|
36
35
|
linuxRiskStepCount: portabilityFindings.filter((finding) => finding.platforms.includes("linux")).length,
|
|
@@ -38,6 +37,7 @@ export function buildPlatformProbes(options = {}) {
|
|
|
38
37
|
},
|
|
39
38
|
entrypoints,
|
|
40
39
|
portabilityFindings,
|
|
40
|
+
coveredPortabilityFindings,
|
|
41
41
|
recommendations: buildRecommendations(portabilityFindings, entrypoints),
|
|
42
42
|
};
|
|
43
43
|
}
|
|
@@ -112,6 +112,19 @@ export function renderPlatformProbesMarkdown(report, options = {}) {
|
|
|
112
112
|
["Fixture", "Step", "Platforms", "Risks", "Mitigation"],
|
|
113
113
|
),
|
|
114
114
|
"",
|
|
115
|
+
"## Covered Portability Findings",
|
|
116
|
+
"",
|
|
117
|
+
markdownTable(
|
|
118
|
+
(report.coveredPortabilityFindings ?? []).map((finding) => [
|
|
119
|
+
finding.fixture,
|
|
120
|
+
finding.kind,
|
|
121
|
+
finding.platforms.join(", ") || "-",
|
|
122
|
+
finding.riskCodes.join(", "),
|
|
123
|
+
finding.coverage,
|
|
124
|
+
]),
|
|
125
|
+
["Fixture", "Step", "Platforms", "Covered Risks", "Coverage"],
|
|
126
|
+
),
|
|
127
|
+
"",
|
|
115
128
|
"## Recommendations",
|
|
116
129
|
"",
|
|
117
130
|
markdownTable(
|
|
@@ -140,16 +153,48 @@ function summarizeEntrypoint(fixtureId, entrypoint) {
|
|
|
140
153
|
};
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
function summarizeStep(fixtureId, entrypoint, step) {
|
|
156
|
+
function summarizeStep(fixtureId, entrypoint, step, stepCoverage) {
|
|
144
157
|
const riskCodes = stepRiskCodes(step);
|
|
145
|
-
|
|
158
|
+
const coverage = normalizeStepCoverage(stepCoverage?.({ fixture: fixtureId, entrypoint, step, riskCodes }), riskCodes);
|
|
159
|
+
const residualRiskCodes = riskCodes.filter((code) => !coverage.riskCodes.includes(code));
|
|
160
|
+
const common = {
|
|
146
161
|
fixture: fixtureId,
|
|
147
162
|
entrypoint: entrypoint.id,
|
|
148
163
|
kind: step.kind,
|
|
149
|
-
platforms: platformsForRiskCodes(riskCodes),
|
|
150
|
-
riskCodes,
|
|
151
164
|
command: step.command,
|
|
152
|
-
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
residual:
|
|
168
|
+
residualRiskCodes.length > 0
|
|
169
|
+
? {
|
|
170
|
+
...common,
|
|
171
|
+
coveredRiskCodes: coverage.riskCodes,
|
|
172
|
+
platforms: platformsForRiskCodes(residualRiskCodes),
|
|
173
|
+
riskCodes: residualRiskCodes,
|
|
174
|
+
mitigation: mitigationForRiskCodes(residualRiskCodes),
|
|
175
|
+
}
|
|
176
|
+
: null,
|
|
177
|
+
covered:
|
|
178
|
+
coverage.riskCodes.length > 0
|
|
179
|
+
? {
|
|
180
|
+
...common,
|
|
181
|
+
coverage: coverage.reason,
|
|
182
|
+
platforms: platformsForRiskCodes(coverage.riskCodes),
|
|
183
|
+
riskCodes: coverage.riskCodes,
|
|
184
|
+
}
|
|
185
|
+
: null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function normalizeStepCoverage(coverage, riskCodes) {
|
|
190
|
+
if (!coverage) {
|
|
191
|
+
return { reason: "", riskCodes: [] };
|
|
192
|
+
}
|
|
193
|
+
const requested = coverage === true ? riskCodes : coverage.coveredRiskCodes ?? coverage.handledRiskCodes ?? coverage.riskCodes ?? coverage;
|
|
194
|
+
const covered = Array.isArray(requested) ? requested : [];
|
|
195
|
+
return {
|
|
196
|
+
reason: typeof coverage.reason === "string" && coverage.reason ? coverage.reason : "covered by isolated workspace executor",
|
|
197
|
+
riskCodes: covered.filter((code) => riskCodes.includes(code)).sort(),
|
|
153
198
|
};
|
|
154
199
|
}
|
|
155
200
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function sanitizeReportArtifact(report, options = {}) {
|
|
4
|
+
const sensitivePaths = sensitiveOpenClawPaths(report);
|
|
5
|
+
if (sensitivePaths.length === 0) {
|
|
6
|
+
return report;
|
|
7
|
+
}
|
|
8
|
+
const placeholder = options.openclawPathPlaceholder ?? "<OPENCLAW_PATH>";
|
|
9
|
+
return sanitizeValue(report, sensitivePaths, placeholder);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function sensitiveOpenClawPaths(report) {
|
|
13
|
+
const targetOpenClaw = report?.targetOpenClaw;
|
|
14
|
+
return unique(
|
|
15
|
+
[targetOpenClaw?.configuredPath, ...(targetOpenClaw?.searchedPaths ?? [])]
|
|
16
|
+
.filter((value) => typeof value === "string" && isAbsolutePath(value))
|
|
17
|
+
.sort((left, right) => right.length - left.length),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function sanitizeValue(value, sensitivePaths, placeholder) {
|
|
22
|
+
if (typeof value === "string") {
|
|
23
|
+
return sensitivePaths.reduce((result, sensitivePath) => result.replaceAll(sensitivePath, placeholder), value);
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
return value.map((item) => sanitizeValue(item, sensitivePaths, placeholder));
|
|
27
|
+
}
|
|
28
|
+
if (value && typeof value === "object") {
|
|
29
|
+
return Object.fromEntries(
|
|
30
|
+
Object.entries(value).map(([key, entryValue]) => [key, sanitizeValue(entryValue, sensitivePaths, placeholder)]),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isAbsolutePath(value) {
|
|
37
|
+
return path.isAbsolute(value) || /^[A-Za-z]:[\\/]/u.test(value) || value.startsWith("\\\\");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function unique(values) {
|
|
41
|
+
return [...new Set(values)];
|
|
42
|
+
}
|
package/src/report.js
CHANGED
|
@@ -4,6 +4,7 @@ import { renderCompatibilityIssuesReport, renderCompatibilityMarkdownReport } fr
|
|
|
4
4
|
import { buildContractProbes } from "./contract-probes.js";
|
|
5
5
|
import { classifyCompatibilityFixture } from "./fixture-summary.js";
|
|
6
6
|
import { buildIssues, summarizeIssueClasses } from "./issues.js";
|
|
7
|
+
import { sanitizeReportArtifact } from "./report-sanitizer.js";
|
|
7
8
|
|
|
8
9
|
export function buildReport({ config, inspections, failures = [], generatedAt = "deterministic" }) {
|
|
9
10
|
const inspectionById = new Map(inspections.map((inspection) => [inspection.id, inspection]));
|
|
@@ -233,12 +234,13 @@ export async function writeReport(report, options = {}) {
|
|
|
233
234
|
const basename = options.basename ?? "plugin-inspector-report";
|
|
234
235
|
const jsonPath = path.join(outDir, `${basename}.json`);
|
|
235
236
|
const markdownPath = path.join(outDir, `${basename}.md`);
|
|
237
|
+
const artifactReport = sanitizeReportArtifact(report, options);
|
|
236
238
|
|
|
237
239
|
return writeJsonMarkdownArtifacts({
|
|
238
240
|
jsonPath,
|
|
239
241
|
markdownPath,
|
|
240
|
-
json:
|
|
241
|
-
markdown: renderMarkdownReport(
|
|
242
|
+
json: artifactReport,
|
|
243
|
+
markdown: renderMarkdownReport(artifactReport),
|
|
242
244
|
check: options.check,
|
|
243
245
|
});
|
|
244
246
|
}
|
|
@@ -249,6 +251,7 @@ export async function writeCompatibilityReport(report, options = {}) {
|
|
|
249
251
|
const jsonPath = options.jsonPath ?? path.join(outDir, `${basename}.json`);
|
|
250
252
|
const markdownPath = options.markdownPath ?? path.join(outDir, `${basename}.md`);
|
|
251
253
|
const issuesPath = options.issuesPath ?? path.join(outDir, options.issuesBasename ?? "plugin-inspector-issues.md");
|
|
254
|
+
const artifactReport = sanitizeReportArtifact(report, options);
|
|
252
255
|
const markdownOptions = compatibilityRenderOptions(options, {
|
|
253
256
|
title: options.markdownTitle ?? options.title,
|
|
254
257
|
...options.markdownOptions,
|
|
@@ -260,14 +263,16 @@ export async function writeCompatibilityReport(report, options = {}) {
|
|
|
260
263
|
|
|
261
264
|
return writeArtifacts(
|
|
262
265
|
[
|
|
263
|
-
{ name: "jsonPath", path: jsonPath, json:
|
|
264
|
-
{ name: "markdownPath", path: markdownPath, markdown: renderCompatibilityMarkdownReport(
|
|
265
|
-
{ name: "issuesPath", path: issuesPath, markdown: renderCompatibilityIssuesReport(
|
|
266
|
+
{ name: "jsonPath", path: jsonPath, json: artifactReport },
|
|
267
|
+
{ name: "markdownPath", path: markdownPath, markdown: renderCompatibilityMarkdownReport(artifactReport, markdownOptions) },
|
|
268
|
+
{ name: "issuesPath", path: issuesPath, markdown: renderCompatibilityIssuesReport(artifactReport, issuesOptions) },
|
|
266
269
|
],
|
|
267
270
|
{ check: options.check },
|
|
268
271
|
);
|
|
269
272
|
}
|
|
270
273
|
|
|
274
|
+
export { sanitizeReportArtifact };
|
|
275
|
+
|
|
271
276
|
function compatibilityRenderOptions(options, overrides) {
|
|
272
277
|
const renderOptions = {
|
|
273
278
|
formatEvidence: options.formatEvidence,
|
package/src/synthetic-probes.js
CHANGED
|
@@ -16,19 +16,64 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
16
16
|
callableProperties: ["send", "sendMessage", "receive", "handleMessage"],
|
|
17
17
|
option: "includeChannelRuntime",
|
|
18
18
|
},
|
|
19
|
+
registerAgentHarness: {
|
|
20
|
+
mode: "metadata-only",
|
|
21
|
+
callableProperties: [],
|
|
22
|
+
reason: "agent harness factories are captured as registration metadata; agent runtime execution remains isolated opt-in",
|
|
23
|
+
},
|
|
24
|
+
registerAgentToolResultMiddleware: {
|
|
25
|
+
mode: "metadata-only",
|
|
26
|
+
callableProperties: [],
|
|
27
|
+
reason: "agent tool-result middleware is captured as registration metadata before tool-result pipeline execution",
|
|
28
|
+
},
|
|
29
|
+
registerAutoEnableProbe: {
|
|
30
|
+
mode: "metadata-only",
|
|
31
|
+
callableProperties: [],
|
|
32
|
+
reason: "auto-enable probes are captured as registration metadata before runtime activation checks",
|
|
33
|
+
},
|
|
19
34
|
registerCli: {
|
|
20
35
|
mode: "direct",
|
|
21
36
|
callableProperties: ["handler", "run", "execute"],
|
|
22
37
|
},
|
|
38
|
+
registerCliBackend: {
|
|
39
|
+
mode: "metadata-only",
|
|
40
|
+
callableProperties: [],
|
|
41
|
+
reason: "CLI backend descriptors are captured as registration metadata before backend process execution",
|
|
42
|
+
},
|
|
23
43
|
registerCommand: {
|
|
24
44
|
mode: "direct",
|
|
25
45
|
callableProperties: ["handler", "run", "execute"],
|
|
26
46
|
},
|
|
47
|
+
registerCodexAppServerExtensionFactory: {
|
|
48
|
+
mode: "metadata-only",
|
|
49
|
+
callableProperties: [],
|
|
50
|
+
reason: "Codex app server extension factories are captured as registration metadata before host UI execution",
|
|
51
|
+
},
|
|
52
|
+
registerCompactionProvider: {
|
|
53
|
+
mode: "metadata-only",
|
|
54
|
+
callableProperties: [],
|
|
55
|
+
reason: "compaction providers are captured as registration metadata before compaction runtime execution",
|
|
56
|
+
},
|
|
57
|
+
registerConfigMigration: {
|
|
58
|
+
mode: "metadata-only",
|
|
59
|
+
callableProperties: [],
|
|
60
|
+
reason: "config migrations are captured as registration metadata before mutating stored plugin config",
|
|
61
|
+
},
|
|
27
62
|
registerContextEngine: {
|
|
28
63
|
mode: "metadata-only",
|
|
29
64
|
callableProperties: [],
|
|
30
65
|
reason: "context engine factories are captured as registration metadata; engine startup remains isolated opt-in",
|
|
31
66
|
},
|
|
67
|
+
registerDetachedTaskRuntime: {
|
|
68
|
+
mode: "metadata-only",
|
|
69
|
+
callableProperties: [],
|
|
70
|
+
reason: "detached task runtimes are captured as registration metadata before async task execution",
|
|
71
|
+
},
|
|
72
|
+
registerGatewayDiscoveryService: {
|
|
73
|
+
mode: "metadata-only",
|
|
74
|
+
callableProperties: [],
|
|
75
|
+
reason: "gateway discovery services are captured as registration metadata before network discovery execution",
|
|
76
|
+
},
|
|
32
77
|
registerGatewayMethod: {
|
|
33
78
|
mode: "direct",
|
|
34
79
|
callableProperties: ["handler", "run", "execute", "invoke"],
|
|
@@ -46,16 +91,91 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
46
91
|
callableProperties: [],
|
|
47
92
|
reason: "legacy hook registrar is captured as metadata; hook handlers are probed through hook events",
|
|
48
93
|
},
|
|
94
|
+
registerImageGenerationProvider: {
|
|
95
|
+
mode: "metadata-only",
|
|
96
|
+
callableProperties: [],
|
|
97
|
+
reason: "image generation providers are captured as registration metadata before provider runtime execution",
|
|
98
|
+
},
|
|
49
99
|
registerMemoryPromptSection: {
|
|
50
100
|
mode: "metadata-only",
|
|
51
101
|
callableProperties: [],
|
|
52
102
|
reason: "memory prompt section renderers are captured as metadata before prompt-runtime execution",
|
|
53
103
|
},
|
|
104
|
+
registerMediaUnderstandingProvider: {
|
|
105
|
+
mode: "metadata-only",
|
|
106
|
+
callableProperties: [],
|
|
107
|
+
reason: "media understanding providers are captured as registration metadata before provider runtime execution",
|
|
108
|
+
},
|
|
109
|
+
registerMemoryCapability: {
|
|
110
|
+
mode: "metadata-only",
|
|
111
|
+
callableProperties: [],
|
|
112
|
+
reason: "memory capabilities are captured as registration metadata before memory runtime execution",
|
|
113
|
+
},
|
|
114
|
+
registerMemoryCorpusSupplement: {
|
|
115
|
+
mode: "metadata-only",
|
|
116
|
+
callableProperties: [],
|
|
117
|
+
reason: "memory corpus supplements are captured as registration metadata before memory runtime execution",
|
|
118
|
+
},
|
|
119
|
+
registerMemoryEmbeddingProvider: {
|
|
120
|
+
mode: "metadata-only",
|
|
121
|
+
callableProperties: [],
|
|
122
|
+
reason: "memory embedding providers are captured as registration metadata before provider runtime execution",
|
|
123
|
+
},
|
|
124
|
+
registerMemoryFlushPlan: {
|
|
125
|
+
mode: "metadata-only",
|
|
126
|
+
callableProperties: [],
|
|
127
|
+
reason: "memory flush plans are captured as registration metadata before memory runtime execution",
|
|
128
|
+
},
|
|
54
129
|
registerMemoryRuntime: {
|
|
55
130
|
mode: "metadata-only",
|
|
56
131
|
callableProperties: [],
|
|
57
132
|
reason: "memory runtime factories are captured as metadata; external memory startup remains isolated opt-in",
|
|
58
133
|
},
|
|
134
|
+
registerMemoryPromptSupplement: {
|
|
135
|
+
mode: "metadata-only",
|
|
136
|
+
callableProperties: [],
|
|
137
|
+
reason: "memory prompt supplements are captured as registration metadata before prompt-runtime execution",
|
|
138
|
+
},
|
|
139
|
+
registerMigrationProvider: {
|
|
140
|
+
mode: "metadata-only",
|
|
141
|
+
callableProperties: [],
|
|
142
|
+
reason: "migration providers are captured as registration metadata before migration runtime execution",
|
|
143
|
+
},
|
|
144
|
+
registerMusicGenerationProvider: {
|
|
145
|
+
mode: "metadata-only",
|
|
146
|
+
callableProperties: [],
|
|
147
|
+
reason: "music generation providers are captured as registration metadata before provider runtime execution",
|
|
148
|
+
},
|
|
149
|
+
registerNodeHostCommand: {
|
|
150
|
+
mode: "metadata-only",
|
|
151
|
+
callableProperties: [],
|
|
152
|
+
reason: "node host commands are captured as registration metadata before host process execution",
|
|
153
|
+
},
|
|
154
|
+
registerProvider: {
|
|
155
|
+
mode: "metadata-only",
|
|
156
|
+
callableProperties: [],
|
|
157
|
+
reason: "provider descriptors are captured as registration metadata before provider runtime execution",
|
|
158
|
+
},
|
|
159
|
+
registerRealtimeTranscriptionProvider: {
|
|
160
|
+
mode: "metadata-only",
|
|
161
|
+
callableProperties: [],
|
|
162
|
+
reason: "realtime transcription providers are captured as registration metadata before provider runtime execution",
|
|
163
|
+
},
|
|
164
|
+
registerRealtimeVoiceProvider: {
|
|
165
|
+
mode: "metadata-only",
|
|
166
|
+
callableProperties: [],
|
|
167
|
+
reason: "realtime voice providers are captured as registration metadata before provider runtime execution",
|
|
168
|
+
},
|
|
169
|
+
registerReload: {
|
|
170
|
+
mode: "metadata-only",
|
|
171
|
+
callableProperties: [],
|
|
172
|
+
reason: "reload handlers are captured as registration metadata before runtime reload execution",
|
|
173
|
+
},
|
|
174
|
+
registerSecurityAuditCollector: {
|
|
175
|
+
mode: "metadata-only",
|
|
176
|
+
callableProperties: [],
|
|
177
|
+
reason: "security audit collectors are captured as registration metadata before filesystem or policy scans",
|
|
178
|
+
},
|
|
59
179
|
registerService: {
|
|
60
180
|
mode: "lifecycle-opt-in",
|
|
61
181
|
callableProperties: ["start", "stop", "dispose"],
|
|
@@ -70,6 +190,26 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
70
190
|
mode: "direct",
|
|
71
191
|
callableProperties: ["run", "handler", "execute"],
|
|
72
192
|
},
|
|
193
|
+
registerTextTransforms: {
|
|
194
|
+
mode: "metadata-only",
|
|
195
|
+
callableProperties: [],
|
|
196
|
+
reason: "text transforms are captured as registration metadata before content mutation execution",
|
|
197
|
+
},
|
|
198
|
+
registerVideoGenerationProvider: {
|
|
199
|
+
mode: "metadata-only",
|
|
200
|
+
callableProperties: [],
|
|
201
|
+
reason: "video generation providers are captured as registration metadata before provider runtime execution",
|
|
202
|
+
},
|
|
203
|
+
registerWebFetchProvider: {
|
|
204
|
+
mode: "metadata-only",
|
|
205
|
+
callableProperties: [],
|
|
206
|
+
reason: "web fetch providers are captured as registration metadata before provider runtime execution",
|
|
207
|
+
},
|
|
208
|
+
registerWebSearchProvider: {
|
|
209
|
+
mode: "metadata-only",
|
|
210
|
+
callableProperties: [],
|
|
211
|
+
reason: "web search providers are captured as registration metadata before provider runtime execution",
|
|
212
|
+
},
|
|
73
213
|
};
|
|
74
214
|
|
|
75
215
|
export const defaultSyntheticHookEvents = {
|
package/src/workspace-plan.js
CHANGED
|
@@ -218,7 +218,7 @@ async function buildEntrypointPlan({ fixtureId, entrypoint, packageSummary, pack
|
|
|
218
218
|
const packageManager = detectPackageManager(settings.rootDir, packageDir, packageJson);
|
|
219
219
|
const lockfile = findNearestLockfile(settings.rootDir, packageDir);
|
|
220
220
|
const buildScript = packageJson.scripts?.build;
|
|
221
|
-
const requiredCapabilities = requiredCapabilitiesFor(entrypoint);
|
|
221
|
+
const requiredCapabilities = requiredCapabilitiesFor(entrypoint, packageSummary);
|
|
222
222
|
const loaderStrategy = loaderStrategyFor(entrypoint);
|
|
223
223
|
const blockers = [...entrypoint.blockers];
|
|
224
224
|
const workspacePath = posixJoin(settings.workspaceRoot, fixtureId);
|
|
@@ -342,7 +342,7 @@ function loaderStrategyFor(entrypoint) {
|
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
function requiredCapabilitiesFor(entrypoint) {
|
|
345
|
+
function requiredCapabilitiesFor(entrypoint, packageSummary = {}) {
|
|
346
346
|
const capabilities = new Set();
|
|
347
347
|
for (const blocker of entrypoint.blockers) {
|
|
348
348
|
if (blocker.code === "dependency-install-required") {
|
|
@@ -361,7 +361,7 @@ function requiredCapabilitiesFor(entrypoint) {
|
|
|
361
361
|
capabilities.add("side-effect-sandbox");
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
|
-
if (
|
|
364
|
+
if (hasHostLinkedOpenClawDependency(packageSummary)) {
|
|
365
365
|
capabilities.add("target-openclaw-link");
|
|
366
366
|
}
|
|
367
367
|
capabilities.add("capture-shim");
|
|
@@ -369,6 +369,14 @@ function requiredCapabilitiesFor(entrypoint) {
|
|
|
369
369
|
return [...capabilities].sort();
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
function hasHostLinkedOpenClawDependency(packageSummary) {
|
|
373
|
+
return [
|
|
374
|
+
...(packageSummary.dependencies ?? []),
|
|
375
|
+
...(packageSummary.peerDependencies ?? []),
|
|
376
|
+
...(packageSummary.optionalDependencies ?? []),
|
|
377
|
+
].includes("openclaw");
|
|
378
|
+
}
|
|
379
|
+
|
|
372
380
|
function detectPackageManager(rootDir, packageDir, packageJson) {
|
|
373
381
|
const declared = typeof packageJson.packageManager === "string" ? packageJson.packageManager.split("@")[0] : null;
|
|
374
382
|
if (declared) {
|