@cleocode/core 2026.9.24 → 2026.9.26

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 (481) hide show
  1. package/dist/admin/import-tasks.d.ts.map +1 -1
  2. package/dist/admin/import-tasks.js +5 -2
  3. package/dist/admin/import-tasks.js.map +1 -1
  4. package/dist/admin/import.d.ts.map +1 -1
  5. package/dist/admin/import.js +7 -2
  6. package/dist/admin/import.js.map +1 -1
  7. package/dist/audit-prune.d.ts +32 -0
  8. package/dist/audit-prune.d.ts.map +1 -1
  9. package/dist/audit-prune.js +37 -3
  10. package/dist/audit-prune.js.map +1 -1
  11. package/dist/build-provenance.json +2 -2
  12. package/dist/cloud/crypto.d.ts +1 -2
  13. package/dist/cloud/crypto.d.ts.map +1 -1
  14. package/dist/cloud/crypto.js +1 -11
  15. package/dist/cloud/crypto.js.map +1 -1
  16. package/dist/cloud/index.d.ts +1 -0
  17. package/dist/cloud/index.d.ts.map +1 -1
  18. package/dist/cloud/index.js +1 -0
  19. package/dist/cloud/index.js.map +1 -1
  20. package/dist/cloud/nexus-auth.d.ts +26 -1
  21. package/dist/cloud/nexus-auth.d.ts.map +1 -1
  22. package/dist/cloud/nexus-auth.js +61 -32
  23. package/dist/cloud/nexus-auth.js.map +1 -1
  24. package/dist/cloud/nexus-credentials.js +2 -2
  25. package/dist/cloud/nexus-credentials.js.map +1 -1
  26. package/dist/cloud/nexus-device.d.ts +809 -0
  27. package/dist/cloud/nexus-device.d.ts.map +1 -0
  28. package/dist/cloud/nexus-device.js +1680 -0
  29. package/dist/cloud/nexus-device.js.map +1 -0
  30. package/dist/cloud/nexus-enrol.d.ts +210 -0
  31. package/dist/cloud/nexus-enrol.d.ts.map +1 -0
  32. package/dist/cloud/nexus-enrol.js +1023 -0
  33. package/dist/cloud/nexus-enrol.js.map +1 -0
  34. package/dist/cloud/nexus-link.d.ts +3 -0
  35. package/dist/cloud/nexus-link.d.ts.map +1 -1
  36. package/dist/cloud/nexus-link.js +38 -4
  37. package/dist/cloud/nexus-link.js.map +1 -1
  38. package/dist/cloud/signing.d.ts +20 -0
  39. package/dist/cloud/signing.d.ts.map +1 -1
  40. package/dist/cloud/signing.js +14 -0
  41. package/dist/cloud/signing.js.map +1 -1
  42. package/dist/cloud/uuidv7.d.ts +9 -0
  43. package/dist/cloud/uuidv7.d.ts.map +1 -0
  44. package/dist/cloud/uuidv7.js +19 -0
  45. package/dist/cloud/uuidv7.js.map +1 -0
  46. package/dist/crypto/credentials.d.ts +61 -0
  47. package/dist/crypto/credentials.d.ts.map +1 -1
  48. package/dist/crypto/credentials.js +154 -58
  49. package/dist/crypto/credentials.js.map +1 -1
  50. package/dist/decide/bench/index.d.ts +1 -1
  51. package/dist/decide/bench/index.d.ts.map +1 -1
  52. package/dist/decide/bench/index.js +1 -1
  53. package/dist/decide/bench/index.js.map +1 -1
  54. package/dist/decide/bench/operation.d.ts +1 -1
  55. package/dist/decide/bench/operation.d.ts.map +1 -1
  56. package/dist/decide/bench/operation.js +1 -1
  57. package/dist/decide/bench/operation.js.map +1 -1
  58. package/dist/decide/bench/profiles.d.ts +50 -22
  59. package/dist/decide/bench/profiles.d.ts.map +1 -1
  60. package/dist/decide/bench/profiles.js +103 -59
  61. package/dist/decide/bench/profiles.js.map +1 -1
  62. package/dist/decide/bench/report.d.ts.map +1 -1
  63. package/dist/decide/bench/report.js +6 -1
  64. package/dist/decide/bench/report.js.map +1 -1
  65. package/dist/decide/bench/runner.d.ts +6 -1
  66. package/dist/decide/bench/runner.d.ts.map +1 -1
  67. package/dist/decide/bench/runner.js +1 -0
  68. package/dist/decide/bench/runner.js.map +1 -1
  69. package/dist/decide/client.d.ts.map +1 -1
  70. package/dist/decide/client.js +11 -5
  71. package/dist/decide/client.js.map +1 -1
  72. package/dist/decide/jev-wire.d.ts +31 -1
  73. package/dist/decide/jev-wire.d.ts.map +1 -1
  74. package/dist/decide/jev-wire.js +61 -7
  75. package/dist/decide/jev-wire.js.map +1 -1
  76. package/dist/docs/export-document.js +12907 -10622
  77. package/dist/docs/export-document.js.map +3 -3
  78. package/dist/doctor/dependency-cycles.d.ts +41 -0
  79. package/dist/doctor/dependency-cycles.d.ts.map +1 -0
  80. package/dist/doctor/dependency-cycles.js +39 -0
  81. package/dist/doctor/dependency-cycles.js.map +1 -0
  82. package/dist/doctor/index.d.ts +2 -0
  83. package/dist/doctor/index.d.ts.map +1 -1
  84. package/dist/doctor/index.js +1 -0
  85. package/dist/doctor/index.js.map +1 -1
  86. package/dist/doctor/orphan-ac-bindings.d.ts +64 -0
  87. package/dist/doctor/orphan-ac-bindings.d.ts.map +1 -0
  88. package/dist/doctor/orphan-ac-bindings.js +59 -0
  89. package/dist/doctor/orphan-ac-bindings.js.map +1 -0
  90. package/dist/doctor/projects.d.ts.map +1 -1
  91. package/dist/doctor/projects.js +43 -29
  92. package/dist/doctor/projects.js.map +1 -1
  93. package/dist/doctor/row-identity.d.ts +42 -0
  94. package/dist/doctor/row-identity.d.ts.map +1 -0
  95. package/dist/doctor/row-identity.js +151 -0
  96. package/dist/doctor/row-identity.js.map +1 -0
  97. package/dist/doctor/twin-collapse.d.ts +58 -2
  98. package/dist/doctor/twin-collapse.d.ts.map +1 -1
  99. package/dist/doctor/twin-collapse.js +212 -14
  100. package/dist/doctor/twin-collapse.js.map +1 -1
  101. package/dist/error-catalog.d.ts.map +1 -1
  102. package/dist/error-catalog.js +4 -0
  103. package/dist/error-catalog.js.map +1 -1
  104. package/dist/error-registry.d.ts.map +1 -1
  105. package/dist/error-registry.js +8 -0
  106. package/dist/error-registry.js.map +1 -1
  107. package/dist/lifecycle/verification/satisfies-validator.d.ts.map +1 -1
  108. package/dist/lifecycle/verification/satisfies-validator.js +10 -0
  109. package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
  110. package/dist/llm/plugin-facade.js +17767 -15482
  111. package/dist/llm/plugin-facade.js.map +3 -3
  112. package/dist/memory/index.d.ts.map +1 -1
  113. package/dist/memory/index.js +19 -10
  114. package/dist/memory/index.js.map +1 -1
  115. package/dist/memory/manifest-identity.d.ts +171 -0
  116. package/dist/memory/manifest-identity.d.ts.map +1 -0
  117. package/dist/memory/manifest-identity.js +345 -0
  118. package/dist/memory/manifest-identity.js.map +1 -0
  119. package/dist/memory/mental-model-queue.d.ts +14 -4
  120. package/dist/memory/mental-model-queue.d.ts.map +1 -1
  121. package/dist/memory/mental-model-queue.js +64 -35
  122. package/dist/memory/mental-model-queue.js.map +1 -1
  123. package/dist/memory/pipeline-manifest-sqlite.d.ts +47 -5
  124. package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
  125. package/dist/memory/pipeline-manifest-sqlite.js +130 -14
  126. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
  127. package/dist/memory/retrieval/observe.d.ts.map +1 -1
  128. package/dist/memory/retrieval/observe.js +2 -0
  129. package/dist/memory/retrieval/observe.js.map +1 -1
  130. package/dist/memory/transcript-scanner.js +1 -1
  131. package/dist/nexus/deps.d.ts.map +1 -1
  132. package/dist/nexus/deps.js +7 -14
  133. package/dist/nexus/deps.js.map +1 -1
  134. package/dist/nexus/discover.d.ts.map +1 -1
  135. package/dist/nexus/discover.js +6 -9
  136. package/dist/nexus/discover.js.map +1 -1
  137. package/dist/nexus/git-state.d.ts.map +1 -1
  138. package/dist/nexus/git-state.js +10 -1
  139. package/dist/nexus/git-state.js.map +1 -1
  140. package/dist/nexus/projects-clean.d.ts.map +1 -1
  141. package/dist/nexus/projects-clean.js +22 -11
  142. package/dist/nexus/projects-clean.js.map +1 -1
  143. package/dist/nexus/projects-scan.d.ts.map +1 -1
  144. package/dist/nexus/projects-scan.js +8 -13
  145. package/dist/nexus/projects-scan.js.map +1 -1
  146. package/dist/nexus/query.d.ts.map +1 -1
  147. package/dist/nexus/query.js +4 -12
  148. package/dist/nexus/query.js.map +1 -1
  149. package/dist/nexus/registry-roots.d.ts +5 -2
  150. package/dist/nexus/registry-roots.d.ts.map +1 -1
  151. package/dist/nexus/registry-roots.js +8 -10
  152. package/dist/nexus/registry-roots.js.map +1 -1
  153. package/dist/nexus/registry.d.ts +18 -0
  154. package/dist/nexus/registry.d.ts.map +1 -1
  155. package/dist/nexus/registry.js +37 -16
  156. package/dist/nexus/registry.js.map +1 -1
  157. package/dist/nexus/workspace.d.ts.map +1 -1
  158. package/dist/nexus/workspace.js +54 -4
  159. package/dist/nexus/workspace.js.map +1 -1
  160. package/dist/orchestrate/lifecycle-ops.js +1 -1
  161. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  162. package/dist/orchestrate/pivot.d.ts.map +1 -1
  163. package/dist/orchestrate/pivot.js +4 -2
  164. package/dist/orchestrate/pivot.js.map +1 -1
  165. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  166. package/dist/orchestrate/query-ops.js +3 -3
  167. package/dist/orchestrate/query-ops.js.map +1 -1
  168. package/dist/orchestrate/spawn-ops.js +1 -1
  169. package/dist/orchestrate/spawn-ops.js.map +1 -1
  170. package/dist/orchestrate/worktree-complete.d.ts.map +1 -1
  171. package/dist/orchestrate/worktree-complete.js +40 -2
  172. package/dist/orchestrate/worktree-complete.js.map +1 -1
  173. package/dist/orchestration/index.js +1 -1
  174. package/dist/orchestration/index.js.map +1 -1
  175. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  176. package/dist/orchestration/lead-rollup.js +3 -3
  177. package/dist/orchestration/lead-rollup.js.map +1 -1
  178. package/dist/orchestration/parallel.js +1 -1
  179. package/dist/orchestration/parallel.js.map +1 -1
  180. package/dist/orchestration/spawn-prompt.d.ts +26 -0
  181. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  182. package/dist/orchestration/spawn-prompt.js +62 -91
  183. package/dist/orchestration/spawn-prompt.js.map +1 -1
  184. package/dist/orchestration/wave-topic.d.ts +4 -2
  185. package/dist/orchestration/wave-topic.d.ts.map +1 -1
  186. package/dist/orchestration/wave-topic.js +6 -4
  187. package/dist/orchestration/wave-topic.js.map +1 -1
  188. package/dist/orchestration/waves.d.ts +2 -1
  189. package/dist/orchestration/waves.d.ts.map +1 -1
  190. package/dist/orchestration/waves.js +4 -3
  191. package/dist/orchestration/waves.js.map +1 -1
  192. package/dist/phases/index.d.ts.map +1 -1
  193. package/dist/phases/index.js +4 -2
  194. package/dist/phases/index.js.map +1 -1
  195. package/dist/release/ci-evidence.d.ts +190 -0
  196. package/dist/release/ci-evidence.d.ts.map +1 -1
  197. package/dist/release/ci-evidence.js +436 -4
  198. package/dist/release/ci-evidence.js.map +1 -1
  199. package/dist/sdk/validator-ac-pull.d.ts.map +1 -1
  200. package/dist/sdk/validator-ac-pull.js +2 -1
  201. package/dist/sdk/validator-ac-pull.js.map +1 -1
  202. package/dist/selfimprove/fix-gen.js +12807 -10522
  203. package/dist/selfimprove/fix-gen.js.map +3 -3
  204. package/dist/sentient/cross-project-hygiene.d.ts +5 -0
  205. package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
  206. package/dist/sentient/cross-project-hygiene.js +7 -1
  207. package/dist/sentient/cross-project-hygiene.js.map +1 -1
  208. package/dist/sentient/propose-tick.d.ts.map +1 -1
  209. package/dist/sentient/propose-tick.js +2 -3
  210. package/dist/sentient/propose-tick.js.map +1 -1
  211. package/dist/sentient/stage-drift-tick.d.ts.map +1 -1
  212. package/dist/sentient/stage-drift-tick.js +2 -3
  213. package/dist/sentient/stage-drift-tick.js.map +1 -1
  214. package/dist/session/engine-ops.d.ts.map +1 -1
  215. package/dist/session/engine-ops.js +77 -49
  216. package/dist/session/engine-ops.js.map +1 -1
  217. package/dist/sessions/focus-state-store.d.ts +69 -23
  218. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  219. package/dist/sessions/focus-state-store.js +113 -27
  220. package/dist/sessions/focus-state-store.js.map +1 -1
  221. package/dist/sessions/handoff.js +1 -1
  222. package/dist/sessions/handoff.js.map +1 -1
  223. package/dist/sessions/index.d.ts.map +1 -1
  224. package/dist/sessions/index.js +24 -13
  225. package/dist/sessions/index.js.map +1 -1
  226. package/dist/sessions/terminal-identity.d.ts +165 -17
  227. package/dist/sessions/terminal-identity.d.ts.map +1 -1
  228. package/dist/sessions/terminal-identity.js +341 -32
  229. package/dist/sessions/terminal-identity.js.map +1 -1
  230. package/dist/skills/manifests/research.js +2 -0
  231. package/dist/skills/manifests/research.js.map +1 -1
  232. package/dist/skills/orchestrator/startup.js +2 -2
  233. package/dist/skills/orchestrator/startup.js.map +1 -1
  234. package/dist/skills/orchestrator/validator.d.ts.map +1 -1
  235. package/dist/skills/orchestrator/validator.js +9 -2
  236. package/dist/skills/orchestrator/validator.js.map +1 -1
  237. package/dist/skills/validation.d.ts +4 -1
  238. package/dist/skills/validation.d.ts.map +1 -1
  239. package/dist/skills/validation.js +11 -3
  240. package/dist/skills/validation.js.map +1 -1
  241. package/dist/snapshot/index.d.ts +4 -0
  242. package/dist/snapshot/index.d.ts.map +1 -1
  243. package/dist/snapshot/index.js +50 -3
  244. package/dist/snapshot/index.js.map +1 -1
  245. package/dist/spawn/branch-lock.d.ts +111 -7
  246. package/dist/spawn/branch-lock.d.ts.map +1 -1
  247. package/dist/spawn/branch-lock.js +296 -8
  248. package/dist/spawn/branch-lock.js.map +1 -1
  249. package/dist/store/ac-binding-prune.d.ts +144 -0
  250. package/dist/store/ac-binding-prune.d.ts.map +1 -0
  251. package/dist/store/ac-binding-prune.js +293 -0
  252. package/dist/store/ac-binding-prune.js.map +1 -0
  253. package/dist/store/agent-registry-store.d.ts.map +1 -1
  254. package/dist/store/agent-registry-store.js +3 -2
  255. package/dist/store/agent-registry-store.js.map +1 -1
  256. package/dist/store/backup-sidecar.d.ts +85 -2
  257. package/dist/store/backup-sidecar.d.ts.map +1 -1
  258. package/dist/store/backup-sidecar.js +173 -1
  259. package/dist/store/backup-sidecar.js.map +1 -1
  260. package/dist/store/conduit-sqlite.d.ts.map +1 -1
  261. package/dist/store/conduit-sqlite.js +3 -2
  262. package/dist/store/conduit-sqlite.js.map +1 -1
  263. package/dist/store/credential-transfer.d.ts.map +1 -1
  264. package/dist/store/credential-transfer.js +3 -0
  265. package/dist/store/credential-transfer.js.map +1 -1
  266. package/dist/store/db-helpers.d.ts +32 -13
  267. package/dist/store/db-helpers.d.ts.map +1 -1
  268. package/dist/store/db-helpers.js +144 -40
  269. package/dist/store/db-helpers.js.map +1 -1
  270. package/dist/store/dependency-cycles.d.ts +164 -0
  271. package/dist/store/dependency-cycles.d.ts.map +1 -0
  272. package/dist/store/dependency-cycles.js +360 -0
  273. package/dist/store/dependency-cycles.js.map +1 -0
  274. package/dist/store/display-id-alias.d.ts +585 -0
  275. package/dist/store/display-id-alias.d.ts.map +1 -0
  276. package/dist/store/display-id-alias.js +1538 -0
  277. package/dist/store/display-id-alias.js.map +1 -0
  278. package/dist/store/dual-scope-db.d.ts.map +1 -1
  279. package/dist/store/dual-scope-db.js +48 -0
  280. package/dist/store/dual-scope-db.js.map +1 -1
  281. package/dist/store/exodus/migrate.d.ts.map +1 -1
  282. package/dist/store/exodus/migrate.js +8 -1
  283. package/dist/store/exodus/migrate.js.map +1 -1
  284. package/dist/store/exodus/reconcile.d.ts.map +1 -1
  285. package/dist/store/exodus/reconcile.js +7 -2
  286. package/dist/store/exodus/reconcile.js.map +1 -1
  287. package/dist/store/exodus/recovery.d.ts.map +1 -1
  288. package/dist/store/exodus/recovery.js +20 -1
  289. package/dist/store/exodus/recovery.js.map +1 -1
  290. package/dist/store/global-salt.d.ts +35 -0
  291. package/dist/store/global-salt.d.ts.map +1 -1
  292. package/dist/store/global-salt.js +136 -16
  293. package/dist/store/global-salt.js.map +1 -1
  294. package/dist/store/legacy-tasks-lineage.d.ts.map +1 -1
  295. package/dist/store/legacy-tasks-lineage.js +19 -2
  296. package/dist/store/legacy-tasks-lineage.js.map +1 -1
  297. package/dist/store/migration-sqlite.d.ts.map +1 -1
  298. package/dist/store/migration-sqlite.js +19 -11
  299. package/dist/store/migration-sqlite.js.map +1 -1
  300. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  301. package/dist/store/portable-bundle-scan.js +6 -0
  302. package/dist/store/portable-bundle-scan.js.map +1 -1
  303. package/dist/store/row-identity-flag.d.ts +26 -0
  304. package/dist/store/row-identity-flag.d.ts.map +1 -0
  305. package/dist/store/row-identity-flag.js +31 -0
  306. package/dist/store/row-identity-flag.js.map +1 -0
  307. package/dist/store/row-identity-registry.d.ts +61 -0
  308. package/dist/store/row-identity-registry.d.ts.map +1 -0
  309. package/dist/store/row-identity-registry.js +206 -0
  310. package/dist/store/row-identity-registry.js.map +1 -0
  311. package/dist/store/row-identity.d.ts +480 -0
  312. package/dist/store/row-identity.d.ts.map +1 -0
  313. package/dist/store/row-identity.js +1509 -0
  314. package/dist/store/row-identity.js.map +1 -0
  315. package/dist/store/safety-data-accessor.d.ts +5 -2
  316. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  317. package/dist/store/safety-data-accessor.js +9 -2
  318. package/dist/store/safety-data-accessor.js.map +1 -1
  319. package/dist/store/schema/attachments.d.ts +1 -1
  320. package/dist/store/schema/audit.d.ts +4 -4
  321. package/dist/store/schema/background-jobs.d.ts +1 -1
  322. package/dist/store/schema/cleo-global/nexus.d.ts +1 -1
  323. package/dist/store/schema/cleo-global/skills.d.ts +2 -2
  324. package/dist/store/schema/cleo-project/audit.d.ts +4 -4
  325. package/dist/store/schema/cleo-project/docs.d.ts +1 -1
  326. package/dist/store/schema/cleo-project/lifecycle.d.ts +4 -4
  327. package/dist/store/schema/cleo-project/nexus-graph.d.ts +3 -3
  328. package/dist/store/schema/cleo-project/provenance-commits.d.ts +2 -2
  329. package/dist/store/schema/cleo-project/provenance-rest.d.ts +3 -3
  330. package/dist/store/schema/cleo-project/runtime.d.ts +3 -3
  331. package/dist/store/schema/cleo-project/tasks-core-batch2.d.ts +87 -1
  332. package/dist/store/schema/cleo-project/tasks-core-batch2.d.ts.map +1 -1
  333. package/dist/store/schema/cleo-project/tasks-core-batch2.js +35 -0
  334. package/dist/store/schema/cleo-project/tasks-core-batch2.js.map +1 -1
  335. package/dist/store/schema/cleo-project/tasks-core.d.ts +183 -7
  336. package/dist/store/schema/cleo-project/tasks-core.d.ts.map +1 -1
  337. package/dist/store/schema/cleo-project/tasks-core.js +52 -1
  338. package/dist/store/schema/cleo-project/tasks-core.js.map +1 -1
  339. package/dist/store/schema/cleo-shared/brain.d.ts +6 -6
  340. package/dist/store/schema/lifecycle.d.ts +4 -4
  341. package/dist/store/schema/manifest.d.ts +1 -1
  342. package/dist/store/schema/memory-schema.d.ts +5 -5
  343. package/dist/store/schema/nexus-schema.d.ts +3 -3
  344. package/dist/store/schema/provenance/releases.d.ts +1 -1
  345. package/dist/store/schema/skills-schema.d.ts +2 -2
  346. package/dist/store/schema/tasks.d.ts +6 -6
  347. package/dist/store/session-store.d.ts +63 -3
  348. package/dist/store/session-store.d.ts.map +1 -1
  349. package/dist/store/session-store.js +121 -8
  350. package/dist/store/session-store.js.map +1 -1
  351. package/dist/store/split-brain-import.d.ts +2 -14
  352. package/dist/store/split-brain-import.d.ts.map +1 -1
  353. package/dist/store/split-brain-import.js +40 -19
  354. package/dist/store/split-brain-import.js.map +1 -1
  355. package/dist/store/sqlite-data-accessor.d.ts +326 -2
  356. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  357. package/dist/store/sqlite-data-accessor.js +782 -49
  358. package/dist/store/sqlite-data-accessor.js.map +1 -1
  359. package/dist/store/sync/clock-store.d.ts +67 -0
  360. package/dist/store/sync/clock-store.d.ts.map +1 -0
  361. package/dist/store/sync/clock-store.js +117 -0
  362. package/dist/store/sync/clock-store.js.map +1 -0
  363. package/dist/store/sync/flags.d.ts +51 -0
  364. package/dist/store/sync/flags.d.ts.map +1 -0
  365. package/dist/store/sync/flags.js +81 -0
  366. package/dist/store/sync/flags.js.map +1 -0
  367. package/dist/store/sync/hlc.d.ts +115 -0
  368. package/dist/store/sync/hlc.d.ts.map +1 -0
  369. package/dist/store/sync/hlc.js +172 -0
  370. package/dist/store/sync/hlc.js.map +1 -0
  371. package/dist/store/sync/index.d.ts +20 -0
  372. package/dist/store/sync/index.d.ts.map +1 -0
  373. package/dist/store/sync/index.js +20 -0
  374. package/dist/store/sync/index.js.map +1 -0
  375. package/dist/store/sync/replica-registry.d.ts +85 -0
  376. package/dist/store/sync/replica-registry.d.ts.map +1 -0
  377. package/dist/store/sync/replica-registry.js +147 -0
  378. package/dist/store/sync/replica-registry.js.map +1 -0
  379. package/dist/store/sync/replica.d.ts +181 -0
  380. package/dist/store/sync/replica.d.ts.map +1 -0
  381. package/dist/store/sync/replica.js +305 -0
  382. package/dist/store/sync/replica.js.map +1 -0
  383. package/dist/store/sync/schema.d.ts +68 -0
  384. package/dist/store/sync/schema.d.ts.map +1 -0
  385. package/dist/store/sync/schema.js +130 -0
  386. package/dist/store/sync/schema.js.map +1 -0
  387. package/dist/store/sync/skew-hold.d.ts +71 -0
  388. package/dist/store/sync/skew-hold.d.ts.map +1 -0
  389. package/dist/store/sync/skew-hold.js +89 -0
  390. package/dist/store/sync/skew-hold.js.map +1 -0
  391. package/dist/store/table-classification.d.ts +10 -8
  392. package/dist/store/table-classification.d.ts.map +1 -1
  393. package/dist/store/table-classification.js +93 -25
  394. package/dist/store/table-classification.js.map +1 -1
  395. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  396. package/dist/store/tasks-sqlite.js +23 -3
  397. package/dist/store/tasks-sqlite.js.map +1 -1
  398. package/dist/store/twin-collapse.d.ts +113 -2
  399. package/dist/store/twin-collapse.d.ts.map +1 -1
  400. package/dist/store/twin-collapse.js +247 -19
  401. package/dist/store/twin-collapse.js.map +1 -1
  402. package/dist/store/umbrella-data-accessor.d.ts +5 -2
  403. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  404. package/dist/store/umbrella-data-accessor.js +9 -2
  405. package/dist/store/umbrella-data-accessor.js.map +1 -1
  406. package/dist/store/validation-schemas.d.ts +241 -49
  407. package/dist/store/validation-schemas.d.ts.map +1 -1
  408. package/dist/system/health.d.ts +2 -0
  409. package/dist/system/health.d.ts.map +1 -1
  410. package/dist/system/health.js +57 -0
  411. package/dist/system/health.js.map +1 -1
  412. package/dist/system/project-health.d.ts +9 -2
  413. package/dist/system/project-health.d.ts.map +1 -1
  414. package/dist/system/project-health.js +8 -3
  415. package/dist/system/project-health.js.map +1 -1
  416. package/dist/task-work/index.d.ts.map +1 -1
  417. package/dist/task-work/index.js +9 -6
  418. package/dist/task-work/index.js.map +1 -1
  419. package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
  420. package/dist/tasks/ac-coverage-gate.js +3 -0
  421. package/dist/tasks/ac-coverage-gate.js.map +1 -1
  422. package/dist/tasks/ac-table.d.ts +35 -7
  423. package/dist/tasks/ac-table.d.ts.map +1 -1
  424. package/dist/tasks/ac-table.js +119 -14
  425. package/dist/tasks/ac-table.js.map +1 -1
  426. package/dist/tasks/add.d.ts.map +1 -1
  427. package/dist/tasks/add.js +48 -2
  428. package/dist/tasks/add.js.map +1 -1
  429. package/dist/tasks/analyze.d.ts.map +1 -1
  430. package/dist/tasks/analyze.js +3 -2
  431. package/dist/tasks/analyze.js.map +1 -1
  432. package/dist/tasks/backfill-child-projections.js +47 -6
  433. package/dist/tasks/backfill-child-projections.js.map +1 -1
  434. package/dist/tasks/complete.d.ts.map +1 -1
  435. package/dist/tasks/complete.js +6 -1
  436. package/dist/tasks/complete.js.map +1 -1
  437. package/dist/tasks/completion-evaluation.d.ts.map +1 -1
  438. package/dist/tasks/completion-evaluation.js +3 -0
  439. package/dist/tasks/completion-evaluation.js.map +1 -1
  440. package/dist/tasks/evidence.d.ts +19 -1
  441. package/dist/tasks/evidence.d.ts.map +1 -1
  442. package/dist/tasks/evidence.js +93 -21
  443. package/dist/tasks/evidence.js.map +1 -1
  444. package/dist/tasks/infer-add-params.js +1 -1
  445. package/dist/tasks/infer-add-params.js.map +1 -1
  446. package/dist/tasks/task-context.d.ts.map +1 -1
  447. package/dist/tasks/task-context.js +3 -1
  448. package/dist/tasks/task-context.js.map +1 -1
  449. package/dist/tasks/task-import.d.ts.map +1 -1
  450. package/dist/tasks/task-import.js +11 -3
  451. package/dist/tasks/task-import.js.map +1 -1
  452. package/dist/tasks/task-next.d.ts +5 -1
  453. package/dist/tasks/task-next.d.ts.map +1 -1
  454. package/dist/tasks/task-next.js +7 -2
  455. package/dist/tasks/task-next.js.map +1 -1
  456. package/dist/validation/compliance.d.ts +5 -1
  457. package/dist/validation/compliance.d.ts.map +1 -1
  458. package/dist/validation/compliance.js +24 -3
  459. package/dist/validation/compliance.js.map +1 -1
  460. package/dist/validation/engine-ops.d.ts.map +1 -1
  461. package/dist/validation/engine-ops.js +3 -2
  462. package/dist/validation/engine-ops.js.map +1 -1
  463. package/dist/validation/index.d.ts +1 -1
  464. package/dist/validation/index.d.ts.map +1 -1
  465. package/dist/validation/index.js +1 -1
  466. package/dist/validation/index.js.map +1 -1
  467. package/dist/validation/protocol-common.d.ts +47 -2
  468. package/dist/validation/protocol-common.d.ts.map +1 -1
  469. package/dist/validation/protocol-common.js +86 -21
  470. package/dist/validation/protocol-common.js.map +1 -1
  471. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  472. package/dist/workgraph/scaffold-apply.js +14 -5
  473. package/dist/workgraph/scaffold-apply.js.map +1 -1
  474. package/migrations/README.md +12 -0
  475. package/migrations/drizzle-cleo-project/20260928120000_t12341-row-uids/migration.sql +169 -0
  476. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/migration.sql +76 -0
  477. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/revert.sql +7 -0
  478. package/migrations/sync-journal/20260929140000_t12342-sync-clock/migration.sql +42 -0
  479. package/package.json +13 -13
  480. package/templates/CLEO-INJECTION.md +2 -2
  481. package/templates/CLEO-REFERENCE.md +5 -1
