feat: start-at-login is a Settings switch, off by default (v2.5.0)
- Settings → About → "Start AgentHub automatically" (AutostartSettings.tsx,
GET/PUT /api/settings/autostart, server/lib/autostart.js). The switch is
the systemd --user unit's / LaunchAgent's enabled state: on writes a unit
for the running server when none exists and enables it, off disables it;
neither starts or stops the server. A foreign unit is never touched
(409 EFOREIGN)
- The plugin bootstrap no longer installs a unit on every install: it
installs or starts one only while the user has it enabled, and a rewrite
keeps the enabled state (writeUserService({ enable })). An install that
already had the unit enabled stays on. A stale-but-enabled unit beside a
live detached server is repointed, no longer started onto the bound port
- unitIsEnabled / setUnitEnabled in systemd-service.js and launchd-service.js
Also:
- fix(workspace): status bar no longer covers the chat box below md (#61)
This commit is contained in:
@@ -105,6 +105,26 @@ function unitIsActive(name) {
|
||||
return res.status === 0 && /^\s*state = running$/m.test(String(res.stdout || ""));
|
||||
}
|
||||
|
||||
/** Is our plist set to load at login — the Settings "start automatically"
|
||||
* switch. A `launchctl disable` override is what turns it off. */
|
||||
function unitIsEnabled(name) {
|
||||
if (!systemctlAvailable() || !unitIsOurs(name)) return false;
|
||||
const res = launchctl(["print-disabled", domain()]);
|
||||
const line = String(res.stdout || "")
|
||||
.split("\n")
|
||||
.find((l) => l.includes(`"${jobLabel(name)}"`));
|
||||
return !line || !/=>\s*(disabled|true)/.test(line);
|
||||
}
|
||||
|
||||
/** Enable or disable loading at login — never loads or unloads the job, so
|
||||
* the server answering the switch stays up either way. */
|
||||
function setUnitEnabled(name, on) {
|
||||
if (!systemctlAvailable()) return "unavailable";
|
||||
if (!unitIsOurs(name)) return "foreign";
|
||||
const res = launchctl([on ? "enable" : "disable", `${domain()}/${jobLabel(name)}`]);
|
||||
return res.status === 0 ? "ok" : "failed";
|
||||
}
|
||||
|
||||
/**
|
||||
* `argv` is the program and its arguments as separate strings (the systemd
|
||||
* twin's `execStart` is one line; a path with a space would split here).
|
||||
@@ -153,14 +173,15 @@ ${envLines}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the plist and clears any "disabled" override — and starts nothing.
|
||||
* Writes the plist and clears any "disabled" override (unless `enable:
|
||||
* false`, which leaves it as it is) — and starts nothing.
|
||||
* A loaded job keeps running its OLD definition until `restartUserService`;
|
||||
* an unloaded one is picked up at the next login. Same split, same reason, as
|
||||
* the systemd twin.
|
||||
*
|
||||
* @returns {"written"|"unchanged"|"foreign"|"unavailable"|"failed"}
|
||||
*/
|
||||
function writeUserService({ name, workingDir, argv, env = {} }) {
|
||||
function writeUserService({ name, workingDir, argv, env = {}, enable = true }) {
|
||||
if (!systemctlAvailable()) return "unavailable";
|
||||
const body = unitContent({ name, workingDir, argv, env });
|
||||
const target = unitPath(name);
|
||||
@@ -173,7 +194,7 @@ function writeUserService({ name, workingDir, argv, env = {} }) {
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(logPath()), { recursive: true });
|
||||
fs.writeFileSync(target, body, "utf8");
|
||||
launchctl(["enable", `${domain()}/${jobLabel(name)}`]);
|
||||
if (enable) launchctl(["enable", `${domain()}/${jobLabel(name)}`]);
|
||||
return "written";
|
||||
} catch {
|
||||
return "failed";
|
||||
@@ -229,6 +250,8 @@ module.exports = {
|
||||
unitIsStale,
|
||||
unitRoot,
|
||||
unitIsActive,
|
||||
unitIsEnabled,
|
||||
setUnitEnabled,
|
||||
writeUserService,
|
||||
restartUserService,
|
||||
installUserService,
|
||||
|
||||
Reference in New Issue
Block a user