@openclaw/plugin-inspector 0.1.1 → 0.1.3

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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3 - 2026-04-27
4
+
5
+ ### Added
6
+
7
+ - Add reserved bundled-plugin SDK import detection so external plugins get explicit compatibility findings for private OpenClaw SDK shims.
8
+ - Add packaged workspace capture and synthetic-probe helper CLIs for generated isolated workspace plans.
9
+
10
+ ### Changed
11
+
12
+ - Make `plugin-inspector ci` write compatibility-backed CI summary artifacts instead of the legacy inventory report.
13
+ - Default packaged helper captures to the mocked OpenClaw SDK while preserving `--real-sdk` opt-in behavior.
14
+ - Detect the default runtime capture artifact at `reports/plugin-inspector-runtime-capture.json`.
15
+ - Report the actual log count in CLI text summaries.
16
+
17
+ ## 0.1.2 - 2026-04-27
18
+
19
+ ### Added
20
+
21
+ - Add public SDK subpath mocks, registrar return profiles, richer lifecycle capture probes, and plugin-root `init`/`--plugin-root` CLI flows.
22
+
23
+ ### Changed
24
+
25
+ - Harden runtime capture for TypeScript entrypoints, extensionless local imports, mocked external packages, namespace imports, async output capture, and explicit mock/real SDK lanes.
26
+ - Refresh README and CI examples around `npx @openclaw/plugin-inspector check --no-openclaw` and opt-in runtime capture.
27
+
3
28
  ## 0.1.1 - 2026-04-27
4
29
 
5
30
  ### Changed
package/README.md CHANGED
@@ -1,36 +1,46 @@
1
1
  <img src="docs/plugin-inspector-banner.jpg" alt="openclaw plugin inspector banner"/>
2
2
 
3
- # 💊 OpenClaw Plugin Inspector
3
+ # OpenClaw Plugin Inspector
4
4
 
5
- `plugin-inspector` is the reusable OpenClaw plugin compatibility inspector. It
6
- wraps the static inspection, registration capture, and report model prototyped
7
- in crabpot into an npm-publishable package.
5
+ `plugin-inspector` is the offline compatibility check for OpenClaw plugins. Run
6
+ it from a plugin root to inspect package metadata, `openclaw.plugin.json`, SDK
7
+ imports, `api.on(...)`, `api.register*`, and optional runtime registration
8
+ capture.
8
9
 
9
- ## Install
10
+ ## Quick Start
10
11
 
11
- Install it as a dev dependency in a plugin repo:
12
+ From a plugin package directory:
12
13
 
13
14
  ```bash
14
- npm install --save-dev @openclaw/plugin-inspector
15
+ npx @openclaw/plugin-inspector
15
16
  ```
16
17
 
17
- Then run it from the plugin root:
18
+ That runs `check`, writes report artifacts to `reports/`, and exits non-zero
19
+ when compatibility breakages are found.
20
+
21
+ Add a local config and GitHub Actions workflow:
18
22
 
19
23
  ```bash
20
- npx @openclaw/plugin-inspector check
24
+ npx @openclaw/plugin-inspector init --ci
21
25
  ```
22
26
 
23
- ## CLI
27
+ Or install it as a dev dependency:
24
28
 
25
- Run the default plugin-root check from a plugin package directory:
29
+ ```bash
30
+ npm install --save-dev @openclaw/plugin-inspector
31
+ npx plugin-inspector check
32
+ ```
33
+
34
+ ## Commands
26
35
 
27
36
  ```bash
28
- plugin-inspector check
37
+ npx @openclaw/plugin-inspector check
38
+ npx @openclaw/plugin-inspector check --plugin-root ./plugins/weather
39
+ npx @openclaw/plugin-inspector init --ci --package-manager pnpm
29
40
  ```
30
41
 
