@nylorun/runtime 0.17.0-beta → 0.19.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 (223) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +88 -58
  3. package/dist/adapters/execution/restate.d.ts +1 -3
  4. package/dist/adapters/execution/restate.js +4 -31
  5. package/dist/adapters/replication/pgoutput.js +1 -1
  6. package/dist/adapters/sandbox/local.js +1 -1
  7. package/dist/adapters/sandbox/virtual.js +1 -1
  8. package/dist/api/ag-ui/routes.js +1 -1
  9. package/dist/api/ag-ui/translate.d.ts +1 -3
  10. package/dist/api/ag-ui/translate.js +2 -17
  11. package/dist/api/components.d.ts +4871 -2614
  12. package/dist/api/components.js +12 -11
  13. package/dist/api/gate/routes.d.ts +7 -8
  14. package/dist/api/gate/routes.js +58 -52
  15. package/dist/api/http/app.d.ts +6 -0
  16. package/dist/api/http/app.js +18 -14
  17. package/dist/api/http/define.d.ts +14 -2
  18. package/dist/api/http/define.js +34 -15
  19. package/dist/api/http/routes/access.d.ts +3 -3
  20. package/dist/api/http/routes/access.js +18 -17
  21. package/dist/api/http/routes/artifacts.js +2 -4
  22. package/dist/api/http/routes/files.d.ts +8 -0
  23. package/dist/api/http/routes/files.js +77 -0
  24. package/dist/api/http/routes/keys.d.ts +10 -0
  25. package/dist/api/http/routes/keys.js +64 -0
  26. package/dist/api/http/routes/me.js +5 -2
  27. package/dist/api/http/routes/reads.d.ts +66 -0
  28. package/dist/api/http/routes/reads.js +127 -0
  29. package/dist/api/http/routes/sandboxes.js +7 -4
  30. package/dist/api/http/routes/sessions.js +25 -8
  31. package/dist/api/http/routes/tenant.js +15 -18
  32. package/dist/api/http/routes/vaults.d.ts +10 -9
  33. package/dist/api/http/routes/vaults.js +27 -23
  34. package/dist/api/openapi.d.ts +10 -11
  35. package/dist/api/openapi.js +291 -61
  36. package/dist/artifacts/links.js +1 -1
  37. package/dist/core/flow-host.d.ts +44 -23
  38. package/dist/core/flow-host.js +88 -106
  39. package/dist/core/provider.d.ts +4 -1
  40. package/dist/core/provider.js +6 -1
  41. package/dist/core/sandbox-routes.d.ts +0 -9
  42. package/dist/core/sandbox-routes.js +2 -21
  43. package/dist/execution/memory.d.ts +4 -5
  44. package/dist/execution/memory.js +11 -25
  45. package/dist/execution/types.d.ts +3 -33
  46. package/dist/execution/types.js +2 -12
  47. package/dist/gates/http-tool.d.ts +86 -0
  48. package/dist/gates/http-tool.js +197 -0
  49. package/dist/gates/mcp-handler.d.ts +3 -0
  50. package/dist/gates/mcp-handler.js +1 -3
  51. package/dist/gates/meter.d.ts +2 -2
  52. package/dist/gates/meter.js +6 -0
  53. package/dist/gates/tenant-vaults.d.ts +5 -9
  54. package/dist/gates/tool-calls.d.ts +7 -15
  55. package/dist/gates/tool-calls.js +4 -4
  56. package/dist/gates/tool-client.d.ts +1 -1
  57. package/dist/gates/tool-client.js +23 -47
  58. package/dist/gates/tool-contract.d.ts +27 -39
  59. package/dist/gates/tool-contract.js +30 -21
  60. package/dist/gates/tool-gate.d.ts +25 -24
  61. package/dist/gates/tool-gate.js +11 -7
  62. package/dist/harness/calls.d.ts +10 -4
  63. package/dist/harness/calls.js +15 -6
  64. package/dist/harness/executors.d.ts +1 -1
  65. package/dist/harness/executors.js +17 -12
  66. package/dist/harness/main.d.ts +2 -2
  67. package/dist/harness/main.js +5 -13
  68. package/dist/harness/service.d.ts +4 -5
  69. package/dist/harness/service.js +9 -2
  70. package/dist/harness-api/in-process.js +2 -2
  71. package/dist/harness-api/record.d.ts +6 -3
  72. package/dist/harness-api/record.js +65 -72
  73. package/dist/harness-api/server.d.ts +1 -9
  74. package/dist/harness-api/server.js +19 -17
  75. package/dist/harness-api/start.js +1 -5
  76. package/dist/harness-api/workspace.js +2 -3
  77. package/dist/harness-api/ws-server.js +1 -1
  78. package/dist/host/app.d.ts +2 -9
  79. package/dist/host/app.js +15 -25
  80. package/dist/host/config-for.d.ts +1 -5
  81. package/dist/host/config-for.js +0 -3
  82. package/dist/host/config.d.ts +0 -5
  83. package/dist/host/create-host.d.ts +3 -33
  84. package/dist/host/create-host.js +18 -94
  85. package/dist/host/environment.d.ts +0 -3
  86. package/dist/host/environment.js +0 -9
  87. package/dist/host/gates.d.ts +4 -1
  88. package/dist/host/gates.js +3 -2
  89. package/dist/host/http.d.ts +1 -2
  90. package/dist/host/http.js +1 -7
  91. package/dist/host/index.d.ts +2 -2
  92. package/dist/host/index.js +2 -2
  93. package/dist/host/main.js +27 -34
  94. package/dist/host/operate.d.ts +29 -0
  95. package/dist/host/operate.js +223 -0
  96. package/dist/host/stack-config.d.ts +12 -12
  97. package/dist/host/stack-config.js +5 -32
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/infra/workers.d.ts +1 -1
  101. package/dist/keys/keys.js +2 -2
  102. package/dist/management-openapi.json +6491 -0
  103. package/dist/mcp/connect.d.ts +8 -4
  104. package/dist/mcp/connect.js +6 -26
  105. package/dist/mcp/pool.d.ts +5 -9
  106. package/dist/mcp/pool.js +8 -19
  107. package/dist/mcp/snapshot.d.ts +8 -2
  108. package/dist/mcp/snapshot.js +29 -2
  109. package/dist/media.d.ts +1 -1
  110. package/dist/media.js +1 -1
  111. package/dist/model/pi-model.js +3 -0
  112. package/dist/openapi.json +4700 -12350
  113. package/dist/reads/access.d.ts +4 -0
  114. package/dist/reads/access.js +12 -0
  115. package/dist/reads/cursor.d.ts +11 -0
  116. package/dist/reads/cursor.js +80 -0
  117. package/dist/reads/types.d.ts +25 -0
  118. package/dist/reads/types.js +1 -0
  119. package/dist/sandbox/manager.d.ts +16 -0
  120. package/dist/sandbox/manager.js +40 -0
  121. package/dist/sandbox/session-sandbox.js +13 -3
  122. package/dist/sandbox/skills.d.ts +13 -0
  123. package/dist/sandbox/skills.js +46 -0
  124. package/dist/sandbox/types.d.ts +2 -2
  125. package/dist/store/ownership.d.ts +9 -0
  126. package/dist/store/ownership.js +13 -0
  127. package/dist/store/postgres/connect.d.ts +2 -0
  128. package/dist/store/postgres/connect.js +29 -0
  129. package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
  130. package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
  131. package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
  132. package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
  133. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  134. package/dist/store/postgres/reads.d.ts +3 -0
  135. package/dist/store/postgres/reads.js +272 -0
  136. package/dist/store/postgres/schema.d.ts +294 -489
  137. package/dist/store/postgres/schema.js +41 -76
  138. package/dist/store/postgres/store.js +51 -154
  139. package/dist/store/postgres/tenant.d.ts +34 -2
  140. package/dist/store/postgres/tenant.js +16 -32
  141. package/dist/store/types.d.ts +27 -96
  142. package/dist/store/types.js +0 -1
  143. package/dist/streams/control.d.ts +0 -2
  144. package/dist/streams/control.js +0 -5
  145. package/dist/streams/types.d.ts +1 -10
  146. package/dist/tenant/advance.d.ts +3 -3
  147. package/dist/tenant/advance.js +10 -7
  148. package/dist/tenant/auth.d.ts +14 -9
  149. package/dist/tenant/auth.js +24 -28
  150. package/dist/tenant/cause.d.ts +3 -3
  151. package/dist/tenant/cause.js +1 -1
  152. package/dist/tenant/commands.d.ts +2 -18
  153. package/dist/tenant/commands.js +35 -172
  154. package/dist/tenant/context.d.ts +14 -24
  155. package/dist/tenant/definition-files.d.ts +24 -0
  156. package/dist/tenant/definition-files.js +103 -0
  157. package/dist/tenant/effects.d.ts +18 -20
  158. package/dist/tenant/effects.js +56 -119
  159. package/dist/tenant/ephemeral.d.ts +7 -6
  160. package/dist/tenant/ephemeral.js +3 -11
  161. package/dist/tenant/host-token.js +1 -1
  162. package/dist/tenant/jwt.js +1 -1
  163. package/dist/tenant/module.d.ts +1 -4
  164. package/dist/tenant/module.js +3 -12
  165. package/dist/tenant/operator-keys.d.ts +34 -8
  166. package/dist/tenant/operator-keys.js +50 -18
  167. package/dist/tenant/outbound.d.ts +10 -1
  168. package/dist/tenant/outbound.js +44 -27
  169. package/dist/tenant/paths.d.ts +1 -1
  170. package/dist/tenant/paths.js +1 -3
  171. package/dist/tenant/principals.d.ts +11 -2
  172. package/dist/tenant/principals.js +27 -8
  173. package/dist/tenant/run-token.js +3 -3
  174. package/dist/tenant/runtime.d.ts +4 -6
  175. package/dist/tenant/runtime.js +19 -18
  176. package/dist/tenant/sandboxes.d.ts +4 -1
  177. package/dist/tenant/sandboxes.js +11 -0
  178. package/dist/tenant/scheduler.d.ts +1 -5
  179. package/dist/tenant/scheduler.js +2 -5
  180. package/dist/tenant/session-streams.d.ts +2 -1
  181. package/dist/tenant/session-streams.js +39 -5
  182. package/dist/tenant/session.d.ts +8 -41
  183. package/dist/tenant/session.js +0 -37
  184. package/dist/tenant/sessions.js +16 -8
  185. package/dist/tenant/signing-keys.d.ts +3 -3
  186. package/dist/tenant/signing-keys.js +5 -5
  187. package/dist/tenant/skill-tool.d.ts +20 -0
  188. package/dist/tenant/skill-tool.js +72 -0
  189. package/dist/tenant/slim.d.ts +1 -1
  190. package/dist/tenant/slim.js +1 -1
  191. package/dist/tenant/status.js +7 -12
  192. package/dist/tenant/store-pg.d.ts +15 -0
  193. package/dist/tenant/store-pg.js +7 -3
  194. package/dist/tenant/streams.d.ts +3 -10
  195. package/dist/tenant/streams.js +1 -24
  196. package/dist/tenant/sweep.js +0 -5
  197. package/dist/tenant/transcript.d.ts +2 -2
  198. package/dist/tenant/transcript.js +2 -2
  199. package/dist/tenant/types.d.ts +15 -22
  200. package/dist/tenant/worker.d.ts +1 -3
  201. package/dist/tenant/worker.js +3 -8
  202. package/dist/vault/sources.d.ts +7 -1
  203. package/dist/vault/sources.js +22 -11
  204. package/dist/version.d.ts +1 -1
  205. package/dist/version.js +1 -1
  206. package/package.json +4 -4
  207. package/dist/admin-openapi.json +0 -904
  208. package/dist/api/http/routes/actions.d.ts +0 -8
  209. package/dist/api/http/routes/actions.js +0 -75
  210. package/dist/api/http/routes/endpoints.d.ts +0 -7
  211. package/dist/api/http/routes/endpoints.js +0 -100
  212. package/dist/host/admin-api.d.ts +0 -30
  213. package/dist/host/admin-api.js +0 -140
  214. package/dist/mcp/stdio.d.ts +0 -17
  215. package/dist/mcp/stdio.js +0 -82
  216. package/dist/tenant/actions.d.ts +0 -14
  217. package/dist/tenant/actions.js +0 -29
  218. package/dist/tenant/delivery-token.d.ts +0 -41
  219. package/dist/tenant/delivery-token.js +0 -104
  220. package/dist/tenant/delivery.d.ts +0 -62
  221. package/dist/tenant/delivery.js +0 -481
  222. package/dist/tenant/endpoints.d.ts +0 -67
  223. package/dist/tenant/endpoints.js +0 -90
