feat(launcher): add sync command to merge stats and history between machines over SSH

subminer sync <host> exchanges VACUUM INTO snapshots over ssh/scp and each
side merges the other's data as an insert-only union keyed on session UUIDs,
video keys, series title keys, and word/kanji identity. Lifetime totals and
daily/monthly rollups are applied incrementally so pre-retention history
survives, remote-only historical rollups are copied, and re-syncing is
idempotent. sync --snapshot/--merge expose the underlying steps for manual
transfers; a pid-file/mpv-socket guard refuses to run while SubMiner may be
writing the database and schema-version mismatches abort the merge.
This commit is contained in:
2026-07-08 23:28:10 -07:00
parent cdb1475a54
commit be31f96f02
21 changed files with 2762 additions and 0 deletions
+17
View File
@@ -199,6 +199,13 @@ export function createDefaultArgs(
texthookerOpenBrowser: false,
useRofi: false,
history: false,
sync: false,
syncHost: '',
syncSnapshotPath: '',
syncMergePath: '',
syncRemoteCmd: '',
syncDbPath: '',
syncForce: false,
logLevel: loggingConfig.level ?? 'warn',
logRotation: loggingConfig.rotation ?? 7,
passwordStore: '',
@@ -264,6 +271,16 @@ export function applyInvocationsToArgs(parsed: Args, invocations: CliInvocations
) {
fail('Dictionary target path is required.');
}
if (invocations.syncTriggered) {
parsed.sync = true;
parsed.syncHost = invocations.syncHost ?? '';
parsed.syncSnapshotPath = invocations.syncSnapshotPath ?? '';
parsed.syncMergePath = invocations.syncMergePath ?? '';
parsed.syncRemoteCmd = invocations.syncRemoteCmd ?? '';
parsed.syncDbPath = invocations.syncDbPath ?? '';
parsed.syncForce = invocations.syncForce;
if (invocations.syncLogLevel) parsed.logLevel = parseLogLevel(invocations.syncLogLevel);
}
if (invocations.doctorTriggered) parsed.doctor = true;
if (invocations.doctorRefreshKnownWords) parsed.doctorRefreshKnownWords = true;
if (invocations.logsTriggered && !invocations.logsExport) {