@narumitw/pi-sync 0.50.3 → 0.51.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.
Files changed (218) hide show
  1. package/README.md +4 -1
  2. package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
  3. package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
  4. package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
  5. package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
  6. package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
  7. package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
  8. package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
  9. package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
  10. package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
  11. package/dist/chunks/chunk-6546TS54.ts.map +7 -0
  12. package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
  13. package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
  14. package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
  15. package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
  16. package/dist/chunks/{chunk-YDWUZVPN.ts → chunk-7KHKWL3Q.ts} +28 -47
  17. package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
  18. package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
  19. package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
  20. package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
  21. package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
  22. package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
  23. package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
  24. package/dist/chunks/{chunk-YHEMENKV.ts → chunk-E42GXD6D.ts} +13 -19
  25. package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
  26. package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
  27. package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
  28. package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
  29. package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
  30. package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
  31. package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
  32. package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
  33. package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
  34. package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
  35. package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
  36. package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
  37. package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
  38. package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
  39. package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
  40. package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
  41. package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
  42. package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
  43. package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
  44. package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
  45. package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
  46. package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
  47. package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
  48. package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
  49. package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
  50. package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
  51. package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
  52. package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
  53. package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
  54. package/dist/chunks/{chunk-DHVFS4QK.ts → chunk-UA7RXOIT.ts} +37 -90
  55. package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
  56. package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
  57. package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
  58. package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
  59. package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
  60. package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
  61. package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
  62. package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
  63. package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
  64. package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
  65. package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
  67. package/dist/chunks/{manager-ui-IPAOAWZA.ts → manager-ui-763NEKJY.ts} +124 -251
  68. package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
  70. package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
  71. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
  73. package/dist/chunks/{setup-wizard-JUJZYNNM.ts → setup-wizard-YIEWGX32.ts} +12 -12
  74. package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
  75. package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
  76. package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
  77. package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
  78. package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
  79. package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
  80. package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
  81. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
  82. package/dist/index.ts +49 -74
  83. package/dist/index.ts.map +3 -3
  84. package/docs/settings.md +9 -0
  85. package/package.json +61 -62
  86. package/src/backends/backend-factory.ts +15 -15
  87. package/src/backends/backend-identity.ts +31 -31
  88. package/src/backends/backend-types.ts +42 -42
  89. package/src/backends/git/git-backend.ts +714 -788
  90. package/src/backends/git/git-config.ts +104 -111
  91. package/src/backends/git/git-runner.ts +230 -247
  92. package/src/backends/git/git-storage.ts +228 -245
  93. package/src/backends/s3/s3-backend.ts +408 -441
  94. package/src/backends/s3/s3-client.ts +288 -333
  95. package/src/backends/sync-backend.ts +65 -65
  96. package/src/backends/webdav/webdav-backend.ts +542 -589
  97. package/src/backends/webdav/webdav-client.ts +292 -316
  98. package/src/backends/webdav/webdav-config.ts +57 -68
  99. package/src/commands/command-execution.ts +270 -289
  100. package/src/commands/command-handler.ts +160 -163
  101. package/src/commands/command-types.ts +13 -13
  102. package/src/commands/command.ts +190 -196
  103. package/src/commands/setup-completions.ts +1 -1
  104. package/src/paths.ts +47 -47
  105. package/src/settings/config-errors.ts +4 -5
  106. package/src/settings/config-file.ts +473 -506
  107. package/src/settings/config.ts +171 -174
  108. package/src/settings/settings-management.ts +191 -216
  109. package/src/settings/settings-store.ts +63 -77
  110. package/src/settings/settings-types.ts +78 -75
  111. package/src/settings/settings-validation.ts +328 -364
  112. package/src/snapshot/session-paths.ts +41 -55
  113. package/src/snapshot/snapshot-apply.ts +197 -229
  114. package/src/snapshot/snapshot-codec.ts +29 -31
  115. package/src/snapshot/snapshot-paths.ts +4 -6
  116. package/src/snapshot/snapshot-transaction.ts +161 -170
  117. package/src/snapshot/snapshot-types.ts +24 -24
  118. package/src/snapshot/snapshot.ts +310 -343
  119. package/src/state/json-file.ts +11 -11
  120. package/src/state/lock.ts +226 -242
  121. package/src/state/lockfile-fs.ts +20 -20
  122. package/src/state/operation-availability.ts +36 -41
  123. package/src/state/state-directory.ts +171 -173
  124. package/src/state/state-types.ts +15 -15
  125. package/src/state/sync-state-store.ts +23 -23
  126. package/src/sync/automatic-sync.ts +75 -80
  127. package/src/sync/remote-snapshot.ts +52 -60
  128. package/src/sync/setup-switch.ts +99 -114
  129. package/src/sync/signals.ts +3 -5
  130. package/src/sync/startup-check.ts +97 -111
  131. package/src/sync/sync-decision.ts +76 -80
  132. package/src/sync/sync-error-guidance.ts +41 -48
  133. package/src/sync/sync-errors.ts +10 -10
  134. package/src/sync/sync-inspection.ts +29 -33
  135. package/src/sync/sync-loaders.ts +28 -36
  136. package/src/sync/sync-mutations.ts +510 -622
  137. package/src/sync/sync-operations.ts +7 -7
  138. package/src/sync/sync-policy.ts +296 -326
  139. package/src/sync/sync-queries.ts +175 -194
  140. package/src/sync/sync-state.ts +74 -100
  141. package/src/sync-extension.ts +89 -101
  142. package/src/sync.ts +26 -26
  143. package/src/types.ts +37 -37
  144. package/src/ui/automatic-sync-summary.ts +1 -3
  145. package/src/ui/cancellable-operation.ts +89 -101
  146. package/src/ui/file-selection.ts +250 -274
  147. package/src/ui/manager-attention.ts +51 -63
  148. package/src/ui/manager-recovery.ts +48 -57
  149. package/src/ui/manager-result-dispatcher.ts +62 -76
  150. package/src/ui/manager-state.ts +200 -232
  151. package/src/ui/manager-ui.ts +264 -362
  152. package/src/ui/remote-selection-ui.ts +547 -588
  153. package/src/ui/secret-input.ts +197 -205
  154. package/src/ui/settings-ui.ts +175 -172
  155. package/src/ui/setup/git-ui.ts +232 -261
  156. package/src/ui/setup/s3-credentials-ui.ts +58 -65
  157. package/src/ui/setup/s3-ui.ts +171 -195
  158. package/src/ui/setup/setup-actions.ts +91 -101
  159. package/src/ui/setup/setup-location-ui.ts +121 -137
  160. package/src/ui/setup/setup-name-ui.ts +27 -27
  161. package/src/ui/setup/setup-prompts.ts +65 -65
  162. package/src/ui/setup/setup-review.ts +58 -59
  163. package/src/ui/setup/setup-switcher.ts +156 -160
  164. package/src/ui/setup/setup-wizard.ts +28 -28
  165. package/src/ui/setup/text-input.ts +54 -58
  166. package/src/ui/setup/webdav-ui.ts +246 -293
  167. package/src/ui/storage-connections-ui.ts +317 -369
  168. package/src/ui/sync-attention.ts +148 -164
  169. package/src/ui/sync-format.ts +110 -121
  170. package/src/ui/sync-resolution-ui.ts +147 -153
  171. package/src/ui/sync-setups-ui.ts +218 -238
  172. package/src/ui/sync-status.ts +17 -0
  173. package/src/ui/terminal-text.ts +2 -2
  174. package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
  175. package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
  176. package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
  177. package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
  178. package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
  179. package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
  180. package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
  181. package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
  182. package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
  183. package/dist/chunks/chunk-DHVFS4QK.ts.map +0 -7
  184. package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
  185. package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
  186. package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
  187. package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
  188. package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
  189. package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
  190. package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
  191. package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
  192. package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
  193. package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
  194. package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
  195. package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
  196. package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
  197. package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
  198. package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
  199. package/dist/chunks/chunk-YDWUZVPN.ts.map +0 -7
  200. package/dist/chunks/chunk-YHEMENKV.ts.map +0 -7
  201. package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
  202. package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
  203. package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
  204. package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
  205. package/dist/chunks/manager-result-dispatcher-ASKGOSW5.ts +0 -22
  206. package/dist/chunks/manager-ui-IPAOAWZA.ts.map +0 -7
  207. package/dist/chunks/remote-selection-ui-QDNR7WEI.ts +0 -21
  208. package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
  209. package/dist/chunks/setup-wizard-JUJZYNNM.ts.map +0 -7
  210. package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
  211. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
  212. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
  213. /package/dist/chunks/{manager-result-dispatcher-ASKGOSW5.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
  214. /package/dist/chunks/{remote-selection-ui-QDNR7WEI.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
  215. /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
  216. /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
  217. /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
  218. /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
@@ -1,238 +1,235 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { backendIdentityCoordinates } from "../backends/backend-identity.js";
3
- import {
4
- normalizeGitBranch,
5
- normalizeGitDirectory,
6
- normalizeGitRemote,
7
- } from "../backends/git/git-config.js";
3
+ import { normalizeGitBranch, normalizeGitDirectory, normalizeGitRemote } from "../backends/git/git-config.js";
8
4
  import { normalizeWebDavPath, normalizeWebDavUrl } from "../backends/webdav/webdav-config.js";
9
5
  import { normalizeSyncInclude } from "../sync/sync-policy.js";
10
6
  import { localConfigPath, readActiveLocalConfigDocumentForRepair } from "./config-file.js";
11
7
  import { requireSettings } from "./settings-store.js";
12
8
  import type {
13
- AnySyncConfig,
14
- OnSwitchAction,
15
- PartialConfig,
16
- PiSyncSettingsV3,
17
- StorageConnectionSettings,
18
- SyncSetupSettings,
9
+ AnySyncConfig,
10
+ OnSwitchAction,
11
+ PartialConfig,
12
+ PiSyncSettingsV3,
13
+ StorageConnectionSettings,
14
+ SyncSetupSettings,
19
15
  } from "./settings-types.js";
20
16
  import {
21
- DEFAULT_ON_SWITCH,
22
- isCloudflareR2Endpoint,
23
- normalizeS3Bucket,
24
- normalizeS3Endpoint,
25
- normalizeStoragePath,
26
- optionalString,
27
- ownObject,
28
- requiredString,
29
- validateConfigName,
30
- validateSettingsDocument,
17
+ DEFAULT_ON_SWITCH,
18
+ isCloudflareR2Endpoint,
19
+ normalizeS3Bucket,
20
+ normalizeS3Endpoint,
21
+ normalizeStoragePath,
22
+ optionalString,
23
+ ownObject,
24
+ requiredString,
25
+ validateConfigName,
26
+ validateSettingsDocument,
31
27
  } from "./settings-validation.js";
32
28
 
33
29
  export async function loadConfig(setupName?: string): Promise<AnySyncConfig> {
34
- return configFromSettings(await requireSettings(), setupName);
30
+ return configFromSettings(await requireSettings(), setupName);
35
31
  }
36
32
 
37
33
  /** A background freshness read must never trigger legacy-file publication. */
38
34
  export async function loadConfigForCheck(): Promise<AnySyncConfig> {
39
- const document = await readActiveLocalConfigDocumentForRepair();
40
- if (!document) throw new Error(`Missing pi-sync settings: ${localConfigPath()}`);
41
- validateSettingsDocument(document.parsed);
42
- return configFromSettings(document.parsed as PiSyncSettingsV3);
35
+ const document = await readActiveLocalConfigDocumentForRepair();
36
+ if (!document) throw new Error(`Missing pi-sync settings: ${localConfigPath()}`);
37
+ validateSettingsDocument(document.parsed);
38
+ return configFromSettings(document.parsed as PiSyncSettingsV3);
43
39
  }
44
40
 
45
41
  function configFromSettings(settings: PiSyncSettingsV3, setupName?: string): AnySyncConfig {
46
- const selectedName = setupName ?? settings.activeSyncSetup;
47
- if (!selectedName) throw new Error("No sync setups are configured.");
48
- validateConfigName(selectedName, "sync setup");
49
- const setup = ownObject<SyncSetupSettings>(settings.syncSetups, selectedName);
50
- if (!setup)
51
- throw new Error(`Invalid pi-sync settings: sync setup “${selectedName}” was not found.`);
52
- const connectionName = setup.storage.connection;
53
- const connection = ownObject<StorageConnectionSettings>(
54
- settings.storageConnections,
55
- connectionName,
56
- );
57
- if (!connection) {
58
- throw new Error(
59
- `Invalid pi-sync settings: sync setup “${selectedName}” references missing storage connection “${connectionName}”.`,
60
- );
61
- }
62
- return resolveSyncConfig(
63
- selectedName,
64
- setup,
65
- connectionName,
66
- connection,
67
- settings.onSwitch,
68
- settings.skipSecretScan ?? false,
69
- );
42
+ const selectedName = setupName ?? settings.activeSyncSetup;
43
+ if (!selectedName) throw new Error("No sync setups are configured.");
44
+ validateConfigName(selectedName, "sync setup");
45
+ const setup = ownObject<SyncSetupSettings>(settings.syncSetups, selectedName);
46
+ if (!setup) throw new Error(`Invalid pi-sync settings: sync setup “${selectedName}” was not found.`);
47
+ const connectionName = setup.storage.connection;
48
+ const connection = ownObject<StorageConnectionSettings>(settings.storageConnections, connectionName);
49
+ if (!connection) {
50
+ throw new Error(
51
+ `Invalid pi-sync settings: sync setup “${selectedName}” references missing storage connection “${connectionName}”.`,
52
+ );
53
+ }
54
+ return resolveSyncConfig(
55
+ selectedName,
56
+ setup,
57
+ connectionName,
58
+ connection,
59
+ settings.onSwitch,
60
+ settings.skipSecretScan ?? false,
61
+ settings.showStatus ?? true,
62
+ );
70
63
  }
71
64
 
72
65
  export type SyncSetupStorageReview = Pick<
73
- PartialConfig,
74
- "connectionName" | "storageKind" | "storagePath" | "bucket" | "branch"
66
+ PartialConfig,
67
+ "connectionName" | "storageKind" | "storagePath" | "bucket" | "branch"
75
68
  >;
76
69
 
77
70
  /** A validated setup-facing projection used by manager and settings UI. */
78
71
  export async function loadPartialConfig(setupName?: string): Promise<PartialConfig> {
79
- const config = await loadConfig(setupName);
80
- return {
81
- setupName: config.setupName,
82
- ...storageReviewFromConfig(config),
83
- include: [...config.include],
84
- automatic: config.automatic,
85
- onSwitch: config.onSwitch,
86
- };
72
+ const config = await loadConfig(setupName);
73
+ return {
74
+ setupName: config.setupName,
75
+ ...storageReviewFromConfig(config),
76
+ include: [...config.include],
77
+ automatic: config.automatic,
78
+ onSwitch: config.onSwitch,
79
+ showStatus: config.showStatus,
80
+ };
87
81
  }
88
82
 
89
83
  export function syncSetupStorageReview(
90
- setupName: string,
91
- setup: SyncSetupSettings,
92
- connectionName: string,
93
- connection: StorageConnectionSettings,
84
+ setupName: string,
85
+ setup: SyncSetupSettings,
86
+ connectionName: string,
87
+ connection: StorageConnectionSettings,
94
88
  ): SyncSetupStorageReview {
95
- return storageReviewFromConfig(
96
- resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false),
97
- );
89
+ return storageReviewFromConfig(
90
+ resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false, true),
91
+ );
98
92
  }
