@onklave/agent-cli 0.1.45 → 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 +374 -190
- 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, path11, body, extraHeaders) {
|
|
778
|
+
const url = `${this.baseUrl}${GATEWAY_SERVICE_PREFIX}${path11}`;
|
|
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,8 +2824,8 @@ 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";
|
|
2603
2829
|
import { execSync } from "child_process";
|
|
2604
2830
|
async function doctorCommand() {
|
|
2605
2831
|
console.log("Onklave Agent CLI \u2014 Doctor\n");
|
|
@@ -2707,10 +2933,10 @@ function checkNodeVersion() {
|
|
|
2707
2933
|
};
|
|
2708
2934
|
}
|
|
2709
2935
|
function checkProjectConfig() {
|
|
2710
|
-
const configPath =
|
|
2711
|
-
if (
|
|
2936
|
+
const configPath = path6.join(process.cwd(), ".onklave.json");
|
|
2937
|
+
if (fs6.existsSync(configPath)) {
|
|
2712
2938
|
try {
|
|
2713
|
-
const raw =
|
|
2939
|
+
const raw = fs6.readFileSync(configPath, "utf-8");
|
|
2714
2940
|
JSON.parse(raw);
|
|
2715
2941
|
return {
|
|
2716
2942
|
name: "Project config",
|
|
@@ -2760,18 +2986,18 @@ async function checkWebSocket() {
|
|
|
2760
2986
|
}
|
|
2761
2987
|
|
|
2762
2988
|
// _apps/@onklave/agent-cli/src/commands/init.command.ts
|
|
2763
|
-
import * as
|
|
2764
|
-
import * as
|
|
2989
|
+
import * as fs7 from "fs";
|
|
2990
|
+
import * as path7 from "path";
|
|
2765
2991
|
async function initCommand() {
|
|
2766
|
-
const configPath =
|
|
2767
|
-
if (
|
|
2992
|
+
const configPath = path7.join(process.cwd(), ".onklave.json");
|
|
2993
|
+
if (fs7.existsSync(configPath)) {
|
|
2768
2994
|
console.error("Error: .onklave.json already exists in this directory.");
|
|
2769
2995
|
console.log("To overwrite, delete the existing file first.");
|
|
2770
2996
|
process.exitCode = 1;
|
|
2771
2997
|
return;
|
|
2772
2998
|
}
|
|
2773
2999
|
const template = {
|
|
2774
|
-
project:
|
|
3000
|
+
project: path7.basename(process.cwd()),
|
|
2775
3001
|
org: "",
|
|
2776
3002
|
description: "",
|
|
2777
3003
|
defaults: {
|
|
@@ -2806,7 +3032,7 @@ async function initCommand() {
|
|
|
2806
3032
|
system_prompt_append: ""
|
|
2807
3033
|
}
|
|
2808
3034
|
};
|
|
2809
|
-
|
|
3035
|
+
fs7.writeFileSync(
|
|
2810
3036
|
configPath,
|
|
2811
3037
|
JSON.stringify(template, null, 2) + "\n",
|
|
2812
3038
|
"utf-8"
|
|
@@ -2893,7 +3119,7 @@ async function configCommand(args) {
|
|
|
2893
3119
|
}
|
|
2894
3120
|
|
|
2895
3121
|
// _apps/@onklave/agent-cli/src/commands/register.command.ts
|
|
2896
|
-
import * as
|
|
3122
|
+
import * as os4 from "os";
|
|
2897
3123
|
async function registerCommand(args) {
|
|
2898
3124
|
const { flags } = parseArgs(args);
|
|
2899
3125
|
const refresh = flags["refresh"] === true;
|
|
@@ -2940,11 +3166,13 @@ Machine ${creds.machineId} updated successfully.`);
|
|
|
2940
3166
|
return;
|
|
2941
3167
|
}
|
|
2942
3168
|
const metadata = {
|
|
2943
|
-
hostname:
|
|
2944
|
-
os: `${
|
|
2945
|
-
arch:
|
|
3169
|
+
hostname: os4.hostname(),
|
|
3170
|
+
os: `${os4.platform()} ${os4.release()}`,
|
|
3171
|
+
arch: os4.arch(),
|
|
2946
3172
|
nodeVersion: process.version,
|
|
2947
|
-
|
|
3173
|
+
cliVersion: getCurrentVersion(),
|
|
3174
|
+
capabilities: detectCapabilities(),
|
|
3175
|
+
posture: collectPosture()
|
|
2948
3176
|
};
|
|
2949
3177
|
try {
|
|
2950
3178
|
console.log("Registering machine with Onklave platform...");
|
|
@@ -3035,7 +3263,7 @@ async function logsCommand(args) {
|
|
|
3035
3263
|
}
|
|
3036
3264
|
|
|
3037
3265
|
// _apps/@onklave/agent-cli/src/commands/daemon.command.ts
|
|
3038
|
-
import * as
|
|
3266
|
+
import * as fs10 from "fs";
|
|
3039
3267
|
|
|
3040
3268
|
// _apps/@onklave/agent-cli/src/services/daemon-comms.service.ts
|
|
3041
3269
|
var DaemonCommsService = class {
|
|
@@ -3109,7 +3337,7 @@ var DaemonCommsService = class {
|
|
|
3109
3337
|
}
|
|
3110
3338
|
};
|
|
3111
3339
|
function defaultSleep(ms) {
|
|
3112
|
-
return new Promise((
|
|
3340
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
3113
3341
|
}
|
|
3114
3342
|
|
|
3115
3343
|
// _apps/@onklave/agent-cli/src/services/daemon-claim-handler.service.ts
|
|
@@ -3462,8 +3690,8 @@ var PlatformBrokerClient = class {
|
|
|
3462
3690
|
params
|
|
3463
3691
|
);
|
|
3464
3692
|
}
|
|
3465
|
-
async request(method,
|
|
3466
|
-
const url = `${this.baseUrl}/agent-orchestration${
|
|
3693
|
+
async request(method, path11, body) {
|
|
3694
|
+
const url = `${this.baseUrl}/agent-orchestration${path11}`;
|
|
3467
3695
|
const response = await fetch(url, {
|
|
3468
3696
|
method,
|
|
3469
3697
|
headers: {
|
|
@@ -3499,9 +3727,9 @@ var PlatformBrokerClient = class {
|
|
|
3499
3727
|
|
|
3500
3728
|
// _apps/@onklave/agent-cli/src/services/work-item-runner.service.ts
|
|
3501
3729
|
import { spawn as spawn2 } from "child_process";
|
|
3502
|
-
import * as
|
|
3503
|
-
import * as
|
|
3504
|
-
import * as
|
|
3730
|
+
import * as fs8 from "fs";
|
|
3731
|
+
import * as os5 from "os";
|
|
3732
|
+
import * as path8 from "path";
|
|
3505
3733
|
var WorkItemRunner = class {
|
|
3506
3734
|
constructor(opts = {}) {
|
|
3507
3735
|
this.sessionManager = opts.sessionManager ?? new SessionManager();
|
|
@@ -3522,10 +3750,10 @@ var WorkItemRunner = class {
|
|
|
3522
3750
|
summary: `Work item ${workItem.id} has no clonable repo (project.repos[0].url missing).`
|
|
3523
3751
|
};
|
|
3524
3752
|
}
|
|
3525
|
-
const workDir =
|
|
3526
|
-
|
|
3753
|
+
const workDir = fs8.mkdtempSync(
|
|
3754
|
+
path8.join(os5.tmpdir(), `onklave-pickup-${sessionId}-`)
|
|
3527
3755
|
);
|
|
3528
|
-
const repoDir =
|
|
3756
|
+
const repoDir = path8.join(workDir, sanitizeName(repo.name) || "repo");
|
|
3529
3757
|
const branchName = `onklave/work-item/${workItem.id}`;
|
|
3530
3758
|
try {
|
|
3531
3759
|
await this.git(["clone", repo.url, repoDir], workDir);
|
|
@@ -3577,7 +3805,7 @@ var WorkItemRunner = class {
|
|
|
3577
3805
|
systemPromptAppend: null
|
|
3578
3806
|
};
|
|
3579
3807
|
let output = "";
|
|
3580
|
-
const exitCode = await new Promise((
|
|
3808
|
+
const exitCode = await new Promise((resolve4) => {
|
|
3581
3809
|
const timeout = setTimeout(() => {
|
|
3582
3810
|
void this.sessionManager.stopSession(sessionId).catch(() => void 0);
|
|
3583
3811
|
}, this.timeoutSeconds * 1e3);
|
|
@@ -3590,7 +3818,7 @@ var WorkItemRunner = class {
|
|
|
3590
3818
|
},
|
|
3591
3819
|
onExit: (code) => {
|
|
3592
3820
|
clearTimeout(timeout);
|
|
3593
|
-
|
|
3821
|
+
resolve4(code);
|
|
3594
3822
|
}
|
|
3595
3823
|
});
|
|
3596
3824
|
});
|
|
@@ -3623,12 +3851,12 @@ function sanitizeName(name) {
|
|
|
3623
3851
|
}
|
|
3624
3852
|
function cleanup(dir) {
|
|
3625
3853
|
try {
|
|
3626
|
-
|
|
3854
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
3627
3855
|
} catch {
|
|
3628
3856
|
}
|
|
3629
3857
|
}
|
|
3630
3858
|
function defaultGit(args, cwd) {
|
|
3631
|
-
return new Promise((
|
|
3859
|
+
return new Promise((resolve4, reject) => {
|
|
3632
3860
|
const child = spawn2("git", args, {
|
|
3633
3861
|
cwd,
|
|
3634
3862
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3640,7 +3868,7 @@ function defaultGit(args, cwd) {
|
|
|
3640
3868
|
child.on("error", (err) => reject(err));
|
|
3641
3869
|
child.on("exit", (code) => {
|
|
3642
3870
|
if (code === 0) {
|
|
3643
|
-
|
|
3871
|
+
resolve4();
|
|
3644
3872
|
} else {
|
|
3645
3873
|
reject(new Error(`git ${args[0]} exited ${code}: ${stderr.trim()}`));
|
|
3646
3874
|
}
|
|
@@ -3649,7 +3877,7 @@ function defaultGit(args, cwd) {
|
|
|
3649
3877
|
}
|
|
3650
3878
|
|
|
3651
3879
|
// _apps/@onklave/agent-cli/src/services/daemon-resource-sampler.service.ts
|
|
3652
|
-
import * as
|
|
3880
|
+
import * as os6 from "os";
|
|
3653
3881
|
var DEFAULT_SAMPLE_INTERVAL_MS = 5e3;
|
|
3654
3882
|
var DEFAULT_WINDOW_MS = 6e4;
|
|
3655
3883
|
var DaemonResourceSampler = class {
|
|
@@ -3717,8 +3945,8 @@ var DaemonResourceSampler = class {
|
|
|
3717
3945
|
}
|
|
3718
3946
|
};
|
|
3719
3947
|
function defaultCpuPercent() {
|
|
3720
|
-
const cpus2 =
|
|
3721
|
-
const load1m =
|
|
3948
|
+
const cpus2 = os6.cpus().length || 1;
|
|
3949
|
+
const load1m = os6.loadavg()[0];
|
|
3722
3950
|
return load1m / cpus2 * 100;
|
|
3723
3951
|
}
|
|
3724
3952
|
function defaultMemoryRss() {
|
|
@@ -3749,7 +3977,7 @@ var DaemonSpawner = class {
|
|
|
3749
3977
|
orgId: req.orgId,
|
|
3750
3978
|
systemPromptAppend: null
|
|
3751
3979
|
};
|
|
3752
|
-
return new Promise((
|
|
3980
|
+
return new Promise((resolve4, reject) => {
|
|
3753
3981
|
void this.sessionManager.spawnSession(req.sessionId, config, {
|
|
3754
3982
|
onStdout: (data) => {
|
|
3755
3983
|
process.stdout.write(data);
|
|
@@ -3759,7 +3987,7 @@ var DaemonSpawner = class {
|
|
|
3759
3987
|
},
|
|
3760
3988
|
onExit: (code, signal) => {
|
|
3761
3989
|
if (code === 0) {
|
|
3762
|
-
|
|
3990
|
+
resolve4();
|
|
3763
3991
|
} else {
|
|
3764
3992
|
reject(
|
|
3765
3993
|
new Error(
|
|
@@ -3910,123 +4138,48 @@ var DaemonTokenRefresher = class {
|
|
|
3910
4138
|
}
|
|
3911
4139
|
};
|
|
3912
4140
|
|
|
3913
|
-
// _apps/@onklave/agent-cli/src/services/daemon-
|
|
3914
|
-
var
|
|
3915
|
-
var PACKAGE_NAME = "@onklave/agent-cli";
|
|
3916
|
-
var DaemonUpdateChecker = class {
|
|
4141
|
+
// _apps/@onklave/agent-cli/src/services/daemon-upgrade.service.ts
|
|
4142
|
+
var DaemonUpgradeService = class {
|
|
3917
4143
|
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;
|
|
4144
|
+
this.opts = opts;
|
|
3924
4145
|
}
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
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 });
|
|
3932
4159
|
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)`
|
|
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.`
|
|
3952
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}`);
|
|
3953
4175
|
}
|
|
3954
4176
|
}
|
|
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
4177
|
};
|
|
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
4178
|
|
|
4026
4179
|
// _apps/@onklave/agent-cli/src/services/daemon-state.service.ts
|
|
4027
|
-
import * as
|
|
4028
|
-
import * as
|
|
4029
|
-
import * as
|
|
4180
|
+
import * as fs9 from "fs";
|
|
4181
|
+
import * as path9 from "path";
|
|
4182
|
+
import * as os7 from "os";
|
|
4030
4183
|
var VALID_TRANSITIONS = {
|
|
4031
4184
|
installing: ["registered"],
|
|
4032
4185
|
registered: ["starting"],
|
|
@@ -4037,10 +4190,10 @@ var VALID_TRANSITIONS = {
|
|
|
4037
4190
|
stopped: ["starting"]
|
|
4038
4191
|
};
|
|
4039
4192
|
function defaultStateFilePath() {
|
|
4040
|
-
return
|
|
4193
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.state.json");
|
|
4041
4194
|
}
|
|
4042
4195
|
function defaultPidFilePath() {
|
|
4043
|
-
return
|
|
4196
|
+
return path9.join(os7.homedir(), ".config", "onklave", "daemon.pid");
|
|
4044
4197
|
}
|
|
4045
4198
|
var DaemonStateError = class extends Error {
|
|
4046
4199
|
constructor(message) {
|
|
@@ -4064,8 +4217,8 @@ var DaemonStateService = class {
|
|
|
4064
4217
|
*/
|
|
4065
4218
|
static readPersisted(stateFile = defaultStateFilePath()) {
|
|
4066
4219
|
try {
|
|
4067
|
-
if (!
|
|
4068
|
-
const raw =
|
|
4220
|
+
if (!fs9.existsSync(stateFile)) return null;
|
|
4221
|
+
const raw = fs9.readFileSync(stateFile, "utf8");
|
|
4069
4222
|
const parsed = JSON.parse(raw);
|
|
4070
4223
|
if (!parsed.state || !parsed.enteredAt) return null;
|
|
4071
4224
|
return parsed;
|
|
@@ -4115,8 +4268,8 @@ var DaemonStateService = class {
|
|
|
4115
4268
|
}
|
|
4116
4269
|
}
|
|
4117
4270
|
persist(reason) {
|
|
4118
|
-
const dir =
|
|
4119
|
-
|
|
4271
|
+
const dir = path9.dirname(this.stateFile);
|
|
4272
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
4120
4273
|
const payload = {
|
|
4121
4274
|
state: this.current,
|
|
4122
4275
|
enteredAt: this.enteredAt.toISOString(),
|
|
@@ -4126,8 +4279,8 @@ var DaemonStateService = class {
|
|
|
4126
4279
|
...this.latestRuntime ? { runtime: this.latestRuntime } : {}
|
|
4127
4280
|
};
|
|
4128
4281
|
const tmp = `${this.stateFile}.tmp`;
|
|
4129
|
-
|
|
4130
|
-
|
|
4282
|
+
fs9.writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
4283
|
+
fs9.renameSync(tmp, this.stateFile);
|
|
4131
4284
|
}
|
|
4132
4285
|
/**
|
|
4133
4286
|
* Publish the latest runtime snapshot. Persists to the state file
|
|
@@ -4145,7 +4298,7 @@ var DaemonStateService = class {
|
|
|
4145
4298
|
*/
|
|
4146
4299
|
clearPersisted() {
|
|
4147
4300
|
try {
|
|
4148
|
-
|
|
4301
|
+
fs9.unlinkSync(this.stateFile);
|
|
4149
4302
|
} catch {
|
|
4150
4303
|
}
|
|
4151
4304
|
}
|
|
@@ -4389,7 +4542,9 @@ async function daemonStart() {
|
|
|
4389
4542
|
platformUrl,
|
|
4390
4543
|
deviceToken: creds.deviceToken,
|
|
4391
4544
|
machineId: creds.machineId,
|
|
4392
|
-
getActiveSessionCount: () => claimHandler.getActiveSessionCount()
|
|
4545
|
+
getActiveSessionCount: () => claimHandler.getActiveSessionCount(),
|
|
4546
|
+
cliVersion: readPackageVersion() ?? "0.0.0",
|
|
4547
|
+
posture: collectPosture()
|
|
4393
4548
|
});
|
|
4394
4549
|
const drainAndExit = async (reason) => {
|
|
4395
4550
|
if (shuttingDown) return;
|
|
@@ -4452,6 +4607,35 @@ async function daemonStart() {
|
|
|
4452
4607
|
}
|
|
4453
4608
|
console.log("Connected.");
|
|
4454
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
|
+
});
|
|
4455
4639
|
await stateService.transition("online");
|
|
4456
4640
|
heartbeat.start();
|
|
4457
4641
|
resourceSampler.start();
|
|
@@ -4601,7 +4785,7 @@ function transitionToAction(next) {
|
|
|
4601
4785
|
}
|
|
4602
4786
|
function readPid(pidFile) {
|
|
4603
4787
|
try {
|
|
4604
|
-
const raw =
|
|
4788
|
+
const raw = fs10.readFileSync(pidFile, "utf8").trim();
|
|
4605
4789
|
const parsed = Number.parseInt(raw, 10);
|
|
4606
4790
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
4607
4791
|
} catch {
|
|
@@ -4610,12 +4794,12 @@ function readPid(pidFile) {
|
|
|
4610
4794
|
}
|
|
4611
4795
|
function writePid(pidFile) {
|
|
4612
4796
|
const dir = pidFile.replace(/\/[^/]+$/, "");
|
|
4613
|
-
|
|
4614
|
-
|
|
4797
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4798
|
+
fs10.writeFileSync(pidFile, String(process.pid), { mode: 384 });
|
|
4615
4799
|
}
|
|
4616
4800
|
function removePid(pidFile) {
|
|
4617
4801
|
try {
|
|
4618
|
-
|
|
4802
|
+
fs10.unlinkSync(pidFile);
|
|
4619
4803
|
} catch {
|
|
4620
4804
|
}
|
|
4621
4805
|
}
|
|
@@ -4659,15 +4843,15 @@ var COMMANDS = {
|
|
|
4659
4843
|
};
|
|
4660
4844
|
|
|
4661
4845
|
// _apps/@onklave/agent-cli/src/services/update-notifier.service.ts
|
|
4662
|
-
import * as
|
|
4663
|
-
import * as
|
|
4664
|
-
import * as
|
|
4846
|
+
import * as fs11 from "node:fs";
|
|
4847
|
+
import * as path10 from "node:path";
|
|
4848
|
+
import * as os8 from "node:os";
|
|
4665
4849
|
import { createInterface } from "node:readline/promises";
|
|
4666
4850
|
import { spawnSync } from "node:child_process";
|
|
4667
4851
|
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4668
4852
|
var FETCH_DEADLINE_MS = 1500;
|
|
4669
|
-
var CACHE_PATH =
|
|
4670
|
-
|
|
4853
|
+
var CACHE_PATH = path10.join(
|
|
4854
|
+
os8.homedir(),
|
|
4671
4855
|
".config",
|
|
4672
4856
|
"onklave",
|
|
4673
4857
|
"update-check.json"
|
|
@@ -4746,7 +4930,7 @@ async function maybeNotifyUpdate(deps = {}) {
|
|
|
4746
4930
|
}
|
|
4747
4931
|
}
|
|
4748
4932
|
function withTimeout(promise, ms) {
|
|
4749
|
-
return new Promise((
|
|
4933
|
+
return new Promise((resolve4, reject) => {
|
|
4750
4934
|
const timer = setTimeout(
|
|
4751
4935
|
() => reject(new Error("update check timed out")),
|
|
4752
4936
|
ms
|
|
@@ -4755,7 +4939,7 @@ function withTimeout(promise, ms) {
|
|
|
4755
4939
|
promise.then(
|
|
4756
4940
|
(value) => {
|
|
4757
4941
|
clearTimeout(timer);
|
|
4758
|
-
|
|
4942
|
+
resolve4(value);
|
|
4759
4943
|
},
|
|
4760
4944
|
(err) => {
|
|
4761
4945
|
clearTimeout(timer);
|
|
@@ -4777,16 +4961,16 @@ function isCacheFresh(cache, nowMs) {
|
|
|
4777
4961
|
}
|
|
4778
4962
|
function readCache() {
|
|
4779
4963
|
try {
|
|
4780
|
-
if (!
|
|
4781
|
-
return JSON.parse(
|
|
4964
|
+
if (!fs11.existsSync(CACHE_PATH)) return {};
|
|
4965
|
+
return JSON.parse(fs11.readFileSync(CACHE_PATH, "utf8"));
|
|
4782
4966
|
} catch {
|
|
4783
4967
|
return {};
|
|
4784
4968
|
}
|
|
4785
4969
|
}
|
|
4786
4970
|
function writeCache(cache) {
|
|
4787
4971
|
try {
|
|
4788
|
-
|
|
4789
|
-
|
|
4972
|
+
fs11.mkdirSync(path10.dirname(CACHE_PATH), { recursive: true, mode: 448 });
|
|
4973
|
+
fs11.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), "utf8");
|
|
4790
4974
|
} catch {
|
|
4791
4975
|
}
|
|
4792
4976
|
}
|