@onklave/agent-cli 0.1.44 → 0.1.46
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 +390 -192
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -285,6 +285,20 @@ async function loginCommand(args) {
|
|
|
285
285
|
process.exitCode = 1;
|
|
286
286
|
return;
|
|
287
287
|
}
|
|
288
|
+
const linkingTokenPrefixes = ["ok_link_", "ok_worker_", "ok_device_"];
|
|
289
|
+
if (linkingTokenPrefixes.some((prefix) => token.startsWith(prefix))) {
|
|
290
|
+
console.error(
|
|
291
|
+
"That looks like a worker/device linking token, not a login credential.\n"
|
|
292
|
+
);
|
|
293
|
+
console.error(
|
|
294
|
+
"Linking tokens register a machine \u2014 use: onklave register --token <token>"
|
|
295
|
+
);
|
|
296
|
+
console.error(
|
|
297
|
+
"To log in, generate a CLI token in the Portal under Settings > API Tokens."
|
|
298
|
+
);
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
288
302
|
const metadata = {};
|
|
289
303
|
if (typeof platformUrl === "string") {
|
|
290
304
|
metadata.platformUrl = platformUrl;
|
|
@@ -729,6 +743,19 @@ var PlatformClient = class {
|
|
|
729
743
|
async listMachines() {
|
|
730
744
|
return this.request("GET", "/api/v1/runner/machines");
|
|
731
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
|
+
}
|
|
732
759
|
/*
|
|
733
760
|
* Check platform health.
|
|
734
761
|
*/
|
|
@@ -747,8 +774,8 @@ var PlatformClient = class {
|
|
|
747
774
|
/*
|
|
748
775
|
* Make an authenticated HTTP request to the platform.
|
|
749
776
|
*/
|
|
750
|
-
async request(method,
|
|
751
|
-
const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${
|
|
777
|
+
async request(method, path11, body, extraHeaders) {
|
|
778
|
+
const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${path11}`;
|
|
752
779
|
const headers = {
|
|
753
780
|
Authorization: `Bearer ${this.token}`,
|
|
754
781
|
"Content-Type": "application/json",
|
|
@@ -803,7 +830,7 @@ var CommsClient = class {
|
|
|
803
830
|
* (e.g. `assignment:claim-available`) to this specific runner.
|
|
804
831
|
*/
|
|
805
832
|
async connect(commsUrl, token, extra) {
|
|
806
|
-
return new Promise((
|
|
833
|
+
return new Promise((resolve4, reject) => {
|
|
807
834
|
const baseUrl = commsUrl.replace(/\/+$/, "");
|
|
808
835
|
this.socket = io(`${baseUrl}/agent-cli`, {
|
|
809
836
|
auth: {
|
|
@@ -828,7 +855,7 @@ var CommsClient = class {
|
|
|
828
855
|
this.socket.on("connect", () => {
|
|
829
856
|
clearTimeout(connectTimeout);
|
|
830
857
|
this.reconnectAttempts = 0;
|
|
831
|
-
|
|
858
|
+
resolve4();
|
|
832
859
|
});
|
|
833
860
|
this.socket.on("connect_error", (error) => {
|
|
834
861
|
this.reconnectAttempts++;
|
|
@@ -914,6 +941,15 @@ var CommsClient = class {
|
|
|
914
941
|
onAssignmentAvailable(handler) {
|
|
915
942
|
this.socket?.on("assignment:claim-available", handler);
|
|
916
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
|
+
}
|
|
917
953
|
};
|
|
918
954
|
|
|
919
955
|
// _apps/@onklave/agent-cli/src/services/session-manager.ts
|
|
@@ -1004,16 +1040,16 @@ var SessionManager = class {
|
|
|
1004
1040
|
throw new Error(`No active session found with ID: ${sessionId}`);
|
|
1005
1041
|
}
|
|
1006
1042
|
session.process.kill("SIGTERM");
|
|
1007
|
-
await new Promise((
|
|
1043
|
+
await new Promise((resolve4) => {
|
|
1008
1044
|
const forceTimeout = setTimeout(() => {
|
|
1009
1045
|
if (session.process.killed === false) {
|
|
1010
1046
|
session.process.kill("SIGKILL");
|
|
1011
1047
|
}
|
|
1012
|
-
|
|
1048
|
+
resolve4();
|
|
1013
1049
|
}, 5e3);
|
|
1014
1050
|
session.process.on("exit", () => {
|
|
1015
1051
|
clearTimeout(forceTimeout);
|
|
1016
|
-
|
|
1052
|
+
resolve4();
|
|
1017
1053
|
});
|
|
1018
1054
|
});
|
|
1019
1055
|
this.activeSessions.delete(sessionId);
|
|
@@ -1213,7 +1249,50 @@ var AuditStreamer = class {
|
|
|
1213
1249
|
};
|
|
1214
1250
|
|
|
1215
1251
|
// _apps/@onklave/agent-cli/src/services/guardrail-enforcer.ts
|
|
1252
|
+
import * as fs3 from "fs";
|
|
1216
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
|
+
}
|
|
1217
1296
|
var GuardrailEnforcer = class {
|
|
1218
1297
|
constructor(config) {
|
|
1219
1298
|
this.config = config;
|
|
@@ -1272,11 +1351,11 @@ var GuardrailEnforcer = class {
|
|
|
1272
1351
|
* Check if a file path is accessible for the given operation.
|
|
1273
1352
|
*/
|
|
1274
1353
|
checkPathAccess(filePath, operation) {
|
|
1275
|
-
const
|
|
1354
|
+
const realPath = safeRealpath(filePath);
|
|
1276
1355
|
if (operation === "write" || operation === "delete") {
|
|
1277
1356
|
if (this.config.writablePaths.length > 0) {
|
|
1278
1357
|
const isWritable = this.config.writablePaths.some(
|
|
1279
|
-
(wp) =>
|
|
1358
|
+
(wp) => isPathWithin(filePath, wp)
|
|
1280
1359
|
);
|
|
1281
1360
|
if (!isWritable) {
|
|
1282
1361
|
return {
|
|
@@ -1289,7 +1368,7 @@ var GuardrailEnforcer = class {
|
|
|
1289
1368
|
if (operation === "read") {
|
|
1290
1369
|
if (this.config.readablePaths.length > 0) {
|
|
1291
1370
|
const isReadable = this.config.readablePaths.some(
|
|
1292
|
-
(rp) =>
|
|
1371
|
+
(rp) => isPathWithin(filePath, rp)
|
|
1293
1372
|
);
|
|
1294
1373
|
if (!isReadable) {
|
|
1295
1374
|
return {
|
|
@@ -1308,9 +1387,10 @@ var GuardrailEnforcer = class {
|
|
|
1308
1387
|
"id_rsa",
|
|
1309
1388
|
"id_ed25519"
|
|
1310
1389
|
];
|
|
1311
|
-
const basename3 = path3.basename(
|
|
1390
|
+
const basename3 = path3.basename(realPath);
|
|
1391
|
+
const normForward = realPath.split(path3.sep).join("/");
|
|
1312
1392
|
for (const sensitive of sensitivePatterns) {
|
|
1313
|
-
if (basename3 === sensitive ||
|
|
1393
|
+
if (basename3 === sensitive || normForward.includes(`/${sensitive}`)) {
|
|
1314
1394
|
return {
|
|
1315
1395
|
allowed: false,
|
|
1316
1396
|
reason: `Access to sensitive path "${filePath}" is blocked by default`
|
|
@@ -1417,7 +1497,9 @@ var HeartbeatService = class {
|
|
|
1417
1497
|
machineId: this.opts.machineId,
|
|
1418
1498
|
status: "online",
|
|
1419
1499
|
activeSessionCount: this.opts.getActiveSessionCount(),
|
|
1420
|
-
resourceUsage: this.opts.getResourceUsage?.()
|
|
1500
|
+
resourceUsage: this.opts.getResourceUsage?.(),
|
|
1501
|
+
cliVersion: this.opts.cliVersion,
|
|
1502
|
+
posture: this.opts.posture
|
|
1421
1503
|
};
|
|
1422
1504
|
try {
|
|
1423
1505
|
const response = await fetch(
|
|
@@ -1446,6 +1528,141 @@ var HeartbeatService = class {
|
|
|
1446
1528
|
}
|
|
1447
1529
|
};
|
|
1448
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
|
+
|
|
1449
1666
|
// _apps/@onklave/agent-cli/src/tui/render.tsx
|
|
1450
1667
|
import { render } from "ink";
|
|
1451
1668
|
|
|
@@ -2032,6 +2249,27 @@ async function runCommand(args) {
|
|
|
2032
2249
|
resolvedConfig.platformUrl,
|
|
2033
2250
|
creds.token
|
|
2034
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
|
+
}
|
|
2035
2273
|
const sessionManager = new SessionManager();
|
|
2036
2274
|
let heartbeat = null;
|
|
2037
2275
|
if (creds.machineId && creds.deviceToken) {
|
|
@@ -2039,7 +2277,9 @@ async function runCommand(args) {
|
|
|
2039
2277
|
platformUrl: resolvedConfig.platformUrl,
|
|
2040
2278
|
deviceToken: creds.deviceToken,
|
|
2041
2279
|
machineId: creds.machineId,
|
|
2042
|
-
getActiveSessionCount: () => sessionManager.getActiveSessionIds().length
|
|
2280
|
+
getActiveSessionCount: () => sessionManager.getActiveSessionIds().length,
|
|
2281
|
+
cliVersion: getCurrentVersion(),
|
|
2282
|
+
posture: collectPosture()
|
|
2043
2283
|
});
|
|
2044
2284
|
heartbeat.start();
|
|
2045
2285
|
const stopHeartbeat = () => heartbeat?.stop();
|
|
@@ -2121,8 +2361,8 @@ async function runCommand(args) {
|
|
|
2121
2361
|
}
|
|
2122
2362
|
const addDirs = [
|
|
2123
2363
|
.../* @__PURE__ */ new Set([
|
|
2124
|
-
|
|
2125
|
-
...
|
|
2364
|
+
path5.resolve(resolvedConfig.context),
|
|
2365
|
+
...effectiveAllowedRoots
|
|
2126
2366
|
])
|
|
2127
2367
|
];
|
|
2128
2368
|
const sessionConfig = {
|
|
@@ -2235,15 +2475,15 @@ async function runCommand(args) {
|
|
|
2235
2475
|
}
|
|
2236
2476
|
}
|
|
2237
2477
|
});
|
|
2238
|
-
await new Promise((
|
|
2478
|
+
await new Promise((resolve4) => {
|
|
2239
2479
|
const checkInterval = setInterval(() => {
|
|
2240
2480
|
if (!sessionManager.isSessionActive(sessionId)) {
|
|
2241
2481
|
clearInterval(checkInterval);
|
|
2242
|
-
|
|
2482
|
+
resolve4();
|
|
2243
2483
|
}
|
|
2244
2484
|
}, 500);
|
|
2245
2485
|
});
|
|
2246
|
-
await new Promise((
|
|
2486
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
2247
2487
|
tuiInstance.unmount();
|
|
2248
2488
|
} catch (err) {
|
|
2249
2489
|
tuiInstance.unmount();
|
|
@@ -2349,11 +2589,11 @@ Session timed out after ${resolvedConfig.timeout} seconds.`
|
|
|
2349
2589
|
commsClient.disconnect();
|
|
2350
2590
|
}
|
|
2351
2591
|
});
|
|
2352
|
-
await new Promise((
|
|
2592
|
+
await new Promise((resolve4) => {
|
|
2353
2593
|
const checkInterval = setInterval(() => {
|
|
2354
2594
|
if (!sessionManager.isSessionActive(sessionId)) {
|
|
2355
2595
|
clearInterval(checkInterval);
|
|
2356
|
-
|
|
2596
|
+
resolve4();
|
|
2357
2597
|
}
|
|
2358
2598
|
}, 500);
|
|
2359
2599
|
});
|
|
@@ -2584,8 +2824,8 @@ async function denyCommand(args) {
|
|
|
2584
2824
|
}
|
|
2585
2825
|
|
|
2586
2826
|
// _apps/@onklave/agent-cli/src/commands/doctor.command.ts
|
|
2587
|
-
import * as
|
|
2588
|
-
import * as
|
|
2827
|
+
import * as fs6 from "fs";
|
|
2828
|
+
import * as path6 from "path";
|
|
2589
2829
|
import { execSync } from "child_process";
|
|
2590
2830
|
async function doctorCommand() {
|
|
2591
2831
|
console.log("Onklave Agent CLI \u2014 Doctor\n");
|
|
@@ -2693,10 +2933,10 @@ function checkNodeVersion() {
|
|
|
2693
2933
|
};
|
|
2694
2934
|
}
|
|
2695
2935
|
function checkProjectConfig() {
|
|
2696
|
-
const configPath =
|
|
2697
|
-
if (
|
|
2936
|
+
const configPath = path6.join(process.cwd(), ".onklave.json");
|
|
2937
|
+
if (fs6.existsSync(configPath)) {
|
|
2698
2938
|
try {
|
|
2699
|
-
const raw =
|
|
2939
|
+
const raw = fs6.readFileSync(configPath, "utf-8");
|
|
2700
2940
|
JSON.parse(raw);
|
|
2701
2941
|
return {
|
|
2702
2942
|
name: "Project config",
|
|
@@ -2746,18 +2986,18 @@ async function checkWebSocket() {
|
|
|
2746
2986
|
}
|
|
2747
2987
|
|
|
2748
2988
|
// _apps/@onklave/agent-cli/src/commands/init.command.ts
|
|
2749
|
-
import * as
|
|
2750
|
-
import * as
|
|
2989
|
+
import * as fs7 from "fs";
|
|
2990
|
+
import * as path7 from "path";
|
|
2751
2991
|
async function initCommand() {
|
|
2752
|
-
const configPath =
|
|
2753
|
-
if (
|
|
2992
|
+
const configPath = path7.join(process.cwd(), ".onklave.json");
|
|
2993
|
+
if (fs7.existsSync(configPath)) {
|
|
2754
2994
|
console.error("Error: .onklave.json already exists in this directory.");
|
|
2755
2995
|
console.log("To overwrite, delete the existing file first.");
|
|
2756
2996
|
process.exitCode = 1;
|
|
2757
2997
|
return;
|
|
2758
2998
|
}
|
|
2759
2999
|
const template = {
|
|
2760
|
-
project:
|
|
3000
|
+
project: path7.basename(process.cwd()),
|
|
2761
3001
|
org: "",
|
|
2762
3002
|
description: "",
|
|
2763
3003
|
defaults: {
|
|
@@ -2792,7 +3032,7 @@ async function initCommand() {
|
|
|
2792
3032
|
system_prompt_append: ""
|
|
2793
3033
|
}
|
|
2794
3034
|
};
|
|
2795
|
-
|
|
3035
|
+
fs7.writeFileSync(
|
|
2796
3036
|
configPath,
|
|
2797
3037
|
JSON.stringify(template, null, 2) + "\n",
|
|
2798
3038
|
"utf-8"
|
|
@@ -2879,7 +3119,7 @@ async function configCommand(args) {
|
|
|
2879
3119
|
}
|
|
2880
3120
|
|
|
2881
3121
|
// _apps/@onklave/agent-cli/src/commands/register.command.ts
|
|
2882
|
-
import * as
|
|
3122
|
+
import * as os4 from "os";
|
|
2883
3123
|
async function registerCommand(args) {
|
|
2884
3124
|
const { flags } = parseArgs(args);
|
|
2885
3125
|
const refresh = flags["refresh"] === true;
|
|
@@ -2891,8 +3131,8 @@ async function registerCommand(args) {
|
|
|
2891
3131
|
console.log(" onklave register --refresh # re-detect capabilities");
|
|
2892
3132
|
console.log("\nTo obtain a linking token:");
|
|
2893
3133
|
console.log(" 1. Log in to the Onklave Portal");
|
|
2894
|
-
console.log(" 2. Navigate to
|
|
2895
|
-
console.log(' 3. Click "
|
|
3134
|
+
console.log(" 2. Navigate to Worker Nodes");
|
|
3135
|
+
console.log(' 3. Click "Add Worker Node" to generate a linking token');
|
|
2896
3136
|
process.exitCode = 1;
|
|
2897
3137
|
return;
|
|
2898
3138
|
}
|
|
@@ -2926,11 +3166,13 @@ Machine ${creds.machineId} updated successfully.`);
|
|
|
2926
3166
|
return;
|
|
2927
3167
|
}
|
|
2928
3168
|
const metadata = {
|
|
2929
|
-
hostname:
|
|
2930
|
-
os: `${
|
|
2931
|
-
arch:
|
|
3169
|
+
hostname: os4.hostname(),
|
|
3170
|
+
os: `${os4.platform()} ${os4.release()}`,
|
|
3171
|
+
arch: os4.arch(),
|
|
2932
3172
|
nodeVersion: process.version,
|
|
2933
|
-
|
|
3173
|
+
cliVersion: getCurrentVersion(),
|
|
3174
|
+
capabilities: detectCapabilities(),
|
|
3175
|
+
posture: collectPosture()
|
|
2934
3176
|
};
|
|
2935
3177
|
try {
|
|
2936
3178
|
console.log("Registering machine with Onklave platform...");
|
|
@@ -3021,7 +3263,7 @@ async function logsCommand(args) {
|
|
|
3021
3263
|
}
|
|
3022
3264
|
|
|
3023
3265
|
// _apps/@onklave/agent-cli/src/commands/daemon.command.ts
|
|
3024
|
-
import * as
|
|
3266
|
+
import * as fs10 from "fs";
|
|
3025
3267
|
|
|
3026
3268
|
// _apps/@onklave/agent-cli/src/services/daemon-comms.service.ts
|
|
3027
3269
|
var DaemonCommsService = class {
|
|
@@ -3095,7 +3337,7 @@ var DaemonCommsService = class {
|
|
|
3095
3337
|
}
|
|
3096
3338
|
};
|
|
3097
3339
|
function defaultSleep(ms) {
|
|
3098
|
-
return new Promise((
|
|
3340
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
3099
3341
|
}
|
|
3100
3342
|
|
|
3101
3343
|
// _apps/@onklave/agent-cli/src/services/daemon-claim-handler.service.ts
|
|
@@ -3448,8 +3690,8 @@ var PlatformBrokerClient = class {
|
|
|
3448
3690
|
params
|
|
3449
3691
|
);
|
|
3450
3692
|
}
|
|
3451
|
-
async request(method,
|
|
3452
|
-
const url = `${this.baseUrl}/agent-orchestration${
|
|
3693
|
+
async request(method, path11, body) {
|
|
3694
|
+
const url = `${this.baseUrl}/agent-orchestration${path11}`;
|
|
3453
3695
|
const response = await fetch(url, {
|
|
3454
3696
|
method,
|
|
3455
3697
|
headers: {
|
|
@@ -3485,9 +3727,9 @@ var PlatformBrokerClient = class {
|
|
|
3485
3727
|
|
|
3486
3728
|
// _apps/@onklave/agent-cli/src/services/work-item-runner.service.ts
|
|
3487
3729
|
import { spawn as spawn2 } from "child_process";
|
|
3488
|
-
import * as
|
|
3489
|
-
import * as
|
|
3490
|
-
import * as
|
|
3730
|
+
import * as fs8 from "fs";
|
|
3731
|
+
import * as os5 from "os";
|
|
3732
|
+
import * as path8 from "path";
|
|
3491
3733
|
var WorkItemRunner = class {
|
|
3492
3734
|
constructor(opts = {}) {
|
|
3493
3735
|
this.sessionManager = opts.sessionManager ?? new SessionManager();
|
|
@@ -3508,10 +3750,10 @@ var WorkItemRunner = class {
|
|
|
3508
3750
|
summary: `Work item ${workItem.id} has no clonable repo (project.repos[0].url missing).`
|
|
3509
3751
|
};
|
|
3510
3752
|
}
|
|
3511
|
-
const workDir =
|
|
3512
|
-
|
|
3753
|
+
const workDir = fs8.mkdtempSync(
|
|
3754
|
+
path8.join(os5.tmpdir(), `onklave-pickup-${sessionId}-`)
|
|
3513
3755
|
);
|
|
3514
|
-
const repoDir =
|
|
3756
|
+
const repoDir = path8.join(workDir, sanitizeName(repo.name) || "repo");
|
|
3515
3757
|
const branchName = `onklave/work-item/${workItem.id}`;
|
|
3516
3758
|
try {
|
|
3517
3759
|
await this.git(["clone", repo.url, repoDir], workDir);
|
|
@@ -3563,7 +3805,7 @@ var WorkItemRunner = class {
|
|
|
3563
3805
|
systemPromptAppend: null
|
|
3564
3806
|
};
|
|
3565
3807
|
let output = "";
|
|
3566
|
-
const exitCode = await new Promise((
|
|
3808
|
+
const exitCode = await new Promise((resolve4) => {
|
|
3567
3809
|
const timeout = setTimeout(() => {
|
|
3568
3810
|
void this.sessionManager.stopSession(sessionId).catch(() => void 0);
|
|
3569
3811
|
}, this.timeoutSeconds * 1e3);
|
|
@@ -3576,7 +3818,7 @@ var WorkItemRunner = class {
|
|
|
3576
3818
|
},
|
|
3577
3819
|
onExit: (code) => {
|
|
3578
3820
|
clearTimeout(timeout);
|
|
3579
|
-
|
|
3821
|
+
resolve4(code);
|
|
3580
3822
|
}
|
|
3581
3823
|
});
|
|
3582
3824
|
});
|
|
@@ -3609,12 +3851,12 @@ function sanitizeName(name) {
|
|
|
3609
3851
|
}
|
|
3610
3852
|
function cleanup(dir) {
|
|
3611
3853
|
try {
|
|
3612
|
-
|
|
3854
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
3613
3855
|
} catch {
|
|
3614
3856
|
}
|
|
3615
3857
|
}
|
|
3616
3858
|
function defaultGit(args, cwd) {
|
|
3617
|
-
return new Promise((
|
|
3859
|
+
return new Promise((resolve4, reject) => {
|
|
3618
3860
|
const child = spawn2("git", args, {
|
|
3619
3861
|
cwd,
|
|
3620
3862
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3626,7 +3868,7 @@ function defaultGit(args, cwd) {
|
|
|
3626
3868
|
child.on("error", (err) => reject(err));
|
|
3627
3869
|
child.on("exit", (code) => {
|
|
3628
3870
|
if (code === 0) {
|
|
3629
|
-
|
|
3871
|
+
resolve4();
|
|
3630
3872
|
} else {
|
|
3631
3873
|
reject(new Error(`git ${args[0]} exited ${code}: ${stderr.trim()}`));
|
|
3632
3874
|
}
|
|
@@ -3635,7 +3877,7 @@ function defaultGit(args, cwd) {
|
|
|
3635
3877
|
}
|
|
3636
3878
|
|
|
3637
3879
|
// _apps/@onklave/agent-cli/src/services/daemon-resource-sampler.service.ts
|
|
3638
|
-
import * as
|
|
3880
|
+
import * as os6 from "os";
|
|
3639
3881
|
var DEFAULT_SAMPLE_INTERVAL_MS = 5e3;
|
|
3640
3882
|
var DEFAULT_WINDOW_MS = 6e4;
|
|
3641
3883
|
var DaemonResourceSampler = class {
|
|
@@ -3703,8 +3945,8 @@ var DaemonResourceSampler = class {
|
|
|
3703
3945
|
}
|
|
3704
3946
|
};
|
|
3705
3947
|
function defaultCpuPercent() {
|
|
3706
|
-
const cpus2 =
|
|
3707
|
-
const load1m =
|
|
3948
|
+
const cpus2 = os6.cpus().length || 1;
|
|
3949
|
+
const load1m = os6.loadavg()[0];
|
|
3708
3950
|
return load1m / cpus2 * 100;
|
|
3709
3951
|
}
|
|
3710
3952
|
function defaultMemoryRss() {
|
|
@@ -3735,7 +3977,7 @@ var DaemonSpawner = class {
|
|
|
3735
3977
|
orgId: req.orgId,
|
|
3736
3978
|
systemPromptAppend: null
|
|
3737
3979
|
};
|
|
3738
|
-
return new Promise((
|
|
3980
|
+
return new Promise((resolve4, reject) => {
|
|
3739
3981
|
void this.sessionManager.spawnSession(req.sessionId, config, {
|
|
3740
3982
|
onStdout: (data) => {
|
|
3741
3983
|
process.stdout.write(data);
|
|
@@ -3745,7 +3987,7 @@ var DaemonSpawner = class {
|
|
|
3745
3987
|
},
|
|
3746
3988
|
onExit: (code, signal) => {
|
|
3747
3989
|
if (code === 0) {
|
|
3748
|
-
|
|
3990
|
+
resolve4();
|
|
3749
3991
|
} else {
|
|
3750
3992
|
reject(
|
|
3751
3993
|
new Error(
|
|
@@ -3896,123 +4138,48 @@ var DaemonTokenRefresher = class {
|
|
|
3896
4138
|
}
|
|
3897
4139
|
};
|
|
3898
4140
|
|
|
3899
|
-
// _apps/@onklave/agent-cli/src/services/daemon-
|
|
3900
|
-
var
|
|
3901
|
-
var PACKAGE_NAME = "@onklave/agent-cli";
|
|
3902
|
-
var DaemonUpdateChecker = class {
|
|
4141
|
+
// _apps/@onklave/agent-cli/src/services/daemon-upgrade.service.ts
|
|
4142
|
+
var DaemonUpgradeService = class {
|
|
3903
4143
|
constructor(opts) {
|
|
3904
|
-
this.
|
|
3905
|
-
this.lastCheckedAt = null;
|
|
3906
|
-
this.emittedForVersion = null;
|
|
3907
|
-
this.currentVersion = opts.currentVersion;
|
|
3908
|
-
this.audit = opts.audit;
|
|
3909
|
-
this.registryFetcher = opts.registryFetcher ?? defaultRegistryFetcher;
|
|
4144
|
+
this.opts = opts;
|
|
3910
4145
|
}
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4146
|
+
async handle() {
|
|
4147
|
+
const { allowAutoUpgrade, currentVersion } = this.opts;
|
|
4148
|
+
if (!allowAutoUpgrade) {
|
|
4149
|
+
this.opts.audit("upgrade_requested_denied", {
|
|
4150
|
+
reason: "auto_upgrade_disabled",
|
|
4151
|
+
currentVersion
|
|
4152
|
+
});
|
|
4153
|
+
this.opts.log?.(
|
|
4154
|
+
"[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"
|
|
4155
|
+
);
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
this.opts.audit("upgrade_started", { from: currentVersion });
|
|
3918
4159
|
try {
|
|
3919
|
-
const
|
|
3920
|
-
this.
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
action: DAEMON_AUDIT_ACTIONS.UPDATE_AVAILABLE,
|
|
3927
|
-
details: {
|
|
3928
|
-
currentVersion: this.currentVersion,
|
|
3929
|
-
latestVersion: latest
|
|
3930
|
-
},
|
|
3931
|
-
outcome: "success"
|
|
3932
|
-
});
|
|
3933
|
-
this.emittedForVersion = latest;
|
|
3934
|
-
}
|
|
3935
|
-
} catch (err) {
|
|
3936
|
-
console.warn(
|
|
3937
|
-
`[daemon] update check failed: ${err.message} (will retry)`
|
|
4160
|
+
const newVersion = await this.opts.runInstall();
|
|
4161
|
+
this.opts.audit("upgrade_completed", {
|
|
4162
|
+
from: currentVersion,
|
|
4163
|
+
to: newVersion
|
|
4164
|
+
});
|
|
4165
|
+
this.opts.log?.(
|
|
4166
|
+
`[daemon] upgraded ${currentVersion} -> ${newVersion}; restarting for supervisor pickup.`
|
|
3938
4167
|
);
|
|
4168
|
+
this.opts.requestRestart();
|
|
4169
|
+
} catch (err) {
|
|
4170
|
+
this.opts.audit("upgrade_failed", {
|
|
4171
|
+
from: currentVersion,
|
|
4172
|
+
error: err.message
|
|
4173
|
+
});
|
|
4174
|
+
this.opts.log?.(`[daemon] upgrade failed: ${err.message}`);
|
|
3939
4175
|
}
|
|
3940
4176
|
}
|
|
3941
|
-
info() {
|
|
3942
|
-
return {
|
|
3943
|
-
currentVersion: this.currentVersion,
|
|
3944
|
-
latestVersion: this.latestVersion,
|
|
3945
|
-
updateAvailable: !!this.latestVersion && isNewerVersion(this.latestVersion, this.currentVersion),
|
|
3946
|
-
checkedAt: this.lastCheckedAt?.toISOString() ?? null
|
|
3947
|
-
};
|
|
3948
|
-
}
|
|
3949
4177
|
};
|
|
3950
|
-
async function defaultRegistryFetcher(packageName) {
|
|
3951
|
-
const url = `${REGISTRY_URL}/${encodeURIComponent(packageName)}/latest`;
|
|
3952
|
-
const response = await fetch(url, {
|
|
3953
|
-
method: "GET",
|
|
3954
|
-
headers: { Accept: "application/json" },
|
|
3955
|
-
signal: AbortSignal.timeout(1e4)
|
|
3956
|
-
});
|
|
3957
|
-
if (!response.ok) {
|
|
3958
|
-
throw new Error(`registry ${response.status} ${response.statusText}`);
|
|
3959
|
-
}
|
|
3960
|
-
const body = await response.json();
|
|
3961
|
-
if (!body.version) throw new Error("registry response missing version");
|
|
3962
|
-
return body.version;
|
|
3963
|
-
}
|
|
3964
|
-
function isNewerVersion(a, b) {
|
|
3965
|
-
const pa = parseSemverNumeric(a);
|
|
3966
|
-
const pb = parseSemverNumeric(b);
|
|
3967
|
-
if (!pa || !pb) return false;
|
|
3968
|
-
for (let i = 0; i < 3; i += 1) {
|
|
3969
|
-
if (pa[i] > pb[i]) return true;
|
|
3970
|
-
if (pa[i] < pb[i]) return false;
|
|
3971
|
-
}
|
|
3972
|
-
return false;
|
|
3973
|
-
}
|
|
3974
|
-
function parseSemverNumeric(v) {
|
|
3975
|
-
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
3976
|
-
if (!match) return null;
|
|
3977
|
-
return [
|
|
3978
|
-
Number.parseInt(match[1], 10),
|
|
3979
|
-
Number.parseInt(match[2], 10),
|
|
3980
|
-
Number.parseInt(match[3], 10)
|
|
3981
|
-
];
|
|
3982
|
-
}
|
|
3983
|
-
|
|
3984
|
-
// _apps/@onklave/agent-cli/src/services/cli-version.ts
|
|
3985
|
-
import * as fs6 from "node:fs";
|
|
3986
|
-
import * as path7 from "node:path";
|
|
3987
|
-
import { fileURLToPath } from "node:url";
|
|
3988
|
-
function readPackageVersion() {
|
|
3989
|
-
try {
|
|
3990
|
-
const moduleDir = path7.dirname(fileURLToPath(import.meta.url));
|
|
3991
|
-
const candidates = [
|
|
3992
|
-
path7.join(moduleDir, "package.json"),
|
|
3993
|
-
// bundled: dist root
|
|
3994
|
-
path7.join(moduleDir, "..", "package.json"),
|
|
3995
|
-
path7.join(moduleDir, "..", "..", "package.json"),
|
|
3996
|
-
// dev: src/services -> root
|
|
3997
|
-
path7.join(moduleDir, "..", "..", "..", "package.json")
|
|
3998
|
-
];
|
|
3999
|
-
for (const p of candidates) {
|
|
4000
|
-
if (!fs6.existsSync(p)) continue;
|
|
4001
|
-
const pkg = JSON.parse(fs6.readFileSync(p, "utf8"));
|
|
4002
|
-
if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
|
|
4003
|
-
}
|
|
4004
|
-
} catch {
|
|
4005
|
-
}
|
|
4006
|
-
return null;
|
|
4007
|
-
}
|
|
4008
|
-
function getCurrentVersion() {
|
|
4009
|
-
return readPackageVersion() ?? "0.0.0";
|
|
4010
|
-
}
|
|
4011
4178
|
|
|
4012
4179
|
// _apps/@onklave/agent-cli/src/services/daemon-state.service.ts
|
|
4013
|
-
import * as
|
|
4014
|
-
import * as
|
|
4015
|
-
import * as
|
|
4180
|
+
import * as fs9 from "fs";
|
|
4181
|
+
import * as path9 from "path";
|
|
4182
|
+
import * as os7 from "os";
|
|
4016
4183
|
var VALID_TRANSITIONS = {
|
|
4017
4184
|
installing: ["registered"],
|
|
4018
4185
|
registered: ["starting"],
|
|
@@ -4023,10 +4190,10 @@ var VALID_TRANSITIONS = {
|
|
|
4023
4190
|
stopped: ["starting"]
|
|
4024
4191
|
};
|
|
4025
4192
|
function defaultStateFilePath() {
|
|
4026
|
-
return
|
|
4193
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.state.json");
|
|
4027
4194
|
}
|
|
4028
4195
|
function defaultPidFilePath() {
|
|
4029
|
-
return
|
|
4196
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.pid");
|
|
4030
4197
|
}
|
|
4031
4198
|
var DaemonStateError = class extends Error {
|
|
4032
4199
|
constructor(message) {
|
|
@@ -4050,8 +4217,8 @@ var DaemonStateService = class {
|
|
|
4050
4217
|
*/
|
|
4051
4218
|
static readPersisted(stateFile = defaultStateFilePath()) {
|
|
4052
4219
|
try {
|
|
4053
|
-
if (!
|
|
4054
|
-
const raw =
|
|
4220
|
+
if (!fs9.existsSync(stateFile)) return null;
|
|
4221
|
+
const raw = fs9.readFileSync(stateFile, "utf8");
|
|
4055
4222
|
const parsed = JSON.parse(raw);
|
|
4056
4223
|
if (!parsed.state || !parsed.enteredAt) return null;
|
|
4057
4224
|
return parsed;
|
|
@@ -4101,8 +4268,8 @@ var DaemonStateService = class {
|
|
|
4101
4268
|
}
|
|
4102
4269
|
}
|
|
4103
4270
|
persist(reason) {
|
|
4104
|
-
const dir =
|
|
4105
|
-
|
|
4271
|
+
const dir = path9.dirname(this.stateFile);
|
|
4272
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
4106
4273
|
const payload = {
|
|
4107
4274
|
state: this.current,
|
|
4108
4275
|
enteredAt: this.enteredAt.toISOString(),
|
|
@@ -4112,8 +4279,8 @@ var DaemonStateService = class {
|
|
|
4112
4279
|
...this.latestRuntime ? { runtime: this.latestRuntime } : {}
|
|
4113
4280
|
};
|
|
4114
4281
|
const tmp = `${this.stateFile}.tmp`;
|
|
4115
|
-
|
|
4116
|
-
|
|
4282
|
+
fs9.writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
4283
|
+
fs9.renameSync(tmp, this.stateFile);
|
|
4117
4284
|
}
|
|
4118
4285
|
/**
|
|
4119
4286
|
* Publish the latest runtime snapshot. Persists to the state file
|
|
@@ -4131,7 +4298,7 @@ var DaemonStateService = class {
|
|
|
4131
4298
|
*/
|
|
4132
4299
|
clearPersisted() {
|
|
4133
4300
|
try {
|
|
4134
|
-
|
|
4301
|
+
fs9.unlinkSync(this.stateFile);
|
|
4135
4302
|
} catch {
|
|
4136
4303
|
}
|
|
4137
4304
|
}
|
|
@@ -4375,7 +4542,9 @@ async function daemonStart() {
|
|
|
4375
4542
|
platformUrl,
|
|
4376
4543
|
deviceToken: creds.deviceToken,
|
|
4377
4544
|
machineId: creds.machineId,
|
|
4378
|
-
getActiveSessionCount: () => claimHandler.getActiveSessionCount()
|
|
4545
|
+
getActiveSessionCount: () => claimHandler.getActiveSessionCount(),
|
|
4546
|
+
cliVersion: readPackageVersion() ?? "0.0.0",
|
|
4547
|
+
posture: collectPosture()
|
|
4379
4548
|
});
|
|
4380
4549
|
const drainAndExit = async (reason) => {
|
|
4381
4550
|
if (shuttingDown) return;
|
|
@@ -4438,6 +4607,35 @@ async function daemonStart() {
|
|
|
4438
4607
|
}
|
|
4439
4608
|
console.log("Connected.");
|
|
4440
4609
|
claimHandler.attachToSocket(comms.inner());
|
|
4610
|
+
const upgradeService = new DaemonUpgradeService({
|
|
4611
|
+
allowAutoUpgrade: !!process.env["ONKLAVE_ALLOW_AUTO_UPGRADE"],
|
|
4612
|
+
currentVersion: readPackageVersion() ?? "0.0.0",
|
|
4613
|
+
runInstall: async () => {
|
|
4614
|
+
const { execFileSync } = __require("child_process");
|
|
4615
|
+
execFileSync("npm", ["install", "-g", "@onklave/agent-cli@latest"], {
|
|
4616
|
+
stdio: "pipe",
|
|
4617
|
+
timeout: 18e4
|
|
4618
|
+
});
|
|
4619
|
+
const v = execFileSync(
|
|
4620
|
+
"npm",
|
|
4621
|
+
["view", "@onklave/agent-cli@latest", "version"],
|
|
4622
|
+
{ stdio: "pipe", timeout: 3e4 }
|
|
4623
|
+
).toString().trim();
|
|
4624
|
+
return v || "latest";
|
|
4625
|
+
},
|
|
4626
|
+
requestRestart: () => void drainAndExit("upgrade"),
|
|
4627
|
+
audit: (action, details) => auditStreamer.record({
|
|
4628
|
+
sessionId: creds.machineId,
|
|
4629
|
+
type: "daemon_lifecycle" /* DAEMON_LIFECYCLE */,
|
|
4630
|
+
action,
|
|
4631
|
+
details,
|
|
4632
|
+
outcome: action === "upgrade_failed" ? "failure" : "success"
|
|
4633
|
+
}),
|
|
4634
|
+
log: (m) => console.log(m)
|
|
4635
|
+
});
|
|
4636
|
+
comms.inner().onUpgradeRequest(() => {
|
|
4637
|
+
void upgradeService.handle();
|
|
4638
|
+
});
|
|
4441
4639
|
await stateService.transition("online");
|
|
4442
4640
|
heartbeat.start();
|
|
4443
4641
|
resourceSampler.start();
|
|
@@ -4587,7 +4785,7 @@ function transitionToAction(next) {
|
|
|
4587
4785
|
}
|
|
4588
4786
|
function readPid(pidFile) {
|
|
4589
4787
|
try {
|
|
4590
|
-
const raw =
|
|
4788
|
+
const raw = fs10.readFileSync(pidFile, "utf8").trim();
|
|
4591
4789
|
const parsed = Number.parseInt(raw, 10);
|
|
4592
4790
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
4593
4791
|
} catch {
|
|
@@ -4596,12 +4794,12 @@ function readPid(pidFile) {
|
|
|
4596
4794
|
}
|
|
4597
4795
|
function writePid(pidFile) {
|
|
4598
4796
|
const dir = pidFile.replace(/\/[^/]+$/, "");
|
|
4599
|
-
|
|
4600
|
-
|
|
4797
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4798
|
+
fs10.writeFileSync(pidFile, String(process.pid), { mode: 384 });
|
|
4601
4799
|
}
|
|
4602
4800
|
function removePid(pidFile) {
|
|
4603
4801
|
try {
|
|
4604
|
-
|
|
4802
|
+
fs10.unlinkSync(pidFile);
|
|
4605
4803
|
} catch {
|
|
4606
4804
|
}
|
|
4607
4805
|
}
|
|
@@ -4645,15 +4843,15 @@ var COMMANDS = {
|
|
|
4645
4843
|
};
|
|
4646
4844
|
|
|
4647
4845
|
// _apps/@onklave/agent-cli/src/services/update-notifier.service.ts
|
|
4648
|
-
import * as
|
|
4649
|
-
import * as
|
|
4650
|
-
import * as
|
|
4846
|
+
import * as fs11 from "node:fs";
|
|
4847
|
+
import * as path10 from "node:path";
|
|
4848
|
+
import * as os8 from "node:os";
|
|
4651
4849
|
import { createInterface } from "node:readline/promises";
|
|
4652
4850
|
import { spawnSync } from "node:child_process";
|
|
4653
4851
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4654
4852
|
var FETCH_DEADLINE_MS = 1500;
|
|
4655
|
-
var CACHE_PATH =
|
|
4656
|
-
|
|
4853
|
+
var CACHE_PATH = path10.join(
|
|
4854
|
+
os8.homedir(),
|
|
4657
4855
|
".config",
|
|
4658
4856
|
"onklave",
|
|
4659
4857
|
"update-check.json"
|
|
@@ -4732,7 +4930,7 @@ async function maybeNotifyUpdate(deps = {}) {
|
|
|
4732
4930
|
}
|
|
4733
4931
|
}
|
|
4734
4932
|
function withTimeout(promise, ms) {
|
|
4735
|
-
return new Promise((
|
|
4933
|
+
return new Promise((resolve4, reject) => {
|
|
4736
4934
|
const timer = setTimeout(
|
|
4737
4935
|
() => reject(new Error("update check timed out")),
|
|
4738
4936
|
ms
|
|
@@ -4741,7 +4939,7 @@ function withTimeout(promise, ms) {
|
|
|
4741
4939
|
promise.then(
|
|
4742
4940
|
(value) => {
|
|
4743
4941
|
clearTimeout(timer);
|
|
4744
|
-
|
|
4942
|
+
resolve4(value);
|
|
4745
4943
|
},
|
|
4746
4944
|
(err) => {
|
|
4747
4945
|
clearTimeout(timer);
|
|
@@ -4763,16 +4961,16 @@ function isCacheFresh(cache, nowMs) {
|
|
|
4763
4961
|
}
|
|
4764
4962
|
function readCache() {
|
|
4765
4963
|
try {
|
|
4766
|
-
if (!
|
|
4767
|
-
return JSON.parse(
|
|
4964
|
+
if (!fs11.existsSync(CACHE_PATH)) return {};
|
|
4965
|
+
return JSON.parse(fs11.readFileSync(CACHE_PATH, "utf8"));
|
|
4768
4966
|
} catch {
|
|
4769
4967
|
return {};
|
|
4770
4968
|
}
|
|
4771
4969
|
}
|
|
4772
4970
|
function writeCache(cache) {
|
|
4773
4971
|
try {
|
|
4774
|
-
|
|
4775
|
-
|
|
4972
|
+
fs11.mkdirSync(path10.dirname(CACHE_PATH), { recursive: true, mode: 448 });
|
|
4973
|
+
fs11.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), "utf8");
|
|
4776
4974
|
} catch {
|
|
4777
4975
|
}
|
|
4778
4976
|
}
|