ccdeck 3.43.0 → 3.43.1

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.
@@ -1 +1 @@
1
- import"./app-9p127oSR.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function s(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=s(e);fetch(e.href,r)}})();
1
+ import"./app-ByCusSHX.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function s(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=s(e);fetch(e.href,r)}})();
@@ -77,8 +77,8 @@
77
77
  document.documentElement.setAttribute("data-density", density === "tightest" || density === "tighter" || density === "compact" || density === "comfortable" ? density : "default");
78
78
  })();
79
79
  </script>
80
- <script type="module" crossorigin src="/assets/index-Bkx27-wy.js"></script>
81
- <link rel="modulepreload" crossorigin href="/assets/app-9p127oSR.js">
80
+ <script type="module" crossorigin src="/assets/index-CzIBjYsB.js"></script>
81
+ <link rel="modulepreload" crossorigin href="/assets/app-ByCusSHX.js">
82
82
  <link rel="stylesheet" crossorigin href="/assets/app-DsHSufBt.css">
83
83
  </head>
84
84
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccdeck",
3
- "version": "3.43.0",
3
+ "version": "3.43.1",
4
4
  "description": "Local dashboard for Claude Code and Codex CLI sessions: which Claude Code session needs input, every tool call and Claude Code subagent live, token cost and quota, several Claude accounts. Run npx ccdeck.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -76,7 +76,8 @@
76
76
  "3.38.6",
77
77
  "3.39.4",
78
78
  "3.40.1",
79
- "3.41.3"
79
+ "3.41.3",
80
+ "3.43.1"
80
81
  ],
81
82
  "3.43.0": [
82
83
  {
@@ -146,14 +146,21 @@ async function engineState(accounts) {
146
146
  };
147
147
  }
148
148
 
149
- /** The accounts the decks this one is paired with say they are working on.
149
+ /** The accounts paired decks have recently said they are working on.
150
150
  * One login live on two machines races for every renewal (README, "One login
151
- * live in one place"), so a tick never moves onto one of them. */
152
- async function accountsOnOtherDecks() {
151
+ * live in one place"), so a tick never moves onto one of them. The LAN table
152
+ * retains old manifests for display after a peer goes quiet; those are not
153
+ * current reservations. Use the authenticated manifest's receipt time, not a
154
+ * beacon or a failed round, which cannot confirm the account still in use. */
155
+ async function accountsOnOtherDecks(now) {
153
156
  try {
154
- const { lanEngine } = await import("./lan-deck.mjs");
157
+ const [{ lanEngine }, { PRESENT_MS }] = await Promise.all([
158
+ import("./lan-deck.mjs"), import("./lan-sync.mjs"),
159
+ ]);
155
160
  const peers = lanEngine.status()?.peers ?? [];
156
- return new Set(peers.map(p => p?.offers?.current?.key).filter(k => typeof k === "string"));
161
+ return new Set(peers
162
+ .filter(p => p?.paired === true && Number.isFinite(p.offers?.at) && p.offers.at > 0 && now - p.offers.at < PRESENT_MS)
163
+ .map(p => p.offers.current?.key).filter(k => typeof k === "string"));
157
164
  } catch {
158
165
  return new Set();
159
166
  }
@@ -242,7 +249,7 @@ export async function chooseBeforeEngine({ now = Date.now() } = {}) {
242
249
  choice: { next: choice.next, home: choice.home?.key ?? null, manual, goHome },
243
250
  threshold,
244
251
  models,
245
- busy: new Set([...await accountsOnOtherDecks(), ...await accountsInCswapRun(accounts)]),
252
+ busy: new Set([...await accountsOnOtherDecks(now), ...await accountsInCswapRun(accounts)]),
246
253
  quarantined: engine.quarantined,
247
254
  sinceMove: now - Math.max(choice.lastMoveAt, engine.lastSwitchAt),
248
255
  cooldownMs: numberOr(cfg["autoswitch.cooldownSeconds"]?.value, 300) * 1000,
@@ -102,8 +102,23 @@ async function runAutoTick() {
102
102
  if (await externalAutoRunning()) return { skipped: "external-engine" };
103
103
  // The deck's own rules first. A move they make is the tick; when they have
104
104
  // nothing to say — or cannot say it — the engine decides, as it always did.
105
- const chose = await chooseBeforeEngine().catch(() => ({ result: null, accounts: null }));
105
+ let chose = await chooseBeforeEngine().catch(() => ({ result: null, accounts: null }));
106
106
  if (chose.result) return { chose: chose.result };
107
+ // The store may still have a reading from before the active account
108
+ // reached its limit. The engine polls before deciding; letting
109
+ // its real tick run now would switch by `best` before our weekly/home
110
+ // rules ever saw that reading. Poll without moving first, then give the
111
+ // configured rules the refreshed roster under this same store lock.
112
+ // No rule configured (accounts === null) keeps the original single run.
113
+ if (chose.accounts) {
114
+ const preview = await run(await cswapBin(), ["auto", "--once", "--json", "--dry-run"], { timeout: TICK_TIMEOUT_MS });
115
+ if (preview.timedOut || (!preview.ok && !preview.stdout)) return preview;
116
+ if (!_enabled) return { skipped: "disabled" };
117
+ if (await externalAutoRunning()) return { skipped: "external-engine" };
118
+ invalidateClaudeAccountsCache();
119
+ chose = await chooseBeforeEngine().catch(() => ({ result: null, accounts: null }));
120
+ if (chose.result) return { chose: chose.result };
121
+ }
107
122
  const out = await run(await cswapBin(), ["auto", "--once", "--json"], { timeout: TICK_TIMEOUT_MS });
108
123
  return { ...out, accounts: chose.accounts };
109
124
  });