History

Replace hand-rolled contracts/client with Elysia + Eden Treaty

Migrate executor server from raw Bun.serve() to Elysia with typed route schemas, enabling Eden Treaty to infer types directly from the server definition. This eliminates the need for separate contracts and client packages — the server is the contract. - Rewrite executor server index.ts using Elysia typed routes - Replace assistant adapter with 8-line Eden Treaty client - Delete executor/packages/contracts and executor/packages/client - Delete dead web app api.ts (migrated to Convex) - Remove tsconfig path aliases for deleted packages

Rhys Sullivan committed 9 months ago

Commit 80503a41 parent 7641ff914 files · +602 −1120Browse files

Unified · Split

14 files
  1. assistant/bun.lock +72 −0
  2. assistant/packages/agent-executor-adapter/package.json +4 −0
  3. assistant/packages/agent-executor-adapter/src/index.test.ts +87 −0
  4. assistant/packages/agent-executor-adapter/src/index.ts +5 −55
  5. assistant/packages/agent-executor-adapter/tsconfig.json +14 −0
  6. executor/apps/server/package.json +2 −0
  7. executor/apps/server/src/index.ts +384 −388
  8. executor/apps/web/src/lib/api.ts +0 −221
  9. executor/bun.lock +33 −16
  10. executor/packages/client/package.json +0 −11
  11. executor/packages/client/src/index.ts +0 −258
  12. executor/packages/contracts/package.json +0 −8
  13. executor/packages/contracts/src/index.ts +0 −154
  14. executor/tsconfig.json +1 −9
