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,229 +0,0 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import { c } from '../ui/colors';
4
-
5
- const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744310/arc-402/v0.2.0";
6
-
7
- const HS_TYPE_LABELS: Record<number, string> = {
8
- 0: "Respected",
9
- 1: "Curious",
10
- 2: "Endorsed",
11
- 3: "Thanked",
12
- 4: "Collaborated",
13
- 5: "Challenged",
14
- 6: "Referred",
15
- 7: "Hello",
16
- };
17
-
18
- async function gql(query: string): Promise<Record<string, unknown>> {
19
- const res = await fetch(SUBGRAPH_URL, {
20
- method: "POST",
21
- headers: { "Content-Type": "application/json" },
22
- body: JSON.stringify({ query }),
23
- });
24
- if (!res.ok) throw new Error(`Subgraph HTTP ${res.status}`);
25
- const json = (await res.json()) as { data?: Record<string, unknown>; errors?: unknown[] };
26
- if (json.errors?.length) throw new Error(`Subgraph error: ${JSON.stringify(json.errors[0])}`);
27
- return json.data ?? {};
28
- }
29
-
30
- function shortAddr(addr: string): string {
31
- return addr.length > 12 ? `${addr.slice(0, 6)}...${addr.slice(-4)}` : addr;
32
- }
33
-
34
- function utcTime(ts: number): string {
35
- const d = new Date(ts * 1000);
36
- const h = d.getUTCHours().toString().padStart(2, "0");
37
- const m = d.getUTCMinutes().toString().padStart(2, "0");
38
- return `${h}:${m} UTC`;
39
- }
40
-
41
- function weiToEth(wei: string): string {
42
- return (Number(BigInt(wei)) / 1e18).toFixed(4);
43
- }
44
-
45
- function weiToUsdc(wei: string): string {
46
- return (Number(BigInt(wei)) / 1e6).toFixed(2);
47
- }
48
-
49
- type FeedEvent = {
50
- type: "handshake" | "hire" | "fulfill" | "vouch";
51
- timestamp: number;
52
- raw: Record<string, unknown>;
53
- };
54
-
55
- async function fetchFeedEvents(limit: number, typeFilter?: string, sinceTs?: number): Promise<FeedEvent[]> {
56
- const includeHandshakes = !typeFilter || typeFilter === "handshake";
57
- const includeHire = !typeFilter || typeFilter === "hire";
58
- const includeFulfill = !typeFilter || typeFilter === "fulfill";
59
- const includeVouch = !typeFilter || typeFilter === "vouch";
60
-
61
- const tsFilt = sinceTs ? `, where: { timestamp_gt: "${sinceTs}" }` : "";
62
- const agTsFilt = sinceTs ? `, where: { proposedAt_gt: "${sinceTs}" }` : "";
63
-
64
- const parts: string[] = [];
65
-
66
- if (includeHandshakes) {
67
- parts.push(`
68
- handshakes(orderBy: timestamp, orderDirection: desc, first: ${limit}${tsFilt}) {
69
- id from { id name } to { id name } hsType note timestamp isNewConnection
70
- }`);
71
- }
72
-
73
- if (includeHire || includeFulfill) {
74
- parts.push(`
75
- agreements(orderBy: proposedAt, orderDirection: desc, first: ${limit}${agTsFilt}) {
76
- id client provider serviceType price state proposedAt updatedAt
77
- }`);
78
- }
79
-
80
- if (includeVouch) {
81
- parts.push(`
82
- vouches(orderBy: id, orderDirection: desc, first: ${limit}) {
83
- id voucher { id name } newAgent { id name } stakeAmount active
84
- }`);
85
- }
86
-
87
- const data = await gql(`{ ${parts.join("\n")} }`);
88
- const events: FeedEvent[] = [];
89
-
90
- if (includeHandshakes) {
91
- for (const h of (data.handshakes as Record<string, unknown>[]) ?? []) {
92
- events.push({ type: "handshake", timestamp: Number(h["timestamp"]), raw: h });
93
- }
94
- }
95
-
96
- if (data["agreements"]) {
97
- for (const a of data["agreements"] as Record<string, unknown>[]) {
98
- const state = Number(a["state"]);
99
- if (includeHire && state === 0) {
100
- events.push({ type: "hire", timestamp: Number(a["proposedAt"]), raw: a });
101
- }
102
- if (includeFulfill && state === 2) {
103
- events.push({ type: "fulfill", timestamp: Number(a["updatedAt"]), raw: a });
104
- }
105
- }
106
- }
107
-
108
- if (includeVouch) {
109
- for (const v of (data["vouches"] as Record<string, unknown>[]) ?? []) {
110
- events.push({ type: "vouch", timestamp: 0, raw: v });
111
- }
112
- }
113
-
114
- events.sort((a, b) => b.timestamp - a.timestamp);
115
- return events.slice(0, limit);
116
- }
117
-
118
- function renderFeedEvent(ev: FeedEvent): string {
119
- const ts = ev.timestamp > 0 ? `[${utcTime(ev.timestamp)}]` : "[ ]";
120
-
121
- switch (ev.type) {
122
- case "handshake": {
123
- const h = ev.raw;
124
- const from = h["from"] as Record<string, string>;
125
- const to = h["to"] as Record<string, string>;
126
- const fromName = from["name"] || shortAddr(from["id"]);
127
- const toName = to["name"] || shortAddr(to["id"]);
128
- const label = HS_TYPE_LABELS[Number(h["hsType"])] ?? `Type${h["hsType"]}`;
129
- const note = h["note"] ? ` "${h["note"]}"` : "";
130
- return chalk.cyan(`${ts} 🤝 ${fromName} → ${toName} ${label}${note}`);
131
- }
132
- case "hire": {
133
- const a = ev.raw;
134
- const price = a["price"] ? ` ${weiToEth(a["price"] as string)} ETH` : "";
135
- return chalk.yellow(
136
- `${ts} 📋 ${shortAddr(a["client"] as string)} hired ${shortAddr(a["provider"] as string)} ${a["serviceType"]}${price}`,
137
- );
138
- }
139
- case "fulfill": {
140
- const a = ev.raw;
141
- const price = a["price"] ? ` ${weiToEth(a["price"] as string)} ETH released` : "";
142
- return chalk.green(`${ts} ✅ Agreement #${(a["id"] as string).slice(0, 8)} fulfilled${price}`);
143
- }
144
- case "vouch": {
145
- const v = ev.raw;
146
- const voucher = v["voucher"] as Record<string, string>;
147
- const newAgent = v["newAgent"] as Record<string, string>;
148
- const vName = voucher["name"] || shortAddr(voucher["id"]);
149
- const nName = newAgent["name"] || shortAddr(newAgent["id"]);
150
- const stake = v["stakeAmount"] ? ` ${weiToUsdc(v["stakeAmount"] as string)} USDC staked` : "";
151
- return chalk.magenta(`${ts} 🔗 ${vName} vouched for ${nName}${stake}`);
152
- }
153
- }
154
- }
155
-
156
- export interface FeedOptions {
157
- limit?: string;
158
- live?: boolean;
159
- type?: string;
160
- json?: boolean;
161
- }
162
-
163
- export async function runFeed(opts: FeedOptions): Promise<void> {
164
- const limit = parseInt(opts.limit ?? "20", 10);
165
- const typeFilter = opts.type;
166
-
167
- const events = await fetchFeedEvents(limit, typeFilter);
168
-
169
- if (opts.json) {
170
- console.log(JSON.stringify(events, null, 2));
171
- if (!opts.live) return;
172
- } else {
173
- for (const ev of [...events].reverse()) {
174
- console.log(renderFeedEvent(ev));
175
- }
176
- if (!opts.live) return;
177
- }
178
-
179
- // Live mode — keep process alive and poll every 30s
180
- process.stdin.resume();
181
- let lastTs = events.length > 0 ? events[0].timestamp : Math.floor(Date.now() / 1000);
182
-
183
- const interval = setInterval(async () => {
184
- try {
185
- const newEvents = await fetchFeedEvents(limit, typeFilter, lastTs);
186
- if (newEvents.length > 0) {
187
- if (opts.json) {
188
- console.log(JSON.stringify(newEvents, null, 2));
189
- } else {
190
- console.log(chalk.dim("─".repeat(50)));
191
- for (const ev of [...newEvents].reverse()) {
192
- console.log(renderFeedEvent(ev));
193
- }
194
- }
195
- lastTs = newEvents[0].timestamp;
196
- }
197
- } catch {
198
- if (!opts.json) console.log(chalk.dim(" (subgraph unavailable)"));
199
- }
200
- }, 30_000);
201
-
202
- process.on("SIGINT", () => {
203
- clearInterval(interval);
204
- process.exit(0);
205
- });
206
- }
207
-
208
- export function registerFeedCommand(program: Command): void {
209
- program
210
- .command("feed")
211
- .description("Live terminal feed of recent Arena events")
212
- .option("--limit <n>", "Number of events to show", "20")
213
- .option("--live", "Poll every 30s for new events")
214
- .option("--type <type>", "Filter by event type: handshake|hire|fulfill|vouch")
215
- .option("--json", "Output as JSON")
216
- .action(async (opts) => {
217
- try {
218
- await runFeed(opts as FeedOptions);
219
- } catch (err) {
220
- const msg = err instanceof Error ? err.message : String(err);
221
- if ((opts as FeedOptions).json) {
222
- console.log(JSON.stringify({ error: "Subgraph unavailable", details: msg }));
223
- } else {
224
- console.error(chalk.red(`Subgraph unavailable: ${msg}`));
225
- }
226
- process.exit(1);
227
- }
228
- });
229
- }
@@ -1,245 +0,0 @@
1
- import { Command } from "commander";
2
- import { ServiceAgreementClient, SessionManager } from "@arc402/sdk";
3
- import { ethers } from "ethers";
4
- import { getUsdcAddress, loadConfig } from "../config";
5
- import { requireSigner } from "../client";
6
- import { hashFile, hashString } from "../utils/hash";
7
- import { parseDuration } from "../utils/time";
8
- import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router";
9
- import { AGENT_REGISTRY_ABI, SERVICE_AGREEMENT_ABI } from "../abis";
10
- import { c } from '../ui/colors';
11
- import { startSpinner } from '../ui/spinner';
12
- import { renderTree, TreeItem } from '../ui/tree';
13
- import { formatAddress } from '../ui/format';
14
-
15
- const DEFAULT_REGISTRY_ADDRESS = "0xD5c2851B00090c92Ba7F4723FB548bb30C9B6865";
16
-
17
- const sessionManager = new SessionManager();
18
-
19
- export function registerHireCommand(program: Command): void {
20
- program
21
- .command("hire")
22
- .description("Create the on-chain commitment after off-chain negotiation")
23
- .requiredOption("--agent <address>")
24
- .requiredOption("--task <description>")
25
- .requiredOption("--service-type <type>")
26
- .option("--max <amount>", "Max price in wei (e.g. 1000000000000000) or ETH (e.g. 0.001eth) or USDC (e.g. 1USDC). Required unless --session is provided.")
27
- .option("--deadline <duration>", "Deadline as duration (1h, 30m, 7d) or absolute ISO date (2026-04-01). Required unless --session is provided.")
28
- .option("--token <token>", "eth or usdc", "eth")
29
- .option("--deliverable-spec <filepath>")
30
- .option("--session <sessionId>", "Load agreed price and deadline from a completed negotiation session")
31
- .option("--json")
32
- .action(async (opts) => {
33
- const config = loadConfig();
34
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
35
- const { signer, address } = await requireSigner(config);
36
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
37
-
38
- let maxAmount: string;
39
- let deadlineArg: string;
40
- let transcriptHash: string | undefined;
41
-
42
- if (opts.session) {
43
- const session = sessionManager.load(opts.session);
44
- if (session.state !== "ACCEPTED") throw new Error(`Session ${opts.session} is not in ACCEPTED state (state: ${session.state})`);
45
- if (!session.agreedPrice || !session.agreedDeadline) throw new Error(`Session ${opts.session} is missing agreedPrice or agreedDeadline`);
46
- maxAmount = session.agreedPrice;
47
- deadlineArg = session.agreedDeadline;
48
- transcriptHash = session.transcriptHash;
49
- } else {
50
- if (!opts.max) throw new Error("--max is required when --session is not provided. Examples: 0.001eth, 1000000000000000 (wei), 1USDC");
51
- if (!opts.deadline) throw new Error("--deadline is required when --session is not provided. Examples: 1h, 30m, 7d, 2026-04-01");
52
- maxAmount = opts.max;
53
- deadlineArg = opts.deadline;
54
- }
55
-
56
- // Normalise --max: strip trailing 'eth' or 'USDC' suffix and convert to correct unit
57
- const useUsdc = String(opts.token).toLowerCase() === "usdc";
58
- const ethSuffix = /^(\d+(?:\.\d+)?)eth$/i.exec(maxAmount);
59
- const usdcSuffix = /^(\d+(?:\.\d+)?)usdc$/i.exec(maxAmount);
60
- if (ethSuffix) maxAmount = String(BigInt(Math.round(parseFloat(ethSuffix[1]) * 1e18)));
61
- else if (usdcSuffix) maxAmount = usdcSuffix[1]; // keep decimal for USDC path
62
- const token = useUsdc ? getUsdcAddress(config) : ethers.ZeroAddress;
63
- let price: bigint;
64
- try {
65
- price = useUsdc ? BigInt(Math.round(Number(maxAmount) * 1_000_000)) : BigInt(maxAmount);
66
- } catch {
67
- throw new Error(`Invalid --max value "${opts.max}". Use wei (1000000000000000), ETH (0.001eth), or USDC (1USDC)`);
68
- }
69
- if (price <= 0n) throw new Error(`--max must be greater than zero`);
70
-
71
- // Pre-flight: check client !== provider (J2-03)
72
- if (address.toLowerCase() === opts.agent.toLowerCase()) {
73
- console.error("Cannot hire yourself: client and provider addresses are the same.");
74
- process.exit(1);
75
- }
76
-
77
- // Pre-flight: check provider is registered in AgentRegistry (J2-02)
78
- const agentRegistryAddress = config.agentRegistryV2Address ?? config.agentRegistryAddress;
79
- if (agentRegistryAddress) {
80
- const arProvider = new ethers.JsonRpcProvider(config.rpcUrl);
81
- const arCheck = new ethers.Contract(
82
- agentRegistryAddress,
83
- ["function isRegistered(address wallet) external view returns (bool)"],
84
- arProvider,
85
- );
86
- let isRegistered = true;
87
- try {
88
- isRegistered = await arCheck.isRegistered(opts.agent);
89
- } catch { /* assume registered if read fails */ }
90
- if (!isRegistered) {
91
- console.error(`Provider ${opts.agent} is not registered in AgentRegistry.`);
92
- console.error(`Verify the agent address is correct, or check the registry at ${agentRegistryAddress}.`);
93
- process.exit(1);
94
- }
95
- }
96
-
97
- // Pre-flight: check token is allowed on this ServiceAgreement (J2-01)
98
- if (useUsdc) {
99
- const saProvider = new ethers.JsonRpcProvider(config.rpcUrl);
100
- const saCheck = new ethers.Contract(
101
- config.serviceAgreementAddress,
102
- ["function allowedTokens(address) external view returns (bool)"],
103
- saProvider,
104
- );
105
- let isAllowed = false;
106
- try {
107
- isAllowed = await saCheck.allowedTokens(token);
108
- } catch { isAllowed = true; /* assume allowed if read fails */ }
109
- if (!isAllowed) {
110
- console.error(`Token ${token} is not allowed on this ServiceAgreement.`);
111
- console.error(`Only the SA owner can allowlist tokens via:`);
112
- console.error(` cast send ${config.serviceAgreementAddress} "allowToken(address)" ${token}`);
113
- console.error(`For ETH payments, use --token eth`);
114
- process.exit(1);
115
- }
116
- }
117
-
118
- // Use spec hash as deliverables hash; if transcript exists, incorporate it
119
- const baseHash = opts.deliverableSpec ? hashFile(opts.deliverableSpec) : hashString(opts.task);
120
- const deliverablesHash = transcriptHash
121
- ? (ethers.keccak256(ethers.toUtf8Bytes(baseHash + transcriptHash)) as `0x${string}`)
122
- : baseHash;
123
-
124
- // Parse deadline: if it looks like an ISO date, convert to seconds from now
125
- let deadlineSeconds: number;
126
- const isoMatch = deadlineArg.match(/^\d{4}-\d{2}-\d{2}/);
127
- if (isoMatch) {
128
- const target = Math.floor(new Date(deadlineArg).getTime() / 1000);
129
- deadlineSeconds = target - Math.floor(Date.now() / 1000);
130
- if (deadlineSeconds <= 0) throw new Error(`Deadline ${deadlineArg} is in the past`);
131
- } else {
132
- deadlineSeconds = parseDuration(deadlineArg);
133
- }
134
-
135
- printSenderInfo(config);
136
-
137
- let agreementId: bigint;
138
-
139
- const hireSpinner = startSpinner('Submitting agreement...');
140
-
141
- if (config.walletContractAddress) {
142
- // Smart wallet path — wallet handles per-tx USDC approval via maxApprovalAmount
143
- const tx = await executeContractWriteViaWallet(
144
- config.walletContractAddress,
145
- signer,
146
- config.serviceAgreementAddress,
147
- SERVICE_AGREEMENT_ABI,
148
- "propose",
149
- [opts.agent, opts.serviceType, opts.task, price, token, deadlineSeconds, deliverablesHash],
150
- useUsdc ? 0n : price, // ETH value forwarded to SA; 0 for USDC agreements
151
- useUsdc ? token : ethers.ZeroAddress, // approvalToken for USDC
152
- useUsdc ? price : 0n, // maxApprovalAmount for USDC
153
- );
154
- const receipt = await tx.wait();
155
- const saInterface = new ethers.Interface(SERVICE_AGREEMENT_ABI);
156
- let found = false;
157
- for (const log of receipt!.logs) {
158
- if (log.address.toLowerCase() === config.serviceAgreementAddress.toLowerCase()) {
159
- try {
160
- const parsed = saInterface.parseLog(log);
161
- if (parsed?.name === "AgreementProposed") {
162
- agreementId = parsed.args[0] as bigint;
163
- found = true;
164
- break;
165
- }
166
- } catch { /* skip unparseable logs */ }
167
- }
168
- }
169
- if (!found) throw new Error("AgreementProposed event not found in transaction receipt");
170
- } else {
171
- // EOA path — existing behaviour
172
- if (useUsdc) {
173
- const usdc = new ethers.Contract(
174
- token,
175
- ["function approve(address spender,uint256 amount) external returns (bool)", "function allowance(address owner,address spender) external view returns (uint256)"],
176
- signer
177
- );
178
- const allowance = await usdc.allowance(address, config.serviceAgreementAddress);
179
- if (allowance < price) await (await usdc.approve(config.serviceAgreementAddress, price)).wait();
180
- }
181
-
182
- const result = await client.propose({
183
- provider: opts.agent,
184
- serviceType: opts.serviceType,
185
- description: opts.task,
186
- price,
187
- token,
188
- deadline: deadlineSeconds,
189
- deliverablesHash,
190
- });
191
- agreementId = result.agreementId;
192
- }
193
-
194
- hireSpinner.succeed('Agreement proposed');
195
-
196
- // Notify provider's HTTP endpoint (non-blocking)
197
- const hireRegistryAddress = config.agentRegistryV2Address ?? config.agentRegistryAddress ?? DEFAULT_REGISTRY_ADDRESS;
198
- try {
199
- const hireProvider = new ethers.JsonRpcProvider(config.rpcUrl);
200
- const hireRegistry = new ethers.Contract(hireRegistryAddress, AGENT_REGISTRY_ABI, hireProvider);
201
- const agentData = await hireRegistry.getAgent(opts.agent);
202
- const endpoint = agentData.endpoint as string;
203
- if (endpoint) {
204
- await fetch(`${endpoint}/hire`, {
205
- method: "POST",
206
- headers: { "Content-Type": "application/json" },
207
- body: JSON.stringify({
208
- agreementId: agreementId!.toString(),
209
- from: address,
210
- provider: opts.agent,
211
- serviceType: opts.serviceType,
212
- task: opts.task,
213
- price: price.toString(),
214
- token,
215
- deadline: deadlineSeconds,
216
- deliverablesHash,
217
- }),
218
- });
219
- }
220
- } catch (err) {
221
- console.warn(`Warning: could not notify provider endpoint: ${err instanceof Error ? err.message : String(err)}`);
222
- }
223
-
224
- if (opts.session) {
225
- sessionManager.setOnChainId(opts.session, agreementId!.toString());
226
- }
227
-
228
- if (opts.json) {
229
- const output: Record<string, unknown> = { agreementId: agreementId!.toString(), deliverablesHash };
230
- if (transcriptHash) output.transcriptHash = transcriptHash;
231
- if (opts.session) output.sessionId = opts.session;
232
- return console.log(JSON.stringify(output, null, 2));
233
- }
234
-
235
- console.log(' ' + c.success + c.white(` Agreement #${agreementId!} proposed`));
236
- const hireTreeItems: TreeItem[] = [
237
- { label: 'Agent', value: formatAddress(opts.agent) },
238
- { label: 'Task', value: opts.task.slice(0, 60) + (opts.task.length > 60 ? '...' : '') },
239
- { label: 'Service', value: opts.serviceType },
240
- { label: 'Hash', value: String(deliverablesHash), last: !transcriptHash },
241
- ];
242
- if (transcriptHash) hireTreeItems.push({ label: 'Transcript', value: transcriptHash, last: true });
243
- renderTree(hireTreeItems);
244
- });
245
- }
@@ -1,177 +0,0 @@
1
- import { Command } from "commander";
2
- import { ethers } from "ethers";
3
- import { loadConfig } from "../config";
4
- import { getClient, requireSigner } from "../client";
5
- import { c } from '../ui/colors';
6
- import { startSpinner } from '../ui/spinner';
7
- import { renderTree } from '../ui/tree';
8
- import { formatAddress } from '../ui/format';
9
-
10
- const MIGRATION_REGISTRY_ABI = [
11
- "function registerMigration(address oldWallet, address newWallet) external",
12
- "function resolveActiveWallet(address wallet) external view returns (address)",
13
- "function getLineage(address wallet) external view returns (address[])",
14
- "function migratedTo(address wallet) external view returns (address)",
15
- "function migratedFrom(address wallet) external view returns (address)",
16
- "event MigrationRegistered(address indexed oldWallet, address indexed newWallet, address indexed owner, uint256 migratedAt, uint256 scoreAtMigration, uint256 appliedDecay)",
17
- ] as const;
18
-
19
- export function registerMigrateCommands(program: Command): void {
20
- const migrate = program
21
- .command("migrate")
22
- .description("Wallet migration — register, query status, or print lineage history")
23
- .argument("[oldWallet]", "old wallet address (required for registration)")
24
- .argument("[newWallet]", "new wallet address (required for registration)")
25
- .option("--json")
26
- .action(async (oldWallet, newWallet, opts) => {
27
- if (!oldWallet || !newWallet) {
28
- console.error("Usage: arc402 migrate <oldWallet> <newWallet>");
29
- console.error("Both wallets must share the same registered owner address.");
30
- process.exit(1);
31
- }
32
-
33
- const config = loadConfig();
34
- if (!config.migrationRegistryAddress) {
35
- console.error("migrationRegistryAddress not configured. Run `arc402 config set migrationRegistryAddress <address>`.");
36
- process.exit(1);
37
- }
38
-
39
- const { signer } = await requireSigner(config);
40
- const contract = new ethers.Contract(config.migrationRegistryAddress, MIGRATION_REGISTRY_ABI, signer);
41
-
42
- const migrateSpinner = startSpinner('Registering migration...');
43
- const tx = await contract.registerMigration(oldWallet, newWallet);
44
- const receipt = await tx.wait();
45
- migrateSpinner.succeed('Migration registered');
46
-
47
- const payload = {
48
- oldWallet,
49
- newWallet,
50
- txHash: receipt.hash,
51
- };
52
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
53
- renderTree([
54
- { label: 'Old', value: formatAddress(oldWallet) },
55
- { label: 'New', value: formatAddress(newWallet) },
56
- { label: 'Note', value: '10% trust score decay applied', last: true },
57
- ]);
58
- });
59
-
60
- // ─── migrate status <address> ──────────────────────────────────────────────
61
-
62
- migrate
63
- .command("status <address>")
64
- .description("Show whether a wallet is in a migration lineage and its current active wallet")
65
- .option("--json")
66
- .action(async (address, opts) => {
67
- const config = loadConfig();
68
- if (!config.migrationRegistryAddress) {
69
- console.error("migrationRegistryAddress not configured. Run `arc402 config set migrationRegistryAddress <address>`.");
70
- process.exit(1);
71
- }
72
-
73
- const { provider } = await getClient(config);
74
- const contract = new ethers.Contract(config.migrationRegistryAddress, MIGRATION_REGISTRY_ABI, provider);
75
-
76
- const [activeWallet, migratedTo, migratedFrom] = await Promise.all([
77
- contract.resolveActiveWallet(address),
78
- contract.migratedTo(address),
79
- contract.migratedFrom(address),
80
- ]);
81
-
82
- const isCurrent = activeWallet.toLowerCase() === address.toLowerCase();
83
- const hasMigrated = migratedTo !== ethers.ZeroAddress;
84
- const wasSource = migratedFrom !== ethers.ZeroAddress;
85
-
86
- const payload = {
87
- address,
88
- activeWallet,
89
- isCurrent,
90
- migratedTo: hasMigrated ? migratedTo : null,
91
- migratedFrom: wasSource ? migratedFrom : null,
92
- };
93
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
94
- const statusItems: import('../ui/tree').TreeItem[] = [
95
- { label: 'Address', value: formatAddress(address) },
96
- { label: 'Active', value: formatAddress(activeWallet) },
97
- { label: 'Status', value: isCurrent ? 'current (no further migration)' : `migrated — resolves to ${formatAddress(activeWallet)}` },
98
- ];
99
- if (hasMigrated) statusItems.push({ label: 'Migrated to', value: formatAddress(migratedTo) });
100
- if (wasSource) statusItems.push({ label: 'Migrated from', value: formatAddress(migratedFrom) });
101
- statusItems[statusItems.length - 1].last = true;
102
- renderTree(statusItems);
103
- });
104
-
105
- // ─── migrate lineage <address> ────────────────────────────────────────────
106
-
107
- migrate
108
- .command("lineage <address>")
109
- .description("Print full migration lineage history with timestamps")
110
- .option("--json")
111
- .action(async (address, opts) => {
112
- const config = loadConfig();
113
- if (!config.migrationRegistryAddress) {
114
- console.error("migrationRegistryAddress not configured. Run `arc402 config set migrationRegistryAddress <address>`.");
115
- process.exit(1);
116
- }
117
-
118
- const { provider } = await getClient(config);
119
- const contract = new ethers.Contract(config.migrationRegistryAddress, MIGRATION_REGISTRY_ABI, provider);
120
-
121
- const lineage: string[] = await contract.getLineage(address);
122
-
123
- if (lineage.length === 0) {
124
- const payload = { address, lineage: [], migrations: 0 };
125
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
126
- console.log(`address=${address}`);
127
- console.log(` no migration history`);
128
- return;
129
- }
130
-
131
- // Fetch MigrationRegistered events for timestamps and decay info
132
- interface MigrationEntry { step: number; from: string; to: string; timestamp: string | null; scoreAtMigration: string | null; decayBps: string | null }
133
- const entries: MigrationEntry[] = [];
134
-
135
- for (let i = 0; i < lineage.length - 1; i++) {
136
- const from = lineage[i];
137
- const to = lineage[i + 1];
138
- let timestamp: string | null = null;
139
- let scoreAtMigration: string | null = null;
140
- let decayBps: string | null = null;
141
-
142
- try {
143
- const filter = contract.filters.MigrationRegistered(from, to);
144
- const events = await contract.queryFilter(filter);
145
- if (events.length > 0) {
146
- const ev = events[0] as ethers.EventLog;
147
- const block = await provider.getBlock(ev.blockNumber);
148
- timestamp = block ? new Date(block.timestamp * 1000).toISOString() : null;
149
- scoreAtMigration = ev.args[3]?.toString() ?? null;
150
- decayBps = ev.args[4]?.toString() ?? null;
151
- }
152
- } catch {
153
- // event query not critical — continue without timestamps
154
- }
155
-
156
- entries.push({ step: i + 1, from, to, timestamp, scoreAtMigration, decayBps });
157
- }
158
-
159
- const payload = {
160
- address,
161
- lineage,
162
- migrations: entries.length,
163
- history: entries,
164
- };
165
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
166
-
167
- const lineageItems = lineage.map((addr, i) => {
168
- const roleLabel = i === 0 ? ' (origin)' : i === lineage.length - 1 ? ' (current)' : '';
169
- const e = i < entries.length ? entries[i] : null;
170
- let value = formatAddress(addr) + roleLabel;
171
- if (e?.timestamp) value += ` · ${e.timestamp}`;
172
- if (e?.scoreAtMigration) value += ` · score: ${e.scoreAtMigration} (decay: ${Number(e.decayBps) / 100}%)`;
173
- return { label: `[${i}]`, value, last: i === lineage.length - 1 };
174
- });
175
- renderTree(lineageItems);
176
- });
177
- }