arc402-cli 0.2.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 (308) hide show
  1. package/README.md +245 -0
  2. package/dist/abis.d.ts +19 -0
  3. package/dist/abis.d.ts.map +1 -0
  4. package/dist/abis.js +177 -0
  5. package/dist/abis.js.map +1 -0
  6. package/dist/bundler.d.ts +65 -0
  7. package/dist/bundler.d.ts.map +1 -0
  8. package/dist/bundler.js +181 -0
  9. package/dist/bundler.js.map +1 -0
  10. package/dist/client.d.ts +14 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +24 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/coinbase-smart-wallet.d.ts +28 -0
  15. package/dist/coinbase-smart-wallet.d.ts.map +1 -0
  16. package/dist/coinbase-smart-wallet.js +38 -0
  17. package/dist/coinbase-smart-wallet.js.map +1 -0
  18. package/dist/commands/accept.d.ts +3 -0
  19. package/dist/commands/accept.d.ts.map +1 -0
  20. package/dist/commands/accept.js +26 -0
  21. package/dist/commands/accept.js.map +1 -0
  22. package/dist/commands/agent-handshake.d.ts +3 -0
  23. package/dist/commands/agent-handshake.d.ts.map +1 -0
  24. package/dist/commands/agent-handshake.js +61 -0
  25. package/dist/commands/agent-handshake.js.map +1 -0
  26. package/dist/commands/agent.d.ts +3 -0
  27. package/dist/commands/agent.d.ts.map +1 -0
  28. package/dist/commands/agent.js +417 -0
  29. package/dist/commands/agent.js.map +1 -0
  30. package/dist/commands/agreements.d.ts +3 -0
  31. package/dist/commands/agreements.d.ts.map +1 -0
  32. package/dist/commands/agreements.js +344 -0
  33. package/dist/commands/agreements.js.map +1 -0
  34. package/dist/commands/arbitrator.d.ts +3 -0
  35. package/dist/commands/arbitrator.d.ts.map +1 -0
  36. package/dist/commands/arbitrator.js +157 -0
  37. package/dist/commands/arbitrator.js.map +1 -0
  38. package/dist/commands/arena-handshake.d.ts +3 -0
  39. package/dist/commands/arena-handshake.d.ts.map +1 -0
  40. package/dist/commands/arena-handshake.js +187 -0
  41. package/dist/commands/arena-handshake.js.map +1 -0
  42. package/dist/commands/cancel.d.ts +3 -0
  43. package/dist/commands/cancel.d.ts.map +1 -0
  44. package/dist/commands/cancel.js +30 -0
  45. package/dist/commands/cancel.js.map +1 -0
  46. package/dist/commands/channel.d.ts +3 -0
  47. package/dist/commands/channel.d.ts.map +1 -0
  48. package/dist/commands/channel.js +238 -0
  49. package/dist/commands/channel.js.map +1 -0
  50. package/dist/commands/coldstart.d.ts +3 -0
  51. package/dist/commands/coldstart.d.ts.map +1 -0
  52. package/dist/commands/coldstart.js +148 -0
  53. package/dist/commands/coldstart.js.map +1 -0
  54. package/dist/commands/config.d.ts +3 -0
  55. package/dist/commands/config.d.ts.map +1 -0
  56. package/dist/commands/config.js +40 -0
  57. package/dist/commands/config.js.map +1 -0
  58. package/dist/commands/contract-interaction.d.ts +3 -0
  59. package/dist/commands/contract-interaction.d.ts.map +1 -0
  60. package/dist/commands/contract-interaction.js +165 -0
  61. package/dist/commands/contract-interaction.js.map +1 -0
  62. package/dist/commands/daemon.d.ts +3 -0
  63. package/dist/commands/daemon.d.ts.map +1 -0
  64. package/dist/commands/daemon.js +891 -0
  65. package/dist/commands/daemon.js.map +1 -0
  66. package/dist/commands/deliver.d.ts +3 -0
  67. package/dist/commands/deliver.d.ts.map +1 -0
  68. package/dist/commands/deliver.js +156 -0
  69. package/dist/commands/deliver.js.map +1 -0
  70. package/dist/commands/discover.d.ts +3 -0
  71. package/dist/commands/discover.d.ts.map +1 -0
  72. package/dist/commands/discover.js +224 -0
  73. package/dist/commands/discover.js.map +1 -0
  74. package/dist/commands/dispute.d.ts +3 -0
  75. package/dist/commands/dispute.d.ts.map +1 -0
  76. package/dist/commands/dispute.js +348 -0
  77. package/dist/commands/dispute.js.map +1 -0
  78. package/dist/commands/endpoint.d.ts +3 -0
  79. package/dist/commands/endpoint.d.ts.map +1 -0
  80. package/dist/commands/endpoint.js +604 -0
  81. package/dist/commands/endpoint.js.map +1 -0
  82. package/dist/commands/hire.d.ts +3 -0
  83. package/dist/commands/hire.d.ts.map +1 -0
  84. package/dist/commands/hire.js +189 -0
  85. package/dist/commands/hire.js.map +1 -0
  86. package/dist/commands/migrate.d.ts +3 -0
  87. package/dist/commands/migrate.d.ts.map +1 -0
  88. package/dist/commands/migrate.js +163 -0
  89. package/dist/commands/migrate.js.map +1 -0
  90. package/dist/commands/negotiate.d.ts +3 -0
  91. package/dist/commands/negotiate.d.ts.map +1 -0
  92. package/dist/commands/negotiate.js +247 -0
  93. package/dist/commands/negotiate.js.map +1 -0
  94. package/dist/commands/openshell.d.ts +3 -0
  95. package/dist/commands/openshell.d.ts.map +1 -0
  96. package/dist/commands/openshell.js +952 -0
  97. package/dist/commands/openshell.js.map +1 -0
  98. package/dist/commands/owner.d.ts +3 -0
  99. package/dist/commands/owner.d.ts.map +1 -0
  100. package/dist/commands/owner.js +32 -0
  101. package/dist/commands/owner.js.map +1 -0
  102. package/dist/commands/policy.d.ts +4 -0
  103. package/dist/commands/policy.d.ts.map +1 -0
  104. package/dist/commands/policy.js +248 -0
  105. package/dist/commands/policy.js.map +1 -0
  106. package/dist/commands/relay.d.ts +3 -0
  107. package/dist/commands/relay.d.ts.map +1 -0
  108. package/dist/commands/relay.js +279 -0
  109. package/dist/commands/relay.js.map +1 -0
  110. package/dist/commands/remediate.d.ts +3 -0
  111. package/dist/commands/remediate.d.ts.map +1 -0
  112. package/dist/commands/remediate.js +42 -0
  113. package/dist/commands/remediate.js.map +1 -0
  114. package/dist/commands/reputation.d.ts +4 -0
  115. package/dist/commands/reputation.d.ts.map +1 -0
  116. package/dist/commands/reputation.js +72 -0
  117. package/dist/commands/reputation.js.map +1 -0
  118. package/dist/commands/setup.d.ts +3 -0
  119. package/dist/commands/setup.d.ts.map +1 -0
  120. package/dist/commands/setup.js +332 -0
  121. package/dist/commands/setup.js.map +1 -0
  122. package/dist/commands/trust.d.ts +3 -0
  123. package/dist/commands/trust.d.ts.map +1 -0
  124. package/dist/commands/trust.js +23 -0
  125. package/dist/commands/trust.js.map +1 -0
  126. package/dist/commands/verify.d.ts +3 -0
  127. package/dist/commands/verify.d.ts.map +1 -0
  128. package/dist/commands/verify.js +88 -0
  129. package/dist/commands/verify.js.map +1 -0
  130. package/dist/commands/wallet.d.ts +3 -0
  131. package/dist/commands/wallet.d.ts.map +1 -0
  132. package/dist/commands/wallet.js +2520 -0
  133. package/dist/commands/wallet.js.map +1 -0
  134. package/dist/commands/watchtower.d.ts +3 -0
  135. package/dist/commands/watchtower.d.ts.map +1 -0
  136. package/dist/commands/watchtower.js +238 -0
  137. package/dist/commands/watchtower.js.map +1 -0
  138. package/dist/commands/workroom.d.ts +3 -0
  139. package/dist/commands/workroom.d.ts.map +1 -0
  140. package/dist/commands/workroom.js +855 -0
  141. package/dist/commands/workroom.js.map +1 -0
  142. package/dist/config.d.ts +62 -0
  143. package/dist/config.d.ts.map +1 -0
  144. package/dist/config.js +141 -0
  145. package/dist/config.js.map +1 -0
  146. package/dist/daemon/config.d.ts +74 -0
  147. package/dist/daemon/config.d.ts.map +1 -0
  148. package/dist/daemon/config.js +271 -0
  149. package/dist/daemon/config.js.map +1 -0
  150. package/dist/daemon/hire-listener.d.ts +31 -0
  151. package/dist/daemon/hire-listener.d.ts.map +1 -0
  152. package/dist/daemon/hire-listener.js +207 -0
  153. package/dist/daemon/hire-listener.js.map +1 -0
  154. package/dist/daemon/index.d.ts +29 -0
  155. package/dist/daemon/index.d.ts.map +1 -0
  156. package/dist/daemon/index.js +535 -0
  157. package/dist/daemon/index.js.map +1 -0
  158. package/dist/daemon/job-lifecycle.d.ts +62 -0
  159. package/dist/daemon/job-lifecycle.d.ts.map +1 -0
  160. package/dist/daemon/job-lifecycle.js +201 -0
  161. package/dist/daemon/job-lifecycle.js.map +1 -0
  162. package/dist/daemon/notify.d.ts +22 -0
  163. package/dist/daemon/notify.d.ts.map +1 -0
  164. package/dist/daemon/notify.js +148 -0
  165. package/dist/daemon/notify.js.map +1 -0
  166. package/dist/daemon/token-metering.d.ts +42 -0
  167. package/dist/daemon/token-metering.d.ts.map +1 -0
  168. package/dist/daemon/token-metering.js +178 -0
  169. package/dist/daemon/token-metering.js.map +1 -0
  170. package/dist/daemon/userops.d.ts +21 -0
  171. package/dist/daemon/userops.d.ts.map +1 -0
  172. package/dist/daemon/userops.js +88 -0
  173. package/dist/daemon/userops.js.map +1 -0
  174. package/dist/daemon/wallet-monitor.d.ts +16 -0
  175. package/dist/daemon/wallet-monitor.d.ts.map +1 -0
  176. package/dist/daemon/wallet-monitor.js +57 -0
  177. package/dist/daemon/wallet-monitor.js.map +1 -0
  178. package/dist/drain-v4.d.ts +2 -0
  179. package/dist/drain-v4.d.ts.map +1 -0
  180. package/dist/drain-v4.js +167 -0
  181. package/dist/drain-v4.js.map +1 -0
  182. package/dist/endpoint-config.d.ts +36 -0
  183. package/dist/endpoint-config.d.ts.map +1 -0
  184. package/dist/endpoint-config.js +96 -0
  185. package/dist/endpoint-config.js.map +1 -0
  186. package/dist/index.d.ts +3 -0
  187. package/dist/index.d.ts.map +1 -0
  188. package/dist/index.js +79 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/openshell-runtime.d.ts +55 -0
  191. package/dist/openshell-runtime.d.ts.map +1 -0
  192. package/dist/openshell-runtime.js +268 -0
  193. package/dist/openshell-runtime.js.map +1 -0
  194. package/dist/signing.d.ts +2 -0
  195. package/dist/signing.d.ts.map +1 -0
  196. package/dist/signing.js +23 -0
  197. package/dist/signing.js.map +1 -0
  198. package/dist/telegram-notify.d.ts +23 -0
  199. package/dist/telegram-notify.d.ts.map +1 -0
  200. package/dist/telegram-notify.js +106 -0
  201. package/dist/telegram-notify.js.map +1 -0
  202. package/dist/ui/banner.d.ts +7 -0
  203. package/dist/ui/banner.d.ts.map +1 -0
  204. package/dist/ui/banner.js +37 -0
  205. package/dist/ui/banner.js.map +1 -0
  206. package/dist/ui/colors.d.ts +14 -0
  207. package/dist/ui/colors.d.ts.map +1 -0
  208. package/dist/ui/colors.js +29 -0
  209. package/dist/ui/colors.js.map +1 -0
  210. package/dist/ui/format.d.ts +26 -0
  211. package/dist/ui/format.d.ts.map +1 -0
  212. package/dist/ui/format.js +77 -0
  213. package/dist/ui/format.js.map +1 -0
  214. package/dist/ui/spinner.d.ts +8 -0
  215. package/dist/ui/spinner.d.ts.map +1 -0
  216. package/dist/ui/spinner.js +43 -0
  217. package/dist/ui/spinner.js.map +1 -0
  218. package/dist/utils/format.d.ts +10 -0
  219. package/dist/utils/format.d.ts.map +1 -0
  220. package/dist/utils/format.js +61 -0
  221. package/dist/utils/format.js.map +1 -0
  222. package/dist/utils/hash.d.ts +3 -0
  223. package/dist/utils/hash.d.ts.map +1 -0
  224. package/dist/utils/hash.js +43 -0
  225. package/dist/utils/hash.js.map +1 -0
  226. package/dist/utils/time.d.ts +3 -0
  227. package/dist/utils/time.d.ts.map +1 -0
  228. package/dist/utils/time.js +21 -0
  229. package/dist/utils/time.js.map +1 -0
  230. package/dist/wallet-router.d.ts +25 -0
  231. package/dist/wallet-router.d.ts.map +1 -0
  232. package/dist/wallet-router.js +153 -0
  233. package/dist/wallet-router.js.map +1 -0
  234. package/dist/walletconnect-session.d.ts +12 -0
  235. package/dist/walletconnect-session.d.ts.map +1 -0
  236. package/dist/walletconnect-session.js +26 -0
  237. package/dist/walletconnect-session.js.map +1 -0
  238. package/dist/walletconnect.d.ts +46 -0
  239. package/dist/walletconnect.d.ts.map +1 -0
  240. package/dist/walletconnect.js +267 -0
  241. package/dist/walletconnect.js.map +1 -0
  242. package/package.json +38 -0
  243. package/scripts/authorize-machine-key.ts +43 -0
  244. package/scripts/drain-wallet.ts +149 -0
  245. package/scripts/execute-spend-only.ts +81 -0
  246. package/scripts/register-agent-userop.ts +186 -0
  247. package/src/abis.ts +187 -0
  248. package/src/bundler.ts +235 -0
  249. package/src/client.ts +34 -0
  250. package/src/coinbase-smart-wallet.ts +51 -0
  251. package/src/commands/accept.ts +25 -0
  252. package/src/commands/agent-handshake.ts +67 -0
  253. package/src/commands/agent.ts +458 -0
  254. package/src/commands/agreements.ts +324 -0
  255. package/src/commands/arbitrator.ts +129 -0
  256. package/src/commands/arena-handshake.ts +217 -0
  257. package/src/commands/cancel.ts +26 -0
  258. package/src/commands/channel.ts +208 -0
  259. package/src/commands/coldstart.ts +156 -0
  260. package/src/commands/config.ts +35 -0
  261. package/src/commands/contract-interaction.ts +166 -0
  262. package/src/commands/daemon.ts +971 -0
  263. package/src/commands/deliver.ts +116 -0
  264. package/src/commands/discover.ts +295 -0
  265. package/src/commands/dispute.ts +373 -0
  266. package/src/commands/endpoint.ts +619 -0
  267. package/src/commands/hire.ts +200 -0
  268. package/src/commands/migrate.ts +175 -0
  269. package/src/commands/negotiate.ts +270 -0
  270. package/src/commands/openshell.ts +1053 -0
  271. package/src/commands/owner.ts +30 -0
  272. package/src/commands/policy.ts +252 -0
  273. package/src/commands/relay.ts +272 -0
  274. package/src/commands/remediate.ts +22 -0
  275. package/src/commands/reputation.ts +71 -0
  276. package/src/commands/setup.ts +343 -0
  277. package/src/commands/trust.ts +15 -0
  278. package/src/commands/verify.ts +88 -0
  279. package/src/commands/wallet.ts +2892 -0
  280. package/src/commands/watchtower.ts +232 -0
  281. package/src/commands/workroom.ts +889 -0
  282. package/src/config.ts +153 -0
  283. package/src/daemon/config.ts +308 -0
  284. package/src/daemon/hire-listener.ts +226 -0
  285. package/src/daemon/index.ts +609 -0
  286. package/src/daemon/job-lifecycle.ts +215 -0
  287. package/src/daemon/notify.ts +157 -0
  288. package/src/daemon/token-metering.ts +183 -0
  289. package/src/daemon/userops.ts +119 -0
  290. package/src/daemon/wallet-monitor.ts +90 -0
  291. package/src/drain-v4.ts +159 -0
  292. package/src/endpoint-config.ts +83 -0
  293. package/src/index.ts +75 -0
  294. package/src/openshell-runtime.ts +277 -0
  295. package/src/signing.ts +28 -0
  296. package/src/telegram-notify.ts +88 -0
  297. package/src/ui/banner.ts +41 -0
  298. package/src/ui/colors.ts +30 -0
  299. package/src/ui/format.ts +77 -0
  300. package/src/ui/spinner.ts +46 -0
  301. package/src/utils/format.ts +48 -0
  302. package/src/utils/hash.ts +5 -0
  303. package/src/utils/time.ts +15 -0
  304. package/src/wallet-router.ts +178 -0
  305. package/src/walletconnect-session.ts +27 -0
  306. package/src/walletconnect.ts +294 -0
  307. package/test/time.test.js +11 -0
  308. package/tsconfig.json +19 -0
