@12ui/design 0.2.71 → 0.2.73

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 (680) hide show
  1. package/README.md +248 -55
  2. package/SKILL.md +28 -33
  3. package/dist/cli-arguments.d.ts.map +1 -1
  4. package/dist/cli-arguments.js +3 -0
  5. package/dist/cli-arguments.js.map +1 -1
  6. package/dist/cli-branch-command.d.ts.map +1 -1
  7. package/dist/cli-branch-command.js +4 -0
  8. package/dist/cli-branch-command.js.map +1 -1
  9. package/dist/cli-capabilities.d.ts +35 -1
  10. package/dist/cli-capabilities.d.ts.map +1 -1
  11. package/dist/cli-capabilities.js +36 -1
  12. package/dist/cli-capabilities.js.map +1 -1
  13. package/dist/cli-corpus-command.d.ts +5 -4
  14. package/dist/cli-corpus-command.d.ts.map +1 -1
  15. package/dist/cli-corpus-command.js +17 -26
  16. package/dist/cli-corpus-command.js.map +1 -1
  17. package/dist/cli-corpus-run.js +1 -1
  18. package/dist/cli-corpus-run.js.map +1 -1
  19. package/dist/cli-draft-command.js +1 -1
  20. package/dist/cli-draft-command.js.map +1 -1
  21. package/dist/cli-help/design.d.ts +26 -6
  22. package/dist/cli-help/design.d.ts.map +1 -1
  23. package/dist/cli-help/design.js +39 -7
  24. package/dist/cli-help/design.js.map +1 -1
  25. package/dist/cli-help/index.d.ts +206 -7
  26. package/dist/cli-help/index.d.ts.map +1 -1
  27. package/dist/cli-help/index.js +4 -0
  28. package/dist/cli-help/index.js.map +1 -1
  29. package/dist/cli-help/meta.d.ts +1 -1
  30. package/dist/cli-help/meta.d.ts.map +1 -1
  31. package/dist/cli-help/meta.js +2 -1
  32. package/dist/cli-help/meta.js.map +1 -1
  33. package/dist/cli-help/scene.d.ts +96 -0
  34. package/dist/cli-help/scene.d.ts.map +1 -0
  35. package/dist/cli-help/scene.js +162 -0
  36. package/dist/cli-help/scene.js.map +1 -0
  37. package/dist/cli-help/workflow.d.ts +90 -0
  38. package/dist/cli-help/workflow.d.ts.map +1 -0
  39. package/dist/cli-help/workflow.js +89 -0
  40. package/dist/cli-help/workflow.js.map +1 -0
  41. package/dist/cli-open-browser.d.ts +7 -0
  42. package/dist/cli-open-browser.d.ts.map +1 -0
  43. package/dist/cli-open-browser.js +25 -0
  44. package/dist/cli-open-browser.js.map +1 -0
  45. package/dist/cli-prototype-command.d.ts +3 -1
  46. package/dist/cli-prototype-command.d.ts.map +1 -1
  47. package/dist/cli-prototype-command.js +33 -7
  48. package/dist/cli-prototype-command.js.map +1 -1
  49. package/dist/cli-review-command.d.ts.map +1 -1
  50. package/dist/cli-review-command.js +1 -16
  51. package/dist/cli-review-command.js.map +1 -1
  52. package/dist/cli-run.d.ts.map +1 -1
  53. package/dist/cli-run.js +41 -3
  54. package/dist/cli-run.js.map +1 -1
  55. package/dist/cli-scene-command.d.ts +3 -0
  56. package/dist/cli-scene-command.d.ts.map +1 -0
  57. package/dist/cli-scene-command.js +292 -0
  58. package/dist/cli-scene-command.js.map +1 -0
  59. package/dist/cli-usage.d.ts +8 -0
  60. package/dist/cli-usage.d.ts.map +1 -1
  61. package/dist/cli-usage.js +65 -6
  62. package/dist/cli-usage.js.map +1 -1
  63. package/dist/cli-workflow-command.d.ts +8 -0
  64. package/dist/cli-workflow-command.d.ts.map +1 -0
  65. package/dist/cli-workflow-command.js +97 -0
  66. package/dist/cli-workflow-command.js.map +1 -0
  67. package/dist/cli-workspace-command.d.ts +25 -0
  68. package/dist/cli-workspace-command.d.ts.map +1 -0
  69. package/dist/cli-workspace-command.js +95 -0
  70. package/dist/cli-workspace-command.js.map +1 -0
  71. package/dist/config.d.ts +6 -0
  72. package/dist/config.d.ts.map +1 -1
  73. package/dist/config.js +12 -4
  74. package/dist/config.js.map +1 -1
  75. package/dist/corpus-access.d.ts +11 -0
  76. package/dist/corpus-access.d.ts.map +1 -0
  77. package/dist/corpus-access.js +18 -0
  78. package/dist/corpus-access.js.map +1 -0
  79. package/dist/corpus-client.d.ts +11 -11
  80. package/dist/corpus-client.d.ts.map +1 -1
  81. package/dist/corpus-client.js +17 -6
  82. package/dist/corpus-client.js.map +1 -1
  83. package/dist/corpus-inspiration-failure.d.ts +1 -1
  84. package/dist/corpus-inspiration-failure.js +2 -2
  85. package/dist/corpus-inspiration-failure.js.map +1 -1
  86. package/dist/corpus-reference-download-retry.d.ts.map +1 -1
  87. package/dist/corpus-reference-download-retry.js +11 -1
  88. package/dist/corpus-reference-download-retry.js.map +1 -1
  89. package/dist/corpus-request-error.d.ts +2 -0
  90. package/dist/corpus-request-error.d.ts.map +1 -1
  91. package/dist/corpus-request-error.js +2 -0
  92. package/dist/corpus-request-error.js.map +1 -1
  93. package/dist/corpus-retry-guidance.d.ts +5 -0
  94. package/dist/corpus-retry-guidance.d.ts.map +1 -0
  95. package/dist/corpus-retry-guidance.js +18 -0
  96. package/dist/corpus-retry-guidance.js.map +1 -0
  97. package/dist/improve-pricing-plan.d.ts.map +1 -1
  98. package/dist/improve-pricing-plan.js +2 -4
  99. package/dist/improve-pricing-plan.js.map +1 -1
  100. package/dist/index.d.ts +10 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +10 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/legacy-skill-catalog.d.ts.map +1 -1
  105. package/dist/legacy-skill-catalog.js +52 -0
  106. package/dist/legacy-skill-catalog.js.map +1 -1
  107. package/dist/mcp-slate-command.d.ts +31 -0
  108. package/dist/mcp-slate-command.d.ts.map +1 -0
  109. package/dist/mcp-slate-command.js +118 -0
  110. package/dist/mcp-slate-command.js.map +1 -0
  111. package/dist/mcp-slate-image.d.ts +22 -0
  112. package/dist/mcp-slate-image.d.ts.map +1 -0
  113. package/dist/mcp-slate-image.js +28 -0
  114. package/dist/mcp-slate-image.js.map +1 -0
  115. package/dist/mcp-slate-latest.d.ts +13 -0
  116. package/dist/mcp-slate-latest.d.ts.map +1 -0
  117. package/dist/mcp-slate-latest.js +34 -0
  118. package/dist/mcp-slate-latest.js.map +1 -0
  119. package/dist/mcp-slate-names.d.ts +25 -0
  120. package/dist/mcp-slate-names.d.ts.map +1 -0
  121. package/dist/mcp-slate-names.js +49 -0
  122. package/dist/mcp-slate-names.js.map +1 -0
  123. package/dist/mcp-slate-resource.d.ts +5 -0
  124. package/dist/mcp-slate-resource.d.ts.map +1 -0
  125. package/dist/mcp-slate-resource.js +37 -0
  126. package/dist/mcp-slate-resource.js.map +1 -0
  127. package/dist/mcp-slate-run.d.ts +41 -0
  128. package/dist/mcp-slate-run.d.ts.map +1 -0
  129. package/dist/mcp-slate-run.js +88 -0
  130. package/dist/mcp-slate-run.js.map +1 -0
  131. package/dist/mcp-slate-tool-defs.d.ts +61 -0
  132. package/dist/mcp-slate-tool-defs.d.ts.map +1 -0
  133. package/dist/mcp-slate-tool-defs.js +247 -0
  134. package/dist/mcp-slate-tool-defs.js.map +1 -0
  135. package/dist/mcp-slate-tools.d.ts +8 -0
  136. package/dist/mcp-slate-tools.d.ts.map +1 -0
  137. package/dist/mcp-slate-tools.js +45 -0
  138. package/dist/mcp-slate-tools.js.map +1 -0
  139. package/dist/mcp-slate-ui-copy.d.ts +134 -0
  140. package/dist/mcp-slate-ui-copy.d.ts.map +1 -0
  141. package/dist/mcp-slate-ui-copy.js +136 -0
  142. package/dist/mcp-slate-ui-copy.js.map +1 -0
  143. package/dist/mcp-slate-ui-script-bridge.d.ts +18 -0
  144. package/dist/mcp-slate-ui-script-bridge.d.ts.map +1 -0
  145. package/dist/mcp-slate-ui-script-bridge.js +137 -0
  146. package/dist/mcp-slate-ui-script-bridge.js.map +1 -0
  147. package/dist/mcp-slate-ui-script-browser.d.ts +12 -0
  148. package/dist/mcp-slate-ui-script-browser.d.ts.map +1 -0
  149. package/dist/mcp-slate-ui-script-browser.js +19 -0
  150. package/dist/mcp-slate-ui-script-browser.js.map +1 -0
  151. package/dist/mcp-slate-ui-script-dialog.d.ts +20 -0
  152. package/dist/mcp-slate-ui-script-dialog.d.ts.map +1 -0
  153. package/dist/mcp-slate-ui-script-dialog.js +332 -0
  154. package/dist/mcp-slate-ui-script-dialog.js.map +1 -0
  155. package/dist/mcp-slate-ui-script-inline.d.ts +10 -0
  156. package/dist/mcp-slate-ui-script-inline.d.ts.map +1 -0
  157. package/dist/mcp-slate-ui-script-inline.js +221 -0
  158. package/dist/mcp-slate-ui-script-inline.js.map +1 -0
  159. package/dist/mcp-slate-ui-script-inspiration.d.ts +12 -0
  160. package/dist/mcp-slate-ui-script-inspiration.d.ts.map +1 -0
  161. package/dist/mcp-slate-ui-script-inspiration.js +216 -0
  162. package/dist/mcp-slate-ui-script-inspiration.js.map +1 -0
  163. package/dist/mcp-slate-ui-script-more.d.ts +30 -0
  164. package/dist/mcp-slate-ui-script-more.d.ts.map +1 -0
  165. package/dist/mcp-slate-ui-script-more.js +225 -0
  166. package/dist/mcp-slate-ui-script-more.js.map +1 -0
  167. package/dist/mcp-slate-ui-script-progress.d.ts +7 -0
  168. package/dist/mcp-slate-ui-script-progress.d.ts.map +1 -0
  169. package/dist/mcp-slate-ui-script-progress.js +9 -0
  170. package/dist/mcp-slate-ui-script-progress.js.map +1 -0
  171. package/dist/mcp-slate-ui-script-remote.d.ts +28 -0
  172. package/dist/mcp-slate-ui-script-remote.d.ts.map +1 -0
  173. package/dist/mcp-slate-ui-script-remote.js +147 -0
  174. package/dist/mcp-slate-ui-script-remote.js.map +1 -0
  175. package/dist/mcp-slate-ui-script-scribble.d.ts +19 -0
  176. package/dist/mcp-slate-ui-script-scribble.d.ts.map +1 -0
  177. package/dist/mcp-slate-ui-script-scribble.js +114 -0
  178. package/dist/mcp-slate-ui-script-scribble.js.map +1 -0
  179. package/dist/mcp-slate-ui-script-select.d.ts +16 -0
  180. package/dist/mcp-slate-ui-script-select.d.ts.map +1 -0
  181. package/dist/mcp-slate-ui-script-select.js +73 -0
  182. package/dist/mcp-slate-ui-script-select.js.map +1 -0
  183. package/dist/mcp-slate-ui-script-stage.d.ts +11 -0
  184. package/dist/mcp-slate-ui-script-stage.d.ts.map +1 -0
  185. package/dist/mcp-slate-ui-script-stage.js +181 -0
  186. package/dist/mcp-slate-ui-script-stage.js.map +1 -0
  187. package/dist/mcp-slate-ui-script-state.d.ts +13 -0
  188. package/dist/mcp-slate-ui-script-state.d.ts.map +1 -0
  189. package/dist/mcp-slate-ui-script-state.js +306 -0
  190. package/dist/mcp-slate-ui-script-state.js.map +1 -0
  191. package/dist/mcp-slate-ui-script-thumbtools.d.ts +23 -0
  192. package/dist/mcp-slate-ui-script-thumbtools.d.ts.map +1 -0
  193. package/dist/mcp-slate-ui-script-thumbtools.js +103 -0
  194. package/dist/mcp-slate-ui-script-thumbtools.js.map +1 -0
  195. package/dist/mcp-slate-ui-script.d.ts +28 -0
  196. package/dist/mcp-slate-ui-script.d.ts.map +1 -0
  197. package/dist/mcp-slate-ui-script.js +58 -0
  198. package/dist/mcp-slate-ui-script.js.map +1 -0
  199. package/dist/mcp-slate-ui-styles-dialog.d.ts +17 -0
  200. package/dist/mcp-slate-ui-styles-dialog.d.ts.map +1 -0
  201. package/dist/mcp-slate-ui-styles-dialog.js +73 -0
  202. package/dist/mcp-slate-ui-styles-dialog.js.map +1 -0
  203. package/dist/mcp-slate-ui-styles-stage.d.ts +12 -0
  204. package/dist/mcp-slate-ui-styles-stage.d.ts.map +1 -0
  205. package/dist/mcp-slate-ui-styles-stage.js +53 -0
  206. package/dist/mcp-slate-ui-styles-stage.js.map +1 -0
  207. package/dist/mcp-slate-ui-styles-thumbtools.d.ts +10 -0
  208. package/dist/mcp-slate-ui-styles-thumbtools.d.ts.map +1 -0
  209. package/dist/mcp-slate-ui-styles-thumbtools.js +25 -0
  210. package/dist/mcp-slate-ui-styles-thumbtools.js.map +1 -0
  211. package/dist/mcp-slate-ui-styles.d.ts +13 -0
  212. package/dist/mcp-slate-ui-styles.d.ts.map +1 -0
  213. package/dist/mcp-slate-ui-styles.js +117 -0
  214. package/dist/mcp-slate-ui-styles.js.map +1 -0
  215. package/dist/mcp-slate-ui.d.ts +16 -0
  216. package/dist/mcp-slate-ui.d.ts.map +1 -0
  217. package/dist/mcp-slate-ui.js +58 -0
  218. package/dist/mcp-slate-ui.js.map +1 -0
  219. package/dist/mcp-slate-workspace-tools.d.ts +15 -0
  220. package/dist/mcp-slate-workspace-tools.d.ts.map +1 -0
  221. package/dist/mcp-slate-workspace-tools.js +155 -0
  222. package/dist/mcp-slate-workspace-tools.js.map +1 -0
  223. package/dist/mcp-tool-definitions.d.ts.map +1 -1
  224. package/dist/mcp-tool-definitions.js +9 -7
  225. package/dist/mcp-tool-definitions.js.map +1 -1
  226. package/dist/mcp-tool-handler.d.ts +8 -2
  227. package/dist/mcp-tool-handler.d.ts.map +1 -1
  228. package/dist/mcp-tool-handler.js +34 -13
  229. package/dist/mcp-tool-handler.js.map +1 -1
  230. package/dist/mcp-workflow-tools.d.ts +6 -0
  231. package/dist/mcp-workflow-tools.d.ts.map +1 -0
  232. package/dist/mcp-workflow-tools.js +108 -0
  233. package/dist/mcp-workflow-tools.js.map +1 -0
  234. package/dist/prototype-browsers.d.ts +8 -2
  235. package/dist/prototype-browsers.d.ts.map +1 -1
  236. package/dist/prototype-browsers.js +8 -3
  237. package/dist/prototype-browsers.js.map +1 -1
  238. package/dist/prototype-label-client.d.ts +5 -0
  239. package/dist/prototype-label-client.d.ts.map +1 -1
  240. package/dist/prototype-label-client.js.map +1 -1
  241. package/dist/prototype-poststep.d.ts +2 -2
  242. package/dist/prototype-poststep.d.ts.map +1 -1
  243. package/dist/prototype-poststep.js +3 -0
  244. package/dist/prototype-poststep.js.map +1 -1
  245. package/dist/prototype-process.js +1 -1
  246. package/dist/prototype-process.js.map +1 -1
  247. package/dist/prototype-run-record.d.ts +6 -2
  248. package/dist/prototype-run-record.d.ts.map +1 -1
  249. package/dist/prototype-run-record.js +6 -0
  250. package/dist/prototype-run-record.js.map +1 -1
  251. package/dist/prototype-runner.d.ts +2 -2
  252. package/dist/prototype-runner.d.ts.map +1 -1
  253. package/dist/prototype-runner.js +5 -2
  254. package/dist/prototype-runner.js.map +1 -1
  255. package/dist/prototype-workflow-contract.d.ts +13 -0
  256. package/dist/prototype-workflow-contract.d.ts.map +1 -0
  257. package/dist/prototype-workflow-contract.js +82 -0
  258. package/dist/prototype-workflow-contract.js.map +1 -0
  259. package/dist/prototype-workflow-labels.d.ts +29 -0
  260. package/dist/prototype-workflow-labels.d.ts.map +1 -0
  261. package/dist/prototype-workflow-labels.js +93 -0
  262. package/dist/prototype-workflow-labels.js.map +1 -0
  263. package/dist/run-journal-commands.d.ts.map +1 -1
  264. package/dist/run-journal-commands.js +24 -5
  265. package/dist/run-journal-commands.js.map +1 -1
  266. package/dist/scene/camera-model.d.ts +48 -0
  267. package/dist/scene/camera-model.d.ts.map +1 -0
  268. package/dist/scene/camera-model.js +89 -0
  269. package/dist/scene/camera-model.js.map +1 -0
  270. package/dist/scene/camera-solve.d.ts +58 -0
  271. package/dist/scene/camera-solve.d.ts.map +1 -0
  272. package/dist/scene/camera-solve.js +288 -0
  273. package/dist/scene/camera-solve.js.map +1 -0
  274. package/dist/scene/capture-plan.d.ts +28 -0
  275. package/dist/scene/capture-plan.d.ts.map +1 -0
  276. package/dist/scene/capture-plan.js +79 -0
  277. package/dist/scene/capture-plan.js.map +1 -0
  278. package/dist/scene/capture.d.ts +21 -0
  279. package/dist/scene/capture.d.ts.map +1 -0
  280. package/dist/scene/capture.js +118 -0
  281. package/dist/scene/capture.js.map +1 -0
  282. package/dist/scene/image-colors.d.ts +25 -0
  283. package/dist/scene/image-colors.d.ts.map +1 -0
  284. package/dist/scene/image-colors.js +110 -0
  285. package/dist/scene/image-colors.js.map +1 -0
  286. package/dist/scene/image-rectify.d.ts +25 -0
  287. package/dist/scene/image-rectify.d.ts.map +1 -0
  288. package/dist/scene/image-rectify.js +82 -0
  289. package/dist/scene/image-rectify.js.map +1 -0
  290. package/dist/scene/image-sheet.d.ts +19 -0
  291. package/dist/scene/image-sheet.d.ts.map +1 -0
  292. package/dist/scene/image-sheet.js +118 -0
  293. package/dist/scene/image-sheet.js.map +1 -0
  294. package/dist/scene/least-squares.d.ts +31 -0
  295. package/dist/scene/least-squares.d.ts.map +1 -0
  296. package/dist/scene/least-squares.js +173 -0
  297. package/dist/scene/least-squares.js.map +1 -0
  298. package/dist/scene/numbers.d.ts +17 -0
  299. package/dist/scene/numbers.d.ts.map +1 -0
  300. package/dist/scene/numbers.js +39 -0
  301. package/dist/scene/numbers.js.map +1 -0
  302. package/dist/scene/placement.d.ts +43 -0
  303. package/dist/scene/placement.d.ts.map +1 -0
  304. package/dist/scene/placement.js +107 -0
  305. package/dist/scene/placement.js.map +1 -0
  306. package/dist/scene/raster.d.ts +18 -0
  307. package/dist/scene/raster.d.ts.map +1 -0
  308. package/dist/scene/raster.js +31 -0
  309. package/dist/scene/raster.js.map +1 -0
  310. package/dist/scene/scene-kit.d.ts +20 -0
  311. package/dist/scene/scene-kit.d.ts.map +1 -0
  312. package/dist/scene/scene-kit.js +74 -0
  313. package/dist/scene/scene-kit.js.map +1 -0
  314. package/dist/scene/scene-verbs.d.ts +4 -0
  315. package/dist/scene/scene-verbs.d.ts.map +1 -0
  316. package/dist/scene/scene-verbs.js +5 -0
  317. package/dist/scene/scene-verbs.js.map +1 -0
  318. package/dist/scene/seeded-random.d.ts +20 -0
  319. package/dist/scene/seeded-random.d.ts.map +1 -0
  320. package/dist/scene/seeded-random.js +42 -0
  321. package/dist/scene/seeded-random.js.map +1 -0
  322. package/dist/scene/spec-expression.d.ts +25 -0
  323. package/dist/scene/spec-expression.d.ts.map +1 -0
  324. package/dist/scene/spec-expression.js +234 -0
  325. package/dist/scene/spec-expression.js.map +1 -0
  326. package/dist/scene/static-server.d.ts +13 -0
  327. package/dist/scene/static-server.d.ts.map +1 -0
  328. package/dist/scene/static-server.js +99 -0
  329. package/dist/scene/static-server.js.map +1 -0
  330. package/dist/server.d.ts +22 -2
  331. package/dist/server.d.ts.map +1 -1
  332. package/dist/server.js +44 -8
  333. package/dist/server.js.map +1 -1
  334. package/dist/slate-actions.d.ts +38 -0
  335. package/dist/slate-actions.d.ts.map +1 -0
  336. package/dist/slate-actions.js +102 -0
  337. package/dist/slate-actions.js.map +1 -0
  338. package/dist/slate-annotate.d.ts +36 -0
  339. package/dist/slate-annotate.d.ts.map +1 -0
  340. package/dist/slate-annotate.js +71 -0
  341. package/dist/slate-annotate.js.map +1 -0
  342. package/dist/slate-approvals.d.ts +61 -0
  343. package/dist/slate-approvals.d.ts.map +1 -0
  344. package/dist/slate-approvals.js +153 -0
  345. package/dist/slate-approvals.js.map +1 -0
  346. package/dist/slate-browser-decision.d.ts +30 -0
  347. package/dist/slate-browser-decision.d.ts.map +1 -0
  348. package/dist/slate-browser-decision.js +24 -0
  349. package/dist/slate-browser-decision.js.map +1 -0
  350. package/dist/slate-browser-host-script.d.ts +14 -0
  351. package/dist/slate-browser-host-script.d.ts.map +1 -0
  352. package/dist/slate-browser-host-script.js +125 -0
  353. package/dist/slate-browser-host-script.js.map +1 -0
  354. package/dist/slate-browser-host.d.ts +9 -0
  355. package/dist/slate-browser-host.d.ts.map +1 -0
  356. package/dist/slate-browser-host.js +37 -0
  357. package/dist/slate-browser-host.js.map +1 -0
  358. package/dist/slate-browser-server.d.ts +37 -0
  359. package/dist/slate-browser-server.d.ts.map +1 -0
  360. package/dist/slate-browser-server.js +186 -0
  361. package/dist/slate-browser-server.js.map +1 -0
  362. package/dist/slate-commit.d.ts +28 -0
  363. package/dist/slate-commit.d.ts.map +1 -0
  364. package/dist/slate-commit.js +218 -0
  365. package/dist/slate-commit.js.map +1 -0
  366. package/dist/slate-corpus.d.ts +14 -0
  367. package/dist/slate-corpus.d.ts.map +1 -0
  368. package/dist/slate-corpus.js +32 -0
  369. package/dist/slate-corpus.js.map +1 -0
  370. package/dist/slate-edit-prompt.d.ts +18 -0
  371. package/dist/slate-edit-prompt.d.ts.map +1 -0
  372. package/dist/slate-edit-prompt.js +41 -0
  373. package/dist/slate-edit-prompt.js.map +1 -0
  374. package/dist/slate-edit-runner.d.ts +4 -0
  375. package/dist/slate-edit-runner.d.ts.map +1 -0
  376. package/dist/slate-edit-runner.js +90 -0
  377. package/dist/slate-edit-runner.js.map +1 -0
  378. package/dist/slate-editability.d.ts +20 -0
  379. package/dist/slate-editability.d.ts.map +1 -0
  380. package/dist/slate-editability.js +19 -0
  381. package/dist/slate-editability.js.map +1 -0
  382. package/dist/slate-errors.d.ts +15 -0
  383. package/dist/slate-errors.d.ts.map +1 -0
  384. package/dist/slate-errors.js +21 -0
  385. package/dist/slate-errors.js.map +1 -0
  386. package/dist/slate-handoff-options.d.ts +40 -0
  387. package/dist/slate-handoff-options.d.ts.map +1 -0
  388. package/dist/slate-handoff-options.js +127 -0
  389. package/dist/slate-handoff-options.js.map +1 -0
  390. package/dist/slate-handoff.d.ts +11 -0
  391. package/dist/slate-handoff.d.ts.map +1 -0
  392. package/dist/slate-handoff.js +56 -0
  393. package/dist/slate-handoff.js.map +1 -0
  394. package/dist/slate-ids.d.ts +33 -0
  395. package/dist/slate-ids.d.ts.map +1 -0
  396. package/dist/slate-ids.js +47 -0
  397. package/dist/slate-ids.js.map +1 -0
  398. package/dist/slate-image-executor.d.ts +39 -0
  399. package/dist/slate-image-executor.d.ts.map +1 -0
  400. package/dist/slate-image-executor.js +52 -0
  401. package/dist/slate-image-executor.js.map +1 -0
  402. package/dist/slate-inspiration.d.ts +22 -0
  403. package/dist/slate-inspiration.d.ts.map +1 -0
  404. package/dist/slate-inspiration.js +112 -0
  405. package/dist/slate-inspiration.js.map +1 -0
  406. package/dist/slate-labels.d.ts +11 -0
  407. package/dist/slate-labels.d.ts.map +1 -0
  408. package/dist/slate-labels.js +32 -0
  409. package/dist/slate-labels.js.map +1 -0
  410. package/dist/slate-ledger.d.ts +29 -0
  411. package/dist/slate-ledger.d.ts.map +1 -0
  412. package/dist/slate-ledger.js +65 -0
  413. package/dist/slate-ledger.js.map +1 -0
  414. package/dist/slate-liveness.d.ts +26 -0
  415. package/dist/slate-liveness.d.ts.map +1 -0
  416. package/dist/slate-liveness.js +49 -0
  417. package/dist/slate-liveness.js.map +1 -0
  418. package/dist/slate-local-image.d.ts +19 -0
  419. package/dist/slate-local-image.d.ts.map +1 -0
  420. package/dist/slate-local-image.js +36 -0
  421. package/dist/slate-local-image.js.map +1 -0
  422. package/dist/slate-lock.d.ts +64 -0
  423. package/dist/slate-lock.d.ts.map +1 -0
  424. package/dist/slate-lock.js +178 -0
  425. package/dist/slate-lock.js.map +1 -0
  426. package/dist/slate-paths.d.ts +45 -0
  427. package/dist/slate-paths.d.ts.map +1 -0
  428. package/dist/slate-paths.js +132 -0
  429. package/dist/slate-paths.js.map +1 -0
  430. package/dist/slate-pick.d.ts +29 -0
  431. package/dist/slate-pick.d.ts.map +1 -0
  432. package/dist/slate-pick.js +55 -0
  433. package/dist/slate-pick.js.map +1 -0
  434. package/dist/slate-reference-ids.d.ts +6 -0
  435. package/dist/slate-reference-ids.d.ts.map +1 -0
  436. package/dist/slate-reference-ids.js +17 -0
  437. package/dist/slate-reference-ids.js.map +1 -0
  438. package/dist/slate-reference-store.d.ts +23 -0
  439. package/dist/slate-reference-store.d.ts.map +1 -0
  440. package/dist/slate-reference-store.js +46 -0
  441. package/dist/slate-reference-store.js.map +1 -0
  442. package/dist/slate-resume.d.ts +19 -0
  443. package/dist/slate-resume.d.ts.map +1 -0
  444. package/dist/slate-resume.js +94 -0
  445. package/dist/slate-resume.js.map +1 -0
  446. package/dist/slate-round-params.d.ts +20 -0
  447. package/dist/slate-round-params.d.ts.map +1 -0
  448. package/dist/slate-round-params.js +32 -0
  449. package/dist/slate-round-params.js.map +1 -0
  450. package/dist/slate-round-prompt.d.ts +18 -0
  451. package/dist/slate-round-prompt.d.ts.map +1 -0
  452. package/dist/slate-round-prompt.js +20 -0
  453. package/dist/slate-round-prompt.js.map +1 -0
  454. package/dist/slate-round-runner.d.ts +4 -0
  455. package/dist/slate-round-runner.d.ts.map +1 -0
  456. package/dist/slate-round-runner.js +125 -0
  457. package/dist/slate-round-runner.js.map +1 -0
  458. package/dist/slate-round-search.d.ts +7 -0
  459. package/dist/slate-round-search.d.ts.map +1 -0
  460. package/dist/slate-round-search.js +77 -0
  461. package/dist/slate-round-search.js.map +1 -0
  462. package/dist/slate-run-action.d.ts +14 -0
  463. package/dist/slate-run-action.d.ts.map +1 -0
  464. package/dist/slate-run-action.js +81 -0
  465. package/dist/slate-run-action.js.map +1 -0
  466. package/dist/slate-runner-context.d.ts +57 -0
  467. package/dist/slate-runner-context.d.ts.map +1 -0
  468. package/dist/slate-runner-context.js +79 -0
  469. package/dist/slate-runner-context.js.map +1 -0
  470. package/dist/slate-runner-failure.d.ts +8 -0
  471. package/dist/slate-runner-failure.d.ts.map +1 -0
  472. package/dist/slate-runner-failure.js +17 -0
  473. package/dist/slate-runner-failure.js.map +1 -0
  474. package/dist/slate-runner-flag.d.ts +3 -0
  475. package/dist/slate-runner-flag.d.ts.map +1 -0
  476. package/dist/slate-runner-flag.js +3 -0
  477. package/dist/slate-runner-flag.js.map +1 -0
  478. package/dist/slate-runner-main.d.ts +31 -0
  479. package/dist/slate-runner-main.d.ts.map +1 -0
  480. package/dist/slate-runner-main.js +132 -0
  481. package/dist/slate-runner-main.js.map +1 -0
  482. package/dist/slate-runner-spawn.d.ts +23 -0
  483. package/dist/slate-runner-spawn.d.ts.map +1 -0
  484. package/dist/slate-runner-spawn.js +27 -0
  485. package/dist/slate-runner-spawn.js.map +1 -0
  486. package/dist/slate-runtime.d.ts +38 -0
  487. package/dist/slate-runtime.d.ts.map +1 -0
  488. package/dist/slate-runtime.js +48 -0
  489. package/dist/slate-runtime.js.map +1 -0
  490. package/dist/slate-versions.d.ts +26 -0
  491. package/dist/slate-versions.d.ts.map +1 -0
  492. package/dist/slate-versions.js +110 -0
  493. package/dist/slate-versions.js.map +1 -0
  494. package/dist/slate-view-contract.d.ts +233 -0
  495. package/dist/slate-view-contract.d.ts.map +1 -0
  496. package/dist/slate-view-contract.js +17 -0
  497. package/dist/slate-view-contract.js.map +1 -0
  498. package/dist/slate-view-types.d.ts +2 -0
  499. package/dist/slate-view-types.d.ts.map +1 -0
  500. package/dist/slate-view-types.js +2 -0
  501. package/dist/slate-view-types.js.map +1 -0
  502. package/dist/slate-view.d.ts +19 -0
  503. package/dist/slate-view.d.ts.map +1 -0
  504. package/dist/slate-view.js +184 -0
  505. package/dist/slate-view.js.map +1 -0
  506. package/dist/slate-workspace-parse.d.ts +20 -0
  507. package/dist/slate-workspace-parse.d.ts.map +1 -0
  508. package/dist/slate-workspace-parse.js +437 -0
  509. package/dist/slate-workspace-parse.js.map +1 -0
  510. package/dist/slate-workspace-store.d.ts +44 -0
  511. package/dist/slate-workspace-store.d.ts.map +1 -0
  512. package/dist/slate-workspace-store.js +270 -0
  513. package/dist/slate-workspace-store.js.map +1 -0
  514. package/dist/slate-workspace-types.d.ts +176 -0
  515. package/dist/slate-workspace-types.d.ts.map +1 -0
  516. package/dist/slate-workspace-types.js +13 -0
  517. package/dist/slate-workspace-types.js.map +1 -0
  518. package/dist/workflow/adapter-12ui.d.ts +4 -0
  519. package/dist/workflow/adapter-12ui.d.ts.map +1 -0
  520. package/dist/workflow/adapter-12ui.js +147 -0
  521. package/dist/workflow/adapter-12ui.js.map +1 -0
  522. package/dist/workflow/adapter-layerdoc.d.ts +16 -0
  523. package/dist/workflow/adapter-layerdoc.d.ts.map +1 -0
  524. package/dist/workflow/adapter-layerdoc.js +116 -0
  525. package/dist/workflow/adapter-layerdoc.js.map +1 -0
  526. package/dist/workflow/adapter-openai.d.ts +4 -0
  527. package/dist/workflow/adapter-openai.d.ts.map +1 -0
  528. package/dist/workflow/adapter-openai.js +166 -0
  529. package/dist/workflow/adapter-openai.js.map +1 -0
  530. package/dist/workflow/adapter-process.d.ts +7 -0
  531. package/dist/workflow/adapter-process.d.ts.map +1 -0
  532. package/dist/workflow/adapter-process.js +174 -0
  533. package/dist/workflow/adapter-process.js.map +1 -0
  534. package/dist/workflow/adapter-prototype-labels.d.ts +15 -0
  535. package/dist/workflow/adapter-prototype-labels.d.ts.map +1 -0
  536. package/dist/workflow/adapter-prototype-labels.js +71 -0
  537. package/dist/workflow/adapter-prototype-labels.js.map +1 -0
  538. package/dist/workflow/adapter-types.d.ts +40 -0
  539. package/dist/workflow/adapter-types.d.ts.map +1 -0
  540. package/dist/workflow/adapter-types.js +2 -0
  541. package/dist/workflow/adapter-types.js.map +1 -0
  542. package/dist/workflow/adapter-utils.d.ts +11 -0
  543. package/dist/workflow/adapter-utils.d.ts.map +1 -0
  544. package/dist/workflow/adapter-utils.js +72 -0
  545. package/dist/workflow/adapter-utils.js.map +1 -0
  546. package/dist/workflow/adapters.d.ts +6 -0
  547. package/dist/workflow/adapters.d.ts.map +1 -0
  548. package/dist/workflow/adapters.js +25 -0
  549. package/dist/workflow/adapters.js.map +1 -0
  550. package/dist/workflow/artifact.d.ts +11 -0
  551. package/dist/workflow/artifact.d.ts.map +1 -0
  552. package/dist/workflow/artifact.js +94 -0
  553. package/dist/workflow/artifact.js.map +1 -0
  554. package/dist/workflow/bundle-collect.d.ts +8 -0
  555. package/dist/workflow/bundle-collect.d.ts.map +1 -0
  556. package/dist/workflow/bundle-collect.js +56 -0
  557. package/dist/workflow/bundle-collect.js.map +1 -0
  558. package/dist/workflow/bundle-export.d.ts +16 -0
  559. package/dist/workflow/bundle-export.d.ts.map +1 -0
  560. package/dist/workflow/bundle-export.js +174 -0
  561. package/dist/workflow/bundle-export.js.map +1 -0
  562. package/dist/workflow/bundle-paths.d.ts +7 -0
  563. package/dist/workflow/bundle-paths.d.ts.map +1 -0
  564. package/dist/workflow/bundle-paths.js +35 -0
  565. package/dist/workflow/bundle-paths.js.map +1 -0
  566. package/dist/workflow/bundle.d.ts +4 -0
  567. package/dist/workflow/bundle.d.ts.map +1 -0
  568. package/dist/workflow/bundle.js +4 -0
  569. package/dist/workflow/bundle.js.map +1 -0
  570. package/dist/workflow/capture.d.ts +36 -0
  571. package/dist/workflow/capture.d.ts.map +1 -0
  572. package/dist/workflow/capture.js +81 -0
  573. package/dist/workflow/capture.js.map +1 -0
  574. package/dist/workflow/continuation-branch.d.ts +15 -0
  575. package/dist/workflow/continuation-branch.d.ts.map +1 -0
  576. package/dist/workflow/continuation-branch.js +103 -0
  577. package/dist/workflow/continuation-branch.js.map +1 -0
  578. package/dist/workflow/continuation-plans.d.ts +38 -0
  579. package/dist/workflow/continuation-plans.d.ts.map +1 -0
  580. package/dist/workflow/continuation-plans.js +78 -0
  581. package/dist/workflow/continuation-plans.js.map +1 -0
  582. package/dist/workflow/continuation.d.ts +25 -0
  583. package/dist/workflow/continuation.d.ts.map +1 -0
  584. package/dist/workflow/continuation.js +89 -0
  585. package/dist/workflow/continuation.js.map +1 -0
  586. package/dist/workflow/layerdoc-reader.d.ts +27 -0
  587. package/dist/workflow/layerdoc-reader.d.ts.map +1 -0
  588. package/dist/workflow/layerdoc-reader.js +81 -0
  589. package/dist/workflow/layerdoc-reader.js.map +1 -0
  590. package/dist/workflow/project-evidence.d.ts +48 -0
  591. package/dist/workflow/project-evidence.d.ts.map +1 -0
  592. package/dist/workflow/project-evidence.js +200 -0
  593. package/dist/workflow/project-evidence.js.map +1 -0
  594. package/dist/workflow/providers.d.ts +35 -0
  595. package/dist/workflow/providers.d.ts.map +1 -0
  596. package/dist/workflow/providers.js +110 -0
  597. package/dist/workflow/providers.js.map +1 -0
  598. package/dist/workflow/recipes-layerdoc-validation.d.ts +4 -0
  599. package/dist/workflow/recipes-layerdoc-validation.d.ts.map +1 -0
  600. package/dist/workflow/recipes-layerdoc-validation.js +98 -0
  601. package/dist/workflow/recipes-layerdoc-validation.js.map +1 -0
  602. package/dist/workflow/recipes-result-validation.d.ts +6 -0
  603. package/dist/workflow/recipes-result-validation.d.ts.map +1 -0
  604. package/dist/workflow/recipes-result-validation.js +38 -0
  605. package/dist/workflow/recipes-result-validation.js.map +1 -0
  606. package/dist/workflow/recipes-schemas.d.ts +7 -0
  607. package/dist/workflow/recipes-schemas.d.ts.map +1 -0
  608. package/dist/workflow/recipes-schemas.js +67 -0
  609. package/dist/workflow/recipes-schemas.js.map +1 -0
  610. package/dist/workflow/recipes-validation.d.ts +14 -0
  611. package/dist/workflow/recipes-validation.d.ts.map +1 -0
  612. package/dist/workflow/recipes-validation.js +68 -0
  613. package/dist/workflow/recipes-validation.js.map +1 -0
  614. package/dist/workflow/recipes.d.ts +20 -0
  615. package/dist/workflow/recipes.d.ts.map +1 -0
  616. package/dist/workflow/recipes.js +167 -0
  617. package/dist/workflow/recipes.js.map +1 -0
  618. package/dist/workflow/routing.d.ts +4 -0
  619. package/dist/workflow/routing.d.ts.map +1 -0
  620. package/dist/workflow/routing.js +28 -0
  621. package/dist/workflow/routing.js.map +1 -0
  622. package/dist/workflow/schema.d.ts +5 -0
  623. package/dist/workflow/schema.d.ts.map +1 -0
  624. package/dist/workflow/schema.js +29 -0
  625. package/dist/workflow/schema.js.map +1 -0
  626. package/dist/workflow/service.d.ts +59 -0
  627. package/dist/workflow/service.d.ts.map +1 -0
  628. package/dist/workflow/service.js +170 -0
  629. package/dist/workflow/service.js.map +1 -0
  630. package/dist/workflow/store-json.d.ts +5 -0
  631. package/dist/workflow/store-json.d.ts.map +1 -0
  632. package/dist/workflow/store-json.js +35 -0
  633. package/dist/workflow/store-json.js.map +1 -0
  634. package/dist/workflow/store-lock.d.ts +3 -0
  635. package/dist/workflow/store-lock.d.ts.map +1 -0
  636. package/dist/workflow/store-lock.js +85 -0
  637. package/dist/workflow/store-lock.js.map +1 -0
  638. package/dist/workflow/store-paths.d.ts +17 -0
  639. package/dist/workflow/store-paths.d.ts.map +1 -0
  640. package/dist/workflow/store-paths.js +67 -0
  641. package/dist/workflow/store-paths.js.map +1 -0
  642. package/dist/workflow/store-validation.d.ts +7 -0
  643. package/dist/workflow/store-validation.d.ts.map +1 -0
  644. package/dist/workflow/store-validation.js +119 -0
  645. package/dist/workflow/store-validation.js.map +1 -0
  646. package/dist/workflow/store.d.ts +10 -0
  647. package/dist/workflow/store.d.ts.map +1 -0
  648. package/dist/workflow/store.js +193 -0
  649. package/dist/workflow/store.js.map +1 -0
  650. package/dist/workflow/types.d.ts +120 -0
  651. package/dist/workflow/types.d.ts.map +1 -0
  652. package/dist/workflow/types.js +15 -0
  653. package/dist/workflow/types.js.map +1 -0
  654. package/dist/workflow-transport.d.ts +28 -0
  655. package/dist/workflow-transport.d.ts.map +1 -0
  656. package/dist/workflow-transport.js +184 -0
  657. package/dist/workflow-transport.js.map +1 -0
  658. package/open-design.json +2 -2
  659. package/package.json +4 -1
  660. package/prototype-kit/tests/rules-check.mjs +9 -0
  661. package/runtime/codex-conversion/src/images/native-invoke.mjs +1 -1
  662. package/runtime/codex-conversion/src/images/native.mjs +8 -4
  663. package/runtime/layerdoc-validator.mjs +1860 -0
  664. package/scene-kit/starter/camera.js +10 -0
  665. package/scene-kit/starter/gobo.js +60 -0
  666. package/scene-kit/starter/index.html +23 -0
  667. package/scene-kit/starter/lib.js +84 -0
  668. package/scene-kit/starter/main.js +78 -0
  669. package/scene-kit/starter/scene.js +33 -0
  670. package/scene-kit/vendor/LICENSE +21 -0
  671. package/scene-kit/vendor/addons/geometries/RoundedBoxGeometry.js +216 -0
  672. package/scene-kit/vendor/addons/renderers/CSS3DRenderer.js +454 -0
  673. package/scene-kit/vendor/three.core.js +60586 -0
  674. package/scene-kit/vendor/three.module.js +19719 -0
  675. package/skills/12ui-design/SKILL.md +21 -61
  676. package/skills/12ui-design/improve.md +5 -1
  677. package/skills/12ui-design/inspire.md +8 -5
  678. package/skills/12ui-design/outputs.md +23 -2
  679. package/skills/12ui-design/scene.md +172 -0
  680. package/skills/12ui-design/session.md +161 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * O_EXCL lock files with guarded, compare-before-unlink stale takeover.
