@imgly/codesign-mcp 0.1.6 → 0.1.8

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 (448) hide show
  1. package/README.md +5 -10
  2. package/assets/docs/api/index.d.ts +9 -3
  3. package/assets/docs/guide/SKILL.md +2 -2
  4. package/assets/docs/guide/api/node/classes/creativeengine.md +13 -13
  5. package/assets/docs/guide/edit-image/transform/scale.md +1 -3
  6. package/assets/docs/guide/filters-and-effects/duotone.md +1 -1
  7. package/assets/docs/guide/get-started/bun.md +0 -2
  8. package/assets/docs/guide/get-started/deno.md +0 -1
  9. package/assets/docs/guide/get-started/vanilla.md +0 -2
  10. package/assets/docs/guide/import-media/default-assets.md +20 -24
  11. package/assets/docs/guide/plugins/print-ready-pdf.md +0 -2
  12. package/assets/docs/guide/serve-assets.md +80 -23
  13. package/assets/docs/handbook/SKILL.md +6 -7
  14. package/dist/assets/inline.generated.js +12 -12
  15. package/dist/assets/inline.generated.js.map +1 -1
  16. package/dist/cli/http.d.ts.map +1 -1
  17. package/dist/cli/http.js +29 -5
  18. package/dist/cli/http.js.map +1 -1
  19. package/dist/cli/stdio.d.ts.map +1 -1
  20. package/dist/cli/stdio.js +145 -48
  21. package/dist/cli/stdio.js.map +1 -1
  22. package/dist/cli/trial-license.generated.d.ts +1 -1
  23. package/dist/cli/trial-license.generated.js +1 -1
  24. package/dist/cli/trial-license.generated.js.map +1 -1
  25. package/dist/config.d.ts +17 -2
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +20 -1
  28. package/dist/config.js.map +1 -1
  29. package/dist/diagnostics/bundle.d.ts +3 -0
  30. package/dist/diagnostics/bundle.d.ts.map +1 -1
  31. package/dist/diagnostics/bundle.js +25 -22
  32. package/dist/diagnostics/bundle.js.map +1 -1
  33. package/dist/diagnostics/events.d.ts +6 -0
  34. package/dist/diagnostics/events.d.ts.map +1 -1
  35. package/dist/diagnostics/events.js.map +1 -1
  36. package/dist/diagnostics/prompt.d.ts +3 -0
  37. package/dist/diagnostics/prompt.d.ts.map +1 -0
  38. package/dist/diagnostics/prompt.js +21 -0
  39. package/dist/diagnostics/prompt.js.map +1 -0
  40. package/dist/diagnostics/router.d.ts +12 -0
  41. package/dist/diagnostics/router.d.ts.map +1 -0
  42. package/dist/diagnostics/router.js +64 -0
  43. package/dist/diagnostics/router.js.map +1 -0
  44. package/dist/diagnostics/spa-assets.generated.d.ts.map +1 -0
  45. package/dist/diagnostics/spa-assets.generated.js +1231 -0
  46. package/dist/diagnostics/spa-assets.generated.js.map +1 -0
  47. package/dist/diagnostics/status.d.ts +38 -0
  48. package/dist/diagnostics/status.d.ts.map +1 -0
  49. package/dist/diagnostics/status.js +53 -0
  50. package/dist/diagnostics/status.js.map +1 -0
  51. package/dist/diagnostics/summary.d.ts +48 -0
  52. package/dist/diagnostics/summary.d.ts.map +1 -0
  53. package/dist/diagnostics/summary.js +116 -0
  54. package/dist/diagnostics/summary.js.map +1 -0
  55. package/dist/diagnostics/tool.d.ts +16 -0
  56. package/dist/diagnostics/tool.d.ts.map +1 -0
  57. package/dist/diagnostics/tool.js +90 -0
  58. package/dist/diagnostics/tool.js.map +1 -0
  59. package/dist/engine/asset-sources.d.ts +8 -0
  60. package/dist/engine/asset-sources.d.ts.map +1 -1
  61. package/dist/engine/asset-sources.js +9 -2
  62. package/dist/engine/asset-sources.js.map +1 -1
  63. package/dist/engine/cesdk-adapter.d.ts +5 -1
  64. package/dist/engine/cesdk-adapter.d.ts.map +1 -1
  65. package/dist/engine/cesdk-adapter.js +8 -1
  66. package/dist/engine/cesdk-adapter.js.map +1 -1
  67. package/dist/engine/engine-capacity.d.ts +16 -0
  68. package/dist/engine/engine-capacity.d.ts.map +1 -0
  69. package/dist/engine/engine-capacity.js +48 -0
  70. package/dist/engine/engine-capacity.js.map +1 -0
  71. package/dist/engine/engine-slots.d.ts +48 -0
  72. package/dist/engine/engine-slots.d.ts.map +1 -0
  73. package/dist/engine/engine-slots.js +182 -0
  74. package/dist/engine/engine-slots.js.map +1 -0
  75. package/dist/engine/uri-resolver.js.map +1 -1
  76. package/dist/server.d.ts +25 -0
  77. package/dist/server.d.ts.map +1 -1
  78. package/dist/server.js +24 -4
  79. package/dist/server.js.map +1 -1
  80. package/dist/tools/engine.d.ts +92 -14
  81. package/dist/tools/engine.d.ts.map +1 -1
  82. package/dist/tools/engine.js +147 -20
  83. package/dist/tools/engine.js.map +1 -1
  84. package/dist/tools/export.d.ts.map +1 -1
  85. package/dist/tools/export.js +34 -4
  86. package/dist/tools/export.js.map +1 -1
  87. package/dist/tools/index.js +2 -2
  88. package/dist/tools/index.js.map +1 -1
  89. package/dist/tools/load.d.ts.map +1 -1
  90. package/dist/tools/load.js +26 -19
  91. package/dist/tools/load.js.map +1 -1
  92. package/dist/tools/preview.d.ts.map +1 -1
  93. package/dist/tools/preview.js +60 -40
  94. package/dist/tools/preview.js.map +1 -1
  95. package/dist/tools/{open-editor.d.ts → view.d.ts} +3 -3
  96. package/dist/tools/view.d.ts.map +1 -0
  97. package/dist/tools/{open-editor.js → view.js} +20 -18
  98. package/dist/tools/view.js.map +1 -0
  99. package/dist/viewer/listener.d.ts +2 -0
  100. package/dist/viewer/listener.d.ts.map +1 -1
  101. package/dist/viewer/listener.js +2 -0
  102. package/dist/viewer/listener.js.map +1 -1
  103. package/dist/viewer/presigned-r2-url-builder.d.ts +1 -1
  104. package/dist/viewer/presigned-r2-url-builder.js +1 -1
  105. package/dist/viewer/presigned-r2-url-builder.js.map +1 -1
  106. package/dist/viewer/router.d.ts.map +1 -1
  107. package/dist/viewer/router.js +8 -17
  108. package/dist/viewer/router.js.map +1 -1
  109. package/dist/viewer/session-mount.js.map +1 -1
  110. package/dist/viewer/spa-assets.generated.d.ts.map +1 -1
  111. package/dist/viewer/spa-assets.generated.js +750 -869
  112. package/dist/viewer/spa-assets.generated.js.map +1 -1
  113. package/dist/viewer/url-builder.d.ts +4 -3
  114. package/dist/viewer/url-builder.d.ts.map +1 -1
  115. package/dist/viewer/url-builder.js +4 -4
  116. package/dist/viewer/url-builder.js.map +1 -1
  117. package/package.json +7 -6
  118. package/dist/dashboard/router.d.ts +0 -3
  119. package/dist/dashboard/router.d.ts.map +0 -1
  120. package/dist/dashboard/router.js +0 -19
  121. package/dist/dashboard/router.js.map +0 -1
  122. package/dist/dashboard/spa-assets.generated.d.ts.map +0 -1
  123. package/dist/dashboard/spa-assets.generated.js +0 -220
  124. package/dist/dashboard/spa-assets.generated.js.map +0 -1
  125. package/dist/metrics/mount.d.ts +0 -11
  126. package/dist/metrics/mount.d.ts.map +0 -1
  127. package/dist/metrics/mount.js +0 -13
  128. package/dist/metrics/mount.js.map +0 -1
  129. package/dist/metrics/recorder.d.ts +0 -55
  130. package/dist/metrics/recorder.d.ts.map +0 -1
  131. package/dist/metrics/recorder.js +0 -97
  132. package/dist/metrics/recorder.js.map +0 -1
  133. package/dist/test/scripts/changelog-add.d.ts +0 -8
  134. package/dist/test/scripts/changelog-add.d.ts.map +0 -1
  135. package/dist/test/scripts/changelog-add.js +0 -50
  136. package/dist/test/scripts/changelog-add.js.map +0 -1
  137. package/dist/test/scripts/changelog-collate.d.ts +0 -38
  138. package/dist/test/scripts/changelog-collate.d.ts.map +0 -1
  139. package/dist/test/scripts/changelog-collate.js +0 -171
  140. package/dist/test/scripts/changelog-collate.js.map +0 -1
  141. package/dist/test/test/changelog.test.d.ts +0 -2
  142. package/dist/test/test/changelog.test.d.ts.map +0 -1
  143. package/dist/test/test/changelog.test.js +0 -293
  144. package/dist/test/test/changelog.test.js.map +0 -1
  145. package/dist/test/test/cli/dispatcher.test.d.ts +0 -2
  146. package/dist/test/test/cli/dispatcher.test.d.ts.map +0 -1
  147. package/dist/test/test/cli/dispatcher.test.js +0 -54
  148. package/dist/test/test/cli/dispatcher.test.js.map +0 -1
  149. package/dist/test/test/cli/redirect-console.test.d.ts +0 -2
  150. package/dist/test/test/cli/redirect-console.test.d.ts.map +0 -1
  151. package/dist/test/test/cli/redirect-console.test.js +0 -78
  152. package/dist/test/test/cli/redirect-console.test.js.map +0 -1
  153. package/dist/test/test/cli/resolve-license.test.d.ts +0 -2
  154. package/dist/test/test/cli/resolve-license.test.d.ts.map +0 -1
  155. package/dist/test/test/cli/resolve-license.test.js +0 -29
  156. package/dist/test/test/cli/resolve-license.test.js.map +0 -1
  157. package/dist/test/test/cli/stdio-diagnostics.test.d.ts +0 -2
  158. package/dist/test/test/cli/stdio-diagnostics.test.d.ts.map +0 -1
  159. package/dist/test/test/cli/stdio-diagnostics.test.js +0 -31
  160. package/dist/test/test/cli/stdio-diagnostics.test.js.map +0 -1
  161. package/dist/test/test/cli/stdio-license.test.d.ts +0 -2
  162. package/dist/test/test/cli/stdio-license.test.d.ts.map +0 -1
  163. package/dist/test/test/cli/stdio-license.test.js +0 -19
  164. package/dist/test/test/cli/stdio-license.test.js.map +0 -1
  165. package/dist/test/test/config.test.d.ts +0 -2
  166. package/dist/test/test/config.test.d.ts.map +0 -1
  167. package/dist/test/test/config.test.js +0 -95
  168. package/dist/test/test/config.test.js.map +0 -1
  169. package/dist/test/test/cross-ref.test.d.ts +0 -2
  170. package/dist/test/test/cross-ref.test.d.ts.map +0 -1
  171. package/dist/test/test/cross-ref.test.js +0 -39
  172. package/dist/test/test/cross-ref.test.js.map +0 -1
  173. package/dist/test/test/diagnostics/bundle.test.d.ts +0 -2
  174. package/dist/test/test/diagnostics/bundle.test.d.ts.map +0 -1
  175. package/dist/test/test/diagnostics/bundle.test.js +0 -53
  176. package/dist/test/test/diagnostics/bundle.test.js.map +0 -1
  177. package/dist/test/test/diagnostics/compose.test.d.ts +0 -2
  178. package/dist/test/test/diagnostics/compose.test.d.ts.map +0 -1
  179. package/dist/test/test/diagnostics/compose.test.js +0 -31
  180. package/dist/test/test/diagnostics/compose.test.js.map +0 -1
  181. package/dist/test/test/diagnostics/file-sink.test.d.ts +0 -2
  182. package/dist/test/test/diagnostics/file-sink.test.d.ts.map +0 -1
  183. package/dist/test/test/diagnostics/file-sink.test.js +0 -113
  184. package/dist/test/test/diagnostics/file-sink.test.js.map +0 -1
  185. package/dist/test/test/diagnostics/redact.test.d.ts +0 -2
  186. package/dist/test/test/diagnostics/redact.test.d.ts.map +0 -1
  187. package/dist/test/test/diagnostics/redact.test.js +0 -59
  188. package/dist/test/test/diagnostics/redact.test.js.map +0 -1
  189. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts +0 -2
  190. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts.map +0 -1
  191. package/dist/test/test/diagnostics/tool-call-adapter.test.js +0 -34
  192. package/dist/test/test/diagnostics/tool-call-adapter.test.js.map +0 -1
  193. package/dist/test/test/engine/asset-sources.test.d.ts +0 -2
  194. package/dist/test/test/engine/asset-sources.test.d.ts.map +0 -1
  195. package/dist/test/test/engine/asset-sources.test.js +0 -100
  196. package/dist/test/test/engine/asset-sources.test.js.map +0 -1
  197. package/dist/test/test/engine/cesdk-adapter.test.d.ts +0 -2
  198. package/dist/test/test/engine/cesdk-adapter.test.d.ts.map +0 -1
  199. package/dist/test/test/engine/cesdk-adapter.test.js +0 -32
  200. package/dist/test/test/engine/cesdk-adapter.test.js.map +0 -1
  201. package/dist/test/test/engine/mutex.test.d.ts +0 -2
  202. package/dist/test/test/engine/mutex.test.d.ts.map +0 -1
  203. package/dist/test/test/engine/mutex.test.js +0 -63
  204. package/dist/test/test/engine/mutex.test.js.map +0 -1
  205. package/dist/test/test/engine/session.test.d.ts +0 -2
  206. package/dist/test/test/engine/session.test.d.ts.map +0 -1
  207. package/dist/test/test/engine/session.test.js +0 -159
  208. package/dist/test/test/engine/session.test.js.map +0 -1
  209. package/dist/test/test/engine/unsplash-source.test.d.ts +0 -2
  210. package/dist/test/test/engine/unsplash-source.test.d.ts.map +0 -1
  211. package/dist/test/test/engine/unsplash-source.test.js +0 -129
  212. package/dist/test/test/engine/unsplash-source.test.js.map +0 -1
  213. package/dist/test/test/engine/uri-resolver.test.d.ts +0 -2
  214. package/dist/test/test/engine/uri-resolver.test.d.ts.map +0 -1
  215. package/dist/test/test/engine/uri-resolver.test.js +0 -38
  216. package/dist/test/test/engine/uri-resolver.test.js.map +0 -1
  217. package/dist/test/test/gateway/hosted-stub.test.d.ts +0 -2
  218. package/dist/test/test/gateway/hosted-stub.test.d.ts.map +0 -1
  219. package/dist/test/test/gateway/hosted-stub.test.js +0 -11
  220. package/dist/test/test/gateway/hosted-stub.test.js.map +0 -1
  221. package/dist/test/test/gateway/local.test.d.ts +0 -2
  222. package/dist/test/test/gateway/local.test.d.ts.map +0 -1
  223. package/dist/test/test/gateway/local.test.js +0 -29
  224. package/dist/test/test/gateway/local.test.js.map +0 -1
  225. package/dist/test/test/handbook-contract.test.d.ts +0 -2
  226. package/dist/test/test/handbook-contract.test.d.ts.map +0 -1
  227. package/dist/test/test/handbook-contract.test.js +0 -79
  228. package/dist/test/test/handbook-contract.test.js.map +0 -1
  229. package/dist/test/test/http/server.test.d.ts +0 -2
  230. package/dist/test/test/http/server.test.d.ts.map +0 -1
  231. package/dist/test/test/http/server.test.js +0 -338
  232. package/dist/test/test/http/server.test.js.map +0 -1
  233. package/dist/test/test/http/session-id-header.test.d.ts +0 -2
  234. package/dist/test/test/http/session-id-header.test.d.ts.map +0 -1
  235. package/dist/test/test/http/session-id-header.test.js +0 -76
  236. package/dist/test/test/http/session-id-header.test.js.map +0 -1
  237. package/dist/test/test/http/session-manager.test.d.ts +0 -2
  238. package/dist/test/test/http/session-manager.test.d.ts.map +0 -1
  239. package/dist/test/test/http/session-manager.test.js +0 -97
  240. package/dist/test/test/http/session-manager.test.js.map +0 -1
  241. package/dist/test/test/http/workspace-auth.test.d.ts +0 -2
  242. package/dist/test/test/http/workspace-auth.test.d.ts.map +0 -1
  243. package/dist/test/test/http/workspace-auth.test.js +0 -40
  244. package/dist/test/test/http/workspace-auth.test.js.map +0 -1
  245. package/dist/test/test/metrics/mount.test.d.ts +0 -2
  246. package/dist/test/test/metrics/mount.test.d.ts.map +0 -1
  247. package/dist/test/test/metrics/mount.test.js +0 -59
  248. package/dist/test/test/metrics/mount.test.js.map +0 -1
  249. package/dist/test/test/metrics/recorder.test.d.ts +0 -2
  250. package/dist/test/test/metrics/recorder.test.d.ts.map +0 -1
  251. package/dist/test/test/metrics/recorder.test.js +0 -118
  252. package/dist/test/test/metrics/recorder.test.js.map +0 -1
  253. package/dist/test/test/revisions/hash.test.d.ts +0 -2
  254. package/dist/test/test/revisions/hash.test.d.ts.map +0 -1
  255. package/dist/test/test/revisions/hash.test.js +0 -33
  256. package/dist/test/test/revisions/hash.test.js.map +0 -1
  257. package/dist/test/test/revisions/store.test.d.ts +0 -2
  258. package/dist/test/test/revisions/store.test.d.ts.map +0 -1
  259. package/dist/test/test/revisions/store.test.js +0 -315
  260. package/dist/test/test/revisions/store.test.js.map +0 -1
  261. package/dist/test/test/server.metrics.test.d.ts +0 -2
  262. package/dist/test/test/server.metrics.test.d.ts.map +0 -1
  263. package/dist/test/test/server.metrics.test.js +0 -87
  264. package/dist/test/test/server.metrics.test.js.map +0 -1
  265. package/dist/test/test/server.test.d.ts +0 -2
  266. package/dist/test/test/server.test.d.ts.map +0 -1
  267. package/dist/test/test/server.test.js +0 -146
  268. package/dist/test/test/server.test.js.map +0 -1
  269. package/dist/test/test/skill-tools.test.d.ts +0 -2
  270. package/dist/test/test/skill-tools.test.d.ts.map +0 -1
  271. package/dist/test/test/skill-tools.test.js +0 -84
  272. package/dist/test/test/skill-tools.test.js.map +0 -1
  273. package/dist/test/test/smoke.test.d.ts +0 -2
  274. package/dist/test/test/smoke.test.d.ts.map +0 -1
  275. package/dist/test/test/smoke.test.js +0 -8
  276. package/dist/test/test/smoke.test.js.map +0 -1
  277. package/dist/test/test/tools/asset-generate.test.d.ts +0 -2
  278. package/dist/test/test/tools/asset-generate.test.d.ts.map +0 -1
  279. package/dist/test/test/tools/asset-generate.test.js +0 -66
  280. package/dist/test/test/tools/asset-generate.test.js.map +0 -1
  281. package/dist/test/test/tools/asset-search.test.d.ts +0 -2
  282. package/dist/test/test/tools/asset-search.test.d.ts.map +0 -1
  283. package/dist/test/test/tools/asset-search.test.js +0 -121
  284. package/dist/test/test/tools/asset-search.test.js.map +0 -1
  285. package/dist/test/test/tools/edit-harness.d.ts +0 -14
  286. package/dist/test/test/tools/edit-harness.d.ts.map +0 -1
  287. package/dist/test/test/tools/edit-harness.js +0 -30
  288. package/dist/test/test/tools/edit-harness.js.map +0 -1
  289. package/dist/test/test/tools/edit.telemetry.test.d.ts +0 -2
  290. package/dist/test/test/tools/edit.telemetry.test.d.ts.map +0 -1
  291. package/dist/test/test/tools/edit.telemetry.test.js +0 -46
  292. package/dist/test/test/tools/edit.telemetry.test.js.map +0 -1
  293. package/dist/test/test/tools/edit.test.d.ts +0 -2
  294. package/dist/test/test/tools/edit.test.d.ts.map +0 -1
  295. package/dist/test/test/tools/edit.test.js +0 -267
  296. package/dist/test/test/tools/edit.test.js.map +0 -1
  297. package/dist/test/test/tools/export-harness.d.ts +0 -13
  298. package/dist/test/test/tools/export-harness.d.ts.map +0 -1
  299. package/dist/test/test/tools/export-harness.js +0 -27
  300. package/dist/test/test/tools/export-harness.js.map +0 -1
  301. package/dist/test/test/tools/export.telemetry.test.d.ts +0 -2
  302. package/dist/test/test/tools/export.telemetry.test.d.ts.map +0 -1
  303. package/dist/test/test/tools/export.telemetry.test.js +0 -65
  304. package/dist/test/test/tools/export.telemetry.test.js.map +0 -1
  305. package/dist/test/test/tools/export.test.d.ts +0 -2
  306. package/dist/test/test/tools/export.test.d.ts.map +0 -1
  307. package/dist/test/test/tools/export.test.js +0 -174
  308. package/dist/test/test/tools/export.test.js.map +0 -1
  309. package/dist/test/test/tools/history.test.d.ts +0 -2
  310. package/dist/test/test/tools/history.test.d.ts.map +0 -1
  311. package/dist/test/test/tools/history.test.js +0 -29
  312. package/dist/test/test/tools/history.test.js.map +0 -1
  313. package/dist/test/test/tools/import.test.d.ts +0 -2
  314. package/dist/test/test/tools/import.test.d.ts.map +0 -1
  315. package/dist/test/test/tools/import.test.js +0 -122
  316. package/dist/test/test/tools/import.test.js.map +0 -1
  317. package/dist/test/test/tools/index.test.d.ts +0 -2
  318. package/dist/test/test/tools/index.test.d.ts.map +0 -1
  319. package/dist/test/test/tools/index.test.js +0 -117
  320. package/dist/test/test/tools/index.test.js.map +0 -1
  321. package/dist/test/test/tools/inspect.test.d.ts +0 -2
  322. package/dist/test/test/tools/inspect.test.d.ts.map +0 -1
  323. package/dist/test/test/tools/inspect.test.js +0 -62
  324. package/dist/test/test/tools/inspect.test.js.map +0 -1
  325. package/dist/test/test/tools/list.test.d.ts +0 -2
  326. package/dist/test/test/tools/list.test.d.ts.map +0 -1
  327. package/dist/test/test/tools/list.test.js +0 -23
  328. package/dist/test/test/tools/list.test.js.map +0 -1
  329. package/dist/test/test/tools/load.integration.test.d.ts +0 -2
  330. package/dist/test/test/tools/load.integration.test.d.ts.map +0 -1
  331. package/dist/test/test/tools/load.integration.test.js +0 -165
  332. package/dist/test/test/tools/load.integration.test.js.map +0 -1
  333. package/dist/test/test/tools/load.test.d.ts +0 -2
  334. package/dist/test/test/tools/load.test.d.ts.map +0 -1
  335. package/dist/test/test/tools/load.test.js +0 -387
  336. package/dist/test/test/tools/load.test.js.map +0 -1
  337. package/dist/test/test/tools/metrics.test.d.ts +0 -2
  338. package/dist/test/test/tools/metrics.test.d.ts.map +0 -1
  339. package/dist/test/test/tools/metrics.test.js +0 -29
  340. package/dist/test/test/tools/metrics.test.js.map +0 -1
  341. package/dist/test/test/tools/open-editor.test.d.ts +0 -2
  342. package/dist/test/test/tools/open-editor.test.d.ts.map +0 -1
  343. package/dist/test/test/tools/open-editor.test.js +0 -53
  344. package/dist/test/test/tools/open-editor.test.js.map +0 -1
  345. package/dist/test/test/tools/preview-harness.d.ts +0 -9
  346. package/dist/test/test/tools/preview-harness.d.ts.map +0 -1
  347. package/dist/test/test/tools/preview-harness.js +0 -23
  348. package/dist/test/test/tools/preview-harness.js.map +0 -1
  349. package/dist/test/test/tools/preview.telemetry.test.d.ts +0 -2
  350. package/dist/test/test/tools/preview.telemetry.test.d.ts.map +0 -1
  351. package/dist/test/test/tools/preview.telemetry.test.js +0 -36
  352. package/dist/test/test/tools/preview.telemetry.test.js.map +0 -1
  353. package/dist/test/test/tools/preview.test.d.ts +0 -2
  354. package/dist/test/test/tools/preview.test.d.ts.map +0 -1
  355. package/dist/test/test/tools/preview.test.js +0 -75
  356. package/dist/test/test/tools/preview.test.js.map +0 -1
  357. package/dist/test/test/tools/rename.test.d.ts +0 -2
  358. package/dist/test/test/tools/rename.test.d.ts.map +0 -1
  359. package/dist/test/test/tools/rename.test.js +0 -16
  360. package/dist/test/test/tools/rename.test.js.map +0 -1
  361. package/dist/test/test/tools/save.test.d.ts +0 -2
  362. package/dist/test/test/tools/save.test.d.ts.map +0 -1
  363. package/dist/test/test/tools/save.test.js +0 -137
  364. package/dist/test/test/tools/save.test.js.map +0 -1
  365. package/dist/test/test/tools/schema-best-practices.d.ts +0 -13
  366. package/dist/test/test/tools/schema-best-practices.d.ts.map +0 -1
  367. package/dist/test/test/tools/schema-best-practices.js +0 -169
  368. package/dist/test/test/tools/schema-best-practices.js.map +0 -1
  369. package/dist/test/test/tools/schema-best-practices.test.d.ts +0 -3
  370. package/dist/test/test/tools/schema-best-practices.test.d.ts.map +0 -1
  371. package/dist/test/test/tools/schema-best-practices.test.js +0 -194
  372. package/dist/test/test/tools/schema-best-practices.test.js.map +0 -1
  373. package/dist/test/test/tools/spans.test.d.ts +0 -2
  374. package/dist/test/test/tools/spans.test.d.ts.map +0 -1
  375. package/dist/test/test/tools/spans.test.js +0 -84
  376. package/dist/test/test/tools/spans.test.js.map +0 -1
  377. package/dist/test/test/viewer/broadcaster.test.d.ts +0 -2
  378. package/dist/test/test/viewer/broadcaster.test.d.ts.map +0 -1
  379. package/dist/test/test/viewer/broadcaster.test.js +0 -53
  380. package/dist/test/test/viewer/broadcaster.test.js.map +0 -1
  381. package/dist/test/test/viewer/cesdk-cdn.test.d.ts +0 -2
  382. package/dist/test/test/viewer/cesdk-cdn.test.d.ts.map +0 -1
  383. package/dist/test/test/viewer/cesdk-cdn.test.js +0 -19
  384. package/dist/test/test/viewer/cesdk-cdn.test.js.map +0 -1
  385. package/dist/test/test/viewer/config-route.test.d.ts +0 -2
  386. package/dist/test/test/viewer/config-route.test.d.ts.map +0 -1
  387. package/dist/test/test/viewer/config-route.test.js +0 -28
  388. package/dist/test/test/viewer/config-route.test.js.map +0 -1
  389. package/dist/test/test/viewer/resource-route.test.d.ts +0 -2
  390. package/dist/test/test/viewer/resource-route.test.d.ts.map +0 -1
  391. package/dist/test/test/viewer/resource-route.test.js +0 -70
  392. package/dist/test/test/viewer/resource-route.test.js.map +0 -1
  393. package/dist/test/test/viewer/router.test.d.ts +0 -2
  394. package/dist/test/test/viewer/router.test.d.ts.map +0 -1
  395. package/dist/test/test/viewer/router.test.js +0 -227
  396. package/dist/test/test/viewer/router.test.js.map +0 -1
  397. package/dist/test/test/viewer/session-mount.test.d.ts +0 -2
  398. package/dist/test/test/viewer/session-mount.test.d.ts.map +0 -1
  399. package/dist/test/test/viewer/session-mount.test.js +0 -52
  400. package/dist/test/test/viewer/session-mount.test.js.map +0 -1
  401. package/dist/test/test/viewer/spa-version.test.d.ts +0 -2
  402. package/dist/test/test/viewer/spa-version.test.d.ts.map +0 -1
  403. package/dist/test/test/viewer/spa-version.test.js +0 -14
  404. package/dist/test/test/viewer/spa-version.test.js.map +0 -1
  405. package/dist/test/test/viewer/url-builder.test.d.ts +0 -2
  406. package/dist/test/test/viewer/url-builder.test.d.ts.map +0 -1
  407. package/dist/test/test/viewer/url-builder.test.js +0 -98
  408. package/dist/test/test/viewer/url-builder.test.js.map +0 -1
  409. package/dist/test/test/workspace/content-hash.test.d.ts +0 -2
  410. package/dist/test/test/workspace/content-hash.test.d.ts.map +0 -1
  411. package/dist/test/test/workspace/content-hash.test.js +0 -17
  412. package/dist/test/test/workspace/content-hash.test.js.map +0 -1
  413. package/dist/test/test/workspace/hosted-stub.test.d.ts +0 -2
  414. package/dist/test/test/workspace/hosted-stub.test.d.ts.map +0 -1
  415. package/dist/test/test/workspace/hosted-stub.test.js +0 -30
  416. package/dist/test/test/workspace/hosted-stub.test.js.map +0 -1
  417. package/dist/test/test/workspace/in-memory.test.d.ts +0 -2
  418. package/dist/test/test/workspace/in-memory.test.d.ts.map +0 -1
  419. package/dist/test/test/workspace/in-memory.test.js +0 -4
  420. package/dist/test/test/workspace/in-memory.test.js.map +0 -1
  421. package/dist/test/test/workspace/node-fs.test.d.ts +0 -2
  422. package/dist/test/test/workspace/node-fs.test.d.ts.map +0 -1
  423. package/dist/test/test/workspace/node-fs.test.js +0 -20
  424. package/dist/test/test/workspace/node-fs.test.js.map +0 -1
  425. package/dist/test/test/workspace/s3.test.d.ts +0 -2
  426. package/dist/test/test/workspace/s3.test.d.ts.map +0 -1
  427. package/dist/test/test/workspace/s3.test.js +0 -153
  428. package/dist/test/test/workspace/s3.test.js.map +0 -1
  429. package/dist/test/test/workspace/scoped.test.d.ts +0 -2
  430. package/dist/test/test/workspace/scoped.test.d.ts.map +0 -1
  431. package/dist/test/test/workspace/scoped.test.js +0 -59
  432. package/dist/test/test/workspace/scoped.test.js.map +0 -1
  433. package/dist/test/test/workspace/shared-contract.d.ts +0 -3
  434. package/dist/test/test/workspace/shared-contract.d.ts.map +0 -1
  435. package/dist/test/test/workspace/shared-contract.js +0 -92
  436. package/dist/test/test/workspace/shared-contract.js.map +0 -1
  437. package/dist/test/tsconfig.test.tsbuildinfo +0 -1
  438. package/dist/test/vitest.config.d.ts +0 -3
  439. package/dist/test/vitest.config.d.ts.map +0 -1
  440. package/dist/test/vitest.config.js +0 -8
  441. package/dist/test/vitest.config.js.map +0 -1
  442. package/dist/tools/metrics.d.ts +0 -11
  443. package/dist/tools/metrics.d.ts.map +0 -1
  444. package/dist/tools/metrics.js +0 -28
  445. package/dist/tools/metrics.js.map +0 -1
  446. package/dist/tools/open-editor.d.ts.map +0 -1
  447. package/dist/tools/open-editor.js.map +0 -1
  448. /package/dist/{dashboard → diagnostics}/spa-assets.generated.d.ts +0 -0
