agent-theatre 1.3.0 → 1.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/dist/cli.js +51 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +51 -11
- package/dist/index.js.map +1 -1
- package/hooks/agent-theatre-hook.mjs +9 -7
- package/package.json +1 -1
- package/web/assets/browserAll-eGDH56DF.js +2 -0
- package/web/assets/{browserAll-DMCo76hh.js.map → browserAll-eGDH56DF.js.map} +1 -1
- package/web/assets/{index-BI2vZjgg.css → index-CWrWoYA0.css} +1 -1
- package/web/assets/{index-BULZyIph.js → index-HehktIj0.js} +63 -63
- package/web/assets/index-HehktIj0.js.map +1 -0
- package/web/assets/webworkerAll-2M7wVkAJ.js +2 -0
- package/web/assets/{webworkerAll-CrSrPeea.js.map → webworkerAll-2M7wVkAJ.js.map} +1 -1
- package/web/index.html +2 -2
- package/web/assets/browserAll-DMCo76hh.js +0 -2
- package/web/assets/index-BULZyIph.js.map +0 -1
- package/web/assets/webworkerAll-CrSrPeea.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -63,7 +63,8 @@ var INSTALL = /\b(npm\s+(i|install|ci)|pnpm\s+(i|install|add)|yarn(\s+add|\s+ins
|
|
|
63
63
|
var SEARCH = /\b(grep|rg|ag|ack|find|fd|locate)\b/;
|
|
64
64
|
var READ = /^\s*(cat|head|tail|less|more|bat|wc|stat|jq)\b/;
|
|
65
65
|
function argv0Of(command) {
|
|
66
|
-
const
|
|
66
|
+
const segments = command.split(/\s*(?:&&|\|\||;|\|)\s*/);
|
|
67
|
+
const first = (segments.find((s) => !/^\s*(\[|test\b|!)/.test(s)) ?? segments[0] ?? "").replace(/^\s*!\s*/, "");
|
|
67
68
|
const toks = first.trim().split(/\s+/).map((t) => t.replace(/^[({]+/, "")).filter(Boolean);
|
|
68
69
|
let i = 0;
|
|
69
70
|
while (i < toks.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(toks[i])) i++;
|
|
@@ -1204,6 +1205,8 @@ var Theatre = class {
|
|
|
1204
1205
|
signal = "ok";
|
|
1205
1206
|
/** The most recent Claude Code session that spoke to this stage; what a prompt from the stage resumes. */
|
|
1206
1207
|
lastSessionId = null;
|
|
1208
|
+
/** The most recent cwd of a Claude Code session that is NOT this repo (so an empty stage can point the person there). */
|
|
1209
|
+
elsewhere = null;
|
|
1207
1210
|
timer = null;
|
|
1208
1211
|
port = 0;
|
|
1209
1212
|
routes() {
|
|
@@ -1216,7 +1219,13 @@ var Theatre = class {
|
|
|
1216
1219
|
return c.body(null, 204);
|
|
1217
1220
|
}
|
|
1218
1221
|
const cwd = typeof payload?.cwd === "string" ? payload.cwd : "";
|
|
1219
|
-
if (cwd && !isWithin(cwd, this.opts.cwd))
|
|
1222
|
+
if (cwd && !isWithin(cwd, this.opts.cwd)) {
|
|
1223
|
+
if (this.elsewhere !== cwd) {
|
|
1224
|
+
this.elsewhere = cwd;
|
|
1225
|
+
this.broadcast({ type: "elsewhere", cwd });
|
|
1226
|
+
}
|
|
1227
|
+
return c.body(null, 204);
|
|
1228
|
+
}
|
|
1220
1229
|
const sid = payload.session_id;
|
|
1221
1230
|
if (typeof sid === "string" && sid) this.lastSessionId = sid;
|
|
1222
1231
|
const { events } = hookPayloadToEvents(payload, { home: homedir2() });
|
|
@@ -1229,7 +1238,7 @@ var Theatre = class {
|
|
|
1229
1238
|
for (const [k, v] of Object.entries(cors)) c.res.headers.set(k, v);
|
|
1230
1239
|
});
|
|
1231
1240
|
a.options("/api/*", (c) => c.body(null, 204, cors));
|
|
1232
|
-
a.get("/api/scene", (c) => c.json(
|
|
1241
|
+
a.get("/api/scene", (c) => c.json(this.scene()));
|
|
1233
1242
|
a.get("/api/sessions", (c) => c.json(listSessions(this.opts.cwd).map((s) => ({ id: s.id, title: s.title, mtime: s.mtime, bytes: s.bytes }))));
|
|
1234
1243
|
a.get("/api/replay/:id", (c) => {
|
|
1235
1244
|
const id = c.req.param("id");
|
|
@@ -1315,7 +1324,26 @@ var Theatre = class {
|
|
|
1315
1324
|
this.port = typeof addr === "object" && addr ? addr.port : port;
|
|
1316
1325
|
const home = this.opts.home ?? join2(homedir2(), ".agent-theatre");
|
|
1317
1326
|
mkdirSync(home, { recursive: true });
|
|
1327
|
+
try {
|
|
1328
|
+
const old = parseInt(readFileSync2(join2(home, "port"), "utf8").trim(), 10);
|
|
1329
|
+
if (old > 0 && old !== this.port && portAlive(old) && !existsSync(join2(home, "ports", String(old)))) {
|
|
1330
|
+
mkdirSync(join2(home, "ports"), { recursive: true });
|
|
1331
|
+
writeFileSync(join2(home, "ports", String(old)), "(started by an older build)");
|
|
1332
|
+
}
|
|
1333
|
+
} catch {
|
|
1334
|
+
}
|
|
1318
1335
|
writeFileSync(join2(home, "port"), String(this.port));
|
|
1336
|
+
mkdirSync(join2(home, "ports"), { recursive: true });
|
|
1337
|
+
for (const f of readdirSync2(join2(home, "ports"))) {
|
|
1338
|
+
const n = parseInt(f, 10);
|
|
1339
|
+
if (n > 0 && n !== this.port && !portAlive(n)) {
|
|
1340
|
+
try {
|
|
1341
|
+
rmSync(join2(home, "ports", f));
|
|
1342
|
+
} catch {
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
writeFileSync(join2(home, "ports", String(this.port)), this.opts.cwd);
|
|
1319
1347
|
const lostAfter = this.opts.lostAfterMs ?? 9e4;
|
|
1320
1348
|
this.timer = setInterval(() => {
|
|
1321
1349
|
const l = this.live;
|
|
@@ -1334,13 +1362,17 @@ var Theatre = class {
|
|
|
1334
1362
|
if (readFileSync2(join2(home, "port"), "utf8").trim() === String(this.port)) rmSync(join2(home, "port"));
|
|
1335
1363
|
} catch {
|
|
1336
1364
|
}
|
|
1365
|
+
try {
|
|
1366
|
+
rmSync(join2(home, "ports", String(this.port)));
|
|
1367
|
+
} catch {
|
|
1368
|
+
}
|
|
1337
1369
|
for (const ws of this.wss?.clients ?? []) ws.close();
|
|
1338
1370
|
this.wss?.close();
|
|
1339
1371
|
await new Promise((res) => this.server ? this.server.close(() => res()) : res());
|
|
1340
1372
|
}
|
|
1341
1373
|
/** The live scene as /api/scene returns it. */
|
|
1342
1374
|
scene() {
|
|
1343
|
-
return { mode: "live", repoId: this.opts.repoId, world: this.live.world, events: this.live.events, beats: this.live.beats, signal: this.signal };
|
|
1375
|
+
return { mode: "live", repoId: this.opts.repoId, cwd: this.opts.cwd, elsewhere: this.elsewhere, world: this.live.world, events: this.live.events, beats: this.live.beats, signal: this.signal };
|
|
1344
1376
|
}
|
|
1345
1377
|
get url() {
|
|
1346
1378
|
return `http://127.0.0.1:${this.port}/`;
|
|
@@ -1377,6 +1409,12 @@ function claudeBinary() {
|
|
|
1377
1409
|
}
|
|
1378
1410
|
return null;
|
|
1379
1411
|
}
|
|
1412
|
+
function portAlive(port) {
|
|
1413
|
+
const r = spawnSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], { encoding: "utf8", timeout: 2e3 });
|
|
1414
|
+
if (r.status === 0 && r.stdout.trim()) return true;
|
|
1415
|
+
if (r.error) return true;
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1380
1418
|
|
|
1381
1419
|
// src/scan.ts
|
|
1382
1420
|
import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
@@ -1511,20 +1549,22 @@ function hasOurHooks(existing) {
|
|
|
1511
1549
|
// src/hooks/script.ts
|
|
1512
1550
|
var HOOK_SCRIPT = `#!/usr/bin/env node
|
|
1513
1551
|
// Agent Theatre hook \u2014 forwards Claude Code events to the local theatre. Never blocks the agent.
|
|
1514
|
-
import { readFileSync } from "node:fs";
|
|
1552
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
1515
1553
|
import { homedir } from "node:os";
|
|
1516
1554
|
import { join } from "node:path";
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
if (
|
|
1555
|
+
// Every running stage registers itself in ports/<port>; the event goes to all of them (browser, VS Code, a second repo\u2026).
|
|
1556
|
+
const home = process.env.AGENT_THEATRE_HOME || join(homedir(), ".agent-theatre");
|
|
1557
|
+
const ports = new Set();
|
|
1558
|
+
try { for (const f of readdirSync(join(home, "ports"))) { const n = parseInt(f, 10); if (n > 0) ports.add(n); } } catch { /* no registry yet */ }
|
|
1559
|
+
try { const n = parseInt(readFileSync(join(home, "port"), "utf8").trim(), 10); if (n > 0) ports.add(n); } catch { /* no port file */ }
|
|
1560
|
+
if (!ports.size) process.exit(0);
|
|
1521
1561
|
let body = "";
|
|
1522
1562
|
process.stdin.setEncoding("utf8");
|
|
1523
1563
|
process.stdin.on("data", (c) => { body += c; if (body.length > 2_000_000) body = body.slice(0, 2_000_000); });
|
|
1524
1564
|
process.stdin.on("end", async () => {
|
|
1525
1565
|
try {
|
|
1526
|
-
await fetch(\`http://127.0.0.1:\${port}/hook\`, { method: "POST", headers: { "content-type": "application/json" }, body, signal: AbortSignal.timeout(300) });
|
|
1527
|
-
} catch { /* theatre not running or slow: that is fine */ }
|
|
1566
|
+
await Promise.allSettled([...ports].map((port) => fetch(\`http://127.0.0.1:\${port}/hook\`, { method: "POST", headers: { "content-type": "application/json" }, body, signal: AbortSignal.timeout(300) })));
|
|
1567
|
+
} catch { /* a theatre not running or slow: that is fine */ }
|
|
1528
1568
|
process.exit(0);
|
|
1529
1569
|
});
|
|
1530
1570
|
setTimeout(() => process.exit(0), 600).unref();
|