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
Unified · Split
| @@ -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 | } | ||
| @@ -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 | } |
| @@ -0,0 +1,87 @@ | |||
| 1 | import { expect, test } from "bun:test"; | ||
| 2 | import { Elysia, t } from "elysia"; | ||
| 3 | import { treaty } from "@elysiajs/eden"; | ||
| 4 | |||
| 5 | // Minimal Elysia server that mimics the executor's task creation route | ||
| 6 | function 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 | |||
| 38 | test("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 | |||
| 57 | test("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 | |||
| 72 | test("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 | }); | ||
| @@ -1,58 +1,8 @@ | |||
| 1 | export interface RunCodeRequest { | 1 | import { treaty } from "@elysiajs/eden"; |
| 2 | code: string; | 2 | import type { App } from "@executor/server/src/index"; |
| 3 | timeoutMs?: number; | ||
| 4 | runtimeId?: string; | ||
| 5 | } | ||
| 6 | |||
| 7 | export interface RunCodeResponse { | ||
| 8 | taskId: string; | ||
| 9 | } | ||
| 10 | |||
| 11 | export 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 }; | 4 | export function createExecutorClient(baseUrl: string) { |
| 26 | return { taskId: payload.taskId }; | 5 | return treaty<App>(baseUrl); |
| 27 | } | 6 | } |
| 28 | 7 | ||
| 29 | export function subscribeToTaskEvents( | 8 | export 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 | } | ||
| @@ -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 | } | ||
| @@ -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": { |
| @@ -1,3 +1,4 @@ | |||
| 1 | import { Elysia, t } from "elysia"; | ||
| 1 | import { ExecutorDatabase } from "./database"; | 2 | import { ExecutorDatabase } from "./database"; |
| 2 | import { TaskEventHub } from "./events"; | 3 | import { TaskEventHub } from "./events"; |
| 3 | import { handleMcpRequest } from "./mcp-server"; | 4 | import { handleMcpRequest } from "./mcp-server"; |
| @@ -9,15 +10,13 @@ import { loadExternalTools, parseToolSourcesFromEnv } from "./tool-sources"; · 5 unmodified lines | |||
| 9 | import { DEFAULT_TOOLS } from "./tools"; | 10 | import { DEFAULT_TOOLS } from "./tools"; |
| 10 | import type { | 11 | import 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 | |||
| 21 | const port = Number(Bun.env.PORT ?? "4001"); | 20 | const port = Number(Bun.env.PORT ?? "4001"); |
| 22 | const autoFunnelEnabled = Bun.env.EXECUTOR_AUTO_TAILSCALE_FUNNEL !== "0"; | 21 | const autoFunnelEnabled = Bun.env.EXECUTOR_AUTO_TAILSCALE_FUNNEL !== "0"; |
| 23 | const explicitInternalBaseUrl = Bun.env.EXECUTOR_INTERNAL_BASE_URL ?? Bun.env.EXECUTOR_PUBLIC_BASE_URL; | 22 | const 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 | ||
| 68 | const 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 | |||
| 75 | function json(data: unknown, status = 200): Response { | ||
| 76 | return new Response(JSON.stringify(data), { | ||
| 77 | status, | ||
| 78 | headers: jsonHeaders, | ||
| 79 | }); | ||
| 80 | } | ||
| 81 | |||
| 82 | function error(status: number, message: string): Response { | ||
| 83 | return json({ error: message }, status); | ||
| 84 | } | ||
| 85 | |||
| 86 | function 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 | |||
| 99 | async 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 | |||
| 107 | async 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 | ||
| 69 | function 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 | ||
| 182 | const 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)); | 147 | function 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, | 156 | const 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); | 528 | export 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 | ||
| 534 | console.log(`executor server listening on http://localhost:${server.port}`); | 530 | console.log(`executor server listening on http://localhost:${app.server!.port}`); |
| 535 | console.log(`[executor] internal callback base: ${internalBaseUrl} (${internalBaseSource})`); | 531 | console.log(`[executor] internal callback base: ${internalBaseUrl} (${internalBaseSource})`); |
| 536 | console.log(`[executor] internal callback auth token enabled: yes`); | 532 | console.log(`[executor] internal callback auth token enabled: yes`); |
| 537 | console.log(`[executor] tools loaded: ${tools.length} (${externalTools.length} external)`); | 533 | console.log(`[executor] tools loaded: ${tools.length} (${externalTools.length} external)`); |
| @@ -1,221 +0,0 @@ | |||
| 1 | // Thin API client that calls through the Next.js rewrite proxy (/api/* -> backend) | ||
| 2 | |||
| 3 | import 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 | |||
| 18 | const BASE = ""; | ||
| 19 | |||
| 20 | async 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 | |||
| 30 | export 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 | |||
| 43 | export 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 | |||
| 54 | export 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 | |||
| 61 | export 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 | |||
| 73 | export 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 | |||
| 82 | export 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 | |||
| 92 | export 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 | |||
| 109 | export 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 | |||
| 118 | export 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 | |||
| 132 | export 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 | |||
| 141 | export 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 | |||
| 157 | export 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 | |||
| 170 | export 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 | |||
| 179 | export 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 | |||
| 198 | export 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 | |||
| 207 | export 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 | } | ||
| @@ -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=="], |
| @@ -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 | } | ||
| @@ -1,258 +0,0 @@ | |||
| 1 | import 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 | |||
| 19 | function withBase(baseUrl: string, path: string): string { | ||
| 20 | return `${baseUrl.replace(/\/$/, "")}${path}`; | ||
| 21 | } | ||
| 22 | |||
| 23 | async 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 | |||
| 31 | export 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 | |||
| 43 | export async function listTasks(baseUrl: string): Promise<TaskRecord[]> { | ||
| 44 | throw new Error("listTasks now requires workspaceId. Use listTasksForWorkspace."); | ||
| 45 | } | ||
| 46 | |||
| 47 | export 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 | |||
| 54 | export 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 | |||
| 64 | export 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 | |||
| 76 | export 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 | |||
| 86 | export 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 | |||
| 99 | export async function listRuntimeTargets(baseUrl: string): Promise<RuntimeTargetDescriptor[]> { | ||
| 100 | const response = await fetch(withBase(baseUrl, "/api/runtime-targets")); | ||
| 101 | return parseJson<RuntimeTargetDescriptor[]>(response); | ||
| 102 | } | ||
| 103 | |||
| 104 | export async function listTools(baseUrl: string): Promise<ToolDescriptor[]> { | ||
| 105 | throw new Error("listTools now requires workspace context. Use listToolsForContext."); | ||
| 106 | } | ||
| 107 | |||
| 108 | export 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 | |||
| 119 | export 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 | |||
| 129 | export 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 | |||
| 148 | export 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 | |||
| 163 | export 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 | |||
| 175 | export 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 | |||
| 185 | export 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 | |||
| 205 | export 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 | |||
| 215 | export 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 | |||
| 234 | export 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 | } | ||
| @@ -1,8 +0,0 @@ | |||
| 1 | { | ||
| 2 | "name": "@executor/contracts", | ||
| 3 | "private": true, | ||
| 4 | "type": "module", | ||
| 5 | "exports": { | ||
| 6 | ".": "./src/index.ts" | ||
| 7 | } | ||
| 8 | } | ||
| @@ -1,154 +0,0 @@ | |||
| 1 | export type TaskStatus = | ||
| 2 | | "queued" | ||
| 3 | | "running" | ||
| 4 | | "completed" | ||
| 5 | | "failed" | ||
| 6 | | "timed_out" | ||
| 7 | | "denied"; | ||
| 8 | |||
| 9 | export type ApprovalStatus = "pending" | "approved" | "denied"; | ||
| 10 | export type ApprovalDecision = "approved" | "denied"; | ||
| 11 | |||
| 12 | export 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 | |||
| 22 | export interface CreateTaskResponse { | ||
| 23 | taskId: string; | ||
| 24 | status: TaskStatus; | ||
| 25 | } | ||
| 26 | |||
| 27 | export 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 | |||
| 47 | export 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 | |||
| 59 | export interface PendingApprovalRecord extends ApprovalRecord { | ||
| 60 | task: Pick<TaskRecord, "id" | "status" | "runtimeId" | "timeoutMs" | "createdAt">; | ||
| 61 | } | ||
| 62 | |||
| 63 | export interface ResolveApprovalRequest { | ||
| 64 | workspaceId: string; | ||
| 65 | decision: ApprovalDecision; | ||
| 66 | reviewerId?: string; | ||
| 67 | reason?: string; | ||
| 68 | } | ||
| 69 | |||
| 70 | export interface AnonymousContext { | ||
| 71 | sessionId: string; | ||
| 72 | workspaceId: string; | ||
| 73 | actorId: string; | ||
| 74 | clientId: string; | ||
| 75 | createdAt: number; | ||
| 76 | lastSeenAt: number; | ||
| 77 | } | ||
| 78 | |||
| 79 | export interface RuntimeTargetDescriptor { | ||
| 80 | id: string; | ||
| 81 | label: string; | ||
| 82 | description: string; | ||
| 83 | } | ||
| 84 | |||
| 85 | export interface ToolDescriptor { | ||
| 86 | path: string; | ||
| 87 | description: string; | ||
| 88 | approval: "auto" | "required"; | ||
| 89 | source?: string; | ||
| 90 | argsType?: string; | ||
| 91 | returnsType?: string; | ||
| 92 | } | ||
| 93 | |||
| 94 | export type PolicyDecision = "allow" | "require_approval" | "deny"; | ||
| 95 | |||
| 96 | export 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 | |||
| 108 | export type CredentialScope = "workspace" | "actor"; | ||
| 109 | |||
| 110 | export interface CredentialDescriptor { | ||
| 111 | id: string; | ||
| 112 | workspaceId: string; | ||
| 113 | sourceKey: string; | ||
| 114 | scope: CredentialScope; | ||
| 115 | actorId?: string; | ||
| 116 | hasSecret: boolean; | ||
| 117 | } | ||
| 118 | |||
| 119 | export type ToolSourceType = "mcp" | "openapi" | "graphql"; | ||
| 120 | |||
| 121 | export 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 | |||
| 132 | export 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 | |||
| 141 | export interface SandboxExecutionRequest { | ||
| 142 | taskId: string; | ||
| 143 | code: string; | ||
| 144 | timeoutMs: number; | ||
| 145 | } | ||
| 146 | |||
| 147 | export 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 | } | ||
| @@ -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", |