kaizen-agent 1.9.0 → 1.9.1
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/install.ts +6 -4
- package/cli/web.ts +18 -4
- package/package.json +1 -1
package/cli/install.ts
CHANGED
|
@@ -279,7 +279,6 @@ if (wantWeb) {
|
|
|
279
279
|
const port = at !== -1 ? Number(Bun.argv[at + 1]) || undefined : undefined;
|
|
280
280
|
const url = `http://127.0.0.1:${port ?? 7420}/`;
|
|
281
281
|
const pidFile = join(home, ".kaizen", "web.pid");
|
|
282
|
-
const alive = async () => { try { await fetch(url); return true; } catch { return false; } };
|
|
283
282
|
|
|
284
283
|
if (args.has("--stop")) {
|
|
285
284
|
// The pid file only knows a --daemon; a board started from the shortcut or a
|
|
@@ -300,7 +299,10 @@ if (wantWeb) {
|
|
|
300
299
|
if (args.has("--daemon")) {
|
|
301
300
|
// The daemon is this same command, detached, with the browser left to us:
|
|
302
301
|
// ignored stdio is what lets it outlive the terminal.
|
|
303
|
-
|
|
302
|
+
// A board running an older kaizen is not "already running": the child finds
|
|
303
|
+
// it on the port and replaces it (web() in web.ts).
|
|
304
|
+
const { current } = await import("./web.ts");
|
|
305
|
+
if (await current(url)) {
|
|
304
306
|
console.log(` kaizen web already running at ${url}`);
|
|
305
307
|
} else {
|
|
306
308
|
const argv = Bun.argv.slice(2).filter((a) => a !== "--daemon" && a !== "--no-open");
|
|
@@ -310,8 +312,8 @@ if (wantWeb) {
|
|
|
310
312
|
child.unref();
|
|
311
313
|
mkdirSync(dirname(pidFile), { recursive: true });
|
|
312
314
|
writeFileSync(pidFile, String(child.pid));
|
|
313
|
-
for (let i = 0; i < 30 && !(await
|
|
314
|
-
if (!(await
|
|
315
|
+
for (let i = 0; i < 30 && !(await current(url)); i++) await Bun.sleep(100);
|
|
316
|
+
if (!(await current(url))) { console.log(` kaizen web did not come up on ${url}`); process.exit(1); }
|
|
315
317
|
console.log(`\n kaizen web ${url} (pid ${child.pid}, detached)`);
|
|
316
318
|
console.log(` kaizen web --stop to stop it\n`);
|
|
317
319
|
}
|
package/cli/web.ts
CHANGED
|
@@ -16,6 +16,19 @@ const PAGE = join(dirname(import.meta.dir), "web", "board.html");
|
|
|
16
16
|
const NOTES_PAGE = join(dirname(import.meta.dir), "web", "notes.html");
|
|
17
17
|
const NOTES_JS = join(dirname(import.meta.dir), "web", "notebook.js");
|
|
18
18
|
const DEFAULT_PORT = 7420;
|
|
19
|
+
// Read once at start: a board keeps the code it started with, so this is what it
|
|
20
|
+
// runs, not what is installed now. `/` sends it; `current()` compares the two.
|
|
21
|
+
const VERSION = (() => {
|
|
22
|
+
try { return JSON.parse(readFileSync(join(dirname(import.meta.dir), "package.json"), "utf8")).version as string; }
|
|
23
|
+
catch { return "unknown"; }
|
|
24
|
+
})();
|
|
25
|
+
|
|
26
|
+
// A board answers at url and runs the installed version. A board from before the
|
|
27
|
+
// version header sends none, so it counts as stale too.
|
|
28
|
+
export async function current(url: string) {
|
|
29
|
+
const r = await fetch(url, { signal: AbortSignal.timeout(1500) }).catch(() => null);
|
|
30
|
+
return !!r?.ok && r.headers.get("x-kaizen-version") === VERSION;
|
|
31
|
+
}
|
|
19
32
|
|
|
20
33
|
type Opts = { port?: number; open?: boolean };
|
|
21
34
|
|
|
@@ -158,11 +171,12 @@ export async function web(repoDir: string, opts: Opts = {}) {
|
|
|
158
171
|
if (!["EADDRINUSE", "EACCES"].includes((e as { code?: string }).code ?? "")) throw e;
|
|
159
172
|
const url = `http://127.0.0.1:${port}/`;
|
|
160
173
|
const up = await fetch(url, { signal: AbortSignal.timeout(1500) }).then((r) => r.ok, () => false);
|
|
161
|
-
if (up) { console.log(`\n kaizen web already at ${url}\n`); if (opts.open !== false) await openApp(url); return; }
|
|
162
|
-
// A listener that does not answer is a board that hung
|
|
174
|
+
if (up && await current(url)) { console.log(`\n kaizen web already at ${url}\n`); if (opts.open !== false) await openApp(url); return; }
|
|
175
|
+
// A listener that does not answer is a board that hung, and one running an
|
|
176
|
+
// older kaizen serves 404 for whatever came since; both are ours to stop.
|
|
163
177
|
const pid = await pidOnPort(port);
|
|
164
178
|
if (pid && pid !== process.pid) {
|
|
165
|
-
try { process.kill(pid, "SIGTERM"); console.log(` stopped a hung board on ${port} (pid ${pid})`); } catch { /* not ours to kill */ }
|
|
179
|
+
try { process.kill(pid, "SIGTERM"); console.log(` stopped a ${up ? "stale" : "hung"} board on ${port} (pid ${pid})`); } catch { /* not ours to kill */ }
|
|
166
180
|
await Bun.sleep(300);
|
|
167
181
|
try { server = serve(port); continue; } catch { /* still held */ }
|
|
168
182
|
}
|
|
@@ -186,7 +200,7 @@ export async function web(repoDir: string, opts: Opts = {}) {
|
|
|
186
200
|
if (!existsSync(PAGE)) return new Response("web/board.html missing", { status: 500 });
|
|
187
201
|
// The page gets the notify rule from state.ts rather than keeping its own copy.
|
|
188
202
|
return new Response(readFileSync(PAGE, "utf8").replace("/*notifier*/", () => notifier.toString()), {
|
|
189
|
-
headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
|
|
203
|
+
headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "x-kaizen-version": VERSION },
|
|
190
204
|
});
|
|
191
205
|
}
|
|
192
206
|
|
package/package.json
CHANGED