fix(sync): harden detached launch, bounded --check probes, and pipe-clos

- `sync --ui` now launches detached; closing standalone Sync window exits app
- `--check` uses BatchMode + ConnectTimeout + 15s timeoutMs on all SSH calls
- `runSyncLauncher` settles on `exit` (not just `close`) for Electron pipe inheritance
- Added `timeoutMs` to `runSyncLauncher`; check-host passes 30s deadline
- `handleSyncCliAtEntry` wraps exit deps for testability; fixes Linux AppImage GUI fallthrough
- `config-settings-window` gains optional `onClosed` hook
- Added preload-syncui bundle to `build:syncui` script
This commit is contained in:
2026-07-12 19:42:34 -07:00
parent a4c12165af
commit 5d8673f299
20 changed files with 246 additions and 68 deletions
+31 -6
View File
@@ -10,10 +10,7 @@ import {
parseSyncCliTokens,
syncCliUsage,
} from '../core/services/stats-sync/cli-args';
import {
createDbSnapshot,
findLiveStatsDaemonPid,
} from '../core/services/stats-sync/shared';
import { createDbSnapshot, findLiveStatsDaemonPid } from '../core/services/stats-sync/shared';
import { formatMergeSummary, mergeSnapshotIntoDb } from '../core/services/stats-sync/merge';
import {
assertSafeSshHost,
@@ -28,9 +25,17 @@ import {
runSyncFlow,
type SyncFlowDeps,
} from '../core/services/stats-sync/sync-flow';
import { recordSyncResult, readSyncHostsState, writeSyncHostsState, getSyncHostsPath } from '../shared/sync/sync-hosts-store';
import {
recordSyncResult,
readSyncHostsState,
writeSyncHostsState,
getSyncHostsPath,
} from '../shared/sync/sync-hosts-store';
export function shouldHandleSyncCliAtEntry(argv: readonly string[], env: NodeJS.ProcessEnv): boolean {
export function shouldHandleSyncCliAtEntry(
argv: readonly string[],
env: NodeJS.ProcessEnv,
): boolean {
if (env.ELECTRON_RUN_AS_NODE === '1') return false;
return extractSyncCliTokens(argv) !== null;
}
@@ -188,3 +193,23 @@ export async function runSyncCliFromProcess(
return 1;
}
}
export async function handleSyncCliAtEntry(
argv: readonly string[],
env: NodeJS.ProcessEnv,
appVersion: string,
deps: {
run: typeof runSyncCliFromProcess;
exit: (code: number) => void;
} = {
run: runSyncCliFromProcess,
exit: (code) => process.exit(code),
},
): Promise<boolean> {
if (!shouldHandleSyncCliAtEntry(argv, env)) return false;
const exitCode = await deps.run(argv, appVersion);
// This path runs before app readiness and must not call app.exit(): on Linux
// that can throw and make the entrypoint fall back to full GUI startup.
deps.exit(exitCode);
return true;
}