@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18

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 (357) hide show
  1. package/README.md +134 -15
  2. package/assets/integrations/skills/pappra/SKILL.md +28 -7
  3. package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
  4. package/assets/integrations/skills/pappra/references/environment.md +360 -0
  5. package/assets/integrations/skills/pappra/references/hosts.md +20 -0
  6. package/assets/integrations/skills/pappra/references/operations.md +36 -5
  7. package/dist/adapters/pi/recovery-extension.d.ts +65 -0
  8. package/dist/adapters/pi/recovery-extension.js +238 -0
  9. package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
  10. package/dist/adapters/pi/session-transition-extension.js +265 -0
  11. package/dist/app/automatic-handoff-events.d.ts +26 -0
  12. package/dist/app/automatic-handoff-events.js +134 -0
  13. package/dist/app/bounded-file.d.ts +2 -0
  14. package/dist/app/bounded-file.js +2 -0
  15. package/dist/app/capability-evidence/claude.d.ts +37 -0
  16. package/dist/app/capability-evidence/claude.js +206 -0
  17. package/dist/app/capability-evidence/codex.d.ts +35 -0
  18. package/dist/app/capability-evidence/codex.js +193 -0
  19. package/dist/app/capability-evidence/index.d.ts +33 -0
  20. package/dist/app/capability-evidence/index.js +117 -0
  21. package/dist/app/capability-evidence/pi.d.ts +73 -0
  22. package/dist/app/capability-evidence/pi.js +669 -0
  23. package/dist/app/capability-evidence/shared.d.ts +69 -0
  24. package/dist/app/capability-evidence/shared.js +195 -0
  25. package/dist/app/collab-auto-handoff.d.ts +55 -0
  26. package/dist/app/collab-auto-handoff.js +384 -0
  27. package/dist/app/collab-bindings.d.ts +57 -0
  28. package/dist/app/collab-bindings.js +1006 -0
  29. package/dist/app/collab-bootstrap-transport.d.ts +163 -0
  30. package/dist/app/collab-bootstrap-transport.js +769 -0
  31. package/dist/app/collab-bootstrap.d.ts +137 -0
  32. package/dist/app/collab-bootstrap.js +3455 -0
  33. package/dist/app/collab-handoff.d.ts +12 -0
  34. package/dist/app/collab-handoff.js +511 -0
  35. package/dist/app/collab-messages.d.ts +2 -0
  36. package/dist/app/collab-messages.js +2 -0
  37. package/dist/app/collab-notify.d.ts +156 -0
  38. package/dist/app/collab-notify.js +1208 -0
  39. package/dist/app/collab-preflight.d.ts +34 -0
  40. package/dist/app/collab-preflight.js +96 -0
  41. package/dist/app/collab-recovery-executor.d.ts +161 -0
  42. package/dist/app/collab-recovery-executor.js +1153 -0
  43. package/dist/app/collab-recovery-projection.d.ts +103 -0
  44. package/dist/app/collab-recovery-projection.js +659 -0
  45. package/dist/app/collab-recovery.d.ts +25 -0
  46. package/dist/app/collab-recovery.js +429 -0
  47. package/dist/app/collab-resources.d.ts +16 -0
  48. package/dist/app/collab-resources.js +278 -0
  49. package/dist/app/collab-role-events.d.ts +22 -0
  50. package/dist/app/collab-role-events.js +329 -0
  51. package/dist/app/collab-role-orchestration.d.ts +53 -0
  52. package/dist/app/collab-role-orchestration.js +601 -0
  53. package/dist/app/collab-role-plan.d.ts +81 -0
  54. package/dist/app/collab-role-plan.js +504 -0
  55. package/dist/app/collab-role-repair.d.ts +109 -0
  56. package/dist/app/collab-role-repair.js +821 -0
  57. package/dist/app/collab-rollover.d.ts +263 -0
  58. package/dist/app/collab-rollover.js +1112 -0
  59. package/dist/app/collab-route.d.ts +64 -0
  60. package/dist/app/collab-route.js +240 -0
  61. package/dist/app/collab-session-transition.d.ts +221 -0
  62. package/dist/app/collab-session-transition.js +3334 -0
  63. package/dist/app/collab-state.d.ts +7 -0
  64. package/dist/app/collab-state.js +162 -0
  65. package/dist/app/collab-task-store.d.ts +22 -0
  66. package/dist/app/collab-task-store.js +204 -0
  67. package/dist/app/collab-work-execution.d.ts +127 -0
  68. package/dist/app/collab-work-execution.js +1027 -0
  69. package/dist/app/collab-work-plan.d.ts +87 -0
  70. package/dist/app/collab-work-plan.js +437 -0
  71. package/dist/app/collaboration.d.ts +8 -2
  72. package/dist/app/collaboration.js +101 -12
  73. package/dist/app/doctor.js +108 -5
  74. package/dist/app/host-mcp.js +34 -8
  75. package/dist/app/host-probe.js +4 -4
  76. package/dist/app/init.d.ts +58 -4
  77. package/dist/app/init.js +768 -155
  78. package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
  79. package/dist/app/managed-pi-bootstrap-admission.js +104 -0
  80. package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
  81. package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
  82. package/dist/app/managed-pi-delivery.d.ts +8 -0
  83. package/dist/app/managed-pi-delivery.js +110 -0
  84. package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
  85. package/dist/app/managed-pi-handoff-receiver.js +263 -0
  86. package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
  87. package/dist/app/managed-pi-handoff-reference.js +60 -0
  88. package/dist/app/managed-pi-herdr.d.ts +21 -0
  89. package/dist/app/managed-pi-herdr.js +185 -0
  90. package/dist/app/managed-pi-initial-admission.d.ts +28 -0
  91. package/dist/app/managed-pi-initial-admission.js +416 -0
  92. package/dist/app/managed-pi-initial-launch.d.ts +5 -0
  93. package/dist/app/managed-pi-initial-launch.js +61 -0
  94. package/dist/app/managed-pi-native-observation.d.ts +36 -0
  95. package/dist/app/managed-pi-native-observation.js +191 -0
  96. package/dist/app/managed-pi-readiness.d.ts +29 -0
  97. package/dist/app/managed-pi-readiness.js +288 -0
  98. package/dist/app/managed-pi-repair-stop.d.ts +21 -0
  99. package/dist/app/managed-pi-repair-stop.js +371 -0
  100. package/dist/app/managed-pi-resume-admission.d.ts +32 -0
  101. package/dist/app/managed-pi-resume-admission.js +618 -0
  102. package/dist/app/managed-pi-retirement.d.ts +28 -0
  103. package/dist/app/managed-pi-retirement.js +112 -0
  104. package/dist/app/managed-pi-sdk-control.d.ts +7 -0
  105. package/dist/app/managed-pi-sdk-control.js +169 -0
  106. package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
  107. package/dist/app/managed-pi-sdk-runtime.js +483 -0
  108. package/dist/app/managed-pi-source-admission.d.ts +14 -0
  109. package/dist/app/managed-pi-source-admission.js +323 -0
  110. package/dist/app/managed-pi-transition-host.d.ts +36 -0
  111. package/dist/app/managed-pi-transition-host.js +292 -0
  112. package/dist/app/managed-pi.d.ts +54 -0
  113. package/dist/app/managed-pi.js +248 -0
  114. package/dist/app/shell-readiness.d.ts +66 -0
  115. package/dist/app/shell-readiness.js +368 -0
  116. package/dist/app/task-pin.d.ts +2 -23
  117. package/dist/app/task-pin.js +2 -113
  118. package/dist/cli/commands/collaboration-binding.d.ts +13 -0
  119. package/dist/cli/commands/collaboration-binding.js +118 -0
  120. package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
  121. package/dist/cli/commands/collaboration-bootstrap.js +84 -0
  122. package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
  123. package/dist/cli/commands/collaboration-capabilities.js +127 -0
  124. package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
  125. package/dist/cli/commands/collaboration-handoff.js +102 -0
  126. package/dist/cli/commands/collaboration-message.d.ts +17 -0
  127. package/dist/cli/commands/collaboration-message.js +177 -0
  128. package/dist/cli/commands/collaboration-plan.d.ts +8 -0
  129. package/dist/cli/commands/collaboration-plan.js +108 -0
  130. package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
  131. package/dist/cli/commands/collaboration-preflight.js +167 -0
  132. package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
  133. package/dist/cli/commands/collaboration-recovery-run.js +224 -0
  134. package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
  135. package/dist/cli/commands/collaboration-recovery.js +104 -0
  136. package/dist/cli/commands/collaboration-resources.d.ts +11 -0
  137. package/dist/cli/commands/collaboration-resources.js +96 -0
  138. package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
  139. package/dist/cli/commands/collaboration-role-plan.js +69 -0
  140. package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
  141. package/dist/cli/commands/collaboration-role-run.js +128 -0
  142. package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
  143. package/dist/cli/commands/collaboration-rollover.js +103 -0
  144. package/dist/cli/commands/collaboration-state.d.ts +10 -0
  145. package/dist/cli/commands/collaboration-state.js +96 -0
  146. package/dist/cli/commands/collaboration-transition.d.ts +10 -0
  147. package/dist/cli/commands/collaboration-transition.js +75 -0
  148. package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
  149. package/dist/cli/commands/collaboration-work-run.js +178 -0
  150. package/dist/cli/commands/collaboration.d.ts +4 -0
  151. package/dist/cli/commands/collaboration.js +39 -4
  152. package/dist/cli/commands/continuity.js +2 -6
  153. package/dist/cli/commands/docs.d.ts +2 -0
  154. package/dist/cli/commands/docs.js +89 -15
  155. package/dist/cli/commands/host.d.ts +7 -1
  156. package/dist/cli/commands/host.js +122 -7
  157. package/dist/cli/commands/init.d.ts +20 -3
  158. package/dist/cli/commands/init.js +897 -232
  159. package/dist/cli/commands/mcp.d.ts +1 -0
  160. package/dist/cli/commands/mcp.js +11 -5
  161. package/dist/cli/commands/repair.d.ts +12 -0
  162. package/dist/cli/commands/repair.js +41 -0
  163. package/dist/cli/commands/runtime.d.ts +1 -1
  164. package/dist/cli/commands/runtime.js +139 -38
  165. package/dist/cli/commands/session.d.ts +7 -0
  166. package/dist/cli/commands/session.js +82 -0
  167. package/dist/cli/commands/task.js +19 -5
  168. package/dist/cli/run.js +866 -24
  169. package/dist/code/identity.d.ts +7 -0
  170. package/dist/code/identity.js +7 -3
  171. package/dist/code/registration.js +7 -0
  172. package/dist/code/service.d.ts +9 -0
  173. package/dist/code/service.js +16 -3
  174. package/dist/code/text.js +19 -7
  175. package/dist/collab/admission.d.ts +91 -66
  176. package/dist/collab/admission.js +21 -6
  177. package/dist/collab/canonical.d.ts +1 -1
  178. package/dist/collab/canonical.js +44 -3
  179. package/dist/collab/envelope.d.ts +2 -2
  180. package/dist/collab/envelope.js +12 -12
  181. package/dist/collab/first-work.d.ts +3 -0
  182. package/dist/collab/first-work.js +15 -0
  183. package/dist/collab/handoff.d.ts +16 -0
  184. package/dist/collab/handoff.js +39 -5
  185. package/dist/collab/index.d.ts +97 -1
  186. package/dist/collab/index.js +9 -1
  187. package/dist/collab/messages.js +126 -3
  188. package/dist/collab/persisted-messages.d.ts +123 -0
  189. package/dist/collab/persisted-messages.js +496 -0
  190. package/dist/collab/preflight.d.ts +1 -1
  191. package/dist/collab/resources.d.ts +13 -3
  192. package/dist/collab/resources.js +37 -6
  193. package/dist/collab/rollover.d.ts +311 -0
  194. package/dist/collab/rollover.js +1024 -0
  195. package/dist/collab/store.d.ts +1 -0
  196. package/dist/collab/store.js +1 -4
  197. package/dist/collab/types.d.ts +2 -354
  198. package/dist/collab/validation.d.ts +2 -10
  199. package/dist/collab/validation.js +2 -303
  200. package/dist/collab/work-plan.d.ts +105 -0
  201. package/dist/collab/work-plan.js +843 -0
  202. package/dist/continuity/context.d.ts +11 -2
  203. package/dist/continuity/context.js +100 -13
  204. package/dist/continuity/handoff-bundle.d.ts +296 -0
  205. package/dist/continuity/handoff-bundle.js +477 -0
  206. package/dist/continuity/index.d.ts +2 -0
  207. package/dist/continuity/index.js +1 -0
  208. package/dist/continuity/paths.js +10 -2
  209. package/dist/continuity/service.js +91 -15
  210. package/dist/continuity/task-pin.d.ts +24 -0
  211. package/dist/continuity/task-pin.js +590 -0
  212. package/dist/doctor.d.ts +1 -0
  213. package/dist/doctor.js +6 -0
  214. package/dist/experimental/index.d.ts +13 -0
  215. package/dist/experimental/index.js +25 -0
  216. package/dist/experimental/pi.d.ts +4 -0
  217. package/dist/experimental/pi.js +12 -0
  218. package/dist/hosts/claude/native.js +1 -1
  219. package/dist/hosts/codex/native.js +1 -1
  220. package/dist/hosts/codex/protocol.js +1 -1
  221. package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
  222. package/dist/hosts/integration/bootstrap-config.js +254 -0
  223. package/dist/hosts/integration/bundle-export.d.ts +44 -0
  224. package/dist/hosts/integration/bundle-export.js +366 -3
  225. package/dist/hosts/integration/bundle.js +3 -0
  226. package/dist/hosts/integration/hook.js +36 -9
  227. package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
  228. package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
  229. package/dist/hosts/integration/pappra-hook.mjs +61 -24749
  230. package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
  231. package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
  232. package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
  233. package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
  234. package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
  235. package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
  236. package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
  237. package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
  238. package/dist/hosts/integration/pi-native-events.d.ts +112 -0
  239. package/dist/hosts/integration/pi-native-events.js +348 -0
  240. package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
  241. package/dist/hosts/integration/pi-rollover-extension.js +431 -0
  242. package/dist/hosts/integration/recovery-config.d.ts +48 -0
  243. package/dist/hosts/integration/recovery-config.js +117 -0
  244. package/dist/hosts/integration/service.d.ts +6 -1
  245. package/dist/hosts/integration/service.js +485 -35
  246. package/dist/hosts/integration/support.d.ts +7 -0
  247. package/dist/hosts/integration/support.js +9 -1
  248. package/dist/hosts/integration/templates.d.ts +3 -0
  249. package/dist/hosts/integration/templates.js +26 -3
  250. package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
  251. package/dist/hosts/pi/managed-host-identity.js +40 -0
  252. package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
  253. package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
  254. package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
  255. package/dist/hosts/pi/managed-sdk-session.js +1639 -0
  256. package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
  257. package/dist/hosts/pi/managed-session-extension.js +84 -0
  258. package/dist/hosts/pi/managed-session.d.ts +177 -0
  259. package/dist/hosts/pi/managed-session.js +1557 -0
  260. package/dist/hosts/pi/protocol.js +1 -1
  261. package/dist/hosts/pi/query-result.js +1 -1
  262. package/dist/hosts/pi/resume-capability.d.ts +41 -0
  263. package/dist/hosts/pi/resume-capability.js +33 -0
  264. package/dist/hosts/pi/rpc.d.ts +182 -134
  265. package/dist/hosts/pi/rpc.js +230 -37
  266. package/dist/hosts/pilot.d.ts +97 -74
  267. package/dist/hosts/pilot.js +5 -1
  268. package/dist/hosts/version.d.ts +3 -0
  269. package/dist/hosts/version.js +18 -0
  270. package/dist/index.d.ts +0 -1
  271. package/dist/index.js +0 -1
  272. package/dist/infra/bounded-file.d.ts +19 -0
  273. package/dist/infra/bounded-file.js +66 -0
  274. package/dist/infra/fs.js +10 -2
  275. package/dist/infra/git.d.ts +29 -0
  276. package/dist/infra/git.js +45 -9
  277. package/dist/infra/json-property.d.ts +2 -0
  278. package/dist/infra/json-property.js +69 -0
  279. package/dist/infra/json.d.ts +4 -0
  280. package/dist/infra/json.js +24 -0
  281. package/dist/infra/owned-process-group.js +1 -1
  282. package/dist/infra/private-lock.d.ts +16 -0
  283. package/dist/infra/private-lock.js +148 -0
  284. package/dist/infra/private-state.d.ts +7 -0
  285. package/dist/infra/private-state.js +146 -0
  286. package/dist/infra/process-identity.d.ts +12 -0
  287. package/dist/infra/process-identity.js +142 -0
  288. package/dist/infra/validation.d.ts +5 -0
  289. package/dist/infra/validation.js +15 -0
  290. package/dist/init.d.ts +1 -0
  291. package/dist/init.js +6 -0
  292. package/dist/install/apply.d.ts +9 -1
  293. package/dist/install/apply.js +176 -12
  294. package/dist/install/dependencies.d.ts +38 -0
  295. package/dist/install/dependencies.js +282 -0
  296. package/dist/install/ownership.js +30 -1
  297. package/dist/install/references.d.ts +31 -0
  298. package/dist/install/references.js +208 -0
  299. package/dist/install/repair.d.ts +37 -0
  300. package/dist/install/repair.js +798 -0
  301. package/dist/install/reset-local.d.ts +16 -0
  302. package/dist/install/reset-local.js +304 -0
  303. package/dist/install/rollback.d.ts +3 -0
  304. package/dist/install/rollback.js +9 -3
  305. package/dist/install/runtime.d.ts +10 -6
  306. package/dist/install/runtime.js +299 -59
  307. package/dist/install/source-fingerprint.d.ts +2 -0
  308. package/dist/install/source-fingerprint.js +103 -0
  309. package/dist/knowledge/catalog.d.ts +1 -0
  310. package/dist/knowledge/catalog.js +143 -10
  311. package/dist/knowledge/journal.js +17 -7
  312. package/dist/knowledge/paths.js +2 -1
  313. package/dist/knowledge/refs.js +4 -0
  314. package/dist/knowledge/register-batch.d.ts +4 -0
  315. package/dist/knowledge/register-batch.js +225 -0
  316. package/dist/knowledge/relocate.d.ts +5 -0
  317. package/dist/knowledge/relocate.js +92 -0
  318. package/dist/knowledge/scan.js +103 -47
  319. package/dist/knowledge/service.d.ts +3 -0
  320. package/dist/knowledge/service.js +33 -1
  321. package/dist/knowledge/write.d.ts +4 -1
  322. package/dist/knowledge/write.js +1 -1
  323. package/dist/mcp/server.d.ts +4 -2
  324. package/dist/mcp/server.js +87 -136
  325. package/dist/schemas/collaboration-state-validation.d.ts +6 -0
  326. package/dist/schemas/collaboration-state-validation.js +342 -0
  327. package/dist/schemas/collaboration-state.d.ts +436 -0
  328. package/dist/schemas/collaboration-state.js +1 -0
  329. package/dist/schemas/collaboration.d.ts +335 -0
  330. package/dist/schemas/collaboration.js +272 -0
  331. package/dist/schemas/continuity.d.ts +8 -0
  332. package/dist/schemas/continuity.js +13 -3
  333. package/dist/schemas/doctor.d.ts +1 -0
  334. package/dist/schemas/doctor.js +1 -0
  335. package/dist/schemas/document.d.ts +61 -0
  336. package/dist/schemas/document.js +9 -0
  337. package/dist/schemas/errors.d.ts +1 -0
  338. package/dist/schemas/errors.js +1 -0
  339. package/dist/schemas/host.d.ts +24 -0
  340. package/dist/schemas/host.js +19 -0
  341. package/dist/schemas/install.d.ts +8 -2
  342. package/dist/schemas/install.js +11 -1
  343. package/dist/schemas/managed-pi-execution.d.ts +16 -0
  344. package/dist/schemas/managed-pi-execution.js +2 -0
  345. package/dist/schemas/outcome.d.ts +9 -5
  346. package/dist/workspace/config-file.js +1 -3
  347. package/dist/workspace/documents.js +12 -2
  348. package/dist/workspace/repositories.d.ts +7 -0
  349. package/dist/workspace/repositories.js +14 -5
  350. package/package.json +35 -35
  351. package/skills/pappra/SKILL.md +28 -7
  352. package/skills/pappra/references/collaboration.md +633 -0
  353. package/skills/pappra/references/environment.md +360 -0
  354. package/skills/pappra/references/hosts.md +20 -0
  355. package/skills/pappra/references/operations.md +36 -5
  356. package/dist/app/collab-from.d.ts +0 -35
  357. package/dist/app/collab-from.js +0 -84
