@narumitw/pi-sync 0.49.15 → 0.50.1

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 (213) hide show
  1. package/README.md +49 -16
  2. package/dist/chunks/attention-widget-Z7G3SYYJ.ts +9 -0
  3. package/dist/chunks/attention-widget-Z7G3SYYJ.ts.map +7 -0
  4. package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
  5. package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
  6. package/dist/chunks/{chunk-ARN2672F.ts → chunk-4CHTZMEO.ts} +23 -55
  7. package/dist/chunks/chunk-4CHTZMEO.ts.map +7 -0
  8. package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
  9. package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
  10. package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
  11. package/dist/chunks/chunk-AUSDLD6F.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-CCSN3F47.ts +51 -0
  15. package/dist/chunks/chunk-CCSN3F47.ts.map +7 -0
  16. package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
  17. package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
  18. package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
  19. package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
  20. package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
  21. package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
  22. package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-ETTRJDNP.ts} +4 -4
  23. package/dist/chunks/chunk-ETTRJDNP.ts.map +7 -0
  24. package/dist/chunks/{chunk-44LPGQTI.ts → chunk-G4RNQQZS.ts} +9 -7
  25. package/dist/chunks/chunk-G4RNQQZS.ts.map +7 -0
  26. package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
  27. package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
  28. package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-IJ6FJFA5.ts} +9 -9
  29. package/dist/chunks/chunk-IJ6FJFA5.ts.map +7 -0
  30. package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
  31. package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
  32. package/dist/chunks/chunk-KW7YQEZN.ts +1255 -0
  33. package/dist/chunks/chunk-KW7YQEZN.ts.map +7 -0
  34. package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
  35. package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
  36. package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
  37. package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
  38. package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
  39. package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
  40. package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
  41. package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
  42. package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
  43. package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
  44. package/dist/chunks/chunk-S7YBZR42.ts +146 -0
  45. package/dist/chunks/chunk-S7YBZR42.ts.map +7 -0
  46. package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
  47. package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
  48. package/dist/chunks/chunk-SZWBGPID.ts +57 -0
  49. package/dist/chunks/chunk-SZWBGPID.ts.map +7 -0
  50. package/dist/chunks/chunk-T2B5IU74.ts +25 -0
  51. package/dist/chunks/chunk-T2B5IU74.ts.map +7 -0
  52. package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
  53. package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
  54. package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
  55. package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
  56. package/dist/chunks/chunk-THMF4BVL.ts +18 -0
  57. package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
  58. package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
  59. package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
  60. package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
  61. package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
  62. package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
  63. package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
  64. package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-GMVS4PD4.ts} +39 -14
  65. package/dist/chunks/git-backend-GMVS4PD4.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-FKIBM3GO.ts +22 -0
  67. package/dist/chunks/manager-ui-R3XPUFFS.ts +1680 -0
  68. package/dist/chunks/manager-ui-R3XPUFFS.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-D7RBRJNL.ts +21 -0
  70. package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-66O4FGBH.ts} +94 -16
  71. package/dist/chunks/s3-backend-66O4FGBH.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
  73. package/dist/chunks/setup-wizard-IUB6ESEU.ts +89 -0
  74. package/dist/chunks/setup-wizard-IUB6ESEU.ts.map +7 -0
  75. package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
  76. package/dist/chunks/sync-inspection-CST2KL65.ts +20 -0
  77. package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-LBX3GKCM.ts} +369 -289
  78. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +7 -0
  79. package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
  80. package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
  81. package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
  82. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
  83. package/dist/index.ts +476 -341
  84. package/dist/index.ts.map +4 -4
  85. package/docs/settings.md +30 -6
  86. package/package.json +1 -1
  87. package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
  88. package/src/backends/backend-identity.ts +40 -0
  89. package/src/backends/backend-types.ts +81 -0
  90. package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
  91. package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
  92. package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
  93. package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
  94. package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
  95. package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
  96. package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
  97. package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
  98. package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
  99. package/src/commands/command-execution.ts +330 -0
  100. package/src/commands/command-handler.ts +201 -0
  101. package/src/commands/command-types.ts +18 -0
  102. package/src/{command.ts → commands/command.ts} +2 -1
  103. package/src/commands/setup-completions.ts +6 -0
  104. package/src/settings/config.ts +238 -0
  105. package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
  106. package/src/settings/settings-store.ts +109 -0
  107. package/src/settings/settings-types.ts +128 -0
  108. package/src/settings/settings-validation.ts +443 -0
  109. package/src/snapshot/session-paths.ts +84 -0
  110. package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
  111. package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
  112. package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
  113. package/src/snapshot/snapshot-types.ts +38 -0
  114. package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
  115. package/src/state/json-file.ts +20 -0
  116. package/src/{lock.ts → state/lock.ts} +3 -2
  117. package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
  118. package/src/state/state-types.ts +20 -0
  119. package/src/state/sync-state-store.ts +62 -0
  120. package/src/sync/automatic-sync.ts +103 -0
  121. package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
  122. package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
  123. package/src/sync/signals.ts +10 -0
  124. package/src/sync/startup-check.ts +128 -0
  125. package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
  126. package/src/sync/sync-error-guidance.ts +53 -0
  127. package/src/sync/sync-inspection.ts +51 -0
  128. package/src/sync/sync-loaders.ts +54 -0
  129. package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
  130. package/src/sync/sync-operations.ts +12 -0
  131. package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
  132. package/src/sync/sync-queries.ts +235 -0
  133. package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
  134. package/src/sync-extension.ts +64 -708
  135. package/src/sync.ts +8 -8
  136. package/src/types.ts +47 -286
  137. package/src/ui/attention-widget.ts +1 -0
  138. package/src/ui/automatic-sync-summary.ts +6 -0
  139. package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
  140. package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
  141. package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
  142. package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
  143. package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
  144. package/src/ui/manager-ui.ts +386 -0
  145. package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
  146. package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
  147. package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
  148. package/src/ui/setup/git-ui.ts +293 -0
  149. package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
  150. package/src/ui/setup/s3-ui.ts +217 -0
  151. package/src/ui/setup/setup-actions.ts +127 -0
  152. package/src/ui/setup/setup-location-ui.ts +167 -0
  153. package/src/ui/setup/setup-name-ui.ts +34 -0
  154. package/src/ui/setup/setup-prompts.ts +92 -0
  155. package/src/ui/setup/setup-review.ts +70 -0
  156. package/src/ui/setup/setup-switcher.ts +181 -0
  157. package/src/ui/setup/setup-wizard.ts +39 -0
  158. package/src/ui/setup/text-input.ts +78 -0
  159. package/src/ui/setup/webdav-ui.ts +330 -0
  160. package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
  161. package/src/ui/sync-attention.ts +187 -0
  162. package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
  163. package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
  164. package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
  165. package/src/ui/terminal-text.ts +4 -0
  166. package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
  167. package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
  168. package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
  169. package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
  170. package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
  171. package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
  172. package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
  173. package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
  174. package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
  175. package/dist/chunks/chunk-E4COSV32.ts +0 -82
  176. package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
  177. package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
  178. package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
  179. package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
  180. package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
  181. package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
  182. package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
  183. package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
  184. package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
  185. package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
  186. package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
  187. package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
  188. package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
  189. package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
  190. package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
  191. package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
  192. package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
  193. package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
  194. package/src/config.ts +0 -923
  195. package/src/git-ui.ts +0 -307
  196. package/src/manager-helpers.ts +0 -64
  197. package/src/manager-ui.ts +0 -997
  198. package/src/sync-attention.ts +0 -100
  199. package/src/webdav-ui.ts +0 -421
  200. /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-FKIBM3GO.ts.map} +0 -0
  201. /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-D7RBRJNL.ts.map} +0 -0
  202. /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
  203. /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
  204. /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-CST2KL65.ts.map} +0 -0
  205. /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
  206. /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
  207. /package/src/{config-file.ts → settings/config-file.ts} +0 -0
  208. /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
  209. /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
  210. /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
  211. /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
  212. /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
  213. /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
