@tanakayuto/intmax402-cli 0.1.2 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +109 -70
  2. package/package.json +9 -5
package/dist/index.js CHANGED
@@ -1,106 +1,145 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
3
6
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const crypto_1 = require("crypto");
7
+ const minimist_1 = __importDefault(require("minimist"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ethers_1 = require("ethers");
5
10
  const intmax402_core_1 = require("@tanakayuto/intmax402-core");
6
- const intmax402_client_1 = require("@tanakayuto/intmax402-client");
7
- const [, , command, ...args] = process.argv;
11
+ const argv = (0, minimist_1.default)(process.argv.slice(2), {
12
+ string: ["mode"],
13
+ boolean: ["help"],
14
+ alias: { h: "help" },
15
+ default: { mode: "identity" },
16
+ });
17
+ const command = argv._[0];
8
18
  async function main() {
19
+ if (argv.help || !command) {
20
+ printHelp();
21
+ return;
22
+ }
9
23
  switch (command) {
10
24
  case "test":
11
- await testCommand(args[0]);
25
+ await testCommand(argv._[1], argv.mode);
12
26
  break;
13
27
  case "keygen":
14
28
  keygenCommand();
15
29
  break;
16
- case "verify":
17
- verifyCommand(args[0]);
18
- break;
19
30
  default:
20
- printUsage();
31
+ console.error(chalk_1.default.red(`Unknown command: ${command}`));
32
+ printHelp();
33
+ process.exit(1);
21
34
  }
22
35
  }
23
- async function testCommand(url) {
36
+ async function testCommand(url, mode = "identity") {
24
37
  if (!url) {
25
- console.error("Usage: intmax402 test <url>");
38
+ console.error(chalk_1.default.red("Usage: intmax402 test <url> [--mode identity|payment]"));
26
39
  process.exit(1);
27
40
  }
28
- console.log(`Testing INTMAX402 flow against ${url}...\n`);
29
- // Step 1: Initial request
30
- console.log("Step 1: Sending initial request...");
31
- const response = await fetch(url);
32
- console.log(` Status: ${response.status}`);
33
- if (response.status !== 401 && response.status !== 402) {
34
- console.log(" No 402 challenge received. Endpoint may not be protected.");
41
+ console.log(`Testing: ${chalk_1.default.cyan(url)}\n`);
42
+ const startTime = Date.now();
43
+ // Generate a random wallet for testing
44
+ const wallet = ethers_1.ethers.Wallet.createRandom();
45
+ const privateKey = wallet.privateKey;
46
+ const address = wallet.address;
47
+ // Step 1: Initial GET expect 401
48
+ process.stdout.write(` ${chalk_1.default.yellow("①")} GET ${new URL(url).pathname} → `);
49
+ let res1;
50
+ try {
51
+ res1 = await fetch(url);
52
+ }
53
+ catch (err) {
54
+ console.log(chalk_1.default.red(`error: ${err.message}`));
55
+ process.exit(1);
56
+ }
57
+ const statusColor = res1.status === 401 || res1.status === 402 ? chalk_1.default.yellow : chalk_1.default.green;
58
+ console.log(statusColor(`${res1.status}`));
59
+ if (res1.status !== 401 && res1.status !== 402) {
60
+ console.log(chalk_1.default.yellow(" (No 402/401 challenge received. Endpoint may not be protected.)"));
35
61
  return;
36
62
  }
37
- const wwwAuth = response.headers.get("www-authenticate");
63
+ // Step 2: Parse nonce
64
+ const wwwAuth = res1.headers.get("www-authenticate");
38
65
  if (!wwwAuth) {
39
- console.error(" No WWW-Authenticate header found.");
40
- return;
66
+ console.error(chalk_1.default.red(" No WWW-Authenticate header found."));
67
+ process.exit(1);
41
68
  }
42
- console.log(` WWW-Authenticate: ${wwwAuth}`);
43
69
  const challenge = (0, intmax402_core_1.parseWWWAuthenticate)(wwwAuth);
44
70
  if (!challenge) {
45
- console.error(" Failed to parse challenge.");
46
- return;
71
+ console.error(chalk_1.default.red(" Failed to parse WWW-Authenticate challenge."));
72
+ process.exit(1);
47
73
  }
48
- console.log(` Mode: ${challenge.mode}`);
49
- console.log(` Nonce: ${challenge.nonce}`);
50
- // Step 2: Generate key and sign
51
- console.log("\nStep 2: Generating key and signing...");
52
- const privateKey = "0x" + (0, crypto_1.randomBytes)(32).toString("hex");
53
- const client = new intmax402_client_1.INTMAX402Client({ privateKey });
54
- await client.init();
55
- console.log(` Address: ${client.getAddress()}`);
56
- const signature = await client.sign(challenge.nonce);
57
- console.log(` Signature: ${signature.slice(0, 20)}...`);
58
- // Step 3: Retry with credentials
59
- console.log("\nStep 3: Retrying with credentials...");
60
- const retryResponse = await client.fetch(url);
61
- console.log(` Status: ${retryResponse.status}`);
62
- if (retryResponse.ok) {
63
- const body = await retryResponse.text();
64
- console.log(` Response: ${body.slice(0, 200)}`);
74
+ const nonceShort = challenge.nonce.slice(0, 16) + "...";
75
+ console.log(` ${chalk_1.default.yellow("②")} nonce: ${chalk_1.default.dim(nonceShort)} (${challenge.realm})`);
76
+ // Step 3: Sign
77
+ console.log(` ${chalk_1.default.yellow("③")} Signing with wallet: ${chalk_1.default.dim(address.slice(0, 8) + "...")}`);
78
+ let authHeader;
79
+ if (mode === "payment") {
80
+ // Payment mode: include a mock txHash
81
+ const mockTxHash = "0x" + Buffer.alloc(32).fill(0xab).toString("hex");
82
+ const message = `${challenge.nonce}:${mockTxHash}`;
83
+ const signature = await wallet.signMessage(message);
84
+ authHeader = `INTMAX402 address="${address}",nonce="${challenge.nonce}",signature="${signature}",txHash="${mockTxHash}"`;
65
85
  }
66
86
  else {
67
- console.log(` Response: ${await retryResponse.text()}`);
87
+ // Identity mode: sign just the nonce
88
+ const signature = await wallet.signMessage(challenge.nonce);
89
+ authHeader = `INTMAX402 address="${address}",nonce="${challenge.nonce}",signature="${signature}"`;
68
90
  }
69
- }
70
- function keygenCommand() {
71
- const privateKey = "0x" + (0, crypto_1.randomBytes)(32).toString("hex");
72
- const client = new intmax402_client_1.INTMAX402Client({ privateKey });
73
- console.log("Generated test keypair:");
74
- console.log(` Private Key: ${privateKey}`);
75
- console.log(` Address: ${client.getAddress()}`);
76
- }
77
- function verifyCommand(header) {
78
- if (!header) {
79
- console.error("Usage: intmax402 verify <authorization-header>");
80
- process.exit(1);
91
+ // Step 4: Retry with Authorization
92
+ process.stdout.write(` ${chalk_1.default.yellow("④")} GET ${new URL(url).pathname} + Authorization → `);
93
+ let res2;
94
+ try {
95
+ res2 = await fetch(url, {
96
+ headers: { Authorization: authHeader },
97
+ });
81
98
  }
82
- const credential = (0, intmax402_core_1.parseAuthorization)(header);
83
- if (!credential) {
84
- console.error("Failed to parse Authorization header.");
99
+ catch (err) {
100
+ console.log(chalk_1.default.red(`error: ${err.message}`));
85
101
  process.exit(1);
86
102
  }
87
- console.log("Parsed credential:");
88
- console.log(` Address: ${credential.address}`);
89
- console.log(` Nonce: ${credential.nonce}`);
90
- console.log(` Signature: ${credential.signature.slice(0, 20)}...`);
91
- if (credential.txHash) {
92
- console.log(` TX Hash: ${credential.txHash}`);
103
+ const elapsed = Date.now() - startTime;
104
+ if (res2.ok) {
105
+ console.log(`${chalk_1.default.green(String(res2.status))} ${chalk_1.default.green("✅")}`);
106
+ }
107
+ else {
108
+ console.log(`${chalk_1.default.red(String(res2.status))} ${chalk_1.default.red("❌")}`);
109
+ const body = await res2.text().catch(() => "");
110
+ if (body)
111
+ console.log(chalk_1.default.dim(` ${body.slice(0, 200)}`));
112
+ }
113
+ console.log();
114
+ console.log(`${chalk_1.default.bold("Address:")} ${address}`);
115
+ console.log(`${chalk_1.default.bold("Time:")} ${elapsed}ms`);
116
+ if (!res2.ok) {
117
+ process.exit(1);
93
118
  }
94
119
  }
95
- function printUsage() {
96
- console.log("intmax402 CLI - HTTP 402 Payment Gate Test Tool");
97
- console.log("");
98
- console.log("Usage:");
99
- console.log(" intmax402 test <url> Test 402 flow against a URL");
100
- console.log(" intmax402 keygen Generate a test ETH keypair");
101
- console.log(" intmax402 verify <header> Parse an Authorization header");
120
+ function keygenCommand() {
121
+ const wallet = ethers_1.ethers.Wallet.createRandom();
122
+ console.log("\nGenerated wallet:");
123
+ console.log(` ${chalk_1.default.bold("Address:")} ${chalk_1.default.green(wallet.address)}`);
124
+ console.log(` ${chalk_1.default.bold("Private Key:")} ${chalk_1.default.yellow(wallet.privateKey)}`);
125
+ console.log();
126
+ console.log(chalk_1.default.red(" Use for testing only. Never use in production."));
127
+ }
128
+ function printHelp() {
129
+ console.log(`${chalk_1.default.bold("intmax402")} - HTTP 402 Payment Gate CLI Tool`);
130
+ console.log();
131
+ console.log(chalk_1.default.bold("Usage:"));
132
+ console.log(` intmax402 ${chalk_1.default.cyan("test")} <url> [--mode identity|payment]`);
133
+ console.log(` Test 402 flow against a URL`);
134
+ console.log(` intmax402 ${chalk_1.default.cyan("keygen")} Generate a test Ethereum wallet`);
135
+ console.log(` intmax402 ${chalk_1.default.cyan("--help")} Show this help message`);
136
+ console.log();
137
+ console.log(chalk_1.default.bold("Examples:"));
138
+ console.log(` intmax402 test http://localhost:3760/identity`);
139
+ console.log(` intmax402 test http://localhost:3760/paid --mode payment`);
140
+ console.log(` intmax402 keygen`);
102
141
  }
103
142
  main().catch((err) => {
104
- console.error("Error:", err.message);
143
+ console.error(chalk_1.default.red("Error:"), err.message);
105
144
  process.exit(1);
106
145
  });
package/package.json CHANGED
@@ -1,17 +1,21 @@
1
1
  {
2
2
  "name": "@tanakayuto/intmax402-cli",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "intmax402": "dist/index.js"
7
7
  },
8
8
  "dependencies": {
9
- "@tanakayuto/intmax402-core": "0.1.2",
10
- "@tanakayuto/intmax402-client": "0.1.2"
9
+ "@tanakayuto/intmax402-core": "0.2.0",
10
+ "chalk": "^4.1.2",
11
+ "ethers": "^6.16.0",
12
+ "minimist": "^1.2.8"
11
13
  },
12
14
  "devDependencies": {
13
- "typescript": "^5.4.0",
14
- "@types/node": "^20.0.0"
15
+ "@types/chalk": "^2.2.4",
16
+ "@types/minimist": "^1.2.5",
17
+ "@types/node": "^20.0.0",
18
+ "typescript": "^5.4.0"
15
19
  },
16
20
  "files": [
17
21
  "dist",