create-windy 0.3.2 → 0.3.5

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 (343) hide show
  1. package/README.md +27 -9
  2. package/dist/cli.js +825 -117
  3. package/package.json +1 -1
  4. package/template/.agents/skills/windy-business-module/SKILL.md +16 -3
  5. package/template/.windy-template.json +2 -2
  6. package/template/AGENTS.md +4 -0
  7. package/template/README.md +18 -11
  8. package/template/_gitignore +1 -0
  9. package/template/apps/agent-server/README.md +2 -0
  10. package/template/apps/agent-server/pyproject.toml +8 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +87 -10
  12. package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +18 -3
  13. package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +78 -11
  14. package/template/apps/agent-server/src/southwind_agent_server/app.py +120 -115
  15. package/template/apps/agent-server/src/southwind_agent_server/artifact_input.py +153 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/config.py +29 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +81 -1
  18. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +8 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/engine.py +28 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/fingerprint.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/http_helpers.py +62 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/lease.py +15 -0
  23. package/template/apps/agent-server/src/southwind_agent_server/multimodal.py +76 -0
  24. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +112 -20
  25. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +14 -1
  26. package/template/apps/agent-server/src/southwind_agent_server/postgres_store.py +329 -0
  27. package/template/apps/agent-server/src/southwind_agent_server/postgres_store_backend.py +332 -0
  28. package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +64 -3
  29. package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +68 -0
  30. package/template/apps/agent-server/src/southwind_agent_server/service.py +218 -56
  31. package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +36 -0
  32. package/template/apps/agent-server/src/southwind_agent_server/sqlite_catalog.py +107 -0
  33. package/template/apps/agent-server/src/southwind_agent_server/sqlite_schema.py +65 -0
  34. package/template/apps/agent-server/src/southwind_agent_server/sqlite_serialization.py +16 -0
  35. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +89 -57
  36. package/template/apps/agent-server/src/southwind_agent_server/store.py +105 -2
  37. package/template/apps/agent-server/src/southwind_agent_server/store_cipher.py +40 -0
  38. package/template/apps/agent-server/src/southwind_agent_server/telemetry.py +89 -0
  39. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +72 -0
  40. package/template/apps/agent-server/tests/test_adk_adapter.py +6 -2
  41. package/template/apps/agent-server/tests/test_api.py +24 -1
  42. package/template/apps/agent-server/tests/test_artifact_input.py +110 -0
  43. package/template/apps/agent-server/tests/test_config.py +4 -0
  44. package/template/apps/agent-server/tests/test_fingerprint.py +10 -0
  45. package/template/apps/agent-server/tests/test_multimodal.py +51 -0
  46. package/template/apps/agent-server/tests/test_openai_provider.py +6 -2
  47. package/template/apps/agent-server/tests/test_postgres_store.py +131 -0
  48. package/template/apps/agent-server/tests/test_provider_governance.py +61 -0
  49. package/template/apps/agent-server/tests/test_sqlite_store.py +95 -10
  50. package/template/apps/agent-server/tests/test_telemetry.py +25 -0
  51. package/template/apps/agent-server/tests/test_tool_approval.py +28 -0
  52. package/template/apps/agent-server/uv.lock +165 -0
  53. package/template/apps/document-server/Dockerfile +19 -0
  54. package/template/apps/document-server/pyproject.toml +43 -0
  55. package/template/apps/document-server/src/southwind_document_server/__init__.py +1 -0
  56. package/template/apps/document-server/src/southwind_document_server/adapter.py +337 -0
  57. package/template/apps/document-server/src/southwind_document_server/app.py +151 -0
  58. package/template/apps/document-server/src/southwind_document_server/config.py +94 -0
  59. package/template/apps/document-server/src/southwind_document_server/contracts.py +103 -0
  60. package/template/apps/document-server/src/southwind_document_server/service.py +149 -0
  61. package/template/apps/document-server/src/southwind_document_server/store.py +185 -0
  62. package/template/apps/document-server/tests/__init__.py +1 -0
  63. package/template/apps/document-server/tests/test_config.py +42 -0
  64. package/template/apps/document-server/tests/test_service.py +140 -0
  65. package/template/apps/document-server/uv.lock +430 -0
  66. package/template/apps/embedding-server/Dockerfile +17 -0
  67. package/template/apps/embedding-server/pyproject.toml +44 -0
  68. package/template/apps/embedding-server/src/southwind_embedding_server/__init__.py +1 -0
  69. package/template/apps/embedding-server/src/southwind_embedding_server/adapter.py +254 -0
  70. package/template/apps/embedding-server/src/southwind_embedding_server/app.py +119 -0
  71. package/template/apps/embedding-server/src/southwind_embedding_server/config.py +120 -0
  72. package/template/apps/embedding-server/src/southwind_embedding_server/contracts.py +106 -0
  73. package/template/apps/embedding-server/src/southwind_embedding_server/error.py +6 -0
  74. package/template/apps/embedding-server/src/southwind_embedding_server/image_input.py +87 -0
  75. package/template/apps/embedding-server/tests/__init__.py +1 -0
  76. package/template/apps/embedding-server/tests/test_adapter.py +92 -0
  77. package/template/apps/embedding-server/tests/test_config.py +38 -0
  78. package/template/apps/embedding-server/uv.lock +449 -0
  79. package/template/apps/server/src/agent/background-operation-port.ts +37 -0
  80. package/template/apps/server/src/agent/background-operation.test.ts +231 -0
  81. package/template/apps/server/src/agent/background-operation.ts +252 -0
  82. package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +114 -0
  83. package/template/apps/server/src/agent/execution-grants.ts +7 -1
  84. package/template/apps/server/src/agent/fake-runtime.test-support.ts +43 -0
  85. package/template/apps/server/src/agent/idempotency-key.test.ts +28 -0
  86. package/template/apps/server/src/agent/idempotency-key.ts +11 -0
  87. package/template/apps/server/src/agent/remote-runtime.test.ts +115 -1
  88. package/template/apps/server/src/agent/remote-runtime.ts +173 -5
  89. package/template/apps/server/src/agent/run-authorization.test-support.ts +9 -0
  90. package/template/apps/server/src/agent/run-authorization.test.ts +126 -0
  91. package/template/apps/server/src/agent/run-authorization.ts +180 -0
  92. package/template/apps/server/src/agent/run-binding-repository.ts +93 -0
  93. package/template/apps/server/src/agent/run-contracts.ts +88 -1
  94. package/template/apps/server/src/agent/run-definition-support.ts +58 -0
  95. package/template/apps/server/src/agent/run-errors.ts +25 -0
  96. package/template/apps/server/src/agent/run-facade.test.ts +57 -46
  97. package/template/apps/server/src/agent/run-facade.ts +185 -65
  98. package/template/apps/server/src/agent/run-release.ts +78 -0
  99. package/template/apps/server/src/agent/run-route-input.ts +169 -0
  100. package/template/apps/server/src/agent/run-routes.ts +63 -76
  101. package/template/apps/server/src/agent/runtime-bootstrap.ts +169 -13
  102. package/template/apps/server/src/agent/sandbox-client.ts +200 -0
  103. package/template/apps/server/src/agent/sandbox-protocol.ts +76 -0
  104. package/template/apps/server/src/agent/sandbox-registry.ts +205 -0
  105. package/template/apps/server/src/agent/sandbox-runtime.test.ts +137 -0
  106. package/template/apps/server/src/agent/sandbox-worker-server.ts +195 -0
  107. package/template/apps/server/src/agent/sandbox-worker.ts +12 -0
  108. package/template/apps/server/src/agent/tool-approval.test.ts +75 -0
  109. package/template/apps/server/src/agent/tool-approval.ts +115 -0
  110. package/template/apps/server/src/agent/tool-audit.ts +62 -0
  111. package/template/apps/server/src/agent/tool-host.test.ts +39 -11
  112. package/template/apps/server/src/agent/tool-host.ts +184 -17
  113. package/template/apps/server/src/agent/tool-registry.ts +52 -47
  114. package/template/apps/server/src/agent/vision-facade.test.ts +256 -0
  115. package/template/apps/server/src/agent/vision-input.ts +120 -0
  116. package/template/apps/server/src/ai-artifact/codec.test.ts +44 -0
  117. package/template/apps/server/src/ai-artifact/codec.ts +137 -0
  118. package/template/apps/server/src/ai-artifact/routes.ts +37 -0
  119. package/template/apps/server/src/ai-artifact/runtime.ts +205 -0
  120. package/template/apps/server/src/application-services.ts +62 -2
  121. package/template/apps/server/src/auth/reauthentication.test.ts +40 -0
  122. package/template/apps/server/src/auth/reauthentication.ts +19 -0
  123. package/template/apps/server/src/auth/runtime.ts +9 -1
  124. package/template/apps/server/src/data-access/context.ts +6 -4
  125. package/template/apps/server/src/data-access/scope-snapshot.ts +27 -0
  126. package/template/apps/server/src/data-governance/export/approval-port.ts +6 -3
  127. package/template/apps/server/src/data-governance/export/authorization-port.test.ts +23 -4
  128. package/template/apps/server/src/data-governance/export/authorization-port.ts +89 -25
  129. package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
  130. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
  131. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +15 -4
  132. package/template/apps/server/src/data-governance/export/job-runtime.ts +68 -15
  133. package/template/apps/server/src/data-governance/export/ndjson-stream.ts +25 -6
  134. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +4 -2
  135. package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
  136. package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
  137. package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
  138. package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
  139. package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
  140. package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
  141. package/template/apps/server/src/data-governance/export/routes.ts +11 -1
  142. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +6 -18
  143. package/template/apps/server/src/data-governance/export/service-support.ts +188 -0
  144. package/template/apps/server/src/data-governance/export/service.ts +90 -113
  145. package/template/apps/server/src/data-governance/export/source.ts +101 -0
  146. package/template/apps/server/src/data-governance/export/types.ts +27 -0
  147. package/template/apps/server/src/document/contracts.ts +76 -0
  148. package/template/apps/server/src/document/facade.ts +339 -0
  149. package/template/apps/server/src/document/remote-runtime.test.ts +96 -0
  150. package/template/apps/server/src/document/remote-runtime.ts +223 -0
  151. package/template/apps/server/src/document/routes.ts +193 -0
  152. package/template/apps/server/src/embedding/contracts.ts +83 -0
  153. package/template/apps/server/src/embedding/error.ts +10 -0
  154. package/template/apps/server/src/embedding/facade.ts +217 -0
  155. package/template/apps/server/src/embedding/input.ts +172 -0
  156. package/template/apps/server/src/embedding/remote-runtime.test.ts +66 -0
  157. package/template/apps/server/src/embedding/remote-runtime.ts +218 -0
  158. package/template/apps/server/src/embedding/routes.ts +152 -0
  159. package/template/apps/server/src/index.ts +40 -43
  160. package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
  161. package/template/apps/server/src/jobs/artifact-service.ts +10 -0
  162. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
  163. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
  164. package/template/apps/server/src/jobs/drizzle-mappers.ts +4 -1
  165. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
  166. package/template/apps/server/src/jobs/drizzle-repository.ts +1 -0
  167. package/template/apps/server/src/jobs/execution-repository.ts +17 -0
  168. package/template/apps/server/src/jobs/execution.test.ts +34 -0
  169. package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
  170. package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
  171. package/template/apps/server/src/jobs/legacy-service-runtime.ts +5 -2
  172. package/template/apps/server/src/jobs/repository.ts +3 -0
  173. package/template/apps/server/src/jobs/worker.ts +80 -41
  174. package/template/apps/server/src/module-host/ai-operation-port.ts +170 -0
  175. package/template/apps/server/src/module-host/durable-context.test.ts +312 -0
  176. package/template/apps/server/src/module-host/durable-context.ts +124 -0
  177. package/template/apps/server/src/module-host/durable-handler-registration.ts +72 -0
  178. package/template/apps/server/src/module-host/governed-export-route-support.ts +171 -0
  179. package/template/apps/server/src/module-host/governed-export-routes.test.ts +327 -0
  180. package/template/apps/server/src/module-host/governed-export-routes.ts +238 -0
  181. package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
  182. package/template/apps/server/src/module-host/governed-resource-routes.ts +331 -0
  183. package/template/apps/server/src/module-host/governed-resources.test.ts +344 -0
  184. package/template/apps/server/src/module-host/governed-resources.ts +203 -0
  185. package/template/apps/server/src/module-host/http-runtime.ts +95 -0
  186. package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
  187. package/template/apps/server/src/module-host/initialize.test.ts +49 -1
  188. package/template/apps/server/src/module-host/initialize.ts +39 -16
  189. package/template/apps/server/src/module-host/job-port.test.ts +262 -0
  190. package/template/apps/server/src/module-host/job-port.ts +334 -0
  191. package/template/apps/server/src/module-host/request-context.test.ts +88 -2
  192. package/template/apps/server/src/module-host/request-context.ts +56 -22
  193. package/template/apps/server/src/module-host/route-installer.test.ts +10 -0
  194. package/template/apps/server/src/module-host/route-installer.ts +36 -1
  195. package/template/apps/server/src/module-host/sandbox-tool-registration.ts +42 -0
  196. package/template/apps/server/src/module-host/tool-handlers.ts +67 -31
  197. package/template/apps/server/src/module-host.ts +1 -0
  198. package/template/apps/server/src/module-runtime-validation.ts +7 -3
  199. package/template/apps/server/src/speech/contracts.ts +76 -0
  200. package/template/apps/server/src/speech/facade.ts +316 -0
  201. package/template/apps/server/src/speech/remote-runtime.test.ts +88 -0
  202. package/template/apps/server/src/speech/remote-runtime.ts +227 -0
  203. package/template/apps/server/src/speech/routes.ts +180 -0
  204. package/template/apps/server/src/system/actor.ts +39 -0
  205. package/template/apps/server/src/system/route-helpers.ts +1 -0
  206. package/template/apps/server/src/workflow/service.ts +2 -1
  207. package/template/apps/speech-server/Dockerfile +22 -0
  208. package/template/apps/speech-server/pyproject.toml +43 -0
  209. package/template/apps/speech-server/src/southwind_speech_server/__init__.py +1 -0
  210. package/template/apps/speech-server/src/southwind_speech_server/adapter.py +249 -0
  211. package/template/apps/speech-server/src/southwind_speech_server/app.py +183 -0
  212. package/template/apps/speech-server/src/southwind_speech_server/config.py +100 -0
  213. package/template/apps/speech-server/src/southwind_speech_server/contracts.py +103 -0
  214. package/template/apps/speech-server/src/southwind_speech_server/errors.py +6 -0
  215. package/template/apps/speech-server/src/southwind_speech_server/normalizer.py +108 -0
  216. package/template/apps/speech-server/src/southwind_speech_server/output_validation.py +128 -0
  217. package/template/apps/speech-server/src/southwind_speech_server/service.py +162 -0
  218. package/template/apps/speech-server/src/southwind_speech_server/store.py +209 -0
  219. package/template/apps/speech-server/tests/__init__.py +1 -0
  220. package/template/apps/speech-server/tests/test_config.py +33 -0
  221. package/template/apps/speech-server/tests/test_service.py +154 -0
  222. package/template/apps/speech-server/uv.lock +430 -0
  223. package/template/apps/web/src/app/business-routes.ts +22 -0
  224. package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
  225. package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
  226. package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
  227. package/template/apps/web/src/components/common/FormDialog.vue +6 -0
  228. package/template/apps/web/src/components/common/ModalLayout.webtest.ts +13 -0
  229. package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
  230. package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
  231. package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
  232. package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
  233. package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
  234. package/template/apps/web/src/layout/AppShell.vue +28 -25
  235. package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
  236. package/template/apps/web/src/router/business-routes.ts +68 -0
  237. package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
  238. package/template/apps/web/src/router/index.ts +7 -0
  239. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
  240. package/template/apps/web/src/services/access-api.ts +5 -1
  241. package/template/apps/web/src/services/access-api.webtest.ts +35 -0
  242. package/template/docker-compose.yml +127 -0
  243. package/template/docs/architecture/ai-egress.md +12 -9
  244. package/template/docs/architecture/ai-runtime.md +23 -11
  245. package/template/docs/architecture/durable-jobs.md +25 -3
  246. package/template/docs/architecture/foundation-infrastructure.md +163 -0
  247. package/template/docs/architecture/module-host.md +115 -0
  248. package/template/docs/architecture/server-module-interface.md +39 -0
  249. package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
  250. package/template/docs/development/business-web-routes.md +66 -0
  251. package/template/docs/development/custom-login-page.md +3 -3
  252. package/template/docs/development/module-lifecycle.md +71 -0
  253. package/template/docs/operations/observability.md +5 -1
  254. package/template/docs/platform/agent-runtime.md +21 -11
  255. package/template/docs/platform/agent-tool-sandbox.md +58 -0
  256. package/template/docs/platform/agent-tools.md +5 -0
  257. package/template/docs/platform/document-ai-runtime.md +204 -0
  258. package/template/docs/platform/governed-export-renderers.md +132 -0
  259. package/template/docs/platform/navigation-registry.md +5 -0
  260. package/template/docs/platform/organization-membership.md +2 -1
  261. package/template/docs/platform/speech-ai-runtime.md +156 -0
  262. package/template/docs/platform/system-crud-api.md +7 -1
  263. package/template/docs/platform/vision-ai-runtime.md +97 -0
  264. package/template/docs/platform/web-system-crud.md +6 -0
  265. package/template/package.json +16 -2
  266. package/template/packages/ai-client/index.ts +4 -0
  267. package/template/packages/ai-client/package.json +5 -2
  268. package/template/packages/ai-client/src/agent/client/artifact.ts +26 -0
  269. package/template/packages/ai-client/src/agent/client/attach.test.ts +124 -0
  270. package/template/packages/ai-client/src/agent/client/client.ts +119 -7
  271. package/template/packages/ai-client/src/agent/client/run.ts +14 -0
  272. package/template/packages/ai-client/src/agent/client/types.ts +18 -0
  273. package/template/packages/ai-client/src/agent/client/vision.test.ts +53 -0
  274. package/template/packages/ai-client/src/agent/contracts/contracts.test.ts +1 -0
  275. package/template/packages/ai-client/src/agent/contracts/events.ts +33 -0
  276. package/template/packages/ai-client/src/agent/contracts/runs.ts +71 -0
  277. package/template/packages/ai-client/src/agent/index.ts +1 -0
  278. package/template/packages/ai-client/src/artifact-ref.ts +8 -0
  279. package/template/packages/ai-client/src/document/client.test.ts +114 -0
  280. package/template/packages/ai-client/src/document/client.ts +134 -0
  281. package/template/packages/ai-client/src/document/contracts.ts +152 -0
  282. package/template/packages/ai-client/src/document/error.ts +43 -0
  283. package/template/packages/ai-client/src/document/index.ts +3 -0
  284. package/template/packages/ai-client/src/document/run.ts +104 -0
  285. package/template/packages/ai-client/src/document/validation.ts +115 -0
  286. package/template/packages/ai-client/src/embedding/client.test.ts +77 -0
  287. package/template/packages/ai-client/src/embedding/client.ts +96 -0
  288. package/template/packages/ai-client/src/embedding/contracts.ts +46 -0
  289. package/template/packages/ai-client/src/embedding/error.ts +61 -0
  290. package/template/packages/ai-client/src/embedding/index.ts +3 -0
  291. package/template/packages/ai-client/src/embedding/validation.ts +117 -0
  292. package/template/packages/ai-client/src/speech/client.test.ts +135 -0
  293. package/template/packages/ai-client/src/speech/client.ts +104 -0
  294. package/template/packages/ai-client/src/speech/contracts.ts +129 -0
  295. package/template/packages/ai-client/src/speech/error.ts +43 -0
  296. package/template/packages/ai-client/src/speech/index.ts +3 -0
  297. package/template/packages/ai-client/src/speech/run.ts +133 -0
  298. package/template/packages/ai-client/src/speech/validation.ts +132 -0
  299. package/template/packages/config/package.json +2 -2
  300. package/template/packages/database/drizzle/0039_early_ezekiel_stane.sql +1 -0
  301. package/template/packages/database/drizzle/0040_colossal_baron_strucker.sql +63 -0
  302. package/template/packages/database/drizzle/0041_yummy_prowler.sql +21 -0
  303. package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
  304. package/template/packages/database/drizzle/meta/0039_snapshot.json +7755 -0
  305. package/template/packages/database/drizzle/meta/0040_snapshot.json +8150 -0
  306. package/template/packages/database/drizzle/meta/0041_snapshot.json +8323 -0
  307. package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
  308. package/template/packages/database/drizzle/meta/_journal.json +28 -0
  309. package/template/packages/database/package.json +2 -2
  310. package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
  311. package/template/packages/database/src/schema/agent-runtime.ts +149 -0
  312. package/template/packages/database/src/schema/common.ts +2 -14
  313. package/template/packages/database/src/schema/durable-jobs.ts +5 -0
  314. package/template/packages/database/src/schema/governed-exports.ts +17 -5
  315. package/template/packages/database/src/schema/index.ts +3 -0
  316. package/template/packages/database/src/schema-durable-recovery.test.ts +31 -0
  317. package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
  318. package/template/packages/database/src/schema.test.ts +6 -23
  319. package/template/packages/jobs/package.json +3 -2
  320. package/template/packages/jobs/src/handlers.test.ts +13 -0
  321. package/template/packages/jobs/src/handlers.ts +7 -1
  322. package/template/packages/jobs/src/types.ts +4 -0
  323. package/template/packages/modules/package.json +2 -2
  324. package/template/packages/modules/src/ai-operation-composition.test-support.ts +165 -0
  325. package/template/packages/modules/src/ai-operation-composition.test.ts +91 -127
  326. package/template/packages/modules/src/ai-operation-composition.ts +59 -1
  327. package/template/packages/modules/src/manifest.ts +25 -1
  328. package/template/packages/modules/src/tool-composition.ts +18 -0
  329. package/template/packages/server-sdk/index.ts +50 -0
  330. package/template/packages/server-sdk/package.json +10 -4
  331. package/template/packages/server-sdk/src/ai-artifact-port.ts +34 -0
  332. package/template/packages/server-sdk/src/ai-operation-port.ts +65 -0
  333. package/template/packages/server-sdk/src/ai-operation-registration.ts +42 -1
  334. package/template/packages/server-sdk/src/background-ai-port.ts +43 -0
  335. package/template/packages/server-sdk/src/data-governance-registration.ts +116 -0
  336. package/template/packages/server-sdk/src/durable-handler.ts +29 -0
  337. package/template/packages/server-sdk/src/jobs-port.ts +69 -0
  338. package/template/packages/server-sdk/src/module-host.ts +16 -2
  339. package/template/packages/server-sdk/src/request-context.ts +6 -2
  340. package/template/packages/server-sdk/src/sandbox-tool.ts +21 -0
  341. package/template/packages/server-sdk/src/tool-registration.ts +2 -0
  342. package/template/packages/shared/package.json +1 -1
  343. package/template/packages/shared/src/rbac.ts +30 -0
