agent-dag 1.35.30 → 1.35.31
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/web/assets/index-Cx_7gonm.js +77 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/server/codex-quota.mjs +10 -7
- package/src/server/cswap-install.mjs +5 -11
- package/src/server/quota.mjs +8 -9
- package/src/server/reset-label.mjs +78 -0
- package/src/server/self-update.mjs +12 -3
- package/dist/web/assets/index-DCAbQHgs.js +0 -77
package/dist/web/index.html
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
document.documentElement.setAttribute("data-theme", stored === "light" ? "light" : "dark");
|
|
41
41
|
})();
|
|
42
42
|
</script>
|
|
43
|
-
<script type="module" crossorigin src="/assets/index-
|
|
43
|
+
<script type="module" crossorigin src="/assets/index-Cx_7gonm.js"></script>
|
|
44
44
|
<link rel="stylesheet" crossorigin href="/assets/index-CIKdHXRn.css">
|
|
45
45
|
</head>
|
|
46
46
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-dag",
|
|
3
|
-
"version": "1.35.
|
|
3
|
+
"version": "1.35.31",
|
|
4
4
|
"description": "Live deck of Claude Code and Codex agents — watch tool calls, token spend and every Claude Code subagent on one calm canvas. Also available as npx ccdeck and npx agent-dag.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { CODEX_HOME } from "./codex-dir.mjs";
|
|
13
13
|
import { getCodexAuth, forceCodexRefresh, isCredentialHost } from "./codex-auth.mjs";
|
|
14
14
|
import { PRODUCT } from "./brand.mjs";
|
|
15
|
+
import { resetLabel } from "./reset-label.mjs";
|
|
15
16
|
|
|
16
17
|
// Resolved by codex-dir.mjs rather than here. This file used to spell it
|
|
17
18
|
// `process.env.CODEX_HOME ?? join(homedir(), ".codex")`, which keeps an empty
|
|
@@ -76,13 +77,15 @@ function resetAt(o) {
|
|
|
76
77
|
return num(o?.resets_at) ?? num(o?.resetsAt) ?? num(o?.reset_at) ?? null;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
/** "Jun 18, 4:09pm" — matches the Claude quota formatting so both read alike.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
/** "Jun 18, 4:09pm" — matches the Claude quota formatting so both read alike.
|
|
81
|
+
*
|
|
82
|
+
* It did not, and the sentence above is why #374 called this one out: this
|
|
83
|
+
* copy passed the same options to `toLocaleString` and then stripped the comma
|
|
84
|
+
* and lower-cased the whole string, so it printed "jun 18 4:09pm" where the
|
|
85
|
+
* Claude lane one row up printed "Jun 18, 4:09pm". Swept over 2,794 instants
|
|
86
|
+
* the two disagreed on every one. Both read from reset-label.mjs now, and the
|
|
87
|
+
* claim above is true for the first time. */
|
|
88
|
+
const fmtReset = resetLabel;
|
|
86
89
|
|
|
87
90
|
// ── window classification ──────────────────────────────────────────────────
|
|
88
91
|
// Slot position is NOT the lane. Free plans return a weekly window in the
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
// entirely. It is always best-effort — the deck's core function does not
|
|
9
9
|
// depend on it, so a failure is reported and then ignored.
|
|
10
10
|
import { run, runDetached } from "./exec.mjs";
|
|
11
|
+
// The version comparator was written out here as well, identical apart from a
|
|
12
|
+
// type guard this copy lacked, and only the self-update one was under test
|
|
13
|
+
// (#374). No cycle: self-update.mjs imports node:* and ./exec.mjs, which this
|
|
14
|
+
// file already imports itself.
|
|
15
|
+
import { isOlder } from "./self-update.mjs";
|
|
11
16
|
import { bootstrapUv, existingBootstrappedUv } from "./uv-bootstrap.mjs";
|
|
12
17
|
import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
13
18
|
import { join, posix as posixPath, win32 as winPath } from "node:path";
|
|
@@ -268,17 +273,6 @@ async function latestOnPypi() {
|
|
|
268
273
|
}
|
|
269
274
|
}
|
|
270
275
|
|
|
271
|
-
/** Numeric-segment version compare; returns true when `a` is older than `b`. */
|
|
272
|
-
function isOlder(a, b) {
|
|
273
|
-
const seg = (v) => v.split(/[.\-+]/).map(n => parseInt(n, 10)).map(n => Number.isNaN(n) ? 0 : n);
|
|
274
|
-
const x = seg(a), y = seg(b);
|
|
275
|
-
for (let i = 0; i < Math.max(x.length, y.length); i++) {
|
|
276
|
-
const d = (x[i] ?? 0) - (y[i] ?? 0);
|
|
277
|
-
if (d !== 0) return d < 0;
|
|
278
|
-
}
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
276
|
/**
|
|
283
277
|
* Upgrade claude-swap in the background when a newer release exists.
|
|
284
278
|
*
|
package/src/server/quota.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import { readFile } from "node:fs/promises";
|
|
|
35
35
|
import { join } from "node:path";
|
|
36
36
|
import { homedir } from "node:os";
|
|
37
37
|
import { PRODUCT } from "./brand.mjs";
|
|
38
|
+
import { resetLabelIso } from "./reset-label.mjs";
|
|
38
39
|
|
|
39
40
|
const USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
40
41
|
const BETA_HEADER = "oauth-2025-04-20";
|
|
@@ -83,15 +84,13 @@ async function readOAuthToken() {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
// ISO-8601 → "Jun 19, 1:19pm" (local time, matching the CLI display format).
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}).replace(/\s+(AM|PM)/, (_, p) => p.toLowerCase());
|
|
94
|
-
}
|
|
87
|
+
//
|
|
88
|
+
// The body moved to reset-label.mjs in #374: codex-quota.mjs had a copy that
|
|
89
|
+
// claimed in its own comment to match this one and did not, so the Codex lanes
|
|
90
|
+
// and the Claude lanes printed the same instant two different ways in the same
|
|
91
|
+
// panel. This rendering is the one both surfaces use now. The alias stays so
|
|
92
|
+
// the four call sites below read the way they always have.
|
|
93
|
+
const fmtResetIso = resetLabelIso;
|
|
95
94
|
|
|
96
95
|
function isoToSec(iso) {
|
|
97
96
|
if (!iso) return null;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// When a quota window resets, written one way — for the Claude lanes and the
|
|
2
|
+
// Codex lanes both, which land in the same panel.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS MODULE EXISTS (#374). quota.mjs had `fmtResetIso` and codex-quota.mjs
|
|
5
|
+
// had `fmtReset`. They passed identical option bags to `toLocaleString` and then
|
|
6
|
+
// post-processed the result differently, and codex-quota's own doc comment said
|
|
7
|
+
// of its copy: "matches the Claude quota formatting so both read alike." It did
|
|
8
|
+
// not. Over the same instant:
|
|
9
|
+
//
|
|
10
|
+
// toLocaleString : "Jun 18, 4:09 PM"
|
|
11
|
+
// quota.mjs : "Jun 18, 4:09pm"
|
|
12
|
+
// codex-quota.mjs: "jun 18 4:09pm" ← comma stripped, month lower-cased
|
|
13
|
+
//
|
|
14
|
+
// Swept over 2,794 instants across 40 days, the two disagreed on every single
|
|
15
|
+
// one — not at a boundary, on all of them, because the difference is two
|
|
16
|
+
// unconditional string operations rather than a tier that rarely fires. Both
|
|
17
|
+
// strings surface in the same usage panel, one lane above the other.
|
|
18
|
+
//
|
|
19
|
+
// The Claude rendering is the one kept, on the comment's own terms: it is the
|
|
20
|
+
// one the other claimed to match, and it is the one that reads like a date —
|
|
21
|
+
// `Intl` capitalises the month and puts the comma in for the locale's own
|
|
22
|
+
// reasons, and stripping both is a hand edit to output that was already right.
|
|
23
|
+
// This is a FIX to what the Codex lanes print, not a neutral merge, and it is
|
|
24
|
+
// the only visible change in this consolidation.
|
|
25
|
+
//
|
|
26
|
+
// The one genuine difference between the copies was the input unit — Codex
|
|
27
|
+
// answers with a Unix timestamp in seconds and Anthropic with an ISO-8601
|
|
28
|
+
// string — so that is the wrapper below rather than a second formatter.
|
|
29
|
+
//
|
|
30
|
+
// A module of its own rather than one importing the other: quota.mjs reads
|
|
31
|
+
// Claude's OAuth credentials off disk, and having the Codex path import that to
|
|
32
|
+
// reach a date formatter would drag the whole Claude credential chain into a
|
|
33
|
+
// request that has nothing to do with it.
|
|
34
|
+
|
|
35
|
+
/** en-US on purpose, not the host locale. This string is generated on the
|
|
36
|
+
* server and shipped to a browser whose locale nobody here has asked, so a
|
|
37
|
+
* server set to de-DE would otherwise send "18. Juni, 16:09" into a panel
|
|
38
|
+
* written in English. Both copies already hardcoded en-US; it is written down
|
|
39
|
+
* here so the next reader knows it was a decision. */
|
|
40
|
+
const OPTS = { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", hour12: true };
|
|
41
|
+
|
|
42
|
+
/** The rendering itself, over a Date both entry points have already validated.
|
|
43
|
+
* The meridiem is lower-cased and its space removed — the only edit made to
|
|
44
|
+
* what `Intl` produces — because "4:09 PM" shouts in a lane label that is a
|
|
45
|
+
* few characters of chrome wide. */
|
|
46
|
+
function label(d) {
|
|
47
|
+
return d.toLocaleString("en-US", OPTS).replace(/\s+(AM|PM)/, (_, p) => p.toLowerCase());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** "Jun 18, 4:09pm" from a Unix timestamp in seconds, which is how the Codex
|
|
51
|
+
* usage endpoint spells a reset time. Null when there is no reset to name.
|
|
52
|
+
*
|
|
53
|
+
* The validity check is inherited from the ISO copy, which had one where the
|
|
54
|
+
* seconds copy did not: a finite but absurd number — the only shape that gets
|
|
55
|
+
* past the falsy guard and still fails to be a date — used to render as the
|
|
56
|
+
* literal string "invalid date" in a quota lane. Nothing observed sends one,
|
|
57
|
+
* so this is hardening rather than a fix, and it is the safer of the two
|
|
58
|
+
* behaviours in the same way `isOlder`'s type guard is. */
|
|
59
|
+
export function resetLabel(unixSec) {
|
|
60
|
+
if (!unixSec) return null;
|
|
61
|
+
const d = new Date(unixSec * 1000);
|
|
62
|
+
if (isNaN(d.getTime())) return null;
|
|
63
|
+
return label(d);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The same label from an ISO-8601 instant, which is how Anthropic's usage
|
|
67
|
+
* endpoint spells a reset time. Invalid and absent both answer null, so a
|
|
68
|
+
* malformed field renders as no reset rather than "Invalid Date".
|
|
69
|
+
*
|
|
70
|
+
* Not written as `resetLabel(d.getTime() / 1000)`: that would send an instant
|
|
71
|
+
* at the Unix epoch through the falsy guard above and answer null for a date
|
|
72
|
+
* this function has already established is valid. */
|
|
73
|
+
export function resetLabelIso(iso) {
|
|
74
|
+
if (!iso) return null;
|
|
75
|
+
const d = new Date(iso);
|
|
76
|
+
if (isNaN(d.getTime())) return null;
|
|
77
|
+
return label(d);
|
|
78
|
+
}
|
|
@@ -69,9 +69,18 @@ const _inflight = new Map();
|
|
|
69
69
|
|
|
70
70
|
// ── version comparison ───────────────────────────────────────────────────────
|
|
71
71
|
|
|
72
|
-
/** True when `a` sorts before `b`. Numeric-segment compare
|
|
73
|
-
*
|
|
74
|
-
*
|
|
72
|
+
/** True when `a` sorts before `b`. Numeric-segment compare — non-numeric
|
|
73
|
+
* segments count as 0, missing segments pad with 0, so "1.30" < "1.30.1" and
|
|
74
|
+
* "1.9.0" < "1.10.0".
|
|
75
|
+
*
|
|
76
|
+
* cswap-install.mjs had this written out a second time, without the type guard
|
|
77
|
+
* below, and imports it from here now (#374). The two bodies were identical:
|
|
78
|
+
* swept over 271,441 version-string pairs they disagreed on none, and the
|
|
79
|
+
* guard was the whole difference — `isOlder(null, "1.0.0")` answers false here
|
|
80
|
+
* and threw a TypeError there. Nothing could reach that call with a non-string
|
|
81
|
+
* (both arguments are behind `typeof v === "string"` checks at the one call
|
|
82
|
+
* site), so this is the copy with a test behind it absorbing the one without,
|
|
83
|
+
* not a bug fix. */
|
|
75
84
|
export function isOlder(a, b) {
|
|
76
85
|
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
77
86
|
const seg = (v) => v.split(/[.\-+]/).map(n => parseInt(n, 10)).map(n => Number.isNaN(n) ? 0 : n);
|