31
- That command reads the current directory as one plugin, inspects package
32
- metadata, `openclaw.plugin.json`, source imports, `api.on(...)`,
33
- `api.register*`, and writes:
42
+ `check` reads the current directory as one plugin unless `--plugin-root` is set.
43
+ It writes:
34
44
 
35
45
  - `reports/plugin-inspector-report.json`
36
46
  - `reports/plugin-inspector-report.md`
@@ -43,8 +53,8 @@ checkout:
43
53
  plugin-inspector check --no-openclaw
44
54
  ```
45
55
 
46
- Use a simple plugin-root config when you want stable fixture metadata or
47
- expected seams:
56
+ Use `plugin-inspector.config.json` when CI needs stable fixture metadata,
57
+ expected seams, or runtime capture defaults:
48
58
 
49
59
  ```json
50
60
  {
@@ -58,6 +68,9 @@ expected seams:
58
68
  "registrations": ["registerTool"]
59
69
  }
60
70
  },
71
+ "capture": {
72
+ "mockSdk": true
73
+ },
61
74
  "openclaw": {
62
75
  "defaultCheckoutPath": "../openclaw"
63
76
  }
@@ -70,48 +83,51 @@ Then run:
70
83
  plugin-inspector check --config plugin-inspector.config.json
71
84
  ```
72
85
 
73
- Copy-ready examples live in `examples/plugin-inspector.config.json` and
86
+ `init --ci` writes this shape for you, plus
87
+ `.github/workflows/plugin-inspector.yml`. Copy-ready examples also live in
88
+ `examples/plugin-inspector.config.json` and
74
89
  `examples/github-actions-plugin-inspector.yml`.
75
90
 
76
- Fixture-set configs are still supported for crabpot-style compatibility suites:
77
-
78
- ```bash
79
- plugin-inspector report --config crabpot.config.json --out reports
80
- ```
91
+ ## Runtime Capture
81
92
 
82
- Capture a plugin entrypoint in an explicitly isolated execution lane:
93
+ Runtime capture imports plugin entrypoints in an isolated subprocess and records
94
+ the registrations made during `register(api)`. It is opt-in because it executes
95
+ plugin code:
83
96
 
84
97
  ```bash
85
- PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector capture ./dist/index.js --mock-sdk
98
+ PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 npx @openclaw/plugin-inspector check --runtime --mock-sdk
86
99
  ```
87
100
 
88
- Run the optional runtime capture smoke during `check`:
89
-
90
- ```bash
91
- PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --no-openclaw --capture
92
- ```
101
+ By default, runtime capture uses a generated mock for `openclaw/plugin-sdk` and
102
+ common external packages so plugin code can load in clean CI without OpenClaw
103
+ installed. Use `--real-sdk` only when the plugin workspace already has real SDK
104
+ dependencies installed and you intentionally want to test that path.
93
105
 
94
- Runtime capture creates a temporary mock `openclaw/plugin-sdk` package, imports
95
- declared OpenClaw package entrypoints, calls their `register(api)` function with
96
- the capture API, and writes:
106
+ Runtime capture writes:
97
107
 
98
108
  - `reports/plugin-inspector-runtime-capture.json`
99
109
  - `reports/plugin-inspector-runtime-capture.md`
100
110
 
101
- ### CI
111
+ You can also capture one entrypoint directly:
102
112
 
103
- With a dev dependency:
113
+ ```bash
114
+ PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector capture ./dist/index.js --mock-sdk
115
+ ```
116
+
117
+ ## CI
118
+
119
+ Minimal package scripts:
104
120
 
105
121
  ```json
106
122
  {
107
123
  "scripts": {
108
124
  "plugin:check": "plugin-inspector check --no-openclaw",
109
- "plugin:check:runtime": "PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --no-openclaw --capture"
125
+ "plugin:check:runtime": "PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 plugin-inspector check --no-openclaw --runtime --mock-sdk"
110
126
  }
111
127
  }
112
128
  ```
113
129
 
114
- GitHub Actions:
130
+ GitHub Actions without a local dev dependency:
115
131
 
116
132
  ```yaml
