diff --git a/apps/electron/electron-builder.yml b/apps/electron/electron-builder.yml index 32234dd28..35eb5954d 100644 --- a/apps/electron/electron-builder.yml +++ b/apps/electron/electron-builder.yml @@ -60,7 +60,7 @@ files: extraMetadata: main: dist/main.cjs -# Auto-update is disabled until we have an owned update server. +# Auto-update publish metadata is generated from the active brand config. # Disable ASAR to avoid decompression overhead and click delays asar: false diff --git a/apps/electron/src/main/auto-update.ts b/apps/electron/src/main/auto-update.ts index 93bf9699c..b3628c2bb 100644 --- a/apps/electron/src/main/auto-update.ts +++ b/apps/electron/src/main/auto-update.ts @@ -1,9 +1,9 @@ /** * Auto-update module using electron-updater * - * Auto-update is disabled for this fork until we have an update server we own. - * Keep the public API in place so renderer/main callers fail closed instead of - * accidentally reaching the upstream Craft update feed. + * Auto-update is enabled only for packaged builds whose active brand declares + * a brand-owned update source. Development builds keep the same public API but + * skip network update checks. * * Platform behavior: * - macOS: Downloads zip, extracts and swaps app bundle atomically @@ -21,6 +21,7 @@ import * as path from 'path' import * as fs from 'fs' import { mainLog } from './logger' import { getAppVersion } from '@craft-agent/shared/version' +import { BRAND } from '@craft-agent/shared/branding' import { getDismissedUpdateVersion, clearDismissedUpdateVersion, @@ -33,7 +34,8 @@ import type { EventSink } from '@craft-agent/server-core/transport' const PLATFORM = platform() const IS_MAC = PLATFORM === 'darwin' const IS_WINDOWS = PLATFORM === 'win32' -const AUTO_UPDATE_ENABLED = false +const UPDATE_SOURCE = BRAND.updates +const AUTO_UPDATE_ENABLED = app.isPackaged && !!UPDATE_SOURCE // Get the update cache directory path (for file watcher fallback on macOS) // electron-updater uses these paths: @@ -112,11 +114,11 @@ function broadcastDownloadProgress(progress: number): void { // ─── Configure electron-updater ─────────────────────────────────────────────── -// Do not download or install updates until an owned update server is configured. +// Download updates only for packaged builds with a brand-owned update source. autoUpdater.autoDownload = AUTO_UPDATE_ENABLED -// Prevent cached upstream downloads from being applied on quit. -autoUpdater.autoInstallOnAppQuit = AUTO_UPDATE_ENABLED +autoUpdater.allowPrerelease = false +autoUpdater.allowDowngrade = false // Use the logger for electron-updater internal logging autoUpdater.logger = { @@ -316,6 +318,11 @@ function checkForExistingDownload(): { exists: boolean; version?: string } { */ export async function checkForUpdates(options: CheckOptions = {}): Promise { if (!AUTO_UPDATE_ENABLED) { + mainLog.info('[auto-update] Skipping update check', { + packaged: app.isPackaged, + brand: BRAND.id, + hasUpdateSource: !!UPDATE_SOURCE, + }) updateInfo = { available: false, currentVersion: getAppVersion(), @@ -327,6 +334,13 @@ export async function checkForUpdates(options: CheckOptions = {}): Promise { if (!AUTO_UPDATE_ENABLED) { - throw new Error('Auto-update is disabled') + throw new Error('Auto-update is not available for this build') } if (updateInfo.downloadState !== 'ready') { @@ -430,11 +444,20 @@ export interface UpdateOnLaunchResult { */ export async function checkForUpdatesOnLaunch(): Promise { if (!AUTO_UPDATE_ENABLED) { - mainLog.info('[auto-update] Skipping auto-update; disabled until an owned update server is configured') - return { action: 'skipped', reason: 'disabled' } + mainLog.info('[auto-update] Skipping launch update check', { + packaged: app.isPackaged, + brand: BRAND.id, + hasUpdateSource: !!UPDATE_SOURCE, + }) + return { action: 'skipped', reason: app.isPackaged ? 'unconfigured' : 'development' } } - mainLog.info('[auto-update] Checking for updates on launch...') + mainLog.info('[auto-update] Checking for updates on launch...', { + brand: BRAND.id, + provider: UPDATE_SOURCE.provider, + owner: UPDATE_SOURCE.owner, + repo: UPDATE_SOURCE.repo, + }) const info = await checkForUpdates({ autoDownload: true }) diff --git a/apps/electron/src/renderer/hooks/useUpdateChecker.ts b/apps/electron/src/renderer/hooks/useUpdateChecker.ts index a46923eea..2b2b33bdb 100644 --- a/apps/electron/src/renderer/hooks/useUpdateChecker.ts +++ b/apps/electron/src/renderer/hooks/useUpdateChecker.ts @@ -1,4 +1,4 @@ -import { useCallback } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { APP_VERSION } from '@craft-agent/shared/branding' import type { UpdateInfo } from '../../shared/types' @@ -11,6 +11,8 @@ interface UseUpdateCheckerResult { isDownloading: boolean /** Whether update is ready to install */ isReadyToInstall: boolean + /** Whether a manual update check is running */ + isChecking: boolean /** Download progress (0-100) */ downloadProgress: number /** Check for updates manually */ @@ -28,18 +30,84 @@ const DISABLED_UPDATE_INFO: UpdateInfo = { } export function useUpdateChecker(): UseUpdateCheckerResult { + const [updateInfo, setUpdateInfo] = useState(DISABLED_UPDATE_INFO) + const [isChecking, setIsChecking] = useState(false) + + useEffect(() => { + let cancelled = false + + window.electronAPI.getUpdateInfo() + .then((info) => { + if (!cancelled) setUpdateInfo(info) + }) + .catch((error) => { + if (cancelled) return + setUpdateInfo({ + ...DISABLED_UPDATE_INFO, + downloadState: 'error', + error: error instanceof Error ? error.message : 'Unable to read update state', + }) + }) + + const unsubscribeInfo = window.electronAPI.onUpdateAvailable((info) => { + setUpdateInfo(info) + }) + + const unsubscribeProgress = window.electronAPI.onUpdateDownloadProgress((progress) => { + setUpdateInfo((current) => ({ + ...current, + downloadState: current.downloadState === 'ready' ? 'ready' : 'downloading', + downloadProgress: progress, + })) + }) + + return () => { + cancelled = true + unsubscribeInfo() + unsubscribeProgress() + } + }, []) + const installUpdate = useCallback(async () => { - throw new Error('Auto-update is disabled') + try { + await window.electronAPI.installUpdate() + } catch (error) { + setUpdateInfo((current) => ({ + ...current, + downloadState: 'error', + error: error instanceof Error ? error.message : 'Unable to install update', + })) + throw error + } + }, []) + + const checkForUpdates = useCallback(async () => { + setIsChecking(true) + try { + const info = await window.electronAPI.checkForUpdates() + setUpdateInfo(info) + } catch (error) { + setUpdateInfo((current) => ({ + ...current, + downloadState: 'error', + error: error instanceof Error ? error.message : 'Unable to check for updates', + })) + } finally { + setIsChecking(false) + } }, []) - const checkForUpdates = useCallback(async () => undefined, []) + const derived = useMemo(() => ({ + updateAvailable: updateInfo.available, + isDownloading: updateInfo.downloadState === 'downloading', + isReadyToInstall: updateInfo.downloadState === 'ready', + downloadProgress: updateInfo.downloadProgress, + }), [updateInfo]) return { - updateInfo: DISABLED_UPDATE_INFO, - updateAvailable: false, - isDownloading: false, - isReadyToInstall: false, - downloadProgress: 0, + updateInfo, + ...derived, + isChecking, checkForUpdates, installUpdate, } diff --git a/apps/electron/src/renderer/pages/settings/AppSettingsPage.tsx b/apps/electron/src/renderer/pages/settings/AppSettingsPage.tsx index 42e513b09..ba6aa88be 100644 --- a/apps/electron/src/renderer/pages/settings/AppSettingsPage.tsx +++ b/apps/electron/src/renderer/pages/settings/AppSettingsPage.tsx @@ -6,6 +6,7 @@ * Settings: * - Notifications * - Network (proxy) + * - Updates * - About (version) * * Note: AI settings (connections, model, thinking) have been moved to AiSettingsPage. @@ -19,6 +20,7 @@ import { ScrollArea } from '@/components/ui/scroll-area' import { Button } from '@/components/ui/button' import { HeaderMenu } from '@/components/ui/HeaderMenu' import { routes } from '@/lib/navigate' +import { useUpdateChecker } from '@/hooks/useUpdateChecker' import { Spinner } from '@craft-agent/ui' import { APP_VERSION } from '@craft-agent/shared/branding' import type { DetailsPageMeta } from '@/lib/navigation-registry' @@ -103,6 +105,15 @@ export default function AppSettingsPage() { // Tools state const [browserToolEnabled, setBrowserToolEnabled] = useState(true) + const { + updateInfo, + isChecking, + isDownloading, + isReadyToInstall, + downloadProgress, + checkForUpdates, + installUpdate, + } = useUpdateChecker() // Proxy state const [proxyForm, setProxyForm] = useState(EMPTY_PROXY_FORM) @@ -185,6 +196,48 @@ export default function AppSettingsPage() { setProxyError(undefined) }, [savedProxyForm]) + const currentVersion = updateInfo?.currentVersion ?? APP_VERSION + const latestVersion = updateInfo?.latestVersion + const isInstallingUpdate = updateInfo?.downloadState === 'installing' + const updateActionDisabled = isChecking || isDownloading || isInstallingUpdate + const updateStatusDescription = (() => { + if (updateInfo?.downloadState === 'error') { + return updateInfo.error || t("settings.updates.errorDesc") + } + if (isInstallingUpdate) { + return t("settings.updates.installingDesc") + } + if (isReadyToInstall) { + return t("settings.updates.readyDesc", { version: latestVersion ?? '' }) + } + if (isDownloading) { + return t("settings.updates.downloadingDesc", { progress: downloadProgress }) + } + if (updateInfo?.available && latestVersion) { + return t("settings.updates.availableDesc", { version: latestVersion }) + } + if (latestVersion) { + return t("settings.updates.upToDateDesc", { version: currentVersion }) + } + return t("settings.updates.idleDesc") + })() + + const handleUpdateAction = useCallback(async () => { + if (isReadyToInstall) { + await installUpdate() + return + } + await checkForUpdates() + }, [checkForUpdates, installUpdate, isReadyToInstall]) + + const updateActionLabel = (() => { + if (isInstallingUpdate) return t("settings.updates.installing") + if (isReadyToInstall) return t("settings.updates.restartToUpdate") + if (isDownloading) return t("settings.updates.downloading") + if (isChecking) return t("settings.updates.checking") + return t("settings.updates.check") + })() + return (
} /> @@ -294,6 +347,56 @@ export default function AppSettingsPage() { + {/* Updates */} + + + + {(isChecking || isDownloading || isInstallingUpdate) && ( + + )} + {updateActionLabel} + + } + > + {currentVersion} + + {latestVersion && latestVersion !== currentVersion && ( + + {latestVersion} + + )} + {isDownloading && ( + +
+
+
+
+ + {downloadProgress}% + +
+ + )} + + + {/* About */} diff --git a/docs/plans/2026-06-08-desktop-auto-update-design.md b/docs/plans/2026-06-08-desktop-auto-update-design.md new file mode 100644 index 000000000..0cd193a55 --- /dev/null +++ b/docs/plans/2026-06-08-desktop-auto-update-design.md @@ -0,0 +1,68 @@ +# Desktop Auto-Update Design + +## Goal + +Enable stable-channel desktop auto-updates for packaged OpenWork and Qwen Code desktop builds using public GitHub Releases and `electron-updater`. + +## Scope + +The first version supports stable releases only. Draft releases, prereleases, nightly builds, staged rollouts, and forced updates are intentionally out of scope. + +## Pre-Implementation State + +Before this change, the desktop app already had most of the runtime surface: + +- `apps/electron/src/main/auto-update.ts` wraps `electron-updater`, broadcasts update state, and checks on launch when a packaged build has a brand update source. +- `apps/electron/src/main/handlers/system.ts` exposes update RPC handlers. +- `apps/electron/src/transport/channel-map.ts` exposes renderer API methods for update RPC and events. +- `apps/electron/src/renderer/hooks/useUpdateChecker.ts` returned a disabled stub. +- `apps/electron/electron-builder.yml` had no `publish` configuration, so release builds did not produce update feed metadata. + +## Brand-Owned Update Source + +Update source configuration belongs in `packages/shared/src/branding.ts` with the rest of desktop brand metadata. Each brand owns its release location: + +- `openwork` uses `modelstudioai/openwork`. +- `qwen-code` uses `QwenLM/qwen-code`. + +The brand config exposes a GitHub update source with `provider`, `owner`, `repo`, and `releasePageUrl`. `scripts/electron-builder-config.ts` reads it and emits the `publish` block in `apps/electron/electron-builder.generated.yml`. Runtime code reads the same brand update source for logs and fallback release-page actions. + +## Release Flow + +The existing desktop release workflow remains responsible for uploading assets to GitHub Releases. `electron-builder` should generate updater metadata, but the workflow continues to publish assets itself. + +Expected assets include platform installers and feed files: + +- macOS: zip, dmg, blockmaps, `latest-mac.yml` +- Windows: NSIS exe, blockmap, `latest.yml` +- Linux: AppImage, blockmap, Linux feed metadata if generated + +Only non-draft, non-prerelease GitHub Releases are expected to reach stable-channel users. + +## Runtime Flow + +Packaged desktop builds enable auto-update. Development builds stay disabled to avoid replacing local development apps. + +On launch, the app checks for updates and auto-downloads available stable updates. It does not force an immediate restart. Once a download is ready, the renderer can prompt the user to restart; if the user quits normally after a download is ready, `electron-updater` may apply the update on quit using its default behavior. Manual checks in Settings use the same update API and can show dismissed versions. + +## UI + +The first UI lives in `Settings > App` as an Updates section: + +- Show current version. +- Let users manually check for updates. +- Show download progress. +- Show a restart button once the update is ready. +- Show concise errors in Settings while logging details in the main process. + +Global interruption is deliberately minimal. Startup checks stay silent unless an update is ready. + +## Testing + +Use focused local checks first: + +- Generate builder config for `openwork` and `qwen-code`, verifying each brand emits the right `publish` config. +- Typecheck the Electron app. +- Run i18n parity checks after adding Settings copy. + +CI dry runs validate asset and metadata generation. A true update test requires installing an older packaged release, publishing a newer non-draft stable release, and confirming the old app downloads and installs the new version. diff --git a/docs/superpowers/plans/2026-06-08-desktop-auto-update.md b/docs/superpowers/plans/2026-06-08-desktop-auto-update.md new file mode 100644 index 000000000..0e96bc681 --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-desktop-auto-update.md @@ -0,0 +1,91 @@ +# Desktop Auto-Update Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Enable stable-channel desktop auto-updates with brand-owned GitHub Release feeds and a Settings UI for manual checks and restart-to-update. + +**Architecture:** Store update feed ownership in the existing brand config, generate `electron-builder` publish metadata from that config, enable `electron-updater` only in packaged builds with a configured feed, and connect the existing renderer update hook to the existing RPC channels. Keep the first version stable-only and user-initiated for installation. + +**Tech Stack:** Electron, electron-builder, electron-updater, React, TypeScript, i18next, GitHub Releases. + +--- + +### Task 1: Add Brand Update Feed Config + +**Files:** +- Modify: `packages/shared/src/branding.ts` +- Modify: `scripts/electron-builder-config.ts` + +- [x] Add an `updates` field to `BrandConfig`: + +```ts +updates?: { + provider: 'github'; + owner: string; + repo: string; + releasePageUrl: string; +}; +``` + +- [x] Configure `openwork` as `modelstudioai/openwork` and `qwen-code` as `QwenLM/qwen-code`. +- [x] Update `scripts/electron-builder-config.ts` so `config.publish` is generated from `BRAND.updates`. +- [x] Run `CRAFT_BRAND=openwork bun run electron:builder-config` and verify `apps/electron/electron-builder.generated.yml` contains the OpenWork GitHub provider. +- [x] Run `CRAFT_BRAND=qwen-code bun run electron:builder-config` and verify the generated config points to QwenLM/qwen-code. + +### Task 2: Enable Packaged Auto-Update Runtime + +**Files:** +- Modify: `apps/electron/src/main/auto-update.ts` + +- [x] Replace the hard disabled constant with a packaged-build enablement check based on `BRAND.updates`. +- [x] Set `autoUpdater.autoDownload` from that enablement check and keep the default `electron-updater` install-on-quit behavior. +- [x] Keep development builds disabled. +- [x] Add logs that identify the configured provider and repo when checks run. +- [x] Preserve current dismissed-version and quit-and-install behavior. + +### Task 3: Connect Renderer Hook to Existing RPC + +**Files:** +- Modify: `apps/electron/src/renderer/hooks/useUpdateChecker.ts` + +- [x] Load initial update info from `window.electronAPI.getUpdateInfo()`. +- [x] Subscribe to `onUpdateAvailable` and `onUpdateDownloadProgress`. +- [x] Implement `checkForUpdates()` through `window.electronAPI.checkForUpdates()`. +- [x] Implement `installUpdate()` through `window.electronAPI.installUpdate()`. +- [x] Track `isChecking` so the Settings button cannot issue duplicate checks. + +### Task 4: Add Settings App Update Controls + +**Files:** +- Modify: `apps/electron/src/renderer/pages/settings/AppSettingsPage.tsx` +- Modify: `packages/shared/src/i18n/locales/en.json` +- Modify: `packages/shared/src/i18n/locales/de.json` +- Modify: `packages/shared/src/i18n/locales/es.json` +- Modify: `packages/shared/src/i18n/locales/hu.json` +- Modify: `packages/shared/src/i18n/locales/ja.json` +- Modify: `packages/shared/src/i18n/locales/pl.json` +- Modify: `packages/shared/src/i18n/locales/zh-Hans.json` + +- [x] Add an Updates section near About. +- [x] Show current version, latest version when present, and download progress. +- [x] Add a primary button that maps state to check, checking, downloading, or restart-to-update. +- [x] Show concise error copy when `downloadState` is `error`. +- [x] Add matching i18n keys to every locale file so parity stays green. + +### Task 5: Verify + +**Files:** +- Test generated config and touched TypeScript. + +- [x] Run `bun run lint:i18n:parity`. +- [ ] Run `bun run typecheck:electron`. +- [x] Run `CRAFT_BRAND=openwork bun run electron:builder-config`. +- [x] Run `CRAFT_BRAND=qwen-code bun run electron:builder-config`. +- [x] Inspect generated `publish` blocks manually. +- [x] Do not commit generated `apps/electron/electron-builder.generated.yml` unless it is already tracked and intentionally changed. + +`bun run typecheck:electron` currently fails on pre-existing test typing issues outside this change: + +- `src/main/handlers/__tests__/settings-default-thinking.test.ts` +- `src/renderer/lib/__tests__/session-delete-navigation.test.ts` +- `src/renderer/lib/__tests__/skills-loading.test.ts` diff --git a/packages/shared/src/branding.ts b/packages/shared/src/branding.ts index 6704a1a16..3f085608f 100644 --- a/packages/shared/src/branding.ts +++ b/packages/shared/src/branding.ts @@ -29,6 +29,13 @@ export interface BrandConfig { selfReferName: string; /** Session viewer base URL */ viewerUrl: string; + /** Stable desktop auto-update source for packaged app builds. */ + updates?: { + provider: 'github'; + owner: string; + repo: string; + releasePageUrl: string; + }; /** Brand-owned external links shown in the Help menu */ helpMenuLinks: Array<{ labelKey: string; url: string; icon: string }>; /** Brand-specific Electron resource paths, relative to apps/electron/ */ @@ -72,6 +79,12 @@ const QWEN_CODE_BRAND: BrandConfig = { coAuthorLine: 'Co-Authored-By: Qwen Code ', selfReferName: 'Qwen Code', viewerUrl: 'https://agents.craft.do', + updates: { + provider: 'github', + owner: 'QwenLM', + repo: 'qwen-code', + releasePageUrl: 'https://github.com/QwenLM/qwen-code/releases', + }, helpMenuLinks: [ { labelKey: 'menu.homepage', @@ -106,6 +119,12 @@ const BRANDS: Record = { coAuthorLine: 'Co-Authored-By: OpenWork ', selfReferName: 'OpenWork', viewerUrl: 'https://agents.craft.do', + updates: { + provider: 'github', + owner: 'modelstudioai', + repo: 'openwork', + releasePageUrl: 'https://github.com/modelstudioai/openwork/releases', + }, helpMenuLinks: [ { labelKey: 'menu.homepage', diff --git a/packages/shared/src/i18n/locales/de.json b/packages/shared/src/i18n/locales/de.json index f4d02abc3..ad23be300 100644 --- a/packages/shared/src/i18n/locales/de.json +++ b/packages/shared/src/i18n/locales/de.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "Validierung fehlgeschlagen", "settings.app.description": "Benachrichtigungen und Updates", "settings.app.title": "App", + "settings.updates.availableDesc": "Version {{version}} ist verfügbar und wird heruntergeladen.", + "settings.updates.check": "Nach Updates suchen", + "settings.updates.checking": "Suche läuft...", + "settings.updates.currentVersion": "Aktuelle Version", + "settings.updates.downloadProgress": "Download-Fortschritt", + "settings.updates.downloading": "Wird heruntergeladen...", + "settings.updates.downloadingDesc": "Update wird heruntergeladen: {{progress}}%", + "settings.updates.errorDesc": "Updates konnten nicht geprüft werden.", + "settings.updates.idleDesc": "GitHub Releases nach der neuesten stabilen Version prüfen.", + "settings.updates.installing": "Neustart...", + "settings.updates.installingDesc": "Neustart zum Installieren des Updates.", + "settings.updates.latestVersion": "Neueste Version", + "settings.updates.readyDesc": "Version {{version}} ist bereit zur Installation.", + "settings.updates.restartToUpdate": "Neu starten und aktualisieren", + "settings.updates.title": "Updates", + "settings.updates.upToDateDesc": "Du bist auf Version {{version}} aktuell.", "settings.appearance.colorTheme": "Farbschema", "settings.appearance.commandsHeader": "Befehle", "settings.appearance.connectionIcons": "Verbindungssymbole", diff --git a/packages/shared/src/i18n/locales/en.json b/packages/shared/src/i18n/locales/en.json index 738f059ce..515571e0a 100644 --- a/packages/shared/src/i18n/locales/en.json +++ b/packages/shared/src/i18n/locales/en.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "Validation failed", "settings.app.description": "Notifications and updates", "settings.app.title": "App", + "settings.updates.availableDesc": "Version {{version}} is available and downloading.", + "settings.updates.check": "Check for updates", + "settings.updates.checking": "Checking...", + "settings.updates.currentVersion": "Current version", + "settings.updates.downloadProgress": "Download progress", + "settings.updates.downloading": "Downloading...", + "settings.updates.downloadingDesc": "Downloading update: {{progress}}%", + "settings.updates.errorDesc": "Unable to check for updates.", + "settings.updates.idleDesc": "Check GitHub Releases for the latest stable version.", + "settings.updates.installing": "Restarting...", + "settings.updates.installingDesc": "Restarting to install the update.", + "settings.updates.latestVersion": "Latest version", + "settings.updates.readyDesc": "Version {{version}} is ready to install.", + "settings.updates.restartToUpdate": "Restart to update", + "settings.updates.title": "Updates", + "settings.updates.upToDateDesc": "You are up to date on version {{version}}.", "settings.appearance.colorTheme": "Color theme", "settings.appearance.commandsHeader": "Commands", "settings.appearance.connectionIcons": "Connection icons", diff --git a/packages/shared/src/i18n/locales/es.json b/packages/shared/src/i18n/locales/es.json index 374876800..0ae1c140c 100644 --- a/packages/shared/src/i18n/locales/es.json +++ b/packages/shared/src/i18n/locales/es.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "Validación fallida", "settings.app.description": "Notificaciones y actualizaciones", "settings.app.title": "App", + "settings.updates.availableDesc": "La versión {{version}} está disponible y descargándose.", + "settings.updates.check": "Buscar actualizaciones", + "settings.updates.checking": "Buscando...", + "settings.updates.currentVersion": "Versión actual", + "settings.updates.downloadProgress": "Progreso de descarga", + "settings.updates.downloading": "Descargando...", + "settings.updates.downloadingDesc": "Descargando actualización: {{progress}}%", + "settings.updates.errorDesc": "No se pudieron buscar actualizaciones.", + "settings.updates.idleDesc": "Buscar la última versión estable en GitHub Releases.", + "settings.updates.installing": "Reiniciando...", + "settings.updates.installingDesc": "Reiniciando para instalar la actualización.", + "settings.updates.latestVersion": "Última versión", + "settings.updates.readyDesc": "La versión {{version}} está lista para instalarse.", + "settings.updates.restartToUpdate": "Reiniciar para actualizar", + "settings.updates.title": "Actualizaciones", + "settings.updates.upToDateDesc": "Ya tienes la versión {{version}}.", "settings.appearance.colorTheme": "Tema de color", "settings.appearance.commandsHeader": "Comandos", "settings.appearance.connectionIcons": "Iconos de conexión", diff --git a/packages/shared/src/i18n/locales/hu.json b/packages/shared/src/i18n/locales/hu.json index b004a93d5..9f613dd4f 100644 --- a/packages/shared/src/i18n/locales/hu.json +++ b/packages/shared/src/i18n/locales/hu.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "Az ellenőrzés sikertelen", "settings.app.description": "Értesítések és frissítések", "settings.app.title": "Alkalmazás", + "settings.updates.availableDesc": "A(z) {{version}} verzió elérhető, letöltés folyamatban.", + "settings.updates.check": "Frissítések keresése", + "settings.updates.checking": "Keresés...", + "settings.updates.currentVersion": "Jelenlegi verzió", + "settings.updates.downloadProgress": "Letöltési folyamat", + "settings.updates.downloading": "Letöltés...", + "settings.updates.downloadingDesc": "Frissítés letöltése: {{progress}}%", + "settings.updates.errorDesc": "Nem sikerült frissítéseket keresni.", + "settings.updates.idleDesc": "A legújabb stabil verzió keresése a GitHub Releases oldalon.", + "settings.updates.installing": "Újraindítás...", + "settings.updates.installingDesc": "Újraindítás a frissítés telepítéséhez.", + "settings.updates.latestVersion": "Legújabb verzió", + "settings.updates.readyDesc": "A(z) {{version}} verzió telepítésre kész.", + "settings.updates.restartToUpdate": "Újraindítás a frissítéshez", + "settings.updates.title": "Frissítések", + "settings.updates.upToDateDesc": "A(z) {{version}} verzió naprakész.", "settings.appearance.colorTheme": "Színtéma", "settings.appearance.commandsHeader": "Parancsok", "settings.appearance.connectionIcons": "Kapcsolat ikonok", diff --git a/packages/shared/src/i18n/locales/ja.json b/packages/shared/src/i18n/locales/ja.json index 75c7b3621..6c09b71d4 100644 --- a/packages/shared/src/i18n/locales/ja.json +++ b/packages/shared/src/i18n/locales/ja.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "検証に失敗しました", "settings.app.description": "通知とアップデート", "settings.app.title": "アプリ", + "settings.updates.availableDesc": "バージョン {{version}} を利用できます。ダウンロード中です。", + "settings.updates.check": "アップデートを確認", + "settings.updates.checking": "確認中...", + "settings.updates.currentVersion": "現在のバージョン", + "settings.updates.downloadProgress": "ダウンロード進行状況", + "settings.updates.downloading": "ダウンロード中...", + "settings.updates.downloadingDesc": "アップデートをダウンロード中: {{progress}}%", + "settings.updates.errorDesc": "アップデートを確認できませんでした。", + "settings.updates.idleDesc": "GitHub Releases で最新の安定版を確認します。", + "settings.updates.installing": "再起動中...", + "settings.updates.installingDesc": "アップデートをインストールするために再起動しています。", + "settings.updates.latestVersion": "最新バージョン", + "settings.updates.readyDesc": "バージョン {{version}} をインストールできます。", + "settings.updates.restartToUpdate": "再起動して更新", + "settings.updates.title": "アップデート", + "settings.updates.upToDateDesc": "バージョン {{version}} は最新です。", "settings.appearance.colorTheme": "カラーテーマ", "settings.appearance.commandsHeader": "コマンド", "settings.appearance.connectionIcons": "接続アイコン", diff --git a/packages/shared/src/i18n/locales/pl.json b/packages/shared/src/i18n/locales/pl.json index b3730f066..049c922ce 100644 --- a/packages/shared/src/i18n/locales/pl.json +++ b/packages/shared/src/i18n/locales/pl.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "Weryfikacja nie powiodła się", "settings.app.description": "Powiadomienia i aktualizacje", "settings.app.title": "Aplikacja", + "settings.updates.availableDesc": "Wersja {{version}} jest dostępna i jest pobierana.", + "settings.updates.check": "Sprawdź aktualizacje", + "settings.updates.checking": "Sprawdzanie...", + "settings.updates.currentVersion": "Obecna wersja", + "settings.updates.downloadProgress": "Postęp pobierania", + "settings.updates.downloading": "Pobieranie...", + "settings.updates.downloadingDesc": "Pobieranie aktualizacji: {{progress}}%", + "settings.updates.errorDesc": "Nie można sprawdzić aktualizacji.", + "settings.updates.idleDesc": "Sprawdź najnowszą stabilną wersję w GitHub Releases.", + "settings.updates.installing": "Ponowne uruchamianie...", + "settings.updates.installingDesc": "Ponowne uruchamianie w celu instalacji aktualizacji.", + "settings.updates.latestVersion": "Najnowsza wersja", + "settings.updates.readyDesc": "Wersja {{version}} jest gotowa do instalacji.", + "settings.updates.restartToUpdate": "Uruchom ponownie, aby zaktualizować", + "settings.updates.title": "Aktualizacje", + "settings.updates.upToDateDesc": "Masz aktualną wersję {{version}}.", "settings.appearance.colorTheme": "Motyw kolorystyczny", "settings.appearance.commandsHeader": "Polecenia", "settings.appearance.connectionIcons": "Ikony połączeń", diff --git a/packages/shared/src/i18n/locales/zh-Hans.json b/packages/shared/src/i18n/locales/zh-Hans.json index a460ccd4a..1879d2df7 100644 --- a/packages/shared/src/i18n/locales/zh-Hans.json +++ b/packages/shared/src/i18n/locales/zh-Hans.json @@ -751,6 +751,22 @@ "settings.ai.validationFailed": "验证失败", "settings.app.description": "通知和更新", "settings.app.title": "应用", + "settings.updates.availableDesc": "版本 {{version}} 可用,正在下载。", + "settings.updates.check": "检查更新", + "settings.updates.checking": "检查中...", + "settings.updates.currentVersion": "当前版本", + "settings.updates.downloadProgress": "下载进度", + "settings.updates.downloading": "下载中...", + "settings.updates.downloadingDesc": "正在下载更新:{{progress}}%", + "settings.updates.errorDesc": "无法检查更新。", + "settings.updates.idleDesc": "检查 GitHub Releases 上的最新稳定版本。", + "settings.updates.installing": "正在重启...", + "settings.updates.installingDesc": "正在重启以安装更新。", + "settings.updates.latestVersion": "最新版本", + "settings.updates.readyDesc": "版本 {{version}} 已准备好安装。", + "settings.updates.restartToUpdate": "重启并更新", + "settings.updates.title": "更新", + "settings.updates.upToDateDesc": "你已是最新版本 {{version}}。", "settings.appearance.colorTheme": "颜色主题", "settings.appearance.commandsHeader": "命令", "settings.appearance.connectionIcons": "连接图标", diff --git a/scripts/electron-builder-config.ts b/scripts/electron-builder-config.ts index 35b3f8e62..984e8e28f 100644 --- a/scripts/electron-builder-config.ts +++ b/scripts/electron-builder-config.ts @@ -48,5 +48,15 @@ const linux = section(config, 'linux'); linux.icon = BRAND.assets.linuxIcon; linux.artifactName = artifactName; +if (BRAND.updates) { + config.publish = { + provider: BRAND.updates.provider, + owner: BRAND.updates.owner, + repo: BRAND.updates.repo, + }; +} else { + delete config.publish; +} + writeFileSync(outputPath, yaml.dump(config, { lineWidth: -1 })); console.log(`Generated ${outputPath} for ${BRAND.id}`);