arc402-cli 0.9.19 → 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.d.ts.map +1 -1
- package/dist/endpoint-notify.js +49 -15
- 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 -134
- 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
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ComputeMetering — GPU utilization tracking for ARC-402 compute sessions.
|
|
4
|
+
*
|
|
5
|
+
* - Polls nvidia-smi every N seconds (configurable; default 30)
|
|
6
|
+
* - Falls back to mock metrics when nvidia-smi is not available (testing)
|
|
7
|
+
* - Aggregates into 15-minute UsageReports signed with the provider key
|
|
8
|
+
* - Persists raw metrics to ~/.arc402/compute/<sessionId>/metrics.jsonl
|
|
9
|
+
* - Persists signed reports to ~/.arc402/compute/<sessionId>/reports/
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.ComputeMetering = void 0;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
const ethers_1 = require("ethers");
|
|
50
|
+
const config_1 = require("./config");
|
|
51
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
52
|
+
const COMPUTE_DATA_DIR = path.join(config_1.DAEMON_DIR, "compute");
|
|
53
|
+
const GPU_ACTIVE_THRESHOLD = 5; // utilization % above which we count compute-minutes
|
|
54
|
+
// ─── nvidia-smi poll ──────────────────────────────────────────────────────────
|
|
55
|
+
function pollNvidiaSmi() {
|
|
56
|
+
try {
|
|
57
|
+
const output = (0, child_process_1.execSync)("nvidia-smi --query-gpu=utilization.gpu,memory.used,temperature.gpu --format=csv,noheader,nounits", { timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
58
|
+
const parts = output.split(",").map(s => s.trim());
|
|
59
|
+
if (parts.length < 3)
|
|
60
|
+
return null;
|
|
61
|
+
const utilization = parseFloat(parts[0]);
|
|
62
|
+
const memoryMB = parseFloat(parts[1]);
|
|
63
|
+
const temperatureC = parseFloat(parts[2]);
|
|
64
|
+
if (isNaN(utilization) || isNaN(memoryMB) || isNaN(temperatureC))
|
|
65
|
+
return null;
|
|
66
|
+
return { utilization, memoryMB, temperatureC };
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function mockMetrics() {
|
|
73
|
+
// Deterministic-ish mock that oscillates for test realism
|
|
74
|
+
const t = Date.now() / 1000;
|
|
75
|
+
return {
|
|
76
|
+
utilization: Math.round(50 + 30 * Math.sin(t / 60)),
|
|
77
|
+
memoryMB: Math.round(4000 + 1000 * Math.cos(t / 90)),
|
|
78
|
+
temperatureC: Math.round(65 + 10 * Math.sin(t / 120)),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// ─── Report signing ───────────────────────────────────────────────────────────
|
|
82
|
+
/**
|
|
83
|
+
* Produce the same digest that ComputeAgreement.sol verifies.
|
|
84
|
+
* CA-IND-1: includes chainId and contractAddress to prevent cross-chain/cross-contract replay.
|
|
85
|
+
* structHash = keccak256(abi.encode(chainId, contractAddress, sessionId, periodStart,
|
|
86
|
+
* periodEnd, computeMinutes, avgUtilization, metricsHash))
|
|
87
|
+
* digest = EIP-191 personal_sign(structHash)
|
|
88
|
+
*/
|
|
89
|
+
function buildReportDigest(chainId, contractAddress, sessionId, periodStart, periodEnd, computeMinutes, avgUtilization, metricsHash) {
|
|
90
|
+
const encoded = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "address", "bytes32", "uint256", "uint256", "uint256", "uint256", "bytes32"], [
|
|
91
|
+
BigInt(chainId),
|
|
92
|
+
contractAddress,
|
|
93
|
+
sessionId,
|
|
94
|
+
BigInt(periodStart),
|
|
95
|
+
BigInt(periodEnd),
|
|
96
|
+
BigInt(computeMinutes),
|
|
97
|
+
BigInt(avgUtilization),
|
|
98
|
+
metricsHash,
|
|
99
|
+
]);
|
|
100
|
+
const structHash = ethers_1.ethers.keccak256(encoded);
|
|
101
|
+
// EIP-191 prefix — matches "\x19Ethereum Signed Message:\n32" + structHash
|
|
102
|
+
return ethers_1.ethers.hashMessage(ethers_1.ethers.getBytes(structHash));
|
|
103
|
+
}
|
|
104
|
+
async function signReport(wallet, chainId, contractAddress, sessionId, periodStart, periodEnd, computeMinutes, avgUtilization, metricsHash) {
|
|
105
|
+
const encoded = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "address", "bytes32", "uint256", "uint256", "uint256", "uint256", "bytes32"], [
|
|
106
|
+
BigInt(chainId),
|
|
107
|
+
contractAddress,
|
|
108
|
+
sessionId,
|
|
109
|
+
BigInt(periodStart),
|
|
110
|
+
BigInt(periodEnd),
|
|
111
|
+
BigInt(computeMinutes),
|
|
112
|
+
BigInt(avgUtilization),
|
|
113
|
+
metricsHash,
|
|
114
|
+
]);
|
|
115
|
+
const structHash = ethers_1.ethers.keccak256(encoded);
|
|
116
|
+
return wallet.signMessage(ethers_1.ethers.getBytes(structHash));
|
|
117
|
+
}
|
|
118
|
+
// ─── Main class ───────────────────────────────────────────────────────────────
|
|
119
|
+
const SUBMIT_USAGE_REPORT_ABI = [
|
|
120
|
+
"function submitUsageReport(bytes32 sessionId, uint256 periodStart, uint256 periodEnd, uint256 computeMinutes, uint256 avgUtilization, bytes providerSignature, bytes32 metricsHash) external",
|
|
121
|
+
];
|
|
122
|
+
class ComputeMetering {
|
|
123
|
+
constructor(providerPrivateKey, chainId, contractAddress, meteringIntervalSeconds = 30, reportIntervalMinutes = 15, onchainProvider) {
|
|
124
|
+
this.sessions = new Map();
|
|
125
|
+
this.wallet = new ethers_1.ethers.Wallet(providerPrivateKey);
|
|
126
|
+
this.chainId = chainId;
|
|
127
|
+
this.contractAddress = contractAddress;
|
|
128
|
+
this.meteringIntervalMs = meteringIntervalSeconds * 1000;
|
|
129
|
+
this.reportIntervalMs = reportIntervalMinutes * 60 * 1000;
|
|
130
|
+
this.onchainProvider = onchainProvider;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Start metering a new session.
|
|
134
|
+
* Polls nvidia-smi on the metering interval and aggregates 15-min reports.
|
|
135
|
+
*/
|
|
136
|
+
startMetering(sessionId) {
|
|
137
|
+
if (this.sessions.has(sessionId))
|
|
138
|
+
return;
|
|
139
|
+
const sessionDir = path.join(COMPUTE_DATA_DIR, sessionId);
|
|
140
|
+
const reportsDir = path.join(sessionDir, "reports");
|
|
141
|
+
fs.mkdirSync(reportsDir, { recursive: true, mode: 0o700 });
|
|
142
|
+
const state = {
|
|
143
|
+
sessionId,
|
|
144
|
+
startTime: Date.now(),
|
|
145
|
+
lastReportTime: Date.now(),
|
|
146
|
+
rawMetrics: [],
|
|
147
|
+
allReports: [],
|
|
148
|
+
metricsFilePath: path.join(sessionDir, "metrics.jsonl"),
|
|
149
|
+
reportsDir,
|
|
150
|
+
intervalHandle: null,
|
|
151
|
+
reportIntervalHandle: null,
|
|
152
|
+
};
|
|
153
|
+
// Metering poll
|
|
154
|
+
state.intervalHandle = setInterval(() => {
|
|
155
|
+
this._collectMetric(state);
|
|
156
|
+
}, this.meteringIntervalMs);
|
|
157
|
+
// Report generation
|
|
158
|
+
state.reportIntervalHandle = setInterval(() => {
|
|
159
|
+
void this._generateReport(state);
|
|
160
|
+
}, this.reportIntervalMs);
|
|
161
|
+
this.sessions.set(sessionId, state);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Stop metering and return the final usage report.
|
|
165
|
+
* Generates a final report covering remaining metrics since last report.
|
|
166
|
+
*/
|
|
167
|
+
async stopMetering(sessionId) {
|
|
168
|
+
const state = this.sessions.get(sessionId);
|
|
169
|
+
if (!state)
|
|
170
|
+
return null;
|
|
171
|
+
clearInterval(state.intervalHandle);
|
|
172
|
+
clearInterval(state.reportIntervalHandle);
|
|
173
|
+
// Final report from remaining buffered metrics
|
|
174
|
+
const finalReport = await this._generateReport(state);
|
|
175
|
+
this.sessions.delete(sessionId);
|
|
176
|
+
return finalReport;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Return the latest metrics snapshot for a running session.
|
|
180
|
+
*/
|
|
181
|
+
getCurrentMetrics(sessionId) {
|
|
182
|
+
const state = this.sessions.get(sessionId);
|
|
183
|
+
if (!state || state.rawMetrics.length === 0)
|
|
184
|
+
return null;
|
|
185
|
+
return state.rawMetrics[state.rawMetrics.length - 1];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Return all usage reports generated for a session.
|
|
189
|
+
*/
|
|
190
|
+
getUsageReports(sessionId) {
|
|
191
|
+
const state = this.sessions.get(sessionId);
|
|
192
|
+
if (!state) {
|
|
193
|
+
// Try to load persisted reports from disk
|
|
194
|
+
return this._loadReportsFromDisk(sessionId);
|
|
195
|
+
}
|
|
196
|
+
return [...state.allReports];
|
|
197
|
+
}
|
|
198
|
+
// ─── Private ────────────────────────────────────────────────────────────────
|
|
199
|
+
_collectMetric(state) {
|
|
200
|
+
const gpu = pollNvidiaSmi() ?? mockMetrics();
|
|
201
|
+
const now = Date.now();
|
|
202
|
+
const wallMs = now - state.startTime;
|
|
203
|
+
const wallMins = wallMs / 60000;
|
|
204
|
+
// Compute-minutes: count wall-clock intervals where utilization exceeded threshold
|
|
205
|
+
const active = gpu.utilization > GPU_ACTIVE_THRESHOLD;
|
|
206
|
+
const addComputeMins = active ? this.meteringIntervalMs / 60000 : 0;
|
|
207
|
+
// Accumulate into the current period buffer
|
|
208
|
+
const prevComputeMins = state.rawMetrics.length > 0
|
|
209
|
+
? state.rawMetrics[state.rawMetrics.length - 1].computeMinutes
|
|
210
|
+
: 0;
|
|
211
|
+
const metric = {
|
|
212
|
+
sessionId: state.sessionId,
|
|
213
|
+
gpuUtilizationPercent: gpu.utilization,
|
|
214
|
+
gpuMemoryUsedMB: gpu.memoryMB,
|
|
215
|
+
gpuTemperatureC: gpu.temperatureC,
|
|
216
|
+
activeMinutes: wallMins,
|
|
217
|
+
computeMinutes: prevComputeMins + addComputeMins,
|
|
218
|
+
timestamp: now,
|
|
219
|
+
};
|
|
220
|
+
state.rawMetrics.push(metric);
|
|
221
|
+
// Persist to JSONL
|
|
222
|
+
try {
|
|
223
|
+
fs.appendFileSync(state.metricsFilePath, JSON.stringify(metric) + "\n", { mode: 0o600 });
|
|
224
|
+
}
|
|
225
|
+
catch { /* non-fatal — disk full etc */ }
|
|
226
|
+
}
|
|
227
|
+
async _generateReport(state) {
|
|
228
|
+
if (state.rawMetrics.length === 0)
|
|
229
|
+
return null;
|
|
230
|
+
const periodStart = state.lastReportTime;
|
|
231
|
+
const periodEnd = Date.now();
|
|
232
|
+
// Metrics accumulated in this period
|
|
233
|
+
const periodMetrics = state.rawMetrics.filter(m => m.timestamp >= periodStart);
|
|
234
|
+
if (periodMetrics.length === 0)
|
|
235
|
+
return null;
|
|
236
|
+
const computeMinutes = periodMetrics.reduce((acc, m) => {
|
|
237
|
+
// Each poll interval contributes proportionally
|
|
238
|
+
const active = m.gpuUtilizationPercent > GPU_ACTIVE_THRESHOLD;
|
|
239
|
+
return acc + (active ? this.meteringIntervalMs / 60000 : 0);
|
|
240
|
+
}, 0);
|
|
241
|
+
const avgUtilization = Math.round(periodMetrics.reduce((acc, m) => acc + m.gpuUtilizationPercent, 0) / periodMetrics.length);
|
|
242
|
+
// Hash raw metrics for dispute evidence
|
|
243
|
+
const metricsJson = JSON.stringify(periodMetrics);
|
|
244
|
+
const metricsHash = ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(metricsJson));
|
|
245
|
+
const roundedMinutes = Math.round(computeMinutes);
|
|
246
|
+
const sig = await signReport(this.wallet, this.chainId, this.contractAddress, state.sessionId, Math.floor(periodStart / 1000), Math.floor(periodEnd / 1000), roundedMinutes, avgUtilization, metricsHash);
|
|
247
|
+
const report = {
|
|
248
|
+
sessionId: state.sessionId,
|
|
249
|
+
periodStart: Math.floor(periodStart / 1000),
|
|
250
|
+
periodEnd: Math.floor(periodEnd / 1000),
|
|
251
|
+
computeMinutes: roundedMinutes,
|
|
252
|
+
avgUtilization,
|
|
253
|
+
providerSignature: sig,
|
|
254
|
+
metricsHash,
|
|
255
|
+
};
|
|
256
|
+
state.allReports.push(report);
|
|
257
|
+
state.lastReportTime = periodEnd;
|
|
258
|
+
// Clear the processed period's metrics from the buffer
|
|
259
|
+
state.rawMetrics = state.rawMetrics.filter(m => m.timestamp >= periodEnd);
|
|
260
|
+
// Persist report to disk
|
|
261
|
+
const reportFile = path.join(state.reportsDir, `report_${report.periodStart}.json`);
|
|
262
|
+
try {
|
|
263
|
+
fs.writeFileSync(reportFile, JSON.stringify(report, null, 2), { mode: 0o600 });
|
|
264
|
+
}
|
|
265
|
+
catch { /* non-fatal */ }
|
|
266
|
+
// Submit on-chain if provider is configured
|
|
267
|
+
if (this.onchainProvider && this.contractAddress) {
|
|
268
|
+
void this._submitOnChain(report).catch(() => { });
|
|
269
|
+
}
|
|
270
|
+
return report;
|
|
271
|
+
}
|
|
272
|
+
async _submitOnChain(report) {
|
|
273
|
+
if (!this.onchainProvider)
|
|
274
|
+
return;
|
|
275
|
+
const signer = this.wallet.connect(this.onchainProvider);
|
|
276
|
+
const contract = new ethers_1.ethers.Contract(this.contractAddress, SUBMIT_USAGE_REPORT_ABI, signer);
|
|
277
|
+
const tx = await contract.submitUsageReport(report.sessionId, BigInt(report.periodStart), BigInt(report.periodEnd), BigInt(report.computeMinutes), BigInt(report.avgUtilization), report.providerSignature, report.metricsHash);
|
|
278
|
+
await tx.wait();
|
|
279
|
+
}
|
|
280
|
+
_loadReportsFromDisk(sessionId) {
|
|
281
|
+
const reportsDir = path.join(COMPUTE_DATA_DIR, sessionId, "reports");
|
|
282
|
+
if (!fs.existsSync(reportsDir))
|
|
283
|
+
return [];
|
|
284
|
+
try {
|
|
285
|
+
const files = fs.readdirSync(reportsDir)
|
|
286
|
+
.filter(f => f.endsWith(".json"))
|
|
287
|
+
.sort();
|
|
288
|
+
return files.map(f => {
|
|
289
|
+
const raw = fs.readFileSync(path.join(reportsDir, f), "utf-8");
|
|
290
|
+
return JSON.parse(raw);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.ComputeMetering = ComputeMetering;
|
|
299
|
+
//# sourceMappingURL=compute-metering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compute-metering.js","sourceRoot":"","sources":["../../src/daemon/compute-metering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComputeSessionManager — session lifecycle management for ARC-402 compute rental.
|
|
3
|
+
*
|
|
4
|
+
* Owns the in-memory + persisted state for all compute sessions handled by
|
|
5
|
+
* this provider node. Coordinates with ComputeMetering for GPU tracking.
|
|
6
|
+
*/
|
|
7
|
+
import { ComputeMetering, UsageReport } from "./compute-metering";
|
|
8
|
+
export type ComputeSessionStatus = "proposed" | "accepted" | "active" | "completed" | "disputed";
|
|
9
|
+
export interface ComputeProposal {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
clientAddress: string;
|
|
12
|
+
providerAddress: string;
|
|
13
|
+
ratePerHourWei: string;
|
|
14
|
+
maxHours: number;
|
|
15
|
+
gpuSpecHash: string;
|
|
16
|
+
workloadDescription: string;
|
|
17
|
+
depositAmount: string;
|
|
18
|
+
proposedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ComputeSessionState {
|
|
21
|
+
proposal: ComputeProposal;
|
|
22
|
+
status: ComputeSessionStatus;
|
|
23
|
+
startedAt: number | null;
|
|
24
|
+
endedAt: number | null;
|
|
25
|
+
accessEndpoint: string | null;
|
|
26
|
+
consumedMinutes: number;
|
|
27
|
+
usageReports: UsageReport[];
|
|
28
|
+
updatedAt: number;
|
|
29
|
+
}
|
|
30
|
+
export interface SettlementResult {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
consumedMinutes: number;
|
|
33
|
+
costWei: bigint;
|
|
34
|
+
depositWei: bigint;
|
|
35
|
+
refundWei: bigint;
|
|
36
|
+
reports: UsageReport[];
|
|
37
|
+
}
|
|
38
|
+
export declare class ComputeSessionManager {
|
|
39
|
+
sessions: Map<string, ComputeSessionState>;
|
|
40
|
+
private metering;
|
|
41
|
+
constructor(metering?: ComputeMetering);
|
|
42
|
+
/**
|
|
43
|
+
* Record an incoming compute proposal (from a client).
|
|
44
|
+
* Validates it doesn't conflict with capacity limits.
|
|
45
|
+
*/
|
|
46
|
+
handleProposal(proposal: ComputeProposal): {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Provider accepts the proposal. On-chain call should follow.
|
|
52
|
+
*/
|
|
53
|
+
acceptSession(sessionId: string): {
|
|
54
|
+
ok: boolean;
|
|
55
|
+
error?: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Provider marks the session as active. Starts GPU metering.
|
|
59
|
+
* @param accessEndpoint Optional SSH / API endpoint to surface to client.
|
|
60
|
+
*/
|
|
61
|
+
startSession(sessionId: string, accessEndpoint?: string): {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
error?: string;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Either party ends the session. Stops metering, calculates settlement.
|
|
67
|
+
*/
|
|
68
|
+
endSession(sessionId: string): Promise<{
|
|
69
|
+
ok: boolean;
|
|
70
|
+
error?: string;
|
|
71
|
+
result?: SettlementResult;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Client disputes the session — freezes it pending arbitration.
|
|
75
|
+
*/
|
|
76
|
+
disputeSession(sessionId: string): {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
error?: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Append a usage report (from daemon's periodic reporting loop).
|
|
82
|
+
*/
|
|
83
|
+
appendUsageReport(sessionId: string, report: UsageReport): {
|
|
84
|
+
ok: boolean;
|
|
85
|
+
error?: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Get a single session by ID.
|
|
89
|
+
*/
|
|
90
|
+
getSession(sessionId: string): ComputeSessionState | null;
|
|
91
|
+
/**
|
|
92
|
+
* List all sessions.
|
|
93
|
+
*/
|
|
94
|
+
listSessions(): ComputeSessionState[];
|
|
95
|
+
/**
|
|
96
|
+
* Count sessions in a given status.
|
|
97
|
+
*/
|
|
98
|
+
countByStatus(status: ComputeSessionStatus): number;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=compute-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compute-session.d.ts","sourceRoot":"","sources":["../../src/daemon/compute-session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAY,MAAM,CAAC;IAC5B,aAAa,EAAQ,MAAM,CAAC;IAC5B,eAAe,EAAM,MAAM,CAAC;IAC5B,cAAc,EAAO,MAAM,CAAC;IAC5B,QAAQ,EAAa,MAAM,CAAC;IAC5B,WAAW,EAAU,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAQ,MAAM,CAAC;IAC5B,UAAU,EAAW,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAS,eAAe,CAAC;IACjC,MAAM,EAAW,oBAAoB,CAAC;IACtC,SAAS,EAAQ,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAU,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAG,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAK,WAAW,EAAE,CAAC;IAC/B,SAAS,EAAQ,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAQ,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAU,MAAM,CAAC;IACxB,UAAU,EAAO,MAAM,CAAC;IACxB,SAAS,EAAQ,MAAM,CAAC;IACxB,OAAO,EAAU,WAAW,EAAE,CAAC;CAChC;AA8BD,qBAAa,qBAAqB;IACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAClD,OAAO,CAAC,QAAQ,CAAyB;gBAE7B,QAAQ,CAAC,EAAE,eAAe;IAKtC;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAqB1E;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAWjE;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAoBzF;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,gBAAgB,CAAA;KAAE,CAAC;IA8CxG;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAWlE;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAY1F;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI;IAIzD;;OAEG;IACH,YAAY,IAAI,mBAAmB,EAAE;IAIrC;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM;CAOpD"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ComputeSessionManager — session lifecycle management for ARC-402 compute rental.
|
|
4
|
+
*
|
|
5
|
+
* Owns the in-memory + persisted state for all compute sessions handled by
|
|
6
|
+
* this provider node. Coordinates with ComputeMetering for GPU tracking.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.ComputeSessionManager = void 0;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const config_1 = require("./config");
|
|
46
|
+
// ─── Persistence ──────────────────────────────────────────────────────────────
|
|
47
|
+
const SESSIONS_DIR = path.join(config_1.DAEMON_DIR, "compute");
|
|
48
|
+
function sessionsFile() {
|
|
49
|
+
return path.join(SESSIONS_DIR, "sessions.json");
|
|
50
|
+
}
|
|
51
|
+
function persistSessions(sessions) {
|
|
52
|
+
fs.mkdirSync(SESSIONS_DIR, { recursive: true, mode: 0o700 });
|
|
53
|
+
const obj = Object.fromEntries(sessions.entries());
|
|
54
|
+
fs.writeFileSync(sessionsFile(), JSON.stringify(obj, null, 2), { mode: 0o600 });
|
|
55
|
+
}
|
|
56
|
+
function loadSessions() {
|
|
57
|
+
const file = sessionsFile();
|
|
58
|
+
if (!fs.existsSync(file))
|
|
59
|
+
return new Map();
|
|
60
|
+
try {
|
|
61
|
+
const raw = fs.readFileSync(file, "utf-8");
|
|
62
|
+
const obj = JSON.parse(raw);
|
|
63
|
+
return new Map(Object.entries(obj));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return new Map();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ─── Manager class ────────────────────────────────────────────────────────────
|
|
70
|
+
class ComputeSessionManager {
|
|
71
|
+
constructor(metering) {
|
|
72
|
+
this.sessions = loadSessions();
|
|
73
|
+
this.metering = metering ?? null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Record an incoming compute proposal (from a client).
|
|
77
|
+
* Validates it doesn't conflict with capacity limits.
|
|
78
|
+
*/
|
|
79
|
+
handleProposal(proposal) {
|
|
80
|
+
if (this.sessions.has(proposal.sessionId)) {
|
|
81
|
+
return { ok: false, error: "session_already_exists" };
|
|
82
|
+
}
|
|
83
|
+
const state = {
|
|
84
|
+
proposal,
|
|
85
|
+
status: "proposed",
|
|
86
|
+
startedAt: null,
|
|
87
|
+
endedAt: null,
|
|
88
|
+
accessEndpoint: null,
|
|
89
|
+
consumedMinutes: 0,
|
|
90
|
+
usageReports: [],
|
|
91
|
+
updatedAt: Math.floor(Date.now() / 1000),
|
|
92
|
+
};
|
|
93
|
+
this.sessions.set(proposal.sessionId, state);
|
|
94
|
+
persistSessions(this.sessions);
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Provider accepts the proposal. On-chain call should follow.
|
|
99
|
+
*/
|
|
100
|
+
acceptSession(sessionId) {
|
|
101
|
+
const s = this.sessions.get(sessionId);
|
|
102
|
+
if (!s)
|
|
103
|
+
return { ok: false, error: "session_not_found" };
|
|
104
|
+
if (s.status !== "proposed")
|
|
105
|
+
return { ok: false, error: `wrong_status:${s.status}` };
|
|
106
|
+
s.status = "accepted";
|
|
107
|
+
s.updatedAt = Math.floor(Date.now() / 1000);
|
|
108
|
+
persistSessions(this.sessions);
|
|
109
|
+
return { ok: true };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Provider marks the session as active. Starts GPU metering.
|
|
113
|
+
* @param accessEndpoint Optional SSH / API endpoint to surface to client.
|
|
114
|
+
*/
|
|
115
|
+
startSession(sessionId, accessEndpoint) {
|
|
116
|
+
const s = this.sessions.get(sessionId);
|
|
117
|
+
if (!s)
|
|
118
|
+
return { ok: false, error: "session_not_found" };
|
|
119
|
+
if (s.status !== "accepted")
|
|
120
|
+
return { ok: false, error: `wrong_status:${s.status}` };
|
|
121
|
+
const now = Math.floor(Date.now() / 1000);
|
|
122
|
+
s.status = "active";
|
|
123
|
+
s.startedAt = now;
|
|
124
|
+
s.accessEndpoint = accessEndpoint ?? null;
|
|
125
|
+
s.updatedAt = now;
|
|
126
|
+
persistSessions(this.sessions);
|
|
127
|
+
if (this.metering) {
|
|
128
|
+
this.metering.startMetering(sessionId);
|
|
129
|
+
}
|
|
130
|
+
return { ok: true };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Either party ends the session. Stops metering, calculates settlement.
|
|
134
|
+
*/
|
|
135
|
+
async endSession(sessionId) {
|
|
136
|
+
const s = this.sessions.get(sessionId);
|
|
137
|
+
if (!s)
|
|
138
|
+
return { ok: false, error: "session_not_found" };
|
|
139
|
+
if (s.status !== "active")
|
|
140
|
+
return { ok: false, error: `wrong_status:${s.status}` };
|
|
141
|
+
// Stop metering — generates final usage report
|
|
142
|
+
if (this.metering) {
|
|
143
|
+
const finalReport = await this.metering.stopMetering(sessionId);
|
|
144
|
+
if (finalReport) {
|
|
145
|
+
s.usageReports.push(finalReport);
|
|
146
|
+
s.consumedMinutes += finalReport.computeMinutes;
|
|
147
|
+
}
|
|
148
|
+
// Absorb any other reports generated during the session
|
|
149
|
+
const allReports = this.metering.getUsageReports(sessionId);
|
|
150
|
+
for (const r of allReports) {
|
|
151
|
+
if (!s.usageReports.find(x => x.periodStart === r.periodStart)) {
|
|
152
|
+
s.usageReports.push(r);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const now = Math.floor(Date.now() / 1000);
|
|
157
|
+
s.status = "completed";
|
|
158
|
+
s.endedAt = now;
|
|
159
|
+
s.updatedAt = now;
|
|
160
|
+
persistSessions(this.sessions);
|
|
161
|
+
// Settlement math: (consumedMinutes * ratePerHour) / 60
|
|
162
|
+
const deposit = BigInt(s.proposal.depositAmount);
|
|
163
|
+
const rate = BigInt(s.proposal.ratePerHourWei);
|
|
164
|
+
const costWei = BigInt(s.consumedMinutes) * rate / 60n;
|
|
165
|
+
const clamped = costWei > deposit ? deposit : costWei;
|
|
166
|
+
const refundWei = deposit - clamped;
|
|
167
|
+
const result = {
|
|
168
|
+
sessionId,
|
|
169
|
+
consumedMinutes: s.consumedMinutes,
|
|
170
|
+
costWei: clamped,
|
|
171
|
+
depositWei: deposit,
|
|
172
|
+
refundWei,
|
|
173
|
+
reports: s.usageReports,
|
|
174
|
+
};
|
|
175
|
+
return { ok: true, result };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Client disputes the session — freezes it pending arbitration.
|
|
179
|
+
*/
|
|
180
|
+
disputeSession(sessionId) {
|
|
181
|
+
const s = this.sessions.get(sessionId);
|
|
182
|
+
if (!s)
|
|
183
|
+
return { ok: false, error: "session_not_found" };
|
|
184
|
+
if (s.status !== "active")
|
|
185
|
+
return { ok: false, error: `wrong_status:${s.status}` };
|
|
186
|
+
s.status = "disputed";
|
|
187
|
+
s.updatedAt = Math.floor(Date.now() / 1000);
|
|
188
|
+
persistSessions(this.sessions);
|
|
189
|
+
return { ok: true };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Append a usage report (from daemon's periodic reporting loop).
|
|
193
|
+
*/
|
|
194
|
+
appendUsageReport(sessionId, report) {
|
|
195
|
+
const s = this.sessions.get(sessionId);
|
|
196
|
+
if (!s)
|
|
197
|
+
return { ok: false, error: "session_not_found" };
|
|
198
|
+
if (s.status !== "active")
|
|
199
|
+
return { ok: false, error: `wrong_status:${s.status}` };
|
|
200
|
+
s.usageReports.push(report);
|
|
201
|
+
s.consumedMinutes += report.computeMinutes;
|
|
202
|
+
s.updatedAt = Math.floor(Date.now() / 1000);
|
|
203
|
+
persistSessions(this.sessions);
|
|
204
|
+
return { ok: true };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get a single session by ID.
|
|
208
|
+
*/
|
|
209
|
+
getSession(sessionId) {
|
|
210
|
+
return this.sessions.get(sessionId) ?? null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* List all sessions.
|
|
214
|
+
*/
|
|
215
|
+
listSessions() {
|
|
216
|
+
return Array.from(this.sessions.values());
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Count sessions in a given status.
|
|
220
|
+
*/
|
|
221
|
+
countByStatus(status) {
|
|
222
|
+
let n = 0;
|
|
223
|
+
for (const s of this.sessions.values()) {
|
|
224
|
+
if (s.status === status)
|
|
225
|
+
n++;
|
|
226
|
+
}
|
|
227
|
+
return n;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.ComputeSessionManager = ComputeSessionManager;
|
|
231
|
+
//# sourceMappingURL=compute-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compute-session.js","sourceRoot":"","sources":["../../src/daemon/compute-session.ts"],"names":[],"mappings":"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"}
|