Skip to content

Commit a03e558

Browse files
committed
fix: require explicit update downloads
Keep automatic update checks and the persistent update banner, but disable background update downloads so releases do not create uncontrolled traffic spikes. Remove the onboarding and settings auto-download controls because downloads are now always user-initiated. Tests: bun run typecheck; bun run build; git diff --check Codex: removed the auto-download preference and forced electron-updater downloads behind the explicit Update action.
1 parent 6ce0871 commit a03e558

5 files changed

Lines changed: 5 additions & 78 deletions

File tree

src/main/app-updater.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Event translation table (electron-updater → AppUpdateStatusJson.state):
55
* checking-for-update → "checking"
6-
* update-available → "available" (manual) or "downloading" (auto)
6+
* update-available → "available" (user chooses when to download)
77
* update-not-available → "up-to-date"
88
* download-progress → "downloading" (with 0–100 progress)
99
* update-downloaded → "ready" (updateReady → user can restart)
@@ -16,7 +16,6 @@
1616
import { app } from "electron";
1717
import pkg from "electron-updater";
1818
import type { AppUpdateStatusJson } from "../shared/rpc-schema";
19-
import { readSettings } from "./settings";
2019

2120
const { autoUpdater } = pkg;
2221

@@ -31,12 +30,6 @@ let lastError: string | null = null;
3130
let state: AppUpdateStatusJson["state"] = "idle";
3231
let updateDownloaded = false;
3332
let downloadPromise: Promise<string[]> | null = null;
34-
let autoDownloadUpdates = true;
35-
36-
function refreshAutoDownloadPreference(): void {
37-
autoDownloadUpdates = readSettings().auto_download_updates !== false;
38-
autoUpdater.autoDownload = autoDownloadUpdates;
39-
}
4033

