arispay 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.
@@ -0,0 +1,953 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../shared/dist/constants.js
4
+ var CRYPTO_CHAINS = {
5
+ ETHEREUM: "ethereum",
6
+ BASE: "base",
7
+ POLYGON: "polygon",
8
+ SOLANA: "solana"
9
+ };
10
+ var SUPPORTED_CHAINS = Object.values(CRYPTO_CHAINS);
11
+ var ERROR_CODES = {
12
+ INSUFFICIENT_FUNDS: "INSUFFICIENT_FUNDS",
13
+ SPEND_LIMIT_EXCEEDED: "SPEND_LIMIT_EXCEEDED",
14
+ MERCHANT_REJECTED: "MERCHANT_REJECTED",
15
+ TAP_VERIFICATION_FAILED: "TAP_VERIFICATION_FAILED",
16
+ CARD_DECLINED: "CARD_DECLINED",
17
+ INVALID_API_KEY: "INVALID_API_KEY",
18
+ RATE_LIMITED: "RATE_LIMITED",
19
+ IDEMPOTENCY_CONFLICT: "IDEMPOTENCY_CONFLICT",
20
+ AGENT_NOT_FOUND: "AGENT_NOT_FOUND",
21
+ USER_NOT_FOUND: "USER_NOT_FOUND",
22
+ PAYMENT_NOT_FOUND: "PAYMENT_NOT_FOUND",
23
+ INVALID_REQUEST: "INVALID_REQUEST",
24
+ INTERNAL_ERROR: "INTERNAL_ERROR",
25
+ CIRCUIT_BREAKER_TRIPPED: "CIRCUIT_BREAKER_TRIPPED",
26
+ AGENT_SUSPENDED: "AGENT_SUSPENDED",
27
+ WALLET_NOT_FOUND: "WALLET_NOT_FOUND",
28
+ INSUFFICIENT_ALLOWANCE: "INSUFFICIENT_ALLOWANCE",
29
+ INSUFFICIENT_TOKEN_BALANCE: "INSUFFICIENT_TOKEN_BALANCE",
30
+ CRYPTO_TRANSFER_FAILED: "CRYPTO_TRANSFER_FAILED",
31
+ INVALID_WALLET_ADDRESS: "INVALID_WALLET_ADDRESS",
32
+ UNSUPPORTED_CHAIN: "UNSUPPORTED_CHAIN",
33
+ UNSUPPORTED_TOKEN: "UNSUPPORTED_TOKEN",
34
+ UNSUPPORTED_CARD_NETWORK: "UNSUPPORTED_CARD_NETWORK",
35
+ NETWORK_REGISTRY_ERROR: "NETWORK_REGISTRY_ERROR",
36
+ INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
37
+ AGENT_MODE_MISMATCH: "AGENT_MODE_MISMATCH",
38
+ THREE_DS_FAILED: "THREE_DS_FAILED",
39
+ MERCHANT_NOT_FOUND: "MERCHANT_NOT_FOUND",
40
+ INVALID_MERCHANT_API_KEY: "INVALID_MERCHANT_API_KEY"
41
+ };
42
+
43
+ // ../shared/dist/errors.js
44
+ var ArisPayError = class extends Error {
45
+ code;
46
+ statusCode;
47
+ constructor(code, message, statusCode = 400) {
48
+ super(message);
49
+ this.code = code;
50
+ this.statusCode = statusCode;
51
+ this.name = "ArisPayError";
52
+ }
53
+ toJSON() {
54
+ return {
55
+ error: {
56
+ code: this.code,
57
+ message: this.message
58
+ }
59
+ };
60
+ }
61
+ };
62
+
63
+ // src/client.ts
64
+ var SDK_REQUEST_TIMEOUT_MS = 3e4;
65
+ var VALID_ERROR_CODES = new Set(Object.values(ERROR_CODES));
66
+ function toErrorCode(code) {
67
+ if (typeof code === "string" && VALID_ERROR_CODES.has(code)) {
68
+ return code;
69
+ }
70
+ return "INTERNAL_ERROR";
71
+ }
72
+ var HttpClient = class {
73
+ constructor(baseUrl, apiKey) {
74
+ this.baseUrl = baseUrl;
75
+ this.apiKey = apiKey;
76
+ }
77
+ async request(method, path, body) {
78
+ const url = `${this.baseUrl}${path}`;
79
+ const headers = {
80
+ Authorization: `Bearer ${this.apiKey}`,
81
+ "Content-Type": "application/json"
82
+ };
83
+ const res = await fetch(url, {
84
+ method,
85
+ headers,
86
+ body: body ? JSON.stringify(body) : void 0,
87
+ signal: AbortSignal.timeout(SDK_REQUEST_TIMEOUT_MS)
88
+ });
89
+ const data = await res.json();
90
+ if (!res.ok) {
91
+ const err = data;
92
+ throw new ArisPayError(
93
+ toErrorCode(err.error?.code),
94
+ err.error?.message ?? "Request failed",
95
+ res.status
96
+ );
97
+ }
98
+ return data;
99
+ }
100
+ get(path) {
101
+ return this.request("GET", path);
102
+ }
103
+ post(path, body) {
104
+ return this.request("POST", path, body);
105
+ }
106
+ put(path, body) {
107
+ return this.request("PUT", path, body);
108
+ }
109
+ delete(path) {
110
+ return this.request("DELETE", path);
111
+ }
112
+ };
113
+
114
+ // src/agents.ts
115
+ var AgentService = class {
116
+ constructor(client) {
117
+ this.client = client;
118
+ }
119
+ async register(params) {
120
+ return this.client.post("/v1/agents", params);
121
+ }
122
+ async get(agentId) {
123
+ return this.client.get(`/v1/agents/${agentId}`);
124
+ }
125
+ async list(params) {
126
+ const query = new URLSearchParams();
127
+ if (params?.name) query.set("name", params.name);
128
+ if (params?.status) query.set("status", params.status);
129
+ if (params?.limit) query.set("limit", String(params.limit));
130
+ if (params?.offset) query.set("offset", String(params.offset));
131
+ const qs = query.toString();
132
+ return this.client.get(`/v1/agents${qs ? `?${qs}` : ""}`);
133
+ }
134
+ async listChildren(agentId) {
135
+ return this.client.get(`/v1/agents/${agentId}/children`);
136
+ }
137
+ async resume(agentId) {
138
+ return this.client.post(`/v1/agents/${agentId}/resume`);
139
+ }
140
+ async setCircuitBreaker(agentId, params) {
141
+ return this.client.put(`/v1/agents/${agentId}/circuit-breaker`, params);
142
+ }
143
+ async getCircuitBreaker(agentId) {
144
+ return this.client.get(`/v1/agents/${agentId}/circuit-breaker`);
145
+ }
146
+ async rotateKeys(agentId) {
147
+ return this.client.post(`/v1/agents/${agentId}/rotate-keys`);
148
+ }
149
+ // ── Autonomous Agent Methods ──────────────────────────
150
+ /**
151
+ * Get the current balance for an autonomous agent.
152
+ */
153
+ async getBalance(agentId) {
154
+ return this.client.get(`/v1/agents/${agentId}/balance`);
155
+ }
156
+ /**
157
+ * Fund an autonomous agent's balance (topup).
158
+ */
159
+ async topup(agentId, params) {
160
+ return this.client.post(`/v1/agents/${agentId}/topup`, params);
161
+ }
162
+ /**
163
+ * Withdraw from an autonomous agent's balance (sweep).
164
+ * Omit `amount` to sweep everything above the sweep threshold.
165
+ */
166
+ async sweep(agentId, params) {
167
+ return this.client.post(`/v1/agents/${agentId}/sweep`, params ?? {});
168
+ }
169
+ /**
170
+ * Get paginated ledger history for an autonomous agent.
171
+ */
172
+ async getLedger(agentId, params) {
173
+ const query = new URLSearchParams();
174
+ if (params?.limit) query.set("limit", String(params.limit));
175
+ if (params?.offset) query.set("offset", String(params.offset));
176
+ if (params?.type) query.set("type", params.type);
177
+ if (params?.from) query.set("from", params.from);
178
+ if (params?.to) query.set("to", params.to);
179
+ const qs = query.toString();
180
+ return this.client.get(`/v1/agents/${agentId}/ledger${qs ? `?${qs}` : ""}`);
181
+ }
182
+ /**
183
+ * Record an earning (inbound money) for an autonomous agent.
184
+ */
185
+ async recordEarning(agentId, params) {
186
+ return this.client.post(`/v1/agents/${agentId}/earn`, params);
187
+ }
188
+ };
189
+
190
+ // src/users.ts
191
+ var UserService = class {
192
+ constructor(client) {
193
+ this.client = client;
194
+ }
195
+ async create(params) {
196
+ return this.client.post("/v1/users", params);
197
+ }
198
+ async get(userId) {
199
+ return this.client.get(`/v1/users/${userId}`);
200
+ }
201
+ async getByExternalId(externalId) {
202
+ return this.client.get(`/v1/users/by-external/${encodeURIComponent(externalId)}`);
203
+ }
204
+ async getWalletStatus(userId) {
205
+ return this.client.get(`/v1/users/${userId}/wallet-status`);
206
+ }
207
+ async addPaymentMethod(params) {
208
+ const { userId, ...body } = params;
209
+ return this.client.post(`/v1/users/${userId}/payment-methods`, body);
210
+ }
211
+ async completePaymentMethod(params) {
212
+ const { userId, ...body } = params;
213
+ return this.client.post(`/v1/users/${userId}/payment-methods/complete`, body);
214
+ }
215
+ async setLimits(params) {
216
+ const { userId, ...body } = params;
217
+ return this.client.put(`/v1/users/${userId}/limits`, body);
218
+ }
219
+ };
220
+
221
+ // src/payments.ts
222
+ var PaymentService = class {
223
+ constructor(client) {
224
+ this.client = client;
225
+ }
226
+ async create(params) {
227
+ return this.client.post("/v1/payments", params);
228
+ }
229
+ async get(paymentId) {
230
+ return this.client.get(`/v1/payments/${paymentId}`);
231
+ }
232
+ /** Complete a 3D Secure authentication step (method or challenge) */
233
+ async complete3ds(paymentId, params) {
234
+ return this.client.post(
235
+ `/v1/payments/${paymentId}/3ds/complete`,
236
+ params
237
+ );
238
+ }
239
+ };
240
+
241
+ // src/transactions.ts
242
+ var TransactionService = class {
243
+ constructor(client) {
244
+ this.client = client;
245
+ }
246
+ async list(params) {
247
+ const searchParams = new URLSearchParams();
248
+ if (params?.userId) searchParams.set("userId", params.userId);
249
+ if (params?.agentId) searchParams.set("agentId", params.agentId);
250
+ if (params?.from) searchParams.set("from", params.from);
251
+ if (params?.to) searchParams.set("to", params.to);
252
+ if (params?.limit) searchParams.set("limit", String(params.limit));
253
+ if (params?.offset) searchParams.set("offset", String(params.offset));
254
+ const qs = searchParams.toString();
255
+ return this.client.get(`/v1/transactions${qs ? `?${qs}` : ""}`);
256
+ }
257
+ };
258
+
259
+ // src/webhooks.ts
260
+ import { createHmac, timingSafeEqual } from "crypto";
261
+ var WEBHOOK_TOLERANCE_SECONDS = 300;
262
+ var WebhookService = class {
263
+ constructor(client) {
264
+ this.client = client;
265
+ }
266
+ async register(params) {
267
+ return this.client.post("/v1/webhooks", params);
268
+ }
269
+ async list() {
270
+ return this.client.get("/v1/webhooks");
271
+ }
272
+ async delete(webhookId) {
273
+ await this.client.delete(`/v1/webhooks/${webhookId}`);
274
+ }
275
+ /**
276
+ * Verify a webhook signature from an incoming request.
277
+ *
278
+ * ArisPay signs every webhook delivery with HMAC-SHA256 using the secret
279
+ * returned when you registered the webhook. The signature is sent in the
280
+ * `X-ArisPay-Signature` header and the unix timestamp in `X-ArisPay-Timestamp`.
281
+ *
282
+ * @param body - The raw request body string (do NOT parse it first)
283
+ * @param signature - Value of the `X-ArisPay-Signature` header
284
+ * @param timestamp - Value of the `X-ArisPay-Timestamp` header
285
+ * @param secret - Your webhook secret (`whsec_...`)
286
+ * @param toleranceSec - Maximum allowed age in seconds (default: 300 = 5 min)
287
+ * @returns `true` if the signature is valid and the timestamp is within tolerance
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * import { WebhookService } from 'arispay';
292
+ *
293
+ * app.post('/webhooks', (req, res) => {
294
+ * const isValid = WebhookService.verifyWebhookSignature(
295
+ * req.body, // raw body string
296
+ * req.headers['x-arispay-signature'],
297
+ * req.headers['x-arispay-timestamp'],
298
+ * process.env.WEBHOOK_SECRET!,
299
+ * );
300
+ * if (!isValid) return res.status(401).send('Invalid signature');
301
+ * // Process the webhook event...
302
+ * });
303
+ * ```
304
+ */
305
+ static verifyWebhookSignature(body, signature, timestamp, secret, toleranceSec = WEBHOOK_TOLERANCE_SECONDS) {
306
+ if (!body || !signature || !timestamp || !secret) return false;
307
+ const ts = parseInt(timestamp, 10);
308
+ if (isNaN(ts)) return false;
309
+ const age = Math.abs(Math.floor(Date.now() / 1e3) - ts);
310
+ if (age > toleranceSec) return false;
311
+ const signaturePayload = `${timestamp}.${body}`;
312
+ const expected = createHmac("sha256", secret).update(signaturePayload).digest("hex");
313
+ try {
314
+ return timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
315
+ } catch {
316
+ return false;
317
+ }
318
+ }
319
+ };
320
+
321
+ // src/index.ts
322
+ var ArisPay = class {
323
+ /**
324
+ * Initialize the ArisPay SDK.
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * import ArisPay from 'arispay';
329
+ *
330
+ * const arispay = ArisPay.init({
331
+ * apiKey: 'ap_test_abc123',
332
+ * environment: 'sandbox',
333
+ * });
334
+ *
335
+ * const agent = await arispay.agents.register({
336
+ * name: 'TableClaw',
337
+ * description: 'AI restaurant booking agent',
338
+ * permissions: ['browse', 'payment'],
339
+ * });
340
+ *
341
+ * const user = await arispay.users.create({
342
+ * externalId: 'user_123',
343
+ * email: 'jane@example.com',
344
+ * });
345
+ *
346
+ * const payment = await arispay.payments.create({
347
+ * agentId: agent.id,
348
+ * userId: user.id,
349
+ * amount: 20000,
350
+ * currency: 'USD',
351
+ * merchantUrl: 'https://nobu.com',
352
+ * memo: 'Table for 2',
353
+ * idempotencyKey: 'booking_123',
354
+ * });
355
+ * ```
356
+ */
357
+ static init(config) {
358
+ const defaultUrl = config.environment === "production" ? "https://api.arispay.app" : "https://api-production-79ea.up.railway.app";
359
+ const baseUrl = config.baseUrl || defaultUrl;
360
+ const client = new HttpClient(baseUrl, config.apiKey);
361
+ return {
362
+ agents: new AgentService(client),
363
+ users: new UserService(client),
364
+ payments: new PaymentService(client),
365
+ transactions: new TransactionService(client),
366
+ webhooks: new WebhookService(client)
367
+ };
368
+ }
369
+ };
370
+
371
+ // src/cli-shared.ts
372
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
373
+ import { homedir } from "os";
374
+ import { join } from "path";
375
+ import { createInterface } from "readline";
376
+ function createBrand(cmd, authHint) {
377
+ const configDir = join(homedir(), `.${cmd}`);
378
+ const envPrefix = cmd.toUpperCase();
379
+ return { cmd, configDir, envPrefix, authHint };
380
+ }
381
+ function configFile(brand) {
382
+ return join(brand.configDir, "config.json");
383
+ }
384
+ function loadConfig(brand) {
385
+ const file = configFile(brand);
386
+ const fileConfig = existsSync(file) ? JSON.parse(readFileSync(file, "utf-8")) : {};
387
+ return {
388
+ ...fileConfig,
389
+ apiKey: env(`${brand.envPrefix}_API_KEY`) || fileConfig.apiKey,
390
+ environment: env(`${brand.envPrefix}_ENV`) || fileConfig.environment || "sandbox",
391
+ baseUrl: env(`${brand.envPrefix}_BASE_URL`) || fileConfig.baseUrl
392
+ };
393
+ }
394
+ function saveConfig(brand, config) {
395
+ if (!existsSync(brand.configDir)) mkdirSync(brand.configDir, { recursive: true });
396
+ writeFileSync(configFile(brand), JSON.stringify(config, null, 2) + "\n");
397
+ }
398
+ function getClient(brand) {
399
+ const config = loadConfig(brand);
400
+ const key = config.apiKey || config.accessToken;
401
+ if (!key) {
402
+ error(`No credentials configured. Run: ${brand.authHint}`);
403
+ }
404
+ return ArisPay.init({
405
+ apiKey: key,
406
+ environment: config.environment,
407
+ baseUrl: config.baseUrl
408
+ });
409
+ }
410
+ function env(key) {
411
+ return process.env[key];
412
+ }
413
+ function error(msg) {
414
+ console.error(`\x1B[31m\u2717\x1B[0m ${msg}`);
415
+ process.exit(1);
416
+ }
417
+ function success(msg) {
418
+ console.log(`\x1B[32m\u2713\x1B[0m ${msg}`);
419
+ }
420
+ function dim(msg) {
421
+ return `\x1B[2m${msg}\x1B[0m`;
422
+ }
423
+ function bold(msg) {
424
+ return `\x1B[1m${msg}\x1B[0m`;
425
+ }
426
+ function cyan(msg) {
427
+ return `\x1B[36m${msg}\x1B[0m`;
428
+ }
429
+ function json(data) {
430
+ console.log(JSON.stringify(data, null, 2));
431
+ }
432
+ function table(rows) {
433
+ if (rows.length === 0) {
434
+ console.log(dim(" (none)"));
435
+ return;
436
+ }
437
+ const keys = Object.keys(rows[0]);
438
+ const widths = keys.map(
439
+ (k) => Math.max(k.length, ...rows.map((r) => String(r[k] ?? "").length))
440
+ );
441
+ console.log(keys.map((k, i) => bold(k.padEnd(widths[i]))).join(" "));
442
+ console.log(widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500"));
443
+ for (const row of rows) {
444
+ console.log(keys.map((k, i) => String(row[k] ?? "").padEnd(widths[i])).join(" "));
445
+ }
446
+ }
447
+ function prompt(question) {
448
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
449
+ return new Promise((resolve) => {
450
+ rl.question(question, (answer) => {
451
+ rl.close();
452
+ resolve(answer.trim());
453
+ });
454
+ });
455
+ }
456
+ function promptSecret(question) {
457
+ return new Promise((resolve) => {
458
+ process.stdout.write(question);
459
+ const rl = createInterface({ input: process.stdin, terminal: false });
460
+ if (process.stdin.isTTY) {
461
+ process.stdin.setRawMode?.(true);
462
+ }
463
+ let input = "";
464
+ const onData = (ch) => {
465
+ const c = ch.toString();
466
+ if (c === "\n" || c === "\r") {
467
+ process.stdin.removeListener("data", onData);
468
+ if (process.stdin.isTTY) process.stdin.setRawMode?.(false);
469
+ rl.close();
470
+ process.stdout.write("\n");
471
+ resolve(input.trim());
472
+ } else if (c === "\x7F" || c === "\b") {
473
+ input = input.slice(0, -1);
474
+ } else if (c === "") {
475
+ process.exit(0);
476
+ } else {
477
+ input += c;
478
+ }
479
+ };
480
+ if (process.stdin.isTTY) {
481
+ process.stdin.resume();
482
+ process.stdin.on("data", onData);
483
+ } else {
484
+ rl.on("line", (line) => {
485
+ rl.close();
486
+ resolve(line.trim());
487
+ });
488
+ }
489
+ });
490
+ }
491
+ function parseFlags(args) {
492
+ const flags = {};
493
+ const positional = [];
494
+ for (let i = 0; i < args.length; i++) {
495
+ const arg = args[i];
496
+ if (arg.startsWith("--")) {
497
+ const eqIdx = arg.indexOf("=");
498
+ if (eqIdx !== -1) {
499
+ flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
500
+ } else {
501
+ flags[arg.slice(2)] = args[++i] ?? "true";
502
+ }
503
+ } else if (arg.startsWith("-") && arg.length === 2) {
504
+ flags[arg.slice(1)] = args[++i] ?? "true";
505
+ } else {
506
+ positional.push(arg);
507
+ }
508
+ }
509
+ return { flags, positional };
510
+ }
511
+ function requireFlag(flags, key, label) {
512
+ const val = flags[key];
513
+ if (!val) error(`Missing required flag: --${key}${label ? ` (${label})` : ""}`);
514
+ return val;
515
+ }
516
+ async function cmdAgents(args, brand) {
517
+ const [sub, ...rest] = args;
518
+ if (!sub || sub === "help" || sub === "--help") {
519
+ return cmdAgentsHelp(brand);
520
+ }
521
+ const { flags, positional } = parseFlags(rest);
522
+ const client = getClient(brand);
523
+ const $ = brand.cmd;
524
+ switch (sub) {
525
+ case "list":
526
+ case "ls": {
527
+ const agents = await client.agents.list({
528
+ limit: flags["limit"] ? Number(flags["limit"]) : 20,
529
+ offset: flags["offset"] ? Number(flags["offset"]) : void 0,
530
+ status: flags["status"],
531
+ name: flags["name"]
532
+ });
533
+ table(
534
+ agents.agents?.map((a) => ({
535
+ id: a.id.slice(0, 12) + "\u2026",
536
+ name: a.name,
537
+ mode: a.mode || "platform",
538
+ status: a.status,
539
+ created: a.createdAt?.slice(0, 10)
540
+ })) ?? []
541
+ );
542
+ break;
543
+ }
544
+ case "create": {
545
+ const name = flags["name"] || positional[0];
546
+ if (!name) error(`Usage: ${$} agents create --name <name> [--mode platform|autonomous]`);
547
+ const agent = await client.agents.register({
548
+ name,
549
+ description: flags["description"],
550
+ permissions: (flags["permissions"] || "browse,payment").split(","),
551
+ mode: flags["mode"] || void 0,
552
+ sweepThreshold: flags["sweep-threshold"] ? Number(flags["sweep-threshold"]) : void 0
553
+ });
554
+ success(`Agent created: ${agent.id}`);
555
+ json(agent);
556
+ break;
557
+ }
558
+ case "get": {
559
+ const id = flags["id"] || positional[0];
560
+ if (!id) error(`Usage: ${$} agents get <id>`);
561
+ const agent = await client.agents.get(id);
562
+ json(agent);
563
+ break;
564
+ }
565
+ case "rotate-keys": {
566
+ const id = flags["id"] || positional[0];
567
+ if (!id) error(`Usage: ${$} agents rotate-keys <id>`);
568
+ const result = await client.agents.rotateKeys(id);
569
+ success("Keys rotated");
570
+ json(result);
571
+ break;
572
+ }
573
+ case "balance": {
574
+ const id = flags["id"] || positional[0];
575
+ if (!id) error(`Usage: ${$} agents balance <id>`);
576
+ const balance = await client.agents.getBalance(id);
577
+ const cents = balance.balance ?? balance.balanceCents ?? 0;
578
+ console.log(`
579
+ ${bold("Balance:")} ${cyan("$" + (cents / 100).toFixed(2))} ${balance.currency || "USD"}
580
+ `);
581
+ break;
582
+ }
583
+ case "ledger": {
584
+ const id = flags["id"] || positional[0];
585
+ if (!id) error(`Usage: ${$} agents ledger <id>`);
586
+ const ledger = await client.agents.getLedger(id, {
587
+ limit: flags["limit"] ? Number(flags["limit"]) : 20
588
+ });
589
+ table(
590
+ (ledger.entries ?? []).map((e) => ({
591
+ type: e.type,
592
+ direction: e.direction,
593
+ amount: "$" + (e.amount / 100).toFixed(2),
594
+ balance: "$" + (e.balanceAfter / 100).toFixed(2),
595
+ description: (e.description || "").slice(0, 30),
596
+ date: e.createdAt?.slice(0, 10)
597
+ }))
598
+ );
599
+ break;
600
+ }
601
+ case "topup": {
602
+ const id = flags["id"] || positional[0];
603
+ if (!id) error(`Usage: ${$} agents topup <id> --amount <cents> --user <userId>`);
604
+ const amount = Number(requireFlag(flags, "amount", "in cents"));
605
+ const userId = requireFlag(flags, "user", "end user ID");
606
+ const result = await client.agents.topup(id, { amount, userId });
607
+ success(`Topped up $${(amount / 100).toFixed(2)}`);
608
+ json(result);
609
+ break;
610
+ }
611
+ case "sweep": {
612
+ const id = flags["id"] || positional[0];
613
+ if (!id) error(`Usage: ${$} agents sweep <id> [--amount <cents>]`);
614
+ const result = await client.agents.sweep(id, {
615
+ amount: flags["amount"] ? Number(flags["amount"]) : void 0
616
+ });
617
+ success("Sweep complete");
618
+ json(result);
619
+ break;
620
+ }
621
+ case "resume": {
622
+ const id = flags["id"] || positional[0];
623
+ if (!id) error(`Usage: ${$} agents resume <id>`);
624
+ const result = await client.agents.resume(id);
625
+ success(`Agent resumed at ${result.resumedAt}`);
626
+ break;
627
+ }
628
+ default:
629
+ cmdAgentsHelp(brand);
630
+ }
631
+ }
632
+ function cmdAgentsHelp(brand) {
633
+ const $ = brand.cmd;
634
+ console.log(`
635
+ ${bold(`${$} agents`)} \u2014 Manage AI payment agents
636
+
637
+ ${cyan(`${$} agents list`)} List agents
638
+ ${cyan(`${$} agents create`)} Register a new agent
639
+ ${cyan(`${$} agents get <id>`)} Get agent details
640
+ ${cyan(`${$} agents balance <id>`)} Check agent balance
641
+ ${cyan(`${$} agents ledger <id>`)} View agent ledger
642
+ ${cyan(`${$} agents topup <id>`)} Fund agent from user's card
643
+ ${cyan(`${$} agents sweep <id>`)} Withdraw agent balance
644
+ ${cyan(`${$} agents rotate-keys <id>`)} Rotate TAP keypair
645
+ ${cyan(`${$} agents resume <id>`)} Resume suspended agent
646
+ `);
647
+ }
648
+ async function cmdUsers(args, brand) {
649
+ const [sub, ...rest] = args;
650
+ if (!sub || sub === "help" || sub === "--help") {
651
+ return cmdUsersHelp(brand);
652
+ }
653
+ const { flags, positional } = parseFlags(rest);
654
+ const client = getClient(brand);
655
+ const $ = brand.cmd;
656
+ switch (sub) {
657
+ case "create": {
658
+ const externalId = flags["external-id"] || positional[0];
659
+ if (!externalId) error(`Usage: ${$} users create --external-id <id> [--email <email>]`);
660
+ const user = await client.users.create({
661
+ externalId,
662
+ email: flags["email"]
663
+ });
664
+ success(`User created: ${user.id}`);
665
+ json(user);
666
+ break;
667
+ }
668
+ case "get": {
669
+ const id = flags["id"] || positional[0];
670
+ if (!id) error(`Usage: ${$} users get <id>`);
671
+ const user = await client.users.get(id);
672
+ json(user);
673
+ break;
674
+ }
675
+ case "find": {
676
+ const externalId = flags["external-id"] || positional[0];
677
+ if (!externalId) error(`Usage: ${$} users find <externalId>`);
678
+ const user = await client.users.getByExternalId(externalId);
679
+ json(user);
680
+ break;
681
+ }
682
+ case "add-card": {
683
+ const id = flags["id"] || positional[0];
684
+ if (!id) error(`Usage: ${$} users add-card <userId> --return-url <url>`);
685
+ const returnUrl = requireFlag(flags, "return-url");
686
+ const result = await client.users.addPaymentMethod({
687
+ userId: id,
688
+ type: "card",
689
+ returnUrl
690
+ });
691
+ success("Card setup initiated");
692
+ console.log(`
693
+ ${bold("Setup URL:")} ${result.setupUrl}`);
694
+ console.log(` ${bold("Session ID:")} ${result.sessionId}
695
+ `);
696
+ break;
697
+ }
698
+ case "add-wallet": {
699
+ const id = flags["id"] || positional[0];
700
+ if (!id) error(`Usage: ${$} users add-wallet <userId> --address <addr> --chain <chain>`);
701
+ const walletAddress = requireFlag(flags, "address", "wallet address");
702
+ const chain = requireFlag(flags, "chain", "ethereum|base|polygon|solana");
703
+ const result = await client.users.addPaymentMethod({
704
+ userId: id,
705
+ type: "wallet",
706
+ walletAddress,
707
+ chain
708
+ });
709
+ success("Wallet registered");
710
+ json(result);
711
+ break;
712
+ }
713
+ case "set-limits": {
714
+ const id = flags["id"] || positional[0];
715
+ if (!id) error(`Usage: ${$} users set-limits <userId> --agent <agentId> [--per-tx <cents>] [--daily <cents>] [--monthly <cents>]`);
716
+ const agentId = requireFlag(flags, "agent", "agent ID");
717
+ const result = await client.users.setLimits({
718
+ userId: id,
719
+ agentId,
720
+ maxPerTransaction: flags["per-tx"] ? Number(flags["per-tx"]) : void 0,
721
+ maxDaily: flags["daily"] ? Number(flags["daily"]) : void 0,
722
+ maxMonthly: flags["monthly"] ? Number(flags["monthly"]) : void 0
723
+ });
724
+ success("Spend limits updated");
725
+ json(result);
726
+ break;
727
+ }
728
+ case "wallet-status": {
729
+ const id = flags["id"] || positional[0];
730
+ if (!id) error(`Usage: ${$} users wallet-status <userId>`);
731
+ const status = await client.users.getWalletStatus(id);
732
+ json(status);
733
+ break;
734
+ }
735
+ default:
736
+ cmdUsersHelp(brand);
737
+ }
738
+ }
739
+ function cmdUsersHelp(brand) {
740
+ const $ = brand.cmd;
741
+ console.log(`
742
+ ${bold(`${$} users`)} \u2014 Manage end users & payment methods
743
+
744
+ ${cyan(`${$} users create`)} Create end user
745
+ ${cyan(`${$} users get <id>`)} Get user by ID
746
+ ${cyan(`${$} users find <extId>`)} Find user by external ID
747
+ ${cyan(`${$} users add-card <id>`)} Start card tokenization
748
+ ${cyan(`${$} users add-wallet <id>`)} Register crypto wallet
749
+ ${cyan(`${$} users set-limits <id>`)} Set spend limits
750
+ ${cyan(`${$} users wallet-status <id>`)} Check wallet USDC status
751
+ `);
752
+ }
753
+ async function cmdPay(args, brand) {
754
+ const { flags } = parseFlags(args);
755
+ const client = getClient(brand);
756
+ const agentId = requireFlag(flags, "agent", "agent ID");
757
+ const amount = Number(requireFlag(flags, "amount", "amount in cents"));
758
+ const currency = flags["currency"] || "USD";
759
+ const memo = requireFlag(flags, "memo", "payment memo");
760
+ const idempotencyKey = flags["idempotency-key"] || `cli_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
761
+ const params = {
762
+ agentId,
763
+ amount,
764
+ currency,
765
+ memo,
766
+ idempotencyKey
767
+ };
768
+ if (flags["user"]) params.userId = flags["user"];
769
+ if (flags["merchant-url"]) params.merchantUrl = flags["merchant-url"];
770
+ if (flags["merchant-name"]) params.merchantName = flags["merchant-name"];
771
+ if (flags["rail"]) params.rail = flags["rail"];
772
+ if (flags["chain"]) params.chain = flags["chain"];
773
+ if (flags["recipient"]) params.recipientAddress = flags["recipient"];
774
+ if (flags["token"]) params.token = flags["token"];
775
+ if (flags["metadata"]) {
776
+ try {
777
+ params.metadata = JSON.parse(flags["metadata"]);
778
+ } catch {
779
+ error("--metadata must be valid JSON");
780
+ }
781
+ }
782
+ console.log(`
783
+ ${dim("Creating payment...")}
784
+ `);
785
+ const payment = await client.payments.create(params);
786
+ const p = payment;
787
+ if (p.status === "succeeded") {
788
+ success(`Payment ${bold(p.id)} \u2014 $${(amount / 100).toFixed(2)} ${currency}`);
789
+ } else if (p.status === "requires_action") {
790
+ console.log(` ${bold("Status:")} requires_action (3DS)`);
791
+ if (p.nextAction?.type === "redirect") {
792
+ console.log(` ${bold("Redirect:")} ${p.nextAction.url}`);
793
+ }
794
+ } else {
795
+ console.log(` ${bold("Status:")} ${p.status}`);
796
+ }
797
+ console.log();
798
+ json(payment);
799
+ }
800
+ async function cmdPaymentGet(args, brand) {
801
+ const { positional } = parseFlags(args);
802
+ const id = positional[0];
803
+ if (!id) error(`Usage: ${brand.cmd} payment <id>`);
804
+ const client = getClient(brand);
805
+ const payment = await client.payments.get(id);
806
+ json(payment);
807
+ }
808
+ async function cmdTransactions(args, brand) {
809
+ const { flags } = parseFlags(args);
810
+ const client = getClient(brand);
811
+ const result = await client.transactions.list({
812
+ agentId: flags["agent"],
813
+ userId: flags["user"],
814
+ from: flags["from"],
815
+ to: flags["to"],
816
+ limit: flags["limit"] ? Number(flags["limit"]) : 20,
817
+ offset: flags["offset"] ? Number(flags["offset"]) : void 0
818
+ });
819
+ const payments = result.payments ?? result.transactions ?? [];
820
+ table(
821
+ payments.map((p) => ({
822
+ id: p.id.slice(0, 12) + "\u2026",
823
+ amount: "$" + (p.amount / 100).toFixed(2),
824
+ status: p.status,
825
+ rail: p.rail || "card",
826
+ memo: (p.memo || "").slice(0, 25),
827
+ date: p.createdAt?.slice(0, 10)
828
+ }))
829
+ );
830
+ }
831
+ async function cmdWebhooks(args, brand) {
832
+ const [sub, ...rest] = args;
833
+ if (!sub || sub === "help" || sub === "--help") {
834
+ return cmdWebhooksHelp(brand);
835
+ }
836
+ const { flags, positional } = parseFlags(rest);
837
+ const client = getClient(brand);
838
+ const $ = brand.cmd;
839
+ switch (sub) {
840
+ case "list":
841
+ case "ls": {
842
+ const result = await client.webhooks.list();
843
+ const webhooks = Array.isArray(result) ? result : result.data ?? [];
844
+ table(
845
+ webhooks.map((w) => ({
846
+ id: w.id.slice(0, 12) + "\u2026",
847
+ url: w.url.slice(0, 40),
848
+ events: (w.events || []).join(", ").slice(0, 30),
849
+ status: w.status || "active"
850
+ }))
851
+ );
852
+ break;
853
+ }
854
+ case "create": {
855
+ const url = flags["url"] || positional[0];
856
+ if (!url) error(`Usage: ${$} webhooks create --url <url> --events <evt1,evt2>`);
857
+ const events = (flags["events"] || "payment.succeeded,payment.failed").split(",");
858
+ const result = await client.webhooks.register({ url, events });
859
+ success("Webhook created");
860
+ console.log(`
861
+ ${bold("Secret:")} ${result.secret}`);
862
+ console.log(dim(" Store this \u2014 it won't be shown again.\n"));
863
+ json(result);
864
+ break;
865
+ }
866
+ case "delete":
867
+ case "rm": {
868
+ const id = flags["id"] || positional[0];
869
+ if (!id) error(`Usage: ${$} webhooks delete <id>`);
870
+ await client.webhooks.delete(id);
871
+ success(`Webhook ${id} deleted`);
872
+ break;
873
+ }
874
+ default:
875
+ cmdWebhooksHelp(brand);
876
+ }
877
+ }
878
+ function cmdWebhooksHelp(brand) {
879
+ const $ = brand.cmd;
880
+ console.log(`
881
+ ${bold(`${$} webhooks`)} \u2014 Manage webhook endpoints
882
+
883
+ ${cyan(`${$} webhooks list`)} List webhooks
884
+ ${cyan(`${$} webhooks create`)} Register new webhook
885
+ ${cyan(`${$} webhooks delete <id>`)} Delete webhook
886
+ `);
887
+ }
888
+ async function cmdStatus(brand) {
889
+ const config = loadConfig(brand);
890
+ console.log(`
891
+ ${bold(`${brand.cmd} status`)}
892
+ `);
893
+ const key = config.apiKey || config.accessToken;
894
+ if (config.email) {
895
+ console.log(` ${bold("Logged in:")} ${config.email}`);
896
+ }
897
+ if (key) {
898
+ const masked = key.slice(0, 12) + "\u2026" + key.slice(-4);
899
+ console.log(` ${bold("Auth:")} ${masked}`);
900
+ } else {
901
+ console.log(` ${bold("Auth:")} ${dim(`not configured \u2014 run: ${brand.authHint}`)}`);
902
+ }
903
+ console.log(` ${bold("Environment:")} ${config.environment || "sandbox"}`);
904
+ if (config.baseUrl) console.log(` ${bold("Base URL:")} ${config.baseUrl}`);
905
+ console.log(` ${bold("Config:")} ${configFile(brand)}`);
906
+ if (key) {
907
+ try {
908
+ const client = getClient(brand);
909
+ await client.transactions.list({ limit: 1 });
910
+ console.log(` ${bold("API:")} \x1B[32mconnected\x1B[0m`);
911
+ } catch (e) {
912
+ if (e.statusCode === 401) {
913
+ console.log(` ${bold("API:")} \x1B[31minvalid / expired\x1B[0m`);
914
+ } else {
915
+ console.log(` ${bold("API:")} \x1B[33munreachable\x1B[0m ${dim(e.message || "")}`);
916
+ }
917
+ }
918
+ }
919
+ console.log();
920
+ }
921
+ function getVersion() {
922
+ try {
923
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
924
+ return pkg.version;
925
+ } catch {
926
+ return "0.1.0";
927
+ }
928
+ }
929
+
930
+ export {
931
+ ArisPay,
932
+ createBrand,
933
+ configFile,
934
+ loadConfig,
935
+ saveConfig,
936
+ error,
937
+ success,
938
+ dim,
939
+ bold,
940
+ cyan,
941
+ table,
942
+ prompt,
943
+ promptSecret,
944
+ parseFlags,
945
+ cmdAgents,
946
+ cmdUsers,
947
+ cmdPay,
948
+ cmdPaymentGet,
949
+ cmdTransactions,
950
+ cmdWebhooks,
951
+ cmdStatus,
952
+ getVersion
953
+ };