agentglow 0.3.1 → 0.3.2
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/cli/agentglow.mjs +8 -4
- package/cli/lib/autostart.mjs +23 -0
- package/cli/lib/server.mjs +49 -0
- package/dist/chunks/{KitScene-DC6xid52.js → KitScene-DbBTTc3H.js} +2352 -2309
- package/dist/chunks/{index-Ddw88ax_.js → index-B2VQ34Za.js} +1 -1
- package/dist/chunks/{index-BDKoRpg-.js → index-CHJsMIrM.js} +1 -1
- package/dist/chunks/{index-oEkhKSlb.js → index-CpXj5lT0.js} +72 -72
- package/dist/chunks/{index-BxCQhz1w.js → index-D-egBmZU.js} +1 -1
- package/dist/chunks/{index-tr3L_z5e.js → index-DZvO9oiI.js} +66 -66
- package/dist/chunks/{index-D_pLQJb9.js → index-OgLlwjoE.js} +5 -5
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/dist/types/world.d.ts +8 -0
- package/package.json +1 -1
package/cli/agentglow.mjs
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
baseUrl, claudeSettings, installSettingsFile, isOurHook, pluginIdsIn, readState, startHookCommand, uninstallSettingsFile,
|
|
11
11
|
} from "./lib/settings.mjs";
|
|
12
12
|
import {
|
|
13
|
-
clearLock, installCliCopy, localBase, readLock, readPid, removeCliCopies, serveForeground, spawnStarter,
|
|
13
|
+
clearLock, installCliCopy, localBase, readLock, readPid, refreshStaleServer, removeCliCopies, serveForeground, spawnStarter,
|
|
14
14
|
startBackground, stopServer,
|
|
15
15
|
} from "./lib/server.mjs";
|
|
16
16
|
import { which } from "./lib/uv.mjs";
|
|
17
|
-
import { hasAutostart, installAutostart, launchdLogPath, loginPath, removeAutostart, stopLoginItem } from "./lib/autostart.mjs";
|
|
17
|
+
import { hasAutostart, installAutostart, launchdLogPath, loginPath, removeAutostart, restartLoginItem, stopLoginItem } from "./lib/autostart.mjs";
|
|
18
18
|
|
|
19
19
|
const SELF = fileURLToPath(import.meta.url);
|
|
20
20
|
const here = path.dirname(SELF);
|
|
@@ -180,8 +180,12 @@ async function cmdSetup(o) {
|
|
|
180
180
|
const wantLogin = o.autostart === true || (o.autostart !== false && process.platform !== "win32");
|
|
181
181
|
const login = wantLogin ? installLoginItem(port) : null;
|
|
182
182
|
try {
|
|
183
|
-
if (login?.startsNow && (await waitHealthy(port, 120000)))
|
|
184
|
-
|
|
183
|
+
if (login?.startsNow && (await waitHealthy(port, 120000))) {
|
|
184
|
+
// an older server may still hold the port (the login item's new `start` then exits at once): replace it
|
|
185
|
+
const r = await refreshStaleServer({ port, expected: VERSION, deps: { restart: () => restartLoginItem() } });
|
|
186
|
+
if (r === "stale") console.error(`agentglow: warning: an older AgentGlow server still runs on port ${port}; stop it: npx agentglow stop${portFlag(port)}`);
|
|
187
|
+
console.log(`AgentGlow server ${r === "down" ? "is starting" : "is running"} on port ${port} (managed by ${login.method}).`);
|
|
188
|
+
} else {
|
|
185
189
|
const s2 = await startLocal(port);
|
|
186
190
|
console.log(s2.started ? `Started the AgentGlow server on port ${port}.` : `AgentGlow server already running on port ${port}.`);
|
|
187
191
|
}
|
package/cli/lib/autostart.mjs
CHANGED
|
@@ -143,6 +143,29 @@ export function stopLoginItem({ platform = process.platform } = {}) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** argv that restarts the login-item server now (kills the running one first); null on Windows (two schtasks calls). */
|
|
147
|
+
export function restartArgv({ platform = process.platform, uid = process.getuid?.() ?? 0 } = {}) {
|
|
148
|
+
if (platform === "darwin") return ["launchctl", "kickstart", "-k", `gui/${uid}/${LABEL}`];
|
|
149
|
+
if (platform === "win32") return null;
|
|
150
|
+
return ["systemctl", "--user", "restart", "agentglow"];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Restart the login-item server (e.g. setup installed a newer version while an older one runs). Never throws. */
|
|
154
|
+
export function restartLoginItem({ platform = process.platform } = {}) {
|
|
155
|
+
try {
|
|
156
|
+
if (platform === "win32") {
|
|
157
|
+
try { execFileSync("schtasks", ["/end", "/tn", TASK_NAME], { stdio: "pipe" }); } catch { /* not running */ }
|
|
158
|
+
execFileSync("schtasks", ["/run", "/tn", TASK_NAME], { stdio: "pipe" });
|
|
159
|
+
} else {
|
|
160
|
+
const [cmd, ...args] = restartArgv({ platform });
|
|
161
|
+
execFileSync(cmd, args, { stdio: "pipe" });
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
} catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
146
169
|
/** Best-effort check: is the autostart entry currently installed? Never throws. */
|
|
147
170
|
export function hasAutostart({ platform = process.platform, home = os.homedir() } = {}) {
|
|
148
171
|
try {
|
package/cli/lib/server.mjs
CHANGED
|
@@ -121,6 +121,55 @@ export function killTree(pid) {
|
|
|
121
121
|
try { process.kill(-pid, "SIGTERM"); } catch { try { process.kill(pid, "SIGTERM"); } catch { /* gone */ } }
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/** Version the server on `port` reports in /live/health ("" when down or unknown). */
|
|
125
|
+
export async function serverVersion(port) {
|
|
126
|
+
try { return JSON.parse((await fetchText(localBase(port) + "/live/health", 2000))?.body || "{}").version || ""; } catch { return ""; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** PIDs listening on a local TCP port (lsof; macOS / Linux). [] when unknown. */
|
|
130
|
+
export function listenerPids(port) {
|
|
131
|
+
if (process.platform === "win32") return [];
|
|
132
|
+
const r = spawnSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], { encoding: "utf8" });
|
|
133
|
+
return (r.stdout || "").split(/\s+/).map(Number).filter((n) => n > 0 && n !== process.pid);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* After `setup` (re)installed the login item: a server of another version still answering on the port (started by
|
|
138
|
+
* an older CLI, the SessionStart hook, or a login item launchd/systemd did not restart) is replaced by this version.
|
|
139
|
+
* Steps, each only while the old version still answers: stop the server this CLI started (pid file), restart the
|
|
140
|
+
* login item (launchctl kickstart -k / systemctl --user restart), then terminate whatever AgentGlow server still
|
|
141
|
+
* listens on the port. Waits for `expected` to answer. Returns "current" | "restarted" | "stale" (old one still up)
|
|
142
|
+
* | "down". Dependencies are injectable (tests).
|
|
143
|
+
*/
|
|
144
|
+
export async function refreshStaleServer({ port, expected, log = console.log, deps = {} }) {
|
|
145
|
+
const d = {
|
|
146
|
+
version: serverVersion, stop: stopServer, restart: () => false, listeners: listenerPids, kill: (pid) => process.kill(pid, "SIGTERM"),
|
|
147
|
+
sleep, timeoutMs: 120000, ...deps,
|
|
148
|
+
};
|
|
149
|
+
const running = await d.version(port);
|
|
150
|
+
if (!running || !expected || running === expected) return "current";
|
|
151
|
+
log(`The AgentGlow server on port ${port} is ${running}; this CLI is ${expected}. Restarting it ...`);
|
|
152
|
+
const waitFor = async (ms) => {
|
|
153
|
+
for (const t0 = Date.now(); Date.now() - t0 < ms; await d.sleep(500)) {
|
|
154
|
+
const v = await d.version(port);
|
|
155
|
+
if (v === expected) return v;
|
|
156
|
+
if (v && v !== running) return v;
|
|
157
|
+
}
|
|
158
|
+
return d.version(port);
|
|
159
|
+
};
|
|
160
|
+
await d.stop(port);
|
|
161
|
+
let v = await d.version(port);
|
|
162
|
+
if (v === running && d.restart()) v = await waitFor(8000);
|
|
163
|
+
if (v === running) { // nobody manages it (or the supervisor left it): it is AgentGlow (it answered), stop it
|
|
164
|
+
for (const pid of d.listeners(port)) { try { d.kill(pid); } catch { /* gone */ } }
|
|
165
|
+
await d.sleep(1000);
|
|
166
|
+
if (!d.restart()) log(" (no login item to restart: the next claude session starts the server)");
|
|
167
|
+
}
|
|
168
|
+
v = await waitFor(d.timeoutMs);
|
|
169
|
+
if (v === expected) return "restarted";
|
|
170
|
+
return v ? "stale" : "down";
|
|
171
|
+
}
|
|
172
|
+
|
|
124
173
|
/** Stop a server this CLI started. Returns "stopped" | "not-ours" | "none". */
|
|
125
174
|
export async function stopServer(port) {
|
|
126
175
|
const starter = readLock(port);
|