@@ -0,0 +1,809 @@
1
+ /**
2
+ * Cleo Nexus device store: `<cleoHome>/nexus-device.json`.
3
+ *
4
+ * One file per CLEO home holds every Nexus device identity of that home, keyed
5
+ * by API origin and then by user id (cleo-nexus device contract §2.2, §2.4,
6
+ * §2.5, §3.1, §3.5). Each entry carries:
7
+ *
8
+ * - `deviceId`, the client-minted UUIDv7 (§3.1);
9
+ * - `keys`, the device's X25519 and Ed25519 key pairs, local-only (§2.4);
10
+ * - `current`, the device credential in use (`cnx_d1_…`, §2.2);
11
+ * - `pending`, a client-minted rotation credential not yet confirmed (§2.5);
12
+ * - `pendingSignOut` and `pendingRevoke`, credentials kept only to retry an
13
+ * unconfirmed sign-out (E9) or revoke (E10) (§3.5, M3);
14
+ * - `retired`, the unsettled sign-out or revoke of a device this entry
15
+ * replaced, so a live credential is never dropped on a device change.
16
+ *
17
+ * The file is separate from `nexus-credentials.json`, which stays format
18
+ * version 1 so a downgraded CLI can read it and never touches this file
19
+ * (finding 8).
20
+ *
21
+ * Safety rules, all enforced here:
22
+ *
23
+ * - **Sealed at rest.** Every token and private key is encrypted with
24
+ * AES-256-GCM under the machine key (`crypto/credentials.ts`,
25
+ * KDF id `nexus-device:` + `JSON.stringify([origin, userId])`, which no
26
+ * choice of origin and user id can make collide). Each value carries its
27
+ * field path (for example `current.token`) as GCM associated data, so
28
+ * values cannot be swapped between fields either. A copy of the file on
29
+ * another machine, or under another (origin, user), cannot be opened. The
30
+ * file is also excluded from every backup bundle. Read paths (`get`,
31
+ * `list`) never create the machine key or salt: without them an entry
32
+ * reads as unreadable. A lost key never blocks re-login: the login discards
33
+ * the unreadable entry ({@link NexusDeviceTransaction.discardUnreadable}).
34
+ * - **Owner-only.** The file is created 0600 (`O_EXCL | O_NOFOLLOW`). Every
35
+ * read opens it without following links and checks the open descriptor:
36
+ * a regular file, one link, owned by this user, no group or other bits.
37
+ * The CLEO home must be owned by this user, inside a parent no other user
38
+ * can rewrite; a home the user owns that is group- or world-writable is
39
+ * tightened with `chmod go-w` on the first write, with a warning in
40
+ * {@link NexusDeviceTransaction.warnings}. Each refusal names the fix.
41
+ * - **Locked, re-read, atomic.** Every mutation runs through
42
+ * {@link NexusDeviceStore.update}: it takes this store's own cross-process
43
+ * lock (about a minute of waiting, see {@link NexusDeviceStoreOptions}),
44
+ * re-reads the file **after** acquiring it (M6, N1), and writes a temp file
45
+ * that is fsynced and renamed over the original, then fsyncs the directory.
46
+ * On macOS, Node's fsync is `F_FULLFSYNC` (libuv), so the data reaches the
47
+ * disk, not only the drive cache. No rotated backups are made; any found are
48
+ * purged (L3), and stale temp files are swept under the lock.
49
+ * - **Never held across a network call (contract v2.9).** The lock covers
50
+ * only a local read-modify-write. Every change that involves the server
51
+ * follows one order: under the lock, re-read and persist the intent (a
52
+ * `pending` credential before E8, §2.5 step 1; the E1 intent before E1,
53
+ * §3.3 step 5); **return from `update`, releasing the lock**; make the call
54
+ * (E1, E8, E9, E10) with a plain `AbortSignal.timeout(...)`; then call
55
+ * `update` again and re-check with a compare-and-swap (is the entry still
56
+ * the one this process wrote?) before promoting or clearing. `update`
57
+ * writes when `fn` returns, so the intent is durable before the request.
58
+ * - **Lock loss aborts.** If the lock is taken over (a stale-lock takeover
59
+ * after the machine slept, say), {@link NexusDeviceTransaction.signal} is
60
+ * aborted, no further write happens, and `update` rejects with
61
+ * `E_NEXUS_DEVICE_LOCK_COMPROMISED`.
62
+ * - **No downgrade.** A file whose `version` is newer than this CLI knows is
63
+ * refused, never read as empty and never rewritten. A malformed file is
64
+ * refused the same way. Unknown fields of a known version, at any depth,
65
+ * are kept on rewrite.
66
+ * - **Unreadable entries are carried, not dropped.** An entry whose secrets do
67
+ * not open under this machine's key (copied in from another machine) is
68
+ * written back byte-identical and never decrypted, rewritten or deleted.
69
+ * Updates to other entries go ahead; only an operation on that entry fails
70
+ * (`E_NEXUS_DEVICE_UNSEAL_FAILED`), and {@link NexusDeviceStore.list}
71
+ * reports it as an {@link UnreadableNexusDevice}.
72
+ * - **Fenced writes.** Before each rename the file is compared (inode, size,
73
+ * mtime in ns) with what this transaction last read or wrote; a change
74
+ * made by anyone else aborts with `E_NEXUS_DEVICE_LOCK_COMPROMISED`.
75
+ * - **Sealed handles.** Nothing handed out prints a secret: entries from
76
+ * {@link NexusDeviceTransaction.get}, {@link SealedNexusDevice.unseal} and
77
+ * the `apply*` transitions carry non-enumerable `toJSON` and `util.inspect`
78
+ * guards, and {@link NexusDeviceEnrolment} holds its secrets privately.
79
+ * {@link redactNexusDeviceSecrets} masks credentials and private keys in any
80
+ * diagnostic text. The guards are non-enumerable, so spreading a guarded
81
+ * entry (`{ ...entry }`) produces an unguarded copy: T12868–T12871 must
82
+ * never spread an entry, or any part of one, into a log line or envelope.
83
+ *
84
+ * Behaviour that uses it ships behind `CLEO_NEXUS_DEVICE=1`
85
+ * ({@link isNexusDeviceEnabled}): login enrolment and the 9.24 upgrade
86
+ * (`nexus-enrol.ts`, T12868), then rotation, logout and the cloud reads
87
+ * (T12869–T12871).
88
+ *
89
+ * @task T12867
90
+ * @epic T12323
91
+ */
92
+ import { inspect } from 'node:util';
93
+ import { z } from 'zod';
94
+ /** File name of the store, directly under the CLEO home. */
95
+ export declare const NEXUS_DEVICE_FILE = "nexus-device.json";
96
+ /** The file format version this CLI reads and writes. A newer version is refused. */
97
+ export declare const NEXUS_DEVICE_FILE_VERSION = 1;
98
+ /** Environment switch that turns on the device-credential behaviour (T12867–T12871). */
99
+ export declare const NEXUS_DEVICE_ENV = "CLEO_NEXUS_DEVICE";
100
+ /**
101
+ * The device credential format (contract §2.2, `DEVICE_CREDENTIAL` in §4.1):
102
+ * `cnx_d1_` followed by 43 base64url characters (32 random bytes).
103
+ */
104
+ export declare const NEXUS_DEVICE_CREDENTIAL_PATTERN: RegExp;
105
+ /** Every scope a device credential can carry (contract §2.3, `DeviceScope`). */
106
+ export declare const NEXUS_DEVICE_SCOPES: readonly ["account:read", "devices:read", "projects:read", "projects:write", "sync:read", "sync:write", "keys:read", "keys:write"];
107
+ /** Credential profiles (contract §2.3, `DeviceProfile`). */
108
+ export declare const NEXUS_DEVICE_PROFILES: readonly ["device", "read-only"];
109
+ /** Why an E1 call is in flight: an interactive login (§3.3) or the 9.24 auto-upgrade (§3.4). */
110
+ export declare const NEXUS_DEVICE_INTENT_KINDS: readonly ["login", "upgrade"];
111
+ /**
112
+ * Most credentials one sign-out or revoke slot may hold. A transition that
113
+ * would need more refuses (`E_NEXUS_DEVICE_SLOT_FULL`) instead of dropping a
114
+ * live credential.
115
+ */
116
+ export declare const NEXUS_DEVICE_MAX_SLOT_CREDENTIALS = 8;
117
+ /** Most retired-device items one entry may hold; beyond it a device change refuses. */
118
+ export declare const NEXUS_DEVICE_MAX_RETIRED = 8;
119
+ /** Default time {@link NexusDeviceStore.update} waits for the lock. */
120
+ export declare const NEXUS_DEVICE_LOCK_WAIT_MS = 60000;
121
+ /** Default age after which a lock whose holder stopped refreshing it may be taken over. */
122
+ export declare const NEXUS_DEVICE_LOCK_STALE_MS = 30000;
123
+ declare const entrySchema: z.ZodObject<{
124
+ deviceId: z.ZodString;
125
+ createdAt: z.ZodISODateTime;
126
+ keys: z.ZodNullable<z.ZodObject<{
127
+ encryption: z.ZodObject<{
128
+ publicKey: z.ZodString;
129
+ privateKey: z.ZodString;
130
+ }, z.core.$loose>;
131
+ signing: z.ZodObject<{
132
+ publicKey: z.ZodString;
133
+ privateKey: z.ZodString;
134
+ }, z.core.$loose>;
135
+ }, z.core.$loose>>;
136
+ current: z.ZodNullable<z.ZodObject<{
137
+ credentialId: z.ZodString;
138
+ token: z.ZodString;
139
+ profile: z.ZodEnum<{
140
+ device: "device";
141
+ "read-only": "read-only";
142
+ }>;
143
+ scopes: z.ZodArray<z.ZodEnum<{
144
+ "account:read": "account:read";
145
+ "devices:read": "devices:read";
146
+ "projects:read": "projects:read";
147
+ "projects:write": "projects:write";
148
+ "sync:read": "sync:read";
149
+ "sync:write": "sync:write";
150
+ "keys:read": "keys:read";
151
+ "keys:write": "keys:write";
152
+ }>>;
153
+ createdAt: z.ZodISODateTime;
154
+ }, z.core.$loose>>;
155
+ pending: z.ZodNullable<z.ZodObject<{
156
+ /** Server id of the new credential; `null` until E8 answers (the client minted the token). */
157
+ credentialId: z.ZodNullable<z.ZodString>;
158
+ token: z.ZodString;
159
+ createdAt: z.ZodISODateTime;
160
+ }, z.core.$loose>>;
161
+ pendingSignOut: z.ZodNullable<z.ZodObject<{
162
+ credentials: z.ZodArray<z.ZodObject<{
163
+ credentialId: z.ZodNullable<z.ZodString>;
164
+ token: z.ZodString;
165
+ }, z.core.$loose>>;
166
+ requestedAt: z.ZodISODateTime;
167
+ }, z.core.$loose>>;
168
+ pendingRevoke: z.ZodNullable<z.ZodObject<{
169
+ credentials: z.ZodArray<z.ZodObject<{
170
+ credentialId: z.ZodNullable<z.ZodString>;
171
+ token: z.ZodString;
172
+ }, z.core.$loose>>;
173
+ requestedAt: z.ZodISODateTime;
174
+ }, z.core.$loose>>;
175
+ retired: z.ZodOptional<z.ZodArray<z.ZodObject<{
176
+ deviceId: z.ZodString;
177
+ kind: z.ZodEnum<{
178
+ "sign-out": "sign-out";
179
+ revoke: "revoke";
180
+ }>;
181
+ credentials: z.ZodArray<z.ZodObject<{
182
+ credentialId: z.ZodNullable<z.ZodString>;
183
+ token: z.ZodString;
184
+ }, z.core.$loose>>;
185
+ requestedAt: z.ZodISODateTime;
186
+ }, z.core.$loose>>>;
187
+ /**
188
+ * An E1 call in flight (contract §3.3 step 5, §3.4 step 1): persisted under
189
+ * the lock before E1 is sent, cleared at step 7. Holds no secret.
190
+ */
191
+ /**
192
+ * A credential E1 issued to this process while the file already held
193
+ * another one for the same device, and the server could not yet say which
194
+ * is live (§3.3 step 7, ruling A). Kept, sealed, until E2 settles it, so
195
+ * the only live credential is never lost.
196
+ */
197
+ raceCandidate: z.ZodOptional<z.ZodNullable<z.ZodObject<{
198
+ credentialId: z.ZodString;
199
+ token: z.ZodString;
200
+ profile: z.ZodEnum<{
201
+ device: "device";
202
+ "read-only": "read-only";
203
+ }>;
204
+ scopes: z.ZodArray<z.ZodEnum<{
205
+ "account:read": "account:read";
206
+ "devices:read": "devices:read";
207
+ "projects:read": "projects:read";
208
+ "projects:write": "projects:write";
209
+ "sync:read": "sync:read";
210
+ "sync:write": "sync:write";
211
+ "keys:read": "keys:read";
212
+ "keys:write": "keys:write";
213
+ }>>;
214
+ createdAt: z.ZodISODateTime;
215
+ }, z.core.$loose>>>;
216
+ enrolIntent: z.ZodOptional<z.ZodNullable<z.ZodObject<{
217
+ deviceId: z.ZodString;
218
+ kind: z.ZodEnum<{
219
+ upgrade: "upgrade";
220
+ login: "login";
221
+ }>;
222
+ /** Random id of the process that wrote it (a CAS token). */
223
+ owner: z.ZodString;
224
+ startedAt: z.ZodISODateTime;
225
+ }, z.core.$loose>>>;
226
+ }, z.core.$loose>;
227
+ /** One (origin, user) entry of the device file, secrets in plaintext (in memory only). */
228
+ export type NexusDeviceEntry = z.infer<typeof entrySchema>;
229
+ /** The device's two key pairs (contract §2.4). */
230
+ export type NexusDeviceKeys = NonNullable<NexusDeviceEntry['keys']>;
231
+ /** A stored key pair, base64 (32 raw bytes each). */
232
+ export type NexusDeviceKeyPair = NexusDeviceKeys['encryption'];
233
+ /** The credential in use, as issued by E1 or promoted from `pending`. */
234
+ export type NexusDeviceCurrentCredential = NonNullable<NexusDeviceEntry['current']>;
235
+ /** A client-minted rotation credential awaiting confirmation (contract §2.5). */
236
+ export type NexusDevicePendingCredential = NonNullable<NexusDeviceEntry['pending']>;
237
+ /** A `pendingSignOut` or `pendingRevoke` slot (contract §3.5). */
238
+ export type NexusDevicePendingEnd = NonNullable<NexusDeviceEntry['pendingSignOut']>;
239
+ /** One credential kept in a sign-out or revoke slot. */
240
+ export type NexusDeviceSlotCredential = NexusDevicePendingEnd['credentials'][number];
241
+ /** An unsettled sign-out or revoke of a device an entry replaced. */
242
+ export type NexusDeviceRetired = NonNullable<NexusDeviceEntry['retired']>[number];
243
+ /** An E1 call in flight for an entry (§3.3 step 5, §3.4 step 1). */
244
+ export type NexusDeviceEnrolIntent = NonNullable<NexusDeviceEntry['enrolIntent']>;
245
+ /** A racing login's unsettled credential (§3.3 step 7). */
246
+ export type NexusDeviceRaceCandidate = NonNullable<NexusDeviceEntry['raceCandidate']>;
247
+ /** A device credential profile. */
248
+ export type NexusDeviceProfile = (typeof NEXUS_DEVICE_PROFILES)[number];
249
+ /** A device credential scope. */
250
+ export type NexusDeviceScope = (typeof NEXUS_DEVICE_SCOPES)[number];
251
+ /** Codes carried by {@link NexusDeviceStoreError}. */
252
+ export type NexusDeviceStoreErrorCode = 'E_NEXUS_DEVICE_FILE_PERMISSIONS' | 'E_NEXUS_DEVICE_FILE_UNSAFE' | 'E_NEXUS_DEVICE_FILE_SYMLINK' | 'E_NEXUS_DEVICE_DIR_UNSAFE' | 'E_NEXUS_DEVICE_FILE_NEWER' | 'E_NEXUS_DEVICE_FILE_INVALID' | 'E_NEXUS_DEVICE_UNSEAL_FAILED' | 'E_NEXUS_DEVICE_MACHINE_KEY' | 'E_NEXUS_DEVICE_IO' | 'E_NEXUS_DEVICE_ENTRY_INVALID' | 'E_NEXUS_DEVICE_PENDING_EXISTS' | 'E_NEXUS_DEVICE_REVOKE_PENDING' | 'E_NEXUS_DEVICE_SLOT_FULL' | 'E_NEXUS_DEVICE_BUSY' | 'E_NEXUS_DEVICE_REENTRANT' | 'E_NEXUS_DEVICE_LOCK_COMPROMISED';
253
+ /** Thrown when the device file cannot be read or written safely. Never carries a secret. */
254
+ export declare class NexusDeviceStoreError extends Error {
255
+ /** Machine-readable reason. */
256
+ readonly code: NexusDeviceStoreErrorCode;
257
+ /**
258
+ * @param code - Machine-readable reason.
259
+ * @param message - Human-readable message; passed through {@link redactNexusDeviceSecrets}.
260
+ */
261
+ constructor(code: NexusDeviceStoreErrorCode, message: string);
262
+ }
263
+ /**
264
+ * Whether the device-credential behaviour is switched on (`CLEO_NEXUS_DEVICE=1`).
265
+ *
266
+ * @param env - Environment to read; defaults to `process.env`.
267
+ * @returns `true` only for the exact value `1`.
268
+ */
269
+ export declare function isNexusDeviceEnabled(env?: NodeJS.ProcessEnv): boolean;
270
+ /**
271
+ * Absolute path of the device store.
272
+ *
273
+ * @returns `<cleoHome>/nexus-device.json`, through `@cleocode/paths` (arch gate 2).
274
+ */
275
+ export declare function nexusDevicePath(): string;
276
+ /**
277
+ * Mask a device credential down to a safe preview.
278
+ *
279
+ * The preview keeps the last 4 characters (24 of the token's 256 random
280
+ * bits), enough to tell two credentials apart in a diagnostic and far too
281
+ * little to guess the rest. The same rule as `maskNexusToken`.
282
+ *
283
+ * @param value - Plaintext credential (read transiently, not retained).
284
+ * @returns `cnx_d1_…` plus the last 4 characters, or `…` for anything shorter.
285
+ */
286
+ export declare function maskNexusDeviceCredential(value: string): string;
287
+ /**
288
+ * Mask every device credential and every private-key field in `text`. Use on
289
+ * any diagnostic string (errors, logs, envelopes) that might hold one.
290
+ *
291
+ * @param text - Arbitrary text.
292
+ * @returns The text with every `cnx_d1_…` credential masked and every
293
+ * `privateKey` value replaced by `[redacted]`.
294
+ */
295
+ export declare function redactNexusDeviceSecrets(text: string): string;
296
+ /**
297
+ * Attach the store's non-enumerable `toJSON` and `util.inspect` guards to any
298
+ * value that holds device secrets (tokens, private keys), at every depth, so
299
+ * printing or serialising it shows only the redacted view. Never spread the
300
+ * result: a spread drops the guards.
301
+ *
302
+ * @param value - An object holding `token` or `privateKey` fields.
303
+ * @returns The same object, guarded.
304
+ */
305
+ export declare function guardNexusDeviceSecrets<T>(value: T): T;
306
+ /**
307
+ * Secret-free diagnostic view of one entry. Public keys are shown; private
308
+ * keys and tokens never are.
309
+ *
310
+ * @param entry - A stored entry.
311
+ * @returns A view safe to print, log or put in an envelope.
312
+ */
313
+ export declare function redactNexusDeviceEntry(entry: NexusDeviceEntry): Record<string, unknown>;
314
+ /**
315
+ * Opaque read handle to one entry. Tokens and private keys are held in a
316
+ * private field; serialising or inspecting the handle yields
317
+ * {@link redactNexusDeviceEntry} only. The accessors are for the code that
318
+ * builds an `Authorization` header or signs with the device key.
319
+ */
320
+ export declare class SealedNexusDevice {
321
+ #private;
322
+ /** Always `true`: this entry opened under this machine's key. */
323
+ readonly readable = true;
324
+ /** API origin. */
325
+ readonly origin: string;
326
+ /** Nexus user id. */
327
+ readonly userId: string;
328
+ /** Nexus device id (UUIDv7). */
329
+ readonly deviceId: string;
330
+ /**
331
+ * @param origin - API origin.
332
+ * @param userId - Nexus user id.
333
+ * @param entry - Stored entry; captured privately as a copy.
334
+ */
335
+ constructor(origin: string, userId: string, entry: NexusDeviceEntry);
336
+ /** The current credential's token, or `null`. Call only where the request is built. */
337
+ currentBearer(): string | null;
338
+ /** The pending rotation credential's token, or `null` (§2.5: try it before `current`). */
339
+ pendingBearer(): string | null;
340
+ /**
341
+ * A guarded copy of the whole entry, secrets included, for the flows that
342
+ * must act on them. Printing or serialising it shows the redacted view.
343
+ */
344
+ unseal(): NexusDeviceEntry;
345
+ /** Secret-free JSON form. */
346
+ toJSON(): Record<string, unknown>;
347
+ /** Secret-free string form. */
348
+ toString(): string;
349
+ /** Secret-free `util.inspect` form. */
350
+ [inspect.custom](): string;
351
+ }
352
+ /**
353
+ * What E1 returned, as it is about to be stored (contract §3.3 step 7). The
354
+ * issued credential and any device keys are held in private fields, so the
355
+ * value never prints a secret.
356
+ */
357
+ export declare class NexusDeviceEnrolment {
358
+ #private;
359
+ /** The device id that was enrolled. */
360
+ readonly deviceId: string;
361
+ /**
362
+ * @param input - The enrolled device id, the keys when this enrolment
363
+ * created or confirmed them (else `null`), and the credential E1 issued.
364
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_ENTRY_INVALID` on a malformed credential or keys.
365
+ */
366
+ constructor(input: {
367
+ readonly deviceId: string;
368
+ readonly keys: NexusDeviceKeys | null;
369
+ readonly credential: NexusDeviceCurrentCredential;
370
+ });
371
+ /** A guarded copy of the keys, or `null`. */
372
+ keys(): NexusDeviceKeys | null;
373
+ /** A guarded copy of the issued credential. */
374
+ credential(): NexusDeviceCurrentCredential;
375
+ /** Secret-free JSON form. */
376
+ toJSON(): Record<string, unknown>;
377
+ /** Secret-free string form. */
378
+ toString(): string;
379
+ /** Secret-free `util.inspect` form. */
380
+ [inspect.custom](): string;
381
+ }
382
+ /**
383
+ * Throw unless a login may enrol `deviceId` over `entry`. While a revoke of
384
+ * the same device is unconfirmed, enrolling would re-activate a device the
385
+ * user asked to revoke: retry E10 first (`cleo logout nexus --revoke`), and
386
+ * if it cannot be confirmed, revoke the device on cleocode.dev. Call this
387
+ * before E1 is sent; {@link applyEnrolment} checks it again.
388
+ *
389
+ * @param entry - The entry read under the lock, or `null`.
390
+ * @param deviceId - The device id the login would enrol.
391
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_REVOKE_PENDING`.
392
+ */
393
+ export declare function assertEnrolmentAllowed(entry: NexusDeviceEntry | null, deviceId: string): void;
394
+ /**
395
+ * Record a successful E1 (M6).
396
+ *
397
+ * - **Same device:** store the new credential and clear `pending` and
398
+ * `pendingSignOut`, because E1 revoked every older credential of the device
399
+ * on the server. Refused while `pendingRevoke` is set
400
+ * ({@link assertEnrolmentAllowed}).
401
+ * - **New device** (a fresh identity replaced the entry's device): the old
402
+ * device's unsettled revoke, and any credential it still holds, move to
403
+ * `retired`, so they are retried rather than dropped.
404
+ *
405
+ * @param entry - The entry read under the lock, or `null` for a new identity.
406
+ * @param enrolment - E1's result.
407
+ * @param now - Clock, for `createdAt` of a new entry and retired items.
408
+ * @returns The updated entry, guarded.
409
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_REVOKE_PENDING`, or
410
+ * `E_NEXUS_DEVICE_SLOT_FULL` when `retired` is full.
411
+ */
412
+ export declare function applyEnrolment(entry: NexusDeviceEntry | null, enrolment: NexusDeviceEnrolment, now?: Date): NexusDeviceEntry;
413
+ /**
414
+ * Persist the identity a login or upgrade is about to enrol, with its E1
415
+ * intent (§3.3 step 5, §3.4 step 1), before E1 is sent.
416
+ *
417
+ * - **No entry:** a new identity with `deviceId` and `keys`.
418
+ * - **Same device:** the intent is recorded; `keys` fill in only when the
419
+ * entry has none (lost keys, §3.3 step 6 `device-keys-changed`). Every
420
+ * credential and slot is kept: E1 has not run yet.
421
+ * - **Another device** (a fresh identity after 409 `device-revoked`,
422
+ * `device-other-account`, `device-id-taken` or `device-keys-changed`): the
423
+ * old device's credentials and unsettled slots move to `retired`, as in
424
+ * {@link applyEnrolment}, so nothing live is dropped.
425
+ *
426
+ * @param entry - The entry read under the lock, or `null`.
427
+ * @param identity - The device id and keys to enrol, and the intent.
428
+ * @param now - Clock.
429
+ * @returns The updated entry, guarded.
430
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_REVOKE_PENDING` for the same
431
+ * device while a revoke is unconfirmed, or `E_NEXUS_DEVICE_SLOT_FULL`.
432
+ */
433
+ export declare function applyEnrolIntent(entry: NexusDeviceEntry | null, identity: {
434
+ readonly deviceId: string;
435
+ readonly keys: NexusDeviceKeys;
436
+ readonly intent: NexusDeviceEnrolIntent;
437
+ }, now?: Date): NexusDeviceEntry;
438
+ /**
439
+ * Clear an E1 intent, as a compare-and-swap on its owner: another process's
440
+ * intent is never cleared.
441
+ *
442
+ * @param entry - The entry read under the lock.
443
+ * @param owner - The intent owner this process wrote.
444
+ * @returns The updated entry (guarded), or the entry unchanged.
445
+ */
446
+ export declare function applyClearEnrolIntent(entry: NexusDeviceEntry, owner: string): NexusDeviceEntry;
447
+ /**
448
+ * Drop `current`, as a compare-and-swap on its credential id: used when the
449
+ * server refused it and no other credential is live (§3.3 step 7, both
450
+ * racing credentials answered 401). Nothing is moved to a sign-out slot: a
451
+ * refused credential needs no E9.
452
+ *
453
+ * @param entry - The entry read under the lock.
454
+ * @param credentialId - The credential id the caller saw refused.
455
+ * @returns The updated entry (guarded), or the entry unchanged when `current` moved on.
456
+ */
457
+ export declare function applyDropCurrent(entry: NexusDeviceEntry, credentialId: string): NexusDeviceEntry;
458
+ /**
459
+ * Refresh this process's E1 intent right before E1 is sent (compare-and-swap
460
+ * on the owner), so a live intent never looks stale while its owner is still
461
+ * working (§3.4 step 1).
462
+ *
463
+ * @param entry - The entry read under the lock.
464
+ * @param owner - The intent owner this process wrote.
465
+ * @param now - Clock.
466
+ * @returns The updated entry (guarded), or `null` when the intent is no
467
+ * longer this process's (another process replaced or took it over).
468
+ */
469
+ export declare function applyRefreshEnrolIntent(entry: NexusDeviceEntry, owner: string, now?: Date): NexusDeviceEntry | null;
470
+ /**
471
+ * Park a racing login's credential beside `current` until E2 settles which is
472
+ * live (§3.3 step 7). An existing candidate is replaced only by a newer one;
473
+ * the older is dropped, because E1 re-enrolment revoked it.
474
+ *
475
+ * @param entry - The entry read under the lock.
476
+ * @param candidate - The credential E1 issued to this process.
477
+ * @returns The updated entry, guarded.
478
+ */
479
+ export declare function applySetRaceCandidate(entry: NexusDeviceEntry, candidate: NexusDeviceRaceCandidate): NexusDeviceEntry;
480
+ /**
481
+ * Drop the race candidate, as a compare-and-swap on its credential id (it
482
+ * lost: the server refused it, or `current` is newer).
483
+ *
484
+ * @param entry - The entry read under the lock.
485
+ * @param credentialId - The candidate the caller settled.
486
+ * @returns The updated entry (guarded), or the entry unchanged.
487
+ */
488
+ export declare function applyDropRaceCandidate(entry: NexusDeviceEntry, credentialId: string): NexusDeviceEntry;
489
+ /**
490
+ * Store a freshly minted rotation credential as `pending` (§2.5 step 1). A
491
+ * second pending credential is never minted: when one exists the caller must
492
+ * replay it instead. The caller MUST let `update` write it (return from the
493
+ * transaction, releasing the lock) before sending E8, never send E8 inside it.
494
+ *
495
+ * @param entry - The entry read under the lock.
496
+ * @param newToken - The client-minted `cnx_d1_…` token.
497
+ * @param now - Clock.
498
+ * @returns The updated entry, guarded.
499
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_PENDING_EXISTS` when `pending` is already set.
500
+ */
501
+ export declare function applyPendingRotation(entry: NexusDeviceEntry, newToken: string, now?: Date): NexusDeviceEntry;
502
+ /**
503
+ * Promote `pending` to `current` (§2.5 step 5, or its first successful use),
504
+ * as a compare-and-swap: only when `pending` still holds `expectedToken`.
505
+ * The profile and scopes carry over: E8 issues the new credential with the
506
+ * same profile and scopes as the one it replaced.
507
+ *
508
+ * @param entry - The entry read under the lock.
509
+ * @param expectedToken - The pending token the caller saw succeed.
510
+ * @param credentialId - The server id of the promoted credential.
511
+ * @returns The updated entry (guarded), or the entry unchanged when `pending`
512
+ * moved on or there is nothing to promote.
513
+ */
514
+ export declare function applyPromotePending(entry: NexusDeviceEntry, expectedToken: string, credentialId: string): NexusDeviceEntry;
515
+ /**
516
+ * CAS-drop `pending`: only when it still holds `expectedToken` (§2.5).
517
+ *
518
+ * @param entry - The entry read under the lock.
519
+ * @param expectedToken - The pending token the caller saw fail.
520
+ * @returns The updated entry (guarded), or the entry unchanged when `pending` moved on.
521
+ */
522
+ export declare function applyDropPending(entry: NexusDeviceEntry, expectedToken: string): NexusDeviceEntry;
523
+ /**
524
+ * Begin `cleo logout nexus` (§3.5, M3): move the live credentials into
525
+ * `pendingSignOut`, newest first (`pending`, then `current`, then any
526
+ * credential an earlier unsettled sign-out holds), so they are used for
527
+ * nothing but the E9 retry. The device keys stay.
528
+ *
529
+ * @param entry - The entry read under the lock.
530
+ * @param now - Clock.
531
+ * @returns The updated entry (guarded), unchanged when it holds no credential at all.
532
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_SLOT_FULL` rather than drop a credential.
533
+ */
534
+ export declare function applyBeginSignOut(entry: NexusDeviceEntry, now?: Date): NexusDeviceEntry;
535
+ /**
536
+ * Begin `cleo logout nexus --revoke` (§3.5, M3): move every credential the
537
+ * entry holds into `pendingRevoke`, newest first: `pending`, `current`, an
538
+ * unsettled sign-out's, then an earlier unsettled revoke's. The entry, and its
539
+ * keys, stay until E10 is confirmed.
540
+ *
541
+ * @param entry - The entry read under the lock.
542
+ * @param now - Clock.
543
+ * @returns The updated entry (guarded), unchanged when it holds no credential at all.
544
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_SLOT_FULL` rather than drop a credential.
545
+ */
546
+ export declare function applyBeginRevoke(entry: NexusDeviceEntry, now?: Date): NexusDeviceEntry;
547
+ /**
548
+ * Settle a confirmed sign-out (E9 answered 200, or 401 `device-signed-out` or
549
+ * `device-revoked`): clear `pendingSignOut`.
550
+ *
551
+ * @param entry - The entry read under the lock.
552
+ * @returns The updated entry, guarded.
553
+ */
554
+ export declare function applySignOutConfirmed(entry: NexusDeviceEntry): NexusDeviceEntry;
555
+ /**
556
+ * Settle a confirmed sign-out or revoke of a retired device.
557
+ *
558
+ * @param entry - The entry read under the lock.
559
+ * @param deviceId - The retired device the server confirmed.
560
+ * @param kind - Which request was confirmed.
561
+ * @returns The updated entry, guarded.
562
+ */
563
+ export declare function applyRetiredSettled(entry: NexusDeviceEntry, deviceId: string, kind: NexusDeviceRetired['kind']): NexusDeviceEntry;
564
+ /** Read and write access to the file, valid only inside {@link NexusDeviceStore.update}. */
565
+ export interface NexusDeviceTransaction {
566
+ /**
567
+ * Aborted when the lock is lost (taken over as stale). No HTTP call is ever
568
+ * made inside `update` (v2.9): persist, return, call, then re-lock and CAS.
569
+ */
570
+ readonly signal: AbortSignal;
571
+ /**
572
+ * The entry for (origin of `apiUrl`, `userId`), as re-read under the lock.
573
+ *
574
+ * @returns A guarded copy; change it and pass it to {@link NexusDeviceTransaction.set}.
575
+ */
576
+ get(apiUrl: string, userId: string): NexusDeviceEntry | null;
577
+ /** Replace the entry for (origin of `apiUrl`, `userId`). Validated before it is kept. */
578
+ set(apiUrl: string, userId: string, entry: NexusDeviceEntry): void;
579
+ /**
580
+ * CAS delete (§2.5): remove the entry only if it still has `expected`'s
581
+ * device id and current credential id (`null` when it must have none).
582
+ * Returns `true` when an entry was removed.
583
+ */
584
+ delete(apiUrl: string, userId: string, expected: {
585
+ readonly deviceId: string;
586
+ readonly credentialId: string | null;
587
+ }): boolean;
588
+ /**
589
+ * Write the changes so far to disk now, atomically, with the lock still
590
+ * held. `update` also writes when `fn` returns, which is the normal path: a
591
+ * `pending` credential or an E1 intent is persisted by returning from
592
+ * `update` (releasing the lock) before E8 or E1 is sent. `flush` is for a
593
+ * long transaction that must make a change durable before it continues
594
+ * locally; never before a network call made with the lock held.
595
+ *
596
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_LOCK_COMPROMISED` once the lock is lost.
597
+ */
598
+ flush(): Promise<void>;
599
+ /**
600
+ * Every (origin, userId) pair in the file. `readable: false` marks an entry
601
+ * this machine's key cannot open: it is carried through untouched, and any
602
+ * `get`, `set` or `delete` on it fails with `E_NEXUS_DEVICE_UNSEAL_FAILED`.
603
+ */
604
+ keys(): Array<{
605
+ readonly origin: string;
606
+ readonly userId: string;
607
+ readonly readable: boolean;
608
+ }>;
609
+ /**
610
+ * Drop an entry this machine cannot open (a lost machine key, or one copied
611
+ * in from elsewhere), so a login can enrol afresh. Compare-and-swap on the
612
+ * device id, which is stored in clear. The server-side device stays active,
613
+ * so the returned warning (also added to {@link NexusDeviceTransaction.warnings})
614
+ * names the device id to revoke on cleocode.dev. A readable entry is never
615
+ * discarded this way; use `delete`.
616
+ *
617
+ * @returns Whether the entry was dropped, and the warning when it was.
618
+ */
619
+ discardUnreadable(apiUrl: string, userId: string, expected: {
620
+ readonly deviceId: string;
621
+ }): {
622
+ readonly discarded: boolean;
623
+ readonly warning: string | null;
624
+ };
625
+ /**
626
+ * One-line warnings from this transaction (for example, the CLEO home was
627
+ * tightened with `chmod go-w`). The caller puts them in its result envelope.
628
+ */
629
+ readonly warnings: readonly string[];
630
+ }
631
+ /** An entry this machine's key cannot open, as {@link NexusDeviceStore.list} reports it. No secrets. */
632
+ export declare class UnreadableNexusDevice {
633
+ /** Always `false`: the entry could not be opened here. */
634
+ readonly readable = false;
635
+ /** API origin. */
636
+ readonly origin: string;
637
+ /** Nexus user id. */
638
+ readonly userId: string;
639
+ /** The device id stored in clear, when present. */
640
+ readonly deviceId: string | null;
641
+ /** Why it could not be opened. */
642
+ readonly reason: string;
643
+ /**
644
+ * @param origin - API origin.
645
+ * @param userId - Nexus user id.
646
+ * @param deviceId - The device id stored in clear, when present.
647
+ * @param reason - Why it could not be opened.
648
+ */
649
+ constructor(origin: string, userId: string, deviceId: string | null, reason: string);
650
+ }
651
+ /** Tuning for {@link NexusDeviceStore}. The defaults suit production; tests shorten them. */
652
+ export interface NexusDeviceStoreOptions {
653
+ /** How long `update` waits for the lock before `E_NEXUS_DEVICE_BUSY`. Default one minute. */
654
+ readonly lockWaitMs?: number;
655
+ /** Lock staleness threshold (minimum 2000). Default 30 s. */
656
+ readonly lockStaleMs?: number;
657
+ /** CLEO home whose machine key seals the secrets. Default: the store file's directory. */
658
+ readonly cleoHome?: string;
659
+ }
660
+ /**
661
+ * File-backed store for Nexus device identities and credentials. See the
662
+ * module comment for the safety rules.
663
+ */
664
+ export declare class NexusDeviceStore {
665
+ #private;
666
+ /** Absolute path of the store file. */
667
+ readonly location: string;
668
+ /**
669
+ * @param path - Store path; defaults to {@link nexusDevicePath}.
670
+ * @param options - Lock and sealing options.
671
+ */
672
+ constructor(path?: string, options?: NexusDeviceStoreOptions);
673
+ /**
674
+ * A sealed snapshot of one entry, read without the lock. Use it to decide;
675
+ * any change goes through {@link NexusDeviceStore.update}, which re-reads.
676
+ *
677
+ * @param apiUrl - Any URL on the API host.
678
+ * @param userId - Nexus user id.
679
+ * @returns The sealed entry, or `null`.
680
+ * @throws {NexusDeviceStoreError} On an unsafe, newer, malformed or foreign-sealed file.
681
+ */
682
+ get(apiUrl: string, userId: string): Promise<SealedNexusDevice | null>;
683
+ /**
684
+ * Every entry, sorted by origin then user id. An entry this machine's key
685
+ * cannot open is listed as an {@link UnreadableNexusDevice} (no secrets)
686
+ * instead of failing the whole listing.
687
+ *
688
+ * @returns The entries.
689
+ * @throws {NexusDeviceStoreError} On an unsafe, newer or malformed file.
690
+ */
691
+ list(): Promise<Array<SealedNexusDevice | UnreadableNexusDevice>>;
692
+ /**
693
+ * Run `fn` under this store's cross-process lock (M6, N1).
694
+ *
695
+ * - The file is re-read **after** the lock is acquired, so `fn` always sees
696
+ * the latest state, never a snapshot taken before another process wrote.
697
+ * - `fn` may await local work (another store's lock, taken after this one),
698
+ * but MUST NOT make a network call: the lock is never held across E1,
699
+ * E8, E9 or E10 (v2.9). Persist the intent, return, make the call, then
700
+ * call `update` again and re-check with a CAS.
701
+ * - `tx.flush()` writes mid-transaction; whatever is still unflushed is
702
+ * written when `fn` returns. When `fn` throws, unflushed changes are
703
+ * dropped.
704
+ * - Waiting for the lock gives up after `lockWaitMs` with
705
+ * `E_NEXUS_DEVICE_BUSY`. Calling `update` on the same file from inside
706
+ * `fn` fails at once with `E_NEXUS_DEVICE_REENTRANT` (it would otherwise
707
+ * wait on itself).
708
+ * - If the lock is lost while `fn` runs, `tx.signal` aborts, nothing more
709
+ * is written, and `update` rejects with `E_NEXUS_DEVICE_LOCK_COMPROMISED`.
710
+ *
711
+ * @param fn - The read-modify-write step.
712
+ * @returns What `fn` returned.
713
+ * @throws {NexusDeviceStoreError} As above, or on an unsafe, newer, malformed or foreign-sealed file.
714
+ */
715
+ update<R>(fn: (tx: NexusDeviceTransaction) => R | Promise<R>): Promise<R>;
716
+ /** The body of {@link NexusDeviceStore.update}, with the lock held. */
717
+ private transact;
718
+ /**
719
+ * Load this home's machine key and salt, mapping any failure other than
720
+ * "missing" to `E_NEXUS_DEVICE_MACHINE_KEY` (review N5), so it is never
721
+ * mistaken for a file copied from another machine.
722
+ *
723
+ * @param create - Whether missing material may be created (write paths only).
724
+ * @returns The material, or `null` when it is missing and `create` is false.
725
+ */
726
+ private material;
727
+ /**
728
+ * Decrypt one sealed entry and validate the plaintext.
729
+ *
730
+ * @throws {NexusDeviceStoreError} `E_NEXUS_DEVICE_UNSEAL_FAILED` when there
731
+ * is no key material or a value fails authentication.
732
+ */
733
+ private open;
734
+ /**
735
+ * Seal the in-memory state into the on-disk form, keeping unknown top-level
736
+ * fields, and put every unopenable entry back exactly as it was read.
737
+ */
738
+ private seal;
739
+ /**
740
+ * Read and validate the file. A missing or empty file (the seed's
741
+ * placeholder) reads as an empty store. Anything else that does not parse
742
+ * as a known version is refused, never read as empty, so it is never
743
+ * overwritten.
744
+ */
745
+ private readSealed;
746
+ /**
747
+ * Open the file without following links and check the open descriptor
748
+ * (so nothing can be swapped between the check and the read): a regular
749
+ * file with one link, owned by this user, with no group or other bits.
750
+ *
751
+ * @returns The contents and the file's fencing fingerprint, or `null` when
752
+ * the file does not exist.
753
+ */
754
+ private readPrivate;
755
+ /** The remedy for a mode or owner problem, including the sudo case. */
756
+ private ownerRemedy;
757
+ /** A symlink refusal for `op`. */
758
+ private symlinkError;
759
+ /**
760
+ * Refuse a CLEO home another user could use to replace the file: one owned
761
+ * by someone else, or one inside a parent that another user owns and that
762
+ * is group- or world-writable without the sticky bit. A symlinked home is
763
+ * resolved first, and the checks apply to the real directory and its real
764
+ * parent (review N4). A home owned by the caller that is group- or
765
+ * world-writable is allowed here: reads check the open descriptor, and
766
+ * {@link NexusDeviceStore.prepareDirectory} tightens it on the first write.
767
+ * Skipped on Windows, where the profile ACL governs.
768
+ *
769
+ * @returns The real home, its stat and whether the configured path is a
770
+ * symlink, or `null` when it does not exist (or on Windows).
771
+ */
772
+ private assertSafeDirectory;
773
+ /**
774
+ * Before a write: create the CLEO home 0700 if it is missing, check it,
775
+ * and tighten a home the caller owns that is group- or world-writable with
776
+ * `chmod go-w` (the CLEO home is CLEO's own directory). A home reached
777
+ * through a symlink is never changed: its target may be a directory CLEO
778
+ * does not own the purpose of, so only a warning is given (review N4).
779
+ *
780
+ * @returns A one-line warning when the home was tightened or should be, else `null`.
781
+ */
782
+ private prepareDirectory;
783
+ /**
784
+ * Write `state` atomically at 0600: a new temp file (`O_EXCL | O_NOFOLLOW`),
785
+ * fsync (F_FULLFSYNC on macOS, through libuv), rename over the target, then
786
+ * fsync the directory so the rename survives a crash. No backup copy is
787
+ * made. On Windows a rename blocked by a scanner (EPERM, EBUSY) is retried.
788
+ */
789
+ private writeAtomic;
790
+ /**
791
+ * Create the file owner-only, in an owner-only directory, if it does not
792
+ * exist, so the lock never creates it at the default mode. `O_EXCL` plus
793
+ * `O_NOFOLLOW` close the window between the symlink check and the create.
794
+ */
795
+ private seed;
796
+ /**
797
+ * Delete temp files a crashed writer left behind. Runs under the lock, so
798
+ * no live writer's temp file can match.
799
+ */
800
+ private sweepTempFiles;
801
+ /**
802
+ * Delete any rotated backup of the file (L3). This store never makes one,
803
+ * but a generic `withLock` write elsewhere would, and a backup keeps old
804
+ * tokens and keys on disk. Best effort.
805
+ */
806
+ private purgeBackups;
807
+ }
808
+ export {};
809
+ //# sourceMappingURL=nexus-device.d.ts.map