@@ -7,6 +7,9 @@ import { errorMessage, failed, succeeded } from '../schemas/outcome.js';
7
7
  import { fileIdentitySchema, installJournalSchema, installLockSchema, workspaceInstallManifestSchema, } from '../schemas/install.js';
8
8
  import { isPathInside, samePath } from '../schemas/paths.js';
9
9
  import { observePath, readTextFileBounded, resolveRealpath, sha256Digest, } from '../infra/fs.js';
10
+ import { ensurePrivateState } from '../infra/private-state.js';
11
+ import { publishPrivateLock } from '../infra/private-lock.js';
12
+ import { captureProcessIdentity } from '../infra/process-identity.js';
10
13
  export function workspaceInstallPaths(root) {
11
14
  const localDir = join(root, '.pappra', 'local');
12
15
  return {
@@ -143,6 +146,11 @@ export async function writeBytesAtomic(path, bytes, boundaryRoot) {
143
146
  const boundary = await ensureStatePathBoundary(boundaryRoot, path);
144
147
  if (!boundary.ok)
145
148
  return boundary;
149
+ if (isPathInside(workspaceInstallPaths(boundaryRoot).localDir, path)) {
150
+ const prepared = await ensurePrivateState(boundaryRoot);
151
+ if (!prepared.ok)
152
+ return prepared;
153
+ }
146
154
  }
147
155
  const temporary = `${path}.tmp-${randomUUID()}`;
148
156
  try {
@@ -182,6 +190,18 @@ export async function acquireInstallLock(path, operation, target, boundaryRoot)
182
190
  existing: null,
183
191
  };
184
192
  }
193
+ if (isPathInside(workspaceInstallPaths(boundaryRoot).localDir, path)) {
194
+ const prepared = await ensurePrivateState(boundaryRoot);
195
+ if (!prepared.ok)
196
+ return {
197
+ ok: false,
198
+ status: 'failed',
199
+ lock: null,
200
+ path,
201
+ message: prepared.message,
202
+ existing: null,
203
+ };
204
+ }
185
205
  }
