agent-dag 3.22.0 → 3.22.3

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.
Files changed (70) hide show
  1. package/README.md +6 -477
  2. package/package.json +14 -48
  3. package/shim.js +107 -0
  4. package/LICENSE +0 -661
  5. package/LICENSING.md +0 -82
  6. package/THIRD_PARTY_NOTICES.md +0 -395
  7. package/bin/agent-dag.js +0 -626
  8. package/bin/deck.js +0 -1805
  9. package/dist/web/assets/index-3FWd7g_W.css +0 -1
  10. package/dist/web/assets/index-BOwtoP02.js +0 -266
  11. package/dist/web/index.html +0 -49
  12. package/hook/hook.js +0 -542
  13. package/release-notes.json +0 -392
  14. package/src/server/activity.mjs +0 -52
  15. package/src/server/agent-activity.mjs +0 -522
  16. package/src/server/args.mjs +0 -183
  17. package/src/server/auto-update.mjs +0 -79
  18. package/src/server/block-notify.mjs +0 -173
  19. package/src/server/boot-deadline.mjs +0 -127
  20. package/src/server/brand.mjs +0 -16
  21. package/src/server/browser-history.mjs +0 -497
  22. package/src/server/browser-presence.mjs +0 -211
  23. package/src/server/browser-profiles.mjs +0 -279
  24. package/src/server/browser-react.mjs +0 -284
  25. package/src/server/browser-watch-store.mjs +0 -350
  26. package/src/server/browser-watch.mjs +0 -905
  27. package/src/server/ccusage.mjs +0 -1168
  28. package/src/server/claude-accounts.mjs +0 -951
  29. package/src/server/claude-dir.mjs +0 -213
  30. package/src/server/codex-auth.mjs +0 -388
  31. package/src/server/codex-dir.mjs +0 -171
  32. package/src/server/codex-quota.mjs +0 -449
  33. package/src/server/codex-usage.mjs +0 -512
  34. package/src/server/cswap-admin.mjs +0 -1562
  35. package/src/server/cswap-auto.mjs +0 -658
  36. package/src/server/cswap-install.mjs +0 -641
  37. package/src/server/deck-home.mjs +0 -243
  38. package/src/server/deck-prefs.mjs +0 -301
  39. package/src/server/deck-probe.mjs +0 -111
  40. package/src/server/detach.mjs +0 -244
  41. package/src/server/exec.mjs +0 -996
  42. package/src/server/global-install.mjs +0 -67
  43. package/src/server/hwmonitor.mjs +0 -56
  44. package/src/server/index.mjs +0 -6043
  45. package/src/server/installer.mjs +0 -912
  46. package/src/server/invoked-as.mjs +0 -144
  47. package/src/server/lan-about.mjs +0 -119
  48. package/src/server/lan-engine.mjs +0 -952
  49. package/src/server/lan-reach.mjs +0 -256
  50. package/src/server/lan-socket.mjs +0 -682
  51. package/src/server/lan-sync.mjs +0 -941
  52. package/src/server/lhm-parse.mjs +0 -91
  53. package/src/server/log-tail.mjs +0 -139
  54. package/src/server/log-writer.mjs +0 -322
  55. package/src/server/login-service.mjs +0 -473
  56. package/src/server/macmon.mjs +0 -310
  57. package/src/server/npx.mjs +0 -264
  58. package/src/server/open-url.mjs +0 -242
  59. package/src/server/presence.mjs +0 -40
  60. package/src/server/quota.mjs +0 -792
  61. package/src/server/relay-guard.mjs +0 -507
  62. package/src/server/reset-label.mjs +0 -78
  63. package/src/server/retire-sound-hook.mjs +0 -349
  64. package/src/server/running-deck.mjs +0 -234
  65. package/src/server/self-update.mjs +0 -1380
  66. package/src/server/stop-deck.mjs +0 -171
  67. package/src/server/supervisor.mjs +0 -392
  68. package/src/server/system-metrics.mjs +0 -1825
  69. package/src/server/term.mjs +0 -686
  70. package/src/server/uv-bootstrap.mjs +0 -337
package/README.md CHANGED
@@ -1,482 +1,11 @@
1
- <div align="center">
1
+ # agent-dag
2
2
 
