arc402-cli 1.0.0-rc.1 → 1.0.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 (253) hide show
  1. package/README.md +41 -2
  2. package/dist/abis.d.ts +1 -0
  3. package/dist/abis.d.ts.map +1 -1
  4. package/dist/abis.js +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/wallet.d.ts.map +1 -1
  28. package/dist/commands/wallet.js +299 -65
  29. package/dist/commands/wallet.js.map +1 -1
  30. package/dist/commands/watch.d.ts.map +1 -1
  31. package/dist/commands/watch.js +146 -9
  32. package/dist/commands/watch.js.map +1 -1
  33. package/dist/commands/workroom.d.ts.map +1 -1
  34. package/dist/commands/workroom.js +112 -6
  35. package/dist/commands/workroom.js.map +1 -1
  36. package/dist/config.d.ts +12 -0
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js +41 -4
  39. package/dist/config.js.map +1 -1
  40. package/dist/daemon/compute-metering.d.ts +61 -0
  41. package/dist/daemon/compute-metering.d.ts.map +1 -0
  42. package/dist/daemon/compute-metering.js +299 -0
  43. package/dist/daemon/compute-metering.js.map +1 -0
  44. package/dist/daemon/compute-session.d.ts +100 -0
  45. package/dist/daemon/compute-session.d.ts.map +1 -0
  46. package/dist/daemon/compute-session.js +231 -0
  47. package/dist/daemon/compute-session.js.map +1 -0
  48. package/dist/daemon/config.d.ts +33 -1
  49. package/dist/daemon/config.d.ts.map +1 -1
  50. package/dist/daemon/config.js +69 -0
  51. package/dist/daemon/config.js.map +1 -1
  52. package/dist/daemon/credentials.d.ts +24 -0
  53. package/dist/daemon/credentials.d.ts.map +1 -0
  54. package/dist/daemon/credentials.js +80 -0
  55. package/dist/daemon/credentials.js.map +1 -0
  56. package/dist/daemon/delivery-client.d.ts +35 -0
  57. package/dist/daemon/delivery-client.d.ts.map +1 -0
  58. package/dist/daemon/delivery-client.js +231 -0
  59. package/dist/daemon/delivery-client.js.map +1 -0
  60. package/dist/daemon/file-delivery.d.ts +98 -0
  61. package/dist/daemon/file-delivery.d.ts.map +1 -0
  62. package/dist/daemon/file-delivery.js +461 -0
  63. package/dist/daemon/file-delivery.js.map +1 -0
  64. package/dist/daemon/index.d.ts +1 -0
  65. package/dist/daemon/index.d.ts.map +1 -1
  66. package/dist/daemon/index.js +793 -227
  67. package/dist/daemon/index.js.map +1 -1
  68. package/dist/daemon/notify.d.ts +35 -6
  69. package/dist/daemon/notify.d.ts.map +1 -1
  70. package/dist/daemon/notify.js +176 -48
  71. package/dist/daemon/notify.js.map +1 -1
  72. package/dist/daemon/worker-executor.d.ts +71 -0
  73. package/dist/daemon/worker-executor.d.ts.map +1 -0
  74. package/dist/daemon/worker-executor.js +382 -0
  75. package/dist/daemon/worker-executor.js.map +1 -0
  76. package/dist/drain-v4.js +2 -2
  77. package/dist/drain-v4.js.map +1 -1
  78. package/dist/endpoint-notify.d.ts +9 -1
  79. package/dist/endpoint-notify.d.ts.map +1 -1
  80. package/dist/endpoint-notify.js +116 -3
  81. package/dist/endpoint-notify.js.map +1 -1
  82. package/dist/index.js +81 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/program.d.ts.map +1 -1
  85. package/dist/program.js +6 -0
  86. package/dist/program.js.map +1 -1
  87. package/dist/repl.d.ts.map +1 -1
  88. package/dist/repl.js +69 -486
  89. package/dist/repl.js.map +1 -1
  90. package/dist/tui/App.d.ts +12 -0
  91. package/dist/tui/App.d.ts.map +1 -0
  92. package/dist/tui/App.js +154 -0
  93. package/dist/tui/App.js.map +1 -0
  94. package/dist/tui/Footer.d.ts +11 -0
  95. package/dist/tui/Footer.d.ts.map +1 -0
  96. package/dist/tui/Footer.js +13 -0
  97. package/dist/tui/Footer.js.map +1 -0
  98. package/dist/tui/Header.d.ts +14 -0
  99. package/dist/tui/Header.d.ts.map +1 -0
  100. package/dist/tui/Header.js +19 -0
  101. package/dist/tui/Header.js.map +1 -0
  102. package/dist/tui/InputLine.d.ts +11 -0
  103. package/dist/tui/InputLine.d.ts.map +1 -0
  104. package/dist/tui/InputLine.js +145 -0
  105. package/dist/tui/InputLine.js.map +1 -0
  106. package/dist/tui/Viewport.d.ts +14 -0
  107. package/dist/tui/Viewport.d.ts.map +1 -0
  108. package/dist/tui/Viewport.js +48 -0
  109. package/dist/tui/Viewport.js.map +1 -0
  110. package/dist/tui/WalletConnectPairing.d.ts +23 -0
  111. package/dist/tui/WalletConnectPairing.d.ts.map +1 -0
  112. package/dist/tui/WalletConnectPairing.js +61 -0
  113. package/dist/tui/WalletConnectPairing.js.map +1 -0
  114. package/dist/tui/components/Button.d.ts +7 -0
  115. package/dist/tui/components/Button.d.ts.map +1 -0
  116. package/dist/tui/components/Button.js +21 -0
  117. package/dist/tui/components/Button.js.map +1 -0
  118. package/dist/tui/components/CeremonyView.d.ts +13 -0
  119. package/dist/tui/components/CeremonyView.d.ts.map +1 -0
  120. package/dist/tui/components/CeremonyView.js +10 -0
  121. package/dist/tui/components/CeremonyView.js.map +1 -0
  122. package/dist/tui/components/CompletionDropdown.d.ts +7 -0
  123. package/dist/tui/components/CompletionDropdown.d.ts.map +1 -0
  124. package/dist/tui/components/CompletionDropdown.js +23 -0
  125. package/dist/tui/components/CompletionDropdown.js.map +1 -0
  126. package/dist/tui/components/ConfirmPrompt.d.ts +9 -0
  127. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  128. package/dist/tui/components/ConfirmPrompt.js +10 -0
  129. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  130. package/dist/tui/components/CustomTextInput.d.ts +15 -0
  131. package/dist/tui/components/CustomTextInput.d.ts.map +1 -0
  132. package/dist/tui/components/CustomTextInput.js +99 -0
  133. package/dist/tui/components/CustomTextInput.js.map +1 -0
  134. package/dist/tui/components/InteractiveTable.d.ts +14 -0
  135. package/dist/tui/components/InteractiveTable.d.ts.map +1 -0
  136. package/dist/tui/components/InteractiveTable.js +61 -0
  137. package/dist/tui/components/InteractiveTable.js.map +1 -0
  138. package/dist/tui/components/StepSpinner.d.ts +11 -0
  139. package/dist/tui/components/StepSpinner.d.ts.map +1 -0
  140. package/dist/tui/components/StepSpinner.js +32 -0
  141. package/dist/tui/components/StepSpinner.js.map +1 -0
  142. package/dist/tui/components/Toast.d.ts +18 -0
  143. package/dist/tui/components/Toast.d.ts.map +1 -0
  144. package/dist/tui/components/Toast.js +29 -0
  145. package/dist/tui/components/Toast.js.map +1 -0
  146. package/dist/tui/index.d.ts +2 -0
  147. package/dist/tui/index.d.ts.map +1 -0
  148. package/dist/tui/index.js +55 -0
  149. package/dist/tui/index.js.map +1 -0
  150. package/dist/tui/useChat.d.ts +11 -0
  151. package/dist/tui/useChat.d.ts.map +1 -0
  152. package/dist/tui/useChat.js +91 -0
  153. package/dist/tui/useChat.js.map +1 -0
  154. package/dist/tui/useCommand.d.ts +12 -0
  155. package/dist/tui/useCommand.d.ts.map +1 -0
  156. package/dist/tui/useCommand.js +137 -0
  157. package/dist/tui/useCommand.js.map +1 -0
  158. package/dist/tui/useNotifications.d.ts +9 -0
  159. package/dist/tui/useNotifications.d.ts.map +1 -0
  160. package/dist/tui/useNotifications.js +17 -0
  161. package/dist/tui/useNotifications.js.map +1 -0
  162. package/dist/tui/useScroll.d.ts +17 -0
  163. package/dist/tui/useScroll.d.ts.map +1 -0
  164. package/dist/tui/useScroll.js +46 -0
  165. package/dist/tui/useScroll.js.map +1 -0
  166. package/dist/ui/format.d.ts.map +1 -1
  167. package/dist/ui/format.js +2 -0
  168. package/dist/ui/format.js.map +1 -1
  169. package/dist/ui/qr-render.d.ts +25 -0
  170. package/dist/ui/qr-render.d.ts.map +1 -0
  171. package/dist/ui/qr-render.js +90 -0
  172. package/dist/ui/qr-render.js.map +1 -0
  173. package/dist/ui/rpc-fallback.d.ts +11 -0
  174. package/dist/ui/rpc-fallback.d.ts.map +1 -0
  175. package/dist/ui/rpc-fallback.js +58 -0
  176. package/dist/ui/rpc-fallback.js.map +1 -0
  177. package/dist/walletconnect.d.ts +4 -0
  178. package/dist/walletconnect.d.ts.map +1 -1
  179. package/dist/walletconnect.js.map +1 -1
  180. package/package.json +10 -2
  181. package/scripts/authorize-machine-key.ts +0 -43
  182. package/scripts/drain-wallet.ts +0 -149
  183. package/scripts/execute-spend-only.ts +0 -81
  184. package/scripts/register-agent-userop.ts +0 -186
  185. package/src/abis.ts +0 -187
  186. package/src/bundler.ts +0 -235
  187. package/src/client.ts +0 -36
  188. package/src/coinbase-smart-wallet.ts +0 -51
  189. package/src/commands/accept.ts +0 -64
  190. package/src/commands/agent-handshake.ts +0 -72
  191. package/src/commands/agent.ts +0 -691
  192. package/src/commands/agreements.ts +0 -350
  193. package/src/commands/arbitrator.ts +0 -180
  194. package/src/commands/arena-handshake.ts +0 -257
  195. package/src/commands/arena.ts +0 -122
  196. package/src/commands/cancel.ts +0 -35
  197. package/src/commands/channel.ts +0 -218
  198. package/src/commands/coldstart.ts +0 -165
  199. package/src/commands/config.ts +0 -58
  200. package/src/commands/contract-interaction.ts +0 -166
  201. package/src/commands/daemon.ts +0 -978
  202. package/src/commands/deliver.ts +0 -148
  203. package/src/commands/discover.ts +0 -297
  204. package/src/commands/dispute.ts +0 -375
  205. package/src/commands/endpoint.ts +0 -620
  206. package/src/commands/feed.ts +0 -229
  207. package/src/commands/hire.ts +0 -245
  208. package/src/commands/migrate.ts +0 -177
  209. package/src/commands/negotiate.ts +0 -271
  210. package/src/commands/openshell.ts +0 -1055
  211. package/src/commands/owner.ts +0 -35
  212. package/src/commands/policy.ts +0 -263
  213. package/src/commands/relay.ts +0 -273
  214. package/src/commands/remediate.ts +0 -24
  215. package/src/commands/reputation.ts +0 -79
  216. package/src/commands/setup.ts +0 -343
  217. package/src/commands/trust.ts +0 -27
  218. package/src/commands/verify.ts +0 -91
  219. package/src/commands/wallet.ts +0 -3280
  220. package/src/commands/watch.ts +0 -23
  221. package/src/commands/watchtower.ts +0 -248
  222. package/src/commands/workroom.ts +0 -959
  223. package/src/config.ts +0 -174
  224. package/src/daemon/config.ts +0 -308
  225. package/src/daemon/hire-listener.ts +0 -226
  226. package/src/daemon/index.ts +0 -955
  227. package/src/daemon/job-lifecycle.ts +0 -215
  228. package/src/daemon/notify.ts +0 -157
  229. package/src/daemon/token-metering.ts +0 -183
  230. package/src/daemon/userops.ts +0 -119
  231. package/src/daemon/wallet-monitor.ts +0 -90
  232. package/src/drain-v4.ts +0 -159
  233. package/src/endpoint-config.ts +0 -83
  234. package/src/endpoint-notify.ts +0 -46
  235. package/src/index.ts +0 -26
  236. package/src/openshell-runtime.ts +0 -277
  237. package/src/program.ts +0 -83
  238. package/src/repl.ts +0 -680
  239. package/src/signing.ts +0 -28
  240. package/src/telegram-notify.ts +0 -88
  241. package/src/ui/banner.ts +0 -51
  242. package/src/ui/colors.ts +0 -30
  243. package/src/ui/format.ts +0 -77
  244. package/src/ui/spinner.ts +0 -56
  245. package/src/ui/tree.ts +0 -16
  246. package/src/utils/format.ts +0 -48
  247. package/src/utils/hash.ts +0 -5
  248. package/src/utils/time.ts +0 -15
  249. package/src/wallet-router.ts +0 -178
  250. package/src/walletconnect-session.ts +0 -27
  251. package/src/walletconnect.ts +0 -294
  252. package/test/time.test.js +0 -11
  253. 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;