@@ -0,0 +1,232 @@
1
+ import { Command } from "commander";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import { loadConfig } from "../config";
6
+ import { requireSigner, getClient } from "../client";
7
+ import { WatchtowerClient, ChannelClient } from "@arc402/sdk";
8
+
9
+ const CHANNEL_STATES_DIR = path.join(os.homedir(), ".arc402", "channel-states");
10
+
11
+ function loadStateFile(filePath: string): { [key: string]: unknown; cumulativePayment: bigint; sequenceNumber: number } {
12
+ const raw = fs.readFileSync(filePath, "utf-8");
13
+ const obj = JSON.parse(raw) as Record<string, unknown>;
14
+ return {
15
+ ...obj,
16
+ cumulativePayment: BigInt(obj.cumulativePayment as string),
17
+ sequenceNumber: Number(obj.sequenceNumber),
18
+ };
19
+ }
20
+
21
+ export function registerWatchtowerCommands(program: Command): void {
22
+ const wt = program
23
+ .command("watchtower")
24
+ .description("Watchtower management — register, status, and monitor channels for bad-faith closes");
25
+
26
+ // ─── register ──────────────────────────────────────────────────────────────
27
+
28
+ wt.command("register")
29
+ .description("Register this node as a watchtower in the on-chain WatchtowerRegistry")
30
+ .requiredOption("--name <name>", "Watchtower display name")
31
+ .option("--description <desc>", "Short description", "ARC-402 watchtower node")
32
+ .option("--capabilities <caps>", "Comma-separated capability tags", "challenge")
33
+ .option("--json", "JSON output")
34
+ .action(async (opts) => {
35
+ const config = loadConfig();
36
+ if (!config.watchtowerRegistryAddress) throw new Error("watchtowerRegistryAddress missing in config");
37
+ if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
38
+ const { signer } = await requireSigner(config);
39
+ const client = new WatchtowerClient(
40
+ config.watchtowerRegistryAddress,
41
+ config.serviceAgreementAddress,
42
+ signer
43
+ );
44
+ const capabilities = opts.capabilities
45
+ .split(",")
46
+ .map((s: string) => s.trim())
47
+ .filter(Boolean);
48
+ const result = await client.registerWatchtower({
49
+ name: opts.name,
50
+ description: opts.description,
51
+ capabilities,
52
+ });
53
+ if (opts.json || program.opts().json) {
54
+ console.log(JSON.stringify(result));
55
+ } else {
56
+ console.log(`registered as watchtower: ${opts.name}`);
57
+ console.log(`tx: ${result.txHash}`);
58
+ }
59
+ });
60
+
61
+ // ─── status ────────────────────────────────────────────────────────────────
62
+
63
+ wt.command("status [address]")
64
+ .description("Check watchtower registration status (defaults to your own address)")
65
+ .option("--json", "JSON output")
66
+ .action(async (address, opts) => {
67
+ const config = loadConfig();
68
+ if (!config.watchtowerRegistryAddress) throw new Error("watchtowerRegistryAddress missing in config");
69
+ if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
70
+ const { provider, address: myAddress } = await getClient(config);
71
+ const client = new WatchtowerClient(
72
+ config.watchtowerRegistryAddress,
73
+ config.serviceAgreementAddress,
74
+ provider
75
+ );
76
+ const target = address ?? myAddress;
77
+ if (!target) {
78
+ console.error("No address provided and no private key configured");
79
+ process.exit(1);
80
+ }
81
+ const status = await client.getWatchtowerStatus(target);
82
+ if (opts.json || program.opts().json) {
83
+ console.log(JSON.stringify(status, null, 2));
84
+ } else {
85
+ console.log(`watchtower: ${status.addr}`);
86
+ console.log(` name: ${status.name}`);
87
+ console.log(` description: ${status.description}`);
88
+ console.log(` capabilities: ${status.capabilities.join(", ") || "(none)"}`);
89
+ console.log(` active: ${status.active}`);
90
+ if (status.registeredAt) {
91
+ console.log(` registered: ${new Date(status.registeredAt * 1000).toISOString()}`);
92
+ }
93
+ }
94
+ });
95
+
96
+ // ─── watch ─────────────────────────────────────────────────────────────────
97
+
98
+ wt.command("watch <channelId>")
99
+ .description("Monitor a session channel for bad-faith closes and auto-submit a challenge")
100
+ .option(
101
+ "--state <path>",
102
+ "Path to latest signed state JSON (defaults to ~/.arc402/channel-states/<channelId>.json)"
103
+ )
104
+ .option("--interval <ms>", "Polling interval in milliseconds", "12000")
105
+ .option("--json", "JSON output")
106
+ .action(async (channelId, opts) => {
107
+ const config = loadConfig();
108
+ if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
109
+ if (!config.watchtowerRegistryAddress) throw new Error("watchtowerRegistryAddress missing in config");
110
+ const { signer, address } = await requireSigner(config);
111
+
112
+ const watchtowerClient = new WatchtowerClient(
113
+ config.watchtowerRegistryAddress,
114
+ config.serviceAgreementAddress,
115
+ signer
116
+ );
117
+ const channelClient = new ChannelClient(config.serviceAgreementAddress, signer);
118
+
119
+ const statePath = opts.state ?? path.join(CHANNEL_STATES_DIR, `${channelId}.json`);
120
+ if (!fs.existsSync(statePath)) {
121
+ console.error(`No state file at ${statePath}`);
122
+ console.error(`Store state first: arc402 channel store-state ${channelId} <state.json>`);
123
+ process.exit(1);
124
+ }
125
+ const state = loadStateFile(statePath);
126
+ const interval = Math.max(1000, Number(opts.interval));
127
+
128
+ if (!opts.json) {
129
+ console.log(`watching: ${channelId}`);
130
+ console.log(` stored seq: ${state.sequenceNumber}`);
131
+ console.log(` poll interval: ${interval}ms`);
132
+ console.log(` press Ctrl+C to stop`);
133
+ }
134
+
135
+ let challenged = false;
136
+
137
+ const poll = async () => {
138
+ if (challenged) return;
139
+ try {
140
+ const ch = await channelClient.getChannelStatus(channelId);
141
+
142
+ if (ch.status === "SETTLED") {
143
+ if (!opts.json) {
144
+ console.log(`[${new Date().toISOString()}] channel settled — stopping watch`);
145
+ }
146
+ return;
147
+ }
148
+
149
+ if (ch.status === "CLOSING" || ch.status === "CHALLENGED") {
150
+ if (ch.lastSequenceNumber < (state.sequenceNumber as number)) {
151
+ if (!opts.json) {
152
+ console.log(
153
+ `[${new Date().toISOString()}] stale close detected ` +
154
+ `(on-chain seq=${ch.lastSequenceNumber}, stored seq=${state.sequenceNumber}) — challenging...`
155
+ );
156
+ }
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const result = await watchtowerClient.submitChallenge(channelId, state as any, address);
159
+ challenged = true;
160
+ if (opts.json) {
161
+ console.log(JSON.stringify({ event: "challenge_submitted", channelId, txHash: result.txHash }));
162
+ } else {
163
+ console.log(`challenge submitted: ${result.txHash}`);
164
+ }
165
+ return;
166
+ } else {
167
+ if (!opts.json) {
168
+ console.log(
169
+ `[${new Date().toISOString()}] channel closing with seq=${ch.lastSequenceNumber} — no challenge needed`
170
+ );
171
+ }
172
+ }
173
+ }
174
+ } catch (err) {
175
+ if (!opts.json) {
176
+ console.error(`[${new Date().toISOString()}] poll error: ${err}`);
177
+ }
178
+ }
179
+ setTimeout(poll, interval);
180
+ };
181
+
182
+ poll();
183
+ });
184
+
185
+ // ─── authorize ─────────────────────────────────────────────────────────────
186
+
187
+ wt.command("authorize <channelId> <watchtower>")
188
+ .description("Authorize a watchtower address to challenge on your behalf for a channel")
189
+ .option("--json", "JSON output")
190
+ .action(async (channelId, watchtower, opts) => {
191
+ const config = loadConfig();
192
+ if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
193
+ if (!config.watchtowerRegistryAddress) throw new Error("watchtowerRegistryAddress missing in config");
194
+ const { signer } = await requireSigner(config);
195
+ const client = new WatchtowerClient(
196
+ config.watchtowerRegistryAddress,
197
+ config.serviceAgreementAddress,
198
+ signer
199
+ );
200
+ const result = await client.authorizeWatchtower(channelId, watchtower);
201
+ if (opts.json || program.opts().json) {
202
+ console.log(JSON.stringify(result));
203
+ } else {
204
+ console.log(`watchtower authorized: ${watchtower}`);
205
+ console.log(`tx: ${result.txHash}`);
206
+ }
207
+ });
208
+
209
+ // ─── revoke ────────────────────────────────────────────────────────────────
210
+
211
+ wt.command("revoke <channelId> <watchtower>")
212
+ .description("Revoke a watchtower's authorization for a channel")
213
+ .option("--json", "JSON output")
214
+ .action(async (channelId, watchtower, opts) => {
215
+ const config = loadConfig();
216
+ if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
217
+ if (!config.watchtowerRegistryAddress) throw new Error("watchtowerRegistryAddress missing in config");
218
+ const { signer } = await requireSigner(config);
219
+ const client = new WatchtowerClient(
220
+ config.watchtowerRegistryAddress,
221
+ config.serviceAgreementAddress,
222
+ signer
223
+ );
224
+ const result = await client.revokeWatchtower(channelId, watchtower);
225
+ if (opts.json || program.opts().json) {
226
+ console.log(JSON.stringify(result));
227
+ } else {
228
+ console.log(`watchtower revoked: ${watchtower}`);
229
+ console.log(`tx: ${result.txHash}`);
230
+ }
231
+ });
232
+ }