@openclaw/plugin-inspector 0.2.0 → 0.3.0
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 +20 -0
- package/README.md +26 -5
- package/examples/circleci-plugin-inspector.yml +19 -0
- package/examples/github-actions-code-scanning.yml +31 -0
- package/examples/github-actions-plugin-inspector.yml +1 -1
- package/examples/gitlab-ci-plugin-inspector.yml +11 -0
- package/examples/package-json-plugin-inspector.json +1 -1
- package/package.json +4 -1
- package/src/advanced.js +10 -0
- package/src/api.js +58 -2
- package/src/capture-api.js +41 -3
- package/src/cli.js +48 -15
- package/src/index.js +5 -0
- package/src/init.js +90 -13
- package/src/mock-sdk-capture-runner.js +12 -8
- package/src/report.js +60 -8
- package/src/sdk-mock.js +202 -9
- package/src/synthetic-probe-suite.js +19 -0
- package/src/synthetic-probes-cli.js +16 -12
- package/src/synthetic-probes.js +55 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 - 2026-04-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `--allow-execute` as a cross-platform runtime capture opt-in flag.
|
|
8
|
+
- Add `plugin-inspector init --dry-run` for setup previews.
|
|
9
|
+
- Add `plugin-inspector init --json` for machine-readable setup summaries.
|
|
10
|
+
- Add `plugin-inspector init --scripts` for `plugin:check` and `plugin:ci` package scripts.
|
|
11
|
+
- Add public fixture-set report helpers and synthetic probe suite helpers for Crabpot and downstream compatibility suites.
|
|
12
|
+
- Add a Crabpot follow-through release checklist for source refs, package pins, and smoke commands.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Make generated runtime CI commands use `--allow-execute` instead of shell-specific inline environment syntax.
|
|
17
|
+
- Make `plugin-inspector init --ci` detect `packageManager` and common lockfiles before generating CI install/run commands.
|
|
18
|
+
- Make `plugin-inspector init` output repo-relative file paths and preflight generated files before writing.
|
|
19
|
+
- Make `plugin-inspector init` infer `sourceRoot: "src"` from package export maps like `"./src/index.js"`.
|
|
20
|
+
- Improve CLI failure summaries with report artifact paths and top blocking findings.
|
|
21
|
+
- Harden mock SDK capture by keeping generated loader fixtures available until subprocess exit.
|
|
22
|
+
|
|
3
23
|
## 0.2.0 - 2026-04-27
|
|
4
24
|
|
|
5
25
|
### Added
|
package/README.md
CHANGED
|
@@ -24,6 +24,11 @@ Add a local config and GitHub Actions workflow:
|
|
|
24
24
|
npx @openclaw/plugin-inspector init --ci
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
`init --ci` detects `packageManager` and common lockfiles. Pass
|
|
28
|
+
`--package-manager pnpm`, `npm`, `yarn`, or `bun` when you want to override it.
|
|
29
|
+
Add `--scripts` to write `plugin:check` and `plugin:ci` package scripts.
|
|
30
|
+
Use `--dry-run` to preview the files first.
|
|
31
|
+
|
|
27
32
|
Or install it as a dev dependency:
|
|
28
33
|
|
|
29
34
|
```bash
|
|
@@ -40,6 +45,8 @@ npx @openclaw/plugin-inspector ci --no-openclaw
|
|
|
40
45
|
npx @openclaw/plugin-inspector config
|
|
41
46
|
npx @openclaw/plugin-inspector check --plugin-root ./plugins/weather
|
|
42
47
|
npx @openclaw/plugin-inspector init --ci --package-manager pnpm
|
|
48
|
+
npx @openclaw/plugin-inspector init --ci --scripts --dry-run
|
|
49
|
+
npx @openclaw/plugin-inspector init --ci --scripts --dry-run --json
|
|
43
50
|
```
|
|
44
51
|
|
|
45
52
|
`check` and `inspect` read the current directory as one plugin unless
|
|
@@ -132,7 +139,8 @@ For a single plugin package, the same config can live in `package.json`:
|
|
|
132
139
|
`.github/workflows/plugin-inspector.yml`. Copy-ready examples also live in
|
|
133
140
|
`examples/plugin-inspector.config.json` and
|
|
134
141
|
`examples/package-json-plugin-inspector.json` and
|
|
135
|
-
`examples/github-actions-plugin-inspector.yml`.
|
|
142
|
+
`examples/github-actions-plugin-inspector.yml`. SARIF/JUnit CI consumption
|
|
143
|
+
examples live alongside them.
|
|
136
144
|
|
|
137
145
|
## Runtime Capture
|
|
138
146
|
|
|
@@ -141,9 +149,13 @@ the registrations made during `register(api)`. It is opt-in because it executes
|
|
|
141
149
|
plugin code:
|
|
142
150
|
|
|
143
151
|
```bash
|
|
144
|
-
|
|
152
|
+
npx @openclaw/plugin-inspector check --runtime --mock-sdk --allow-execute
|
|
145
153
|
```
|
|
146
154
|
|
|
155
|
+
`--allow-execute` is the explicit guard for modes that import plugin code. The
|
|
156
|
+
older `PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1` environment guard still works for
|
|
157
|
+
custom harnesses.
|
|
158
|
+
|
|
147
159
|
By default, runtime capture uses a generated mock for `openclaw/plugin-sdk` and
|
|
148
160
|
common external packages so plugin code can load in clean CI without OpenClaw
|
|
149
161
|
installed. Use `--real-sdk` only when the plugin workspace already has real SDK
|
|
@@ -157,7 +169,7 @@ Runtime capture writes:
|
|
|
157
169
|
You can also capture one entrypoint directly:
|
|
158
170
|
|
|
159
171
|
```bash
|
|
160
|
-
|
|
172
|
+
plugin-inspector capture ./dist/index.js --mock-sdk --allow-execute
|
|
161
173
|
```
|
|
162
174
|
|
|
163
175
|
## CI
|
|
@@ -168,7 +180,7 @@ Minimal package scripts:
|
|
|
168
180
|
{
|
|
169
181
|
"scripts": {
|
|
170
182
|
"plugin:check": "plugin-inspector inspect --no-openclaw",
|
|
171
|
-
"plugin:ci": "
|
|
183
|
+
"plugin:ci": "plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute"
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
```
|
|
@@ -193,7 +205,7 @@ jobs:
|
|
|
193
205
|
node-version: 24
|
|
194
206
|
cache: npm
|
|
195
207
|
- run: npm ci
|
|
196
|
-
- run:
|
|
208
|
+
- run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
197
209
|
- uses: actions/upload-artifact@v5
|
|
198
210
|
if: always()
|
|
199
211
|
with:
|
|
@@ -204,6 +216,15 @@ jobs:
|
|
|
204
216
|
`ci` writes the normal report, CI summary, SARIF, and JUnit files by default.
|
|
205
217
|
Pass `--no-sarif` or `--no-junit` only if your CI surface cannot consume them.
|
|
206
218
|
|
|
219
|
+
For GitHub code scanning, use
|
|
220
|
+
`examples/github-actions-code-scanning.yml`; it uploads
|
|
221
|
+
`reports/plugin-inspector.sarif` through CodeQL's SARIF upload action.
|
|
222
|
+
|
|
223
|
+
For CI test-summary UIs, point JUnit ingestion at
|
|
224
|
+
`reports/plugin-inspector.junit.xml`. Copy-ready GitLab and CircleCI examples
|
|
225
|
+
live in `examples/gitlab-ci-plugin-inspector.yml` and
|
|
226
|
+
`examples/circleci-plugin-inspector.yml`.
|
|
227
|
+
|
|
207
228
|
## Fixture Suites
|
|
208
229
|
|
|
209
230
|
Fixture-set configs are still supported for crabpot-style compatibility suites:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
|
|
3
|
+
jobs:
|
|
4
|
+
plugin-inspector:
|
|
5
|
+
docker:
|
|
6
|
+
- image: cimg/node:24.0
|
|
7
|
+
steps:
|
|
8
|
+
- checkout
|
|
9
|
+
- run: npm ci
|
|
10
|
+
- run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
11
|
+
- store_test_results:
|
|
12
|
+
path: reports
|
|
13
|
+
- store_artifacts:
|
|
14
|
+
path: reports
|
|
15
|
+
|
|
16
|
+
workflows:
|
|
17
|
+
plugin-inspector:
|
|
18
|
+
jobs:
|
|
19
|
+
- plugin-inspector
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: plugin-inspector
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
security-events: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
check:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
- uses: actions/setup-node@v5
|
|
18
|
+
with:
|
|
19
|
+
node-version: 24
|
|
20
|
+
cache: npm
|
|
21
|
+
- run: npm ci
|
|
22
|
+
- run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
23
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
24
|
+
if: always()
|
|
25
|
+
with:
|
|
26
|
+
sarif_file: reports/plugin-inspector.sarif
|
|
27
|
+
- uses: actions/upload-artifact@v5
|
|
28
|
+
if: always()
|
|
29
|
+
with:
|
|
30
|
+
name: plugin-inspector-reports
|
|
31
|
+
path: reports/plugin-inspector-*
|
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
node-version: 24
|
|
16
16
|
cache: npm
|
|
17
17
|
- run: npm ci
|
|
18
|
-
- run:
|
|
18
|
+
- run: npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
19
19
|
- uses: actions/upload-artifact@v5
|
|
20
20
|
if: always()
|
|
21
21
|
with:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
plugin_inspector:
|
|
2
|
+
image: node:24
|
|
3
|
+
script:
|
|
4
|
+
- npm ci
|
|
5
|
+
- npx @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
6
|
+
artifacts:
|
|
7
|
+
when: always
|
|
8
|
+
paths:
|
|
9
|
+
- reports/plugin-inspector-*
|
|
10
|
+
reports:
|
|
11
|
+
junit: reports/plugin-inspector.junit.xml
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"scripts": {
|
|
3
3
|
"plugin:check": "plugin-inspector inspect --no-openclaw",
|
|
4
|
-
"plugin:ci": "
|
|
4
|
+
"plugin:ci": "plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute"
|
|
5
5
|
},
|
|
6
6
|
"pluginInspector": {
|
|
7
7
|
"version": 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/plugin-inspector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Offline compatibility inspector for OpenClaw plugins.",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"./ref-diff": "./src/ref-diff.js",
|
|
37
37
|
"./runtime-capture-report": "./src/runtime-capture-report.js",
|
|
38
38
|
"./runtime-profile": "./src/runtime-profile.js",
|
|
39
|
+
"./synthetic-probe-suite": "./src/synthetic-probe-suite.js",
|
|
40
|
+
"./synthetic-probes": "./src/synthetic-probes.js",
|
|
39
41
|
"./workspace-plan": "./src/workspace-plan.js"
|
|
40
42
|
},
|
|
41
43
|
"files": [
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
],
|
|
48
50
|
"scripts": {
|
|
49
51
|
"check": "npm test && npm pack --dry-run",
|
|
52
|
+
"release:crabpot": "node scripts/check-crabpot-followthrough.mjs",
|
|
50
53
|
"release:local": "npm run check",
|
|
51
54
|
"test": "node --test test/*.test.js"
|
|
52
55
|
},
|
package/src/advanced.js
CHANGED
|
@@ -136,8 +136,10 @@ export {
|
|
|
136
136
|
} from "./config.js";
|
|
137
137
|
export {
|
|
138
138
|
buildPluginInspectorConfig,
|
|
139
|
+
defaultInitPackageScripts,
|
|
139
140
|
defaultInitConfigPath,
|
|
140
141
|
defaultInitWorkflowPath,
|
|
142
|
+
detectPackageManager,
|
|
141
143
|
renderGithubActionsWorkflow,
|
|
142
144
|
writePluginInspectorInit,
|
|
143
145
|
} from "./init.js";
|
|
@@ -196,6 +198,7 @@ export {
|
|
|
196
198
|
validateSyntheticProbePlan,
|
|
197
199
|
writeSyntheticProbePlan,
|
|
198
200
|
} from "./synthetic-probes.js";
|
|
201
|
+
export { buildSyntheticProbePlanFromReport } from "./synthetic-probe-suite.js";
|
|
199
202
|
export {
|
|
200
203
|
buildWorkspacePlan,
|
|
201
204
|
defaultWorkspacePlanOptions,
|
|
@@ -203,3 +206,10 @@ export {
|
|
|
203
206
|
validateWorkspacePlan,
|
|
204
207
|
writeWorkspacePlan,
|
|
205
208
|
} from "./workspace-plan.js";
|
|
209
|
+
export {
|
|
210
|
+
inspectCompatibilityFixtureSetConfig,
|
|
211
|
+
renderFixtureSetIssuesReport,
|
|
212
|
+
renderFixtureSetMarkdownReport,
|
|
213
|
+
runFixtureSetReport,
|
|
214
|
+
writeFixtureSetReports,
|
|
215
|
+
} from "./api.js";
|
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 { renderCompatibilityIssuesReport, renderCompatibilityMarkdownReport } from "./compatibility-report.js";
|
|
4
5
|
import { writePluginInspectorInit } from "./init.js";
|
|
5
6
|
import { captureEntrypoint } from "./inspector.js";
|
|
6
7
|
import { renderTextSummary, writeCompatibilityReport } from "./report.js";
|
|
@@ -35,6 +36,15 @@ export async function inspectFixtureSetConfig(options = {}) {
|
|
|
35
36
|
return inspectFixtureSet(config, { generatedAt: options.generatedAt });
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
export async function inspectCompatibilityFixtureSetConfig(options = {}) {
|
|
40
|
+
const config = await loadFixtureSetConfig(options);
|
|
41
|
+
return inspectCompatibilityFixtureSet(config, {
|
|
42
|
+
generatedAt: options.generatedAt,
|
|
43
|
+
openclawPath: options.openclawPath,
|
|
44
|
+
targetOpenClaw: options.targetOpenClaw,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export async function writePluginReports(report, options = {}) {
|
|
39
49
|
return writeCompatibilityReport(report, {
|
|
40
50
|
basename: options.basename,
|
|
@@ -45,6 +55,38 @@ export async function writePluginReports(report, options = {}) {
|
|
|
45
55
|
});
|
|
46
56
|
}
|
|
47
57
|
|
|
58
|
+
export async function writeFixtureSetReports(report, options = {}) {
|
|
59
|
+
return writeCompatibilityReport(report, {
|
|
60
|
+
basename: options.basename,
|
|
61
|
+
check: options.check,
|
|
62
|
+
cwd: options.cwd,
|
|
63
|
+
formatEvidence: options.formatEvidence,
|
|
64
|
+
issuesBasename: options.issuesBasename,
|
|
65
|
+
issuesPath: options.issuesPath,
|
|
66
|
+
issuesTitle: options.issuesTitle,
|
|
67
|
+
jsonPath: options.jsonPath,
|
|
68
|
+
markdownPath: options.markdownPath,
|
|
69
|
+
markdownTitle: options.markdownTitle,
|
|
70
|
+
outDir: options.outDir,
|
|
71
|
+
severityLabels: options.severityLabels,
|
|
72
|
+
title: options.title,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function renderFixtureSetMarkdownReport(report, options = {}) {
|
|
77
|
+
return renderCompatibilityMarkdownReport(report, options);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function renderFixtureSetIssuesReport(report, options = {}) {
|
|
81
|
+
return renderCompatibilityIssuesReport(report, options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function runFixtureSetReport(options = {}) {
|
|
85
|
+
const report = await inspectCompatibilityFixtureSetConfig(options);
|
|
86
|
+
const paths = options.write === false ? null : await writeFixtureSetReports(report, options);
|
|
87
|
+
return { report, paths };
|
|
88
|
+
}
|
|
89
|
+
|
|
48
90
|
export async function runPluginCheck(options = {}) {
|
|
49
91
|
const outDir = options.outDir ?? "reports";
|
|
50
92
|
const config = await loadPluginConfig(options);
|
|
@@ -55,8 +97,8 @@ export async function runPluginCheck(options = {}) {
|
|
|
55
97
|
const mockSdk = options.mockSdk ?? config.capture?.mockSdk ?? true;
|
|
56
98
|
|
|
57
99
|
if (capture === true) {
|
|
58
|
-
if (
|
|
59
|
-
throw new Error("runtime capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 in an isolated workspace");
|
|
100
|
+
if (!executionAllowed(options)) {
|
|
101
|
+
throw new Error("runtime capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 or --allow-execute in an isolated workspace");
|
|
60
102
|
}
|
|
61
103
|
const runtimeCapture = await buildRuntimeCaptureReport({
|
|
62
104
|
mockSdk,
|
|
@@ -90,3 +132,17 @@ export async function setupPluginInspector(options = {}) {
|
|
|
90
132
|
}
|
|
91
133
|
|
|
92
134
|
export { createCaptureApi, renderTextSummary, writeCiOutputArtifacts };
|
|
135
|
+
|
|
136
|
+
function executionAllowed(options) {
|
|
137
|
+
return options.allowExecution === true || process.env.PLUGIN_INSPECTOR_EXECUTE_ISOLATED === "1";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function loadFixtureSetConfig(options) {
|
|
141
|
+
if (options.config) {
|
|
142
|
+
return {
|
|
143
|
+
...options.config,
|
|
144
|
+
rootDir: options.config.rootDir ?? options.rootDir ?? options.cwd ?? process.cwd(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return loadInspectorConfig(options.configPath, { cwd: options.cwd ?? options.rootDir });
|
|
148
|
+
}
|
package/src/capture-api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const defaultCaptureApiRegistrarProfiles = {
|
|
2
2
|
registerChannel: {
|
|
3
|
-
returnValue: ({ args }) =>
|
|
3
|
+
returnValue: ({ args }) => channelRegistrationObject(args),
|
|
4
4
|
},
|
|
5
5
|
registerCli: {
|
|
6
6
|
returnValue: ({ args }) => registrationObject(args, { name: "cli" }),
|
|
@@ -12,7 +12,7 @@ export const defaultCaptureApiRegistrarProfiles = {
|
|
|
12
12
|
returnValue: ({ args }) => registrationObject(args, { id: "context-engine" }),
|
|
13
13
|
},
|
|
14
14
|
registerGatewayMethod: {
|
|
15
|
-
returnValue: ({ args }) =>
|
|
15
|
+
returnValue: ({ args }) => gatewayMethodRegistrationObject(args),
|
|
16
16
|
},
|
|
17
17
|
registerHook: {
|
|
18
18
|
returnValue: ({ api }) => api,
|
|
@@ -37,9 +37,10 @@ export const defaultCaptureApiRegistrarProfiles = {
|
|
|
37
37
|
},
|
|
38
38
|
registerService: {
|
|
39
39
|
returnValue: ({ args }) => ({
|
|
40
|
-
...registrationObject(args, { name: "service" }),
|
|
40
|
+
...registrationObject(args, { id: "service", name: "service" }),
|
|
41
41
|
start: async () => undefined,
|
|
42
42
|
stop: async () => undefined,
|
|
43
|
+
dispose: async () => undefined,
|
|
43
44
|
}),
|
|
44
45
|
},
|
|
45
46
|
registerSpeechProvider: {
|
|
@@ -125,6 +126,7 @@ export function createCaptureApi(options = {}) {
|
|
|
125
126
|
|
|
126
127
|
export function createCaptureContext(options = {}) {
|
|
127
128
|
return {
|
|
129
|
+
registrationMode: options.registrationMode ?? "full",
|
|
128
130
|
config: options.config ?? {},
|
|
129
131
|
logger: options.logger ?? console,
|
|
130
132
|
pluginConfig: options.pluginConfig ?? {},
|
|
@@ -142,6 +144,12 @@ export function createCaptureContext(options = {}) {
|
|
|
142
144
|
},
|
|
143
145
|
gateway: options.gateway ?? {
|
|
144
146
|
baseUrl: "http://127.0.0.1:0",
|
|
147
|
+
async call(method, params) {
|
|
148
|
+
return { ok: true, method, params };
|
|
149
|
+
},
|
|
150
|
+
respond(ok, result, error) {
|
|
151
|
+
return { ok, result, ...(error ? { error } : {}) };
|
|
152
|
+
},
|
|
145
153
|
registerRoute(route) {
|
|
146
154
|
return {
|
|
147
155
|
...route,
|
|
@@ -242,6 +250,36 @@ function registrationObject(args, defaults) {
|
|
|
242
250
|
return withCallableDefaults({ ...defaults }, callable);
|
|
243
251
|
}
|
|
244
252
|
|
|
253
|
+
function channelRegistrationObject(args) {
|
|
254
|
+
const first = args[0];
|
|
255
|
+
const registration = registrationObject(args, { id: "channel" });
|
|
256
|
+
if (first?.plugin && typeof first.plugin === "object") {
|
|
257
|
+
return {
|
|
258
|
+
...registration,
|
|
259
|
+
id: objectId(first.plugin) ?? registration.id,
|
|
260
|
+
plugin: first.plugin,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return registration;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function gatewayMethodRegistrationObject(args) {
|
|
267
|
+
const [method, handler, options] = args;
|
|
268
|
+
const registration = registrationObject(args, { name: "gateway.method" });
|
|
269
|
+
if (typeof method !== "string") {
|
|
270
|
+
return registration;
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
...registration,
|
|
274
|
+
name: method,
|
|
275
|
+
method,
|
|
276
|
+
handler: typeof handler === "function" ? handler : registration.handler,
|
|
277
|
+
run: typeof handler === "function" ? handler : registration.run,
|
|
278
|
+
execute: typeof handler === "function" ? handler : registration.execute,
|
|
279
|
+
scope: options?.scope ?? registration.scope,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
245
283
|
function firstCallable(args) {
|
|
246
284
|
return args.find((arg) => typeof arg === "function");
|
|
247
285
|
}
|
package/src/cli.js
CHANGED
|
@@ -72,8 +72,17 @@ async function runCheck(commandArgs) {
|
|
|
72
72
|
const json = commandArgs.includes("--json");
|
|
73
73
|
const capture = readRuntimeFlag(commandArgs);
|
|
74
74
|
const mockSdk = readMockSdkFlag(commandArgs);
|
|
75
|
+
const allowExecution = readAllowExecutionFlag(commandArgs);
|
|
75
76
|
const ciOutputs = readCiOutputFlags(commandArgs);
|
|
76
|
-
const { report, paths } = await runPluginCheck({
|
|
77
|
+
const { report, paths } = await runPluginCheck({
|
|
78
|
+
allowExecution,
|
|
79
|
+
capture,
|
|
80
|
+
configPath,
|
|
81
|
+
mockSdk,
|
|
82
|
+
openclawPath,
|
|
83
|
+
outDir,
|
|
84
|
+
pluginRoot,
|
|
85
|
+
});
|
|
77
86
|
await writeCiOutputArtifacts(report, {
|
|
78
87
|
...ciOutputs,
|
|
79
88
|
cwd: path.dirname(paths.jsonPath),
|
|
@@ -83,7 +92,7 @@ async function runCheck(commandArgs) {
|
|
|
83
92
|
if (json) {
|
|
84
93
|
console.log(JSON.stringify(report, null, 2));
|
|
85
94
|
} else {
|
|
86
|
-
console.log(renderTextSummary(report));
|
|
95
|
+
console.log(renderTextSummary(report, { artifacts: paths }));
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
if (report.status !== "pass") {
|
|
@@ -95,19 +104,27 @@ async function runInit(commandArgs) {
|
|
|
95
104
|
const pluginRoot = readFlag(commandArgs, "--plugin-root") ?? readFlag(commandArgs, "--root");
|
|
96
105
|
const configPath = readFlag(commandArgs, "--config") ?? undefined;
|
|
97
106
|
const workflowPath = readFlag(commandArgs, "--workflow") ?? undefined;
|
|
98
|
-
const packageManager = readFlag(commandArgs, "--package-manager") ??
|
|
107
|
+
const packageManager = readFlag(commandArgs, "--package-manager") ?? undefined;
|
|
99
108
|
const result = await writePluginInspectorInit({
|
|
100
109
|
pluginRoot,
|
|
101
110
|
configPath,
|
|
102
111
|
workflowPath,
|
|
103
112
|
packageManager,
|
|
104
113
|
ci: commandArgs.includes("--ci"),
|
|
114
|
+
dryRun: commandArgs.includes("--dry-run"),
|
|
115
|
+
scripts: commandArgs.includes("--scripts"),
|
|
105
116
|
force: commandArgs.includes("--force"),
|
|
106
117
|
});
|
|
107
118
|
|
|
119
|
+
if (commandArgs.includes("--json")) {
|
|
120
|
+
console.log(JSON.stringify(initCommandSummary(result), null, 2));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
108
124
|
for (const filePath of result.written) {
|
|
109
|
-
console.log(
|
|
125
|
+
console.log(`${result.dryRun ? "would write" : "wrote"} ${path.relative(result.pluginRoot, filePath)}`);
|
|
110
126
|
}
|
|
127
|
+
console.log(`package manager: ${result.packageManager}`);
|
|
111
128
|
}
|
|
112
129
|
|
|
113
130
|
async function runReport(command, commandArgs) {
|
|
@@ -128,7 +145,7 @@ async function runReport(command, commandArgs) {
|
|
|
128
145
|
if (json) {
|
|
129
146
|
console.log(JSON.stringify(report, null, 2));
|
|
130
147
|
} else {
|
|
131
|
-
console.log(renderTextSummary(report));
|
|
148
|
+
console.log(renderTextSummary(report, { artifacts: paths }));
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
if (check && report.status !== "pass") {
|
|
@@ -144,8 +161,10 @@ async function runCi(commandArgs) {
|
|
|
144
161
|
const json = commandArgs.includes("--json");
|
|
145
162
|
const capture = readRuntimeFlag(commandArgs);
|
|
146
163
|
const mockSdk = readMockSdkFlag(commandArgs);
|
|
164
|
+
const allowExecution = readAllowExecutionFlag(commandArgs);
|
|
147
165
|
const ciOutputs = readCiOutputFlags(commandArgs, { defaultEnabled: true });
|
|
148
166
|
const { report, reportDir } = await runCiCompatibilityReport({
|
|
167
|
+
allowExecution,
|
|
149
168
|
capture,
|
|
150
169
|
configPath,
|
|
151
170
|
mockSdk,
|
|
@@ -184,7 +203,7 @@ async function runCi(commandArgs) {
|
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
|
|
187
|
-
async function runCiCompatibilityReport({ capture, configPath, mockSdk, openclawPath, outDir, pluginRoot }) {
|
|
206
|
+
async function runCiCompatibilityReport({ allowExecution, capture, configPath, mockSdk, openclawPath, outDir, pluginRoot }) {
|
|
188
207
|
if (configPath) {
|
|
189
208
|
const config = await loadInspectorConfig(configPath, { cwd: pluginRoot });
|
|
190
209
|
const report = await inspectCompatibilityFixtureSet(config, { openclawPath });
|
|
@@ -195,7 +214,7 @@ async function runCiCompatibilityReport({ capture, configPath, mockSdk, openclaw
|
|
|
195
214
|
};
|
|
196
215
|
}
|
|
197
216
|
|
|
198
|
-
const { report } = await runPluginCheck({
|
|
217
|
+
const { report } = await runPluginCheck({ allowExecution, capture, mockSdk, openclawPath, outDir, pluginRoot });
|
|
199
218
|
return {
|
|
200
219
|
report,
|
|
201
220
|
reportDir: path.resolve(pluginRoot ?? process.cwd(), outDir),
|
|
@@ -207,11 +226,12 @@ async function runCapture(commandArgs) {
|
|
|
207
226
|
const outputPath = readFlag(commandArgs, "--output");
|
|
208
227
|
const pluginRoot = readFlag(commandArgs, "--plugin-root");
|
|
209
228
|
const mockSdk = readMockSdkFlag(commandArgs) ?? commandArgs.includes("--mock-sdk");
|
|
229
|
+
const allowExecution = readAllowExecutionFlag(commandArgs);
|
|
210
230
|
if (!entrypoint) {
|
|
211
231
|
throw new Error("capture requires an entrypoint path");
|
|
212
232
|
}
|
|
213
|
-
if (process.env.PLUGIN_INSPECTOR_EXECUTE_ISOLATED !== "1") {
|
|
214
|
-
throw new Error("capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 in an isolated workspace");
|
|
233
|
+
if (!allowExecution && process.env.PLUGIN_INSPECTOR_EXECUTE_ISOLATED !== "1") {
|
|
234
|
+
throw new Error("capture imports plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 or --allow-execute in an isolated workspace");
|
|
215
235
|
}
|
|
216
236
|
|
|
217
237
|
const result = await captureEntrypoint(entrypoint, { mockSdk, pluginRoot });
|
|
@@ -281,6 +301,10 @@ function readMockSdkFlag(commandArgs) {
|
|
|
281
301
|
return undefined;
|
|
282
302
|
}
|
|
283
303
|
|
|
304
|
+
function readAllowExecutionFlag(commandArgs) {
|
|
305
|
+
return commandArgs.includes("--allow-execute");
|
|
306
|
+
}
|
|
307
|
+
|
|
284
308
|
function renderCiTextSummary(summary) {
|
|
285
309
|
return [
|
|
286
310
|
`Status: ${summary.status.toUpperCase()}`,
|
|
@@ -290,6 +314,15 @@ function renderCiTextSummary(summary) {
|
|
|
290
314
|
].join("\n");
|
|
291
315
|
}
|
|
292
316
|
|
|
317
|
+
function initCommandSummary(result) {
|
|
318
|
+
return {
|
|
319
|
+
dryRun: result.dryRun,
|
|
320
|
+
packageManager: result.packageManager,
|
|
321
|
+
pluginRoot: result.pluginRoot,
|
|
322
|
+
files: result.written.map((filePath) => path.relative(result.pluginRoot, filePath)),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
293
326
|
function renderConfigTextSummary(config) {
|
|
294
327
|
const fixture = config.fixtures[0];
|
|
295
328
|
return [
|
|
@@ -308,16 +341,16 @@ function printHelp() {
|
|
|
308
341
|
|
|
309
342
|
Usage:
|
|
310
343
|
plugin-inspector
|
|
311
|
-
plugin-inspector check [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--json]
|
|
344
|
+
plugin-inspector check [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--json]
|
|
312
345
|
plugin-inspector config [--plugin-root <path>] [--config <path>] [--json]
|
|
313
|
-
plugin-inspector init [--plugin-root <path>] [--config <path>] [--ci] [--package-manager npm|pnpm|yarn|bun] [--force]
|
|
346
|
+
plugin-inspector init [--plugin-root <path>] [--config <path>] [--ci] [--scripts] [--package-manager npm|pnpm|yarn|bun] [--dry-run] [--json] [--force]
|
|
314
347
|
plugin-inspector report --config <path> [--out <dir>] [--check] [--json]
|
|
315
|
-
plugin-inspector inspect [--plugin-root <path>] [--config <path>] [--out <dir>] [--check] [--json] [--sarif [path]] [--junit [path]]
|
|
316
|
-
plugin-inspector ci [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--json] [--no-sarif] [--no-junit]
|
|
317
|
-
|
|
348
|
+
plugin-inspector inspect [--plugin-root <path>] [--config <path>] [--out <dir>] [--check] [--json] [--sarif [path]] [--junit [path]] [--allow-execute]
|
|
349
|
+
plugin-inspector ci [--plugin-root <path>] [--config <path>] [--out <dir>] [--openclaw <path>] [--no-openclaw] [--runtime] [--mock-sdk|--real-sdk] [--allow-execute] [--json] [--no-sarif] [--no-junit]
|
|
350
|
+
plugin-inspector capture <entrypoint> [--mock-sdk|--real-sdk] [--allow-execute] [--plugin-root <path>] [--output <path>]
|
|
318
351
|
|
|
319
352
|
Default check runs from the current plugin root and writes reports/ unless --out is set.
|
|
320
353
|
CI writes SARIF and JUnit artifacts by default; check/inspect can write them with --sarif and --junit.
|
|
321
|
-
Runtime capture is opt-in because it imports plugin code; use --runtime with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1.
|
|
354
|
+
Runtime capture is opt-in because it imports plugin code; use --runtime with --allow-execute or PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1.
|
|
322
355
|
`);
|
|
323
356
|
}
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
export {
|
|
2
2
|
capturePluginEntrypoint,
|
|
3
3
|
createCaptureApi,
|
|
4
|
+
inspectCompatibilityFixtureSetConfig,
|
|
4
5
|
inspectFixtureSetConfig,
|
|
5
6
|
inspectPluginRoot,
|
|
6
7
|
loadPluginConfig,
|
|
8
|
+
renderFixtureSetIssuesReport,
|
|
9
|
+
renderFixtureSetMarkdownReport,
|
|
7
10
|
renderTextSummary,
|
|
11
|
+
runFixtureSetReport,
|
|
8
12
|
runPluginCheck,
|
|
9
13
|
setupPluginInspector,
|
|
10
14
|
writeCiOutputArtifacts,
|
|
15
|
+
writeFixtureSetReports,
|
|
11
16
|
writePluginReports,
|
|
12
17
|
} from "./api.js";
|
package/src/init.js
CHANGED
|
@@ -5,32 +5,66 @@ import { inferPluginSeams, packageId } from "./config.js";
|
|
|
5
5
|
|
|
6
6
|
export const defaultInitConfigPath = "plugin-inspector.config.json";
|
|
7
7
|
export const defaultInitWorkflowPath = ".github/workflows/plugin-inspector.yml";
|
|
8
|
+
export const defaultInitPackageScripts = {
|
|
9
|
+
"plugin:check": "plugin-inspector inspect --no-openclaw",
|
|
10
|
+
"plugin:ci": "plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute",
|
|
11
|
+
};
|
|
8
12
|
|
|
9
13
|
export async function writePluginInspectorInit(options = {}) {
|
|
10
14
|
const pluginRoot = path.resolve(options.pluginRoot ?? options.cwd ?? process.cwd());
|
|
11
15
|
const configPath = path.resolve(pluginRoot, options.configPath ?? defaultInitConfigPath);
|
|
16
|
+
const workflowPath = options.ci === true ? path.resolve(pluginRoot, options.workflowPath ?? defaultInitWorkflowPath) : null;
|
|
17
|
+
const packageManager = options.packageManager ?? (await detectPackageManager(pluginRoot));
|
|
18
|
+
const dryRun = options.dryRun === true;
|
|
12
19
|
const written = [];
|
|
13
20
|
|
|
14
|
-
if (existsSync(configPath) && options.force !== true) {
|
|
21
|
+
if (!dryRun && existsSync(configPath) && options.force !== true) {
|
|
15
22
|
throw new Error(`${path.relative(pluginRoot, configPath)} already exists; pass --force to overwrite it`);
|
|
16
23
|
}
|
|
24
|
+
if (!dryRun && workflowPath && existsSync(workflowPath) && options.force !== true) {
|
|
25
|
+
throw new Error(`${path.relative(pluginRoot, workflowPath)} already exists; pass --force to overwrite it`);
|
|
26
|
+
}
|
|
27
|
+
const packageJsonPath = path.join(pluginRoot, "package.json");
|
|
28
|
+
const packageJson = options.scripts === true ? await readJsonIfExists(packageJsonPath) : null;
|
|
29
|
+
if (options.scripts === true) {
|
|
30
|
+
if (!packageJson) {
|
|
31
|
+
throw new Error("package.json is required to write plugin-inspector package scripts");
|
|
32
|
+
}
|
|
33
|
+
for (const name of Object.keys(defaultInitPackageScripts)) {
|
|
34
|
+
if (!dryRun && packageJson.scripts?.[name] && options.force !== true) {
|
|
35
|
+
throw new Error(`package.json scripts.${name} already exists; pass --force to overwrite it`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
17
39
|
|
|
18
40
|
const config = await buildPluginInspectorConfig({ pluginRoot });
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
if (!dryRun) {
|
|
42
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
43
|
+
await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
44
|
+
}
|
|
21
45
|
written.push(configPath);
|
|
22
46
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
if (workflowPath) {
|
|
48
|
+
if (!dryRun) {
|
|
49
|
+
await mkdir(path.dirname(workflowPath), { recursive: true });
|
|
50
|
+
await writeFile(workflowPath, renderGithubActionsWorkflow({ packageManager }), "utf8");
|
|
27
51
|
}
|
|
28
|
-
await mkdir(path.dirname(workflowPath), { recursive: true });
|
|
29
|
-
await writeFile(workflowPath, renderGithubActionsWorkflow({ packageManager: options.packageManager }), "utf8");
|
|
30
52
|
written.push(workflowPath);
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
|
|
55
|
+
if (options.scripts === true) {
|
|
56
|
+
const existingScripts = packageJson.scripts ?? {};
|
|
57
|
+
packageJson.scripts = {
|
|
58
|
+
...existingScripts,
|
|
59
|
+
...defaultInitPackageScripts,
|
|
60
|
+
};
|
|
61
|
+
if (!dryRun) {
|
|
62
|
+
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
|
|
63
|
+
}
|
|
64
|
+
written.push(packageJsonPath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { pluginRoot, configPath, dryRun, packageManager, written };
|
|
34
68
|
}
|
|
35
69
|
|
|
36
70
|
export async function buildPluginInspectorConfig(options = {}) {
|
|
@@ -79,7 +113,7 @@ jobs:
|
|
|
79
113
|
node-version: 24
|
|
80
114
|
cache: ${setup.cache}
|
|
81
115
|
${setup.corepack ? " - run: corepack enable\n" : ""} - run: ${setup.install}
|
|
82
|
-
- run:
|
|
116
|
+
- run: ${setup.exec} @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute
|
|
83
117
|
- uses: actions/upload-artifact@v5
|
|
84
118
|
if: always()
|
|
85
119
|
with:
|
|
@@ -88,19 +122,62 @@ ${setup.corepack ? " - run: corepack enable\n" : ""} - run: ${setup.in
|
|
|
88
122
|
`;
|
|
89
123
|
}
|
|
90
124
|
|
|
125
|
+
export async function detectPackageManager(pluginRoot) {
|
|
126
|
+
const root = path.resolve(pluginRoot ?? process.cwd());
|
|
127
|
+
const packageJson = await readJsonIfExists(path.join(root, "package.json"));
|
|
128
|
+
const packageManager = packageJson?.packageManager;
|
|
129
|
+
if (typeof packageManager === "string") {
|
|
130
|
+
const [name] = packageManager.split("@");
|
|
131
|
+
if (["npm", "pnpm", "yarn", "bun"].includes(name)) {
|
|
132
|
+
return name;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (existsSync(path.join(root, "pnpm-lock.yaml"))) {
|
|
137
|
+
return "pnpm";
|
|
138
|
+
}
|
|
139
|
+
if (existsSync(path.join(root, "yarn.lock"))) {
|
|
140
|
+
return "yarn";
|
|
141
|
+
}
|
|
142
|
+
if (existsSync(path.join(root, "bun.lockb")) || existsSync(path.join(root, "bun.lock"))) {
|
|
143
|
+
return "bun";
|
|
144
|
+
}
|
|
145
|
+
return "npm";
|
|
146
|
+
}
|
|
147
|
+
|
|
91
148
|
function inferSourceRoot(packageJson) {
|
|
92
149
|
const entrypoints = [
|
|
93
150
|
packageJson?.openclaw?.entrypoint,
|
|
94
151
|
...(packageJson?.openclaw?.extensions ?? []),
|
|
95
152
|
...(packageJson?.openclaw?.runtimeExtensions ?? []),
|
|
153
|
+
...entrypointStrings(packageJson?.exports?.["."]),
|
|
154
|
+
...entrypointStrings(packageJson?.exports),
|
|
155
|
+
packageJson?.module,
|
|
156
|
+
packageJson?.main,
|
|
96
157
|
].filter((value) => typeof value === "string");
|
|
97
|
-
const entrypoint = entrypoints[0] ??
|
|
98
|
-
if (
|
|
158
|
+
const entrypoint = entrypoints[0] ?? "src/index.js";
|
|
159
|
+
if (stripRelativePrefix(entrypoint).startsWith("src/")) {
|
|
99
160
|
return "src";
|
|
100
161
|
}
|
|
101
162
|
return ".";
|
|
102
163
|
}
|
|
103
164
|
|
|
165
|
+
function entrypointStrings(value) {
|
|
166
|
+
if (typeof value === "string") {
|
|
167
|
+
return [value];
|
|
168
|
+
}
|
|
169
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
return ["import", "default", "require", "node", "module"]
|
|
173
|
+
.map((key) => value[key])
|
|
174
|
+
.filter((item) => typeof item === "string");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function stripRelativePrefix(filePath) {
|
|
178
|
+
return filePath.replace(/^\.\//, "");
|
|
179
|
+
}
|
|
180
|
+
|
|
104
181
|
async function readJsonIfExists(filePath) {
|
|
105
182
|
if (!existsSync(filePath)) {
|
|
106
183
|
return null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { mkdtemp } from "node:fs/promises";
|
|
3
4
|
import { register } from "node:module";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import path from "node:path";
|
|
@@ -26,13 +27,16 @@ async function run(options) {
|
|
|
26
27
|
const pluginRoot = path.resolve(options.cwd ?? process.cwd(), options.pluginRoot ?? path.dirname(entrypoint));
|
|
27
28
|
const workspace = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-mock-sdk-"));
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
cleanupTempDirOnExit(workspace);
|
|
31
|
+
const { loaderPath } = await createMockSdkPackage(workspace, { pluginRoot });
|
|
32
|
+
register(pathToFileURL(loaderPath));
|
|
33
|
+
return await captureLinkedEntrypoint(entrypoint, options);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function cleanupTempDirOnExit(dir) {
|
|
37
|
+
process.once("exit", () => {
|
|
38
|
+
rmSync(dir, { force: true, recursive: true });
|
|
39
|
+
});
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
async function captureLinkedEntrypoint(entrypoint, options) {
|
package/src/report.js
CHANGED
|
@@ -246,27 +246,79 @@ export async function writeReport(report, options = {}) {
|
|
|
246
246
|
export async function writeCompatibilityReport(report, options = {}) {
|
|
247
247
|
const outDir = path.resolve(options.cwd ?? process.cwd(), options.outDir ?? "reports");
|
|
248
248
|
const basename = options.basename ?? "plugin-inspector-report";
|
|
249
|
-
const jsonPath = path.join(outDir, `${basename}.json`);
|
|
250
|
-
const markdownPath = path.join(outDir, `${basename}.md`);
|
|
251
|
-
const issuesPath = path.join(outDir, options.issuesBasename ?? "plugin-inspector-issues.md");
|
|
249
|
+
const jsonPath = options.jsonPath ?? path.join(outDir, `${basename}.json`);
|
|
250
|
+
const markdownPath = options.markdownPath ?? path.join(outDir, `${basename}.md`);
|
|
251
|
+
const issuesPath = options.issuesPath ?? path.join(outDir, options.issuesBasename ?? "plugin-inspector-issues.md");
|
|
252
|
+
const markdownOptions = compatibilityRenderOptions(options, {
|
|
253
|
+
title: options.markdownTitle ?? options.title,
|
|
254
|
+
...options.markdownOptions,
|
|
255
|
+
});
|
|
256
|
+
const issuesOptions = compatibilityRenderOptions(options, {
|
|
257
|
+
title: options.issuesTitle ?? options.title,
|
|
258
|
+
...options.issuesOptions,
|
|
259
|
+
});
|
|
252
260
|
|
|
253
261
|
return writeArtifacts(
|
|
254
262
|
[
|
|
255
263
|
{ name: "jsonPath", path: jsonPath, json: report },
|
|
256
|
-
{ name: "markdownPath", path: markdownPath, markdown: renderCompatibilityMarkdownReport(report) },
|
|
257
|
-
{ name: "issuesPath", path: issuesPath, markdown: renderCompatibilityIssuesReport(report) },
|
|
264
|
+
{ name: "markdownPath", path: markdownPath, markdown: renderCompatibilityMarkdownReport(report, markdownOptions) },
|
|
265
|
+
{ name: "issuesPath", path: issuesPath, markdown: renderCompatibilityIssuesReport(report, issuesOptions) },
|
|
258
266
|
],
|
|
259
267
|
{ check: options.check },
|
|
260
268
|
);
|
|
261
269
|
}
|
|
262
270
|
|
|
263
|
-
|
|
264
|
-
|
|
271
|
+
function compatibilityRenderOptions(options, overrides) {
|
|
272
|
+
const renderOptions = {
|
|
273
|
+
formatEvidence: options.formatEvidence,
|
|
274
|
+
severityLabels: options.severityLabels,
|
|
275
|
+
...overrides,
|
|
276
|
+
};
|
|
277
|
+
return Object.fromEntries(Object.entries(renderOptions).filter(([, value]) => value !== undefined));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function renderTextSummary(report, options = {}) {
|
|
281
|
+
const lines = [
|
|
265
282
|
`Status: ${report.status.toUpperCase()}`,
|
|
266
283
|
`Fixtures: ${report.summary.fixtureCount}`,
|
|
267
284
|
`Breakages: ${report.summary.breakageCount}`,
|
|
285
|
+
...(typeof report.summary.issueCount === "number" ? [`Issues: ${report.summary.issueCount}`] : []),
|
|
268
286
|
`Logs: ${report.summary.logCount}`,
|
|
269
|
-
]
|
|
287
|
+
];
|
|
288
|
+
const artifacts = Object.entries(options.artifacts ?? {}).filter(([, filePath]) => Boolean(filePath));
|
|
289
|
+
if (artifacts.length > 0) {
|
|
290
|
+
lines.push("", "Reports:", ...artifacts.map(([name, filePath]) => `- ${artifactLabel(name)}: ${filePath}`));
|
|
291
|
+
}
|
|
292
|
+
const findings = topTextFindings(report, options.topFindings ?? 3);
|
|
293
|
+
if (findings.length > 0) {
|
|
294
|
+
lines.push("", "Top findings:", ...findings.map((finding) => `- ${finding}`));
|
|
295
|
+
}
|
|
296
|
+
return lines.join("\n");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function topTextFindings(report, limit) {
|
|
300
|
+
if (report.status === "pass" || limit <= 0) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
return [
|
|
304
|
+
...(report.breakages ?? []).map((finding) => formatTextFinding(finding, "breakage")),
|
|
305
|
+
...(report.issues ?? [])
|
|
306
|
+
.filter((issue) => issue.status === "blocking" || issue.severity === "P0" || issue.severity === "P1")
|
|
307
|
+
.map((issue) => formatTextFinding(issue, issue.severity ?? "issue")),
|
|
308
|
+
...(report.warnings ?? []).map((finding) => formatTextFinding(finding, "warning")),
|
|
309
|
+
].slice(0, limit);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function formatTextFinding(finding, fallbackLevel) {
|
|
313
|
+
const level = finding.level ?? finding.severity ?? fallbackLevel;
|
|
314
|
+
const code = finding.code ? ` ${finding.code}` : "";
|
|
315
|
+
const message = finding.message ?? finding.title ?? "see report";
|
|
316
|
+
const evidence = Array.isArray(finding.evidence) && finding.evidence.length > 0 ? ` (${finding.evidence[0]})` : "";
|
|
317
|
+
return `${String(level).toUpperCase()} ${finding.fixture ?? "unknown"}${code}: ${message}${evidence}`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function artifactLabel(name) {
|
|
321
|
+
return String(name).replace(/Path$/u, "");
|
|
270
322
|
}
|
|
271
323
|
|
|
272
324
|
export function renderMarkdownReport(report) {
|
package/src/sdk-mock.js
CHANGED
|
@@ -11,8 +11,11 @@ export const mockSdkSubpathExports = {
|
|
|
11
11
|
"emptyPluginConfigSchema",
|
|
12
12
|
],
|
|
13
13
|
core: [
|
|
14
|
+
"buildChannelOutboundSessionRoute",
|
|
14
15
|
"buildChannelConfigSchema",
|
|
15
16
|
"buildPluginConfigSchema",
|
|
17
|
+
"createActionGate",
|
|
18
|
+
"createChannelPluginBase",
|
|
16
19
|
"createChatChannelPlugin",
|
|
17
20
|
"createDedupeCache",
|
|
18
21
|
"defineChannelPluginEntry",
|
|
@@ -22,12 +25,24 @@ export const mockSdkSubpathExports = {
|
|
|
22
25
|
"emptyPluginConfigSchema",
|
|
23
26
|
"jsonResult",
|
|
24
27
|
"readNumberParam",
|
|
28
|
+
"readReactionParams",
|
|
29
|
+
"readStringArrayParam",
|
|
30
|
+
"readStringParam",
|
|
31
|
+
],
|
|
32
|
+
"channel-actions": [
|
|
33
|
+
"createActionGate",
|
|
34
|
+
"jsonResult",
|
|
35
|
+
"readNumberParam",
|
|
36
|
+
"readReactionParams",
|
|
37
|
+
"readStringArrayParam",
|
|
25
38
|
"readStringParam",
|
|
26
39
|
],
|
|
27
40
|
"channel-core": [
|
|
28
41
|
"buildChannelConfigSchema",
|
|
42
|
+
"buildChannelOutboundSessionRoute",
|
|
29
43
|
"buildThreadAwareOutboundSessionRoute",
|
|
30
44
|
"clearAccountEntryFields",
|
|
45
|
+
"createChannelPluginBase",
|
|
31
46
|
"createChatChannelPlugin",
|
|
32
47
|
"defineChannelPluginEntry",
|
|
33
48
|
"defineSetupPluginEntry",
|
|
@@ -718,6 +733,10 @@ function mockSdkSource() {
|
|
|
718
733
|
return typeof entry === "function" ? { register: entry } : entry;
|
|
719
734
|
}
|
|
720
735
|
|
|
736
|
+
function normalizeRegistrationMode(api) {
|
|
737
|
+
return api?.registrationMode ?? "full";
|
|
738
|
+
}
|
|
739
|
+
|
|
721
740
|
function isPlainObject(value) {
|
|
722
741
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
723
742
|
}
|
|
@@ -754,15 +773,139 @@ export function definePluginEntry(entry) {
|
|
|
754
773
|
}
|
|
755
774
|
|
|
756
775
|
export function defineChannelPluginEntry(entry) {
|
|
757
|
-
|
|
776
|
+
if (!isPlainObject(entry) || !entry.plugin) {
|
|
777
|
+
return normalizeEntry(entry);
|
|
778
|
+
}
|
|
779
|
+
const resolved = {
|
|
780
|
+
id: entry.id,
|
|
781
|
+
name: entry.name,
|
|
782
|
+
description: entry.description,
|
|
783
|
+
configSchema: createConfigSchema(entry.configSchema),
|
|
784
|
+
channelPlugin: entry.plugin,
|
|
785
|
+
register(api) {
|
|
786
|
+
const mode = normalizeRegistrationMode(api);
|
|
787
|
+
if (mode === "cli-metadata") {
|
|
788
|
+
entry.registerCliMetadata?.(api);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
api.registerChannel?.({ plugin: entry.plugin });
|
|
792
|
+
entry.setRuntime?.(api.runtime);
|
|
793
|
+
if (mode === "discovery") {
|
|
794
|
+
entry.registerCliMetadata?.(api);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (mode !== "full") {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
entry.registerCliMetadata?.(api);
|
|
801
|
+
entry.registerFull?.(api);
|
|
802
|
+
},
|
|
803
|
+
};
|
|
804
|
+
if (entry.setRuntime) {
|
|
805
|
+
resolved.setChannelRuntime = entry.setRuntime;
|
|
806
|
+
}
|
|
807
|
+
return resolved;
|
|
758
808
|
}
|
|
759
809
|
|
|
760
810
|
export function defineSetupPluginEntry(entry) {
|
|
761
|
-
return
|
|
811
|
+
return isPlainObject(entry) && entry.plugin ? entry : { plugin: entry };
|
|
762
812
|
}
|
|
763
813
|
|
|
764
814
|
export function createChatChannelPlugin(entry) {
|
|
765
|
-
|
|
815
|
+
if (!isPlainObject(entry) || !entry.base) {
|
|
816
|
+
return normalizeEntry(entry);
|
|
817
|
+
}
|
|
818
|
+
return {
|
|
819
|
+
...entry.base,
|
|
820
|
+
conversationBindings: {
|
|
821
|
+
supportsCurrentConversationBinding: true,
|
|
822
|
+
...(entry.base.conversationBindings ?? {}),
|
|
823
|
+
},
|
|
824
|
+
...(entry.security ? { security: resolveChannelSecurity(entry.security) } : {}),
|
|
825
|
+
...(entry.pairing ? { pairing: resolveChannelPairing(entry.pairing) } : {}),
|
|
826
|
+
...(entry.threading ? { threading: resolveChannelThreading(entry.threading) } : {}),
|
|
827
|
+
...(entry.outbound ? { outbound: resolveChannelOutbound(entry.outbound) } : {}),
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
export function createChannelPluginBase(params = {}) {
|
|
832
|
+
return {
|
|
833
|
+
id: params.id ?? "fixture-channel",
|
|
834
|
+
meta: { id: params.id ?? "fixture-channel", ...(params.meta ?? {}) },
|
|
835
|
+
...(params.setupWizard ? { setupWizard: params.setupWizard } : {}),
|
|
836
|
+
...(params.capabilities ? { capabilities: params.capabilities } : {}),
|
|
837
|
+
...(params.commands ? { commands: params.commands } : {}),
|
|
838
|
+
...(params.doctor ? { doctor: params.doctor } : {}),
|
|
839
|
+
...(params.agentPrompt ? { agentPrompt: params.agentPrompt } : {}),
|
|
840
|
+
...(params.streaming ? { streaming: params.streaming } : {}),
|
|
841
|
+
...(params.reload ? { reload: params.reload } : {}),
|
|
842
|
+
...(params.gatewayMethods ? { gatewayMethods: params.gatewayMethods } : {}),
|
|
843
|
+
...(params.configSchema ? { configSchema: createConfigSchema(params.configSchema) } : {}),
|
|
844
|
+
...(params.config ? { config: params.config } : {}),
|
|
845
|
+
...(params.security ? { security: params.security } : {}),
|
|
846
|
+
...(params.groups ? { groups: params.groups } : {}),
|
|
847
|
+
setup: params.setup ?? (() => ({})),
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function resolveChannelSecurity(security) {
|
|
852
|
+
if (!isPlainObject(security) || !security.dm) {
|
|
853
|
+
return security;
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
resolveDmPolicy: ({ account } = {}) => ({
|
|
857
|
+
policy: security.dm.resolvePolicy?.(account ?? {}) ?? security.dm.defaultPolicy ?? "allow",
|
|
858
|
+
allowFrom: security.dm.resolveAllowFrom?.(account ?? {}) ?? [],
|
|
859
|
+
}),
|
|
860
|
+
...(security.collectWarnings ? { collectWarnings: security.collectWarnings } : {}),
|
|
861
|
+
...(security.collectAuditFindings ? { collectAuditFindings: security.collectAuditFindings } : {}),
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function resolveChannelPairing(pairing) {
|
|
866
|
+
if (!isPlainObject(pairing) || !pairing.text) {
|
|
867
|
+
return pairing;
|
|
868
|
+
}
|
|
869
|
+
return {
|
|
870
|
+
idLabel: pairing.text.idLabel,
|
|
871
|
+
normalizeAllowEntry: pairing.text.normalizeAllowEntry,
|
|
872
|
+
notifyApproval: (ctx) => pairing.text.notify?.({ ...ctx, message: pairing.text.message }),
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function resolveChannelThreading(threading) {
|
|
877
|
+
if (!isPlainObject(threading)) {
|
|
878
|
+
return threading;
|
|
879
|
+
}
|
|
880
|
+
if (threading.resolveReplyToMode) {
|
|
881
|
+
return threading;
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
...threading,
|
|
885
|
+
resolveReplyToMode: () =>
|
|
886
|
+
threading.topLevelReplyToMode ??
|
|
887
|
+
threading.scopedAccountReplyToMode?.fallback ??
|
|
888
|
+
"thread",
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function resolveChannelOutbound(outbound) {
|
|
893
|
+
if (!isPlainObject(outbound) || !outbound.attachedResults) {
|
|
894
|
+
return outbound;
|
|
895
|
+
}
|
|
896
|
+
const { base = {}, attachedResults } = outbound;
|
|
897
|
+
return {
|
|
898
|
+
...base,
|
|
899
|
+
...(attachedResults.sendText
|
|
900
|
+
? { sendText: async (ctx) => ({ channel: attachedResults.channel, ...(await attachedResults.sendText(ctx)) }) }
|
|
901
|
+
: {}),
|
|
902
|
+
...(attachedResults.sendMedia
|
|
903
|
+
? { sendMedia: async (ctx) => ({ channel: attachedResults.channel, ...(await attachedResults.sendMedia(ctx)) }) }
|
|
904
|
+
: {}),
|
|
905
|
+
...(attachedResults.sendPoll
|
|
906
|
+
? { sendPoll: async (ctx) => ({ channel: attachedResults.channel, ...(await attachedResults.sendPoll(ctx)) }) }
|
|
907
|
+
: {}),
|
|
908
|
+
};
|
|
766
909
|
}
|
|
767
910
|
|
|
768
911
|
export function definePlugin(entry) {
|
|
@@ -805,13 +948,63 @@ export function jsonResult(value) {
|
|
|
805
948
|
return { content: [{ type: "text", text: JSON.stringify(value) }] };
|
|
806
949
|
}
|
|
807
950
|
|
|
808
|
-
export function readNumberParam(value,
|
|
809
|
-
const
|
|
810
|
-
|
|
951
|
+
export function readNumberParam(value, keyOrFallback = 0, options = {}) {
|
|
952
|
+
const raw = isPlainObject(value) ? value[keyOrFallback] : value;
|
|
953
|
+
const parsed = Number(raw);
|
|
954
|
+
if (Number.isFinite(parsed)) {
|
|
955
|
+
return options.integer ? Math.trunc(parsed) : parsed;
|
|
956
|
+
}
|
|
957
|
+
return isPlainObject(value) ? undefined : keyOrFallback;
|
|
811
958
|
}
|
|
812
959
|
|
|
813
|
-
export function readStringParam(value,
|
|
814
|
-
|
|
960
|
+
export function readStringParam(value, keyOrFallback = "") {
|
|
961
|
+
if (isPlainObject(value)) {
|
|
962
|
+
const raw = value[keyOrFallback];
|
|
963
|
+
return typeof raw === "string" ? raw : undefined;
|
|
964
|
+
}
|
|
965
|
+
return typeof value === "string" ? value : keyOrFallback;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export function readStringArrayParam(value, key) {
|
|
969
|
+
const raw = isPlainObject(value) ? value[key] : value;
|
|
970
|
+
if (Array.isArray(raw)) {
|
|
971
|
+
return raw.map((entry) => String(entry));
|
|
972
|
+
}
|
|
973
|
+
return typeof raw === "string" && raw ? [raw] : [];
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
export function readReactionParams(value = {}) {
|
|
977
|
+
return {
|
|
978
|
+
messageId: value.messageId ?? value.id ?? "",
|
|
979
|
+
reaction: value.reaction ?? value.emoji ?? "",
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
export function createActionGate(actions = {}) {
|
|
984
|
+
return (key, defaultValue = true) => {
|
|
985
|
+
const value = actions?.[key];
|
|
986
|
+
return value === undefined ? defaultValue : value !== false;
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
export function buildChannelOutboundSessionRoute(params = {}) {
|
|
991
|
+
const peer = params.peer ?? { kind: params.chatType ?? "direct", id: params.to ?? "fixture-peer" };
|
|
992
|
+
const baseSessionKey = [
|
|
993
|
+
params.agentId ?? "agent",
|
|
994
|
+
params.channel ?? "channel",
|
|
995
|
+
params.accountId ?? "default",
|
|
996
|
+
peer.kind,
|
|
997
|
+
peer.id,
|
|
998
|
+
].filter(Boolean).join(":");
|
|
999
|
+
return {
|
|
1000
|
+
sessionKey: baseSessionKey,
|
|
1001
|
+
baseSessionKey,
|
|
1002
|
+
peer,
|
|
1003
|
+
chatType: params.chatType ?? peer.kind ?? "direct",
|
|
1004
|
+
from: params.from ?? "fixture-source",
|
|
1005
|
+
to: params.to ?? peer.id,
|
|
1006
|
+
...(params.threadId !== undefined ? { threadId: params.threadId } : {}),
|
|
1007
|
+
};
|
|
815
1008
|
}
|
|
816
1009
|
|
|
817
1010
|
export function createDedupeCache() {
|
|
@@ -1129,7 +1322,7 @@ export function createSubsystemLogger() {
|
|
|
1129
1322
|
}
|
|
1130
1323
|
|
|
1131
1324
|
export function buildThreadAwareOutboundSessionRoute(route = {}) {
|
|
1132
|
-
return route;
|
|
1325
|
+
return route.route ?? route;
|
|
1133
1326
|
}
|
|
1134
1327
|
|
|
1135
1328
|
export function clearAccountEntryFields(entry = {}) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { buildContractCapture } from "./contract-capture.js";
|
|
2
|
+
import { buildSyntheticProbePlan } from "./synthetic-probes.js";
|
|
3
|
+
|
|
4
|
+
export function buildSyntheticProbePlanFromReport(report, options = {}) {
|
|
5
|
+
const capture = options.capture ?? buildContractCapture({
|
|
6
|
+
report,
|
|
7
|
+
hookAssertions: options.hookAssertions,
|
|
8
|
+
hookContexts: options.hookContexts,
|
|
9
|
+
hookEvents: options.hookEvents,
|
|
10
|
+
registrationArguments: options.registrationArguments,
|
|
11
|
+
registrationAssertions: options.registrationAssertions,
|
|
12
|
+
});
|
|
13
|
+
return buildSyntheticProbePlan({
|
|
14
|
+
capture,
|
|
15
|
+
hookContexts: options.hookContexts,
|
|
16
|
+
hookEvents: options.hookEvents,
|
|
17
|
+
registrationArguments: options.registrationArguments,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { mkdtemp } from "node:fs/promises";
|
|
3
4
|
import { register } from "node:module";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import path from "node:path";
|
|
@@ -59,17 +60,20 @@ async function captureForSyntheticProbes(entrypoint, options) {
|
|
|
59
60
|
const resolvedEntrypoint = path.resolve(process.cwd(), entrypoint);
|
|
60
61
|
const pluginRoot = path.resolve(process.cwd(), options.pluginRoot ?? path.dirname(resolvedEntrypoint));
|
|
61
62
|
const workspace = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-synthetic-mock-sdk-"));
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
cleanupTempDirOnExit(workspace);
|
|
64
|
+
const { loaderPath } = await createMockSdkPackage(workspace, { pluginRoot });
|
|
65
|
+
register(pathToFileURL(loaderPath));
|
|
66
|
+
return captureEntrypoint(entrypoint, {
|
|
67
|
+
...options,
|
|
68
|
+
mockSdk: false,
|
|
69
|
+
pluginRoot,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function cleanupTempDirOnExit(dir) {
|
|
74
|
+
process.once("exit", () => {
|
|
75
|
+
rmSync(dir, { force: true, recursive: true });
|
|
76
|
+
});
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
function readFlag(commandArgs, name) {
|
package/src/synthetic-probes.js
CHANGED
|
@@ -214,6 +214,12 @@ export const defaultSyntheticRegistrationProbeInputs = {
|
|
|
214
214
|
handler: commandProbeArgs,
|
|
215
215
|
run: commandProbeArgs,
|
|
216
216
|
},
|
|
217
|
+
registerChannel: {
|
|
218
|
+
handleMessage: channelReceiveProbeArgs,
|
|
219
|
+
receive: channelReceiveProbeArgs,
|
|
220
|
+
send: channelSendProbeArgs,
|
|
221
|
+
sendMessage: channelSendProbeArgs,
|
|
222
|
+
},
|
|
217
223
|
registerGatewayMethod: {
|
|
218
224
|
execute: gatewayProbeArgs,
|
|
219
225
|
handler: gatewayProbeArgs,
|
|
@@ -523,6 +529,9 @@ function syntheticRegistrationEvent(registrar, property, options) {
|
|
|
523
529
|
id: beforeToolCall.toolCallId,
|
|
524
530
|
name: beforeToolCall.toolName,
|
|
525
531
|
},
|
|
532
|
+
respond(ok, result, error) {
|
|
533
|
+
return { ok, result, ...(error ? { error } : {}) };
|
|
534
|
+
},
|
|
526
535
|
};
|
|
527
536
|
}
|
|
528
537
|
|
|
@@ -576,9 +585,11 @@ function commandProbeArgs(event) {
|
|
|
576
585
|
function gatewayProbeArgs(event) {
|
|
577
586
|
return [
|
|
578
587
|
{
|
|
588
|
+
...event,
|
|
579
589
|
params: event.params,
|
|
580
590
|
body: event.body,
|
|
581
591
|
headers: event.headers,
|
|
592
|
+
respond: event.respond,
|
|
582
593
|
},
|
|
583
594
|
{
|
|
584
595
|
source: event.source,
|
|
@@ -587,6 +598,47 @@ function gatewayProbeArgs(event) {
|
|
|
587
598
|
];
|
|
588
599
|
}
|
|
589
600
|
|
|
601
|
+
function channelSendProbeArgs(event) {
|
|
602
|
+
return [
|
|
603
|
+
{
|
|
604
|
+
source: event.source,
|
|
605
|
+
channelId: "fixture-channel",
|
|
606
|
+
accountId: "fixture-account",
|
|
607
|
+
to: "fixture-recipient",
|
|
608
|
+
text: "fixture message",
|
|
609
|
+
replyToId: "fixture-reply",
|
|
610
|
+
threadId: "fixture-thread",
|
|
611
|
+
logger: console,
|
|
612
|
+
signal: new AbortController().signal,
|
|
613
|
+
},
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function channelReceiveProbeArgs(event) {
|
|
618
|
+
return [
|
|
619
|
+
{
|
|
620
|
+
source: event.source,
|
|
621
|
+
channelId: "fixture-channel",
|
|
622
|
+
accountId: "fixture-account",
|
|
623
|
+
message: {
|
|
624
|
+
id: "message-fixture",
|
|
625
|
+
text: "fixture inbound message",
|
|
626
|
+
sender: { id: "sender-fixture", displayName: "Fixture Sender" },
|
|
627
|
+
},
|
|
628
|
+
route: {
|
|
629
|
+
sessionKey: "fixture-session",
|
|
630
|
+
baseSessionKey: "fixture-base-session",
|
|
631
|
+
peer: { kind: "direct", id: "sender-fixture" },
|
|
632
|
+
chatType: "direct",
|
|
633
|
+
from: "sender-fixture",
|
|
634
|
+
to: "fixture-channel",
|
|
635
|
+
},
|
|
636
|
+
logger: console,
|
|
637
|
+
signal: new AbortController().signal,
|
|
638
|
+
},
|
|
639
|
+
];
|
|
640
|
+
}
|
|
641
|
+
|
|
590
642
|
function interactiveProbeArgs(event) {
|
|
591
643
|
return [
|
|
592
644
|
{
|
|
@@ -604,7 +656,10 @@ function lifecycleProbeArgs(event) {
|
|
|
604
656
|
return [
|
|
605
657
|
{
|
|
606
658
|
source: event.source,
|
|
659
|
+
config: {},
|
|
607
660
|
logger: console,
|
|
661
|
+
runtime: { env: {}, logger: console },
|
|
662
|
+
secrets: { get: async () => null, has: async () => false },
|
|
608
663
|
signal: new AbortController().signal,
|
|
609
664
|
},
|
|
610
665
|
];
|