@alan-ai-hq/agent-manager 0.1.94 → 0.1.95
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 +521 -219
- 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: createHash19 } = 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 = createHash19("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: createHash19 } = 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 = createHash19("sha1").update(key + GUID).digest("base64");
|
|
4729
4729
|
const headers = [
|
|
4730
4730
|
"HTTP/1.1 101 Switching Protocols",
|
|
4731
4731
|
"Upgrade: websocket",
|
|
@@ -15451,7 +15451,7 @@ function describeError(error61) {
|
|
|
15451
15451
|
}
|
|
15452
15452
|
|
|
15453
15453
|
// src/version.ts
|
|
15454
|
-
var AGENT_VERSION = "0.1.
|
|
15454
|
+
var AGENT_VERSION = "0.1.95";
|
|
15455
15455
|
|
|
15456
15456
|
// src/daemon-worktree.ts
|
|
15457
15457
|
var import_node_child_process3 = require("child_process");
|
|
@@ -49610,6 +49610,16 @@ async function probeLocalDaemon(port, token, timeoutMs = 750) {
|
|
|
49610
49610
|
const probe = await probeLocalDaemonEndpoint(port, token, timeoutMs);
|
|
49611
49611
|
return probe.state === "running" ? probe.status : null;
|
|
49612
49612
|
}
|
|
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
|
+
}
|
|
49613
49623
|
async function adoptLocalDaemonControl(input2) {
|
|
49614
49624
|
const timeoutMs = input2.timeoutMs ?? 750;
|
|
49615
49625
|
try {
|
|
@@ -76687,13 +76697,13 @@ var RuntimeRunJournal = class {
|
|
|
76687
76697
|
* Keep the exact interruption outcome until a durable authenticated control
|
|
76688
76698
|
* channel can prove both identity and ownership.
|
|
76689
76699
|
*/
|
|
76690
|
-
assessRecovery(runId,
|
|
76700
|
+
assessRecovery(runId, isProcessAlive2) {
|
|
76691
76701
|
const entry = this.entries.get(runId);
|
|
76692
76702
|
if (!entry) return void 0;
|
|
76693
76703
|
if (entry.providerPid === void 0) {
|
|
76694
76704
|
return { reattachable: false, reason: "provider_not_recorded" };
|
|
76695
76705
|
}
|
|
76696
|
-
if (!
|
|
76706
|
+
if (!isProcessAlive2(entry.providerPid)) {
|
|
76697
76707
|
return {
|
|
76698
76708
|
reattachable: false,
|
|
76699
76709
|
reason: "provider_not_running",
|
|
@@ -80440,6 +80450,17 @@ async function runLocalDaemonRecoveryLadder(input2) {
|
|
|
80440
80450
|
error: "The local daemon exhausted its reconnect and safe-restart recovery budget."
|
|
80441
80451
|
};
|
|
80442
80452
|
}
|
|
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
|
+
}
|
|
80443
80464
|
async function setupDaemon(args) {
|
|
80444
80465
|
bindConfigPath(args);
|
|
80445
80466
|
const previousConfig = readConfigForRegistration();
|
|
@@ -80500,7 +80521,7 @@ async function setupDaemon(args) {
|
|
|
80500
80521
|
credentialGeneration: body2.credentialGeneration,
|
|
80501
80522
|
configPath: getConfigPath()
|
|
80502
80523
|
});
|
|
80503
|
-
return
|
|
80524
|
+
return body2.runtimeId !== liveConfig.runtimeId;
|
|
80504
80525
|
}
|
|
80505
80526
|
if (readLocalDaemonOwnerLease(getConfigPath())) {
|
|
80506
80527
|
throw new Error(
|
|
@@ -80514,23 +80535,17 @@ async function setupDaemon(args) {
|
|
|
80514
80535
|
if (setupToken && error61 instanceof RuntimeSetupGrantExpiredError) {
|
|
80515
80536
|
console.info("[alan-agent] Setup link expired; continuing with browser authorization");
|
|
80516
80537
|
await loginWithDeviceCode(args);
|
|
80517
|
-
return
|
|
80538
|
+
return false;
|
|
80518
80539
|
}
|
|
80519
80540
|
throw error61;
|
|
80520
80541
|
}
|
|
80521
|
-
|
|
80542
|
+
writeRegisteredRuntimeConfig(args, previousConfig, {
|
|
80522
80543
|
apiUrl,
|
|
80523
80544
|
wsUrl,
|
|
80524
80545
|
endpointProfile,
|
|
80525
80546
|
runtimeId: body.runtimeId,
|
|
80526
80547
|
runtimeToken: body.runtimeToken,
|
|
80527
80548
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
80528
|
-
localApiPort: getLocalApiPort(args, {}),
|
|
80529
|
-
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto19.randomBytes)(24).toString("hex"),
|
|
80530
|
-
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
80531
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80532
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80533
|
-
daemonEpoch: previousConfig.daemonEpoch,
|
|
80534
80549
|
displayName: body.displayName,
|
|
80535
80550
|
installationId
|
|
80536
80551
|
});
|
|
@@ -80539,7 +80554,7 @@ async function setupDaemon(args) {
|
|
|
80539
80554
|
runtimeId: body.runtimeId,
|
|
80540
80555
|
configPath: getConfigPath()
|
|
80541
80556
|
});
|
|
80542
|
-
return
|
|
80557
|
+
return false;
|
|
80543
80558
|
}
|
|
80544
80559
|
async function loginWithDeviceCode(args) {
|
|
80545
80560
|
bindConfigPath(args);
|
|
@@ -80628,19 +80643,13 @@ async function loginWithDeviceCode(args) {
|
|
|
80628
80643
|
if (ownedPending?.operationId !== operation.id) {
|
|
80629
80644
|
throw new Error("browser authorization sidecar ownership was lost before finalization");
|
|
80630
80645
|
}
|
|
80631
|
-
|
|
80646
|
+
writeRegisteredRuntimeConfig(args, previousConfig, {
|
|
80632
80647
|
apiUrl,
|
|
80633
80648
|
wsUrl,
|
|
80634
80649
|
endpointProfile,
|
|
80635
80650
|
runtimeId: body.runtime.id,
|
|
80636
80651
|
runtimeToken: body.runtimeToken,
|
|
80637
80652
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
80638
|
-
localApiPort: getLocalApiPort(args, {}),
|
|
80639
|
-
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto19.randomBytes)(24).toString("hex"),
|
|
80640
|
-
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto19.randomUUID)(),
|
|
80641
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80642
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto19.randomBytes)(32).toString("base64url"),
|
|
80643
|
-
daemonEpoch: previousConfig.daemonEpoch,
|
|
80644
80653
|
displayName: body.runtime.displayName ?? displayName,
|
|
80645
80654
|
installationId
|
|
80646
80655
|
});
|
|
@@ -80679,14 +80688,7 @@ async function stopDaemon(args) {
|
|
|
80679
80688
|
console.info("[alan-agent] No daemon listening", { port: localApiPort });
|
|
80680
80689
|
return;
|
|
80681
80690
|
}
|
|
80682
|
-
const response = await
|
|
80683
|
-
method: "POST",
|
|
80684
|
-
headers: {
|
|
80685
|
-
authorization: `Bearer ${localApiToken}`,
|
|
80686
|
-
"x-alan-shutdown-reason": "runtime_shutdown"
|
|
80687
|
-
},
|
|
80688
|
-
signal: AbortSignal.timeout(5e3)
|
|
80689
|
-
});
|
|
80691
|
+
const response = await requestLocalDaemonShutdown(localApiPort, localApiToken);
|
|
80690
80692
|
if (!response.ok) {
|
|
80691
80693
|
throw new Error(`Failed to stop daemon: HTTP ${response.status}`);
|
|
80692
80694
|
}
|
|
@@ -80831,14 +80833,7 @@ async function drainAndStopLocalDaemonBeforeCredentialCleanup(args, stored) {
|
|
|
80831
80833
|
`Logout could not safely drain the local daemon (HTTP ${prepare.status}). Stop or update the local service before removing credentials.`
|
|
80832
80834
|
);
|
|
80833
80835
|
}
|
|
80834
|
-
const shutdown = await
|
|
80835
|
-
method: "POST",
|
|
80836
|
-
headers: {
|
|
80837
|
-
authorization: `Bearer ${localApiToken}`,
|
|
80838
|
-
"x-alan-shutdown-reason": "runtime_shutdown"
|
|
80839
|
-
},
|
|
80840
|
-
signal: AbortSignal.timeout(5e3)
|
|
80841
|
-
});
|
|
80836
|
+
const shutdown = await requestLocalDaemonShutdown(localApiPort, localApiToken);
|
|
80842
80837
|
if (!shutdown.ok) {
|
|
80843
80838
|
throw new Error(`Logout could not stop the local daemon (HTTP ${shutdown.status}).`);
|
|
80844
80839
|
}
|
|
@@ -80999,6 +80994,9 @@ async function printDoctor(args = []) {
|
|
|
80999
80994
|
);
|
|
81000
80995
|
}
|
|
81001
80996
|
|
|
80997
|
+
// src/install-command.ts
|
|
80998
|
+
var import_promises6 = require("timers/promises");
|
|
80999
|
+
|
|
81002
81000
|
// src/mcp-config-adapters.ts
|
|
81003
81001
|
var import_node_crypto20 = require("crypto");
|
|
81004
81002
|
var import_node_fs23 = require("fs");
|
|
@@ -82383,12 +82381,14 @@ function runMcpIntegrationCommand(args, options = {}) {
|
|
|
82383
82381
|
|
|
82384
82382
|
// src/service-manager.ts
|
|
82385
82383
|
var import_node_child_process9 = require("child_process");
|
|
82384
|
+
var import_node_crypto21 = require("crypto");
|
|
82386
82385
|
var import_node_fs25 = require("fs");
|
|
82387
82386
|
var import_node_os16 = require("os");
|
|
82388
82387
|
var import_node_path27 = require("path");
|
|
82389
82388
|
var SERVICE_LABEL = "ai.tryalan.agent";
|
|
82390
82389
|
var SYSTEMD_UNIT = "alan-agent.service";
|
|
82391
82390
|
var WINDOWS_TASK = "Alan Agent";
|
|
82391
|
+
var STOP_POLL_INTERVAL_MS = 200;
|
|
82392
82392
|
function xml(value2) {
|
|
82393
82393
|
return value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
82394
82394
|
}
|
|
@@ -82398,6 +82398,18 @@ function systemdArg(value2) {
|
|
|
82398
82398
|
function powershellLiteral(value2) {
|
|
82399
82399
|
return `'${value2.replaceAll("'", "''")}'`;
|
|
82400
82400
|
}
|
|
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
|
+
}
|
|
82401
82413
|
function serviceNames(profile) {
|
|
82402
82414
|
if (profile === "production") {
|
|
82403
82415
|
return { launchd: SERVICE_LABEL, systemd: SYSTEMD_UNIT, windows: WINDOWS_TASK };
|
|
@@ -82419,7 +82431,7 @@ function buildDaemonServicePlan(input2) {
|
|
|
82419
82431
|
const domain2 = `gui/${userId}`;
|
|
82420
82432
|
const serviceTarget = `${domain2}/${names.launchd}`;
|
|
82421
82433
|
const manifestPath = (0, import_node_path27.join)(input2.homeDir, "Library", "LaunchAgents", `${names.launchd}.plist`);
|
|
82422
|
-
const
|
|
82434
|
+
const logDir2 = (0, import_node_path27.join)(resolveAgentConfigDirectory(input2.profile, input2.homeDir), "logs");
|
|
82423
82435
|
const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
|
|
82424
82436
|
const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
|
|
82425
82437
|
<string>${xml(value2)}</string>`).join("\n");
|
|
@@ -82448,9 +82460,9 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82448
82460
|
<key>ThrottleInterval</key>
|
|
82449
82461
|
<integer>5</integer>
|
|
82450
82462
|
<key>StandardOutPath</key>
|
|
82451
|
-
<string>${xml((0, import_node_path27.join)(
|
|
82463
|
+
<string>${xml((0, import_node_path27.join)(logDir2, "daemon.log"))}</string>
|
|
82452
82464
|
<key>StandardErrorPath</key>
|
|
82453
|
-
<string>${xml((0, import_node_path27.join)(
|
|
82465
|
+
<string>${xml((0, import_node_path27.join)(logDir2, "daemon-error.log"))}</string>
|
|
82454
82466
|
</dict>
|
|
82455
82467
|
</plist>
|
|
82456
82468
|
`;
|
|
@@ -82458,7 +82470,7 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82458
82470
|
platform: "darwin",
|
|
82459
82471
|
manifestPath,
|
|
82460
82472
|
manifest,
|
|
82461
|
-
logDirectory:
|
|
82473
|
+
logDirectory: logDir2,
|
|
82462
82474
|
loadDefinitionCommands: [
|
|
82463
82475
|
{
|
|
82464
82476
|
command: "launchctl",
|
|
@@ -82499,23 +82511,6 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82499
82511
|
tolerateFailure: true
|
|
82500
82512
|
}
|
|
82501
82513
|
],
|
|
82502
|
-
replaceCommands: [
|
|
82503
|
-
{
|
|
82504
|
-
command: "launchctl",
|
|
82505
|
-
args: ["bootout", domain2, manifestPath],
|
|
82506
|
-
tolerateFailure: true
|
|
82507
|
-
},
|
|
82508
|
-
{
|
|
82509
|
-
command: "launchctl",
|
|
82510
|
-
args: ["bootstrap", domain2, manifestPath],
|
|
82511
|
-
tolerateFailure: false
|
|
82512
|
-
},
|
|
82513
|
-
{
|
|
82514
|
-
command: "launchctl",
|
|
82515
|
-
args: ["kickstart", "-k", serviceTarget],
|
|
82516
|
-
tolerateFailure: false
|
|
82517
|
-
}
|
|
82518
|
-
],
|
|
82519
82514
|
uninstallCommands: [
|
|
82520
82515
|
{
|
|
82521
82516
|
command: "launchctl",
|
|
@@ -82532,8 +82527,7 @@ ${environmentBlock} <key>RunAtLoad</key>
|
|
|
82532
82527
|
command: "launchctl",
|
|
82533
82528
|
args: ["print", serviceTarget],
|
|
82534
82529
|
tolerateFailure: true
|
|
82535
|
-
}
|
|
82536
|
-
lingerStatusCommand: null
|
|
82530
|
+
}
|
|
82537
82531
|
};
|
|
82538
82532
|
}
|
|
82539
82533
|
if (input2.platform === "linux") {
|
|
@@ -82596,18 +82590,6 @@ WantedBy=default.target
|
|
|
82596
82590
|
tolerateFailure: true
|
|
82597
82591
|
}
|
|
82598
82592
|
],
|
|
82599
|
-
replaceCommands: [
|
|
82600
|
-
{
|
|
82601
|
-
command: "systemctl",
|
|
82602
|
-
args: ["--user", "daemon-reload"],
|
|
82603
|
-
tolerateFailure: false
|
|
82604
|
-
},
|
|
82605
|
-
{
|
|
82606
|
-
command: "systemctl",
|
|
82607
|
-
args: ["--user", "restart", names.systemd],
|
|
82608
|
-
tolerateFailure: false
|
|
82609
|
-
}
|
|
82610
|
-
],
|
|
82611
82593
|
uninstallCommands: [
|
|
82612
82594
|
{
|
|
82613
82595
|
command: "systemctl",
|
|
@@ -82629,117 +82611,179 @@ WantedBy=default.target
|
|
|
82629
82611
|
command: "systemctl",
|
|
82630
82612
|
args: ["--user", "is-active", names.systemd],
|
|
82631
82613
|
tolerateFailure: true
|
|
82632
|
-
},
|
|
82633
|
-
lingerStatusCommand: {
|
|
82634
|
-
command: "loginctl",
|
|
82635
|
-
args: ["show-user", "$(id -un)", "-p", "Linger"],
|
|
82636
|
-
tolerateFailure: true
|
|
82637
82614
|
}
|
|
82638
82615
|
};
|
|
82639
82616
|
}
|
|
82640
|
-
const
|
|
82641
|
-
const
|
|
82642
|
-
const
|
|
82643
|
-
const
|
|
82644
|
-
|
|
82617
|
+
const logDir = (0, import_node_path27.join)(resolveAgentConfigDirectory(input2.profile, input2.homeDir), "logs");
|
|
82618
|
+
const legacyEnvironmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
|
|
82619
|
+
const legacyDirectArguments = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
|
|
82620
|
+
const legacyExecutable = legacyEnvironmentPrefix ? "powershell.exe" : input2.nodeExecutable;
|
|
82621
|
+
const legacyArguments = legacyEnvironmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${legacyEnvironmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : legacyDirectArguments;
|
|
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)}`
|
|
82645
82625
|
);
|
|
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("; ");
|
|
82646
82718
|
const registerScript = [
|
|
82647
|
-
|
|
82648
|
-
|
|
82719
|
+
taskContext,
|
|
82720
|
+
resolveTaskSid,
|
|
82721
|
+
windowsDefinitionContext,
|
|
82722
|
+
`$action = New-ScheduledTaskAction -Execute ${executable} -Argument $expectedLauncherArguments -WorkingDirectory $expectedWorkingDirectory`,
|
|
82649
82723
|
"$trigger = New-ScheduledTaskTrigger -AtLogOn -User $currentUser",
|
|
82650
82724
|
"$principal = New-ScheduledTaskPrincipal -UserId $currentUser -LogonType Interactive -RunLevel Limited",
|
|
82651
|
-
|
|
82652
|
-
|
|
82725
|
+
`$settings = ${settingsScript}`,
|
|
82726
|
+
assertOwnedTasks,
|
|
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' }"
|
|
82653
82742
|
].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);
|
|
82654
82768
|
return {
|
|
82655
82769
|
platform: "win32",
|
|
82656
82770
|
manifestPath: null,
|
|
82657
82771
|
manifest: null,
|
|
82658
|
-
logDirectory:
|
|
82659
|
-
loadDefinitionCommands: [
|
|
82660
|
-
|
|
82661
|
-
command: "powershell.exe",
|
|
82662
|
-
args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
|
|
82663
|
-
tolerateFailure: false
|
|
82664
|
-
}
|
|
82665
|
-
],
|
|
82666
|
-
reloadDefinitionCommands: [
|
|
82667
|
-
{
|
|
82668
|
-
command: "powershell.exe",
|
|
82669
|
-
args: ["-NoProfile", "-NonInteractive", "-Command", registerScript],
|
|
82670
|
-
tolerateFailure: false
|
|
82671
|
-
}
|
|
82672
|
-
],
|
|
82772
|
+
logDirectory: logDir,
|
|
82773
|
+
loadDefinitionCommands: [registerCommand],
|
|
82774
|
+
reloadDefinitionCommands: [registerCommand],
|
|
82673
82775
|
enableCommands: [],
|
|
82674
|
-
startCommands: [
|
|
82675
|
-
{
|
|
82676
|
-
command: "powershell.exe",
|
|
82677
|
-
args: [
|
|
82678
|
-
"-NoProfile",
|
|
82679
|
-
"-NonInteractive",
|
|
82680
|
-
"-Command",
|
|
82681
|
-
`Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
|
|
82682
|
-
],
|
|
82683
|
-
tolerateFailure: false
|
|
82684
|
-
}
|
|
82685
|
-
],
|
|
82776
|
+
startCommands: [powershellCommand(`${taskContext}; Start-ScheduledTask -TaskName $taskName`)],
|
|
82686
82777
|
stopCommands: [
|
|
82687
|
-
|
|
82688
|
-
|
|
82689
|
-
|
|
82690
|
-
|
|
82691
|
-
"-NonInteractive",
|
|
82692
|
-
"-Command",
|
|
82693
|
-
`Stop-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue`
|
|
82694
|
-
],
|
|
82695
|
-
tolerateFailure: true
|
|
82696
|
-
}
|
|
82697
|
-
],
|
|
82698
|
-
replaceCommands: [
|
|
82699
|
-
{
|
|
82700
|
-
command: "powershell.exe",
|
|
82701
|
-
args: [
|
|
82702
|
-
"-NoProfile",
|
|
82703
|
-
"-NonInteractive",
|
|
82704
|
-
"-Command",
|
|
82705
|
-
`${registerScript}; Start-ScheduledTask -TaskName ${powershellLiteral(names.windows)}`
|
|
82706
|
-
],
|
|
82707
|
-
tolerateFailure: false
|
|
82708
|
-
}
|
|
82709
|
-
],
|
|
82710
|
-
uninstallCommands: [
|
|
82711
|
-
{
|
|
82712
|
-
command: "powershell.exe",
|
|
82713
|
-
args: [
|
|
82714
|
-
"-NoProfile",
|
|
82715
|
-
"-NonInteractive",
|
|
82716
|
-
"-Command",
|
|
82717
|
-
`Unregister-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -Confirm:$false -ErrorAction SilentlyContinue`
|
|
82718
|
-
],
|
|
82719
|
-
tolerateFailure: true
|
|
82720
|
-
}
|
|
82778
|
+
powershellCommand(
|
|
82779
|
+
`${taskContext}; Stop-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue`,
|
|
82780
|
+
true
|
|
82781
|
+
)
|
|
82721
82782
|
],
|
|
82722
|
-
|
|
82723
|
-
|
|
82724
|
-
|
|
82725
|
-
|
|
82726
|
-
"-NonInteractive",
|
|
82727
|
-
"-Command",
|
|
82728
|
-
`Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} | Format-List TaskName,State`
|
|
82729
|
-
],
|
|
82730
|
-
tolerateFailure: true
|
|
82731
|
-
},
|
|
82732
|
-
isActiveCommand: {
|
|
82733
|
-
command: "powershell.exe",
|
|
82734
|
-
args: [
|
|
82735
|
-
"-NoProfile",
|
|
82736
|
-
"-NonInteractive",
|
|
82737
|
-
"-Command",
|
|
82738
|
-
`(Get-ScheduledTask -TaskName ${powershellLiteral(names.windows)} -ErrorAction SilentlyContinue).State`
|
|
82739
|
-
],
|
|
82740
|
-
tolerateFailure: true
|
|
82741
|
-
},
|
|
82742
|
-
lingerStatusCommand: null
|
|
82783
|
+
uninstallCommands: [powershellCommand(uninstallScript)],
|
|
82784
|
+
statusCommand: powershellCommand(statusScript, true),
|
|
82785
|
+
isActiveCommand: powershellCommand(activeScript, true),
|
|
82786
|
+
definitionMarker
|
|
82743
82787
|
};
|
|
82744
82788
|
}
|
|
82745
82789
|
function resolveCurrentPlatform() {
|
|
@@ -82749,14 +82793,14 @@ function resolveCurrentPlatform() {
|
|
|
82749
82793
|
}
|
|
82750
82794
|
return currentPlatform;
|
|
82751
82795
|
}
|
|
82752
|
-
function currentServicePlan(args, runtime) {
|
|
82753
|
-
const currentPlatform = resolveCurrentPlatform();
|
|
82796
|
+
function currentServicePlan(args, runtime, options = {}) {
|
|
82797
|
+
const currentPlatform = options.platform ?? resolveCurrentPlatform();
|
|
82754
82798
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
82755
82799
|
const cliEntry = runtime?.cliEntry ?? process.argv[1];
|
|
82756
82800
|
if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
|
|
82757
82801
|
return buildDaemonServicePlan({
|
|
82758
82802
|
platform: currentPlatform,
|
|
82759
|
-
homeDir: (0, import_node_os16.homedir)(),
|
|
82803
|
+
homeDir: options.homeDir ?? (0, import_node_os16.homedir)(),
|
|
82760
82804
|
nodeExecutable: runtime?.executable ?? process.execPath,
|
|
82761
82805
|
cliEntry: (0, import_node_path27.resolve)(cliEntry),
|
|
82762
82806
|
profile,
|
|
@@ -82779,9 +82823,13 @@ function defaultReadManifest(path2) {
|
|
|
82779
82823
|
return (0, import_node_fs25.readFileSync)(path2, "utf8");
|
|
82780
82824
|
}
|
|
82781
82825
|
function runServiceCommand(command) {
|
|
82782
|
-
const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, {
|
|
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
|
+
});
|
|
82783
82831
|
const status = result.status ?? 1;
|
|
82784
|
-
const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
82832
|
+
const output2 = `${result.stdout ?? ""}${result.stderr ?? ""}${result.error?.message ?? ""}`.trim();
|
|
82785
82833
|
if (status !== 0 && !command.tolerateFailure) {
|
|
82786
82834
|
throw new Error(
|
|
82787
82835
|
`Service command failed (${command.command} ${command.args.join(" ")}): ${output2 || `exit ${status}`}`
|
|
@@ -82794,6 +82842,23 @@ function defaultSleep(ms) {
|
|
|
82794
82842
|
setTimeout(resolveSleep, ms);
|
|
82795
82843
|
});
|
|
82796
82844
|
}
|
|
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
|
+
}
|
|
82797
82862
|
function parseServiceHostState(servicePlatform, result) {
|
|
82798
82863
|
const output2 = result.output;
|
|
82799
82864
|
if (servicePlatform === "linux") {
|
|
@@ -82819,9 +82884,62 @@ function parseServiceHostState(servicePlatform, result) {
|
|
|
82819
82884
|
const stateMatch = output2.match(/\b(?:State|state)\s*[:=]\s*(\w+)/);
|
|
82820
82885
|
const state = (stateMatch?.[1] ?? output2.trim()).toLowerCase();
|
|
82821
82886
|
if (state === "running") return "running";
|
|
82822
|
-
if (state === "
|
|
82887
|
+
if (state === "queued") return "starting";
|
|
82888
|
+
if (state === "ready" || state === "disabled") return "stopped";
|
|
82823
82889
|
return "unknown";
|
|
82824
82890
|
}
|
|
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
|
+
}
|
|
82825
82943
|
function resolveEnsureDefinitionOutcome(input2) {
|
|
82826
82944
|
if (!input2.definitionChanged) {
|
|
82827
82945
|
return { changed: false, updatePending: false };
|
|
@@ -82831,9 +82949,6 @@ function resolveEnsureDefinitionOutcome(input2) {
|
|
|
82831
82949
|
}
|
|
82832
82950
|
return { changed: true, updatePending: false };
|
|
82833
82951
|
}
|
|
82834
|
-
function shouldStartOnInstall(hostState) {
|
|
82835
|
-
return hostState === "stopped";
|
|
82836
|
-
}
|
|
82837
82952
|
function resolveUsername(options) {
|
|
82838
82953
|
if (options?.username) return options.username;
|
|
82839
82954
|
try {
|
|
@@ -82842,17 +82957,14 @@ function resolveUsername(options) {
|
|
|
82842
82957
|
return process.env.USER || process.env.USERNAME || "unknown";
|
|
82843
82958
|
}
|
|
82844
82959
|
}
|
|
82845
|
-
function buildLingerStatusCommand(username) {
|
|
82846
|
-
return {
|
|
82847
|
-
command: "loginctl",
|
|
82848
|
-
args: ["show-user", username, "-p", "Linger"],
|
|
82849
|
-
tolerateFailure: true
|
|
82850
|
-
};
|
|
82851
|
-
}
|
|
82852
82960
|
function assertSystemdUserLingerEnabled(options = {}) {
|
|
82853
82961
|
const username = resolveUsername(options);
|
|
82854
82962
|
const run = options.runCommand ?? runServiceCommand;
|
|
82855
|
-
const result = run(
|
|
82963
|
+
const result = run({
|
|
82964
|
+
command: "loginctl",
|
|
82965
|
+
args: ["show-user", username, "-p", "Linger"],
|
|
82966
|
+
tolerateFailure: true
|
|
82967
|
+
});
|
|
82856
82968
|
if (result.status !== 0) {
|
|
82857
82969
|
throw new Error(
|
|
82858
82970
|
`Cannot verify systemd user linger for ${username} (loginctl failed). For durable headless VM boot run: sudo loginctl enable-linger ${username}`
|
|
@@ -82866,9 +82978,9 @@ function assertSystemdUserLingerEnabled(options = {}) {
|
|
|
82866
82978
|
}
|
|
82867
82979
|
function createHostContext(options = {}) {
|
|
82868
82980
|
const args = options.args ?? [];
|
|
82869
|
-
const plan = currentServicePlan(args, options.runtime);
|
|
82981
|
+
const plan = currentServicePlan(args, options.runtime, options);
|
|
82870
82982
|
const run = options.runCommand ?? runServiceCommand;
|
|
82871
|
-
const
|
|
82983
|
+
const sleep5 = options.sleep ?? defaultSleep;
|
|
82872
82984
|
const nowMs = options.nowMs ?? Date.now;
|
|
82873
82985
|
const readManifest = options.readManifest ?? defaultReadManifest;
|
|
82874
82986
|
const writeManifest = options.writeManifest ?? writeManifestAtomic;
|
|
@@ -82880,13 +82992,20 @@ function createHostContext(options = {}) {
|
|
|
82880
82992
|
args,
|
|
82881
82993
|
plan,
|
|
82882
82994
|
run,
|
|
82883
|
-
sleep:
|
|
82995
|
+
sleep: sleep5,
|
|
82884
82996
|
nowMs,
|
|
82885
82997
|
readManifest,
|
|
82886
82998
|
writeManifest,
|
|
82887
82999
|
removeManifest,
|
|
82888
83000
|
ensureLogDir,
|
|
82889
|
-
profile: resolveEndpointProfileFromArgs(args)
|
|
83001
|
+
configPath: options.configPath ?? resolveAgentConfigPath({ profile: resolveEndpointProfileFromArgs(args) }),
|
|
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
|
|
82890
83009
|
};
|
|
82891
83010
|
}
|
|
82892
83011
|
function inspectDaemonService(options = {}) {
|
|
@@ -82902,8 +83021,9 @@ function inspectDaemonService(options = {}) {
|
|
|
82902
83021
|
}
|
|
82903
83022
|
function ensureDaemonServiceDefinition(options = {}) {
|
|
82904
83023
|
const ctx = createHostContext(options);
|
|
82905
|
-
const { plan, run, readManifest, writeManifest, ensureLogDir } = ctx;
|
|
83024
|
+
const { plan, run, nowMs, readManifest, writeManifest, ensureLogDir } = ctx;
|
|
82906
83025
|
let definitionChanged = false;
|
|
83026
|
+
let inspection;
|
|
82907
83027
|
if (plan.manifestPath && plan.manifest) {
|
|
82908
83028
|
ensureLogDir();
|
|
82909
83029
|
const existing = readManifest(plan.manifestPath);
|
|
@@ -82912,20 +83032,38 @@ function ensureDaemonServiceDefinition(options = {}) {
|
|
|
82912
83032
|
writeManifest(plan.manifestPath, plan.manifest);
|
|
82913
83033
|
}
|
|
82914
83034
|
} else if (plan.platform === "win32") {
|
|
82915
|
-
|
|
83035
|
+
ensureLogDir();
|
|
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
|
+
};
|
|
82916
83050
|
}
|
|
82917
|
-
|
|
83051
|
+
inspection ??= inspectDaemonService(options);
|
|
83052
|
+
const definitionHostState = plan.platform === "win32" && inspection.state === "starting" ? "stopped" : inspection.state;
|
|
82918
83053
|
const outcome = resolveEnsureDefinitionOutcome({
|
|
82919
83054
|
definitionChanged,
|
|
82920
|
-
hostState:
|
|
83055
|
+
hostState: definitionHostState
|
|
82921
83056
|
});
|
|
82922
83057
|
if (outcome.updatePending) {
|
|
82923
83058
|
return outcome;
|
|
82924
83059
|
}
|
|
82925
|
-
if (
|
|
83060
|
+
if (plan.platform === "win32" && !outcome.changed) {
|
|
82926
83061
|
return outcome;
|
|
82927
83062
|
}
|
|
82928
|
-
|
|
83063
|
+
if (definitionHostState === "running" || definitionHostState === "starting") {
|
|
83064
|
+
return outcome;
|
|
83065
|
+
}
|
|
83066
|
+
const definitionCommands = definitionHostState === "stopped" ? plan.reloadDefinitionCommands : plan.loadDefinitionCommands;
|
|
82929
83067
|
for (const command of definitionCommands) run(command);
|
|
82930
83068
|
return outcome;
|
|
82931
83069
|
}
|
|
@@ -82937,10 +83075,140 @@ function startDaemonService(options = {}) {
|
|
|
82937
83075
|
const { plan, run } = createHostContext(options);
|
|
82938
83076
|
for (const command of plan.startCommands) run(command);
|
|
82939
83077
|
}
|
|
82940
|
-
function
|
|
83078
|
+
async function stopDaemonServiceAndWait(identity, timeoutMs, options = {}) {
|
|
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 = {}) {
|
|
82941
83147
|
const ctx = createHostContext({ ...options, args: options.args ?? args });
|
|
82942
|
-
|
|
82943
|
-
|
|
83148
|
+
if (ctx.plan.platform === "win32") {
|
|
83149
|
+
const ownership = ctx.run(ctx.plan.statusCommand);
|
|
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
|
+
}
|
|
82944
83212
|
console.info("[alan-agent] Per-user daemon service removed");
|
|
82945
83213
|
}
|
|
82946
83214
|
function installDaemonService(args = [], runtime, options = {}) {
|
|
@@ -82968,7 +83236,7 @@ function installDaemonService(args = [], runtime, options = {}) {
|
|
|
82968
83236
|
});
|
|
82969
83237
|
return;
|
|
82970
83238
|
}
|
|
82971
|
-
if (
|
|
83239
|
+
if (inspection.state !== "stopped") {
|
|
82972
83240
|
console.info("[alan-agent] Per-user daemon service already present", {
|
|
82973
83241
|
profile,
|
|
82974
83242
|
state: inspection.state
|
|
@@ -82992,14 +83260,48 @@ ${result.output}
|
|
|
82992
83260
|
}
|
|
82993
83261
|
|
|
82994
83262
|
// src/install-command.ts
|
|
82995
|
-
|
|
83263
|
+
var WINDOWS_SETUP_READY_TIMEOUT_MS = 9e3;
|
|
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
|
+
}
|
|
82996
83268
|
bindConfigPath(commandArgs);
|
|
82997
83269
|
const configured = readConfig();
|
|
82998
83270
|
const configurationMode = resolveInstallConfigurationMode(commandArgs, configured);
|
|
82999
|
-
|
|
83271
|
+
const isWindowsSetup = currentPlatform === "win32" && configurationMode === "setup";
|
|
83272
|
+
const deadline = isWindowsSetup ? Date.now() + WINDOWS_SETUP_READY_TIMEOUT_MS : 0;
|
|
83273
|
+
const runtimeIdentityChanged = configurationMode === "setup" ? await setupDaemon(commandArgs) : false;
|
|
83000
83274
|
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
|
+
}
|
|
83001
83295
|
installDaemonService(commandArgs);
|
|
83002
|
-
|
|
83296
|
+
while (Date.now() < deadline) {
|
|
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");
|
|
83003
83305
|
}
|
|
83004
83306
|
|
|
83005
83307
|
// src/lifecycle-logger.ts
|
|
@@ -86376,7 +86678,7 @@ var REQUEST_STATE_ONLY_LEG_PACING_MS2 = 250;
|
|
|
86376
86678
|
function inputRequiredRoundsExceededMessage2(method, maxRounds) {
|
|
86377
86679
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
86378
86680
|
}
|
|
86379
|
-
function
|
|
86681
|
+
function sleep4(ms, signal) {
|
|
86380
86682
|
return new Promise((resolve15, reject) => {
|
|
86381
86683
|
if (signal?.aborted) {
|
|
86382
86684
|
reject(signal.reason instanceof SdkError2 ? signal.reason : new SdkError2(SdkErrorCode2.RequestTimeout, String(signal.reason)));
|
|
@@ -94643,7 +94945,7 @@ var LegacyInputRequiredShim = class {
|
|
|
94643
94945
|
} finally {
|
|
94644
94946
|
roundAbort.dispose();
|
|
94645
94947
|
}
|
|
94646
|
-
} else await
|
|
94948
|
+
} else await sleep4(REQUEST_STATE_ONLY_LEG_PACING_MS2, outerSignal);
|
|
94647
94949
|
let ctxNext = {
|
|
94648
94950
|
...ctx,
|
|
94649
94951
|
mcpReq: {
|
|
@@ -95440,7 +95742,7 @@ async function runMcpStdioProxy(env = process.env, local = new StdioServerTransp
|
|
|
95440
95742
|
}
|
|
95441
95743
|
|
|
95442
95744
|
// src/native-hook-command.ts
|
|
95443
|
-
var
|
|
95745
|
+
var import_node_crypto22 = require("crypto");
|
|
95444
95746
|
var import_node_fs27 = require("fs");
|
|
95445
95747
|
var import_node_os17 = require("os");
|
|
95446
95748
|
var import_node_path29 = require("path");
|
|
@@ -95809,7 +96111,7 @@ function parseNativeHookEnvelope(input2) {
|
|
|
95809
96111
|
const projectPin = eventType === "session_start" && resolvedCwd ? readAlanProjectPin(resolvedCwd) : void 0;
|
|
95810
96112
|
return {
|
|
95811
96113
|
schemaVersion: 1,
|
|
95812
|
-
eventId: (0,
|
|
96114
|
+
eventId: (0, import_node_crypto22.randomUUID)(),
|
|
95813
96115
|
provider: capability.provider,
|
|
95814
96116
|
eventType,
|
|
95815
96117
|
observedAt: (input2.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -96305,7 +96607,7 @@ function delay2(ms) {
|
|
|
96305
96607
|
}
|
|
96306
96608
|
|
|
96307
96609
|
// src/web-presenter.ts
|
|
96308
|
-
var
|
|
96610
|
+
var import_node_crypto23 = require("crypto");
|
|
96309
96611
|
var WebPresenter = class {
|
|
96310
96612
|
constructor(wsClient, conversationId, cwd) {
|
|
96311
96613
|
this.conversationId = conversationId;
|
|
@@ -96491,7 +96793,7 @@ var WebPresenter = class {
|
|
|
96491
96793
|
this.applicationProblem = createAlanProblem({
|
|
96492
96794
|
domain: "application",
|
|
96493
96795
|
code: input2.code,
|
|
96494
|
-
id: (0,
|
|
96796
|
+
id: (0, import_node_crypto23.randomUUID)(),
|
|
96495
96797
|
title: APPLICATION_PROBLEM_TITLES[input2.code],
|
|
96496
96798
|
detail: input2.message,
|
|
96497
96799
|
// The schema's slot for the technical cause, so the problem carries it
|
|
@@ -96561,7 +96863,7 @@ var WebPresenter = class {
|
|
|
96561
96863
|
};
|
|
96562
96864
|
|
|
96563
96865
|
// src/ws-client.ts
|
|
96564
|
-
var
|
|
96866
|
+
var import_node_crypto25 = require("crypto");
|
|
96565
96867
|
|
|
96566
96868
|
// ../shared/dist/agent-liveness.js
|
|
96567
96869
|
var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
|
|
@@ -96589,13 +96891,13 @@ var agentProbeAckSchema = external_exports.object({
|
|
|
96589
96891
|
});
|
|
96590
96892
|
|
|
96591
96893
|
// src/sandbox-outbox.ts
|
|
96592
|
-
var
|
|
96894
|
+
var import_node_crypto24 = require("crypto");
|
|
96593
96895
|
var import_node_fs28 = require("fs");
|
|
96594
96896
|
function deriveSandboxOutboxKey(sessionToken) {
|
|
96595
96897
|
if (!sessionToken) {
|
|
96596
96898
|
throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
|
|
96597
96899
|
}
|
|
96598
|
-
return (0,
|
|
96900
|
+
return (0, import_node_crypto24.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
|
|
96599
96901
|
}
|
|
96600
96902
|
function sandboxOutboxSpoolPath(conversationId) {
|
|
96601
96903
|
return `/tmp/alan-agent-outbox-${conversationId}.enc`;
|
|
@@ -96772,7 +97074,7 @@ var WSClient = class {
|
|
|
96772
97074
|
}
|
|
96773
97075
|
socket;
|
|
96774
97076
|
/** Stable id for this agent process; fences stale heartbeats server-side. */
|
|
96775
|
-
agentSessionId = (0,
|
|
97077
|
+
agentSessionId = (0, import_node_crypto25.randomUUID)();
|
|
96776
97078
|
/** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
|
|
96777
97079
|
heartbeatSeq = 0;
|
|
96778
97080
|
heartbeatTimer = null;
|
|
@@ -97604,7 +97906,7 @@ async function runSandbox(config2) {
|
|
|
97604
97906
|
}
|
|
97605
97907
|
|
|
97606
97908
|
// src/skills/skill-cli.ts
|
|
97607
|
-
var
|
|
97909
|
+
var import_promises7 = require("fs/promises");
|
|
97608
97910
|
var import_node_os19 = require("os");
|
|
97609
97911
|
var import_node_path30 = require("path");
|
|
97610
97912
|
async function runSkillsCommand(args) {
|
|
@@ -97651,7 +97953,7 @@ async function defaultScanInput(homeDirectory2) {
|
|
|
97651
97953
|
}
|
|
97652
97954
|
async function readProtectedJson(path2) {
|
|
97653
97955
|
const absolutePath = (0, import_node_path30.resolve)(path2);
|
|
97654
|
-
const stat2 = await (0,
|
|
97956
|
+
const stat2 = await (0, import_promises7.lstat)(absolutePath);
|
|
97655
97957
|
if (!stat2.isFile()) throw new Error(`Skill input is not a regular file: ${absolutePath}`);
|
|
97656
97958
|
if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
|
|
97657
97959
|
throw new Error(`Skill input must not be accessible by group or other users: ${absolutePath}`);
|
|
@@ -97660,7 +97962,7 @@ async function readProtectedJson(path2) {
|
|
|
97660
97962
|
throw new Error(`Skill input must be owned by the current user: ${absolutePath}`);
|
|
97661
97963
|
}
|
|
97662
97964
|
if (stat2.size > 2 * 1024 * 1024) throw new Error("Skill input exceeds 2 MiB");
|
|
97663
|
-
return JSON.parse(await (0,
|
|
97965
|
+
return JSON.parse(await (0, import_promises7.readFile)(absolutePath, "utf8"));
|
|
97664
97966
|
}
|
|
97665
97967
|
function option(args, name) {
|
|
97666
97968
|
const index = args.indexOf(name);
|
|
@@ -97818,7 +98120,7 @@ async function main() {
|
|
|
97818
98120
|
return;
|
|
97819
98121
|
}
|
|
97820
98122
|
if (command === "service" && commandArgs[0] === "uninstall") {
|
|
97821
|
-
uninstallDaemonService(commandArgs.slice(1));
|
|
98123
|
+
await uninstallDaemonService(commandArgs.slice(1));
|
|
97822
98124
|
return;
|
|
97823
98125
|
}
|
|
97824
98126
|
if (command === "service" && commandArgs[0] === "status") {
|