@narumitw/pi-sync 0.49.15 → 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.
Files changed (209) hide show
  1. package/README.md +49 -16
  2. package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
  3. package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
  4. package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
  5. package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
  6. package/dist/chunks/chunk-6UHKGALE.ts +134 -0
  7. package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
  8. package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
  9. package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
  10. package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-AXXS2BPA.ts} +9 -9
  11. package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
  12. package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
  13. package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
  14. package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
  15. package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
  16. package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
  17. package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
  18. package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
  19. package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
  20. package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-GWMHAQG4.ts} +4 -4
  21. package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
  22. package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
  23. package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
  24. package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
  25. package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
  26. package/dist/chunks/chunk-JSYMGJQQ.ts +51 -0
  27. package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
  28. package/dist/chunks/chunk-L7TBO32M.ts +25 -0
  29. package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
  30. package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
  31. package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
  32. package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
  33. package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
  34. package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
  35. package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
  36. package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
  37. package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
  38. package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
  39. package/dist/chunks/chunk-OUHGBFYF.ts.map +7 -0
  40. package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
  41. package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
  42. package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
  43. package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
  44. package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
  45. package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
  46. package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
  47. package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
  48. package/dist/chunks/chunk-THMF4BVL.ts +18 -0
  49. package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
  50. package/dist/chunks/{chunk-44LPGQTI.ts → chunk-X5OM6GXT.ts} +9 -7
  51. package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
  52. package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
  53. package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
  54. package/dist/chunks/{chunk-ARN2672F.ts → chunk-YKJHPTGO.ts} +23 -55
  55. package/dist/chunks/chunk-YKJHPTGO.ts.map +7 -0
  56. package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
  57. package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
  58. package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
  59. package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
  60. package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
  61. package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
  62. package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-OQW46G2X.ts} +42 -17
  63. package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
  64. package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
  65. package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
  66. package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
  67. package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
  68. package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-3P33LQHU.ts} +96 -18
  69. package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
  70. package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
  71. package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
  72. package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
  73. package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
  74. package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
  75. package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-ONJ27QRL.ts} +374 -294
  76. package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
  77. package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
  78. package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
  79. package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
  80. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
  81. package/dist/index.ts +477 -342
  82. package/dist/index.ts.map +4 -4
  83. package/docs/settings.md +30 -6
  84. package/package.json +1 -1
  85. package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
  86. package/src/backends/backend-identity.ts +40 -0
  87. package/src/backends/backend-types.ts +81 -0
  88. package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
  89. package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
  90. package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
  91. package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
  92. package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
  93. package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
  94. package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
  95. package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
  96. package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
  97. package/src/commands/command-execution.ts +330 -0
  98. package/src/commands/command-handler.ts +201 -0
  99. package/src/commands/command-types.ts +18 -0
  100. package/src/{command.ts → commands/command.ts} +2 -1
  101. package/src/commands/setup-completions.ts +6 -0
  102. package/src/settings/config.ts +238 -0
  103. package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
  104. package/src/settings/settings-store.ts +109 -0
  105. package/src/settings/settings-types.ts +128 -0
  106. package/src/settings/settings-validation.ts +443 -0
  107. package/src/snapshot/session-paths.ts +84 -0
  108. package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
  109. package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
  110. package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
  111. package/src/snapshot/snapshot-types.ts +38 -0
  112. package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
  113. package/src/state/json-file.ts +20 -0
  114. package/src/{lock.ts → state/lock.ts} +3 -2
  115. package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
  116. package/src/state/state-types.ts +20 -0
  117. package/src/state/sync-state-store.ts +62 -0
  118. package/src/sync/automatic-sync.ts +103 -0
  119. package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
  120. package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
  121. package/src/sync/signals.ts +10 -0
  122. package/src/sync/startup-check.ts +128 -0
  123. package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
  124. package/src/sync/sync-error-guidance.ts +53 -0
  125. package/src/sync/sync-inspection.ts +51 -0
  126. package/src/sync/sync-loaders.ts +54 -0
  127. package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
  128. package/src/sync/sync-operations.ts +12 -0
  129. package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
  130. package/src/sync/sync-queries.ts +235 -0
  131. package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
  132. package/src/sync-extension.ts +64 -708
  133. package/src/sync.ts +8 -8
  134. package/src/types.ts +47 -286
  135. package/src/ui/automatic-sync-summary.ts +6 -0
  136. package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
  137. package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
  138. package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
  139. package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
  140. package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
  141. package/src/ui/manager-ui.ts +386 -0
  142. package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
  143. package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
  144. package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
  145. package/src/ui/setup/git-ui.ts +293 -0
  146. package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
  147. package/src/ui/setup/s3-ui.ts +217 -0
  148. package/src/ui/setup/setup-actions.ts +127 -0
  149. package/src/ui/setup/setup-location-ui.ts +167 -0
  150. package/src/ui/setup/setup-name-ui.ts +34 -0
  151. package/src/ui/setup/setup-prompts.ts +92 -0
  152. package/src/ui/setup/setup-review.ts +70 -0
  153. package/src/ui/setup/setup-switcher.ts +181 -0
  154. package/src/ui/setup/setup-wizard.ts +39 -0
  155. package/src/ui/setup/text-input.ts +78 -0
  156. package/src/ui/setup/webdav-ui.ts +330 -0
  157. package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
  158. package/src/{sync-attention.ts → ui/sync-attention.ts} +75 -6
  159. package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
  160. package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
  161. package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
  162. package/src/ui/terminal-text.ts +4 -0
  163. package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
  164. package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
  165. package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
  166. package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
  167. package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
  168. package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
  169. package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
  170. package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
  171. package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
  172. package/dist/chunks/chunk-E4COSV32.ts +0 -82
  173. package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
  174. package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
  175. package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
  176. package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
  177. package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
  178. package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
  179. package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
  180. package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
  181. package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
  182. package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
  183. package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
  184. package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
  185. package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
  186. package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
  187. package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
  188. package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
  189. package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
  190. package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
  191. package/src/config.ts +0 -923
  192. package/src/git-ui.ts +0 -307
  193. package/src/manager-helpers.ts +0 -64
  194. package/src/manager-ui.ts +0 -997
  195. package/src/webdav-ui.ts +0 -421
  196. /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
  197. /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
  198. /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
  199. /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
  200. /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-FEP4L5TR.ts.map} +0 -0
  201. /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
  202. /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
  203. /package/src/{config-file.ts → settings/config-file.ts} +0 -0
  204. /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
  205. /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
  206. /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
  207. /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
  208. /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
  209. /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/settings/config-file.ts", "../../src/settings/settings-validation.ts", "../../src/settings/settings-store.ts", "../../src/settings/config.ts"],
