feat(sync-ui): defer app quit until async cleanup resolves

- check-host participates in active-run coordination and can be cancelled
- shutdown() cancels and awaits the active launcher run on quit
- onWillQuit calls preventDefault and re-triggers quit once cleanup settles
- Snapshot mode awaits tracker quiescent before writing output
- Auto-scheduler catches synchronous triggerHostSync failures
- SCP endpoint accepts Windows absolute paths; cmd.exe rejects % in quoted values
- runAppCommand unified (inherit vs pipe stdio) in launcher/mpv.ts
- Docs: add --check, --json, --ui, --sync-cli, --make-temp/--remove-temp examples
This commit is contained in:
2026-07-12 03:35:39 -07:00
parent f8c10edce0
commit a013a7ea55
30 changed files with 356 additions and 135 deletions
+8 -4
View File
@@ -145,11 +145,12 @@ export function withJsonEvents(deps: SyncFlowDeps): SyncFlowDeps {
};
}
export function runSnapshotMode(
export async function runSnapshotMode(
context: SyncFlowContext,
dbPath: string,
deps: SyncFlowDeps,
): void {
): Promise<void> {
await deps.ensureTrackerQuiescent(context, dbPath);
const outPath = deps.resolvePath(context.args.syncSnapshotPath);
deps.emitEvent({
type: 'stage',
@@ -409,7 +410,10 @@ export async function runHostSync(
}
}
export async function runSyncFlow(context: SyncFlowContext, inputDeps: SyncFlowDeps): Promise<boolean> {
export async function runSyncFlow(
context: SyncFlowContext,
inputDeps: SyncFlowDeps,
): Promise<boolean> {
let deps = inputDeps;
const { args } = context;
if (!args.sync) return false;
@@ -430,7 +434,7 @@ export async function runSyncFlow(context: SyncFlowContext, inputDeps: SyncFlowD
if (args.syncCheck) {
await runCheckMode(context, deps);
} else if (args.syncSnapshotPath) {
runSnapshotMode(context, dbPath, deps);
await runSnapshotMode(context, dbPath, deps);
} else if (args.syncMergePath) {
await runMergeMode(context, dbPath, deps);
} else if (args.syncHost) {