arc402-cli 0.9.18 → 0.10.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 +41 -2
- package/dist/abis.d.ts +1 -0
- package/dist/abis.d.ts.map +1 -1
- package/dist/abis.js +45 -14
- package/dist/abis.js.map +1 -1
- package/dist/bundler.d.ts +1 -1
- package/dist/bundler.d.ts.map +1 -1
- package/dist/bundler.js +61 -27
- package/dist/bundler.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -5
- package/dist/client.js.map +1 -1
- package/dist/coinbase-smart-wallet.js +4 -1
- package/dist/coinbase-smart-wallet.js.map +1 -1
- package/dist/commands/accept.js +28 -25
- package/dist/commands/accept.js.map +1 -1
- package/dist/commands/agent-handshake.js +18 -15
- package/dist/commands/agent-handshake.js.map +1 -1
- package/dist/commands/agent.js +104 -98
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/agreements.js +98 -62
- package/dist/commands/agreements.js.map +1 -1
- package/dist/commands/arbitrator.js +81 -45
- package/dist/commands/arbitrator.js.map +1 -1
- package/dist/commands/arena-handshake.d.ts.map +1 -1
- package/dist/commands/arena-handshake.js +35 -53
- package/dist/commands/arena-handshake.js.map +1 -1
- package/dist/commands/arena.js +18 -12
- package/dist/commands/arena.js.map +1 -1
- package/dist/commands/backup.js +36 -30
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cancel.js +18 -15
- package/dist/commands/cancel.js.map +1 -1
- package/dist/commands/channel.js +81 -45
- package/dist/commands/channel.js.map +1 -1
- package/dist/commands/coldstart.js +34 -31
- package/dist/commands/coldstart.js.map +1 -1
- package/dist/commands/compute.d.ts +14 -0
- package/dist/commands/compute.d.ts.map +1 -0
- package/dist/commands/compute.js +466 -0
- package/dist/commands/compute.js.map +1 -0
- package/dist/commands/config.js +30 -24
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/contract-interaction.js +15 -12
- package/dist/commands/contract-interaction.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +135 -98
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/deliver.js +76 -37
- package/dist/commands/deliver.js.map +1 -1
- package/dist/commands/discover.js +27 -24
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dispute.js +110 -104
- package/dist/commands/dispute.js.map +1 -1
- package/dist/commands/doctor.js +55 -16
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/endpoint.js +95 -56
- package/dist/commands/endpoint.js.map +1 -1
- package/dist/commands/feed.js +18 -11
- package/dist/commands/feed.js.map +1 -1
- package/dist/commands/hire.js +40 -37
- package/dist/commands/hire.js.map +1 -1
- package/dist/commands/migrate.js +33 -30
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/negotiate.d.ts.map +1 -1
- package/dist/commands/negotiate.js +36 -34
- package/dist/commands/negotiate.js.map +1 -1
- package/dist/commands/openshell.js +104 -68
- package/dist/commands/openshell.js.map +1 -1
- package/dist/commands/owner.js +20 -17
- package/dist/commands/owner.js.map +1 -1
- package/dist/commands/policy.js +43 -41
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/relay.d.ts.map +1 -1
- package/dist/commands/relay.js +51 -18
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/remediate.js +23 -20
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/reputation.js +27 -25
- package/dist/commands/reputation.js.map +1 -1
- package/dist/commands/setup.js +104 -65
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/trust.js +20 -17
- package/dist/commands/trust.js.map +1 -1
- package/dist/commands/verify.js +21 -18
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +645 -679
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.js +36 -33
- package/dist/commands/watch.js.map +1 -1
- package/dist/commands/watchtower.js +73 -37
- package/dist/commands/watchtower.js.map +1 -1
- package/dist/commands/workroom.d.ts.map +1 -1
- package/dist/commands/workroom.js +282 -143
- package/dist/commands/workroom.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +71 -22
- package/dist/config.js.map +1 -1
- package/dist/daemon/compute-metering.d.ts +61 -0
- package/dist/daemon/compute-metering.d.ts.map +1 -0
- package/dist/daemon/compute-metering.js +299 -0
- package/dist/daemon/compute-metering.js.map +1 -0
- package/dist/daemon/compute-session.d.ts +100 -0
- package/dist/daemon/compute-session.d.ts.map +1 -0
- package/dist/daemon/compute-session.js +231 -0
- package/dist/daemon/compute-session.js.map +1 -0
- package/dist/daemon/config.d.ts +19 -1
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +90 -16
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/credentials.d.ts +24 -0
- package/dist/daemon/credentials.d.ts.map +1 -0
- package/dist/daemon/credentials.js +80 -0
- package/dist/daemon/credentials.js.map +1 -0
- package/dist/daemon/delivery-client.d.ts +35 -0
- package/dist/daemon/delivery-client.d.ts.map +1 -0
- package/dist/daemon/delivery-client.js +231 -0
- package/dist/daemon/delivery-client.js.map +1 -0
- package/dist/daemon/file-delivery.d.ts +98 -0
- package/dist/daemon/file-delivery.d.ts.map +1 -0
- package/dist/daemon/file-delivery.js +461 -0
- package/dist/daemon/file-delivery.js.map +1 -0
- package/dist/daemon/hire-listener.d.ts +3 -3
- package/dist/daemon/hire-listener.d.ts.map +1 -1
- package/dist/daemon/hire-listener.js +47 -13
- package/dist/daemon/hire-listener.js.map +1 -1
- package/dist/daemon/index.d.ts +2 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +526 -53
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/job-lifecycle.d.ts +1 -1
- package/dist/daemon/job-lifecycle.d.ts.map +1 -1
- package/dist/daemon/job-lifecycle.js +51 -11
- package/dist/daemon/job-lifecycle.js.map +1 -1
- package/dist/daemon/notify.d.ts +1 -1
- package/dist/daemon/notify.d.ts.map +1 -1
- package/dist/daemon/notify.js +53 -19
- package/dist/daemon/notify.js.map +1 -1
- package/dist/daemon/token-metering.js +47 -8
- package/dist/daemon/token-metering.js.map +1 -1
- package/dist/daemon/userops.d.ts +2 -2
- package/dist/daemon/userops.d.ts.map +1 -1
- package/dist/daemon/userops.js +27 -23
- package/dist/daemon/userops.js.map +1 -1
- package/dist/daemon/wallet-monitor.d.ts +1 -1
- package/dist/daemon/wallet-monitor.d.ts.map +1 -1
- package/dist/daemon/wallet-monitor.js +12 -8
- package/dist/daemon/wallet-monitor.js.map +1 -1
- package/dist/daemon/worker-executor.d.ts +71 -0
- package/dist/daemon/worker-executor.d.ts.map +1 -0
- package/dist/daemon/worker-executor.js +382 -0
- package/dist/daemon/worker-executor.js.map +1 -0
- package/dist/drain-v4.js +64 -26
- package/dist/drain-v4.js.map +1 -1
- package/dist/endpoint-config.js +63 -20
- package/dist/endpoint-config.js.map +1 -1
- package/dist/endpoint-notify.js +48 -9
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +50 -18
- package/dist/index.js.map +1 -1
- package/dist/openshell-runtime.d.ts.map +1 -1
- package/dist/openshell-runtime.js +82 -38
- package/dist/openshell-runtime.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +85 -78
- package/dist/program.js.map +1 -1
- package/dist/repl.js +31 -25
- package/dist/repl.js.map +1 -1
- package/dist/signing.js +6 -3
- package/dist/signing.js.map +1 -1
- package/dist/telegram-notify.js +40 -3
- package/dist/telegram-notify.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +56 -89
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Footer.js +7 -4
- package/dist/tui/Footer.js.map +1 -1
- package/dist/tui/Header.d.ts +1 -1
- package/dist/tui/Header.d.ts.map +1 -1
- package/dist/tui/Header.js +14 -9
- package/dist/tui/Header.js.map +1 -1
- package/dist/tui/InputLine.d.ts +2 -1
- package/dist/tui/InputLine.d.ts.map +1 -1
- package/dist/tui/InputLine.js +47 -97
- package/dist/tui/InputLine.js.map +1 -1
- package/dist/tui/Viewport.d.ts +1 -2
- package/dist/tui/Viewport.d.ts.map +1 -1
- package/dist/tui/Viewport.js +26 -6
- package/dist/tui/Viewport.js.map +1 -1
- package/dist/tui/WalletConnectPairing.js +19 -16
- package/dist/tui/WalletConnectPairing.js.map +1 -1
- package/dist/tui/components/Button.js +9 -6
- package/dist/tui/components/Button.js.map +1 -1
- package/dist/tui/components/CeremonyView.js +8 -5
- package/dist/tui/components/CeremonyView.js.map +1 -1
- package/dist/tui/components/CompletionDropdown.js +9 -6
- package/dist/tui/components/CompletionDropdown.js.map +1 -1
- package/dist/tui/components/ConfirmPrompt.js +8 -5
- package/dist/tui/components/ConfirmPrompt.js.map +1 -1
- package/dist/tui/components/CustomTextInput.js +14 -11
- package/dist/tui/components/CustomTextInput.js.map +1 -1
- package/dist/tui/components/InteractiveTable.js +12 -9
- package/dist/tui/components/InteractiveTable.js.map +1 -1
- package/dist/tui/components/StepSpinner.js +13 -10
- package/dist/tui/components/StepSpinner.js.map +1 -1
- package/dist/tui/components/Toast.js +12 -8
- package/dist/tui/components/Toast.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +21 -28
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/useChat.js +19 -13
- package/dist/tui/useChat.js.map +1 -1
- package/dist/tui/useCommand.d.ts +2 -3
- package/dist/tui/useCommand.d.ts.map +1 -1
- package/dist/tui/useCommand.js +24 -100
- package/dist/tui/useCommand.js.map +1 -1
- package/dist/tui/useNotifications.js +8 -5
- package/dist/tui/useNotifications.js.map +1 -1
- package/dist/tui/useScroll.d.ts.map +1 -1
- package/dist/tui/useScroll.js +12 -15
- package/dist/tui/useScroll.js.map +1 -1
- package/dist/ui/banner.d.ts +0 -12
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +19 -35
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/colors.js +19 -13
- package/dist/ui/colors.js.map +1 -1
- package/dist/ui/format.js +14 -6
- package/dist/ui/format.js.map +1 -1
- package/dist/ui/qr-render.js +11 -4
- package/dist/ui/qr-render.js.map +1 -1
- package/dist/ui/rpc-fallback.js +11 -6
- package/dist/ui/rpc-fallback.js.map +1 -1
- package/dist/ui/spinner.js +12 -6
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/tree.js +6 -3
- package/dist/ui/tree.js.map +1 -1
- package/dist/utils/format.js +41 -27
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/hash.js +42 -4
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/time.js +6 -2
- package/dist/utils/time.js.map +1 -1
- package/dist/wallet-router.d.ts +1 -1
- package/dist/wallet-router.d.ts.map +1 -1
- package/dist/wallet-router.js +19 -12
- package/dist/wallet-router.js.map +1 -1
- package/dist/walletconnect-session.d.ts +1 -1
- package/dist/walletconnect-session.d.ts.map +1 -1
- package/dist/walletconnect-session.js +11 -6
- package/dist/walletconnect-session.js.map +1 -1
- package/dist/walletconnect.d.ts +5 -6
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +35 -32
- package/dist/walletconnect.js.map +1 -1
- package/package.json +11 -10
- package/INK6-UX-SPEC.md +0 -446
- package/MIGRATION-SPEC.md +0 -108
- package/TUI-SPEC.md +0 -214
- package/scripts/authorize-machine-key.ts +0 -43
- package/scripts/drain-wallet.ts +0 -149
- package/scripts/execute-spend-only.ts +0 -81
- package/scripts/register-agent-userop.ts +0 -186
- package/src/abis.ts +0 -187
- package/src/bundler.ts +0 -235
- package/src/client.ts +0 -36
- package/src/coinbase-smart-wallet.ts +0 -51
- package/src/commands/accept.ts +0 -64
- package/src/commands/agent-handshake.ts +0 -72
- package/src/commands/agent.ts +0 -691
- package/src/commands/agreements.ts +0 -350
- package/src/commands/arbitrator.ts +0 -180
- package/src/commands/arena-handshake.ts +0 -274
- package/src/commands/arena.ts +0 -122
- package/src/commands/backup.ts +0 -117
- package/src/commands/cancel.ts +0 -35
- package/src/commands/channel.ts +0 -218
- package/src/commands/coldstart.ts +0 -165
- package/src/commands/config.ts +0 -68
- package/src/commands/contract-interaction.ts +0 -166
- package/src/commands/daemon.ts +0 -1054
- package/src/commands/deliver.ts +0 -148
- package/src/commands/discover.ts +0 -350
- package/src/commands/dispute.ts +0 -375
- package/src/commands/doctor.ts +0 -172
- package/src/commands/endpoint.ts +0 -620
- package/src/commands/feed.ts +0 -229
- package/src/commands/hire.ts +0 -245
- package/src/commands/migrate.ts +0 -177
- package/src/commands/negotiate.ts +0 -272
- package/src/commands/openshell.ts +0 -1055
- package/src/commands/owner.ts +0 -35
- package/src/commands/policy.ts +0 -263
- package/src/commands/relay.ts +0 -277
- package/src/commands/remediate.ts +0 -24
- package/src/commands/reputation.ts +0 -79
- package/src/commands/setup.ts +0 -343
- package/src/commands/trust.ts +0 -27
- package/src/commands/verify.ts +0 -91
- package/src/commands/wallet.ts +0 -3548
- package/src/commands/watch.ts +0 -220
- package/src/commands/watchtower.ts +0 -248
- package/src/commands/workroom.ts +0 -963
- package/src/config.ts +0 -220
- package/src/daemon/config.ts +0 -344
- package/src/daemon/hire-listener.ts +0 -226
- package/src/daemon/index.ts +0 -1089
- package/src/daemon/job-lifecycle.ts +0 -215
- package/src/daemon/notify.ts +0 -297
- package/src/daemon/token-metering.ts +0 -183
- package/src/daemon/userops.ts +0 -119
- package/src/daemon/wallet-monitor.ts +0 -90
- package/src/drain-v4.ts +0 -159
- package/src/endpoint-config.ts +0 -83
- package/src/endpoint-notify.ts +0 -129
- package/src/index.ts +0 -74
- package/src/openshell-runtime.ts +0 -281
- package/src/program.ts +0 -88
- package/src/repl.ts +0 -178
- package/src/signing.ts +0 -28
- package/src/telegram-notify.ts +0 -88
- package/src/tui/App.tsx +0 -263
- package/src/tui/Footer.tsx +0 -18
- package/src/tui/Header.tsx +0 -45
- package/src/tui/InputLine.tsx +0 -243
- package/src/tui/Viewport.tsx +0 -51
- package/src/tui/WalletConnectPairing.tsx +0 -114
- package/src/tui/components/Button.tsx +0 -38
- package/src/tui/components/CeremonyView.tsx +0 -39
- package/src/tui/components/CompletionDropdown.tsx +0 -56
- package/src/tui/components/ConfirmPrompt.tsx +0 -36
- package/src/tui/components/CustomTextInput.tsx +0 -132
- package/src/tui/components/InteractiveTable.tsx +0 -106
- package/src/tui/components/StepSpinner.tsx +0 -84
- package/src/tui/components/Toast.tsx +0 -59
- package/src/tui/index.tsx +0 -90
- package/src/tui/useChat.ts +0 -103
- package/src/tui/useCommand.ts +0 -238
- package/src/tui/useNotifications.ts +0 -28
- package/src/tui/useScroll.ts +0 -69
- package/src/ui/banner.ts +0 -78
- package/src/ui/colors.ts +0 -30
- package/src/ui/format.ts +0 -78
- package/src/ui/qr-render.ts +0 -92
- package/src/ui/rpc-fallback.ts +0 -59
- package/src/ui/spinner.ts +0 -56
- package/src/ui/tree.ts +0 -16
- package/src/utils/format.ts +0 -48
- package/src/utils/hash.ts +0 -5
- package/src/utils/time.ts +0 -15
- package/src/wallet-router.ts +0 -178
- package/src/walletconnect-session.ts +0 -27
- package/src/walletconnect.ts +0 -309
- package/test/time.test.js +0 -11
- package/tsconfig.json +0 -33
package/dist/daemon/index.js
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runDaemon = runDaemon;
|
|
1
40
|
/**
|
|
2
41
|
* ARC-402 Daemon — main process entry point.
|
|
3
42
|
*
|
|
@@ -7,22 +46,26 @@
|
|
|
7
46
|
* IPC: Unix socket at ~/.arc402/daemon.sock (JSON-lines protocol).
|
|
8
47
|
* Signals: SIGTERM → graceful shutdown.
|
|
9
48
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const net = __importStar(require("net"));
|
|
52
|
+
const http = __importStar(require("http"));
|
|
53
|
+
const ethers_1 = require("ethers");
|
|
54
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
55
|
+
const crypto = __importStar(require("crypto"));
|
|
56
|
+
const config_1 = require("./config");
|
|
57
|
+
const compute_metering_1 = require("./compute-metering");
|
|
58
|
+
const compute_session_1 = require("./compute-session");
|
|
59
|
+
const wallet_monitor_1 = require("./wallet-monitor");
|
|
60
|
+
const notify_1 = require("./notify");
|
|
61
|
+
const hire_listener_1 = require("./hire-listener");
|
|
62
|
+
const userops_1 = require("./userops");
|
|
63
|
+
const job_lifecycle_1 = require("./job-lifecycle");
|
|
64
|
+
const file_delivery_1 = require("./file-delivery");
|
|
65
|
+
const delivery_client_1 = require("./delivery-client");
|
|
66
|
+
const abis_1 = require("../abis");
|
|
24
67
|
function openStateDB(dbPath) {
|
|
25
|
-
const db = new
|
|
68
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
26
69
|
db.pragma("journal_mode = WAL");
|
|
27
70
|
db.exec(`
|
|
28
71
|
CREATE TABLE IF NOT EXISTS hire_requests (
|
|
@@ -77,6 +120,7 @@ function openStateDB(dbPath) {
|
|
|
77
120
|
(@id, @agreement_id, @hirer_address, @capability, @price_eth, @deadline_unix, @spec_hash, @status, @created_at, @updated_at, @reject_reason)
|
|
78
121
|
`);
|
|
79
122
|
const getHireRequest = db.prepare(`SELECT * FROM hire_requests WHERE id = ?`);
|
|
123
|
+
const getHireRequestByAgreementId = db.prepare(`SELECT * FROM hire_requests WHERE agreement_id = ? ORDER BY created_at DESC LIMIT 1`);
|
|
80
124
|
const updateStatus = db.prepare(`UPDATE hire_requests SET status = ?, reject_reason = ?, updated_at = ? WHERE id = ?`);
|
|
81
125
|
const listPending = db.prepare(`SELECT * FROM hire_requests WHERE status = 'pending_approval' ORDER BY created_at ASC`);
|
|
82
126
|
const listActive = db.prepare(`SELECT * FROM hire_requests WHERE status IN ('accepted', 'delivered') ORDER BY created_at ASC`);
|
|
@@ -89,6 +133,9 @@ function openStateDB(dbPath) {
|
|
|
89
133
|
getHireRequest(id) {
|
|
90
134
|
return getHireRequest.get(id);
|
|
91
135
|
},
|
|
136
|
+
getHireRequestByAgreementId(agreementId) {
|
|
137
|
+
return getHireRequestByAgreementId.get(agreementId);
|
|
138
|
+
},
|
|
92
139
|
updateHireRequestStatus(id, status, rejectReason) {
|
|
93
140
|
updateStatus.run(status, rejectReason ?? null, Date.now(), id);
|
|
94
141
|
},
|
|
@@ -108,7 +155,7 @@ function openStateDB(dbPath) {
|
|
|
108
155
|
};
|
|
109
156
|
}
|
|
110
157
|
// ─── Auth token ───────────────────────────────────────────────────────────────
|
|
111
|
-
const DAEMON_TOKEN_FILE = path.join(path.dirname(DAEMON_SOCK), "daemon.token");
|
|
158
|
+
const DAEMON_TOKEN_FILE = path.join(path.dirname(config_1.DAEMON_SOCK), "daemon.token");
|
|
112
159
|
function generateApiToken() {
|
|
113
160
|
return crypto.randomBytes(32).toString("hex");
|
|
114
161
|
}
|
|
@@ -126,7 +173,7 @@ function loadApiToken() {
|
|
|
126
173
|
}
|
|
127
174
|
const rateLimitMap = new Map();
|
|
128
175
|
const RATE_LIMIT = 30;
|
|
129
|
-
const RATE_WINDOW_MS =
|
|
176
|
+
const RATE_WINDOW_MS = 60000;
|
|
130
177
|
function checkRateLimit(ip) {
|
|
131
178
|
const now = Date.now();
|
|
132
179
|
let bucket = rateLimitMap.get(ip);
|
|
@@ -160,8 +207,8 @@ function openLogger(logPath, foreground) {
|
|
|
160
207
|
}
|
|
161
208
|
function startIpcServer(ctx, log, apiToken) {
|
|
162
209
|
// Remove stale socket
|
|
163
|
-
if (fs.existsSync(DAEMON_SOCK)) {
|
|
164
|
-
fs.unlinkSync(DAEMON_SOCK);
|
|
210
|
+
if (fs.existsSync(config_1.DAEMON_SOCK)) {
|
|
211
|
+
fs.unlinkSync(config_1.DAEMON_SOCK);
|
|
165
212
|
}
|
|
166
213
|
const server = net.createServer((socket) => {
|
|
167
214
|
let buf = "";
|
|
@@ -200,9 +247,9 @@ function startIpcServer(ctx, log, apiToken) {
|
|
|
200
247
|
});
|
|
201
248
|
socket.on("error", () => { });
|
|
202
249
|
});
|
|
203
|
-
server.listen(DAEMON_SOCK, () => {
|
|
204
|
-
fs.chmodSync(DAEMON_SOCK, 0o600);
|
|
205
|
-
log({ event: "ipc_ready", socket: DAEMON_SOCK });
|
|
250
|
+
server.listen(config_1.DAEMON_SOCK, () => {
|
|
251
|
+
fs.chmodSync(config_1.DAEMON_SOCK, 0o600);
|
|
252
|
+
log({ event: "ipc_ready", socket: config_1.DAEMON_SOCK });
|
|
206
253
|
});
|
|
207
254
|
return server;
|
|
208
255
|
}
|
|
@@ -259,7 +306,7 @@ function handleIpcCommand(cmd, ctx, log) {
|
|
|
259
306
|
log({ event: "hire_approved", id: cmd.id });
|
|
260
307
|
// Trigger accept UserOp (fire and forget)
|
|
261
308
|
if (ctx.userOps && ctx.config.serviceAgreementAddress) {
|
|
262
|
-
const callData = buildAcceptCalldata(ctx.config.serviceAgreementAddress, hire.agreement_id ?? cmd.id, ctx.walletAddress);
|
|
309
|
+
const callData = (0, userops_1.buildAcceptCalldata)(ctx.config.serviceAgreementAddress, hire.agreement_id ?? cmd.id, ctx.walletAddress);
|
|
263
310
|
ctx.userOps.submit(callData, ctx.walletAddress).then((hash) => {
|
|
264
311
|
log({ event: "userop_submitted", id: cmd.id, hash });
|
|
265
312
|
}).catch((err) => {
|
|
@@ -293,7 +340,7 @@ function handleIpcCommand(cmd, ctx, log) {
|
|
|
293
340
|
const now = new Date().toISOString();
|
|
294
341
|
const startedAt = new Date(hire.created_at).toISOString();
|
|
295
342
|
// Generate execution receipt
|
|
296
|
-
const receipt = generateReceipt({
|
|
343
|
+
const receipt = (0, job_lifecycle_1.generateReceipt)({
|
|
297
344
|
agreementId: hire.agreement_id ?? cmd.id,
|
|
298
345
|
deliverableHash: hire.spec_hash ?? "0x0",
|
|
299
346
|
walletAddress: ctx.walletAddress,
|
|
@@ -302,7 +349,7 @@ function handleIpcCommand(cmd, ctx, log) {
|
|
|
302
349
|
});
|
|
303
350
|
log({ event: "receipt_generated", id: cmd.id, receipt_hash: receipt.receipt_hash });
|
|
304
351
|
// Extract learnings
|
|
305
|
-
extractLearnings({
|
|
352
|
+
(0, job_lifecycle_1.extractLearnings)({
|
|
306
353
|
agreementId: hire.agreement_id ?? cmd.id,
|
|
307
354
|
taskDescription: hire.capability ?? "unknown",
|
|
308
355
|
deliverableHash: hire.spec_hash ?? "0x0",
|
|
@@ -315,7 +362,7 @@ function handleIpcCommand(cmd, ctx, log) {
|
|
|
315
362
|
// Update status to complete
|
|
316
363
|
ctx.db.updateHireRequestStatus(cmd.id, "complete");
|
|
317
364
|
// Clean job directory (keep receipt + memory)
|
|
318
|
-
cleanJobDirectory(hire.agreement_id ?? cmd.id);
|
|
365
|
+
(0, job_lifecycle_1.cleanJobDirectory)(hire.agreement_id ?? cmd.id);
|
|
319
366
|
return {
|
|
320
367
|
ok: true,
|
|
321
368
|
data: {
|
|
@@ -339,15 +386,15 @@ function formatUptime(seconds) {
|
|
|
339
386
|
return `${m}m ${s}s`;
|
|
340
387
|
return `${s}s`;
|
|
341
388
|
}
|
|
342
|
-
|
|
343
|
-
fs.mkdirSync(DAEMON_DIR, { recursive: true, mode: 0o700 });
|
|
344
|
-
const log = openLogger(DAEMON_LOG, foreground);
|
|
389
|
+
async function runDaemon(foreground = false) {
|
|
390
|
+
fs.mkdirSync(config_1.DAEMON_DIR, { recursive: true, mode: 0o700 });
|
|
391
|
+
const log = openLogger(config_1.DAEMON_LOG, foreground);
|
|
345
392
|
log({ event: "daemon_starting" });
|
|
346
393
|
// ── Step 1: Load config ──────────────────────────────────────────────────
|
|
347
394
|
let config;
|
|
348
395
|
try {
|
|
349
|
-
config = loadDaemonConfig();
|
|
350
|
-
log({ event: "config_loaded", path: path.join(os.homedir(), ".arc402", "daemon.toml") });
|
|
396
|
+
config = (0, config_1.loadDaemonConfig)();
|
|
397
|
+
log({ event: "config_loaded", path: require("path").join(require("os").homedir(), ".arc402", "daemon.toml") });
|
|
351
398
|
}
|
|
352
399
|
catch (err) {
|
|
353
400
|
process.stderr.write(`Config error: ${err}\n`);
|
|
@@ -357,7 +404,7 @@ export async function runDaemon(foreground = false) {
|
|
|
357
404
|
let machineKeyAddress;
|
|
358
405
|
let machinePrivateKey;
|
|
359
406
|
try {
|
|
360
|
-
const mk = loadMachineKey(config);
|
|
407
|
+
const mk = (0, config_1.loadMachineKey)(config);
|
|
361
408
|
machinePrivateKey = mk.privateKey;
|
|
362
409
|
machineKeyAddress = mk.address;
|
|
363
410
|
log({ event: "machine_key_loaded", address: machineKeyAddress });
|
|
@@ -367,7 +414,7 @@ export async function runDaemon(foreground = false) {
|
|
|
367
414
|
process.exit(1);
|
|
368
415
|
}
|
|
369
416
|
// ── Step 3: Connect to RPC ───────────────────────────────────────────────
|
|
370
|
-
const provider = new ethers.JsonRpcProvider(config.network.rpc_url);
|
|
417
|
+
const provider = new ethers_1.ethers.JsonRpcProvider(config.network.rpc_url);
|
|
371
418
|
try {
|
|
372
419
|
const chainId = (await provider.getNetwork()).chainId;
|
|
373
420
|
if (Number(chainId) !== config.network.chain_id) {
|
|
@@ -383,7 +430,7 @@ export async function runDaemon(foreground = false) {
|
|
|
383
430
|
}
|
|
384
431
|
// ── Step 4+5: Verify wallet ──────────────────────────────────────────────
|
|
385
432
|
try {
|
|
386
|
-
const walletStatus = await verifyWallet(config, provider, machineKeyAddress);
|
|
433
|
+
const walletStatus = await (0, wallet_monitor_1.verifyWallet)(config, provider, machineKeyAddress);
|
|
387
434
|
log({
|
|
388
435
|
event: "wallet_verified",
|
|
389
436
|
address: walletStatus.contractAddress,
|
|
@@ -397,7 +444,7 @@ export async function runDaemon(foreground = false) {
|
|
|
397
444
|
process.exit(1);
|
|
398
445
|
}
|
|
399
446
|
// ── Step 6: Connect bundler ──────────────────────────────────────────────
|
|
400
|
-
const userOps = new UserOpsManager(config, provider);
|
|
447
|
+
const userOps = new userops_1.UserOpsManager(config, provider);
|
|
401
448
|
const bundlerOk = await userOps.pingBundler();
|
|
402
449
|
if (!bundlerOk) {
|
|
403
450
|
log({ event: "bundler_warn", msg: "Bundler endpoint unreachable — will retry on demand" });
|
|
@@ -407,17 +454,44 @@ export async function runDaemon(foreground = false) {
|
|
|
407
454
|
// ── Step 7: Open state DB ────────────────────────────────────────────────
|
|
408
455
|
let db;
|
|
409
456
|
try {
|
|
410
|
-
db = openStateDB(DAEMON_DB);
|
|
411
|
-
log({ event: "state_db_opened", path: DAEMON_DB });
|
|
457
|
+
db = openStateDB(config_1.DAEMON_DB);
|
|
458
|
+
log({ event: "state_db_opened", path: config_1.DAEMON_DB });
|
|
412
459
|
}
|
|
413
460
|
catch (err) {
|
|
414
461
|
process.stderr.write(`State DB error: ${err}\n`);
|
|
415
462
|
process.exit(1);
|
|
416
463
|
}
|
|
417
464
|
// ── Setup notifier ───────────────────────────────────────────────────────
|
|
418
|
-
const notifier = buildNotifier(config);
|
|
465
|
+
const notifier = (0, notify_1.buildNotifier)(config);
|
|
466
|
+
// ── File delivery subsystem ──────────────────────────────────────────────
|
|
467
|
+
const fileDelivery = new file_delivery_1.FileDeliveryManager({
|
|
468
|
+
maxFileSizeMb: config.delivery.max_file_size_mb,
|
|
469
|
+
maxJobSizeMb: config.delivery.max_job_size_mb,
|
|
470
|
+
});
|
|
471
|
+
fileDelivery.setPartyResolver((agreementId) => {
|
|
472
|
+
const row = db.getHireRequestByAgreementId(agreementId);
|
|
473
|
+
if (!row)
|
|
474
|
+
return null;
|
|
475
|
+
return {
|
|
476
|
+
hirerAddress: row.hirer_address,
|
|
477
|
+
providerAddress: config.wallet.contract_address,
|
|
478
|
+
};
|
|
479
|
+
});
|
|
480
|
+
const deliveryClient = new delivery_client_1.DeliveryClient({ autoDownload: config.delivery.auto_download });
|
|
481
|
+
deliveryClient.log = log;
|
|
482
|
+
log({ event: "file_delivery_ready", serve_files: config.delivery.serve_files, auto_download: config.delivery.auto_download });
|
|
483
|
+
// ── Compute rental subsystem ─────────────────────────────────────────────
|
|
484
|
+
let computeMetering = null;
|
|
485
|
+
let computeSessions = null;
|
|
486
|
+
// Machine key signer — used for on-chain compute contract calls
|
|
487
|
+
const machineKeySigner = new ethers_1.ethers.Wallet(machinePrivateKey, provider);
|
|
488
|
+
if (config.compute.enabled) {
|
|
489
|
+
computeMetering = new compute_metering_1.ComputeMetering(machinePrivateKey, config.network.chain_id, config.compute.compute_agreement_address || config.wallet.contract_address, config.compute.metering_interval_seconds, config.compute.report_interval_minutes, config.compute.compute_agreement_address ? provider : undefined);
|
|
490
|
+
computeSessions = new compute_session_1.ComputeSessionManager(computeMetering);
|
|
491
|
+
log({ event: "compute_enabled", gpu_spec: config.compute.gpu_spec, rate_wei: config.compute.rate_per_hour_wei });
|
|
492
|
+
}
|
|
419
493
|
// ── Step 10: Start relay listener ───────────────────────────────────────
|
|
420
|
-
const hireListener = new HireListener(config, db, notifier, config.wallet.contract_address);
|
|
494
|
+
const hireListener = new hire_listener_1.HireListener(config, db, notifier, config.wallet.contract_address);
|
|
421
495
|
const ipcCtx = {
|
|
422
496
|
db,
|
|
423
497
|
config,
|
|
@@ -436,7 +510,7 @@ export async function runDaemon(foreground = false) {
|
|
|
436
510
|
if (!hire || !hire.agreement_id || !config.serviceAgreementAddress)
|
|
437
511
|
return;
|
|
438
512
|
try {
|
|
439
|
-
const callData = buildAcceptCalldata(config.serviceAgreementAddress, hire.agreement_id, config.wallet.contract_address);
|
|
513
|
+
const callData = (0, userops_1.buildAcceptCalldata)(config.serviceAgreementAddress, hire.agreement_id, config.wallet.contract_address);
|
|
440
514
|
const hash = await userOps.submit(callData, config.wallet.contract_address);
|
|
441
515
|
log({ event: "hire_auto_accepted", id: hireId, userop_hash: hash });
|
|
442
516
|
if (config.notifications.notify_on_hire_accepted) {
|
|
@@ -466,7 +540,7 @@ export async function runDaemon(foreground = false) {
|
|
|
466
540
|
log({ event: "hire_timeout_rejected", id: req.id });
|
|
467
541
|
}
|
|
468
542
|
}
|
|
469
|
-
},
|
|
543
|
+
}, 30000);
|
|
470
544
|
// Rate limit map cleanup — every 5 minutes (prevents unbounded growth)
|
|
471
545
|
rateLimitCleanupInterval = setInterval(() => {
|
|
472
546
|
const now = Date.now();
|
|
@@ -478,7 +552,7 @@ export async function runDaemon(foreground = false) {
|
|
|
478
552
|
// Balance monitor — every 5 minutes
|
|
479
553
|
const balanceInterval = setInterval(async () => {
|
|
480
554
|
try {
|
|
481
|
-
const balance = await getWalletBalance(config.wallet.contract_address, provider);
|
|
555
|
+
const balance = await (0, wallet_monitor_1.getWalletBalance)(config.wallet.contract_address, provider);
|
|
482
556
|
const threshold = parseFloat(config.notifications.low_balance_threshold_eth);
|
|
483
557
|
if (parseFloat(balance) < threshold) {
|
|
484
558
|
log({ event: "low_balance", balance_eth: balance, threshold_eth: config.notifications.low_balance_threshold_eth });
|
|
@@ -489,8 +563,8 @@ export async function runDaemon(foreground = false) {
|
|
|
489
563
|
}, 5 * 60 * 1000);
|
|
490
564
|
// ── Step 11: Write PID file (if not foreground) ──────────────────────────
|
|
491
565
|
if (!foreground) {
|
|
492
|
-
fs.writeFileSync(DAEMON_PID, String(process.pid), { mode: 0o600 });
|
|
493
|
-
log({ event: "pid_written", pid: process.pid, path: DAEMON_PID });
|
|
566
|
+
fs.writeFileSync(config_1.DAEMON_PID, String(process.pid), { mode: 0o600 });
|
|
567
|
+
log({ event: "pid_written", pid: process.pid, path: config_1.DAEMON_PID });
|
|
494
568
|
}
|
|
495
569
|
// ── Generate and save API token ──────────────────────────────────────────
|
|
496
570
|
const apiToken = generateApiToken();
|
|
@@ -510,7 +584,7 @@ export async function runDaemon(foreground = false) {
|
|
|
510
584
|
return;
|
|
511
585
|
const claimedSigner = req.headers["x-arc402-signer"];
|
|
512
586
|
try {
|
|
513
|
-
const recovered = ethers.verifyMessage(body, sig);
|
|
587
|
+
const recovered = ethers_1.ethers.verifyMessage(body, sig);
|
|
514
588
|
if (claimedSigner && recovered.toLowerCase() !== claimedSigner.toLowerCase()) {
|
|
515
589
|
log({ event: "sig_mismatch", claimed: claimedSigner, recovered });
|
|
516
590
|
}
|
|
@@ -577,8 +651,9 @@ export async function runDaemon(foreground = false) {
|
|
|
577
651
|
res.end(JSON.stringify({ error: "too_many_requests" }));
|
|
578
652
|
return;
|
|
579
653
|
}
|
|
580
|
-
// Auth required on all POST endpoints (GET public paths are open)
|
|
581
|
-
|
|
654
|
+
// Auth required on all POST endpoints (GET public paths are open).
|
|
655
|
+
// /job/* GET routes use party auth (verifyPartyAccess) instead of daemon bearer token.
|
|
656
|
+
if (req.method === "POST" || (req.method === "GET" && !PUBLIC_GET_PATHS.has(pathname) && !pathname.startsWith("/job/"))) {
|
|
582
657
|
const authHeader = req.headers["authorization"] ?? "";
|
|
583
658
|
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
584
659
|
if (token !== apiToken) {
|
|
@@ -643,7 +718,7 @@ export async function runDaemon(foreground = false) {
|
|
|
643
718
|
return;
|
|
644
719
|
}
|
|
645
720
|
// Policy check
|
|
646
|
-
const { evaluatePolicy } = await
|
|
721
|
+
const { evaluatePolicy } = await Promise.resolve().then(() => __importStar(require("./hire-listener")));
|
|
647
722
|
const activeCount = db.countActiveHireRequests();
|
|
648
723
|
const policyResult = evaluatePolicy(proposal, config, activeCount);
|
|
649
724
|
if (!policyResult.allowed) {
|
|
@@ -761,8 +836,9 @@ export async function runDaemon(foreground = false) {
|
|
|
761
836
|
const msg = JSON.parse(body);
|
|
762
837
|
const agreementId = String(msg.agreementId ?? msg.agreement_id ?? "");
|
|
763
838
|
const deliverableHash = String(msg.deliverableHash ?? msg.deliverable_hash ?? "");
|
|
839
|
+
const filesUrl = String(msg.files_url ?? msg.filesUrl ?? "");
|
|
764
840
|
const from = String(msg.from ?? "");
|
|
765
|
-
log({ event: "delivery_received", agreementId, deliverableHash, from });
|
|
841
|
+
log({ event: "delivery_received", agreementId, deliverableHash, from, has_files_url: !!filesUrl });
|
|
766
842
|
// Update DB: mark delivered
|
|
767
843
|
const active = db.listActiveHireRequests();
|
|
768
844
|
const found = active.find(r => r.agreement_id === agreementId);
|
|
@@ -771,6 +847,15 @@ export async function runDaemon(foreground = false) {
|
|
|
771
847
|
if (config.notifications.notify_on_delivery) {
|
|
772
848
|
await notifier.notifyDelivery(agreementId, deliverableHash, "");
|
|
773
849
|
}
|
|
850
|
+
// Auto-download and verify if files_url provided and auto_download enabled
|
|
851
|
+
if (filesUrl && config.delivery.auto_download) {
|
|
852
|
+
void deliveryClient.handleDeliveryNotification({ agreementId, deliverableHash, filesUrl })
|
|
853
|
+
.then(result => {
|
|
854
|
+
if (result)
|
|
855
|
+
log({ event: "delivery_auto_downloaded", agreementId, ok: result.ok, root_hash_match: result.rootHashMatch, files: result.fileResults.length });
|
|
856
|
+
})
|
|
857
|
+
.catch(err => log({ event: "delivery_download_error", agreementId, error: String(err) }));
|
|
858
|
+
}
|
|
774
859
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
775
860
|
res.end(JSON.stringify({ received: true, agent: config.wallet.contract_address }));
|
|
776
861
|
}
|
|
@@ -895,11 +980,399 @@ export async function runDaemon(foreground = false) {
|
|
|
895
980
|
if (statusAuthed) {
|
|
896
981
|
statusPayload.active_agreements = db.listActiveHireRequests().length;
|
|
897
982
|
statusPayload.pending_approval = db.listPendingHireRequests().length;
|
|
983
|
+
if (config.delivery.serve_files) {
|
|
984
|
+
const deliveriesDir = path.join(config_1.DAEMON_DIR, "deliveries");
|
|
985
|
+
let totalDeliveries = 0, totalFiles = 0;
|
|
986
|
+
if (fs.existsSync(deliveriesDir)) {
|
|
987
|
+
const entries = fs.readdirSync(deliveriesDir);
|
|
988
|
+
totalDeliveries = entries.length;
|
|
989
|
+
for (const entry of entries) {
|
|
990
|
+
const entryPath = path.join(deliveriesDir, entry);
|
|
991
|
+
if (fs.statSync(entryPath).isDirectory()) {
|
|
992
|
+
totalFiles += fs.readdirSync(entryPath).filter(f => f !== "_manifest.json").length;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
statusPayload.file_delivery = { total_deliveries: totalDeliveries, total_files: totalFiles };
|
|
997
|
+
}
|
|
898
998
|
}
|
|
899
999
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
900
1000
|
res.end(JSON.stringify(statusPayload));
|
|
901
1001
|
return;
|
|
902
1002
|
}
|
|
1003
|
+
// ── Compute rental routes ─────────────────────────────────────────────
|
|
1004
|
+
// POST /compute/propose — client proposes a compute session
|
|
1005
|
+
if (pathname === "/compute/propose" && req.method === "POST") {
|
|
1006
|
+
const body = await readBody(req, res);
|
|
1007
|
+
if (body === null)
|
|
1008
|
+
return;
|
|
1009
|
+
try {
|
|
1010
|
+
const msg = JSON.parse(body);
|
|
1011
|
+
if (!computeSessions) {
|
|
1012
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1013
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const proposal = {
|
|
1017
|
+
sessionId: String(msg.sessionId ?? ""),
|
|
1018
|
+
clientAddress: String(msg.clientAddress ?? msg.client ?? ""),
|
|
1019
|
+
providerAddress: config.wallet.contract_address,
|
|
1020
|
+
ratePerHourWei: config.compute.rate_per_hour_wei,
|
|
1021
|
+
maxHours: Number(msg.maxHours ?? 1),
|
|
1022
|
+
gpuSpecHash: String(msg.gpuSpecHash ?? "0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
1023
|
+
workloadDescription: String(msg.workloadDescription ?? ""),
|
|
1024
|
+
depositAmount: String(msg.depositAmount ?? "0"),
|
|
1025
|
+
proposedAt: Math.floor(Date.now() / 1000),
|
|
1026
|
+
};
|
|
1027
|
+
if (!proposal.sessionId) {
|
|
1028
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1029
|
+
res.end(JSON.stringify({ error: "sessionId required" }));
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
// Check capacity
|
|
1033
|
+
const activeSessions = computeSessions.countByStatus("active");
|
|
1034
|
+
if (activeSessions >= config.compute.max_concurrent_sessions) {
|
|
1035
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1036
|
+
res.end(JSON.stringify({ status: "rejected", reason: "at_capacity" }));
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const result = computeSessions.handleProposal(proposal);
|
|
1040
|
+
if (!result.ok) {
|
|
1041
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1042
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
log({ event: "compute_proposed", sessionId: proposal.sessionId, client: proposal.clientAddress });
|
|
1046
|
+
// Auto-accept if configured
|
|
1047
|
+
if (config.compute.auto_accept_compute) {
|
|
1048
|
+
computeSessions.acceptSession(proposal.sessionId);
|
|
1049
|
+
log({ event: "compute_auto_accepted", sessionId: proposal.sessionId });
|
|
1050
|
+
// Wire to on-chain: provider calls acceptSession
|
|
1051
|
+
if (config.compute.compute_agreement_address) {
|
|
1052
|
+
try {
|
|
1053
|
+
const caContract = new ethers_1.ethers.Contract(config.compute.compute_agreement_address, abis_1.COMPUTE_AGREEMENT_ABI, machineKeySigner);
|
|
1054
|
+
const tx = await caContract.acceptSession(proposal.sessionId);
|
|
1055
|
+
await tx.wait();
|
|
1056
|
+
log({ event: "compute_accept_onchain", sessionId: proposal.sessionId, txHash: tx.hash });
|
|
1057
|
+
}
|
|
1058
|
+
catch (onchainErr) {
|
|
1059
|
+
log({ event: "compute_accept_onchain_error", sessionId: proposal.sessionId, error: String(onchainErr) });
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
const status = config.compute.auto_accept_compute ? "accepted" : "proposed";
|
|
1064
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1065
|
+
res.end(JSON.stringify({ status, sessionId: proposal.sessionId }));
|
|
1066
|
+
}
|
|
1067
|
+
catch (err) {
|
|
1068
|
+
log({ event: "compute_propose_error", error: String(err) });
|
|
1069
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1070
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1071
|
+
}
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
// POST /compute/accept — provider accepts a proposed session
|
|
1075
|
+
if (pathname === "/compute/accept" && req.method === "POST") {
|
|
1076
|
+
const body = await readBody(req, res);
|
|
1077
|
+
if (body === null)
|
|
1078
|
+
return;
|
|
1079
|
+
try {
|
|
1080
|
+
const msg = JSON.parse(body);
|
|
1081
|
+
const sessionId = String(msg.sessionId ?? "");
|
|
1082
|
+
if (!computeSessions) {
|
|
1083
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1084
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const result = computeSessions.acceptSession(sessionId);
|
|
1088
|
+
if (!result.ok) {
|
|
1089
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1090
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
log({ event: "compute_accepted", sessionId });
|
|
1094
|
+
// Wire to on-chain: provider calls acceptSession
|
|
1095
|
+
if (config.compute.compute_agreement_address) {
|
|
1096
|
+
try {
|
|
1097
|
+
const caContract = new ethers_1.ethers.Contract(config.compute.compute_agreement_address, abis_1.COMPUTE_AGREEMENT_ABI, machineKeySigner);
|
|
1098
|
+
const tx = await caContract.acceptSession(sessionId);
|
|
1099
|
+
await tx.wait();
|
|
1100
|
+
log({ event: "compute_accept_onchain", sessionId, txHash: tx.hash });
|
|
1101
|
+
}
|
|
1102
|
+
catch (onchainErr) {
|
|
1103
|
+
log({ event: "compute_accept_onchain_error", sessionId, error: String(onchainErr) });
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1107
|
+
res.end(JSON.stringify({ status: "accepted", sessionId }));
|
|
1108
|
+
}
|
|
1109
|
+
catch (err) {
|
|
1110
|
+
log({ event: "compute_accept_error", error: String(err) });
|
|
1111
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1112
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1113
|
+
}
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
// POST /compute/started — provider marks session as started
|
|
1117
|
+
if (pathname === "/compute/started" && req.method === "POST") {
|
|
1118
|
+
const body = await readBody(req, res);
|
|
1119
|
+
if (body === null)
|
|
1120
|
+
return;
|
|
1121
|
+
try {
|
|
1122
|
+
const msg = JSON.parse(body);
|
|
1123
|
+
const sessionId = String(msg.sessionId ?? "");
|
|
1124
|
+
const accessEndpoint = msg.accessEndpoint ? String(msg.accessEndpoint) : undefined;
|
|
1125
|
+
if (!computeSessions) {
|
|
1126
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1127
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
const result = computeSessions.startSession(sessionId, accessEndpoint);
|
|
1131
|
+
if (!result.ok) {
|
|
1132
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1133
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
log({ event: "compute_started", sessionId });
|
|
1137
|
+
// Wire to on-chain: provider calls startSession
|
|
1138
|
+
if (config.compute.compute_agreement_address) {
|
|
1139
|
+
try {
|
|
1140
|
+
const caContract = new ethers_1.ethers.Contract(config.compute.compute_agreement_address, abis_1.COMPUTE_AGREEMENT_ABI, machineKeySigner);
|
|
1141
|
+
const tx = await caContract.startSession(sessionId);
|
|
1142
|
+
await tx.wait();
|
|
1143
|
+
log({ event: "compute_start_onchain", sessionId, txHash: tx.hash });
|
|
1144
|
+
}
|
|
1145
|
+
catch (onchainErr) {
|
|
1146
|
+
log({ event: "compute_start_onchain_error", sessionId, error: String(onchainErr) });
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1150
|
+
res.end(JSON.stringify({ status: "active", sessionId }));
|
|
1151
|
+
}
|
|
1152
|
+
catch (err) {
|
|
1153
|
+
log({ event: "compute_started_error", error: String(err) });
|
|
1154
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1155
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1156
|
+
}
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
// POST /compute/metrics — get current metrics (polling endpoint)
|
|
1160
|
+
if (pathname === "/compute/metrics" && req.method === "POST") {
|
|
1161
|
+
const body = await readBody(req, res);
|
|
1162
|
+
if (body === null)
|
|
1163
|
+
return;
|
|
1164
|
+
try {
|
|
1165
|
+
const msg = JSON.parse(body);
|
|
1166
|
+
const sessionId = String(msg.sessionId ?? "");
|
|
1167
|
+
if (!computeMetering || !computeSessions) {
|
|
1168
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1169
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
const session = computeSessions.getSession(sessionId);
|
|
1173
|
+
if (!session) {
|
|
1174
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1175
|
+
res.end(JSON.stringify({ error: "session_not_found" }));
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const current = computeMetering.getCurrentMetrics(sessionId);
|
|
1179
|
+
const reports = computeMetering.getUsageReports(sessionId);
|
|
1180
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1181
|
+
res.end(JSON.stringify({ sessionId, current, reports, consumedMinutes: session.consumedMinutes }));
|
|
1182
|
+
}
|
|
1183
|
+
catch (err) {
|
|
1184
|
+
log({ event: "compute_metrics_error", error: String(err) });
|
|
1185
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1186
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1187
|
+
}
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
// POST /compute/end — either party ends the session
|
|
1191
|
+
if (pathname === "/compute/end" && req.method === "POST") {
|
|
1192
|
+
const body = await readBody(req, res);
|
|
1193
|
+
if (body === null)
|
|
1194
|
+
return;
|
|
1195
|
+
try {
|
|
1196
|
+
const msg = JSON.parse(body);
|
|
1197
|
+
const sessionId = String(msg.sessionId ?? "");
|
|
1198
|
+
if (!computeSessions) {
|
|
1199
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1200
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const endResult = await computeSessions.endSession(sessionId);
|
|
1204
|
+
if (!endResult.ok) {
|
|
1205
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1206
|
+
res.end(JSON.stringify({ error: endResult.error }));
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const r = endResult.result;
|
|
1210
|
+
log({
|
|
1211
|
+
event: "compute_ended",
|
|
1212
|
+
sessionId,
|
|
1213
|
+
consumedMinutes: r.consumedMinutes,
|
|
1214
|
+
costWei: r.costWei.toString(),
|
|
1215
|
+
refundWei: r.refundWei.toString(),
|
|
1216
|
+
});
|
|
1217
|
+
// Wire to on-chain: call endSession
|
|
1218
|
+
if (config.compute.compute_agreement_address) {
|
|
1219
|
+
try {
|
|
1220
|
+
const caContract = new ethers_1.ethers.Contract(config.compute.compute_agreement_address, abis_1.COMPUTE_AGREEMENT_ABI, machineKeySigner);
|
|
1221
|
+
const tx = await caContract.endSession(sessionId);
|
|
1222
|
+
await tx.wait();
|
|
1223
|
+
log({ event: "compute_end_onchain", sessionId, txHash: tx.hash });
|
|
1224
|
+
}
|
|
1225
|
+
catch (onchainErr) {
|
|
1226
|
+
log({ event: "compute_end_onchain_error", sessionId, error: String(onchainErr) });
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1230
|
+
res.end(JSON.stringify({
|
|
1231
|
+
status: "completed",
|
|
1232
|
+
sessionId,
|
|
1233
|
+
consumedMinutes: r.consumedMinutes,
|
|
1234
|
+
costWei: r.costWei.toString(),
|
|
1235
|
+
refundWei: r.refundWei.toString(),
|
|
1236
|
+
reports: r.reports,
|
|
1237
|
+
}));
|
|
1238
|
+
}
|
|
1239
|
+
catch (err) {
|
|
1240
|
+
log({ event: "compute_end_error", error: String(err) });
|
|
1241
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1242
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1243
|
+
}
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
// POST /compute/dispute — client disputes a session
|
|
1247
|
+
if (pathname === "/compute/dispute" && req.method === "POST") {
|
|
1248
|
+
const body = await readBody(req, res);
|
|
1249
|
+
if (body === null)
|
|
1250
|
+
return;
|
|
1251
|
+
try {
|
|
1252
|
+
const msg = JSON.parse(body);
|
|
1253
|
+
const sessionId = String(msg.sessionId ?? "");
|
|
1254
|
+
if (!computeSessions) {
|
|
1255
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1256
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
const result = computeSessions.disputeSession(sessionId);
|
|
1260
|
+
if (!result.ok) {
|
|
1261
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1262
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
log({ event: "compute_disputed", sessionId });
|
|
1266
|
+
if (config.notifications.notify_on_dispute) {
|
|
1267
|
+
await notifier.notifyDispute(sessionId, String(msg.from ?? "client"));
|
|
1268
|
+
}
|
|
1269
|
+
// Wire to on-chain: call disputeSession
|
|
1270
|
+
if (config.compute.compute_agreement_address) {
|
|
1271
|
+
try {
|
|
1272
|
+
const caContract = new ethers_1.ethers.Contract(config.compute.compute_agreement_address, abis_1.COMPUTE_AGREEMENT_ABI, machineKeySigner);
|
|
1273
|
+
const tx = await caContract.disputeSession(sessionId);
|
|
1274
|
+
await tx.wait();
|
|
1275
|
+
log({ event: "compute_dispute_onchain", sessionId, txHash: tx.hash });
|
|
1276
|
+
}
|
|
1277
|
+
catch (onchainErr) {
|
|
1278
|
+
log({ event: "compute_dispute_onchain_error", sessionId, error: String(onchainErr) });
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1282
|
+
res.end(JSON.stringify({ status: "disputed", sessionId }));
|
|
1283
|
+
}
|
|
1284
|
+
catch (err) {
|
|
1285
|
+
log({ event: "compute_dispute_error", error: String(err) });
|
|
1286
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1287
|
+
res.end(JSON.stringify({ error: "invalid_request" }));
|
|
1288
|
+
}
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
// GET /compute/session/:id — session details
|
|
1292
|
+
if (pathname.startsWith("/compute/session/") && req.method === "GET") {
|
|
1293
|
+
const sessionId = pathname.slice("/compute/session/".length);
|
|
1294
|
+
if (!computeSessions) {
|
|
1295
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1296
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
const session = computeSessions.getSession(sessionId);
|
|
1300
|
+
if (!session) {
|
|
1301
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1302
|
+
res.end(JSON.stringify({ error: "session_not_found" }));
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const current = computeMetering ? computeMetering.getCurrentMetrics(sessionId) : null;
|
|
1306
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1307
|
+
res.end(JSON.stringify({ session, current }));
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
// GET /compute/sessions — list all sessions
|
|
1311
|
+
if (pathname === "/compute/sessions" && req.method === "GET") {
|
|
1312
|
+
if (!computeSessions) {
|
|
1313
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1314
|
+
res.end(JSON.stringify({ error: "compute_disabled" }));
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
const sessions = computeSessions.listSessions();
|
|
1318
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1319
|
+
res.end(JSON.stringify({ sessions, count: sessions.length }));
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
// ── File delivery routes ──────────────────────────────────────────────
|
|
1323
|
+
// POST /job/:id/upload — store a file (daemon auth required via global gate above)
|
|
1324
|
+
if (pathname.startsWith("/job/") && req.method === "POST") {
|
|
1325
|
+
const parts = pathname.split("/");
|
|
1326
|
+
// /job/<id>/upload → ["", "job", "<id>", "upload"]
|
|
1327
|
+
if (parts.length === 4 && parts[3] === "upload") {
|
|
1328
|
+
const agreementId = parts[2];
|
|
1329
|
+
if (!config.delivery.serve_files) {
|
|
1330
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1331
|
+
res.end(JSON.stringify({ error: "file_delivery_disabled" }));
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
await fileDelivery.handleUpload(req, res, agreementId, log);
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
// GET /job/:id/files, GET /job/:id/files/:name, GET /job/:id/manifest — party-gated
|
|
1339
|
+
if (pathname.startsWith("/job/") && req.method === "GET") {
|
|
1340
|
+
const parts = pathname.split("/");
|
|
1341
|
+
// /job/<id>/files/<name> → ["", "job", "<id>", "files", "<name>"]
|
|
1342
|
+
if (parts.length === 5 && parts[3] === "files") {
|
|
1343
|
+
const agreementId = parts[2];
|
|
1344
|
+
const filename = decodeURIComponent(parts[4]);
|
|
1345
|
+
if (!config.delivery.serve_files) {
|
|
1346
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1347
|
+
res.end(JSON.stringify({ error: "file_delivery_disabled" }));
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
fileDelivery.handleDownloadFile(req, res, agreementId, filename, apiToken, log);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
// /job/<id>/files → ["", "job", "<id>", "files"]
|
|
1354
|
+
if (parts.length === 4 && parts[3] === "files") {
|
|
1355
|
+
const agreementId = parts[2];
|
|
1356
|
+
if (!config.delivery.serve_files) {
|
|
1357
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1358
|
+
res.end(JSON.stringify({ error: "file_delivery_disabled" }));
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
fileDelivery.handleListFiles(req, res, agreementId, apiToken, log);
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
// /job/<id>/manifest → ["", "job", "<id>", "manifest"]
|
|
1365
|
+
if (parts.length === 4 && parts[3] === "manifest") {
|
|
1366
|
+
const agreementId = parts[2];
|
|
1367
|
+
if (!config.delivery.serve_files) {
|
|
1368
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1369
|
+
res.end(JSON.stringify({ error: "file_delivery_disabled" }));
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
fileDelivery.handleManifest(req, res, agreementId, apiToken, log);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
903
1376
|
// 404
|
|
904
1377
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
905
1378
|
res.end(JSON.stringify({ error: "not_found" }));
|
|
@@ -934,13 +1407,13 @@ export async function runDaemon(foreground = false) {
|
|
|
934
1407
|
// Close HTTP + IPC
|
|
935
1408
|
httpServer.close();
|
|
936
1409
|
ipcServer.close();
|
|
937
|
-
if (fs.existsSync(DAEMON_SOCK))
|
|
938
|
-
fs.unlinkSync(DAEMON_SOCK);
|
|
1410
|
+
if (fs.existsSync(config_1.DAEMON_SOCK))
|
|
1411
|
+
fs.unlinkSync(config_1.DAEMON_SOCK);
|
|
939
1412
|
await notifier.notifyStopped();
|
|
940
1413
|
log({ event: "daemon_stopped" });
|
|
941
1414
|
// Clean up PID file
|
|
942
|
-
if (!foreground && fs.existsSync(DAEMON_PID)) {
|
|
943
|
-
fs.unlinkSync(DAEMON_PID);
|
|
1415
|
+
if (!foreground && fs.existsSync(config_1.DAEMON_PID)) {
|
|
1416
|
+
fs.unlinkSync(config_1.DAEMON_PID);
|
|
944
1417
|
}
|
|
945
1418
|
db.close();
|
|
946
1419
|
process.exit(0);
|