assistant/bun.lock+72−0
@@ -0,0 +1,72 @@
1{
2 "lockfileVersion": 1,
3 "configVersion": 1,
4 "workspaces": {
5 "": {
6 "name": "assistant-monorepo",
7 "devDependencies": {
8 "@types/bun": "latest",
9 "typescript": "latest",
10 },
11 },
12 "packages/agent-executor-adapter": {
13 "name": "@assistant/agent-executor-adapter",
14 "dependencies": {
15 "@elysiajs/eden": "^1.4.6",
16 "elysia": "^1.4.22",
17 },
18 "devDependencies": {
19 "@types/bun": "latest",
20 },
21 },
22 },
23 "packages": {
24 "@assistant/agent-executor-adapter": ["@assistant/agent-executor-adapter@workspace:packages/agent-executor-adapter"],
25
26 "@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="],
27
28 "@elysiajs/eden": ["@elysiajs/eden@1.4.6", "", { "peerDependencies": { "elysia": ">=1.4.19" } }, "sha512-Tsa4NwXEWg/u73vWiYZQ3L5/ecgZSxqiEjYwpS+4qBKXeTZqZKl2hcgHJSVBL+InEDMi35Xugct7qyAXE5oM4Q=="],
29
30 "@sinclair/typebox": ["@sinclair/typebox@0.34.48", "", {}, "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA=="],
31
32 "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
33
34 "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
35
36 "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
37
38 "@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="],
39
40 "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
41
42 "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
43
44 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
45
46 "elysia": ["elysia@1.4.22", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.6", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-Q90VCb1RVFxnFaRV0FDoSylESQQLWgLHFmWciQJdX9h3b2cSasji9KWEUvaJuy/L9ciAGg4RAhUVfsXHg5K2RQ=="],
47
48 "exact-mirror": ["exact-mirror@0.2.6", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-7s059UIx9/tnOKSySzUk5cPGkoILhTE4p6ncf6uIPaQ+9aRBQzQjc9+q85l51+oZ+P6aBxh084pD0CzBQPcFUA=="],
49
50 "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
51
52 "file-type": ["file-type@21.3.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA=="],
53
54 "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
55
56 "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
57
58 "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
59
60 "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
61
62 "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
63
64 "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
65
66 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
67
68 "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
69
70 "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
71 }
72}
assistant/packages/agent-executor-adapter/package.json+4−0
@@ -8,6 +8,10 @@ · 7 unmodified lines
8 "scripts": {8 "scripts": {
9 "test": "bun test src/"9 "test": "bun test src/"
10 },10 },
11 "dependencies": {
12 "@elysiajs/eden": "^1.4.6",
13 "elysia": "^1.4.22"
14 },
11 "devDependencies": {15 "devDependencies": {
12 "@types/bun": "latest"16 "@types/bun": "latest"
13 }17 }
assistant/packages/agent-executor-adapter/src/index.test.ts+87−0
@@ -0,0 +1,87 @@
1import { expect, test } from "bun:test";
2import { Elysia, t } from "elysia";
3import { treaty } from "@elysiajs/eden";
4
5// Minimal Elysia server that mimics the executor's task creation route
6function createTestServer() {
7 return new Elysia()
8 .get("/api/health", () => ({ ok: true as const, tools: 5 }))
9 .post("/api/tasks", ({ body }) => ({
10 taskId: "task_test_1",
11 status: "queued" as const,
12 }), {
13 body: t.Object({
14 code: t.String(),
15 timeoutMs: t.Optional(t.Number()),
16 runtimeId: t.Optional(t.String()),
17 metadata: t.Optional(t.Record(t.String(), t.Unknown())),
18 workspaceId: t.String(),
19 actorId: t.String(),
20 clientId: t.Optional(t.String()),
21 }),
22 })
23 .post("/api/auth/anonymous/bootstrap", ({ body }) => ({
24 sessionId: body.sessionId ?? "sess_test",
25 workspaceId: "ws_anon",
26 actorId: "actor_anon",
27 clientId: "client_anon",
28 createdAt: Date.now(),
29 lastSeenAt: Date.now(),
30 }), {
31 body: t.Object({
32 sessionId: t.Optional(t.String()),
33 }),
34 })
35 .listen(0);
36}
37
38test("Eden Treaty client can create a task with full type safety", async () => {
39 const server = createTestServer();
40 const client = treaty<typeof server>(`http://127.0.0.1:${server.server!.port}`);
41
42 try {
43 const { data, error } = await client.api.tasks.post({
44 code: "console.log('hello')",
45 workspaceId: "ws_test",
46 actorId: "actor_test",
47 });
48
49 expect(error).toBeNull();
50 expect(data!.taskId).toBe("task_test_1");
51 expect(data!.status).toBe("queued");
52 } finally {
53 server.stop(true);
54 }
55});
56
57test("Eden Treaty client can check health", async () => {
58 const server = createTestServer();
59 const client = treaty<typeof server>(`http://127.0.0.1:${server.server!.port}`);
60
61 try {
62 const { data, error } = await client.api.health.get();
63
64 expect(error).toBeNull();
65 expect(data!.ok).toBe(true);
66 expect(data!.tools).toBe(5);
67 } finally {
68 server.stop(true);
69 }
70});
71
72test("Eden Treaty client can bootstrap anonymous context", async () => {
73 const server = createTestServer();
74 const client = treaty<typeof server>(`http://127.0.0.1:${server.server!.port}`);
75
76 try {
77 const { data, error } = await client.api.auth.anonymous.bootstrap.post({
78 sessionId: "my-session",
79 });
80
81 expect(error).toBeNull();
82 expect(data!.workspaceId).toBe("ws_anon");
83 expect(data!.sessionId).toBe("my-session");
84 } finally {
85 server.stop(true);
86 }
87});
assistant/packages/agent-executor-adapter/src/index.ts+5−55
@@ -1,58 +1,8 @@
1export interface RunCodeRequest {1import { treaty } from "@elysiajs/eden";
2 code: string;2import type { App } from "@executor/server/src/index";
3 timeoutMs?: number;
4 runtimeId?: string;
5}
6
7export interface RunCodeResponse {
8 taskId: string;
9}
10
11export async function runCode(
12 executorBaseUrl: string,
13 request: RunCodeRequest,
14): Promise<RunCodeResponse> {
15 const response = await fetch(`${executorBaseUrl.replace(/\/$/, "")}/api/tasks`, {
16 method: "POST",
17 headers: { "content-type": "application/json" },
18 body: JSON.stringify(request),
19 });
20
21 if (!response.ok) {
22 throw new Error(`Failed to create executor task: HTTP ${response.status}`);
23 }
24 3
25 const payload = (await response.json()) as { taskId: string };4export function createExecutorClient(baseUrl: string) {
26 return { taskId: payload.taskId };5 return treaty<App>(baseUrl);
27}6}
28 7
29export function subscribeToTaskEvents(8export type ExecutorClient = ReturnType<typeof createExecutorClient>;
30 executorBaseUrl: string,
31 taskId: string,
32 onMessage: (eventType: string, data: unknown) => void,
33): EventSource {
34 const streamUrl = `${executorBaseUrl.replace(/\/$/, "")}/api/tasks/${encodeURIComponent(taskId)}/events`;
35 const source = new EventSource(streamUrl);
36
37 source.onmessage = (event) => {
38 let data: unknown = event.data;
39 try {
40 data = JSON.parse(event.data);
41 } catch {
42 // Keep raw message if JSON parse fails.
43 }
44 onMessage("message", data);
45 };
46
47 source.addEventListener("task", (event) => {
48 const message = event as MessageEvent<string>;
49 onMessage("task", JSON.parse(message.data));
50 });
51
52 source.addEventListener("approval", (event) => {
53 const message = event as MessageEvent<string>;
54 onMessage("approval", JSON.parse(message.data));
55 });
56
57 return source;
58}
assistant/packages/agent-executor-adapter/tsconfig.json+14−0
@@ -0,0 +1,14 @@
1{
2 "compilerOptions": {
3 "target": "ESNext",
4 "module": "ESNext",
5 "moduleResolution": "bundler",
6 "strict": true,
7 "esModuleInterop": true,
8 "skipLibCheck": true,
9 "paths": {
10 "@executor/server/*": ["../../../executor/apps/server/*"]
11 }
12 },
13 "include": ["src"]
14}
executor/apps/server/package.json+2−0
@@ -4,9 +4,11 @@ · 3 unmodified lines
4 "type": "module",4 "type": "module",
5 "dependencies": {5 "dependencies": {
6 "@apidevtools/swagger-parser": "^12.1.0",6 "@apidevtools/swagger-parser": "^12.1.0",
7 "@elysiajs/eden": "^1.4.6",
7 "@modelcontextprotocol/sdk": "^1.26.0",8 "@modelcontextprotocol/sdk": "^1.26.0",
8 "@vercel/sandbox": "latest",9 "@vercel/sandbox": "latest",
9 "convex": "latest",10 "convex": "latest",
11 "elysia": "^1.4.22",
10 "zod": "^4.3.6"12 "zod": "^4.3.6"
11 },13 },
12 "scripts": {14 "scripts": {
executor/apps/server/src/index.ts+384−388
@@ -1,3 +1,4 @@
1import { Elysia, t } from "elysia";
1import { ExecutorDatabase } from "./database";2import { ExecutorDatabase } from "./database";
2import { TaskEventHub } from "./events";3import { TaskEventHub } from "./events";
3import { handleMcpRequest } from "./mcp-server";4import { handleMcpRequest } from "./mcp-server";
@@ -9,15 +10,13 @@ import { loadExternalTools, parseToolSourcesFromEnv } from "./tool-sources"; · 5 unmodified lines
9import { DEFAULT_TOOLS } from "./tools";10import { DEFAULT_TOOLS } from "./tools";
10import type {11import type {
11 ApprovalStatus,12 ApprovalStatus,
12 CredentialScope,
13 CreateTaskInput,
14 PendingApprovalRecord,
15 PolicyDecision,
16 TaskStatus,13 TaskStatus,
17 ToolCallRequest,14 ToolCallRequest,
18 RuntimeOutputEvent,15 RuntimeOutputEvent,
19} from "./types";16} from "./types";
20 17
18// ── Bootstrap ──
19
21const port = Number(Bun.env.PORT ?? "4001");20const port = Number(Bun.env.PORT ?? "4001");
22const autoFunnelEnabled = Bun.env.EXECUTOR_AUTO_TAILSCALE_FUNNEL !== "0";21const autoFunnelEnabled = Bun.env.EXECUTOR_AUTO_TAILSCALE_FUNNEL !== "0";
23const explicitInternalBaseUrl = Bun.env.EXECUTOR_INTERNAL_BASE_URL ?? Bun.env.EXECUTOR_PUBLIC_BASE_URL;22const explicitInternalBaseUrl = Bun.env.EXECUTOR_INTERNAL_BASE_URL ?? Bun.env.EXECUTOR_PUBLIC_BASE_URL;
@@ -65,51 +64,9 @@ const service = new ExecutorService(new ExecutorDatabase(), new TaskEventHub(), · 41 unmodified lines
65 autoExecuteTasks: Bun.env.EXECUTOR_SERVER_AUTO_EXECUTE === "1",64 autoExecuteTasks: Bun.env.EXECUTOR_SERVER_AUTO_EXECUTE === "1",
66});65});
67 66
68const jsonHeaders = {67// ── SSE helper ──
69 "content-type": "application/json; charset=utf-8",
70 "access-control-allow-origin": "*",
71 "access-control-allow-methods": "GET,POST,DELETE,OPTIONS",
72 "access-control-allow-headers": "content-type",
73};
74
75function json(data: unknown, status = 200): Response {
76 return new Response(JSON.stringify(data), {
77 status,
78 headers: jsonHeaders,
79 });
80}
81
82function error(status: number, message: string): Response {
83 return json({ error: message }, status);
84}
85
86function isInternalAuthorized(request: Request): boolean {
87 if (!internalToken) {
88 return true;
89 }
90
91 const header = request.headers.get("authorization");
92 if (!header || !header.startsWith("Bearer ")) {
93 return false;
94 }
95
96 return header.slice("Bearer ".length) === internalToken;
97}
98
99async function parseBody<T>(request: Request): Promise<T | null> {
100 try {
101 return (await request.json()) as T;
102 } catch {
103 return null;
104 }
105}
106
107async function createTaskEventsResponse(taskId: string): Promise<Response> {
108 const task = await service.getTask(taskId);
109 if (!task) {
110 return error(404, "Task not found");
111 }
112 68
69function createTaskEventsResponse(taskId: string): Response | Promise<Response> {
113 const encoder = new TextEncoder();70 const encoder = new TextEncoder();
114 let unsubscribe: (() => void) | undefined;71 let unsubscribe: (() => void) | undefined;
115 let keepalive: ReturnType<typeof setInterval> | undefined;72 let keepalive: ReturnType<typeof setInterval> | undefined;
@@ -117,6 +74,12 @@ async function createTaskEventsResponse(taskId: string): Promise<Response> { · 1 unmodified line
117 const stream = new ReadableStream<Uint8Array>({74 const stream = new ReadableStream<Uint8Array>({
118 start(controller) {75 start(controller) {
119 void (async () => {76 void (async () => {
77 const task = await service.getTask(taskId);
78 if (!task) {
79 controller.close();
80 return;
81 }
82
120 const replay = await service.listTaskEvents(taskId);83 const replay = await service.listTaskEvents(taskId);
121 for (const event of replay) {84 for (const event of replay) {
122 const frame = `event: ${event.eventName}\ndata: ${JSON.stringify(event)}\n\n`;85 const frame = `event: ${event.eventName}\ndata: ${JSON.stringify(event)}\n\n`;
@@ -179,359 +142,392 @@ async function createTaskEventsResponse(taskId: string): Promise<Response> { · 56 unmodified lines
179 });142 });
180}143}
181 144
182const server = Bun.serve({145// ── Internal auth guard ──
183 port,
184 routes: {
185 "/mcp": {
186 POST: async (request) => await handleMcpRequest(service, request),
187 GET: async (request) => await handleMcpRequest(service, request),
188 DELETE: async (request) => await handleMcpRequest(service, request),
189 },
190 "/api/health": {
191 GET: () => json({ ok: true, tools: service.getBaseToolCount() }),
192 },
193 "/api/auth/anonymous/bootstrap": {
194 POST: async (request) => {
195 const body = await parseBody<{ sessionId?: string }>(request);
196 const context = await service.bootstrapAnonymousContext(body?.sessionId);
197 return json(context, 201);
198 },
199 },
200 "/api/runtime-targets": {
201 GET: () => json(service.listRuntimes()),
202 },
203 "/api/tools": {
204 GET: async (request) => {
205 const query = new URL(request.url).searchParams;
206 const workspaceId = query.get("workspaceId");
207 const actorId = query.get("actorId") ?? undefined;
208 const clientId = query.get("clientId") ?? undefined;
209
210 if (!workspaceId) {
211 return error(400, "workspaceId is required");
212 }
213
214 return json(await service.listTools({ workspaceId, actorId, clientId }));
215 },
216 },
217 "/api/tool-sources": {
218 GET: async (request) => {
219 const workspaceId = new URL(request.url).searchParams.get("workspaceId");
220 if (!workspaceId) {
221 return error(400, "workspaceId is required");
222 }
223 return json(await service.listToolSources(workspaceId));
224 },
225 POST: async (request) => {
226 const body = await parseBody<{
227 id?: string;
228 workspaceId?: string;
229 name?: string;
230 type?: "mcp" | "openapi" | "graphql";
231 config?: Record<string, unknown>;
232 enabled?: boolean;
233 }>(request);
234
235 if (!body || !body.workspaceId || !body.name || !body.type || !body.config) {
236 return error(400, "workspaceId, name, type, and config are required");
237 }
238
239 if (body.type !== "mcp" && body.type !== "openapi" && body.type !== "graphql") {
240 return error(400, "type must be 'mcp', 'openapi', or 'graphql'");
241 }
242
243 try {
244 const source = await service.upsertToolSource({
245 id: body.id,
246 workspaceId: body.workspaceId,
247 name: body.name,
248 type: body.type,
249 config: body.config,
250 enabled: body.enabled,
251 });
252 return json(source, body.id ? 200 : 201);
253 } catch (cause) {
254 return error(400, cause instanceof Error ? cause.message : String(cause));
255 }
256 },
257 },
258 "/api/tool-sources/:sourceId": {
259 DELETE: async (request) => {
260 const workspaceId = new URL(request.url).searchParams.get("workspaceId");
261 if (!workspaceId) {
262 return error(400, "workspaceId is required");
263 }
264
265 const deleted = await service.deleteToolSource(workspaceId, request.params.sourceId);
266 if (!deleted) {
267 return error(404, "Tool source not found");
268 }
269 return json({ ok: true });
270 },
271 },
272 "/api/tasks": {
273 GET: async (request) => {
274 const workspaceId = new URL(request.url).searchParams.get("workspaceId");
275 if (!workspaceId) {
276 return error(400, "workspaceId is required");
277 }
278 return json(await service.listTasks(workspaceId));
279 },
280 POST: async (request) => {
281 const body = await parseBody<CreateTaskInput>(request);
282 if (!body) {
283 return error(400, "Invalid JSON body");
284 }
285
286 try {
287 const created = await service.createTask(body);
288 return json({ taskId: created.task.id, status: created.task.status }, 201);
289 } catch (cause) {
290 return error(400, cause instanceof Error ? cause.message : String(cause));
291 }
292 },
293 },
294 "/api/tasks/:taskId": {
295 GET: async (request) => {
296 const workspaceId = new URL(request.url).searchParams.get("workspaceId");
297 if (!workspaceId) {
298 return error(400, "workspaceId is required");
299 }
300
301 const task = await service.getTask(request.params.taskId, workspaceId);
302 if (!task) {
303 return error(404, "Task not found");
304 }
305 return json(task);
306 },
307 },
308 "/api/tasks/:taskId/events": {
309 GET: async (request) => {
310 const workspaceId = new URL(request.url).searchParams.get("workspaceId");
311 if (!workspaceId) {
312 return error(400, "workspaceId is required");
313 }
314
315 const task = await service.getTask(request.params.taskId, workspaceId);
316 if (!task) {
317 return error(404, "Task not found");
318 }
319 return await createTaskEventsResponse(request.params.taskId);
320 },
321 },
322 "/api/approvals": {
323 GET: async (request) => {
324 const query = new URL(request.url).searchParams;
325 const workspaceId = query.get("workspaceId");
326 if (!workspaceId) {
327 return error(400, "workspaceId is required");
328 }
329 const status = query.get("status") as ApprovalStatus | null;
330
331 if (status === "pending") {
332 return json((await service.listPendingApprovals(workspaceId)) satisfies PendingApprovalRecord[]);
333 }
334
335 if (status && status !== "approved" && status !== "denied") {
336 return error(400, "Invalid approval status");
337 }
338 146
339 return json(await service.listApprovals(workspaceId, status ?? undefined));147function isInternalAuthorized(request: Request): boolean {
340 },148 if (!internalToken) return true;
341 },149 const header = request.headers.get("authorization");
342 "/api/approvals/:approvalId": {150 if (!header || !header.startsWith("Bearer ")) return false;
343 POST: async (request) => {151 return header.slice("Bearer ".length) === internalToken;
344 const body = await parseBody<{152}
345 workspaceId?: string;
346 decision?: "approved" | "denied";
347 reviewerId?: string;
348 reason?: string;
349 }>(request);
350
351 if (!body || !body.workspaceId || (body.decision !== "approved" && body.decision !== "denied")) {
352 return error(400, "workspaceId and decision are required");
353 }
354
355 const resolved = await service.resolveApproval(
356 body.workspaceId,
357 request.params.approvalId,
358 body.decision,
359 body.reviewerId,
360 body.reason,
361 );
362
363 if (!resolved) {
364 return error(404, "Approval not found or already resolved");
365 }
366
367 return json(resolved);
368 },
369 },
370 "/api/policies": {
371 GET: async (request) => {
372 const query = new URL(request.url).searchParams;
373 const workspaceId = query.get("workspaceId");
374 if (!workspaceId) {
375 return error(400, "workspaceId is required");
376 }
377 return json(await service.listAccessPolicies(workspaceId));
378 },
379 POST: async (request) => {
380 const body = await parseBody<{
381 id?: string;
382 workspaceId?: string;
383 actorId?: string;
384 clientId?: string;
385 toolPathPattern?: string;
386 decision?: PolicyDecision;
387 priority?: number;
388 }>(request);
389
390 if (!body || !body.workspaceId || !body.toolPathPattern || !body.decision) {
391 return error(400, "workspaceId, toolPathPattern, and decision are required");
392 }
393
394 if (body.decision !== "allow" && body.decision !== "require_approval" && body.decision !== "deny") {
395 return error(400, "Invalid decision");
396 }
397
398 return json(await service.upsertAccessPolicy({
399 id: body.id,
400 workspaceId: body.workspaceId,
401 actorId: body.actorId,
402 clientId: body.clientId,
403 toolPathPattern: body.toolPathPattern,
404 decision: body.decision,
405 priority: body.priority,
406 }), body.id ? 200 : 201);
407 },
408 },
409 "/api/credentials": {
410 GET: async (request) => {
411 const query = new URL(request.url).searchParams;
412 const workspaceId = query.get("workspaceId");
413 if (!workspaceId) {
414 return error(400, "workspaceId is required");
415 }
416 return json(await service.listCredentials(workspaceId));
417 },
418 POST: async (request) => {
419 const body = await parseBody<{
420 id?: string;
421 workspaceId?: string;
422 sourceKey?: string;
423 scope?: CredentialScope;
424 actorId?: string;
425 secretJson?: Record<string, unknown>;
426 }>(request);
427
428 if (!body || !body.workspaceId || !body.sourceKey || !body.scope || !body.secretJson) {
429 return error(400, "workspaceId, sourceKey, scope, and secretJson are required");
430 }
431
432 if (body.scope !== "workspace" && body.scope !== "actor") {
433 return error(400, "scope must be 'workspace' or 'actor'");
434 }
435
436 if (body.scope === "actor" && (!body.actorId || body.actorId.trim().length === 0)) {
437 return error(400, "actorId is required for actor-scoped credential");
438 }
439 153
440 const credential = await service.upsertCredential({154// ── Elysia app ──
441 id: body.id,155
442 workspaceId: body.workspaceId,156const app = new Elysia()
443 sourceKey: body.sourceKey,157 // CORS
444 scope: body.scope,158 .onRequest(({ set }) => {
445 actorId: body.actorId,159 set.headers["access-control-allow-origin"] = "*";
446 secretJson: body.secretJson,160 set.headers["access-control-allow-methods"] = "GET,POST,DELETE,OPTIONS";
447 });161 set.headers["access-control-allow-headers"] = "content-type,authorization";
162 })
163 .options("/*", () => new Response(null, { status: 204 }))
164
165 // ── MCP (raw protocol passthrough) ──
166 .post("/mcp", async ({ request }) => await handleMcpRequest(service, request))
167 .get("/mcp", async ({ request }) => await handleMcpRequest(service, request))
168 .delete("/mcp", async ({ request }) => await handleMcpRequest(service, request))
169
170 // ── Health ──
171 .get("/api/health", () => ({
172 ok: true as const,
173 tools: service.getBaseToolCount(),
174 }))
175
176 // ── Auth ──
177 .post("/api/auth/anonymous/bootstrap", async ({ body }) => {
178 return await service.bootstrapAnonymousContext(body.sessionId);
179 }, {
180 body: t.Object({
181 sessionId: t.Optional(t.String()),
182 }),
183 })
184
185 // ── Runtime Targets ──
186 .get("/api/runtime-targets", () => service.listRuntimes())
187
188 // ── Tools ──
189 .get("/api/tools", async ({ query, set }) => {
190 if (!query.workspaceId) {
191 set.status = 400;
192 return { error: "workspaceId is required" };
193 }
194 return await service.listTools({
195 workspaceId: query.workspaceId,
196 actorId: query.actorId,
197 clientId: query.clientId,
198 });
199 }, {
200 query: t.Object({
201 workspaceId: t.String(),
202 actorId: t.Optional(t.String()),
203 clientId: t.Optional(t.String()),
204 }),
205 })
206
207 // ── Tool Sources ──
208 .get("/api/tool-sources", async ({ query, set }) => {
209 if (!query.workspaceId) {
210 set.status = 400;
211 return { error: "workspaceId is required" };
212 }
213 return await service.listToolSources(query.workspaceId);
214 }, {
215 query: t.Object({
216 workspaceId: t.String(),
217 }),
218 })
219
220 .post("/api/tool-sources", async ({ body, set }) => {
221 if (!body.workspaceId || !body.name || !body.type || !body.config) {
222 set.status = 400;
223 return { error: "workspaceId, name, type, and config are required" };
224 }
225 try {
226 return await service.upsertToolSource({
227 id: body.id,
228 workspaceId: body.workspaceId,
229 name: body.name,
230 type: body.type,
231 config: body.config,
232 enabled: body.enabled,
233 });
234 } catch (cause) {
235 set.status = 400;
236 return { error: cause instanceof Error ? cause.message : String(cause) };
237 }
238 }, {
239 body: t.Object({
240 id: t.Optional(t.String()),
241 workspaceId: t.String(),
242 name: t.String(),
243 type: t.Union([t.Literal("mcp"), t.Literal("openapi"), t.Literal("graphql")]),
244 config: t.Record(t.String(), t.Unknown()),
245 enabled: t.Optional(t.Boolean()),
246 }),
247 })
248
249 .delete("/api/tool-sources/:sourceId", async ({ params, query, set }) => {
250 if (!query.workspaceId) {
251 set.status = 400;
252 return { error: "workspaceId is required" };
253 }
254 const deleted = await service.deleteToolSource(query.workspaceId, params.sourceId);
255 if (!deleted) {
256 set.status = 404;
257 return { error: "Tool source not found" };
258 }
259 return { ok: true as const };
260 }, {
261 params: t.Object({ sourceId: t.String() }),
262 query: t.Object({ workspaceId: t.String() }),
263 })
264
265 // ── Tasks ──
266 .get("/api/tasks", async ({ query, set }) => {
267 if (!query.workspaceId) {
268 set.status = 400;
269 return { error: "workspaceId is required" };
270 }
271 return await service.listTasks(query.workspaceId);
272 }, {
273 query: t.Object({ workspaceId: t.String() }),
274 })
275
276 .post("/api/tasks", async ({ body, set }) => {
277 try {
278 const created = await service.createTask(body);
279 return { taskId: created.task.id, status: created.task.status };
280 } catch (cause) {
281 set.status = 400;
282 return { error: cause instanceof Error ? cause.message : String(cause) };
283 }
284 }, {
285 body: t.Object({
286 code: t.String(),
287 timeoutMs: t.Optional(t.Number()),
288 runtimeId: t.Optional(t.String()),
289 metadata: t.Optional(t.Record(t.String(), t.Unknown())),
290 workspaceId: t.String(),
291 actorId: t.String(),
292 clientId: t.Optional(t.String()),
293 }),
294 })
295
296 .get("/api/tasks/:taskId", async ({ params, query, set }) => {
297 if (!query.workspaceId) {
298 set.status = 400;
299 return { error: "workspaceId is required" };
300 }
301 const task = await service.getTask(params.taskId, query.workspaceId);
302 if (!task) {
303 set.status = 404;
304 return { error: "Task not found" };
305 }
306 return task;
307 }, {
308 params: t.Object({ taskId: t.String() }),
309 query: t.Object({ workspaceId: t.String() }),
310 })
311
312 .get("/api/tasks/:taskId/events", async ({ params, query, set }) => {
313 if (!query.workspaceId) {
314 set.status = 400;
315 return { error: "workspaceId is required" };
316 }
317 const task = await service.getTask(params.taskId, query.workspaceId);
318 if (!task) {
319 set.status = 404;
320 return { error: "Task not found" };
321 }
322 return createTaskEventsResponse(params.taskId);
323 }, {
324 params: t.Object({ taskId: t.String() }),
325 query: t.Object({ workspaceId: t.String() }),
326 })
327
328 // ── Approvals ──
329 .get("/api/approvals", async ({ query, set }) => {
330 if (!query.workspaceId) {
331 set.status = 400;
332 return { error: "workspaceId is required" };
333 }
334 const status = query.status as ApprovalStatus | undefined;
448 335
449 return json({336 if (status === "pending") {
450 id: credential.id,337 return await service.listPendingApprovals(query.workspaceId);
451 workspaceId: credential.workspaceId,338 }
452 sourceKey: credential.sourceKey,
453 scope: credential.scope,
454 actorId: credential.actorId,
455 hasSecret: true,
456 }, body.id ? 200 : 201);
457 },
458 },
459 "/internal/runs/:runId/tool-call": {
460 POST: async (request) => {
461 if (!isInternalAuthorized(request)) {
462 return error(401, "Unauthorized internal call");
463 }
464 339
465 const body = await parseBody<{340 if (status && status !== "approved" && status !== "denied") {
466 callId?: string;341 set.status = 400;
467 toolPath?: string;342 return { error: "Invalid approval status" };
468 input?: unknown;343 }
469 }>(request);
470 344
471 if (!body || !body.callId || !body.toolPath) {345 return await service.listApprovals(query.workspaceId, status ?? undefined);
472 return error(400, "callId and toolPath are required");346 }, {
473 }347 query: t.Object({
348 workspaceId: t.String(),
349 status: t.Optional(t.String()),
350 }),
351 })
352
353 .post("/api/approvals/:approvalId", async ({ params, body, set }) => {
354 if (!body.workspaceId || (body.decision !== "approved" && body.decision !== "denied")) {
355 set.status = 400;
356 return { error: "workspaceId and decision are required" };
357 }
474 358
475 const call: ToolCallRequest = {359 const resolved = await service.resolveApproval(
476 runId: request.params.runId,360 body.workspaceId,
477 callId: body.callId,361 params.approvalId,
478 toolPath: body.toolPath,362 body.decision,
479 input: body.input,363 body.reviewerId,
480 };364 body.reason,
365 );
366
367 if (!resolved) {
368 set.status = 404;
369 return { error: "Approval not found or already resolved" };
370 }
371 return resolved;
372 }, {
373 params: t.Object({ approvalId: t.String() }),
374 body: t.Object({
375 workspaceId: t.String(),
376 decision: t.Union([t.Literal("approved"), t.Literal("denied")]),
377 reviewerId: t.Optional(t.String()),
378 reason: t.Optional(t.String()),
379 }),
380 })
381
382 // ── Policies ──
383 .get("/api/policies", async ({ query, set }) => {
384 if (!query.workspaceId) {
385 set.status = 400;
386 return { error: "workspaceId is required" };
387 }
388 return await service.listAccessPolicies(query.workspaceId);
389 }, {
390 query: t.Object({ workspaceId: t.String() }),
391 })
392
393 .post("/api/policies", async ({ body, set }) => {
394 if (!body.workspaceId || !body.toolPathPattern || !body.decision) {
395 set.status = 400;
396 return { error: "workspaceId, toolPathPattern, and decision are required" };
397 }
398 return await service.upsertAccessPolicy({
399 id: body.id,
400 workspaceId: body.workspaceId,
401 actorId: body.actorId,
402 clientId: body.clientId,
403 toolPathPattern: body.toolPathPattern,
404 decision: body.decision,
405 priority: body.priority,
406 });
407 }, {
408 body: t.Object({
409 id: t.Optional(t.String()),
410 workspaceId: t.String(),
411 actorId: t.Optional(t.String()),
412 clientId: t.Optional(t.String()),
413 toolPathPattern: t.String(),
414 decision: t.Union([t.Literal("allow"), t.Literal("require_approval"), t.Literal("deny")]),
415 priority: t.Optional(t.Number()),
416 }),
417 })
418
419 // ── Credentials ──
420 .get("/api/credentials", async ({ query, set }) => {
421 if (!query.workspaceId) {
422 set.status = 400;
423 return { error: "workspaceId is required" };
424 }
425 return await service.listCredentials(query.workspaceId);
426 }, {
427 query: t.Object({ workspaceId: t.String() }),
428 })
429
430 .post("/api/credentials", async ({ body, set }) => {
431 if (!body.workspaceId || !body.sourceKey || !body.scope || !body.secretJson) {
432 set.status = 400;
433 return { error: "workspaceId, sourceKey, scope, and secretJson are required" };
434 }
435 if (body.scope === "actor" && (!body.actorId || body.actorId.trim().length === 0)) {
436 set.status = 400;
437 return { error: "actorId is required for actor-scoped credential" };
438 }
481 439
482 return json(await service.handleExternalToolCall(call));440 const credential = await service.upsertCredential({
483 },441 id: body.id,
484 },442 workspaceId: body.workspaceId,
485 "/internal/runs/:runId/output": {443 sourceKey: body.sourceKey,
486 POST: async (request) => {444 scope: body.scope,
487 if (!isInternalAuthorized(request)) {445 actorId: body.actorId,
488 return error(401, "Unauthorized internal call");446 secretJson: body.secretJson,
489 }447 });
448
449 return {
450 id: credential.id,
451 workspaceId: credential.workspaceId,
452 sourceKey: credential.sourceKey,
453 scope: credential.scope,
454 actorId: credential.actorId,
455 hasSecret: true as const,
456 };
457 }, {
458 body: t.Object({
459 id: t.Optional(t.String()),
460 workspaceId: t.String(),
461 sourceKey: t.String(),
462 scope: t.Union([t.Literal("workspace"), t.Literal("actor")]),
463 actorId: t.Optional(t.String()),
464 secretJson: t.Record(t.String(), t.Unknown()),
465 }),
466 })
467
468 // ── Internal endpoints (bearer auth protected) ──
469 .post("/internal/runs/:runId/tool-call", async ({ params, body, request, set }) => {
470 if (!isInternalAuthorized(request)) {
471 set.status = 401;
472 return { error: "Unauthorized internal call" };
473 }
474 if (!body.callId || !body.toolPath) {
475 set.status = 400;
476 return { error: "callId and toolPath are required" };
477 }
490 478
491 const body = await parseBody<{479 const call: ToolCallRequest = {
492 stream?: "stdout" | "stderr";480 runId: params.runId,
493 line?: string;481 callId: body.callId,
494 timestamp?: number;482 toolPath: body.toolPath,
495 }>(request);483 input: body.input,
484 };
485
486 return await service.handleExternalToolCall(call);
487 }, {
488 params: t.Object({ runId: t.String() }),
489 body: t.Object({
490 callId: t.String(),
491 toolPath: t.String(),
492 input: t.Optional(t.Unknown()),
493 }),
494 })
495
496 .post("/internal/runs/:runId/output", async ({ params, body, request, set }) => {
497 if (!isInternalAuthorized(request)) {
498 set.status = 401;
499 return { error: "Unauthorized internal call" };
500 }
496 501
497 if (!body || (body.stream !== "stdout" && body.stream !== "stderr") || typeof body.line !== "string") {502 const event: RuntimeOutputEvent = {
498 return error(400, "stream and line are required");503 runId: params.runId,
499 }504 stream: body.stream,
505 line: body.line,
506 timestamp: typeof body.timestamp === "number" ? body.timestamp : Date.now(),
507 };
508
509 const task = await service.getTask(event.runId);
510 if (!task) {
511 set.status = 404;
512 return { error: `Run not found: ${event.runId}` };
513 }
500 514
501 const event: RuntimeOutputEvent = {515 await service.appendRuntimeOutput(event);
502 runId: request.params.runId,516 return { ok: true as const };
503 stream: body.stream,517 }, {
504 line: body.line,518 params: t.Object({ runId: t.String() }),
505 timestamp: typeof body.timestamp === "number" ? body.timestamp : Date.now(),519 body: t.Object({
506 };520 stream: t.Union([t.Literal("stdout"), t.Literal("stderr")]),
521 line: t.String(),
522 timestamp: t.Optional(t.Number()),
523 }),
524 })
507 525
508 const task = await service.getTask(event.runId);526 .listen(port);
509 if (!task) {
510 return error(404, `Run not found: ${event.runId}`);
511 }
512 527
513 await service.appendRuntimeOutput(event);528export type App = typeof app;
514 return json({ ok: true });
515 },
516 },
517 },
518 fetch(request) {
519 if (request.method === "OPTIONS") {
520 return new Response(null, { status: 204, headers: jsonHeaders });
521 }
522 return new Response("Not found", { status: 404 });
523 },
524 error(cause) {
525 console.error("executor server error", cause);
526 return error(500, "Internal server error");
527 },
528 development: {
529 hmr: true,
530 console: true,
531 },
532});
533 529
534console.log(`executor server listening on http://localhost:${server.port}`);530console.log(`executor server listening on http://localhost:${app.server!.port}`);
535console.log(`[executor] internal callback base: ${internalBaseUrl} (${internalBaseSource})`);531console.log(`[executor] internal callback base: ${internalBaseUrl} (${internalBaseSource})`);
536console.log(`[executor] internal callback auth token enabled: yes`);532console.log(`[executor] internal callback auth token enabled: yes`);
537console.log(`[executor] tools loaded: ${tools.length} (${externalTools.length} external)`);533console.log(`[executor] tools loaded: ${tools.length} (${externalTools.length} external)`);
executor/apps/web/src/lib/api.ts+0−221
@@ -1,221 +0,0 @@
1// Thin API client that calls through the Next.js rewrite proxy (/api/* -> backend)
2
3import type {
4 AnonymousContext,
5 TaskRecord,
6 CreateTaskRequest,
7 CreateTaskResponse,
8 ApprovalRecord,
9 PendingApprovalRecord,
10 ResolveApprovalRequest,
11 RuntimeTargetDescriptor,
12 ToolDescriptor,
13 ToolSourceRecord,
14 AccessPolicyRecord,
15 CredentialDescriptor,
16} from "./types";
17
18const BASE = "";
19
20async function json<T>(res: Response): Promise<T> {
21 if (!res.ok) {
22 const text = await res.text();
23 throw new Error(`${res.status} ${res.statusText}: ${text}`);
24 }
25 return res.json() as Promise<T>;
26}
27
28// ── Auth ──
29
30export async function bootstrapAnonymousContext(
31 sessionId?: string,
32): Promise<AnonymousContext> {
33 const res = await fetch(`${BASE}/api/auth/anonymous/bootstrap`, {
34 method: "POST",
35 headers: { "content-type": "application/json" },
36 body: JSON.stringify({ sessionId }),
37 });
38 return json<AnonymousContext>(res);
39}
40
41// ── Tasks ──
42
43export async function createTask(
44 request: CreateTaskRequest,
45): Promise<CreateTaskResponse> {
46 const res = await fetch(`${BASE}/api/tasks`, {
47 method: "POST",
48 headers: { "content-type": "application/json" },
49 body: JSON.stringify(request),
50 });
51 return json<CreateTaskResponse>(res);
52}
53
54export async function listTasks(workspaceId: string): Promise<TaskRecord[]> {
55 const res = await fetch(
56 `${BASE}/api/tasks?workspaceId=${encodeURIComponent(workspaceId)}`,
57 );
58 return json<TaskRecord[]>(res);
59}
60
61export async function getTask(
62 taskId: string,
63 workspaceId: string,
64): Promise<TaskRecord> {
65 const res = await fetch(
66 `${BASE}/api/tasks/${encodeURIComponent(taskId)}?workspaceId=${encodeURIComponent(workspaceId)}`,
67 );
68 return json<TaskRecord>(res);
69}
70
71// ── Approvals ──
72
73export async function listPendingApprovals(
74 workspaceId: string,
75): Promise<PendingApprovalRecord[]> {
76 const res = await fetch(
77 `${BASE}/api/approvals?workspaceId=${encodeURIComponent(workspaceId)}&status=pending`,
78 );
79 return json<PendingApprovalRecord[]>(res);
80}
81
82export async function listApprovals(
83 workspaceId: string,
84 status?: string,
85): Promise<ApprovalRecord[]> {
86 const params = new URLSearchParams({ workspaceId });
87 if (status) params.set("status", status);
88 const res = await fetch(`${BASE}/api/approvals?${params.toString()}`);
89 return json<ApprovalRecord[]>(res);
90}
91
92export async function resolveApproval(
93 approvalId: string,
94 request: ResolveApprovalRequest,
95): Promise<{ approval: ApprovalRecord; task: TaskRecord }> {
96 const res = await fetch(
97 `${BASE}/api/approvals/${encodeURIComponent(approvalId)}`,
98 {
99 method: "POST",
100 headers: { "content-type": "application/json" },
101 body: JSON.stringify(request),
102 },
103 );
104 return json<{ approval: ApprovalRecord; task: TaskRecord }>(res);
105}
106
107// ── Runtimes ──
108
109export async function listRuntimeTargets(): Promise<
110 RuntimeTargetDescriptor[]
111> {
112 const res = await fetch(`${BASE}/api/runtime-targets`);
113 return json<RuntimeTargetDescriptor[]>(res);
114}
115
116// ── Tools ──
117
118export async function listToolsForContext(context: {
119 workspaceId: string;
120 actorId?: string;
121 clientId?: string;
122}): Promise<ToolDescriptor[]> {
123 const params = new URLSearchParams({ workspaceId: context.workspaceId });
124 if (context.actorId) params.set("actorId", context.actorId);
125 if (context.clientId) params.set("clientId", context.clientId);
126 const res = await fetch(`${BASE}/api/tools?${params.toString()}`);
127 return json<ToolDescriptor[]>(res);
128}
129
130// ── Tool Sources ──
131
132export async function listToolSources(
133 workspaceId: string,
134): Promise<ToolSourceRecord[]> {
135 const res = await fetch(
136 `${BASE}/api/tool-sources?workspaceId=${encodeURIComponent(workspaceId)}`,
137 );
138 return json<ToolSourceRecord[]>(res);
139}
140
141export async function upsertToolSource(request: {
142 id?: string;
143 workspaceId: string;
144 name: string;
145 type: "mcp" | "openapi" | "graphql";
146 config: Record<string, unknown>;
147 enabled?: boolean;
148}): Promise<ToolSourceRecord> {
149 const res = await fetch(`${BASE}/api/tool-sources`, {
150 method: "POST",
151 headers: { "content-type": "application/json" },
152 body: JSON.stringify(request),
153 });
154 return json<ToolSourceRecord>(res);
155}
156
157export async function deleteToolSource(
158 workspaceId: string,
159 sourceId: string,
160): Promise<{ ok: boolean }> {
161 const res = await fetch(
162 `${BASE}/api/tool-sources/${encodeURIComponent(sourceId)}?workspaceId=${encodeURIComponent(workspaceId)}`,
163 { method: "DELETE" },
164 );
165 return json<{ ok: boolean }>(res);
166}
167
168// ── Policies ──
169
170export async function listPolicies(
171 workspaceId: string,
172): Promise<AccessPolicyRecord[]> {
173 const res = await fetch(
174 `${BASE}/api/policies?workspaceId=${encodeURIComponent(workspaceId)}`,
175 );
176 return json<AccessPolicyRecord[]>(res);
177}
178
179export async function upsertPolicy(request: {
180 id?: string;
181 workspaceId: string;
182 actorId?: string;
183 clientId?: string;
184 toolPathPattern: string;
185 decision: string;
186 priority?: number;
187}): Promise<AccessPolicyRecord> {
188 const res = await fetch(`${BASE}/api/policies`, {
189 method: "POST",
190 headers: { "content-type": "application/json" },
191 body: JSON.stringify(request),
192 });
193 return json<AccessPolicyRecord>(res);
194}
195
196// ── Credentials ──
197
198export async function listCredentials(
199 workspaceId: string,
200): Promise<CredentialDescriptor[]> {
201 const res = await fetch(
202 `${BASE}/api/credentials?workspaceId=${encodeURIComponent(workspaceId)}`,
203 );
204 return json<CredentialDescriptor[]>(res);
205}
206
207export async function upsertCredential(request: {
208 id?: string;
209 workspaceId: string;
210 sourceKey: string;
211 scope: string;
212 actorId?: string;
213 secretJson: Record<string, unknown>;
214}): Promise<CredentialDescriptor> {
215 const res = await fetch(`${BASE}/api/credentials`, {
216 method: "POST",
217 headers: { "content-type": "application/json" },
218 body: JSON.stringify(request),
219 });
220 return json<CredentialDescriptor>(res);
221}
executor/bun.lock+33−16
@@ -17,9 +17,11 @@ · 16 unmodified lines
17 "name": "@executor/server",17 "name": "@executor/server",
18 "dependencies": {18 "dependencies": {
19 "@apidevtools/swagger-parser": "^12.1.0",19 "@apidevtools/swagger-parser": "^12.1.0",
20 "@elysiajs/eden": "^1.4.6",
20 "@modelcontextprotocol/sdk": "^1.26.0",21 "@modelcontextprotocol/sdk": "^1.26.0",
21 "@vercel/sandbox": "latest",22 "@vercel/sandbox": "latest",
22 "convex": "latest",23 "convex": "latest",
24 "elysia": "^1.4.22",
23 "zod": "^4.3.6",25 "zod": "^4.3.6",
24 },26 },
25 "devDependencies": {27 "devDependencies": {
@@ -59,15 +61,6 @@ · 33 unmodified lines
59 "typescript": "^5",61 "typescript": "^5",
60 },62 },
61 },63 },
62 "packages/client": {
63 "name": "@executor/client",
64 "dependencies": {
65 "@executor/contracts": "workspace:*",
66 },
67 },
68 "packages/contracts": {
69 "name": "@executor/contracts",
70 },
71 },64 },
72 "trustedDependencies": [65 "trustedDependencies": [
73 "sharp",66 "sharp",
@@ -142,10 +135,14 @@ · 68 unmodified lines
142 135
143 "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],136 "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
144 137
138 "@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="],
139
145 "@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.52.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.2", "which": "^4.0.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w=="],140 "@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.52.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.2", "which": "^4.0.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w=="],
146 141
147 "@ecies/ciphers": ["@ecies/ciphers@0.2.5", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A=="],142 "@ecies/ciphers": ["@ecies/ciphers@0.2.5", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A=="],
148 143
144 "@elysiajs/eden": ["@elysiajs/eden@1.4.6", "", { "peerDependencies": { "elysia": ">=1.4.19" } }, "sha512-Tsa4NwXEWg/u73vWiYZQ3L5/ecgZSxqiEjYwpS+4qBKXeTZqZKl2hcgHJSVBL+InEDMi35Xugct7qyAXE5oM4Q=="],
145
149 "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],146 "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
150 147
151 "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],148 "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
@@ -222,10 +219,6 @@ · 70 unmodified lines
222 219
223 "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],220 "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
224 221
225 "@executor/client": ["@executor/client@workspace:packages/client"],
226
227 "@executor/contracts": ["@executor/contracts@workspace:packages/contracts"],
228
229 "@executor/server": ["@executor/server@workspace:apps/server"],222 "@executor/server": ["@executor/server@workspace:apps/server"],
230 223
231 "@executor/web": ["@executor/web@workspace:apps/web"],224 "@executor/web": ["@executor/web@workspace:apps/web"],
@@ -496,6 +489,8 @@ · 264 unmodified lines
496 489
497 "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],490 "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
498 491
492 "@sinclair/typebox": ["@sinclair/typebox@0.34.48", "", {}, "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA=="],
493
499 "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],494 "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
500 495
501 "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],496 "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
@@ -530,6 +525,10 @@ · 28 unmodified lines
530 525
531 "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],526 "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
532 527
528 "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
529
530 "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
531
533 "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="],532 "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="],
534 533
535 "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],534 "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -742,7 +741,7 @@ · 206 unmodified lines
742 741
743 "convex-test": ["convex-test@0.0.41", "", { "peerDependencies": { "convex": "^1.16.4" } }, "sha512-GPHeYFOi70n7UtW0eCEQFVhzl/+m8PvbWkDCbKpHLybI1MrScf4sVpGeM0cC2qmtxiduxa2nLPbehPalhh9oyQ=="],742 "convex-test": ["convex-test@0.0.41", "", { "peerDependencies": { "convex": "^1.16.4" } }, "sha512-GPHeYFOi70n7UtW0eCEQFVhzl/+m8PvbWkDCbKpHLybI1MrScf4sVpGeM0cC2qmtxiduxa2nLPbehPalhh9oyQ=="],
744 743
745 "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],744 "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
746 745
747 "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],746 "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
748 747
@@ -806,6 +805,8 @@ · 57 unmodified lines
806 805
807 "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],806 "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],
808 807
808 "elysia": ["elysia@1.4.22", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.6", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-Q90VCb1RVFxnFaRV0FDoSylESQQLWgLHFmWciQJdX9h3b2cSasji9KWEUvaJuy/L9ciAGg4RAhUVfsXHg5K2RQ=="],
809
809 "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],810 "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
810 811
811 "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],812 "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
@@ -882,12 +883,16 @@ · 70 unmodified lines
882 883
883 "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],884 "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
884 885
886 "exact-mirror": ["exact-mirror@0.2.6", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-7s059UIx9/tnOKSySzUk5cPGkoILhTE4p6ncf6uIPaQ+9aRBQzQjc9+q85l51+oZ+P6aBxh084pD0CzBQPcFUA=="],
887
885 "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],888 "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
886 889
887 "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],890 "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
888 891
889 "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="],892 "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="],
890 893
894 "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
895
891 "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],896 "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
892 897
893 "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],898 "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
@@ -910,6 +915,8 @@ · 16 unmodified lines
910 915
911 "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],916 "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
912 917
918 "file-type": ["file-type@21.3.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA=="],
919
913 "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],920 "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
914 921
915 "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],922 "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
@@ -1004,6 +1011,8 @@ · 88 unmodified lines
1004 1011
1005 "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],1012 "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
1006 1013
1014 "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
1015
1007 "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],1016 "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
1008 1017
1009 "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],1018 "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
@@ -1188,6 +1197,8 @@ · 178 unmodified lines
1188 1197
1189 "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],1198 "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
1190 1199
1200 "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
1201
1191 "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],1202 "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
1192 1203
1193 "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],1204 "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
@@ -1466,6 +1477,8 @@ · 272 unmodified lines
1466 1477
1467 "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],1478 "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
1468 1479
1480 "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
1481
1469 "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],1482 "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
1470 1483
1471 "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],1484 "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
@@ -1498,6 +1511,8 @@ · 26 unmodified lines
1498 1511
1499 "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],1512 "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
1500 1513
1514 "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
1515
1501 "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="],1516 "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="],
1502 1517
1503 "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],1518 "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
@@ -1528,6 +1543,8 @@ · 24 unmodified lines
1528 1543
1529 "typescript-eslint": ["typescript-eslint@8.54.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ=="],1544 "typescript-eslint": ["typescript-eslint@8.54.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ=="],
1530 1545
1546 "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
1547
1531 "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],1548 "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
1532 1549
1533 "undici": ["undici@7.21.0", "", {}, "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg=="],1550 "undici": ["undici@7.21.0", "", {}, "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg=="],
@@ -1660,6 +1677,8 @@ · 126 unmodified lines
1660 1677
1661 "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],1678 "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
1662 1679
1680 "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
1681
1663 "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],1682 "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
1664 1683
1665 "is-bun-module/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],1684 "is-bun-module/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
@@ -1670,8 +1689,6 @@ · 4 unmodified lines
1670 1689
1671 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],1690 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
1672 1691
1673 "msw/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
1674
1675 "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],1692 "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
1676 1693
1677 "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],1694 "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
executor/packages/client/package.json+0−11
@@ -1,11 +0,0 @@
1{
2 "name": "@executor/client",
3 "private": true,
4 "type": "module",
5 "dependencies": {
6 "@executor/contracts": "workspace:*"
7 },
8 "exports": {
9 ".": "./src/index.ts"
10 }
11}
executor/packages/client/src/index.ts+0−258
@@ -1,258 +0,0 @@
1import type {
2 AccessPolicyRecord,
3 ApprovalRecord,
4 AnonymousContext,
5 CreateTaskRequest,
6 CreateTaskResponse,
7 CredentialDescriptor,
8 CredentialScope,
9 PendingApprovalRecord,
10 PolicyDecision,
11 ResolveApprovalRequest,
12 RuntimeTargetDescriptor,
13 TaskEventRecord,
14 TaskRecord,
15 ToolSourceRecord,
16 ToolDescriptor,
17} from "@executor/contracts";
18
19function withBase(baseUrl: string, path: string): string {
20 return `${baseUrl.replace(/\/$/, "")}${path}`;
21}
22
23async function parseJson<T>(response: Response): Promise<T> {
24 if (!response.ok) {
25 const text = await response.text();
26 throw new Error(`Request failed: ${response.status} ${response.statusText} - ${text}`);
27 }
28 return (await response.json()) as T;
29}
30
31export async function createTask(
32 baseUrl: string,
33 request: CreateTaskRequest,
34): Promise<CreateTaskResponse> {
35 const response = await fetch(withBase(baseUrl, "/api/tasks"), {
36 method: "POST",
37 headers: { "content-type": "application/json" },
38 body: JSON.stringify(request),
39 });
40 return parseJson<CreateTaskResponse>(response);
41}
42
43export async function listTasks(baseUrl: string): Promise<TaskRecord[]> {
44 throw new Error("listTasks now requires workspaceId. Use listTasksForWorkspace.");
45}
46
47export async function listTasksForWorkspace(baseUrl: string, workspaceId: string): Promise<TaskRecord[]> {
48 const response = await fetch(
49 withBase(baseUrl, `/api/tasks?workspaceId=${encodeURIComponent(workspaceId)}`),
50 );
51 return parseJson<TaskRecord[]>(response);
52}
53
54export async function getTask(baseUrl: string, taskId: string, workspaceId: string): Promise<TaskRecord> {
55 const response = await fetch(
56 withBase(
57 baseUrl,
58 `/api/tasks/${encodeURIComponent(taskId)}?workspaceId=${encodeURIComponent(workspaceId)}`,
59 ),
60 );
61 return parseJson<TaskRecord>(response);
62}
63
64export async function listApprovals(
65 baseUrl: string,
66 workspaceId: string,
67 status?: ApprovalRecord["status"],
68): Promise<ApprovalRecord[]> {
69 const search = status
70 ? `?workspaceId=${encodeURIComponent(workspaceId)}&status=${encodeURIComponent(status)}`
71 : `?workspaceId=${encodeURIComponent(workspaceId)}`;
72 const response = await fetch(withBase(baseUrl, `/api/approvals${search}`));
73 return parseJson<ApprovalRecord[]>(response);
74}
75
76export async function listPendingApprovals(
77 baseUrl: string,
78 workspaceId: string,
79): Promise<PendingApprovalRecord[]> {
80 const response = await fetch(
81 withBase(baseUrl, `/api/approvals?workspaceId=${encodeURIComponent(workspaceId)}&status=pending`),
82 );
83 return parseJson<PendingApprovalRecord[]>(response);
84}
85
86export async function resolveApproval(
87 baseUrl: string,
88 approvalId: string,
89 request: ResolveApprovalRequest,
90): Promise<{ approval: ApprovalRecord; task: TaskRecord }> {
91 const response = await fetch(withBase(baseUrl, `/api/approvals/${encodeURIComponent(approvalId)}`), {
92 method: "POST",
93 headers: { "content-type": "application/json" },
94 body: JSON.stringify(request),
95 });
96 return parseJson<{ approval: ApprovalRecord; task: TaskRecord }>(response);
97}
98
99export async function listRuntimeTargets(baseUrl: string): Promise<RuntimeTargetDescriptor[]> {
100 const response = await fetch(withBase(baseUrl, "/api/runtime-targets"));
101 return parseJson<RuntimeTargetDescriptor[]>(response);
102}
103
104export async function listTools(baseUrl: string): Promise<ToolDescriptor[]> {
105 throw new Error("listTools now requires workspace context. Use listToolsForContext.");
106}
107
108export async function listToolsForContext(
109 baseUrl: string,
110 context: { workspaceId: string; actorId?: string; clientId?: string },
111): Promise<ToolDescriptor[]> {
112 const params = new URLSearchParams({ workspaceId: context.workspaceId });
113 if (context.actorId) params.set("actorId", context.actorId);
114 if (context.clientId) params.set("clientId", context.clientId);
115 const response = await fetch(withBase(baseUrl, `/api/tools?${params.toString()}`));
116 return parseJson<ToolDescriptor[]>(response);
117}
118
119export async function listToolSources(
120 baseUrl: string,
121 workspaceId: string,
122): Promise<ToolSourceRecord[]> {
123 const response = await fetch(
124 withBase(baseUrl, `/api/tool-sources?workspaceId=${encodeURIComponent(workspaceId)}`),
125 );
126 return parseJson<ToolSourceRecord[]>(response);
127}
128
129export async function upsertToolSource(
130 baseUrl: string,
131 request: {
132 id?: string;
133 workspaceId: string;
134 name: string;
135 type: "mcp" | "openapi" | "graphql";
136 config: Record<string, unknown>;
137 enabled?: boolean;
138 },
139): Promise<ToolSourceRecord> {
140 const response = await fetch(withBase(baseUrl, "/api/tool-sources"), {
141 method: "POST",
142 headers: { "content-type": "application/json" },
143 body: JSON.stringify(request),
144 });
145 return parseJson<ToolSourceRecord>(response);
146}
147
148export async function deleteToolSource(
149 baseUrl: string,
150 workspaceId: string,
151 sourceId: string,
152): Promise<{ ok: boolean }> {
153 const response = await fetch(
154 withBase(
155 baseUrl,
156 `/api/tool-sources/${encodeURIComponent(sourceId)}?workspaceId=${encodeURIComponent(workspaceId)}`,
157 ),
158 { method: "DELETE" },
159 );
160 return parseJson<{ ok: boolean }>(response);
161}
162
163export async function bootstrapAnonymousContext(
164 baseUrl: string,
165 sessionId?: string,
166): Promise<AnonymousContext> {
167 const response = await fetch(withBase(baseUrl, "/api/auth/anonymous/bootstrap"), {
168 method: "POST",
169 headers: { "content-type": "application/json" },
170 body: JSON.stringify({ sessionId }),
171 });
172 return parseJson<AnonymousContext>(response);
173}
174
175export async function listPolicies(
176 baseUrl: string,
177 workspaceId: string,
178): Promise<AccessPolicyRecord[]> {
179 const response = await fetch(
180 withBase(baseUrl, `/api/policies?workspaceId=${encodeURIComponent(workspaceId)}`),
181 );
182 return parseJson<AccessPolicyRecord[]>(response);
183}
184
185export async function upsertPolicy(
186 baseUrl: string,
187 request: {
188 id?: string;
189 workspaceId: string;
190 actorId?: string;
191 clientId?: string;
192 toolPathPattern: string;
193 decision: PolicyDecision;
194 priority?: number;
195 },
196): Promise<AccessPolicyRecord> {
197 const response = await fetch(withBase(baseUrl, "/api/policies"), {
198 method: "POST",
199 headers: { "content-type": "application/json" },
200 body: JSON.stringify(request),
201 });
202 return parseJson<AccessPolicyRecord>(response);
203}
204
205export async function listCredentials(
206 baseUrl: string,
207 workspaceId: string,
208): Promise<CredentialDescriptor[]> {
209 const response = await fetch(
210 withBase(baseUrl, `/api/credentials?workspaceId=${encodeURIComponent(workspaceId)}`),
211 );
212 return parseJson<CredentialDescriptor[]>(response);
213}
214
215export async function upsertCredential(
216 baseUrl: string,
217 request: {
218 id?: string;
219 workspaceId: string;
220 sourceKey: string;
221 scope: CredentialScope;
222 actorId?: string;
223 secretJson: Record<string, unknown>;
224 },
225): Promise<CredentialDescriptor> {
226 const response = await fetch(withBase(baseUrl, "/api/credentials"), {
227 method: "POST",
228 headers: { "content-type": "application/json" },
229 body: JSON.stringify(request),
230 });
231 return parseJson<CredentialDescriptor>(response);
232}
233
234export function subscribeToTaskEvents(
235 baseUrl: string,
236 taskId: string,
237 workspaceId: string,
238 onEvent: (eventName: TaskEventRecord["eventName"], event: TaskEventRecord) => void,
239): EventSource {
240 const source = new EventSource(
241 withBase(
242 baseUrl,
243 `/api/tasks/${encodeURIComponent(taskId)}/events?workspaceId=${encodeURIComponent(workspaceId)}`,
244 ),
245 );
246
247 source.addEventListener("task", (event) => {
248 const message = event as MessageEvent<string>;
249 onEvent("task", JSON.parse(message.data) as TaskEventRecord);
250 });
251
252 source.addEventListener("approval", (event) => {
253 const message = event as MessageEvent<string>;
254 onEvent("approval", JSON.parse(message.data) as TaskEventRecord);
255 });
256
257 return source;
258}
executor/packages/contracts/package.json+0−8
@@ -1,8 +0,0 @@
1{
2 "name": "@executor/contracts",
3 "private": true,
4 "type": "module",
5 "exports": {
6 ".": "./src/index.ts"
7 }
8}
executor/packages/contracts/src/index.ts+0−154
@@ -1,154 +0,0 @@
1export type TaskStatus =
2 | "queued"
3 | "running"
4 | "completed"
5 | "failed"
6 | "timed_out"
7 | "denied";
8
9export type ApprovalStatus = "pending" | "approved" | "denied";
10export type ApprovalDecision = "approved" | "denied";
11
12export interface CreateTaskRequest {
13 code: string;
14 runtimeId?: string;
15 timeoutMs?: number;
16 metadata?: Record<string, unknown>;
17 workspaceId: string;
18 actorId: string;
19 clientId?: string;
20}
21
22export interface CreateTaskResponse {
23 taskId: string;
24 status: TaskStatus;
25}
26
27export interface TaskRecord {
28 id: string;
29 code: string;
30 runtimeId: string;
31 timeoutMs: number;
32 metadata: Record<string, unknown>;
33 workspaceId: string;
34 actorId?: string;
35 clientId?: string;
36 status: TaskStatus;
37 createdAt: number;
38 updatedAt: number;
39 startedAt?: number;
40 completedAt?: number;
41 stdout?: string;
42 stderr?: string;
43 exitCode?: number;
44 error?: string;
45}
46
47export interface ApprovalRecord {
48 id: string;
49 taskId: string;
50 toolPath: string;
51 input: unknown;
52 status: ApprovalStatus;
53 createdAt: number;
54 resolvedAt?: number;
55 reviewerId?: string;
56 reason?: string;
57}
58
59export interface PendingApprovalRecord extends ApprovalRecord {
60 task: Pick<TaskRecord, "id" | "status" | "runtimeId" | "timeoutMs" | "createdAt">;
61}
62
63export interface ResolveApprovalRequest {
64 workspaceId: string;
65 decision: ApprovalDecision;
66 reviewerId?: string;
67 reason?: string;
68}
69
70export interface AnonymousContext {
71 sessionId: string;
72 workspaceId: string;
73 actorId: string;
74 clientId: string;
75 createdAt: number;
76 lastSeenAt: number;
77}
78
79export interface RuntimeTargetDescriptor {
80 id: string;
81 label: string;
82 description: string;
83}
84
85export interface ToolDescriptor {
86 path: string;
87 description: string;
88 approval: "auto" | "required";
89 source?: string;
90 argsType?: string;
91 returnsType?: string;
92}
93
94export type PolicyDecision = "allow" | "require_approval" | "deny";
95
96export interface AccessPolicyRecord {
97 id: string;
98 workspaceId: string;
99 actorId?: string;
100 clientId?: string;
101 toolPathPattern: string;
102 decision: PolicyDecision;
103 priority: number;
104 createdAt: number;
105 updatedAt: number;
106}
107
108export type CredentialScope = "workspace" | "actor";
109
110export interface CredentialDescriptor {
111 id: string;
112 workspaceId: string;
113 sourceKey: string;
114 scope: CredentialScope;
115 actorId?: string;
116 hasSecret: boolean;
117}
118
119export type ToolSourceType = "mcp" | "openapi" | "graphql";
120
121export interface ToolSourceRecord {
122 id: string;
123 workspaceId: string;
124 name: string;
125 type: ToolSourceType;
126 config: Record<string, unknown>;
127 enabled: boolean;
128 createdAt: number;
129 updatedAt: number;
130}
131
132export interface TaskEventRecord {
133 id: number;
134 taskId: string;
135 eventName: "task" | "approval";
136 type: string;
137 payload: Record<string, unknown>;
138 createdAt: number;
139}
140
141export interface SandboxExecutionRequest {
142 taskId: string;
143 code: string;
144 timeoutMs: number;
145}
146
147export interface SandboxExecutionResult {
148 status: "completed" | "failed" | "timed_out" | "denied";
149 stdout: string;
150 stderr: string;
151 exitCode?: number;
152 error?: string;
153 durationMs: number;
154}
executor/tsconfig.json+1−9
@@ -13,15 +13,7 @@ · 12 unmodified lines
13 "ESNext",13 "ESNext",
14 "DOM"14 "DOM"
15 ],15 ],
16 "baseUrl": ".",16 "baseUrl": "."
17 "paths": {
18 "@executor/contracts": [
19 "packages/contracts/src/index.ts"
20 ],
21 "@executor/client": [
22 "packages/client/src/index.ts"
23 ]
24 }
25 },17 },
26 "include": [18 "include": [
27 "apps/**/*.ts",19 "apps/**/*.ts",