@@ -0,0 +1,134 @@
1
+ import type {
2
+ DocumentAiClient,
3
+ DocumentFetch,
4
+ DocumentOperation,
5
+ DocumentRun,
6
+ SubmitDocumentRunInput,
7
+ } from "./contracts.js";
8
+ import { DocumentClientError, documentHttpError } from "./error.js";
9
+ import { HttpDocumentRun } from "./run.js";
10
+ import { parseArtifactRef, parseCreatedRun } from "./validation.js";
11
+
12
+ export interface DocumentClientOptions {
13
+ readonly baseUrl: string | URL;
14
+ readonly runsPath?: string;
15
+ readonly fetch?: DocumentFetch;
16
+ }
17
+
18
+ export class HttpDocumentAiClient implements DocumentAiClient {
19
+ private readonly baseUrl: URL;
20
+ private readonly runsPath: string;
21
+ private readonly fetch: DocumentFetch;
22
+
23
+ constructor(options: DocumentClientOptions) {
24
+ this.baseUrl = new URL(options.baseUrl);
25
+ if (!["http:", "https:"].includes(this.baseUrl.protocol)) {
26
+ throw new DocumentClientError(
27
+ "INVALID_CONFIGURATION",
28
+ "Document Client 只允许 HTTP 或 HTTPS",
29
+ false,
30
+ );
31
+ }
32
+ this.runsPath = options.runsPath ?? "/api/document/runs";
33
+ this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
34
+ }
35
+
36
+ async submit<Options, Output>(
37
+ operation: DocumentOperation<Options, Output>,
38
+ input: SubmitDocumentRunInput<Options>,
39
+ context: { idempotencyKey: string; signal?: AbortSignal },
40
+ ): Promise<DocumentRun<Output>> {
41
+ if (!operation.key.trim() || !context.idempotencyKey.trim()) {
42
+ throw new DocumentClientError(
43
+ "INVALID_CONFIGURATION",
44
+ "Document Operation 与幂等键不能为空",
45
+ false,
46
+ );
47
+ }
48
+ const sources = parseSources(input);
49
+ const options = input.options
50
+ ? (operation.parseOptions?.(input.options) ?? input.options)
51
+ : undefined;
52
+ let response: Response;
53
+ try {
54
+ response = await this.fetch(this.endpoint(this.runsPath), {
55
+ method: "POST",
56
+ headers: {
57
+ "content-type": "application/json",
58
+ "idempotency-key": context.idempotencyKey,
59
+ },
60
+ body: JSON.stringify({
61
+ operationKey: operation.key,
62
+ ...(sources.length === 1 && input.source
63
+ ? { source: sources[0] }
64
+ : { sources }),
65
+ options,
66
+ }),
67
+ signal: context.signal,
68
+ });
69
+ } catch (error) {
70
+ throw new DocumentClientError(
71
+ context.signal?.aborted ? "RUN_CANCELLED" : "RUNTIME_UNAVAILABLE",
72
+ context.signal?.aborted
73
+ ? "创建文档任务已取消"
74
+ : "Document Runtime 暂不可用",
75
+ !context.signal?.aborted,
76
+ undefined,
77
+ { cause: error },
78
+ );
79
+ }
80
+ if (!response.ok)
81
+ throw await documentHttpError(response, "创建文档任务失败");
82
+ const created = parseCreatedRun(await response.json());
83
+ return new HttpDocumentRun({
84
+ id: created.runId,
85
+ eventsUrl: this.endpoint(created.eventsUrl),
86
+ resultUrl: this.endpoint(
87
+ `${this.runsPath}/${encodeURIComponent(created.runId)}/result`,
88
+ ),
89
+ cancelUrl: this.endpoint(
90
+ `${this.runsPath}/${encodeURIComponent(created.runId)}/cancel`,
91
+ ),
92
+ fetch: this.fetch,
93
+ signal: context.signal,
94
+ parseOutput: operation.parseOutput,
95
+ });
96
+ }
97
+
98
+ private endpoint(path: string): URL {
99
+ const endpoint = new URL(path, this.baseUrl);
100
+ if (endpoint.origin !== this.baseUrl.origin) {
101
+ throw new DocumentClientError(
102
+ "INVALID_RESPONSE",
103
+ "Document Runtime 返回了不受信任的 Endpoint",
104
+ false,
105
+ );
106
+ }
107
+ return endpoint;
108
+ }
109
+ }
110
+
111
+ function parseSources<Options>(
112
+ input: SubmitDocumentRunInput<Options>,
113
+ ): readonly ReturnType<typeof parseArtifactRef>[] {
114
+ if (input.source && input.sources) {
115
+ throw new DocumentClientError(
116
+ "INVALID_CONFIGURATION",
117
+ "Document source 与 sources 不能同时提供",
118
+ false,
119
+ );
120
+ }
121
+ const values = input.source ? [input.source] : input.sources;
122
+ if (!values?.length || values.length > 100) {
123
+ throw new DocumentClientError(
124
+ "INVALID_CONFIGURATION",
125
+ "Document 输入文件数量必须为 1 至 100",
126
+ false,
127
+ );
128
+ }
129
+ return values.map(parseArtifactRef);
130
+ }
131
+
132
+ export function createDocumentAiClient(options: DocumentClientOptions) {
133
+ return new HttpDocumentAiClient(options);
134
+ }
@@ -0,0 +1,152 @@
1
+ export type { ArtifactRef } from "../artifact-ref.js";
2
+ import type { ArtifactRef } from "../artifact-ref.js";
3
+
4
+ export type DocumentFetch = (
5
+ input: string | URL | Request,
6
+ init?: RequestInit,
7
+ ) => Promise<Response>;
8
+
9
+ export interface DocumentProcessingOptions {
10
+ readonly languages?: readonly string[];
11
+ readonly pageRange?: {
12
+ readonly from: number;
13
+ readonly to: number;
14
+ };
15
+ readonly outputs?: readonly DocumentOutputKind[];
16
+ }
17
+
18
+ export type DocumentOutputKind =
19
+ | "pages"
20
+ | "layout"
21
+ | "tables"
22
+ | "searchable-pdf"
23
+ | "json";
24
+
25
+ export interface DocumentOperation<Options, Output> {
26
+ readonly key: string;
27
+ parseOutput(value: unknown): Output;
28
+ parseOptions?(value: Options): Options;
29
+ }
30
+
31
+ export interface SubmitDocumentRunInput<Options> {
32
+ /** 单文档处理使用 source;图片合并等多源任务使用 sources,二者只能提供一个。 */
33
+ readonly source?: ArtifactRef;
34
+ readonly sources?: readonly ArtifactRef[];
35
+ readonly options?: Options & DocumentProcessingOptions;
36
+ }
37
+
38
+ export interface DocumentTextBlock {
39
+ readonly id: string;
40
+ readonly text: string;
41
+ readonly confidence?: number;
42
+ readonly bounds?: DocumentBounds;
43
+ }
44
+
45
+ export interface DocumentBounds {
46
+ readonly x: number;
47
+ readonly y: number;
48
+ readonly width: number;
49
+ readonly height: number;
50
+ readonly unit: "pixel" | "normalized";
51
+ }
52
+
53
+ export interface DocumentPage {
54
+ readonly pageNumber: number;
55
+ readonly width?: number;
56
+ readonly height?: number;
57
+ readonly text: string;
58
+ readonly blocks: readonly DocumentTextBlock[];
59
+ readonly layout?: readonly DocumentLayoutNode[];
60
+ }
61
+
62
+ export interface DocumentLayoutNode {
63
+ readonly id: string;
64
+ readonly type: string;
65
+ readonly text?: string;
66
+ readonly bounds?: DocumentBounds;
67
+ readonly children?: readonly DocumentLayoutNode[];
68
+ }
69
+
70
+ export interface DocumentTable {
71
+ readonly pageNumber: number;
72
+ readonly bounds?: DocumentBounds;
73
+ readonly rows: readonly (readonly string[])[];
74
+ }
75
+
76
+ export interface DocumentResult {
77
+ readonly pages: readonly DocumentPage[];
78
+ readonly tables: readonly DocumentTable[];
79
+ readonly artifacts: readonly ArtifactRef[];
80
+ readonly metadata: Readonly<Record<string, string | number | boolean>>;
81
+ }
82
+
83
+ export type DocumentRunStatus =
84
+ | "accepted"
85
+ | "running"
86
+ | "completed"
87
+ | "failed"
88
+ | "cancelled";
89
+
90
+ export type DocumentEvent =
91
+ | {
92
+ readonly type: "run.started";
93
+ readonly runId: string;
94
+ readonly sequence: number;
95
+ }
96
+ | {
97
+ readonly type: "document.page.completed";
98
+ readonly runId: string;
99
+ readonly sequence: number;
100
+ readonly pageNumber: number;
101
+ readonly completedPages: number;
102
+ readonly totalPages?: number;
103
+ }
104
+ | {
105
+ readonly type: "run.completed" | "run.cancelled";
106
+ readonly runId: string;
107
+ readonly sequence: number;
108
+ }
109
+ | {
110
+ readonly type: "run.failed";
111
+ readonly runId: string;
112
+ readonly sequence: number;
113
+ readonly error: DocumentErrorPayload;
114
+ };
115
+
116
+ export interface DocumentErrorPayload {
117
+ readonly code: DocumentErrorCode;
118
+ readonly message: string;
119
+ readonly requestId: string;
120
+ readonly retryable: boolean;
121
+ }
122
+
123
+ export type DocumentErrorCode =
124
+ | "OPERATION_NOT_FOUND"
125
+ | "OPERATION_DISABLED"
126
+ | "INVALID_INPUT"
127
+ | "FORBIDDEN"
128
+ | "LICENSE_NOT_ALLOWED"
129
+ | "ARTIFACT_UNAVAILABLE"
130
+ | "ARTIFACT_EXPIRED"
131
+ | "CAPABILITY_UNAVAILABLE"
132
+ | "RUNTIME_UNAVAILABLE"
133
+ | "BUDGET_EXCEEDED"
134
+ | "OUTPUT_VALIDATION_FAILED"
135
+ | "RUN_CONFLICT"
136
+ | "RUN_TIMEOUT"
137
+ | "RUN_CANCELLED";
138
+
139
+ export interface DocumentRun<Output> {
140
+ readonly id: string;
141
+ readonly events: AsyncIterable<DocumentEvent>;
142
+ result(): Promise<Output>;
143
+ cancel(reason?: string): Promise<void>;
144
+ }
145
+
146
+ export interface DocumentAiClient {
147
+ submit<Options, Output>(
148
+ operation: DocumentOperation<Options, Output>,
149
+ input: SubmitDocumentRunInput<Options>,
150
+ context: { idempotencyKey: string; signal?: AbortSignal },
151
+ ): Promise<DocumentRun<Output>>;
152
+ }
@@ -0,0 +1,43 @@
1
+ import type { DocumentErrorCode } from "./contracts.js";
2
+
3
+ export class DocumentClientError extends Error {
4
+ constructor(
5
+ readonly code:
6
+ | DocumentErrorCode
7
+ | "INVALID_CONFIGURATION"
8
+ | "INVALID_RESPONSE",
9
+ message: string,
10
+ readonly retryable: boolean,
11
+ readonly requestId?: string,
12
+ options?: ErrorOptions,
13
+ ) {
14
+ super(message, options);
15
+ this.name = "DocumentClientError";
16
+ }
17
+ }
18
+
19
+ export async function documentHttpError(response: Response, fallback: string) {
20
+ try {
21
+ const value = (await response.json()) as Record<string, unknown>;
22
+ if (
23
+ typeof value.code === "string" &&
24
+ typeof value.message === "string" &&
25
+ typeof value.retryable === "boolean" &&
26
+ value.message.length <= 200
27
+ ) {
28
+ return new DocumentClientError(
29
+ value.code as DocumentErrorCode,
30
+ value.message,
31
+ value.retryable,
32
+ typeof value.requestId === "string" ? value.requestId : undefined,
33
+ );
34
+ }
35
+ } catch {
36
+ // Runtime 响应正文不可信,不向业务透传。
37
+ }
38
+ return new DocumentClientError(
39
+ "RUNTIME_UNAVAILABLE",
40
+ `${fallback}(HTTP ${response.status})`,
41
+ response.status >= 500,
42
+ );
43
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./client.js";
2
+ export * from "./contracts.js";
3
+ export * from "./error.js";
@@ -0,0 +1,104 @@
1
+ import type { DocumentEvent, DocumentFetch, DocumentRun } from "./contracts.js";
2
+ import { DocumentClientError, documentHttpError } from "./error.js";
3
+ import { parseDocumentEvent, record } from "./validation.js";
4
+
5
+ export interface HttpDocumentRunInput<Output> {
6
+ id: string;
7
+ eventsUrl: URL;
8
+ resultUrl: URL;
9
+ cancelUrl: URL;
10
+ fetch: DocumentFetch;
11
+ signal?: AbortSignal;
12
+ parseOutput(value: unknown): Output;
13
+ }
14
+
15
+ export class HttpDocumentRun<Output> implements DocumentRun<Output> {
16
+ readonly id: string;
17
+ readonly events: AsyncIterable<DocumentEvent>;
18
+
19
+ constructor(private readonly input: HttpDocumentRunInput<Output>) {
20
+ this.id = input.id;
21
+ this.events = streamEvents(input);
22
+ }
23
+
24
+ async result(): Promise<Output> {
25
+ const response = await this.input.fetch(this.input.resultUrl, {
26
+ signal: this.input.signal,
27
+ });
28
+ if (!response.ok)
29
+ throw await documentHttpError(response, "读取文档结果失败");
30
+ const body = record(await response.json(), "DocumentResult");
31
+ return this.input.parseOutput(body.output);
32
+ }
33
+
34
+ async cancel(reason?: string): Promise<void> {
35
+ const response = await this.input.fetch(this.input.cancelUrl, {
36
+ method: "POST",
37
+ headers: { "content-type": "application/json" },
38
+ body: JSON.stringify({ reason: reason?.slice(0, 200) }),
39
+ signal: this.input.signal,
40
+ });
41
+ if (!response.ok)
42
+ throw await documentHttpError(response, "取消文档任务失败");
43
+ }
44
+ }
45
+
46
+ async function* streamEvents<Output>(
47
+ input: HttpDocumentRunInput<Output>,
48
+ ): AsyncIterable<DocumentEvent> {
49
+ let cursor = 0;
50
+ while (true) {
51
+ const url = new URL(input.eventsUrl);
52
+ url.searchParams.set("after", String(cursor));
53
+ const response = await input.fetch(url, { signal: input.signal });
54
+ if (!response.ok)
55
+ throw await documentHttpError(response, "读取文档事件失败");
56
+ if (!response.body) {
57
+ throw new DocumentClientError(
58
+ "INVALID_RESPONSE",
59
+ "Document Runtime 未返回事件流",
60
+ true,
61
+ );
62
+ }
63
+ for await (const event of decodeSse(response.body)) {
64
+ const parsed = parseDocumentEvent(event);
65
+ if (parsed.sequence <= cursor) continue;
66
+ cursor = parsed.sequence;
67
+ yield parsed;
68
+ if (
69
+ ["run.completed", "run.failed", "run.cancelled"].includes(parsed.type)
70
+ ) {
71
+ return;
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ async function* decodeSse(body: ReadableStream<Uint8Array>) {
78
+ const reader = body.getReader();
79
+ const decoder = new TextDecoder();
80
+ let buffer = "";
81
+ try {
82
+ while (true) {
83
+ const chunk = await reader.read();
84
+ if (chunk.done) return;
85
+ buffer += decoder
86
+ .decode(chunk.value, { stream: true })
87
+ .replaceAll("\r\n", "\n");
88
+ let boundary = buffer.indexOf("\n\n");
89
+ while (boundary >= 0) {
90
+ const frame = buffer.slice(0, boundary);
91
+ buffer = buffer.slice(boundary + 2);
92
+ const data = frame
93
+ .split("\n")
94
+ .filter((line) => line.startsWith("data:"))
95
+ .map((line) => line.slice(5).trimStart())
96
+ .join("\n");
97
+ if (data) yield JSON.parse(data) as unknown;
98
+ boundary = buffer.indexOf("\n\n");
99
+ }
100
+ }
101
+ } finally {
102
+ reader.releaseLock();
103
+ }
104
+ }
@@ -0,0 +1,115 @@
1
+ import { DocumentClientError } from "./error.js";
2
+ import type {
3
+ DocumentErrorPayload,
4
+ DocumentEvent,
5
+ DocumentRunStatus,
6
+ } from "./contracts.js";
7
+ import type { ArtifactRef } from "../artifact-ref.js";
8
+
9
+ export function parseArtifactRef(value: unknown): ArtifactRef {
10
+ const item = record(value, "ArtifactRef");
11
+ const byteSize = integer(item.byteSize, "ArtifactRef.byteSize", 0);
12
+ const sha256 = text(item.sha256, "ArtifactRef.sha256");
13
+ if (!/^[a-f0-9]{64}$/.test(sha256)) invalid("ArtifactRef.sha256");
14
+ return {
15
+ value: text(item.value, "ArtifactRef.value"),
16
+ mediaType: text(item.mediaType, "ArtifactRef.mediaType"),
17
+ byteSize,
18
+ sha256,
19
+ ...(item.expiresAt === undefined
20
+ ? {}
21
+ : { expiresAt: text(item.expiresAt, "ArtifactRef.expiresAt") }),
22
+ };
23
+ }
24
+
25
+ export function parseDocumentEvent(value: unknown): DocumentEvent {
26
+ const item = record(value, "DocumentEvent");
27
+ const type = text(item.type, "DocumentEvent.type");
28
+ const base = {
29
+ runId: text(item.runId, "DocumentEvent.runId"),
30
+ sequence: integer(item.sequence, "DocumentEvent.sequence", 1),
31
+ };
32
+ if (
33
+ type === "run.started" ||
34
+ type === "run.completed" ||
35
+ type === "run.cancelled"
36
+ ) {
37
+ return { type, ...base };
38
+ }
39
+ if (type === "document.page.completed") {
40
+ return {
41
+ type,
42
+ ...base,
43
+ pageNumber: integer(item.pageNumber, "DocumentEvent.pageNumber", 1),
44
+ completedPages: integer(
45
+ item.completedPages,
46
+ "DocumentEvent.completedPages",
47
+ 1,
48
+ ),
49
+ ...(item.totalPages === undefined
50
+ ? {}
51
+ : {
52
+ totalPages: integer(item.totalPages, "DocumentEvent.totalPages", 1),
53
+ }),
54
+ };
55
+ }
56
+ if (type === "run.failed") {
57
+ return { type, ...base, error: parseError(item.error) };
58
+ }
59
+ invalid("DocumentEvent.type");
60
+ }
61
+
62
+ export function parseCreatedRun(value: unknown): {
63
+ runId: string;
64
+ status: DocumentRunStatus;
65
+ eventsUrl: string;
66
+ } {
67
+ const item = record(value, "DocumentRun");
68
+ const status = text(item.status, "DocumentRun.status");
69
+ if (
70
+ !["accepted", "running", "completed", "failed", "cancelled"].includes(
71
+ status,
72
+ )
73
+ ) {
74
+ invalid("DocumentRun.status");
75
+ }
76
+ return {
77
+ runId: text(item.runId, "DocumentRun.runId"),
78
+ status: status as DocumentRunStatus,
79
+ eventsUrl: text(item.eventsUrl, "DocumentRun.eventsUrl"),
80
+ };
81
+ }
82
+
83
+ function parseError(value: unknown): DocumentErrorPayload {
84
+ const item = record(value, "DocumentError");
85
+ return {
86
+ code: text(item.code, "DocumentError.code") as DocumentErrorPayload["code"],
87
+ message: text(item.message, "DocumentError.message"),
88
+ requestId: text(item.requestId, "DocumentError.requestId"),
89
+ retryable: item.retryable === true,
90
+ };
91
+ }
92
+
93
+ export function record(value: unknown, path: string) {
94
+ if (!value || typeof value !== "object" || Array.isArray(value))
95
+ invalid(path);
96
+ return value as Record<string, unknown>;
97
+ }
98
+
99
+ export function text(value: unknown, path: string) {
100
+ if (typeof value !== "string" || !value.trim()) invalid(path);
101
+ return value;
102
+ }
103
+
104
+ function integer(value: unknown, path: string, minimum: number) {
105
+ if (!Number.isSafeInteger(value) || Number(value) < minimum) invalid(path);
106
+ return Number(value);
107
+ }
108
+
109
+ function invalid(path: string): never {
110
+ throw new DocumentClientError(
111
+ "INVALID_RESPONSE",
112
+ `Document Runtime 响应字段无效:${path}`,
113
+ false,
114
+ );
115
+ }
@@ -0,0 +1,77 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createEmbeddingClient } from "./client.js";
3
+ import { EmbeddingClientError } from "./error.js";
4
+
5
+ describe("Embedding Client", () => {
6
+ test("提交稳定批次并验证固定部署元数据", async () => {
7
+ let body: unknown;
8
+ const client = createEmbeddingClient({
9
+ baseUrl: "https://windy.example",
10
+ fetch: async (_input, init) => {
11
+ body = JSON.parse(String(init?.body));
12
+ return Response.json({
13
+ batchId: "embed_1",
14
+ items: [
15
+ {
16
+ id: "text-1",
17
+ vector: [1, 0],
18
+ inputSha256: "a".repeat(64),
19
+ },
20
+ ],
21
+ metadata: {
22
+ deploymentAlias: "intranet-text-v1",
23
+ model: "local-model",
24
+ version: "2026-07",
25
+ dimensions: 2,
26
+ normalized: true,
27
+ },
28
+ });
29
+ },
30
+ });
31
+
32
+ const result = await client.embed(
33
+ { key: "analysis.embedding.text" },
34
+ { items: [{ id: "text-1", text: "测试" }] },
35
+ { idempotencyKey: "batch-1" },
36
+ );
37
+
38
+ expect(body).toEqual({
39
+ operationKey: "analysis.embedding.text",
40
+ items: [{ id: "text-1", text: "测试" }],
41
+ });
42
+ expect(result.metadata.deploymentAlias).toBe("intranet-text-v1");
43
+ expect(result.items[0]?.vector).toEqual([1, 0]);
44
+ });
45
+
46
+ test("拒绝响应顺序和归一化元数据漂移", async () => {
47
+ const response = {
48
+ batchId: "embed_1",
49
+ items: [
50
+ {
51
+ id: "other",
52
+ vector: [1],
53
+ inputSha256: "a".repeat(64),
54
+ },
55
+ ],
56
+ metadata: {
57
+ deploymentAlias: "local",
58
+ model: "model",
59
+ version: "v1",
60
+ dimensions: 1,
61
+ normalized: "true",
62
+ },
63
+ };
64
+ const client = createEmbeddingClient({
65
+ baseUrl: "https://windy.example",
66
+ fetch: async () => Response.json(response),
67
+ });
68
+
69
+ await expect(
70
+ client.embed(
71
+ { key: "analysis.embedding.text" },
72
+ { items: [{ id: "text-1", text: "测试" }] },
73
+ { idempotencyKey: "batch-1" },
74
+ ),
75
+ ).rejects.toBeInstanceOf(EmbeddingClientError);
76
+ });
77
+ });