agentspend 0.1.9 → 0.2.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 +20 -25
- package/SKILL.md +118 -0
- package/dist/cli.js +63 -0
- package/dist/commands/check.js +40 -0
- package/dist/commands/configure.js +99 -0
- package/dist/commands/pay.js +89 -206
- package/dist/commands/search.js +16 -0
- package/dist/commands/setup.js +36 -0
- package/dist/commands/status.js +7 -0
- package/dist/dev-index.js +10 -0
- package/dist/index.js +5 -15
- package/dist/lib/api.js +98 -0
- package/dist/lib/credentials.js +65 -0
- package/dist/lib/output.js +76 -0
- package/dist/lib/request-options.js +35 -0
- package/dist/types.js +1 -0
- package/package.json +17 -18
- package/dist/commands/card.d.ts +0 -2
- package/dist/commands/card.js +0 -197
- package/dist/commands/crypto-wallet.d.ts +0 -2
- package/dist/commands/crypto-wallet.js +0 -113
- package/dist/commands/pay.d.ts +0 -2
- package/dist/commands/wallet.d.ts +0 -2
- package/dist/commands/wallet.js +0 -102
- package/dist/index.d.ts +0 -2
- package/src/commands/card.ts +0 -252
- package/src/commands/pay.ts +0 -270
- package/src/commands/wallet.ts +0 -118
- package/src/index.ts +0 -19
- package/tsconfig.json +0 -8
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const METHOD_TOKEN_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
2
|
+
export function normalizeMethod(method) {
|
|
3
|
+
const normalized = (method ?? "GET").trim().toUpperCase();
|
|
4
|
+
if (!normalized || !METHOD_TOKEN_PATTERN.test(normalized)) {
|
|
5
|
+
throw new Error(`Invalid HTTP method: ${method ?? ""}`);
|
|
6
|
+
}
|
|
7
|
+
return normalized;
|
|
8
|
+
}
|
|
9
|
+
export function parseHeaders(rawHeaders) {
|
|
10
|
+
const parsed = {};
|
|
11
|
+
for (const header of rawHeaders ?? []) {
|
|
12
|
+
const separator = header.indexOf(":");
|
|
13
|
+
if (separator === -1) {
|
|
14
|
+
throw new Error(`Invalid header format: ${header}. Expected key:value.`);
|
|
15
|
+
}
|
|
16
|
+
const key = header.slice(0, separator).trim();
|
|
17
|
+
const value = header.slice(separator + 1).trim();
|
|
18
|
+
if (!key || !value) {
|
|
19
|
+
throw new Error(`Invalid header format: ${header}. Expected key:value.`);
|
|
20
|
+
}
|
|
21
|
+
parsed[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
export function parseBody(rawBody) {
|
|
26
|
+
if (rawBody === undefined) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(rawBody);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return rawBody;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentspend",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/jpbonch/agentspend.git"
|
|
10
|
-
},
|
|
11
|
-
"engines": {
|
|
12
|
-
"node": ">=18"
|
|
13
|
-
},
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "AgentSpend CLI for managed x402 spending",
|
|
5
|
+
"files": ["dist", "SKILL.md"],
|
|
6
|
+
"type": "module",
|
|
14
7
|
"bin": {
|
|
15
8
|
"agentspend": "dist/index.js"
|
|
16
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"dev:local": "tsx src/dev-index.ts",
|
|
14
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
15
|
+
},
|
|
17
16
|
"dependencies": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"commander": "^13.0.0",
|
|
21
|
-
"viem": "^2.0.0"
|
|
17
|
+
"bcryptjs": "^2.4.3",
|
|
18
|
+
"commander": "^12.1.0"
|
|
22
19
|
},
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bcryptjs": "^2.4.6",
|
|
22
|
+
"@types/node": "^22.10.2",
|
|
23
|
+
"tsx": "^4.19.2",
|
|
24
|
+
"typescript": "^5.7.2"
|
|
26
25
|
}
|
|
27
26
|
}
|
package/dist/commands/card.d.ts
DELETED
package/dist/commands/card.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerCardCommands = registerCardCommands;
|
|
4
|
-
const promises_1 = require("node:fs/promises");
|
|
5
|
-
const node_os_1 = require("node:os");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
|
-
const node_child_process_1 = require("node:child_process");
|
|
8
|
-
const node_process_1 = require("node:process");
|
|
9
|
-
const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".agentspend");
|
|
10
|
-
const SETUP_FILE = (0, node_path_1.join)(CONFIG_DIR, "setup.json");
|
|
11
|
-
const CARD_FILE = (0, node_path_1.join)(CONFIG_DIR, "card.json");
|
|
12
|
-
const API_BASE = process.env.AGENTSPEND_API_URL ?? "https://api.agentspend.co";
|
|
13
|
-
async function ensureConfigDir() {
|
|
14
|
-
await (0, promises_1.mkdir)(CONFIG_DIR, { recursive: true });
|
|
15
|
-
await (0, promises_1.chmod)(CONFIG_DIR, 0o700);
|
|
16
|
-
}
|
|
17
|
-
function openUrl(url) {
|
|
18
|
-
const cmd = node_process_1.platform === "darwin" ? "open" : node_process_1.platform === "win32" ? "start" : "xdg-open";
|
|
19
|
-
(0, node_child_process_1.exec)(`${cmd} ${JSON.stringify(url)}`);
|
|
20
|
-
}
|
|
21
|
-
async function readSetupId() {
|
|
22
|
-
try {
|
|
23
|
-
const data = JSON.parse(await (0, promises_1.readFile)(SETUP_FILE, "utf-8"));
|
|
24
|
-
if (typeof data.setup_id === "string" && data.setup_id) {
|
|
25
|
-
return data.setup_id;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
// file doesn't exist or is invalid
|
|
30
|
-
}
|
|
31
|
-
throw new Error("No setup_id found. Run 'agentspend card configure' first.");
|
|
32
|
-
}
|
|
33
|
-
async function readCardFile() {
|
|
34
|
-
try {
|
|
35
|
-
const data = JSON.parse(await (0, promises_1.readFile)(CARD_FILE, "utf-8"));
|
|
36
|
-
if (typeof data.card_id === "string" && typeof data.card_secret === "string") {
|
|
37
|
-
return { card_id: data.card_id, card_secret: data.card_secret };
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// file doesn't exist or is invalid
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
async function createCard() {
|
|
46
|
-
const response = await fetch(`${API_BASE}/v1/card/create`, {
|
|
47
|
-
method: "POST",
|
|
48
|
-
headers: { "content-type": "application/json" },
|
|
49
|
-
body: JSON.stringify({})
|
|
50
|
-
});
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
const body = await response.text();
|
|
53
|
-
throw new Error(`Failed to create card (${response.status}): ${body}`);
|
|
54
|
-
}
|
|
55
|
-
return (await response.json());
|
|
56
|
-
}
|
|
57
|
-
async function requestConfigure(cardId, cardSecret) {
|
|
58
|
-
const response = await fetch(`${API_BASE}/v1/card/configure`, {
|
|
59
|
-
method: "POST",
|
|
60
|
-
headers: { "content-type": "application/json" },
|
|
61
|
-
body: JSON.stringify({ card_id: cardId, card_secret: cardSecret })
|
|
62
|
-
});
|
|
63
|
-
if (!response.ok) {
|
|
64
|
-
const body = await response.text();
|
|
65
|
-
throw new Error(`Failed to open configure page (${response.status}): ${body}`);
|
|
66
|
-
}
|
|
67
|
-
return (await response.json());
|
|
68
|
-
}
|
|
69
|
-
async function getSetupStatus(setupId) {
|
|
70
|
-
const response = await fetch(`${API_BASE}/v1/card/setup/${encodeURIComponent(setupId)}`);
|
|
71
|
-
if (!response.ok) {
|
|
72
|
-
const body = await response.text();
|
|
73
|
-
throw new Error(`Failed to get setup status (${response.status}): ${body}`);
|
|
74
|
-
}
|
|
75
|
-
return (await response.json());
|
|
76
|
-
}
|
|
77
|
-
async function getCardStatus(cardId, cardSecret) {
|
|
78
|
-
const response = await fetch(`${API_BASE}/v1/card/${encodeURIComponent(cardId)}/status`, {
|
|
79
|
-
headers: { "x-card-secret": cardSecret }
|
|
80
|
-
});
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
const body = await response.text();
|
|
83
|
-
throw new Error(`Failed to get card status (${response.status}): ${body}`);
|
|
84
|
-
}
|
|
85
|
-
return (await response.json());
|
|
86
|
-
}
|
|
87
|
-
function sleep(ms) {
|
|
88
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
89
|
-
}
|
|
90
|
-
function formatCents(cents) {
|
|
91
|
-
return `$${(cents / 100).toFixed(2)}`;
|
|
92
|
-
}
|
|
93
|
-
function registerCardCommands(program) {
|
|
94
|
-
const card = program
|
|
95
|
-
.command("card")
|
|
96
|
-
.description("Manage AgentSpend cards");
|
|
97
|
-
card
|
|
98
|
-
.command("status")
|
|
99
|
-
.description("Show card dashboard: weekly budget, services, and recent charges")
|
|
100
|
-
.action(async () => {
|
|
101
|
-
try {
|
|
102
|
-
// If card.json exists, show full dashboard
|
|
103
|
-
const cardData = await readCardFile();
|
|
104
|
-
if (cardData) {
|
|
105
|
-
const status = await getCardStatus(cardData.card_id, cardData.card_secret);
|
|
106
|
-
console.log(`Card ID: ${status.card_id}`);
|
|
107
|
-
console.log(`Weekly budget: ${formatCents(status.weekly_spent_cents)} / ${formatCents(status.weekly_limit_cents)} used this week`);
|
|
108
|
-
console.log(`Remaining: ${formatCents(status.weekly_remaining_cents)}`);
|
|
109
|
-
console.log();
|
|
110
|
-
if (status.services.length > 0) {
|
|
111
|
-
console.log("Authorized services:");
|
|
112
|
-
for (const svc of status.services) {
|
|
113
|
-
console.log(` - ${svc.name} (${svc.status}, since ${new Date(svc.created_at).toLocaleDateString()})`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
console.log("Authorized services: No services authorized yet");
|
|
118
|
-
}
|
|
119
|
-
console.log();
|
|
120
|
-
if (status.recent_charges.length > 0) {
|
|
121
|
-
console.log("Recent charges:");
|
|
122
|
-
for (const ch of status.recent_charges) {
|
|
123
|
-
console.log(` - ${ch.service_name}: ${formatCents(ch.amount_cents)} on ${new Date(ch.created_at).toLocaleDateString()}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
console.log("Recent charges: No charges yet");
|
|
128
|
-
}
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
// Fall back to checking pending setup status
|
|
132
|
-
const setupId = await readSetupId();
|
|
133
|
-
const status = await getSetupStatus(setupId);
|
|
134
|
-
console.log(`Setup ID: ${status.setup_id}`);
|
|
135
|
-
console.log(`Status: ${status.status}`);
|
|
136
|
-
console.log(`Expires: ${status.expires_at}`);
|
|
137
|
-
if (status.card_id) {
|
|
138
|
-
console.log(`Card ID: ${status.card_id}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
catch (err) {
|
|
142
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
card
|
|
147
|
-
.command("configure")
|
|
148
|
-
.description("Set up or reconfigure your card — opens the configuration page in your browser")
|
|
149
|
-
.action(async () => {
|
|
150
|
-
try {
|
|
151
|
-
// Check if card.json exists — reconfigure flow
|
|
152
|
-
const cardData = await readCardFile();
|
|
153
|
-
if (cardData) {
|
|
154
|
-
const result = await requestConfigure(cardData.card_id, cardData.card_secret);
|
|
155
|
-
console.log("Opened configuration page in browser.");
|
|
156
|
-
openUrl(result.configure_url);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
// First-time setup flow
|
|
160
|
-
const result = await createCard();
|
|
161
|
-
console.log(`Setup ID: ${result.setup_id}`);
|
|
162
|
-
console.log(`Opening configuration page in browser...`);
|
|
163
|
-
openUrl(result.setup_url);
|
|
164
|
-
await ensureConfigDir();
|
|
165
|
-
await (0, promises_1.writeFile)(SETUP_FILE, JSON.stringify({ setup_id: result.setup_id }, null, 2));
|
|
166
|
-
await (0, promises_1.chmod)(SETUP_FILE, 0o600);
|
|
167
|
-
console.log("Waiting for setup to complete...");
|
|
168
|
-
while (true) {
|
|
169
|
-
await sleep(3000);
|
|
170
|
-
const status = await getSetupStatus(result.setup_id);
|
|
171
|
-
if (status.status === "ready") {
|
|
172
|
-
console.log(`Card is ready!`);
|
|
173
|
-
if (status.card_id) {
|
|
174
|
-
await (0, promises_1.writeFile)(CARD_FILE, JSON.stringify({
|
|
175
|
-
card_id: status.card_id,
|
|
176
|
-
card_secret: status.card_secret,
|
|
177
|
-
}, null, 2));
|
|
178
|
-
await (0, promises_1.chmod)(CARD_FILE, 0o600);
|
|
179
|
-
console.log(`Card ID saved to ${CARD_FILE}`);
|
|
180
|
-
}
|
|
181
|
-
// Clean up setup file
|
|
182
|
-
await (0, promises_1.unlink)(SETUP_FILE).catch(() => { });
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
if (status.status === "expired" || status.status === "failed") {
|
|
186
|
-
await (0, promises_1.unlink)(SETUP_FILE).catch(() => { });
|
|
187
|
-
throw new Error(`Setup ${status.status}. Please try again.`);
|
|
188
|
-
}
|
|
189
|
-
process.stdout.write(".");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
catch (err) {
|
|
193
|
-
console.error(`\nError: ${err instanceof Error ? err.message : err}`);
|
|
194
|
-
process.exit(1);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerCryptoWalletCommands = registerCryptoWalletCommands;
|
|
4
|
-
const promises_1 = require("node:fs/promises");
|
|
5
|
-
const node_os_1 = require("node:os");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
|
-
const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".agentspend");
|
|
8
|
-
const CRYPTO_WALLET_FILE = (0, node_path_1.join)(CONFIG_DIR, "crypto-wallet.json");
|
|
9
|
-
async function ensureConfigDir() {
|
|
10
|
-
await (0, promises_1.mkdir)(CONFIG_DIR, { recursive: true });
|
|
11
|
-
}
|
|
12
|
-
async function readCryptoWalletConfig() {
|
|
13
|
-
try {
|
|
14
|
-
const data = JSON.parse(await (0, promises_1.readFile)(CRYPTO_WALLET_FILE, "utf-8"));
|
|
15
|
-
if (typeof data.address === "string" && data.address) {
|
|
16
|
-
return data;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
// file doesn't exist or is invalid
|
|
21
|
-
}
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
function registerCryptoWalletCommands(program) {
|
|
25
|
-
const cryptoWallet = program
|
|
26
|
-
.command("crypto-wallet")
|
|
27
|
-
.description("Manage AgentSpend crypto wallets");
|
|
28
|
-
cryptoWallet
|
|
29
|
-
.command("setup")
|
|
30
|
-
.description("Set up a crypto wallet from a private key")
|
|
31
|
-
.requiredOption("--private-key <key>", "Ethereum private key (0x...)")
|
|
32
|
-
.option("--network <network>", "Chain identifier", "eip155:8453")
|
|
33
|
-
.action(async (opts) => {
|
|
34
|
-
try {
|
|
35
|
-
const privateKey = opts.privateKey.trim();
|
|
36
|
-
if (!privateKey.startsWith("0x") || privateKey.length !== 66) {
|
|
37
|
-
console.error("Error: Private key must be a 0x-prefixed 32-byte hex string (66 characters)");
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
// Derive address from private key using viem
|
|
41
|
-
let address;
|
|
42
|
-
try {
|
|
43
|
-
const { privateKeyToAccount } = await import("viem/accounts");
|
|
44
|
-
const account = privateKeyToAccount(privateKey);
|
|
45
|
-
address = account.address;
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
console.error(`Error: Failed to derive address from private key: ${err instanceof Error ? err.message : err}`);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
await ensureConfigDir();
|
|
52
|
-
const config = {
|
|
53
|
-
address,
|
|
54
|
-
network: opts.network
|
|
55
|
-
};
|
|
56
|
-
// Store the private key (in production, this should be encrypted)
|
|
57
|
-
// For now, store it so @x402/fetch can use it
|
|
58
|
-
const fullConfig = {
|
|
59
|
-
...config,
|
|
60
|
-
private_key: privateKey
|
|
61
|
-
};
|
|
62
|
-
await (0, promises_1.writeFile)(CRYPTO_WALLET_FILE, JSON.stringify(fullConfig, null, 2));
|
|
63
|
-
console.log(`Crypto wallet configured.`);
|
|
64
|
-
console.log(` Address: ${address}`);
|
|
65
|
-
console.log(` Network: ${opts.network}`);
|
|
66
|
-
console.log(` Saved to: ${CRYPTO_WALLET_FILE}`);
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
cryptoWallet
|
|
74
|
-
.command("status")
|
|
75
|
-
.description("Show configured crypto wallet address, network, and USDC balance")
|
|
76
|
-
.action(async () => {
|
|
77
|
-
try {
|
|
78
|
-
const config = await readCryptoWalletConfig();
|
|
79
|
-
if (!config) {
|
|
80
|
-
console.log("No crypto wallet configured.");
|
|
81
|
-
console.log("Run: agentspend crypto-wallet setup --private-key 0x...");
|
|
82
|
-
process.exit(0);
|
|
83
|
-
}
|
|
84
|
-
console.log(`Address: ${config.address}`);
|
|
85
|
-
console.log(`Network: ${config.network}`);
|
|
86
|
-
// Fetch USDC balance on Base
|
|
87
|
-
try {
|
|
88
|
-
const { createPublicClient, http, parseAbi } = await import("viem");
|
|
89
|
-
const { base } = await import("viem/chains");
|
|
90
|
-
const client = createPublicClient({
|
|
91
|
-
chain: base,
|
|
92
|
-
transport: http()
|
|
93
|
-
});
|
|
94
|
-
const usdcAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
95
|
-
const balance = await client.readContract({
|
|
96
|
-
address: usdcAddress,
|
|
97
|
-
abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
|
|
98
|
-
functionName: "balanceOf",
|
|
99
|
-
args: [config.address]
|
|
100
|
-
});
|
|
101
|
-
const usdcBalance = Number(balance) / 1e6;
|
|
102
|
-
console.log(`USDC Balance: ${usdcBalance.toFixed(2)} USDC`);
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
console.log("USDC Balance: (unable to fetch)");
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
package/dist/commands/pay.d.ts
DELETED
package/dist/commands/wallet.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerWalletCommands = registerWalletCommands;
|
|
4
|
-
const promises_1 = require("node:fs/promises");
|
|
5
|
-
const node_os_1 = require("node:os");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
|
-
const CONFIG_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".agentspend");
|
|
8
|
-
const WALLET_FILE = (0, node_path_1.join)(CONFIG_DIR, "wallet.json");
|
|
9
|
-
async function ensureConfigDir() {
|
|
10
|
-
await (0, promises_1.mkdir)(CONFIG_DIR, { recursive: true });
|
|
11
|
-
await (0, promises_1.chmod)(CONFIG_DIR, 0o700);
|
|
12
|
-
}
|
|
13
|
-
async function readWalletConfig() {
|
|
14
|
-
try {
|
|
15
|
-
const data = JSON.parse(await (0, promises_1.readFile)(WALLET_FILE, "utf-8"));
|
|
16
|
-
if (typeof data.address === "string" && data.address) {
|
|
17
|
-
return data;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
// file doesn't exist or is invalid
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
function registerWalletCommands(program) {
|
|
26
|
-
const wallet = program
|
|
27
|
-
.command("wallet")
|
|
28
|
-
.description("Manage AgentSpend crypto wallets");
|
|
29
|
-
wallet
|
|
30
|
-
.command("create")
|
|
31
|
-
.description("Generate a new crypto wallet for x402 payments")
|
|
32
|
-
.option("--network <network>", "Chain identifier", "eip155:8453")
|
|
33
|
-
.action(async (opts) => {
|
|
34
|
-
try {
|
|
35
|
-
const existing = await readWalletConfig();
|
|
36
|
-
if (existing) {
|
|
37
|
-
console.log(`Wallet already exists.`);
|
|
38
|
-
console.log(` Address: ${existing.address}`);
|
|
39
|
-
console.log(` Network: ${existing.network}`);
|
|
40
|
-
console.log(` Saved at: ${WALLET_FILE}`);
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
44
|
-
const privateKey = generatePrivateKey();
|
|
45
|
-
const account = privateKeyToAccount(privateKey);
|
|
46
|
-
await ensureConfigDir();
|
|
47
|
-
const config = {
|
|
48
|
-
address: account.address,
|
|
49
|
-
network: opts.network,
|
|
50
|
-
private_key: privateKey,
|
|
51
|
-
};
|
|
52
|
-
await (0, promises_1.writeFile)(WALLET_FILE, JSON.stringify(config, null, 2));
|
|
53
|
-
await (0, promises_1.chmod)(WALLET_FILE, 0o600);
|
|
54
|
-
console.log(`Wallet created. Address: ${account.address} — Fund it with USDC on Base.`);
|
|
55
|
-
console.log(`Private key saved to ${WALLET_FILE} (owner-only permissions). Keep this file secure.`);
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
wallet
|
|
63
|
-
.command("status")
|
|
64
|
-
.description("Show wallet address, network, and USDC balance")
|
|
65
|
-
.action(async () => {
|
|
66
|
-
try {
|
|
67
|
-
const config = await readWalletConfig();
|
|
68
|
-
if (!config) {
|
|
69
|
-
console.log("No wallet configured.");
|
|
70
|
-
console.log("Run: agentspend wallet create");
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
73
|
-
console.log(`Address: ${config.address}`);
|
|
74
|
-
console.log(`Network: ${config.network}`);
|
|
75
|
-
// Fetch USDC balance on Base
|
|
76
|
-
try {
|
|
77
|
-
const { createPublicClient, http, parseAbi } = await import("viem");
|
|
78
|
-
const { base } = await import("viem/chains");
|
|
79
|
-
const client = createPublicClient({
|
|
80
|
-
chain: base,
|
|
81
|
-
transport: http(),
|
|
82
|
-
});
|
|
83
|
-
const usdcAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
84
|
-
const balance = await client.readContract({
|
|
85
|
-
address: usdcAddress,
|
|
86
|
-
abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
|
|
87
|
-
functionName: "balanceOf",
|
|
88
|
-
args: [config.address],
|
|
89
|
-
});
|
|
90
|
-
const usdcBalance = Number(balance) / 1e6;
|
|
91
|
-
console.log(`USDC Balance: ${usdcBalance.toFixed(2)} USDC`);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
console.log("USDC Balance: (unable to fetch)");
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
package/dist/index.d.ts
DELETED