arc402-cli 1.0.0-rc.1 → 1.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.
Files changed (257) hide show
  1. package/README.md +43 -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/tunnel.d.ts +3 -0
  28. package/dist/commands/tunnel.d.ts.map +1 -0
  29. package/dist/commands/tunnel.js +281 -0
  30. package/dist/commands/tunnel.js.map +1 -0
  31. package/dist/commands/wallet.d.ts.map +1 -1
  32. package/dist/commands/wallet.js +299 -65
  33. package/dist/commands/wallet.js.map +1 -1
  34. package/dist/commands/watch.d.ts.map +1 -1
  35. package/dist/commands/watch.js +146 -9
  36. package/dist/commands/watch.js.map +1 -1
  37. package/dist/commands/workroom.d.ts.map +1 -1
  38. package/dist/commands/workroom.js +112 -6
  39. package/dist/commands/workroom.js.map +1 -1
  40. package/dist/config.d.ts +13 -0
  41. package/dist/config.d.ts.map +1 -1
  42. package/dist/config.js +41 -4
  43. package/dist/config.js.map +1 -1
  44. package/dist/daemon/compute-metering.d.ts +61 -0
  45. package/dist/daemon/compute-metering.d.ts.map +1 -0
  46. package/dist/daemon/compute-metering.js +299 -0
  47. package/dist/daemon/compute-metering.js.map +1 -0
  48. package/dist/daemon/compute-session.d.ts +100 -0
  49. package/dist/daemon/compute-session.d.ts.map +1 -0
  50. package/dist/daemon/compute-session.js +231 -0
  51. package/dist/daemon/compute-session.js.map +1 -0
  52. package/dist/daemon/config.d.ts +33 -1
  53. package/dist/daemon/config.d.ts.map +1 -1
  54. package/dist/daemon/config.js +69 -0
  55. package/dist/daemon/config.js.map +1 -1
  56. package/dist/daemon/credentials.d.ts +24 -0
  57. package/dist/daemon/credentials.d.ts.map +1 -0
  58. package/dist/daemon/credentials.js +80 -0
  59. package/dist/daemon/credentials.js.map +1 -0
  60. package/dist/daemon/delivery-client.d.ts +35 -0
  61. package/dist/daemon/delivery-client.d.ts.map +1 -0
  62. package/dist/daemon/delivery-client.js +231 -0
  63. package/dist/daemon/delivery-client.js.map +1 -0
  64. package/dist/daemon/file-delivery.d.ts +98 -0
  65. package/dist/daemon/file-delivery.d.ts.map +1 -0
  66. package/dist/daemon/file-delivery.js +461 -0
  67. package/dist/daemon/file-delivery.js.map +1 -0
  68. package/dist/daemon/index.d.ts +1 -0
  69. package/dist/daemon/index.d.ts.map +1 -1
  70. package/dist/daemon/index.js +793 -227
  71. package/dist/daemon/index.js.map +1 -1
  72. package/dist/daemon/notify.d.ts +35 -6
  73. package/dist/daemon/notify.d.ts.map +1 -1
  74. package/dist/daemon/notify.js +176 -48
  75. package/dist/daemon/notify.js.map +1 -1
  76. package/dist/daemon/worker-executor.d.ts +71 -0
  77. package/dist/daemon/worker-executor.d.ts.map +1 -0
  78. package/dist/daemon/worker-executor.js +382 -0
  79. package/dist/daemon/worker-executor.js.map +1 -0
  80. package/dist/drain-v4.js +2 -2
  81. package/dist/drain-v4.js.map +1 -1
  82. package/dist/endpoint-notify.d.ts +9 -1
  83. package/dist/endpoint-notify.d.ts.map +1 -1
  84. package/dist/endpoint-notify.js +116 -3
  85. package/dist/endpoint-notify.js.map +1 -1
  86. package/dist/index.js +81 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/program.d.ts.map +1 -1
  89. package/dist/program.js +8 -0
  90. package/dist/program.js.map +1 -1
  91. package/dist/repl.d.ts.map +1 -1
  92. package/dist/repl.js +69 -486
  93. package/dist/repl.js.map +1 -1
  94. package/dist/tui/App.d.ts +12 -0
  95. package/dist/tui/App.d.ts.map +1 -0
  96. package/dist/tui/App.js +154 -0
  97. package/dist/tui/App.js.map +1 -0
  98. package/dist/tui/Footer.d.ts +11 -0
  99. package/dist/tui/Footer.d.ts.map +1 -0
  100. package/dist/tui/Footer.js +13 -0
  101. package/dist/tui/Footer.js.map +1 -0
  102. package/dist/tui/Header.d.ts +14 -0
  103. package/dist/tui/Header.d.ts.map +1 -0
  104. package/dist/tui/Header.js +19 -0
  105. package/dist/tui/Header.js.map +1 -0
  106. package/dist/tui/InputLine.d.ts +11 -0
  107. package/dist/tui/InputLine.d.ts.map +1 -0
  108. package/dist/tui/InputLine.js +145 -0
  109. package/dist/tui/InputLine.js.map +1 -0
  110. package/dist/tui/Viewport.d.ts +14 -0
  111. package/dist/tui/Viewport.d.ts.map +1 -0
  112. package/dist/tui/Viewport.js +48 -0
  113. package/dist/tui/Viewport.js.map +1 -0
  114. package/dist/tui/WalletConnectPairing.d.ts +23 -0
  115. package/dist/tui/WalletConnectPairing.d.ts.map +1 -0
  116. package/dist/tui/WalletConnectPairing.js +61 -0
  117. package/dist/tui/WalletConnectPairing.js.map +1 -0
  118. package/dist/tui/components/Button.d.ts +7 -0
  119. package/dist/tui/components/Button.d.ts.map +1 -0
  120. package/dist/tui/components/Button.js +21 -0
  121. package/dist/tui/components/Button.js.map +1 -0
  122. package/dist/tui/components/CeremonyView.d.ts +13 -0
  123. package/dist/tui/components/CeremonyView.d.ts.map +1 -0
  124. package/dist/tui/components/CeremonyView.js +10 -0
  125. package/dist/tui/components/CeremonyView.js.map +1 -0
  126. package/dist/tui/components/CompletionDropdown.d.ts +7 -0
  127. package/dist/tui/components/CompletionDropdown.d.ts.map +1 -0
  128. package/dist/tui/components/CompletionDropdown.js +23 -0
  129. package/dist/tui/components/CompletionDropdown.js.map +1 -0
  130. package/dist/tui/components/ConfirmPrompt.d.ts +9 -0
  131. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  132. package/dist/tui/components/ConfirmPrompt.js +10 -0
  133. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  134. package/dist/tui/components/CustomTextInput.d.ts +15 -0
  135. package/dist/tui/components/CustomTextInput.d.ts.map +1 -0
  136. package/dist/tui/components/CustomTextInput.js +99 -0
  137. package/dist/tui/components/CustomTextInput.js.map +1 -0
  138. package/dist/tui/components/InteractiveTable.d.ts +14 -0
  139. package/dist/tui/components/InteractiveTable.d.ts.map +1 -0
  140. package/dist/tui/components/InteractiveTable.js +61 -0
  141. package/dist/tui/components/InteractiveTable.js.map +1 -0
  142. package/dist/tui/components/StepSpinner.d.ts +11 -0
  143. package/dist/tui/components/StepSpinner.d.ts.map +1 -0
  144. package/dist/tui/components/StepSpinner.js +32 -0
  145. package/dist/tui/components/StepSpinner.js.map +1 -0
  146. package/dist/tui/components/Toast.d.ts +18 -0
  147. package/dist/tui/components/Toast.d.ts.map +1 -0
  148. package/dist/tui/components/Toast.js +29 -0
  149. package/dist/tui/components/Toast.js.map +1 -0
  150. package/dist/tui/index.d.ts +2 -0
  151. package/dist/tui/index.d.ts.map +1 -0
  152. package/dist/tui/index.js +55 -0
  153. package/dist/tui/index.js.map +1 -0
  154. package/dist/tui/useChat.d.ts +11 -0
  155. package/dist/tui/useChat.d.ts.map +1 -0
  156. package/dist/tui/useChat.js +91 -0
  157. package/dist/tui/useChat.js.map +1 -0
  158. package/dist/tui/useCommand.d.ts +12 -0
  159. package/dist/tui/useCommand.d.ts.map +1 -0
  160. package/dist/tui/useCommand.js +137 -0
  161. package/dist/tui/useCommand.js.map +1 -0
  162. package/dist/tui/useNotifications.d.ts +9 -0
  163. package/dist/tui/useNotifications.d.ts.map +1 -0
  164. package/dist/tui/useNotifications.js +17 -0
  165. package/dist/tui/useNotifications.js.map +1 -0
  166. package/dist/tui/useScroll.d.ts +17 -0
  167. package/dist/tui/useScroll.d.ts.map +1 -0
  168. package/dist/tui/useScroll.js +46 -0
  169. package/dist/tui/useScroll.js.map +1 -0
  170. package/dist/ui/format.d.ts.map +1 -1
  171. package/dist/ui/format.js +2 -0
  172. package/dist/ui/format.js.map +1 -1
  173. package/dist/ui/qr-render.d.ts +25 -0
  174. package/dist/ui/qr-render.d.ts.map +1 -0
  175. package/dist/ui/qr-render.js +90 -0
  176. package/dist/ui/qr-render.js.map +1 -0
  177. package/dist/ui/rpc-fallback.d.ts +11 -0
  178. package/dist/ui/rpc-fallback.d.ts.map +1 -0
  179. package/dist/ui/rpc-fallback.js +58 -0
  180. package/dist/ui/rpc-fallback.js.map +1 -0
  181. package/dist/walletconnect.d.ts +4 -0
  182. package/dist/walletconnect.d.ts.map +1 -1
  183. package/dist/walletconnect.js.map +1 -1
  184. package/package.json +11 -3
  185. package/scripts/authorize-machine-key.ts +0 -43
  186. package/scripts/drain-wallet.ts +0 -149
  187. package/scripts/execute-spend-only.ts +0 -81
  188. package/scripts/register-agent-userop.ts +0 -186
  189. package/src/abis.ts +0 -187
  190. package/src/bundler.ts +0 -235
  191. package/src/client.ts +0 -36
  192. package/src/coinbase-smart-wallet.ts +0 -51
  193. package/src/commands/accept.ts +0 -64
  194. package/src/commands/agent-handshake.ts +0 -72
  195. package/src/commands/agent.ts +0 -691
  196. package/src/commands/agreements.ts +0 -350
  197. package/src/commands/arbitrator.ts +0 -180
  198. package/src/commands/arena-handshake.ts +0 -257
  199. package/src/commands/arena.ts +0 -122
  200. package/src/commands/cancel.ts +0 -35
  201. package/src/commands/channel.ts +0 -218
  202. package/src/commands/coldstart.ts +0 -165
  203. package/src/commands/config.ts +0 -58
  204. package/src/commands/contract-interaction.ts +0 -166
  205. package/src/commands/daemon.ts +0 -978
  206. package/src/commands/deliver.ts +0 -148
  207. package/src/commands/discover.ts +0 -297
  208. package/src/commands/dispute.ts +0 -375
  209. package/src/commands/endpoint.ts +0 -620
  210. package/src/commands/feed.ts +0 -229
  211. package/src/commands/hire.ts +0 -245
  212. package/src/commands/migrate.ts +0 -177
  213. package/src/commands/negotiate.ts +0 -271
  214. package/src/commands/openshell.ts +0 -1055
  215. package/src/commands/owner.ts +0 -35
  216. package/src/commands/policy.ts +0 -263
  217. package/src/commands/relay.ts +0 -273
  218. package/src/commands/remediate.ts +0 -24
  219. package/src/commands/reputation.ts +0 -79
  220. package/src/commands/setup.ts +0 -343
  221. package/src/commands/trust.ts +0 -27
  222. package/src/commands/verify.ts +0 -91
  223. package/src/commands/wallet.ts +0 -3280
  224. package/src/commands/watch.ts +0 -23
  225. package/src/commands/watchtower.ts +0 -248
  226. package/src/commands/workroom.ts +0 -959
  227. package/src/config.ts +0 -174
  228. package/src/daemon/config.ts +0 -308
  229. package/src/daemon/hire-listener.ts +0 -226
  230. package/src/daemon/index.ts +0 -955
  231. package/src/daemon/job-lifecycle.ts +0 -215
  232. package/src/daemon/notify.ts +0 -157
  233. package/src/daemon/token-metering.ts +0 -183
  234. package/src/daemon/userops.ts +0 -119
  235. package/src/daemon/wallet-monitor.ts +0 -90
  236. package/src/drain-v4.ts +0 -159
  237. package/src/endpoint-config.ts +0 -83
  238. package/src/endpoint-notify.ts +0 -46
  239. package/src/index.ts +0 -26
  240. package/src/openshell-runtime.ts +0 -277
  241. package/src/program.ts +0 -83
  242. package/src/repl.ts +0 -680
  243. package/src/signing.ts +0 -28
  244. package/src/telegram-notify.ts +0 -88
  245. package/src/ui/banner.ts +0 -51
  246. package/src/ui/colors.ts +0 -30
  247. package/src/ui/format.ts +0 -77
  248. package/src/ui/spinner.ts +0 -56
  249. package/src/ui/tree.ts +0 -16
  250. package/src/utils/format.ts +0 -48
  251. package/src/utils/hash.ts +0 -5
  252. package/src/utils/time.ts +0 -15
  253. package/src/wallet-router.ts +0 -178
  254. package/src/walletconnect-session.ts +0 -27
  255. package/src/walletconnect.ts +0 -294
  256. package/test/time.test.js +0 -11
  257. 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
- }