4134
function snapshot(): AppUpdateStatusJson {
4235
return {
@@ -67,7 +60,7 @@ function setState(next: AppUpdateStatusJson["state"]): void {
6760
}
6861

6962
function wireAutoUpdaterEvents(): void {
70-
refreshAutoDownloadPreference();
63+
autoUpdater.autoDownload = false;
7164
autoUpdater.autoInstallOnAppQuit = true;
7265

7366
autoUpdater.on("checking-for-update", () => {
@@ -80,7 +73,7 @@ function wireAutoUpdaterEvents(): void {
8073
remoteVersion = info?.version ?? null;
8174
downloadProgress = null;
8275
updateDownloaded = false;
83-
setState(autoDownloadUpdates ? "downloading" : "available");
76+
setState("available");
8477
});
8578

8679
autoUpdater.on("update-not-available", () => {
@@ -122,16 +115,15 @@ export function initAppUpdater(
122115

123116
wireAutoUpdaterEvents();
124117

125-
// Fire-and-forget initial check, then poll every 6h.
126-
refreshAutoDownloadPreference();
118+
// Fire-and-forget initial check, then poll every 6h. Downloads are always
119+
// user-initiated to avoid uncontrolled release traffic spikes.
127120
void autoUpdater.checkForUpdates().catch((err) => {
128121
lastError = err?.message ?? String(err);
129122
setState("error");
130123
});
131124

132125
recheckTimer = setInterval(
133126
() => {
134-
refreshAutoDownloadPreference();
135127
void autoUpdater.checkForUpdates().catch((err) => {
136128
lastError = err?.message ?? String(err);
137129
setState("error");
@@ -160,7 +152,6 @@ export function getAppUpdateStatus(): AppUpdateStatusJson {
160152
export async function checkForUpdate(): Promise<AppUpdateStatusJson> {
161153
if (!app.isPackaged) return snapshot();
162154
try {
163-
refreshAutoDownloadPreference();
164155
await autoUpdater.checkForUpdates();
165156
} catch (err) {
166157
lastError = (err as Error)?.message ?? String(err);

src/mainview/components/OnboardingWizard.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export default function OnboardingWizard({
4242
);
4343

4444
const [step, setStep] = useState<Step>("welcome");
45-
const [autoDownloadUpdates, setAutoDownloadUpdates] = useState(true);
4645
const [popular, setPopular] = useState<MarketplaceSkillJson[] | null>(null);
4746
const [loadingPopular, setLoadingPopular] = useState(false);
4847
const [loadError, setLoadError] = useState<string | null>(null);
@@ -101,16 +100,6 @@ export default function OnboardingWizard({
101100
} catch {
102101
/* ignore */
103102
}
104-
void invoke("read_settings")
105-
.then((settings) =>
106-
invoke("write_settings", {
107-
settings: {
108-
...settings,
109-
auto_download_updates: autoDownloadUpdates,
110-
},
111-
}),
112-
)
113-
.catch(() => {});
114103
onClose();
115104
}
116105

@@ -411,23 +400,6 @@ export default function OnboardingWizard({
411400
</button>
412401
</div>
413402

414-
<label className="mx-auto flex max-w-md cursor-pointer select-none items-start gap-2.5 rounded-lg border border-border/40 bg-muted/5 px-3 py-2.5 transition hover:bg-black/[0.03] dark:hover:bg-white/[0.04]">
415-
<input
416-
type="checkbox"
417-
checked={autoDownloadUpdates}
418-
onChange={(e) => setAutoDownloadUpdates(e.target.checked)}
419-
className="mt-0.5 size-3.5 rounded accent-primary"
420-
/>
421-
<span className="min-w-0">
422-
<span className="block text-xs font-medium text-foreground">
423-
{t("onboarding.autoDownloadUpdatesTitle")}
424-
</span>
425-
<span className="mt-0.5 block text-[11px] leading-relaxed text-muted-foreground">
426-
{t("onboarding.autoDownloadUpdatesBody")}
427-
</span>
428-
</span>
429-
</label>
430-
431403
<div className="flex justify-end">
432404
<Button variant="outline" size="sm" onClick={markDoneAndClose}>
433405
{t("onboarding.finish")}

src/mainview/i18n/en.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,6 @@ const en = {
329329
updateBannerAction: "Update",
330330
updateBannerUpdating: "Updating",
331331
updateBannerDismiss: "Dismiss update notification",
332-
updateAutoDownload: "Download app updates automatically",
333-
updateAutoDownloadDescription:
334-
"Skiller downloads new versions in the background and asks before restarting.",
335332
// Marketplace Cache
336333
onboardingTitle: "Onboarding",
337334
onboardingDescription:
@@ -481,9 +478,6 @@ const en = {
481478
noDescription: "No description available — check the repo before installing.",
482479
doneTitle: "You're ready",
483480
doneBody: "Jump straight into the app — here's where to go first:",
484-
autoDownloadUpdatesTitle: "Download app updates automatically",
485-
autoDownloadUpdatesBody:
486-
"Skiller will fetch new versions in the background, then ask before restarting.",
487481
ctaSkillsTitle: "Manage skills",
488482
ctaSkillsBody: "See what's installed, copy a skill to another agent, or clean house.",
489483
ctaMarketplaceTitle: "Open Marketplace",

src/mainview/pages/Settings.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ interface AppSettings {
2929
path_overrides: Record<string, string[]> | null
3030
close_action: string | null
3131
analytics_enabled?: boolean | null
32-
auto_download_updates?: boolean | null
3332
macos_window_blur?: boolean | null
3433
assumed_listing_char_budget?: number | null
3534
assumed_context_window_chars?: number | null
@@ -41,7 +40,6 @@ const DEFAULT_SETTINGS: AppSettings = {
4140
path_overrides: null,
4241
close_action: null,
4342
analytics_enabled: null,
44-
auto_download_updates: null,
4543
macos_window_blur: null,
4644
}
4745

@@ -602,32 +600,6 @@ export default function SettingsPage() {
602600
</div>
603601
</div>
604602

605-
<label className="flex cursor-pointer select-none items-start gap-3 rounded-lg border border-border/40 bg-muted/5 px-3 py-2.5 transition hover:bg-black/[0.03] dark:hover:bg-white/[0.04]">
606-
<input
607-
type="checkbox"
608-
checked={settings?.auto_download_updates !== false}
609-
onChange={(e) => {
610-
const enabled = e.target.checked
611-
saveMutation.mutate({
612-
...(settings ?? DEFAULT_SETTINGS),
613-
auto_download_updates: enabled,
614-
})
615-
if (enabled && updateStatus?.state === 'available') {
616-
void handleUpdateDownload()
617-
}
618-
}}
619-
className="mt-0.5 size-3.5 rounded accent-primary"
620-
/>
621-
<span className="min-w-0">
622-
<span className="block text-xs font-medium text-foreground">
623-
{t('settings.updateAutoDownload')}
624-
</span>
625-
<span className="mt-0.5 block text-xs leading-relaxed text-muted-foreground">
626-
{t('settings.updateAutoDownloadDescription')}
627-
</span>
628-
</span>
629-
</label>
630-
631603
{/* Download progress bar — the percentage is already in the
632604
* subtitle, but a visual bar gives actual feedback on long
633605
* downloads (200+ MB DMG can take minutes on slow networks).

src/shared/rpc-schema.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ export type AppSettingsJson = {
9797
assumed_context_window_chars?: number | null;
9898
/** Product telemetry and analytics (PostHog). Default true when omitted. */
9999
analytics_enabled?: boolean | null;
100-
/** Download app updates automatically after they are found. Default true when omitted. */
101-
auto_download_updates?: boolean | null;
102100
/** One-time GitHub star prompt cadence metadata. */
103101
github_star_prompt?: {
104102
first_seen_at?: string | null;

0 commit comments

Comments
 (0)