@kindgi/api 0.0.0-bootstrap.0 → 0.1.1

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 (505) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +216 -1
  3. package/dist/adapter-binding.d.ts +177 -0
  4. package/dist/adapter-binding.d.ts.map +1 -0
  5. package/dist/adapter-binding.js +12 -0
  6. package/dist/adapter-binding.js.map +1 -0
  7. package/dist/agent-binding.d.ts +207 -0
  8. package/dist/agent-binding.d.ts.map +1 -0
  9. package/dist/agent-binding.js +4 -0
  10. package/dist/agent-binding.js.map +1 -0
  11. package/dist/app.d.ts +770 -0
  12. package/dist/app.d.ts.map +1 -0
  13. package/dist/app.js +397 -0
  14. package/dist/app.js.map +1 -0
  15. package/dist/capability-binding.d.ts +76 -0
  16. package/dist/capability-binding.d.ts.map +1 -0
  17. package/dist/capability-binding.js +4 -0
  18. package/dist/capability-binding.js.map +1 -0
  19. package/dist/cost-binding.d.ts +175 -0
  20. package/dist/cost-binding.d.ts.map +1 -0
  21. package/dist/cost-binding.js +13 -0
  22. package/dist/cost-binding.js.map +1 -0
  23. package/dist/deployment-binding.d.ts +201 -0
  24. package/dist/deployment-binding.d.ts.map +1 -0
  25. package/dist/deployment-binding.js +4 -0
  26. package/dist/deployment-binding.js.map +1 -0
  27. package/dist/env-binding.d.ts +132 -0
  28. package/dist/env-binding.d.ts.map +1 -0
  29. package/dist/env-binding.js +4 -0
  30. package/dist/env-binding.js.map +1 -0
  31. package/dist/errors.d.ts +37 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +268 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/eval-run-binding.d.ts +196 -0
  36. package/dist/eval-run-binding.d.ts.map +1 -0
  37. package/dist/eval-run-binding.js +16 -0
  38. package/dist/eval-run-binding.js.map +1 -0
  39. package/dist/eval-run-dispatcher.d.ts +131 -0
  40. package/dist/eval-run-dispatcher.d.ts.map +1 -0
  41. package/dist/eval-run-dispatcher.js +368 -0
  42. package/dist/eval-run-dispatcher.js.map +1 -0
  43. package/dist/eval-suite-binding.d.ts +227 -0
  44. package/dist/eval-suite-binding.d.ts.map +1 -0
  45. package/dist/eval-suite-binding.js +24 -0
  46. package/dist/eval-suite-binding.js.map +1 -0
  47. package/dist/event-bus-binding.d.ts +107 -0
  48. package/dist/event-bus-binding.d.ts.map +1 -0
  49. package/dist/event-bus-binding.js +5 -0
  50. package/dist/event-bus-binding.js.map +1 -0
  51. package/dist/flow-binding.d.ts +194 -0
  52. package/dist/flow-binding.d.ts.map +1 -0
  53. package/dist/flow-binding.js +4 -0
  54. package/dist/flow-binding.js.map +1 -0
  55. package/dist/guardrail-binding.d.ts +138 -0
  56. package/dist/guardrail-binding.d.ts.map +1 -0
  57. package/dist/guardrail-binding.js +4 -0
  58. package/dist/guardrail-binding.js.map +1 -0
  59. package/dist/handler-binding.d.ts +126 -0
  60. package/dist/handler-binding.d.ts.map +1 -0
  61. package/dist/handler-binding.js +4 -0
  62. package/dist/handler-binding.js.map +1 -0
  63. package/dist/hitl-binding.d.ts +115 -0
  64. package/dist/hitl-binding.d.ts.map +1 -0
  65. package/dist/hitl-binding.js +4 -0
  66. package/dist/hitl-binding.js.map +1 -0
  67. package/dist/identity-directory-binding.d.ts +116 -0
  68. package/dist/identity-directory-binding.d.ts.map +1 -0
  69. package/dist/identity-directory-binding.js +4 -0
  70. package/dist/identity-directory-binding.js.map +1 -0
  71. package/dist/identity-provider-binding.d.ts +184 -0
  72. package/dist/identity-provider-binding.d.ts.map +1 -0
  73. package/dist/identity-provider-binding.js +4 -0
  74. package/dist/identity-provider-binding.js.map +1 -0
  75. package/dist/image-registry-binding.d.ts +165 -0
  76. package/dist/image-registry-binding.d.ts.map +1 -0
  77. package/dist/image-registry-binding.js +4 -0
  78. package/dist/image-registry-binding.js.map +1 -0
  79. package/dist/index.d.ts +72 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +26 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/key-management-binding.d.ts +101 -0
  84. package/dist/key-management-binding.d.ts.map +1 -0
  85. package/dist/key-management-binding.js +4 -0
  86. package/dist/key-management-binding.js.map +1 -0
  87. package/dist/mcp-endpoint-binding.d.ts +312 -0
  88. package/dist/mcp-endpoint-binding.d.ts.map +1 -0
  89. package/dist/mcp-endpoint-binding.js +12 -0
  90. package/dist/mcp-endpoint-binding.js.map +1 -0
  91. package/dist/memory-binding.d.ts +209 -0
  92. package/dist/memory-binding.d.ts.map +1 -0
  93. package/dist/memory-binding.js +4 -0
  94. package/dist/memory-binding.js.map +1 -0
  95. package/dist/middleware/auth.d.ts +168 -0
  96. package/dist/middleware/auth.d.ts.map +1 -0
  97. package/dist/middleware/auth.js +247 -0
  98. package/dist/middleware/auth.js.map +1 -0
  99. package/dist/middleware/authorize.d.ts +26 -0
  100. package/dist/middleware/authorize.d.ts.map +1 -0
  101. package/dist/middleware/authorize.js +65 -0
  102. package/dist/middleware/authorize.js.map +1 -0
  103. package/dist/middleware/error-mapper.d.ts +20 -0
  104. package/dist/middleware/error-mapper.d.ts.map +1 -0
  105. package/dist/middleware/error-mapper.js +34 -0
  106. package/dist/middleware/error-mapper.js.map +1 -0
  107. package/dist/middleware/idempotency.d.ts +43 -0
  108. package/dist/middleware/idempotency.d.ts.map +1 -0
  109. package/dist/middleware/idempotency.js +118 -0
  110. package/dist/middleware/idempotency.js.map +1 -0
  111. package/dist/middleware/principal.d.ts +4 -0
  112. package/dist/middleware/principal.d.ts.map +1 -0
  113. package/dist/middleware/principal.js +42 -0
  114. package/dist/middleware/principal.js.map +1 -0
  115. package/dist/middleware/public-run-routes.d.ts +16 -0
  116. package/dist/middleware/public-run-routes.d.ts.map +1 -0
  117. package/dist/middleware/public-run-routes.js +43 -0
  118. package/dist/middleware/public-run-routes.js.map +1 -0
  119. package/dist/middleware/request-id.d.ts +14 -0
  120. package/dist/middleware/request-id.d.ts.map +1 -0
  121. package/dist/middleware/request-id.js +24 -0
  122. package/dist/middleware/request-id.js.map +1 -0
  123. package/dist/middleware/sigv4-verify.d.ts +64 -0
  124. package/dist/middleware/sigv4-verify.d.ts.map +1 -0
  125. package/dist/middleware/sigv4-verify.js +286 -0
  126. package/dist/middleware/sigv4-verify.js.map +1 -0
  127. package/dist/middleware/sigv4.d.ts +25 -0
  128. package/dist/middleware/sigv4.d.ts.map +1 -0
  129. package/dist/middleware/sigv4.js +120 -0
  130. package/dist/middleware/sigv4.js.map +1 -0
  131. package/dist/openapi/generate.d.ts +15 -0
  132. package/dist/openapi/generate.d.ts.map +1 -0
  133. package/dist/openapi/generate.js +241 -0
  134. package/dist/openapi/generate.js.map +1 -0
  135. package/dist/openapi/operations.d.ts +70 -0
  136. package/dist/openapi/operations.d.ts.map +1 -0
  137. package/dist/openapi/operations.js +5050 -0
  138. package/dist/openapi/operations.js.map +1 -0
  139. package/dist/openapi/schemas.d.ts +590 -0
  140. package/dist/openapi/schemas.d.ts.map +1 -0
  141. package/dist/openapi/schemas.js +5820 -0
  142. package/dist/openapi/schemas.js.map +1 -0
  143. package/dist/provenance-binding.d.ts +116 -0
  144. package/dist/provenance-binding.d.ts.map +1 -0
  145. package/dist/provenance-binding.js +31 -0
  146. package/dist/provenance-binding.js.map +1 -0
  147. package/dist/provider-binding.d.ts +174 -0
  148. package/dist/provider-binding.d.ts.map +1 -0
  149. package/dist/provider-binding.js +4 -0
  150. package/dist/provider-binding.js.map +1 -0
  151. package/dist/public-run-token.d.ts +97 -0
  152. package/dist/public-run-token.d.ts.map +1 -0
  153. package/dist/public-run-token.js +167 -0
  154. package/dist/public-run-token.js.map +1 -0
  155. package/dist/retention-binding.d.ts +69 -0
  156. package/dist/retention-binding.d.ts.map +1 -0
  157. package/dist/retention-binding.js +4 -0
  158. package/dist/retention-binding.js.map +1 -0
  159. package/dist/reviewer-binding.d.ts +84 -0
  160. package/dist/reviewer-binding.d.ts.map +1 -0
  161. package/dist/reviewer-binding.js +4 -0
  162. package/dist/reviewer-binding.js.map +1 -0
  163. package/dist/rotation-status-store.d.ts +96 -0
  164. package/dist/rotation-status-store.d.ts.map +1 -0
  165. package/dist/rotation-status-store.js +106 -0
  166. package/dist/rotation-status-store.js.map +1 -0
  167. package/dist/routes/adapters.d.ts +29 -0
  168. package/dist/routes/adapters.d.ts.map +1 -0
  169. package/dist/routes/adapters.js +230 -0
  170. package/dist/routes/adapters.js.map +1 -0
  171. package/dist/routes/agents.d.ts +17 -0
  172. package/dist/routes/agents.d.ts.map +1 -0
  173. package/dist/routes/agents.js +342 -0
  174. package/dist/routes/agents.js.map +1 -0
  175. package/dist/routes/approvals.d.ts +37 -0
  176. package/dist/routes/approvals.d.ts.map +1 -0
  177. package/dist/routes/approvals.js +551 -0
  178. package/dist/routes/approvals.js.map +1 -0
  179. package/dist/routes/artifacts.d.ts +25 -0
  180. package/dist/routes/artifacts.d.ts.map +1 -0
  181. package/dist/routes/artifacts.js +265 -0
  182. package/dist/routes/artifacts.js.map +1 -0
  183. package/dist/routes/audit.d.ts +6 -0
  184. package/dist/routes/audit.d.ts.map +1 -0
  185. package/dist/routes/audit.js +162 -0
  186. package/dist/routes/audit.js.map +1 -0
  187. package/dist/routes/auth.d.ts +47 -0
  188. package/dist/routes/auth.d.ts.map +1 -0
  189. package/dist/routes/auth.js +630 -0
  190. package/dist/routes/auth.js.map +1 -0
  191. package/dist/routes/capabilities.d.ts +14 -0
  192. package/dist/routes/capabilities.d.ts.map +1 -0
  193. package/dist/routes/capabilities.js +62 -0
  194. package/dist/routes/capabilities.js.map +1 -0
  195. package/dist/routes/compliance.d.ts +37 -0
  196. package/dist/routes/compliance.d.ts.map +1 -0
  197. package/dist/routes/compliance.js +321 -0
  198. package/dist/routes/compliance.js.map +1 -0
  199. package/dist/routes/conversations.d.ts +13 -0
  200. package/dist/routes/conversations.d.ts.map +1 -0
  201. package/dist/routes/conversations.js +295 -0
  202. package/dist/routes/conversations.js.map +1 -0
  203. package/dist/routes/cost.d.ts +24 -0
  204. package/dist/routes/cost.d.ts.map +1 -0
  205. package/dist/routes/cost.js +243 -0
  206. package/dist/routes/cost.js.map +1 -0
  207. package/dist/routes/deployments.d.ts +122 -0
  208. package/dist/routes/deployments.d.ts.map +1 -0
  209. package/dist/routes/deployments.js +1036 -0
  210. package/dist/routes/deployments.js.map +1 -0
  211. package/dist/routes/env.d.ts +62 -0
  212. package/dist/routes/env.d.ts.map +1 -0
  213. package/dist/routes/env.js +311 -0
  214. package/dist/routes/env.js.map +1 -0
  215. package/dist/routes/eval-runs.d.ts +28 -0
  216. package/dist/routes/eval-runs.d.ts.map +1 -0
  217. package/dist/routes/eval-runs.js +479 -0
  218. package/dist/routes/eval-runs.js.map +1 -0
  219. package/dist/routes/eval-suites.d.ts +24 -0
  220. package/dist/routes/eval-suites.d.ts.map +1 -0
  221. package/dist/routes/eval-suites.js +381 -0
  222. package/dist/routes/eval-suites.js.map +1 -0
  223. package/dist/routes/event-triggers.d.ts +12 -0
  224. package/dist/routes/event-triggers.d.ts.map +1 -0
  225. package/dist/routes/event-triggers.js +218 -0
  226. package/dist/routes/event-triggers.js.map +1 -0
  227. package/dist/routes/flows.d.ts +23 -0
  228. package/dist/routes/flows.d.ts.map +1 -0
  229. package/dist/routes/flows.js +344 -0
  230. package/dist/routes/flows.js.map +1 -0
  231. package/dist/routes/guardrails.d.ts +33 -0
  232. package/dist/routes/guardrails.d.ts.map +1 -0
  233. package/dist/routes/guardrails.js +236 -0
  234. package/dist/routes/guardrails.js.map +1 -0
  235. package/dist/routes/identity.d.ts +39 -0
  236. package/dist/routes/identity.d.ts.map +1 -0
  237. package/dist/routes/identity.js +138 -0
  238. package/dist/routes/identity.js.map +1 -0
  239. package/dist/routes/mcp.d.ts +23 -0
  240. package/dist/routes/mcp.d.ts.map +1 -0
  241. package/dist/routes/mcp.js +727 -0
  242. package/dist/routes/mcp.js.map +1 -0
  243. package/dist/routes/memory.d.ts +16 -0
  244. package/dist/routes/memory.d.ts.map +1 -0
  245. package/dist/routes/memory.js +353 -0
  246. package/dist/routes/memory.js.map +1 -0
  247. package/dist/routes/observations.d.ts +19 -0
  248. package/dist/routes/observations.d.ts.map +1 -0
  249. package/dist/routes/observations.js +135 -0
  250. package/dist/routes/observations.js.map +1 -0
  251. package/dist/routes/orgs.d.ts +24 -0
  252. package/dist/routes/orgs.d.ts.map +1 -0
  253. package/dist/routes/orgs.js +208 -0
  254. package/dist/routes/orgs.js.map +1 -0
  255. package/dist/routes/pagination.d.ts +18 -0
  256. package/dist/routes/pagination.d.ts.map +1 -0
  257. package/dist/routes/pagination.js +35 -0
  258. package/dist/routes/pagination.js.map +1 -0
  259. package/dist/routes/policies.d.ts +23 -0
  260. package/dist/routes/policies.d.ts.map +1 -0
  261. package/dist/routes/policies.js +314 -0
  262. package/dist/routes/policies.js.map +1 -0
  263. package/dist/routes/projects.d.ts +45 -0
  264. package/dist/routes/projects.d.ts.map +1 -0
  265. package/dist/routes/projects.js +485 -0
  266. package/dist/routes/projects.js.map +1 -0
  267. package/dist/routes/proposals.d.ts +24 -0
  268. package/dist/routes/proposals.d.ts.map +1 -0
  269. package/dist/routes/proposals.js +581 -0
  270. package/dist/routes/proposals.js.map +1 -0
  271. package/dist/routes/provenance.d.ts +34 -0
  272. package/dist/routes/provenance.d.ts.map +1 -0
  273. package/dist/routes/provenance.js +274 -0
  274. package/dist/routes/provenance.js.map +1 -0
  275. package/dist/routes/providers.d.ts +32 -0
  276. package/dist/routes/providers.d.ts.map +1 -0
  277. package/dist/routes/providers.js +490 -0
  278. package/dist/routes/providers.js.map +1 -0
  279. package/dist/routes/public-run-tokens.d.ts +17 -0
  280. package/dist/routes/public-run-tokens.d.ts.map +1 -0
  281. package/dist/routes/public-run-tokens.js +69 -0
  282. package/dist/routes/public-run-tokens.js.map +1 -0
  283. package/dist/routes/retention.d.ts +19 -0
  284. package/dist/routes/retention.d.ts.map +1 -0
  285. package/dist/routes/retention.js +152 -0
  286. package/dist/routes/retention.js.map +1 -0
  287. package/dist/routes/reviewers.d.ts +5 -0
  288. package/dist/routes/reviewers.d.ts.map +1 -0
  289. package/dist/routes/reviewers.js +161 -0
  290. package/dist/routes/reviewers.js.map +1 -0
  291. package/dist/routes/runs.d.ts +34 -0
  292. package/dist/routes/runs.d.ts.map +1 -0
  293. package/dist/routes/runs.js +705 -0
  294. package/dist/routes/runs.js.map +1 -0
  295. package/dist/routes/s3.d.ts +31 -0
  296. package/dist/routes/s3.d.ts.map +1 -0
  297. package/dist/routes/s3.js +433 -0
  298. package/dist/routes/s3.js.map +1 -0
  299. package/dist/routes/schedules.d.ts +20 -0
  300. package/dist/routes/schedules.d.ts.map +1 -0
  301. package/dist/routes/schedules.js +234 -0
  302. package/dist/routes/schedules.js.map +1 -0
  303. package/dist/routes/scope-params.d.ts +57 -0
  304. package/dist/routes/scope-params.d.ts.map +1 -0
  305. package/dist/routes/scope-params.js +105 -0
  306. package/dist/routes/scope-params.js.map +1 -0
  307. package/dist/routes/secret-ref.d.ts +20 -0
  308. package/dist/routes/secret-ref.d.ts.map +1 -0
  309. package/dist/routes/secret-ref.js +42 -0
  310. package/dist/routes/secret-ref.js.map +1 -0
  311. package/dist/routes/secrets.d.ts +53 -0
  312. package/dist/routes/secrets.d.ts.map +1 -0
  313. package/dist/routes/secrets.js +715 -0
  314. package/dist/routes/secrets.js.map +1 -0
  315. package/dist/routes/signing-keys.d.ts +23 -0
  316. package/dist/routes/signing-keys.d.ts.map +1 -0
  317. package/dist/routes/signing-keys.js +212 -0
  318. package/dist/routes/signing-keys.js.map +1 -0
  319. package/dist/routes/sse.d.ts +58 -0
  320. package/dist/routes/sse.d.ts.map +1 -0
  321. package/dist/routes/sse.js +92 -0
  322. package/dist/routes/sse.js.map +1 -0
  323. package/dist/routes/teams.d.ts +30 -0
  324. package/dist/routes/teams.d.ts.map +1 -0
  325. package/dist/routes/teams.js +443 -0
  326. package/dist/routes/teams.js.map +1 -0
  327. package/dist/routes/tenant.d.ts +57 -0
  328. package/dist/routes/tenant.d.ts.map +1 -0
  329. package/dist/routes/tenant.js +352 -0
  330. package/dist/routes/tenant.js.map +1 -0
  331. package/dist/routes/tokens.d.ts +14 -0
  332. package/dist/routes/tokens.d.ts.map +1 -0
  333. package/dist/routes/tokens.js +231 -0
  334. package/dist/routes/tokens.js.map +1 -0
  335. package/dist/routes/tools.d.ts +33 -0
  336. package/dist/routes/tools.d.ts.map +1 -0
  337. package/dist/routes/tools.js +380 -0
  338. package/dist/routes/tools.js.map +1 -0
  339. package/dist/routes/webhook-endpoints.d.ts +10 -0
  340. package/dist/routes/webhook-endpoints.d.ts.map +1 -0
  341. package/dist/routes/webhook-endpoints.js +407 -0
  342. package/dist/routes/webhook-endpoints.js.map +1 -0
  343. package/dist/routes/webhooks.d.ts +21 -0
  344. package/dist/routes/webhooks.d.ts.map +1 -0
  345. package/dist/routes/webhooks.js +228 -0
  346. package/dist/routes/webhooks.js.map +1 -0
  347. package/dist/s3-credential-binding.d.ts +54 -0
  348. package/dist/s3-credential-binding.d.ts.map +1 -0
  349. package/dist/s3-credential-binding.js +4 -0
  350. package/dist/s3-credential-binding.js.map +1 -0
  351. package/dist/secrets-binding.d.ts +281 -0
  352. package/dist/secrets-binding.d.ts.map +1 -0
  353. package/dist/secrets-binding.js +10 -0
  354. package/dist/secrets-binding.js.map +1 -0
  355. package/dist/secrets-provider-binding.d.ts +164 -0
  356. package/dist/secrets-provider-binding.d.ts.map +1 -0
  357. package/dist/secrets-provider-binding.js +4 -0
  358. package/dist/secrets-provider-binding.js.map +1 -0
  359. package/dist/session-store-binding.d.ts +148 -0
  360. package/dist/session-store-binding.d.ts.map +1 -0
  361. package/dist/session-store-binding.js +4 -0
  362. package/dist/session-store-binding.js.map +1 -0
  363. package/dist/signing-key-binding.d.ts +210 -0
  364. package/dist/signing-key-binding.d.ts.map +1 -0
  365. package/dist/signing-key-binding.js +4 -0
  366. package/dist/signing-key-binding.js.map +1 -0
  367. package/dist/state-store-binding.d.ts +53 -0
  368. package/dist/state-store-binding.d.ts.map +1 -0
  369. package/dist/state-store-binding.js +38 -0
  370. package/dist/state-store-binding.js.map +1 -0
  371. package/dist/supervisor-binding.d.ts +526 -0
  372. package/dist/supervisor-binding.d.ts.map +1 -0
  373. package/dist/supervisor-binding.js +4 -0
  374. package/dist/supervisor-binding.js.map +1 -0
  375. package/dist/tenant-host-access.d.ts +41 -0
  376. package/dist/tenant-host-access.d.ts.map +1 -0
  377. package/dist/tenant-host-access.js +42 -0
  378. package/dist/tenant-host-access.js.map +1 -0
  379. package/dist/token-admin.d.ts +99 -0
  380. package/dist/token-admin.d.ts.map +1 -0
  381. package/dist/token-admin.js +4 -0
  382. package/dist/token-admin.js.map +1 -0
  383. package/dist/tool-binding.d.ts +247 -0
  384. package/dist/tool-binding.d.ts.map +1 -0
  385. package/dist/tool-binding.js +4 -0
  386. package/dist/tool-binding.js.map +1 -0
  387. package/dist/trigger-binding.d.ts +13 -0
  388. package/dist/trigger-binding.d.ts.map +1 -0
  389. package/dist/trigger-binding.js +4 -0
  390. package/dist/trigger-binding.js.map +1 -0
  391. package/dist/types.d.ts +71 -0
  392. package/dist/types.d.ts.map +1 -0
  393. package/dist/types.js +4 -0
  394. package/dist/types.js.map +1 -0
  395. package/dist/webhook-endpoint-binding.d.ts +230 -0
  396. package/dist/webhook-endpoint-binding.d.ts.map +1 -0
  397. package/dist/webhook-endpoint-binding.js +12 -0
  398. package/dist/webhook-endpoint-binding.js.map +1 -0
  399. package/dist/xml.d.ts +79 -0
  400. package/dist/xml.d.ts.map +1 -0
  401. package/dist/xml.js +115 -0
  402. package/dist/xml.js.map +1 -0
  403. package/openapi.json +29649 -0
  404. package/package.json +76 -4
  405. package/scripts/emit-openapi.mjs +44 -0
  406. package/src/adapter-binding.ts +204 -0
  407. package/src/agent-binding.ts +231 -0
  408. package/src/app.ts +1286 -0
  409. package/src/capability-binding.ts +83 -0
  410. package/src/cost-binding.ts +213 -0
  411. package/src/deployment-binding.ts +225 -0
  412. package/src/env-binding.ts +140 -0
  413. package/src/errors.ts +295 -0
  414. package/src/eval-run-binding.ts +209 -0
  415. package/src/eval-run-dispatcher.ts +497 -0
  416. package/src/eval-suite-binding.ts +257 -0
  417. package/src/event-bus-binding.ts +127 -0
  418. package/src/flow-binding.ts +213 -0
  419. package/src/guardrail-binding.ts +151 -0
  420. package/src/handler-binding.ts +132 -0
  421. package/src/hitl-binding.ts +172 -0
  422. package/src/identity-directory-binding.ts +128 -0
  423. package/src/identity-provider-binding.ts +203 -0
  424. package/src/image-registry-binding.ts +193 -0
  425. package/src/index.ts +606 -0
  426. package/src/key-management-binding.ts +91 -0
  427. package/src/mcp-endpoint-binding.ts +311 -0
  428. package/src/memory-binding.ts +239 -0
  429. package/src/middleware/auth.ts +437 -0
  430. package/src/middleware/authorize.ts +125 -0
  431. package/src/middleware/error-mapper.ts +40 -0
  432. package/src/middleware/idempotency.ts +160 -0
  433. package/src/middleware/principal.ts +64 -0
  434. package/src/middleware/public-run-routes.ts +50 -0
  435. package/src/middleware/request-id.ts +28 -0
  436. package/src/middleware/sigv4-verify.ts +374 -0
  437. package/src/middleware/sigv4.ts +165 -0
  438. package/src/openapi/generate.ts +307 -0
  439. package/src/openapi/operations.ts +5444 -0
  440. package/src/openapi/schemas.ts +6363 -0
  441. package/src/provenance-binding.ts +169 -0
  442. package/src/provider-binding.ts +196 -0
  443. package/src/public-run-token.ts +263 -0
  444. package/src/retention-binding.ts +78 -0
  445. package/src/reviewer-binding.ts +99 -0
  446. package/src/rotation-status-store.ts +209 -0
  447. package/src/routes/adapters.ts +319 -0
  448. package/src/routes/agents.ts +424 -0
  449. package/src/routes/approvals.ts +759 -0
  450. package/src/routes/artifacts.ts +317 -0
  451. package/src/routes/audit.ts +219 -0
  452. package/src/routes/auth.ts +806 -0
  453. package/src/routes/capabilities.ts +79 -0
  454. package/src/routes/compliance.ts +482 -0
  455. package/src/routes/conversations.ts +369 -0
  456. package/src/routes/cost.ts +327 -0
  457. package/src/routes/deployments.ts +1493 -0
  458. package/src/routes/env.ts +429 -0
  459. package/src/routes/eval-runs.ts +631 -0
  460. package/src/routes/eval-suites.ts +490 -0
  461. package/src/routes/event-triggers.ts +283 -0
  462. package/src/routes/flows.ts +423 -0
  463. package/src/routes/guardrails.ts +320 -0
  464. package/src/routes/identity.ts +203 -0
  465. package/src/routes/mcp.ts +940 -0
  466. package/src/routes/memory.ts +430 -0
  467. package/src/routes/observations.ts +175 -0
  468. package/src/routes/orgs.ts +273 -0
  469. package/src/routes/pagination.ts +51 -0
  470. package/src/routes/policies.ts +413 -0
  471. package/src/routes/projects.ts +634 -0
  472. package/src/routes/proposals.ts +719 -0
  473. package/src/routes/provenance.ts +406 -0
  474. package/src/routes/providers.ts +624 -0
  475. package/src/routes/public-run-tokens.ts +104 -0
  476. package/src/routes/retention.ts +193 -0
  477. package/src/routes/reviewers.ts +209 -0
  478. package/src/routes/runs.ts +892 -0
  479. package/src/routes/s3.ts +519 -0
  480. package/src/routes/schedules.ts +300 -0
  481. package/src/routes/scope-params.ts +133 -0
  482. package/src/routes/secret-ref.ts +52 -0
  483. package/src/routes/secrets.ts +975 -0
  484. package/src/routes/signing-keys.ts +259 -0
  485. package/src/routes/sse.ts +148 -0
  486. package/src/routes/teams.ts +584 -0
  487. package/src/routes/tenant.ts +530 -0
  488. package/src/routes/tokens.ts +285 -0
  489. package/src/routes/tools.ts +491 -0
  490. package/src/routes/webhook-endpoints.ts +534 -0
  491. package/src/routes/webhooks.ts +296 -0
  492. package/src/s3-credential-binding.ts +58 -0
  493. package/src/secrets-binding.ts +308 -0
  494. package/src/secrets-provider-binding.ts +167 -0
  495. package/src/session-store-binding.ts +163 -0
  496. package/src/signing-key-binding.ts +235 -0
  497. package/src/state-store-binding.ts +84 -0
  498. package/src/supervisor-binding.ts +554 -0
  499. package/src/tenant-host-access.ts +62 -0
  500. package/src/token-admin.ts +104 -0
  501. package/src/tool-binding.ts +274 -0
  502. package/src/trigger-binding.ts +38 -0
  503. package/src/types.ts +74 -0
  504. package/src/webhook-endpoint-binding.ts +246 -0
  505. package/src/xml.ts +196 -0