99
93
 
100
94
  export function syncSetupReviewIdentity(
101
- setupName: string,
102
- setup: SyncSetupSettings,
103
- connectionName: string,
104
- connection: StorageConnectionSettings,
95
+ setupName: string,
96
+ setup: SyncSetupSettings,
97
+ connectionName: string,
98
+ connection: StorageConnectionSettings,
105
99
  ) {
106
- return syncConfigReviewIdentity(
107
- resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false),
108
- );
100
+ return syncConfigReviewIdentity(
101
+ resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false, true),
102
+ );
109
103
  }
110
104
 
111
105
  export function syncConfigReviewIdentity(config: AnySyncConfig) {
112
- return JSON.stringify([
113
- config.setupName,
114
- config.connectionName,
115
- backendIdentityCoordinates(config),
116
- config.include,
117
- config.automatic,
118
- ]);
106
+ return JSON.stringify([
107
+ config.setupName,
108
+ config.connectionName,
109
+ backendIdentityCoordinates(config),
110
+ config.include,
111
+ config.automatic,
112
+ ]);
119
113
  }
120
114
 
121
115
  export function syncConfigReviewFingerprint(config: AnySyncConfig) {
122
- return createHash("sha256").update(syncConfigReviewIdentity(config)).digest("hex");
116
+ return createHash("sha256").update(syncConfigReviewIdentity(config)).digest("hex");
123
117
  }