+ }
@@ -0,0 +1,51 @@
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
+ backendIdentityCoordinates
5
+ } from "./chunk-6LP3MCR2.ts";
6
+ import {
7
+ stateDir
8
+ } from "./chunk-ZI5MXMEX.ts";
9
+ import {
10
+ readJsonIfExists,
11
+ writeJson
12
+ } from "./chunk-T3HH3UEJ.ts";
13
+
14
+ // src/state/sync-state-store.ts
15
+ import { createHash } from "node:crypto";
16
+ import fs from "node:fs/promises";
17
+ import path from "node:path";
18
+ var STATE_VERSION = 2;
19
+ function syncStateFingerprint(state) {
20
+ return createHash("sha256").update(JSON.stringify(state)).digest("hex");
21
+ }
22
+ async function readStateForConfig(config) {
23
+ return await readJsonIfExists(statePathForConfig(config)) ?? {
24
+ version: STATE_VERSION,
25
+ profile: config.snapshotIdentity,
26
+ lastFileHashes: {}
27
+ };
28
+ }
29
+ async function writeStateForConfig(config, state) {
30
+ await writeJson(statePathForConfig(config), state);
31
+ }
32
+ function statePathForConfig(config) {
33
+ const identity = backendIdentityCoordinates(config);
34
+ const hash = createHash("sha256").update(identity).digest("hex").slice(0, 16);
35
+ return path.join(stateDir(), "setups", `${config.backend.type}-${hash}.state.json`);
36
+ }
37
+ function lockPath() {
38
+ return path.join(stateDir(), "lock");
39
+ }
40
+ async function ensureStateDir() {
41
+ await fs.mkdir(stateDir(), { recursive: true });
42
+ }
43
+
44
+ export {
45
+ syncStateFingerprint,
46
+ readStateForConfig,
47
+ writeStateForConfig,
48
+ lockPath,
49
+ ensureStateDir
50
+ };
51
+ //# sourceMappingURL=chunk-CCSN3F47.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/state/sync-state-store.ts"],
4
+ "sourcesContent": ["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { backendIdentityCoordinates } from \"../backends/backend-identity.js\";\nimport { safeName } from \"../paths.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { readJsonIfExists, writeJson } from \"./json-file.js\";\nimport { stateDir } from \"./state-directory.js\";\nimport type { SyncState } from \"./state-types.js\";\n\nconst STATE_VERSION = 2;\n\n/** Bounded observation token; do not retain the full baseline in passive UI state. */\nexport function syncStateFingerprint(state: SyncState) {\n\treturn createHash(\"sha256\").update(JSON.stringify(state)).digest(\"hex\");\n}\n\nexport async function readState(profile: string): Promise<SyncState> {\n\treturn (\n\t\t(await readJsonIfExists<SyncState>(statePath(profile))) ?? {\n\t\t\tversion: STATE_VERSION,\n\t\t\tprofile,\n\t\t\tlastFileHashes: {},\n\t\t}\n\t);\n}\n\nexport async function writeState(profile: string, state: SyncState) {\n\tawait writeJson(statePath(profile), state);\n}\n\nexport async function readStateForConfig(config: AnySyncConfig): Promise<SyncState> {\n\treturn (\n\t\t(await readJsonIfExists<SyncState>(statePathForConfig(config))) ?? {\n\t\t\tversion: STATE_VERSION,\n\t\t\tprofile: config.snapshotIdentity,\n\t\t\tlastFileHashes: {},\n\t\t}\n\t);\n}\n\nexport async function writeStateForConfig(config: AnySyncConfig, state: SyncState) {\n\tawait writeJson(statePathForConfig(config), state);\n}\n\nexport function statePathForConfig(config: AnySyncConfig) {\n\tconst identity = backendIdentityCoordinates(config);\n\tconst hash = createHash(\"sha256\").update(identity).digest(\"hex\").slice(0, 16);\n\treturn path.join(stateDir(), \"setups\", `${config.backend.type}-${hash}.state.json`);\n}\n\nfunction statePath(profile: string) {\n\treturn path.join(stateDir(), `${safeName(profile)}.state.json`);\n}\n\nexport function lockPath() {\n\treturn path.join(stateDir(), \"lock\");\n}\n\nexport async function ensureStateDir() {\n\tawait fs.mkdir(stateDir(), { recursive: true });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AACf,OAAO,UAAU;AAQjB,IAAM,gBAAgB;AAGf,SAAS,qBAAqB,OAAkB;AACtD,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK;AACvE;AAgBA,eAAsB,mBAAmB,QAA2C;AACnF,SACE,MAAM,iBAA4B,mBAAmB,MAAM,CAAC,KAAM;AAAA,IAClE,SAAS;AAAA,IACT,SAAS,OAAO;AAAA,IAChB,gBAAgB,CAAC;AAAA,EAClB;AAEF;AAEA,eAAsB,oBAAoB,QAAuB,OAAkB;AAClF,QAAM,UAAU,mBAAmB,MAAM,GAAG,KAAK;AAClD;AAEO,SAAS,mBAAmB,QAAuB;AACzD,QAAM,WAAW,2BAA2B,MAAM;AAClD,QAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC5E,SAAO,KAAK,KAAK,SAAS,GAAG,UAAU,GAAG,OAAO,QAAQ,IAAI,IAAI,IAAI,aAAa;AACnF;AAMO,SAAS,WAAW;AAC1B,SAAO,KAAK,KAAK,SAAS,GAAG,MAAM;AACpC;AAEA,eAAsB,iBAAiB;AACtC,QAAM,GAAG,MAAM,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/C;",
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/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-CCSN3F47.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-ETTRJDNP.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
+ }
@@ -3,17 +3,19 @@
3
3
  import {
4
4
  runCancellableOperation,
5
5
  showRemoteSelectionReview
6
- } from "./chunk-ARN2672F.ts";
6
+ } from "./chunk-4CHTZMEO.ts";
7
7
  import {
8
- errorMessage,
9
8
  safeTerminalText
10
- } from "./chunk-C7LXZ3UH.ts";
9
+ } from "./chunk-LTBWGSBV.ts";
11
10
  import {
12
11
  loadConfig,
13
12
  syncConfigReviewIdentity
14
- } from "./chunk-5T442OP3.ts";
13
+ } from "./chunk-C7XY5NF5.ts";
14
+ import {
15
+ errorMessage
16
+ } from "./chunk-2B2ELUF5.ts";
15
17
 
