@openclaw/plugin-inspector 0.3.7 → 0.3.8
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 +6 -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/CHANGELOG.md
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);
|