@kindgi/api 0.0.0-bootstrap.0 → 0.1.0

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 +396 -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 +264 -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 +13 -0
  104. package/dist/middleware/error-mapper.d.ts.map +1 -0
  105. package/dist/middleware/error-mapper.js +32 -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 +5051 -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 +773 -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 +29659 -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 +1285 -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 +291 -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 +37 -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 +5445 -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 +981 -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,806 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { createHash, randomBytes } from 'node:crypto';
5
+
6
+ import { Hono } from 'hono';
7
+ import type { Context } from 'hono';
8
+
9
+ import type { SessionId, TenantId, UserId } from '@kindgi/types';
10
+
11
+ import { statusFor, toWireError } from '../errors.js';
12
+ import type {
13
+ ClaimMappingScopesSpec,
14
+ ClaimMappingSpec,
15
+ ExchangeCodeFn,
16
+ IdentityProviderBinding,
17
+ ProviderConfig,
18
+ RefreshTokenFn,
19
+ } from '../identity-provider-binding.js';
20
+ import { encodeSessionToken } from '../middleware/auth.js';
21
+ import type { Session, SessionStoreBinding } from '../session-store-binding.js';
22
+ import type { OauthStateStore } from '../state-store-binding.js';
23
+ import type { AppEnv } from '../types.js';
24
+
25
+ /**
26
+ * Auth routes. Layer OAuth 2.0 / OIDC on top of the static
27
+ * bearer-token infrastructure. Bearer-token routes remain byte-shape-
28
+ * identical; auth flow is caller-plugged via `IdentityProviderBinding`
29
+ * (provider catalog) + `SessionStoreBinding` (session persistence) +
30
+ * `OauthStateStore` (short-lived CSRF/PKCE cache).
31
+ *
32
+ * Every route runs INSIDE the `/v1/*` auth chain and requires either a
33
+ * bearer or session token, EXCEPT `/v1/auth/callback/:providerId` which
34
+ * accepts the redirect from the provider carrying no framework token
35
+ * (that would be a chicken-and-egg — the caller hasn't obtained a
36
+ * session yet). `createApp` (`app.ts`) mounts the callback outside the
37
+ * auth chain so unauthenticated redirects reach it.
38
+ */
39
+ export interface AuthRouterOptions {
40
+ readonly sessionStore: SessionStoreBinding;
41
+ readonly identityProvider: IdentityProviderBinding;
42
+ readonly stateStore: OauthStateStore;
43
+ readonly exchangeCode: ExchangeCodeFn;
44
+ readonly refreshToken?: RefreshTokenFn;
45
+ /**
46
+ * Default TTL for the CSRF/PKCE state cache entries. 10 minutes covers
47
+ * a normal browser hop with margin for slow provider consent screens
48
+ * without keeping a lost row around forever.
49
+ */
50
+ readonly stateTtlMs?: number;
51
+ }
52
+
53
+ const DEFAULT_STATE_TTL_MS = 10 * 60 * 1000;
54
+
55
+ /**
56
+ * Build the auth router. Two Hono routers are returned — one gated
57
+ * (mounted inside the `/v1/*` bearer chain: provider catalog CRUD,
58
+ * login initiation, refresh, logout) and one public-adjacent (mounted
59
+ * outside the bearer chain: callback endpoint the provider redirects
60
+ * to; still tenant-scoped via the state row's `tenantId`).
61
+ *
62
+ * `whoami` lives at `/v1/identity/whoami` and is unconditionally
63
+ * mounted from `identityRouter`.
64
+ */
65
+ export function authRouters(options: AuthRouterOptions): {
66
+ readonly authed: Hono<AppEnv>;
67
+ readonly callback: Hono<AppEnv>;
68
+ } {
69
+ const { sessionStore, identityProvider, stateStore, exchangeCode } = options;
70
+ const refreshToken = options.refreshToken;
71
+ const stateTtlMs = options.stateTtlMs ?? DEFAULT_STATE_TTL_MS;
72
+
73
+ const authed = new Hono<AppEnv>();
74
+
75
+ // ---------- GET /providers ----------
76
+ authed.get('/providers', async (c) => {
77
+ const tenantId = c.get('tenantId') as TenantId;
78
+ const page = await identityProvider.list({ tenantId });
79
+ return c.json({ data: page.data.map(serializeProviderConfig) });
80
+ });
81
+
82
+ // ---------- POST /providers ----------
83
+ authed.post('/providers', async (c) => {
84
+ const requestId = c.get('requestId');
85
+ const tenantId = c.get('tenantId') as TenantId;
86
+
87
+ const parsedJson = await parseJsonBody(c);
88
+ if (parsedJson.kind === 'err') {
89
+ c.status(statusFor('bad-input') as never);
90
+ return c.json(toWireError(parsedJson.error, requestId));
91
+ }
92
+ const parsed = parseProviderConfig(parsedJson.value);
93
+ if (parsed.kind === 'err') {
94
+ c.status(statusFor(parsed.error.code) as never);
95
+ return c.json(toWireError(parsed.error, requestId));
96
+ }
97
+
98
+ const existing = await identityProvider.get({ tenantId, providerId: parsed.value.providerId });
99
+ if (existing !== null) {
100
+ c.status(statusFor('identity-provider-already-registered') as never);
101
+ return c.json(
102
+ toWireError(
103
+ {
104
+ code: 'identity-provider-already-registered',
105
+ message: `Identity provider "${parsed.value.providerId}" is already registered for this tenant`,
106
+ providerId: parsed.value.providerId,
107
+ },
108
+ requestId,
109
+ ),
110
+ );
111
+ }
112
+
113
+ const outcome = await identityProvider.register({ tenantId, config: parsed.value });
114
+ c.status(201);
115
+ return c.json({ providerId: outcome.providerId });
116
+ });
117
+
118
+ // ---------- POST /providers/:providerId/unregister ----------
119
+ authed.post('/providers/:providerId/unregister', async (c) => {
120
+ const requestId = c.get('requestId');
121
+ const tenantId = c.get('tenantId') as TenantId;
122
+ const providerId = c.req.param('providerId');
123
+
124
+ const outcome = await identityProvider.unregister({ tenantId, providerId });
125
+ if (!outcome.unregistered) {
126
+ c.status(statusFor('identity-provider-not-found') as never);
127
+ return c.json(
128
+ toWireError(
129
+ {
130
+ code: 'identity-provider-not-found',
131
+ message: `No identity provider registered with id "${providerId}"`,
132
+ providerId,
133
+ },
134
+ requestId,
135
+ ),
136
+ );
137
+ }
138
+ return c.json({ providerId, unregistered: true });
139
+ });
140
+
141
+ // ---------- POST /login/:providerId ----------
142
+ authed.post('/login/:providerId', async (c) => {
143
+ const requestId = c.get('requestId');
144
+ const tenantId = c.get('tenantId') as TenantId;
145
+ const providerId = c.req.param('providerId');
146
+
147
+ const config = await identityProvider.get({ tenantId, providerId });
148
+ if (config === null) {
149
+ c.status(statusFor('identity-provider-not-found') as never);
150
+ return c.json(
151
+ toWireError(
152
+ {
153
+ code: 'identity-provider-not-found',
154
+ message: `No identity provider registered with id "${providerId}"`,
155
+ providerId,
156
+ },
157
+ requestId,
158
+ ),
159
+ );
160
+ }
161
+
162
+ let redirectUri: string | undefined;
163
+ const parsedBody = await parseOptionalJsonBody(c);
164
+ if (parsedBody.kind === 'err') {
165
+ c.status(statusFor('bad-input') as never);
166
+ return c.json(toWireError(parsedBody.error, requestId));
167
+ }
168
+ if (parsedBody.value !== undefined) {
169
+ const raw = (parsedBody.value as { redirectUri?: unknown }).redirectUri;
170
+ if (raw !== undefined) {
171
+ if (typeof raw !== 'string' || raw.length === 0) {
172
+ c.status(statusFor('bad-input') as never);
173
+ return c.json(
174
+ toWireError(
175
+ { code: 'bad-input', message: '`redirectUri` must be a non-empty string' },
176
+ requestId,
177
+ ),
178
+ );
179
+ }
180
+ redirectUri = raw;
181
+ }
182
+ }
183
+
184
+ const state = base64Url(randomBytes(32));
185
+ const codeVerifier = base64Url(randomBytes(64));
186
+ const codeChallenge = base64Url(createHash('sha256').update(codeVerifier).digest());
187
+ const effectiveRedirect =
188
+ redirectUri ??
189
+ (typeof config.metadata?.defaultRedirectUri === 'string'
190
+ ? String(config.metadata?.defaultRedirectUri)
191
+ : '');
192
+ if (effectiveRedirect.length === 0) {
193
+ c.status(statusFor('bad-input') as never);
194
+ return c.json(
195
+ toWireError(
196
+ {
197
+ code: 'bad-input',
198
+ message:
199
+ '`redirectUri` must be supplied in the body or via `metadata.defaultRedirectUri` on the provider config',
200
+ },
201
+ requestId,
202
+ ),
203
+ );
204
+ }
205
+
206
+ // OAuth 2.1 BCP redirect-URI allowlist enforcement. Absent /
207
+ // empty list means pass-through (no allowlist check); populated
208
+ // list requires an exact-string match against the effective
209
+ // redirect URI (either the body-supplied value or the resolved
210
+ // `metadata.defaultRedirectUri`). This is the primary gate — the
211
+ // callback route re-verifies against the same list for
212
+ // belt-and-suspenders defense in case the allowlist tightened
213
+ // between login and callback.
214
+ if (
215
+ config.allowedRedirectUris !== undefined &&
216
+ config.allowedRedirectUris.length > 0 &&
217
+ !config.allowedRedirectUris.includes(effectiveRedirect)
218
+ ) {
219
+ c.status(statusFor('redirect-uri-not-allowed') as never);
220
+ return c.json(
221
+ toWireError(
222
+ {
223
+ code: 'redirect-uri-not-allowed',
224
+ message: `redirect_uri "${effectiveRedirect}" is not in the provider's allowedRedirectUris list`,
225
+ providerId,
226
+ },
227
+ requestId,
228
+ ),
229
+ );
230
+ }
231
+
232
+ await stateStore.put({
233
+ state,
234
+ tenantId,
235
+ providerId,
236
+ codeVerifier,
237
+ redirectUri: effectiveRedirect,
238
+ expiresAt: Date.now() + stateTtlMs,
239
+ });
240
+
241
+ const authorizationUrl = buildAuthorizationUrl({
242
+ config,
243
+ state,
244
+ codeChallenge,
245
+ redirectUri: effectiveRedirect,
246
+ });
247
+ return c.json({
248
+ authorizationUrl,
249
+ state,
250
+ codeChallenge,
251
+ codeChallengeMethod: 'S256',
252
+ });
253
+ });
254
+
255
+ // ---------- POST /refresh ----------
256
+ authed.post('/refresh', async (c) => {
257
+ const requestId = c.get('requestId');
258
+ const tenantId = c.get('tenantId') as TenantId;
259
+ const sessionId = c.get('sessionId') as SessionId | undefined;
260
+ if (sessionId === undefined) {
261
+ c.status(statusFor('auth-not-session-token') as never);
262
+ return c.json(
263
+ toWireError(
264
+ {
265
+ code: 'auth-not-session-token',
266
+ message:
267
+ 'Refresh is only valid for session tokens (`kgi_sk_*`); bearer tokens are managed via `/v1/tokens`',
268
+ },
269
+ requestId,
270
+ ),
271
+ );
272
+ }
273
+ const current = await sessionStore.get({ tenantId, sessionId });
274
+ if (current === null || current.revokedAt !== undefined) {
275
+ c.status(statusFor('session-not-found') as never);
276
+ return c.json(
277
+ toWireError(
278
+ { code: 'session-not-found', message: 'Session no longer exists', sessionId },
279
+ requestId,
280
+ ),
281
+ );
282
+ }
283
+
284
+ let created: { readonly session: Session; readonly rawToken: string };
285
+ if (refreshToken !== undefined && current.refreshToken !== undefined) {
286
+ let rotated: Awaited<ReturnType<RefreshTokenFn>>;
287
+ try {
288
+ rotated = await refreshToken({
289
+ tenantId,
290
+ providerId: current.providerId,
291
+ refreshToken: current.refreshToken,
292
+ });
293
+ } catch (err) {
294
+ c.status(statusFor('oauth-refresh-failed') as never);
295
+ return c.json(
296
+ toWireError(
297
+ {
298
+ code: 'oauth-refresh-failed',
299
+ message: err instanceof Error ? err.message : 'refresh failed',
300
+ },
301
+ requestId,
302
+ ),
303
+ );
304
+ }
305
+ const createdSession = await sessionStore.create({
306
+ tenantId,
307
+ userId: current.userId,
308
+ providerId: current.providerId,
309
+ accessToken: rotated.accessToken,
310
+ ...(rotated.refreshToken !== undefined && { refreshToken: rotated.refreshToken }),
311
+ expiresAt: rotated.expiresAt.toISOString() as never,
312
+ scopes: rotated.scopes,
313
+ ...(rotated.claims !== undefined && { metadata: rotated.claims }),
314
+ });
315
+ const fresh = await sessionStore.get({ tenantId, sessionId: createdSession.sessionId });
316
+ if (fresh === null) throw new Error('session vanished immediately after create');
317
+ created = { session: fresh, rawToken: encodeSessionToken(createdSession.sessionId) };
318
+ } else {
319
+ // Rotate the framework token only; keep provider tokens as-is.
320
+ const createdSession = await sessionStore.create({
321
+ tenantId,
322
+ userId: current.userId,
323
+ providerId: current.providerId,
324
+ accessToken: current.accessToken,
325
+ ...(current.refreshToken !== undefined && { refreshToken: current.refreshToken }),
326
+ expiresAt: current.expiresAt,
327
+ scopes: current.scopes,
328
+ ...(current.metadata !== undefined && { metadata: current.metadata }),
329
+ });
330
+ const fresh = await sessionStore.get({ tenantId, sessionId: createdSession.sessionId });
331
+ if (fresh === null) throw new Error('session vanished immediately after create');
332
+ created = { session: fresh, rawToken: encodeSessionToken(createdSession.sessionId) };
333
+ }
334
+
335
+ // OAuth 2.1 BCP: mark the old session as ROTATED (not just revoked)
336
+ // so the middleware can return `401 refresh-token-invalid` on reuse
337
+ // rather than the generic `401 auth-revoked`. Reuse detection lets
338
+ // compliant clients retry with the fresh token instead of forcing
339
+ // a full re-auth.
340
+ await sessionStore.revoke({ tenantId, sessionId, reason: 'rotate' });
341
+
342
+ return c.json({
343
+ sessionToken: created.rawToken,
344
+ sessionId: created.session.id,
345
+ expiresAt: created.session.expiresAt,
346
+ });
347
+ });
348
+
349
+ // ---------- POST /logout ----------
350
+ authed.post('/logout', async (c) => {
351
+ const requestId = c.get('requestId');
352
+ const tenantId = c.get('tenantId') as TenantId;
353
+ const sessionId = c.get('sessionId') as SessionId | undefined;
354
+ if (sessionId === undefined) {
355
+ c.status(statusFor('auth-not-session-token') as never);
356
+ return c.json(
357
+ toWireError(
358
+ {
359
+ code: 'auth-not-session-token',
360
+ message:
361
+ 'Logout is only valid for session tokens (`kgi_sk_*`); bearer tokens are managed via `/v1/tokens`',
362
+ },
363
+ requestId,
364
+ ),
365
+ );
366
+ }
367
+ const outcome = await sessionStore.revoke({ tenantId, sessionId });
368
+ return c.json({ sessionId, revoked: outcome.revoked });
369
+ });
370
+
371
+ const callback = new Hono<AppEnv>();
372
+
373
+ // ---------- POST /callback/:providerId (mounted outside the bearer chain) ----------
374
+ callback.post('/:providerId', async (c) => {
375
+ const requestId = c.get('requestId');
376
+ const providerId = c.req.param('providerId');
377
+
378
+ const parsedJson = await parseJsonBody(c);
379
+ if (parsedJson.kind === 'err') {
380
+ c.status(statusFor('bad-input') as never);
381
+ return c.json(toWireError(parsedJson.error, requestId));
382
+ }
383
+ const parsedCallback = parseCallbackBody(parsedJson.value);
384
+ if (parsedCallback.kind === 'err') {
385
+ c.status(statusFor(parsedCallback.error.code) as never);
386
+ return c.json(toWireError(parsedCallback.error, requestId));
387
+ }
388
+ const { code, state } = parsedCallback.value;
389
+
390
+ // `state` alone is uniquely identifying (256 bits of entropy). The
391
+ // store returns the row's `tenantId` so we know which tenant the
392
+ // callback belongs to — cross-checking `providerId` guards against
393
+ // a stolen `state` being replayed against the wrong provider mount.
394
+ const entry = await stateStore.take({ state, providerId });
395
+ if (entry === null) {
396
+ c.status(statusFor('oauth-state-invalid') as never);
397
+ return c.json(
398
+ toWireError(
399
+ {
400
+ code: 'oauth-state-invalid',
401
+ message: '`state` is unknown, expired, or already consumed',
402
+ },
403
+ requestId,
404
+ ),
405
+ );
406
+ }
407
+
408
+ // Belt-and-suspenders redirect-URI check. Login already validated
409
+ // the stored redirect_uri against `allowedRedirectUris` when the
410
+ // row was written, but the allowlist may have tightened between
411
+ // login and callback — if so, refuse the exchange rather than
412
+ // handing the caller a session under a redirect the tenant no
413
+ // longer trusts. Also runs when the provider config went missing
414
+ // (unregister mid-flight) so we don't silently proceed.
415
+ const currentConfig = await identityProvider.get({
416
+ tenantId: entry.tenantId,
417
+ providerId,
418
+ });
419
+ if (
420
+ currentConfig !== null &&
421
+ currentConfig.allowedRedirectUris !== undefined &&
422
+ currentConfig.allowedRedirectUris.length > 0 &&
423
+ !currentConfig.allowedRedirectUris.includes(entry.redirectUri)
424
+ ) {
425
+ c.status(statusFor('redirect-uri-mismatch') as never);
426
+ return c.json(
427
+ toWireError(
428
+ {
429
+ code: 'redirect-uri-mismatch',
430
+ message:
431
+ "redirect_uri from the login state row is not in the provider's current allowedRedirectUris list",
432
+ providerId,
433
+ },
434
+ requestId,
435
+ ),
436
+ );
437
+ }
438
+
439
+ let outcome: Awaited<ReturnType<ExchangeCodeFn>>;
440
+ try {
441
+ outcome = await exchangeCode({
442
+ tenantId: entry.tenantId,
443
+ providerId,
444
+ code,
445
+ codeVerifier: entry.codeVerifier,
446
+ redirectUri: entry.redirectUri,
447
+ });
448
+ } catch (err) {
449
+ c.status(statusFor('oauth-code-exchange-failed') as never);
450
+ return c.json(
451
+ toWireError(
452
+ {
453
+ code: 'oauth-code-exchange-failed',
454
+ message: err instanceof Error ? err.message : 'code exchange failed',
455
+ },
456
+ requestId,
457
+ ),
458
+ );
459
+ }
460
+
461
+ const created = await sessionStore.create({
462
+ tenantId: entry.tenantId,
463
+ userId: outcome.userId as unknown as UserId,
464
+ providerId,
465
+ accessToken: outcome.accessToken,
466
+ ...(outcome.refreshToken !== undefined && { refreshToken: outcome.refreshToken }),
467
+ expiresAt: outcome.expiresAt.toISOString() as never,
468
+ scopes: outcome.scopes,
469
+ ...(outcome.claims !== undefined && { metadata: outcome.claims }),
470
+ });
471
+
472
+ c.status(201);
473
+ return c.json({
474
+ sessionToken: encodeSessionToken(created.sessionId),
475
+ sessionId: created.sessionId,
476
+ expiresAt: created.expiresAt,
477
+ });
478
+ });
479
+
480
+ return { authed, callback };
481
+ }
482
+
483
+ // ---------- helpers ----------
484
+
485
+ function base64Url(buf: Buffer): string {
486
+ return buf.toString('base64url');
487
+ }
488
+
489
+ function buildAuthorizationUrl(input: {
490
+ readonly config: ProviderConfig;
491
+ readonly state: string;
492
+ readonly codeChallenge: string;
493
+ readonly redirectUri: string;
494
+ }): string {
495
+ const { config, state, codeChallenge, redirectUri } = input;
496
+ const url = new URL(config.authorizationEndpoint);
497
+ url.searchParams.set('response_type', 'code');
498
+ url.searchParams.set('client_id', config.clientId);
499
+ url.searchParams.set('redirect_uri', redirectUri);
500
+ url.searchParams.set('scope', config.scopes.join(' '));
501
+ url.searchParams.set('state', state);
502
+ url.searchParams.set('code_challenge', codeChallenge);
503
+ url.searchParams.set('code_challenge_method', 'S256');
504
+ return url.toString();
505
+ }
506
+
507
+ function serializeProviderConfig(c: ProviderConfig): Record<string, unknown> {
508
+ // Every field except clientSecretRef (which is a REFERENCE, not the
509
+ // secret itself) is safe to return. Clients need enough to render an
510
+ // "sign in with X" button and know what scopes they're granting.
511
+ return {
512
+ providerId: c.providerId,
513
+ kind: c.kind,
514
+ clientId: c.clientId,
515
+ clientSecretRef: c.clientSecretRef,
516
+ authorizationEndpoint: c.authorizationEndpoint,
517
+ tokenEndpoint: c.tokenEndpoint,
518
+ ...(c.userinfoEndpoint !== undefined && { userinfoEndpoint: c.userinfoEndpoint }),
519
+ scopes: c.scopes,
520
+ ...(c.allowedRedirectUris !== undefined && {
521
+ allowedRedirectUris: c.allowedRedirectUris,
522
+ }),
523
+ ...(c.claimMapping !== undefined && { claimMapping: c.claimMapping }),
524
+ ...(c.metadata !== undefined && { metadata: c.metadata }),
525
+ };
526
+ }
527
+
528
+ type ParsedOk<T> = { readonly kind: 'ok'; readonly value: T };
529
+ type ParsedErr = {
530
+ readonly kind: 'err';
531
+ readonly error: { readonly code: string; readonly message: string };
532
+ };
533
+
534
+ async function parseJsonBody(c: Context<AppEnv>): Promise<ParsedOk<unknown> | ParsedErr> {
535
+ try {
536
+ const text = await c.req.text();
537
+ if (text.length === 0) {
538
+ return { kind: 'err', error: { code: 'bad-input', message: 'Request body is required' } };
539
+ }
540
+ return { kind: 'ok', value: JSON.parse(text) };
541
+ } catch {
542
+ return {
543
+ kind: 'err',
544
+ error: { code: 'bad-input', message: 'Request body must be valid JSON' },
545
+ };
546
+ }
547
+ }
548
+
549
+ async function parseOptionalJsonBody(
550
+ c: Context<AppEnv>,
551
+ ): Promise<ParsedOk<unknown | undefined> | ParsedErr> {
552
+ try {
553
+ const text = await c.req.text();
554
+ if (text.length === 0) return { kind: 'ok', value: undefined };
555
+ return { kind: 'ok', value: JSON.parse(text) };
556
+ } catch {
557
+ return {
558
+ kind: 'err',
559
+ error: { code: 'bad-input', message: 'Request body must be valid JSON' },
560
+ };
561
+ }
562
+ }
563
+
564
+ function parseProviderConfig(body: unknown): ParsedOk<ProviderConfig> | ParsedErr {
565
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
566
+ return { kind: 'err', error: { code: 'bad-input', message: 'Request body must be an object' } };
567
+ }
568
+ const b = body as Record<string, unknown>;
569
+ const requiredStr = (k: string): string | ParsedErr => {
570
+ const v = b[k];
571
+ if (typeof v !== 'string' || v.length === 0) {
572
+ return {
573
+ kind: 'err',
574
+ error: {
575
+ code: 'invalid-provider-config',
576
+ message: `Field \`${k}\` must be a non-empty string`,
577
+ },
578
+ };
579
+ }
580
+ return v;
581
+ };
582
+ const providerId = requiredStr('providerId');
583
+ if (typeof providerId !== 'string') return providerId;
584
+ const clientId = requiredStr('clientId');
585
+ if (typeof clientId !== 'string') return clientId;
586
+ const clientSecretRef = requiredStr('clientSecretRef');
587
+ if (typeof clientSecretRef !== 'string') return clientSecretRef;
588
+ const authorizationEndpoint = requiredStr('authorizationEndpoint');
589
+ if (typeof authorizationEndpoint !== 'string') return authorizationEndpoint;
590
+ const tokenEndpoint = requiredStr('tokenEndpoint');
591
+ if (typeof tokenEndpoint !== 'string') return tokenEndpoint;
592
+
593
+ const kindRaw = b.kind;
594
+ if (kindRaw !== 'oauth2' && kindRaw !== 'oidc') {
595
+ return {
596
+ kind: 'err',
597
+ error: {
598
+ code: 'invalid-provider-config',
599
+ message: '`kind` must be `oauth2` or `oidc`',
600
+ },
601
+ };
602
+ }
603
+
604
+ const scopesRaw = b.scopes;
605
+ if (!Array.isArray(scopesRaw) || scopesRaw.some((s) => typeof s !== 'string')) {
606
+ return {
607
+ kind: 'err',
608
+ error: {
609
+ code: 'invalid-provider-config',
610
+ message: '`scopes` must be an array of strings',
611
+ },
612
+ };
613
+ }
614
+ const scopes = scopesRaw as string[];
615
+
616
+ const userinfoEndpointRaw = b.userinfoEndpoint;
617
+ if (userinfoEndpointRaw !== undefined && typeof userinfoEndpointRaw !== 'string') {
618
+ return {
619
+ kind: 'err',
620
+ error: {
621
+ code: 'invalid-provider-config',
622
+ message: '`userinfoEndpoint` must be a string when supplied',
623
+ },
624
+ };
625
+ }
626
+ const metadataRaw = b.metadata;
627
+ if (
628
+ metadataRaw !== undefined &&
629
+ (metadataRaw === null || typeof metadataRaw !== 'object' || Array.isArray(metadataRaw))
630
+ ) {
631
+ return {
632
+ kind: 'err',
633
+ error: {
634
+ code: 'invalid-provider-config',
635
+ message: '`metadata` must be an object when supplied',
636
+ },
637
+ };
638
+ }
639
+
640
+ const allowedRedirectUrisRaw = b.allowedRedirectUris;
641
+ let allowedRedirectUris: readonly string[] | undefined;
642
+ if (allowedRedirectUrisRaw !== undefined) {
643
+ if (
644
+ !Array.isArray(allowedRedirectUrisRaw) ||
645
+ allowedRedirectUrisRaw.some((s) => typeof s !== 'string' || s.length === 0)
646
+ ) {
647
+ return {
648
+ kind: 'err',
649
+ error: {
650
+ code: 'invalid-provider-config',
651
+ message: '`allowedRedirectUris` must be an array of non-empty strings when supplied',
652
+ },
653
+ };
654
+ }
655
+ allowedRedirectUris = allowedRedirectUrisRaw as string[];
656
+ }
657
+
658
+ const claimMappingRaw = b.claimMapping;
659
+ let claimMapping: ClaimMappingSpec | undefined;
660
+ if (claimMappingRaw !== undefined) {
661
+ const parsed = parseClaimMapping(claimMappingRaw);
662
+ if (parsed.kind === 'err') return parsed;
663
+ claimMapping = parsed.value;
664
+ }
665
+
666
+ return {
667
+ kind: 'ok',
668
+ value: {
669
+ providerId,
670
+ kind: kindRaw,
671
+ clientId,
672
+ clientSecretRef,
673
+ authorizationEndpoint,
674
+ tokenEndpoint,
675
+ ...(typeof userinfoEndpointRaw === 'string' && { userinfoEndpoint: userinfoEndpointRaw }),
676
+ scopes,
677
+ ...(allowedRedirectUris !== undefined && { allowedRedirectUris }),
678
+ ...(claimMapping !== undefined && { claimMapping }),
679
+ ...(metadataRaw !== undefined && { metadata: metadataRaw as Record<string, unknown> }),
680
+ },
681
+ };
682
+ }
683
+
684
+ function parseClaimMapping(raw: unknown): ParsedOk<ClaimMappingSpec> | ParsedErr {
685
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
686
+ return {
687
+ kind: 'err',
688
+ error: {
689
+ code: 'invalid-provider-config',
690
+ message: '`claimMapping` must be an object when supplied',
691
+ },
692
+ };
693
+ }
694
+ const m = raw as Record<string, unknown>;
695
+ const optionalString = (
696
+ key: keyof ClaimMappingSpec,
697
+ value: unknown,
698
+ ): string | ParsedErr | undefined => {
699
+ if (value === undefined) return undefined;
700
+ if (typeof value !== 'string' || value.length === 0) {
701
+ return {
702
+ kind: 'err',
703
+ error: {
704
+ code: 'invalid-provider-config',
705
+ message: `\`claimMapping.${String(key)}\` must be a non-empty string when supplied`,
706
+ },
707
+ };
708
+ }
709
+ return value;
710
+ };
711
+ const userId = optionalString('userId', m.userId);
712
+ if (typeof userId !== 'string' && userId !== undefined) return userId;
713
+ const email = optionalString('email', m.email);
714
+ if (typeof email !== 'string' && email !== undefined) return email;
715
+ const displayName = optionalString('displayName', m.displayName);
716
+ if (typeof displayName !== 'string' && displayName !== undefined) return displayName;
717
+
718
+ let scopes: ClaimMappingScopesSpec | undefined;
719
+ if (m.scopes !== undefined) {
720
+ const s = m.scopes;
721
+ if (s === null || typeof s !== 'object' || Array.isArray(s)) {
722
+ return {
723
+ kind: 'err',
724
+ error: {
725
+ code: 'invalid-provider-config',
726
+ message: '`claimMapping.scopes` must be an object when supplied',
727
+ },
728
+ };
729
+ }
730
+ const so = s as Record<string, unknown>;
731
+ if (typeof so.claim !== 'string' || so.claim.length === 0) {
732
+ return {
733
+ kind: 'err',
734
+ error: {
735
+ code: 'invalid-provider-config',
736
+ message: '`claimMapping.scopes.claim` must be a non-empty string',
737
+ },
738
+ };
739
+ }
740
+ if (so.delimiter !== undefined && typeof so.delimiter !== 'string') {
741
+ return {
742
+ kind: 'err',
743
+ error: {
744
+ code: 'invalid-provider-config',
745
+ message: '`claimMapping.scopes.delimiter` must be a string when supplied',
746
+ },
747
+ };
748
+ }
749
+ scopes = {
750
+ claim: so.claim,
751
+ ...(typeof so.delimiter === 'string' && { delimiter: so.delimiter }),
752
+ };
753
+ }
754
+
755
+ let metadata: readonly string[] | undefined;
756
+ if (m.metadata !== undefined) {
757
+ if (
758
+ !Array.isArray(m.metadata) ||
759
+ m.metadata.some((s) => typeof s !== 'string' || s.length === 0)
760
+ ) {
761
+ return {
762
+ kind: 'err',
763
+ error: {
764
+ code: 'invalid-provider-config',
765
+ message: '`claimMapping.metadata` must be an array of non-empty strings when supplied',
766
+ },
767
+ };
768
+ }
769
+ metadata = m.metadata as string[];
770
+ }
771
+
772
+ return {
773
+ kind: 'ok',
774
+ value: {
775
+ ...(userId !== undefined && { userId }),
776
+ ...(email !== undefined && { email }),
777
+ ...(displayName !== undefined && { displayName }),
778
+ ...(scopes !== undefined && { scopes }),
779
+ ...(metadata !== undefined && { metadata }),
780
+ },
781
+ };
782
+ }
783
+
784
+ function parseCallbackBody(
785
+ body: unknown,
786
+ ): ParsedOk<{ readonly code: string; readonly state: string }> | ParsedErr {
787
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
788
+ return { kind: 'err', error: { code: 'bad-input', message: 'Request body must be an object' } };
789
+ }
790
+ const b = body as Record<string, unknown>;
791
+ const code = b.code;
792
+ if (typeof code !== 'string' || code.length === 0) {
793
+ return {
794
+ kind: 'err',
795
+ error: { code: 'bad-input', message: '`code` must be a non-empty string' },
796
+ };
797
+ }
798
+ const state = b.state;
799
+ if (typeof state !== 'string' || state.length === 0) {
800
+ return {
801
+ kind: 'err',
802
+ error: { code: 'bad-input', message: '`state` must be a non-empty string' },
803
+ };
804
+ }
805
+ return { kind: 'ok', value: { code, state } };
806
+ }