local-commander-agent 0.3.6 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -5
- package/bin/command-path.mjs +102 -0
- package/bin/localcommander.mjs +15 -0
- package/bin/monitor.mjs +131 -0
- package/package.json +1 -1
- package/runtime/JobHost.exe +0 -0
- package/runtime/LocalCommanderAgent.exe +0 -0
- package/runtime/OwnerBridge.exe +0 -0
- package/runtime/agent.mjs +334 -104
package/runtime/agent.mjs
CHANGED
|
@@ -2278,7 +2278,7 @@ var require_websocket = __commonJS({
|
|
|
2278
2278
|
var http = __require("http");
|
|
2279
2279
|
var net = __require("net");
|
|
2280
2280
|
var tls = __require("tls");
|
|
2281
|
-
var { randomBytes: randomBytes4, createHash:
|
|
2281
|
+
var { randomBytes: randomBytes4, createHash: createHash11 } = __require("crypto");
|
|
2282
2282
|
var { Duplex, Readable } = __require("stream");
|
|
2283
2283
|
var { URL: URL2 } = __require("url");
|
|
2284
2284
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2959,7 +2959,7 @@ var require_websocket = __commonJS({
|
|
|
2959
2959
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
2960
2960
|
return;
|
|
2961
2961
|
}
|
|
2962
|
-
const digest4 =
|
|
2962
|
+
const digest4 = createHash11("sha1").update(key + GUID).digest("base64");
|
|
2963
2963
|
if (res.headers["sec-websocket-accept"] !== digest4) {
|
|
2964
2964
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
2965
2965
|
return;
|
|
@@ -3240,7 +3240,7 @@ var require_stream = __commonJS({
|
|
|
3240
3240
|
};
|
|
3241
3241
|
duplex._final = function(callback) {
|
|
3242
3242
|
if (ws.readyState === ws.CONNECTING) {
|
|
3243
|
-
ws.once("open", function
|
|
3243
|
+
ws.once("open", function open8() {
|
|
3244
3244
|
duplex._final(callback);
|
|
3245
3245
|
});
|
|
3246
3246
|
return;
|
|
@@ -3261,7 +3261,7 @@ var require_stream = __commonJS({
|
|
|
3261
3261
|
};
|
|
3262
3262
|
duplex._write = function(chunk, encoding, callback) {
|
|
3263
3263
|
if (ws.readyState === ws.CONNECTING) {
|
|
3264
|
-
ws.once("open", function
|
|
3264
|
+
ws.once("open", function open8() {
|
|
3265
3265
|
duplex._write(chunk, encoding, callback);
|
|
3266
3266
|
});
|
|
3267
3267
|
return;
|
|
@@ -3328,7 +3328,7 @@ var require_websocket_server = __commonJS({
|
|
|
3328
3328
|
var EventEmitter = __require("events");
|
|
3329
3329
|
var http = __require("http");
|
|
3330
3330
|
var { Duplex } = __require("stream");
|
|
3331
|
-
var { createHash:
|
|
3331
|
+
var { createHash: createHash11 } = __require("crypto");
|
|
3332
3332
|
var extension2 = require_extension();
|
|
3333
3333
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3334
3334
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3635,7 +3635,7 @@ var require_websocket_server = __commonJS({
|
|
|
3635
3635
|
);
|
|
3636
3636
|
}
|
|
3637
3637
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3638
|
-
const digest4 =
|
|
3638
|
+
const digest4 = createHash11("sha1").update(key + GUID).digest("base64");
|
|
3639
3639
|
const headers = [
|
|
3640
3640
|
"HTTP/1.1 101 Switching Protocols",
|
|
3641
3641
|
"Upgrade: websocket",
|
|
@@ -3723,9 +3723,9 @@ var require_websocket_server = __commonJS({
|
|
|
3723
3723
|
});
|
|
3724
3724
|
|
|
3725
3725
|
// agent/src/main.ts
|
|
3726
|
-
import
|
|
3726
|
+
import path14 from "node:path";
|
|
3727
3727
|
import os3 from "node:os";
|
|
3728
|
-
import { mkdir as
|
|
3728
|
+
import { mkdir as mkdir6, open as open7, readFile as readFile5, rm as rm5 } from "node:fs/promises";
|
|
3729
3729
|
import { fileURLToPath } from "node:url";
|
|
3730
3730
|
|
|
3731
3731
|
// packages/operations/src/crypto.ts
|
|
@@ -4674,10 +4674,10 @@ function mergeDefs(...defs) {
|
|
|
4674
4674
|
function cloneDef(schema) {
|
|
4675
4675
|
return mergeDefs(schema._zod.def);
|
|
4676
4676
|
}
|
|
4677
|
-
function getElementAtPath(obj,
|
|
4678
|
-
if (!
|
|
4677
|
+
function getElementAtPath(obj, path15) {
|
|
4678
|
+
if (!path15)
|
|
4679
4679
|
return obj;
|
|
4680
|
-
return
|
|
4680
|
+
return path15.reduce((acc, key) => acc?.[key], obj);
|
|
4681
4681
|
}
|
|
4682
4682
|
function promiseAllObject(promisesObj) {
|
|
4683
4683
|
const keys = Object.keys(promisesObj);
|
|
@@ -5017,11 +5017,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
5017
5017
|
}
|
|
5018
5018
|
return false;
|
|
5019
5019
|
}
|
|
5020
|
-
function prefixIssues(
|
|
5020
|
+
function prefixIssues(path15, issues) {
|
|
5021
5021
|
return issues.map((iss) => {
|
|
5022
5022
|
var _a3;
|
|
5023
5023
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
5024
|
-
iss.path.unshift(
|
|
5024
|
+
iss.path.unshift(path15);
|
|
5025
5025
|
return iss;
|
|
5026
5026
|
});
|
|
5027
5027
|
}
|
|
@@ -5471,16 +5471,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5471
5471
|
}
|
|
5472
5472
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
5473
5473
|
const fieldErrors = { _errors: [] };
|
|
5474
|
-
const processError = (error63,
|
|
5474
|
+
const processError = (error63, path15 = []) => {
|
|
5475
5475
|
for (const issue2 of error63.issues) {
|
|
5476
5476
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5477
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5477
|
+
issue2.errors.map((issues) => processError({ issues }, [...path15, ...issue2.path]));
|
|
5478
5478
|
} else if (issue2.code === "invalid_key") {
|
|
5479
|
-
processError({ issues: issue2.issues }, [...
|
|
5479
|
+
processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
|
|
5480
5480
|
} else if (issue2.code === "invalid_element") {
|
|
5481
|
-
processError({ issues: issue2.issues }, [...
|
|
5481
|
+
processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
|
|
5482
5482
|
} else {
|
|
5483
|
-
const fullpath = [...
|
|
5483
|
+
const fullpath = [...path15, ...issue2.path];
|
|
5484
5484
|
if (fullpath.length === 0) {
|
|
5485
5485
|
fieldErrors._errors.push(mapper(issue2));
|
|
5486
5486
|
} else {
|
|
@@ -5519,17 +5519,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5519
5519
|
}
|
|
5520
5520
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
5521
5521
|
const result2 = { errors: [] };
|
|
5522
|
-
const processError = (error63,
|
|
5522
|
+
const processError = (error63, path15 = []) => {
|
|
5523
5523
|
var _a3;
|
|
5524
5524
|
for (const issue2 of error63.issues) {
|
|
5525
5525
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5526
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5526
|
+
issue2.errors.map((issues) => processError({ issues }, [...path15, ...issue2.path]));
|
|
5527
5527
|
} else if (issue2.code === "invalid_key") {
|
|
5528
|
-
processError({ issues: issue2.issues }, [...
|
|
5528
|
+
processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
|
|
5529
5529
|
} else if (issue2.code === "invalid_element") {
|
|
5530
|
-
processError({ issues: issue2.issues }, [...
|
|
5530
|
+
processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
|
|
5531
5531
|
} else {
|
|
5532
|
-
const fullpath = [...
|
|
5532
|
+
const fullpath = [...path15, ...issue2.path];
|
|
5533
5533
|
if (fullpath.length === 0) {
|
|
5534
5534
|
result2.errors.push(mapper(issue2));
|
|
5535
5535
|
continue;
|
|
@@ -5568,8 +5568,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
5568
5568
|
}
|
|
5569
5569
|
function toDotPath(_path) {
|
|
5570
5570
|
const segs = [];
|
|
5571
|
-
const
|
|
5572
|
-
for (const seg of
|
|
5571
|
+
const path15 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5572
|
+
for (const seg of path15) {
|
|
5573
5573
|
if (typeof seg === "number")
|
|
5574
5574
|
segs.push(`[${seg}]`);
|
|
5575
5575
|
else if (typeof seg === "symbol")
|
|
@@ -22671,13 +22671,13 @@ function resolveRef(ref, ctx) {
|
|
|
22671
22671
|
if (!ref.startsWith("#")) {
|
|
22672
22672
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
22673
22673
|
}
|
|
22674
|
-
const
|
|
22675
|
-
if (
|
|
22674
|
+
const path15 = ref.slice(1).split("/").filter(Boolean);
|
|
22675
|
+
if (path15.length === 0) {
|
|
22676
22676
|
return ctx.rootSchema;
|
|
22677
22677
|
}
|
|
22678
22678
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
22679
|
-
if (
|
|
22680
|
-
const key =
|
|
22679
|
+
if (path15[0] === defsKey) {
|
|
22680
|
+
const key = path15[1] === void 0 ? void 0 : decodeJSONPointerSegment(path15[1]);
|
|
22681
22681
|
if (!key || !ctx.defs[key]) {
|
|
22682
22682
|
throw new Error(`Reference not found: ${ref}`);
|
|
22683
22683
|
}
|
|
@@ -25288,12 +25288,21 @@ async function planPermissionChange(current2, programs, input2) {
|
|
|
25288
25288
|
workspace.permissions.writeFiles = change.enabled;
|
|
25289
25289
|
detail = "File writes: " + (change.enabled ? "ALLOW" : "DENY");
|
|
25290
25290
|
} else if (change.kind === "localhost") {
|
|
25291
|
+
if (workspace.fullAccess) {
|
|
25292
|
+
workspace.deniedLocalhostPorts = change.enabled ? (workspace.deniedLocalhostPorts ?? []).filter((port) => port !== change.port) : [.../* @__PURE__ */ new Set([...workspace.deniedLocalhostPorts ?? [], change.port])];
|
|
25293
|
+
if (workspace.deniedLocalhostPorts.length > 32) invalid2("PERMISSION_LIMIT");
|
|
25294
|
+
}
|
|
25291
25295
|
workspace.localhostPorts = change.enabled ? [.../* @__PURE__ */ new Set([...workspace.localhostPorts, change.port])].sort((a, b) => a - b) : workspace.localhostPorts.filter((port) => port !== change.port);
|
|
25292
25296
|
if (workspace.localhostPorts.length > 32) invalid2("PERMISSION_LIMIT");
|
|
25293
|
-
workspace.permissions.localhost = workspace.localhostPorts.length > 0;
|
|
25297
|
+
workspace.permissions.localhost = workspace.fullAccess === true || workspace.localhostPorts.length > 0;
|
|
25294
25298
|
resource = { rootHash: workspace.rootHash, kind: change.kind, port: change.port };
|
|
25295
25299
|
detail = "Localhost port " + change.port + ": " + (change.enabled ? "ALLOW" : "DENY");
|
|
25296
25300
|
} else {
|
|
25301
|
+
if (workspace.fullAccess) {
|
|
25302
|
+
const key = change.name.replace(/\.(exe|com|cmd|bat)$/i, "");
|
|
25303
|
+
workspace.deniedCommands = change.enabled ? (workspace.deniedCommands ?? []).filter((name2) => name2.replace(/\.(exe|com|cmd|bat)$/i, "") !== key) : [.../* @__PURE__ */ new Set([...workspace.deniedCommands ?? [], change.name])];
|
|
25304
|
+
if (workspace.deniedCommands.length > 32) invalid2("PERMISSION_LIMIT");
|
|
25305
|
+
}
|
|
25297
25306
|
let executableIdentity = null;
|
|
25298
25307
|
if (change.enabled) {
|
|
25299
25308
|
const executable = change.executable;
|
|
@@ -25307,7 +25316,7 @@ async function planPermissionChange(current2, programs, input2) {
|
|
|
25307
25316
|
if (workspace.allowedCommands.length > 32 || Object.keys(nextPrograms).length > 32) invalid2("PERMISSION_LIMIT");
|
|
25308
25317
|
executableIdentity = { executable: canonical2, dev: stat.dev, ino: stat.ino, size: stat.size, mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs };
|
|
25309
25318
|
} else workspace.allowedCommands = workspace.allowedCommands.filter((value) => value !== change.name);
|
|
25310
|
-
workspace.permissions.executeCommands = workspace.allowedCommands.length > 0;
|
|
25319
|
+
workspace.permissions.executeCommands = workspace.fullAccess === true || workspace.allowedCommands.length > 0;
|
|
25311
25320
|
resource = { rootHash: workspace.rootHash, kind: change.kind, name: change.name, executable: nextPrograms[change.name] ?? null, executableIdentity };
|
|
25312
25321
|
detail = "Command " + permissionLabel(change.name) + ": " + (change.enabled ? "ALLOW" : "DENY") + "\nExact executable: " + permissionLabel(nextPrograms[change.name] ?? "not registered") + nonAsciiNotice(nextPrograms[change.name]) + "\nA permitted program can access resources available to your Windows account, including outside this folder. This is not an OS sandbox.";
|
|
25313
25322
|
}
|
|
@@ -25679,9 +25688,12 @@ function validateState(value) {
|
|
|
25679
25688
|
const pendingWorkspaceRevokes = value.pendingWorkspaceRevokes ?? [];
|
|
25680
25689
|
if (!Array.isArray(pendingWorkspaceRevokes) || pendingWorkspaceRevokes.length > 32 || pendingWorkspaceRevokes.some((id) => typeof id !== "string" || !UUID.test(id) || id !== id.toLowerCase()) || new Set(pendingWorkspaceRevokes).size !== pendingWorkspaceRevokes.length) throw fail("STATE_CORRUPT");
|
|
25681
25690
|
if (!programs || typeof programs !== "object" || Array.isArray(programs) || Object.keys(programs).length > 32 || Object.entries(programs).some(([name2, executable]) => !/^[a-z0-9_.-]{1,64}$/.test(name2) || typeof executable !== "string" || !path5.isAbsolute(executable) || executable.length > 4096 || /[\u0000-\u001f]/.test(executable))) throw fail("STATE_CORRUPT");
|
|
25682
|
-
if (!Array.isArray(workspaces) || workspaces.length > 32 || workspaces.some((w) => !w || !UUID.test(w.workspaceGrantId) || typeof w.root !== "string" || !path5.isAbsolute(w.root) || w.root.length > 4096 || /[\u0000-\u001f]/.test(w.root) || !/^[a-f0-9]{64}$/.test(w.rootHash) || typeof w.displayName !== "string" || !w.displayName || w.displayName.length > 120 || w.policyVersion !== 1 || !w.permissions || w.permissions.readFiles !== true || typeof w.permissions.writeFiles !== "boolean" || typeof w.permissions.executeCommands !== "boolean" || typeof w.permissions.localhost !== "boolean" || !Array.isArray(w.allowedCommands) || w.allowedCommands.length > 32 || w.allowedCommands.some((name2) => typeof name2 !== "string" || !Object.hasOwn(programs, name2)) || new Set(w.allowedCommands).size !== w.allowedCommands.length || w.permissions.executeCommands !== w.allowedCommands.length > 0 || !Array.isArray(w.localhostPorts) || w.localhostPorts.length > 32 || w.localhostPorts.some((port) => !Number.isInteger(port) || port < 1 || port > 65535) || new Set(w.localhostPorts).size !== w.localhostPorts.length || w.permissions.localhost !== w.localhostPorts.length > 0)) throw fail("STATE_CORRUPT");
|
|
25691
|
+
if (!Array.isArray(workspaces) || workspaces.length > 32 || workspaces.some((w) => !w || !UUID.test(w.workspaceGrantId) || typeof w.root !== "string" || !path5.isAbsolute(w.root) || w.root.length > 4096 || /[\u0000-\u001f]/.test(w.root) || !/^[a-f0-9]{64}$/.test(w.rootHash) || typeof w.displayName !== "string" || !w.displayName || w.displayName.length > 120 || w.policyVersion !== 1 || !w.permissions || w.permissions.readFiles !== true || typeof w.permissions.writeFiles !== "boolean" || typeof w.permissions.executeCommands !== "boolean" || typeof w.permissions.localhost !== "boolean" || !Array.isArray(w.allowedCommands) || w.allowedCommands.length > 32 || w.allowedCommands.some((name2) => typeof name2 !== "string" || !Object.hasOwn(programs, name2)) || new Set(w.allowedCommands).size !== w.allowedCommands.length || w.permissions.executeCommands !== (w.fullAccess === true || w.allowedCommands.length > 0) || !Array.isArray(w.localhostPorts) || w.localhostPorts.length > 32 || w.localhostPorts.some((port) => !Number.isInteger(port) || port < 1 || port > 65535) || new Set(w.localhostPorts).size !== w.localhostPorts.length || w.permissions.localhost !== (w.fullAccess === true || w.localhostPorts.length > 0))) throw fail("STATE_CORRUPT");
|
|
25683
25692
|
if (new Set(workspaces.map((w) => w.workspaceGrantId)).size !== workspaces.length || new Set(workspaces.map((w) => w.rootHash)).size !== workspaces.length) throw fail("STATE_CORRUPT");
|
|
25684
25693
|
if (workspaces.some((w) => pendingWorkspaceRevokes.includes(w.workspaceGrantId))) throw fail("STATE_CORRUPT");
|
|
25694
|
+
for (const w of workspaces) {
|
|
25695
|
+
if (w.fullAccess !== void 0 && w.fullAccess !== true || w.deniedCommands !== void 0 && (!w.fullAccess || !Array.isArray(w.deniedCommands) || w.deniedCommands.length > 32 || w.deniedCommands.some((name2) => typeof name2 !== "string" || !/^[a-z0-9_.-]{1,64}$/.test(name2))) || w.deniedLocalhostPorts !== void 0 && (!w.fullAccess || !Array.isArray(w.deniedLocalhostPorts) || w.deniedLocalhostPorts.length > 32 || w.deniedLocalhostPorts.some((port) => !Number.isInteger(port) || port < 1 || port > 65535))) throw fail("STATE_CORRUPT");
|
|
25696
|
+
}
|
|
25685
25697
|
return { version: 1, publicKeyPem: value.publicKeyPem, privateKeyPem: value.privateKeyPem, fingerprint: value.fingerprint, dataKey: value.dataKey, ...enrollment ? { enrollment } : {}, workspaces, programs, pendingWorkspaceRevokes, ...value.permissionHistory ? { permissionHistory: permissionHistory.data } : {}, ...policyBundle ? { policyBundle } : {}, ...approvals?.success ? { approvals: approvals.data } : {}, ...value.guardRequired ? { guardRequired: true } : {}, ...guard ? { guard } : {} };
|
|
25686
25698
|
}
|
|
25687
25699
|
async function noSymlinkChain(target) {
|
|
@@ -26154,7 +26166,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
|
|
|
26154
26166
|
return this.run(async () => {
|
|
26155
26167
|
const state = await this.readState();
|
|
26156
26168
|
if (guardActive === false && state?.guardRequired) throw fail("GUARD_RESTART_REQUIRED");
|
|
26157
|
-
return
|
|
26169
|
+
return structuredClone(state?.workspaces ?? []);
|
|
26158
26170
|
});
|
|
26159
26171
|
}
|
|
26160
26172
|
loadPrograms() {
|
|
@@ -26178,7 +26190,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
|
|
|
26178
26190
|
const before = await this.rawHash();
|
|
26179
26191
|
state.workspaces = state.workspaces.filter((w) => w.workspaceGrantId !== id);
|
|
26180
26192
|
state.pendingWorkspaceRevokes = [...state.pendingWorkspaceRevokes ?? [], id];
|
|
26181
|
-
for (const name2 of Object.keys(state.programs ?? {})) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2))) delete state.programs[name2];
|
|
26193
|
+
for (const name2 of Object.keys(state.programs ?? {})) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2) || w.deniedCommands?.includes(name2))) delete state.programs[name2];
|
|
26182
26194
|
await this.writeState(state, before);
|
|
26183
26195
|
}));
|
|
26184
26196
|
}
|
|
@@ -26220,7 +26232,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
|
|
|
26220
26232
|
const before = await this.rawHash();
|
|
26221
26233
|
state.workspaces = state.workspaces.map((w) => w.workspaceGrantId === verified.workspace.workspaceGrantId ? verified.workspace : w);
|
|
26222
26234
|
state.programs = verified.programs;
|
|
26223
|
-
for (const name2 of Object.keys(state.programs)) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2))) delete state.programs[name2];
|
|
26235
|
+
for (const name2 of Object.keys(state.programs)) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2) || w.deniedCommands?.includes(name2))) delete state.programs[name2];
|
|
26224
26236
|
const record2 = {
|
|
26225
26237
|
id: randomUUID3(),
|
|
26226
26238
|
changedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -26267,7 +26279,8 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
|
|
|
26267
26279
|
rootHash,
|
|
26268
26280
|
displayName: (path5.basename(root) || root).slice(0, 120),
|
|
26269
26281
|
policyVersion: 1,
|
|
26270
|
-
|
|
26282
|
+
...selected.mode === "all-drives" ? { fullAccess: true } : {},
|
|
26283
|
+
permissions: { readFiles: true, writeFiles: true, executeCommands: selected.mode === "all-drives", localhost: selected.mode === "all-drives" },
|
|
26271
26284
|
allowedCommands: [],
|
|
26272
26285
|
localhostPorts: []
|
|
26273
26286
|
};
|
|
@@ -26323,7 +26336,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
|
|
|
26323
26336
|
// agent/src/tools.ts
|
|
26324
26337
|
import { constants as constants3 } from "node:fs";
|
|
26325
26338
|
import { lstat as lstat5, open as open4, readdir as readdir3, realpath as realpath5, rename as rename3, rm as rm3 } from "node:fs/promises";
|
|
26326
|
-
import
|
|
26339
|
+
import path10 from "node:path";
|
|
26327
26340
|
import { createHash as createHash9, randomUUID as randomUUID4 } from "node:crypto";
|
|
26328
26341
|
|
|
26329
26342
|
// src/guard.ts
|
|
@@ -26567,11 +26580,31 @@ function invokeRead(bridge, file2, maxBytes, prefix, signal) {
|
|
|
26567
26580
|
});
|
|
26568
26581
|
}
|
|
26569
26582
|
|
|
26583
|
+
// src/process.ts
|
|
26584
|
+
import { statSync } from "node:fs";
|
|
26585
|
+
import path8 from "node:path";
|
|
26586
|
+
var WINDOWS_LAUNCH_EXTENSIONS = [".com", ".exe", ".bat", ".cmd"];
|
|
26587
|
+
function resolveWindowsLauncher(commandName, env = process.env) {
|
|
26588
|
+
const extensions = (env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((ext) => ext.trim().toLowerCase()).filter((ext) => WINDOWS_LAUNCH_EXTENSIONS.includes(ext));
|
|
26589
|
+
for (const entry of (env.PATH ?? "").split(";")) {
|
|
26590
|
+
const dir = entry.trim().replace(/^"(.*)"$/, "$1");
|
|
26591
|
+
if (!path8.win32.isAbsolute(dir)) continue;
|
|
26592
|
+
for (const ext of extensions) {
|
|
26593
|
+
const file2 = path8.win32.join(dir, commandName + ext);
|
|
26594
|
+
try {
|
|
26595
|
+
if (statSync(file2).isFile()) return { file: file2, shell: ext === ".bat" || ext === ".cmd" };
|
|
26596
|
+
} catch {
|
|
26597
|
+
}
|
|
26598
|
+
}
|
|
26599
|
+
}
|
|
26600
|
+
return void 0;
|
|
26601
|
+
}
|
|
26602
|
+
|
|
26570
26603
|
// agent/src/job-supervisor.ts
|
|
26571
26604
|
import { spawn as spawn2 } from "node:child_process";
|
|
26572
|
-
import
|
|
26605
|
+
import path9 from "node:path";
|
|
26573
26606
|
function absolute(value) {
|
|
26574
|
-
return typeof value === "string" && value.length > 0 && value.length < 32767 && !value.includes("\0") &&
|
|
26607
|
+
return typeof value === "string" && value.length > 0 && value.length < 32767 && !value.includes("\0") && path9.isAbsolute(value) && path9.resolve(value) === value;
|
|
26575
26608
|
}
|
|
26576
26609
|
async function runSupervised(helper, request, options = {}) {
|
|
26577
26610
|
if (process.platform !== "win32") throw Error("WINDOWS_SUPERVISOR_REQUIRED");
|
|
@@ -26587,7 +26620,7 @@ async function runSupervised(helper, request, options = {}) {
|
|
|
26587
26620
|
let pid = null, outcome = null;
|
|
26588
26621
|
let buffer = "", failure5 = null, closed = false, cancelSent = false;
|
|
26589
26622
|
let killTimer, hardTimer;
|
|
26590
|
-
const child = spawn2(helper, [], { cwd:
|
|
26623
|
+
const child = spawn2(helper, [], { cwd: path9.dirname(helper), stdio: ["pipe", "pipe", "pipe"], windowsHide: true, shell: false, env: buildChildEnvironment(process.env) });
|
|
26591
26624
|
const cancel = () => {
|
|
26592
26625
|
if (closed || cancelSent) return;
|
|
26593
26626
|
cancelSent = true;
|
|
@@ -26741,7 +26774,7 @@ var NativeTools = class {
|
|
|
26741
26774
|
options;
|
|
26742
26775
|
async readFile(file2, maxBytes, signal) {
|
|
26743
26776
|
try {
|
|
26744
|
-
return await readBoundedFile(file2, maxBytes, this.options.ownerBridgePath ??
|
|
26777
|
+
return await readBoundedFile(file2, maxBytes, this.options.ownerBridgePath ?? path10.join(path10.dirname(this.options.helperPath), "OwnerBridge.exe"), signal);
|
|
26745
26778
|
} catch (error62) {
|
|
26746
26779
|
if (error62.code === "ENOENT") throw error62;
|
|
26747
26780
|
if (error62 instanceof Error && /^[A-Z_]{1,40}$/.test(error62.message)) fail2(error62.message);
|
|
@@ -26766,15 +26799,15 @@ var NativeTools = class {
|
|
|
26766
26799
|
try {
|
|
26767
26800
|
const canonical2 = await realpath5(workspace.root);
|
|
26768
26801
|
if (!equalPath3(canonical2, workspace.root) || hash5(process.platform === "win32" ? canonical2.toLowerCase() : canonical2) !== workspace.rootHash) fail2("WORKSPACE_CHANGED");
|
|
26769
|
-
const runtimeDirectory =
|
|
26802
|
+
const runtimeDirectory = path10.dirname(this.options.helperPath);
|
|
26770
26803
|
const protectedPaths = [
|
|
26771
26804
|
this.options.stateDirectory,
|
|
26772
26805
|
this.options.helperPath,
|
|
26773
|
-
...["agent.mjs", "node.exe", "OwnerBridge.exe", "LocalCommanderAgent.exe"].map((name2) =>
|
|
26806
|
+
...["agent.mjs", "node.exe", "OwnerBridge.exe", "LocalCommanderAgent.exe"].map((name2) => path10.join(runtimeDirectory, name2))
|
|
26774
26807
|
];
|
|
26775
26808
|
if (this.options.ownerBridgePath) protectedPaths.push(this.options.ownerBridgePath);
|
|
26776
|
-
if (process.platform === "win32" && process.env.LOCALAPPDATA &&
|
|
26777
|
-
protectedPaths.push(
|
|
26809
|
+
if (process.platform === "win32" && process.env.LOCALAPPDATA && path10.isAbsolute(process.env.LOCALAPPDATA)) {
|
|
26810
|
+
protectedPaths.push(path10.join(process.env.LOCALAPPDATA, "LocalCommander"));
|
|
26778
26811
|
}
|
|
26779
26812
|
guard = await PathGuard.create([canonical2], false, { protectedPaths });
|
|
26780
26813
|
} catch (e) {
|
|
@@ -26806,7 +26839,7 @@ var NativeTools = class {
|
|
|
26806
26839
|
const bytes3 = await this.readFile(filename, 1048576, signal), contents = utf8(bytes3), lines = contents.split(/\r?\n/);
|
|
26807
26840
|
const selected = lines.slice(first - 1, Math.min(last, lines.length)).join("\n");
|
|
26808
26841
|
if (Buffer.byteLength(selected) > 12e4) fail2("OUTPUT_LIMIT_EXCEEDED");
|
|
26809
|
-
return result({ path:
|
|
26842
|
+
return result({ path: path10.relative(workspace.root, filename), sha256: hash5(bytes3), startLine: first, endLine: Math.min(last, lines.length), totalLines: lines.length, content: selected, truncated: last < lines.length });
|
|
26810
26843
|
} };
|
|
26811
26844
|
}
|
|
26812
26845
|
if (call.name === "fs_write" || call.name === "fs_replace") {
|
|
@@ -26842,7 +26875,7 @@ var NativeTools = class {
|
|
|
26842
26875
|
}
|
|
26843
26876
|
const bytes3 = Buffer.from(contents, "utf8");
|
|
26844
26877
|
if (bytes3.length > MAX_FILE_WRITE_BYTES) fail2("FILE_WRITE_LIMIT");
|
|
26845
|
-
const temp =
|
|
26878
|
+
const temp = path10.join(path10.dirname(target), ".lc-write-" + randomUUID4() + ".tmp");
|
|
26846
26879
|
const handle = await open4(temp, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
26847
26880
|
try {
|
|
26848
26881
|
await handle.writeFile(bytes3);
|
|
@@ -26859,7 +26892,7 @@ var NativeTools = class {
|
|
|
26859
26892
|
await link(temp, target);
|
|
26860
26893
|
await rm3(temp);
|
|
26861
26894
|
}
|
|
26862
|
-
return result({ path:
|
|
26895
|
+
return result({ path: path10.relative(workspace.root, target), bytes: bytes3.length, sha256: hash5(bytes3), ...call.name === "fs_replace" ? { replacements } : {} });
|
|
26863
26896
|
} catch (e) {
|
|
26864
26897
|
await handle.close().catch(() => {
|
|
26865
26898
|
});
|
|
@@ -26880,12 +26913,12 @@ var NativeTools = class {
|
|
|
26880
26913
|
stopped(signal);
|
|
26881
26914
|
if (matches2.length >= call.args.maxResults || scanned >= 300) break;
|
|
26882
26915
|
try {
|
|
26883
|
-
const filename = await guard.resolveExisting(
|
|
26916
|
+
const filename = await guard.resolveExisting(path10.join(base, entry.path));
|
|
26884
26917
|
guard.assertSafeFile(filename);
|
|
26885
26918
|
const data = utf8(await this.readFile(filename, 256e3, signal));
|
|
26886
26919
|
scanned++;
|
|
26887
26920
|
const lines = data.split(/\r?\n/);
|
|
26888
|
-
for (let i = 0; i < lines.length && matches2.length < call.args.maxResults; i++) if ((call.args.caseSensitive ? lines[i] : lines[i].toLowerCase()).includes(query)) matches2.push({ path:
|
|
26921
|
+
for (let i = 0; i < lines.length && matches2.length < call.args.maxResults; i++) if ((call.args.caseSensitive ? lines[i] : lines[i].toLowerCase()).includes(query)) matches2.push({ path: path10.relative(workspace.root, filename), line: i + 1, text: lines[i].slice(0, 500) });
|
|
26889
26922
|
} catch (error62) {
|
|
26890
26923
|
if (error62 instanceof NativeToolError && ["FILE_READ_FAILED", "CANCELLED"].includes(error62.code)) throw error62;
|
|
26891
26924
|
}
|
|
@@ -26895,7 +26928,7 @@ var NativeTools = class {
|
|
|
26895
26928
|
}
|
|
26896
26929
|
if (call.name === "localhost_request") {
|
|
26897
26930
|
const url2 = new URL(call.args.url), port = Number(url2.port || (url2.protocol === "https:" ? 443 : 80));
|
|
26898
|
-
if (!["http:", "https:"].includes(url2.protocol) || !["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname) || url2.username || url2.password || url2.hash || !workspace.localhostPorts.includes(port) || this.options.protectedPorts?.includes(port)) fail2("LOCALHOST_NOT_ALLOWED");
|
|
26931
|
+
if (!["http:", "https:"].includes(url2.protocol) || !["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname) || url2.username || url2.password || url2.hash || (workspace.fullAccess ? workspace.deniedLocalhostPorts?.includes(port) : !workspace.localhostPorts.includes(port)) || this.options.protectedPorts?.includes(port)) fail2("LOCALHOST_NOT_ALLOWED");
|
|
26899
26932
|
const headers = {};
|
|
26900
26933
|
for (const [name2, value] of Object.entries(call.args.headers ?? {})) {
|
|
26901
26934
|
if (!/^[A-Za-z0-9-]+$/.test(name2) || /[\r\n]/.test(value) || ["host", "connection", "content-length", "transfer-encoding", "upgrade", "proxy-authorization", "proxy-connection"].includes(name2.toLowerCase()) || Object.keys(headers).some((k) => k.toLowerCase() === name2.toLowerCase())) fail2("INVALID_HTTP_HEADER");
|
|
@@ -26935,10 +26968,10 @@ var NativeTools = class {
|
|
|
26935
26968
|
} };
|
|
26936
26969
|
}
|
|
26937
26970
|
if (call.name === "command_run") {
|
|
26938
|
-
const cwd2 = await resolve(call.args.cwd, true), program = this.resolveProgram(call.args.command, workspace);
|
|
26971
|
+
const cwd2 = await resolve(call.args.cwd, true), program = await this.resolveProgram(call.args.command, workspace, call.args.args);
|
|
26939
26972
|
const claims2 = writeClaim(cwd2);
|
|
26940
|
-
return { call, claims: claims2, execution: {
|
|
26941
|
-
const r = await runSupervised(this.options.helperPath, {
|
|
26973
|
+
return { call, claims: claims2, execution: { ...program, cwd: cwd2 }, run: async (signal, onStarted, onOutput) => {
|
|
26974
|
+
const r = await runSupervised(this.options.helperPath, { ...program, cwd: cwd2, timeoutMs: call.args.timeoutMs, maxProcesses: 32 }, { signal, maxOutputBytes: 9e4, onStarted, onOutput });
|
|
26942
26975
|
const { pid: _pid, ...output2 } = r;
|
|
26943
26976
|
return { ...output2, resultCode: r.cancelled ? "CANCELLED" : r.timedOut ? "TIMEOUT" : r.exitCode === 0 ? "SUCCESS" : "USER_PROGRAM_FAILED" };
|
|
26944
26977
|
} };
|
|
@@ -26952,9 +26985,9 @@ var NativeTools = class {
|
|
|
26952
26985
|
else if (call.name === "git_diff") {
|
|
26953
26986
|
argv = ["diff", "--no-ext-diff", "--no-textconv", ...call.args.staged ? ["--cached"] : []];
|
|
26954
26987
|
if (call.args.file) {
|
|
26955
|
-
const file2 = await resolve(
|
|
26956
|
-
const relative =
|
|
26957
|
-
if (relative.startsWith(".." +
|
|
26988
|
+
const file2 = await resolve(path10.resolve(cwd, call.args.file));
|
|
26989
|
+
const relative = path10.relative(cwd, file2);
|
|
26990
|
+
if (relative.startsWith(".." + path10.sep) || relative === ".." || path10.isAbsolute(relative)) fail2("PATH_NOT_ALLOWED");
|
|
26958
26991
|
argv.push("--", relative);
|
|
26959
26992
|
} else argv.push("--stat");
|
|
26960
26993
|
} else fail2("TOOL_MISSING");
|
|
@@ -26965,7 +26998,7 @@ var NativeTools = class {
|
|
|
26965
26998
|
const listing = await this.list(guard, cwd, 1, 150, signal);
|
|
26966
26999
|
let packageInfo = null;
|
|
26967
27000
|
try {
|
|
26968
|
-
const name2 = await guard.resolveExisting(
|
|
27001
|
+
const name2 = await guard.resolveExisting(path10.join(cwd, "package.json"));
|
|
26969
27002
|
guard.assertSafeFile(name2);
|
|
26970
27003
|
const value = JSON.parse(utf8(await this.readFile(name2, 64e3, signal)));
|
|
26971
27004
|
packageInfo = { name: typeof value.name === "string" ? value.name : null, version: typeof value.version === "string" ? value.version : null, scripts: value.scripts && typeof value.scripts === "object" ? Object.keys(value.scripts).slice(0, 100) : [] };
|
|
@@ -26981,16 +27014,44 @@ var NativeTools = class {
|
|
|
26981
27014
|
return { ...output2, resultCode: r.exitCode === 0 ? "SUCCESS" : "GIT_FAILED" };
|
|
26982
27015
|
} };
|
|
26983
27016
|
}
|
|
26984
|
-
resolveProgram(name2, workspace) {
|
|
26985
|
-
const entry = Object.entries(this.options.programs).find(([key, value]) => key.toLowerCase() === name2.toLowerCase() ||
|
|
26986
|
-
if (
|
|
26987
|
-
|
|
27017
|
+
async resolveProgram(name2, workspace, args) {
|
|
27018
|
+
const entry = Object.entries(this.options.programs).find(([key, value]) => key.toLowerCase() === name2.toLowerCase() || path10.isAbsolute(name2) && equalPath3(value.executable, path10.resolve(name2)));
|
|
27019
|
+
if (workspace.fullAccess) {
|
|
27020
|
+
const commandKey = (value) => path10.basename(value).toLowerCase().replace(/\.(exe|com|cmd|bat)$/i, "");
|
|
27021
|
+
const denied = new Set((workspace.deniedCommands ?? []).map(commandKey));
|
|
27022
|
+
if (denied.has(commandKey(name2)) || entry && denied.has(commandKey(entry[0]))) fail2("COMMAND_NOT_ALLOWED");
|
|
27023
|
+
const extension2 = path10.extname(name2).toLowerCase();
|
|
27024
|
+
const candidate = entry?.[1].executable ?? (path10.isAbsolute(name2) ? name2 : /^[\w.-]+$/.test(name2) ? resolveWindowsLauncher(
|
|
27025
|
+
commandKey(name2),
|
|
27026
|
+
[".exe", ".com", ".cmd", ".bat"].includes(extension2) ? { ...process.env, PATHEXT: extension2 } : process.env
|
|
27027
|
+
)?.file : void 0);
|
|
27028
|
+
if (!candidate || !path10.isAbsolute(candidate)) fail2("COMMAND_NOT_FOUND");
|
|
27029
|
+
const executable = await realpath5(candidate).catch(() => fail2("COMMAND_NOT_FOUND"));
|
|
27030
|
+
if (!(await lstat5(executable)).isFile()) fail2("COMMAND_NOT_FOUND");
|
|
27031
|
+
if (denied.has(commandKey(executable))) fail2("COMMAND_NOT_ALLOWED");
|
|
27032
|
+
for (const key of workspace.deniedCommands ?? []) {
|
|
27033
|
+
const configured = this.options.programs[key]?.executable;
|
|
27034
|
+
if (configured && equalPath3(executable, await realpath5(configured).catch(() => configured))) fail2("COMMAND_NOT_ALLOWED");
|
|
27035
|
+
}
|
|
27036
|
+
if (/\.(cmd|bat)$/i.test(executable)) {
|
|
27037
|
+
if ([executable, ...args].some((value) => /[&|<>^%!"\r\n]/.test(value))) fail2("INVALID_SCRIPT_ARGUMENTS");
|
|
27038
|
+
const literal2 = (value) => "'" + value.replaceAll("'", "''") + "'";
|
|
27039
|
+
const shell = path10.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
27040
|
+
if (denied.has("powershell") || denied.has("cmd")) fail2("COMMAND_NOT_ALLOWED");
|
|
27041
|
+
const script = "& " + literal2(executable) + " @(" + args.map(literal2).join(",") + "); exit $LASTEXITCODE";
|
|
27042
|
+
return { executable: shell, args: ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")] };
|
|
27043
|
+
}
|
|
27044
|
+
if (!/\.(exe|com)$/i.test(executable)) fail2("COMMAND_NOT_ALLOWED");
|
|
27045
|
+
return { executable, args: [...entry?.[1].prefixArgs ?? [], ...args] };
|
|
27046
|
+
}
|
|
27047
|
+
if (!entry || !workspace.allowedCommands.some((v) => v.toLowerCase() === entry[0].toLowerCase()) || !path10.isAbsolute(entry[1].executable)) fail2("COMMAND_NOT_ALLOWED");
|
|
27048
|
+
return { executable: entry[1].executable, args: [...entry[1].prefixArgs ?? [], ...args] };
|
|
26988
27049
|
}
|
|
26989
27050
|
async gitExecution(cwd, args) {
|
|
26990
27051
|
const configured = this.options.programs.git;
|
|
26991
|
-
const candidates = configured ? [configured.executable] : process.platform === "win32" ? [[process.env.ProgramFiles, "Git"], [process.env["ProgramFiles(x86)"], "Git"], [process.env.LOCALAPPDATA, "Programs/Git"]].filter(([root]) => root &&
|
|
27052
|
+
const candidates = configured ? [configured.executable] : process.platform === "win32" ? [[process.env.ProgramFiles, "Git"], [process.env["ProgramFiles(x86)"], "Git"], [process.env.LOCALAPPDATA, "Programs/Git"]].filter(([root]) => root && path10.isAbsolute(root)).map(([root, suffix]) => path10.join(root, suffix, "cmd", "git.exe")) : [];
|
|
26992
27053
|
for (const candidate of candidates) {
|
|
26993
|
-
if (!
|
|
27054
|
+
if (!path10.isAbsolute(candidate)) continue;
|
|
26994
27055
|
try {
|
|
26995
27056
|
const stat = await lstat5(candidate);
|
|
26996
27057
|
if (!stat.isFile() || stat.isSymbolicLink() || !equalPath3(candidate, await realpath5(candidate))) continue;
|
|
@@ -27016,7 +27077,7 @@ var NativeTools = class {
|
|
|
27016
27077
|
break;
|
|
27017
27078
|
}
|
|
27018
27079
|
if (item.isSymbolicLink() || excluded.has(item.name.toLowerCase())) continue;
|
|
27019
|
-
const candidate =
|
|
27080
|
+
const candidate = path10.join(current2.path, item.name);
|
|
27020
27081
|
let resolved;
|
|
27021
27082
|
try {
|
|
27022
27083
|
resolved = await guard.resolveExisting(candidate);
|
|
@@ -27026,11 +27087,11 @@ var NativeTools = class {
|
|
|
27026
27087
|
}
|
|
27027
27088
|
if (!equalPath3(candidate, resolved)) continue;
|
|
27028
27089
|
if (item.isDirectory()) {
|
|
27029
|
-
entries.push({ path:
|
|
27090
|
+
entries.push({ path: path10.relative(root, resolved), type: "directory" });
|
|
27030
27091
|
if (current2.level < depth) queue.push({ path: resolved, level: current2.level + 1 });
|
|
27031
27092
|
} else if (item.isFile()) {
|
|
27032
27093
|
const stat = await lstat5(resolved);
|
|
27033
|
-
entries.push({ path:
|
|
27094
|
+
entries.push({ path: path10.relative(root, resolved), type: "file", size: stat.size });
|
|
27034
27095
|
}
|
|
27035
27096
|
}
|
|
27036
27097
|
if (truncated) break;
|
|
@@ -27040,7 +27101,7 @@ var NativeTools = class {
|
|
|
27040
27101
|
async gitCommonDir(cwd, workspace, guard) {
|
|
27041
27102
|
let directory = cwd;
|
|
27042
27103
|
while (guard.isWithin(directory, workspace.root)) {
|
|
27043
|
-
const dotGit =
|
|
27104
|
+
const dotGit = path10.join(directory, ".git");
|
|
27044
27105
|
try {
|
|
27045
27106
|
const stat = await lstat5(dotGit);
|
|
27046
27107
|
if (stat.isSymbolicLink()) fail2("GIT_METADATA_CHANGED");
|
|
@@ -27049,14 +27110,14 @@ var NativeTools = class {
|
|
|
27049
27110
|
else {
|
|
27050
27111
|
const content = utf8(await this.readFile(dotGit, 4096)).trim();
|
|
27051
27112
|
if (!content.startsWith("gitdir: ")) fail2("GIT_METADATA_CHANGED");
|
|
27052
|
-
gitDir = await realpath5(
|
|
27113
|
+
gitDir = await realpath5(path10.resolve(directory, content.slice(8)));
|
|
27053
27114
|
}
|
|
27054
27115
|
if (guard.isWithin(gitDir, this.options.stateDirectory) || !guard.isWithin(gitDir, workspace.root) && (!workspace.gitCommonDir || !guard.isWithin(gitDir, workspace.gitCommonDir))) fail2("GIT_COMMON_DIRECTORY_NOT_APPROVED");
|
|
27055
27116
|
let common = gitDir;
|
|
27056
27117
|
try {
|
|
27057
|
-
const content = utf8(await this.readFile(
|
|
27118
|
+
const content = utf8(await this.readFile(path10.join(gitDir, "commondir"), 4096)).trim();
|
|
27058
27119
|
if (!content) fail2("GIT_METADATA_CHANGED");
|
|
27059
|
-
common = await realpath5(
|
|
27120
|
+
common = await realpath5(path10.resolve(gitDir, content));
|
|
27060
27121
|
} catch (e) {
|
|
27061
27122
|
if (e.code !== "ENOENT") throw e;
|
|
27062
27123
|
}
|
|
@@ -27067,22 +27128,22 @@ var NativeTools = class {
|
|
|
27067
27128
|
if (e.code !== "ENOENT") throw e;
|
|
27068
27129
|
}
|
|
27069
27130
|
if (equalPath3(directory, workspace.root)) break;
|
|
27070
|
-
directory =
|
|
27131
|
+
directory = path10.dirname(directory);
|
|
27071
27132
|
}
|
|
27072
27133
|
return null;
|
|
27073
27134
|
}
|
|
27074
27135
|
};
|
|
27075
27136
|
|
|
27076
27137
|
// agent/src/scheduler.ts
|
|
27077
|
-
import
|
|
27138
|
+
import path11 from "node:path";
|
|
27078
27139
|
import os2 from "node:os";
|
|
27079
27140
|
function canonical(value) {
|
|
27080
|
-
const p =
|
|
27141
|
+
const p = path11.resolve(value);
|
|
27081
27142
|
return process.platform === "win32" ? p.toLowerCase() : p;
|
|
27082
27143
|
}
|
|
27083
27144
|
function ancestor(a, b) {
|
|
27084
|
-
const relative =
|
|
27085
|
-
return relative === "" || !
|
|
27145
|
+
const relative = path11.relative(a, b);
|
|
27146
|
+
return relative === "" || !path11.isAbsolute(relative) && relative !== ".." && !relative.startsWith(".." + path11.sep);
|
|
27086
27147
|
}
|
|
27087
27148
|
function claimsConflict(a, b) {
|
|
27088
27149
|
return a.some((x) => b.some((y) => (x.namespace ?? "filesystem") === (y.namespace ?? "filesystem") && (x.mode === "write" || y.mode === "write") && (ancestor(x.path, y.path) || ancestor(y.path, x.path))));
|
|
@@ -27102,7 +27163,7 @@ var NativeScheduler = class {
|
|
|
27102
27163
|
schedule(id, claims, deadlineAt, run, signal) {
|
|
27103
27164
|
if (!this.accepting) return Promise.reject(Error("AGENT_PAUSED"));
|
|
27104
27165
|
if (!id || id.length > 100 || this.active.has(id) || this.waiting.some((j) => j.id === id)) return Promise.reject(Error("DUPLICATE_SCHEDULED_EXECUTION"));
|
|
27105
|
-
if (!Array.isArray(claims) || !claims.length || claims.length > 32 || claims.some((c) => !c || !
|
|
27166
|
+
if (!Array.isArray(claims) || !claims.length || claims.length > 32 || claims.some((c) => !c || !path11.isAbsolute(c.path) || !["read", "write"].includes(c.mode))) return Promise.reject(Error("INVALID_RESOURCE_CLAIM"));
|
|
27106
27167
|
const deadline = deadlineAt.getTime();
|
|
27107
27168
|
if (!Number.isFinite(deadline) || deadline <= Date.now()) return Promise.reject(Error("ADMISSION_DEADLINE_EXPIRED"));
|
|
27108
27169
|
if (signal?.aborted) return Promise.reject(Error("CANCELLED_BEFORE_ACCEPTANCE"));
|
|
@@ -28553,8 +28614,8 @@ function canonicalOrigin2(value) {
|
|
|
28553
28614
|
fail4("ORIGIN_INVALID");
|
|
28554
28615
|
}
|
|
28555
28616
|
}
|
|
28556
|
-
function endpoint(origin,
|
|
28557
|
-
return origin +
|
|
28617
|
+
function endpoint(origin, path15) {
|
|
28618
|
+
return origin + path15;
|
|
28558
28619
|
}
|
|
28559
28620
|
function uuid17(value, code) {
|
|
28560
28621
|
if (typeof value !== "string" || !UUID3.test(value)) fail4(code);
|
|
@@ -29057,14 +29118,14 @@ async function deliverGuardEvidence(target, packet, options = {}) {
|
|
|
29057
29118
|
// agent/src/optimization-ledger.ts
|
|
29058
29119
|
import { constants as constants4 } from "node:fs";
|
|
29059
29120
|
import { lstat as lstat6, mkdir as mkdir4, open as open5, readFile as readFile4, realpath as realpath6 } from "node:fs/promises";
|
|
29060
|
-
import
|
|
29121
|
+
import path12 from "node:path";
|
|
29061
29122
|
var MAX_LEDGER_BYTES = 64 * 1024 * 1024;
|
|
29062
29123
|
var MAX_NOTICE_LEDGER_BYTES = 8 * 1024 * 1024;
|
|
29063
29124
|
function failure4(code) {
|
|
29064
29125
|
return Object.assign(new Error(code), { code });
|
|
29065
29126
|
}
|
|
29066
29127
|
async function safeDirectory2(directory) {
|
|
29067
|
-
const absolute2 =
|
|
29128
|
+
const absolute2 = path12.resolve(directory);
|
|
29068
29129
|
let current2 = absolute2;
|
|
29069
29130
|
for (; ; ) {
|
|
29070
29131
|
try {
|
|
@@ -29072,7 +29133,7 @@ async function safeDirectory2(directory) {
|
|
|
29072
29133
|
} catch (error62) {
|
|
29073
29134
|
if (error62.code !== "ENOENT") throw error62;
|
|
29074
29135
|
}
|
|
29075
|
-
const parent =
|
|
29136
|
+
const parent = path12.dirname(current2);
|
|
29076
29137
|
if (parent === current2) break;
|
|
29077
29138
|
current2 = parent;
|
|
29078
29139
|
}
|
|
@@ -29097,8 +29158,8 @@ var OptimizationLedger = class _OptimizationLedger {
|
|
|
29097
29158
|
closed = false;
|
|
29098
29159
|
static async open(directory) {
|
|
29099
29160
|
const dir = await safeDirectory2(directory);
|
|
29100
|
-
const filename =
|
|
29101
|
-
const noticesFilename =
|
|
29161
|
+
const filename = path12.join(dir, "live-efficiency.v1.jsonl");
|
|
29162
|
+
const noticesFilename = path12.join(dir, "live-efficiency-notices.v1.jsonl");
|
|
29102
29163
|
let existing = "";
|
|
29103
29164
|
try {
|
|
29104
29165
|
const stat = await lstat6(filename);
|
|
@@ -29230,8 +29291,144 @@ var OptimizationLedger = class _OptimizationLedger {
|
|
|
29230
29291
|
}
|
|
29231
29292
|
};
|
|
29232
29293
|
|
|
29294
|
+
// agent/src/monitor.ts
|
|
29295
|
+
import { createHash as createHash10, randomUUID as randomUUID7 } from "node:crypto";
|
|
29296
|
+
import { lstat as lstat7, mkdir as mkdir5, open as open6, rename as rename4, rm as rm4 } from "node:fs/promises";
|
|
29297
|
+
import path13 from "node:path";
|
|
29298
|
+
var ProductionMonitor = class {
|
|
29299
|
+
constructor(directory, stateDirectory, agentVersion3) {
|
|
29300
|
+
this.agentVersion = agentVersion3;
|
|
29301
|
+
const key = createHash10("sha256").update(path13.resolve(stateDirectory).toLowerCase()).digest("hex");
|
|
29302
|
+
this.file = path13.join(directory, key + ".json");
|
|
29303
|
+
}
|
|
29304
|
+
agentVersion;
|
|
29305
|
+
instanceId = randomUUID7();
|
|
29306
|
+
events = [];
|
|
29307
|
+
sessions = /* @__PURE__ */ new Map();
|
|
29308
|
+
sequence = 0;
|
|
29309
|
+
connection = {
|
|
29310
|
+
state: "starting",
|
|
29311
|
+
origin: null,
|
|
29312
|
+
epoch: null
|
|
29313
|
+
};
|
|
29314
|
+
writing = null;
|
|
29315
|
+
file;
|
|
29316
|
+
record(event, details = {}) {
|
|
29317
|
+
this.events.push({ seq: ++this.sequence, at: (/* @__PURE__ */ new Date()).toISOString(), event, ...details });
|
|
29318
|
+
if (this.events.length > 100) this.events.shift();
|
|
29319
|
+
}
|
|
29320
|
+
configureOrigin(origin) {
|
|
29321
|
+
try {
|
|
29322
|
+
const url2 = new URL(origin);
|
|
29323
|
+
if (url2.origin === origin && !url2.username && !url2.password && (url2.protocol === "https:" || url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) {
|
|
29324
|
+
this.connection.origin = url2.origin;
|
|
29325
|
+
}
|
|
29326
|
+
} catch {
|
|
29327
|
+
}
|
|
29328
|
+
}
|
|
29329
|
+
setConnection(state, connection) {
|
|
29330
|
+
if (connection) {
|
|
29331
|
+
try {
|
|
29332
|
+
this.configureOrigin(new URL(connection.url).origin.replace(/^ws/, "http"));
|
|
29333
|
+
} catch {
|
|
29334
|
+
}
|
|
29335
|
+
this.connection.epoch = connection.connectionEpoch;
|
|
29336
|
+
}
|
|
29337
|
+
this.connection.state = state;
|
|
29338
|
+
this.record(state);
|
|
29339
|
+
}
|
|
29340
|
+
request(request) {
|
|
29341
|
+
const payload = request.payload;
|
|
29342
|
+
const parsed = ExecutionBindingSchema.safeParse(payload?.binding);
|
|
29343
|
+
if (!parsed.success || parsed.data.tenantId !== request.tenantId || parsed.data.deviceId !== request.deviceId || parsed.data.executionId !== request.executionId || parsed.data.operationId !== request.operationId) return;
|
|
29344
|
+
const binding2 = parsed.data;
|
|
29345
|
+
this.sessions.delete(binding2.sessionId);
|
|
29346
|
+
this.sessions.set(binding2.sessionId, {
|
|
29347
|
+
sessionId: binding2.sessionId,
|
|
29348
|
+
workspaceGrantId: binding2.workspaceGrantId,
|
|
29349
|
+
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29350
|
+
model: null
|
|
29351
|
+
});
|
|
29352
|
+
if (this.sessions.size > 32) this.sessions.delete(this.sessions.keys().next().value);
|
|
29353
|
+
const tool = ToolCallSchema.safeParse(payload?.tool);
|
|
29354
|
+
this.record(request.kind, {
|
|
29355
|
+
sessionId: binding2.sessionId,
|
|
29356
|
+
executionId: binding2.executionId,
|
|
29357
|
+
...tool.success ? { tool: tool.data.name } : {}
|
|
29358
|
+
});
|
|
29359
|
+
}
|
|
29360
|
+
result(binding2, state, code) {
|
|
29361
|
+
this.record(["finished", "failed", "cancelled", "unknown"].includes(state) ? state : "result", {
|
|
29362
|
+
sessionId: binding2.sessionId,
|
|
29363
|
+
executionId: binding2.executionId,
|
|
29364
|
+
code: code === "SUCCESS" || ErrorCodeSchema.safeParse(code).success ? code : "RESULT"
|
|
29365
|
+
});
|
|
29366
|
+
}
|
|
29367
|
+
requestFailed() {
|
|
29368
|
+
this.record("request_failed");
|
|
29369
|
+
}
|
|
29370
|
+
connectionFailed(error62) {
|
|
29371
|
+
const code = error62 instanceof Error ? error62.message : "";
|
|
29372
|
+
this.record("connection_error", { code: [
|
|
29373
|
+
"RELAY_CONNECT_FAILED",
|
|
29374
|
+
"RELAY_CONNECT_CLOSED",
|
|
29375
|
+
"RELAY_CONNECT_TIMEOUT",
|
|
29376
|
+
"RELAY_HANDSHAKE_TIMEOUT",
|
|
29377
|
+
"RELAY_RESULT_TIMEOUT",
|
|
29378
|
+
"WORKSPACE_ACK_TIMEOUT"
|
|
29379
|
+
].includes(code) ? code : "CONNECTION_FAILED" });
|
|
29380
|
+
}
|
|
29381
|
+
response(value) {
|
|
29382
|
+
const result2 = value;
|
|
29383
|
+
if (result2?.accepted === false) this.record("rejected", {
|
|
29384
|
+
code: ErrorCodeSchema.safeParse(result2.code).success ? String(result2.code) : "REJECTED"
|
|
29385
|
+
});
|
|
29386
|
+
}
|
|
29387
|
+
snapshot() {
|
|
29388
|
+
return {
|
|
29389
|
+
schemaVersion: 1,
|
|
29390
|
+
instanceId: this.instanceId,
|
|
29391
|
+
pid: process.pid,
|
|
29392
|
+
agentVersion: this.agentVersion,
|
|
29393
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29394
|
+
connection: { ...this.connection },
|
|
29395
|
+
sessions: [...this.sessions.values()].map((session) => ({ ...session })),
|
|
29396
|
+
events: this.events.map((event) => ({ ...event }))
|
|
29397
|
+
};
|
|
29398
|
+
}
|
|
29399
|
+
flush() {
|
|
29400
|
+
if (this.writing) return this.writing;
|
|
29401
|
+
this.writing = (async () => {
|
|
29402
|
+
const directory = path13.dirname(this.file);
|
|
29403
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
29404
|
+
const stat = await lstat7(directory);
|
|
29405
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw Error("MONITOR_DIRECTORY_INVALID");
|
|
29406
|
+
const temporary = this.file + "." + this.instanceId + ".tmp";
|
|
29407
|
+
const handle = await open6(temporary, "wx", 384);
|
|
29408
|
+
try {
|
|
29409
|
+
try {
|
|
29410
|
+
await handle.writeFile(JSON.stringify(this.snapshot()) + "\n");
|
|
29411
|
+
} finally {
|
|
29412
|
+
await handle.close();
|
|
29413
|
+
}
|
|
29414
|
+
await rename4(temporary, this.file);
|
|
29415
|
+
} finally {
|
|
29416
|
+
await rm4(temporary, { force: true });
|
|
29417
|
+
}
|
|
29418
|
+
})().finally(() => {
|
|
29419
|
+
this.writing = null;
|
|
29420
|
+
});
|
|
29421
|
+
return this.writing;
|
|
29422
|
+
}
|
|
29423
|
+
async close() {
|
|
29424
|
+
await this.writing?.catch(() => {
|
|
29425
|
+
});
|
|
29426
|
+
await this.flush();
|
|
29427
|
+
}
|
|
29428
|
+
};
|
|
29429
|
+
|
|
29233
29430
|
// agent/src/main.ts
|
|
29234
|
-
var AGENT_VERSION2 = true ? "0.3.
|
|
29431
|
+
var AGENT_VERSION2 = true ? "0.3.7" : "0.3.2";
|
|
29235
29432
|
function processAlive(pid) {
|
|
29236
29433
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
29237
29434
|
try {
|
|
@@ -29242,11 +29439,11 @@ function processAlive(pid) {
|
|
|
29242
29439
|
}
|
|
29243
29440
|
}
|
|
29244
29441
|
async function claimAgentProcess(stateDir, runtimeDir, version2) {
|
|
29245
|
-
await
|
|
29246
|
-
const file2 =
|
|
29442
|
+
await mkdir6(stateDir, { recursive: true });
|
|
29443
|
+
const file2 = path14.join(stateDir, "agent-process.json");
|
|
29247
29444
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
29248
29445
|
try {
|
|
29249
|
-
const handle = await
|
|
29446
|
+
const handle = await open7(file2, "wx", 384);
|
|
29250
29447
|
try {
|
|
29251
29448
|
await handle.writeFile(JSON.stringify({ pid: process.pid, runtimeDir, version: version2, startedAt: (/* @__PURE__ */ new Date()).toISOString() }) + "\n", "utf8");
|
|
29252
29449
|
} finally {
|
|
@@ -29255,7 +29452,7 @@ async function claimAgentProcess(stateDir, runtimeDir, version2) {
|
|
|
29255
29452
|
return async () => {
|
|
29256
29453
|
try {
|
|
29257
29454
|
const current2 = JSON.parse(await readFile5(file2, "utf8"));
|
|
29258
|
-
if (current2.pid === process.pid) await
|
|
29455
|
+
if (current2.pid === process.pid) await rm5(file2, { force: true });
|
|
29259
29456
|
} catch {
|
|
29260
29457
|
}
|
|
29261
29458
|
};
|
|
@@ -29268,7 +29465,7 @@ async function claimAgentProcess(stateDir, runtimeDir, version2) {
|
|
|
29268
29465
|
} catch {
|
|
29269
29466
|
}
|
|
29270
29467
|
if (!stale) throw Error("AGENT_ALREADY_RUNNING");
|
|
29271
|
-
await
|
|
29468
|
+
await rm5(file2, { force: true });
|
|
29272
29469
|
}
|
|
29273
29470
|
}
|
|
29274
29471
|
throw Error("AGENT_ALREADY_RUNNING");
|
|
@@ -29291,6 +29488,7 @@ async function runAgent(options) {
|
|
|
29291
29488
|
if (!Number.isInteger(delay) || delay < 0 || delay > 3e4) throw Error("RECONNECT_DELAY_INVALID");
|
|
29292
29489
|
const identity = await options.store.load();
|
|
29293
29490
|
if (!identity?.enrollment) throw Error("ENROLLMENT_MISSING");
|
|
29491
|
+
options.monitor?.configureOrigin(identity.enrollment.serverOrigin);
|
|
29294
29492
|
await options.journal.recoverAfterRestart();
|
|
29295
29493
|
await options.store.loadWorkspaces();
|
|
29296
29494
|
} catch (error62) {
|
|
@@ -29300,6 +29498,7 @@ async function runAgent(options) {
|
|
|
29300
29498
|
const runtime = new NativeRuntime({ journal: options.journal, tools: options.tools, scheduler: new NativeScheduler(), workspaces: () => options.store.loadWorkspaces(Boolean(options.guard)), guard: options.guard, efficiencyLedger: options.efficiencyLedger });
|
|
29301
29499
|
let connection = null;
|
|
29302
29500
|
const bridge = new NativeRelayBridge(runtime, (binding2, state, code, metrics) => {
|
|
29501
|
+
options.monitor?.result(binding2, state, code);
|
|
29303
29502
|
if (!connection) throw Error("RELAY_SOCKET_CLOSED");
|
|
29304
29503
|
return connection.sendTerminal(binding2, state, code, metrics);
|
|
29305
29504
|
}, options.journal);
|
|
@@ -29311,11 +29510,21 @@ async function runAgent(options) {
|
|
|
29311
29510
|
ready = resolve;
|
|
29312
29511
|
});
|
|
29313
29512
|
try {
|
|
29513
|
+
options.monitor?.setConnection("connecting");
|
|
29314
29514
|
connection = await connect({ store: options.store, agentVersion: options.agentVersion, handleRequest: async (request) => {
|
|
29315
29515
|
await readyPromise;
|
|
29316
|
-
|
|
29516
|
+
options.monitor?.request(request);
|
|
29517
|
+
try {
|
|
29518
|
+
const result2 = await bridge.handle(request);
|
|
29519
|
+
options.monitor?.response(result2);
|
|
29520
|
+
return result2;
|
|
29521
|
+
} catch (error62) {
|
|
29522
|
+
options.monitor?.requestFailed();
|
|
29523
|
+
throw error62;
|
|
29524
|
+
}
|
|
29317
29525
|
} });
|
|
29318
29526
|
await bridge.connected(connection);
|
|
29527
|
+
options.monitor?.setConnection("connected", connection);
|
|
29319
29528
|
ready();
|
|
29320
29529
|
if (options.signal.aborted) connection.close("agent_shutdown");
|
|
29321
29530
|
else {
|
|
@@ -29337,9 +29546,11 @@ async function runAgent(options) {
|
|
|
29337
29546
|
}
|
|
29338
29547
|
} catch (error62) {
|
|
29339
29548
|
ready();
|
|
29549
|
+
options.monitor?.connectionFailed(error62);
|
|
29340
29550
|
if (error62 instanceof RelayClientError && ["RELAY_CONNECT_FAILED", "RELAY_CONNECT_CLOSED", "RELAY_CONNECT_TIMEOUT", "RELAY_HANDSHAKE_TIMEOUT"].includes(error62.code)) {
|
|
29341
29551
|
} else throw error62;
|
|
29342
29552
|
} finally {
|
|
29553
|
+
options.monitor?.setConnection("disconnected");
|
|
29343
29554
|
connection?.close("agent_disconnect");
|
|
29344
29555
|
await bridge.disconnected();
|
|
29345
29556
|
connection = null;
|
|
@@ -29354,10 +29565,10 @@ async function runAgent(options) {
|
|
|
29354
29565
|
async function main() {
|
|
29355
29566
|
if (process.platform !== "win32") throw Error("WINDOWS_REQUIRED");
|
|
29356
29567
|
const appData = process.env.LOCALAPPDATA;
|
|
29357
|
-
if (!appData || !
|
|
29358
|
-
const stateDir = process.env.LC_AGENT_STATE_DIR ??
|
|
29359
|
-
const binaryDir =
|
|
29360
|
-
const ownerBridgePath = process.env.LC_OWNER_BRIDGE ??
|
|
29568
|
+
if (!appData || !path14.isAbsolute(appData)) throw Error("APP_DATA_MISSING");
|
|
29569
|
+
const stateDir = process.env.LC_AGENT_STATE_DIR ?? path14.join(appData, "LocalCommander", "Agent");
|
|
29570
|
+
const binaryDir = path14.dirname(process.execPath);
|
|
29571
|
+
const ownerBridgePath = process.env.LC_OWNER_BRIDGE ?? path14.join(binaryDir, "OwnerBridge.exe");
|
|
29361
29572
|
const store = await WindowsPrivateStore.open(stateDir, ownerBridgePath);
|
|
29362
29573
|
const command = process.argv[2] ?? "run";
|
|
29363
29574
|
if (command === "pair") {
|
|
@@ -29395,6 +29606,9 @@ async function main() {
|
|
|
29395
29606
|
displayName: w.displayName,
|
|
29396
29607
|
path: w.root,
|
|
29397
29608
|
permissions: w.permissions,
|
|
29609
|
+
accessMode: w.fullAccess ? "full" : "scoped",
|
|
29610
|
+
deniedCommands: w.deniedCommands ?? [],
|
|
29611
|
+
deniedLocalhostPorts: w.deniedLocalhostPorts ?? [],
|
|
29398
29612
|
allowedCommands: w.allowedCommands,
|
|
29399
29613
|
executables: Object.fromEntries(w.allowedCommands.map((name2) => [name2, programs[name2]?.executable ?? null])),
|
|
29400
29614
|
localhostPorts: w.localhostPorts,
|
|
@@ -29405,7 +29619,7 @@ async function main() {
|
|
|
29405
29619
|
const args = process.argv.slice(3);
|
|
29406
29620
|
if (command === "efficiency-report") {
|
|
29407
29621
|
if (args.length) throw Error("AGENT_COMMAND_INVALID");
|
|
29408
|
-
const ledger = await OptimizationLedger.open(
|
|
29622
|
+
const ledger = await OptimizationLedger.open(path14.join(stateDir, "optimization"));
|
|
29409
29623
|
try {
|
|
29410
29624
|
process.stdout.write(JSON.stringify(await ledger.report(), null, 2) + "\n");
|
|
29411
29625
|
} finally {
|
|
@@ -29415,7 +29629,7 @@ async function main() {
|
|
|
29415
29629
|
}
|
|
29416
29630
|
if (command === "guard-configure") {
|
|
29417
29631
|
if (args.length !== 1) throw Error("AGENT_COMMAND_INVALID");
|
|
29418
|
-
const filename = await (await import("node:fs/promises")).realpath(
|
|
29632
|
+
const filename = await (await import("node:fs/promises")).realpath(path14.resolve(args[0]));
|
|
29419
29633
|
const input2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readWindowsFile(ownerBridgePath, filename, 2e5)));
|
|
29420
29634
|
if (!input2 || typeof input2 !== "object" || Object.keys(input2).sort().join(",") !== "compact,settings") throw Error("GUARD_CONFIGURATION_INVALID");
|
|
29421
29635
|
await store.configureGuard(input2.settings, input2.compact);
|
|
@@ -29434,7 +29648,7 @@ async function main() {
|
|
|
29434
29648
|
}
|
|
29435
29649
|
if (command === "guard-evidence") {
|
|
29436
29650
|
if (args.length) throw Error("AGENT_COMMAND_INVALID");
|
|
29437
|
-
const journal = await NativeJournal.open(
|
|
29651
|
+
const journal = await NativeJournal.open(path14.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
|
|
29438
29652
|
try {
|
|
29439
29653
|
process.stdout.write(JSON.stringify(await journal.guardEvidence()) + "\n");
|
|
29440
29654
|
} finally {
|
|
@@ -29446,7 +29660,7 @@ async function main() {
|
|
|
29446
29660
|
if (args.length !== (command === "guard-export" ? 0 : 1)) throw Error("AGENT_COMMAND_INVALID");
|
|
29447
29661
|
const identity = await store.load();
|
|
29448
29662
|
if (!identity?.enrollment) throw Error("ENROLLMENT_MISSING");
|
|
29449
|
-
const journal = await NativeJournal.open(
|
|
29663
|
+
const journal = await NativeJournal.open(path14.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
|
|
29450
29664
|
try {
|
|
29451
29665
|
const { tenantId, deviceId } = identity.enrollment;
|
|
29452
29666
|
const records = (await journal.guardEvidence()).filter((record2) => record2.tenantId === tenantId && record2.deviceId === deviceId);
|
|
@@ -29484,25 +29698,41 @@ async function main() {
|
|
|
29484
29698
|
if (args.length) throw Error("AGENT_COMMAND_INVALID");
|
|
29485
29699
|
if (!(await store.load())?.enrollment) throw Error("ENROLLMENT_MISSING");
|
|
29486
29700
|
const releaseProcess = await claimAgentProcess(stateDir, binaryDir, AGENT_VERSION2);
|
|
29701
|
+
const monitor = new ProductionMonitor(path14.join(appData, "LocalCommander", "Monitor"), stateDir, AGENT_VERSION2);
|
|
29702
|
+
const publishMonitor = () => monitor.flush().catch(() => {
|
|
29703
|
+
process.stderr.write("MONITOR_WRITE_FAILED\n");
|
|
29704
|
+
});
|
|
29705
|
+
await publishMonitor();
|
|
29706
|
+
const monitorTimer = setInterval(() => {
|
|
29707
|
+
void publishMonitor();
|
|
29708
|
+
}, 2e3);
|
|
29487
29709
|
try {
|
|
29488
|
-
const journal = await NativeJournal.open(
|
|
29489
|
-
const efficiencyLedger = await OptimizationLedger.open(
|
|
29710
|
+
const journal = await NativeJournal.open(path14.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
|
|
29711
|
+
const efficiencyLedger = await OptimizationLedger.open(path14.join(stateDir, "optimization"));
|
|
29490
29712
|
try {
|
|
29491
|
-
const tools = new NativeTools({ stateDirectory: stateDir, helperPath: process.env.LC_JOB_HOST ??
|
|
29713
|
+
const tools = new NativeTools({ stateDirectory: stateDir, helperPath: process.env.LC_JOB_HOST ?? path14.join(binaryDir, "JobHost.exe"), ownerBridgePath, programs: await store.loadPrograms() });
|
|
29492
29714
|
const stop = new AbortController();
|
|
29493
29715
|
process.once("SIGINT", () => stop.abort());
|
|
29494
29716
|
process.once("SIGTERM", () => stop.abort());
|
|
29495
29717
|
const guard = await NativeGuard.open(store, journal);
|
|
29496
|
-
await runAgent({ store, journal, tools, signal: stop.signal, agentVersion: AGENT_VERSION2, guard, efficiencyLedger });
|
|
29718
|
+
await runAgent({ store, journal, tools, signal: stop.signal, agentVersion: AGENT_VERSION2, guard, efficiencyLedger, monitor });
|
|
29719
|
+
monitor.setConnection("stopped");
|
|
29497
29720
|
} finally {
|
|
29498
29721
|
await efficiencyLedger.close();
|
|
29499
29722
|
await journal.close();
|
|
29500
29723
|
}
|
|
29724
|
+
} catch (error62) {
|
|
29725
|
+
monitor.setConnection("failed");
|
|
29726
|
+
throw error62;
|
|
29501
29727
|
} finally {
|
|
29728
|
+
clearInterval(monitorTimer);
|
|
29729
|
+
await monitor.close().catch(() => {
|
|
29730
|
+
process.stderr.write("MONITOR_WRITE_FAILED\n");
|
|
29731
|
+
});
|
|
29502
29732
|
await releaseProcess();
|
|
29503
29733
|
}
|
|
29504
29734
|
}
|
|
29505
|
-
if (process.argv[1] &&
|
|
29735
|
+
if (process.argv[1] && path14.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
29506
29736
|
void main().catch((error62) => {
|
|
29507
29737
|
const code = error62 instanceof Error && /^[A-Z0-9_]{1,64}$/.test(error62.message) ? error62.message : "AGENT_START_FAILED";
|
|
29508
29738
|
process.stderr.write(code + "\n");
|