@openclaw/plugin-inspector 0.3.22 → 0.3.24
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 +13 -0
- package/README.md +6 -0
- package/package.json +1 -1
- package/src/inspector.js +11 -37
- package/src/synthetic-probes.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.24 - 2026-08-31
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Recognize compiled CommonJS plugin factory calls when checking expected channel registrations, preserving source references and excluding factory values passed to wrappers.
|
|
10
|
+
- Classify widget presenters as metadata-only synthetic probes without invoking presentation callbacks.
|
|
11
|
+
|
|
12
|
+
## 0.3.23 - 2026-08-30
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Mask source comments in spans so inspecting large bundled JavaScript stays within serverless memory limits while preserving source offsets and findings.
|
|
17
|
+
|
|
5
18
|
## 0.3.22 - 2026-08-30
|
|
6
19
|
|
|
7
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -208,6 +208,8 @@ plugin-inspector --help
|
|
|
208
208
|
|
|
209
209
|
## Runtime Capture
|
|
210
210
|
|
|
211
|
+
Static inspection recognizes direct plugin factory calls and compiled CommonJS
|
|
212
|
+
calls such as `(0, sdk.defineBundledChannelEntry)(...)` without importing them.
|
|
211
213
|
Runtime capture imports plugin entrypoints in an isolated subprocess and records
|
|
212
214
|
what `register(api)` does. Use it when static inspection cannot prove the actual
|
|
213
215
|
registrations made at runtime.
|
|
@@ -230,6 +232,10 @@ That keeps compatibility CI offline and credential-free. It does not call live
|
|
|
230
232
|
services, launch OpenClaw, run provider SDKs, or emulate service lifecycle side
|
|
231
233
|
effects.
|
|
232
234
|
|
|
235
|
+
Synthetic probes classify widget presenters as metadata-only. They record the
|
|
236
|
+
registration without calling its match, availability, or presentation callbacks,
|
|
237
|
+
including when channel, provider, or lifecycle execution is enabled.
|
|
238
|
+
|
|
233
239
|
Use `--real-sdk` only when the plugin workspace already has real SDK
|
|
234
240
|
dependencies installed and you intentionally want that path.
|
|
235
241
|
|
package/package.json
CHANGED
package/src/inspector.js
CHANGED
|
@@ -15,6 +15,9 @@ import { buildCompatibilityReport, buildReport } from "./report.js";
|
|
|
15
15
|
import { inspectSdkDeprecations } from "./sdk-deprecation-rules.js";
|
|
16
16
|
|
|
17
17
|
const execFileAsync = promisify(execFile);
|
|
18
|
+
const pluginFactoryNames = "defineBundledChannelEntry|defineChannelPluginEntry|createChatChannelPlugin|definePluginEntry";
|
|
19
|
+
// Bundlers emit unbound calls as (0, sdk.factory)(...), including inline require receivers.
|
|
20
|
+
const compiledFactoryCall = new RegExp(String.raw`\(\s*0\s*,\s*(?:require\s*\(\s*(?:"[^"\r\n]*"|'[^'\r\n]*')\s*\)|[$A-Z_a-z][$\w]*)(?:\s*\.\s*[$A-Z_a-z][$\w]*)*\s*\.\s*(${pluginFactoryNames})\s*\)\s*\(`, "dg");
|
|
18
21
|
const registrationEquivalents = new Map([
|
|
19
22
|
["registerChannel", new Set(["createChatChannelPlugin", "defineBundledChannelEntry", "defineChannelPluginEntry", "registerChannel"])],
|
|
20
23
|
]);
|
|
@@ -164,10 +167,8 @@ export function inspectSourceText(text, filePath = "source.js") {
|
|
|
164
167
|
const hooks = collectDetailedMatches(searchableText, /\bapi\.on\(\s*["'`]([^"'`]+)["'`]/g, filePath, "name");
|
|
165
168
|
const registrations = [
|
|
166
169
|
...collectDetailedMatches(searchableText, /\bapi\.(register[A-Za-z0-9]+)\s*\(/g, filePath, "name"),
|
|
167
|
-
...collectDetailedMatches(searchableText,
|
|
168
|
-
...collectDetailedMatches(searchableText,
|
|
169
|
-
...collectDetailedMatches(searchableText, /\b(createChatChannelPlugin)\s*\(/g, filePath, "name"),
|
|
170
|
-
...collectDetailedMatches(searchableText, /\b(definePluginEntry)\s*\(/g, filePath, "name"),
|
|
170
|
+
...collectDetailedMatches(searchableText, new RegExp(String.raw`\b(${pluginFactoryNames})\s*\(`, "g"), filePath, "name"),
|
|
171
|
+
...collectDetailedMatches(searchableText, compiledFactoryCall, filePath, "name"),
|
|
171
172
|
];
|
|
172
173
|
const sdkImports = collectSdkImports(searchableText, filePath);
|
|
173
174
|
const sdkDeprecations = inspectSdkDeprecations(searchableText, filePath);
|
|
@@ -373,7 +374,7 @@ function emptyInspection(fixture, status) {
|
|
|
373
374
|
function collectDetailedMatches(text, regex, filePath, key) {
|
|
374
375
|
const details = [];
|
|
375
376
|
for (const match of text.matchAll(regex)) {
|
|
376
|
-
const line = lineForOffset(text, match.index ?? 0);
|
|
377
|
+
const line = lineForOffset(text, match.indices?.[1]?.[0] ?? match.index ?? 0);
|
|
377
378
|
details.push({
|
|
378
379
|
[key]: match[1],
|
|
379
380
|
file: filePath,
|
|
@@ -727,38 +728,11 @@ function lineForOffset(text, offset) {
|
|
|
727
728
|
}
|
|
728
729
|
|
|
729
730
|
function stripComments(text) {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
result += " ";
|
|
736
|
-
index += 2;
|
|
737
|
-
while (index < text.length && !(text[index] === "*" && text[index + 1] === "/")) {
|
|
738
|
-
result += blankCommentChar(text[index]);
|
|
739
|
-
index += 1;
|
|
740
|
-
}
|
|
741
|
-
if (index < text.length) {
|
|
742
|
-
result += " ";
|
|
743
|
-
index += 1;
|
|
744
|
-
}
|
|
745
|
-
} else if (char === "/" && next === "/") {
|
|
746
|
-
result += " ";
|
|
747
|
-
index += 2;
|
|
748
|
-
while (index < text.length && text[index] !== "\n" && text[index] !== "\r") {
|
|
749
|
-
result += " ";
|
|
750
|
-
index += 1;
|
|
751
|
-
}
|
|
752
|
-
index -= 1;
|
|
753
|
-
} else {
|
|
754
|
-
result += char;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
return result;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
function blankCommentChar(char) {
|
|
761
|
-
return char === "\n" || char === "\r" ? char : " ";
|
|
731
|
+
// Mask spans rather than building one string node per UTF-16 code unit.
|
|
732
|
+
// Keep offsets and CR/LF intact for findings, including unterminated comments.
|
|
733
|
+
return text.replace(/\/\*[\s\S]*?(?:\*\/|$)|\/\/[^\r\n]*/g, (comment) =>
|
|
734
|
+
comment.replace(/[^\r\n]+/g, (span) => " ".repeat(span.length)),
|
|
735
|
+
);
|
|
762
736
|
}
|
|
763
737
|
|
|
764
738
|
function sortDetails(details) {
|
package/src/synthetic-probes.js
CHANGED
|
@@ -295,6 +295,11 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
295
295
|
callableProperties: [],
|
|
296
296
|
reason: "web search providers are captured as registration metadata before provider runtime execution",
|
|
297
297
|
},
|
|
298
|
+
registerWidgetPresenter: {
|
|
299
|
+
mode: "metadata-only",
|
|
300
|
+
callableProperties: [],
|
|
301
|
+
reason: "widget presenters are captured as registration metadata before presentation runtime execution",
|
|
302
|
+
},
|
|
298
303
|
};
|
|
299
304
|
|
|
300
305
|
export const defaultSyntheticHookEvents = {
|