@imgly/codesign-mcp 0.1.6 → 0.1.7

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 (360) hide show
  1. package/README.md +5 -10
  2. package/dist/cli/http.d.ts.map +1 -1
  3. package/dist/cli/http.js +12 -2
  4. package/dist/cli/http.js.map +1 -1
  5. package/dist/cli/stdio.d.ts.map +1 -1
  6. package/dist/cli/stdio.js +103 -35
  7. package/dist/cli/stdio.js.map +1 -1
  8. package/dist/config.d.ts +11 -2
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +12 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/diagnostics/events.d.ts +6 -0
  13. package/dist/diagnostics/events.d.ts.map +1 -1
  14. package/dist/diagnostics/events.js.map +1 -1
  15. package/dist/engine/cesdk-adapter.d.ts +5 -1
  16. package/dist/engine/cesdk-adapter.d.ts.map +1 -1
  17. package/dist/engine/cesdk-adapter.js +8 -1
  18. package/dist/engine/cesdk-adapter.js.map +1 -1
  19. package/dist/engine/engine-capacity.d.ts +16 -0
  20. package/dist/engine/engine-capacity.d.ts.map +1 -0
  21. package/dist/engine/engine-capacity.js +48 -0
  22. package/dist/engine/engine-capacity.js.map +1 -0
  23. package/dist/engine/engine-slots.d.ts +48 -0
  24. package/dist/engine/engine-slots.d.ts.map +1 -0
  25. package/dist/engine/engine-slots.js +182 -0
  26. package/dist/engine/engine-slots.js.map +1 -0
  27. package/dist/tools/engine.d.ts +13 -0
  28. package/dist/tools/engine.d.ts.map +1 -1
  29. package/dist/tools/engine.js +28 -0
  30. package/dist/tools/engine.js.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/dashboard/router.d.ts +0 -3
  33. package/dist/dashboard/router.d.ts.map +0 -1
  34. package/dist/dashboard/router.js +0 -19
  35. package/dist/dashboard/router.js.map +0 -1
  36. package/dist/dashboard/spa-assets.generated.d.ts +0 -2
  37. package/dist/dashboard/spa-assets.generated.d.ts.map +0 -1
  38. package/dist/dashboard/spa-assets.generated.js +0 -220
  39. package/dist/dashboard/spa-assets.generated.js.map +0 -1
  40. package/dist/metrics/mount.d.ts +0 -11
  41. package/dist/metrics/mount.d.ts.map +0 -1
  42. package/dist/metrics/mount.js +0 -13
  43. package/dist/metrics/mount.js.map +0 -1
  44. package/dist/metrics/recorder.d.ts +0 -55
  45. package/dist/metrics/recorder.d.ts.map +0 -1
  46. package/dist/metrics/recorder.js +0 -97
  47. package/dist/metrics/recorder.js.map +0 -1
  48. package/dist/test/scripts/changelog-add.d.ts +0 -8
  49. package/dist/test/scripts/changelog-add.d.ts.map +0 -1
  50. package/dist/test/scripts/changelog-add.js +0 -50
  51. package/dist/test/scripts/changelog-add.js.map +0 -1
  52. package/dist/test/scripts/changelog-collate.d.ts +0 -38
  53. package/dist/test/scripts/changelog-collate.d.ts.map +0 -1
  54. package/dist/test/scripts/changelog-collate.js +0 -171
  55. package/dist/test/scripts/changelog-collate.js.map +0 -1
  56. package/dist/test/test/changelog.test.d.ts +0 -2
  57. package/dist/test/test/changelog.test.d.ts.map +0 -1
  58. package/dist/test/test/changelog.test.js +0 -293
  59. package/dist/test/test/changelog.test.js.map +0 -1
  60. package/dist/test/test/cli/dispatcher.test.d.ts +0 -2
  61. package/dist/test/test/cli/dispatcher.test.d.ts.map +0 -1
  62. package/dist/test/test/cli/dispatcher.test.js +0 -54
  63. package/dist/test/test/cli/dispatcher.test.js.map +0 -1
  64. package/dist/test/test/cli/redirect-console.test.d.ts +0 -2
  65. package/dist/test/test/cli/redirect-console.test.d.ts.map +0 -1
  66. package/dist/test/test/cli/redirect-console.test.js +0 -78
  67. package/dist/test/test/cli/redirect-console.test.js.map +0 -1
  68. package/dist/test/test/cli/resolve-license.test.d.ts +0 -2
  69. package/dist/test/test/cli/resolve-license.test.d.ts.map +0 -1
  70. package/dist/test/test/cli/resolve-license.test.js +0 -29
  71. package/dist/test/test/cli/resolve-license.test.js.map +0 -1
  72. package/dist/test/test/cli/stdio-diagnostics.test.d.ts +0 -2
  73. package/dist/test/test/cli/stdio-diagnostics.test.d.ts.map +0 -1
  74. package/dist/test/test/cli/stdio-diagnostics.test.js +0 -31
  75. package/dist/test/test/cli/stdio-diagnostics.test.js.map +0 -1
  76. package/dist/test/test/cli/stdio-license.test.d.ts +0 -2
  77. package/dist/test/test/cli/stdio-license.test.d.ts.map +0 -1
  78. package/dist/test/test/cli/stdio-license.test.js +0 -19
  79. package/dist/test/test/cli/stdio-license.test.js.map +0 -1
  80. package/dist/test/test/config.test.d.ts +0 -2
  81. package/dist/test/test/config.test.d.ts.map +0 -1
  82. package/dist/test/test/config.test.js +0 -95
  83. package/dist/test/test/config.test.js.map +0 -1
  84. package/dist/test/test/cross-ref.test.d.ts +0 -2
  85. package/dist/test/test/cross-ref.test.d.ts.map +0 -1
  86. package/dist/test/test/cross-ref.test.js +0 -39
  87. package/dist/test/test/cross-ref.test.js.map +0 -1
  88. package/dist/test/test/diagnostics/bundle.test.d.ts +0 -2
  89. package/dist/test/test/diagnostics/bundle.test.d.ts.map +0 -1
  90. package/dist/test/test/diagnostics/bundle.test.js +0 -53
  91. package/dist/test/test/diagnostics/bundle.test.js.map +0 -1
  92. package/dist/test/test/diagnostics/compose.test.d.ts +0 -2
  93. package/dist/test/test/diagnostics/compose.test.d.ts.map +0 -1
  94. package/dist/test/test/diagnostics/compose.test.js +0 -31
  95. package/dist/test/test/diagnostics/compose.test.js.map +0 -1
  96. package/dist/test/test/diagnostics/file-sink.test.d.ts +0 -2
  97. package/dist/test/test/diagnostics/file-sink.test.d.ts.map +0 -1
  98. package/dist/test/test/diagnostics/file-sink.test.js +0 -113
  99. package/dist/test/test/diagnostics/file-sink.test.js.map +0 -1
  100. package/dist/test/test/diagnostics/redact.test.d.ts +0 -2
  101. package/dist/test/test/diagnostics/redact.test.d.ts.map +0 -1
  102. package/dist/test/test/diagnostics/redact.test.js +0 -59
  103. package/dist/test/test/diagnostics/redact.test.js.map +0 -1
  104. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts +0 -2
  105. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts.map +0 -1
  106. package/dist/test/test/diagnostics/tool-call-adapter.test.js +0 -34
  107. package/dist/test/test/diagnostics/tool-call-adapter.test.js.map +0 -1
  108. package/dist/test/test/engine/asset-sources.test.d.ts +0 -2
  109. package/dist/test/test/engine/asset-sources.test.d.ts.map +0 -1
  110. package/dist/test/test/engine/asset-sources.test.js +0 -100
  111. package/dist/test/test/engine/asset-sources.test.js.map +0 -1
  112. package/dist/test/test/engine/cesdk-adapter.test.d.ts +0 -2
  113. package/dist/test/test/engine/cesdk-adapter.test.d.ts.map +0 -1
  114. package/dist/test/test/engine/cesdk-adapter.test.js +0 -32
  115. package/dist/test/test/engine/cesdk-adapter.test.js.map +0 -1
  116. package/dist/test/test/engine/mutex.test.d.ts +0 -2
  117. package/dist/test/test/engine/mutex.test.d.ts.map +0 -1
  118. package/dist/test/test/engine/mutex.test.js +0 -63
  119. package/dist/test/test/engine/mutex.test.js.map +0 -1
  120. package/dist/test/test/engine/session.test.d.ts +0 -2
  121. package/dist/test/test/engine/session.test.d.ts.map +0 -1
  122. package/dist/test/test/engine/session.test.js +0 -159
  123. package/dist/test/test/engine/session.test.js.map +0 -1
  124. package/dist/test/test/engine/unsplash-source.test.d.ts +0 -2
  125. package/dist/test/test/engine/unsplash-source.test.d.ts.map +0 -1
  126. package/dist/test/test/engine/unsplash-source.test.js +0 -129
  127. package/dist/test/test/engine/unsplash-source.test.js.map +0 -1
  128. package/dist/test/test/engine/uri-resolver.test.d.ts +0 -2
  129. package/dist/test/test/engine/uri-resolver.test.d.ts.map +0 -1
  130. package/dist/test/test/engine/uri-resolver.test.js +0 -38
  131. package/dist/test/test/engine/uri-resolver.test.js.map +0 -1
  132. package/dist/test/test/gateway/hosted-stub.test.d.ts +0 -2
  133. package/dist/test/test/gateway/hosted-stub.test.d.ts.map +0 -1
  134. package/dist/test/test/gateway/hosted-stub.test.js +0 -11
  135. package/dist/test/test/gateway/hosted-stub.test.js.map +0 -1
  136. package/dist/test/test/gateway/local.test.d.ts +0 -2
  137. package/dist/test/test/gateway/local.test.d.ts.map +0 -1
  138. package/dist/test/test/gateway/local.test.js +0 -29
  139. package/dist/test/test/gateway/local.test.js.map +0 -1
  140. package/dist/test/test/handbook-contract.test.d.ts +0 -2
  141. package/dist/test/test/handbook-contract.test.d.ts.map +0 -1
  142. package/dist/test/test/handbook-contract.test.js +0 -79
  143. package/dist/test/test/handbook-contract.test.js.map +0 -1
  144. package/dist/test/test/http/server.test.d.ts +0 -2
  145. package/dist/test/test/http/server.test.d.ts.map +0 -1
  146. package/dist/test/test/http/server.test.js +0 -338
  147. package/dist/test/test/http/server.test.js.map +0 -1
  148. package/dist/test/test/http/session-id-header.test.d.ts +0 -2
  149. package/dist/test/test/http/session-id-header.test.d.ts.map +0 -1
  150. package/dist/test/test/http/session-id-header.test.js +0 -76
  151. package/dist/test/test/http/session-id-header.test.js.map +0 -1
  152. package/dist/test/test/http/session-manager.test.d.ts +0 -2
  153. package/dist/test/test/http/session-manager.test.d.ts.map +0 -1
  154. package/dist/test/test/http/session-manager.test.js +0 -97
  155. package/dist/test/test/http/session-manager.test.js.map +0 -1
  156. package/dist/test/test/http/workspace-auth.test.d.ts +0 -2
  157. package/dist/test/test/http/workspace-auth.test.d.ts.map +0 -1
  158. package/dist/test/test/http/workspace-auth.test.js +0 -40
  159. package/dist/test/test/http/workspace-auth.test.js.map +0 -1
  160. package/dist/test/test/metrics/mount.test.d.ts +0 -2
  161. package/dist/test/test/metrics/mount.test.d.ts.map +0 -1
  162. package/dist/test/test/metrics/mount.test.js +0 -59
  163. package/dist/test/test/metrics/mount.test.js.map +0 -1
  164. package/dist/test/test/metrics/recorder.test.d.ts +0 -2
  165. package/dist/test/test/metrics/recorder.test.d.ts.map +0 -1
  166. package/dist/test/test/metrics/recorder.test.js +0 -118
  167. package/dist/test/test/metrics/recorder.test.js.map +0 -1
  168. package/dist/test/test/revisions/hash.test.d.ts +0 -2
  169. package/dist/test/test/revisions/hash.test.d.ts.map +0 -1
  170. package/dist/test/test/revisions/hash.test.js +0 -33
  171. package/dist/test/test/revisions/hash.test.js.map +0 -1
  172. package/dist/test/test/revisions/store.test.d.ts +0 -2
  173. package/dist/test/test/revisions/store.test.d.ts.map +0 -1
  174. package/dist/test/test/revisions/store.test.js +0 -315
  175. package/dist/test/test/revisions/store.test.js.map +0 -1
  176. package/dist/test/test/server.metrics.test.d.ts +0 -2
  177. package/dist/test/test/server.metrics.test.d.ts.map +0 -1
  178. package/dist/test/test/server.metrics.test.js +0 -87
  179. package/dist/test/test/server.metrics.test.js.map +0 -1
  180. package/dist/test/test/server.test.d.ts +0 -2
  181. package/dist/test/test/server.test.d.ts.map +0 -1
  182. package/dist/test/test/server.test.js +0 -146
  183. package/dist/test/test/server.test.js.map +0 -1
  184. package/dist/test/test/skill-tools.test.d.ts +0 -2
  185. package/dist/test/test/skill-tools.test.d.ts.map +0 -1
  186. package/dist/test/test/skill-tools.test.js +0 -84
  187. package/dist/test/test/skill-tools.test.js.map +0 -1
  188. package/dist/test/test/smoke.test.d.ts +0 -2
  189. package/dist/test/test/smoke.test.d.ts.map +0 -1
  190. package/dist/test/test/smoke.test.js +0 -8
  191. package/dist/test/test/smoke.test.js.map +0 -1
  192. package/dist/test/test/tools/asset-generate.test.d.ts +0 -2
  193. package/dist/test/test/tools/asset-generate.test.d.ts.map +0 -1
  194. package/dist/test/test/tools/asset-generate.test.js +0 -66
  195. package/dist/test/test/tools/asset-generate.test.js.map +0 -1
  196. package/dist/test/test/tools/asset-search.test.d.ts +0 -2
  197. package/dist/test/test/tools/asset-search.test.d.ts.map +0 -1
  198. package/dist/test/test/tools/asset-search.test.js +0 -121
  199. package/dist/test/test/tools/asset-search.test.js.map +0 -1
  200. package/dist/test/test/tools/edit-harness.d.ts +0 -14
  201. package/dist/test/test/tools/edit-harness.d.ts.map +0 -1
  202. package/dist/test/test/tools/edit-harness.js +0 -30
  203. package/dist/test/test/tools/edit-harness.js.map +0 -1
  204. package/dist/test/test/tools/edit.telemetry.test.d.ts +0 -2
  205. package/dist/test/test/tools/edit.telemetry.test.d.ts.map +0 -1
  206. package/dist/test/test/tools/edit.telemetry.test.js +0 -46
  207. package/dist/test/test/tools/edit.telemetry.test.js.map +0 -1
  208. package/dist/test/test/tools/edit.test.d.ts +0 -2
  209. package/dist/test/test/tools/edit.test.d.ts.map +0 -1
  210. package/dist/test/test/tools/edit.test.js +0 -267
  211. package/dist/test/test/tools/edit.test.js.map +0 -1
  212. package/dist/test/test/tools/export-harness.d.ts +0 -13
  213. package/dist/test/test/tools/export-harness.d.ts.map +0 -1
  214. package/dist/test/test/tools/export-harness.js +0 -27
  215. package/dist/test/test/tools/export-harness.js.map +0 -1
  216. package/dist/test/test/tools/export.telemetry.test.d.ts +0 -2
  217. package/dist/test/test/tools/export.telemetry.test.d.ts.map +0 -1
  218. package/dist/test/test/tools/export.telemetry.test.js +0 -65
  219. package/dist/test/test/tools/export.telemetry.test.js.map +0 -1
  220. package/dist/test/test/tools/export.test.d.ts +0 -2
  221. package/dist/test/test/tools/export.test.d.ts.map +0 -1
  222. package/dist/test/test/tools/export.test.js +0 -174
  223. package/dist/test/test/tools/export.test.js.map +0 -1
  224. package/dist/test/test/tools/history.test.d.ts +0 -2
  225. package/dist/test/test/tools/history.test.d.ts.map +0 -1
  226. package/dist/test/test/tools/history.test.js +0 -29
  227. package/dist/test/test/tools/history.test.js.map +0 -1
  228. package/dist/test/test/tools/import.test.d.ts +0 -2
  229. package/dist/test/test/tools/import.test.d.ts.map +0 -1
  230. package/dist/test/test/tools/import.test.js +0 -122
  231. package/dist/test/test/tools/import.test.js.map +0 -1
  232. package/dist/test/test/tools/index.test.d.ts +0 -2
  233. package/dist/test/test/tools/index.test.d.ts.map +0 -1
  234. package/dist/test/test/tools/index.test.js +0 -117
  235. package/dist/test/test/tools/index.test.js.map +0 -1
  236. package/dist/test/test/tools/inspect.test.d.ts +0 -2
  237. package/dist/test/test/tools/inspect.test.d.ts.map +0 -1
  238. package/dist/test/test/tools/inspect.test.js +0 -62
  239. package/dist/test/test/tools/inspect.test.js.map +0 -1
  240. package/dist/test/test/tools/list.test.d.ts +0 -2
  241. package/dist/test/test/tools/list.test.d.ts.map +0 -1
  242. package/dist/test/test/tools/list.test.js +0 -23
  243. package/dist/test/test/tools/list.test.js.map +0 -1
  244. package/dist/test/test/tools/load.integration.test.d.ts +0 -2
  245. package/dist/test/test/tools/load.integration.test.d.ts.map +0 -1
  246. package/dist/test/test/tools/load.integration.test.js +0 -165
  247. package/dist/test/test/tools/load.integration.test.js.map +0 -1
  248. package/dist/test/test/tools/load.test.d.ts +0 -2
  249. package/dist/test/test/tools/load.test.d.ts.map +0 -1
  250. package/dist/test/test/tools/load.test.js +0 -387
  251. package/dist/test/test/tools/load.test.js.map +0 -1
  252. package/dist/test/test/tools/metrics.test.d.ts +0 -2
  253. package/dist/test/test/tools/metrics.test.d.ts.map +0 -1
  254. package/dist/test/test/tools/metrics.test.js +0 -29
  255. package/dist/test/test/tools/metrics.test.js.map +0 -1
  256. package/dist/test/test/tools/open-editor.test.d.ts +0 -2
  257. package/dist/test/test/tools/open-editor.test.d.ts.map +0 -1
  258. package/dist/test/test/tools/open-editor.test.js +0 -53
  259. package/dist/test/test/tools/open-editor.test.js.map +0 -1
  260. package/dist/test/test/tools/preview-harness.d.ts +0 -9
  261. package/dist/test/test/tools/preview-harness.d.ts.map +0 -1
  262. package/dist/test/test/tools/preview-harness.js +0 -23
  263. package/dist/test/test/tools/preview-harness.js.map +0 -1
  264. package/dist/test/test/tools/preview.telemetry.test.d.ts +0 -2
  265. package/dist/test/test/tools/preview.telemetry.test.d.ts.map +0 -1
  266. package/dist/test/test/tools/preview.telemetry.test.js +0 -36
  267. package/dist/test/test/tools/preview.telemetry.test.js.map +0 -1
  268. package/dist/test/test/tools/preview.test.d.ts +0 -2
  269. package/dist/test/test/tools/preview.test.d.ts.map +0 -1
  270. package/dist/test/test/tools/preview.test.js +0 -75
  271. package/dist/test/test/tools/preview.test.js.map +0 -1
  272. package/dist/test/test/tools/rename.test.d.ts +0 -2
  273. package/dist/test/test/tools/rename.test.d.ts.map +0 -1
  274. package/dist/test/test/tools/rename.test.js +0 -16
  275. package/dist/test/test/tools/rename.test.js.map +0 -1
  276. package/dist/test/test/tools/save.test.d.ts +0 -2
  277. package/dist/test/test/tools/save.test.d.ts.map +0 -1
  278. package/dist/test/test/tools/save.test.js +0 -137
  279. package/dist/test/test/tools/save.test.js.map +0 -1
  280. package/dist/test/test/tools/schema-best-practices.d.ts +0 -13
  281. package/dist/test/test/tools/schema-best-practices.d.ts.map +0 -1
  282. package/dist/test/test/tools/schema-best-practices.js +0 -169
  283. package/dist/test/test/tools/schema-best-practices.js.map +0 -1
  284. package/dist/test/test/tools/schema-best-practices.test.d.ts +0 -3
  285. package/dist/test/test/tools/schema-best-practices.test.d.ts.map +0 -1
  286. package/dist/test/test/tools/schema-best-practices.test.js +0 -194
  287. package/dist/test/test/tools/schema-best-practices.test.js.map +0 -1
  288. package/dist/test/test/tools/spans.test.d.ts +0 -2
  289. package/dist/test/test/tools/spans.test.d.ts.map +0 -1
  290. package/dist/test/test/tools/spans.test.js +0 -84
  291. package/dist/test/test/tools/spans.test.js.map +0 -1
  292. package/dist/test/test/viewer/broadcaster.test.d.ts +0 -2
  293. package/dist/test/test/viewer/broadcaster.test.d.ts.map +0 -1
  294. package/dist/test/test/viewer/broadcaster.test.js +0 -53
  295. package/dist/test/test/viewer/broadcaster.test.js.map +0 -1
  296. package/dist/test/test/viewer/cesdk-cdn.test.d.ts +0 -2
  297. package/dist/test/test/viewer/cesdk-cdn.test.d.ts.map +0 -1
  298. package/dist/test/test/viewer/cesdk-cdn.test.js +0 -19
  299. package/dist/test/test/viewer/cesdk-cdn.test.js.map +0 -1
  300. package/dist/test/test/viewer/config-route.test.d.ts +0 -2
  301. package/dist/test/test/viewer/config-route.test.d.ts.map +0 -1
  302. package/dist/test/test/viewer/config-route.test.js +0 -28
  303. package/dist/test/test/viewer/config-route.test.js.map +0 -1
  304. package/dist/test/test/viewer/resource-route.test.d.ts +0 -2
  305. package/dist/test/test/viewer/resource-route.test.d.ts.map +0 -1
  306. package/dist/test/test/viewer/resource-route.test.js +0 -70
  307. package/dist/test/test/viewer/resource-route.test.js.map +0 -1
  308. package/dist/test/test/viewer/router.test.d.ts +0 -2
  309. package/dist/test/test/viewer/router.test.d.ts.map +0 -1
  310. package/dist/test/test/viewer/router.test.js +0 -227
  311. package/dist/test/test/viewer/router.test.js.map +0 -1
  312. package/dist/test/test/viewer/session-mount.test.d.ts +0 -2
  313. package/dist/test/test/viewer/session-mount.test.d.ts.map +0 -1
  314. package/dist/test/test/viewer/session-mount.test.js +0 -52
  315. package/dist/test/test/viewer/session-mount.test.js.map +0 -1
  316. package/dist/test/test/viewer/spa-version.test.d.ts +0 -2
  317. package/dist/test/test/viewer/spa-version.test.d.ts.map +0 -1
  318. package/dist/test/test/viewer/spa-version.test.js +0 -14
  319. package/dist/test/test/viewer/spa-version.test.js.map +0 -1
  320. package/dist/test/test/viewer/url-builder.test.d.ts +0 -2
  321. package/dist/test/test/viewer/url-builder.test.d.ts.map +0 -1
  322. package/dist/test/test/viewer/url-builder.test.js +0 -98
  323. package/dist/test/test/viewer/url-builder.test.js.map +0 -1
  324. package/dist/test/test/workspace/content-hash.test.d.ts +0 -2
  325. package/dist/test/test/workspace/content-hash.test.d.ts.map +0 -1
  326. package/dist/test/test/workspace/content-hash.test.js +0 -17
  327. package/dist/test/test/workspace/content-hash.test.js.map +0 -1
  328. package/dist/test/test/workspace/hosted-stub.test.d.ts +0 -2
  329. package/dist/test/test/workspace/hosted-stub.test.d.ts.map +0 -1
  330. package/dist/test/test/workspace/hosted-stub.test.js +0 -30
  331. package/dist/test/test/workspace/hosted-stub.test.js.map +0 -1
  332. package/dist/test/test/workspace/in-memory.test.d.ts +0 -2
  333. package/dist/test/test/workspace/in-memory.test.d.ts.map +0 -1
  334. package/dist/test/test/workspace/in-memory.test.js +0 -4
  335. package/dist/test/test/workspace/in-memory.test.js.map +0 -1
  336. package/dist/test/test/workspace/node-fs.test.d.ts +0 -2
  337. package/dist/test/test/workspace/node-fs.test.d.ts.map +0 -1
  338. package/dist/test/test/workspace/node-fs.test.js +0 -20
  339. package/dist/test/test/workspace/node-fs.test.js.map +0 -1
  340. package/dist/test/test/workspace/s3.test.d.ts +0 -2
  341. package/dist/test/test/workspace/s3.test.d.ts.map +0 -1
  342. package/dist/test/test/workspace/s3.test.js +0 -153
  343. package/dist/test/test/workspace/s3.test.js.map +0 -1
  344. package/dist/test/test/workspace/scoped.test.d.ts +0 -2
  345. package/dist/test/test/workspace/scoped.test.d.ts.map +0 -1
  346. package/dist/test/test/workspace/scoped.test.js +0 -59
  347. package/dist/test/test/workspace/scoped.test.js.map +0 -1
  348. package/dist/test/test/workspace/shared-contract.d.ts +0 -3
  349. package/dist/test/test/workspace/shared-contract.d.ts.map +0 -1
  350. package/dist/test/test/workspace/shared-contract.js +0 -92
  351. package/dist/test/test/workspace/shared-contract.js.map +0 -1
  352. package/dist/test/tsconfig.test.tsbuildinfo +0 -1
  353. package/dist/test/vitest.config.d.ts +0 -3
  354. package/dist/test/vitest.config.d.ts.map +0 -1
  355. package/dist/test/vitest.config.js +0 -8
  356. package/dist/test/vitest.config.js.map +0 -1
  357. package/dist/tools/metrics.d.ts +0 -11
  358. package/dist/tools/metrics.d.ts.map +0 -1
  359. package/dist/tools/metrics.js +0 -28
  360. package/dist/tools/metrics.js.map +0 -1
