armory-cli 0.2.11 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -4
- package/dist/cli.js +543 -58
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,22 +1,129 @@
|
|
|
1
1
|
# armory-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI for the Armory x402 payment protocol. Scaffold apps, query networks/tokens, and verify endpoints.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
bun add -g armory-cli
|
|
9
|
+
# or
|
|
10
|
+
npm install -g armory-cli
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
### `armory create <template> [name]`
|
|
16
|
+
|
|
17
|
+
Scaffold a new x402 payment-enabled project.
|
|
18
|
+
|
|
19
|
+
**Server Templates:**
|
|
20
|
+
- `bun-server` - Bun server with payment middleware
|
|
21
|
+
- `express-server` - Express v5 server
|
|
22
|
+
- `hono-server` - Hono server with extension support
|
|
23
|
+
- `elysia-server` - Elysia/Bun server
|
|
24
|
+
- `next-server` - Next.js middleware
|
|
25
|
+
|
|
26
|
+
**Client Templates:**
|
|
27
|
+
- `viem-client` - Viem x402 client
|
|
28
|
+
- `ethers-client` - Ethers.js v6 client
|
|
29
|
+
- `web3-client` - Web3.js client
|
|
30
|
+
|
|
31
|
+
**Legacy:**
|
|
32
|
+
- `facilitator` - Payment verification server (deprecated)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
armory create bun-server my-api
|
|
36
|
+
armory create hono-server my-hono-api
|
|
37
|
+
armory create viem-client my-client
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `armory networks`
|
|
41
|
+
|
|
42
|
+
List supported blockchain networks.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
armory networks # All networks
|
|
46
|
+
armory networks --mainnet # Mainnets only
|
|
47
|
+
armory networks --testnet # Testnets only
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### `armory tokens [network]`
|
|
51
|
+
|
|
52
|
+
List supported tokens.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
armory tokens # All tokens
|
|
56
|
+
armory tokens base # Tokens on Base
|
|
57
|
+
armory tokens 8453 # By chain ID
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `armory validate <type> <value>`
|
|
61
|
+
|
|
62
|
+
Validate a network or token identifier.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
armory validate network base
|
|
66
|
+
armory validate network 8453
|
|
67
|
+
armory validate token usdc
|
|
68
|
+
armory validate token 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `armory extensions`
|
|
72
|
+
|
|
73
|
+
List available x402 protocol extensions.
|
|
12
74
|
|
|
13
75
|
```bash
|
|
14
|
-
armory
|
|
15
|
-
|
|
76
|
+
armory extensions
|
|
77
|
+
armory ext
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `armory verify <url>`
|
|
81
|
+
|
|
82
|
+
Check x402 payment headers on an endpoint.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
armory verify https://api.example.com/data
|
|
86
|
+
armory inspect https://api.example.com/data
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Create a payment server
|
|
93
|
+
armory create bun-server my-payment-api
|
|
94
|
+
cd my-payment-api
|
|
16
95
|
bun install
|
|
17
96
|
bun run dev
|
|
97
|
+
|
|
98
|
+
# In another terminal, create a client
|
|
99
|
+
armory create viem-client my-client
|
|
100
|
+
cd my-client
|
|
101
|
+
bun install
|
|
102
|
+
PRIVATE_KEY=0x... bun run dev
|
|
103
|
+
|
|
104
|
+
# Check available networks and tokens
|
|
105
|
+
armory networks
|
|
106
|
+
armory tokens base
|
|
107
|
+
|
|
108
|
+
# Verify an endpoint
|
|
109
|
+
armory verify http://localhost:3000/api/data
|
|
18
110
|
```
|
|
19
111
|
|
|
112
|
+
## Supported Networks
|
|
113
|
+
|
|
114
|
+
| Network | Chain ID |
|
|
115
|
+
|---------|----------|
|
|
116
|
+
| Ethereum | 1 |
|
|
117
|
+
| Base | 8453 |
|
|
118
|
+
| Base Sepolia | 84532 |
|
|
119
|
+
| SKALE Base | 1187947933 |
|
|
120
|
+
| SKALE Base Sepolia | 324705682 |
|
|
121
|
+
| Ethereum Sepolia | 11155111 |
|
|
122
|
+
|
|
123
|
+
## Supported Tokens
|
|
124
|
+
|
|
125
|
+
USDC, EURC, USDT, WBTC, WETH, SKL across supported networks.
|
|
126
|
+
|
|
20
127
|
---
|
|
21
128
|
|
|
22
129
|
MIT License | Sawyer Cutler 2026 | Provided "AS IS" without warranty
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { mkdir, writeFile } from "fs/promises";
|
|
6
6
|
import prompts from "prompts";
|
|
7
|
+
import {
|
|
8
|
+
getMainnets,
|
|
9
|
+
getTestnets,
|
|
10
|
+
getAllTokens,
|
|
11
|
+
getTokensByChain,
|
|
12
|
+
resolveNetwork,
|
|
13
|
+
resolveToken,
|
|
14
|
+
isResolvedNetwork,
|
|
15
|
+
isResolvedToken
|
|
16
|
+
} from "@armory-sh/base";
|
|
7
17
|
var GITIGNORE = `node_modules
|
|
8
18
|
dist
|
|
9
19
|
.env
|
|
@@ -11,6 +21,26 @@ dist
|
|
|
11
21
|
*.log
|
|
12
22
|
.DS_Store
|
|
13
23
|
`;
|
|
24
|
+
var EXTENSIONS_INFO = [
|
|
25
|
+
{
|
|
26
|
+
name: "bazaar",
|
|
27
|
+
description: "Resource discovery - let clients discover your API resources",
|
|
28
|
+
package: "@armory-sh/extensions",
|
|
29
|
+
hooks: ["declareDiscoveryExtension", "extractDiscoveryInfo", "validateDiscoveryExtension"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "siwx",
|
|
33
|
+
description: "Sign-In-With-X - wallet-based authentication",
|
|
34
|
+
package: "@armory-sh/extensions",
|
|
35
|
+
hooks: ["createSIWxHook", "createSIWxPayload", "verifySIWxSignature"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "payment-id",
|
|
39
|
+
description: "Payment Identifier - idempotency for payment requests",
|
|
40
|
+
package: "@armory-sh/extensions",
|
|
41
|
+
hooks: ["createPaymentIdHook", "declarePaymentIdentifierExtension"]
|
|
42
|
+
}
|
|
43
|
+
];
|
|
14
44
|
function getTemplateFiles(template, projectName) {
|
|
15
45
|
switch (template) {
|
|
16
46
|
case "facilitator":
|
|
@@ -21,18 +51,62 @@ function getTemplateFiles(template, projectName) {
|
|
|
21
51
|
".gitignore": GITIGNORE
|
|
22
52
|
};
|
|
23
53
|
case "server":
|
|
54
|
+
case "bun-server":
|
|
55
|
+
return {
|
|
56
|
+
"package.json": SERVER_PACKAGE(projectName, "bun"),
|
|
57
|
+
"src/index.ts": SERVER_INDEX_BUN,
|
|
58
|
+
"README.md": SERVER_README(projectName, "Bun"),
|
|
59
|
+
".gitignore": GITIGNORE
|
|
60
|
+
};
|
|
61
|
+
case "express-server":
|
|
62
|
+
return {
|
|
63
|
+
"package.json": SERVER_PACKAGE(projectName, "express"),
|
|
64
|
+
"src/index.ts": SERVER_INDEX_EXPRESS,
|
|
65
|
+
"README.md": SERVER_README(projectName, "Express"),
|
|
66
|
+
".gitignore": GITIGNORE
|
|
67
|
+
};
|
|
68
|
+
case "hono-server":
|
|
24
69
|
return {
|
|
25
|
-
"package.json": SERVER_PACKAGE(projectName),
|
|
26
|
-
"src/index.ts":
|
|
27
|
-
"README.md": SERVER_README(projectName),
|
|
70
|
+
"package.json": SERVER_PACKAGE(projectName, "hono"),
|
|
71
|
+
"src/index.ts": SERVER_INDEX_HONO,
|
|
72
|
+
"README.md": SERVER_README(projectName, "Hono"),
|
|
73
|
+
".gitignore": GITIGNORE
|
|
74
|
+
};
|
|
75
|
+
case "elysia-server":
|
|
76
|
+
return {
|
|
77
|
+
"package.json": SERVER_PACKAGE(projectName, "elysia"),
|
|
78
|
+
"src/index.ts": SERVER_INDEX_ELYSIA,
|
|
79
|
+
"README.md": SERVER_README(projectName, "Elysia"),
|
|
80
|
+
".gitignore": GITIGNORE
|
|
81
|
+
};
|
|
82
|
+
case "next-server":
|
|
83
|
+
return {
|
|
84
|
+
"package.json": SERVER_PACKAGE(projectName, "next"),
|
|
85
|
+
"src/middleware.ts": SERVER_INDEX_NEXT,
|
|
86
|
+
"README.md": SERVER_README(projectName, "Next.js"),
|
|
28
87
|
".gitignore": GITIGNORE
|
|
29
88
|
};
|
|
30
89
|
case "client":
|
|
90
|
+
case "viem-client":
|
|
31
91
|
return {
|
|
32
|
-
"package.json": CLIENT_PACKAGE(projectName),
|
|
33
|
-
"src/index.ts":
|
|
34
|
-
"src/client.ts":
|
|
35
|
-
"README.md": CLIENT_README(projectName),
|
|
92
|
+
"package.json": CLIENT_PACKAGE(projectName, "viem"),
|
|
93
|
+
"src/index.ts": CLIENT_INDEX_VIEM,
|
|
94
|
+
"src/client.ts": CLIENT_IMPL_VIEM,
|
|
95
|
+
"README.md": CLIENT_README(projectName, "Viem"),
|
|
96
|
+
".gitignore": GITIGNORE
|
|
97
|
+
};
|
|
98
|
+
case "ethers-client":
|
|
99
|
+
return {
|
|
100
|
+
"package.json": CLIENT_PACKAGE(projectName, "ethers"),
|
|
101
|
+
"src/index.ts": CLIENT_INDEX_ETHERS,
|
|
102
|
+
"README.md": CLIENT_README(projectName, "Ethers"),
|
|
103
|
+
".gitignore": GITIGNORE
|
|
104
|
+
};
|
|
105
|
+
case "web3-client":
|
|
106
|
+
return {
|
|
107
|
+
"package.json": CLIENT_PACKAGE(projectName, "web3"),
|
|
108
|
+
"src/index.ts": CLIENT_INDEX_WEB3,
|
|
109
|
+
"README.md": CLIENT_README(projectName, "Web3.js"),
|
|
36
110
|
".gitignore": GITIGNORE
|
|
37
111
|
};
|
|
38
112
|
default:
|
|
@@ -89,37 +163,160 @@ bun install
|
|
|
89
163
|
bun run dev
|
|
90
164
|
\`\`\`
|
|
91
165
|
`;
|
|
92
|
-
var SERVER_PACKAGE = (name) =>
|
|
166
|
+
var SERVER_PACKAGE = (name, framework) => {
|
|
167
|
+
const deps = {
|
|
168
|
+
bun: `"@armory-sh/base": "latest",
|
|
169
|
+
"@armory-sh/middleware-bun": "latest"`,
|
|
170
|
+
express: `"@armory-sh/base": "latest",
|
|
171
|
+
"@armory-sh/middleware-express": "latest",
|
|
172
|
+
"express": "^5.0.0"`,
|
|
173
|
+
hono: `"@armory-sh/base": "latest",
|
|
174
|
+
"@armory-sh/middleware-hono": "latest",
|
|
175
|
+
"hono": "^4.0.0"`,
|
|
176
|
+
elysia: `"@armory-sh/base": "latest",
|
|
177
|
+
"@armory-sh/middleware-elysia": "latest",
|
|
178
|
+
"elysia": "^1.0.0"`,
|
|
179
|
+
next: `"@armory-sh/base": "latest",
|
|
180
|
+
"@armory-sh/middleware-next": "latest",
|
|
181
|
+
"next": "^15.0.0"`
|
|
182
|
+
};
|
|
183
|
+
const scripts = {
|
|
184
|
+
bun: `"dev": "bun run src/index.ts"`,
|
|
185
|
+
express: `"dev": "bun run --bun src/index.ts"`,
|
|
186
|
+
hono: `"dev": "bun run --bun src/index.ts"`,
|
|
187
|
+
elysia: `"dev": "bun run --bun src/index.ts"`,
|
|
188
|
+
next: `"dev": "bun run --bun src/middleware.ts"`
|
|
189
|
+
};
|
|
190
|
+
return `{
|
|
93
191
|
"name": "${name}",
|
|
94
192
|
"version": "0.1.0",
|
|
95
193
|
"type": "module",
|
|
96
194
|
"scripts": {
|
|
97
|
-
|
|
195
|
+
${scripts[framework]}
|
|
98
196
|
},
|
|
99
197
|
"dependencies": {
|
|
100
|
-
|
|
101
|
-
"@armory-sh/middleware": "latest"
|
|
198
|
+
${deps[framework]}
|
|
102
199
|
}
|
|
103
200
|
}`;
|
|
104
|
-
|
|
201
|
+
};
|
|
202
|
+
var SERVER_INDEX_BUN = `import { Bun } from "bun";
|
|
105
203
|
import { paymentMiddleware } from "@armory-sh/middleware-bun";
|
|
106
204
|
import { USDC_BASE } from "@armory-sh/base";
|
|
107
205
|
|
|
108
206
|
const app = Bun.serve({
|
|
109
207
|
port: 3000,
|
|
110
208
|
fetch: paymentMiddleware({
|
|
111
|
-
payTo: "0x" + "0".repeat(40),
|
|
209
|
+
payTo: "0x" + "0".repeat(40),
|
|
112
210
|
network: "base",
|
|
113
211
|
token: USDC_BASE,
|
|
114
|
-
price: () => "2000000",
|
|
212
|
+
price: () => "2000000",
|
|
115
213
|
}),
|
|
116
214
|
});
|
|
117
215
|
|
|
118
216
|
console.log("Server running on http://localhost:3000");
|
|
119
217
|
`;
|
|
120
|
-
var
|
|
218
|
+
var SERVER_INDEX_EXPRESS = `import express from "express";
|
|
219
|
+
import { paymentMiddleware } from "@armory-sh/middleware-express";
|
|
220
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
221
|
+
|
|
222
|
+
const app = express();
|
|
223
|
+
|
|
224
|
+
app.use(
|
|
225
|
+
"/api",
|
|
226
|
+
paymentMiddleware({
|
|
227
|
+
requirements: {
|
|
228
|
+
scheme: "exact",
|
|
229
|
+
network: "base",
|
|
230
|
+
maxAmountRequired: "2000000",
|
|
231
|
+
resource: "https://example.com/api",
|
|
232
|
+
description: "API access",
|
|
233
|
+
mimeType: "application/json",
|
|
234
|
+
payTo: "0x" + "0".repeat(40),
|
|
235
|
+
assetId: USDC_BASE.contractAddress,
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
app.get("/api/data", (req, res) => {
|
|
241
|
+
res.json({ message: "Hello, paid user!", payment: req.payment });
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
app.listen(3000, () => {
|
|
245
|
+
console.log("Server running on http://localhost:3000");
|
|
246
|
+
});
|
|
247
|
+
`;
|
|
248
|
+
var SERVER_INDEX_HONO = `import { Hono } from "hono";
|
|
249
|
+
import { paymentMiddleware, createPaymentRequirements } from "@armory-sh/middleware-hono";
|
|
250
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
251
|
+
|
|
252
|
+
const app = new Hono();
|
|
253
|
+
|
|
254
|
+
const requirements = createPaymentRequirements({
|
|
255
|
+
payTo: "0x" + "0".repeat(40),
|
|
256
|
+
network: "base",
|
|
257
|
+
assetId: USDC_BASE.contractAddress,
|
|
258
|
+
amount: "2000000",
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
app.use("/api/*", paymentMiddleware({ requirements }));
|
|
262
|
+
|
|
263
|
+
app.get("/api/data", (c) => {
|
|
264
|
+
const payment = c.get("payment");
|
|
265
|
+
return c.json({ message: "Hello, paid user!", payment });
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
export default app;
|
|
269
|
+
|
|
270
|
+
// Start server
|
|
271
|
+
Bun.serve({
|
|
272
|
+
port: 3000,
|
|
273
|
+
fetch: app.fetch,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
console.log("Server running on http://localhost:3000");
|
|
277
|
+
`;
|
|
278
|
+
var SERVER_INDEX_ELYSIA = `import { Elysia } from "elysia";
|
|
279
|
+
import { paymentMiddleware } from "@armory-sh/middleware-elysia";
|
|
280
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
121
281
|
|
|
122
|
-
|
|
282
|
+
const app = new Elysia()
|
|
283
|
+
.use(
|
|
284
|
+
paymentMiddleware({
|
|
285
|
+
payTo: "0x" + "0".repeat(40),
|
|
286
|
+
network: "base",
|
|
287
|
+
token: USDC_BASE,
|
|
288
|
+
price: () => "2000000",
|
|
289
|
+
})
|
|
290
|
+
)
|
|
291
|
+
.get("/api/data", () => ({ message: "Hello, paid user!" }))
|
|
292
|
+
.listen(3000);
|
|
293
|
+
|
|
294
|
+
console.log("Server running on http://localhost:3000");
|
|
295
|
+
`;
|
|
296
|
+
var SERVER_INDEX_NEXT = `import { createMiddleware } from "@armory-sh/middleware-next";
|
|
297
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
298
|
+
import { NextResponse } from "next/server";
|
|
299
|
+
|
|
300
|
+
const paymentMiddleware = createMiddleware({
|
|
301
|
+
payTo: process.env.PAY_TO_ADDRESS ?? "0x" + "0".repeat(40),
|
|
302
|
+
network: "base",
|
|
303
|
+
token: USDC_BASE,
|
|
304
|
+
price: () => "2000000",
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
export function middleware(request: NextRequest) {
|
|
308
|
+
const response = paymentMiddleware(request);
|
|
309
|
+
if (response) return response;
|
|
310
|
+
return NextResponse.next();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const config = {
|
|
314
|
+
matcher: "/api/:path*",
|
|
315
|
+
};
|
|
316
|
+
`;
|
|
317
|
+
var SERVER_README = (name, framework) => `# ${name}
|
|
318
|
+
|
|
319
|
+
x402 Payment Protected Server (${framework})
|
|
123
320
|
|
|
124
321
|
## Development
|
|
125
322
|
|
|
@@ -134,7 +331,19 @@ bun run dev
|
|
|
134
331
|
curl http://localhost:3000/api/data
|
|
135
332
|
\`\`\`
|
|
136
333
|
`;
|
|
137
|
-
var CLIENT_PACKAGE = (name) =>
|
|
334
|
+
var CLIENT_PACKAGE = (name, library) => {
|
|
335
|
+
const deps = {
|
|
336
|
+
viem: `"@armory-sh/client-viem": "latest",
|
|
337
|
+
"@armory-sh/base": "latest",
|
|
338
|
+
"viem": "^2.0.0"`,
|
|
339
|
+
ethers: `"@armory-sh/client-ethers": "latest",
|
|
340
|
+
"@armory-sh/base": "latest",
|
|
341
|
+
"ethers": "^6.0.0"`,
|
|
342
|
+
web3: `"@armory-sh/client-web3": "latest",
|
|
343
|
+
"@armory-sh/base": "latest",
|
|
344
|
+
"web3": "^4.0.0"`
|
|
345
|
+
};
|
|
346
|
+
return `{
|
|
138
347
|
"name": "${name}",
|
|
139
348
|
"version": "0.1.0",
|
|
140
349
|
"type": "module",
|
|
@@ -142,11 +351,11 @@ var CLIENT_PACKAGE = (name) => `{
|
|
|
142
351
|
"dev": "bun run src/index.ts"
|
|
143
352
|
},
|
|
144
353
|
"dependencies": {
|
|
145
|
-
|
|
146
|
-
"@armory-sh/base": "latest"
|
|
354
|
+
${deps[library]}
|
|
147
355
|
}
|
|
148
356
|
}`;
|
|
149
|
-
|
|
357
|
+
};
|
|
358
|
+
var CLIENT_INDEX_VIEM = `import { createX402Client } from "@armory-sh/client-viem";
|
|
150
359
|
import { privateKeyToAccount } from "viem/accounts";
|
|
151
360
|
import { USDC_BASE } from "@armory-sh/base";
|
|
152
361
|
import { client } from "./client.js";
|
|
@@ -167,7 +376,7 @@ async function main() {
|
|
|
167
376
|
|
|
168
377
|
main().catch(console.error);
|
|
169
378
|
`;
|
|
170
|
-
var
|
|
379
|
+
var CLIENT_IMPL_VIEM = `import type { X402Client } from "@armory-sh/client-viem";
|
|
171
380
|
|
|
172
381
|
export async function fetchData(client: X402Client) {
|
|
173
382
|
const response = await client.fetch("http://localhost:3000/api/data");
|
|
@@ -176,9 +385,46 @@ export async function fetchData(client: X402Client) {
|
|
|
176
385
|
|
|
177
386
|
export const client = { fetchData };
|
|
178
387
|
`;
|
|
179
|
-
var
|
|
388
|
+
var CLIENT_INDEX_ETHERS = `import { createX402Client } from "@armory-sh/client-ethers";
|
|
389
|
+
import { Wallet } from "ethers";
|
|
390
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
180
391
|
|
|
181
|
-
|
|
392
|
+
const wallet = new Wallet(process.env.PRIVATE_KEY ?? "0x" + "1".repeat(64));
|
|
393
|
+
|
|
394
|
+
const x402Client = createX402Client({
|
|
395
|
+
wallet: { type: "wallet", wallet },
|
|
396
|
+
token: USDC_BASE,
|
|
397
|
+
version: 2,
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
async function main() {
|
|
401
|
+
const response = await x402Client.fetch("http://localhost:3000/api/data");
|
|
402
|
+
const data = await response.json();
|
|
403
|
+
console.log("Response:", data);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
main().catch(console.error);
|
|
407
|
+
`;
|
|
408
|
+
var CLIENT_INDEX_WEB3 = `import { createX402Client } from "@armory-sh/client-web3";
|
|
409
|
+
import { USDC_BASE } from "@armory-sh/base";
|
|
410
|
+
|
|
411
|
+
const x402Client = createX402Client({
|
|
412
|
+
wallet: { type: "privateKey", privateKey: process.env.PRIVATE_KEY ?? "0x" + "1".repeat(64) },
|
|
413
|
+
token: USDC_BASE,
|
|
414
|
+
version: 2,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
async function main() {
|
|
418
|
+
const response = await x402Client.fetch("http://localhost:3000/api/data");
|
|
419
|
+
const data = await response.json();
|
|
420
|
+
console.log("Response:", data);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
main().catch(console.error);
|
|
424
|
+
`;
|
|
425
|
+
var CLIENT_README = (name, library) => `# ${name}
|
|
426
|
+
|
|
427
|
+
x402-Enabled API Client (${library})
|
|
182
428
|
|
|
183
429
|
## Environment
|
|
184
430
|
|
|
@@ -193,52 +439,255 @@ bun install
|
|
|
193
439
|
bun run dev
|
|
194
440
|
\`\`\`
|
|
195
441
|
`;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
\
|
|
208
|
-
\u2551
|
|
209
|
-
\
|
|
210
|
-
\u2551
|
|
211
|
-
\
|
|
212
|
-
\u2551
|
|
213
|
-
\u2551
|
|
214
|
-
\u2551
|
|
215
|
-
\u2551
|
|
216
|
-
\u2551
|
|
217
|
-
\u2551
|
|
218
|
-
\
|
|
219
|
-
|
|
220
|
-
|
|
442
|
+
function printHelp() {
|
|
443
|
+
console.log("Usage:");
|
|
444
|
+
console.log(" armory <command> [options]");
|
|
445
|
+
console.log("Templates:");
|
|
446
|
+
console.log(" bun-server, express-server, hono-server, elysia-server, next-server");
|
|
447
|
+
console.log(" viem-client, ethers-client, web3-client, facilitator");
|
|
448
|
+
console.log("Examples:");
|
|
449
|
+
console.log(" armory create bun-server my-api");
|
|
450
|
+
console.log(" armory networks");
|
|
451
|
+
console.log(" armory tokens base");
|
|
452
|
+
console.log(`
|
|
453
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
454
|
+
\u2551 Armory x402 CLI \u2551
|
|
455
|
+
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
456
|
+
\u2551 Create payment-enabled apps with the x402 protocol \u2551
|
|
457
|
+
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
458
|
+
\u2551 \u2551
|
|
459
|
+
\u2551 COMMANDS \u2551
|
|
460
|
+
\u2551 \u2551
|
|
461
|
+
\u2551 create <template> [name] Scaffold a new project \u2551
|
|
462
|
+
\u2551 networks List supported networks \u2551
|
|
463
|
+
\u2551 tokens [network] List tokens (optionally by network) \u2551
|
|
464
|
+
\u2551 validate network <value> Validate a network identifier \u2551
|
|
465
|
+
\u2551 validate token <value> Validate a token identifier \u2551
|
|
466
|
+
\u2551 extensions List available extensions \u2551
|
|
467
|
+
\u2551 verify <url> Check x402 headers on an endpoint \u2551
|
|
468
|
+
\u2551 \u2551
|
|
469
|
+
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
470
|
+
\u2551 TEMPLATES \u2551
|
|
471
|
+
\u2551 \u2551
|
|
472
|
+
\u2551 Servers: \u2551
|
|
473
|
+
\u2551 bun-server Bun server with middleware \u2551
|
|
474
|
+
\u2551 express-server Express v5 server \u2551
|
|
475
|
+
\u2551 hono-server Hono server (with extensions) \u2551
|
|
476
|
+
\u2551 elysia-server Elysia server \u2551
|
|
477
|
+
\u2551 next-server Next.js middleware \u2551
|
|
478
|
+
\u2551 \u2551
|
|
479
|
+
\u2551 Clients: \u2551
|
|
480
|
+
\u2551 viem-client Viem x402 client \u2551
|
|
481
|
+
\u2551 ethers-client Ethers.js v6 client \u2551
|
|
482
|
+
\u2551 web3-client Web3.js client \u2551
|
|
483
|
+
\u2551 \u2551
|
|
484
|
+
\u2551 Legacy: \u2551
|
|
485
|
+
\u2551 facilitator Payment verification server (deprecated) \u2551
|
|
486
|
+
\u2551 \u2551
|
|
487
|
+
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
488
|
+
\u2551 EXAMPLES \u2551
|
|
489
|
+
\u2551 \u2551
|
|
490
|
+
\u2551 armory create bun-server my-api \u2551
|
|
491
|
+
\u2551 armory create hono-server my-hono-api \u2551
|
|
492
|
+
\u2551 armory create viem-client my-client \u2551
|
|
493
|
+
\u2551 armory networks \u2551
|
|
494
|
+
\u2551 armory tokens base \u2551
|
|
495
|
+
\u2551 armory validate network 8453 \u2551
|
|
496
|
+
\u2551 armory validate token usdc \u2551
|
|
497
|
+
\u2551 armory verify https://api.example.com/data \u2551
|
|
498
|
+
\u2551 \u2551
|
|
499
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
500
|
+
`);
|
|
501
|
+
}
|
|
502
|
+
function formatNetworkTable(networks) {
|
|
503
|
+
console.log("\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
504
|
+
console.log("\u2502 Name \u2502 Chain ID \u2502 RPC URL \u2502");
|
|
505
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
506
|
+
for (const network of networks) {
|
|
507
|
+
const name = network.name.padEnd(19);
|
|
508
|
+
const chainId = String(network.chainId).padEnd(8);
|
|
509
|
+
const rpc = network.rpcUrl.substring(0, 34).padEnd(34);
|
|
510
|
+
console.log(`\u2502 ${name} \u2502 ${chainId} \u2502 ${rpc} \u2502`);
|
|
221
511
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
512
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n");
|
|
513
|
+
}
|
|
514
|
+
function formatTokenTable(tokens) {
|
|
515
|
+
console.log("\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
516
|
+
console.log("\u2502 Symbol \u2502 Name \u2502 Chain ID \u2502 Address \u2502");
|
|
517
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
518
|
+
for (const token of tokens) {
|
|
519
|
+
const symbol = token.symbol.padEnd(7);
|
|
520
|
+
const name = token.name.substring(0, 15).padEnd(15);
|
|
521
|
+
const chainId = String(token.chainId).padEnd(8);
|
|
522
|
+
const address = token.contractAddress.padEnd(40);
|
|
523
|
+
console.log(`\u2502 ${symbol} \u2502 ${name} \u2502 ${chainId} \u2502 ${address} \u2502`);
|
|
524
|
+
}
|
|
525
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n");
|
|
526
|
+
}
|
|
527
|
+
function networksCommand(args) {
|
|
528
|
+
const showTestnets = args.includes("--testnet") || args.includes("-t");
|
|
529
|
+
const showMainnets = args.includes("--mainnet") || args.includes("-m");
|
|
530
|
+
if (showTestnets) {
|
|
531
|
+
console.log("\n\u{1F4E6} Testnet Networks:\n");
|
|
532
|
+
formatNetworkTable(getTestnets());
|
|
533
|
+
} else if (showMainnets) {
|
|
534
|
+
console.log("\n\u{1F310} Mainnet Networks:\n");
|
|
535
|
+
formatNetworkTable(getMainnets());
|
|
536
|
+
} else {
|
|
537
|
+
console.log("\n\u{1F310} Mainnet Networks:\n");
|
|
538
|
+
formatNetworkTable(getMainnets());
|
|
539
|
+
console.log("\u{1F4E6} Testnet Networks:\n");
|
|
540
|
+
formatNetworkTable(getTestnets());
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function tokensCommand(args) {
|
|
544
|
+
const networkFilter = args.find((a) => !a.startsWith("-"));
|
|
545
|
+
if (networkFilter) {
|
|
546
|
+
const resolved = resolveNetwork(networkFilter);
|
|
547
|
+
if (!isResolvedNetwork(resolved)) {
|
|
548
|
+
console.error(`Unknown network: ${networkFilter}`);
|
|
549
|
+
console.log("Run 'armory networks' to see available networks");
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
const tokens = getTokensByChain(resolved.chainId);
|
|
553
|
+
if (tokens.length === 0) {
|
|
554
|
+
console.log(`No tokens configured for ${resolved.name}`);
|
|
555
|
+
} else {
|
|
556
|
+
console.log(`
|
|
557
|
+
\u{1F4B0} Tokens on ${resolved.name} (Chain ID: ${resolved.chainId}):
|
|
558
|
+
`);
|
|
559
|
+
formatTokenTable(tokens);
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
const tokens = getAllTokens();
|
|
563
|
+
console.log("\n\u{1F4B0} All Configured Tokens:\n");
|
|
564
|
+
formatTokenTable(tokens);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function validateCommand(args) {
|
|
568
|
+
const [type, value] = args;
|
|
569
|
+
if (!type || !value) {
|
|
570
|
+
console.error("Usage: armory validate <network|token> <value>");
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
if (type === "network") {
|
|
574
|
+
const resolved = resolveNetwork(value);
|
|
575
|
+
if (isResolvedNetwork(resolved)) {
|
|
576
|
+
console.log(`
|
|
577
|
+
\u2705 Valid network: ${resolved.name}`);
|
|
578
|
+
console.log(` Chain ID: ${resolved.chainId}`);
|
|
579
|
+
console.log(` CAIP-2: ${resolved.caip2Id}`);
|
|
580
|
+
console.log(` RPC: ${resolved.rpcUrl}
|
|
581
|
+
`);
|
|
582
|
+
} else {
|
|
583
|
+
console.error(`
|
|
584
|
+
\u274C Invalid network: ${value}`);
|
|
585
|
+
console.log("Run 'armory networks' to see available networks\n");
|
|
586
|
+
process.exit(1);
|
|
587
|
+
}
|
|
588
|
+
} else if (type === "token") {
|
|
589
|
+
const resolved = resolveToken(value);
|
|
590
|
+
if (isResolvedToken(resolved)) {
|
|
591
|
+
console.log(`
|
|
592
|
+
\u2705 Valid token: ${resolved.symbol}`);
|
|
593
|
+
console.log(` Name: ${resolved.name}`);
|
|
594
|
+
console.log(` Chain ID: ${resolved.chainId}`);
|
|
595
|
+
console.log(` Address: ${resolved.contractAddress}`);
|
|
596
|
+
console.log(` Decimals: ${resolved.decimals ?? 18}
|
|
597
|
+
`);
|
|
598
|
+
} else {
|
|
599
|
+
console.error(`
|
|
600
|
+
\u274C Invalid token: ${value}`);
|
|
601
|
+
console.log("Run 'armory tokens' to see available tokens\n");
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
} else {
|
|
605
|
+
console.error(`Unknown validation type: ${type}`);
|
|
606
|
+
console.log("Valid types: network, token");
|
|
607
|
+
process.exit(1);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function extensionsCommand() {
|
|
611
|
+
console.log("\n\u{1F50C} Available x402 Extensions:\n");
|
|
612
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
613
|
+
console.log("\u2502 Extension \u2502 Description \u2502");
|
|
614
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
615
|
+
for (const ext of EXTENSIONS_INFO) {
|
|
616
|
+
const name = ext.name.padEnd(13);
|
|
617
|
+
const desc = ext.description.substring(0, 51).padEnd(51);
|
|
618
|
+
console.log(`\u2502 ${name} \u2502 ${desc} \u2502`);
|
|
619
|
+
}
|
|
620
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n");
|
|
621
|
+
console.log("Usage:");
|
|
622
|
+
console.log(" import { createSIWxHook, createPaymentIdHook } from '@armory-sh/extensions';\n");
|
|
623
|
+
console.log("Extension Hooks:");
|
|
624
|
+
for (const ext of EXTENSIONS_INFO) {
|
|
625
|
+
console.log(` ${ext.name}: ${ext.hooks.slice(0, 2).join(", ")}`);
|
|
626
|
+
}
|
|
627
|
+
console.log("");
|
|
628
|
+
}
|
|
629
|
+
async function verifyCommand(args) {
|
|
630
|
+
const url = args[0];
|
|
631
|
+
if (!url) {
|
|
632
|
+
console.error("Usage: armory verify <url>");
|
|
633
|
+
process.exit(1);
|
|
634
|
+
}
|
|
635
|
+
console.log(`
|
|
636
|
+
\u{1F50D} Checking x402 headers on: ${url}
|
|
637
|
+
`);
|
|
638
|
+
try {
|
|
639
|
+
const response = await fetch(url);
|
|
640
|
+
const paymentRequired = response.headers.get("x-payment-required");
|
|
641
|
+
const paymentResponse = response.headers.get("x-payment-response");
|
|
642
|
+
console.log(`Status: ${response.status} ${response.statusText}`);
|
|
643
|
+
console.log(`x-payment-required: ${paymentRequired ?? "not set"}`);
|
|
644
|
+
console.log(`x-payment-response: ${paymentResponse ?? "not set"}`);
|
|
645
|
+
if (response.status === 402) {
|
|
646
|
+
console.log("\n\u2705 Endpoint requires payment (402)");
|
|
647
|
+
} else if (paymentRequired) {
|
|
648
|
+
console.log("\n\u26A0\uFE0F Payment header present but status is not 402");
|
|
649
|
+
} else {
|
|
650
|
+
console.log("\n\u2139\uFE0F No payment required for this endpoint");
|
|
651
|
+
}
|
|
652
|
+
} catch (error) {
|
|
653
|
+
console.error(`
|
|
654
|
+
\u274C Failed to fetch: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
225
655
|
process.exit(1);
|
|
226
656
|
}
|
|
657
|
+
}
|
|
658
|
+
async function createCommand(args) {
|
|
227
659
|
const p = prompts;
|
|
228
|
-
let template =
|
|
229
|
-
let projectName =
|
|
660
|
+
let template = args[0];
|
|
661
|
+
let projectName = args[1];
|
|
662
|
+
const templates = [
|
|
663
|
+
"bun-server",
|
|
664
|
+
"express-server",
|
|
665
|
+
"hono-server",
|
|
666
|
+
"elysia-server",
|
|
667
|
+
"next-server",
|
|
668
|
+
"viem-client",
|
|
669
|
+
"ethers-client",
|
|
670
|
+
"web3-client",
|
|
671
|
+
"facilitator",
|
|
672
|
+
"server",
|
|
673
|
+
"client"
|
|
674
|
+
];
|
|
230
675
|
if (!template) {
|
|
231
676
|
const result = await p.select({
|
|
232
677
|
message: "What do you want to create?",
|
|
233
678
|
choices: [
|
|
234
|
-
{ title: "
|
|
235
|
-
{ title: "Server -
|
|
236
|
-
{ title: "
|
|
679
|
+
{ title: "Bun Server - Simple x402 payment server", value: "bun-server" },
|
|
680
|
+
{ title: "Express Server - Express v5 with x402 middleware", value: "express-server" },
|
|
681
|
+
{ title: "Hono Server - Hono with extensions support", value: "hono-server" },
|
|
682
|
+
{ title: "Elysia Server - Elysia/Bun x402 server", value: "elysia-server" },
|
|
683
|
+
{ title: "Next.js Middleware - Next.js payment middleware", value: "next-server" },
|
|
684
|
+
{ title: "Viem Client - x402 client with Viem", value: "viem-client" },
|
|
685
|
+
{ title: "Ethers Client - x402 client with Ethers.js", value: "ethers-client" },
|
|
686
|
+
{ title: "Web3 Client - x402 client with Web3.js", value: "web3-client" }
|
|
237
687
|
]
|
|
238
688
|
});
|
|
239
689
|
template = result;
|
|
240
690
|
}
|
|
241
|
-
const templates = ["facilitator", "server", "client"];
|
|
242
691
|
if (!templates.includes(template)) {
|
|
243
692
|
console.error(`Unknown template: ${template}`);
|
|
244
693
|
console.log(`Available: ${templates.join(", ")}`);
|
|
@@ -247,7 +696,7 @@ async function main() {
|
|
|
247
696
|
if (!projectName) {
|
|
248
697
|
projectName = await p.text({
|
|
249
698
|
message: "Project name?",
|
|
250
|
-
default: `my-${template}`,
|
|
699
|
+
default: `my-${template.replace("-server", "").replace("-client", "")}`,
|
|
251
700
|
validate: (n) => /^[a-z0-9-]+$/.test(n) || "Use lowercase, numbers, dashes only"
|
|
252
701
|
});
|
|
253
702
|
}
|
|
@@ -273,6 +722,42 @@ Creating ${template} in ${projectPath}...
|
|
|
273
722
|
console.log(` bun run dev
|
|
274
723
|
`);
|
|
275
724
|
}
|
|
725
|
+
async function main() {
|
|
726
|
+
const args = process.argv.slice(2);
|
|
727
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
728
|
+
printHelp();
|
|
729
|
+
process.exit(0);
|
|
730
|
+
}
|
|
731
|
+
const [command, ...rest] = args;
|
|
732
|
+
switch (command) {
|
|
733
|
+
case "create":
|
|
734
|
+
await createCommand(rest);
|
|
735
|
+
break;
|
|
736
|
+
case "networks":
|
|
737
|
+
case "network":
|
|
738
|
+
networksCommand(rest);
|
|
739
|
+
break;
|
|
740
|
+
case "tokens":
|
|
741
|
+
case "token":
|
|
742
|
+
tokensCommand(rest);
|
|
743
|
+
break;
|
|
744
|
+
case "validate":
|
|
745
|
+
validateCommand(rest);
|
|
746
|
+
break;
|
|
747
|
+
case "extensions":
|
|
748
|
+
case "ext":
|
|
749
|
+
extensionsCommand();
|
|
750
|
+
break;
|
|
751
|
+
case "verify":
|
|
752
|
+
case "inspect":
|
|
753
|
+
await verifyCommand(rest);
|
|
754
|
+
break;
|
|
755
|
+
default:
|
|
756
|
+
console.error(`Unknown command: ${command}`);
|
|
757
|
+
console.log("Run 'armory --help' for usage");
|
|
758
|
+
process.exit(1);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
276
761
|
main().catch((err) => {
|
|
277
762
|
console.error("Error:", err.message);
|
|
278
763
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "armory-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Scaffold x402 payment-enabled apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"test": "bun test"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@armory-sh/base": "0.2.21",
|
|
43
|
+
"@armory-sh/extensions": "0.1.2",
|
|
42
44
|
"prompts": "2.4.2"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|