ccdeck 3.29.10 → 3.30.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.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/bin/agent-dag.js +67 -118
  3. package/bin/cli/help.js +67 -0
  4. package/bin/cli/login-item.js +182 -0
  5. package/bin/cli/one-shot.js +254 -0
  6. package/bin/cli/package.js +39 -0
  7. package/bin/cli/pulse.js +98 -0
  8. package/bin/cli/restart.js +179 -0
  9. package/bin/cli/screen.js +166 -0
  10. package/bin/cli/second-start.js +120 -0
  11. package/bin/cli/startup.js +459 -0
  12. package/bin/cli/uninstall.js +169 -0
  13. package/bin/deck.js +112 -1559
  14. package/dist/web/assets/BrowserWatchModal-DP3YMtEi.js +1 -0
  15. package/dist/web/assets/UsageHistoryModal-CcdPVXAw.js +1 -0
  16. package/dist/web/assets/index-3vS6A0vt.css +1 -0
  17. package/dist/web/assets/index-BVTWHDta.js +298 -0
  18. package/dist/web/index.html +2 -2
  19. package/hook/hook.js +15 -14
  20. package/package.json +1 -1
  21. package/release-notes.json +28 -0
  22. package/src/server/account-projects.mjs +25 -11
  23. package/src/server/account-routes.mjs +254 -0
  24. package/src/server/activity.mjs +3 -3
  25. package/src/server/append-budget.mjs +185 -0
  26. package/src/server/append-failures.mjs +147 -0
  27. package/src/server/args.mjs +37 -5
  28. package/src/server/atomic-write.mjs +300 -0
  29. package/src/server/block-notify.mjs +0 -5
  30. package/src/server/boot-lock.mjs +1 -1
  31. package/src/server/brand.mjs +7 -6
  32. package/src/server/browser-react.mjs +10 -2
  33. package/src/server/browser-watch-decks.mjs +146 -0
  34. package/src/server/browser-watch-feed.mjs +69 -0
  35. package/src/server/browser-watch-log.mjs +173 -0
  36. package/src/server/browser-watch-mtime.mjs +18 -0
  37. package/src/server/browser-watch-relay.mjs +112 -0
  38. package/src/server/browser-watch-routes.mjs +172 -0
  39. package/src/server/browser-watch-seen.mjs +127 -0
  40. package/src/server/browser-watch-store.mjs +14 -143
  41. package/src/server/browser-watch.mjs +278 -626
  42. package/src/server/canonical-path.mjs +150 -0
  43. package/src/server/ccusage-failure.mjs +64 -0
  44. package/src/server/ccusage-install.mjs +459 -0
  45. package/src/server/ccusage-runner.mjs +271 -0
  46. package/src/server/ccusage.mjs +21 -756
  47. package/src/server/claude-accounts.mjs +220 -464
  48. package/src/server/claude-collector.mjs +189 -0
  49. package/src/server/claude-dir.mjs +2 -2
  50. package/src/server/claude-identity.mjs +5 -4
  51. package/src/server/claude-reset-credits.mjs +130 -0
  52. package/src/server/claude-verdicts.mjs +174 -0
  53. package/src/server/clear-route.mjs +191 -0
  54. package/src/server/codex-auth.mjs +4 -4
  55. package/src/server/codex-dir.mjs +52 -8
  56. package/src/server/codex-enrichment.mjs +310 -0
  57. package/src/server/codex-quota.mjs +5 -3
  58. package/src/server/codex-translate.mjs +407 -0
  59. package/src/server/codex-usage.mjs +8 -5
  60. package/src/server/codex-watch.mjs +496 -0
  61. package/src/server/cswap-auto-loop.mjs +342 -0
  62. package/src/server/cswap-auto-readers.mjs +303 -0
  63. package/src/server/cswap-auto.mjs +51 -609
  64. package/src/server/cswap-install.mjs +69 -265
  65. package/src/server/cswap-layout.mjs +242 -0
  66. package/src/server/deck-home.mjs +1 -1
  67. package/src/server/deck-prefs.mjs +51 -184
  68. package/src/server/deck-scope.mjs +52 -0
  69. package/src/server/desktop-update-routes.mjs +75 -0
  70. package/src/server/detach.mjs +78 -0
  71. package/src/server/discovery.mjs +298 -0
  72. package/src/server/event-log.mjs +328 -0
  73. package/src/server/event-pipeline.mjs +235 -0
  74. package/src/server/event-ring.mjs +214 -0
  75. package/src/server/event-routes.mjs +352 -0
  76. package/src/server/event-sink.mjs +26 -0
  77. package/src/server/exec-children.mjs +128 -0
  78. package/src/server/exec-not-found.mjs +213 -0
  79. package/src/server/exec-output.mjs +23 -0
  80. package/src/server/exec-spec.mjs +341 -0
  81. package/src/server/exec.mjs +74 -661
  82. package/src/server/health-route.mjs +42 -0
  83. package/src/server/hook-challenge.mjs +37 -0
  84. package/src/server/http-io.mjs +104 -0
  85. package/src/server/index.mjs +172 -7620
  86. package/src/server/install-layout.mjs +506 -0
  87. package/src/server/installer.mjs +79 -608
  88. package/src/server/jsonl-chunks.mjs +139 -0
  89. package/src/server/lan-accounts.mjs +27 -0
  90. package/src/server/lan-beacon.mjs +457 -0
  91. package/src/server/lan-call.mjs +239 -0
  92. package/src/server/lan-copies.mjs +270 -0
  93. package/src/server/lan-deck.mjs +583 -0
  94. package/src/server/lan-dials.mjs +161 -0
  95. package/src/server/lan-engine.mjs +475 -1024
  96. package/src/server/lan-hearing.mjs +49 -0
  97. package/src/server/lan-inbound.mjs +100 -0
  98. package/src/server/lan-invite-offer.mjs +77 -0
  99. package/src/server/lan-invite.mjs +207 -0
  100. package/src/server/lan-lines.mjs +73 -0
  101. package/src/server/lan-manifest.mjs +74 -0
  102. package/src/server/lan-reach.mjs +82 -69
  103. package/src/server/lan-requests.mjs +249 -0
  104. package/src/server/lan-round-record.mjs +45 -0
  105. package/src/server/lan-round-timer.mjs +62 -0
  106. package/src/server/lan-routes.mjs +143 -0
  107. package/src/server/lan-serve.mjs +112 -0
  108. package/src/server/lan-socket.mjs +243 -925
  109. package/src/server/lan-sync.mjs +58 -1026
  110. package/src/server/lan-tailnet-poll.mjs +57 -0
  111. package/src/server/lan-turns.mjs +80 -0
  112. package/src/server/lan-wire.mjs +620 -0
  113. package/src/server/lifecycle.mjs +421 -0
  114. package/src/server/listen.mjs +110 -0
  115. package/src/server/live-decks.mjs +138 -0
  116. package/src/server/load-average.mjs +32 -0
  117. package/src/server/log-election.mjs +235 -0
  118. package/src/server/log-replay.mjs +374 -0
  119. package/src/server/log-writer.mjs +72 -557
  120. package/src/server/login-service.mjs +3 -2
  121. package/src/server/macmon.mjs +1 -1
  122. package/src/server/memory-files.mjs +163 -0
  123. package/src/server/memory-metrics.mjs +178 -0
  124. package/src/server/metrics-history.mjs +94 -0
  125. package/src/server/metrics-run.mjs +100 -0
  126. package/src/server/music-routes.mjs +134 -0
  127. package/src/server/network-metrics.mjs +5 -3
  128. package/src/server/network-sampler.mjs +256 -0
  129. package/src/server/npm-latest.mjs +385 -0
  130. package/src/server/npm-upgrade.mjs +352 -0
  131. package/src/server/npx.mjs +41 -7
  132. package/src/server/output-watch.mjs +5 -4
  133. package/src/server/pinned-build.mjs +108 -0
  134. package/src/server/prefs-path.mjs +14 -0
  135. package/src/server/prefs-refusal.mjs +77 -0
  136. package/src/server/prefs-routes.mjs +76 -0
  137. package/src/server/prefs-set-aside.mjs +86 -0
  138. package/src/server/prefs-state.mjs +51 -0
  139. package/src/server/process-command.mjs +104 -0
  140. package/src/server/process-list.mjs +547 -0
  141. package/src/server/pulse-line.mjs +132 -0
  142. package/src/server/purge-key.mjs +7 -7
  143. package/src/server/quota-cli.mjs +174 -0
  144. package/src/server/quota-oauth.mjs +190 -0
  145. package/src/server/quota-shape.mjs +229 -0
  146. package/src/server/quota-store-resets.mjs +206 -0
  147. package/src/server/quota.mjs +123 -509
  148. package/src/server/relay-guard.mjs +3 -3
  149. package/src/server/request-gates.mjs +441 -0
  150. package/src/server/restart-note.mjs +180 -0
  151. package/src/server/retire-sound-hook.mjs +3 -3
  152. package/src/server/ring-bounds.mjs +215 -0
  153. package/src/server/running-deck.mjs +1 -1
  154. package/src/server/running-version.mjs +24 -0
  155. package/src/server/self-update.mjs +39 -1371
  156. package/src/server/session-enrichment.mjs +622 -0
  157. package/src/server/session-read-gate.mjs +49 -0
  158. package/src/server/session-tracking.mjs +242 -0
  159. package/src/server/sse-clients.mjs +222 -0
  160. package/src/server/stale-discovery.mjs +30 -0
  161. package/src/server/static-serve.mjs +81 -0
  162. package/src/server/supervisor.mjs +73 -12
  163. package/src/server/system-metrics.mjs +169 -1935
  164. package/src/server/term.mjs +11 -249
  165. package/src/server/thermal-metrics.mjs +520 -0
  166. package/src/server/thermal-sampler.mjs +204 -0
  167. package/src/server/token-redact.mjs +123 -0
  168. package/src/server/transcript-gate.mjs +107 -0
  169. package/src/server/transcript-scan.mjs +563 -0
  170. package/src/server/usage-routes.mjs +93 -0
  171. package/src/server/uv-bootstrap.mjs +1 -1
  172. package/src/server/wordmark.mjs +127 -0
  173. package/dist/web/assets/BrowserWatchModal-CMn28FHb.js +0 -1
  174. package/dist/web/assets/UsageHistoryModal-C-sPhsKT.js +0 -1
  175. package/dist/web/assets/index-C1YFI9f4.js +0 -298
  176. package/dist/web/assets/index-Ck2RAla3.css +0 -1
