feat: add remote daemon self-update from client (#1513)

* feat: add remote daemon self-update from client

Adds a daemon.update.request RPC that lets the client trigger a remote
daemon update over WebSocket. The daemon runs npm update -g @getpaseo/cli,
reports progress via daemon_update_progress status events, then restarts
through the supervisor to load the new code.

New feature flag server_info.features.daemonSelfUpdate gates the UI.
The UpdateDaemonCard appears on the host settings page when the daemon
supports self-update and the app/daemon versions differ.

* fix: clean up progress subscription on unmount, use typed phase cast

Address Greptile review:
- P1: store unsubscribe ref and clean up on component unmount to prevent
  listener leak during in-flight update
- P2: use DaemonUpdateProgressStatusPayload type instead of { phase?: string }

* fix: use npm install@latest and global concurrency guard

Address Greptile review:
- npm update -g can silently exit 0 without resolving major-version
  gaps; switch to npm install -g @getpaseo/cli@latest
- Move daemonUpdateInProgress from per-session instance to module-level
  variable so concurrent clients share the same guard

* feat(daemon): harden remote self-update

Probe the global npm CLI install before updating, keep request handling in a daemon self-update session controller, and emit scoped daemon.update.progress messages.

* test(daemon): make self-update paths platform-stable

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
Davy
2026-06-26 16:13:29 +08:00
committed by GitHub
co-authored by Mohamed Boudra
parent 9ad49fca92
commit 40265782d4
26 changed files with 1536 additions and 8 deletions
+25
View File
@@ -1765,6 +1765,31 @@ export const ja: TranslationResources = {
"再起動リクエストの送信に失敗しました。Paseoは自動的に再接続します。ホストがオンラインになったら再試行してください。",
dialogFailedMessage: "再起動確認ダイアログを開けませんでした。",
},
update: {
title: "デーモンを更新",
hint: "デーモンを最新バージョンに更新して再起動します",
confirm: "更新",
confirmTitle: "{{name}}を更新",
confirmMessage:
"これによりデーモンが最新バージョンに更新され、再起動されます。実行中のエージェントは一時的に中断されます。",
updating: "更新中...",
phaseStarting: "更新を準備中...",
phaseDownloading: "パッケージをダウンロード中...",
phaseInstalling: "インストール中...",
phaseComplete: "更新が完了しました。再起動中...",
unableToReconnectTitle: "再接続できません",
unableToReconnectMessage:
"{{name}}が更新後にオンラインに戻りませんでした。デーモンが再起動されたことを確認してください。",
unavailableTitle: "ホストが利用できません",
unavailableMessage:
"このホストは接続されていません。更新する前にオンラインになるまでお待ちください。",
offlineTitle: "ホストオフライン",
offlineMessage:
"このホストはオフラインです。オンラインに戻るまで待ってから更新してください。",
requestFailedTitle: "更新に失敗しました",
requestFailedMessage: "デーモンの更新に失敗しました: {{error}}",
dialogFailedMessage: "更新確認ダイアログを開けませんでした。",
},
dangerZone: "危険ゾーン",
remove: {
title: "ホストを削除",