3
+ *
4
+ * Three locks share this code: the workspace lock and the approval-store lock
5
+ * (a holder is stale only when its PID is gone; a live holder is waited on for
6
+ * a bounded time and never taken over) and the per-op runner lock (a holder is
7
+ * alive only while its PID is alive AND its mtime, the heartbeat, is fresh).
8
+ */
9
+ export declare const LOCK_WAIT_MS = 10000;
10
+ export declare const RUNNER_HEARTBEAT_INTERVAL_MS = 5000;
11
+ export declare const RUNNER_ALIVE_WINDOW_MS = 20000;
12
+ export type LockHolder = {
13
+ pid: number;
14
+ nonce: string;
15
+ startedAt: string;
16
+ };
17
+ export declare const processIsAlive: (pid: number) => boolean;
18
+ export declare const parseHolder: (raw: string) => LockHolder | undefined;
19
+ export type StalePolicy = (holder: LockHolder, mtimeMs: number, now: number) => boolean;
20
+ /** Workspace and approval locks: stale only when the holder process is gone. */
21
+ export declare const staleWhenProcessGone: StalePolicy;
22
+ /** Per-op runner lock: alive needs the PID and a fresh heartbeat. */
23
+ export declare const staleWhenHeartbeatLapsed: StalePolicy;
24
+ export type HeldLock = {
25
+ file: string;
26
+ holder: LockHolder;
27
+ release: () => Promise<void>;
28
+ touch: () => Promise<void>;
29
+ };
30
+ /**
31
+ * Take a stale lock over, one taker at a time. Takeover is serialised behind an
32
+ * exclusive guard file (`<lock>.takeover`, created with O_EXCL). Under the
33
+ * guard the lock is re-read: it is unlinked only if it is still byte-for-byte
34
+ * the stale holder the caller judged, so a lock that a rival re-created in the
35
+ * meantime is never touched. The caller then creates the new lock with the
36
+ * ordinary exclusive create. Returns true when the stale lock was removed,
37
+ * false when the guard was busy or the lock had already changed (the caller
38
+ * simply loops and re-observes).
39
+ */
40
+ export declare const takeOverStale: (file: string, observedRaw: string) => Promise<boolean>;
41
+ export declare const holderState: (file: string, policy: StalePolicy, now?: number) => Promise<{
42
+ state: "absent";
43
+ } | {
44
+ state: "live" | "stale";
45
+ holder: LockHolder | undefined;
46
+ raw: string;
47
+ mtimeMs: number;
48
+ }>;
49
+ export declare const acquireLock: (args: {
50
+ file: string;
51
+ policy: StalePolicy;
52
+ /** How long to wait for a live holder; 0 means fail at once. */
53
+ waitMs?: number;
54
+ busyMessage?: string;
55
+ }) => Promise<HeldLock>;
56
+ /** In-process queue per lock path, then the file lock: one writer at a time in this process too. */
57
+ export declare const withLock: <T>(args: {
58
+ file: string;
59
+ policy?: StalePolicy;
60
+ waitMs?: number;
61
+ busyMessage?: string;
62
+ run: () => Promise<T>;
63
+ }) => Promise<T>;
64
+ //# sourceMappingURL=slate-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-lock.d.ts","sourceRoot":"","sources":["../src/slate-lock.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AAEH,eAAO,MAAM,YAAY,QAAS,CAAC;AACnC,eAAO,MAAM,4BAA4B,OAAQ,CAAC;AAClD,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,OAQ5C,CAAC;AAIF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,UAAU,GAAG,SAQtD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;AAExF,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,EAAE,WAAqD,CAAC;AAEzF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,EAAE,WAEtC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAiBtH;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,KAAG,OAAO,CAAC,OAAO,CAgBtF,CAAC;AAYF,eAAO,MAAM,WAAW,GACtB,MAAM,MAAM,EACZ,QAAQ,WAAW,EACnB,YAAgB,KACf,OAAO,CAAC;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiBzH,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,KAAG,OAAO,CAAC,QAAQ,CA2BnB,CAAC;AAIF,oGAAoG;AACpG,eAAO,MAAM,QAAQ,GAAU,CAAC,EAAE,MAAM;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;CACvB,KAAG,OAAO,CAAC,CAAC,CAqBZ,CAAC"}
@@ -0,0 +1,178 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { open, readFile, rm, stat, utimes } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { SlateError } from './slate-errors.js';
5
+ /**
6
+ * O_EXCL lock files with guarded, compare-before-unlink stale takeover.
7
+ *
8
+ * Three locks share this code: the workspace lock and the approval-store lock
9
+ * (a holder is stale only when its PID is gone; a live holder is waited on for
10
+ * a bounded time and never taken over) and the per-op runner lock (a holder is
11
+ * alive only while its PID is alive AND its mtime, the heartbeat, is fresh).
12
+ */
13
+ export const LOCK_WAIT_MS = 10_000;
14
+ export const RUNNER_HEARTBEAT_INTERVAL_MS = 5_000;
15
+ export const RUNNER_ALIVE_WINDOW_MS = 20_000;
16
+ export const processIsAlive = (pid) => {
17
+ try {
18
+ process.kill(pid, 0);
19
+ return true;
20
+ }
21
+ catch (error) {
22
+ // EPERM means the process exists and belongs to somebody else.
23
+ return error.code === 'EPERM';
24
+ }
25
+ };
26
+ const sleep = (milliseconds) => new Promise((resolve) => { setTimeout(resolve, milliseconds); });
27
+ export const parseHolder = (raw) => {
28
+ try {
29
+ const parsed = JSON.parse(raw);
30
+ if (Number.isSafeInteger(parsed.pid) && typeof parsed.nonce === 'string' && typeof parsed.startedAt === 'string') {
31
+ return { pid: parsed.pid, nonce: parsed.nonce, startedAt: parsed.startedAt };
32
+ }
33
+ }
34
+ catch { /* an unreadable lock is treated as held by someone else */ }
35
+ return undefined;
36
+ };
37
+ /** Workspace and approval locks: stale only when the holder process is gone. */
38
+ export const staleWhenProcessGone = (holder) => !processIsAlive(holder.pid);
39
+ /** Per-op runner lock: alive needs the PID and a fresh heartbeat. */
40
+ export const staleWhenHeartbeatLapsed = (holder, mtimeMs, now) => (!processIsAlive(holder.pid) || now - mtimeMs > RUNNER_ALIVE_WINDOW_MS);
41
+ const tryCreate = async (file, holder) => {
42
+ try {
43
+ const handle = await open(file, 'wx', 0o600);
44
+ try {
45
+ await handle.writeFile(`${JSON.stringify(holder)}\n`, 'utf8');
46
+ }
47
+ finally {
48
+ await handle.close();
49
+ }
50
+ return true;
51
+ }
52
+ catch (error) {
53
+ if (error.code === 'EEXIST')
54
+ return false;
55
+ throw error;
56
+ }
57
+ };
58
+ /**
59
+ * Take a stale lock over, one taker at a time. Takeover is serialised behind an
60
+ * exclusive guard file (`<lock>.takeover`, created with O_EXCL). Under the
61
+ * guard the lock is re-read: it is unlinked only if it is still byte-for-byte
62
+ * the stale holder the caller judged, so a lock that a rival re-created in the
63
+ * meantime is never touched. The caller then creates the new lock with the
64
+ * ordinary exclusive create. Returns true when the stale lock was removed,
65
+ * false when the guard was busy or the lock had already changed (the caller
66
+ * simply loops and re-observes).
67
+ */
68
+ export const takeOverStale = async (file, observedRaw) => {
69
+ const guardFile = `${file}.takeover`;
70
+ const guardHolder = { pid: process.pid, nonce: randomUUID(), startedAt: new Date().toISOString() };
71
+ if (!(await tryCreate(guardFile, guardHolder))) {
72
+ await clearDeadGuard(guardFile);
73
+ return false;
74
+ }
75
+ try {
76
+ const current = await readFile(file, 'utf8').catch(() => undefined);
77
+ if (current !== observedRaw)
78
+ return false;
79
+ await rm(file, { force: true });
80
+ return true;
81
+ }
82
+ finally {
83
+ const guardNow = await readFile(guardFile, 'utf8').catch(() => '');
84
+ if (parseHolder(guardNow)?.nonce === guardHolder.nonce)
85
+ await rm(guardFile, { force: true });
86
+ }
87
+ };
88
+ /** A guard is held for milliseconds; one whose creator process is gone is cleared. */
89
+ const clearDeadGuard = async (guardFile) => {
90
+ const raw = await readFile(guardFile, 'utf8').catch(() => undefined);
91
+ if (raw === undefined)
92
+ return;
93
+ const holder = parseHolder(raw);
94
+ if (holder ? processIsAlive(holder.pid) : (Date.now() - (await stat(guardFile).then((s) => s.mtimeMs, () => Date.now()))) < 2_000)
95
+ return;
96
+ const again = await readFile(guardFile, 'utf8').catch(() => undefined);
97
+ if (again === raw)
98
+ await rm(guardFile, { force: true });
99
+ };
100
+ export const holderState = async (file, policy, now = Date.now()) => {
101
+ let raw;
102
+ let mtimeMs;
103
+ try {
104
+ raw = await readFile(file, 'utf8');
105
+ mtimeMs = (await stat(file)).mtimeMs;
106
+ }
107
+ catch (error) {
108
+ if (error.code === 'ENOENT')
109
+ return { state: 'absent' };
110
+ throw error;
111
+ }
112
+ const holder = parseHolder(raw);
113
+ if (!holder) {
114
+ // A lock that has no readable holder is only stale once it is old: a
115
+ // creator may be between open and write.
116
+ return { state: now - mtimeMs > 2_000 ? 'stale' : 'live', holder, raw, mtimeMs };
117
+ }
118
+ return { state: policy(holder, mtimeMs, now) ? 'stale' : 'live', holder, raw, mtimeMs };
119
+ };
120
+ export const acquireLock = async (args) => {
121
+ const holder = { pid: process.pid, nonce: randomUUID(), startedAt: new Date().toISOString() };
122
+ const deadline = Date.now() + (args.waitMs ?? LOCK_WAIT_MS);
123
+ for (;;) {
124
+ if (await tryCreate(args.file, holder)) {
125
+ return {
126
+ file: args.file,
127
+ holder,
128
+ touch: async () => { const now = new Date(); await utimes(args.file, now, now); },
129
+ release: async () => {
130
+ // Remove only our own lock: a takeover after a long pause may have replaced it.
131
+ const current = await readFile(args.file, 'utf8').catch(() => '');
132
+ if (parseHolder(current)?.nonce === holder.nonce)
133
+ await rm(args.file, { force: true });
134
+ },
135
+ };
136
+ }
137
+ const state = await holderState(args.file, args.policy);
138
+ if (state.state === 'stale') {
139
+ if (!(await takeOverStale(args.file, state.raw)))
140
+ await sleep(2 + Math.floor(Math.random() * 6));
141
+ continue;
142
+ }
143
+ if (state.state === 'absent')
144
+ continue;
145
+ if (Date.now() >= deadline) {
146
+ throw new SlateError('workspace_busy', args.busyMessage ?? `Another process holds ${path.basename(args.file)}; try again in a moment.`);
147
+ }
148
+ await sleep(25 + Math.floor(Math.random() * 25));
149
+ }
150
+ };
151
+ const processMutexes = new Map();
152
+ /** In-process queue per lock path, then the file lock: one writer at a time in this process too. */
153
+ export const withLock = async (args) => {
154
+ const previous = processMutexes.get(args.file) ?? Promise.resolve();
155
+ const turn = previous.catch(() => undefined).then(async () => {
156
+ const lock = await acquireLock({
157
+ file: args.file,
158
+ policy: args.policy ?? staleWhenProcessGone,
159
+ ...(args.waitMs === undefined ? {} : { waitMs: args.waitMs }),
160
+ ...(args.busyMessage ? { busyMessage: args.busyMessage } : {}),
161
+ });
162
+ try {
163
+ return await args.run();
164
+ }
165
+ finally {
166
+ await lock.release();
167
+ }
168
+ });
169
+ processMutexes.set(args.file, turn);
170
+ try {
171
+ return await turn;
172
+ }
173
+ finally {
174
+ if (processMutexes.get(args.file) === turn)
175
+ processMutexes.delete(args.file);
176
+ }
177
+ };
178
+ //# sourceMappingURL=slate-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-lock.js","sourceRoot":"","sources":["../src/slate-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { SlateWorkspace } from './slate-workspace-types.js';
2
+ /**
3
+ * The one place ids become paths. Nothing read from a workspace record is used
4
+ * as a path: every file is recomputed from ids that passed their patterns, then
5
+ * resolved with `realpath` and required to stay inside the workspace root.
6
+ */
7
+ export declare const inside: (root: string, target: string) => boolean;
8
+ export declare const WORKSPACE_SUBDIRECTORIES: readonly ["ops", "versions", "edits", "inspiration", "calls"];
9
+ export type WorkspaceSubdirectory = typeof WORKSPACE_SUBDIRECTORIES[number];
10
+ /** A real directory inside the root: not a symlink, created 0700 on first use. */
11
+ export declare const ensureRealDirectory: (root: string, name: WorkspaceSubdirectory) => Promise<string>;
12
+ export declare const opLockFile: (root: string, opId: string) => string;
13
+ export declare const opLogFile: (root: string, opId: string) => string;
14
+ export declare const workspaceFile: (root: string) => string;
15
+ export declare const workspaceLockFile: (root: string) => string;
16
+ export declare const ledgerFile: (root: string) => string;
17
+ /** Every image a workspace draws or edits lives at `versions/<versionId>.png`, written once. */
18
+ export declare const versionImageFile: (root: string, versionId: string) => string;
19
+ /** The root run's own option image: `candidates/<A-P>.png` inside the finished draft run. */
20
+ export declare const rootCandidateFile: (root: string, localSlot: string) => string;
21
+ /** The file a version's image lives in, computed only from ids in the record. */
22
+ export declare const versionFilePath: (root: string, workspace: SlateWorkspace, versionId: string) => string;
23
+ /** `edits/<opId>/`: the parent copy and the annotated image an edit was committed with. */
24
+ export declare const editDirectory: (root: string, opId: string) => string;
25
+ /** Create `edits/<opId>/` as a real directory (0700); a link or a file in its place is refused. */
26
+ export declare const ensureEditDirectory: (root: string, opId: string) => Promise<string>;
27
+ export declare const editSourceFile: (root: string, opId: string) => string;
28
+ export declare const editAnnotatedFile: (root: string, opId: string) => string;
29
+ export type ReferenceSize = 'thumb' | 'ref';
30
+ export declare const referenceFile: (root: string, referenceId: string, size: ReferenceSize) => string;
31
+ export type ReadFileResult = {
32
+ state: 'ok';
33
+ bytes: Buffer;
34
+ sha256: string;
35
+ } | {
36
+ state: 'missing';
37
+ } | {
38
+ state: 'outside';
39
+ };
40
+ /**
41
+ * Read one workspace file. A symlink out of the root is refused (`outside`); a
42
+ * missing file is `missing`. The caller compares the sha256 with the record.
43
+ */
44
+ export declare const readWorkspaceFile: (root: string, file: string) => Promise<ReadFileResult>;
45
+ //# sourceMappingURL=slate-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-paths.d.ts","sourceRoot":"","sources":["../src/slate-paths.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;;GAIG;AAEH,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAG,OAIrD,CAAC;AAEF,eAAO,MAAM,wBAAwB,+DAAgE,CAAC;AACtG,MAAM,MAAM,qBAAqB,GAAG,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC;AAE5E,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,GAAU,MAAM,MAAM,EAAE,MAAM,qBAAqB,KAAG,OAAO,CAAC,MAAM,CAUnG,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAGvD,CAAC;AACF,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAGtD,CAAC;AACF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAA2C,CAAC;AACzF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAA2C,CAAC;AAC7F,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAAyC,CAAC;AAEpF,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAGlE,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,WAAW,MAAM,KAAG,MAGnE,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,WAAW,cAAc,EAAE,WAAW,MAAM,KAAG,MAO5F,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAG1D,CAAC;AACF,mGAAmG;AACnG,eAAO,MAAM,mBAAmB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,CAWpF,CAAC;AACF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAA4D,CAAC;AACzH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAA+D,CAAC;AAE/H,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,CAAC;AAC5C,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,aAAa,MAAM,EAAE,MAAM,aAAa,KAAG,MAGtF,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAIzB;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,cAAc,CAyB1F,CAAC"}
@@ -0,0 +1,132 @@
1
+ import { constants } from 'node:fs';
2
+ import { lstat, mkdir, open, realpath } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { SlateError } from './slate-errors.js';
5
+ import { LOCAL_SLOT_PATTERN, OP_ID_PATTERN, REFERENCE_ID_PATTERN, VERSION_ID_PATTERN, sha256Hex } from './slate-ids.js';
6
+ /**
7
+ * The one place ids become paths. Nothing read from a workspace record is used
8
+ * as a path: every file is recomputed from ids that passed their patterns, then
9
+ * resolved with `realpath` and required to stay inside the workspace root.
10
+ */
11
+ export const inside = (root, target) => {
12
+ const relative = path.relative(root, target);
13
+ return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`)
14
+ && !path.isAbsolute(relative);
15
+ };
16
+ export const WORKSPACE_SUBDIRECTORIES = ['ops', 'versions', 'edits', 'inspiration', 'calls'];
17
+ /** A real directory inside the root: not a symlink, created 0700 on first use. */
18
+ export const ensureRealDirectory = async (root, name) => {
19
+ const directory = path.join(root, name);
20
+ await mkdir(directory, { recursive: false, mode: 0o700 }).catch((error) => {
21
+ if (error.code !== 'EEXIST')
22
+ throw error;
23
+ });
24
+ const entry = await lstat(directory);
25
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
26
+ throw new SlateError('state_invalid', `${directory} must be a real directory inside the workspace, not a link or a file`);
27
+ }
28
+ return directory;
29
+ };
30
+ export const opLockFile = (root, opId) => {
31
+ if (!OP_ID_PATTERN.test(opId))
32
+ throw new SlateError('invalid_input', `${JSON.stringify(opId)} is not an op id`);
33
+ return path.join(root, 'ops', `${opId}.lock`);
34
+ };
35
+ export const opLogFile = (root, opId) => {
36
+ if (!OP_ID_PATTERN.test(opId))
37
+ throw new SlateError('invalid_input', `${JSON.stringify(opId)} is not an op id`);
38
+ return path.join(root, 'ops', `${opId}.log`);
39
+ };
40
+ export const workspaceFile = (root) => path.join(root, 'workspace.json');
41
+ export const workspaceLockFile = (root) => path.join(root, 'workspace.lock');
42
+ export const ledgerFile = (root) => path.join(root, 'ledger.jsonl');
43
+ /** Every image a workspace draws or edits lives at `versions/<versionId>.png`, written once. */
44
+ export const versionImageFile = (root, versionId) => {
45
+ if (!VERSION_ID_PATTERN.test(versionId))
46
+ throw new SlateError('invalid_input', `${JSON.stringify(versionId)} is not a version id`);
47
+ return path.join(root, 'versions', `${versionId}.png`);
48
+ };
49
+ /** The root run's own option image: `candidates/<A-P>.png` inside the finished draft run. */
50
+ export const rootCandidateFile = (root, localSlot) => {
51
+ if (!LOCAL_SLOT_PATTERN.test(localSlot))
52
+ throw new SlateError('state_invalid', `${JSON.stringify(localSlot)} is not a local slot`);
53
+ return path.join(root, 'candidates', `${localSlot}.png`);
54
+ };
55
+ /** The file a version's image lives in, computed only from ids in the record. */
56
+ export const versionFilePath = (root, workspace, versionId) => {
57
+ const version = workspace.versions.find((entry) => entry.versionId === versionId);
58
+ if (!version)
59
+ throw new SlateError('not_found', `Unknown version ${versionId}`);
60
+ const candidate = workspace.candidates.find((entry) => entry.candidateId === version.candidateId);
61
+ if (!candidate)
62
+ throw new SlateError('state_invalid', `Version ${versionId} has no option`);
63
+ if (version.kind === 'generation' && candidate.localSlot !== null)
64
+ return rootCandidateFile(root, candidate.localSlot);
65
+ return versionImageFile(root, version.versionId);
66
+ };
67
+ /** `edits/<opId>/`: the parent copy and the annotated image an edit was committed with. */
68
+ export const editDirectory = (root, opId) => {
69
+ if (!OP_ID_PATTERN.test(opId))
70
+ throw new SlateError('invalid_input', `${JSON.stringify(opId)} is not an op id`);
71
+ return path.join(root, 'edits', opId);
72
+ };
73
+ /** Create `edits/<opId>/` as a real directory (0700); a link or a file in its place is refused. */
74
+ export const ensureEditDirectory = async (root, opId) => {
75
+ await ensureRealDirectory(root, 'edits');
76
+ const directory = editDirectory(root, opId);
77
+ await mkdir(directory, { recursive: false, mode: 0o700 }).catch((error) => {
78
+ if (error.code !== 'EEXIST')
79
+ throw error;
80
+ });
81
+ const entry = await lstat(directory);
82
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
83
+ throw new SlateError('state_invalid', `${directory} must be a real directory inside the workspace, not a link or a file`);
84
+ }
85
+ return directory;
86
+ };
87
+ export const editSourceFile = (root, opId) => path.join(editDirectory(root, opId), 'source.png');
88
+ export const editAnnotatedFile = (root, opId) => path.join(editDirectory(root, opId), 'annotated.png');
89
+ export const referenceFile = (root, referenceId, size) => {
90
+ if (!REFERENCE_ID_PATTERN.test(referenceId))
91
+ throw new SlateError('invalid_input', `${JSON.stringify(referenceId)} is not a reference id`);
92
+ return path.join(root, 'inspiration', `${referenceId}.${size}.png`);
93
+ };
94
+ const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0;
95
+ /**
96
+ * Read one workspace file. A symlink out of the root is refused (`outside`); a
97
+ * missing file is `missing`. The caller compares the sha256 with the record.
98
+ */
99
+ export const readWorkspaceFile = async (root, file) => {
100
+ let resolved;
101
+ try {
102
+ resolved = await realpath(file);
103
+ }
104
+ catch (error) {
105
+ if (error.code === 'ENOENT')
106
+ return { state: 'missing' };
107
+ throw error;
108
+ }
109
+ const realRoot = await realpath(root);
110
+ if (!inside(realRoot, resolved))
111
+ return { state: 'outside' };
112
+ let handle;
113
+ try {
114
+ handle = await open(file, constants.O_RDONLY | O_NOFOLLOW);
115
+ }
116
+ catch (error) {
117
+ const code = error.code;
118
+ if (code === 'ENOENT')
119
+ return { state: 'missing' };
120
+ if (code === 'ELOOP')
121
+ return { state: 'outside' };
122
+ throw error;
123
+ }
124
+ try {
125
+ const bytes = await handle.readFile();
126
+ return { state: 'ok', bytes, sha256: sha256Hex(bytes) };
127
+ }
128
+ finally {
129
+ await handle.close();
130
+ }
131
+ };
132
+ //# sourceMappingURL=slate-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-paths.js","sourceRoot":"","sources":["../src/slate-paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { SlateRuntime } from './slate-runtime.js';
2
+ import type { SlateWorkspace } from './slate-workspace-types.js';
3
+ /**
4
+ * Select: free, local, idempotent. A pick copies the chosen image to a durable
5
+ * user-level folder first, so an accepted pick survives the OS sweeping the
6
+ * temp directory the run lives in. The reason is optional everywhere.
7
+ */
8
+ export declare const PICK_REASON_MAX_CHARACTERS = 500;
9
+ export declare const recordPick: (args: {
10
+ runtime: SlateRuntime;
11
+ root: string;
12
+ versionId: unknown;
13
+ reason: unknown;
14
+ }) => Promise<void>;
15
+ /**
16
+ * A legacy `pick.json` is adopted in memory when the workspace is first read
17
+ * (only if its image is still in the run, see `readLegacyPick`). This writes
18
+ * the durable copy that a real selection would have made, so the adopted pick
19
+ * reads as picked, not as missing. Nothing else is written; a workspace that
20
+ * is already recorded is left alone (a recorded pick with a lost copy is
21
+ * reported, never quietly rewritten).
22
+ */
23
+ export declare const copyAdoptedPick: (args: {
24
+ runtime: SlateRuntime;
25
+ root: string;
26
+ workspace: SlateWorkspace;
27
+ persisted: boolean;
28
+ }) => Promise<void>;
29
+ //# sourceMappingURL=slate-pick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-pick.d.ts","sourceRoot":"","sources":["../src/slate-pick.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;;GAIG;AAEH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAY9C,eAAO,MAAM,UAAU,GAAU,MAAM;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,KAAG,OAAO,CAAC,IAAI,CAiBf,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAU,MAAM;IAC1C,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB,KAAG,OAAO,CAAC,IAAI,CAUf,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { SlateError } from './slate-errors.js';
2
+ import { appendLedger } from './slate-ledger.js';
3
+ import { cleanUserText } from './slate-actions.js';
4
+ import { pickImagePath } from './slate-view.js';
5
+ import { readVersionBytes, writeImageOnce } from './slate-versions.js';
6
+ import { NoWrite, findVersion, loadWorkspaceAt, mutateWorkspace } from './slate-workspace-store.js';
7
+ /**
8
+ * Select: free, local, idempotent. A pick copies the chosen image to a durable
9
+ * user-level folder first, so an accepted pick survives the OS sweeping the
10
+ * temp directory the run lives in. The reason is optional everywhere.
11
+ */
12
+ export const PICK_REASON_MAX_CHARACTERS = 500;
13
+ const normalizeReason = (value) => {
14
+ if (value === undefined || value === null)
15
+ return null;
16
+ if (typeof value !== 'string')
17
+ throw new SlateError('invalid_input', 'reason must be text');
18
+ const reason = cleanUserText(value);
19
+ if ([...reason].length > PICK_REASON_MAX_CHARACTERS) {
20
+ throw new SlateError('invalid_input', `A reason can be at most ${PICK_REASON_MAX_CHARACTERS} characters.`);
21
+ }
22
+ return reason || null;
23
+ };
24
+ export const recordPick = async (args) => {
25
+ const reason = normalizeReason(args.reason);
26
+ const { workspace } = await loadWorkspaceAt(args.root, args.runtime.now);
27
+ const version = findVersion(workspace, args.versionId);
28
+ // Explicit errors for a missing or changed file: only a verified image can be picked.
29
+ const bytes = await readVersionBytes(args.root, workspace, version);
30
+ await writeImageOnce(pickImagePath(args.runtime.stateRoot, workspace.workspaceId, version.versionId, version.sha256), bytes, version.sha256);
31
+ await mutateWorkspace(args.root, async (ws, context) => {
32
+ if (ws.pick?.versionId === version.versionId && ws.pick.reason === reason)
33
+ return new NoWrite(undefined);
34
+ ws.pick = { versionId: version.versionId, reason, pickedAt: context.now.toISOString(), sha256: version.sha256 };
35
+ await appendLedger(args.root, { event: 'pick', versionId: version.versionId, at: context.now.toISOString() });
36
+ return undefined;
37
+ }, { now: args.runtime.now });
38
+ };
39
+ /**
40
+ * A legacy `pick.json` is adopted in memory when the workspace is first read
41
+ * (only if its image is still in the run, see `readLegacyPick`). This writes
42
+ * the durable copy that a real selection would have made, so the adopted pick
43
+ * reads as picked, not as missing. Nothing else is written; a workspace that
44
+ * is already recorded is left alone (a recorded pick with a lost copy is
45
+ * reported, never quietly rewritten).
46
+ */
47
+ export const copyAdoptedPick = async (args) => {
48
+ const { runtime, root, workspace, persisted } = args;
49
+ if (persisted || !workspace.pick)
50
+ return;
51
+ const version = findVersion(workspace, workspace.pick.versionId);
52
+ const bytes = await readVersionBytes(root, workspace, version);
53
+ await writeImageOnce(pickImagePath(runtime.stateRoot, workspace.workspaceId, version.versionId, version.sha256), bytes, version.sha256);
54
+ };
55
+ //# sourceMappingURL=slate-pick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-pick.js","sourceRoot":"","sources":["../src/slate-pick.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { SlateWorkspace } from './slate-workspace-types.js';
2
+ /** Every reference id the record names, in the order a person meets them: rounds, options, searches, selection. */
3
+ export declare const namedReferenceIds: (workspace: SlateWorkspace) => string[];
4
+ /** The metadata cache keeps exactly the ids the record names, so it cannot grow without bound. */
5
+ export declare const pruneReferences: (workspace: SlateWorkspace) => void;
6
+ //# sourceMappingURL=slate-reference-ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-reference-ids.d.ts","sourceRoot":"","sources":["../src/slate-reference-ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAErF,mHAAmH;AACnH,eAAO,MAAM,iBAAiB,GAAI,WAAW,cAAc,KAAG,MAAM,EAKjE,CAAC;AAEJ,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAAI,WAAW,cAAc,KAAG,IAK3D,CAAC"}
@@ -0,0 +1,17 @@
1
+ /** Every reference id the record names, in the order a person meets them: rounds, options, searches, selection. */
2
+ export const namedReferenceIds = (workspace) => [...new Set([
3
+ ...workspace.rounds.flatMap((round) => round.referenceIds),
4
+ ...workspace.candidates.flatMap((candidate) => (candidate.referenceId ? [candidate.referenceId] : [])),
5
+ ...workspace.inspiration.searches.flatMap((search) => search.referenceIds),
6
+ ...workspace.inspiration.selected,
7
+ ])];
8
+ /** The metadata cache keeps exactly the ids the record names, so it cannot grow without bound. */
9
+ export const pruneReferences = (workspace) => {
10
+ const named = new Set(namedReferenceIds(workspace));
11
+ const kept = {};
12
+ for (const [id, meta] of Object.entries(workspace.references))
13
+ if (named.has(id))
14
+ kept[id] = meta;
15
+ workspace.references = kept;
16
+ };
17
+ //# sourceMappingURL=slate-reference-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-reference-ids.js","sourceRoot":"","sources":["../src/slate-reference-ids.ts"],"names":[],"mappings":"AAEA,mHAAmH;AACnH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAyB,EAAY,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;QACpF,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;QAC1D,GAAG,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,GAAG,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC;QAC1E,GAAG,SAAS,CAAC,WAAW,CAAC,QAAQ;KAClC,CAAC,CAAC,CAAC;AAEJ,kGAAkG;AAClG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,SAAyB,EAAQ,EAAE;IACjE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAuC,EAAE,CAAC;IACpD,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC;QAAE,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IAClG,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC;AAC9B,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type SlateCorpus } from './slate-corpus.js';
2
+ import { type ReferenceSize } from './slate-paths.js';
3
+ /**
4
+ * Reference images cached inside the workspace, `inspiration/<id>.<size>.png`.
5
+ * One place downloads them: the corpus is asked only on a miss, the bytes are
6
+ * decoded before they are cached (a corrupt download is never kept), and the
7
+ * file is written temp-then-rename so a reader never sees half of one. The
8
+ * reader confines the file to the workspace root, like every other read.
9
+ */
10
+ export type ReferenceImage = {
11
+ bytes: Buffer;
12
+ width: number;
13
+ height: number;
14
+ cached: boolean;
15
+ };
16
+ export declare const ensureReferenceImage: (args: {
17
+ root: string;
18
+ corpus: () => SlateCorpus;
19
+ referenceId: string;
20
+ size: ReferenceSize;
21
+ signal?: AbortSignal;
22
+ }) => Promise<ReferenceImage>;
23
+ //# sourceMappingURL=slate-reference-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slate-reference-store.d.ts","sourceRoot":"","sources":["../src/slate-reference-store.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAoC,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAExF;;;;;;GAMG;AAEH,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAQ/F,eAAO,MAAM,oBAAoB,GAAU,MAAM;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,WAAW,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,cAAc,CA2BzB,CAAC"}