124
118
 
125
119
  /** Internal freshness token, including credentials; never display or persist it. */
126
120
  export function syncCheckConfigFingerprint(config: AnySyncConfig) {
127
- return createHash("sha256").update(JSON.stringify(config)).digest("hex");
121
+ return createHash("sha256")
122
+ .update(JSON.stringify({ ...config, showStatus: undefined }))
123
+ .digest("hex");
128
124
  }
129
125
 
130
126
  function storageReviewFromConfig(config: AnySyncConfig): SyncSetupStorageReview {
131
- return {
132
- connectionName: config.connectionName,
133
- storageKind: config.backend.type,
134
- storagePath: config.storagePath,
135
- ...(config.backend.type === "s3"
136
- ? { bucket: config.backend.destination.bucket }
137
- : config.backend.type === "git"
138
- ? { branch: config.backend.destination.branch }
139
- : {}),
140
- };
127
+ return {
128
+ connectionName: config.connectionName,
129
+ storageKind: config.backend.type,
130
+ storagePath: config.storagePath,
131
+ ...(config.backend.type === "s3"
132
+ ? { bucket: config.backend.destination.bucket }
133
+ : config.backend.type === "git"
134
+ ? { branch: config.backend.destination.branch }
135
+ : {}),
136
+ };
141
137
  }