117
133
  name: plugin-inspector
@@ -131,8 +147,8 @@ jobs:
131
147
  node-version: 24
132
148
  cache: npm
133
149
  - run: npm ci
134
- - run: npm run plugin:check
135
- - run: npm run plugin:check:runtime
150
+ - run: npx @openclaw/plugin-inspector check --no-openclaw
151
+ - run: PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 npx @openclaw/plugin-inspector check --no-openclaw --runtime --mock-sdk
136
152
  - uses: actions/upload-artifact@v5
137
153
  if: always()
138
154
  with:
@@ -140,6 +156,31 @@ jobs:
140
156
  path: reports/plugin-inspector-*
141
157
  ```
142
158
 
159
+ ## Fixture Suites
160
+
161
+ Fixture-set configs are still supported for crabpot-style compatibility suites:
162
+
163
+ ```bash
164
+ plugin-inspector report --config crabpot.config.json --out reports
165
+ ```
166
+
167
+ Use fixture suites when one repo wants to inspect many plugins. Use plugin-root
168
+ `check` for normal plugin CI.
169
+
170
+ ## Mocking Model
171
+
172
+ Default inspection is static, offline, and credential-free. Runtime capture is
173
+ the only mode that imports plugin code.
174
+
175
+ When `--mock-sdk` is enabled, the inspector generates temporary modules for
176
+ `openclaw/plugin-sdk` subpaths and unresolved external packages discovered in
177
+ the plugin import graph. The mock SDK captures registrations; it does not call
178
+ network services, launch OpenClaw, run provider SDKs, or emulate service
179
+ lifecycle side effects.
180
+
181
+ Use the mock lane for plugin compatibility CI. Keep live provider/service tests
182
+ in the plugin repo behind their own credentials and explicit opt-in flags.
183
+
143
184
  ## Scope
144
185
 
145
186
  Default inspection is offline and credential-free. It reads manifests, package
@@ -148,5 +189,5 @@ metadata, and source files, then reports observed `api.on(...)`,
148
189
  OpenClaw target checkout parsing is limited to public compatibility registries,
149
190
  SDK package exports, manifest types, hooks, and captured registrar metadata.
150
191
 
151
- Cold import capture and synthetic contract probes are explicit opt-in modes.
152
- Live lanes will stay credential-gated and must never run in default CI.
192
+ Cold import capture, synthetic contract probes, and runtime capture are explicit
193
+ opt-in modes. Live lanes stay credential-gated and must never run in default CI.
@@ -15,8 +15,8 @@ jobs:
15
15
  node-version: 24
16
16
  cache: npm
17
17
  - run: npm ci
18
- - run: npm run plugin:check
19
- - run: npm run plugin:check:runtime
18
+ - run: npx @openclaw/plugin-inspector check --no-openclaw
19
+ - run: PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 npx @openclaw/plugin-inspector check --no-openclaw --runtime --mock-sdk
20
20
  - uses: actions/upload-artifact@v5
21
21
  if: always()
22
22
  with:
@@ -9,6 +9,9 @@
9
9
  "registrations": ["registerTool"]
10
10
  }
11
11
  },
12
+ "capture": {
13
+ "mockSdk": true
14
+ },
12
15
  "openclaw": {
13
16
  "defaultCheckoutPath": "../openclaw"
14
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/plugin-inspector",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Offline compatibility inspector for OpenClaw plugins.",
6
6
  "type": "module",
package/src/advanced.js CHANGED
@@ -100,6 +100,7 @@ export {
100
100
  openClawTargetPathCandidates,
101
101
  parseCompatRecordEntries,
102
102
  parseExportedStringArray,
103
+ parsePluginSdkEntrypointSpecifiers,
103
104
  parsePluginSdkExports,
104
105
  parseTypeFields,
105
106
  readOpenClawTargetSurface,
@@ -116,12 +117,21 @@ export {
116
117
  defaultPluginRootConfigFiles,
117
118
  fixtureCheckoutPath,
118
119
  fixtureSourceRoot,
120
+ inferPluginSeams,
119
121
  loadInspectorConfig,
120
122
  loadPluginRootConfig,
121
123
  normalizeInspectorConfig,
122
124
  normalizePluginRootConfig,
125
+ packageId,
123
126
  validateInspectorConfig,
124
127
  } from "./config.js";
128
+ export {
129
+ buildPluginInspectorConfig,
130
+ defaultInitConfigPath,
131
+ defaultInitWorkflowPath,
132
+ renderGithubActionsWorkflow,
133
+ writePluginInspectorInit,
134
+ } from "./init.js";
125
135
  export {
126
136
  buildPlatformProbes,
127
137
  defaultPlatformTargets,
package/src/api.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { createCaptureApi } from "./capture-api.js";
3
3
  import { loadInspectorConfig, loadPluginRootConfig } from "./config.js";
4
+ import { writePluginInspectorInit } from "./init.js";
4
5
  import { captureEntrypoint } from "./inspector.js";
5
6
  import { renderTextSummary, writeCompatibilityReport } from "./report.js";
6
7
  import { buildRuntimeCaptureReport, writeRuntimeCaptureReport } from "./runtime-capture-report.js";
@@ -45,24 +46,25 @@ export async function writePluginReports(report, options = {}) {
45
46
 
46
47
  export async function runPluginCheck(options = {}) {
47
48
  const outDir = options.outDir ?? "reports";
48
- const report = await inspectPluginRoot(options);
49
- const paths = await writePluginReports(report, { ...options, outDir });
49
+ const config = await loadPluginConfig(options);
50
+ const report = await inspectPluginRoot({ ...options, config });
51
+ const paths = await writePluginReports(report, { ...options, pluginRoot: config.rootDir, outDir });
50
52
  const result = { report, paths };
53
+ const capture = options.capture ?? config.capture?.runtime ?? false;
54
+ const mockSdk = options.mockSdk ?? config.capture?.mockSdk ?? true;
51
55
 
52
- if (options.capture === true) {
56
+ if (capture === true) {
53
57
  if (process.env.PLUGIN_INSPECTOR_EXECUTE_ISOLATED !== "1") {
54
58
  throw new Error("runtime capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 in an isolated workspace");
55
59
  }
56
- const config = await loadPluginConfig(options);
57
60
  const runtimeCapture = await buildRuntimeCaptureReport({
58
- mockSdk: options.mockSdk ?? true,
61
+ mockSdk,
59
62
  report,
60
63
  rootDir: config.rootDir,
61
64
  });
62
- const outputRoot = options.cwd ?? options.pluginRoot ?? process.cwd();
63
65
  const runtimeCapturePaths = await writeRuntimeCaptureReport(runtimeCapture, {
64
- jsonPath: path.resolve(outputRoot, outDir, "plugin-inspector-runtime-capture.json"),
65
- markdownPath: path.resolve(outputRoot, outDir, "plugin-inspector-runtime-capture.md"),
66
+ jsonPath: path.resolve(config.rootDir, outDir, "plugin-inspector-runtime-capture.json"),
67
+ markdownPath: path.resolve(config.rootDir, outDir, "plugin-inspector-runtime-capture.md"),
66
68
  });
67
69
  result.runtimeCapture = runtimeCapture;
68
70
  result.runtimeCapturePaths = runtimeCapturePaths;
@@ -82,4 +84,8 @@ export async function capturePluginEntrypoint(entrypoint, options = {}) {
82
84
  return captureEntrypoint(entrypoint, options);
83
85
  }
84
86
 
87
+ export async function setupPluginInspector(options = {}) {
88
+ return writePluginInspectorInit(options);
89
+ }
90
+
85
91
  export { createCaptureApi, renderTextSummary };
@@ -1,15 +1,68 @@
1
+ export const defaultCaptureApiRegistrarProfiles = {
2
+ registerChannel: {
3
+ returnValue: ({ args }) => registrationObject(args, { id: "channel" }),
4
+ },
5
+ registerCli: {
6
+ returnValue: ({ args }) => registrationObject(args, { name: "cli" }),
7
+ },
8
+ registerCommand: {
9
+ returnValue: ({ args }) => registrationObject(args, { name: "command" }),
10
+ },
11
+ registerContextEngine: {
12
+ returnValue: ({ args }) => registrationObject(args, { id: "context-engine" }),
13
+ },
14
+ registerGatewayMethod: {
15
+ returnValue: ({ args }) => registrationObject(args, { name: "gateway.method" }),
16
+ },
17
+ registerHook: {
18
+ returnValue: ({ api }) => api,
19
+ },
20
+ registerHttpRoute: {
21
+ returnValue: ({ args }) => ({
22
+ ...registrationObject(args, { method: "GET", path: "/" }),
23
+ unregister() {},
24
+ }),
25
+ },
26
+ registerInteractiveHandler: {
27
+ returnValue: ({ args }) => registrationObject(args, { id: "interactive-handler" }),
28
+ },
29
+ registerMemoryPromptSection: {
30
+ returnValue: ({ args }) => registrationObject(args, { id: "memory-prompt-section" }),
31
+ },
32
+ registerMemoryRuntime: {
33
+ returnValue: ({ args }) => registrationObject(args, { id: "memory-runtime" }),
34
+ },
35
+ registerProvider: {
36
+ returnValue: ({ args }) => registrationObject(args, { id: "provider" }),
37
+ },
38
+ registerService: {
39
+ returnValue: ({ args }) => ({
40
+ ...registrationObject(args, { name: "service" }),
41
+ start: async () => undefined,
42
+ stop: async () => undefined,
43
+ }),
44
+ },
45
+ registerSpeechProvider: {
46
+ returnValue: ({ args }) => registrationObject(args, { id: "speech-provider" }),
47
+ },
48
+ registerTool: {
49
+ returnValue: ({ args }) => registrationObject(args, { name: "tool" }),
50
+ },
51
+ };
52
+
1
53
  export function createCaptureApi(options = {}) {
2
54
  const captured = [];
3
55
  const retained = [];
4
- const knownRegistrars = new Set(options.knownRegistrars ?? []);
56
+ const registrarProfiles = {
57
+ ...defaultCaptureApiRegistrarProfiles,
58
+ ...(options.registrarProfiles ?? {}),
59
+ };
60
+ const knownRegistrars = new Set(options.knownRegistrars ?? Object.keys(registrarProfiles));
5
61
  const retainHandlers = options.retainHandlers === true;
6
62
 
7
63
  const api = new Proxy(
8
64
  {
9
- config: options.config ?? {},
10
- logger: options.logger ?? console,
11
- pluginConfig: options.pluginConfig ?? {},
12
- runtime: options.runtime ?? {},
65
+ ...createCaptureContext(options),
13
66
  on(name, handler) {
14
67
  const captureIndex =
15
68
  captured.push({
@@ -42,6 +95,7 @@ export function createCaptureApi(options = {}) {
42
95
  }
43
96
  if (typeof property === "string" && isRegistrarProperty(property)) {
44
97
  return (...args) => {
98
+ const returnValue = registrationReturnValue(property, args, { api, registrarProfiles });
45
99
  const captureIndex =
46
100
  captured.push({
47
101
  kind: "registration",
@@ -54,10 +108,11 @@ export function createCaptureApi(options = {}) {
54
108
  kind: "registration",
55
109
  name: property,
56
110
  arguments: args,
111
+ returnValue,
57
112
  captureIndex,
58
113
  });
59
114
  }
60
- return registrationReturnValue(property, args);
115
+ return returnValue;
61
116
  };
62
117
  }
63
118
  return undefined;
@@ -68,19 +123,122 @@ export function createCaptureApi(options = {}) {
68
123
  return api;
69
124
  }
70
125
 
126
+ export function createCaptureContext(options = {}) {
127
+ return {
128
+ config: options.config ?? {},
129
+ logger: options.logger ?? console,
130
+ pluginConfig: options.pluginConfig ?? {},
131
+ runtime: options.runtime ?? createRuntimeContext(options),
132
+ secrets: options.secrets ?? createSecretContext(options),
133
+ store: options.store ?? createStoreContext(options),
134
+ paths: options.paths ?? {
135
+ cacheDir: ".plugin-inspector/cache",
136
+ configDir: ".plugin-inspector/config",
137
+ dataDir: ".plugin-inspector/data",
138
+ },
139
+ agent: options.agent ?? {
140
+ id: "plugin-inspector-agent",
141
+ accountId: "default",
142
+ },
143
+ gateway: options.gateway ?? {
144
+ baseUrl: "http://127.0.0.1:0",
145
+ registerRoute(route) {
146
+ return {
147
+ ...route,
148
+ unregister() {},
149
+ };
150
+ },
151
+ },
152
+ fetch: options.fetch ?? (async () => ({ ok: true, status: 200, json: async () => ({}), text: async () => "" })),
153
+ };
154
+ }
155
+
71
156
  function isRegistrarProperty(property) {
72
157
  return property.startsWith("register") || property.startsWith("define");
73
158
  }
74
159
 
75
- function registrationReturnValue(name, args) {
76
- if (name === "registerService") {
160
+ function registrationReturnValue(name, args, context) {
161
+ const profile = context.registrarProfiles[name];
162
+ if (profile?.returnValue) {
163
+ return profile.returnValue({ name, args, api: context.api });
164
+ }
165
+ return registrationObject(args, {});
166
+ }
167
+
168
+ function createRuntimeContext(options) {
169
+ const runtime = options.runtime ?? {};
170
+ return {
171
+ ...runtime,
172
+ agent: runtime.agent ?? {},
173
+ env: options.env ?? {},
174
+ logger: options.logger ?? console,
175
+ now: () => new Date(0),
176
+ tts: runtime.tts ?? {},
177
+ state: {
178
+ resolveStateDir: () => options.stateDir ?? process.cwd(),
179
+ ...(runtime.state ?? {}),
180
+ },
181
+ };
182
+ }
183
+
184
+ function createSecretContext(options) {
185
+ const secrets = new Map(Object.entries(options.secretValues ?? {}));
186
+ return {
187
+ async get(name) {
188
+ return secrets.get(name) ?? null;
189
+ },
190
+ async has(name) {
191
+ return secrets.has(name);
192
+ },
193
+ async require(name) {
194
+ if (!secrets.has(name)) {
195
+ throw new Error(`Missing mocked secret: ${name}`);
196
+ }
197
+ return secrets.get(name);
198
+ },
199
+ async resolve(value) {
200
+ return typeof value === "string" && value.startsWith("secret:") ? (secrets.get(value.slice(7)) ?? null) : value;
201
+ },
202
+ };
203
+ }
204
+
205
+ function createStoreContext() {
206
+ const values = new Map();
207
+ return {
208
+ async delete(key) {
209
+ return values.delete(key);
210
+ },
211
+ async get(key) {
212
+ return values.get(key);
213
+ },
214
+ async list() {
215
+ return [...values.keys()].sort();
216
+ },
217
+ async set(key, value) {
218
+ values.set(key, value);
219
+ return value;
220
+ },
221
+ };
222
+ }
223
+
224
+ function registrationObject(args, defaults) {
225
+ const first = args[0];
226
+ if (first && typeof first === "object") {
77
227
  return {
78
- name: objectName(args[0]),
79
- start: async () => undefined,
80
- stop: async () => undefined,
228
+ ...defaults,
229
+ ...first,
230
+ name: objectName(first) ?? defaults.name,
231
+ id: objectId(first) ?? defaults.id,
81
232
  };
82
233
  }
83
- return objectName(args[0]) ?? undefined;
234
+ if (typeof first === "string") {
235
+ return {
236
+ ...defaults,
237
+ name: first,
238
+ id: defaults.id,
239
+ };
240
+ }
241
+ return { ...defaults };
84
242
  }
85
243
 
86
244
  function summarizeArguments(args) {
@@ -113,3 +271,13 @@ function objectName(value) {
113
271
  }
114
272
  return typeof value.id === "string" ? value.id : null;
115
273
  }
274
+
275
+ function objectId(value) {
276
+ if (!value || typeof value !== "object") {
277
+ return null;
278
+ }
279
+ if (typeof value.id === "string") {
280
+ return value.id;
281
+ }
282
+ return typeof value.name === "string" ? value.name : null;
283
+ }
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { captureEntrypoint, writeArtifacts } from "./advanced.js";
3
+
4
+ const args = process.argv.slice(2);
5
+
6
+ try {
7
+ await run(args);
8
+ } catch (error) {
9
+ console.error(error.message);
10
+ process.exitCode = 1;
11
+ }
12
+
13
+ async function run(commandArgs) {
14
+ const entrypoint = commandArgs.find((arg) => !arg.startsWith("-"));
15
+ const outputPath = readFlag(commandArgs, "--output");
16
+ const pluginRoot = readFlag(commandArgs, "--plugin-root");
17
+ const mockSdk = readMockSdkFlag(commandArgs) ?? true;
18
+
19
+ if (!entrypoint) {
20
+ throw new Error("capture requires an entrypoint path");
21
+ }
22
+ if (process.env.PLUGIN_INSPECTOR_EXECUTE_ISOLATED !== "1") {
23
+ throw new Error("capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 in an isolated workspace");
24
+ }
25
+
26
+ const result = await captureEntrypoint(entrypoint, { mockSdk, pluginRoot });
27
+ const json = `${JSON.stringify(result, null, 2)}\n`;
28
+ if (outputPath) {
29
+ await writeArtifacts([{ path: outputPath, content: json }]);
30
+ } else {
31
+ process.stdout.write(json);
32
+ }
33
+ }
34
+
35
+ function readFlag(commandArgs, name) {
36
+ const index = commandArgs.indexOf(name);
37
+ if (index === -1) {
38
+ return null;
39
+ }
40
+ return commandArgs[index + 1] ?? null;
41
+ }
42
+
43
+ function readMockSdkFlag(commandArgs) {
44
+ const sdk = readFlag(commandArgs, "--sdk");
45
+ if (sdk === "mock") {
46
+ return true;
47
+ }
48
+ if (sdk === "real") {
49
+ return false;
50
+ }
51
+ if (sdk && !["mock", "real"].includes(sdk)) {
52
+ throw new Error("--sdk must be mock or real");
53
+ }
54
+ if (commandArgs.includes("--mock-sdk")) {
55
+ return true;
56
+ }
57
+ if (commandArgs.includes("--real-sdk")) {
58
+ return false;
59
+ }
60
+ return undefined;
61
+ }
package/src/ci-summary.js CHANGED
@@ -5,7 +5,7 @@ import { readOptionalJsonFile } from "./json-file.js";
5
5
 
6
6
  export const defaultCiReportPaths = {
7
7
  compatibility: "reports/plugin-inspector-report.json",
8
- capture: "reports/plugin-inspector-capture.json",
8
+ capture: "reports/plugin-inspector-runtime-capture.json",
9
9
  synthetic: "reports/plugin-inspector-synthetic-probes.json",
10
10
  coldImport: "reports/plugin-inspector-cold-import.json",
11
11
  workspace: "reports/plugin-inspector-workspace-plan.json",