@narumitw/pi-sync 0.49.14 → 0.50.0
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 +134 -241
- package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
- package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
- package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
- package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
- package/dist/chunks/chunk-6UHKGALE.ts +134 -0
- package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
- package/dist/chunks/{chunk-RGJ6SOYR.ts → chunk-AUSDLD6F.ts} +4 -4
- package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
- package/dist/chunks/{chunk-3NDD5QPM.ts → chunk-AXXS2BPA.ts} +9 -9
- package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
- package/dist/chunks/{chunk-APZVBSM6.ts → chunk-C7XY5NF5.ts} +214 -365
- package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
- package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
- package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
- package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
- package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
- package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
- package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
- package/dist/chunks/{chunk-LJDSHNS3.ts → chunk-GWMHAQG4.ts} +4 -4
- package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
- package/dist/chunks/{chunk-FR6CVXAL.ts → chunk-HUDQATCL.ts} +25 -25
- package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
- package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
- package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
- package/dist/chunks/chunk-JSYMGJQQ.ts +51 -0
- package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
- package/dist/chunks/chunk-L7TBO32M.ts +25 -0
- package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
- package/dist/chunks/{chunk-4F3W4CSC.ts → chunk-LCRI3ABW.ts} +22 -8
- package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
- package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
- package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
- package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
- package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
- package/dist/chunks/{chunk-EWX3TPLQ.ts → chunk-OEX4ABY6.ts} +39 -37
- package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
- package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
- package/dist/chunks/chunk-OUHGBFYF.ts.map +7 -0
- package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
- package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
- package/dist/chunks/{chunk-BHJUISB4.ts → chunk-SL64RLXR.ts} +8 -6
- package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
- package/dist/chunks/chunk-THMF4BVL.ts +18 -0
- package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
- package/dist/chunks/{chunk-LONPFFNP.ts → chunk-X5OM6GXT.ts} +9 -7
- package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
- package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
- package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
- package/dist/chunks/{chunk-5WJNWFTG.ts → chunk-YKJHPTGO.ts} +23 -55
- package/dist/chunks/chunk-YKJHPTGO.ts.map +7 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
- package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
- package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
- package/dist/chunks/{file-selection-4UABOP44.ts → file-selection-FTTKAOYX.ts} +10 -7
- package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
- package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-OQW46G2X.ts} +42 -17
- package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
- package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
- package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
- package/dist/chunks/{s3-backend-3QZGO2EF.ts → s3-backend-3P33LQHU.ts} +96 -18
- package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
- package/dist/chunks/{setup-switch-X3W3FALZ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
- package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
- package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
- package/dist/chunks/{snapshot-WBSV4MQG.ts → snapshot-HKEWCVK6.ts} +7 -6
- package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
- package/dist/chunks/{sync-operations-MPAFVVHA.ts → sync-operations-ONJ27QRL.ts} +381 -299
- package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
- package/dist/chunks/{sync-state-36H7ULG2.ts → sync-state-Q2FTXTW6.ts} +6 -6
- package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
- package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
- package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
- package/dist/index.ts +477 -342
- package/dist/index.ts.map +4 -4
- package/docs/settings.md +211 -0
- package/package.json +6 -8
- package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
- package/src/backends/backend-identity.ts +40 -0
- package/src/backends/backend-types.ts +81 -0
- package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
- package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
- package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
- package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
- package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
- package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
- package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
- package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
- package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
- package/src/commands/command-execution.ts +330 -0
- package/src/commands/command-handler.ts +201 -0
- package/src/commands/command-types.ts +18 -0
- package/src/{command.ts → commands/command.ts} +2 -1
- package/src/commands/setup-completions.ts +6 -0
- package/src/settings/config.ts +238 -0
- package/src/{settings-management.ts → settings/settings-management.ts} +40 -14
- package/src/settings/settings-store.ts +109 -0
- package/src/settings/settings-types.ts +128 -0
- package/src/settings/settings-validation.ts +443 -0
- package/src/snapshot/session-paths.ts +84 -0
- package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
- package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
- package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
- package/src/snapshot/snapshot-types.ts +38 -0
- package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
- package/src/state/json-file.ts +20 -0
- package/src/{lock.ts → state/lock.ts} +3 -2
- package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
- package/src/state/state-types.ts +20 -0
- package/src/state/sync-state-store.ts +62 -0
- package/src/sync/automatic-sync.ts +103 -0
- package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
- package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
- package/src/sync/signals.ts +10 -0
- package/src/sync/startup-check.ts +128 -0
- package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
- package/src/sync/sync-error-guidance.ts +53 -0
- package/src/sync/sync-inspection.ts +51 -0
- package/src/sync/sync-loaders.ts +54 -0
- package/src/{sync-operations.ts → sync/sync-mutations.ts} +41 -330
- package/src/sync/sync-operations.ts +12 -0
- package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
- package/src/sync/sync-queries.ts +235 -0
- package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
- package/src/sync-extension.ts +64 -708
- package/src/sync.ts +8 -8
- package/src/types.ts +47 -284
- package/src/ui/automatic-sync-summary.ts +6 -0
- package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
- package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
- package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
- package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
- package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
- package/src/ui/manager-ui.ts +386 -0
- package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
- package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
- package/src/{settings-ui.ts → ui/settings-ui.ts} +42 -11
- package/src/ui/setup/git-ui.ts +293 -0
- package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
- package/src/ui/setup/s3-ui.ts +217 -0
- package/src/ui/setup/setup-actions.ts +127 -0
- package/src/ui/setup/setup-location-ui.ts +167 -0
- package/src/ui/setup/setup-name-ui.ts +34 -0
- package/src/ui/setup/setup-prompts.ts +92 -0
- package/src/ui/setup/setup-review.ts +70 -0
- package/src/ui/setup/setup-switcher.ts +181 -0
- package/src/ui/setup/setup-wizard.ts +39 -0
- package/src/ui/setup/text-input.ts +78 -0
- package/src/ui/setup/webdav-ui.ts +330 -0
- package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
- package/src/{sync-attention.ts → ui/sync-attention.ts} +75 -6
- package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
- package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
- package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
- package/src/ui/terminal-text.ts +4 -0
- package/dist/chunks/chunk-3NDD5QPM.ts.map +0 -7
- package/dist/chunks/chunk-4F3W4CSC.ts.map +0 -7
- package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
- package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
- package/dist/chunks/chunk-5WJNWFTG.ts.map +0 -7
- package/dist/chunks/chunk-APZVBSM6.ts.map +0 -7
- package/dist/chunks/chunk-BHJUISB4.ts.map +0 -7
- package/dist/chunks/chunk-EWX3TPLQ.ts.map +0 -7
- package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
- package/dist/chunks/chunk-FR6CVXAL.ts.map +0 -7
- package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
- package/dist/chunks/chunk-JNOOSPEW.ts +0 -82
- package/dist/chunks/chunk-JNOOSPEW.ts.map +0 -7
- package/dist/chunks/chunk-LJDSHNS3.ts.map +0 -7
- package/dist/chunks/chunk-LONPFFNP.ts.map +0 -7
- package/dist/chunks/chunk-RGJ6SOYR.ts.map +0 -7
- package/dist/chunks/chunk-W64NEHXT.ts +0 -167
- package/dist/chunks/chunk-W64NEHXT.ts.map +0 -7
- package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
- package/dist/chunks/file-selection-4UABOP44.ts.map +0 -7
- package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-RNRQA3PW.ts +0 -18
- package/dist/chunks/manager-ui-QO2P4V3Z.ts +0 -2757
- package/dist/chunks/manager-ui-QO2P4V3Z.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-LHSAGKGA.ts +0 -17
- package/dist/chunks/s3-backend-3QZGO2EF.ts.map +0 -7
- package/dist/chunks/sync-operations-MPAFVVHA.ts.map +0 -7
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
- package/src/config.ts +0 -910
- package/src/git-ui.ts +0 -307
- package/src/manager-helpers.ts +0 -64
- package/src/manager-ui.ts +0 -997
- package/src/webdav-ui.ts +0 -421
- /package/dist/chunks/{manager-result-dispatcher-RNRQA3PW.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-LHSAGKGA.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-X3W3FALZ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
- /package/dist/chunks/{snapshot-WBSV4MQG.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
- /package/dist/chunks/{sync-state-36H7ULG2.ts.map → sync-inspection-FEP4L5TR.ts.map} +0 -0
- /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
- /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
- /package/src/{config-file.ts → settings/config-file.ts} +0 -0
- /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
- /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
- /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
- /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
- /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
- /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
package/dist/index.ts
CHANGED
|
@@ -2,8 +2,28 @@
|
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
3
|
import {
|
|
4
4
|
createSyncAttentionController,
|
|
5
|
+
observationMatchesConfig,
|
|
5
6
|
syncAttentionMatchesConfig
|
|
6
|
-
} from "./chunks/chunk-
|
|
7
|
+
} from "./chunks/chunk-6UHKGALE.ts";
|
|
8
|
+
import {
|
|
9
|
+
recoverSnapshotTransactionsOnStartup
|
|
10
|
+
} from "./chunks/chunk-AXXS2BPA.ts";
|
|
11
|
+
import {
|
|
12
|
+
unlock,
|
|
13
|
+
withLock
|
|
14
|
+
} from "./chunks/chunk-GWMHAQG4.ts";
|
|
15
|
+
import {
|
|
16
|
+
combineSignals,
|
|
17
|
+
throwIfAborted
|
|
18
|
+
} from "./chunks/chunk-NJFUIVYL.ts";
|
|
19
|
+
import {
|
|
20
|
+
ensureStateDir,
|
|
21
|
+
readStateForConfig,
|
|
22
|
+
syncStateFingerprint
|
|
23
|
+
} from "./chunks/chunk-JSYMGJQQ.ts";
|
|
24
|
+
import {
|
|
25
|
+
automaticSyncSummary
|
|
26
|
+
} from "./chunks/chunk-Z4DQF6L3.ts";
|
|
7
27
|
import {
|
|
8
28
|
completeSyncArguments,
|
|
9
29
|
parseOptions,
|
|
@@ -12,290 +32,51 @@ import {
|
|
|
12
32
|
splitArgs,
|
|
13
33
|
usage,
|
|
14
34
|
validateCommandOptions
|
|
15
|
-
} from "./chunks/chunk-
|
|
35
|
+
} from "./chunks/chunk-CHH26D4F.ts";
|
|
16
36
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
migrateLegacyStateDirectory,
|
|
38
|
+
stateDirectoryMigrationNotice,
|
|
39
|
+
withStateDirectoryAccess
|
|
40
|
+
} from "./chunks/chunk-ZI5MXMEX.ts";
|
|
19
41
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from "./chunks/chunk-LJDSHNS3.ts";
|
|
42
|
+
syncErrorGuidance
|
|
43
|
+
} from "./chunks/chunk-CPEHGXJV.ts";
|
|
23
44
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
isSyncDecisionRequiredError
|
|
27
|
-
} from "./chunks/chunk-W64NEHXT.ts";
|
|
28
|
-
import "./chunks/chunk-RGJ6SOYR.ts";
|
|
29
|
-
import "./chunks/chunk-EWX3TPLQ.ts";
|
|
45
|
+
safeTerminalText
|
|
46
|
+
} from "./chunks/chunk-LTBWGSBV.ts";
|
|
30
47
|
import {
|
|
31
48
|
activeLocalConfigPath,
|
|
32
49
|
configuredSyncSetupNames,
|
|
33
50
|
consumeLocalConfigMigrationNotice,
|
|
34
51
|
createLocalConfigDocument,
|
|
35
|
-
ensureStateDir,
|
|
36
|
-
isMissingConfigError,
|
|
37
52
|
loadConfig,
|
|
53
|
+
loadConfigForCheck,
|
|
38
54
|
loadPartialConfig,
|
|
39
55
|
localConfigPath,
|
|
40
56
|
localConfigTemplate,
|
|
41
57
|
readLocalConfigObject,
|
|
42
|
-
readStateForConfig,
|
|
43
58
|
sessionTokenWarnings,
|
|
59
|
+
syncCheckConfigFingerprint,
|
|
44
60
|
syncSessionsWarnings
|
|
45
|
-
} from "./chunks/chunk-
|
|
61
|
+
} from "./chunks/chunk-C7XY5NF5.ts";
|
|
62
|
+
import "./chunks/chunk-6LP3MCR2.ts";
|
|
63
|
+
import "./chunks/chunk-TEN2ZBJV.ts";
|
|
46
64
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} from "./chunks/chunk-
|
|
65
|
+
SetupPullRequiresUiError,
|
|
66
|
+
errorMessage,
|
|
67
|
+
isSyncDecisionRequiredError
|
|
68
|
+
} from "./chunks/chunk-2B2ELUF5.ts";
|
|
69
|
+
import "./chunks/chunk-THMF4BVL.ts";
|
|
70
|
+
import {
|
|
71
|
+
snapshotOptionsForContext
|
|
72
|
+
} from "./chunks/chunk-T3HH3UEJ.ts";
|
|
51
73
|
import {
|
|
52
74
|
RemoteSelectionMismatchError,
|
|
53
75
|
formatRemoteSelectionMismatch
|
|
54
|
-
} from "./chunks/chunk-
|
|
76
|
+
} from "./chunks/chunk-JAAKLL6M.ts";
|
|
55
77
|
|
|
56
|
-
// src/
|
|
78
|
+
// src/commands/command-execution.ts
|
|
57
79
|
var STATUS_KEY = "sync";
|
|
58
|
-
var AUTO_SYNC_OPTIONS = {
|
|
59
|
-
yes: true,
|
|
60
|
-
force: false,
|
|
61
|
-
stale: false,
|
|
62
|
-
silent: true,
|
|
63
|
-
reload: false,
|
|
64
|
-
auto: true,
|
|
65
|
-
args: []
|
|
66
|
-
};
|
|
67
|
-
function sync(pi, dependencies = {}) {
|
|
68
|
-
const loaders = {
|
|
69
|
-
setupSwitch: cachedModuleLoader(
|
|
70
|
-
dependencies.loadSetupSwitch ?? (() => import("./chunks/setup-switch-X3W3FALZ.ts"))
|
|
71
|
-
),
|
|
72
|
-
snapshot: cachedModuleLoader(dependencies.loadSnapshot ?? (() => import("./chunks/snapshot-WBSV4MQG.ts"))),
|
|
73
|
-
syncState: cachedModuleLoader(dependencies.loadSyncState ?? (() => import("./chunks/sync-state-36H7ULG2.ts"))),
|
|
74
|
-
operations: cachedModuleLoader(
|
|
75
|
-
dependencies.loadSyncOperations ?? (() => import("./chunks/sync-operations-MPAFVVHA.ts"))
|
|
76
|
-
)
|
|
77
|
-
};
|
|
78
|
-
const attention = createSyncAttentionController();
|
|
79
|
-
let sessionAbort = new AbortController();
|
|
80
|
-
let shutdownAbort;
|
|
81
|
-
pi.registerCommand("sync", {
|
|
82
|
-
description: "Sync Pi settings through Git, WebDAV, R2, or S3-compatible storage",
|
|
83
|
-
getArgumentCompletions: completeSyncArguments,
|
|
84
|
-
handler: async (args, ctx) => {
|
|
85
|
-
if (!ctx.hasUI) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
"/sync requires TUI or RPC mode so results and safety prompts are observable."
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
const run = () => handleCommand(args, ctx, sessionAbort.signal, loaders, attention);
|
|
91
|
-
if (splitArgs(args)[0] === "migrate-state") await run();
|
|
92
|
-
else await withStateDirectoryAccess(run);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
96
|
-
shutdownAbort?.abort(new DOMException("Session replaced", "AbortError"));
|
|
97
|
-
shutdownAbort = void 0;
|
|
98
|
-
sessionAbort.abort(new DOMException("Session replaced", "AbortError"));
|
|
99
|
-
sessionAbort = new AbortController();
|
|
100
|
-
const signal = sessionAbort.signal;
|
|
101
|
-
attention.reset(ctx);
|
|
102
|
-
let decision;
|
|
103
|
-
try {
|
|
104
|
-
decision = await withStateDirectoryAccess(() => startSession(ctx, signal, loaders));
|
|
105
|
-
} catch (error) {
|
|
106
|
-
if (signal.aborted) return;
|
|
107
|
-
ctx.ui.notify(`pi-sync state access failed: ${errorMessage(error)}`, "error");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (!decision || signal.aborted) return;
|
|
111
|
-
attention.set(decision, "sync");
|
|
112
|
-
if (ctx.mode !== "tui") {
|
|
113
|
-
ctx.ui.notify(
|
|
114
|
-
`pi-sync auto sync skipped: ${formatRemoteSelectionMismatch(
|
|
115
|
-
decision.setupName,
|
|
116
|
-
decision.localInclude,
|
|
117
|
-
decision.remoteInclude
|
|
118
|
-
)}
|
|
119
|
-
RPC review is read-only.`,
|
|
120
|
-
"warning"
|
|
121
|
-
);
|
|
122
|
-
} else if (attention.markOffered()) {
|
|
123
|
-
await resolveSelectionAttention(ctx, attention, signal, loaders, {
|
|
124
|
-
cancelLabel: "Later",
|
|
125
|
-
withStateAccess: withStateDirectoryAccess
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
if (!signal.aborted) attention.publish(ctx);
|
|
129
|
-
});
|
|
130
|
-
pi.on("session_shutdown", async (event, ctx) => {
|
|
131
|
-
sessionAbort.abort(new DOMException("Session shut down", "AbortError"));
|
|
132
|
-
attention.reset(ctx);
|
|
133
|
-
shutdownAbort?.abort(new DOMException("Session shut down again", "AbortError"));
|
|
134
|
-
const controller = new AbortController();
|
|
135
|
-
shutdownAbort = controller;
|
|
136
|
-
const signal = combineSignals(controller.signal, AbortSignal.timeout(3e4));
|
|
137
|
-
const reason = typeof event === "object" && event ? event.reason : void 0;
|
|
138
|
-
try {
|
|
139
|
-
if (reason !== "reload") {
|
|
140
|
-
await withStateDirectoryAccess(async () => {
|
|
141
|
-
if (signal.aborted) return;
|
|
142
|
-
await autoPushSessions(ctx, signal, loaders);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
} catch (error) {
|
|
146
|
-
if (!signal.aborted) {
|
|
147
|
-
ctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, "warning");
|
|
148
|
-
}
|
|
149
|
-
} finally {
|
|
150
|
-
if (shutdownAbort === controller) shutdownAbort = void 0;
|
|
151
|
-
}
|
|
152
|
-
if (signal.aborted) return;
|
|
153
|
-
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
async function startSession(ctx, signal, loaders) {
|
|
157
|
-
if (signal.aborted) return;
|
|
158
|
-
try {
|
|
159
|
-
const migrationNotice2 = stateDirectoryMigrationNotice();
|
|
160
|
-
if (migrationNotice2) ctx.ui.notify(migrationNotice2, "warning");
|
|
161
|
-
} catch (error) {
|
|
162
|
-
ctx.ui.notify(`pi-sync state directory requires attention: ${errorMessage(error)}`, "error");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
try {
|
|
166
|
-
await recoverSnapshotTransactionsOnStartup();
|
|
167
|
-
if (signal.aborted) return;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
if (signal.aborted) return;
|
|
170
|
-
ctx.ui.notify(`pi-sync recovery required: ${errorMessage(error)}`, "error");
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
setSyncSetupCompletions(await configuredSyncSetupNames());
|
|
175
|
-
if (signal.aborted) return;
|
|
176
|
-
} catch {
|
|
177
|
-
if (signal.aborted) return;
|
|
178
|
-
setSyncSetupCompletions([]);
|
|
179
|
-
}
|
|
180
|
-
const migrationNotice = consumeLocalConfigMigrationNotice();
|
|
181
|
-
if (migrationNotice) ctx.ui.notify(migrationNotice, "warning");
|
|
182
|
-
if (signal.aborted) return;
|
|
183
|
-
return autoSync(ctx, signal, loaders);
|
|
184
|
-
}
|
|
185
|
-
async function handleCommand(rawArgs, ctx, sessionSignal, loaders, attention) {
|
|
186
|
-
if (!rawArgs.trim()) {
|
|
187
|
-
try {
|
|
188
|
-
const { showSyncManager } = await import("./chunks/manager-ui-QO2P4V3Z.ts");
|
|
189
|
-
if (sessionSignal.aborted) return;
|
|
190
|
-
await showSyncManager(
|
|
191
|
-
ctx,
|
|
192
|
-
(route, signal, onCommit, target) => executeCommand(
|
|
193
|
-
route,
|
|
194
|
-
ctx,
|
|
195
|
-
combineSignals(sessionSignal, signal),
|
|
196
|
-
loaders,
|
|
197
|
-
onCommit,
|
|
198
|
-
target
|
|
199
|
-
),
|
|
200
|
-
sessionSignal,
|
|
201
|
-
{
|
|
202
|
-
getAttention: () => attention.current(),
|
|
203
|
-
onSelectionResolved: (expected) => {
|
|
204
|
-
if (attention.current() === expected) attention.clear(ctx);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
if (sessionSignal.aborted) return;
|
|
210
|
-
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
211
|
-
ctx.ui.notify(errorMessage(error), "error");
|
|
212
|
-
}
|
|
213
|
-
if (!sessionSignal.aborted) attention.publish(ctx);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const result = await executeCommand(rawArgs, ctx, sessionSignal, loaders);
|
|
217
|
-
if (result.kind === "decision-required") {
|
|
218
|
-
ctx.ui.notify(result.decision.directMessage, "error");
|
|
219
|
-
} else if (result.kind === "remote-selection-required") {
|
|
220
|
-
const origin = directSelectionOrigin(rawArgs);
|
|
221
|
-
if (origin) attention.set(result.decision, origin);
|
|
222
|
-
const deterministic = splitArgs(rawArgs).some((arg) => arg === "--yes" || arg === "-y");
|
|
223
|
-
if (origin && ctx.mode === "tui" && !deterministic) {
|
|
224
|
-
await resolveSelectionAttention(ctx, attention, sessionSignal, loaders);
|
|
225
|
-
} else {
|
|
226
|
-
ctx.ui.notify(
|
|
227
|
-
formatRemoteSelectionMismatch(
|
|
228
|
-
result.decision.setupName,
|
|
229
|
-
result.decision.localInclude,
|
|
230
|
-
result.decision.remoteInclude
|
|
231
|
-
),
|
|
232
|
-
"error"
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
await clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);
|
|
237
|
-
await reconcileSelectionAttention(ctx, attention, sessionSignal);
|
|
238
|
-
if (!sessionSignal.aborted) attention.publish(ctx);
|
|
239
|
-
}
|
|
240
|
-
async function clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, signal) {
|
|
241
|
-
if (result.kind !== "completed" || result.outcome === "cancelled" || signal.aborted) return;
|
|
242
|
-
const [command, ...rest] = splitArgs(rawArgs);
|
|
243
|
-
if (command !== "sync" && command !== "pull" && command !== "push") return;
|
|
244
|
-
const current = attention.current();
|
|
245
|
-
if (!current) return;
|
|
246
|
-
try {
|
|
247
|
-
const options = parseOptions(rest);
|
|
248
|
-
const setupName = options.setup ?? (await loadConfig()).setupName;
|
|
249
|
-
if (signal.aborted || attention.current() !== current) return;
|
|
250
|
-
if (current.decision.setupName === setupName) attention.clear(ctx);
|
|
251
|
-
} catch {
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
async function reconcileSelectionAttention(ctx, attention, signal) {
|
|
255
|
-
const current = attention.current();
|
|
256
|
-
if (!current || signal.aborted) return;
|
|
257
|
-
try {
|
|
258
|
-
const config = await loadConfig(current.decision.setupName);
|
|
259
|
-
if (signal.aborted || attention.current() !== current) return;
|
|
260
|
-
if (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);
|
|
261
|
-
} catch {
|
|
262
|
-
if (!signal.aborted && attention.current() === current) attention.clear(ctx);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
function directSelectionOrigin(rawArgs) {
|
|
266
|
-
const command = splitArgs(rawArgs)[0];
|
|
267
|
-
return command === "sync" || command === "pull" || command === "push" ? command : void 0;
|
|
268
|
-
}
|
|
269
|
-
async function resolveSelectionAttention(ctx, attention, signal, loaders, options = {}) {
|
|
270
|
-
const current = attention.current();
|
|
271
|
-
if (!current || signal.aborted) return;
|
|
272
|
-
const { dispatchManagerResult } = await import("./chunks/manager-result-dispatcher-RNRQA3PW.ts");
|
|
273
|
-
if (signal.aborted || attention.current() !== current) return;
|
|
274
|
-
await dispatchManagerResult(
|
|
275
|
-
ctx,
|
|
276
|
-
{ kind: "remote-selection-required", decision: current.decision },
|
|
277
|
-
current.origin,
|
|
278
|
-
(route, actionSignal, onCommit, target) => {
|
|
279
|
-
const execute = () => executeRecoveryCommand(
|
|
280
|
-
route,
|
|
281
|
-
ctx,
|
|
282
|
-
combineSignals(signal, actionSignal),
|
|
283
|
-
loaders,
|
|
284
|
-
onCommit,
|
|
285
|
-
target
|
|
286
|
-
);
|
|
287
|
-
return options.withStateAccess ? options.withStateAccess(execute) : execute();
|
|
288
|
-
},
|
|
289
|
-
signal,
|
|
290
|
-
{
|
|
291
|
-
cancelLabel: options.cancelLabel,
|
|
292
|
-
withStateAccess: options.withStateAccess,
|
|
293
|
-
onSelectionResolved: () => {
|
|
294
|
-
if (attention.current() === current) attention.clear(ctx);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
80
|
async function executeRecoveryCommand(rawArgs, ctx, signal, loaders, onCommit, setup) {
|
|
300
81
|
try {
|
|
301
82
|
const [subcommand, ...rest] = splitArgs(rawArgs);
|
|
@@ -330,7 +111,7 @@ async function executeRecoveryCommand(rawArgs, ctx, signal, loaders, onCommit, s
|
|
|
330
111
|
if (isSyncDecisionRequiredError(error)) {
|
|
331
112
|
return { kind: "decision-required", decision: error.decision };
|
|
332
113
|
}
|
|
333
|
-
ctx.ui.notify(
|
|
114
|
+
ctx.ui.notify(syncErrorGuidance(error), "error");
|
|
334
115
|
return { kind: "failed" };
|
|
335
116
|
}
|
|
336
117
|
}
|
|
@@ -374,7 +155,7 @@ async function executeCommand(rawArgs, ctx, signal, loaders, onCommit, setup) {
|
|
|
374
155
|
await showConfig(ctx, options);
|
|
375
156
|
return { kind: "completed" };
|
|
376
157
|
case "files": {
|
|
377
|
-
const { showFileSelection } = await import("./chunks/file-selection-
|
|
158
|
+
const { showFileSelection } = await import("./chunks/file-selection-FTTKAOYX.ts");
|
|
378
159
|
throwIfAborted(options.signal);
|
|
379
160
|
await showFileSelection(ctx, options.setup, options.signal);
|
|
380
161
|
return { kind: "completed" };
|
|
@@ -449,7 +230,7 @@ ${usage()}`, "warning");
|
|
|
449
230
|
if (isSyncDecisionRequiredError(error)) {
|
|
450
231
|
return { kind: "decision-required", decision: error.decision };
|
|
451
232
|
}
|
|
452
|
-
ctx.ui.notify(
|
|
233
|
+
ctx.ui.notify(syncErrorGuidance(error), "error");
|
|
453
234
|
return { kind: "failed" };
|
|
454
235
|
}
|
|
455
236
|
}
|
|
@@ -476,63 +257,6 @@ async function migrateStateDirectory(ctx, options) {
|
|
|
476
257
|
}
|
|
477
258
|
ctx.ui.notify(result.message, result.status === "migrated" ? "info" : "warning");
|
|
478
259
|
}
|
|
479
|
-
async function autoSync(ctx, signal, loaders) {
|
|
480
|
-
try {
|
|
481
|
-
const partial = await loadPartialConfig();
|
|
482
|
-
throwIfAborted(signal);
|
|
483
|
-
if (!partial.automatic) return;
|
|
484
|
-
await ensureStateDir();
|
|
485
|
-
throwIfAborted(signal);
|
|
486
|
-
await loadConfig();
|
|
487
|
-
throwIfAborted(signal);
|
|
488
|
-
const operations = await loaders.operations();
|
|
489
|
-
throwIfAborted(signal);
|
|
490
|
-
await withLock("auto-sync", () => {
|
|
491
|
-
throwIfAborted(signal);
|
|
492
|
-
return operations.syncBoth(ctx, { ...AUTO_SYNC_OPTIONS, signal });
|
|
493
|
-
});
|
|
494
|
-
} catch (error) {
|
|
495
|
-
if (signal.aborted || isMissingConfigError(error)) return;
|
|
496
|
-
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
497
|
-
if (error instanceof RemoteSelectionMismatchError) return error.decision;
|
|
498
|
-
ctx.ui.notify(`pi-sync auto sync skipped: ${errorMessage(error)}`, "warning");
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
async function autoPushSessions(ctx, signal, loaders) {
|
|
502
|
-
try {
|
|
503
|
-
const partial = await loadPartialConfig();
|
|
504
|
-
throwIfAborted(signal);
|
|
505
|
-
if (!partial.automatic) return;
|
|
506
|
-
if (!partial.include.includes("sessions")) return;
|
|
507
|
-
await ensureStateDir();
|
|
508
|
-
throwIfAborted(signal);
|
|
509
|
-
const config = await loadConfig();
|
|
510
|
-
throwIfAborted(signal);
|
|
511
|
-
if (!config.include.includes("sessions")) return;
|
|
512
|
-
const [operations, snapshotModule, syncStateModule] = await Promise.all([
|
|
513
|
-
loaders.operations(),
|
|
514
|
-
loaders.snapshot(),
|
|
515
|
-
loaders.syncState()
|
|
516
|
-
]);
|
|
517
|
-
throwIfAborted(signal);
|
|
518
|
-
await withLock("auto-session-push", async () => {
|
|
519
|
-
throwIfAborted(signal);
|
|
520
|
-
const state = await readStateForConfig(config);
|
|
521
|
-
throwIfAborted(signal);
|
|
522
|
-
const local = await snapshotModule.createSnapshot(
|
|
523
|
-
config.snapshotIdentity,
|
|
524
|
-
snapshotOptionsForContext(ctx, config)
|
|
525
|
-
);
|
|
526
|
-
throwIfAborted(signal);
|
|
527
|
-
if (!syncStateModule.hasLocalChanges(local, state, config)) return;
|
|
528
|
-
await operations.push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });
|
|
529
|
-
});
|
|
530
|
-
} catch (error) {
|
|
531
|
-
if (signal.aborted || isMissingConfigError(error)) return;
|
|
532
|
-
ctx.ui.setStatus(STATUS_KEY, void 0);
|
|
533
|
-
ctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, "warning");
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
260
|
async function initConfig(ctx, signal) {
|
|
537
261
|
const configPath = localConfigPath();
|
|
538
262
|
if (await readLocalConfigObject()) {
|
|
@@ -540,7 +264,7 @@ async function initConfig(ctx, signal) {
|
|
|
540
264
|
return;
|
|
541
265
|
}
|
|
542
266
|
if (ctx.mode === "tui") {
|
|
543
|
-
const { showSetupWizard } = await import("./chunks/
|
|
267
|
+
const { showSetupWizard } = await import("./chunks/setup-wizard-VB3VDOV5.ts");
|
|
544
268
|
throwIfAborted(signal);
|
|
545
269
|
await showSetupWizard(ctx, signal);
|
|
546
270
|
return;
|
|
@@ -565,7 +289,7 @@ async function showConfig(ctx, options) {
|
|
|
565
289
|
`storage connection: ${config.connectionName}`,
|
|
566
290
|
...storageLines,
|
|
567
291
|
`storage path: ${config.storagePath}`,
|
|
568
|
-
`automatic sync: ${config.automatic
|
|
292
|
+
`automatic sync: ${automaticSyncSummary(config.automatic)}`,
|
|
569
293
|
`included content: ${config.include.join(", ") || "none"}`,
|
|
570
294
|
`sessions: ${config.include.includes("sessions") ? "included" : "not included"}`,
|
|
571
295
|
`settings file: ${localConfigPath()}`,
|
|
@@ -628,27 +352,342 @@ function displayWebDavUrl(value, username) {
|
|
|
628
352
|
return "invalid (value hidden)";
|
|
629
353
|
}
|
|
630
354
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
355
|
+
|
|
356
|
+
// src/commands/command-handler.ts
|
|
357
|
+
var STATUS_KEY2 = "sync";
|
|
358
|
+
async function handleCommand(rawArgs, ctx, sessionSignal, loaders, attention) {
|
|
359
|
+
await reconcileObservation(attention, sessionSignal);
|
|
360
|
+
if (sessionSignal.aborted) return;
|
|
361
|
+
const run = (route, signal, onCommit, target) => executeCommand(
|
|
362
|
+
route,
|
|
363
|
+
ctx,
|
|
364
|
+
combineSignals(sessionSignal, signal),
|
|
365
|
+
loaders,
|
|
366
|
+
observationCommitCallback(attention, sessionSignal, onCommit),
|
|
367
|
+
target
|
|
368
|
+
);
|
|
369
|
+
if (!rawArgs.trim()) {
|
|
370
|
+
try {
|
|
371
|
+
const { showSyncManager } = await import("./chunks/manager-ui-ABATHC36.ts");
|
|
372
|
+
if (sessionSignal.aborted) return;
|
|
373
|
+
await showSyncManager(ctx, run, sessionSignal, {
|
|
374
|
+
getAttention: () => attention.current(),
|
|
375
|
+
getObservation: () => attention.observation(),
|
|
376
|
+
onObservationInvalidated: () => attention.clearObservation(),
|
|
377
|
+
onSelectionResolved: (expected) => {
|
|
378
|
+
if (attention.current() === expected) attention.clear(ctx);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (sessionSignal.aborted) return;
|
|
383
|
+
ctx.ui.setStatus(STATUS_KEY2, void 0);
|
|
384
|
+
ctx.ui.notify(errorMessage(error), "error");
|
|
385
|
+
}
|
|
386
|
+
await reconcileObservation(attention, sessionSignal);
|
|
387
|
+
if (!sessionSignal.aborted) attention.publish(ctx);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const result = await run(rawArgs);
|
|
391
|
+
if (result.kind === "decision-required") {
|
|
392
|
+
ctx.ui.notify(result.decision.directMessage, "error");
|
|
393
|
+
} else if (result.kind === "remote-selection-required") {
|
|
394
|
+
const origin = directSelectionOrigin(rawArgs);
|
|
395
|
+
if (origin) attention.set(result.decision, origin);
|
|
396
|
+
const deterministic = splitArgs(rawArgs).some((arg) => arg === "--yes" || arg === "-y");
|
|
397
|
+
if (origin && ctx.mode === "tui" && !deterministic) {
|
|
398
|
+
await resolveSelectionAttention(ctx, attention, sessionSignal, loaders);
|
|
399
|
+
} else {
|
|
400
|
+
ctx.ui.notify(
|
|
401
|
+
formatRemoteSelectionMismatch(
|
|
402
|
+
result.decision.setupName,
|
|
403
|
+
result.decision.localInclude,
|
|
404
|
+
result.decision.remoteInclude
|
|
405
|
+
),
|
|
406
|
+
"error"
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
await clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);
|
|
411
|
+
await reconcileSelectionAttention(ctx, attention, sessionSignal);
|
|
412
|
+
await reconcileObservation(attention, sessionSignal);
|
|
413
|
+
if (!sessionSignal.aborted) attention.publish(ctx);
|
|
414
|
+
}
|
|
415
|
+
function observationCommitCallback(attention, sessionSignal, onCommit) {
|
|
416
|
+
const observed = attention.observation();
|
|
417
|
+
return () => {
|
|
418
|
+
if (!sessionSignal.aborted && attention.observation() === observed)
|
|
419
|
+
attention.clearObservation();
|
|
420
|
+
onCommit?.();
|
|
421
|
+
};
|
|
634
422
|
}
|
|
635
|
-
function
|
|
636
|
-
|
|
423
|
+
async function reconcileObservation(attention, signal) {
|
|
424
|
+
const observed = attention.observation();
|
|
425
|
+
if (!observed || signal.aborted) return;
|
|
426
|
+
try {
|
|
427
|
+
const config = await loadConfig();
|
|
428
|
+
if (signal.aborted || attention.observation() !== observed) return;
|
|
429
|
+
if (!observationMatchesConfig(observed, config)) {
|
|
430
|
+
attention.clearObservation();
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const state = await readStateForConfig(config);
|
|
434
|
+
if (signal.aborted || attention.observation() !== observed) return;
|
|
435
|
+
if (syncStateFingerprint(state) !== observed.inspection.stateIdentity)
|
|
436
|
+
attention.clearObservation();
|
|
437
|
+
} catch {
|
|
438
|
+
if (!signal.aborted && attention.observation() === observed) attention.clearObservation();
|
|
439
|
+
}
|
|
637
440
|
}
|
|
638
|
-
function
|
|
441
|
+
async function clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, signal) {
|
|
442
|
+
if (result.kind !== "completed" || result.outcome === "cancelled" || signal.aborted) return;
|
|
443
|
+
const [command, ...rest] = splitArgs(rawArgs);
|
|
444
|
+
if (command !== "sync" && command !== "pull" && command !== "push") return;
|
|
445
|
+
const current = attention.current();
|
|
446
|
+
if (!current) return;
|
|
447
|
+
try {
|
|
448
|
+
const options = parseOptions(rest);
|
|
449
|
+
const setupName = options.setup ?? (await loadConfig()).setupName;
|
|
450
|
+
if (signal.aborted || attention.current() !== current) return;
|
|
451
|
+
if (current.decision.setupName === setupName) attention.clear(ctx);
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function reconcileSelectionAttention(ctx, attention, signal) {
|
|
456
|
+
const current = attention.current();
|
|
457
|
+
if (!current || signal.aborted) return;
|
|
458
|
+
try {
|
|
459
|
+
const config = await loadConfig(current.decision.setupName);
|
|
460
|
+
if (signal.aborted || attention.current() !== current) return;
|
|
461
|
+
if (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);
|
|
462
|
+
} catch {
|
|
463
|
+
if (!signal.aborted && attention.current() === current) attention.clear(ctx);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function directSelectionOrigin(rawArgs) {
|
|
467
|
+
const command = splitArgs(rawArgs)[0];
|
|
468
|
+
return command === "sync" || command === "pull" || command === "push" ? command : void 0;
|
|
469
|
+
}
|
|
470
|
+
async function resolveSelectionAttention(ctx, attention, signal, loaders, options = {}) {
|
|
471
|
+
const current = attention.current();
|
|
472
|
+
if (!current || signal.aborted) return;
|
|
473
|
+
const { dispatchManagerResult } = await import("./chunks/manager-result-dispatcher-U23ODD32.ts");
|
|
474
|
+
if (signal.aborted || attention.current() !== current) return;
|
|
475
|
+
await dispatchManagerResult(
|
|
476
|
+
ctx,
|
|
477
|
+
{ kind: "remote-selection-required", decision: current.decision },
|
|
478
|
+
current.origin,
|
|
479
|
+
(route, actionSignal, onCommit, target) => {
|
|
480
|
+
const execute = () => executeRecoveryCommand(
|
|
481
|
+
route,
|
|
482
|
+
ctx,
|
|
483
|
+
combineSignals(signal, actionSignal),
|
|
484
|
+
loaders,
|
|
485
|
+
observationCommitCallback(attention, signal, onCommit),
|
|
486
|
+
target
|
|
487
|
+
);
|
|
488
|
+
return options.withStateAccess ? options.withStateAccess(execute) : execute();
|
|
489
|
+
},
|
|
490
|
+
signal,
|
|
491
|
+
{
|
|
492
|
+
cancelLabel: options.cancelLabel,
|
|
493
|
+
withStateAccess: options.withStateAccess,
|
|
494
|
+
onSelectionResolved: () => {
|
|
495
|
+
if (attention.current() === current) attention.clear(ctx);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/settings/config-errors.ts
|
|
502
|
+
function isMissingConfigError(error) {
|
|
503
|
+
return error instanceof Error && (error.message.startsWith("Missing pi-sync settings.") || error.message === "No sync setups are configured.");
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/sync/automatic-sync.ts
|
|
507
|
+
var AUTO_SYNC_OPTIONS = {
|
|
508
|
+
yes: true,
|
|
509
|
+
force: false,
|
|
510
|
+
stale: false,
|
|
511
|
+
silent: true,
|
|
512
|
+
reload: false,
|
|
513
|
+
auto: true,
|
|
514
|
+
args: []
|
|
515
|
+
};
|
|
516
|
+
var STATUS_KEY3 = "sync";
|
|
517
|
+
async function startSession(ctx, signal) {
|
|
518
|
+
throwIfAborted(signal);
|
|
519
|
+
const stateNotice = stateDirectoryMigrationNotice();
|
|
520
|
+
if (stateNotice && ctx.hasUI) ctx.ui.notify(stateNotice, "warning");
|
|
521
|
+
await recoverSnapshotTransactionsOnStartup();
|
|
522
|
+
throwIfAborted(signal);
|
|
523
|
+
try {
|
|
524
|
+
const names = await configuredSyncSetupNames();
|
|
525
|
+
throwIfAborted(signal);
|
|
526
|
+
setSyncSetupCompletions(names);
|
|
527
|
+
} catch {
|
|
528
|
+
if (signal.aborted) return;
|
|
529
|
+
setSyncSetupCompletions([]);
|
|
530
|
+
}
|
|
531
|
+
const migrationNotice = consumeLocalConfigMigrationNotice();
|
|
532
|
+
if (migrationNotice && ctx.hasUI) ctx.ui.notify(migrationNotice, "warning");
|
|
533
|
+
throwIfAborted(signal);
|
|
534
|
+
if (!ctx.hasUI) return;
|
|
535
|
+
try {
|
|
536
|
+
const config = await loadConfig();
|
|
537
|
+
throwIfAborted(signal);
|
|
538
|
+
return config.automatic ? config : void 0;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
throwIfAborted(signal);
|
|
541
|
+
if (!isMissingConfigError(error)) {
|
|
542
|
+
ctx.ui.notify(
|
|
543
|
+
`pi-sync startup check skipped: ${safeTerminalText(errorMessage(error))}`,
|
|
544
|
+
"warning"
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async function autoPushSessions(ctx, signal, loaders) {
|
|
550
|
+
try {
|
|
551
|
+
const partial = await loadPartialConfig();
|
|
552
|
+
throwIfAborted(signal);
|
|
553
|
+
if (!partial.automatic) return;
|
|
554
|
+
if (!partial.include.includes("sessions")) return;
|
|
555
|
+
await ensureStateDir();
|
|
556
|
+
throwIfAborted(signal);
|
|
557
|
+
const config = await loadConfig();
|
|
558
|
+
throwIfAborted(signal);
|
|
559
|
+
if (!config.include.includes("sessions")) return;
|
|
560
|
+
const [operations, snapshotModule, syncStateModule] = await Promise.all([
|
|
561
|
+
loaders.operations(),
|
|
562
|
+
loaders.snapshot(),
|
|
563
|
+
loaders.syncState()
|
|
564
|
+
]);
|
|
565
|
+
throwIfAborted(signal);
|
|
566
|
+
await withLock("auto-session-push", async () => {
|
|
567
|
+
throwIfAborted(signal);
|
|
568
|
+
const state = await readStateForConfig(config);
|
|
569
|
+
throwIfAborted(signal);
|
|
570
|
+
const local = await snapshotModule.createSnapshot(config.snapshotIdentity, {
|
|
571
|
+
...snapshotOptionsForContext(ctx, config),
|
|
572
|
+
signal
|
|
573
|
+
});
|
|
574
|
+
throwIfAborted(signal);
|
|
575
|
+
if (!syncStateModule.hasLocalChanges(local, state, config)) return;
|
|
576
|
+
await operations.push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });
|
|
577
|
+
});
|
|
578
|
+
} catch (error) {
|
|
579
|
+
if (signal.aborted || isMissingConfigError(error)) return;
|
|
580
|
+
ctx.ui.setStatus(STATUS_KEY3, void 0);
|
|
581
|
+
ctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, "warning");
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/sync/startup-check.ts
|
|
586
|
+
var STATUS_KEY4 = "sync";
|
|
587
|
+
function createStartupCheck(loaders, attention, timeoutMs = 3e4) {
|
|
588
|
+
let active;
|
|
639
589
|
return {
|
|
640
|
-
|
|
641
|
-
|
|
590
|
+
start(ctx, sessionSignal, initialConfig) {
|
|
591
|
+
if (!ctx.hasUI || sessionSignal.aborted) return;
|
|
592
|
+
if (active) throw new Error("Previous startup check has not been drained.");
|
|
593
|
+
const controller = new AbortController();
|
|
594
|
+
const signal = combineSignals(sessionSignal, controller.signal);
|
|
595
|
+
const task = { controller, owner: ctx.sessionManager, promise: Promise.resolve() };
|
|
596
|
+
active = task;
|
|
597
|
+
const isCurrent = () => !sessionSignal.aborted && active === task && task.owner === ctx.sessionManager;
|
|
598
|
+
const timer = setTimeout(
|
|
599
|
+
() => controller.abort(new DOMException("Startup check timed out", "TimeoutError")),
|
|
600
|
+
timeoutMs
|
|
601
|
+
);
|
|
602
|
+
let config;
|
|
603
|
+
let checking = false;
|
|
604
|
+
task.promise = Promise.resolve().then(async () => {
|
|
605
|
+
try {
|
|
606
|
+
throwIfAborted(signal);
|
|
607
|
+
config = initialConfig ?? await loadConfigForCheck();
|
|
608
|
+
throwIfAborted(signal);
|
|
609
|
+
if (!config.automatic) return;
|
|
610
|
+
const captured = config;
|
|
611
|
+
const identity = syncCheckConfigFingerprint(captured);
|
|
612
|
+
checking = true;
|
|
613
|
+
ctx.ui.setStatus(STATUS_KEY4, "checking in background");
|
|
614
|
+
const { inspectSync } = await loaders.inspection();
|
|
615
|
+
throwIfAborted(signal);
|
|
616
|
+
const currentConfig = await loadConfigForCheck();
|
|
617
|
+
throwIfAborted(signal);
|
|
618
|
+
if (syncCheckConfigFingerprint(currentConfig) !== identity) return;
|
|
619
|
+
const inspection = await withStateDirectoryAccess(() => {
|
|
620
|
+
throwIfAborted(signal);
|
|
621
|
+
return withLock("startup-check", () => {
|
|
622
|
+
throwIfAborted(signal);
|
|
623
|
+
return inspectSync(captured, snapshotOptionsForContext(ctx, captured), signal);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
throwIfAborted(signal);
|
|
627
|
+
const latest = await loadConfigForCheck();
|
|
628
|
+
throwIfAborted(signal);
|
|
629
|
+
if (syncCheckConfigFingerprint(latest) !== identity) return;
|
|
630
|
+
const state = await withStateDirectoryAccess(() => readStateForConfig(latest));
|
|
631
|
+
throwIfAborted(signal);
|
|
632
|
+
if (syncStateFingerprint(state) !== inspection.stateIdentity || !isCurrent()) return;
|
|
633
|
+
attention.observe({
|
|
634
|
+
setupName: captured.setupName,
|
|
635
|
+
configIdentity: identity,
|
|
636
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
637
|
+
inspection
|
|
638
|
+
});
|
|
639
|
+
if (ctx.mode === "rpc") attention.notifyObservation(ctx);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
if (!isCurrent() || sessionSignal.aborted || isMissingConfigError(error)) return;
|
|
642
|
+
if (signal.aborted && controller.signal.reason?.name !== "TimeoutError") return;
|
|
643
|
+
if (config) {
|
|
644
|
+
const latest = await loadConfigForCheck().catch(() => void 0);
|
|
645
|
+
if (!isCurrent() || signal.aborted && controller.signal.reason?.name !== "TimeoutError")
|
|
646
|
+
return;
|
|
647
|
+
if (!latest || syncCheckConfigFingerprint(latest) !== syncCheckConfigFingerprint(config))
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
ctx.ui.notify(
|
|
651
|
+
`pi-sync startup check skipped: ${safeTerminalText(errorMessage(error))}. Run /sync status to retry. No startup transfer was performed.`,
|
|
652
|
+
"warning"
|
|
653
|
+
);
|
|
654
|
+
} finally {
|
|
655
|
+
clearTimeout(timer);
|
|
656
|
+
if (isCurrent() && checking) attention.publish(ctx);
|
|
657
|
+
if (active === task) active = void 0;
|
|
658
|
+
}
|
|
659
|
+
}).catch(() => {
|
|
660
|
+
clearTimeout(timer);
|
|
661
|
+
if (active === task) active = void 0;
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
async stop() {
|
|
665
|
+
const task = active;
|
|
666
|
+
if (!task) return;
|
|
667
|
+
task.controller.abort(new DOMException("Startup check cancelled", "AbortError"));
|
|
668
|
+
await task.promise;
|
|
669
|
+
}
|
|
642
670
|
};
|
|
643
671
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
672
|
+
|
|
673
|
+
// src/sync/sync-loaders.ts
|
|
674
|
+
function createSyncLoaders(dependencies) {
|
|
675
|
+
const loaders = {
|
|
676
|
+
inspection: cachedModuleLoader(
|
|
677
|
+
dependencies.loadSyncInspection ?? (() => import("./chunks/sync-inspection-FEP4L5TR.ts"))
|
|
678
|
+
),
|
|
679
|
+
setupSwitch: cachedModuleLoader(
|
|
680
|
+
dependencies.loadSetupSwitch ?? (() => import("./chunks/setup-switch-IS2PEZ3J.ts"))
|
|
681
|
+
),
|
|
682
|
+
snapshot: cachedModuleLoader(
|
|
683
|
+
dependencies.loadSnapshot ?? (() => import("./chunks/snapshot-HKEWCVK6.ts"))
|
|
684
|
+
),
|
|
685
|
+
syncState: cachedModuleLoader(dependencies.loadSyncState ?? (() => import("./chunks/sync-state-Q2FTXTW6.ts"))),
|
|
686
|
+
operations: cachedModuleLoader(
|
|
687
|
+
dependencies.loadSyncOperations ?? (() => import("./chunks/sync-operations-ONJ27QRL.ts"))
|
|
688
|
+
)
|
|
689
|
+
};
|
|
690
|
+
return loaders;
|
|
652
691
|
}
|
|
653
692
|
function cachedModuleLoader(load) {
|
|
654
693
|
let pending;
|
|
@@ -662,6 +701,102 @@ function cachedModuleLoader(load) {
|
|
|
662
701
|
return pending;
|
|
663
702
|
};
|
|
664
703
|
}
|
|
704
|
+
|
|
705
|
+
// src/sync-extension.ts
|
|
706
|
+
var STATUS_KEY5 = "sync";
|
|
707
|
+
function sync(pi, dependencies = {}) {
|
|
708
|
+
const loaders = createSyncLoaders(dependencies);
|
|
709
|
+
const attention = createSyncAttentionController();
|
|
710
|
+
const check = createStartupCheck(loaders, attention);
|
|
711
|
+
let sessionAbort = new AbortController();
|
|
712
|
+
let shutdownAbort;
|
|
713
|
+
let initialization = Promise.resolve(void 0);
|
|
714
|
+
pi.registerCommand("sync", {
|
|
715
|
+
description: "Sync Pi settings through Git, WebDAV, R2, or S3-compatible storage",
|
|
716
|
+
getArgumentCompletions: completeSyncArguments,
|
|
717
|
+
handler: async (args, ctx) => {
|
|
718
|
+
if (!ctx.hasUI) {
|
|
719
|
+
throw new Error(
|
|
720
|
+
"/sync requires TUI or RPC mode so results and safety prompts are observable."
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
const signal = sessionAbort.signal;
|
|
724
|
+
const ready = await initialization;
|
|
725
|
+
if (signal.aborted) return;
|
|
726
|
+
await check.stop();
|
|
727
|
+
if (signal.aborted) return;
|
|
728
|
+
const command = splitArgs(args)[0];
|
|
729
|
+
if (ready === false && command !== "help" && command !== "unlock" && command !== "migrate-state") {
|
|
730
|
+
ctx.ui.notify(
|
|
731
|
+
"pi-sync recovery required. Repair the startup error, then /reload before syncing. Use /sync help for recovery guidance.",
|
|
732
|
+
"error"
|
|
733
|
+
);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const run = () => handleCommand(args, ctx, signal, loaders, attention);
|
|
737
|
+
if (command === "migrate-state" || command === "help") await run();
|
|
738
|
+
else await withStateDirectoryAccess(run);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
742
|
+
shutdownAbort?.abort(new DOMException("Session replaced", "AbortError"));
|
|
743
|
+
shutdownAbort = void 0;
|
|
744
|
+
sessionAbort.abort(new DOMException("Session replaced", "AbortError"));
|
|
745
|
+
sessionAbort = new AbortController();
|
|
746
|
+
const signal = sessionAbort.signal;
|
|
747
|
+
attention.reset(ctx);
|
|
748
|
+
const previous = initialization;
|
|
749
|
+
initialization = (async () => {
|
|
750
|
+
await previous;
|
|
751
|
+
if (signal.aborted) return false;
|
|
752
|
+
await check.stop();
|
|
753
|
+
if (signal.aborted) return false;
|
|
754
|
+
try {
|
|
755
|
+
const config = await withStateDirectoryAccess(() => startSession(ctx, signal));
|
|
756
|
+
return signal.aborted ? false : config;
|
|
757
|
+
} catch (error) {
|
|
758
|
+
if (!signal.aborted && ctx.hasUI) {
|
|
759
|
+
ctx.ui.notify(
|
|
760
|
+
`pi-sync recovery required: ${safeTerminalText(errorMessage(error))}`,
|
|
761
|
+
"error"
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
})();
|
|
767
|
+
const ready = await initialization;
|
|
768
|
+
if (ready && !signal.aborted) check.start(ctx, signal, ready);
|
|
769
|
+
});
|
|
770
|
+
pi.on("session_shutdown", async (event, ctx) => {
|
|
771
|
+
sessionAbort.abort(new DOMException("Session shut down", "AbortError"));
|
|
772
|
+
attention.reset(ctx);
|
|
773
|
+
shutdownAbort?.abort(new DOMException("Session shut down again", "AbortError"));
|
|
774
|
+
const controller = new AbortController();
|
|
775
|
+
shutdownAbort = controller;
|
|
776
|
+
const signal = combineSignals(controller.signal, AbortSignal.timeout(3e4));
|
|
777
|
+
const reason = typeof event === "object" && event ? event.reason : void 0;
|
|
778
|
+
try {
|
|
779
|
+
const ready = await initialization;
|
|
780
|
+
if (signal.aborted) return;
|
|
781
|
+
await check.stop();
|
|
782
|
+
if (signal.aborted) return;
|
|
783
|
+
if (ready !== false && reason !== "reload") {
|
|
784
|
+
await withStateDirectoryAccess(async () => {
|
|
785
|
+
if (signal.aborted) return;
|
|
786
|
+
await autoPushSessions(ctx, signal, loaders);
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
} catch (error) {
|
|
790
|
+
if (!signal.aborted) {
|
|
791
|
+
ctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, "warning");
|
|
792
|
+
}
|
|
793
|
+
} finally {
|
|
794
|
+
if (shutdownAbort === controller) shutdownAbort = void 0;
|
|
795
|
+
}
|
|
796
|
+
if (signal.aborted) return;
|
|
797
|
+
ctx.ui.setStatus(STATUS_KEY5, void 0);
|
|
798
|
+
});
|
|
799
|
+
}
|
|
665
800
|
export {
|
|
666
801
|
sync as default
|
|
667
802
|
};
|