arc402-cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +245 -0
  2. package/dist/abis.d.ts +19 -0
  3. package/dist/abis.d.ts.map +1 -0
  4. package/dist/abis.js +177 -0
  5. package/dist/abis.js.map +1 -0
  6. package/dist/bundler.d.ts +65 -0
  7. package/dist/bundler.d.ts.map +1 -0
  8. package/dist/bundler.js +181 -0
  9. package/dist/bundler.js.map +1 -0
  10. package/dist/client.d.ts +14 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +24 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/coinbase-smart-wallet.d.ts +28 -0
  15. package/dist/coinbase-smart-wallet.d.ts.map +1 -0
  16. package/dist/coinbase-smart-wallet.js +38 -0
  17. package/dist/coinbase-smart-wallet.js.map +1 -0
  18. package/dist/commands/accept.d.ts +3 -0
  19. package/dist/commands/accept.d.ts.map +1 -0
  20. package/dist/commands/accept.js +26 -0
  21. package/dist/commands/accept.js.map +1 -0
  22. package/dist/commands/agent-handshake.d.ts +3 -0
  23. package/dist/commands/agent-handshake.d.ts.map +1 -0
  24. package/dist/commands/agent-handshake.js +61 -0
  25. package/dist/commands/agent-handshake.js.map +1 -0
  26. package/dist/commands/agent.d.ts +3 -0
  27. package/dist/commands/agent.d.ts.map +1 -0
  28. package/dist/commands/agent.js +417 -0
  29. package/dist/commands/agent.js.map +1 -0
  30. package/dist/commands/agreements.d.ts +3 -0
  31. package/dist/commands/agreements.d.ts.map +1 -0
  32. package/dist/commands/agreements.js +344 -0
  33. package/dist/commands/agreements.js.map +1 -0
  34. package/dist/commands/arbitrator.d.ts +3 -0
  35. package/dist/commands/arbitrator.d.ts.map +1 -0
  36. package/dist/commands/arbitrator.js +157 -0
  37. package/dist/commands/arbitrator.js.map +1 -0
  38. package/dist/commands/arena-handshake.d.ts +3 -0
  39. package/dist/commands/arena-handshake.d.ts.map +1 -0
  40. package/dist/commands/arena-handshake.js +187 -0
  41. package/dist/commands/arena-handshake.js.map +1 -0
  42. package/dist/commands/cancel.d.ts +3 -0
  43. package/dist/commands/cancel.d.ts.map +1 -0
  44. package/dist/commands/cancel.js +30 -0
  45. package/dist/commands/cancel.js.map +1 -0
  46. package/dist/commands/channel.d.ts +3 -0
  47. package/dist/commands/channel.d.ts.map +1 -0
  48. package/dist/commands/channel.js +238 -0
  49. package/dist/commands/channel.js.map +1 -0
  50. package/dist/commands/coldstart.d.ts +3 -0
  51. package/dist/commands/coldstart.d.ts.map +1 -0
  52. package/dist/commands/coldstart.js +148 -0
  53. package/dist/commands/coldstart.js.map +1 -0
  54. package/dist/commands/config.d.ts +3 -0
  55. package/dist/commands/config.d.ts.map +1 -0
  56. package/dist/commands/config.js +40 -0
  57. package/dist/commands/config.js.map +1 -0
  58. package/dist/commands/contract-interaction.d.ts +3 -0
  59. package/dist/commands/contract-interaction.d.ts.map +1 -0
  60. package/dist/commands/contract-interaction.js +165 -0
  61. package/dist/commands/contract-interaction.js.map +1 -0
  62. package/dist/commands/daemon.d.ts +3 -0
  63. package/dist/commands/daemon.d.ts.map +1 -0
  64. package/dist/commands/daemon.js +891 -0
  65. package/dist/commands/daemon.js.map +1 -0
  66. package/dist/commands/deliver.d.ts +3 -0
  67. package/dist/commands/deliver.d.ts.map +1 -0
  68. package/dist/commands/deliver.js +156 -0
  69. package/dist/commands/deliver.js.map +1 -0
  70. package/dist/commands/discover.d.ts +3 -0
  71. package/dist/commands/discover.d.ts.map +1 -0
  72. package/dist/commands/discover.js +224 -0
  73. package/dist/commands/discover.js.map +1 -0
  74. package/dist/commands/dispute.d.ts +3 -0
  75. package/dist/commands/dispute.d.ts.map +1 -0
  76. package/dist/commands/dispute.js +348 -0
  77. package/dist/commands/dispute.js.map +1 -0
  78. package/dist/commands/endpoint.d.ts +3 -0
  79. package/dist/commands/endpoint.d.ts.map +1 -0
  80. package/dist/commands/endpoint.js +604 -0
  81. package/dist/commands/endpoint.js.map +1 -0
  82. package/dist/commands/hire.d.ts +3 -0
  83. package/dist/commands/hire.d.ts.map +1 -0
  84. package/dist/commands/hire.js +189 -0
  85. package/dist/commands/hire.js.map +1 -0
  86. package/dist/commands/migrate.d.ts +3 -0
  87. package/dist/commands/migrate.d.ts.map +1 -0
  88. package/dist/commands/migrate.js +163 -0
  89. package/dist/commands/migrate.js.map +1 -0
  90. package/dist/commands/negotiate.d.ts +3 -0
  91. package/dist/commands/negotiate.d.ts.map +1 -0
  92. package/dist/commands/negotiate.js +247 -0
  93. package/dist/commands/negotiate.js.map +1 -0
  94. package/dist/commands/openshell.d.ts +3 -0
  95. package/dist/commands/openshell.d.ts.map +1 -0
  96. package/dist/commands/openshell.js +952 -0
  97. package/dist/commands/openshell.js.map +1 -0
  98. package/dist/commands/owner.d.ts +3 -0
  99. package/dist/commands/owner.d.ts.map +1 -0
  100. package/dist/commands/owner.js +32 -0
  101. package/dist/commands/owner.js.map +1 -0
  102. package/dist/commands/policy.d.ts +4 -0
  103. package/dist/commands/policy.d.ts.map +1 -0
  104. package/dist/commands/policy.js +248 -0
  105. package/dist/commands/policy.js.map +1 -0
  106. package/dist/commands/relay.d.ts +3 -0
  107. package/dist/commands/relay.d.ts.map +1 -0
  108. package/dist/commands/relay.js +279 -0
  109. package/dist/commands/relay.js.map +1 -0
  110. package/dist/commands/remediate.d.ts +3 -0
  111. package/dist/commands/remediate.d.ts.map +1 -0
  112. package/dist/commands/remediate.js +42 -0
  113. package/dist/commands/remediate.js.map +1 -0
  114. package/dist/commands/reputation.d.ts +4 -0
  115. package/dist/commands/reputation.d.ts.map +1 -0
  116. package/dist/commands/reputation.js +72 -0
  117. package/dist/commands/reputation.js.map +1 -0
  118. package/dist/commands/setup.d.ts +3 -0
  119. package/dist/commands/setup.d.ts.map +1 -0
  120. package/dist/commands/setup.js +332 -0
  121. package/dist/commands/setup.js.map +1 -0
  122. package/dist/commands/trust.d.ts +3 -0
  123. package/dist/commands/trust.d.ts.map +1 -0
  124. package/dist/commands/trust.js +23 -0
  125. package/dist/commands/trust.js.map +1 -0
  126. package/dist/commands/verify.d.ts +3 -0
  127. package/dist/commands/verify.d.ts.map +1 -0
  128. package/dist/commands/verify.js +88 -0
  129. package/dist/commands/verify.js.map +1 -0
  130. package/dist/commands/wallet.d.ts +3 -0
  131. package/dist/commands/wallet.d.ts.map +1 -0
  132. package/dist/commands/wallet.js +2520 -0
  133. package/dist/commands/wallet.js.map +1 -0
  134. package/dist/commands/watchtower.d.ts +3 -0
  135. package/dist/commands/watchtower.d.ts.map +1 -0
  136. package/dist/commands/watchtower.js +238 -0
  137. package/dist/commands/watchtower.js.map +1 -0
  138. package/dist/commands/workroom.d.ts +3 -0
  139. package/dist/commands/workroom.d.ts.map +1 -0
  140. package/dist/commands/workroom.js +855 -0
  141. package/dist/commands/workroom.js.map +1 -0
  142. package/dist/config.d.ts +62 -0
  143. package/dist/config.d.ts.map +1 -0
  144. package/dist/config.js +141 -0
  145. package/dist/config.js.map +1 -0
  146. package/dist/daemon/config.d.ts +74 -0
  147. package/dist/daemon/config.d.ts.map +1 -0
  148. package/dist/daemon/config.js +271 -0
  149. package/dist/daemon/config.js.map +1 -0
  150. package/dist/daemon/hire-listener.d.ts +31 -0
  151. package/dist/daemon/hire-listener.d.ts.map +1 -0
  152. package/dist/daemon/hire-listener.js +207 -0
  153. package/dist/daemon/hire-listener.js.map +1 -0
  154. package/dist/daemon/index.d.ts +29 -0
  155. package/dist/daemon/index.d.ts.map +1 -0
  156. package/dist/daemon/index.js +535 -0
  157. package/dist/daemon/index.js.map +1 -0
  158. package/dist/daemon/job-lifecycle.d.ts +62 -0
  159. package/dist/daemon/job-lifecycle.d.ts.map +1 -0
  160. package/dist/daemon/job-lifecycle.js +201 -0
  161. package/dist/daemon/job-lifecycle.js.map +1 -0
  162. package/dist/daemon/notify.d.ts +22 -0
  163. package/dist/daemon/notify.d.ts.map +1 -0
  164. package/dist/daemon/notify.js +148 -0
  165. package/dist/daemon/notify.js.map +1 -0
  166. package/dist/daemon/token-metering.d.ts +42 -0
  167. package/dist/daemon/token-metering.d.ts.map +1 -0
  168. package/dist/daemon/token-metering.js +178 -0
  169. package/dist/daemon/token-metering.js.map +1 -0
  170. package/dist/daemon/userops.d.ts +21 -0
  171. package/dist/daemon/userops.d.ts.map +1 -0
  172. package/dist/daemon/userops.js +88 -0
  173. package/dist/daemon/userops.js.map +1 -0
  174. package/dist/daemon/wallet-monitor.d.ts +16 -0
  175. package/dist/daemon/wallet-monitor.d.ts.map +1 -0
  176. package/dist/daemon/wallet-monitor.js +57 -0
  177. package/dist/daemon/wallet-monitor.js.map +1 -0
  178. package/dist/drain-v4.d.ts +2 -0
  179. package/dist/drain-v4.d.ts.map +1 -0
  180. package/dist/drain-v4.js +167 -0
  181. package/dist/drain-v4.js.map +1 -0
  182. package/dist/endpoint-config.d.ts +36 -0
  183. package/dist/endpoint-config.d.ts.map +1 -0
  184. package/dist/endpoint-config.js +96 -0
  185. package/dist/endpoint-config.js.map +1 -0
  186. package/dist/index.d.ts +3 -0
  187. package/dist/index.d.ts.map +1 -0
  188. package/dist/index.js +79 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/openshell-runtime.d.ts +55 -0
  191. package/dist/openshell-runtime.d.ts.map +1 -0
  192. package/dist/openshell-runtime.js +268 -0
  193. package/dist/openshell-runtime.js.map +1 -0
  194. package/dist/signing.d.ts +2 -0
  195. package/dist/signing.d.ts.map +1 -0
  196. package/dist/signing.js +23 -0
  197. package/dist/signing.js.map +1 -0
  198. package/dist/telegram-notify.d.ts +23 -0
  199. package/dist/telegram-notify.d.ts.map +1 -0
  200. package/dist/telegram-notify.js +106 -0
  201. package/dist/telegram-notify.js.map +1 -0
  202. package/dist/ui/banner.d.ts +7 -0
  203. package/dist/ui/banner.d.ts.map +1 -0
  204. package/dist/ui/banner.js +37 -0
  205. package/dist/ui/banner.js.map +1 -0
  206. package/dist/ui/colors.d.ts +14 -0
  207. package/dist/ui/colors.d.ts.map +1 -0
  208. package/dist/ui/colors.js +29 -0
  209. package/dist/ui/colors.js.map +1 -0
  210. package/dist/ui/format.d.ts +26 -0
  211. package/dist/ui/format.d.ts.map +1 -0
  212. package/dist/ui/format.js +77 -0
  213. package/dist/ui/format.js.map +1 -0
  214. package/dist/ui/spinner.d.ts +8 -0
  215. package/dist/ui/spinner.d.ts.map +1 -0
  216. package/dist/ui/spinner.js +43 -0
  217. package/dist/ui/spinner.js.map +1 -0
  218. package/dist/utils/format.d.ts +10 -0
  219. package/dist/utils/format.d.ts.map +1 -0
  220. package/dist/utils/format.js +61 -0
  221. package/dist/utils/format.js.map +1 -0
  222. package/dist/utils/hash.d.ts +3 -0
  223. package/dist/utils/hash.d.ts.map +1 -0
  224. package/dist/utils/hash.js +43 -0
  225. package/dist/utils/hash.js.map +1 -0
  226. package/dist/utils/time.d.ts +3 -0
  227. package/dist/utils/time.d.ts.map +1 -0
  228. package/dist/utils/time.js +21 -0
  229. package/dist/utils/time.js.map +1 -0
  230. package/dist/wallet-router.d.ts +25 -0
  231. package/dist/wallet-router.d.ts.map +1 -0
  232. package/dist/wallet-router.js +153 -0
  233. package/dist/wallet-router.js.map +1 -0
  234. package/dist/walletconnect-session.d.ts +12 -0
  235. package/dist/walletconnect-session.d.ts.map +1 -0
  236. package/dist/walletconnect-session.js +26 -0
  237. package/dist/walletconnect-session.js.map +1 -0
  238. package/dist/walletconnect.d.ts +46 -0
  239. package/dist/walletconnect.d.ts.map +1 -0
  240. package/dist/walletconnect.js +267 -0
  241. package/dist/walletconnect.js.map +1 -0
  242. package/package.json +38 -0
  243. package/scripts/authorize-machine-key.ts +43 -0
  244. package/scripts/drain-wallet.ts +149 -0
  245. package/scripts/execute-spend-only.ts +81 -0
  246. package/scripts/register-agent-userop.ts +186 -0
  247. package/src/abis.ts +187 -0
  248. package/src/bundler.ts +235 -0
  249. package/src/client.ts +34 -0
  250. package/src/coinbase-smart-wallet.ts +51 -0
  251. package/src/commands/accept.ts +25 -0
  252. package/src/commands/agent-handshake.ts +67 -0
  253. package/src/commands/agent.ts +458 -0
  254. package/src/commands/agreements.ts +324 -0
  255. package/src/commands/arbitrator.ts +129 -0
  256. package/src/commands/arena-handshake.ts +217 -0
  257. package/src/commands/cancel.ts +26 -0
  258. package/src/commands/channel.ts +208 -0
  259. package/src/commands/coldstart.ts +156 -0
  260. package/src/commands/config.ts +35 -0
  261. package/src/commands/contract-interaction.ts +166 -0
  262. package/src/commands/daemon.ts +971 -0
  263. package/src/commands/deliver.ts +116 -0
  264. package/src/commands/discover.ts +295 -0
  265. package/src/commands/dispute.ts +373 -0
  266. package/src/commands/endpoint.ts +619 -0
  267. package/src/commands/hire.ts +200 -0
  268. package/src/commands/migrate.ts +175 -0
  269. package/src/commands/negotiate.ts +270 -0
  270. package/src/commands/openshell.ts +1053 -0
  271. package/src/commands/owner.ts +30 -0
  272. package/src/commands/policy.ts +252 -0
  273. package/src/commands/relay.ts +272 -0
  274. package/src/commands/remediate.ts +22 -0
  275. package/src/commands/reputation.ts +71 -0
  276. package/src/commands/setup.ts +343 -0
  277. package/src/commands/trust.ts +15 -0
  278. package/src/commands/verify.ts +88 -0
  279. package/src/commands/wallet.ts +2892 -0
  280. package/src/commands/watchtower.ts +232 -0
  281. package/src/commands/workroom.ts +889 -0
  282. package/src/config.ts +153 -0
  283. package/src/daemon/config.ts +308 -0
  284. package/src/daemon/hire-listener.ts +226 -0
  285. package/src/daemon/index.ts +609 -0
  286. package/src/daemon/job-lifecycle.ts +215 -0
  287. package/src/daemon/notify.ts +157 -0
  288. package/src/daemon/token-metering.ts +183 -0
  289. package/src/daemon/userops.ts +119 -0
  290. package/src/daemon/wallet-monitor.ts +90 -0
  291. package/src/drain-v4.ts +159 -0
  292. package/src/endpoint-config.ts +83 -0
  293. package/src/index.ts +75 -0
  294. package/src/openshell-runtime.ts +277 -0
  295. package/src/signing.ts +28 -0
  296. package/src/telegram-notify.ts +88 -0
  297. package/src/ui/banner.ts +41 -0
  298. package/src/ui/colors.ts +30 -0
  299. package/src/ui/format.ts +77 -0
  300. package/src/ui/spinner.ts +46 -0
  301. package/src/utils/format.ts +48 -0
  302. package/src/utils/hash.ts +5 -0
  303. package/src/utils/time.ts +15 -0
  304. package/src/wallet-router.ts +178 -0
  305. package/src/walletconnect-session.ts +27 -0
  306. package/src/walletconnect.ts +294 -0
  307. package/test/time.test.js +11 -0
  308. package/tsconfig.json +19 -0