4
+ "sourcesContent": ["import { randomUUID } from \"node:crypto\";\nimport {\n\tmkdir,\n\tmkdirSync,\n\trealpath,\n\trealpathSync,\n\trmdir,\n\trmdirSync,\n\tstat,\n\tstatSync,\n\tutimes,\n\tutimesSync,\n} from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport lockfile from \"proper-lockfile\";\n\nconst CONFIG_FILE_NAME = \"pi-sync.json\";\nconst LEGACY_CONFIG_FILE_NAME = \"pi-sync.local.json\";\nconst configMigrationNotices = new Map<string, string>();\nconst legacyPresenceNoticed = new Set<string>();\nconst CONFIG_LOCK_STALE_MS = 30_000;\nconst CONFIG_LOCK_UPDATE_MS = 10_000;\nconst LOCKFILE_FS_ADAPTER = {\n\tmkdir,\n\tmkdirSync,\n\trealpath,\n\trealpathSync,\n\trmdir,\n\trmdirSync,\n\tstat,\n\tstatSync,\n\tutimes,\n\tutimesSync,\n};\n\ntype PublishFile = (source: string, destination: string) => Promise<void>;\nlet publishConfigFile: PublishFile = publishFileWithoutReplacement;\nlet beforeConfigPublicationHook: () => Promise<void> = async () => undefined;\nlet afterMissingConfigReadProbeHook: () => Promise<void> = async () => undefined;\nlet afterReplacementInstalledHook: () => Promise<void> = async () => undefined;\nlet afterConfigQuarantinedHook: () => Promise<void> = async () => undefined;\n\nexport type FileIdentity = { dev: number; ino: number };\n\nexport async function withConfigFilePublicationForTest<T>(\n\tpublish: PublishFile,\n\trun: () => Promise<T>,\n): Promise<T> {\n\tconst previous = publishConfigFile;\n\tpublishConfigFile = publish;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tpublishConfigFile = previous;\n\t}\n}\n\nexport async function withConfigPublicationReadyHookForTest<T>(\n\thook: () => Promise<void>,\n\trun: () => Promise<T>,\n): Promise<T> {\n\tconst previous = beforeConfigPublicationHook;\n\tbeforeConfigPublicationHook = hook;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tbeforeConfigPublicationHook = previous;\n\t}\n}\n\nexport async function withMissingConfigReadProbeHookForTest<T>(\n\thook: () => Promise<void>,\n\trun: () => Promise<T>,\n): Promise<T> {\n\tconst previous = afterMissingConfigReadProbeHook;\n\tafterMissingConfigReadProbeHook = hook;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tafterMissingConfigReadProbeHook = previous;\n\t}\n}\n\nexport async function withConfigReplacementInstalledHookForTest<T>(\n\thook: () => Promise<void>,\n\trun: () => Promise<T>,\n): Promise<T> {\n\tconst previous = afterReplacementInstalledHook;\n\tafterReplacementInstalledHook = hook;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tafterReplacementInstalledHook = previous;\n\t}\n}\n\nexport async function withConfigQuarantinedHookForTest<T>(\n\thook: () => Promise<void>,\n\trun: () => Promise<T>,\n): Promise<T> {\n\tconst previous = afterConfigQuarantinedHook;\n\tafterConfigQuarantinedHook = hook;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tafterConfigQuarantinedHook = previous;\n\t}\n}\n\ntype ConfigSnapshot = {\n\tbytes: Buffer;\n\tidentity: FileIdentity;\n\tparsed: Record<string, unknown>;\n};\n\nexport type LocalConfigDocument = ConfigSnapshot & { path: string };\n\nexport function localConfigPath() {\n\treturn path.join(getAgentDir(), CONFIG_FILE_NAME);\n}\n\nexport function legacyLocalConfigPath() {\n\treturn path.join(getAgentDir(), LEGACY_CONFIG_FILE_NAME);\n}\n\nexport async function activeLocalConfigPath() {\n\tconst canonicalPath = localConfigPath();\n\tconst legacyPath = legacyLocalConfigPath();\n\treturn withLocalConfigReadLockIfNeeded(async () => {\n\t\tif (await pathExists(canonicalPath)) return canonicalPath;\n\t\treturn (await pathExists(legacyPath)) ? legacyPath : canonicalPath;\n\t});\n}\n\nexport function consumeLocalConfigMigrationNotice() {\n\tconst configPath = localConfigPath();\n\tconst notice = configMigrationNotices.get(configPath);\n\tconfigMigrationNotices.delete(configPath);\n\treturn notice;\n}\n\nexport async function readActiveLocalConfigDocumentForRepair(): Promise<\n\tLocalConfigDocument | undefined\n> {\n\tconst canonicalPath = localConfigPath();\n\tconst legacyPath = legacyLocalConfigPath();\n\treturn withLocalConfigReadLockIfNeeded(async () => {\n\t\tconst filePath = (await pathExists(canonicalPath)) ? canonicalPath : legacyPath;\n\t\tconst snapshot = await readConfigSnapshotIfExists(filePath);\n\t\treturn snapshot ? { path: filePath, ...snapshot } : undefined;\n\t});\n}\n\nexport async function readMigratingLocalConfigDocument(\n\tvalidateForMigration: (settings: Record<string, unknown>) => void,\n): Promise<LocalConfigDocument | undefined> {\n\treturn withLocalConfigReadLockIfNeeded(async () => {\n\t\tconst configPath = await prepareLocalConfigPath(validateForMigration);\n\t\tconst snapshot = await readConfigSnapshotIfExists(configPath);\n\t\treturn snapshot ? { path: configPath, ...snapshot } : undefined;\n\t});\n}\n\nasync function withLocalConfigReadLockIfNeeded<T>(read: () => Promise<T>): Promise<T> {\n\tif ((await pathExists(localConfigPath())) || (await pathExists(legacyLocalConfigPath()))) {\n\t\treturn withLocalConfigFileLock(read);\n\t}\n\tawait afterMissingConfigReadProbeHook();\n\tif (await pathExists(configMutationLockPath())) return withLocalConfigFileLock(read);\n\treturn read();\n}\n\nexport function updateLocalConfigDocument<T extends Record<string, unknown>>(\n\tdefaultValue: T,\n\tupdate: (current: T) => T,\n\tvalidate: (value: Record<string, unknown>) => void,\n\tsignal?: AbortSignal,\n): Promise<T> {\n\treturn withLocalConfigFileLock(async () => {\n\t\tsignal?.throwIfAborted();\n\t\tconst configPath = await prepareLocalConfigPath(validate);\n\t\tconst snapshot = await readConfigSnapshotIfExists(configPath);\n\t\tconst document = snapshot ? { path: configPath, ...snapshot } : undefined;\n\t\tconst current = document\n\t\t\t? (structuredClone(document.parsed) as T)\n\t\t\t: structuredClone(defaultValue);\n\t\tconst next = update(current);\n\t\tvalidate(next);\n\t\tsignal?.throwIfAborted();\n\t\tif (document && JSON.stringify(document.parsed) === JSON.stringify(next)) return next;\n\t\tif (document) await replaceLocalConfigDocumentUnlocked(document, next);\n\t\telse await installPrivateConfigExclusively(localConfigPath(), serializedConfig(next));\n\t\treturn next;\n\t});\n}\n\nexport function createLocalConfigDocument(value: Record<string, unknown>) {\n\treturn withLocalConfigFileLock(async () => {\n\t\tconst bytes = serializedConfig(value);\n\t\ttry {\n\t\t\tawait installPrivateConfigExclusively(localConfigPath(), bytes);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\t\tthrow new Error(\"Pi-sync settings were created concurrently; reopen settings and retry.\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t});\n}\n\nexport function replaceLocalConfigDocument(\n\tdocument: LocalConfigDocument,\n\tvalue: Record<string, unknown>,\n) {\n\treturn withLocalConfigFileLock(() => replaceLocalConfigDocumentUnlocked(document, value));\n}\n\nasync function replaceLocalConfigDocumentUnlocked(\n\tdocument: LocalConfigDocument,\n\tvalue: Record<string, unknown>,\n) {\n\tconst nextBytes = serializedConfig(value);\n\tconst canonicalPath = localConfigPath();\n\tif (document.path !== canonicalPath) {\n\t\tif (!(await configDocumentStillMatches(document))) throw settingsChangedError();\n\t\tlet installed: FileIdentity;\n\t\ttry {\n\t\t\tinstalled = await installPrivateConfigExclusively(canonicalPath, nextBytes);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\t\tthrow new Error(\"Canonical settings were created concurrently; no settings were replaced.\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (!(await configDocumentStillMatches(document))) {\n\t\t\tawait quarantineAndRemoveConfigIfMatchesUnlocked(canonicalPath, installed, nextBytes);\n\t\t\tthrow settingsChangedError();\n\t\t}\n\t\treturn;\n\t}\n\n\tconst quarantinePath = await claimCanonicalConfigDocument(document);\n\tlet installed: FileIdentity;\n\ttry {\n\t\tinstalled = await installPrivateConfigExclusively(canonicalPath, nextBytes);\n\t} catch (error) {\n\t\tawait restoreQuarantinedConfig(canonicalPath, quarantinePath);\n\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\tthrow new Error(\"Canonical settings changed concurrently; no settings were replaced.\");\n\t\t}\n\t\tthrow error;\n\t}\n\ttry {\n\t\tawait afterReplacementInstalledHook();\n\t\tif (!(await fileIdentityAndContentsMatch(quarantinePath, document.identity, document.bytes))) {\n\t\t\tthrow settingsChangedError();\n\t\t}\n\t\tif (process.platform !== \"win32\") await fs.chmod(quarantinePath, 0o600);\n\t} catch (error) {\n\t\tawait quarantineAndRemoveConfigIfMatchesUnlocked(canonicalPath, installed, nextBytes);\n\t\tawait restoreQuarantinedConfig(canonicalPath, quarantinePath);\n\t\tthrow error;\n\t}\n\tawait fs.rm(quarantinePath).catch(() => undefined);\n\tawait syncParentDirectory(canonicalPath).catch(() => undefined);\n}\n\nasync function prepareLocalConfigPath(\n\tvalidateForMigration: (settings: Record<string, unknown>) => void,\n) {\n\tconst canonicalPath = localConfigPath();\n\tconst legacyPath = legacyLocalConfigPath();\n\tif (await pathExists(canonicalPath)) {\n\t\tconst legacyStatus = await secureIgnoredLegacyIfPresent(legacyPath);\n\t\tif (legacyStatus !== \"missing\" && !legacyPresenceNoticed.has(canonicalPath)) {\n\t\t\tlegacyPresenceNoticed.add(canonicalPath);\n\t\t\trecordConfigMigrationNotice(\n\t\t\t\tcanonicalPath,\n\t\t\t\tlegacyStatus === \"private\"\n\t\t\t\t\t? `${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} takes precedence. Delete ${LEGACY_CONFIG_FILE_NAME} after confirming your settings.`\n\t\t\t\t\t: `${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} takes precedence, but pi-sync could not verify them as a private regular file. Secure or delete the legacy path after confirming your settings.`,\n\t\t\t);\n\t\t}\n\t\treturn canonicalPath;\n\t}\n\n\tconst legacy = await readConfigSnapshotIfExists(legacyPath);\n\tif (!legacy) return canonicalPath;\n\tvalidateForMigration(legacy.parsed);\n\n\tlet installedIdentity: FileIdentity;\n\ttry {\n\t\tinstalledIdentity = await installPrivateConfigExclusively(canonicalPath, legacy.bytes);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\trecordConfigMigrationNotice(\n\t\t\t\tcanonicalPath,\n\t\t\t\t`${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} was created concurrently and takes precedence.`,\n\t\t\t);\n\t\t\treturn canonicalPath;\n\t\t}\n\t\trecordConfigMigrationNotice(\n\t\t\tcanonicalPath,\n\t\t\t`Could not migrate ${LEGACY_CONFIG_FILE_NAME} to ${CONFIG_FILE_NAME}; the legacy settings were used for this session and were not changed.`,\n\t\t);\n\t\treturn legacyPath;\n\t}\n\n\tif (!(await configSnapshotStillMatches(legacyPath, legacy))) {\n\t\tconst removed = await quarantineAndRemoveConfigIfMatchesUnlocked(\n\t\t\tcanonicalPath,\n\t\t\tinstalledIdentity,\n\t\t\tlegacy.bytes,\n\t\t);\n\t\trecordConfigMigrationNotice(\n\t\t\tcanonicalPath,\n\t\t\tremoved\n\t\t\t\t? `${LEGACY_CONFIG_FILE_NAME} changed during migration; the stale ${CONFIG_FILE_NAME} copy was removed and the legacy settings were used for this session.`\n\t\t\t\t: `${LEGACY_CONFIG_FILE_NAME} changed during migration, but ${CONFIG_FILE_NAME} was replaced concurrently and takes precedence.`,\n\t\t);\n\t\treturn removed ? legacyPath : canonicalPath;\n\t}\n\n\tlegacyPresenceNoticed.add(canonicalPath);\n\trecordConfigMigrationNotice(\n\t\tcanonicalPath,\n\t\t`pi-sync settings migrated from ${LEGACY_CONFIG_FILE_NAME} to ${CONFIG_FILE_NAME}; the private legacy file was retained as a recovery copy and can be deleted after verification.`,\n\t);\n\treturn canonicalPath;\n}\n\nasync function secureIgnoredLegacyIfPresent(filePath: string) {\n\tlet pathStat: Awaited<ReturnType<typeof fs.lstat>>;\n\ttry {\n\t\tpathStat = await fs.lstat(filePath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"missing\" as const;\n\t\treturn \"unsafe\" as const;\n\t}\n\tif (pathStat.isSymbolicLink() || !pathStat.isFile()) return \"unsafe\" as const;\n\n\tlet handle: fs.FileHandle | undefined;\n\ttry {\n\t\thandle = await fs.open(filePath, \"r\");\n\t\tconst openedStat = await handle.stat();\n\t\tif (openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) {\n\t\t\treturn \"unsafe\" as const;\n\t\t}\n\t\tif (process.platform !== \"win32\" && (openedStat.mode & 0o777) !== 0o600) {\n\t\t\tawait handle.chmod(0o600);\n\t\t}\n\t\treturn \"private\" as const;\n\t} catch {\n\t\treturn \"unsafe\" as const;\n\t} finally {\n\t\tawait handle?.close().catch(() => undefined);\n\t}\n}\n\nasync function readConfigSnapshotIfExists(filePath: string): Promise<ConfigSnapshot | undefined> {\n\tlet pathStat: Awaited<ReturnType<typeof fs.lstat>>;\n\ttry {\n\t\tpathStat = await fs.lstat(filePath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n\tif (pathStat.isSymbolicLink()) {\n\t\tthrow new Error(`Refusing to read symlinked pi-sync config: ${filePath}`);\n\t}\n\tif (!pathStat.isFile()) throw new Error(`pi-sync config is not a regular file: ${filePath}`);\n\n\tconst handle = await fs.open(filePath, \"r\");\n\ttry {\n\t\tconst openedStat = await handle.stat();\n\t\tif (openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) {\n\t\t\tthrow new Error(`pi-sync config changed while opening: ${filePath}`);\n\t\t}\n\t\tif (process.platform !== \"win32\" && (openedStat.mode & 0o777) !== 0o600) {\n\t\t\tawait handle.chmod(0o600);\n\t\t}\n\t\tconst bytes = await handle.readFile();\n\t\treturn {\n\t\t\tbytes,\n\t\t\tidentity: { dev: openedStat.dev, ino: openedStat.ino },\n\t\t\tparsed: parseConfigObject(bytes, filePath),\n\t\t};\n\t} finally {\n\t\tawait handle.close();\n\t}\n}\n\nfunction serializedConfig(value: Record<string, unknown>) {\n\treturn Buffer.from(`${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n}\n\nfunction parseConfigObject(bytes: Buffer, filePath: string) {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(bytes.toString(\"utf8\"));\n\t} catch {\n\t\tthrow new SyntaxError(`Invalid JSON in pi-sync config: ${filePath}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`pi-sync config must contain a JSON object: ${filePath}`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nasync function installPrivateConfigExclusively(filePath: string, bytes: Buffer) {\n\tawait fs.mkdir(path.dirname(filePath), { recursive: true });\n\tconst temporaryPath = path.join(\n\t\tpath.dirname(filePath),\n\t\t`.${path.basename(filePath)}.${process.pid}.${randomUUID()}.migrate`,\n\t);\n\tlet handle: fs.FileHandle | undefined;\n\ttry {\n\t\thandle = await fs.open(temporaryPath, \"wx\", 0o600);\n\t\tawait handle.writeFile(bytes);\n\t\tif (process.platform !== \"win32\") await handle.chmod(0o600);\n\t\tawait handle.sync();\n\t\tawait handle.close();\n\t\thandle = undefined;\n\t\tawait publishConfigFile(temporaryPath, filePath);\n\t\tconst installed = await fs.lstat(filePath);\n\t\tlet publishedHandle: fs.FileHandle | undefined;\n\t\tlet publicationError: unknown;\n\t\ttry {\n\t\t\tif (installed.isSymbolicLink() || !installed.isFile()) {\n\t\t\t\tthrow new Error(`Published pi-sync settings are not a regular file: ${filePath}`);\n\t\t\t}\n\t\t\tpublishedHandle = await fs.open(filePath, \"r+\");\n\t\t\tconst published = await publishedHandle.stat();\n\t\t\tif (published.dev !== installed.dev || published.ino !== installed.ino) {\n\t\t\t\tthrow new Error(`Published pi-sync settings changed while opening: ${filePath}`);\n\t\t\t}\n\t\t\tif (process.platform !== \"win32\") await publishedHandle.chmod(0o600);\n\t\t\tawait publishedHandle.sync();\n\t\t\tawait syncParentDirectory(filePath);\n\t\t} catch (error) {\n\t\t\tpublicationError = error;\n\t\t} finally {\n\t\t\tawait publishedHandle?.close().catch(() => undefined);\n\t\t}\n\t\tif (publicationError) {\n\t\t\tawait quarantineAndRemoveConfigIfMatchesUnlocked(\n\t\t\t\tfilePath,\n\t\t\t\t{ dev: installed.dev, ino: installed.ino },\n\t\t\t\tbytes,\n\t\t\t);\n\t\t\tthrow publicationError;\n\t\t}\n\t\treturn { dev: installed.dev, ino: installed.ino };\n\t} finally {\n\t\tawait handle?.close().catch(() => undefined);\n\t\tawait fs.rm(temporaryPath, { force: true }).catch(() => undefined);\n\t}\n}\n\nasync function configDocumentStillMatches(document: LocalConfigDocument) {\n\treturn fileIdentityAndContentsMatch(document.path, document.identity, document.bytes);\n}\n\nasync function claimCanonicalConfigDocument(document: LocalConfigDocument) {\n\tconst quarantinePath = path.join(\n\t\tpath.dirname(document.path),\n\t\t`.${path.basename(document.path)}.${randomUUID()}.schema-migration-source`,\n\t);\n\ttry {\n\t\tawait fs.rename(document.path, quarantinePath);\n\t\tawait syncParentDirectory(document.path);\n\t} catch (error) {\n\t\tawait restoreQuarantinedConfig(document.path, quarantinePath);\n\t\tthrow error;\n\t}\n\tif (!(await fileIdentityAndContentsMatch(quarantinePath, document.identity, document.bytes))) {\n\t\tawait restoreQuarantinedConfig(document.path, quarantinePath);\n\t\tthrow settingsChangedError();\n\t}\n\tif (await pathExists(document.path)) {\n\t\tawait restoreQuarantinedConfig(document.path, quarantinePath);\n\t\tthrow new Error(\"Canonical settings changed concurrently; no settings were replaced.\");\n\t}\n\treturn quarantinePath;\n}\n\nfunction settingsChangedError() {\n\treturn new Error(\"pi-sync settings changed during migration; no settings were replaced.\");\n}\n\nasync function configSnapshotStillMatches(filePath: string, snapshot: ConfigSnapshot) {\n\treturn fileIdentityAndContentsMatch(filePath, snapshot.identity, snapshot.bytes);\n}\n\nexport function quarantineAndRemoveConfigIfMatches(\n\tfilePath: string,\n\tidentity: FileIdentity,\n\texpectedBytes: Buffer,\n) {\n\treturn withLocalConfigFileLock(() =>\n\t\tquarantineAndRemoveConfigIfMatchesUnlocked(filePath, identity, expectedBytes),\n\t);\n}\n\nasync function quarantineAndRemoveConfigIfMatchesUnlocked(\n\tfilePath: string,\n\tidentity: FileIdentity,\n\texpectedBytes: Buffer,\n) {\n\tconst quarantinePath = path.join(\n\t\tpath.dirname(filePath),\n\t\t`.${path.basename(filePath)}.${randomUUID()}.migration-retired`,\n\t);\n\ttry {\n\t\tawait fs.rename(filePath, quarantinePath);\n\t} catch {\n\t\treturn false;\n\t}\n\tawait afterConfigQuarantinedHook();\n\ttry {\n\t\tawait syncParentDirectory(filePath);\n\t} catch {\n\t\tawait restoreQuarantinedConfig(filePath, quarantinePath);\n\t\treturn false;\n\t}\n\n\tconst matches = await fileIdentityAndContentsMatch(quarantinePath, identity, expectedBytes);\n\tif (!matches) {\n\t\tawait restoreQuarantinedConfig(filePath, quarantinePath);\n\t\treturn false;\n\t}\n\tif (await pathExists(filePath)) {\n\t\tawait fs.rm(quarantinePath, { force: true });\n\t\tawait syncParentDirectory(filePath);\n\t\treturn false;\n\t}\n\tawait fs.rm(quarantinePath);\n\tawait syncParentDirectory(filePath);\n\treturn true;\n}\n\nasync function fileIdentityAndContentsMatch(\n\tfilePath: string,\n\tidentity: FileIdentity,\n\texpectedBytes: Buffer,\n) {\n\ttry {\n\t\tconst current = await fs.lstat(filePath);\n\t\tif (current.isSymbolicLink()) return false;\n\t\tif (current.dev !== identity.dev || current.ino !== identity.ino) return false;\n\t\treturn (await fs.readFile(filePath)).equals(expectedBytes);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function restoreQuarantinedConfig(filePath: string, quarantinePath: string) {\n\ttry {\n\t\tawait renameFileWithoutReplacement(quarantinePath, filePath);\n\t\tif (process.platform !== \"win32\") await fs.chmod(filePath, 0o600);\n\t\tawait syncParentDirectory(filePath);\n\t\treturn;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") return;\n\t}\n\ttry {\n\t\tif (process.platform !== \"win32\") await fs.chmod(quarantinePath, 0o600);\n\t\tawait syncParentDirectory(filePath);\n\t} catch {\n\t\t// Preserve the quarantine rather than risk deleting settings that changed concurrently.\n\t}\n}\n\nfunction configMutationLockPath() {\n\treturn `${localConfigPath()}.mutation-lock`;\n}\n\nexport async function withLocalConfigFileLock<T>(run: () => Promise<T>): Promise<T> {\n\tconst configPath = localConfigPath();\n\tawait fs.mkdir(path.dirname(configPath), { recursive: true });\n\tlet compromisedError: Error | undefined;\n\tconst release = await lockfile.lock(configPath, {\n\t\tfs: LOCKFILE_FS_ADAPTER,\n\t\tlockfilePath: configMutationLockPath(),\n\t\trealpath: false,\n\t\tstale: CONFIG_LOCK_STALE_MS,\n\t\tupdate: CONFIG_LOCK_UPDATE_MS,\n\t\tretries: { retries: 100, factor: 1.2, minTimeout: 10, maxTimeout: 100 },\n\t\tonCompromised: (error) => {\n\t\t\tcompromisedError = error;\n\t\t},\n\t});\n\ttry {\n\t\tconst result = await run();\n\t\tif (compromisedError) throw compromisedError;\n\t\treturn result;\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function syncParentDirectory(filePath: string) {\n\tif (process.platform === \"win32\") return;\n\tlet handle: fs.FileHandle | undefined;\n\ttry {\n\t\thandle = await fs.open(path.dirname(filePath), \"r\");\n\t\tawait handle.sync();\n\t} finally {\n\t\tawait handle?.close().catch(() => undefined);\n\t}\n}\n\nasync function publishFileWithoutReplacement(source: string, destination: string) {\n\tawait beforeConfigPublicationHook();\n\tawait renameFileWithoutReplacement(source, destination);\n}\n\nasync function renameFileWithoutReplacement(source: string, destination: string) {\n\tif (await pathExists(destination)) {\n\t\tthrow Object.assign(new Error(`Settings already exist: ${destination}`), { code: \"EEXIST\" });\n\t}\n\tawait fs.rename(source, destination);\n}\n\nasync function pathExists(filePath: string) {\n\ttry {\n\t\tawait fs.lstat(filePath);\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n}\n\nfunction recordConfigMigrationNotice(configPath: string, notice: string) {\n\tif (!configMigrationNotices.has(configPath)) configMigrationNotices.set(configPath, notice);\n}\n", "import path from \"node:path\";\nimport { normalizeEndpointIdentity } from \"../backends/backend-identity.js\";\nimport {\n\tnormalizeGitBranch,\n\tnormalizeGitDirectory,\n\tnormalizeGitRemote,\n\tnormalizeGitRemoteIdentity,\n} from \"../backends/git/git-config.js\";\nimport {\n\tnormalizeWebDavIdentityUrl,\n\tnormalizeWebDavPath,\n\tnormalizeWebDavUrl,\n\tvalidateWebDavCredentials,\n} from \"../backends/webdav/webdav-config.js\";\nimport { normalizeSyncInclude } from \"../sync/sync-policy.js\";\nimport { localConfigPath } from \"./config-file.js\";\nimport type {\n\tOnSwitchAction,\n\tPiSyncSettingsV3,\n\tStorageConnectionSettings,\n\tSyncSetupSettings,\n} from \"./settings-types.js\";\n\nexport const DEFAULT_ON_SWITCH: OnSwitchAction = \"ask-before-pull\";\n\nexport function normalizeOnSwitch(value: unknown): OnSwitchAction {\n\tif (value === \"ask-before-pull\" || value === \"pull-after-switch\" || value === \"switch-only\") {\n\t\treturn value;\n\t}\n\tthrow new Error(\n\t\t'Invalid pi-sync settings: onSwitch must be \"ask-before-pull\", \"pull-after-switch\", or \"switch-only\".',\n\t);\n}\n\nexport function validateSettingsDocument(value: Record<string, unknown>): PiSyncSettingsV3 {\n\tif (value.version !== 3) {\n\t\tthrow new Error(\n\t\t\t`Unsupported pi-sync settings: version 3 is required. Keep the existing file for recovery, then create a new version 3 ${path.basename(localConfigPath())}; pi-sync will not migrate or overwrite old settings.`,\n\t\t);\n\t}\n\trejectLegacyFields(\n\t\tvalue,\n\t\t[\n\t\t\t\"profiles\",\n\t\t\t\"targets\",\n\t\t\t\"activeTarget\",\n\t\t\t\"targetSwitchAction\",\n\t\t\t\"endpoint\",\n\t\t\t\"bucket\",\n\t\t\t\"region\",\n\t\t\t\"accessKeyId\",\n\t\t\t\"secretAccessKey\",\n\t\t\t\"sessionToken\",\n\t\t\t\"profile\",\n\t\t\t\"prefix\",\n\t\t\t\"autoSync\",\n\t\t\t\"syncFiles\",\n\t\t\t\"syncSessions\",\n\t\t\t\"extraFiles\",\n\t\t],\n\t\t\"top level\",\n\t);\n\tnormalizeOnSwitch(value.onSwitch);\n\tif (value.skipSecretScan !== undefined && typeof value.skipSecretScan !== \"boolean\") {\n\t\tthrow new Error(\"Invalid pi-sync settings: skipSecretScan must be boolean.\");\n\t}\n\tconst storageConnections = requireNamedObjectMap(\n\t\tvalue.storageConnections,\n\t\t\"storageConnections\",\n\t\t\"storage connection\",\n\t);\n\tconst syncSetups = requireNamedObjectMap(value.syncSetups, \"syncSetups\", \"sync setup\");\n\tfor (const name of Object.keys(storageConnections)) {\n\t\tvalidateStorageConnection(\n\t\t\tname,\n\t\t\trequireOwnObject(storageConnections, name, \"storage connection\") as Record<string, unknown>,\n\t\t);\n\t}\n\tfor (const name of Object.keys(syncSetups)) {\n\t\tvalidateSyncSetup(\n\t\t\tname,\n\t\t\trequireOwnObject(syncSetups, name, \"sync setup\") as Record<string, unknown>,\n\t\t\tstorageConnections,\n\t\t);\n\t}\n\tconst names = Object.keys(syncSetups);\n\tconst activeSyncSetup = optionalCanonicalReference(value.activeSyncSetup, \"activeSyncSetup\");\n\tif (names.length === 0) {\n\t\tif (activeSyncSetup !== undefined) {\n\t\t\tthrow new Error(\"Invalid pi-sync settings: empty syncSetups cannot have activeSyncSetup.\");\n\t\t}\n\t} else if (!activeSyncSetup || !Object.hasOwn(syncSetups, activeSyncSetup)) {\n\t\tthrow new Error(\n\t\t\t\"Invalid pi-sync settings: activeSyncSetup must reference an existing own-property sync setup.\",\n\t\t);\n\t}\n\tvalidateUniqueRemoteSyncSetups(syncSetups, storageConnections);\n\treturn value as PiSyncSettingsV3;\n}\n\nfunction validateStorageConnection(name: string, value: Record<string, unknown>) {\n\trejectLegacyFields(\n\t\tvalue,\n\t\t[\"kind\", \"accessKeyId\", \"secretAccessKey\", \"sessionToken\", \"username\", \"password\"],\n\t\t`storage connection \u201C${name}\u201D`,\n\t);\n\tconst type = requiredString(value.type, `storage connection \u201C${name}\u201D type`);\n\tif (type !== \"s3\" && type !== \"git\" && type !== \"webdav\") {\n\t\tthrow new Error(`Invalid pi-sync settings: storage connection \u201C${name}\u201D has unsupported type.`);\n\t}\n\tconst known = [\"endpoint\", \"region\", \"remote\", \"url\", \"credentials\"];\n\tconst allowed =\n\t\ttype === \"s3\"\n\t\t\t? new Set([\"endpoint\", \"region\", \"credentials\"])\n\t\t\t: type === \"git\"\n\t\t\t\t? new Set([\"remote\"])\n\t\t\t\t: new Set([\"url\", \"credentials\"]);\n\tif (known.some((field) => Object.hasOwn(value, field) && !allowed.has(field))) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: ${type.toUpperCase()} storage connection \u201C${name}\u201D mixes backend fields.`,\n\t\t);\n\t}\n\tif (type === \"git\") {\n\t\tif (!normalizeGitRemote(requiredString(value.remote, `Git remote for \u201C${name}\u201D`))) {\n\t\t\tthrow new Error(`Invalid pi-sync settings: Git remote for \u201C${name}\u201D is required.`);\n\t\t}\n\t\treturn;\n\t}\n\tconst credentials = requireRecord(\n\t\tvalue.credentials,\n\t\t`credentials for storage connection \u201C${name}\u201D`,\n\t);\n\tif (type === \"webdav\") {\n\t\tnormalizeWebDavUrl(requiredString(value.url, `WebDAV URL for \u201C${name}\u201D`));\n\t\tconst username = requiredString(credentials.username, `WebDAV username for \u201C${name}\u201D`);\n\t\tconst password = requiredSecret(credentials.password, `WebDAV password for \u201C${name}\u201D`);\n\t\tif (\n\t\t\t[\"accessKeyId\", \"secretAccessKey\", \"sessionToken\"].some((field) =>\n\t\t\t\tObject.hasOwn(credentials, field),\n\t\t\t)\n\t\t) {\n\t\t\tthrow new Error(`Invalid pi-sync settings: WebDAV credentials for \u201C${name}\u201D mix fields.`);\n\t\t}\n\t\tvalidateWebDavCredentials(username, password);\n\t\treturn;\n\t}\n\tnormalizeS3Endpoint(requiredString(value.endpoint, `S3 endpoint for \u201C${name}\u201D`));\n\trequiredString(value.region, `S3 region for \u201C${name}\u201D`);\n\trequiredString(credentials.accessKeyId, `S3 access key id for \u201C${name}\u201D`);\n\trequiredSecret(credentials.secretAccessKey, `S3 secret access key for \u201C${name}\u201D`);\n\toptionalString(credentials.sessionToken, `S3 session token for \u201C${name}\u201D`);\n\tif ([\"username\", \"password\"].some((field) => Object.hasOwn(credentials, field))) {\n\t\tthrow new Error(`Invalid pi-sync settings: S3 credentials for \u201C${name}\u201D mix fields.`);\n\t}\n}\n\nfunction validateSyncSetup(\n\tname: string,\n\tvalue: Record<string, unknown>,\n\tconnections: Record<string, unknown>,\n) {\n\trejectLegacyFields(\n\t\tvalue,\n\t\t[\n\t\t\t\"profile\",\n\t\t\t\"bucket\",\n\t\t\t\"branch\",\n\t\t\t\"path\",\n\t\t\t\"prefix\",\n\t\t\t\"directory\",\n\t\t\t\"namespace\",\n\t\t\t\"autoSync\",\n\t\t\t\"syncFiles\",\n\t\t\t\"syncSessions\",\n\t\t\t\"extraFiles\",\n\t\t],\n\t\t`sync setup \u201C${name}\u201D`,\n\t);\n\tconst storage = requireRecord(value.storage, `storage for sync setup \u201C${name}\u201D`);\n\tconst sync = requireRecord(value.sync, `sync policy for sync setup \u201C${name}\u201D`);\n\trejectLegacyFields(\n\t\tstorage,\n\t\t[\"profile\", \"prefix\", \"directory\", \"namespace\"],\n\t\t`storage for sync setup \u201C${name}\u201D`,\n\t);\n\trejectLegacyFields(\n\t\tsync,\n\t\t[\"autoSync\", \"syncFiles\", \"syncSessions\", \"extraFiles\"],\n\t\t`sync policy for sync setup \u201C${name}\u201D`,\n\t);\n\tconst connectionName = requiredCanonicalReference(\n\t\tstorage.connection,\n\t\t`storage connection reference for sync setup \u201C${name}\u201D`,\n\t);\n\tvalidateConfigName(connectionName, \"storage connection reference\");\n\tconst connection = ownObject<Record<string, unknown>>(connections, connectionName);\n\tif (!connection) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync setup \u201C${name}\u201D references missing storage connection \u201C${connectionName}\u201D.`,\n\t\t);\n\t}\n\tconst type = connection.type;\n\tnormalizeStoragePath(requiredString(storage.path, `storage path for sync setup \u201C${name}\u201D`));\n\tif (type === \"s3\") {\n\t\tnormalizeS3Bucket(requiredString(storage.bucket, `S3 bucket for sync setup \u201C${name}\u201D`));\n\t\tif (Object.hasOwn(storage, \"branch\")) mixedSetupError(\"S3\", name);\n\t} else if (type === \"git\") {\n\t\tnormalizeGitBranch(requiredString(storage.branch, `Git branch for sync setup \u201C${name}\u201D`));\n\t\tif (Object.hasOwn(storage, \"bucket\")) mixedSetupError(\"Git\", name);\n\t} else if (type === \"webdav\") {\n\t\tif (Object.hasOwn(storage, \"bucket\") || Object.hasOwn(storage, \"branch\")) {\n\t\t\tmixedSetupError(\"WebDAV\", name);\n\t\t}\n\t}\n\tif (!Object.hasOwn(sync, \"include\")) {\n\t\tthrow new Error(`Invalid pi-sync settings: sync setup \u201C${name}\u201D is missing sync.include.`);\n\t}\n\tnormalizeSyncInclude(sync.include);\n\tif (typeof sync.automatic !== \"boolean\") {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync setup \u201C${name}\u201D sync.automatic must be boolean.`,\n\t\t);\n\t}\n}\n\nfunction mixedSetupError(type: string, name: string): never {\n\tthrow new Error(`Invalid pi-sync settings: ${type} sync setup \u201C${name}\u201D mixes backend fields.`);\n}\n\nexport function validateUniqueRemoteSyncSetups(\n\tsetups: Record<string, unknown>,\n\tconnections: Record<string, unknown>,\n) {\n\tconst identities = new Map<string, string>();\n\tfor (const name of Object.keys(setups)) {\n\t\tconst setup = requireOwnObject(setups, name, \"sync setup\") as unknown as SyncSetupSettings;\n\t\tconst connection = requireOwnObject(\n\t\t\tconnections,\n\t\t\tsetup.storage.connection,\n\t\t\t\"storage connection\",\n\t\t) as unknown as StorageConnectionSettings;\n\t\tconst identity = effectiveSyncSetupRemoteIdentity(setup, connection);\n\t\tconst existing = identities.get(identity);\n\t\tif (existing) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid pi-sync settings: sync setups \u201C${existing}\u201D and \u201C${name}\u201D use the same normalized remote location.`,\n\t\t\t);\n\t\t}\n\t\tidentities.set(identity, name);\n\t}\n}\n\nexport function effectiveSyncSetupRemoteIdentity(\n\tsetup: SyncSetupSettings,\n\tconnection: StorageConnectionSettings,\n) {\n\tconst storagePath = normalizeStoragePath(setup.storage.path);\n\tif (connection.type === \"git\") {\n\t\treturn JSON.stringify([\n\t\t\t\"git\",\n\t\t\tnormalizeGitRemoteIdentity(connection.remote),\n\t\t\tnormalizeGitBranch(setup.storage.branch),\n\t\t\tnormalizeGitDirectory(storagePath),\n\t\t]);\n\t}\n\tif (connection.type === \"webdav\") {\n\t\treturn JSON.stringify([\n\t\t\t\"webdav\",\n\t\t\tnormalizeWebDavIdentityUrl(connection.url),\n\t\t\tconnection.credentials.username.trim(),\n\t\t\tnormalizeWebDavPath(storagePath),\n\t\t]);\n\t}\n\treturn JSON.stringify([\n\t\t\"s3\",\n\t\tnormalizeEndpointIdentity(connection.endpoint),\n\t\tnormalizeS3Bucket(setup.storage.bucket),\n\t\tstoragePath,\n\t]);\n}\n\nfunction rejectLegacyFields(\n\tvalue: Record<string, unknown>,\n\tfields: readonly string[],\n\tcontext: string,\n) {\n\tconst field = fields.find((candidate) => Object.hasOwn(value, candidate));\n\tif (field) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: ${context} contains unsupported version 1/2 field \u201C${field}\u201D.`,\n\t\t);\n\t}\n}\n\nfunction requireNamedObjectMap(value: unknown, field: string, itemLabel: string) {\n\tconst result = requireRecord(value, field);\n\tfor (const name of Object.keys(result)) validateConfigName(name, itemLabel);\n\treturn result;\n}\n\nfunction requireOwnObject(value: Record<string, unknown>, key: string, label: string) {\n\tconst item = ownObject(value, key);\n\tif (!item) throw new Error(`Invalid pi-sync settings: ${label} \u201C${key}\u201D must be an object.`);\n\treturn item;\n}\n\nexport function ownObject<T extends object>(\n\tvalue: Record<string, unknown>,\n\tkey: string,\n): T | undefined {\n\tif (!Object.hasOwn(value, key)) return undefined;\n\tconst item = value[key];\n\treturn item && typeof item === \"object\" && !Array.isArray(item) ? (item as T) : undefined;\n}\n\nfunction requireRecord(value: unknown, field: string): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must be an object.`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nexport function validateConfigName(value: string, field: string) {\n\tif (\n\t\t!value.trim() ||\n\t\tvalue !== value.trim() ||\n\t\tvalue.length > 100 ||\n\t\tvalue === \"__proto__\" ||\n\t\tvalue === \"prototype\" ||\n\t\tvalue === \"constructor\" ||\n\t\thasControlCharacter(value)\n\t) {\n\t\tthrow new Error(`Invalid pi-sync settings: invalid ${field} name.`);\n\t}\n}\n\nexport function requiredString(value: unknown, field: string) {\n\tif (typeof value !== \"string\" || !value.trim() || hasControlCharacter(value)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must be a non-empty string.`);\n\t}\n\treturn value.trim();\n}\n\nfunction requiredCanonicalReference(value: unknown, field: string) {\n\tconst normalized = requiredString(value, field);\n\tif (value !== normalized) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must not have surrounding whitespace.`);\n\t}\n\treturn normalized;\n}\n\nfunction optionalCanonicalReference(value: unknown, field: string) {\n\tconst normalized = optionalString(value, field);\n\tif (normalized !== undefined && value !== normalized) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must not have surrounding whitespace.`);\n\t}\n\treturn normalized;\n}\n\nfunction requiredSecret(value: unknown, field: string) {\n\tif (typeof value !== \"string\" || !value || hasControlCharacter(value)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must be configured.`);\n\t}\n\treturn value;\n}\n\nexport function optionalString(value: unknown, field: string) {\n\tif (value === undefined) return undefined;\n\tif (typeof value !== \"string\" || hasControlCharacter(value)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${field} must be a string.`);\n\t}\n\treturn value.trim() || undefined;\n}\n\nexport function normalizeStoragePath(value: string) {\n\tif (value.trim() === \".\" || value.trim() === \"./\") return \"./\";\n\tconst normalized = value.trim().replace(/^\\/+|\\/+$/gu, \"\");\n\tif (\n\t\t!normalized ||\n\t\tnormalized.length > 1024 ||\n\t\tnormalized.startsWith(\"-\") ||\n\t\tnormalized.includes(\"\\\\\") ||\n\t\thasControlCharacter(normalized) ||\n\t\tnormalized.split(\"/\").some((segment) => !segment || segment === \".\" || segment === \"..\")\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync settings: storage.path must be a safe relative path.\");\n\t}\n\treturn normalized;\n}\n\nexport function normalizeS3Endpoint(value: string) {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value.trim());\n\t} catch {\n\t\tthrow new Error(\"Invalid pi-sync S3 endpoint.\");\n\t}\n\tconst loopback =\n\t\turl.hostname === \"127.0.0.1\" || url.hostname === \"localhost\" || url.hostname === \"[::1]\";\n\tif (\n\t\t(url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback)) ||\n\t\turl.username ||\n\t\turl.password ||\n\t\turl.search ||\n\t\turl.hash\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync S3 endpoint: HTTPS is required except for loopback.\");\n\t}\n\turl.pathname = url.pathname.replace(/\\/+$/gu, \"\");\n\treturn url.toString().replace(/\\/$/u, \"\");\n}\n\nexport function normalizeS3Bucket(value: string | undefined) {\n\tconst bucket = requiredString(value, \"S3 bucket\");\n\tif (bucket.includes(\"/\") || bucket.includes(\"\\\\\") || bucket.startsWith(\"-\")) {\n\t\tthrow new Error(\"Invalid pi-sync S3 bucket.\");\n\t}\n\treturn bucket;\n}\n\nfunction hasControlCharacter(value: string) {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Stored settings cannot contain controls.\n\treturn /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(value);\n}\n\nexport function isCloudflareR2Endpoint(endpoint: string | undefined) {\n\tconst value = endpoint?.trim();\n\tif (!value) return false;\n\ttry {\n\t\tconst hostname = new URL(value).hostname.toLowerCase();\n\t\treturn (\n\t\t\thostname === \"r2.cloudflarestorage.com\" || hostname.endsWith(\".r2.cloudflarestorage.com\")\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function ownRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n", "import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n\tlocalConfigPath,\n\treadMigratingLocalConfigDocument,\n\tupdateLocalConfigDocument,\n} from \"./config-file.js\";\nimport type { OnSwitchAction, PiSyncSettingsV3 } from \"./settings-types.js\";\nimport { DEFAULT_ON_SWITCH, validateSettingsDocument } from \"./settings-validation.js\";\n\nlet configUpdateQueue: Promise<void> = Promise.resolve();\n\nexport async function requireSettings() {\n\tconst settings = await readLocalConfigObject();\n\tif (!settings) {\n\t\tthrow new Error(\n\t\t\t`Missing pi-sync settings. Open /sync and choose Set up sync, or create ${localConfigPath()}.`,\n\t\t);\n\t}\n\treturn settings;\n}\n\nexport async function configuredSyncSetupNames() {\n\tconst settings = await readLocalConfigObject();\n\treturn settings\n\t\t? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right))\n\t\t: [];\n}\n\nexport async function loadOnSwitch(): Promise<OnSwitchAction> {\n\treturn (await requireSettings()).onSwitch;\n}\n\nexport function localConfigTemplate(): PiSyncSettingsV3 {\n\treturn {\n\t\tversion: 3,\n\t\tonSwitch: DEFAULT_ON_SWITCH,\n\t\tskipSecretScan: false,\n\t\tstorageConnections: {},\n\t\tsyncSetups: {},\n\t};\n}\n\nexport async function readLocalConfigDocument() {\n\tconst document = await readMigratingLocalConfigDocument((settings) => {\n\t\tvalidateSettingsDocument(settings);\n\t});\n\tif (document) validateSettingsDocument(document.parsed);\n\treturn document;\n}\n\nexport async function readLocalConfigObject(): Promise<PiSyncSettingsV3 | undefined> {\n\treturn (await readLocalConfigDocument())?.parsed as PiSyncSettingsV3 | undefined;\n}\n\nexport function updateLocalConfig(\n\tupdate: (current: PiSyncSettingsV3) => PiSyncSettingsV3,\n\tsignal?: AbortSignal,\n) {\n\tconst operation = configUpdateQueue.then(() => {\n\t\tsignal?.throwIfAborted();\n\t\treturn performLocalConfigUpdate(update, signal);\n\t});\n\tconfigUpdateQueue = operation.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn operation;\n}\n\nasync function performLocalConfigUpdate(\n\tupdate: (current: PiSyncSettingsV3) => PiSyncSettingsV3,\n\tsignal?: AbortSignal,\n) {\n\treturn updateLocalConfigDocument(localConfigTemplate(), update, validateSettingsDocument, signal);\n}\n\nexport async function writeLocalConfigObject(value: PiSyncSettingsV3 | Record<string, unknown>) {\n\tvalidateSettingsDocument(value as Record<string, unknown>);\n\tconst configPath = localConfigPath();\n\tawait fs.mkdir(path.dirname(configPath), { recursive: true });\n\ttry {\n\t\tconst stat = await fs.lstat(configPath);\n\t\tif (stat.isSymbolicLink())\n\t\t\tthrow new Error(`Refusing to overwrite symlinked pi-sync settings: ${configPath}`);\n\t\tif (!stat.isFile()) throw new Error(`pi-sync settings are not a regular file: ${configPath}`);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t}\n\tconst temporaryPath = path.join(\n\t\tpath.dirname(configPath),\n\t\t`.${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`,\n\t);\n\tlet handle: fs.FileHandle | undefined;\n\ttry {\n\t\thandle = await fs.open(temporaryPath, \"wx\", 0o600);\n\t\tawait handle.writeFile(`${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n\t\tif (process.platform !== \"win32\") await handle.chmod(0o600);\n\t\tawait handle.sync();\n\t\tawait handle.close();\n\t\thandle = undefined;\n\t\tawait fs.rename(temporaryPath, configPath);\n\t} catch (error) {\n\t\tawait handle?.close().catch(() => undefined);\n\t\tawait fs.rm(temporaryPath, { force: true }).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n", "import { createHash } from \"node:crypto\";\nimport { backendIdentityCoordinates } from \"../backends/backend-identity.js\";\nimport {\n\tnormalizeGitBranch,\n\tnormalizeGitDirectory,\n\tnormalizeGitRemote,\n} from \"../backends/git/git-config.js\";\nimport { normalizeWebDavPath, normalizeWebDavUrl } from \"../backends/webdav/webdav-config.js\";\nimport { normalizeSyncInclude } from \"../sync/sync-policy.js\";\nimport { localConfigPath, readActiveLocalConfigDocumentForRepair } from \"./config-file.js\";\nimport { requireSettings } from \"./settings-store.js\";\nimport type {\n\tAnySyncConfig,\n\tOnSwitchAction,\n\tPartialConfig,\n\tPiSyncSettingsV3,\n\tStorageConnectionSettings,\n\tSyncSetupSettings,\n} from \"./settings-types.js\";\nimport {\n\tDEFAULT_ON_SWITCH,\n\tisCloudflareR2Endpoint,\n\tnormalizeS3Bucket,\n\tnormalizeS3Endpoint,\n\tnormalizeStoragePath,\n\toptionalString,\n\townObject,\n\trequiredString,\n\tvalidateConfigName,\n\tvalidateSettingsDocument,\n} from \"./settings-validation.js\";\n\nexport async function loadConfig(setupName?: string): Promise<AnySyncConfig> {\n\treturn configFromSettings(await requireSettings(), setupName);\n}\n\n/** A background freshness read must never trigger legacy-file publication. */\nexport async function loadConfigForCheck(): Promise<AnySyncConfig> {\n\tconst document = await readActiveLocalConfigDocumentForRepair();\n\tif (!document) throw new Error(`Missing pi-sync settings: ${localConfigPath()}`);\n\tvalidateSettingsDocument(document.parsed);\n\treturn configFromSettings(document.parsed as PiSyncSettingsV3);\n}\n\nfunction configFromSettings(settings: PiSyncSettingsV3, setupName?: string): AnySyncConfig {\n\tconst selectedName = setupName ?? settings.activeSyncSetup;\n\tif (!selectedName) throw new Error(\"No sync setups are configured.\");\n\tvalidateConfigName(selectedName, \"sync setup\");\n\tconst setup = ownObject<SyncSetupSettings>(settings.syncSetups, selectedName);\n\tif (!setup)\n\t\tthrow new Error(`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D was not found.`);\n\tconst connectionName = setup.storage.connection;\n\tconst connection = ownObject<StorageConnectionSettings>(\n\t\tsettings.storageConnections,\n\t\tconnectionName,\n\t);\n\tif (!connection) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D references missing storage connection \u201C${connectionName}\u201D.`,\n\t\t);\n\t}\n\treturn resolveSyncConfig(\n\t\tselectedName,\n\t\tsetup,\n\t\tconnectionName,\n\t\tconnection,\n\t\tsettings.onSwitch,\n\t\tsettings.skipSecretScan ?? false,\n\t);\n}\n\nexport type SyncSetupStorageReview = Pick<\n\tPartialConfig,\n\t\"connectionName\" | \"storageKind\" | \"storagePath\" | \"bucket\" | \"branch\"\n>;\n\n/** A validated setup-facing projection used by manager and settings UI. */\nexport async function loadPartialConfig(setupName?: string): Promise<PartialConfig> {\n\tconst config = await loadConfig(setupName);\n\treturn {\n\t\tsetupName: config.setupName,\n\t\t...storageReviewFromConfig(config),\n\t\tinclude: [...config.include],\n\t\tautomatic: config.automatic,\n\t\tonSwitch: config.onSwitch,\n\t};\n}\n\nexport function syncSetupStorageReview(\n\tsetupName: string,\n\tsetup: SyncSetupSettings,\n\tconnectionName: string,\n\tconnection: StorageConnectionSettings,\n): SyncSetupStorageReview {\n\treturn storageReviewFromConfig(\n\t\tresolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false),\n\t);\n}\n\nexport function syncSetupReviewIdentity(\n\tsetupName: string,\n\tsetup: SyncSetupSettings,\n\tconnectionName: string,\n\tconnection: StorageConnectionSettings,\n) {\n\treturn syncConfigReviewIdentity(\n\t\tresolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false),\n\t);\n}\n\nexport function syncConfigReviewIdentity(config: AnySyncConfig) {\n\treturn JSON.stringify([\n\t\tconfig.setupName,\n\t\tconfig.connectionName,\n\t\tbackendIdentityCoordinates(config),\n\t\tconfig.include,\n\t\tconfig.automatic,\n\t]);\n}\n\nexport function syncConfigReviewFingerprint(config: AnySyncConfig) {\n\treturn createHash(\"sha256\").update(syncConfigReviewIdentity(config)).digest(\"hex\");\n}\n\n/** Internal freshness token, including credentials; never display or persist it. */\nexport function syncCheckConfigFingerprint(config: AnySyncConfig) {\n\treturn createHash(\"sha256\").update(JSON.stringify(config)).digest(\"hex\");\n}\n\nfunction storageReviewFromConfig(config: AnySyncConfig): SyncSetupStorageReview {\n\treturn {\n\t\tconnectionName: config.connectionName,\n\t\tstorageKind: config.backend.type,\n\t\tstoragePath: config.storagePath,\n\t\t...(config.backend.type === \"s3\"\n\t\t\t? { bucket: config.backend.destination.bucket }\n\t\t\t: config.backend.type === \"git\"\n\t\t\t\t? { branch: config.backend.destination.branch }\n\t\t\t\t: {}),\n\t};\n}\n\nfunction resolveSyncConfig(\n\tsetupName: string,\n\tsetup: SyncSetupSettings,\n\tconnectionName: string,\n\tconnection: StorageConnectionSettings,\n\tonSwitch: OnSwitchAction,\n\tskipSecretScan: boolean,\n): AnySyncConfig {\n\tconst storagePath = normalizeStoragePath(setup.storage.path);\n\tconst namespace =\n\t\tstoragePath === \"./\" ? \"root\" : storagePath.slice(storagePath.lastIndexOf(\"/\") + 1);\n\tconst include = normalizeSyncInclude(setup.sync.include);\n\tconst common = {\n\t\tsetupName,\n\t\tconnectionName,\n\t\tstoragePath,\n\t\tsnapshotIdentity: namespace,\n\t\tinclude,\n\t\tautomatic: setup.sync.automatic,\n\t\tonSwitch,\n\t\tskipSecretScan,\n\t};\n\tif (connection.type === \"git\") {\n\t\treturn {\n\t\t\t...common,\n\t\t\tbackend: {\n\t\t\t\ttype: \"git\",\n\t\t\t\tprofile: { kind: \"git\", remote: normalizeGitRemote(connection.remote) as string },\n\t\t\t\tdestination: {\n\t\t\t\t\tbranch: normalizeGitBranch(setup.storage.branch),\n\t\t\t\t\tdirectory: normalizeGitDirectory(storagePath),\n\t\t\t\t\tnamespace,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\tif (connection.type === \"webdav\") {\n\t\treturn {\n\t\t\t...common,\n\t\t\tbackend: {\n\t\t\t\ttype: \"webdav\",\n\t\t\t\tprofile: {\n\t\t\t\t\tkind: \"webdav\",\n\t\t\t\t\turl: normalizeWebDavUrl(connection.url) as string,\n\t\t\t\t\tusername: connection.credentials.username,\n\t\t\t\t\tpassword: connection.credentials.password,\n\t\t\t\t},\n\t\t\t\tdestination: { path: normalizeWebDavPath(storagePath), namespace },\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\t...common,\n\t\tbackend: {\n\t\t\ttype: \"s3\",\n\t\t\tprofile: {\n\t\t\t\tkind: isCloudflareR2Endpoint(connection.endpoint) ? \"r2\" : \"s3-compatible\",\n\t\t\t\tendpoint: normalizeS3Endpoint(connection.endpoint),\n\t\t\t\tregion: requiredString(connection.region, \"S3 region\"),\n\t\t\t\taccessKeyId: connection.credentials.accessKeyId,\n\t\t\t\tsecretAccessKey: connection.credentials.secretAccessKey,\n\t\t\t\tsessionToken: optionalString(connection.credentials.sessionToken, \"S3 session token\"),\n\t\t\t},\n\t\t\tdestination: {\n\t\t\t\tbucket: normalizeS3Bucket(setup.storage.bucket),\n\t\t\t\tprefix: storagePath,\n\t\t\t\tnamespace,\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport function sessionTokenWarnings(config: { endpoint?: string; sessionToken?: string }) {\n\tif (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];\n\treturn [\n\t\t\"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.\",\n\t];\n}\n\nexport function syncSessionsWarnings(config: { include: readonly string[] }) {\n\tif (!config.include.includes(\"sessions\")) return [];\n\treturn [\n\t\t\"sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust.\",\n\t];\n}\n\nexport function isEnabled(value: boolean | string | undefined, defaultValue: boolean) {\n\tif (value === undefined) return defaultValue;\n\tif (typeof value === \"boolean\") return value;\n\treturn ![\"0\", \"false\", \"no\", \"off\"].includes(value.trim().toLowerCase());\n}\n\nexport function isExplicitlyEnabled(value: boolean | string | undefined) {\n\tif (typeof value === \"boolean\") return value;\n\treturn [\"1\", \"true\", \"yes\", \"on\"].includes(value?.trim().toLowerCase() ?? \"\");\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["installed", "path", "path"]
7
+ }
@@ -1,7 +1,7 @@
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
 
4
- // src/command.ts
4
+ // src/commands/command.ts
5
5
  var YES_FLAG_COMPLETIONS = [
6
6
  { value: "--yes", label: "--yes", description: "Skip confirmation prompts" },
7
7
  { value: "-y", label: "-y", description: "Skip confirmation prompts" }
@@ -205,6 +205,7 @@ function usage() {
205
205
  return [
206
206
  "Usage: /sync <command>",
207
207
  `Commands: ${commands}`,
208
+ "Automatic sync: background startup checks only; use /sync to review and transfer. At shutdown, selected content is pushed automatically only when sessions are included. Startup checks run in TUI/RPC, not print/JSON.",
208
209
  "Settings: use /sync init or edit storage connections and sync setups in ~/.pi/agent/pi-sync.json (or the configured Pi agent directory). Version 1 and version 2 settings are unsupported and are never rewritten."
209
210
  ].join("\n");
210
211
  }
@@ -218,4 +219,4 @@ export {
218
219
  resolveSyncCommand,
219
220
  usage
220
221
  };
221
- //# sourceMappingURL=chunk-4U6AFLJB.ts.map
222
+ //# sourceMappingURL=chunk-CHH26D4F.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/command.ts"],
4
+ "sourcesContent": ["import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport type { CommandArgumentCompletion, CommandOptions } from \"./command-types.js\";\n\nconst YES_FLAG_COMPLETIONS: readonly CommandArgumentCompletion[] = [\n\t{ value: \"--yes\", label: \"--yes\", description: \"Skip confirmation prompts\" },\n\t{ value: \"-y\", label: \"-y\", description: \"Skip confirmation prompts\" },\n];\nexport const SYNC_COMMANDS = [\n\t{ name: \"help\", description: \"Show command usage\" },\n\t{ name: \"use\", description: \"Switch the current sync setup\", usageSuffix: \" <setup>\" },\n\t{ name: \"init\", description: \"Create local config template\" },\n\t{ name: \"config\", description: \"Show resolved configuration\" },\n\t{ name: \"files\", description: \"Choose included content\" },\n\t{ name: \"status\", description: \"Show sync status\" },\n\t{ name: \"diff\", description: \"Show local/remote diff\" },\n\t{ name: \"doctor\", description: \"Check config, secrets, and lock state\" },\n\t{ name: \"push\", description: \"Upload local settings\" },\n\t{ name: \"pull\", description: \"Apply remote settings\" },\n\t{ name: \"sync\", description: \"Push or pull as needed\" },\n\t{ name: \"history\", description: \"Show recent remote snapshots\" },\n\t{ name: \"rollback\", description: \"Apply a previous snapshot\", usageSuffix: \" <snapshot>\" },\n\t{ name: \"migrate-state\", description: \"Move legacy state into pi-sync/\" },\n\t{ name: \"unlock\", description: \"Remove a stale local lock\", usageSuffix: \" --stale\" },\n] as const;\n\nexport type SyncCommandName = (typeof SYNC_COMMANDS)[number][\"name\"];\n\nconst SYNC_COMMAND_COMPLETIONS: readonly CommandArgumentCompletion[] = SYNC_COMMANDS.map(\n\t({ name, description }) => ({ value: name, label: name, description }),\n);\nlet setupCompletionNames: string[] = [];\n\nexport function setSyncSetupCompletions(names: readonly string[]) {\n\tsetupCompletionNames = [...new Set(names)].sort((left, right) => left.localeCompare(right));\n}\nconst SETUP_FLAG_COMPLETION = {\n\tvalue: \"--setup\",\n\tlabel: \"--setup\",\n\tdescription: \"Address a sync setup without switching\",\n} as const;\nconst SYNC_FLAG_COMPLETIONS: Record<string, readonly CommandArgumentCompletion[]> = {\n\tconfig: [SETUP_FLAG_COMPLETION],\n\tfiles: [SETUP_FLAG_COMPLETION],\n\tstatus: [SETUP_FLAG_COMPLETION],\n\tdiff: [SETUP_FLAG_COMPLETION],\n\tdoctor: [SETUP_FLAG_COMPLETION],\n\tpush: [\n\t\t...YES_FLAG_COMPLETIONS,\n\t\t{ value: \"--force\", label: \"--force\", description: \"Overwrite visible remote changes\" },\n\t\tSETUP_FLAG_COMPLETION,\n\t],\n\tpull: [\n\t\t...YES_FLAG_COMPLETIONS,\n\t\t{ value: \"--force\", label: \"--force\", description: \"Overwrite local changes\" },\n\t\tSETUP_FLAG_COMPLETION,\n\t],\n\tsync: [\n\t\t...YES_FLAG_COMPLETIONS,\n\t\t{ value: \"--force\", label: \"--force\", description: \"Resolve conflicts by forcing action\" },\n\t\tSETUP_FLAG_COMPLETION,\n\t],\n\thistory: [SETUP_FLAG_COMPLETION],\n\trollback: [...YES_FLAG_COMPLETIONS, SETUP_FLAG_COMPLETION],\n\t\"migrate-state\": YES_FLAG_COMPLETIONS,\n\tunlock: [{ value: \"--stale\", label: \"--stale\", description: \"Remove only a stale lock\" }],\n};\n\nexport function splitArgs(input: string) {\n\treturn (\n\t\tinput.match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g)?.map((arg) => arg.replace(/^['\"]|['\"]$/g, \"\")) ??\n\t\t[]\n\t);\n}\n\nexport function parseOptions(args: string[]): CommandOptions {\n\tconst values: string[] = [];\n\tlet yes = false;\n\tlet force = false;\n\tlet stale = false;\n\tlet setup: string | undefined;\n\tfor (let index = 0; index < args.length; index += 1) {\n\t\tconst arg = args[index];\n\t\tif (arg === \"--yes\" || arg === \"-y\") yes = true;\n\t\telse if (arg === \"--force\") force = true;\n\t\telse if (arg === \"--stale\") stale = true;\n\t\telse if (arg === \"--setup\") {\n\t\t\tconst name = args[index + 1];\n\t\t\tif (!name || name.startsWith(\"-\")) throw new Error(\"--setup requires a sync setup name.\");\n\t\t\tif (setup !== undefined) throw new Error(\"--setup may be provided only once.\");\n\t\t\tsetup = name;\n\t\t\tindex += 1;\n\t\t} else if (arg.startsWith(\"-\")) {\n\t\t\tthrow new Error(`Unknown sync option: ${arg}`);\n\t\t} else values.push(arg);\n\t}\n\treturn {\n\t\tyes,\n\t\tforce,\n\t\tstale,\n\t\tsilent: false,\n\t\treload: true,\n\t\tauto: false,\n\t\t...(setup === undefined ? {} : { setup }),\n\t\targs: values,\n\t};\n}\n\nexport function validateCommandOptions(command: string, options: CommandOptions) {\n\tconst setupAllowed = new Set([\n\t\t\"config\",\n\t\t\"files\",\n\t\t\"status\",\n\t\t\"diff\",\n\t\t\"doctor\",\n\t\t\"push\",\n\t\t\"pull\",\n\t\t\"sync\",\n\t\t\"history\",\n\t\t\"rollback\",\n\t]);\n\tif (options.setup && !setupAllowed.has(command)) {\n\t\tthrow new Error(`--setup is not supported by /sync ${command}.`);\n\t}\n\tif (options.yes && ![\"push\", \"pull\", \"sync\", \"rollback\", \"migrate-state\"].includes(command)) {\n\t\tthrow new Error(`Confirmation/force options are not supported by /sync ${command}.`);\n\t}\n\tif (options.force && ![\"push\", \"pull\", \"sync\", \"rollback\"].includes(command)) {\n\t\tthrow new Error(`Confirmation/force options are not supported by /sync ${command}.`);\n\t}\n\tif (options.stale && command !== \"unlock\") {\n\t\tthrow new Error(`--stale is not supported by /sync ${command}.`);\n\t}\n\tconst expectedValues = command === \"rollback\" || command === \"use\" ? 1 : 0;\n\tif (options.args.length !== expectedValues) {\n\t\tif (command === \"rollback\")\n\t\t\tthrow new Error(\"Usage: /sync rollback <snapshot-id> [--yes] [--setup <name>]\");\n\t\tif (command === \"use\") throw new Error(\"Usage: /sync use <setup>\");\n\t\tthrow new Error(`Unexpected argument for /sync ${command}: ${options.args.join(\" \")}`);\n\t}\n}\n\nexport function completeSyncArguments(argumentPrefix: string): CommandArgumentCompletion[] | null {\n\tconst prefix = argumentPrefix.trimStart();\n\tif (prefix === \"\") return [...SYNC_COMMAND_COMPLETIONS];\n\n\tconst trailingSpace = /\\s$/.test(prefix);\n\tconst tokens = splitArgs(prefix);\n\tif (tokens.length === 0) return [...SYNC_COMMAND_COMPLETIONS];\n\n\tconst [command] = tokens;\n\tif (tokens.length === 1 && !trailingSpace) {\n\t\tconst matches = SYNC_COMMAND_COMPLETIONS.filter((item) => item.value.startsWith(command));\n\t\treturn matches.length > 0 ? [...matches] : null;\n\t}\n\n\tconst args = tokens.slice(1);\n\tif (command === \"use\") {\n\t\tif (args.length > 1 || (trailingSpace && args.length > 0)) return null;\n\t\treturn completeSetupValue(prefix, trailingSpace ? \"\" : (args[0] ?? \"\"));\n\t}\n\tconst setupFlagIndex = args.lastIndexOf(\"--setup\");\n\tif (setupFlagIndex >= 0 && setupFlagIndex === args.length - (trailingSpace ? 1 : 2)) {\n\t\tconst currentSetup = trailingSpace ? \"\" : (args.at(-1) ?? \"\");\n\t\tif (!currentSetup.startsWith(\"-\")) return completeSetupValue(prefix, currentSetup);\n\t}\n\n\tconst flagCompletions = SYNC_FLAG_COMPLETIONS[command];\n\tif (!flagCompletions) return null;\n\n\tconst completedArgs = trailingSpace ? args : args.slice(0, -1);\n\tconst completedValues = completedArgs.filter((arg) => !arg.startsWith(\"-\"));\n\tif (command === \"rollback\" ? completedValues.length > 1 : completedValues.length > 0) {\n\t\treturn null;\n\t}\n\n\tconst current = trailingSpace ? \"\" : (args.at(-1) ?? \"\");\n\tif (current && !current.startsWith(\"-\")) return null;\n\n\tconst currentRaw = trailingSpace ? \"\" : (prefix.match(/\\S+$/)?.[0] ?? \"\");\n\tconst completionPrefix = trailingSpace\n\t\t? prefix\n\t\t: prefix.slice(0, prefix.length - currentRaw.length);\n\tconst matches = flagCompletions.filter((item) => item.value.startsWith(current));\n\treturn matches.length > 0\n\t\t? matches.map((item) => ({ ...item, value: `${completionPrefix}${item.value}` }))\n\t\t: null;\n}\n\nfunction completeSetupValue(prefix: string, current: string) {\n\tconst currentRaw = current ? (prefix.match(/\\S+$/u)?.[0] ?? \"\") : \"\";\n\tconst completionPrefix = currentRaw ? prefix.slice(0, prefix.length - currentRaw.length) : prefix;\n\tconst matches = setupCompletionNames.filter((name) => name.startsWith(current));\n\treturn matches.length > 0\n\t\t? matches.map((name) => ({\n\t\t\t\tvalue: `${completionPrefix}${name}`,\n\t\t\t\tlabel: name,\n\t\t\t\tdescription: \"Sync setup\",\n\t\t\t}))\n\t\t: null;\n}\n\nexport function syncMenuOptions() {\n\treturn SYNC_COMMANDS.map(({ name, description }) => `${name} \u2014 ${description}`);\n}\n\nexport function syncCommandFromMenuOption(option: string): SyncCommandName | undefined {\n\treturn SYNC_COMMANDS.find(({ name, description }) => option === `${name} \u2014 ${description}`)?.name;\n}\n\nexport async function resolveSyncCommand(input: string, ctx: ExtensionCommandContext) {\n\tconst [subcommand, ...rest] = splitArgs(input);\n\tif (subcommand) return { subcommand, rest };\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(usage(), \"info\");\n\t\treturn undefined;\n\t}\n\n\tconst selectedOption = await ctx.ui.select(\"pi-sync\", syncMenuOptions());\n\tconst selected = selectedOption ? syncCommandFromMenuOption(selectedOption) : undefined;\n\tif (!selected) return undefined;\n\tif (selected !== \"rollback\") return { subcommand: selected, rest: [] };\n\n\tconst target = (await ctx.ui.input(\"Rollback snapshot\", \"snapshot id\"))?.trim();\n\tif (!target) {\n\t\tctx.ui.notify(\"Rollback cancelled.\", \"info\");\n\t\treturn undefined;\n\t}\n\treturn { subcommand: selected, rest: [target] };\n}\n\nexport function usage() {\n\tconst commands = SYNC_COMMANDS.map(\n\t\t(command) => `${command.name}${\"usageSuffix\" in command ? command.usageSuffix : \"\"}`,\n\t).join(\", \");\n\treturn [\n\t\t\"Usage: /sync <command>\",\n\t\t`Commands: ${commands}`,\n\t\t\"Automatic sync: background startup checks only; use /sync to review and transfer. At shutdown, selected content is pushed automatically only when sessions are included. Startup checks run in TUI/RPC, not print/JSON.\",\n\t\t\"Settings: use /sync init or edit storage connections and sync setups in ~/.pi/agent/pi-sync.json (or the configured Pi agent directory). Version 1 and version 2 settings are unsupported and are never rewritten.\",\n\t].join(\"\\n\");\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["matches"]
7
+ }
@@ -0,0 +1,50 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+ import {
4
+ errorMessage
5
+ } from "./chunk-2B2ELUF5.ts";
6
+
7
+ // src/sync/sync-error-guidance.ts
8
+ function syncErrorGuidance(error) {
9
+ const detail = errorMessage(error);
10
+ const message = detail.length > 1200 ? `${detail.slice(0, 1200)}\u2026 (details truncated)` : detail;
11
+ const code = error?.code;
12
+ const status = error?.status;
13
+ let next;
14
+ if (code === "ENOSPC") {
15
+ next = "Free disk space for the private pi-sync settings/state directory, then retry.";
16
+ } else if (code && ["EACCES", "EPERM", "EROFS"].includes(code)) {
17
+ next = "Check write access to the private pi-sync settings/state directory, then retry.";
18
+ } else if (code && ["EBUSY", "EIO", "EMFILE", "ENFILE"].includes(code)) {
19
+ next = "Check local disk availability and other processes using the private settings file, then retry.";
20
+ } else if (/changed while|type changed|reopen/iu.test(message)) {
21
+ next = "Reopen the item in /sync and review its current settings before saving again.";
22
+ } else if (status === 401 || status === 403 || /Permission denied|Authentication failed|HTTP (401|403)|failed \((401|403)\)/iu.test(message)) {
23
+ next = "Check the storage connection's credentials and remote permissions in /sync \u2192 More \u2192 Storage connections.";
24
+ } else if (status === 404 || /HTTP 404|failed \(404\)/iu.test(message)) {
25
+ next = "Verify the connection address and setup's bucket, branch, or path with your provider; this response alone does not prove which resource is missing.";
26
+ } else if (status !== void 0 && status >= 500) {
27
+ next = "Check your storage provider's service status, then retry Check setup before syncing.";
28
+ } else if (/timed out|timeout|request failed|resolve host|Could not resolve|ENOTFOUND|ECONNREFUSED/iu.test(
29
+ message
30
+ )) {
31
+ next = "Check the server address and network connection, then run /sync doctor for the affected setup.";
32
+ } else if (/strong ETag|If-Match|If-None-Match|precondition|conditional header/iu.test(message)) {
33
+ next = "Ask your provider to check strong ETags and conditional writes, including proxy behavior, or choose compatible storage. Do not bypass these safety checks.";
34
+ } else if (/spawn git ENOENT/iu.test(message)) {
35
+ next = "Install Git 2.30 or newer and make sure Pi can find git on PATH, then retry.";
36
+ } else if (/response exceeds.*byte limit/iu.test(message)) {
37
+ next = "Verify the storage endpoint; the server returned an unexpectedly large response. Check your provider's logs before retrying.";
38
+ } else if (/settings|config|already exists|duplicates|same normalized/iu.test(message)) {
39
+ next = "Correct the named field or item in /sync. If the settings file is invalid, repair it before retrying; do not replace it with defaults.";
40
+ } else {
41
+ next = "Open /sync \u2192 More \u2192 Check setup and review the reported checks before retrying.";
42
+ }
43
+ return `${message}
44
+ ${next}`;
45
+ }
46
+
47
+ export {
48
+ syncErrorGuidance
49
+ };
50
+ //# sourceMappingURL=chunk-CPEHGXJV.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/sync-error-guidance.ts"],
4
+ "sourcesContent": ["import { errorMessage } from \"./sync-errors.js\";\n\n/** Add recovery advice without changing backend outcome semantics or printing error causes. */\nexport function syncErrorGuidance(error: unknown): string {\n\tconst detail = errorMessage(error);\n\tconst message = detail.length > 1200 ? `${detail.slice(0, 1200)}\u2026 (details truncated)` : detail;\n\tconst code = (error as NodeJS.ErrnoException | undefined)?.code;\n\tconst status = (error as { status?: number } | undefined)?.status;\n\tlet next: string;\n\tif (code === \"ENOSPC\") {\n\t\tnext = \"Free disk space for the private pi-sync settings/state directory, then retry.\";\n\t} else if (code && [\"EACCES\", \"EPERM\", \"EROFS\"].includes(code)) {\n\t\tnext = \"Check write access to the private pi-sync settings/state directory, then retry.\";\n\t} else if (code && [\"EBUSY\", \"EIO\", \"EMFILE\", \"ENFILE\"].includes(code)) {\n\t\tnext =\n\t\t\t\"Check local disk availability and other processes using the private settings file, then retry.\";\n\t} else if (/changed while|type changed|reopen/iu.test(message)) {\n\t\tnext = \"Reopen the item in /sync and review its current settings before saving again.\";\n\t} else if (\n\t\tstatus === 401 ||\n\t\tstatus === 403 ||\n\t\t/Permission denied|Authentication failed|HTTP (401|403)|failed \\((401|403)\\)/iu.test(message)\n\t) {\n\t\tnext =\n\t\t\t\"Check the storage connection's credentials and remote permissions in /sync \u2192 More \u2192 Storage connections.\";\n\t} else if (status === 404 || /HTTP 404|failed \\(404\\)/iu.test(message)) {\n\t\tnext =\n\t\t\t\"Verify the connection address and setup's bucket, branch, or path with your provider; this response alone does not prove which resource is missing.\";\n\t} else if (status !== undefined && status >= 500) {\n\t\tnext = \"Check your storage provider's service status, then retry Check setup before syncing.\";\n\t} else if (\n\t\t/timed out|timeout|request failed|resolve host|Could not resolve|ENOTFOUND|ECONNREFUSED/iu.test(\n\t\t\tmessage,\n\t\t)\n\t) {\n\t\tnext =\n\t\t\t\"Check the server address and network connection, then run /sync doctor for the affected setup.\";\n\t} else if (/strong ETag|If-Match|If-None-Match|precondition|conditional header/iu.test(message)) {\n\t\tnext =\n\t\t\t\"Ask your provider to check strong ETags and conditional writes, including proxy behavior, or choose compatible storage. Do not bypass these safety checks.\";\n\t} else if (/spawn git ENOENT/iu.test(message)) {\n\t\tnext = \"Install Git 2.30 or newer and make sure Pi can find git on PATH, then retry.\";\n\t} else if (/response exceeds.*byte limit/iu.test(message)) {\n\t\tnext =\n\t\t\t\"Verify the storage endpoint; the server returned an unexpectedly large response. Check your provider's logs before retrying.\";\n\t} else if (/settings|config|already exists|duplicates|same normalized/iu.test(message)) {\n\t\tnext =\n\t\t\t\"Correct the named field or item in /sync. If the settings file is invalid, repair it before retrying; do not replace it with defaults.\";\n\t} else {\n\t\tnext = \"Open /sync \u2192 More \u2192 Check setup and review the reported checks before retrying.\";\n\t}\n\treturn `${message}\\n${next}`;\n}\n"],
5
+ "mappings": ";;;;;;;AAGO,SAAS,kBAAkB,OAAwB;AACzD,QAAM,SAAS,aAAa,KAAK;AACjC,QAAM,UAAU,OAAO,SAAS,OAAO,GAAG,OAAO,MAAM,GAAG,IAAI,CAAC,+BAA0B;AACzF,QAAM,OAAQ,OAA6C;AAC3D,QAAM,SAAU,OAA2C;AAC3D,MAAI;AACJ,MAAI,SAAS,UAAU;AACtB,WAAO;AAAA,EACR,WAAW,QAAQ,CAAC,UAAU,SAAS,OAAO,EAAE,SAAS,IAAI,GAAG;AAC/D,WAAO;AAAA,EACR,WAAW,QAAQ,CAAC,SAAS,OAAO,UAAU,QAAQ,EAAE,SAAS,IAAI,GAAG;AACvE,WACC;AAAA,EACF,WAAW,sCAAsC,KAAK,OAAO,GAAG;AAC/D,WAAO;AAAA,EACR,WACC,WAAW,OACX,WAAW,OACX,gFAAgF,KAAK,OAAO,GAC3F;AACD,WACC;AAAA,EACF,WAAW,WAAW,OAAO,4BAA4B,KAAK,OAAO,GAAG;AACvE,WACC;AAAA,EACF,WAAW,WAAW,UAAa,UAAU,KAAK;AACjD,WAAO;AAAA,EACR,WACC,2FAA2F;AAAA,IAC1F;AAAA,EACD,GACC;AACD,WACC;AAAA,EACF,WAAW,uEAAuE,KAAK,OAAO,GAAG;AAChG,WACC;AAAA,EACF,WAAW,qBAAqB,KAAK,OAAO,GAAG;AAC9C,WAAO;AAAA,EACR,WAAW,iCAAiC,KAAK,OAAO,GAAG;AAC1D,WACC;AAAA,EACF,WAAW,8DAA8D,KAAK,OAAO,GAAG;AACvF,WACC;AAAA,EACF,OAAO;AACN,WAAO;AAAA,EACR;AACA,SAAO,GAAG,OAAO;AAAA,EAAK,IAAI;AAC3B;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
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
 
4
- // src/sync-backend.ts
4
+ // src/backends/sync-backend.ts
5
5
  var SyncBackendConflictError = class extends Error {
6
6
  code = "SYNC_BACKEND_CONFLICT";
7
7
  phase;
@@ -31,4 +31,4 @@ export {
31
31
  SyncBackendPublicationOutcomeUnknownError,
32
32
  expectedRemoteHead
33
33
  };
34
- //# sourceMappingURL=chunk-5MQ76BK5.ts.map
34
+ //# sourceMappingURL=chunk-D5X27LCA.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/backends/sync-backend.ts"],
4
+ "sourcesContent": ["import type { Snapshot, SnapshotSelection } from \"../snapshot/snapshot-types.js\";\n\nexport type PublicationCapability =\n\t| \"read-check-write-verify\"\n\t| \"conditional-required\"\n\t| \"atomic-conditional\"\n\t| \"lease-protected\";\n\nexport interface RemoteHead {\n\t/** Backend-owned reference used only to retrieve this immutable snapshot. */\n\tsnapshotRef: string;\n\t/** Backend-neutral snapshot content identity retained in local sync state. */\n\tsnapshotId: string;\n\trevision: string;\n\tcreatedAt: string;\n\tmachine: string;\n\tsyncSessions: boolean;\n\t/** Validated lightweight projection; revalidate it against the immutable snapshot before adoption. */\n\tselection?: SnapshotSelection;\n}\n\nexport interface RemoteHistoryEntry {\n\tsnapshotRef: string;\n\tsnapshotId: string;\n\tcreatedAt: string;\n\tmachine: string;\n\tsyncSessions: boolean;\n}\n\nexport type ExpectedRemoteHead = { kind: \"missing\" } | { kind: \"revision\"; revision: string };\n\nexport interface PublishSnapshotOptions {\n\tsignal?: AbortSignal;\n\t/** Called at the backend's active-head commit boundary. */\n\tonCommit?: () => void;\n}\n\nexport interface PublishSnapshotResult {\n\thead: RemoteHead;\n\twarnings: string[];\n}\n\nexport interface BackendDiagnostic {\n\tkey: string;\n\tlevel: \"info\" | \"warning\" | \"error\";\n\tmessage: string;\n}\n\nexport interface SyncBackend {\n\treadonly identity: string;\n\treadonly destination: string;\n\treadonly capability: PublicationCapability;\n\t/** Compare opaque revisions produced by this backend identity. */\n\tsameRevision(left: string, right: string): boolean;\n\treadHead(signal?: AbortSignal): Promise<RemoteHead | undefined>;\n\treadSnapshot(reference: string, signal?: AbortSignal): Promise<Snapshot>;\n\tpublishSnapshot(\n\t\tsnapshot: Snapshot,\n\t\texpected: ExpectedRemoteHead,\n\t\toptions?: PublishSnapshotOptions,\n\t): Promise<PublishSnapshotResult>;\n\tlistHistory(signal?: AbortSignal): Promise<RemoteHistoryEntry[]>;\n\tdiagnose(signal?: AbortSignal): Promise<BackendDiagnostic[]>;\n}\n\nexport class SyncBackendConflictError extends Error {\n\treadonly code = \"SYNC_BACKEND_CONFLICT\";\n\treadonly phase: \"before-commit\" | \"after-commit\";\n\treadonly currentHead?: RemoteHead;\n\treadonly candidateMayHaveBeenActive: boolean;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: ErrorOptions & {\n\t\t\tphase?: \"before-commit\" | \"after-commit\";\n\t\t\tcurrentHead?: RemoteHead;\n\t\t\tcandidateMayHaveBeenActive?: boolean;\n\t\t} = {},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SyncBackendConflictError\";\n\t\tthis.phase = options.phase ?? \"before-commit\";\n\t\tthis.currentHead = options.currentHead;\n\t\tthis.candidateMayHaveBeenActive = options.candidateMayHaveBeenActive ?? false;\n\t}\n}\n\nexport class SyncBackendPublicationOutcomeUnknownError extends Error {\n\treadonly code = \"SYNC_BACKEND_PUBLICATION_OUTCOME_UNKNOWN\";\n\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SyncBackendPublicationOutcomeUnknownError\";\n\t}\n}\n\nexport function expectedRemoteHead(head: RemoteHead | undefined): ExpectedRemoteHead {\n\treturn head ? { kind: \"revision\", revision: head.revision } : { kind: \"missing\" };\n}\n"],
5
+ "mappings": ";;;;AAiEO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAC1C,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACC,SACA,UAII,CAAC,GACJ;AACD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,cAAc,QAAQ;AAC3B,SAAK,6BAA6B,QAAQ,8BAA8B;AAAA,EACzE;AACD;AAEO,IAAM,4CAAN,cAAwD,MAAM;AAAA,EAC3D,OAAO;AAAA,EAEhB,YAAY,SAAiB,SAAwB;AACpD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACb;AACD;AAEO,SAAS,mBAAmB,MAAkD;AACpF,SAAO,OAAO,EAAE,MAAM,YAAY,UAAU,KAAK,SAAS,IAAI,EAAE,MAAM,UAAU;AACjF;",
6
+ "names": []
7
+ }
@@ -3,14 +3,14 @@
3
3
  import {
4
4
  ensureStateDir,
5
5
  lockPath
6
- } from "./chunk-5T442OP3.ts";
6
+ } from "./chunk-JSYMGJQQ.ts";
7
7
  import {
8
8
  LOCKFILE_FS_ADAPTER,
9
9
  LOCK_GUARD_STALE_MS,
10
10
  LOCK_GUARD_UPDATE_MS
11
- } from "./chunk-F5QL2GPY.ts";
11
+ } from "./chunk-ZI5MXMEX.ts";
12
12
 
