@onklave/agent-cli 0.1.45 → 0.1.47
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/main.js +411 -210
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -743,6 +743,19 @@ var PlatformClient = class {
|
|
|
743
743
|
async listMachines() {
|
|
744
744
|
return this.request("GET", "/api/v1/runner/machines");
|
|
745
745
|
}
|
|
746
|
+
/*
|
|
747
|
+
* Worker Node Security 1b — fetch this runner's server-side policy
|
|
748
|
+
* (org-admin-defined allowed work paths). Device-token authed; the endpoint
|
|
749
|
+
* is public and reads x-device-token.
|
|
750
|
+
*/
|
|
751
|
+
async getRunnerPolicy(deviceToken) {
|
|
752
|
+
return this.request(
|
|
753
|
+
"GET",
|
|
754
|
+
"/api/v1/runner/policy",
|
|
755
|
+
void 0,
|
|
756
|
+
{ "x-device-token": deviceToken }
|
|
757
|
+
);
|
|
758
|
+
}
|
|
746
759
|
/*
|
|
747
760
|
* Check platform health.
|
|
748
761
|
*/
|
|
@@ -761,8 +774,8 @@ var PlatformClient = class {
|
|
|
761
774
|
/*
|
|
762
775
|
* Make an authenticated HTTP request to the platform.
|
|
763
776
|
*/
|
|
764
|
-
async request(method,
|
|
765
|
-
const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${
|
|
777
|
+
async request(method, path12, body, extraHeaders) {
|
|
778
|
+
const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${path12}`;
|
|
766
779
|
const headers = {
|
|
767
780
|
Authorization: `Bearer ${this.token}`,
|
|
768
781
|
"Content-Type": "application/json",
|
|
@@ -817,7 +830,7 @@ var CommsClient = class {
|
|
|
817
830
|
* (e.g. `assignment:claim-available`) to this specific runner.
|
|
818
831
|
*/
|
|
819
832
|
async connect(commsUrl, token, extra) {
|
|
820
|
-
return new Promise((
|
|
833
|
+
return new Promise((resolve4, reject) => {
|
|
821
834
|
const baseUrl = commsUrl.replace(/\/+$/, "");
|
|
822
835
|
this.socket = io(`${baseUrl}/agent-cli`, {
|
|
823
836
|
auth: {
|
|
@@ -842,7 +855,7 @@ var CommsClient = class {
|
|
|
842
855
|
this.socket.on("connect", () => {
|
|
843
856
|
clearTimeout(connectTimeout);
|
|
844
857
|
this.reconnectAttempts = 0;
|
|
845
|
-
|
|
858
|
+
resolve4();
|
|
846
859
|
});
|
|
847
860
|
this.socket.on("connect_error", (error) => {
|
|
848
861
|
this.reconnectAttempts++;
|
|
@@ -928,6 +941,15 @@ var CommsClient = class {
|
|
|
928
941
|
onAssignmentAvailable(handler) {
|
|
929
942
|
this.socket?.on("assignment:claim-available", handler);
|
|
930
943
|
}
|
|
944
|
+
/*
|
|
945
|
+
* Listen for daemon:upgrade requests pushed by an org admin from the
|
|
946
|
+
* portal. The daemon only acts on these when auto-upgrade is opted in
|
|
947
|
+
* (ONKLAVE_ALLOW_AUTO_UPGRADE) — see DaemonUpgradeService. Worker Node
|
|
948
|
+
* Security Phase 3.
|
|
949
|
+
*/
|
|
950
|
+
onUpgradeRequest(handler) {
|
|
951
|
+
this.socket?.on("daemon:upgrade", handler);
|
|
952
|
+
}
|
|
931
953
|
};
|
|
932
954
|
|
|
933
955
|
// _apps/@onklave/agent-cli/src/services/session-manager.ts
|
|
@@ -1018,16 +1040,16 @@ var SessionManager = class {
|
|
|
1018
1040
|
throw new Error(`No active session found with ID: ${sessionId}`);
|
|
1019
1041
|
}
|
|
1020
1042
|
session.process.kill("SIGTERM");
|
|
1021
|
-
await new Promise((
|
|
1043
|
+
await new Promise((resolve4) => {
|
|
1022
1044
|
const forceTimeout = setTimeout(() => {
|
|
1023
1045
|
if (session.process.killed === false) {
|
|
1024
1046
|
session.process.kill("SIGKILL");
|
|
1025
1047
|
}
|
|
1026
|
-
|
|
1048
|
+
resolve4();
|
|
1027
1049
|
}, 5e3);
|
|
1028
1050
|
session.process.on("exit", () => {
|
|
1029
1051
|
clearTimeout(forceTimeout);
|
|
1030
|
-
|
|
1052
|
+
resolve4();
|
|
1031
1053
|
});
|
|
1032
1054
|
});
|
|
1033
1055
|
this.activeSessions.delete(sessionId);
|
|
@@ -1227,7 +1249,50 @@ var AuditStreamer = class {
|
|
|
1227
1249
|
};
|
|
1228
1250
|
|
|
1229
1251
|
// _apps/@onklave/agent-cli/src/services/guardrail-enforcer.ts
|
|
1252
|
+
import * as fs3 from "fs";
|
|
1230
1253
|
import * as path3 from "path";
|
|
1254
|
+
function safeRealpath(target) {
|
|
1255
|
+
let current = path3.resolve(target);
|
|
1256
|
+
const tail = [];
|
|
1257
|
+
for (let i = 0; i < 4096; i++) {
|
|
1258
|
+
try {
|
|
1259
|
+
const real = fs3.realpathSync(current);
|
|
1260
|
+
return tail.length ? path3.join(real, ...tail.reverse()) : real;
|
|
1261
|
+
} catch {
|
|
1262
|
+
const parent = path3.dirname(current);
|
|
1263
|
+
if (parent === current) break;
|
|
1264
|
+
tail.push(path3.basename(current));
|
|
1265
|
+
current = parent;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return path3.resolve(target);
|
|
1269
|
+
}
|
|
1270
|
+
function isPathWithin(target, root) {
|
|
1271
|
+
const realRoot = safeRealpath(root);
|
|
1272
|
+
const realTarget = safeRealpath(target);
|
|
1273
|
+
if (realTarget === realRoot) return true;
|
|
1274
|
+
const rootWithSep = realRoot.endsWith(path3.sep) ? realRoot : realRoot + path3.sep;
|
|
1275
|
+
return realTarget.startsWith(rootWithSep);
|
|
1276
|
+
}
|
|
1277
|
+
function checkWorkspaceAccess(contextPath, allowedRoots) {
|
|
1278
|
+
const real = safeRealpath(contextPath);
|
|
1279
|
+
if (real === path3.parse(real).root) {
|
|
1280
|
+
return {
|
|
1281
|
+
allowed: false,
|
|
1282
|
+
reason: `Refusing to run with the filesystem root "${real}" as the workspace.`
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
if (allowedRoots.length > 0) {
|
|
1286
|
+
const within = allowedRoots.some((root) => isPathWithin(contextPath, root));
|
|
1287
|
+
if (!within) {
|
|
1288
|
+
return {
|
|
1289
|
+
allowed: false,
|
|
1290
|
+
reason: `Working directory "${contextPath}" is outside the allowed paths.`
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
return { allowed: true };
|
|
1295
|
+
}
|
|
1231
1296
|
var GuardrailEnforcer = class {
|
|
1232
1297
|
constructor(config) {
|
|
1233
1298
|
this.config = config;
|
|
@@ -1286,11 +1351,11 @@ var GuardrailEnforcer = class {
|
|
|
1286
1351
|
* Check if a file path is accessible for the given operation.
|
|
1287
1352
|
*/
|
|
1288
1353
|
checkPathAccess(filePath, operation) {
|
|
1289
|
-
const
|
|
1354
|
+
const realPath = safeRealpath(filePath);
|
|
1290
1355
|
if (operation === "write" || operation === "delete") {
|
|
1291
1356
|
if (this.config.writablePaths.length > 0) {
|
|
1292
1357
|
const isWritable = this.config.writablePaths.some(
|
|
1293
|
-
(wp) =>
|
|
1358
|
+
(wp) => isPathWithin(filePath, wp)
|
|
1294
1359
|
);
|
|
1295
1360
|
if (!isWritable) {
|
|
1296
1361
|
return {
|
|
@@ -1303,7 +1368,7 @@ var GuardrailEnforcer = class {
|
|
|
1303
1368
|
if (operation === "read") {
|
|
1304
1369
|
if (this.config.readablePaths.length > 0) {
|
|
1305
1370
|
const isReadable = this.config.readablePaths.some(
|
|
1306
|
-
(rp) =>
|
|
1371
|
+
(rp) => isPathWithin(filePath, rp)
|
|
1307
1372
|
);
|
|
1308
1373
|
if (!isReadable) {
|
|
1309
1374
|
return {
|
|
@@ -1322,9 +1387,10 @@ var GuardrailEnforcer = class {
|
|
|
1322
1387
|
"id_rsa",
|
|
1323
1388
|
"id_ed25519"
|
|
1324
1389
|
];
|
|
1325
|
-
const basename3 = path3.basename(
|
|
1390
|
+
const basename3 = path3.basename(realPath);
|
|
1391
|
+
const normForward = realPath.split(path3.sep).join("/");
|
|
1326
1392
|
for (const sensitive of sensitivePatterns) {
|
|
1327
|
-
if (basename3 === sensitive ||
|
|
1393
|
+
if (basename3 === sensitive || normForward.includes(`/${sensitive}`)) {
|
|
1328
1394
|
return {
|
|
1329
1395
|
allowed: false,
|
|
1330
1396
|
reason: `Access to sensitive path "${filePath}" is blocked by default`
|
|
@@ -1431,7 +1497,9 @@ var HeartbeatService = class {
|
|
|
1431
1497
|
machineId: this.opts.machineId,
|
|
1432
1498
|
status: "online",
|
|
1433
1499
|
activeSessionCount: this.opts.getActiveSessionCount(),
|
|
1434
|
-
resourceUsage: this.opts.getResourceUsage?.()
|
|
1500
|
+
resourceUsage: this.opts.getResourceUsage?.(),
|
|
1501
|
+
cliVersion: this.opts.cliVersion,
|
|
1502
|
+
posture: this.opts.posture
|
|
1435
1503
|
};
|
|
1436
1504
|
try {
|
|
1437
1505
|
const response = await fetch(
|
|
@@ -1460,6 +1528,141 @@ var HeartbeatService = class {
|
|
|
1460
1528
|
}
|
|
1461
1529
|
};
|
|
1462
1530
|
|
|
1531
|
+
// _apps/@onklave/agent-cli/src/services/cli-version.ts
|
|
1532
|
+
import * as fs4 from "node:fs";
|
|
1533
|
+
import * as path4 from "node:path";
|
|
1534
|
+
import { fileURLToPath } from "node:url";
|
|
1535
|
+
|
|
1536
|
+
// _apps/@onklave/agent-cli/src/services/daemon-update-checker.service.ts
|
|
1537
|
+
var REGISTRY_URL = "https://registry.npmjs.org";
|
|
1538
|
+
var PACKAGE_NAME = "@onklave/agent-cli";
|
|
1539
|
+
var DaemonUpdateChecker = class {
|
|
1540
|
+
constructor(opts) {
|
|
1541
|
+
this.latestVersion = null;
|
|
1542
|
+
this.lastCheckedAt = null;
|
|
1543
|
+
this.emittedForVersion = null;
|
|
1544
|
+
this.currentVersion = opts.currentVersion;
|
|
1545
|
+
this.audit = opts.audit;
|
|
1546
|
+
this.registryFetcher = opts.registryFetcher ?? defaultRegistryFetcher;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Hit the registry. On success, sets latestVersion and (if newer than
|
|
1550
|
+
* current) emits `daemon.update_available` — exactly once per version,
|
|
1551
|
+
* even across repeated checks. On failure, logs and returns; the next
|
|
1552
|
+
* tick can retry.
|
|
1553
|
+
*/
|
|
1554
|
+
async check() {
|
|
1555
|
+
try {
|
|
1556
|
+
const latest = await this.registryFetcher(PACKAGE_NAME);
|
|
1557
|
+
this.latestVersion = latest;
|
|
1558
|
+
this.lastCheckedAt = /* @__PURE__ */ new Date();
|
|
1559
|
+
if (isNewerVersion(latest, this.currentVersion) && this.emittedForVersion !== latest) {
|
|
1560
|
+
this.audit.record({
|
|
1561
|
+
sessionId: this.currentVersion,
|
|
1562
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
1563
|
+
action: DAEMON_AUDIT_ACTIONS.UPDATE_AVAILABLE,
|
|
1564
|
+
details: {
|
|
1565
|
+
currentVersion: this.currentVersion,
|
|
1566
|
+
latestVersion: latest
|
|
1567
|
+
},
|
|
1568
|
+
outcome: "success"
|
|
1569
|
+
});
|
|
1570
|
+
this.emittedForVersion = latest;
|
|
1571
|
+
}
|
|
1572
|
+
} catch (err) {
|
|
1573
|
+
console.warn(
|
|
1574
|
+
`[daemon] update check failed: ${err.message} (will retry)`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
info() {
|
|
1579
|
+
return {
|
|
1580
|
+
currentVersion: this.currentVersion,
|
|
1581
|
+
latestVersion: this.latestVersion,
|
|
1582
|
+
updateAvailable: !!this.latestVersion && isNewerVersion(this.latestVersion, this.currentVersion),
|
|
1583
|
+
checkedAt: this.lastCheckedAt?.toISOString() ?? null
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
async function defaultRegistryFetcher(packageName) {
|
|
1588
|
+
const url = `${REGISTRY_URL}/${encodeURIComponent(packageName)}/latest`;
|
|
1589
|
+
const response = await fetch(url, {
|
|
1590
|
+
method: "GET",
|
|
1591
|
+
headers: { Accept: "application/json" },
|
|
1592
|
+
signal: AbortSignal.timeout(1e4)
|
|
1593
|
+
});
|
|
1594
|
+
if (!response.ok) {
|
|
1595
|
+
throw new Error(`registry ${response.status} ${response.statusText}`);
|
|
1596
|
+
}
|
|
1597
|
+
const body = await response.json();
|
|
1598
|
+
if (!body.version) throw new Error("registry response missing version");
|
|
1599
|
+
return body.version;
|
|
1600
|
+
}
|
|
1601
|
+
function isNewerVersion(a, b) {
|
|
1602
|
+
const pa = parseSemverNumeric(a);
|
|
1603
|
+
const pb = parseSemverNumeric(b);
|
|
1604
|
+
if (!pa || !pb) return false;
|
|
1605
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1606
|
+
if (pa[i] > pb[i]) return true;
|
|
1607
|
+
if (pa[i] < pb[i]) return false;
|
|
1608
|
+
}
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
function parseSemverNumeric(v) {
|
|
1612
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
1613
|
+
if (!match) return null;
|
|
1614
|
+
return [
|
|
1615
|
+
Number.parseInt(match[1], 10),
|
|
1616
|
+
Number.parseInt(match[2], 10),
|
|
1617
|
+
Number.parseInt(match[3], 10)
|
|
1618
|
+
];
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// _apps/@onklave/agent-cli/src/services/cli-version.ts
|
|
1622
|
+
function readPackageVersion() {
|
|
1623
|
+
try {
|
|
1624
|
+
const moduleDir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1625
|
+
const candidates = [
|
|
1626
|
+
path4.join(moduleDir, "package.json"),
|
|
1627
|
+
// bundled: dist root
|
|
1628
|
+
path4.join(moduleDir, "..", "package.json"),
|
|
1629
|
+
path4.join(moduleDir, "..", "..", "package.json"),
|
|
1630
|
+
// dev: src/services -> root
|
|
1631
|
+
path4.join(moduleDir, "..", "..", "..", "package.json")
|
|
1632
|
+
];
|
|
1633
|
+
for (const p of candidates) {
|
|
1634
|
+
if (!fs4.existsSync(p)) continue;
|
|
1635
|
+
const pkg = JSON.parse(fs4.readFileSync(p, "utf8"));
|
|
1636
|
+
if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
|
|
1637
|
+
}
|
|
1638
|
+
} catch {
|
|
1639
|
+
}
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
function getCurrentVersion() {
|
|
1643
|
+
return readPackageVersion() ?? "0.0.0";
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// _apps/@onklave/agent-cli/src/services/posture.ts
|
|
1647
|
+
import * as fs5 from "fs";
|
|
1648
|
+
import * as os3 from "os";
|
|
1649
|
+
function collectPosture() {
|
|
1650
|
+
let dockerized = false;
|
|
1651
|
+
try {
|
|
1652
|
+
dockerized = fs5.existsSync("/.dockerenv") || !!process.env["ONKLAVE_RUNNER_DOCKERIZED"];
|
|
1653
|
+
} catch {
|
|
1654
|
+
}
|
|
1655
|
+
let runningAsRoot = false;
|
|
1656
|
+
try {
|
|
1657
|
+
runningAsRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
1658
|
+
} catch {
|
|
1659
|
+
}
|
|
1660
|
+
return { osVersion: os3.release(), dockerized, runningAsRoot };
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// _apps/@onklave/agent-cli/src/commands/run.command.ts
|
|
1664
|
+
import * as path5 from "path";
|
|
1665
|
+
|
|
1463
1666
|
// _apps/@onklave/agent-cli/src/tui/render.tsx
|
|
1464
1667
|
import { render } from "ink";
|
|
1465
1668
|
|
|
@@ -2046,6 +2249,27 @@ async function runCommand(args) {
|
|
|
2046
2249
|
resolvedConfig.platformUrl,
|
|
2047
2250
|
creds.token
|
|
2048
2251
|
);
|
|
2252
|
+
let serverAllowedPaths = [];
|
|
2253
|
+
if (creds.machineId && creds.deviceToken) {
|
|
2254
|
+
try {
|
|
2255
|
+
const policy = await platformClient.getRunnerPolicy(creds.deviceToken);
|
|
2256
|
+
serverAllowedPaths = policy.allowedWorkPaths ?? [];
|
|
2257
|
+
} catch (err) {
|
|
2258
|
+
console.warn(
|
|
2259
|
+
`Warning: could not fetch runner policy: ${err.message}`
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
const effectiveAllowedRoots = serverAllowedPaths.length ? serverAllowedPaths : [...resolvedConfig.writablePaths, ...resolvedConfig.readablePaths];
|
|
2264
|
+
const workspaceVerdict = checkWorkspaceAccess(
|
|
2265
|
+
resolvedConfig.context,
|
|
2266
|
+
effectiveAllowedRoots
|
|
2267
|
+
);
|
|
2268
|
+
if (!workspaceVerdict.allowed) {
|
|
2269
|
+
console.error(`Error: ${workspaceVerdict.reason}`);
|
|
2270
|
+
process.exitCode = 1;
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2049
2273
|
const sessionManager = new SessionManager();
|
|
2050
2274
|
let heartbeat = null;
|
|
2051
2275
|
if (creds.machineId && creds.deviceToken) {
|
|
@@ -2053,7 +2277,9 @@ async function runCommand(args) {
|
|
|
2053
2277
|
platformUrl: resolvedConfig.platformUrl,
|
|
2054
2278
|
deviceToken: creds.deviceToken,
|
|
2055
2279
|
machineId: creds.machineId,
|
|
2056
|
-
getActiveSessionCount: () => sessionManager.getActiveSessionIds().length
|
|
2280
|
+
getActiveSessionCount: () => sessionManager.getActiveSessionIds().length,
|
|
2281
|
+
cliVersion: getCurrentVersion(),
|
|
2282
|
+
posture: collectPosture()
|
|
2057
2283
|
});
|
|
2058
2284
|
heartbeat.start();
|
|
2059
2285
|
const stopHeartbeat = () => heartbeat?.stop();
|
|
@@ -2135,8 +2361,8 @@ async function runCommand(args) {
|
|
|
2135
2361
|
}
|
|
2136
2362
|
const addDirs = [
|
|
2137
2363
|
.../* @__PURE__ */ new Set([
|
|
2138
|
-
|
|
2139
|
-
...
|
|
2364
|
+
path5.resolve(resolvedConfig.context),
|
|
2365
|
+
...effectiveAllowedRoots
|
|
2140
2366
|
])
|
|
2141
2367
|
];
|
|
2142
2368
|
const sessionConfig = {
|
|
@@ -2249,15 +2475,15 @@ async function runCommand(args) {
|
|
|
2249
2475
|
}
|
|
2250
2476
|
}
|
|
2251
2477
|
});
|
|
2252
|
-
await new Promise((
|
|
2478
|
+
await new Promise((resolve4) => {
|
|
2253
2479
|
const checkInterval = setInterval(() => {
|
|
2254
2480
|
if (!sessionManager.isSessionActive(sessionId)) {
|
|
2255
2481
|
clearInterval(checkInterval);
|
|
2256
|
-
|
|
2482
|
+
resolve4();
|
|
2257
2483
|
}
|
|
2258
2484
|
}, 500);
|
|
2259
2485
|
});
|
|
2260
|
-
await new Promise((
|
|
2486
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
2261
2487
|
tuiInstance.unmount();
|
|
2262
2488
|
} catch (err) {
|
|
2263
2489
|
tuiInstance.unmount();
|
|
@@ -2363,11 +2589,11 @@ Session timed out after ${resolvedConfig.timeout} seconds.`
|
|
|
2363
2589
|
commsClient.disconnect();
|
|
2364
2590
|
}
|
|
2365
2591
|
});
|
|
2366
|
-
await new Promise((
|
|
2592
|
+
await new Promise((resolve4) => {
|
|
2367
2593
|
const checkInterval = setInterval(() => {
|
|
2368
2594
|
if (!sessionManager.isSessionActive(sessionId)) {
|
|
2369
2595
|
clearInterval(checkInterval);
|
|
2370
|
-
|
|
2596
|
+
resolve4();
|
|
2371
2597
|
}
|
|
2372
2598
|
}, 500);
|
|
2373
2599
|
});
|
|
@@ -2598,9 +2824,35 @@ async function denyCommand(args) {
|
|
|
2598
2824
|
}
|
|
2599
2825
|
|
|
2600
2826
|
// _apps/@onklave/agent-cli/src/commands/doctor.command.ts
|
|
2601
|
-
import * as
|
|
2602
|
-
import * as
|
|
2827
|
+
import * as fs6 from "fs";
|
|
2828
|
+
import * as path6 from "path";
|
|
2829
|
+
|
|
2830
|
+
// _apps/@onklave/agent-cli/src/services/command-exists.ts
|
|
2603
2831
|
import { execSync } from "child_process";
|
|
2832
|
+
function lookupCommand(command) {
|
|
2833
|
+
return process.platform === "win32" ? `where ${command}` : `which ${command}`;
|
|
2834
|
+
}
|
|
2835
|
+
function commandExists(command) {
|
|
2836
|
+
try {
|
|
2837
|
+
execSync(lookupCommand(command), { stdio: "ignore", timeout: 3e3 });
|
|
2838
|
+
return true;
|
|
2839
|
+
} catch {
|
|
2840
|
+
return false;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
function commandPath(command) {
|
|
2844
|
+
try {
|
|
2845
|
+
const out = execSync(lookupCommand(command), {
|
|
2846
|
+
encoding: "utf-8",
|
|
2847
|
+
timeout: 5e3
|
|
2848
|
+
}).trim();
|
|
2849
|
+
return out.split(/\r?\n/)[0] || null;
|
|
2850
|
+
} catch {
|
|
2851
|
+
return null;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
// _apps/@onklave/agent-cli/src/commands/doctor.command.ts
|
|
2604
2856
|
async function doctorCommand() {
|
|
2605
2857
|
console.log("Onklave Agent CLI \u2014 Doctor\n");
|
|
2606
2858
|
console.log("Running diagnostics...\n");
|
|
@@ -2669,23 +2921,19 @@ async function checkPlatform() {
|
|
|
2669
2921
|
};
|
|
2670
2922
|
}
|
|
2671
2923
|
function checkClaudeCli() {
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
encoding: "utf-8",
|
|
2675
|
-
timeout: 5e3
|
|
2676
|
-
}).trim();
|
|
2924
|
+
const claudePath = commandPath("claude");
|
|
2925
|
+
if (claudePath) {
|
|
2677
2926
|
return {
|
|
2678
2927
|
name: "Claude Code CLI",
|
|
2679
2928
|
status: "ok",
|
|
2680
2929
|
detail: `Found at ${claudePath}`
|
|
2681
2930
|
};
|
|
2682
|
-
} catch {
|
|
2683
|
-
return {
|
|
2684
|
-
name: "Claude Code CLI",
|
|
2685
|
-
status: "fail",
|
|
2686
|
-
detail: "Not found. Install Claude Code CLI: https://docs.anthropic.com/claude-code"
|
|
2687
|
-
};
|
|
2688
2931
|
}
|
|
2932
|
+
return {
|
|
2933
|
+
name: "Claude Code CLI",
|
|
2934
|
+
status: "fail",
|
|
2935
|
+
detail: "Not found. Install Claude Code CLI: https://docs.anthropic.com/claude-code"
|
|
2936
|
+
};
|
|
2689
2937
|
}
|
|
2690
2938
|
function checkNodeVersion() {
|
|
2691
2939
|
const version = process.version;
|
|
@@ -2707,10 +2955,10 @@ function checkNodeVersion() {
|
|
|
2707
2955
|
};
|
|
2708
2956
|
}
|
|
2709
2957
|
function checkProjectConfig() {
|
|
2710
|
-
const configPath =
|
|
2711
|
-
if (
|
|
2958
|
+
const configPath = path6.join(process.cwd(), ".onklave.json");
|
|
2959
|
+
if (fs6.existsSync(configPath)) {
|
|
2712
2960
|
try {
|
|
2713
|
-
const raw =
|
|
2961
|
+
const raw = fs6.readFileSync(configPath, "utf-8");
|
|
2714
2962
|
JSON.parse(raw);
|
|
2715
2963
|
return {
|
|
2716
2964
|
name: "Project config",
|
|
@@ -2760,18 +3008,18 @@ async function checkWebSocket() {
|
|
|
2760
3008
|
}
|
|
2761
3009
|
|
|
2762
3010
|
// _apps/@onklave/agent-cli/src/commands/init.command.ts
|
|
2763
|
-
import * as
|
|
2764
|
-
import * as
|
|
3011
|
+
import * as fs7 from "fs";
|
|
3012
|
+
import * as path7 from "path";
|
|
2765
3013
|
async function initCommand() {
|
|
2766
|
-
const configPath =
|
|
2767
|
-
if (
|
|
3014
|
+
const configPath = path7.join(process.cwd(), ".onklave.json");
|
|
3015
|
+
if (fs7.existsSync(configPath)) {
|
|
2768
3016
|
console.error("Error: .onklave.json already exists in this directory.");
|
|
2769
3017
|
console.log("To overwrite, delete the existing file first.");
|
|
2770
3018
|
process.exitCode = 1;
|
|
2771
3019
|
return;
|
|
2772
3020
|
}
|
|
2773
3021
|
const template = {
|
|
2774
|
-
project:
|
|
3022
|
+
project: path7.basename(process.cwd()),
|
|
2775
3023
|
org: "",
|
|
2776
3024
|
description: "",
|
|
2777
3025
|
defaults: {
|
|
@@ -2806,7 +3054,7 @@ async function initCommand() {
|
|
|
2806
3054
|
system_prompt_append: ""
|
|
2807
3055
|
}
|
|
2808
3056
|
};
|
|
2809
|
-
|
|
3057
|
+
fs7.writeFileSync(
|
|
2810
3058
|
configPath,
|
|
2811
3059
|
JSON.stringify(template, null, 2) + "\n",
|
|
2812
3060
|
"utf-8"
|
|
@@ -2893,7 +3141,7 @@ async function configCommand(args) {
|
|
|
2893
3141
|
}
|
|
2894
3142
|
|
|
2895
3143
|
// _apps/@onklave/agent-cli/src/commands/register.command.ts
|
|
2896
|
-
import * as
|
|
3144
|
+
import * as os4 from "os";
|
|
2897
3145
|
async function registerCommand(args) {
|
|
2898
3146
|
const { flags } = parseArgs(args);
|
|
2899
3147
|
const refresh = flags["refresh"] === true;
|
|
@@ -2940,11 +3188,13 @@ Machine ${creds.machineId} updated successfully.`);
|
|
|
2940
3188
|
return;
|
|
2941
3189
|
}
|
|
2942
3190
|
const metadata = {
|
|
2943
|
-
hostname:
|
|
2944
|
-
os: `${
|
|
2945
|
-
arch:
|
|
3191
|
+
hostname: os4.hostname(),
|
|
3192
|
+
os: `${os4.platform()} ${os4.release()}`,
|
|
3193
|
+
arch: os4.arch(),
|
|
2946
3194
|
nodeVersion: process.version,
|
|
2947
|
-
|
|
3195
|
+
cliVersion: getCurrentVersion(),
|
|
3196
|
+
capabilities: detectCapabilities(),
|
|
3197
|
+
posture: collectPosture()
|
|
2948
3198
|
};
|
|
2949
3199
|
try {
|
|
2950
3200
|
console.log("Registering machine with Onklave platform...");
|
|
@@ -2982,17 +3232,11 @@ This machine can now receive remote agent session requests.`
|
|
|
2982
3232
|
}
|
|
2983
3233
|
function detectCapabilities() {
|
|
2984
3234
|
const capabilities = ["agent-runner"];
|
|
2985
|
-
|
|
2986
|
-
const { execSync: execSync2 } = __require("child_process");
|
|
2987
|
-
execSync2("which claude", { timeout: 3e3, stdio: "pipe" });
|
|
3235
|
+
if (commandExists("claude")) {
|
|
2988
3236
|
capabilities.push("claude-code");
|
|
2989
|
-
} catch {
|
|
2990
3237
|
}
|
|
2991
|
-
|
|
2992
|
-
const { execSync: execSync2 } = __require("child_process");
|
|
2993
|
-
execSync2("which docker", { timeout: 3e3, stdio: "pipe" });
|
|
3238
|
+
if (commandExists("docker")) {
|
|
2994
3239
|
capabilities.push("docker");
|
|
2995
|
-
} catch {
|
|
2996
3240
|
}
|
|
2997
3241
|
return capabilities;
|
|
2998
3242
|
}
|
|
@@ -3035,7 +3279,8 @@ async function logsCommand(args) {
|
|
|
3035
3279
|
}
|
|
3036
3280
|
|
|
3037
3281
|
// _apps/@onklave/agent-cli/src/commands/daemon.command.ts
|
|
3038
|
-
import * as
|
|
3282
|
+
import * as fs10 from "fs";
|
|
3283
|
+
import * as path10 from "path";
|
|
3039
3284
|
|
|
3040
3285
|
// _apps/@onklave/agent-cli/src/services/daemon-comms.service.ts
|
|
3041
3286
|
var DaemonCommsService = class {
|
|
@@ -3109,7 +3354,7 @@ var DaemonCommsService = class {
|
|
|
3109
3354
|
}
|
|
3110
3355
|
};
|
|
3111
3356
|
function defaultSleep(ms) {
|
|
3112
|
-
return new Promise((
|
|
3357
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
3113
3358
|
}
|
|
3114
3359
|
|
|
3115
3360
|
// _apps/@onklave/agent-cli/src/services/daemon-claim-handler.service.ts
|
|
@@ -3462,8 +3707,8 @@ var PlatformBrokerClient = class {
|
|
|
3462
3707
|
params
|
|
3463
3708
|
);
|
|
3464
3709
|
}
|
|
3465
|
-
async request(method,
|
|
3466
|
-
const url = `${this.baseUrl}/agent-orchestration${
|
|
3710
|
+
async request(method, path12, body) {
|
|
3711
|
+
const url = `${this.baseUrl}/agent-orchestration${path12}`;
|
|
3467
3712
|
const response = await fetch(url, {
|
|
3468
3713
|
method,
|
|
3469
3714
|
headers: {
|
|
@@ -3499,9 +3744,9 @@ var PlatformBrokerClient = class {
|
|
|
3499
3744
|
|
|
3500
3745
|
// _apps/@onklave/agent-cli/src/services/work-item-runner.service.ts
|
|
3501
3746
|
import { spawn as spawn2 } from "child_process";
|
|
3502
|
-
import * as
|
|
3503
|
-
import * as
|
|
3504
|
-
import * as
|
|
3747
|
+
import * as fs8 from "fs";
|
|
3748
|
+
import * as os5 from "os";
|
|
3749
|
+
import * as path8 from "path";
|
|
3505
3750
|
var WorkItemRunner = class {
|
|
3506
3751
|
constructor(opts = {}) {
|
|
3507
3752
|
this.sessionManager = opts.sessionManager ?? new SessionManager();
|
|
@@ -3522,10 +3767,10 @@ var WorkItemRunner = class {
|
|
|
3522
3767
|
summary: `Work item ${workItem.id} has no clonable repo (project.repos[0].url missing).`
|
|
3523
3768
|
};
|
|
3524
3769
|
}
|
|
3525
|
-
const workDir =
|
|
3526
|
-
|
|
3770
|
+
const workDir = fs8.mkdtempSync(
|
|
3771
|
+
path8.join(os5.tmpdir(), `onklave-pickup-${sessionId}-`)
|
|
3527
3772
|
);
|
|
3528
|
-
const repoDir =
|
|
3773
|
+
const repoDir = path8.join(workDir, sanitizeName(repo.name) || "repo");
|
|
3529
3774
|
const branchName = `onklave/work-item/${workItem.id}`;
|
|
3530
3775
|
try {
|
|
3531
3776
|
await this.git(["clone", repo.url, repoDir], workDir);
|
|
@@ -3577,7 +3822,7 @@ var WorkItemRunner = class {
|
|
|
3577
3822
|
systemPromptAppend: null
|
|
3578
3823
|
};
|
|
3579
3824
|
let output = "";
|
|
3580
|
-
const exitCode = await new Promise((
|
|
3825
|
+
const exitCode = await new Promise((resolve4) => {
|
|
3581
3826
|
const timeout = setTimeout(() => {
|
|
3582
3827
|
void this.sessionManager.stopSession(sessionId).catch(() => void 0);
|
|
3583
3828
|
}, this.timeoutSeconds * 1e3);
|
|
@@ -3590,7 +3835,7 @@ var WorkItemRunner = class {
|
|
|
3590
3835
|
},
|
|
3591
3836
|
onExit: (code) => {
|
|
3592
3837
|
clearTimeout(timeout);
|
|
3593
|
-
|
|
3838
|
+
resolve4(code);
|
|
3594
3839
|
}
|
|
3595
3840
|
});
|
|
3596
3841
|
});
|
|
@@ -3623,12 +3868,12 @@ function sanitizeName(name) {
|
|
|
3623
3868
|
}
|
|
3624
3869
|
function cleanup(dir) {
|
|
3625
3870
|
try {
|
|
3626
|
-
|
|
3871
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
3627
3872
|
} catch {
|
|
3628
3873
|
}
|
|
3629
3874
|
}
|
|
3630
3875
|
function defaultGit(args, cwd) {
|
|
3631
|
-
return new Promise((
|
|
3876
|
+
return new Promise((resolve4, reject) => {
|
|
3632
3877
|
const child = spawn2("git", args, {
|
|
3633
3878
|
cwd,
|
|
3634
3879
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3640,7 +3885,7 @@ function defaultGit(args, cwd) {
|
|
|
3640
3885
|
child.on("error", (err) => reject(err));
|
|
3641
3886
|
child.on("exit", (code) => {
|
|
3642
3887
|
if (code === 0) {
|
|
3643
|
-
|
|
3888
|
+
resolve4();
|
|
3644
3889
|
} else {
|
|
3645
3890
|
reject(new Error(`git ${args[0]} exited ${code}: ${stderr.trim()}`));
|
|
3646
3891
|
}
|
|
@@ -3649,7 +3894,7 @@ function defaultGit(args, cwd) {
|
|
|
3649
3894
|
}
|
|
3650
3895
|
|
|
3651
3896
|
// _apps/@onklave/agent-cli/src/services/daemon-resource-sampler.service.ts
|
|
3652
|
-
import * as
|
|
3897
|
+
import * as os6 from "os";
|
|
3653
3898
|
var DEFAULT_SAMPLE_INTERVAL_MS = 5e3;
|
|
3654
3899
|
var DEFAULT_WINDOW_MS = 6e4;
|
|
3655
3900
|
var DaemonResourceSampler = class {
|
|
@@ -3717,8 +3962,8 @@ var DaemonResourceSampler = class {
|
|
|
3717
3962
|
}
|
|
3718
3963
|
};
|
|
3719
3964
|
function defaultCpuPercent() {
|
|
3720
|
-
const cpus2 =
|
|
3721
|
-
const load1m =
|
|
3965
|
+
const cpus2 = os6.cpus().length || 1;
|
|
3966
|
+
const load1m = os6.loadavg()[0];
|
|
3722
3967
|
return load1m / cpus2 * 100;
|
|
3723
3968
|
}
|
|
3724
3969
|
function defaultMemoryRss() {
|
|
@@ -3749,7 +3994,7 @@ var DaemonSpawner = class {
|
|
|
3749
3994
|
orgId: req.orgId,
|
|
3750
3995
|
systemPromptAppend: null
|
|
3751
3996
|
};
|
|
3752
|
-
return new Promise((
|
|
3997
|
+
return new Promise((resolve4, reject) => {
|
|
3753
3998
|
void this.sessionManager.spawnSession(req.sessionId, config, {
|
|
3754
3999
|
onStdout: (data) => {
|
|
3755
4000
|
process.stdout.write(data);
|
|
@@ -3759,7 +4004,7 @@ var DaemonSpawner = class {
|
|
|
3759
4004
|
},
|
|
3760
4005
|
onExit: (code, signal) => {
|
|
3761
4006
|
if (code === 0) {
|
|
3762
|
-
|
|
4007
|
+
resolve4();
|
|
3763
4008
|
} else {
|
|
3764
4009
|
reject(
|
|
3765
4010
|
new Error(
|
|
@@ -3910,123 +4155,48 @@ var DaemonTokenRefresher = class {
|
|
|
3910
4155
|
}
|
|
3911
4156
|
};
|
|
3912
4157
|
|
|
3913
|
-
// _apps/@onklave/agent-cli/src/services/daemon-
|
|
3914
|
-
var
|
|
3915
|
-
var PACKAGE_NAME = "@onklave/agent-cli";
|
|
3916
|
-
var DaemonUpdateChecker = class {
|
|
4158
|
+
// _apps/@onklave/agent-cli/src/services/daemon-upgrade.service.ts
|
|
4159
|
+
var DaemonUpgradeService = class {
|
|
3917
4160
|
constructor(opts) {
|
|
3918
|
-
this.
|
|
3919
|
-
this.lastCheckedAt = null;
|
|
3920
|
-
this.emittedForVersion = null;
|
|
3921
|
-
this.currentVersion = opts.currentVersion;
|
|
3922
|
-
this.audit = opts.audit;
|
|
3923
|
-
this.registryFetcher = opts.registryFetcher ?? defaultRegistryFetcher;
|
|
4161
|
+
this.opts = opts;
|
|
3924
4162
|
}
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
4163
|
+
async handle() {
|
|
4164
|
+
const { allowAutoUpgrade, currentVersion } = this.opts;
|
|
4165
|
+
if (!allowAutoUpgrade) {
|
|
4166
|
+
this.opts.audit("upgrade_requested_denied", {
|
|
4167
|
+
reason: "auto_upgrade_disabled",
|
|
4168
|
+
currentVersion
|
|
4169
|
+
});
|
|
4170
|
+
this.opts.log?.(
|
|
4171
|
+
"[daemon] upgrade requested, but auto-upgrade is disabled (set ONKLAVE_ALLOW_AUTO_UPGRADE=1 to opt in). Upgrade manually: npm install -g @onklave/agent-cli@latest"
|
|
4172
|
+
);
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
4175
|
+
this.opts.audit("upgrade_started", { from: currentVersion });
|
|
3932
4176
|
try {
|
|
3933
|
-
const
|
|
3934
|
-
this.
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
action: DAEMON_AUDIT_ACTIONS.UPDATE_AVAILABLE,
|
|
3941
|
-
details: {
|
|
3942
|
-
currentVersion: this.currentVersion,
|
|
3943
|
-
latestVersion: latest
|
|
3944
|
-
},
|
|
3945
|
-
outcome: "success"
|
|
3946
|
-
});
|
|
3947
|
-
this.emittedForVersion = latest;
|
|
3948
|
-
}
|
|
3949
|
-
} catch (err) {
|
|
3950
|
-
console.warn(
|
|
3951
|
-
`[daemon] update check failed: ${err.message} (will retry)`
|
|
4177
|
+
const newVersion = await this.opts.runInstall();
|
|
4178
|
+
this.opts.audit("upgrade_completed", {
|
|
4179
|
+
from: currentVersion,
|
|
4180
|
+
to: newVersion
|
|
4181
|
+
});
|
|
4182
|
+
this.opts.log?.(
|
|
4183
|
+
`[daemon] upgraded ${currentVersion} -> ${newVersion}; restarting for supervisor pickup.`
|
|
3952
4184
|
);
|
|
4185
|
+
this.opts.requestRestart();
|
|
4186
|
+
} catch (err) {
|
|
4187
|
+
this.opts.audit("upgrade_failed", {
|
|
4188
|
+
from: currentVersion,
|
|
4189
|
+
error: err.message
|
|
4190
|
+
});
|
|
4191
|
+
this.opts.log?.(`[daemon] upgrade failed: ${err.message}`);
|
|
3953
4192
|
}
|
|
3954
4193
|
}
|
|
3955
|
-
info() {
|
|
3956
|
-
return {
|
|
3957
|
-
currentVersion: this.currentVersion,
|
|
3958
|
-
latestVersion: this.latestVersion,
|
|
3959
|
-
updateAvailable: !!this.latestVersion && isNewerVersion(this.latestVersion, this.currentVersion),
|
|
3960
|
-
checkedAt: this.lastCheckedAt?.toISOString() ?? null
|
|
3961
|
-
};
|
|
3962
|
-
}
|
|
3963
4194
|
};
|
|
3964
|
-
async function defaultRegistryFetcher(packageName) {
|
|
3965
|
-
const url = `${REGISTRY_URL}/${encodeURIComponent(packageName)}/latest`;
|
|
3966
|
-
const response = await fetch(url, {
|
|
3967
|
-
method: "GET",
|
|
3968
|
-
headers: { Accept: "application/json" },
|
|
3969
|
-
signal: AbortSignal.timeout(1e4)
|
|
3970
|
-
});
|
|
3971
|
-
if (!response.ok) {
|
|
3972
|
-
throw new Error(`registry ${response.status} ${response.statusText}`);
|
|
3973
|
-
}
|
|
3974
|
-
const body = await response.json();
|
|
3975
|
-
if (!body.version) throw new Error("registry response missing version");
|
|
3976
|
-
return body.version;
|
|
3977
|
-
}
|
|
3978
|
-
function isNewerVersion(a, b) {
|
|
3979
|
-
const pa = parseSemverNumeric(a);
|
|
3980
|
-
const pb = parseSemverNumeric(b);
|
|
3981
|
-
if (!pa || !pb) return false;
|
|
3982
|
-
for (let i = 0; i < 3; i += 1) {
|
|
3983
|
-
if (pa[i] > pb[i]) return true;
|
|
3984
|
-
if (pa[i] < pb[i]) return false;
|
|
3985
|
-
}
|
|
3986
|
-
return false;
|
|
3987
|
-
}
|
|
3988
|
-
function parseSemverNumeric(v) {
|
|
3989
|
-
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
3990
|
-
if (!match) return null;
|
|
3991
|
-
return [
|
|
3992
|
-
Number.parseInt(match[1], 10),
|
|
3993
|
-
Number.parseInt(match[2], 10),
|
|
3994
|
-
Number.parseInt(match[3], 10)
|
|
3995
|
-
];
|
|
3996
|
-
}
|
|
3997
|
-
|
|
3998
|
-
// _apps/@onklave/agent-cli/src/services/cli-version.ts
|
|
3999
|
-
import * as fs6 from "node:fs";
|
|
4000
|
-
import * as path7 from "node:path";
|
|
4001
|
-
import { fileURLToPath } from "node:url";
|
|
4002
|
-
function readPackageVersion() {
|
|
4003
|
-
try {
|
|
4004
|
-
const moduleDir = path7.dirname(fileURLToPath(import.meta.url));
|
|
4005
|
-
const candidates = [
|
|
4006
|
-
path7.join(moduleDir, "package.json"),
|
|
4007
|
-
// bundled: dist root
|
|
4008
|
-
path7.join(moduleDir, "..", "package.json"),
|
|
4009
|
-
path7.join(moduleDir, "..", "..", "package.json"),
|
|
4010
|
-
// dev: src/services -> root
|
|
4011
|
-
path7.join(moduleDir, "..", "..", "..", "package.json")
|
|
4012
|
-
];
|
|
4013
|
-
for (const p of candidates) {
|
|
4014
|
-
if (!fs6.existsSync(p)) continue;
|
|
4015
|
-
const pkg = JSON.parse(fs6.readFileSync(p, "utf8"));
|
|
4016
|
-
if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
|
|
4017
|
-
}
|
|
4018
|
-
} catch {
|
|
4019
|
-
}
|
|
4020
|
-
return null;
|
|
4021
|
-
}
|
|
4022
|
-
function getCurrentVersion() {
|
|
4023
|
-
return readPackageVersion() ?? "0.0.0";
|
|
4024
|
-
}
|
|
4025
4195
|
|
|
4026
4196
|
// _apps/@onklave/agent-cli/src/services/daemon-state.service.ts
|
|
4027
|
-
import * as
|
|
4028
|
-
import * as
|
|
4029
|
-
import * as
|
|
4197
|
+
import * as fs9 from "fs";
|
|
4198
|
+
import * as path9 from "path";
|
|
4199
|
+
import * as os7 from "os";
|
|
4030
4200
|
var VALID_TRANSITIONS = {
|
|
4031
4201
|
installing: ["registered"],
|
|
4032
4202
|
registered: ["starting"],
|
|
@@ -4037,10 +4207,10 @@ var VALID_TRANSITIONS = {
|
|
|
4037
4207
|
stopped: ["starting"]
|
|
4038
4208
|
};
|
|
4039
4209
|
function defaultStateFilePath() {
|
|
4040
|
-
return
|
|
4210
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.state.json");
|
|
4041
4211
|
}
|
|
4042
4212
|
function defaultPidFilePath() {
|
|
4043
|
-
return
|
|
4213
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.pid");
|
|
4044
4214
|
}
|
|
4045
4215
|
var DaemonStateError = class extends Error {
|
|
4046
4216
|
constructor(message) {
|
|
@@ -4064,8 +4234,8 @@ var DaemonStateService = class {
|
|
|
4064
4234
|
*/
|
|
4065
4235
|
static readPersisted(stateFile = defaultStateFilePath()) {
|
|
4066
4236
|
try {
|
|
4067
|
-
if (!
|
|
4068
|
-
const raw =
|
|
4237
|
+
if (!fs9.existsSync(stateFile)) return null;
|
|
4238
|
+
const raw = fs9.readFileSync(stateFile, "utf8");
|
|
4069
4239
|
const parsed = JSON.parse(raw);
|
|
4070
4240
|
if (!parsed.state || !parsed.enteredAt) return null;
|
|
4071
4241
|
return parsed;
|
|
@@ -4115,8 +4285,8 @@ var DaemonStateService = class {
|
|
|
4115
4285
|
}
|
|
4116
4286
|
}
|
|
4117
4287
|
persist(reason) {
|
|
4118
|
-
const dir =
|
|
4119
|
-
|
|
4288
|
+
const dir = path9.dirname(this.stateFile);
|
|
4289
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
4120
4290
|
const payload = {
|
|
4121
4291
|
state: this.current,
|
|
4122
4292
|
enteredAt: this.enteredAt.toISOString(),
|
|
@@ -4126,8 +4296,8 @@ var DaemonStateService = class {
|
|
|
4126
4296
|
...this.latestRuntime ? { runtime: this.latestRuntime } : {}
|
|
4127
4297
|
};
|
|
4128
4298
|
const tmp = `${this.stateFile}.tmp`;
|
|
4129
|
-
|
|
4130
|
-
|
|
4299
|
+
fs9.writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
4300
|
+
fs9.renameSync(tmp, this.stateFile);
|
|
4131
4301
|
}
|
|
4132
4302
|
/**
|
|
4133
4303
|
* Publish the latest runtime snapshot. Persists to the state file
|
|
@@ -4145,7 +4315,7 @@ var DaemonStateService = class {
|
|
|
4145
4315
|
*/
|
|
4146
4316
|
clearPersisted() {
|
|
4147
4317
|
try {
|
|
4148
|
-
|
|
4318
|
+
fs9.unlinkSync(this.stateFile);
|
|
4149
4319
|
} catch {
|
|
4150
4320
|
}
|
|
4151
4321
|
}
|
|
@@ -4389,7 +4559,9 @@ async function daemonStart() {
|
|
|
4389
4559
|
platformUrl,
|
|
4390
4560
|
deviceToken: creds.deviceToken,
|
|
4391
4561
|
machineId: creds.machineId,
|
|
4392
|
-
getActiveSessionCount: () => claimHandler.getActiveSessionCount()
|
|
4562
|
+
getActiveSessionCount: () => claimHandler.getActiveSessionCount(),
|
|
4563
|
+
cliVersion: readPackageVersion() ?? "0.0.0",
|
|
4564
|
+
posture: collectPosture()
|
|
4393
4565
|
});
|
|
4394
4566
|
const drainAndExit = async (reason) => {
|
|
4395
4567
|
if (shuttingDown) return;
|
|
@@ -4452,6 +4624,35 @@ async function daemonStart() {
|
|
|
4452
4624
|
}
|
|
4453
4625
|
console.log("Connected.");
|
|
4454
4626
|
claimHandler.attachToSocket(comms.inner());
|
|
4627
|
+
const upgradeService = new DaemonUpgradeService({
|
|
4628
|
+
allowAutoUpgrade: !!process.env["ONKLAVE_ALLOW_AUTO_UPGRADE"],
|
|
4629
|
+
currentVersion: readPackageVersion() ?? "0.0.0",
|
|
4630
|
+
runInstall: async () => {
|
|
4631
|
+
const { execFileSync } = __require("child_process");
|
|
4632
|
+
execFileSync("npm", ["install", "-g", "@onklave/agent-cli@latest"], {
|
|
4633
|
+
stdio: "pipe",
|
|
4634
|
+
timeout: 18e4
|
|
4635
|
+
});
|
|
4636
|
+
const v = execFileSync(
|
|
4637
|
+
"npm",
|
|
4638
|
+
["view", "@onklave/agent-cli@latest", "version"],
|
|
4639
|
+
{ stdio: "pipe", timeout: 3e4 }
|
|
4640
|
+
).toString().trim();
|
|
4641
|
+
return v || "latest";
|
|
4642
|
+
},
|
|
4643
|
+
requestRestart: () => void drainAndExit("upgrade"),
|
|
4644
|
+
audit: (action, details) => auditStreamer.record({
|
|
4645
|
+
sessionId: creds.machineId,
|
|
4646
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
4647
|
+
action,
|
|
4648
|
+
details,
|
|
4649
|
+
outcome: action === "upgrade_failed" ? "failure" : "success"
|
|
4650
|
+
}),
|
|
4651
|
+
log: (m) => console.log(m)
|
|
4652
|
+
});
|
|
4653
|
+
comms.inner().onUpgradeRequest(() => {
|
|
4654
|
+
void upgradeService.handle();
|
|
4655
|
+
});
|
|
4455
4656
|
await stateService.transition("online");
|
|
4456
4657
|
heartbeat.start();
|
|
4457
4658
|
resourceSampler.start();
|
|
@@ -4601,7 +4802,7 @@ function transitionToAction(next) {
|
|
|
4601
4802
|
}
|
|
4602
4803
|
function readPid(pidFile) {
|
|
4603
4804
|
try {
|
|
4604
|
-
const raw =
|
|
4805
|
+
const raw = fs10.readFileSync(pidFile, "utf8").trim();
|
|
4605
4806
|
const parsed = Number.parseInt(raw, 10);
|
|
4606
4807
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
4607
4808
|
} catch {
|
|
@@ -4609,13 +4810,13 @@ function readPid(pidFile) {
|
|
|
4609
4810
|
}
|
|
4610
4811
|
}
|
|
4611
4812
|
function writePid(pidFile) {
|
|
4612
|
-
const dir =
|
|
4613
|
-
|
|
4614
|
-
|
|
4813
|
+
const dir = path10.dirname(pidFile);
|
|
4814
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4815
|
+
fs10.writeFileSync(pidFile, String(process.pid), { mode: 384 });
|
|
4615
4816
|
}
|
|
4616
4817
|
function removePid(pidFile) {
|
|
4617
4818
|
try {
|
|
4618
|
-
|
|
4819
|
+
fs10.unlinkSync(pidFile);
|
|
4619
4820
|
} catch {
|
|
4620
4821
|
}
|
|
4621
4822
|
}
|
|
@@ -4659,15 +4860,15 @@ var COMMANDS = {
|
|
|
4659
4860
|
};
|
|
4660
4861
|
|
|
4661
4862
|
// _apps/@onklave/agent-cli/src/services/update-notifier.service.ts
|
|
4662
|
-
import * as
|
|
4663
|
-
import * as
|
|
4664
|
-
import * as
|
|
4863
|
+
import * as fs11 from "node:fs";
|
|
4864
|
+
import * as path11 from "node:path";
|
|
4865
|
+
import * as os8 from "node:os";
|
|
4665
4866
|
import { createInterface } from "node:readline/promises";
|
|
4666
4867
|
import { spawnSync } from "node:child_process";
|
|
4667
4868
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4668
4869
|
var FETCH_DEADLINE_MS = 1500;
|
|
4669
|
-
var CACHE_PATH =
|
|
4670
|
-
|
|
4870
|
+
var CACHE_PATH = path11.join(
|
|
4871
|
+
os8.homedir(),
|
|
4671
4872
|
".config",
|
|
4672
4873
|
"onklave",
|
|
4673
4874
|
"update-check.json"
|
|
@@ -4746,7 +4947,7 @@ async function maybeNotifyUpdate(deps = {}) {
|
|
|
4746
4947
|
}
|
|
4747
4948
|
}
|
|
4748
4949
|
function withTimeout(promise, ms) {
|
|
4749
|
-
return new Promise((
|
|
4950
|
+
return new Promise((resolve4, reject) => {
|
|
4750
4951
|
const timer = setTimeout(
|
|
4751
4952
|
() => reject(new Error("update check timed out")),
|
|
4752
4953
|
ms
|
|
@@ -4755,7 +4956,7 @@ function withTimeout(promise, ms) {
|
|
|
4755
4956
|
promise.then(
|
|
4756
4957
|
(value) => {
|
|
4757
4958
|
clearTimeout(timer);
|
|
4758
|
-
|
|
4959
|
+
resolve4(value);
|
|
4759
4960
|
},
|
|
4760
4961
|
(err) => {
|
|
4761
4962
|
clearTimeout(timer);
|
|
@@ -4777,16 +4978,16 @@ function isCacheFresh(cache, nowMs) {
|
|
|
4777
4978
|
}
|
|
4778
4979
|
function readCache() {
|
|
4779
4980
|
try {
|
|
4780
|
-
if (!
|
|
4781
|
-
return JSON.parse(
|
|
4981
|
+
if (!fs11.existsSync(CACHE_PATH)) return {};
|
|
4982
|
+
return JSON.parse(fs11.readFileSync(CACHE_PATH, "utf8"));
|
|
4782
4983
|
} catch {
|
|
4783
4984
|
return {};
|
|
4784
4985
|
}
|
|
4785
4986
|
}
|
|
4786
4987
|
function writeCache(cache) {
|
|
4787
4988
|
try {
|
|
4788
|
-
|
|
4789
|
-
|
|
4989
|
+
fs11.mkdirSync(path11.dirname(CACHE_PATH), { recursive: true, mode: 448 });
|
|
4990
|
+
fs11.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), "utf8");
|
|
4790
4991
|
} catch {
|
|
4791
4992
|
}
|
|
4792
4993
|
}
|