@narumitw/pi-sync 0.54.3 → 0.57.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 (181) hide show
  1. package/README.md +38 -11
  2. package/dist/chunks/agent-candidate-3JN6DJKT.ts +26 -0
  3. package/dist/chunks/agent-plan-QCUJWBP4.ts +99 -0
  4. package/dist/chunks/agent-plan-QCUJWBP4.ts.map +7 -0
  5. package/dist/chunks/agent-runner-E2JU2VZ6.ts +182 -0
  6. package/dist/chunks/agent-runner-E2JU2VZ6.ts.map +7 -0
  7. package/dist/chunks/agent-store-A3F6IYWZ.ts +152 -0
  8. package/dist/chunks/agent-store-A3F6IYWZ.ts.map +7 -0
  9. package/dist/chunks/chunk-5Q5SSKNG.ts +128 -0
  10. package/dist/chunks/chunk-5Q5SSKNG.ts.map +7 -0
  11. package/dist/chunks/chunk-5XZOC7FD.ts +215 -0
  12. package/dist/chunks/chunk-5XZOC7FD.ts.map +7 -0
  13. package/dist/chunks/{chunk-GC3QOL6J.ts → chunk-AAEEHR7H.ts} +31 -87
  14. package/dist/chunks/chunk-AAEEHR7H.ts.map +7 -0
  15. package/dist/chunks/{chunk-R35N4CL3.ts → chunk-B4QZBDBT.ts} +2 -2
  16. package/dist/chunks/{chunk-KGE47GCZ.ts → chunk-CRKTAPSL.ts} +8 -8
  17. package/dist/chunks/chunk-DT7M4VQS.ts +69 -0
  18. package/dist/chunks/chunk-DT7M4VQS.ts.map +7 -0
  19. package/dist/chunks/chunk-ED3TYIVW.ts +229 -0
  20. package/dist/chunks/chunk-ED3TYIVW.ts.map +7 -0
  21. package/dist/chunks/{chunk-HMXUKU43.ts → chunk-EUMINX7L.ts} +6 -6
  22. package/dist/chunks/chunk-FQOSHGJ7.ts +215 -0
  23. package/dist/chunks/chunk-FQOSHGJ7.ts.map +7 -0
  24. package/dist/chunks/chunk-GNH73BJ5.ts +153 -0
  25. package/dist/chunks/chunk-GNH73BJ5.ts.map +7 -0
  26. package/dist/chunks/{chunk-YTHQEW35.ts → chunk-GP5DU62N.ts} +8 -9
  27. package/dist/chunks/chunk-GP5DU62N.ts.map +7 -0
  28. package/dist/chunks/{chunk-AAZDBIBJ.ts → chunk-GSNPASKB.ts} +2 -2
  29. package/dist/chunks/{chunk-E6ONFG57.ts → chunk-IWZH5KYS.ts} +11 -8
  30. package/dist/chunks/chunk-IWZH5KYS.ts.map +7 -0
  31. package/dist/chunks/{chunk-CAGJOKB7.ts → chunk-JJTARUSO.ts} +15 -14
  32. package/dist/chunks/chunk-JJTARUSO.ts.map +7 -0
  33. package/dist/chunks/{chunk-ITFR6NNQ.ts → chunk-NLWS55B6.ts} +2 -1
  34. package/dist/chunks/{chunk-ITFR6NNQ.ts.map → chunk-NLWS55B6.ts.map} +2 -2
  35. package/dist/chunks/{chunk-2FETCGGT.ts → chunk-OEWQVWLC.ts} +4 -4
  36. package/dist/chunks/{chunk-35GM6XKM.ts → chunk-OTZRCKYP.ts} +128 -382
  37. package/dist/chunks/chunk-OTZRCKYP.ts.map +7 -0
  38. package/dist/chunks/{chunk-XU2VXGQV.ts → chunk-OWBAZA6J.ts} +14 -8
  39. package/dist/chunks/chunk-OWBAZA6J.ts.map +7 -0
  40. package/dist/chunks/chunk-PRUEIIK7.ts +24 -0
  41. package/dist/chunks/chunk-PRUEIIK7.ts.map +7 -0
  42. package/dist/chunks/chunk-QGK445K7.ts +21 -0
  43. package/dist/chunks/chunk-QGK445K7.ts.map +7 -0
  44. package/dist/chunks/{chunk-KATKRR3D.ts → chunk-QO333NM4.ts} +32 -11
  45. package/dist/chunks/chunk-QO333NM4.ts.map +7 -0
  46. package/dist/chunks/{chunk-I4JYVFUS.ts → chunk-QSHMOL3I.ts} +21 -22
  47. package/dist/chunks/chunk-QSHMOL3I.ts.map +7 -0
  48. package/dist/chunks/chunk-S2FMH5FT.ts +97 -0
  49. package/dist/chunks/chunk-S2FMH5FT.ts.map +7 -0
  50. package/dist/chunks/{chunk-BVVP33U5.ts → chunk-SWRVXVFD.ts} +12 -9
  51. package/dist/chunks/chunk-SWRVXVFD.ts.map +7 -0
  52. package/dist/chunks/{chunk-ZX2Q6THB.ts → chunk-UBV24CG4.ts} +4 -5
  53. package/dist/chunks/chunk-UBV24CG4.ts.map +7 -0
  54. package/dist/chunks/chunk-UOHR7LRO.ts +70 -0
  55. package/dist/chunks/chunk-UOHR7LRO.ts.map +7 -0
  56. package/dist/chunks/{chunk-VCL7TZYW.ts → chunk-UPNVIUBN.ts} +3 -3
  57. package/dist/chunks/chunk-VJN7KU4J.ts +350 -0
  58. package/dist/chunks/chunk-VJN7KU4J.ts.map +7 -0
  59. package/dist/chunks/chunk-YHT4YTYG.ts +31 -0
  60. package/dist/chunks/chunk-YHT4YTYG.ts.map +7 -0
  61. package/dist/chunks/{chunk-A3TFXY4O.ts → chunk-YK52VDXG.ts} +6 -5
  62. package/dist/chunks/chunk-YK52VDXG.ts.map +7 -0
  63. package/dist/chunks/config-KGIYTTXQ.ts +37 -0
  64. package/dist/chunks/{conflict-artifacts-4CZFB7WI.ts → conflict-artifacts-FXFIIBCB.ts} +8 -7
  65. package/dist/chunks/{conflict-review-ZOTOHL2E.ts → conflict-review-6WIEQLXW.ts} +31 -25
  66. package/dist/chunks/{conflict-review-ZOTOHL2E.ts.map → conflict-review-6WIEQLXW.ts.map} +1 -1
  67. package/dist/chunks/continuous-record-LFRVXQBD.ts +30 -0
  68. package/dist/chunks/continuous-sync-AWAAOXK2.ts +545 -0
  69. package/dist/chunks/continuous-sync-AWAAOXK2.ts.map +7 -0
  70. package/dist/chunks/{file-selection-UNHBVVON.ts → file-selection-3JJYOVNR.ts} +5 -5
  71. package/dist/chunks/{git-backend-FD7AVSK6.ts → git-backend-KYDZ673X.ts} +28 -32
  72. package/dist/chunks/git-backend-KYDZ673X.ts.map +7 -0
  73. package/dist/chunks/{manager-result-dispatcher-HVRTPEVK.ts → manager-result-dispatcher-AGBBVDDS.ts} +10 -9
  74. package/dist/chunks/{manager-ui-ZIN4BUSF.ts → manager-ui-KRAW3NE2.ts} +184 -45
  75. package/dist/chunks/manager-ui-KRAW3NE2.ts.map +7 -0
  76. package/dist/chunks/{merge-baseline-store-ICXVTCML.ts → merge-baseline-store-RFCAH7KL.ts} +5 -5
  77. package/dist/chunks/{remote-selection-ui-H6AEVA3Q.ts → remote-selection-ui-5725JDNC.ts} +9 -8
  78. package/dist/chunks/remote-selection-ui-5725JDNC.ts.map +7 -0
  79. package/dist/chunks/{s3-backend-Y4P3O5B5.ts → s3-backend-Y7WSCLJI.ts} +38 -38
  80. package/dist/chunks/s3-backend-Y7WSCLJI.ts.map +7 -0
  81. package/dist/chunks/{setup-switch-ZMW5C4M7.ts → setup-switch-E7PHKI6J.ts} +7 -6
  82. package/dist/chunks/setup-switch-E7PHKI6J.ts.map +7 -0
  83. package/dist/chunks/{setup-wizard-VW7F3JN4.ts → setup-wizard-65DROCIJ.ts} +14 -13
  84. package/dist/chunks/setup-wizard-65DROCIJ.ts.map +7 -0
  85. package/dist/chunks/{sync-inspection-3EMS2J5B.ts → sync-inspection-XFJOAMJD.ts} +8 -8
  86. package/dist/chunks/sync-inspection-XFJOAMJD.ts.map +7 -0
  87. package/dist/chunks/{sync-operations-2CWST333.ts → sync-operations-HDUDT5PD.ts} +62 -63
  88. package/dist/chunks/sync-operations-HDUDT5PD.ts.map +7 -0
  89. package/dist/chunks/{webdav-backend-TBILVEQK.ts → webdav-backend-6K26F6LT.ts} +39 -48
  90. package/dist/chunks/webdav-backend-6K26F6LT.ts.map +7 -0
  91. package/dist/index.ts +165 -85
  92. package/dist/index.ts.map +3 -3
  93. package/docs/agent-mode-verification.md +99 -0
  94. package/docs/continuous-sync-verification.md +87 -0
  95. package/docs/settings.md +32 -247
  96. package/package.json +11 -5
  97. package/skills/configure-pi-sync/SKILL.md +54 -0
  98. package/skills/configure-pi-sync/references/configuration.md +650 -0
  99. package/src/backends/git/git-backend.ts +1 -4
  100. package/src/backends/git/git-runner.ts +1 -5
  101. package/src/backends/s3/s3-backend.ts +3 -25
  102. package/src/backends/s3/s3-client.ts +14 -2
  103. package/src/backends/snapshot-pointer.ts +27 -0
  104. package/src/backends/webdav/webdav-backend.ts +3 -29
  105. package/src/backends/webdav/webdav-client.ts +11 -7
  106. package/src/commands/command-execution.ts +5 -2
  107. package/src/commands/command-handler.ts +15 -3
  108. package/src/commands/command-types.ts +6 -0
  109. package/src/commands/command.ts +1 -0
  110. package/src/settings/config.ts +7 -0
  111. package/src/settings/settings-store.ts +0 -34
  112. package/src/settings/settings-types.ts +8 -0
  113. package/src/settings/settings-validation.ts +1 -1
  114. package/src/snapshot/snapshot-codec.ts +1 -5
  115. package/src/state/lock.ts +13 -10
  116. package/src/state/merge-baseline-store.ts +2 -2
  117. package/src/state/state-directory.ts +8 -1
  118. package/src/sync/agent-candidate.ts +251 -0
  119. package/src/sync/agent-plan.ts +87 -0
  120. package/src/sync/agent-policy.ts +13 -0
  121. package/src/sync/agent-request.ts +17 -0
  122. package/src/sync/agent-runner.ts +188 -0
  123. package/src/sync/agent-store.ts +151 -0
  124. package/src/sync/agent-sync.ts +91 -0
  125. package/src/sync/automatic-sync.ts +2 -1
  126. package/src/sync/continuous-errors.ts +85 -0
  127. package/src/sync/continuous-lease.ts +221 -0
  128. package/src/sync/continuous-record.ts +179 -0
  129. package/src/sync/continuous-storage.ts +55 -0
  130. package/src/sync/continuous-sync.ts +516 -0
  131. package/src/sync/continuous-watch.ts +59 -0
  132. package/src/sync/merged-sync.ts +70 -12
  133. package/src/sync/remote-snapshot.ts +2 -3
  134. package/src/sync/settings-merge.ts +19 -3
  135. package/src/sync/setup-switch.ts +1 -5
  136. package/src/sync/startup-check.ts +15 -11
  137. package/src/sync/sync-inspection.ts +8 -1
  138. package/src/sync/sync-mutations.ts +6 -17
  139. package/src/sync/sync-queries.ts +5 -0
  140. package/src/sync/text-merge.ts +3 -3
  141. package/src/sync-extension.ts +75 -19
  142. package/src/ui/agent-mode-setting.ts +91 -0
  143. package/src/ui/manager-state.ts +2 -0
  144. package/src/ui/remote-selection-ui.ts +5 -5
  145. package/src/ui/settings-ui.ts +83 -1
  146. package/src/ui/setup/s3-credentials-ui.ts +1 -5
  147. package/src/ui/setup/setup-name-ui.ts +2 -5
  148. package/src/ui/setup/setup-switcher.ts +2 -0
  149. package/src/ui/sync-setups-ui.ts +3 -5
  150. package/dist/chunks/chunk-26PMXY7E.ts +0 -362
  151. package/dist/chunks/chunk-26PMXY7E.ts.map +0 -7
  152. package/dist/chunks/chunk-35GM6XKM.ts.map +0 -7
  153. package/dist/chunks/chunk-A3TFXY4O.ts.map +0 -7
  154. package/dist/chunks/chunk-BVVP33U5.ts.map +0 -7
  155. package/dist/chunks/chunk-CAGJOKB7.ts.map +0 -7
  156. package/dist/chunks/chunk-E6ONFG57.ts.map +0 -7
  157. package/dist/chunks/chunk-GC3QOL6J.ts.map +0 -7
  158. package/dist/chunks/chunk-I4JYVFUS.ts.map +0 -7
  159. package/dist/chunks/chunk-KATKRR3D.ts.map +0 -7
  160. package/dist/chunks/chunk-XU2VXGQV.ts.map +0 -7
  161. package/dist/chunks/chunk-YTHQEW35.ts.map +0 -7
  162. package/dist/chunks/chunk-ZX2Q6THB.ts.map +0 -7
  163. package/dist/chunks/git-backend-FD7AVSK6.ts.map +0 -7
  164. package/dist/chunks/manager-ui-ZIN4BUSF.ts.map +0 -7
  165. package/dist/chunks/s3-backend-Y4P3O5B5.ts.map +0 -7
  166. package/dist/chunks/setup-wizard-VW7F3JN4.ts.map +0 -7
  167. package/dist/chunks/sync-operations-2CWST333.ts.map +0 -7
  168. package/dist/chunks/webdav-backend-TBILVEQK.ts.map +0 -7
  169. /package/dist/chunks/{conflict-artifacts-4CZFB7WI.ts.map → agent-candidate-3JN6DJKT.ts.map} +0 -0
  170. /package/dist/chunks/{chunk-R35N4CL3.ts.map → chunk-B4QZBDBT.ts.map} +0 -0
  171. /package/dist/chunks/{chunk-KGE47GCZ.ts.map → chunk-CRKTAPSL.ts.map} +0 -0
  172. /package/dist/chunks/{chunk-HMXUKU43.ts.map → chunk-EUMINX7L.ts.map} +0 -0
  173. /package/dist/chunks/{chunk-AAZDBIBJ.ts.map → chunk-GSNPASKB.ts.map} +0 -0
  174. /package/dist/chunks/{chunk-2FETCGGT.ts.map → chunk-OEWQVWLC.ts.map} +0 -0
  175. /package/dist/chunks/{chunk-VCL7TZYW.ts.map → chunk-UPNVIUBN.ts.map} +0 -0
  176. /package/dist/chunks/{manager-result-dispatcher-HVRTPEVK.ts.map → config-KGIYTTXQ.ts.map} +0 -0
  177. /package/dist/chunks/{merge-baseline-store-ICXVTCML.ts.map → conflict-artifacts-FXFIIBCB.ts.map} +0 -0
  178. /package/dist/chunks/{remote-selection-ui-H6AEVA3Q.ts.map → continuous-record-LFRVXQBD.ts.map} +0 -0
  179. /package/dist/chunks/{file-selection-UNHBVVON.ts.map → file-selection-3JJYOVNR.ts.map} +0 -0
  180. /package/dist/chunks/{setup-switch-ZMW5C4M7.ts.map → manager-result-dispatcher-AGBBVDDS.ts.map} +0 -0
  181. /package/dist/chunks/{sync-inspection-3EMS2J5B.ts.map → merge-baseline-store-RFCAH7KL.ts.map} +0 -0
