@arcdot/agent 0.1.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/README.md +67 -0
- package/bin/arcdot.mjs +2 -0
- package/dist/cli.js +685 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +678 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.js +633 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
ARC_CHAIN_ID: () => ARC_CHAIN_ID,
|
|
24
|
+
ARC_RPC_URL_DEFAULT: () => ARC_RPC_URL_DEFAULT,
|
|
25
|
+
GATEWAY_FEE_WEI_DEFAULT: () => GATEWAY_FEE_WEI_DEFAULT,
|
|
26
|
+
callMcpToolWithAutoSettle: () => callMcpToolWithAutoSettle,
|
|
27
|
+
createArcdotAgent: () => createArcdotAgent,
|
|
28
|
+
createWallet: () => createWallet,
|
|
29
|
+
fetchService: () => fetchService,
|
|
30
|
+
getNativeBalance: () => getNativeBalance,
|
|
31
|
+
isGateway402Body: () => isGateway402Body,
|
|
32
|
+
loadWallet: () => loadWallet,
|
|
33
|
+
makePaymentId: () => makePaymentId,
|
|
34
|
+
mcpRpc: () => mcpRpc,
|
|
35
|
+
parseMcpPaymentNeeded: () => parseMcpPaymentNeeded,
|
|
36
|
+
parsePaymentRequired: () => parsePaymentRequired,
|
|
37
|
+
paymentDepositArgs: () => paymentDepositArgs,
|
|
38
|
+
resolveGateway: () => resolveGateway,
|
|
39
|
+
resolvePrivateKey: () => resolvePrivateKey,
|
|
40
|
+
runMcpProxy: () => runMcpProxy,
|
|
41
|
+
settlePayment: () => settlePayment,
|
|
42
|
+
slugFromToolName: () => slugFromToolName,
|
|
43
|
+
toolNameForSlug: () => toolNameForSlug,
|
|
44
|
+
unlockWithAutoSettle: () => unlockWithAutoSettle,
|
|
45
|
+
walletAccountFromKey: () => walletAccountFromKey,
|
|
46
|
+
walletPath: () => walletPath
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(src_exports);
|
|
49
|
+
|
|
50
|
+
// src/constants.ts
|
|
51
|
+
var ARC_CHAIN_ID = 5042;
|
|
52
|
+
var ARC_RPC_URL_DEFAULT = "https://rpc.mainnet.arc.io";
|
|
53
|
+
var ARC_EXPLORER = "https://explorer.arc.io";
|
|
54
|
+
var GATEWAY_FEE_WEI_DEFAULT = 10000000000000000n;
|
|
55
|
+
|
|
56
|
+
// src/wallet/store.ts
|
|
57
|
+
var import_node_fs = require("fs");
|
|
58
|
+
var import_node_os = require("os");
|
|
59
|
+
var import_node_path = require("path");
|
|
60
|
+
var import_accounts = require("viem/accounts");
|
|
61
|
+
function walletDir() {
|
|
62
|
+
return process.env.ARCDOT_HOME?.trim() || (0, import_node_path.join)((0, import_node_os.homedir)(), ".arcdot");
|
|
63
|
+
}
|
|
64
|
+
function walletPath() {
|
|
65
|
+
return (0, import_node_path.join)(walletDir(), "wallet.json");
|
|
66
|
+
}
|
|
67
|
+
function isHexKey(key) {
|
|
68
|
+
return /^0x[a-fA-F0-9]{64}$/.test(key);
|
|
69
|
+
}
|
|
70
|
+
function resolvePrivateKey() {
|
|
71
|
+
const fromEnv = process.env.ARCDOT_PRIVATE_KEY?.trim() || process.env.AGENT_PRIVATE_KEY?.trim();
|
|
72
|
+
if (fromEnv) {
|
|
73
|
+
if (!isHexKey(fromEnv)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"ARCDOT_PRIVATE_KEY / AGENT_PRIVATE_KEY must be a 0x-prefixed 32-byte hex key."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return fromEnv;
|
|
79
|
+
}
|
|
80
|
+
const stored = loadWallet();
|
|
81
|
+
if (!stored) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"No agent wallet found. Run: npx @arcdot/agent wallet create"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return stored.privateKey;
|
|
87
|
+
}
|
|
88
|
+
function loadWallet() {
|
|
89
|
+
const path = walletPath();
|
|
90
|
+
if (!(0, import_node_fs.existsSync)(path)) return null;
|
|
91
|
+
const raw = JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
92
|
+
if (!raw.address || !isHexKey(raw.privateKey)) {
|
|
93
|
+
throw new Error(`Corrupt wallet file at ${path}`);
|
|
94
|
+
}
|
|
95
|
+
return raw;
|
|
96
|
+
}
|
|
97
|
+
function createWallet(opts) {
|
|
98
|
+
const path = walletPath();
|
|
99
|
+
if ((0, import_node_fs.existsSync)(path) && !opts?.force) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Wallet already exists at ${path}. Pass --force to overwrite, or use wallet address.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
(0, import_node_fs.mkdirSync)(walletDir(), { recursive: true });
|
|
105
|
+
const privateKey = (0, import_accounts.generatePrivateKey)();
|
|
106
|
+
const account = (0, import_accounts.privateKeyToAccount)(privateKey);
|
|
107
|
+
const wallet = {
|
|
108
|
+
address: account.address,
|
|
109
|
+
privateKey,
|
|
110
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
111
|
+
};
|
|
112
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(wallet, null, 2) + "\n", {
|
|
113
|
+
encoding: "utf8",
|
|
114
|
+
mode: 384
|
|
115
|
+
});
|
|
116
|
+
try {
|
|
117
|
+
(0, import_node_fs.chmodSync)(path, 384);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
return wallet;
|
|
121
|
+
}
|
|
122
|
+
function walletAccountFromKey(privateKey) {
|
|
123
|
+
return (0, import_accounts.privateKeyToAccount)(privateKey);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/settle/pay.ts
|
|
127
|
+
var import_viem4 = require("viem");
|
|
128
|
+
var import_accounts2 = require("viem/accounts");
|
|
129
|
+
|
|
130
|
+
// src/arc/chain.ts
|
|
131
|
+
var import_viem = require("viem");
|
|
132
|
+
function resolveRpcUrl(override) {
|
|
133
|
+
return override || process.env.ARC_RPC_URL || process.env.NEXT_PUBLIC_ARC_RPC_URL || ARC_RPC_URL_DEFAULT;
|
|
134
|
+
}
|
|
135
|
+
function arcMainnet(rpcUrl) {
|
|
136
|
+
return (0, import_viem.defineChain)({
|
|
137
|
+
id: ARC_CHAIN_ID,
|
|
138
|
+
name: "Arc",
|
|
139
|
+
nativeCurrency: { name: "USDC", symbol: "USDC", decimals: 18 },
|
|
140
|
+
rpcUrls: {
|
|
141
|
+
default: { http: [resolveRpcUrl(rpcUrl)] }
|
|
142
|
+
},
|
|
143
|
+
blockExplorers: {
|
|
144
|
+
default: { name: "Arc Explorer", url: ARC_EXPLORER }
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
var promptGatewayAbi = [
|
|
149
|
+
{
|
|
150
|
+
type: "function",
|
|
151
|
+
name: "depositPayment",
|
|
152
|
+
stateMutability: "payable",
|
|
153
|
+
inputs: [
|
|
154
|
+
{ name: "paymentId", type: "bytes32" },
|
|
155
|
+
{ name: "seller", type: "address" }
|
|
156
|
+
],
|
|
157
|
+
outputs: []
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
type: "function",
|
|
161
|
+
name: "minFee",
|
|
162
|
+
stateMutability: "view",
|
|
163
|
+
inputs: [],
|
|
164
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
// src/auth/challenge.ts
|
|
169
|
+
var import_viem2 = require("viem");
|
|
170
|
+
function hashGatewayInput(input) {
|
|
171
|
+
return (0, import_viem2.keccak256)((0, import_viem2.stringToBytes)(JSON.stringify(input ?? null)));
|
|
172
|
+
}
|
|
173
|
+
function buildAuthChallenge(msg) {
|
|
174
|
+
return [
|
|
175
|
+
"arcdot.gateway",
|
|
176
|
+
`chainId:${msg.chainId}`,
|
|
177
|
+
`gateway:${msg.gateway.toLowerCase()}`,
|
|
178
|
+
`txHash:${msg.txHash.toLowerCase()}`,
|
|
179
|
+
`feeWei:${msg.feeWei}`,
|
|
180
|
+
`service:${msg.service}`,
|
|
181
|
+
`inputHash:${msg.inputHash.toLowerCase()}`,
|
|
182
|
+
`issuedAt:${msg.issuedAt}`,
|
|
183
|
+
`expiresAt:${msg.expiresAt}`
|
|
184
|
+
].join("\n");
|
|
185
|
+
}
|
|
186
|
+
function buildGatewayAuthMessage(params) {
|
|
187
|
+
return {
|
|
188
|
+
domain: "arcdot.gateway",
|
|
189
|
+
chainId: ARC_CHAIN_ID,
|
|
190
|
+
gateway: params.gateway,
|
|
191
|
+
txHash: params.txHash,
|
|
192
|
+
feeWei: params.feeWei.toString(),
|
|
193
|
+
service: params.service,
|
|
194
|
+
inputHash: hashGatewayInput(params.input),
|
|
195
|
+
issuedAt: params.issuedAt,
|
|
196
|
+
expiresAt: params.expiresAt
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/settle/paymentId.ts
|
|
201
|
+
var import_viem3 = require("viem");
|
|
202
|
+
function makePaymentId(params) {
|
|
203
|
+
return (0, import_viem3.keccak256)(
|
|
204
|
+
(0, import_viem3.encodeAbiParameters)(
|
|
205
|
+
[
|
|
206
|
+
{ type: "address" },
|
|
207
|
+
{ type: "string" },
|
|
208
|
+
{ type: "uint256" }
|
|
209
|
+
],
|
|
210
|
+
[params.payer, params.service, BigInt(params.nonce)]
|
|
211
|
+
)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/settle/pay.ts
|
|
216
|
+
async function settlePayment(params) {
|
|
217
|
+
const account = (0, import_accounts2.privateKeyToAccount)(params.privateKey);
|
|
218
|
+
const chain = arcMainnet(params.rpcUrl);
|
|
219
|
+
const rpcUrl = resolveRpcUrl(params.rpcUrl);
|
|
220
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
221
|
+
chain,
|
|
222
|
+
transport: (0, import_viem4.http)(rpcUrl)
|
|
223
|
+
});
|
|
224
|
+
const walletClient = (0, import_viem4.createWalletClient)({
|
|
225
|
+
account,
|
|
226
|
+
chain,
|
|
227
|
+
transport: (0, import_viem4.http)(rpcUrl)
|
|
228
|
+
});
|
|
229
|
+
const paymentId = makePaymentId({
|
|
230
|
+
payer: account.address,
|
|
231
|
+
service: params.service,
|
|
232
|
+
nonce: Date.now()
|
|
233
|
+
});
|
|
234
|
+
const txHash = await walletClient.writeContract({
|
|
235
|
+
address: params.gateway,
|
|
236
|
+
abi: promptGatewayAbi,
|
|
237
|
+
functionName: "depositPayment",
|
|
238
|
+
args: [paymentId, params.seller],
|
|
239
|
+
value: params.feeWei,
|
|
240
|
+
chain,
|
|
241
|
+
account
|
|
242
|
+
});
|
|
243
|
+
await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
244
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
245
|
+
const authMessage = buildGatewayAuthMessage({
|
|
246
|
+
gateway: params.gateway,
|
|
247
|
+
txHash,
|
|
248
|
+
feeWei: params.feeWei,
|
|
249
|
+
service: params.service,
|
|
250
|
+
input: params.input,
|
|
251
|
+
issuedAt: now,
|
|
252
|
+
expiresAt: now + 120
|
|
253
|
+
});
|
|
254
|
+
const signature = await walletClient.signMessage({
|
|
255
|
+
account,
|
|
256
|
+
message: buildAuthChallenge(authMessage)
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
paymentId,
|
|
260
|
+
feeWei: params.feeWei,
|
|
261
|
+
proof: {
|
|
262
|
+
txHash,
|
|
263
|
+
address: account.address,
|
|
264
|
+
signature,
|
|
265
|
+
issuedAt: now,
|
|
266
|
+
expiresAt: now + 120
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async function getNativeBalance(address, rpcUrl) {
|
|
271
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
272
|
+
chain: arcMainnet(rpcUrl),
|
|
273
|
+
transport: (0, import_viem4.http)(resolveRpcUrl(rpcUrl))
|
|
274
|
+
});
|
|
275
|
+
const wei = await publicClient.getBalance({ address });
|
|
276
|
+
return { wei, formatted: (0, import_viem4.formatEther)(wei) };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/client/paymentParse.ts
|
|
280
|
+
function isGateway402Body(value) {
|
|
281
|
+
if (!value || typeof value !== "object") return false;
|
|
282
|
+
const v = value;
|
|
283
|
+
if (v.ok !== false || v.status !== 402) return false;
|
|
284
|
+
const err = v.error;
|
|
285
|
+
if (!err || typeof err !== "object") return false;
|
|
286
|
+
return Boolean(err.payment);
|
|
287
|
+
}
|
|
288
|
+
function parsePaymentRequired(body) {
|
|
289
|
+
if (!isGateway402Body(body)) return null;
|
|
290
|
+
const payment = body.error.payment;
|
|
291
|
+
if (!payment?.gateway || !payment.feeWei) return null;
|
|
292
|
+
return payment;
|
|
293
|
+
}
|
|
294
|
+
function parseMcpPaymentNeeded(toolResult) {
|
|
295
|
+
const text = extractMcpText(toolResult);
|
|
296
|
+
if (!text) return null;
|
|
297
|
+
let parsed;
|
|
298
|
+
try {
|
|
299
|
+
parsed = JSON.parse(text);
|
|
300
|
+
} catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
304
|
+
const obj = parsed;
|
|
305
|
+
const err = obj.error;
|
|
306
|
+
if (obj.status === 402 || err?.code === "PAYMENT_REQUIRED") {
|
|
307
|
+
const payment = err?.payment;
|
|
308
|
+
if (!payment?.gateway || !payment.feeWei) return null;
|
|
309
|
+
const slug = err?.service?.slug || (typeof obj.service === "string" ? obj.service : "") || "";
|
|
310
|
+
return { payment, serviceSlug: slug, raw: parsed };
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
function extractMcpText(toolResult) {
|
|
315
|
+
if (!toolResult || typeof toolResult !== "object") return null;
|
|
316
|
+
const r = toolResult;
|
|
317
|
+
if (Array.isArray(r.content)) {
|
|
318
|
+
const texts = r.content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text);
|
|
319
|
+
if (texts.length) return texts.join("\n");
|
|
320
|
+
}
|
|
321
|
+
if (typeof toolResult.text === "string") {
|
|
322
|
+
return toolResult.text;
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
function paymentDepositArgs(payment) {
|
|
327
|
+
if (!payment.seller) {
|
|
328
|
+
throw new Error("Payment instructions missing seller address");
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
feeWei: BigInt(payment.feeWei),
|
|
332
|
+
seller: payment.seller,
|
|
333
|
+
gateway: payment.gateway
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/client/gateway.ts
|
|
338
|
+
async function fetchService(origin, slug) {
|
|
339
|
+
const res = await fetch(new URL(`/api/services/${slug}`, origin));
|
|
340
|
+
const json = await res.json();
|
|
341
|
+
if (!res.ok || !json.service) {
|
|
342
|
+
throw new Error(json.error || `Service not found: ${slug}`);
|
|
343
|
+
}
|
|
344
|
+
const sellerRaw = json.service.seller || json.service.owner_address;
|
|
345
|
+
if (!sellerRaw || !/^0x[a-fA-F0-9]{40}$/.test(sellerRaw)) {
|
|
346
|
+
throw new Error("Service is missing a valid seller address");
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
slug: json.service.slug,
|
|
350
|
+
price_wei: json.service.price_wei,
|
|
351
|
+
seller: sellerRaw,
|
|
352
|
+
title: json.service.title
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async function resolveGateway(origin, override) {
|
|
356
|
+
if (override && override.length === 42) return override;
|
|
357
|
+
if (process.env.ARCDOT_GATEWAY?.startsWith("0x")) {
|
|
358
|
+
return process.env.ARCDOT_GATEWAY;
|
|
359
|
+
}
|
|
360
|
+
const res = await fetch(new URL("/api/services", origin));
|
|
361
|
+
const json = await res.json();
|
|
362
|
+
if (json.gateway && json.gateway.length === 42) {
|
|
363
|
+
return json.gateway;
|
|
364
|
+
}
|
|
365
|
+
throw new Error(
|
|
366
|
+
"Gateway address not configured on host. Set ARCDOT_GATEWAY or deploy PromptGateway."
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
async function postGatewayPaid(params) {
|
|
370
|
+
const body = {
|
|
371
|
+
service: params.service,
|
|
372
|
+
input: params.input,
|
|
373
|
+
clientRequestId: params.clientRequestId,
|
|
374
|
+
auth: {
|
|
375
|
+
issuedAt: params.proof.issuedAt,
|
|
376
|
+
expiresAt: params.proof.expiresAt
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const res = await fetch(new URL("/api/gateway", params.origin), {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: {
|
|
382
|
+
"Content-Type": "application/json",
|
|
383
|
+
"X-Arc-Tx-Hash": params.proof.txHash,
|
|
384
|
+
"X-Arc-Address": params.proof.address,
|
|
385
|
+
"X-Arc-Signature": params.proof.signature
|
|
386
|
+
},
|
|
387
|
+
body: JSON.stringify(body)
|
|
388
|
+
});
|
|
389
|
+
const json = await res.json();
|
|
390
|
+
if (res.status === 200 && json?.ok === true) {
|
|
391
|
+
return { ok: true, status: 200, body: json };
|
|
392
|
+
}
|
|
393
|
+
return { ok: false, status: res.status, body: json };
|
|
394
|
+
}
|
|
395
|
+
async function unlockWithAutoSettle(params) {
|
|
396
|
+
const privateKey = params.privateKey ?? resolvePrivateKey();
|
|
397
|
+
const origin = params.origin.replace(/\/$/, "");
|
|
398
|
+
const probe = await fetch(new URL("/api/gateway", origin), {
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: { "Content-Type": "application/json" },
|
|
401
|
+
body: JSON.stringify({
|
|
402
|
+
service: params.service,
|
|
403
|
+
input: params.input,
|
|
404
|
+
clientRequestId: params.clientRequestId
|
|
405
|
+
})
|
|
406
|
+
});
|
|
407
|
+
const probeJson = await probe.json();
|
|
408
|
+
if (probe.status === 200 && probeJson?.ok === true) {
|
|
409
|
+
return {
|
|
410
|
+
gateway: { ok: true, status: 200, body: probeJson },
|
|
411
|
+
settled: false
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const payment = parsePaymentRequired(probeJson);
|
|
415
|
+
const service = await fetchService(origin, params.service);
|
|
416
|
+
const gatewayAddr = params.gateway || (payment ? payment.gateway : await resolveGateway(origin));
|
|
417
|
+
let feeWei = BigInt(service.price_wei);
|
|
418
|
+
let seller = service.seller;
|
|
419
|
+
if (payment) {
|
|
420
|
+
const args = paymentDepositArgs(payment);
|
|
421
|
+
feeWei = args.feeWei;
|
|
422
|
+
seller = args.seller;
|
|
423
|
+
}
|
|
424
|
+
if (feeWei <= 0n) feeWei = GATEWAY_FEE_WEI_DEFAULT;
|
|
425
|
+
const settled = await settlePayment({
|
|
426
|
+
privateKey,
|
|
427
|
+
gateway: gatewayAddr,
|
|
428
|
+
seller,
|
|
429
|
+
service: params.service,
|
|
430
|
+
feeWei,
|
|
431
|
+
input: params.input,
|
|
432
|
+
rpcUrl: params.rpcUrl
|
|
433
|
+
});
|
|
434
|
+
const gateway = await postGatewayPaid({
|
|
435
|
+
origin,
|
|
436
|
+
service: params.service,
|
|
437
|
+
input: params.input,
|
|
438
|
+
proof: settled.proof,
|
|
439
|
+
feeWei: settled.feeWei,
|
|
440
|
+
clientRequestId: params.clientRequestId
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
gateway,
|
|
444
|
+
txHash: settled.proof.txHash,
|
|
445
|
+
paymentId: settled.paymentId,
|
|
446
|
+
settled: true
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/mcp/toolNames.ts
|
|
451
|
+
function toolNameForSlug(slug) {
|
|
452
|
+
return `arcdot_${slug.replace(/-/g, "_")}`;
|
|
453
|
+
}
|
|
454
|
+
function slugFromToolName(name) {
|
|
455
|
+
if (!name.startsWith("arcdot_")) return null;
|
|
456
|
+
if (name === "arcdot_catalog" || name === "arcdot_health") return null;
|
|
457
|
+
return name.slice("arcdot_".length).replace(/_/g, "-");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/client/mcp.ts
|
|
461
|
+
async function mcpRpc(origin, method, params, id = 1) {
|
|
462
|
+
const endpoint = new URL("/api/mcp", origin.replace(/\/$/, ""));
|
|
463
|
+
const res = await fetch(endpoint, {
|
|
464
|
+
method: "POST",
|
|
465
|
+
headers: { "Content-Type": "application/json" },
|
|
466
|
+
body: JSON.stringify({
|
|
467
|
+
jsonrpc: "2.0",
|
|
468
|
+
id,
|
|
469
|
+
method,
|
|
470
|
+
params: params ?? {}
|
|
471
|
+
})
|
|
472
|
+
});
|
|
473
|
+
return await res.json();
|
|
474
|
+
}
|
|
475
|
+
async function callMcpToolWithAutoSettle(params) {
|
|
476
|
+
const origin = params.origin.replace(/\/$/, "");
|
|
477
|
+
const args = { ...params.arguments ?? {} };
|
|
478
|
+
const first = await mcpRpc(origin, "tools/call", {
|
|
479
|
+
name: params.name,
|
|
480
|
+
arguments: args
|
|
481
|
+
});
|
|
482
|
+
if (first.error) return first;
|
|
483
|
+
const needed = parseMcpPaymentNeeded(first.result);
|
|
484
|
+
if (!needed) return first;
|
|
485
|
+
const privateKey = params.privateKey ?? resolvePrivateKey();
|
|
486
|
+
const deposit = paymentDepositArgs(needed.payment);
|
|
487
|
+
const slug = needed.serviceSlug || slugFromToolName(params.name) || String(args.service ?? "");
|
|
488
|
+
if (!slug) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`Cannot settle: missing service slug for tool ${params.name}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const input = {
|
|
494
|
+
prompt: typeof args.prompt === "string" ? args.prompt : JSON.stringify(args)
|
|
495
|
+
};
|
|
496
|
+
const gateway = params.gateway || deposit.gateway || await resolveGateway(origin);
|
|
497
|
+
const settled = await settlePayment({
|
|
498
|
+
privateKey,
|
|
499
|
+
gateway,
|
|
500
|
+
seller: deposit.seller,
|
|
501
|
+
service: slug,
|
|
502
|
+
feeWei: deposit.feeWei,
|
|
503
|
+
input,
|
|
504
|
+
rpcUrl: params.rpcUrl
|
|
505
|
+
});
|
|
506
|
+
const retryArgs = {
|
|
507
|
+
...args,
|
|
508
|
+
payment: {
|
|
509
|
+
txHash: settled.proof.txHash,
|
|
510
|
+
address: settled.proof.address,
|
|
511
|
+
signature: settled.proof.signature,
|
|
512
|
+
issuedAt: settled.proof.issuedAt,
|
|
513
|
+
expiresAt: settled.proof.expiresAt
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
return mcpRpc(origin, "tools/call", {
|
|
517
|
+
name: params.name,
|
|
518
|
+
arguments: retryArgs
|
|
519
|
+
}, 2);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/createAgent.ts
|
|
523
|
+
async function createArcdotAgent(options) {
|
|
524
|
+
const origin = options.origin.replace(/\/$/, "");
|
|
525
|
+
const privateKey = options.privateKey ?? resolvePrivateKey();
|
|
526
|
+
return {
|
|
527
|
+
origin,
|
|
528
|
+
async unlock(params) {
|
|
529
|
+
return unlockWithAutoSettle({
|
|
530
|
+
origin,
|
|
531
|
+
service: params.service,
|
|
532
|
+
input: params.input,
|
|
533
|
+
privateKey,
|
|
534
|
+
rpcUrl: options.rpcUrl,
|
|
535
|
+
gateway: options.gateway,
|
|
536
|
+
clientRequestId: params.clientRequestId
|
|
537
|
+
});
|
|
538
|
+
},
|
|
539
|
+
async callMcpTool(name, args) {
|
|
540
|
+
const res = await callMcpToolWithAutoSettle({
|
|
541
|
+
origin,
|
|
542
|
+
name,
|
|
543
|
+
arguments: args,
|
|
544
|
+
privateKey,
|
|
545
|
+
rpcUrl: options.rpcUrl,
|
|
546
|
+
gateway: options.gateway
|
|
547
|
+
});
|
|
548
|
+
if (res.error) {
|
|
549
|
+
throw new Error(res.error.message);
|
|
550
|
+
}
|
|
551
|
+
return res.result;
|
|
552
|
+
},
|
|
553
|
+
async listMcpTools() {
|
|
554
|
+
const res = await mcpRpc(origin, "tools/list");
|
|
555
|
+
if (res.error) throw new Error(res.error.message);
|
|
556
|
+
return res.result;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/mcp/proxy.ts
|
|
562
|
+
var import_node_readline = require("readline");
|
|
563
|
+
function write(msg) {
|
|
564
|
+
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
565
|
+
}
|
|
566
|
+
function ok(id, result) {
|
|
567
|
+
write({ jsonrpc: "2.0", id: id ?? null, result });
|
|
568
|
+
}
|
|
569
|
+
function fail(id, code, message) {
|
|
570
|
+
write({
|
|
571
|
+
jsonrpc: "2.0",
|
|
572
|
+
id: id ?? null,
|
|
573
|
+
error: { code, message }
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
async function runMcpProxy(origin) {
|
|
577
|
+
const base = origin.replace(/\/$/, "");
|
|
578
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, crlfDelay: Infinity });
|
|
579
|
+
console.error(`[arcdot] MCP proxy \u2192 ${base}/api/mcp`);
|
|
580
|
+
for await (const line of rl) {
|
|
581
|
+
const trimmed = line.trim();
|
|
582
|
+
if (!trimmed) continue;
|
|
583
|
+
let msg;
|
|
584
|
+
try {
|
|
585
|
+
msg = JSON.parse(trimmed);
|
|
586
|
+
} catch {
|
|
587
|
+
fail(null, -32700, "Parse error");
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const id = msg.id;
|
|
591
|
+
const method = msg.method;
|
|
592
|
+
if (method === "notifications/initialized" || id === void 0) {
|
|
593
|
+
if (method && method.startsWith("notifications/")) continue;
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
if (method === "initialize") {
|
|
597
|
+
const remote = await mcpRpc(base, "initialize", msg.params);
|
|
598
|
+
if (remote.error) {
|
|
599
|
+
fail(id, remote.error.code, remote.error.message);
|
|
600
|
+
} else {
|
|
601
|
+
const result = remote.result;
|
|
602
|
+
ok(id, result ?? {
|
|
603
|
+
protocolVersion: "2024-11-05",
|
|
604
|
+
capabilities: { tools: {} },
|
|
605
|
+
serverInfo: { name: "arcdot-proxy", version: "0.1.0" }
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (method === "ping") {
|
|
611
|
+
ok(id, {});
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (method === "tools/list") {
|
|
615
|
+
const remote = await mcpRpc(base, "tools/list", msg.params);
|
|
616
|
+
if (remote.error) fail(id, remote.error.code, remote.error.message);
|
|
617
|
+
else ok(id, remote.result);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
if (method === "tools/call") {
|
|
621
|
+
const p = msg.params;
|
|
622
|
+
if (!p?.name) {
|
|
623
|
+
fail(id, -32602, "tools/call requires params.name");
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const remote = await callMcpToolWithAutoSettle({
|
|
627
|
+
origin: base,
|
|
628
|
+
name: p.name,
|
|
629
|
+
arguments: p.arguments
|
|
630
|
+
});
|
|
631
|
+
if (remote.error) fail(id, remote.error.code, remote.error.message);
|
|
632
|
+
else ok(id, remote.result);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
if (method) {
|
|
636
|
+
const remote = await mcpRpc(base, method, msg.params);
|
|
637
|
+
if (remote.error) fail(id, remote.error.code, remote.error.message);
|
|
638
|
+
else ok(id, remote.result);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
fail(id, -32600, "Invalid Request");
|
|
642
|
+
} catch (err) {
|
|
643
|
+
fail(
|
|
644
|
+
id,
|
|
645
|
+
-32e3,
|
|
646
|
+
err instanceof Error ? err.message : "Proxy error"
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
652
|
+
0 && (module.exports = {
|
|
653
|
+
ARC_CHAIN_ID,
|
|
654
|
+
ARC_RPC_URL_DEFAULT,
|
|
655
|
+
GATEWAY_FEE_WEI_DEFAULT,
|
|
656
|
+
callMcpToolWithAutoSettle,
|
|
657
|
+
createArcdotAgent,
|
|
658
|
+
createWallet,
|
|
659
|
+
fetchService,
|
|
660
|
+
getNativeBalance,
|
|
661
|
+
isGateway402Body,
|
|
662
|
+
loadWallet,
|
|
663
|
+
makePaymentId,
|
|
664
|
+
mcpRpc,
|
|
665
|
+
parseMcpPaymentNeeded,
|
|
666
|
+
parsePaymentRequired,
|
|
667
|
+
paymentDepositArgs,
|
|
668
|
+
resolveGateway,
|
|
669
|
+
resolvePrivateKey,
|
|
670
|
+
runMcpProxy,
|
|
671
|
+
settlePayment,
|
|
672
|
+
slugFromToolName,
|
|
673
|
+
toolNameForSlug,
|
|
674
|
+
unlockWithAutoSettle,
|
|
675
|
+
walletAccountFromKey,
|
|
676
|
+
walletPath
|
|
677
|
+
});
|
|
678
|
+
//# sourceMappingURL=index.cjs.map
|