13
- // src/lock.ts
13
+ // src/state/lock.ts
14
14
  import { randomUUID } from "node:crypto";
15
15
  import fs from "node:fs/promises";
16
16
  import lockfile from "proper-lockfile";
@@ -256,4 +256,4 @@ export {
256
256
  isStaleLock,
257
257
  unlock
258
258
  };
259
- //# sourceMappingURL=chunk-WSOBYHIZ.ts.map
259
+ //# sourceMappingURL=chunk-GWMHAQG4.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/state/lock.ts"],
4
+ "sourcesContent": ["import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport lockfile from \"proper-lockfile\";\nimport type { CommandOptions } from \"../commands/command-types.js\";\nimport { LOCK_GUARD_STALE_MS, LOCK_GUARD_UPDATE_MS } from \"./lock-policy.js\";\nimport { LOCKFILE_FS_ADAPTER } from \"./lockfile-fs.js\";\nimport type { LockFile } from \"./state-types.js\";\nimport { ensureStateDir, lockPath } from \"./sync-state-store.js\";\n\nconst LOCK_STALE_MS = 30 * 60 * 1000;\nconst MAX_PROCESS_ID = 2_147_483_647;\n\ninterface Guard {\n\trelease: () => Promise<void>;\n\tthrowIfCompromised: () => void;\n\tisCompromised: () => boolean;\n}\n\nexport type LockInspection =\n\t| { status: \"missing\" }\n\t| { status: \"unreadable\" }\n\t| { status: \"valid\"; lock: LockFile };\n\nexport async function withLock<T>(\n\tcommand: string,\n\tfn: () => Promise<T>,\n\toptions: { reclaimStale?: boolean } = {},\n): Promise<T> {\n\tawait ensureStateDir();\n\tconst lock: LockFile = {\n\t\tid: randomUUID(),\n\t\tpid: process.pid,\n\t\tcommand,\n\t\tstartedAt: new Date().toISOString(),\n\t};\n\tlet guard: Guard | undefined;\n\tlet result: T | undefined;\n\tlet failed = false;\n\tlet failure: unknown;\n\ttry {\n\t\ttry {\n\t\t\tguard = await acquireGuard();\n\t\t} catch (error) {\n\t\t\tif (!isLockHeldError(error)) throw error;\n\t\t\tthrow await describeHeldLock();\n\t\t}\n\n\t\tlet inspection = await inspectLock();\n\t\tif (inspection.status === \"valid\" && isStaleLock(inspection.lock)) {\n\t\t\tif (!options.reclaimStale) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-sync lock is stale (pid ${inspection.lock.pid}). Run /sync unlock --stale, then retry.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tguard.throwIfCompromised();\n\t\t\tconst rechecked = await inspectLock();\n\t\t\tif (\n\t\t\t\trechecked.status !== \"valid\" ||\n\t\t\t\trechecked.lock.id !== inspection.lock.id ||\n\t\t\t\t!isStaleLock(rechecked.lock)\n\t\t\t) {\n\t\t\t\tthrow new Error(\"pi-sync lock changed while preparing transaction recovery; retry.\");\n\t\t\t}\n\t\t\tawait fs.rm(lockPath(), { force: true });\n\t\t\tinspection = { status: \"missing\" };\n\t\t}\n\t\tif (inspection.status === \"valid\") {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-sync is already running (${inspection.lock.command}, pid ${inspection.lock.pid}, started ${inspection.lock.startedAt}).`,\n\t\t\t);\n\t\t}\n\t\tif (inspection.status === \"unreadable\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"pi-sync lock metadata is unreadable. Run /sync unlock --stale after verifying no sync is running.\",\n\t\t\t);\n\t\t}\n\n\t\tawait fs.writeFile(lockPath(), JSON.stringify(lock, null, \"\\t\"), { flag: \"wx\" });\n\t\tguard.throwIfCompromised();\n\t\tresult = await fn();\n\t\tguard.throwIfCompromised();\n\t} catch (error) {\n\t\tfailed = true;\n\t\tfailure = error;\n\t}\n\n\ttry {\n\t\tconst current = await readLock();\n\t\tif (current?.id === lock.id) await fs.rm(lockPath(), { force: true });\n\t} catch (error) {\n\t\tif (!failed) {\n\t\t\tfailed = true;\n\t\t\tfailure = error;\n\t\t}\n\t}\n\tif (guard) {\n\t\tconst releaseError = await releaseGuard(guard);\n\t\tif (releaseError && !failed) {\n\t\t\tfailed = true;\n\t\t\tfailure = releaseError;\n\t\t}\n\t}\n\tif (failed) throw failure;\n\treturn result as T;\n}\n\nexport async function inspectLock(): Promise<LockInspection> {\n\ttry {\n\t\tconst text = await fs.readFile(lockPath(), \"utf8\");\n\t\tif (text.trim().length === 0) return { status: \"unreadable\" };\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\treturn isLockFile(parsed) ? { status: \"valid\", lock: parsed } : { status: \"unreadable\" };\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { status: \"missing\" };\n\t\tif (error instanceof SyntaxError) return { status: \"unreadable\" };\n\t\tthrow error;\n\t}\n}\n\nexport async function readLock(): Promise<LockFile | undefined> {\n\tconst inspection = await inspectLock();\n\treturn inspection.status === \"valid\" ? inspection.lock : undefined;\n}\n\nexport async function lockFileExists(): Promise<boolean> {\n\ttry {\n\t\tawait fs.stat(lockPath());\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n}\n\nexport function isLockGuardHeld() {\n\treturn lockfile.check(lockPath(), {\n\t\tfs: LOCKFILE_FS_ADAPTER,\n\t\tlockfilePath: `${lockPath()}.guard`,\n\t\trealpath: false,\n\t\tstale: LOCK_GUARD_STALE_MS,\n\t});\n}\n\nexport function isStaleLock(lock: LockFile) {\n\ttry {\n\t\tprocess.kill(lock.pid, 0);\n\t\treturn false;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ESRCH\") return true;\n\t\treturn Date.now() - Date.parse(lock.startedAt) > LOCK_STALE_MS;\n\t}\n}\n\nexport async function unlock(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tthrowIfAborted(options.signal);\n\tawait ensureStateDir();\n\tthrowIfAborted(options.signal);\n\tlet guard: Guard;\n\ttry {\n\t\tguard = await acquireGuard();\n\t} catch (error) {\n\t\tif (!isLockHeldError(error)) throw error;\n\t\tctx.ui.notify((await describeHeldLock()).message, \"warning\");\n\t\treturn;\n\t}\n\n\tlet failed = false;\n\tlet failure: unknown;\n\ttry {\n\t\tawait unlockGuarded(ctx, options);\n\t} catch (error) {\n\t\tfailed = true;\n\t\tfailure = error;\n\t}\n\tconst releaseError = await releaseGuard(guard);\n\tif (releaseError && !failed) {\n\t\tfailed = true;\n\t\tfailure = releaseError;\n\t}\n\tif (failed) throw failure;\n}\n\nasync function unlockGuarded(ctx: ExtensionCommandContext, options: CommandOptions) {\n\tthrowIfAborted(options.signal);\n\tlet inspection = await inspectLock();\n\tthrowIfAborted(options.signal);\n\tif (inspection.status === \"missing\") {\n\t\tctx.ui.notify(\"No pi-sync lock is present.\", \"info\");\n\t\treturn;\n\t}\n\tif (inspection.status === \"unreadable\") {\n\t\tif (!options.stale) {\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Pi-sync lock metadata is unreadable. Use /sync unlock --stale only after verifying no sync is running.\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tinspection = await inspectLock();\n\t\tthrowIfAborted(options.signal);\n\t\tif (inspection.status === \"unreadable\") {\n\t\t\t// Legacy writers expose an empty file before writing owner metadata, so no\n\t\t\t// automatic test can prove this file is abandoned. The explicit --stale\n\t\t\t// flag is the user's confirmation that no legacy sync is still running.\n\t\t\tthrowIfAborted(options.signal);\n\t\t\tawait fs.rm(lockPath(), { force: true });\n\t\t\tif (!options.signal?.aborted) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\"Removed unreadable pi-sync lock. No settings, files, sync state, or remote data were changed.\",\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (inspection.status === \"missing\") {\n\t\t\tctx.ui.notify(\"No pi-sync lock is present.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif (!isStaleLock(inspection.lock)) {\n\t\tctx.ui.notify(\"Lock owner is still live; refusing to remove it.\", \"warning\");\n\t\treturn;\n\t}\n\tthrowIfAborted(options.signal);\n\tawait fs.rm(lockPath(), { force: true });\n\tif (!options.signal?.aborted) {\n\t\tctx.ui.notify(\n\t\t\t\"Removed stale pi-sync lock. No settings, files, sync state, or remote data were changed.\",\n\t\t\t\"info\",\n\t\t);\n\t}\n}\n\nfunction throwIfAborted(signal?: AbortSignal) {\n\tif (!signal?.aborted) return;\n\tthrow signal.reason instanceof Error\n\t\t? signal.reason\n\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n\nasync function releaseGuard(guard: Guard) {\n\ttry {\n\t\tawait guard.release();\n\t\treturn undefined;\n\t} catch (error) {\n\t\treturn guard.isCompromised() ? undefined : error;\n\t}\n}\n\nasync function acquireGuard(): Promise<Guard> {\n\tlet compromisedError: Error | undefined;\n\tconst release = await lockfile.lock(lockPath(), {\n\t\tfs: LOCKFILE_FS_ADAPTER,\n\t\tlockfilePath: `${lockPath()}.guard`,\n\t\trealpath: false,\n\t\tstale: LOCK_GUARD_STALE_MS,\n\t\tupdate: LOCK_GUARD_UPDATE_MS,\n\t\tonCompromised: (error) => {\n\t\t\tcompromisedError = error;\n\t\t},\n\t});\n\treturn {\n\t\trelease,\n\t\tthrowIfCompromised: () => {\n\t\t\tif (compromisedError) throw compromisedError;\n\t\t},\n\t\tisCompromised: () => compromisedError !== undefined,\n\t};\n}\n\nasync function describeHeldLock() {\n\tconst current = await readLock();\n\tif (current && isStaleLock(current)) {\n\t\treturn new Error(\"pi-sync lock owner exited; retry shortly while the lock guard expires.\");\n\t}\n\tif (current) {\n\t\treturn new Error(\n\t\t\t`Pi-sync is currently running (${current.command}, pid ${current.pid}, started ${current.startedAt}).`,\n\t\t);\n\t}\n\treturn new Error(\n\t\t\"Pi-sync is currently running (lock metadata is unreadable or still being written).\",\n\t);\n}\n\nfunction isLockHeldError(error: unknown) {\n\treturn (error as NodeJS.ErrnoException).code === \"ELOCKED\";\n}\n\nfunction isLockFile(value: unknown): value is LockFile {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst lock = value as Partial<LockFile>;\n\treturn (\n\t\ttypeof lock.id === \"string\" &&\n\t\tlock.id.length > 0 &&\n\t\tNumber.isInteger(lock.pid) &&\n\t\t(lock.pid ?? 0) > 0 &&\n\t\t(lock.pid ?? 0) <= MAX_PROCESS_ID &&\n\t\ttypeof lock.command === \"string\" &&\n\t\tlock.command.length > 0 &&\n\t\ttypeof lock.startedAt === \"string\" &&\n\t\tNumber.isFinite(Date.parse(lock.startedAt))\n\t);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -2,36 +2,21 @@
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
3
  import {
4
4
  safeTerminalText
5
- } from "./chunk-C7LXZ3UH.ts";
5
+ } from "./chunk-LTBWGSBV.ts";
6
6
  import {
7
7
  syncConfigReviewFingerprint
8
- } from "./chunk-5T442OP3.ts";
8
+ } from "./chunk-C7XY5NF5.ts";
9
+ import {
10
+ filterSnapshotForConfigPolicy
11
+ } from "./chunk-OEX4ABY6.ts";
9
12
  import {
10
13
  inspectRemoteSelection,
11
14
  remoteSelectionMismatch,
12
15
  sameSyncInclude,
13
16
  snapshotSelectionInclude
14
- } from "./chunk-YQ6UW7IF.ts";
17
+ } from "./chunk-JAAKLL6M.ts";
15
18
 