@@ -0,0 +1,97 @@
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
+ setSyncStatus
5
+ } from "./chunk-K3RIEL6J.ts";
6
+ import {
7
+ AgentResolutionRequired
8
+ } from "./chunk-QGK445K7.ts";
9
+ import {
10
+ withLock
11
+ } from "./chunk-AAEEHR7H.ts";
12
+ import {
13
+ ContinuousError
14
+ } from "./chunk-DT7M4VQS.ts";
15
+ import {
16
+ AGENT_MAX_ATTEMPTS
17
+ } from "./chunk-PRUEIIK7.ts";
18
+
19
+ // src/sync/agent-sync.ts
20
+ async function withAgentSyncLock(ctx, options, run) {
21
+ let request;
22
+ const background = options.background;
23
+ try {
24
+ return await withLock("sync", () => run(options));
25
+ } catch (error) {
26
+ if (!(error instanceof AgentResolutionRequired)) throw error;
27
+ request = error;
28
+ }
29
+ const { withAgentClaim, readAgentCandidate, saveAgentCandidate } = await import("./agent-store-A3F6IYWZ.ts");
30
+ const { parseAgentCandidate } = await import("./agent-candidate-3JN6DJKT.ts");
31
+ const { runAgentResolver } = await import("./agent-runner-E2JU2VZ6.ts");
32
+ return withAgentClaim(async (assertOwner) => {
33
+ const clearStatus = () => setSyncStatus(ctx, void 0);
34
+ const onAbort = () => {
35
+ try {
36
+ clearStatus();
37
+ } catch {
38
+ }
39
+ };
40
+ options.signal?.addEventListener("abort", onAbort, { once: true });
41
+ try {
42
+ options.signal?.throwIfAborted();
43
+ for (let attempt = 0; attempt < AGENT_MAX_ATTEMPTS; attempt++) {
44
+ const validate = () => withLock("agent-validate", async () => {
45
+ options.signal?.throwIfAborted();
46
+ await assertOwner();
47
+ await request.validate();
48
+ options.signal?.throwIfAborted();
49
+ });
50
+ await validate();
51
+ setSyncStatus(ctx, "sync resolving");
52
+ const cached = await withLock("agent-cache-read", async () => {
53
+ await assertOwner();
54
+ return readAgentCandidate(request.key);
55
+ });
56
+ const raw = cached === void 0 ? await runAgentResolver(
57
+ ctx,
58
+ request.evidence,
59
+ validate,
60
+ options.signal,
61
+ background ? () => background.reserve(request.key) : void 0
62
+ ) : JSON.stringify(cached);
63
+ let candidate;
64
+ try {
65
+ candidate = parseAgentCandidate(raw, request.evidence);
66
+ } catch {
67
+ throw new ContinuousError("model", "Agent mode paused: invalid resolver output; evidence retained.");
68
+ }
69
+ await validate();
70
+ await withLock("agent-cache-save", async () => {
71
+ await assertOwner();
72
+ await saveAgentCandidate(request.key, candidate);
73
+ });
74
+ const acceptance = { key: request.key, candidate };
75
+ try {
76
+ return await withLock("sync", async () => {
77
+ await assertOwner();
78
+ options.signal?.throwIfAborted();
79
+ return run({ ...options, agentAcceptance: acceptance });
80
+ });
81
+ } catch (error) {
82
+ if (!(error instanceof AgentResolutionRequired)) throw error;
83
+ request = error;
84
+ }
85
+ }
86
+ throw new ContinuousError("stale", "Agent mode paused: content changed repeatedly; retry later.");
87
+ } finally {
88
+ options.signal?.removeEventListener("abort", onAbort);
89
+ if (!options.signal?.aborted) clearStatus();
90
+ }
91
+ });
92
+ }
93
+
94
+ export {
95
+ withAgentSyncLock
96
+ };
97
+ //# sourceMappingURL=chunk-S2FMH5FT.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/agent-sync.ts"],
4
+ "sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport type { CommandOptions } from \"../commands/command-types.js\";\nimport { withLock } from \"../state/lock.js\";\nimport { setSyncStatus } from \"../ui/sync-status.js\";\nimport { AGENT_MAX_ATTEMPTS } from \"./agent-policy.js\";\nimport { type AgentAcceptance, AgentResolutionRequired } from \"./agent-request.js\";\nimport { ContinuousError } from \"./continuous-errors.js\";\n\n/** All callers retain their existing state-directory access guard. Off stays lazy. */\nexport async function withAgentSyncLock<T>(\n ctx: ExtensionContext,\n options: CommandOptions,\n run: (options: CommandOptions) => Promise<T>,\n): Promise<T> {\n let request: AgentResolutionRequired;\n const background = options.background;\n try {\n return await withLock(\"sync\", () => run(options));\n } catch (error) {\n if (!(error instanceof AgentResolutionRequired)) throw error;\n request = error;\n }\n const { withAgentClaim, readAgentCandidate, saveAgentCandidate } = await import(\"./agent-store.js\");\n const { parseAgentCandidate } = await import(\"./agent-candidate.js\");\n const { runAgentResolver } = await import(\"./agent-runner.js\");\n return withAgentClaim(async (assertOwner) => {\n const clearStatus = () => setSyncStatus(ctx, undefined);\n const onAbort = () => {\n try {\n clearStatus();\n } catch {\n /* UI can already be disposed. */\n }\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n options.signal?.throwIfAborted();\n for (let attempt = 0; attempt < AGENT_MAX_ATTEMPTS; attempt++) {\n const validate = () =>\n withLock(\"agent-validate\", async () => {\n options.signal?.throwIfAborted();\n await assertOwner();\n await request.validate();\n options.signal?.throwIfAborted();\n });\n await validate();\n setSyncStatus(ctx, \"sync resolving\");\n const cached = await withLock(\"agent-cache-read\", async () => {\n await assertOwner();\n return readAgentCandidate(request.key);\n });\n const raw =\n cached === undefined\n ? await runAgentResolver(\n ctx,\n request.evidence,\n validate,\n options.signal,\n background ? () => background.reserve(request.key) : undefined,\n )\n : JSON.stringify(cached);\n let candidate: ReturnType<typeof parseAgentCandidate>;\n try {\n candidate = parseAgentCandidate(raw, request.evidence);\n } catch {\n throw new ContinuousError(\"model\", \"Agent mode paused: invalid resolver output; evidence retained.\");\n }\n await validate();\n await withLock(\"agent-cache-save\", async () => {\n await assertOwner();\n await saveAgentCandidate(request.key, candidate);\n });\n const acceptance: AgentAcceptance = { key: request.key, candidate };\n try {\n return await withLock(\"sync\", async () => {\n await assertOwner();\n options.signal?.throwIfAborted();\n return run({ ...options, agentAcceptance: acceptance });\n });\n } catch (error) {\n if (!(error instanceof AgentResolutionRequired)) throw error;\n request = error;\n }\n }\n throw new ContinuousError(\"stale\", \"Agent mode paused: content changed repeatedly; retry later.\");\n } finally {\n options.signal?.removeEventListener(\"abort\", onAbort);\n if (!options.signal?.aborted) clearStatus();\n }\n });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AASA,eAAsB,kBACpB,KACA,SACA,KACY;AACZ,MAAI;AACJ,QAAM,aAAa,QAAQ;AAC3B,MAAI;AACF,WAAO,MAAM,SAAS,QAAQ,MAAM,IAAI,OAAO,CAAC;AAAA,EAClD,SAAS,OAAO;AACd,QAAI,EAAE,iBAAiB,yBAA0B,OAAM;AACvD,cAAU;AAAA,EACZ;AACA,QAAM,EAAE,gBAAgB,oBAAoB,mBAAmB,IAAI,MAAM,OAAO,2BAAkB;AAClG,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,+BAAsB;AACnE,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,4BAAmB;AAC7D,SAAO,eAAe,OAAO,gBAAgB;AAC3C,UAAM,cAAc,MAAM,cAAc,KAAK,MAAS;AACtD,UAAM,UAAU,MAAM;AACpB,UAAI;AACF,oBAAY;AAAA,MACd,QAAQ;AAAA,MAER;AAAA,IACF;AACA,YAAQ,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACjE,QAAI;AACF,cAAQ,QAAQ,eAAe;AAC/B,eAAS,UAAU,GAAG,UAAU,oBAAoB,WAAW;AAC7D,cAAM,WAAW,MACf,SAAS,kBAAkB,YAAY;AACrC,kBAAQ,QAAQ,eAAe;AAC/B,gBAAM,YAAY;AAClB,gBAAM,QAAQ,SAAS;AACvB,kBAAQ,QAAQ,eAAe;AAAA,QACjC,CAAC;AACH,cAAM,SAAS;AACf,sBAAc,KAAK,gBAAgB;AACnC,cAAM,SAAS,MAAM,SAAS,oBAAoB,YAAY;AAC5D,gBAAM,YAAY;AAClB,iBAAO,mBAAmB,QAAQ,GAAG;AAAA,QACvC,CAAC;AACD,cAAM,MACJ,WAAW,SACP,MAAM;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,UACR,aAAa,MAAM,WAAW,QAAQ,QAAQ,GAAG,IAAI;AAAA,QACvD,IACA,KAAK,UAAU,MAAM;AAC3B,YAAI;AACJ,YAAI;AACF,sBAAY,oBAAoB,KAAK,QAAQ,QAAQ;AAAA,QACvD,QAAQ;AACN,gBAAM,IAAI,gBAAgB,SAAS,gEAAgE;AAAA,QACrG;AACA,cAAM,SAAS;AACf,cAAM,SAAS,oBAAoB,YAAY;AAC7C,gBAAM,YAAY;AAClB,gBAAM,mBAAmB,QAAQ,KAAK,SAAS;AAAA,QACjD,CAAC;AACD,cAAM,aAA8B,EAAE,KAAK,QAAQ,KAAK,UAAU;AAClE,YAAI;AACF,iBAAO,MAAM,SAAS,QAAQ,YAAY;AACxC,kBAAM,YAAY;AAClB,oBAAQ,QAAQ,eAAe;AAC/B,mBAAO,IAAI,EAAE,GAAG,SAAS,iBAAiB,WAAW,CAAC;AAAA,UACxD,CAAC;AAAA,QACH,SAAS,OAAO;AACd,cAAI,EAAE,iBAAiB,yBAA0B,OAAM;AACvD,oBAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,IAAI,gBAAgB,SAAS,6DAA6D;AAAA,IAClG,UAAE;AACA,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,UAAI,CAAC,QAAQ,QAAQ,QAAS,aAAY;AAAA,IAC5C;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -1,13 +1,6 @@
1
1
  // @generated by scripts/build-runtime.mjs; do not edit.
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
3
 