16
- // src/sync-resolution-ui.ts
18
+ // src/ui/sync-resolution-ui.ts
17
19
  import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
18
20
  async function showSyncResolution(ctx, initialDecision, runRoute, sessionSignal) {
19
21
  let currentDecision = initialDecision;
@@ -146,7 +148,7 @@ function pullLabel(decision) {
146
148
  return decision.kind.startsWith("first-sync-") ? "Use remote as initial source\u2026" : "Use remote content and replace local\u2026";
147
149
  }
148
150
 
149
- // src/manager-result-dispatcher.ts
151
+ // src/ui/manager-result-dispatcher.ts
150
152
  var MAX_DECISION_TRANSITIONS = 32;
151
153
  async function dispatchManagerResult(ctx, initialResult, origin, runRoute, signal, options = {}) {
152
154
  let result = initialResult;
@@ -210,4 +212,4 @@ async function dispatchManagerResult(ctx, initialResult, origin, runRoute, signa
210
212
  export {
211
213
  dispatchManagerResult
212
214
  };
213
- //# sourceMappingURL=chunk-44LPGQTI.ts.map
215
+ //# sourceMappingURL=chunk-G4RNQQZS.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/ui/sync-resolution-ui.ts", "../../src/ui/manager-result-dispatcher.ts"],
4
+ "sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { defineMenu, runMenu } from \"@narumitw/pi-tui-kit\";\nimport { loadConfig, syncConfigReviewIdentity } from \"../settings/config.js\";\nimport type { SyncDecision, SyncResolutionDirection } from \"../sync/sync-decision.js\";\nimport { errorMessage } from \"../sync/sync-errors.js\";\nimport {\n\ttype RunRoute,\n\ttype RunRouteResult,\n\trunCancellableOperation,\n} from \"./cancellable-operation.js\";\nimport { safeTerminalText } from \"./terminal-text.js\";\n\nexport type SyncResolutionResult =\n\t| { kind: \"resolved\"; direction: SyncResolutionDirection }\n\t| {\n\t\t\tkind: \"route-result\";\n\t\t\tresult: RunRouteResult;\n\t\t\troute: SyncResolutionDirection;\n\t }\n\t| { kind: \"back\" }\n\t| { kind: \"closed\" }\n\t| { kind: \"stale\" };\n\nexport async function showSyncResolution(\n\tctx: ExtensionContext,\n\tinitialDecision: SyncDecision,\n\trunRoute: RunRoute,\n\tsessionSignal?: AbortSignal,\n): Promise<SyncResolutionResult> {\n\ttype Screen = \"resolve\" | \"review\";\n\ttype Action = \"push\" | \"pull\" | \"back\";\n\tlet currentDecision = initialDecision;\n\tlet resolvedDirection: SyncResolutionDirection | undefined;\n\tlet chainedResult: { result: RunRouteResult; route: SyncResolutionDirection } | undefined;\n\tconst menu = defineMenu<SyncDecision, Screen, Action, ExtensionContext>({\n\t\tstart: \"resolve\",\n\t\tscreens: {\n\t\t\tresolve: ({ state }) => ({\n\t\t\t\tkind: \"actions\",\n\t\t\t\ttitle: resolutionTitle(state),\n\t\t\t\tlines: resolutionLines(state),\n\t\t\t\titems: [\n\t\t\t\t\t{ id: \"review\", label: \"Review differences (recommended)\", to: \"review\" },\n\t\t\t\t\t...(state.directions.includes(\"push\")\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: \"push\",\n\t\t\t\t\t\t\t\t\tlabel: pushLabel(state),\n\t\t\t\t\t\t\t\t\tdescription: \"Review an exact push before replacing the remote version.\",\n\t\t\t\t\t\t\t\t\taction: \"push\" as const,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: []),\n\t\t\t\t\t...(state.directions.includes(\"pull\")\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: \"pull\",\n\t\t\t\t\t\t\t\t\tlabel: pullLabel(state),\n\t\t\t\t\t\t\t\t\tdescription: \"Review exact local changes and create a backup before applying.\",\n\t\t\t\t\t\t\t\t\taction: \"pull\" as const,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: []),\n\t\t\t\t\t{ id: \"back\", label: \"Back\", action: \"back\" },\n\t\t\t\t],\n\t\t\t\thint: \"back\",\n\t\t\t}),\n\t\t\treview: ({ state }) => ({\n\t\t\t\tkind: \"review\",\n\t\t\t\ttitle: `Review differences \u00B7 ${safeTerminalText(state.setupName)}`,\n\t\t\t\tcontent: state.review,\n\t\t\t\tformat: { kind: \"text\" },\n\t\t\t\tviewportSize: \"adaptive\",\n\t\t\t\thint: \"back\",\n\t\t\t}),\n\t\t},\n\t\tactions: {\n\t\t\tpush: ({ state, signal }) => resolveDirection(\"push\", state, signal),\n\t\t\tpull: ({ state, signal }) => resolveDirection(\"pull\", state, signal),\n\t\t\tback: async () => ({ kind: \"back\" }),\n\t\t},\n\t});\n\n\tasync function resolveDirection(\n\t\tdirection: SyncResolutionDirection,\n\t\tdecision: SyncDecision,\n\t\tactionSignal: AbortSignal,\n\t) {\n\t\tconst signal = sessionSignal ? AbortSignal.any([sessionSignal, actionSignal]) : actionSignal;\n\t\tconst config = await loadConfig(decision.setupName);\n\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\tif (syncConfigReviewIdentity(config) !== decision.configIdentity) {\n\t\t\tthrow new Error(\n\t\t\t\t`Sync setup \u201C${safeTerminalText(decision.setupName)}\u201D changed while conflict resolution was open; return to the sync manager and retry.`,\n\t\t\t);\n\t\t}\n\t\tconst result = await runCancellableOperation(\n\t\t\tctx,\n\t\t\tdirection === \"push\"\n\t\t\t\t? \"Preparing local-wins push preview\u2026\"\n\t\t\t\t: \"Preparing remote-wins pull preview\u2026\",\n\t\t\t`${direction} --force`,\n\t\t\trunRoute,\n\t\t\t{\n\t\t\t\tcommitAware: true,\n\t\t\t\tcancelledMessage:\n\t\t\t\t\tdirection === \"push\"\n\t\t\t\t\t\t? \"Push preparation cancelled; no remote files were changed.\"\n\t\t\t\t\t\t: \"Pull check cancelled; no local files were changed.\",\n\t\t\t\ttarget: decision.setupName,\n\t\t\t\tsignal,\n\t\t\t},\n\t\t);\n\t\tif (result.kind === \"decision-required\") {\n\t\t\tcurrentDecision = result.decision;\n\t\t\treturn { kind: \"stay\" as const };\n\t\t}\n\t\tif (result.kind === \"remote-selection-required\") {\n\t\t\tchainedResult = { result, route: direction };\n\t\t\treturn { kind: \"close\" as const };\n\t\t}\n\t\tif (result.kind === \"completed\") {\n\t\t\tif (result.outcome === \"cancelled\") return { kind: \"stay\" as const };\n\t\t\tresolvedDirection = direction;\n\t\t\treturn { kind: \"close\" as const };\n\t\t}\n\t\treturn result.kind === \"closed\" ? { kind: \"close\" as const } : { kind: \"stay\" as const };\n\t}\n\n\tconst result = await runMenu(ctx, menu, {\n\t\tgetState: () => currentDecision,\n\t\tsignal: sessionSignal,\n\t\tisCurrent: () => !sessionSignal?.aborted,\n\t\tonError: (_menuCtx, error) => ctx.ui.notify(errorMessage(error), \"error\"),\n\t});\n\tif (sessionSignal?.aborted || result.kind === \"stale\") return { kind: \"stale\" };\n\tif (chainedResult) return { kind: \"route-result\", ...chainedResult };\n\tif (resolvedDirection) return { kind: \"resolved\", direction: resolvedDirection };\n\tif (result.kind === \"closed\") {\n\t\treturn result.reason === \"back\" ? { kind: \"back\" } : { kind: \"closed\" };\n\t}\n\treturn { kind: \"closed\" };\n}\n\nfunction resolutionTitle(decision: SyncDecision) {\n\treturn decision.kind === \"remote-empty\" ? \"Remote is empty\" : \"Resolve sync conflict\";\n}\n\nfunction resolutionLines(decision: SyncDecision) {\n\treturn [\n\t\t`Sync setup: ${safeTerminalText(decision.setupName)}`,\n\t\t...causeSummary(decision),\n\t\t\"No files have been changed by this failed operation.\",\n\t];\n}\n\nfunction causeSummary(decision: SyncDecision) {\n\tif (decision.kind === \"first-sync-settings-diverged\") {\n\t\treturn [\"This machine and the remote have different Pi settings on first sync.\"];\n\t}\n\tif (decision.kind === \"first-sync-sessions-diverged\") {\n\t\treturn [\"Pi settings match, but local and remote sessions differ on first sync.\"];\n\t}\n\tif (decision.kind === \"remote-empty\") return [\"The remote storage location has no snapshot.\"];\n\treturn [\n\t\t...(decision.causes.localChanged ? [\"Local content changed since the last sync.\"] : []),\n\t\t...(decision.causes.remoteChanged ? [\"Remote content changed since the last sync.\"] : []),\n\t\t...(decision.causes.policyChanged ? [\"Included content changed since the last sync.\"] : []),\n\t];\n}\n\nfunction pushLabel(decision: SyncDecision) {\n\tif (decision.kind === \"remote-empty\") return \"Push local content\u2026\";\n\tif (decision.kind.startsWith(\"first-sync-\")) return \"Use local as initial source\u2026\";\n\treturn \"Keep local content and replace remote\u2026\";\n}\n\nfunction pullLabel(decision: SyncDecision) {\n\treturn decision.kind.startsWith(\"first-sync-\")\n\t\t? \"Use remote as initial source\u2026\"\n\t\t: \"Use remote content and replace local\u2026\";\n}\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tCancellableOperationResult,\n\tRunRoute,\n\tRunRouteResult,\n} from \"./cancellable-operation.js\";\nimport { type RemoteSelectionOrigin, showRemoteSelectionReview } from \"./remote-selection-ui.js\";\nimport { showSyncResolution } from \"./sync-resolution-ui.js\";\n\nconst MAX_DECISION_TRANSITIONS = 32;\n\nexport interface ManagerResultDisposition {\n\tkind: \"close\" | \"stay\";\n\tappliedRoute?: \"sync\" | \"pull\" | \"push\";\n}\n\nexport interface ManagerResultOptions {\n\tcancelLabel?: string;\n\tonSelectionResolved?: () => void;\n\twithStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;\n}\n\nexport async function dispatchManagerResult(\n\tctx: ExtensionContext,\n\tinitialResult: CancellableOperationResult,\n\torigin: Exclude<RemoteSelectionOrigin, \"settings\">,\n\trunRoute: RunRoute,\n\tsignal?: AbortSignal,\n\toptions: ManagerResultOptions = {},\n): Promise<ManagerResultDisposition> {\n\tlet result: CancellableOperationResult | RunRouteResult = initialResult;\n\tlet currentRoute: \"sync\" | \"pull\" | \"push\" = origin;\n\tlet resolving = false;\n\n\tfor (let transition = 0; transition < MAX_DECISION_TRANSITIONS; transition += 1) {\n\t\tif (signal?.aborted) return { kind: \"close\" };\n\t\tif (result.kind === \"remote-selection-required\") {\n\t\t\tresolving = true;\n\t\t\tconst resolution = await showRemoteSelectionReview(\n\t\t\t\tctx,\n\t\t\t\tresult.decision.setupName,\n\t\t\t\tsignal,\n\t\t\t\tundefined,\n\t\t\t\t{\n\t\t\t\t\tdecision: result.decision,\n\t\t\t\t\torigin: currentRoute,\n\t\t\t\t\trunRoute,\n\t\t\t\t\tcancelLabel: options.cancelLabel,\n\t\t\t\t\tonSelectionResolved: options.onSelectionResolved,\n\t\t\t\t\twithStateAccess: options.withStateAccess,\n\t\t\t\t},\n\t\t\t);\n\t\t\tif (resolution.kind === \"route-result\") {\n\t\t\t\tresult = resolution.result;\n\t\t\t\tcurrentRoute = resolution.route;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn resolution.kind === \"closed\" || resolution.kind === \"stale\"\n\t\t\t\t? { kind: \"close\" }\n\t\t\t\t: { kind: \"stay\" };\n\t\t}\n\t\tif (result.kind === \"decision-required\") {\n\t\t\tresolving = true;\n\t\t\tconst resolution = await showSyncResolution(ctx, result.decision, runRoute, signal);\n\t\t\tif (resolution.kind === \"route-result\") {\n\t\t\t\tresult = resolution.result;\n\t\t\t\tcurrentRoute = resolution.route;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (resolution.kind === \"resolved\") {\n\t\t\t\treturn { kind: \"close\", appliedRoute: resolution.direction };\n\t\t\t}\n\t\t\treturn resolution.kind === \"closed\" || resolution.kind === \"stale\"\n\t\t\t\t? { kind: \"close\" }\n\t\t\t\t: { kind: \"stay\" };\n\t\t}\n\t\tif (result.kind === \"closed\") return { kind: \"close\" };\n\t\tif (result.kind === \"completed\") {\n\t\t\tconst appliedRoute = result.outcome === \"applied\" ? currentRoute : undefined;\n\t\t\tif (resolving || (origin === \"pull\" && result.outcome === \"applied\")) {\n\t\t\t\treturn { kind: \"close\", ...(appliedRoute ? { appliedRoute } : {}) };\n\t\t\t}\n\t\t\treturn { kind: \"stay\", ...(appliedRoute ? { appliedRoute } : {}) };\n\t\t}\n\t\treturn { kind: \"stay\" };\n\t}\n\n\tctx.ui.notify(\n\t\t\"Sync resolution stopped after too many state changes. Start /sync again to review fresh state.\",\n\t\t\"warning\",\n\t);\n\treturn { kind: \"stay\" };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["result"]
7
+ }