142
138
 
143
139
  function resolveSyncConfig(
144
- setupName: string,
145
- setup: SyncSetupSettings,
146
- connectionName: string,
147
- connection: StorageConnectionSettings,
148
- onSwitch: OnSwitchAction,
149
- skipSecretScan: boolean,
140
+ setupName: string,
141
+ setup: SyncSetupSettings,
142
+ connectionName: string,
143
+ connection: StorageConnectionSettings,
144
+ onSwitch: OnSwitchAction,
145
+ skipSecretScan: boolean,
146
+ showStatus: boolean,
150
147
  ): AnySyncConfig {
151
- const storagePath = normalizeStoragePath(setup.storage.path);
152
- const namespace =
153
- storagePath === "./" ? "root" : storagePath.slice(storagePath.lastIndexOf("/") + 1);
154
- const include = normalizeSyncInclude(setup.sync.include);
155
- const common = {
156
- setupName,
157
- connectionName,
158
- storagePath,
159
- snapshotIdentity: namespace,
160
- include,
161
- automatic: setup.sync.automatic,
162
- onSwitch,
163
- skipSecretScan,
164
- };
165
- if (connection.type === "git") {
166
- return {
167
- ...common,
168
- backend: {
169
- type: "git",
170
- profile: { kind: "git", remote: normalizeGitRemote(connection.remote) as string },
171
- destination: {
172
- branch: normalizeGitBranch(setup.storage.branch),
173
- directory: normalizeGitDirectory(storagePath),
174
- namespace,
175
- },
176
- },
177
- };
178
- }
179
- if (connection.type === "webdav") {
180
- return {
181
- ...common,
182
- backend: {
183
- type: "webdav",
184
- profile: {
185
- kind: "webdav",
186
- url: normalizeWebDavUrl(connection.url) as string,
187
- username: connection.credentials.username,
188
- password: connection.credentials.password,
189
- },
190
- destination: { path: normalizeWebDavPath(storagePath), namespace },
191
- },
192
- };
193
- }
194
- return {
195
- ...common,
196
- backend: {
197
- type: "s3",
198
- profile: {
199
- kind: isCloudflareR2Endpoint(connection.endpoint) ? "r2" : "s3-compatible",
200
- endpoint: normalizeS3Endpoint(connection.endpoint),
201
- region: requiredString(connection.region, "S3 region"),
202
- accessKeyId: connection.credentials.accessKeyId,
203
- secretAccessKey: connection.credentials.secretAccessKey,
204
- sessionToken: optionalString(connection.credentials.sessionToken, "S3 session token"),
205
- },
206
- destination: {
207
- bucket: normalizeS3Bucket(setup.storage.bucket),
208
- prefix: storagePath,
209
- namespace,
210
- },
211
- },
212
- };
148
+ const storagePath = normalizeStoragePath(setup.storage.path);
149
+ const namespace = storagePath === "./" ? "root" : storagePath.slice(storagePath.lastIndexOf("/") + 1);
150
+ const include = normalizeSyncInclude(setup.sync.include);
151
+ const common = {
152
+ setupName,
153
+ connectionName,
154
+ storagePath,
155
+ snapshotIdentity: namespace,
156
+ include,
157
+ automatic: setup.sync.automatic,
158
+ onSwitch,
159
+ skipSecretScan,
160
+ showStatus,
161
+ };
162
+ if (connection.type === "git") {
163
+ return {
164
+ ...common,
165
+ backend: {
166
+ type: "git",
167
+ profile: { kind: "git", remote: normalizeGitRemote(connection.remote) as string },
168
+ destination: {
169
+ branch: normalizeGitBranch(setup.storage.branch),
170
+ directory: normalizeGitDirectory(storagePath),
171
+ namespace,
172
+ },
173
+ },
174
+ };
175
+ }
176
+ if (connection.type === "webdav") {
177
+ return {
178
+ ...common,
179
+ backend: {
180
+ type: "webdav",
181
+ profile: {
182
+ kind: "webdav",
183
+ url: normalizeWebDavUrl(connection.url) as string,
184
+ username: connection.credentials.username,
185
+ password: connection.credentials.password,
186
+ },
187
+ destination: { path: normalizeWebDavPath(storagePath), namespace },
188
+ },
189
+ };
190
+ }
191
+ return {
192
+ ...common,
193
+ backend: {
194
+ type: "s3",
195
+ profile: {
196
+ kind: isCloudflareR2Endpoint(connection.endpoint) ? "r2" : "s3-compatible",
197
+ endpoint: normalizeS3Endpoint(connection.endpoint),
198
+ region: requiredString(connection.region, "S3 region"),
199
+ accessKeyId: connection.credentials.accessKeyId,
200
+ secretAccessKey: connection.credentials.secretAccessKey,
201
+ sessionToken: optionalString(connection.credentials.sessionToken, "S3 session token"),
202
+ },
203
+ destination: {
204
+ bucket: normalizeS3Bucket(setup.storage.bucket),
205
+ prefix: storagePath,
206
+ namespace,
207
+ },
208
+ },
209
+ };
213
210
  }
