@nylorun/runtime 0.17.0-beta → 0.19.1-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.
- package/CHANGELOG.md +155 -0
- package/README.md +89 -58
- package/dist/adapters/execution/restate.d.ts +1 -3
- package/dist/adapters/execution/restate.js +4 -31
- package/dist/adapters/replication/pgoutput.js +1 -1
- package/dist/adapters/sandbox/local.js +1 -1
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/api/ag-ui/routes.js +1 -1
- package/dist/api/ag-ui/translate.d.ts +1 -3
- package/dist/api/ag-ui/translate.js +2 -17
- package/dist/api/components.d.ts +4871 -2614
- package/dist/api/components.js +12 -11
- package/dist/api/gate/routes.d.ts +7 -8
- package/dist/api/gate/routes.js +58 -52
- package/dist/api/http/app.d.ts +6 -0
- package/dist/api/http/app.js +18 -14
- package/dist/api/http/define.d.ts +14 -2
- package/dist/api/http/define.js +34 -15
- package/dist/api/http/routes/access.d.ts +3 -3
- package/dist/api/http/routes/access.js +18 -17
- package/dist/api/http/routes/artifacts.js +2 -4
- package/dist/api/http/routes/files.d.ts +8 -0
- package/dist/api/http/routes/files.js +77 -0
- package/dist/api/http/routes/keys.d.ts +10 -0
- package/dist/api/http/routes/keys.js +64 -0
- package/dist/api/http/routes/me.js +5 -2
- package/dist/api/http/routes/reads.d.ts +66 -0
- package/dist/api/http/routes/reads.js +127 -0
- package/dist/api/http/routes/sandboxes.js +7 -4
- package/dist/api/http/routes/sessions.js +25 -8
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +10 -9
- package/dist/api/http/routes/vaults.js +27 -23
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +288 -63
- package/dist/artifacts/links.js +1 -1
- package/dist/core/flow-host.d.ts +44 -23
- package/dist/core/flow-host.js +88 -106
- package/dist/core/provider.d.ts +4 -1
- package/dist/core/provider.js +6 -1
- package/dist/core/sandbox-routes.d.ts +0 -9
- package/dist/core/sandbox-routes.js +2 -21
- package/dist/execution/memory.d.ts +4 -5
- package/dist/execution/memory.js +11 -25
- package/dist/execution/types.d.ts +3 -33
- package/dist/execution/types.js +2 -12
- package/dist/gates/http-tool.d.ts +86 -0
- package/dist/gates/http-tool.js +197 -0
- package/dist/gates/mcp-handler.d.ts +3 -0
- package/dist/gates/mcp-handler.js +1 -3
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +5 -9
- package/dist/gates/tool-calls.d.ts +7 -15
- package/dist/gates/tool-calls.js +4 -4
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +23 -47
- package/dist/gates/tool-contract.d.ts +27 -39
- package/dist/gates/tool-contract.js +30 -21
- package/dist/gates/tool-gate.d.ts +25 -24
- package/dist/gates/tool-gate.js +11 -7
- package/dist/harness/calls.d.ts +10 -4
- package/dist/harness/calls.js +15 -6
- package/dist/harness/executors.d.ts +1 -1
- package/dist/harness/executors.js +17 -12
- package/dist/harness/main.d.ts +2 -2
- package/dist/harness/main.js +5 -13
- package/dist/harness/service.d.ts +4 -5
- package/dist/harness/service.js +9 -2
- package/dist/harness-api/in-process.js +2 -2
- package/dist/harness-api/record.d.ts +6 -3
- package/dist/harness-api/record.js +65 -72
- package/dist/harness-api/server.d.ts +1 -9
- package/dist/harness-api/server.js +19 -17
- package/dist/harness-api/start.js +1 -5
- package/dist/harness-api/workspace.js +2 -3
- package/dist/harness-api/ws-server.js +1 -1
- package/dist/host/app.d.ts +2 -9
- package/dist/host/app.js +15 -25
- package/dist/host/config-for.d.ts +1 -5
- package/dist/host/config-for.js +0 -3
- package/dist/host/config.d.ts +0 -5
- package/dist/host/create-host.d.ts +3 -33
- package/dist/host/create-host.js +18 -94
- package/dist/host/environment.d.ts +0 -3
- package/dist/host/environment.js +0 -9
- package/dist/host/gates.d.ts +4 -1
- package/dist/host/gates.js +3 -2
- package/dist/host/http.d.ts +1 -2
- package/dist/host/http.js +1 -7
- package/dist/host/index.d.ts +2 -2
- package/dist/host/index.js +2 -2
- package/dist/host/main.js +27 -34
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +12 -12
- package/dist/host/stack-config.js +5 -32
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/keys.js +2 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +6 -26
- package/dist/mcp/pool.d.ts +5 -9
- package/dist/mcp/pool.js +8 -19
- package/dist/mcp/snapshot.d.ts +8 -2
- package/dist/mcp/snapshot.js +29 -2
- package/dist/media.d.ts +1 -1
- package/dist/media.js +1 -1
- package/dist/model/pi-model.js +3 -0
- package/dist/openapi.json +9372 -17030
- package/dist/reads/access.d.ts +4 -0
- package/dist/reads/access.js +12 -0
- package/dist/reads/cursor.d.ts +11 -0
- package/dist/reads/cursor.js +80 -0
- package/dist/reads/types.d.ts +25 -0
- package/dist/reads/types.js +1 -0
- package/dist/sandbox/manager.d.ts +16 -0
- package/dist/sandbox/manager.js +40 -0
- package/dist/sandbox/session-sandbox.js +13 -3
- package/dist/sandbox/skills.d.ts +13 -0
- package/dist/sandbox/skills.js +46 -0
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/ownership.d.ts +9 -0
- package/dist/store/ownership.js +13 -0
- package/dist/store/postgres/connect.d.ts +2 -0
- package/dist/store/postgres/connect.js +29 -0
- package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
- package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
- package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
- package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/reads.d.ts +3 -0
- package/dist/store/postgres/reads.js +272 -0
- package/dist/store/postgres/schema.d.ts +294 -489
- package/dist/store/postgres/schema.js +41 -76
- package/dist/store/postgres/store.js +51 -154
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +27 -96
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -2
- package/dist/streams/control.js +0 -5
- package/dist/streams/types.d.ts +1 -10
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +14 -9
- package/dist/tenant/auth.js +24 -28
- package/dist/tenant/cause.d.ts +3 -3
- package/dist/tenant/cause.js +1 -1
- package/dist/tenant/commands.d.ts +2 -18
- package/dist/tenant/commands.js +35 -172
- package/dist/tenant/context.d.ts +14 -24
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +18 -20
- package/dist/tenant/effects.js +56 -119
- package/dist/tenant/ephemeral.d.ts +7 -6
- package/dist/tenant/ephemeral.js +3 -11
- package/dist/tenant/host-token.js +1 -1
- package/dist/tenant/jwt.js +1 -1
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +34 -8
- package/dist/tenant/operator-keys.js +50 -18
- package/dist/tenant/outbound.d.ts +10 -1
- package/dist/tenant/outbound.js +44 -27
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +11 -2
- package/dist/tenant/principals.js +27 -8
- package/dist/tenant/run-token.js +3 -3
- package/dist/tenant/runtime.d.ts +4 -6
- package/dist/tenant/runtime.js +19 -18
- package/dist/tenant/sandboxes.d.ts +4 -1
- package/dist/tenant/sandboxes.js +11 -0
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +2 -1
- package/dist/tenant/session-streams.js +39 -5
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.js +16 -8
- package/dist/tenant/signing-keys.d.ts +3 -3
- package/dist/tenant/signing-keys.js +5 -5
- package/dist/tenant/skill-tool.d.ts +20 -0
- package/dist/tenant/skill-tool.js +72 -0
- package/dist/tenant/slim.d.ts +1 -1
- package/dist/tenant/slim.js +1 -1
- package/dist/tenant/status.js +7 -12
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +7 -3
- package/dist/tenant/streams.d.ts +3 -10
- package/dist/tenant/streams.js +1 -24
- package/dist/tenant/sweep.js +0 -5
- package/dist/tenant/transcript.d.ts +2 -2
- package/dist/tenant/transcript.js +2 -2
- package/dist/tenant/types.d.ts +15 -22
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/sources.d.ts +7 -1
- package/dist/vault/sources.js +22 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/admin-openapi.json +0 -904
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -75
- package/dist/api/http/routes/endpoints.d.ts +0 -7
- package/dist/api/http/routes/endpoints.js +0 -100
- package/dist/host/admin-api.d.ts +0 -30
- package/dist/host/admin-api.js +0 -140
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- package/dist/tenant/delivery-token.d.ts +0 -41
- package/dist/tenant/delivery-token.js +0 -104
- package/dist/tenant/delivery.d.ts +0 -62
- package/dist/tenant/delivery.js +0 -481
- package/dist/tenant/endpoints.d.ts +0 -67
- package/dist/tenant/endpoints.js +0 -90
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DEFINITION_FILE_MAX_BYTES } from "@nylorun/core/contracts";
|
|
3
|
+
import { DefinitionFileView } from "../../components.js";
|
|
4
|
+
import { normalizeContentType } from "../../../artifacts/media-types.js";
|
|
5
|
+
import { hasDefinitionFile, putDefinitionFile } from "../../../tenant/definition-files.js";
|
|
6
|
+
import { fail } from "../../../tenant/http.js";
|
|
7
|
+
import { tenantRoute } from "../define.js";
|
|
8
|
+
import { jsonResponse } from "../respond.js";
|
|
9
|
+
const APPLICATION = { credentials: ["application"], scopes: "never" };
|
|
10
|
+
const UPLOAD = { ...APPLICATION, bytes: true };
|
|
11
|
+
const file = z.object({
|
|
12
|
+
file: z.string().meta({ description: "`sha256:` and the SHA-256 of the file's bytes, 64 lowercase hex digits" }),
|
|
13
|
+
});
|
|
14
|
+
const view = (description) => ({
|
|
15
|
+
description,
|
|
16
|
+
content: { "application/json": { schema: DefinitionFileView } },
|
|
17
|
+
});
|
|
18
|
+
/** The declared `Content-Length`, when there is one. */
|
|
19
|
+
function declaredLength(request) {
|
|
20
|
+
const raw = request.headers.get("content-length");
|
|
21
|
+
return raw !== null && /^\d+$/.test(raw) ? Number(raw) : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** The declared media type, unless it is absent or `application/octet-stream`. */
|
|
24
|
+
function declaredType(request) {
|
|
25
|
+
const raw = request.headers.get("content-type") ?? undefined;
|
|
26
|
+
if (raw === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
const type = normalizeContentType(raw);
|
|
29
|
+
if (type === undefined)
|
|
30
|
+
return fail(400, "Content-Type is not a media type");
|
|
31
|
+
return type.toLowerCase().startsWith("application/octet-stream") ? undefined : type;
|
|
32
|
+
}
|
|
33
|
+
export function fileRoutes(api) {
|
|
34
|
+
tenantRoute(api, UPLOAD, {
|
|
35
|
+
method: "put",
|
|
36
|
+
path: "/v1/files/{file}",
|
|
37
|
+
tags: ["Definition files"],
|
|
38
|
+
summary: "Upload a definition file",
|
|
39
|
+
description: `Stores the body, at most ${DEFINITION_FILE_MAX_BYTES} bytes, at the SHA-256 the path names; a body with another hash stores nothing (\`400\`). A file the Tenant holds already is kept as it is (\`200\`).`,
|
|
40
|
+
request: {
|
|
41
|
+
params: file,
|
|
42
|
+
body: {
|
|
43
|
+
required: true,
|
|
44
|
+
description: "The file's bytes",
|
|
45
|
+
content: { "application/octet-stream": { schema: z.string().meta({ format: "binary" }) } },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
responses: {
|
|
49
|
+
200: view("The Tenant held the file already"),
|
|
50
|
+
201: view("The file, stored"),
|
|
51
|
+
413: { description: `Larger than ${DEFINITION_FILE_MAX_BYTES} bytes (\`limit_exceeded\`)` },
|
|
52
|
+
},
|
|
53
|
+
}, async (c) => {
|
|
54
|
+
const request = c.req.raw;
|
|
55
|
+
const length = declaredLength(request);
|
|
56
|
+
const type = declaredType(request);
|
|
57
|
+
const stored = await putDefinitionFile(c.env.tenant, c.req.param("file"), {
|
|
58
|
+
body: request.body,
|
|
59
|
+
...(length === undefined ? {} : { declaredBytes: length }),
|
|
60
|
+
...(type === undefined ? {} : { contentType: type }),
|
|
61
|
+
}, request.signal);
|
|
62
|
+
return jsonResponse(stored.status, stored.view);
|
|
63
|
+
});
|
|
64
|
+
tenantRoute(api, APPLICATION, {
|
|
65
|
+
method: "head",
|
|
66
|
+
path: "/v1/files/{file}",
|
|
67
|
+
tags: ["Definition files"],
|
|
68
|
+
summary: "Whether the Runtime holds a definition file",
|
|
69
|
+
description: "`200` when the Tenant holds the file, `404` when it does not: upload it.",
|
|
70
|
+
request: { params: file },
|
|
71
|
+
responses: { 200: { description: "The Tenant holds the file" } },
|
|
72
|
+
}, async (c) => {
|
|
73
|
+
if (!(await hasDefinitionFile(c.env.tenant, c.req.param("file"))))
|
|
74
|
+
return fail(404, "The Runtime does not hold this file");
|
|
75
|
+
return new Response(null, { status: 200 });
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application keys (`/v1/tenant/keys`, the Management API, protocol 8): a management key
|
|
3
|
+
* creates, rotates, lists and deletes the Tenant's application keys by name. It never touches a
|
|
4
|
+
* management key, `studio` or `bootstrap`: those come only from the Tenant's machine
|
|
5
|
+
* (`nylorun-operate`, `NYLORUN_MANAGEMENT_KEY_FILE`), so a leaked management key cannot mint
|
|
6
|
+
* another. A rotated or deleted key stops authenticating on its next request.
|
|
7
|
+
*/
|
|
8
|
+
import type { OpenAPIHono } from "@hono/zod-openapi";
|
|
9
|
+
import type { TenantEnv } from "../app.js";
|
|
10
|
+
export declare function keyRoutes(api: OpenAPIHono<TenantEnv>): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DeleteOperatorKeyResponse, ListOperatorKeysResponse, PutOperatorKeyResponse, } from "../../components.js";
|
|
3
|
+
import { fail } from "../../../tenant/http.js";
|
|
4
|
+
import { operatorKeys } from "../../../tenant/operator-keys.js";
|
|
5
|
+
import { tenantRoute } from "../define.js";
|
|
6
|
+
import { jsonResponse } from "../respond.js";
|
|
7
|
+
const MANAGEMENT = { credentials: ["management"], scopes: "never" };
|
|
8
|
+
const keyId = z.object({
|
|
9
|
+
keyId: z
|
|
10
|
+
.string()
|
|
11
|
+
.meta({ description: "The key's name: `^[a-z][a-z0-9-]{0,31}$`, never `studio` or `bootstrap`" }),
|
|
12
|
+
});
|
|
13
|
+
const json = (schema, description) => ({
|
|
14
|
+
description,
|
|
15
|
+
content: { "application/json": { schema } },
|
|
16
|
+
});
|
|
17
|
+
function refused(refusal) {
|
|
18
|
+
return fail(400, refusal.message, {
|
|
19
|
+
code: refusal.reason === "invalid" ? "invalid_request" : "request_rejected",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function keyRoutes(api) {
|
|
23
|
+
tenantRoute(api, MANAGEMENT, {
|
|
24
|
+
method: "get",
|
|
25
|
+
path: "/v1/tenant/keys",
|
|
26
|
+
tags: ["Application keys"],
|
|
27
|
+
summary: "List the Tenant's keys",
|
|
28
|
+
description: "Every key of the Tenant by name, with its role (`application`, `management` or `studio`) and when it was issued. Keys themselves are never shown again.",
|
|
29
|
+
responses: { 200: json(ListOperatorKeysResponse, "The keys, by id") },
|
|
30
|
+
}, async (c) => jsonResponse(200, { keys: await operatorKeys(c.env.tenant.store).list() }));
|
|
31
|
+
tenantRoute(api, MANAGEMENT, {
|
|
32
|
+
method: "put",
|
|
33
|
+
path: "/v1/tenant/keys/{keyId}",
|
|
34
|
+
tags: ["Application keys"],
|
|
35
|
+
summary: "Create or rotate an application key",
|
|
36
|
+
description: "Issues a new application key named `keyId` and returns it this once. An application key of that name is replaced: the old one stops authenticating at once. A management key, `studio` and `bootstrap` are refused.",
|
|
37
|
+
request: { params: keyId },
|
|
38
|
+
responses: {
|
|
39
|
+
200: json(PutOperatorKeyResponse, "The new key, shown once"),
|
|
40
|
+
400: { description: "Not a key name, a reserved name, or a management key's name" },
|
|
41
|
+
},
|
|
42
|
+
}, async (c) => {
|
|
43
|
+
const put = await operatorKeys(c.env.tenant.store).put(c.req.param("keyId"), "application");
|
|
44
|
+
return "reason" in put ? refused(put) : jsonResponse(200, put);
|
|
45
|
+
});
|
|
46
|
+
tenantRoute(api, MANAGEMENT, {
|
|
47
|
+
method: "delete",
|
|
48
|
+
path: "/v1/tenant/keys/{keyId}",
|
|
49
|
+
tags: ["Application keys"],
|
|
50
|
+
summary: "Delete an application key",
|
|
51
|
+
description: "The key stops authenticating at once. A management key, `studio` and `bootstrap` are refused.",
|
|
52
|
+
request: { params: keyId },
|
|
53
|
+
responses: {
|
|
54
|
+
200: json(DeleteOperatorKeyResponse, "The key is deleted"),
|
|
55
|
+
400: { description: "Not a key name, a reserved name, or a management key's name" },
|
|
56
|
+
},
|
|
57
|
+
}, async (c) => {
|
|
58
|
+
const id = c.req.param("keyId");
|
|
59
|
+
const deleted = await operatorKeys(c.env.tenant.store).delete(id, "application");
|
|
60
|
+
if (typeof deleted !== "boolean")
|
|
61
|
+
return refused(deleted);
|
|
62
|
+
return deleted ? jsonResponse(200, { id, deleted: true }) : fail(404, `No key ${id}`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -22,18 +22,21 @@ export function describeCaller(scope) {
|
|
|
22
22
|
sandboxes: [...(scope.sandboxes ?? [])],
|
|
23
23
|
via: `issuer:${scope.issuer}`,
|
|
24
24
|
};
|
|
25
|
+
case "management":
|
|
26
|
+
return { scopes: [], agents: [], via: `management:${scope.principalId}` };
|
|
25
27
|
default:
|
|
26
28
|
return fail(403, "This credential acts on no Tenant resource");
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
export function meRoutes(api) {
|
|
30
|
-
tenantRoute(api, { credentials: ["application", "subject", "token"], scopes: "any" }, {
|
|
32
|
+
tenantRoute(api, { credentials: ["application", "subject", "management", "token"], scopes: "any" }, {
|
|
31
33
|
method: "get",
|
|
32
34
|
path: "/v1/me",
|
|
33
35
|
tags: ["Access"],
|
|
34
36
|
summary: "Get who the caller is",
|
|
35
37
|
description: "The subject, scopes, agents and sandbox grants of the credential that sends it: an " +
|
|
36
|
-
"application key (alone or acting for a subject),
|
|
38
|
+
"application key (alone or acting for a subject), a management key " +
|
|
39
|
+
"(`via: management:<principalId>`, no scopes, no agents), or a trusted issuer's token " +
|
|
37
40
|
"(`via: issuer:<name>`).",
|
|
38
41
|
responses: {
|
|
39
42
|
200: {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Read-only sub-resources and opt-in pages. The execution routes retain their contracts. */
|
|
2
|
+
import type { OpenAPIHono } from "@hono/zod-openapi";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import type { TenantEnv } from "../app.js";
|
|
5
|
+
export declare const pageQuery: z.ZodObject<{
|
|
6
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
7
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export declare const sessionPageQuery: z.ZodObject<{
|
|
10
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
11
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
12
|
+
agentId: z.ZodOptional<z.ZodString>;
|
|
13
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
14
|
+
cancelled: "cancelled";
|
|
15
|
+
completed: "completed";
|
|
16
|
+
failed: "failed";
|
|
17
|
+
idle: "idle";
|
|
18
|
+
paused: "paused";
|
|
19
|
+
runnable: "runnable";
|
|
20
|
+
running: "running";
|
|
21
|
+
uncertain: "uncertain";
|
|
22
|
+
waiting: "waiting";
|
|
23
|
+
}>>;
|
|
24
|
+
sandboxId: z.ZodOptional<z.ZodString>;
|
|
25
|
+
ownerUserId: z.ZodOptional<z.ZodString>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
export declare function parseQuery<T>(schema: z.ZodType<T>, query: unknown): T;
|
|
28
|
+
export declare function sessionPage(ctx: TenantEnv["Bindings"]["tenant"], scope: TenantEnv["Variables"]["scope"], query: Record<string, string>): Promise<{
|
|
29
|
+
sessions: {
|
|
30
|
+
id: string;
|
|
31
|
+
agentId: string;
|
|
32
|
+
ownerUserId: string;
|
|
33
|
+
status: "cancelled" | "completed" | "failed" | "idle" | "paused" | "runnable" | "running" | "uncertain" | "waiting";
|
|
34
|
+
activeTurnId: string | null;
|
|
35
|
+
lastTurnId: string | null;
|
|
36
|
+
sandboxId: string | null;
|
|
37
|
+
createdAt: string | null;
|
|
38
|
+
lastEventAt: string | null;
|
|
39
|
+
}[];
|
|
40
|
+
nextCursor: string | null;
|
|
41
|
+
}>;
|
|
42
|
+
export declare function sandboxPage(ctx: TenantEnv["Bindings"]["tenant"], scope: TenantEnv["Variables"]["scope"], query: Record<string, string>, labels: Record<string, string>): Promise<{
|
|
43
|
+
sandboxes: {
|
|
44
|
+
id: string;
|
|
45
|
+
kind: "pod" | "virtual";
|
|
46
|
+
labels: Record<string, string>;
|
|
47
|
+
spec: Record<string, unknown>;
|
|
48
|
+
state: "creating" | "ready" | "running" | "stopped";
|
|
49
|
+
pod?: {
|
|
50
|
+
desired: "deleted" | "running" | "suspended";
|
|
51
|
+
observed: "creating" | "deleting" | "expired" | "failed" | "lost" | "running" | "suspended";
|
|
52
|
+
volumeGeneration: number;
|
|
53
|
+
hostEpoch: number;
|
|
54
|
+
expiresAt?: string | undefined;
|
|
55
|
+
reason?: string | undefined;
|
|
56
|
+
} | undefined;
|
|
57
|
+
sessions: {
|
|
58
|
+
id: string;
|
|
59
|
+
activeTurnId: string | null;
|
|
60
|
+
}[];
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
}[];
|
|
64
|
+
nextCursor: string | null;
|
|
65
|
+
}>;
|
|
66
|
+
export declare function readRoutes(api: OpenAPIHono<TenantEnv>): void;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SESSION_STATUSES } from "@nylorun/core/contracts";
|
|
3
|
+
import { SessionManifestView, SessionUsageTotals, ModelCallsPage, ModelCallExportPage, } from "../../components.js";
|
|
4
|
+
import { readAccess, readStoreOf } from "../../../reads/access.js";
|
|
5
|
+
import { requireApplication, requirePrincipal } from "../../../tenant/auth.js";
|
|
6
|
+
import { fail } from "../../../tenant/http.js";
|
|
7
|
+
import { sandboxGrantsOf } from "../../../tenant/sandboxes.js";
|
|
8
|
+
import { tenantRoute } from "../define.js";
|
|
9
|
+
import { jsonResponse } from "../respond.js";
|
|
10
|
+
export const pageQuery = z.object({
|
|
11
|
+
limit: z.coerce.number().int().min(1).max(200).optional(),
|
|
12
|
+
cursor: z.string().optional(),
|
|
13
|
+
});
|
|
14
|
+
export const sessionPageQuery = pageQuery
|
|
15
|
+
.extend({
|
|
16
|
+
agentId: z.string().optional(),
|
|
17
|
+
status: z.enum(SESSION_STATUSES).optional(),
|
|
18
|
+
sandboxId: z.string().optional(),
|
|
19
|
+
ownerUserId: z.string().optional(),
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
export function parseQuery(schema, query) {
|
|
23
|
+
const result = schema.safeParse(query);
|
|
24
|
+
return result.success ? result.data : fail(400, "Invalid read query", { code: "query_invalid" });
|
|
25
|
+
}
|
|
26
|
+
export async function sessionPage(ctx, scope, query) {
|
|
27
|
+
const { limit, cursor, ...filters } = parseQuery(sessionPageQuery, query);
|
|
28
|
+
if (limit === undefined)
|
|
29
|
+
return fail(400, "Pagination requires limit");
|
|
30
|
+
if (filters.ownerUserId !== undefined)
|
|
31
|
+
requireApplication(scope);
|
|
32
|
+
return readStoreOf(ctx).sessions(filters, { limit, cursor }, readAccess(scope));
|
|
33
|
+
}
|
|
34
|
+
export async function sandboxPage(ctx, scope, query, labels) {
|
|
35
|
+
const { limit, cursor } = parseQuery(pageQuery.extend({ label: z.string().optional() }).strict(), query);
|
|
36
|
+
if (limit === undefined)
|
|
37
|
+
return fail(400, "Pagination requires limit");
|
|
38
|
+
return readStoreOf(ctx).sandboxes(labels, { limit, cursor }, readAccess(scope), sandboxGrantsOf(scope));
|
|
39
|
+
}
|
|
40
|
+
export function readRoutes(api) {
|
|
41
|
+
const sessionId = z.object({ sessionId: z.string() });
|
|
42
|
+
const turnQuery = z.object({ turnId: z.string().optional() }).strict();
|
|
43
|
+
tenantRoute(api, { credentials: ["application", "subject"], scopes: ["agents:read", "agents:write"] }, {
|
|
44
|
+
method: "get",
|
|
45
|
+
path: "/v1/sessions/{sessionId}/manifest",
|
|
46
|
+
tags: ["Sessions"],
|
|
47
|
+
summary: "Read the session's pinned manifest",
|
|
48
|
+
request: { params: sessionId },
|
|
49
|
+
responses: {
|
|
50
|
+
200: {
|
|
51
|
+
description: "The pinned public manifest",
|
|
52
|
+
content: { "application/json": { schema: SessionManifestView } },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}, async (c) => {
|
|
56
|
+
requirePrincipal(c.get("scope"));
|
|
57
|
+
return jsonResponse(200, await readStoreOf(c.env.tenant).manifest(c.req.param("sessionId"), readAccess(c.get("scope"))));
|
|
58
|
+
});
|
|
59
|
+
for (const kind of ["usage", "calls/model"]) {
|
|
60
|
+
const query = kind === "usage"
|
|
61
|
+
? turnQuery
|
|
62
|
+
: turnQuery
|
|
63
|
+
.extend({
|
|
64
|
+
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
65
|
+
cursor: z.string().optional(),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
// Spend is the application's to show: no subject scope reaches it (`tenant:settings`,
|
|
69
|
+
// which used to, is retired in protocol 8).
|
|
70
|
+
tenantRoute(api, { credentials: ["application"], scopes: "never" }, {
|
|
71
|
+
method: "get",
|
|
72
|
+
path: `/v1/sessions/{sessionId}/${kind}`,
|
|
73
|
+
tags: ["Sessions"],
|
|
74
|
+
summary: kind === "usage"
|
|
75
|
+
? "Read a session's recorded model usage"
|
|
76
|
+
: "Page a session's model calls",
|
|
77
|
+
request: { params: sessionId, query },
|
|
78
|
+
responses: {
|
|
79
|
+
200: {
|
|
80
|
+
description: "Recorded usage, including duplicate billed calls",
|
|
81
|
+
content: {
|
|
82
|
+
"application/json": {
|
|
83
|
+
schema: kind === "usage" ? SessionUsageTotals : ModelCallsPage,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
}, async (c) => {
|
|
89
|
+
requirePrincipal(c.get("scope"));
|
|
90
|
+
const store = readStoreOf(c.env.tenant), access = readAccess(c.get("scope")), id = c.req.param("sessionId");
|
|
91
|
+
if (kind === "usage") {
|
|
92
|
+
const q = parseQuery(turnQuery, c.req.query());
|
|
93
|
+
return jsonResponse(200, await store.usage(id, q.turnId, access));
|
|
94
|
+
}
|
|
95
|
+
const q = parseQuery(turnQuery
|
|
96
|
+
.extend({
|
|
97
|
+
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
98
|
+
cursor: z.string().optional(),
|
|
99
|
+
})
|
|
100
|
+
.strict(), c.req.query());
|
|
101
|
+
return jsonResponse(200, await store.modelCalls(id, q.turnId, q, access));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const exportQuery = z
|
|
105
|
+
.object({
|
|
106
|
+
after: z.string().optional(),
|
|
107
|
+
limit: z.coerce.number().int().min(1).max(1000).default(200),
|
|
108
|
+
})
|
|
109
|
+
.strict();
|
|
110
|
+
// The whole Tenant's ledger is the Management API's (protocol 8): a management key, as itself.
|
|
111
|
+
tenantRoute(api, { credentials: ["management"], scopes: "never" }, {
|
|
112
|
+
method: "get",
|
|
113
|
+
path: "/v1/tenant/calls/model",
|
|
114
|
+
tags: ["Tenant"],
|
|
115
|
+
summary: "Export committed model calls in safe transaction order",
|
|
116
|
+
request: { query: exportQuery },
|
|
117
|
+
responses: {
|
|
118
|
+
200: {
|
|
119
|
+
description: "An unfiltered resumable ledger page",
|
|
120
|
+
content: { "application/json": { schema: ModelCallExportPage } },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}, async (c) => {
|
|
124
|
+
const q = parseQuery(exportQuery, c.req.query());
|
|
125
|
+
return jsonResponse(200, await readStoreOf(c.env.tenant).exportModel(q.after, q.limit));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { isSandboxId, PutSandboxRequestSchema } from "@nylorun/core/contracts";
|
|
3
|
-
import { DeleteSandboxResponse, ListSandboxEventsResponse, ListSandboxesResponse, PutSandboxRequest, SandboxView, } from "../../components.js";
|
|
3
|
+
import { DeleteSandboxResponse, ListSandboxEventsResponse, ListSandboxesResponse, SandboxPage, PutSandboxRequest, SandboxView, } from "../../components.js";
|
|
4
4
|
import { fail } from "../../../tenant/http.js";
|
|
5
5
|
import { deleteSandbox, getSandbox, listSandboxes, putSandbox, resetSandbox, sandboxEventsOf, stopSandbox, } from "../../../tenant/sandboxes.js";
|
|
6
6
|
import { readJson } from "../body.js";
|
|
7
7
|
import { tenantRoute } from "../define.js";
|
|
8
|
+
import { pageQuery, sandboxPage } from "./reads.js";
|
|
8
9
|
import { jsonResponse } from "../respond.js";
|
|
9
10
|
/** Reading a sandbox: any caller that may use sessions, or change sandboxes. */
|
|
10
11
|
const READ = {
|
|
@@ -50,15 +51,17 @@ export function sandboxRoutes(api) {
|
|
|
50
51
|
summary: "List sandboxes",
|
|
51
52
|
description: "Every sandbox with all the labels asked for. A token caller sees only the sandboxes its grants reach.",
|
|
52
53
|
request: {
|
|
53
|
-
query:
|
|
54
|
+
query: pageQuery.extend({
|
|
54
55
|
label: z
|
|
55
56
|
.union([z.string(), z.array(z.string())])
|
|
56
57
|
.optional()
|
|
57
58
|
.meta({ description: "`key=value`; repeat it to require several labels" }),
|
|
58
59
|
}),
|
|
59
60
|
},
|
|
60
|
-
responses: { 200: json(ListSandboxesResponse, "The sandboxes") },
|
|
61
|
-
}, async (c) => jsonResponse(200,
|
|
61
|
+
responses: { 200: json(z.union([ListSandboxesResponse, SandboxPage]), "The sandboxes; limit opts into pagination") },
|
|
62
|
+
}, async (c) => jsonResponse(200, c.req.query("limit") !== undefined
|
|
63
|
+
? await sandboxPage(c.env.tenant, c.get("scope"), c.req.query(), labelsOf(c.req.queries("label")))
|
|
64
|
+
: await listSandboxes(c.env.tenant, labelsOf(c.req.queries("label")), c.get("scope"))));
|
|
62
65
|
tenantRoute(api, WRITE, {
|
|
63
66
|
method: "put",
|
|
64
67
|
path: "/v1/sandboxes/{sandboxId}",
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { PutAgentRequestSchema, PutSessionRequestSchema } from "@nylorun/core/contracts";
|
|
3
|
+
import { PutAgentRequestSchema, PutSessionRequestSchema, SessionCommandSchema, } from "@nylorun/core/contracts";
|
|
4
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";
|
|
5
|
+
import { AcceptedResponse, ListAgentsResponse, ListPublicAgentsResponse, ListSessionsResponse, SessionPage, HistoryPage, SessionEvent, PutAgentRequest, PutAgentResponse, PutSessionRequest, SandboxToolOutcome, SessionCommand, SessionItemsResponse, SessionView, StreamClosedFrame, } from "../../components.js";
|
|
7
6
|
import { accessOf, requireApplication } from "../../../tenant/auth.js";
|
|
7
|
+
import { command } from "../../../tenant/commands.js";
|
|
8
8
|
import { loadSession, sessionOf } from "../../../tenant/context.js";
|
|
9
9
|
import { fail } from "../../../tenant/http.js";
|
|
10
|
-
import { sandboxGrantsOf } from "../../../tenant/sandboxes.js";
|
|
10
|
+
import { sandboxGrantsOf, sandboxRouteDeps } from "../../../tenant/sandboxes.js";
|
|
11
11
|
import { readHistory, requestCursor, streamHolderOf, streamSessionEvents } from "../../../tenant/session-streams.js";
|
|
12
12
|
import { listAgentsPublic, listDefinitions, listSessions, putDefinition, putSession, sessionView, } from "../../../tenant/sessions.js";
|
|
13
13
|
import { readJson } from "../body.js";
|
|
14
14
|
import { tenantRoute } from "../define.js";
|
|
15
|
+
import { pageQuery, sessionPage, sessionPageQuery, parseQuery } from "./reads.js";
|
|
15
16
|
import { jsonResponse } from "../respond.js";
|
|
16
17
|
const OWN_SESSIONS = {
|
|
17
18
|
credentials: ["application", "subject", "token"],
|
|
@@ -45,6 +46,18 @@ async function sessionBelow(ctx, incoming, id, access) {
|
|
|
45
46
|
return requestCursor(incoming, urlOf(incoming));
|
|
46
47
|
}
|
|
47
48
|
export function sessionRoutes(api) {
|
|
49
|
+
tenantRoute(api, OWN_SESSIONS, {
|
|
50
|
+
method: "post",
|
|
51
|
+
path: "/v1/sessions/{sessionId}/commands",
|
|
52
|
+
tags: ["Sessions"],
|
|
53
|
+
summary: "Send a command to a session",
|
|
54
|
+
description: "A message starts a turn; the others answer what the session waits on, or cancel. Idempotent on the command's `idempotencyKey`.",
|
|
55
|
+
request: { params: sessionId, body: body(SessionCommand) },
|
|
56
|
+
responses: {
|
|
57
|
+
200: json(AcceptedResponse, "The command, accepted"),
|
|
58
|
+
409: { description: "A turn is running, or the session does not wait on this" },
|
|
59
|
+
},
|
|
60
|
+
}, async (c) => jsonResponse(200, await command(c.env.tenant, c.req.param("sessionId"), SessionCommandSchema.parse(await readJson(c.req.raw)), c.get("scope"))));
|
|
48
61
|
tenantRoute(api, {
|
|
49
62
|
credentials: ["application", "subject", "token"],
|
|
50
63
|
scopes: ["agents:read", "agents:write"],
|
|
@@ -80,10 +93,12 @@ export function sessionRoutes(api) {
|
|
|
80
93
|
tags: ["Sessions"],
|
|
81
94
|
summary: "List sessions",
|
|
82
95
|
description: "Acting for a person, only theirs.",
|
|
83
|
-
request: { query:
|
|
84
|
-
responses: { 200: json(ListSessionsResponse, "The sessions") },
|
|
96
|
+
request: { query: sessionPageQuery },
|
|
97
|
+
responses: { 200: json(z.union([ListSessionsResponse, SessionPage]), "The sessions; limit opts into pagination") },
|
|
85
98
|
}, async (c) => {
|
|
86
99
|
const scope = c.get("scope");
|
|
100
|
+
if (c.req.query("limit") !== undefined)
|
|
101
|
+
return jsonResponse(200, await sessionPage(c.env.tenant, scope, c.req.query()));
|
|
87
102
|
return jsonResponse(200, await listSessions(c.env.tenant, c.req.query("agentId") ?? null, accessOf(scope)));
|
|
88
103
|
});
|
|
89
104
|
tenantRoute(api, OWN_SESSIONS, {
|
|
@@ -129,19 +144,21 @@ export function sessionRoutes(api) {
|
|
|
129
144
|
request: {
|
|
130
145
|
params: sessionId,
|
|
131
146
|
query: resume.extend({
|
|
147
|
+
limit: pageQuery.shape.limit,
|
|
132
148
|
agent: z
|
|
133
149
|
.string()
|
|
134
150
|
.optional()
|
|
135
151
|
.meta({ description: "Only the events of this agent (the root agent, or one it uses as a tool)" }),
|
|
136
152
|
}),
|
|
137
153
|
},
|
|
138
|
-
responses: { 200: json(SessionItemsResponse, "Events up to now
|
|
154
|
+
responses: { 200: json(z.union([SessionItemsResponse, HistoryPage]), "Events up to now; limit returns a bounded page and tail") },
|
|
139
155
|
}, async (c) => {
|
|
140
156
|
const scope = c.get("scope");
|
|
141
157
|
const ctx = c.env.tenant;
|
|
142
158
|
const id = c.req.param("sessionId");
|
|
143
159
|
const cursor = await sessionBelow(ctx, c.env.incoming, id, accessOf(scope));
|
|
144
|
-
|
|
160
|
+
const { limit } = parseQuery(pageQuery.pick({ limit: true }), { limit: c.req.query("limit") });
|
|
161
|
+
return jsonResponse(200, await readHistory(ctx, id, cursor, c.req.query("agent"), limit));
|
|
145
162
|
});
|
|
146
163
|
tenantRoute(api, OWN_SESSIONS, {
|
|
147
164
|
method: "get",
|
|
@@ -16,11 +16,8 @@ import { readJson } from "../body.js";
|
|
|
16
16
|
import { pathSegments, tenantRoute } from "../define.js";
|
|
17
17
|
import { jsonResponse } from "../respond.js";
|
|
18
18
|
import { rearmPodTimers } from "../../../tenant/sandboxes.js";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
scopes,
|
|
22
|
-
});
|
|
23
|
-
const SETTINGS = settings(["tenant:settings"]);
|
|
19
|
+
/** Every Management API route (`/v1/tenant/*`): a management key, acting as itself (protocol 8). */
|
|
20
|
+
const MANAGEMENT = { credentials: ["management"], scopes: "never" };
|
|
24
21
|
const json = (schema, description) => ({
|
|
25
22
|
description,
|
|
26
23
|
content: { "application/json": { schema } },
|
|
@@ -79,7 +76,7 @@ async function clusterOf(ctx) {
|
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
export function tenantRoutes(api) {
|
|
82
|
-
tenantRoute(api,
|
|
79
|
+
tenantRoute(api, MANAGEMENT, {
|
|
83
80
|
method: "get",
|
|
84
81
|
path: "/v1/tenant",
|
|
85
82
|
tags: ["Tenant"],
|
|
@@ -103,7 +100,7 @@ export function tenantRoutes(api) {
|
|
|
103
100
|
harness: harnessStatusOf(ctx),
|
|
104
101
|
}));
|
|
105
102
|
});
|
|
106
|
-
tenantRoute(api,
|
|
103
|
+
tenantRoute(api, MANAGEMENT, {
|
|
107
104
|
method: "post",
|
|
108
105
|
path: "/v1/tenant/reset",
|
|
109
106
|
tags: ["Tenant"],
|
|
@@ -134,7 +131,7 @@ export function tenantRoutes(api) {
|
|
|
134
131
|
ctx.closing = false;
|
|
135
132
|
return jsonResponse(200, { ok: true });
|
|
136
133
|
});
|
|
137
|
-
tenantRoute(api,
|
|
134
|
+
tenantRoute(api, MANAGEMENT, {
|
|
138
135
|
method: "put",
|
|
139
136
|
path: "/v1/tenant/config/seed",
|
|
140
137
|
tags: ["Tenant"],
|
|
@@ -147,7 +144,7 @@ export function tenantRoutes(api) {
|
|
|
147
144
|
const request = SeedTenantConfigRequestSchema.parse(await readJson(c.req.raw));
|
|
148
145
|
return jsonResponse(200, await seedTenantConfig({ store: ctx.store, vault: ctx.vault, keys: ctx.keys }, request));
|
|
149
146
|
});
|
|
150
|
-
tenantRoute(api,
|
|
147
|
+
tenantRoute(api, MANAGEMENT, {
|
|
151
148
|
method: "get",
|
|
152
149
|
path: "/v1/tenant/models",
|
|
153
150
|
tags: ["Tenant"],
|
|
@@ -157,14 +154,14 @@ export function tenantRoutes(api) {
|
|
|
157
154
|
c.env.tenant;
|
|
158
155
|
return jsonResponse(200, hostModelCatalog());
|
|
159
156
|
});
|
|
160
|
-
tenantRoute(api,
|
|
157
|
+
tenantRoute(api, MANAGEMENT, {
|
|
161
158
|
method: "get",
|
|
162
159
|
path: "/v1/tenant/sandbox",
|
|
163
160
|
tags: ["Tenant"],
|
|
164
161
|
summary: "Get the Tenant's sandbox configuration",
|
|
165
162
|
responses: { 200: json(TenantSandboxView, "The backend in use and the configuration in force") },
|
|
166
163
|
}, async (c) => jsonResponse(200, await sandboxView(c.env.tenant)));
|
|
167
|
-
tenantRoute(api,
|
|
164
|
+
tenantRoute(api, MANAGEMENT, {
|
|
168
165
|
method: "put",
|
|
169
166
|
path: "/v1/tenant/sandbox",
|
|
170
167
|
tags: ["Tenant"],
|
|
@@ -183,7 +180,7 @@ export function tenantRoutes(api) {
|
|
|
183
180
|
await rearmPodTimers(ctx);
|
|
184
181
|
return jsonResponse(200, await sandboxView(ctx));
|
|
185
182
|
});
|
|
186
|
-
tenantRoute(api,
|
|
183
|
+
tenantRoute(api, MANAGEMENT, {
|
|
187
184
|
method: "get",
|
|
188
185
|
path: "/v1/tenant/usage",
|
|
189
186
|
tags: ["Tenant"],
|
|
@@ -212,7 +209,7 @@ export function tenantRoutes(api) {
|
|
|
212
209
|
...totals,
|
|
213
210
|
});
|
|
214
211
|
});
|
|
215
|
-
tenantRoute(api,
|
|
212
|
+
tenantRoute(api, MANAGEMENT, {
|
|
216
213
|
method: "get",
|
|
217
214
|
path: "/v1/tenant/budgets",
|
|
218
215
|
tags: ["Tenant"],
|
|
@@ -222,7 +219,7 @@ export function tenantRoutes(api) {
|
|
|
222
219
|
const rows = await c.env.tenant.store.tx((t) => t.listModelBudgets());
|
|
223
220
|
return jsonResponse(200, { budgets: rows.map(budgetView) });
|
|
224
221
|
});
|
|
225
|
-
tenantRoute(api,
|
|
222
|
+
tenantRoute(api, MANAGEMENT, {
|
|
226
223
|
method: "put",
|
|
227
224
|
path: "/v1/tenant/budgets",
|
|
228
225
|
tags: ["Tenant"],
|
|
@@ -239,21 +236,21 @@ export function tenantRoutes(api) {
|
|
|
239
236
|
});
|
|
240
237
|
return jsonResponse(200, { budgets: rows.map(budgetView) });
|
|
241
238
|
});
|
|
242
|
-
tenantRoute(api,
|
|
239
|
+
tenantRoute(api, MANAGEMENT, {
|
|
243
240
|
method: "get",
|
|
244
241
|
path: "/v1/tenant/providers",
|
|
245
242
|
tags: ["Tenant"],
|
|
246
243
|
summary: "List the model providers the Tenant has credentials for",
|
|
247
244
|
responses: { 200: json(ListProvidersResponse, "The providers") },
|
|
248
245
|
}, async (c) => jsonResponse(200, await (c.env.tenant).vault.listHostProviders()));
|
|
249
|
-
tenantRoute(api,
|
|
246
|
+
tenantRoute(api, MANAGEMENT, {
|
|
250
247
|
method: "get",
|
|
251
248
|
path: "/v1/tenant/model",
|
|
252
249
|
tags: ["Tenant"],
|
|
253
250
|
summary: "Get the Tenant's model",
|
|
254
251
|
responses: { 200: json(HostModelView, "The model its sessions call") },
|
|
255
252
|
}, async (c) => jsonResponse(200, await (c.env.tenant).vault.getHostModel()));
|
|
256
|
-
tenantRoute(api,
|
|
253
|
+
tenantRoute(api, MANAGEMENT, {
|
|
257
254
|
method: "put",
|
|
258
255
|
path: "/v1/tenant/model",
|
|
259
256
|
tags: ["Tenant"],
|
|
@@ -264,7 +261,7 @@ export function tenantRoutes(api) {
|
|
|
264
261
|
const ctx = c.env.tenant;
|
|
265
262
|
return jsonResponse(200, await ctx.keys.putHostModel(PutHostModelRequestSchema.parse(await readJson(c.req.raw))));
|
|
266
263
|
});
|
|
267
|
-
tenantRoute(api,
|
|
264
|
+
tenantRoute(api, MANAGEMENT, {
|
|
268
265
|
method: "put",
|
|
269
266
|
path: "/v1/tenant/model/selection",
|
|
270
267
|
tags: ["Tenant"],
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vaults and their credentials (`/v1/vaults
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* installation's own vaults (`scope: "installation"`) attach to any session; a vault
|
|
6
|
-
* person (`ownerUserId`) attaches only to that person's sessions. A person's own
|
|
7
|
-
* come from the operator's credential resolver (`vault/sources.ts`), not from these
|
|
2
|
+
* Vaults and their credentials (`/v1/tenant/vaults/**`, the Management API, protocol 8): secrets
|
|
3
|
+
* a session's tools use. Only a management key (or Studio's key, acting as itself) reaches these
|
|
4
|
+
* routes; an application key is `403 key_role_mismatch`, and nothing acts for a subject here. The
|
|
5
|
+
* installation's own vaults (`scope: "installation"`) attach to any session (`vaultIds`); a vault
|
|
6
|
+
* of one person (`ownerUserId`) attaches only to that person's sessions. A person's own
|
|
7
|
+
* credentials come from the operator's credential resolver (`vault/sources.ts`), not from these
|
|
8
|
+
* routes.
|
|
8
9
|
*
|
|
9
|
-
* MCP OAuth connect
|
|
10
|
-
*
|
|
11
|
-
* `GET /v1/oauth/callback`. Both only route: the gateway's keys module does the OAuth
|
|
10
|
+
* MCP OAuth connect: a management key starts one into an installation vault (`/oauth/start`);
|
|
11
|
+
* the authorization server sends the browser back to the anonymous, unversioned
|
|
12
|
+
* `GET /v1/oauth/callback`. Both only route: the gateway's keys module does the OAuth.
|
|
12
13
|
*/
|
|
13
14
|
import type { OpenAPIHono } from "@hono/zod-openapi";
|
|
14
15
|
import type { TenantEnv } from "../app.js";
|