186
206
  try {
187
207
  await mkdir(dirname(path), { recursive: true });
@@ -202,10 +222,19 @@ export async function acquireInstallLock(path, operation, target, boundaryRoot)
202
222
  operation,
203
223
  target,
204
224
  pid: process.pid,
225
+ ...(captureProcessIdentity() === null
226
+ ? {}
227
+ : { process_identity: captureProcessIdentity() }),
205
228
  createdAt: new Date().toISOString(),
206
229
  };
207
230
  try {
208
- await writeNewFile(path, Buffer.from(`${JSON.stringify(lock, null, 2)}\n`));
231
+ await publishPrivateLock(path, Buffer.from(`${JSON.stringify(lock, null, 2)}\n`), async () => {
232
+ if (boundaryRoot !== undefined) {
233
+ const boundary = await ensureStatePathBoundary(boundaryRoot, path);
234
+ if (!boundary.ok)
235
+ throw new Error(boundary.message);
236
+ }
237
+ });
209
238
  return {
210
239
  ok: true,
211
240
  status: 'acquired',
@@ -0,0 +1,31 @@
1
+ import type { RuntimeManifest } from '../schemas/install.js';
2
+ import { type Outcome } from '../schemas/outcome.js';
3
+ export interface RuntimeReferenceReport {
4
+ readonly coverage: 'registered-workspaces-only';
5
+ readonly active: readonly {
6
+ workspaceRoot: string;
7
+ workspaceId: string;
8
+ executable: string;
9
+ }[];
10
+ readonly unknown: readonly string[];
11
+ /** A registry never proves that no unregistered workspace exists. */
12
+ readonly unregisteredCoverage: 'unknown';
13
+ }
14
+ export declare function runtimeReferencesDirectory(dataDir: string): string;
15
+ /** Lexical and real-path aliases of the same global data directory are one runtime. */
16
+ export declare function runtimeBindingMatches(dataDir: string, version: string, binding: {
17
+ readonly version: string;
18
+ readonly executable: string;
19
+ } | null | undefined): Promise<boolean>;
20
+ /** Reads only explicitly registered roots. No home-directory discovery occurs. */
21
+ export declare function inspectRuntimeReferences(dataDir: string, version: string): Promise<RuntimeReferenceReport>;
22
+ export interface RuntimeReferenceLease {
23
+ readonly path: string;
24
+ readonly lockPath: string;
25
+ readonly release: () => Promise<Outcome<null>>;
26
+ }
27
+ /**
28
+ * Publish before machine binding while holding the runtime's deletion/install
29
+ * lock. A crash cannot create a new binding absent from the explicit registry.
30
+ */
31
+ export declare function beginRuntimeReference(root: string, runtime: RuntimeManifest): Promise<Outcome<RuntimeReferenceLease>>;
@@ -0,0 +1,208 @@
1
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
2
+ import { readdir } from 'node:fs/promises';
3
+ import { z } from 'zod';
4
+ import { observePath, resolveRealpath, sha256Digest } from '../infra/fs.js';
5
+ import { inspectMachineConfig, inspectWorkspaceConfig, } from '../workspace/config.js';
6
+ import { isPathInside } from '../schemas/paths.js';
7
+ import { failed, succeeded } from '../schemas/outcome.js';
8
+ import { diagnosticCodes } from '../schemas/errors.js';
9
+ import { acquireInstallLock, ensureStatePathBoundary, readJsonDocument, releaseInstallLock, writeJsonAtomic, } from './ownership.js';
10
+ const referenceSchema = z.strictObject({
11
+ schema: z.literal('pappra/runtime-reference/1'),
12
+ workspaceRoot: z.string().min(1).max(4096).refine(isAbsolute),
13
+ workspaceId: z.uuid(),
14
+ rootIdentity: z.strictObject({
15
+ dev: z.number().int().nonnegative(),
16
+ ino: z.number().int().nonnegative(),
17
+ }),
18
+ intendedRuntime: z.strictObject({
19
+ version: z.string().min(1).max(128),
20
+ executable: z.string().min(1).max(4096),
21
+ runtimeRoot: z.string().min(1).max(4096),
22
+ }),
23
+ registeredAt: z.iso.datetime({ offset: true }),
24
+ });
25
+ export function runtimeReferencesDirectory(dataDir) {
26
+ return join(resolve(dataDir), 'runtime-references');
27
+ }
28
+ /** Lexical and real-path aliases of the same global data directory are one runtime. */
29
+ export async function runtimeBindingMatches(dataDir, version, binding) {
30
+ if (binding?.version !== version)
31
+ return false;
32
+ const target = join(resolve(dataDir), 'runtimes', version);
33
+ const executable = resolve(binding.executable);
34
+ if (isPathInside(target, executable))
35
+ return true;
36
+ const realTarget = await resolveRealpath(target);
37
+ const realExecutable = await resolveRealpath(executable);
38
+ return (realTarget !== null &&
39
+ realExecutable !== null &&
40
+ isPathInside(realTarget, realExecutable));
41
+ }
42
+ /** Reads only explicitly registered roots. No home-directory discovery occurs. */
43
+ export async function inspectRuntimeReferences(dataDir, version) {
44
+ const active = [];
45
+ const unknown = [];
46
+ const canonicalDataDir = (await resolveRealpath(dataDir)) ?? resolve(dataDir);
47
+ const directory = runtimeReferencesDirectory(canonicalDataDir);
48
+ const report = () => ({
49
+ coverage: 'registered-workspaces-only',
50
+ active,
51
+ unknown,
52
+ unregisteredCoverage: 'unknown',
53
+ });
54
+ const boundary = await ensureStatePathBoundary(canonicalDataDir, directory);
55
+ if (!boundary.ok) {
56
+ unknown.push('registry_path_unsafe');
57
+ return report();
58
+ }
59
+ const presence = await observePath(directory).catch(() => null);
60
+ if (presence?.kind === 'missing') {
61
+ unknown.push('registry_missing');
62
+ return report();
63
+ }
64
+ if (presence?.kind !== 'directory') {
65
+ unknown.push('registry_unreadable_or_redirected');
66
+ return report();
67
+ }
68
+ let names;
69
+ try {
70
+ names = (await readdir(directory)).sort();
71
+ }
72
+ catch {
73
+ unknown.push('registry_unreadable');
74
+ return report();
75
+ }
76
+ if (names.length > 4096) {
77
+ unknown.push('registry_entry_limit');
78
+ return report();
79
+ }
80
+ for (const name of names) {
81
+ if (!/^sha256-[0-9a-f]{64}\.json$/.test(name)) {
82
+ unknown.push('unexpected_registry_entry');
83
+ continue;
84
+ }
85
+ const read = await readJsonDocument(join(directory, name), referenceSchema);
86
+ if (!read.value) {
87
+ unknown.push(`reference_unreadable:${name}`);
88
+ continue;
89
+ }
90
+ const entry = read.value;
91
+ const root = await observePath(entry.workspaceRoot).catch(() => null);
92
+ const canonical = await resolveRealpath(entry.workspaceRoot);
93
+ if (root?.kind !== 'directory' ||
94
+ !root.stats ||
95
+ canonical !== entry.workspaceRoot ||
96
+ root.stats.dev !== entry.rootIdentity.dev ||
97
+ root.stats.ino !== entry.rootIdentity.ino) {
98
+ unknown.push(`workspace_missing_or_replaced:${name}`);
99
+ continue;
100
+ }
101
+ const workspace = await inspectWorkspaceConfig(entry.workspaceRoot);
102
+ if (workspace.value?.workspaceId !== entry.workspaceId) {
103
+ unknown.push(`workspace_identity_unknown:${name}`);
104
+ continue;
105
+ }
106
+ const machine = await inspectMachineConfig(entry.workspaceRoot);
107
+ if (!machine.value) {
108
+ unknown.push(`machine_binding_unknown:${name}`);
109
+ continue;
110
+ }
111
+ const binding = machine.value.runtime;
112
+ if (binding && (await runtimeBindingMatches(dataDir, version, binding)))
113
+ active.push({
114
+ workspaceRoot: entry.workspaceRoot,
115
+ workspaceId: entry.workspaceId,
116
+ executable: binding.executable,
117
+ });
118
+ }
119
+ return report();
120
+ }
121
+ /**
122
+ * Publish before machine binding while holding the runtime's deletion/install
123
+ * lock. A crash cannot create a new binding absent from the explicit registry.
124
+ */
125
+ export async function beginRuntimeReference(root, runtime) {
126
+ const canonical = await resolveRealpath(root);
127
+ const observation = canonical === null ? null : await observePath(canonical).catch(() => null);
128
+ const workspace = canonical === null ? null : await inspectWorkspaceConfig(canonical);
129
+ if (!canonical ||
130
+ observation?.kind !== 'directory' ||
131
+ !observation.stats ||
132
+ !workspace?.value)
133
+ return failed(diagnosticCodes.initPlanStale, 'Cannot register a runtime reference for an unverified workspace root.');
134
+ const selectedDataDir = dirname(dirname(resolve(runtime.runtimeRoot)));
135
+ if (join(selectedDataDir, 'runtimes', runtime.runtimeVersion) !==
136
+ resolve(runtime.runtimeRoot) ||
137
+ !isPathInside(runtime.runtimeRoot, runtime.executable))
138
+ return failed(diagnosticCodes.initTargetUnsafe, 'Runtime reference is not inside its exact version directory.');
139
+ const dataDir = (await resolveRealpath(selectedDataDir)) ?? selectedDataDir;
140
+ const lockPath = join(dataDir, 'runtimes', '.locks', `${runtime.runtimeVersion}.lock`);
141
+ const entry = {
142
+ schema: 'pappra/runtime-reference/1',
143
+ workspaceRoot: canonical,
144
+ workspaceId: workspace.value.workspaceId,
145
+ rootIdentity: { dev: observation.stats.dev, ino: observation.stats.ino },
146
+ intendedRuntime: {
147
+ version: runtime.runtimeVersion,
148
+ executable: runtime.executable,
149
+ runtimeRoot: runtime.runtimeRoot,
150
+ },
151
+ registeredAt: new Date().toISOString(),
152
+ };
153
+ const key = sha256Digest(JSON.stringify([
154
+ canonical,
155
+ entry.workspaceId,
156
+ entry.rootIdentity.dev,
157
+ entry.rootIdentity.ino,
158
+ ])).replace(':', '-');
159
+ const path = join(runtimeReferencesDirectory(dataDir), `${key}.json`);
160
+ const boundary = await ensureStatePathBoundary(dataDir, path);
161
+ if (!boundary.ok)
162
+ return boundary;
163
+ let existing = await readJsonDocument(path, referenceSchema);
164
+ const unchanged = existing.value &&
165
+ existing.value.workspaceRoot === canonical &&
166
+ existing.value.workspaceId === entry.workspaceId &&
167
+ existing.value.rootIdentity.dev === entry.rootIdentity.dev &&
168
+ existing.value.rootIdentity.ino === entry.rootIdentity.ino &&
169
+ JSON.stringify(existing.value.intendedRuntime) ===
170
+ JSON.stringify(entry.intendedRuntime);
171
+ if (unchanged && (await observePath(lockPath)).kind === 'missing') {
172
+ const machine = await inspectMachineConfig(canonical);
173
+ if (machine.value?.runtime?.version === runtime.runtimeVersion &&
174
+ machine.value.runtime.executable === runtime.executable) {
175
+ // This binding is already published and protected from deletion by its
176
+ // verified registry entry. A read-only repeat needs no registration lease.
177
+ return succeeded({
178
+ path,
179
+ lockPath,
180
+ release: async () => succeeded(null),
181
+ });
182
+ }
183
+ }
184
+ const lock = await acquireInstallLock(lockPath, 'update-machine-runtime', runtime.runtimeRoot, dataDir);
185
+ if (!lock.ok || !lock.lock)
186
+ return failed(diagnosticCodes.initApplyResidual, lock.message ?? 'Runtime reference registration is locked.');
187
+ const release = () => releaseInstallLock(lockPath, lock.lock.token);
188
+ existing = await readJsonDocument(path, referenceSchema);
189
+ if (existing.exists &&
190
+ (!existing.value ||
191
+ existing.value.workspaceRoot !== canonical ||
192
+ existing.value.workspaceId !== entry.workspaceId ||
193
+ existing.value.rootIdentity.dev !== entry.rootIdentity.dev ||
194
+ existing.value.rootIdentity.ino !== entry.rootIdentity.ino)) {
195
+ await release();
196
+ return failed(diagnosticCodes.initApplyResidual, 'Existing runtime reference is unreadable or belongs to another workspace identity; it was preserved.');
197
+ }
198
+ if (existing.value &&
199
+ JSON.stringify(existing.value.intendedRuntime) ===
200
+ JSON.stringify(entry.intendedRuntime))
201
+ return succeeded({ path, lockPath, release });
202
+ const written = await writeJsonAtomic(path, entry, dataDir);
203
+ if (!written.ok) {
204
+ await release();
205
+ return written;
206
+ }
207
+ return succeeded({ path, lockPath, release });
208
+ }
@@ -0,0 +1,37 @@
1
+ import { type PrivateLockObservation } from '../infra/private-lock.js';
2
+ import { type LocalResetResult } from './reset-local.js';
3
+ export interface RepairOptions {
4
+ readonly root: string;
5
+ readonly runtimeVersion?: string;
6
+ readonly installId?: string;
7
+ readonly dataDir?: string;
8
+ readonly apply?: boolean;
9
+ /** Explicit operator assertion that every potentially affected writer has stopped. */
10
+ readonly quarantineUnownedLocks?: boolean;
11
+ readonly resetLocal?: boolean;
12
+ readonly allWritersStopped?: boolean;
13
+ /** Test-only dependency seam; never supplied by the CLI. */
14
+ readonly afterLocalArchive?: () => Promise<void> | void;
15
+ }
16
+ export interface RepairEntry {
17
+ readonly journal: string;
18
+ readonly installId: string;
19
+ readonly operation: string;
20
+ readonly status: string;
21
+ }
22
+ export interface RepairResult {
23
+ readonly ok: boolean;
24
+ readonly mode: 'plan' | 'apply';
25
+ readonly entries: readonly RepairEntry[];
26
+ readonly repaired: readonly string[];
27
+ readonly locks: readonly string[];
28
+ readonly errors: readonly string[];
29
+ readonly lockStates: readonly Pick<PrivateLockObservation, 'file' | 'state'>[];
30
+ readonly preserved: readonly string[];
31
+ readonly quarantined: readonly string[];
32
+ readonly reset: LocalResetResult | null;
33
+ }
34
+ /** Bounded private lock inventory shared by doctor and explicit repair. */
35
+ export declare function inspectWorkspaceLocks(root: string): Promise<readonly PrivateLockObservation[]>;
36
+ /** Explicit, bounded journal recovery. Preview never removes a lock or changes state. */
37
+ export declare function repairInstall(options: RepairOptions): Promise<RepairResult>;