@nylorun/runtime 0.13.0-beta → 0.15.0-beta

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 (309) hide show
  1. package/CHANGELOG.md +434 -0
  2. package/README.md +137 -41
  3. package/dist/adapters/execution/restate.d.ts +7 -2
  4. package/dist/adapters/execution/restate.js +40 -10
  5. package/dist/adapters/replication/pgoutput.d.ts +21 -0
  6. package/dist/adapters/replication/pgoutput.js +170 -0
  7. package/dist/adapters/streams/s2.js +37 -20
  8. package/dist/admin-openapi.json +570 -0
  9. package/dist/api/a2a/card.d.ts +15 -0
  10. package/dist/api/a2a/card.js +24 -0
  11. package/dist/api/a2a/endpoint.d.ts +8 -0
  12. package/dist/api/a2a/endpoint.js +81 -0
  13. package/dist/api/a2a/protocol.d.ts +144 -0
  14. package/dist/api/a2a/protocol.js +188 -0
  15. package/dist/api/a2a/routes.d.ts +19 -0
  16. package/dist/api/a2a/routes.js +289 -0
  17. package/dist/api/a2a/tasks.d.ts +50 -0
  18. package/dist/api/a2a/tasks.js +186 -0
  19. package/dist/api/ag-ui/endpoint.d.ts +8 -0
  20. package/dist/api/ag-ui/endpoint.js +126 -0
  21. package/dist/api/ag-ui/history.d.ts +7 -0
  22. package/dist/api/ag-ui/history.js +60 -0
  23. package/dist/api/ag-ui/routes.d.ts +32 -0
  24. package/dist/api/ag-ui/routes.js +305 -0
  25. package/dist/api/ag-ui/session-id.d.ts +1 -0
  26. package/dist/api/ag-ui/session-id.js +12 -0
  27. package/dist/api/ag-ui/sse.d.ts +9 -0
  28. package/dist/api/ag-ui/sse.js +7 -0
  29. package/dist/api/ag-ui/translate.d.ts +38 -0
  30. package/dist/api/ag-ui/translate.js +222 -0
  31. package/dist/api/components.d.ts +8065 -0
  32. package/dist/api/components.js +82 -0
  33. package/dist/api/gate/keys.d.ts +14 -0
  34. package/dist/api/gate/keys.js +59 -0
  35. package/dist/api/gate/routes.d.ts +34 -0
  36. package/dist/api/gate/routes.js +235 -0
  37. package/dist/api/http/app.d.ts +18 -0
  38. package/dist/api/http/app.js +58 -0
  39. package/dist/api/http/body.d.ts +4 -0
  40. package/dist/api/http/body.js +37 -0
  41. package/dist/api/http/define.d.ts +39 -0
  42. package/dist/api/http/define.js +122 -0
  43. package/dist/api/http/respond.d.ts +9 -0
  44. package/dist/api/http/respond.js +44 -0
  45. package/dist/api/http/routes/access.d.ts +24 -0
  46. package/dist/api/http/routes/access.js +266 -0
  47. package/dist/api/http/routes/actions.d.ts +8 -0
  48. package/dist/api/http/routes/actions.js +76 -0
  49. package/dist/api/http/routes/endpoints.d.ts +7 -0
  50. package/dist/api/http/routes/endpoints.js +100 -0
  51. package/dist/api/http/routes/sessions.d.ts +3 -0
  52. package/dist/api/http/routes/sessions.js +205 -0
  53. package/dist/api/http/routes/tenant.d.ts +11 -0
  54. package/dist/api/http/routes/tenant.js +244 -0
  55. package/dist/api/http/routes/vaults.d.ts +8 -0
  56. package/dist/api/http/routes/vaults.js +167 -0
  57. package/dist/api/openapi.d.ts +19 -0
  58. package/dist/api/openapi.js +137 -0
  59. package/dist/api/route.d.ts +12 -0
  60. package/dist/api/route.js +4 -0
  61. package/dist/core/bearer.d.ts +8 -0
  62. package/dist/core/bearer.js +11 -0
  63. package/dist/core/flow-host.d.ts +7 -15
  64. package/dist/core/flow-host.js +16 -41
  65. package/dist/core/sandbox-routes.d.ts +12 -7
  66. package/dist/core/sandbox-routes.js +24 -25
  67. package/dist/execution/memory.d.ts +8 -5
  68. package/dist/execution/memory.js +33 -13
  69. package/dist/execution/types.d.ts +38 -6
  70. package/dist/execution/types.js +12 -3
  71. package/dist/gates/contract.d.ts +51 -0
  72. package/dist/gates/contract.js +37 -0
  73. package/dist/gates/handler.d.ts +21 -0
  74. package/dist/gates/handler.js +55 -0
  75. package/dist/gates/http-client.d.ts +12 -0
  76. package/dist/gates/http-client.js +160 -0
  77. package/dist/gates/in-process.d.ts +32 -0
  78. package/dist/gates/in-process.js +42 -0
  79. package/dist/gates/inflight.d.ts +39 -0
  80. package/dist/gates/inflight.js +70 -0
  81. package/dist/gates/mcp-handler.d.ts +35 -0
  82. package/dist/gates/mcp-handler.js +142 -0
  83. package/dist/gates/meter.d.ts +24 -0
  84. package/dist/gates/meter.js +165 -0
  85. package/dist/gates/model-gate.d.ts +45 -0
  86. package/dist/gates/model-gate.js +1 -0
  87. package/dist/gates/tenant-vaults.d.ts +64 -0
  88. package/dist/gates/tenant-vaults.js +94 -0
  89. package/dist/gates/tool-calls.d.ts +51 -0
  90. package/dist/gates/tool-calls.js +64 -0
  91. package/dist/gates/tool-client.d.ts +12 -0
  92. package/dist/gates/tool-client.js +201 -0
  93. package/dist/gates/tool-contract.d.ts +116 -0
  94. package/dist/gates/tool-contract.js +64 -0
  95. package/dist/gates/tool-gate.d.ts +45 -0
  96. package/dist/gates/tool-gate.js +10 -0
  97. package/dist/host/admin-api.d.ts +23 -0
  98. package/dist/host/admin-api.js +74 -0
  99. package/dist/host/app.d.ts +34 -0
  100. package/dist/host/app.js +181 -0
  101. package/dist/host/config-for.d.ts +3 -1
  102. package/dist/host/config-for.js +3 -2
  103. package/dist/host/config.d.ts +11 -0
  104. package/dist/host/cors.d.ts +27 -0
  105. package/dist/host/cors.js +64 -0
  106. package/dist/host/create-host.d.ts +36 -8
  107. package/dist/host/create-host.js +161 -330
  108. package/dist/host/execution.d.ts +20 -18
  109. package/dist/host/execution.js +2 -2
  110. package/dist/host/gates.d.ts +58 -0
  111. package/dist/host/gates.js +127 -0
  112. package/dist/host/http.d.ts +20 -4
  113. package/dist/host/http.js +81 -23
  114. package/dist/host/index.d.ts +1 -1
  115. package/dist/host/main.js +189 -58
  116. package/dist/host/stack-config.d.ts +91 -19
  117. package/dist/host/stack-config.js +246 -17
  118. package/dist/infra/database.d.ts +2 -2
  119. package/dist/infra/execution.d.ts +5 -5
  120. package/dist/infra/execution.js +8 -6
  121. package/dist/infra/index.d.ts +1 -1
  122. package/dist/infra/workers.d.ts +17 -17
  123. package/dist/infra/workers.js +9 -9
  124. package/dist/keys/client.d.ts +9 -0
  125. package/dist/keys/client.js +70 -0
  126. package/dist/keys/contract.d.ts +24 -0
  127. package/dist/keys/contract.js +12 -0
  128. package/dist/keys/keys.d.ts +41 -0
  129. package/dist/keys/keys.js +50 -0
  130. package/dist/mcp/connect.d.ts +43 -3
  131. package/dist/mcp/connect.js +12 -5
  132. package/dist/mcp/pool.d.ts +39 -3
  133. package/dist/mcp/pool.js +74 -11
  134. package/dist/model/catalog.d.ts +2 -10
  135. package/dist/model/classify.d.ts +13 -0
  136. package/dist/model/classify.js +61 -0
  137. package/dist/model/models.d.ts +10 -1
  138. package/dist/model/models.js +14 -6
  139. package/dist/model/pi-model.d.ts +17 -2
  140. package/dist/model/pi-model.js +366 -222
  141. package/dist/openapi.json +25889 -0
  142. package/dist/record/append.d.ts +51 -0
  143. package/dist/record/append.js +20 -0
  144. package/dist/record/envelope.d.ts +19 -0
  145. package/dist/record/envelope.js +49 -0
  146. package/dist/record/index.d.ts +11 -0
  147. package/dist/record/index.js +11 -0
  148. package/dist/sandbox/manager.d.ts +13 -1
  149. package/dist/sandbox/manager.js +8 -11
  150. package/dist/sandbox/resolve.d.ts +33 -0
  151. package/dist/sandbox/resolve.js +81 -0
  152. package/dist/sandbox/session-sandbox.d.ts +11 -0
  153. package/dist/sandbox/session-sandbox.js +51 -0
  154. package/dist/sandbox/share.d.ts +14 -2
  155. package/dist/sandbox/share.js +33 -3
  156. package/dist/sandbox/tenant-config.d.ts +37 -0
  157. package/dist/sandbox/tenant-config.js +51 -0
  158. package/dist/sandbox/tools.d.ts +7 -9
  159. package/dist/sandbox/tools.js +11 -3
  160. package/dist/sandbox/types.d.ts +8 -2
  161. package/dist/store/postgres/connect.d.ts +25 -9
  162. package/dist/store/postgres/connect.js +27 -10
  163. package/dist/store/postgres/db.d.ts +16 -0
  164. package/dist/store/postgres/db.js +28 -0
  165. package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
  166. package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
  167. package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
  168. package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
  169. package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
  170. package/dist/store/postgres/migrate.d.ts +68 -0
  171. package/dist/store/postgres/migrate.js +183 -0
  172. package/dist/store/postgres/record-writer.d.ts +4 -0
  173. package/dist/store/postgres/record-writer.js +34 -0
  174. package/dist/store/postgres/record.d.ts +5 -0
  175. package/dist/store/postgres/record.js +60 -0
  176. package/dist/store/postgres/schema.d.ts +3096 -0
  177. package/dist/store/postgres/schema.js +363 -0
  178. package/dist/store/postgres/store.d.ts +2 -4
  179. package/dist/store/postgres/store.js +590 -434
  180. package/dist/store/postgres/tenant.d.ts +56 -0
  181. package/dist/store/postgres/tenant.js +154 -0
  182. package/dist/store/types.d.ts +183 -132
  183. package/dist/store/types.js +0 -1
  184. package/dist/streams/basin.d.ts +18 -1
  185. package/dist/streams/basin.js +34 -2
  186. package/dist/streams/control.d.ts +18 -0
  187. package/dist/streams/control.js +33 -0
  188. package/dist/streams/memory.d.ts +3 -1
  189. package/dist/streams/memory.js +14 -8
  190. package/dist/streams/relay/core.d.ts +35 -0
  191. package/dist/streams/relay/core.js +366 -0
  192. package/dist/streams/relay/types.d.ts +78 -0
  193. package/dist/streams/relay/types.js +8 -0
  194. package/dist/streams/types.d.ts +71 -36
  195. package/dist/streams/types.js +18 -53
  196. package/dist/tenant/access-policy.d.ts +30 -0
  197. package/dist/tenant/access-policy.js +52 -0
  198. package/dist/tenant/actions.d.ts +10 -149
  199. package/dist/tenant/actions.js +5 -167
  200. package/dist/tenant/advance.d.ts +7 -2
  201. package/dist/tenant/advance.js +125 -14
  202. package/dist/tenant/auth.d.ts +19 -17
  203. package/dist/tenant/auth.js +90 -93
  204. package/dist/tenant/browser.d.ts +15 -0
  205. package/dist/tenant/browser.js +39 -0
  206. package/dist/tenant/cause.d.ts +25 -0
  207. package/dist/tenant/cause.js +45 -0
  208. package/dist/tenant/commands.d.ts +18 -2
  209. package/dist/tenant/commands.js +111 -115
  210. package/dist/tenant/context.d.ts +101 -21
  211. package/dist/tenant/context.js +14 -10
  212. package/dist/tenant/delivery-token.d.ts +41 -0
  213. package/dist/tenant/delivery-token.js +104 -0
  214. package/dist/tenant/delivery.d.ts +62 -0
  215. package/dist/tenant/delivery.js +481 -0
  216. package/dist/tenant/effects.d.ts +20 -1
  217. package/dist/tenant/effects.js +159 -42
  218. package/dist/tenant/endpoints.d.ts +67 -0
  219. package/dist/tenant/endpoints.js +90 -0
  220. package/dist/tenant/ephemeral.d.ts +22 -7
  221. package/dist/tenant/ephemeral.js +83 -125
  222. package/dist/tenant/history.d.ts +64 -0
  223. package/dist/tenant/history.js +120 -0
  224. package/dist/tenant/http.d.ts +8 -9
  225. package/dist/tenant/http.js +13 -27
  226. package/dist/tenant/jwt.d.ts +16 -0
  227. package/dist/tenant/jwt.js +50 -0
  228. package/dist/tenant/model-setting.d.ts +2 -2
  229. package/dist/tenant/module.d.ts +17 -28
  230. package/dist/tenant/module.js +133 -279
  231. package/dist/tenant/outbound.d.ts +44 -0
  232. package/dist/tenant/outbound.js +127 -0
  233. package/dist/tenant/paths.d.ts +11 -16
  234. package/dist/tenant/paths.js +24 -68
  235. package/dist/tenant/pool.d.ts +1 -3
  236. package/dist/tenant/pool.js +1 -19
  237. package/dist/tenant/principals.d.ts +23 -11
  238. package/dist/tenant/principals.js +61 -49
  239. package/dist/tenant/reset.d.ts +0 -4
  240. package/dist/tenant/reset.js +0 -5
  241. package/dist/tenant/runtime.d.ts +28 -20
  242. package/dist/tenant/runtime.js +95 -64
  243. package/dist/tenant/scheduler.d.ts +10 -3
  244. package/dist/tenant/scheduler.js +9 -5
  245. package/dist/tenant/session-streams.d.ts +127 -0
  246. package/dist/tenant/session-streams.js +404 -0
  247. package/dist/tenant/sessions.d.ts +28 -4
  248. package/dist/tenant/sessions.js +118 -24
  249. package/dist/tenant/signing-keys.d.ts +55 -0
  250. package/dist/tenant/signing-keys.js +172 -0
  251. package/dist/tenant/slim.d.ts +8 -0
  252. package/dist/tenant/slim.js +21 -0
  253. package/dist/tenant/status.d.ts +3 -3
  254. package/dist/tenant/status.js +20 -13
  255. package/dist/tenant/store-pg.d.ts +9 -6
  256. package/dist/tenant/store-pg.js +60 -125
  257. package/dist/tenant/streams.d.ts +62 -85
  258. package/dist/tenant/streams.js +143 -226
  259. package/dist/tenant/subject-limits.d.ts +16 -0
  260. package/dist/tenant/subject-limits.js +45 -0
  261. package/dist/tenant/sweep.d.ts +1 -10
  262. package/dist/tenant/sweep.js +29 -69
  263. package/dist/tenant/tokens.d.ts +14 -0
  264. package/dist/tenant/tokens.js +180 -0
  265. package/dist/tenant/transcript.d.ts +48 -5
  266. package/dist/tenant/transcript.js +58 -1
  267. package/dist/tenant/types.d.ts +69 -98
  268. package/dist/tenant/types.js +4 -15
  269. package/dist/tenant/worker.d.ts +3 -1
  270. package/dist/tenant/worker.js +13 -7
  271. package/dist/vault/host-model.d.ts +31 -0
  272. package/dist/vault/host-model.js +57 -0
  273. package/dist/vault/service.d.ts +44 -17
  274. package/dist/vault/service.js +126 -121
  275. package/dist/version.d.ts +1 -1
  276. package/dist/version.js +1 -1
  277. package/package.json +25 -9
  278. package/dist/core/executors.d.ts +0 -44
  279. package/dist/core/executors.js +0 -95
  280. package/dist/store/memory.d.ts +0 -57
  281. package/dist/store/memory.js +0 -645
  282. package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
  283. package/dist/store/postgres/migrations/001_initial.js +0 -196
  284. package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
  285. package/dist/store/postgres/migrations/002_session_owner.js +0 -13
  286. package/dist/store/postgres/migrations/index.d.ts +0 -49
  287. package/dist/store/postgres/migrations/index.js +0 -76
  288. package/dist/store/postgres/names.d.ts +0 -11
  289. package/dist/store/postgres/names.js +0 -42
  290. package/dist/store/postgres/tenants.d.ts +0 -90
  291. package/dist/store/postgres/tenants.js +0 -214
  292. package/dist/streams/relay.d.ts +0 -37
  293. package/dist/streams/relay.js +0 -254
  294. package/dist/tenant/envelope.d.ts +0 -7
  295. package/dist/tenant/envelope.js +0 -22
  296. package/dist/tenant/live.d.ts +0 -92
  297. package/dist/tenant/live.js +0 -290
  298. package/dist/tenant/quarantine-error.d.ts +0 -6
  299. package/dist/tenant/quarantine-error.js +0 -9
  300. package/dist/tenant/quarantine.d.ts +0 -27
  301. package/dist/tenant/quarantine.js +0 -72
  302. package/dist/tenant/routes-tenant.d.ts +0 -12
  303. package/dist/tenant/routes-tenant.js +0 -137
  304. package/dist/tenant/routes.d.ts +0 -9
  305. package/dist/tenant/routes.js +0 -151
  306. package/dist/tenant/store-memory.d.ts +0 -16
  307. package/dist/tenant/store-memory.js +0 -111
  308. /package/dist/{store → record}/cursor.d.ts +0 -0
  309. /package/dist/{store → record}/cursor.js +0 -0
