@inharness-ai/claude4spec 1.0.21 → 1.0.23

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 (241) hide show
  1. package/.claude/skills/c4s-brief-implementer/SKILL.md +120 -53
  2. package/CHANGELOG.md +20 -0
  3. package/dist/bin/c4s/commands/agent.d.ts +18 -0
  4. package/dist/bin/c4s/commands/agent.js +102 -0
  5. package/dist/bin/c4s/commands/agent.js.map +1 -0
  6. package/dist/bin/c4s/commands/ask.d.ts +9 -8
  7. package/dist/bin/c4s/commands/ask.js +23 -31
  8. package/dist/bin/c4s/commands/ask.js.map +1 -1
  9. package/dist/bin/c4s/commands/catalog.d.ts +1 -1
  10. package/dist/bin/c4s/commands/catalog.js +2 -2
  11. package/dist/bin/c4s/commands/catalog.js.map +1 -1
  12. package/dist/bin/c4s/commands/describe.d.ts +1 -1
  13. package/dist/bin/c4s/commands/describe.js +2 -2
  14. package/dist/bin/c4s/commands/describe.js.map +1 -1
  15. package/dist/bin/c4s/commands/detail.d.ts +1 -1
  16. package/dist/bin/c4s/commands/detail.js +2 -2
  17. package/dist/bin/c4s/commands/detail.js.map +1 -1
  18. package/dist/bin/c4s/commands/element-list.d.ts +1 -1
  19. package/dist/bin/c4s/commands/element-list.js +2 -2
  20. package/dist/bin/c4s/commands/element-list.js.map +1 -1
  21. package/dist/bin/c4s/commands/find-references.js +1 -1
  22. package/dist/bin/c4s/commands/find-references.js.map +1 -1
  23. package/dist/bin/c4s/commands/inline-mention.d.ts +1 -1
  24. package/dist/bin/c4s/commands/inline-mention.js +2 -2
  25. package/dist/bin/c4s/commands/inline-mention.js.map +1 -1
  26. package/dist/bin/c4s/commands/list-slugs.d.ts +1 -1
  27. package/dist/bin/c4s/commands/list-slugs.js +2 -2
  28. package/dist/bin/c4s/commands/list-slugs.js.map +1 -1
  29. package/dist/bin/c4s/commands/list-tags.d.ts +1 -1
  30. package/dist/bin/c4s/commands/list-tags.js +2 -2
  31. package/dist/bin/c4s/commands/list-tags.js.map +1 -1
  32. package/dist/bin/c4s/commands/plugins.d.ts +9 -0
  33. package/dist/bin/c4s/commands/plugins.js +81 -0
  34. package/dist/bin/c4s/commands/plugins.js.map +1 -0
  35. package/dist/bin/c4s/commands/resolve.d.ts +1 -1
  36. package/dist/bin/c4s/commands/resolve.js +2 -2
  37. package/dist/bin/c4s/commands/resolve.js.map +1 -1
  38. package/dist/bin/c4s/commands/single-element.d.ts +1 -1
  39. package/dist/bin/c4s/commands/single-element.js +2 -2
  40. package/dist/bin/c4s/commands/single-element.js.map +1 -1
  41. package/dist/bin/c4s/commands/tagged-list-mixed.d.ts +1 -1
  42. package/dist/bin/c4s/commands/tagged-list-mixed.js +2 -2
  43. package/dist/bin/c4s/commands/tagged-list-mixed.js.map +1 -1
  44. package/dist/bin/c4s/commands/tagged-list.d.ts +1 -1
  45. package/dist/bin/c4s/commands/tagged-list.js +2 -2
  46. package/dist/bin/c4s/commands/tagged-list.js.map +1 -1
  47. package/dist/bin/c4s/context.d.ts +3 -3
  48. package/dist/bin/c4s/context.js +7 -4
  49. package/dist/bin/c4s/context.js.map +1 -1
  50. package/dist/bin/c4s/errors.d.ts +1 -1
  51. package/dist/bin/c4s/errors.js.map +1 -1
  52. package/dist/bin/c4s-mcp.js +6 -2
  53. package/dist/bin/c4s-mcp.js.map +1 -1
  54. package/dist/bin/c4s.js +19 -4
  55. package/dist/bin/c4s.js.map +1 -1
  56. package/dist/bin/gitignore.js +7 -11
  57. package/dist/bin/gitignore.js.map +1 -1
  58. package/dist/client/assets/{arc-Coa9fbDO.js → arc-C9oTB3b1.js} +1 -1
  59. package/dist/client/assets/{architectureDiagram-3BPJPVTR-VJZcRMEN.js → architectureDiagram-3BPJPVTR-Bno5ViqK.js} +1 -1
  60. package/dist/client/assets/{blockDiagram-GPEHLZMM-C7dU2lW1.js → blockDiagram-GPEHLZMM-CJYBoeWQ.js} +1 -1
  61. package/dist/client/assets/{c4Diagram-AAUBKEIU-CG_BBm_5.js → c4Diagram-AAUBKEIU-DaaqWE6s.js} +1 -1
  62. package/dist/client/assets/channel-Cei_E79M.js +1 -0
  63. package/dist/client/assets/{chunk-2J33WTMH-BnS95YpK.js → chunk-2J33WTMH-P_I0OPMy.js} +1 -1
  64. package/dist/client/assets/{chunk-4BX2VUAB-COr8w3X3.js → chunk-4BX2VUAB-CCySMlh5.js} +1 -1
  65. package/dist/client/assets/{chunk-55IACEB6-CxrIbZhB.js → chunk-55IACEB6-CEUyqKGF.js} +1 -1
  66. package/dist/client/assets/{chunk-727SXJPM-DRchPPRe.js → chunk-727SXJPM-CoTSYOVz.js} +1 -1
  67. package/dist/client/assets/{chunk-AQP2D5EJ-_SfN7mED.js → chunk-AQP2D5EJ-DB0nMxPr.js} +1 -1
  68. package/dist/client/assets/{chunk-FMBD7UC4-p2tIiP1D.js → chunk-FMBD7UC4-PG3m6-_a.js} +1 -1
  69. package/dist/client/assets/{chunk-ND2GUHAM-B1mbl2hP.js → chunk-ND2GUHAM-CBNk6IaV.js} +1 -1
  70. package/dist/client/assets/{chunk-QZHKN3VN-DBUpeqvS.js → chunk-QZHKN3VN-CLFa0WvN.js} +1 -1
  71. package/dist/client/assets/classDiagram-4FO5ZUOK-DUe_fAto.js +1 -0
  72. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DUe_fAto.js +1 -0
  73. package/dist/client/assets/{cose-bilkent-S5V4N54A-BBsY5aKD.js → cose-bilkent-S5V4N54A-BDwDDxHN.js} +1 -1
  74. package/dist/client/assets/{dagre-BM42HDAG-b-vu4RC9.js → dagre-BM42HDAG-DR-1e8N5.js} +1 -1
  75. package/dist/client/assets/{diagram-2AECGRRQ-j-DW9qSf.js → diagram-2AECGRRQ-C-tHgBWt.js} +1 -1
  76. package/dist/client/assets/{diagram-5GNKFQAL-DHX_WYWQ.js → diagram-5GNKFQAL-uLmoKaJL.js} +1 -1
  77. package/dist/client/assets/{diagram-KO2AKTUF-DxdQok_M.js → diagram-KO2AKTUF-8Fd-xuOy.js} +1 -1
  78. package/dist/client/assets/{diagram-LMA3HP47-BbTIX5c1.js → diagram-LMA3HP47-C1gniWsn.js} +1 -1
  79. package/dist/client/assets/{diagram-OG6HWLK6-BQ9ODAlH.js → diagram-OG6HWLK6-BMWjZbeC.js} +1 -1
  80. package/dist/client/assets/{erDiagram-TEJ5UH35-6lwAL1y9.js → erDiagram-TEJ5UH35-CIUO8hrR.js} +1 -1
  81. package/dist/client/assets/{flowDiagram-I6XJVG4X-ioSHb5-t.js → flowDiagram-I6XJVG4X-Ccw5YEf3.js} +1 -1
  82. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CXFQp9Nj.js → ganttDiagram-6RSMTGT7-BAvlf8cY.js} +1 -1
  83. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-C1pVD_NQ.js → gitGraphDiagram-PVQCEYII-ChCkdYar.js} +1 -1
  84. package/dist/client/assets/{index-JaAxQJXh.js → index-BotCWOKC.js} +1 -1
  85. package/dist/client/assets/index-CtdzGyqj.js +805 -0
  86. package/dist/client/assets/{index-hz5T_W2e.js → index-DtpKHGD1.js} +1 -1
  87. package/dist/client/assets/{infoDiagram-5YYISTIA-C2Guev-g.js → infoDiagram-5YYISTIA-CZMGwNyB.js} +1 -1
  88. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D3v8VBb2.js → ishikawaDiagram-YF4QCWOH-CubjZGk6.js} +1 -1
  89. package/dist/client/assets/{journeyDiagram-JHISSGLW-DX1AW_Ap.js → journeyDiagram-JHISSGLW-DzwOyVzB.js} +1 -1
  90. package/dist/client/assets/{kanban-definition-UN3LZRKU-CzXThvq2.js → kanban-definition-UN3LZRKU-CXznoTY9.js} +1 -1
  91. package/dist/client/assets/{linear-CVw4gl5i.js → linear-EDwTFRrU.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-C29eoXRj.js → mermaid.core-Lae2097R.js} +4 -4
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DEEGVQVA.js → mindmap-definition-RKZ34NQL-DFjIW41g.js} +1 -1
  94. package/dist/client/assets/{pieDiagram-4H26LBE5-Dme-VM6m.js → pieDiagram-4H26LBE5-CZDLR1jd.js} +1 -1
  95. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CURFNo24.js → quadrantDiagram-W4KKPZXB-CTGDzbqp.js} +1 -1
  96. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DxMZWXME.js → requirementDiagram-4Y6WPE33-CeH0NbMv.js} +1 -1
  97. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BW64D4_P.js → sankeyDiagram-5OEKKPKP-B6ZTjurV.js} +1 -1
  98. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BATRPm8Q.js → sequenceDiagram-3UESZ5HK-DcTRztVx.js} +1 -1
  99. package/dist/client/assets/{stateDiagram-AJRCARHV-pgHVMnO3.js → stateDiagram-AJRCARHV-nUgRVZSA.js} +1 -1
  100. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D9pJuQbI.js +1 -0
  101. package/dist/client/assets/{timeline-definition-PNZ67QCA-qBxJZPWd.js → timeline-definition-PNZ67QCA-CXm5n4rV.js} +1 -1
  102. package/dist/client/assets/{vennDiagram-CIIHVFJN-BZJK9hqJ.js → vennDiagram-CIIHVFJN-EmNJBYNd.js} +1 -1
  103. package/dist/client/assets/{wardley-L42UT6IY-BobtgEXg.js → wardley-L42UT6IY-MLrlp8H-.js} +1 -1
  104. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DuWfX_-P.js → wardleyDiagram-YWT4CUSO-5QoES25n.js} +1 -1
  105. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DCw1iURo.js → xychartDiagram-2RQKCTM6-CbDEQbY0.js} +1 -1
  106. package/dist/client/index.html +1 -1
  107. package/dist/core/agent/run-agent.d.ts +61 -0
  108. package/dist/core/agent/run-agent.js +212 -0
  109. package/dist/core/agent/run-agent.js.map +1 -0
  110. package/dist/core/ask/run-ask.d.ts +1 -1
  111. package/dist/core/ask/run-ask.js +14 -5
  112. package/dist/core/ask/run-ask.js.map +1 -1
  113. package/dist/core/workspace/resolve.d.ts +2 -0
  114. package/dist/core/workspace/resolve.js +2 -1
  115. package/dist/core/workspace/resolve.js.map +1 -1
  116. package/dist/plugin-types/ambient.d.ts +30 -0
  117. package/dist/plugin-types/plugin-runtime.d.ts +172 -0
  118. package/dist/plugin-types/ui.d.ts +154 -0
  119. package/dist/server/config.d.ts +12 -0
  120. package/dist/server/config.js +26 -0
  121. package/dist/server/config.js.map +1 -1
  122. package/dist/server/core/plugin-host/__fixtures__/plugins.d.ts +14 -0
  123. package/dist/server/core/plugin-host/__fixtures__/plugins.js +56 -0
  124. package/dist/server/core/plugin-host/__fixtures__/plugins.js.map +1 -0
  125. package/dist/server/core/plugin-host/base-packages.d.ts +14 -0
  126. package/dist/server/core/plugin-host/base-packages.js +26 -0
  127. package/dist/server/core/plugin-host/base-packages.js.map +1 -0
  128. package/dist/server/core/plugin-host/cache-bust.d.ts +10 -0
  129. package/dist/server/core/plugin-host/cache-bust.js +21 -0
  130. package/dist/server/core/plugin-host/cache-bust.js.map +1 -0
  131. package/dist/server/core/plugin-host/cli-engine.d.ts +7 -2
  132. package/dist/server/core/plugin-host/cli-engine.js +11 -3
  133. package/dist/server/core/plugin-host/cli-engine.js.map +1 -1
  134. package/dist/server/core/plugin-host/cli-plugins.d.ts +23 -0
  135. package/dist/server/core/plugin-host/cli-plugins.js +49 -0
  136. package/dist/server/core/plugin-host/cli-plugins.js.map +1 -0
  137. package/dist/server/core/plugin-host/cross-cutting.d.ts +41 -4
  138. package/dist/server/core/plugin-host/cross-cutting.js +52 -4
  139. package/dist/server/core/plugin-host/cross-cutting.js.map +1 -1
  140. package/dist/server/core/plugin-host/frontend-assets.d.ts +100 -0
  141. package/dist/server/core/plugin-host/frontend-assets.js +262 -0
  142. package/dist/server/core/plugin-host/frontend-assets.js.map +1 -0
  143. package/dist/server/core/plugin-host/frontend-manifest.d.ts +33 -0
  144. package/dist/server/core/plugin-host/frontend-manifest.js +60 -0
  145. package/dist/server/core/plugin-host/frontend-manifest.js.map +1 -0
  146. package/dist/server/core/plugin-host/loader.d.ts +107 -0
  147. package/dist/server/core/plugin-host/loader.js +196 -0
  148. package/dist/server/core/plugin-host/loader.js.map +1 -0
  149. package/dist/server/core/plugin-host/manifest-adapter.d.ts +30 -0
  150. package/dist/server/core/plugin-host/manifest-adapter.js +119 -0
  151. package/dist/server/core/plugin-host/manifest-adapter.js.map +1 -0
  152. package/dist/server/core/plugin-host/overlay-loader.d.ts +60 -0
  153. package/dist/server/core/plugin-host/overlay-loader.js +243 -0
  154. package/dist/server/core/plugin-host/overlay-loader.js.map +1 -0
  155. package/dist/server/core/plugin-host/plugin-watcher.d.ts +41 -0
  156. package/dist/server/core/plugin-host/plugin-watcher.js +95 -0
  157. package/dist/server/core/plugin-host/plugin-watcher.js.map +1 -0
  158. package/dist/server/core/plugin-host/project-host.d.ts +8 -2
  159. package/dist/server/core/plugin-host/project-host.js +56 -6
  160. package/dist/server/core/plugin-host/project-host.js.map +1 -1
  161. package/dist/server/core/plugin-host/registry.d.ts +26 -2
  162. package/dist/server/core/plugin-host/registry.js +107 -2
  163. package/dist/server/core/plugin-host/registry.js.map +1 -1
  164. package/dist/server/core/plugin-host/runtime-shims.d.ts +26 -0
  165. package/dist/server/core/plugin-host/runtime-shims.js +96 -0
  166. package/dist/server/core/plugin-host/runtime-shims.js.map +1 -0
  167. package/dist/server/core/plugin-host/types.d.ts +105 -3
  168. package/dist/server/db/migrations/042_chat_thread_drop_context_type_check.sql +61 -0
  169. package/dist/server/external-skills/brief-implementer-template.d.ts +1 -1
  170. package/dist/server/external-skills/brief-implementer-template.js +5 -2
  171. package/dist/server/external-skills/brief-implementer-template.js.map +1 -1
  172. package/dist/server/external-skills/spec-reader-template.d.ts +1 -1
  173. package/dist/server/external-skills/spec-reader-template.js +14 -0
  174. package/dist/server/external-skills/spec-reader-template.js.map +1 -1
  175. package/dist/server/index.js +82 -1
  176. package/dist/server/index.js.map +1 -1
  177. package/dist/server/mcp/c4s-tools.d.ts +6 -1
  178. package/dist/server/mcp/c4s-tools.js +18 -13
  179. package/dist/server/mcp/c4s-tools.js.map +1 -1
  180. package/dist/server/plugin-runtime/index.d.ts +17 -0
  181. package/dist/server/plugin-runtime/index.js +16 -0
  182. package/dist/server/plugin-runtime/index.js.map +1 -0
  183. package/dist/server/plugin-runtime/ui.d.ts +15 -0
  184. package/dist/server/plugin-runtime/ui.js +15 -0
  185. package/dist/server/plugin-runtime/ui.js.map +1 -0
  186. package/dist/server/routes/agent-turn.d.ts +8 -1
  187. package/dist/server/routes/agent-turn.js +67 -39
  188. package/dist/server/routes/agent-turn.js.map +1 -1
  189. package/dist/server/routes/config.d.ts +8 -0
  190. package/dist/server/routes/config.js +41 -3
  191. package/dist/server/routes/config.js.map +1 -1
  192. package/dist/server/routes/plugins.d.ts +43 -0
  193. package/dist/server/routes/plugins.js +93 -0
  194. package/dist/server/routes/plugins.js.map +1 -0
  195. package/dist/server/routes/threads.js +21 -3
  196. package/dist/server/routes/threads.js.map +1 -1
  197. package/dist/server/serialization/registerAll.d.ts +4 -0
  198. package/dist/server/serialization/registerAll.js +4 -2
  199. package/dist/server/serialization/registerAll.js.map +1 -1
  200. package/dist/server/services/chat-context.d.ts +34 -0
  201. package/dist/server/services/chat-context.js +58 -15
  202. package/dist/server/services/chat-context.js.map +1 -1
  203. package/dist/server/services/chat.d.ts +6 -0
  204. package/dist/server/services/chat.js +18 -2
  205. package/dist/server/services/chat.js.map +1 -1
  206. package/dist/server/services/entity-writer.d.ts +2 -3
  207. package/dist/server/services/entity-writer.js.map +1 -1
  208. package/dist/server/services/skill-registry.d.ts +31 -2
  209. package/dist/server/services/skill-registry.js +73 -1
  210. package/dist/server/services/skill-registry.js.map +1 -1
  211. package/dist/server/workspace/context-cache.d.ts +9 -0
  212. package/dist/server/workspace/context-cache.js +14 -0
  213. package/dist/server/workspace/context-cache.js.map +1 -1
  214. package/dist/server/workspace/project-context.d.ts +3 -0
  215. package/dist/server/workspace/project-context.js +106 -4
  216. package/dist/server/workspace/project-context.js.map +1 -1
  217. package/dist/server/workspace/registry.d.ts +28 -1
  218. package/dist/server/workspace/registry.js +57 -1
  219. package/dist/server/workspace/registry.js.map +1 -1
  220. package/dist/server/workspace/types.d.ts +15 -0
  221. package/dist/shared/entities.d.ts +4 -1
  222. package/dist/shared/entities.js.map +1 -1
  223. package/dist/shared/plugin-host/frontend-manifest.d.ts +56 -0
  224. package/dist/shared/plugin-host/frontend-manifest.js +89 -0
  225. package/dist/shared/plugin-host/frontend-manifest.js.map +1 -0
  226. package/dist/shared/plugin-host/host-api.d.ts +60 -0
  227. package/dist/shared/plugin-host/host-api.js +67 -0
  228. package/dist/shared/plugin-host/host-api.js.map +1 -0
  229. package/dist/shared/plugin-host/manifest.d.ts +194 -0
  230. package/dist/shared/plugin-host/manifest.js +52 -0
  231. package/dist/shared/plugin-host/manifest.js.map +1 -0
  232. package/dist/shared/plugin-host/ui-kit-surface.d.ts +22 -0
  233. package/dist/shared/plugin-host/ui-kit-surface.js +25 -0
  234. package/dist/shared/plugin-host/ui-kit-surface.js.map +1 -0
  235. package/dist/shared/types.d.ts +5 -0
  236. package/package.json +18 -3
  237. package/dist/client/assets/channel-D5gzBvo7.js +0 -1
  238. package/dist/client/assets/classDiagram-4FO5ZUOK-CT-amIxa.js +0 -1
  239. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CT-amIxa.js +0 -1
  240. package/dist/client/assets/index-DyAnw7YN.js +0 -798
  241. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-szm_LIuD.js +0 -1
