arc402-cli 0.9.19 → 0.10.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 (359) 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 +45 -14
  5. package/dist/abis.js.map +1 -1
  6. package/dist/bundler.d.ts +1 -1
  7. package/dist/bundler.d.ts.map +1 -1
  8. package/dist/bundler.js +61 -27
  9. package/dist/bundler.js.map +1 -1
  10. package/dist/client.d.ts +1 -1
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +9 -5
  13. package/dist/client.js.map +1 -1
  14. package/dist/coinbase-smart-wallet.js +4 -1
  15. package/dist/coinbase-smart-wallet.js.map +1 -1
  16. package/dist/commands/accept.js +28 -25
  17. package/dist/commands/accept.js.map +1 -1
  18. package/dist/commands/agent-handshake.js +18 -15
  19. package/dist/commands/agent-handshake.js.map +1 -1
  20. package/dist/commands/agent.js +104 -98
  21. package/dist/commands/agent.js.map +1 -1
  22. package/dist/commands/agreements.js +98 -62
  23. package/dist/commands/agreements.js.map +1 -1
  24. package/dist/commands/arbitrator.js +81 -45
  25. package/dist/commands/arbitrator.js.map +1 -1
  26. package/dist/commands/arena-handshake.d.ts.map +1 -1
  27. package/dist/commands/arena-handshake.js +35 -53
  28. package/dist/commands/arena-handshake.js.map +1 -1
  29. package/dist/commands/arena.js +18 -12
  30. package/dist/commands/arena.js.map +1 -1
  31. package/dist/commands/backup.js +36 -30
  32. package/dist/commands/backup.js.map +1 -1
  33. package/dist/commands/cancel.js +18 -15
  34. package/dist/commands/cancel.js.map +1 -1
  35. package/dist/commands/channel.js +81 -45
  36. package/dist/commands/channel.js.map +1 -1
  37. package/dist/commands/coldstart.js +34 -31
  38. package/dist/commands/coldstart.js.map +1 -1
  39. package/dist/commands/compute.d.ts +14 -0
  40. package/dist/commands/compute.d.ts.map +1 -0
  41. package/dist/commands/compute.js +466 -0
  42. package/dist/commands/compute.js.map +1 -0
  43. package/dist/commands/config.js +30 -24
  44. package/dist/commands/config.js.map +1 -1
  45. package/dist/commands/contract-interaction.js +15 -12
  46. package/dist/commands/contract-interaction.js.map +1 -1
  47. package/dist/commands/daemon.d.ts.map +1 -1
  48. package/dist/commands/daemon.js +135 -98
  49. package/dist/commands/daemon.js.map +1 -1
  50. package/dist/commands/deliver.js +76 -37
  51. package/dist/commands/deliver.js.map +1 -1
  52. package/dist/commands/discover.js +27 -24
  53. package/dist/commands/discover.js.map +1 -1
  54. package/dist/commands/dispute.js +110 -104
  55. package/dist/commands/dispute.js.map +1 -1
  56. package/dist/commands/doctor.js +55 -16
  57. package/dist/commands/doctor.js.map +1 -1
  58. package/dist/commands/endpoint.js +95 -56
  59. package/dist/commands/endpoint.js.map +1 -1
  60. package/dist/commands/feed.js +18 -11
  61. package/dist/commands/feed.js.map +1 -1
  62. package/dist/commands/hire.js +40 -37
  63. package/dist/commands/hire.js.map +1 -1
  64. package/dist/commands/migrate.js +33 -30
  65. package/dist/commands/migrate.js.map +1 -1
  66. package/dist/commands/negotiate.d.ts.map +1 -1
  67. package/dist/commands/negotiate.js +36 -34
  68. package/dist/commands/negotiate.js.map +1 -1
  69. package/dist/commands/openshell.js +104 -68
  70. package/dist/commands/openshell.js.map +1 -1
  71. package/dist/commands/owner.js +20 -17
  72. package/dist/commands/owner.js.map +1 -1
  73. package/dist/commands/policy.js +43 -41
  74. package/dist/commands/policy.js.map +1 -1
  75. package/dist/commands/relay.d.ts.map +1 -1
  76. package/dist/commands/relay.js +51 -18
  77. package/dist/commands/relay.js.map +1 -1
  78. package/dist/commands/remediate.js +23 -20
  79. package/dist/commands/remediate.js.map +1 -1
  80. package/dist/commands/reputation.js +27 -25
  81. package/dist/commands/reputation.js.map +1 -1
  82. package/dist/commands/setup.js +104 -65
  83. package/dist/commands/setup.js.map +1 -1
  84. package/dist/commands/trust.js +20 -17
  85. package/dist/commands/trust.js.map +1 -1
  86. package/dist/commands/verify.js +21 -18
  87. package/dist/commands/verify.js.map +1 -1
  88. package/dist/commands/wallet.d.ts.map +1 -1
  89. package/dist/commands/wallet.js +645 -679
  90. package/dist/commands/wallet.js.map +1 -1
  91. package/dist/commands/watch.js +36 -33
  92. package/dist/commands/watch.js.map +1 -1
  93. package/dist/commands/watchtower.js +73 -37
  94. package/dist/commands/watchtower.js.map +1 -1
  95. package/dist/commands/workroom.d.ts.map +1 -1
  96. package/dist/commands/workroom.js +282 -143
  97. package/dist/commands/workroom.js.map +1 -1
  98. package/dist/config.d.ts +3 -0
  99. package/dist/config.d.ts.map +1 -1
  100. package/dist/config.js +71 -22
  101. package/dist/config.js.map +1 -1
  102. package/dist/daemon/compute-metering.d.ts +61 -0
  103. package/dist/daemon/compute-metering.d.ts.map +1 -0
  104. package/dist/daemon/compute-metering.js +299 -0
  105. package/dist/daemon/compute-metering.js.map +1 -0
  106. package/dist/daemon/compute-session.d.ts +100 -0
  107. package/dist/daemon/compute-session.d.ts.map +1 -0
  108. package/dist/daemon/compute-session.js +231 -0
  109. package/dist/daemon/compute-session.js.map +1 -0
  110. package/dist/daemon/config.d.ts +19 -1
  111. package/dist/daemon/config.d.ts.map +1 -1
  112. package/dist/daemon/config.js +90 -16
  113. package/dist/daemon/config.js.map +1 -1
  114. package/dist/daemon/credentials.d.ts +24 -0
  115. package/dist/daemon/credentials.d.ts.map +1 -0
  116. package/dist/daemon/credentials.js +80 -0
  117. package/dist/daemon/credentials.js.map +1 -0
  118. package/dist/daemon/delivery-client.d.ts +35 -0
  119. package/dist/daemon/delivery-client.d.ts.map +1 -0
  120. package/dist/daemon/delivery-client.js +231 -0
  121. package/dist/daemon/delivery-client.js.map +1 -0
  122. package/dist/daemon/file-delivery.d.ts +98 -0
  123. package/dist/daemon/file-delivery.d.ts.map +1 -0
  124. package/dist/daemon/file-delivery.js +461 -0
  125. package/dist/daemon/file-delivery.js.map +1 -0
  126. package/dist/daemon/hire-listener.d.ts +3 -3
  127. package/dist/daemon/hire-listener.d.ts.map +1 -1
  128. package/dist/daemon/hire-listener.js +47 -13
  129. package/dist/daemon/hire-listener.js.map +1 -1
  130. package/dist/daemon/index.d.ts +2 -1
  131. package/dist/daemon/index.d.ts.map +1 -1
  132. package/dist/daemon/index.js +526 -53
  133. package/dist/daemon/index.js.map +1 -1
  134. package/dist/daemon/job-lifecycle.d.ts +1 -1
  135. package/dist/daemon/job-lifecycle.d.ts.map +1 -1
  136. package/dist/daemon/job-lifecycle.js +51 -11
  137. package/dist/daemon/job-lifecycle.js.map +1 -1
  138. package/dist/daemon/notify.d.ts +1 -1
  139. package/dist/daemon/notify.d.ts.map +1 -1
  140. package/dist/daemon/notify.js +53 -19
  141. package/dist/daemon/notify.js.map +1 -1
  142. package/dist/daemon/token-metering.js +47 -8
  143. package/dist/daemon/token-metering.js.map +1 -1
  144. package/dist/daemon/userops.d.ts +2 -2
  145. package/dist/daemon/userops.d.ts.map +1 -1
  146. package/dist/daemon/userops.js +27 -23
  147. package/dist/daemon/userops.js.map +1 -1
  148. package/dist/daemon/wallet-monitor.d.ts +1 -1
  149. package/dist/daemon/wallet-monitor.d.ts.map +1 -1
  150. package/dist/daemon/wallet-monitor.js +12 -8
  151. package/dist/daemon/wallet-monitor.js.map +1 -1
  152. package/dist/daemon/worker-executor.d.ts +71 -0
  153. package/dist/daemon/worker-executor.d.ts.map +1 -0
  154. package/dist/daemon/worker-executor.js +382 -0
  155. package/dist/daemon/worker-executor.js.map +1 -0
  156. package/dist/drain-v4.js +64 -26
  157. package/dist/drain-v4.js.map +1 -1
  158. package/dist/endpoint-config.js +63 -20
  159. package/dist/endpoint-config.js.map +1 -1
  160. package/dist/endpoint-notify.d.ts.map +1 -1
  161. package/dist/endpoint-notify.js +49 -15
  162. package/dist/endpoint-notify.js.map +1 -1
  163. package/dist/index.js +50 -18
  164. package/dist/index.js.map +1 -1
  165. package/dist/openshell-runtime.d.ts.map +1 -1
  166. package/dist/openshell-runtime.js +82 -38
  167. package/dist/openshell-runtime.js.map +1 -1
  168. package/dist/program.d.ts.map +1 -1
  169. package/dist/program.js +85 -78
  170. package/dist/program.js.map +1 -1
  171. package/dist/repl.js +31 -25
  172. package/dist/repl.js.map +1 -1
  173. package/dist/signing.js +6 -3
  174. package/dist/signing.js.map +1 -1
  175. package/dist/telegram-notify.js +40 -3
  176. package/dist/telegram-notify.js.map +1 -1
  177. package/dist/tui/App.d.ts.map +1 -1
  178. package/dist/tui/App.js +56 -89
  179. package/dist/tui/App.js.map +1 -1
  180. package/dist/tui/Footer.js +7 -4
  181. package/dist/tui/Footer.js.map +1 -1
  182. package/dist/tui/Header.d.ts +1 -1
  183. package/dist/tui/Header.d.ts.map +1 -1
  184. package/dist/tui/Header.js +14 -9
  185. package/dist/tui/Header.js.map +1 -1
  186. package/dist/tui/InputLine.d.ts +2 -1
  187. package/dist/tui/InputLine.d.ts.map +1 -1
  188. package/dist/tui/InputLine.js +47 -97
  189. package/dist/tui/InputLine.js.map +1 -1
  190. package/dist/tui/Viewport.d.ts +1 -2
  191. package/dist/tui/Viewport.d.ts.map +1 -1
  192. package/dist/tui/Viewport.js +26 -6
  193. package/dist/tui/Viewport.js.map +1 -1
  194. package/dist/tui/WalletConnectPairing.js +19 -16
  195. package/dist/tui/WalletConnectPairing.js.map +1 -1
  196. package/dist/tui/components/Button.js +9 -6
  197. package/dist/tui/components/Button.js.map +1 -1
  198. package/dist/tui/components/CeremonyView.js +8 -5
  199. package/dist/tui/components/CeremonyView.js.map +1 -1
  200. package/dist/tui/components/CompletionDropdown.js +9 -6
  201. package/dist/tui/components/CompletionDropdown.js.map +1 -1
  202. package/dist/tui/components/ConfirmPrompt.js +8 -5
  203. package/dist/tui/components/ConfirmPrompt.js.map +1 -1
  204. package/dist/tui/components/CustomTextInput.js +14 -11
  205. package/dist/tui/components/CustomTextInput.js.map +1 -1
  206. package/dist/tui/components/InteractiveTable.js +12 -9
  207. package/dist/tui/components/InteractiveTable.js.map +1 -1
  208. package/dist/tui/components/StepSpinner.js +13 -10
  209. package/dist/tui/components/StepSpinner.js.map +1 -1
  210. package/dist/tui/components/Toast.js +12 -8
  211. package/dist/tui/components/Toast.js.map +1 -1
  212. package/dist/tui/index.d.ts.map +1 -1
  213. package/dist/tui/index.js +21 -28
  214. package/dist/tui/index.js.map +1 -1
  215. package/dist/tui/useChat.js +19 -13
  216. package/dist/tui/useChat.js.map +1 -1
  217. package/dist/tui/useCommand.d.ts +2 -3
  218. package/dist/tui/useCommand.d.ts.map +1 -1
  219. package/dist/tui/useCommand.js +24 -100
  220. package/dist/tui/useCommand.js.map +1 -1
  221. package/dist/tui/useNotifications.js +8 -5
  222. package/dist/tui/useNotifications.js.map +1 -1
  223. package/dist/tui/useScroll.d.ts.map +1 -1
  224. package/dist/tui/useScroll.js +12 -15
  225. package/dist/tui/useScroll.js.map +1 -1
  226. package/dist/ui/banner.d.ts +0 -12
  227. package/dist/ui/banner.d.ts.map +1 -1
  228. package/dist/ui/banner.js +19 -35
  229. package/dist/ui/banner.js.map +1 -1
  230. package/dist/ui/colors.js +19 -13
  231. package/dist/ui/colors.js.map +1 -1
  232. package/dist/ui/format.js +14 -6
  233. package/dist/ui/format.js.map +1 -1
  234. package/dist/ui/qr-render.js +11 -4
  235. package/dist/ui/qr-render.js.map +1 -1
  236. package/dist/ui/rpc-fallback.js +11 -6
  237. package/dist/ui/rpc-fallback.js.map +1 -1
  238. package/dist/ui/spinner.js +12 -6
  239. package/dist/ui/spinner.js.map +1 -1
  240. package/dist/ui/tree.js +6 -3
  241. package/dist/ui/tree.js.map +1 -1
  242. package/dist/utils/format.js +41 -27
  243. package/dist/utils/format.js.map +1 -1
  244. package/dist/utils/hash.js +42 -4
  245. package/dist/utils/hash.js.map +1 -1
  246. package/dist/utils/time.js +6 -2
  247. package/dist/utils/time.js.map +1 -1
  248. package/dist/wallet-router.d.ts +1 -1
  249. package/dist/wallet-router.d.ts.map +1 -1
  250. package/dist/wallet-router.js +19 -12
  251. package/dist/wallet-router.js.map +1 -1
  252. package/dist/walletconnect-session.d.ts +1 -1
  253. package/dist/walletconnect-session.d.ts.map +1 -1
  254. package/dist/walletconnect-session.js +11 -6
  255. package/dist/walletconnect-session.js.map +1 -1
  256. package/dist/walletconnect.d.ts +5 -6
  257. package/dist/walletconnect.d.ts.map +1 -1
  258. package/dist/walletconnect.js +35 -32
  259. package/dist/walletconnect.js.map +1 -1
  260. package/package.json +11 -10
  261. package/INK6-UX-SPEC.md +0 -446
  262. package/MIGRATION-SPEC.md +0 -108
  263. package/TUI-SPEC.md +0 -214
  264. package/scripts/authorize-machine-key.ts +0 -43
  265. package/scripts/drain-wallet.ts +0 -149
  266. package/scripts/execute-spend-only.ts +0 -81
  267. package/scripts/register-agent-userop.ts +0 -186
  268. package/src/abis.ts +0 -187
  269. package/src/bundler.ts +0 -235
  270. package/src/client.ts +0 -36
  271. package/src/coinbase-smart-wallet.ts +0 -51
  272. package/src/commands/accept.ts +0 -64
  273. package/src/commands/agent-handshake.ts +0 -72
  274. package/src/commands/agent.ts +0 -691
  275. package/src/commands/agreements.ts +0 -350
  276. package/src/commands/arbitrator.ts +0 -180
  277. package/src/commands/arena-handshake.ts +0 -274
  278. package/src/commands/arena.ts +0 -122
  279. package/src/commands/backup.ts +0 -117
  280. package/src/commands/cancel.ts +0 -35
  281. package/src/commands/channel.ts +0 -218
  282. package/src/commands/coldstart.ts +0 -165
  283. package/src/commands/config.ts +0 -68
  284. package/src/commands/contract-interaction.ts +0 -166
  285. package/src/commands/daemon.ts +0 -1054
  286. package/src/commands/deliver.ts +0 -148
  287. package/src/commands/discover.ts +0 -350
  288. package/src/commands/dispute.ts +0 -375
  289. package/src/commands/doctor.ts +0 -172
  290. package/src/commands/endpoint.ts +0 -620
  291. package/src/commands/feed.ts +0 -229
  292. package/src/commands/hire.ts +0 -245
  293. package/src/commands/migrate.ts +0 -177
  294. package/src/commands/negotiate.ts +0 -272
  295. package/src/commands/openshell.ts +0 -1055
  296. package/src/commands/owner.ts +0 -35
  297. package/src/commands/policy.ts +0 -263
  298. package/src/commands/relay.ts +0 -277
  299. package/src/commands/remediate.ts +0 -24
  300. package/src/commands/reputation.ts +0 -79
  301. package/src/commands/setup.ts +0 -343
  302. package/src/commands/trust.ts +0 -27
  303. package/src/commands/verify.ts +0 -91
  304. package/src/commands/wallet.ts +0 -3548
  305. package/src/commands/watch.ts +0 -220
  306. package/src/commands/watchtower.ts +0 -248
  307. package/src/commands/workroom.ts +0 -963
  308. package/src/config.ts +0 -220
  309. package/src/daemon/config.ts +0 -344
  310. package/src/daemon/hire-listener.ts +0 -226
  311. package/src/daemon/index.ts +0 -1089
  312. package/src/daemon/job-lifecycle.ts +0 -215
  313. package/src/daemon/notify.ts +0 -297
  314. package/src/daemon/token-metering.ts +0 -183
  315. package/src/daemon/userops.ts +0 -119
  316. package/src/daemon/wallet-monitor.ts +0 -90
  317. package/src/drain-v4.ts +0 -159
  318. package/src/endpoint-config.ts +0 -83
  319. package/src/endpoint-notify.ts +0 -134
  320. package/src/index.ts +0 -74
  321. package/src/openshell-runtime.ts +0 -281
  322. package/src/program.ts +0 -88
  323. package/src/repl.ts +0 -178
  324. package/src/signing.ts +0 -28
  325. package/src/telegram-notify.ts +0 -88
  326. package/src/tui/App.tsx +0 -263
  327. package/src/tui/Footer.tsx +0 -18
  328. package/src/tui/Header.tsx +0 -45
  329. package/src/tui/InputLine.tsx +0 -243
  330. package/src/tui/Viewport.tsx +0 -51
  331. package/src/tui/WalletConnectPairing.tsx +0 -114
  332. package/src/tui/components/Button.tsx +0 -38
  333. package/src/tui/components/CeremonyView.tsx +0 -39
  334. package/src/tui/components/CompletionDropdown.tsx +0 -56
  335. package/src/tui/components/ConfirmPrompt.tsx +0 -36
  336. package/src/tui/components/CustomTextInput.tsx +0 -132
  337. package/src/tui/components/InteractiveTable.tsx +0 -106
  338. package/src/tui/components/StepSpinner.tsx +0 -84
  339. package/src/tui/components/Toast.tsx +0 -59
  340. package/src/tui/index.tsx +0 -90
  341. package/src/tui/useChat.ts +0 -103
  342. package/src/tui/useCommand.ts +0 -238
  343. package/src/tui/useNotifications.ts +0 -28
  344. package/src/tui/useScroll.ts +0 -69
  345. package/src/ui/banner.ts +0 -78
  346. package/src/ui/colors.ts +0 -30
  347. package/src/ui/format.ts +0 -78
  348. package/src/ui/qr-render.ts +0 -92
  349. package/src/ui/rpc-fallback.ts +0 -59
  350. package/src/ui/spinner.ts +0 -56
  351. package/src/ui/tree.ts +0 -16
  352. package/src/utils/format.ts +0 -48
  353. package/src/utils/hash.ts +0 -5
  354. package/src/utils/time.ts +0 -15
  355. package/src/wallet-router.ts +0 -178
  356. package/src/walletconnect-session.ts +0 -27
  357. package/src/walletconnect.ts +0 -309
  358. package/test/time.test.js +0 -11
  359. package/tsconfig.json +0 -33
