agent-dag 1.34.1 → 1.34.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.
@@ -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-JwmQLh6n.js"></script>
43
+ <script type="module" crossorigin src="/assets/index-CLdlWxJO.js"></script>
44
44
  <link rel="stylesheet" crossorigin href="/assets/index-CV3aF__6.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.34.1",
3
+ "version": "1.34.2",
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": {
@@ -638,7 +638,13 @@ function maybeResolveContext(payload) {
638
638
  const CODEX_HOME = process.env.CODEX_HOME
639
639
  ? resolve(process.env.CODEX_HOME)
640
640
  : join(homedir(), ".codex");
641
- const CODEX_SESSIONS_DIR = join(CODEX_HOME, "sessions");
641
+ // Exported because bin/deck.js prints this path in the boot banner, and it used
642
+ // to build its own `join(homedir(), ".codex", "sessions")` for the purpose —
643
+ // which ignored CODEX_HOME and so named a directory that does not exist on any
644
+ // machine that sets it. Handing out the binding the watcher itself reads, rather
645
+ // than a second computation of the same rule, is what makes the printed path and
646
+ // the tailed path unable to disagree.
647
+ export const CODEX_SESSIONS_DIR = join(CODEX_HOME, "sessions");
642
648
  const codexRolloutPathBySid = new Map();
643
649
  const lastCodexUsageReadAt = new Map();
644
650
  const pendingCodexUsageReads = new Set();
@@ -13,7 +13,8 @@
13
13
  // schedule and writes what it got; reading that file costs nothing and
14
14
  // spends none of the budget. Used whenever it holds a recent enough row.
15
15
  // 2. The OAuth usage API directly, with the token from
16
- // ~/.claude/.credentials.json. Exact and instant. Mechanism
16
+ // .credentials.json inside the Claude config dir — $CLAUDE_CONFIG_DIR when
17
+ // it is set, ~/.claude otherwise. Exact and instant. Mechanism
17
18
  // reverse-engineered from steipete/CodexBar.
18
19
  // 3. `claude --print /usage`, parsed. Used when there is no readable token —
19
20
  // notably on macOS, where Claude Code keeps credentials in the Keychain
@@ -27,6 +28,7 @@
27
28
  // 2 and 3 are rate-floored (SELF_POLL_MS) and gated behind the same 429
28
29
  // cooldown; 1 is not, because it is a local file read.
29
30
  import { activeAccountUsage, requestCollection } from "./claude-accounts.mjs";
31
+ import { claudeConfigDir } from "./claude-dir.mjs";
30
32
  import { run } from "./exec.mjs";
31
33
  import { existsSync } from "node:fs";
32
34
  import { readFile } from "node:fs/promises";
@@ -34,7 +36,6 @@ import { join, posix as posixPath, win32 as winPath } from "node:path";
34
36
  import { homedir } from "node:os";
35
37
  import { PRODUCT } from "./brand.mjs";
36
38
 
37
- const CREDS_PATH = join(homedir(), ".claude", ".credentials.json");
38
39
  const USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
39
40
  const BETA_HEADER = "oauth-2025-04-20";
40
41
  const WIN_5H_SEC = 18000;
@@ -43,9 +44,34 @@ const WIN_7D_SEC = 604800;
43
44
  // 429 cooldown gate — after a rate-limit, skip the API until this passes.
44
45
  let _rateLimitedUntil = 0;
45
46
 
47
+ /**
48
+ * Where Claude Code keeps the OAuth credentials this module borrows a token
49
+ * from.
50
+ *
51
+ * It is `.credentials.json` inside the Claude config dir, and that dir moves:
52
+ * CLAUDE_CONFIG_DIR replaces ~/.claude wholesale rather than overlaying it, so
53
+ * on a machine where it is set there is no ~/.claude to read at all. Hardcoding
54
+ * ~/.claude here did not fail loudly — it made readOAuthToken() return null
55
+ * forever, which reads exactly like "this machine keeps its credentials in the
56
+ * Keychain", and the quota chain quietly fell through to source 3 on every poll
57
+ * it was allowed to make. See src/server/claude-dir.mjs, which owns the rule
58
+ * and is the only place it is spelled.
59
+ *
60
+ * Resolved per call rather than frozen into a module-level constant, for the
61
+ * same reason claudeConfigDir() is a function: a constant captured at import
62
+ * time is a value nothing can observe or correct afterwards, and this module is
63
+ * imported lazily by the /api/quota route rather than at a point in startup
64
+ * anyone here controls.
65
+ *
66
+ * Exported for tests — it is the whole of the bug, and it is pure.
67
+ */
68
+ export function credentialsPath() {
69
+ return join(claudeConfigDir(), ".credentials.json");
70
+ }
71
+
46
72
  async function readOAuthToken() {
47
73
  try {
48
- const raw = await readFile(CREDS_PATH, "utf8");
74
+ const raw = await readFile(credentialsPath(), "utf8");
49
75
  const auth = JSON.parse(raw)?.claudeAiOauth;
50
76
  if (!auth?.accessToken) return null;
51
77
  // expiresAt is epoch milliseconds. If expired, the CLI fallback handles it.