arc402-cli 1.0.0-rc.1 → 1.0.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 (253) hide show
  1. package/README.md +41 -2
  2. package/dist/abis.d.ts +1 -0
  3. package/dist/abis.d.ts.map +1 -1
  4. package/dist/abis.js +29 -1
  5. package/dist/abis.js.map +1 -1
  6. package/dist/commands/backup.d.ts +3 -0
  7. package/dist/commands/backup.d.ts.map +1 -0
  8. package/dist/commands/backup.js +106 -0
  9. package/dist/commands/backup.js.map +1 -0
  10. package/dist/commands/compute.d.ts +14 -0
  11. package/dist/commands/compute.d.ts.map +1 -0
  12. package/dist/commands/compute.js +466 -0
  13. package/dist/commands/compute.js.map +1 -0
  14. package/dist/commands/config.d.ts.map +1 -1
  15. package/dist/commands/config.js +11 -1
  16. package/dist/commands/config.js.map +1 -1
  17. package/dist/commands/daemon.d.ts.map +1 -1
  18. package/dist/commands/daemon.js +67 -0
  19. package/dist/commands/daemon.js.map +1 -1
  20. package/dist/commands/discover.d.ts.map +1 -1
  21. package/dist/commands/discover.js +60 -15
  22. package/dist/commands/discover.js.map +1 -1
  23. package/dist/commands/doctor.d.ts +3 -0
  24. package/dist/commands/doctor.d.ts.map +1 -0
  25. package/dist/commands/doctor.js +205 -0
  26. package/dist/commands/doctor.js.map +1 -0
  27. package/dist/commands/wallet.d.ts.map +1 -1
  28. package/dist/commands/wallet.js +299 -65
  29. package/dist/commands/wallet.js.map +1 -1
  30. package/dist/commands/watch.d.ts.map +1 -1
  31. package/dist/commands/watch.js +146 -9
  32. package/dist/commands/watch.js.map +1 -1
  33. package/dist/commands/workroom.d.ts.map +1 -1
  34. package/dist/commands/workroom.js +112 -6
  35. package/dist/commands/workroom.js.map +1 -1
  36. package/dist/config.d.ts +12 -0
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js +41 -4
  39. package/dist/config.js.map +1 -1
  40. package/dist/daemon/compute-metering.d.ts +61 -0
  41. package/dist/daemon/compute-metering.d.ts.map +1 -0
  42. package/dist/daemon/compute-metering.js +299 -0
  43. package/dist/daemon/compute-metering.js.map +1 -0
  44. package/dist/daemon/compute-session.d.ts +100 -0
  45. package/dist/daemon/compute-session.d.ts.map +1 -0
  46. package/dist/daemon/compute-session.js +231 -0
  47. package/dist/daemon/compute-session.js.map +1 -0
  48. package/dist/daemon/config.d.ts +33 -1
  49. package/dist/daemon/config.d.ts.map +1 -1
  50. package/dist/daemon/config.js +69 -0
  51. package/dist/daemon/config.js.map +1 -1
  52. package/dist/daemon/credentials.d.ts +24 -0
  53. package/dist/daemon/credentials.d.ts.map +1 -0
  54. package/dist/daemon/credentials.js +80 -0
  55. package/dist/daemon/credentials.js.map +1 -0
  56. package/dist/daemon/delivery-client.d.ts +35 -0
  57. package/dist/daemon/delivery-client.d.ts.map +1 -0
  58. package/dist/daemon/delivery-client.js +231 -0
  59. package/dist/daemon/delivery-client.js.map +1 -0
  60. package/dist/daemon/file-delivery.d.ts +98 -0
  61. package/dist/daemon/file-delivery.d.ts.map +1 -0
  62. package/dist/daemon/file-delivery.js +461 -0
  63. package/dist/daemon/file-delivery.js.map +1 -0
  64. package/dist/daemon/index.d.ts +1 -0
  65. package/dist/daemon/index.d.ts.map +1 -1
  66. package/dist/daemon/index.js +793 -227
  67. package/dist/daemon/index.js.map +1 -1
  68. package/dist/daemon/notify.d.ts +35 -6
  69. package/dist/daemon/notify.d.ts.map +1 -1
  70. package/dist/daemon/notify.js +176 -48
  71. package/dist/daemon/notify.js.map +1 -1
  72. package/dist/daemon/worker-executor.d.ts +71 -0
  73. package/dist/daemon/worker-executor.d.ts.map +1 -0
  74. package/dist/daemon/worker-executor.js +382 -0
  75. package/dist/daemon/worker-executor.js.map +1 -0
  76. package/dist/drain-v4.js +2 -2
  77. package/dist/drain-v4.js.map +1 -1
  78. package/dist/endpoint-notify.d.ts +9 -1
  79. package/dist/endpoint-notify.d.ts.map +1 -1
  80. package/dist/endpoint-notify.js +116 -3
  81. package/dist/endpoint-notify.js.map +1 -1
  82. package/dist/index.js +81 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/program.d.ts.map +1 -1
  85. package/dist/program.js +6 -0
  86. package/dist/program.js.map +1 -1
  87. package/dist/repl.d.ts.map +1 -1
  88. package/dist/repl.js +69 -486
  89. package/dist/repl.js.map +1 -1
  90. package/dist/tui/App.d.ts +12 -0
  91. package/dist/tui/App.d.ts.map +1 -0
  92. package/dist/tui/App.js +154 -0
  93. package/dist/tui/App.js.map +1 -0
  94. package/dist/tui/Footer.d.ts +11 -0
  95. package/dist/tui/Footer.d.ts.map +1 -0
  96. package/dist/tui/Footer.js +13 -0
  97. package/dist/tui/Footer.js.map +1 -0
  98. package/dist/tui/Header.d.ts +14 -0
  99. package/dist/tui/Header.d.ts.map +1 -0
  100. package/dist/tui/Header.js +19 -0
  101. package/dist/tui/Header.js.map +1 -0
  102. package/dist/tui/InputLine.d.ts +11 -0
  103. package/dist/tui/InputLine.d.ts.map +1 -0
  104. package/dist/tui/InputLine.js +145 -0
  105. package/dist/tui/InputLine.js.map +1 -0
  106. package/dist/tui/Viewport.d.ts +14 -0
  107. package/dist/tui/Viewport.d.ts.map +1 -0
  108. package/dist/tui/Viewport.js +48 -0
  109. package/dist/tui/Viewport.js.map +1 -0
  110. package/dist/tui/WalletConnectPairing.d.ts +23 -0
  111. package/dist/tui/WalletConnectPairing.d.ts.map +1 -0
  112. package/dist/tui/WalletConnectPairing.js +61 -0
  113. package/dist/tui/WalletConnectPairing.js.map +1 -0
  114. package/dist/tui/components/Button.d.ts +7 -0
  115. package/dist/tui/components/Button.d.ts.map +1 -0
  116. package/dist/tui/components/Button.js +21 -0
  117. package/dist/tui/components/Button.js.map +1 -0
  118. package/dist/tui/components/CeremonyView.d.ts +13 -0
  119. package/dist/tui/components/CeremonyView.d.ts.map +1 -0
  120. package/dist/tui/components/CeremonyView.js +10 -0
  121. package/dist/tui/components/CeremonyView.js.map +1 -0
  122. package/dist/tui/components/CompletionDropdown.d.ts +7 -0
  123. package/dist/tui/components/CompletionDropdown.d.ts.map +1 -0
  124. package/dist/tui/components/CompletionDropdown.js +23 -0
  125. package/dist/tui/components/CompletionDropdown.js.map +1 -0
  126. package/dist/tui/components/ConfirmPrompt.d.ts +9 -0
  127. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  128. package/dist/tui/components/ConfirmPrompt.js +10 -0
  129. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  130. package/dist/tui/components/CustomTextInput.d.ts +15 -0
  131. package/dist/tui/components/CustomTextInput.d.ts.map +1 -0
  132. package/dist/tui/components/CustomTextInput.js +99 -0
  133. package/dist/tui/components/CustomTextInput.js.map +1 -0
  134. package/dist/tui/components/InteractiveTable.d.ts +14 -0
  135. package/dist/tui/components/InteractiveTable.d.ts.map +1 -0
  136. package/dist/tui/components/InteractiveTable.js +61 -0
  137. package/dist/tui/components/InteractiveTable.js.map +1 -0
  138. package/dist/tui/components/StepSpinner.d.ts +11 -0
  139. package/dist/tui/components/StepSpinner.d.ts.map +1 -0
  140. package/dist/tui/components/StepSpinner.js +32 -0
  141. package/dist/tui/components/StepSpinner.js.map +1 -0
  142. package/dist/tui/components/Toast.d.ts +18 -0
  143. package/dist/tui/components/Toast.d.ts.map +1 -0
  144. package/dist/tui/components/Toast.js +29 -0
  145. package/dist/tui/components/Toast.js.map +1 -0
  146. package/dist/tui/index.d.ts +2 -0
  147. package/dist/tui/index.d.ts.map +1 -0
  148. package/dist/tui/index.js +55 -0
  149. package/dist/tui/index.js.map +1 -0
  150. package/dist/tui/useChat.d.ts +11 -0
  151. package/dist/tui/useChat.d.ts.map +1 -0
  152. package/dist/tui/useChat.js +91 -0
  153. package/dist/tui/useChat.js.map +1 -0
  154. package/dist/tui/useCommand.d.ts +12 -0
  155. package/dist/tui/useCommand.d.ts.map +1 -0
  156. package/dist/tui/useCommand.js +137 -0
  157. package/dist/tui/useCommand.js.map +1 -0
  158. package/dist/tui/useNotifications.d.ts +9 -0
  159. package/dist/tui/useNotifications.d.ts.map +1 -0
  160. package/dist/tui/useNotifications.js +17 -0
  161. package/dist/tui/useNotifications.js.map +1 -0
  162. package/dist/tui/useScroll.d.ts +17 -0
  163. package/dist/tui/useScroll.d.ts.map +1 -0
  164. package/dist/tui/useScroll.js +46 -0
  165. package/dist/tui/useScroll.js.map +1 -0
  166. package/dist/ui/format.d.ts.map +1 -1
  167. package/dist/ui/format.js +2 -0
  168. package/dist/ui/format.js.map +1 -1
  169. package/dist/ui/qr-render.d.ts +25 -0
  170. package/dist/ui/qr-render.d.ts.map +1 -0
  171. package/dist/ui/qr-render.js +90 -0
  172. package/dist/ui/qr-render.js.map +1 -0
  173. package/dist/ui/rpc-fallback.d.ts +11 -0
  174. package/dist/ui/rpc-fallback.d.ts.map +1 -0
  175. package/dist/ui/rpc-fallback.js +58 -0
  176. package/dist/ui/rpc-fallback.js.map +1 -0
  177. package/dist/walletconnect.d.ts +4 -0
  178. package/dist/walletconnect.d.ts.map +1 -1
  179. package/dist/walletconnect.js.map +1 -1
  180. package/package.json +10 -2
  181. package/scripts/authorize-machine-key.ts +0 -43
  182. package/scripts/drain-wallet.ts +0 -149
  183. package/scripts/execute-spend-only.ts +0 -81
  184. package/scripts/register-agent-userop.ts +0 -186
  185. package/src/abis.ts +0 -187
  186. package/src/bundler.ts +0 -235
  187. package/src/client.ts +0 -36
  188. package/src/coinbase-smart-wallet.ts +0 -51
  189. package/src/commands/accept.ts +0 -64
  190. package/src/commands/agent-handshake.ts +0 -72
  191. package/src/commands/agent.ts +0 -691
  192. package/src/commands/agreements.ts +0 -350
  193. package/src/commands/arbitrator.ts +0 -180
  194. package/src/commands/arena-handshake.ts +0 -257
  195. package/src/commands/arena.ts +0 -122
  196. package/src/commands/cancel.ts +0 -35
  197. package/src/commands/channel.ts +0 -218
  198. package/src/commands/coldstart.ts +0 -165
  199. package/src/commands/config.ts +0 -58
  200. package/src/commands/contract-interaction.ts +0 -166
  201. package/src/commands/daemon.ts +0 -978
  202. package/src/commands/deliver.ts +0 -148
  203. package/src/commands/discover.ts +0 -297
  204. package/src/commands/dispute.ts +0 -375
  205. package/src/commands/endpoint.ts +0 -620
  206. package/src/commands/feed.ts +0 -229
  207. package/src/commands/hire.ts +0 -245
  208. package/src/commands/migrate.ts +0 -177
  209. package/src/commands/negotiate.ts +0 -271
  210. package/src/commands/openshell.ts +0 -1055
  211. package/src/commands/owner.ts +0 -35
  212. package/src/commands/policy.ts +0 -263
  213. package/src/commands/relay.ts +0 -273
  214. package/src/commands/remediate.ts +0 -24
  215. package/src/commands/reputation.ts +0 -79
  216. package/src/commands/setup.ts +0 -343
  217. package/src/commands/trust.ts +0 -27
  218. package/src/commands/verify.ts +0 -91
  219. package/src/commands/wallet.ts +0 -3280
  220. package/src/commands/watch.ts +0 -23
  221. package/src/commands/watchtower.ts +0 -248
  222. package/src/commands/workroom.ts +0 -959
  223. package/src/config.ts +0 -174
  224. package/src/daemon/config.ts +0 -308
  225. package/src/daemon/hire-listener.ts +0 -226
  226. package/src/daemon/index.ts +0 -955
  227. package/src/daemon/job-lifecycle.ts +0 -215
  228. package/src/daemon/notify.ts +0 -157
  229. package/src/daemon/token-metering.ts +0 -183
  230. package/src/daemon/userops.ts +0 -119
  231. package/src/daemon/wallet-monitor.ts +0 -90
  232. package/src/drain-v4.ts +0 -159
  233. package/src/endpoint-config.ts +0 -83
  234. package/src/endpoint-notify.ts +0 -46
  235. package/src/index.ts +0 -26
  236. package/src/openshell-runtime.ts +0 -277
  237. package/src/program.ts +0 -83
  238. package/src/repl.ts +0 -680
  239. package/src/signing.ts +0 -28
  240. package/src/telegram-notify.ts +0 -88
  241. package/src/ui/banner.ts +0 -51
  242. package/src/ui/colors.ts +0 -30
  243. package/src/ui/format.ts +0 -77
  244. package/src/ui/spinner.ts +0 -56
  245. package/src/ui/tree.ts +0 -16
  246. package/src/utils/format.ts +0 -48
  247. package/src/utils/hash.ts +0 -5
  248. package/src/utils/time.ts +0 -15
  249. package/src/wallet-router.ts +0 -178
  250. package/src/walletconnect-session.ts +0 -27
  251. package/src/walletconnect.ts +0 -294
  252. package/test/time.test.js +0 -11
  253. package/tsconfig.json +0 -19