package/README.md CHANGED
@@ -23,22 +23,17 @@ for the exact per-host snippets — all spawn
23
23
  Set `CESDK_LICENSE` in the server's `env` to use your own CE.SDK node license;
24
24
  during the internal beta a 30-day trial is bundled, so it's optional.
25
25
 
26
- ### From a GitHub Release tarball (offline / air-gapped)
26
+ ### Offline / air-gapped install
27
27
 
28
- Releases also ship a self-contained tarball with a date-based, commit-derived
29
- version `0.1.0-<YYYYMMDD>-g<shortSHA>`:
28
+ Fetch the published package as a self-contained tarball on a connected machine,
29
+ then move it to the target and install it there:
30
30
 
31
31
  ```bash
32
- npm i -g https://github.com/imgly/imgly-codesign/releases/download/<tag>/imgly-codesign-mcp-0.1.0-<YYYYMMDD>-g<sha>.tgz
32
+ npm pack @imgly/codesign-mcp # writes imgly-codesign-mcp-<version>.tgz
33
+ npm i -g ./imgly-codesign-mcp-<version>.tgz # on the air-gapped machine
33
34
  codesign-mcp --help
34
35
  ```
35
36
 
36
- Build the tarball locally (output in `<repo>/release/`):
37
-
38
- ```bash
39
- pnpm run release:tarball # from the repo root
40
- ```
41
-
42
37
  ## Run
43
38
 
44
39
  One bin, two transports:
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/cli/http.ts"],"names":[],"mappings":"AA0EA,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4L5D"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/cli/http.ts"],"names":[],"mappings":"AA6EA,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyM5D"}
package/dist/cli/http.js CHANGED
@@ -16,7 +16,7 @@ import { LocalGatewayClient } from '../gateway/local.js';
16
16
  import { createCodesignHttpServer } from '../http/server.js';
17
17
  import { makeWorkspaceResolver } from '../http/workspace-auth.js';
18
18
  import { RevisionStore } from '../revisions/store.js';
19
- import { ensureHeadlessEngine } from '../tools/engine.js';
19
+ import { disposeHeadlessEngine, ensureHeadlessEngine } from '../tools/engine.js';
20
20
  import { buildDesignToolRegistrations } from '../tools/index.js';