@@ -15,6 +15,10 @@ import type { EngineAdapter } from './session.js';
15
15
  * calls `create()` on the first slot (first edit), awaiting it then — by which
16
16
  * point init is usually already settled. The other methods receive the resolved
17
17
  * engine from EngineSession, so they never touch the promise.
18
+ *
19
+ * `engine` may also be a thunk `() => Promise<CreativeEngine>`: the stdio
20
+ * transport passes a lazy, slot-gated boot so no engine initializes until the
21
+ * first edit calls create().
18
22
  */
19
- export declare function cesdkAdapter(engine: CreativeEngine | Promise<CreativeEngine>): EngineAdapter<CreativeEngine>;
23
+ export declare function cesdkAdapter(engine: CreativeEngine | Promise<CreativeEngine> | (() => CreativeEngine | Promise<CreativeEngine>)): EngineAdapter<CreativeEngine>;
20
24
  //# sourceMappingURL=cesdk-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cesdk-adapter.d.ts","sourceRoot":"","sources":["../../src/engine/cesdk-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,cAAc,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,GAC/C,aAAa,CAAC,cAAc,CAAC,CAc/B"}
1
+ {"version":3,"file":"cesdk-adapter.d.ts","sourceRoot":"","sources":["../../src/engine/cesdk-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,cAAc,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EACF,cAAc,GACd,OAAO,CAAC,cAAc,CAAC,GACvB,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,GACnD,aAAa,CAAC,cAAc,CAAC,CAiB/B"}
@@ -14,10 +14,17 @@ import { SCENE_ALLOWED_RESOURCE_SCHEMES } from './scene-resource-schemes.js';
14
14
  * calls `create()` on the first slot (first edit), awaiting it then — by which
15
15
  * point init is usually already settled. The other methods receive the resolved
16
16
  * engine from EngineSession, so they never touch the promise.
17
+ *
18
+ * `engine` may also be a thunk `() => Promise<CreativeEngine>`: the stdio
19
+ * transport passes a lazy, slot-gated boot so no engine initializes until the
20
+ * first edit calls create().
17
21
  */
18
22
  export function cesdkAdapter(engine) {
19
23
  return {
20
- create: () => engine, // single-engine adapter: always the same instance (awaited lazily if a promise)
24
+ // Value/promise: always the same instance. Thunk: the stdio transport passes
25
+ // a lazy slot-gated boot (CODE-59) so nothing initializes until the first
26
+ // edit; EngineSession(size:1) calls create() only once, on that first edit.
27
+ create: () => (typeof engine === 'function' ? engine() : engine),
21
28
  reset: async (eng) => {
22
29
  eng.scene.create(); // wipes the active scene; design-unit patch from tools/engine.ts re-applies
23
30
  },
@@ -1 +1 @@
1
- {"version":3,"file":"cesdk-adapter.js","sourceRoot":"","sources":["../../src/engine/cesdk-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AAG7E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAgD;IAEhD,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,gFAAgF;QACtG,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACnB,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,4EAA4E;QAClG,CAAC;QACD,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;YACnC,MAAM,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QACD,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC1B,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC;YACrB,sBAAsB,EAAE,8BAA8B;SACvD,CAAC;KACL,CAAC;AACJ,CAAC","sourcesContent":["import type CreativeEngine from '@cesdk/engine';\nimport { SCENE_ALLOWED_RESOURCE_SCHEMES } from './scene-resource-schemes.js';\nimport type { EngineAdapter } from './session.js';\n\n/**\n * Adapts a `@cesdk/engine` CreativeEngine instance to the EngineSession's\n * generic EngineAdapter shape. One-to-one mapping; two quirks. First, `reset`\n * (CE.SDK has no `reset()` — we wipe by creating a new scene via the same\n * patched create path `ensureHeadlessEngine` installed for the design-unit fix).\n * Second, `saveToString` passes an explicit `allowedResourceSchemes`: the\n * engine default rejects our durable `workspace://` scheme, so an edit commit\n * of a scene referencing workspace assets would fail without it (see\n * scene-resource-schemes.ts, CODE-3).\n *\n * `engine` may be a Promise: the stdio transport defers engine init off the\n * MCP-handshake path and hands the in-flight promise here. EngineSession only\n * calls `create()` on the first slot (first edit), awaiting it then — by which\n * point init is usually already settled. The other methods receive the resolved\n * engine from EngineSession, so they never touch the promise.\n */\nexport function cesdkAdapter(\n engine: CreativeEngine | Promise<CreativeEngine>\n): EngineAdapter<CreativeEngine> {\n return {\n create: () => engine, // single-engine adapter: always the same instance (awaited lazily if a promise)\n reset: async (eng) => {\n eng.scene.create(); // wipes the active scene; design-unit patch from tools/engine.ts re-applies\n },\n loadFromString: async (eng, scene) => {\n await eng.scene.loadFromString(scene);\n },\n saveToString: async (eng) =>\n eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES\n })\n };\n}\n"]}
1
+ {"version":3,"file":"cesdk-adapter.js","sourceRoot":"","sources":["../../src/engine/cesdk-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AAG7E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAC1B,MAGoD;IAEpD,OAAO;QACL,6EAA6E;QAC7E,0EAA0E;QAC1E,4EAA4E;QAC5E,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAChE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACnB,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,4EAA4E;QAClG,CAAC;QACD,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;YACnC,MAAM,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QACD,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC1B,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC;YACrB,sBAAsB,EAAE,8BAA8B;SACvD,CAAC;KACL,CAAC;AACJ,CAAC","sourcesContent":["import type CreativeEngine from '@cesdk/engine';\nimport { SCENE_ALLOWED_RESOURCE_SCHEMES } from './scene-resource-schemes.js';\nimport type { EngineAdapter } from './session.js';\n\n/**\n * Adapts a `@cesdk/engine` CreativeEngine instance to the EngineSession's\n * generic EngineAdapter shape. One-to-one mapping; two quirks. First, `reset`\n * (CE.SDK has no `reset()` — we wipe by creating a new scene via the same\n * patched create path `ensureHeadlessEngine` installed for the design-unit fix).\n * Second, `saveToString` passes an explicit `allowedResourceSchemes`: the\n * engine default rejects our durable `workspace://` scheme, so an edit commit\n * of a scene referencing workspace assets would fail without it (see\n * scene-resource-schemes.ts, CODE-3).\n *\n * `engine` may be a Promise: the stdio transport defers engine init off the\n * MCP-handshake path and hands the in-flight promise here. EngineSession only\n * calls `create()` on the first slot (first edit), awaiting it then — by which\n * point init is usually already settled. The other methods receive the resolved\n * engine from EngineSession, so they never touch the promise.\n *\n * `engine` may also be a thunk `() => Promise<CreativeEngine>`: the stdio\n * transport passes a lazy, slot-gated boot so no engine initializes until the\n * first edit calls create().\n */\nexport function cesdkAdapter(\n engine:\n | CreativeEngine\n | Promise<CreativeEngine>\n | (() => CreativeEngine | Promise<CreativeEngine>)\n): EngineAdapter<CreativeEngine> {\n return {\n // Value/promise: always the same instance. Thunk: the stdio transport passes\n // a lazy slot-gated boot (CODE-59) so nothing initializes until the first\n // edit; EngineSession(size:1) calls create() only once, on that first edit.\n create: () => (typeof engine === 'function' ? engine() : engine),\n reset: async (eng) => {\n eng.scene.create(); // wipes the active scene; design-unit patch from tools/engine.ts re-applies\n },\n loadFromString: async (eng, scene) => {\n await eng.scene.loadFromString(scene);\n },\n saveToString: async (eng) =>\n eng.scene.saveToString({\n allowedResourceSchemes: SCENE_ALLOWED_RESOURCE_SCHEMES\n })\n };\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { CodesignMcpToolRegistration } from '../server.js';
2
+ /** Thrown from the slot-gated engine boot when all engine slots are held. */
3
+ export declare class EngineCapacityError extends Error {
4
+ readonly max: number;
5
+ constructor(max: number);
6
+ }
7
+ /** The agent-facing capacity message, templated on the live cap. */
8
+ export declare function capacityMessage(max: number): string;
9
+ /**
10
+ * Wrap a tool registration so a thrown EngineCapacityError becomes a clean MCP
11
+ * isError result (the capacity message) instead of propagating as a protocol
12
+ * error. Other errors pass through untouched. Applied to every tool at the stdio
13
+ * composition root; a no-op for tools that never touch the engine.
14
+ */
15
+ export declare function wrapEngineCapacity(reg: CodesignMcpToolRegistration): CodesignMcpToolRegistration;
16
+ //# sourceMappingURL=engine-capacity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-capacity.d.ts","sourceRoot":"","sources":["../../src/engine/engine-capacity.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAEhE,6EAA6E;AAC7E,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAAX,GAAG,EAAE,MAAM;CAIjC;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYnD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,2BAA2B,GAC/B,2BAA2B,CAiB7B"}
@@ -0,0 +1,48 @@
1
+ /** Thrown from the slot-gated engine boot when all engine slots are held. */
2
+ export class EngineCapacityError extends Error {
3
+ constructor(max) {
4
+ super(`engine capacity reached (max=${max})`);
5
+ this.max = max;
6
+ this.name = 'EngineCapacityError';
7
+ }
8
+ }
9
+ /** The agent-facing capacity message, templated on the live cap. */
10
+ export function capacityMessage(max) {
11
+ return [
12
+ `Design engine capacity reached — ${max} of ${max} CE.SDK engines are already running on this machine.`,
13
+ `This is a temporary, machine-wide limit that prevents parallel design sessions from exhausting memory. Your request was valid — nothing is wrong with your code or inputs, and no design was created. A slot frees automatically as soon as another session finishes or closes.`,
14
+ ``,
15
+ `What to do:`,
16
+ `- Retry the exact same call after a short wait — resending it unchanged will succeed once a slot is free.`,
17
+ `- Do not modify your design code in response to this; the code is not the problem.`,
18
+ `- If you are running several design tasks at once, run them one at a time instead of in parallel.`,
19
+ ``,
20
+ `(The human can raise the limit by setting IMGLY_MAX_STDIO_ENGINES higher and restarting. Current limit: ${max}.)`
21
+ ].join('\n');
22
+ }
23
+ /**
24
+ * Wrap a tool registration so a thrown EngineCapacityError becomes a clean MCP
25
+ * isError result (the capacity message) instead of propagating as a protocol
26
+ * error. Other errors pass through untouched. Applied to every tool at the stdio
27
+ * composition root; a no-op for tools that never touch the engine.
28
+ */
29
+ export function wrapEngineCapacity(reg) {
30
+ return {
31
+ ...reg,
32
+ call: async (args) => {
33
+ try {
34
+ return await reg.call(args);
35
+ }
36
+ catch (err) {
37
+ if (err instanceof EngineCapacityError) {
38
+ return {
39
+ isError: true,
40
+ content: [{ type: 'text', text: capacityMessage(err.max) }]
41
+ };
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+ };
47
+ }
48
+ //# sourceMappingURL=engine-capacity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-capacity.js","sourceRoot":"","sources":["../../src/engine/engine-capacity.ts"],"names":[],"mappings":"AAYA,6EAA6E;AAC7E,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAqB,GAAW;QAC9B,KAAK,CAAC,gCAAgC,GAAG,GAAG,CAAC,CAAC;QAD3B,QAAG,GAAH,GAAG,CAAQ;QAE9B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO;QACL,oCAAoC,GAAG,OAAO,GAAG,sDAAsD;QACvG,iRAAiR;QACjR,EAAE;QACF,aAAa;QACb,2GAA2G;QAC3G,oFAAoF;QACpF,mGAAmG;QACnG,EAAE;QACF,2GAA2G,GAAG,IAAI;KACnH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAgC;IAEhC,OAAO;QACL,GAAG,GAAG;QACN,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACnB,IAAI,CAAC;gBACH,OAAO,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;oBACvC,OAAO;wBACL,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;qBAC5D,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// packages/codesign-mcp/src/engine/engine-capacity.ts\n//\n// The machine-wide engine cap (CODE-59) fails fast: when every slot is held, the\n// triggering tool call returns an MCP isError result carrying an agent-facing\n// message — NOT a thrown protocol error. This module owns that error type, the\n// message, and the wrapper that turns the throw into the result.\n//\n// The message is a deliberate design surface: it is read by an LLM, so it (a)\n// states the request/code is fine (stops the model rewriting its design), (b)\n// says it is retryable and how, (c) tells an orchestrator to reduce parallelism.\nimport type { CodesignMcpToolRegistration } from '../server.js';\n\n/** Thrown from the slot-gated engine boot when all engine slots are held. */\nexport class EngineCapacityError extends Error {\n constructor(readonly max: number) {\n super(`engine capacity reached (max=${max})`);\n this.name = 'EngineCapacityError';\n }\n}\n\n/** The agent-facing capacity message, templated on the live cap. */\nexport function capacityMessage(max: number): string {\n return [\n `Design engine capacity reached — ${max} of ${max} CE.SDK engines are already running on this machine.`,\n `This is a temporary, machine-wide limit that prevents parallel design sessions from exhausting memory. Your request was valid — nothing is wrong with your code or inputs, and no design was created. A slot frees automatically as soon as another session finishes or closes.`,\n ``,\n `What to do:`,\n `- Retry the exact same call after a short wait — resending it unchanged will succeed once a slot is free.`,\n `- Do not modify your design code in response to this; the code is not the problem.`,\n `- If you are running several design tasks at once, run them one at a time instead of in parallel.`,\n ``,\n `(The human can raise the limit by setting IMGLY_MAX_STDIO_ENGINES higher and restarting. Current limit: ${max}.)`\n ].join('\\n');\n}\n\n/**\n * Wrap a tool registration so a thrown EngineCapacityError becomes a clean MCP\n * isError result (the capacity message) instead of propagating as a protocol\n * error. Other errors pass through untouched. Applied to every tool at the stdio\n * composition root; a no-op for tools that never touch the engine.\n */\nexport function wrapEngineCapacity(\n reg: CodesignMcpToolRegistration\n): CodesignMcpToolRegistration {\n return {\n ...reg,\n call: async (args) => {\n try {\n return await reg.call(args);\n } catch (err) {\n if (err instanceof EngineCapacityError) {\n return {\n isError: true,\n content: [{ type: 'text', text: capacityMessage(err.max) }]\n };\n }\n throw err;\n }\n }\n };\n}\n"]}
@@ -0,0 +1,48 @@
1
+ export interface SlotHandle {
2
+ /** Which numbered slot (0-based) this handle owns. */
3
+ readonly index: number;
4
+ /** Release the slot (unlink its file). Idempotent; best-effort. */
5
+ release(): Promise<void>;
6
+ }
7
+ export interface AcquireEngineSlotOptions {
8
+ /** Max concurrent engines. The (max)th acquire succeeds; the (max+1)th returns null. */
9
+ max: number;
10
+ /** Slot directory. Defaults to ~/.codesign/engine-slots. Override in tests. */
11
+ dir?: string;
12
+ /** Liveness probe for a slot's owner PID. Defaults to a real kill(pid,0). Injectable for tests. */
13
+ isPidAlive?: (pid: number) => boolean;
14
+ /** This process's PID, recorded in the claimed slot. Defaults to process.pid. */
15
+ pid?: number;
16
+ }
17
+ /**
18
+ * True if a process with `pid` is alive. `kill(pid, 0)` sends no signal; it only
19
+ * probes. ESRCH => no such process (dead). EPERM (exists, another user) or any
20
+ * other error => assume alive (conservative — never steal a slot we're unsure of).
21
+ */
22
+ export declare function isPidAlive(pid: number): boolean;
23
+ /**
24
+ * Claim one of `max` numbered slots. Returns a handle for the first free (or
25
+ * reclaimable-stale) slot, or null when all `max` are held by live owners.
26
+ */
27
+ export declare function acquireEngineSlot(opts: AcquireEngineSlotOptions): Promise<SlotHandle | null>;
28
+ export interface SlotGatedEngine<E> {
29
+ /**
30
+ * Boot the engine on first call, gated by a machine-wide slot. Memoized: later
31
+ * calls return the same engine. Throws EngineCapacityError at capacity (the
32
+ * memo is NOT cached, so a later retry re-attempts). A boot FAILURE is cached
33
+ * (a hard init error must not churn slots on retry) and its slot is released.
34
+ */
35
+ get(): Promise<E>;
36
+ /** Release the held slot (for the clean-shutdown path). Idempotent. */
37
+ release(): Promise<void>;
38
+ }
39
+ export declare function createSlotGatedEngine<E>(opts: {
40
+ max: number;
41
+ boot: () => Promise<E>;
42
+ dir?: string;
43
+ acquire?: (o: {
44
+ max: number;
45
+ dir?: string;
46
+ }) => Promise<SlotHandle | null>;
47
+ }): SlotGatedEngine<E>;
48
+ //# sourceMappingURL=engine-slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-slots.d.ts","sourceRoot":"","sources":["../../src/engine/engine-slots.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,UAAU;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAU5B;AA+ED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC;;;;;OAKG;IACH,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAClB,uEAAuE;IACvE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CAC5E,GAAG,eAAe,CAAC,CAAC,CAAC,CAmCrB"}
@@ -0,0 +1,182 @@
1
+ // packages/codesign-mcp/src/engine/engine-slots.ts
2
+ //
3
+ // Machine-wide (per-user) semaphore bounding how many CE.SDK engines boot
4
+ // concurrently across ALL independent codesign-mcp stdio processes. Each engine
5
+ // costs ~82 MB idle / 200-300 MB active, so a handful stacked across parallel
6
+ // sessions can OOM the machine (CODE-59). No in-process cap can bound the total —
7
+ // each stdio server is its own process with its own singleton engine — so the
8
+ // coordination point lives on the filesystem.
9
+ //
10
+ // Mechanism: N slot files slot-0..slot-(N-1) under ~/.codesign/engine-slots. A
11
+ // slot is claimed by atomically creating its file with O_EXCL (open(path,'wx')).
12
+ // The file records the owner PID; a slot whose PID is dead (process.kill(pid,0)
13
+ // -> ESRCH) is reclaimed on the next acquire — the backstop for SIGKILL, where
14
+ // the clean release path (CODE-53 shutdown) never runs. Reclaim renames the
15
+ // stale file to a per-PID tombstone; rename() requires the source to exist, so
16
+ // only one concurrent reclaimer of the SAME stale file wins (losers get ENOENT
17
+ // and re-loop to see the new owner). Clean release unlinks.
18
+ //
19
+ // Scope: the slot dir is under the user's home, so the cap is really per-user —
20
+ // which matches the OOM scenario (one user's many parallel sessions).
21
+ //
22
+ // Known limitations — both narrow, both at worst a TRANSIENT over-provision by
23
+ // ~1 (never the unbounded, dozens-of-engines OOM this prevents), self-correcting
24
+ // on process exit. Neither is fixable with portable fs primitives: O_EXCL create
25
+ // is the only atomic mutual-exclusion primitive, and it cannot reclaim an
26
+ // already-occupied name, so every content-blind removal (unlink OR rename) has a
27
+ // TOCTOU. A single-process broker (the deferred "Approach C") is the real fix if
28
+ // hard multi-process correctness is ever required.
29
+ // 1. PID reuse: a dead owner's PID could be recycled by an unrelated live
30
+ // process, making a stale slot look alive until that PID exits.
31
+ // 2. Concurrent-reclaim straggler: rename() is content-blind, so if reclaimer
32
+ // B's rename syscall lags reclaimer A's full reclaim+recreate cycle, B's
33
+ // rename can steal A's already-recreated FRESH file and both end up owning
34
+ // the slot. rename() closes the common simultaneous case; this residual
35
+ // needs adversarial sub-millisecond syscall reordering across processes.
36
+ // (A tombstone orphaned by SIGKILL between rename and its cleanup unlink is inert
37
+ // litter — acquire only ever touches exact `slot-<i>` paths, never `*.dead.*`.)
38
+ import { mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
39
+ import { homedir } from 'node:os';
40
+ import { join } from 'node:path';
41
+ import { EngineCapacityError } from './engine-capacity.js';
42
+ const DEFAULT_SLOT_DIR = join(homedir(), '.codesign', 'engine-slots');
43
+ /**
44
+ * True if a process with `pid` is alive. `kill(pid, 0)` sends no signal; it only
45
+ * probes. ESRCH => no such process (dead). EPERM (exists, another user) or any
46
+ * other error => assume alive (conservative — never steal a slot we're unsure of).
47
+ */
48
+ export function isPidAlive(pid) {
49
+ try {
50
+ process.kill(pid, 0);
51
+ return true;
52
+ }
53
+ catch (err) {
54
+ return err.code !== 'ESRCH';
55
+ }
56
+ }
57
+ /**
58
+ * Claim one of `max` numbered slots. Returns a handle for the first free (or
59
+ * reclaimable-stale) slot, or null when all `max` are held by live owners.
60
+ */
61
+ export async function acquireEngineSlot(opts) {
62
+ const dir = opts.dir ?? DEFAULT_SLOT_DIR;
63
+ const alive = opts.isPidAlive ?? isPidAlive;
64
+ const pid = opts.pid ?? process.pid;
65
+ await mkdir(dir, { recursive: true });
66
+ for (let i = 0; i < opts.max; i++) {
67
+ const handle = await tryClaimSlot(join(dir, `slot-${i}`), i, pid, alive);
68
+ if (handle)
69
+ return handle;
70
+ }
71
+ return null;
72
+ }
73
+ async function tryClaimSlot(path, index, pid, alive) {
74
+ // Two attempts: create; if occupied-but-dead, reclaim and retry the create.
75
+ for (let attempt = 0; attempt < 2; attempt++) {
76
+ try {
77
+ const fh = await open(path, 'wx'); // O_CREAT | O_EXCL — atomic claim
78
+ try {
79
+ await fh.writeFile(JSON.stringify({ pid, bootedAt: Date.now() }));
80
+ }
81
+ finally {
82
+ await fh.close();
83
+ }
84
+ return makeHandle(path, index);
85
+ }
86
+ catch (err) {
87
+ if (err.code !== 'EEXIST')
88
+ throw err;
89
+ if (!(await ownerIsDead(path, alive)))
90
+ return null; // held by a live owner
91
+ // Reclaim atomically: rename the stale file aside. rename() requires the
92
+ // source to exist, so only ONE concurrent reclaimer of the same slot
93
+ // succeeds; the losers get ENOENT and re-loop to see the new owner. An
94
+ // unconditional unlink+recreate would instead let a second reclaimer
95
+ // delete the first's fresh file and double-occupy the slot.
96
+ const tomb = `${path}.dead.${pid}`;
97
+ try {
98
+ await rename(path, tomb);
99
+ }
100
+ catch (e) {
101
+ if (e.code === 'ENOENT')
102
+ continue; // lost the reclaim; retry the slot
103
+ throw e;
104
+ }
105
+ await unlink(tomb).catch(() => { }); // best-effort tombstone cleanup
106
+ // slot path is now free; loop retries the exclusive create
107
+ }
108
+ }
109
+ return null; // lost the reclaim race — treat as taken, move on
110
+ }
111
+ /**
112
+ * Is the slot file's recorded owner provably dead? Only a slot with a readable,
113
+ * valid PID that is not alive is reclaimable. Empty/malformed/unreadable => treat
114
+ * as ALIVE (return false) so we never steal a slot a peer just created with
115
+ * O_EXCL but has not finished writing yet.
116
+ */
117
+ async function ownerIsDead(path, alive) {
118
+ let raw;
119
+ try {
120
+ raw = await readFile(path, 'utf8');
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ let pid;
126
+ try {
127
+ pid = JSON.parse(raw).pid;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ return typeof pid === 'number' && !alive(pid);
133
+ }
134
+ function makeHandle(path, index) {
135
+ let released = false;
136
+ return {
137
+ index,
138
+ release: async () => {
139
+ if (released)
140
+ return;
141
+ released = true;
142
+ await unlink(path).catch(() => { }); // ENOENT etc. — best-effort
143
+ }
144
+ };
145
+ }
146
+ export function createSlotGatedEngine(opts) {
147
+ const acquire = opts.acquire ?? acquireEngineSlot;
148
+ let engine;
149
+ let handle;
150
+ const boot = async () => {
151
+ const h = await acquire({
152
+ max: opts.max,
153
+ ...(opts.dir !== undefined ? { dir: opts.dir } : {})
154
+ });
155
+ if (!h) {
156
+ engine = undefined; // capacity is retryable — don't memoize the rejection
157
+ throw new EngineCapacityError(opts.max);
158
+ }
159
+ handle = h;
160
+ try {
161
+ return await opts.boot();
162
+ }
163
+ catch (err) {
164
+ // Hard init failure: free the slot (don't hold it for a dead engine). Keep
165
+ // the rejected memo so retries don't churn slots on a permanent failure.
166
+ await h.release().catch(() => { });
167
+ handle = undefined;
168
+ throw err;
169
+ }
170
+ };
171
+ return {
172
+ get: () => (engine ??= boot()),
173
+ release: async () => {
174
+ const h = handle;
175
+ handle = undefined;
176
+ engine = undefined;
177
+ if (h)
178
+ await h.release();
179
+ }
180
+ };
181
+ }
182
+ //# sourceMappingURL=engine-slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-slots.js","sourceRoot":"","sources":["../../src/engine/engine-slots.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/engine/engine-slots.ts\n//\n// Machine-wide (per-user) semaphore bounding how many CE.SDK engines boot\n// concurrently across ALL independent codesign-mcp stdio processes. Each engine\n// costs ~82 MB idle / 200-300 MB active, so a handful stacked across parallel\n// sessions can OOM the machine (CODE-59). No in-process cap can bound the total —\n// each stdio server is its own process with its own singleton engine — so the\n// coordination point lives on the filesystem.\n//\n// Mechanism: N slot files slot-0..slot-(N-1) under ~/.codesign/engine-slots. A\n// slot is claimed by atomically creating its file with O_EXCL (open(path,'wx')).\n// The file records the owner PID; a slot whose PID is dead (process.kill(pid,0)\n// -> ESRCH) is reclaimed on the next acquire — the backstop for SIGKILL, where\n// the clean release path (CODE-53 shutdown) never runs. Reclaim renames the\n// stale file to a per-PID tombstone; rename() requires the source to exist, so\n// only one concurrent reclaimer of the SAME stale file wins (losers get ENOENT\n// and re-loop to see the new owner). Clean release unlinks.\n//\n// Scope: the slot dir is under the user's home, so the cap is really per-user —\n// which matches the OOM scenario (one user's many parallel sessions).\n//\n// Known limitations — both narrow, both at worst a TRANSIENT over-provision by\n// ~1 (never the unbounded, dozens-of-engines OOM this prevents), self-correcting\n// on process exit. Neither is fixable with portable fs primitives: O_EXCL create\n// is the only atomic mutual-exclusion primitive, and it cannot reclaim an\n// already-occupied name, so every content-blind removal (unlink OR rename) has a\n// TOCTOU. A single-process broker (the deferred \"Approach C\") is the real fix if\n// hard multi-process correctness is ever required.\n// 1. PID reuse: a dead owner's PID could be recycled by an unrelated live\n// process, making a stale slot look alive until that PID exits.\n// 2. Concurrent-reclaim straggler: rename() is content-blind, so if reclaimer\n// B's rename syscall lags reclaimer A's full reclaim+recreate cycle, B's\n// rename can steal A's already-recreated FRESH file and both end up owning\n// the slot. rename() closes the common simultaneous case; this residual\n// needs adversarial sub-millisecond syscall reordering across processes.\n// (A tombstone orphaned by SIGKILL between rename and its cleanup unlink is inert\n// litter — acquire only ever touches exact `slot-<i>` paths, never `*.dead.*`.)\nimport { mkdir, open, readFile, rename, unlink } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { EngineCapacityError } from './engine-capacity.js';\n\nconst DEFAULT_SLOT_DIR = join(homedir(), '.codesign', 'engine-slots');\n\nexport interface SlotHandle {\n /** Which numbered slot (0-based) this handle owns. */\n readonly index: number;\n /** Release the slot (unlink its file). Idempotent; best-effort. */\n release(): Promise<void>;\n}\n\nexport interface AcquireEngineSlotOptions {\n /** Max concurrent engines. The (max)th acquire succeeds; the (max+1)th returns null. */\n max: number;\n /** Slot directory. Defaults to ~/.codesign/engine-slots. Override in tests. */\n dir?: string;\n /** Liveness probe for a slot's owner PID. Defaults to a real kill(pid,0). Injectable for tests. */\n isPidAlive?: (pid: number) => boolean;\n /** This process's PID, recorded in the claimed slot. Defaults to process.pid. */\n pid?: number;\n}\n\n/**\n * True if a process with `pid` is alive. `kill(pid, 0)` sends no signal; it only\n * probes. ESRCH => no such process (dead). EPERM (exists, another user) or any\n * other error => assume alive (conservative — never steal a slot we're unsure of).\n */\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n/**\n * Claim one of `max` numbered slots. Returns a handle for the first free (or\n * reclaimable-stale) slot, or null when all `max` are held by live owners.\n */\nexport async function acquireEngineSlot(\n opts: AcquireEngineSlotOptions\n): Promise<SlotHandle | null> {\n const dir = opts.dir ?? DEFAULT_SLOT_DIR;\n const alive = opts.isPidAlive ?? isPidAlive;\n const pid = opts.pid ?? process.pid;\n await mkdir(dir, { recursive: true });\n for (let i = 0; i < opts.max; i++) {\n const handle = await tryClaimSlot(join(dir, `slot-${i}`), i, pid, alive);\n if (handle) return handle;\n }\n return null;\n}\n\nasync function tryClaimSlot(\n path: string,\n index: number,\n pid: number,\n alive: (pid: number) => boolean\n): Promise<SlotHandle | null> {\n // Two attempts: create; if occupied-but-dead, reclaim and retry the create.\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const fh = await open(path, 'wx'); // O_CREAT | O_EXCL — atomic claim\n try {\n await fh.writeFile(JSON.stringify({ pid, bootedAt: Date.now() }));\n } finally {\n await fh.close();\n }\n return makeHandle(path, index);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;\n if (!(await ownerIsDead(path, alive))) return null; // held by a live owner\n // Reclaim atomically: rename the stale file aside. rename() requires the\n // source to exist, so only ONE concurrent reclaimer of the same slot\n // succeeds; the losers get ENOENT and re-loop to see the new owner. An\n // unconditional unlink+recreate would instead let a second reclaimer\n // delete the first's fresh file and double-occupy the slot.\n const tomb = `${path}.dead.${pid}`;\n try {\n await rename(path, tomb);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') continue; // lost the reclaim; retry the slot\n throw e;\n }\n await unlink(tomb).catch(() => {}); // best-effort tombstone cleanup\n // slot path is now free; loop retries the exclusive create\n }\n }\n return null; // lost the reclaim race — treat as taken, move on\n}\n\n/**\n * Is the slot file's recorded owner provably dead? Only a slot with a readable,\n * valid PID that is not alive is reclaimable. Empty/malformed/unreadable => treat\n * as ALIVE (return false) so we never steal a slot a peer just created with\n * O_EXCL but has not finished writing yet.\n */\nasync function ownerIsDead(\n path: string,\n alive: (pid: number) => boolean\n): Promise<boolean> {\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch {\n return false;\n }\n let pid: unknown;\n try {\n pid = (JSON.parse(raw) as { pid?: unknown }).pid;\n } catch {\n return false;\n }\n return typeof pid === 'number' && !alive(pid);\n}\n\nfunction makeHandle(path: string, index: number): SlotHandle {\n let released = false;\n return {\n index,\n release: async () => {\n if (released) return;\n released = true;\n await unlink(path).catch(() => {}); // ENOENT etc. — best-effort\n }\n };\n}\n\n// ── Slot-gated lazy engine ──────────────────────────────────────────────────\n\nexport interface SlotGatedEngine<E> {\n /**\n * Boot the engine on first call, gated by a machine-wide slot. Memoized: later\n * calls return the same engine. Throws EngineCapacityError at capacity (the\n * memo is NOT cached, so a later retry re-attempts). A boot FAILURE is cached\n * (a hard init error must not churn slots on retry) and its slot is released.\n */\n get(): Promise<E>;\n /** Release the held slot (for the clean-shutdown path). Idempotent. */\n release(): Promise<void>;\n}\n\nexport function createSlotGatedEngine<E>(opts: {\n max: number;\n boot: () => Promise<E>;\n dir?: string;\n acquire?: (o: { max: number; dir?: string }) => Promise<SlotHandle | null>;\n}): SlotGatedEngine<E> {\n const acquire = opts.acquire ?? acquireEngineSlot;\n let engine: Promise<E> | undefined;\n let handle: SlotHandle | undefined;\n\n const boot = async (): Promise<E> => {\n const h = await acquire({\n max: opts.max,\n ...(opts.dir !== undefined ? { dir: opts.dir } : {})\n });\n if (!h) {\n engine = undefined; // capacity is retryable — don't memoize the rejection\n throw new EngineCapacityError(opts.max);\n }\n handle = h;\n try {\n return await opts.boot();\n } catch (err) {\n // Hard init failure: free the slot (don't hold it for a dead engine). Keep\n // the rejected memo so retries don't churn slots on a permanent failure.\n await h.release().catch(() => {});\n handle = undefined;\n throw err;\n }\n };\n\n return {\n get: () => (engine ??= boot()),\n release: async () => {\n const h = handle;\n handle = undefined;\n engine = undefined;\n if (h) await h.release();\n }\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"uri-resolver.js","sourceRoot":"","sources":["../../src/engine/uri-resolver.ts"],"names":[],"mappings":"AAOA,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAc5C;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAA2B,EAC3B,UAA4B;IAE5B,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,CACpD,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC;QAClC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAChE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CACzB,CAAC;AACJ,CAAC","sourcesContent":["//\n// Registers a CE.SDK URI resolver that rewrites `workspace://<relpath>` asset\n// URIs (stored durably in scenes) to the HTTP `/r/` URL the engine can fetch.\n// Everything else delegates to CE.SDK's default resolver. Used by the headless\n// node engine; the browser viewer mirrors this logic in viewer.js.\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\n\nconst WORKSPACE_URI_PREFIX = 'workspace://';\n\n/** The minimal slice of CreativeEngine this module needs — keeps it testable. */\nexport interface UriResolvableEngine {\n editor: {\n setURIResolver(\n resolver: (\n uri: string,\n defaultResolver: (uri: string) => string\n ) => string\n ): void;\n };\n}\n\n/**\n * Make the engine resolve `workspace://…` asset URIs to the `/r/` HTTP route.\n * Scenes persist the durable `workspace://` form; this rewrites it to a live\n * fetch URL at load time, so a design survives a process restart / rewind.\n */\nexport function registerWorkspaceUriResolver(\n engine: UriResolvableEngine,\n urlBuilder: ViewerUrlBuilder\n): void {\n engine.editor.setURIResolver((uri, defaultResolver) =>\n uri.startsWith(WORKSPACE_URI_PREFIX)\n ? urlBuilder.forResource(uri.slice(WORKSPACE_URI_PREFIX.length))\n : defaultResolver(uri)\n );\n}\n"]}
1
+ {"version":3,"file":"uri-resolver.js","sourceRoot":"","sources":["../../src/engine/uri-resolver.ts"],"names":[],"mappings":"AAOA,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAc5C;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAA2B,EAC3B,UAA4B;IAE5B,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,CACpD,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC;QAClC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAChE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CACzB,CAAC;AACJ,CAAC","sourcesContent":["//\n// Registers a CE.SDK URI resolver that rewrites `workspace://<relpath>` asset\n// URIs (stored durably in scenes) to the HTTP `/r/` URL the engine can fetch.\n// Everything else delegates to CE.SDK's default resolver. Used by the headless\n// node engine; the browser editor mirrors this logic in editor.js.\nimport type { ViewerUrlBuilder } from '../viewer/url-builder.js';\n\nconst WORKSPACE_URI_PREFIX = 'workspace://';\n\n/** The minimal slice of CreativeEngine this module needs — keeps it testable. */\nexport interface UriResolvableEngine {\n editor: {\n setURIResolver(\n resolver: (\n uri: string,\n defaultResolver: (uri: string) => string\n ) => string\n ): void;\n };\n}\n\n/**\n * Make the engine resolve `workspace://…` asset URIs to the `/r/` HTTP route.\n * Scenes persist the durable `workspace://` form; this rewrites it to a live\n * fetch URL at load time, so a design survives a process restart / rewind.\n */\nexport function registerWorkspaceUriResolver(\n engine: UriResolvableEngine,\n urlBuilder: ViewerUrlBuilder\n): void {\n engine.editor.setURIResolver((uri, defaultResolver) =>\n uri.startsWith(WORKSPACE_URI_PREFIX)\n ? urlBuilder.forResource(uri.slice(WORKSPACE_URI_PREFIX.length))\n : defaultResolver(uri)\n );\n}\n"]}
package/dist/server.d.ts CHANGED
@@ -22,6 +22,29 @@ export interface CodesignMcpToolRegistration {
22
22
  isError?: boolean;
23
23
  }>;
24
24
  }
25
+ /**
26
+ * One prompt (slash command) the server exposes via prompts/list +
27
+ * prompts/get. Mirrors `CodesignMcpToolRegistration`'s caller-supplied shape.
28
+ */
29
+ export interface CodesignMcpPromptRegistration {
30
+ name: string;
31
+ description?: string;
32
+ arguments?: Array<{
33
+ name: string;
34
+ description?: string;
35
+ required?: boolean;
36
+ }>;
37
+ get: (args: Record<string, string> | undefined) => {
38
+ description?: string;
39
+ messages: Array<{
40
+ role: 'user' | 'assistant';
41
+ content: {
42
+ type: 'text';
43
+ text: string;
44
+ };
45
+ }>;
46
+ };
47
+ }
25
48
  export interface CodesignMcpServerOptions {
26
49
  /**
27
50
  * Asset bundle. Keys are forward-slash paths under one of:
@@ -34,6 +57,8 @@ export interface CodesignMcpServerOptions {
34
57
  resourceScheme?: string;
35
58
  /** Tools to expose via tools/list + tools/call. */
36
59
  tools?: CodesignMcpToolRegistration[];
60
+ /** MCP prompts (slash commands). Advertised only when at least one is passed. */
61
+ prompts?: CodesignMcpPromptRegistration[];
37
62
  /**
38
63
  * Build identity reported as `serverInfo.version` on `initialize`. A client
39
64
  * (the deploy smoketest) can assert it to confirm it's talking to the
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;QAC/B,OAAO,EAAE,KAAK,CACV;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,GAC9B;YAAE,IAAI,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CACpD,CAAC;QACF,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,KAAK,CAAC,EAAE,2BAA2B,EAAE,CAAC;IACtC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,MAAM,CAmIR"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAcnE;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;QAC/B,OAAO,EAAE,KAAK,CACV;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,GAC9B;YAAE,IAAI,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CACpD,CAAC;QACF,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,KAAK;QACjD,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;YAC3B,OAAO,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;SACzC,CAAC,CAAC;KACJ,CAAC;CACH;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,KAAK,CAAC,EAAE,2BAA2B,EAAE,CAAC;IACtC,iFAAiF;IACjF,OAAO,CAAC,EAAE,6BAA6B,EAAE,CAAC;IAC1C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,MAAM,CA2JR"}
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
2
+ import { CallToolRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { rewriteSkillLinks } from './cross-ref.js';
4
4
  import { buildSkillReadTools } from './skill-tools.js';
5
5
  const DOC_FILE = /^docs\/([^/]+)\/(.+)$/;
@@ -31,7 +31,7 @@ export function createCodesignMcpServer(opts) {
31
31
  '',
32
32
  'Design tools (after reading the handbook):',
33
33
  '- `edit({ parent, code, title?, note? })` — mutate the canvas; `parent` is REQUIRED (revision id, or null for a new design).',
34
- '- `preview({ blockId, revision? })` — render a block to an inline image.',
34
+ '- `preview({ blockId, revision })` — render a block to an inline image. `revision` is required (the id `edit` returned).',
35
35
  '- `export({ target, format, revision, blockId })` — render a block to pdf/image and write it into the workspace.',
36
36
  '- `save({ revision, outPath? })` — write a self-contained `.zip` archive (scene + assets) of a revision into the workspace.',
37
37
  '',
@@ -40,7 +40,7 @@ export function createCodesignMcpServer(opts) {
40
40
  '- `history({ revision })` — walk back to the root; how you re-ground after a rewind.',
41
41
  '- `inspect({ revision })` — read one revision in full, including the code that produced it (patterns, not block ids).',
42
42
  '- `rename({ revision, title })` — rename a design.',
43
- '- `open_editor({ revision? })` — URL the human can open to view the design.',
43
+ '- `view({ revision })` — URL the human can open to view the design.',
44
44
  '- `asset_search({ sourceId?, query? })` — browse asset sources; apply results in `edit`.'
45
45
  ]
46
46
  .concat(hasAssetGenerate
@@ -64,7 +64,11 @@ export function createCodesignMcpServer(opts) {
64
64
  rewrittenContent.set(uri, rewriteSkillLinks(content, doc, scheme));
65
65
  }
66
66
  const server = new Server({ name: 'codesign', version: opts.buildId ?? 'dev' }, {
67
- capabilities: { resources: {}, tools: {} },
67
+ capabilities: {
68
+ resources: {},
69
+ tools: {},
70
+ ...(opts.prompts && opts.prompts.length > 0 ? { prompts: {} } : {})
71
+ },
68
72
  instructions
69
73
  });
70
74
  // `resources/list` advertises only the per-skill SKILL.md (the entry
@@ -114,6 +118,22 @@ export function createCodesignMcpServer(opts) {
114
118
  }
115
119
  return await t.call(req.params.arguments ?? {});
116
120
  });
121
+ if (opts.prompts && opts.prompts.length > 0) {
122
+ const prompts = new Map(opts.prompts.map((p) => [p.name, p]));
123
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
124
+ prompts: Array.from(prompts.values()).map((p) => ({
125
+ name: p.name,
126
+ ...(p.description ? { description: p.description } : {}),
127
+ ...(p.arguments ? { arguments: p.arguments } : {})
128
+ }))
129
+ }));
130
+ server.setRequestHandler(GetPromptRequestSchema, async (req) => {
131
+ const p = prompts.get(req.params.name);
132
+ if (!p)
133
+ throw new McpError(ErrorCode.InvalidParams, `Unknown prompt: ${req.params.name}`);
134
+ return p.get(req.params.arguments);
135
+ });
136
+ }
117
137
  return server;
118
138
  }
119
139
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema\n} from '@modelcontextprotocol/sdk/types.js';\nimport { rewriteSkillLinks } from './cross-ref.js';\nimport { buildSkillReadTools } from './skill-tools.js';\n\n/**\n * One tool the server exposes via tools/list + tools/call. Caller-supplied so\n * the engine-bound implementations live near the surface that owns the engine\n * (the demo passes its existing `createDesignTools` output adapted to this\n * shape; an external bootstrap could pass server-side equivalents).\n */\nexport interface CodesignMcpToolRegistration {\n name: string;\n description?: string;\n /** JSON Schema describing the tool's input arguments. */\n inputSchema: unknown;\n call: (args: unknown) => Promise<{\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string }\n >;\n isError?: boolean;\n }>;\n}\n\nexport interface CodesignMcpServerOptions {\n /**\n * Asset bundle. Keys are forward-slash paths under one of:\n * \"docs/<doc>/SKILL.md\" — entry point for handbook / api / guide\n * \"docs/<doc>/<file>\" — sub-files reachable via the doc tools\n * Values are file contents.\n */\n assets: Record<string, string>;\n /** URI scheme for resources. Default: \"codesign\". */\n resourceScheme?: string;\n /** Tools to expose via tools/list + tools/call. */\n tools?: CodesignMcpToolRegistration[];\n /**\n * Build identity reported as `serverInfo.version` on `initialize`. A client\n * (the deploy smoketest) can assert it to confirm it's talking to the\n * just-deployed build, not a stale container. Default `'dev'`.\n */\n buildId?: string;\n}\n\nconst DOC_FILE = /^docs\\/([^/]+)\\/(.+)$/;\n\nexport function createCodesignMcpServer(\n opts: CodesignMcpServerOptions\n): Server {\n const scheme = opts.resourceScheme ?? 'codesign';\n const assets = opts.assets;\n\n // `instructions` is intentionally short — it tells the model what the\n // server is and STRONGLY directs it to call `codesign_read_codesign_system`\n // before its first edit. Skills are now first-class tools (not MCP\n // resources buried in an XML block), so discovery happens via tools/list\n // on every host without needing ReadMcpResourceTool support.\n //\n // Why short: Claude Code wraps MCP `instructions` in a <system-reminder>\n // and truncates long ones mid-stream. Keeping `instructions` under ~1.5 KB\n // ensures the whole pointer survives any sane truncation.\n //\n // The asset-generation hint is included only when an `asset_generate` tool\n // is actually registered (Local hides it without an API_KEY), so we never\n // advertise a tool the model can't see in tools/list.\n const hasAssetGenerate = (opts.tools ?? []).some(\n (t) => t.name === 'asset_generate'\n );\n const instructions = [\n 'This is the **codesign** MCP server. It hosts a CE.SDK CreativeEngine and a persistent design workspace (revision DAG) for vector design + print-quality PDF output.',\n '',\n '**Required first step** — before any `edit`, call `handbook` (no args). It teaches the mandatory `parent` argument, dirty-on-throw safety, rewind recovery via `list`/`history`, the typeface schema setFont requires, and the runtime quirks that bite first attempts. Skipping it produces broken output.',\n '',\n 'Reference docs, on demand:',\n '- `api({ path?, offset?, limit? })` — CE.SDK TypeScript signatures.',\n '- `guide({ path? })` — CE.SDK Vanilla JS reference docs / topic guides.',\n '',\n 'Design tools (after reading the handbook):',\n '- `edit({ parent, code, title?, note? })` — mutate the canvas; `parent` is REQUIRED (revision id, or null for a new design).',\n '- `preview({ blockId, revision? })` — render a block to an inline image.',\n '- `export({ target, format, revision, blockId })` — render a block to pdf/image and write it into the workspace.',\n '- `save({ revision, outPath? })` — write a self-contained `.zip` archive (scene + assets) of a revision into the workspace.',\n '',\n 'Workspace tools (for resuming + navigating across designs):',\n '- `list()` — every design in the workspace, newest first.',\n '- `history({ revision })` — walk back to the root; how you re-ground after a rewind.',\n '- `inspect({ revision })` — read one revision in full, including the code that produced it (patterns, not block ids).',\n '- `rename({ revision, title })` — rename a design.',\n '- `open_editor({ revision? })` — URL the human can open to view the design.',\n '- `asset_search({ sourceId?, query? })` — browse asset sources; apply results in `edit`.'\n ]\n .concat(\n hasAssetGenerate\n ? [\n '',\n 'Asset generation:',\n \"- `asset_generate({ kind: 'image', prompt, width?, height? })` — opt-in (Local needs API_KEY); Hosted is cost-gated.\"\n ]\n : []\n )\n .join('\\n');\n\n // Build the resource map up-front: URI → rewritten markdown body. Cross-refs\n // inside doc content are rewritten to absolute MCP URIs so the model never\n // has to resolve a relative path against a doc dir.\n const rewrittenContent = new Map<string, string>();\n for (const [key, content] of Object.entries(assets)) {\n const m = key.match(DOC_FILE);\n if (!m) continue;\n const [, doc, rest] = m;\n const uri = `${scheme}://docs/${doc}/${rest}`;\n rewrittenContent.set(uri, rewriteSkillLinks(content, doc, scheme));\n }\n\n const server = new Server(\n { name: 'codesign', version: opts.buildId ?? 'dev' },\n {\n capabilities: { resources: {}, tools: {} },\n instructions\n }\n );\n\n // `resources/list` advertises only the per-skill SKILL.md (the entry\n // point), not the 392 nested cesdk-guide files. Sub-files stay readable\n // via `resources/read` against any cached URI, but skill content discovery\n // happens through the codesign_read_* tools — those are the universal\n // surface across MCP hosts.\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: Array.from(rewrittenContent.keys())\n .filter((uri) => uri.endsWith('/SKILL.md'))\n .map((uri) => ({\n uri,\n mimeType: 'text/markdown',\n name: uri.replace(`${scheme}://`, '')\n }))\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (req) => {\n const text = rewrittenContent.get(req.params.uri);\n if (text === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown resource: ${req.params.uri}`\n );\n }\n return {\n contents: [{ uri: req.params.uri, mimeType: 'text/markdown', text }]\n };\n });\n\n // Tools registered with the server: caller-supplied design tools + the\n // auto-generated skill-read tools (one per skill in the asset bundle).\n // The skill tools always come first so Level 1 wording in the design tool\n // descriptions (\"call codesign_read_codesign_system first\") points at\n // something the model already sees in tools/list.\n const tools = new Map<string, CodesignMcpToolRegistration>();\n for (const t of buildSkillReadTools(assets)) tools.set(t.name, t);\n for (const t of opts.tools ?? []) tools.set(t.name, t);\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: Array.from(tools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as never\n }))\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const t = tools.get(req.params.name);\n if (!t) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown tool: ${req.params.name}`\n );\n }\n return await t.call(req.params.arguments ?? {});\n });\n\n return server;\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema\n} from '@modelcontextprotocol/sdk/types.js';\nimport { rewriteSkillLinks } from './cross-ref.js';\nimport { buildSkillReadTools } from './skill-tools.js';\n\n/**\n * One tool the server exposes via tools/list + tools/call. Caller-supplied so\n * the engine-bound implementations live near the surface that owns the engine\n * (the demo passes its existing `createDesignTools` output adapted to this\n * shape; an external bootstrap could pass server-side equivalents).\n */\nexport interface CodesignMcpToolRegistration {\n name: string;\n description?: string;\n /** JSON Schema describing the tool's input arguments. */\n inputSchema: unknown;\n call: (args: unknown) => Promise<{\n content: Array<\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string }\n >;\n isError?: boolean;\n }>;\n}\n\n/**\n * One prompt (slash command) the server exposes via prompts/list +\n * prompts/get. Mirrors `CodesignMcpToolRegistration`'s caller-supplied shape.\n */\nexport interface CodesignMcpPromptRegistration {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n get: (args: Record<string, string> | undefined) => {\n description?: string;\n messages: Array<{\n role: 'user' | 'assistant';\n content: { type: 'text'; text: string };\n }>;\n };\n}\n\nexport interface CodesignMcpServerOptions {\n /**\n * Asset bundle. Keys are forward-slash paths under one of:\n * \"docs/<doc>/SKILL.md\" — entry point for handbook / api / guide\n * \"docs/<doc>/<file>\" — sub-files reachable via the doc tools\n * Values are file contents.\n */\n assets: Record<string, string>;\n /** URI scheme for resources. Default: \"codesign\". */\n resourceScheme?: string;\n /** Tools to expose via tools/list + tools/call. */\n tools?: CodesignMcpToolRegistration[];\n /** MCP prompts (slash commands). Advertised only when at least one is passed. */\n prompts?: CodesignMcpPromptRegistration[];\n /**\n * Build identity reported as `serverInfo.version` on `initialize`. A client\n * (the deploy smoketest) can assert it to confirm it's talking to the\n * just-deployed build, not a stale container. Default `'dev'`.\n */\n buildId?: string;\n}\n\nconst DOC_FILE = /^docs\\/([^/]+)\\/(.+)$/;\n\nexport function createCodesignMcpServer(\n opts: CodesignMcpServerOptions\n): Server {\n const scheme = opts.resourceScheme ?? 'codesign';\n const assets = opts.assets;\n\n // `instructions` is intentionally short — it tells the model what the\n // server is and STRONGLY directs it to call `codesign_read_codesign_system`\n // before its first edit. Skills are now first-class tools (not MCP\n // resources buried in an XML block), so discovery happens via tools/list\n // on every host without needing ReadMcpResourceTool support.\n //\n // Why short: Claude Code wraps MCP `instructions` in a <system-reminder>\n // and truncates long ones mid-stream. Keeping `instructions` under ~1.5 KB\n // ensures the whole pointer survives any sane truncation.\n //\n // The asset-generation hint is included only when an `asset_generate` tool\n // is actually registered (Local hides it without an API_KEY), so we never\n // advertise a tool the model can't see in tools/list.\n const hasAssetGenerate = (opts.tools ?? []).some(\n (t) => t.name === 'asset_generate'\n );\n const instructions = [\n 'This is the **codesign** MCP server. It hosts a CE.SDK CreativeEngine and a persistent design workspace (revision DAG) for vector design + print-quality PDF output.',\n '',\n '**Required first step** — before any `edit`, call `handbook` (no args). It teaches the mandatory `parent` argument, dirty-on-throw safety, rewind recovery via `list`/`history`, the typeface schema setFont requires, and the runtime quirks that bite first attempts. Skipping it produces broken output.',\n '',\n 'Reference docs, on demand:',\n '- `api({ path?, offset?, limit? })` — CE.SDK TypeScript signatures.',\n '- `guide({ path? })` — CE.SDK Vanilla JS reference docs / topic guides.',\n '',\n 'Design tools (after reading the handbook):',\n '- `edit({ parent, code, title?, note? })` — mutate the canvas; `parent` is REQUIRED (revision id, or null for a new design).',\n '- `preview({ blockId, revision })` — render a block to an inline image. `revision` is required (the id `edit` returned).',\n '- `export({ target, format, revision, blockId })` — render a block to pdf/image and write it into the workspace.',\n '- `save({ revision, outPath? })` — write a self-contained `.zip` archive (scene + assets) of a revision into the workspace.',\n '',\n 'Workspace tools (for resuming + navigating across designs):',\n '- `list()` — every design in the workspace, newest first.',\n '- `history({ revision })` — walk back to the root; how you re-ground after a rewind.',\n '- `inspect({ revision })` — read one revision in full, including the code that produced it (patterns, not block ids).',\n '- `rename({ revision, title })` — rename a design.',\n '- `view({ revision })` — URL the human can open to view the design.',\n '- `asset_search({ sourceId?, query? })` — browse asset sources; apply results in `edit`.'\n ]\n .concat(\n hasAssetGenerate\n ? [\n '',\n 'Asset generation:',\n \"- `asset_generate({ kind: 'image', prompt, width?, height? })` — opt-in (Local needs API_KEY); Hosted is cost-gated.\"\n ]\n : []\n )\n .join('\\n');\n\n // Build the resource map up-front: URI → rewritten markdown body. Cross-refs\n // inside doc content are rewritten to absolute MCP URIs so the model never\n // has to resolve a relative path against a doc dir.\n const rewrittenContent = new Map<string, string>();\n for (const [key, content] of Object.entries(assets)) {\n const m = key.match(DOC_FILE);\n if (!m) continue;\n const [, doc, rest] = m;\n const uri = `${scheme}://docs/${doc}/${rest}`;\n rewrittenContent.set(uri, rewriteSkillLinks(content, doc, scheme));\n }\n\n const server = new Server(\n { name: 'codesign', version: opts.buildId ?? 'dev' },\n {\n capabilities: {\n resources: {},\n tools: {},\n ...(opts.prompts && opts.prompts.length > 0 ? { prompts: {} } : {})\n },\n instructions\n }\n );\n\n // `resources/list` advertises only the per-skill SKILL.md (the entry\n // point), not the 392 nested cesdk-guide files. Sub-files stay readable\n // via `resources/read` against any cached URI, but skill content discovery\n // happens through the codesign_read_* tools — those are the universal\n // surface across MCP hosts.\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: Array.from(rewrittenContent.keys())\n .filter((uri) => uri.endsWith('/SKILL.md'))\n .map((uri) => ({\n uri,\n mimeType: 'text/markdown',\n name: uri.replace(`${scheme}://`, '')\n }))\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (req) => {\n const text = rewrittenContent.get(req.params.uri);\n if (text === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown resource: ${req.params.uri}`\n );\n }\n return {\n contents: [{ uri: req.params.uri, mimeType: 'text/markdown', text }]\n };\n });\n\n // Tools registered with the server: caller-supplied design tools + the\n // auto-generated skill-read tools (one per skill in the asset bundle).\n // The skill tools always come first so Level 1 wording in the design tool\n // descriptions (\"call codesign_read_codesign_system first\") points at\n // something the model already sees in tools/list.\n const tools = new Map<string, CodesignMcpToolRegistration>();\n for (const t of buildSkillReadTools(assets)) tools.set(t.name, t);\n for (const t of opts.tools ?? []) tools.set(t.name, t);\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: Array.from(tools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as never\n }))\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const t = tools.get(req.params.name);\n if (!t) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown tool: ${req.params.name}`\n );\n }\n return await t.call(req.params.arguments ?? {});\n });\n\n if (opts.prompts && opts.prompts.length > 0) {\n const prompts = new Map(opts.prompts.map((p) => [p.name, p]));\n server.setRequestHandler(ListPromptsRequestSchema, async () => ({\n prompts: Array.from(prompts.values()).map((p) => ({\n name: p.name,\n ...(p.description ? { description: p.description } : {}),\n ...(p.arguments ? { arguments: p.arguments } : {})\n }))\n }));\n server.setRequestHandler(GetPromptRequestSchema, async (req) => {\n const p = prompts.get(req.params.name);\n if (!p)\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown prompt: ${req.params.name}`\n );\n return p.get(req.params.arguments);\n });\n }\n\n return server;\n}\n"]}