@alan-ai-hq/agent-manager 0.1.95 → 0.1.97
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/dist/index.cjs +428 -539
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3404,7 +3404,7 @@ var require_websocket = __commonJS({
|
|
|
3404
3404
|
var http2 = require("http");
|
|
3405
3405
|
var net = require("net");
|
|
3406
3406
|
var tls = require("tls");
|
|
3407
|
-
var { randomBytes: randomBytes13, createHash:
|
|
3407
|
+
var { randomBytes: randomBytes13, createHash: createHash18 } = require("crypto");
|
|
3408
3408
|
var { Duplex, Readable: Readable2 } = require("stream");
|
|
3409
3409
|
var { URL: URL2 } = require("url");
|
|
3410
3410
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -4061,7 +4061,7 @@ var require_websocket = __commonJS({
|
|
|
4061
4061
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
4062
4062
|
return;
|
|
4063
4063
|
}
|
|
4064
|
-
const digest =
|
|
4064
|
+
const digest = createHash18("sha1").update(key + GUID).digest("base64");
|
|
4065
4065
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
4066
4066
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
4067
4067
|
return;
|
|
@@ -4428,7 +4428,7 @@ var require_websocket_server = __commonJS({
|
|
|
4428
4428
|
var EventEmitter = require("events");
|
|
4429
4429
|
var http2 = require("http");
|
|
4430
4430
|
var { Duplex } = require("stream");
|
|
4431
|
-
var { createHash:
|
|
4431
|
+
var { createHash: createHash18 } = require("crypto");
|
|
4432
4432
|
var extension = require_extension();
|
|
4433
4433
|
var PerMessageDeflate = require_permessage_deflate();
|
|
4434
4434
|
var subprotocol = require_subprotocol();
|
|
@@ -4725,7 +4725,7 @@ var require_websocket_server = __commonJS({
|
|
|
4725
4725
|
);
|
|
4726
4726
|
}
|
|
4727
4727
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
4728
|
-
const digest =
|
|
4728
|
+
const digest = createHash18("sha1").update(key + GUID).digest("base64");
|
|
4729
4729
|
const headers = [
|
|
4730
4730
|
"HTTP/1.1 101 Switching Protocols",
|
|
4731
4731
|
"Upgrade: websocket",
|
|
@@ -14583,6 +14583,15 @@ var import_node_os = require("os");
|
|
|
14583
14583
|
var import_node_path = require("path");
|
|
14584
14584
|
|
|
14585
14585
|
// ../sandbox-runtime-spec/dist/index.js
|
|
14586
|
+
var SANDBOX_AGENT_BROWSER_PACKAGE = "agent-browser";
|
|
14587
|
+
var SANDBOX_AGENT_BROWSER_VERSION_POLICY = ">=0.37.1 <1.0.0";
|
|
14588
|
+
var SANDBOX_AGENT_BROWSER_NPM_SPEC = `${SANDBOX_AGENT_BROWSER_PACKAGE}@${SANDBOX_AGENT_BROWSER_VERSION_POLICY}`;
|
|
14589
|
+
var SANDBOX_DESKTOP_BROWSER_ENV = Object.freeze({
|
|
14590
|
+
DISPLAY: ":0",
|
|
14591
|
+
AGENT_BROWSER_HEADED: "true",
|
|
14592
|
+
AGENT_BROWSER_NO_XVFB: "1",
|
|
14593
|
+
AGENT_BROWSER_EXECUTABLE_PATH: "/usr/bin/google-chrome-stable"
|
|
14594
|
+
});
|
|
14586
14595
|
var definitions = [
|
|
14587
14596
|
{
|
|
14588
14597
|
id: "claude",
|
|
@@ -15451,7 +15460,7 @@ function describeError(error61) {
|
|
|
15451
15460
|
}
|
|
15452
15461
|
|
|
15453
15462
|
// src/version.ts
|
|
15454
|
-
var AGENT_VERSION = "0.1.
|
|
15463
|
+
var AGENT_VERSION = "0.1.97";
|
|
15455
15464
|
|
|
15456
15465
|
// src/daemon-worktree.ts
|
|
15457
15466
|
var import_node_child_process3 = require("child_process");
|
|
@@ -20292,9 +20301,9 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
20292
20301
|
}`;
|
|
20293
20302
|
doc.write(`const input = payload.value;`);
|
|
20294
20303
|
const ids = /* @__PURE__ */ Object.create(null);
|
|
20295
|
-
let
|
|
20304
|
+
let counter2 = 0;
|
|
20296
20305
|
for (const key of normalized.allKeys) {
|
|
20297
|
-
ids[key] = `key_${
|
|
20306
|
+
ids[key] = `key_${counter2++}`;
|
|
20298
20307
|
}
|
|
20299
20308
|
doc.write(memo4 ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);
|
|
20300
20309
|
for (const key of normalized.allKeys) {
|
|
@@ -49610,16 +49619,6 @@ async function probeLocalDaemon(port, token, timeoutMs = 750) {
|
|
|
49610
49619
|
const probe = await probeLocalDaemonEndpoint(port, token, timeoutMs);
|
|
49611
49620
|
return probe.state === "running" ? probe.status : null;
|
|
49612
49621
|
}
|
|
49613
|
-
function requestLocalDaemonShutdown(port, token) {
|
|
49614
|
-
return fetch(`http://127.0.0.1:${port}/shutdown?force=1`, {
|
|
49615
|
-
method: "POST",
|
|
49616
|
-
headers: {
|
|
49617
|
-
authorization: `Bearer ${token}`,
|
|
49618
|
-
"x-alan-shutdown-reason": "runtime_shutdown"
|
|
49619
|
-
},
|
|
49620
|
-
signal: AbortSignal.timeout(5e3)
|
|
49621
|
-
});
|
|
49622
|
-
}
|
|
49623
49622
|
async function adoptLocalDaemonControl(input2) {
|
|
49624
49623
|
const timeoutMs = input2.timeoutMs ?? 750;
|
|
49625
49624
|
try {
|
|
@@ -74439,6 +74438,14 @@ var skillVersionIdSchema = external_exports.uuid().transform((value2) => value2)
|
|
|
74439
74438
|
var runtimeIdSchema = external_exports.uuid().transform((value2) => value2);
|
|
74440
74439
|
var skillPreviewIdSchema = external_exports.uuid().transform((value2) => value2);
|
|
74441
74440
|
var skillCommandIdSchema = external_exports.uuid().transform((value2) => value2);
|
|
74441
|
+
var skillPackageFileSchema = defineWireSchema()(external_exports.object({
|
|
74442
|
+
path: external_exports.string().min(1),
|
|
74443
|
+
kind: external_exports.enum(["instructions", "script", "reference", "asset", "license", "other"]),
|
|
74444
|
+
size: external_exports.number().int().nonnegative(),
|
|
74445
|
+
mode: external_exports.union([external_exports.literal(420), external_exports.literal(493)]),
|
|
74446
|
+
sha256: sha256Schema,
|
|
74447
|
+
preview: external_exports.enum(["markdown", "text", "code", "image", "binary"])
|
|
74448
|
+
}));
|
|
74442
74449
|
var skillInvocationSchema = defineWireSchema()(external_exports.discriminatedUnion("kind", [
|
|
74443
74450
|
external_exports.object({
|
|
74444
74451
|
kind: external_exports.literal("skill_version"),
|
|
@@ -74540,6 +74547,7 @@ function projectSkillFrontmatter(admitted) {
|
|
|
74540
74547
|
};
|
|
74541
74548
|
const license = optionalText("license", 1024);
|
|
74542
74549
|
const compatibility = optionalText("compatibility", 500);
|
|
74550
|
+
const version2 = optionalText("version", 64);
|
|
74543
74551
|
const allowedTools = optionalToolList("allowed-tools", 1024);
|
|
74544
74552
|
const metadata = {};
|
|
74545
74553
|
const rawMetadata = raw.metadata;
|
|
@@ -74588,6 +74596,7 @@ function projectSkillFrontmatter(admitted) {
|
|
|
74588
74596
|
description: admitted.description,
|
|
74589
74597
|
...license ? { license } : {},
|
|
74590
74598
|
...compatibility ? { compatibility } : {},
|
|
74599
|
+
...version2 ? { version: version2 } : {},
|
|
74591
74600
|
metadata,
|
|
74592
74601
|
...allowedTools ? { allowedTools } : {}
|
|
74593
74602
|
},
|
|
@@ -76697,13 +76706,13 @@ var RuntimeRunJournal = class {
|
|
|
76697
76706
|
* Keep the exact interruption outcome until a durable authenticated control
|
|
76698
76707
|
* channel can prove both identity and ownership.
|
|
76699
76708
|
*/
|
|
76700
|
-
assessRecovery(runId,
|
|
76709
|
+
assessRecovery(runId, isProcessAlive) {
|
|
76701
76710
|
const entry = this.entries.get(runId);
|
|
76702
76711
|
if (!entry) return void 0;
|
|
76703
76712
|
if (entry.providerPid === void 0) {
|
|
76704
76713
|
return { reattachable: false, reason: "provider_not_recorded" };
|
|
76705
76714
|
}
|
|
76706
|
-
if (!
|
|
76715
|
+
if (!isProcessAlive(entry.providerPid)) {
|
|
76707
76716
|
return {
|
|
76708
76717
|
reattachable: false,
|
|
76709
76718
|
reason: "provider_not_running",
|
|
@@ -80450,17 +80459,6 @@ async function runLocalDaemonRecoveryLadder(input2) {
|
|
|
80450
80459
|
error: "The local daemon exhausted its reconnect and safe-restart recovery budget."
|
|
80451
80460
|
};
|
|
80452
80461
|
}
|
|
80453
|
-
function writeRegisteredRuntimeConfig(args, previousConfig, registered) {
|
|
80454
|
-
writeConfig({
|
|
80455
|
-
...registered,
|
|
80456
|
-
localApiPort: getLocalApiPort(args, {}),
|
|
80457
|
-
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto19.randomBytes)(24).toString("hex"),
|
|
80458
|
-
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
80459
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80460
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80461
|
-
daemonEpoch: previousConfig.daemonEpoch
|
|
80462
|
-
});
|
|
80463
|
-
}
|
|
80464
80462
|
async function setupDaemon(args) {
|
|
80465
80463
|
bindConfigPath(args);
|
|
80466
80464
|
const previousConfig = readConfigForRegistration();
|
|
@@ -80521,7 +80519,7 @@ async function setupDaemon(args) {
|
|
|
80521
80519
|
credentialGeneration: body2.credentialGeneration,
|
|
80522
80520
|
configPath: getConfigPath()
|
|
80523
80521
|
});
|
|
80524
|
-
return
|
|
80522
|
+
return { daemonAlreadyRunning: true };
|
|
80525
80523
|
}
|
|
80526
80524
|
if (readLocalDaemonOwnerLease(getConfigPath())) {
|
|
80527
80525
|
throw new Error(
|
|
@@ -80535,17 +80533,23 @@ async function setupDaemon(args) {
|
|
|
80535
80533
|
if (setupToken && error61 instanceof RuntimeSetupGrantExpiredError) {
|
|
80536
80534
|
console.info("[alan-agent] Setup link expired; continuing with browser authorization");
|
|
80537
80535
|
await loginWithDeviceCode(args);
|
|
80538
|
-
return false;
|
|
80536
|
+
return { daemonAlreadyRunning: false };
|
|
80539
80537
|
}
|
|
80540
80538
|
throw error61;
|
|
80541
80539
|
}
|
|
80542
|
-
|
|
80540
|
+
writeConfig({
|
|
80543
80541
|
apiUrl,
|
|
80544
80542
|
wsUrl,
|
|
80545
80543
|
endpointProfile,
|
|
80546
80544
|
runtimeId: body.runtimeId,
|
|
80547
80545
|
runtimeToken: body.runtimeToken,
|
|
80548
80546
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
80547
|
+
localApiPort: getLocalApiPort(args, {}),
|
|
80548
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto19.randomBytes)(24).toString("hex"),
|
|
80549
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
80550
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80551
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80552
|
+
daemonEpoch: previousConfig.daemonEpoch,
|
|
80549
80553
|
displayName: body.displayName,
|
|
80550
80554
|
installationId
|
|
80551
80555
|
});
|
|
@@ -80554,7 +80558,7 @@ async function setupDaemon(args) {
|
|
|
80554
80558
|
runtimeId: body.runtimeId,
|
|
80555
80559
|
configPath: getConfigPath()
|
|
80556
80560
|
});
|
|
80557
|
-
return false;
|
|
80561
|
+
return { daemonAlreadyRunning: false };
|
|
80558
80562
|
}
|
|
80559
80563
|
async function loginWithDeviceCode(args) {
|
|
80560
80564
|
bindConfigPath(args);
|
|
@@ -80643,13 +80647,19 @@ async function loginWithDeviceCode(args) {
|
|
|
80643
80647
|
if (ownedPending?.operationId !== operation.id) {
|
|
80644
80648
|
throw new Error("browser authorization sidecar ownership was lost before finalization");
|
|
80645
80649
|
}
|
|
80646
|
-
|
|
80650
|
+
writeConfig({
|
|
80647
80651
|
apiUrl,
|
|
80648
80652
|
wsUrl,
|
|
80649
80653
|
endpointProfile,
|
|
80650
80654
|
runtimeId: body.runtime.id,
|
|
80651
80655
|
runtimeToken: body.runtimeToken,
|
|
80652
80656
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
80657
|
+
localApiPort: getLocalApiPort(args, {}),
|
|
80658
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto19.randomBytes)(24).toString("hex"),
|
|
80659
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
80660
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80661
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80662
|
+
daemonEpoch: previousConfig.daemonEpoch,
|
|
80653
80663
|
displayName: body.runtime.displayName ?? displayName,
|
|
80654
80664
|
installationId
|
|
80655
80665
|
});
|
|
@@ -80688,7 +80698,14 @@ async function stopDaemon(args) {
|
|
|
80688
80698
|
console.info("[alan-agent] No daemon listening", { port: localApiPort });
|
|
80689
80699
|
return;
|
|
80690
80700
|
}
|
|
80691
|
-
const response = await
|
|
80701
|
+
const response = await fetch(`http://127.0.0.1:${localApiPort}/shutdown?force=1`, {
|
|
80702
|
+
method: "POST",
|
|
80703
|
+
headers: {
|
|
80704
|
+
authorization: `Bearer ${localApiToken}`,
|
|
80705
|
+
"x-alan-shutdown-reason": "runtime_shutdown"
|
|
80706
|
+
},
|
|
80707
|
+
signal: AbortSignal.timeout(5e3)
|
|
80708
|
+
});
|
|
80692
80709
|
if (!response.ok) {
|
|
80693
80710
|
throw new Error(`Failed to stop daemon: HTTP ${response.status}`);
|
|
80694
80711
|
}
|
|
@@ -80833,7 +80850,14 @@ async function drainAndStopLocalDaemonBeforeCredentialCleanup(args, stored) {
|
|
|
80833
80850
|
`Logout could not safely drain the local daemon (HTTP ${prepare.status}). Stop or update the local service before removing credentials.`
|
|
80834
80851
|
);
|
|
80835
80852
|
}
|
|
80836
|
-
const shutdown = await
|
|
80853
|
+
const shutdown = await fetch(`http://127.0.0.1:${localApiPort}/shutdown?force=1`, {
|
|
80854
|
+
method: "POST",
|
|
80855
|
+
headers: {
|
|
80856
|
+
authorization: `Bearer ${localApiToken}`,
|
|
80857
|
+
"x-alan-shutdown-reason": "runtime_shutdown"
|
|
80858
|
+
},
|
|
80859
|
+
signal: AbortSignal.timeout(5e3)
|
|
80860
|
+
});
|
|
80837
80861
|
if (!shutdown.ok) {
|
|
80838
80862
|
throw new Error(`Logout could not stop the local daemon (HTTP ${shutdown.status}).`);
|
|
80839
80863
|
}
|
|
@@ -80994,9 +81018,6 @@ async function printDoctor(args = []) {
|
|
|
80994
81018
|
);
|
|
80995
81019
|
}
|
|
80996
81020
|
|
|
80997
|
-
// src/install-command.ts
|
|
80998
|
-
var import_promises6 = require("timers/promises");
|
|
80999
|
-
|
|
81000
81021
|
// src/mcp-config-adapters.ts
|
|
81001
81022
|
var import_node_crypto20 = require("crypto");
|
|
81002
81023
|
var import_node_fs23 = require("fs");
|
|
@@ -82381,14 +82402,12 @@ function runMcpIntegrationCommand(args, options = {}) {
|
|
|
82381
82402
|
|
|
82382
82403
|
// src/service-manager.ts
|
|
82383
82404
|
var import_node_child_process9 = require("child_process");
|
|
82384
|
-
var import_node_crypto21 = require("crypto");
|
|
82385
82405
|
var import_node_fs25 = require("fs");
|
|
82386
82406
|
var import_node_os16 = require("os");
|
|
82387
82407
|
var import_node_path27 = require("path");
|
|
82388
82408
|
var SERVICE_LABEL = "ai.tryalan.agent";
|
|
82389
82409
|
var SYSTEMD_UNIT = "alan-agent.service";
|
|
82390
82410
|
var WINDOWS_TASK = "Alan Agent";
|
|
82391
|
-
var STOP_POLL_INTERVAL_MS = 200;
|
|
82392
82411
|
function xml(value2) {
|
|
82393
82412
|
return value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
82394
82413
|
}
|
|
@@ -82398,18 +82417,6 @@ function systemdArg(value2) {
|
|
|
82398
82417
|
function powershellLiteral(value2) {
|
|
82399
82418
|
return `'${value2.replaceAll("'", "''")}'`;
|
|
82400
82419
|
}
|
|
82401
|
-
function powershellCommand(script, tolerateFailure = false) {
|
|
82402
|
-
return {
|
|
82403
|
-
command: "powershell.exe",
|
|
82404
|
-
args: ["-NoProfile", "-NonInteractive", "-Command", script],
|
|
82405
|
-
tolerateFailure
|
|
82406
|
-
};
|
|
82407
|
-
}
|
|
82408
|
-
function windowsCommandLineArgument(value2) {
|
|
82409
|
-
if (value2 && !/[\s"]/u.test(value2)) return value2;
|
|
82410
|
-
const escaped = value2.replace(/(\\*)"/gu, (_match, slashes) => `${slashes}${slashes}\\"`).replace(/\\+$/u, (slashes) => `${slashes}${slashes}`);
|
|
82411
|
-
return `"${escaped}"`;
|
|
82412
|
-
}
|
|
82413
82420
|
function serviceNames(profile) {
|
|
82414
82421
|
if (profile === "production") {
|
|
82415
82422
|
return { launchd: SERVICE_LABEL, systemd: SYSTEMD_UNIT, windows: WINDOWS_TASK };
|
|
@@ -82431,7 +82438,7 @@ function buildDaemonServicePlan(input2) {
|
|
|
82431
82438
|
const domain2 = `gui/${userId}`;
|
|
82432
82439
|
const serviceTarget = `${domain2}/${names.launchd}`;
|
|
82433
82440
|
const manifestPath = (0, import_node_path27.join)(input2.homeDir, "Library", "LaunchAgents", `${names.launchd}.plist`);
|
|
82434
|
-
const
|
|
82441
|
+
const logDir = (0, import_node_path27.join)(resolveAgentConfigDirectory(input2.profile, input2.homeDir), "logs");
|
|
82435
82442
|
const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
|
|
82436
82443
|
const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
|
|
82437
82444
|
<string>${xml(value2)}</string>`).join("\n");
|
|
@@ -82460,9 +82467,9 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82460
82467
|
<key>ThrottleInterval</key>
|
|
82461
82468
|
<integer>5</integer>
|
|
82462
82469
|
<key>StandardOutPath</key>
|
|
82463
|
-
<string>${xml((0, import_node_path27.join)(
|
|
82470
|
+
<string>${xml((0, import_node_path27.join)(logDir, "daemon.log"))}</string>
|
|
82464
82471
|
<key>StandardErrorPath</key>
|
|
82465
|
-
<string>${xml((0, import_node_path27.join)(
|
|
82472
|
+
<string>${xml((0, import_node_path27.join)(logDir, "daemon-error.log"))}</string>
|
|
82466
82473
|
</dict>
|
|
82467
82474
|
</plist>
|
|
82468
82475
|
`;
|
|
@@ -82470,7 +82477,7 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82470
82477
|
platform: "darwin",
|
|
82471
82478
|
manifestPath,
|
|
82472
82479
|
manifest,
|
|
82473
|
-
logDirectory:
|
|
82480
|
+
logDirectory: logDir,
|
|
82474
82481
|
loadDefinitionCommands: [
|
|
82475
82482
|
{
|
|
82476
82483
|
command: "launchctl",
|
|
@@ -82511,6 +82518,23 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82511
82518
|
tolerateFailure: true
|
|
82512
82519
|
}
|
|
82513
82520
|
],
|
|
82521
|
+
replaceCommands: [
|
|
82522
|
+
{
|
|
82523
|
+
command: "launchctl",
|
|
82524
|
+
args: ["bootout", domain2, manifestPath],
|
|
82525
|
+
tolerateFailure: true
|
|
82526
|
+
},
|
|
82527
|
+
{
|
|
82528
|
+
command: "launchctl",
|
|
82529
|
+
args: ["bootstrap", domain2, manifestPath],
|
|
82530
|
+
tolerateFailure: false
|
|
82531
|
+
},
|
|
82532
|
+
{
|
|
82533
|
+
command: "launchctl",
|
|
82534
|
+
args: ["kickstart", "-k", serviceTarget],
|
|
82535
|
+
tolerateFailure: false
|
|
82536
|
+
}
|
|
82537
|
+
],
|
|
82514
82538
|
uninstallCommands: [
|
|
82515
82539
|
{
|
|
82516
82540
|
command: "launchctl",
|
|
@@ -82527,7 +82551,8 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82527
82551
|
command: "launchctl",
|
|
82528
82552
|
args: ["print", serviceTarget],
|
|
82529
82553
|
tolerateFailure: true
|
|
82530
|
-
}
|
|
82554
|
+
},
|
|
82555
|
+
lingerStatusCommand: null
|
|
82531
82556
|
};
|
|
82532
82557
|
}
|
|
82533
82558
|
if (input2.platform === "linux") {
|
|
@@ -82590,6 +82615,18 @@ WantedBy=default.target
|
|
|
82590
82615
|
tolerateFailure: true
|
|
82591
82616
|
}
|
|
82592
82617
|
],
|
|
82618
|
+
replaceCommands: [
|
|
82619
|
+
{
|
|
82620
|
+
command: "systemctl",
|
|
82621
|
+
args: ["--user", "daemon-reload"],
|
|
82622
|
+
tolerateFailure: false
|
|
82623
|
+
},
|
|
82624
|
+
{
|
|
82625
|
+
command: "systemctl",
|
|
82626
|
+
args: ["--user", "restart", names.systemd],
|
|
82627
|
+
tolerateFailure: false
|
|
82628
|
+
}
|
|
82629
|
+
],
|
|
82593
82630
|
uninstallCommands: [
|
|
82594
82631
|
{
|
|
82595
82632
|
command: "systemctl",
|
|
@@ -82611,179 +82648,117 @@ WantedBy=default.target
|
|
|
82611
82648
|
command: "systemctl",
|
|
82612
82649
|
args: ["--user", "is-active", names.systemd],
|
|
82613
82650
|
tolerateFailure: true
|
|
82651
|
+
},
|
|
82652
|
+
lingerStatusCommand: {
|
|
82653
|
+
command: "loginctl",
|
|
82654
|
+
args: ["show-user", "$(id -un)", "-p", "Linger"],
|
|
82655
|
+
tolerateFailure: true
|
|
82614
82656
|
}
|
|
82615
82657
|
};
|
|
82616
82658
|
}
|
|
82617
|
-
const
|
|
82618
|
-
const
|
|
82619
|
-
const
|
|
82620
|
-
const
|
|
82621
|
-
|
|
82622
|
-
const daemonArguments = [input2.cliEntry, "daemon", "--profile", input2.profile].map(windowsCommandLineArgument).join(" ");
|
|
82623
|
-
const environmentStatements = Object.entries(input2.environment ?? {}).map(
|
|
82624
|
-
([key, value2]) => `$startInfo.EnvironmentVariables[${powershellLiteral(key)}] = ${powershellLiteral(value2)}`
|
|
82659
|
+
const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
|
|
82660
|
+
const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
|
|
82661
|
+
const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
|
|
82662
|
+
const argument = powershellLiteral(
|
|
82663
|
+
environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
|
|
82625
82664
|
);
|
|
82626
|
-
const launcherScript = [
|
|
82627
|
-
"$ErrorActionPreference = 'Stop'",
|
|
82628
|
-
"$startInfo = [System.Diagnostics.ProcessStartInfo]::new()",
|
|
82629
|
-
`$startInfo.FileName = ${powershellLiteral(input2.nodeExecutable)}`,
|
|
82630
|
-
`$startInfo.Arguments = ${powershellLiteral(daemonArguments)}`,
|
|
82631
|
-
`$startInfo.WorkingDirectory = ${powershellLiteral((0, import_node_path27.dirname)(input2.cliEntry))}`,
|
|
82632
|
-
"$startInfo.UseShellExecute = $false",
|
|
82633
|
-
"$startInfo.CreateNoWindow = $true",
|
|
82634
|
-
"$startInfo.RedirectStandardOutput = $true",
|
|
82635
|
-
"$startInfo.RedirectStandardError = $true",
|
|
82636
|
-
...environmentStatements,
|
|
82637
|
-
`$stdout = [System.IO.File]::Open(${powershellLiteral((0, import_node_path27.join)(logDir, "daemon-launcher.log"))}, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite)`,
|
|
82638
|
-
`$stderr = [System.IO.File]::Open(${powershellLiteral((0, import_node_path27.join)(logDir, "daemon-launcher-error.log"))}, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite)`,
|
|
82639
|
-
"$process = [System.Diagnostics.Process]::new()",
|
|
82640
|
-
"$process.StartInfo = $startInfo",
|
|
82641
|
-
"try { if (-not $process.Start()) { throw 'Alan daemon process did not start' }; $stdoutCopy = $process.StandardOutput.BaseStream.CopyToAsync($stdout); $stderrCopy = $process.StandardError.BaseStream.CopyToAsync($stderr); $process.WaitForExit(); [System.Threading.Tasks.Task]::WaitAll([System.Threading.Tasks.Task[]]@($stdoutCopy, $stderrCopy)); $exitCode = $process.ExitCode } finally { $stdout.Dispose(); $stderr.Dispose(); $process.Dispose() }",
|
|
82642
|
-
"exit $exitCode"
|
|
82643
|
-
].join("; ");
|
|
82644
|
-
const executable = powershellLiteral("powershell.exe");
|
|
82645
|
-
const launcherArguments = `-NoProfile -NonInteractive -WindowStyle Hidden -EncodedCommand ${Buffer.from(launcherScript, "utf16le").toString("base64")}`;
|
|
82646
|
-
const settingsScript = "New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries";
|
|
82647
|
-
const ownerMarker = `Alan daemon owner v1 ${(0, import_node_crypto21.createHash)("sha256").update(
|
|
82648
|
-
JSON.stringify({
|
|
82649
|
-
executable: input2.nodeExecutable,
|
|
82650
|
-
cliEntry: input2.cliEntry,
|
|
82651
|
-
profile: input2.profile,
|
|
82652
|
-
taskBaseName: names.windows
|
|
82653
|
-
})
|
|
82654
|
-
).digest("hex")}`;
|
|
82655
|
-
const definitionMarker = `${ownerMarker}; definition v3 ${(0, import_node_crypto21.createHash)("sha256").update(
|
|
82656
|
-
JSON.stringify({
|
|
82657
|
-
executable: "powershell.exe",
|
|
82658
|
-
launcherArguments,
|
|
82659
|
-
workingDirectory: (0, import_node_path27.dirname)(input2.cliEntry),
|
|
82660
|
-
settingsScript,
|
|
82661
|
-
taskBaseName: names.windows
|
|
82662
|
-
})
|
|
82663
|
-
).digest("hex")}`;
|
|
82664
|
-
const windowsDefinitionContext = [
|
|
82665
|
-
`$expectedLauncherArguments = ${powershellLiteral(launcherArguments)}`,
|
|
82666
|
-
`$expectedWorkingDirectory = ${powershellLiteral((0, import_node_path27.dirname)(input2.cliEntry))}`,
|
|
82667
|
-
`$expectedOwnerMarker = ${powershellLiteral(`${ownerMarker};`)}`,
|
|
82668
|
-
`$expectedDefinitionMarker = ${powershellLiteral(definitionMarker)}`,
|
|
82669
|
-
`$expectedLegacyExecutable = ${powershellLiteral(legacyExecutable)}`,
|
|
82670
|
-
`$expectedLegacyArguments = ${powershellLiteral(legacyArguments)}`
|
|
82671
|
-
].join("; ");
|
|
82672
|
-
const taskContext = [
|
|
82673
|
-
"$currentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()",
|
|
82674
|
-
"$currentUser = $currentIdentity.Name",
|
|
82675
|
-
"$currentSid = $currentIdentity.User.Value",
|
|
82676
|
-
`$taskName = ${powershellLiteral(names.windows)} + ' ' + $currentSid`
|
|
82677
|
-
].join("; ");
|
|
82678
|
-
const resolveTaskSid = "function Resolve-TaskSid([string]$value) { try { return ([System.Security.Principal.NTAccount]$value).Translate([System.Security.Principal.SecurityIdentifier]).Value } catch { try { return [System.Security.Principal.SecurityIdentifier]::new($value).Value } catch { return '' } } }";
|
|
82679
|
-
const currentLookup = "$task = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq $taskName })[0]";
|
|
82680
|
-
const checkCurrentOwnership = [
|
|
82681
|
-
currentLookup,
|
|
82682
|
-
"$currentOwned = $false",
|
|
82683
|
-
"if ($null -ne $task) { try {",
|
|
82684
|
-
"$taskAction = @($task.Actions)[0]",
|
|
82685
|
-
"$currentOwned = @($task.Actions).Count -eq 1 -and (Resolve-TaskSid ([string]$task.Principal.UserId)) -eq $currentSid -and (([string]$task.Description).StartsWith($expectedOwnerMarker, [System.StringComparison]::Ordinal) -or (([string]$taskAction.Execute) -ieq 'powershell.exe' -and ([string]$taskAction.Arguments) -ceq $expectedLauncherArguments -and ([string]$taskAction.WorkingDirectory) -ieq $expectedWorkingDirectory))",
|
|
82686
|
-
"} catch { $currentOwned = $false } }"
|
|
82687
|
-
].join("; ");
|
|
82688
|
-
const legacyLookup = `$legacyTask = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq ${powershellLiteral(names.windows)} })[0]`;
|
|
82689
|
-
const checkLegacyOwnership = [
|
|
82690
|
-
legacyLookup,
|
|
82691
|
-
"$legacyOwned = $false",
|
|
82692
|
-
"$legacyState = 'Missing'",
|
|
82693
|
-
"if ($null -ne $legacyTask) { try {",
|
|
82694
|
-
"$legacyState = [string]$legacyTask.State",
|
|
82695
|
-
"$legacySid = Resolve-TaskSid ([string]$legacyTask.Principal.UserId)",
|
|
82696
|
-
"$legacyAction = @($legacyTask.Actions)[0]",
|
|
82697
|
-
"if ($null -ne $legacyAction) {",
|
|
82698
|
-
"$legacyOwned = $legacySid -eq $currentSid -and @($legacyTask.Actions).Count -eq 1 -and ((([string]$legacyAction.Execute) -ieq $expectedLegacyExecutable -and ([string]$legacyAction.Arguments) -ieq $expectedLegacyArguments -and ([string]$legacyAction.WorkingDirectory) -eq '') -or (([string]$legacyAction.Execute) -ieq 'powershell.exe' -and ([string]$legacyAction.Arguments) -ceq $expectedLauncherArguments -and ([string]$legacyAction.WorkingDirectory) -ieq $expectedWorkingDirectory))",
|
|
82699
|
-
"}",
|
|
82700
|
-
"} catch { $legacyOwned = $false } }"
|
|
82701
|
-
].join("; ");
|
|
82702
|
-
const assertOwnedTasks = [
|
|
82703
|
-
checkCurrentOwnership,
|
|
82704
|
-
checkLegacyOwnership,
|
|
82705
|
-
`if ($null -ne $task -and -not $currentOwned) { throw ${powershellLiteral(`${names.windows} task is not owned by this Alan installation`)} }`,
|
|
82706
|
-
`if ($null -ne $legacyTask -and -not $legacyOwned) { throw ${powershellLiteral(`Legacy ${names.windows} task is not owned by this Alan installation`)} }`,
|
|
82707
|
-
"if ($null -ne $task -and @('Ready', 'Disabled', 'Queued') -notcontains ([string]$task.State)) { throw 'Alan daemon task became active or has unknown state' }",
|
|
82708
|
-
"if ($null -ne $legacyTask -and @('Ready', 'Disabled', 'Queued') -notcontains ([string]$legacyTask.State)) { throw 'Legacy Alan daemon task became active or has unknown state' }"
|
|
82709
|
-
].join("; ");
|
|
82710
|
-
const cleanupLegacyTask = [
|
|
82711
|
-
checkLegacyOwnership,
|
|
82712
|
-
`if ($null -ne $legacyTask -and -not $legacyOwned) { throw ${powershellLiteral(`Legacy ${names.windows} task is not owned by this Alan installation`)} }`,
|
|
82713
|
-
"if ($null -ne $legacyTask -and @('Ready', 'Disabled', 'Queued') -notcontains ([string]$legacyTask.State)) { throw 'Legacy Alan daemon task became active or has unknown state' }",
|
|
82714
|
-
`if ($null -ne $legacyTask) { Unregister-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -TaskPath '\\' -Confirm:$false -ErrorAction Stop }`,
|
|
82715
|
-
`$legacyRemaining = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq ${powershellLiteral(names.windows)} })[0]`,
|
|
82716
|
-
"if ($null -ne $legacyRemaining) { throw 'Legacy Alan daemon task still exists after migration' }"
|
|
82717
|
-
].join("; ");
|
|
82718
82665
|
const registerScript = [
|
|
82719
|
-
|
|
82720
|
-
|
|
82721
|
-
windowsDefinitionContext,
|
|
82722
|
-
`$action = New-ScheduledTaskAction -Execute ${executable} -Argument $expectedLauncherArguments -WorkingDirectory $expectedWorkingDirectory`,
|
|
82666
|
+
"$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
|
|
82667
|
+
`$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
|
|
82723
82668
|
"$trigger = New-ScheduledTaskTrigger -AtLogOn -User $currentUser",
|
|
82724
82669
|
"$principal = New-ScheduledTaskPrincipal -UserId $currentUser -LogonType Interactive -RunLevel Limited",
|
|
82725
|
-
|
|
82726
|
-
|
|
82727
|
-
"Register-ScheduledTask -TaskName $taskName -Description $expectedDefinitionMarker -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force -ErrorAction Stop | Out-Null",
|
|
82728
|
-
cleanupLegacyTask,
|
|
82729
|
-
"$registeredTask = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq $taskName })[0]",
|
|
82730
|
-
"if ($null -eq $registeredTask -or $registeredTask.Description -cne $expectedDefinitionMarker) { throw 'Alan daemon task definition was not applied' }"
|
|
82731
|
-
].join("; ");
|
|
82732
|
-
const uninstallScript = [
|
|
82733
|
-
taskContext,
|
|
82734
|
-
resolveTaskSid,
|
|
82735
|
-
windowsDefinitionContext,
|
|
82736
|
-
assertOwnedTasks,
|
|
82737
|
-
"if ($null -ne $legacyTask) { Unregister-ScheduledTask -TaskName $legacyTask.TaskName -TaskPath $legacyTask.TaskPath -Confirm:$false -ErrorAction Stop }",
|
|
82738
|
-
"$task = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq $taskName })[0]",
|
|
82739
|
-
"if ($null -ne $task) { Unregister-ScheduledTask -TaskName $taskName -TaskPath '\\' -Confirm:$false -ErrorAction Stop }",
|
|
82740
|
-
"$remaining = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and ($_.TaskName -eq $taskName -or $_.TaskName -eq $legacyTask.TaskName) })",
|
|
82741
|
-
"if ($remaining.Count -gt 0) { throw 'Alan daemon task still exists after unregister' }"
|
|
82670
|
+
"$settings = New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero)",
|
|
82671
|
+
`Register-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null`
|
|
82742
82672
|
].join("; ");
|
|
82743
|
-
const statusScript = [
|
|
82744
|
-
taskContext,
|
|
82745
|
-
resolveTaskSid,
|
|
82746
|
-
windowsDefinitionContext,
|
|
82747
|
-
checkCurrentOwnership,
|
|
82748
|
-
checkLegacyOwnership,
|
|
82749
|
-
"$definitionMatches = $false",
|
|
82750
|
-
"if ($null -eq $task) { Write-Output 'State: Missing' } else {",
|
|
82751
|
-
"$taskTrigger = @($task.Triggers)[0]",
|
|
82752
|
-
"$definitionMatches = @($task.Actions).Count -eq 1 -and @($task.Triggers).Count -eq 1 -and $task.Description -ceq $expectedDefinitionMarker -and (Resolve-TaskSid ([string]$task.Principal.UserId)) -eq $currentSid -and [string]$task.Principal.LogonType -eq 'Interactive' -and [string]$task.Principal.RunLevel -eq 'Limited' -and [string]$taskAction.Execute -ieq 'powershell.exe' -and [string]$taskAction.Arguments -ceq $expectedLauncherArguments -and [string]$taskAction.WorkingDirectory -ieq $expectedWorkingDirectory -and $taskTrigger.CimClass.CimClassName -eq 'MSFT_TaskLogonTrigger' -and (Resolve-TaskSid ([string]$taskTrigger.UserId)) -eq $currentSid -and -not $task.Settings.DisallowStartIfOnBatteries -and -not $task.Settings.StopIfGoingOnBatteries -and $task.Settings.RestartCount -eq 3 -and [string]$task.Settings.RestartInterval -eq 'PT1M' -and [string]$task.Settings.ExecutionTimeLimit -eq 'PT0S'",
|
|
82753
|
-
"Write-Output ('TaskName: ' + $task.TaskName); Write-Output ('State: ' + $task.State); Write-Output ('Definition: ' + $task.Description)",
|
|
82754
|
-
"}",
|
|
82755
|
-
"Write-Output ('DefinitionMatch: ' + $definitionMatches)",
|
|
82756
|
-
"Write-Output ('CurrentOwned: ' + $currentOwned)",
|
|
82757
|
-
"$legacyPresent = $null -ne $legacyTask",
|
|
82758
|
-
"Write-Output ('LegacyTask: ' + $legacyPresent)",
|
|
82759
|
-
"Write-Output ('LegacyOwned: ' + $legacyOwned)",
|
|
82760
|
-
"Write-Output ('LegacyState: ' + $legacyState)"
|
|
82761
|
-
].join("; ");
|
|
82762
|
-
const activeScript = [
|
|
82763
|
-
taskContext,
|
|
82764
|
-
"$task = @(Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.TaskPath -eq '\\' -and $_.TaskName -eq $taskName })[0]",
|
|
82765
|
-
"if ($null -eq $task) { Write-Output 'Missing' } else { Write-Output $task.State }"
|
|
82766
|
-
].join("; ");
|
|
82767
|
-
const registerCommand = powershellCommand(registerScript);
|
|
82768
82673
|
return {
|
|
82769
82674
|
platform: "win32",
|
|
82770
82675
|
manifestPath: null,
|
|
82771
82676
|
manifest: null,
|
|
82772
|
-
logDirectory:
|
|
82773
|
-
loadDefinitionCommands: [
|
|
82774
|
-
|
|
82677
|
+
logDirectory: null,
|
|
82678
|
+
loadDefinitionCommands: [
|
|
82679
|
+
{
|
|
82680
|
+
command: "powershell.exe",
|
|
82681
|
+
args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
|
|
82682
|
+
tolerateFailure: false
|
|
82683
|
+
}
|
|
82684
|
+
],
|
|
82685
|
+
reloadDefinitionCommands: [
|
|
82686
|
+
{
|
|
82687
|
+
command: "powershell.exe",
|
|
82688
|
+
args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
|
|
82689
|
+
tolerateFailure: false
|
|
82690
|
+
}
|
|
82691
|
+
],
|
|
82775
82692
|
enableCommands: [],
|
|
82776
|
-
startCommands: [
|
|
82693
|
+
startCommands: [
|
|
82694
|
+
{
|
|
82695
|
+
command: "powershell.exe",
|
|
82696
|
+
args: [
|
|
82697
|
+
"-NoProfile",
|
|
82698
|
+
"-NonInteractive",
|
|
82699
|
+
"-Command",
|
|
82700
|
+
`Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
|
|
82701
|
+
],
|
|
82702
|
+
tolerateFailure: false
|
|
82703
|
+
}
|
|
82704
|
+
],
|
|
82777
82705
|
stopCommands: [
|
|
82778
|
-
|
|
82779
|
-
|
|
82780
|
-
|
|
82781
|
-
|
|
82706
|
+
{
|
|
82707
|
+
command: "powershell.exe",
|
|
82708
|
+
args: [
|
|
82709
|
+
"-NoProfile",
|
|
82710
|
+
"-NonInteractive",
|
|
82711
|
+
"-Command",
|
|
82712
|
+
`Stop-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue`
|
|
82713
|
+
],
|
|
82714
|
+
tolerateFailure: true
|
|
82715
|
+
}
|
|
82782
82716
|
],
|
|
82783
|
-
|
|
82784
|
-
|
|
82785
|
-
|
|
82786
|
-
|
|
82717
|
+
replaceCommands: [
|
|
82718
|
+
{
|
|
82719
|
+
command: "powershell.exe",
|
|
82720
|
+
args: [
|
|
82721
|
+
"-NoProfile",
|
|
82722
|
+
"-NonInteractive",
|
|
82723
|
+
"-Command",
|
|
82724
|
+
`${registerScript}; Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
|
|
82725
|
+
],
|
|
82726
|
+
tolerateFailure: false
|
|
82727
|
+
}
|
|
82728
|
+
],
|
|
82729
|
+
uninstallCommands: [
|
|
82730
|
+
{
|
|
82731
|
+
command: "powershell.exe",
|
|
82732
|
+
args: [
|
|
82733
|
+
"-NoProfile",
|
|
82734
|
+
"-NonInteractive",
|
|
82735
|
+
"-Command",
|
|
82736
|
+
`Unregister-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Confirm:$false -ErrorAction SilentlyContinue`
|
|
82737
|
+
],
|
|
82738
|
+
tolerateFailure: true
|
|
82739
|
+
}
|
|
82740
|
+
],
|
|
82741
|
+
statusCommand: {
|
|
82742
|
+
command: "powershell.exe",
|
|
82743
|
+
args: [
|
|
82744
|
+
"-NoProfile",
|
|
82745
|
+
"-NonInteractive",
|
|
82746
|
+
"-Command",
|
|
82747
|
+
`Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} | Format-List TaskName,State`
|
|
82748
|
+
],
|
|
82749
|
+
tolerateFailure: true
|
|
82750
|
+
},
|
|
82751
|
+
isActiveCommand: {
|
|
82752
|
+
command: "powershell.exe",
|
|
82753
|
+
args: [
|
|
82754
|
+
"-NoProfile",
|
|
82755
|
+
"-NonInteractive",
|
|
82756
|
+
"-Command",
|
|
82757
|
+
`(Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue).State`
|
|
82758
|
+
],
|
|
82759
|
+
tolerateFailure: true
|
|
82760
|
+
},
|
|
82761
|
+
lingerStatusCommand: null
|
|
82787
82762
|
};
|
|
82788
82763
|
}
|
|
82789
82764
|
function resolveCurrentPlatform() {
|
|
@@ -82793,14 +82768,14 @@ function resolveCurrentPlatform() {
|
|
|
82793
82768
|
}
|
|
82794
82769
|
return currentPlatform;
|
|
82795
82770
|
}
|
|
82796
|
-
function currentServicePlan(args, runtime
|
|
82797
|
-
const currentPlatform =
|
|
82771
|
+
function currentServicePlan(args, runtime) {
|
|
82772
|
+
const currentPlatform = resolveCurrentPlatform();
|
|
82798
82773
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
82799
82774
|
const cliEntry = runtime?.cliEntry ?? process.argv[1];
|
|
82800
82775
|
if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
|
|
82801
82776
|
return buildDaemonServicePlan({
|
|
82802
82777
|
platform: currentPlatform,
|
|
82803
|
-
homeDir:
|
|
82778
|
+
homeDir: (0, import_node_os16.homedir)(),
|
|
82804
82779
|
nodeExecutable: runtime?.executable ?? process.execPath,
|
|
82805
82780
|
cliEntry: (0, import_node_path27.resolve)(cliEntry),
|
|
82806
82781
|
profile,
|
|
@@ -82823,13 +82798,9 @@ function defaultReadManifest(path2) {
|
|
|
82823
82798
|
return (0, import_node_fs25.readFileSync)(path2, "utf8");
|
|
82824
82799
|
}
|
|
82825
82800
|
function runServiceCommand(command) {
|
|
82826
|
-
const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, {
|
|
82827
|
-
encoding: "utf8",
|
|
82828
|
-
shell: false,
|
|
82829
|
-
...(0, import_node_os16.platform)() === "win32" ? { timeout: 15e3, windowsHide: true } : {}
|
|
82830
|
-
});
|
|
82801
|
+
const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, { encoding: "utf8", shell: false });
|
|
82831
82802
|
const status = result.status ?? 1;
|
|
82832
|
-
const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}
|
|
82803
|
+
const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
82833
82804
|
if (status !== 0 && !command.tolerateFailure) {
|
|
82834
82805
|
throw new Error(
|
|
82835
82806
|
`Service command failed (${command.command} ${command.args.join(" ")}): ${output2 || `exit ${status}`}`
|
|
@@ -82842,23 +82813,6 @@ function defaultSleep(ms) {
|
|
|
82842
82813
|
setTimeout(resolveSleep, ms);
|
|
82843
82814
|
});
|
|
82844
82815
|
}
|
|
82845
|
-
function isProcessAlive(pid) {
|
|
82846
|
-
if (pid <= 0) return false;
|
|
82847
|
-
try {
|
|
82848
|
-
process.kill(pid, 0);
|
|
82849
|
-
return true;
|
|
82850
|
-
} catch (error61) {
|
|
82851
|
-
return error61.code === "EPERM";
|
|
82852
|
-
}
|
|
82853
|
-
}
|
|
82854
|
-
async function requestWindowsDaemonShutdown(owner) {
|
|
82855
|
-
try {
|
|
82856
|
-
const response = await requestLocalDaemonShutdown(owner.localApiPort, owner.localApiToken);
|
|
82857
|
-
return response.ok ? "accepted" : "rejected";
|
|
82858
|
-
} catch {
|
|
82859
|
-
return "unreachable";
|
|
82860
|
-
}
|
|
82861
|
-
}
|
|
82862
82816
|
function parseServiceHostState(servicePlatform, result) {
|
|
82863
82817
|
const output2 = result.output;
|
|
82864
82818
|
if (servicePlatform === "linux") {
|
|
@@ -82884,62 +82838,9 @@ function parseServiceHostState(servicePlatform, result) {
|
|
|
82884
82838
|
const stateMatch = output2.match(/\b(?:State|state)\s*[:=]\s*(\w+)/);
|
|
82885
82839
|
const state = (stateMatch?.[1] ?? output2.trim()).toLowerCase();
|
|
82886
82840
|
if (state === "running") return "running";
|
|
82887
|
-
if (state === "queued") return "
|
|
82888
|
-
if (state === "ready" || state === "disabled") return "stopped";
|
|
82841
|
+
if (state === "ready" || state === "disabled" || state === "queued") return "stopped";
|
|
82889
82842
|
return "unknown";
|
|
82890
82843
|
}
|
|
82891
|
-
function parseWindowsTaskInspection(result) {
|
|
82892
|
-
if (result.status !== 0) {
|
|
82893
|
-
throw new Error("Cannot inspect Windows daemon task definition");
|
|
82894
|
-
}
|
|
82895
|
-
const value2 = (name) => {
|
|
82896
|
-
const match = result.output.match(new RegExp(`^${name}:\\s*(.*)$`, "imu"));
|
|
82897
|
-
if (!match) throw new Error(`Incomplete Windows daemon task inspection: missing ${name}`);
|
|
82898
|
-
return match[1]?.trim() ?? "";
|
|
82899
|
-
};
|
|
82900
|
-
const boolean4 = (name) => {
|
|
82901
|
-
const parsed = value2(name).toLowerCase();
|
|
82902
|
-
if (parsed === "true") return true;
|
|
82903
|
-
if (parsed === "false") return false;
|
|
82904
|
-
throw new Error(`Incomplete Windows daemon task inspection: invalid ${name}`);
|
|
82905
|
-
};
|
|
82906
|
-
const currentPresent = /^TaskName:\s*.+$/imu.test(result.output);
|
|
82907
|
-
const currentStateValue = value2("State");
|
|
82908
|
-
const currentOwned = boolean4("CurrentOwned");
|
|
82909
|
-
const definitionMatches = boolean4("DefinitionMatch");
|
|
82910
|
-
const legacyPresent = boolean4("LegacyTask");
|
|
82911
|
-
const legacyOwned = boolean4("LegacyOwned");
|
|
82912
|
-
const legacyStateValue = value2("LegacyState");
|
|
82913
|
-
if (currentPresent !== (currentStateValue.toLowerCase() !== "missing")) {
|
|
82914
|
-
throw new Error("Incomplete Windows daemon task inspection: inconsistent current task state");
|
|
82915
|
-
}
|
|
82916
|
-
if (!currentPresent && (currentOwned || definitionMatches)) {
|
|
82917
|
-
throw new Error(
|
|
82918
|
-
"Incomplete Windows daemon task inspection: absent current task is marked owned"
|
|
82919
|
-
);
|
|
82920
|
-
}
|
|
82921
|
-
if (legacyPresent !== (legacyStateValue.toLowerCase() !== "missing") || !legacyPresent && legacyOwned) {
|
|
82922
|
-
throw new Error("Incomplete Windows daemon task inspection: inconsistent legacy task state");
|
|
82923
|
-
}
|
|
82924
|
-
return {
|
|
82925
|
-
currentPresent,
|
|
82926
|
-
currentOwned,
|
|
82927
|
-
currentState: currentPresent ? parseServiceHostState("win32", { status: 0, output: currentStateValue }) : "stopped",
|
|
82928
|
-
definitionMatches,
|
|
82929
|
-
definitionMarker: result.output.match(/^Definition:\s*(.+)$/imu)?.[1]?.trim(),
|
|
82930
|
-
legacyPresent,
|
|
82931
|
-
legacyOwned,
|
|
82932
|
-
legacyState: legacyPresent ? parseServiceHostState("win32", { status: 0, output: legacyStateValue }) : "stopped"
|
|
82933
|
-
};
|
|
82934
|
-
}
|
|
82935
|
-
function assertWindowsTaskOwnership(tasks) {
|
|
82936
|
-
if (tasks.currentPresent && !tasks.currentOwned) {
|
|
82937
|
-
throw new Error("Windows daemon task is not owned by this Alan installation");
|
|
82938
|
-
}
|
|
82939
|
-
if (tasks.legacyPresent && !tasks.legacyOwned) {
|
|
82940
|
-
throw new Error("Legacy Windows daemon task is not owned by this Alan installation");
|
|
82941
|
-
}
|
|
82942
|
-
}
|
|
82943
82844
|
function resolveEnsureDefinitionOutcome(input2) {
|
|
82944
82845
|
if (!input2.definitionChanged) {
|
|
82945
82846
|
return { changed: false, updatePending: false };
|
|
@@ -82949,6 +82850,9 @@ function resolveEnsureDefinitionOutcome(input2) {
|
|
|
82949
82850
|
}
|
|
82950
82851
|
return { changed: true, updatePending: false };
|
|
82951
82852
|
}
|
|
82853
|
+
function shouldStartOnInstall(hostState) {
|
|
82854
|
+
return hostState === "stopped";
|
|
82855
|
+
}
|
|
82952
82856
|
function resolveUsername(options) {
|
|
82953
82857
|
if (options?.username) return options.username;
|
|
82954
82858
|
try {
|
|
@@ -82957,14 +82861,17 @@ function resolveUsername(options) {
|
|
|
82957
82861
|
return process.env.USER || process.env.USERNAME || "unknown";
|
|
82958
82862
|
}
|
|
82959
82863
|
}
|
|
82960
|
-
function
|
|
82961
|
-
|
|
82962
|
-
const run = options.runCommand ?? runServiceCommand;
|
|
82963
|
-
const result = run({
|
|
82864
|
+
function buildLingerStatusCommand(username) {
|
|
82865
|
+
return {
|
|
82964
82866
|
command: "loginctl",
|
|
82965
82867
|
args: ["show-user", username, "-p", "Linger"],
|
|
82966
82868
|
tolerateFailure: true
|
|
82967
|
-
}
|
|
82869
|
+
};
|
|
82870
|
+
}
|
|
82871
|
+
function assertSystemdUserLingerEnabled(options = {}) {
|
|
82872
|
+
const username = resolveUsername(options);
|
|
82873
|
+
const run = options.runCommand ?? runServiceCommand;
|
|
82874
|
+
const result = run(buildLingerStatusCommand(username));
|
|
82968
82875
|
if (result.status !== 0) {
|
|
82969
82876
|
throw new Error(
|
|
82970
82877
|
`Cannot verify systemd user linger for ${username} (loginctl failed). For durable headless VM boot run: sudo loginctl enable-linger ${username}`
|
|
@@ -82978,9 +82885,9 @@ function assertSystemdUserLingerEnabled(options = {}) {
|
|
|
82978
82885
|
}
|
|
82979
82886
|
function createHostContext(options = {}) {
|
|
82980
82887
|
const args = options.args ?? [];
|
|
82981
|
-
const plan = currentServicePlan(args, options.runtime
|
|
82888
|
+
const plan = currentServicePlan(args, options.runtime);
|
|
82982
82889
|
const run = options.runCommand ?? runServiceCommand;
|
|
82983
|
-
const
|
|
82890
|
+
const sleep4 = options.sleep ?? defaultSleep;
|
|
82984
82891
|
const nowMs = options.nowMs ?? Date.now;
|
|
82985
82892
|
const readManifest = options.readManifest ?? defaultReadManifest;
|
|
82986
82893
|
const writeManifest = options.writeManifest ?? writeManifestAtomic;
|
|
@@ -82992,20 +82899,13 @@ function createHostContext(options = {}) {
|
|
|
82992
82899
|
args,
|
|
82993
82900
|
plan,
|
|
82994
82901
|
run,
|
|
82995
|
-
sleep:
|
|
82902
|
+
sleep: sleep4,
|
|
82996
82903
|
nowMs,
|
|
82997
82904
|
readManifest,
|
|
82998
82905
|
writeManifest,
|
|
82999
82906
|
removeManifest,
|
|
83000
82907
|
ensureLogDir,
|
|
83001
|
-
|
|
83002
|
-
readOwner: options.readOwner ?? readLocalDaemonOwnerLease,
|
|
83003
|
-
isProcessAlive: options.isProcessAlive ?? isProcessAlive,
|
|
83004
|
-
probeEndpoint: options.probeEndpoint ?? probeLocalDaemonEndpoint,
|
|
83005
|
-
requestWindowsShutdown: options.requestWindowsShutdown ?? requestWindowsDaemonShutdown,
|
|
83006
|
-
inspectJournal: options.inspectJournal ?? inspectLocalDaemonRunJournal,
|
|
83007
|
-
acquireMaintenance: options.acquireMaintenance ?? acquireLocalDaemonMaintenanceLease,
|
|
83008
|
-
releaseMaintenance: options.releaseMaintenance ?? releaseLocalDaemonMaintenanceLease
|
|
82908
|
+
profile: resolveEndpointProfileFromArgs(args)
|
|
83009
82909
|
};
|
|
83010
82910
|
}
|
|
83011
82911
|
function inspectDaemonService(options = {}) {
|
|
@@ -83021,9 +82921,8 @@ function inspectDaemonService(options = {}) {
|
|
|
83021
82921
|
}
|
|
83022
82922
|
function ensureDaemonServiceDefinition(options = {}) {
|
|
83023
82923
|
const ctx = createHostContext(options);
|
|
83024
|
-
const { plan, run,
|
|
82924
|
+
const { plan, run, readManifest, writeManifest, ensureLogDir } = ctx;
|
|
83025
82925
|
let definitionChanged = false;
|
|
83026
|
-
let inspection;
|
|
83027
82926
|
if (plan.manifestPath && plan.manifest) {
|
|
83028
82927
|
ensureLogDir();
|
|
83029
82928
|
const existing = readManifest(plan.manifestPath);
|
|
@@ -83032,38 +82931,20 @@ function ensureDaemonServiceDefinition(options = {}) {
|
|
|
83032
82931
|
writeManifest(plan.manifestPath, plan.manifest);
|
|
83033
82932
|
}
|
|
83034
82933
|
} else if (plan.platform === "win32") {
|
|
83035
|
-
|
|
83036
|
-
const result = run(plan.statusCommand);
|
|
83037
|
-
const tasks = parseWindowsTaskInspection(result);
|
|
83038
|
-
assertWindowsTaskOwnership(tasks);
|
|
83039
|
-
if (tasks.currentPresent && tasks.currentState === "unknown" || tasks.legacyPresent && tasks.legacyState === "unknown") {
|
|
83040
|
-
throw new Error("Cannot safely update a Windows daemon task with unknown state");
|
|
83041
|
-
}
|
|
83042
|
-
definitionChanged = tasks.definitionMarker !== plan.definitionMarker || !tasks.definitionMatches || tasks.legacyPresent;
|
|
83043
|
-
const running = tasks.currentState === "running" || tasks.legacyState === "running";
|
|
83044
|
-
inspection = {
|
|
83045
|
-
state: running ? "running" : tasks.currentState,
|
|
83046
|
-
platform: plan.platform,
|
|
83047
|
-
detail: result.output || void 0,
|
|
83048
|
-
observedAtMs: nowMs()
|
|
83049
|
-
};
|
|
82934
|
+
definitionChanged = true;
|
|
83050
82935
|
}
|
|
83051
|
-
inspection
|
|
83052
|
-
const definitionHostState = plan.platform === "win32" && inspection.state === "starting" ? "stopped" : inspection.state;
|
|
82936
|
+
const inspection = inspectDaemonService(options);
|
|
83053
82937
|
const outcome = resolveEnsureDefinitionOutcome({
|
|
83054
82938
|
definitionChanged,
|
|
83055
|
-
hostState:
|
|
82939
|
+
hostState: inspection.state
|
|
83056
82940
|
});
|
|
83057
82941
|
if (outcome.updatePending) {
|
|
83058
82942
|
return outcome;
|
|
83059
82943
|
}
|
|
83060
|
-
if (
|
|
83061
|
-
return outcome;
|
|
83062
|
-
}
|
|
83063
|
-
if (definitionHostState === "running" || definitionHostState === "starting") {
|
|
82944
|
+
if (inspection.state === "running" || inspection.state === "starting") {
|
|
83064
82945
|
return outcome;
|
|
83065
82946
|
}
|
|
83066
|
-
const definitionCommands =
|
|
82947
|
+
const definitionCommands = inspection.state === "stopped" ? plan.reloadDefinitionCommands : plan.loadDefinitionCommands;
|
|
83067
82948
|
for (const command of definitionCommands) run(command);
|
|
83068
82949
|
return outcome;
|
|
83069
82950
|
}
|
|
@@ -83075,140 +82956,10 @@ function startDaemonService(options = {}) {
|
|
|
83075
82956
|
const { plan, run } = createHostContext(options);
|
|
83076
82957
|
for (const command of plan.startCommands) run(command);
|
|
83077
82958
|
}
|
|
83078
|
-
|
|
83079
|
-
const {
|
|
83080
|
-
plan,
|
|
83081
|
-
run,
|
|
83082
|
-
sleep: sleep5,
|
|
83083
|
-
nowMs,
|
|
83084
|
-
configPath,
|
|
83085
|
-
readOwner,
|
|
83086
|
-
isProcessAlive: processIsAlive,
|
|
83087
|
-
probeEndpoint,
|
|
83088
|
-
requestWindowsShutdown: requestShutdown
|
|
83089
|
-
} = createHostContext(options);
|
|
83090
|
-
let owner;
|
|
83091
|
-
try {
|
|
83092
|
-
owner = readOwner(configPath);
|
|
83093
|
-
} catch {
|
|
83094
|
-
return "identity_changed";
|
|
83095
|
-
}
|
|
83096
|
-
const ownerMatches = (candidate) => candidate.pid === identity.pid && candidate.daemonSessionId === identity.daemonSessionId && candidate.localApiPort === identity.port && (identity.serviceId === void 0 || candidate.localServiceId === identity.serviceId) && (identity.startedAtMs === void 0 || candidate.startedAtMs === identity.startedAtMs);
|
|
83097
|
-
if (!owner || !ownerMatches(owner)) {
|
|
83098
|
-
return "identity_changed";
|
|
83099
|
-
}
|
|
83100
|
-
if (plan.platform === "win32") {
|
|
83101
|
-
const shutdown = await requestShutdown(owner);
|
|
83102
|
-
if (shutdown === "rejected") return "identity_changed";
|
|
83103
|
-
if (shutdown === "unreachable") {
|
|
83104
|
-
for (const command of plan.stopCommands) run(command);
|
|
83105
|
-
}
|
|
83106
|
-
const deadline2 = nowMs() + timeoutMs;
|
|
83107
|
-
while (nowMs() < deadline2) {
|
|
83108
|
-
let currentOwner;
|
|
83109
|
-
try {
|
|
83110
|
-
currentOwner = readOwner(configPath);
|
|
83111
|
-
} catch {
|
|
83112
|
-
return "identity_changed";
|
|
83113
|
-
}
|
|
83114
|
-
if (currentOwner && !ownerMatches(currentOwner)) {
|
|
83115
|
-
return "identity_changed";
|
|
83116
|
-
}
|
|
83117
|
-
if (!processIsAlive(identity.pid)) {
|
|
83118
|
-
const endpoint = await probeEndpoint(identity.port, owner.localApiToken, 250);
|
|
83119
|
-
if (endpoint.state === "absent") return "exited";
|
|
83120
|
-
if (endpoint.state === "listening_unauthorized" || endpoint.status.daemonSessionId !== identity.daemonSessionId) {
|
|
83121
|
-
return "identity_changed";
|
|
83122
|
-
}
|
|
83123
|
-
}
|
|
83124
|
-
await sleep5(STOP_POLL_INTERVAL_MS);
|
|
83125
|
-
}
|
|
83126
|
-
return "timeout";
|
|
83127
|
-
}
|
|
83128
|
-
for (const command of plan.stopCommands) run(command);
|
|
83129
|
-
const deadline = nowMs() + timeoutMs;
|
|
83130
|
-
while (nowMs() < deadline) {
|
|
83131
|
-
const result = run(plan.isActiveCommand);
|
|
83132
|
-
const state = parseServiceHostState(plan.platform, result);
|
|
83133
|
-
if (state === "stopped") return "exited";
|
|
83134
|
-
if (state === "unknown") {
|
|
83135
|
-
} else if (identity.pid > 0) {
|
|
83136
|
-
try {
|
|
83137
|
-
process.kill(identity.pid, 0);
|
|
83138
|
-
} catch {
|
|
83139
|
-
return "exited";
|
|
83140
|
-
}
|
|
83141
|
-
}
|
|
83142
|
-
await sleep5(STOP_POLL_INTERVAL_MS);
|
|
83143
|
-
}
|
|
83144
|
-
return "timeout";
|
|
83145
|
-
}
|
|
83146
|
-
async function uninstallDaemonService(args = [], options = {}) {
|
|
82959
|
+
function uninstallDaemonService(args = [], options = {}) {
|
|
83147
82960
|
const ctx = createHostContext({ ...options, args: options.args ?? args });
|
|
83148
|
-
|
|
83149
|
-
|
|
83150
|
-
let tasks;
|
|
83151
|
-
try {
|
|
83152
|
-
tasks = parseWindowsTaskInspection(ownership);
|
|
83153
|
-
} catch (error61) {
|
|
83154
|
-
throw new Error(
|
|
83155
|
-
`Cannot inspect Windows daemon task ownership before uninstall: ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
83156
|
-
);
|
|
83157
|
-
}
|
|
83158
|
-
assertWindowsTaskOwnership(tasks);
|
|
83159
|
-
if (!tasks.currentPresent && !tasks.legacyPresent) {
|
|
83160
|
-
console.info("[alan-agent] Per-user daemon service removed");
|
|
83161
|
-
return;
|
|
83162
|
-
}
|
|
83163
|
-
const acquired = ctx.acquireMaintenance(ctx.configPath, "watchdog", 3e4);
|
|
83164
|
-
if (!acquired.acquired) {
|
|
83165
|
-
throw new Error("Cannot uninstall the Windows daemon service while maintenance is active");
|
|
83166
|
-
}
|
|
83167
|
-
try {
|
|
83168
|
-
const journal = ctx.inspectJournal(ctx.configPath);
|
|
83169
|
-
if (!journal.safeToRestart) {
|
|
83170
|
-
throw new Error(
|
|
83171
|
-
`Windows daemon service uninstall is deferred while ${journal.activeRunCount} active run${journal.activeRunCount === 1 ? "" : "s"} finish`
|
|
83172
|
-
);
|
|
83173
|
-
}
|
|
83174
|
-
let owner;
|
|
83175
|
-
try {
|
|
83176
|
-
owner = ctx.readOwner(ctx.configPath);
|
|
83177
|
-
} catch (error61) {
|
|
83178
|
-
throw new Error(
|
|
83179
|
-
`Cannot verify Windows daemon ownership before uninstall: ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
83180
|
-
);
|
|
83181
|
-
}
|
|
83182
|
-
if (owner) {
|
|
83183
|
-
const stopped = await stopDaemonServiceAndWait(
|
|
83184
|
-
{
|
|
83185
|
-
pid: owner.pid,
|
|
83186
|
-
serviceId: owner.localServiceId,
|
|
83187
|
-
daemonSessionId: owner.daemonSessionId,
|
|
83188
|
-
port: owner.localApiPort,
|
|
83189
|
-
startedAtMs: owner.startedAtMs
|
|
83190
|
-
},
|
|
83191
|
-
1e4,
|
|
83192
|
-
{ ...options, args: ctx.args }
|
|
83193
|
-
);
|
|
83194
|
-
if (stopped !== "exited") {
|
|
83195
|
-
throw new Error(
|
|
83196
|
-
stopped === "identity_changed" ? "Windows daemon ownership changed before uninstall" : "Timed out waiting for the Windows daemon to stop before uninstall"
|
|
83197
|
-
);
|
|
83198
|
-
}
|
|
83199
|
-
} else {
|
|
83200
|
-
if (tasks.currentState !== "stopped" || tasks.legacyState !== "stopped") {
|
|
83201
|
-
throw new Error("Cannot uninstall a running Windows daemon without verified ownership");
|
|
83202
|
-
}
|
|
83203
|
-
}
|
|
83204
|
-
for (const command of ctx.plan.uninstallCommands) ctx.run(command);
|
|
83205
|
-
} finally {
|
|
83206
|
-
ctx.releaseMaintenance(ctx.configPath, acquired.lease.id);
|
|
83207
|
-
}
|
|
83208
|
-
} else {
|
|
83209
|
-
for (const command of ctx.plan.uninstallCommands) ctx.run(command);
|
|
83210
|
-
if (ctx.plan.manifestPath) ctx.removeManifest(ctx.plan.manifestPath);
|
|
83211
|
-
}
|
|
82961
|
+
for (const command of ctx.plan.uninstallCommands) ctx.run(command);
|
|
82962
|
+
if (ctx.plan.manifestPath) ctx.removeManifest(ctx.plan.manifestPath);
|
|
83212
82963
|
console.info("[alan-agent] Per-user daemon service removed");
|
|
83213
82964
|
}
|
|
83214
82965
|
function installDaemonService(args = [], runtime, options = {}) {
|
|
@@ -83236,7 +82987,7 @@ function installDaemonService(args = [], runtime, options = {}) {
|
|
|
83236
82987
|
});
|
|
83237
82988
|
return;
|
|
83238
82989
|
}
|
|
83239
|
-
if (inspection.state
|
|
82990
|
+
if (!shouldStartOnInstall(inspection.state)) {
|
|
83240
82991
|
console.info("[alan-agent] Per-user daemon service already present", {
|
|
83241
82992
|
profile,
|
|
83242
82993
|
state: inspection.state
|
|
@@ -83260,48 +83011,14 @@ ${result.output}
|
|
|
83260
83011
|
}
|
|
83261
83012
|
|
|
83262
83013
|
// src/install-command.ts
|
|
83263
|
-
|
|
83264
|
-
async function runInstallCommand(commandArgs, currentPlatform = process.platform) {
|
|
83265
|
-
if (currentPlatform === "win32" && commandArgs.includes("--headless")) {
|
|
83266
|
-
throw new Error("--headless is only supported on Linux systemd user services");
|
|
83267
|
-
}
|
|
83014
|
+
async function runInstallCommand(commandArgs) {
|
|
83268
83015
|
bindConfigPath(commandArgs);
|
|
83269
83016
|
const configured = readConfig();
|
|
83270
83017
|
const configurationMode = resolveInstallConfigurationMode(commandArgs, configured);
|
|
83271
|
-
|
|
83272
|
-
const deadline = isWindowsSetup ? Date.now() + WINDOWS_SETUP_READY_TIMEOUT_MS : 0;
|
|
83273
|
-
const runtimeIdentityChanged = configurationMode === "setup" ? await setupDaemon(commandArgs) : false;
|
|
83018
|
+
if (configurationMode === "setup") await setupDaemon(commandArgs);
|
|
83274
83019
|
if (configurationMode === "login") await loginWithDeviceCode(commandArgs);
|
|
83275
|
-
if (!isWindowsSetup) {
|
|
83276
|
-
installDaemonService(commandArgs);
|
|
83277
|
-
runMcpIntegrationCommand(["reconcile", ...commandArgs]);
|
|
83278
|
-
return;
|
|
83279
|
-
}
|
|
83280
|
-
const { runtimeId, localApiPort, localApiToken } = readConfig();
|
|
83281
|
-
if (!runtimeId || !localApiPort || !localApiToken) {
|
|
83282
|
-
throw new Error("Windows runtime setup completed without a local readiness endpoint");
|
|
83283
|
-
}
|
|
83284
|
-
if (runtimeIdentityChanged) {
|
|
83285
|
-
while (Date.now() < deadline) {
|
|
83286
|
-
const status = await probeLocalDaemon(localApiPort, localApiToken, 250);
|
|
83287
|
-
if (status?.runtimeId === runtimeId && status.connected) break;
|
|
83288
|
-
if (!status) {
|
|
83289
|
-
const service = inspectDaemonService({ args: commandArgs });
|
|
83290
|
-
if (service.state !== "running" && service.state !== "starting") break;
|
|
83291
|
-
}
|
|
83292
|
-
await (0, import_promises6.setTimeout)(100);
|
|
83293
|
-
}
|
|
83294
|
-
}
|
|
83295
83020
|
installDaemonService(commandArgs);
|
|
83296
|
-
|
|
83297
|
-
const status = await probeLocalDaemon(localApiPort, localApiToken, 250);
|
|
83298
|
-
if (status?.runtimeId === runtimeId && status.connected) {
|
|
83299
|
-
runMcpIntegrationCommand(["reconcile", ...commandArgs]);
|
|
83300
|
-
return;
|
|
83301
|
-
}
|
|
83302
|
-
await (0, import_promises6.setTimeout)(100);
|
|
83303
|
-
}
|
|
83304
|
-
throw new Error("Windows runtime did not become ready within 9 seconds");
|
|
83021
|
+
runMcpIntegrationCommand(["reconcile", ...commandArgs]);
|
|
83305
83022
|
}
|
|
83306
83023
|
|
|
83307
83024
|
// src/lifecycle-logger.ts
|
|
@@ -86678,7 +86395,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS2 = 250;
|
|
|
86678
86395
|
function inputRequiredRoundsExceededMessage2(method, maxRounds) {
|
|
86679
86396
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
86680
86397
|
}
|
|
86681
|
-
function
|
|
86398
|
+
function sleep3(ms, signal) {
|
|
86682
86399
|
return new Promise((resolve15, reject) => {
|
|
86683
86400
|
if (signal?.aborted) {
|
|
86684
86401
|
reject(signal.reason instanceof SdkError2 ? signal.reason : new SdkError2(SdkErrorCode2.RequestTimeout, String(signal.reason)));
|
|
@@ -94945,7 +94662,7 @@ var LegacyInputRequiredShim = class {
|
|
|
94945
94662
|
} finally {
|
|
94946
94663
|
roundAbort.dispose();
|
|
94947
94664
|
}
|
|
94948
|
-
} else await
|
|
94665
|
+
} else await sleep3(REQUEST_STATE_ONLY_LEG_PACING_MS2, outerSignal);
|
|
94949
94666
|
let ctxNext = {
|
|
94950
94667
|
...ctx,
|
|
94951
94668
|
mcpReq: {
|
|
@@ -95742,7 +95459,7 @@ async function runMcpStdioProxy(env = process.env, local = new StdioServerTransp
|
|
|
95742
95459
|
}
|
|
95743
95460
|
|
|
95744
95461
|
// src/native-hook-command.ts
|
|
95745
|
-
var
|
|
95462
|
+
var import_node_crypto21 = require("crypto");
|
|
95746
95463
|
var import_node_fs27 = require("fs");
|
|
95747
95464
|
var import_node_os17 = require("os");
|
|
95748
95465
|
var import_node_path29 = require("path");
|
|
@@ -96111,7 +95828,7 @@ function parseNativeHookEnvelope(input2) {
|
|
|
96111
95828
|
const projectPin = eventType === "session_start" && resolvedCwd ? readAlanProjectPin(resolvedCwd) : void 0;
|
|
96112
95829
|
return {
|
|
96113
95830
|
schemaVersion: 1,
|
|
96114
|
-
eventId: (0,
|
|
95831
|
+
eventId: (0, import_node_crypto21.randomUUID)(),
|
|
96115
95832
|
provider: capability.provider,
|
|
96116
95833
|
eventType,
|
|
96117
95834
|
observedAt: (input2.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -96278,7 +95995,7 @@ function decodeResumeFallbackContext(encoded) {
|
|
|
96278
95995
|
}
|
|
96279
95996
|
|
|
96280
95997
|
// src/sandbox.ts
|
|
96281
|
-
var
|
|
95998
|
+
var import_node_os19 = require("os");
|
|
96282
95999
|
|
|
96283
96000
|
// src/execution-activity-tracker.ts
|
|
96284
96001
|
var ExecutionActivityTracker = class {
|
|
@@ -96607,7 +96324,7 @@ function delay2(ms) {
|
|
|
96607
96324
|
}
|
|
96608
96325
|
|
|
96609
96326
|
// src/web-presenter.ts
|
|
96610
|
-
var
|
|
96327
|
+
var import_node_crypto22 = require("crypto");
|
|
96611
96328
|
var WebPresenter = class {
|
|
96612
96329
|
constructor(wsClient, conversationId, cwd) {
|
|
96613
96330
|
this.conversationId = conversationId;
|
|
@@ -96793,7 +96510,7 @@ var WebPresenter = class {
|
|
|
96793
96510
|
this.applicationProblem = createAlanProblem({
|
|
96794
96511
|
domain: "application",
|
|
96795
96512
|
code: input2.code,
|
|
96796
|
-
id: (0,
|
|
96513
|
+
id: (0, import_node_crypto22.randomUUID)(),
|
|
96797
96514
|
title: APPLICATION_PROBLEM_TITLES[input2.code],
|
|
96798
96515
|
detail: input2.message,
|
|
96799
96516
|
// The schema's slot for the technical cause, so the problem carries it
|
|
@@ -96863,7 +96580,7 @@ var WebPresenter = class {
|
|
|
96863
96580
|
};
|
|
96864
96581
|
|
|
96865
96582
|
// src/ws-client.ts
|
|
96866
|
-
var
|
|
96583
|
+
var import_node_crypto24 = require("crypto");
|
|
96867
96584
|
|
|
96868
96585
|
// ../shared/dist/agent-liveness.js
|
|
96869
96586
|
var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
|
|
@@ -96874,6 +96591,34 @@ var agentRunStateSchema = external_exports.object({
|
|
|
96874
96591
|
/** Run ids currently executing (cloud sandbox agent runs one at a time, but keep it a list). */
|
|
96875
96592
|
activeRunIds: external_exports.array(external_exports.string()).default([])
|
|
96876
96593
|
});
|
|
96594
|
+
var AGENT_RESOURCE_SAMPLE_INTERVAL_MS = 6e4;
|
|
96595
|
+
var resourceCounter = external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
96596
|
+
var resourceMeasurement = external_exports.number().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
96597
|
+
var pressurePercentage = external_exports.number().min(0).max(100);
|
|
96598
|
+
var agentResourceDiagnosticsSchema = external_exports.object({
|
|
96599
|
+
sampledAtMs: resourceCounter,
|
|
96600
|
+
memoryTotalBytes: resourceCounter,
|
|
96601
|
+
memoryFreeBytes: resourceCounter,
|
|
96602
|
+
/** Linux MemAvailable includes reclaimable memory; free memory alone does not. */
|
|
96603
|
+
memoryAvailableBytes: resourceCounter.optional(),
|
|
96604
|
+
swapTotalBytes: resourceCounter.optional(),
|
|
96605
|
+
swapFreeBytes: resourceCounter.optional(),
|
|
96606
|
+
processRssBytes: resourceCounter,
|
|
96607
|
+
cpuCount: external_exports.number().int().positive().max(65536),
|
|
96608
|
+
/** Omitted on Windows, where os.loadavg() always returns zero. */
|
|
96609
|
+
loadAverage1m: resourceMeasurement.optional(),
|
|
96610
|
+
eventLoopDelayMaxMs: resourceMeasurement.optional(),
|
|
96611
|
+
eventLoopDelayP99Ms: resourceMeasurement.optional(),
|
|
96612
|
+
memoryPressureSomeAvg10: pressurePercentage.optional(),
|
|
96613
|
+
memoryPressureFullAvg10: pressurePercentage.optional(),
|
|
96614
|
+
cpuPressureSomeAvg10: pressurePercentage.optional(),
|
|
96615
|
+
ioPressureSomeAvg10: pressurePercentage.optional(),
|
|
96616
|
+
/** Counters for the agent's current cgroup v2, including its child workload. */
|
|
96617
|
+
cgroupMemoryCurrentBytes: resourceCounter.optional(),
|
|
96618
|
+
cgroupMemoryLimitBytes: resourceCounter.optional(),
|
|
96619
|
+
cgroupMemoryOomEvents: resourceCounter.optional(),
|
|
96620
|
+
cgroupMemoryOomKillEvents: resourceCounter.optional()
|
|
96621
|
+
});
|
|
96877
96622
|
var agentHeartbeatSchema = external_exports.object({
|
|
96878
96623
|
/** Stable id for this agent OS process; changes only on a fresh agent start. */
|
|
96879
96624
|
agentSessionId: external_exports.string().min(1),
|
|
@@ -96882,7 +96627,9 @@ var agentHeartbeatSchema = external_exports.object({
|
|
|
96882
96627
|
/** Capability marker — presence is what distinguishes a new agent from a legacy one. */
|
|
96883
96628
|
protocolVersion: external_exports.number().int().positive(),
|
|
96884
96629
|
runState: agentRunStateSchema,
|
|
96885
|
-
agentVersion: external_exports.string().optional()
|
|
96630
|
+
agentVersion: external_exports.string().optional(),
|
|
96631
|
+
// Bad optional diagnostics must never discard otherwise valid liveness evidence.
|
|
96632
|
+
resources: agentResourceDiagnosticsSchema.optional().catch(void 0)
|
|
96886
96633
|
});
|
|
96887
96634
|
var agentProbeAckSchema = external_exports.object({
|
|
96888
96635
|
agentSessionId: external_exports.string().min(1),
|
|
@@ -96890,14 +96637,152 @@ var agentProbeAckSchema = external_exports.object({
|
|
|
96890
96637
|
protocolVersion: external_exports.number().int().positive().optional()
|
|
96891
96638
|
});
|
|
96892
96639
|
|
|
96893
|
-
// src/
|
|
96894
|
-
var import_node_crypto24 = require("crypto");
|
|
96640
|
+
// src/resource-diagnostics.ts
|
|
96895
96641
|
var import_node_fs28 = require("fs");
|
|
96642
|
+
var import_node_os18 = require("os");
|
|
96643
|
+
var import_node_path30 = require("path");
|
|
96644
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
96645
|
+
var MAX_RESOURCE_FILE_BYTES = 16384;
|
|
96646
|
+
var RESOURCE_READ_TIMEOUT_MS = 500;
|
|
96647
|
+
async function readResourceFile(path2) {
|
|
96648
|
+
try {
|
|
96649
|
+
const stream = (0, import_node_fs28.createReadStream)(path2, {
|
|
96650
|
+
encoding: "utf8",
|
|
96651
|
+
start: 0,
|
|
96652
|
+
end: MAX_RESOURCE_FILE_BYTES,
|
|
96653
|
+
signal: AbortSignal.timeout(RESOURCE_READ_TIMEOUT_MS)
|
|
96654
|
+
});
|
|
96655
|
+
let text = "";
|
|
96656
|
+
for await (const chunk of stream) {
|
|
96657
|
+
text += chunk;
|
|
96658
|
+
if (text.length > MAX_RESOURCE_FILE_BYTES) return void 0;
|
|
96659
|
+
}
|
|
96660
|
+
return text;
|
|
96661
|
+
} catch {
|
|
96662
|
+
return void 0;
|
|
96663
|
+
}
|
|
96664
|
+
}
|
|
96665
|
+
function counter(value2) {
|
|
96666
|
+
if (!value2 || !/^\d+$/.test(value2)) return void 0;
|
|
96667
|
+
const parsed = Number(value2);
|
|
96668
|
+
return Number.isSafeInteger(parsed) ? parsed : void 0;
|
|
96669
|
+
}
|
|
96670
|
+
function pressure(text, kind) {
|
|
96671
|
+
const value2 = text?.match(new RegExp(`^${kind} avg10=(\\d+(?:\\.\\d+)?) `, "m"))?.[1];
|
|
96672
|
+
if (value2 === void 0) return void 0;
|
|
96673
|
+
const parsed = Number(value2);
|
|
96674
|
+
return Number.isFinite(parsed) && parsed <= 100 ? parsed : void 0;
|
|
96675
|
+
}
|
|
96676
|
+
function meminfoBytes(text, field) {
|
|
96677
|
+
const kb = counter(text?.match(new RegExp(`^${field}:\\s+(\\d+) kB$`, "m"))?.[1]);
|
|
96678
|
+
return kb !== void 0 && Number.isSafeInteger(kb * 1024) ? kb * 1024 : void 0;
|
|
96679
|
+
}
|
|
96680
|
+
function cgroupDirectory(text) {
|
|
96681
|
+
const path2 = text?.match(/^0::(\/[^\n]*)$/m)?.[1];
|
|
96682
|
+
if (!path2 || path2.split("/").includes("..")) return void 0;
|
|
96683
|
+
return import_node_path30.posix.join("/sys/fs/cgroup", path2);
|
|
96684
|
+
}
|
|
96685
|
+
async function collectAgentResourceDiagnostics(options = {}) {
|
|
96686
|
+
const platform6 = options.platform ?? process.platform;
|
|
96687
|
+
const readFile7 = async (path2) => {
|
|
96688
|
+
try {
|
|
96689
|
+
return await (options.readFile ?? readResourceFile)(path2);
|
|
96690
|
+
} catch {
|
|
96691
|
+
return void 0;
|
|
96692
|
+
}
|
|
96693
|
+
};
|
|
96694
|
+
const sample = {
|
|
96695
|
+
sampledAtMs: Date.now(),
|
|
96696
|
+
memoryTotalBytes: (0, import_node_os18.totalmem)(),
|
|
96697
|
+
memoryFreeBytes: (0, import_node_os18.freemem)(),
|
|
96698
|
+
processRssBytes: process.memoryUsage.rss(),
|
|
96699
|
+
cpuCount: (0, import_node_os18.availableParallelism)(),
|
|
96700
|
+
...platform6 === "win32" ? {} : { loadAverage1m: (0, import_node_os18.loadavg)()[0] }
|
|
96701
|
+
};
|
|
96702
|
+
if (platform6 !== "linux") return sample;
|
|
96703
|
+
const [meminfo, memoryPsi, cpuPsi, ioPsi, cgroup] = await Promise.all([
|
|
96704
|
+
readFile7("/proc/meminfo"),
|
|
96705
|
+
readFile7("/proc/pressure/memory"),
|
|
96706
|
+
readFile7("/proc/pressure/cpu"),
|
|
96707
|
+
readFile7("/proc/pressure/io"),
|
|
96708
|
+
readFile7("/proc/self/cgroup")
|
|
96709
|
+
]);
|
|
96710
|
+
sample.memoryAvailableBytes = meminfoBytes(meminfo, "MemAvailable");
|
|
96711
|
+
sample.swapTotalBytes = meminfoBytes(meminfo, "SwapTotal");
|
|
96712
|
+
sample.swapFreeBytes = meminfoBytes(meminfo, "SwapFree");
|
|
96713
|
+
sample.memoryPressureSomeAvg10 = pressure(memoryPsi, "some");
|
|
96714
|
+
sample.memoryPressureFullAvg10 = pressure(memoryPsi, "full");
|
|
96715
|
+
sample.cpuPressureSomeAvg10 = pressure(cpuPsi, "some");
|
|
96716
|
+
sample.ioPressureSomeAvg10 = pressure(ioPsi, "some");
|
|
96717
|
+
const directory = cgroupDirectory(cgroup);
|
|
96718
|
+
if (!directory) return sample;
|
|
96719
|
+
const [current, limit, events] = await Promise.all([
|
|
96720
|
+
readFile7(`${directory}/memory.current`),
|
|
96721
|
+
readFile7(`${directory}/memory.max`),
|
|
96722
|
+
readFile7(`${directory}/memory.events`)
|
|
96723
|
+
]);
|
|
96724
|
+
sample.cgroupMemoryCurrentBytes = counter(current?.trim());
|
|
96725
|
+
sample.cgroupMemoryLimitBytes = counter(limit?.trim());
|
|
96726
|
+
sample.cgroupMemoryOomEvents = counter(events?.match(/^oom (\d+)$/m)?.[1]);
|
|
96727
|
+
sample.cgroupMemoryOomKillEvents = counter(events?.match(/^oom_kill (\d+)$/m)?.[1]);
|
|
96728
|
+
return sample;
|
|
96729
|
+
}
|
|
96730
|
+
var AgentResourceSampler = class {
|
|
96731
|
+
constructor(collect = collectAgentResourceDiagnostics) {
|
|
96732
|
+
this.collect = collect;
|
|
96733
|
+
}
|
|
96734
|
+
delay = (0, import_node_perf_hooks.monitorEventLoopDelay)({ resolution: 20 });
|
|
96735
|
+
timer;
|
|
96736
|
+
inFlight = false;
|
|
96737
|
+
generation = 0;
|
|
96738
|
+
sample;
|
|
96739
|
+
start() {
|
|
96740
|
+
if (this.timer) return;
|
|
96741
|
+
this.generation += 1;
|
|
96742
|
+
this.delay.reset();
|
|
96743
|
+
this.delay.enable();
|
|
96744
|
+
this.timer = setInterval(() => void this.refresh(), AGENT_RESOURCE_SAMPLE_INTERVAL_MS);
|
|
96745
|
+
this.timer.unref();
|
|
96746
|
+
void this.refresh();
|
|
96747
|
+
}
|
|
96748
|
+
stop() {
|
|
96749
|
+
this.generation += 1;
|
|
96750
|
+
if (this.timer) clearInterval(this.timer);
|
|
96751
|
+
this.timer = void 0;
|
|
96752
|
+
this.delay.disable();
|
|
96753
|
+
this.sample = void 0;
|
|
96754
|
+
}
|
|
96755
|
+
latest() {
|
|
96756
|
+
return this.sample;
|
|
96757
|
+
}
|
|
96758
|
+
async refresh() {
|
|
96759
|
+
if (this.inFlight) return;
|
|
96760
|
+
this.inFlight = true;
|
|
96761
|
+
const generation = this.generation;
|
|
96762
|
+
const delay3 = this.delay.count > 0 ? {
|
|
96763
|
+
eventLoopDelayMaxMs: this.delay.max / 1e6,
|
|
96764
|
+
eventLoopDelayP99Ms: this.delay.percentile(99) / 1e6
|
|
96765
|
+
} : {};
|
|
96766
|
+
this.delay.reset();
|
|
96767
|
+
try {
|
|
96768
|
+
const sample = await this.collect();
|
|
96769
|
+
if (generation === this.generation) this.sample = { ...sample, ...delay3 };
|
|
96770
|
+
} catch {
|
|
96771
|
+
if (generation === this.generation) this.sample = void 0;
|
|
96772
|
+
} finally {
|
|
96773
|
+
this.inFlight = false;
|
|
96774
|
+
}
|
|
96775
|
+
}
|
|
96776
|
+
};
|
|
96777
|
+
|
|
96778
|
+
// src/sandbox-outbox.ts
|
|
96779
|
+
var import_node_crypto23 = require("crypto");
|
|
96780
|
+
var import_node_fs29 = require("fs");
|
|
96896
96781
|
function deriveSandboxOutboxKey(sessionToken) {
|
|
96897
96782
|
if (!sessionToken) {
|
|
96898
96783
|
throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
|
|
96899
96784
|
}
|
|
96900
|
-
return (0,
|
|
96785
|
+
return (0, import_node_crypto23.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
|
|
96901
96786
|
}
|
|
96902
96787
|
function sandboxOutboxSpoolPath(conversationId) {
|
|
96903
96788
|
return `/tmp/alan-agent-outbox-${conversationId}.enc`;
|
|
@@ -96913,24 +96798,24 @@ function assertValidOutboxKey(key, path2) {
|
|
|
96913
96798
|
}
|
|
96914
96799
|
function loadOrCreateSandboxOutboxKey(input2) {
|
|
96915
96800
|
const keyPath = sandboxOutboxKeyPath(input2.spoolPath);
|
|
96916
|
-
if ((0,
|
|
96917
|
-
(0,
|
|
96918
|
-
return assertValidOutboxKey((0,
|
|
96801
|
+
if ((0, import_node_fs29.existsSync)(keyPath)) {
|
|
96802
|
+
(0, import_node_fs29.chmodSync)(keyPath, 384);
|
|
96803
|
+
return assertValidOutboxKey((0, import_node_fs29.readFileSync)(keyPath, "utf8").trim(), keyPath);
|
|
96919
96804
|
}
|
|
96920
96805
|
const initialKey = deriveSandboxOutboxKey(input2.sessionToken);
|
|
96921
96806
|
try {
|
|
96922
|
-
(0,
|
|
96807
|
+
(0, import_node_fs29.writeFileSync)(keyPath, initialKey, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
96923
96808
|
return initialKey;
|
|
96924
96809
|
} catch (error61) {
|
|
96925
96810
|
if (error61.code !== "EEXIST") throw error61;
|
|
96926
|
-
(0,
|
|
96927
|
-
return assertValidOutboxKey((0,
|
|
96811
|
+
(0, import_node_fs29.chmodSync)(keyPath, 384);
|
|
96812
|
+
return assertValidOutboxKey((0, import_node_fs29.readFileSync)(keyPath, "utf8").trim(), keyPath);
|
|
96928
96813
|
}
|
|
96929
96814
|
}
|
|
96930
96815
|
function quarantineUnusableOutbox(path2) {
|
|
96931
96816
|
const suffix = `corrupt-${Date.now()}-${process.pid}`;
|
|
96932
96817
|
for (const candidate of [path2, eventJournalDir(path2)]) {
|
|
96933
|
-
if ((0,
|
|
96818
|
+
if ((0, import_node_fs29.existsSync)(candidate)) (0, import_node_fs29.renameSync)(candidate, `${candidate}.${suffix}`);
|
|
96934
96819
|
}
|
|
96935
96820
|
}
|
|
96936
96821
|
function createSandboxEventOutbox(input2) {
|
|
@@ -97074,10 +96959,11 @@ var WSClient = class {
|
|
|
97074
96959
|
}
|
|
97075
96960
|
socket;
|
|
97076
96961
|
/** Stable id for this agent process; fences stale heartbeats server-side. */
|
|
97077
|
-
agentSessionId = (0,
|
|
96962
|
+
agentSessionId = (0, import_node_crypto24.randomUUID)();
|
|
97078
96963
|
/** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
|
|
97079
96964
|
heartbeatSeq = 0;
|
|
97080
96965
|
heartbeatTimer = null;
|
|
96966
|
+
resourceSampler = new AgentResourceSampler();
|
|
97081
96967
|
supervisedReconnectTimer = null;
|
|
97082
96968
|
supervisedReconnectAttempt = 0;
|
|
97083
96969
|
connectionDirective = null;
|
|
@@ -97126,11 +97012,13 @@ var WSClient = class {
|
|
|
97126
97012
|
seq: this.heartbeatSeq,
|
|
97127
97013
|
protocolVersion: AGENT_LIVENESS_PROTOCOL_VERSION,
|
|
97128
97014
|
runState: this.currentRunState(),
|
|
97129
|
-
agentVersion: this.agentVersion || void 0
|
|
97015
|
+
agentVersion: this.agentVersion || void 0,
|
|
97016
|
+
resources: this.resourceSampler.latest()
|
|
97130
97017
|
};
|
|
97131
97018
|
}
|
|
97132
97019
|
/** Emit `agent.hello` on (re)connect and (re)start the heartbeat interval. */
|
|
97133
97020
|
startHeartbeat() {
|
|
97021
|
+
this.resourceSampler.start();
|
|
97134
97022
|
if (this.socket.connected) {
|
|
97135
97023
|
this.socket.emit("agent.hello", this.buildHeartbeat());
|
|
97136
97024
|
}
|
|
@@ -97142,6 +97030,7 @@ var WSClient = class {
|
|
|
97142
97030
|
this.heartbeatTimer.unref?.();
|
|
97143
97031
|
}
|
|
97144
97032
|
stopHeartbeat() {
|
|
97033
|
+
this.resourceSampler.stop();
|
|
97145
97034
|
if (this.heartbeatTimer) {
|
|
97146
97035
|
clearInterval(this.heartbeatTimer);
|
|
97147
97036
|
this.heartbeatTimer = null;
|
|
@@ -97606,7 +97495,7 @@ async function runSandbox(config2) {
|
|
|
97606
97495
|
}
|
|
97607
97496
|
const presenter = new WebPresenter(wsClient, config2.sessionId, config2.projectPath);
|
|
97608
97497
|
presenter.setSuppressSessionLifecycle(true);
|
|
97609
|
-
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0,
|
|
97498
|
+
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os19.homedir)() });
|
|
97610
97499
|
let lastProviderSessionId = config2.providerSessionId;
|
|
97611
97500
|
let resumeFallbackContext = config2.resumeFallbackContext;
|
|
97612
97501
|
let activeBackendKind = config2.backendKind || "claude_cli";
|
|
@@ -97906,12 +97795,12 @@ async function runSandbox(config2) {
|
|
|
97906
97795
|
}
|
|
97907
97796
|
|
|
97908
97797
|
// src/skills/skill-cli.ts
|
|
97909
|
-
var
|
|
97910
|
-
var
|
|
97911
|
-
var
|
|
97798
|
+
var import_promises6 = require("fs/promises");
|
|
97799
|
+
var import_node_os20 = require("os");
|
|
97800
|
+
var import_node_path31 = require("path");
|
|
97912
97801
|
async function runSkillsCommand(args) {
|
|
97913
97802
|
const [subcommand, ...options] = args;
|
|
97914
|
-
const homeDirectory2 = option(options, "--home") ?? (0,
|
|
97803
|
+
const homeDirectory2 = option(options, "--home") ?? (0, import_node_os20.homedir)();
|
|
97915
97804
|
const manager = await RuntimeSkillManager.open({ homeDirectory: homeDirectory2 });
|
|
97916
97805
|
if (subcommand === "scan") {
|
|
97917
97806
|
const inputPath = option(options, "--input");
|
|
@@ -97952,8 +97841,8 @@ async function defaultScanInput(homeDirectory2) {
|
|
|
97952
97841
|
});
|
|
97953
97842
|
}
|
|
97954
97843
|
async function readProtectedJson(path2) {
|
|
97955
|
-
const absolutePath = (0,
|
|
97956
|
-
const stat2 = await (0,
|
|
97844
|
+
const absolutePath = (0, import_node_path31.resolve)(path2);
|
|
97845
|
+
const stat2 = await (0, import_promises6.lstat)(absolutePath);
|
|
97957
97846
|
if (!stat2.isFile()) throw new Error(`Skill input is not a regular file: ${absolutePath}`);
|
|
97958
97847
|
if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
|
|
97959
97848
|
throw new Error(`Skill input must not be accessible by group or other users: ${absolutePath}`);
|
|
@@ -97962,7 +97851,7 @@ async function readProtectedJson(path2) {
|
|
|
97962
97851
|
throw new Error(`Skill input must be owned by the current user: ${absolutePath}`);
|
|
97963
97852
|
}
|
|
97964
97853
|
if (stat2.size > 2 * 1024 * 1024) throw new Error("Skill input exceeds 2 MiB");
|
|
97965
|
-
return JSON.parse(await (0,
|
|
97854
|
+
return JSON.parse(await (0, import_promises6.readFile)(absolutePath, "utf8"));
|
|
97966
97855
|
}
|
|
97967
97856
|
function option(args, name) {
|
|
97968
97857
|
const index = args.indexOf(name);
|
|
@@ -98120,7 +98009,7 @@ async function main() {
|
|
|
98120
98009
|
return;
|
|
98121
98010
|
}
|
|
98122
98011
|
if (command === "service" && commandArgs[0] === "uninstall") {
|
|
98123
|
-
|
|
98012
|
+
uninstallDaemonService(commandArgs.slice(1));
|
|
98124
98013
|
return;
|
|
98125
98014
|
}
|
|
98126
98015
|
if (command === "service" && commandArgs[0] === "status") {
|