@nklisch/pi-enhanced 0.1.0 → 0.1.2

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 (206) hide show
  1. package/node_modules/@nklisch/pi-conveniences/package.json +5 -2
  2. package/node_modules/@nklisch/pi-fff-compat/package.json +5 -2
  3. package/node_modules/@nklisch/pi-plugins/README.md +16 -13
  4. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-trust-continuity.d.ts +34 -0
  5. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-trust-continuity.js +310 -0
  6. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-trust-continuity.js.map +1 -0
  7. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-coordinator.d.ts +2 -2
  8. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-coordinator.js +30 -17
  9. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-coordinator.js.map +1 -1
  10. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-eligibility.d.ts +0 -3
  11. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-eligibility.js +0 -1
  12. package/node_modules/@nklisch/pi-plugins/dist/application/automatic-update-eligibility.js.map +1 -1
  13. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-contract.d.ts +40 -0
  14. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-contract.js +28 -0
  15. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-contract.js.map +1 -0
  16. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-service.d.ts +24 -0
  17. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-service.js +67 -0
  18. package/node_modules/@nklisch/pi-plugins/dist/application/hook-visibility-service.js.map +1 -0
  19. package/node_modules/@nklisch/pi-plugins/dist/application/installed-trust-grant-service.d.ts +110 -0
  20. package/node_modules/@nklisch/pi-plugins/dist/application/installed-trust-grant-service.js +186 -0
  21. package/node_modules/@nklisch/pi-plugins/dist/application/installed-trust-grant-service.js.map +1 -0
  22. package/node_modules/@nklisch/pi-plugins/dist/application/lifecycle-transition-reconciler.d.ts +3 -0
  23. package/node_modules/@nklisch/pi-plugins/dist/application/lifecycle-transition-reconciler.js.map +1 -1
  24. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-plugin-probe.d.ts +0 -1
  25. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-plugin-probe.js +38 -11
  26. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-plugin-probe.js.map +1 -1
  27. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-refresh-service.d.ts +2 -0
  28. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-refresh-service.js +22 -2
  29. package/node_modules/@nklisch/pi-plugins/dist/application/marketplace-refresh-service.js.map +1 -1
  30. package/node_modules/@nklisch/pi-plugins/dist/application/native-automatic-run-presenter.d.ts +11 -0
  31. package/node_modules/@nklisch/pi-plugins/dist/application/native-automatic-run-presenter.js +58 -0
  32. package/node_modules/@nklisch/pi-plugins/dist/application/native-automatic-run-presenter.js.map +1 -0
  33. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-contract.d.ts +2 -0
  34. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-dispatcher.js +2 -0
  35. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-dispatcher.js.map +1 -1
  36. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-help.d.ts +5 -1
  37. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-mutation-dispatch.js +47 -2
  38. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-mutation-dispatch.js.map +1 -1
  39. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-operation.d.ts +2505 -2439
  40. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-parser.d.ts +2 -0
  41. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-parser.js +3 -1
  42. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-parser.js.map +1 -1
  43. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-progress.d.ts +4 -0
  44. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-registry.d.ts +46 -0
  45. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-registry.js +7 -2
  46. package/node_modules/@nklisch/pi-plugins/dist/application/native-control-registry.js.map +1 -1
  47. package/node_modules/@nklisch/pi-plugins/dist/application/native-diagnostic-registry.d.ts +12 -1
  48. package/node_modules/@nklisch/pi-plugins/dist/application/native-diagnostic-registry.js +2 -1
  49. package/node_modules/@nklisch/pi-plugins/dist/application/native-diagnostic-registry.js.map +1 -1
  50. package/node_modules/@nklisch/pi-plugins/dist/application/native-failure-presenter.js +1 -1
  51. package/node_modules/@nklisch/pi-plugins/dist/application/native-failure-presenter.js.map +1 -1
  52. package/node_modules/@nklisch/pi-plugins/dist/application/native-inspection-contract.d.ts +5 -0
  53. package/node_modules/@nklisch/pi-plugins/dist/application/native-installed-inspection.js +7 -2
  54. package/node_modules/@nklisch/pi-plugins/dist/application/native-installed-inspection.js.map +1 -1
  55. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation-contract.d.ts +464 -0
  56. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation-contract.js +3 -2
  57. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation-contract.js.map +1 -1
  58. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation-session.js +1 -1
  59. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation-session.js.map +1 -1
  60. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-operation.d.ts +77 -11
  61. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-result.js +3 -0
  62. package/node_modules/@nklisch/pi-plugins/dist/application/native-lifecycle-result.js.map +1 -1
  63. package/node_modules/@nklisch/pi-plugins/dist/application/native-update-contract.d.ts +11 -1
  64. package/node_modules/@nklisch/pi-plugins/dist/application/native-update-contract.js +9 -1
  65. package/node_modules/@nklisch/pi-plugins/dist/application/native-update-contract.js.map +1 -1
  66. package/node_modules/@nklisch/pi-plugins/dist/application/plugin-lifecycle-service.d.ts +22 -1
  67. package/node_modules/@nklisch/pi-plugins/dist/application/plugin-lifecycle-service.js +21 -2
  68. package/node_modules/@nklisch/pi-plugins/dist/application/plugin-lifecycle-service.js.map +1 -1
  69. package/node_modules/@nklisch/pi-plugins/dist/application/ports/automatic-update-lifecycle.d.ts +4 -0
  70. package/node_modules/@nklisch/pi-plugins/dist/application/ports/mcp-runtime.d.ts +0 -2
  71. package/node_modules/@nklisch/pi-plugins/dist/application/ports/mcp-runtime.js +2 -1
  72. package/node_modules/@nklisch/pi-plugins/dist/application/ports/mcp-runtime.js.map +1 -1
  73. package/node_modules/@nklisch/pi-plugins/dist/application/ports/native-control-applications.d.ts +4 -0
  74. package/node_modules/@nklisch/pi-plugins/dist/application/state-contract.d.ts +28 -0
  75. package/node_modules/@nklisch/pi-plugins/dist/application/trusted-install-contract.d.ts +15 -0
  76. package/node_modules/@nklisch/pi-plugins/dist/application/trusted-install-contract.js +1 -1
  77. package/node_modules/@nklisch/pi-plugins/dist/application/trusted-install-contract.js.map +1 -1
  78. package/node_modules/@nklisch/pi-plugins/dist/application/trusted-install-service.js +4 -0
  79. package/node_modules/@nklisch/pi-plugins/dist/application/trusted-install-service.js.map +1 -1
  80. package/node_modules/@nklisch/pi-plugins/dist/composition/automatic-update-lifecycle-adapter.js +88 -80
  81. package/node_modules/@nklisch/pi-plugins/dist/composition/automatic-update-lifecycle-adapter.js.map +1 -1
  82. package/node_modules/@nklisch/pi-plugins/dist/composition/complete-plugin-reload.js +32 -4
  83. package/node_modules/@nklisch/pi-plugins/dist/composition/complete-plugin-reload.js.map +1 -1
  84. package/node_modules/@nklisch/pi-plugins/dist/composition/create-native-update-management-service.d.ts +2 -2
  85. package/node_modules/@nklisch/pi-plugins/dist/composition/create-native-update-management-service.js +9 -1
  86. package/node_modules/@nklisch/pi-plugins/dist/composition/create-native-update-management-service.js.map +1 -1
  87. package/node_modules/@nklisch/pi-plugins/dist/composition/create-packaged-plugin-host.js +49 -10
  88. package/node_modules/@nklisch/pi-plugins/dist/composition/create-packaged-plugin-host.js.map +1 -1
  89. package/node_modules/@nklisch/pi-plugins/dist/composition/create-skill-hook-runtime.d.ts +2 -0
  90. package/node_modules/@nklisch/pi-plugins/dist/composition/create-skill-hook-runtime.js +1 -1
  91. package/node_modules/@nklisch/pi-plugins/dist/composition/create-skill-hook-runtime.js.map +1 -1
  92. package/node_modules/@nklisch/pi-plugins/dist/composition/packaged-host-startup.js +4 -3
  93. package/node_modules/@nklisch/pi-plugins/dist/composition/packaged-host-startup.js.map +1 -1
  94. package/node_modules/@nklisch/pi-plugins/dist/composition/packaged-plugin-host-contract.d.ts +2 -2
  95. package/node_modules/@nklisch/pi-plugins/dist/composition/runtime-desired-state.js +21 -7
  96. package/node_modules/@nklisch/pi-plugins/dist/composition/runtime-desired-state.js.map +1 -1
  97. package/node_modules/@nklisch/pi-plugins/dist/domain/hook-output-contract.d.ts +7 -1
  98. package/node_modules/@nklisch/pi-plugins/dist/domain/hook-output-contract.js.map +1 -1
  99. package/node_modules/@nklisch/pi-plugins/dist/domain/hook-visibility.d.ts +16 -0
  100. package/node_modules/@nklisch/pi-plugins/dist/domain/hook-visibility.js +12 -0
  101. package/node_modules/@nklisch/pi-plugins/dist/domain/hook-visibility.js.map +1 -0
  102. package/node_modules/@nklisch/pi-plugins/dist/domain/mcp-session-environment.d.ts +23 -0
  103. package/node_modules/@nklisch/pi-plugins/dist/domain/mcp-session-environment.js +30 -0
  104. package/node_modules/@nklisch/pi-plugins/dist/domain/mcp-session-environment.js.map +1 -0
  105. package/node_modules/@nklisch/pi-plugins/dist/domain/state/codec.js +1 -1
  106. package/node_modules/@nklisch/pi-plugins/dist/domain/state/codec.js.map +1 -1
  107. package/node_modules/@nklisch/pi-plugins/dist/domain/state/config-state.d.ts +15 -0
  108. package/node_modules/@nklisch/pi-plugins/dist/domain/state/config-state.js +7 -0
  109. package/node_modules/@nklisch/pi-plugins/dist/domain/state/config-state.js.map +1 -1
  110. package/node_modules/@nklisch/pi-plugins/dist/domain/state/registry.d.ts +7 -0
  111. package/node_modules/@nklisch/pi-plugins/dist/domain/update-policy.js +2 -0
  112. package/node_modules/@nklisch/pi-plugins/dist/domain/update-policy.js.map +1 -1
  113. package/node_modules/@nklisch/pi-plugins/dist/index.d.ts +2 -1
  114. package/node_modules/@nklisch/pi-plugins/dist/index.js +1 -0
  115. package/node_modules/@nklisch/pi-plugins/dist/index.js.map +1 -1
  116. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/project/node-project-root-resolver.js +8 -1
  117. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/project/node-project-root-resolver.js.map +1 -1
  118. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/local-recovery-filesystem.d.ts +0 -2
  119. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/local-recovery-filesystem.js +3 -52
  120. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/local-recovery-filesystem.js.map +1 -1
  121. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/process-revision-leases.js +6 -8
  122. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/process-revision-leases.js.map +1 -1
  123. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-revision-retention.js +6 -6
  124. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-revision-retention.js.map +1 -1
  125. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-transition-journal.js +4 -49
  126. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-transition-journal.js.map +1 -1
  127. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-lifecycle-state-store.js +9 -18
  128. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-lifecycle-state-store.js.map +1 -1
  129. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-open.d.ts +23 -0
  130. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-open.js +80 -0
  131. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-open.js.map +1 -0
  132. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-scope-lock.d.ts +1 -11
  133. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-scope-lock.js +59 -593
  134. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/sqlite-scope-lock.js.map +1 -1
  135. package/node_modules/@nklisch/pi-plugins/dist/pi/extension.js +2 -1
  136. package/node_modules/@nklisch/pi-plugins/dist/pi/extension.js.map +1 -1
  137. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-context-renderer.d.ts +18 -0
  138. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-context-renderer.js +47 -0
  139. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-context-renderer.js.map +1 -0
  140. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-decision-adapter.d.ts +3 -1
  141. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-decision-adapter.js +28 -11
  142. package/node_modules/@nklisch/pi-plugins/dist/pi/hooks/pi-hook-decision-adapter.js.map +1 -1
  143. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-install-component.js +2 -2
  144. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-install-component.js.map +1 -1
  145. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-actions.d.ts +7 -1
  146. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-actions.js +26 -2
  147. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-actions.js.map +1 -1
  148. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-commands.d.ts +1 -1
  149. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-commands.js +1 -1
  150. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-commands.js.map +1 -1
  151. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-component.d.ts +4 -0
  152. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-component.js +25 -3
  153. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-component.js.map +1 -1
  154. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-controller.js +9 -1
  155. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-controller.js.map +1 -1
  156. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-model.d.ts +4 -0
  157. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-model.js +5 -0
  158. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-model.js.map +1 -1
  159. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-render.d.ts +3 -0
  160. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-render.js +20 -12
  161. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-render.js.map +1 -1
  162. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-session.js +43 -14
  163. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-manager-session.js.map +1 -1
  164. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-operation-view.js +13 -8
  165. package/node_modules/@nklisch/pi-plugins/dist/pi/manager/plugin-operation-view.js.map +1 -1
  166. package/node_modules/@nklisch/pi-plugins/dist/pi/native-control-human.js +2 -2
  167. package/node_modules/@nklisch/pi-plugins/dist/pi/native-control-human.js.map +1 -1
  168. package/node_modules/@nklisch/pi-plugins/dist/pi/pi-reload-broker.d.ts +2 -2
  169. package/node_modules/@nklisch/pi-plugins/dist/pi/pi-trust-review.d.ts +8 -0
  170. package/node_modules/@nklisch/pi-plugins/dist/pi/pi-trust-review.js +93 -0
  171. package/node_modules/@nklisch/pi-plugins/dist/pi/pi-trust-review.js.map +1 -0
  172. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-command.js +3 -3
  173. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-command.js.map +1 -1
  174. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-lifecycle.d.ts +2 -0
  175. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-lifecycle.js +8 -2
  176. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-lifecycle.js.map +1 -1
  177. package/node_modules/@nklisch/pi-plugins/dist/runtime/hooks/hook-decision-aggregator.js +1 -1
  178. package/node_modules/@nklisch/pi-plugins/dist/runtime/hooks/hook-decision-aggregator.js.map +1 -1
  179. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/launch-value-provider.js +24 -0
  180. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/launch-value-provider.js.map +1 -1
  181. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js +1 -7
  182. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js.map +1 -1
  183. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js +1 -2
  184. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js.map +1 -1
  185. package/node_modules/@nklisch/pi-plugins/dist/runtime/published-package-receipt.d.ts +10 -13
  186. package/node_modules/@nklisch/pi-plugins/dist/runtime/published-package-receipt.js +7 -92
  187. package/node_modules/@nklisch/pi-plugins/dist/runtime/published-package-receipt.js.map +1 -1
  188. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-lifecycle.js +2 -2
  189. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js +1 -7
  190. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js.map +1 -1
  191. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/subagent-hook-coordinator.js +2 -2
  192. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/subagent-hook-coordinator.js.map +1 -1
  193. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +5 -2
  194. package/node_modules/@nklisch/pi-plugins/package.json +3 -3
  195. package/package.json +7 -7
  196. package/node_modules/@nklisch/pi-conveniences/extensions/agents-context.test.ts +0 -140
  197. package/node_modules/@nklisch/pi-conveniences/extensions/context-window-footer.test.ts +0 -30
  198. package/node_modules/@nklisch/pi-conveniences/extensions/exit.test.ts +0 -44
  199. package/node_modules/@nklisch/pi-conveniences/extensions/model-list.test.ts +0 -34
  200. package/node_modules/@nklisch/pi-fff-compat/extensions/fff-compat-search.test.ts +0 -76
  201. package/node_modules/@nklisch/pi-plugins/dist/application/ports/update-activation-context.d.ts +0 -4
  202. package/node_modules/@nklisch/pi-plugins/dist/application/ports/update-activation-context.js +0 -2
  203. package/node_modules/@nklisch/pi-plugins/dist/application/ports/update-activation-context.js.map +0 -1
  204. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/identity-bound-sqlite.d.ts +0 -22
  205. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/identity-bound-sqlite.js +0 -256
  206. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/identity-bound-sqlite.js.map +0 -1