@@ -0,0 +1,161 @@
1
+ // The addresses this deck dials that the beacon did not hand it — typed in,
2
+ // pressed accept on, joined by invite, asked from a beacon, or learned from a
3
+ // paired deck that called in — and what answered at each: lifted out of
4
+ // createEngine in lan-engine.mjs with the three collections only they used.
5
+ // Nothing here dials; a round in the engine walks rows() and reports back
6
+ // through answered() and failed().
7
+
8
+ /**
9
+ * The most addresses `autoAsk` may put on the dial list on its own.
10
+ *
11
+ * Measured, because the shape of it is not the obvious one: the dial list is
12
+ * keyed `host:port`, not by fingerprint, so five hundred beacons from one
13
+ * address on one port make one row. Five hundred beacons from one address on
14
+ * five hundred PORTS make five hundred rows, and a round dials them one at a
15
+ * time with a ROUND_MS bell on each — so a list that size is eighty minutes of
16
+ * round, and the decks somebody actually paired with sit at the end of it
17
+ * waiting their turn. The ceiling on that without this number is 65,535 rows
18
+ * from a single host.
19
+ *
20
+ * It bounds only what the deck added BY ITSELF. Addresses a person typed are
21
+ * not capped: a list of those is somebody's own decision and the deck is in no
22
+ * position to tell them they have too many machines.
23
+ */
24
+ export const MAX_AUTO_PEERS = 32;
25
+
26
+ export function createDials() {
27
+ /** Peers the user typed in, which the beacon will never find.
28
+ *
29
+ * Broadcast dies at the first router and is dropped by a switch that
30
+ * filters it, so a deck across a VPN or on another subnet is unreachable by
31
+ * discovery and perfectly reachable by address. Typing one is a decision to
32
+ * trust whatever answers there the first time, and to pin it: an address is
33
+ * a way to reach a deck, and the accept on the other machine is what lets
34
+ * anything move.
35
+ *
36
+ * Keyed by `host:port` rather than by fingerprint, because a fingerprint is
37
+ * what a deck says about itself after the handshake and this list has to
38
+ * exist before there has been one.
39
+ *
40
+ * EVERY ROW SAYS WHERE IT CAME FROM, in `typed`, and the difference decides
41
+ * whether reaching it may pin a key sight unseen. A row somebody put in the
42
+ * address field — or pressed accept on, or joined by invite — is a person
43
+ * naming a machine. A row `autoAsk` added from a beacon is this deck
44
+ * answering a shout, which is not the same claim and must not read as one.
45
+ * See roundWith in lan-engine.mjs, where the difference is the whole of the
46
+ * trust rule. */
47
+ const manual = new Map();
48
+ /** What answered at a typed address, once something has. Keyed the same way
49
+ * `manual` is, because until a connection succeeds an address is all there
50
+ * is to key on. */
51
+ const learned = new Map();
52
+ /** Addresses this deck added because a paired deck called in from them and
53
+ * nothing here dialled it — see learnCaller in lan-engine.mjs. Kept until a
54
+ * round proves the address answers: one that does is an ordinary dialled
55
+ * peer from then on and leaves this set; one that does not is a caller this
56
+ * deck cannot reach back (a strict NAT, a one-way path), and its row is
57
+ * taken away again so it reverts to "calls in" rather than failing every
58
+ * round. A person naming the address ends its trial too, and so does a
59
+ * settings write that takes its row away (#1674). */
60
+ const calledBack = new Set();
61
+
62
+ /** Dial this address on every round from now on. Returns false for an
63
+ * address that is not one, rather than storing a row that can never
64
+ * connect and reports an error every minute forever. */
65
+ const add = (addr, port, { typed = true } = {}) => {
66
+ const p = Number(port);
67
+ if (typeof addr !== "string" || !addr.trim() || !Number.isInteger(p) || p < 1 || p > 65_535) return false;
68
+ const host = addr.trim();
69
+ const at = `${host}:${p}`;
70
+ // MAKING ROOM RATHER THAN REFUSING, and only among rows the deck added
71
+ // itself. A hard refusal at the cap would let whoever got there first
72
+ // keep the whole budget, so a real deck starting later would never be
73
+ // asked — which turns a cap meant to protect the round into a way to
74
+ // silence it. Evicted first is the oldest auto row that has never
75
+ // answered: `learned` holds an entry only for an address that completed a
76
+ // handshake, so a row with no entry there has cost a round and returned
77
+ // nothing. When every auto row has answered, the new one waits.
78
+ if (!typed && !manual.has(at)) {
79
+ const auto = [...manual.entries()].filter(([, v]) => !v.typed);
80
+ if (auto.length >= MAX_AUTO_PEERS) {
81
+ const stale = auto.find(([k]) => !learned.has(k));
82
+ if (!stale) return false;
83
+ manual.delete(stale[0]);
84
+ learned.delete(stale[0]);
85
+ }
86
+ }
87
+ // A row somebody typed outranks one the deck added: the same address
88
+ // arriving by hand after a beacon put it there is a person vouching for
89
+ // it, and nothing about that should be undone by the next beacon.
90
+ const was = manual.get(at);
91
+ manual.set(at, {
92
+ fp: `manual:${at}`, name: host, addr: host, port: p, manual: true,
93
+ typed: typed || was?.typed === true,
94
+ });
95
+ // AND A PERSON NAMING IT ENDS ITS TRIAL (#1674). A dial-back is taken away
96
+ // when a round cannot reach it, and a row somebody typed is not the deck's
97
+ // to take away: it stays, failing, and says why.
98
+ if (typed) calledBack.delete(at);
99
+ return true;
100
+ };
101
+
102
+ return {
103
+ add,
104
+ remove(addr, port) { return manual.delete(`${String(addr).trim()}:${Number(port)}`); },
105
+ /** Replace the typed list wholesale, which is what a settings write means.
106
+ * Adding one at a time would leave a removed address still being dialled
107
+ * every minute until the next restart — the row would vanish from the
108
+ * panel while the socket kept opening, which is the worst of both. The
109
+ * size of the list it leaves. */
110
+ replace(entries) {
111
+ manual.clear();
112
+ // The trials go with the rows they were for (#1674). One left behind
113
+ // outlived its row, and the next round that failed at that address took
114
+ // away whatever was dialled there by then.
115
+ calledBack.clear();
116
+ for (const entry of Array.isArray(entries) ? entries : []) {
117
+ const at = String(entry).lastIndexOf(":");
118
+ if (at > 0) add(String(entry).slice(0, at), Number(String(entry).slice(at + 1)));
119
+ }
120
+ return manual.size;
121
+ },
122
+
123
+ /** Every row on the list, oldest first. */
124
+ rows: () => [...manual.values()],
125
+ /** What answered at `host:port`, once something has. */
126
+ metAt: at => learned.get(at),
127
+ /** Say what answered at `host:port` — a handshake that just finished said
128
+ * so, before any round has. */
129
+ meet(at, met) { learned.set(at, met); },
130
+ /** Whether a row on the list answered as `fp` — one of the two ways this
131
+ * deck already dials a deck; the other is a beacon it still hears. */
132
+ answersAs(fp) {
133
+ for (const [at, met] of learned) if (met?.fp === fp && manual.has(at)) return true;
134
+ return false;
135
+ },
136
+ /** The row whose answer was `fp`, or undefined. */
137
+ rowAnswering: fp => [...manual.values()].find(p => learned.get(`${p.addr}:${p.port}`)?.fp === fp),
138
+
139
+ /** A row put on for a paired deck that called in from it, on trial until a
140
+ * round proves the address answers — see calledBack. */
141
+ trial(at, met) {
142
+ learned.set(at, met);
143
+ calledBack.add(at);
144
+ },
145
+ /** A round reached `host:port` and `met` is who answered there. A
146
+ * dial-back that answered is an ordinary row from now on. */
147
+ answered(at, met) {
148
+ learned.set(at, met);
149
+ calledBack.delete(at);
150
+ },
151
+ /** A round did not reach `host:port`. A dial-back on trial is taken away
152
+ * again, and true says one was; any other row stays and fails again. */
153
+ failed(at) {
154
+ if (!calledBack.has(at)) return false;
155
+ calledBack.delete(at);
156
+ manual.delete(at);
157
+ learned.delete(at);
158
+ return true;
159
+ },
160
+ };
161
+ }