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
@@ -26,6 +26,26 @@ const tree_1 = require("../ui/tree");
26
26
  const spinner_1 = require("../ui/spinner");
27
27
  const colors_1 = require("../ui/colors");
28
28
  const POLICY_ENGINE_DEFAULT = "0x44102e70c2A366632d98Fe40d892a2501fC7fFF2";
29
+ const GUARDIAN_KEY_PATH = path_1.default.join(os_1.default.homedir(), ".arc402", "guardian.key");
30
+ /** Save guardian private key to a restricted standalone file (never to config.json). */
31
+ function saveGuardianKey(privateKey) {
32
+ fs_1.default.mkdirSync(path_1.default.dirname(GUARDIAN_KEY_PATH), { recursive: true, mode: 0o700 });
33
+ fs_1.default.writeFileSync(GUARDIAN_KEY_PATH, privateKey + "\n", { mode: 0o400 });
34
+ }
35
+ /** Load guardian private key from file, falling back to config for backwards compat. */
36
+ function loadGuardianKey(config) {
37
+ try {
38
+ return fs_1.default.readFileSync(GUARDIAN_KEY_PATH, "utf-8").trim();
39
+ }
40
+ catch {
41
+ // Migration: if key still in config, migrate it to the file now
42
+ if (config.guardianPrivateKey) {
43
+ saveGuardianKey(config.guardianPrivateKey);
44
+ return config.guardianPrivateKey;
45
+ }
46
+ return null;
47
+ }
48
+ }
29
49
  function parseAmount(raw) {
30
50
  const lower = raw.toLowerCase();
31
51
  if (lower.endsWith("eth")) {
@@ -161,6 +181,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
161
181
  await sendTx({ to: walletAddress, data: mkIface.encodeFunctionData("authorizeMachineKey", [machineKeyAddress]), value: "0x0" }, "authorizeMachineKey");
162
182
  console.log(" " + colors_1.c.success + " Machine key authorized");
163
183
  }
184
+ // Save progress after machine key step
185
+ config.onboardingProgress = { walletAddress, step: 2, completedSteps: ["machineKey"] };
186
+ (0, config_1.saveConfig)(config);
164
187
  // ── Step 3: Passkey ───────────────────────────────────────────────────────
165
188
  console.log("\n" + colors_1.c.dim("── Step 3: Passkey (Face ID / WebAuthn) ──────────────────────"));
166
189
  let passkeyActive = false;
@@ -192,6 +215,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
192
215
  console.log(" " + colors_1.c.success + " Passkey set (via browser)");
193
216
  }
194
217
  }
218
+ // Save progress after passkey step
219
+ config.onboardingProgress = { walletAddress, step: 3, completedSteps: ["machineKey", "passkey"] };
220
+ (0, config_1.saveConfig)(config);
195
221
  // ── Step 4: Policy ────────────────────────────────────────────────────────
196
222
  console.log("\n" + colors_1.c.dim("── Step 4: Policy ─────────────────────────────────────────────"));
197
223
  // 4a) setVelocityLimit
@@ -240,12 +266,15 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
240
266
  const guardianInput = guardianAns.guardian?.trim() ?? "";
