forgeos 0.1.0-alpha.4 → 0.1.0-alpha.41

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 (416) hide show
  1. package/.npmignore +10 -0
  2. package/AGENTS.md +142 -90
  3. package/CHANGELOG.md +433 -0
  4. package/LICENSE +21 -0
  5. package/README.md +67 -8
  6. package/adapters/go/README.md +23 -0
  7. package/adapters/go/go.mod +3 -0
  8. package/adapters/go/http.go +149 -0
  9. package/adapters/go/registry.go +234 -0
  10. package/adapters/go/types.go +136 -0
  11. package/adapters/java/README.md +68 -0
  12. package/adapters/java/pom.xml +34 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  33. package/adapters/java-spring-boot-starter/README.md +32 -0
  34. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  35. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  36. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  37. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  38. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  39. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  41. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  42. package/bin/forge.mjs +18 -0
  43. package/docs/cair-protocol.md +103 -0
  44. package/docs/changelog.md +548 -0
  45. package/docs/forge-protocol.md +189 -0
  46. package/examples/go-billing/go.mod +7 -0
  47. package/examples/go-billing/main.go +120 -0
  48. package/examples/java-billing/pom.xml +52 -0
  49. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  50. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  51. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  52. package/package.json +31 -3
  53. package/schemas/forge-manifest.schema.json +57 -0
  54. package/src/forge/_generated/releaseManifest.json +1 -2
  55. package/src/forge/_generated/releaseManifest.ts +3 -3
  56. package/src/forge/agent-adapters/index.ts +1584 -125
  57. package/src/forge/agent-adapters/types.ts +224 -1
  58. package/src/forge/agent-memory/bridge.ts +1333 -0
  59. package/src/forge/agent-memory/context-pack.ts +277 -0
  60. package/src/forge/agent-memory/hook-runner.ts +312 -0
  61. package/src/forge/agent-memory/mcp.ts +224 -0
  62. package/src/forge/agent-memory/normalize.ts +498 -0
  63. package/src/forge/agent-memory/redaction.ts +103 -0
  64. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  65. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
  66. package/src/forge/agent-memory/sources/codex.ts +119 -0
  67. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  68. package/src/forge/agent-memory/types.ts +204 -0
  69. package/src/forge/bench.ts +248 -0
  70. package/src/forge/brownfield-import/index.ts +801 -0
  71. package/src/forge/brownfield-import/types.ts +127 -0
  72. package/src/forge/cair/action-journal.ts +61 -0
  73. package/src/forge/cair/action-parser.ts +314 -0
  74. package/src/forge/cair/action-validator.ts +40 -0
  75. package/src/forge/cair/actions.ts +1818 -0
  76. package/src/forge/cair/format.ts +77 -0
  77. package/src/forge/cair/index.ts +106 -0
  78. package/src/forge/cair/query.ts +478 -0
  79. package/src/forge/cair/snapshot.ts +315 -0
  80. package/src/forge/cair/types.ts +248 -0
  81. package/src/forge/cli/ai.ts +339 -21
  82. package/src/forge/cli/auth.ts +348 -10
  83. package/src/forge/cli/authmd.ts +441 -0
  84. package/src/forge/cli/baseline.ts +112 -0
  85. package/src/forge/cli/build.ts +20 -4
  86. package/src/forge/cli/changed.ts +437 -0
  87. package/src/forge/cli/codex-app-server.ts +877 -0
  88. package/src/forge/cli/commands.ts +1620 -20
  89. package/src/forge/cli/db.ts +209 -2
  90. package/src/forge/cli/deploy.ts +887 -0
  91. package/src/forge/cli/deps.ts +82 -13
  92. package/src/forge/cli/dev.ts +961 -46
  93. package/src/forge/cli/docs.ts +265 -0
  94. package/src/forge/cli/doctor.ts +297 -0
  95. package/src/forge/cli/field-test.ts +329 -0
  96. package/src/forge/cli/handoff.ts +336 -0
  97. package/src/forge/cli/index.ts +1 -0
  98. package/src/forge/cli/last-run.ts +84 -0
  99. package/src/forge/cli/main.ts +107 -3
  100. package/src/forge/cli/new.ts +152 -13
  101. package/src/forge/cli/next-actions.ts +23 -0
  102. package/src/forge/cli/output.ts +389 -6
  103. package/src/forge/cli/parse.ts +1050 -34
  104. package/src/forge/cli/progress.ts +51 -0
  105. package/src/forge/cli/query.ts +32 -0
  106. package/src/forge/cli/release.ts +35 -11
  107. package/src/forge/cli/rls.ts +43 -4
  108. package/src/forge/cli/run.ts +41 -0
  109. package/src/forge/cli/security.ts +112 -0
  110. package/src/forge/cli/self-host.ts +56 -14
  111. package/src/forge/cli/studio.ts +2218 -0
  112. package/src/forge/cli/verify.ts +1455 -32
  113. package/src/forge/cli/windows.ts +23 -0
  114. package/src/forge/cli/workos.ts +1020 -0
  115. package/src/forge/compiler/agent-contract/build.ts +670 -64
  116. package/src/forge/compiler/agent-contract/types.ts +45 -2
  117. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  118. package/src/forge/compiler/api-surface/build.ts +47 -0
  119. package/src/forge/compiler/app-graph/build.ts +112 -9
  120. package/src/forge/compiler/app-graph/extract.ts +107 -0
  121. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  122. package/src/forge/compiler/app-graph/parser.ts +24 -24
  123. package/src/forge/compiler/app-graph/profile.ts +26 -0
  124. package/src/forge/compiler/app-graph/versions.ts +1 -1
  125. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  126. package/src/forge/compiler/classifier/classify.ts +32 -8
  127. package/src/forge/compiler/classifier/secrets.ts +3 -2
  128. package/src/forge/compiler/classifier/signals.ts +91 -1
  129. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  130. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  131. package/src/forge/compiler/data-graph/parse.ts +20 -4
  132. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  133. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  134. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  135. package/src/forge/compiler/diagnostics/codes.ts +30 -0
  136. package/src/forge/compiler/diagnostics/create.ts +21 -2
  137. package/src/forge/compiler/diagnostics/index.ts +2 -0
  138. package/src/forge/compiler/emitter/barrel.ts +3 -0
  139. package/src/forge/compiler/emitter/render.ts +9 -0
  140. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  141. package/src/forge/compiler/external-manifest/types.ts +91 -0
  142. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  143. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  144. package/src/forge/compiler/integration/add.ts +618 -24
  145. package/src/forge/compiler/integration/plan.ts +43 -12
  146. package/src/forge/compiler/integration/render.ts +29 -0
  147. package/src/forge/compiler/integration/snapshot.ts +2 -0
  148. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  149. package/src/forge/compiler/integration/templates/index.ts +1 -0
  150. package/src/forge/compiler/integration/templates/render.ts +38 -0
  151. package/src/forge/compiler/integration/templates/types.ts +3 -0
  152. package/src/forge/compiler/integration/templates/workos.ts +1219 -0
  153. package/src/forge/compiler/make-registry/build.ts +8 -9
  154. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  155. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  156. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  157. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  158. package/src/forge/compiler/orchestrator/plan.ts +247 -10
  159. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  160. package/src/forge/compiler/orchestrator/run.ts +97 -31
  161. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  162. package/src/forge/compiler/orchestrator/types.ts +1 -1
  163. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  164. package/src/forge/compiler/package-graph/constants.ts +1 -1
  165. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  166. package/src/forge/compiler/package-manager/commands.ts +4 -0
  167. package/src/forge/compiler/package-manager/executor.ts +30 -1
  168. package/src/forge/compiler/package-manager/version.ts +36 -12
  169. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  170. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  171. package/src/forge/compiler/policy-registry/build.ts +47 -2
  172. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  173. package/src/forge/compiler/recipes/definitions.ts +63 -0
  174. package/src/forge/compiler/recipes/index.ts +2 -0
  175. package/src/forge/compiler/recipes/registry.ts +6 -0
  176. package/src/forge/compiler/test-graph/build.ts +11 -3
  177. package/src/forge/compiler/types/app-graph.ts +8 -2
  178. package/src/forge/compiler/types/cli.ts +75 -1
  179. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  180. package/src/forge/compiler/types/emit.ts +2 -0
  181. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  182. package/src/forge/compiler/types/integration.ts +1 -0
  183. package/src/forge/compiler/types/package-graph.ts +6 -0
  184. package/src/forge/compiler/types/policy-registry.ts +3 -1
  185. package/src/forge/delta/classifier.ts +52 -0
  186. package/src/forge/delta/explain.ts +238 -0
  187. package/src/forge/delta/git-observer.ts +43 -0
  188. package/src/forge/delta/ids.ts +44 -0
  189. package/src/forge/delta/index.ts +25 -0
  190. package/src/forge/delta/recorder.ts +540 -0
  191. package/src/forge/delta/redaction.ts +50 -0
  192. package/src/forge/delta/schema.ts +240 -0
  193. package/src/forge/delta/session.ts +142 -0
  194. package/src/forge/delta/status.ts +1202 -0
  195. package/src/forge/delta/store.ts +3284 -0
  196. package/src/forge/delta/timeline.ts +178 -0
  197. package/src/forge/dev/server.ts +808 -17
  198. package/src/forge/dev/types.ts +15 -1
  199. package/src/forge/dev/watch.ts +17 -7
  200. package/src/forge/dev-console/cycle.ts +280 -33
  201. package/src/forge/dev-console/types.ts +47 -1
  202. package/src/forge/impact/index.ts +204 -9
  203. package/src/forge/impact/types.ts +33 -1
  204. package/src/forge/intent/index.ts +35 -16
  205. package/src/forge/make/fields.ts +26 -0
  206. package/src/forge/make/index.ts +40 -6
  207. package/src/forge/make/templates.ts +155 -2
  208. package/src/forge/make/types.ts +2 -1
  209. package/src/forge/policy.ts +1 -1
  210. package/src/forge/react/index.ts +160 -2
  211. package/src/forge/refactor/index.ts +1 -0
  212. package/src/forge/repair/rules/index.ts +2 -2
  213. package/src/forge/review/index.ts +158 -12
  214. package/src/forge/review/types.ts +15 -0
  215. package/src/forge/runtime/auth/config.ts +17 -0
  216. package/src/forge/runtime/auth/evaluate.ts +15 -2
  217. package/src/forge/runtime/auth/resolve.ts +61 -6
  218. package/src/forge/runtime/auth/types.ts +1 -0
  219. package/src/forge/runtime/db/factory.ts +1 -3
  220. package/src/forge/runtime/db/generated-client.ts +13 -2
  221. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  222. package/src/forge/runtime/db/pglite-adapter.ts +263 -2
  223. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  224. package/src/forge/runtime/executor.ts +129 -16
  225. package/src/forge/runtime/external/bridge.ts +649 -0
  226. package/src/forge/runtime/runner/run-entry.ts +16 -7
  227. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  228. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  229. package/src/forge/runtime/webhooks/security.ts +12 -7
  230. package/src/forge/server.ts +17 -3
  231. package/src/forge/ui/index.ts +209 -0
  232. package/src/forge/ui/types.ts +1 -0
  233. package/src/forge/version.ts +1 -1
  234. package/src/forge/vue/index.ts +422 -0
  235. package/src/forge/workspace/baseline.ts +112 -0
  236. package/src/forge/workspace/change-summary.ts +250 -0
  237. package/src/forge/workspace/forge-cli.ts +42 -0
  238. package/src/forge/workspace/git-summary.ts +391 -0
  239. package/templates/agent-workroom/AGENTS.md +29 -0
  240. package/templates/agent-workroom/README.md +34 -0
  241. package/templates/agent-workroom/forge.config.ts +3 -0
  242. package/templates/agent-workroom/package.json +33 -0
  243. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  244. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  245. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  246. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  247. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  248. package/templates/agent-workroom/src/policies.ts +6 -0
  249. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  250. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  251. package/templates/agent-workroom/tsconfig.json +16 -0
  252. package/templates/agent-workroom/web/index.html +12 -0
  253. package/templates/agent-workroom/web/package.json +21 -0
  254. package/templates/agent-workroom/web/src/App.tsx +345 -0
  255. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  256. package/templates/agent-workroom/web/src/main.tsx +13 -0
  257. package/templates/agent-workroom/web/src/styles.css +545 -0
  258. package/templates/agent-workroom/web/tsconfig.json +27 -0
  259. package/templates/b2b-support-web/package.json +1 -0
  260. package/templates/b2b-support-web/web/package.json +1 -1
  261. package/templates/minimal-web/package.json +1 -1
  262. package/templates/minimal-web/web/package.json +2 -2
  263. package/templates/nuxt-web/.vscode/settings.json +14 -0
  264. package/templates/nuxt-web/README.md +30 -0
  265. package/templates/nuxt-web/forge.config.ts +3 -0
  266. package/templates/nuxt-web/package.json +33 -0
  267. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  268. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  269. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  270. package/templates/nuxt-web/src/policies.ts +6 -0
  271. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  272. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  273. package/templates/nuxt-web/tsconfig.json +17 -0
  274. package/templates/nuxt-web/web/app.vue +67 -0
  275. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  276. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  277. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  278. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  279. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  280. package/templates/nuxt-web/web/package.json +18 -0
  281. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  282. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  283. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  284. package/templates/nuxt-web/web/tsconfig.json +6 -0
  285. package/src/forge/_generated/actionSubscriptions.json +0 -2
  286. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  287. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  288. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  289. package/src/forge/_generated/agentContract.json +0 -2
  290. package/src/forge/_generated/agentContract.ts +0 -7829
  291. package/src/forge/_generated/agentQuickstart.md +0 -34
  292. package/src/forge/_generated/agentTools.json +0 -2
  293. package/src/forge/_generated/agentTools.md +0 -16
  294. package/src/forge/_generated/agentTools.ts +0 -12
  295. package/src/forge/_generated/aiContext.ts +0 -125
  296. package/src/forge/_generated/aiModels.json +0 -2
  297. package/src/forge/_generated/aiModels.ts +0 -51
  298. package/src/forge/_generated/aiProviders.json +0 -2
  299. package/src/forge/_generated/aiProviders.ts +0 -23
  300. package/src/forge/_generated/aiRegistry.json +0 -2
  301. package/src/forge/_generated/aiRegistry.ts +0 -31
  302. package/src/forge/_generated/api.json +0 -2
  303. package/src/forge/_generated/api.ts +0 -8
  304. package/src/forge/_generated/appGraph.json +0 -2
  305. package/src/forge/_generated/appGraph.ts +0 -14919
  306. package/src/forge/_generated/appMap.md +0 -55
  307. package/src/forge/_generated/artifactManifest.json +0 -2
  308. package/src/forge/_generated/artifactManifest.ts +0 -7
  309. package/src/forge/_generated/authClaims.json +0 -2
  310. package/src/forge/_generated/authClaims.ts +0 -13
  311. package/src/forge/_generated/authConfig.json +0 -2
  312. package/src/forge/_generated/authConfig.ts +0 -17
  313. package/src/forge/_generated/authContext.ts +0 -23
  314. package/src/forge/_generated/authRegistry.json +0 -2
  315. package/src/forge/_generated/authRegistry.ts +0 -25
  316. package/src/forge/_generated/buildInfo.json +0 -2
  317. package/src/forge/_generated/buildInfo.ts +0 -9
  318. package/src/forge/_generated/capabilityMap.json +0 -2
  319. package/src/forge/_generated/capabilityMap.md +0 -15
  320. package/src/forge/_generated/capabilityMap.ts +0 -17
  321. package/src/forge/_generated/client.ts +0 -282
  322. package/src/forge/_generated/clientApi.ts +0 -9
  323. package/src/forge/_generated/clientManifest.json +0 -2
  324. package/src/forge/_generated/clientManifest.ts +0 -39
  325. package/src/forge/_generated/clientTypes.ts +0 -78
  326. package/src/forge/_generated/configRegistry.json +0 -2
  327. package/src/forge/_generated/configRegistry.ts +0 -4
  328. package/src/forge/_generated/dataGraph.json +0 -2
  329. package/src/forge/_generated/dataGraph.ts +0 -8
  330. package/src/forge/_generated/db.json +0 -2
  331. package/src/forge/_generated/db.ts +0 -2
  332. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  333. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  334. package/src/forge/_generated/dbSessionContext.json +0 -2
  335. package/src/forge/_generated/dbSessionContext.ts +0 -39
  336. package/src/forge/_generated/deployManifest.json +0 -2
  337. package/src/forge/_generated/deployManifest.ts +0 -14
  338. package/src/forge/_generated/devManifest.json +0 -2
  339. package/src/forge/_generated/devManifest.ts +0 -62
  340. package/src/forge/_generated/envSchema.json +0 -2
  341. package/src/forge/_generated/envSchema.ts +0 -59
  342. package/src/forge/_generated/frontendGraph.json +0 -2
  343. package/src/forge/_generated/frontendGraph.ts +0 -27
  344. package/src/forge/_generated/importGuards.json +0 -2
  345. package/src/forge/_generated/importGuards.ts +0 -686
  346. package/src/forge/_generated/index.ts +0 -68
  347. package/src/forge/_generated/liveProductionManifest.json +0 -2
  348. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  349. package/src/forge/_generated/liveProtocol.json +0 -2
  350. package/src/forge/_generated/liveProtocol.ts +0 -21
  351. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  352. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  353. package/src/forge/_generated/liveTransportConfig.json +0 -2
  354. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  355. package/src/forge/_generated/makeRegistry.json +0 -2
  356. package/src/forge/_generated/makeRegistry.ts +0 -177
  357. package/src/forge/_generated/makeTemplates.json +0 -2
  358. package/src/forge/_generated/makeTemplates.ts +0 -66
  359. package/src/forge/_generated/mockMap.json +0 -2
  360. package/src/forge/_generated/mockMap.ts +0 -7
  361. package/src/forge/_generated/operationPlaybooks.md +0 -167
  362. package/src/forge/_generated/packageGraph.json +0 -2
  363. package/src/forge/_generated/packageGraph.ts +0 -249334
  364. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  365. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  366. package/src/forge/_generated/permissionMatrix.json +0 -2
  367. package/src/forge/_generated/permissionMatrix.ts +0 -7
  368. package/src/forge/_generated/policyRegistry.json +0 -2
  369. package/src/forge/_generated/policyRegistry.ts +0 -11
  370. package/src/forge/_generated/queryRegistry.json +0 -2
  371. package/src/forge/_generated/queryRegistry.ts +0 -9
  372. package/src/forge/_generated/react.d.ts +0 -22
  373. package/src/forge/_generated/react.ts +0 -29
  374. package/src/forge/_generated/reactManifest.json +0 -2
  375. package/src/forge/_generated/reactManifest.ts +0 -19
  376. package/src/forge/_generated/rlsPolicies.json +0 -2
  377. package/src/forge/_generated/rlsPolicies.sql +0 -34
  378. package/src/forge/_generated/rlsPolicies.ts +0 -6
  379. package/src/forge/_generated/runtimeGraph.json +0 -2
  380. package/src/forge/_generated/runtimeGraph.ts +0 -8
  381. package/src/forge/_generated/runtimeMatrix.json +0 -2
  382. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  383. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  384. package/src/forge/_generated/runtimeRules.md +0 -91
  385. package/src/forge/_generated/secretRegistry.json +0 -2
  386. package/src/forge/_generated/secretRegistry.ts +0 -50
  387. package/src/forge/_generated/secretsContext.ts +0 -11
  388. package/src/forge/_generated/serverApi.ts +0 -10
  389. package/src/forge/_generated/sourceMapManifest.json +0 -2
  390. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  391. package/src/forge/_generated/sqlPlan.json +0 -2
  392. package/src/forge/_generated/sqlPlan.ts +0 -88
  393. package/src/forge/_generated/subscriptionManifest.json +0 -2
  394. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  395. package/src/forge/_generated/symbolicationManifest.json +0 -2
  396. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  397. package/src/forge/_generated/telemetryRegistry.json +0 -2
  398. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  399. package/src/forge/_generated/telemetrySinks.json +0 -2
  400. package/src/forge/_generated/telemetrySinks.ts +0 -11
  401. package/src/forge/_generated/tenantScope.json +0 -2
  402. package/src/forge/_generated/tenantScope.ts +0 -8
  403. package/src/forge/_generated/testGraph.json +0 -2
  404. package/src/forge/_generated/testGraph.ts +0 -3430
  405. package/src/forge/_generated/testPlanRegistry.json +0 -2
  406. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  407. package/src/forge/_generated/uiRoutes.json +0 -2
  408. package/src/forge/_generated/uiRoutes.ts +0 -16
  409. package/src/forge/_generated/uiScenarios.json +0 -2
  410. package/src/forge/_generated/uiScenarios.ts +0 -30
  411. package/src/forge/_generated/uiTestManifest.json +0 -2
  412. package/src/forge/_generated/uiTestManifest.ts +0 -27
  413. package/src/forge/_generated/workflowRegistry.json +0 -2
  414. package/src/forge/_generated/workflowRegistry.ts +0 -9
  415. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  416. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -0,0 +1,205 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { createDiagnostic } from "../diagnostics/create.ts";
