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.
- package/README.md +245 -0
- package/dist/abis.d.ts +19 -0
- package/dist/abis.d.ts.map +1 -0
- package/dist/abis.js +177 -0
- package/dist/abis.js.map +1 -0
- package/dist/bundler.d.ts +65 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +181 -0
- package/dist/bundler.js.map +1 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +24 -0
- package/dist/client.js.map +1 -0
- package/dist/coinbase-smart-wallet.d.ts +28 -0
- package/dist/coinbase-smart-wallet.d.ts.map +1 -0
- package/dist/coinbase-smart-wallet.js +38 -0
- package/dist/coinbase-smart-wallet.js.map +1 -0
- package/dist/commands/accept.d.ts +3 -0
- package/dist/commands/accept.d.ts.map +1 -0
- package/dist/commands/accept.js +26 -0
- package/dist/commands/accept.js.map +1 -0
- package/dist/commands/agent-handshake.d.ts +3 -0
- package/dist/commands/agent-handshake.d.ts.map +1 -0
- package/dist/commands/agent-handshake.js +61 -0
- package/dist/commands/agent-handshake.js.map +1 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +417 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/agreements.d.ts +3 -0
- package/dist/commands/agreements.d.ts.map +1 -0
- package/dist/commands/agreements.js +344 -0
- package/dist/commands/agreements.js.map +1 -0
- package/dist/commands/arbitrator.d.ts +3 -0
- package/dist/commands/arbitrator.d.ts.map +1 -0
- package/dist/commands/arbitrator.js +157 -0
- package/dist/commands/arbitrator.js.map +1 -0
- package/dist/commands/arena-handshake.d.ts +3 -0
- package/dist/commands/arena-handshake.d.ts.map +1 -0
- package/dist/commands/arena-handshake.js +187 -0
- package/dist/commands/arena-handshake.js.map +1 -0
- package/dist/commands/cancel.d.ts +3 -0
- package/dist/commands/cancel.d.ts.map +1 -0
- package/dist/commands/cancel.js +30 -0
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/channel.d.ts +3 -0
- package/dist/commands/channel.d.ts.map +1 -0
- package/dist/commands/channel.js +238 -0
- package/dist/commands/channel.js.map +1 -0
- package/dist/commands/coldstart.d.ts +3 -0
- package/dist/commands/coldstart.d.ts.map +1 -0
- package/dist/commands/coldstart.js +148 -0
- package/dist/commands/coldstart.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +40 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/contract-interaction.d.ts +3 -0
- package/dist/commands/contract-interaction.d.ts.map +1 -0
- package/dist/commands/contract-interaction.js +165 -0
- package/dist/commands/contract-interaction.js.map +1 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +891 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/deliver.d.ts +3 -0
- package/dist/commands/deliver.d.ts.map +1 -0
- package/dist/commands/deliver.js +156 -0
- package/dist/commands/deliver.js.map +1 -0
- package/dist/commands/discover.d.ts +3 -0
- package/dist/commands/discover.d.ts.map +1 -0
- package/dist/commands/discover.js +224 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/dispute.d.ts +3 -0
- package/dist/commands/dispute.d.ts.map +1 -0
- package/dist/commands/dispute.js +348 -0
- package/dist/commands/dispute.js.map +1 -0
- package/dist/commands/endpoint.d.ts +3 -0
- package/dist/commands/endpoint.d.ts.map +1 -0
- package/dist/commands/endpoint.js +604 -0
- package/dist/commands/endpoint.js.map +1 -0
- package/dist/commands/hire.d.ts +3 -0
- package/dist/commands/hire.d.ts.map +1 -0
- package/dist/commands/hire.js +189 -0
- package/dist/commands/hire.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +163 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/negotiate.d.ts +3 -0
- package/dist/commands/negotiate.d.ts.map +1 -0
- package/dist/commands/negotiate.js +247 -0
- package/dist/commands/negotiate.js.map +1 -0
- package/dist/commands/openshell.d.ts +3 -0
- package/dist/commands/openshell.d.ts.map +1 -0
- package/dist/commands/openshell.js +952 -0
- package/dist/commands/openshell.js.map +1 -0
- package/dist/commands/owner.d.ts +3 -0
- package/dist/commands/owner.d.ts.map +1 -0
- package/dist/commands/owner.js +32 -0
- package/dist/commands/owner.js.map +1 -0
- package/dist/commands/policy.d.ts +4 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +248 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/relay.d.ts +3 -0
- package/dist/commands/relay.d.ts.map +1 -0
- package/dist/commands/relay.js +279 -0
- package/dist/commands/relay.js.map +1 -0
- package/dist/commands/remediate.d.ts +3 -0
- package/dist/commands/remediate.d.ts.map +1 -0
- package/dist/commands/remediate.js +42 -0
- package/dist/commands/remediate.js.map +1 -0
- package/dist/commands/reputation.d.ts +4 -0
- package/dist/commands/reputation.d.ts.map +1 -0
- package/dist/commands/reputation.js +72 -0
- package/dist/commands/reputation.js.map +1 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +332 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/trust.d.ts +3 -0
- package/dist/commands/trust.d.ts.map +1 -0
- package/dist/commands/trust.js +23 -0
- package/dist/commands/trust.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +88 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/wallet.d.ts +3 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +2520 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/commands/watchtower.d.ts +3 -0
- package/dist/commands/watchtower.d.ts.map +1 -0
- package/dist/commands/watchtower.js +238 -0
- package/dist/commands/watchtower.js.map +1 -0
- package/dist/commands/workroom.d.ts +3 -0
- package/dist/commands/workroom.d.ts.map +1 -0
- package/dist/commands/workroom.js +855 -0
- package/dist/commands/workroom.js.map +1 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +141 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/config.d.ts +74 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +271 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/hire-listener.d.ts +31 -0
- package/dist/daemon/hire-listener.d.ts.map +1 -0
- package/dist/daemon/hire-listener.js +207 -0
- package/dist/daemon/hire-listener.js.map +1 -0
- package/dist/daemon/index.d.ts +29 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +535 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/job-lifecycle.d.ts +62 -0
- package/dist/daemon/job-lifecycle.d.ts.map +1 -0
- package/dist/daemon/job-lifecycle.js +201 -0
- package/dist/daemon/job-lifecycle.js.map +1 -0
- package/dist/daemon/notify.d.ts +22 -0
- package/dist/daemon/notify.d.ts.map +1 -0
- package/dist/daemon/notify.js +148 -0
- package/dist/daemon/notify.js.map +1 -0
- package/dist/daemon/token-metering.d.ts +42 -0
- package/dist/daemon/token-metering.d.ts.map +1 -0
- package/dist/daemon/token-metering.js +178 -0
- package/dist/daemon/token-metering.js.map +1 -0
- package/dist/daemon/userops.d.ts +21 -0
- package/dist/daemon/userops.d.ts.map +1 -0
- package/dist/daemon/userops.js +88 -0
- package/dist/daemon/userops.js.map +1 -0
- package/dist/daemon/wallet-monitor.d.ts +16 -0
- package/dist/daemon/wallet-monitor.d.ts.map +1 -0
- package/dist/daemon/wallet-monitor.js +57 -0
- package/dist/daemon/wallet-monitor.js.map +1 -0
- package/dist/drain-v4.d.ts +2 -0
- package/dist/drain-v4.d.ts.map +1 -0
- package/dist/drain-v4.js +167 -0
- package/dist/drain-v4.js.map +1 -0
- package/dist/endpoint-config.d.ts +36 -0
- package/dist/endpoint-config.d.ts.map +1 -0
- package/dist/endpoint-config.js +96 -0
- package/dist/endpoint-config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/openshell-runtime.d.ts +55 -0
- package/dist/openshell-runtime.d.ts.map +1 -0
- package/dist/openshell-runtime.js +268 -0
- package/dist/openshell-runtime.js.map +1 -0
- package/dist/signing.d.ts +2 -0
- package/dist/signing.d.ts.map +1 -0
- package/dist/signing.js +23 -0
- package/dist/signing.js.map +1 -0
- package/dist/telegram-notify.d.ts +23 -0
- package/dist/telegram-notify.d.ts.map +1 -0
- package/dist/telegram-notify.js +106 -0
- package/dist/telegram-notify.js.map +1 -0
- package/dist/ui/banner.d.ts +7 -0
- package/dist/ui/banner.d.ts.map +1 -0
- package/dist/ui/banner.js +37 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/colors.d.ts +14 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +29 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/format.d.ts +26 -0
- package/dist/ui/format.d.ts.map +1 -0
- package/dist/ui/format.js +77 -0
- package/dist/ui/format.js.map +1 -0
- package/dist/ui/spinner.d.ts +8 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +43 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +61 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/hash.d.ts +3 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +43 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/time.d.ts +3 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +21 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/wallet-router.d.ts +25 -0
- package/dist/wallet-router.d.ts.map +1 -0
- package/dist/wallet-router.js +153 -0
- package/dist/wallet-router.js.map +1 -0
- package/dist/walletconnect-session.d.ts +12 -0
- package/dist/walletconnect-session.d.ts.map +1 -0
- package/dist/walletconnect-session.js +26 -0
- package/dist/walletconnect-session.js.map +1 -0
- package/dist/walletconnect.d.ts +46 -0
- package/dist/walletconnect.d.ts.map +1 -0
- package/dist/walletconnect.js +267 -0
- package/dist/walletconnect.js.map +1 -0
- package/package.json +38 -0
- package/scripts/authorize-machine-key.ts +43 -0
- package/scripts/drain-wallet.ts +149 -0
- package/scripts/execute-spend-only.ts +81 -0
- package/scripts/register-agent-userop.ts +186 -0
- package/src/abis.ts +187 -0
- package/src/bundler.ts +235 -0
- package/src/client.ts +34 -0
- package/src/coinbase-smart-wallet.ts +51 -0
- package/src/commands/accept.ts +25 -0
- package/src/commands/agent-handshake.ts +67 -0
- package/src/commands/agent.ts +458 -0
- package/src/commands/agreements.ts +324 -0
- package/src/commands/arbitrator.ts +129 -0
- package/src/commands/arena-handshake.ts +217 -0
- package/src/commands/cancel.ts +26 -0
- package/src/commands/channel.ts +208 -0
- package/src/commands/coldstart.ts +156 -0
- package/src/commands/config.ts +35 -0
- package/src/commands/contract-interaction.ts +166 -0
- package/src/commands/daemon.ts +971 -0
- package/src/commands/deliver.ts +116 -0
- package/src/commands/discover.ts +295 -0
- package/src/commands/dispute.ts +373 -0
- package/src/commands/endpoint.ts +619 -0
- package/src/commands/hire.ts +200 -0
- package/src/commands/migrate.ts +175 -0
- package/src/commands/negotiate.ts +270 -0
- package/src/commands/openshell.ts +1053 -0
- package/src/commands/owner.ts +30 -0
- package/src/commands/policy.ts +252 -0
- package/src/commands/relay.ts +272 -0
- package/src/commands/remediate.ts +22 -0
- package/src/commands/reputation.ts +71 -0
- package/src/commands/setup.ts +343 -0
- package/src/commands/trust.ts +15 -0
- package/src/commands/verify.ts +88 -0
- package/src/commands/wallet.ts +2892 -0
- package/src/commands/watchtower.ts +232 -0
- package/src/commands/workroom.ts +889 -0
- package/src/config.ts +153 -0
- package/src/daemon/config.ts +308 -0
- package/src/daemon/hire-listener.ts +226 -0
- package/src/daemon/index.ts +609 -0
- package/src/daemon/job-lifecycle.ts +215 -0
- package/src/daemon/notify.ts +157 -0
- package/src/daemon/token-metering.ts +183 -0
- package/src/daemon/userops.ts +119 -0
- package/src/daemon/wallet-monitor.ts +90 -0
- package/src/drain-v4.ts +159 -0
- package/src/endpoint-config.ts +83 -0
- package/src/index.ts +75 -0
- package/src/openshell-runtime.ts +277 -0
- package/src/signing.ts +28 -0
- package/src/telegram-notify.ts +88 -0
- package/src/ui/banner.ts +41 -0
- package/src/ui/colors.ts +30 -0
- package/src/ui/format.ts +77 -0
- package/src/ui/spinner.ts +46 -0
- package/src/utils/format.ts +48 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/time.ts +15 -0
- package/src/wallet-router.ts +178 -0
- package/src/walletconnect-session.ts +27 -0
- package/src/walletconnect.ts +294 -0
- package/test/time.test.js +11 -0
- 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
|