@@ -1,148 +0,0 @@
1
- import { Command } from "commander";
2
- import { ServiceAgreementClient, uploadEncryptedIPFS } from "@arc402/sdk";
3
- import { ethers } from "ethers";
4
- import { loadConfig } from "../config.js";
5
- import { getClient, requireSigner } from "../client.js";
6
- import { hashFile, hashString } from "../utils/hash.js";
7
- import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router.js";
8
- import { SERVICE_AGREEMENT_ABI } from "../abis.js";
9
- import { readFile } from "fs/promises";
10
- import prompts from "prompts";
11
- import { resolveAgentEndpoint, notifyAgent, DEFAULT_REGISTRY_ADDRESS } from "../endpoint-notify.js";
12
- import { c } from '../ui/colors.js';
13
- import { startSpinner } from '../ui/spinner.js';
14
- import { renderTree } from '../ui/tree.js';
15
-
16
- export function registerDeliverCommand(program: Command): void {
17
- program
18
- .command("deliver <id>")
19
- .description("Provider commits a deliverable for verification; legacy fulfill mode is compatibility-only")
20
- .option("--output <filepath>")
21
- .option("--message <text>")
22
- .option("--fulfill", "Use legacy trusted-only fulfill() instead of commitDeliverable()", false)
23
- .option("--encrypt", "Encrypt the deliverable before uploading to IPFS (prompts for recipient public key)", false)
24
- .action(async (id, opts) => {
25
- const config = loadConfig();
26
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
27
- const { signer, address: signerAddress } = await requireSigner(config);
28
- printSenderInfo(config);
29
-
30
- // Pre-flight: check deadline and legacyFulfillEnabled (J3-01, J3-02)
31
- let clientAddress = "";
32
- {
33
- const { provider: prefProvider } = await getClient(config);
34
- const saAbi = [
35
- "function getAgreement(uint256 id) external view returns (tuple(uint256 id, address client, address provider, string serviceType, string description, uint256 price, address token, uint256 deadline, bytes32 deliverablesHash, uint8 status, uint256 createdAt, uint256 resolvedAt, uint256 verifyWindowEnd, bytes32 committedHash))",
36
- "function legacyFulfillEnabled() external view returns (bool)",
37
- "function legacyFulfillProviders(address) external view returns (bool)",
38
- ];
39
- const saContract = new ethers.Contract(config.serviceAgreementAddress!, saAbi, prefProvider);
40
- try {
41
- const ag = await saContract.getAgreement(BigInt(id));
42
- const deadline = Number(ag.deadline);
43
- const nowSec = Math.floor(Date.now() / 1000);
44
- if (nowSec > deadline) {
45
- const deadlineDate = new Date(deadline * 1000).toISOString();
46
- console.error(`Delivery deadline has passed (${deadlineDate}). This transaction will revert.`);
47
- console.error(`Contact the client to open a new agreement.`);
48
- process.exit(1);
49
- }
50
- clientAddress = String(ag.client ?? "");
51
- } catch (e) {
52
- // If it's a contract read failure, skip the check (let the tx reveal the error)
53
- if (e instanceof Error && !e.message.includes("CALL_EXCEPTION") && !e.message.includes("could not decode")) throw e;
54
- }
55
-
56
- if (opts.fulfill) {
57
- let legacyEnabled = false;
58
- try {
59
- legacyEnabled = await saContract.legacyFulfillEnabled();
60
- } catch { /* assume enabled if read fails */ legacyEnabled = true; }
61
- if (!legacyEnabled) {
62
- console.error("Legacy fulfill is disabled on this deployment. Use `arc402 deliver <id>` (without --fulfill) to deliver via the standard flow.");
63
- process.exit(1);
64
- }
65
- let isLegacyProvider = false;
66
- try {
67
- isLegacyProvider = await saContract.legacyFulfillProviders(signerAddress);
68
- } catch { /* assume allowed if read fails */ isLegacyProvider = true; }
69
- if (!isLegacyProvider) {
70
- console.error("You are not in the legacy fulfill providers list for this agreement.");
71
- process.exit(1);
72
- }
73
- }
74
- }
75
-
76
- if (opts.encrypt) {
77
- if (!opts.output) throw new Error("--encrypt requires --output <filepath>");
78
-
79
- const { recipientPubKeyHex } = await prompts({
80
- type: "text",
81
- name: "recipientPubKeyHex",
82
- message: "Recipient NaCl box public key (32 bytes, hex):",
83
- validate: (v: string) => /^[0-9a-fA-F]{64}$/.test(v.trim()) || "Must be 64 hex characters (32 bytes)",
84
- });
85
- if (!recipientPubKeyHex) throw new Error("Recipient public key is required for encrypted delivery");
86
-
87
- const recipientPublicKey = Uint8Array.from(Buffer.from(recipientPubKeyHex.trim(), "hex"));
88
- const buffer = await readFile(opts.output);
89
- const { cid, uri } = await uploadEncryptedIPFS(buffer, recipientPublicKey);
90
- // Hash plaintext so recipient can verify integrity after decryption
91
- const { ethers } = await import("ethers");
92
- const hash = ethers.keccak256(buffer);
93
-
94
- console.log(' ' + c.dim('Encrypted upload:') + ' ' + c.white(uri));
95
-
96
- const encSpinner = startSpinner('Committing deliverable…');
97
- if (config.walletContractAddress) {
98
- await executeContractWriteViaWallet(
99
- config.walletContractAddress, signer, config.serviceAgreementAddress,
100
- SERVICE_AGREEMENT_ABI, "commitDeliverable", [BigInt(id), hash],
101
- );
102
- } else {
103
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
104
- await client.commitDeliverable(BigInt(id), hash);
105
- }
106
- encSpinner.succeed(` Delivered — agreement #${id}`);
107
- renderTree([
108
- { label: 'Hash', value: hash },
109
- { label: 'CID', value: cid },
110
- { label: 'URI', value: uri, last: true },
111
- ]);
112
- return;
113
- }
114
-
115
- const hash = opts.output ? hashFile(opts.output) : hashString(opts.message ?? `agreement:${id}`);
116
- const deliverSpinner = startSpinner(`${opts.fulfill ? 'Fulfilling' : 'Committing deliverable for'} agreement #${id}…`);
117
- if (config.walletContractAddress) {
118
- const fn = opts.fulfill ? "fulfill" : "commitDeliverable";
119
- await executeContractWriteViaWallet(
120
- config.walletContractAddress, signer, config.serviceAgreementAddress,
121
- SERVICE_AGREEMENT_ABI, fn, [BigInt(id), hash],
122
- );
123
- } else {
124
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
125
- if (opts.fulfill) await client.fulfill(BigInt(id), hash); else await client.commitDeliverable(BigInt(id), hash);
126
- }
127
- deliverSpinner.succeed(` ${opts.fulfill ? 'Fulfilled' : 'Delivered'} — agreement #${id}`);
128
- renderTree([
129
- { label: 'Hash', value: hash, last: true },
130
- ]);
131
-
132
- // Notify client's HTTP endpoint (non-blocking)
133
- if (clientAddress) {
134
- try {
135
- const notifyProvider = new ethers.JsonRpcProvider(config.rpcUrl);
136
- const registryAddress = config.agentRegistryV2Address ?? config.agentRegistryAddress ?? DEFAULT_REGISTRY_ADDRESS;
137
- const endpoint = await resolveAgentEndpoint(clientAddress, notifyProvider, registryAddress);
138
- await notifyAgent(endpoint, "/delivery", {
139
- agreementId: id,
140
- deliverableHash: hash,
141
- from: signerAddress,
142
- });
143
- } catch (err) {
144
- console.warn(`Warning: could not notify client endpoint: ${err instanceof Error ? err.message : String(err)}`);
145
- }
146
- }
147
- });
148
- }
@@ -1,350 +0,0 @@
1
- import { Command } from "commander";
2
- import { ethers } from "ethers";
3
- import { AgentRegistryClient, CapabilityRegistryClient, ReputationOracleClient, SponsorshipAttestationClient } from "@arc402/sdk";
4
- import { loadConfig } from "../config.js";
5
- import { getClient } from "../client.js";
6
- import { getTrustTier, printTable, truncateAddress } from "../utils/format.js";
7
- import { c } from '../ui/colors.js';
8
- import { renderTree } from '../ui/tree.js';
9
-
10
- // Minimal ABI for the new getAgentsWithCapability function (Spec 18)
11
- const CAPABILITY_REGISTRY_EXTRA_ABI = [
12
- "function getAgentsWithCapability(string calldata capability) external view returns (address[])",
13
- ];
14
-
15
- // ─── Composite scoring helpers ────────────────────────────────────────────────
16
-
17
- interface ScoredAgent {
18
- wallet: string;
19
- name: string;
20
- serviceType: string;
21
- endpoint: string;
22
- metadataURI: string;
23
- capabilities: string[];
24
- canonicalCapabilities: string[];
25
- trustScore: number;
26
- stake: bigint;
27
- completedJobs: number;
28
- priceUsd: number | null;
29
- compositeScore: number;
30
- rank: number;
31
- operational: { uptimeScore: number; responseScore: number };
32
- highestTier?: unknown;
33
- reputation?: unknown;
34
- }
35
-
36
- function normalise(values: number[]): number[] {
37
- const min = Math.min(...values);
38
- const max = Math.max(...values);
39
- const range = max - min;
40
- if (range === 0) return values.map(() => 1);
41
- return values.map((v) => (v - min) / range);
42
- }
43
-
44
- function computeCompositeScores(agents: Omit<ScoredAgent, "compositeScore" | "rank">[]): ScoredAgent[] {
45
- if (agents.length === 0) return [];
46
-
47
- const trustVals = normalise(agents.map((a) => a.trustScore));
48
- const stakeVals = normalise(agents.map((a) => Number(a.stake)));
49
- const jobsVals = normalise(agents.map((a) => a.completedJobs));
50
-
51
- // Price: lower is better — invert. Missing price gets neutral 0.5.
52
- const rawPrices = agents.map((a) => (a.priceUsd !== null ? a.priceUsd : -1));
53
- const validPrices = rawPrices.filter((p) => p >= 0);
54
- const maxPrice = validPrices.length > 0 ? Math.max(...validPrices) : 1;
55
- const priceInvVals = rawPrices.map((p) =>
56
- p < 0 ? 0.5 : maxPrice > 0 ? 1 - p / maxPrice : 1
57
- );
58
-
59
- return agents.map((agent, i) => ({
60
- ...agent,
61
- compositeScore:
62
- trustVals[i] * 0.5 +
63
- stakeVals[i] * 0.2 +
64
- jobsVals[i] * 0.2 +
65
- priceInvVals[i] * 0.1,
66
- rank: 0, // filled after sort
67
- }));
68
- }
69
-
70
- // ─── Endpoint health check ────────────────────────────────────────────────────
71
-
72
- async function pingEndpoint(endpoint: string): Promise<"online" | "offline"> {
73
- if (!endpoint || !/^https?:\/\//.test(endpoint)) return "offline";
74
- try {
75
- const ctrl = new AbortController();
76
- const tid = setTimeout(() => ctrl.abort(), 3000);
77
- const resp = await fetch(`${endpoint.replace(/\/$/, "")}/health`, { signal: ctrl.signal });
78
- clearTimeout(tid);
79
- return resp.ok ? "online" : "offline";
80
- } catch {
81
- return "offline";
82
- }
83
- }
84
-
85
- // ─── Command ──────────────────────────────────────────────────────────────────
86
-
87
- export function registerDiscoverCommand(program: Command): void {
88
- program
89
- .command("discover")
90
- .description(
91
- "Discover agents by capability with trust/price/stake filters and composite ranking (Specs 16, 18)"
92
- )
93
- .option("--capability <cap>", "Exact canonical capability (e.g. legal.patent-analysis.us.v1)")
94
- .option("--capability-prefix <pfx>", "Prefix match against registered capabilities")
95
- .option("--service-type <type>", "Filter by serviceType substring")
96
- .option("--type <type>", "Filter by serviceType substring (alias for --service-type)")
97
- .option("--min-trust <score>", "Minimum trust score", "0")
98
- .option("--max-price <usd>", "Maximum price in USD (from agent metadataURI, best-effort)", "0")
99
- .option("--min-stake <wei>", "Minimum stake in wei", "0")
100
- .option("--top <n>", "Show top N agents by trust score")
101
- .option("--sort <field>", "Sort by: trust | price | jobs | stake | composite", "composite")
102
- .option("--limit <n>", "Max results", "20")
103
- .option("--online", "Only show agents whose /health endpoint responds")
104
- .option("--json", "Machine-parseable output")
105
- .action(async (opts) => {
106
- const config = loadConfig();
107
- if (!config.agentRegistryAddress) throw new Error("agentRegistryAddress missing in config");
108
- const { provider } = await getClient(config);
109
-
110
- const registry = new AgentRegistryClient(config.agentRegistryAddress, provider);
111
- const capabilitySDK = config.capabilityRegistryAddress
112
- ? new CapabilityRegistryClient(config.capabilityRegistryAddress, provider)
113
- : null;
114
- const sponsorship = config.sponsorshipAttestationAddress
115
- ? new SponsorshipAttestationClient(config.sponsorshipAttestationAddress, provider)
116
- : null;
117
- const reputation = config.reputationOracleAddress
118
- ? new ReputationOracleClient(config.reputationOracleAddress, provider)
119
- : null;
120
-
121
- // --top <n> sets sort to trust and overrides limit
122
- const effectiveSort = opts.top ? "trust" : opts.sort;
123
- const limit = opts.top ? Number(opts.top) : Number(opts.limit);
124
- const minTrust = Number(opts.minTrust);
125
- const maxPriceUsd = Number(opts.maxPrice); // 0 = no filter
126
- const minStakeWei = BigInt(opts.minStake);
127
- // --type is an alias for --service-type
128
- if (opts.type && !opts.serviceType) opts.serviceType = opts.type;
129
-
130
- // ── Step 1: Get candidate addresses ─────────────────────────────────────
131
-
132
- let candidateAddresses: string[] | null = null;
133
-
134
- if (opts.capability && config.capabilityRegistryAddress) {
135
- // Use the reverse index for O(1) exact-match (Spec 18: getAgentsWithCapability)
136
- const capContract = new ethers.Contract(
137
- config.capabilityRegistryAddress,
138
- CAPABILITY_REGISTRY_EXTRA_ABI,
139
- provider
140
- );
141
- try {
142
- const addrs: string[] = await capContract.getAgentsWithCapability(opts.capability);
143
- candidateAddresses = addrs;
144
- } catch {
145
- // Contract may not have been upgraded yet; fall through to listAgents
146
- }
147
- }
148
-
149
- // ── Step 2: Load agent data ───────────────────────────────────────────
150
-
151
- let agentInfos: Awaited<ReturnType<typeof registry.listAgents>>;
152
-
153
- if (candidateAddresses !== null) {
154
- const results = await Promise.allSettled(
155
- candidateAddresses.map((addr) => registry.getAgent(addr))
156
- );
157
- agentInfos = results
158
- .filter((r): r is PromiseFulfilledResult<typeof agentInfos[number]> => r.status === "fulfilled")
159
- .map((r) => r.value);
160
- } else {
161
- agentInfos = await registry.listAgents(limit * 10);
162
- }
163
-
164
- // ── Step 3: Apply filters ────────────────────────────────────────────
165
-
166
- let filtered = agentInfos.filter((a) => a.active !== false);
167
-
168
- if (opts.capability) {
169
- filtered = filtered.filter((a) =>
170
- a.capabilities.some((c: string) => c === opts.capability)
171
- );
172
- }
173
-
174
- if (opts.capabilityPrefix) {
175
- filtered = filtered.filter((a) =>
176
- a.capabilities.some((c: string) => c.startsWith(opts.capabilityPrefix as string))
177
- );
178
- }
179
-
180
- if (opts.serviceType) {
181
- filtered = filtered.filter((a) =>
182
- a.serviceType.toLowerCase().includes(String(opts.serviceType).toLowerCase())
183
- );
184
- }
185
-
186
- filtered = filtered.filter((a) => Number(a.trustScore ?? 0n) >= minTrust);
187
-
188
- if (minStakeWei > 0n) {
189
- filtered = filtered.filter((a) => {
190
- const stake = (a as unknown as { stake?: bigint }).stake ?? 0n;
191
- return BigInt(stake) >= minStakeWei;
192
- });
193
- }
194
-
195
- // ── Step 4: Enrich ──────────────────────────────────────────────────
196
-
197
- const enriched = await Promise.all(
198
- filtered.slice(0, limit * 5).map(async (agent) => {
199
- const [operational, canonicalCapabilities, highestTier, rep] = await Promise.all([
200
- registry.getOperationalMetrics(agent.wallet),
201
- capabilitySDK ? capabilitySDK.getCapabilities(agent.wallet) : Promise.resolve([]),
202
- sponsorship ? sponsorship.getHighestTier(agent.wallet) : Promise.resolve(undefined),
203
- reputation ? reputation.getReputation(agent.wallet) : Promise.resolve(undefined),
204
- ]);
205
-
206
- // Best-effort metadata fetch for priceUsd
207
- let priceUsd: number | null = null;
208
- if (agent.metadataURI && /^https?:\/\//.test(agent.metadataURI)) {
209
- try {
210
- const ctrl = new AbortController();
211
- const tid = setTimeout(() => ctrl.abort(), 2000);
212
- const resp = await fetch(agent.metadataURI, { signal: ctrl.signal });
213
- clearTimeout(tid);
214
- if (resp.ok) {
215
- const meta = await resp.json() as { pricing?: { priceUsd?: number } };
216
- if (meta.pricing?.priceUsd != null) priceUsd = meta.pricing.priceUsd;
217
- }
218
- } catch { /* ignore — advisory only */ }
219
- }
220
-
221
- // Apply max-price filter post-enrichment
222
- if (maxPriceUsd > 0 && priceUsd !== null && priceUsd > maxPriceUsd) {
223
- return null;
224
- }
225
-
226
- const repObj = rep && typeof rep === "object" ? (rep as unknown) as Record<string, unknown> : {};
227
-
228
- return {
229
- wallet: agent.wallet,
230
- name: agent.name,
231
- serviceType: agent.serviceType,
232
- endpoint: agent.endpoint,
233
- metadataURI: agent.metadataURI,
234
- capabilities: agent.capabilities as string[],
235
- canonicalCapabilities: canonicalCapabilities as string[],
236
- trustScore: Number(agent.trustScore ?? 0n),
237
- stake: (agent as unknown as { stake?: bigint }).stake ?? 0n,
238
- completedJobs: Number(repObj.completedJobs ?? 0),
239
- priceUsd,
240
- compositeScore: 0,
241
- rank: 0,
242
- operational: {
243
- uptimeScore: Number(operational.uptimeScore),
244
- responseScore: Number(operational.responseScore),
245
- },
246
- highestTier,
247
- reputation: rep,
248
- } as Omit<ScoredAgent, "compositeScore" | "rank">;
249
- })
250
- );
251
-
252
- const validAgents = enriched.filter((a): a is Omit<ScoredAgent, "compositeScore" | "rank"> => a !== null);
253
-
254
- // ── Step 5: Score ──────────────────────────────────────────────────────
255
-
256
- let scored = computeCompositeScores(validAgents);
257
-
258
- // Sort
259
- switch (effectiveSort) {
260
- case "trust":
261
- scored.sort((a, b) => b.trustScore - a.trustScore);
262
- break;
263
- case "price":
264
- scored.sort((a, b) => {
265
- if (a.priceUsd === null && b.priceUsd === null) return 0;
266
- if (a.priceUsd === null) return 1;
267
- if (b.priceUsd === null) return -1;
268
- return a.priceUsd - b.priceUsd;
269
- });
270
- break;
271
- case "jobs":
272
- scored.sort((a, b) => b.completedJobs - a.completedJobs);
273
- break;
274
- case "stake":
275
- scored.sort((a, b) => (b.stake > a.stake ? 1 : b.stake < a.stake ? -1 : 0));
276
- break;
277
- default: // "composite"
278
- scored.sort((a, b) => b.compositeScore - a.compositeScore);
279
- }
280
-
281
- // Assign 1-based ranks after sort
282
- scored = scored.slice(0, limit).map((a, i) => ({ ...a, rank: i + 1 }));
283
-
284
- // ── Step 6: Endpoint health checks ────────────────────────────────────
285
-
286
- type ScoredWithStatus = ScoredAgent & { endpointStatus: "online" | "offline" | "unknown" };
287
-
288
- let withStatus: ScoredWithStatus[];
289
-
290
- if (opts.online || /* always ping for tree display */ true) {
291
- const statuses = await Promise.all(
292
- scored.map(async (agent) => {
293
- if (!agent.endpoint) return "unknown" as const;
294
- return pingEndpoint(agent.endpoint);
295
- })
296
- );
297
- withStatus = scored.map((agent, i) => ({ ...agent, endpointStatus: statuses[i] as "online" | "offline" | "unknown" }));
298
- } else {
299
- withStatus = scored.map((agent) => ({ ...agent, endpointStatus: "unknown" as const }));
300
- }
301
-
302
- // Apply --online filter
303
- if (opts.online) {
304
- withStatus = withStatus.filter((a) => a.endpointStatus === "online");
305
- if (withStatus.length === 0) {
306
- console.log(`\n ${c.warning} No agents with responding /health endpoints found.`);
307
- return;
308
- }
309
- }
310
-
311
- // ── Step 7: Output ─────────────────────────────────────────────────────
312
-
313
- if (opts.json) {
314
- return console.log(JSON.stringify(
315
- withStatus,
316
- (_k, value) => typeof value === "bigint" ? value.toString() : value,
317
- 2
318
- ));
319
- }
320
-
321
- const onlineCount = withStatus.filter((a) => a.endpointStatus === "online").length;
322
- console.log('\n ' + c.mark + c.white(' Discover Results') + c.dim(` — ${withStatus.length} agent${withStatus.length !== 1 ? 's' : ''} found, ${onlineCount} online`));
323
-
324
- // Tree output per agent
325
- for (const agent of withStatus) {
326
- const caps = (agent.canonicalCapabilities.length
327
- ? agent.canonicalCapabilities
328
- : agent.capabilities
329
- ).slice(0, 3).join(", ") || c.dim("none");
330
-
331
- const statusIcon = agent.endpointStatus === "online"
332
- ? c.green("● online")
333
- : agent.endpointStatus === "offline"
334
- ? c.red("○ offline")
335
- : c.dim("? unknown");
336
-
337
- const tierStr = getTrustTier(agent.trustScore);
338
-
339
- console.log(`\n ${c.dim(`#${agent.rank}`)} ${c.white(agent.name)} ${c.dim(truncateAddress(agent.wallet))}`);
340
- renderTree([
341
- { label: "service", value: agent.serviceType },
342
- { label: "trust", value: `${agent.trustScore} ${tierStr}` },
343
- { label: "score", value: agent.compositeScore.toFixed(3) },
344
- { label: "caps", value: caps },
345
- { label: "endpoint", value: statusIcon, last: true },
346
- ]);
347
- }
348
- console.log();
349
- });
350
- }