feat(updates): detect the newest release and self-apply it, including plugin installs

Update detection was a commit count against the default branch, which says nothing about whether a *version* shipped and is pure noise on the plugin cache (a depth-1 clone, where every distance is a graft artefact). It now reads the forge's own releases API and compares the published tag to this install's package.json version, falling back to the commit count only when no release can be read — with the reason in release_check, so "up to date" never silently means "we could not look".

Two states a commit count cannot express: dev_build (ahead of the newest release, missing nothing from it) now reads "Dev build - N ahead" instead of a false "Up to date"; and on a shallow clone every commits_*_release field is null and dev_build is never asserted, because the numbers would be meaningless.

POST /api/updates/apply gains a second mode, chosen by install kind and never by the request body. A checkout still fast-forwards onto the release tag, rebuilds and self-restarts. A ~/.claude/plugins/cache/ install is refused by applyUpdate (that directory is Claude Code's to replace) and handed instead to a detached chain: claude plugin marketplace update -> claude plugin update -y -> plugin-bootstrap.js --force from the NEW cache dir, which retires this server and starts its replacement. Both write the HTTP response before anything stops the process.

The chain records every outcome to plugin-update.json in the runtime dir, outside the cache the update replaces, so the next server reports it as last_plugin_update: a failed chain is otherwise indistinguishable from a slow one, and the tab would poll a server that never returns. /reload-plugins is deliberately not in the chain - it is REPL-scoped to the session that runs it, so no background process can perform it, and plugin-bootstrap derives PLUGIN_ROOT from its own __dirname, which is what makes it unnecessary for the dashboard.

Also rebrands the sidebar subtitle, splash and page title to SmartGift Solution, and corrects three ARCHITECTURE.md statements that claimed /apply did not exist.

Verified: server 1683/1683, client 486/486, build, prettier, file-header audit, openapi.yaml regenerated.
This commit is contained in:
2026-09-10 16:03:07 +07:00
parent eb63569324
commit cc85e35439
23 changed files with 1712 additions and 92 deletions
+4 -2
View File
@@ -4,7 +4,7 @@
"analytics": "Analytics",
"avg": "avg",
"brand": "Agent Dashboard",
"brandSub": "Claude Code Monitor",
"brandSub": "SmartGift Solution",
"ccConfig": "Claude Config",
"checkFailed": "Update check failed",
"checkForUpdates": "Check for updates",
@@ -65,5 +65,7 @@
"website": "Website",
"workflows": "Workflows",
"workspace": "Workspace",
"wsEndpoint": "WebSocket"
"wsEndpoint": "WebSocket",
"releaseAvailable": "{{version}} available",
"devBuild": "Dev build · {{count}} ahead"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"tagline": "Nothing your agents do stays in the dark.",
"sub1": "Real-time observability for every Claude Code session.",
"sub2": "Tokens, tools, costs, and workflows, all live.",
"brand": "Claude Code Monitor",
"brand": "SmartGift Solution",
"taglines": [
"Nothing your agents do stays in the dark.",
"See every move your agents make.",
+10 -1
View File
@@ -16,5 +16,14 @@
"updating": "Updating...",
"restarting": "Rebuilt — restarting the dashboard...",
"applyError": "Could not apply the update",
"reasonNotFastForwardable": "This checkout isn't on the tracked default branch — apply the command above manually."
"reasonNotFastForwardable": "This checkout isn't on the tracked default branch — apply the command above manually.",
"releaseTitle": "Release {{version}} available",
"releaseSubtitle": "You are running {{current}}",
"releaseLead": "A new release of the dashboard has been published. \"Update now\" fast-forwards this install onto the release tag, reinstalls dependencies, rebuilds, and restarts the server.",
"viewRelease": "Release notes",
"reasonPluginManaged": "This install is managed by the Claude Code plugin cache — run /agenthub-update instead.",
"releaseUnavailable": "Could not read the latest release from the repository ({{reason}}); fell back to comparing commits.",
"pluginAutoNote": "This dashboard is installed as a Claude Code plugin. \"Update now\" pulls the new plugin version, reinstalls the runtime, and restarts the server for you — it takes a few minutes. Newly added skills and slash commands only appear in an already-open Claude Code session after you run /reload-plugins there.",
"pluginUpdating": "Pulling the new plugin version and reinstalling the runtime — this takes a few minutes, the page reloads itself when the new server is up...",
"lastPluginUpdateFailed": "The last plugin update failed at step \"{{step}}\": {{error}} — run /agenthub-update in Claude Code to recover."
}
+4 -2
View File
@@ -4,7 +4,7 @@
"analytics": "Phân tích",
"avg": "TB",
"brand": "Bảng điều khiển Agent",
"brandSub": "Giám sát Claude Code",
"brandSub": "SmartGift Solution",
"ccConfig": "Cấu hình Claude",
"checkFailed": "Kiểm tra cập nhật thất bại",
"checkForUpdates": "Kiểm tra cập nhật",
@@ -65,5 +65,7 @@
"website": "Website",
"workflows": "Quy trình",
"workspace": "Không gian làm việc",
"wsEndpoint": "WebSocket"
"wsEndpoint": "WebSocket",
"releaseAvailable": "Có {{version}}",
"devBuild": "Bản dev · trước {{count}} commit"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"tagline": "Mọi điều agent của bạn làm đều không ẩn trong bóng tối.",
"sub1": "Khả năng quan sát thời gian thực cho mọi phiên Claude Code.",
"sub2": "Token, công cụ, chi phí và workflow, tất cả trực tiếp.",
"brand": "Giám sát Claude Code",
"brand": "SmartGift Solution",
"taglines": [
"Mọi điều agent của bạn làm đều không ẩn trong bóng tối.",
"Thấy rõ từng bước đi của agent.",
+10 -1
View File
@@ -16,5 +16,14 @@
"updating": "Đang cập nhật...",
"restarting": "Đã build xong — đang khởi động lại bảng điều khiển...",
"applyError": "Không thể áp dụng cập nhật",
"reasonNotFastForwardable": "Nhánh hiện tại không phải nhánh mặc định được theo dõi — hãy chạy lệnh ở trên thủ công."
"reasonNotFastForwardable": "Nhánh hiện tại không phải nhánh mặc định được theo dõi — hãy chạy lệnh ở trên thủ công.",
"releaseTitle": "Đã có bản phát hành {{version}}",
"releaseSubtitle": "Bạn đang chạy {{current}}",
"releaseLead": "Repo vừa phát hành bản mới. Bấm \"Cập nhật ngay\" để fast-forward install này lên tag của bản phát hành, cài lại dependency, build lại và khởi động lại server.",
"viewRelease": "Ghi chú phát hành",
"reasonPluginManaged": "Bản cài này do plugin cache của Claude Code quản lý — hãy chạy /agenthub-update.",
"releaseUnavailable": "Không đọc được bản phát hành mới nhất từ repo ({{reason}}); đã quay về so sánh theo commit.",
"pluginAutoNote": "Dashboard này cài dạng plugin của Claude Code. Bấm \"Cập nhật ngay\" sẽ tự tải bản plugin mới, cài lại runtime và khởi động lại server — mất vài phút. Riêng skill và slash command mới thì session Claude Code đang mở chỉ thấy sau khi bạn chạy /reload-plugins ở đó.",
"pluginUpdating": "Đang tải bản plugin mới và cài lại runtime — mất vài phút, trang sẽ tự reload khi server mới lên...",
"lastPluginUpdateFailed": "Lần cập nhật plugin trước thất bại ở bước \"{{step}}\": {{error}} — chạy /agenthub-update trong Claude Code để khắc phục."
}