21
21
  import { createViewerRouter, LocalViewerUrlBuilder, mountSessionViewer, SseBroadcaster, viewerBaseFor } from '../viewer/index.js';
22
22
  import { PresignedR2UrlBuilder } from '../viewer/presigned-r2-url-builder.js';
@@ -79,7 +79,7 @@ export async function runHttp(_argv) {
79
79
  const app = await createCodesignHttpServer({
80
80
  assets: loadNodeAssets(),
81
81
  buildId: cfg.buildId,
82
- maxSessions: cfg.maxEngines,
82
+ maxSessions: cfg.maxHttpEngines,
83
83
  idleTimeoutMs: cfg.idleTimeoutMs,
84
84
  resolveWorkspaceId,
85
85
  trustedSessionIdHeader: 'x-mcp-session-id',
@@ -203,6 +203,16 @@ export async function runHttp(_argv) {
203
203
  server.closeAllConnections();
204
204
  broadcaster.closeAll();
205
205
  await app.shutdown();
206
+ // The engine is a process singleton shared by every session (see
207
+ // ensureHeadlessEngine), so per-session destroy can't dispose it — the
208
+ // process exit does. Release it deterministically here. When engines become
209
+ // a fixed pool, disposal moves to per-slot teardown inside app.shutdown().
210
+ try {
211
+ await disposeHeadlessEngine();
212
+ }
213
+ catch (err) {
214
+ process.stderr.write(`codesign-mcp http: engine dispose failed — ${err instanceof Error ? err.message : String(err)}\n`);
215
+ }
206
216
  process.exit(0);
207
217
  };
208
218
  process.on('SIGINT', () => void shutdown('SIGINT'));
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/cli/http.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/cli/http.ts\n//\n// codesign-mcp http — Streamable HTTP transport. Composition root: loads\n// IMGLY_* config, selects the storage backend (local|s3), builds a\n// PER-SESSION ScopedWorkspaceFS + RevisionStore, wires the workspace resolver,\n// and mounts the viewer: fixed `/viewer` in `single` mode, multi-tenant\n// `/v/<sid>` in `session` mode (disabled in `header` mode — follow-up).\nimport { loadConfig, type ServerConfig } from '../config.js';\nimport { ensureEngineAssetSources } from '../engine/asset-sources.js';\nimport { cesdkAdapter } from '../engine/cesdk-adapter.js';\nimport { Mutex } from '../engine/mutex.js';\nimport { EngineSession } from '../engine/session.js';\nimport { registerUnsplashSource } from '../engine/unsplash-source.js';\nimport { registerWorkspaceUriResolver } from '../engine/uri-resolver.js';\nimport { LocalGatewayClient } from '../gateway/local.js';\nimport { createCodesignHttpServer } from '../http/server.js';\nimport { makeWorkspaceResolver } from '../http/workspace-auth.js';\nimport { RevisionStore } from '../revisions/store.js';\nimport { ensureHeadlessEngine } from '../tools/engine.js';\nimport { buildDesignToolRegistrations } from '../tools/index.js';\nimport {\n createViewerRouter,\n LocalViewerUrlBuilder,\n mountSessionViewer,\n SseBroadcaster,\n viewerBaseFor\n} from '../viewer/index.js';\nimport { PresignedR2UrlBuilder } from '../viewer/presigned-r2-url-builder.js';\nimport { NodeWorkspaceFS } from '../workspace/node-fs.js';\nimport { S3WorkspaceFS } from '../workspace/s3.js';\nimport { ScopedWorkspaceFS } from '../workspace/scoped.js';\nimport { loadNodeAssets } from '../assets/load-node.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\n\nconst HOST = '0.0.0.0'; // bind all interfaces; TLS/routing is the front's job\n\ninterface S3BackendInfo {\n /** The real S3Client — used to build per-session presigned download URLs. */\n client: import('@aws-sdk/client-s3').S3Client;\n bucket: string;\n}\n\ninterface BackendResult {\n rawFs: WorkspaceFS;\n /** Present only when the s3 backend is active. */\n s3?: S3BackendInfo;\n}\n\nasync function buildBackend(cfg: ServerConfig): Promise<BackendResult> {\n if (cfg.workspace.backend === 's3') {\n const { S3Client } = await import('@aws-sdk/client-s3');\n const s3 = cfg.workspace.s3;\n if (!s3)\n throw new Error('config: s3 backend selected but s3 config missing');\n const client = new S3Client({\n region: s3.region,\n ...(s3.endpoint ? { endpoint: s3.endpoint, forcePathStyle: true } : {}),\n ...(s3.accessKeyId && s3.secretAccessKey\n ? {\n credentials: {\n accessKeyId: s3.accessKeyId,\n secretAccessKey: s3.secretAccessKey\n }\n }\n : {})\n });\n return {\n rawFs: new S3WorkspaceFS({ client, bucket: s3.bucket }),\n s3: { client, bucket: s3.bucket }\n };\n }\n return { rawFs: new NodeWorkspaceFS(cfg.workspace.dir ?? '/data') };\n}\n\nexport async function runHttp(_argv: string[]): Promise<void> {\n let cfg: ServerConfig;\n try {\n cfg = loadConfig();\n } catch (err) {\n process.stderr.write(\n `codesign-mcp http: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n process.exit(1);\n }\n\n const { buildDiagnostics } = await import('./stdio.js');\n const { telemetry } = buildDiagnostics();\n\n const license = cfg.cesdkLicense;\n const { rawFs, s3: s3Backend } = await buildBackend(cfg);\n const broadcaster = new SseBroadcaster();\n const gateway = new LocalGatewayClient({\n apiKey: cfg.gateway.apiKey,\n ...(cfg.gateway.baseUrl ? { endpoint: cfg.gateway.baseUrl } : {})\n });\n // ensureHeadlessEngine is a process singleton, so EVERY session's tools\n // operate on the same CE.SDK engine. This one lock — created once here, NOT\n // per session — serializes all engine access across sessions so two clients\n // editing concurrently can't corrupt each other's scene. When the engine\n // becomes a per-session/per-workspace pool, this moves to be one lock per\n // engine (per pool slot) so distinct engines run in parallel.\n const engineLock = new Mutex();\n const resolveWorkspaceId = makeWorkspaceResolver(cfg);\n\n const app = await createCodesignHttpServer({\n assets: loadNodeAssets(),\n buildId: cfg.buildId,\n maxSessions: cfg.maxEngines,\n idleTimeoutMs: cfg.idleTimeoutMs,\n resolveWorkspaceId,\n trustedSessionIdHeader: 'x-mcp-session-id',\n ...(cfg.viewerCorsOrigins\n ? { cors: { origin: cfg.viewerCorsOrigins } }\n : {}),\n createSession: async ({ sessionId, workspaceId }) => {\n const scopedFs = new ScopedWorkspaceFS(rawFs, workspaceId);\n const store = new RevisionStore(scopedFs);\n const engine = await ensureHeadlessEngine(license);\n // Idempotent (engine is a process singleton) — only the first successful registration pays the CDN fetch.\n await ensureEngineAssetSources(engine);\n // Demo `unsplash` custom source (synchronous, idempotent) so asset_search\n // has a real remote source alongside the CDN ones. Public IMG.LY proxy.\n registerUnsplashSource(engine);\n // Per-session viewer base: session mode lives under /v/<sid> (reachable\n // through the CF Worker when IMGLY_PUBLIC_BASE_URL is set); single mode\n // keeps the fixed /viewer mount.\n const viewerBase = viewerBaseFor({\n publicBaseUrl: cfg.publicBaseUrl,\n port: cfg.port,\n mode: cfg.auth.mode,\n sessionId\n });\n // Build a per-session URL builder. On the S3/R2 backend we use the\n // PresignedR2UrlBuilder so forDownload returns a presigned GET URL\n // directly from R2 — the /r/ Express route is not mounted on the hosted\n // Cloudflare deployment. On the local backend we fall back to\n // LocalViewerUrlBuilder (forDownload == forResource == /r/ URL).\n const urlBuilder = s3Backend\n ? new PresignedR2UrlBuilder({\n base: viewerBase,\n client: s3Backend.client,\n bucket: s3Backend.bucket,\n workspaceId\n })\n : new LocalViewerUrlBuilder({ base: viewerBase });\n // The engine and its URI resolver are process-global (ensureHeadlessEngine\n // is a cached singleton). Re-registering here is redundant after the first\n // session but harmless — the resolver is always the same function.\n // PresignedR2UrlBuilder inherits forResource (sync) from\n // LocalViewerUrlBuilder, so the sync URI-resolver callback stays valid.\n registerWorkspaceUriResolver(engine, urlBuilder);\n const session = new EngineSession({\n size: 1,\n adapter: cesdkAdapter(engine)\n });\n const renderThumbnail = async ({\n engine: eng\n }: {\n revision: string;\n engine: import('@cesdk/engine').default;\n }) => {\n const pages = eng.scene.getPages?.() ?? [];\n if (pages.length === 0) throw new Error('no pages');\n const blob = await eng.block.export(pages[0], 'image/webp', {\n targetWidth: 256\n });\n return new Uint8Array(await blob.arrayBuffer());\n };\n const tools = buildDesignToolRegistrations({\n fs: scopedFs,\n store,\n session,\n engineLock,\n gateway,\n urlBuilder,\n allowOutPath: false,\n onLeafWritten: (e) => broadcaster.broadcast(e),\n // `as never`: buildDesignToolRegistrations is generic over the engine type\n // (unknown at this composition root); the concrete @cesdk/engine type isn't\n // threaded up here, so this is the sanctioned boundary cast (see no-as-any).\n renderThumbnail: renderThumbnail as never,\n telemetry\n });\n return { engine, tools };\n },\n mountExtras: (expressApp) => {\n if (cfg.auth.mode === 'session') {\n // Multi-tenant viewer: /v/<sid> scopes reads to s/<sid> per request.\n mountSessionViewer(expressApp, {\n rawFs,\n broadcaster,\n cesdkLicense: license\n });\n return;\n }\n if (cfg.auth.mode !== 'single') {\n process.stderr.write(\n `codesign-mcp http: viewer disabled in \"${cfg.auth.mode}\" mode (multi-tenant viewer is a follow-up).\\n`\n );\n return;\n }\n const viewerScopedFs = new ScopedWorkspaceFS(\n rawFs,\n cfg.auth.fixedWorkspaceId ?? 'default'\n );\n const viewerStore = new RevisionStore(viewerScopedFs);\n expressApp.use(\n '/viewer',\n createViewerRouter({\n store: viewerStore,\n fs: viewerScopedFs,\n broadcaster,\n cesdkLicense: license\n })\n );\n }\n });\n\n const evictTimer = setInterval(\n () =>\n void app.sessions.evictIdle().then((evicted) => {\n if (evicted.length > 0)\n process.stderr.write(\n `codesign-mcp http: evicted ${evicted.length} idle session(s)\\n`\n );\n }),\n Math.max(10_000, cfg.idleTimeoutMs / 2)\n );\n\n const server = app.listen(cfg.port, HOST, () => {\n process.stderr.write(\n `codesign-mcp http: listening on http://${HOST}:${cfg.port}/mcp` +\n ` (mode=${cfg.auth.mode} backend=${cfg.workspace.backend})\\n`\n );\n });\n\n let shuttingDown = false;\n const shutdown = async (signal: string) => {\n if (shuttingDown) {\n process.stderr.write(\n `codesign-mcp http: ${signal} again — forcing exit.\\n`\n );\n process.exit(1);\n }\n shuttingDown = true;\n process.stderr.write(`codesign-mcp http: ${signal} — shutting down.\\n`);\n const hardExit = setTimeout(() => {\n process.stderr.write(\n 'codesign-mcp http: shutdown timed out — forcing exit.\\n'\n );\n process.exit(1);\n }, 5000);\n hardExit.unref();\n clearInterval(evictTimer);\n server.close();\n server.closeAllConnections();\n broadcaster.closeAll();\n await app.shutdown();\n process.exit(0);\n };\n process.on('SIGINT', () => void shutdown('SIGINT'));\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/cli/http.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/cli/http.ts\n//\n// codesign-mcp http — Streamable HTTP transport. Composition root: loads\n// IMGLY_* config, selects the storage backend (local|s3), builds a\n// PER-SESSION ScopedWorkspaceFS + RevisionStore, wires the workspace resolver,\n// and mounts the viewer: fixed `/viewer` in `single` mode, multi-tenant\n// `/v/<sid>` in `session` mode (disabled in `header` mode — follow-up).\nimport { loadConfig, type ServerConfig } from '../config.js';\nimport { ensureEngineAssetSources } from '../engine/asset-sources.js';\nimport { cesdkAdapter } from '../engine/cesdk-adapter.js';\nimport { Mutex } from '../engine/mutex.js';\nimport { EngineSession } from '../engine/session.js';\nimport { registerUnsplashSource } from '../engine/unsplash-source.js';\nimport { registerWorkspaceUriResolver } from '../engine/uri-resolver.js';\nimport { LocalGatewayClient } from '../gateway/local.js';\nimport { createCodesignHttpServer } from '../http/server.js';\nimport { makeWorkspaceResolver } from '../http/workspace-auth.js';\nimport { RevisionStore } from '../revisions/store.js';\nimport {\n disposeHeadlessEngine,\n ensureHeadlessEngine\n} from '../tools/engine.js';\nimport { buildDesignToolRegistrations } from '../tools/index.js';\nimport {\n createViewerRouter,\n LocalViewerUrlBuilder,\n mountSessionViewer,\n SseBroadcaster,\n viewerBaseFor\n} from '../viewer/index.js';\nimport { PresignedR2UrlBuilder } from '../viewer/presigned-r2-url-builder.js';\nimport { NodeWorkspaceFS } from '../workspace/node-fs.js';\nimport { S3WorkspaceFS } from '../workspace/s3.js';\nimport { ScopedWorkspaceFS } from '../workspace/scoped.js';\nimport { loadNodeAssets } from '../assets/load-node.js';\nimport type { WorkspaceFS } from '../workspace/types.js';\n\nconst HOST = '0.0.0.0'; // bind all interfaces; TLS/routing is the front's job\n\ninterface S3BackendInfo {\n /** The real S3Client — used to build per-session presigned download URLs. */\n client: import('@aws-sdk/client-s3').S3Client;\n bucket: string;\n}\n\ninterface BackendResult {\n rawFs: WorkspaceFS;\n /** Present only when the s3 backend is active. */\n s3?: S3BackendInfo;\n}\n\nasync function buildBackend(cfg: ServerConfig): Promise<BackendResult> {\n if (cfg.workspace.backend === 's3') {\n const { S3Client } = await import('@aws-sdk/client-s3');\n const s3 = cfg.workspace.s3;\n if (!s3)\n throw new Error('config: s3 backend selected but s3 config missing');\n const client = new S3Client({\n region: s3.region,\n ...(s3.endpoint ? { endpoint: s3.endpoint, forcePathStyle: true } : {}),\n ...(s3.accessKeyId && s3.secretAccessKey\n ? {\n credentials: {\n accessKeyId: s3.accessKeyId,\n secretAccessKey: s3.secretAccessKey\n }\n }\n : {})\n });\n return {\n rawFs: new S3WorkspaceFS({ client, bucket: s3.bucket }),\n s3: { client, bucket: s3.bucket }\n };\n }\n return { rawFs: new NodeWorkspaceFS(cfg.workspace.dir ?? '/data') };\n}\n\nexport async function runHttp(_argv: string[]): Promise<void> {\n let cfg: ServerConfig;\n try {\n cfg = loadConfig();\n } catch (err) {\n process.stderr.write(\n `codesign-mcp http: ${err instanceof Error ? err.message : String(err)}\\n`\n );\n process.exit(1);\n }\n\n const { buildDiagnostics } = await import('./stdio.js');\n const { telemetry } = buildDiagnostics();\n\n const license = cfg.cesdkLicense;\n const { rawFs, s3: s3Backend } = await buildBackend(cfg);\n const broadcaster = new SseBroadcaster();\n const gateway = new LocalGatewayClient({\n apiKey: cfg.gateway.apiKey,\n ...(cfg.gateway.baseUrl ? { endpoint: cfg.gateway.baseUrl } : {})\n });\n // ensureHeadlessEngine is a process singleton, so EVERY session's tools\n // operate on the same CE.SDK engine. This one lock — created once here, NOT\n // per session — serializes all engine access across sessions so two clients\n // editing concurrently can't corrupt each other's scene. When the engine\n // becomes a per-session/per-workspace pool, this moves to be one lock per\n // engine (per pool slot) so distinct engines run in parallel.\n const engineLock = new Mutex();\n const resolveWorkspaceId = makeWorkspaceResolver(cfg);\n\n const app = await createCodesignHttpServer({\n assets: loadNodeAssets(),\n buildId: cfg.buildId,\n maxSessions: cfg.maxHttpEngines,\n idleTimeoutMs: cfg.idleTimeoutMs,\n resolveWorkspaceId,\n trustedSessionIdHeader: 'x-mcp-session-id',\n ...(cfg.viewerCorsOrigins\n ? { cors: { origin: cfg.viewerCorsOrigins } }\n : {}),\n createSession: async ({ sessionId, workspaceId }) => {\n const scopedFs = new ScopedWorkspaceFS(rawFs, workspaceId);\n const store = new RevisionStore(scopedFs);\n const engine = await ensureHeadlessEngine(license);\n // Idempotent (engine is a process singleton) — only the first successful registration pays the CDN fetch.\n await ensureEngineAssetSources(engine);\n // Demo `unsplash` custom source (synchronous, idempotent) so asset_search\n // has a real remote source alongside the CDN ones. Public IMG.LY proxy.\n registerUnsplashSource(engine);\n // Per-session viewer base: session mode lives under /v/<sid> (reachable\n // through the CF Worker when IMGLY_PUBLIC_BASE_URL is set); single mode\n // keeps the fixed /viewer mount.\n const viewerBase = viewerBaseFor({\n publicBaseUrl: cfg.publicBaseUrl,\n port: cfg.port,\n mode: cfg.auth.mode,\n sessionId\n });\n // Build a per-session URL builder. On the S3/R2 backend we use the\n // PresignedR2UrlBuilder so forDownload returns a presigned GET URL\n // directly from R2 — the /r/ Express route is not mounted on the hosted\n // Cloudflare deployment. On the local backend we fall back to\n // LocalViewerUrlBuilder (forDownload == forResource == /r/ URL).\n const urlBuilder = s3Backend\n ? new PresignedR2UrlBuilder({\n base: viewerBase,\n client: s3Backend.client,\n bucket: s3Backend.bucket,\n workspaceId\n })\n : new LocalViewerUrlBuilder({ base: viewerBase });\n // The engine and its URI resolver are process-global (ensureHeadlessEngine\n // is a cached singleton). Re-registering here is redundant after the first\n // session but harmless — the resolver is always the same function.\n // PresignedR2UrlBuilder inherits forResource (sync) from\n // LocalViewerUrlBuilder, so the sync URI-resolver callback stays valid.\n registerWorkspaceUriResolver(engine, urlBuilder);\n const session = new EngineSession({\n size: 1,\n adapter: cesdkAdapter(engine)\n });\n const renderThumbnail = async ({\n engine: eng\n }: {\n revision: string;\n engine: import('@cesdk/engine').default;\n }) => {\n const pages = eng.scene.getPages?.() ?? [];\n if (pages.length === 0) throw new Error('no pages');\n const blob = await eng.block.export(pages[0], 'image/webp', {\n targetWidth: 256\n });\n return new Uint8Array(await blob.arrayBuffer());\n };\n const tools = buildDesignToolRegistrations({\n fs: scopedFs,\n store,\n session,\n engineLock,\n gateway,\n urlBuilder,\n allowOutPath: false,\n onLeafWritten: (e) => broadcaster.broadcast(e),\n // `as never`: buildDesignToolRegistrations is generic over the engine type\n // (unknown at this composition root); the concrete @cesdk/engine type isn't\n // threaded up here, so this is the sanctioned boundary cast (see no-as-any).\n renderThumbnail: renderThumbnail as never,\n telemetry\n });\n return { engine, tools };\n },\n mountExtras: (expressApp) => {\n if (cfg.auth.mode === 'session') {\n // Multi-tenant viewer: /v/<sid> scopes reads to s/<sid> per request.\n mountSessionViewer(expressApp, {\n rawFs,\n broadcaster,\n cesdkLicense: license\n });\n return;\n }\n if (cfg.auth.mode !== 'single') {\n process.stderr.write(\n `codesign-mcp http: viewer disabled in \"${cfg.auth.mode}\" mode (multi-tenant viewer is a follow-up).\\n`\n );\n return;\n }\n const viewerScopedFs = new ScopedWorkspaceFS(\n rawFs,\n cfg.auth.fixedWorkspaceId ?? 'default'\n );\n const viewerStore = new RevisionStore(viewerScopedFs);\n expressApp.use(\n '/viewer',\n createViewerRouter({\n store: viewerStore,\n fs: viewerScopedFs,\n broadcaster,\n cesdkLicense: license\n })\n );\n }\n });\n\n const evictTimer = setInterval(\n () =>\n void app.sessions.evictIdle().then((evicted) => {\n if (evicted.length > 0)\n process.stderr.write(\n `codesign-mcp http: evicted ${evicted.length} idle session(s)\\n`\n );\n }),\n Math.max(10_000, cfg.idleTimeoutMs / 2)\n );\n\n const server = app.listen(cfg.port, HOST, () => {\n process.stderr.write(\n `codesign-mcp http: listening on http://${HOST}:${cfg.port}/mcp` +\n ` (mode=${cfg.auth.mode} backend=${cfg.workspace.backend})\\n`\n );\n });\n\n let shuttingDown = false;\n const shutdown = async (signal: string) => {\n if (shuttingDown) {\n process.stderr.write(\n `codesign-mcp http: ${signal} again — forcing exit.\\n`\n );\n process.exit(1);\n }\n shuttingDown = true;\n process.stderr.write(`codesign-mcp http: ${signal} — shutting down.\\n`);\n const hardExit = setTimeout(() => {\n process.stderr.write(\n 'codesign-mcp http: shutdown timed out — forcing exit.\\n'\n );\n process.exit(1);\n }, 5000);\n hardExit.unref();\n clearInterval(evictTimer);\n server.close();\n server.closeAllConnections();\n broadcaster.closeAll();\n await app.shutdown();\n // The engine is a process singleton shared by every session (see\n // ensureHeadlessEngine), so per-session destroy can't dispose it — the\n // process exit does. Release it deterministically here. When engines become\n // a fixed pool, disposal moves to per-slot teardown inside app.shutdown().\n try {\n await disposeHeadlessEngine();\n } catch (err) {\n process.stderr.write(\n `codesign-mcp http: engine dispose failed — ${\n err instanceof Error ? err.message : String(err)\n }\\n`\n );\n }\n process.exit(0);\n };\n process.on('SIGINT', () => void shutdown('SIGINT'));\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAclC,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAc5E,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,GAAiB,GAAG;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAYA;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CA8I9C"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAMxC,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAmBlC,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAc5E,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,GAAiB,GAAG;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAYA;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAiN9C"}
package/dist/cli/stdio.js CHANGED
@@ -27,12 +27,14 @@ import { homedir } from 'node:os';
27
27
  import { join as joinPath } from 'node:path';
28
28
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
29
29
  import { loadNodeAssets } from '../assets/load-node.js';
30
- import { diagnosticsFileEnabled, resolveDiagnosticsDir } from '../config.js';
30
+ import { diagnosticsFileEnabled, resolveDiagnosticsDir, resolveMaxStdioEngines } from '../config.js';
31
31
  import { NOOP_DIAGNOSTICS_SINK } from '../diagnostics/events.js';
32
32
  import { FileDiagnosticsSink } from '../diagnostics/file-sink.js';
33
33
  import { toolCallSink } from '../diagnostics/tool-call-adapter.js';
34
34
  import { ensureEngineAssetSources } from '../engine/asset-sources.js';
35
35
  import { cesdkAdapter } from '../engine/cesdk-adapter.js';
36
+ import { wrapEngineCapacity } from '../engine/engine-capacity.js';
37
+ import { createSlotGatedEngine } from '../engine/engine-slots.js';
36
38
  import { Mutex } from '../engine/mutex.js';
37
39
  import { EngineSession } from '../engine/session.js';
38
40
  import { registerUnsplashSource } from '../engine/unsplash-source.js';
@@ -40,7 +42,7 @@ import { registerWorkspaceUriResolver } from '../engine/uri-resolver.js';
40
42
  import { LocalGatewayClient } from '../gateway/local.js';
41
43
  import { RevisionStore } from '../revisions/store.js';
42
44
  import { createCodesignMcpServer } from '../server.js';
43
- import { ensureHeadlessEngine } from '../tools/engine.js';
45
+ import { disposeHeadlessEngine, ensureHeadlessEngine } from '../tools/engine.js';
44
46
  import { buildDesignToolRegistrations } from '../tools/index.js';
45
47
  import { NOOP_TELEMETRY_SINK } from '../tools/spans.js';
46
48
  import { createViewerRouter, LocalViewerUrlBuilder, SseBroadcaster, startViewerListener } from '../viewer/index.js';
@@ -106,35 +108,43 @@ export async function runStdio() {
106
108
  text: 'viewer_listening'
107
109
  });
108
110
  const urlBuilder = new LocalViewerUrlBuilder({ base: listener.base });
109
- // Kick off the heavy engine bootstrap WITHOUT awaiting it see the file
110
- // header. server.connect() below answers the handshake while this runs.
111
- diag.emit({
112
- kind: 'lifecycle',
113
- timestamp: Date.now(),
114
- text: 'engine_init_start'
115
- });
116
- const enginePromise = bootstrapEngine({ license, urlBuilder, usedTrial });
117
- // Nothing awaits enginePromise until the first tool call. Attach a logger so a
118
- // background init failure surfaces on stderr instead of crashing the process
119
- // on an unhandled rejection; the awaiting tool call still sees the real error.
120
- void enginePromise.catch((err) => {
121
- process.stderr.write(`codesign-mcp stdio: engine initialization failed — ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
122
- diag.emit({
123
- kind: 'lifecycle',
124
- timestamp: Date.now(),
125
- text: 'engine_init_failed',
126
- error: {
127
- message: err instanceof Error ? err.message : String(err),
128
- ...(err instanceof Error && err.stack ? { stack: err.stack } : {})
129
- }
130
- });
111
+ // Engine boot is DEFERRED to the first engine-using tool call and gated by a
112
+ // machine-wide slot (CODE-59): idle panes hold no engine (~8-12 MB) and no
113
+ // slot; only real design work boots one, and at most IMGLY_MAX_STDIO_ENGINES
114
+ // engines run across all stdio processes at once. bootstrapEngine still runs
115
+ // OFF the handshake path — it is just triggered lazily now, not eagerly.
116
+ // At capacity, get() throws EngineCapacityError, which wrapEngineCapacity
117
+ // (below) turns into an isError tool result.
118
+ const gatedEngine = createSlotGatedEngine({
119
+ max: resolveMaxStdioEngines(process.env),
120
+ boot: () => {
121
+ diag.emit({
122
+ kind: 'lifecycle',
123
+ timestamp: Date.now(),
124
+ text: 'engine_init_start'
125
+ });
126
+ const p = bootstrapEngine({ license, urlBuilder, usedTrial });
127
+ // Surface a background init failure on stderr + diagnostics. The awaiting
128
+ // tool call still sees the real error; this just avoids a silent gap.
129
+ void p.catch((err) => {
130
+ process.stderr.write(`codesign-mcp stdio: engine initialization failed — ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
131
+ diag.emit({
132
+ kind: 'lifecycle',
133
+ timestamp: Date.now(),
134
+ text: 'engine_init_failed',
135
+ error: {
136
+ message: err instanceof Error ? err.message : String(err),
137
+ ...(err instanceof Error && err.stack ? { stack: err.stack } : {})
138
+ }
139
+ });
140
+ });
141
+ return p;
142
+ }
131
143
  });
