@narumitw/pi-sync 0.49.15 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +49 -16
  2. package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
  3. package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
  4. package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
  5. package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
  6. package/dist/chunks/chunk-6UHKGALE.ts +134 -0
  7. package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
  8. package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
  9. package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
  10. package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-AXXS2BPA.ts} +9 -9
  11. package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
  12. package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
  13. package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
  14. package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
  15. package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
  16. package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
  17. package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
  18. package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
  19. package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
  20. package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-GWMHAQG4.ts} +4 -4
  21. package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
  22. package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
  23. package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
  24. package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
  25. package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
  26. package/dist/chunks/chunk-JSYMGJQQ.ts +51 -0
  27. package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
  28. package/dist/chunks/chunk-L7TBO32M.ts +25 -0
  29. package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
  30. package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
  31. package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
  32. package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
  33. package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
  34. package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
  35. package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
  36. package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
  37. package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
  38. package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
  39. package/dist/chunks/chunk-OUHGBFYF.ts.map +7 -0
  40. package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
  41. package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
  42. package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
  43. package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
  44. package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
  45. package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
  46. package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
  47. package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
  48. package/dist/chunks/chunk-THMF4BVL.ts +18 -0
  49. package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
  50. package/dist/chunks/{chunk-44LPGQTI.ts → chunk-X5OM6GXT.ts} +9 -7
  51. package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
  52. package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
  53. package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
  54. package/dist/chunks/{chunk-ARN2672F.ts → chunk-YKJHPTGO.ts} +23 -55
  55. package/dist/chunks/chunk-YKJHPTGO.ts.map +7 -0
  56. package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
  57. package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
  58. package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
  59. package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
  60. package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
  61. package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
  62. package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-OQW46G2X.ts} +42 -17
  63. package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
  64. package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
  65. package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
  66. package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
  67. package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
  68. package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-3P33LQHU.ts} +96 -18
  69. package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
  70. package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
  71. package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
  72. package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
  73. package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
  74. package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
  75. package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-ONJ27QRL.ts} +374 -294
  76. package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
  77. package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
  78. package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
  79. package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
  80. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
  81. package/dist/index.ts +477 -342
  82. package/dist/index.ts.map +4 -4
  83. package/docs/settings.md +30 -6
  84. package/package.json +1 -1
  85. package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
  86. package/src/backends/backend-identity.ts +40 -0
  87. package/src/backends/backend-types.ts +81 -0
  88. package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
  89. package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
  90. package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
  91. package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
  92. package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
  93. package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
  94. package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
  95. package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
  96. package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
  97. package/src/commands/command-execution.ts +330 -0
  98. package/src/commands/command-handler.ts +201 -0
  99. package/src/commands/command-types.ts +18 -0
  100. package/src/{command.ts → commands/command.ts} +2 -1
  101. package/src/commands/setup-completions.ts +6 -0
  102. package/src/settings/config.ts +238 -0
  103. package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
  104. package/src/settings/settings-store.ts +109 -0
  105. package/src/settings/settings-types.ts +128 -0
  106. package/src/settings/settings-validation.ts +443 -0
  107. package/src/snapshot/session-paths.ts +84 -0
  108. package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
  109. package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
  110. package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
  111. package/src/snapshot/snapshot-types.ts +38 -0
  112. package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
  113. package/src/state/json-file.ts +20 -0
  114. package/src/{lock.ts → state/lock.ts} +3 -2
  115. package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
  116. package/src/state/state-types.ts +20 -0
  117. package/src/state/sync-state-store.ts +62 -0
  118. package/src/sync/automatic-sync.ts +103 -0
  119. package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
  120. package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
  121. package/src/sync/signals.ts +10 -0
  122. package/src/sync/startup-check.ts +128 -0
  123. package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
  124. package/src/sync/sync-error-guidance.ts +53 -0
  125. package/src/sync/sync-inspection.ts +51 -0
  126. package/src/sync/sync-loaders.ts +54 -0
  127. package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
  128. package/src/sync/sync-operations.ts +12 -0
  129. package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
  130. package/src/sync/sync-queries.ts +235 -0
  131. package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
  132. package/src/sync-extension.ts +64 -708
  133. package/src/sync.ts +8 -8
  134. package/src/types.ts +47 -286
  135. package/src/ui/automatic-sync-summary.ts +6 -0
  136. package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
  137. package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
  138. package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
  139. package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
  140. package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
  141. package/src/ui/manager-ui.ts +386 -0
  142. package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
  143. package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
  144. package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
  145. package/src/ui/setup/git-ui.ts +293 -0
  146. package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
  147. package/src/ui/setup/s3-ui.ts +217 -0
  148. package/src/ui/setup/setup-actions.ts +127 -0
  149. package/src/ui/setup/setup-location-ui.ts +167 -0
  150. package/src/ui/setup/setup-name-ui.ts +34 -0
  151. package/src/ui/setup/setup-prompts.ts +92 -0
  152. package/src/ui/setup/setup-review.ts +70 -0
  153. package/src/ui/setup/setup-switcher.ts +181 -0
  154. package/src/ui/setup/setup-wizard.ts +39 -0
  155. package/src/ui/setup/text-input.ts +78 -0
  156. package/src/ui/setup/webdav-ui.ts +330 -0
  157. package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
  158. package/src/{sync-attention.ts → ui/sync-attention.ts} +75 -6
  159. package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
  160. package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
  161. package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
  162. package/src/ui/terminal-text.ts +4 -0
  163. package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
  164. package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
  165. package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
  166. package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
  167. package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
  168. package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
  169. package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
  170. package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
  171. package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
  172. package/dist/chunks/chunk-E4COSV32.ts +0 -82
  173. package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
  174. package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
  175. package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
  176. package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
  177. package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
  178. package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
  179. package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
  180. package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
  181. package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
  182. package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
  183. package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
  184. package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
  185. package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
  186. package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
  187. package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
  188. package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
  189. package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
  190. package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
  191. package/src/config.ts +0 -923
  192. package/src/git-ui.ts +0 -307
  193. package/src/manager-helpers.ts +0 -64
  194. package/src/manager-ui.ts +0 -997
  195. package/src/webdav-ui.ts +0 -421
  196. /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
  197. /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
  198. /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
  199. /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
  200. /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-FEP4L5TR.ts.map} +0 -0
  201. /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
  202. /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
  203. /package/src/{config-file.ts → settings/config-file.ts} +0 -0
  204. /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
  205. /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
  206. /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
  207. /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
  208. /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
  209. /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/remote-selection-ui.ts", "../../src/cancellable-operation.ts", "../../src/manager-helpers.ts"],