4
- // src/state/state-directory.ts
5
- import { lstatSync } from "node:fs";
6
- import fs from "node:fs/promises";
7
- import path from "node:path";
8
- import { getAgentDir } from "@earendil-works/pi-coding-agent";
9
- import lockfile from "proper-lockfile";
10
-
11
4
  // src/state/lock-policy.ts
12
5
  var LOCK_GUARD_STALE_MS = 3e4;
13
6
  var LOCK_GUARD_UPDATE_MS = 1e4;
@@ -39,6 +32,11 @@ var LOCKFILE_FS_ADAPTER = {
39
32
  };
40
33
 
41
34
  // src/state/state-directory.ts
35
+ import { lstatSync } from "node:fs";
36
+ import fs from "node:fs/promises";
37
+ import path from "node:path";
38
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
39
+ import lockfile from "proper-lockfile";
42
40
  var CANONICAL_DIRECTORY_NAME = "pi-sync";
43
41
  var LEGACY_DIRECTORY_NAME = ".pisync";
44
42
  var MIGRATION_LOCK_NAME = ".pi-sync-state-migration.lock";
@@ -87,7 +85,12 @@ async function migrateLegacyStateDirectory() {
87
85
  realpath: false,
88
86
  stale: LOCK_GUARD_STALE_MS
89
87
  });