214
211
 
215
212
  export function sessionTokenWarnings(config: { endpoint?: string; sessionToken?: string }) {
216
- if (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];
217
- return [
218
- "session token: configured for Cloudflare R2; if R2 rejects X-Amz-Security-Token, pi-sync retries once without it. R2 static access keys usually do not need a session token.",
219
- ];
213
+ if (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];
214
+ return [
215
+ "session token: configured for Cloudflare R2; if R2 rejects X-Amz-Security-Token, pi-sync retries once without it. R2 static access keys usually do not need a session token.",
216
+ ];
220
217
  }
221
218
 
222
219
  export function syncSessionsWarnings(config: { include: readonly string[] }) {
223
- if (!config.include.includes("sessions")) return [];
224
- return [
225
- "sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust.",
226
- ];
220
+ if (!config.include.includes("sessions")) return [];
221
+ return [
222
+ "sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust.",
223
+ ];
227
224
  }
228
225
 
229
226
  export function isEnabled(value: boolean | string | undefined, defaultValue: boolean) {
230
- if (value === undefined) return defaultValue;
231
- if (typeof value === "boolean") return value;
232
- return !["0", "false", "no", "off"].includes(value.trim().toLowerCase());
227
+ if (value === undefined) return defaultValue;
228
+ if (typeof value === "boolean") return value;
229
+ return !["0", "false", "no", "off"].includes(value.trim().toLowerCase());
233
230
  }
234
231
 
235
232
  export function isExplicitlyEnabled(value: boolean | string | undefined) {
236
- if (typeof value === "boolean") return value;
237
- return ["1", "true", "yes", "on"].includes(value?.trim().toLowerCase() ?? "");
233
+ if (typeof value === "boolean") return value;
234
+ return ["1", "true", "yes", "on"].includes(value?.trim().toLowerCase() ?? "");
238
235
  }