@narumitw/pi-sync 0.50.3 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/README.md +4 -1
  2. package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
  3. package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
  4. package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
  5. package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
  6. package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
  7. package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
  8. package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
  9. package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
  10. package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
  11. package/dist/chunks/chunk-6546TS54.ts.map +7 -0
  12. package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
  13. package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
  14. package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
  15. package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
  16. package/dist/chunks/{chunk-YDWUZVPN.ts → chunk-7KHKWL3Q.ts} +28 -47
  17. package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
  18. package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
  19. package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
  20. package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
  21. package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
  22. package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
  23. package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
  24. package/dist/chunks/{chunk-YHEMENKV.ts → chunk-E42GXD6D.ts} +13 -19
  25. package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
  26. package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
  27. package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
  28. package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
  29. package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
  30. package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
  31. package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
  32. package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
  33. package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
  34. package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
  35. package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
  36. package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
  37. package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
  38. package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
  39. package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
  40. package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
  41. package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
  42. package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
  43. package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
  44. package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
  45. package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
  46. package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
  47. package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
  48. package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
  49. package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
  50. package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
  51. package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
  52. package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
  53. package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
  54. package/dist/chunks/{chunk-DHVFS4QK.ts → chunk-UA7RXOIT.ts} +37 -90
  55. package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
  56. package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
  57. package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
  58. package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
  59. package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
  60. package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
  61. package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
  62. package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
  63. package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
  64. package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
  65. package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
  67. package/dist/chunks/{manager-ui-IPAOAWZA.ts → manager-ui-763NEKJY.ts} +124 -251
  68. package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
  70. package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
  71. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
  73. package/dist/chunks/{setup-wizard-JUJZYNNM.ts → setup-wizard-YIEWGX32.ts} +12 -12
  74. package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
  75. package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
  76. package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
  77. package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
  78. package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
  79. package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
  80. package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
  81. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
  82. package/dist/index.ts +49 -74
  83. package/dist/index.ts.map +3 -3
  84. package/docs/settings.md +9 -0
  85. package/package.json +61 -62
  86. package/src/backends/backend-factory.ts +15 -15
  87. package/src/backends/backend-identity.ts +31 -31
  88. package/src/backends/backend-types.ts +42 -42
  89. package/src/backends/git/git-backend.ts +714 -788
  90. package/src/backends/git/git-config.ts +104 -111
  91. package/src/backends/git/git-runner.ts +230 -247
  92. package/src/backends/git/git-storage.ts +228 -245
  93. package/src/backends/s3/s3-backend.ts +408 -441
  94. package/src/backends/s3/s3-client.ts +288 -333
  95. package/src/backends/sync-backend.ts +65 -65
  96. package/src/backends/webdav/webdav-backend.ts +542 -589
  97. package/src/backends/webdav/webdav-client.ts +292 -316
  98. package/src/backends/webdav/webdav-config.ts +57 -68
  99. package/src/commands/command-execution.ts +270 -289
  100. package/src/commands/command-handler.ts +160 -163
  101. package/src/commands/command-types.ts +13 -13
  102. package/src/commands/command.ts +190 -196
  103. package/src/commands/setup-completions.ts +1 -1
  104. package/src/paths.ts +47 -47
  105. package/src/settings/config-errors.ts +4 -5
  106. package/src/settings/config-file.ts +473 -506
  107. package/src/settings/config.ts +171 -174
  108. package/src/settings/settings-management.ts +191 -216
  109. package/src/settings/settings-store.ts +63 -77
  110. package/src/settings/settings-types.ts +78 -75
  111. package/src/settings/settings-validation.ts +328 -364
  112. package/src/snapshot/session-paths.ts +41 -55
  113. package/src/snapshot/snapshot-apply.ts +197 -229
  114. package/src/snapshot/snapshot-codec.ts +29 -31
  115. package/src/snapshot/snapshot-paths.ts +4 -6
  116. package/src/snapshot/snapshot-transaction.ts +161 -170
  117. package/src/snapshot/snapshot-types.ts +24 -24
  118. package/src/snapshot/snapshot.ts +310 -343
  119. package/src/state/json-file.ts +11 -11
  120. package/src/state/lock.ts +226 -242
  121. package/src/state/lockfile-fs.ts +20 -20
  122. package/src/state/operation-availability.ts +36 -41
  123. package/src/state/state-directory.ts +171 -173
  124. package/src/state/state-types.ts +15 -15
  125. package/src/state/sync-state-store.ts +23 -23
  126. package/src/sync/automatic-sync.ts +75 -80
  127. package/src/sync/remote-snapshot.ts +52 -60
  128. package/src/sync/setup-switch.ts +99 -114
  129. package/src/sync/signals.ts +3 -5
  130. package/src/sync/startup-check.ts +97 -111
  131. package/src/sync/sync-decision.ts +76 -80
  132. package/src/sync/sync-error-guidance.ts +41 -48
  133. package/src/sync/sync-errors.ts +10 -10
  134. package/src/sync/sync-inspection.ts +29 -33
  135. package/src/sync/sync-loaders.ts +28 -36
  136. package/src/sync/sync-mutations.ts +510 -622
  137. package/src/sync/sync-operations.ts +7 -7
  138. package/src/sync/sync-policy.ts +296 -326
  139. package/src/sync/sync-queries.ts +175 -194
  140. package/src/sync/sync-state.ts +74 -100
  141. package/src/sync-extension.ts +89 -101
  142. package/src/sync.ts +26 -26
  143. package/src/types.ts +37 -37
  144. package/src/ui/automatic-sync-summary.ts +1 -3
  145. package/src/ui/cancellable-operation.ts +89 -101
  146. package/src/ui/file-selection.ts +250 -274
  147. package/src/ui/manager-attention.ts +51 -63
  148. package/src/ui/manager-recovery.ts +48 -57
  149. package/src/ui/manager-result-dispatcher.ts +62 -76
  150. package/src/ui/manager-state.ts +200 -232
  151. package/src/ui/manager-ui.ts +264 -362
  152. package/src/ui/remote-selection-ui.ts +547 -588
  153. package/src/ui/secret-input.ts +197 -205
  154. package/src/ui/settings-ui.ts +175 -172
  155. package/src/ui/setup/git-ui.ts +232 -261
  156. package/src/ui/setup/s3-credentials-ui.ts +58 -65
  157. package/src/ui/setup/s3-ui.ts +171 -195
  158. package/src/ui/setup/setup-actions.ts +91 -101
  159. package/src/ui/setup/setup-location-ui.ts +121 -137
  160. package/src/ui/setup/setup-name-ui.ts +27 -27
  161. package/src/ui/setup/setup-prompts.ts +65 -65
  162. package/src/ui/setup/setup-review.ts +58 -59
  163. package/src/ui/setup/setup-switcher.ts +156 -160
  164. package/src/ui/setup/setup-wizard.ts +28 -28
  165. package/src/ui/setup/text-input.ts +54 -58
  166. package/src/ui/setup/webdav-ui.ts +246 -293
  167. package/src/ui/storage-connections-ui.ts +317 -369
  168. package/src/ui/sync-attention.ts +148 -164
  169. package/src/ui/sync-format.ts +110 -121
  170. package/src/ui/sync-resolution-ui.ts +147 -153
  171. package/src/ui/sync-setups-ui.ts +218 -238
  172. package/src/ui/sync-status.ts +17 -0
  173. package/src/ui/terminal-text.ts +2 -2
  174. package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
  175. package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
  176. package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
  177. package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
  178. package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
  179. package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
  180. package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
  181. package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
  182. package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
  183. package/dist/chunks/chunk-DHVFS4QK.ts.map +0 -7
  184. package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
  185. package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
  186. package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
  187. package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
  188. package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
  189. package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
  190. package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
  191. package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
  192. package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
  193. package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
  194. package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
  195. package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
  196. package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
  197. package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
  198. package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
  199. package/dist/chunks/chunk-YDWUZVPN.ts.map +0 -7
  200. package/dist/chunks/chunk-YHEMENKV.ts.map +0 -7
  201. package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
  202. package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
  203. package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
  204. package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
  205. package/dist/chunks/manager-result-dispatcher-ASKGOSW5.ts +0 -22
  206. package/dist/chunks/manager-ui-IPAOAWZA.ts.map +0 -7
  207. package/dist/chunks/remote-selection-ui-QDNR7WEI.ts +0 -21
  208. package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
  209. package/dist/chunks/setup-wizard-JUJZYNNM.ts.map +0 -7
  210. package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
  211. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
  212. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
  213. /package/dist/chunks/{manager-result-dispatcher-ASKGOSW5.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
  214. /package/dist/chunks/{remote-selection-ui-QDNR7WEI.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
  215. /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
  216. /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
  217. /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
  218. /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
@@ -7,195 +7,192 @@ import type { SyncLoaders } from "../sync/sync-loaders.js";
7
7
  import { formatRemoteSelectionMismatch } from "../sync/sync-policy.js";
8
8
  import type { RunRouteResult } from "../ui/cancellable-operation.js";
9
9
  import {
10
- observationMatchesConfig,
11
- type SyncAttentionController,
12
- type SyncAttentionOrigin,
13
- syncAttentionMatchesConfig,
10
+ observationMatchesConfig,
11
+ type SyncAttentionController,
12
+ type SyncAttentionOrigin,
13
+ syncAttentionMatchesConfig,
14
14
  } from "../ui/sync-attention.js";
15
+ import { setSyncStatus } from "../ui/sync-status.js";
15
16
  import { parseOptions, splitArgs } from "./command.js";
16
17
  import { executeCommand, executeRecoveryCommand } from "./command-execution.js";
17
18
 
18
- const STATUS_KEY = "sync";
19
-
20
19
  export async function handleCommand(
21
- rawArgs: string,
22
- ctx: ExtensionCommandContext,
23
- sessionSignal: AbortSignal,
24
- loaders: SyncLoaders,
25
- attention: SyncAttentionController,
20
+ rawArgs: string,
21
+ ctx: ExtensionCommandContext,
22
+ sessionSignal: AbortSignal,
23
+ loaders: SyncLoaders,
24
+ attention: SyncAttentionController,
26
25
  ) {
27
- await reconcileObservation(attention, sessionSignal);
28
- if (sessionSignal.aborted) return;
29
- const run = (route: string, signal?: AbortSignal, onCommit?: () => void, target?: string) =>
30
- executeCommand(
31
- route,
32
- ctx,
33
- combineSignals(sessionSignal, signal),
34
- loaders,
35
- observationCommitCallback(attention, sessionSignal, onCommit),
36
- target,
37
- );
38
- if (!rawArgs.trim()) {
39
- try {
40
- const { showSyncManager } = await import("../ui/manager-ui.js");
41
- if (sessionSignal.aborted) return;
42
- await showSyncManager(ctx, run, sessionSignal, {
43
- getAttention: () => attention.current(),
44
- getObservation: () => attention.observation(),
45
- onObservationInvalidated: () => attention.clearObservation(),
46
- onSelectionResolved: (expected) => {
47
- if (attention.current() === expected) attention.clear(ctx);
48
- },
49
- });
50
- } catch (error) {
51
- if (sessionSignal.aborted) return;
52
- ctx.ui.setStatus(STATUS_KEY, undefined);
53
- ctx.ui.notify(errorMessage(error), "error");
54
- }
55
- await reconcileObservation(attention, sessionSignal);
56
- if (!sessionSignal.aborted) await attention.publish(ctx, sessionSignal);
57
- return;
58
- }
59
- const result = await run(rawArgs);
60
- if (result.kind === "decision-required") {
61
- ctx.ui.notify(result.decision.directMessage, "error");
62
- } else if (result.kind === "remote-selection-required") {
63
- const origin = directSelectionOrigin(rawArgs);
64
- if (origin) attention.set(result.decision, origin);
65
- const deterministic = splitArgs(rawArgs).some((arg) => arg === "--yes" || arg === "-y");
66
- if (origin && ctx.mode === "tui" && !deterministic) {
67
- await resolveSelectionAttention(ctx, attention, sessionSignal, loaders);
68
- } else {
69
- ctx.ui.notify(
70
- formatRemoteSelectionMismatch(
71
- result.decision.setupName,
72
- result.decision.localInclude,
73
- result.decision.remoteInclude,
74
- ),
75
- "error",
76
- );
77
- }
78
- }
79
- await clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);
80
- await reconcileSelectionAttention(ctx, attention, sessionSignal);
81
- await reconcileObservation(attention, sessionSignal);
82
- if (!sessionSignal.aborted) await attention.publish(ctx, sessionSignal);
26
+ await reconcileObservation(attention, sessionSignal);
27
+ if (sessionSignal.aborted) return;
28
+ const run = (route: string, signal?: AbortSignal, onCommit?: () => void, target?: string) =>
29
+ executeCommand(
30
+ route,
31
+ ctx,
32
+ combineSignals(sessionSignal, signal),
33
+ loaders,
34
+ observationCommitCallback(attention, sessionSignal, onCommit),
35
+ target,
36
+ );
37
+ if (!rawArgs.trim()) {
38
+ try {
39
+ const { showSyncManager } = await import("../ui/manager-ui.js");
40
+ if (sessionSignal.aborted) return;
41
+ await showSyncManager(ctx, run, sessionSignal, {
42
+ getAttention: () => attention.current(),
43
+ getObservation: () => attention.observation(),
44
+ onObservationInvalidated: () => attention.clearObservation(),
45
+ onSelectionResolved: (expected) => {
46
+ if (attention.current() === expected) attention.clear(ctx);
47
+ },
48
+ });
49
+ } catch (error) {
50
+ if (sessionSignal.aborted) return;
51
+ setSyncStatus(ctx, undefined);
52
+ ctx.ui.notify(errorMessage(error), "error");
53
+ }
54
+ await reconcileObservation(attention, sessionSignal);
55
+ if (!sessionSignal.aborted) await attention.publish(ctx, sessionSignal);
56
+ return;
57
+ }
58
+ const result = await run(rawArgs);
59
+ if (result.kind === "decision-required") {
60
+ ctx.ui.notify(result.decision.directMessage, "error");
61
+ } else if (result.kind === "remote-selection-required") {
62
+ const origin = directSelectionOrigin(rawArgs);
63
+ if (origin) attention.set(result.decision, origin);
64
+ const deterministic = splitArgs(rawArgs).some((arg) => arg === "--yes" || arg === "-y");
65
+ if (origin && ctx.mode === "tui" && !deterministic) {
66
+ await resolveSelectionAttention(ctx, attention, sessionSignal, loaders);
67
+ } else {
68
+ ctx.ui.notify(
69
+ formatRemoteSelectionMismatch(
70
+ result.decision.setupName,
71
+ result.decision.localInclude,
72
+ result.decision.remoteInclude,
73
+ ),
74
+ "error",
75
+ );
76
+ }
77
+ }
78
+ await clearAttentionAfterCompletedOperation(rawArgs, result, ctx, attention, sessionSignal);
79
+ await reconcileSelectionAttention(ctx, attention, sessionSignal);
80
+ await reconcileObservation(attention, sessionSignal);
81
+ if (!sessionSignal.aborted) await attention.publish(ctx, sessionSignal);
83
82
  }
84
83
 
85
84
  function observationCommitCallback(
86
- attention: SyncAttentionController,
87
- sessionSignal: AbortSignal,
88
- onCommit?: () => void,
85
+ attention: SyncAttentionController,
86
+ sessionSignal: AbortSignal,
87
+ onCommit?: () => void,
89
88
  ) {
90
- const observed = attention.observation();
91
- return () => {
92
- // Opening or cancelling a review changes nothing. A commit may change data
93
- // even if later publication/baseline cleanup fails; never restore old hints.
94
- if (!sessionSignal.aborted && attention.observation() === observed)
95
- attention.clearObservation();
96
- onCommit?.();
97
- };
89
+ const observed = attention.observation();
90
+ return () => {
91
+ // Opening or cancelling a review changes nothing. A commit may change data
92
+ // even if later publication/baseline cleanup fails; never restore old hints.
93
+ if (!sessionSignal.aborted && attention.observation() === observed) attention.clearObservation();
94
+ onCommit?.();
95
+ };
98
96
  }
99
97
 
100
98
  async function reconcileObservation(attention: SyncAttentionController, signal: AbortSignal) {
101
- const observed = attention.observation();
102
- if (!observed || signal.aborted) return;
103
- try {
104
- const config = await loadConfig();
105
- if (signal.aborted || attention.observation() !== observed) return;
106
- if (!observationMatchesConfig(observed, config)) {
107
- attention.clearObservation();
108
- return;
109
- }
110
- const state = await readStateForConfig(config);
111
- if (signal.aborted || attention.observation() !== observed) return;
112
- if (syncStateFingerprint(state) !== observed.inspection.stateIdentity)
113
- attention.clearObservation();
114
- } catch {
115
- if (!signal.aborted && attention.observation() === observed) attention.clearObservation();
116
- }
99
+ const observed = attention.observation();
100
+ if (!observed || signal.aborted) return;
101
+ try {
102
+ const config = await loadConfig();
103
+ if (signal.aborted || attention.observation() !== observed) return;
104
+ if (!observationMatchesConfig(observed, config)) {
105
+ attention.clearObservation();
106
+ return;
107
+ }
108
+ const state = await readStateForConfig(config);
109
+ if (signal.aborted || attention.observation() !== observed) return;
110
+ if (syncStateFingerprint(state) !== observed.inspection.stateIdentity) attention.clearObservation();
111
+ } catch {
112
+ if (!signal.aborted && attention.observation() === observed) attention.clearObservation();
113
+ }
117
114
  }
118
115
 
119
116
  async function clearAttentionAfterCompletedOperation(
120
- rawArgs: string,
121
- result: RunRouteResult,
122
- ctx: ExtensionContext,
123
- attention: SyncAttentionController,
124
- signal: AbortSignal,
117
+ rawArgs: string,
118
+ result: RunRouteResult,
119
+ ctx: ExtensionContext,
120
+ attention: SyncAttentionController,
121
+ signal: AbortSignal,
125
122
  ) {
126
- if (result.kind !== "completed" || result.outcome === "cancelled" || signal.aborted) return;
127
- const [command, ...rest] = splitArgs(rawArgs);
128
- if (command !== "sync" && command !== "pull" && command !== "push") return;
129
- const current = attention.current();
130
- if (!current) return;
131
- try {
132
- const options = parseOptions(rest);
133
- const setupName = options.setup ?? (await loadConfig()).setupName;
134
- if (signal.aborted || attention.current() !== current) return;
135
- if (current.decision.setupName === setupName) attention.clear(ctx);
136
- } catch {
137
- // Attention reconciliation below owns malformed or concurrently changed settings.
138
- }
123
+ if (result.kind !== "completed" || result.outcome === "cancelled" || signal.aborted) return;
124
+ const [command, ...rest] = splitArgs(rawArgs);
125
+ if (command !== "sync" && command !== "pull" && command !== "push") return;
126
+ const current = attention.current();
127
+ if (!current) return;
128
+ try {
129
+ const options = parseOptions(rest);
130
+ const setupName = options.setup ?? (await loadConfig()).setupName;
131
+ if (signal.aborted || attention.current() !== current) return;
132
+ if (current.decision.setupName === setupName) attention.clear(ctx);
133
+ } catch {
134
+ // Attention reconciliation below owns malformed or concurrently changed settings.
135
+ }
139
136
  }
140
137
 
141
138
  async function reconcileSelectionAttention(
142
- ctx: ExtensionContext,
143
- attention: SyncAttentionController,
144
- signal: AbortSignal,
139
+ ctx: ExtensionContext,
140
+ attention: SyncAttentionController,
141
+ signal: AbortSignal,
145
142
  ) {
146
- const current = attention.current();
147
- if (!current || signal.aborted) return;
148
- try {
149
- const config = await loadConfig(current.decision.setupName);
150
- if (signal.aborted || attention.current() !== current) return;
151
- if (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);
152
- } catch {
153
- if (!signal.aborted && attention.current() === current) attention.clear(ctx);
154
- }
143
+ const current = attention.current();
144
+ if (!current || signal.aborted) return;
145
+ try {
146
+ const config = await loadConfig(current.decision.setupName);
147
+ if (signal.aborted || attention.current() !== current) return;
148
+ if (!syncAttentionMatchesConfig(current, config)) attention.clear(ctx);
149
+ } catch {
150
+ if (!signal.aborted && attention.current() === current) attention.clear(ctx);
151
+ }
155
152
  }
156
153
 
157
154
  function directSelectionOrigin(rawArgs: string): SyncAttentionOrigin | undefined {
158
- const command = splitArgs(rawArgs)[0];
159
- return command === "sync" || command === "pull" || command === "push" ? command : undefined;
155
+ const command = splitArgs(rawArgs)[0];
156
+ return command === "sync" || command === "pull" || command === "push" ? command : undefined;
160
157
  }
161
158
 
162
159
  export async function resolveSelectionAttention(
163
- ctx: ExtensionContext,
164
- attention: SyncAttentionController,
165
- signal: AbortSignal,
166
- loaders: SyncLoaders,
167
- options: {
168
- cancelLabel?: string;
169
- withStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;
170
- } = {},
160
+ ctx: ExtensionContext,
161
+ attention: SyncAttentionController,
162
+ signal: AbortSignal,
163
+ loaders: SyncLoaders,
164
+ options: {
165
+ cancelLabel?: string;
166
+ withStateAccess?: <T>(task: () => Promise<T>) => Promise<T>;
167
+ } = {},
171
168
  ) {
172
- const current = attention.current();
173
- if (!current || signal.aborted) return;
174
- const { dispatchManagerResult } = await import("../ui/manager-result-dispatcher.js");
175
- if (signal.aborted || attention.current() !== current) return;
176
- await dispatchManagerResult(
177
- ctx,
178
- { kind: "remote-selection-required", decision: current.decision },
179
- current.origin,
180
- (route, actionSignal, onCommit, target) => {
181
- const execute = () =>
182
- executeRecoveryCommand(
183
- route,
184
- ctx,
185
- combineSignals(signal, actionSignal),
186
- loaders,
187
- observationCommitCallback(attention, signal, onCommit),
188
- target,
189
- );
190
- return options.withStateAccess ? options.withStateAccess(execute) : execute();
191
- },
192
- signal,
193
- {
194
- cancelLabel: options.cancelLabel,
195
- withStateAccess: options.withStateAccess,
196
- onSelectionResolved: () => {
197
- if (attention.current() === current) attention.clear(ctx);
198
- },
199
- },
200
- );
169
+ const current = attention.current();
170
+ if (!current || signal.aborted) return;
171
+ const { dispatchManagerResult } = await import("../ui/manager-result-dispatcher.js");
172
+ if (signal.aborted || attention.current() !== current) return;
173
+ await dispatchManagerResult(
174
+ ctx,
175
+ { kind: "remote-selection-required", decision: current.decision },
176
+ current.origin,
177
+ (route, actionSignal, onCommit, target) => {
178
+ const execute = () =>
179
+ executeRecoveryCommand(
180
+ route,
181
+ ctx,
182
+ combineSignals(signal, actionSignal),
183
+ loaders,
184
+ observationCommitCallback(attention, signal, onCommit),
185
+ target,
186
+ );
187
+ return options.withStateAccess ? options.withStateAccess(execute) : execute();
188
+ },
189
+ signal,
190
+ {
191
+ cancelLabel: options.cancelLabel,
192
+ withStateAccess: options.withStateAccess,
193
+ onSelectionResolved: () => {
194
+ if (attention.current() === current) attention.clear(ctx);
195
+ },
196
+ },
197
+ );
201
198
  }
@@ -1,18 +1,18 @@
1
1
  export interface CommandOptions {
2
- yes: boolean;
3
- force: boolean;
4
- stale: boolean;
5
- silent: boolean;
6
- reload: boolean;
7
- auto: boolean;
8
- setup?: string;
9
- signal?: AbortSignal;
10
- onCommit?: () => void;
11
- args: string[];
2
+ yes: boolean;
3
+ force: boolean;
4
+ stale: boolean;
5
+ silent: boolean;
6
+ reload: boolean;
7
+ auto: boolean;
8
+ setup?: string;
9
+ signal?: AbortSignal;
10
+ onCommit?: () => void;
11
+ args: string[];
12
12
  }
13
13
 
14
14
  export interface CommandArgumentCompletion {
15
- value: string;
16
- label: string;
17
- description?: string;
15
+ value: string;
16
+ label: string;
17
+ description?: string;
18
18
  }