132
- // size=1, single shared engine resolved lazily from enginePromise.
133
- // EngineSession only calls adapter.create() (awaiting the engine) on the first
134
- // edit, by which point the bootstrap is usually already settled.
144
+ // size=1, single shared engine resolved lazily (and slot-gated) on first edit.
135
145
  const session = new EngineSession({
136
146
  size: 1,
137
- adapter: cesdkAdapter(enginePromise)
147
+ adapter: cesdkAdapter(() => gatedEngine.get())
138
148
  });
139
149
  const renderThumbnail = async ({ engine: eng }) => {
140
150
  // Thumbnail = the scene's first page rendered to a 256-px webp. Best-effort.
@@ -158,21 +168,79 @@ export async function runStdio() {
158
168
  onLeafWritten: (e) => broadcaster.broadcast(e),
159
169
  renderThumbnail: renderThumbnail, // adapter's E generic is `unknown`; cesdk engine type fits
160
170
  telemetry
161
- });
171
+ }).map(wrapEngineCapacity);
162
172
  const assets = loadNodeAssets();
163
173
  const server = createCodesignMcpServer({
164
174
  assets,
165
175
  tools
166
176
  });
167
177
  const transport = new StdioServerTransport();
168
- await server.connect(transport);
169
- const shutdown = async () => {
170
- await listener.close();
171
- await server.close();
178
+ let shuttingDown = false;
179
+ const shutdown = async (reason) => {
180
+ if (shuttingDown)
181
+ return; // idempotent — a signal and the transport close can race
182
+ shuttingDown = true;
183
+ diag.emit({
184
+ kind: 'lifecycle',
185
+ timestamp: Date.now(),
186
+ text: `shutdown:${reason}`
187
+ });
188
+ // Flush the breadcrumb NOW, before the slower teardown below. emit() is
189
+ // fire-and-forget, engine dispose can take seconds, and a client may SIGKILL
190
+ // after a short grace window — landing the event first lets it survive even
191
+ // if we're killed mid-teardown.
192
+ try {
193
+ await diag.flush?.();
194
+ }
195
+ catch {
196
+ // best-effort
197
+ }
198
+ try {
199
+ await listener.close();
200
+ }
201
+ catch {
202
+ // best-effort — keep tearing down
203
+ }
204
+ try {
205
+ await server.close();
206
+ }
207
+ catch {
208
+ // best-effort
209
+ }
210
+ // Release the CE.SDK engine (WASM/native runtime) deterministically instead
211
+ // of leaving it to process teardown. Best-effort: a dispose error must never
212
+ // block the exit below.
213
+ try {
214
+ await disposeHeadlessEngine();
215
+ }
216
+ catch (err) {
217
+ process.stderr.write(`codesign-mcp stdio: engine dispose failed — ${err instanceof Error ? err.message : String(err)}\n`);
218
+ }
219
+ // Release the machine-wide engine slot (CODE-59) so another session can boot.
220
+ // Clean path; SIGKILL is covered by PID-liveness reaping on the next acquire.
221
+ try {
222
+ await gatedEngine.release();
223
+ }
224
+ catch {
225
+ // best-effort — never block the exit
226
+ }
172
227
  process.exit(0);
173
228
  };
174
- process.on('SIGINT', () => void shutdown());
175
- process.on('SIGTERM', () => void shutdown());
229
+ // A client can disconnect by closing the stdio pipe (stdin EOF) WITHOUT a
230
+ // signal — e.g. an orphaned/crashed host. `StdioServerTransport` only listens
231
+ // for stdin `data`/`error`, never EOF, so it never surfaces that disconnect on
232
+ // its own; the viewer listener's open socket then keeps the event loop alive
233
+ // and the process lingers with the engine resident. So detect stdin EOF here
234
+ // and reap. Listeners are attached before connect() (which puts stdin into
235
+ // flowing mode) so we can't miss the `end`. `server.onclose` additionally
236
+ // covers a protocol-level close routed through transport.close().
237
+ server.onclose = () => void shutdown('server-close');
238
+ process.stdin.on('end', () => void shutdown('stdin-end'));
239
+ process.stdin.on('close', () => void shutdown('stdin-close'));
240
+ await server.connect(transport);
241
+ process.on('SIGINT', () => void shutdown('SIGINT'));
242
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
243
+ process.on('SIGHUP', () => void shutdown('SIGHUP'));
176
244
  }