@@ -1,8 +0,0 @@
1
- /**
2
- * Session commands, and the callbacks an Action endpoint makes about the Action it was
3
- * delivered: heartbeats and sandbox tools, with the Action's delivery token (Action endpoints).
4
- * The background result is in `endpoints.ts`.
5
- */
6
- import type { OpenAPIHono } from "@hono/zod-openapi";
7
- import type { TenantEnv } from "../app.js";
8
- export declare function actionRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -1,75 +0,0 @@
1
- import { z } from "zod";
2
- import { SessionCommandSchema } from "@nylorun/core/contracts";
3
- import { AcceptedResponse, DeliveryHeartbeatResponse, SandboxToolOutcome, SessionCommand, } from "../../components.js";
4
- import { actionSandboxTool } from "../../../tenant/actions.js";
5
- import { command } from "../../../tenant/commands.js";
6
- import { deliveryHeartbeat } from "../../../tenant/delivery.js";
7
- import { fail } from "../../../tenant/http.js";
8
- import { readJson } from "../body.js";
9
- import { tenantRoute } from "../define.js";
10
- import { jsonResponse } from "../respond.js";
11
- /** An Action's callbacks: only the delivery token of its current delivery. */
12
- const CALLBACK = { credentials: ["delivery"], scopes: "never" };
13
- const json = (schema, description) => ({
14
- description,
15
- content: { "application/json": { schema } },
16
- });
17
- const body = (schema) => ({
18
- required: true,
19
- content: { "application/json": { schema } },
20
- });
21
- const actionId = z.object({ actionId: z.string() });
22
- const SANDBOX_TOOLS = ["bash", "read", "write", "edit", "grep", "glob"];
23
- function deliveryOf(scope) {
24
- if (scope.kind !== "delivery")
25
- return fail(403, "A delivery token is required");
26
- return scope;
27
- }
28
- export function actionRoutes(api) {
29
- tenantRoute(api, CALLBACK, {
30
- method: "post",
31
- path: "/v1/actions/{actionId}/sandbox/{tool}",
32
- tags: ["Action endpoints"],
33
- summary: "Run a sandbox tool for an Action being delivered",
34
- description: "Called by the Action endpoint with the Action's delivery token. The body is the tool's input.",
35
- request: {
36
- params: actionId.extend({ tool: z.enum(SANDBOX_TOOLS) }),
37
- body: body(z.looseObject({}).meta({ description: "The tool's input" })),
38
- },
39
- responses: {
40
- 200: json(SandboxToolOutcome, "The tool's output, or why it failed"),
41
- 409: { description: "The delivery was cancelled, lost or sent again" },
42
- },
43
- }, async (c) => jsonResponse(200, await actionSandboxTool(c.env.tenant, deliveryOf(c.get("scope")), c.req.param("actionId"), c.req.param("tool"), () => readJson(c.req.raw), c.req.raw.signal)));
44
- tenantRoute(api, CALLBACK, {
45
- method: "post",
46
- path: "/v1/actions/{actionId}/heartbeat",
47
- tags: ["Action endpoints"],
48
- summary: "Keep a background delivery alive",
49
- description: "Called by an Action endpoint that answered a delivery with 202: extends the delivery by a " +
50
- "lease and returns a fresh delivery token.",
51
- request: { params: actionId },
52
- responses: {
53
- 200: json(DeliveryHeartbeatResponse, "The delivery's new deadline, and a fresh token"),
54
- 409: { description: "The delivery was cancelled, lost or sent again" },
55
- },
56
- }, async (c) => jsonResponse(200, await deliveryHeartbeat(c.env.tenant, deliveryOf(c.get("scope")), c.req.param("actionId"))));
57
- tenantRoute(api, {
58
- credentials: ["application", "subject", "token"],
59
- scopes: ["sessions:own"],
60
- }, {
61
- method: "post",
62
- path: "/v1/sessions/{sessionId}/commands",
63
- tags: ["Sessions"],
64
- summary: "Send a command to a session",
65
- description: "A message starts a turn; the others answer what the session waits on, or cancel. Idempotent on the command's `idempotencyKey`.",
66
- request: {
67
- params: z.object({ sessionId: z.string() }),
68
- body: body(SessionCommand),
69
- },
70
- responses: {
71
- 200: json(AcceptedResponse, "The command, accepted"),
72
- 409: { description: "A turn is running, or the session does not wait on this" },
73
- },
74
- }, async (c) => jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c.req.raw)), c.get("scope"))));
75
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Action endpoints: where the Runtime delivers each agent's Actions (design: Action endpoints
3
- * §4.2). Registered, listed and removed with the application key; no subject reaches them.
4
- */
5
- import type { OpenAPIHono } from "@hono/zod-openapi";
6
- import type { TenantEnv } from "../app.js";
7
- export declare function endpointRoutes(api: OpenAPIHono<TenantEnv>): void;
@@ -1,100 +0,0 @@
1
- import { z } from "zod";
2
- import { PutEndpointsRequestSchema } from "@nylorun/core/contracts";
3
- import { DeleteEndpointResponse, EndpointPingResponse, ListEndpointsResponse, PutEndpointsRequest, } from "../../components.js";
4
- import { ActionOutcomeSchema } from "@nylorun/core/contracts";
5
- import { ActionOutcome, ActionResultReceipt } from "../../components.js";
6
- import { deliveryResult, pingEndpoint } from "../../../tenant/delivery.js";
7
- import { requireApplication } from "../../../tenant/auth.js";
8
- import { fail } from "../../../tenant/http.js";
9
- import { deleteEndpoint, listEndpoints, putEndpoints, } from "../../../tenant/endpoints.js";
10
- import { readJson } from "../body.js";
11
- import { tenantRoute } from "../define.js";
12
- import { jsonResponse } from "../respond.js";
13
- const APPLICATION = { credentials: ["application"], scopes: "never" };
14
- const json = (schema, description) => ({
15
- description,
16
- content: { "application/json": { schema } },
17
- });
18
- export function endpointRoutes(api) {
19
- tenantRoute(api, APPLICATION, {
20
- method: "get",
21
- path: "/v1/endpoints",
22
- tags: ["Action endpoints"],
23
- summary: "List Action endpoints",
24
- description: "Each agent's Action endpoint, with what recent deliveries say about it.",
25
- responses: { 200: json(ListEndpointsResponse, "The endpoints") },
26
- }, async (c) => {
27
- requireApplication(c.get("scope"));
28
- return jsonResponse(200, await listEndpoints(c.env.tenant));
29
- });
30
- tenantRoute(api, APPLICATION, {
31
- method: "put",
32
- path: "/v1/endpoints",
33
- tags: ["Action endpoints"],
34
- summary: "Register Action endpoints",
35
- description: "Points the Runtime at the URL that runs each agent's Actions. A new URL starts with no " +
36
- "health.",
37
- request: {
38
- body: { required: true, content: { "application/json": { schema: PutEndpointsRequest } } },
39
- },
40
- responses: { 200: json(ListEndpointsResponse, "The registered endpoints") },
41
- }, async (c) => {
42
- const principalId = requireApplication(c.get("scope"));
43
- return jsonResponse(200, await putEndpoints(c.env.tenant, principalId, PutEndpointsRequestSchema.parse(await readJson(c.req.raw))));
44
- });
45
- tenantRoute(api, APPLICATION, {
46
- method: "delete",
47
- path: "/v1/endpoints/{agentId}",
48
- tags: ["Action endpoints"],
49
- summary: "Remove an agent's Action endpoint",
50
- description: "The agent's pending Actions wait until an endpoint is registered again.",
51
- request: { params: z.object({ agentId: z.string() }) },
52
- responses: {
53
- 200: json(DeleteEndpointResponse, "The endpoint was removed"),
54
- 404: { description: "The agent has no endpoint" },
55
- },
56
- }, async (c) => {
57
- requireApplication(c.get("scope"));
58
- return jsonResponse(200, await deleteEndpoint(c.env.tenant, c.req.param("agentId")));
59
- });
60
- tenantRoute(api, APPLICATION, {
61
- method: "post",
62
- path: "/v1/endpoints/{agentId}/ping",
63
- tags: ["Action endpoints"],
64
- summary: "Ping an agent's Action endpoint",
65
- description: "Sends a signed ping through the endpoint and records what it answers it serves. A " +
66
- "wrong URL, a tunnel that is down or a handler that does not serve the agent answers 502.",
67
- request: { params: z.object({ agentId: z.string() }) },
68
- responses: {
69
- 200: json(EndpointPingResponse, "What the endpoint serves"),
70
- 404: { description: "The agent has no endpoint" },
71
- 502: { description: "The endpoint did not answer the ping" },
72
- },
73
- }, async (c) => {
74
- requireApplication(c.get("scope"));
75
- return jsonResponse(200, await pingEndpoint(c.env.tenant, c.req.param("agentId")));
76
- });
77
- tenantRoute(api, { credentials: ["delivery"], scopes: "never" }, {
78
- method: "post",
79
- path: "/v1/actions/{actionId}/result",
80
- tags: ["Action endpoints"],
81
- summary: "Post the outcome of an Action answered with 202",
82
- description: "Called by an Action endpoint with the Action's delivery token after it answered the " +
83
- "delivery with 202. The same result again returns the first receipt.",
84
- request: {
85
- params: z.object({ actionId: z.string() }),
86
- body: { required: true, content: { "application/json": { schema: ActionOutcome } } },
87
- },
88
- responses: {
89
- 200: json(ActionResultReceipt, "The outcome was recorded"),
90
- 409: {
91
- description: "The delivery was cancelled, lost or sent again, or the Action has another result",
92
- },
93
- },
94
- }, async (c) => {
95
- const scope = c.get("scope");
96
- if (scope.kind !== "delivery")
97
- return fail(403, "A delivery token is required");
98
- return jsonResponse(200, await deliveryResult(c.env.tenant, scope, c.req.param("actionId"), ActionOutcomeSchema.parse(await readJson(c.req.raw))));
99
- });
100
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * The Admin API (`/v1/admin/**`): the Host's status and shutdown, and the Tenant's operator
3
- * keys (F9 I1), for a caller with the admin key. A Host serves one Tenant, so there are no
4
- * Tenant routes: the status names the Tenant and, when it could not be opened, why
5
- * (tenancy.md §9). The Host checks the listener, the protocol and the admin key first
6
- * (`app.ts`); what gets here is authorized. Routes are declared once, for serving and for the
7
- * OpenAPI document.
8
- */
9
- import { OpenAPIHono } from "@hono/zod-openapi";
10
- import type { AdminStatus } from "@nylorun/core/contracts";
11
- import type { OperatorKeys } from "../tenant/operator-keys.js";
12
- import type { NodeBindings } from "../tenant/types.js";
13
- export interface AdminApiOptions {
14
- /** `GET /v1/admin/status`. */
15
- status(): Promise<AdminStatus>;
16
- /** Stops the Host once the shutdown answer is sent. */
17
- shutdown(): void;
18
- /** `GET /v1/admin/openapi.json`: this API's OpenAPI document (`api/openapi.ts`). */
19
- document?(): unknown;
20
- /**
21
- * The open Tenant's operator keys (`/v1/admin/keys`); undefined while the Tenant is not open.
22
- * Rejects with `TenantUnavailableError` (503) when it cannot be reached.
23
- */
24
- operatorKeys?(): Promise<OperatorKeys | undefined>;
25
- }
26
- type AdminEnv = {
27
- Bindings: NodeBindings;
28
- };
29
- export declare function createAdminApi(options: AdminApiOptions): OpenAPIHono<AdminEnv>;
30
- export {};
@@ -1,140 +0,0 @@
1
- /**
2
- * The Admin API (`/v1/admin/**`): the Host's status and shutdown, and the Tenant's operator
3
- * keys (F9 I1), for a caller with the admin key. A Host serves one Tenant, so there are no
4
- * Tenant routes: the status names the Tenant and, when it could not be opened, why
5
- * (tenancy.md §9). The Host checks the listener, the protocol and the admin key first
6
- * (`app.ts`); what gets here is authorized. Routes are declared once, for serving and for the
7
- * OpenAPI document.
8
- */
9
- import { OpenAPIHono } from "@hono/zod-openapi";
10
- import { z } from "zod";
11
- import { AdminStatus as AdminStatusBody, DeleteOperatorKeyResponse, HostShutdownResponse, ListOperatorKeysResponse, PutOperatorKeyResponse, ProtocolRejected, Rejected, } from "../api/components.js";
12
- import { serveRoute } from "../api/route.js";
13
- import { jsonResponse, rejectedResponse } from "./http.js";
14
- const json = (schema, description) => ({
15
- description,
16
- content: { "application/json": { schema } },
17
- });
18
- const rejected = (description) => json(Rejected, description);
19
- /** What every Admin route may answer before its own logic runs. */
20
- const gated = {
21
- 404: rejected("Not found: an unknown admin key, or the public listener"),
22
- 426: json(ProtocolRejected, "`Nylorun-Protocol` missing or unsupported"),
23
- };
24
- function adminRoute(route) {
25
- return {
26
- ...route,
27
- tags: ["Admin"],
28
- security: [{ adminKey: [] }],
29
- responses: { ...gated, ...route.responses },
30
- };
31
- }
32
- export function createAdminApi(options) {
33
- const api = new OpenAPIHono();
34
- // HEAD is not a GET here: an Admin route answers only the methods it declares.
35
- api.use(async (c, next) => {
36
- if (c.req.method === "HEAD")
37
- return notFound();
38
- await next();
39
- });
40
- // D12: `/v1/admin/status` and `/v1/admin/host` are one answer.
41
- for (const path of ["/v1/admin/status", "/v1/admin/host"])
42
- serveRoute(api, adminRoute({
43
- method: "get",
44
- path,
45
- summary: path.endsWith("host") ? "Get the Host's status (alias)" : "Get the Host's status",
46
- responses: { 200: json(AdminStatusBody, "Version, protocol, the Tenant and the Host") },
47
- }), async () => jsonResponse(200, await options.status()));
48
- serveRoute(api, adminRoute({
49
- method: "post",
50
- path: "/v1/admin/host/shutdown",
51
- summary: "Shut the Host down",
52
- description: "Answers, then stops the Host. Served by a self-hosted Runtime only.",
53
- responses: { 200: json(HostShutdownResponse, "The Host is stopping") },
54
- }), (c) => {
55
- c.env.outgoing.once("finish", () => options.shutdown());
56
- return jsonResponse(200, { status: "shutting_down" });
57
- });
58
- const keyId = z.object({
59
- id: z.string().meta({ description: "The key's name: `^[a-z][a-z0-9-]{0,31}$`, never `studio`" }),
60
- });
61
- const keysOf = async () => (await options.operatorKeys?.()) ??
62
- rejectedResponse(503, "request_rejected", "The Tenant is not open: see /v1/admin/status");
63
- serveRoute(api, adminRoute({
64
- method: "get",
65
- path: "/v1/admin/keys",
66
- summary: "List the Tenant's keys",
67
- description: "Every application key of the Tenant by name, with its role and when it was issued. Keys themselves are never shown again.",
68
- responses: {
69
- 200: json(ListOperatorKeysResponse, "The keys, by id"),
70
- 503: rejected("The Tenant is not open"),
71
- },
72
- }), async () => {
73
- const keys = await keysOf();
74
- if (keys instanceof Response)
75
- return keys;
76
- return jsonResponse(200, { keys: await keys.list() });
77
- });
78
- serveRoute(api, adminRoute({
79
- method: "put",
80
- path: "/v1/admin/keys/{id}",
81
- summary: "Create or rotate a key",
82
- description: "Issues a new key named `id` and returns it this once. A key of that name is replaced: the old one stops authenticating at once. `studio` is refused.",
83
- request: { params: keyId },
84
- responses: {
85
- 200: json(PutOperatorKeyResponse, "The new key, shown once"),
86
- 400: rejected("Not a key name, or `studio`"),
87
- 503: rejected("The Tenant is not open"),
88
- },
89
- }), async (c) => {
90
- const keys = await keysOf();
91
- if (keys instanceof Response)
92
- return keys;
93
- const put = await keys.put(c.req.param("id") ?? "");
94
- return "reason" in put ? refusedResponse(put) : jsonResponse(200, put);
95
- });
96
- serveRoute(api, adminRoute({
97
- method: "delete",
98
- path: "/v1/admin/keys/{id}",
99
- summary: "Delete a key",
100
- description: "The key stops authenticating at once. `studio` is refused.",
101
- request: { params: keyId },
102
- responses: {
103
- 200: json(DeleteOperatorKeyResponse, "The key is deleted"),
104
- 400: rejected("Not a key name, or `studio`"),
105
- 404: rejected("No key of that name; or an unknown admin key, or the public listener"),
106
- 503: rejected("The Tenant is not open"),
107
- },
108
- }), async (c) => {
109
- const keys = await keysOf();
110
- if (keys instanceof Response)
111
- return keys;
112
- const id = c.req.param("id") ?? "";
113
- const deleted = await keys.delete(id);
114
- if (typeof deleted !== "boolean")
115
- return refusedResponse(deleted);
116
- return deleted
117
- ? jsonResponse(200, { id, deleted: true })
118
- : rejectedResponse(404, "not_found", `No key ${id}`);
119
- });
120
- serveRoute(api, adminRoute({
121
- method: "get",
122
- path: "/v1/admin/openapi.json",
123
- summary: "Get this document",
124
- responses: { 200: { description: "The Admin API's OpenAPI document" } },
125
- }), () => options.document
126
- ? jsonResponse(200, options.document(), { "cache-control": "no-cache" })
127
- : notFound());
128
- api.notFound(() => notFound());
129
- // The Host answers what a route throws (`app.ts`), as it did before Hono.
130
- api.onError((error) => {
131
- throw error;
132
- });
133
- return api;
134
- }
135
- function notFound() {
136
- return rejectedResponse(404, "not_found", "Route not found");
137
- }
138
- function refusedResponse(refusal) {
139
- return rejectedResponse(400, refusal.reason === "invalid" ? "invalid_request" : "request_rejected", refusal.message);
140
- }
@@ -1,17 +0,0 @@
1
- import type { McpServerManifest } from "@nylorun/core/define";
2
- /** Single-pass replacement. Text inserted for a token is not scanned again. */
3
- export declare function expandPluginPlaceholders(value: string, pluginRoot: string, pluginData: string): string;
4
- export interface StdioLaunch {
5
- readonly command: string;
6
- readonly args: readonly string[];
7
- readonly cwd: string;
8
- readonly env: Readonly<Record<string, string>>;
9
- }
10
- export declare function prepareStdioLaunch(server: Extract<McpServerManifest, {
11
- type: "stdio";
12
- }>, options: {
13
- readonly pluginRoot?: string;
14
- readonly pluginData: string;
15
- /** Allowlisted base env (Tenant HOME/TMPDIR/PATH). Manifest env overlays it (A9). */
16
- readonly childEnv?: Readonly<Record<string, string>>;
17
- }): StdioLaunch;
package/dist/mcp/stdio.js DELETED
@@ -1,82 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import { isAbsolute, relative, resolve, sep } from "node:path";
3
- const ROOT_TOKEN = "${PLUGIN_ROOT}";
4
- const DATA_TOKEN = "${PLUGIN_DATA}";
5
- /** Single-pass replacement. Text inserted for a token is not scanned again. */
6
- export function expandPluginPlaceholders(value, pluginRoot, pluginData) {
7
- let out = "";
8
- for (let index = 0; index < value.length;) {
9
- if (value.startsWith(ROOT_TOKEN, index)) {
10
- out += pluginRoot;
11
- index += ROOT_TOKEN.length;
12
- continue;
13
- }
14
- if (value.startsWith(DATA_TOKEN, index)) {
15
- out += pluginData;
16
- index += DATA_TOKEN.length;
17
- continue;
18
- }
19
- out += value[index];
20
- index += 1;
21
- }
22
- return out;
23
- }
24
- export function prepareStdioLaunch(server, options) {
25
- const pluginData = options.pluginData;
26
- mkdirSync(pluginData, { recursive: true });
27
- const pluginRoot = options.pluginRoot;
28
- if (needsPluginRoot(server) && !pluginRoot)
29
- throw new Error("stdio server requires a plugin root");
30
- for (const key of Object.keys(server.env ?? {})) {
31
- if (key === "PLUGIN_ROOT" || key === "PLUGIN_DATA")
32
- throw new Error(`stdio env must not set ${key}`);
33
- }
34
- const command = server.command.startsWith("./")
35
- ? resolve(pluginRoot, server.command)
36
- : server.command;
37
- const args = (server.args ?? []).map((arg) => expand(arg, pluginRoot, pluginData));
38
- const cwd = server.cwd
39
- ? resolveContainedCwd(server.cwd, pluginRoot, pluginData)
40
- : (pluginRoot ?? pluginData);
41
- // childEnv under manifest env; HOME/TMPDIR from childEnv override Host defaults
42
- // that StdioClientTransport merges via getDefaultEnvironment() (R2).
43
- const env = { ...(options.childEnv ?? {}) };
44
- for (const [key, value] of Object.entries(server.env ?? {}))
45
- env[key] = expand(value, pluginRoot, pluginData);
46
- if (pluginRoot)
47
- env.PLUGIN_ROOT = pluginRoot;
48
- env.PLUGIN_DATA = pluginData;
49
- return { command, args, cwd, env };
50
- }
51
- function needsPluginRoot(server) {
52
- if (server.command.startsWith("./"))
53
- return true;
54
- const values = [
55
- ...(server.args ?? []),
56
- ...Object.values(server.env ?? {}),
57
- ...(server.cwd === undefined ? [] : [server.cwd]),
58
- ];
59
- return values.some((value) => value.includes(ROOT_TOKEN));
60
- }
61
- function expand(value, pluginRoot, pluginData) {
62
- if (value.includes(ROOT_TOKEN) && !pluginRoot)
63
- throw new Error("stdio server requires a plugin root");
64
- return expandPluginPlaceholders(value, pluginRoot ?? "", pluginData);
65
- }
66
- function resolveContainedCwd(cwd, pluginRoot, pluginData) {
67
- const expanded = expand(cwd, pluginRoot, pluginData);
68
- const base = cwdTargetsData(cwd) ? pluginData : pluginRoot;
69
- if (!base)
70
- throw new Error("stdio server requires a plugin root");
71
- const absolute = isAbsolute(expanded) ? expanded : resolve(base, expanded);
72
- if (!isInside(base, absolute))
73
- throw new Error(`Working directory escapes ${base}`);
74
- return absolute;
75
- }
76
- function cwdTargetsData(cwd) {
77
- return cwd === DATA_TOKEN || cwd.startsWith(`${DATA_TOKEN}/`);
78
- }
79
- function isInside(root, target) {
80
- const rel = relative(root, target);
81
- return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
82
- }
@@ -1,14 +0,0 @@
1
- import { type SandboxRouteDeps } from "../core/sandbox-routes.js";
2
- import { type TenantContext } from "./context.js";
3
- import type { DeliveryScope } from "./delivery-token.js";
4
- /** Session and Action lookups for the sandbox route handlers. */
5
- export declare function sandboxRouteDeps(ctx: TenantContext): SandboxRouteDeps;
6
- /** `POST /v1/actions/:id/sandbox/:tool`: a sandbox tool call for the Action being delivered. */
7
- export declare function actionSandboxTool(ctx: TenantContext, scope: DeliveryScope, actionId: string, toolName: string, body: () => Promise<unknown>, signal: AbortSignal): Promise<{
8
- kind: "completed";
9
- output: unknown;
10
- } | {
11
- kind: "failed";
12
- code: string;
13
- message: string;
14
- }>;
@@ -1,29 +0,0 @@
1
- import { SandboxRouteError, handleActionSandboxTool, } from "../core/sandbox-routes.js";
2
- import { scoped } from "./auth.js";
3
- import { sandboxLookup, sessionOf } from "./context.js";
4
- import { fail } from "./http.js";
5
- /** Session and Action lookups for the sandbox route handlers. */
6
- export function sandboxRouteDeps(ctx) {
7
- return {
8
- sandbox: ctx.sandbox,
9
- session: (id) => ctx.store.tx(async (t) => {
10
- const session = await sessionOf(t, id);
11
- return { session, lookup: await sandboxLookup(t, id) };
12
- }),
13
- getAction: (id) => ctx.store.tx((t) => t.get("actions", id)),
14
- };
15
- }
16
- /** `POST /v1/actions/:id/sandbox/:tool`: a sandbox tool call for the Action being delivered. */
17
- export async function actionSandboxTool(ctx, scope, actionId, toolName, body, signal) {
18
- const action = (await ctx.store.tx((t) => t.get("actions", actionId))) ??
19
- fail(404, "Action not found");
20
- scoped(scope, action);
21
- try {
22
- return await handleActionSandboxTool(sandboxRouteDeps(ctx), actionId, toolName, await body(), signal, { generation: scope.generation });
23
- }
24
- catch (error) {
25
- if (error instanceof SandboxRouteError)
26
- fail(error.status, error.message);
27
- throw error;
28
- }
29
- }
@@ -1,41 +0,0 @@
1
- import type { AuthScope, TenantContext } from "./context.js";
2
- /** What a delivery token is for: one delivery of an Action, or a ping. */
3
- export type DeliveryTokenFor = {
4
- kind: "action";
5
- actionId: string;
6
- agentId: string;
7
- generation: number;
8
- } | {
9
- kind: "ping";
10
- agentId: string;
11
- };
12
- export interface MintedDeliveryToken {
13
- token: string;
14
- /** Milliseconds since the epoch. */
15
- expiresAt: number;
16
- tokenId: string;
17
- keyId: string;
18
- }
19
- /** The request scope of a verified delivery token (`AuthScope` kind `delivery`). */
20
- export type DeliveryScope = Extract<AuthScope, {
21
- kind: "delivery";
22
- }>;
23
- /** base64url SHA-256 of a request body: the token's `bdy` claim. */
24
- export declare function bodyHash(body: string | Uint8Array): string;
25
- /**
26
- * Signs a token for `delivery`, for the endpoint at `audience`, covering `body`. `ttlSeconds`
27
- * is capped at `DELIVERY_TOKEN_MAX_TTL_SECONDS`.
28
- */
29
- export declare function mintDeliveryToken(ctx: TenantContext, options: {
30
- for: DeliveryTokenFor;
31
- audience: string;
32
- body: string | Uint8Array;
33
- ttlSeconds: number;
34
- }): Promise<MintedDeliveryToken>;
35
- /**
36
- * Verifies a delivery token presented as a bearer. Answers `401 token_expired` for an expired
37
- * token or a revoked key, and the opaque 404 for everything else, a ping token included: only
38
- * an Action's token reaches its callbacks. Whether the Action is still being delivered at the
39
- * token's generation is checked where the Action is read (`auth.ts` `scoped`).
40
- */
41
- export declare function verifyDeliveryToken(ctx: TenantContext, raw: string): Promise<DeliveryScope>;
@@ -1,104 +0,0 @@
1
- /**
2
- * Delivery tokens (design: Action endpoints §8.1): the ES256 JWT the Runtime signs, with the
3
- * Tenant's current signing key, for one delivery of one Action, or for a ping. It travels in
4
- * `Nylorun-Signature` on the request to the Action endpoint, which verifies it with the
5
- * Tenant's public keys, and comes back as the bearer of that Action's callbacks (a background
6
- * result, heartbeats, sandbox tools).
7
- *
8
- * A token lives at most `DELIVERY_TOKEN_MAX_TTL_SECONDS` (`TOKEN_TTL_MAX_SECONDS`), and key
9
- * rotation waits at least that long before it revokes the previous key
10
- * (`signing-keys.ts`), so a rotation never revokes a live token.
11
- */
12
- import { createHash, randomUUID } from "node:crypto";
13
- import { errors, jwtVerify } from "jose";
14
- import { DELIVERY_TOKEN_MAX_TTL_SECONDS, DELIVERY_TOKEN_TYPE, tenantTokenIssuer, } from "@nylorun/core/contracts";
15
- import { failOpaque, HttpError } from "./http.js";
16
- import { CLOCK_TOLERANCE_SECONDS, tokenKeyId } from "./jwt.js";
17
- /** base64url SHA-256 of a request body: the token's `bdy` claim. */
18
- export function bodyHash(body) {
19
- return createHash("sha256").update(body).digest("base64url");
20
- }
21
- /**
22
- * Signs a token for `delivery`, for the endpoint at `audience`, covering `body`. `ttlSeconds`
23
- * is capped at `DELIVERY_TOKEN_MAX_TTL_SECONDS`.
24
- */
25
- export async function mintDeliveryToken(ctx, options) {
26
- const ttl = Math.max(1, Math.min(Math.floor(options.ttlSeconds), DELIVERY_TOKEN_MAX_TTL_SECONDS));
27
- const iat = Math.floor(Date.now() / 1000);
28
- const tokenId = randomUUID();
29
- const delivery = options.for;
30
- // Signed by the keys service (F4.2): this process never holds the private key.
31
- const { token, keyId } = await ctx.keys.sign({
32
- typ: DELIVERY_TOKEN_TYPE,
33
- claims: {
34
- agt: delivery.agentId,
35
- gen: delivery.kind === "action" ? delivery.generation : 0,
36
- bdy: bodyHash(options.body),
37
- iss: tenantTokenIssuer(ctx.config.tenantId),
38
- aud: options.audience,
39
- sub: delivery.kind === "action" ? delivery.actionId : "ping",
40
- iat,
41
- exp: iat + ttl,
42
- jti: tokenId,
43
- },
44
- });
45
- return { token, expiresAt: (iat + ttl) * 1000, tokenId, keyId };
46
- }
47
- /**
48
- * Verifies a delivery token presented as a bearer. Answers `401 token_expired` for an expired
49
- * token or a revoked key, and the opaque 404 for everything else, a ping token included: only
50
- * an Action's token reaches its callbacks. Whether the Action is still being delivered at the
51
- * token's generation is checked where the Action is read (`auth.ts` `scoped`).
52
- */
53
- export async function verifyDeliveryToken(ctx, raw) {
54
- const checked = tokenKeyId(raw, DELIVERY_TOKEN_TYPE);
55
- if ("refused" in checked)
56
- return refused(ctx, `delivery_token_${checked.refused}`);
57
- const { kid } = checked;
58
- const row = await ctx.store.tx((t) => t.signingKey(kid));
59
- if (!row)
60
- return refused(ctx, "delivery_token_key_unknown");
61
- let claims;
62
- try {
63
- const verified = await jwtVerify(raw, await ctx.signingKeys.publicKey(row), {
64
- algorithms: ["ES256"],
65
- issuer: tenantTokenIssuer(ctx.config.tenantId),
66
- typ: DELIVERY_TOKEN_TYPE,
67
- clockTolerance: CLOCK_TOLERANCE_SECONDS,
68
- requiredClaims: ["sub", "aud", "iat", "exp", "jti"],
69
- });
70
- claims = verified.payload;
71
- }
72
- catch (error) {
73
- if (error instanceof errors.JWTExpired)
74
- expired(ctx, "delivery_token_expired");
75
- return refused(ctx, "delivery_token_invalid");
76
- }
77
- if (row.state === "revoked")
78
- expired(ctx, "delivery_token_key_revoked");
79
- const { sub, agt, gen, exp, iat, jti } = claims;
80
- if (sub === "ping" ||
81
- typeof agt !== "string" ||
82
- typeof gen !== "number" ||
83
- !Number.isInteger(gen) ||
84
- gen < 1 ||
85
- exp - iat > DELIVERY_TOKEN_MAX_TTL_SECONDS)
86
- return refused(ctx, "delivery_token_claims");
87
- return {
88
- kind: "delivery",
89
- actionId: sub,
90
- agentId: agt,
91
- generation: gen,
92
- expiresAt: exp * 1000,
93
- tokenId: jti,
94
- keyId: kid,
95
- };
96
- }
97
- function refused(ctx, reason) {
98
- ctx.config.logger.warn("credential rejected", { reason });
99
- return failOpaque();
100
- }
101
- function expired(ctx, reason) {
102
- ctx.config.logger.warn("delivery token refused", { reason });
103
- throw new HttpError(401, "The delivery token has expired or its key was revoked", { code: "token_expired" }, { "www-authenticate": 'Bearer error="invalid_token"' });
104
- }