90
- if (legacyGuardHeld || await pathExists(legacyLock)) {
88
+ const schedulerHeld = await lockfile.check(path.join(legacyStateDir(), "continuous", "scheduler"), {
89
+ fs: LOCKFILE_FS_ADAPTER,
90
+ realpath: false,
91
+ stale: LOCK_GUARD_STALE_MS
92
+ });
93
+ if (legacyGuardHeld || schedulerHeld || await pathExists(legacyLock)) {
91
94
  return {
92
95
  status: "deferred",
93
96
  message: "pi-sync state migration was deferred because the legacy directory is busy. Close other Pi sessions, clear any confirmed stale sync lock, and retry."
@@ -242,4 +245,4 @@ export {
242
245
  withStateDirectoryAccess,
243
246
  migrateLegacyStateDirectory
244
247
  };
245
- //# sourceMappingURL=chunk-BVVP33U5.ts.map
248
+ //# sourceMappingURL=chunk-SWRVXVFD.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/state/lock-policy.ts", "../../src/state/lockfile-fs.ts", "../../src/state/state-directory.ts"],
4
+ "sourcesContent": ["export const LOCK_GUARD_STALE_MS = 30_000;\nexport const LOCK_GUARD_UPDATE_MS = 10_000;\n", "import {\n mkdir,\n mkdirSync,\n realpath,\n realpathSync,\n rmdir,\n rmdirSync,\n stat,\n statSync,\n utimes,\n utimesSync,\n} from \"node:fs\";\n\nexport const LOCKFILE_FS_ADAPTER = {\n mkdir,\n mkdirSync,\n realpath,\n realpathSync,\n rmdir,\n rmdirSync,\n stat,\n statSync,\n utimes,\n utimesSync,\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 | { status: \"ready\" }\n | { status: \"migrated\"; message: string }\n | { status: \"deferred\"; message: string };\n\ninterface StateDirectoryGuard {\n release: () => Promise<void>;\n throwIfCompromised: () => void;\n}\n\ninterface SharedStateDirectoryGuard {\n guard: Promise<StateDirectoryGuard>;\n users: number;\n closing?: Promise<void>;\n}\n\nconst sharedStateDirectoryGuards = new Map<string, SharedStateDirectoryGuard>();\n\nexport function stateDir() {\n const roots = inspectStateRoots();\n if (roots.canonical) return canonicalStateDir();\n if (roots.legacy) return legacyStateDir();\n return canonicalStateDir();\n}\n\nexport function legacyStateDir() {\n return path.join(getAgentDir(), LEGACY_DIRECTORY_NAME);\n}\n\nexport function stateDirectoryMigrationNotice() {\n const roots = inspectStateRoots();\n if (!roots.legacy) return undefined;\n return \"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 const roots = inspectStateRoots();\n if (!roots.legacy) return fn();\n return runWithStateDirectoryGuard(await acquireSharedStateDirectoryGuard(), fn);\n}\n\nexport async function migrateLegacyStateDirectory(): Promise<StateDirectoryPreparation> {\n const initial = inspectStateRoots();\n if (initial.canonical || !initial.legacy) return { status: \"ready\" };\n\n let guard: StateDirectoryGuard;\n try {\n guard = await acquireStateDirectoryGuard();\n } catch (error) {\n if (!isLockHeldError(error)) throw error;\n return {\n status: \"deferred\",\n message:\n \"pi-sync state migration was deferred because another state user is active. Close other Pi sessions and retry.\",\n };\n }\n return runWithStateDirectoryGuard(guard, async () => {\n const roots = inspectStateRoots();\n if (roots.canonical || !roots.legacy) return { status: \"ready\" };\n\n const legacyLock = path.join(legacyStateDir(), \"lock\");\n const legacyGuardHeld = await lockfile.check(legacyLock, {\n fs: LOCKFILE_FS_ADAPTER,\n lockfilePath: `${legacyLock}.guard`,\n realpath: false,\n stale: LOCK_GUARD_STALE_MS,\n });\n // Idle continuous owners do not retain the exclusive migration guard.\n // A live renewable lease still prevents moving their private state root.\n const schedulerHeld = await lockfile.check(path.join(legacyStateDir(), \"continuous\", \"scheduler\"), {\n fs: LOCKFILE_FS_ADAPTER,\n realpath: false,\n stale: LOCK_GUARD_STALE_MS,\n });\n if (legacyGuardHeld || schedulerHeld || (await pathExists(legacyLock))) {\n return {\n status: \"deferred\",\n message:\n \"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 };\n }\n guard.throwIfCompromised();\n await fs.rename(legacyStateDir(), canonicalStateDir());\n guard.throwIfCompromised();\n inspectStateRoots();\n return {\n status: \"migrated\",\n message: `Migrated pi-sync state from ${legacyStateDir()} to ${canonicalStateDir()}.`,\n };\n });\n}\n\nasync function acquireSharedStateDirectoryGuard(): Promise<StateDirectoryGuard> {\n const key = migrationLockPath();\n for (;;) {\n let shared = sharedStateDirectoryGuards.get(key);\n if (shared?.closing) {\n await shared.closing;\n continue;\n }\n if (!shared) {\n shared = { guard: acquireStateDirectoryGuard(), users: 0 };\n sharedStateDirectoryGuards.set(key, shared);\n }\n shared.users += 1;\n let guard: StateDirectoryGuard;\n try {\n guard = await shared.guard;\n } catch (error) {\n if (sharedStateDirectoryGuards.get(key) === shared) {\n sharedStateDirectoryGuards.delete(key);\n }\n throw error;\n }\n let released = false;\n return {\n throwIfCompromised: guard.throwIfCompromised,\n release: async () => {\n if (released) return;\n released = true;\n shared.users -= 1;\n if (shared.users > 0) return;\n const closing = guard.release();\n shared.closing = closing;\n try {\n await closing;\n } finally {\n if (sharedStateDirectoryGuards.get(key) === shared) {\n sharedStateDirectoryGuards.delete(key);\n }\n }\n },\n };\n }\n}\n\nasync function acquireStateDirectoryGuard(): Promise<StateDirectoryGuard> {\n let compromisedError: Error | undefined;\n const release = await lockfile.lock(getAgentDir(), {\n fs: LOCKFILE_FS_ADAPTER,\n lockfilePath: migrationLockPath(),\n realpath: false,\n stale: MIGRATION_LOCK_STALE_MS,\n update: MIGRATION_LOCK_UPDATE_MS,\n retries: { retries: 20, minTimeout: 10, maxTimeout: 50 },\n onCompromised: (error) => {\n compromisedError = error;\n },\n });\n return {\n release,\n throwIfCompromised: () => {\n if (compromisedError) throw compromisedError;\n },\n };\n}\n\nasync function runWithStateDirectoryGuard<T>(guard: StateDirectoryGuard, fn: () => Promise<T>) {\n let result: T | undefined;\n let failed = false;\n let failure: unknown;\n try {\n guard.throwIfCompromised();\n result = await fn();\n guard.throwIfCompromised();\n } catch (error) {\n failed = true;\n failure = error;\n }\n try {\n await guard.release();\n } catch (error) {\n if (!failed) {\n failed = true;\n failure = error;\n }\n }\n if (failed) throw failure;\n return result as T;\n}\n\nfunction isLockHeldError(error: unknown) {\n return (error as NodeJS.ErrnoException).code === \"ELOCKED\";\n}\n\nfunction canonicalStateDir() {\n return path.join(getAgentDir(), CANONICAL_DIRECTORY_NAME);\n}\n\nfunction migrationLockPath() {\n return path.join(getAgentDir(), MIGRATION_LOCK_NAME);\n}\n\nfunction inspectStateRoots() {\n const canonical = isDirectoryRoot(canonicalStateDir(), \"canonical\");\n const legacy = isDirectoryRoot(legacyStateDir(), \"legacy\");\n if (canonical && legacy) {\n throw new Error(\n `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 );\n }\n return { canonical, legacy };\n}\n\nfunction isDirectoryRoot(directory: string, label: string) {\n try {\n const entry = lstatSync(directory);\n if (entry.isSymbolicLink()) {\n throw new Error(`Refusing to use ${label} pi-sync state directory symbolic link: ${directory}`);\n }\n if (!entry.isDirectory()) {\n throw new Error(`${label} pi-sync state path is not a directory: ${directory}`);\n }\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\n\nasync function pathExists(filePath: string) {\n try {\n await fs.lstat(filePath);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,5 +1,8 @@
1
1
  // @generated by scripts/build-runtime.mjs; do not edit.
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+ import {
4
+ throwIfAborted
5
+ } from "./chunk-PWMWOJP4.ts";
3
6
  import {
4
7
  snapshotSelectionInclude
5
8
  } from "./chunk-QOFMXPM5.ts";
@@ -46,13 +49,9 @@ async function decodeSnapshot(buffer, options = {}) {
46
49
  snapshotSelectionInclude(parsed);
47
50
  return parsed;
48
51
  }
49
- function throwIfAborted(signal) {
50
- if (!signal?.aborted) return;
51
- throw signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
52
- }
53
52
 
54
53
  export {
55
54
  encodeSnapshot,
56
55
  decodeSnapshot
57
56
  };
58
- //# sourceMappingURL=chunk-ZX2Q6THB.ts.map
57
+ //# sourceMappingURL=chunk-UBV24CG4.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/snapshot/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 { throwIfAborted } from \"../sync/signals.js\";\nimport { snapshotSelectionInclude } from \"../sync/sync-policy.js\";\nimport type { Snapshot } from \"./snapshot-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 snapshotSelectionInclude(snapshot);\n return gzipAsync(Buffer.from(JSON.stringify(snapshot), \"utf8\"));\n}\n\nexport async function decodeSnapshot(\n buffer: Buffer,\n options: { signal?: AbortSignal; maxOutputLength?: number } = {},\n): Promise<Snapshot> {\n throwIfAborted(options.signal);\n const limit = options.maxOutputLength ?? MAX_DECOMPRESSED_SNAPSHOT_BYTES;\n const chunks: Buffer[] = [];\n let total = 0;\n const sink = new Writable({\n write(chunk: Buffer, _encoding, callback) {\n total += chunk.byteLength;\n if (total > limit) {\n callback(new Error(`Decompressed snapshot exceeds the ${limit}-byte limit.`));\n return;\n }\n chunks.push(Buffer.from(chunk));\n callback();\n },\n });\n await pipeline(Readable.from([buffer]), createGunzip(), sink, { signal: options.signal });\n throwIfAborted(options.signal);\n const parsed = JSON.parse(Buffer.concat(chunks, total).toString(\"utf8\")) as Snapshot;\n if ((parsed.version !== VERSION && parsed.version !== 2 && parsed.version !== 3) || !Array.isArray(parsed.files)) {\n throw new Error(\"Unsupported snapshot format.\");\n }\n if (parsed.version === 2 || parsed.version === 3) {\n const { normalizeLocalFields, validatePortableSnapshot } = await import(\"../sync/local-fields.js\");\n validatePortableSnapshot(parsed);\n if (parsed.localFields !== undefined) parsed.localFields = normalizeLocalFields(parsed.localFields);\n } else if (parsed.localFields !== undefined) throw new Error(\"Portable field policy requires snapshot version 2.\");\n snapshotSelectionInclude(parsed);\n return parsed;\n}\n"],
5
+ "mappings": ";;;;;;;;;;AAAA,SAAS,UAAU,gBAAgB;AACnC,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,cAAc,YAAY;AAKnC,IAAM,UAAU;AAChB,IAAM,kCAAkC,MAAM,OAAO;AACrD,IAAM,YAAY,UAAU,IAAI;AAEhC,eAAsB,eAAe,UAAoB;AACvD,2BAAyB,QAAQ;AACjC,SAAO,UAAU,OAAO,KAAK,KAAK,UAAU,QAAQ,GAAG,MAAM,CAAC;AAChE;AAEA,eAAsB,eACpB,QACA,UAA8D,CAAC,GAC5C;AACnB,iBAAe,QAAQ,MAAM;AAC7B,QAAM,QAAQ,QAAQ,mBAAmB;AACzC,QAAM,SAAmB,CAAC;AAC1B,MAAI,QAAQ;AACZ,QAAM,OAAO,IAAI,SAAS;AAAA,IACxB,MAAM,OAAe,WAAW,UAAU;AACxC,eAAS,MAAM;AACf,UAAI,QAAQ,OAAO;AACjB,iBAAS,IAAI,MAAM,qCAAqC,KAAK,cAAc,CAAC;AAC5E;AAAA,MACF;AACA,aAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAC9B,eAAS;AAAA,IACX;AAAA,EACF,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,MAAK,OAAO,YAAY,WAAW,OAAO,YAAY,KAAK,OAAO,YAAY,KAAM,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG;AAChH,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACA,MAAI,OAAO,YAAY,KAAK,OAAO,YAAY,GAAG;AAChD,UAAM,EAAE,sBAAsB,yBAAyB,IAAI,MAAM,OAAO,4BAAyB;AACjG,6BAAyB,MAAM;AAC/B,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,qBAAqB,OAAO,WAAW;AAAA,EACpG,WAAW,OAAO,gBAAgB,OAAW,OAAM,IAAI,MAAM,oDAAoD;AACjH,2BAAyB,MAAM;AAC/B,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,70 @@
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
+ encodeSnapshot
5
+ } from "./chunk-UBV24CG4.ts";
6
+ import {
7
+ throwIfAborted
8
+ } from "./chunk-PWMWOJP4.ts";
9
+ import {
10
+ agentDir,
11
+ createSnapshot,
12
+ sessionDirFromContext,
13
+ sessionSnapshotPathFromAbsolute
14
+ } from "./chunk-QOFMXPM5.ts";
15
+ import {
16
+ stateDir
17
+ } from "./chunk-SWRVXVFD.ts";
18
+ import {
19
+ syncDirectory
20
+ } from "./chunk-HUZ5CR4W.ts";
21
+
22
+ // src/sync/sync-local.ts
23
+ import fs from "node:fs/promises";
24
+ import path from "node:path";
25
+ function protectedSessionPaths(ctx, sessionRoot = sessionDirFromContext(ctx)) {
26
+ const getSessionFile = ctx.sessionManager.getSessionFile;
27
+ if (typeof getSessionFile !== "function") return /* @__PURE__ */ new Set();
28
+ const sessionFile = getSessionFile.call(ctx.sessionManager);
29
+ const snapshotPath = sessionFile ? sessionSnapshotPathFromAbsolute(sessionFile, sessionRoot) : void 0;
30
+ return snapshotPath ? /* @__PURE__ */ new Set([snapshotPath]) : /* @__PURE__ */ new Set();
31
+ }
32
+ function captureMutationOwner(ctx, signal) {
33
+ const manager = ctx.sessionManager;
34
+ const file = manager.getSessionFile?.();
35
+ const id = manager.getSessionId?.();
36
+ const cwd = manager.getCwd?.();
37
+ const root = agentDir();
38
+ return () => {
39
+ throwIfAborted(signal);
40
+ if (ctx.sessionManager !== manager || manager.getSessionFile?.() !== file || manager.getSessionId?.() !== id || manager.getCwd?.() !== cwd || agentDir() !== root)
41
+ throw new Error("Session changed during file mutation; evidence retained for review.");
42
+ };
43
+ }
44
+ async function backupLocal(profile, options = {}, signal) {
45
+ throwIfAborted(signal);
46
+ const snapshot = await createSnapshot(profile, { ...options, signal });
47
+ throwIfAborted(signal);
48
+ const backupDirectory = path.join(stateDir(), "backups");
49
+ await fs.mkdir(backupDirectory, { recursive: true, mode: 448 });
50
+ throwIfAborted(signal);
51
+ const backupPath = path.join(backupDirectory, `${snapshot.id}.json.gz`);
52
+ const encoded = await encodeSnapshot(snapshot);
53
+ throwIfAborted(signal);
54
+ const handle = await fs.open(backupPath, "wx", 384);
55
+ try {
56
+ await handle.writeFile(encoded);
57
+ await handle.sync();
58
+ } finally {
59
+ await handle.close();
60
+ }
61
+ await syncDirectory(backupDirectory);
62
+ return backupPath;
63
+ }
64
+
65
+ export {
66
+ protectedSessionPaths,
67
+ captureMutationOwner,
68
+ backupLocal
69
+ };
70
+ //# sourceMappingURL=chunk-UOHR7LRO.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/sync-local.ts"],
4
+ "sourcesContent": ["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { agentDir, sessionDirFromContext } from \"../snapshot/session-paths.js\";\nimport { createSnapshot, sessionSnapshotPathFromAbsolute } from \"../snapshot/snapshot.js\";\nimport { encodeSnapshot } from \"../snapshot/snapshot-codec.js\";\nimport type { SnapshotOptions } from \"../snapshot/snapshot-types.js\";\nimport { syncDirectory } from \"../state/json-file.js\";\nimport { stateDir } from \"../state/state-directory.js\";\nimport { throwIfAborted } from \"./signals.js\";\n\nexport function protectedSessionPaths(\n ctx: ExtensionCommandContext | ExtensionContext,\n sessionRoot = sessionDirFromContext(ctx),\n) {\n const getSessionFile = ctx.sessionManager.getSessionFile;\n if (typeof getSessionFile !== \"function\") return new Set<string>();\n const sessionFile = getSessionFile.call(ctx.sessionManager) as string | undefined;\n const snapshotPath = sessionFile ? sessionSnapshotPathFromAbsolute(sessionFile, sessionRoot) : undefined;\n return snapshotPath ? new Set([snapshotPath]) : new Set<string>();\n}\n\nexport function captureMutationOwner(ctx: ExtensionContext | ExtensionCommandContext, signal?: AbortSignal) {\n const manager = ctx.sessionManager;\n const file = manager.getSessionFile?.();\n const id = manager.getSessionId?.();\n const cwd = manager.getCwd?.();\n const root = agentDir();\n return () => {\n throwIfAborted(signal);\n if (\n ctx.sessionManager !== manager ||\n manager.getSessionFile?.() !== file ||\n manager.getSessionId?.() !== id ||\n manager.getCwd?.() !== cwd ||\n agentDir() !== root\n )\n throw new Error(\"Session changed during file mutation; evidence retained for review.\");\n };\n}\n\nexport async function backupLocal(profile: string, options: SnapshotOptions = {}, signal?: AbortSignal) {\n throwIfAborted(signal);\n const snapshot = await createSnapshot(profile, { ...options, signal });\n throwIfAborted(signal);\n const backupDirectory = path.join(stateDir(), \"backups\");\n await fs.mkdir(backupDirectory, { recursive: true, mode: 0o700 });\n throwIfAborted(signal);\n const backupPath = path.join(backupDirectory, `${snapshot.id}.json.gz`);\n const encoded = await encodeSnapshot(snapshot);\n throwIfAborted(signal);\n const handle = await fs.open(backupPath, \"wx\", 0o600);\n try {\n await handle.writeFile(encoded);\n await handle.sync();\n } finally {\n await handle.close();\n }\n await syncDirectory(backupDirectory);\n return backupPath;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAUV,SAAS,sBACd,KACA,cAAc,sBAAsB,GAAG,GACvC;AACA,QAAM,iBAAiB,IAAI,eAAe;AAC1C,MAAI,OAAO,mBAAmB,WAAY,QAAO,oBAAI,IAAY;AACjE,QAAM,cAAc,eAAe,KAAK,IAAI,cAAc;AAC1D,QAAM,eAAe,cAAc,gCAAgC,aAAa,WAAW,IAAI;AAC/F,SAAO,eAAe,oBAAI,IAAI,CAAC,YAAY,CAAC,IAAI,oBAAI,IAAY;AAClE;AAEO,SAAS,qBAAqB,KAAiD,QAAsB;AAC1G,QAAM,UAAU,IAAI;AACpB,QAAM,OAAO,QAAQ,iBAAiB;AACtC,QAAM,KAAK,QAAQ,eAAe;AAClC,QAAM,MAAM,QAAQ,SAAS;AAC7B,QAAM,OAAO,SAAS;AACtB,SAAO,MAAM;AACX,mBAAe,MAAM;AACrB,QACE,IAAI,mBAAmB,WACvB,QAAQ,iBAAiB,MAAM,QAC/B,QAAQ,eAAe,MAAM,MAC7B,QAAQ,SAAS,MAAM,OACvB,SAAS,MAAM;AAEf,YAAM,IAAI,MAAM,qEAAqE;AAAA,EACzF;AACF;AAEA,eAAsB,YAAY,SAAiB,UAA2B,CAAC,GAAG,QAAsB;AACtG,iBAAe,MAAM;AACrB,QAAM,WAAW,MAAM,eAAe,SAAS,EAAE,GAAG,SAAS,OAAO,CAAC;AACrE,iBAAe,MAAM;AACrB,QAAM,kBAAkB,KAAK,KAAK,SAAS,GAAG,SAAS;AACvD,QAAM,GAAG,MAAM,iBAAiB,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAChE,iBAAe,MAAM;AACrB,QAAM,aAAa,KAAK,KAAK,iBAAiB,GAAG,SAAS,EAAE,UAAU;AACtE,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,iBAAe,MAAM;AACrB,QAAM,SAAS,MAAM,GAAG,KAAK,YAAY,MAAM,GAAK;AACpD,MAAI;AACF,UAAM,OAAO,UAAU,OAAO;AAC9B,UAAM,OAAO,KAAK;AAAA,EACpB,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACA,QAAM,cAAc,eAAe;AACnC,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -2,7 +2,7 @@
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
3
  import {
4
4
  stateDir
5
- } from "./chunk-BVVP33U5.ts";
5
+ } from "./chunk-SWRVXVFD.ts";
6
6
  import {
7
7
  backendIdentityCoordinates
8
8
  } from "./chunk-2JZFHNLM.ts";
@@ -30,7 +30,7 @@ async function writeStateForConfig(config, state, snapshot, validate = () => {
30
30
  }) {
31
31
  validate();
32
32
  if (snapshot) {
33
- const { stageMergeBaseline } = await import("./merge-baseline-store-ICXVTCML.ts");
33
+ const { stageMergeBaseline } = await import("./merge-baseline-store-RFCAH7KL.ts");
34
34
  validate();
35
35
  await stageMergeBaseline(config, snapshot, state);
36
36
  validate();
@@ -58,4 +58,4 @@ export {
58
58
  lockPath,
59
59
  ensureStateDir
60
60
  };
61
- //# sourceMappingURL=chunk-VCL7TZYW.ts.map
61
+ //# sourceMappingURL=chunk-UPNVIUBN.ts.map