@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,975 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { Hono } from 'hono';
5
+
6
+ import { tuplesForCreate } from '@kindgi/authz';
7
+ import type { Scope } from '@kindgi/platform';
8
+ import type { Cursor, EnvName, TenantId } from '@kindgi/types';
9
+
10
+ import { statusFor, toWireError } from '../errors.js';
11
+ import type { Authorizer } from '../middleware/authorize.js';
12
+ import type { RotationStatus, RotationStatusStore } from '../rotation-status-store.js';
13
+ import type {
14
+ SecretBinding,
15
+ SecretRecord,
16
+ SecretSetOutcome,
17
+ SecretVersionRecord,
18
+ } from '../secrets-binding.js';
19
+ import type { AppEnv } from '../types.js';
20
+ import { hasCapability, requireEnvName, requireScope, scopesEqual } from './env.js';
21
+ import { clampLimit } from './pagination.js';
22
+ import { queryScopeResourceRef, scopeResourceRef } from './scope-params.js';
23
+
24
+ /**
25
+ * `/v1/secrets/*` — HTTP surface for `SecretBinding`. Nine endpoints
26
+ * covering the full
27
+ * lifecycle: list / get / set / rotate (sync + async) / rotation-status
28
+ * poll / rotation-status SSE / revoke / version list / version get.
29
+ *
30
+ * Wire-safety:
31
+ * - `value` NEVER returns from list / get / getVersion / listVersions.
32
+ * - Within `/v1/secrets`, plaintext is accepted only by `POST /`
33
+ * (`value`, `secrets:write` capability) and `POST /:name/rotate`
34
+ * (optional `newValue`, `secrets:rotate` capability).
35
+ * - Async rotation returns 202 with `{ rotationId, statusUrl,
36
+ * eventsUrl }`; sync rotation returns 201 with the outcome inline.
37
+ *
38
+ * Capability gates:
39
+ * - `secrets:write` → POST /
40
+ * - `secrets:rotate` → POST /:name/rotate
41
+ * - `secrets:revoke` → DELETE /:name (soft)
42
+ * - `secrets:revoke:hard` → DELETE /:name?hard=true
43
+ * - `secrets:reveal` → not used by these routes; there is no HTTP
44
+ * `resolve` path.
45
+ *
46
+ * SSE:
47
+ * - `GET /:name/rotations/:rotationId/events` streams
48
+ * `event: rotation-update\ndata: <json>\n\n` frames on each status
49
+ * change. An `event: ping` heartbeat (empty data) every 15s keeps
50
+ * proxies from timing the stream out; the stream closes on terminal
51
+ * state.
52
+ */
53
+ export interface SecretsRouterOptions {
54
+ readonly secretsBinding: SecretBinding;
55
+ readonly rotationStatusStore: RotationStatusStore;
56
+ /**
57
+ * Overrideable SSE heartbeat interval. Default 15_000 ms; tests
58
+ * pass a low value to keep suites fast.
59
+ */
60
+ readonly sseHeartbeatMs?: number;
61
+ /**
62
+ * Optional fine-grained authorizer. When present, gates every route
63
+ * on `admin` on the request's `scope` (query for GET/DELETE, body for
64
+ * create, body or query for rotate). Reads are STRICT: strict-read means `admin from scope`
65
+ * or explicit `reader` grant — cascade via the scope tuple written
66
+ * by `secretsBinding.set` on fresh create.
67
+ */
68
+ readonly authorizer?: Authorizer;
69
+ }
70
+
71
+ export function secretsRouter(options: SecretsRouterOptions): Hono<AppEnv> {
72
+ const { secretsBinding, rotationStatusStore, authorizer } = options;
73
+ const heartbeatMs = options.sseHeartbeatMs ?? 15_000;
74
+ const r = new Hono<AppEnv>();
75
+
76
+ // Authorization PEP — scope-anchored.
77
+ // STRICT-READ: only scope-admins or explicit `reader` grantees pass
78
+ // `can_read` on a secret. Each check derives the scope with the same
79
+ // parser as its handler: `POST /` from `body.scope`; `POST /:name/rotate`
80
+ // from `body.scope` or the query (resolveRotateScope); every other route
81
+ // from `?scopeKind` + `?scopeId`.
82
+ if (authorizer !== undefined) {
83
+ const scopeFromQuery = (c: import('hono').Context<AppEnv>) =>
84
+ queryScopeResourceRef((n) => c.req.query(n), { tenantId: c.get('tenantId') as TenantId });
85
+ r.use('/', async (c, next) => {
86
+ if (c.req.method === 'POST') {
87
+ const tenantId = c.get('tenantId') as TenantId;
88
+ const mw = authorizer.authorize('admin', async () => {
89
+ let bodyScope: unknown;
90
+ try {
91
+ const body = await c.req.json();
92
+ if (body !== null && typeof body === 'object') {
93
+ bodyScope = (body as Record<string, unknown>).scope;
94
+ }
95
+ } catch {
96
+ // POST handler validates + returns 400
97
+ }
98
+ const s = validateBodyScope(bodyScope, tenantId);
99
+ return scopeResourceRef(s.kind === 'ok' ? s.scope : undefined, tenantId);
100
+ });
101
+ return mw(c, next);
102
+ }
103
+ if (c.req.method === 'GET') {
104
+ const mw = authorizer.authorize('admin', scopeFromQuery);
105
+ return mw(c, next);
106
+ }
107
+ return next();
108
+ });
109
+ // `/:name/*` also matches `/:name`: one check per request. The only
110
+ // POST below `/:name` is rotate, which may carry its scope in the body;
111
+ // read it with `text()`, as the handler does.
112
+ r.use('/:name/*', async (c, next) => {
113
+ if (c.req.method === 'POST') {
114
+ const tenantId = c.get('tenantId') as TenantId;
115
+ const mw = authorizer.authorize('admin', async () => {
116
+ const s = resolveRotateScope(
117
+ parseJsonObject(await c.req.text()),
118
+ c.req.query(),
119
+ tenantId,
120
+ );
121
+ return scopeResourceRef(s.kind === 'ok' ? s.scope : undefined, tenantId);
122
+ });
123
+ return mw(c, next);
124
+ }
125
+ const mw = authorizer.authorize('admin', scopeFromQuery);
126
+ return mw(c, next);
127
+ });
128
+ }
129
+
130
+ // ------------------------------ GET / ------------------------------
131
+ r.get('/', async (c) => {
132
+ const requestId = c.get('requestId');
133
+ const tenantId = c.get('tenantId') as TenantId;
134
+
135
+ const envNameResult = requireEnvName(c.req.query('envName'));
136
+ if (envNameResult.kind === 'err') {
137
+ c.status(statusFor(envNameResult.code) as never);
138
+ return c.json(
139
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
140
+ );
141
+ }
142
+ const scopeResult = requireScope(c.req.query(), tenantId);
143
+ if (scopeResult.kind === 'err') {
144
+ c.status(statusFor(scopeResult.code) as never);
145
+ return c.json(
146
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
147
+ );
148
+ }
149
+
150
+ const limit = clampLimit(c.req.query('limit'));
151
+ const cursorRaw = c.req.query('cursor');
152
+ const namePrefix = c.req.query('namePrefix');
153
+
154
+ const page = await secretsBinding.list({
155
+ scope: scopeResult.scope,
156
+ envName: envNameResult.envName,
157
+ limit,
158
+ ...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
159
+ ...(namePrefix !== undefined && namePrefix.length > 0 && { namePrefix }),
160
+ });
161
+ return c.json({
162
+ data: page.data.map(serializeSecretRecord),
163
+ ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
164
+ });
165
+ });
166
+
167
+ // ---------------------- GET /:name -----------------------------
168
+ r.get('/:name', async (c) => {
169
+ const requestId = c.get('requestId');
170
+ const tenantId = c.get('tenantId') as TenantId;
171
+ const name = c.req.param('name');
172
+
173
+ const envNameResult = requireEnvName(c.req.query('envName'));
174
+ if (envNameResult.kind === 'err') {
175
+ c.status(statusFor(envNameResult.code) as never);
176
+ return c.json(
177
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
178
+ );
179
+ }
180
+ const scopeResult = requireScope(c.req.query(), tenantId);
181
+ if (scopeResult.kind === 'err') {
182
+ c.status(statusFor(scopeResult.code) as never);
183
+ return c.json(
184
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
185
+ );
186
+ }
187
+
188
+ const rec = await secretsBinding.get({
189
+ scope: scopeResult.scope,
190
+ envName: envNameResult.envName,
191
+ name,
192
+ });
193
+ if (rec === null) {
194
+ c.status(statusFor('secret-not-found') as never);
195
+ return c.json(
196
+ toWireError({ code: 'secret-not-found', message: `No secret "${name}".`, name }, requestId),
197
+ );
198
+ }
199
+ return c.json(serializeSecretRecord(rec));
200
+ });
201
+
202
+ // ------------------ GET /:name/versions -----------------------
203
+ r.get('/:name/versions', async (c) => {
204
+ const requestId = c.get('requestId');
205
+ const tenantId = c.get('tenantId') as TenantId;
206
+ const name = c.req.param('name');
207
+
208
+ const envNameResult = requireEnvName(c.req.query('envName'));
209
+ if (envNameResult.kind === 'err') {
210
+ c.status(statusFor(envNameResult.code) as never);
211
+ return c.json(
212
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
213
+ );
214
+ }
215
+ const scopeResult = requireScope(c.req.query(), tenantId);
216
+ if (scopeResult.kind === 'err') {
217
+ c.status(statusFor(scopeResult.code) as never);
218
+ return c.json(
219
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
220
+ );
221
+ }
222
+
223
+ const limit = clampLimit(c.req.query('limit'));
224
+ const cursorRaw = c.req.query('cursor');
225
+
226
+ const page = await secretsBinding.listVersions({
227
+ scope: scopeResult.scope,
228
+ envName: envNameResult.envName,
229
+ name,
230
+ limit,
231
+ ...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
232
+ });
233
+ return c.json({
234
+ data: page.data.map(serializeVersion),
235
+ ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
236
+ });
237
+ });
238
+
239
+ // -------------- GET /:name/versions/:versionId -----------------
240
+ r.get('/:name/versions/:versionId', async (c) => {
241
+ const requestId = c.get('requestId');
242
+ const tenantId = c.get('tenantId') as TenantId;
243
+ const name = c.req.param('name');
244
+ const versionIdRaw = c.req.param('versionId');
245
+ const versionId = Number.parseInt(versionIdRaw, 10);
246
+ if (!Number.isFinite(versionId) || versionId < 1) {
247
+ c.status(statusFor('bad-input') as never);
248
+ return c.json(
249
+ toWireError(
250
+ { code: 'bad-input', message: '`versionId` must be a positive integer.' },
251
+ requestId,
252
+ ),
253
+ );
254
+ }
255
+
256
+ const envNameResult = requireEnvName(c.req.query('envName'));
257
+ if (envNameResult.kind === 'err') {
258
+ c.status(statusFor(envNameResult.code) as never);
259
+ return c.json(
260
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
261
+ );
262
+ }
263
+ const scopeResult = requireScope(c.req.query(), tenantId);
264
+ if (scopeResult.kind === 'err') {
265
+ c.status(statusFor(scopeResult.code) as never);
266
+ return c.json(
267
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
268
+ );
269
+ }
270
+
271
+ const rec = await secretsBinding.getVersion({
272
+ scope: scopeResult.scope,
273
+ envName: envNameResult.envName,
274
+ name,
275
+ versionId,
276
+ });
277
+ if (rec === null) {
278
+ c.status(statusFor('secret-version-not-found') as never);
279
+ return c.json(
280
+ toWireError(
281
+ {
282
+ code: 'secret-version-not-found',
283
+ message: `No version ${versionId} for secret "${name}".`,
284
+ name,
285
+ versionId,
286
+ },
287
+ requestId,
288
+ ),
289
+ );
290
+ }
291
+ return c.json(serializeVersion(rec));
292
+ });
293
+
294
+ // ------------------------- POST / -----------------------------
295
+ r.post('/', async (c) => {
296
+ const requestId = c.get('requestId');
297
+ const tenantId = c.get('tenantId') as TenantId;
298
+
299
+ if (!hasCapability(c, 'secrets:write')) {
300
+ c.status(statusFor('permission-denied') as never);
301
+ return c.json(
302
+ toWireError(
303
+ {
304
+ code: 'permission-denied',
305
+ message:
306
+ 'Bearer token is missing the `secrets:write` capability required for this route.',
307
+ },
308
+ requestId,
309
+ ),
310
+ );
311
+ }
312
+
313
+ let body: unknown;
314
+ try {
315
+ body = await c.req.json();
316
+ } catch {
317
+ c.status(statusFor('bad-input') as never);
318
+ return c.json(
319
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
320
+ );
321
+ }
322
+ if (body === null || typeof body !== 'object') {
323
+ c.status(statusFor('bad-input') as never);
324
+ return c.json(
325
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
326
+ );
327
+ }
328
+ const b = body as Record<string, unknown>;
329
+
330
+ // Body-first envName + scope validation — POST /v1/secrets carries
331
+ // both entirely in the body (no path params to steer them). Query
332
+ // params are optional here.
333
+ const envNameRaw = (b.envName as string | undefined) ?? c.req.query('envName');
334
+ const envNameResult = requireEnvName(envNameRaw);
335
+ if (envNameResult.kind === 'err') {
336
+ c.status(statusFor(envNameResult.code) as never);
337
+ return c.json(
338
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
339
+ );
340
+ }
341
+ const bodyScope = validateBodyScope(b.scope, tenantId);
342
+ if (bodyScope.kind === 'err') {
343
+ c.status(statusFor(bodyScope.code) as never);
344
+ return c.json(toWireError({ code: bodyScope.code, message: bodyScope.message }, requestId));
345
+ }
346
+
347
+ if (typeof b.name !== 'string' || b.name.length === 0) {
348
+ c.status(statusFor('bad-input') as never);
349
+ return c.json(
350
+ toWireError(
351
+ { code: 'bad-input', message: '`name` is required and must be a non-empty string' },
352
+ requestId,
353
+ ),
354
+ );
355
+ }
356
+ if (typeof b.value !== 'string') {
357
+ c.status(statusFor('bad-input') as never);
358
+ return c.json(
359
+ toWireError(
360
+ { code: 'bad-input', message: '`value` is required and must be a string' },
361
+ requestId,
362
+ ),
363
+ );
364
+ }
365
+ if (b.writeMode !== 'create-new' && b.writeMode !== 'add-version') {
366
+ c.status(statusFor('bad-input') as never);
367
+ return c.json(
368
+ toWireError(
369
+ {
370
+ code: 'bad-input',
371
+ message: '`writeMode` must be one of `create-new` | `add-version`',
372
+ },
373
+ requestId,
374
+ ),
375
+ );
376
+ }
377
+ if (b.ifVersion !== undefined && typeof b.ifVersion !== 'number') {
378
+ c.status(statusFor('bad-input') as never);
379
+ return c.json(
380
+ toWireError(
381
+ { code: 'bad-input', message: '`ifVersion` must be a number when present' },
382
+ requestId,
383
+ ),
384
+ );
385
+ }
386
+ if (b.rotationDueAt !== undefined && typeof b.rotationDueAt !== 'string') {
387
+ c.status(statusFor('bad-input') as never);
388
+ return c.json(
389
+ toWireError(
390
+ { code: 'bad-input', message: '`rotationDueAt` must be an ISO 8601 string when present' },
391
+ requestId,
392
+ ),
393
+ );
394
+ }
395
+ if (
396
+ b.tags !== undefined &&
397
+ (b.tags === null || typeof b.tags !== 'object' || Array.isArray(b.tags))
398
+ ) {
399
+ c.status(statusFor('bad-input') as never);
400
+ return c.json(
401
+ toWireError(
402
+ { code: 'bad-input', message: '`tags` must be a string map when present' },
403
+ requestId,
404
+ ),
405
+ );
406
+ }
407
+
408
+ const setScope = bodyScope.scope;
409
+ const outcome: SecretSetOutcome = await secretsBinding.set({
410
+ scope: setScope,
411
+ envName: envNameResult.envName,
412
+ name: b.name,
413
+ value: b.value,
414
+ writeMode: b.writeMode,
415
+ ...(b.tags !== undefined && { tags: b.tags as Readonly<Record<string, string>> }),
416
+ ...(b.rotationDueAt !== undefined && { rotationDueAt: b.rotationDueAt as string }),
417
+ ...(b.ifVersion !== undefined && { ifVersion: b.ifVersion as number }),
418
+ // Authorization — write `secret#scope@X` tuple on fresh insert.
419
+ enqueueTuples: (secretRowId) =>
420
+ tuplesForCreate({ kind: 'secret', id: secretRowId, tenantId, scope: setScope }),
421
+ });
422
+
423
+ if (outcome.kind === 'ok') {
424
+ // Freshly-created secrets return 201 (`create-new` path); every
425
+ // subsequent version of an existing secret returns 200 (`add-version`).
426
+ const status = b.writeMode === 'create-new' ? 201 : 200;
427
+ c.status(status as never);
428
+ return c.json({
429
+ record: serializeSecretRecord(outcome.record),
430
+ versionId: outcome.versionId,
431
+ });
432
+ }
433
+ if (outcome.kind === 'already-exists') {
434
+ c.status(statusFor('secret-write-conflict') as never);
435
+ return c.json(
436
+ toWireError(
437
+ {
438
+ code: 'secret-write-conflict',
439
+ message: `Secret "${outcome.record.name}" already exists. Use writeMode: 'add-version' to add a new version.`,
440
+ currentVersion: outcome.record.currentVersion,
441
+ },
442
+ requestId,
443
+ ),
444
+ );
445
+ }
446
+ if (outcome.kind === 'version-conflict') {
447
+ c.status(statusFor('secret-write-conflict') as never);
448
+ return c.json(
449
+ toWireError(
450
+ {
451
+ code: 'secret-write-conflict',
452
+ message: `Stored version is ${outcome.currentVersion}; retry with the current value.`,
453
+ currentVersion: outcome.currentVersion,
454
+ },
455
+ requestId,
456
+ ),
457
+ );
458
+ }
459
+ // error case
460
+ c.status(statusFor(outcome.code) as never);
461
+ return c.json(toWireError({ code: outcome.code, message: outcome.message }, requestId));
462
+ });
463
+
464
+ // -------------------- POST /:name/rotate -----------------------
465
+ r.post('/:name/rotate', async (c) => {
466
+ const requestId = c.get('requestId');
467
+ const tenantId = c.get('tenantId') as TenantId;
468
+ const name = c.req.param('name');
469
+
470
+ if (!hasCapability(c, 'secrets:rotate')) {
471
+ c.status(statusFor('permission-denied') as never);
472
+ return c.json(
473
+ toWireError(
474
+ {
475
+ code: 'permission-denied',
476
+ message:
477
+ 'Bearer token is missing the `secrets:rotate` capability required for this route.',
478
+ },
479
+ requestId,
480
+ ),
481
+ );
482
+ }
483
+
484
+ let body: unknown = {};
485
+ const raw = await c.req.text();
486
+ if (raw.length > 0) {
487
+ try {
488
+ body = JSON.parse(raw);
489
+ } catch {
490
+ c.status(statusFor('bad-input') as never);
491
+ return c.json(
492
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
493
+ );
494
+ }
495
+ if (body === null || typeof body !== 'object') {
496
+ c.status(statusFor('bad-input') as never);
497
+ return c.json(
498
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
499
+ );
500
+ }
501
+ }
502
+ const b = body as Record<string, unknown>;
503
+
504
+ const queryEnvName = c.req.query('envName');
505
+ if (b.envName !== undefined && queryEnvName !== undefined && b.envName !== queryEnvName) {
506
+ c.status(statusFor('env-name-mismatch') as never);
507
+ return c.json(
508
+ toWireError(
509
+ {
510
+ code: 'env-name-mismatch',
511
+ message: `Body \`envName\` "${String(b.envName)}" does not match query envName "${queryEnvName}".`,
512
+ },
513
+ requestId,
514
+ ),
515
+ );
516
+ }
517
+ const envNameRaw = (b.envName as string | undefined) ?? queryEnvName;
518
+ const envNameResult = requireEnvName(envNameRaw);
519
+ if (envNameResult.kind === 'err') {
520
+ c.status(statusFor(envNameResult.code) as never);
521
+ return c.json(
522
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
523
+ );
524
+ }
525
+ // The same resolution the authorization middleware checked.
526
+ const scopeResult = resolveRotateScope(b, c.req.query(), tenantId);
527
+ if (scopeResult.kind === 'err') {
528
+ c.status(statusFor(scopeResult.code) as never);
529
+ return c.json(
530
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
531
+ );
532
+ }
533
+ const scope: Scope = scopeResult.scope;
534
+
535
+ if (b.newValue !== undefined && typeof b.newValue !== 'string') {
536
+ c.status(statusFor('bad-input') as never);
537
+ return c.json(
538
+ toWireError(
539
+ { code: 'bad-input', message: '`newValue` must be a string when present' },
540
+ requestId,
541
+ ),
542
+ );
543
+ }
544
+ if (b.revokeOldAfterMs !== undefined && typeof b.revokeOldAfterMs !== 'number') {
545
+ c.status(statusFor('bad-input') as never);
546
+ return c.json(
547
+ toWireError(
548
+ { code: 'bad-input', message: '`revokeOldAfterMs` must be a number when present' },
549
+ requestId,
550
+ ),
551
+ );
552
+ }
553
+
554
+ const result = await secretsBinding.rotate({
555
+ scope,
556
+ envName: envNameResult.envName,
557
+ name,
558
+ ...(b.newValue !== undefined && { newValue: b.newValue as string }),
559
+ ...(b.revokeOldAfterMs !== undefined && { revokeOldAfterMs: b.revokeOldAfterMs as number }),
560
+ });
561
+
562
+ if (result.kind === 'err') {
563
+ c.status(statusFor(result.error.code) as never);
564
+ return c.json(
565
+ toWireError({ code: result.error.code, message: result.error.message, name }, requestId),
566
+ );
567
+ }
568
+
569
+ if (result.value.kind === 'ok') {
570
+ // Sync provider — 201 Created inline.
571
+ c.status(201 as never);
572
+ return c.json({
573
+ kind: 'sync',
574
+ newVersionId: result.value.newVersionId,
575
+ oldVersionId: result.value.oldVersionId,
576
+ ...(result.value.oldVersionRevokedAt !== undefined && {
577
+ oldVersionRevokedAt: result.value.oldVersionRevokedAt,
578
+ }),
579
+ });
580
+ }
581
+
582
+ // Async provider — mint a rotationId, record, return 202.
583
+ const { rotationId } = await rotationStatusStore.create({
584
+ scope,
585
+ envName: envNameResult.envName,
586
+ name,
587
+ provider: result.value.provider,
588
+ resumeToken: result.value.resumeToken,
589
+ });
590
+ const query = encodeStatusQuery(scope, envNameResult.envName);
591
+ const statusUrl = `/v1/secrets/${encodeURIComponent(name)}/rotations/${rotationId}?${query}`;
592
+ const eventsUrl = `/v1/secrets/${encodeURIComponent(name)}/rotations/${rotationId}/events?${query}`;
593
+ c.status(202 as never);
594
+ return c.json({ kind: 'async', rotationId, statusUrl, eventsUrl });
595
+ });
596
+
597
+ // -------- GET /:name/rotations/:rotationId ----------
598
+ r.get('/:name/rotations/:rotationId', async (c) => {
599
+ const requestId = c.get('requestId');
600
+ const tenantId = c.get('tenantId') as TenantId;
601
+ const name = c.req.param('name');
602
+ const rotationId = c.req.param('rotationId');
603
+
604
+ const envNameResult = requireEnvName(c.req.query('envName'));
605
+ if (envNameResult.kind === 'err') {
606
+ c.status(statusFor(envNameResult.code) as never);
607
+ return c.json(
608
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
609
+ );
610
+ }
611
+ const scopeResult = requireScope(c.req.query(), tenantId);
612
+ if (scopeResult.kind === 'err') {
613
+ c.status(statusFor(scopeResult.code) as never);
614
+ return c.json(
615
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
616
+ );
617
+ }
618
+
619
+ const status = await rotationStatusStore.get({
620
+ scope: scopeResult.scope,
621
+ envName: envNameResult.envName,
622
+ name,
623
+ rotationId,
624
+ });
625
+ if (status === null) {
626
+ c.status(statusFor('not-found') as never);
627
+ return c.json(
628
+ toWireError(
629
+ {
630
+ code: 'not-found',
631
+ message: `No rotation "${rotationId}" for secret "${name}".`,
632
+ },
633
+ requestId,
634
+ ),
635
+ );
636
+ }
637
+ return c.json(serializeRotationStatus(status));
638
+ });
639
+
640
+ // -- GET /:name/rotations/:rotationId/events (SSE) --
641
+ r.get('/:name/rotations/:rotationId/events', async (c) => {
642
+ const requestId = c.get('requestId');
643
+ const tenantId = c.get('tenantId') as TenantId;
644
+ const name = c.req.param('name');
645
+ const rotationId = c.req.param('rotationId');
646
+
647
+ const envNameResult = requireEnvName(c.req.query('envName'));
648
+ if (envNameResult.kind === 'err') {
649
+ c.status(statusFor(envNameResult.code) as never);
650
+ return c.json(
651
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
652
+ );
653
+ }
654
+ const scopeResult = requireScope(c.req.query(), tenantId);
655
+ if (scopeResult.kind === 'err') {
656
+ c.status(statusFor(scopeResult.code) as never);
657
+ return c.json(
658
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
659
+ );
660
+ }
661
+
662
+ const existing = await rotationStatusStore.get({
663
+ scope: scopeResult.scope,
664
+ envName: envNameResult.envName,
665
+ name,
666
+ rotationId,
667
+ });
668
+ if (existing === null) {
669
+ c.status(statusFor('not-found') as never);
670
+ return c.json(
671
+ toWireError(
672
+ {
673
+ code: 'not-found',
674
+ message: `No rotation "${rotationId}" for secret "${name}".`,
675
+ },
676
+ requestId,
677
+ ),
678
+ );
679
+ }
680
+
681
+ const encoder = new TextEncoder();
682
+ const initialStatus = existing;
683
+ const body = new ReadableStream<Uint8Array>({
684
+ async start(controller) {
685
+ let closed = false;
686
+ const close = (): void => {
687
+ if (closed) return;
688
+ closed = true;
689
+ try {
690
+ controller.close();
691
+ } catch {
692
+ /* already closed */
693
+ }
694
+ };
695
+ const emit = (event: string, data: unknown): void => {
696
+ if (closed) return;
697
+ const payload = event === 'ping' ? '' : JSON.stringify(data);
698
+ try {
699
+ controller.enqueue(encoder.encode(`event: ${event}\ndata: ${payload}\n\n`));
700
+ } catch {
701
+ /* already closed */
702
+ }
703
+ };
704
+
705
+ // Emit the initial state so a subscriber that connects after
706
+ // the first update still learns where the rotation stands.
707
+ emit('rotation-update', initialStatus);
708
+
709
+ if (initialStatus.status !== 'pending') {
710
+ close();
711
+ return;
712
+ }
713
+
714
+ let terminal = false;
715
+ let notifier: (() => void) | undefined;
716
+ const wakeup = (): void => {
717
+ if (notifier !== undefined) {
718
+ const cb = notifier;
719
+ notifier = undefined;
720
+ cb();
721
+ }
722
+ };
723
+
724
+ const unsub = rotationStatusStore.subscribe({
725
+ rotationId,
726
+ onUpdate: (s) => {
727
+ emit('rotation-update', s);
728
+ if (s.status !== 'pending') {
729
+ terminal = true;
730
+ }
731
+ wakeup();
732
+ },
733
+ });
734
+
735
+ const timer = setInterval(() => {
736
+ if (terminal) return;
737
+ emit('ping', {});
738
+ }, heartbeatMs);
739
+
740
+ try {
741
+ while (!terminal) {
742
+ await new Promise<void>((resolve) => {
743
+ notifier = resolve;
744
+ });
745
+ }
746
+ } finally {
747
+ clearInterval(timer);
748
+ unsub();
749
+ close();
750
+ }
751
+ },
752
+ });
753
+ return new Response(body, {
754
+ status: 200,
755
+ headers: {
756
+ 'Content-Type': 'text/event-stream; charset=utf-8',
757
+ 'Cache-Control': 'no-cache',
758
+ 'X-Accel-Buffering': 'no',
759
+ Connection: 'keep-alive',
760
+ },
761
+ });
762
+ });
763
+
764
+ // ------------------------ DELETE /:name -----------------------
765
+ r.delete('/:name', async (c) => {
766
+ const requestId = c.get('requestId');
767
+ const tenantId = c.get('tenantId') as TenantId;
768
+ const name = c.req.param('name');
769
+ const hardFlag = c.req.query('hard') === 'true';
770
+
771
+ const requiredCap = hardFlag ? 'secrets:revoke:hard' : 'secrets:revoke';
772
+ if (!hasCapability(c, requiredCap)) {
773
+ c.status(statusFor('permission-denied') as never);
774
+ return c.json(
775
+ toWireError(
776
+ {
777
+ code: 'permission-denied',
778
+ message: `Bearer token is missing the \`${requiredCap}\` capability required for this route.`,
779
+ },
780
+ requestId,
781
+ ),
782
+ );
783
+ }
784
+
785
+ const envNameResult = requireEnvName(c.req.query('envName'));
786
+ if (envNameResult.kind === 'err') {
787
+ c.status(statusFor(envNameResult.code) as never);
788
+ return c.json(
789
+ toWireError({ code: envNameResult.code, message: envNameResult.message }, requestId),
790
+ );
791
+ }
792
+ const scopeResult = requireScope(c.req.query(), tenantId);
793
+ if (scopeResult.kind === 'err') {
794
+ c.status(statusFor(scopeResult.code) as never);
795
+ return c.json(
796
+ toWireError({ code: scopeResult.code, message: scopeResult.message }, requestId),
797
+ );
798
+ }
799
+
800
+ const reason = c.req.query('reason');
801
+ const outcome = await secretsBinding.revoke({
802
+ scope: scopeResult.scope,
803
+ envName: envNameResult.envName,
804
+ name,
805
+ ...(hardFlag && { hard: true }),
806
+ ...(reason !== undefined && reason.length > 0 && { reason }),
807
+ });
808
+
809
+ if (outcome.kind === 'err') {
810
+ c.status(statusFor(outcome.error.code) as never);
811
+ return c.json(
812
+ toWireError({ code: outcome.error.code, message: outcome.error.message, name }, requestId),
813
+ );
814
+ }
815
+ return c.json({ revoked: outcome.value.revoked, hard: outcome.value.hard });
816
+ });
817
+
818
+ return r;
819
+ }
820
+
821
+ // --------------------------- helpers ---------------------------
822
+
823
+ function encodeStatusQuery(scope: Scope, envName: EnvName): string {
824
+ const params = new URLSearchParams();
825
+ params.set('envName', envName as unknown as string);
826
+ params.set('scopeKind', scope.kind);
827
+ if (scope.kind === 'org') {
828
+ params.set('scopeId', scope.orgId as unknown as string);
829
+ } else if (scope.kind === 'project') {
830
+ params.set('scopeId', scope.projectId as unknown as string);
831
+ }
832
+ return params.toString();
833
+ }
834
+
835
+ type BodyScopeResult =
836
+ | { readonly kind: 'ok'; readonly scope: Scope }
837
+ | { readonly kind: 'err'; readonly code: string; readonly message: string };
838
+
839
+ function validateBodyScope(raw: unknown, tenantId: TenantId): BodyScopeResult {
840
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
841
+ return {
842
+ kind: 'err',
843
+ code: 'scope-kind-required',
844
+ message: '`scope` is required in the request body.',
845
+ };
846
+ }
847
+ const obj = raw as Record<string, unknown>;
848
+ if (obj.kind !== 'tenant' && obj.kind !== 'org' && obj.kind !== 'project') {
849
+ return {
850
+ kind: 'err',
851
+ code: 'scope-kind-required',
852
+ message: '`scope.kind` must be one of `tenant` | `org` | `project`.',
853
+ };
854
+ }
855
+ // Cross-tenant refuse: body `tenantId` must match session tenantId.
856
+ if (obj.tenantId !== undefined && obj.tenantId !== (tenantId as unknown as string)) {
857
+ return {
858
+ kind: 'err',
859
+ code: 'scope-mismatch',
860
+ message: 'Body `scope.tenantId` does not match session tenantId.',
861
+ };
862
+ }
863
+ if (obj.kind === 'tenant') {
864
+ return {
865
+ kind: 'ok',
866
+ scope: { kind: 'tenant', tenantId } as Scope,
867
+ };
868
+ }
869
+ if (obj.kind === 'org') {
870
+ if (typeof obj.orgId !== 'string' || obj.orgId.length === 0) {
871
+ return {
872
+ kind: 'err',
873
+ code: 'scope-invalid',
874
+ message: '`scope.orgId` is required when scope.kind is `org`.',
875
+ };
876
+ }
877
+ return {
878
+ kind: 'ok',
879
+ scope: { kind: 'org', tenantId, orgId: obj.orgId } as Scope,
880
+ };
881
+ }
882
+ if (typeof obj.projectId !== 'string' || obj.projectId.length === 0) {
883
+ return {
884
+ kind: 'err',
885
+ code: 'scope-invalid',
886
+ message: '`scope.projectId` is required when scope.kind is `project`.',
887
+ };
888
+ }
889
+ return {
890
+ kind: 'ok',
891
+ scope: { kind: 'project', tenantId, projectId: obj.projectId } as Scope,
892
+ };
893
+ }
894
+
895
+ /**
896
+ * The scope `POST /:name/rotate` acts on: `body.scope` when present, else
897
+ * the `?scopeKind` + `?scopeId` query parameters. When the body and the
898
+ * query both carry a scope they must name the same one (`scope-mismatch`).
899
+ * The authorization middleware and the handler both resolve through here.
900
+ */
901
+ function resolveRotateScope(
902
+ body: Record<string, unknown>,
903
+ query: Record<string, string>,
904
+ tenantId: TenantId,
905
+ ): BodyScopeResult {
906
+ const queryHasScope = query.scopeKind !== undefined || query.scopeId !== undefined;
907
+ const queryScope = requireScope(query, tenantId);
908
+ if (body.scope === undefined) return queryScope;
909
+ const bodyScope = validateBodyScope(body.scope, tenantId);
910
+ if (bodyScope.kind === 'err' || !queryHasScope) return bodyScope;
911
+ if (queryScope.kind === 'err') return queryScope;
912
+ if (!scopesEqual(body.scope, queryScope.scope)) {
913
+ return {
914
+ kind: 'err',
915
+ code: 'scope-mismatch',
916
+ message: 'Body `scope` does not match query scopeKind/scopeId.',
917
+ };
918
+ }
919
+ return bodyScope;
920
+ }
921
+
922
+ /** The request body as an object; `{}` when it is empty or not a JSON object. */
923
+ function parseJsonObject(raw: string): Record<string, unknown> {
924
+ if (raw.length === 0) return {};
925
+ try {
926
+ const parsed: unknown = JSON.parse(raw);
927
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
928
+ ? (parsed as Record<string, unknown>)
929
+ : {};
930
+ } catch {
931
+ return {};
932
+ }
933
+ }
934
+
935
+ function serializeSecretRecord(rec: SecretRecord): Record<string, unknown> {
936
+ return {
937
+ scope: rec.scope as unknown as Record<string, unknown>,
938
+ envName: rec.envName as unknown as string,
939
+ name: rec.name,
940
+ currentVersion: rec.currentVersion,
941
+ createdAt: rec.createdAt,
942
+ updatedAt: rec.updatedAt,
943
+ ...(rec.revokedAt !== undefined && { revokedAt: rec.revokedAt }),
944
+ ...(rec.revokeReason !== undefined && { revokeReason: rec.revokeReason }),
945
+ ...(rec.tags !== undefined && { tags: rec.tags }),
946
+ ...(rec.rotationDueAt !== undefined && { rotationDueAt: rec.rotationDueAt }),
947
+ };
948
+ }
949
+
950
+ function serializeVersion(rec: SecretVersionRecord): Record<string, unknown> {
951
+ return {
952
+ scope: rec.scope as unknown as Record<string, unknown>,
953
+ envName: rec.envName as unknown as string,
954
+ name: rec.name,
955
+ versionId: rec.versionId,
956
+ createdAt: rec.createdAt,
957
+ // Redaction: `value` is ALWAYS null on getVersion / listVersions.
958
+ // The only `value`-returning path is `resolve`, which has no HTTP
959
+ // surface.
960
+ value: null,
961
+ ...(rec.revokedAt !== undefined && { revokedAt: rec.revokedAt }),
962
+ };
963
+ }
964
+
965
+ function serializeRotationStatus(s: RotationStatus): Record<string, unknown> {
966
+ return {
967
+ rotationId: s.rotationId,
968
+ status: s.status,
969
+ startedAt: s.startedAt,
970
+ updatedAt: s.updatedAt,
971
+ ...(s.newVersionId !== undefined && { newVersionId: s.newVersionId }),
972
+ ...(s.oldVersionId !== undefined && { oldVersionId: s.oldVersionId }),
973
+ ...(s.error !== undefined && { error: s.error }),
974
+ };
975
+ }