@x402r/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +128 -0
- package/dist/bin/x402r.d.ts +13 -0
- package/dist/bin/x402r.js +39 -0
- package/dist/bin/x402r.js.map +1 -0
- package/dist/src/commands/config.d.ts +5 -0
- package/dist/src/commands/config.js +54 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/dispute.d.ts +5 -0
- package/dist/src/commands/dispute.js +121 -0
- package/dist/src/commands/dispute.js.map +1 -0
- package/dist/src/commands/list.d.ts +5 -0
- package/dist/src/commands/list.js +46 -0
- package/dist/src/commands/list.js.map +1 -0
- package/dist/src/commands/show.d.ts +5 -0
- package/dist/src/commands/show.js +56 -0
- package/dist/src/commands/show.js.map +1 -0
- package/dist/src/commands/status.d.ts +5 -0
- package/dist/src/commands/status.js +62 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/verify.d.ts +5 -0
- package/dist/src/commands/verify.js +60 -0
- package/dist/src/commands/verify.js.map +1 -0
- package/dist/src/config.d.ts +29 -0
- package/dist/src/config.js +71 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/ipfs.d.ts +9 -0
- package/dist/src/ipfs.js +41 -0
- package/dist/src/ipfs.js.map +1 -0
- package/dist/src/setup.d.ts +24 -0
- package/dist/src/setup.js +72 -0
- package/dist/src/setup.js.map +1 -0
- package/dist/src/state.d.ts +44 -0
- package/dist/src/state.js +113 -0
- package/dist/src/state.js.map +1 -0
- package/package.json +40 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: x402r-dispute
|
|
3
|
+
description: File and track payment disputes on the x402r refundable payments protocol
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
author: x402r
|
|
6
|
+
tags: [x402r, payments, disputes, web3, arbitration]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# x402r Dispute Resolution CLI
|
|
10
|
+
|
|
11
|
+
You help users file and track payment disputes on the x402r protocol. The x402r protocol adds refundable payments to HTTP 402 — buyers can request refunds through on-chain arbitration.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
The CLI is available via npx (no install needed):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @x402r/cli <command>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install globally:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @x402r/cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## First-Time Setup
|
|
28
|
+
|
|
29
|
+
Before using any commands, configure the CLI with the user's wallet and operator:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
x402r config --key <private-key> --operator <operator-address> --arbiter-url <arbiter-server-url>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `--key`: The user's Ethereum private key (0x-prefixed). Stored in `~/.x402r/config.json`.
|
|
36
|
+
- `--operator`: The PaymentOperator contract address for the marketplace.
|
|
37
|
+
- `--arbiter-url`: URL of the arbiter server (e.g., `https://arbiter.example.com`). Defaults to `http://localhost:3000`.
|
|
38
|
+
- `--network`: Network ID in EIP-155 format (default: `eip155:84532` for Base Sepolia).
|
|
39
|
+
- `--rpc`: Custom RPC URL (optional).
|
|
40
|
+
|
|
41
|
+
To view current config: `x402r config`
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
### File a Dispute
|
|
46
|
+
|
|
47
|
+
Creates an on-chain refund request and submits evidence in one step:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
x402r dispute "Service was not delivered as promised" --evidence "Paid for API access but received 503 errors for 3 hours"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Options:
|
|
54
|
+
- First argument (required): The reason for the dispute
|
|
55
|
+
- `-e, --evidence <text>`: Additional evidence text
|
|
56
|
+
- `-f, --file <path>`: Path to a JSON file with structured evidence
|
|
57
|
+
- `-p, --payment-json <json>`: Payment info JSON (uses saved state from last payment if omitted)
|
|
58
|
+
- `-n, --nonce <nonce>`: Nonce for the refund request (default: 0)
|
|
59
|
+
- `-a, --amount <amount>`: Refund amount in token units (default: full payment amount)
|
|
60
|
+
|
|
61
|
+
The command saves dispute state to `~/.x402r/last-dispute.json` so subsequent commands can reference it.
|
|
62
|
+
|
|
63
|
+
### Check Dispute Status
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
x402r status
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
- `--id <compositeKey>`: Look up by composite key
|
|
71
|
+
- `-p, --payment-json <json>`: Payment info JSON
|
|
72
|
+
- `-n, --nonce <nonce>`: Nonce
|
|
73
|
+
|
|
74
|
+
Tries the arbiter server first, falls back to on-chain query. Returns: Pending, Approved, Denied, or Cancelled.
|
|
75
|
+
|
|
76
|
+
### List Disputes
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
x402r list
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
- `-r, --receiver <address>`: Filter by receiver address
|
|
84
|
+
- `--offset <n>`: Pagination offset (default: 0)
|
|
85
|
+
- `--count <n>`: Number of results (default: 20)
|
|
86
|
+
|
|
87
|
+
Lists disputes from the arbiter server with pagination.
|
|
88
|
+
|
|
89
|
+
### View Evidence
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
x402r show
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Shows all evidence entries (payer, merchant, arbiter) for a dispute. Each entry shows: role, submitter address, timestamp, and CID.
|
|
96
|
+
|
|
97
|
+
Options:
|
|
98
|
+
- `-p, --payment-json <json>`: Payment info JSON
|
|
99
|
+
- `-n, --nonce <nonce>`: Nonce
|
|
100
|
+
|
|
101
|
+
### Verify Arbiter Ruling
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
x402r verify
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Replays the arbiter's AI evaluation to verify the commitment hash matches. Shows:
|
|
108
|
+
- Commitment hash, prompt hash, response hash, seed
|
|
109
|
+
- The AI's decision, confidence, and reasoning
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
- `-p, --payment-json <json>`: Payment info JSON
|
|
113
|
+
- `-n, --nonce <nonce>`: Nonce
|
|
114
|
+
|
|
115
|
+
## Typical Workflow
|
|
116
|
+
|
|
117
|
+
1. User makes an HTTP 402 payment and receives poor service
|
|
118
|
+
2. `x402r dispute "reason" --evidence "details"` — files the dispute
|
|
119
|
+
3. `x402r status` — checks if the arbiter has ruled
|
|
120
|
+
4. `x402r show` — views all evidence from both parties and the arbiter
|
|
121
|
+
5. `x402r verify` — verifies the AI ruling was deterministic
|
|
122
|
+
|
|
123
|
+
## Important Notes
|
|
124
|
+
|
|
125
|
+
- The CLI saves state between commands. After `dispute`, you can run `status`, `show`, `verify` without re-specifying payment info.
|
|
126
|
+
- Evidence can be stored on IPFS (if Pinata keys are configured) or inline as JSON strings.
|
|
127
|
+
- The `verify` command requires the arbiter server to be running — it replays the AI evaluation server-side.
|
|
128
|
+
- All on-chain operations require ETH for gas on the configured network.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* x402r CLI — Agent-friendly dispute resolution tool
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* x402r config --key 0x... --operator 0x...
|
|
7
|
+
* x402r dispute "reason" --evidence "details"
|
|
8
|
+
* x402r status
|
|
9
|
+
* x402r verify
|
|
10
|
+
* x402r list
|
|
11
|
+
* x402r show
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* x402r CLI — Agent-friendly dispute resolution tool
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* x402r config --key 0x... --operator 0x...
|
|
7
|
+
* x402r dispute "reason" --evidence "details"
|
|
8
|
+
* x402r status
|
|
9
|
+
* x402r verify
|
|
10
|
+
* x402r list
|
|
11
|
+
* x402r show
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
import { config as dotenvConfig } from "dotenv";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
import { dirname, join } from "path";
|
|
17
|
+
import { registerConfigCommand } from "../src/commands/config.js";
|
|
18
|
+
import { registerDisputeCommand } from "../src/commands/dispute.js";
|
|
19
|
+
import { registerStatusCommand } from "../src/commands/status.js";
|
|
20
|
+
import { registerVerifyCommand } from "../src/commands/verify.js";
|
|
21
|
+
import { registerListCommand } from "../src/commands/list.js";
|
|
22
|
+
import { registerShowCommand } from "../src/commands/show.js";
|
|
23
|
+
// Load .env from cli/ directory
|
|
24
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
25
|
+
const __dirname = dirname(__filename);
|
|
26
|
+
dotenvConfig({ path: join(__dirname, "..", ".env") });
|
|
27
|
+
const program = new Command();
|
|
28
|
+
program
|
|
29
|
+
.name("x402r")
|
|
30
|
+
.description("Agent-friendly CLI for x402r dispute resolution")
|
|
31
|
+
.version("0.1.0");
|
|
32
|
+
registerConfigCommand(program);
|
|
33
|
+
registerDisputeCommand(program);
|
|
34
|
+
registerStatusCommand(program);
|
|
35
|
+
registerVerifyCommand(program);
|
|
36
|
+
registerListCommand(program);
|
|
37
|
+
registerShowCommand(program);
|
|
38
|
+
program.parse();
|
|
39
|
+
//# sourceMappingURL=x402r.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402r.js","sourceRoot":"","sources":["../../bin/x402r.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,gCAAgC;AAChC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AAEtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,iDAAiD,CAAC;KAC9D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config command — Save/load CLI configuration
|
|
3
|
+
*/
|
|
4
|
+
import { saveConfigFile, printConfig } from "../config.js";
|
|
5
|
+
export function registerConfigCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("config")
|
|
8
|
+
.description("Save or view CLI configuration")
|
|
9
|
+
.option("-k, --key <privateKey>", "Set private key")
|
|
10
|
+
.option("-o, --operator <address>", "Set operator address")
|
|
11
|
+
.option("-a, --arbiter-url <url>", "Set arbiter server URL")
|
|
12
|
+
.option("-n, --network <networkId>", "Set network ID (e.g., eip155:84532)")
|
|
13
|
+
.option("-r, --rpc <url>", "Set RPC URL")
|
|
14
|
+
.option("--pinata-key <key>", "Set Pinata API key")
|
|
15
|
+
.option("--pinata-secret <secret>", "Set Pinata secret key")
|
|
16
|
+
.action((options) => {
|
|
17
|
+
const updates = {};
|
|
18
|
+
let hasUpdates = false;
|
|
19
|
+
if (options.key) {
|
|
20
|
+
updates.privateKey = options.key;
|
|
21
|
+
hasUpdates = true;
|
|
22
|
+
}
|
|
23
|
+
if (options.operator) {
|
|
24
|
+
updates.operatorAddress = options.operator;
|
|
25
|
+
hasUpdates = true;
|
|
26
|
+
}
|
|
27
|
+
if (options.arbiterUrl) {
|
|
28
|
+
updates.arbiterUrl = options.arbiterUrl;
|
|
29
|
+
hasUpdates = true;
|
|
30
|
+
}
|
|
31
|
+
if (options.network) {
|
|
32
|
+
updates.networkId = options.network;
|
|
33
|
+
hasUpdates = true;
|
|
34
|
+
}
|
|
35
|
+
if (options.rpc) {
|
|
36
|
+
updates.rpcUrl = options.rpc;
|
|
37
|
+
hasUpdates = true;
|
|
38
|
+
}
|
|
39
|
+
if (options.pinataKey) {
|
|
40
|
+
updates.pinataApiKey = options.pinataKey;
|
|
41
|
+
hasUpdates = true;
|
|
42
|
+
}
|
|
43
|
+
if (options.pinataSecret) {
|
|
44
|
+
updates.pinataSecretKey = options.pinataSecret;
|
|
45
|
+
hasUpdates = true;
|
|
46
|
+
}
|
|
47
|
+
if (hasUpdates) {
|
|
48
|
+
saveConfigFile(updates);
|
|
49
|
+
console.log("Config updated.");
|
|
50
|
+
}
|
|
51
|
+
printConfig();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAsB,MAAM,cAAc,CAAC;AAE/E,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,wBAAwB,EAAE,iBAAiB,CAAC;SACnD,MAAM,CAAC,0BAA0B,EAAE,sBAAsB,CAAC;SAC1D,MAAM,CAAC,yBAAyB,EAAE,wBAAwB,CAAC;SAC3D,MAAM,CAAC,2BAA2B,EAAE,qCAAqC,CAAC;SAC1E,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;SACxC,MAAM,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;SAClD,MAAM,CAAC,0BAA0B,EAAE,uBAAuB,CAAC;SAC3D,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAClB,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,IAAI,UAAU,GAAG,KAAK,CAAC;QAEvB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC;YACjC,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO,CAAC,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;YAC3C,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;YACxC,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;YACpC,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;YAC7B,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;YACzC,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,CAAC,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC;YAC/C,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QAED,IAAI,UAAU,EAAE,CAAC;YACf,cAAc,CAAC,OAAO,CAAC,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACjC,CAAC;QAED,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dispute command — All-in-one: requestRefund + pin evidence + submitEvidence
|
|
3
|
+
*/
|
|
4
|
+
import { X402rClient } from "@x402r/client";
|
|
5
|
+
import { initCli } from "../setup.js";
|
|
6
|
+
import { getPaymentInfo, saveDisputeState } from "../state.js";
|
|
7
|
+
import { pinToIpfs } from "../ipfs.js";
|
|
8
|
+
export function registerDisputeCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("dispute")
|
|
11
|
+
.description("Create a dispute for the last payment (requestRefund + submit evidence)")
|
|
12
|
+
.argument("<reason>", "Reason for the dispute")
|
|
13
|
+
.option("-e, --evidence <text>", "Additional evidence text")
|
|
14
|
+
.option("-f, --file <path>", "Path to evidence file (JSON)")
|
|
15
|
+
.option("-p, --payment-json <json>", "Payment info JSON (uses saved state if omitted)")
|
|
16
|
+
.option("-n, --nonce <nonce>", "Nonce (default: 0)", "0")
|
|
17
|
+
.option("-a, --amount <amount>", "Refund amount (default: full payment amount)")
|
|
18
|
+
.action(async (reason, options) => {
|
|
19
|
+
const { publicClient, walletClient, addresses, operatorAddress } = initCli();
|
|
20
|
+
const paymentInfo = getPaymentInfo(options);
|
|
21
|
+
const nonce = BigInt(options.nonce);
|
|
22
|
+
const amount = options.amount ? BigInt(options.amount) : paymentInfo.maxAmount;
|
|
23
|
+
console.log("\n=== Creating Dispute ===");
|
|
24
|
+
console.log(" Reason:", reason);
|
|
25
|
+
console.log(" Amount:", amount.toString());
|
|
26
|
+
console.log(" Nonce:", nonce.toString());
|
|
27
|
+
const client = new X402rClient({
|
|
28
|
+
publicClient: publicClient,
|
|
29
|
+
walletClient: walletClient,
|
|
30
|
+
operatorAddress,
|
|
31
|
+
refundRequestAddress: addresses.refundRequestAddress,
|
|
32
|
+
refundRequestEvidenceAddress: addresses.evidenceAddress,
|
|
33
|
+
chainId: addresses.chainId,
|
|
34
|
+
});
|
|
35
|
+
// Step 1: Request refund on-chain
|
|
36
|
+
console.log("\n[1/3] Requesting refund on-chain...");
|
|
37
|
+
let refundTxHash;
|
|
38
|
+
try {
|
|
39
|
+
const hasRequest = await client.hasRefundRequest(paymentInfo, nonce);
|
|
40
|
+
if (hasRequest) {
|
|
41
|
+
const status = await client.getRefundStatus(paymentInfo, nonce);
|
|
42
|
+
const statusNames = ["Pending", "Approved", "Denied", "Cancelled"];
|
|
43
|
+
console.log(` Refund request already exists (status: ${statusNames[status] || status})`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const { txHash } = await client.requestRefund(paymentInfo, amount, nonce);
|
|
47
|
+
refundTxHash = txHash;
|
|
48
|
+
console.log(" Refund requested:", txHash);
|
|
49
|
+
console.log(" Waiting for confirmation...");
|
|
50
|
+
await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
51
|
+
console.log(" Confirmed.");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(" Failed to request refund:", error instanceof Error ? error.message : error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// Step 2: Build and pin evidence
|
|
59
|
+
console.log("\n[2/3] Building and pinning evidence...");
|
|
60
|
+
let fileContent;
|
|
61
|
+
if (options.file) {
|
|
62
|
+
try {
|
|
63
|
+
const { readFileSync } = await import("fs");
|
|
64
|
+
fileContent = JSON.parse(readFileSync(options.file, "utf-8"));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error(" Failed to read evidence file:", error instanceof Error ? error.message : error);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const evidenceData = {
|
|
72
|
+
reason,
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
payer: paymentInfo.payer,
|
|
75
|
+
receiver: paymentInfo.receiver,
|
|
76
|
+
};
|
|
77
|
+
if (options.evidence) {
|
|
78
|
+
evidenceData.evidence = options.evidence;
|
|
79
|
+
}
|
|
80
|
+
if (fileContent) {
|
|
81
|
+
evidenceData.attachments = fileContent;
|
|
82
|
+
}
|
|
83
|
+
let cid;
|
|
84
|
+
try {
|
|
85
|
+
cid = await pinToIpfs(evidenceData);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error(" Failed to pin evidence:", error instanceof Error ? error.message : error);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
// Step 3: Submit evidence on-chain
|
|
92
|
+
console.log("\n[3/3] Submitting evidence on-chain...");
|
|
93
|
+
let evidenceTxHash;
|
|
94
|
+
try {
|
|
95
|
+
const { txHash } = await client.submitEvidence(paymentInfo, nonce, cid);
|
|
96
|
+
evidenceTxHash = txHash;
|
|
97
|
+
console.log(" Evidence submitted:", txHash);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error(" Failed to submit evidence:", error instanceof Error ? error.message : error);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
// Save dispute state
|
|
104
|
+
saveDisputeState({
|
|
105
|
+
nonce: nonce.toString(),
|
|
106
|
+
refundTxHash,
|
|
107
|
+
evidenceTxHash,
|
|
108
|
+
evidenceCid: cid,
|
|
109
|
+
timestamp: new Date().toISOString(),
|
|
110
|
+
});
|
|
111
|
+
console.log("\n=== Dispute Created ===");
|
|
112
|
+
if (refundTxHash)
|
|
113
|
+
console.log(" Refund Tx:", refundTxHash);
|
|
114
|
+
if (evidenceTxHash)
|
|
115
|
+
console.log(" Evidence Tx:", evidenceTxHash);
|
|
116
|
+
console.log(" Evidence CID:", cid);
|
|
117
|
+
console.log("\n State saved to ~/.x402r/last-dispute.json");
|
|
118
|
+
console.log(" Run 'x402r status' to check dispute status");
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=dispute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispute.js","sourceRoot":"","sources":["../../../src/commands/dispute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list command — List disputes via arbiter server or SDK
|
|
3
|
+
*/
|
|
4
|
+
import { getConfig } from "../config.js";
|
|
5
|
+
export function registerListCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("list")
|
|
8
|
+
.description("List pending disputes")
|
|
9
|
+
.option("-r, --receiver <address>", "Filter by receiver address")
|
|
10
|
+
.option("--offset <n>", "Offset for pagination", "0")
|
|
11
|
+
.option("--count <n>", "Number of results", "20")
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
const config = getConfig();
|
|
14
|
+
const url = config.arbiterUrl;
|
|
15
|
+
console.log(`\nQuerying disputes from ${url}...`);
|
|
16
|
+
const params = new URLSearchParams();
|
|
17
|
+
if (options.receiver)
|
|
18
|
+
params.set("receiver", options.receiver);
|
|
19
|
+
params.set("offset", options.offset);
|
|
20
|
+
params.set("count", options.count);
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(`${url}/api/disputes?${params.toString()}`);
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
const error = await response.text();
|
|
25
|
+
console.error(`\nArbiter returned ${response.status}:`, error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
console.log(`\n=== Disputes (${data.keys.length} of ${data.total}) ===`);
|
|
30
|
+
if (data.keys.length === 0) {
|
|
31
|
+
console.log(" No pending disputes found.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (let i = 0; i < data.keys.length; i++) {
|
|
35
|
+
console.log(` [${parseInt(data.offset) + i}] ${data.keys[i]}`);
|
|
36
|
+
}
|
|
37
|
+
console.log(`\n Showing ${data.offset}-${parseInt(data.offset) + data.keys.length} of ${data.total}`);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error("\nFailed to list disputes:", error instanceof Error ? error.message : error);
|
|
41
|
+
console.error("Is the arbiter server running at", url, "?");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,0BAA0B,EAAE,4BAA4B,CAAC;SAChE,MAAM,CAAC,cAAc,EAAE,uBAAuB,EAAE,GAAG,CAAC;SACpD,MAAM,CAAC,aAAa,EAAE,mBAAmB,EAAE,IAAI,CAAC;SAChD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;QAE9B,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,KAAK,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAEnC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,iBAAiB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAEzE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,CAAC,MAAM,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAK/B,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,CAAC,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;YAEzE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACzG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC5F,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* show command — Show evidence for a dispute
|
|
3
|
+
*/
|
|
4
|
+
import { X402rClient } from "@x402r/client";
|
|
5
|
+
import { SubmitterRole } from "@x402r/core";
|
|
6
|
+
import { initReadOnly } from "../setup.js";
|
|
7
|
+
import { getPaymentInfo, getNonce } from "../state.js";
|
|
8
|
+
function shortAddress(address) {
|
|
9
|
+
return `${address.slice(0, 10)}...${address.slice(-8)}`;
|
|
10
|
+
}
|
|
11
|
+
function roleName(role) {
|
|
12
|
+
switch (role) {
|
|
13
|
+
case SubmitterRole.Payer:
|
|
14
|
+
return "Payer";
|
|
15
|
+
case SubmitterRole.Receiver:
|
|
16
|
+
return "Receiver";
|
|
17
|
+
case SubmitterRole.Arbiter:
|
|
18
|
+
return "Arbiter";
|
|
19
|
+
default:
|
|
20
|
+
return `Unknown(${role})`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function formatEvidence(evidence, index) {
|
|
24
|
+
const ts = new Date(Number(evidence.timestamp) * 1000).toISOString();
|
|
25
|
+
return ` [${index}] ${roleName(evidence.role)} ${shortAddress(evidence.submitter)} | ${ts} | CID: ${evidence.cid}`;
|
|
26
|
+
}
|
|
27
|
+
export function registerShowCommand(program) {
|
|
28
|
+
program
|
|
29
|
+
.command("show")
|
|
30
|
+
.description("Show all evidence for a dispute")
|
|
31
|
+
.option("-p, --payment-json <json>", "Payment info JSON (uses saved state if omitted)")
|
|
32
|
+
.option("-n, --nonce <nonce>", "Nonce")
|
|
33
|
+
.action(async (options) => {
|
|
34
|
+
const { publicClient, addresses, operatorAddress } = initReadOnly();
|
|
35
|
+
const paymentInfo = getPaymentInfo(options);
|
|
36
|
+
const nonce = getNonce(options);
|
|
37
|
+
const client = new X402rClient({
|
|
38
|
+
publicClient: publicClient,
|
|
39
|
+
operatorAddress,
|
|
40
|
+
refundRequestEvidenceAddress: addresses.evidenceAddress,
|
|
41
|
+
chainId: addresses.chainId,
|
|
42
|
+
});
|
|
43
|
+
console.log("\nFetching evidence...");
|
|
44
|
+
const count = await client.getEvidenceCount(paymentInfo, nonce);
|
|
45
|
+
console.log(`\n=== Evidence (${count} entries) ===`);
|
|
46
|
+
if (count === 0n) {
|
|
47
|
+
console.log(" No evidence submitted.");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const entries = await client.getAllEvidence(paymentInfo, nonce);
|
|
51
|
+
for (let i = 0; i < entries.length; i++) {
|
|
52
|
+
console.log(formatEvidence(entries[i], i));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.js","sourceRoot":"","sources":["../../../src/commands/show.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAiB,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvD,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,QAAQ,CAAC,IAAmB;IACnC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa,CAAC,KAAK;YACtB,OAAO,OAAO,CAAC;QACjB,KAAK,aAAa,CAAC,QAAQ;YACzB,OAAO,UAAU,CAAC;QACpB,KAAK,aAAa,CAAC,OAAO;YACxB,OAAO,SAAS,CAAC;QACnB;YACE,OAAO,WAAW,IAAI,GAAG,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAkB,EAAE,KAAa;IACvD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACrE,OAAO,MAAM,KAAK,KAAK,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,QAAQ,CAAC,GAAG,EAAE,CAAC;AACtH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,iCAAiC,CAAC;SAC9C,MAAM,CAAC,2BAA2B,EAAE,iDAAiD,CAAC;SACtF,MAAM,CAAC,qBAAqB,EAAE,OAAO,CAAC;SACtC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,YAAY,EAAE,CAAC;QACpE,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEhC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,YAAY,EAAE,YAAmB;YACjC,eAAe;YACf,4BAA4B,EAAE,SAAS,CAAC,eAAe;YACvD,OAAO,EAAE,SAAS,CAAC,OAAO;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAEtC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAChE,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,eAAe,CAAC,CAAC;QAErD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YACxC,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAChE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* status command — Check dispute status via arbiter server or SDK
|
|
3
|
+
*/
|
|
4
|
+
import { X402rClient } from "@x402r/client";
|
|
5
|
+
import { initReadOnly } from "../setup.js";
|
|
6
|
+
import { getPaymentInfo, getNonce, getCompositeKey } from "../state.js";
|
|
7
|
+
import { getConfig } from "../config.js";
|
|
8
|
+
export function registerStatusCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("status")
|
|
11
|
+
.description("Check the status of a dispute")
|
|
12
|
+
.option("--id <compositeKey>", "Composite key of the dispute")
|
|
13
|
+
.option("-p, --payment-json <json>", "Payment info JSON (uses saved state if omitted)")
|
|
14
|
+
.option("-n, --nonce <nonce>", "Nonce")
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
const config = getConfig();
|
|
17
|
+
const compositeKey = getCompositeKey(options);
|
|
18
|
+
// Try arbiter server first if composite key available
|
|
19
|
+
if (compositeKey && config.arbiterUrl) {
|
|
20
|
+
try {
|
|
21
|
+
console.log(`\nQuerying arbiter at ${config.arbiterUrl}...`);
|
|
22
|
+
const response = await fetch(`${config.arbiterUrl}/api/dispute/${compositeKey}`);
|
|
23
|
+
if (response.ok) {
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
const statusNames = { 0: "Pending", 1: "Approved", 2: "Denied", 3: "Cancelled" };
|
|
26
|
+
console.log("\n=== Dispute Status ===");
|
|
27
|
+
console.log(" Composite Key:", compositeKey);
|
|
28
|
+
console.log(" Status:", statusNames[data.status] || data.status);
|
|
29
|
+
console.log(" Amount:", data.amount);
|
|
30
|
+
console.log(" Nonce:", data.nonce);
|
|
31
|
+
console.log(" Payment Info Hash:", data.paymentInfoHash);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log(" Arbiter returned", response.status, "— falling back to on-chain");
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
console.log(" Arbiter unavailable — falling back to on-chain");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Fall back to on-chain query
|
|
41
|
+
const { publicClient, addresses, operatorAddress } = initReadOnly();
|
|
42
|
+
const paymentInfo = getPaymentInfo(options);
|
|
43
|
+
const nonce = getNonce(options);
|
|
44
|
+
const client = new X402rClient({
|
|
45
|
+
publicClient: publicClient,
|
|
46
|
+
operatorAddress,
|
|
47
|
+
refundRequestAddress: addresses.refundRequestAddress,
|
|
48
|
+
chainId: addresses.chainId,
|
|
49
|
+
});
|
|
50
|
+
const hasRequest = await client.hasRefundRequest(paymentInfo, nonce);
|
|
51
|
+
if (!hasRequest) {
|
|
52
|
+
console.log("\nNo refund request found for this payment (nonce:", nonce.toString(), ")");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const status = await client.getRefundStatus(paymentInfo, nonce);
|
|
56
|
+
const statusNames = ["Pending", "Approved", "Denied", "Cancelled"];
|
|
57
|
+
console.log("\n=== Dispute Status (on-chain) ===");
|
|
58
|
+
console.log(" Status:", statusNames[status] || status);
|
|
59
|
+
console.log(" Nonce:", nonce.toString());
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify command — Replay arbiter evaluation and verify commitment hashes
|
|
3
|
+
*/
|
|
4
|
+
import { initReadOnly } from "../setup.js";
|
|
5
|
+
import { getPaymentInfo, getNonce } from "../state.js";
|
|
6
|
+
import { getConfig } from "../config.js";
|
|
7
|
+
export function registerVerifyCommand(program) {
|
|
8
|
+
program
|
|
9
|
+
.command("verify")
|
|
10
|
+
.description("Verify arbiter ruling by replaying AI evaluation")
|
|
11
|
+
.option("-p, --payment-json <json>", "Payment info JSON (uses saved state if omitted)")
|
|
12
|
+
.option("-n, --nonce <nonce>", "Nonce")
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
const { arbiterUrl } = initReadOnly();
|
|
16
|
+
const paymentInfo = getPaymentInfo(options);
|
|
17
|
+
const nonce = getNonce(options);
|
|
18
|
+
const url = config.arbiterUrl || arbiterUrl;
|
|
19
|
+
console.log(`\nVerifying dispute via ${url}...`);
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(`${url}/api/verify`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: { "Content-Type": "application/json" },
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
paymentInfo: JSON.parse(JSON.stringify(paymentInfo, (_, v) => (typeof v === "bigint" ? v.toString() : v))),
|
|
26
|
+
nonce: nonce.toString(),
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const error = await response.text();
|
|
31
|
+
console.error(`\nArbiter returned ${response.status}:`, error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
console.log("\n=== Verification Result ===");
|
|
36
|
+
console.log("\n Commitment Hash:", data.replayCommitment.commitmentHash);
|
|
37
|
+
console.log(" Prompt Hash:", data.replayCommitment.promptHash);
|
|
38
|
+
console.log(" Response Hash:", data.replayCommitment.responseHash);
|
|
39
|
+
console.log(" Seed:", data.replayCommitment.seed);
|
|
40
|
+
// Try to parse the AI response
|
|
41
|
+
try {
|
|
42
|
+
const decision = JSON.parse(data.displayContent);
|
|
43
|
+
console.log("\n=== AI Decision (Replay) ===");
|
|
44
|
+
console.log(" Decision:", decision.decision);
|
|
45
|
+
console.log(" Confidence:", decision.confidence);
|
|
46
|
+
console.log(" Reasoning:", decision.reasoning);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
console.log("\n Raw Response:", data.displayContent);
|
|
50
|
+
}
|
|
51
|
+
console.log("\n Note:", data.note);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error("\nFailed to verify:", error instanceof Error ? error.message : error);
|
|
55
|
+
console.error("Is the arbiter server running at", url, "?");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/commands/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI configuration — loads from ~/.x402r/config.json, .env, and env vars.
|
|
3
|
+
* Priority: env vars > .env > config file > defaults
|
|
4
|
+
*/
|
|
5
|
+
export interface CliConfigFile {
|
|
6
|
+
privateKey?: string;
|
|
7
|
+
operatorAddress?: string;
|
|
8
|
+
arbiterUrl?: string;
|
|
9
|
+
networkId?: string;
|
|
10
|
+
rpcUrl?: string;
|
|
11
|
+
pinataApiKey?: string;
|
|
12
|
+
pinataSecretKey?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load config from ~/.x402r/config.json
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadConfigFile(): CliConfigFile;
|
|
18
|
+
/**
|
|
19
|
+
* Save config to ~/.x402r/config.json
|
|
20
|
+
*/
|
|
21
|
+
export declare function saveConfigFile(config: CliConfigFile): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get resolved config: env vars > .env > config file > defaults
|
|
24
|
+
*/
|
|
25
|
+
export declare function getConfig(): Required<Pick<CliConfigFile, "networkId" | "arbiterUrl">> & CliConfigFile;
|
|
26
|
+
/**
|
|
27
|
+
* Print current config (masked key)
|
|
28
|
+
*/
|
|
29
|
+
export declare function printConfig(): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI configuration — loads from ~/.x402r/config.json, .env, and env vars.
|
|
3
|
+
* Priority: env vars > .env > config file > defaults
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
const CONFIG_DIR = path.join(os.homedir(), ".x402r");
|
|
9
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
10
|
+
/**
|
|
11
|
+
* Load config from ~/.x402r/config.json
|
|
12
|
+
*/
|
|
13
|
+
export function loadConfigFile() {
|
|
14
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Save config to ~/.x402r/config.json
|
|
26
|
+
*/
|
|
27
|
+
export function saveConfigFile(config) {
|
|
28
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
29
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
// Merge with existing config
|
|
32
|
+
const existing = loadConfigFile();
|
|
33
|
+
const merged = { ...existing, ...config };
|
|
34
|
+
// Remove undefined/null values
|
|
35
|
+
for (const key of Object.keys(merged)) {
|
|
36
|
+
if (merged[key] === undefined || merged[key] === null) {
|
|
37
|
+
delete merged[key];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get resolved config: env vars > .env > config file > defaults
|
|
44
|
+
*/
|
|
45
|
+
export function getConfig() {
|
|
46
|
+
const file = loadConfigFile();
|
|
47
|
+
return {
|
|
48
|
+
privateKey: process.env.PRIVATE_KEY || file.privateKey,
|
|
49
|
+
operatorAddress: process.env.OPERATOR_ADDRESS || file.operatorAddress,
|
|
50
|
+
arbiterUrl: process.env.ARBITER_URL || file.arbiterUrl || "http://localhost:3000",
|
|
51
|
+
networkId: process.env.NETWORK_ID || file.networkId || "eip155:84532",
|
|
52
|
+
rpcUrl: process.env.RPC_URL || file.rpcUrl || "https://sepolia.base.org",
|
|
53
|
+
pinataApiKey: process.env.PINATA_API_KEY || file.pinataApiKey,
|
|
54
|
+
pinataSecretKey: process.env.PINATA_SECRET_KEY || file.pinataSecretKey,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Print current config (masked key)
|
|
59
|
+
*/
|
|
60
|
+
export function printConfig() {
|
|
61
|
+
const config = getConfig();
|
|
62
|
+
console.log("\n=== x402r CLI Config ===");
|
|
63
|
+
console.log(" Private Key:", config.privateKey ? `${config.privateKey.slice(0, 6)}...${config.privateKey.slice(-4)}` : "(not set)");
|
|
64
|
+
console.log(" Operator:", config.operatorAddress || "(not set)");
|
|
65
|
+
console.log(" Arbiter URL:", config.arbiterUrl);
|
|
66
|
+
console.log(" Network:", config.networkId);
|
|
67
|
+
console.log(" RPC URL:", config.rpcUrl);
|
|
68
|
+
console.log(" Pinata API Key:", config.pinataApiKey ? `${config.pinataApiKey.slice(0, 8)}...` : "(not set)");
|
|
69
|
+
console.log(`\n Config file: ${CONFIG_FILE}`);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPFS pinning via Pinata API.
|
|
3
|
+
* Falls back to inline string if no Pinata key configured.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Pin JSON to IPFS via Pinata.
|
|
7
|
+
* Returns IPFS CID if Pinata configured, otherwise returns JSON string directly.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pinToIpfs(data: Record<string, unknown>): Promise<string>;
|
package/dist/src/ipfs.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPFS pinning via Pinata API.
|
|
3
|
+
* Falls back to inline string if no Pinata key configured.
|
|
4
|
+
*/
|
|
5
|
+
import { getConfig } from "./config.js";
|
|
6
|
+
/**
|
|
7
|
+
* Pin JSON to IPFS via Pinata.
|
|
8
|
+
* Returns IPFS CID if Pinata configured, otherwise returns JSON string directly.
|
|
9
|
+
*/
|
|
10
|
+
export async function pinToIpfs(data) {
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
if (!config.pinataApiKey || !config.pinataSecretKey) {
|
|
13
|
+
console.log(" (No Pinata key — storing evidence as inline string)");
|
|
14
|
+
return JSON.stringify(data);
|
|
15
|
+
}
|
|
16
|
+
console.log(" Pinning to IPFS via Pinata...");
|
|
17
|
+
const response = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
pinata_api_key: config.pinataApiKey,
|
|
22
|
+
pinata_secret_api_key: config.pinataSecretKey,
|
|
23
|
+
},
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
pinataContent: data,
|
|
26
|
+
pinataMetadata: {
|
|
27
|
+
name: `x402r-evidence-${Date.now()}`,
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const text = await response.text();
|
|
33
|
+
console.warn(` Pinata failed (${response.status}): ${text}`);
|
|
34
|
+
console.log(" Falling back to inline string");
|
|
35
|
+
return JSON.stringify(data);
|
|
36
|
+
}
|
|
37
|
+
const result = (await response.json());
|
|
38
|
+
console.log(` Pinned: ${result.IpfsHash}`);
|
|
39
|
+
return result.IpfsHash;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=ipfs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipfs.js","sourceRoot":"","sources":["../../src/ipfs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAA6B;IAC3D,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gDAAgD,EAAE;QAC7E,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,cAAc,EAAE,MAAM,CAAC,YAAY;YACnC,qBAAqB,EAAE,MAAM,CAAC,eAAe;SAC9C;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE;gBACd,IAAI,EAAE,kBAAkB,IAAI,CAAC,GAAG,EAAE,EAAE;aACrC;SACF,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,oBAAoB,QAAQ,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,QAAQ,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI setup — creates viem clients from resolved config.
|
|
3
|
+
* Ported from x402r-sdk/examples/dev-tools/shared/cli-setup.ts with config file support.
|
|
4
|
+
*/
|
|
5
|
+
import { type PublicClient, type WalletClient } from "viem";
|
|
6
|
+
import { type PrivateKeyAccount } from "viem/accounts";
|
|
7
|
+
import { type ResolvedAddresses } from "@x402r/core";
|
|
8
|
+
export interface CliSetup {
|
|
9
|
+
account: PrivateKeyAccount;
|
|
10
|
+
publicClient: PublicClient;
|
|
11
|
+
walletClient: WalletClient;
|
|
12
|
+
networkId: string;
|
|
13
|
+
addresses: ResolvedAddresses;
|
|
14
|
+
operatorAddress: `0x${string}`;
|
|
15
|
+
arbiterUrl: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize CLI: validates config, creates viem clients.
|
|
19
|
+
*/
|
|
20
|
+
export declare function initCli(): CliSetup;
|
|
21
|
+
/**
|
|
22
|
+
* Read-only setup — no private key required.
|
|
23
|
+
*/
|
|
24
|
+
export declare function initReadOnly(): Pick<CliSetup, "publicClient" | "networkId" | "addresses" | "arbiterUrl" | "operatorAddress">;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI setup — creates viem clients from resolved config.
|
|
3
|
+
* Ported from x402r-sdk/examples/dev-tools/shared/cli-setup.ts with config file support.
|
|
4
|
+
*/
|
|
5
|
+
import { createPublicClient, createWalletClient, http, } from "viem";
|
|
6
|
+
import { baseSepolia, base, sepolia } from "viem/chains";
|
|
7
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
8
|
+
import { resolveAddresses } from "@x402r/core";
|
|
9
|
+
import { getConfig } from "./config.js";
|
|
10
|
+
const CHAINS = {
|
|
11
|
+
84532: baseSepolia,
|
|
12
|
+
8453: base,
|
|
13
|
+
11155111: sepolia,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Initialize CLI: validates config, creates viem clients.
|
|
17
|
+
*/
|
|
18
|
+
export function initCli() {
|
|
19
|
+
const config = getConfig();
|
|
20
|
+
if (!config.privateKey) {
|
|
21
|
+
console.error("Error: Private key not configured.");
|
|
22
|
+
console.error("Run: x402r config --key 0x...");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
if (!config.operatorAddress) {
|
|
26
|
+
console.error("Error: Operator address not configured.");
|
|
27
|
+
console.error("Run: x402r config --operator 0x...");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const account = privateKeyToAccount(config.privateKey);
|
|
31
|
+
const addresses = resolveAddresses(config.networkId);
|
|
32
|
+
const chainId = addresses.chainId;
|
|
33
|
+
const chain = CHAINS[chainId];
|
|
34
|
+
if (!chain) {
|
|
35
|
+
console.error(`Unsupported chain ID: ${chainId} (network: ${config.networkId})`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const transport = http(config.rpcUrl);
|
|
39
|
+
const publicClient = createPublicClient({ chain, transport });
|
|
40
|
+
const walletClient = createWalletClient({ account, chain, transport });
|
|
41
|
+
return {
|
|
42
|
+
account,
|
|
43
|
+
publicClient,
|
|
44
|
+
walletClient,
|
|
45
|
+
networkId: config.networkId,
|
|
46
|
+
addresses,
|
|
47
|
+
operatorAddress: config.operatorAddress,
|
|
48
|
+
arbiterUrl: config.arbiterUrl,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read-only setup — no private key required.
|
|
53
|
+
*/
|
|
54
|
+
export function initReadOnly() {
|
|
55
|
+
const config = getConfig();
|
|
56
|
+
const addresses = resolveAddresses(config.networkId);
|
|
57
|
+
const chainId = addresses.chainId;
|
|
58
|
+
const chain = CHAINS[chainId];
|
|
59
|
+
if (!chain) {
|
|
60
|
+
console.error(`Unsupported chain ID: ${chainId}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const publicClient = createPublicClient({ chain, transport: http(config.rpcUrl) });
|
|
64
|
+
return {
|
|
65
|
+
publicClient,
|
|
66
|
+
networkId: config.networkId,
|
|
67
|
+
addresses,
|
|
68
|
+
arbiterUrl: config.arbiterUrl,
|
|
69
|
+
operatorAddress: (config.operatorAddress || "0x0000000000000000000000000000000000000000"),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State persistence — saves payment and dispute state to ~/.x402r/
|
|
3
|
+
* Ported from x402r-sdk/examples/dev-tools/shared/state.ts with dispute state added.
|
|
4
|
+
*/
|
|
5
|
+
import type { PaymentInfo } from "@x402r/core";
|
|
6
|
+
export interface PaymentState {
|
|
7
|
+
paymentInfo: PaymentInfo;
|
|
8
|
+
operatorAddress: string;
|
|
9
|
+
paymentHash: string;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
networkId: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function savePaymentState(state: PaymentState): void;
|
|
14
|
+
export declare function loadPaymentState(): PaymentState | null;
|
|
15
|
+
/**
|
|
16
|
+
* Get PaymentInfo from CLI options or state file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getPaymentInfo(options: {
|
|
19
|
+
paymentJson?: string;
|
|
20
|
+
}): PaymentInfo;
|
|
21
|
+
export interface DisputeState {
|
|
22
|
+
nonce: string;
|
|
23
|
+
compositeKey?: string;
|
|
24
|
+
refundTxHash?: string;
|
|
25
|
+
evidenceTxHash?: string;
|
|
26
|
+
evidenceCid?: string;
|
|
27
|
+
arbiterResponse?: Record<string, unknown>;
|
|
28
|
+
timestamp: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function saveDisputeState(state: DisputeState): void;
|
|
31
|
+
export declare function loadDisputeState(): DisputeState | null;
|
|
32
|
+
/**
|
|
33
|
+
* Get nonce from CLI options or dispute state.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getNonce(options: {
|
|
36
|
+
nonce?: string;
|
|
37
|
+
id?: string;
|
|
38
|
+
}): bigint;
|
|
39
|
+
/**
|
|
40
|
+
* Get composite key from CLI options or dispute state.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getCompositeKey(options: {
|
|
43
|
+
id?: string;
|
|
44
|
+
}): string | undefined;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State persistence — saves payment and dispute state to ~/.x402r/
|
|
3
|
+
* Ported from x402r-sdk/examples/dev-tools/shared/state.ts with dispute state added.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import { parsePaymentInfo as coreParsePaymentInfo } from "@x402r/core";
|
|
9
|
+
const STATE_DIR = path.join(os.homedir(), ".x402r");
|
|
10
|
+
const PAYMENT_STATE_FILE = path.join(STATE_DIR, "last-payment.json");
|
|
11
|
+
const DISPUTE_STATE_FILE = path.join(STATE_DIR, "last-dispute.json");
|
|
12
|
+
function ensureDir() {
|
|
13
|
+
if (!fs.existsSync(STATE_DIR)) {
|
|
14
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function savePaymentState(state) {
|
|
18
|
+
ensureDir();
|
|
19
|
+
const serializable = {
|
|
20
|
+
...state,
|
|
21
|
+
paymentInfo: {
|
|
22
|
+
...state.paymentInfo,
|
|
23
|
+
maxAmount: state.paymentInfo.maxAmount.toString(),
|
|
24
|
+
preApprovalExpiry: state.paymentInfo.preApprovalExpiry.toString(),
|
|
25
|
+
authorizationExpiry: state.paymentInfo.authorizationExpiry.toString(),
|
|
26
|
+
refundExpiry: state.paymentInfo.refundExpiry.toString(),
|
|
27
|
+
salt: state.paymentInfo.salt.toString(),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
fs.writeFileSync(PAYMENT_STATE_FILE, JSON.stringify(serializable, null, 2));
|
|
31
|
+
}
|
|
32
|
+
export function loadPaymentState() {
|
|
33
|
+
if (!fs.existsSync(PAYMENT_STATE_FILE)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const raw = JSON.parse(fs.readFileSync(PAYMENT_STATE_FILE, "utf-8"));
|
|
38
|
+
return {
|
|
39
|
+
...raw,
|
|
40
|
+
paymentInfo: {
|
|
41
|
+
...raw.paymentInfo,
|
|
42
|
+
maxAmount: BigInt(raw.paymentInfo.maxAmount),
|
|
43
|
+
preApprovalExpiry: BigInt(raw.paymentInfo.preApprovalExpiry),
|
|
44
|
+
authorizationExpiry: BigInt(raw.paymentInfo.authorizationExpiry),
|
|
45
|
+
refundExpiry: BigInt(raw.paymentInfo.refundExpiry),
|
|
46
|
+
salt: BigInt(raw.paymentInfo.salt),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get PaymentInfo from CLI options or state file.
|
|
56
|
+
*/
|
|
57
|
+
export function getPaymentInfo(options) {
|
|
58
|
+
if (options.paymentJson) {
|
|
59
|
+
try {
|
|
60
|
+
return coreParsePaymentInfo(options.paymentJson);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.error("Error: Invalid payment JSON");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const state = loadPaymentState();
|
|
68
|
+
if (state) {
|
|
69
|
+
console.log(` (Using saved payment from ${state.timestamp})`);
|
|
70
|
+
return state.paymentInfo;
|
|
71
|
+
}
|
|
72
|
+
console.error("Error: No payment state found. Make a payment first or provide --payment-json.");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
export function saveDisputeState(state) {
|
|
76
|
+
ensureDir();
|
|
77
|
+
fs.writeFileSync(DISPUTE_STATE_FILE, JSON.stringify(state, null, 2));
|
|
78
|
+
}
|
|
79
|
+
export function loadDisputeState() {
|
|
80
|
+
if (!fs.existsSync(DISPUTE_STATE_FILE)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(fs.readFileSync(DISPUTE_STATE_FILE, "utf-8"));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get nonce from CLI options or dispute state.
|
|
92
|
+
*/
|
|
93
|
+
export function getNonce(options) {
|
|
94
|
+
if (options.nonce !== undefined) {
|
|
95
|
+
return BigInt(options.nonce);
|
|
96
|
+
}
|
|
97
|
+
const state = loadDisputeState();
|
|
98
|
+
if (state) {
|
|
99
|
+
console.log(` (Using saved dispute nonce: ${state.nonce})`);
|
|
100
|
+
return BigInt(state.nonce);
|
|
101
|
+
}
|
|
102
|
+
return 0n;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get composite key from CLI options or dispute state.
|
|
106
|
+
*/
|
|
107
|
+
export function getCompositeKey(options) {
|
|
108
|
+
if (options.id)
|
|
109
|
+
return options.id;
|
|
110
|
+
const state = loadDisputeState();
|
|
111
|
+
return state?.compositeKey;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@x402r/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-friendly CLI for x402r dispute resolution",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"x402r": "./dist/bin/x402r.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "tsx bin/x402r.ts",
|
|
11
|
+
"dev": "tsx bin/x402r.ts",
|
|
12
|
+
"build": "tsc && sed -i '' '1s|#!/usr/bin/env tsx|#!/usr/bin/env node|' dist/bin/x402r.js && chmod +x dist/bin/x402r.js",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@x402r/client": "^0.0.2",
|
|
17
|
+
"@x402r/core": "^0.0.2",
|
|
18
|
+
"commander": "^12.1.0",
|
|
19
|
+
"dotenv": "^16.4.0",
|
|
20
|
+
"viem": "^2.21.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.11.0",
|
|
24
|
+
"tsx": "^4.7.0",
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/",
|
|
29
|
+
"SKILL.md"
|
|
30
|
+
],
|
|
31
|
+
"keywords": [
|
|
32
|
+
"x402r",
|
|
33
|
+
"dispute",
|
|
34
|
+
"arbiter",
|
|
35
|
+
"payments",
|
|
36
|
+
"web3",
|
|
37
|
+
"openclaw"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|