diff --git a/apps/cloud/package.json b/apps/cloud/package.json index b982e2a2af..8d971c040c 100644 --- a/apps/cloud/package.json +++ b/apps/cloud/package.json @@ -66,7 +66,6 @@ "@tanstack/react-router": "catalog:", "@tanstack/react-start": "catalog:", "@workos-inc/node": "^8.11.1", - "agents": "^0.10.0", "autumn-js": "^1.2.8", "drizzle-orm": "catalog:", "effect": "catalog:", diff --git a/apps/cloud/src/api/protected.test.ts b/apps/cloud/src/api/protected.test.ts index 5a1a02283e..858f9710f4 100644 --- a/apps/cloud/src/api/protected.test.ts +++ b/apps/cloud/src/api/protected.test.ts @@ -17,6 +17,8 @@ const makeBaseEngine = (): ExecutionEngine => result: { result: "ok", logs: [] }, }), getPausedExecution: () => Effect.succeed(null), + pausedExecutionCount: () => Effect.succeed(0), + hasPausedExecutions: () => Effect.succeed(false), getDescription: Effect.succeed("desc"), }) as ExecutionEngine; diff --git a/apps/cloud/src/engine/execution-usage.ts b/apps/cloud/src/engine/execution-usage.ts index cd4489984f..50ba3fc345 100644 --- a/apps/cloud/src/engine/execution-usage.ts +++ b/apps/cloud/src/engine/execution-usage.ts @@ -19,5 +19,7 @@ export const withExecutionUsageTracking = ( // resume doesn't count as usage resume: (executionId, response) => engine.resume(executionId, response), getPausedExecution: (executionId) => engine.getPausedExecution(executionId), + pausedExecutionCount: () => engine.pausedExecutionCount(), + hasPausedExecutions: () => engine.hasPausedExecutions(), getDescription: engine.getDescription, }); diff --git a/apps/cloud/src/mcp/agent-handler.ts b/apps/cloud/src/mcp/agent-handler.ts new file mode 100644 index 0000000000..142175d14e --- /dev/null +++ b/apps/cloud/src/mcp/agent-handler.ts @@ -0,0 +1,138 @@ +import { Effect, Predicate } from "effect"; + +import { McpAuthProvider, jsonRpcErrorBody, type AuthOutcome } from "@executor-js/host-mcp"; +import { + currentPropagationHeaders, + readElicitationMode, + withVerifiedIdentityHeaders, +} from "@executor-js/cloudflare/mcp/do-headers"; +import type { McpSessionProps } from "@executor-js/cloudflare/mcp/agent-durable-object"; + +import { cloudMcpAuth } from "./auth-provider"; +import { McpSessionDO } from "./session-durable-object"; + +interface McpAgentSessionStub { + readonly validateMcpSessionOwner: (identity: { + readonly accountId: string; + readonly organizationId: string; + }) => Promise<"ok" | "not_found" | "forbidden">; + readonly _cf_scheduleDestroy: () => Promise; +} + +const corsPreflightResponse = (): Response => + new Response(null, { + status: 204, + headers: { + "access-control-allow-origin": "*", + "access-control-allow-methods": "GET, POST, DELETE, OPTIONS", + "access-control-allow-headers": + "content-type, authorization, mcp-session-id, accept, mcp-protocol-version", + "access-control-expose-headers": "mcp-session-id, WWW-Authenticate", + }, + }); + +const jsonRpcResponse = ( + status: number, + code: number, + message: string, + challenge?: string, +): Response => + challenge === undefined + ? jsonRpcErrorBody(status, code, message) + : jsonRpcErrorBody(status, code, message, { challenge }); + +const renderAuthError = ( + auth: McpAuthProvider["Service"], + request: Request, + outcome: Exclude, +): Response => { + if (Predicate.isTagged(outcome, "Unauthorized")) { + return jsonRpcResponse( + 401, + -32001, + "Unauthorized", + outcome.challenge ?? `Bearer resource_metadata="${auth.resourceMetadataUrl(request)}"`, + ); + } + if (Predicate.isTagged(outcome, "Forbidden")) { + return jsonRpcResponse(403, outcome.code ?? -32001, outcome.message); + } + return jsonRpcResponse(503, -32001, outcome.message); +}; + +const sessionStub = (env: Env, sessionId: string): McpAgentSessionStub => + // oxlint-disable-next-line executor/no-double-cast -- boundary: Workers types expose only DurableObjectStub, but RPC methods are generated from the bound DO class. + env.MCP_SESSION.get( + env.MCP_SESSION.idFromName(`streamable-http:${sessionId}`), + ) as unknown as McpAgentSessionStub; + +const authenticate = (request: Request) => + Effect.gen(function* () { + const auth = yield* McpAuthProvider; + const outcome = yield* auth.authenticate(request); + return { auth, outcome }; + }).pipe(Effect.provide(cloudMcpAuth)); + +const propsForPrincipal = ( + request: Request, + principal: Extract["principal"], +): Effect.Effect => + Effect.gen(function* () { + const propagation = yield* currentPropagationHeaders(request); + return { + session: { + organizationId: principal.organizationId, + userId: principal.accountId, + elicitationMode: readElicitationMode(request), + webOrigin: new URL(request.url).origin, + }, + propagation, + }; + }); + +export const makeCloudMcpAgentHandler = () => { + const serve = McpSessionDO.serve("/mcp", { + binding: "MCP_SESSION", + transport: "streamable-http", + }); + + return async (request: Request, env: Env, ctx: ExecutionContext): Promise => { + if (request.method === "OPTIONS") return corsPreflightResponse(); + const sessionId = request.headers.get("mcp-session-id"); + + const { auth, outcome } = await Effect.runPromise(authenticate(request)); + if (!Predicate.isTagged(outcome, "Authenticated")) { + if (Predicate.isTagged(outcome, "Forbidden") && sessionId) { + await Effect.runPromise( + Effect.ignore(Effect.tryPromise(() => sessionStub(env, sessionId)._cf_scheduleDestroy())), + ); + } + return renderAuthError(auth, request, outcome); + } + + if (!sessionId && request.method === "DELETE") { + return new Response(null, { status: 204, headers: { "access-control-allow-origin": "*" } }); + } + + if (sessionId) { + const owner = await sessionStub(env, sessionId).validateMcpSessionOwner({ + accountId: outcome.principal.accountId, + organizationId: outcome.principal.organizationId, + }); + if (owner === "not_found") { + return jsonRpcResponse(404, -32001, "Session not found"); + } + if (owner === "forbidden") { + return jsonRpcResponse(403, -32003, "MCP session does not belong to the current bearer"); + } + } + + const props = await Effect.runPromise(propsForPrincipal(request, outcome.principal)); + (ctx as ExecutionContext & { props?: McpSessionProps }).props = props; + const forwarded = withVerifiedIdentityHeaders(request, { + accountId: outcome.principal.accountId, + organizationId: outcome.principal.organizationId, + }); + return serve.fetch(forwarded, env, ctx); + }; +}; diff --git a/apps/cloud/src/mcp/mount.ts b/apps/cloud/src/mcp/mount.ts index 58c4405072..037e425522 100644 --- a/apps/cloud/src/mcp/mount.ts +++ b/apps/cloud/src/mcp/mount.ts @@ -3,11 +3,11 @@ // host-mcp serving envelope (@executor-js/host-mcp) behind cloud's two seams. // --------------------------------------------------------------------------- // -// PRODUCTION serves /mcp through `app.ts`'s unified `ExecutorApp.make` handler -// (the same `McpServingRoutes` envelope provided `cloudMcpAuth` + -// `cloudMcpSessions`), dispatched by start.ts alongside /api. This module is the -// TEST-WORKER counterpart: it exposes the two pieces `test-worker.ts` needs to -// build the identical envelope with swapped auth seams — +// PRODUCTION serves /mcp through `server.ts`'s hibernatable Agent bridge. +// Discovery docs still flow through `app.ts`'s unified `ExecutorApp.make` +// handler, dispatched by start.ts alongside /api. This module is the TEST-WORKER +// counterpart: it exposes the two pieces `test-worker.ts` needs to build the +// legacy envelope with swapped auth seams — // - `makeMcpWebHandler` — bind `McpServingRoutes` to a web handler over a // given auth provider + seam requirements + telemetry runtime, mirroring the // self-host mount (`HttpRouter.toWebHandler`). @@ -172,9 +172,8 @@ export const makeMcpWebHandler = (options: { ).handler; }; -// Production no longer mounts /mcp here — `app.ts`'s unified `ExecutorApp.make` -// handler serves it (the same `McpServingRoutes` envelope + cloud seams as -// `cloudMcpAuth`/`cloudMcpSessions`), dispatched by start.ts alongside /api. -// `classifyMcpPath` + `makeMcpWebHandler` remain because the workerd/miniflare -// test worker (`test-worker.ts`) builds the same envelope with swapped auth -// seams and classifies MCP paths with the identical predicate. +// Production no longer mounts /mcp here. `server.ts` intercepts MCP transport +// requests for the hibernatable Agent bridge, while `ExecutorApp.make` continues +// serving discovery docs. `classifyMcpPath` + `makeMcpWebHandler` remain because +// the workerd/miniflare test worker (`test-worker.ts`) builds the same envelope +// with swapped auth seams and classifies MCP paths with the identical predicate. diff --git a/apps/cloud/src/mcp/session-durable-object.ts b/apps/cloud/src/mcp/session-durable-object.ts index 5513ddb901..9533b72aae 100644 --- a/apps/cloud/src/mcp/session-durable-object.ts +++ b/apps/cloud/src/mcp/session-durable-object.ts @@ -1,8 +1,8 @@ // --------------------------------------------------------------------------- // Cloud MCP Session Durable Object — the cloud binding of the shared -// `McpSessionDOBase` (@executor-js/cloudflare). All session lifecycle (cold -// restore, the inactivity alarm, owner validation, transport upgrade, the -// browser-approval store, the per-request span bridge) lives in the base; cloud +// `McpAgentSessionDOBase` (@executor-js/cloudflare). Hibernatable transport +// serving, cold restore, the inactivity alarm, owner validation, browser +// approval storage, and the per-request span bridge live in the base. Cloud // supplies ONLY its injected dependencies: // - openSessionDb → a long-lived postgres.js handle // - resolveSessionMeta → WorkOS/UserStore organization resolution @@ -24,12 +24,12 @@ import postgres, { type Sql } from "postgres"; import { createExecutorMcpServer } from "@executor-js/host-mcp/tool-server"; import { buildResumeApprovalUrl } from "@executor-js/host-mcp/browser-approval"; import { - McpSessionDOBase, + McpAgentSessionDOBase, type BuiltMcpServer, type IncomingTraceHeaders, type McpSessionInit, type SessionMeta, -} from "@executor-js/cloudflare/mcp/durable-object"; +} from "@executor-js/cloudflare/mcp/agent-durable-object"; import { buildExecuteDescription } from "@executor-js/execution"; // The DO only needs the neutral boot-scoped service (WorkOSClient). It never @@ -62,7 +62,7 @@ export type { McpSessionResumeApprovalResult, McpSessionInit, IncomingTraceHeaders, -} from "@executor-js/cloudflare/mcp/durable-object"; +} from "@executor-js/cloudflare/mcp/agent-durable-object"; // --------------------------------------------------------------------------- // Cloud DB handle — one postgres.js client per session runtime @@ -152,7 +152,7 @@ const makeSessionServices = (dbHandle: CloudSessionDbHandle) => { // Durable Object // --------------------------------------------------------------------------- -export class McpSessionDO extends McpSessionDOBase { +export class McpSessionDO extends McpAgentSessionDOBase { protected override openSessionDb(): CloudSessionDbHandle { return makeDbHandle({ idleTimeout: LONG_LIVED_DB_IDLE_TIMEOUT_SECONDS, @@ -208,6 +208,7 @@ export class McpSessionDO extends McpSessionDOBase { parentSpan: () => self.currentParentSpan(), debug: env.EXECUTOR_MCP_DEBUG === "true", browserApprovalStore: self.browserApprovalStore, + pausedExecutionHooks: self.pausedExecutionHooks, elicitationMode: sessionElicitationMode === "browser" ? { diff --git a/apps/cloud/src/server.ts b/apps/cloud/src/server.ts index 226f364e64..7760fef7d2 100644 --- a/apps/cloud/src/server.ts +++ b/apps/cloud/src/server.ts @@ -10,6 +10,8 @@ import * as Sentry from "@sentry/cloudflare"; import handler from "@tanstack/react-start/server-entry"; import { isAppOwnedPath } from "./app-paths"; +import { makeCloudMcpAgentHandler } from "./mcp/agent-handler"; +import { classifyMcpPath, prepareMcpOrgScope } from "./mcp/mount"; import { McpSessionDO as McpSessionDOBase } from "./mcp/session-durable-object"; import { browserTracesResponse } from "./observability/browser-traces"; import { flushTracerProvider, installTracerProvider } from "./observability/telemetry"; @@ -77,6 +79,7 @@ const fetchHandler = handler.fetch as ( ) => Response | Promise; const tracer = trace.getTracer("executor-cloud-worker"); +const mcpAgentHandler = makeCloudMcpAgentHandler(); const cloudflareHandler: ExportedHandler = { fetch: async (request, env, ctx) => { @@ -89,6 +92,18 @@ const cloudflareHandler: ExportedHandler = { return fetchHandler(request, env, ctx); } const url = new URL(request.url); + const mcpRoute = classifyMcpPath(url.pathname); + if (mcpRoute?.kind === "mcp") { + // The Cloudflare Agents MCP bridge needs the platform ExecutionContext + // to pass authenticated session props into the hibernatable DO. + // Discovery docs still flow through the app-level MCP envelope. + // oxlint-disable-next-line executor/no-try-catch-or-throw -- adapter boundary; keep trace export alive after the Agents bridge resolves or rejects + try { + return await mcpAgentHandler(prepareMcpOrgScope(request), env, ctx); + } finally { + ctx.waitUntil(flushTracerProvider()); + } + } // Effect-served paths bring their own http.server span (with traceparent // join) — opening one here too would duplicate it. See the header note. if (isAppOwnedPath(url.pathname)) { diff --git a/apps/host-cloudflare/src/app.ts b/apps/host-cloudflare/src/app.ts index 9c284a48a1..8c3eebf789 100644 --- a/apps/host-cloudflare/src/app.ts +++ b/apps/host-cloudflare/src/app.ts @@ -14,15 +14,16 @@ import { } from "./execution"; import { ErrorCaptureLive } from "./observability"; import { cloudflareAccountMiddleware } from "./account/account-provider"; -import { makeCloudflareMcpSeams } from "./mcp"; +import { makeCloudflareApprovalHandler } from "./mcp"; +import { makeCloudflareMcpAgentHandler } from "./mcp/agent-handler"; import { preloadQuickJs } from "./quickjs"; // =========================================================================== -// The Cloudflare host, as ONE `ExecutorApp.make` call — the 4th app alongside +// The Cloudflare host, as ONE `ExecutorApp.make` call: the 4th app alongside // cloud / self-host / local, differing only by the injected Layers. // // The whole scenario in 60 seconds: Cloudflare Access is the identity (validate -// the Cf-Access-Jwt-Assertion JWT — no Better Auth, no WorkOS, no app login), +// the Cf-Access-Jwt-Assertion JWT, no Better Auth, no WorkOS, no app login), // D1 is the SQLite store (same FumaDB assembly as self-host), QuickJS is the // in-process code substrate, no billing, single-tenant. `diff` against // host-selfhost/src/app.ts is three injected Layers: identity, db, plugins/config. @@ -37,23 +38,22 @@ export const makeCloudflareApp = async (env: CloudflareEnv) => { const plugins = makeCloudflarePlugins(config.secretKey); // Load the Workers-compatible (WASM-inlined) QuickJS variant before any - // executor is built — the default variant can't fetch its .wasm on Workers. + // executor is built, the default variant cannot fetch its .wasm on Workers. await preloadQuickJs(); - // Open + idempotently bring up the D1 schema once (the long-lived handle the - // per-request scoped executor reads through the DbProvider seam). + // Open and idempotently bring up the D1 schema once. This is the long-lived + // handle the per-request scoped executor reads through the DbProvider seam. const dbHandle = await createD1ExecutorDb(env.DB, env.BLOBS); const identityLayer = cloudflareAccessIdentityLayer(config); - // MCP runs through the `MCP_SESSION` Durable Object (cross-isolate sessions); - // each session DO opens its own D1 handle, so it takes `env`, not `dbHandle`. - const mcp = makeCloudflareMcpSeams(config, env); + const mcpAgentHandler = makeCloudflareMcpAgentHandler(config); + const approvalHandler = makeCloudflareApprovalHandler(config, env); const { appLayer, toWebHandler } = ExecutorApp.make({ plugins, providers: { identity: identityLayer, db: dbProviderLayer(Effect.succeed(dbHandle)), - engine: { codeExecutor: CloudflareCodeExecutorProvider }, // decorator defaults to no-op + engine: { codeExecutor: CloudflareCodeExecutorProvider }, plugins: { provider: makeCloudflarePluginsProvider(config), config: makeCloudflareHostConfig(config), @@ -63,21 +63,18 @@ export const makeCloudflareApp = async (env: CloudflareEnv) => { // auth context; `me` reflects the Access principal. Members/keys are // Access-managed, so the rest of the surface is stubbed. account: cloudflareAccountMiddleware(config), - // The MCP serving envelope: Access-JWT auth + the shared in-process session - // store over the QuickJS engine. - mcp: { auth: mcp.auth, sessions: mcp.sessions, reporter: mcp.reporter }, }, extensions: { routes: [ // Browser approval of paused MCP executions: the console resume page // reads paused detail (GET) and records the decision (POST .../resume), // Access-gated, routed to the owning session's Durable Object. - HttpRouter.add("*", "/api/mcp-sessions/*", HttpEffect.fromWebHandler(mcp.approvalHandler)), + HttpRouter.add("*", "/api/mcp-sessions/*", HttpEffect.fromWebHandler(approvalHandler)), ], }, config: { mountPrefix: "/api", failure: textFailureStrategy }, boot: identityLayer, }); - return { appLayer, toWebHandler }; + return { appLayer, toWebHandler, mcpAgentHandler }; }; diff --git a/apps/host-cloudflare/src/mcp/agent-handler.ts b/apps/host-cloudflare/src/mcp/agent-handler.ts new file mode 100644 index 0000000000..879f4958bf --- /dev/null +++ b/apps/host-cloudflare/src/mcp/agent-handler.ts @@ -0,0 +1,144 @@ +import { Effect, Predicate } from "effect"; + +import { + McpAuthProvider, + jsonRpcErrorBody, + type AuthOutcome, + type Principal, +} from "@executor-js/host-mcp"; +import { + currentPropagationHeaders, + readElicitationMode, + withVerifiedIdentityHeaders, +} from "@executor-js/cloudflare/mcp/do-headers"; +import type { McpSessionProps } from "@executor-js/cloudflare/mcp/agent-durable-object"; + +import type { CloudflareConfig, CloudflareEnv } from "../config"; +import { cloudflareAccessMcpAuth } from "./auth"; +import { McpSessionDO } from "./session-durable-object"; + +interface McpAgentSessionStub { + readonly validateMcpSessionOwner: (identity: { + readonly accountId: string; + readonly organizationId: string; + }) => Promise<"ok" | "not_found" | "forbidden">; + readonly _cf_scheduleDestroy: () => Promise; +} + +const corsPreflightResponse = (): Response => + new Response(null, { + status: 204, + headers: { + "access-control-allow-origin": "*", + "access-control-allow-methods": "GET, POST, DELETE, OPTIONS", + "access-control-allow-headers": + "content-type, authorization, mcp-session-id, accept, mcp-protocol-version", + "access-control-expose-headers": "mcp-session-id, WWW-Authenticate", + }, + }); + +const jsonRpcResponse = ( + status: number, + code: number, + message: string, + challenge?: string, +): Response => + challenge === undefined + ? jsonRpcErrorBody(status, code, message) + : jsonRpcErrorBody(status, code, message, { challenge }); + +const renderAuthError = ( + auth: McpAuthProvider["Service"], + request: Request, + outcome: Exclude, +): Response => { + if (Predicate.isTagged(outcome, "Unauthorized")) { + return jsonRpcResponse( + 401, + -32001, + "Unauthorized", + outcome.challenge ?? `Bearer resource_metadata="${auth.resourceMetadataUrl(request)}"`, + ); + } + if (Predicate.isTagged(outcome, "Forbidden")) { + return jsonRpcResponse(403, outcome.code ?? -32001, outcome.message); + } + return jsonRpcResponse(503, -32001, outcome.message); +}; + +const sessionStub = (env: CloudflareEnv, sessionId: string): McpAgentSessionStub => + // oxlint-disable-next-line executor/no-double-cast -- boundary: Workers types expose only DurableObjectStub, but RPC methods are generated from the bound DO class. + env.MCP_SESSION.get( + env.MCP_SESSION.idFromName(`streamable-http:${sessionId}`), + ) as unknown as McpAgentSessionStub; + +const authenticate = (request: Request, config: CloudflareConfig) => + Effect.gen(function* () { + const auth = yield* McpAuthProvider; + const outcome = yield* auth.authenticate(request); + return { auth, outcome }; + }).pipe(Effect.provide(cloudflareAccessMcpAuth(config))); + +const propsForPrincipal = ( + request: Request, + principal: Principal, +): Effect.Effect => + Effect.gen(function* () { + const propagation = yield* currentPropagationHeaders(request); + return { + session: { + organizationId: principal.organizationId, + userId: principal.accountId, + elicitationMode: readElicitationMode(request), + webOrigin: new URL(request.url).origin, + }, + propagation, + }; + }); + +export const makeCloudflareMcpAgentHandler = (config: CloudflareConfig) => { + const serve = McpSessionDO.serve("/mcp", { + binding: "MCP_SESSION", + transport: "streamable-http", + }); + + return async (request: Request, env: CloudflareEnv, ctx: ExecutionContext): Promise => { + if (request.method === "OPTIONS") return corsPreflightResponse(); + const sessionId = request.headers.get("mcp-session-id"); + + const { auth, outcome } = await Effect.runPromise(authenticate(request, config)); + if (!Predicate.isTagged(outcome, "Authenticated")) { + if (Predicate.isTagged(outcome, "Forbidden") && sessionId) { + await Effect.runPromise( + Effect.ignore(Effect.tryPromise(() => sessionStub(env, sessionId)._cf_scheduleDestroy())), + ); + } + return renderAuthError(auth, request, outcome); + } + + if (!sessionId && request.method === "DELETE") { + return new Response(null, { status: 204, headers: { "access-control-allow-origin": "*" } }); + } + + if (sessionId) { + const owner = await sessionStub(env, sessionId).validateMcpSessionOwner({ + accountId: outcome.principal.accountId, + organizationId: outcome.principal.organizationId, + }); + if (owner === "not_found") { + return jsonRpcResponse(404, -32001, "Session not found"); + } + if (owner === "forbidden") { + return jsonRpcResponse(403, -32003, "MCP session does not belong to the current bearer"); + } + } + + const props = await Effect.runPromise(propsForPrincipal(request, outcome.principal)); + (ctx as ExecutionContext & { props?: McpSessionProps }).props = props; + const forwarded = withVerifiedIdentityHeaders(request, { + accountId: outcome.principal.accountId, + organizationId: outcome.principal.organizationId, + }); + return serve.fetch(forwarded, env, ctx); + }; +}; diff --git a/apps/host-cloudflare/src/mcp/index.ts b/apps/host-cloudflare/src/mcp/index.ts index 87e875646d..bac962c581 100644 --- a/apps/host-cloudflare/src/mcp/index.ts +++ b/apps/host-cloudflare/src/mcp/index.ts @@ -1,58 +1,19 @@ -import { Effect, type Layer } from "effect"; +import { Effect } from "effect"; -import type { McpAuthProvider, McpErrorReporter, McpSessionStore } from "@executor-js/host-mcp"; import { decodeResumeResponse } from "@executor-js/host-mcp/browser-approval"; import type { McpApprovalOwner, McpSessionApprovalResult, McpSessionResumeApprovalResult, -} from "@executor-js/cloudflare/mcp/durable-object"; +} from "@executor-js/cloudflare/mcp/agent-durable-object"; import type { ResumeResponse } from "@executor-js/execution"; import type { CloudflareConfig, CloudflareEnv } from "../config"; import { makeAccessVerifier } from "../auth/cloudflare-access"; -import { cloudflareAccessMcpAuth } from "./auth"; -import { cloudflareMcpReporter, makeCloudflareMcpSessionStore } from "./session-store"; export { cloudflareAccessMcpAuth } from "./auth"; -export { cloudflareMcpReporter, makeCloudflareMcpSessionStore } from "./session-store"; export { McpSessionDO } from "./session-durable-object"; -// --------------------------------------------------------------------------- -// The Cloudflare MCP serving seams, fed to `ExecutorApp.make`'s `mcp` group. -// -// `ExecutorApp.make` mounts the shared, provider-neutral MCP serving envelope -// (@executor-js/host-mcp) at the top-level `/mcp`, outside the API's execution -// middleware. The Cloudflare host provides the two envelope seams plus the -// error-reporter override: -// - McpAuthProvider -> `cloudflareAccessMcpAuth`: validate the Access JWT -// (same identity as the API gate); no MCP OAuth. -// - McpSessionStore -> the shared Durable-Object dispatcher over the host's -// `MCP_SESSION` namespace (cross-isolate, same as cloud). -// - McpErrorReporter -> `cloudflareMcpReporter`: route 500 defects through the -// host's console capture. -// --------------------------------------------------------------------------- - -export interface CloudflareMcpSeams { - /** Validate the Access JWT to an MCP `AuthOutcome`; declares no discovery routes. */ - readonly auth: Layer.Layer; - /** The Durable-Object session store seam (dispatch + lifetime). */ - readonly sessions: Layer.Layer; - /** Route 500 defects through the host's console `ErrorCapture`. */ - readonly reporter: Layer.Layer; - /** - * The browser-approval HTTP handler, mounted by the app at - * `/api/mcp-sessions/*`: an Access-gated web handler that reads paused-execution - * detail (GET) and records the human's decision (POST `/resume`) for the console - * resume page, routing each to the owning session's Durable Object RPCs. - */ - readonly approvalHandler: (request: Request) => Promise; -} - -// The MCP session Durable Object exposes the approval RPCs (the base class -// implements them); `@cloudflare/workers-types` types the stub generically, so -// narrow at this one boundary via a single `unknown`-param hop (same shape the -// session-store seam uses for the dispatch stub). const toApprovalStub = (stub: unknown): McpApprovalStub => stub as McpApprovalStub; interface McpApprovalStub { @@ -73,19 +34,13 @@ const RESUME_PATH = /^\/api\/mcp-sessions\/([^/?#]+)\/executions\/([^/?#]+)\/res const jsonResponse = (value: unknown, status: number): Response => new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } }); -/** - * Resolve the request to its Access principal (dev-auth → the fixed dev admin), - * then route the browser-approval call to the owning session's Durable Object — - * the same RPCs cloud serves through its HttpApi. The DO validates that the - * principal owns the session before reading or resuming. - */ -const makeCloudflareApprovalHandler = ( +export const makeCloudflareApprovalHandler = ( config: CloudflareConfig, env: CloudflareEnv, ): ((request: Request) => Promise) => { const { verify } = makeAccessVerifier(config); const stubFor = (sessionId: string): McpApprovalStub => - toApprovalStub(env.MCP_SESSION.get(env.MCP_SESSION.idFromString(sessionId))); + toApprovalStub(env.MCP_SESSION.get(env.MCP_SESSION.idFromName(`streamable-http:${sessionId}`))); return async (request) => { const principal = await Effect.runPromise(verify(request)); @@ -136,18 +91,3 @@ const makeCloudflareApprovalHandler = ( return jsonResponse({ error: "Not found" }, 404); }; }; - -/** - * Build the Cloudflare MCP serving seams over the host's `MCP_SESSION` Durable - * Object namespace. No per-session DB handle is threaded here — each session DO - * opens its own D1 handle in its own isolate. - */ -export const makeCloudflareMcpSeams = ( - config: CloudflareConfig, - env: CloudflareEnv, -): CloudflareMcpSeams => ({ - auth: cloudflareAccessMcpAuth(config), - sessions: makeCloudflareMcpSessionStore(env), - reporter: cloudflareMcpReporter, - approvalHandler: makeCloudflareApprovalHandler(config, env), -}); diff --git a/apps/host-cloudflare/src/mcp/session-durable-object.ts b/apps/host-cloudflare/src/mcp/session-durable-object.ts index 26caac4dff..f4ce323fc6 100644 --- a/apps/host-cloudflare/src/mcp/session-durable-object.ts +++ b/apps/host-cloudflare/src/mcp/session-durable-object.ts @@ -4,11 +4,11 @@ import { createExecutorMcpServer } from "@executor-js/host-mcp/tool-server"; import { buildResumeApprovalUrl } from "@executor-js/host-mcp/browser-approval"; import type { ExecutorDbHandle } from "@executor-js/api/server"; import { - McpSessionDOBase, + McpAgentSessionDOBase, type BuiltMcpServer, type McpSessionInit, type SessionMeta, -} from "@executor-js/cloudflare/mcp/durable-object"; +} from "@executor-js/cloudflare/mcp/agent-durable-object"; import { loadConfig, type CloudflareConfig, type CloudflareEnv } from "../config"; import { createD1ExecutorDb } from "../db/d1"; @@ -35,14 +35,14 @@ import { preloadQuickJs } from "../quickjs"; // own lifecycle (the binding is the connection), so `end` is `close` — a no-op. type CfSessionDbHandle = ExecutorDbHandle & { readonly end: () => Promise }; -export class McpSessionDO extends McpSessionDOBase { +export class McpSessionDO extends McpAgentSessionDOBase { private readonly cfEnv: CloudflareEnv; private readonly cfConfig: CloudflareConfig; - // `ctx`'s type is taken from the base constructor so it tracks whichever - // `@cloudflare/workers-types` the shared package resolves (avoids a - // cross-version `DurableObjectState` mismatch at the `super` call). - constructor(ctx: ConstructorParameters[0], env: CloudflareEnv) { + constructor( + ctx: ConstructorParameters>[0], + env: CloudflareEnv, + ) { super(ctx, env); this.cfEnv = env; this.cfConfig = loadConfig(env); @@ -88,6 +88,7 @@ export class McpSessionDO extends McpSessionDOBase { const mcpServer = yield* createExecutorMcpServer({ engine, browserApprovalStore: self.browserApprovalStore, + pausedExecutionHooks: self.pausedExecutionHooks, elicitationMode: elicitationMode === "browser" ? { diff --git a/apps/host-cloudflare/src/mcp/session-store.ts b/apps/host-cloudflare/src/mcp/session-store.ts deleted file mode 100644 index cb4a8b1146..0000000000 --- a/apps/host-cloudflare/src/mcp/session-store.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Layer } from "effect"; - -import { makeConsoleMcpErrorReporter } from "@executor-js/api/server"; -import type { McpErrorReporter, McpSessionStore } from "@executor-js/host-mcp"; -import { - makeDurableObjectMcpSessionStore, - type McpSessionDOStub, -} from "@executor-js/cloudflare/mcp/session-store"; - -import type { CloudflareEnv } from "../config"; -import { ErrorCaptureLive } from "../observability"; - -// --------------------------------------------------------------------------- -// Cloudflare McpSessionStore wiring — the SAME shared Durable-Object dispatcher -// as cloud (@executor-js/cloudflare), over host-cloudflare's `MCP_SESSION` -// namespace. The dispatch/identity/trace/peek logic all lives in the shared -// package; the host supplies ONLY its DO stub accessors (the session id IS the -// DO id, so every follow-up request routes back to the same isolate). -// -// This replaces the in-process store: an in-memory session map is invisible to -// the next Worker isolate, so `tools/list` after `initialize` failed in -// production ("Not connected"). The DO holds the session in one addressable -// isolate, fixing that across the board. -// --------------------------------------------------------------------------- - -// The DO RPC stub structurally satisfies `McpSessionDOStub` (init/handleRequest/ -// clearSession), but `@cloudflare/workers-types` types it as a generic -// `DurableObjectStub`. Narrow at this one boundary via an `unknown` hop — a -// single cast, so no double-cast through the worker-types stub type. -const toSessionStub = (stub: unknown): McpSessionDOStub => stub as McpSessionDOStub; - -/** Build the DO-backed MCP session store over the host's `MCP_SESSION` namespace. */ -export const makeCloudflareMcpSessionStore = (env: CloudflareEnv): Layer.Layer => - makeDurableObjectMcpSessionStore({ - getStub: (sessionId) => - toSessionStub(env.MCP_SESSION.get(env.MCP_SESSION.idFromString(sessionId))), - newStub: () => toSessionStub(env.MCP_SESSION.get(env.MCP_SESSION.newUniqueId())), - // host-cf has no Sentry; a 500-defect surfaces through the reporter seam below. - }); - -/** Route 500-defects through the host's console `ErrorCapture`. */ -export const cloudflareMcpReporter: Layer.Layer = - makeConsoleMcpErrorReporter(ErrorCaptureLive); diff --git a/apps/host-cloudflare/src/worker.e2e.node.test.ts b/apps/host-cloudflare/src/worker.e2e.node.test.ts index fd627e7ccf..6f145fb2b4 100644 --- a/apps/host-cloudflare/src/worker.e2e.node.test.ts +++ b/apps/host-cloudflare/src/worker.e2e.node.test.ts @@ -4,6 +4,7 @@ import { resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { afterAll, beforeAll, describe, expect, it } from "@effect/vitest"; +import { Schema } from "effect"; import { unstable_dev, type Unstable_DevWorker } from "wrangler"; // --------------------------------------------------------------------------- @@ -29,6 +30,28 @@ const SPEC = JSON.stringify({ }, }); +const decodeUnknownJson = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); + +interface JsonReadableResponse { + readonly headers: { get(name: string): string | null }; + json(): Promise; + text(): Promise; +} + +const readMcpJson = async (response: JsonReadableResponse): Promise => { + const contentType = response.headers.get("content-type") ?? ""; + if (contentType.includes("application/json")) { + return (await response.json()) as A; + } + const text = await response.text(); + const data = text + .split("\n") + .find((line) => line.startsWith("data: ")) + ?.slice("data: ".length); + expect(data).toBeTruthy(); + return decodeUnknownJson(data!) as A; +}; + describe("cloudflare host e2e (workerd/miniflare)", () => { let worker: Unstable_DevWorker; @@ -209,13 +232,55 @@ describe("cloudflare host e2e (workerd/miniflare)", () => { method: "tools/list", }); expect(list.status).toBe(200); - const listed = (await list.json()) as { + const listed = await readMcpJson<{ result?: { tools?: ReadonlyArray<{ name: string }> }; - }; + }>(list); const toolNames = listed.result?.tools?.map((t) => t.name) ?? []; expect(toolNames).toContain("execute"); }, 60_000); + it("serves streamable HTTP GET only for initialized sessions", async () => { + const missing = await worker.fetch("/mcp", { + method: "GET", + headers: { accept: "text/event-stream" }, + }); + expect(missing.status).toBe(400); + await missing.text(); + + const init = await worker.fetch("/mcp", { + method: "POST", + headers: { + "content-type": "application/json", + accept: "application/json, text/event-stream", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2025-03-26", + capabilities: {}, + clientInfo: { name: "test", version: "1" }, + }, + }), + }); + expect(init.status).toBe(200); + const sessionId = init.headers.get("mcp-session-id"); + expect(sessionId).toBeTruthy(); + await init.text(); + + const stream = await worker.fetch("/mcp", { + method: "GET", + headers: { + accept: "text/event-stream", + "mcp-session-id": sessionId!, + }, + }); + expect(stream.status).toBe(200); + expect(stream.headers.get("content-type")).toContain("text/event-stream"); + await stream.body?.cancel(); + }, 60_000); + it("invokes the execute tool over MCP (initialize → tools/call → QuickJS)", async () => { const accept = "application/json, text/event-stream"; const rpc = (sessionId: string | null, body: unknown) => @@ -255,9 +320,9 @@ describe("cloudflare host e2e (workerd/miniflare)", () => { params: { name: "execute", arguments: { code: "export default 6 * 7" } }, }); expect(call.status).toBe(200); - const result = (await call.json()) as { + const result = await readMcpJson<{ result?: { structuredContent?: { result?: number } }; - }; + }>(call); expect(result.result?.structuredContent?.result).toBe(42); }, 60_000); }); diff --git a/apps/host-cloudflare/src/worker.ts b/apps/host-cloudflare/src/worker.ts index 228b60a551..2602e9948a 100644 --- a/apps/host-cloudflare/src/worker.ts +++ b/apps/host-cloudflare/src/worker.ts @@ -6,25 +6,33 @@ import type { CloudflareEnv } from "./config"; export { McpSessionDO } from "./mcp"; // --------------------------------------------------------------------------- -// The Worker fetch entry. `ExecutorApp.make`'s `toWebHandler()` produces a -// `(Request) => Promise` — exactly a Worker handler — so the entry is -// thin: build the app ONCE per isolate (memoized; the build runs the D1 schema -// bring-up), then forward every request to its handler. `env` (the D1 binding + -// Access vars) arrives with the request and is captured at build time. +// The Worker fetch entry. Most requests go to `ExecutorApp.make`'s Effect web +// handler. `/mcp` stays at this edge boundary because `McpAgent.serve()` needs +// the Cloudflare `ExecutionContext` to pass authenticated session props into the +// hibernatable Durable Object bridge. // --------------------------------------------------------------------------- -let handlerPromise: Promise<(request: Request) => Promise> | null = null; +let handlerPromise: Promise<{ + readonly app: (request: Request) => Promise; + readonly mcp: (request: Request, env: CloudflareEnv, ctx: ExecutionContext) => Promise; +}> | null = null; -const resolveHandler = (env: CloudflareEnv): Promise<(request: Request) => Promise> => { +const resolveHandler = (env: CloudflareEnv) => { if (!handlerPromise) { - handlerPromise = makeCloudflareApp(env).then(({ toWebHandler }) => toWebHandler().handler); + handlerPromise = makeCloudflareApp(env).then(({ toWebHandler, mcpAgentHandler }) => ({ + app: toWebHandler().handler, + mcp: mcpAgentHandler, + })); } return handlerPromise; }; export default { - fetch: async (request: Request, env: CloudflareEnv): Promise => { + fetch: async (request: Request, env: CloudflareEnv, ctx: ExecutionContext): Promise => { const serve = await resolveHandler(env); - return serve(request); + if (new URL(request.url).pathname === "/mcp") { + return serve.mcp(request, env, ctx); + } + return serve.app(request); }, }; diff --git a/bun.lock b/bun.lock index fe8fc88d3c..6d19fd4da1 100644 --- a/bun.lock +++ b/bun.lock @@ -90,7 +90,6 @@ "@tanstack/react-router": "catalog:", "@tanstack/react-start": "catalog:", "@workos-inc/node": "^8.11.1", - "agents": "^0.10.0", "autumn-js": "^1.2.8", "drizzle-orm": "catalog:", "effect": "catalog:", @@ -650,7 +649,7 @@ "@executor-js/host-mcp": "workspace:*", "@executor-js/sdk": "workspace:*", "@modelcontextprotocol/sdk": "^1.29.0", - "agents": "^0.10.0", + "agents": "^0.16.2", "effect": "catalog:", }, "devDependencies": { @@ -1238,8 +1237,6 @@ "@antfu/ni": ["@antfu/ni@0.23.2", "", { "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nu": "bin/nu.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs" } }, "sha512-FSEVWXvwroExDXUu8qV6Wqp2X3D1nJ0Li4LFymCyvCVrm7I3lNfG0zZWSWvGU1RE7891eTnFTyh31L3igOwNKQ=="], - "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], - "@astrojs/cloudflare": ["@astrojs/cloudflare@13.1.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/underscore-redirects": "1.0.3", "@cloudflare/vite-plugin": "^1.25.6", "piccolore": "^0.1.3", "tinyglobby": "^0.2.15", "vite": "^7.3.1" }, "peerDependencies": { "astro": "^6.0.0", "wrangler": "^4.61.1" } }, "sha512-Ogl8p4MifPMHbpHKJL78eqEL+I3wbHrYmo83P/FkKZQ9VzzKi2A1RjnbaiiPAwrA8xQOqIUo4zlN7+Mse0aJAQ=="], "@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="], @@ -1286,27 +1283,27 @@ "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="], "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow=="], + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="], "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="], "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="], "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], - "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.28.6", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg=="], + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.29.7", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="], - "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="], "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], @@ -1316,11 +1313,11 @@ "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], - "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.29.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-decorators": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA=="], + "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.29.7", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-syntax-decorators": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg=="], - "@babel/plugin-syntax-decorators": ["@babel/plugin-syntax-decorators@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA=="], + "@babel/plugin-syntax-decorators": ["@babel/plugin-syntax-decorators@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg=="], "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], @@ -1366,6 +1363,8 @@ "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], + "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="], + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], @@ -1424,6 +1423,8 @@ "@clack/prompts": ["@clack/prompts@1.3.0", "", { "dependencies": { "@clack/core": "1.3.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw=="], + "@cloudflare/codemode": ["@cloudflare/codemode@0.4.1", "", { "dependencies": { "@types/json-schema": "^7.0.15", "acorn": "^8.17.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.0", "@tanstack/ai": ">=0.8.0 <1.0.0", "ai": "^6.0.0", "zod": "^4.0.0" }, "optionalPeers": ["@modelcontextprotocol/sdk", "@tanstack/ai", "ai", "zod"] }, "sha512-i73cD7GeEzJVxcjqb4yPDCXXq7/h/Le/Vvz/Z3Xxf5qFiiw9G75fd1+KGWvZ/3/Gk6bgL7eUnnwMhByK3b5EUg=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], @@ -1910,8 +1911,6 @@ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], - "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], - "@kitlangton/terminal-control": ["@kitlangton/terminal-control@0.3.0", "", { "optionalDependencies": { "@kitlangton/terminal-control-darwin-arm64": "0.3.0", "@kitlangton/terminal-control-darwin-x64": "0.3.0", "@kitlangton/terminal-control-linux-arm64-gnu": "0.3.0", "@kitlangton/terminal-control-linux-x64-gnu": "0.3.0" }, "peerDependencies": { "vitest": ">=3.0.0" }, "optionalPeers": ["vitest"] }, "sha512-YlOx9ztDInHBETgxWNRd6nq/YzpMlw1YEJ1OIrhdeWTqrQdJHIYThWGiWda9GxmQAThWNULkKR17as+QDgyKdQ=="], "@kitlangton/terminal-control-darwin-arm64": ["@kitlangton/terminal-control-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64", "bin": { "termctrl": "bin/termctrl" } }, "sha512-HydPv5if5pAjwHNbA7TnZAJYKfq9AiZITaJuFyF1HsUMedzin3qzv9WRVBqIiGsQ/FiR1i1kaGhyLzaVpZR35w=="], @@ -1980,8 +1979,12 @@ "@mishieck/ink-titled-box": ["@mishieck/ink-titled-box@0.3.0", "", { "peerDependencies": { "ink": "^6.0.0", "react": "^19.1.0", "typescript": "^5" } }, "sha512-ugzVH9hixp3hwKfQ8On/qnsrdAxS3y9rTu/aGOFed4zVUvtZyGZNIR4rxAwXult8HKI4vJEh0OM8wib9NPrwUg=="], + "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@mongodb-js/zstd": ["@mongodb-js/zstd@7.0.0", "", { "dependencies": { "node-addon-api": "^8.5.0", "prebuild-install": "^7.1.3" } }, "sha512-mQ2s0pYYiav+tzCDR05Zptem8Ey2v8s11lri5RKGhTtL4COVCvVCk5vtyRYNT+9L8qSfyOqqefF9UtnW8mC5jA=="], + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], @@ -2028,6 +2031,8 @@ "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="], + "@nodable/entities": ["@nodable/entities@2.2.0", "", {}, "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -2820,6 +2825,8 @@ "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.162.0", "", {}, "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA=="], + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], "@turbo/darwin-64": ["@turbo/darwin-64@2.9.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg=="], @@ -2940,8 +2947,6 @@ "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], - "@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="], - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], @@ -3064,7 +3069,7 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "agents": ["agents@0.10.2", "", { "dependencies": { "@babel/plugin-proposal-decorators": "^7.29.0", "@cfworker/json-schema": "^4.1.1", "@modelcontextprotocol/sdk": "1.29.0", "@rolldown/plugin-babel": "^0.2.3", "cron-schedule": "^6.0.0", "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "mimetext": "^3.0.28", "nanoid": "^5.1.7", "partyserver": "^0.4.1", "partysocket": "1.1.16", "picomatch": "^4.0.4", "yargs": "^18.0.0" }, "peerDependencies": { "@ai-sdk/openai": "^3.0.0", "@ai-sdk/react": "^3.0.0", "@cloudflare/ai-chat": "^0.4.2", "@cloudflare/codemode": "^0.3.4", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "react": "^19.0.0", "viem": ">=2.0.0", "vite": ">=6.0.0 <9.0.0", "zod": "^4.0.0" }, "optionalPeers": ["@ai-sdk/openai", "@ai-sdk/react", "@cloudflare/ai-chat", "@cloudflare/codemode", "@x402/core", "@x402/evm", "viem", "vite"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-zltFKKENDClZgIJB7aRneI8tRpnCbMbP4RGPh4t8OgqKHomxUNZJEqMrjcgwP/0FX1W9GGrrLJVo9Ez2ibHVvA=="], + "agents": ["agents@0.16.2", "", { "dependencies": { "@babel/plugin-proposal-decorators": "^7.29.7", "@cfworker/json-schema": "^4.1.1", "@cloudflare/codemode": "^0.4.1", "@modelcontextprotocol/sdk": "1.29.0", "@rolldown/plugin-babel": "^0.2.3", "cron-schedule": "^6.0.0", "esbuild": "^0.28.1", "just-bash": "^3.0.1", "mimetext": "^3.0.28", "nanoid": "^5.1.11", "partyserver": "^0.5.8", "partysocket": "1.2.0", "yaml": "^2.9.0", "yargs": "^18.0.0" }, "peerDependencies": { "@cloudflare/ai-chat": ">=0.8.5 <1.0.0", "@tanstack/ai": ">=0.10.2 <1.0.0", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "chat": "^4.29.0", "react": "^19.0.0", "vite": ">=6.0.0 <9.0.0", "zod": "^4.0.0" }, "optionalPeers": ["@cloudflare/ai-chat", "@tanstack/ai", "@x402/core", "@x402/evm", "chat", "vite"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-pSWRw4l5j14cGsCPLDhXGjEfv4RX/kUHdgYsqCohd3AfutPNUEyGhbqfNzg8HklfQdZLpjJj+X7vsRAect5/4Q=="], "ahooks": ["ahooks@3.9.7", "", { "dependencies": { "@babel/runtime": "^7.21.0", "@types/js-cookie": "^3.0.6", "dayjs": "^1.9.1", "intersection-observer": "^0.12.0", "js-cookie": "^3.0.5", "lodash": "^4.17.21", "react-fast-compare": "^3.2.2", "resize-observer-polyfill": "^1.5.1", "screenfull": "^5.0.0", "tslib": "^2.4.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-S0lvzhbdlhK36RFBkGv+RbOM/dbbweym+BIHM/bwwuWVSVN5TuVErHPMWo4w0t1NDYg5KPp2iEf7Y7E5LASYiw=="], @@ -3096,6 +3101,8 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "anynum": ["anynum@1.0.1", "", {}, "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A=="], + "app-builder-bin": ["app-builder-bin@5.0.0-alpha.12", "", {}, "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w=="], "app-builder-lib": ["app-builder-lib@26.8.1", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.3", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.8.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.8.1", "electron-builder-squirrel-windows": "26.8.1" } }, "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw=="], @@ -3738,6 +3745,10 @@ "fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="], + "fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="], + + "fast-xml-parser": ["fast-xml-parser@5.9.3", "", { "dependencies": { "@nodable/entities": "^2.2.0", "fast-xml-builder": "^1.2.0", "is-unsafe": "^1.0.1", "path-expression-matcher": "^1.5.0", "strnum": "^2.4.1", "xml-naming": "^0.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g=="], + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], "fd-package-json": ["fd-package-json@2.0.0", "", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="], @@ -3754,7 +3765,7 @@ "file-selector": ["file-selector@0.5.0", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA=="], - "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], + "file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="], "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], @@ -4054,6 +4065,8 @@ "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + "is-unsafe": ["is-unsafe@1.0.1", "", {}, "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA=="], + "is-wayland": ["is-wayland@0.1.0", "", {}, "sha512-QkbMsWkIfkrzOPxenwye0h56iAXirZYHG9eHVPb22fO9y+wPbaX/CHacOWBa/I++4ohTcByimhM1/nyCsH8KNA=="], "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], @@ -4102,8 +4115,6 @@ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], - "json-schema-to-typescript": ["json-schema-to-typescript@15.0.4", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.5.5", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.17.7", "is-glob": "^4.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "minimist": "^1.2.8", "prettier": "^3.2.5", "tinyglobby": "^0.2.9" }, "bin": { "json2ts": "dist/src/cli.js" } }, "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ=="], - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], @@ -4120,6 +4131,8 @@ "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], + "just-bash": ["just-bash@3.0.2", "", { "dependencies": { "diff": "^8.0.2", "fast-xml-parser": "^5.7.3", "file-type": "^21.2.0", "ini": "^6.0.0", "minimatch": "^10.1.1", "modern-tar": "^0.7.3", "papaparse": "^5.5.3", "quickjs-emscripten": "^0.32.0", "re2js": "^1.2.1", "seek-bzip": "^2.0.0", "smol-toml": "^1.6.0", "sprintf-js": "^1.1.3", "sql.js": "^1.13.0", "turndown": "^7.2.2", "yaml": "^2.8.2" }, "optionalDependencies": { "@mongodb-js/zstd": "^7.0.0", "node-liblzma": "^2.0.3" }, "bin": { "just-bash": "dist/bin/just-bash.js", "just-bash-shell": "dist/bin/shell/shell.js" } }, "sha512-uY8LMD2NpADp1HlYUcZSw9ffuq0tRhW76sg8xkeo+ZRDMu7mbekWbKbYDWquazVVi7pqpGZlic/liMFktQKy6w=="], + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], @@ -4416,6 +4429,8 @@ "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="], + "modern-tar": ["modern-tar@0.7.6", "", {}, "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg=="], + "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="], @@ -4442,7 +4457,7 @@ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nanoid": ["nanoid@5.1.9", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ZUvP7KeBLe3OZ1ypw6dI/TzYJuvHP77IM4Ry73waSQTLn8/g8rpdjfyVAh7t1/+FjBtG4lCP42MEbDxOsRpBMw=="], + "nanoid": ["nanoid@5.1.15", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kBg3RpGtIe+RpTbyXwoI6pk5yD7KUiI3sygUqgeBMRst42KmhB4RZC7eiO9Wa1HIpaCCtpE2DJ6OI4Wi5ebwFw=="], "nanostores": ["nanostores@1.3.0", "", {}, "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA=="], @@ -4456,7 +4471,7 @@ "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], - "node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], + "node-addon-api": ["node-addon-api@8.8.0", "", {}, "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA=="], "node-api-version": ["node-api-version@0.2.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q=="], @@ -4468,8 +4483,12 @@ "node-gyp": ["node-gyp@12.3.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg=="], + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], + "node-liblzma": ["node-liblzma@2.2.0", "", { "dependencies": { "node-addon-api": "^8.5.0", "node-gyp-build": "^4.8.4" }, "bin": { "nxz": "lib/cli/nxz.js" } }, "sha512-s0KzNOWwOJJgPG6wxg6cKohnAl9Wk/oW1KrQaVzJBjQwVcUGPQCzpR46Ximygjqj/3KhOrtJXnYMp/xYAXp75g=="], + "node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="], "node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="], @@ -4554,6 +4573,8 @@ "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + "papaparse": ["papaparse@5.5.4", "", {}, "sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ=="], + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="], @@ -4578,9 +4599,9 @@ "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - "partyserver": ["partyserver@0.4.1", "", { "dependencies": { "nanoid": "^5.1.6" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240729.0" } }, "sha512-StSs0oY8RmTxjGNil7VbCG4gnTN+4rYX20fiUIItAxTPpr/5rPDZT6PIvMROkk9M1Gn7GzE1wuQXwhxceaGhXA=="], + "partyserver": ["partyserver@0.5.8", "", { "dependencies": { "nanoid": "^5.1.9" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260424.1" } }, "sha512-htgSwiBcBu9zIYLrsxBAOvdkjukHvncbTk0nDrJgfruvZ08rxtEN1Ab4T7j9osykP80Bq3zA2oWFd3ngc4Z9uw=="], - "partysocket": ["partysocket@1.1.16", "", { "dependencies": { "event-target-polyfill": "^0.0.4" }, "peerDependencies": { "react": ">=17" }, "optionalPeers": ["react"] }, "sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w=="], + "partysocket": ["partysocket@1.2.0", "", { "dependencies": { "event-target-polyfill": "^0.0.4" }, "peerDependencies": { "react": ">=17" }, "optionalPeers": ["react"] }, "sha512-xgXql4N0b3umN263lHkrZMvvtYC906h4YjY8l63LNcF0x1bfJmQtZLQGqNQWzFHAxLns/6h6/rjdLW4EdYqQwA=="], "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], @@ -4588,6 +4609,8 @@ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-expression-matcher": ["path-expression-matcher@1.6.1", "", {}, "sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ=="], + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -4768,6 +4791,8 @@ "re-resizable": ["re-resizable@6.11.2", "", { "peerDependencies": { "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A=="], + "re2js": ["re2js@1.4.0", "", {}, "sha512-KTOIcZTSOpOxbu3i0+T6mFQ6tkxXKlTxfcMFs1trQbsMnG84qNq+DjXr8Afu+FEFjvF1NNlldpC7roPyazFI8g=="], + "react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="], "react-avatar-editor": ["react-avatar-editor@15.1.0", "", { "peerDependencies": { "react": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Zto7u9l6Wd5LPPtjeFJ+7uwoT4bs01OSgkN2kxD18lWl8IiZ0GY3nWCbKPx4qIU7Au1vENsMJm19rfVWHHayaQ=="], @@ -4982,6 +5007,8 @@ "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + "seek-bzip": ["seek-bzip@2.0.0", "", { "dependencies": { "commander": "^6.0.0" }, "bin": { "seek-bunzip": "bin/seek-bunzip", "seek-table": "bin/seek-bzip-table" } }, "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], @@ -5074,6 +5101,8 @@ "sql-highlight": ["sql-highlight@6.1.0", "", {}, "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA=="], + "sql.js": ["sql.js@1.14.1", "", {}, "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A=="], + "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], @@ -5114,7 +5143,9 @@ "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], - "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], + "strnum": ["strnum@2.4.1", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg=="], + + "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="], "stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="], @@ -5214,7 +5245,7 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], + "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=="], "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], @@ -5246,6 +5277,8 @@ "turbo": ["turbo@2.9.6", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.6", "@turbo/darwin-arm64": "2.9.6", "@turbo/linux-64": "2.9.6", "@turbo/linux-arm64": "2.9.6", "@turbo/windows-64": "2.9.6", "@turbo/windows-arm64": "2.9.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg=="], + "turndown": ["turndown@7.2.4", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], @@ -5416,6 +5449,8 @@ "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], + "xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="], + "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="], "xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="], @@ -5476,20 +5511,52 @@ "@azure/msal-node/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "@babel/core/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@babel/generator/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + + "@babel/helper-annotate-as-pure/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@babel/helper-create-class-features-plugin/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@babel/helper-member-expression-to-functions/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/helper-member-expression-to-functions/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/helper-optimise-call-expression/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/helper-replace-supers/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/parser/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/plugin-proposal-decorators/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + + "@babel/plugin-syntax-decorators/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + + "@babel/template/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + + "@babel/traverse/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@better-auth/core/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], "@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "@cloudflare/codemode/acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg=="], "@cloudflare/vite-plugin/miniflare": ["miniflare@4.20260415.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", "workerd": "1.20260415.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-JoExRWN4YBI2luA5BoSMFEgi8rQWXUGzo3mtE+58VXCLV3jj/Xnk5Yeqs/IXWz8Es5GJIaq6BtsixDvAxXSIng=="], @@ -5594,6 +5661,8 @@ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + "@jimp/core/file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="], + "@jimp/core/mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], "@jimp/plugin-blit/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -5914,6 +5983,8 @@ "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], "@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="], @@ -5922,6 +5993,10 @@ "@tanstack/start-plugin-core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@types/babel__core/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + + "@types/babel__template/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@types/better-sqlite3/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/cacheable-request/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], @@ -5946,6 +6021,10 @@ "@types/yauzl/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "agents/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "agents/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + "agents/yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], "ai/@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -5978,6 +6057,8 @@ "atmn/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "babel-dead-code-elimination/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "better-auth/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], "better-call/rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], @@ -6080,6 +6161,8 @@ "iconv-corefoundation/cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], + "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], + "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="], "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], @@ -6096,12 +6179,18 @@ "ink-confirm-input/ink-text-input": ["ink-text-input@3.3.0", "", { "dependencies": { "chalk": "^3.0.0", "prop-types": "^15.5.10" }, "peerDependencies": { "ink": "^2.0.0", "react": "^16.5.2" } }, "sha512-gO4wrOf2ie3YuEARTIwGlw37lMjFn3Gk6CKIDrMlHb46WFMagZU7DplohjM24zynlqfnXA5UDEIfC2NBcvD8kg=="], + "just-bash/quickjs-emscripten": ["quickjs-emscripten@0.32.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-release-sync": "0.32.0", "quickjs-emscripten-core": "0.32.0" } }, "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA=="], + + "just-bash/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "knip/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], "libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], + "magicast/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "matcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], @@ -6194,6 +6283,8 @@ "run-jxa/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + "seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], + "serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], "set-value/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], @@ -6258,8 +6349,64 @@ "@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], + "@babel/helper-annotate-as-pure/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-annotate-as-pure/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-member-expression-to-functions/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-member-expression-to-functions/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-optimise-call-expression/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-optimise-call-expression/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/parser/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/parser/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@cloudflare/vite-plugin/miniflare/undici": ["undici@7.24.8", "", {}, "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ=="], "@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20260415.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260415.1", "@cloudflare/workerd-darwin-arm64": "1.20260415.1", "@cloudflare/workerd-linux-64": "1.20260415.1", "@cloudflare/workerd-linux-arm64": "1.20260415.1", "@cloudflare/workerd-windows-64": "1.20260415.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-phyPjRnx+mQDfkhN9ENPioL1L0SdhYs4S0YmJK/xF9Oga+ykNfdSy1MHnsOj8yqnOV96zcVQMx32dJ0r3pq0jQ=="], @@ -6460,6 +6607,10 @@ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "@jimp/core/file-type/strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], + + "@jimp/core/file-type/token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="], + "@libsql/kysely-libsql/@libsql/client/@libsql/core": ["@libsql/core@0.8.1", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-u6nrj6HZMTPsgJ9EBhLzO2uhqhlHQJQmVHV+0yFLvfGf3oSP8w7TjZCNUgu1G8jHISx6KFi7bmcrdXW9lRt++A=="], "@libsql/kysely-libsql/@libsql/client/@libsql/hrana-client": ["@libsql/hrana-client@0.6.2", "", { "dependencies": { "@libsql/isomorphic-fetch": "^0.2.1", "@libsql/isomorphic-ws": "^0.1.5", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, "sha512-MWxgD7mXLNf9FXXiM0bc90wCjZSpErWKr5mGza7ERy2FJNNMXd7JIOv+DepBA1FQTIfI8TFO4/QDYgaQC0goNw=="], @@ -6544,6 +6695,8 @@ "@tanstack/router-generator/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="], + "@tanstack/router-generator/@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@tanstack/start-plugin-core/@tanstack/router-generator/@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="], "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], @@ -6570,6 +6723,58 @@ "@types/yauzl/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "agents/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "agents/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "agents/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "agents/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "agents/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "agents/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "agents/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "agents/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "agents/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "agents/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "agents/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "agents/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "agents/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "agents/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "agents/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "agents/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "agents/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "agents/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "agents/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "agents/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "agents/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "agents/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "agents/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "agents/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "agents/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "agents/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + "agents/yargs/cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], "agents/yargs/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -6770,6 +6975,16 @@ "ink-confirm-input/ink-text-input/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg=="], + + "just-bash/quickjs-emscripten/quickjs-emscripten-core": ["quickjs-emscripten-core@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg=="], + "mimetext/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260424.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-yFR1XaJbSDLg/qbwtrYaU2xwFXatIPKR5nrMQCN1q/m6+Qe/j6r+kCnFEvOJjMZOm9iCKsE6Qly5clgl4u32qw=="], @@ -6890,6 +7105,22 @@ "yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-replace-supers/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260415.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-dsxaKsQm3LnPGNPEdsRv09QN3Y4DqCw7kX5j6noKqbAtro2jTr95sVlYM1jUxZ5FkOl1f7SXgaKKB9t5H5Nkbg=="], "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260415.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+JgSgVA49KyKteHRA1SnonE4Zn5Ei5zdAp5FQMxFmXI8qulZw4Hl7safXxRyK4i9sTO8gl7TFOKO5Q64VPvSDQ=="], @@ -7048,6 +7279,16 @@ "ink-confirm-input/ink-text-input/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-asyncify/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-sync/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-asyncify/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-sync/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/quickjs-emscripten-core/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + "ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "posthog-js/@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], diff --git a/packages/core/execution/src/engine.test.ts b/packages/core/execution/src/engine.test.ts index 89f13e367b..593cf6b2b9 100644 --- a/packages/core/execution/src/engine.test.ts +++ b/packages/core/execution/src/engine.test.ts @@ -85,3 +85,18 @@ describe("executeWithPause failure propagation", () => { }), ); }); + +describe("pausedExecutionCount", () => { + it.effect("starts at zero", () => + Effect.gen(function* () { + const executor = yield* makeExecutor(); + const engine = createExecutionEngine({ + executor, + codeExecutor: succeedingExecutor, + }); + + expect(yield* engine.pausedExecutionCount()).toBe(0); + expect(yield* engine.hasPausedExecutions()).toBe(false); + }), + ); +}); diff --git a/packages/core/execution/src/engine.ts b/packages/core/execution/src/engine.ts index 6655488528..6af3754caf 100644 --- a/packages/core/execution/src/engine.ts +++ b/packages/core/execution/src/engine.ts @@ -388,6 +388,12 @@ export type ExecutionEngine */ readonly getPausedExecution: (executionId: string) => Effect.Effect; + /** Count of executions currently paused awaiting resume. */ + readonly pausedExecutionCount: () => Effect.Effect; + + /** Whether any executions are paused awaiting resume. */ + readonly hasPausedExecutions: () => Effect.Effect; + /** * Get the dynamic tool description (workflow + namespaces). */ @@ -608,6 +614,8 @@ export const createExecutionEngine = Effect.sync(() => pausedExecutions.get(executionId) ?? null), + pausedExecutionCount: () => Effect.sync(() => pausedExecutions.size), + hasPausedExecutions: () => Effect.sync(() => pausedExecutions.size > 0), getDescription: buildExecuteDescription(executor), }; }; diff --git a/packages/core/execution/src/promise.ts b/packages/core/execution/src/promise.ts index cf57304a59..87878b95af 100644 --- a/packages/core/execution/src/promise.ts +++ b/packages/core/execution/src/promise.ts @@ -47,6 +47,8 @@ export type ExecutionEngine = { response: ResumeResponse, ) => Promise; readonly getPausedExecution: (executionId: string) => Promise; + readonly pausedExecutionCount: () => Promise; + readonly hasPausedExecutions: () => Promise; readonly getDescription: () => Promise; }; @@ -152,6 +154,8 @@ export const toPromiseExecutionEngine = ( executeWithPause: (code) => Effect.runPromise(engine.executeWithPause(code)), resume: (executionId, response) => Effect.runPromise(engine.resume(executionId, response)), getPausedExecution: (executionId) => Effect.runPromise(engine.getPausedExecution(executionId)), + pausedExecutionCount: () => Effect.runPromise(engine.pausedExecutionCount()), + hasPausedExecutions: () => Effect.runPromise(engine.hasPausedExecutions()), getDescription: () => Effect.runPromise(engine.getDescription), }); diff --git a/packages/hosts/cloudflare/package.json b/packages/hosts/cloudflare/package.json index e6b03ef592..4d210ccad4 100644 --- a/packages/hosts/cloudflare/package.json +++ b/packages/hosts/cloudflare/package.json @@ -28,6 +28,10 @@ "types": "./src/mcp/session-durable-object.ts", "default": "./src/mcp/session-durable-object.ts" }, + "./mcp/agent-durable-object": { + "types": "./src/mcp/agent-session-durable-object.ts", + "default": "./src/mcp/agent-session-durable-object.ts" + }, "./mcp/request-id-queue": { "types": "./src/mcp/request-id-queue.ts", "default": "./src/mcp/request-id-queue.ts" @@ -44,7 +48,7 @@ "@executor-js/host-mcp": "workspace:*", "@executor-js/sdk": "workspace:*", "@modelcontextprotocol/sdk": "^1.29.0", - "agents": "^0.10.0", + "agents": "^0.16.2", "effect": "catalog:" }, "devDependencies": { diff --git a/packages/hosts/cloudflare/src/mcp/agent-session-durable-object.ts b/packages/hosts/cloudflare/src/mcp/agent-session-durable-object.ts new file mode 100644 index 0000000000..b5c3d2f598 --- /dev/null +++ b/packages/hosts/cloudflare/src/mcp/agent-session-durable-object.ts @@ -0,0 +1,781 @@ +import { Cause, Deferred, Effect, Exit, Option, Schema } from "effect"; +import type * as Tracer from "effect/Tracer"; +import type { Connection, ConnectionContext } from "agents"; +import { McpAgent } from "agents/mcp"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +import { RequestOrgSlug, RequestWebOrigin } from "@executor-js/api/server"; +import { + formatPausedExecution, + type ExecutionEngine, + type ResumeResponse, +} from "@executor-js/execution"; +import { + PAUSED_APPROVAL_TIMEOUT_MS, + type PausedExecutionHooks, +} from "@executor-js/host-mcp/tool-server"; + +import type { IncomingPropagationHeaders, McpElicitationMode } from "./do-headers"; +import { + SESSION_TIMEOUT_MS, + decideSessionAlarm, + pausedLeaseExtensionLog, +} from "./session-alarm-policy"; + +export type IncomingTraceHeaders = IncomingPropagationHeaders; + +export interface McpSessionInit { + readonly organizationId: string; + readonly userId: string; + readonly elicitationMode: McpElicitationMode; + readonly webOrigin?: string; +} + +export interface McpSessionProps extends Record { + readonly session: McpSessionInit; + readonly propagation?: IncomingTraceHeaders; +} + +export type McpApprovalOwner = { + readonly accountId: string; + readonly organizationId: string; +}; + +type McpSessionApprovalErrorResult = + | { readonly status: "not_found" } + | { readonly status: "forbidden" }; + +type PendingApprovalLease = { + readonly disposeKeepAlive: () => void; + timeout: ReturnType | null; + expiring: boolean; +}; + +export type McpSessionApprovalResult = + | { + readonly status: "ok"; + readonly text: string; + readonly structured: Record; + } + | McpSessionApprovalErrorResult; + +export type McpSessionResumeApprovalResult = + | { + readonly status: "ok"; + readonly executionStatus: "completed" | "paused"; + readonly text: string; + readonly structured: Record; + readonly isError?: boolean; + } + | McpSessionApprovalErrorResult; + +export interface SessionDbHandle { + readonly end: () => Promise | void; +} + +export interface SessionMeta { + readonly organizationId: string; + readonly organizationName: string; + /** The org's URL slug, when the host's `resolveSessionMeta` carried one. + * Pins browser-handoff URLs to the right org's console. */ + readonly organizationSlug?: string; + readonly userId: string; + readonly elicitationMode?: "browser" | "model" | "native"; + readonly webOrigin?: string; +} + +export interface BuiltMcpServer { + readonly mcpServer: McpServer; + readonly engine: ExecutionEngine; +} + +export interface BrowserApprovalStore { + readonly takeResponse: (executionId: string) => Effect.Effect; + readonly waitForResponse: (executionId: string) => Effect.Effect; +} + +const SESSION_META_KEY = "session-meta"; +const LAST_ACTIVITY_KEY = "last-activity-ms"; +const PARTYSERVER_NAME_KEY = "__ps_name"; +const MCP_HTTP_METHOD_HEADER = "cf-mcp-method"; +const MCP_MESSAGE_HEADER = "cf-mcp-message"; +const ACTIVE_POST_RESPONSE_WAIT_POLL_MS = 25; +const ACTIVE_POST_RESPONSE_WAIT_MAX_MS = PAUSED_APPROVAL_TIMEOUT_MS + 30_000; +const approvalResponseKey = (executionId: string) => `approval-response:${executionId}`; + +type JsonRpcRequestId = string | number; +const JsonRpcRequestWithId = Schema.Struct({ + id: Schema.Union([Schema.String, Schema.Number]), + method: Schema.String, +}); +const JsonRpcPostPayload = Schema.fromJsonString(Schema.Unknown); +const decodeJsonRpcPostPayload = Schema.decodeUnknownOption(JsonRpcPostPayload); +const decodeJsonRpcRequestWithId = Schema.decodeUnknownOption(JsonRpcRequestWithId); + +const resumeApprovalResult = ( + executionId: string, + response: ResumeResponse, +): Extract => { + const textByAction = { + accept: "I've approved it", + decline: "I've denied it", + cancel: "I've canceled it", + } satisfies Record; + const statusByAction = { + accept: "approved", + decline: "denied", + cancel: "canceled", + } satisfies Record; + + return { + status: "ok", + executionStatus: "completed", + text: textByAction[response.action], + structured: { status: statusByAction[response.action], executionId }, + isError: false, + }; +}; + +const isSessionProps = (props: unknown): props is McpSessionProps => + typeof props === "object" && + props !== null && + "session" in props && + typeof (props as { readonly session?: unknown }).session === "object" && + (props as { readonly session?: unknown }).session !== null; + +const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +const readActivePostRequestIds = (request: Request): readonly JsonRpcRequestId[] => { + if (request.headers.get(MCP_HTTP_METHOD_HEADER) !== "POST") return []; + const encoded = request.headers.get(MCP_MESSAGE_HEADER); + if (!encoded) return []; + const decoded = Effect.runSyncExit( + Effect.try({ + try: () => atob(encoded), + catch: () => "invalid_base64" as const, + }), + ); + if (Exit.isFailure(decoded)) { + console.warn( + JSON.stringify({ + event: "mcp_active_post_response_wait_parse_failed", + reason: "invalid_base64", + }), + ); + return []; + } + const parsed = decodeJsonRpcPostPayload(decoded.value); + if (Option.isNone(parsed)) { + console.warn( + JSON.stringify({ + event: "mcp_active_post_response_wait_parse_failed", + reason: "invalid_json", + }), + ); + return []; + } + const messages = Array.isArray(parsed.value) ? parsed.value : [parsed.value]; + const requestIds: JsonRpcRequestId[] = []; + for (const message of messages) { + const decoded = decodeJsonRpcRequestWithId(message); + if (Option.isSome(decoded)) requestIds.push(decoded.value.id); + } + return requestIds; +}; + +export abstract class McpAgentSessionDOBase< + Env extends Cloudflare.Env = Cloudflare.Env, + TDbHandle extends SessionDbHandle = SessionDbHandle, +> extends McpAgent { + server!: McpServer; + private engine: ExecutionEngine | null = null; + private dbHandle: TDbHandle | null = null; + private sessionMeta: SessionMeta | null = null; + private initialized = false; + private lastActivityMs = 0; + private approvalResponses = new Map(); + private approvalWaiters = new Map>(); + private pendingApprovalLeases = new Map(); + + protected abstract openSessionDb(): TDbHandle | Promise; + + protected abstract resolveSessionMeta(token: McpSessionInit): Effect.Effect; + + protected abstract buildMcpServer( + sessionMeta: SessionMeta, + dbHandle: TDbHandle, + ): Effect.Effect; + + protected withTelemetry( + effect: Effect.Effect, + _incoming?: IncomingTraceHeaders, + ): Effect.Effect { + return effect; + } + + protected captureCause(_cause: Cause.Cause): void {} + + protected flushTelemetry(): Promise { + return Promise.resolve(); + } + + protected get sessionId(): string { + return this.getSessionId(); + } + + protected currentParentSpan(): Tracer.AnySpan | undefined { + return undefined; + } + + protected readonly browserApprovalStore: BrowserApprovalStore = { + takeResponse: (executionId) => this.takeApprovalResponse(executionId), + waitForResponse: (executionId) => this.waitForApprovalResponse(executionId), + }; + + protected readonly pausedExecutionHooks: PausedExecutionHooks = { + onExecutionPaused: (executionId) => + Effect.sync(() => { + this.queuePendingApprovalLeaseStart(executionId); + }), + onResumeStarted: (executionId) => this.beginPendingApprovalResume(executionId), + onResumeSettled: (executionId) => this.finishPendingApprovalResume(executionId), + }; + + override async onConnect(conn: Connection, context: ConnectionContext): Promise { + const requestIds = readActivePostRequestIds(context.request); + if (requestIds.length === 0) { + await super.onConnect(conn, context); + return; + } + + await this.keepAliveWhile(async () => { + await super.onConnect(conn, context); + await this.waitForActivePostResponseDrain(conn.id); + }); + } + + private openSessionDbHandle(): Effect.Effect { + return Effect.promise(() => Promise.resolve(this.openSessionDb())); + } + + private async waitForActivePostResponseDrain(streamId: string): Promise { + const startedAt = Date.now(); + for (;;) { + const requestIds = await this.getStreamRequestIds(streamId); + if (!requestIds || requestIds.length === 0) return; + if (Date.now() - startedAt >= ACTIVE_POST_RESPONSE_WAIT_MAX_MS) { + console.warn( + JSON.stringify({ + event: "mcp_active_post_response_wait_timeout", + sessionId: this.sessionId, + streamId, + requestIds, + elapsedMs: Date.now() - startedAt, + }), + ); + return; + } + await sleep(ACTIVE_POST_RESPONSE_WAIT_POLL_MS); + } + } + + private loadSessionMeta(): Effect.Effect { + return Effect.promise(async () => { + if (this.sessionMeta) return this.sessionMeta; + const stored = await this.ctx.storage.get(SESSION_META_KEY); + this.sessionMeta = stored ?? null; + return this.sessionMeta; + }).pipe(Effect.withSpan("mcp.session.load_meta")); + } + + private async saveSessionMeta(sessionMeta: SessionMeta): Promise { + this.sessionMeta = sessionMeta; + await this.ctx.storage.put(SESSION_META_KEY, sessionMeta); + } + + private async markActivity(now = Date.now()): Promise { + this.lastActivityMs = now; + await Promise.all([ + this.ctx.storage.put(LAST_ACTIVITY_KEY, now), + this.ctx.storage.setAlarm(now + SESSION_TIMEOUT_MS), + ]); + } + + private async loadLastActivity(): Promise { + if (this.lastActivityMs > 0) return this.lastActivityMs; + const stored = await this.ctx.storage.get(LAST_ACTIVITY_KEY); + this.lastActivityMs = stored ?? 0; + return this.lastActivityMs; + } + + private async hasPartyServerName(): Promise { + if (this.ctx.id.name) return true; + const stored = await this.ctx.storage.get(PARTYSERVER_NAME_KEY); + return !!stored; + } + + private async cleanupUnaddressableSessionAlarm(): Promise { + await Effect.runPromise(this.closeRuntime()); + await Effect.runPromise( + Effect.all([ + Effect.ignore(Effect.tryPromise(() => this.ctx.storage.deleteAlarm())), + Effect.ignore(Effect.tryPromise(() => this.ctx.storage.delete(LAST_ACTIVITY_KEY))), + ]), + ); + } + + private resolveAndStoreSessionMeta(token: McpSessionInit) { + const self = this; + return Effect.gen(function* () { + const resolved = yield* self.resolveSessionMeta(token); + const sessionMeta: SessionMeta = { + ...resolved, + ...(token.webOrigin ? { webOrigin: token.webOrigin } : {}), + }; + yield* Effect.promise(() => self.saveSessionMeta(sessionMeta)).pipe( + Effect.withSpan("mcp.session.save_meta"), + ); + return sessionMeta; + }).pipe(Effect.withSpan("mcp.session.resolve_and_store_meta")); + } + + private recordCauseOnSpan(cause: Cause.Cause): Effect.Effect { + const errors = Cause.prettyErrors(cause); + if (errors.length === 0) return Effect.void; + const first = errors[0]; + return Effect.annotateCurrentSpan({ + "exception.type": first?.name ?? "Error", + "exception.message": first?.message ?? "unknown", + "exception.stacktrace": Cause.pretty(cause), + }); + } + + private withSpanFlush(effect: Effect.Effect): Effect.Effect { + const self = this; + return effect.pipe(Effect.ensuring(Effect.promise(() => self.flushTelemetry()))); + } + + private buildRuntime(sessionMeta: SessionMeta, dbHandle: TDbHandle) { + const built = sessionMeta.organizationSlug + ? this.buildMcpServer(sessionMeta, dbHandle).pipe( + Effect.provideService(RequestOrgSlug, { slug: sessionMeta.organizationSlug }), + ) + : this.buildMcpServer(sessionMeta, dbHandle); + return sessionMeta.webOrigin + ? built.pipe(Effect.provideService(RequestWebOrigin, { origin: sessionMeta.webOrigin })) + : built; + } + + private closeRuntime(): Effect.Effect { + const self = this; + return Effect.gen(function* () { + yield* self.releaseAllPendingApprovalLeases(); + if (self.server) { + const server = self.server; + yield* Effect.promise(() => server.close()).pipe(Effect.ignore); + } + self.engine = null; + if (self.dbHandle) { + const dbHandle = self.dbHandle; + self.dbHandle = null; + yield* Effect.promise(() => Promise.resolve(dbHandle.end())).pipe(Effect.ignore); + } + self.initialized = false; + }); + } + + private ensureRuntimeForApproval(): Effect.Effect { + const self = this; + return Effect.gen(function* () { + if (self.initialized && self.engine) return true; + + const sessionMeta = yield* self.loadSessionMeta(); + if (!sessionMeta) return false; + + yield* self.closeRuntime(); + const dbHandle = yield* self.openSessionDbHandle(); + const { mcpServer, engine } = yield* self.buildRuntime(sessionMeta, dbHandle); + self.dbHandle = dbHandle; + self.server = mcpServer; + self.engine = engine; + self.initialized = true; + yield* Effect.promise(() => self.markActivity()).pipe( + Effect.withSpan("McpSessionDO.markActivity"), + ); + return true; + }).pipe(Effect.withSpan("McpSessionDO.ensure_runtime_for_approval")); + } + + async init(): Promise { + if (this.initialized) return; + const props = isSessionProps(this.props) ? this.props : null; + if (!props) { + // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: McpAgent.init is a Promise-only framework hook and props are required before any Effect runtime exists. + throw new Error("MCP session props are required"); + } + const self = this; + const program = Effect.gen(function* () { + const sessionMeta = yield* self.resolveAndStoreSessionMeta(props.session); + const dbHandle = yield* self.openSessionDbHandle(); + const { mcpServer, engine } = yield* self.buildRuntime(sessionMeta, dbHandle); + self.dbHandle = dbHandle; + self.server = mcpServer; + self.engine = engine; + self.initialized = true; + yield* Effect.promise(() => self.markActivity()).pipe( + Effect.withSpan("McpSessionDO.markActivity"), + ); + }).pipe( + Effect.tapCause((cause) => + Effect.gen(function* () { + console.error("[mcp-session] init failed:", Cause.pretty(cause)); + self.captureCause(cause); + yield* self.recordCauseOnSpan(cause); + }), + ), + Effect.catchCause((cause) => + Effect.gen(function* () { + yield* Effect.promise(() => self.cleanup()); + return yield* Effect.failCause(cause); + }), + ), + Effect.withSpan("McpSessionDO.init", { + attributes: { + "mcp.auth.organization_id": props?.session.organizationId ?? "", + }, + }), + ); + const traced = this.withTelemetry(program, props?.propagation); + return Effect.runPromise( + traced.pipe( + // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: Durable Object init method can only reject its Promise + Effect.orDie, + (effect) => self.withSpanFlush(effect), + ), + ); + } + + async validateMcpSessionOwner( + identity: McpApprovalOwner, + ): Promise<"ok" | "not_found" | "forbidden"> { + const self = this; + return Effect.runPromise( + Effect.gen(function* () { + const sessionMeta = yield* self.loadSessionMeta(); + if (!sessionMeta) return "not_found" as const; + yield* Effect.promise(() => self.markActivity()).pipe( + Effect.withSpan("McpSessionDO.markActivity"), + ); + return identity.accountId === sessionMeta.userId && + identity.organizationId === sessionMeta.organizationId + ? ("ok" as const) + : ("forbidden" as const); + }).pipe( + Effect.withSpan("McpSessionDO.validateMcpSessionOwner"), + // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: DO RPC exposes Promise results + Effect.orDie, + ), + ); + } + + async getPausedExecutionForApproval( + executionId: string, + identity: McpApprovalOwner, + incoming?: IncomingTraceHeaders, + ): Promise { + const self = this; + return Effect.runPromise( + Effect.gen(function* () { + const owner = yield* self.validateApprovalIdentity(identity); + if (owner !== "ok") return { status: owner } as const; + + const restored = yield* self.ensureRuntimeForApproval(); + if (!restored || !self.engine) return { status: "not_found" } as const; + + const paused = yield* self.engine.getPausedExecution(executionId); + if (!paused) return { status: "not_found" } as const; + + const formatted = formatPausedExecution(paused); + return { + status: "ok" as const, + text: formatted.text, + structured: formatted.structured, + }; + }).pipe( + Effect.withSpan("McpSessionDO.getPausedExecutionForApproval", { + attributes: { "mcp.execution.id": executionId }, + }), + (eff) => this.withTelemetry(eff, incoming), + // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: DO RPC exposes Promise results + Effect.orDie, + (eff) => self.withSpanFlush(eff), + ), + ); + } + + async resumeExecutionForApproval( + executionId: string, + identity: McpApprovalOwner, + response: ResumeResponse, + incoming?: IncomingTraceHeaders, + ): Promise { + const self = this; + return Effect.runPromise( + Effect.gen(function* () { + const owner = yield* self.validateApprovalIdentity(identity); + if (owner !== "ok") return { status: owner } as const; + + const restored = yield* self.ensureRuntimeForApproval(); + if (!restored || !self.engine) return { status: "not_found" } as const; + + const paused = yield* self.engine.getPausedExecution(executionId); + if (!paused) return { status: "not_found" } as const; + + yield* self.recordApprovalResponse(executionId, response); + return resumeApprovalResult(executionId, response); + }).pipe( + Effect.withSpan("McpSessionDO.resumeExecutionForApproval", { + attributes: { "mcp.execution.id": executionId }, + }), + (eff) => this.withTelemetry(eff, incoming), + // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: DO RPC exposes Promise results + Effect.orDie, + (eff) => self.withSpanFlush(eff), + ), + ); + } + + override async destroy(): Promise { + await this.cleanup(); + await super.destroy(); + } + + private async pausedExecutionCount(): Promise { + if (!this.engine) return 0; + return Effect.runPromise(this.engine.pausedExecutionCount()); + } + + override async alarm(): Promise { + if (!(await this.hasPartyServerName())) { + await this.cleanupUnaddressableSessionAlarm(); + return; + } + const lastActivityMs = await this.loadLastActivity(); + const idleMs = lastActivityMs > 0 ? Date.now() - lastActivityMs : 0; + const pausedExecutionCount = await this.pausedExecutionCount(); + const decision = decideSessionAlarm({ idleMs, pausedExecutionCount }); + + if (decision.kind === "idle_within_timeout") { + await super.alarm(); + return; + } + + if (decision.kind === "extend_paused_lease") { + console.info( + JSON.stringify( + pausedLeaseExtensionLog({ + sessionId: this.sessionId, + pausedExecutionCount, + idleMs, + leaseMs: decision.leaseMs, + }), + ), + ); + await this.ctx.storage.setAlarm(Date.now() + decision.leaseMs); + return; + } + + await this.destroy(); + } + + private validateApprovalIdentity( + identity: McpApprovalOwner, + ): Effect.Effect<"ok" | "not_found" | "forbidden"> { + const self = this; + return Effect.gen(function* () { + const sessionMeta = yield* self.loadSessionMeta(); + if (!sessionMeta) return "not_found" as const; + return identity.accountId === sessionMeta.userId && + identity.organizationId === sessionMeta.organizationId + ? ("ok" as const) + : ("forbidden" as const); + }).pipe(Effect.withSpan("mcp.session.validate_approval_identity")); + } + + private startPendingApprovalLease(executionId: string): Effect.Effect { + const self = this; + return Effect.gen(function* () { + if (self.pendingApprovalLeases.has(executionId)) return; + + yield* Effect.promise(() => self.markActivity()).pipe( + Effect.withSpan("McpSessionDO.markActivity"), + ); + const disposeKeepAlive = yield* Effect.promise(() => self.keepAlive()); + const timeout = setTimeout(() => { + self.queuePendingApprovalLeaseExpiration(executionId); + }, PAUSED_APPROVAL_TIMEOUT_MS); + self.pendingApprovalLeases.set(executionId, { disposeKeepAlive, timeout, expiring: false }); + }).pipe( + Effect.withSpan("McpSessionDO.pending_approval_lease.start", { + attributes: { "mcp.execution.id": executionId }, + }), + Effect.tapCause((cause) => + Effect.sync(() => { + console.error("[mcp-session] pending approval lease start failed:", Cause.pretty(cause)); + self.captureCause(cause); + }), + ), + Effect.ignore, + ); + } + + private queuePendingApprovalLeaseStart(executionId: string): void { + this.ctx.waitUntil(Effect.runPromise(this.startPendingApprovalLease(executionId))); + } + + private queuePendingApprovalLeaseExpiration(executionId: string): void { + this.ctx.waitUntil( + Effect.runPromise( + this.expirePendingApproval(executionId).pipe( + Effect.tapCause((cause) => + Effect.sync(() => { + console.error( + "[mcp-session] pending approval lease expiration failed:", + Cause.pretty(cause), + ); + this.captureCause(cause); + }), + ), + Effect.ignore, + ), + ), + ); + } + + private beginPendingApprovalResume(executionId: string): Effect.Effect { + return Effect.sync(() => { + const lease = this.pendingApprovalLeases.get(executionId); + if (!lease || lease.expiring) return; + if (lease.timeout) clearTimeout(lease.timeout); + lease.timeout = null; + }).pipe( + Effect.withSpan("McpSessionDO.pending_approval_lease.begin_resume", { + attributes: { "mcp.execution.id": executionId }, + }), + ); + } + + private finishPendingApprovalResume(executionId: string): Effect.Effect { + return this.releasePendingApprovalLease(executionId).pipe( + Effect.withSpan("McpSessionDO.pending_approval_lease.finish", { + attributes: { "mcp.execution.id": executionId }, + }), + ); + } + + private releasePendingApprovalLease(executionId: string): Effect.Effect { + return Effect.sync(() => { + const lease = this.pendingApprovalLeases.get(executionId); + if (!lease) return; + if (lease.timeout) clearTimeout(lease.timeout); + this.pendingApprovalLeases.delete(executionId); + lease.disposeKeepAlive(); + }); + } + + private releaseAllPendingApprovalLeases(): Effect.Effect { + return Effect.sync(() => { + for (const executionId of this.pendingApprovalLeases.keys()) { + const lease = this.pendingApprovalLeases.get(executionId); + if (!lease) continue; + if (lease.timeout) clearTimeout(lease.timeout); + lease.disposeKeepAlive(); + } + this.pendingApprovalLeases.clear(); + }); + } + + private recordApprovalResponse( + executionId: string, + response: ResumeResponse, + ): Effect.Effect { + const self = this; + return Effect.gen(function* () { + self.approvalResponses.set(executionId, response); + yield* Effect.promise(() => self.ctx.storage.put(approvalResponseKey(executionId), response)); + const waiter = self.approvalWaiters.get(executionId); + if (waiter) yield* Deferred.succeed(waiter, response); + }); + } + + private expirePendingApproval(executionId: string): Effect.Effect { + const self = this; + return Effect.gen(function* () { + const lease = self.pendingApprovalLeases.get(executionId); + if (!lease || lease.expiring) return; + lease.expiring = true; + if (lease.timeout) clearTimeout(lease.timeout); + lease.timeout = null; + if (self.approvalResponses.has(executionId)) return; + + const response = { + action: "decline", + content: { reason: "approval_timeout" }, + } satisfies ResumeResponse; + yield* Effect.sync(() => { + console.info(JSON.stringify({ event: "mcp_pending_approval_lease_expire", executionId })); + }); + yield* self.recordApprovalResponse(executionId, response); + if (self.engine && !self.approvalWaiters.has(executionId)) { + yield* self.engine.resume(executionId, response).pipe(Effect.ignore); + } + }).pipe( + Effect.ensuring(self.releasePendingApprovalLease(executionId)), + Effect.withSpan("McpSessionDO.pending_approval_lease.expire", { + attributes: { "mcp.execution.id": executionId }, + }), + ); + } + + private takeApprovalResponse(executionId: string): Effect.Effect { + const self = this; + return Effect.promise(async () => { + const memoryResponse = self.approvalResponses.get(executionId); + if (memoryResponse) { + self.approvalResponses.delete(executionId); + await self.ctx.storage.delete(approvalResponseKey(executionId)); + return memoryResponse; + } + const stored = await self.ctx.storage.get(approvalResponseKey(executionId)); + if (!stored) return null; + await self.ctx.storage.delete(approvalResponseKey(executionId)); + return stored; + }); + } + + private waitForApprovalResponse(executionId: string): Effect.Effect { + const self = this; + return Effect.gen(function* () { + const existing = yield* self.takeApprovalResponse(executionId); + if (existing) return existing; + + const waiter = + self.approvalWaiters.get(executionId) ?? (yield* Deferred.make()); + self.approvalWaiters.set(executionId, waiter); + yield* Deferred.await(waiter).pipe( + Effect.ensuring( + Effect.sync(() => { + if (self.approvalWaiters.get(executionId) === waiter) { + self.approvalWaiters.delete(executionId); + } + }), + ), + ); + return yield* self.takeApprovalResponse(executionId); + }); + } + + private async cleanup(): Promise { + await Effect.runPromise(this.closeRuntime()); + } +} diff --git a/packages/hosts/cloudflare/src/mcp/session-alarm-policy.test.ts b/packages/hosts/cloudflare/src/mcp/session-alarm-policy.test.ts new file mode 100644 index 0000000000..48db8c918a --- /dev/null +++ b/packages/hosts/cloudflare/src/mcp/session-alarm-policy.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from "@effect/vitest"; + +import { + MAX_PAUSED_SESSION_IDLE_MS, + PAUSED_EXECUTION_LEASE_MS, + SESSION_TIMEOUT_MS, + decideSessionAlarm, +} from "./session-alarm-policy"; + +describe("decideSessionAlarm", () => { + it("keeps the session within the idle timeout", () => { + expect( + decideSessionAlarm({ + idleMs: SESSION_TIMEOUT_MS - 1, + pausedExecutionCount: 0, + }), + ).toEqual({ kind: "idle_within_timeout" }); + }); + + it("stays within the idle timeout regardless of paused work", () => { + expect( + decideSessionAlarm({ + idleMs: SESSION_TIMEOUT_MS - 1, + pausedExecutionCount: 3, + }), + ).toEqual({ kind: "idle_within_timeout" }); + }); + + it("destroys an idle session with no paused work", () => { + expect( + decideSessionAlarm({ + idleMs: SESSION_TIMEOUT_MS, + pausedExecutionCount: 0, + }), + ).toEqual({ kind: "destroy_idle_session" }); + }); + + it("extends the lease when paused continuations exist", () => { + expect( + decideSessionAlarm({ + idleMs: SESSION_TIMEOUT_MS, + pausedExecutionCount: 1, + }), + ).toEqual({ + kind: "extend_paused_lease", + leaseMs: PAUSED_EXECUTION_LEASE_MS, + }); + }); + + it("destroys a paused session once the lease cap elapses", () => { + expect( + decideSessionAlarm({ + idleMs: MAX_PAUSED_SESSION_IDLE_MS, + pausedExecutionCount: 1, + }), + ).toEqual({ kind: "destroy_idle_session" }); + }); +}); diff --git a/packages/hosts/cloudflare/src/mcp/session-alarm-policy.ts b/packages/hosts/cloudflare/src/mcp/session-alarm-policy.ts new file mode 100644 index 0000000000..f4c79caa0d --- /dev/null +++ b/packages/hosts/cloudflare/src/mcp/session-alarm-policy.ts @@ -0,0 +1,47 @@ +import { PAUSED_APPROVAL_TIMEOUT_MS } from "@executor-js/host-mcp/tool-server"; + +/** Idle timeout for MCP sessions with no paused continuations. */ +export const SESSION_TIMEOUT_MS = 5 * 60 * 1000; + +/** Lease extension while paused executions block hibernation (matches browser approval wait). */ +export const PAUSED_EXECUTION_LEASE_MS = PAUSED_APPROVAL_TIMEOUT_MS; + +/** + * Hard upper bound on idle time before a paused session is torn down regardless + * of outstanding paused work. The lease grants a single approval window of grace + * past the idle timeout — once it elapses the browser approval wait has already + * timed out (see tool-server `waitForBrowserApprovalResponse`), so the paused + * execution is no longer resumable and the DO must not keep extending forever. + */ +export const MAX_PAUSED_SESSION_IDLE_MS = SESSION_TIMEOUT_MS + PAUSED_EXECUTION_LEASE_MS; + +export type SessionAlarmDecision = + | { readonly kind: "idle_within_timeout" } + | { readonly kind: "destroy_idle_session" } + | { readonly kind: "extend_paused_lease"; readonly leaseMs: number }; + +export const decideSessionAlarm = (input: { + readonly idleMs: number; + readonly pausedExecutionCount: number; +}): SessionAlarmDecision => { + if (input.idleMs < SESSION_TIMEOUT_MS) { + return { kind: "idle_within_timeout" }; + } + if (input.pausedExecutionCount > 0 && input.idleMs < MAX_PAUSED_SESSION_IDLE_MS) { + return { kind: "extend_paused_lease", leaseMs: PAUSED_EXECUTION_LEASE_MS }; + } + return { kind: "destroy_idle_session" }; +}; + +export const pausedLeaseExtensionLog = (input: { + readonly sessionId: string; + readonly pausedExecutionCount: number; + readonly idleMs: number; + readonly leaseMs: number; +}): Record => ({ + event: "mcp_session_paused_lease_extension", + sessionId: input.sessionId, + pausedExecutionCount: input.pausedExecutionCount, + idleMs: input.idleMs, + leaseMs: input.leaseMs, +}); diff --git a/packages/hosts/cloudflare/src/mcp/session-durable-object.ts b/packages/hosts/cloudflare/src/mcp/session-durable-object.ts index e23a99c7ba..896d19e525 100644 --- a/packages/hosts/cloudflare/src/mcp/session-durable-object.ts +++ b/packages/hosts/cloudflare/src/mcp/session-durable-object.ts @@ -26,6 +26,7 @@ import { } from "@executor-js/execution"; import { makeMcpWorkerTransport, type McpWorkerTransport } from "./worker-transport"; +import { decideSessionAlarm, pausedLeaseExtensionLog } from "./session-alarm-policy"; import { INTERNAL_ACCOUNT_ID_HEADER, INTERNAL_ORGANIZATION_ID_HEADER, @@ -81,7 +82,6 @@ const resumeApprovalResult = ( }); const HEARTBEAT_MS = 30 * 1000; -const SESSION_TIMEOUT_MS = 5 * 60 * 1000; const TRANSPORT_STATE_KEY = "transport"; const SESSION_META_KEY = "session-meta"; const LAST_ACTIVITY_KEY = "last-activity-ms"; @@ -897,14 +897,38 @@ export abstract class McpSessionDOBase< ); } + private async pausedExecutionCount(): Promise { + if (!this.engine) return 0; + return Effect.runPromise(this.engine.pausedExecutionCount()); + } + private async runAlarm(): Promise { const lastActivityMs = await this.loadLastActivity(); const idleMs = Date.now() - lastActivityMs; - if (idleMs >= SESSION_TIMEOUT_MS) { + const pausedExecutionCount = await this.pausedExecutionCount(); + const decision = decideSessionAlarm({ idleMs, pausedExecutionCount }); + + if (decision.kind === "extend_paused_lease") { + console.info( + JSON.stringify( + pausedLeaseExtensionLog({ + sessionId: this.sessionId, + pausedExecutionCount, + idleMs, + leaseMs: decision.leaseMs, + }), + ), + ); + await this.ctx.storage.setAlarm(Date.now() + decision.leaseMs); + return; + } + + if (decision.kind === "destroy_idle_session") { await Effect.runPromise(this.closeRuntime()); await this.ctx.storage.deleteAlarm(); return; } + await this.ctx.storage.setAlarm(Date.now() + HEARTBEAT_MS); } diff --git a/packages/hosts/mcp/src/tool-server.test.ts b/packages/hosts/mcp/src/tool-server.test.ts index d42b5f7410..a751afafa1 100644 --- a/packages/hosts/mcp/src/tool-server.test.ts +++ b/packages/hosts/mcp/src/tool-server.test.ts @@ -38,6 +38,8 @@ const makeStubEngine = (overrides: { (() => Effect.succeed({ status: "completed", result: { result: "default" } })), resume: overrides.resume ?? (() => Effect.succeed(null)), getPausedExecution: () => Effect.succeed(null), + pausedExecutionCount: () => Effect.succeed(0), + hasPausedExecutions: () => Effect.succeed(false), getDescription: Effect.succeed(overrides.description ?? "test executor"), }); @@ -46,7 +48,10 @@ const withClient = async ( engine: ExecutionEngine, capabilities: ClientCapabilities, fn: (client: Client) => Promise, - config?: Pick, "debug" | "elicitationMode" | "browserApprovalStore">, + config?: Pick< + ExecutorMcpServerConfig, + "debug" | "elicitationMode" | "browserApprovalStore" | "pausedExecutionHooks" + >, ) => { const mcpServer = await Effect.runPromise(createExecutorMcpServer({ engine, ...config })); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); @@ -1092,6 +1097,49 @@ describe("MCP host server — client without elicitation (pause/resume)", () => ); }); + it("reports pause and resume lifecycle boundaries", async () => { + const events: string[] = []; + const approval = FormElicitation.make({ message: "Approve", requestedSchema: {} }); + const engine = makeStubEngine({ + executeWithPause: () => Effect.succeed(makePausedResult("exec_1", approval)), + resume: () => Effect.succeed(makePausedResult("exec_2", approval)), + }); + + await withClient( + engine, + NO_CAPS, + async (client) => { + await client.callTool({ + name: "execute", + arguments: { code: "pause" }, + }); + expect(events).toEqual(["pause:exec_1"]); + + await client.callTool({ + name: "resume", + arguments: { executionId: "exec_1", action: "accept", content: "{}" }, + }); + expect(events).toEqual([ + "pause:exec_1", + "resume-start:exec_1", + "resume-settle:exec_1", + "pause:exec_2", + ]); + }, + { + elicitationMode: { mode: "model" }, + pausedExecutionHooks: { + onExecutionPaused: (executionId) => + Effect.sync(() => events.push(`pause:${executionId}`)), + onResumeStarted: (executionId) => + Effect.sync(() => events.push(`resume-start:${executionId}`)), + onResumeSettled: (executionId) => + Effect.sync(() => events.push(`resume-settle:${executionId}`)), + }, + }, + ); + }); + it("resume tool passes parsed content to engine", async () => { let receivedContent: Record | undefined; const engine = makeStubEngine({ diff --git a/packages/hosts/mcp/src/tool-server.ts b/packages/hosts/mcp/src/tool-server.ts index ff7989b557..c5ea72c613 100644 --- a/packages/hosts/mcp/src/tool-server.ts +++ b/packages/hosts/mcp/src/tool-server.ts @@ -1,4 +1,4 @@ -import { Effect, Match, Option, Schema } from "effect"; +import { Duration, Effect, Match, Option, Schema } from "effect"; import * as Cause from "effect/Cause"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { ContentBlockSchema, type ContentBlock } from "@modelcontextprotocol/sdk/types.js"; @@ -91,6 +91,12 @@ type SharedMcpServerConfig = { readonly mode: "native"; }; readonly browserApprovalStore?: BrowserApprovalStore; + /** + * Host-owned lifecycle for paused executions. The MCP server reports pause + * boundaries; the host decides whether that means a keepAlive lease, browser + * wait, durable record, or no-op. + */ + readonly pausedExecutionHooks?: PausedExecutionHooks; }; export type ExecutorMcpServerConfig = @@ -104,6 +110,15 @@ export type BrowserApprovalStore = { readonly waitForResponse?: (executionId: string) => Effect.Effect; }; +export const PAUSED_APPROVAL_TIMEOUT_MS = 4 * 60 * 1000; +const BROWSER_APPROVAL_WAIT_TIMEOUT_MS = PAUSED_APPROVAL_TIMEOUT_MS + 1000; + +export type PausedExecutionHooks = { + readonly onExecutionPaused?: (executionId: string) => Effect.Effect; + readonly onResumeStarted?: (executionId: string) => Effect.Effect; + readonly onResumeSettled?: (executionId: string) => Effect.Effect; +}; + // --------------------------------------------------------------------------- // Elicitation bridge // --------------------------------------------------------------------------- @@ -544,6 +559,17 @@ export const createExecutorMcpServer = ( ({ mode: "model", } as const); + const onExecutionPaused = (executionId: string): Effect.Effect => + config.pausedExecutionHooks?.onExecutionPaused?.(executionId) ?? Effect.void; + const onResumeStarted = (executionId: string): Effect.Effect => + config.pausedExecutionHooks?.onResumeStarted?.(executionId) ?? Effect.void; + const onResumeSettled = (executionId: string): Effect.Effect => + config.pausedExecutionHooks?.onResumeSettled?.(executionId) ?? Effect.void; + const resumeWithLifecycle = (executionId: string, response: ResumeResponse) => + Effect.gen(function* () { + yield* onResumeStarted(executionId); + return yield* engine.resume(executionId, response); + }).pipe(Effect.ensuring(onResumeSettled(executionId))); const resolveParentSpan = (): Tracer.AnySpan | undefined => { const ps = config.parentSpan; @@ -594,6 +620,14 @@ export const createExecutorMcpServer = ( ? pausedInteractionKind(outcome.execution.elicitationContext.request) : undefined, }); + if (outcome.status === "paused") { + yield* Effect.annotateCurrentSpan({ + "mcp.execute.paused": true, + "mcp.execute.paused_execution_id": outcome.execution.id, + "mcp.execute.pause_source": "execute", + }); + yield* onExecutionPaused(outcome.execution.id); + } return outcome.status === "completed" ? toMcpResult(outcome.result) : elicitationMode.mode === "browser" @@ -620,7 +654,7 @@ export const createExecutorMcpServer = ( hasContent: content !== undefined, clientCapabilities: server.server.getClientCapabilities() ?? null, }); - const outcome = yield* engine.resume(executionId, { action, content }); + const outcome = yield* resumeWithLifecycle(executionId, { action, content }); if (!outcome) { debugLog("resume.missing_execution", { executionId }); return missingExecutionResult(executionId); @@ -634,6 +668,14 @@ export const createExecutorMcpServer = ( ? pausedInteractionKind(outcome.execution.elicitationContext.request) : undefined, }); + if (outcome.status === "paused") { + yield* Effect.annotateCurrentSpan({ + "mcp.execute.paused": true, + "mcp.execute.paused_execution_id": outcome.execution.id, + "mcp.execute.pause_source": "resume", + }); + yield* onExecutionPaused(outcome.execution.id); + } return outcome.status === "completed" ? toMcpResult(outcome.result) : toMcpPausedResult(formatPausedExecution(outcome.execution)); @@ -682,7 +724,7 @@ export const createExecutorMcpServer = ( return waitForResponse(executionId).pipe( Effect.timeoutOrElse({ - duration: "10 minutes", + duration: Duration.millis(BROWSER_APPROVAL_WAIT_TIMEOUT_MS), orElse: () => Effect.succeed(null), }), ); @@ -693,10 +735,18 @@ export const createExecutorMcpServer = ( const response = yield* waitForBrowserApprovalResponse(executionId); if (!response) return yield* requireUserResumeApproval(executionId); - const outcome = yield* engine.resume(executionId, response); + const outcome = yield* resumeWithLifecycle(executionId, response); if (!outcome) { return missingExecutionResult(executionId); } + if (outcome.status === "paused") { + yield* Effect.annotateCurrentSpan({ + "mcp.execute.paused": true, + "mcp.execute.paused_execution_id": outcome.execution.id, + "mcp.execute.pause_source": "browser_resume", + }); + yield* onExecutionPaused(outcome.execution.id); + } return outcome.status === "completed" ? toMcpResult(outcome.result) : yield* requireUserResumeApproval(outcome.execution.id); diff --git a/packages/plugins/openapi/src/sdk/backing.ts b/packages/plugins/openapi/src/sdk/backing.ts index 2415eeb6cb..0018671e9e 100644 --- a/packages/plugins/openapi/src/sdk/backing.ts +++ b/packages/plugins/openapi/src/sdk/backing.ts @@ -651,15 +651,14 @@ export const resolveOpenApiBackedAnnotations = (input: { readonly toolRows: readonly { readonly name: string }[]; }) => Effect.gen(function* () { - const ops = yield* input.ctx.storage.listOperations(String(input.integration)); - const byName = new Map(); - for (const op of ops) byName.set(op.toolName, op.binding); const out: Record> = {}; for (const row of input.toolRows) { - const binding = byName.get(row.name); - if (binding) { - out[row.name] = annotationsForOperation(binding.method, binding.pathTemplate); - } + const operation = yield* input.ctx.storage.getOperation(input.integration, row.name); + if (!operation) continue; + out[row.name] = annotationsForOperation( + operation.binding.method, + operation.binding.pathTemplate, + ); } return out; });