From 572d615d4ea7f8f7b0e63bf692dd1d76fd638493 Mon Sep 17 00:00:00 2001 From: nntrivi2001 Date: Fri, 23 Oct 2026 11:26:12 +0700 Subject: [PATCH] =?UTF-8?q?chore:=20v2.8.22=20=E2=80=94=20Users=20today/7/?= =?UTF-8?q?30-day=20spend=20counts=20every=20tool,=20update=20chip=20follo?= =?UTF-8?q?ws=20a=20running=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Users: today / 7 / 30-day spend read only token_usage_hourly, which holds Claude transcript hours only — omp/Codex/Antigravity/Cursor sessions, sub-agent files, baselines and long-context pricing were missing. Now booked by hourlyTokenRows + calculateCost like the cost routes. - Update: a page adopts a plugin update the server reports as running (after a reload, another tab, or one started from an older bundle), and the progress chip shows whenever an update runs and the modal is not visible — also when the status stops offering it mid-run. --- CLAUDE.md | 2 +- client/src/components/UpdateNotifier.tsx | 36 ++++++-- .../__tests__/UpdateNotifier.test.tsx | 33 +++++++ .../screens.snapshot.test.tsx.snap | 2 +- docs/SERVER-MODE.md | 3 +- package-lock.json | 4 +- package.json | 2 +- server/__tests__/server-mode-users.test.js | 15 +++- server/routes/users.js | 87 +++++++++---------- 9 files changed, 122 insertions(+), 62 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b76355d4..ba9a9f18 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Claude Code Working Guide -_Last synced: v2.8.21 (2026-10-23) — Claude Docs docs and session documents (.pptx/.docx/.xlsx/.pdf/.html) land in Artifacts, unowned rows are claimed; a plugin update reports its running step (`plugin_update_progress`); a hidden update or clone leaves a progress chip._ A change that makes any statement in this file, `AGENTS.md`, or a README wrong — a moved path, a new or changed extension point, a retired rule — updates that file in the same change-set and bumps this marker. **Bump = replace the version/date and a ≤ 1-line summary; never append a changelog here** — details belong in the commit message and `docs/`, because this whole file is loaded into every session. +_Last synced: v2.8.22 (2026-10-23) — Users' today/7/30-day spend is booked like the cost routes (every tool counts); a page adopts a running plugin update and keeps its progress chip._ A change that makes any statement in this file, `AGENTS.md`, or a README wrong — a moved path, a new or changed extension point, a retired rule — updates that file in the same change-set and bumps this marker. **Bump = replace the version/date and a ≤ 1-line summary; never append a changelog here** — details belong in the commit message and `docs/`, because this whole file is loaded into every session. ## Project mission - Maintain a reliable local-first dashboard for Claude Code session monitoring. diff --git a/client/src/components/UpdateNotifier.tsx b/client/src/components/UpdateNotifier.tsx index 1e18908d..ed4908cf 100644 --- a/client/src/components/UpdateNotifier.tsx +++ b/client/src/components/UpdateNotifier.tsx @@ -19,9 +19,12 @@ * background process can do for it. Escape, the backdrop and × close it for * this page load only — also while an update applies: it keeps running in the * background (the page still reloads itself once the new server is up), and a - * failure re-opens the modal with its error. While hidden, a small chip in the - * corner keeps showing the update — with the plugin chain's current step from - * `plugin_update_progress` — and a click on it shows the modal again; "Skip this version" is the one + * failure re-opens the modal with its error. While hidden — or once the status + * stops offering the update mid-run — a small chip in the corner keeps showing + * it, with the plugin chain's current step from `plugin_update_progress`, and a + * click on it shows the modal again. A chain the server reports as running + * (started before a reload or from another tab) is adopted the same way; + * "Skip this version" is the one * persistent, per-version dismiss (never while applying). After a failed update, "Auto fix" hands the * error to an AI agent: it opens (or adopts) a workspace on the install folder * and starts a Claude Code session there with the error as its prompt — the @@ -242,13 +245,14 @@ export function UpdateNotifier() { status?.latest_release || status?.remote_sha || (installed ? `installed:${installed.root}` : null); - // Updating replaces the host's install: only an admin is offered it. + // Updating replaces the host's install: only an admin is offered it. A + // running update stays shown (unless closed) even once the status stops + // offering it — the new version may already be on disk before it restarts. const show = Boolean( isAdmin && - status?.update_available && dismissKey && - dismissedSha !== dismissKey && - closedKey !== dismissKey + closedKey !== dismissKey && + (applying || restarting || (status?.update_available && dismissedSha !== dismissKey)) ); const busy = applying || restarting || fixing; @@ -344,6 +348,19 @@ export function UpdateNotifier() { [syncFromPayload, t] ); + // A chain already running on the server (started before a reload, in another + // tab, or from the old bundle) is adopted: this tab shows its steps, then + // reloads onto the new server like the tab that started it. + const running = status?.plugin_update_progress ?? null; + const adoptedRef = useRef(false); + useEffect(() => { + if (!running || !isAdmin || applying || restarting || adoptedRef.current) return; + adoptedRef.current = true; + setProgress(running); + setRestarting(true); + pollUntilBack(true, status?.local_version ?? null); + }, [running, isAdmin, applying, restarting, pollUntilBack, status?.local_version]); + const applyNow = async () => { if (applying || restarting) return; setError(null); @@ -406,8 +423,9 @@ export function UpdateNotifier() { }) : null; - // Hidden while it runs: a chip keeps it in sight and brings the modal back. - if (!show && isAdmin && (applying || restarting) && dismissKey && closedKey === dismissKey) { + // Hidden while it runs — closed, or the status no longer offers the update + // (the new version is already on disk): a chip keeps it in sight. + if (!show && isAdmin && (applying || restarting)) { return (