@@ -0,0 +1,634 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { Hono } from 'hono';
5
+
6
+ import { type Principal, ref } from '@kindgi/authz';
7
+ import type { TenantHierarchyBinding } from '@kindgi/platform';
8
+ import type {
9
+ Project,
10
+ ProjectBinding,
11
+ ProjectMembership,
12
+ ProjectMembershipBinding,
13
+ ProjectPatch,
14
+ ProjectRole,
15
+ ProjectSpec,
16
+ } from '@kindgi/platform';
17
+ import type { Cursor, OrgId, ProjectId, TenantId, UserId } from '@kindgi/types';
18
+
19
+ import { statusFor, toWireError } from '../errors.js';
20
+ import type { Authorizer } from '../middleware/authorize.js';
21
+ import type { AppEnv } from '../types.js';
22
+ import { clampLimit } from './pagination.js';
23
+
24
+ /**
25
+ * Projects resource routes — part of the multi-tenant hierarchy.
26
+ *
27
+ * Ten endpoints on `/v1/projects`:
28
+ * - `GET /` — cursor-paginated list;
29
+ * `?limit=` / `?cursor=` / `?orgId=` /
30
+ * `?nameContains=`.
31
+ * - `POST /` — create; body `{ name, slug,
32
+ * orgId?, description?, isDefault? }`;
33
+ * 201 `{ id }`.
34
+ * - `GET /default` — returns the tenant's Default
35
+ * project or 404 `project-not-found`.
36
+ * MUST be mounted BEFORE `/:projectId`.
37
+ * - `GET /:projectId` — get; 200 or 404 `project-not-found`.
38
+ * - `PATCH /:projectId` — partial update; 204 or 404.
39
+ * - `DELETE /:projectId` — 204 idempotent.
40
+ * - `GET /:projectId/memberships` — list; cursor-paginated.
41
+ * - `POST /:projectId/memberships` — add member; body `{ userId, role }`;
42
+ * 201. Idempotent per binding contract.
43
+ * - `DELETE /:projectId/memberships/:userId`
44
+ * — 204 idempotent.
45
+ * - `PATCH /:projectId/memberships/:userId`
46
+ * — updateRole via `{ role }`; 204 or
47
+ * 404 `project-membership-not-found`.
48
+ */
49
+ export function projectsRouter(
50
+ binding: ProjectBinding,
51
+ membershipBinding: ProjectMembershipBinding,
52
+ /**
53
+ * Tenant-hierarchy binding. When `authorizer` is also set, POST /
54
+ * routes through `tenantHierarchy.createProject` — writes FGA
55
+ * parent/owner tuples atomically with the insert. Without an
56
+ * authorizer, POST / falls back to `binding.create()` (no tuples).
57
+ */
58
+ tenantHierarchy: TenantHierarchyBinding,
59
+ /**
60
+ * Optional authorizer. When provided, routes are gated by
61
+ * `authorize()` before the handler runs, and the list is filtered to
62
+ * projects the caller can read.
63
+ */
64
+ authorizer?: Authorizer,
65
+ ): Hono<AppEnv> {
66
+ const r = new Hono<AppEnv>();
67
+
68
+ // ---------- GET /default (literal segment; must precede /:projectId) ----------
69
+ r.get('/default', async (c) => {
70
+ const requestId = c.get('requestId');
71
+ const tenantId = c.get('tenantId') as TenantId;
72
+ const proj = await binding.getDefault(tenantId);
73
+ if (proj === undefined) {
74
+ c.status(statusFor('project-not-found') as never);
75
+ return c.json(
76
+ toWireError(
77
+ {
78
+ code: 'project-not-found',
79
+ message: 'Tenant has no Default project',
80
+ },
81
+ requestId,
82
+ ),
83
+ );
84
+ }
85
+ return c.json(serializeProject(proj));
86
+ });
87
+
88
+ // ---------- GET / (list) ----------
89
+ r.get('/', async (c) => {
90
+ const tenantId = c.get('tenantId') as TenantId;
91
+ const limit = clampLimit(c.req.query('limit'));
92
+ const cursorRaw = c.req.query('cursor');
93
+ const orgIdRaw = c.req.query('orgId');
94
+ const nameContainsRaw = c.req.query('nameContains');
95
+ const page = await binding.list(tenantId, {
96
+ limit,
97
+ ...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
98
+ ...(orgIdRaw !== undefined && orgIdRaw.length > 0 && { orgId: orgIdRaw as OrgId }),
99
+ ...(nameContainsRaw !== undefined &&
100
+ nameContainsRaw.length > 0 && { nameContains: nameContainsRaw }),
101
+ });
102
+ // Authorization list-filter — strip projects the caller can't read.
103
+ const visible =
104
+ authorizer !== undefined
105
+ ? await authorizer.filterByCan(c, 'read', page.items, (p) =>
106
+ ref('project', p.id as unknown as string),
107
+ )
108
+ : [...page.items];
109
+ return c.json({
110
+ data: visible.map(serializeProject),
111
+ hasMore: page.nextCursor !== undefined,
112
+ ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
113
+ });
114
+ });
115
+
116
+ // ---------- Authorization middleware ----------
117
+ // Full PEP surface for projects + memberships:
118
+ // POST / → admin on tenant
119
+ // GET /:projectId → read on project
120
+ // PATCH /:projectId → admin on project
121
+ // DELETE /:projectId → delete on project
122
+ // GET /:projectId/memberships[/:userId] → read on project
123
+ // other /:projectId/memberships[/:userId] → admin on project
124
+ // GET / → list-filter by can_read
125
+ if (authorizer !== undefined) {
126
+ r.use('/', async (c, next) => {
127
+ if (c.req.method !== 'POST') return next();
128
+ const mw = authorizer.authorize('admin', (ctx) =>
129
+ ref('tenant', ctx.get('tenantId') as unknown as string),
130
+ );
131
+ return mw(c, next);
132
+ });
133
+ r.use('/:projectId', async (c, next) => {
134
+ const method = c.req.method;
135
+ if (method !== 'GET' && method !== 'PATCH' && method !== 'DELETE') return next();
136
+ const projectId = c.req.param('projectId');
137
+ const action = method === 'GET' ? 'read' : method === 'DELETE' ? 'delete' : 'admin';
138
+ const mw = authorizer.authorize(action, () => ref('project', projectId));
139
+ return mw(c, next);
140
+ });
141
+ // Membership routes: reads require read, mutations admin on the project.
142
+ r.use('/:projectId/memberships', async (c, next) => {
143
+ const projectId = c.req.param('projectId');
144
+ const action = c.req.method === 'GET' ? 'read' : 'admin';
145
+ const mw = authorizer.authorize(action, () => ref('project', projectId));
146
+ return mw(c, next);
147
+ });
148
+ r.use('/:projectId/memberships/:userId', async (c, next) => {
149
+ const projectId = c.req.param('projectId');
150
+ const action = c.req.method === 'GET' ? 'read' : 'admin';
151
+ const mw = authorizer.authorize(action, () => ref('project', projectId));
152
+ return mw(c, next);
153
+ });
154
+ }
155
+ r.post('/', async (c) => {
156
+ const requestId = c.get('requestId');
157
+ const tenantId = c.get('tenantId') as TenantId;
158
+
159
+ let body: unknown;
160
+ try {
161
+ body = await c.req.json();
162
+ } catch {
163
+ c.status(statusFor('bad-input') as never);
164
+ return c.json(
165
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
166
+ );
167
+ }
168
+ if (body === null || typeof body !== 'object') {
169
+ c.status(statusFor('bad-input') as never);
170
+ return c.json(
171
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
172
+ );
173
+ }
174
+ const b = body as Record<string, unknown>;
175
+ if (typeof b.name !== 'string' || b.name.length === 0) {
176
+ c.status(statusFor('bad-input') as never);
177
+ return c.json(
178
+ toWireError(
179
+ { code: 'bad-input', message: '`name` is required and must be a non-empty string' },
180
+ requestId,
181
+ ),
182
+ );
183
+ }
184
+ if (typeof b.slug !== 'string' || b.slug.length === 0) {
185
+ c.status(statusFor('bad-input') as never);
186
+ return c.json(
187
+ toWireError(
188
+ { code: 'bad-input', message: '`slug` is required and must be a non-empty string' },
189
+ requestId,
190
+ ),
191
+ );
192
+ }
193
+ if (b.description !== undefined && typeof b.description !== 'string') {
194
+ c.status(statusFor('bad-input') as never);
195
+ return c.json(
196
+ toWireError(
197
+ { code: 'bad-input', message: '`description` must be a string when present' },
198
+ requestId,
199
+ ),
200
+ );
201
+ }
202
+ if (b.orgId !== undefined && typeof b.orgId !== 'string') {
203
+ c.status(statusFor('bad-input') as never);
204
+ return c.json(
205
+ toWireError(
206
+ { code: 'bad-input', message: '`orgId` must be a string when present' },
207
+ requestId,
208
+ ),
209
+ );
210
+ }
211
+ if (b.isDefault !== undefined && typeof b.isDefault !== 'boolean') {
212
+ c.status(statusFor('bad-input') as never);
213
+ return c.json(
214
+ toWireError(
215
+ { code: 'bad-input', message: '`isDefault` must be a boolean when present' },
216
+ requestId,
217
+ ),
218
+ );
219
+ }
220
+ const spec: ProjectSpec = {
221
+ name: b.name,
222
+ slug: b.slug,
223
+ ...(b.description !== undefined && { description: b.description as string }),
224
+ ...(b.orgId !== undefined && { orgId: b.orgId as unknown as OrgId }),
225
+ ...(b.isDefault !== undefined && { isDefault: b.isDefault as boolean }),
226
+ };
227
+ // With an authorizer, create through the tenant-hierarchy binding —
228
+ // it writes FGA tuples atomically. The tuples are what makes downstream
229
+ // authz (member add, tenant-admin cascade to project) actually work.
230
+ if (authorizer !== undefined) {
231
+ const principal = c.get('principal') as Principal | undefined;
232
+ const creatorUserId =
233
+ principal?.actor.kind === 'user'
234
+ ? (principal.actor.id as UserId)
235
+ : ('00000000-0000-0000-0000-000000000000' as UserId);
236
+ const result = await tenantHierarchy.createProject({ tenantId, creatorUserId, spec });
237
+ if (result.kind === 'err') {
238
+ const code =
239
+ result.error.code === 'slug-conflict'
240
+ ? 'slug-conflict'
241
+ : result.error.code === 'default-conflict'
242
+ ? 'project-default-already-exists'
243
+ : 'internal-server-error';
244
+ c.status(statusFor(code) as never);
245
+ return c.json(toWireError({ code, message: result.error.message }, requestId));
246
+ }
247
+ c.status(201);
248
+ return c.json({ id: result.value.projectId as unknown as string });
249
+ }
250
+ // No authorizer configured — fall back to the raw binding create;
251
+ // no FGA tuples written.
252
+ const id = await binding.create(tenantId, spec);
253
+ c.status(201);
254
+ return c.json({ id: id as unknown as string });
255
+ });
256
+
257
+ // ---------- GET /:projectId/memberships ----------
258
+ // Mount BEFORE /:projectId so `memberships` is not swallowed by the
259
+ // param.
260
+ r.get('/:projectId/memberships', async (c) => {
261
+ const requestId = c.get('requestId');
262
+ const tenantId = c.get('tenantId') as TenantId;
263
+ const projectId = c.req.param('projectId') as ProjectId;
264
+
265
+ const project = await binding.get(tenantId, projectId);
266
+ if (project === undefined) {
267
+ c.status(statusFor('project-not-found') as never);
268
+ return c.json(
269
+ toWireError(
270
+ {
271
+ code: 'project-not-found',
272
+ message: `No project with id "${projectId as unknown as string}"`,
273
+ projectId: projectId as unknown as string,
274
+ },
275
+ requestId,
276
+ ),
277
+ );
278
+ }
279
+ const limit = clampLimit(c.req.query('limit'));
280
+ const cursorRaw = c.req.query('cursor');
281
+ const page = await membershipBinding.list(tenantId, projectId, {
282
+ limit,
283
+ ...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
284
+ });
285
+ return c.json({
286
+ data: page.items.map(serializeProjectMembership),
287
+ hasMore: page.nextCursor !== undefined,
288
+ ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
289
+ });
290
+ });
291
+
292
+ // ---------- POST /:projectId/memberships ----------
293
+ r.post('/:projectId/memberships', async (c) => {
294
+ const requestId = c.get('requestId');
295
+ const tenantId = c.get('tenantId') as TenantId;
296
+ const projectId = c.req.param('projectId') as ProjectId;
297
+
298
+ const project = await binding.get(tenantId, projectId);
299
+ if (project === undefined) {
300
+ c.status(statusFor('project-not-found') as never);
301
+ return c.json(
302
+ toWireError(
303
+ {
304
+ code: 'project-not-found',
305
+ message: `No project with id "${projectId as unknown as string}"`,
306
+ projectId: projectId as unknown as string,
307
+ },
308
+ requestId,
309
+ ),
310
+ );
311
+ }
312
+ let body: unknown;
313
+ try {
314
+ body = await c.req.json();
315
+ } catch {
316
+ c.status(statusFor('bad-input') as never);
317
+ return c.json(
318
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
319
+ );
320
+ }
321
+ if (body === null || typeof body !== 'object') {
322
+ c.status(statusFor('bad-input') as never);
323
+ return c.json(
324
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
325
+ );
326
+ }
327
+ const b = body as Record<string, unknown>;
328
+ if (typeof b.userId !== 'string' || b.userId.length === 0) {
329
+ c.status(statusFor('bad-input') as never);
330
+ return c.json(
331
+ toWireError(
332
+ { code: 'bad-input', message: '`userId` is required and must be a non-empty string' },
333
+ requestId,
334
+ ),
335
+ );
336
+ }
337
+ if (!isProjectRole(b.role)) {
338
+ c.status(statusFor('bad-input') as never);
339
+ return c.json(
340
+ toWireError(
341
+ {
342
+ code: 'bad-input',
343
+ message: '`role` must be one of: viewer, editor, owner, admin, member',
344
+ },
345
+ requestId,
346
+ ),
347
+ );
348
+ }
349
+ try {
350
+ // With an authorizer, add through the tenant-hierarchy binding — it
351
+ // writes the FGA `<role>@project` tuple atomically so the granted
352
+ // user's permissions land in FGA.
353
+ if (authorizer !== undefined) {
354
+ const res = await tenantHierarchy.addProjectMember({
355
+ tenantId,
356
+ projectId,
357
+ userId: b.userId as unknown as UserId,
358
+ role: b.role as ProjectRole,
359
+ });
360
+ if (res.kind === 'err') throw new Error(res.error.message);
361
+ } else {
362
+ await membershipBinding.add(tenantId, {
363
+ projectId,
364
+ userId: b.userId as unknown as UserId,
365
+ role: b.role,
366
+ });
367
+ }
368
+ } catch (cause) {
369
+ const message = cause instanceof Error ? cause.message : String(cause);
370
+ if (message.startsWith('project-not-found')) {
371
+ // Racy — project was deleted between preliminary get and add.
372
+ c.status(statusFor('project-not-found') as never);
373
+ return c.json(
374
+ toWireError(
375
+ {
376
+ code: 'project-not-found',
377
+ message: `No project with id "${projectId as unknown as string}"`,
378
+ projectId: projectId as unknown as string,
379
+ },
380
+ requestId,
381
+ ),
382
+ );
383
+ }
384
+ throw cause;
385
+ }
386
+ c.status(201);
387
+ return c.json({
388
+ projectId: projectId as unknown as string,
389
+ userId: b.userId,
390
+ role: b.role,
391
+ });
392
+ });
393
+
394
+ // ---------- DELETE /:projectId/memberships/:userId ----------
395
+ r.delete('/:projectId/memberships/:userId', async (c) => {
396
+ const tenantId = c.get('tenantId') as TenantId;
397
+ const projectId = c.req.param('projectId') as ProjectId;
398
+ const userId = c.req.param('userId') as UserId;
399
+ await membershipBinding.remove(tenantId, projectId, userId);
400
+ c.status(204);
401
+ return c.body(null);
402
+ });
403
+
404
+ // ---------- PATCH /:projectId/memberships/:userId ----------
405
+ r.patch('/:projectId/memberships/:userId', async (c) => {
406
+ const requestId = c.get('requestId');
407
+ const tenantId = c.get('tenantId') as TenantId;
408
+ const projectId = c.req.param('projectId') as ProjectId;
409
+ const userId = c.req.param('userId') as UserId;
410
+
411
+ let body: unknown;
412
+ try {
413
+ body = await c.req.json();
414
+ } catch {
415
+ c.status(statusFor('bad-input') as never);
416
+ return c.json(
417
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
418
+ );
419
+ }
420
+ if (body === null || typeof body !== 'object') {
421
+ c.status(statusFor('bad-input') as never);
422
+ return c.json(
423
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
424
+ );
425
+ }
426
+ const b = body as Record<string, unknown>;
427
+ if (!isProjectRole(b.role)) {
428
+ c.status(statusFor('bad-input') as never);
429
+ return c.json(
430
+ toWireError(
431
+ {
432
+ code: 'bad-input',
433
+ message: '`role` must be one of: viewer, editor, owner, admin, member',
434
+ },
435
+ requestId,
436
+ ),
437
+ );
438
+ }
439
+ try {
440
+ await membershipBinding.updateRole(tenantId, projectId, userId, b.role);
441
+ } catch (cause) {
442
+ const message = cause instanceof Error ? cause.message : String(cause);
443
+ if (message.startsWith('project-membership-not-found')) {
444
+ c.status(statusFor('project-membership-not-found') as never);
445
+ return c.json(
446
+ toWireError(
447
+ {
448
+ code: 'project-membership-not-found',
449
+ message: `No membership for project "${projectId as unknown as string}" + user "${userId as unknown as string}"`,
450
+ projectId: projectId as unknown as string,
451
+ userId: userId as unknown as string,
452
+ },
453
+ requestId,
454
+ ),
455
+ );
456
+ }
457
+ if (message.startsWith('project-not-found')) {
458
+ c.status(statusFor('project-not-found') as never);
459
+ return c.json(
460
+ toWireError(
461
+ {
462
+ code: 'project-not-found',
463
+ message: `No project with id "${projectId as unknown as string}"`,
464
+ projectId: projectId as unknown as string,
465
+ },
466
+ requestId,
467
+ ),
468
+ );
469
+ }
470
+ throw cause;
471
+ }
472
+ c.status(204);
473
+ return c.body(null);
474
+ });
475
+
476
+ // ---------- GET /:projectId ----------
477
+ r.get('/:projectId', async (c) => {
478
+ const requestId = c.get('requestId');
479
+ const tenantId = c.get('tenantId') as TenantId;
480
+ const projectId = c.req.param('projectId') as ProjectId;
481
+
482
+ const project = await binding.get(tenantId, projectId);
483
+ if (project === undefined) {
484
+ c.status(statusFor('project-not-found') as never);
485
+ return c.json(
486
+ toWireError(
487
+ {
488
+ code: 'project-not-found',
489
+ message: `No project with id "${projectId as unknown as string}"`,
490
+ projectId: projectId as unknown as string,
491
+ },
492
+ requestId,
493
+ ),
494
+ );
495
+ }
496
+ return c.json(serializeProject(project));
497
+ });
498
+
499
+ // ---------- PATCH /:projectId ----------
500
+ r.patch('/:projectId', async (c) => {
501
+ const requestId = c.get('requestId');
502
+ const tenantId = c.get('tenantId') as TenantId;
503
+ const projectId = c.req.param('projectId') as ProjectId;
504
+
505
+ let body: unknown;
506
+ try {
507
+ body = await c.req.json();
508
+ } catch {
509
+ c.status(statusFor('bad-input') as never);
510
+ return c.json(
511
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
512
+ );
513
+ }
514
+ if (body === null || typeof body !== 'object') {
515
+ c.status(statusFor('bad-input') as never);
516
+ return c.json(
517
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
518
+ );
519
+ }
520
+ const b = body as Record<string, unknown>;
521
+ const shaped: { -readonly [K in keyof ProjectPatch]: ProjectPatch[K] } = {};
522
+ if (b.name !== undefined) {
523
+ if (typeof b.name !== 'string') {
524
+ c.status(statusFor('bad-input') as never);
525
+ return c.json(
526
+ toWireError(
527
+ { code: 'bad-input', message: '`name` must be a string when present' },
528
+ requestId,
529
+ ),
530
+ );
531
+ }
532
+ shaped.name = b.name;
533
+ }
534
+ if (b.slug !== undefined) {
535
+ if (typeof b.slug !== 'string') {
536
+ c.status(statusFor('bad-input') as never);
537
+ return c.json(
538
+ toWireError(
539
+ { code: 'bad-input', message: '`slug` must be a string when present' },
540
+ requestId,
541
+ ),
542
+ );
543
+ }
544
+ shaped.slug = b.slug;
545
+ }
546
+ if (b.description !== undefined) {
547
+ if (typeof b.description !== 'string') {
548
+ c.status(statusFor('bad-input') as never);
549
+ return c.json(
550
+ toWireError(
551
+ { code: 'bad-input', message: '`description` must be a string when present' },
552
+ requestId,
553
+ ),
554
+ );
555
+ }
556
+ shaped.description = b.description;
557
+ }
558
+ if ('orgId' in b) {
559
+ const raw = b.orgId;
560
+ if (raw === null) {
561
+ shaped.orgId = null;
562
+ } else if (typeof raw === 'string') {
563
+ shaped.orgId = raw as unknown as OrgId;
564
+ } else {
565
+ c.status(statusFor('bad-input') as never);
566
+ return c.json(
567
+ toWireError(
568
+ { code: 'bad-input', message: '`orgId` must be a string or null when present' },
569
+ requestId,
570
+ ),
571
+ );
572
+ }
573
+ }
574
+ try {
575
+ await binding.update(tenantId, projectId, shaped);
576
+ } catch (cause) {
577
+ const message = cause instanceof Error ? cause.message : String(cause);
578
+ if (message.startsWith('project-not-found')) {
579
+ c.status(statusFor('project-not-found') as never);
580
+ return c.json(
581
+ toWireError(
582
+ {
583
+ code: 'project-not-found',
584
+ message: `No project with id "${projectId as unknown as string}"`,
585
+ projectId: projectId as unknown as string,
586
+ },
587
+ requestId,
588
+ ),
589
+ );
590
+ }
591
+ throw cause;
592
+ }
593
+ c.status(204);
594
+ return c.body(null);
595
+ });
596
+
597
+ // ---------- DELETE /:projectId ----------
598
+ r.delete('/:projectId', async (c) => {
599
+ const tenantId = c.get('tenantId') as TenantId;
600
+ const projectId = c.req.param('projectId') as ProjectId;
601
+ await binding.delete(tenantId, projectId);
602
+ c.status(204);
603
+ return c.body(null);
604
+ });
605
+
606
+ return r;
607
+ }
608
+
609
+ function isProjectRole(x: unknown): x is 'viewer' | 'editor' | 'owner' | 'admin' | 'member' {
610
+ return x === 'viewer' || x === 'editor' || x === 'owner' || x === 'admin' || x === 'member';
611
+ }
612
+
613
+ function serializeProject(p: Project): Record<string, unknown> {
614
+ return {
615
+ id: p.id as unknown as string,
616
+ tenantId: p.tenantId as unknown as string,
617
+ ...(p.orgId !== undefined && { orgId: p.orgId as unknown as string }),
618
+ name: p.name,
619
+ slug: p.slug,
620
+ isDefault: p.isDefault,
621
+ ...(p.description !== undefined && { description: p.description }),
622
+ createdAt: p.createdAt as unknown as string,
623
+ updatedAt: p.updatedAt as unknown as string,
624
+ };
625
+ }
626
+
627
+ function serializeProjectMembership(m: ProjectMembership): Record<string, unknown> {
628
+ return {
629
+ projectId: m.projectId as unknown as string,
630
+ userId: m.userId as unknown as string,
631
+ role: m.role,
632
+ joinedAt: m.joinedAt as unknown as string,
633
+ };
634
+ }