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
+18 -6
View File
@@ -1,7 +1,7 @@
import os from 'node:os';
import path from 'node:path';
import { quoteForRemoteShell } from './ssh';
import type { RemoteRunResult, RemoteShellFlavor } from './ssh';
import type { RemoteRunResult, RemoteShellFlavor, RunSshOptions } from './ssh';
import type { SyncMergeSummary, SyncProgressEvent } from '../../../shared/sync/sync-events';
import type { SyncResultStatus } from '../../../shared/sync/sync-hosts-store';
@@ -45,9 +45,10 @@ export interface SyncFlowDeps {
host: string,
preferred: string | null,
flavor: RemoteShellFlavor,
runRemote?: (host: string, remoteCommand: string) => RemoteRunResult,
) => string;
runScp: (from: string, to: string) => void;
runSsh: (host: string, remoteCommand: string) => RemoteRunResult;
runSsh: (host: string, remoteCommand: string, options?: RunSshOptions) => RemoteRunResult;
fail: (message: string) => never;
log: (level: string, configuredLevel: string, message: string) => void;
canConnectUnixSocket: (socketPath: string) => Promise<boolean>;
@@ -198,17 +199,28 @@ export async function runCheckMode(context: SyncFlowContext, deps: SyncFlowDeps)
let remoteVersion: string | null = null;
let error: string | null = null;
const probe = deps.runSsh(host, 'echo subminer-check-ok');
const runCheck = (checkHost: string, command: string) =>
deps.runSsh(checkHost, command, {
batchMode: true,
connectTimeoutSeconds: 10,
timeoutMs: 15_000,
});
const probe = runCheck(host, 'echo subminer-check-ok');
const sshOk = probe.status === 0 && probe.stdout.includes('subminer-check-ok');
if (!sshOk) {
error = formatRemoteRunError(`Could not reach ${host} over SSH.`, probe);
} else {
deps.consoleLog('SSH connection: ok');
try {
const flavor = deps.detectRemoteShellFlavor(host, deps.runSsh);
const flavor = deps.detectRemoteShellFlavor(host, runCheck);
if (flavor !== 'posix') deps.consoleLog(`Remote platform: Windows (${flavor})`);
remoteCommand = deps.resolveRemoteSubminerCommand(host, args.syncRemoteCmd || null, flavor);
const version = deps.runSsh(host, `${remoteCommand} --version`);
remoteCommand = deps.resolveRemoteSubminerCommand(
host,
args.syncRemoteCmd || null,
flavor,
runCheck,
);
const version = runCheck(host, `${remoteCommand} --version`);
remoteVersion = version.status === 0 ? version.stdout.trim() || null : null;
deps.consoleLog(
`Remote subminer: ${remoteCommand}${remoteVersion ? ` (${remoteVersion})` : ''}`,