Files
AgentHub/server/lib/autostart.js
T
nntrivi2001 a9c71cd905 feat: v2.5.12 — plan and quota for every AI tool, Grok Build and Kimi Code, Quit AgentHub (#65)
Usage panel (status bar):
- every account shows its plan (Claude subscriptionType, Codex
  chatgpt_plan_type, Cursor membershipType, Grok subscriptionTier)
- new quota rows: Cursor CLI (cursor.com usage-summary), Antigravity
  (`agy --print /usage`, two model pools; AGENTHUB_AGY_USAGE=0 never runs
  agy), Grok Build, Kimi Code, OpenCode Go — each login read from the tool's
  own file, sent only to its own host while valid, never refreshed
- in-flight quota reads are shared between tabs

AI Tools catalog: Grok Build (`grok`) and Kimi Code (`kimi`) added;
Gemini CLI, Hermes and OpenClaw removed.

Also: Quit AgentHub (#65, `agenthub stop`), status-area icon, Add to Chat
on an editor selection, Settings in three groups, JetBrains-style Changes
panel (ticked files/hunks, ignore, blame, file history), terminal font.

--no-verify: :3000 is taken by another dev server (lane-detect tests);
both suites run by hand — server 2636/2639 (the 3 are :3000), client
1131/1131.
2026-10-08 12:22:14 +07:00

80 lines
2.7 KiB
JavaScript

/**
* @file autostart.js
* @description Settings → System's "Start automatically" switch: whether the
* dashboard starts at boot/login through the per-user service
* (`scripts/service-manager.js` — a systemd `--user` unit, or a LaunchAgent on
* macOS). The switch IS the unit's enabled state, nothing stored beside it.
* Turning it on writes a unit for THIS server when none exists, and enables
* it; turning it off disables it. Neither ever starts or stops anything — the
* server answering the click is the one that would die. Off by default: the
* plugin bootstrap installs no unit until the user opts in.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const fs = require("fs");
const path = require("path");
const svc = require("../../scripts/service-manager");
const NAME = "agenthub";
const ROOT = path.resolve(__dirname, "..", "..");
/** A unit by our name that AgentHub did not write — never ours to touch. */
function isForeign() {
return fs.existsSync(svc.unitPath(NAME)) && !svc.unitIsOurs(NAME);
}
function status() {
const available = svc.systemctlAvailable();
return {
available,
kind: svc.kind,
enabled: available && svc.unitIsEnabled(NAME),
foreign: isForeign(),
};
}
/** The unit for the running server — same shape the bootstrap writes. */
function unitForThisServer() {
const entry = path.join(ROOT, "server", "index.js");
const env = { NODE_ENV: "production", PATH: process.env.PATH || "" };
for (const key of ["NODE_PATH", "DASHBOARD_CLIENT_DIST", "DASHBOARD_PORT"]) {
if (process.env[key]) env[key] = process.env[key];
}
return {
name: NAME,
description: "AgentHub dashboard server",
workingDir: ROOT,
execStart: `${process.execPath} ${entry}`,
argv: [process.execPath, entry],
env,
};
}
function fail(code, message) {
return Object.assign(new Error(message), { code });
}
function setEnabled(on) {
if (!svc.systemctlAvailable()) {
throw fail("EUNAVAILABLE", `no ${svc.label} manager is available on this machine`);
}
let result;
if (svc.unitIsOurs(NAME)) result = svc.setUnitEnabled(NAME, on);
else if (isForeign()) result = "foreign";
else if (!on)
result = "ok"; // nothing installed: already off
else {
result = svc.writeUserService(unitForThisServer()); // writes + enables, starts nothing
if (result === "written") svc.enableLinger();
}
if (result === "foreign") {
throw fail("EFOREIGN", `${svc.unitPath(NAME)} was not written by AgentHub — not touching it`);
}
if (result === "failed" || result === "unavailable") {
throw fail("ESERVICE", `could not change the ${svc.label}`);
}
return status();
}
module.exports = { status, setEnabled };