4
- "sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { defineMenu, runMenu } from \"@narumitw/pi-tui-kit\";\nimport { createSyncBackend, type SyncBackendFactory } from \"./backend-factory.js\";\nimport {\n\ttype RunRoute,\n\ttype RunRouteResult,\n\trunCancellableOperation,\n} from \"./cancellable-operation.js\";\nimport {\n\tloadConfig,\n\tloadPartialConfig,\n\ttype SyncSetupStorageReview,\n\tsyncConfigReviewFingerprint,\n} from \"./config.js\";\nimport { readSnapshotForHead } from \"./remote-snapshot.js\";\nimport { SyncSetupReviewChangedError, updateSyncSetup } from \"./settings-management.js\";\nimport type { RemoteHead, SyncBackend } from \"./sync-backend.js\";\nimport { errorMessage, safeTerminalText } from \"./sync-format.js\";\nimport {\n\tcompareSyncInclude,\n\tinspectRemoteSelection,\n\ttype RemoteSelectionDecision,\n\ttype RemoteSelectionState,\n\tsameSyncInclude,\n} from \"./sync-policy.js\";\nimport type { AnySyncConfig, PartialConfig } from \"./types.js\";\n\nconst STATUS_KEY = \"sync\";\n\nexport type RemoteSelectionOrigin = \"settings\" | \"sync\" | \"pull\" | \"push\";\n\nexport interface RemoteSelectionReviewOptions {\n\tdecision?: RemoteSelectionDecision;\n\torigin?: RemoteSelectionOrigin;\n\trunRoute?: RunRoute;\n\tcancelLabel?: string;\n\tonSelectionResolved?: () => void;\n\twithStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;\n}\n\nexport type RemoteSelectionReviewResult =\n\t| { kind: \"back\" }\n\t| { kind: \"closed\" }\n\t| { kind: \"done\" }\n\t| { kind: \"stale\" }\n\t| { kind: \"route-result\"; result: RunRouteResult; route: \"sync\" | \"pull\" | \"push\" };\n\nexport async function showRemoteSelectionReview(\n\tctx: ExtensionContext,\n\tsetupName?: string,\n\tsignal?: AbortSignal,\n\tfactory: SyncBackendFactory = createSyncBackend,\n\toptions: RemoteSelectionReviewOptions = {},\n): Promise<RemoteSelectionReviewResult> {\n\ttry {\n\t\tlet decision = options.decision;\n\t\tif (!decision) {\n\t\t\tconst inspected = await inspectConfiguredRemoteSelection(ctx, setupName, signal, factory);\n\t\t\tif (!inspected || signal?.aborted) return { kind: \"stale\" };\n\t\t\tif (inspected.kind === \"empty\") {\n\t\t\t\tctx.ui.notify(\"Remote storage has no snapshot or synced-content list yet.\", \"info\");\n\t\t\t\treturn { kind: \"back\" };\n\t\t\t}\n\t\t\tif (inspected.state.kind === \"same\") {\n\t\t\t\tctx.ui.notify(\"Remote synced content already matches this sync setup.\", \"info\");\n\t\t\t\treturn { kind: \"back\" };\n\t\t\t}\n\t\t\tif (inspected.state.kind === \"legacy\") {\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(formatLegacySummary(inspected.config, inspected.state.discovered), \"info\");\n\t\t\t\t\treturn { kind: \"back\" };\n\t\t\t\t}\n\t\t\t\tawait showLegacyDiscovery(ctx, inspected.config, inspected.state.discovered, signal);\n\t\t\t\treturn signal?.aborted ? { kind: \"stale\" } : { kind: \"back\" };\n\t\t\t}\n\t\t\tdecision = decisionFromState(inspected.config, inspected.state);\n\t\t}\n\n\t\tif (ctx.mode !== \"tui\") {\n\t\t\tctx.ui.notify(formatRemoteSelectionSummary(decision), \"warning\");\n\t\t\treturn { kind: \"back\" };\n\t\t}\n\n\t\tlet currentDecision = decision;\n\t\tfor (;;) {\n\t\t\tif (signal?.aborted) return { kind: \"stale\" };\n\t\t\tconst result = await showSelectionDifference(\n\t\t\t\tctx,\n\t\t\t\tcurrentDecision,\n\t\t\t\toptions.origin ?? \"settings\",\n\t\t\t\toptions.runRoute,\n\t\t\t\tsignal,\n\t\t\t\tfactory,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (result.kind !== \"refresh\") return result;\n\t\t\tconst refreshed = await runWithOptionalStateAccess(options, () =>\n\t\t\t\tinspectConfiguredRemoteSelection(ctx, currentDecision.setupName, signal, factory),\n\t\t\t);\n\t\t\tif (!refreshed || signal?.aborted) return { kind: \"stale\" };\n\t\t\tif (refreshed.kind === \"empty\") {\n\t\t\t\tctx.ui.notify(\"Remote storage no longer has a snapshot or synced-content list.\", \"warning\");\n\t\t\t\treturn { kind: \"back\" };\n\t\t\t}\n\t\t\tif (refreshed.state.kind !== \"different\") {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\trefreshed.state.kind === \"same\"\n\t\t\t\t\t\t? \"Remote synced content now matches this sync setup.\"\n\t\t\t\t\t\t: \"The refreshed legacy snapshot has no authoritative synced-content list.\",\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\toptions.onSelectionResolved?.();\n\t\t\t\treturn { kind: \"done\" };\n\t\t\t}\n\t\t\tcurrentDecision = decisionFromState(refreshed.config, refreshed.state);\n\t\t}\n\t} catch (error) {\n\t\tif (signal?.aborted) return { kind: \"stale\" };\n\t\tctx.ui.notify(`Could not review synced content: ${errorMessage(error)}`, \"error\");\n\t\treturn { kind: \"back\" };\n\t} finally {\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t}\n}\n\ntype DifferenceFlowResult = RemoteSelectionReviewResult | { kind: \"refresh\" };\n\nasync function showSelectionDifference(\n\tctx: ExtensionContext,\n\tinitialDecision: RemoteSelectionDecision,\n\torigin: RemoteSelectionOrigin,\n\trunRoute: RunRoute | undefined,\n\tsessionSignal: AbortSignal | undefined,\n\tfactory: SyncBackendFactory,\n\toptions: Pick<\n\t\tRemoteSelectionReviewOptions,\n\t\t\"cancelLabel\" | \"onSelectionResolved\" | \"withStateAccess\"\n\t>,\n): Promise<DifferenceFlowResult> {\n\ttype Screen = \"choice\" | \"review\" | \"saved\";\n\ttype Action = \"adopt\" | \"keep\" | \"cancel\" | \"continue\" | \"done\";\n\tinterface FlowState {\n\t\tdecision: RemoteSelectionDecision;\n\t\tsaved: boolean;\n\t}\n\tlet flowState: FlowState = { decision: initialDecision, saved: false };\n\tlet continuationReview: PartialConfig | undefined;\n\tlet nextResult: RemoteSelectionReviewResult | undefined;\n\tlet refreshRequested = false;\n\tconst route = origin === \"settings\" ? \"sync\" : origin;\n\tconst menu = defineMenu<FlowState, Screen, Action, ExtensionContext>({\n\t\tstart: \"choice\",\n\t\tscreens: {\n\t\t\tchoice: ({ state }) => ({\n\t\t\t\tkind: \"actions\",\n\t\t\t\ttitle: \"Synced content differs\",\n\t\t\t\tlines: selectionSummaryLines(state.decision),\n\t\t\t\titems: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"review\",\n\t\t\t\t\t\tlabel: \"Review all paths (recommended)\",\n\t\t\t\t\t\tdescription: \"Compare exact remote-only, device-only, and ordered lists.\",\n\t\t\t\t\t\tto: \"review\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"adopt\",\n\t\t\t\t\t\tlabel: \"Use remote content list\",\n\t\t\t\t\t\tdescription: \"Save the reviewed list on this device without pulling files.\",\n\t\t\t\t\t\taction: \"adopt\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"keep\",\n\t\t\t\t\t\tlabel: \"Keep this device's content list and update remote\u2026\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Open the existing exact force-push preview without skipping confirmation.\",\n\t\t\t\t\t\taction: \"keep\",\n\t\t\t\t\t},\n\t\t\t\t\t{ id: \"cancel\", label: options.cancelLabel ?? \"Cancel\", action: \"cancel\" },\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 synced content \u00B7 ${safeTerminalText(state.decision.setupName)}`,\n\t\t\t\tcontent: formatSelectionDifference(state.decision),\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\tsaved: ({ state }) => ({\n\t\t\t\tkind: \"actions\",\n\t\t\t\ttitle: \"Remote content list saved\",\n\t\t\t\tlines: [\n\t\t\t\t\t`Sync setup: ${safeTerminalText(state.decision.setupName)}`,\n\t\t\t\t\t\"Only the included-content setting was saved.\",\n\t\t\t\t\t\"No files were pulled and sync state was not changed.\",\n\t\t\t\t],\n\t\t\t\titems: [\n\t\t\t\t\t...(runRoute\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: \"continue\",\n\t\t\t\t\t\t\t\t\tlabel: continueLabel(origin),\n\t\t\t\t\t\t\t\t\tdescription: \"Start a fresh check and exact preview for this sync setup.\",\n\t\t\t\t\t\t\t\t\taction: \"continue\" 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: \"done\", label: \"Done\", action: \"done\" },\n\t\t\t\t],\n\t\t\t\thint: \"close\",\n\t\t\t}),\n\t\t},\n\t\tactions: {\n\t\t\tadopt: async ({ state, signal: actionSignal }) => {\n\t\t\t\tconst signal = combineSignals(sessionSignal, actionSignal);\n\t\t\t\ttry {\n\t\t\t\t\tconst currentConfig = await loadConfig(state.decision.setupName);\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tassertLocalSelectionCurrent(currentConfig, state.decision);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!currentConfig.include.includes(\"sessions\") &&\n\t\t\t\t\t\tstate.decision.remoteInclude.includes(\"sessions\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst acknowledged = await ctx.ui.confirm(\n\t\t\t\t\t\t\t\"Use a content list that includes session conversations?\",\n\t\t\t\t\t\t\t\"Session JSONL may contain prompts, tool output, file paths, images, and secrets. This saves the list only; it does not pull files.\",\n\t\t\t\t\t\t\t{ signal },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\t\tif (!acknowledged) return { kind: \"stay\" as const };\n\t\t\t\t\t}\n\t\t\t\t\tconst review = await runWithOptionalStateAccess(options, async () => {\n\t\t\t\t\t\tconst loaded = await loadAdoptionReview(state.decision, signal, factory);\n\t\t\t\t\t\tif (signal.aborted) throw signal.reason;\n\t\t\t\t\t\tawait revalidateAndAdopt(loaded, state.decision, signal);\n\t\t\t\t\t\treturn loaded;\n\t\t\t\t\t});\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\toptions.onSelectionResolved?.();\n\t\t\t\t\tcontinuationReview = {\n\t\t\t\t\t\t...review.storageReview,\n\t\t\t\t\t\tsetupName: state.decision.setupName,\n\t\t\t\t\t\tinclude: [...state.decision.remoteInclude],\n\t\t\t\t\t\tautomatic: review.config.automatic,\n\t\t\t\t\t\tonSwitch: review.config.onSwitch,\n\t\t\t\t\t};\n\t\t\t\t\tflowState = { decision: state.decision, saved: true };\n\t\t\t\t\treturn { kind: \"to\" as const, screen: \"saved\" as const };\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tif (isStaleReviewError(error)) {\n\t\t\t\t\t\tctx.ui.notify(`${errorMessage(error)} Refreshing the comparison.`, \"warning\");\n\t\t\t\t\t\trefreshRequested = true;\n\t\t\t\t\t\treturn { kind: \"close\" as const };\n\t\t\t\t\t}\n\t\t\t\t\tctx.ui.notify(`Could not save the remote content list: ${errorMessage(error)}`, \"error\");\n\t\t\t\t\treturn { kind: \"stay\" as const };\n\t\t\t\t}\n\t\t\t},\n\t\t\tkeep: async ({ state, signal: actionSignal }) => {\n\t\t\t\tif (!runRoute) {\n\t\t\t\t\tctx.ui.notify(\"The reviewed update-remote route is unavailable.\", \"error\");\n\t\t\t\t\treturn { kind: \"stay\" as const };\n\t\t\t\t}\n\t\t\t\tconst signal = combineSignals(sessionSignal, actionSignal);\n\t\t\t\ttry {\n\t\t\t\t\tconst latest = await loadConfig(state.decision.setupName);\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tassertLocalSelectionCurrent(latest, state.decision);\n\t\t\t\t\tconst result = await runCancellableOperation(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\"Preparing this device's push preview\u2026\",\n\t\t\t\t\t\t\"push --force\",\n\t\t\t\t\t\trunRoute,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommitAware: true,\n\t\t\t\t\t\t\tcancelledMessage: \"Push preparation cancelled; no remote files were changed.\",\n\t\t\t\t\t\t\ttarget: state.decision.setupName,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\treturn handleNestedRouteResult(result, \"push\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tif (isStaleReviewError(error)) {\n\t\t\t\t\t\tctx.ui.notify(`${errorMessage(error)} Refreshing the comparison.`, \"warning\");\n\t\t\t\t\t\trefreshRequested = true;\n\t\t\t\t\t\treturn { kind: \"close\" as const };\n\t\t\t\t\t}\n\t\t\t\t\tctx.ui.notify(`Could not prepare the remote update: ${errorMessage(error)}`, \"error\");\n\t\t\t\t\treturn { kind: \"stay\" as const };\n\t\t\t\t}\n\t\t\t},\n\t\t\tcontinue: async ({ state, signal: actionSignal }) => {\n\t\t\t\tif (!runRoute || !continuationReview) return { kind: \"stay\" as const };\n\t\t\t\tconst signal = combineSignals(sessionSignal, actionSignal);\n\t\t\t\ttry {\n\t\t\t\t\tconst latest = await loadPartialConfig(state.decision.setupName);\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tif (!sameContinuationReview(latest, continuationReview)) {\n\t\t\t\t\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t\t\t\t\t`Sync setup \u201C${safeTerminalText(state.decision.setupName)}\u201D changed after the remote content list was saved.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst result = await runCancellableOperation(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tcontinueBusyLabel(origin),\n\t\t\t\t\t\troute,\n\t\t\t\t\t\trunRoute,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommitAware: true,\n\t\t\t\t\t\t\tcancelledMessage: continuationCancelledMessage(route),\n\t\t\t\t\t\t\ttarget: state.decision.setupName,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\treturn handleNestedRouteResult(result, route);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (signal.aborted) return { kind: \"close\" as const };\n\t\t\t\t\tctx.ui.notify(`Could not continue: ${errorMessage(error)}`, \"error\");\n\t\t\t\t\treturn { kind: \"stay\" as const };\n\t\t\t\t}\n\t\t\t},\n\t\t\tcancel: async () => ({ kind: \"back\" }),\n\t\t\tdone: async () => {\n\t\t\t\tnextResult = { kind: \"done\" };\n\t\t\t\treturn { kind: \"close\" };\n\t\t\t},\n\t\t},\n\t});\n\n\tfunction handleNestedRouteResult(\n\t\tresult: Awaited<ReturnType<typeof runCancellableOperation>>,\n\t\tnestedRoute: \"sync\" | \"pull\" | \"push\",\n\t) {\n\t\tif (result.kind === \"completed\" && result.outcome === \"applied\") {\n\t\t\toptions.onSelectionResolved?.();\n\t\t}\n\t\tif (result.kind === \"closed\") {\n\t\t\tnextResult = { kind: \"closed\" };\n\t\t\treturn { kind: \"close\" as const };\n\t\t}\n\t\tif (\n\t\t\tresult.kind === \"cancelled\" ||\n\t\t\t(result.kind === \"completed\" && result.outcome === \"cancelled\") ||\n\t\t\tresult.kind === \"failed\"\n\t\t) {\n\t\t\treturn { kind: \"stay\" as const };\n\t\t}\n\t\tnextResult = { kind: \"route-result\", result, route: nestedRoute };\n\t\treturn { kind: \"close\" as const };\n\t}\n\n\tconst menuResult = await runMenu(ctx, menu, {\n\t\tgetState: () => flowState,\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 || menuResult.kind === \"stale\") return { kind: \"stale\" };\n\tif (refreshRequested) return { kind: \"refresh\" };\n\tif (nextResult) return nextResult;\n\tif (menuResult.kind === \"closed\") {\n\t\treturn menuResult.reason === \"back\" ? { kind: \"back\" } : { kind: \"closed\" };\n\t}\n\treturn { kind: \"closed\" };\n}\n\ninterface AdoptionReview {\n\tconfig: AnySyncConfig;\n\tstorageReview: SyncSetupStorageReview;\n\tbackend: SyncBackend;\n\treviewedHead: RemoteHead;\n}\n\nasync function loadAdoptionReview(\n\tdecision: RemoteSelectionDecision,\n\tsignal: AbortSignal,\n\tfactory: SyncBackendFactory,\n): Promise<AdoptionReview> {\n\tconst config = await loadConfig(decision.setupName);\n\tthrowIfAborted(signal);\n\tassertLocalSelectionCurrent(config, decision);\n\tconst partial = await loadPartialConfig(decision.setupName);\n\tthrowIfAborted(signal);\n\tif (!sameSyncInclude(partial.include, decision.localInclude)) {\n\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t`Sync setup \u201C${safeTerminalText(decision.setupName)}\u201D changed while the comparison was open.`,\n\t\t);\n\t}\n\tconst backend = await factory(config);\n\tthrowIfAborted(signal);\n\tconst reviewedHead = await backend.readHead(signal);\n\tthrowIfAborted(signal);\n\tif (!reviewedHead) {\n\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t\"Remote storage changed while the comparison was open.\",\n\t\t);\n\t}\n\tconst snapshot = await readSnapshotForHead(backend, reviewedHead, signal);\n\tthrowIfAborted(signal);\n\tconst state = inspectRemoteSelection(config.include, snapshot);\n\tif (state.kind !== \"different\" || !sameSyncInclude(state.include, decision.remoteInclude)) {\n\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t\"Remote synced content changed while the comparison was open.\",\n\t\t);\n\t}\n\treturn { config, storageReview: partial, backend, reviewedHead };\n}\n\nasync function revalidateAndAdopt(\n\treview: AdoptionReview,\n\tdecision: RemoteSelectionDecision,\n\tsignal: AbortSignal,\n) {\n\tconst currentHead = await review.backend.readHead(signal);\n\tthrowIfAborted(signal);\n\tif (\n\t\t!currentHead ||\n\t\t!review.backend.sameRevision(review.reviewedHead.revision, currentHead.revision)\n\t) {\n\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t\"Remote storage changed while the comparison was open.\",\n\t\t);\n\t}\n\tconst currentSnapshot = await readSnapshotForHead(review.backend, currentHead, signal);\n\tthrowIfAborted(signal);\n\tconst currentState = inspectRemoteSelection(review.config.include, currentSnapshot);\n\tif (\n\t\tcurrentState.kind !== \"different\" ||\n\t\t!sameSyncInclude(currentState.include, decision.remoteInclude)\n\t) {\n\t\tthrow new StaleRemoteSelectionReviewError(\n\t\t\t\"Remote synced content changed while the comparison was open.\",\n\t\t);\n\t}\n\tawait updateSyncSetup(\n\t\tdecision.setupName,\n\t\t(setup) => ({\n\t\t\t...setup,\n\t\t\tsync: { ...setup.sync, include: [...decision.remoteInclude] },\n\t\t}),\n\t\t{\n\t\t\texpectedStorage: review.storageReview,\n\t\t\texpectedInclude: decision.localInclude,\n\t\t\tsignal,\n\t\t},\n\t);\n}\n\nasync function inspectConfiguredRemoteSelection(\n\tctx: ExtensionContext,\n\tsetupName: string | undefined,\n\tsignal: AbortSignal | undefined,\n\tfactory: SyncBackendFactory,\n): Promise<\n\t| { kind: \"empty\"; config: AnySyncConfig }\n\t| { kind: \"selection\"; config: AnySyncConfig; state: RemoteSelectionState }\n\t| undefined\n> {\n\tconst config = await loadConfig(setupName);\n\tif (signal?.aborted) return undefined;\n\tctx.ui.setStatus(STATUS_KEY, `checking synced content for ${safeTerminalText(config.setupName)}`);\n\tconst backend = await factory(config);\n\tif (signal?.aborted) return undefined;\n\tconst head = await backend.readHead(signal);\n\tif (signal?.aborted) return undefined;\n\tif (!head) return { kind: \"empty\", config };\n\tconst snapshot = await readSnapshotForHead(backend, head, signal);\n\tif (signal?.aborted) return undefined;\n\treturn {\n\t\tkind: \"selection\",\n\t\tconfig,\n\t\tstate: inspectRemoteSelection(config.include, snapshot),\n\t};\n}\n\nasync function showLegacyDiscovery(\n\tctx: ExtensionContext,\n\tconfig: AnySyncConfig,\n\tdiscovered: string[],\n\tsignal?: AbortSignal,\n) {\n\tconst menu = defineMenu<undefined, \"choice\" | \"review\", \"back\", ExtensionContext>({\n\t\tstart: \"choice\",\n\t\tscreens: {\n\t\t\tchoice: () => ({\n\t\t\t\tkind: \"actions\",\n\t\t\t\ttitle: `Compare synced content \u00B7 ${safeTerminalText(config.setupName)}`,\n\t\t\t\tlines: [\n\t\t\t\t\t\"This legacy snapshot has no portable synced-content list.\",\n\t\t\t\t\t\"Discovered paths are partial and read-only; preserved files may not have been selected.\",\n\t\t\t\t],\n\t\t\t\titems: [\n\t\t\t\t\t{ id: \"review\", label: \"Review discovered paths\", to: \"review\" },\n\t\t\t\t\t{ id: \"back\", label: \"Back\", action: \"back\" },\n\t\t\t\t],\n\t\t\t\thint: \"close\",\n\t\t\t}),\n\t\t\treview: () => ({\n\t\t\t\tkind: \"review\",\n\t\t\t\ttitle: \"Partial discovery from legacy snapshot\",\n\t\t\t\tcontent: [\n\t\t\t\t\t\"Partial discovery only \u2014 not an authoritative selection.\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t...(discovered.length > 0\n\t\t\t\t\t\t? discovered.map((item) => `Discovered: ${safeTerminalText(item)}`)\n\t\t\t\t\t\t: [\"No safe paths were discovered.\"]),\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"Use Add custom path\u2026 in the local Included Content editor if needed.\",\n\t\t\t\t].join(\"\\n\"),\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: { back: async () => ({ kind: \"close\" }) },\n\t});\n\tawait runMenu(ctx, menu, {\n\t\tgetState: () => undefined,\n\t\tsignal,\n\t\tisCurrent: () => !signal?.aborted,\n\t});\n}\n\nfunction selectionSummaryLines(decision: RemoteSelectionDecision) {\n\tconst comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n\treturn [\n\t\t`Sync setup: ${safeTerminalText(decision.setupName)}`,\n\t\t\"Nothing changed. Review both lists before choosing what happens next.\",\n\t\t...(comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n\t\t\t? [\"Only the ordering differs; membership is the same.\"]\n\t\t\t: [\n\t\t\t\t\t`Remote-only paths: ${comparison.remoteOnly.length} \u00B7 Device-only paths: ${comparison.localOnly.length}`,\n\t\t\t\t]),\n\t];\n}\n\nfunction formatSelectionDifference(decision: RemoteSelectionDecision) {\n\tconst comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n\treturn [\n\t\t...(comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n\t\t\t? [\"Only ordering differs; both lists contain the same paths.\", \"\"]\n\t\t\t: []),\n\t\t\"Remote-only paths:\",\n\t\t...(comparison.remoteOnly.length > 0\n\t\t\t? comparison.remoteOnly.map((item) => `+ ${safeTerminalText(item)}`)\n\t\t\t: [\"(none)\"]),\n\t\t\"\",\n\t\t\"Device-only paths:\",\n\t\t...(comparison.localOnly.length > 0\n\t\t\t? comparison.localOnly.map((item) => `- ${safeTerminalText(item)}`)\n\t\t\t: [\"(none)\"]),\n\t\t\"\",\n\t\t\"Remote ordered list:\",\n\t\t...(decision.remoteInclude.length > 0\n\t\t\t? decision.remoteInclude.map((item, index) => `${index + 1}. ${safeTerminalText(item)}`)\n\t\t\t: [\"(none)\"]),\n\t\t\"\",\n\t\t\"This device's ordered list:\",\n\t\t...(decision.localInclude.length > 0\n\t\t\t? decision.localInclude.map((item, index) => `${index + 1}. ${safeTerminalText(item)}`)\n\t\t\t: [\"(none)\"]),\n\t\t\"\",\n\t\t\"Using the remote list saves settings only and does not pull files.\",\n\t].join(\"\\n\");\n}\n\nfunction formatRemoteSelectionSummary(decision: RemoteSelectionDecision) {\n\tconst comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n\treturn [\n\t\t`Synced content for \u201C${safeTerminalText(decision.setupName)}\u201D differs from this device.`,\n\t\t`Remote-only: ${safeList(comparison.remoteOnly)}`,\n\t\t`Device-only: ${safeList(comparison.localOnly)}`,\n\t\t...(comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n\t\t\t? [\n\t\t\t\t\t\"Only ordering differs.\",\n\t\t\t\t\t`Remote order: ${safeList(decision.remoteInclude)}`,\n\t\t\t\t\t`Device order: ${safeList(decision.localInclude)}`,\n\t\t\t\t]\n\t\t\t: []),\n\t\t\"Run /sync in TUI to choose a content list; RPC review is read-only.\",\n\t].join(\"\\n\");\n}\n\nfunction formatLegacySummary(config: AnySyncConfig, discovered: readonly string[]) {\n\treturn `Remote snapshot for \u201C${safeTerminalText(config.setupName)}\u201D has no portable synced-content list; ${discovered.length} safe path${discovered.length === 1 ? \" was\" : \"s were\"} discovered, but the result is partial and read-only.`;\n}\n\nfunction decisionFromState(\n\tconfig: AnySyncConfig,\n\tstate: Extract<RemoteSelectionState, { kind: \"different\" }>,\n): RemoteSelectionDecision {\n\treturn {\n\t\tsetupName: config.setupName,\n\t\tconfigIdentity: syncConfigReviewFingerprint(config),\n\t\tlocalInclude: [...config.include],\n\t\tremoteInclude: [...state.include],\n\t};\n}\n\nfunction assertLocalSelectionCurrent(config: AnySyncConfig, decision: RemoteSelectionDecision) {\n\tif (\n\t\tsyncConfigReviewFingerprint(config) === decision.configIdentity &&\n\t\tsameSyncInclude(config.include, decision.localInclude)\n\t) {\n\t\treturn;\n\t}\n\tthrow new StaleRemoteSelectionReviewError(\n\t\t`Sync setup \u201C${safeTerminalText(config.setupName)}\u201D changed while the comparison was open.`,\n\t);\n}\n\nfunction sameContinuationReview(left: PartialConfig, right: PartialConfig) {\n\treturn (\n\t\tleft.setupName === right.setupName &&\n\t\tleft.connectionName === right.connectionName &&\n\t\tleft.storageKind === right.storageKind &&\n\t\tleft.storagePath === right.storagePath &&\n\t\tleft.bucket === right.bucket &&\n\t\tleft.branch === right.branch &&\n\t\tsameSyncInclude(left.include, right.include)\n\t);\n}\n\nfunction continueLabel(origin: RemoteSelectionOrigin) {\n\tif (origin === \"pull\") return \"Continue Pull now\u2026\";\n\tif (origin === \"push\") return \"Continue Push now\u2026\";\n\treturn \"Continue Sync now\u2026\";\n}\n\nfunction continueBusyLabel(origin: RemoteSelectionOrigin) {\n\tif (origin === \"pull\") return \"Checking remote changes\u2026\";\n\tif (origin === \"push\") return \"Preparing push preview\u2026\";\n\treturn \"Checking current sync setup\u2026\";\n}\n\nfunction continuationCancelledMessage(route: \"sync\" | \"pull\" | \"push\") {\n\tif (route === \"pull\") return \"Pull check cancelled; no local files were changed.\";\n\tif (route === \"push\") return \"Push preparation cancelled; no remote files were changed.\";\n\treturn \"Sync check cancelled; no settings or files were changed.\";\n}\n\nfunction safeList(values: readonly string[]) {\n\treturn values.length > 0 ? values.map(safeTerminalText).join(\", \") : \"none\";\n}\n\nclass StaleRemoteSelectionReviewError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"StaleRemoteSelectionReviewError\";\n\t}\n}\n\nfunction isStaleReviewError(error: unknown) {\n\treturn (\n\t\terror instanceof StaleRemoteSelectionReviewError || error instanceof SyncSetupReviewChangedError\n\t);\n}\n\nfunction runWithOptionalStateAccess<T>(\n\toptions: Pick<RemoteSelectionReviewOptions, \"withStateAccess\">,\n\ttask: () => Promise<T>,\n) {\n\treturn options.withStateAccess ? options.withStateAccess(task) : task();\n}\n\nfunction combineSignals(sessionSignal: AbortSignal | undefined, actionSignal: AbortSignal) {\n\treturn sessionSignal ? AbortSignal.any([sessionSignal, actionSignal]) : actionSignal;\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", "import {\n\tBorderedLoader,\n\ttype ExtensionContext,\n\ttype KeybindingsManager,\n} from \"@earendil-works/pi-coding-agent\";\nimport { Key, matchesKey, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { runCustomInteraction } from \"@narumitw/pi-tui-kit\";\nimport { safeTerminalText } from \"./manager-helpers.js\";\nimport type { SetupPullOutcome } from \"./setup-switch.js\";\nimport type { SyncDecision } from \"./sync-decision.js\";\nimport type { RemoteSelectionDecision } from \"./sync-policy.js\";\n\nexport type RunRouteResult =\n\t| { kind: \"completed\"; outcome?: SetupPullOutcome }\n\t| { kind: \"decision-required\"; decision: SyncDecision }\n\t| { kind: \"remote-selection-required\"; decision: RemoteSelectionDecision }\n\t| { kind: \"failed\" };\n\nexport type RunRoute = (\n\troute: string,\n\tsignal?: AbortSignal,\n\tonCommit?: () => void,\n\ttarget?: string,\n) => Promise<RunRouteResult | undefined>;\n\nexport type CancellableOperationResult =\n\t| RunRouteResult\n\t| { kind: \"closed\" }\n\t| { kind: \"cancelled\" };\n\ninterface CancellableOperationOptions {\n\tcommitAware?: boolean;\n\tcancelledMessage?: string | null;\n\ttarget?: string;\n\tsignal?: AbortSignal;\n}\n\nexport async function runCancellableOperation(\n\tctx: ExtensionContext,\n\tmessage: string,\n\troute: string,\n\trunRoute: RunRoute,\n\toptions: CancellableOperationOptions = {},\n): Promise<CancellableOperationResult> {\n\tconst {\n\t\tcommitAware = false,\n\t\tcancelledMessage = \"Check cancelled; no settings or files were changed.\",\n\t\ttarget,\n\t\tsignal,\n\t} = options;\n\tif (ctx.mode !== \"tui\") {\n\t\treturn (await runRoute(route, signal, undefined, target)) ?? { kind: \"failed\" };\n\t}\n\tlet commitStarted = false;\n\tlet routeResult: RunRouteResult | undefined;\n\tconst interaction = await runCustomInteraction<\n\t\t{ cancelled?: boolean; error?: unknown },\n\t\tExtensionContext\n\t>(ctx, {\n\t\tsignal,\n\t\tisCurrent: () => !signal?.aborted,\n\t\tcreate: ({ tui, theme, keybindings, signal: interactionSignal, complete }) => {\n\t\t\tconst loader = new BorderedLoader(tui, theme, message, { cancellable: false });\n\t\t\tconst cancelHint = `${keybindingText(keybindings, \"tui.select.cancel\", \"esc\")} cancel`;\n\t\t\tconst operation = runRoute(\n\t\t\t\troute,\n\t\t\t\tinteractionSignal,\n\t\t\t\tcommitAware ? () => (commitStarted = true) : undefined,\n\t\t\t\ttarget,\n\t\t\t).then(\n\t\t\t\t(result) => {\n\t\t\t\t\trouteResult = result;\n\t\t\t\t\tcomplete({});\n\t\t\t\t},\n\t\t\t\t(error: unknown) => complete({ error }),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\trender(width: number) {\n\t\t\t\t\tconst safeWidth = Math.max(1, width);\n\t\t\t\t\tconst lines = loader.render(safeWidth);\n\t\t\t\t\tconst bottomBorder = lines.at(-1);\n\t\t\t\t\treturn [\n\t\t\t\t\t\t...lines.slice(0, -1),\n\t\t\t\t\t\ttruncateToWidth(theme.fg(\"dim\", cancelHint), safeWidth, \"\"),\n\t\t\t\t\t\t...(bottomBorder === undefined ? [] : [bottomBorder]),\n\t\t\t\t\t];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => loader.invalidate(),\n\t\t\t\thandleInput(data: string) {\n\t\t\t\t\tif (!matchesKey(data, Key.ctrl(\"c\")) && !keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (commitStarted) {\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\t\"Applying or publishing has started and cannot be cancelled safely.\",\n\t\t\t\t\t\t\t\"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcomplete({ cancelled: true });\n\t\t\t\t},\n\t\t\t\tdispose: () => loader.dispose(),\n\t\t\t\twaitForPending: () => operation,\n\t\t\t};\n\t\t},\n\t});\n\tif (interaction.kind === \"error\") throw interaction.error;\n\tif (interaction.kind !== \"completed\") return { kind: \"closed\" };\n\tif (interaction.value.cancelled) {\n\t\tif (cancelledMessage) ctx.ui.notify(cancelledMessage, \"info\");\n\t\treturn { kind: \"cancelled\" };\n\t}\n\tif (interaction.value.error) throw interaction.value.error;\n\treturn routeResult ?? { kind: \"failed\" };\n}\n\nfunction keybindingText(\n\tkeybindings: Pick<KeybindingsManager, \"getKeys\">,\n\tbinding: Parameters<KeybindingsManager[\"getKeys\"]>[0],\n\tfallback: string,\n) {\n\tconst keys = [...new Set([...keybindings.getKeys(binding), \"ctrl+c\"])]\n\t\t.map(String)\n\t\t.map((key) => {\n\t\t\tif (key === \"return\") return \"enter\";\n\t\t\tif (key === \"escape\") return \"esc\";\n\t\t\treturn safeTerminalText(key);\n\t\t})\n\t\t.filter(Boolean);\n\treturn keys.join(\"/\") || fallback;\n}\n", "import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { isCloudflareR2Endpoint } from \"./config.js\";\n\nexport async function requiredExistingBucket(\n\tctx: ExtensionCommandContext,\n\texample: string,\n\tsignal?: AbortSignal,\n) {\n\tconst value = await ctx.ui.input(\"Existing bucket\", `Example: ${example}`, { signal });\n\tif (signal?.aborted) {\n\t\tthrow signal.reason instanceof Error\n\t\t\t? signal.reason\n\t\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n\t}\n\tif (value === undefined) return undefined;\n\tconst normalized = value.trim();\n\tif (!normalized) {\n\t\tctx.ui.notify(\"Enter the name of an existing R2/S3 bucket, or cancel setup.\", \"warning\");\n\t\treturn undefined;\n\t}\n\treturn normalized;\n}\n\nexport async function requiredInput(\n\tctx: ExtensionCommandContext,\n\ttitle: string,\n\tplaceholder: string,\n\tsignal?: AbortSignal,\n) {\n\tconst value = await ctx.ui.input(title, placeholder, { signal });\n\tif (signal?.aborted) {\n\t\tthrow signal.reason instanceof Error\n\t\t\t? signal.reason\n\t\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n\t}\n\tif (value === undefined) return undefined;\n\tconst normalized = value.trim() || placeholder;\n\treturn normalized.includes(\"<\") || normalized.includes(\">\") ? undefined : normalized;\n}\n\nexport function storageDescription(\n\tkind: string | undefined,\n\tendpoint: string | undefined,\n\tbucket: string | undefined,\n) {\n\tconst label =\n\t\tkind === \"r2\" || isCloudflareR2Endpoint(endpoint) ? \"Cloudflare R2\" : \"S3-compatible\";\n\treturn `${label} \u00B7 ${safeTerminalText(bucket ?? \"bucket missing\")}`;\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\nexport function safeTerminalText(value: string) {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n\nexport function errorMessage(error: unknown) {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"],
5
- "mappings": "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",
6
- "names": ["safeTerminalText", "errorMessage", "safeTerminalText"]
7
- }
@@ -1,167 +0,0 @@
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
- fileHashMap
5
- } from "./chunk-JF6MZCT4.ts";
6
- import {
7
- agentDir
8
- } from "./chunk-5T442OP3.ts";
9
- import {
10
- inspectRemoteSelection
11
- } from "./chunk-YQ6UW7IF.ts";
12
-
13
- // src/sync-errors.ts
14
- var SetupPullRequiresUiError = class extends Error {
15
- };
16
- var SyncDecisionRequiredError = class extends Error {
17
- decision;
18
- constructor(decision) {
19
- super(decision.directMessage);
20
- this.name = "SyncDecisionRequiredError";
21
- this.decision = decision;
22
- }
23
- };
24
- function isSyncDecisionRequiredError(error) {
25
- return error instanceof SyncDecisionRequiredError;
26
- }
27
- function errorMessage(error) {
28
- const message = error instanceof Error ? error.message : String(error);
29
- return message.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
30
- }
31
-
32
- // src/sync-format.ts
33
- function formatDiff(local, remote) {
34
- const localMap = fileHashMap(local);
35
- const remoteMap = fileHashMap(remote);
36
- const allPaths = [.../* @__PURE__ */ new Set([...Object.keys(localMap), ...Object.keys(remoteMap)])].sort();
37
- const lines = [
38
- `local: ${local.files.length} files`,
39
- `remote: ${remote.id} (${remote.files.length} files)`,
40
- ""
41
- ];
42
- let changed = 0;
43
- for (const filePath of allPaths) {
44
- if (!localMap[filePath]) {
45
- lines.push(`Remote only: ${filePath}`);
46
- changed += 1;
47
- } else if (!remoteMap[filePath]) {
48
- lines.push(`Local only: ${filePath}`);
49
- changed += 1;
50
- } else if (localMap[filePath] !== remoteMap[filePath]) {
51
- lines.push(`Different: ${filePath}`);
52
- changed += 1;
53
- }
54
- }
55
- if (changed === 0) lines.push("No file differences.");
56
- return lines.join("\n");
57
- }
58
- function formatSnapshotOnlyDiff(title, snapshot) {
59
- return [`${title}: ${snapshot.id}`, ...snapshot.files.map((file) => `Add: ${file.path}`)].join(
60
- "\n"
61
- );
62
- }
63
- function formatPushSummary(config, destination, upload, head, preservedRemoteFileCount = 0, remote) {
64
- return [
65
- `Sync setup: ${safeTerminalText(config.setupName)}`,
66
- `Storage location: ${safeTerminalText(destination)}`,
67
- `Upload ${upload.files.length} files from ${safeTerminalText(agentDir())}.`,
68
- `Sessions: ${upload.syncSessions ? "included \u2014 may contain private conversations" : "not included"}`,
69
- head ? `Remote latest: ${head.snapshotId}` : "Remote latest: empty",
70
- "Publication effect: the backend's active head will reference the new immutable snapshot.",
71
- ...remote && inspectRemoteSelection(config.include, remote).kind === "different" ? [
72
- "Included-content policy effect: replace the differing remote selection with this setup's local selection."
73
- ] : [],
74
- formatPublicationPreview(remote, upload),
75
- preservedRemoteFileCount > 0 ? `Possible secrets in locally managed files were scanned before this prompt; ${preservedRemoteFileCount} preserved remote file(s) were not rescanned.` : "Possible secrets were scanned before this prompt."
76
- ].join("\n");
77
- }
78
- function formatApplyPreview(local, remote) {
79
- return formatDirectionalChanges(local, remote, {
80
- add: "Add locally",
81
- update: "Update locally",
82
- remove: "Remove locally"
83
- });
84
- }
85
- function formatPullSummary(config, destination, local, remote, protectedSessionCount) {
86
- return [
87
- `Sync setup: ${safeTerminalText(config.setupName)}`,
88
- `Storage location: ${safeTerminalText(destination)}`,
89
- `Snapshot: ${safeTerminalText(remote.id)}`,
90
- `Sessions: ${remote.syncSessions ? "included \u2014 may contain private conversations" : "not included"}`,
91
- `Protected live sessions: ${protectedSessionCount || "none"}`,
92
- formatApplyPreview(local, remote),
93
- "A local backup is created before these writes/deletes. The remote active snapshot is unchanged."
94
- ].join("\n");
95
- }
96
- function formatRollbackSummary(config, destination, local, remote, requestedSnapshot, protectedSessionCount) {
97
- return [
98
- `Sync setup: ${safeTerminalText(config.setupName)}`,
99
- `Storage location: ${safeTerminalText(destination)}`,
100
- `Snapshot: ${safeTerminalText(requestedSnapshot)}`,
101
- `Sessions: ${remote.syncSessions ? "included \u2014 may contain private conversations" : "not included"}`,
102
- `Protected live sessions: ${protectedSessionCount || "none"}`,
103
- formatApplyPreview(local, remote),
104
- "A local backup is created before applying; the backend's active head will change."
105
- ].join("\n");
106
- }
107
- function formatPublicationPreview(remote, upload) {
108
- if (!remote) {
109
- return ["Remote is empty.", ...upload.files.map((file) => `Add remotely: ${file.path}`)].join(
110
- "\n"
111
- );
112
- }
113
- return formatDirectionalChanges(remote, upload, {
114
- add: "Add remotely",
115
- update: "Update remotely",
116
- remove: "Remove remotely"
117
- });
118
- }
119
- function formatDirectionalChanges(before, after, labels) {
120
- const beforeMap = fileHashMap(before);
121
- const afterMap = fileHashMap(after);
122
- const paths = [.../* @__PURE__ */ new Set([...Object.keys(beforeMap), ...Object.keys(afterMap)])].sort();
123
- const lines = [];
124
- for (const filePath of paths) {
125
- if (!beforeMap[filePath]) lines.push(`${labels.add}: ${filePath}`);
126
- else if (!afterMap[filePath]) lines.push(`${labels.remove}: ${filePath}`);
127
- else if (beforeMap[filePath] !== afterMap[filePath])
128
- lines.push(`${labels.update}: ${filePath}`);
129
- }
130
- if (lines.length === 0) lines.push("No file changes.");
131
- return lines.join("\n");
132
- }
133
- function countPreservedRemoteFiles(local, upload) {
134
- const localPaths = new Set(local.files.map((file) => file.path));
135
- return upload.files.filter((file) => !localPaths.has(file.path)).length;
136
- }
137
- function publicationCapabilityDescription(capability) {
138
- switch (capability) {
139
- case "lease-protected":
140
- return "lease-protected (exact expected-ref update)";
141
- case "atomic-conditional":
142
- return "atomic-conditional (verified atomic precondition)";
143
- case "conditional-required":
144
- return "conditional-required (read-only until atomic preconditions are verified)";
145
- case "read-check-write-verify":
146
- return "read-check-write-verify (visible races rejected; simultaneous unseen races remain possible)";
147
- }
148
- }
149
- function safeTerminalText(value) {
150
- return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
151
- }
152
-
153
- export {
154
- SetupPullRequiresUiError,
155
- SyncDecisionRequiredError,
156
- isSyncDecisionRequiredError,
157
- errorMessage,
158
- formatDiff,
159
- formatSnapshotOnlyDiff,
160
- formatPushSummary,
161
- formatPullSummary,
162
- formatRollbackSummary,
163
- countPreservedRemoteFiles,
164
- publicationCapabilityDescription,
165
- safeTerminalText
166
- };
167
- //# sourceMappingURL=chunk-C7LXZ3UH.ts.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/sync-errors.ts", "../../src/sync-format.ts"],
4
- "sourcesContent": ["import type { SyncDecision } from \"./sync-decision.js\";\n\nexport class SetupPullRequiresUiError extends Error {}\n\nexport class SyncDecisionRequiredError extends Error {\n\treadonly decision: SyncDecision;\n\n\tconstructor(decision: SyncDecision) {\n\t\tsuper(decision.directMessage);\n\t\tthis.name = \"SyncDecisionRequiredError\";\n\t\tthis.decision = decision;\n\t}\n}\n\nexport function isSyncDecisionRequiredError(error: unknown): error is SyncDecisionRequiredError {\n\treturn error instanceof SyncDecisionRequiredError;\n}\n\nexport function errorMessage(error: unknown) {\n\tconst message = error instanceof Error ? error.message : String(error);\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn message.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n", "import { agentDir } from \"./config.js\";\n\nexport { errorMessage } from \"./sync-errors.js\";\n\nimport type { PublicationCapability, RemoteHead } from \"./sync-backend.js\";\nimport { inspectRemoteSelection } from \"./sync-policy.js\";\nimport { fileHashMap } from \"./sync-state.js\";\nimport type { CommonSyncConfig, Snapshot } from \"./types.js\";\n\nexport function formatDiff(local: Snapshot, remote: Snapshot) {\n\tconst localMap = fileHashMap(local);\n\tconst remoteMap = fileHashMap(remote);\n\tconst allPaths = [...new Set([...Object.keys(localMap), ...Object.keys(remoteMap)])].sort();\n\tconst lines = [\n\t\t`local: ${local.files.length} files`,\n\t\t`remote: ${remote.id} (${remote.files.length} files)`,\n\t\t\"\",\n\t];\n\tlet changed = 0;\n\tfor (const filePath of allPaths) {\n\t\tif (!localMap[filePath]) {\n\t\t\tlines.push(`Remote only: ${filePath}`);\n\t\t\tchanged += 1;\n\t\t} else if (!remoteMap[filePath]) {\n\t\t\tlines.push(`Local only: ${filePath}`);\n\t\t\tchanged += 1;\n\t\t} else if (localMap[filePath] !== remoteMap[filePath]) {\n\t\t\tlines.push(`Different: ${filePath}`);\n\t\t\tchanged += 1;\n\t\t}\n\t}\n\tif (changed === 0) lines.push(\"No file differences.\");\n\treturn lines.join(\"\\n\");\n}\n\nexport function formatSnapshotOnlyDiff(title: string, snapshot: Snapshot) {\n\treturn [`${title}: ${snapshot.id}`, ...snapshot.files.map((file) => `Add: ${file.path}`)].join(\n\t\t\"\\n\",\n\t);\n}\n\nexport function formatPushSummary(\n\tconfig: CommonSyncConfig,\n\tdestination: string,\n\tupload: Snapshot,\n\thead: RemoteHead | undefined,\n\tpreservedRemoteFileCount = 0,\n\tremote?: Snapshot,\n) {\n\treturn [\n\t\t`Sync setup: ${safeTerminalText(config.setupName)}`,\n\t\t`Storage location: ${safeTerminalText(destination)}`,\n\t\t`Upload ${upload.files.length} files from ${safeTerminalText(agentDir())}.`,\n\t\t`Sessions: ${upload.syncSessions ? \"included \u2014 may contain private conversations\" : \"not included\"}`,\n\t\thead ? `Remote latest: ${head.snapshotId}` : \"Remote latest: empty\",\n\t\t\"Publication effect: the backend's active head will reference the new immutable snapshot.\",\n\t\t...(remote && inspectRemoteSelection(config.include, remote).kind === \"different\"\n\t\t\t? [\n\t\t\t\t\t\"Included-content policy effect: replace the differing remote selection with this setup's local selection.\",\n\t\t\t\t]\n\t\t\t: []),\n\t\tformatPublicationPreview(remote, upload),\n\t\tpreservedRemoteFileCount > 0\n\t\t\t? `Possible secrets in locally managed files were scanned before this prompt; ${preservedRemoteFileCount} preserved remote file(s) were not rescanned.`\n\t\t\t: \"Possible secrets were scanned before this prompt.\",\n\t].join(\"\\n\");\n}\n\nexport function formatApplyPreview(local: Snapshot, remote: Snapshot) {\n\treturn formatDirectionalChanges(local, remote, {\n\t\tadd: \"Add locally\",\n\t\tupdate: \"Update locally\",\n\t\tremove: \"Remove locally\",\n\t});\n}\n\nexport function formatPullSummary(\n\tconfig: CommonSyncConfig,\n\tdestination: string,\n\tlocal: Snapshot,\n\tremote: Snapshot,\n\tprotectedSessionCount: number,\n) {\n\treturn [\n\t\t`Sync setup: ${safeTerminalText(config.setupName)}`,\n\t\t`Storage location: ${safeTerminalText(destination)}`,\n\t\t`Snapshot: ${safeTerminalText(remote.id)}`,\n\t\t`Sessions: ${remote.syncSessions ? \"included \u2014 may contain private conversations\" : \"not included\"}`,\n\t\t`Protected live sessions: ${protectedSessionCount || \"none\"}`,\n\t\tformatApplyPreview(local, remote),\n\t\t\"A local backup is created before these writes/deletes. The remote active snapshot is unchanged.\",\n\t].join(\"\\n\");\n}\n\nexport function formatRollbackSummary(\n\tconfig: CommonSyncConfig,\n\tdestination: string,\n\tlocal: Snapshot,\n\tremote: Snapshot,\n\trequestedSnapshot: string,\n\tprotectedSessionCount: number,\n) {\n\treturn [\n\t\t`Sync setup: ${safeTerminalText(config.setupName)}`,\n\t\t`Storage location: ${safeTerminalText(destination)}`,\n\t\t`Snapshot: ${safeTerminalText(requestedSnapshot)}`,\n\t\t`Sessions: ${remote.syncSessions ? \"included \u2014 may contain private conversations\" : \"not included\"}`,\n\t\t`Protected live sessions: ${protectedSessionCount || \"none\"}`,\n\t\tformatApplyPreview(local, remote),\n\t\t\"A local backup is created before applying; the backend's active head will change.\",\n\t].join(\"\\n\");\n}\n\nfunction formatPublicationPreview(remote: Snapshot | undefined, upload: Snapshot) {\n\tif (!remote) {\n\t\treturn [\"Remote is empty.\", ...upload.files.map((file) => `Add remotely: ${file.path}`)].join(\n\t\t\t\"\\n\",\n\t\t);\n\t}\n\treturn formatDirectionalChanges(remote, upload, {\n\t\tadd: \"Add remotely\",\n\t\tupdate: \"Update remotely\",\n\t\tremove: \"Remove remotely\",\n\t});\n}\n\nfunction formatDirectionalChanges(\n\tbefore: Snapshot,\n\tafter: Snapshot,\n\tlabels: { add: string; update: string; remove: string },\n) {\n\tconst beforeMap = fileHashMap(before);\n\tconst afterMap = fileHashMap(after);\n\tconst paths = [...new Set([...Object.keys(beforeMap), ...Object.keys(afterMap)])].sort();\n\tconst lines: string[] = [];\n\tfor (const filePath of paths) {\n\t\tif (!beforeMap[filePath]) lines.push(`${labels.add}: ${filePath}`);\n\t\telse if (!afterMap[filePath]) lines.push(`${labels.remove}: ${filePath}`);\n\t\telse if (beforeMap[filePath] !== afterMap[filePath])\n\t\t\tlines.push(`${labels.update}: ${filePath}`);\n\t}\n\tif (lines.length === 0) lines.push(\"No file changes.\");\n\treturn lines.join(\"\\n\");\n}\n\nexport function countPreservedRemoteFiles(local: Snapshot, upload: Snapshot) {\n\tconst localPaths = new Set(local.files.map((file) => file.path));\n\treturn upload.files.filter((file) => !localPaths.has(file.path)).length;\n}\n\nexport function publicationCapabilityDescription(capability: PublicationCapability) {\n\tswitch (capability) {\n\t\tcase \"lease-protected\":\n\t\t\treturn \"lease-protected (exact expected-ref update)\";\n\t\tcase \"atomic-conditional\":\n\t\t\treturn \"atomic-conditional (verified atomic precondition)\";\n\t\tcase \"conditional-required\":\n\t\t\treturn \"conditional-required (read-only until atomic preconditions are verified)\";\n\t\tcase \"read-check-write-verify\":\n\t\t\treturn \"read-check-write-verify (visible races rejected; simultaneous unseen races remain possible)\";\n\t}\n}\n\nexport function safeTerminalText(value: string) {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n\nexport function redact(value: string) {\n\treturn value.length <= 8 ? \"configured\" : `${value.slice(0, 4)}\u2026${value.slice(-4)}`;\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/settings-management.ts"],
4
- "sourcesContent": ["import {\n\teffectiveSyncSetupRemoteIdentity,\n\tlocalConfigPath,\n\tnormalizeSyncInclude,\n\ttype SyncSetupStorageReview,\n\tsyncSetupStorageReview,\n\tupdateLocalConfig,\n\tvalidateConfigName,\n} from \"./config.js\";\nimport type { PiSyncSettingsV3, StorageConnectionSettings, SyncSetupSettings } from \"./types.js\";\n\nexport class SyncSetupReviewChangedError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"SyncSetupReviewChangedError\";\n\t}\n}\n\nexport async function saveNewV3Settings(\n\tinput: {\n\t\tsetupName: string;\n\t\tconnectionName: string;\n\t\tconnection: StorageConnectionSettings;\n\t\tsetup: SyncSetupSettings;\n\t},\n\tsignal?: AbortSignal,\n) {\n\tvalidateConfigName(input.setupName, \"sync setup\");\n\tvalidateConfigName(input.connectionName, \"storage connection\");\n\tconst settings: PiSyncSettingsV3 = {\n\t\tversion: 3,\n\t\tactiveSyncSetup: input.setupName,\n\t\tonSwitch: \"ask-before-pull\",\n\t\tskipSecretScan: false,\n\t\tstorageConnections: { [input.connectionName]: structuredClone(input.connection) },\n\t\tsyncSetups: {\n\t\t\t[input.setupName]: {\n\t\t\t\t...structuredClone(input.setup),\n\t\t\t\tstorage: { ...input.setup.storage, connection: input.connectionName },\n\t\t\t},\n\t\t},\n\t};\n\tawait updateLocalConfig((current) => {\n\t\tif (Object.keys(current.storageConnections).length || Object.keys(current.syncSetups).length) {\n\t\t\tthrow new Error(`Settings already exist: ${localConfigPath()}`);\n\t\t}\n\t\treturn settings;\n\t}, signal);\n\treturn settings;\n}\n\nexport async function addStorageConnection(\n\tname: string,\n\tconnection: StorageConnectionSettings,\n\tsignal?: AbortSignal,\n) {\n\tvalidateConfigName(name, \"storage connection\");\n\tawait updateSettings((settings) => {\n\t\tif (Object.hasOwn(settings.storageConnections, name)) {\n\t\t\tthrow new Error(`Storage connection already exists: ${name}`);\n\t\t}\n\t\treturn {\n\t\t\t...settings,\n\t\t\tstorageConnections: {\n\t\t\t\t...settings.storageConnections,\n\t\t\t\t[name]: structuredClone(connection),\n\t\t\t},\n\t\t};\n\t}, signal);\n}\n\nexport async function updateStorageConnection(\n\tname: string,\n\tupdate: (connection: StorageConnectionSettings) => StorageConnectionSettings,\n\texpectedSetups?: readonly string[],\n\tsignal?: AbortSignal,\n) {\n\tvalidateConfigName(name, \"storage connection\");\n\tawait updateSettings((settings) => {\n\t\tconst connection = settings.storageConnections[name];\n\t\tif (!connection) throw new Error(`Storage connection not found: ${name}`);\n\t\tconst currentSetups = referencingSetupNames(settings.syncSetups, name);\n\t\tif (expectedSetups && !sameNames(currentSetups, expectedSetups)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Storage connection \u201C${name}\u201D usage changed while it was open; reopen it and review the affected sync setups.`,\n\t\t\t);\n\t\t}\n\t\tconst nextConnection = update(structuredClone(connection));\n\t\tconst nextConnections = { ...settings.storageConnections, [name]: nextConnection };\n\t\tassertUniqueLocations(settings.syncSetups, nextConnections);\n\t\treturn { ...settings, storageConnections: nextConnections };\n\t}, signal);\n}\n\nexport async function addSyncSetup(name: string, setup: SyncSetupSettings, signal?: AbortSignal) {\n\tvalidateConfigName(name, \"sync setup\");\n\tawait updateSettings((settings) => {\n\t\tif (Object.hasOwn(settings.syncSetups, name))\n\t\t\tthrow new Error(`Sync setup already exists: ${name}`);\n\t\tif (!Object.hasOwn(settings.storageConnections, setup.storage.connection)) {\n\t\t\tthrow new Error(`Storage connection not found: ${setup.storage.connection}`);\n\t\t}\n\t\tconst nextSetups = { ...settings.syncSetups, [name]: structuredClone(setup) };\n\t\tassertUniqueLocations(nextSetups, settings.storageConnections);\n\t\treturn {\n\t\t\t...settings,\n\t\t\tsyncSetups: nextSetups,\n\t\t\t...(settings.activeSyncSetup ? {} : { activeSyncSetup: name }),\n\t\t};\n\t}, signal);\n}\n\nexport async function updateSyncSetup(\n\tname: string,\n\tupdate: (setup: SyncSetupSettings) => SyncSetupSettings,\n\toptions: {\n\t\texpectedStorage?: SyncSetupStorageReview;\n\t\texpectedInclude?: readonly string[];\n\t\tsignal?: AbortSignal;\n\t} = {},\n) {\n\tvalidateConfigName(name, \"sync setup\");\n\tawait updateSettings((settings) => {\n\t\tconst setup = settings.syncSetups[name];\n\t\tif (!setup) throw new Error(`Sync setup not found: ${name}`);\n\t\tif (\n\t\t\toptions.expectedInclude &&\n\t\t\t!sameNames(normalizeSyncInclude(setup.sync.include), options.expectedInclude)\n\t\t) {\n\t\t\tthrow new SyncSetupReviewChangedError(\n\t\t\t\t`Sync setup \u201C${name}\u201D included content changed while it was open; reopen it and review the current selection.`,\n\t\t\t);\n\t\t}\n\t\tif (options.expectedStorage) {\n\t\t\tconst connectionName = setup.storage.connection;\n\t\t\tconst connection = settings.storageConnections[connectionName];\n\t\t\tif (\n\t\t\t\t!connection ||\n\t\t\t\t!sameStorageReview(\n\t\t\t\t\tsyncSetupStorageReview(name, setup, connectionName, connection),\n\t\t\t\t\toptions.expectedStorage,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new SyncSetupReviewChangedError(\n\t\t\t\t\t`Sync setup \u201C${name}\u201D storage changed while it was open; reopen it and review the current storage location.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst nextSetup = update(structuredClone(setup));\n\t\tif (!Object.hasOwn(settings.storageConnections, nextSetup.storage.connection)) {\n\t\t\tthrow new Error(`Storage connection not found: ${nextSetup.storage.connection}`);\n\t\t}\n\t\tconst nextSetups = { ...settings.syncSetups, [name]: nextSetup };\n\t\tassertUniqueLocations(nextSetups, settings.storageConnections);\n\t\treturn { ...settings, syncSetups: nextSetups };\n\t}, options.signal);\n}\n\nexport async function removeSyncSetup(name: string, signal?: AbortSignal) {\n\tvalidateConfigName(name, \"sync setup\");\n\tawait updateSettings((settings) => {\n\t\tif (!Object.hasOwn(settings.syncSetups, name)) throw new Error(`Sync setup not found: ${name}`);\n\t\tconst isCurrent = settings.activeSyncSetup === name;\n\t\tif (isCurrent && Object.keys(settings.syncSetups).length > 1) {\n\t\t\tthrow new Error(\"Switch to another sync setup before removing the current setup.\");\n\t\t}\n\t\tconst syncSetups = { ...settings.syncSetups };\n\t\tdelete syncSetups[name];\n\t\tconst next = { ...settings, syncSetups };\n\t\tif (isCurrent) delete next.activeSyncSetup;\n\t\treturn next;\n\t}, signal);\n}\n\nexport async function removeStorageConnection(name: string, signal?: AbortSignal) {\n\tvalidateConfigName(name, \"storage connection\");\n\tawait updateSettings((settings) => {\n\t\tconst referenced = referencingSetupNames(settings.syncSetups, name)[0];\n\t\tif (referenced) {\n\t\t\tthrow new Error(`Storage connection \u201C${name}\u201D is used by sync setup \u201C${referenced}\u201D.`);\n\t\t}\n\t\tif (!Object.hasOwn(settings.storageConnections, name)) {\n\t\t\tthrow new Error(`Storage connection not found: ${name}`);\n\t\t}\n\t\tconst storageConnections = { ...settings.storageConnections };\n\t\tdelete storageConnections[name];\n\t\treturn { ...settings, storageConnections };\n\t}, signal);\n}\n\nasync function updateSettings(\n\tupdate: (settings: PiSyncSettingsV3) => PiSyncSettingsV3,\n\tsignal?: AbortSignal,\n) {\n\treturn updateLocalConfig((settings) => {\n\t\tif (settings.version !== 3) {\n\t\t\tthrow new Error(\"Storage connections and sync setups require version 3 pi-sync settings.\");\n\t\t}\n\t\treturn update(settings);\n\t}, signal);\n}\n\nfunction referencingSetupNames(setups: Record<string, SyncSetupSettings>, connection: string) {\n\treturn Object.entries(setups)\n\t\t.filter(([, setup]) => setup.storage.connection === connection)\n\t\t.map(([name]) => name)\n\t\t.sort((left, right) => left.localeCompare(right));\n}\n\nfunction assertUniqueLocations(\n\tsetups: Record<string, SyncSetupSettings>,\n\tconnections: Record<string, StorageConnectionSettings>,\n) {\n\tconst identities = new Map<string, string>();\n\tfor (const [name, setup] of Object.entries(setups)) {\n\t\tconst connection = connections[setup.storage.connection];\n\t\tif (!connection) throw new Error(`Storage connection not found: ${setup.storage.connection}`);\n\t\tconst identity = effectiveSyncSetupRemoteIdentity(setup, connection);\n\t\tconst previous = identities.get(identity);\n\t\tif (previous) {\n\t\t\tthrow new Error(`Sync setup \u201C${name}\u201D duplicates the storage location of \u201C${previous}\u201D.`);\n\t\t}\n\t\tidentities.set(identity, name);\n\t}\n}\n\nfunction sameNames(left: readonly string[], right: readonly string[]) {\n\treturn left.length === right.length && left.every((name, index) => name === right[index]);\n}\n\nfunction sameStorageReview(left: SyncSetupStorageReview, right: SyncSetupStorageReview) {\n\treturn (\n\t\tleft.connectionName === right.connectionName &&\n\t\tleft.storageKind === right.storageKind &&\n\t\tleft.storagePath === right.storagePath &&\n\t\tleft.bucket === right.bucket &&\n\t\tleft.branch === right.branch\n\t);\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,82 +0,0 @@
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
- safeTerminalText
5
- } from "./chunk-C7LXZ3UH.ts";
6
- import {
7
- syncConfigReviewFingerprint
8
- } from "./chunk-5T442OP3.ts";
9
- import {
10
- compareSyncInclude,
11
- sameSyncInclude
12
- } from "./chunk-YQ6UW7IF.ts";
13
-
14
- // src/sync-attention.ts
15
- import { truncateToWidth } from "@earendil-works/pi-tui";
16
- var STATUS_KEY = "sync";
17
- var WIDGET_KEY = "sync:attention";
18
- function syncAttentionMatchesConfig(attention, config) {
19
- return attention.decision.setupName === config.setupName && attention.decision.configIdentity === syncConfigReviewFingerprint(config) && sameSyncInclude(attention.decision.localInclude, config.include);
20
- }
21
- function createSyncAttentionController() {
22
- let state;
23
- return {
24
- set(decision, origin) {
25
- state = { decision, origin, offered: false };
26
- },
27
- current() {
28
- return state;
29
- },
30
- markOffered() {
31
- if (!state || state.offered) return false;
32
- state = { ...state, offered: true };
33
- return true;
34
- },
35
- clear(ctx) {
36
- state = void 0;
37
- clearAttentionPresentation(ctx);
38
- },
39
- reset(ctx) {
40
- state = void 0;
41
- clearAttentionPresentation(ctx);
42
- },
43
- publish(ctx) {
44
- if (!state) {
45
- clearAttentionPresentation(ctx);
46
- return;
47
- }
48
- const presentation = attentionPresentation(state.decision);
49
- ctx.ui.setStatus(STATUS_KEY, presentation.status);
50
- if (ctx.mode !== "tui") return;
51
- ctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => ({
52
- render(width) {
53
- const safeWidth = Math.max(1, width);
54
- return presentation.lines.map(
55
- (line, index) => truncateToWidth(theme.fg(index === 0 ? "warning" : "muted", line), safeWidth, "\u2026")
56
- );
57
- },
58
- invalidate() {
59
- }
60
- }));
61
- }
62
- };
63
- }
64
- function attentionPresentation(decision) {
65
- const setupName = safeTerminalText(decision.setupName);
66
- const comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);
67
- const difference = comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0 ? "Only list order differs" : `Remote ${comparison.remoteOnly.length} \xB7 Device ${comparison.localOnly.length}`;
68
- return {
69
- status: "review needed",
70
- lines: [`Pi Sync needs review \xB7 ${setupName}`, difference, "No changes \xB7 Run /sync to review"]
71
- };
72
- }
73
- function clearAttentionPresentation(ctx) {
74
- ctx.ui.setStatus(STATUS_KEY, void 0);
75
- ctx.ui.setWidget(WIDGET_KEY, void 0);
76
- }
77
-
78
- export {
79
- syncAttentionMatchesConfig,
80
- createSyncAttentionController
81
- };
82
- //# sourceMappingURL=chunk-E4COSV32.ts.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/sync-attention.ts"],
4
- "sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { syncConfigReviewFingerprint } from \"./config.js\";\nimport { safeTerminalText } from \"./sync-format.js\";\nimport {\n\tcompareSyncInclude,\n\ttype RemoteSelectionDecision,\n\tsameSyncInclude,\n} from \"./sync-policy.js\";\nimport type { AnySyncConfig } from \"./types.js\";\n\nconst STATUS_KEY = \"sync\";\nconst WIDGET_KEY = \"sync:attention\";\n\nexport type SyncAttentionOrigin = \"sync\" | \"pull\" | \"push\";\n\nexport interface SyncAttentionState {\n\tdecision: RemoteSelectionDecision;\n\torigin: SyncAttentionOrigin;\n\toffered: boolean;\n}\n\nexport function syncAttentionMatchesConfig(attention: SyncAttentionState, config: AnySyncConfig) {\n\treturn (\n\t\tattention.decision.setupName === config.setupName &&\n\t\tattention.decision.configIdentity === syncConfigReviewFingerprint(config) &&\n\t\tsameSyncInclude(attention.decision.localInclude, config.include)\n\t);\n}\n\nexport interface SyncAttentionController {\n\tset(decision: RemoteSelectionDecision, origin: SyncAttentionOrigin): void;\n\tcurrent(): SyncAttentionState | undefined;\n\tmarkOffered(): boolean;\n\tclear(ctx: ExtensionContext): void;\n\treset(ctx: ExtensionContext): void;\n\tpublish(ctx: ExtensionContext): void;\n}\n\nexport function createSyncAttentionController(): SyncAttentionController {\n\tlet state: SyncAttentionState | undefined;\n\n\treturn {\n\t\tset(decision, origin) {\n\t\t\tstate = { decision, origin, offered: false };\n\t\t},\n\t\tcurrent() {\n\t\t\treturn state;\n\t\t},\n\t\tmarkOffered() {\n\t\t\tif (!state || state.offered) return false;\n\t\t\tstate = { ...state, offered: true };\n\t\t\treturn true;\n\t\t},\n\t\tclear(ctx) {\n\t\t\tstate = undefined;\n\t\t\tclearAttentionPresentation(ctx);\n\t\t},\n\t\treset(ctx) {\n\t\t\tstate = undefined;\n\t\t\tclearAttentionPresentation(ctx);\n\t\t},\n\t\tpublish(ctx) {\n\t\t\tif (!state) {\n\t\t\t\tclearAttentionPresentation(ctx);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst presentation = attentionPresentation(state.decision);\n\t\t\tctx.ui.setStatus(STATUS_KEY, presentation.status);\n\t\t\tif (ctx.mode !== \"tui\") return;\n\t\t\tctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => ({\n\t\t\t\trender(width: number) {\n\t\t\t\t\tconst safeWidth = Math.max(1, width);\n\t\t\t\t\treturn presentation.lines.map((line, index) =>\n\t\t\t\t\t\ttruncateToWidth(theme.fg(index === 0 ? \"warning\" : \"muted\", line), safeWidth, \"\u2026\"),\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tinvalidate() {},\n\t\t\t}));\n\t\t},\n\t};\n}\n\nfunction attentionPresentation(decision: RemoteSelectionDecision) {\n\tconst setupName = safeTerminalText(decision.setupName);\n\tconst comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n\tconst difference =\n\t\tcomparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n\t\t\t? \"Only list order differs\"\n\t\t\t: `Remote ${comparison.remoteOnly.length} \u00B7 Device ${comparison.localOnly.length}`;\n\treturn {\n\t\tstatus: \"review needed\",\n\t\tlines: [`Pi Sync needs review \u00B7 ${setupName}`, difference, \"No changes \u00B7 Run /sync to review\"],\n\t};\n}\n\nfunction clearAttentionPresentation(ctx: ExtensionContext) {\n\tctx.ui.setStatus(STATUS_KEY, undefined);\n\tctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;AACA,SAAS,uBAAuB;AAUhC,IAAM,aAAa;AACnB,IAAM,aAAa;AAUZ,SAAS,2BAA2B,WAA+B,QAAuB;AAChG,SACC,UAAU,SAAS,cAAc,OAAO,aACxC,UAAU,SAAS,mBAAmB,4BAA4B,MAAM,KACxE,gBAAgB,UAAU,SAAS,cAAc,OAAO,OAAO;AAEjE;AAWO,SAAS,gCAAyD;AACxE,MAAI;AAEJ,SAAO;AAAA,IACN,IAAI,UAAU,QAAQ;AACrB,cAAQ,EAAE,UAAU,QAAQ,SAAS,MAAM;AAAA,IAC5C;AAAA,IACA,UAAU;AACT,aAAO;AAAA,IACR;AAAA,IACA,cAAc;AACb,UAAI,CAAC,SAAS,MAAM,QAAS,QAAO;AACpC,cAAQ,EAAE,GAAG,OAAO,SAAS,KAAK;AAClC,aAAO;AAAA,IACR;AAAA,IACA,MAAM,KAAK;AACV,cAAQ;AACR,iCAA2B,GAAG;AAAA,IAC/B;AAAA,IACA,MAAM,KAAK;AACV,cAAQ;AACR,iCAA2B,GAAG;AAAA,IAC/B;AAAA,IACA,QAAQ,KAAK;AACZ,UAAI,CAAC,OAAO;AACX,mCAA2B,GAAG;AAC9B;AAAA,MACD;AACA,YAAM,eAAe,sBAAsB,MAAM,QAAQ;AACzD,UAAI,GAAG,UAAU,YAAY,aAAa,MAAM;AAChD,UAAI,IAAI,SAAS,MAAO;AACxB,UAAI,GAAG,UAAU,YAAY,CAAC,MAAM,WAAW;AAAA,QAC9C,OAAO,OAAe;AACrB,gBAAM,YAAY,KAAK,IAAI,GAAG,KAAK;AACnC,iBAAO,aAAa,MAAM;AAAA,YAAI,CAAC,MAAM,UACpC,gBAAgB,MAAM,GAAG,UAAU,IAAI,YAAY,SAAS,IAAI,GAAG,WAAW,QAAG;AAAA,UAClF;AAAA,QACD;AAAA,QACA,aAAa;AAAA,QAAC;AAAA,MACf,EAAE;AAAA,IACH;AAAA,EACD;AACD;AAEA,SAAS,sBAAsB,UAAmC;AACjE,QAAM,YAAY,iBAAiB,SAAS,SAAS;AACrD,QAAM,aAAa,mBAAmB,SAAS,cAAc,SAAS,aAAa;AACnF,QAAM,aACL,WAAW,WAAW,WAAW,KAAK,WAAW,UAAU,WAAW,IACnE,4BACA,UAAU,WAAW,WAAW,MAAM,gBAAa,WAAW,UAAU,MAAM;AAClF,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,OAAO,CAAC,6BAA0B,SAAS,IAAI,YAAY,qCAAkC;AAAA,EAC9F;AACD;AAEA,SAAS,2BAA2B,KAAuB;AAC1D,MAAI,GAAG,UAAU,YAAY,MAAS;AACtC,MAAI,GAAG,UAAU,YAAY,MAAS;AACvC;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/git-config.ts", "../../src/state-directory.ts", "../../src/lock-policy.ts", "../../src/lockfile-fs.ts"],
4
- "sourcesContent": ["const DEFAULT_GIT_BRANCH = \"pi-sync\";\nconst DEFAULT_GIT_DIRECTORY = \"pi-sync\";\n\nexport function normalizeGitRemote(value: string | undefined) {\n\tconst normalized = normalizeOptionalString(value);\n\tif (!normalized) return undefined;\n\tif (\n\t\thasControlCharacter(normalized) ||\n\t\t/\\s/u.test(normalized) ||\n\t\tnormalized.startsWith(\"-\") ||\n\t\tnormalized.includes(\"\\\\\")\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync Git remote.\");\n\t}\n\tif (\n\t\t!normalized.includes(\"://\") &&\n\t\t/^(?:[A-Za-z0-9._-]+@)?(?:\\[[0-9A-Fa-f:]+\\]|[A-Za-z0-9.-]+):(?!-)[^:].+$/u.test(normalized)\n\t) {\n\t\treturn normalized;\n\t}\n\tif (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(normalized) && !normalized.includes(\"://\")) {\n\t\tthrow new Error(\"Invalid pi-sync Git remote: unsupported transport or remote-helper syntax.\");\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(normalized);\n\t} catch {\n\t\tthrow new Error(\"Invalid pi-sync Git remote: use an SSH or HTTPS remote.\");\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"ssh:\") {\n\t\tthrow new Error(\"Invalid pi-sync Git remote: only SSH and HTTPS are supported.\");\n\t}\n\tif (url.password || (url.protocol === \"https:\" && url.username)) {\n\t\tthrow new Error(\"Invalid pi-sync Git remote: URL credentials or userinfo are not allowed.\");\n\t}\n\tif (url.search || url.hash || !url.hostname || !url.pathname || url.pathname === \"/\") {\n\t\tthrow new Error(\"Invalid pi-sync Git remote.\");\n\t}\n\treturn url.toString();\n}\n\nexport function normalizeGitRemoteIdentity(value: string) {\n\tconst normalized = normalizeGitRemote(value);\n\tif (!normalized) return \"\";\n\tif (!normalized.includes(\"://\")) {\n\t\tconst match =\n\t\t\t/^(?<user>[A-Za-z0-9._-]+@)?(?<host>\\[[0-9A-Fa-f:]+\\]|[A-Za-z0-9.-]+):(?<path>.+)$/u.exec(\n\t\t\t\tnormalized,\n\t\t\t);\n\t\tif (!match?.groups) return normalized;\n\t\tconst userAndHost = `${match.groups.user ?? \"\"}${match.groups.host.toLowerCase()}`;\n\t\tconst remotePath = match.groups.path.replace(/\\/+$/gu, \"\");\n\t\treturn remotePath.startsWith(\"/\")\n\t\t\t? `scp-absolute://${userAndHost}${remotePath}`\n\t\t\t: `ssh://${userAndHost}/${remotePath}`;\n\t}\n\tconst url = new URL(normalized);\n\turl.hostname = url.hostname.toLowerCase();\n\tif (url.protocol === \"ssh:\" && url.port === \"22\") url.port = \"\";\n\turl.pathname = url.pathname.replace(/\\/+$/gu, \"\");\n\tif (url.protocol === \"ssh:\") {\n\t\treturn `ssh://${url.username ? `${url.username}@` : \"\"}${url.host}${url.pathname}`;\n\t}\n\treturn url.toString();\n}\n\nexport function normalizeGitBranch(value: string | undefined) {\n\tconst branch = normalizeOptionalString(value) ?? DEFAULT_GIT_BRANCH;\n\tif (\n\t\tbranch === \"@\" ||\n\t\tbranch.startsWith(\"-\") ||\n\t\tbranch.startsWith(\"refs/\") ||\n\t\tbranch.startsWith(\"/\") ||\n\t\tbranch.endsWith(\"/\") ||\n\t\tbranch.endsWith(\".\") ||\n\t\tbranch.includes(\"..\") ||\n\t\tbranch.includes(\"@{\") ||\n\t\tbranch.includes(\"\\\\\") ||\n\t\thasControlCharacter(branch) ||\n\t\t/[ ~^:?*[\\]]/u.test(branch) ||\n\t\tbranch\n\t\t\t.split(\"/\")\n\t\t\t.some((segment) => !segment || segment.startsWith(\".\") || segment.endsWith(\".lock\"))\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync Git branch.\");\n\t}\n\treturn branch;\n}\n\nexport function normalizeGitDirectory(value: string | undefined) {\n\tconst directory = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_GIT_DIRECTORY);\n\tif (\n\t\t!directory ||\n\t\tdirectory.startsWith(\"-\") ||\n\t\tdirectory.includes(\"\\\\\") ||\n\t\thasControlCharacter(directory) ||\n\t\tdirectory\n\t\t\t.split(\"/\")\n\t\t\t.some((segment) => !segment || segment === \".\" || segment === \"..\" || segment === \".git\")\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync Git directory.\");\n\t}\n\treturn directory;\n}\n\nexport function validateGitNamespace(value: string) {\n\tif (\n\t\tvalue.length > 256 ||\n\t\tvalue === \".\" ||\n\t\tvalue === \"..\" ||\n\t\tvalue.includes(\"/\") ||\n\t\tvalue.includes(\"\\\\\") ||\n\t\thasControlCharacter(value)\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync Git namespace.\");\n\t}\n}\n\nfunction normalizeOptionalString(value: unknown) {\n\tif (value !== undefined && typeof value !== \"string\") {\n\t\tthrow new Error(\"Invalid pi-sync settings: expected a string.\");\n\t}\n\tconst normalized = value?.trim();\n\treturn normalized || undefined;\n}\n\nfunction trimSlashes(value: string) {\n\treturn value.replace(/^\\/+|\\/+$/gu, \"\");\n}\n\nfunction hasControlCharacter(value: string) {\n\treturn [...value].some((character) => {\n\t\tconst code = character.codePointAt(0) ?? 0;\n\t\treturn code < 0x20 || (code >= 0x7f && code <= 0x9f);\n\t});\n}\n", "import { lstatSync } 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\";\nimport { LOCK_GUARD_STALE_MS } from \"./lock-policy.js\";\nimport { LOCKFILE_FS_ADAPTER } from \"./lockfile-fs.js\";\n\nconst CANONICAL_DIRECTORY_NAME = \"pi-sync\";\nconst LEGACY_DIRECTORY_NAME = \".pisync\";\nconst MIGRATION_LOCK_NAME = \".pi-sync-state-migration.lock\";\nconst MIGRATION_LOCK_STALE_MS = 30_000;\nconst MIGRATION_LOCK_UPDATE_MS = 10_000;\n\nexport type StateDirectoryPreparation =\n\t| { status: \"ready\" }\n\t| { status: \"migrated\"; message: string }\n\t| { status: \"deferred\"; message: string };\n\ninterface StateDirectoryGuard {\n\trelease: () => Promise<void>;\n\tthrowIfCompromised: () => void;\n}\n\ninterface SharedStateDirectoryGuard {\n\tguard: Promise<StateDirectoryGuard>;\n\tusers: number;\n\tclosing?: Promise<void>;\n}\n\nconst sharedStateDirectoryGuards = new Map<string, SharedStateDirectoryGuard>();\n\nexport function stateDir() {\n\tconst roots = inspectStateRoots();\n\tif (roots.canonical) return canonicalStateDir();\n\tif (roots.legacy) return legacyStateDir();\n\treturn canonicalStateDir();\n}\n\nexport function legacyStateDir() {\n\treturn path.join(getAgentDir(), LEGACY_DIRECTORY_NAME);\n}\n\nexport function stateDirectoryMigrationNotice() {\n\tconst roots = inspectStateRoots();\n\tif (!roots.legacy) return undefined;\n\treturn \"Legacy pi-sync state is still stored in .pisync. Close other Pi sessions, then run /sync migrate-state to move it to pi-sync/.\";\n}\n\nexport async function withStateDirectoryAccess<T>(fn: () => Promise<T>): Promise<T> {\n\tconst roots = inspectStateRoots();\n\tif (!roots.legacy) return fn();\n\treturn runWithStateDirectoryGuard(await acquireSharedStateDirectoryGuard(), fn);\n}\n\nexport async function migrateLegacyStateDirectory(): Promise<StateDirectoryPreparation> {\n\tconst initial = inspectStateRoots();\n\tif (initial.canonical || !initial.legacy) return { status: \"ready\" };\n\n\tlet guard: StateDirectoryGuard;\n\ttry {\n\t\tguard = await acquireStateDirectoryGuard();\n\t} catch (error) {\n\t\tif (!isLockHeldError(error)) throw error;\n\t\treturn {\n\t\t\tstatus: \"deferred\",\n\t\t\tmessage:\n\t\t\t\t\"pi-sync state migration was deferred because another state user is active. Close other Pi sessions and retry.\",\n\t\t};\n\t}\n\treturn runWithStateDirectoryGuard(guard, async () => {\n\t\tconst roots = inspectStateRoots();\n\t\tif (roots.canonical || !roots.legacy) return { status: \"ready\" };\n\n\t\tconst legacyLock = path.join(legacyStateDir(), \"lock\");\n\t\tconst legacyGuardHeld = await lockfile.check(legacyLock, {\n\t\t\tfs: LOCKFILE_FS_ADAPTER,\n\t\t\tlockfilePath: `${legacyLock}.guard`,\n\t\t\trealpath: false,\n\t\t\tstale: LOCK_GUARD_STALE_MS,\n\t\t});\n\t\tif (legacyGuardHeld || (await pathExists(legacyLock))) {\n\t\t\treturn {\n\t\t\t\tstatus: \"deferred\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"pi-sync state migration was deferred because the legacy directory is busy. Close other Pi sessions, clear any confirmed stale sync lock, and retry.\",\n\t\t\t};\n\t\t}\n\t\tguard.throwIfCompromised();\n\t\tawait fs.rename(legacyStateDir(), canonicalStateDir());\n\t\tguard.throwIfCompromised();\n\t\tinspectStateRoots();\n\t\treturn {\n\t\t\tstatus: \"migrated\",\n\t\t\tmessage: `Migrated pi-sync state from ${legacyStateDir()} to ${canonicalStateDir()}.`,\n\t\t};\n\t});\n}\n\nasync function acquireSharedStateDirectoryGuard(): Promise<StateDirectoryGuard> {\n\tconst key = migrationLockPath();\n\tfor (;;) {\n\t\tlet shared = sharedStateDirectoryGuards.get(key);\n\t\tif (shared?.closing) {\n\t\t\tawait shared.closing;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!shared) {\n\t\t\tshared = { guard: acquireStateDirectoryGuard(), users: 0 };\n\t\t\tsharedStateDirectoryGuards.set(key, shared);\n\t\t}\n\t\tshared.users += 1;\n\t\tlet guard: StateDirectoryGuard;\n\t\ttry {\n\t\t\tguard = await shared.guard;\n\t\t} catch (error) {\n\t\t\tif (sharedStateDirectoryGuards.get(key) === shared) {\n\t\t\t\tsharedStateDirectoryGuards.delete(key);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tlet released = false;\n\t\treturn {\n\t\t\tthrowIfCompromised: guard.throwIfCompromised,\n\t\t\trelease: async () => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tshared.users -= 1;\n\t\t\t\tif (shared.users > 0) return;\n\t\t\t\tconst closing = guard.release();\n\t\t\t\tshared.closing = closing;\n\t\t\t\ttry {\n\t\t\t\t\tawait closing;\n\t\t\t\t} finally {\n\t\t\t\t\tif (sharedStateDirectoryGuards.get(key) === shared) {\n\t\t\t\t\t\tsharedStateDirectoryGuards.delete(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n}\n\nasync function acquireStateDirectoryGuard(): Promise<StateDirectoryGuard> {\n\tlet compromisedError: Error | undefined;\n\tconst release = await lockfile.lock(getAgentDir(), {\n\t\tfs: LOCKFILE_FS_ADAPTER,\n\t\tlockfilePath: migrationLockPath(),\n\t\trealpath: false,\n\t\tstale: MIGRATION_LOCK_STALE_MS,\n\t\tupdate: MIGRATION_LOCK_UPDATE_MS,\n\t\tretries: { retries: 20, minTimeout: 10, maxTimeout: 50 },\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};\n}\n\nasync function runWithStateDirectoryGuard<T>(guard: StateDirectoryGuard, fn: () => Promise<T>) {\n\tlet result: T | undefined;\n\tlet failed = false;\n\tlet failure: unknown;\n\ttry {\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\ttry {\n\t\tawait guard.release();\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 (failed) throw failure;\n\treturn result as T;\n}\n\nfunction isLockHeldError(error: unknown) {\n\treturn (error as NodeJS.ErrnoException).code === \"ELOCKED\";\n}\n\nfunction canonicalStateDir() {\n\treturn path.join(getAgentDir(), CANONICAL_DIRECTORY_NAME);\n}\n\nfunction migrationLockPath() {\n\treturn path.join(getAgentDir(), MIGRATION_LOCK_NAME);\n}\n\nfunction inspectStateRoots() {\n\tconst canonical = isDirectoryRoot(canonicalStateDir(), \"canonical\");\n\tconst legacy = isDirectoryRoot(legacyStateDir(), \"legacy\");\n\tif (canonical && legacy) {\n\t\tthrow new Error(\n\t\t\t`Both legacy ${legacyStateDir()} and canonical ${canonicalStateDir()} pi-sync state directories exist. Close all Pi sessions and reconcile the directories manually; pi-sync will not merge or delete either directory.`,\n\t\t);\n\t}\n\treturn { canonical, legacy };\n}\n\nfunction isDirectoryRoot(directory: string, label: string) {\n\ttry {\n\t\tconst entry = lstatSync(directory);\n\t\tif (entry.isSymbolicLink()) {\n\t\t\tthrow new Error(\n\t\t\t\t`Refusing to use ${label} pi-sync state directory symbolic link: ${directory}`,\n\t\t\t);\n\t\t}\n\t\tif (!entry.isDirectory()) {\n\t\t\tthrow new Error(`${label} pi-sync state path is not a directory: ${directory}`);\n\t\t}\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\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", "export const LOCK_GUARD_STALE_MS = 30_000;\nexport const LOCK_GUARD_UPDATE_MS = 10_000;\n", "import {\n\tmkdir,\n\tmkdirSync,\n\trealpath,\n\trealpathSync,\n\trmdir,\n\trmdirSync,\n\tstat,\n\tstatSync,\n\tutimes,\n\tutimesSync,\n} from \"node:fs\";\n\nexport const 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"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/setup-switch.ts"],
4
- "sourcesContent": ["import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { setSyncSetupCompletions } from \"./command.js\";\nimport {\n\tconfiguredSyncSetupNames,\n\tloadConfig,\n\tnormalizeOnSwitch,\n\tsyncConfigReviewIdentity,\n\tsyncSetupReviewIdentity,\n\tupdateLocalConfig,\n} from \"./config.js\";\nimport { SetupPullRequiresUiError } from \"./sync-errors.js\";\nimport { safeTerminalText } from \"./sync-format.js\";\nimport type { OnSwitchAction } from \"./types.js\";\n\nexport { SetupPullRequiresUiError } from \"./sync-errors.js\";\n\nexport const SETUP_SWITCH_ACTION_OPTIONS: ReadonlyArray<{\n\tlabel: string;\n\tvalue: OnSwitchAction;\n}> = [\n\t{ label: \"Ask before pull\", value: \"ask-before-pull\" },\n\t{ label: \"Start pull\", value: \"pull-after-switch\" },\n\t{ label: \"Switch only\", value: \"switch-only\" },\n];\n\nexport function setupSwitchActionLabel(action: OnSwitchAction) {\n\treturn SETUP_SWITCH_ACTION_OPTIONS.find((option) => option.value === action)?.label ?? action;\n}\n\nexport function setupSwitchActionFromLabel(label: string): OnSwitchAction | undefined {\n\treturn SETUP_SWITCH_ACTION_OPTIONS.find((option) => option.label === label)?.value;\n}\n\nexport async function saveOnSwitch(action: OnSwitchAction, signal?: AbortSignal) {\n\tawait updateLocalConfig((settings) => ({ ...settings, onSwitch: action }), signal);\n}\n\nexport type SetupPullOutcome = \"applied\" | \"cancelled\";\n\nexport interface SetupSwitchResult {\n\tpullApplied: boolean;\n}\n\nexport async function useSyncSetup(\n\tctx: ExtensionCommandContext,\n\tname: string,\n\tpullCurrentSetup?: (setup: string) => Promise<SetupPullOutcome | undefined>,\n\texpectedAction?: OnSwitchAction,\n\tsignal?: AbortSignal,\n\texpectedSetupIdentity?: string,\n): Promise<SetupSwitchResult> {\n\tconst normalized = name.trim();\n\tif (!normalized) throw new Error(\"Usage: /sync use <setup>\");\n\tconst loadedConfig = await loadConfig(normalized);\n\tconst reviewedSetupIdentity = expectedSetupIdentity ?? syncConfigReviewIdentity(loadedConfig);\n\tthrowIfAborted(signal);\n\tconst switchResult: { action: OnSwitchAction; switched: boolean } = {\n\t\taction: \"ask-before-pull\",\n\t\tswitched: false,\n\t};\n\tawait updateLocalConfig((current) => {\n\t\tthrowIfAborted(signal);\n\t\tconst setup = current.syncSetups[normalized];\n\t\tif (!setup) {\n\t\t\tthrow new Error(`Sync setup \u201C${safeTerminalText(normalized)}\u201D no longer exists.`);\n\t\t}\n\t\tconst connectionName = setup.storage.connection;\n\t\tconst connection = current.storageConnections[connectionName];\n\t\tif (\n\t\t\t!connection ||\n\t\t\tsyncSetupReviewIdentity(normalized, setup, connectionName, connection) !==\n\t\t\t\treviewedSetupIdentity\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Sync setup \u201C${safeTerminalText(normalized)}\u201D changed while the switch preview was open; reopen it and review the current destination.`,\n\t\t\t);\n\t\t}\n\t\tswitchResult.action = normalizeOnSwitch(current.onSwitch);\n\t\tif (expectedAction !== undefined && switchResult.action !== expectedAction) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Setup-switch behavior changed while the preview was open; reopen it and retry.\",\n\t\t\t);\n\t\t}\n\t\tif (switchResult.action === \"pull-after-switch\" && !ctx.hasUI) {\n\t\t\tthrow new SetupPullRequiresUiError(\n\t\t\t\t`Automatic setup pulls require interactive confirmation; sync setup \u201C${safeTerminalText(normalized)}\u201D was not switched. Use TUI or RPC mode.`,\n\t\t\t);\n\t\t}\n\t\tif (current.activeSyncSetup === normalized) return current;\n\t\tswitchResult.switched = true;\n\t\treturn { ...current, activeSyncSetup: normalized };\n\t}, signal);\n\tthrowIfAborted(signal);\n\tif (!switchResult.switched) {\n\t\tctx.ui.notify(`Sync setup \u201C${safeTerminalText(normalized)}\u201D is already current.`, \"info\");\n\t\treturn { pullApplied: false };\n\t}\n\tsetSyncSetupCompletions(await configuredSyncSetupNames());\n\tthrowIfAborted(signal);\n\n\tif (switchResult.action === \"switch-only\") {\n\t\tctx.ui.notify(`Switched to \u201C${safeTerminalText(normalized)}\u201D. No files were pulled.`, \"info\");\n\t\treturn { pullApplied: false };\n\t}\n\tif (switchResult.action === \"ask-before-pull\") {\n\t\tif (ctx.mode !== \"tui\") {\n\t\t\tctx.ui.notify(\n\t\t\t\t`Switched to \u201C${safeTerminalText(normalized)}\u201D. No files were pulled because confirmation requires TUI mode; run /sync pull to apply this setup.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t\treturn { pullApplied: false };\n\t\t}\n\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t`Review a pull for sync setup \u201C${safeTerminalText(normalized)}\u201D now?`,\n\t\t\t\"pi-sync will check the remote snapshot and show the exact local writes and deletions before applying anything.\",\n\t\t\t{ signal },\n\t\t);\n\t\tthrowIfAborted(signal);\n\t\tif (!confirmed) {\n\t\t\tctx.ui.notify(\n\t\t\t\t`Switched to \u201C${safeTerminalText(normalized)}\u201D; files were not pulled.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t\treturn { pullApplied: false };\n\t\t}\n\t}\n\n\tctx.ui.notify(\n\t\t`Switched to \u201C${safeTerminalText(normalized)}\u201D. Checking remote files for a reviewed pull\u2026`,\n\t\t\"info\",\n\t);\n\tif (!pullCurrentSetup) {\n\t\tthrow new Error(`Switched to \u201C${safeTerminalText(normalized)}\u201D, but pull is unavailable.`);\n\t}\n\tconst pullOutcome = await pullCurrentSetup(normalized);\n\tthrowIfAborted(signal);\n\tif (pullOutcome === \"cancelled\") {\n\t\tctx.ui.notify(\n\t\t\t`Pull cancelled; sync setup \u201C${safeTerminalText(normalized)}\u201D remains current and synced files were not changed.`,\n\t\t\t\"info\",\n\t\t);\n\t}\n\treturn { pullApplied: pullOutcome === \"applied\" };\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"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/snapshot-codec.ts"],
4
- "sourcesContent": ["import { Readable, Writable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { promisify } from \"node:util\";\nimport { createGunzip, gzip } from \"node:zlib\";\nimport { snapshotSelectionInclude } from \"./sync-policy.js\";\nimport type { Snapshot } from \"./types.js\";\n\nconst VERSION = 1;\nconst MAX_DECOMPRESSED_SNAPSHOT_BYTES = 512 * 1024 * 1024;\nconst gzipAsync = promisify(gzip);\n\nexport async function encodeSnapshot(snapshot: Snapshot) {\n\tsnapshotSelectionInclude(snapshot);\n\treturn gzipAsync(Buffer.from(JSON.stringify(snapshot), \"utf8\"));\n}\n\nexport async function decodeSnapshot(\n\tbuffer: Buffer,\n\toptions: { signal?: AbortSignal; maxOutputLength?: number } = {},\n): Promise<Snapshot> {\n\tthrowIfAborted(options.signal);\n\tconst limit = options.maxOutputLength ?? MAX_DECOMPRESSED_SNAPSHOT_BYTES;\n\tconst chunks: Buffer[] = [];\n\tlet total = 0;\n\tconst sink = new Writable({\n\t\twrite(chunk: Buffer, _encoding, callback) {\n\t\t\ttotal += chunk.byteLength;\n\t\t\tif (total > limit) {\n\t\t\t\tcallback(new Error(`Decompressed snapshot exceeds the ${limit}-byte limit.`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tchunks.push(Buffer.from(chunk));\n\t\t\tcallback();\n\t\t},\n\t});\n\tawait pipeline(Readable.from([buffer]), createGunzip(), sink, { signal: options.signal });\n\tthrowIfAborted(options.signal);\n\tconst parsed = JSON.parse(Buffer.concat(chunks, total).toString(\"utf8\")) as Snapshot;\n\tif (parsed.version !== VERSION || !Array.isArray(parsed.files)) {\n\t\tthrow new Error(\"Unsupported snapshot format.\");\n\t}\n\tsnapshotSelectionInclude(parsed);\n\treturn parsed;\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"],
5
- "mappings": ";;;;;;;AAAA,SAAS,UAAU,gBAAgB;AACnC,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,cAAc,YAAY;AAInC,IAAM,UAAU;AAChB,IAAM,kCAAkC,MAAM,OAAO;AACrD,IAAM,YAAY,UAAU,IAAI;AAEhC,eAAsB,eAAe,UAAoB;AACxD,2BAAyB,QAAQ;AACjC,SAAO,UAAU,OAAO,KAAK,KAAK,UAAU,QAAQ,GAAG,MAAM,CAAC;AAC/D;AAEA,eAAsB,eACrB,QACA,UAA8D,CAAC,GAC3C;AACpB,iBAAe,QAAQ,MAAM;AAC7B,QAAM,QAAQ,QAAQ,mBAAmB;AACzC,QAAM,SAAmB,CAAC;AAC1B,MAAI,QAAQ;AACZ,QAAM,OAAO,IAAI,SAAS;AAAA,IACzB,MAAM,OAAe,WAAW,UAAU;AACzC,eAAS,MAAM;AACf,UAAI,QAAQ,OAAO;AAClB,iBAAS,IAAI,MAAM,qCAAqC,KAAK,cAAc,CAAC;AAC5E;AAAA,MACD;AACA,aAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAC9B,eAAS;AAAA,IACV;AAAA,EACD,CAAC;AACD,QAAM,SAAS,SAAS,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa,GAAG,MAAM,EAAE,QAAQ,QAAQ,OAAO,CAAC;AACxF,iBAAe,QAAQ,MAAM;AAC7B,QAAM,SAAS,KAAK,MAAM,OAAO,OAAO,QAAQ,KAAK,EAAE,SAAS,MAAM,CAAC;AACvE,MAAI,OAAO,YAAY,WAAW,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG;AAC/D,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAC/C;AACA,2BAAyB,MAAM;AAC/B,SAAO;AACR;AAEA,SAAS,eAAe,QAAsB;AAC7C,MAAI,CAAC,QAAQ,QAAS;AACtB,QAAM,OAAO,kBAAkB,QAC5B,OAAO,SACP,IAAI,aAAa,6BAA6B,YAAY;AAC9D;",
6
- "names": []
7
- }