@@ -0,0 +1,855 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerWorkroomCommands = registerWorkroomCommands;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const openshell_runtime_1 = require("../openshell-runtime");
41
+ const config_1 = require("../daemon/config");
42
+ // ─── Constants ────────────────────────────────────────────────────────────────
43
+ const WORKROOM_IMAGE = "arc402-workroom";
44
+ const WORKROOM_CONTAINER = "arc402-workroom";
45
+ const POLICY_FILE = path.join(openshell_runtime_1.ARC402_DIR, "openshell-policy.yaml");
46
+ const ARENA_POLICY_FILE = path.join(openshell_runtime_1.ARC402_DIR, "arena-policy.yaml");
47
+ const ARENA_DATA_DIR = path.join(openshell_runtime_1.ARC402_DIR, "arena");
48
+ const WORKROOM_DIR = path.join(__dirname, "..", "..", "..", "workroom"); // relative to cli/dist
49
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
50
+ function dockerAvailable() {
51
+ const r = (0, openshell_runtime_1.runCmd)("docker", ["info", "--format", "{{.ServerVersion}}"]);
52
+ return r.ok;
53
+ }
54
+ function containerExists() {
55
+ const r = (0, openshell_runtime_1.runCmd)("docker", ["inspect", WORKROOM_CONTAINER, "--format", "{{.State.Status}}"]);
56
+ return r.ok;
57
+ }
58
+ function containerRunning() {
59
+ const r = (0, openshell_runtime_1.runCmd)("docker", ["inspect", WORKROOM_CONTAINER, "--format", "{{.State.Running}}"]);
60
+ return r.ok && r.stdout.trim() === "true";
61
+ }
62
+ function imageExists() {
63
+ const r = (0, openshell_runtime_1.runCmd)("docker", ["image", "inspect", WORKROOM_IMAGE, "--format", "{{.Id}}"]);
64
+ return r.ok;
65
+ }
66
+ function buildImage() {
67
+ // Find the workroom directory (contains Dockerfile)
68
+ const workroomSrc = path.resolve(__dirname, "..", "..", "..", "workroom");
69
+ if (!fs.existsSync(path.join(workroomSrc, "Dockerfile"))) {
70
+ console.error(`Dockerfile not found at ${workroomSrc}/Dockerfile`);
71
+ return false;
72
+ }
73
+ console.log("Building ARC-402 Workroom image...");
74
+ const result = (0, child_process_1.spawnSync)("docker", ["build", "-t", WORKROOM_IMAGE, workroomSrc], {
75
+ stdio: "inherit",
76
+ });
77
+ return result.status === 0;
78
+ }
79
+ function getPolicyHash() {
80
+ if (!fs.existsSync(POLICY_FILE))
81
+ return "(no policy file)";
82
+ const content = fs.readFileSync(POLICY_FILE, "utf-8");
83
+ const crypto = require("crypto");
84
+ return "0x" + crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
85
+ }
86
+ // ─── Commands ─────────────────────────────────────────────────────────────────
87
+ function registerWorkroomCommands(program) {
88
+ const workroom = program
89
+ .command("workroom")
90
+ .description("ARC-402 Workroom — governed execution environment for hired work. Your OpenClaw stays on the host; work runs inside the workroom.");
91
+ // ── init ──────────────────────────────────────────────────────────────────
92
+ workroom
93
+ .command("init")
94
+ .description("Create the ARC-402 Workroom: build Docker image, validate policy, prepare runtime bundle.")
95
+ .action(async () => {
96
+ console.log("ARC-402 Workroom Init");
97
+ console.log("─────────────────────");
98
+ // Check Docker
99
+ if (!dockerAvailable()) {
100
+ console.error("Docker is not available. Install Docker Desktop and try again.");
101
+ process.exit(1);
102
+ }
103
+ console.log("✓ Docker available");
104
+ // Check policy file
105
+ if (!fs.existsSync(POLICY_FILE)) {
106
+ console.log("No policy file found. Generating default...");
107
+ // Import and call the existing policy generator
108
+ const { registerOpenShellCommands } = require("./openshell");
109
+ console.log(`Policy file will be generated at: ${POLICY_FILE}`);
110
+ console.log("Run 'arc402 workroom policy preset core-launch' after init to apply defaults.");
111
+ }
112
+ else {
113
+ console.log(`✓ Policy file: ${POLICY_FILE}`);
114
+ }
115
+ // Check daemon.toml
116
+ if (!fs.existsSync(config_1.DAEMON_TOML)) {
117
+ console.error("daemon.toml not found. Run 'arc402 daemon init' first.");
118
+ process.exit(1);
119
+ }
120
+ console.log("✓ daemon.toml found");
121
+ // Set up Arena directories and default policy
122
+ if (!fs.existsSync(ARENA_DATA_DIR)) {
123
+ fs.mkdirSync(ARENA_DATA_DIR, { recursive: true });
124
+ for (const sub of ["feed", "profile", "state", "queue"]) {
125
+ fs.mkdirSync(path.join(ARENA_DATA_DIR, sub), { recursive: true });
126
+ }
127
+ console.log("✓ Arena directories created");
128
+ }
129
+ else {
130
+ console.log("✓ Arena directories exist");
131
+ }
132
+ // Copy default arena policy if not present
133
+ if (!fs.existsSync(ARENA_POLICY_FILE)) {
134
+ const defaultArenaPolicy = path.join(WORKROOM_DIR, "arena-policy.yaml");
135
+ if (fs.existsSync(defaultArenaPolicy)) {
136
+ fs.copyFileSync(defaultArenaPolicy, ARENA_POLICY_FILE);
137
+ console.log("✓ Arena policy: default installed");
138
+ }
139
+ else {
140
+ console.log("⚠ Arena policy template not found — create manually at " + ARENA_POLICY_FILE);
141
+ }
142
+ }
143
+ else {
144
+ console.log("✓ Arena policy exists");
145
+ }
146
+ // Build image
147
+ if (!imageExists()) {
148
+ if (!buildImage()) {
149
+ console.error("Failed to build workroom image.");
150
+ process.exit(1);
151
+ }
152
+ }
153
+ console.log(`✓ Image: ${WORKROOM_IMAGE}`);
154
+ // Package CLI runtime for the workroom
155
+ const cliDist = path.resolve(__dirname, "..", "..");
156
+ const cliPackage = path.resolve(__dirname, "..", "..", "..", "package.json");
157
+ if (fs.existsSync(cliDist) && fs.existsSync(cliPackage)) {
158
+ console.log("✓ CLI runtime available for workroom mount");
159
+ }
160
+ else {
161
+ console.warn("⚠ CLI dist not found — workroom will need runtime bundle");
162
+ }
163
+ console.log("\nWorkroom initialized. Start with: arc402 workroom start");
164
+ console.log(`Policy hash: ${getPolicyHash()}`);
165
+ });
166
+ // ── start ─────────────────────────────────────────────────────────────────
167
+ workroom
168
+ .command("start")
169
+ .description("Start the ARC-402 Workroom (always-on governed container with daemon inside).")
170
+ .action(async () => {
171
+ if (!dockerAvailable()) {
172
+ console.error("Docker is not available.");
173
+ process.exit(1);
174
+ }
175
+ if (containerRunning()) {
176
+ console.log("Workroom is already running.");
177
+ process.exit(0);
178
+ }
179
+ // Remove stopped container if exists
180
+ if (containerExists()) {
181
+ (0, openshell_runtime_1.runCmd)("docker", ["rm", "-f", WORKROOM_CONTAINER]);
182
+ }
183
+ // Build image if needed
184
+ if (!imageExists()) {
185
+ if (!buildImage()) {
186
+ console.error("Failed to build workroom image.");
187
+ process.exit(1);
188
+ }
189
+ }
190
+ // Resolve secrets from local config
191
+ const machineKey = process.env.ARC402_MACHINE_KEY || "";
192
+ const telegramBot = process.env.TELEGRAM_BOT_TOKEN || "";
193
+ const telegramChat = process.env.TELEGRAM_CHAT_ID || "";
194
+ if (!machineKey) {
195
+ console.error("ARC402_MACHINE_KEY not set in environment.");
196
+ console.error("Export it before starting: export ARC402_MACHINE_KEY=0x...");
197
+ process.exit(1);
198
+ }
199
+ // CLI runtime path
200
+ const cliRoot = path.resolve(__dirname, "..", "..", "..");
201
+ console.log("Starting ARC-402 Workroom...");
202
+ const args = [
203
+ "run", "-d",
204
+ "--name", WORKROOM_CONTAINER,
205
+ "--restart", "unless-stopped",
206
+ "--cap-add", "NET_ADMIN", // Required for iptables
207
+ // Mount config (read-write for daemon state/logs)
208
+ "-v", `${openshell_runtime_1.ARC402_DIR}:/workroom/.arc402:rw`,
209
+ // Mount CLI runtime (read-only)
210
+ "-v", `${cliRoot}:/workroom/runtime:ro`,
211
+ // Mount jobs directory
212
+ "-v", `${path.join(openshell_runtime_1.ARC402_DIR, "jobs")}:/workroom/jobs:rw`,
213
+ // Mount worker directory (identity, memory, skills, knowledge)
214
+ "-v", `${path.join(openshell_runtime_1.ARC402_DIR, "worker")}:/workroom/worker:rw`,
215
+ // Mount Arena data directory (feed index, profile cache, state, queue)
216
+ "-v", `${ARENA_DATA_DIR}:/workroom/arena:rw`,
217
+ // Inject secrets as env vars
218
+ "-e", `ARC402_MACHINE_KEY=${machineKey}`,
219
+ "-e", `TELEGRAM_BOT_TOKEN=${telegramBot}`,
220
+ "-e", `TELEGRAM_CHAT_ID=${telegramChat}`,
221
+ "-e", `ARC402_DAEMON_PROCESS=1`,
222
+ "-e", `ARC402_DAEMON_FOREGROUND=1`,
223
+ // Expose relay port
224
+ "-p", "4402:4402",
225
+ WORKROOM_IMAGE,
226
+ ];
227
+ const result = (0, child_process_1.spawnSync)("docker", args, { stdio: "inherit" });
228
+ if (result.status !== 0) {
229
+ console.error("Failed to start workroom container.");
230
+ process.exit(1);
231
+ }
232
+ // Wait briefly and check health
233
+ (0, child_process_1.spawnSync)("sleep", ["2"]);
234
+ if (containerRunning()) {
235
+ console.log("\n✓ ARC-402 Workroom is running");
236
+ console.log(` Container: ${WORKROOM_CONTAINER}`);
237
+ console.log(` Policy hash: ${getPolicyHash()}`);
238
+ console.log(` Relay port: 4402`);
239
+ console.log(` Logs: arc402 workroom logs`);
240
+ }
241
+ else {
242
+ console.error("Workroom started but exited immediately. Check logs:");
243
+ console.error(" docker logs arc402-workroom");
244
+ process.exit(1);
245
+ }
246
+ });
247
+ // ── stop ──────────────────────────────────────────────────────────────────
248
+ workroom
249
+ .command("stop")
250
+ .description("Stop the ARC-402 Workroom.")
251
+ .action(async () => {
252
+ if (!containerRunning()) {
253
+ console.log("Workroom is not running.");
254
+ return;
255
+ }
256
+ console.log("Stopping ARC-402 Workroom...");
257
+ (0, openshell_runtime_1.runCmd)("docker", ["stop", WORKROOM_CONTAINER]);
258
+ console.log("✓ Workroom stopped");
259
+ });
260
+ // ── status ────────────────────────────────────────────────────────────────
261
+ workroom
262
+ .command("status")
263
+ .description("Show ARC-402 Workroom health, policy, and active state.")
264
+ .action(async () => {
265
+ console.log("ARC-402 Workroom Status");
266
+ console.log("───────────────────────");
267
+ // Docker
268
+ if (!dockerAvailable()) {
269
+ console.log("Docker: ❌ not available");
270
+ return;
271
+ }
272
+ console.log("Docker: ✓ available");
273
+ // Image
274
+ console.log(`Image: ${imageExists() ? "✓ " + WORKROOM_IMAGE : "❌ not built (run: arc402 workroom init)"}`);
275
+ // Container
276
+ if (containerRunning()) {
277
+ console.log(`Container: ✓ running (${WORKROOM_CONTAINER})`);
278
+ // Get container uptime
279
+ const inspect = (0, openshell_runtime_1.runCmd)("docker", ["inspect", WORKROOM_CONTAINER, "--format", "{{.State.StartedAt}}"]);
280
+ if (inspect.ok) {
281
+ const started = new Date(inspect.stdout.trim());
282
+ const uptime = Math.floor((Date.now() - started.getTime()) / 1000);
283
+ const h = Math.floor(uptime / 3600);
284
+ const m = Math.floor((uptime % 3600) / 60);
285
+ console.log(`Uptime: ${h}h ${m}m`);
286
+ }
287
+ // Get iptables rule count from inside container
288
+ const rules = (0, openshell_runtime_1.runCmd)("docker", ["exec", WORKROOM_CONTAINER, "iptables", "-L", "OUTPUT", "-n", "--line-numbers"]);
289
+ if (rules.ok) {
290
+ const ruleCount = rules.stdout.split("\n").filter(l => l.match(/^\d+/)).length;
291
+ console.log(`Network rules: ${ruleCount} iptables rules enforced`);
292
+ }
293
+ }
294
+ else if (containerExists()) {
295
+ console.log(`Container: ⚠ stopped (run: arc402 workroom start)`);
296
+ }
297
+ else {
298
+ console.log(`Container: ❌ not created (run: arc402 workroom init)`);
299
+ }
300
+ // Policy
301
+ console.log(`Policy file: ${fs.existsSync(POLICY_FILE) ? "✓ " + POLICY_FILE : "❌ missing"}`);
302
+ console.log(`Policy hash: ${getPolicyHash()}`);
303
+ // Arena
304
+ const arenaExists = fs.existsSync(ARENA_DATA_DIR);
305
+ const arenaPolicy = fs.existsSync(ARENA_POLICY_FILE);
306
+ console.log(`Arena data: ${arenaExists ? "✓ " + ARENA_DATA_DIR : "❌ missing (run: arc402 workroom init)"}`);
307
+ console.log(`Arena policy: ${arenaPolicy ? "✓ loaded" : "❌ missing"}`);
308
+ // Arena queue (pending approvals)
309
+ if (arenaExists) {
310
+ const queueDir = path.join(ARENA_DATA_DIR, "queue");
311
+ if (fs.existsSync(queueDir)) {
312
+ const pending = fs.readdirSync(queueDir).filter(f => f.endsWith(".json")).length;
313
+ if (pending > 0) {
314
+ console.log(`Arena queue: ⚠ ${pending} action(s) awaiting approval`);
315
+ }
316
+ else {
317
+ console.log(`Arena queue: ✓ empty`);
318
+ }
319
+ }
320
+ }
321
+ });
322
+ // ── logs ──────────────────────────────────────────────────────────────────
323
+ workroom
324
+ .command("logs")
325
+ .description("Tail workroom daemon logs.")
326
+ .option("--follow", "Stream live log output")
327
+ .option("-n, --lines <n>", "Number of lines", "50")
328
+ .action(async (opts) => {
329
+ const args = ["logs"];
330
+ if (opts.follow)
331
+ args.push("-f");
332
+ args.push("--tail", opts.lines);
333
+ args.push(WORKROOM_CONTAINER);
334
+ (0, child_process_1.spawnSync)("docker", args, { stdio: "inherit" });
335
+ });
336
+ // ── shell ─────────────────────────────────────────────────────────────────
337
+ workroom
338
+ .command("shell")
339
+ .description("Open a shell inside the workroom for debugging.")
340
+ .action(async () => {
341
+ if (!containerRunning()) {
342
+ console.error("Workroom is not running.");
343
+ process.exit(1);
344
+ }
345
+ (0, child_process_1.spawnSync)("docker", ["exec", "-it", WORKROOM_CONTAINER, "/bin/bash"], {
346
+ stdio: "inherit",
347
+ });
348
+ });
349
+ // ── doctor ────────────────────────────────────────────────────────────────
350
+ workroom
351
+ .command("doctor")
352
+ .description("Diagnose workroom health: Docker, image, container, network, policy, daemon.")
353
+ .action(async () => {
354
+ console.log("ARC-402 Workroom Doctor");
355
+ console.log("───────────────────────");
356
+ const checks = [];
357
+ // Docker
358
+ const docker = dockerAvailable();
359
+ checks.push({ label: "Docker", pass: docker, detail: docker ? "available" : "not available — install Docker Desktop" });
360
+ // Image
361
+ const img = imageExists();
362
+ checks.push({ label: "Image", pass: img, detail: img ? WORKROOM_IMAGE : "not built — run: arc402 workroom init" });
363
+ // Container
364
+ const running = containerRunning();
365
+ checks.push({ label: "Container", pass: running, detail: running ? "running" : "not running — run: arc402 workroom start" });
366
+ // Policy
367
+ const policyExists = fs.existsSync(POLICY_FILE);
368
+ checks.push({ label: "Policy file", pass: policyExists, detail: policyExists ? POLICY_FILE : "missing" });
369
+ // daemon.toml
370
+ const daemonCfg = fs.existsSync(config_1.DAEMON_TOML);
371
+ checks.push({ label: "daemon.toml", pass: daemonCfg, detail: daemonCfg ? "found" : "missing — run: arc402 daemon init" });
372
+ // Machine key env
373
+ const mk = !!process.env.ARC402_MACHINE_KEY;
374
+ checks.push({ label: "Machine key env", pass: mk, detail: mk ? "set" : "ARC402_MACHINE_KEY not in environment" });
375
+ // Network connectivity (if running)
376
+ if (running) {
377
+ const rpcTest = (0, openshell_runtime_1.runCmd)("docker", ["exec", WORKROOM_CONTAINER, "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "5", "https://mainnet.base.org"]);
378
+ const rpcOk = rpcTest.ok && rpcTest.stdout.trim() !== "000";
379
+ checks.push({ label: "Base RPC from workroom", pass: rpcOk, detail: rpcOk ? `HTTP ${rpcTest.stdout.trim()}` : "FAILED — network policy may be blocking RPC" });
380
+ }
381
+ // Print results
382
+ for (const c of checks) {
383
+ const icon = c.pass ? "✓" : "✗";
384
+ const color = c.pass ? "" : " ← FIX";
385
+ console.log(` ${icon} ${c.label}: ${c.detail}${color}`);
386
+ }
387
+ const failures = checks.filter(c => !c.pass);
388
+ if (failures.length === 0) {
389
+ console.log("\n✓ All checks passed. Workroom is healthy.");
390
+ }
391
+ else {
392
+ console.log(`\n✗ ${failures.length} issue(s) found.`);
393
+ }
394
+ });
395
+ // ── policy (delegate to existing openshell policy commands) ───────────────
396
+ workroom
397
+ .command("policy")
398
+ .description("Manage workroom network policy. Delegates to the existing policy UX.")
399
+ .action(() => {
400
+ console.log("Use the policy subcommands:");
401
+ console.log(" arc402 workroom policy list");
402
+ console.log(" arc402 workroom policy preset <name>");
403
+ console.log(" arc402 workroom policy peer add <host>");
404
+ console.log(" arc402 workroom policy test <host>");
405
+ console.log(" arc402 workroom policy hash");
406
+ console.log(" arc402 workroom policy reload");
407
+ console.log("\nFor now, these delegate to 'arc402 openshell policy' commands.");
408
+ console.log("Full native workroom policy management coming in next release.");
409
+ });
410
+ // ── policy hash ──────────────────────────────────────────────────────────
411
+ const policyCmd = workroom.command("policy-hash")
412
+ .description("Get the SHA-256 hash of the current workroom policy (for AgentRegistry).")
413
+ .action(async () => {
414
+ console.log(getPolicyHash());
415
+ });
416
+ // ── policy test ──────────────────────────────────────────────────────────
417
+ workroom
418
+ .command("policy-test <host>")
419
+ .description("Test if a specific host is reachable from inside the workroom.")
420
+ .action(async (host) => {
421
+ if (!containerRunning()) {
422
+ console.error("Workroom is not running. Start it first: arc402 workroom start");
423
+ process.exit(1);
424
+ }
425
+ console.log(`Testing connectivity to ${host} from inside workroom...`);
426
+ const result = (0, openshell_runtime_1.runCmd)("docker", [
427
+ "exec", WORKROOM_CONTAINER,
428
+ "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "5",
429
+ `https://${host}`,
430
+ ]);
431
+ if (result.ok && result.stdout.trim() !== "000") {
432
+ console.log(`✓ ${host} is reachable (HTTP ${result.stdout.trim()})`);
433
+ }
434
+ else {
435
+ console.log(`✗ ${host} is NOT reachable from the workroom`);
436
+ console.log(" This host may not be in the workroom policy.");
437
+ console.log(" Add it with: arc402 openshell policy add <name> <host>");
438
+ }
439
+ });
440
+ // ── worker ─────────────────────────────────────────────────────────────────
441
+ const worker = workroom.command("worker").description("Manage the workroom worker — the agent identity that executes hired tasks.");
442
+ worker
443
+ .command("init")
444
+ .description("Initialize the workroom worker identity and configuration.")
445
+ .option("--name <name>", "Worker display name", "Worker")
446
+ .option("--model <model>", "Preferred LLM model for task execution")
447
+ .action(async (opts) => {
448
+ const workerDir = path.join(openshell_runtime_1.ARC402_DIR, "worker");
449
+ const memoryDir = path.join(workerDir, "memory");
450
+ const skillsDir = path.join(workerDir, "skills");
451
+ fs.mkdirSync(memoryDir, { recursive: true });
452
+ fs.mkdirSync(skillsDir, { recursive: true });
453
+ // Generate default worker SOUL.md
454
+ const soulPath = path.join(workerDir, "SOUL.md");
455
+ if (!fs.existsSync(soulPath)) {
456
+ fs.writeFileSync(soulPath, `# Worker Identity — ${opts.name}
457
+
458
+ You are a professional worker operating under an ARC-402 governed workroom.
459
+
460
+ ## Your role
461
+ - Execute hired tasks within governance bounds
462
+ - Produce high-quality deliverables on deadline
463
+ - Follow the task specification precisely
464
+ - Report issues early if the task cannot be completed as specified
465
+
466
+ ## What you have access to
467
+ - The task specification from the hiring agreement
468
+ - Skills relevant to your registered capabilities
469
+ - Accumulated learnings from previous jobs (in memory/learnings.md)
470
+ - Network access only to policy-approved hosts
471
+
472
+ ## What you do NOT have access to
473
+ - The operator's personal conversations or memory
474
+ - The operator's other agents or their state
475
+ - Network hosts not in the workroom policy
476
+ - Files outside the workroom
477
+
478
+ ## How you learn
479
+ After completing each job, reflect on:
480
+ - What techniques worked well
481
+ - What patterns you noticed in the task
482
+ - What domain knowledge you acquired
483
+ - What you would do differently next time
484
+
485
+ Write these learnings concisely. They will be available on your next job.
486
+
487
+ ## Professional standards
488
+ - Deliver on time or communicate blockers before the deadline
489
+ - Never fabricate data or claim work was done when it wasn't
490
+ - If the task is unclear, produce the best interpretation and document assumptions
491
+ - Every deliverable must be verifiable against the task spec
492
+ `);
493
+ console.log(`✓ Worker SOUL.md created: ${soulPath}`);
494
+ }
495
+ else {
496
+ console.log(`✓ Worker SOUL.md already exists: ${soulPath}`);
497
+ }
498
+ // Generate default MEMORY.md
499
+ const memoryPath = path.join(workerDir, "MEMORY.md");
500
+ if (!fs.existsSync(memoryPath)) {
501
+ fs.writeFileSync(memoryPath, `# Worker Memory
502
+
503
+ *Last updated: ${new Date().toISOString().split("T")[0]}*
504
+
505
+ ## Job count: 0
506
+ ## Total earned: 0 ETH
507
+
508
+ ## Learnings
509
+
510
+ No jobs completed yet. Learnings will accumulate here as the worker completes hired tasks.
511
+ `);
512
+ console.log(`✓ Worker MEMORY.md created: ${memoryPath}`);
513
+ }
514
+ // Generate learnings.md
515
+ const learningsPath = path.join(memoryDir, "learnings.md");
516
+ if (!fs.existsSync(learningsPath)) {
517
+ fs.writeFileSync(learningsPath, `# Accumulated Learnings
518
+
519
+ *Distilled from completed jobs. Available to the worker on every new task.*
520
+
521
+ ---
522
+
523
+ No learnings yet. Complete your first hired task to start accumulating expertise.
524
+ `);
525
+ console.log(`✓ Learnings file created: ${learningsPath}`);
526
+ }
527
+ // Worker config
528
+ const configPath = path.join(workerDir, "config.json");
529
+ if (!fs.existsSync(configPath)) {
530
+ const config = {
531
+ name: opts.name,
532
+ model: opts.model || "default",
533
+ capabilities: [],
534
+ created: new Date().toISOString(),
535
+ job_count: 0,
536
+ total_earned_eth: "0",
537
+ };
538
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
539
+ console.log(`✓ Worker config created: ${configPath}`);
540
+ }
541
+ console.log(`\nWorker initialized at: ${workerDir}`);
542
+ console.log("Next: customize the worker SOUL.md and add skills.");
543
+ console.log(" arc402 workroom worker set-soul <file>");
544
+ console.log(" arc402 workroom worker set-skills <dir>");
545
+ });
546
+ worker
547
+ .command("status")
548
+ .description("Show worker identity, job count, learnings, and configuration.")
549
+ .action(async () => {
550
+ const workerDir = path.join(openshell_runtime_1.ARC402_DIR, "worker");
551
+ const configPath = path.join(workerDir, "config.json");
552
+ if (!fs.existsSync(configPath)) {
553
+ console.error("Worker not initialized. Run: arc402 workroom worker init");
554
+ process.exit(1);
555
+ }
556
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
557
+ const memoryDir = path.join(workerDir, "memory");
558
+ const jobFiles = fs.existsSync(memoryDir)
559
+ ? fs.readdirSync(memoryDir).filter(f => f.startsWith("job-")).length
560
+ : 0;
561
+ const learningsPath = path.join(memoryDir, "learnings.md");
562
+ const learningsSize = fs.existsSync(learningsPath)
563
+ ? fs.statSync(learningsPath).size
564
+ : 0;
565
+ const skillsDir = path.join(workerDir, "skills");
566
+ const skillCount = fs.existsSync(skillsDir)
567
+ ? fs.readdirSync(skillsDir).length
568
+ : 0;
569
+ console.log("ARC-402 Workroom Worker");
570
+ console.log("───────────────────────");
571
+ console.log(`Name: ${config.name}`);
572
+ console.log(`Model: ${config.model}`);
573
+ console.log(`Created: ${config.created}`);
574
+ console.log(`Jobs done: ${config.job_count}`);
575
+ console.log(`Job memories: ${jobFiles}`);
576
+ console.log(`Learnings: ${learningsSize > 200 ? Math.round(learningsSize / 1024) + " KB" : "empty"}`);
577
+ console.log(`Skills: ${skillCount}`);
578
+ console.log(`Total earned: ${config.total_earned_eth} ETH`);
579
+ });
580
+ worker
581
+ .command("set-soul <file>")
582
+ .description("Upload a custom worker SOUL.md.")
583
+ .action(async (file) => {
584
+ if (!fs.existsSync(file)) {
585
+ console.error(`File not found: ${file}`);
586
+ process.exit(1);
587
+ }
588
+ const dest = path.join(openshell_runtime_1.ARC402_DIR, "worker", "SOUL.md");
589
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
590
+ fs.copyFileSync(file, dest);
591
+ console.log(`✓ Worker SOUL.md updated from: ${file}`);
592
+ });
593
+ worker
594
+ .command("set-skills <dir>")
595
+ .description("Copy skills into the workroom worker.")
596
+ .action(async (dir) => {
597
+ if (!fs.existsSync(dir)) {
598
+ console.error(`Directory not found: ${dir}`);
599
+ process.exit(1);
600
+ }
601
+ const dest = path.join(openshell_runtime_1.ARC402_DIR, "worker", "skills");
602
+ fs.mkdirSync(dest, { recursive: true });
603
+ // Copy all files from source to dest
604
+ const files = fs.readdirSync(dir);
605
+ for (const f of files) {
606
+ const src = path.join(dir, f);
607
+ const dst = path.join(dest, f);
608
+ if (fs.statSync(src).isFile()) {
609
+ fs.copyFileSync(src, dst);
610
+ }
611
+ else if (fs.statSync(src).isDirectory()) {
612
+ // Recursive copy for skill directories
613
+ fs.cpSync(src, dst, { recursive: true });
614
+ }
615
+ }
616
+ console.log(`✓ ${files.length} items copied to worker skills`);
617
+ });
618
+ worker
619
+ .command("set-knowledge <dir>")
620
+ .description("Mount a knowledge directory into the workroom. Contains reference materials, training data, domain docs — anything the worker needs to deliver its services.")
621
+ .action(async (dir) => {
622
+ if (!fs.existsSync(dir)) {
623
+ console.error(`Directory not found: ${dir}`);
624
+ process.exit(1);
625
+ }
626
+ const dest = path.join(openshell_runtime_1.ARC402_DIR, "worker", "knowledge");
627
+ fs.mkdirSync(dest, { recursive: true });
628
+ const files = fs.readdirSync(dir);
629
+ let count = 0;
630
+ for (const f of files) {
631
+ const src = path.join(dir, f);
632
+ const dst = path.join(dest, f);
633
+ if (fs.statSync(src).isFile()) {
634
+ fs.copyFileSync(src, dst);
635
+ count++;
636
+ }
637
+ else if (fs.statSync(src).isDirectory()) {
638
+ fs.cpSync(src, dst, { recursive: true });
639
+ count++;
640
+ }
641
+ }
642
+ console.log(`✓ ${count} items copied to worker knowledge`);
643
+ console.log(` Path: ${dest}`);
644
+ console.log(` The worker can reference these files during hired tasks.`);
645
+ console.log(` To update: run this command again with the updated directory.`);
646
+ });
647
+ worker
648
+ .command("knowledge")
649
+ .description("List the worker's knowledge directory contents.")
650
+ .action(async () => {
651
+ const knowledgeDir = path.join(openshell_runtime_1.ARC402_DIR, "worker", "knowledge");
652
+ if (!fs.existsSync(knowledgeDir)) {
653
+ console.log("No knowledge directory. Add one with: arc402 workroom worker set-knowledge <dir>");
654
+ return;
655
+ }
656
+ const files = fs.readdirSync(knowledgeDir, { recursive: true, withFileTypes: false });
657
+ if (files.length === 0) {
658
+ console.log("Knowledge directory is empty.");
659
+ return;
660
+ }
661
+ console.log(`Worker knowledge (${files.length} items):\n`);
662
+ for (const f of fs.readdirSync(knowledgeDir)) {
663
+ const stat = fs.statSync(path.join(knowledgeDir, f));
664
+ const size = stat.isDirectory() ? "dir" : `${(stat.size / 1024).toFixed(1)} KB`;
665
+ console.log(` ${f.padEnd(40)} ${size}`);
666
+ }
667
+ });
668
+ worker
669
+ .command("memory")
670
+ .description("Show the worker's accumulated learnings.")
671
+ .action(async () => {
672
+ const learningsPath = path.join(openshell_runtime_1.ARC402_DIR, "worker", "memory", "learnings.md");
673
+ if (!fs.existsSync(learningsPath)) {
674
+ console.log("No learnings yet. Complete a hired task first.");
675
+ return;
676
+ }
677
+ console.log(fs.readFileSync(learningsPath, "utf-8"));
678
+ });
679
+ worker
680
+ .command("memory-reset")
681
+ .description("Clear the worker's accumulated memory (start fresh).")
682
+ .action(async () => {
683
+ const memoryDir = path.join(openshell_runtime_1.ARC402_DIR, "worker", "memory");
684
+ if (fs.existsSync(memoryDir)) {
685
+ const files = fs.readdirSync(memoryDir);
686
+ for (const f of files)
687
+ fs.unlinkSync(path.join(memoryDir, f));
688
+ fs.writeFileSync(path.join(memoryDir, "learnings.md"), `# Accumulated Learnings\n\n*Reset: ${new Date().toISOString()}*\n\nNo learnings yet.\n`);
689
+ }
690
+ // Reset job count in config
691
+ const configPath = path.join(openshell_runtime_1.ARC402_DIR, "worker", "config.json");
692
+ if (fs.existsSync(configPath)) {
693
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
694
+ config.job_count = 0;
695
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
696
+ }
697
+ console.log("✓ Worker memory cleared. Starting fresh.");
698
+ });
699
+ // ── token usage ──────────────────────────────────────────────────────────
700
+ workroom
701
+ .command("token-usage [agreementId]")
702
+ .description("Show token usage for a specific agreement or across all jobs.")
703
+ .action(async (agreementId) => {
704
+ const { readUsageReport, formatUsageReport } = require("../daemon/token-metering");
705
+ if (agreementId) {
706
+ const usage = readUsageReport(agreementId);
707
+ if (!usage) {
708
+ console.log(`No token usage data for agreement: ${agreementId}`);
709
+ return;
710
+ }
711
+ console.log(formatUsageReport(usage));
712
+ }
713
+ else {
714
+ // Aggregate across all receipts
715
+ const receiptsDir = path.join(openshell_runtime_1.ARC402_DIR, "receipts");
716
+ if (!fs.existsSync(receiptsDir)) {
717
+ console.log("No receipts yet.");
718
+ return;
719
+ }
720
+ const files = fs.readdirSync(receiptsDir).filter((f) => f.endsWith(".json"));
721
+ let totalInput = 0;
722
+ let totalOutput = 0;
723
+ let totalCost = 0;
724
+ let jobsWithUsage = 0;
725
+ for (const f of files) {
726
+ try {
727
+ const receipt = JSON.parse(fs.readFileSync(path.join(receiptsDir, f), "utf-8"));
728
+ if (receipt.token_usage) {
729
+ totalInput += receipt.token_usage.total_input || 0;
730
+ totalOutput += receipt.token_usage.total_output || 0;
731
+ totalCost += receipt.token_usage.estimated_cost_usd || 0;
732
+ jobsWithUsage++;
733
+ }
734
+ }
735
+ catch { /* skip */ }
736
+ }
737
+ if (jobsWithUsage === 0) {
738
+ console.log("No token usage data in any receipts yet.");
739
+ return;
740
+ }
741
+ console.log("Aggregate Token Usage");
742
+ console.log("─────────────────────");
743
+ console.log(`Jobs with data: ${jobsWithUsage}`);
744
+ console.log(`Total tokens: ${(totalInput + totalOutput).toLocaleString()} (${totalInput.toLocaleString()} in / ${totalOutput.toLocaleString()} out)`);
745
+ console.log(`Est. total cost: $${totalCost.toFixed(4)}`);
746
+ if (jobsWithUsage > 0) {
747
+ console.log(`Avg per job: $${(totalCost / jobsWithUsage).toFixed(4)}`);
748
+ }
749
+ }
750
+ });
751
+ // ── receipts + earnings ──────────────────────────────────────────────────
752
+ workroom
753
+ .command("receipts")
754
+ .description("List all execution receipts from completed jobs.")
755
+ .action(async () => {
756
+ const receiptsDir = path.join(openshell_runtime_1.ARC402_DIR, "receipts");
757
+ if (!fs.existsSync(receiptsDir)) {
758
+ console.log("No receipts yet.");
759
+ return;
760
+ }
761
+ const files = fs.readdirSync(receiptsDir).filter(f => f.endsWith(".json")).sort();
762
+ if (files.length === 0) {
763
+ console.log("No receipts yet.");
764
+ return;
765
+ }
766
+ console.log(`${files.length} execution receipt(s):\n`);
767
+ for (const f of files) {
768
+ try {
769
+ const receipt = JSON.parse(fs.readFileSync(path.join(receiptsDir, f), "utf-8"));
770
+ const id = receipt.agreement_id || f.replace(".json", "");
771
+ const time = receipt.completed_at || "unknown";
772
+ const hash = receipt.deliverable_hash ? receipt.deliverable_hash.slice(0, 10) + "..." : "—";
773
+ console.log(` ${id} ${time} deliverable: ${hash}`);
774
+ }
775
+ catch {
776
+ console.log(` ${f} (unreadable)`);
777
+ }
778
+ }
779
+ });
780
+ workroom
781
+ .command("receipt <agreementId>")
782
+ .description("Show full execution receipt for a specific job.")
783
+ .action(async (agreementId) => {
784
+ const receiptPath = path.join(openshell_runtime_1.ARC402_DIR, "receipts", `${agreementId}.json`);
785
+ if (!fs.existsSync(receiptPath)) {
786
+ console.error(`No receipt found for agreement: ${agreementId}`);
787
+ process.exit(1);
788
+ }
789
+ console.log(fs.readFileSync(receiptPath, "utf-8"));
790
+ });
791
+ workroom
792
+ .command("earnings")
793
+ .description("Show total earnings from completed jobs.")
794
+ .option("--period <period>", "Time period (e.g. 7d, 30d, all)", "all")
795
+ .action(async (opts) => {
796
+ const configPath = path.join(openshell_runtime_1.ARC402_DIR, "worker", "config.json");
797
+ if (!fs.existsSync(configPath)) {
798
+ console.log("No worker configured. Run: arc402 workroom worker init");
799
+ return;
800
+ }
801
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
802
+ console.log("ARC-402 Earnings");
803
+ console.log("────────────────");
804
+ console.log(`Total earned: ${config.total_earned_eth} ETH`);
805
+ console.log(`Jobs completed: ${config.job_count}`);
806
+ if (config.job_count > 0) {
807
+ const avg = (parseFloat(config.total_earned_eth) / config.job_count).toFixed(6);
808
+ console.log(`Average/job: ${avg} ETH`);
809
+ }
810
+ });
811
+ workroom
812
+ .command("history")
813
+ .description("Show job history with outcomes and earnings.")
814
+ .action(async () => {
815
+ const memoryDir = path.join(openshell_runtime_1.ARC402_DIR, "worker", "memory");
816
+ if (!fs.existsSync(memoryDir)) {
817
+ console.log("No job history yet.");
818
+ return;
819
+ }
820
+ const jobFiles = fs.readdirSync(memoryDir).filter(f => f.startsWith("job-")).sort();
821
+ if (jobFiles.length === 0) {
822
+ console.log("No job history yet.");
823
+ return;
824
+ }
825
+ console.log(`${jobFiles.length} completed job(s):\n`);
826
+ for (const f of jobFiles) {
827
+ const content = fs.readFileSync(path.join(memoryDir, f), "utf-8");
828
+ const firstLine = content.split("\n").find(l => l.startsWith("#")) || f;
829
+ console.log(` ${f.replace(".md", "")} ${firstLine.replace(/^#+\s*/, "")}`);
830
+ }
831
+ });
832
+ // ── policy reload ────────────────────────────────────────────────────────
833
+ workroom
834
+ .command("policy-reload")
835
+ .description("Re-read the policy file and update iptables rules inside the running workroom.")
836
+ .action(async () => {
837
+ if (!containerRunning()) {
838
+ console.error("Workroom is not running.");
839
+ process.exit(1);
840
+ }
841
+ console.log("Reloading workroom policy...");
842
+ // Trigger DNS refresh manually (which re-reads policy and updates iptables)
843
+ const result = (0, openshell_runtime_1.runCmd)("docker", [
844
+ "exec", WORKROOM_CONTAINER,
845
+ "bash", "-c", "/dns-refresh.sh /workroom/.arc402/openshell-policy.yaml &",
846
+ ]);
847
+ if (result.ok) {
848
+ console.log("✓ Policy reload triggered");
849
+ }
850
+ else {
851
+ console.error("Failed to reload policy");
852
+ }
853
+ });
854
+ }
855
+ //# sourceMappingURL=workroom.js.map