x402-proxy 0.6.0 → 0.7.1
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 +22 -1
- package/README.md +11 -2
- package/dist/bin/cli.js +76 -29
- package/dist/index.js +18 -14
- package/dist/openclaw/plugin.d.ts +55 -0
- package/dist/openclaw/plugin.js +1120 -0
- package/dist/openclaw.plugin.json +28 -0
- package/openclaw.plugin.json +28 -0
- package/package.json +26 -4
- package/skills/SKILL.md +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.1] - 2026-03-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Non-402 server errors (500, 503, etc.) now indicate whether payment was attempted, helping users distinguish "server down" from "payment failed"
|
|
14
|
+
|
|
15
|
+
## [0.7.0] - 2026-03-20
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `--verbose` flag on `fetch` command - debug logging for protocol negotiation, headers, session lifecycle, and payment flow
|
|
19
|
+
- OpenClaw plugin integration (`x402-proxy/openclaw`) - registers `x_balance` tool, `x_payment` tool, `/x_wallet` command, and HTTP route proxy for upstream x402 endpoints
|
|
20
|
+
- `openclaw.plugin.json` manifest with config schema for providers, keypair path, RPC URL, and dashboard URL
|
|
21
|
+
- `./openclaw` subpath export in package.json
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- MPP SSE requests silently losing `Content-Type` and other headers when `Headers` instances are spread (workaround for mppx SDK bug, upstream fix: wevm/mppx#209)
|
|
25
|
+
- MPP session `close()` errors no longer crash the CLI - wrapped in try/catch with verbose error reporting
|
|
26
|
+
- MPP payment history now includes `amount` (converted from base units) and `channelId` in transaction records
|
|
27
|
+
- MPP streaming history records now use `channelId` as fallback for `tx` field when no receipt reference is available
|
|
28
|
+
|
|
10
29
|
## [0.6.0] - 2026-03-19
|
|
11
30
|
|
|
12
31
|
### Added
|
|
@@ -172,7 +191,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
172
191
|
- `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
|
|
173
192
|
- Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
|
|
174
193
|
|
|
175
|
-
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.
|
|
194
|
+
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.1...HEAD
|
|
195
|
+
[0.7.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.0...v0.7.1
|
|
196
|
+
[0.7.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.6.0...v0.7.0
|
|
176
197
|
[0.6.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.2...v0.6.0
|
|
177
198
|
[0.5.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.1...v0.5.2
|
|
178
199
|
[0.5.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.0...v0.5.1
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# x402-proxy
|
|
2
2
|
|
|
3
|
-
`curl` for [x402](https://www.x402.org/) paid APIs. Auto-pays HTTP 402 responses with USDC on Base, Solana, and Tempo - zero crypto code on the buyer side. Supports
|
|
3
|
+
`curl` for [x402](https://www.x402.org/) and [MPP](https://mpp.dev/) paid APIs. Auto-pays HTTP 402 responses with USDC on Base, Solana, and [Tempo](https://tempo.xyz/) - zero crypto code on the buyer side. Supports one-time payments (x402, MPP charge) and pay-per-token streaming (MPP sessions).
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -30,7 +30,7 @@ Let your AI agent consume any paid MCP server. Configure in Claude, Cursor, or a
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The proxy sits between your agent and the remote server, intercepting 402 responses, paying automatically, and forwarding the result. Your agent never touches crypto.
|
|
33
|
+
The proxy sits between your agent and the remote server, intercepting 402 responses, paying automatically, and forwarding the result. Supports both x402 and MPP protocols. Your agent never touches crypto.
|
|
34
34
|
|
|
35
35
|
## HTTP Requests
|
|
36
36
|
|
|
@@ -49,6 +49,9 @@ $ npx x402-proxy --method POST \
|
|
|
49
49
|
# Force a specific network
|
|
50
50
|
$ npx x402-proxy --network base https://api.example.com/data
|
|
51
51
|
|
|
52
|
+
# Debug protocol negotiation and payment flow
|
|
53
|
+
$ npx x402-proxy --verbose https://api.example.com/data
|
|
54
|
+
|
|
52
55
|
# Use MPP protocol for streaming payments
|
|
53
56
|
$ npx x402-proxy --protocol mpp \
|
|
54
57
|
--method POST \
|
|
@@ -118,6 +121,12 @@ import {
|
|
|
118
121
|
|
|
119
122
|
See the [library API docs](https://github.com/cascade-protocol/x402-proxy/tree/main/packages/x402-proxy#library-api) for details.
|
|
120
123
|
|
|
124
|
+
## OpenClaw Plugin
|
|
125
|
+
|
|
126
|
+
x402-proxy ships as an [OpenClaw](https://openclaw.dev) plugin, giving your gateway automatic x402 payment capabilities. Registers `x_balance` and `x_payment` tools, `/x_wallet` command, and an HTTP route proxy for upstream x402 endpoints.
|
|
127
|
+
|
|
128
|
+
Configure providers and models in OpenClaw plugin settings. Uses the standard wallet resolution (env vars or `wallet.json`).
|
|
129
|
+
|
|
121
130
|
## License
|
|
122
131
|
|
|
123
132
|
Apache-2.0
|
package/dist/bin/cli.js
CHANGED
|
@@ -115,20 +115,24 @@ async function createMppProxyHandler(opts) {
|
|
|
115
115
|
async close() {
|
|
116
116
|
if (session?.opened) {
|
|
117
117
|
const receipt = await session.close();
|
|
118
|
-
if (receipt)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
118
|
+
if (receipt) {
|
|
119
|
+
const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
|
|
120
|
+
paymentQueue.push({
|
|
121
|
+
protocol: "mpp",
|
|
122
|
+
network: TEMPO_NETWORK,
|
|
123
|
+
intent: "session",
|
|
124
|
+
amount: spentUsdc,
|
|
125
|
+
channelId: session.channelId ?? void 0,
|
|
126
|
+
receipt: {
|
|
127
|
+
method: receipt.method,
|
|
128
|
+
reference: receipt.reference,
|
|
129
|
+
status: receipt.status,
|
|
130
|
+
timestamp: receipt.timestamp,
|
|
131
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
132
|
+
txHash: receipt.txHash
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
};
|
|
@@ -195,6 +199,11 @@ Examples:
|
|
|
195
199
|
kind: "boolean",
|
|
196
200
|
brief: "Force JSON output",
|
|
197
201
|
default: false
|
|
202
|
+
},
|
|
203
|
+
verbose: {
|
|
204
|
+
kind: "boolean",
|
|
205
|
+
brief: "Show debug details (protocol negotiation, headers, payment flow)",
|
|
206
|
+
default: false
|
|
198
207
|
}
|
|
199
208
|
},
|
|
200
209
|
positional: {
|
|
@@ -207,6 +216,18 @@ Examples:
|
|
|
207
216
|
}
|
|
208
217
|
},
|
|
209
218
|
async func(flags, url) {
|
|
219
|
+
const verbose = (msg) => {
|
|
220
|
+
if (flags.verbose) dim(` [verbose] ${msg}`);
|
|
221
|
+
};
|
|
222
|
+
const closeMppSession = async (handler) => {
|
|
223
|
+
verbose("closing MPP session...");
|
|
224
|
+
try {
|
|
225
|
+
await handler.close();
|
|
226
|
+
verbose("session closed successfully");
|
|
227
|
+
} catch (closeErr) {
|
|
228
|
+
verbose(`session close failed: ${closeErr instanceof Error ? closeErr.message : String(closeErr)}`);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
210
231
|
if (!url) {
|
|
211
232
|
if (isConfigured()) {
|
|
212
233
|
const { displayStatus } = await import("../status-w5y-fhhe.js");
|
|
@@ -270,6 +291,8 @@ Examples:
|
|
|
270
291
|
const config = loadConfig();
|
|
271
292
|
const resolvedProtocol = flags.protocol ?? config?.preferredProtocol;
|
|
272
293
|
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
294
|
+
verbose(`wallet source: ${wallet.source}`);
|
|
295
|
+
verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
|
|
273
296
|
let preferredNetwork = config?.defaultNetwork;
|
|
274
297
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
275
298
|
const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
|
|
@@ -287,7 +310,7 @@ Examples:
|
|
|
287
310
|
const method = flags.method || "GET";
|
|
288
311
|
const init = {
|
|
289
312
|
method,
|
|
290
|
-
headers
|
|
313
|
+
headers: Object.fromEntries(headers.entries())
|
|
291
314
|
};
|
|
292
315
|
if (flags.body) init.body = flags.body;
|
|
293
316
|
if (isTTY()) dim(` ${method} ${parsedUrl.toString()}`);
|
|
@@ -306,17 +329,30 @@ Examples:
|
|
|
306
329
|
evmKey: wallet.evmKey,
|
|
307
330
|
maxDeposit
|
|
308
331
|
});
|
|
309
|
-
|
|
332
|
+
const isStreamingRequest = flags.body != null && /"stream"\s*:\s*true/.test(flags.body);
|
|
333
|
+
verbose(`mpp handler created, streaming: ${isStreamingRequest}`);
|
|
334
|
+
if (isStreamingRequest) {
|
|
310
335
|
try {
|
|
336
|
+
verbose("opening SSE session...");
|
|
311
337
|
const tokens = await mppHandler.sse(parsedUrl.toString(), init);
|
|
338
|
+
verbose("SSE stream opened, reading tokens...");
|
|
312
339
|
for await (const token of tokens) process.stdout.write(token);
|
|
340
|
+
verbose("SSE stream complete");
|
|
313
341
|
} finally {
|
|
314
|
-
await mppHandler
|
|
342
|
+
await closeMppSession(mppHandler);
|
|
315
343
|
}
|
|
316
|
-
|
|
344
|
+
mppHandler.shiftPayment();
|
|
345
|
+
const closeReceipt = mppHandler.shiftPayment();
|
|
346
|
+
verbose(closeReceipt ? `close receipt: amount=${closeReceipt.amount ?? "none"}, channelId=${closeReceipt.channelId ?? "none"}, txHash=${closeReceipt.receipt?.txHash ?? "none"}` : "no close receipt (session close may have failed)");
|
|
347
|
+
mppPayment = closeReceipt ?? {
|
|
348
|
+
protocol: "mpp",
|
|
349
|
+
network: TEMPO_NETWORK,
|
|
350
|
+
intent: "session"
|
|
351
|
+
};
|
|
317
352
|
usedProtocol = "mpp";
|
|
318
353
|
const elapsedMs = Date.now() - startMs;
|
|
319
|
-
|
|
354
|
+
const spentAmount = mppPayment.amount ? Number(mppPayment.amount) : void 0;
|
|
355
|
+
if (mppPayment && isTTY()) info(` MPP session: ${spentAmount != null ? `${spentAmount.toFixed(4)} USDC ` : ""}(${displayNetwork(mppPayment.network)})`);
|
|
320
356
|
if (isTTY()) dim(` Streamed (${elapsedMs}ms)`);
|
|
321
357
|
if (mppPayment) {
|
|
322
358
|
const record = {
|
|
@@ -325,8 +361,8 @@ Examples:
|
|
|
325
361
|
kind: "mpp_payment",
|
|
326
362
|
net: mppPayment.network,
|
|
327
363
|
from: wallet.evmAddress ?? "unknown",
|
|
328
|
-
tx: mppPayment.receipt?.reference,
|
|
329
|
-
amount:
|
|
364
|
+
tx: mppPayment.receipt?.reference ?? mppPayment.channelId,
|
|
365
|
+
amount: spentAmount,
|
|
330
366
|
token: "USDC",
|
|
331
367
|
ms: elapsedMs,
|
|
332
368
|
label: parsedUrl.hostname
|
|
@@ -336,10 +372,12 @@ Examples:
|
|
|
336
372
|
if (isTTY()) process.stdout.write("\n");
|
|
337
373
|
return;
|
|
338
374
|
}
|
|
375
|
+
verbose("sending non-streaming MPP request...");
|
|
339
376
|
try {
|
|
340
377
|
response = await mppHandler.fetch(parsedUrl.toString(), init);
|
|
378
|
+
verbose(`response: ${response.status} ${response.statusText}`);
|
|
341
379
|
} finally {
|
|
342
|
-
await mppHandler
|
|
380
|
+
await closeMppSession(mppHandler);
|
|
343
381
|
}
|
|
344
382
|
mppPayment = mppHandler.shiftPayment();
|
|
345
383
|
usedProtocol = "mpp";
|
|
@@ -364,15 +402,19 @@ Examples:
|
|
|
364
402
|
x402Payment = handler.shiftPayment();
|
|
365
403
|
usedProtocol = "x402";
|
|
366
404
|
if (response.status === 402 && wallet.evmKey) {
|
|
367
|
-
|
|
405
|
+
const detected = detectProtocols(response);
|
|
406
|
+
verbose(`auto-detect: x402=${detected.x402}, mpp=${detected.mpp}`);
|
|
407
|
+
if (detected.mpp) {
|
|
408
|
+
verbose("falling through to MPP...");
|
|
368
409
|
const mppHandler = await createMppProxyHandler({
|
|
369
410
|
evmKey: wallet.evmKey,
|
|
370
411
|
maxDeposit
|
|
371
412
|
});
|
|
372
413
|
try {
|
|
373
414
|
response = await mppHandler.fetch(parsedUrl.toString(), init);
|
|
415
|
+
verbose(`MPP response: ${response.status} ${response.statusText}`);
|
|
374
416
|
} finally {
|
|
375
|
-
await mppHandler
|
|
417
|
+
await closeMppSession(mppHandler);
|
|
376
418
|
}
|
|
377
419
|
mppPayment = mppHandler.shiftPayment();
|
|
378
420
|
x402Payment = void 0;
|
|
@@ -387,6 +429,10 @@ Examples:
|
|
|
387
429
|
const elapsedMs = Date.now() - startMs;
|
|
388
430
|
const payment = x402Payment ?? mppPayment;
|
|
389
431
|
const txSig = extractTxSignature(response);
|
|
432
|
+
verbose(`protocol used: ${usedProtocol ?? "none"}`);
|
|
433
|
+
for (const [k, v] of response.headers) if (/payment|auth|www|x-pay/i.test(k)) verbose(`header ${k}: ${v.slice(0, 200)}`);
|
|
434
|
+
if (!response.ok && response.status !== 402 && isTTY()) if (!payment) dim(" Server returned error before payment was attempted.");
|
|
435
|
+
else dim(" Payment was processed but server returned an error.");
|
|
390
436
|
if (response.status === 402 && isTTY()) {
|
|
391
437
|
const detected = detectProtocols(response);
|
|
392
438
|
const prHeader = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
|
|
@@ -491,6 +537,7 @@ Examples:
|
|
|
491
537
|
net: mppPayment.network,
|
|
492
538
|
from: wallet.evmAddress ?? "unknown",
|
|
493
539
|
tx: mppPayment.receipt?.reference ?? txSig,
|
|
540
|
+
amount: mppPayment.amount ? Number(mppPayment.amount) : void 0,
|
|
494
541
|
token: "USDC",
|
|
495
542
|
ms: elapsedMs,
|
|
496
543
|
label: parsedUrl.hostname
|
|
@@ -617,7 +664,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
617
664
|
const { x402MCPClient } = await import("@x402/mcp");
|
|
618
665
|
const remoteClient = new Client({
|
|
619
666
|
name: "x402-proxy",
|
|
620
|
-
version: "0.
|
|
667
|
+
version: "0.7.1"
|
|
621
668
|
});
|
|
622
669
|
const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
|
|
623
670
|
autoPayment: true,
|
|
@@ -656,7 +703,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
656
703
|
}
|
|
657
704
|
const localServer = new Server({
|
|
658
705
|
name: "x402-proxy",
|
|
659
|
-
version: "0.
|
|
706
|
+
version: "0.7.1"
|
|
660
707
|
}, { capabilities: {
|
|
661
708
|
tools: tools.length > 0 ? {} : void 0,
|
|
662
709
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
@@ -722,7 +769,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
722
769
|
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
723
770
|
const remoteClient = new Client({
|
|
724
771
|
name: "x402-proxy",
|
|
725
|
-
version: "0.
|
|
772
|
+
version: "0.7.1"
|
|
726
773
|
});
|
|
727
774
|
await connectTransport(remoteClient);
|
|
728
775
|
const mppClient = McpClient.wrap(remoteClient, { methods: [tempo({
|
|
@@ -740,7 +787,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
740
787
|
}
|
|
741
788
|
const localServer = new Server({
|
|
742
789
|
name: "x402-proxy",
|
|
743
|
-
version: "0.
|
|
790
|
+
version: "0.7.1"
|
|
744
791
|
}, { capabilities: {
|
|
745
792
|
tools: tools.length > 0 ? {} : void 0,
|
|
746
793
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
@@ -940,7 +987,7 @@ const routes = buildRouteMap({
|
|
|
940
987
|
});
|
|
941
988
|
const app = buildApplication(routes, {
|
|
942
989
|
name: "x402-proxy",
|
|
943
|
-
versionInfo: { currentVersion: "0.
|
|
990
|
+
versionInfo: { currentVersion: "0.7.1" },
|
|
944
991
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
945
992
|
});
|
|
946
993
|
|
package/dist/index.js
CHANGED
|
@@ -112,20 +112,24 @@ async function createMppProxyHandler(opts) {
|
|
|
112
112
|
async close() {
|
|
113
113
|
if (session?.opened) {
|
|
114
114
|
const receipt = await session.close();
|
|
115
|
-
if (receipt)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
if (receipt) {
|
|
116
|
+
const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
|
|
117
|
+
paymentQueue.push({
|
|
118
|
+
protocol: "mpp",
|
|
119
|
+
network: TEMPO_NETWORK,
|
|
120
|
+
intent: "session",
|
|
121
|
+
amount: spentUsdc,
|
|
122
|
+
channelId: session.channelId ?? void 0,
|
|
123
|
+
receipt: {
|
|
124
|
+
method: receipt.method,
|
|
125
|
+
reference: receipt.reference,
|
|
126
|
+
status: receipt.status,
|
|
127
|
+
timestamp: receipt.timestamp,
|
|
128
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
129
|
+
txHash: receipt.txHash
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { x402Client } from "@x402/fetch";
|
|
2
|
+
//#region src/openclaw/tools.d.ts
|
|
3
|
+
type ModelEntry = {
|
|
4
|
+
provider: string;
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
maxTokens: number;
|
|
8
|
+
reasoning: boolean;
|
|
9
|
+
input: string[];
|
|
10
|
+
cost: {
|
|
11
|
+
input: number;
|
|
12
|
+
output: number;
|
|
13
|
+
cacheRead: number;
|
|
14
|
+
cacheWrite: number;
|
|
15
|
+
};
|
|
16
|
+
contextWindow: number;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/openclaw/plugin.d.ts
|
|
20
|
+
type OpenClawPluginApi = {
|
|
21
|
+
pluginConfig?: Record<string, unknown>;
|
|
22
|
+
logger: {
|
|
23
|
+
info: (msg: string) => void;
|
|
24
|
+
error: (msg: string) => void;
|
|
25
|
+
};
|
|
26
|
+
registerProvider: (provider: {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
auth: unknown[];
|
|
30
|
+
models: {
|
|
31
|
+
baseUrl: string;
|
|
32
|
+
api: string;
|
|
33
|
+
authHeader: boolean;
|
|
34
|
+
models: Array<Omit<ModelEntry, "provider"> & {
|
|
35
|
+
input: Array<"text" | "image">;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
}) => void;
|
|
39
|
+
registerTool: (tool: unknown) => void;
|
|
40
|
+
registerCommand: (command: unknown) => void;
|
|
41
|
+
registerService: (service: {
|
|
42
|
+
id: string;
|
|
43
|
+
start: () => Promise<void>;
|
|
44
|
+
stop: () => Promise<void>;
|
|
45
|
+
}) => void;
|
|
46
|
+
registerHttpRoute: (params: {
|
|
47
|
+
path: string;
|
|
48
|
+
match: string;
|
|
49
|
+
auth: string;
|
|
50
|
+
handler: (req: unknown, res: unknown) => Promise<void>;
|
|
51
|
+
}) => void;
|
|
52
|
+
};
|
|
53
|
+
declare function register(api: OpenClawPluginApi): void;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { register };
|