@narumitw/pi-sync 0.54.1 → 0.54.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -11
- package/dist/chunks/{chunk-ORQRHEEO.ts → chunk-26PMXY7E.ts} +3 -3
- package/dist/chunks/{chunk-SDZCTCYQ.ts → chunk-2FETCGGT.ts} +2 -2
- package/dist/chunks/{chunk-HUTYAPFP.ts → chunk-35GM6XKM.ts} +275 -151
- package/dist/chunks/chunk-35GM6XKM.ts.map +7 -0
- package/dist/chunks/{chunk-V6VZ5TLG.ts → chunk-A3TFXY4O.ts} +2 -2
- package/dist/chunks/{chunk-5RZPINUQ.ts → chunk-AAZDBIBJ.ts} +2 -2
- package/dist/chunks/{chunk-5ZWQ33XL.ts → chunk-CAGJOKB7.ts} +5 -5
- package/dist/chunks/{chunk-QE7NZZWT.ts → chunk-E6ONFG57.ts} +15 -2
- package/dist/chunks/{chunk-QE7NZZWT.ts.map → chunk-E6ONFG57.ts.map} +2 -2
- package/dist/chunks/{chunk-IJ6VV6FN.ts → chunk-GC3QOL6J.ts} +2 -2
- package/dist/chunks/{chunk-VHXBCEVF.ts → chunk-HMXUKU43.ts} +3 -3
- package/dist/chunks/{chunk-4SBTAVFL.ts → chunk-I4JYVFUS.ts} +5 -6
- package/dist/chunks/chunk-I4JYVFUS.ts.map +7 -0
- package/dist/chunks/{chunk-2DI6PDZV.ts → chunk-ITFR6NNQ.ts} +14 -2
- package/dist/chunks/chunk-ITFR6NNQ.ts.map +7 -0
- package/dist/chunks/{chunk-3B4Z2SKV.ts → chunk-KATKRR3D.ts} +18 -11
- package/dist/chunks/chunk-KATKRR3D.ts.map +7 -0
- package/dist/chunks/{chunk-ANWNQIR7.ts → chunk-KGE47GCZ.ts} +4 -4
- package/dist/chunks/chunk-KGE47GCZ.ts.map +7 -0
- package/dist/chunks/{chunk-CHSIAC4N.ts → chunk-R35N4CL3.ts} +2 -2
- package/dist/chunks/chunk-UZPMDLTL.ts +54 -0
- package/dist/chunks/chunk-UZPMDLTL.ts.map +7 -0
- package/dist/chunks/{chunk-4QVPTIZB.ts → chunk-VCL7TZYW.ts} +2 -2
- package/dist/chunks/{chunk-WFKUKVCP.ts → chunk-XU2VXGQV.ts} +14 -8
- package/dist/chunks/chunk-XU2VXGQV.ts.map +7 -0
- package/dist/chunks/{chunk-UQ3ALBIB.ts → chunk-YTHQEW35.ts} +3 -3
- package/dist/chunks/{conflict-artifacts-BB2FZMIE.ts → conflict-artifacts-4CZFB7WI.ts} +3 -3
- package/dist/chunks/{conflict-review-JRFJIBEN.ts → conflict-review-ZOTOHL2E.ts} +11 -10
- package/dist/chunks/{conflict-review-JRFJIBEN.ts.map → conflict-review-ZOTOHL2E.ts.map} +1 -1
- package/dist/chunks/{file-selection-WZX7HAY2.ts → file-selection-UNHBVVON.ts} +3 -3
- package/dist/chunks/{manager-result-dispatcher-6MQKFCWB.ts → manager-result-dispatcher-HVRTPEVK.ts} +7 -7
- package/dist/chunks/{manager-ui-IURS4WXY.ts → manager-ui-ZIN4BUSF.ts} +297 -136
- package/dist/chunks/manager-ui-ZIN4BUSF.ts.map +7 -0
- package/dist/chunks/{merge-baseline-store-EVQAAML6.ts → merge-baseline-store-ICXVTCML.ts} +5 -3
- package/dist/chunks/{remote-selection-ui-6LJJ4IMQ.ts → remote-selection-ui-H6AEVA3Q.ts} +6 -6
- package/dist/chunks/{s3-backend-AQGV6LFT.ts → s3-backend-Y4P3O5B5.ts} +2 -2
- package/dist/chunks/{setup-switch-KTUWL6DE.ts → setup-switch-ZMW5C4M7.ts} +4 -4
- package/dist/chunks/{setup-wizard-GSDPDFC4.ts → setup-wizard-VW7F3JN4.ts} +5 -5
- package/dist/chunks/{sync-inspection-K45QVLHB.ts → sync-inspection-3EMS2J5B.ts} +5 -4
- package/dist/chunks/{sync-operations-QQ2GXKVC.ts → sync-operations-2CWST333.ts} +140 -24
- package/dist/chunks/sync-operations-2CWST333.ts.map +7 -0
- package/dist/index.ts +103 -68
- package/dist/index.ts.map +2 -2
- package/docs/settings.md +47 -10
- package/docs/tui-contract.md +61 -0
- package/package.json +1 -1
- package/src/commands/command-execution.ts +2 -1
- package/src/commands/command-types.ts +4 -0
- package/src/commands/command.ts +3 -1
- package/src/settings/config.ts +13 -10
- package/src/settings/settings-store.ts +1 -0
- package/src/settings/settings-types.ts +2 -0
- package/src/settings/settings-validation.ts +3 -0
- package/src/snapshot/snapshot-transaction.ts +4 -2
- package/src/state/merge-baseline-store.ts +18 -0
- package/src/sync/apply-review.ts +63 -0
- package/src/sync/automatic-sync.ts +45 -33
- package/src/sync/merged-sync.ts +202 -104
- package/src/sync/startup-check.ts +39 -21
- package/src/sync/startup-timings.ts +90 -0
- package/src/sync/sync-inspection.ts +8 -4
- package/src/sync/sync-mutations.ts +49 -11
- package/src/sync/sync-queries.ts +14 -3
- package/src/ui/manager-state.ts +10 -6
- package/src/ui/manager-ui.ts +57 -7
- package/src/ui/merge-review.ts +3 -3
- package/src/ui/merged-sync-summary.ts +2 -18
- package/src/ui/settings-frame.ts +55 -0
- package/src/ui/settings-ui.ts +141 -43
- package/src/ui/setup/setup-prompts.ts +4 -3
- package/src/ui/sync-behavior.ts +15 -0
- package/src/ui/sync-format.ts +69 -38
- package/src/ui/transfer-changes.ts +33 -0
- package/src/ui/transfer-review.ts +27 -0
- package/dist/chunks/chunk-2DI6PDZV.ts.map +0 -7
- package/dist/chunks/chunk-3B4Z2SKV.ts.map +0 -7
- package/dist/chunks/chunk-4SBTAVFL.ts.map +0 -7
- package/dist/chunks/chunk-ANWNQIR7.ts.map +0 -7
- package/dist/chunks/chunk-HUTYAPFP.ts.map +0 -7
- package/dist/chunks/chunk-WFKUKVCP.ts.map +0 -7
- package/dist/chunks/manager-ui-IURS4WXY.ts.map +0 -7
- package/dist/chunks/sync-operations-QQ2GXKVC.ts.map +0 -7
- /package/dist/chunks/{chunk-ORQRHEEO.ts.map → chunk-26PMXY7E.ts.map} +0 -0
- /package/dist/chunks/{chunk-SDZCTCYQ.ts.map → chunk-2FETCGGT.ts.map} +0 -0
- /package/dist/chunks/{chunk-V6VZ5TLG.ts.map → chunk-A3TFXY4O.ts.map} +0 -0
- /package/dist/chunks/{chunk-5RZPINUQ.ts.map → chunk-AAZDBIBJ.ts.map} +0 -0
- /package/dist/chunks/{chunk-5ZWQ33XL.ts.map → chunk-CAGJOKB7.ts.map} +0 -0
- /package/dist/chunks/{chunk-IJ6VV6FN.ts.map → chunk-GC3QOL6J.ts.map} +0 -0
- /package/dist/chunks/{chunk-VHXBCEVF.ts.map → chunk-HMXUKU43.ts.map} +0 -0
- /package/dist/chunks/{chunk-CHSIAC4N.ts.map → chunk-R35N4CL3.ts.map} +0 -0
- /package/dist/chunks/{chunk-4QVPTIZB.ts.map → chunk-VCL7TZYW.ts.map} +0 -0
- /package/dist/chunks/{chunk-UQ3ALBIB.ts.map → chunk-YTHQEW35.ts.map} +0 -0
- /package/dist/chunks/{conflict-artifacts-BB2FZMIE.ts.map → conflict-artifacts-4CZFB7WI.ts.map} +0 -0
- /package/dist/chunks/{file-selection-WZX7HAY2.ts.map → file-selection-UNHBVVON.ts.map} +0 -0
- /package/dist/chunks/{manager-result-dispatcher-6MQKFCWB.ts.map → manager-result-dispatcher-HVRTPEVK.ts.map} +0 -0
- /package/dist/chunks/{merge-baseline-store-EVQAAML6.ts.map → merge-baseline-store-ICXVTCML.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-6LJJ4IMQ.ts.map → remote-selection-ui-H6AEVA3Q.ts.map} +0 -0
- /package/dist/chunks/{s3-backend-AQGV6LFT.ts.map → s3-backend-Y4P3O5B5.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-KTUWL6DE.ts.map → setup-switch-ZMW5C4M7.ts.map} +0 -0
- /package/dist/chunks/{setup-wizard-GSDPDFC4.ts.map → setup-wizard-VW7F3JN4.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-K45QVLHB.ts.map → sync-inspection-3EMS2J5B.ts.map} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
3
|
import {
|
|
4
|
-
AUTOMATIC_SYNC_DESCRIPTION,
|
|
5
4
|
applyS3CredentialUpdate,
|
|
6
5
|
chooseS3CredentialUpdate,
|
|
7
6
|
chooseS3Credentials,
|
|
@@ -20,33 +19,33 @@ import {
|
|
|
20
19
|
showEditS3Target,
|
|
21
20
|
showEditWebDavStorageProfile,
|
|
22
21
|
showEditWebDavTarget
|
|
23
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-I4JYVFUS.ts";
|
|
24
23
|
import {
|
|
25
24
|
dispatchManagerResult
|
|
26
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HMXUKU43.ts";
|
|
27
26
|
import {
|
|
28
27
|
runCancellableOperation
|
|
29
|
-
} from "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-CAGJOKB7.ts";
|
|
29
|
+
import "./chunk-A3TFXY4O.ts";
|
|
30
|
+
import "./chunk-2FETCGGT.ts";
|
|
32
31
|
import {
|
|
33
32
|
SETUP_SWITCH_ACTION_OPTIONS,
|
|
34
33
|
saveOnSwitch,
|
|
35
34
|
setupSwitchActionFromLabel,
|
|
36
35
|
setupSwitchActionLabel,
|
|
37
36
|
useSyncSetup
|
|
38
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-YTHQEW35.ts";
|
|
39
38
|
import {
|
|
40
39
|
observationMatchesConfig,
|
|
41
40
|
observationSummary,
|
|
42
41
|
syncAttentionMatchesConfig
|
|
43
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-AAZDBIBJ.ts";
|
|
44
43
|
import {
|
|
45
44
|
captureMutationOwner,
|
|
46
45
|
inspectLock,
|
|
47
46
|
isLockGuardHeld,
|
|
48
47
|
isStaleLock
|
|
49
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-GC3QOL6J.ts";
|
|
50
49
|
import "./chunk-PWMWOJP4.ts";
|
|
51
50
|
import {
|
|
52
51
|
automaticSyncSummary
|
|
@@ -54,14 +53,18 @@ import {
|
|
|
54
53
|
import {
|
|
55
54
|
configureSyncStatus
|
|
56
55
|
} from "./chunk-K3RIEL6J.ts";
|
|
57
|
-
import
|
|
56
|
+
import {
|
|
57
|
+
AUTOMATIC_SYNC_DESCRIPTION,
|
|
58
|
+
STARTUP_TRANSFER_DESCRIPTION,
|
|
59
|
+
startupTransferSummary
|
|
60
|
+
} from "./chunk-ITFR6NNQ.ts";
|
|
58
61
|
import {
|
|
59
62
|
safeTerminalText
|
|
60
63
|
} from "./chunk-AMJRUXFT.ts";
|
|
61
64
|
import {
|
|
62
65
|
readStateForConfig,
|
|
63
66
|
syncStateFingerprint
|
|
64
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-VCL7TZYW.ts";
|
|
65
68
|
import "./chunk-BVVP33U5.ts";
|
|
66
69
|
import {
|
|
67
70
|
addStorageConnection,
|
|
@@ -69,7 +72,7 @@ import {
|
|
|
69
72
|
removeSyncSetup,
|
|
70
73
|
updateStorageConnection,
|
|
71
74
|
updateSyncSetup
|
|
72
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-R35N4CL3.ts";
|
|
73
76
|
import {
|
|
74
77
|
activeLocalConfigPath,
|
|
75
78
|
isCloudflareR2Endpoint,
|
|
@@ -80,9 +83,10 @@ import {
|
|
|
80
83
|
normalizeS3Endpoint,
|
|
81
84
|
ownRecord,
|
|
82
85
|
readLocalConfigObject,
|
|
86
|
+
syncCheckConfigFingerprint,
|
|
83
87
|
syncConfigReviewIdentity,
|
|
84
88
|
updateLocalConfig
|
|
85
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-KATKRR3D.ts";
|
|
86
90
|
import "./chunk-2JZFHNLM.ts";
|
|
87
91
|
import "./chunk-T2SJD3N7.ts";
|
|
88
92
|
import "./chunk-ZX2Q6THB.ts";
|
|
@@ -172,7 +176,7 @@ function blockedSyncMenuItem(label, manager) {
|
|
|
172
176
|
};
|
|
173
177
|
}
|
|
174
178
|
async function showManagerAttention(ctx, attention, runRoute, signal, onSelectionResolved) {
|
|
175
|
-
const { showRemoteSelectionReview } = await import("./remote-selection-ui-
|
|
179
|
+
const { showRemoteSelectionReview } = await import("./remote-selection-ui-H6AEVA3Q.ts");
|
|
176
180
|
if (signal?.aborted) return "close";
|
|
177
181
|
const review = await showRemoteSelectionReview(ctx, attention.decision.setupName, signal, void 0, {
|
|
178
182
|
decision: attention.decision,
|
|
@@ -453,6 +457,8 @@ var MAIN_MENU_ACTIONS = [
|
|
|
453
457
|
"Sync now",
|
|
454
458
|
"Pull from remote\u2026",
|
|
455
459
|
"Push to remote\u2026",
|
|
460
|
+
"View differences",
|
|
461
|
+
"Check remote status",
|
|
456
462
|
"Settings",
|
|
457
463
|
"History",
|
|
458
464
|
"Diagnostics"
|
|
@@ -514,7 +520,7 @@ async function describeManagerState(signal, attention, inspectOperation = inspec
|
|
|
514
520
|
const noSyncedContent = config.include.length === 0;
|
|
515
521
|
const syncState = changesBlocked ? void 0 : await readStateForConfig(config).catch(() => void 0);
|
|
516
522
|
const currentObservation = observation && observationMatchesConfig(observation, config) && syncState && syncStateFingerprint(syncState) === observation.inspection.stateIdentity ? observation : void 0;
|
|
517
|
-
const lastAppliedSnapshot = changesBlocked ? "Unavailable while operations are locked" : syncState?.lastAppliedSnapshot ? safeTerminalText(syncState.lastAppliedSnapshot) : syncState ? "Never synced" : "Unavailable";
|
|
523
|
+
const lastAppliedSnapshot = changesBlocked ? "Unavailable while operations are locked" : syncState?.lastAppliedSnapshot ? `${truncateToWidth(safeTerminalText(syncState.lastAppliedSnapshot), 20, "\u2026")} (application time unavailable; full ID in History)` : syncState ? "Never synced" : "Unavailable";
|
|
518
524
|
const canSwitch = await countValidSyncSetups(configuredTargets, signal) > 1;
|
|
519
525
|
const mainActions = [...MAIN_MENU_ACTIONS];
|
|
520
526
|
if (canSwitch) mainActions.push("Switch sync setup");
|
|
@@ -524,10 +530,7 @@ async function describeManagerState(signal, attention, inspectOperation = inspec
|
|
|
524
530
|
"",
|
|
525
531
|
`Current sync setup: ${safeTerminalText(config.setupName)}`,
|
|
526
532
|
`Storage: ${backendStorageDescription(config)}`,
|
|
527
|
-
|
|
528
|
-
`Automatic sync: ${automaticSyncSummary(config.automatic)}`,
|
|
529
|
-
`Automatic transfer at startup: ${config.automaticTransfer ? "On \u2014 upload/replace/delete at idle; no reload" : "Off"}`,
|
|
530
|
-
`Last applied: ${lastAppliedSnapshot}`,
|
|
533
|
+
"CURRENT STATE",
|
|
531
534
|
...syncState?.unresolved?.length ? [
|
|
532
535
|
`Unresolved: ${syncState.unresolved.length} dependency groups \xB7 choose Review unresolved conflicts`,
|
|
533
536
|
`Last observed: ${safeTerminalText(syncState.lastObservedSnapshot ?? "unknown")}`
|
|
@@ -540,9 +543,13 @@ async function describeManagerState(signal, attention, inspectOperation = inspec
|
|
|
540
543
|
`Startup check (${safeTerminalText(currentObservation.checkedAt)}): ${observationSummary(currentObservation)}`,
|
|
541
544
|
"Check-time observation only; synchronization rechecks current content."
|
|
542
545
|
] : ["Remote status: Not checked"],
|
|
546
|
+
`Included: ${selection.builtIns.length} built-in group${selection.builtIns.length === 1 ? "" : "s"} \xB7 ${selection.custom.length} extra path${selection.custom.length === 1 ? "" : "s"} \xB7 Sessions ${selection.sessions ? "on" : "off"}`,
|
|
547
|
+
`Automatic sync: ${automaticSyncSummary(config.automatic)}`,
|
|
548
|
+
`Startup transfer: ${startupTransferSummary(config)}`,
|
|
549
|
+
`Last applied: ${lastAppliedSnapshot}`,
|
|
543
550
|
...noSyncedContent ? ["", "No included content is selected. Choose included content in Settings before syncing."] : [],
|
|
544
551
|
"",
|
|
545
|
-
"
|
|
552
|
+
"ACTIONS \xB7 transfer, inspect, or manage"
|
|
546
553
|
];
|
|
547
554
|
return {
|
|
548
555
|
title: (changesBlocked ? ["Manage sync", ...operationStatusLines(operation)] : ordinaryTitle).join("\n"),
|
|
@@ -630,6 +637,50 @@ function backendStorageDescription(config) {
|
|
|
630
637
|
// src/ui/settings-ui.ts
|
|
631
638
|
import { defineMenu as defineMenu4, runMenu as runMenu4 } from "@narumitw/pi-tui-kit";
|
|
632
639
|
|
|
640
|
+
// src/ui/settings-frame.ts
|
|
641
|
+
import { stripVTControlCharacters } from "node:util";
|
|
642
|
+
function boundedSettingsUI(ctx) {
|
|
643
|
+
return {
|
|
644
|
+
...ctx.ui,
|
|
645
|
+
custom: (factory, options) => ctx.ui.custom((tui, ...args) => {
|
|
646
|
+
const terminal = new Proxy(tui.terminal, {
|
|
647
|
+
get: (target, key) => key === "rows" ? Math.max(8, target.rows) : Reflect.get(target, key)
|
|
648
|
+
});
|
|
649
|
+
const screenTui = new Proxy(tui, {
|
|
650
|
+
get(target, key) {
|
|
651
|
+
if (key === "terminal") return terminal;
|
|
652
|
+
const value = Reflect.get(target, key);
|
|
653
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
const guard = (component2) => {
|
|
657
|
+
const render = component2.render.bind(component2);
|
|
658
|
+
const handleMouse = component2.handleMouse?.bind(component2);
|
|
659
|
+
let sourceRow = 0;
|
|
660
|
+
const compact = () => tui.terminal.rows < 8;
|
|
661
|
+
component2.render = (width) => {
|
|
662
|
+
if (width <= 0) return [];
|
|
663
|
+
const lines = render(width);
|
|
664
|
+
if (!compact()) return lines;
|
|
665
|
+
const active = lines.findIndex((line) => stripVTControlCharacters(line).startsWith("\u2192"));
|
|
666
|
+
const search = lines.findIndex((line) => stripVTControlCharacters(line).startsWith(">"));
|
|
667
|
+
sourceRow = active < 0 ? Math.max(0, search) : active;
|
|
668
|
+
return lines.slice(sourceRow, sourceRow + 1);
|
|
669
|
+
};
|
|
670
|
+
if (handleMouse)
|
|
671
|
+
component2.handleMouse = (event) => {
|
|
672
|
+
if (event.width <= 0 || compact() && event.y !== 0) return void 0;
|
|
673
|
+
component2.render(event.width);
|
|
674
|
+
return handleMouse(compact() ? { ...event, y: event.y + sourceRow } : event);
|
|
675
|
+
};
|
|
676
|
+
return component2;
|
|
677
|
+
};
|
|
678
|
+
const component = factory(screenTui, ...args);
|
|
679
|
+
return component instanceof Promise ? component.then(guard) : guard(component);
|
|
680
|
+
}, options)
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
633
684
|
// src/ui/storage-connections-ui.ts
|
|
634
685
|
import { isDeepStrictEqual } from "node:util";
|
|
635
686
|
import { defineMenu as defineMenu2, runMenu as runMenu2 } from "@narumitw/pi-tui-kit";
|
|
@@ -1227,118 +1278,153 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1227
1278
|
const initial = await loadConfig();
|
|
1228
1279
|
if (signal?.aborted) return "back";
|
|
1229
1280
|
const setupName = initial.setupName;
|
|
1281
|
+
const groups = {
|
|
1282
|
+
content: { label: "Sync content", ids: ["include", "remoteInclude", "localFields"] },
|
|
1283
|
+
behavior: { label: "Sync behavior", ids: ["automatic", "automaticTransfer", "onSwitch"] },
|
|
1284
|
+
storage: { label: "Storage & setups", ids: ["storageLocation", "setups", "connections"] },
|
|
1285
|
+
advanced: { label: "Advanced", ids: ["mergeSettings", "contentPolicy", "skipSecretScan", "showStatus", "debug"] },
|
|
1286
|
+
all: { label: "All settings (search)", ids: [] }
|
|
1287
|
+
};
|
|
1288
|
+
let saveStatus = "Simple choices save immediately. Back does not undo saved changes.";
|
|
1289
|
+
const settingsScreen = (state, group) => ({
|
|
1290
|
+
kind: "settings",
|
|
1291
|
+
title: `Pi Sync Settings \xB7 ${groups[group].label}`,
|
|
1292
|
+
lines: [
|
|
1293
|
+
`Setup: ${safeTerminalText(state.setupName)} \xB7 Connection: ${safeTerminalText(state.connectionName)}`,
|
|
1294
|
+
saveStatus
|
|
1295
|
+
],
|
|
1296
|
+
items: [
|
|
1297
|
+
{
|
|
1298
|
+
id: "include",
|
|
1299
|
+
label: "Included paths",
|
|
1300
|
+
description: "This setup: choose paths in a draft, then review and Save or Discard. No transfer is started.",
|
|
1301
|
+
currentValue: `${state.include.length} paths \xB7 Edit`,
|
|
1302
|
+
action: "include"
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
id: "remoteInclude",
|
|
1306
|
+
label: "Compare included paths",
|
|
1307
|
+
description: "This setup: compare included-path policies, not file contents. Review before saving either list; no files transfer unless you explicitly Continue.",
|
|
1308
|
+
currentValue: "Review",
|
|
1309
|
+
action: "remote-include"
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
id: "localFields",
|
|
1313
|
+
label: "Machine-local fields",
|
|
1314
|
+
description: "This setup: settings.json root fields excluded from future portable snapshots, not project overrides. Policy changes require reviewed directional migration; old history remains.",
|
|
1315
|
+
currentValue: `${state.localFields?.length ?? 0} fields \xB7 Edit`,
|
|
1316
|
+
action: "local-fields"
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
id: "storageLocation",
|
|
1320
|
+
label: "Storage location",
|
|
1321
|
+
description: "This setup: review and save bucket, branch, or path changes. Remote history is not moved.",
|
|
1322
|
+
currentValue: state.backend.type === "s3" ? `${safeTerminalText(state.backend.destination.bucket)} \xB7 ${safeTerminalText(state.backend.destination.prefix)}` : state.backend.type === "git" ? `${safeTerminalText(state.backend.destination.branch)} \xB7 ${safeTerminalText(state.backend.destination.directory)}` : safeTerminalText(state.backend.destination.path),
|
|
1323
|
+
action: "storage-location"
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
id: "setups",
|
|
1327
|
+
label: "Manage sync setups",
|
|
1328
|
+
description: "This device: add, switch, edit, or remove sync setups. Each edit identifies its owner before saving.",
|
|
1329
|
+
currentValue: "Open",
|
|
1330
|
+
action: "setups"
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
id: "connections",
|
|
1334
|
+
label: "Manage storage connections",
|
|
1335
|
+
description: "Shared connections on this device: review affected setups before saving server or credential changes.",
|
|
1336
|
+
currentValue: "Open",
|
|
1337
|
+
action: "connections"
|
|
1338
|
+
},
|
|
1339
|
+
{
|
|
1340
|
+
id: "automatic",
|
|
1341
|
+
label: "Automatic sync",
|
|
1342
|
+
description: AUTOMATIC_SYNC_DESCRIPTION,
|
|
1343
|
+
currentValue: state.automatic ? "On" : "Off",
|
|
1344
|
+
values: ["On", "Off"],
|
|
1345
|
+
action: "automatic"
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
id: "automaticTransfer",
|
|
1349
|
+
label: "Startup transfer",
|
|
1350
|
+
description: `${STARTUP_TRANSFER_DESCRIPTION} Saved preference and requirements: ${startupTransferSummary(state)}.`,
|
|
1351
|
+
currentValue: state.automaticTransfer ? "On" : "Off",
|
|
1352
|
+
values: ["On", "Off"],
|
|
1353
|
+
action: "automatic-transfer"
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
id: "onSwitch",
|
|
1357
|
+
label: "On setup switch",
|
|
1358
|
+
description: "All setups on this device: ask before a pull review, open it automatically, or switch without pulling. Applies to subsequent switches.",
|
|
1359
|
+
currentValue: setupSwitchActionLabel(state.onSwitch),
|
|
1360
|
+
values: SETUP_SWITCH_ACTION_OPTIONS.map(({ label }) => label),
|
|
1361
|
+
action: "on-switch"
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
id: "mergeSettings",
|
|
1365
|
+
label: "Merge settings fields",
|
|
1366
|
+
description: "This setup, experimental opt-in: merge independent global settings.json root fields with a verified private ancestor. Arrays and nested objects remain atomic. Applies to subsequent sync; no reload.",
|
|
1367
|
+
currentValue: state.mergeSettings ? "On" : "Off",
|
|
1368
|
+
values: ["On", "Off"],
|
|
1369
|
+
action: "merge-settings"
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
id: "contentPolicy",
|
|
1373
|
+
label: "Content merge / partial progress",
|
|
1374
|
+
description: "This setup, experimental opt-in: review the version-5 upgrade before saving. Merge bounded text and prefix-only sessions; partial progress keeps withheld versions and baselines. Applies to subsequent sync; disabling does not clear recovery evidence.",
|
|
1375
|
+
currentValue: state.mergeContent ? state.partialSync ? "Merge + partial" : "Merge only" : state.partialSync ? "Partial only" : "Off",
|
|
1376
|
+
values: ["Off", "Merge only", "Merge + partial", "Partial only"],
|
|
1377
|
+
action: "content-policy"
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
id: "skipSecretScan",
|
|
1381
|
+
label: "Skip secret scan",
|
|
1382
|
+
description: "All setups on this device: skip possible-secret checks on subsequent pushes, including automatic pushes. Other safety checks remain. Diagnostics still scans.",
|
|
1383
|
+
currentValue: state.skipSecretScan ? "On" : "Off",
|
|
1384
|
+
values: ["On", "Off"],
|
|
1385
|
+
action: "skip-secret-scan"
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
id: "showStatus",
|
|
1389
|
+
label: "Show sync status",
|
|
1390
|
+
description: "All setups on this device: saved and applied immediately to Pi status. Review widgets and notifications remain available.",
|
|
1391
|
+
currentValue: state.showStatus ? "On" : "Off",
|
|
1392
|
+
values: ["On", "Off"],
|
|
1393
|
+
action: "show-status"
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
id: "debug",
|
|
1397
|
+
label: "Debug startup timings",
|
|
1398
|
+
description: "All setups on this device: show startup durations in TUI/RPC notifications. Applies next session start; no credentials or file contents.",
|
|
1399
|
+
currentValue: state.debug ? "On" : "Off",
|
|
1400
|
+
values: ["On", "Off"],
|
|
1401
|
+
action: "debug"
|
|
1402
|
+
}
|
|
1403
|
+
].filter((item) => group === "all" || groups[group].ids.includes(item.id))
|
|
1404
|
+
});
|
|
1230
1405
|
const menu = defineMenu4({
|
|
1231
|
-
start: "
|
|
1406
|
+
start: "categories",
|
|
1232
1407
|
screens: {
|
|
1233
|
-
|
|
1234
|
-
kind: "
|
|
1408
|
+
categories: ({ state }) => ({
|
|
1409
|
+
kind: "actions",
|
|
1235
1410
|
title: "Pi Sync Settings",
|
|
1236
1411
|
lines: [
|
|
1237
|
-
`
|
|
1412
|
+
`Setup: ${safeTerminalText(state.setupName)} \xB7 Connection: ${safeTerminalText(state.connectionName)}`,
|
|
1413
|
+
"Choose a task, or search all settings. Scope is shown in each setting's description.",
|
|
1414
|
+
saveStatus
|
|
1238
1415
|
],
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
label: "Compare synced content",
|
|
1252
|
-
description: "Review this device and remote content lists before choosing either one.",
|
|
1253
|
-
currentValue: "Review",
|
|
1254
|
-
action: "remote-include"
|
|
1255
|
-
},
|
|
1256
|
-
{
|
|
1257
|
-
id: "localFields",
|
|
1258
|
-
label: "Machine-local settings fields",
|
|
1259
|
-
description: "Root field names omitted from future portable snapshots. Policy changes require directional migration; old history remains.",
|
|
1260
|
-
currentValue: `${state.localFields?.length ?? 0} fields \xB7 Edit`,
|
|
1261
|
-
action: "local-fields"
|
|
1262
|
-
},
|
|
1263
|
-
{
|
|
1264
|
-
id: "storageLocation",
|
|
1265
|
-
label: "Storage location",
|
|
1266
|
-
description: "Destination: edit this setup's bucket, branch, or path; remote history is not moved.",
|
|
1267
|
-
currentValue: state.backend.type === "s3" ? `${safeTerminalText(state.backend.destination.bucket)} \xB7 ${safeTerminalText(state.backend.destination.prefix)}` : state.backend.type === "git" ? `${safeTerminalText(state.backend.destination.branch)} \xB7 ${safeTerminalText(state.backend.destination.directory)}` : safeTerminalText(state.backend.destination.path),
|
|
1268
|
-
action: "storage-location"
|
|
1269
|
-
},
|
|
1270
|
-
{
|
|
1271
|
-
id: "setups",
|
|
1272
|
-
label: "Manage sync setups",
|
|
1273
|
-
description: "Destination: add, switch, edit, or remove sync setups.",
|
|
1274
|
-
currentValue: "Open",
|
|
1275
|
-
action: "setups"
|
|
1276
|
-
},
|
|
1277
|
-
{
|
|
1278
|
-
id: "connections",
|
|
1279
|
-
label: "Manage storage connections",
|
|
1280
|
-
description: "Destination: manage shared server addresses and credentials; edits may affect multiple setups.",
|
|
1281
|
-
currentValue: "Open",
|
|
1282
|
-
action: "connections"
|
|
1283
|
-
},
|
|
1284
|
-
{
|
|
1285
|
-
id: "automatic",
|
|
1286
|
-
label: "Automatic sync",
|
|
1287
|
-
description: AUTOMATIC_SYNC_DESCRIPTION,
|
|
1288
|
-
currentValue: state.automatic ? "On" : "Off",
|
|
1289
|
-
values: ["On", "Off"],
|
|
1290
|
-
action: "automatic"
|
|
1291
|
-
},
|
|
1292
|
-
{
|
|
1293
|
-
id: "automaticTransfer",
|
|
1294
|
-
label: "Automatic transfer at startup",
|
|
1295
|
-
description: "May upload, replace, or delete selected files once at idle startup in TUI/RPC. Requires a baseline and conditional/lease publication; never reloads resources. Turning off cancels pending work.",
|
|
1296
|
-
currentValue: state.automaticTransfer ? "On" : "Off",
|
|
1297
|
-
values: ["On", "Off"],
|
|
1298
|
-
action: "automatic-transfer"
|
|
1299
|
-
},
|
|
1300
|
-
{
|
|
1301
|
-
id: "onSwitch",
|
|
1302
|
-
label: "After switching setup (all setups)",
|
|
1303
|
-
description: "All setups: ask before a pull review, open it automatically, or switch without pulling.",
|
|
1304
|
-
currentValue: setupSwitchActionLabel(state.onSwitch),
|
|
1305
|
-
values: SETUP_SWITCH_ACTION_OPTIONS.map(({ label }) => label),
|
|
1306
|
-
action: "on-switch"
|
|
1307
|
-
},
|
|
1308
|
-
{
|
|
1309
|
-
id: "mergeSettings",
|
|
1310
|
-
label: "Settings field merge (experimental)",
|
|
1311
|
-
description: "Combine independent global settings.json fields using a verified private ancestor; arrays and nested objects remain atomic. No reload.",
|
|
1312
|
-
currentValue: state.mergeSettings ? "On" : "Off",
|
|
1313
|
-
values: ["On", "Off"],
|
|
1314
|
-
action: "merge-settings"
|
|
1315
|
-
},
|
|
1316
|
-
{
|
|
1317
|
-
id: "contentPolicy",
|
|
1318
|
-
label: "Content / partial sync (experimental)",
|
|
1319
|
-
description: "Version-5 opt-in: bounded text and prefix-only sessions; partial progress keeps full withheld versions and old baseline hashes.",
|
|
1320
|
-
currentValue: state.mergeContent ? state.partialSync ? "Content & partial" : "Content only" : state.partialSync ? "Partial only" : "Off",
|
|
1321
|
-
values: ["Off", "Content only", "Content & partial", "Partial only"],
|
|
1322
|
-
action: "content-policy"
|
|
1323
|
-
},
|
|
1324
|
-
{
|
|
1325
|
-
id: "skipSecretScan",
|
|
1326
|
-
label: "Skip secret scan (all setups)",
|
|
1327
|
-
description: "All setups: allow pushes without checking managed local files for possible secrets.",
|
|
1328
|
-
currentValue: state.skipSecretScan ? "On" : "Off",
|
|
1329
|
-
values: ["On", "Off"],
|
|
1330
|
-
action: "skip-secret-scan"
|
|
1331
|
-
},
|
|
1332
|
-
{
|
|
1333
|
-
id: "showStatus",
|
|
1334
|
-
label: "Show status (all setups)",
|
|
1335
|
-
description: "All setups: show sync progress and attention in Pi status.",
|
|
1336
|
-
currentValue: state.showStatus ? "On" : "Off",
|
|
1337
|
-
values: ["On", "Off"],
|
|
1338
|
-
action: "show-status"
|
|
1339
|
-
}
|
|
1340
|
-
]
|
|
1341
|
-
})
|
|
1416
|
+
items: Object.entries(groups).map(([id, group]) => ({
|
|
1417
|
+
id,
|
|
1418
|
+
label: group.label,
|
|
1419
|
+
to: id
|
|
1420
|
+
})),
|
|
1421
|
+
hint: "back"
|
|
1422
|
+
}),
|
|
1423
|
+
content: ({ state }) => settingsScreen(state, "content"),
|
|
1424
|
+
behavior: ({ state }) => settingsScreen(state, "behavior"),
|
|
1425
|
+
storage: ({ state }) => settingsScreen(state, "storage"),
|
|
1426
|
+
advanced: ({ state }) => settingsScreen(state, "advanced"),
|
|
1427
|
+
all: ({ state }) => settingsScreen(state, "all")
|
|
1342
1428
|
},
|
|
1343
1429
|
actions: {
|
|
1344
1430
|
"storage-location": async ({ signal: actionSignal }) => {
|
|
@@ -1362,8 +1448,8 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1362
1448
|
try {
|
|
1363
1449
|
const previous = await loadConfig(setupName);
|
|
1364
1450
|
validate();
|
|
1365
|
-
const mergeContent = value === "
|
|
1366
|
-
const partialSync = value === "Partial only" || value === "
|
|
1451
|
+
const mergeContent = value === "Merge only" || value === "Merge + partial";
|
|
1452
|
+
const partialSync = value === "Partial only" || value === "Merge + partial";
|
|
1367
1453
|
if (!await ctx.ui.confirm(
|
|
1368
1454
|
"Save experimental content policy?",
|
|
1369
1455
|
"This explicitly upgrades settings to version 5; partial snapshots use version 3 and older clients must refuse them. Text/session merge is conservative. Partial sync preserves withheld versions and old baselines. Disabling does not clear unresolved groups or recovery evidence. Review a directional migration first if portable policy changes.",
|
|
@@ -1371,6 +1457,7 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1371
1457
|
))
|
|
1372
1458
|
return { kind: "rejected" };
|
|
1373
1459
|
validate();
|
|
1460
|
+
ctx.ui.notify("Saving reviewed Pi Sync policy\u2026", "info");
|
|
1374
1461
|
await updateLocalConfig((current) => {
|
|
1375
1462
|
validate();
|
|
1376
1463
|
const setup = current.syncSetups[setupName];
|
|
@@ -1386,6 +1473,8 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1386
1473
|
};
|
|
1387
1474
|
}, mutationSignal);
|
|
1388
1475
|
validate();
|
|
1476
|
+
saveStatus = "Saved reviewed policy for this setup. Applies to subsequent sync operations; recovery evidence is retained.";
|
|
1477
|
+
ctx.ui.notify(saveStatus, "info");
|
|
1389
1478
|
return { kind: "stay" };
|
|
1390
1479
|
} catch (error) {
|
|
1391
1480
|
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
@@ -1436,6 +1525,7 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1436
1525
|
);
|
|
1437
1526
|
validate();
|
|
1438
1527
|
if (!confirmed) return { kind: "rejected" };
|
|
1528
|
+
ctx.ui.notify("Saving reviewed Pi Sync policy\u2026", "info");
|
|
1439
1529
|
await updateLocalConfig((current) => {
|
|
1440
1530
|
validate();
|
|
1441
1531
|
const setup = current.syncSetups[setupName];
|
|
@@ -1451,6 +1541,8 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1451
1541
|
};
|
|
1452
1542
|
}, mutationSignal);
|
|
1453
1543
|
validate();
|
|
1544
|
+
saveStatus = "Saved reviewed policy for this setup. Applies to subsequent sync operations; recovery evidence is retained.";
|
|
1545
|
+
ctx.ui.notify(saveStatus, "info");
|
|
1454
1546
|
return { kind: "stay" };
|
|
1455
1547
|
} catch (error) {
|
|
1456
1548
|
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
@@ -1529,6 +1621,18 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1529
1621
|
return { kind: "rejected" };
|
|
1530
1622
|
}
|
|
1531
1623
|
},
|
|
1624
|
+
debug: async ({ value, signal: actionSignal }) => {
|
|
1625
|
+
const debug = value === "On";
|
|
1626
|
+
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
1627
|
+
try {
|
|
1628
|
+
await updateLocalConfig((settings) => ({ ...settings, debug }), mutationSignal);
|
|
1629
|
+
if (mutationSignal.aborted) return { kind: "rejected" };
|
|
1630
|
+
return { kind: "stay" };
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
1633
|
+
return { kind: "rejected" };
|
|
1634
|
+
}
|
|
1635
|
+
},
|
|
1532
1636
|
"on-switch": async ({ value, signal: actionSignal }) => {
|
|
1533
1637
|
const action = value ? setupSwitchActionFromLabel(value) : void 0;
|
|
1534
1638
|
if (!action) return { kind: "rejected" };
|
|
@@ -1553,7 +1657,7 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1553
1657
|
},
|
|
1554
1658
|
"remote-include": async ({ signal: actionSignal }) => {
|
|
1555
1659
|
const reviewSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
1556
|
-
const { showRemoteSelectionReview } = await import("./remote-selection-ui-
|
|
1660
|
+
const { showRemoteSelectionReview } = await import("./remote-selection-ui-H6AEVA3Q.ts");
|
|
1557
1661
|
if (reviewSignal.aborted) return { kind: "rejected" };
|
|
1558
1662
|
const review = await showRemoteSelectionReview(ctx, setupName, reviewSignal, void 0, {
|
|
1559
1663
|
origin: "settings",
|
|
@@ -1568,6 +1672,28 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1568
1672
|
}
|
|
1569
1673
|
}
|
|
1570
1674
|
});
|
|
1675
|
+
for (const id of [
|
|
1676
|
+
"automatic",
|
|
1677
|
+
"automatic-transfer",
|
|
1678
|
+
"merge-settings",
|
|
1679
|
+
"skip-secret-scan",
|
|
1680
|
+
"show-status",
|
|
1681
|
+
"debug",
|
|
1682
|
+
"on-switch"
|
|
1683
|
+
]) {
|
|
1684
|
+
const save = menu.actions[id];
|
|
1685
|
+
menu.actions[id] = async (action) => {
|
|
1686
|
+
const validate = captureMutationOwner(ctx, action.signal);
|
|
1687
|
+
validate();
|
|
1688
|
+
ctx.ui.notify("Saving Pi Sync setting\u2026", "info");
|
|
1689
|
+
const result = await save(action);
|
|
1690
|
+
if (action.signal.aborted || signal?.aborted) return { kind: "rejected" };
|
|
1691
|
+
validate();
|
|
1692
|
+
saveStatus = result?.kind === "rejected" ? "Save failed or rejected. Previous value retained; retry is available." : id === "automatic-transfer" || id === "automatic" || id === "debug" ? "Saved. Enabling startup work applies next session start; completed transfers are not undone." : "Saved. Applies to subsequent operations; status visibility applies immediately.";
|
|
1693
|
+
ctx.ui.notify(saveStatus, result?.kind === "rejected" ? "warning" : "info");
|
|
1694
|
+
return result;
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1571
1697
|
async function resumeSettings(editorSignal) {
|
|
1572
1698
|
if (editorSignal.aborted) return { kind: "close" };
|
|
1573
1699
|
try {
|
|
@@ -1578,7 +1704,7 @@ async function showSyncSettings(ctx, runRoute, signal) {
|
|
|
1578
1704
|
return { kind: "close" };
|
|
1579
1705
|
}
|
|
1580
1706
|
}
|
|
1581
|
-
await runMenu4(ctx, menu, {
|
|
1707
|
+
await runMenu4({ ...ctx, ui: boundedSettingsUI(ctx) }, menu, {
|
|
1582
1708
|
getState: () => loadConfig(setupName),
|
|
1583
1709
|
signal,
|
|
1584
1710
|
isCurrent: () => !signal?.aborted
|
|
@@ -1626,7 +1752,7 @@ async function showSyncManager(ctx, runRoute, sessionSignal, options = {}) {
|
|
|
1626
1752
|
if (!attention) {
|
|
1627
1753
|
const observation = options.getObservation?.();
|
|
1628
1754
|
if (!observation) return { kind: "stay" };
|
|
1629
|
-
const { showRemoteSelectionReview } = await import("./remote-selection-ui-
|
|
1755
|
+
const { showRemoteSelectionReview } = await import("./remote-selection-ui-H6AEVA3Q.ts");
|
|
1630
1756
|
if (sessionSignal?.aborted) return { kind: "close" };
|
|
1631
1757
|
const config = await loadConfig();
|
|
1632
1758
|
if (sessionSignal?.aborted) return { kind: "close" };
|
|
@@ -1717,12 +1843,45 @@ async function showSyncManager(ctx, runRoute, sessionSignal, options = {}) {
|
|
|
1717
1843
|
});
|
|
1718
1844
|
return { kind: result.kind === "closed" ? "close" : "stay" };
|
|
1719
1845
|
},
|
|
1720
|
-
|
|
1721
|
-
const result = await runCancellableOperation(ctx, "
|
|
1846
|
+
diff: async () => {
|
|
1847
|
+
const result = await runCancellableOperation(ctx, "Comparing local and remote\u2026", "diff", runRoute, {
|
|
1722
1848
|
signal: sessionSignal
|
|
1723
1849
|
});
|
|
1724
1850
|
return { kind: result.kind === "closed" ? "close" : "stay" };
|
|
1725
1851
|
},
|
|
1852
|
+
status: async () => {
|
|
1853
|
+
const result = await runCancellableOperation(ctx, "Checking remote status\u2026", "status", runRoute, {
|
|
1854
|
+
signal: sessionSignal
|
|
1855
|
+
});
|
|
1856
|
+
return { kind: result.kind === "closed" ? "close" : "stay" };
|
|
1857
|
+
},
|
|
1858
|
+
doctor: async ({ signal: actionSignal }) => {
|
|
1859
|
+
const signal = sessionSignal ? AbortSignal.any([sessionSignal, actionSignal]) : actionSignal;
|
|
1860
|
+
const validate = captureMutationOwner(ctx, signal);
|
|
1861
|
+
const config = await loadConfig();
|
|
1862
|
+
validate();
|
|
1863
|
+
const confirmed = await ctx.ui.confirm(
|
|
1864
|
+
"Run diagnostics?",
|
|
1865
|
+
`Setup: ${safeTerminalText(config.setupName)}
|
|
1866
|
+
Storage: ${backendStorageDescription(config)}
|
|
1867
|
+
S3/R2 reads the latest pointer without writing. Git reads remote snapshots and checks its cache. WebDAV probes conditional writes and cleanup, and can repair a missing history entry. Possible-secret checks still run. This is not the read-only Check remote status action.`,
|
|
1868
|
+
{ signal }
|
|
1869
|
+
);
|
|
1870
|
+
if (signal.aborted) return { kind: "close" };
|
|
1871
|
+
validate();
|
|
1872
|
+
if (!confirmed) return { kind: "stay" };
|
|
1873
|
+
const latest = await loadConfig();
|
|
1874
|
+
validate();
|
|
1875
|
+
if (syncCheckConfigFingerprint(config) !== syncCheckConfigFingerprint(latest)) {
|
|
1876
|
+
ctx.ui.notify("Setup changed during diagnostics review; reopen Diagnostics.", "warning");
|
|
1877
|
+
return { kind: "stay" };
|
|
1878
|
+
}
|
|
1879
|
+
const result = await runCancellableOperation(ctx, "Checking setup access\u2026", "doctor", runRoute, {
|
|
1880
|
+
signal,
|
|
1881
|
+
target: config.setupName
|
|
1882
|
+
});
|
|
1883
|
+
return { kind: result.kind === "closed" ? "close" : "stay" };
|
|
1884
|
+
},
|
|
1726
1885
|
recover: async ({ state, signal: actionSignal }) => {
|
|
1727
1886
|
const result = await recoverSyncAccess(ctx, state.manager, runRoute, sessionSignal, actionSignal);
|
|
1728
1887
|
return { kind: result === "close" ? "close" : "stay" };
|
|
@@ -1769,6 +1928,8 @@ function syncMainMenuItem(label) {
|
|
|
1769
1928
|
"Sync now": "sync",
|
|
1770
1929
|
"Pull from remote\u2026": "pull",
|
|
1771
1930
|
"Push to remote\u2026": "push",
|
|
1931
|
+
"View differences": "diff",
|
|
1932
|
+
"Check remote status": "status",
|
|
1772
1933
|
"Switch sync setup": "switch",
|
|
1773
1934
|
Settings: "settings",
|
|
1774
1935
|
History: "history",
|
|
@@ -1788,10 +1949,10 @@ function syncMainMenuItem(label) {
|
|
|
1788
1949
|
id: action,
|
|
1789
1950
|
label,
|
|
1790
1951
|
action,
|
|
1791
|
-
|
|
1952
|
+
description: action === "doctor" ? "Manage: review backend checks before running." : action === "settings" || action === "history" || action === "switch" ? "Manage" : action === "sync" ? "Transfer: choose a safe direction or review conflicts." : action === "pull" ? "Transfer: remote \u2192 local; review writes and removals." : action === "push" ? "Transfer: local \u2192 remote active snapshot; review publication." : action === "status" || action === "diff" ? "Inspect: read current local and remote state without transferring files." : void 0
|
|
1792
1953
|
};
|
|
1793
1954
|
}
|
|
1794
1955
|
export {
|
|
1795
1956
|
showSyncManager
|
|
1796
1957
|
};
|
|
1797
|
-
//# sourceMappingURL=manager-ui-
|
|
1958
|
+
//# sourceMappingURL=manager-ui-ZIN4BUSF.ts.map
|