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,375 +0,0 @@
1
- import { Command } from "commander";
2
- import { ethers } from "ethers";
3
- import prompts from "prompts";
4
- import { ArbitrationVote, DirectDisputeReason, DisputeClass, DisputeMode, DisputeOutcome, EvidenceType, ServiceAgreementClient, DisputeArbitrationClient } from "@arc402/sdk";
5
- import { loadConfig } from "../config";
6
- import { getClient, requireSigner } from "../client";
7
- import { hashFile, hashString } from "../utils/hash";
8
- import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router";
9
- import { SERVICE_AGREEMENT_ABI } from "../abis";
10
- import { c } from '../ui/colors';
11
- import { startSpinner } from '../ui/spinner';
12
-
13
- export function registerDisputeCommand(program: Command): void {
14
- const dispute = program.command("dispute").description("Formal dispute workflow; remediation-first by default, with narrow hard-fail direct-dispute exceptions");
15
-
16
- // Fee quote (requires DisputeArbitration configured) — read-only, no wallet routing needed
17
- dispute.command("fee-quote <agreementId>")
18
- .description("Get dispute fee quote for an agreement")
19
- .requiredOption("--price <price>", "Agreement price in wei/token units")
20
- .requiredOption("--token <token>", "Token address (0x0 for ETH)")
21
- .requiredOption("--mode <mode>", "unilateral|mutual")
22
- .requiredOption("--class <class>", "hard-failure|ambiguity|high-sensitivity")
23
- .action(async (agreementId, opts) => {
24
- const config = loadConfig();
25
- if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
26
- const { provider } = await getClient(config);
27
- const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, provider);
28
- const modeMap: Record<string, DisputeMode> = { unilateral: DisputeMode.UNILATERAL, mutual: DisputeMode.MUTUAL };
29
- const classMap: Record<string, DisputeClass> = {
30
- 'hard-failure': DisputeClass.HARD_FAILURE,
31
- 'ambiguity': DisputeClass.AMBIGUITY_QUALITY,
32
- 'high-sensitivity': DisputeClass.HIGH_SENSITIVITY,
33
- };
34
- const mode = modeMap[String(opts.mode).toLowerCase()];
35
- const disputeClass = classMap[String(opts.class).toLowerCase()];
36
- if (!mode || !disputeClass) throw new Error("Invalid --mode or --class");
37
- const feeInTokens = await client.getFeeQuote(BigInt(opts.price), opts.token, mode, disputeClass);
38
- console.log(' ' + c.mark + c.white(` Fee quote — agreement #${agreementId}: ${feeInTokens.toString()} tokens`));
39
- });
40
-
41
- // Open with explicit mode/class
42
- dispute.command("open-with-mode <agreementId>")
43
- .description("Open dispute with specific mode and class (requires fee in msg.value for ETH)")
44
- .requiredOption("--mode <mode>", "unilateral|mutual")
45
- .requiredOption("--class <class>", "hard-failure|ambiguity|high-sensitivity")
46
- .requiredOption("--reason <reason>")
47
- .option("--fee <fee>", "Fee in wei (for ETH agreements)", "0")
48
- .action(async (agreementId, opts) => {
49
- const config = loadConfig();
50
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
51
- const { signer } = await requireSigner(config);
52
- const modeMap: Record<string, DisputeMode> = { unilateral: DisputeMode.UNILATERAL, mutual: DisputeMode.MUTUAL };
53
- const classMap: Record<string, DisputeClass> = {
54
- 'hard-failure': DisputeClass.HARD_FAILURE,
55
- 'ambiguity': DisputeClass.AMBIGUITY_QUALITY,
56
- 'high-sensitivity': DisputeClass.HIGH_SENSITIVITY,
57
- };
58
- const mode = modeMap[String(opts.mode).toLowerCase()];
59
- const disputeClass = classMap[String(opts.class).toLowerCase()];
60
- if (!mode || !disputeClass) throw new Error("Invalid --mode or --class");
61
- printSenderInfo(config);
62
- if (config.walletContractAddress) {
63
- await executeContractWriteViaWallet(
64
- config.walletContractAddress, signer, config.serviceAgreementAddress,
65
- SERVICE_AGREEMENT_ABI, "openDisputeWithMode",
66
- [BigInt(agreementId), mode, disputeClass, opts.reason],
67
- BigInt(opts.fee),
68
- );
69
- } else {
70
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
71
- await client.openDisputeWithMode(BigInt(agreementId), mode, disputeClass, opts.reason, BigInt(opts.fee));
72
- }
73
- console.log(' ' + c.success + c.white(` Dispute opened — agreement #${agreementId}`));
74
- });
75
-
76
- // Join mutual dispute (respondent pays their half) — DisputeArbitration contract, no wallet routing
77
- dispute.command("join <agreementId>")
78
- .description("Join a mutual dispute as respondent (pays half the fee)")
79
- .option("--fee <fee>", "Half-fee in wei (for ETH agreements)", "0")
80
- .action(async (agreementId, opts) => {
81
- const config = loadConfig();
82
- if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
83
- const { signer } = await requireSigner(config);
84
- const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
85
- await client.joinMutualDispute(BigInt(agreementId), BigInt(opts.fee));
86
- console.log(' ' + c.success + c.white(` Joined mutual dispute — agreement #${agreementId}`));
87
- });
88
-
89
- dispute.command("open <id>")
90
- .requiredOption("--reason <reason>")
91
- .option("--escalated", "Use escalateToDispute after remediation", false)
92
- .option("--direct <type>", "Direct-dispute hard-fail reason: no-delivery|deadline-breach|invalid-deliverable|safety-critical")
93
- .action(async (id, opts) => {
94
- const config = loadConfig();
95
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
96
-
97
- // Pre-flight: check disputeModule is configured (J4-01)
98
- {
99
- const { provider: dpProvider } = await getClient(config);
100
- const saCheck = new ethers.Contract(
101
- config.serviceAgreementAddress,
102
- ["function disputeModule() external view returns (address)"],
103
- dpProvider,
104
- );
105
- let disputeModuleAddr: string = ethers.ZeroAddress;
106
- try {
107
- disputeModuleAddr = await saCheck.disputeModule();
108
- } catch { /* assume not configured */ }
109
- if (disputeModuleAddr === ethers.ZeroAddress) {
110
- console.error(`No dispute module configured on this ServiceAgreement.`);
111
- console.error(`Disputes require a DisputeModule to be set by the SA owner.`);
112
- console.error(`This protocol deployment may not support formal disputes.`);
113
- process.exit(1);
114
- }
115
- }
116
-
117
- // Pre-flight: read dispute fee and prompt user (J4-02)
118
- if (config.disputeArbitrationAddress) {
119
- const { provider: feeProvider } = await getClient(config);
120
- const daCheck = new ethers.Contract(
121
- config.disputeArbitrationAddress,
122
- ["function getDisputeFee() external view returns (uint256)"],
123
- feeProvider,
124
- );
125
- let feeWei = 0n;
126
- try {
127
- feeWei = await daCheck.getDisputeFee();
128
- } catch { /* fee getter may not exist — assume 0 */ }
129
- if (feeWei > 0n) {
130
- const feeEth = ethers.formatEther(feeWei);
131
- console.log(`\nDispute fee: ${feeEth} ETH. This will be deducted from your wallet.`);
132
- const { proceed } = await prompts({
133
- type: "confirm",
134
- name: "proceed",
135
- message: "Continue?",
136
- initial: true,
137
- });
138
- if (!proceed) { console.log("Aborted."); process.exit(0); }
139
- }
140
- }
141
-
142
- const { signer } = await requireSigner(config);
143
- const directMap: Record<string, DirectDisputeReason> = {
144
- 'no-delivery': DirectDisputeReason.NO_DELIVERY,
145
- 'deadline-breach': DirectDisputeReason.HARD_DEADLINE_BREACH,
146
- 'invalid-deliverable': DirectDisputeReason.INVALID_OR_FRAUDULENT_DELIVERABLE,
147
- 'safety-critical': DirectDisputeReason.SAFETY_CRITICAL_VIOLATION,
148
- };
149
- if (opts.escalated && opts.direct) throw new Error('Choose either --escalated or --direct, not both');
150
- printSenderInfo(config);
151
- if (config.walletContractAddress) {
152
- if (opts.direct) {
153
- const directReason = directMap[String(opts.direct).toLowerCase()];
154
- if (directReason === undefined) throw new Error('Unsupported --direct reason');
155
- await executeContractWriteViaWallet(
156
- config.walletContractAddress, signer, config.serviceAgreementAddress,
157
- SERVICE_AGREEMENT_ABI, "directDispute", [BigInt(id), directReason, opts.reason],
158
- );
159
- } else if (opts.escalated) {
160
- await executeContractWriteViaWallet(
161
- config.walletContractAddress, signer, config.serviceAgreementAddress,
162
- SERVICE_AGREEMENT_ABI, "escalateToDispute", [BigInt(id), opts.reason],
163
- );
164
- } else {
165
- await executeContractWriteViaWallet(
166
- config.walletContractAddress, signer, config.serviceAgreementAddress,
167
- SERVICE_AGREEMENT_ABI, "dispute", [BigInt(id), opts.reason],
168
- );
169
- }
170
- } else {
171
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
172
- if (opts.direct) {
173
- const directReason = directMap[String(opts.direct).toLowerCase()];
174
- if (directReason === undefined) throw new Error('Unsupported --direct reason');
175
- await client.directDispute(BigInt(id), directReason, opts.reason);
176
- } else if (opts.escalated) {
177
- await client.escalateToDispute(BigInt(id), opts.reason);
178
- } else {
179
- await client.dispute(BigInt(id), opts.reason);
180
- }
181
- }
182
- console.log(' ' + c.success + c.white(` Dispute opened — agreement #${id}`));
183
-
184
- // J4-04: Display arbitration selection window deadline
185
- try {
186
- const { provider: dpAW } = await getClient(config);
187
- const saAW = new ethers.Contract(
188
- config.serviceAgreementAddress,
189
- ["function ARBITRATION_SELECTION_WINDOW() external view returns (uint256)"],
190
- dpAW,
191
- );
192
- const selectionWindow: bigint = await saAW.ARBITRATION_SELECTION_WINDOW();
193
- const deadlineDate = new Date(Date.now() + Number(selectionWindow) * 1000);
194
- console.log(`Arbitration selection window closes: ${deadlineDate.toLocaleString()}. An arbitrator must be assigned before then.`);
195
- } catch { /* not available on this deployment */ }
196
- });
197
-
198
- dispute.command("evidence <id>").requiredOption("--type <type>", "transcript|deliverable|acceptance|communication|external|other").option("--file <path>").option("--text <text>").option("--uri <uri>", "External evidence URI", "").action(async (id, opts) => {
199
- const config = loadConfig();
200
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
201
- const { signer } = await requireSigner(config);
202
- const mapping: Record<string, EvidenceType> = { transcript: EvidenceType.TRANSCRIPT, deliverable: EvidenceType.DELIVERABLE, acceptance: EvidenceType.ACCEPTANCE_CRITERIA, communication: EvidenceType.COMMUNICATION, external: EvidenceType.EXTERNAL_REFERENCE, other: EvidenceType.OTHER };
203
- const hash = opts.file ? hashFile(opts.file) : hashString(opts.text ?? opts.uri ?? `evidence:${id}`);
204
- const evidenceType = mapping[String(opts.type).toLowerCase()] ?? EvidenceType.OTHER;
205
- printSenderInfo(config);
206
- if (config.walletContractAddress) {
207
- await executeContractWriteViaWallet(
208
- config.walletContractAddress, signer, config.serviceAgreementAddress,
209
- SERVICE_AGREEMENT_ABI, "submitDisputeEvidence", [BigInt(id), evidenceType, hash, opts.uri],
210
- );
211
- } else {
212
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
213
- await client.submitDisputeEvidence(BigInt(id), evidenceType, hash, opts.uri);
214
- }
215
- console.log(' ' + c.success + c.white(` Evidence submitted — agreement #${id}`));
216
- });
217
-
218
- // status — read-only, no wallet routing needed
219
- dispute.command("status <id>").option("--json").action(async (id, opts) => {
220
- const config = loadConfig();
221
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
222
- const { provider } = await getClient(config); const client = new ServiceAgreementClient(config.serviceAgreementAddress, provider);
223
- const result = {
224
- case: await client.getDisputeCase(BigInt(id)),
225
- arbitration: await client.getArbitrationCase(BigInt(id)),
226
- evidence: await client.getDisputeEvidenceAll(BigInt(id)),
227
- };
228
- console.log(JSON.stringify(result, (_k, value) => typeof value === 'bigint' ? value.toString() : value, opts.json ? 2 : 2));
229
- });
230
-
231
- dispute.command("nominate <id>")
232
- .description("Nominate an arbitrator during the on-chain arbitration phase")
233
- .requiredOption("--arbitrator <address>")
234
- .action(async (id, opts) => {
235
- const config = loadConfig();
236
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
237
-
238
- // Pre-flight: check arbitrator is approved (J4-03)
239
- if (config.disputeArbitrationAddress) {
240
- const { provider: arbProvider } = await getClient(config);
241
- const daCheck = new ethers.Contract(
242
- config.disputeArbitrationAddress,
243
- ["function isApprovedArbitrator(address arbitrator) external view returns (bool)"],
244
- arbProvider,
245
- );
246
- let isApproved = true;
247
- try {
248
- isApproved = await daCheck.isApprovedArbitrator(opts.arbitrator);
249
- } catch { /* assume approved if read fails */ }
250
- if (!isApproved) {
251
- console.error(`Arbitrator ${opts.arbitrator} is not approved.`);
252
- console.error(`Use \`arc402 dispute list-arbitrators\` to see approved arbitrators.`);
253
- process.exit(1);
254
- }
255
- }
256
-
257
- const { signer } = await requireSigner(config);
258
- printSenderInfo(config);
259
- if (config.walletContractAddress) {
260
- await executeContractWriteViaWallet(
261
- config.walletContractAddress, signer, config.serviceAgreementAddress,
262
- SERVICE_AGREEMENT_ABI, "nominateArbitrator", [BigInt(id), opts.arbitrator],
263
- );
264
- } else {
265
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
266
- await client.nominateArbitrator(BigInt(id), opts.arbitrator);
267
- }
268
- console.log(' ' + c.success + c.white(` Arbitrator nominated — agreement #${id}`));
269
- });
270
-
271
- dispute.command("vote <id>")
272
- .description("Cast an arbitration vote on-chain")
273
- .requiredOption("--vote <vote>", "provider|refund|split|human-review")
274
- .option("--provider-award <amount>", "Wei/token units", "0")
275
- .option("--client-award <amount>", "Wei/token units", "0")
276
- .action(async (id, opts) => {
277
- const config = loadConfig();
278
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
279
- const { signer, address: voterAddress, provider: voteProvider } = await requireSigner(config);
280
-
281
- // J4-05: Pre-flight — verify caller is on the arbitration panel
282
- try {
283
- const saClient = new ServiceAgreementClient(config.serviceAgreementAddress, voteProvider);
284
- const arbCase = await saClient.getArbitrationCase(BigInt(id));
285
- const onPanel = arbCase.arbitrators.map((a: string) => a.toLowerCase()).includes(voterAddress.toLowerCase());
286
- if (!onPanel) {
287
- console.error(`You are not on the arbitration panel for agreement ${id}. Only assigned arbitrators can vote.`);
288
- process.exit(1);
289
- }
290
- } catch (e) {
291
- if ((e as NodeJS.ErrnoException)?.code === 'ERR_USE_AFTER_CLOSE' || String(e).includes('process.exit')) throw e;
292
- // If read fails, skip the check and let the transaction reveal the error
293
- }
294
-
295
- const mapping: Record<string, ArbitrationVote> = {
296
- provider: ArbitrationVote.PROVIDER_WINS,
297
- refund: ArbitrationVote.CLIENT_REFUND,
298
- split: ArbitrationVote.SPLIT,
299
- 'human-review': ArbitrationVote.HUMAN_REVIEW_REQUIRED,
300
- };
301
- const vote = mapping[String(opts.vote).toLowerCase()];
302
- if (vote === undefined) throw new Error('Unsupported --vote value');
303
- printSenderInfo(config);
304
- if (config.walletContractAddress) {
305
- await executeContractWriteViaWallet(
306
- config.walletContractAddress, signer, config.serviceAgreementAddress,
307
- SERVICE_AGREEMENT_ABI, "castArbitrationVote",
308
- [BigInt(id), vote, BigInt(opts.providerAward), BigInt(opts.clientAward)],
309
- );
310
- } else {
311
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
312
- await client.castArbitrationVote(BigInt(id), vote, BigInt(opts.providerAward), BigInt(opts.clientAward));
313
- }
314
- console.log(' ' + c.success + c.white(` Vote recorded — agreement #${id}`));
315
- });
316
-
317
- dispute.command("human <id>")
318
- .description("Request human escalation when arbitration stalls or requires human backstop")
319
- .requiredOption("--reason <reason>")
320
- .action(async (id, opts) => {
321
- const config = loadConfig();
322
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
323
- const { signer } = await requireSigner(config);
324
- printSenderInfo(config);
325
- if (config.walletContractAddress) {
326
- await executeContractWriteViaWallet(
327
- config.walletContractAddress, signer, config.serviceAgreementAddress,
328
- SERVICE_AGREEMENT_ABI, "requestHumanEscalation", [BigInt(id), opts.reason],
329
- );
330
- } else {
331
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
332
- await client.requestHumanEscalation(BigInt(id), opts.reason);
333
- }
334
- console.log(' ' + c.success + c.white(` Escalated to human — agreement #${id}`));
335
- });
336
-
337
- dispute.command("resolve <id>").description("Owner-only admin path if you are operating the dispute contract").requiredOption("--outcome <outcome>", "provider|refund|partial-provider|partial-client|mutual-cancel|human-review").option("--provider-award <amount>", "Wei/token units", "0").option("--client-award <amount>", "Wei/token units", "0").action(async (id, opts) => {
338
- const config = loadConfig();
339
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
340
- const { signer } = await requireSigner(config);
341
- const mapping: Record<string, DisputeOutcome> = { provider: DisputeOutcome.PROVIDER_WINS, refund: DisputeOutcome.CLIENT_REFUND, 'partial-provider': DisputeOutcome.PARTIAL_PROVIDER, 'partial-client': DisputeOutcome.PARTIAL_CLIENT, 'mutual-cancel': DisputeOutcome.MUTUAL_CANCEL, 'human-review': DisputeOutcome.HUMAN_REVIEW_REQUIRED };
342
- printSenderInfo(config);
343
- if (config.walletContractAddress) {
344
- await executeContractWriteViaWallet(
345
- config.walletContractAddress, signer, config.serviceAgreementAddress,
346
- SERVICE_AGREEMENT_ABI, "resolveDisputeDetailed",
347
- [BigInt(id), mapping[String(opts.outcome)], BigInt(opts.providerAward), BigInt(opts.clientAward)],
348
- );
349
- } else {
350
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
351
- await client.resolveDisputeDetailed(BigInt(id), mapping[String(opts.outcome)], BigInt(opts.providerAward), BigInt(opts.clientAward));
352
- }
353
- console.log(' ' + c.success + c.white(` Resolved — agreement #${id}`));
354
- });
355
-
356
- dispute.command("owner-resolve <agreementId>")
357
- .description("Owner-only: resolve a dispute directly in favor of provider or client. Requires DISPUTED or ESCALATED_TO_HUMAN status.")
358
- .option("--favor-provider", "Resolve in favor of the provider (default: false = favor client)", false)
359
- .action(async (agreementId, opts) => {
360
- const config = loadConfig();
361
- if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
362
- const { signer } = await requireSigner(config);
363
- printSenderInfo(config);
364
- if (config.walletContractAddress) {
365
- await executeContractWriteViaWallet(
366
- config.walletContractAddress, signer, config.serviceAgreementAddress,
367
- SERVICE_AGREEMENT_ABI, "ownerResolveDispute", [BigInt(agreementId), !!opts.favorProvider],
368
- );
369
- } else {
370
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
371
- await client.ownerResolveDispute(BigInt(agreementId), !!opts.favorProvider);
372
- }
373
- console.log(' ' + c.success + c.white(` Owner resolved — agreement #${agreementId}`));
374
- });
375
- }