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,35 +0,0 @@
1
- import { Command } from "commander";
2
- import { DisputeArbitrationClient } from "@arc402/sdk";
3
- import { loadConfig } from "../config";
4
- import { requireSigner } from "../client";
5
- import { c } from '../ui/colors';
6
- import { startSpinner } from '../ui/spinner';
7
- import { formatAddress } from '../ui/format';
8
-
9
- export function registerOwnerCommands(program: Command): void {
10
- const owner = program.command("owner").description("Protocol ownership management (DisputeArbitration two-step transfer)");
11
-
12
- owner.command("propose-transfer <newOwner>")
13
- .description("Step 1: Propose ownership transfer of DisputeArbitration to a new address. New owner must call accept-transfer to complete.")
14
- .action(async (newOwner, _opts) => {
15
- const config = loadConfig();
16
- if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
17
- const { signer } = await requireSigner(config);
18
- const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
19
- const spinner = startSpinner('Proposing ownership transfer…');
20
- await client.proposeOwner(newOwner);
21
- spinner.succeed(c.success + c.white(' Ownership transfer proposed to ' + formatAddress(newOwner)));
22
- });
23
-
24
- owner.command("accept-transfer")
25
- .description("Step 2: Accept pending ownership of DisputeArbitration. Must be called from the pending owner's key.")
26
- .action(async (_opts) => {
27
- const config = loadConfig();
28
- if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
29
- const { signer } = await requireSigner(config);
30
- const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
31
- const spinner = startSpinner('Accepting ownership…');
32
- await client.acceptOwnership();
33
- spinner.succeed(c.success + c.white(' Ownership accepted'));
34
- });
35
- }
@@ -1,263 +0,0 @@
1
- import { Command } from "commander";
2
- import { ethers } from "ethers";
3
- import { loadConfig } from "../config";
4
- import { getClient, requireSigner } from "../client";
5
- import { c } from '../ui/colors';
6
- import { startSpinner } from '../ui/spinner';
7
- import { formatAddress } from '../ui/format';
8
-
9
- const POLICY_ENGINE_EXTENDED_ABI = [
10
- "function addToBlocklist(address wallet, address provider) external",
11
- "function removeFromBlocklist(address wallet, address provider) external",
12
- "function isBlocked(address wallet, address provider) external view returns (bool)",
13
- "function addPreferred(address wallet, string capability, address provider) external",
14
- "function removePreferred(address wallet, string capability, address provider) external",
15
- "function getPreferred(address wallet, string capability) external view returns (address[])",
16
- "function isPreferred(address wallet, string capability, address provider) external view returns (bool)",
17
- "event ProviderBlocked(address indexed wallet, address indexed provider)",
18
- "event ProviderUnblocked(address indexed wallet, address indexed provider)",
19
- "event ProviderPreferred(address indexed wallet, address indexed provider, string capability)",
20
- "event ProviderUnpreferred(address indexed wallet, address indexed provider, string capability)",
21
- ] as const;
22
-
23
- function getPolicyEngine(address: string, runner: ethers.ContractRunner) {
24
- return new ethers.Contract(address, POLICY_ENGINE_EXTENDED_ABI, runner);
25
- }
26
-
27
- const policy = new Command("policy").description("Personal policy enforcement: blocklist and shortlist");
28
-
29
- // ─── blocklist ────────────────────────────────────────────────────────────────
30
-
31
- const blocklist = policy.command("blocklist").description("Hard-stop blocklist: addresses your agent will never accept work from");
32
-
33
- blocklist
34
- .command("add <address>")
35
- .description("Block a provider address")
36
- .option("--json")
37
- .action(async (address, opts) => {
38
- const config = loadConfig();
39
- if (!config.policyEngineAddress) {
40
- console.error("policyEngineAddress not configured. Run `arc402 config set policyEngineAddress <address>`.");
41
- process.exit(1);
42
- }
43
- const { signer, address: wallet } = await requireSigner(config);
44
- const contract = getPolicyEngine(config.policyEngineAddress, signer);
45
- const already = await contract.isBlocked(wallet, address);
46
- if (already) {
47
- if (opts.json) return console.log(JSON.stringify({ address, blocked: true, alreadyBlocked: true }));
48
- return console.log(' ' + c.dim('Already blocked: ' + formatAddress(address)));
49
- }
50
- await (await contract.addToBlocklist(wallet, address)).wait();
51
- if (opts.json) return console.log(JSON.stringify({ address, blocked: true }));
52
- console.log(' ' + c.success + c.white(' Blocked: ' + formatAddress(address)));
53
- });
54
-
55
- blocklist
56
- .command("remove <address>")
57
- .description("Remove a provider from your blocklist")
58
- .option("--json")
59
- .action(async (address, opts) => {
60
- const config = loadConfig();
61
- if (!config.policyEngineAddress) {
62
- console.error("policyEngineAddress not configured. Run `arc402 config set policyEngineAddress <address>`.");
63
- process.exit(1);
64
- }
65
- const { signer, address: wallet } = await requireSigner(config);
66
- const contract = getPolicyEngine(config.policyEngineAddress, signer);
67
- const isBlockedNow = await contract.isBlocked(wallet, address);
68
- if (!isBlockedNow) {
69
- if (opts.json) return console.log(JSON.stringify({ address, blocked: false, notBlocked: true }));
70
- return console.log(' ' + c.dim('Not on blocklist: ' + formatAddress(address)));
71
- }
72
- await (await contract.removeFromBlocklist(wallet, address)).wait();
73
- if (opts.json) return console.log(JSON.stringify({ address, blocked: false }));
74
- console.log(' ' + c.success + c.white(' Unblocked: ' + formatAddress(address)));
75
- });
76
-
77
- blocklist
78
- .command("check <address>")
79
- .description("Check if an address is on your blocklist")
80
- .option("--json")
81
- .action(async (address, opts) => {
82
- const config = loadConfig();
83
- if (!config.policyEngineAddress) {
84
- console.error("policyEngineAddress not configured.");
85
- process.exit(1);
86
- }
87
- const { provider } = await getClient(config);
88
- const { address: wallet } = await requireSigner(config);
89
- const contract = getPolicyEngine(config.policyEngineAddress, provider);
90
- const blocked = await contract.isBlocked(wallet, address);
91
- if (opts.json) return console.log(JSON.stringify({ address, blocked }));
92
- if (blocked) {
93
- console.log(' ' + c.warning + c.white(' ' + formatAddress(address) + ' is BLOCKED'));
94
- } else {
95
- console.log(' ' + c.success + c.white(' ' + formatAddress(address) + ' is not blocked'));
96
- }
97
- });
98
-
99
- blocklist
100
- .command("list")
101
- .description("List all addresses on your blocklist (scans on-chain events)")
102
- .option("--from-block <n>", "Start block for event log query (default: latest-9000 to stay within public RPC limits)")
103
- .option("--json")
104
- .action(async (opts) => {
105
- const config = loadConfig();
106
- if (!config.policyEngineAddress) {
107
- console.error("policyEngineAddress not configured.");
108
- process.exit(1);
109
- }
110
- const { provider } = await getClient(config);
111
- const { address: wallet } = await requireSigner(config);
112
- const contract = getPolicyEngine(config.policyEngineAddress, provider);
113
-
114
- const latestBlock = await provider.getBlockNumber();
115
- const fromBlock = opts.fromBlock !== undefined ? parseInt(opts.fromBlock, 10) : Math.max(0, latestBlock - 9000);
116
-
117
- const [blockedEvents, unblockedEvents] = await Promise.all([
118
- contract.queryFilter(contract.filters.ProviderBlocked(wallet), fromBlock),
119
- contract.queryFilter(contract.filters.ProviderUnblocked(wallet), fromBlock),
120
- ]);
121
- const unblocked = new Set(
122
- unblockedEvents.map((e) => (e as ethers.EventLog).args.provider.toLowerCase())
123
- );
124
- const addresses = blockedEvents
125
- .map((e) => (e as ethers.EventLog).args.provider as string)
126
- .filter((p) => !unblocked.has(p.toLowerCase()));
127
-
128
- if (opts.json) return console.log(JSON.stringify({ wallet, blocked: addresses }, null, 2));
129
- if (addresses.length === 0) return console.log("No addresses on your blocklist");
130
- addresses.forEach((a) => console.log(a));
131
- });
132
-
133
- // ─── shortlist ────────────────────────────────────────────────────────────────
134
-
135
- const shortlist = policy.command("shortlist").description("Preferred providers per capability (preferred or exclusive)");
136
-
137
- shortlist
138
- .command("add <address>")
139
- .description("Add a provider to your shortlist for a capability")
140
- .requiredOption("--capability <name>", "Capability name (e.g. code.review)")
141
- .option("--note <text>", "Optional note (stored off-chain only)")
142
- .option("--json")
143
- .action(async (address, opts) => {
144
- const config = loadConfig();
145
- if (!config.policyEngineAddress) {
146
- console.error("policyEngineAddress not configured. Run `arc402 config set policyEngineAddress <address>`.");
147
- process.exit(1);
148
- }
149
- const { signer, address: wallet } = await requireSigner(config);
150
- const contract = getPolicyEngine(config.policyEngineAddress, signer);
151
- const alreadyPreferred = await contract.isPreferred(wallet, opts.capability, address);
152
- if (alreadyPreferred) {
153
- if (opts.json) return console.log(JSON.stringify({ address, capability: opts.capability, preferred: true, alreadyPreferred: true }));
154
- return console.log(' ' + c.dim('Already shortlisted: ' + formatAddress(address) + ' for ' + opts.capability));
155
- }
156
- await (await contract.addPreferred(wallet, opts.capability, address)).wait();
157
- if (opts.json) return console.log(JSON.stringify({ address, capability: opts.capability, preferred: true }));
158
- console.log(' ' + c.success + c.white(' Shortlisted: ' + formatAddress(address) + ' for ' + opts.capability));
159
- });
160
-
161
- shortlist
162
- .command("remove <address>")
163
- .description("Remove a provider from your shortlist for a capability")
164
- .requiredOption("--capability <name>", "Capability name")
165
- .option("--json")
166
- .action(async (address, opts) => {
167
- const config = loadConfig();
168
- if (!config.policyEngineAddress) {
169
- console.error("policyEngineAddress not configured. Run `arc402 config set policyEngineAddress <address>`.");
170
- process.exit(1);
171
- }
172
- const { signer, address: wallet } = await requireSigner(config);
173
- const contract = getPolicyEngine(config.policyEngineAddress, signer);
174
- const isPreferredNow = await contract.isPreferred(wallet, opts.capability, address);
175
- if (!isPreferredNow) {
176
- if (opts.json) return console.log(JSON.stringify({ address, capability: opts.capability, preferred: false, notPreferred: true }));
177
- return console.log(' ' + c.dim('Not shortlisted: ' + formatAddress(address) + ' for ' + opts.capability));
178
- }
179
- await (await contract.removePreferred(wallet, opts.capability, address)).wait();
180
- if (opts.json) return console.log(JSON.stringify({ address, capability: opts.capability, preferred: false }));
181
- console.log(' ' + c.success + c.white(' Removed from shortlist: ' + formatAddress(address) + ' for ' + opts.capability));
182
- });
183
-
184
- shortlist
185
- .command("check <address>")
186
- .description("Check if an address is shortlisted for a capability")
187
- .requiredOption("--capability <name>", "Capability name")
188
- .option("--json")
189
- .action(async (address, opts) => {
190
- const config = loadConfig();
191
- if (!config.policyEngineAddress) {
192
- console.error("policyEngineAddress not configured.");
193
- process.exit(1);
194
- }
195
- const { provider } = await getClient(config);
196
- const { address: wallet } = await requireSigner(config);
197
- const contract = getPolicyEngine(config.policyEngineAddress, provider);
198
- const preferred = await contract.isPreferred(wallet, opts.capability, address);
199
- if (opts.json) return console.log(JSON.stringify({ address, capability: opts.capability, preferred }));
200
- if (preferred) {
201
- console.log(' ' + c.success + c.white(' ' + formatAddress(address) + ' is shortlisted for ' + opts.capability));
202
- } else {
203
- console.log(' ' + c.warning + c.white(' ' + formatAddress(address) + ' is NOT shortlisted for ' + opts.capability));
204
- }
205
- });
206
-
207
- shortlist
208
- .command("list")
209
- .description("List shortlisted providers, optionally filtered by capability")
210
- .option("--capability <name>", "Filter by capability name")
211
- .option("--from-block <n>", "Start block for event log query (default: latest-9000 to stay within public RPC limits)")
212
- .option("--json")
213
- .action(async (opts) => {
214
- const config = loadConfig();
215
- if (!config.policyEngineAddress) {
216
- console.error("policyEngineAddress not configured.");
217
- process.exit(1);
218
- }
219
- const { provider } = await getClient(config);
220
- const { address: wallet } = await requireSigner(config);
221
- const contract = getPolicyEngine(config.policyEngineAddress, provider);
222
-
223
- if (opts.capability) {
224
- const addresses = await contract.getPreferred(wallet, opts.capability) as string[];
225
- if (opts.json) return console.log(JSON.stringify({ wallet, capability: opts.capability, preferred: addresses }, null, 2));
226
- if (addresses.length === 0) return console.log(`No providers shortlisted for ${opts.capability}`);
227
- addresses.forEach((a) => console.log(a));
228
- return;
229
- }
230
-
231
- // No capability filter — reconstruct from events
232
- const latestBlock = await provider.getBlockNumber();
233
- const fromBlock = opts.fromBlock !== undefined ? parseInt(opts.fromBlock, 10) : Math.max(0, latestBlock - 9000);
234
- const [preferredEvents, unpreferredEvents] = await Promise.all([
235
- contract.queryFilter(contract.filters.ProviderPreferred(wallet), fromBlock),
236
- contract.queryFilter(contract.filters.ProviderUnpreferred(wallet), fromBlock),
237
- ]);
238
- const removed = new Set(
239
- unpreferredEvents.map((e) => {
240
- const args = (e as ethers.EventLog).args;
241
- return `${(args.provider as string).toLowerCase()}::${args.capability as string}`;
242
- })
243
- );
244
- const byCapability: Record<string, string[]> = {};
245
- for (const e of preferredEvents) {
246
- const args = (e as ethers.EventLog).args;
247
- const addr = args.provider as string;
248
- const cap = args.capability as string;
249
- if (removed.has(`${addr.toLowerCase()}::${cap}`)) continue;
250
- if (!byCapability[cap]) byCapability[cap] = [];
251
- if (!byCapability[cap].includes(addr)) byCapability[cap].push(addr);
252
- }
253
-
254
- if (opts.json) return console.log(JSON.stringify({ wallet, shortlist: byCapability }, null, 2));
255
- const caps = Object.keys(byCapability);
256
- if (caps.length === 0) return console.log("No providers on your shortlist");
257
- for (const cap of caps) {
258
- console.log(`${cap}:`);
259
- byCapability[cap].forEach((a) => console.log(` ${a}`));
260
- }
261
- });
262
-
263
- export default policy;
@@ -1,273 +0,0 @@
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 * as http from "http";
6
- import * as https from "https";
7
- import { spawn } from "child_process";
8
- import { c } from '../ui/colors';
9
-
10
- const PID_FILE = path.join(os.homedir(), ".arc402", "relay.pid");
11
-
12
- // ─── HTTP helper (no external deps) ──────────────────────────────────────────
13
-
14
- function relayRequest(
15
- relayUrl: string,
16
- method: string,
17
- urlPath: string,
18
- body?: object
19
- ): Promise<{ status: number; data: unknown }> {
20
- return new Promise((resolve, reject) => {
21
- const parsed = new URL(urlPath, relayUrl);
22
- const isHttps = parsed.protocol === "https:";
23
- const mod = isHttps ? https : http;
24
- const bodyStr = body ? JSON.stringify(body) : undefined;
25
- const options: http.RequestOptions = {
26
- hostname: parsed.hostname,
27
- port: parsed.port || (isHttps ? 443 : 80),
28
- path: parsed.pathname + (parsed.search || ""),
29
- method,
30
- headers: {
31
- "Content-Type": "application/json",
32
- ...(bodyStr ? { "Content-Length": String(Buffer.byteLength(bodyStr)) } : {}),
33
- },
34
- };
35
- const req = mod.request(options, (res) => {
36
- let raw = "";
37
- res.on("data", (c: Buffer) => { raw += c.toString(); });
38
- res.on("end", () => {
39
- try {
40
- resolve({ status: res.statusCode ?? 0, data: JSON.parse(raw) });
41
- } catch {
42
- resolve({ status: res.statusCode ?? 0, data: raw });
43
- }
44
- });
45
- });
46
- req.on("error", reject);
47
- if (bodyStr) req.write(bodyStr);
48
- req.end();
49
- });
50
- }
51
-
52
- // ─── Daemon loop (runs in-process when spawned with ARC402_RELAY_DAEMON=1) ───
53
-
54
- async function runDaemonLoop(opts: {
55
- relayUrl: string;
56
- address: string;
57
- pollInterval: number;
58
- onMessage: string;
59
- }): Promise<void> {
60
- let lastSeen: string | null = null;
61
-
62
- const poll = async () => {
63
- try {
64
- const qs = `?address=${encodeURIComponent(opts.address)}` +
65
- (lastSeen ? `&since=${encodeURIComponent(lastSeen)}` : "");
66
- const result = await relayRequest(opts.relayUrl, "GET", `/poll${qs}`);
67
- const data = result.data as { messages?: Array<{ messageId: string; payload: unknown }> };
68
- const messages = data.messages || [];
69
- for (const msg of messages) {
70
- lastSeen = msg.messageId;
71
- // Spawn the handler script with message JSON on stdin
72
- const child = spawn(opts.onMessage, [], {
73
- stdio: ["pipe", "inherit", "inherit"],
74
- shell: true,
75
- });
76
- child.stdin.write(JSON.stringify(msg));
77
- child.stdin.end();
78
- }
79
- } catch {
80
- // Silent retry — relay may be temporarily unreachable
81
- }
82
- };
83
-
84
- // Initial poll immediately, then on interval
85
- await poll();
86
- setInterval(poll, opts.pollInterval);
87
-
88
- // Keep process alive
89
- process.stdin.resume();
90
- }
91
-
92
- // ─── Command registration ─────────────────────────────────────────────────────
93
-
94
- export function registerRelayCommands(program: Command): void {
95
- const relay = program
96
- .command("relay")
97
- .description("Send and receive messages via an ARC-402 relay (Spec 21)");
98
-
99
- // ── relay send ──────────────────────────────────────────────────────────────
100
- relay
101
- .command("send")
102
- .description("Send a message to an address via the relay")
103
- .requiredOption("--to <address>", "Recipient address")
104
- .requiredOption("--payload <json>", "JSON payload string")
105
- .requiredOption("--relay <url>", "Relay server URL")
106
- .option("--json", "Machine-parseable output")
107
- .action(async (opts) => {
108
- let payload: unknown;
109
- try {
110
- payload = JSON.parse(opts.payload);
111
- } catch {
112
- console.error("Error: --payload must be valid JSON");
113
- process.exit(1);
114
- }
115
-
116
- const result = await relayRequest(opts.relay, "POST", "/send", {
117
- to: opts.to,
118
- payload,
119
- });
120
-
121
- if (result.status !== 200) {
122
- console.error(`Relay error (${result.status}): ${JSON.stringify(result.data)}`);
123
- process.exit(1);
124
- }
125
-
126
- if (opts.json) {
127
- console.log(JSON.stringify(result.data));
128
- } else {
129
- const d = result.data as { messageId?: string };
130
- console.log(' ' + c.success + c.white(' Sent — messageId: ' + d.messageId));
131
- }
132
- });
133
-
134
- // ── relay poll ──────────────────────────────────────────────────────────────
135
- relay
136
- .command("poll")
137
- .description("Poll for messages addressed to an address")
138
- .requiredOption("--address <address>", "Address to poll for")
139
- .requiredOption("--relay <url>", "Relay server URL")
140
- .option("--since <messageId>", "Only return messages after this messageId")
141
- .option("--json", "Machine-parseable output")
142
- .action(async (opts) => {
143
- const qs = `?address=${encodeURIComponent(opts.address)}` +
144
- (opts.since ? `&since=${encodeURIComponent(opts.since)}` : "");
145
-
146
- const result = await relayRequest(opts.relay, "GET", `/poll${qs}`);
147
-
148
- if (result.status !== 200) {
149
- console.error(`Relay error (${result.status}): ${JSON.stringify(result.data)}`);
150
- process.exit(1);
151
- }
152
-
153
- const data = result.data as { messages?: unknown[] };
154
- const messages = data.messages || [];
155
-
156
- if (opts.json) {
157
- console.log(JSON.stringify(data));
158
- return;
159
- }
160
-
161
- if (messages.length === 0) {
162
- console.log(' ' + c.dim('No messages.'));
163
- return;
164
- }
165
-
166
- for (const msg of messages as Array<{ messageId: string; from: string; timestamp: number }>) {
167
- const ts = new Date(msg.timestamp).toISOString();
168
- console.log(`[${ts}] ${msg.messageId.slice(0, 12)}... from=${msg.from}`);
169
- }
170
- });
171
-
172
- // ── relay daemon ────────────────────────────────────────────────────────────
173
- const daemon = relay
174
- .command("daemon")
175
- .description("Persistent relay polling daemon");
176
-
177
- daemon
178
- .command("start")
179
- .description("Start the relay daemon in the background")
180
- .requiredOption("--relay <url>", "Relay server URL")
181
- .requiredOption("--address <address>", "Address to poll for messages")
182
- .requiredOption("--poll-interval <ms>", "Polling interval in milliseconds", "2000")
183
- .requiredOption("--on-message <script>", "Script to invoke for each incoming message (receives JSON on stdin)")
184
- .option("--json", "Machine-parseable output")
185
- .action((opts) => {
186
- // Check if already running
187
- if (fs.existsSync(PID_FILE)) {
188
- try {
189
- const existingPid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim(), 10);
190
- process.kill(existingPid, 0); // Check if alive
191
- console.error(`Daemon already running (PID ${existingPid}). Run 'arc402 relay daemon stop' first.`);
192
- process.exit(1);
193
- } catch {
194
- // PID file is stale — clean it up
195
- fs.unlinkSync(PID_FILE);
196
- }
197
- }
198
-
199
- // Spawn a detached child process
200
- const child = spawn(process.execPath, [__filename], {
201
- detached: true,
202
- stdio: "ignore",
203
- env: {
204
- ...process.env,
205
- ARC402_RELAY_DAEMON: "1",
206
- ARC402_RELAY_URL: opts.relay,
207
- ARC402_RELAY_ADDRESS: opts.address,
208
- ARC402_RELAY_INTERVAL: opts.pollInterval,
209
- ARC402_RELAY_HANDLER: opts.onMessage,
210
- },
211
- });
212
- child.unref();
213
-
214
- fs.mkdirSync(path.dirname(PID_FILE), { recursive: true });
215
- fs.writeFileSync(PID_FILE, String(child.pid), { mode: 0o600 });
216
-
217
- if (opts.json) {
218
- console.log(JSON.stringify({ started: true, pid: child.pid, pidFile: PID_FILE }));
219
- } else {
220
- console.log(' ' + c.success + c.white(' Daemon started (PID ' + child.pid + ')'));
221
- }
222
- });
223
-
224
- daemon
225
- .command("stop")
226
- .description("Stop the relay daemon")
227
- .option("--json", "Machine-parseable output")
228
- .action((opts) => {
229
- if (!fs.existsSync(PID_FILE)) {
230
- if (opts.json) {
231
- console.log(JSON.stringify({ stopped: false, reason: "no pid file" }));
232
- } else {
233
- console.log(' ' + c.dim('No running daemon found.'));
234
- }
235
- return;
236
- }
237
-
238
- const pid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim(), 10);
239
- try {
240
- process.kill(pid, "SIGTERM");
241
- fs.unlinkSync(PID_FILE);
242
- if (opts.json) {
243
- console.log(JSON.stringify({ stopped: true, pid }));
244
- } else {
245
- console.log(' ' + c.success + c.white(' Daemon stopped (PID ' + pid + ')'));
246
- }
247
- } catch (err: unknown) {
248
- const msg = err instanceof Error ? err.message : String(err);
249
- if (opts.json) {
250
- console.log(JSON.stringify({ stopped: false, pid, error: msg }));
251
- } else {
252
- console.error(`Failed to stop daemon: ${msg}`);
253
- // Clean up stale PID file
254
- fs.unlinkSync(PID_FILE);
255
- }
256
- }
257
- });
258
- }
259
-
260
- // ─── Daemon entry point ───────────────────────────────────────────────────────
261
- // When spawned as a background process via daemon start
262
-
263
- if (process.env.ARC402_RELAY_DAEMON === "1") {
264
- runDaemonLoop({
265
- relayUrl: process.env.ARC402_RELAY_URL || "",
266
- address: process.env.ARC402_RELAY_ADDRESS || "",
267
- pollInterval: parseInt(process.env.ARC402_RELAY_INTERVAL || "2000", 10),
268
- onMessage: process.env.ARC402_RELAY_HANDLER || "echo",
269
- }).catch((err) => {
270
- process.stderr.write(`Daemon error: ${err}\n`);
271
- process.exit(1);
272
- });
273
- }
@@ -1,24 +0,0 @@
1
- import { Command } from "commander";
2
- import { ProviderResponseType, ServiceAgreementClient } from "@arc402/sdk";
3
- import { loadConfig } from "../config";
4
- import { getClient, requireSigner } from "../client";
5
- import { hashFile, hashString } from "../utils/hash";
6
- import { c } from '../ui/colors';
7
- import { startSpinner } from '../ui/spinner';
8
-
9
- export function registerRemediateCommands(program: Command): void {
10
- const remediate = program.command("remediate").description("Negotiated remediation before formal dispute");
11
- remediate.command("request <id>").requiredOption("--text <feedback>").option("--uri <uri>", "Structured feedback URI", "").option("--file <path>").option("--previous <hash>", "Previous transcript hash", "0x0000000000000000000000000000000000000000000000000000000000000000").action(async (id, opts) => {
12
- const config = loadConfig(); if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config"); const { signer } = await requireSigner(config); const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
13
- const hash = opts.file ? hashFile(opts.file) : hashString(opts.text); await client.requestRevision(BigInt(id), hash, opts.uri, opts.previous); console.log(' ' + c.success + c.white(' Revision requested — agreement #' + id));
14
- });
15
- remediate.command("respond <id>").requiredOption("--type <type>", "revise|defend|counter|partial-settlement|human-review|escalate").requiredOption("--text <response>").option("--uri <uri>", "Structured response URI", "").option("--file <path>").option("--previous <hash>", "Previous transcript hash", "0x0000000000000000000000000000000000000000000000000000000000000000").option("--provider-payout <amount>", "Wei/token units for partial settlement", "0").action(async (id, opts) => {
16
- const config = loadConfig(); if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config"); const { signer } = await requireSigner(config); const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
17
- const map: Record<string, ProviderResponseType> = { revise: ProviderResponseType.REVISE, defend: ProviderResponseType.DEFEND, counter: ProviderResponseType.COUNTER, 'partial-settlement': ProviderResponseType.PARTIAL_SETTLEMENT, 'human-review': ProviderResponseType.REQUEST_HUMAN_REVIEW, escalate: ProviderResponseType.ESCALATE };
18
- const hash = opts.file ? hashFile(opts.file) : hashString(opts.text); await client.respondToRevision(BigInt(id), map[String(opts.type)], hash, opts.uri, opts.previous, BigInt(opts.providerPayout)); console.log(' ' + c.success + c.white(' Response recorded — agreement #' + id));
19
- });
20
- remediate.command("status <id>").option("--json").action(async (id, opts) => {
21
- const config = loadConfig(); if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config"); const { provider } = await getClient(config); const client = new ServiceAgreementClient(config.serviceAgreementAddress, provider);
22
- const remediation = await client.getRemediationCase(BigInt(id)); const out = { remediation }; console.log(JSON.stringify(out, (_k, value) => typeof value === 'bigint' ? value.toString() : value, opts.json ? 2 : 2));
23
- });
24
- }
@@ -1,79 +0,0 @@
1
- import { Command } from "commander";
2
- import { ReputationOracleClient, ReputationSignalType } from "@arc402/sdk";
3
- import { ethers } from "ethers";
4
- import { loadConfig } from "../config";
5
- import { getClient, requireSigner } from "../client";
6
- import { c } from '../ui/colors';
7
- import { startSpinner } from '../ui/spinner';
8
- import { renderTree } from '../ui/tree';
9
- import { formatAddress } from '../ui/format';
10
-
11
- const reputation = new Command("reputation").description("Network-wide reputation signals");
12
-
13
- reputation
14
- .command("warn <address>")
15
- .description("Publish a WARN signal against an address (trust-weighted)")
16
- .requiredOption("--reason <text>", "Reason for the warning")
17
- .option("--json")
18
- .action(async (address, opts) => {
19
- const config = loadConfig();
20
- if (!config.reputationOracleAddress) {
21
- console.error("reputationOracleAddress not configured. Run `arc402 config set reputationOracleAddress <address>`.");
22
- process.exit(1);
23
- }
24
- const { signer } = await requireSigner(config);
25
- const oracle = new ReputationOracleClient(config.reputationOracleAddress, signer);
26
- await oracle.publishSignal(address, ReputationSignalType.WARN, ethers.ZeroHash, opts.reason);
27
- if (opts.json) return console.log(JSON.stringify({ address, signal: "WARN", reason: opts.reason }, null, 2));
28
- console.log(' ' + c.warning + c.white(' WARN published against ' + formatAddress(address)));
29
- });
30
-
31
- reputation
32
- .command("block <address>")
33
- .description("Publish a BLOCK signal against an address (stronger than warn)")
34
- .requiredOption("--reason <text>", "Reason for the block signal")
35
- .option("--json")
36
- .action(async (address, opts) => {
37
- const config = loadConfig();
38
- if (!config.reputationOracleAddress) {
39
- console.error("reputationOracleAddress not configured. Run `arc402 config set reputationOracleAddress <address>`.");
40
- process.exit(1);
41
- }
42
- const { signer } = await requireSigner(config);
43
- const oracle = new ReputationOracleClient(config.reputationOracleAddress, signer);
44
- await oracle.publishSignal(address, ReputationSignalType.BLOCK, ethers.ZeroHash, opts.reason);
45
- if (opts.json) return console.log(JSON.stringify({ address, signal: "BLOCK", reason: opts.reason }, null, 2));
46
- console.log(' ' + c.failure + c.white(' BLOCK published against ' + formatAddress(address)));
47
- });
48
-
49
- reputation
50
- .command("check <address>")
51
- .description("Query the ReputationOracle for endorsements, warnings, and blocks against an address")
52
- .option("--json")
53
- .action(async (address, opts) => {
54
- const config = loadConfig();
55
- if (!config.reputationOracleAddress) {
56
- console.error("reputationOracleAddress not configured.");
57
- process.exit(1);
58
- }
59
- const { provider } = await getClient(config);
60
- const oracle = new ReputationOracleClient(config.reputationOracleAddress, provider);
61
- const rep = await oracle.getReputation(address);
62
- const payload = {
63
- address,
64
- endorsements: rep.endorsements.toString(),
65
- warnings: rep.warnings.toString(),
66
- blocks: rep.blocks.toString(),
67
- weightedScore: rep.weightedScore.toString(),
68
- };
69
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
70
- console.log('\n ' + c.mark + c.white(' Reputation — ' + formatAddress(address)));
71
- renderTree([
72
- { label: 'Endorsements', value: rep.endorsements.toString() },
73
- { label: 'Warnings', value: rep.warnings.toString() },
74
- { label: 'Blocks', value: rep.blocks.toString() },
75
- { label: 'Score', value: rep.weightedScore.toString(), last: true },
76
- ]);
77
- });
78
-
79
- export default reputation;