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:
@@ -58,6 +58,25 @@ export function createRealpathAwarePathMatcher(target: string): (candidate: stri
|
||||
};
|
||||
}
|
||||
|
||||
export function isPathInsideRoot(root: string, candidate: string): boolean {
|
||||
const compareAsWindows = shouldCompareAsWindows(root, candidate);
|
||||
const platformPath = compareAsWindows ? nodePath.win32 : nodePath.posix;
|
||||
const normalizedRoot = normalizePathForComparison(root, compareAsWindows);
|
||||
const normalizedCandidate = normalizePathForComparison(candidate, compareAsWindows);
|
||||
const relative = platformPath.relative(normalizedRoot, normalizedCandidate);
|
||||
|
||||
return relative === "" || (!relative.startsWith("..") && !platformPath.isAbsolute(relative));
|
||||
}
|
||||
|
||||
export function isRealpathInsideRoot(root: string, candidate: string): boolean {
|
||||
const rootVariants = collectPathVariants(root);
|
||||
const candidateVariants = collectPathVariants(candidate);
|
||||
|
||||
return rootVariants.some((rootVariant) =>
|
||||
candidateVariants.some((candidateVariant) => isPathInsideRoot(rootVariant, candidateVariant)),
|
||||
);
|
||||
}
|
||||
|
||||
function collectPathVariants(value: string): string[] {
|
||||
const variants = new Set<string>([value]);
|
||||
for (const realpath of resolveRealpathVariants(value)) {
|
||||
|
||||
Reference in New Issue
Block a user