@@ -0,0 +1,3 @@
1
+ import type { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { TenantEnv } from "../app.js";
3
+ export declare function sessionRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -0,0 +1,205 @@
1
+ import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
2
+ import { z } from "zod";
3
+ import { PutAgentRequestSchema, PutSessionRequestSchema } from "@nylorun/core/contracts";
4
+ import { handleSessionSandboxTool, SandboxRouteError } from "../../../core/sandbox-routes.js";
5
+ import { ListAgentsResponse, ListPublicAgentsResponse, ListSessionsResponse, SessionEvent, PutAgentRequest, PutAgentResponse, PutSessionRequest, SandboxToolOutcome, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
6
+ import { sandboxRouteDeps } from "../../../tenant/actions.js";
7
+ import { accessOf, requireApplication } from "../../../tenant/auth.js";
8
+ import { loadSession, sessionOf } from "../../../tenant/context.js";
9
+ import { fail } from "../../../tenant/http.js";
10
+ import { readHistory, requestCursor, streamSessionEvents } from "../../../tenant/session-streams.js";
11
+ import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
12
+ import { readJson } from "../body.js";
13
+ import { tenantRoute } from "../define.js";
14
+ import { jsonResponse } from "../respond.js";
15
+ const OWN_SESSIONS = {
16
+ credentials: ["application", "subject", "token"],
17
+ scopes: ["sessions:own"],
18
+ browser: true,
19
+ };
20
+ const json = (schema, description) => ({
21
+ description,
22
+ content: { "application/json": { schema } },
23
+ });
24
+ const body = (schema) => ({
25
+ required: true,
26
+ content: { "application/json": { schema } },
27
+ });
28
+ const sessionId = z.object({ sessionId: z.string() });
29
+ const SANDBOX_TOOLS = ["bash", "read", "write", "edit", "grep", "glob"];
30
+ const resume = z.object({
31
+ cursor: z
32
+ .string()
33
+ .optional()
34
+ .meta({ description: "Continue after this event's cursor; `Last-Event-ID` works too" }),
35
+ });
36
+ function urlOf(incoming) {
37
+ return new URL(incoming.url ?? "/", "http://runtime");
38
+ }
39
+ /**
40
+ * A session route below the session itself: the session must exist and be the caller's, and a
41
+ * resume cursor must be well formed, before anything else.
42
+ */
43
+ async function sessionBelow(ctx, incoming, id, access) {
44
+ await loadSession(ctx, id, access);
45
+ return requestCursor(incoming, urlOf(incoming));
46
+ }
47
+ export function sessionRoutes(api) {
48
+ tenantRoute(api, {
49
+ credentials: ["application", "subject", "token", "publishable"],
50
+ scopes: ["agents:read", "agents:write"],
51
+ browser: true,
52
+ }, {
53
+ method: "get",
54
+ path: "/v1/agents",
55
+ tags: ["Agents"],
56
+ summary: "List agents",
57
+ description: "With an application key, every definition, manifest included. With a subject token or a publishable key, only the agents it may use, by name.",
58
+ responses: {
59
+ 200: json(z.union([ListAgentsResponse, ListPublicAgentsResponse]), "The agents"),
60
+ },
61
+ }, async (c) => {
62
+ const scope = c.get("scope");
63
+ const ctx = c.env.tenant;
64
+ if (scope.kind === "publishable")
65
+ return jsonResponse(200, await listAgentsPublic(ctx, scope.agents));
66
+ return jsonResponse(200, scope.kind === "token"
67
+ ? await listAgentsPublic(ctx, scope.agents)
68
+ : await listDefinitions(ctx));
69
+ });
70
+ tenantRoute(api, { credentials: ["application", "subject"], scopes: ["agents:write"] }, {
71
+ method: "put",
72
+ path: "/v1/agents/{agentId}",
73
+ tags: ["Agents"],
74
+ summary: "Put an agent's definition",
75
+ request: { params: z.object({ agentId: z.string() }), body: body(PutAgentRequest) },
76
+ responses: { 200: json(PutAgentResponse, "The definition, stored") },
77
+ }, async (c) => {
78
+ return jsonResponse(200, await putDefinition(c.env.tenant, c.req.param("agentId"), PutAgentRequestSchema.parse(await readJson(c.req.raw))));
79
+ });
80
+ tenantRoute(api, OWN_SESSIONS, {
81
+ method: "get",
82
+ path: "/v1/sessions",
83
+ tags: ["Sessions"],
84
+ summary: "List sessions",
85
+ description: "Acting for a person, only theirs.",
86
+ request: { query: z.object({ agentId: z.string().optional() }) },
87
+ responses: { 200: json(ListSessionsResponse, "The sessions") },
88
+ }, async (c) => {
89
+ const scope = c.get("scope");
90
+ return jsonResponse(200, await listSessions(c.env.tenant, c.req.query("agentId") ?? null, accessOf(scope)));
91
+ });
92
+ tenantRoute(api, OWN_SESSIONS, {
93
+ method: "put",
94
+ path: "/v1/sessions/{sessionId}",
95
+ tags: ["Sessions"],
96
+ summary: "Open a session",
97
+ description: "Creates the session, or re-attaches vaults to an identical one. Its sandbox is chosen now and fixed for its life.",
98
+ request: { params: sessionId, body: body(PutSessionRequest) },
99
+ responses: {
100
+ 200: json(SessionView, "The session"),
101
+ 409: { description: "A session with this id was opened with other parameters" },
102
+ },
103
+ }, async (c) => {
104
+ const scope = c.get("scope");
105
+ const ctx = c.env.tenant;
106
+ const request = PutSessionRequestSchema.parse(await readJson(c.req.raw));
107
+ // Agent code may trust `info`: only an app server sets it.
108
+ if (scope.kind === "token" && request.info !== undefined)
109
+ fail(403, "A subject token cannot set session info", { code: "scope_required" });
110
+ const session = await putSession(ctx, c.req.param("sessionId"), request, accessOf(scope));
111
+ return jsonResponse(200, await ctx.store.tx((t) => sessionView(t, session)));
112
+ });
113
+ tenantRoute(api, OWN_SESSIONS, {
114
+ method: "get",
115
+ path: "/v1/sessions/{sessionId}",
116
+ tags: ["Sessions"],
117
+ summary: "Get a session",
118
+ request: { params: sessionId },
119
+ responses: { 200: json(SessionView, "The session") },
120
+ }, async (c) => {
121
+ const scope = c.get("scope");
122
+ const ctx = c.env.tenant;
123
+ const id = c.req.param("sessionId");
124
+ return jsonResponse(200, await ctx.store.tx(async (t) => sessionView(t, await sessionOf(t, id, accessOf(scope)))));
125
+ });
126
+ tenantRoute(api, OWN_SESSIONS, {
127
+ method: "get",
128
+ path: "/v1/sessions/{sessionId}/items",
129
+ tags: ["Sessions"],
130
+ summary: "Read a session's history",
131
+ request: {
132
+ params: sessionId,
133
+ query: resume.extend({
134
+ agent: z
135
+ .string()
136
+ .optional()
137
+ .meta({ description: "Only the events of this agent (the root agent, or one it uses as a tool)" }),
138
+ }),
139
+ },
140
+ responses: { 200: json(SessionItemsResponse, "Events up to now, and the cursor to continue") },
141
+ }, async (c) => {
142
+ const scope = c.get("scope");
143
+ const ctx = c.env.tenant;
144
+ const id = c.req.param("sessionId");
145
+ const cursor = await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
146
+ return jsonResponse(200, await readHistory(ctx, id, cursor, c.req.query("agent")));
147
+ });
148
+ tenantRoute(api, OWN_SESSIONS, {
149
+ method: "get",
150
+ path: "/v1/sessions/{sessionId}/events",
151
+ tags: ["Sessions"],
152
+ summary: "Follow a session's events",
153
+ description: "Server-sent events, each with its type as `event` and its cursor as `id`, from the cursor on. Events are on the `nylorun.event/2` envelope, typed by `SessionEvent`; a client ignores a type it does not know. A subject token's stream ends with `event: nylorun.closed` when the token expires or the subject is revoked. A `: keepalive` comment every 15 seconds.",
154
+ request: { params: sessionId, query: resume },
155
+ responses: {
156
+ 200: {
157
+ description: "The session's events",
158
+ content: {
159
+ "text/event-stream": {
160
+ itemSchema: z.union([SessionEvent, StreamClosedFrame]),
161
+ },
162
+ },
163
+ },
164
+ },
165
+ }, async (c) => {
166
+ const scope = c.get("scope");
167
+ const ctx = c.env.tenant;
168
+ const { incoming, outgoing } = c.env;
169
+ const id = c.req.param("sessionId");
170
+ const cursor = await sessionBelow(ctx, incoming, id, accessOf(scope));
171
+ await streamSessionEvents(ctx, incoming, outgoing, id, cursor, scope.kind === "token"
172
+ ? { subject: scope.subject, epoch: scope.epoch, expiresAt: scope.expiresAt }
173
+ : undefined);
174
+ return RESPONSE_ALREADY_SENT;
175
+ });
176
+ tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
177
+ method: "post",
178
+ path: "/v1/sessions/{sessionId}/sandbox/{tool}",
179
+ tags: ["Sessions"],
180
+ summary: "Run a sandbox tool in a session",
181
+ description: "For an app's own tools; not while a turn is running.",
182
+ request: {
183
+ params: sessionId.extend({ tool: z.enum(SANDBOX_TOOLS) }),
184
+ body: body(z.looseObject({}).meta({ description: "The tool's input" })),
185
+ },
186
+ responses: {
187
+ 200: json(SandboxToolOutcome, "The tool's output, or why it failed"),
188
+ 409: { description: "A turn is running" },
189
+ },
190
+ }, async (c) => {
191
+ const scope = c.get("scope");
192
+ const ctx = c.env.tenant;
193
+ const id = c.req.param("sessionId");
194
+ await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
195
+ requireApplication(scope);
196
+ try {
197
+ return jsonResponse(200, await handleSessionSandboxTool(sandboxRouteDeps(ctx), id, c.req.param("tool"), await readJson(c.req.raw), c.req.raw.signal));
198
+ }
199
+ catch (error) {
200
+ if (error instanceof SandboxRouteError)
201
+ fail(error.status, error.message);
202
+ throw error;
203
+ }
204
+ });
205
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The Tenant's own settings (`/v1/tenant/**`): its status, reset, a first configuration, the
3
+ * model it calls, what its model calls cost and may cost (budgets), and the sandboxes its
4
+ * sessions get.
5
+ *
6
+ * A reset that deletes sessions moves the Tenant to a new basin generation, so a session
7
+ * created again starts in an empty basin; the old basin is deleted after a grace period.
8
+ */
9
+ import type { OpenAPIHono } from "@hono/zod-openapi";
10
+ import type { TenantEnv } from "../app.js";
11
+ export declare function tenantRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -0,0 +1,244 @@
1
+ import { z } from "zod";
2
+ import { ModelUsageQuerySchema, PutModelBudgetsRequestSchema, PutHostModelRequestSchema, PutTenantSandboxRequestSchema, ResetTenantRequestSchema, SeedTenantConfigRequestSchema, SelectHostModelRequestSchema, } from "@nylorun/core/contracts";
3
+ import { HostModelCatalog, HostModelView, ListProvidersResponse, ModelBudgets, ModelUsageTotals, PutModelBudgetsRequest, PutHostModelRequest, PutTenantSandboxRequest, ResetTenantRequest, ResetTenantResponse, SeedTenantConfigRequest, SeedTenantConfigResponse, SelectHostModelRequest, TenantSandboxView, TenantStatus, } from "../../components.js";
4
+ import { periodStart } from "../../../gates/meter.js";
5
+ import { hostModelCatalog } from "../../../model/catalog.js";
6
+ import { sandboxConfigErrors } from "../../../sandbox/resolve.js";
7
+ import { effectiveSandboxConfig, readSandboxConfig, writeSandboxConfig, } from "../../../sandbox/tenant-config.js";
8
+ import { fail } from "../../../tenant/http.js";
9
+ import { clearObservers } from "../../../tenant/session-streams.js";
10
+ import { usesFixtureModel } from "../../../tenant/model-setting.js";
11
+ import { resetTenant } from "../../../tenant/reset.js";
12
+ import { clearWork, drain } from "../../../tenant/scheduler.js";
13
+ import { buildTenantStatus, seedTenantConfig } from "../../../tenant/status.js";
14
+ import { tenantReset, streamsStatus, } from "../../../tenant/streams.js";
15
+ import { readJson } from "../body.js";
16
+ import { pathSegments, tenantRoute } from "../define.js";
17
+ import { jsonResponse } from "../respond.js";
18
+ const settings = (scopes) => ({
19
+ credentials: scopes === "never" ? ["application"] : ["application", "subject"],
20
+ scopes,
21
+ });
22
+ const SETTINGS = settings(["tenant:settings"]);
23
+ const json = (schema, description) => ({
24
+ description,
25
+ content: { "application/json": { schema } },
26
+ });
27
+ const body = (schema) => ({
28
+ required: true,
29
+ content: { "application/json": { schema } },
30
+ });
31
+ const budgetRow = (budget, updatedAt) => ({
32
+ scope: budget.scope,
33
+ scopeId: budget.scope === "agent" ? budget.id : "*",
34
+ period: budget.period ?? null,
35
+ limitUsd: budget.limitUsd ?? null,
36
+ limitTokens: budget.limitTokens ?? null,
37
+ updatedAt,
38
+ });
39
+ const budgetView = (row) => ({
40
+ scope: row.scope,
41
+ ...(row.scope === "agent" ? { id: row.scopeId } : {}),
42
+ ...(row.period ? { period: row.period } : {}),
43
+ ...(row.limitUsd !== null ? { limitUsd: row.limitUsd } : {}),
44
+ ...(row.limitTokens !== null ? { limitTokens: row.limitTokens } : {}),
45
+ });
46
+ /** `GET /v1/tenant/sandbox`: the backend report and the configuration with defaults applied. */
47
+ async function sandboxView(ctx) {
48
+ const config = await ctx.store.tx((t) => readSandboxConfig(t));
49
+ return { ...(await ctx.sandbox.report()), config: effectiveSandboxConfig(config) };
50
+ }
51
+ export function tenantRoutes(api) {
52
+ tenantRoute(api, SETTINGS, {
53
+ method: "get",
54
+ path: "/v1/tenant",
55
+ tags: ["Tenant"],
56
+ summary: "Get the Tenant's status",
57
+ responses: { 200: json(TenantStatus, "Readiness, model, sandboxes, streams and counts") },
58
+ }, async (c) => {
59
+ const ctx = c.env.tenant;
60
+ const { vault } = ctx;
61
+ return jsonResponse(200, await buildTenantStatus({
62
+ envelope: ctx.envelope,
63
+ config: ctx.config,
64
+ store: ctx.store,
65
+ vault,
66
+ sandbox: ctx.sandbox,
67
+ closing: ctx.closing || ctx.closed,
68
+ modelConfigured: !ctx.useVaultModel ||
69
+ (await ctx.store.tx((t) => usesFixtureModel(t))) ||
70
+ (await vault.getHostModel()).configured,
71
+ ...(ctx.stuckInvocations ? { stuckInvocations: ctx.stuckInvocations } : {}),
72
+ streamsStatus: () => streamsStatus(ctx),
73
+ }));
74
+ });
75
+ tenantRoute(api, settings("never"), {
76
+ method: "post",
77
+ path: "/v1/tenant/reset",
78
+ tags: ["Tenant"],
79
+ summary: "Reset the Tenant",
80
+ description: "Deletes its sessions, its sandboxes, or both, after draining or cancelling the turns in progress. The Tenant stays open.",
81
+ request: { body: body(ResetTenantRequest) },
82
+ responses: { 200: json(ResetTenantResponse, "Reset") },
83
+ }, async (c) => {
84
+ const ctx = c.env.tenant;
85
+ const request = ResetTenantRequestSchema.parse(await readJson(c.req.raw));
86
+ await drain(ctx, request.activeWork, 30_000);
87
+ // A sessions reset moves the Tenant to a new basin generation, so a session created
88
+ // again with the same id, during or after the reset, starts in an empty basin at 0.
89
+ const sessionsReset = request.scope !== "sandboxes";
90
+ await resetTenant({
91
+ store: ctx.store,
92
+ sandbox: ctx.sandbox,
93
+ paths: ctx.config.paths,
94
+ clearSessionState: () => {
95
+ clearWork(ctx);
96
+ clearObservers(ctx.sessionStreams);
97
+ },
98
+ }, request.scope);
99
+ if (sessionsReset)
100
+ await tenantReset(ctx);
101
+ // Reset leaves the Tenant open for new work.
102
+ ctx.closing = false;
103
+ return jsonResponse(200, { ok: true });
104
+ });
105
+ tenantRoute(api, settings("never"), {
106
+ method: "put",
107
+ path: "/v1/tenant/config/seed",
108
+ tags: ["Tenant"],
109
+ summary: "Seed the Tenant's configuration",
110
+ description: "Sets each setting the Tenant does not have yet; keeps the ones it has.",
111
+ request: { body: body(SeedTenantConfigRequest) },
112
+ responses: { 200: json(SeedTenantConfigResponse, "What was set, and what was kept") },
113
+ }, async (c) => {
114
+ const ctx = c.env.tenant;
115
+ const request = SeedTenantConfigRequestSchema.parse(await readJson(c.req.raw));
116
+ return jsonResponse(200, await seedTenantConfig({ store: ctx.store, vault: ctx.vault, keys: ctx.keys }, request));
117
+ });
118
+ tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
119
+ method: "get",
120
+ path: "/v1/tenant/models",
121
+ tags: ["Tenant"],
122
+ summary: "List model providers and models",
123
+ responses: { 200: json(HostModelCatalog, "Provider and model names") },
124
+ }, async (c) => {
125
+ c.env.tenant;
126
+ return jsonResponse(200, hostModelCatalog());
127
+ });
128
+ tenantRoute(api, SETTINGS, {
129
+ method: "get",
130
+ path: "/v1/tenant/sandbox",
131
+ tags: ["Tenant"],
132
+ summary: "Get the Tenant's sandbox configuration",
133
+ responses: { 200: json(TenantSandboxView, "The backend in use and the configuration in force") },
134
+ }, async (c) => jsonResponse(200, await sandboxView(c.env.tenant)));
135
+ tenantRoute(api, SETTINGS, {
136
+ method: "put",
137
+ path: "/v1/tenant/sandbox",
138
+ tags: ["Tenant"],
139
+ summary: "Set the Tenant's sandbox configuration",
140
+ description: "What a session gets when it names no sandbox, and the limits every session's sandbox must fit.",
141
+ request: { body: body(PutTenantSandboxRequest) },
142
+ responses: { 200: json(TenantSandboxView, "The configuration now in force") },
143
+ }, async (c) => {
144
+ const ctx = c.env.tenant;
145
+ const { requestId: _requestId, ...config } = PutTenantSandboxRequestSchema.parse(await readJson(c.req.raw));
146
+ const errors = sandboxConfigErrors(effectiveSandboxConfig(config));
147
+ if (errors.length > 0)
148
+ fail(400, errors.join(" "));
149
+ await ctx.store.tx((t) => writeSandboxConfig(t, config));
150
+ return jsonResponse(200, await sandboxView(ctx));
151
+ });
152
+ tenantRoute(api, SETTINGS, {
153
+ method: "get",
154
+ path: "/v1/tenant/usage",
155
+ tags: ["Tenant"],
156
+ summary: "Get what the Tenant's model calls used",
157
+ description: "Totals from the model usage ledger, which the model gate writes once per call: for the Tenant, one agent or one turn, over the current UTC day or month, or all time.",
158
+ request: { query: ModelUsageQuerySchema },
159
+ responses: { 200: json(ModelUsageTotals, "The calls, tokens and cost") },
160
+ }, async (c) => {
161
+ const parsed = ModelUsageQuerySchema.safeParse(c.req.query());
162
+ if (!parsed.success)
163
+ return fail(400, parsed.error.issues[0]?.message ?? "Invalid query");
164
+ const { scope, id, period } = parsed.data;
165
+ if (scope !== "tenant" && !id)
166
+ fail(400, `The ${scope} scope needs an id`);
167
+ const since = period === "total" ? undefined : periodStart(period, new Date());
168
+ const totals = await c.env.tenant.store.tx((t) => t.modelUsageTotals({
169
+ scope,
170
+ ...(scope !== "tenant" ? { id: id } : {}),
171
+ ...(since ? { since } : {}),
172
+ }));
173
+ return jsonResponse(200, {
174
+ scope,
175
+ ...(scope !== "tenant" ? { id } : {}),
176
+ period,
177
+ ...(since ? { since } : {}),
178
+ ...totals,
179
+ });
180
+ });
181
+ tenantRoute(api, SETTINGS, {
182
+ method: "get",
183
+ path: "/v1/tenant/budgets",
184
+ tags: ["Tenant"],
185
+ summary: "Get the Tenant's model budgets",
186
+ responses: { 200: json(ModelBudgets, "The hard caps on model spend") },
187
+ }, async (c) => {
188
+ const rows = await c.env.tenant.store.tx((t) => t.listModelBudgets());
189
+ return jsonResponse(200, { budgets: rows.map(budgetView) });
190
+ });
191
+ tenantRoute(api, SETTINGS, {
192
+ method: "put",
193
+ path: "/v1/tenant/budgets",
194
+ tags: ["Tenant"],
195
+ summary: "Set the Tenant's model budgets",
196
+ description: "Replaces every budget. Before each model call the gate checks the scope's spend against its cap; once a cap is reached the call fails with `budget_exhausted` and the turn with `model.budget_exhausted`. An empty list removes every cap.",
197
+ request: { body: body(PutModelBudgetsRequest) },
198
+ responses: { 200: json(ModelBudgets, "The budgets now in force") },
199
+ }, async (c) => {
200
+ const { budgets } = PutModelBudgetsRequestSchema.parse(await readJson(c.req.raw));
201
+ const updatedAt = new Date().toISOString();
202
+ const rows = await c.env.tenant.store.tx(async (t) => {
203
+ await t.putModelBudgets(budgets.map((budget) => budgetRow(budget, updatedAt)));
204
+ return t.listModelBudgets();
205
+ });
206
+ return jsonResponse(200, { budgets: rows.map(budgetView) });
207
+ });
208
+ tenantRoute(api, settings(["tenant:settings", "agents:write"]), {
209
+ method: "get",
210
+ path: "/v1/tenant/providers",
211
+ tags: ["Tenant"],
212
+ summary: "List the model providers the Tenant has credentials for",
213
+ responses: { 200: json(ListProvidersResponse, "The providers") },
214
+ }, async (c) => jsonResponse(200, await (c.env.tenant).vault.listHostProviders()));
215
+ tenantRoute(api, SETTINGS, {
216
+ method: "get",
217
+ path: "/v1/tenant/model",
218
+ tags: ["Tenant"],
219
+ summary: "Get the Tenant's model",
220
+ responses: { 200: json(HostModelView, "The model its sessions call") },
221
+ }, async (c) => jsonResponse(200, await (c.env.tenant).vault.getHostModel()));
222
+ tenantRoute(api, SETTINGS, {
223
+ method: "put",
224
+ path: "/v1/tenant/model",
225
+ tags: ["Tenant"],
226
+ summary: "Set a model provider's credentials",
227
+ request: { body: body(PutHostModelRequest) },
228
+ responses: { 200: json(HostModelView, "The model now in force") },
229
+ }, async (c) => {
230
+ const ctx = c.env.tenant;
231
+ return jsonResponse(200, await ctx.keys.putHostModel(PutHostModelRequestSchema.parse(await readJson(c.req.raw))));
232
+ });
233
+ tenantRoute(api, SETTINGS, {
234
+ method: "put",
235
+ path: "/v1/tenant/model/selection",
236
+ tags: ["Tenant"],
237
+ summary: "Choose the provider and model",
238
+ request: { body: body(SelectHostModelRequest) },
239
+ responses: { 200: json(HostModelView, "The model now in force") },
240
+ }, async (c) => {
241
+ const ctx = c.env.tenant;
242
+ return jsonResponse(200, await ctx.keys.selectHostModel(SelectHostModelRequestSchema.parse(await readJson(c.req.raw))));
243
+ });
244
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Vaults and their credentials (`/v1/vaults/**`): secrets a session's tools use, owned by one
3
+ * person. Acting for a subject reaches only the subject's own vaults, and never on another's
4
+ * behalf.
5
+ */
6
+ import type { OpenAPIHono } from "@hono/zod-openapi";
7
+ import type { TenantEnv } from "../app.js";
8
+ export declare function vaultRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -0,0 +1,167 @@
1
+ import { z } from "zod";
2
+ import { CreateCredentialRequestSchema, CreateVaultRequestSchema, RotateCredentialRequestSchema, } from "@nylorun/core/contracts";
3
+ import { CreateCredentialRequest, CreateVaultRequest, CredentialInfo, DeletedResponse, ListCredentialsResponse, ListVaultsResponse, RotateCredentialRequest, VaultInfo, } from "../../components.js";
4
+ import { ownerOf } from "../../../tenant/auth.js";
5
+ import { fail } from "../../../tenant/http.js";
6
+ import { readJson } from "../body.js";
7
+ import { pathSegments, tenantRoute } from "../define.js";
8
+ import { jsonResponse } from "../respond.js";
9
+ const OWN_VAULTS = {
10
+ credentials: ["application", "subject", "token"],
11
+ scopes: ["vaults:own"],
12
+ browser: true,
13
+ };
14
+ const json = (schema, description) => ({
15
+ description,
16
+ content: { "application/json": { schema } },
17
+ });
18
+ const body = (schema) => ({
19
+ required: true,
20
+ content: { "application/json": { schema } },
21
+ });
22
+ const vaultId = z.object({ vaultId: z.string() });
23
+ const credentialId = vaultId.extend({ credentialId: z.string() });
24
+ /**
25
+ * Who a vault route acts for: the subject, or no one in particular (an application key). Below
26
+ * a vault, the vault must be the subject's before anything else.
27
+ */
28
+ async function ownerFor(c) {
29
+ const { vault } = c.env.tenant;
30
+ const owner = ownerOf(c.get("scope"));
31
+ const id = c.req.param("vaultId");
32
+ if (id !== undefined && owner !== undefined)
33
+ await vault.assertOwner(id, owner);
34
+ return owner;
35
+ }
36
+ export function vaultRoutes(api) {
37
+ tenantRoute(api, OWN_VAULTS, {
38
+ method: "post",
39
+ path: "/v1/vaults",
40
+ tags: ["Vaults"],
41
+ summary: "Create a vault",
42
+ request: { body: body(CreateVaultRequest) },
43
+ responses: {
44
+ 200: json(VaultInfo, "The vault"),
45
+ 409: { description: "The idempotency key was used for another vault" },
46
+ },
47
+ }, async (c) => {
48
+ const owner = await ownerFor(c);
49
+ const request = CreateVaultRequestSchema.parse(await readJson(c.req.raw));
50
+ if (owner !== undefined && request.ownerUserId !== owner)
51
+ fail(403, "ownerUserId must be the subject");
52
+ return jsonResponse(200, await c.env.tenant.vault.createVault(request));
53
+ });
54
+ tenantRoute(api, OWN_VAULTS, {
55
+ method: "get",
56
+ path: "/v1/vaults",
57
+ tags: ["Vaults"],
58
+ summary: "List a person's vaults",
59
+ request: {
60
+ query: z.object({
61
+ ownerUserId: z
62
+ .string()
63
+ .optional()
64
+ .meta({ description: "Whose vaults; required with an application key acting for no one" }),
65
+ }),
66
+ },
67
+ responses: { 200: json(ListVaultsResponse, "The vaults") },
68
+ }, async (c) => {
69
+ const owner = await ownerFor(c);
70
+ const ownerUserId = c.req.query("ownerUserId") ?? owner ?? fail(400, "ownerUserId is required");
71
+ if (owner !== undefined && ownerUserId !== owner)
72
+ fail(403, "ownerUserId must be the subject");
73
+ return jsonResponse(200, { vaults: await c.env.tenant.vault.listVaults(ownerUserId) });
74
+ });
75
+ tenantRoute(api, OWN_VAULTS, {
76
+ method: "get",
77
+ path: "/v1/vaults/{vaultId}",
78
+ tags: ["Vaults"],
79
+ summary: "Get a vault",
80
+ request: { params: vaultId },
81
+ responses: { 200: json(VaultInfo, "The vault") },
82
+ }, async (c) => {
83
+ await ownerFor(c);
84
+ return jsonResponse(200, await c.env.tenant.vault.getVault(c.req.param("vaultId")));
85
+ });
86
+ tenantRoute(api, OWN_VAULTS, {
87
+ method: "delete",
88
+ path: "/v1/vaults/{vaultId}",
89
+ tags: ["Vaults"],
90
+ summary: "Delete a vault and its credentials",
91
+ request: { params: vaultId },
92
+ responses: { 200: json(DeletedResponse, "Deleted") },
93
+ }, async (c) => {
94
+ await ownerFor(c);
95
+ return jsonResponse(200, await c.env.tenant.vault.deleteVault(c.req.param("vaultId")));
96
+ });
97
+ tenantRoute(api, OWN_VAULTS, {
98
+ method: "post",
99
+ path: "/v1/vaults/{vaultId}/credentials",
100
+ tags: ["Vaults"],
101
+ summary: "Add a credential",
102
+ description: "A bearer token or OAuth tokens, bound to the URLs a tool may send them to. A subject token cannot store OAuth refresh credentials.",
103
+ request: { params: vaultId, body: body(CreateCredentialRequest) },
104
+ responses: { 200: json(CredentialInfo, "The credential, without its secret") },
105
+ }, async (c) => {
106
+ await ownerFor(c);
107
+ const request = CreateCredentialRequestSchema.parse(await readJson(c.req.raw));
108
+ // The Runtime calls a refresh credential's token endpoint itself: never for a browser.
109
+ if (c.get("scope").kind === "token" &&
110
+ request.auth.type === "oauth" &&
111
+ request.auth.refresh)
112
+ fail(403, "A subject token cannot store OAuth refresh credentials", {
113
+ code: "scope_required",
114
+ });
115
+ return jsonResponse(200, await c.env.tenant.keys.createCredential(c.req.param("vaultId"), request));
116
+ });
117
+ tenantRoute(api, OWN_VAULTS, {
118
+ method: "get",
119
+ path: "/v1/vaults/{vaultId}/credentials",
120
+ tags: ["Vaults"],
121
+ summary: "List a vault's credentials",
122
+ request: { params: vaultId },
123
+ responses: { 200: json(ListCredentialsResponse, "The credentials, without their secrets") },
124
+ }, async (c) => {
125
+ await ownerFor(c);
126
+ return jsonResponse(200, {
127
+ credentials: await c.env.tenant.vault.listCredentials(c.req.param("vaultId")),
128
+ });
129
+ });
130
+ tenantRoute(api, OWN_VAULTS, {
131
+ method: "get",
132
+ path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
133
+ tags: ["Vaults"],
134
+ summary: "Get a credential",
135
+ request: { params: credentialId },
136
+ responses: { 200: json(CredentialInfo, "The credential, without its secret") },
137
+ }, async (c) => {
138
+ await ownerFor(c);
139
+ return jsonResponse(200, await c.env.tenant.vault.getCredential(c.req.param("vaultId"), c.req.param("credentialId")));
140
+ });
141
+ tenantRoute(api, OWN_VAULTS, {
142
+ method: "post",
143
+ path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
144
+ tags: ["Vaults"],
145
+ summary: "Rotate a credential's secret",
146
+ request: { params: credentialId, body: body(RotateCredentialRequest) },
147
+ responses: {
148
+ 200: json(CredentialInfo, "The credential"),
149
+ 409: { description: "A credential's type cannot change" },
150
+ },
151
+ }, async (c) => {
152
+ await ownerFor(c);
153
+ const request = RotateCredentialRequestSchema.parse(await readJson(c.req.raw));
154
+ return jsonResponse(200, await c.env.tenant.keys.rotateCredential(c.req.param("vaultId"), c.req.param("credentialId"), request));
155
+ });
156
+ tenantRoute(api, OWN_VAULTS, {
157
+ method: "delete",
158
+ path: "/v1/vaults/{vaultId}/credentials/{credentialId}",
159
+ tags: ["Vaults"],
160
+ summary: "Delete a credential",
161
+ request: { params: credentialId },
162
+ responses: { 200: json(DeletedResponse, "Deleted") },
163
+ }, async (c) => {
164
+ await ownerFor(c);
165
+ return jsonResponse(200, await c.env.tenant.vault.deleteCredential(c.req.param("vaultId"), c.req.param("credentialId")));
166
+ });
167
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The Runtime's OpenAPI 3.2 documents, generated from the routes as declared for serving:
3
+ *
4
+ * - the Tenant API (`tenantDocument`): every Tenant route, with `/health`, `/ready` and
5
+ * `/openapi.json`. Served at `GET /openapi.json`, packed as `@nylorun/runtime/openapi.json`
6
+ * and attached to each release.
7
+ * - the Admin API (`adminDocument`): served at `GET /v1/admin/openapi.json` behind the admin
8
+ * key, packed as `@nylorun/runtime/admin-openapi.json`.
9
+ *
10
+ * Event streams are `text/event-stream` with an `itemSchema` (OpenAPI 3.2). Nothing here reads
11
+ * the environment: the same Runtime makes the same document.
12
+ */
13
+ import { OpenApiGeneratorV32 } from "@asteasolutions/zod-to-openapi";
14
+ type OpenApiDocument = ReturnType<OpenApiGeneratorV32["generateDocument"]>;
15
+ /** The Tenant API's document. */
16
+ export declare function tenantDocument(): OpenApiDocument;
17
+ /** The Admin API's document. */
18
+ export declare function adminDocument(): OpenApiDocument;
19
+ export {};