@integrity-labs/agt-cli 0.28.1177 → 0.28.1179
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/bin/agt.js +104 -417
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-DJNCIWVG.js → chunk-2FHYCFZM.js} +2 -2
- package/dist/{chunk-H37WRW2E.js → chunk-GJD536LU.js} +9 -6
- package/dist/chunk-GJD536LU.js.map +1 -0
- package/dist/{manager-supervisor-KIFBGETA.js → chunk-H3JMPEH7.js} +176 -8
- package/dist/chunk-H3JMPEH7.js.map +1 -0
- package/dist/{chunk-NHUXO5H2.js → chunk-KDYZNWZW.js} +2 -2
- package/dist/{chunk-45UH24I2.js → chunk-O5P2OS36.js} +435 -56
- package/dist/chunk-O5P2OS36.js.map +1 -0
- package/dist/{chunk-47P33SZF.js → chunk-PINFVWGX.js} +120 -3
- package/dist/chunk-PINFVWGX.js.map +1 -0
- package/dist/{claude-code-updater-H7RNX6M7.js → claude-code-updater-LL7KVAU3.js} +3 -3
- package/dist/{claude-pair-runtime-53JY6TQX.js → claude-pair-runtime-R5MNSUM7.js} +3 -3
- package/dist/lib/manager-worker.js +636 -262
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/manager-supervisor-TD7E6FPC.js +26 -0
- package/dist/mcp/direct-chat-channel.js +18 -1
- package/dist/mcp/index.js +151 -63
- package/dist/mcp/origami.js +18 -1
- package/dist/mcp/slack-channel.js +18 -1
- package/dist/mcp/telegram-channel.js +18 -1
- package/dist/{persistent-session-HVYGIS5C.js → persistent-session-34QXCPJK.js} +4 -4
- package/dist/persistent-session-34QXCPJK.js.map +1 -0
- package/dist/{responsiveness-probe-OGZ6ADVR.js → responsiveness-probe-ZT6ITMYI.js} +4 -4
- package/dist/{session-auth-dead-2JJLKURE.js → session-auth-dead-KAHV2ES2.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-45UH24I2.js.map +0 -1
- package/dist/chunk-47P33SZF.js.map +0 -1
- package/dist/chunk-H37WRW2E.js.map +0 -1
- package/dist/manager-supervisor-KIFBGETA.js.map +0 -1
- /package/dist/{chunk-DJNCIWVG.js.map → chunk-2FHYCFZM.js.map} +0 -0
- /package/dist/{chunk-NHUXO5H2.js.map → chunk-KDYZNWZW.js.map} +0 -0
- /package/dist/{claude-code-updater-H7RNX6M7.js.map → claude-code-updater-LL7KVAU3.js.map} +0 -0
- /package/dist/{claude-pair-runtime-53JY6TQX.js.map → claude-pair-runtime-R5MNSUM7.js.map} +0 -0
- /package/dist/{persistent-session-HVYGIS5C.js.map → manager-supervisor-TD7E6FPC.js.map} +0 -0
- /package/dist/{responsiveness-probe-OGZ6ADVR.js.map → responsiveness-probe-ZT6ITMYI.js.map} +0 -0
- /package/dist/{session-auth-dead-2JJLKURE.js.map → session-auth-dead-KAHV2ES2.js.map} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "./chunk-DGUM43GV.js";
|
|
2
|
-
|
|
3
1
|
// src/lib/manager-supervisor.ts
|
|
4
2
|
import { execFileSync } from "child_process";
|
|
5
3
|
import {
|
|
6
4
|
existsSync,
|
|
7
5
|
mkdirSync,
|
|
8
6
|
readFileSync,
|
|
7
|
+
renameSync,
|
|
8
|
+
statSync,
|
|
9
9
|
unlinkSync,
|
|
10
10
|
writeFileSync,
|
|
11
11
|
chmodSync
|
|
@@ -630,6 +630,165 @@ StandardError=append:${logPath}
|
|
|
630
630
|
WantedBy=multi-user.target
|
|
631
631
|
`;
|
|
632
632
|
}
|
|
633
|
+
var ENV_FILE_API_KEY_LINE = /^[ \t]*(?:export[ \t]+)?AGT_API_KEY[ \t]*=.*$/gm;
|
|
634
|
+
function rewriteEnvFileApiKey(text, rawKey) {
|
|
635
|
+
ENV_FILE_API_KEY_LINE.lastIndex = 0;
|
|
636
|
+
if (!ENV_FILE_API_KEY_LINE.test(text)) return { kind: "no-key" };
|
|
637
|
+
ENV_FILE_API_KEY_LINE.lastIndex = 0;
|
|
638
|
+
return {
|
|
639
|
+
kind: "rewrite",
|
|
640
|
+
text: text.replace(ENV_FILE_API_KEY_LINE, () => `AGT_API_KEY=${rawKey}`)
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
var PLIST_API_KEY_VALUE = /<key>AGT_API_KEY<\/key>\s*<string>[^<]*<\/string>/;
|
|
644
|
+
function refusePlistApiKey(text, _rawKey) {
|
|
645
|
+
if (!PLIST_API_KEY_VALUE.test(text)) return { kind: "no-key" };
|
|
646
|
+
return {
|
|
647
|
+
kind: "refuse",
|
|
648
|
+
reason: "launchd serves the job definition it loaded, so rewriting the plist would not reach a KeepAlive respawn; a rotation here needs an unload+load this process cannot survive"
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function envFileHeldKey(text) {
|
|
652
|
+
const lines = [...text.matchAll(ENV_FILE_API_KEY_LINE)];
|
|
653
|
+
const last = lines.at(-1)?.[0];
|
|
654
|
+
if (last === void 0) return null;
|
|
655
|
+
const value = last.slice(last.indexOf("=") + 1).trim();
|
|
656
|
+
const unquoted = /^(["'])(.*)\1$/.exec(value)?.[2] ?? value;
|
|
657
|
+
return unquoted || null;
|
|
658
|
+
}
|
|
659
|
+
var SUPERVISOR_KEY_CHANNELS = [
|
|
660
|
+
{ path: MACOS_PLIST, decide: refusePlistApiKey },
|
|
661
|
+
{ path: LINUX_ENV_FILE_PATH, decide: rewriteEnvFileApiKey, heldKey: envFileHeldKey },
|
|
662
|
+
{ path: LINUX_SYSTEM_ENV_FILE_PATH, decide: rewriteEnvFileApiKey, heldKey: envFileHeldKey }
|
|
663
|
+
];
|
|
664
|
+
function writePrivateAtomic(path, text) {
|
|
665
|
+
const tmp = `${path}.agt-rotate.${process.pid}.tmp`;
|
|
666
|
+
writeFileSync(tmp, text, { mode: 384 });
|
|
667
|
+
try {
|
|
668
|
+
renameSync(tmp, path);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
bestEffort(() => unlinkSync(tmp));
|
|
671
|
+
throw err;
|
|
672
|
+
}
|
|
673
|
+
chmodSync(path, 384);
|
|
674
|
+
}
|
|
675
|
+
function channelPresence(path) {
|
|
676
|
+
try {
|
|
677
|
+
statSync(path);
|
|
678
|
+
return "present";
|
|
679
|
+
} catch (err) {
|
|
680
|
+
const code = err?.code;
|
|
681
|
+
if (code === "ENOENT") return "absent";
|
|
682
|
+
return {
|
|
683
|
+
error: `${code ?? "stat failed"}: cannot tell whether this channel holds AGT_API_KEY`,
|
|
684
|
+
permanent: isPermanentFsError(code)
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
var PERMANENT_FS_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
689
|
+
"EACCES",
|
|
690
|
+
"EPERM",
|
|
691
|
+
"ENOTDIR",
|
|
692
|
+
"EISDIR",
|
|
693
|
+
"ELOOP",
|
|
694
|
+
"ENAMETOOLONG"
|
|
695
|
+
]);
|
|
696
|
+
function isPermanentFsError(code) {
|
|
697
|
+
return code !== void 0 && PERMANENT_FS_ERROR_CODES.has(code);
|
|
698
|
+
}
|
|
699
|
+
function supervisorChannelsRefusing(channels = SUPERVISOR_KEY_CHANNELS, requireKeyHolder = false) {
|
|
700
|
+
const runningKeys = typeof requireKeyHolder === "object" ? requireKeyHolder.runningKeys.filter((k) => k) : null;
|
|
701
|
+
const refusals = [];
|
|
702
|
+
let mayHoldKey = false;
|
|
703
|
+
for (const channel of channels) {
|
|
704
|
+
const presence = channelPresence(channel.path);
|
|
705
|
+
if (presence === "absent") continue;
|
|
706
|
+
if (presence !== "present") {
|
|
707
|
+
if (presence.permanent) refusals.push(`${channel.path}: ${presence.error}`);
|
|
708
|
+
else mayHoldKey = true;
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
let current;
|
|
712
|
+
try {
|
|
713
|
+
current = readFileSync(channel.path, "utf-8");
|
|
714
|
+
} catch (err) {
|
|
715
|
+
const code = err?.code;
|
|
716
|
+
if (isPermanentFsError(code)) {
|
|
717
|
+
refusals.push(`${channel.path}: unreadable (${code})`);
|
|
718
|
+
} else {
|
|
719
|
+
mayHoldKey = true;
|
|
720
|
+
}
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const decision = channel.decide(current, "");
|
|
724
|
+
if (decision.kind === "refuse") {
|
|
725
|
+
refusals.push(`${channel.path}: ${decision.reason}`);
|
|
726
|
+
} else if (decision.kind !== "no-key") {
|
|
727
|
+
const held = channel.heldKey ? channel.heldKey(current) : void 0;
|
|
728
|
+
const launchedThisManager = runningKeys === null || held === void 0 || held !== null && runningKeys.includes(held);
|
|
729
|
+
if (launchedThisManager) mayHoldKey = true;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (refusals.length === 0 && !mayHoldKey && requireKeyHolder) {
|
|
733
|
+
refusals.push(
|
|
734
|
+
runningKeys === null ? "no supervisor channel holds AGT_API_KEY, so nothing would hand the new key to the next manager start" : "no supervisor channel holds the key this manager is running on, so none of them launched it and nothing would hand the new key to its next start"
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
return refusals;
|
|
738
|
+
}
|
|
739
|
+
function rewriteSupervisorApiKey(rawKey, channels = SUPERVISOR_KEY_CHANNELS, rollbackTo) {
|
|
740
|
+
const out = { rewritten: [], failed: [], keyless: [] };
|
|
741
|
+
for (const channel of channels) {
|
|
742
|
+
const presence = channelPresence(channel.path);
|
|
743
|
+
if (presence === "absent") continue;
|
|
744
|
+
if (presence !== "present") {
|
|
745
|
+
out.failed.push({ path: channel.path, error: presence.error });
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
let current;
|
|
749
|
+
try {
|
|
750
|
+
current = readFileSync(channel.path, "utf-8");
|
|
751
|
+
} catch (err) {
|
|
752
|
+
out.failed.push({
|
|
753
|
+
path: channel.path,
|
|
754
|
+
error: `unreadable: ${err?.message ?? String(err)}`
|
|
755
|
+
});
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const decision = channel.decide(current, rawKey);
|
|
759
|
+
if (decision.kind === "no-key") {
|
|
760
|
+
out.keyless.push(channel.path);
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
if (decision.kind === "refuse") {
|
|
764
|
+
out.failed.push({ path: channel.path, error: `refused: ${decision.reason}` });
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (decision.text === current) {
|
|
768
|
+
out.rewritten.push(channel.path);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
writePrivateAtomic(channel.path, decision.text);
|
|
773
|
+
out.rewritten.push(channel.path);
|
|
774
|
+
} catch (err) {
|
|
775
|
+
out.failed.push({ path: channel.path, error: err?.message ?? String(err) });
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (rollbackTo && out.failed.length > 0 && out.rewritten.length > 0) {
|
|
779
|
+
const rewrittenPaths = new Set(out.rewritten);
|
|
780
|
+
const back = rewriteSupervisorApiKey(
|
|
781
|
+
rollbackTo,
|
|
782
|
+
channels.filter((c) => rewrittenPaths.has(c.path))
|
|
783
|
+
);
|
|
784
|
+
out.rolledBack = back.rewritten;
|
|
785
|
+
for (const f of back.failed) {
|
|
786
|
+
out.failed.push({ path: f.path, error: `rollback to the running key failed: ${f.error}` });
|
|
787
|
+
}
|
|
788
|
+
out.rewritten = out.rewritten.filter((p) => !back.rewritten.includes(p));
|
|
789
|
+
}
|
|
790
|
+
return out;
|
|
791
|
+
}
|
|
633
792
|
var _internals = {
|
|
634
793
|
MACOS_LABEL,
|
|
635
794
|
MACOS_PLIST,
|
|
@@ -649,6 +808,12 @@ var _internals = {
|
|
|
649
808
|
LINUX_RECOVER_SERVICE_PATH,
|
|
650
809
|
LINUX_RECOVER_TIMER_PATH,
|
|
651
810
|
renderLinuxEnvFile,
|
|
811
|
+
rewriteEnvFileApiKey,
|
|
812
|
+
refusePlistApiKey,
|
|
813
|
+
envFileHeldKey,
|
|
814
|
+
channelPresence,
|
|
815
|
+
isPermanentFsError,
|
|
816
|
+
SUPERVISOR_KEY_CHANNELS,
|
|
652
817
|
escapeXml,
|
|
653
818
|
escapeForSystemdEnv,
|
|
654
819
|
shellQuoteForSystemd,
|
|
@@ -658,14 +823,17 @@ var _internals = {
|
|
|
658
823
|
readEnvFile: () => existsSync(LINUX_ENV_FILE_PATH) ? readFileSync(LINUX_ENV_FILE_PATH, "utf-8") : null,
|
|
659
824
|
readSystemEnvFile: () => existsSync(LINUX_SYSTEM_ENV_FILE_PATH) ? readFileSync(LINUX_SYSTEM_ENV_FILE_PATH, "utf-8") : null
|
|
660
825
|
};
|
|
826
|
+
|
|
661
827
|
export {
|
|
662
|
-
_internals,
|
|
663
|
-
buildSystemUnitRepairExecStartPre,
|
|
664
828
|
installSupervisor,
|
|
665
|
-
|
|
829
|
+
uninstallSupervisor,
|
|
666
830
|
supervisorStatus,
|
|
831
|
+
installSystemUnit,
|
|
832
|
+
uninstallSystemUnit,
|
|
667
833
|
systemUnitStatus,
|
|
668
|
-
|
|
669
|
-
|
|
834
|
+
buildSystemUnitRepairExecStartPre,
|
|
835
|
+
supervisorChannelsRefusing,
|
|
836
|
+
rewriteSupervisorApiKey,
|
|
837
|
+
_internals
|
|
670
838
|
};
|
|
671
|
-
//# sourceMappingURL=
|
|
839
|
+
//# sourceMappingURL=chunk-H3JMPEH7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/manager-supervisor.ts"],"sourcesContent":["/**\n * OS-level supervision for the agt manager daemon (ENG-4593).\n *\n * Hands manager lifecycle off to the OS so a missing process is the OS's\n * problem, not ours. Replaces the in-process `agt manager start\n * --supervise` wrapper from ENG-4488.\n *\n * macOS: launchd LaunchAgent (KeepAlive=true + ThrottleInterval=10)\n * Linux: systemd --user unit (Restart=always + RestartSec=5) [Phase 2]\n * systemd system unit (Restart=on-failure, root) [Phase 3 / ENG-4706]\n *\n * Public surface:\n * - installSupervisor(opts) — write the unit, load/enable it, verify\n * - uninstallSupervisor() — unload/disable + remove the unit (idempotent)\n * - supervisorStatus() — quick readout of whether the OS knows about us\n *\n * - installSystemUnit(opts) — Linux only, root-only. Writes\n * /etc/systemd/system/agt-manager.service\n * so the manager survives EC2 reboot\n * without `loginctl enable-linger`\n * gymnastics. Used by host-bootstrap and\n * the SSM backfill runbook (ENG-4706).\n * - uninstallSystemUnit()\n * - systemUnitStatus()\n *\n * Each platform implementation lives in its own helper. The dispatcher\n * picks one based on `process.platform` and rejects unsupported OSes\n * with a clear error rather than silently no-oping.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n chmodSync,\n} from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// macOS LaunchAgent label and on-disk path. Stable so install/uninstall\n// can find each other without a state file.\nconst MACOS_LABEL = 'team.augmented.agt-manager';\nconst MACOS_PLIST = join(homedir(), 'Library', 'LaunchAgents', `${MACOS_LABEL}.plist`);\n\n// Linux systemd --user unit. Lives under the operator's XDG config so it\n// survives reboot only after `loginctl enable-linger` (we surface that as\n// a follow-up step rather than running it ourselves — needs root).\nconst LINUX_UNIT_NAME = 'agt-manager.service';\nconst LINUX_UNIT_PATH = join(homedir(), '.config', 'systemd', 'user', LINUX_UNIT_NAME);\n// Secrets live in a separate EnvironmentFile (mode 0600) instead of\n// being inlined into the unit body. The unit file itself is still\n// 0600 but plain Environment= lines in it are visible to anything\n// that can read systemd's parsed unit cache (`systemctl show`,\n// `systemd-cgls --all`, etc.). EnvironmentFile content stays private\n// to the systemd manager process.\nconst LINUX_ENV_FILE_PATH = join(homedir(), '.config', 'systemd', 'user', 'agt-manager.env');\n\nexport interface InstallOpts {\n /** Absolute path to the agt binary the supervisor should launch. */\n agtBin: string;\n /** Poll interval passed to `agt manager start`. Min 5 seconds. */\n intervalSec: number;\n /** Manager config directory (where pid/log/state files live). */\n configDir: string;\n /**\n * Environment variables the supervised manager needs at launch time.\n * AGT_HOST and AGT_API_KEY are required for the manager to call the\n * API; AGT_TEAM is optional but typical.\n */\n env: Record<string, string>;\n}\n\nexport type SupervisorPresence =\n | { kind: 'installed'; pid: number | null; details: string }\n | { kind: 'not-installed' }\n | { kind: 'unsupported-platform'; platform: string };\n\n// ---------------------------------------------------------------------------\n// Public dispatcher\n// ---------------------------------------------------------------------------\n\nexport async function installSupervisor(opts: InstallOpts): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return installMacos(opts);\n case 'linux':\n return installLinux(opts);\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor install is macOS / Linux only.` };\n }\n}\n\nexport async function uninstallSupervisor(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n switch (platform()) {\n case 'darwin':\n return uninstallMacos();\n case 'linux':\n return uninstallLinux();\n default:\n return { ok: false, error: `Unsupported platform: ${platform()}. Supervisor uninstall is macOS / Linux only.` };\n }\n}\n\nexport function supervisorStatus(): SupervisorPresence {\n switch (platform()) {\n case 'darwin':\n return statusMacos();\n case 'linux':\n return statusLinux();\n default:\n return { kind: 'unsupported-platform', platform: platform() };\n }\n}\n\n// ---------------------------------------------------------------------------\n// macOS — launchd LaunchAgent\n// ---------------------------------------------------------------------------\n\nfunction installMacos(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the manager. Run `agt setup <token>` first.' };\n }\n\n // If a previous version is already loaded, unload it before rewriting\n // the plist — otherwise launchctl bootstrap will fail with EALREADY.\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n\n // Ensure the LaunchAgents directory exists. macOS creates it on first\n // login, but freshly-installed admin users may not have it yet.\n mkdirSync(dirname(MACOS_PLIST), { recursive: true });\n // 0o600 — plist embeds AGT_API_KEY in EnvironmentVariables. World-\n // readable would expose the host's tlk_ token to every local user.\n writeFileSync(MACOS_PLIST, renderMacosPlist(opts), { mode: 0o600 });\n chmodSync(MACOS_PLIST, 0o600);\n\n try {\n execFileSync('launchctl', ['load', '-w', MACOS_PLIST], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `launchctl load failed: ${(err as Error).message}` };\n }\n\n // Verify launchd actually picked it up. `list <label>` returns 0 with\n // a plist of the loaded job's metadata; non-zero if not loaded.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n return {\n ok: true,\n details: `Loaded launchd LaunchAgent ${MACOS_LABEL}. Logs: ${join(opts.configDir, 'manager.log')}.\\n${out.trim().split('\\n').slice(0, 10).join('\\n')}`,\n };\n } catch (err) {\n return { ok: false, error: `Plist written but launchctl list reported nothing for ${MACOS_LABEL}: ${(err as Error).message}` };\n }\n}\n\nfunction uninstallMacos(): { ok: true; details: string } | { ok: false; error: string } {\n // Both steps are idempotent — unload reports an error if nothing's\n // loaded, removing the file errors if it doesn't exist. Swallow both;\n // the desired end state is \"no LaunchAgent for this label\".\n bestEffort(() => execFileSync('launchctl', ['unload', '-w', MACOS_PLIST], { stdio: 'ignore' }));\n let removed = false;\n if (existsSync(MACOS_PLIST)) {\n try {\n unlinkSync(MACOS_PLIST);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${MACOS_PLIST}: ${(err as Error).message}` };\n }\n }\n return {\n ok: true,\n details: removed\n ? `Removed ${MACOS_LABEL} from launchd and deleted ${MACOS_PLIST}.`\n : `${MACOS_LABEL} was already uninstalled (no plist at ${MACOS_PLIST}).`,\n };\n}\n\nfunction statusMacos(): SupervisorPresence {\n if (!existsSync(MACOS_PLIST)) return { kind: 'not-installed' };\n // launchctl list emits a 3-column line: PID Status Label. PID is \"-\"\n // when the job is loaded but not currently running.\n try {\n const out = execFileSync('launchctl', ['list', MACOS_LABEL], { encoding: 'utf-8' });\n const pidMatch = /\"PID\"\\s*=\\s*(\\d+);/.exec(out);\n const pid = pidMatch ? Number(pidMatch[1]) : null;\n return { kind: 'installed', pid, details: `Plist: ${MACOS_PLIST}` };\n } catch {\n // File exists but launchctl can't find the label → user removed it\n // out-of-band. Surface as \"not installed\" so re-install just works.\n return { kind: 'not-installed' };\n }\n}\n\nfunction renderMacosPlist(opts: InstallOpts): string {\n // Load the operator's env into the plist so the manager has the same\n // AGT_HOST / AGT_API_KEY / AGT_TEAM the shell session does. launchd\n // does NOT inherit env from the user's shell — anything we don't bake\n // in here won't be visible to the process.\n const envEntries = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null)\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n const args = [\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ];\n const argsXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join('\\n');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${MACOS_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(opts.agtBin)}</string>\n${argsXml}\n </array>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <true/>\n\n <!-- Throttle restarts so a crashloop doesn't peg the CPU. 10 seconds\n between launches is enough room for transient API blips while\n still recovering quickly from a real crash. -->\n <key>ThrottleInterval</key>\n <integer>10</integer>\n\n <!-- Where the manager's stdout / stderr land. The manager's logger\n redirects stderr into manager.log itself, but launchd needs an\n explicit destination at the OS level for any pre-logger output\n (e.g., Node startup errors before the watchdog wires up). -->\n <key>StandardOutPath</key>\n <string>${escapeXml(logPath)}</string>\n <key>StandardErrorPath</key>\n <string>${escapeXml(logPath)}</string>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(opts.configDir)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n</dict>\n</plist>\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction bestEffort(fn: () => void): void {\n try { fn(); } catch { /* swallow */ }\n}\n\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd --user unit\n// ---------------------------------------------------------------------------\n\nfunction installLinux(opts: InstallOpts): { ok: true; details: string } | { ok: false; error: string } {\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return { ok: false, error: 'AGT_HOST and AGT_API_KEY must be set in the calling shell so the supervisor can pass them to the unit. Run `agt setup <token>` first.' };\n }\n\n // Stop any previous instance before rewriting the unit — saves a\n // restart loop while daemon-reload picks up the new file.\n bestEffort(() => execFileSync('systemctl', ['--user', 'stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // ~/.config/systemd/user/ is the canonical user-unit path. Create it\n // recursively in case this is a fresh user with no XDG layout yet.\n mkdirSync(dirname(LINUX_UNIT_PATH), { recursive: true, mode: 0o700 });\n // Write the secrets-only env file FIRST (mode 0600) so the unit can\n // reference it via EnvironmentFile=. Non-secret env stays in the\n // unit body via Environment= lines for visibility.\n writeFileSync(LINUX_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_UNIT_PATH, renderLinuxUnit(opts), { mode: 0o600 });\n chmodSync(LINUX_UNIT_PATH, 0o600);\n\n try {\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user daemon-reload failed: ${(err as Error).message}` };\n }\n\n // `enable --now` enables the unit at boot AND starts it immediately.\n // Surviving reboot on a headless host additionally requires\n // `loginctl enable-linger <user>` (root-only); we surface that as a\n // post-install hint rather than running it ourselves.\n try {\n execFileSync('systemctl', ['--user', 'enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl --user enable --now failed: ${(err as Error).message}. (Hint: 'systemctl --user' requires a per-user dbus session; on a fresh SSH login try 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' first.)` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['--user', 'is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n // is-active exits non-zero for inactive/failed states. Capture its\n // output so the operator sees the real status.\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n // Don't claim the install succeeded if the unit ended up failed /\n // inactive / unknown. Without this, the CLI would exit 0 with a\n // green \"supervisor installed\" message while the manager was\n // already broken.\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl --user status ${LINUX_UNIT_NAME}' or 'journalctl --user -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd --user unit ${LINUX_UNIT_NAME} (status: ${isActive}). Logs: ${join(opts.configDir, 'manager.log')}.\\nFor reboot survival on a headless host run as root: 'loginctl enable-linger ${process.env['USER'] ?? '<your-user>'}'`,\n };\n}\n\nfunction uninstallLinux(): { ok: true; details: string } | { ok: false; error: string } {\n // disable --now stops + un-enables in one shot. Both legs are idempotent\n // — disable on a non-existent unit returns 0 with a notice on stderr.\n bestEffort(() => execFileSync('systemctl', ['--user', 'disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n let removed = false;\n if (existsSync(LINUX_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n // Best-effort cleanup of the secrets-only env file too; we don't\n // want a stale AGT_API_KEY left on disk after uninstall.\n if (existsSync(LINUX_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_ENV_FILE_PATH));\n }\n\n // Reload so a future install with the same path doesn't reuse the\n // stale unit metadata systemd cached in memory.\n bestEffort(() => execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd --user unit ${LINUX_UNIT_NAME} and deleted ${LINUX_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} was already uninstalled (no unit file at ${LINUX_UNIT_PATH}).`,\n };\n}\n\nfunction statusLinux(): SupervisorPresence {\n if (!existsSync(LINUX_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['--user', 'show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n // The unit file exists but `systemctl --user` couldn't probe it\n // — most often because XDG_RUNTIME_DIR / the per-user dbus isn't\n // available in this shell. Don't lie to the operator about\n // whether the supervisor is installed; report 'installed,\n // unknown state' instead.\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_UNIT_PATH} (could not query systemd --user: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_UNIT_PATH} (${activeState})` };\n}\n\n// Secrets that go into EnvironmentFile= instead of inline Environment=.\n// Anything in this set is written to LINUX_ENV_FILE_PATH (mode 0600)\n// and stripped from the unit body. Public env (PATH, AGT_HOST, AGT_TEAM)\n// stays inline for visibility in `systemctl show`.\nconst LINUX_SECRET_ENV_KEYS: ReadonlySet<string> = new Set(['AGT_API_KEY']);\n\nfunction renderLinuxUnit(opts: InstallOpts): string {\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from the EnvironmentFile= written separately.\n // systemd parses Environment= with a peculiar grammar — quotes are\n // optional but values can't contain literal newlines. We trust the\n // env values we forward and quote everything to be safe with spaces.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n // ExecStart cannot use shell metacharacters; pass the args explicitly\n // via systemd's array-of-args style (newline-separated wouldn't\n // parse — systemd splits on whitespace within ExecStart). So we\n // shell-quote each arg and join.\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor (ENG-4593)\n# Managed by \\`agt manager install\\` — edits will be overwritten.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# Restart rate-limit lives here per systemd.unit(5). Older systemds\n# tolerated these directives in the service section but newer ones\n# silently ignore them there, leaving the crashloop unprotected.\nStartLimitBurst=5\nStartLimitIntervalSec=60\n\n[Service]\nType=simple\nWorkingDirectory=${opts.configDir}\n${envLines}\nEnvironmentFile=${LINUX_ENV_FILE_PATH}\nExecStart=${execArgs}\n\n# Always come back from any exit; back off 5s between launches so a\n# crashloop can't peg the CPU. The 5/60s window above caps total\n# attempts before systemd marks the unit failed.\nRestart=always\nRestartSec=5\n\n# stdout/stderr → manager.log. journalctl --user -u agt-manager\n# still captures everything; this just gives operators a single file\n# to tail without learning systemd plumbing.\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=default.target\n`;\n}\n\n/**\n * Render the secrets-only env file referenced by EnvironmentFile= in\n * the unit. Format: `KEY=VALUE` per line (systemd's EnvironmentFile\n * parser doesn't support quoting the value the way Environment= does,\n * but it tolerates literal characters as long as the value has no\n * newline). One key per line; trailing newline.\n */\nfunction renderLinuxEnvFile(opts: InstallOpts): string {\n return Object.entries(opts.env)\n .filter(([k, v]) => LINUX_SECRET_ENV_KEYS.has(k) && v != null)\n .map(([k, v]) => `${k}=${v}\\n`)\n .join('');\n}\n\nfunction escapeForSystemdEnv(value: string): string {\n // Inside an Environment=\"K=V\" line, \" and \\\\ need escaping. Newlines\n // would terminate the directive — reject them upstream.\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\nfunction shellQuoteForSystemd(arg: string): string {\n // systemd's ExecStart parser supports double-quoted strings with\n // backslash escapes, similar to a POSIX shell but no command\n // substitution. Wrap any arg containing whitespace or shell metas.\n if (/^[A-Za-z0-9_./:=-]+$/.test(arg)) return arg;\n return `\"${arg.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n}\n\n// ---------------------------------------------------------------------------\n// Linux — systemd system unit (ENG-4706)\n// ---------------------------------------------------------------------------\n//\n// The `--user` unit above survives reboot only if `loginctl enable-linger`\n// is set per-user. On headless EC2 hosts that's a footgun: a reboot (kernel\n// patch, instance-type change, AWS-initiated maintenance) silently takes\n// every agent on the host offline until an operator manually re-launches\n// the manager. The system unit lives at /etc/systemd/system/, runs as root,\n// and is enabled into multi-user.target — so it comes up automatically at\n// boot just like any other service.\n//\n// Both unit kinds can co-exist; statusLinux() looks at the user one,\n// systemUnitStatus() looks at the system one. The CLI surfaces them as\n// `agt manager install` (user, dev workflow) and `agt manager\n// install-system-unit` (root, host-infra workflow) so the operator\n// chooses explicitly.\n\nconst LINUX_SYSTEM_UNIT_PATH = '/etc/systemd/system/agt-manager.service';\nconst LINUX_SYSTEM_ENV_FILE_PATH = '/etc/agt/agt-manager.env';\n// ENG-11695: resets and restarts the manager after StartLimitBurst latches it.\nconst LINUX_RECOVER_SERVICE_NAME = 'agt-manager-recover.service';\nconst LINUX_RECOVER_TIMER_NAME = 'agt-manager-recover.timer';\nconst LINUX_RECOVER_SERVICE_PATH = `/etc/systemd/system/${LINUX_RECOVER_SERVICE_NAME}`;\nconst LINUX_RECOVER_TIMER_PATH = `/etc/systemd/system/${LINUX_RECOVER_TIMER_NAME}`;\n\nexport interface InstallSystemUnitOpts extends InstallOpts {\n /**\n * Unix user the manager runs as. EC2 default is `root`. Other hosts\n * can run it as a less-privileged user, but the user must own\n * `configDir` and have permission to write to it. Defaults to `root`\n * when omitted (matches the ticket spec).\n */\n user?: string;\n}\n\nexport async function installSystemUnit(\n opts: InstallSystemUnitOpts,\n): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return {\n ok: false,\n error: `installSystemUnit is Linux-only (current platform: ${platform()}). For local dev on macOS use installSupervisor.`,\n };\n }\n if (process.getuid?.() !== 0) {\n return {\n ok: false,\n error: 'installSystemUnit requires root. Re-run with sudo, or call from the EC2 user-data bootstrap which already runs as root.',\n };\n }\n if (!existsSync(opts.agtBin)) {\n return { ok: false, error: `agt binary not found at ${opts.agtBin}` };\n }\n if (opts.intervalSec < 5) {\n return { ok: false, error: 'intervalSec must be >= 5' };\n }\n if (!opts.env.AGT_HOST || !opts.env.AGT_API_KEY) {\n return {\n ok: false,\n error: 'AGT_HOST and AGT_API_KEY must be set so the system unit can pass them through. Run `agt setup <token>` first (it writes them to /etc/environment, which this installer reads).',\n };\n }\n\n // Stop any running instance so daemon-reload picks up the rewritten\n // unit cleanly without a respawn race.\n bestEffort(() => execFileSync('systemctl', ['stop', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n // Belt-and-braces (ENG-4706 backfill): kill any pre-existing manager\n // process that wasn't started via systemd. On hosts that were\n // bootstrapped before this PR, the manager runs nohup'd from a one-\n // shot SSM command. Installing the system unit without stopping that\n // older process leaves TWO managers polling Supabase simultaneously —\n // they race on agent ticks, double-inject channel messages, and\n // double-write audit_log rows. SIGTERM gives it 3s to wind down\n // cleanly before the systemd start grabs the supervisor seat.\n // Ignored on hosts that have no rogue manager (the typical fresh-\n // bootstrap path).\n bestEffort(() => {\n const out = execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf-8' });\n const pids = out\n .split('\\n')\n .map((s) => s.trim())\n .filter((s) => s.length > 0 && /^\\d+$/.test(s) && Number(s) !== process.pid);\n if (pids.length === 0) return;\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGTERM'));\n }\n // Brief settle so the just-killed manager releases its Supabase\n // long-polls before the new one connects. 3s matches the default\n // poll interval; a manager that hasn't shut down in that window\n // gets SIGKILLed below.\n const deadline = Date.now() + 3_000;\n while (Date.now() < deadline) {\n try {\n execFileSync('pgrep', ['-f', 'agt manager start'], { stdio: 'ignore' });\n } catch {\n return;\n }\n // Spin — busy-wait is fine for a 3s ceiling and avoids pulling in\n // a sleep helper. This path runs once at install, never on the\n // hot poll loop.\n }\n // Still alive — escalate.\n for (const pid of pids) {\n bestEffort(() => process.kill(Number(pid), 'SIGKILL'));\n }\n });\n\n // Secrets-only env file lives under /etc/agt/ (root:root, 0600). Same\n // rationale as the user-unit env file: keep AGT_API_KEY out of any\n // tool that reads systemd's parsed unit cache.\n mkdirSync(dirname(LINUX_SYSTEM_ENV_FILE_PATH), { recursive: true, mode: 0o700 });\n writeFileSync(LINUX_SYSTEM_ENV_FILE_PATH, renderLinuxEnvFile(opts), { mode: 0o600 });\n chmodSync(LINUX_SYSTEM_ENV_FILE_PATH, 0o600);\n writeFileSync(LINUX_SYSTEM_UNIT_PATH, renderLinuxSystemUnit(opts), { mode: 0o644 });\n chmodSync(LINUX_SYSTEM_UNIT_PATH, 0o644);\n writeFileSync(LINUX_RECOVER_SERVICE_PATH, renderLinuxRecoverService(), { mode: 0o644 });\n chmodSync(LINUX_RECOVER_SERVICE_PATH, 0o644);\n writeFileSync(LINUX_RECOVER_TIMER_PATH, renderLinuxRecoverTimer(), { mode: 0o644 });\n chmodSync(LINUX_RECOVER_TIMER_PATH, 0o644);\n\n try {\n execFileSync('systemctl', ['daemon-reload'], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl daemon-reload failed: ${(err as Error).message}` };\n }\n\n // ENG-11695: enabled BEFORE the manager's own start, so a manager that fails\n // to come up right here is still recovered later. It must not fail the\n // install: the manager runs without it, as it always has, and the failure is\n // reported in the result instead.\n let recoverTimerNote = `Recovery timer ${LINUX_RECOVER_TIMER_NAME} enabled.`;\n try {\n execFileSync('systemctl', ['enable', '--now', LINUX_RECOVER_TIMER_NAME], { stdio: 'pipe' });\n } catch (err) {\n recoverTimerNote = `WARNING: could not enable ${LINUX_RECOVER_TIMER_NAME}, so this host will not recover by itself from StartLimitBurst: ${(err as Error).message}`;\n }\n\n // `enable --now` enables at boot AND starts immediately. Unlike the\n // --user variant, this needs no enable-linger — multi-user.target\n // pulls us in regardless of any user session.\n try {\n execFileSync('systemctl', ['enable', '--now', LINUX_UNIT_NAME], { stdio: 'pipe' });\n } catch (err) {\n return { ok: false, error: `systemctl enable --now failed: ${(err as Error).message}` };\n }\n\n let isActive = '';\n try {\n isActive = execFileSync('systemctl', ['is-active', LINUX_UNIT_NAME], { encoding: 'utf-8' }).trim();\n } catch (err) {\n isActive = ((err as { stdout?: Buffer; stderr?: Buffer }).stdout?.toString() ?? '').trim() || 'unknown';\n }\n\n if (isActive !== 'active') {\n return {\n ok: false,\n error: `systemd loaded ${LINUX_UNIT_NAME} but it did not become active (status: ${isActive}). Run 'systemctl status ${LINUX_UNIT_NAME}' or 'journalctl -u ${LINUX_UNIT_NAME} -n 50' for details.`,\n };\n }\n\n return {\n ok: true,\n details: `Loaded systemd system unit ${LINUX_UNIT_NAME} at ${LINUX_SYSTEM_UNIT_PATH} (status: ${isActive}). Will auto-start on reboot via multi-user.target. Logs: ${join(opts.configDir, 'manager.log')} and 'journalctl -u ${LINUX_UNIT_NAME}'. ${recoverTimerNote}`,\n };\n}\n\nexport async function uninstallSystemUnit(): Promise<{ ok: true; details: string } | { ok: false; error: string }> {\n if (platform() !== 'linux') {\n return { ok: false, error: `uninstallSystemUnit is Linux-only (current platform: ${platform()}).` };\n }\n if (process.getuid?.() !== 0) {\n return { ok: false, error: 'uninstallSystemUnit requires root.' };\n }\n\n // ENG-11695: recovery goes first, or it could restart what this is removing. The\n // service too, because stopping the timer doesn't stop a run already in progress\n // (Obi Wan b12268328098).\n bestEffort(() => execFileSync('systemctl', ['disable', '--now', LINUX_RECOVER_TIMER_NAME], { stdio: 'ignore' }));\n bestEffort(() => execFileSync('systemctl', ['stop', LINUX_RECOVER_SERVICE_NAME], { stdio: 'ignore' }));\n bestEffort(() => execFileSync('systemctl', ['disable', '--now', LINUX_UNIT_NAME], { stdio: 'ignore' }));\n\n for (const path of [LINUX_RECOVER_TIMER_PATH, LINUX_RECOVER_SERVICE_PATH]) {\n if (existsSync(path)) bestEffort(() => unlinkSync(path));\n }\n\n let removed = false;\n if (existsSync(LINUX_SYSTEM_UNIT_PATH)) {\n try {\n unlinkSync(LINUX_SYSTEM_UNIT_PATH);\n removed = true;\n } catch (err) {\n return { ok: false, error: `Failed to remove ${LINUX_SYSTEM_UNIT_PATH}: ${(err as Error).message}` };\n }\n }\n if (existsSync(LINUX_SYSTEM_ENV_FILE_PATH)) {\n bestEffort(() => unlinkSync(LINUX_SYSTEM_ENV_FILE_PATH));\n }\n\n bestEffort(() => execFileSync('systemctl', ['daemon-reload'], { stdio: 'ignore' }));\n\n return {\n ok: true,\n details: removed\n ? `Disabled systemd system unit ${LINUX_UNIT_NAME} and deleted ${LINUX_SYSTEM_UNIT_PATH}.`\n : `${LINUX_UNIT_NAME} system unit was already uninstalled (no unit file at ${LINUX_SYSTEM_UNIT_PATH}).`,\n };\n}\n\nexport function systemUnitStatus(): SupervisorPresence {\n if (platform() !== 'linux') {\n return { kind: 'unsupported-platform', platform: platform() };\n }\n if (!existsSync(LINUX_SYSTEM_UNIT_PATH)) return { kind: 'not-installed' };\n let pid: number | null = null;\n let activeState = 'unknown';\n try {\n const out = execFileSync(\n 'systemctl',\n ['show', LINUX_UNIT_NAME, '--property=MainPID,ActiveState'],\n { encoding: 'utf-8' },\n );\n const pidMatch = /^MainPID=(\\d+)/m.exec(out);\n if (pidMatch && pidMatch[1] !== '0') pid = Number(pidMatch[1]);\n const stateMatch = /^ActiveState=(\\S+)/m.exec(out);\n if (stateMatch) activeState = stateMatch[1] ?? 'unknown';\n } catch (err) {\n return {\n kind: 'installed',\n pid: null,\n details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (could not query systemd: ${(err as Error).message})`,\n };\n }\n return { kind: 'installed', pid, details: `Unit: ${LINUX_SYSTEM_UNIT_PATH} (${activeState})` };\n}\n\n/**\n * ENG-6698: ExecStartPre self-heal for a missing/dangling `agt` binary.\n *\n * The incident (agt-aws-1, full-fleet outage): a CLI self-update killed\n * mid-`npm i -g` (npm removes the old package before writing the new) left\n * `/usr/bin/agt` a dangling symlink, so `ExecStart` failed with 203/EXEC and\n * systemd crash-looped the unit to `failed`. With no recovery path the whole\n * fleet went dark until an operator manually reinstalled over SSM.\n *\n * This guard runs before every `ExecStart`: if `agt --version` won't run, it\n * reinstalls the pinned version (ENG-7833, from the unit's AGT_CLI_PIN_VERSION\n * env) or, when unpinned, the SAME release channel (AGT_CLI_RELEASE_CHANNEL,\n * default `latest`) before the manager starts, so a dangling binary self-heals\n * on the next start attempt instead of latching to `failed` with no way back.\n * Preferring the pin here matters: reinstalling the channel tip on a pinned\n * host would silently un-pin it.\n *\n * Two escaping notes that are load-bearing:\n * - systemd does its OWN `${VAR}` expansion on Exec lines (and does not\n * support the POSIX `:-default` form), so every shell `$` is written as\n * `$$` to pass through verbatim to /bin/sh, which then does the\n * `${VAR:-latest}` expansion itself.\n * - the script is one single-quoted `/bin/sh -c` argument with no single\n * quotes inside; `agtBin` is a filesystem path (no single quotes on our\n * hosts) and is double-quoted so a stray space can't split the arg.\n *\n * ENG-11695 (loyalty-host-2, 26 Oct): a reinstall alone can't work on a full\n * disk, or fix a self-referencing link. So when the binary won't run, this now\n * makes room first and relinks afterwards. A healthy host still takes the first\n * `--version` and runs none of it.\n *\n * It has to be inline shell. It runs precisely when `agt` won't execute, so it\n * can't call into this package, and a helper script on disk would add a way for\n * the unit to fail that a healthy host doesn't have today.\n *\n * `procRoot` exists for tests, so they read a fixture and not a shared host's\n * real processes.\n */\nexport function buildSystemUnitRepairExecStartPre(\n agtBin: string,\n user: string,\n opts: { procRoot?: string } = {},\n): string {\n const sudo = user === 'root' ? '' : 'sudo -n ';\n const proc = opts.procRoot ?? '/proc';\n const bin = '\"' + agtBin + '\"';\n // ENG-7833: prefer the pin (AGT_CLI_PIN_VERSION) over the channel so a\n // dangling-binary repair reinstalls the PINNED version, not the channel tip;\n // otherwise the repair would silently un-pin the host on the next start.\n // Nested POSIX `:-`: pin if set, else channel, else `latest`.\n const versionExpansion = '${AGT_CLI_PIN_VERSION:-${AGT_CLI_RELEASE_CHANNEL:-latest}}';\n const installSpec = '\"@integrity-labs/agt-cli@' + versionExpansion + '\"';\n\n // Written as plain POSIX sh, then every `$` is doubled at the end. The unit\n // line constrains what can appear here: no single quote (it would end the\n // `sh -c` argument), no backslash (systemd unescapes inside quotes), and no\n // `%` (systemd specifiers). That is why `tr` takes `[:cntrl:]`, not `\\000`,\n // and why the root comparison is `-ef`, not `stat -c`.\n const script = [\n bin + ' --version >/dev/null 2>&1 || {',\n 'echo \"[agt-manager] agt binary missing/dangling - reinstalling @integrity-labs/agt-cli@' +\n versionExpansion +\n '\";',\n\n // ENG-11695: the shell twin of `anyPackageInstallRunning` and\n // `isPackageInstallArgv` in disk-guard.ts, because this runs when `agt`\n // cannot, so it cannot call them. Cleaning a package cache under a running\n // install can cost that install its binary (Obi Wan b8ed1339eaf2). Same\n // rules: the process itself must be npm/pnpm, a container's install does\n // not count, and a root or a /proc that cannot be read counts as busy. Also the\n // same limit: a non-root manager under `hidepid` can't see root's installs. The\n // fleet's manager runs as root, which hidepid doesn't restrict.\n 'busy() {',\n '[ -r ' + proc + '/self/cmdline ] || return 0;',\n 'for p in ' + proc + '/[0-9]*; do',\n 'c=$(tr \"[:cntrl:]\" \" \" < \"$p/cmdline\" 2>/dev/null) || continue;',\n 'set -f; set -- $c; set +f;',\n '[ $# -gt 0 ] || continue;',\n 'case \"${1##*/}\" in npm|pnpm) shift ;; node|nodejs) case \"${2##*/}\" in npm|npm-cli.js|pnpm|pnpm.js|pnpm.cjs) shift 2 ;; *) continue ;; esac ;; *) continue ;; esac;',\n 'n=0; for a in \"$@\"; do',\n 'case \"$a\" in -*) continue ;; esac;',\n 'case \"$a\" in',\n 'install|i|ci|add|update|upgrade|up) { [ ! -e \"$p/root\" ] || [ \"$p/root\" -ef / ]; } && return 0; break ;;',\n 'run|run-script|exec|x|dlx|test|start) [ \"$n\" = 0 ] && break ;;',\n 'esac;',\n 'n=$((n+1)); [ \"$n\" -ge 2 ] && break;',\n 'done;',\n 'done; return 1; };',\n 'pkgclean() { if busy; then echo \"[agt-manager] skipping $* - an npm/pnpm install is running\"; else \"$@\" >/dev/null 2>&1 || true; fi; };',\n\n // ENG-11695 ask 2: the same safe cleanup as SAFE_CLEANUP_STEPS, at the same\n // threshold as SELF_UPDATE_MIN_FREE_BYTES (1 GiB in KiB). Caches and logs\n // only, nothing deleted by path. Unlike the self-update guard this never\n // refuses the install: the binary is already broken, so there is nothing\n // left for a failed install to lose. A `df` that fails reads as low.\n 'set -- $(df -Pk / 2>/dev/null | tail -1);',\n 'if [ \"${4:-0}\" -lt 1048576 ]; then',\n 'echo \"[agt-manager] under 1 GiB free - running safe cleanup before reinstalling\";',\n // `timeout` goes INSIDE sudo: sudo forwards TERM but can't forward KILL, so a\n // `timeout` outside it kills sudo and leaves the step running, possibly into\n // the install below (Obi Wan 95130c6e0a21).\n sudo + 'timeout -k 10 120 journalctl --vacuum-size=200M >/dev/null 2>&1 || true;',\n 'pkgclean timeout -k 10 120 npm cache clean --force;',\n 'pkgclean timeout -k 10 120 pnpm store prune;',\n ...(sudo ? ['pkgclean sudo -n -H timeout -k 10 120 npm cache clean --force;'] : []),\n 'fi;',\n\n sudo + 'npm install -g ' + installSpec + ' --registry=https://registry.npmjs.org;',\n\n // ENG-11695 ask 1: npm does not always rewrite a link it did not create, so\n // a dangling or self-referencing one can survive a good install. Only ever\n // a link or nothing: a real file at this path is not ours to replace.\n 'if ! ' + bin + ' --version >/dev/null 2>&1; then',\n 't=\"$(' + sudo + 'npm root -g 2>/dev/null)/@integrity-labs/agt-cli/dist/bin/agt.js\";',\n 'if [ -f \"$t\" ] && { [ -L ' + bin + ' ] || [ ! -e ' + bin + ' ]; }; then',\n 'echo \"[agt-manager] relinking ' + agtBin + ' to $t\";',\n sudo + 'ln -sfn \"$t\" ' + bin + ';',\n 'fi;',\n 'fi;',\n\n // The exit status of the whole repair: did it work?\n bin + ' --version >/dev/null 2>&1;',\n '}',\n ].join(' ');\n\n // systemd does its own ${VAR} expansion, so `$` has to reach it as `$$`.\n return \"ExecStartPre=/bin/sh -c '\" + script.replace(/\\$/g, '$$$$') + \"'\";\n}\n\n/**\n * ENG-11695 ask 3: get out of `failed`.\n *\n * Once StartLimitBurst trips, systemd stops trying, and the ExecStartPre repair\n * above never runs again. A timer rather than `OnFailure=`, because a timer's\n * cadence is the bound: at most one episode (StartLimitBurst starts) per\n * interval, whatever the failure does. `OnFailure=` fires on each failure and\n * would need its own limit.\n *\n * `is-failed` leaves a clean `systemctl stop` alone, because that ends `inactive`.\n * It can't tell an operator's stop from a crash once the unit is `failed`, and a\n * stop can end there two ways: the manager was already `failed` (Obi Wan\n * 9ec9691edd4b), or its drain hung and the 15s backstop in stopPolling exited 1\n * (Obi Wan 08df74eca266). So a deliberate hold always means stopping this timer\n * too, and docs/host-infra/README.md says so.\n *\n * Don't close that gap with SuccessExitStatus=1: under Restart=on-failure a real\n * crash that exits 1 would then never restart. And don't filter on the unit's\n * Result here without testing it on a host. A filter that misses what a latched\n * unit actually reports turns off this recovery with no signal, and that costs far\n * more than a hung stop coming back.\n */\nconst RECOVER_INTERVAL = '15min';\n\nfunction renderLinuxRecoverService(): string {\n return `# agt manager recovery (ENG-11695)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n\n[Unit]\nDescription=Restart the Augmented host manager if systemd has latched it to failed\n\n[Service]\nType=oneshot\nExecStart=/bin/sh -c 'systemctl is-failed --quiet ${LINUX_UNIT_NAME} || exit 0; echo \"[agt-manager-recover] ${LINUX_UNIT_NAME} is failed - resetting and starting\"; systemctl reset-failed ${LINUX_UNIT_NAME}; systemctl start --no-block ${LINUX_UNIT_NAME}'\n`;\n}\n\nfunction renderLinuxRecoverTimer(): string {\n return `# agt manager recovery timer (ENG-11695)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n\n[Unit]\nDescription=Check every ${RECOVER_INTERVAL} whether the Augmented host manager is latched to failed\n\n[Timer]\nOnBootSec=${RECOVER_INTERVAL}\nOnUnitActiveSec=${RECOVER_INTERVAL}\nUnit=${LINUX_RECOVER_SERVICE_NAME}\n\n[Install]\nWantedBy=timers.target\n`;\n}\n\nfunction renderLinuxSystemUnit(opts: InstallSystemUnitOpts): string {\n const user = opts.user ?? 'root';\n const home = user === 'root' ? '/root' : `/home/${user}`;\n\n // Non-secret env stays in the unit body via Environment= lines;\n // secrets are pulled from EnvironmentFile= written separately.\n const envLines = Object.entries(opts.env)\n .filter(([k, v]) => k.length > 0 && v != null && !LINUX_SECRET_ENV_KEYS.has(k))\n .map(([k, v]) => `Environment=\"${k}=${escapeForSystemdEnv(v)}\"`)\n .join('\\n');\n\n const execArgs = [\n opts.agtBin,\n 'manager',\n 'start',\n '--interval',\n String(opts.intervalSec),\n '--config-dir',\n opts.configDir,\n ]\n .map(shellQuoteForSystemd)\n .join(' ');\n\n const logPath = join(opts.configDir, 'manager.log');\n\n return `# agt manager supervisor — system unit (ENG-4706)\n# Managed by \\`agt manager install-system-unit\\` — edits will be overwritten.\n#\n# Unlike the sibling --user unit at ~/.config/systemd/user/agt-manager.service,\n# this one is enabled into multi-user.target so it auto-starts on every\n# boot regardless of any user login. Use this on EC2 / headless hosts.\n\n[Unit]\nDescription=Augmented host manager daemon\nAfter=network-online.target\nWants=network-online.target\n# ENG-6698: widened from 5/60s. The ExecStartPre repair below reinstalls a\n# dangling agt binary on each start attempt (~7s of npm + RestartSec), so a\n# transient miss needs several attempts within the window to self-heal before\n# systemd would otherwise latch the unit to \\`failed\\`. 10 tries / 5 min gives\n# the repair room to recover without spinning forever on a genuinely broken host.\n# ENG-11695: if it does latch, ${LINUX_RECOVER_TIMER_NAME} starts another round.\nStartLimitBurst=10\nStartLimitIntervalSec=300\n\n[Service]\nType=simple\nUser=${user}\nWorkingDirectory=${opts.configDir}\nEnvironment=\"HOME=${home}\"\n${envLines}\nEnvironmentFile=${LINUX_SYSTEM_ENV_FILE_PATH}\n# ENG-6698: self-heal a missing/dangling agt binary before starting, so an\n# interrupted self-update can't latch the unit to \\`failed\\` with no recovery.\n${buildSystemUnitRepairExecStartPre(opts.agtBin, user)}\nExecStart=${execArgs}\n# ENG-11695: this limit covers ExecStartPre, and the default 90s can't fit the\n# repair on a full disk. Worst case is 4 cleanup steps x 130s (each under its own\n# \\`timeout -k 10 120\\`, so one that ignores TERM still can't eat the budget) plus\n# 200s for npm. A hung npm is killed here and retried. A healthy start never gets\n# close.\nTimeoutStartSec=720\n\n# on-failure (not always) so a clean \\`systemctl stop\\` doesn't fight a\n# respawn loop. The ExecStartPre repair + the widened StartLimit above turn a\n# dangling-binary crashloop into a self-healing one. Logs land in ${logPath}\n# via the manager's own writer plus journalctl -u ${LINUX_UNIT_NAME}.\nRestart=on-failure\nRestartSec=5\n\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=multi-user.target\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Rotating AGT_API_KEY in the channels a FUTURE manager process reads\n// (ENG-11642)\n// ---------------------------------------------------------------------------\n//\n// THE SUPERVISOR'S OWN CHANNEL IS THE LOAD-BEARING ONE, and it is not\n// `/etc/environment`. Obi Wan (`188bcf0cc47f`, `a43136702405`) found the first\n// version of the rotation persisting only to `/etc/environment` and\n// `/etc/profile.d/agt.sh` — neither of which any supervisor reads:\n//\n// - the system unit : `EnvironmentFile=/etc/agt/agt-manager.env`\n// - the --user unit : `EnvironmentFile=~/.config/systemd/user/agt-manager.env`\n// - the launchd job : `AGT_API_KEY` baked into the plist's EnvironmentVariables\n//\n// `LINUX_SECRET_ENV_KEYS` strips the key out of the units' inline `Environment=`\n// lines precisely so it comes from the env file instead, and both env files are\n// written ONLY by install. So a rotation that skipped them left the running\n// process working and the next `systemctl restart` — a crash, a reboot, or the\n// frequent CLI self-update restart — booting on the key the ack had just\n// expired. That is the delayed, apparently-unconnected outage the persist-first\n// ordering exists to prevent, moved rather than removed.\n//\n// Rewrite in place rather than re-rendering: `renderLinuxEnvFile` emits\n// AGT_API_KEY alone, and the comment at `manager.ts`'s install-system-unit call\n// records that hosts have hand-added isolation vars to that file and lose them\n// on the next install. A rotation must not repeat that — so only the\n// AGT_API_KEY line moves and every other byte is preserved.\n\n/** What `rewriteSupervisorApiKey` managed to do, per channel. */\nexport interface SupervisorKeyRewrite {\n /** Channels that carried an `AGT_API_KEY` and now carry the new one. */\n rewritten: string[];\n /**\n * Channels that exist and carry an `AGT_API_KEY` this function could not\n * safely leave pointing at the new key. Non-empty means some future process\n * would boot on the OLD key, so the caller must not ack the rotation.\n *\n * It covers three different things on purpose, because the caller's decision\n * is the same for all of them: a write that failed, a file that could not be\n * STATTED or read, and a channel this function refuses to touch at all (the\n * launchd plist — see `refusePlistApiKey`).\n */\n failed: Array<{ path: string; error: string }>;\n /**\n * Channels present on this host that define no `AGT_API_KEY`. They never\n * supplied one, so there is nothing to rotate there — recorded rather than\n * dropped so \"no channel holds the key\" is distinguishable from \"every\n * channel was updated\".\n */\n keyless: string[];\n /**\n * Channels rewritten to the new key and then put BACK to `rollbackTo`\n * because another channel failed. Present only when a rollback key was given.\n */\n rolledBack?: string[];\n}\n\n/** What one channel's handler decided about a file it has just read. */\ntype ChannelDecision =\n /** This file defines no `AGT_API_KEY`; it never supplied one. */\n | { kind: 'no-key' }\n /** Write this text back. */\n | { kind: 'rewrite'; text: string }\n /**\n * This file holds the key and must NOT be rewritten in place. Rewriting it\n * would look like success and leave the key unreachable — worse than failing.\n */\n | { kind: 'refuse'; reason: string };\n\n/**\n * systemd's EnvironmentFile grammar is one `KEY=value` per line, so the line is\n * replaced wholesale. `gm` because a file with two AGT_API_KEY lines would have\n * systemd take the LAST — leaving either behind leaves the old key reachable.\n */\nconst ENV_FILE_API_KEY_LINE = /^[ \\t]*(?:export[ \\t]+)?AGT_API_KEY[ \\t]*=.*$/gm;\n\nfunction rewriteEnvFileApiKey(text: string, rawKey: string): ChannelDecision {\n ENV_FILE_API_KEY_LINE.lastIndex = 0;\n if (!ENV_FILE_API_KEY_LINE.test(text)) return { kind: 'no-key' };\n ENV_FILE_API_KEY_LINE.lastIndex = 0;\n // Function replacement, not a string: `$&` and friends are special in a\n // replacement string, and a key is not a place to find out.\n return {\n kind: 'rewrite',\n text: text.replace(ENV_FILE_API_KEY_LINE, () => `AGT_API_KEY=${rawKey}`),\n };\n}\n\nconst PLIST_API_KEY_VALUE = /<key>AGT_API_KEY<\\/key>\\s*<string>[^<]*<\\/string>/;\n\n/**\n * REFUSES the launchd plist rather than rewriting it, and that is the whole\n * point of this function.\n *\n * CodeRabbit on #7114. Editing the file is necessary and NOT sufficient:\n * `launchctl load` reads the plist once and launchd then serves the registered\n * job definition, so a `KeepAlive` respawn starts the manager with the\n * `AGT_API_KEY` launchd already holds — the old one. Only `launchctl unload` +\n * `load` re-reads it, and unload STOPS the running job, which here is the\n * manager performing the rotation.\n *\n * So a plist rewrite is the worst of the three outcomes: the persist reports\n * success, the ack expires the outgoing key, and the host keeps running on a\n * credential that dies at its next respawn. Refusing instead abandons the\n * rotation with a log line and leaves a working key in force.\n *\n * WHAT IS MISSING, so the next reader does not have to re-derive it: a reload\n * that survives stopping its own process — a detached helper that unloads,\n * loads, and lets the new job start. (The pending ack is persisted across a\n * restart now, so that half exists.) The helper does not, and\n * inventing them inside a review round is how a rotation fix becomes an outage.\n * A macOS host under `agt manager install` therefore cannot routine-rotate;\n * `reason: 'compromised'` is unaffected, since that path never self-rotates.\n */\nfunction refusePlistApiKey(text: string, _rawKey: string): ChannelDecision {\n if (!PLIST_API_KEY_VALUE.test(text)) return { kind: 'no-key' };\n return {\n kind: 'refuse',\n reason:\n 'launchd serves the job definition it loaded, so rewriting the plist would not reach a KeepAlive respawn; a rotation here needs an unload+load this process cannot survive',\n };\n}\n\n/** One channel a supervisor may inject `AGT_API_KEY` from. */\nexport interface SupervisorKeyChannel {\n path: string;\n decide: (text: string, rawKey: string) => ChannelDecision;\n /**\n * The `AGT_API_KEY` this channel would hand the next start, or null. Lets the\n * preflight tell the channel that launched this manager from a stale one\n * that merely sits on disk. A channel without it counts as a key holder\n * whenever it holds a key.\n */\n heldKey?: (text: string) => string | null;\n}\n\n/** The value systemd would read: the LAST `AGT_API_KEY=` line, unquoted. */\nfunction envFileHeldKey(text: string): string | null {\n const lines = [...text.matchAll(ENV_FILE_API_KEY_LINE)];\n const last = lines.at(-1)?.[0];\n if (last === undefined) return null;\n const value = last.slice(last.indexOf('=') + 1).trim();\n const unquoted = /^([\"'])(.*)\\1$/.exec(value)?.[2] ?? value;\n return unquoted || null;\n}\n\nconst SUPERVISOR_KEY_CHANNELS: ReadonlyArray<SupervisorKeyChannel> = [\n { path: MACOS_PLIST, decide: refusePlistApiKey },\n { path: LINUX_ENV_FILE_PATH, decide: rewriteEnvFileApiKey, heldKey: envFileHeldKey },\n { path: LINUX_SYSTEM_ENV_FILE_PATH, decide: rewriteEnvFileApiKey, heldKey: envFileHeldKey },\n];\n\n/**\n * Write via a temp file in the SAME directory then rename, so a crash or a full\n * disk mid-write cannot leave a supervisor env file holding half a key — which\n * no process could authenticate with and nothing would report.\n *\n * All three channels are mode 0600 at install, and `rename` takes the temp\n * file's mode, so the temp file is created 0600 rather than relying on the\n * chmod afterwards.\n */\nfunction writePrivateAtomic(path: string, text: string): void {\n const tmp = `${path}.agt-rotate.${process.pid}.tmp`;\n writeFileSync(tmp, text, { mode: 0o600 });\n try {\n renameSync(tmp, path);\n } catch (err) {\n bestEffort(() => unlinkSync(tmp));\n throw err;\n }\n chmodSync(path, 0o600);\n}\n\n/**\n * Whether a channel file is PRESENT, distinguishing \"absent\" from \"cannot tell\".\n *\n * `existsSync` collapses those two, and CodeRabbit on #7114 found the\n * consequence: `/etc/agt` is mode 0700 root-owned, so a manager running as a\n * non-root `User=` gets EACCES on the lookup, `existsSync` returns false, and\n * the channel holding the outgoing key is SKIPPED — with no `failed` entry, so\n * the persist reports success and the ack expires a key that file still names.\n * A silent skip inside the fix for a silent failure.\n *\n * ONLY ENOENT means absent. Everything else is an error the caller must see —\n * including ENOTDIR, which here means a path component that should be a\n * directory is a file, i.e. a broken host rather than a supervisor that is\n * simply not installed.\n */\nfunction channelPresence(path: string): 'absent' | 'present' | { error: string; permanent: boolean } {\n try {\n statSync(path);\n return 'present';\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === 'ENOENT') return 'absent';\n return {\n error: `${code ?? 'stat failed'}: cannot tell whether this channel holds AGT_API_KEY`,\n permanent: isPermanentFsError(code),\n };\n }\n}\n\n/**\n * Filesystem errors that the next attempt will hit again. A preflight that\n * skips one of these does not save a retry; it guarantees the persist fails\n * after the mint, every time. On a non-root `User=` manager, `/etc/agt` (0700,\n * root-owned) gives EACCES on every attempt, and a broken path gives ENOTDIR on\n * every attempt (CodeRabbit and Obi Wan `a2fca737943f` / `9bb183e7d09e` on\n * #7114). Anything outside this set is treated as transient.\n */\nconst PERMANENT_FS_ERROR_CODES: ReadonlySet<string> = new Set([\n 'EACCES',\n 'EPERM',\n 'ENOTDIR',\n 'EISDIR',\n 'ELOOP',\n 'ENAMETOOLONG',\n]);\n\nfunction isPermanentFsError(code: string | undefined): boolean {\n return code !== undefined && PERMANENT_FS_ERROR_CODES.has(code);\n}\n\n/**\n * Which supervisor channels would REFUSE a rotated key, read-only.\n *\n * Exists so the caller can find out before minting anything. `rewriteSupervisorApiKey`\n * can only answer the question by doing the work, and on a macOS host the answer\n * is always \"refused\" — so without this the mint happens first, every retry\n * inserts another active key, and the host walks into the server-side cap and\n * 409s permanently (CodeRabbit on #7114).\n *\n * A channel is reported when its write is CERTAIN to fail: the decision refuses\n * it (the launchd plist), or it cannot be statted or read for a reason the next\n * attempt will hit again (`PERMANENT_FS_ERROR_CODES`). A transient stat or read\n * error is still not a reason to skip a mint — the retry may well succeed, and\n * refusing on it would turn a blip into a host that never rotates. If one then\n * fails the persist, the manager keeps the minted key and retries with it.\n *\n * `requireKeyHolder` refuses when NO channel holds `AGT_API_KEY`. That host's\n * manager was started by hand or from a container's environment, and nothing\n * here can change what its next launch reads, so a rotation it acked would\n * leave the next start on the expired key (CodeRabbit on #7114). An earlier\n * version fell back to `/etc/environment`, which `agt manager start` never\n * reads. A channel skipped on a transient error counts as possibly holding the\n * key, so it never triggers this.\n *\n * Passed as `{ runningKeys }`, a channel only counts if the key it holds is one\n * of them: the key this manager is running on, or one it minted for this\n * request. A stale unit file left behind by another install, next to a manager\n * started by hand, holds a different key, and would otherwise vouch for a\n * launch it does not perform (CodeRabbit on #7114). The keys are compared in\n * memory and never logged. A stale file holding the SAME key as a manager\n * started by hand is not caught: nothing on disk tells the two apart.\n */\nexport function supervisorChannelsRefusing(\n channels: ReadonlyArray<SupervisorKeyChannel> = SUPERVISOR_KEY_CHANNELS,\n requireKeyHolder: boolean | { runningKeys: ReadonlyArray<string> } = false,\n): string[] {\n const runningKeys =\n typeof requireKeyHolder === 'object' ? requireKeyHolder.runningKeys.filter((k) => k) : null;\n const refusals: string[] = [];\n let mayHoldKey = false;\n for (const channel of channels) {\n const presence = channelPresence(channel.path);\n if (presence === 'absent') continue;\n if (presence !== 'present') {\n if (presence.permanent) refusals.push(`${channel.path}: ${presence.error}`);\n else mayHoldKey = true;\n continue;\n }\n let current: string;\n try {\n current = readFileSync(channel.path, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (isPermanentFsError(code)) {\n refusals.push(`${channel.path}: unreadable (${code})`);\n } else {\n mayHoldKey = true;\n }\n continue;\n }\n // The raw key is irrelevant to a refusal, and passing a real one into a\n // read-only probe would be handing a credential to code that has no use\n // for it. The decision functions only inspect the file.\n const decision = channel.decide(current, '');\n if (decision.kind === 'refuse') {\n refusals.push(`${channel.path}: ${decision.reason}`);\n } else if (decision.kind !== 'no-key') {\n const held = channel.heldKey ? channel.heldKey(current) : undefined;\n const launchedThisManager =\n runningKeys === null || held === undefined || (held !== null && runningKeys.includes(held));\n if (launchedThisManager) mayHoldKey = true;\n }\n }\n if (refusals.length === 0 && !mayHoldKey && requireKeyHolder) {\n refusals.push(\n runningKeys === null\n ? 'no supervisor channel holds AGT_API_KEY, so nothing would hand the new key to the next manager start'\n : 'no supervisor channel holds the key this manager is running on, so none of them launched it and nothing would hand the new key to its next start',\n );\n }\n return refusals;\n}\n\n/**\n * Point every supervisor channel on this host at a rotated `AGT_API_KEY`.\n *\n * Keyed on the file being PRESENT rather than on `process.platform`, because\n * the two Linux unit kinds are documented as co-existing — a host carrying both\n * must have both handled, and reading the platform would update one.\n *\n * Fails closed per channel, in every direction: a channel that cannot be\n * statted, cannot be read, cannot be written, or must not be written lands in\n * `failed`, because each of those means a file may still be naming the\n * outgoing key.\n */\nexport function rewriteSupervisorApiKey(\n rawKey: string,\n /**\n * Overridden ONLY by tests. The real list is three absolute paths — two of\n * them outside any temp directory — so the behaviour (preserve every other\n * line, replace all occurrences, mode 0600, write-then-rename, refuse the\n * plist) is only observable against channels a test owns. The real list is\n * pinned separately by `the channel list covers every supervisor this CLI\n * installs`, so an injected list cannot make the paths themselves untested.\n */\n channels: ReadonlyArray<SupervisorKeyChannel> = SUPERVISOR_KEY_CHANNELS,\n /**\n * The key to put back on channels already rewritten when ANOTHER channel\n * fails, so a failed persist leaves no channel half-switched (Obi Wan\n * `c967f8929b33` on #7114). Without it, a host with two channels where one\n * failed restarted on the minted key with no memory of minting it; its next\n * mint got 409 and the rotation stayed armed for good.\n */\n rollbackTo?: string,\n): SupervisorKeyRewrite {\n const out: SupervisorKeyRewrite = { rewritten: [], failed: [], keyless: [] };\n for (const channel of channels) {\n const presence = channelPresence(channel.path);\n if (presence === 'absent') continue;\n if (presence !== 'present') {\n out.failed.push({ path: channel.path, error: presence.error });\n continue;\n }\n let current: string;\n try {\n current = readFileSync(channel.path, 'utf-8');\n } catch (err) {\n out.failed.push({\n path: channel.path,\n error: `unreadable: ${(err as Error)?.message ?? String(err)}`,\n });\n continue;\n }\n const decision = channel.decide(current, rawKey);\n if (decision.kind === 'no-key') {\n out.keyless.push(channel.path);\n continue;\n }\n if (decision.kind === 'refuse') {\n out.failed.push({ path: channel.path, error: `refused: ${decision.reason}` });\n continue;\n }\n if (decision.text === current) {\n // Already this key — a re-run of a persist whose write landed before the\n // ack did. Counted as rewritten: the channel holds the new key, which is\n // the only thing the caller is asking about.\n out.rewritten.push(channel.path);\n continue;\n }\n try {\n writePrivateAtomic(channel.path, decision.text);\n out.rewritten.push(channel.path);\n } catch (err) {\n out.failed.push({ path: channel.path, error: (err as Error)?.message ?? String(err) });\n }\n }\n if (rollbackTo && out.failed.length > 0 && out.rewritten.length > 0) {\n const rewrittenPaths = new Set(out.rewritten);\n const back = rewriteSupervisorApiKey(\n rollbackTo,\n channels.filter((c) => rewrittenPaths.has(c.path)),\n );\n out.rolledBack = back.rewritten;\n for (const f of back.failed) {\n out.failed.push({ path: f.path, error: `rollback to the running key failed: ${f.error}` });\n }\n out.rewritten = out.rewritten.filter((p) => !back.rewritten.includes(p));\n }\n return out;\n}\n\n// Exposed for unit tests / introspection.\nexport const _internals = {\n MACOS_LABEL,\n MACOS_PLIST,\n LINUX_UNIT_NAME,\n LINUX_UNIT_PATH,\n LINUX_ENV_FILE_PATH,\n LINUX_SYSTEM_UNIT_PATH,\n LINUX_SYSTEM_ENV_FILE_PATH,\n LINUX_SECRET_ENV_KEYS,\n renderMacosPlist,\n renderLinuxUnit,\n renderLinuxSystemUnit,\n renderLinuxRecoverService,\n renderLinuxRecoverTimer,\n LINUX_RECOVER_SERVICE_NAME,\n LINUX_RECOVER_TIMER_NAME,\n LINUX_RECOVER_SERVICE_PATH,\n LINUX_RECOVER_TIMER_PATH,\n renderLinuxEnvFile,\n rewriteEnvFileApiKey,\n refusePlistApiKey,\n envFileHeldKey,\n channelPresence,\n isPermanentFsError,\n SUPERVISOR_KEY_CHANNELS,\n escapeXml,\n escapeForSystemdEnv,\n shellQuoteForSystemd,\n readPlist: () => (existsSync(MACOS_PLIST) ? readFileSync(MACOS_PLIST, 'utf-8') : null),\n readUnit: () => (existsSync(LINUX_UNIT_PATH) ? readFileSync(LINUX_UNIT_PATH, 'utf-8') : null),\n readSystemUnit: () => (existsSync(LINUX_SYSTEM_UNIT_PATH) ? readFileSync(LINUX_SYSTEM_UNIT_PATH, 'utf-8') : null),\n readEnvFile: () => (existsSync(LINUX_ENV_FILE_PATH) ? readFileSync(LINUX_ENV_FILE_PATH, 'utf-8') : null),\n readSystemEnvFile: () => (existsSync(LINUX_SYSTEM_ENV_FILE_PATH) ? readFileSync(LINUX_SYSTEM_ENV_FILE_PATH, 'utf-8') : null),\n};\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-XWVM4KPK.js";
|
|
5
5
|
import {
|
|
6
6
|
FORCED_UPDATE_HARD_DEADLINE_MS
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PINFVWGX.js";
|
|
8
8
|
import {
|
|
9
9
|
effectiveClaudeJsonPath
|
|
10
10
|
} from "./chunk-AIUIMEUS.js";
|
|
@@ -722,4 +722,4 @@ export {
|
|
|
722
722
|
probeClaudeInstalls,
|
|
723
723
|
maybeUpdateClaudeCode
|
|
724
724
|
};
|
|
725
|
-
//# sourceMappingURL=chunk-
|
|
725
|
+
//# sourceMappingURL=chunk-KDYZNWZW.js.map
|