4
+ import { hashStable } from "../primitives/hash.ts";
5
+ import { serializeCanonical } from "../primitives/serialize.ts";
6
+ import type { Diagnostic } from "../types/diagnostic.ts";
7
+ import {
8
+ FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION,
9
+ FORGE_EXTERNAL_MANIFEST_REGISTRY,
10
+ type ForgeExternalManifest,
11
+ type ForgeExternalManifestRegistryFile,
12
+ type ForgeExternalService,
13
+ type ForgeExternalServiceEntry,
14
+ type ForgeExternalServiceGraph,
15
+ } from "./types.ts";
16
+ import {
17
+ validateExternalManifest,
18
+ validateExternalManifestRegistry,
19
+ } from "./validate.ts";
20
+
21
+ function parseJsonFile(path: string): { value: unknown | null; diagnostics: Diagnostic[] } {
22
+ try {
23
+ return { value: JSON.parse(readFileSync(path, "utf8")), diagnostics: [] };
24
+ } catch (error) {
25
+ return {
26
+ value: null,
27
+ diagnostics: [
28
+ createDiagnostic({
29
+ severity: "error",
30
+ code: "FORGE_EXTERNAL_MANIFEST_PARSE",
31
+ message: `failed to parse external manifest JSON: ${error instanceof Error ? error.message : String(error)}`,
32
+ file: path,
33
+ docs: ["docs/forge-protocol.md", "schemas/forge-manifest.schema.json"],
34
+ }),
35
+ ],
36
+ };
37
+ }
38
+ }
39
+
40
+ function registryPath(workspaceRoot: string): string {
41
+ return join(workspaceRoot, FORGE_EXTERNAL_MANIFEST_REGISTRY);
42
+ }
43
+
44
+ function rootManifestPath(workspaceRoot: string): string {
45
+ return join(workspaceRoot, "forge.manifest.json");
46
+ }
47
+
48
+ function normalizeManifests(manifests: ForgeExternalManifest[]): ForgeExternalManifest[] {
49
+ return [...manifests].sort((a, b) => a.service.name.localeCompare(b.service.name));
50
+ }
51
+
52
+ function serviceFromManifest(manifest: ForgeExternalManifest): ForgeExternalService {
53
+ const entries: ForgeExternalServiceEntry[] = manifest.entries.map((entry) => ({
54
+ ...entry,
55
+ service: manifest.service.name,
56
+ language: manifest.language,
57
+ ...(manifest.framework ? { framework: manifest.framework } : {}),
58
+ transport: manifest.service.transport,
59
+ source: "external",
60
+ }));
61
+ return {
62
+ name: manifest.service.name,
63
+ language: manifest.language,
64
+ ...(manifest.framework ? { framework: manifest.framework } : {}),
65
+ transport: manifest.service.transport,
66
+ ...(manifest.service.baseUrl ? { baseUrl: manifest.service.baseUrl } : {}),
67
+ ...(manifest.service.command ? { command: manifest.service.command } : {}),
68
+ ...(manifest.service.commandArgs ? { commandArgs: manifest.service.commandArgs } : {}),
69
+ ...(manifest.service.health ? { health: manifest.service.health } : {}),
70
+ entries,
71
+ };
72
+ }
73
+
74
+ function duplicateServiceDiagnostics(
75
+ manifests: ForgeExternalManifest[],
76
+ file: string,
77
+ ): Diagnostic[] {
78
+ const seen = new Set<string>();
79
+ const diagnostics: Diagnostic[] = [];
80
+ for (const manifest of manifests) {
81
+ if (seen.has(manifest.service.name)) {
82
+ diagnostics.push(createDiagnostic({
83
+ severity: "error",
84
+ code: "FORGE_EXTERNAL_SERVICE_DUPLICATE",
85
+ message: `external service '${manifest.service.name}' is imported more than once`,
86
+ file,
87
+ fixHint: "Keep one manifest per external service name in .forge/external-manifests.json.",
88
+ docs: ["docs/forge-protocol.md"],
89
+ }));
90
+ }
91
+ seen.add(manifest.service.name);
92
+ }
93
+ return diagnostics;
94
+ }
95
+
96
+ export function readExternalManifestFile(path: string): {
97
+ manifest: ForgeExternalManifest | null;
98
+ diagnostics: Diagnostic[];
99
+ } {
100
+ const parsed = parseJsonFile(path);
101
+ if (parsed.value === null) {
102
+ return { manifest: null, diagnostics: parsed.diagnostics };
103
+ }
104
+ const result = validateExternalManifest(parsed.value, { file: path });
105
+ return { manifest: result.manifest, diagnostics: [...parsed.diagnostics, ...result.diagnostics] };
106
+ }
107
+
108
+ export function loadExternalManifestRegistry(
109
+ workspaceRoot: string,
110
+ ): { registry: ForgeExternalManifestRegistryFile; diagnostics: Diagnostic[]; path: string } {
111
+ const path = registryPath(workspaceRoot);
112
+ const diagnostics: Diagnostic[] = [];
113
+ const manifests: ForgeExternalManifest[] = [];
114
+
115
+ if (existsSync(path)) {
116
+ const parsed = parseJsonFile(path);
117
+ diagnostics.push(...parsed.diagnostics);
118
+ if (parsed.value !== null) {
119
+ const result = validateExternalManifestRegistry(parsed.value, { file: path });
120
+ diagnostics.push(...result.diagnostics);
121
+ if (result.registry) {
122
+ manifests.push(...result.registry.manifests);
123
+ }
124
+ }
125
+ }
126
+
127
+ const rootPath = rootManifestPath(workspaceRoot);
128
+ if (existsSync(rootPath)) {
129
+ const result = readExternalManifestFile(rootPath);
130
+ diagnostics.push(...result.diagnostics);
131
+ if (result.manifest) {
132
+ manifests.push(result.manifest);
133
+ }
134
+ }
135
+
136
+ diagnostics.push(...duplicateServiceDiagnostics(manifests, path));
137
+ return {
138
+ path,
139
+ registry: {
140
+ schemaVersion: "0.1.0",
141
+ manifests: normalizeManifests(manifests),
142
+ },
143
+ diagnostics,
144
+ };
145
+ }
146
+
147
+ export function buildExternalServiceGraph(workspaceRoot: string): ForgeExternalServiceGraph {
148
+ const loaded = loadExternalManifestRegistry(workspaceRoot);
149
+ const services = loaded.registry.manifests.map(serviceFromManifest);
150
+ return {
151
+ schemaVersion: "0.1.0",
152
+ protocolVersion: FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION,
153
+ registryPath: FORGE_EXTERNAL_MANIFEST_REGISTRY,
154
+ inputHash: hashStable(serializeCanonical({ services, diagnostics: loaded.diagnostics })),
155
+ services,
156
+ diagnostics: loaded.diagnostics,
157
+ };
158
+ }
159
+
160
+ export function importExternalManifest(
161
+ workspaceRoot: string,
162
+ inputPath: string,
163
+ ): { graph: ForgeExternalServiceGraph; diagnostics: Diagnostic[]; imported: boolean; path: string } {
164
+ const absoluteInput = resolve(workspaceRoot, inputPath);
165
+ const result = readExternalManifestFile(absoluteInput);
166
+ if (!result.manifest || result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
167
+ return {
168
+ graph: buildExternalServiceGraph(workspaceRoot),
169
+ diagnostics: result.diagnostics,
170
+ imported: false,
171
+ path: absoluteInput,
172
+ };
173
+ }
174
+
175
+ const loaded = loadExternalManifestRegistry(workspaceRoot);
176
+ const remaining = loaded.registry.manifests.filter(
177
+ (manifest) => manifest.service.name !== result.manifest?.service.name,
178
+ );
179
+ const registry: ForgeExternalManifestRegistryFile = {
180
+ schemaVersion: "0.1.0",
181
+ manifests: normalizeManifests([...remaining, result.manifest]),
182
+ };
183
+ const path = registryPath(workspaceRoot);
184
+ mkdirSync(dirname(path), { recursive: true });
185
+ writeFileSync(path, `${serializeCanonical(registry).trimEnd()}\n`, "utf8");
186
+ const graph = buildExternalServiceGraph(workspaceRoot);
187
+ return {
188
+ graph,
189
+ diagnostics: [...loaded.diagnostics, ...result.diagnostics],
190
+ imported: true,
191
+ path,
192
+ };
193
+ }
194
+
195
+ export function serializeExternalServiceGraphJson(graph: ForgeExternalServiceGraph): string {
196
+ return serializeCanonical(graph);
197
+ }
198
+
199
+ export function serializeExternalServiceGraphTs(graph: ForgeExternalServiceGraph): string {
200
+ return `export const externalServices = ${JSON.stringify(
201
+ JSON.parse(serializeExternalServiceGraphJson(graph)),
202
+ null,
203
+ 2,
204
+ )} as const;\n`;
205
+ }
@@ -0,0 +1,91 @@
1
+ import type { Diagnostic } from "../types/diagnostic.ts";
2
+
3
+ export const FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION = "1.0";
4
+ export const FORGE_EXTERNAL_MANIFEST_REGISTRY = ".forge/external-manifests.json";
5
+
6
+ export type ForgeExternalManifestLanguage =
7
+ | "go"
8
+ | "java"
9
+ | "kotlin"
10
+ | "csharp"
11
+ | "rust"
12
+ | "python"
13
+ | "typescript"
14
+ | string;
15
+
16
+ export type ForgeExternalTransportKind = "http" | "grpc" | "stdio";
17
+ export type ForgeExternalEntryKind = "command" | "query";
18
+ export type ForgeExternalEntryRisk = "read" | "write" | "destructive" | "external";
19
+ export type ForgeExternalTransactionMode =
20
+ | "read-only"
21
+ | "external-managed"
22
+ | "forge-managed"
23
+ | "saga";
24
+
25
+ export interface ForgeExternalManifestService {
26
+ name: string;
27
+ transport: ForgeExternalTransportKind;
28
+ baseUrl?: string;
29
+ command?: string;
30
+ commandArgs?: string[];
31
+ health?: string;
32
+ }
33
+
34
+ export interface ForgeExternalManifestEntry {
35
+ name: string;
36
+ kind: ForgeExternalEntryKind;
37
+ description?: string;
38
+ path?: string;
39
+ method?: "GET" | "POST";
40
+ inputSchema?: unknown;
41
+ outputSchema?: unknown;
42
+ policy?: string;
43
+ tenantScoped?: boolean;
44
+ transaction?: ForgeExternalTransactionMode;
45
+ risk?: ForgeExternalEntryRisk;
46
+ needsApproval?: boolean;
47
+ effects?: string[];
48
+ }
49
+
50
+ export interface ForgeExternalManifest {
51
+ forgeProtocol: typeof FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION;
52
+ language: ForgeExternalManifestLanguage;
53
+ framework?: string;
54
+ service: ForgeExternalManifestService;
55
+ entries: ForgeExternalManifestEntry[];
56
+ schemas?: Record<string, unknown>;
57
+ }
58
+
59
+ export interface ForgeExternalManifestRegistryFile {
60
+ schemaVersion: "0.1.0";
61
+ manifests: ForgeExternalManifest[];
62
+ }
63
+
64
+ export interface ForgeExternalServiceEntry extends ForgeExternalManifestEntry {
65
+ service: string;
66
+ language: ForgeExternalManifestLanguage;
67
+ framework?: string;
68
+ transport: ForgeExternalTransportKind;
69
+ source: "external";
70
+ }
71
+
72
+ export interface ForgeExternalService {
73
+ name: string;
74
+ language: ForgeExternalManifestLanguage;
75
+ framework?: string;
76
+ transport: ForgeExternalTransportKind;
77
+ baseUrl?: string;
78
+ command?: string;
79
+ commandArgs?: string[];
80
+ health?: string;
81
+ entries: ForgeExternalServiceEntry[];
82
+ }
83
+
84
+ export interface ForgeExternalServiceGraph {
85
+ schemaVersion: "0.1.0";
86
+ protocolVersion: typeof FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION;
87
+ inputHash: string;
88
+ registryPath: string;
89
+ services: ForgeExternalService[];
90
+ diagnostics: Diagnostic[];
91
+ }
@@ -0,0 +1,373 @@
1
+ import { createDiagnostic } from "../diagnostics/create.ts";
2
+ import type { Diagnostic } from "../types/diagnostic.ts";
3
+ import {
4
+ FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION,
5
+ type ForgeExternalEntryKind,
6
+ type ForgeExternalEntryRisk,
7
+ type ForgeExternalManifest,
8
+ type ForgeExternalManifestEntry,
9
+ type ForgeExternalManifestRegistryFile,
10
+ type ForgeExternalTransportKind,
11
+ type ForgeExternalTransactionMode,
12
+ } from "./types.ts";
13
+
14
+ const TRANSPORTS: ForgeExternalTransportKind[] = ["http", "grpc", "stdio"];
15
+ const ENTRY_KINDS: ForgeExternalEntryKind[] = ["command", "query"];
16
+ const RISKS: ForgeExternalEntryRisk[] = ["read", "write", "destructive", "external"];
17
+ const TRANSACTIONS: ForgeExternalTransactionMode[] = [
18
+ "read-only",
19
+ "external-managed",
20
+ "forge-managed",
21
+ "saga",
22
+ ];
23
+
24
+ function isObject(value: unknown): value is Record<string, unknown> {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+
28
+ function isString(value: unknown): value is string {
29
+ return typeof value === "string" && value.trim().length > 0;
30
+ }
31
+
32
+ function stringArray(value: unknown): value is string[] {
33
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
34
+ }
35
+
36
+ function commandArgsArray(value: unknown): value is string[] {
37
+ return Array.isArray(value) &&
38
+ value.length > 0 &&
39
+ typeof value[0] === "string" &&
40
+ value[0].length > 0 &&
41
+ value.every((item) => typeof item === "string");
42
+ }
43
+
44
+ function externalDiagnostic(
45
+ severity: Diagnostic["severity"],
46
+ code: string,
47
+ message: string,
48
+ file?: string,
49
+ fixHint?: string,
50
+ ): Diagnostic {
51
+ return createDiagnostic({
52
+ severity,
53
+ code,
54
+ message,
55
+ ...(file ? { file } : {}),
56
+ ...(fixHint ? { fixHint } : {}),
57
+ docs: ["docs/forge-protocol.md", "schemas/forge-manifest.schema.json"],
58
+ });
59
+ }
60
+
61
+ function validateEntry(
62
+ entry: unknown,
63
+ index: number,
64
+ serviceName: string,
65
+ diagnostics: Diagnostic[],
66
+ file?: string,
67
+ ): ForgeExternalManifestEntry | null {
68
+ if (!isObject(entry)) {
69
+ diagnostics.push(externalDiagnostic(
70
+ "error",
71
+ "FORGE_EXTERNAL_ENTRY_OBJECT",
72
+ `external manifest entry at index ${index} must be an object`,
73
+ file,
74
+ ));
75
+ return null;
76
+ }
77
+
78
+ const name = entry.name;
79
+ const kind = entry.kind;
80
+ if (!isString(name)) {
81
+ diagnostics.push(externalDiagnostic(
82
+ "error",
83
+ "FORGE_EXTERNAL_ENTRY_NAME",
84
+ `external manifest entry at index ${index} must have a non-empty name`,
85
+ file,
86
+ ));
87
+ }
88
+ if (!ENTRY_KINDS.includes(kind as ForgeExternalEntryKind)) {
89
+ diagnostics.push(externalDiagnostic(
90
+ "error",
91
+ "FORGE_EXTERNAL_ENTRY_KIND",
92
+ `external manifest entry '${String(name || index)}' must be kind command or query`,
93
+ file,
94
+ ));
95
+ }
96
+ if (entry.effects !== undefined && !stringArray(entry.effects)) {
97
+ diagnostics.push(externalDiagnostic(
98
+ "error",
99
+ "FORGE_EXTERNAL_ENTRY_EFFECTS",
100
+ `external manifest entry '${String(name || index)}' effects must be a string array`,
101
+ file,
102
+ ));
103
+ }
104
+ if (entry.risk !== undefined && !RISKS.includes(entry.risk as ForgeExternalEntryRisk)) {
105
+ diagnostics.push(externalDiagnostic(
106
+ "error",
107
+ "FORGE_EXTERNAL_ENTRY_RISK",
108
+ `external manifest entry '${String(name || index)}' has unsupported risk '${String(entry.risk)}'`,
109
+ file,
110
+ ));
111
+ }
112
+ if (
113
+ entry.transaction !== undefined &&
114
+ !TRANSACTIONS.includes(entry.transaction as ForgeExternalTransactionMode)
115
+ ) {
116
+ diagnostics.push(externalDiagnostic(
117
+ "error",
118
+ "FORGE_EXTERNAL_ENTRY_TRANSACTION",
119
+ `external manifest entry '${String(name || index)}' has unsupported transaction mode '${String(entry.transaction)}'`,
120
+ file,
121
+ ));
122
+ }
123
+ if (
124
+ entry.kind === "query" &&
125
+ entry.transaction !== undefined &&
126
+ entry.transaction !== "read-only"
127
+ ) {
128
+ diagnostics.push(externalDiagnostic(
129
+ "error",
130
+ "FORGE_EXTERNAL_QUERY_TRANSACTION",
131
+ `external query '${serviceName}.${String(name)}' must use transaction read-only when transaction is declared`,
132
+ file,
133
+ "Declare command for writes, or set transaction to read-only for external queries.",
134
+ ));
135
+ }
136
+ if (entry.kind === "query" && entry.risk !== undefined && entry.risk !== "read") {
137
+ diagnostics.push(externalDiagnostic(
138
+ "error",
139
+ "FORGE_EXTERNAL_QUERY_RISK",
140
+ `external query '${serviceName}.${String(name)}' must use risk read when risk is declared`,
141
+ file,
142
+ ));
143
+ }
144
+
145
+ if (!isString(name) || !ENTRY_KINDS.includes(kind as ForgeExternalEntryKind)) {
146
+ return null;
147
+ }
148
+
149
+ return {
150
+ name,
151
+ kind: kind as ForgeExternalEntryKind,
152
+ ...(isString(entry.description) ? { description: entry.description } : {}),
153
+ ...(isString(entry.path) ? { path: entry.path } : {}),
154
+ ...(entry.method === "GET" || entry.method === "POST" ? { method: entry.method } : {}),
155
+ ...(entry.inputSchema !== undefined ? { inputSchema: entry.inputSchema } : {}),
156
+ ...(entry.outputSchema !== undefined ? { outputSchema: entry.outputSchema } : {}),
157
+ ...(isString(entry.policy) ? { policy: entry.policy } : {}),
158
+ ...(typeof entry.tenantScoped === "boolean" ? { tenantScoped: entry.tenantScoped } : {}),
159
+ ...(TRANSACTIONS.includes(entry.transaction as ForgeExternalTransactionMode)
160
+ ? { transaction: entry.transaction as ForgeExternalTransactionMode }
161
+ : {}),
162
+ ...(RISKS.includes(entry.risk as ForgeExternalEntryRisk)
163
+ ? { risk: entry.risk as ForgeExternalEntryRisk }
164
+ : {}),
165
+ ...(typeof entry.needsApproval === "boolean" ? { needsApproval: entry.needsApproval } : {}),
166
+ ...(stringArray(entry.effects) ? { effects: entry.effects } : {}),
167
+ };
168
+ }
169
+
170
+ export function validateExternalManifest(
171
+ value: unknown,
172
+ options: { file?: string } = {},
173
+ ): { manifest: ForgeExternalManifest | null; diagnostics: Diagnostic[] } {
174
+ const diagnostics: Diagnostic[] = [];
175
+ if (!isObject(value)) {
176
+ return {
177
+ manifest: null,
178
+ diagnostics: [
179
+ externalDiagnostic(
180
+ "error",
181
+ "FORGE_EXTERNAL_MANIFEST_OBJECT",
182
+ "forge external manifest must be a JSON object",
183
+ options.file,
184
+ ),
185
+ ],
186
+ };
187
+ }
188
+
189
+ if (value.forgeProtocol !== FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION) {
190
+ diagnostics.push(externalDiagnostic(
191
+ "error",
192
+ "FORGE_EXTERNAL_PROTOCOL_VERSION",
193
+ `forgeProtocol must be '${FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION}'`,
194
+ options.file,
195
+ ));
196
+ }
197
+ if (!isString(value.language)) {
198
+ diagnostics.push(externalDiagnostic(
199
+ "error",
200
+ "FORGE_EXTERNAL_LANGUAGE",
201
+ "external manifest must declare a non-empty language",
202
+ options.file,
203
+ ));
204
+ }
205
+ if (!isObject(value.service)) {
206
+ diagnostics.push(externalDiagnostic(
207
+ "error",
208
+ "FORGE_EXTERNAL_SERVICE",
209
+ "external manifest must declare a service object",
210
+ options.file,
211
+ ));
212
+ }
213
+ if (!Array.isArray(value.entries)) {
214
+ diagnostics.push(externalDiagnostic(
215
+ "error",
216
+ "FORGE_EXTERNAL_ENTRIES",
217
+ "external manifest must declare an entries array",
218
+ options.file,
219
+ ));
220
+ }
221
+
222
+ const service = isObject(value.service) ? value.service : {};
223
+ if (!isString(service.name)) {
224
+ diagnostics.push(externalDiagnostic(
225
+ "error",
226
+ "FORGE_EXTERNAL_SERVICE_NAME",
227
+ "external service must have a non-empty name",
228
+ options.file,
229
+ ));
230
+ }
231
+ if (!TRANSPORTS.includes(service.transport as ForgeExternalTransportKind)) {
232
+ diagnostics.push(externalDiagnostic(
233
+ "error",
234
+ "FORGE_EXTERNAL_SERVICE_TRANSPORT",
235
+ `external service '${String(service.name || "unknown")}' must use transport http, grpc, or stdio`,
236
+ options.file,
237
+ ));
238
+ }
239
+ if (service.transport === "http" && !isString(service.baseUrl)) {
240
+ diagnostics.push(externalDiagnostic(
241
+ "error",
242
+ "FORGE_EXTERNAL_SERVICE_BASE_URL",
243
+ `external service '${String(service.name || "unknown")}' uses http transport and must declare a non-empty baseUrl`,
244
+ options.file,
245
+ "Set service.baseUrl to the external runtime base URL.",
246
+ ));
247
+ }
248
+ const hasCommand = isString(service.command);
249
+ const hasCommandArgs = commandArgsArray(service.commandArgs);
250
+ if (service.commandArgs !== undefined && !hasCommandArgs) {
251
+ diagnostics.push(externalDiagnostic(
252
+ "error",
253
+ "FORGE_EXTERNAL_SERVICE_COMMAND_ARGS",
254
+ `external service '${String(service.name || "unknown")}' commandArgs must be a non-empty string array`,
255
+ options.file,
256
+ "Set service.commandArgs to an array whose first item is the executable.",
257
+ ));
258
+ }
259
+ if (service.transport === "stdio" && !hasCommand && !hasCommandArgs) {
260
+ diagnostics.push(externalDiagnostic(
261
+ "error",
262
+ "FORGE_EXTERNAL_SERVICE_COMMAND",
263
+ `external service '${String(service.name || "unknown")}' uses stdio transport and must declare a non-empty command or commandArgs`,
264
+ options.file,
265
+ "Set service.command to a command line string or service.commandArgs to a structured executable/args array.",
266
+ ));
267
+ }
268
+
269
+ const entries = Array.isArray(value.entries)
270
+ ? value.entries
271
+ .map((entry, index) => validateEntry(entry, index, String(service.name || "unknown"), diagnostics, options.file))
272
+ .filter((entry): entry is ForgeExternalManifestEntry => entry !== null)
273
+ : [];
274
+ const seen = new Set<string>();
275
+ for (const entry of entries) {
276
+ const key = `${entry.kind}:${entry.name}`;
277
+ if (seen.has(key)) {
278
+ diagnostics.push(externalDiagnostic(
279
+ "error",
280
+ "FORGE_EXTERNAL_ENTRY_DUPLICATE",
281
+ `external service '${String(service.name)}' declares duplicate ${entry.kind} '${entry.name}'`,
282
+ options.file,
283
+ ));
284
+ }
285
+ seen.add(key);
286
+ }
287
+
288
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
289
+ return { manifest: null, diagnostics };
290
+ }
291
+
292
+ return {
293
+ manifest: {
294
+ forgeProtocol: FORGE_EXTERNAL_MANIFEST_PROTOCOL_VERSION,
295
+ language: value.language as ForgeExternalManifest["language"],
296
+ ...(isString(value.framework) ? { framework: value.framework } : {}),
297
+ service: {
298
+ name: service.name as string,
299
+ transport: service.transport as ForgeExternalTransportKind,
300
+ ...(isString(service.baseUrl) ? { baseUrl: service.baseUrl } : {}),
301
+ ...(hasCommand ? { command: service.command as string } : {}),
302
+ ...(hasCommandArgs ? { commandArgs: service.commandArgs as string[] } : {}),
303
+ ...(isString(service.health) ? { health: service.health } : {}),
304
+ },
305
+ entries,
306
+ ...(isObject(value.schemas) ? { schemas: value.schemas } : {}),
307
+ },
308
+ diagnostics,
309
+ };
310
+ }
311
+
312
+ export function validateExternalManifestRegistry(
313
+ value: unknown,
314
+ options: { file?: string } = {},
315
+ ): { registry: ForgeExternalManifestRegistryFile | null; diagnostics: Diagnostic[] } {
316
+ if (!isObject(value)) {
317
+ return {
318
+ registry: null,
319
+ diagnostics: [
320
+ externalDiagnostic(
321
+ "error",
322
+ "FORGE_EXTERNAL_REGISTRY_OBJECT",
323
+ "external manifest registry must be a JSON object",
324
+ options.file,
325
+ ),
326
+ ],
327
+ };
328
+ }
329
+ if (value.schemaVersion !== "0.1.0") {
330
+ return {
331
+ registry: null,
332
+ diagnostics: [
333
+ externalDiagnostic(
334
+ "error",
335
+ "FORGE_EXTERNAL_REGISTRY_VERSION",
336
+ "external manifest registry schemaVersion must be '0.1.0'",
337
+ options.file,
338
+ ),
339
+ ],
340
+ };
341
+ }
342
+ if (!Array.isArray(value.manifests)) {
343
+ return {
344
+ registry: null,
345
+ diagnostics: [
346
+ externalDiagnostic(
347
+ "error",
348
+ "FORGE_EXTERNAL_REGISTRY_MANIFESTS",
349
+ "external manifest registry must include a manifests array",
350
+ options.file,
351
+ ),
352
+ ],
353
+ };
354
+ }
355
+
356
+ const diagnostics: Diagnostic[] = [];
357
+ const manifests: ForgeExternalManifest[] = [];
358
+ for (const [index, manifestValue] of value.manifests.entries()) {
359
+ const result = validateExternalManifest(manifestValue, {
360
+ file: options.file ? `${options.file}#manifests/${index}` : undefined,
361
+ });
362
+ diagnostics.push(...result.diagnostics);
363
+ if (result.manifest) {
364
+ manifests.push(result.manifest);
365
+ }
366
+ }
367
+ return {
368
+ registry: diagnostics.some((diagnostic) => diagnostic.severity === "error")
369
+ ? null
370
+ : { schemaVersion: "0.1.0", manifests },
371
+ diagnostics,
372
+ };
373
+ }