@openclaw/plugin-inspector 0.3.7 → 0.3.9
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 +12 -0
- package/package.json +1 -1
- package/src/capture-api.js +1 -0
- package/src/capture-config.js +126 -0
- package/src/inspector.js +8 -2
- package/src/mock-sdk-capture-runner.js +7 -3
- package/src/sdk-mock.js +80 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.9 - 2026-05-03
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Follow bundled channel `loadBundledEntryExportSync` registration exports during mocked runtime capture.
|
|
10
|
+
|
|
11
|
+
## 0.3.8 - 2026-05-03
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Synthesize manifest config for isolated runtime capture so configured hooks can be observed without credentials.
|
|
16
|
+
|
|
5
17
|
## 0.3.7 - 2026-05-03
|
|
6
18
|
|
|
7
19
|
### Changed
|
package/package.json
CHANGED
package/src/capture-api.js
CHANGED
|
@@ -148,6 +148,7 @@ export function createCaptureContext(options = {}) {
|
|
|
148
148
|
config: options.config ?? {},
|
|
149
149
|
logger: options.logger ?? console,
|
|
150
150
|
pluginConfig: options.pluginConfig ?? {},
|
|
151
|
+
resolvePath: options.resolvePath ?? ((value) => value),
|
|
151
152
|
runtime: options.runtime ?? createRuntimeContext(options),
|
|
152
153
|
secrets: options.secrets ?? createSecretContext(options),
|
|
153
154
|
store: options.store ?? createStoreContext(options),
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export async function captureApiOptionsForPlugin(apiOptions = {}, options = {}) {
|
|
5
|
+
if (apiOptions.pluginConfig !== undefined || !options.pluginRoot) {
|
|
6
|
+
return apiOptions;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const pluginConfig = await readSamplePluginConfig(options.pluginRoot);
|
|
10
|
+
if (pluginConfig === undefined) {
|
|
11
|
+
return apiOptions;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
...apiOptions,
|
|
15
|
+
pluginConfig,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function readSamplePluginConfig(pluginRoot) {
|
|
20
|
+
const manifestPath = path.join(pluginRoot, "openclaw.plugin.json");
|
|
21
|
+
let manifest;
|
|
22
|
+
try {
|
|
23
|
+
manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
24
|
+
} catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sample = sampleJsonSchema(manifest.configSchema, { key: "config" });
|
|
29
|
+
return isPlainObject(sample) && Object.keys(sample).length > 0 ? sample : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sampleJsonSchema(schema, context = {}) {
|
|
33
|
+
if (!isPlainObject(schema)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
38
|
+
return schema.enum[0];
|
|
39
|
+
}
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(schema, "const")) {
|
|
41
|
+
return schema.const;
|
|
42
|
+
}
|
|
43
|
+
if (Object.prototype.hasOwnProperty.call(schema, "default")) {
|
|
44
|
+
return schema.default;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const type = Array.isArray(schema.type) ? schema.type.find((item) => item !== "null") : schema.type;
|
|
48
|
+
if (type === "object" || schema.properties) {
|
|
49
|
+
return sampleObjectSchema(schema);
|
|
50
|
+
}
|
|
51
|
+
if (type === "array") {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
if (type === "boolean") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (type === "number" || type === "integer") {
|
|
58
|
+
return typeof schema.minimum === "number" ? schema.minimum : 1;
|
|
59
|
+
}
|
|
60
|
+
if (type === "string" || !type) {
|
|
61
|
+
return sampleString(context.key);
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sampleObjectSchema(schema) {
|
|
67
|
+
const properties = isPlainObject(schema.properties) ? schema.properties : {};
|
|
68
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
69
|
+
const output = {};
|
|
70
|
+
|
|
71
|
+
for (const key of Object.keys(properties)) {
|
|
72
|
+
if (required.has(key)) {
|
|
73
|
+
const value = sampleJsonSchema(properties[key], { key });
|
|
74
|
+
if (value !== undefined) {
|
|
75
|
+
output[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const key of Object.keys(properties)) {
|
|
81
|
+
if (properties[key]?.type === "boolean") {
|
|
82
|
+
output[key] = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (Object.keys(output).length === 0 && Number(schema.minProperties ?? 0) > 0) {
|
|
87
|
+
const key = preferredSamplePropertyKey(properties);
|
|
88
|
+
if (key) {
|
|
89
|
+
const value = sampleJsonSchema(properties[key], { key });
|
|
90
|
+
if (value !== undefined) {
|
|
91
|
+
output[key] = value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return output;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function preferredSamplePropertyKey(properties) {
|
|
100
|
+
for (const key of ["provider", "model", "apiKey", "id", "name", ...Object.keys(properties)]) {
|
|
101
|
+
if (Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
102
|
+
return key;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sampleString(key = "") {
|
|
109
|
+
if (key === "provider") {
|
|
110
|
+
return "openai";
|
|
111
|
+
}
|
|
112
|
+
if (key === "model") {
|
|
113
|
+
return "text-embedding-3-small";
|
|
114
|
+
}
|
|
115
|
+
if (key === "apiKey") {
|
|
116
|
+
return "fixture-api-key";
|
|
117
|
+
}
|
|
118
|
+
if (key === "dbPath") {
|
|
119
|
+
return ".plugin-inspector/state/lancedb";
|
|
120
|
+
}
|
|
121
|
+
return "fixture";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isPlainObject(value) {
|
|
125
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
126
|
+
}
|
package/src/inspector.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { createCaptureApi } from "./capture-api.js";
|
|
8
|
+
import { captureApiOptionsForPlugin } from "./capture-config.js";
|
|
8
9
|
import { fixtureCheckoutPath, fixtureSourceRoot } from "./config.js";
|
|
9
10
|
import { buildCompatibilityFixtureReport } from "./fixture-summary.js";
|
|
10
11
|
import { readOpenClawTargetSurface } from "./openclaw-target.js";
|
|
@@ -188,7 +189,12 @@ export async function captureEntrypoint(entrypoint, options = {}) {
|
|
|
188
189
|
};
|
|
189
190
|
}
|
|
190
191
|
|
|
191
|
-
const
|
|
192
|
+
const apiOptions = await captureApiOptionsForPlugin(options.apiOptions, {
|
|
193
|
+
pluginRoot: options.pluginRoot
|
|
194
|
+
? path.resolve(options.cwd ?? process.cwd(), options.pluginRoot)
|
|
195
|
+
: path.dirname(resolvedEntrypoint),
|
|
196
|
+
});
|
|
197
|
+
const api = createCaptureApi(apiOptions);
|
|
192
198
|
try {
|
|
193
199
|
await register(api);
|
|
194
200
|
} catch (error) {
|
|
@@ -199,7 +205,7 @@ export async function captureEntrypoint(entrypoint, options = {}) {
|
|
|
199
205
|
entrypoint: resolvedEntrypoint,
|
|
200
206
|
captured: api.getCapturedContracts(),
|
|
201
207
|
};
|
|
202
|
-
if (
|
|
208
|
+
if (apiOptions?.retainHandlers === true) {
|
|
203
209
|
result.retained = api.getRetainedContracts();
|
|
204
210
|
}
|
|
205
211
|
return result;
|
|
@@ -6,6 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
import { createCaptureApi } from "./capture-api.js";
|
|
9
|
+
import { captureApiOptionsForPlugin } from "./capture-config.js";
|
|
9
10
|
import { createMockSdkPackage } from "./sdk-mock.js";
|
|
10
11
|
|
|
11
12
|
const options = JSON.parse(process.argv[2] ?? "{}");
|
|
@@ -30,7 +31,7 @@ async function run(options) {
|
|
|
30
31
|
cleanupTempDirOnExit(workspace);
|
|
31
32
|
const { loaderPath } = await createMockSdkPackage(workspace, { pluginRoot });
|
|
32
33
|
register(pathToFileURL(loaderPath));
|
|
33
|
-
return await captureLinkedEntrypoint(entrypoint, options);
|
|
34
|
+
return await captureLinkedEntrypoint(entrypoint, { ...options, pluginRoot });
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function cleanupTempDirOnExit(dir) {
|
|
@@ -65,7 +66,10 @@ async function captureLinkedEntrypoint(entrypoint, options) {
|
|
|
65
66
|
);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const
|
|
69
|
+
const apiOptions = await captureApiOptionsForPlugin(options.apiOptions, {
|
|
70
|
+
pluginRoot: options.pluginRoot,
|
|
71
|
+
});
|
|
72
|
+
const api = createCaptureApi(apiOptions);
|
|
69
73
|
try {
|
|
70
74
|
await register(api);
|
|
71
75
|
} catch (error) {
|
|
@@ -80,7 +84,7 @@ async function captureLinkedEntrypoint(entrypoint, options) {
|
|
|
80
84
|
mockSdk: true,
|
|
81
85
|
captured: api.getCapturedContracts(),
|
|
82
86
|
};
|
|
83
|
-
if (
|
|
87
|
+
if (apiOptions?.retainHandlers === true) {
|
|
84
88
|
result.retained = api.getRetainedContracts();
|
|
85
89
|
}
|
|
86
90
|
return withProcessOutput(result, outputCapture);
|
package/src/sdk-mock.js
CHANGED
|
@@ -551,6 +551,9 @@ function genericExportStatement(name) {
|
|
|
551
551
|
if (name === "defineBundledChannelSetupEntry") {
|
|
552
552
|
return "export { defineBundledChannelSetupEntry };";
|
|
553
553
|
}
|
|
554
|
+
if (name === "loadBundledEntryExportSync") {
|
|
555
|
+
return "export { loadBundledEntryExportSync };";
|
|
556
|
+
}
|
|
554
557
|
if (/^[A-Z].*Schema$/u.test(name)) {
|
|
555
558
|
return `export const ${name} = createSchema();`;
|
|
556
559
|
}
|
|
@@ -558,7 +561,13 @@ function genericExportStatement(name) {
|
|
|
558
561
|
}
|
|
559
562
|
|
|
560
563
|
function genericMockRuntimeSource(options = {}) {
|
|
561
|
-
return `${options.includeSdkRuntime ? `
|
|
564
|
+
return `${options.includeSdkRuntime ? `import { existsSync } from "node:fs";
|
|
565
|
+
import path from "node:path";
|
|
566
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
567
|
+
|
|
568
|
+
const pendingBundledEntryLoads = new Set();
|
|
569
|
+
|
|
570
|
+
function definePluginEntry(entry) {
|
|
562
571
|
if (entry && typeof entry === "object" && typeof entry.register === "function") {
|
|
563
572
|
return entry;
|
|
564
573
|
}
|
|
@@ -572,7 +581,7 @@ function defineBundledChannelEntry(entry = {}) {
|
|
|
572
581
|
return {
|
|
573
582
|
...entry,
|
|
574
583
|
kind: "bundled-channel-entry",
|
|
575
|
-
register(api) {
|
|
584
|
+
async register(api) {
|
|
576
585
|
if (api?.registrationMode === "cli-metadata") {
|
|
577
586
|
return entry.registerCliMetadata?.(api);
|
|
578
587
|
}
|
|
@@ -585,7 +594,12 @@ function defineBundledChannelEntry(entry = {}) {
|
|
|
585
594
|
});
|
|
586
595
|
}
|
|
587
596
|
entry.registerCliMetadata?.(api);
|
|
588
|
-
|
|
597
|
+
const result = entry.registerFull?.(api);
|
|
598
|
+
if (result && typeof result.then === "function") {
|
|
599
|
+
await result;
|
|
600
|
+
}
|
|
601
|
+
await drainBundledEntryLoads();
|
|
602
|
+
return result;
|
|
589
603
|
},
|
|
590
604
|
};
|
|
591
605
|
}
|
|
@@ -596,6 +610,46 @@ function defineBundledChannelSetupEntry(entry = {}) {
|
|
|
596
610
|
kind: "bundled-channel-setup-entry",
|
|
597
611
|
};
|
|
598
612
|
}
|
|
613
|
+
|
|
614
|
+
function loadBundledEntryExportSync(importMetaUrl, options = {}) {
|
|
615
|
+
return (...args) => {
|
|
616
|
+
const promise = import(resolveBundledEntryUrl(importMetaUrl, options.specifier)).then((module) => {
|
|
617
|
+
const loaded = module[options.exportName] ?? module.default;
|
|
618
|
+
return typeof loaded === "function" ? loaded(...args) : loaded;
|
|
619
|
+
});
|
|
620
|
+
pendingBundledEntryLoads.add(promise);
|
|
621
|
+
promise.finally(() => pendingBundledEntryLoads.delete(promise));
|
|
622
|
+
return promise;
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
async function drainBundledEntryLoads() {
|
|
627
|
+
while (pendingBundledEntryLoads.size > 0) {
|
|
628
|
+
await Promise.all([...pendingBundledEntryLoads]);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function resolveBundledEntryUrl(importMetaUrl, specifier) {
|
|
633
|
+
const basePath = fileURLToPath(importMetaUrl);
|
|
634
|
+
const target = specifier ? path.resolve(path.dirname(basePath), specifier) : basePath;
|
|
635
|
+
const resolved = resolveExistingSourcePath(target);
|
|
636
|
+
return pathToFileURL(resolved).href;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function resolveExistingSourcePath(target) {
|
|
640
|
+
if (existsSync(target)) {
|
|
641
|
+
return target;
|
|
642
|
+
}
|
|
643
|
+
const parsed = path.parse(target);
|
|
644
|
+
const withoutJsExtension = [".js", ".mjs", ".cjs"].includes(parsed.ext) ? path.join(parsed.dir, parsed.name) : null;
|
|
645
|
+
const candidates = [
|
|
646
|
+
...(withoutJsExtension ? [\`\${withoutJsExtension}.ts\`, \`\${withoutJsExtension}.mts\`, \`\${withoutJsExtension}.cts\`] : []),
|
|
647
|
+
\`\${target}.js\`,
|
|
648
|
+
\`\${target}.mjs\`,
|
|
649
|
+
\`\${target}.ts\`,
|
|
650
|
+
];
|
|
651
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? target;
|
|
652
|
+
}
|
|
599
653
|
` : ""}
|
|
600
654
|
function createMockValue(name) {
|
|
601
655
|
function fn(...args) {
|
|
@@ -1122,16 +1176,36 @@ export function normalizeSecretInputString(value) {
|
|
|
1122
1176
|
return String(value ?? "").trim();
|
|
1123
1177
|
}
|
|
1124
1178
|
|
|
1179
|
+
function createSimpleSchema(defaultValue) {
|
|
1180
|
+
return {
|
|
1181
|
+
parse(value) {
|
|
1182
|
+
return value === undefined ? defaultValue : value;
|
|
1183
|
+
},
|
|
1184
|
+
default(value) {
|
|
1185
|
+
return createSimpleSchema(value);
|
|
1186
|
+
},
|
|
1187
|
+
optional() {
|
|
1188
|
+
return this;
|
|
1189
|
+
},
|
|
1190
|
+
nullable() {
|
|
1191
|
+
return this;
|
|
1192
|
+
},
|
|
1193
|
+
nullish() {
|
|
1194
|
+
return this;
|
|
1195
|
+
},
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1125
1199
|
export function buildSecretInputSchema() {
|
|
1126
|
-
return
|
|
1200
|
+
return createSimpleSchema();
|
|
1127
1201
|
}
|
|
1128
1202
|
|
|
1129
1203
|
export function buildOptionalSecretInputSchema() {
|
|
1130
|
-
return
|
|
1204
|
+
return createSimpleSchema();
|
|
1131
1205
|
}
|
|
1132
1206
|
|
|
1133
1207
|
export function buildSecretInputArraySchema() {
|
|
1134
|
-
return
|
|
1208
|
+
return createSimpleSchema([]);
|
|
1135
1209
|
}
|
|
1136
1210
|
|
|
1137
1211
|
export function registerPluginHttpRoute(options = {}) {
|