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:
2026-10-06 18:53:10 +07:00
parent 4aae61297f
commit 03ecc9e29a
25 changed files with 620 additions and 36 deletions
+19
View File
@@ -252,6 +252,25 @@ router.get("/antigravity-hooks", codexHooksRoute(antigravityHooks.status));
router.post("/antigravity-hooks", sameOriginGuard, codexHooksRoute(antigravityHooks.install));
router.delete("/antigravity-hooks", sameOriginGuard, codexHooksRoute(antigravityHooks.remove));
// GET/PUT /api/settings/autostart — start the dashboard at boot/login through
// the per-user service (server/lib/autostart.js). Off by default; a PUT only
// enables or disables the unit, it never starts or stops the server.
const autostart = require("../lib/autostart");
router.get("/autostart", (_req, res) => res.json(autostart.status()));
router.put("/autostart", sameOriginGuard, (req, res) => {
if (typeof req.body?.enabled !== "boolean") {
return res
.status(400)
.json({ error: { code: "EBADVALUE", message: "`enabled` must be a boolean" } });
}
try {
res.json(autostart.setEnabled(req.body.enabled));
} catch (err) {
const status = { EUNAVAILABLE: 400, EFOREIGN: 409 }[err.code] || 500;
res.status(status).json({ error: { code: err.code || "ESERVICE", message: err.message } });
}
});
// POST /api/settings/reset-pricing — reset pricing to defaults. The seed is
// Claude's; a model priced by the automatic sync (an OpenAI one, from Codex
// sessions) is fetched back at once, waiting at most RESET_SYNC_MS for it.