@@ -1,14 +1,62 @@
1
1
  /**
2
- * Cross-cutting routes owned by the plugin host (M13). Currently exposes the
3
- * activation diagnostic endpoint. Phase 4 moves /api/entities/:type/:slug/versions
4
- * here too.
2
+ * Cross-cutting routes owned by the plugin host (M13), mounted on the PER-PROJECT
3
+ * router (so paths resolve under `/api/projects/:id`). Provides overlay-aware
4
+ * diagnostics + the machine-local trust gate:
5
+ *
6
+ * GET /_meta/entities → activation partition (active/inactive/unknown)
7
+ * GET /_meta/plugins → base ∪ overlay package diagnostics + shadow report
8
+ * POST /trust-plugins → persist trustProjectPlugins, trigger context rebuild
5
9
  */
6
10
  import { Router } from 'express';
7
- export function pluginHostRouter(host) {
11
+ import { HOST_API_VERSION } from '../../../shared/plugin-host/manifest.js';
12
+ export function pluginHostRouter(deps) {
13
+ const { host, registry, workspace, projectId, basePackages, overlayRecords, localPluginsPresent, trust, onContextConfigChanged, } = deps;
8
14
  const router = Router();
9
15
  router.get('/_meta/entities', (_req, res) => {
10
16
  res.json(host.partition());
11
17
  });
18
+ // M33: settings/commands of all loaded+trusted plugins in the
19
+ // effective pool — axis B, deliberately NOT filtered by `config.entities`
20
+ // (so a plugin's Settings section + slash-commands survive deactivation of
21
+ // its entity types). The Settings panel renders one section per plugin under
22
+ // `config.plugins[<name>]`; the editor registers each command as a slash
23
+ // extension.
24
+ router.get('/_meta/plugin-settings', (_req, res) => {
25
+ res.json({ sections: host.listSettings() });
26
+ });
27
+ router.get('/_meta/plugin-commands', (_req, res) => {
28
+ res.json({ commands: host.listCommands() });
29
+ });
30
+ router.get('/_meta/plugins', (_req, res) => {
31
+ const baseOriginForType = (type) => basePackages.find((p) => p.contributedTypes?.includes(type))?.package ?? '@c4s/builtin';
32
+ const shadowed = host.shadowReport().map((s) => ({
33
+ type: s.type,
34
+ overlayOrigin: s.overlayOrigin,
35
+ baseOrigin: baseOriginForType(s.type),
36
+ }));
37
+ const response = {
38
+ hostApiVersion: HOST_API_VERSION,
39
+ localPluginsPresent,
40
+ trust,
41
+ packages: [...basePackages, ...overlayRecords],
42
+ shadowed,
43
+ };
44
+ res.json(response);
45
+ });
46
+ // M33 trust gate. Persists the decision per (workspace × project) in
47
+ // `~/.claude4spec/` (never the repo) and rebuilds the context so the overlay
48
+ // (un)loads without a process restart. The client refetches activation after.
49
+ router.post('/trust-plugins', (req, res) => {
50
+ const value = req.body?.trust;
51
+ if (typeof value !== 'boolean') {
52
+ return res
53
+ .status(400)
54
+ .json({ error: { code: 'VALIDATION', message: 'trust must be a boolean' } });
55
+ }
56
+ registry.setProjectTrust(workspace, projectId, value);
57
+ onContextConfigChanged?.();
58
+ res.json({ trust: value });
59
+ });
12
60
  return router;
13
61
  }
14
62
  //# sourceMappingURL=cross-cutting.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cross-cutting.js","sourceRoot":"","sources":["../../../../src/server/core/plugin-host/cross-cutting.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC/C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC1C,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"cross-cutting.js","sourceRoot":"","sources":["../../../../src/server/core/plugin-host/cross-cutting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * M33 — plugin frontend assets, two tiers.
3
+ *
4
+ * The serving layer for "Option B" plugin frontends. This express-free module
5
+ * owns the single path/trust convention shared by the serving route
6
+ * (`routes/plugins.ts`) and the manifest builder (`frontend-manifest.ts`), so
7
+ * the two cannot diverge.
8
+ *
9
+ * • Overlay tier (phase 2): a TRUSTED project ships a precompiled bundle at
10
+ * `<cwd>/.claude4spec/plugins/<name>/dist/frontend.js` (+ optional
11
+ * `frontend.css`). Gated by `trustProjectPlugins` (per workspace × project,
12
+ * machine-local) — the CALLER resolves the gate and passes `trusted`; an
13
+ * untrusted/undecided project emits nothing, exactly like the overlay loader
14
+ * refuses to run untrusted project code.
15
+ *
16
+ * • Workspace tier (phase 3): a workspace/npm plugin (declared in
17
+ * `~/.claude4spec/workspaces.json`, installed under `node_modules`) serves
18
+ * its own `dist/frontend.js` (+ optional `dist/frontend.css`) UNGATED — a
19
+ * consciously-installed base package is trusted by virtue of npm install,
20
+ * parity with the backend base tier. Traversal-guarded by the passed
21
+ * workspace package allowlist (the same list `loadWorkspacePlugins` uses).
22
+ */
23
+ /**
24
+ * Walk up from `startDir` to the NEAREST ancestor that owns a readable
25
+ * `package.json` — the package root — or `null` if none within 12 levels. The
26
+ * caller has already proved the package's identity (`import.meta.resolve`
27
+ * resolved a DECLARED subpath of `packageName`), so we deliberately do NOT gate
28
+ * on the manifest `name`: a workspace plugin whose install/dir name differs from
29
+ * its real (e.g. scoped) `package.json` `name` must still resolve to its root.
30
+ * The 12-level bound is a cheap runaway guard for a pathological deep tree.
31
+ */
32
+ export declare function nearestPackageRoot(startDir: string): string | null;
33
+ /** The two precompiled assets a plugin frontend bundle may ship. */
34
+ export type FrontendAssetFile = 'frontend.js' | 'frontend.css';
35
+ /**
36
+ * Resolve the absolute path to stream for `GET /api/plugins/<name>/<file>`, or
37
+ * `null` when the request must 404. Returns null when:
38
+ * - the trust gate is off (`!trusted`) — never emit project-committed code,
39
+ * - `name` is not an actual overlay package — also guards path traversal, as
40
+ * only directory names enumerated under `.claude4spec/plugins/` are eligible,
41
+ * - the `dist/<file>` does not exist — a present, trusted project never
42
+ * fabricates a bundle.
43
+ */
44
+ export declare function resolveFrontendAsset(cwd: string, trusted: boolean, name: string, file: FrontendAssetFile): string | null;
45
+ /** One plugin (either tier) with a built frontend bundle. */
46
+ export interface FrontendBundle {
47
+ /** The manifest `name`: overlay = dir under `.claude4spec/plugins/`; workspace = npm package name. */
48
+ name: string;
49
+ /** Version from the package's `package.json`, or `'0.0.0'` if unreadable. */
50
+ version: string;
51
+ /** True when the bundle also ships a precompiled `dist/frontend.css`. */
52
+ hasCss: boolean;
53
+ }
54
+ /**
55
+ * Project-local plugins that ship a built `dist/frontend.js`, for the frontend
56
+ * manifest. Pure FS read; the caller gates by trust before calling (an
57
+ * untrusted project advertises nothing). A package without a built bundle is
58
+ * simply omitted — the build is the implementer's responsibility, not the host's.
59
+ */
60
+ export declare function enumerateFrontendBundles(cwd: string): FrontendBundle[];
61
+ /** Resolve an installed workspace plugin package's absolute root dir, or null. */
62
+ export type WorkspaceRootResolver = (packageName: string) => string | null;
63
+ /**
64
+ * Resolve the absolute path to stream for `GET /api/plugins/<name>/<file>` from a
65
+ * workspace/npm plugin, or `null` when the request must 404. UNGATED (a base
66
+ * install is trusted) — but `packageName` MUST be in `allowedPackages` (the
67
+ * resolved workspace plugin list), which both guards path traversal and bounds
68
+ * serving to actually-declared plugins. The JS path comes from the package's
69
+ * `exports['./frontend']` (fallback `dist/frontend.js`); CSS is its sibling
70
+ * `frontend.css`. Returns null when the package is unresolvable or the file is
71
+ * absent (a plugin without a built bundle simply serves nothing).
72
+ */
73
+ export declare function resolveWorkspaceFrontendAsset(packageName: string, file: FrontendAssetFile, allowedPackages: readonly string[], resolveRoot?: WorkspaceRootResolver): string | null;
74
+ /**
75
+ * Workspace/npm plugins that ship a resolvable built `frontend` entry, for the
76
+ * frontend manifest. UNGATED (parity with the backend base tier). A package
77
+ * without a built bundle (or unresolvable) is simply omitted. `resolveRoot` is a
78
+ * test seam — production passes the default `import.meta.resolve`-based resolver.
79
+ *
80
+ * `packageNames` is the resolved workspace allowlist, so every name is trusted by
81
+ * construction (the traversal guard in `resolveWorkspaceFrontendAsset` exists for
82
+ * the user-supplied route `:name`). Each package is resolved + its `package.json`
83
+ * read exactly ONCE here (this runs per `GET /frontend-manifest`).
84
+ */
85
+ export declare function enumerateWorkspaceFrontendBundles(packageNames: readonly string[], resolveRoot?: WorkspaceRootResolver): FrontendBundle[];
86
+ /** Content type for a served asset name, or `null` when the extension is not allowed. */
87
+ export declare function assetContentType(asset: string): string | null;
88
+ /**
89
+ * Overlay tier (trust-gated): resolve any allowlisted sibling of the project-local
90
+ * bundle at `<cwd>/.claude4spec/plugins/<name>/dist/<asset>`. Same gate + package
91
+ * guard as `resolveFrontendAsset`, generalized from the two fixed filenames.
92
+ */
93
+ export declare function resolveOverlayAsset(cwd: string, trusted: boolean, name: string, asset: string): string | null;
94
+ /**
95
+ * Workspace tier (ungated, allowlist-bounded): resolve any allowlisted sibling of
96
+ * a workspace/npm plugin's resolved `frontend` entry (i.e. files in the same
97
+ * `dist/` dir). Generalizes `resolveWorkspaceFrontendAsset` from the two fixed
98
+ * filenames so code-split chunks (`dto-*.js`) and source maps resolve.
99
+ */
100
+ export declare function resolveWorkspaceAsset(packageName: string, asset: string, allowedPackages: readonly string[], resolveRoot?: WorkspaceRootResolver): string | null;
@@ -0,0 +1,262 @@
1
+ /**
2
+ * M33 — plugin frontend assets, two tiers.
3
+ *
4
+ * The serving layer for "Option B" plugin frontends. This express-free module
5
+ * owns the single path/trust convention shared by the serving route
6
+ * (`routes/plugins.ts`) and the manifest builder (`frontend-manifest.ts`), so
7
+ * the two cannot diverge.
8
+ *
9
+ * • Overlay tier (phase 2): a TRUSTED project ships a precompiled bundle at
10
+ * `<cwd>/.claude4spec/plugins/<name>/dist/frontend.js` (+ optional
11
+ * `frontend.css`). Gated by `trustProjectPlugins` (per workspace × project,
12
+ * machine-local) — the CALLER resolves the gate and passes `trusted`; an
13
+ * untrusted/undecided project emits nothing, exactly like the overlay loader
14
+ * refuses to run untrusted project code.
15
+ *
16
+ * • Workspace tier (phase 3): a workspace/npm plugin (declared in
17
+ * `~/.claude4spec/workspaces.json`, installed under `node_modules`) serves
18
+ * its own `dist/frontend.js` (+ optional `dist/frontend.css`) UNGATED — a
19
+ * consciously-installed base package is trusted by virtue of npm install,
20
+ * parity with the backend base tier. Traversal-guarded by the passed
21
+ * workspace package allowlist (the same list `loadWorkspacePlugins` uses).
22
+ */
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+ import { fileURLToPath } from 'node:url';
26
+ import { enumerateOverlayPackages, projectPluginsDir, resolveExportsEntry, } from './overlay-loader.js';
27
+ /** Read + parse a `package.json`, or `null` when missing/unreadable/invalid. */
28
+ function readPackageJson(pkgJsonPath) {
29
+ try {
30
+ return JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /** A manifest's declared `version`, falling back to `'0.0.0'`. */
37
+ function packageVersion(pkg) {
38
+ return pkg && typeof pkg.version === 'string' ? pkg.version : '0.0.0';
39
+ }
40
+ /**
41
+ * Walk up from `startDir` to the NEAREST ancestor that owns a readable
42
+ * `package.json` — the package root — or `null` if none within 12 levels. The
43
+ * caller has already proved the package's identity (`import.meta.resolve`
44
+ * resolved a DECLARED subpath of `packageName`), so we deliberately do NOT gate
45
+ * on the manifest `name`: a workspace plugin whose install/dir name differs from
46
+ * its real (e.g. scoped) `package.json` `name` must still resolve to its root.
47
+ * The 12-level bound is a cheap runaway guard for a pathological deep tree.
48
+ */
49
+ export function nearestPackageRoot(startDir) {
50
+ let dir = startDir;
51
+ for (let i = 0; i < 12; i++) {
52
+ if (readPackageJson(path.join(dir, 'package.json')))
53
+ return dir;
54
+ const parent = path.dirname(dir);
55
+ if (parent === dir)
56
+ break;
57
+ dir = parent;
58
+ }
59
+ return null;
60
+ }
61
+ /** Absolute path of a plugin's precompiled `dist/<file>`. */
62
+ function bundleAssetPath(cwd, name, file) {
63
+ return path.join(projectPluginsDir(cwd), name, 'dist', file);
64
+ }
65
+ /**
66
+ * Resolve the absolute path to stream for `GET /api/plugins/<name>/<file>`, or
67
+ * `null` when the request must 404. Returns null when:
68
+ * - the trust gate is off (`!trusted`) — never emit project-committed code,
69
+ * - `name` is not an actual overlay package — also guards path traversal, as
70
+ * only directory names enumerated under `.claude4spec/plugins/` are eligible,
71
+ * - the `dist/<file>` does not exist — a present, trusted project never
72
+ * fabricates a bundle.
73
+ */
74
+ export function resolveFrontendAsset(cwd, trusted, name, file) {
75
+ if (!trusted)
76
+ return null;
77
+ if (!enumerateOverlayPackages(cwd).includes(name))
78
+ return null;
79
+ const abs = bundleAssetPath(cwd, name, file);
80
+ return fs.existsSync(abs) ? abs : null;
81
+ }
82
+ /** Read an overlay package's declared version, falling back to `'0.0.0'`. */
83
+ function readBundleVersion(cwd, name) {
84
+ return packageVersion(readPackageJson(path.join(projectPluginsDir(cwd), name, 'package.json')));
85
+ }
86
+ /**
87
+ * Project-local plugins that ship a built `dist/frontend.js`, for the frontend
88
+ * manifest. Pure FS read; the caller gates by trust before calling (an
89
+ * untrusted project advertises nothing). A package without a built bundle is
90
+ * simply omitted — the build is the implementer's responsibility, not the host's.
91
+ */
92
+ export function enumerateFrontendBundles(cwd) {
93
+ const bundles = [];
94
+ for (const name of enumerateOverlayPackages(cwd)) {
95
+ if (!fs.existsSync(bundleAssetPath(cwd, name, 'frontend.js')))
96
+ continue;
97
+ bundles.push({
98
+ name,
99
+ version: readBundleVersion(cwd, name),
100
+ hasCss: fs.existsSync(bundleAssetPath(cwd, name, 'frontend.css')),
101
+ });
102
+ }
103
+ return bundles;
104
+ }
105
+ /**
106
+ * Default resolver. Two reasons `createRequire(...).resolve` is wrong here:
107
+ * 1. `require.resolve('<pkg>/package.json')` — a package with an `exports` map
108
+ * blocks undeclared subpaths like `./package.json`; and
109
+ * 2. `require.resolve` uses CJS conditions (`require`/`node`/`default`), but a
110
+ * modern plugin's `exports` is import-only (`{ types, import }`) ⇒ "No
111
+ * exports main defined".
112
+ * So resolve a DECLARED ESM subpath with `import.meta.resolve` (which uses the
113
+ * `import` condition) — that resolution alone PROVES identity — then walk up to
114
+ * the nearest directory owning a `package.json` (the package root) via
115
+ * `nearestPackageRoot`. We do NOT re-check that `package.json`'s `name` equals
116
+ * the requested id: a plugin installed under an unscoped dir name whose real
117
+ * `name` is scoped (`@scope/pkg`) would otherwise never match and silently lose
118
+ * its frontend. Try `./frontend` first (the only subpath we serve), then `.`.
119
+ */
120
+ const defaultWorkspaceRoot = (packageName) => {
121
+ let entryUrl;
122
+ for (const spec of [`${packageName}/frontend`, packageName]) {
123
+ try {
124
+ entryUrl = import.meta.resolve(spec);
125
+ break;
126
+ }
127
+ catch {
128
+ /* try the next entry specifier */
129
+ }
130
+ }
131
+ if (!entryUrl)
132
+ return null;
133
+ return nearestPackageRoot(path.dirname(fileURLToPath(entryUrl)));
134
+ };
135
+ /**
136
+ * The package's declared `./frontend` import target (relative to its root), or
137
+ * the `dist/frontend.js` convention when `exports` does not declare one. Reuses
138
+ * the overlay loader's condition handling so the two tiers can't drift.
139
+ */
140
+ function workspaceFrontendEntryRel(pkg) {
141
+ const sub = pkg?.exports?.['./frontend'];
142
+ return resolveExportsEntry(sub) ?? 'dist/frontend.js';
143
+ }
144
+ /** Absolute path of a workspace package's `frontend.js`, given its parsed manifest. */
145
+ function workspaceFrontendJs(root, pkg) {
146
+ return path.resolve(root, workspaceFrontendEntryRel(pkg));
147
+ }
148
+ /**
149
+ * Resolve the absolute path to stream for `GET /api/plugins/<name>/<file>` from a
150
+ * workspace/npm plugin, or `null` when the request must 404. UNGATED (a base
151
+ * install is trusted) — but `packageName` MUST be in `allowedPackages` (the
152
+ * resolved workspace plugin list), which both guards path traversal and bounds
153
+ * serving to actually-declared plugins. The JS path comes from the package's
154
+ * `exports['./frontend']` (fallback `dist/frontend.js`); CSS is its sibling
155
+ * `frontend.css`. Returns null when the package is unresolvable or the file is
156
+ * absent (a plugin without a built bundle simply serves nothing).
157
+ */
158
+ export function resolveWorkspaceFrontendAsset(packageName, file, allowedPackages, resolveRoot = defaultWorkspaceRoot) {
159
+ if (!allowedPackages.includes(packageName))
160
+ return null;
161
+ const root = resolveRoot(packageName);
162
+ if (!root)
163
+ return null;
164
+ const frontendAbs = workspaceFrontendJs(root, readPackageJson(path.join(root, 'package.json')));
165
+ const abs = file === 'frontend.js' ? frontendAbs : path.join(path.dirname(frontendAbs), 'frontend.css');
166
+ return fs.existsSync(abs) ? abs : null;
167
+ }
168
+ /**
169
+ * Workspace/npm plugins that ship a resolvable built `frontend` entry, for the
170
+ * frontend manifest. UNGATED (parity with the backend base tier). A package
171
+ * without a built bundle (or unresolvable) is simply omitted. `resolveRoot` is a
172
+ * test seam — production passes the default `import.meta.resolve`-based resolver.
173
+ *
174
+ * `packageNames` is the resolved workspace allowlist, so every name is trusted by
175
+ * construction (the traversal guard in `resolveWorkspaceFrontendAsset` exists for
176
+ * the user-supplied route `:name`). Each package is resolved + its `package.json`
177
+ * read exactly ONCE here (this runs per `GET /frontend-manifest`).
178
+ */
179
+ export function enumerateWorkspaceFrontendBundles(packageNames, resolveRoot = defaultWorkspaceRoot) {
180
+ const bundles = [];
181
+ for (const name of packageNames) {
182
+ const root = resolveRoot(name);
183
+ if (!root)
184
+ continue;
185
+ const pkg = readPackageJson(path.join(root, 'package.json'));
186
+ const frontendAbs = workspaceFrontendJs(root, pkg);
187
+ if (!fs.existsSync(frontendAbs))
188
+ continue;
189
+ bundles.push({
190
+ name,
191
+ version: packageVersion(pkg),
192
+ hasCss: fs.existsSync(path.join(path.dirname(frontendAbs), 'frontend.css')),
193
+ });
194
+ }
195
+ return bundles;
196
+ }
197
+ // ─── Generalized dist asset serving (native-ESM siblings) ───────────────────
198
+ //
199
+ // A plugin's `frontend.js` is served as native ESM (m33l5fe0 — the host does NOT
200
+ // re-bundle), so its RELATIVE imports resolve against the module URL: a code-split
201
+ // chunk `./dto-*.js` becomes `GET /api/plugins/<name>/dto-*.js`. Serving only
202
+ // `frontend.js`/`frontend.css` (the original two-file enum) left those siblings
203
+ // 404 and broke any code-split bundle (the first real plugin tripped on it). So
204
+ // the host serves any allowlisted asset that physically sits next to the entry,
205
+ // strictly contained in the bundle dir, under the SAME trust gating as the entry.
206
+ /** Asset extensions a plugin bundle dir may serve, with their content types. */
207
+ const ASSET_CONTENT_TYPE = {
208
+ '.js': 'text/javascript',
209
+ '.css': 'text/css',
210
+ '.map': 'application/json',
211
+ };
212
+ /** Content type for a served asset name, or `null` when the extension is not allowed. */
213
+ export function assetContentType(asset) {
214
+ return ASSET_CONTENT_TYPE[path.extname(asset).toLowerCase()] ?? null;
215
+ }
216
+ /**
217
+ * Resolve a single flat `asset` strictly within `distDir`. Returns `null` (→ 404)
218
+ * on a disallowed extension, any path separator / traversal in the name, an
219
+ * escape outside `distDir`, or a missing file. Vite library output is flat
220
+ * (`frontend.js`, `dto-*.js`, `*.map` all in the bundle root), so a single
221
+ * path-segment name with no separators is exactly what a relative ESM import
222
+ * requests — and rejecting separators is the simplest sound traversal guard.
223
+ */
224
+ function resolveWithinDist(distDir, asset) {
225
+ if (!assetContentType(asset))
226
+ return null;
227
+ if (asset.includes('/') || asset.includes('\\') || asset.includes('..'))
228
+ return null;
229
+ const abs = path.join(distDir, asset);
230
+ const rel = path.relative(distDir, abs);
231
+ if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel))
232
+ return null;
233
+ return fs.existsSync(abs) ? abs : null;
234
+ }
235
+ /**
236
+ * Overlay tier (trust-gated): resolve any allowlisted sibling of the project-local
237
+ * bundle at `<cwd>/.claude4spec/plugins/<name>/dist/<asset>`. Same gate + package
238
+ * guard as `resolveFrontendAsset`, generalized from the two fixed filenames.
239
+ */
240
+ export function resolveOverlayAsset(cwd, trusted, name, asset) {
241
+ if (!trusted)
242
+ return null;
243
+ if (!enumerateOverlayPackages(cwd).includes(name))
244
+ return null;
245
+ return resolveWithinDist(path.join(projectPluginsDir(cwd), name, 'dist'), asset);
246
+ }
247
+ /**
248
+ * Workspace tier (ungated, allowlist-bounded): resolve any allowlisted sibling of
249
+ * a workspace/npm plugin's resolved `frontend` entry (i.e. files in the same
250
+ * `dist/` dir). Generalizes `resolveWorkspaceFrontendAsset` from the two fixed
251
+ * filenames so code-split chunks (`dto-*.js`) and source maps resolve.
252
+ */
253
+ export function resolveWorkspaceAsset(packageName, asset, allowedPackages, resolveRoot = defaultWorkspaceRoot) {
254
+ if (!allowedPackages.includes(packageName))
255
+ return null;
256
+ const root = resolveRoot(packageName);
257
+ if (!root)
258
+ return null;
259
+ const frontendAbs = workspaceFrontendJs(root, readPackageJson(path.join(root, 'package.json')));
260
+ return resolveWithinDist(path.dirname(frontendAbs), asset);
261
+ }
262
+ //# sourceMappingURL=frontend-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-assets.js","sourceRoot":"","sources":["../../../../src/server/core/plugin-host/frontend-assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * M33: build the `GET /api/plugins/frontend-manifest` payload.
3
+ *
4
+ * Emits the fixed peer import map + host API version and the active plugins to
5
+ * boot ("Option B"). Two tiers feed `plugins[]` (see `buildFrontendManifest`):
6
+ * the workspace/npm tier (ungated, every project) and the project-local overlay
7
+ * tier (only when the primary project is trusted) — each pointing the client at
8
+ * the serving routes in `routes/plugins.ts`. No tiers active ⇒ `plugins: []`, so
9
+ * the import map + shim plumbing still stands up end-to-end with nothing to load.
10
+ */
11
+ import type { FrontendManifestResponse } from '../../../shared/plugin-host/frontend-manifest.js';
12
+ import { type WorkspaceRootResolver } from './frontend-assets.js';
13
+ import type { PluginRegistry } from './types.js';
14
+ /**
15
+ * Serving context for the process's primary project. Absent on workspace-only
16
+ * starts (nothing registered) ⇒ only workspace-tier frontends are advertised.
17
+ */
18
+ export interface FrontendManifestServing {
19
+ cwd: string;
20
+ /** `trustProjectPlugins === true` for the primary project. Untrusted ⇒ no overlay plugins. */
21
+ trusted: boolean;
22
+ }
23
+ /**
24
+ * Build the boot manifest. `plugins[]` is base ∪ overlay:
25
+ * • workspace tier (`workspacePackages`) — ALWAYS advertised, ungated (a base
26
+ * npm install is trusted), present for every project of the process; and
27
+ * • overlay tier — the primary project's project-local bundles, ONLY when that
28
+ * project is trusted.
29
+ * De-duped by `name`: an overlay bundle overrides a workspace bundle of the same
30
+ * name (the project-local copy wins). CSS only when the bundle ships it, so a
31
+ * style-less plugin omits `css` and the client never fetches it.
32
+ */
33
+ export declare function buildFrontendManifest(_registry: PluginRegistry, serving?: FrontendManifestServing, workspacePackages?: readonly string[], workspaceRootResolver?: WorkspaceRootResolver): FrontendManifestResponse;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * M33: build the `GET /api/plugins/frontend-manifest` payload.
3
+ *
4
+ * Emits the fixed peer import map + host API version and the active plugins to
5
+ * boot ("Option B"). Two tiers feed `plugins[]` (see `buildFrontendManifest`):
6
+ * the workspace/npm tier (ungated, every project) and the project-local overlay
7
+ * tier (only when the primary project is trusted) — each pointing the client at
8
+ * the serving routes in `routes/plugins.ts`. No tiers active ⇒ `plugins: []`, so
9
+ * the import map + shim plumbing still stands up end-to-end with nothing to load.
10
+ */
11
+ import { HOST_API_VERSION } from '../../../shared/plugin-host/manifest.js';
12
+ import { buildImportMap } from './runtime-shims.js';
13
+ import { enumerateFrontendBundles, enumerateWorkspaceFrontendBundles, } from './frontend-assets.js';
14
+ /** One bundle → the manifest entry pointing at the (tier-agnostic) serving routes. */
15
+ function toEntry(b) {
16
+ // The serving route is `/api/plugins/:name/:asset` — a single path segment for
17
+ // the name. A SCOPED package (`@scope/pkg`) contains a `/`, so it MUST be
18
+ // percent-encoded to stay one segment (the route decodes `:name` back via
19
+ // Express param decoding). Without this a scoped plugin's frontend URL gains an
20
+ // extra segment, the route never matches → 404 → no frontend loads → no sidebar
21
+ // entry. The browser preserves the encoded segment when resolving the bundle's
22
+ // relative code-split siblings (`./chunk.js`), so those resolve too.
23
+ const seg = encodeURIComponent(b.name);
24
+ return {
25
+ name: b.name,
26
+ version: b.version,
27
+ entry: `/api/plugins/${seg}/frontend.js`,
28
+ ...(b.hasCss ? { css: `/api/plugins/${seg}/frontend.css` } : {}),
29
+ };
30
+ }
31
+ /**
32
+ * Build the boot manifest. `plugins[]` is base ∪ overlay:
33
+ * • workspace tier (`workspacePackages`) — ALWAYS advertised, ungated (a base
34
+ * npm install is trusted), present for every project of the process; and
35
+ * • overlay tier — the primary project's project-local bundles, ONLY when that
36
+ * project is trusted.
37
+ * De-duped by `name`: an overlay bundle overrides a workspace bundle of the same
38
+ * name (the project-local copy wins). CSS only when the bundle ships it, so a
39
+ * style-less plugin omits `css` and the client never fetches it.
40
+ */
41
+ export function buildFrontendManifest(_registry, serving, workspacePackages = [], workspaceRootResolver) {
42
+ const byName = new Map();
43
+ for (const b of enumerateWorkspaceFrontendBundles(workspacePackages, workspaceRootResolver))
44
+ byName.set(b.name, toEntry(b));
45
+ if (serving?.trusted) {
46
+ for (const b of enumerateFrontendBundles(serving.cwd))
47
+ byName.set(b.name, toEntry(b));
48
+ }
49
+ const plugins = [...byName.values()];
50
+ // `importMap` is informational here (diagnostics / future non-head consumers).
51
+ // The AUTHORITATIVE copy the browser uses is injected server-side into the SPA
52
+ // `<head>` (see `injectImportMap` in server/index.ts) — both derive from the
53
+ // same `buildImportMap()`, so the two channels cannot diverge.
54
+ return {
55
+ hostApiVersion: HOST_API_VERSION,
56
+ importMap: buildImportMap(),
57
+ plugins,
58
+ };
59
+ }
60
+ //# sourceMappingURL=frontend-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-manifest.js","sourceRoot":"","sources":["../../../../src/server/core/plugin-host/frontend-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAK3E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,wBAAwB,EACxB,iCAAiC,GAGlC,MAAM,sBAAsB,CAAC;AAa9B,sFAAsF;AACtF,SAAS,OAAO,CAAC,CAAiB;IAChC,+EAA+E;IAC/E,0EAA0E;IAC1E,0EAA0E;IAC1E,gFAAgF;IAChF,gFAAgF;IAChF,+EAA+E;IAC/E,qEAAqE;IACrE,MAAM,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,KAAK,EAAE,gBAAgB,GAAG,cAAc;QACxC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,gBAAgB,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAAyB,EACzB,OAAiC,EACjC,oBAAuC,EAAE,EACzC,qBAA6C;IAE7C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;IACtD,KAAK,MAAM,CAAC,IAAI,iCAAiC,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;QACzF,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,OAAO,GAA0B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAE5D,+EAA+E;IAC/E,+EAA+E;IAC/E,6EAA6E;IAC7E,+DAA+D;IAC/D,OAAO;QACL,cAAc,EAAE,gBAAgB;QAChC,SAAS,EAAE,cAAc,EAAE;QAC3B,OAAO;KACR,CAAC;AACJ,CAAC"}