@@ -1,37 +1,14 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import { DatabaseSync } from "node:sqlite";
3
- import { mkdtemp, rm } from "node:fs/promises";
4
- import { chmodSync, closeSync, linkSync, lstatSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { basename, join } from "node:path";
2
+ import { chmodSync, lstatSync } from "node:fs";
3
+ import { join } from "node:path";
6
4
  import { BoundaryError } from "../../domain/errors.js";
7
5
  import { ScopeReferenceSchema, } from "../../domain/state/scope.js";
8
6
  import { ensurePrivateLockRoot, LOCAL_LOCK_DATABASE_MODE, verifyLocalFilesystemCapability, } from "./local-lock-filesystem.js";
9
- import { classifyProcessIdentity, readLinuxProcessStartToken } from "../process/process-identity.js";
10
7
  const PROTOCOL = "pi-plugin-host-scope-lock";
11
8
  const PROTOCOL_VERSION = 1;
12
9
  const PROTOCOL_TABLE = "scope_lock_protocol";
13
- const ROOT_MARKER_NAME = ".scope-lock-root.identity";
14
- const DATABASE_MARKER_SUFFIX = ".identity";
15
- const DATABASE_INITIALIZATION_CLAIM_SUFFIX = ".initializing";
16
- const DATABASE_HANDLE_ALIAS_SUFFIX = ".handle";
17
- const ROOT_MARKER_PROTOCOL = "pi-plugin-host-scope-lock-root";
18
- const DATABASE_MARKER_PROTOCOL = "pi-plugin-host-scope-lock-database";
19
10
  const DEFAULT_RETRY_DELAY = Object.freeze({ minimum: 2, maximum: 25 });
20
11
  const SQLITE_BUSY = 5;
21
- class DatabaseInitializationInProgress extends Error {
22
- owner;
23
- constructor(owner) {
24
- super("scope lock database initialization is in progress");
25
- this.owner = owner;
26
- this.name = "DatabaseInitializationInProgress";
27
- }
28
- }
29
- class DatabaseInitializationSnapshotChanged extends Error {
30
- constructor() {
31
- super("scope lock database initialization snapshot changed");
32
- this.name = "DatabaseInitializationSnapshotChanged";
33
- }
34
- }
35
12
  function assertSignal(signal) {
36
13
  if (signal === null ||
37
14
  typeof signal !== "object" ||
@@ -87,249 +64,9 @@ function scopeDatabaseName(scope) {
87
64
  function databasePath(root, scope) {
88
65
  return join(root, scopeDatabaseName(scope));
89
66
  }
90
- function sameFileIdentity(left, right) {
91
- return left.device === right.device && left.inode === right.inode;
92
- }
93
- function regularFileIdentity(path) {
94
- const stats = lstatSync(path);
95
- if (stats.isSymbolicLink() || !stats.isFile())
96
- throw new Error("scope lock identity path is not a regular file");
97
- return { device: String(stats.dev), inode: String(stats.ino) };
98
- }
99
- function currentInitializationOwner() {
100
- const startTime = readLinuxProcessStartToken(process.pid);
101
- if (startTime === undefined)
102
- throw new Error("scope lock cannot establish process identity");
103
- return { pid: process.pid, startTime };
104
- }
105
- function ownerStatus(owner) {
106
- return classifyProcessIdentity({ pid: owner.pid, startToken: owner.startTime });
107
- }
108
- function parseInitializationOwner(input) {
109
- if (input === null || typeof input !== "object" || Array.isArray(input)) {
110
- throw new Error("scope lock initialization owner is invalid");
111
- }
112
- const value = input;
113
- const pid = value.pid;
114
- const startTime = value.startTime;
115
- if (Object.keys(value).length !== 2 ||
116
- typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0 ||
117
- typeof startTime !== "string" || !/^\d+$/.test(startTime)) {
118
- throw new Error("scope lock initialization owner is invalid");
119
- }
120
- return { pid, startTime };
121
- }
122
- function parseRootIdentityMarker(input) {
123
- if (input === null || typeof input !== "object" || Array.isArray(input))
124
- throw new Error("scope lock root identity marker is invalid");
125
- const value = input;
126
- if (Object.keys(value).length !== 3 ||
127
- value.protocol !== ROOT_MARKER_PROTOCOL ||
128
- value.version !== 1 ||
129
- typeof value.identity !== "string" ||
130
- value.identity.length === 0)
131
- throw new Error("scope lock root identity marker is invalid");
132
- return value;
133
- }
134
- function rootIdentityMarkerPath(root) {
135
- return join(root, ROOT_MARKER_NAME);
136
- }
137
- function ensureRootIdentityMarker(root) {
138
- const path = rootIdentityMarkerPath(root);
139
- let markerExists = false;
140
- try {
141
- regularFileIdentity(path);
142
- markerExists = true;
143
- }
144
- catch (error) {
145
- if (error.code !== "ENOENT")
146
- throw error;
147
- }
148
- if (!markerExists) {
149
- const marker = {
150
- protocol: ROOT_MARKER_PROTOCOL,
151
- version: 1,
152
- identity: randomUUID(),
153
- };
154
- // Exclusive creation exposes an empty file before writeFileSync has
155
- // populated it. Publish a complete marker through a hard-link so another
156
- // first-use process can never parse a partially-written identity.
157
- const temporary = `${path}.${randomUUID()}.tmp`;
158
- writeFileSync(temporary, `${JSON.stringify(marker)}\n`, {
159
- flag: "wx",
160
- mode: LOCAL_LOCK_DATABASE_MODE,
161
- });
162
- try {
163
- linkSync(temporary, path);
164
- }
165
- catch (error) {
166
- if (error.code !== "EEXIST")
167
- throw error;
168
- }
169
- finally {
170
- try {
171
- unlinkSync(temporary);
172
- }
173
- catch (error) {
174
- if (error.code !== "ENOENT")
175
- throw error;
176
- }
177
- }
178
- }
179
- chmodSync(path, LOCAL_LOCK_DATABASE_MODE);
180
- return parseRootIdentityMarker(JSON.parse(readFileSync(path, "utf8"))).identity;
181
- }
182
- function databaseIdentityMarkerPath(path) {
183
- return `${path}${DATABASE_MARKER_SUFFIX}`;
184
- }
185
- function databaseInitializationClaimPath(path) {
186
- return `${path}${DATABASE_INITIALIZATION_CLAIM_SUFFIX}`;
187
- }
188
- function parseDatabaseIdentityMarker(input) {
189
- if (input === null || typeof input !== "object" || Array.isArray(input))
190
- throw new Error("scope lock database identity marker is invalid");
191
- const value = input;
192
- if ((value.state !== "initializing" && value.state !== "ready") ||
193
- Object.keys(value).length !== (value.state === "ready" ? 6 : 6) ||
194
- value.protocol !== DATABASE_MARKER_PROTOCOL ||
195
- value.version !== 1 ||
196
- typeof value.rootIdentity !== "string" ||
197
- value.rootIdentity.length === 0 ||
198
- typeof value.database !== "string" ||
199
- value.database.length === 0)
200
- throw new Error("scope lock database identity marker is invalid");
201
- if (value.state === "initializing")
202
- return {
203
- protocol: DATABASE_MARKER_PROTOCOL,
204
- version: 1,
205
- rootIdentity: value.rootIdentity,
206
- database: value.database,
207
- state: "initializing",
208
- owner: parseInitializationOwner(value.owner),
209
- };
210
- const identity = value.identity;
211
- if (identity === null || typeof identity !== "object" || Array.isArray(identity)) {
212
- throw new Error("scope lock database identity marker is invalid");
213
- }
214
- const fileIdentity = identity;
215
- if (Object.keys(fileIdentity).length !== 2 ||
216
- typeof fileIdentity.device !== "string" ||
217
- typeof fileIdentity.inode !== "string" ||
218
- fileIdentity.device.length === 0 ||
219
- fileIdentity.inode.length === 0)
220
- throw new Error("scope lock database identity marker is invalid");
221
- return {
222
- protocol: DATABASE_MARKER_PROTOCOL,
223
- version: 1,
224
- rootIdentity: value.rootIdentity,
225
- database: value.database,
226
- state: "ready",
227
- identity: { device: fileIdentity.device, inode: fileIdentity.inode },
228
- };
229
- }
230
- function readDatabaseIdentityMarker(path) {
231
- const markerPath = databaseIdentityMarkerPath(path);
232
- try {
233
- regularFileIdentity(markerPath);
234
- }
235
- catch (error) {
236
- if (error.code !== "ENOENT")
237
- throw error;
238
- return undefined;
239
- }
240
- chmodSync(markerPath, LOCAL_LOCK_DATABASE_MODE);
241
- return parseDatabaseIdentityMarker(JSON.parse(readFileSync(markerPath, "utf8")));
242
- }
243
- function readInitializationClaim(path) {
244
- const claimPath = databaseInitializationClaimPath(path);
245
- try {
246
- regularFileIdentity(claimPath);
247
- }
248
- catch (error) {
249
- if (error.code === "ENOENT")
250
- return undefined;
251
- throw error;
252
- }
253
- const claim = parseDatabaseIdentityMarker(JSON.parse(readFileSync(claimPath, "utf8")));
254
- if (claim.state !== "initializing")
255
- throw new Error("scope lock initialization claim is invalid");
256
- return claim;
257
- }
258
- function sameDatabaseMarker(left, right) {
259
- if (left.protocol !== right.protocol || left.version !== right.version ||
260
- left.rootIdentity !== right.rootIdentity || left.database !== right.database ||
261
- left.state !== right.state)
262
- return false;
263
- if (left.state === "initializing" && right.state === "initializing") {
264
- return left.owner.pid === right.owner.pid && left.owner.startTime === right.owner.startTime;
265
- }
266
- return left.state === "ready" && right.state === "ready" &&
267
- left.identity.device === right.identity.device && left.identity.inode === right.identity.inode;
268
- }
269
- function markerMatchesDatabase(marker, rootIdentity, databaseName) {
270
- return marker.rootIdentity === rootIdentity && marker.database === databaseName;
271
- }
272
- function removeIfRegular(path) {
273
- try {
274
- regularFileIdentity(path);
275
- unlinkSync(path);
276
- }
277
- catch (error) {
278
- if (error.code !== "ENOENT")
279
- throw error;
280
- }
281
- }
282
- function reclaimDeadInitialization(path, marker, rootIdentity, databaseName) {
283
- if (marker.state !== "initializing")
284
- throw new Error("scope lock initialization marker is not reclaimable");
285
- if (!markerMatchesDatabase(marker, rootIdentity, databaseName)) {
286
- throw new Error("scope lock initialization marker does not match its path");
287
- }
288
- const status = ownerStatus(marker.owner);
289
- if (status !== "dead") {
290
- throw new DatabaseInitializationInProgress(marker.owner);
291
- }
292
- // Only a proven-dead owner permits removing the marker, claim, and any
293
- // partially-created database. Unknown and live owners remain cancellable.
294
- removeIfRegular(databaseIdentityMarkerPath(path));
295
- removeIfRegular(databaseInitializationClaimPath(path));
296
- removeIfRegular(path);
297
- }
298
- function createExclusiveClaim(path, marker) {
299
- const claimPath = databaseInitializationClaimPath(path);
300
- const temporary = `${claimPath}.${randomUUID()}.tmp`;
301
- writeFileSync(temporary, `${JSON.stringify(marker)}\n`, {
302
- flag: "wx",
303
- mode: LOCAL_LOCK_DATABASE_MODE,
304
- });
305
- try {
306
- linkSync(temporary, claimPath);
307
- }
308
- finally {
309
- try {
310
- unlinkSync(temporary);
311
- }
312
- catch (error) {
313
- if (error.code !== "ENOENT")
314
- throw error;
315
- }
316
- }
317
- }
318
- function validateDatabaseIdentity(root, rootIdentity, path, databaseName, marker) {
319
- if (marker.state === "initializing")
320
- throw new DatabaseInitializationInProgress(marker.owner);
321
- if (marker.rootIdentity !== rootIdentity ||
322
- marker.database !== databaseName ||
323
- basename(path) !== databaseName)
324
- throw new Error("scope lock database identity marker does not match its path");
325
- regularFileIdentity(rootIdentityMarkerPath(root));
326
- if (parseRootIdentityMarker(JSON.parse(readFileSync(rootIdentityMarkerPath(root), "utf8"))).identity !== rootIdentity) {
327
- throw new Error("scope lock root identity marker changed");
328
- }
329
- const identity = regularFileIdentity(path);
330
- if (marker.identity === undefined || !sameFileIdentity(identity, marker.identity))
331
- throw new Error("scope lock database path was replaced");
332
- return identity;
67
+ function createProtocolSchema(database) {
68
+ database.exec(`CREATE TABLE IF NOT EXISTS ${PROTOCOL_TABLE} (protocol TEXT PRIMARY KEY NOT NULL CHECK (protocol = '${PROTOCOL}'), version INTEGER NOT NULL CHECK (version = ${PROTOCOL_VERSION})) STRICT;`);
69
+ database.prepare(`INSERT OR IGNORE INTO ${PROTOCOL_TABLE} (protocol, version) VALUES ('${PROTOCOL}', ${PROTOCOL_VERSION})`).run();
333
70
  }
334
71
  function validateProtocolSchema(database) {
335
72
  const objects = database
@@ -369,227 +106,47 @@ function validateProtocolSchema(database) {
369
106
  throw new Error("scope lock protocol version is invalid");
370
107
  }
371
108
  }
372
- function validateOpenedDatabaseIdentity(prepared, root, rootIdentity, path, databaseName, marker) {
373
- const pathIdentity = validateDatabaseIdentity(root, rootIdentity, path, databaseName, marker);
374
- const aliasIdentity = regularFileIdentity(prepared.aliasPath);
375
- if (!sameFileIdentity(pathIdentity, aliasIdentity)) {
376
- throw new Error("scope lock opened database handle identity does not match its marker");
377
- }
378
- }
379
- async function prepareDatabase(root, rootIdentity, path, databaseName, retryingMissingMarkerDatabase = false, initializationMarkerRead) {
380
- const owner = currentInitializationOwner();
381
- for (;;) {
382
- let marker = readDatabaseIdentityMarker(path);
383
- await initializationMarkerRead?.({
384
- path,
385
- databaseName,
386
- markerState: marker?.state ?? "absent",
387
- });
388
- let existing = true;
389
- try {
390
- regularFileIdentity(path);
391
- }
392
- catch (error) {
393
- if (error.code !== "ENOENT")
394
- throw error;
395
- existing = false;
396
- }
397
- if (marker?.state === "initializing") {
398
- reclaimDeadInitialization(path, marker, rootIdentity, databaseName);
399
- continue;
400
- }
401
- if (marker === undefined && existing) {
402
- // The marker and database path are independent files. A first-use loser
403
- // can read the old marker state, yield to the winner, then observe the
404
- // newly-created database through its later path check. That combination
405
- // is stale evidence, not proof of an orphan. Retry once through the
406
- // caller's cancellable acquisition loop; if the same coherent state is
407
- // still present, fail closed rather than adopting the database.
408
- const claim = readInitializationClaim(path);
409
- if (claim !== undefined) {
410
- reclaimDeadInitialization(path, claim, rootIdentity, databaseName);
411
- continue;
412
- }
413
- if (retryingMissingMarkerDatabase)
414
- throw new Error("scope lock database identity marker is missing");
415
- throw new DatabaseInitializationSnapshotChanged();
416
- }
417
- if (marker === undefined) {
418
- if (existing)
419
- throw new Error("scope lock database identity marker is missing");
420
- const initializing = {
421
- protocol: DATABASE_MARKER_PROTOCOL,
422
- version: 1,
423
- rootIdentity,
424
- database: databaseName,
425
- state: "initializing",
426
- owner,
427
- };
428
- try {
429
- // The claim is a complete atomically-linked file. The marker is then
430
- // published by rename, so a crash at either boundary leaves an owner
431
- // identity that a later process can evaluate without guessing.
432
- createExclusiveClaim(path, initializing);
433
- const temporary = `${databaseIdentityMarkerPath(path)}.${randomUUID()}.tmp`;
434
- writeFileSync(temporary, `${JSON.stringify(initializing)}\n`, {
435
- flag: "wx",
436
- mode: LOCAL_LOCK_DATABASE_MODE,
437
- });
438
- try {
439
- renameSync(temporary, databaseIdentityMarkerPath(path));
440
- }
441
- finally {
442
- try {
443
- unlinkSync(temporary);
444
- }
445
- catch (error) {
446
- if (error.code !== "ENOENT")
447
- throw error;
448
- }
449
- }
450
- marker = initializing;
451
- }
452
- catch (error) {
453
- if (error.code !== "EEXIST")
454
- throw error;
455
- const observed = readDatabaseIdentityMarker(path) ?? readInitializationClaim(path);
456
- if (observed === undefined)
457
- throw new Error("scope lock initialization claim disappeared");
458
- if (observed.state === "initializing")
459
- reclaimDeadInitialization(path, observed, rootIdentity, databaseName);
460
- continue;
461
- }
462
- }
463
- if (marker === undefined)
464
- throw new Error("scope lock database identity marker disappeared");
465
- if (marker.state === "ready" && !existing) {
466
- throw new Error("scope lock database is missing after initialization");
467
- }
468
- let created = false;
469
- if (!existing) {
470
- let descriptor;
471
- try {
472
- descriptor = openSync(path, "wx", LOCAL_LOCK_DATABASE_MODE);
473
- }
474
- catch (error) {
475
- if (error.code === "EEXIST") {
476
- const observed = readDatabaseIdentityMarker(path) ?? readInitializationClaim(path);
477
- if (observed?.state === "initializing")
478
- throw new DatabaseInitializationInProgress(observed.owner);
479
- throw new DatabaseInitializationInProgress();
480
- }
481
- throw error;
482
- }
483
- closeSync(descriptor);
484
- const identity = regularFileIdentity(path);
485
- marker = {
486
- protocol: DATABASE_MARKER_PROTOCOL,
487
- version: 1,
488
- rootIdentity,
489
- database: databaseName,
490
- state: "ready",
491
- identity,
492
- };
493
- created = true;
494
- }
495
- validateDatabaseIdentity(root, rootIdentity, path, databaseName, marker);
496
- const aliasPath = `${path}${DATABASE_HANDLE_ALIAS_SUFFIX}-${randomUUID()}`;
497
- let database;
498
- let aliasLinked = false;
499
- try {
500
- // DatabaseSync accepts a path rather than an fd. A private hard-link
501
- // alias pins the exact inode the native handle opens; the durable path
502
- // and marker are checked separately before BEGIN and on every assert.
503
- linkSync(path, aliasPath);
504
- aliasLinked = true;
505
- database = new DatabaseSync(aliasPath, {
506
- allowExtension: false,
507
- defensive: true,
508
- enableDoubleQuotedStringLiterals: false,
509
- enableForeignKeyConstraints: true,
510
- readOnly: false,
511
- timeout: 0,
512
- });
513
- const prepared = { database, aliasPath };
514
- validateOpenedDatabaseIdentity(prepared, root, rootIdentity, path, databaseName, marker);
515
- database.enableLoadExtension(false);
516
- database.enableDefensive(true);
517
- chmodSync(path, LOCAL_LOCK_DATABASE_MODE);
518
- if (process.platform !== "win32" && (lstatSync(path).mode & 0o777) !== LOCAL_LOCK_DATABASE_MODE) {
519
- throw new Error("scope lock database is not private");
520
- }
521
- database.exec("PRAGMA journal_mode = DELETE; PRAGMA locking_mode = NORMAL; PRAGMA foreign_keys = ON; PRAGMA trusted_schema = OFF;");
522
- if (created) {
523
- database.exec(`CREATE TABLE ${PROTOCOL_TABLE} (protocol TEXT PRIMARY KEY NOT NULL CHECK (protocol = '${PROTOCOL}'), version INTEGER NOT NULL CHECK (version = ${PROTOCOL_VERSION})) STRICT;`);
524
- database.exec(`INSERT INTO ${PROTOCOL_TABLE} (protocol, version) VALUES ('${PROTOCOL}', ${PROTOCOL_VERSION});`);
525
- }
526
- validateProtocolSchema(database);
527
- validateOpenedDatabaseIdentity(prepared, root, rootIdentity, path, databaseName, marker);
528
- const journal = database.prepare("PRAGMA journal_mode").get();
529
- if (journal?.journal_mode !== "delete")
530
- throw new Error("scope lock database is not using rollback journal mode");
531
- if (created) {
532
- const temporary = `${databaseIdentityMarkerPath(path)}.${randomUUID()}.tmp`;
533
- try {
534
- writeFileSync(temporary, `${JSON.stringify(marker)}\n`, { flag: "wx", mode: LOCAL_LOCK_DATABASE_MODE });
535
- renameSync(temporary, databaseIdentityMarkerPath(path));
536
- unlinkSync(databaseInitializationClaimPath(path));
537
- }
538
- catch (error) {
539
- throw new Error("scope lock database identity marker could not be finalized", { cause: error });
540
- }
541
- }
542
- const durableMarker = readDatabaseIdentityMarker(path);
543
- if (durableMarker === undefined || !sameDatabaseMarker(durableMarker, marker)) {
544
- throw new Error("scope lock database identity marker changed during setup");
545
- }
546
- validateOpenedDatabaseIdentity(prepared, root, rootIdentity, path, databaseName, durableMarker);
547
- return prepared;
548
- }
549
- catch (error) {
550
- let closeError;
551
- try {
552
- if (database?.isOpen)
553
- database.close();
554
- }
555
- catch (cause) {
556
- closeError = cause;
557
- }
558
- if (aliasLinked) {
559
- try {
560
- unlinkSync(aliasPath);
561
- }
562
- catch (cause) {
563
- closeError = closeError === undefined ? cause : { close: closeError, unlink: cause };
564
- }
565
- }
566
- if (closeError !== undefined)
567
- throw new Error("scope lock database setup and cleanup failed", { cause: { error, closeError } });
568
- throw error;
569
- }
570
- }
571
- }
572
- async function closeDatabase(prepared) {
573
- let closeError;
109
+ /**
110
+ * Open the scope database and take the write lock. The lock itself is the
111
+ * held BEGIN IMMEDIATE transaction; first-use schema setup runs under the
112
+ * same transaction so concurrent creators serialize on the database. No
113
+ * sidecar marker, claim, or alias files exist to drift across mounts or
114
+ * strand a later process — a killed holder's transaction is released by the
115
+ * OS and the next acquirer proceeds after the busy window.
116
+ */
117
+ function openLockedDatabase(path) {
118
+ const database = new DatabaseSync(path, {
119
+ allowExtension: false,
120
+ defensive: true,
121
+ enableDoubleQuotedStringLiterals: false,
122
+ enableForeignKeyConstraints: true,
123
+ readOnly: false,
124
+ timeout: 0,
125
+ });
574
126
  try {
575
- if (prepared.database.isOpen)
576
- prepared.database.close();
127
+ const stats = lstatSync(path);
128
+ if (!stats.isFile() || stats.isSymbolicLink())
129
+ throw new Error("scope lock database is not a regular file");
130
+ chmodSync(path, LOCAL_LOCK_DATABASE_MODE);
131
+ if (process.platform !== "win32" && (lstatSync(path).mode & 0o777) !== LOCAL_LOCK_DATABASE_MODE) {
132
+ throw new Error("scope lock database is not private");
133
+ }
134
+ database.exec("PRAGMA journal_mode = DELETE; PRAGMA locking_mode = NORMAL; PRAGMA foreign_keys = ON; PRAGMA trusted_schema = OFF;");
135
+ database.exec("BEGIN IMMEDIATE");
136
+ createProtocolSchema(database);
137
+ validateProtocolSchema(database);
138
+ const journal = database.prepare("PRAGMA journal_mode").get();
139
+ if (journal?.journal_mode !== "delete")
140
+ throw new Error("scope lock database is not using rollback journal mode");
141
+ return database;
577
142
  }
578
143
  catch (error) {
579
- closeError = error;
580
- }
581
- finally {
582
144
  try {
583
- unlinkSync(prepared.aliasPath);
584
- }
585
- catch (error) {
586
- if (error.code !== "ENOENT") {
587
- closeError = closeError === undefined ? error : { close: closeError, unlink: error };
588
- }
145
+ database.close();
589
146
  }
147
+ catch { /* preserve primary failure */ }
148
+ throw error;
590
149
  }
591
- if (closeError !== undefined)
592
- throw closeError;
593
150
  }
594
151
  async function waitForRetry(delayMs, signal) {
595
152
  throwIfAborted(signal);
@@ -612,39 +169,19 @@ async function waitForRetry(delayMs, signal) {
612
169
  });
613
170
  }
614
171
  class SqliteScopeLockLease {
615
- prepared;
616
- root;
617
- rootIdentity;
618
- path;
619
- databaseName;
620
- marker;
172
+ database;
621
173
  scope;
622
174
  status = "held";
623
- constructor(scope, prepared, root, rootIdentity, path, databaseName, marker) {
624
- this.prepared = prepared;
625
- this.root = root;
626
- this.rootIdentity = rootIdentity;
627
- this.path = path;
628
- this.databaseName = databaseName;
629
- this.marker = marker;
175
+ constructor(scope, database) {
176
+ this.database = database;
630
177
  this.scope = scope;
631
178
  }
632
179
  async assertOwned(signal) {
633
180
  assertSignal(signal);
634
181
  throwIfAborted(signal);
635
- if (this.status !== "held" || !this.prepared.database.isOpen || !this.prepared.database.isTransaction) {
182
+ if (this.status !== "held" || !this.database.isOpen || !this.database.isTransaction) {
636
183
  throw adapterFailure("scope-lock.assert-owned", new Error("scope lock is no longer held"));
637
184
  }
638
- try {
639
- const marker = readDatabaseIdentityMarker(this.path);
640
- if (marker === undefined || !sameDatabaseMarker(marker, this.marker)) {
641
- throw new Error("scope lock database identity marker changed during ownership");
642
- }
643
- validateOpenedDatabaseIdentity(this.prepared, this.root, this.rootIdentity, this.path, this.databaseName, marker);
644
- }
645
- catch (error) {
646
- throw adapterFailure("scope-lock.assert-owned", error);
647
- }
648
185
  }
649
186
  async release() {
650
187
  if (this.status === "released")
@@ -652,21 +189,22 @@ class SqliteScopeLockLease {
652
189
  let rollbackError;
653
190
  let closeError;
654
191
  try {
655
- if (this.prepared.database.isOpen && this.prepared.database.isTransaction)
656
- this.prepared.database.exec("ROLLBACK");
192
+ if (this.database.isOpen && this.database.isTransaction)
193
+ this.database.exec("ROLLBACK");
657
194
  }
658
195
  catch (error) {
659
196
  rollbackError = error;
660
197
  }
661
198
  finally {
662
199
  try {
663
- await closeDatabase(this.prepared);
200
+ if (this.database.isOpen)
201
+ this.database.close();
664
202
  }
665
203
  catch (error) {
666
204
  closeError = error;
667
205
  }
668
206
  }
669
- if (!this.prepared.database.isOpen && closeError === undefined)
207
+ if (!this.database.isOpen && closeError === undefined)
670
208
  this.status = "released";
671
209
  else
672
210
  this.status = "uncertain";
@@ -677,43 +215,31 @@ class SqliteScopeLockLease {
677
215
  }
678
216
  class SqliteScopeLockManager {
679
217
  root;
680
- rootIdentity;
681
218
  retryDelayMs;
682
219
  random;
683
- initializationMarkerRead;
684
- constructor(root, rootIdentity, retryDelayMs, random, initializationMarkerRead) {
220
+ constructor(root, retryDelayMs, random) {
685
221
  this.root = root;
686
- this.rootIdentity = rootIdentity;
687
222
  this.retryDelayMs = retryDelayMs;
688
223
  this.random = random;
689
- this.initializationMarkerRead = initializationMarkerRead;
690
224
  }
691
225
  async acquire(input, signal) {
692
226
  assertSignal(signal);
693
227
  const scope = ScopeReferenceSchema.parse(input);
694
228
  const path = databasePath(this.root, scope);
695
- let retryingMissingMarkerDatabase = false;
696
229
  for (;;) {
697
230
  throwIfAborted(signal);
698
- let prepared;
231
+ let database;
699
232
  try {
700
- const databaseName = scopeDatabaseName(scope);
701
- prepared = await prepareDatabase(this.root, this.rootIdentity, path, databaseName, retryingMissingMarkerDatabase, this.initializationMarkerRead);
702
- prepared.database.exec("BEGIN IMMEDIATE");
703
- if (signal.aborted) {
704
- await closeDatabase(prepared);
233
+ database = openLockedDatabase(path);
234
+ if (signal.aborted)
705
235
  throw signal.reason;
706
- }
707
- const marker = readDatabaseIdentityMarker(path);
708
- if (marker === undefined)
709
- throw new Error("scope lock database identity marker disappeared");
710
- validateOpenedDatabaseIdentity(prepared, this.root, this.rootIdentity, path, databaseName, marker);
711
- return new SqliteScopeLockLease(scope, prepared, this.root, this.rootIdentity, path, databaseName, marker);
236
+ return new SqliteScopeLockLease(scope, database);
712
237
  }
713
238
  catch (error) {
714
- if (prepared !== undefined) {
239
+ if (database !== undefined) {
715
240
  try {
716
- await closeDatabase(prepared);
241
+ if (database.isOpen)
242
+ database.close();
717
243
  }
718
244
  catch (closeError) {
719
245
  throw adapterFailure("scope-lock.acquire", cleanupCause(error, closeError));
@@ -721,17 +247,6 @@ class SqliteScopeLockManager {
721
247
  }
722
248
  if (signal.aborted)
723
249
  throw signal.reason;
724
- if (error instanceof DatabaseInitializationInProgress) {
725
- const delay = await this.retryDelay();
726
- await waitForRetry(delay, signal);
727
- continue;
728
- }
729
- if (error instanceof DatabaseInitializationSnapshotChanged) {
730
- retryingMissingMarkerDatabase = true;
731
- const delay = await this.retryDelay();
732
- await waitForRetry(delay, signal);
733
- continue;
734
- }
735
250
  if (!isBusy(error))
736
251
  throw adapterFailure("scope-lock.acquire", error);
737
252
  const delay = await this.retryDelay();
@@ -753,54 +268,7 @@ class SqliteScopeLockManager {
753
268
  return Math.floor(this.retryDelayMs.minimum + value * (this.retryDelayMs.maximum - this.retryDelayMs.minimum));
754
269
  }
755
270
  }
756
- async function probeExclusion(root, rootIdentity) {
757
- const probeDirectory = await mkdtemp(join(root, ".scope-lock-probe-"));
758
- const path = join(probeDirectory, "probe.sqlite");
759
- let holder;
760
- let contender;
761
- let released;
762
- try {
763
- holder = await prepareDatabase(root, rootIdentity, path, "probe.sqlite");
764
- holder.database.exec("BEGIN IMMEDIATE");
765
- try {
766
- contender = await prepareDatabase(root, rootIdentity, path, "probe.sqlite");
767
- contender.database.exec("BEGIN IMMEDIATE");
768
- throw new Error("SQLite did not exclude a second writer");
769
- }
770
- catch (error) {
771
- if (!isBusy(error))
772
- throw error;
773
- }
774
- if (contender !== undefined)
775
- await closeDatabase(contender);
776
- contender = undefined;
777
- holder.database.exec("ROLLBACK");
778
- await closeDatabase(holder);
779
- holder = undefined;
780
- released = await prepareDatabase(root, rootIdentity, path, "probe.sqlite");
781
- released.database.exec("BEGIN IMMEDIATE");
782
- released.database.exec("ROLLBACK");
783
- await closeDatabase(released);
784
- released = undefined;
785
- }
786
- finally {
787
- for (const prepared of [contender, holder, released]) {
788
- if (prepared !== undefined) {
789
- try {
790
- await closeDatabase(prepared);
791
- }
792
- catch {
793
- // The probe is failing anyway; cleanup must not expose a native error.
794
- }
795
- }
796
- }
797
- await rm(probeDirectory, { recursive: true, force: true });
798
- }
799
- }
800
- /**
801
- * Create a scope lock manager after proving the configured root has the
802
- * capabilities this safety boundary needs. No process-local fallback exists.
803
- */
271
+ /** Create a scope lock manager rooted at a private, local-filesystem directory. */
804
272
  export async function createSqliteScopeLockManager(options) {
805
273
  if (options === null || typeof options !== "object")
806
274
  throw new TypeError("scope lock options are required");
@@ -811,10 +279,8 @@ export async function createSqliteScopeLockManager(options) {
811
279
  throw adapterFailure("scope-lock.initialize", error);
812
280
  });
813
281
  try {
814
- const rootIdentity = ensureRootIdentityMarker(root);
815
282
  await (options.verifyLocalFilesystem ?? verifyLocalFilesystemCapability)(root);
816
- await probeExclusion(root, rootIdentity);
817
- return new SqliteScopeLockManager(root, rootIdentity, retryDelayMs, random, options.initializationMarkerRead);
283
+ return new SqliteScopeLockManager(root, retryDelayMs, random);
818
284
  }
819
285
  catch (error) {
820
286
  if (error instanceof BoundaryError)