@agentlayer.tech/wallet 0.1.14 → 0.1.15
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/.openclaw/extensions/agent-wallet/README.md +20 -0
- package/.openclaw/extensions/agent-wallet/dist/index.js +1963 -0
- package/.openclaw/extensions/agent-wallet/index.ts +8 -1
- package/.openclaw/extensions/agent-wallet/package.json +44 -5
- package/.openclaw/extensions/pay-bridge/README.md +6 -0
- package/.openclaw/extensions/pay-bridge/dist/core.mjs +287 -0
- package/.openclaw/extensions/pay-bridge/dist/index.js +196 -0
- package/.openclaw/extensions/pay-bridge/package.json +43 -5
- package/CHANGELOG.md +19 -0
- package/README.md +32 -0
- package/RELEASING.md +70 -0
- package/agent-wallet/README.md +14 -0
- package/agent-wallet/pyproject.toml +1 -1
- package/package.json +3 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { promisify } from "node:util";
|
|
6
7
|
|
|
@@ -390,10 +391,16 @@ function resolvePythonBin(config) {
|
|
|
390
391
|
return config.pythonBin || process.env.OPENCLAW_AGENT_WALLET_PYTHON || "python3";
|
|
391
392
|
}
|
|
392
393
|
|
|
394
|
+
function resolveOpenclawHome(config) {
|
|
395
|
+
return path.resolve(config.openclawHome || process.env.OPENCLAW_HOME || path.join(os.homedir(), ".openclaw"));
|
|
396
|
+
}
|
|
397
|
+
|
|
393
398
|
function resolvePackageRoot(config) {
|
|
399
|
+
const openclawHome = resolveOpenclawHome(config);
|
|
394
400
|
const candidates = [
|
|
395
401
|
config.packageRoot,
|
|
396
402
|
process.env.OPENCLAW_AGENT_WALLET_PACKAGE_ROOT,
|
|
403
|
+
path.join(openclawHome, "agent-wallet-runtime/current/agent-wallet"),
|
|
397
404
|
path.resolve(PLUGIN_ROOT, "../../../agent-wallet"),
|
|
398
405
|
path.resolve(process.cwd(), "agent-wallet"),
|
|
399
406
|
].filter(Boolean);
|
|
@@ -405,7 +412,7 @@ function resolvePackageRoot(config) {
|
|
|
405
412
|
}
|
|
406
413
|
}
|
|
407
414
|
throw new Error(
|
|
408
|
-
|
|
415
|
+
`Could not resolve agent-wallet package root. Checked ${path.join(openclawHome, "agent-wallet-runtime/current/agent-wallet")} and local workspace fallbacks. Set plugins.entries.agent-wallet.config.packageRoot if your runtime lives elsewhere.`
|
|
409
416
|
);
|
|
410
417
|
}
|
|
411
418
|
|
|
@@ -1,11 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "agent-wallet",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"
|
|
2
|
+
"name": "@agentlayertech/agent-wallet-plugin",
|
|
3
|
+
"version": "0.1.15",
|
|
4
|
+
"description": "OpenClaw plugin bridge for the AgentLayer wallet runtime.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"license": "SEE LICENSE IN ../../../LICENSE",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/lopushok9/Agent-Layer.git",
|
|
10
|
+
"directory": ".openclaw/extensions/agent-wallet"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/lopushok9/Agent-Layer/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/lopushok9/Agent-Layer/tree/main/.openclaw/extensions/agent-wallet#readme",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"index.ts",
|
|
21
|
+
"dist/",
|
|
22
|
+
"openclaw.plugin.json",
|
|
23
|
+
"README.md",
|
|
24
|
+
"skills/"
|
|
25
|
+
],
|
|
6
26
|
"openclaw": {
|
|
7
27
|
"extensions": [
|
|
8
28
|
"./index.ts"
|
|
9
|
-
]
|
|
10
|
-
|
|
29
|
+
],
|
|
30
|
+
"runtimeExtensions": [
|
|
31
|
+
"./dist/index.js"
|
|
32
|
+
],
|
|
33
|
+
"compat": {
|
|
34
|
+
"pluginApi": ">=2026.3.24-beta.2",
|
|
35
|
+
"minGatewayVersion": "2026.3.24-beta.2"
|
|
36
|
+
},
|
|
37
|
+
"build": {
|
|
38
|
+
"openclawVersion": "2026.3.24-beta.2",
|
|
39
|
+
"pluginSdkVersion": "2026.3.24-beta.2"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"openclaw",
|
|
44
|
+
"plugin",
|
|
45
|
+
"wallet",
|
|
46
|
+
"solana",
|
|
47
|
+
"bitcoin",
|
|
48
|
+
"evm"
|
|
49
|
+
]
|
|
11
50
|
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Thin OpenClaw bridge to the locally installed `pay` CLI.
|
|
4
4
|
|
|
5
|
+
External install path:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
openclaw plugins install clawhub:@agentlayertech/pay-bridge-plugin
|
|
9
|
+
```
|
|
10
|
+
|
|
5
11
|
This plugin is intentionally separate from `agent-wallet`:
|
|
6
12
|
|
|
7
13
|
- `agent-wallet` remains the execution wallet stack for Solana/EVM/BTC
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
const ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
6
|
+
|
|
7
|
+
function stripAnsi(text) {
|
|
8
|
+
return String(text || "").replace(ANSI_RE, "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function nonEmptyLines(text) {
|
|
12
|
+
return stripAnsi(text)
|
|
13
|
+
.split(/\r?\n/)
|
|
14
|
+
.map((line) => line.trim())
|
|
15
|
+
.filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function extractLastJsonValue(text) {
|
|
19
|
+
const lines = nonEmptyLines(text);
|
|
20
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
21
|
+
const candidate = lines[i];
|
|
22
|
+
if (!candidate.startsWith("{") && !candidate.startsWith("[")) continue;
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(candidate);
|
|
25
|
+
} catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function collectStringLeaves(value, acc = new Set()) {
|
|
33
|
+
if (typeof value === "string") {
|
|
34
|
+
acc.add(value);
|
|
35
|
+
return acc;
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
for (const item of value) collectStringLeaves(item, acc);
|
|
39
|
+
return acc;
|
|
40
|
+
}
|
|
41
|
+
if (value && typeof value === "object") {
|
|
42
|
+
for (const item of Object.values(value)) collectStringLeaves(item, acc);
|
|
43
|
+
}
|
|
44
|
+
return acc;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function withAccountArgs(args, account) {
|
|
48
|
+
if (!account) return args;
|
|
49
|
+
return [...args, "--account", account];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolvePayBinary(config = {}) {
|
|
53
|
+
return (
|
|
54
|
+
config.payBinary ||
|
|
55
|
+
process.env.OPENCLAW_PAY_BINARY ||
|
|
56
|
+
"pay"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function runPayCommand(payBinary, args, options = {}) {
|
|
61
|
+
const { cwd, input = null } = options;
|
|
62
|
+
let stdout = "";
|
|
63
|
+
let stderr = "";
|
|
64
|
+
try {
|
|
65
|
+
const result = await execFileAsync(payBinary, args, {
|
|
66
|
+
cwd,
|
|
67
|
+
env: { ...process.env },
|
|
68
|
+
input,
|
|
69
|
+
maxBuffer: 1024 * 1024 * 8,
|
|
70
|
+
});
|
|
71
|
+
stdout = result.stdout ?? "";
|
|
72
|
+
stderr = result.stderr ?? "";
|
|
73
|
+
} catch (error) {
|
|
74
|
+
stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
75
|
+
stderr = typeof error?.stderr === "string" ? error.stderr : "";
|
|
76
|
+
const payload = extractLastJsonValue(stdout) || extractLastJsonValue(stderr);
|
|
77
|
+
const message =
|
|
78
|
+
payload?.error?.message ||
|
|
79
|
+
payload?.message ||
|
|
80
|
+
stripAnsi(stderr || stdout || error?.message || "pay command failed").trim() ||
|
|
81
|
+
"pay command failed";
|
|
82
|
+
const wrapped = new Error(message);
|
|
83
|
+
wrapped.stdout = stdout;
|
|
84
|
+
wrapped.stderr = stderr;
|
|
85
|
+
wrapped.details = payload && typeof payload === "object" ? payload : null;
|
|
86
|
+
throw wrapped;
|
|
87
|
+
}
|
|
88
|
+
return { stdout, stderr };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function parseWhoamiOutput(stdout) {
|
|
92
|
+
const lines = nonEmptyLines(stdout);
|
|
93
|
+
const systemUser = lines[0] || null;
|
|
94
|
+
const noAccount = lines.some((line) => /no mainnet account/i.test(line));
|
|
95
|
+
return {
|
|
96
|
+
system_user: systemUser,
|
|
97
|
+
has_mainnet_account: !noAccount,
|
|
98
|
+
raw_lines: lines,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function parseAccountListOutput(stdout) {
|
|
103
|
+
const lines = nonEmptyLines(stdout);
|
|
104
|
+
const noAccounts = lines.some((line) => /no accounts found/i.test(line));
|
|
105
|
+
return {
|
|
106
|
+
has_accounts: !noAccounts,
|
|
107
|
+
raw_lines: lines,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function getPayStatus(config = {}, options = {}) {
|
|
112
|
+
const payBinary = resolvePayBinary(config);
|
|
113
|
+
const versionResult = await runPayCommand(payBinary, ["--version"], options);
|
|
114
|
+
const whoamiResult = await runPayCommand(
|
|
115
|
+
payBinary,
|
|
116
|
+
withAccountArgs(["whoami"], config.defaultAccount),
|
|
117
|
+
options
|
|
118
|
+
);
|
|
119
|
+
const accountListResult = await runPayCommand(payBinary, ["account", "list"], options);
|
|
120
|
+
return {
|
|
121
|
+
installed: true,
|
|
122
|
+
pay_binary: payBinary,
|
|
123
|
+
version: stripAnsi(versionResult.stdout).trim() || null,
|
|
124
|
+
account_configured: parseWhoamiOutput(whoamiResult.stdout).has_mainnet_account,
|
|
125
|
+
has_any_accounts: parseAccountListOutput(accountListResult.stdout).has_accounts,
|
|
126
|
+
whoami: parseWhoamiOutput(whoamiResult.stdout),
|
|
127
|
+
accounts: parseAccountListOutput(accountListResult.stdout),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function getPayWalletInfo(config = {}, options = {}) {
|
|
132
|
+
const payBinary = resolvePayBinary(config);
|
|
133
|
+
const whoamiResult = await runPayCommand(
|
|
134
|
+
payBinary,
|
|
135
|
+
withAccountArgs(["whoami"], config.defaultAccount),
|
|
136
|
+
options
|
|
137
|
+
);
|
|
138
|
+
const accountListResult = await runPayCommand(payBinary, ["account", "list"], options);
|
|
139
|
+
return {
|
|
140
|
+
pay_binary: payBinary,
|
|
141
|
+
default_account: config.defaultAccount || null,
|
|
142
|
+
whoami: parseWhoamiOutput(whoamiResult.stdout),
|
|
143
|
+
accounts: parseAccountListOutput(accountListResult.stdout),
|
|
144
|
+
notes: [
|
|
145
|
+
"This wallet is managed by pay.sh and is separate from the AgentLayer execution wallet.",
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function searchPayServices(config = {}, params = {}, options = {}) {
|
|
151
|
+
const payBinary = resolvePayBinary(config);
|
|
152
|
+
const args = ["skills", "search"];
|
|
153
|
+
if (params.query) args.push(String(params.query));
|
|
154
|
+
if (params.category) args.push("--category", String(params.category));
|
|
155
|
+
args.push("--json");
|
|
156
|
+
const { stdout, stderr } = await runPayCommand(
|
|
157
|
+
payBinary,
|
|
158
|
+
withAccountArgs(args, params.account || config.defaultAccount),
|
|
159
|
+
options
|
|
160
|
+
);
|
|
161
|
+
const parsed = JSON.parse(stdout.trim() || "{}");
|
|
162
|
+
return {
|
|
163
|
+
query: params.query || "",
|
|
164
|
+
category: params.category || null,
|
|
165
|
+
results: parsed,
|
|
166
|
+
warnings: nonEmptyLines(stderr),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function getPayServiceEndpoints(config = {}, params = {}, options = {}) {
|
|
171
|
+
const payBinary = resolvePayBinary(config);
|
|
172
|
+
const args = [
|
|
173
|
+
"skills",
|
|
174
|
+
"endpoints",
|
|
175
|
+
String(params.service_fqn),
|
|
176
|
+
String(params.resource),
|
|
177
|
+
"--json",
|
|
178
|
+
];
|
|
179
|
+
const { stdout, stderr } = await runPayCommand(
|
|
180
|
+
payBinary,
|
|
181
|
+
withAccountArgs(args, params.account || config.defaultAccount),
|
|
182
|
+
options
|
|
183
|
+
);
|
|
184
|
+
const parsed = JSON.parse(stdout.trim() || "{}");
|
|
185
|
+
return {
|
|
186
|
+
service_fqn: String(params.service_fqn),
|
|
187
|
+
resource: String(params.resource),
|
|
188
|
+
endpoints: parsed,
|
|
189
|
+
warnings: nonEmptyLines(stderr),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function ensureHttps(url, requireHttps = true) {
|
|
194
|
+
if (!requireHttps) return;
|
|
195
|
+
const parsed = new URL(url);
|
|
196
|
+
if (parsed.protocol !== "https:") {
|
|
197
|
+
throw new Error("pay_api_request only allows https URLs.");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function appendQuery(url, query) {
|
|
202
|
+
const parsed = new URL(url);
|
|
203
|
+
if (query && typeof query === "object") {
|
|
204
|
+
for (const [key, value] of Object.entries(query)) {
|
|
205
|
+
if (value === undefined || value === null) continue;
|
|
206
|
+
parsed.searchParams.set(key, String(value));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return parsed.toString();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function endpointPayloadContainsUrl(endpointPayload, url) {
|
|
213
|
+
const strings = collectStringLeaves(endpointPayload);
|
|
214
|
+
return strings.has(url);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function executePayApiRequest(config = {}, params = {}, options = {}) {
|
|
218
|
+
if (params.user_confirmed !== true) {
|
|
219
|
+
throw new Error("pay_api_request requires user_confirmed=true.");
|
|
220
|
+
}
|
|
221
|
+
if (!params.purpose || !String(params.purpose).trim()) {
|
|
222
|
+
throw new Error("pay_api_request requires a non-empty purpose.");
|
|
223
|
+
}
|
|
224
|
+
if (!params.service_fqn || !params.resource || !params.url) {
|
|
225
|
+
throw new Error("pay_api_request requires service_fqn, resource, and url.");
|
|
226
|
+
}
|
|
227
|
+
if (params.json_body !== undefined && params.text_body !== undefined) {
|
|
228
|
+
throw new Error("Provide either json_body or text_body, not both.");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const endpointData = await getPayServiceEndpoints(config, {
|
|
232
|
+
account: params.account,
|
|
233
|
+
resource: params.resource,
|
|
234
|
+
service_fqn: params.service_fqn,
|
|
235
|
+
}, options);
|
|
236
|
+
|
|
237
|
+
const finalUrl = appendQuery(String(params.url), params.query);
|
|
238
|
+
ensureHttps(finalUrl, config.requireHttps !== false);
|
|
239
|
+
if (!endpointPayloadContainsUrl(endpointData.endpoints, String(params.url))) {
|
|
240
|
+
throw new Error("The requested URL is not present in pay_get_service_endpoints for this service/resource.");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const payBinary = resolvePayBinary(config);
|
|
244
|
+
const method = String(params.method || "GET").toUpperCase();
|
|
245
|
+
const args = ["curl"];
|
|
246
|
+
if (params.account || config.defaultAccount) {
|
|
247
|
+
args.push("--account", String(params.account || config.defaultAccount));
|
|
248
|
+
}
|
|
249
|
+
args.push("--request", method);
|
|
250
|
+
|
|
251
|
+
const headers = params.headers && typeof params.headers === "object" ? params.headers : {};
|
|
252
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
253
|
+
args.push("--header", `${key}: ${String(value)}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (params.json_body !== undefined) {
|
|
257
|
+
const hasContentType = Object.keys(headers).some((key) => key.toLowerCase() === "content-type");
|
|
258
|
+
if (!hasContentType) {
|
|
259
|
+
args.push("--header", "content-type: application/json");
|
|
260
|
+
}
|
|
261
|
+
args.push("--data", JSON.stringify(params.json_body));
|
|
262
|
+
} else if (params.text_body !== undefined) {
|
|
263
|
+
args.push("--data", String(params.text_body));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
args.push(finalUrl);
|
|
267
|
+
const { stdout, stderr } = await runPayCommand(payBinary, args, options);
|
|
268
|
+
const trimmed = stdout.trim();
|
|
269
|
+
let responseBody = trimmed;
|
|
270
|
+
if (params.parse_json_response !== false) {
|
|
271
|
+
try {
|
|
272
|
+
responseBody = JSON.parse(trimmed);
|
|
273
|
+
} catch {
|
|
274
|
+
responseBody = trimmed;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
method,
|
|
279
|
+
purpose: String(params.purpose),
|
|
280
|
+
request_url: finalUrl,
|
|
281
|
+
service_fqn: String(params.service_fqn),
|
|
282
|
+
resource: String(params.resource),
|
|
283
|
+
response: responseBody,
|
|
284
|
+
raw_response_text: trimmed,
|
|
285
|
+
warnings: nonEmptyLines(stderr),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executePayApiRequest,
|
|
3
|
+
getPayServiceEndpoints,
|
|
4
|
+
getPayStatus,
|
|
5
|
+
getPayWalletInfo,
|
|
6
|
+
searchPayServices,
|
|
7
|
+
} from "./core.mjs";
|
|
8
|
+
|
|
9
|
+
const PLUGIN_ID = "pay-bridge";
|
|
10
|
+
|
|
11
|
+
function asContent(data) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: JSON.stringify(data, null, 2),
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolvePluginConfig(api) {
|
|
23
|
+
const globalConfig = api?.config ?? {};
|
|
24
|
+
const pluginEntry = globalConfig?.plugins?.entries?.[PLUGIN_ID];
|
|
25
|
+
return pluginEntry?.config ?? globalConfig?.config ?? {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function registerTool(api, definition) {
|
|
29
|
+
api.registerTool({
|
|
30
|
+
name: definition.name,
|
|
31
|
+
description: definition.description,
|
|
32
|
+
parameters: definition.parameters,
|
|
33
|
+
returns: {
|
|
34
|
+
type: "object",
|
|
35
|
+
additionalProperties: true,
|
|
36
|
+
},
|
|
37
|
+
async execute(_id, params = {}) {
|
|
38
|
+
const config = resolvePluginConfig(api);
|
|
39
|
+
let result;
|
|
40
|
+
if (definition.name === "pay_status") {
|
|
41
|
+
result = await getPayStatus(config, { cwd: process.cwd() });
|
|
42
|
+
} else if (definition.name === "pay_wallet_info") {
|
|
43
|
+
result = await getPayWalletInfo(config, { cwd: process.cwd() });
|
|
44
|
+
} else if (definition.name === "pay_search_services") {
|
|
45
|
+
result = await searchPayServices(config, params, { cwd: process.cwd() });
|
|
46
|
+
} else if (definition.name === "pay_get_service_endpoints") {
|
|
47
|
+
result = await getPayServiceEndpoints(config, params, { cwd: process.cwd() });
|
|
48
|
+
} else if (definition.name === "pay_api_request") {
|
|
49
|
+
result = await executePayApiRequest(config, params, { cwd: process.cwd() });
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`Unsupported pay-bridge tool: ${definition.name}`);
|
|
52
|
+
}
|
|
53
|
+
return asContent(result);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const toolDefinitions = [
|
|
59
|
+
{
|
|
60
|
+
name: "pay_status",
|
|
61
|
+
description:
|
|
62
|
+
"Check whether the local pay.sh CLI is installed and whether a pay wallet/account is configured. Use this before any paid API workflow.",
|
|
63
|
+
parameters: {
|
|
64
|
+
type: "object",
|
|
65
|
+
properties: {},
|
|
66
|
+
additionalProperties: false,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "pay_wallet_info",
|
|
71
|
+
description:
|
|
72
|
+
"Show pay wallet/account status. This wallet is separate from the AgentLayer execution wallet and is only for pay.sh API payments.",
|
|
73
|
+
parameters: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {},
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "pay_search_services",
|
|
81
|
+
description:
|
|
82
|
+
"Search the pay.sh skills catalog for paid APIs. Prefer this instead of guessing URLs manually.",
|
|
83
|
+
parameters: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
query: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Search text for service names, descriptions, or endpoint paths.",
|
|
89
|
+
},
|
|
90
|
+
category: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Optional category filter such as ai_ml, maps, data, compute, search, crypto_finance.",
|
|
93
|
+
},
|
|
94
|
+
account: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Optional pay account override.",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "pay_get_service_endpoints",
|
|
104
|
+
description:
|
|
105
|
+
"List the discoverable endpoints for a pay.sh service/resource pair and return their gateway URLs. Use the returned URL with pay_api_request.",
|
|
106
|
+
parameters: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
service_fqn: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "Fully qualified pay service name, for example solana-foundation/google/language.",
|
|
112
|
+
},
|
|
113
|
+
resource: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Resource name inside the service, for example entities or jobs.",
|
|
116
|
+
},
|
|
117
|
+
account: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "Optional pay account override.",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: ["service_fqn", "resource"],
|
|
123
|
+
additionalProperties: false,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "pay_api_request",
|
|
128
|
+
description:
|
|
129
|
+
"Call a paid API through the local pay.sh CLI using a URL returned by pay_get_service_endpoints. Requires explicit user_confirmed=true and keeps the pay wallet separate from AgentLayer execution wallets.",
|
|
130
|
+
optional: true,
|
|
131
|
+
parameters: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
service_fqn: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "Fully qualified pay service name used to validate the request URL.",
|
|
137
|
+
},
|
|
138
|
+
resource: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Resource name used to validate the request URL.",
|
|
141
|
+
},
|
|
142
|
+
url: {
|
|
143
|
+
type: "string",
|
|
144
|
+
description: "Exact HTTPS gateway URL returned by pay_get_service_endpoints.",
|
|
145
|
+
},
|
|
146
|
+
method: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "HTTP method such as GET or POST.",
|
|
149
|
+
},
|
|
150
|
+
headers: {
|
|
151
|
+
type: "object",
|
|
152
|
+
additionalProperties: { type: "string" },
|
|
153
|
+
description: "Optional HTTP headers.",
|
|
154
|
+
},
|
|
155
|
+
query: {
|
|
156
|
+
type: "object",
|
|
157
|
+
additionalProperties: true,
|
|
158
|
+
description: "Optional query parameters to append to the URL.",
|
|
159
|
+
},
|
|
160
|
+
json_body: {
|
|
161
|
+
description: "Optional JSON request body.",
|
|
162
|
+
},
|
|
163
|
+
text_body: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Optional raw text request body. Do not provide with json_body.",
|
|
166
|
+
},
|
|
167
|
+
account: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Optional pay account override.",
|
|
170
|
+
},
|
|
171
|
+
parse_json_response: {
|
|
172
|
+
type: "boolean",
|
|
173
|
+
description: "If true, attempt to parse the response body as JSON.",
|
|
174
|
+
},
|
|
175
|
+
purpose: {
|
|
176
|
+
type: "string",
|
|
177
|
+
description: "Short user-facing reason for this paid API call.",
|
|
178
|
+
},
|
|
179
|
+
user_confirmed: {
|
|
180
|
+
type: "boolean",
|
|
181
|
+
description: "Must be true for paid API requests.",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: ["service_fqn", "resource", "url", "method", "purpose", "user_confirmed"],
|
|
185
|
+
additionalProperties: false,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
export default function registerPayBridgePlugin(api) {
|
|
191
|
+
api?.logger?.info?.("[pay-bridge] registering pay.sh OpenClaw plugin");
|
|
192
|
+
for (const definition of toolDefinitions) {
|
|
193
|
+
registerTool(api, definition);
|
|
194
|
+
}
|
|
195
|
+
api?.logger?.info?.(`[pay-bridge] registered ${toolDefinitions.length} pay tools`);
|
|
196
|
+
}
|
|
@@ -1,11 +1,49 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "pay-bridge",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"
|
|
2
|
+
"name": "@agentlayertech/pay-bridge-plugin",
|
|
3
|
+
"version": "0.1.15",
|
|
4
|
+
"description": "OpenClaw plugin bridge for the local pay.sh CLI.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"license": "SEE LICENSE IN ../../../LICENSE",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/lopushok9/Agent-Layer.git",
|
|
10
|
+
"directory": ".openclaw/extensions/pay-bridge"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/lopushok9/Agent-Layer/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/lopushok9/Agent-Layer/tree/main/.openclaw/extensions/pay-bridge#readme",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"index.ts",
|
|
21
|
+
"core.mjs",
|
|
22
|
+
"dist/",
|
|
23
|
+
"openclaw.plugin.json",
|
|
24
|
+
"README.md",
|
|
25
|
+
"skills/"
|
|
26
|
+
],
|
|
6
27
|
"openclaw": {
|
|
7
28
|
"extensions": [
|
|
8
29
|
"./index.ts"
|
|
9
|
-
]
|
|
10
|
-
|
|
30
|
+
],
|
|
31
|
+
"runtimeExtensions": [
|
|
32
|
+
"./dist/index.js"
|
|
33
|
+
],
|
|
34
|
+
"compat": {
|
|
35
|
+
"pluginApi": ">=2026.3.24-beta.2",
|
|
36
|
+
"minGatewayVersion": "2026.3.24-beta.2"
|
|
37
|
+
},
|
|
38
|
+
"build": {
|
|
39
|
+
"openclawVersion": "2026.3.24-beta.2",
|
|
40
|
+
"pluginSdkVersion": "2026.3.24-beta.2"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"openclaw",
|
|
45
|
+
"plugin",
|
|
46
|
+
"pay",
|
|
47
|
+
"payments"
|
|
48
|
+
]
|
|
11
49
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Added ClawHub-publishable OpenClaw plugin package metadata for
|
|
6
|
+
`.openclaw/extensions/agent-wallet` and `.openclaw/extensions/pay-bridge`,
|
|
7
|
+
including required `openclaw.compat`, `openclaw.build`, and
|
|
8
|
+
`runtimeExtensions` fields for native `openclaw plugins install clawhub:...`
|
|
9
|
+
installs.
|
|
10
|
+
- Added a reproducible OpenClaw plugin package build/check script at
|
|
11
|
+
`scripts/manage_openclaw_plugin_packages.mjs` to generate published runtime
|
|
12
|
+
JS artifacts under `.openclaw/extensions/*/dist/`.
|
|
13
|
+
- Updated the `agent-wallet` OpenClaw bridge to auto-check the packaged runtime
|
|
14
|
+
path at `~/.openclaw/agent-wallet-runtime/current/agent-wallet` before local
|
|
15
|
+
workspace fallbacks, so a ClawHub-installed plugin can ride on top of the
|
|
16
|
+
existing npm runtime installer.
|
|
17
|
+
- Documented the dual-install model: keep `npx @agentlayer.tech/wallet install`
|
|
18
|
+
for runtime/bootstrap, and use `openclaw plugins install clawhub:...` for the
|
|
19
|
+
native OpenClaw plugin packages.
|
|
20
|
+
- Added a GitHub Actions workflow at `.github/workflows/clawhub-plugins.yml`
|
|
21
|
+
that can dry-run ClawHub publishes on pull requests and publish both OpenClaw
|
|
22
|
+
plugin packages on `v*` tags or manual dispatch.
|
|
23
|
+
|
|
5
24
|
## v0.1.14 - 2026-05-13
|
|
6
25
|
|
|
7
26
|
- Added a separate `.openclaw/extensions/pay-bridge/` plugin that keeps
|