241
267
  if (guardianInput.toLowerCase() === "g") {
242
268
  const generatedGuardian = ethers_1.ethers.Wallet.createRandom();
243
- config.guardianPrivateKey = generatedGuardian.privateKey;
269
+ // Save guardian private key to a separate restricted file, NOT config.json
270
+ const guardianKeyPath = path_1.default.join(os_1.default.homedir(), ".arc402", "guardian.key");
271
+ fs_1.default.mkdirSync(path_1.default.dirname(guardianKeyPath), { recursive: true, mode: 0o700 });
272
+ fs_1.default.writeFileSync(guardianKeyPath, generatedGuardian.privateKey + "\n", { mode: 0o400 });
273
+ // Only save address (not private key) to config
244
274
  config.guardianAddress = generatedGuardian.address;
245
275
  (0, config_1.saveConfig)(config);
246
276
  guardianAddress = generatedGuardian.address;
247
- console.log("\n " + colors_1.c.warning + " IMPORTANT: Save this guardian private key (emergency freeze key):");
248
- console.log(" " + colors_1.c.dim(generatedGuardian.privateKey));
277
+ console.log("\n " + colors_1.c.warning + " Guardian key saved to ~/.arc402/guardian.key move offline for security");
249
278
  console.log(" " + colors_1.c.dim("Address: ") + colors_1.c.white(generatedGuardian.address) + "\n");
250
279
  const guardianIface = new ethers_1.ethers.Interface(["function setGuardian(address _guardian) external"]);
251
280
  await sendTx({ to: walletAddress, data: guardianIface.encodeFunctionData("setGuardian", [guardianAddress]), value: "0x0" }, "setGuardian");
@@ -297,6 +326,9 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
297
326
  await sendTx({ to: policyAddress, data: contractInteractionIface.encodeFunctionData("enableContractInteraction", [walletAddress, handshakeAddress]), value: "0x0" }, "enableContractInteraction: Handshake");
298
327
  (0, config_1.saveConfig)(config);
299
328
  console.log(" " + colors_1.c.success + " Policy configured");
329
+ // Save progress after policy step
330
+ config.onboardingProgress = { walletAddress, step: 4, completedSteps: ["machineKey", "passkey", "policy"] };
331
+ (0, config_1.saveConfig)(config);
300
332
  // ── Step 5: Agent Registration ─────────────────────────────────────────────
301
333
  console.log("\n" + colors_1.c.dim("── Step 5: Agent Registration ─────────────────────────────────"));
302
334
  let agentAlreadyRegistered = false;
@@ -374,6 +406,90 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
374
406
  else {
375
407
  console.log(" " + colors_1.c.warning + " AgentRegistry address not configured — skipping");
376
408
  }
409
+ // Save progress after agent step, then clear on ceremony complete
410
+ config.onboardingProgress = { walletAddress, step: 5, completedSteps: ["machineKey", "passkey", "policy", "agent"] };
411
+ (0, config_1.saveConfig)(config);
412
+ // ── Step 7: Workroom Init ─────────────────────────────────────────────────
413
+ console.log("\n" + colors_1.c.dim("── Step 7: Workroom ────────────────────────────────────────────"));
414
+ let workroomInitialized = false;
415
+ let dockerFound = false;
416
+ try {
417
+ const dockerCheck = (0, child_process_1.spawnSync)("docker", ["--version"], { encoding: "utf-8", timeout: 5000 });
418
+ dockerFound = dockerCheck.status === 0;
419
+ }
420
+ catch {
421
+ dockerFound = false;
422
+ }
423
+ if (dockerFound) {
424
+ console.log(" " + colors_1.c.dim("Docker found — initializing workroom..."));
425
+ try {
426
+ const initResult = (0, child_process_1.spawnSync)(process.execPath, [process.argv[1], "workroom", "init"], {
427
+ encoding: "utf-8",
428
+ timeout: 120000,
429
+ env: { ...process.env },
430
+ });
431
+ workroomInitialized = initResult.status === 0;
432
+ if (workroomInitialized) {
433
+ console.log(" " + colors_1.c.success + " Workroom initialized");
434
+ }
435
+ else {
436
+ console.log(" " + colors_1.c.warning + " Workroom init incomplete — run: arc402 workroom init");
437
+ }
438
+ }
439
+ catch {
440
+ console.log(" " + colors_1.c.warning + " Workroom init failed — run: arc402 workroom init");
441
+ }
442
+ }
443
+ else {
444
+ console.log(" " + colors_1.c.warning + " Docker not found");
445
+ console.log(" Install Docker: https://docs.docker.com/get-docker/");
446
+ console.log(" Or run daemon in host mode: " + colors_1.c.white("arc402 daemon start --host"));
447
+ }
448
+ // ── Step 8: Daemon Start ──────────────────────────────────────────────────
449
+ console.log("\n" + colors_1.c.dim("── Step 8: Daemon ──────────────────────────────────────────────"));
450
+ let daemonRunning = false;
451
+ const relayPort = 4402;
452
+ if (workroomInitialized) {
453
+ try {
454
+ const startResult = (0, child_process_1.spawnSync)(process.execPath, [process.argv[1], "workroom", "start"], {
455
+ encoding: "utf-8",
456
+ timeout: 30000,
457
+ env: { ...process.env },
458
+ });
459
+ daemonRunning = startResult.status === 0;
460
+ if (daemonRunning) {
461
+ console.log(" " + colors_1.c.success + " Daemon online (port " + relayPort + ")");
462
+ }
463
+ else {
464
+ console.log(" " + colors_1.c.warning + " Daemon start failed — run: arc402 workroom start");
465
+ }
466
+ }
467
+ catch {
468
+ console.log(" " + colors_1.c.warning + " Daemon start failed — run: arc402 workroom start");
469
+ }
470
+ }
471
+ else if (!dockerFound) {
472
+ try {
473
+ const startResult = (0, child_process_1.spawnSync)(process.execPath, [process.argv[1], "daemon", "start", "--host"], {
474
+ encoding: "utf-8",
475
+ timeout: 30000,
476
+ env: { ...process.env },
477
+ });
478
+ daemonRunning = startResult.status === 0;
479
+ if (daemonRunning) {
480
+ console.log(" " + colors_1.c.success + " Daemon online — host mode (port " + relayPort + ")");
481
+ }
482
+ else {
483
+ console.log(" " + colors_1.c.warning + " Daemon not started — run: arc402 daemon start --host");
484
+ }
485
+ }
486
+ catch {
487
+ console.log(" " + colors_1.c.warning + " Daemon not started — run: arc402 daemon start --host");
488
+ }
489
+ }
490
+ else {
491
+ console.log(" " + colors_1.c.warning + " Daemon not started — run: arc402 workroom init first");
492
+ }
377
493
  // ── Step 6: Summary ───────────────────────────────────────────────────────
378
494
  const trustScore = await (async () => {
379
495
  try {
@@ -385,6 +501,18 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
385
501
  return "100";
386
502
  }
387
503
  })();
504
+ const workroomLabel = dockerFound
505
+ ? (workroomInitialized ? (daemonRunning ? colors_1.c.green("✓ Running") : colors_1.c.yellow("✓ Initialized")) : colors_1.c.yellow("⚠ Init needed"))
506
+ : colors_1.c.yellow("⚠ No Docker");
507
+ const daemonLabel = daemonRunning
508
+ ? colors_1.c.green("✓ Online (port " + relayPort + ")")
509
+ : colors_1.c.dim("not started");
510
+ const endpointLabel = agentEndpoint
511
+ ? colors_1.c.white(agentEndpoint) + colors_1.c.dim(` → localhost:${relayPort}`)
512
+ : colors_1.c.dim("—");
513
+ // Clear onboarding progress — ceremony complete
514
+ delete config.onboardingProgress;
515
+ (0, config_1.saveConfig)(config);
388
516
  console.log("\n " + colors_1.c.success + colors_1.c.white(" Onboarding complete"));
389
517
  (0, tree_1.renderTree)([
390
518
  { label: "Wallet", value: colors_1.c.white(walletAddress) },
@@ -396,11 +524,12 @@ async function runCompleteOnboardingCeremony(walletAddress, ownerAddress, config
396
524
  { label: "Hire limit", value: colors_1.c.white(hireLimit + " ETH") },
397
525
  { label: "Agent", value: agentName ? colors_1.c.white(agentName) : colors_1.c.dim("not registered") },
398
526
  { label: "Service", value: agentServiceType ? colors_1.c.white(agentServiceType) : colors_1.c.dim("—") },
399
- { label: "Endpoint", value: agentEndpoint ? colors_1.c.white(agentEndpoint) : colors_1.c.dim("—") },
527
+ { label: "Workroom", value: workroomLabel },
528
+ { label: "Daemon", value: daemonLabel },
529
+ { label: "Endpoint", value: endpointLabel },
400
530
  { label: "Trust", value: colors_1.c.white(`${trustScore}`), last: true },
401
531
  ]);
402
532
  console.log("\n " + colors_1.c.dim("Next: fund your wallet with 0.002 ETH on Base"));
403
- console.log(" " + colors_1.c.dim(" arc402 daemon start"));
404
533
  }
405
534
  function printOpenShellHint() {
406
535
  const r = (0, child_process_1.spawnSync)("which", ["openshell"], { encoding: "utf-8" });
@@ -422,11 +551,20 @@ function registerWalletCommands(program) {
422
551
  const usdcAddress = (0, config_1.getUsdcAddress)(config);
423
552
  const usdc = new ethers_1.ethers.Contract(usdcAddress, ["function balanceOf(address owner) external view returns (uint256)"], provider);
424
553
  const trust = new sdk_1.TrustClient(config.trustRegistryAddress, provider);
425
- const [ethBalance, usdcBalance, score] = await Promise.all([
426
- provider.getBalance(address),
427
- usdc.balanceOf(address),
428
- trust.getScore(address),
429
- ]);
554
+ const statusSpinner = opts.json ? null : (0, spinner_1.startSpinner)("Loading wallet status…");
555
+ let ethBalance, usdcBalance, score;
556
+ try {
557
+ [ethBalance, usdcBalance, score] = await Promise.all([
558
+ provider.getBalance(address),
559
+ usdc.balanceOf(address),
560
+ trust.getScore(address),
561
+ ]);
562
+ statusSpinner?.succeed("Wallet status loaded");
563
+ }
564
+ catch (err) {
565
+ statusSpinner?.fail("Failed to load wallet status");
566
+ throw err;
567
+ }
430
568
  // Query contract wallet for frozen/guardian state if deployed
431
569
  let contractFrozen = null;
432
570
  let contractGuardian = null;
@@ -546,16 +684,40 @@ function registerWalletCommands(program) {
546
684
  console.log(colors_1.c.dim("Next: fund your wallet with ETH, then run: arc402 wallet deploy"));
547
685
  });
548
686
  // ─── import ────────────────────────────────────────────────────────────────
549
- wallet.command("import <privateKey>")
550
- .description("Import an existing private key")
687
+ wallet.command("import")
688
+ .description("Import an existing private key (use --key-file or stdin prompt)")
551
689
  .option("--network <network>", "Network (base-mainnet or base-sepolia)", "base-sepolia")
552
- .action(async (privateKey, opts) => {
690
+ .option("--key-file <path>", "Read private key from file instead of prompting")
691
+ .action(async (opts) => {
553
692
  const network = opts.network;
554
693
  const defaults = config_1.NETWORK_DEFAULTS[network];
555
694
  if (!defaults) {
556
695
  console.error(`Unknown network: ${network}. Use base-mainnet or base-sepolia.`);
557
696
  process.exit(1);
558
697
  }
698
+ let privateKey;
699
+ if (opts.keyFile) {
700
+ try {
701
+ privateKey = fs_1.default.readFileSync(opts.keyFile, "utf-8").trim();
702
+ }
703
+ catch (e) {
704
+ console.error(`Cannot read key file: ${e instanceof Error ? e.message : String(e)}`);
705
+ process.exit(1);
706
+ }
707
+ }
708
+ else {
709
+ // Interactive prompt — hidden input avoids shell history
710
+ const answer = await (0, prompts_1.default)({
711
+ type: "password",
712
+ name: "key",
713
+ message: "Paste private key (hidden):",
714
+ });
715
+ privateKey = (answer.key ?? "").trim();
716
+ if (!privateKey) {
717
+ console.error("No private key entered.");
718
+ process.exit(1);
719
+ }
720
+ }
559
721
  let imported;
560
722
  try {
561
723
  imported = new ethers_1.ethers.Wallet(privateKey);
@@ -703,8 +865,31 @@ function registerWalletCommands(program) {
703
865
  .option("--smart-wallet", "Connect via Base Smart Wallet (Coinbase Wallet SDK) instead of WalletConnect")
704
866
  .option("--hardware", "Hardware wallet mode: show raw wc: URI only (for Ledger Live, Trezor Suite, etc.)")
705
867
  .option("--sponsored", "Use CDP paymaster for gas sponsorship (requires paymasterUrl + cdpKeyName + CDP_PRIVATE_KEY env)")
868
+ .option("--dry-run", "Simulate the deployment ceremony without sending transactions")
706
869
  .action(async (opts) => {
707
870
  const config = (0, config_1.loadConfig)();
871
+ if (opts.dryRun) {
872
+ const factoryAddr = config.walletFactoryAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress ?? "(not configured)";
873
+ const chainId = config.network === "base-mainnet" ? 8453 : 84532;
874
+ console.log();
875
+ console.log(" " + colors_1.c.dim("── Dry run: wallet deploy ──────────────────────────────────────"));
876
+ console.log(" " + colors_1.c.dim("Network: ") + colors_1.c.white(config.network));
877
+ console.log(" " + colors_1.c.dim("Chain ID: ") + colors_1.c.white(String(chainId)));
878
+ console.log(" " + colors_1.c.dim("RPC: ") + colors_1.c.white(config.rpcUrl));
879
+ console.log(" " + colors_1.c.dim("WalletFactory: ") + colors_1.c.white(factoryAddr));
880
+ console.log(" " + colors_1.c.dim("Signing method: ") + colors_1.c.white(opts.smartWallet ? "Base Smart Wallet" : opts.hardware ? "Hardware (WC URI)" : "WalletConnect"));
881
+ console.log(" " + colors_1.c.dim("Sponsored: ") + colors_1.c.white(opts.sponsored ? "yes" : "no"));
882
+ console.log();
883
+ console.log(" " + colors_1.c.dim("Steps that would run:"));
884
+ console.log(" 1. Connect " + (opts.smartWallet ? "Coinbase Smart Wallet" : "WalletConnect") + " session");
885
+ console.log(" 2. Call WalletFactory.createWallet() → deploy ARC402Wallet");
886
+ console.log(" 3. Save walletContractAddress to config");
887
+ console.log(" 4. Run onboarding ceremony (PolicyEngine, machine key, agent registration)");
888
+ console.log();
889
+ console.log(" " + colors_1.c.dim("No transactions sent (--dry-run mode)."));
890
+ console.log();
891
+ return;
892
+ }
708
893
  const factoryAddress = config.walletFactoryAddress ?? config_1.NETWORK_DEFAULTS[config.network]?.walletFactoryAddress;
709
894
  if (!factoryAddress) {
710
895
  console.error("walletFactoryAddress not found in config or NETWORK_DEFAULTS. Add walletFactoryAddress to your config.");
@@ -845,12 +1030,44 @@ function registerWalletCommands(program) {
845
1030
  const telegramOpts = config.telegramBotToken && config.telegramChatId
846
1031
  ? { botToken: config.telegramBotToken, chatId: config.telegramChatId, threadId: config.telegramThreadId }
847
1032
  : undefined;
1033
+ // ── Resume check ──────────────────────────────────────────────────────
1034
+ const resumeProgress = config.onboardingProgress;
1035
+ const isResuming = !!(resumeProgress?.walletAddress &&
1036
+ resumeProgress.walletAddress === config.walletContractAddress &&
1037
+ config.ownerAddress);
1038
+ if (isResuming) {
1039
+ const stepNames = {
1040
+ 2: "machine key", 3: "passkey", 4: "policy setup", 5: "agent registration",
1041
+ };
1042
+ const nextStep = (resumeProgress.step ?? 1) + 1;
1043
+ console.log(" " + colors_1.c.dim(`◈ Resuming onboarding from step ${nextStep} (${stepNames[nextStep] ?? "ceremony"})...`));
1044
+ }
1045
+ // ── Gas estimation ─────────────────────────────────────────────────────
1046
+ if (!isResuming) {
1047
+ let gasMsg = "~0.003 ETH (6 transactions on Base)";
1048
+ try {
1049
+ const feeData = await provider.getFeeData();
1050
+ const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice ?? BigInt(1500000000);
1051
+ const deployGas = await provider.estimateGas({
1052
+ to: factoryAddress,
1053
+ data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
1054
+ }).catch(() => BigInt(280000));
1055
+ const ceremonyGas = BigInt(700000); // ~5 ceremony txs × ~140k each
1056
+ const totalGasEth = parseFloat(ethers_1.ethers.formatEther((deployGas + ceremonyGas) * gasPrice));
1057
+ gasMsg = `~${totalGasEth.toFixed(4)} ETH (6 transactions on Base)`;
1058
+ }
1059
+ catch { /* use default */ }
1060
+ console.log(" " + colors_1.c.dim(`◈ Estimated gas: ${gasMsg}`));
1061
+ }
848
1062
  // ── Step 1: Connect ────────────────────────────────────────────────────
849
- const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: "Approve ARC402Wallet deployment — you will be set as owner", hardware: !!opts.hardware });
1063
+ const connectPrompt = isResuming
1064
+ ? "Connect wallet to resume onboarding"
1065
+ : "Approve ARC402Wallet deployment — you will be set as owner";
1066
+ const { client, session, account } = await (0, walletconnect_1.connectPhoneWallet)(config.walletConnectProjectId, chainId, config, { telegramOpts, prompt: connectPrompt, hardware: !!opts.hardware });
850
1067
  const networkName = chainId === 8453 ? "Base" : "Base Sepolia";
851
1068
  const shortAddr = `${account.slice(0, 6)}...${account.slice(-5)}`;
852
1069
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Connected: ${shortAddr} on ${networkName}`));
853
- if (telegramOpts) {
1070
+ if (telegramOpts && !isResuming) {
854
1071
  // Send "connected" message with a deploy confirmation button.
855
1072
  // TODO: wire up full callback_data round-trip when a persistent bot process is available.
856
1073
  await (0, telegram_notify_1.sendTelegramMessage)({
@@ -861,50 +1078,59 @@ function registerWalletCommands(program) {
861
1078
  buttons: [[{ text: "🚀 Deploy ARC-402 Wallet", callback_data: "arc402_deploy_confirm" }]],
862
1079
  });
863
1080
  }
864
- // ── Step 2: Confirm & Deploy ───────────────────────────────────────────
865
- // WalletConnect approval already confirmed intent — sending automatically
866
- console.log("Deploying...");
867
- const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
868
- to: factoryAddress,
869
- data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
870
- value: "0x0",
871
- });
872
- console.log(`\nTransaction submitted: ${txHash}`);
873
- console.log("Waiting for confirmation...");
874
- const receipt = await provider.waitForTransaction(txHash);
875
- if (!receipt) {
876
- console.error("Transaction not confirmed. Check on-chain.");
877
- process.exit(1);
1081
+ let walletAddress;
1082
+ if (isResuming) {
1083
+ // Resume: skip deploy, use existing wallet
1084
+ walletAddress = config.walletContractAddress;
1085
+ console.log(" " + colors_1.c.dim(`◈ Using existing wallet: ${walletAddress}`));
878
1086
  }
879
- let walletAddress = null;
880
- const factoryContract = new ethers_1.ethers.Contract(factoryAddress, abis_1.WALLET_FACTORY_ABI, provider);
881
- for (const log of receipt.logs) {
882
- try {
883
- const parsed = factoryContract.interface.parseLog(log);
884
- if (parsed?.name === "WalletCreated") {
885
- walletAddress = parsed.args.walletAddress;
886
- break;
1087
+ else {
1088
+ // ── Step 2: Confirm & Deploy ─────────────────────────────────────────
1089
+ // WalletConnect approval already confirmed intent — sending automatically
1090
+ console.log("Deploying...");
1091
+ const txHash = await (0, walletconnect_1.sendTransactionWithSession)(client, session, account, chainId, {
1092
+ to: factoryAddress,
1093
+ data: factoryInterface.encodeFunctionData("createWallet", ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]),
1094
+ value: "0x0",
1095
+ });
1096
+ console.log(`\nTransaction submitted: ${txHash}`);
1097
+ console.log("Waiting for confirmation...");
1098
+ const receipt = await provider.waitForTransaction(txHash);
1099
+ if (!receipt) {
1100
+ console.error("Transaction not confirmed. Check on-chain.");
1101
+ process.exit(1);
1102
+ }
1103
+ let deployedWallet = null;
1104
+ const factoryContract = new ethers_1.ethers.Contract(factoryAddress, abis_1.WALLET_FACTORY_ABI, provider);
1105
+ for (const log of receipt.logs) {
1106
+ try {
1107
+ const parsed = factoryContract.interface.parseLog(log);
1108
+ if (parsed?.name === "WalletCreated") {
1109
+ deployedWallet = parsed.args.walletAddress;
1110
+ break;
1111
+ }
887
1112
  }
1113
+ catch { /* skip unparseable logs */ }
888
1114
  }
889
- catch { /* skip unparseable logs */ }
890
- }
891
- if (!walletAddress) {
892
- console.error("Could not find WalletCreated event in receipt. Check the transaction on-chain.");
893
- process.exit(1);
894
- }
895
- // ── Step 1 complete: save wallet + owner immediately ─────────────────
896
- config.walletContractAddress = walletAddress;
897
- config.ownerAddress = account;
898
- (0, config_1.saveConfig)(config);
899
- try {
900
- fs_1.default.chmodSync((0, config_1.getConfigPath)(), 0o600);
1115
+ if (!deployedWallet) {
1116
+ console.error("Could not find WalletCreated event in receipt. Check the transaction on-chain.");
1117
+ process.exit(1);
1118
+ }
1119
+ walletAddress = deployedWallet;
1120
+ // ── Step 1 complete: save wallet + owner immediately ─────────────────
1121
+ config.walletContractAddress = walletAddress;
1122
+ config.ownerAddress = account;
1123
+ (0, config_1.saveConfig)(config);
1124
+ try {
1125
+ fs_1.default.chmodSync((0, config_1.getConfigPath)(), 0o600);
1126
+ }
1127
+ catch { /* best-effort */ }
1128
+ console.log("\n " + colors_1.c.success + colors_1.c.white(" Wallet deployed"));
1129
+ (0, tree_1.renderTree)([
1130
+ { label: "Wallet", value: walletAddress },
1131
+ { label: "Owner", value: account, last: true },
1132
+ ]);
901
1133
  }
902
- catch { /* best-effort */ }
903
- console.log("\n " + colors_1.c.success + colors_1.c.white(" Wallet deployed"));
904
- (0, tree_1.renderTree)([
905
- { label: "Wallet", value: walletAddress },
906
- { label: "Owner", value: account, last: true },
907
- ]);
908
1134
  // ── Steps 2–6: Complete onboarding ceremony (same WalletConnect session)
909
1135
  const sendTxCeremony = async (call, description) => {
910
1136
  console.log(" " + colors_1.c.dim(`◈ ${description}`));
@@ -945,8 +1171,11 @@ function registerWalletCommands(program) {
945
1171
  const guardianWallet = ethers_1.ethers.Wallet.createRandom();
946
1172
  config.walletContractAddress = walletAddress;
947
1173
  config.ownerAddress = address;
948
- config.guardianPrivateKey = guardianWallet.privateKey;
949
1174
  config.guardianAddress = guardianWallet.address;
1175
+ // Save key to restricted file — never store in config.json
1176
+ saveGuardianKey(guardianWallet.privateKey);
1177
+ if (config.guardianPrivateKey)
1178
+ delete config.guardianPrivateKey;
950
1179
  (0, config_1.saveConfig)(config);
951
1180
  // Call setGuardian on the deployed wallet
952
1181
  const walletContract = new ethers_1.ethers.Contract(walletAddress, abis_1.ARC402_WALLET_GUARDIAN_ABI, signer);
@@ -967,7 +1196,7 @@ function registerWalletCommands(program) {
967
1196
  { label: "Wallet", value: walletAddress },
968
1197
  { label: "Guardian", value: guardianWallet.address, last: true },
969
1198
  ]);
970
- console.log(`Guardian private key saved to config (keep it safe used for emergency freeze only)`);
1199
+ console.log(`Guardian private key saved to ~/.arc402/guardian.key (chmod 400 — keep it safe, used for emergency freeze only)`);
971
1200
  console.log(`Your wallet contract is ready for policy enforcement`);
972
1201
  printOpenShellHint();
973
1202
  }
@@ -1164,12 +1393,13 @@ function registerWalletCommands(program) {
1164
1393
  console.error("walletContractAddress not set in config. Run `arc402 wallet deploy` first.");
1165
1394
  process.exit(1);
1166
1395
  }
1167
- if (!config.guardianPrivateKey) {
1168
- console.error("guardianPrivateKey not set in config. Guardian key was generated during `arc402 wallet deploy`.");
1396
+ const guardianKey = loadGuardianKey(config);
1397
+ if (!guardianKey) {
1398
+ console.error(`Guardian key not found. Expected at ~/.arc402/guardian.key (or guardianPrivateKey in config for legacy setups).`);
1169
1399
  process.exit(1);
1170
1400
  }
1171
1401
  const provider = new ethers_1.ethers.JsonRpcProvider(config.rpcUrl);
1172
- const guardianSigner = new ethers_1.ethers.Wallet(config.guardianPrivateKey, provider);
1402
+ const guardianSigner = new ethers_1.ethers.Wallet(guardianKey, provider);
1173
1403
  const walletContract = new ethers_1.ethers.Contract(config.walletContractAddress, abis_1.ARC402_WALLET_GUARDIAN_ABI, guardianSigner);
1174
1404
  let tx;
1175
1405
  if (opts.drain) {
@@ -1289,12 +1519,14 @@ function registerWalletCommands(program) {
1289
1519
  value: "0x0",
1290
1520
  });
1291
1521
  await provider.waitForTransaction(txHash);
1292
- config.guardianPrivateKey = guardianWallet.privateKey;
1522
+ saveGuardianKey(guardianWallet.privateKey);
1523
+ if (config.guardianPrivateKey)
1524
+ delete config.guardianPrivateKey;
1293
1525
  config.guardianAddress = guardianWallet.address;
1294
1526
  (0, config_1.saveConfig)(config);
1295
1527
  console.log("\n" + colors_1.c.success + colors_1.c.white(` Guardian set to: ${guardianWallet.address}`));
1296
1528
  console.log(" " + colors_1.c.dim("Tx:") + " " + colors_1.c.white(txHash));
1297
- console.log(" " + colors_1.c.dim("Guardian private key saved to config."));
1529
+ console.log(" " + colors_1.c.dim("Guardian private key saved to ~/.arc402/guardian.key (chmod 400)."));
1298
1530
  console.log(" " + colors_1.c.warning + " " + colors_1.c.yellow("The guardian key can freeze your wallet. Store it separately from your hot key."));
1299
1531
  });
1300
1532
  // ─── policy-engine freeze / unfreeze (legacy — for PolicyEngine-level freeze) ──
@@ -2005,9 +2237,11 @@ function registerWalletCommands(program) {
2005
2237
  txHashes.push(txHash);
2006
2238
  }
2007
2239
  }
2008
- // Persist guardian key if generated
2240
+ // Persist guardian key if generated — save to restricted file, not config.json
2009
2241
  if (guardianWallet) {
2010
- config.guardianPrivateKey = guardianWallet.privateKey;
2242
+ saveGuardianKey(guardianWallet.privateKey);
2243
+ if (config.guardianPrivateKey)
2244
+ delete config.guardianPrivateKey;
2011
2245
  config.guardianAddress = guardianWallet.address;
2012
2246
  (0, config_1.saveConfig)(config);
2013
2247
  }
@@ -2019,7 +2253,7 @@ function registerWalletCommands(program) {
2019
2253
  txHashes.forEach((h, i) => console.log(" " + colors_1.c.dim(`Tx ${i + 1}:`) + " " + colors_1.c.white(h)));
2020
2254
  }
2021
2255
  if (guardianWallet) {
2022
- console.log(" " + colors_1.c.success + colors_1.c.dim(` Guardian key saved to config — address: ${guardianWallet.address}`));
2256
+ console.log(" " + colors_1.c.success + colors_1.c.dim(` Guardian key saved to ~/.arc402/guardian.key — address: ${guardianWallet.address}`));
2023
2257
  console.log(" " + colors_1.c.warning + " " + colors_1.c.yellow("Store the guardian private key separately from your hot key."));
2024
2258
  }
2025
2259
  console.log(colors_1.c.dim("\nVerify with: arc402 wallet status && arc402 wallet policy show"));