3
- # ccdeck — a live dashboard for Claude Code and Codex
4
-
5
- **Know which agent is waiting on you, and for how long.**
6
-
7
- **ccdeck keeps them in one queue** — every session stopped on a human, longest wait first, and the count in the topbar is one click to the oldest. That queue is Claude Code's, because Codex emits no such signal; the canvas under it is both, with every Claude Code subagent on a node of its own.
8
-
9
- [![npm](https://img.shields.io/npm/v/ccdeck?color=cb3837&logo=npm&logoColor=white)](https://www.npmjs.com/package/ccdeck)
10
- [![agents-deck downloads](https://img.shields.io/npm/dm/agents-deck?color=blue&label=agents-deck%20downloads)](https://www.npmjs.com/package/agents-deck)
11
- [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
12
- [![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen?logo=node.js&logoColor=white)](https://nodejs.org)
13
- [![macOS · Linux · Windows](https://img.shields.io/badge/macOS%20·%20Linux%20·%20Windows-supported-6aa9f7)](#requirements)
14
-
15
- ```bash
16
- npx ccdeck
17
- ```
18
-
19
- [![ccdeck — live agent DAG](assets/canvas.png)](assets/canvas.png)
20
-
21
- *A generated session, drawn by the deck itself — see `assets/canvas-demo.mjs`. Click through for full size.*
22
-
23
- who is blocked on you · tool calls · one canvas · cost · quota · local · no telemetry
24
-
25
- [What you get](#what-you-get) · [Quick start](#quick-start) · [How it works](#how-it-works) · [What it touches](#what-it-touches) · [Accounts](#accounts) · [Local network](#local-network) · [Options](#options) · [FAQ](#questions-people-ask)
26
-
27
-
28
- </div>
29
-
30
- ---
31
-
32
- ## Why
33
-
34
- Four agents running, and the machine has been quiet for twenty minutes. One of them stopped to ask something and you did not see it go by. From the outside every terminal tab looks the same — the one that is working and the one that has been holding a permission prompt since the coffee — so you find it by clicking through them, and the agent that was closest to finished is the one that has been waiting longest.
35
-
36
- ccdeck answers that in one place: every session stopped on a human is at the top of the sidebar with the wait beside it, longest first, and the count in the topbar goes straight to the oldest one. The question stops being *which tab* and becomes *this one*.
37
-
38
- That is the sharp end of a wider problem. An agent session is a tree, but a terminal shows it as a scroll: five subagents working in parallel arrive as one interleaved column of text, and the questions you actually have — *what is running right now, what did that subagent do, which one is stuck, what is this costing* — are the ones the scroll answers worst.
39
-
40
- ccdeck draws the tree instead. It is local and needs no configuration: it registers a hook, listens, and paints.
41
-
42
- ## What you get
43
-
44
- One canvas. No tabs. No kanban.
45
-
46
- The deck opens on these eight pictures the first time it runs — they are the whole tour, and `Take the tour` on an empty canvas brings them back.
47
-
48
- <table>
49
- <tr>
50
- <td><img src="assets/guide/welcome-1.svg" width="440" alt="Sessions waiting on you rise to the top, longest wait first."></td>
51
- <td><img src="assets/guide/welcome-2.svg" width="440" alt="Every agent and subagent is a node. Tool calls light up as they run."></td>
52
- </tr>
53
- <tr>
54
- <td align="center"><sub>Sessions waiting on you rise to the top, longest wait first.</sub></td>
55
- <td align="center"><sub>Every agent and subagent is a node. Tool calls light up as they run.</sub></td>
56
- </tr>
57
- <tr>
58
- <td><img src="assets/guide/welcome-3.svg" width="440" alt="Click any node: its prompt, every tool call, tokens and timing."></td>
59
- <td><img src="assets/guide/welcome-4.svg" width="440" alt="What each session costs, and how much quota is left."></td>
60
- </tr>
61
- <tr>
62
- <td align="center"><sub>Click any node: its prompt, every tool call, tokens and timing.</sub></td>
63
- <td align="center"><sub>What each session costs, and how much quota is left.</sub></td>
64
- </tr>
65
- <tr>
66
- <td><img src="assets/guide/welcome-5.svg" width="440" alt="Cores, memory and heat while the agents run, and what is hogging them."></td>
67
- <td><img src="assets/guide/welcome-6.svg" width="440" alt="Several Claude accounts: switch, add one, share one to another machine."></td>
68
- </tr>
69
- <tr>
70
- <td align="center"><sub>Cores, memory and heat while the agents run, and what is hogging them.</sub></td>
71
- <td align="center"><sub>Several Claude accounts: switch, add one, share one to another machine.</sub></td>
72
- </tr>
73
- <tr>
74
- <td><img src="assets/guide/welcome-7.svg" width="440" alt="Your machines repair each other's expired logins over the local network."></td>
75
- <td><img src="assets/guide/welcome-8.svg" width="440" alt="Run claude or codex in any folder. It shows up here on its own."></td>
76
- </tr>
77
- <tr>
78
- <td align="center"><sub>Your machines repair each other's expired logins over the local network.</sub></td>
79
- <td align="center"><sub>Run <code>claude</code> or <code>codex</code> in any folder. It shows up here on its own.</sub></td>
80
- </tr>
81
- </table>
82
-
83
- | | |
84
- |---|---|
85
- | **Blocked on you** | A permission prompt, or a finished turn waiting for your next instruction, sorts that session to the top of the sidebar with how long it has been stuck — longest wait first, so the oldest block is the first row. A permission prompt also puts a count in the topbar that jumps straight to it. Claude Code only — Codex emits no such signal. |
86
- | **Live DAG** | Nodes are agents, edges are spawns and tool calls. In-flight edges animate, settled ones fade. |
87
- | **Both providers, one canvas** | Claude Code through hooks, Codex through its rollout log. The model chip (`Opus 5`, `GPT-5.5`) tells them apart. |
88
- | **Cost and quota, live** | Spend per model and per session, plus Claude and Codex quota windows as they refill. |
89
- | **Click to inspect** | Any node opens its prompt, tool calls, token usage and timing. |
90
- | **Survives restarts** | Events are appended to `~/.claude/agent-dag/events.jsonl` and replayed on open. |
91
- | **Accounts without a terminal** | Sign a new Claude account in, move one or your whole set to another machine, rename, reorder, remove — from the panel. |
92
- | **Logins that repair each other** | A Claude login that expires on one of your machines is copied back from another machine on the same network that still has it — see [Local network](#local-network). |
93
- | **Knows when it is stale** | Node caches modules at startup, so an upgraded-while-running deck keeps executing old code. This one says so, and can restart itself when nothing is running. |
94
- | **Workspace scoping** | `--scope` for the current directory, `--workspace <path>` for any subtree — for Claude Code and Codex alike. |
95
-
96
- ## Quick start
97
-
98
- ```bash
99
- npx ccdeck # or: npx agents-deck · npx agent-dag — same deck
100
- ```
101
-
102
- Opens **http://127.0.0.1:4317**, shows an eight-picture tour the first time, and registers the Claude Code hook on first run. If something else already holds 4317, the deck takes a port between 4318 and 4400 instead and prints the address it ended up on — that line in the terminal is the one to trust. Start any Claude Code or Codex session and the graph fills in live.
103
-
104
- The deck keeps running after you close the terminal, and starts again when you
105
- log in. **`ccdeck --stop` is the off switch**; `Ctrl+C` only cancels a start that
106
- is still printing. `ccdeck --status` says what is running, and `--foreground`
107
- holds the terminal the way every version before 3.20 did.
108
-
109
- No config file. No account. No telemetry — nothing about your sessions is reported anywhere.
110
-
111
- **The deck cannot steer your agent.** The hook it installs is a one-way forwarder: it POSTs the event, exits `0`, and writes nothing to stdout. Those are the two channels Claude Code's hook protocol gives a hook for allowing, denying, deferring or rewriting the tool call it was told about, and this one uses neither — it has no way to answer at all. `src/web/__tests__/hook-read-only.test.ts` pins both halves, over the source and by running the real script.
112
-
113
- What the deck does write, and the short list of what does leave the machine, is in [What it touches](#what-it-touches).
114
-
115
- ## Requirements
116
-
117
- - Node.js ≥ 18 — macOS, Linux and Windows. The floor is checked on every run: CI installs the packed release on Node 18 and boots it, so the badge is a measurement rather than a claim
118
- - Claude Code CLI or OpenAI Codex CLI (or both)
119
- - Optional: [claude-swap](https://pypi.org/project/claude-swap/) for the Accounts panel; the deck can install it for you
120
- - Nothing else. On Apple Silicon the deck fetches [`macmon`](https://github.com/vladkens/macmon) itself for the temperature rows; see below.
121
-
122
- ### Temperature, per machine
123
-
124
- The machine panel shows a **Thermal** section only where the machine actually answers, and it never invents a reading — no sensor means no row.
125
-
126
- | | reads | needs |
127
- | --- | --- | --- |
128
- | Linux | `/sys/class/hwmon`, then `/sys/class/thermal/thermal_zone*` | nothing |
129
- | Windows | the `Thermal Zone Information` performance counter, then `MSAcpi_ThermalZoneTemperature`, then LibreHardwareMonitor's web server if it happens to be running | nothing — where the machine has an ACPI thermal zone. Many do not; see below |
130
- | macOS, Intel | `ioreg` for the GPU, `pmset -g therm` for throttling | nothing |
131
- | macOS, Apple Silicon | `macmon`, which the deck fetches for you | nothing |
132
-
133
- Apple Silicon is the one that needs a tool, and it is not an oversight. No command that ships with macOS prints a temperature on an M-series Mac: `powermetrics` needs root, `pmset -g therm` records nothing there, and the sensors sit behind a private API that only native code can call. [`macmon`](https://github.com/vladkens/macmon) reads them without `sudo` and covers M1 through M5.
134
-
135
- You do not have to install it. The deck downloads the published binary into `~/.agents-deck/tools/macmon` — the same place it already keeps `uv` — verifies it against the SHA-256 the GitHub release publishes, checks that it runs, and only then uses it. Not through Homebrew, because a machine without Homebrew would need Homebrew installed first, and that is a large thing to do to somebody who asked for a dashboard. It happens in the background, after the deck is already up, and never on the first run's critical path.
136
-
137
- It is skipped entirely on a machine that already answers, on an Intel Mac — the release publishes an arm64 build and only that, so the architecture is checked before anything is fetched — and on one where you have `macmon` yourself, which is looked for on PATH and on either Homebrew prefix before the download is considered. `AGENTS_DECK_NO_DOWNLOAD=1` turns it off on its own; `AGENTS_DECK_NO_INSTALL=1` turns it off along with everything else.
138
-
139
- #### Windows, and why it is often blank
140
-
141
- Windows is the platform where this most often shows nothing, and that is not a defect in the deck. Measured on a physical Windows 11 laptop — a Lenovo IdeaPad L340, Intel i5-9300H, English install, checked both as an ordinary user and as an administrator:
142
-
143
- - its firmware declares **zero** ACPI thermal zones, so the performance counter above is registered but has no instances
144
- - `MSAcpi_ThermalZoneTemperature` — which the deck does try, in the same PowerShell child, whenever the counter has no instances — answers `Not supported` **even to an administrator**
145
- - `Win32_TemperatureProbe` exists but every field reads `32768`, which is WMI's value for "unknown"
146
- - the sensors are real and actively managed — Intel Dynamic Tuning is running — but it publishes them in `root\WMI EsifDeviceInformation`, which is **Access denied** without administrator
147
-
148
- That is a class of machine, not a fault: modern Intel laptops moved thermal management into Intel DTT and stopped declaring the ACPI zones that Windows exposes to ordinary programs. There is no standard user-mode Windows API for CPU temperature — which is why HWiNFO, Core Temp and LibreHardwareMonitor all install a kernel driver, and why this deck does not.
149
-
150
- Where the counter does have instances — many desktop boards, servers, and older laptops — it is read without any privileges at all. Its path is currently English-only; see [#747](https://github.com/BarganConstantin/ccdeck/issues/747).
151
-
152
- One thing does work on the machines above, and it costs you nothing to have: if **LibreHardwareMonitor** happens to be running with its web server on, the deck reads its numbers over plain HTTP on localhost, which needs no privileges. That is a read, not a request — the deck does not install it, will not ask you to, and shows no section if it is not there. It is mentioned only so nobody is surprised to see degrees appear on a machine that had none.
153
-
154
- ## How it works
155
-
156
- Two capture paths feed one SSE stream, which feeds one canvas.
157
-
158
- **Claude Code** — on first run, ccdeck adds a hook entry to `~/.claude/settings.json` for every relevant event (or to `$CLAUDE_CONFIG_DIR/settings.json`, and every other path below moves with it, when you have that variable set):
159
-
160
- ```
161
- SessionStart · UserPromptSubmit · PreToolUse · PostToolUse · PostToolUseFailure
162
- SubagentStart · SubagentStop · Stop · SessionEnd · Notification
163
- ```
164
-
165
- Each one fires the bundled `hook.js`, which POSTs the event JSON to the running server. The hook is fire-and-forget with a 1-second timeout: if the deck is not running, your session is not slowed down and nothing fails.
166
-
167
- **OpenAI Codex** — Codex CLI hooks do not fire reliably on Windows, so nothing is installed at all. The server tails Codex's own rollout files at `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` and reconstructs the equivalent stream — session start, prompts, tool calls, token usage, model. No hook install, no trust prompt. Set `CODEX_HOME` to override the path.
168
-
169
- Quota is the one thing that is not just reading. It needs a live token, so when the one in `~/.codex/auth.json` is within 90 seconds of expiring the deck refreshes it exactly as the CLI does and writes the rotated credential back — one refresh at a time, re-reading the file inside the lock, and atomically, because OpenAI's refresh tokens are single-use and a rotation that never reaches disk costs you a `codex login`. It happens only while the page is open, and nothing else in `auth.json` is touched.
170
-
171
- ## What it touches
172
-
173
- It never steers an agent or edits your code, but it is not read-only either — besides the hook entry and its own event log, it manages the two tools it leans on, and it refreshes the Codex token it reads quota with, rewriting `~/.codex/auth.json` the way `codex` itself does. It also reads your browser's history when Browser Watch is on; that is its own section below, because it is the one thing here that is about you rather than about an agent.
174
-
175
- What does go out is short and ordinary: a ~20-byte version check against the npm registry (plus one small request to confirm a version it has not seen before), installs and daily version checks for the two tools the deck manages (claude-swap from PyPI, ccusage from npm), on an Apple Silicon Mac whose sensors stay silent one release lookup and one binary download from GitHub for `macmon`, and, while the page is open, quota reads to Anthropic and OpenAI signed with your own credentials — that is where those numbers live. `AGENTS_DECK_NO_INSTALL=1` turns off everything but the quota reads; `AGENTS_DECK_NO_DOWNLOAD=1` is the narrower version — no `uv` binary is fetched, the managed installs stay.
176
-
177
- ## Browser Watch
178
-
179
- The panel behind the eye icon answers one question: **did something drive your browser while you were away?** An agent with browser control opens pages through an API, and Chromium records that — `visits.transition` says `FROM_API` rather than `LINK` or `TYPED`. So a run of those, in a stretch when you were not touching the machine, is a finding worth showing.
180
-
181
- To answer it the deck reads the browsers' own history. That means, on each poll: it finds every Chromium profile (Chrome, Brave, Edge, Vivaldi), **copies each profile's `History` database** to a temp file — a live database cannot be opened safely — queries the visits newer than the moment this deck started, and deletes the copy. Nothing leaves the machine, and the copy exists for as long as one query takes.
182
-
183
- **The switch decides whether any of that happens in the background.** With Browser Watch off, the deck's five-minute poll reads no browser at all; with it on, the poll reads, keeps what it finds in `~/.claude/agent-dag/browser-watch/state.json`, writes a line per finding to `watch.log` beside it, and performs whatever reaction you chose. Opening the panel always reads live, on either setting — that is you looking, and it is the only way the panel can tell you what is there.
184
-
185
- Only visits **after this deck started** are ever considered. Your existing history is not swept, not archived and not shown.
186
-
187
- ## Accounts
188
-
189
- The Accounts panel reads the store [claude-swap](https://pypi.org/project/claude-swap/) keeps, and can drive it.
190
-
191
- **`+` → Sign in** runs `claude auth login`, shows you the link, takes the code your browser gives back, and hands the result to `cswap add`. The account you were using **stays active** — signing in replaces the live credentials, so the previous one is switched back the moment the new one is recorded. The code goes straight into the CLI's stdin on this machine; it is never stored, logged, or sent anywhere else.
192
-
193
- **`share`** on an account produces a `ccdeck2:…` blob to paste into another deck's **`+` → Paste a share**.
194
-
195
- **`↗`** in the panel header does the same for a set of them, which is what moving your accounts from home to work actually is. Tick the ones to send — all of them to start — and one blob carries the set. The dialog counts sign-in tokens rather than rows, and an account that cannot be exported is named rather than quietly dropped, so the number on the copy button is always the number in the blob.
196
-
197
- An import adds what is missing and leaves a working account exactly as it is. The one it does rewrite unasked is a slot claude-swap has itself quarantined as refresh-token-dead, which is what heals a machine whose login stopped working. The result names every account in the paste — imported, already here, healed, or refused — and an account it skipped can be overwritten one at a time with **update anyway**.
198
-
199
- > [!WARNING]
200
- > A share carries the **live login of every account in it, in the clear** — claude-swap's export format has no encryption, and five ticked boxes is five passwords on your clipboard. It expires ten minutes after it is made and imports refuse it after that. While it lives, treat it as those passwords: anything that can read your clipboard can read the accounts.
201
-
202
- Renaming, reordering and removing are on the same row menu. Removal takes two clicks and cannot be undone.
203
-
204
- ## Local network
205
-
206
- A Claude login expires on the machine that has not used it for a while, and stays alive on the one that has. With two or three machines that is a `claude login` a week, each time on the machine you are not sitting at. **Local network** is the last section of the Claude accounts panel, and it is that chore done for you: decks on one network find each other, pair, and a login that has expired on one is copied back from another that still has it.
207
-
208
- Four pictures, which are also the guide the section opens from `See how it works`:
209
-
210
- <table>
211
- <tr>
212
- <td><img src="assets/guide/lan-1.svg" width="440" alt="A login can expire on one machine while it still works on another."></td>
213
- <td><img src="assets/guide/lan-2.svg" width="440" alt="Turn this on, on both machines. They find each other and pair."></td>
214
- </tr>
215
- <tr>
216
- <td align="center"><sub><b>1.</b> A login can expire on one machine while it still works on another.</sub></td>
217
- <td align="center"><sub><b>2.</b> Turn this on, on both machines. They find each other and pair.</sub></td>
218
- </tr>
219
- <tr>
220
- <td><img src="assets/guide/lan-3.svg" width="440" alt="Tick which logins this machine may hand out. Nothing is shared until you do."></td>
221
- <td><img src="assets/guide/lan-4.svg" width="440" alt="An expired login is copied from a paired machine within a minute."></td>
222
- </tr>
223
- <tr>
224
- <td align="center"><sub><b>3.</b> Tick which logins this machine may hand out. Nothing is shared until you do.</sub></td>
225
- <td align="center"><sub><b>4.</b> An expired login is copied from a paired machine within a minute.</sub></td>
226
- </tr>
227
- </table>
228
-
229
- **On each machine, in order:** open the Claude accounts panel (`A`), switch **Local network** on, and in the dialog that opens tick the logins this machine may hand out. That is all — the two decks find each other over a UDP beacon on port 45317, pair on their own, and from then on every paired deck is asked once a minute for anything this deck's expired logins need.
230
-
231
- **When they do not find each other** — a VPN, a guest network, two subnets — `+` at the top of the section reaches a deck by address, or with an invite the other deck minted. An invite is the route when the machine that cannot be seen is this one.
232
-
233
- **What is shared, and with whom.** Nothing until you tick a login, and then only that login, and only with decks somebody at this machine accepted — the deck asks the machines it finds and says yes to the ones that ask, and both switches are in the same dialog if you would rather press `accept` yourself. A paired deck can fill an *expired* slot of this deck's and nothing else: it cannot overwrite a login that still works here. What crosses the wire is the same live credential a share carries, sealed to the deck it is addressed to, so treat the pairing decision as the moment that matters. Unpairing stops future rounds; a login already copied stays where it went.
234
-
235
- ## Options
3
+ This is the old name for **[ccdeck](https://www.npmjs.com/package/ccdeck)**.
236
4
 
237
5
  ```
238
- ccdeck [options]
239
-
240
- -p, --port <number> Preferred port (default: 4317; fallback: random 4318–4400)
241
- --no-open Don't open the browser automatically
242
- --foreground Hold the terminal, the way every version before 3.20
243
- did (Ctrl+C stops the deck again)
244
- --new Start a second deck even if one is already running
245
- --stop Stop the deck a bare `ccdeck` would open
246
- (--port <n> stops that one; --all stops every deck)
247
- --status What is running on this machine, and on which ports
248
- --logs What a backgrounded deck wrote where a terminal would
249
- have shown it
250
- --workspace <path> Only capture sessions whose cwd is inside <path>
251
- --scope Restrict to the current working directory
252
- --all Capture every session on this machine (default)
253
- (beside --stop: every deck, not every session)
254
- --history <path> Override the events log file
255
- (default: this platform's log directory —
256
- ~/Library/Logs/ccdeck on macOS, %LOCALAPPDATA%\ccdeck\Log
257
- on Windows, $XDG_STATE_HOME/ccdeck on Linux)
258
- --no-persist RAM-only mode — don't write or replay the log
259
- --codex Force Codex capture even if ~/.codex/ is missing
260
- --no-codex Skip Codex capture (Claude only)
261
- --claude Force Claude capture even if Claude Code wasn't found
262
- --no-claude Skip Claude entirely — no hooks, no claude-swap,
263
- no Accounts panel (Codex only)
264
- --uninstall Remove ccdeck's hooks from settings files
265
- -h, --help Show this help
266
- -v, --version Print the version and exit
267
- ```
268
-
269
- Anything else on the command line is reported as an unknown option and then
270
- ignored — the deck still starts, so a typo costs you a warning rather than a
271
- dashboard.
272
-
273
- `--foreground` is there for anything that was relying on the old behaviour — a
274
- wrapper script, a CI step, a supervisor of your own that starts the deck and
275
- waits on it. On the first run after upgrading you may also see a line about a
276
- deck from an older version still running: those publish nothing this one can
277
- check, so it starts beside them rather than attaching to something it cannot
278
- identify, and `ccdeck --stop --all` clears them.
279
-
280
- `ccdeck` runs in the background. The boot report prints in your terminal exactly
281
- as it always has — the hooks, the port, the URL — and then the prompt comes back
282
- and the deck stays up: closing the window does not take it with you any more,
283
- because it is in its own process group and the terminal's hangup never reaches
284
- it. `ccdeck --stop` ends it, `ccdeck --status` says what is running, and
285
- `ccdeck --logs` shows what it wrote after you stopped watching. Ctrl+C while the
286
- boot is still printing cancels the start, which is the one thing it still means.
287
-
288
- One Windows exception, and it is `ssh`'s rather than the deck's: OpenSSH puts a
289
- session's processes in a job object that it kills when the session ends, and
290
- nothing started from inside one survives it. A deck started from an ordinary
291
- Windows terminal, or by the login task below, keeps running — measured on a real
292
- machine, from a second SSH session, still serving.
293
-
294
- It also starts when you log in, so a reboot does not cost you the morning's
295
- events. That is set up once, on the first run, and said out loud when it happens;
296
- `ccdeck --uninstall-service` undoes it and `ccdeck --uninstall` takes it with the
297
- hooks. A launchd agent on macOS, a `systemd --user` unit on Linux, a Task
298
- Scheduler logon task on Windows — none of them carrying a restart policy of its
299
- own, because the one that decides when a crashed deck stops coming back lives in
300
- ccdeck and two policies over one process is how a stop becomes a suggestion. An
301
- `npx` run never installs one: it would name a path inside npm's cache, which npm
302
- deletes without warning. On Linux, `systemd --user` is torn down at logout unless
303
- `loginctl enable-linger` is on for your account — the install says so rather than
304
- changing that for you.
305
-
306
- If the deck falls over on its own, it comes back — five times in ten minutes,
307
- with the wait doubling each time, and then it stops and says so in the log rather
308
- than spinning on a machine nobody is watching. A clean `--stop` is never answered
309
- with a restart, and neither is a deck that failed to start in the first place:
310
- retrying a port the OS will not give you just prints the same refusal six times.
311
-
312
- Typing plain `ccdeck` beside a deck that is already running opens **that deck's
313
- tab** rather than building a second one. It used to build the second one: port
314
- 4317 was busy, so the new deck took a random port out of 4318–4400 and stood
315
- there beside a perfectly healthy first one, and neither mentioned the other.
316
- The port fallback is still there — 4317 is also the standard OTLP collector
317
- port, and Windows reserves whole TCP ranges for Hyper-V, WSL2 and Docker — but
318
- it now runs only when the thing holding the port is *not* one of your decks. The
319
- deck on the port has to prove it is yours, with the same token handshake the
320
- hooks use, before its tab is opened. Any flag that changes what the deck **is**
321
- — a port, a workspace, a log, either `--codex` or `--claude` pair — still starts
322
- its own, and `--new` starts one unconditionally.
323
-
324
- ccdeck looks for each CLI before it does anything on that CLI's behalf. Claude
325
- Code counts as present when its binary is on `PATH` (or in one of the places its
326
- installers put it), or when its config dir carries traces of having been used;
327
- Codex counts as present when `~/.codex/` exists. On a machine with only one of
328
- them, the other one's hooks, installs and panels are skipped rather than shown
329
- empty — the boot banner says which way it went, and `--claude` / `--codex`
330
- override it if the guess is wrong.
331
-
332
- `--workspace` is a filter this deck applies to itself, not a claim on the sessions it matches: **every** running deck whose workspace contains a session's directory draws that session, so a machine-wide deck and one scoped to `~/proj` both show the agents working inside `~/proj`. It reads the same way on all three paths a session can reach the canvas by — Claude Code's hook, Codex's rollout files, and the boot replay of the events log — and the events log still gets exactly one copy of each event, whichever decks are up. A relative path is resolved against the directory you start the deck in, and once, so every path scopes to the same tree. The log is machine-wide by default and shared by every deck on the box, so a scoped deck replays only the part of it that is inside its own workspace: it comes up showing what it will go on to capture, and nothing else.
333
-
334
- That one events log is also the reason Clear is not quite the per-deck button it looks like. The decks elect a single writer for each log file, and only that deck may empty it: Clear on any other deck wipes its own canvas and leaves the file to the deck that writes it. The confirmation says which of the two you are about to do, and how many decks share the log when it is yours to empty — so `--history` or `--no-persist` gives a deck a log of its own if you want Clear to answer to nobody else.
335
-
336
- Environment:
337
-
338
- | Variable | Effect |
339
- |---|---|
340
- | `AGENT_DAG_PORT` | Default port, same as `-p` |
341
- | `CODEX_HOME` | Override `~/.codex` |
342
- | `AGENTS_DECK_NO_INSTALL=1` | Never install or update claude-swap / ccusage, and never ask npm about releases |
343
- | `AGENTS_DECK_NO_DOWNLOAD=1` | Never download the `uv` binary, but keep the managed installs |
344
- | `AGENTS_DECK_NO_UPDATE_CHECK=1` | Don't ask npm about releases, but keep everything else |
345
- | `AGENTS_DECK_NO_FRESHEN=1` | Never nudge claude-swap to collect usage early |
346
- | `AGENTS_DECK_NO_NOTIFY=1` | Never raise a desktop notification when a session blocks and no page is open |
347
- | `AGENTS_DECK_CSWAP` | Full path to `cswap`, when it lives somewhere unusual |
348
- | `AGENTS_DECK_CLAUDE` | Full path to the `claude` CLI |
349
- | `AGENTS_DECK_CCUSAGE` | Full path to your own `ccusage`, used ahead of everything else |
350
- | `CLAUDE_SWAP_BACKUP` | Override the claude-swap store root the Accounts panel reads |
351
- | `CLAUDE_CONFIG_DIR` | Override `~/.claude` — the hook entry, the event log and everything else the deck writes move with it |
352
- | `AGENTS_DECK_LHM_PORT` | Port of a running LibreHardwareMonitor web server, when it is not 8085 (Windows temperatures) |
353
-
354
- Usage history is read with `ccusage`, and the deck takes the first of these that answers: `AGENTS_DECK_CCUSAGE` if you set it, then the copy it installed for itself under `~/.agents-deck/ccusage`, then a `ccusage` on your PATH, then `npx -y ccusage@latest`. So installing ccusage yourself is enough — the deck will not fetch a second copy, and it works under `AGENTS_DECK_NO_INSTALL=1`, which is the combination that variable is for. When something fails, the modal names which of those four it was.
355
-
356
- Being told to restart after an upgrade is local only — no network involved — and cannot be turned off, because a deck running superseded code is a bug you cannot see any other way.
357
-
358
- ## Uninstall
359
-
360
- ```bash
361
- npx ccdeck --uninstall
362
- ```
363
-
364
- Removes every hook entry ccdeck injected from `~/.claude/settings.json`, and `~/.codex/hooks.json` if present — and the login item, if this machine had one. That one exception to the narrowness below is deliberate: a login item left behind would keep starting a deck whose hooks had just been removed.
365
-
366
- It removes the hook entries and nothing else. The forwarder script
367
- (`~/.claude/agent-dag/hook.js`), the discovery directory around it, the events
368
- log, and the tools ccdeck installed for you — claude-swap, ccusage, and a `uv`
369
- binary if it had to fetch one — are all left in place, and each has its own
370
- uninstaller. Deleting `~/.claude/agent-dag/` and `~/.agents-deck/` clears
371
- ccdeck's own files; `uv tool uninstall claude-swap` (or `pipx uninstall
372
- claude-swap`) removes the account switcher.
373
-
374
- ## Updating
375
-
376
- The deck checks npm for a newer release at most once an hour, plus once when it starts — a ~20-byte GET to `registry.npmjs.org`, asking about the package this copy would actually install (a deck started with `npx ccdeck` asks about `ccdeck`). When that names a version it has not seen before, one more request confirms the version is really there: npm moves the dist-tag before the version itself has propagated, and a banner shown inside that window ends in `ETARGET` instead of an upgrade. So a check is one request, or two when there is something new to confirm — and a version that is tagged but not yet installable is looked at again in five minutes rather than in an hour. Click the version chip in the topbar to ask immediately.
377
-
378
- What the banner offers depends on how this copy was installed:
379
-
380
- | Installed as | Offer |
381
- |---|---|
382
- | global npm install | **Update now** — runs `npm install -g` on the name you installed (`ccdeck`, `agents-deck` or `agent-dag`), then restarts once nothing is running |
383
- | `npx` | **Update & restart** — re-runs the spec through npx, which fetches a fresh copy and takes over the same port |
384
- | git checkout | the command, because your working copy leads npm: `git pull && npm run build` |
385
- | directory not writable | the command — a root-owned prefix is declined up front rather than failing inside npm |
386
- | `AGENTS_DECK_NO_INSTALL=1` | the command only; you asked for no installs |
387
-
388
- Nothing is ever installed unless you click, the argument vector is fixed in the server rather than taken from the request, and the command is always on screen — button or no button. If npm fails, the banner shows npm's own last line.
389
-
390
- ### Restarting
391
-
392
- ccdeck runs as a two-process pair: a supervisor that owns nothing but the lifecycle, and the deck itself. When newer code is found, the deck exits with code 75 and the supervisor brings it back **on the port it actually bound**, which is not always the one it asked for. Both live in their own process group since 3.20, so stdout goes to `deck.log` rather than to the terminal you started from — `ccdeck --logs` reads it back. The supervisor also puts the deck back after a crash, five times in ten minutes with the wait doubling, and then stops and says why rather than spinning.
393
-
394
- It restarts on its own only after 30 seconds with nothing running, because hook events are fire-and-forget and anything fired during the gap is lost. The toggle in the banner turns that off; the preference is per-browser. Under `--no-persist` a restart is refused outright — with no event log there is nothing to replay, and the canvas would be gone.
395
-
396
- ## Design
397
-
398
- - Node = agent (root session or subagent)
399
- - Edge = parent → child (spawn), or agent → tool (call)
400
- - In-flight animates; settled dims
401
- - Click a node for the full story
402
-
403
- ## Names
404
-
405
- **ccdeck** is the name — of this repo and of the command. On npm it goes out
406
- under three, and `npx` runs the same deck from any of them.
407
-
408
- ```bash
409
- npx ccdeck # this repo's name — the short one
410
- npx agents-deck # the name it shipped under before the rename
411
- npx agent-dag # the original name; existing installs and scripts keep working
6
+ npx ccdeck
412
7
  ```
413
8
 
414
- The three are one build published three times, so they behave identically:
415
- `npx` on any of them fetches a single package, and `npm i -g` on any of them
416
- puts all three commands on your `PATH`.
417
-
418
- `ccdeck` was the exception until recently — a thin package that depended on
419
- `agents-deck` and spawned its binary. It worked, but `npx ccdeck` downloaded two
420
- packages instead of one and a global `ccdeck` ran under the old package's name.
421
- It is now the same build as the other two.
422
-
423
- The repository was previously named `agents-deck`; the old URL redirects here,
424
- so existing clones, links and bookmarks keep working.
425
-
426
- ## Questions people ask
427
-
428
- **Does it work with Codex, or only Claude Code?**
429
- Both, on one canvas. Claude Code arrives through a hook, Codex through its
430
- rollout log, and the model chip tells them apart. The *blocked on you* queue is
431
- Claude Code only — Codex emits no signal for it.
432
-
433
- **Does anything leave my machine?**
434
- Your sessions, never. The deck binds `127.0.0.1` and has no telemetry. The only
435
- outbound requests are a ~20-byte version check to the npm registry at most once
436
- an hour, and whatever the usage panels ask Anthropic and OpenAI for about your
437
- own quota. [What it touches](#what-it-touches) lists all of it.
438
-
439
- **Can it interfere with what my agent does?**
440
- No. The hook it installs is a one-way forwarder: it POSTs the event, exits `0`,
441
- and writes nothing to stdout — the two channels Claude Code's hook protocol
442
- gives a hook for allowing, denying or rewriting a tool call. It uses neither, so
443
- it has no way to answer at all, and a test pins both halves.
444
-
445
- **Does it need an account, an API key or a config file?**
446
- None of the three. `npx ccdeck` and it runs.
447
-
448
- **macOS, Linux, Windows?**
449
- All three, and each release is tested on all three.
450
-
451
- **What is the difference between `ccdeck`, `agents-deck` and `agent-dag`?**
452
- One deck, three names on npm. `ccdeck` is the one to use; the other two are the
453
- names it shipped under before, kept working so nobody's command breaks. See
454
- [Names](#names).
455
-
456
- **Do I have to keep a terminal open?**
457
- No. Since 3.20 the deck runs in the background, survives the terminal closing,
458
- and starts again when you log in. `ccdeck --stop` ends it.
459
-
460
- ## License
461
-
462
- ccdeck is licensed under the GNU Affero General Public License v3.0 only
463
- (AGPL-3.0-only).
464
-
465
- Copyright © 2026 [Bargan Constantin](https://github.com/BarganConstantin).
466
-
467
- See [LICENSE](./LICENSE) for the full text.
468
-
469
- Running the deck imposes nothing on you. The AGPL's obligations attach to
470
- distributing ccdeck or offering a **modified** version to users over a network
471
- — and what they require is that those users be offered the corresponding
472
- source of that modified version, not that anyone contribute changes back to
473
- this repository.
474
-
475
- Releases up to and including **3.21.4** were published under the MIT licence
476
- and remain available under MIT — this change is not retroactive and revokes
477
- nothing already granted. [LICENSING.md](./LICENSING.md) explains what applies
478
- to which version.
479
-
480
- ccdeck bundles third-party code under permissive licences (MIT, ISC,
481
- BSD-3-Clause); their notices are preserved in
482
- [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
9
+ Running `npx agent-dag` still works and will keep working — it installs ccdeck
10
+ and starts it. There is nothing else in this package, and it is not where the
11
+ deck is developed or documented.
package/package.json CHANGED
@@ -1,33 +1,23 @@
1
1
  {
2
2
  "name": "agent-dag",
3
- "version": "3.22.0",
4
- "description": "Live deck of Claude Code and Codex agents — watch tool calls, token spend and every Claude Code subagent on one calm canvas. Run it with npx ccdeck.",
3
+ "version": "3.22.3",
4
+ "description": "Renamed to ccdeck. This package installs and starts ccdeck for you — run `npx ccdeck` instead.",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/BarganConstantin/ccdeck.git"
9
+ },
10
+ "homepage": "https://github.com/BarganConstantin/ccdeck#readme",
5
11
  "type": "module",
6
12
  "bin": {
7
- "agents-deck": "bin/agent-dag.js",
8
- "agent-dag": "bin/agent-dag.js",
9
- "ccdeck": "bin/agent-dag.js"
13
+ "agent-dag": "shim.js"
10
14
  },
11
15
  "files": [
12
- "bin",
13
- "hook",
14
- "src/server",
15
- "dist/web",
16
- "release-notes.json",
17
- "LICENSE",
18
- "README.md",
19
- "THIRD_PARTY_NOTICES.md",
20
- "LICENSING.md"
16
+ "shim.js",
17
+ "README.md"
21
18
  ],
22
- "scripts": {
23
- "dev:web": "vite",
24
- "dev:server": "node src/server/index.mjs",
25
- "start": "node bin/agent-dag.js",
26
- "build": "vite build",
27
- "test": "vitest run",
28
- "prepublishOnly": "vite build",
29
- "prepack": "vite build",
30
- "typecheck": "tsc --noEmit -p tsconfig.build.json"
19
+ "dependencies": {
20
+ "ccdeck": "^3"
31
21
  },
32
22
  "engines": {
33
23
  "node": ">=18"
@@ -46,29 +36,5 @@
46
36
  "deck",
47
37
  "graph",
48
38
  "live"
49
- ],
50
- "author": "Bargan Constantin",
51
- "license": "AGPL-3.0-only",
52
- "repository": {
53
- "type": "git",
54
- "url": "git+https://github.com/BarganConstantin/ccdeck.git"
55
- },
56
- "bugs": {
57
- "url": "https://github.com/BarganConstantin/ccdeck/issues"
58
- },
59
- "homepage": "https://github.com/BarganConstantin/ccdeck#readme",
60
- "devDependencies": {
61
- "@types/dagre": "^0.7.54",
62
- "@types/node": "^22.20.1",
63
- "@types/react": "^18.3.12",
64
- "@types/react-dom": "^18.3.1",
65
- "@vitejs/plugin-react": "^4.3.4",
66
- "dagre": "^0.8.5",
67
- "react": "^18.3.1",
68
- "react-dom": "^18.3.1",
69
- "reactflow": "^11.11.4",
70
- "typescript": "^5.6.3",
71
- "vite": "^6.0.0",
72
- "vitest": "^2.1.9"
73
- }
39
+ ]
74
40
  }