16
- // src/backend-factory.ts
17
- var createSyncBackend = async (config) => {
18
- switch (config.backend.type) {
19
- case "s3": {
20
- const { S3SyncBackend } = await import("./s3-backend-552LXHE4.ts");
21
- return new S3SyncBackend(config.backend);
22
- }
23
- case "webdav": {
24
- const { WebDavSyncBackend } = await import("./webdav-backend-ZD5RHRIU.ts");
25
- return new WebDavSyncBackend(config.backend);
26
- }
27
- case "git": {
28
- const { GitSyncBackend } = await import("./git-backend-E3UOUUOM.ts");
29
- return new GitSyncBackend(config.backend);
30
- }
31
- }
32
- };
33
-
34
- // src/remote-snapshot.ts
19
+ // src/sync/remote-snapshot.ts
35
20
  async function readSnapshotForHead(backend, head, signal) {
36
21
  const snapshot = await backend.readSnapshot(head.snapshotRef, signal);
37
22
  if (signal?.aborted) {
@@ -71,11 +56,26 @@ function formatRemoteSelectionStatus(state) {
71
56
  function safeList(values) {
72
57
  return values.length > 0 ? values.map(safeTerminalText).join(", ") : "none";
73
58
  }
59
+ async function readRemoteSnapshot(backend, config, signal, options = {}) {
60
+ const head = await backend.readHead(signal);
61
+ if (!head) return { head: void 0, snapshot: void 0, selectionState: void 0 };
62
+ const snapshot = await readSnapshotForHead(backend, head, signal);
63
+ const selectionState = inspectRemoteSelection(config.include, snapshot);
64
+ if (!options.allowSelectionDifference && selectionState.kind === "different") {
65
+ const configIdentity = syncConfigReviewFingerprint(config);
66
+ throw remoteSelectionMismatch(config, selectionState.include, configIdentity);
67
+ }
68
+ return {
69
+ head,
70
+ snapshot: filterSnapshotForConfigPolicy(snapshot, config),
71
+ selectionState
72
+ };
73
+ }
74
74
 
75
75
  export {
76
- createSyncBackend,
77
76
  readSnapshotForHead,
78
77
  requireCompatibleRemoteSelection,
79
- formatRemoteSelectionStatus
78
+ formatRemoteSelectionStatus,
79
+ readRemoteSnapshot
80
80
  };
81
- //# sourceMappingURL=chunk-5LZALPCD.ts.map
81
+ //# sourceMappingURL=chunk-HUDQATCL.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/remote-snapshot.ts"],
4
+ "sourcesContent": ["import type { RemoteHead, SyncBackend } from \"../backends/sync-backend.js\";\nimport { syncConfigReviewFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { filterSnapshotForConfigPolicy } from \"../snapshot/snapshot.js\";\nimport type { Snapshot } from \"../snapshot/snapshot-types.js\";\nimport { safeTerminalText } from \"../ui/terminal-text.js\";\nimport {\n\tinspectRemoteSelection,\n\ttype RemoteSelectionState,\n\tremoteSelectionMismatch,\n\tsameSyncInclude,\n\tsnapshotSelectionInclude,\n} from \"./sync-policy.js\";\n\nexport async function readSnapshotForHead(\n\tbackend: SyncBackend,\n\thead: RemoteHead,\n\tsignal?: AbortSignal,\n) {\n\tconst snapshot = await backend.readSnapshot(head.snapshotRef, signal);\n\tif (signal?.aborted) {\n\t\tthrow signal.reason instanceof Error\n\t\t\t? signal.reason\n\t\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n\t}\n\tif (snapshot.id !== head.snapshotId) {\n\t\tthrow new Error(\n\t\t\t`Remote head ${head.snapshotId} resolved to unexpected snapshot ${snapshot.id}.`,\n\t\t);\n\t}\n\tif (head.selection) {\n\t\tconst snapshotInclude = snapshotSelectionInclude(snapshot);\n\t\tif (!snapshotInclude || !sameSyncInclude(head.selection.include, snapshotInclude)) {\n\t\t\tthrow new Error(\"Remote head selection does not match its immutable snapshot.\");\n\t\t}\n\t}\n\treturn snapshot;\n}\n\nexport function requireCompatibleRemoteSelection(config: AnySyncConfig, snapshot: Snapshot) {\n\tconst state = inspectRemoteSelection(config.include, snapshot);\n\tif (state.kind === \"different\") {\n\t\tthrow remoteSelectionMismatch(config, state.include, syncConfigReviewFingerprint(config));\n\t}\n}\n\nexport function formatRemoteSelectionStatus(state: RemoteSelectionState | undefined) {\n\tif (!state) return \"remote included content: unavailable (remote is empty)\";\n\tif (state.kind === \"same\") return \"remote included content: matches this setup\";\n\tif (state.kind === \"legacy\") {\n\t\treturn `remote included content: unavailable in legacy snapshot (${state.discovered.length} path${state.discovered.length === 1 ? \"\" : \"s\"} discovered, partial)`;\n\t}\n\treturn [\n\t\t\"remote included content: differs from this setup\",\n\t\t`remote-only selection: ${safeList(state.remoteOnly)}`,\n\t\t`local-only selection: ${safeList(state.localOnly)}`,\n\t].join(\"\\n\");\n}\n\nfunction safeList(values: readonly string[]) {\n\treturn values.length > 0 ? values.map(safeTerminalText).join(\", \") : \"none\";\n}\n\nexport async function readRemoteSnapshot(\n\tbackend: SyncBackend,\n\tconfig: AnySyncConfig,\n\tsignal?: AbortSignal,\n\toptions: { allowSelectionDifference?: boolean } = {},\n) {\n\tconst head = await backend.readHead(signal);\n\tif (!head) return { head: undefined, snapshot: undefined, selectionState: undefined };\n\tconst snapshot = await readSnapshotForHead(backend, head, signal);\n\tconst selectionState = inspectRemoteSelection(config.include, snapshot);\n\tif (!options.allowSelectionDifference && selectionState.kind === \"different\") {\n\t\tconst configIdentity = syncConfigReviewFingerprint(config);\n\t\tthrow remoteSelectionMismatch(config, selectionState.include, configIdentity);\n\t}\n\treturn {\n\t\thead,\n\t\tsnapshot: filterSnapshotForConfigPolicy(snapshot, config),\n\t\tselectionState,\n\t};\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AAcA,eAAsB,oBACrB,SACA,MACA,QACC;AACD,QAAM,WAAW,MAAM,QAAQ,aAAa,KAAK,aAAa,MAAM;AACpE,MAAI,QAAQ,SAAS;AACpB,UAAM,OAAO,kBAAkB,QAC5B,OAAO,SACP,IAAI,aAAa,6BAA6B,YAAY;AAAA,EAC9D;AACA,MAAI,SAAS,OAAO,KAAK,YAAY;AACpC,UAAM,IAAI;AAAA,MACT,eAAe,KAAK,UAAU,oCAAoC,SAAS,EAAE;AAAA,IAC9E;AAAA,EACD;AACA,MAAI,KAAK,WAAW;AACnB,UAAM,kBAAkB,yBAAyB,QAAQ;AACzD,QAAI,CAAC,mBAAmB,CAAC,gBAAgB,KAAK,UAAU,SAAS,eAAe,GAAG;AAClF,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAC/E;AAAA,EACD;AACA,SAAO;AACR;AAEO,SAAS,iCAAiC,QAAuB,UAAoB;AAC3F,QAAM,QAAQ,uBAAuB,OAAO,SAAS,QAAQ;AAC7D,MAAI,MAAM,SAAS,aAAa;AAC/B,UAAM,wBAAwB,QAAQ,MAAM,SAAS,4BAA4B,MAAM,CAAC;AAAA,EACzF;AACD;AAEO,SAAS,4BAA4B,OAAyC;AACpF,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,SAAS,OAAQ,QAAO;AAClC,MAAI,MAAM,SAAS,UAAU;AAC5B,WAAO,4DAA4D,MAAM,WAAW,MAAM,QAAQ,MAAM,WAAW,WAAW,IAAI,KAAK,GAAG;AAAA,EAC3I;AACA,SAAO;AAAA,IACN;AAAA,IACA,0BAA0B,SAAS,MAAM,UAAU,CAAC;AAAA,IACpD,yBAAyB,SAAS,MAAM,SAAS,CAAC;AAAA,EACnD,EAAE,KAAK,IAAI;AACZ;AAEA,SAAS,SAAS,QAA2B;AAC5C,SAAO,OAAO,SAAS,IAAI,OAAO,IAAI,gBAAgB,EAAE,KAAK,IAAI,IAAI;AACtE;AAEA,eAAsB,mBACrB,SACA,QACA,QACA,UAAkD,CAAC,GAClD;AACD,QAAM,OAAO,MAAM,QAAQ,SAAS,MAAM;AAC1C,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,QAAW,UAAU,QAAW,gBAAgB,OAAU;AACpF,QAAM,WAAW,MAAM,oBAAoB,SAAS,MAAM,MAAM;AAChE,QAAM,iBAAiB,uBAAuB,OAAO,SAAS,QAAQ;AACtE,MAAI,CAAC,QAAQ,4BAA4B,eAAe,SAAS,aAAa;AAC7E,UAAM,iBAAiB,4BAA4B,MAAM;AACzD,UAAM,wBAAwB,QAAQ,eAAe,SAAS,cAAc;AAAA,EAC7E;AACA,SAAO;AAAA,IACN;AAAA,IACA,UAAU,8BAA8B,UAAU,MAAM;AAAA,IACxD;AAAA,EACD;AACD;",
6
+ "names": []
7
+ }
@@ -48,7 +48,7 @@ function trimSlashes(value) {
48
48
  return value.replace(/^\/+|\/+$/g, "");
49
49
  }
50
50
 
51
- // src/sync-policy.ts
51
+ // src/sync/sync-policy.ts
52
52
  import path2 from "node:path";
53
53
  var BUILT_IN_SYNC_ROOTS = [
54
54
  "settings.json",
@@ -391,4 +391,4 @@ export {
391
391
  normalizeSyncFiles,
392
392
  normalizeExtraFiles
393
393
  };
394
- //# sourceMappingURL=chunk-YQ6UW7IF.ts.map
394
+ //# sourceMappingURL=chunk-JAAKLL6M.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/paths.ts", "../../src/sync/sync-policy.ts"],
4
+ "sourcesContent": ["import path from \"node:path\";\n\nexport function isDeniedPath(relativePath: string) {\n\tconst normalized = toPosix(relativePath);\n\tconst lower = normalized.toLowerCase();\n\tconst segments = lower.split(\"/\");\n\tconst base = path.posix.basename(lower);\n\treturn (\n\t\tsegments.includes(\"node_modules\") ||\n\t\tsegments.includes(\".git\") ||\n\t\tsegments.includes(\".pisync\") ||\n\t\tsegments.includes(\"pi-sync\") ||\n\t\tsegments.includes(\".pi-sync-state-migration.lock\") ||\n\t\tbase === \".env\" ||\n\t\tbase.startsWith(\".env.\") ||\n\t\tbase.endsWith(\".env\") ||\n\t\tbase.includes(\"secret\") ||\n\t\tbase.includes(\"token\") ||\n\t\tbase === \"pi-sync.json\" ||\n\t\tbase.startsWith(\"pi-sync.json.\") ||\n\t\tbase.startsWith(\".pi-sync.json.\") ||\n\t\tbase === \"pi-sync.local.json\" ||\n\t\tbase.startsWith(\"pi-sync.local.json.\") ||\n\t\tbase.startsWith(\".pi-sync.local.json.\")\n\t);\n}\n\nexport function isPathInside(parent: string, child: string) {\n\tconst relative = path.relative(path.resolve(parent), path.resolve(child));\n\treturn relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n\nexport function safeJoin(root: string, relativePath: string) {\n\tconst target = path.resolve(root, relativePath);\n\tassertWithinRoot(root, target, relativePath);\n\treturn target;\n}\n\nexport function assertWithinRoot(root: string, target: string, label = target) {\n\tconst resolvedRoot = path.resolve(root);\n\tconst resolvedTarget = path.resolve(target);\n\tif (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`)) {\n\t\tthrow new Error(`Unsafe path in snapshot: ${label}`);\n\t}\n}\n\nexport function encodeKey(key: string) {\n\treturn key.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\nexport function posixJoin(...parts: string[]) {\n\treturn parts\n\t\t.map((part) => trimSlashes(part))\n\t\t.filter(Boolean)\n\t\t.join(\"/\");\n}\n\nexport function parentPaths(relativePath: string) {\n\tconst results: string[] = [];\n\tlet index = relativePath.lastIndexOf(\"/\");\n\twhile (index > 0) {\n\t\tresults.push(relativePath.slice(0, index));\n\t\tindex = relativePath.lastIndexOf(\"/\", index - 1);\n\t}\n\treturn results;\n}\n\nexport function toPosix(value: string) {\n\treturn value.split(path.sep).join(\"/\");\n}\n\nfunction trimSlashes(value: string) {\n\treturn value.replace(/^\\/+|\\/+$/g, \"\");\n}\n\nexport function safeName(value: string) {\n\treturn value.replace(/[^A-Za-z0-9._-]/g, \"_\");\n}\n", "import path from \"node:path\";\nimport { isDeniedPath, toPosix } from \"../paths.js\";\nimport type { Snapshot, SnapshotSelection } from \"../snapshot/snapshot-types.js\";\n\nexport const BUILT_IN_SYNC_ROOTS = [\n\t\"settings.json\",\n\t\"keybindings.json\",\n\t\"models.json\",\n\t\"AGENTS.md\",\n\t\"APPEND_SYSTEM.md\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"extensions\",\n] as const;\n\nexport const DEFAULT_SYNC_INCLUDE = [...BUILT_IN_SYNC_ROOTS] as const;\nexport type BuiltInSyncFile = (typeof BUILT_IN_SYNC_ROOTS)[number];\nconst SNAPSHOT_SELECTION_VERSION = 1;\nconst MAX_SYNC_INCLUDE_ITEMS = 1_024;\nconst MAX_SYNC_INCLUDE_PATH_BYTES = 4_096;\nconst MAX_SYNC_INCLUDE_TOTAL_BYTES = 256 * 1_024;\n\ninterface IncludePathNode {\n\tselected?: string;\n\tchildren: Map<string, IncludePathNode>;\n}\n\nconst BUILT_IN_BY_LOWER = new Map<string, BuiltInSyncFile>(\n\tBUILT_IN_SYNC_ROOTS.map((fileName) => [fileName.toLowerCase(), fileName]),\n);\nconst TOP_LEVEL_FILE_PATHS = new Map<string, string>(\n\tBUILT_IN_SYNC_ROOTS.filter((fileName) => fileName.includes(\".\")).map((fileName) => [\n\t\tfileName.toLowerCase(),\n\t\tfileName,\n\t]),\n);\nconst TOP_LEVEL_DIRS = new Set<string>(\n\tBUILT_IN_SYNC_ROOTS.filter((fileName) => !fileName.includes(\".\")),\n);\nconst RESERVED_TOP_LEVEL_NAMES = new Set<string>([...BUILT_IN_BY_LOWER.keys(), \"sessions\"]);\n\nexport function normalizeSyncInclude(value: unknown): string[] {\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(\"Invalid pi-sync settings: sync.include must be an array.\");\n\t}\n\tif (value.length > MAX_SYNC_INCLUDE_ITEMS) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync.include has too many items; limit: ${MAX_SYNC_INCLUDE_ITEMS}.`,\n\t\t);\n\t}\n\tconst result: string[] = [];\n\tconst seen = new Set<string>();\n\tconst pathRoot: IncludePathNode = { children: new Map() };\n\tlet totalBytes = 0;\n\tfor (const item of value) {\n\t\tif (typeof item !== \"string\") {\n\t\t\tthrow new Error(\"Invalid pi-sync settings: sync.include items must be strings.\");\n\t\t}\n\t\tconst itemBytes = Buffer.byteLength(item, \"utf8\");\n\t\tif (itemBytes > MAX_SYNC_INCLUDE_PATH_BYTES) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid pi-sync settings: sync.include item is too long; limit: ${MAX_SYNC_INCLUDE_PATH_BYTES} bytes.`,\n\t\t\t);\n\t\t}\n\t\ttotalBytes += itemBytes;\n\t\tif (totalBytes > MAX_SYNC_INCLUDE_TOTAL_BYTES) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid pi-sync settings: sync.include is too large; limit: ${MAX_SYNC_INCLUDE_TOTAL_BYTES} bytes.`,\n\t\t\t);\n\t\t}\n\t\tconst trimmed = item.trim();\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(trimmed.toLowerCase());\n\t\tconst normalized = builtIn ?? (trimmed.toLowerCase() === \"sessions\" ? \"sessions\" : trimmed);\n\t\tif (!builtIn && normalized !== \"sessions\") validateAgentRelativeInclude(normalized);\n\t\tconst identity = normalized.toLowerCase();\n\t\tif (seen.has(identity)) {\n\t\t\tthrow new Error(`Invalid pi-sync settings: duplicate sync.include item: ${item}`);\n\t\t}\n\t\taddIncludePath(pathRoot, identity, item);\n\t\tseen.add(identity);\n\t\tresult.push(normalized);\n\t}\n\treturn result;\n}\n\nfunction addIncludePath(root: IncludePathNode, identity: string, source: string) {\n\tlet node = root;\n\tfor (const segment of identity.split(\"/\")) {\n\t\tif (node.selected !== undefined) throwOverlappingInclude(source);\n\t\tlet child = node.children.get(segment);\n\t\tif (!child) {\n\t\t\tchild = { children: new Map() };\n\t\t\tnode.children.set(segment, child);\n\t\t}\n\t\tnode = child;\n\t}\n\tif (node.children.size > 0) throwOverlappingInclude(source);\n\tnode.selected = identity;\n}\n\nfunction throwOverlappingInclude(item: string): never {\n\tthrow new Error(\n\t\t`Invalid pi-sync settings: overlapping sync.include items are ambiguous: ${item}`,\n\t);\n}\n\nfunction validateAgentRelativeInclude(value: string) {\n\tconst normalized = toPosix(value);\n\tconst topLevel = normalized.split(\"/\")[0]?.toLowerCase();\n\tif (\n\t\t!normalized ||\n\t\tnormalized === \".\" ||\n\t\tnormalized === \"..\" ||\n\t\tnormalized.startsWith(\"../\") ||\n\t\tpath.posix.isAbsolute(normalized) ||\n\t\tnormalized.includes(\"\\\\\") ||\n\t\tpath.posix.normalize(normalized) !== normalized ||\n\t\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Include paths cannot contain controls.\n\t\t/[\\u0000-\\u001f\\u007f-\\u009f]/u.test(normalized)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync.include item must be a safe agent-relative path: ${value}`,\n\t\t);\n\t}\n\tif (isDeniedPath(normalized)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${value} cannot be synced.`);\n\t}\n\tif (topLevel && RESERVED_TOP_LEVEL_NAMES.has(topLevel)) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: use the canonical ${topLevel} root instead of a nested sync.include path.`,\n\t\t);\n\t}\n}\n\nexport function portableSnapshotSelection(value: unknown): SnapshotSelection {\n\tconst selection = value as Partial<SnapshotSelection> | null;\n\tif (\n\t\t!selection ||\n\t\ttypeof selection !== \"object\" ||\n\t\tArray.isArray(selection) ||\n\t\tselection.version !== SNAPSHOT_SELECTION_VERSION ||\n\t\t!Object.hasOwn(selection, \"include\") ||\n\t\tObject.keys(selection).some((key) => key !== \"version\" && key !== \"include\")\n\t) {\n\t\tthrow new Error(\"Invalid snapshot selection policy.\");\n\t}\n\treturn {\n\t\tversion: SNAPSHOT_SELECTION_VERSION,\n\t\tinclude: normalizeSyncInclude(selection.include),\n\t};\n}\n\nexport function snapshotSelectionInclude(snapshot: Pick<Snapshot, \"selection\">) {\n\treturn snapshot.selection === undefined\n\t\t? undefined\n\t\t: portableSnapshotSelection(snapshot.selection).include;\n}\n\nexport function selectionForSnapshot(include: unknown): SnapshotSelection {\n\treturn { version: SNAPSHOT_SELECTION_VERSION, include: normalizeSyncInclude(include) };\n}\n\nexport function sameSyncInclude(left: unknown, right: unknown) {\n\tconst normalizedLeft = normalizeSyncInclude(left);\n\tconst normalizedRight = normalizeSyncInclude(right);\n\treturn (\n\t\tnormalizedLeft.length === normalizedRight.length &&\n\t\tnormalizedLeft.every((item, index) => item === normalizedRight[index])\n\t);\n}\n\nexport function compareSyncInclude(local: unknown, remote: unknown) {\n\tconst localInclude = normalizeSyncInclude(local);\n\tconst remoteInclude = normalizeSyncInclude(remote);\n\tconst localSet = new Set(localInclude);\n\tconst remoteSet = new Set(remoteInclude);\n\treturn {\n\t\tsame: sameSyncInclude(localInclude, remoteInclude),\n\t\tremoteOnly: remoteInclude.filter((item) => !localSet.has(item)),\n\t\tlocalOnly: localInclude.filter((item) => !remoteSet.has(item)),\n\t};\n}\n\nexport type RemoteSelectionState =\n\t| { kind: \"legacy\"; discovered: string[] }\n\t| { kind: \"same\"; include: string[] }\n\t| {\n\t\t\tkind: \"different\";\n\t\t\tinclude: string[];\n\t\t\tremoteOnly: string[];\n\t\t\tlocalOnly: string[];\n\t };\n\nexport function inspectRemoteSelection(\n\tlocalInclude: unknown,\n\tsnapshot: Pick<Snapshot, \"selection\" | \"files\">,\n): RemoteSelectionState {\n\tconst remoteInclude = snapshotSelectionInclude(snapshot);\n\tif (!remoteInclude) {\n\t\treturn { kind: \"legacy\", discovered: discoverLegacySnapshotInclude(snapshot) };\n\t}\n\tconst comparison = compareSyncInclude(localInclude, remoteInclude);\n\treturn comparison.same\n\t\t? { kind: \"same\", include: remoteInclude }\n\t\t: { kind: \"different\", include: remoteInclude, ...comparison };\n}\n\nexport interface RemoteSelectionDecision {\n\tsetupName: string;\n\tconfigIdentity: string;\n\tlocalInclude: readonly string[];\n\tremoteInclude: readonly string[];\n}\n\nexport class RemoteSelectionMismatchError extends Error {\n\treadonly decision: RemoteSelectionDecision;\n\treadonly setupName: string;\n\treadonly localInclude: string[];\n\treadonly remoteInclude: string[];\n\n\tconstructor(\n\t\tsetupName: string,\n\t\tlocalInclude: unknown,\n\t\tremoteInclude: unknown,\n\t\tconfigIdentity = JSON.stringify([setupName, normalizeSyncInclude(localInclude)]),\n\t) {\n\t\tconst local = normalizeSyncInclude(localInclude);\n\t\tconst remote = normalizeSyncInclude(remoteInclude);\n\t\tsuper(formatRemoteSelectionMismatch(setupName, local, remote));\n\t\tthis.name = \"RemoteSelectionMismatchError\";\n\t\tthis.setupName = setupName;\n\t\tthis.localInclude = local;\n\t\tthis.remoteInclude = remote;\n\t\tthis.decision = {\n\t\t\tsetupName,\n\t\t\tconfigIdentity,\n\t\t\tlocalInclude: [...local],\n\t\t\tremoteInclude: [...remote],\n\t\t};\n\t}\n}\n\nexport function remoteSelectionMismatch(\n\tconfig: { setupName: string; include: unknown },\n\tremoteInclude: unknown,\n\tconfigIdentity?: string,\n) {\n\treturn new RemoteSelectionMismatchError(\n\t\tconfig.setupName,\n\t\tconfig.include,\n\t\tremoteInclude,\n\t\tconfigIdentity,\n\t);\n}\n\nexport function formatRemoteSelectionMismatch(\n\tsetupName: string,\n\tlocalInclude: readonly string[],\n\tremoteInclude: readonly string[],\n) {\n\tconst comparison = compareSyncInclude(localInclude, remoteInclude);\n\tconst lines = [\n\t\t`Synced content differs for sync setup \u201C${stripTerminalControls(setupName)}\u201D.`,\n\t\t`Remote-only: ${comparison.remoteOnly.join(\", \") || \"none\"}`,\n\t\t`This-device-only: ${comparison.localOnly.join(\", \") || \"none\"}`,\n\t];\n\tif (comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0) {\n\t\tlines.push(\n\t\t\t\"Only ordering differs.\",\n\t\t\t`Remote order: ${remoteInclude.join(\", \") || \"none\"}`,\n\t\t\t`This device order: ${localInclude.join(\", \") || \"none\"}`,\n\t\t);\n\t}\n\tlines.push(\"Run /sync in TUI to review both content lists and choose what happens next.\");\n\treturn lines.join(\"\\n\");\n}\n\nfunction stripTerminalControls(value: string) {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n\nexport function discoverLegacySnapshotInclude(snapshot: Pick<Snapshot, \"files\">) {\n\tconst builtIns = new Set<BuiltInSyncFile>();\n\tconst custom = new Set<string>();\n\tlet sessions = false;\n\tfor (const file of snapshot.files) {\n\t\tconst normalized = toPosix(file.path);\n\t\tif (\n\t\t\t!normalized ||\n\t\t\tfile.path.includes(\"\\\\\") ||\n\t\t\tnormalized.length > 4096 ||\n\t\t\tnormalized.startsWith(\"../\") ||\n\t\t\tpath.posix.isAbsolute(normalized) ||\n\t\t\tpath.posix.normalize(normalized) !== normalized ||\n\t\t\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Ignore unsafe legacy paths.\n\t\t\t/[\\u0000-\\u001f\\u007f-\\u009f]/u.test(normalized) ||\n\t\t\tisDeniedPath(normalized)\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst [topLevel, ...rest] = normalized.split(\"/\");\n\t\tif (!topLevel) continue;\n\t\tif (topLevel === \"sessions\" && rest.length > 0) {\n\t\t\tsessions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(topLevel.toLowerCase());\n\t\tif (\n\t\t\tbuiltIn &&\n\t\t\t((TOP_LEVEL_DIRS.has(builtIn) && rest.length > 0) ||\n\t\t\t\t(!TOP_LEVEL_DIRS.has(builtIn) && rest.length === 0))\n\t\t) {\n\t\t\tbuiltIns.add(builtIn);\n\t\t\tcontinue;\n\t\t}\n\t\tif (isSafeCustomIncludePath(topLevel)) custom.add(topLevel);\n\t}\n\treturn [\n\t\t...BUILT_IN_SYNC_ROOTS.filter((item) => builtIns.has(item)),\n\t\t...[...custom].sort((left, right) => left.localeCompare(right)),\n\t\t...(sessions ? [\"sessions\"] : []),\n\t];\n}\n\nexport function syncIncludeSelection(value: unknown) {\n\tconst include = normalizeSyncInclude(value);\n\tconst builtIns = include.filter((item): item is BuiltInSyncFile =>\n\t\tBUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t);\n\tconst custom = include.filter(\n\t\t(item) => item !== \"sessions\" && !BUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t);\n\treturn { include, builtIns, custom, sessions: include.includes(\"sessions\") };\n}\n\nexport function customIncludePathsByLower(value: unknown) {\n\treturn new Map(\n\t\tsyncIncludeSelection(value).custom.map((relativePath) => [\n\t\t\trelativePath.toLowerCase(),\n\t\t\trelativePath,\n\t\t]),\n\t);\n}\n\nexport interface SyncSelectionConfig {\n\tinclude?: unknown;\n\tsyncFiles?: unknown;\n\tsyncSessions?: boolean;\n\textraFiles?: unknown;\n}\n\nexport function includeFromSelectionConfig(config: SyncSelectionConfig) {\n\tif (config.include !== undefined) return normalizeSyncInclude(config.include);\n\treturn [\n\t\t...normalizeSyncFiles(config.syncFiles),\n\t\t...normalizeExtraFiles(config.extraFiles),\n\t\t...(config.syncSessions ? [\"sessions\"] : []),\n\t];\n}\n\nexport function isConfiguredSnapshotPath(\n\trelativePath: string,\n\tconfig: SyncSelectionConfig,\n\t_legacyExtraFiles?: Set<string>,\n) {\n\tconst normalized = toPosix(relativePath);\n\tconst selection = syncIncludeSelection(includeFromSelectionConfig(config));\n\tif (normalized.startsWith(\"sessions/\")) return selection.sessions;\n\tconst lower = normalized.toLowerCase();\n\tif (!normalized.includes(\"/\")) {\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(lower);\n\t\tif (builtIn) return selection.builtIns.includes(builtIn) && !TOP_LEVEL_DIRS.has(builtIn);\n\t}\n\tconst topLevel = normalized.slice(0, normalized.indexOf(\"/\"));\n\tif (selection.builtIns.includes(topLevel as BuiltInSyncFile) && TOP_LEVEL_DIRS.has(topLevel)) {\n\t\treturn true;\n\t}\n\treturn selection.custom.some((candidate) => {\n\t\tconst candidateLower = candidate.toLowerCase();\n\t\treturn lower === candidateLower || lower.startsWith(`${candidateLower}/`);\n\t});\n}\n\nexport function canonicalSnapshotPathForConfig(\n\trelativePath: string,\n\tincludePaths: Map<string, string>,\n) {\n\tconst normalized = toPosix(relativePath);\n\tconst lower = normalized.toLowerCase();\n\treturn TOP_LEVEL_FILE_PATHS.get(lower) ?? includePaths.get(lower) ?? normalized;\n}\n\nexport function isPreservableUnmanagedSnapshotPath(relativePath: string) {\n\tconst normalized = toPosix(relativePath);\n\tif (!normalized || isDeniedPath(normalized)) return false;\n\tif (normalized.startsWith(\"sessions/\")) return normalized.endsWith(\".jsonl\");\n\tif (!normalized.includes(\"/\")) {\n\t\tconst lower = normalized.toLowerCase();\n\t\treturn TOP_LEVEL_FILE_PATHS.has(lower) || !RESERVED_TOP_LEVEL_NAMES.has(lower);\n\t}\n\treturn true;\n}\n\nexport function isSafeCustomIncludePath(relativePath: string) {\n\ttry {\n\t\tvalidateAgentRelativeInclude(relativePath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function isBuiltInTopLevelFile(fileName: string) {\n\treturn TOP_LEVEL_FILE_PATHS.has(path.posix.basename(fileName).toLowerCase());\n}\n\n/** Compatibility projection for backend-neutral output while callers move to sync.include. */\nexport function normalizeSyncFiles(value: unknown): BuiltInSyncFile[] {\n\tif (value === undefined) return [...DEFAULT_SYNC_INCLUDE];\n\tif (Array.isArray(value)) {\n\t\treturn normalizeSyncInclude(value).filter((item): item is BuiltInSyncFile =>\n\t\t\tBUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t\t);\n\t}\n\tthrow new Error(\"Invalid pi-sync settings: expected an include array.\");\n}\n\n/** Compatibility projection for old rendering helpers; v3 persistence never writes this split field. */\nexport function normalizeExtraFiles(value: unknown) {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter(\n\t\t(item): item is string => typeof item === \"string\" && isSafeCustomIncludePath(item),\n\t);\n}\n\nexport const extraFilePathsByLower = customIncludePathsByLower;\nexport const isSafeExtraFileName = isSafeCustomIncludePath;\nexport const selectedSyncFileSet = (value: unknown) => new Set(normalizeSyncFiles(value));\n"],
5
+ "mappings": "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",
6
+ "names": ["path", "path"]
7
+ }