agent-dag 1.30.8 → 1.31.0
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/README.md +16 -0
- package/bin/agent-dag.js +94 -298
- package/bin/deck.js +368 -0
- package/dist/web/assets/index-BrkDn1XS.css +1 -0
- package/dist/web/assets/index-CULN68BP.js +66 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/server/index.mjs +37 -2
- package/src/server/quota.mjs +21 -2
- package/dist/web/assets/index-1i-Vckvo.js +0 -66
- package/dist/web/assets/index-CEAcuttN.css +0 -1
package/dist/web/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>agents-deck</title>
|
|
7
7
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='84' font-size='84'%3E%E2%97%89%3C/text%3E%3C/svg%3E" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CULN68BP.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BrkDn1XS.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-dag",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "Live deck of Claude Code and Codex agents — watch parallel subagents fork, call tools, and return on one calm canvas. Also available as npx ccdeck and npx agent-dag.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/server/index.mjs
CHANGED
|
@@ -1015,11 +1015,32 @@ function handleSse(req, res) {
|
|
|
1015
1015
|
});
|
|
1016
1016
|
}
|
|
1017
1017
|
|
|
1018
|
+
// True only when a supervisor is listening AND the event log is being written.
|
|
1019
|
+
// Without persistence a restart wipes the canvas irrecoverably — replayLog has
|
|
1020
|
+
// no file to read — so the deck must not offer to do it.
|
|
1021
|
+
let _canRestart = false;
|
|
1022
|
+
|
|
1018
1023
|
async function handleVersion(_req, res) {
|
|
1019
1024
|
const { versionReport } = await import(
|
|
1020
1025
|
pathToFileURL(join(PKG_ROOT, "src/server/self-update.mjs")).href
|
|
1021
1026
|
);
|
|
1022
|
-
|
|
1027
|
+
const report = await versionReport({ running: RUNNING_VERSION, pkgRoot: PKG_ROOT });
|
|
1028
|
+
send(res, 200, { ...report, canRestart: _canRestart });
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Restart is a two-party act: this half answers before it stops listening, so
|
|
1032
|
+
// the caller learns it was accepted rather than losing the socket mid-reply.
|
|
1033
|
+
function handleRestart(_req, res) {
|
|
1034
|
+
if (!_onRestart) return send(res, 501, { ok: false, reason: "unsupervised" });
|
|
1035
|
+
// Enforced here and not only in the UI. Under --no-persist a restart destroys
|
|
1036
|
+
// the whole canvas — replayLog has no file to read — and a destructive act
|
|
1037
|
+
// must not be prevented by a hidden button alone.
|
|
1038
|
+
if (!_canRestart) return send(res, 409, { ok: false, reason: "no_persist" });
|
|
1039
|
+
if (_restarting) return send(res, 202, { ok: true, already: true });
|
|
1040
|
+
_restarting = true;
|
|
1041
|
+
send(res, 200, { ok: true });
|
|
1042
|
+
// Let the response flush before the listener goes away.
|
|
1043
|
+
setTimeout(() => { try { _onRestart(); } catch { _restarting = false; } }, 120).unref();
|
|
1023
1044
|
}
|
|
1024
1045
|
|
|
1025
1046
|
async function handleQuota(req, res) {
|
|
@@ -1194,7 +1215,20 @@ async function tryListen(server, port, host) {
|
|
|
1194
1215
|
});
|
|
1195
1216
|
}
|
|
1196
1217
|
|
|
1197
|
-
|
|
1218
|
+
// Set from startServer's options. The server cannot restart itself — the
|
|
1219
|
+
// process lifecycle belongs to the supervisor in bin/agent-dag.js, which is the
|
|
1220
|
+
// only thing that can bring a replacement up on the same port without racing
|
|
1221
|
+
// the dying listener. Absent (running the module directly, or under an older
|
|
1222
|
+
// launcher), /api/restart answers 501 and the UI hides the control rather than
|
|
1223
|
+
// offering a button that does nothing.
|
|
1224
|
+
let _onRestart = null;
|
|
1225
|
+
// A restart is in flight. Several browser tabs watching the same deck will each
|
|
1226
|
+
// ask; the second ask must not re-enter the shutdown.
|
|
1227
|
+
let _restarting = false;
|
|
1228
|
+
|
|
1229
|
+
export async function startServer({ port = 4317, host = "127.0.0.1", persist = null, portRange = [4318, 4400], workspace = "", codex = true, onRestart = null } = {}) {
|
|
1230
|
+
_onRestart = typeof onRestart === "function" ? onRestart : null;
|
|
1231
|
+
_canRestart = _onRestart != null && persist != null;
|
|
1198
1232
|
const removed = await sweepStaleDiscovery();
|
|
1199
1233
|
if (removed > 0) console.log(` swept ${removed} stale discovery file(s)`);
|
|
1200
1234
|
if (persist) {
|
|
@@ -1223,6 +1257,7 @@ export async function startServer({ port = 4317, host = "127.0.0.1", persist = n
|
|
|
1223
1257
|
if (req.method === "GET" && url.pathname === "/api/health") return handleHealth(req, res);
|
|
1224
1258
|
if (req.method === "GET" && url.pathname === "/events") return handleSse(req, res);
|
|
1225
1259
|
if (req.method === "GET" && url.pathname === "/api/version") return guard(handleVersion(req, res), res);
|
|
1260
|
+
if (req.method === "POST" && url.pathname === "/api/restart") return handleRestart(req, res);
|
|
1226
1261
|
if (req.method === "GET" && url.pathname === "/api/quota") return guard(handleQuota(req, res), res);
|
|
1227
1262
|
if (req.method === "GET" && url.pathname === "/api/codex-usage") return guard(handleCodexUsage(req, res), res);
|
|
1228
1263
|
if (req.method === "GET" && url.pathname === "/api/codex-quota") return guard(handleCodexQuota(req, res), res);
|
package/src/server/quota.mjs
CHANGED
|
@@ -357,6 +357,20 @@ async function storeQuota() {
|
|
|
357
357
|
const REREAD_TRIES = 3;
|
|
358
358
|
const REREAD_GAP_MS = 800;
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Whichever of two readings was collected later, regardless of source.
|
|
362
|
+
*
|
|
363
|
+
* Quota numbers only ever move forward in time, so "newer" is the only ranking
|
|
364
|
+
* that makes sense between a store row and something we fetched ourselves. A
|
|
365
|
+
* five-hour window can also reset between the two, which makes an older reading
|
|
366
|
+
* not merely stale but wrong — 23% from before the reset, 3% after it.
|
|
367
|
+
*/
|
|
368
|
+
export function freshest(a, b) {
|
|
369
|
+
if (!a) return b ?? null;
|
|
370
|
+
if (!b) return a;
|
|
371
|
+
return (b.fetchedAt ?? 0) > (a.fetchedAt ?? 0) ? b : a;
|
|
372
|
+
}
|
|
373
|
+
|
|
360
374
|
/** Ask for a collection, then watch the store for the result. */
|
|
361
375
|
async function nudgeAndReread(previous) {
|
|
362
376
|
let asked = false;
|
|
@@ -426,8 +440,13 @@ async function _doFetch(now, force = false) {
|
|
|
426
440
|
// Nothing usable in the store. Everything below spends the user's budget, so
|
|
427
441
|
// it happens on a floor, and not at all while a 429 cooldown is running.
|
|
428
442
|
if (!maySelfPoll({ now, force, lastSelfPollAt: _lastSelfPollAt, rateLimitedUntil: _rateLimitedUntil })) {
|
|
429
|
-
// A stale row still beats an empty panel, and says how stale it is
|
|
430
|
-
|
|
443
|
+
// A stale row still beats an empty panel, and says how stale it is — but
|
|
444
|
+
// it must be the freshest thing we hold, not just the store. Preferring
|
|
445
|
+
// the store here threw away readings we had already paid for: after a boot
|
|
446
|
+
// that fell through to the CLI, the panel showed 3% (fetched seconds ago)
|
|
447
|
+
// and then reverted to 23% (from a 48-minute-old store row) on the very
|
|
448
|
+
// next poll, because the store had not moved.
|
|
449
|
+
const held = freshest(store, _lastGood);
|
|
431
450
|
if (held) {
|
|
432
451
|
const result = { ...held, stale: true };
|
|
433
452
|
_cache = result; _cacheAt = now;
|