@@ -1,257 +0,0 @@
1
- import { Command } from "commander";
2
- import { ethers } from "ethers";
3
- import { loadConfig, getUsdcAddress } from "../config";
4
- import { requireSigner } from "../client";
5
- import { AGENT_REGISTRY_ABI } from "../abis";
6
- import { startSpinner } from "../ui/spinner";
7
- import { renderTree } from "../ui/tree";
8
- import { c } from "../ui/colors";
9
-
10
- const DEFAULT_REGISTRY_ADDRESS = "0xD5c2851B00090c92Ba7F4723FB548bb30C9B6865";
11
-
12
- async function pingHandshakeEndpoint(
13
- agentAddress: string,
14
- payload: Record<string, unknown>,
15
- registryAddress: string,
16
- provider: ethers.Provider
17
- ): Promise<void> {
18
- const registry = new ethers.Contract(registryAddress, AGENT_REGISTRY_ABI, provider);
19
- const agentData = await registry.getAgent(agentAddress);
20
- const endpoint = agentData.endpoint as string;
21
- if (!endpoint) return;
22
- await fetch(`${endpoint}/handshake`, {
23
- method: "POST",
24
- headers: { "Content-Type": "application/json" },
25
- body: JSON.stringify(payload),
26
- });
27
- }
28
-
29
- // ─── Handshake Contract ABI (from Handshake.sol) ─────────────────────────────
30
-
31
- const HANDSHAKE_ABI = [
32
- "function sendHandshake(address to, uint8 hsType, string note) payable",
33
- "function sendHandshakeWithToken(address to, uint8 hsType, string note, address token, uint256 tokenAmount)",
34
- "function sendBatch(address[] recipients, uint8[] hsTypes, string[] notes)",
35
- "function hasConnection(address from, address to) view returns (bool)",
36
- "function isMutual(address a, address b) view returns (bool)",
37
- "function getStats(address agent) view returns (uint256 sent, uint256 received, uint256 uniqueInbound)",
38
- "function sentCount(address) view returns (uint256)",
39
- "function receivedCount(address) view returns (uint256)",
40
- "function uniqueSenders(address) view returns (uint256)",
41
- "function totalHandshakes() view returns (uint256)",
42
- "function allowedTokens(address) view returns (bool)",
43
- "event HandshakeSent(uint256 indexed handshakeId, address indexed from, address indexed to, uint8 hsType, address token, uint256 amount, string note, uint256 timestamp)",
44
- "event NewConnection(address indexed from, address indexed to, uint256 handshakeId)",
45
- ];
46
-
47
- const POLICY_ENGINE_ABI = [
48
- "function isContractWhitelisted(address wallet, address target) view returns (bool)",
49
- "function whitelistContract(address wallet, address target)",
50
- ];
51
-
52
- const HANDSHAKE_TYPES: Record<string, number> = {
53
- respect: 0,
54
- curiosity: 1,
55
- endorsement: 2,
56
- thanks: 3,
57
- collaboration: 4,
58
- challenge: 5,
59
- referral: 6,
60
- hello: 7,
61
- };
62
-
63
- // ─── Auto-Whitelist ──────────────────────────────────────────────────────────
64
-
65
- async function ensureWhitelisted(
66
- signer: ethers.Signer,
67
- provider: ethers.Provider,
68
- walletAddress: string,
69
- policyEngineAddress: string,
70
- handshakeAddress: string
71
- ): Promise<void> {
72
- const pe = new ethers.Contract(policyEngineAddress, POLICY_ENGINE_ABI, provider);
73
- const isWhitelisted = await pe.isContractWhitelisted(walletAddress, handshakeAddress);
74
-
75
- if (!isWhitelisted) {
76
- console.log("Handshake contract not yet whitelisted on your wallet.");
77
- console.log("Whitelisting now (one-time setup)...");
78
-
79
- const peSigner = new ethers.Contract(policyEngineAddress, POLICY_ENGINE_ABI, signer);
80
- const tx = await peSigner.whitelistContract(walletAddress, handshakeAddress);
81
- console.log(` tx: ${tx.hash}`);
82
- await tx.wait();
83
- console.log(" ✓ Handshake contract whitelisted\n");
84
- }
85
- }
86
-
87
- // ─── Commands ────────────────────────────────────────────────────────────────
88
-
89
- export function registerArenaHandshakeCommands(program: Command): void {
90
- const hs = program
91
- .command("shake")
92
- .description("ARC Arena social handshake — send a typed trust signal to another agent.");
93
-
94
- // ── send ──────────────────────────────────────────────────────────────────
95
- hs.command("send <agentAddress>")
96
- .description("Send a handshake to another agent.")
97
- .option("--type <type>", "Handshake type: respect, curiosity, endorsement, thanks, collaboration, challenge, referral, hello", "hello")
98
- .option("--note <note>", "Short message (max 280 chars)", "")
99
- .option("--tip <amount>", "ETH tip to attach (e.g. 0.01)")
100
- .option("--usdc <amount>", "USDC tip to attach (e.g. 5.00)")
101
- .option("--json", "Output as JSON")
102
- .action(async (agentAddress: string, opts) => {
103
- const config = loadConfig();
104
- const { signer, provider } = await requireSigner(config);
105
- const myAddress = await signer.getAddress();
106
-
107
- if (!config.handshakeAddress) {
108
- console.error("handshakeAddress not configured. Run: arc402 config set handshakeAddress <address>");
109
- process.exit(1);
110
- }
111
- if (!config.policyEngineAddress) {
112
- console.error("policyEngineAddress not configured.");
113
- process.exit(1);
114
- }
115
-
116
- // Auto-whitelist check
117
- await ensureWhitelisted(signer, provider, myAddress, config.policyEngineAddress, config.handshakeAddress);
118
-
119
- const hsType = HANDSHAKE_TYPES[opts.type.toLowerCase()];
120
- if (hsType === undefined) {
121
- console.error(`Unknown handshake type: ${opts.type}`);
122
- console.error(`Valid types: ${Object.keys(HANDSHAKE_TYPES).join(", ")}`);
123
- process.exit(1);
124
- }
125
-
126
- const handshake = new ethers.Contract(config.handshakeAddress, HANDSHAKE_ABI, signer);
127
-
128
- const hsSpinner = startSpinner(`Sending ${opts.type} handshake...`);
129
- let tx;
130
- if (opts.usdc) {
131
- // USDC handshake
132
- const usdcAddress = getUsdcAddress(config);
133
- const amount = ethers.parseUnits(opts.usdc, 6);
134
- tx = await handshake.sendHandshakeWithToken(agentAddress, hsType, opts.note, usdcAddress, amount);
135
- } else {
136
- // ETH handshake (with optional tip)
137
- const value = opts.tip ? ethers.parseEther(opts.tip) : 0n;
138
- tx = await handshake.sendHandshake(agentAddress, hsType, opts.note, { value });
139
- }
140
- hsSpinner.succeed("Handshake sent");
141
-
142
- // Notify recipient's HTTP endpoint (non-blocking)
143
- const registryAddress = config.agentRegistryV2Address ?? config.agentRegistryAddress ?? DEFAULT_REGISTRY_ADDRESS;
144
- try {
145
- await pingHandshakeEndpoint(
146
- agentAddress,
147
- { from: myAddress, type: opts.type, note: opts.note, txHash: tx.hash },
148
- registryAddress,
149
- provider
150
- );
151
- } catch (err) {
152
- console.warn(`Warning: could not notify recipient endpoint: ${err instanceof Error ? err.message : String(err)}`);
153
- }
154
-
155
- if (opts.json) {
156
- console.log(JSON.stringify({ tx: tx.hash, from: myAddress, to: agentAddress, type: opts.type, note: opts.note }));
157
- } else {
158
- const treeItems = [
159
- { label: "From", value: myAddress },
160
- { label: "To", value: agentAddress },
161
- { label: "Type", value: opts.type },
162
- ...(opts.note ? [{ label: "Note", value: opts.note as string }] : []),
163
- ...(opts.tip ? [{ label: "Tip", value: `${opts.tip as string} ETH` }] : []),
164
- ...(opts.usdc ? [{ label: "Tip", value: `${opts.usdc as string} USDC` }] : []),
165
- { label: "Tx", value: tx.hash as string, last: true },
166
- ];
167
- renderTree(treeItems);
168
- }
169
- });
170
-
171
- // ── batch ─────────────────────────────────────────────────────────────────
172
- hs.command("batch")
173
- .description("Send handshakes to multiple agents at once (onboarding ritual).")
174
- .argument("<agents...>", "Agent addresses to handshake (up to 10)")
175
- .option("--type <type>", "Handshake type for all", "hello")
176
- .option("--note <note>", "Note for all", "")
177
- .action(async (agents: string[], opts) => {
178
- const config = loadConfig();
179
- const { signer, provider } = await requireSigner(config);
180
- const myAddress = await signer.getAddress();
181
-
182
- if (!config.handshakeAddress || !config.policyEngineAddress) {
183
- console.error("handshakeAddress or policyEngineAddress not configured.");
184
- process.exit(1);
185
- }
186
-
187
- await ensureWhitelisted(signer, provider, myAddress, config.policyEngineAddress, config.handshakeAddress);
188
-
189
- const hsType = HANDSHAKE_TYPES[opts.type.toLowerCase()];
190
- if (hsType === undefined) {
191
- console.error(`Unknown type: ${opts.type}. Valid: ${Object.keys(HANDSHAKE_TYPES).join(", ")}`);
192
- process.exit(1);
193
- }
194
-
195
- if (agents.length > 10) {
196
- console.error("Max 10 agents per batch.");
197
- process.exit(1);
198
- }
199
-
200
- const handshake = new ethers.Contract(config.handshakeAddress, HANDSHAKE_ABI, signer);
201
- const types = agents.map(() => hsType);
202
- const notes = agents.map(() => opts.note);
203
-
204
- const tx = await handshake.sendBatch(agents, types, notes);
205
- console.log(`✓ Batch handshake sent to ${agents.length} agents`);
206
- agents.forEach(a => console.log(` → ${a}`));
207
- console.log(` tx: ${tx.hash}`);
208
- });
209
-
210
- // ── stats ─────────────────────────────────────────────────────────────────
211
- hs.command("stats [address]")
212
- .description("View handshake stats for an agent.")
213
- .action(async (address?: string) => {
214
- const config = loadConfig();
215
- const { signer, provider } = await requireSigner(config);
216
- const target = address || await signer.getAddress();
217
-
218
- if (!config.handshakeAddress) {
219
- console.error("handshakeAddress not configured.");
220
- process.exit(1);
221
- }
222
-
223
- const handshake = new ethers.Contract(config.handshakeAddress, HANDSHAKE_ABI, provider);
224
- const [sent, received, unique] = await handshake.getStats(target);
225
- const total = await handshake.totalHandshakes();
226
-
227
- console.log(`Handshake Stats: ${target}`);
228
- console.log(` Sent: ${sent}`);
229
- console.log(` Received: ${received}`);
230
- console.log(` Unique senders: ${unique}`);
231
- console.log(` Network total: ${total}`);
232
- });
233
-
234
- // ── check ─────────────────────────────────────────────────────────────────
235
- hs.command("check <agentAddress>")
236
- .description("Check if a connection or mutual handshake exists with an agent.")
237
- .action(async (agentAddress: string) => {
238
- const config = loadConfig();
239
- const { signer, provider } = await requireSigner(config);
240
- const myAddress = await signer.getAddress();
241
-
242
- if (!config.handshakeAddress) {
243
- console.error("handshakeAddress not configured.");
244
- process.exit(1);
245
- }
246
-
247
- const handshake = new ethers.Contract(config.handshakeAddress, HANDSHAKE_ABI, provider);
248
- const iSent = await handshake.hasConnection(myAddress, agentAddress);
249
- const theySent = await handshake.hasConnection(agentAddress, myAddress);
250
- const mutual = await handshake.isMutual(myAddress, agentAddress);
251
-
252
- console.log(`Connection: ${myAddress} ↔ ${agentAddress}`);
253
- console.log(` You → them: ${iSent ? "✓ handshaked" : "✗ no handshake"}`);
254
- console.log(` Them → you: ${theySent ? "✓ handshaked" : "✗ no handshake"}`);
255
- console.log(` Mutual: ${mutual ? "✓ yes" : "✗ no"}`);
256
- });
257
- }
@@ -1,122 +0,0 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import { runFeed, FeedOptions } from "./feed";
4
- import { c } from "../ui/colors";
5
-
6
- const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744310/arc-402/v0.2.0";
7
-
8
- async function gql(query: string): Promise<Record<string, unknown>> {
9
- const res = await fetch(SUBGRAPH_URL, {
10
- method: "POST",
11
- headers: { "Content-Type": "application/json" },
12
- body: JSON.stringify({ query }),
13
- });
14
- if (!res.ok) throw new Error(`Subgraph HTTP ${res.status}`);
15
- const json = (await res.json()) as { data?: Record<string, unknown>; errors?: unknown[] };
16
- if (json.errors?.length) throw new Error(`Subgraph error: ${JSON.stringify(json.errors[0])}`);
17
- return json.data ?? {};
18
- }
19
-
20
- export function registerArenaCommands(program: Command): void {
21
- const arena = program.command("arena").description("Arena network commands");
22
-
23
- // ─── arena stats ───────────────────────────────────────────────────────────
24
-
25
- arena
26
- .command("stats")
27
- .description("Show Arena network statistics")
28
- .option("--json", "Output as JSON")
29
- .action(async (opts) => {
30
- try {
31
- const data = await gql(`{
32
- protocolStats(id: "global") {
33
- totalAgents
34
- totalWallets
35
- totalAgreements
36
- totalHandshakes
37
- totalConnections
38
- totalVouches
39
- totalCapabilityClaims
40
- }
41
- }`);
42
-
43
- const stats = data["protocolStats"] as Record<string, unknown> | null;
44
- if (!stats) {
45
- const msg = "No stats available — subgraph may still be syncing.";
46
- if (opts.json) {
47
- console.log(JSON.stringify({ error: msg }));
48
- } else {
49
- console.error(' ' + c.warning + c.white(` ${msg}`));
50
- }
51
- process.exit(1);
52
- }
53
-
54
- // Try to count active agreements separately — non-fatal if it fails
55
- let activeAgreements = 0;
56
- try {
57
- const agData = await gql(`{
58
- proposals: agreements(where: { state: 0 }, first: 1000) { id }
59
- accepted: agreements(where: { state: 1 }, first: 1000) { id }
60
- }`);
61
- activeAgreements =
62
- ((agData["proposals"] as unknown[]) ?? []).length +
63
- ((agData["accepted"] as unknown[]) ?? []).length;
64
- } catch {
65
- // ignore — active count just stays 0
66
- }
67
-
68
- if (opts.json) {
69
- console.log(JSON.stringify({ ...stats, activeAgreements }, null, 2));
70
- return;
71
- }
72
-
73
- const pad = (v: unknown) => String(v ?? 0).padStart(6);
74
- console.log(chalk.bold("╔══════════════════════════════════════╗"));
75
- console.log(chalk.bold("║ ARC Arena — Network Stats ║"));
76
- console.log(chalk.bold("╚══════════════════════════════════════╝"));
77
- console.log();
78
- console.log(` Agents ${pad(stats["totalAgents"])} registered`);
79
- console.log(` Wallets ${pad(stats["totalWallets"])} deployed`);
80
- console.log(
81
- ` Agreements ${pad(stats["totalAgreements"])} total (${activeAgreements} active)`,
82
- );
83
- console.log(` Handshakes ${pad(stats["totalHandshakes"])} sent`);
84
- console.log(` Connections ${pad(stats["totalConnections"])} unique pairs`);
85
- console.log(` Vouches ${pad(stats["totalVouches"])} active`);
86
- console.log(` Capabilities ${pad(stats["totalCapabilityClaims"])} claimed`);
87
- console.log();
88
- console.log(chalk.dim(" Subgraph: v0.2.0 · synced"));
89
- } catch (err) {
90
- const msg = err instanceof Error ? err.message : String(err);
91
- if (opts.json) {
92
- console.log(JSON.stringify({ error: "Subgraph unavailable", details: msg }));
93
- } else {
94
- console.error(' ' + c.failure + c.white(` Subgraph unavailable: ${msg}`));
95
- }
96
- process.exit(1);
97
- }
98
- });
99
-
100
- // ─── arena feed (alias) ─────────────────────────────────────────────────────
101
-
102
- arena
103
- .command("feed")
104
- .description("Live terminal feed of recent Arena events (alias for arc402 feed)")
105
- .option("--limit <n>", "Number of events to show", "20")
106
- .option("--live", "Poll every 30s for new events")
107
- .option("--type <type>", "Filter by event type: handshake|hire|fulfill|vouch")
108
- .option("--json", "Output as JSON")
109
- .action(async (opts) => {
110
- try {
111
- await runFeed(opts as FeedOptions);
112
- } catch (err) {
113
- const msg = err instanceof Error ? err.message : String(err);
114
- if ((opts as FeedOptions).json) {
115
- console.log(JSON.stringify({ error: "Subgraph unavailable", details: msg }));
116
- } else {
117
- console.error(' ' + c.failure + c.white(` Subgraph unavailable: ${msg}`));
118
- }
119
- process.exit(1);
120
- }
121
- });
122
- }
@@ -1,35 +0,0 @@
1
- import { Command } from "commander";
2
- import { ServiceAgreementClient } from "@arc402/sdk";
3
- import { loadConfig } from "../config";
4
- import { requireSigner } from "../client";
5
- import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router";
6
- import { SERVICE_AGREEMENT_ABI } from "../abis";
7
- import { c } from "../ui/colors";
8
- import { startSpinner } from "../ui/spinner";
9
-
10
- export function registerCancelCommand(program: Command): void {
11
- program.command("cancel <id>").description("Cancel a proposed agreement; use --expired for post-deadline recovery paths").option("--expired", "Call expiredCancel()", false).action(async (id, opts) => {
12
- const config = loadConfig();
13
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
14
- const { signer } = await requireSigner(config);
15
- printSenderInfo(config);
16
- const spinner = startSpinner("Submitting transaction...");
17
- try {
18
- if (config.walletContractAddress) {
19
- const fn = opts.expired ? "expiredCancel" : "cancel";
20
- await executeContractWriteViaWallet(
21
- config.walletContractAddress, signer, config.serviceAgreementAddress,
22
- SERVICE_AGREEMENT_ABI, fn, [BigInt(id)],
23
- );
24
- } else {
25
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
26
- if (opts.expired) await client.expiredCancel(BigInt(id)); else await client.cancel(BigInt(id));
27
- }
28
- spinner.succeed();
29
- } catch (err) {
30
- spinner.fail();
31
- throw err;
32
- }
33
- console.log(' ' + c.success + c.white(` Cancelled — agreement #${id}`));
34
- });
35
- }
@@ -1,218 +0,0 @@
1
- import { Command } from "commander";
2
- import { loadConfig } from "../config";
3
- import { requireSigner, getClient } from "../client";
4
- import { ChannelClient } from "@arc402/sdk";
5
- import type { ChannelState } from "@arc402/sdk";
6
- import * as fs from "fs";
7
- import * as path from "path";
8
- import * as os from "os";
9
- import { c } from '../ui/colors';
10
- import { startSpinner } from '../ui/spinner';
11
- import { renderTree } from '../ui/tree';
12
- import { formatAddress } from '../ui/format';
13
-
14
- const CHANNEL_STATES_DIR = path.join(os.homedir(), ".arc402", "channel-states");
15
-
16
- function loadStateFile(path: string): ChannelState {
17
- const raw = fs.readFileSync(path, "utf-8");
18
- const obj = JSON.parse(raw);
19
- return {
20
- ...obj,
21
- cumulativePayment: BigInt(obj.cumulativePayment),
22
- } as ChannelState;
23
- }
24
-
25
- export function registerChannelCommands(program: Command): void {
26
- const channel = program.command("channel").description("Session channel management — open, close, challenge, reclaim");
27
-
28
- channel.command("open <provider>")
29
- .description("Open a session channel with a provider")
30
- .requiredOption("--token <address>", "Token address (0x0 for ETH)")
31
- .requiredOption("--max <amount>", "Max deposit amount in wei")
32
- .requiredOption("--rate <amount>", "Expected rate per call in wei (informational)")
33
- .requiredOption("--deadline <timestamp>", "Channel expiry unix timestamp")
34
- .option("--json", "JSON output")
35
- .action(async (provider, opts) => {
36
- const config = loadConfig();
37
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
38
- const { signer } = await requireSigner(config);
39
- const client = new ChannelClient(config.serviceAgreementAddress, signer);
40
- const spinner = startSpinner('Opening session channel…');
41
- const result = await client.openSessionChannel(
42
- provider,
43
- opts.token,
44
- BigInt(opts.max),
45
- BigInt(opts.rate),
46
- Number(opts.deadline)
47
- );
48
- if (opts.json || program.opts().json) {
49
- spinner.stop();
50
- console.log(JSON.stringify(result, null, 2));
51
- } else {
52
- spinner.succeed(' Opened — channel ' + result.channelId);
53
- renderTree([
54
- { label: 'Channel', value: result.channelId },
55
- { label: 'Tx', value: result.txHash, last: true },
56
- ]);
57
- }
58
- });
59
-
60
- channel.command("status <channelId>")
61
- .description("Get channel status")
62
- .option("--json", "JSON output")
63
- .action(async (channelId, opts) => {
64
- const config = loadConfig();
65
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
66
- const { provider } = await getClient(config);
67
- const client = new ChannelClient(config.serviceAgreementAddress, provider);
68
- const ch = await client.getChannelStatus(channelId);
69
- if (opts.json || program.opts().json) {
70
- console.log(JSON.stringify(ch, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2));
71
- } else {
72
- console.log('\n ' + c.mark + c.white(` Channel ${channelId}`));
73
- renderTree([
74
- { label: 'Status', value: ch.status },
75
- { label: 'Client', value: formatAddress(ch.client) },
76
- { label: 'Provider', value: formatAddress(ch.provider) },
77
- { label: 'Deposit', value: ch.depositAmount.toString() },
78
- { label: 'Settled', value: ch.settledAmount.toString() },
79
- { label: 'Seq', value: ch.lastSequenceNumber.toString(), last: true },
80
- ]);
81
- }
82
- });
83
-
84
- channel.command("list <wallet>")
85
- .description("List open channels for a wallet (client or provider)")
86
- .option("--json", "JSON output")
87
- .action(async (wallet, opts) => {
88
- const config = loadConfig();
89
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
90
- const { provider } = await getClient(config);
91
- const client = new ChannelClient(config.serviceAgreementAddress, provider);
92
- const channels = await client.getOpenChannels(wallet);
93
- if (opts.json || program.opts().json) {
94
- console.log(JSON.stringify(channels, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2));
95
- } else {
96
- if (channels.length === 0) {
97
- console.log("no open channels");
98
- } else {
99
- for (const ch of channels) {
100
- console.log(` ${ch.status} deposit=${ch.depositAmount} seq=${ch.lastSequenceNumber} deadline=${ch.deadline}`);
101
- }
102
- }
103
- }
104
- });
105
-
106
- channel.command("close <channelId>")
107
- .description("Close a channel cooperatively with signed final state")
108
- .requiredOption("--state <path>", "Path to signed state JSON file")
109
- .option("--json", "JSON output")
110
- .action(async (channelId, opts) => {
111
- const config = loadConfig();
112
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
113
- const { signer } = await requireSigner(config);
114
- const client = new ChannelClient(config.serviceAgreementAddress, signer);
115
- const state = loadStateFile(opts.state);
116
- const result = await client.closeChannel(channelId, state);
117
- if (opts.json || program.opts().json) {
118
- console.log(JSON.stringify(result, null, 2));
119
- } else {
120
- console.log(' ' + c.success + c.white(` Close submitted — ${result.txHash}`));
121
- console.log(' ' + c.dim('Challenge window open (24h)'));
122
- }
123
- });
124
-
125
- channel.command("challenge <channelId>")
126
- .description("Challenge a stale close with a higher sequence state")
127
- .requiredOption("--state <path>", "Path to signed state JSON file with higher sequenceNumber")
128
- .option("--json", "JSON output")
129
- .action(async (channelId, opts) => {
130
- const config = loadConfig();
131
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
132
- const { signer } = await requireSigner(config);
133
- const client = new ChannelClient(config.serviceAgreementAddress, signer);
134
- const state = loadStateFile(opts.state);
135
- const result = await client.challengeChannel(channelId, state);
136
- if (opts.json || program.opts().json) {
137
- console.log(JSON.stringify(result, null, 2));
138
- } else {
139
- console.log(' ' + c.success + c.white(` Challenge submitted — ${result.txHash}`));
140
- }
141
- });
142
-
143
- channel.command("finalise <channelId>")
144
- .description("Finalise a close after the challenge window expires")
145
- .option("--json", "JSON output")
146
- .action(async (channelId, opts) => {
147
- const config = loadConfig();
148
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
149
- const { signer } = await requireSigner(config);
150
- const client = new ChannelClient(config.serviceAgreementAddress, signer);
151
- const result = await client.finaliseChallenge(channelId);
152
- if (opts.json || program.opts().json) {
153
- console.log(JSON.stringify(result, null, 2));
154
- } else {
155
- console.log(' ' + c.success + c.white(` Finalised — ${result.txHash}`));
156
- }
157
- });
158
-
159
- channel.command("reclaim <channelId>")
160
- .description("Client: reclaim deposit from expired channel")
161
- .option("--json", "JSON output")
162
- .action(async (channelId, opts) => {
163
- const config = loadConfig();
164
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
165
- const { signer } = await requireSigner(config);
166
- const client = new ChannelClient(config.serviceAgreementAddress, signer);
167
- const result = await client.reclaimExpiredChannel(channelId);
168
- if (opts.json || program.opts().json) {
169
- console.log(JSON.stringify(result, null, 2));
170
- } else {
171
- console.log(' ' + c.success + c.white(` Reclaimed — ${result.txHash}`));
172
- }
173
- });
174
-
175
- channel.command("store-state <channelId> <statePath>")
176
- .description(
177
- "Save a signed channel state to local storage (~/.arc402/channel-states/<channelId>.json) " +
178
- "for the `arc402 daemon channel-watch` to use when auto-challenging stale closes."
179
- )
180
- .option("--json", "JSON output")
181
- .action((channelId, statePath, opts) => {
182
- let raw: string;
183
- try {
184
- raw = fs.readFileSync(statePath, "utf-8");
185
- } catch (err) {
186
- console.error(`Cannot read state file: ${statePath}\n${err}`);
187
- process.exit(1);
188
- }
189
-
190
- let state: Record<string, unknown>;
191
- try {
192
- state = JSON.parse(raw) as Record<string, unknown>;
193
- } catch {
194
- console.error("State file is not valid JSON.");
195
- process.exit(1);
196
- }
197
-
198
- const required = ["sequenceNumber", "cumulativePayment", "callCount", "token", "timestamp", "clientSig", "providerSig"];
199
- for (const field of required) {
200
- if (!(field in state)) {
201
- console.error(`State file missing required field: ${field}`);
202
- process.exit(1);
203
- }
204
- }
205
-
206
- fs.mkdirSync(CHANNEL_STATES_DIR, { recursive: true, mode: 0o700 });
207
- const dest = path.join(CHANNEL_STATES_DIR, `${channelId}.json`);
208
- const stored = { ...state, channelId };
209
- fs.writeFileSync(dest, JSON.stringify(stored, null, 2), { mode: 0o600 });
210
-
211
- if (opts.json || program.opts().json) {
212
- console.log(JSON.stringify({ stored: true, channelId, path: dest }));
213
- } else {
214
- console.log(' ' + c.success + c.white(` State stored — ${dest}`));
215
- console.log(' ' + c.dim(`seq: ${state.sequenceNumber}`));
216
- }
217
- });
218
- }