177
245
  // Heavy engine bootstrap, run in the background by runStdio (off the MCP
178
246
  // handshake path). Resolves to a fully-wired engine: design-unit-patched (by
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["// MCP server over stdin/stdout. Bootstraps:\n// - Workspace (NodeWorkspaceFS rooted at $CODESIGN_WORKSPACE or ~/.codesign/workspace)\n// - RevisionStore over the workspace\n// - EngineSession (size=1) over the headless CE.SDK engine\n// - SseBroadcaster for the viewer\n// - Viewer router + ephemeral localhost listener\n// - design + workspace tool registrations\n// - LocalGatewayClient (reads $API_KEY)\n//\n// stdio's single-engine, single-workspace model is the simplest possible\n// wiring; HTTP transport (F2) reuses the same primitives per-session.\n//\n// Startup order matters: everything cheap (workspace, store, viewer listener,\n// tool wiring) runs before `server.connect()` so the MCP handshake replies\n// immediately. The expensive CE.SDK engine init + CDN asset-source fetch are\n// deferred to a background promise (bootstrapEngine) that the first tool call\n// awaits. Doing this work before connect can blow the host's MCP startup\n// timeout on a cold boot — seen as \"fails on add/update, works after Reconnect\".\n//\n// Optional env:\n// CESDK_LICENSE — overrides the build-time-bundled trial license. Mandatory\n// at GA / when no trial is baked in (see resolve-license.ts).\n//\n// Logging note: stdout is reserved for MCP protocol traffic. Everything\n// human-facing (errors, progress) goes to stderr.\nimport { homedir } from 'node:os';\nimport { join as joinPath } from 'node:path';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadNodeAssets } from '../assets/load-node.js';\nimport type { Env } from '../config.js';\nimport { diagnosticsFileEnabled, resolveDiagnosticsDir } from '../config.js';\nimport {\n NOOP_DIAGNOSTICS_SINK,\n type DiagnosticsSink\n} from '../diagnostics/events.js';\nimport { FileDiagnosticsSink } from '../diagnostics/file-sink.js';\nimport { toolCallSink } from '../diagnostics/tool-call-adapter.js';\nimport { ensureEngineAssetSources } from '../engine/asset-sources.js';\nimport { cesdkAdapter } from '../engine/cesdk-adapter.js';\nimport { Mutex } from '../engine/mutex.js';\nimport { EngineSession } from '../engine/session.js';\nimport { registerUnsplashSource } from '../engine/unsplash-source.js';\nimport { registerWorkspaceUriResolver } from '../engine/uri-resolver.js';\nimport { LocalGatewayClient } from '../gateway/local.js';\nimport { RevisionStore } from '../revisions/store.js';\nimport { createCodesignMcpServer } from '../server.js';\nimport { ensureHeadlessEngine } from '../tools/engine.js';\nimport { buildDesignToolRegistrations } from '../tools/index.js';\nimport { NOOP_TELEMETRY_SINK, type TelemetrySink } from '../tools/spans.js';\nimport {\n createViewerRouter,\n LocalViewerUrlBuilder,\n SseBroadcaster,\n startViewerListener\n} from '../viewer/index.js';\nimport { NodeWorkspaceFS } from '../workspace/node-fs.js';\nimport { redirectConsoleToStderr } from './redirect-console.js';\nimport { resolveLicense } from './resolve-license.js';\nimport { TRIAL_CESDK_LICENSE } from './trial-license.generated.js';\n\nconst DEFAULT_WORKSPACE = joinPath(homedir(), '.codesign', 'workspace');\n\nexport function buildDiagnostics(env: Env = process.env): {\n diag: DiagnosticsSink;\n telemetry: TelemetrySink;\n dir: string | undefined;\n enabled: boolean;\n} {\n if (!diagnosticsFileEnabled(env)) {\n return {\n diag: NOOP_DIAGNOSTICS_SINK,\n telemetry: NOOP_TELEMETRY_SINK,\n dir: undefined,\n enabled: false\n };\n }\n const dir = resolveDiagnosticsDir(env);\n const sink = new FileDiagnosticsSink({ dir });\n return { diag: sink, telemetry: toolCallSink(sink), dir, enabled: true };\n}\n\nexport async function runStdio(): Promise<void> {\n const {\n diag,\n telemetry,\n dir: diagnosticsDir,\n enabled: diagnosticsEnabled\n } = buildDiagnostics();\n if (diagnosticsEnabled && diagnosticsDir) {\n await FileDiagnosticsSink.pruneOnStartup({ dir: diagnosticsDir });\n process.stderr.write(\n `codesign-mcp stdio: diagnostics logging to ${diagnosticsDir} — redacted, local-only; disable with IMGLY_DIAGNOSTICS_FILE=false\\n`\n );\n }\n // stdout is the JSON-RPC channel in stdio mode; keep engine console.* noise off it.\n redirectConsoleToStderr((level, text) =>\n diag.emit({ kind: 'engine_log', timestamp: Date.now(), level, text })\n );\n\n const license = resolveLicense(process.env, TRIAL_CESDK_LICENSE);\n if (!license) {\n process.stderr.write(\n 'codesign-mcp stdio: no CESDK_LICENSE set and no bundled trial license — set CESDK_LICENSE (or IMGLY_CESDK_LICENSE) to a CE.SDK node license string.\\n'\n );\n process.exit(1);\n }\n // True when the license came from the bundled trial (no explicit env override).\n // Used only to turn an engine-init rejection into a trial-aware hint below.\n const usedTrial =\n !process.env.CESDK_LICENSE?.trim() &&\n !process.env.IMGLY_CESDK_LICENSE?.trim();\n\n const workspaceRoot = process.env.CODESIGN_WORKSPACE ?? DEFAULT_WORKSPACE;\n const fs = new NodeWorkspaceFS(workspaceRoot);\n const store = new RevisionStore(fs);\n\n // One engine, one lock. stdio is single-session so there is no contention\n // today, but the engine tools require the lock and it keeps wiring uniform\n // with the HTTP transport.\n const engineLock = new Mutex();\n const broadcaster = new SseBroadcaster();\n const gateway = new LocalGatewayClient({ apiKey: process.env.API_KEY });\n\n // The viewer listener binds an ephemeral localhost port — cheap and local, so\n // it stays on the startup path. urlBuilder must exist before we build the tools\n // and before the engine's workspace:// URI resolver is wired.\n const viewerRouter = createViewerRouter({\n store,\n fs,\n broadcaster,\n cesdkLicense: license,\n workspaceRoot\n });\n const listener = await startViewerListener({ router: viewerRouter });\n process.stderr.write(`codesign-mcp stdio: viewer at ${listener.base}\\n`);\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'viewer_listening'\n });\n const urlBuilder = new LocalViewerUrlBuilder({ base: listener.base });\n\n // Kick off the heavy engine bootstrap WITHOUT awaiting it — see the file\n // header. server.connect() below answers the handshake while this runs.\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'engine_init_start'\n });\n const enginePromise = bootstrapEngine({ license, urlBuilder, usedTrial });\n // Nothing awaits enginePromise until the first tool call. Attach a logger so a\n // background init failure surfaces on stderr instead of crashing the process\n // on an unhandled rejection; the awaiting tool call still sees the real error.\n void enginePromise.catch((err) => {\n process.stderr.write(\n `codesign-mcp stdio: engine initialization failed — ${\n err instanceof Error ? (err.stack ?? err.message) : String(err)\n }\\n`\n );\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'engine_init_failed',\n error: {\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof Error && err.stack ? { stack: err.stack } : {})\n }\n });\n });\n\n // size=1, single shared engine resolved lazily from enginePromise.\n // EngineSession only calls adapter.create() (awaiting the engine) on the first\n // edit, by which point the bootstrap is usually already settled.\n const session = new EngineSession({\n size: 1,\n adapter: cesdkAdapter(enginePromise)\n });\n\n const renderThumbnail = async ({\n engine: eng\n }: {\n revision: string;\n engine: import('@cesdk/engine').default;\n }): Promise<Uint8Array> => {\n // Thumbnail = the scene's first page rendered to a 256-px webp. Best-effort.\n // We use page 0 of the scene; if the scene has no pages, throw → swallowed.\n const pages = eng.scene.getPages();\n if (!pages || pages.length === 0) throw new Error('no pages');\n const blob = await eng.block.export(pages[0], 'image/webp', {\n targetWidth: 256\n });\n return new Uint8Array(await blob.arrayBuffer());\n };\n\n const tools = buildDesignToolRegistrations({\n fs,\n store,\n session,\n engineLock,\n gateway,\n urlBuilder,\n allowOutPath: true,\n onLeafWritten: (e) => broadcaster.broadcast(e),\n renderThumbnail: renderThumbnail as never, // adapter's E generic is `unknown`; cesdk engine type fits\n telemetry\n });\n\n const assets = loadNodeAssets();\n const server = createCodesignMcpServer({\n assets,\n tools\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n const shutdown = async () => {\n await listener.close();\n await server.close();\n process.exit(0);\n };\n process.on('SIGINT', () => void shutdown());\n process.on('SIGTERM', () => void shutdown());\n}\n\n// Heavy engine bootstrap, run in the background by runStdio (off the MCP\n// handshake path). Resolves to a fully-wired engine: design-unit-patched (by\n// ensureHeadlessEngine), workspace:// URI resolver installed, and — best-effort\n// — the default + demo + unsplash asset sources registered so asset_search has\n// content. The asset-source step hits the CDN; failures log to stderr and leave\n// the engine usable (asset_search just sees fewer sources).\nasync function bootstrapEngine({\n license,\n urlBuilder,\n usedTrial\n}: {\n license: string;\n urlBuilder: LocalViewerUrlBuilder;\n /** License came from the bundled trial — turns an init rejection into a hint. */\n usedTrial: boolean;\n}): Promise<Awaited<ReturnType<typeof ensureHeadlessEngine>>> {\n let engine: Awaited<ReturnType<typeof ensureHeadlessEngine>>;\n try {\n engine = await ensureHeadlessEngine(license);\n } catch (err) {\n if (usedTrial) {\n process.stderr.write(\n 'codesign-mcp stdio: the bundled trial license was rejected (it may have expired). Set CESDK_LICENSE to a valid CE.SDK node license string.\\n'\n );\n }\n throw err;\n }\n // Resolve workspace:// asset URIs to the listener's /r/ route so the headless\n // engine can fetch generated assets embedded in scenes.\n registerWorkspaceUriResolver(engine, urlBuilder);\n // Default + demo asset sources so `asset_search` has content (CDN fetch,\n // best-effort — a failure logs to stderr and the engine stays usable).\n await ensureEngineAssetSources(engine);\n // Demo `unsplash` custom source (synchronous, idempotent) so asset_search has\n // a real remote source alongside the CDN ones. Public IMG.LY demo proxy.\n registerUnsplashSource(engine);\n return engine;\n}\n"]}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["// MCP server over stdin/stdout. Bootstraps:\n// - Workspace (NodeWorkspaceFS rooted at $CODESIGN_WORKSPACE or ~/.codesign/workspace)\n// - RevisionStore over the workspace\n// - EngineSession (size=1) over the headless CE.SDK engine\n// - SseBroadcaster for the viewer\n// - Viewer router + ephemeral localhost listener\n// - design + workspace tool registrations\n// - LocalGatewayClient (reads $API_KEY)\n//\n// stdio's single-engine, single-workspace model is the simplest possible\n// wiring; HTTP transport (F2) reuses the same primitives per-session.\n//\n// Startup order matters: everything cheap (workspace, store, viewer listener,\n// tool wiring) runs before `server.connect()` so the MCP handshake replies\n// immediately. The expensive CE.SDK engine init + CDN asset-source fetch are\n// deferred to a background promise (bootstrapEngine) that the first tool call\n// awaits. Doing this work before connect can blow the host's MCP startup\n// timeout on a cold boot — seen as \"fails on add/update, works after Reconnect\".\n//\n// Optional env:\n// CESDK_LICENSE — overrides the build-time-bundled trial license. Mandatory\n// at GA / when no trial is baked in (see resolve-license.ts).\n//\n// Logging note: stdout is reserved for MCP protocol traffic. Everything\n// human-facing (errors, progress) goes to stderr.\nimport { homedir } from 'node:os';\nimport { join as joinPath } from 'node:path';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type CreativeEngine from '@cesdk/engine';\nimport { loadNodeAssets } from '../assets/load-node.js';\nimport type { Env } from '../config.js';\nimport {\n diagnosticsFileEnabled,\n resolveDiagnosticsDir,\n resolveMaxStdioEngines\n} from '../config.js';\nimport {\n NOOP_DIAGNOSTICS_SINK,\n type DiagnosticsSink\n} from '../diagnostics/events.js';\nimport { FileDiagnosticsSink } from '../diagnostics/file-sink.js';\nimport { toolCallSink } from '../diagnostics/tool-call-adapter.js';\nimport { ensureEngineAssetSources } from '../engine/asset-sources.js';\nimport { cesdkAdapter } from '../engine/cesdk-adapter.js';\nimport { wrapEngineCapacity } from '../engine/engine-capacity.js';\nimport { createSlotGatedEngine } from '../engine/engine-slots.js';\nimport { Mutex } from '../engine/mutex.js';\nimport { EngineSession } from '../engine/session.js';\nimport { registerUnsplashSource } from '../engine/unsplash-source.js';\nimport { registerWorkspaceUriResolver } from '../engine/uri-resolver.js';\nimport { LocalGatewayClient } from '../gateway/local.js';\nimport { RevisionStore } from '../revisions/store.js';\nimport { createCodesignMcpServer } from '../server.js';\nimport {\n disposeHeadlessEngine,\n ensureHeadlessEngine\n} from '../tools/engine.js';\nimport { buildDesignToolRegistrations } from '../tools/index.js';\nimport { NOOP_TELEMETRY_SINK, type TelemetrySink } from '../tools/spans.js';\nimport {\n createViewerRouter,\n LocalViewerUrlBuilder,\n SseBroadcaster,\n startViewerListener\n} from '../viewer/index.js';\nimport { NodeWorkspaceFS } from '../workspace/node-fs.js';\nimport { redirectConsoleToStderr } from './redirect-console.js';\nimport { resolveLicense } from './resolve-license.js';\nimport { TRIAL_CESDK_LICENSE } from './trial-license.generated.js';\n\nconst DEFAULT_WORKSPACE = joinPath(homedir(), '.codesign', 'workspace');\n\nexport function buildDiagnostics(env: Env = process.env): {\n diag: DiagnosticsSink;\n telemetry: TelemetrySink;\n dir: string | undefined;\n enabled: boolean;\n} {\n if (!diagnosticsFileEnabled(env)) {\n return {\n diag: NOOP_DIAGNOSTICS_SINK,\n telemetry: NOOP_TELEMETRY_SINK,\n dir: undefined,\n enabled: false\n };\n }\n const dir = resolveDiagnosticsDir(env);\n const sink = new FileDiagnosticsSink({ dir });\n return { diag: sink, telemetry: toolCallSink(sink), dir, enabled: true };\n}\n\nexport async function runStdio(): Promise<void> {\n const {\n diag,\n telemetry,\n dir: diagnosticsDir,\n enabled: diagnosticsEnabled\n } = buildDiagnostics();\n if (diagnosticsEnabled && diagnosticsDir) {\n await FileDiagnosticsSink.pruneOnStartup({ dir: diagnosticsDir });\n process.stderr.write(\n `codesign-mcp stdio: diagnostics logging to ${diagnosticsDir} — redacted, local-only; disable with IMGLY_DIAGNOSTICS_FILE=false\\n`\n );\n }\n // stdout is the JSON-RPC channel in stdio mode; keep engine console.* noise off it.\n redirectConsoleToStderr((level, text) =>\n diag.emit({ kind: 'engine_log', timestamp: Date.now(), level, text })\n );\n\n const license = resolveLicense(process.env, TRIAL_CESDK_LICENSE);\n if (!license) {\n process.stderr.write(\n 'codesign-mcp stdio: no CESDK_LICENSE set and no bundled trial license — set CESDK_LICENSE (or IMGLY_CESDK_LICENSE) to a CE.SDK node license string.\\n'\n );\n process.exit(1);\n }\n // True when the license came from the bundled trial (no explicit env override).\n // Used only to turn an engine-init rejection into a trial-aware hint below.\n const usedTrial =\n !process.env.CESDK_LICENSE?.trim() &&\n !process.env.IMGLY_CESDK_LICENSE?.trim();\n\n const workspaceRoot = process.env.CODESIGN_WORKSPACE ?? DEFAULT_WORKSPACE;\n const fs = new NodeWorkspaceFS(workspaceRoot);\n const store = new RevisionStore(fs);\n\n // One engine, one lock. stdio is single-session so there is no contention\n // today, but the engine tools require the lock and it keeps wiring uniform\n // with the HTTP transport.\n const engineLock = new Mutex();\n const broadcaster = new SseBroadcaster();\n const gateway = new LocalGatewayClient({ apiKey: process.env.API_KEY });\n\n // The viewer listener binds an ephemeral localhost port — cheap and local, so\n // it stays on the startup path. urlBuilder must exist before we build the tools\n // and before the engine's workspace:// URI resolver is wired.\n const viewerRouter = createViewerRouter({\n store,\n fs,\n broadcaster,\n cesdkLicense: license,\n workspaceRoot\n });\n const listener = await startViewerListener({ router: viewerRouter });\n process.stderr.write(`codesign-mcp stdio: viewer at ${listener.base}\\n`);\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'viewer_listening'\n });\n const urlBuilder = new LocalViewerUrlBuilder({ base: listener.base });\n\n // Engine boot is DEFERRED to the first engine-using tool call and gated by a\n // machine-wide slot (CODE-59): idle panes hold no engine (~8-12 MB) and no\n // slot; only real design work boots one, and at most IMGLY_MAX_STDIO_ENGINES\n // engines run across all stdio processes at once. bootstrapEngine still runs\n // OFF the handshake path — it is just triggered lazily now, not eagerly.\n // At capacity, get() throws EngineCapacityError, which wrapEngineCapacity\n // (below) turns into an isError tool result.\n const gatedEngine = createSlotGatedEngine<CreativeEngine>({\n max: resolveMaxStdioEngines(process.env),\n boot: () => {\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'engine_init_start'\n });\n const p = bootstrapEngine({ license, urlBuilder, usedTrial });\n // Surface a background init failure on stderr + diagnostics. The awaiting\n // tool call still sees the real error; this just avoids a silent gap.\n void p.catch((err) => {\n process.stderr.write(\n `codesign-mcp stdio: engine initialization failed — ${\n err instanceof Error ? (err.stack ?? err.message) : String(err)\n }\\n`\n );\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: 'engine_init_failed',\n error: {\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof Error && err.stack ? { stack: err.stack } : {})\n }\n });\n });\n return p;\n }\n });\n\n // size=1, single shared engine resolved lazily (and slot-gated) on first edit.\n const session = new EngineSession({\n size: 1,\n adapter: cesdkAdapter(() => gatedEngine.get())\n });\n\n const renderThumbnail = async ({\n engine: eng\n }: {\n revision: string;\n engine: import('@cesdk/engine').default;\n }): Promise<Uint8Array> => {\n // Thumbnail = the scene's first page rendered to a 256-px webp. Best-effort.\n // We use page 0 of the scene; if the scene has no pages, throw → swallowed.\n const pages = eng.scene.getPages();\n if (!pages || pages.length === 0) throw new Error('no pages');\n const blob = await eng.block.export(pages[0], 'image/webp', {\n targetWidth: 256\n });\n return new Uint8Array(await blob.arrayBuffer());\n };\n\n const tools = buildDesignToolRegistrations({\n fs,\n store,\n session,\n engineLock,\n gateway,\n urlBuilder,\n allowOutPath: true,\n onLeafWritten: (e) => broadcaster.broadcast(e),\n renderThumbnail: renderThumbnail as never, // adapter's E generic is `unknown`; cesdk engine type fits\n telemetry\n }).map(wrapEngineCapacity);\n\n const assets = loadNodeAssets();\n const server = createCodesignMcpServer({\n assets,\n tools\n });\n\n const transport = new StdioServerTransport();\n\n let shuttingDown = false;\n const shutdown = async (reason: string) => {\n if (shuttingDown) return; // idempotent — a signal and the transport close can race\n shuttingDown = true;\n diag.emit({\n kind: 'lifecycle',\n timestamp: Date.now(),\n text: `shutdown:${reason}`\n });\n // Flush the breadcrumb NOW, before the slower teardown below. emit() is\n // fire-and-forget, engine dispose can take seconds, and a client may SIGKILL\n // after a short grace window — landing the event first lets it survive even\n // if we're killed mid-teardown.\n try {\n await diag.flush?.();\n } catch {\n // best-effort\n }\n try {\n await listener.close();\n } catch {\n // best-effort — keep tearing down\n }\n try {\n await server.close();\n } catch {\n // best-effort\n }\n // Release the CE.SDK engine (WASM/native runtime) deterministically instead\n // of leaving it to process teardown. Best-effort: a dispose error must never\n // block the exit below.\n try {\n await disposeHeadlessEngine();\n } catch (err) {\n process.stderr.write(\n `codesign-mcp stdio: engine dispose failed — ${\n err instanceof Error ? err.message : String(err)\n }\\n`\n );\n }\n // Release the machine-wide engine slot (CODE-59) so another session can boot.\n // Clean path; SIGKILL is covered by PID-liveness reaping on the next acquire.\n try {\n await gatedEngine.release();\n } catch {\n // best-effort — never block the exit\n }\n process.exit(0);\n };\n\n // A client can disconnect by closing the stdio pipe (stdin EOF) WITHOUT a\n // signal — e.g. an orphaned/crashed host. `StdioServerTransport` only listens\n // for stdin `data`/`error`, never EOF, so it never surfaces that disconnect on\n // its own; the viewer listener's open socket then keeps the event loop alive\n // and the process lingers with the engine resident. So detect stdin EOF here\n // and reap. Listeners are attached before connect() (which puts stdin into\n // flowing mode) so we can't miss the `end`. `server.onclose` additionally\n // covers a protocol-level close routed through transport.close().\n server.onclose = () => void shutdown('server-close');\n process.stdin.on('end', () => void shutdown('stdin-end'));\n process.stdin.on('close', () => void shutdown('stdin-close'));\n\n await server.connect(transport);\n\n process.on('SIGINT', () => void shutdown('SIGINT'));\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n process.on('SIGHUP', () => void shutdown('SIGHUP'));\n}\n\n// Heavy engine bootstrap, run in the background by runStdio (off the MCP\n// handshake path). Resolves to a fully-wired engine: design-unit-patched (by\n// ensureHeadlessEngine), workspace:// URI resolver installed, and — best-effort\n// — the default + demo + unsplash asset sources registered so asset_search has\n// content. The asset-source step hits the CDN; failures log to stderr and leave\n// the engine usable (asset_search just sees fewer sources).\nasync function bootstrapEngine({\n license,\n urlBuilder,\n usedTrial\n}: {\n license: string;\n urlBuilder: LocalViewerUrlBuilder;\n /** License came from the bundled trial — turns an init rejection into a hint. */\n usedTrial: boolean;\n}): Promise<Awaited<ReturnType<typeof ensureHeadlessEngine>>> {\n let engine: Awaited<ReturnType<typeof ensureHeadlessEngine>>;\n try {\n engine = await ensureHeadlessEngine(license);\n } catch (err) {\n if (usedTrial) {\n process.stderr.write(\n 'codesign-mcp stdio: the bundled trial license was rejected (it may have expired). Set CESDK_LICENSE to a valid CE.SDK node license string.\\n'\n );\n }\n throw err;\n }\n // Resolve workspace:// asset URIs to the listener's /r/ route so the headless\n // engine can fetch generated assets embedded in scenes.\n registerWorkspaceUriResolver(engine, urlBuilder);\n // Default + demo asset sources so `asset_search` has content (CDN fetch,\n // best-effort — a failure logs to stderr and the engine stays usable).\n await ensureEngineAssetSources(engine);\n // Demo `unsplash` custom source (synchronous, idempotent) so asset_search has\n // a real remote source alongside the CDN ones. Public IMG.LY demo proxy.\n registerUnsplashSource(engine);\n return engine;\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -31,8 +31,8 @@ export interface ServerConfig {
31
31
  */
32
32
  buildId: string;
33
33
  port: number;
34
- /** Max concurrent engines/sessions — the RAM cap (spec §9.2). */
35
- maxEngines: number;
34
+ /** Max concurrent engines/sessions per HTTP server process — the RAM cap (spec §9.2). */
35
+ maxHttpEngines: number;
36
36
  idleTimeoutMs: number;
37
37
  logLevel: string;
38
38
  workspace: WorkspaceConfig;
@@ -56,6 +56,15 @@ export interface ServerConfig {
56
56
  publicBaseUrl?: string;
57
57
  }
58
58
  export declare function loadConfig(env?: Env): ServerConfig;
59
+ /**
60
+ * Machine-wide cap on concurrently booting CE.SDK engines for the LOCAL stdio
61
+ * transport (CODE-59). Distinct from IMGLY_MAX_HTTP_ENGINES (per HTTP server
62
+ * process): different scope, different default, and different at-capacity
63
+ * behavior (stdio fails fast; HTTP evicts/queues). Floored at 1 so a stray 0
64
+ * cannot brick the server. Read here (not in loadConfig) because the stdio path
65
+ * never calls loadConfig.
66
+ */
67
+ export declare function resolveMaxStdioEngines(env?: Env): number;
59
68
  /**
60
69
  * Local diagnostics file logging is ON by default so evidence exists before a
61
70
  * user knows they hit a bug. Disable with IMGLY_DIAGNOSTICS_FILE=false (or 0).
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,GAAiB,GACrB,MAAM,GAAG,SAAS,CAIpB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE;QACJ,IAAI,EAAE,aAAa,CAAC;QACpB,6DAA6D;QAC7D,MAAM,EAAE,MAAM,CAAC;QACf,uCAAuC;QACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsBD,wBAAgB,UAAU,CAAC,GAAG,GAAE,GAAiB,GAAG,YAAY,CAoE/D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,GAAiB,GAAG,OAAO,CAEtE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAKpE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,GAAiB,GACrB,MAAM,GAAG,SAAS,CAIpB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,eAAe,CAAC;IAC3B,IAAI,EAAE;QACJ,IAAI,EAAE,aAAa,CAAC;QACpB,6DAA6D;QAC7D,MAAM,EAAE,MAAM,CAAC;QACf,uCAAuC;QACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsBD,wBAAgB,UAAU,CAAC,GAAG,GAAE,GAAiB,GAAG,YAAY,CAoE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAErE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,GAAiB,GAAG,OAAO,CAEtE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAKpE"}
package/dist/config.js CHANGED
@@ -87,7 +87,7 @@ export function loadConfig(env = process.env) {
87
87
  cesdkLicense,
88
88
  buildId: env.IMGLY_BUILD_ID ?? 'dev',
89
89
  port: intEnv(env, 'PORT', 8080),
90
- maxEngines: intEnv(env, 'IMGLY_MAX_ENGINES', 8),
90
+ maxHttpEngines: intEnv(env, 'IMGLY_MAX_HTTP_ENGINES', 8),
91
91
  idleTimeoutMs: intEnv(env, 'IMGLY_SESSION_IDLE_TIMEOUT', 900) * 1000,
92
92
  logLevel: env.IMGLY_LOG_LEVEL ?? 'info',
93
93
  workspace,
@@ -100,6 +100,17 @@ export function loadConfig(env = process.env) {
100
100
  cfg.publicBaseUrl = env.IMGLY_PUBLIC_BASE_URL.replace(/\/+$/, '');
101
101
  return cfg;
102
102
  }
103
+ /**
104
+ * Machine-wide cap on concurrently booting CE.SDK engines for the LOCAL stdio
105
+ * transport (CODE-59). Distinct from IMGLY_MAX_HTTP_ENGINES (per HTTP server
106
+ * process): different scope, different default, and different at-capacity
107
+ * behavior (stdio fails fast; HTTP evicts/queues). Floored at 1 so a stray 0
108
+ * cannot brick the server. Read here (not in loadConfig) because the stdio path
109
+ * never calls loadConfig.
110
+ */
111
+ export function resolveMaxStdioEngines(env = process.env) {
112
+ return Math.max(1, Math.floor(intEnv(env, 'IMGLY_MAX_STDIO_ENGINES', 4)));
113
+ }
103
114
  /**
104
115
  * Local diagnostics file logging is ON by default so evidence exists before a
105
116
  * user knows they hit a bug. Disable with IMGLY_DIAGNOSTICS_FILE=false (or 0).
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/src/config.ts\n//\n// The IMGLY_* configuration contract (spec §9.2). One image, switched purely\n// by runtime env. Non-secret knobs are plain env vars; the four secrets\n// (license, gateway key, two S3 keys) also accept the `*_FILE` convention via\n// resolveSecret — the de-facto Docker/K8s secret standard. No secret is ever\n// logged or written; loadConfig only reads.\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join as joinPath } from 'node:path';\n\nexport type Env = Record<string, string | undefined>;\n\n/**\n * Resolve a secret: prefer `<NAME>_FILE` (trimmed file contents — secret files\n * end in a newline) and fall back to the plain `<NAME>` env var. A superset of\n * \"just env vars\": production mounts a file, local dev passes the raw value.\n */\nexport function resolveSecret(\n name: string,\n env: Env = process.env\n): string | undefined {\n const filePath = env[`${name}_FILE`];\n if (filePath) return readFileSync(filePath, 'utf8').trim();\n return env[name];\n}\n\nexport type WorkspaceMode = 'header' | 'session' | 'single';\nexport type WorkspaceBackend = 'local' | 's3';\n\nexport interface S3Config {\n bucket: string;\n endpoint?: string;\n region: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n}\n\nexport interface WorkspaceConfig {\n backend: WorkspaceBackend;\n /** local backend only */\n dir?: string;\n /** s3 backend only */\n s3?: S3Config;\n}\n\nexport interface ServerConfig {\n cesdkLicense: string;\n /**\n * Build identity, surfaced as the MCP `serverInfo.version`. Lets a client\n * (e.g. the deploy smoketest) detect a stale, not-yet-rolled-out container:\n * the deploy injects the git SHA, the smoketest asserts the running server\n * reports it. Default `'dev'` for local runs.\n */\n buildId: string;\n port: number;\n /** Max concurrent engines/sessions — the RAM cap (spec §9.2). */\n maxEngines: number;\n idleTimeoutMs: number;\n logLevel: string;\n workspace: WorkspaceConfig;\n auth: {\n mode: WorkspaceMode;\n /** header carrying the trusted workspaceId (header mode). */\n header: string;\n /** fixed workspaceId (single mode). */\n fixedWorkspaceId?: string;\n };\n gateway: { apiKey?: string; baseUrl?: string };\n viewerCorsOrigins?: string[];\n /**\n * Public origin of this deployment (e.g. the Cloudflare Worker URL),\n * no trailing slash. Used to build viewer URLs the MCP client's human can\n * actually open. Absent → loopback URLs (local dev).\n */\n publicBaseUrl?: string;\n}\n\nfunction requireEnv(env: Env, name: string): string {\n const v = env[name];\n if (!v) throw new Error(`config: ${name} is required`);\n return v;\n}\n\nfunction intEnv(env: Env, name: string, fallback: number): number {\n const raw = env[name];\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n)) throw new Error(`config: ${name} must be a number`);\n return n;\n}\n\nfunction boolEnv(env: Env, name: string, fallback: boolean): boolean {\n const raw = env[name];\n if (raw === undefined) return fallback;\n return raw === 'true' || raw === '1';\n}\n\nexport function loadConfig(env: Env = process.env): ServerConfig {\n const cesdkLicense = resolveSecret('IMGLY_CESDK_LICENSE', env);\n if (!cesdkLicense) throw new Error('config: IMGLY_CESDK_LICENSE is required');\n\n const mode = (env.IMGLY_WORKSPACE_MODE ?? 'header') as WorkspaceMode;\n if (mode !== 'header' && mode !== 'session' && mode !== 'single')\n throw new Error(\n `config: IMGLY_WORKSPACE_MODE must be header|session|single (got \"${mode}\")`\n );\n\n const auth: ServerConfig['auth'] = {\n mode,\n header: env.IMGLY_AUTH_WORKSPACE_HEADER ?? 'X-Workspace-Id'\n };\n if (mode === 'single') {\n auth.fixedWorkspaceId = requireEnv(env, 'IMGLY_WORKSPACE_ID');\n }\n\n const backend = (env.IMGLY_WORKSPACE_BACKEND ?? 'local') as WorkspaceBackend;\n if (backend !== 'local' && backend !== 's3')\n throw new Error(\n `config: IMGLY_WORKSPACE_BACKEND must be local|s3 (got \"${backend}\")`\n );\n\n let workspace: WorkspaceConfig;\n if (backend === 's3') {\n const s3: S3Config = {\n bucket: requireEnv(env, 'IMGLY_WORKSPACE_S3_BUCKET'),\n region: env.IMGLY_WORKSPACE_S3_REGION ?? 'auto'\n };\n if (env.IMGLY_WORKSPACE_S3_ENDPOINT)\n s3.endpoint = env.IMGLY_WORKSPACE_S3_ENDPOINT;\n // Keys are optional: absent -> AWS default credential chain (IAM role).\n const accessKeyId = resolveSecret('IMGLY_WORKSPACE_S3_ACCESS_KEY_ID', env);\n const secretAccessKey = resolveSecret(\n 'IMGLY_WORKSPACE_S3_SECRET_ACCESS_KEY',\n env\n );\n if (accessKeyId) s3.accessKeyId = accessKeyId;\n if (secretAccessKey) s3.secretAccessKey = secretAccessKey;\n workspace = { backend, s3 };\n } else {\n workspace = { backend, dir: env.IMGLY_WORKSPACE_DIR ?? '/data' };\n }\n\n const gateway: ServerConfig['gateway'] = {};\n const gatewayKey = resolveSecret('IMGLY_GATEWAY_API_KEY', env);\n if (gatewayKey) gateway.apiKey = gatewayKey;\n if (env.IMGLY_GATEWAY_BASE_URL) gateway.baseUrl = env.IMGLY_GATEWAY_BASE_URL;\n\n const cfg: ServerConfig = {\n cesdkLicense,\n buildId: env.IMGLY_BUILD_ID ?? 'dev',\n port: intEnv(env, 'PORT', 8080),\n maxEngines: intEnv(env, 'IMGLY_MAX_ENGINES', 8),\n idleTimeoutMs: intEnv(env, 'IMGLY_SESSION_IDLE_TIMEOUT', 900) * 1000,\n logLevel: env.IMGLY_LOG_LEVEL ?? 'info',\n workspace,\n auth,\n gateway\n };\n if (env.IMGLY_VIEWER_CORS_ORIGINS)\n cfg.viewerCorsOrigins = env.IMGLY_VIEWER_CORS_ORIGINS.split(',').map((s) =>\n s.trim()\n );\n if (env.IMGLY_PUBLIC_BASE_URL)\n cfg.publicBaseUrl = env.IMGLY_PUBLIC_BASE_URL.replace(/\\/+$/, '');\n return cfg;\n}\n\n/**\n * Local diagnostics file logging is ON by default so evidence exists before a\n * user knows they hit a bug. Disable with IMGLY_DIAGNOSTICS_FILE=false (or 0).\n */\nexport function diagnosticsFileEnabled(env: Env = process.env): boolean {\n return boolEnv(env, 'IMGLY_DIAGNOSTICS_FILE', true);\n}\n\n/**\n * Where diagnostics NDJSON is written. Explicit IMGLY_DIAGNOSTICS_DIR wins;\n * otherwise <workspaceRoot>/logs, where workspaceRoot mirrors the stdio default\n * ($CODESIGN_WORKSPACE ?? ~/.codesign/workspace).\n */\nexport function resolveDiagnosticsDir(env: Env = process.env): string {\n if (env.IMGLY_DIAGNOSTICS_DIR) return env.IMGLY_DIAGNOSTICS_DIR;\n const workspaceRoot =\n env.CODESIGN_WORKSPACE ?? joinPath(homedir(), '.codesign', 'workspace');\n return joinPath(workspaceRoot, 'logs');\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,4CAA4C;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAC;AAI7C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,MAAW,OAAO,CAAC,GAAG;IAEtB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC;IACrC,IAAI,QAAQ;QAAE,OAAO,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC;AAqDD,SAAS,UAAU,CAAC,GAAQ,EAAE,IAAY;IACxC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,cAAc,CAAC,CAAC;IACvD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,MAAM,CAAC,GAAQ,EAAE,IAAY,EAAE,QAAgB;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACvC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,mBAAmB,CAAC,CAAC;IAC7E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,OAAO,CAAC,GAAQ,EAAE,IAAY,EAAE,QAAiB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAW,OAAO,CAAC,GAAG;IAC/C,MAAM,YAAY,GAAG,aAAa,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAE9E,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,oBAAoB,IAAI,QAAQ,CAAkB,CAAC;IACrE,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;QAC9D,MAAM,IAAI,KAAK,CACb,oEAAoE,IAAI,IAAI,CAC7E,CAAC;IAEJ,MAAM,IAAI,GAAyB;QACjC,IAAI;QACJ,MAAM,EAAE,GAAG,CAAC,2BAA2B,IAAI,gBAAgB;KAC5D,CAAC;IACF,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAqB,CAAC;IAC7E,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,IAAI;QACzC,MAAM,IAAI,KAAK,CACb,0DAA0D,OAAO,IAAI,CACtE,CAAC;IAEJ,IAAI,SAA0B,CAAC;IAC/B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,EAAE,GAAa;YACnB,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,2BAA2B,CAAC;YACpD,MAAM,EAAE,GAAG,CAAC,yBAAyB,IAAI,MAAM;SAChD,CAAC;QACF,IAAI,GAAG,CAAC,2BAA2B;YACjC,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,2BAA2B,CAAC;QAChD,wEAAwE;QACxE,MAAM,WAAW,GAAG,aAAa,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;QAC3E,MAAM,eAAe,GAAG,aAAa,CACnC,sCAAsC,EACtC,GAAG,CACJ,CAAC;QACF,IAAI,WAAW;YAAE,EAAE,CAAC,WAAW,GAAG,WAAW,CAAC;QAC9C,IAAI,eAAe;YAAE,EAAE,CAAC,eAAe,GAAG,eAAe,CAAC;QAC1D,SAAS,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,SAAS,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,mBAAmB,IAAI,OAAO,EAAE,CAAC;IACnE,CAAC;IAED,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,UAAU;QAAE,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC;IAC5C,IAAI,GAAG,CAAC,sBAAsB;QAAE,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC,sBAAsB,CAAC;IAE7E,MAAM,GAAG,GAAiB;QACxB,YAAY;QACZ,OAAO,EAAE,GAAG,CAAC,cAAc,IAAI,KAAK;QACpC,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC;QAC/B,cAAc,EAAE,MAAM,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACxD,aAAa,EAAE,MAAM,CAAC,GAAG,EAAE,4BAA4B,EAAE,GAAG,CAAC,GAAG,IAAI;QACpE,QAAQ,EAAE,GAAG,CAAC,eAAe,IAAI,MAAM;QACvC,SAAS;QACT,IAAI;QACJ,OAAO;KACR,CAAC;IACF,IAAI,GAAG,CAAC,yBAAyB;QAC/B,GAAG,CAAC,iBAAiB,GAAG,GAAG,CAAC,yBAAyB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACzE,CAAC,CAAC,IAAI,EAAE,CACT,CAAC;IACJ,IAAI,GAAG,CAAC,qBAAqB;QAC3B,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAW,OAAO,CAAC,GAAG;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,yBAAyB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAW,OAAO,CAAC,GAAG;IAC3D,OAAO,OAAO,CAAC,GAAG,EAAE,wBAAwB,EAAE,IAAI,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAW,OAAO,CAAC,GAAG;IAC1D,IAAI,GAAG,CAAC,qBAAqB;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC;IAChE,MAAM,aAAa,GACjB,GAAG,CAAC,kBAAkB,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC","sourcesContent":["// packages/codesign-mcp/src/config.ts\n//\n// The IMGLY_* configuration contract (spec §9.2). One image, switched purely\n// by runtime env. Non-secret knobs are plain env vars; the four secrets\n// (license, gateway key, two S3 keys) also accept the `*_FILE` convention via\n// resolveSecret — the de-facto Docker/K8s secret standard. No secret is ever\n// logged or written; loadConfig only reads.\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join as joinPath } from 'node:path';\n\nexport type Env = Record<string, string | undefined>;\n\n/**\n * Resolve a secret: prefer `<NAME>_FILE` (trimmed file contents — secret files\n * end in a newline) and fall back to the plain `<NAME>` env var. A superset of\n * \"just env vars\": production mounts a file, local dev passes the raw value.\n */\nexport function resolveSecret(\n name: string,\n env: Env = process.env\n): string | undefined {\n const filePath = env[`${name}_FILE`];\n if (filePath) return readFileSync(filePath, 'utf8').trim();\n return env[name];\n}\n\nexport type WorkspaceMode = 'header' | 'session' | 'single';\nexport type WorkspaceBackend = 'local' | 's3';\n\nexport interface S3Config {\n bucket: string;\n endpoint?: string;\n region: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n}\n\nexport interface WorkspaceConfig {\n backend: WorkspaceBackend;\n /** local backend only */\n dir?: string;\n /** s3 backend only */\n s3?: S3Config;\n}\n\nexport interface ServerConfig {\n cesdkLicense: string;\n /**\n * Build identity, surfaced as the MCP `serverInfo.version`. Lets a client\n * (e.g. the deploy smoketest) detect a stale, not-yet-rolled-out container:\n * the deploy injects the git SHA, the smoketest asserts the running server\n * reports it. Default `'dev'` for local runs.\n */\n buildId: string;\n port: number;\n /** Max concurrent engines/sessions per HTTP server process — the RAM cap (spec §9.2). */\n maxHttpEngines: number;\n idleTimeoutMs: number;\n logLevel: string;\n workspace: WorkspaceConfig;\n auth: {\n mode: WorkspaceMode;\n /** header carrying the trusted workspaceId (header mode). */\n header: string;\n /** fixed workspaceId (single mode). */\n fixedWorkspaceId?: string;\n };\n gateway: { apiKey?: string; baseUrl?: string };\n viewerCorsOrigins?: string[];\n /**\n * Public origin of this deployment (e.g. the Cloudflare Worker URL),\n * no trailing slash. Used to build viewer URLs the MCP client's human can\n * actually open. Absent → loopback URLs (local dev).\n */\n publicBaseUrl?: string;\n}\n\nfunction requireEnv(env: Env, name: string): string {\n const v = env[name];\n if (!v) throw new Error(`config: ${name} is required`);\n return v;\n}\n\nfunction intEnv(env: Env, name: string, fallback: number): number {\n const raw = env[name];\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n)) throw new Error(`config: ${name} must be a number`);\n return n;\n}\n\nfunction boolEnv(env: Env, name: string, fallback: boolean): boolean {\n const raw = env[name];\n if (raw === undefined) return fallback;\n return raw === 'true' || raw === '1';\n}\n\nexport function loadConfig(env: Env = process.env): ServerConfig {\n const cesdkLicense = resolveSecret('IMGLY_CESDK_LICENSE', env);\n if (!cesdkLicense) throw new Error('config: IMGLY_CESDK_LICENSE is required');\n\n const mode = (env.IMGLY_WORKSPACE_MODE ?? 'header') as WorkspaceMode;\n if (mode !== 'header' && mode !== 'session' && mode !== 'single')\n throw new Error(\n `config: IMGLY_WORKSPACE_MODE must be header|session|single (got \"${mode}\")`\n );\n\n const auth: ServerConfig['auth'] = {\n mode,\n header: env.IMGLY_AUTH_WORKSPACE_HEADER ?? 'X-Workspace-Id'\n };\n if (mode === 'single') {\n auth.fixedWorkspaceId = requireEnv(env, 'IMGLY_WORKSPACE_ID');\n }\n\n const backend = (env.IMGLY_WORKSPACE_BACKEND ?? 'local') as WorkspaceBackend;\n if (backend !== 'local' && backend !== 's3')\n throw new Error(\n `config: IMGLY_WORKSPACE_BACKEND must be local|s3 (got \"${backend}\")`\n );\n\n let workspace: WorkspaceConfig;\n if (backend === 's3') {\n const s3: S3Config = {\n bucket: requireEnv(env, 'IMGLY_WORKSPACE_S3_BUCKET'),\n region: env.IMGLY_WORKSPACE_S3_REGION ?? 'auto'\n };\n if (env.IMGLY_WORKSPACE_S3_ENDPOINT)\n s3.endpoint = env.IMGLY_WORKSPACE_S3_ENDPOINT;\n // Keys are optional: absent -> AWS default credential chain (IAM role).\n const accessKeyId = resolveSecret('IMGLY_WORKSPACE_S3_ACCESS_KEY_ID', env);\n const secretAccessKey = resolveSecret(\n 'IMGLY_WORKSPACE_S3_SECRET_ACCESS_KEY',\n env\n );\n if (accessKeyId) s3.accessKeyId = accessKeyId;\n if (secretAccessKey) s3.secretAccessKey = secretAccessKey;\n workspace = { backend, s3 };\n } else {\n workspace = { backend, dir: env.IMGLY_WORKSPACE_DIR ?? '/data' };\n }\n\n const gateway: ServerConfig['gateway'] = {};\n const gatewayKey = resolveSecret('IMGLY_GATEWAY_API_KEY', env);\n if (gatewayKey) gateway.apiKey = gatewayKey;\n if (env.IMGLY_GATEWAY_BASE_URL) gateway.baseUrl = env.IMGLY_GATEWAY_BASE_URL;\n\n const cfg: ServerConfig = {\n cesdkLicense,\n buildId: env.IMGLY_BUILD_ID ?? 'dev',\n port: intEnv(env, 'PORT', 8080),\n maxHttpEngines: intEnv(env, 'IMGLY_MAX_HTTP_ENGINES', 8),\n idleTimeoutMs: intEnv(env, 'IMGLY_SESSION_IDLE_TIMEOUT', 900) * 1000,\n logLevel: env.IMGLY_LOG_LEVEL ?? 'info',\n workspace,\n auth,\n gateway\n };\n if (env.IMGLY_VIEWER_CORS_ORIGINS)\n cfg.viewerCorsOrigins = env.IMGLY_VIEWER_CORS_ORIGINS.split(',').map((s) =>\n s.trim()\n );\n if (env.IMGLY_PUBLIC_BASE_URL)\n cfg.publicBaseUrl = env.IMGLY_PUBLIC_BASE_URL.replace(/\\/+$/, '');\n return cfg;\n}\n\n/**\n * Machine-wide cap on concurrently booting CE.SDK engines for the LOCAL stdio\n * transport (CODE-59). Distinct from IMGLY_MAX_HTTP_ENGINES (per HTTP server\n * process): different scope, different default, and different at-capacity\n * behavior (stdio fails fast; HTTP evicts/queues). Floored at 1 so a stray 0\n * cannot brick the server. Read here (not in loadConfig) because the stdio path\n * never calls loadConfig.\n */\nexport function resolveMaxStdioEngines(env: Env = process.env): number {\n return Math.max(1, Math.floor(intEnv(env, 'IMGLY_MAX_STDIO_ENGINES', 4)));\n}\n\n/**\n * Local diagnostics file logging is ON by default so evidence exists before a\n * user knows they hit a bug. Disable with IMGLY_DIAGNOSTICS_FILE=false (or 0).\n */\nexport function diagnosticsFileEnabled(env: Env = process.env): boolean {\n return boolEnv(env, 'IMGLY_DIAGNOSTICS_FILE', true);\n}\n\n/**\n * Where diagnostics NDJSON is written. Explicit IMGLY_DIAGNOSTICS_DIR wins;\n * otherwise <workspaceRoot>/logs, where workspaceRoot mirrors the stdio default\n * ($CODESIGN_WORKSPACE ?? ~/.codesign/workspace).\n */\nexport function resolveDiagnosticsDir(env: Env = process.env): string {\n if (env.IMGLY_DIAGNOSTICS_DIR) return env.IMGLY_DIAGNOSTICS_DIR;\n const workspaceRoot =\n env.CODESIGN_WORKSPACE ?? joinPath(homedir(), '.codesign', 'workspace');\n return joinPath(workspaceRoot, 'logs');\n}\n"]}
@@ -28,6 +28,12 @@ export type DiagnosticEvent = {
28
28
  };
29
29
  export interface DiagnosticsSink {
30
30
  emit(event: DiagnosticEvent): void;
31
+ /**
32
+ * Await all queued writes. Optional — a sink may write synchronously or drop
33
+ * events. Call before `process.exit` so a fire-and-forget sink doesn't lose
34
+ * the last events (e.g. the shutdown breadcrumb).
35
+ */
36
+ flush?(): Promise<void>;
31
37
  }
32
38
  /** The off-switch: a sink that accepts and discards every event. */
33
39
  export declare const NOOP_DIAGNOSTICS_SINK: DiagnosticsSink;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/diagnostics/events.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEN,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACpC;AAED,oEAAoE;AACpE,eAAO,MAAM,qBAAqB,EAAE,eAA+B,CAAC"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/diagnostics/events.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEN,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC;;;;OAIG;IACH,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,oEAAoE;AACpE,eAAO,MAAM,qBAAqB,EAAE,eAA+B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/diagnostics/events.ts"],"names":[],"mappings":"AA2CA,oEAAoE;AACpE,MAAM,CAAC,MAAM,qBAAqB,GAAoB,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC","sourcesContent":["// packages/codesign-mcp/src/diagnostics/events.ts\n//\n// The single transport shape for the diagnostics stream. Every diagnostic datum\n// — tool call, engine console line, lifecycle breadcrumb — is one DiagnosticEvent,\n// one NDJSON line. Sinks (file today; OTel/in-memory later) consume this union.\nimport type { Span } from '../tools/spans.js';\n\nexport interface ErrorInfo {\n message: string;\n stack?: string;\n}\n\nexport type DiagnosticEvent =\n | {\n kind: 'tool_call';\n timestamp: number;\n tool: string;\n succeeded: boolean;\n totalMs: number;\n spans: Span[];\n error?: ErrorInfo;\n args?: unknown;\n result?: unknown;\n revision?: string;\n session?: string;\n }\n | {\n kind: 'engine_log';\n timestamp: number;\n level: 'log' | 'info' | 'warn' | 'error' | 'debug' | 'trace';\n text: string;\n }\n | {\n kind: 'lifecycle';\n timestamp: number;\n text: string;\n error?: ErrorInfo;\n };\n\nexport interface DiagnosticsSink {\n emit(event: DiagnosticEvent): void;\n}\n\n/** The off-switch: a sink that accepts and discards every event. */\nexport const NOOP_DIAGNOSTICS_SINK: DiagnosticsSink = { emit() {} };\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/diagnostics/events.ts"],"names":[],"mappings":"AAiDA,oEAAoE;AACpE,MAAM,CAAC,MAAM,qBAAqB,GAAoB,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC","sourcesContent":["// packages/codesign-mcp/src/diagnostics/events.ts\n//\n// The single transport shape for the diagnostics stream. Every diagnostic datum\n// — tool call, engine console line, lifecycle breadcrumb — is one DiagnosticEvent,\n// one NDJSON line. Sinks (file today; OTel/in-memory later) consume this union.\nimport type { Span } from '../tools/spans.js';\n\nexport interface ErrorInfo {\n message: string;\n stack?: string;\n}\n\nexport type DiagnosticEvent =\n | {\n kind: 'tool_call';\n timestamp: number;\n tool: string;\n succeeded: boolean;\n totalMs: number;\n spans: Span[];\n error?: ErrorInfo;\n args?: unknown;\n result?: unknown;\n revision?: string;\n session?: string;\n }\n | {\n kind: 'engine_log';\n timestamp: number;\n level: 'log' | 'info' | 'warn' | 'error' | 'debug' | 'trace';\n text: string;\n }\n | {\n kind: 'lifecycle';\n timestamp: number;\n text: string;\n error?: ErrorInfo;\n };\n\nexport interface DiagnosticsSink {\n emit(event: DiagnosticEvent): void;\n /**\n * Await all queued writes. Optional — a sink may write synchronously or drop\n * events. Call before `process.exit` so a fire-and-forget sink doesn't lose\n * the last events (e.g. the shutdown breadcrumb).\n */\n flush?(): Promise<void>;\n}\n\n/** The off-switch: a sink that accepts and discards every event. */\nexport const NOOP_DIAGNOSTICS_SINK: DiagnosticsSink = { emit() {} };\n"]}