diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 36cda894a0..534a256ce4 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,7 +9,7 @@ handful of domain packages built on top. - **`apps/hub`** — the Interchange hub server. It mounts stock `@intx/hub-api` routes (auth, tenants, principals, grants, workflow - deploy/run) plus one composition block (`apps/hub/src/server.ts`) that adds + deploy/run) plus one composition block (`apps/hub/src/corbits-mount.ts`) that adds Corbits libraries: `@corbits/mailbox` (mail), `@corbits/memory` (recall), `@corbits/artifacts` (files), `@corbits/cron` (scheduling), `@corbits/webhooks` (inbound webhook ingress), `@corbits/mcp` (MCP catalog diff --git a/apps/hub/AGENTS.md b/apps/hub/AGENTS.md index b98f96916d..a15bf02d73 100644 --- a/apps/hub/AGENTS.md +++ b/apps/hub/AGENTS.md @@ -1,6 +1,6 @@ Follow the [root AGENTS.md](../../AGENTS.md). -- Everything above the Corbits mount block in `src/server.ts` mirrors upstream Interchange's `server.ts`. -- Mount only stock `@intx/hub-api` routes plus the Corbits mount block in `src/server.ts`; any other hub mount is cutover debt with a Linear issue. +- `src/server.ts` mirrors upstream Interchange's `server.ts` and delegates Workbench extensions to `src/corbits-mount.ts`. +- Mount only stock `@intx/hub-api` routes plus the Corbits mount block in `src/corbits-mount.ts`; any other hub mount is cutover debt with a Linear issue. - Boot applies `@intx/db` platform migrations and each mounted Corbits library's idempotent migration in `src/migrate.ts`. - Gate every tenant route with `createRequireGrant` and report background or callback errors through `reportError` from `@corbits/error-sink`. diff --git a/apps/hub/README.md b/apps/hub/README.md index 71daac8e2d..60bb3375dd 100644 --- a/apps/hub/README.md +++ b/apps/hub/README.md @@ -6,13 +6,13 @@ one origin. Apps stay generic — every product rule belongs in a package (see ## What it wires -- Everything above the Corbits mount block in `src/server.ts` is upstream - Interchange's `server.ts`, providing `@intx/hub-api` (`createApp`), - `@intx/hub-sessions`, auth, and tenant middleware. +- `src/server.ts` mirrors upstream Interchange's `server.ts`, providing + `@intx/hub-api` (`createApp`), `@intx/hub-sessions`, auth, and tenant + middleware, then calls `mountCorbits` (`src/corbits-mount.ts`). - Boot runs `migrateHub` (`src/migrate.ts`), applying `@intx/db` platform migrations and each mounted Corbits library's idempotent migration under a Postgres advisory lock. -- The Corbits mount block in `src/server.ts` mounts: +- `src/corbits-mount.ts` mounts the Corbits mount block: - `@corbits/artifacts` (`mountArtifacts` and `mountWorkflowArtifacts`) - Live workflow-run SSE stream routes (`src/run-stream.ts`) - `@corbits/agent-token` (`mountAgentTokens` and `createAgentTokenVerifier`) diff --git a/apps/hub/package.json b/apps/hub/package.json index b41fcc9c74..87407d865d 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -12,7 +12,6 @@ "test": "bun test" }, "dependencies": { - "@chat-adapter/state-memory": "^4.35.0", "@corbits/agent-token": "github:corbitsdev/corbits-agent-token#ed286f4592190f911de480ef5330ce03d8605f00", "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", "@corbits/codex-provider": "0.1.2", @@ -29,20 +28,13 @@ "@intx/crypto": "0.5.0", "@intx/db": "workspace:*", "@intx/hub-api": "workspace:*", - "@intx/hub-common": "0.5.0", "@intx/hub-sessions": "workspace:*", "@intx/log": "0.5.0", - "@intx/mime": "0.5.0", "@intx/types": "workspace:*", - "@intx/workflow": "workspace:*", - "@intx/workflow-deploy": "workspace:*", - "@modelcontextprotocol/sdk": "catalog:", "arktype": "catalog:", - "better-auth": "catalog:", "drizzle-orm": "catalog:", "hono": "catalog:", - "postgres": "catalog:", - "simple-icons": "^16.28.0" + "postgres": "catalog:" }, "devDependencies": { "@types/bun": "catalog:", diff --git a/apps/hub/src/corbits-mount.ts b/apps/hub/src/corbits-mount.ts new file mode 100644 index 0000000000..68b7c563a1 --- /dev/null +++ b/apps/hub/src/corbits-mount.ts @@ -0,0 +1,448 @@ +import { createAgentTokenVerifier, mountAgentTokens } from "@corbits/agent-token"; +import { + InlineContentStore, + mountArtifacts, + mountWorkflowArtifacts, + type WorkflowArtifactEnv, +} from "@corbits/artifacts"; +import { + CODEX_PROVIDER, + codexOAuthConfig, + exchangeCodexCode, + refreshCodexTokens, +} from "@corbits/codex-provider"; +import { createCronRoutes, createCronTicker, createRunTriggerCronDeliver } from "@corbits/cron"; +import { reportError } from "@corbits/error-sink"; +import { + createInMemoryMailboxEventBus, + createMailboxDb, + createMailboxPersist, + mountMailbox, +} from "@corbits/mailbox"; +import { mountMcpDiscovery } from "@corbits/mcp/hub"; +import { + createMemory, + loadMemoryConfig, + mountWorkflowMemory, + type WorkflowMemoryEnv, +} from "@corbits/memory"; +import { + createOAuthLoginStore, + createOAuthTokenRefresher, + mountOAuthCallback, + mountOAuthLogin, + type OAuthLoginProviders, +} from "@corbits/oauth-core/hub"; +import { + createRunTriggerDeliverer, + createTenantSystemSender, + installWebhooks, + type HookMailRouter, +} from "@corbits/webhooks"; +import { + XAI_PROVIDER, + exchangeXaiCode, + refreshXaiTokens, + xaiOAuthConfig, +} from "@corbits/xai-provider"; +import { timeWindowEvaluator } from "@intx/authz"; +import type { createEnvKeyCredentialCipher } from "@intx/crypto"; +import type { createDB, createGrantStore, PrincipalKeyStore } from "@intx/db"; +import { + asset as assetTable, + principal as principalTable, + tenant as tenantTable, + workflowRun, +} from "@intx/db/schema"; +import { + type AppEnv, + createMailTriggeredRunGrantsMaterializer, + createRequireGrant, + type TenantEnv, +} from "@intx/hub-api"; +import { pushSourceUpdates, type SidecarLookups, type SidecarRouter } from "@intx/hub-sessions"; +import { getLogger } from "@intx/log"; +import type { ConditionRegistry } from "@intx/types/authz"; +import { and, eq } from "drizzle-orm"; +import { Hono } from "hono"; +import { createHubMailboxAuthorizeSender } from "./mailbox-persist"; +import { captureMailboxRequest, createMailboxDeliver } from "./mailbox-send"; +import { createWorkflowRunStreamRoutes } from "./run-stream"; +import { createWorkflowRunAuthenticator } from "./workflow-run-authenticator"; + +const grantConditionRegistry: ConditionRegistry = { + time_window: timeWindowEvaluator, +}; + +const TENANT_PREFIX = "/api/tenants/:tenantId"; + +export type MountCorbitsDeps = { + readonly app: Hono; + readonly db: ReturnType["db"]; + readonly databaseUrl: string; + readonly port: number; + readonly grantStore: ReturnType; + readonly principalKeyStore: PrincipalKeyStore; + readonly credentialCipher: ReturnType; + readonly sidecarRouter: SidecarRouter; + readonly lookups: SidecarLookups; +}; + +export type MountedCorbits = { + stop(): void; +}; + +export async function mountCorbits({ + app, + db, + databaseUrl, + port, + grantStore, + principalKeyStore, + credentialCipher, + sidecarRouter, + lookups, +}: MountCorbitsDeps): Promise { + const log = getLogger(["hub"]); + const requireGrant = createRequireGrant({ + grantStore, + conditionRegistry: grantConditionRegistry, + }); + + const { db: mailboxDb } = createMailboxDb(databaseUrl); + const mailboxBus = createInMemoryMailboxEventBus(); + const persistMail = createMailboxPersist(mailboxDb, { + upstream: async () => [], + authorizeSender: createHubMailboxAuthorizeSender(db), + bus: mailboxBus, + }); + // `createSidecarRouter` captured `lookups` before this wrapper existed; + // mutating it in place ensures agent outbound mail also lands in principal inboxes. + lookups.persistMail = persistMail; + + { + const artifactsApi = new Hono(); + mountArtifacts(artifactsApi, { + db, + contentStore: InlineContentStore, + requireGrant, + }); + app.route(TENANT_PREFIX, artifactsApi); + } + + app.route( + TENANT_PREFIX, + createWorkflowRunStreamRoutes({ + db, + sidecarRouter, + requireGrant, + }), + ); + + { + const agentTokensApp = new Hono(); + mountAgentTokens(agentTokensApp, { + db, + requireGrant: requireGrant("credential:*", "create"), + resolveTenantId: (ctx) => (ctx as { get(key: "tenant"): { id: string } }).get("tenant").id, + // Scoped to the `workflow` source asset so the workbench can mint the + // token before the deploy that creates the run. + resolveDefinition: async (tenantId, definitionId) => { + const row = await db.query.asset.findFirst({ + where: and( + eq(assetTable.id, definitionId), + eq(assetTable.tenantId, tenantId), + eq(assetTable.kind, "workflow"), + ), + }); + return row !== undefined; + }, + }); + app.route(TENANT_PREFIX, agentTokensApp); + } + + const verifyAgentToken = createAgentTokenVerifier({ db }); + const resolveAgentRun = async (runAddress: string) => { + if (runAddress === "") return null; + const run = await db.query.workflowRun.findFirst({ + where: eq(workflowRun.address, runAddress), + }); + if (run === undefined || run.principalId === null) return null; + return { + tenantId: run.tenantId, + principalId: run.principalId, + runId: run.id, + }; + }; + + { + const workflowArtifactsApi = new Hono(); + const workflowRunAuthenticator = createWorkflowRunAuthenticator({ db }); + mountWorkflowArtifacts(workflowArtifactsApi, { + db, + contentStore: InlineContentStore, + resolveRunScope: (token, runAddress) => workflowRunAuthenticator.resolve(token, runAddress), + agentToken: { + verify: (ctx) => verifyAgentToken(ctx), + resolveRun: resolveAgentRun, + }, + }); + app.route("/api/workflow-artifacts", workflowArtifactsApi); + } + + { + const mailboxApp = new Hono(); + mailboxApp.use("/me/inbox/send", captureMailboxRequest()); + mountMailbox(mailboxApp, { + db: mailboxDb, + bus: mailboxBus, + resolvePrincipal: (ctx) => { + const c = ctx as { get(key: "tenant" | "principal"): { id: string } }; + return { + tenantId: c.get("tenant").id, + principalId: c.get("principal").id, + }; + }, + senderAddressFor: async (principal) => { + const [tenantRow] = await db + .select({ domain: tenantTable.domain }) + .from(tenantTable) + .where(eq(tenantTable.id, principal.tenantId)) + .limit(1); + if (tenantRow === undefined) { + throw new Error(`no tenant "${principal.tenantId}" to address a mailbox sender from`); + } + const [principalRow] = await db + .select({ refId: principalTable.refId }) + .from(principalTable) + .where(eq(principalTable.id, principal.principalId)) + .limit(1); + if (principalRow === undefined) { + throw new Error(`no principal "${principal.principalId}" to address a mailbox sender as`); + } + // Small models lowercase addresses before replying, so stamp lowercase canonically. + return `${principalRow.refId}@${tenantRow.domain}`.toLowerCase(); + }, + deliver: createMailboxDeliver({ app, persistMail }), + }); + app.route(`${TENANT_PREFIX}/mailbox`, mailboxApp); + } + + const systemTriggerMailRouter: HookMailRouter = { + routeMail: (address, rawMessage, authenticatedSender, messageId) => { + if (!sidecarRouter.getRoutableAddresses().includes(address)) { + return false; + } + void sidecarRouter + .routeMail(address, rawMessage, authenticatedSender, messageId) + .catch((error: unknown) => { + reportError(error, { + operation: "hub.systemTriggerMailRouter.routeMail", + extra: { address, authenticatedSender }, + }); + }); + return true; + }, + sendRunGrants: (address, runId, stepGrants, senderIdentities) => + sidecarRouter.sendRunGrants( + address, + runId, + stepGrants as Parameters[2], + senderIdentities as Parameters[3], + ), + }; + + app.route( + `${TENANT_PREFIX}/cron`, + createCronRoutes({ + db, + requireGrant, + }), + ); + + // Due schedules fire without a signed-in user, so they use the system-trigger + // path rather than mailbox persist (which requires a live routable sender). + const cronTicker = createCronTicker({ + db, + intervalMs: 60_000, + deliver: createRunTriggerCronDeliver( + createRunTriggerDeliverer({ + router: systemTriggerMailRouter, + materialize: createMailTriggeredRunGrantsMaterializer({ + db, + principalKeyStore, + grantStore, + }), + tenantDomain: async (tenantId) => { + const [tenantRow] = await db + .select({ domain: tenantTable.domain }) + .from(tenantTable) + .where(eq(tenantTable.id, tenantId)) + .limit(1); + if (tenantRow === undefined) { + throw new Error(`no tenant "${tenantId}" to address cron mail from`); + } + return tenantRow.domain; + }, + senderLocalPart: "cron", + systemSender: createTenantSystemSender({ db, principalKeyStore }), + }), + ), + onDeliveryError: (error, schedule) => { + reportError(error, { + operation: "hub.cron.deliver", + extra: { scheduleId: schedule.id, tenantId: schedule.tenantId }, + }); + }, + onScheduleWaiting: (schedule) => { + log.info("Cron schedule waiting for agent {definitionName}", { + scheduleId: schedule.id, + tenantId: schedule.tenantId, + definitionName: schedule.definitionName, + }); + }, + onScheduleStopped: (schedule) => { + reportError( + new Error(`cron schedule stopped: ${schedule.reason} (${schedule.definitionName})`), + { + operation: "hub.cron.schedule_stopped", + extra: { + scheduleId: schedule.id, + tenantId: schedule.tenantId, + definitionName: schedule.definitionName, + reason: schedule.reason, + }, + }, + ); + }, + }); + cronTicker.start(); + + { + const memoryApp = new Hono(); + const memory = createMemory({ + app: memoryApp, + config: loadMemoryConfig(), + grantStore, + conditionRegistry: grantConditionRegistry, + }); + app.route("/", memoryApp); + + const workflowMemoryApi = new Hono(); + mountWorkflowMemory(workflowMemoryApi, { + memory, + agentToken: { + verify: (ctx) => verifyAgentToken(ctx), + resolveRun: resolveAgentRun, + }, + }); + app.route("/api/workflow-memory", workflowMemoryApi); + } + + const hubPublicUrl = process.env["HUB_PUBLIC_URL"] ?? `http://127.0.0.1:${String(port)}`; + const hubHost = new URL(hubPublicUrl).hostname; + const localDevHub = hubHost === "localhost" || hubHost.startsWith("127.") || hubHost === "[::1]"; + + const oauthLoginApi = new Hono(); + const oauthProviders: OAuthLoginProviders = { + [CODEX_PROVIDER]: { + oauthConfig: codexOAuthConfig, + exchange: (code: string, verifier: string, now: number) => + exchangeCodexCode(code, verifier, now), + refresh: (refreshSecret: string, now: number) => + refreshCodexTokens(refreshSecret, now, { + access: "", + refresh: refreshSecret, + }), + metadata: (tokens) => + "accountId" in tokens && typeof tokens.accountId === "string" + ? { accountId: tokens.accountId } + : {}, + }, + [XAI_PROVIDER]: { + oauthConfig: xaiOAuthConfig, + exchange: (code: string, verifier: string, now: number) => + exchangeXaiCode(code, verifier, now), + refresh: (refreshSecret: string, now: number) => refreshXaiTokens(refreshSecret, now), + }, + }; + const oauthLoginStore = createOAuthLoginStore(); + const oauthCallbackApi = new Hono(); + mountOAuthCallback(oauthCallbackApi, { + store: oauthLoginStore, + path: "/api/oauth/callback", + }); + app.route("/", oauthCallbackApi); + mountOAuthLogin(oauthLoginApi, { + store: oauthLoginStore, + callbackUrl: `${hubPublicUrl.replace(/\/+$/, "")}/api/oauth/callback`, + clientName: "Workbench", + db, + cipher: credentialCipher, + requireGrant: requireGrant("credential:*", "create"), + providers: oauthProviders, + onError: (error, { provider }) => { + reportError(error, { + operation: "hub.oauth-login", + extra: { provider }, + }); + }, + }); + app.route(TENANT_PREFIX, oauthLoginApi); + + const oauthTokenRefresher = createOAuthTokenRefresher({ + db, + cipher: credentialCipher, + providers: oauthProviders, + intervalMs: 60_000, + onRefreshed: ({ tenantId, credentialId }) => { + void pushSourceUpdates(db, sidecarRouter, tenantId, credentialCipher).catch( + (error: unknown) => { + reportError(error, { + operation: "hub.oauth-refresh.push", + extra: { tenantId, credentialId }, + }); + }, + ); + }, + onError: (error, { provider, credentialId }) => { + reportError(error, { + operation: "hub.oauth-refresh", + extra: { + provider: provider ?? "", + credentialId: credentialId ?? "", + }, + }); + }, + }); + oauthTokenRefresher.start(); + + { + const mcpApi = new Hono(); + mountMcpDiscovery(mcpApi, { + allowLoopback: localDevHub, + db, + cipher: credentialCipher, + requireGrant: requireGrant("credential:*", "read"), + onError: (error, { url }) => { + reportError(error, { operation: "hub.mcp-discover", extra: { url } }); + }, + }); + app.route(TENANT_PREFIX, mcpApi); + } + + await installWebhooks({ + app, + db, + credentialCipher, + principalKeyStore, + router: systemTriggerMailRouter, + }); + + return { + stop() { + cronTicker.stop(); + oauthTokenRefresher.stop(); + }, + }; +} diff --git a/apps/hub/src/migrate.ts b/apps/hub/src/migrate.ts index f2295663c0..9731987025 100644 --- a/apps/hub/src/migrate.ts +++ b/apps/hub/src/migrate.ts @@ -33,7 +33,7 @@ async function platformSchemaPresent( return rows[0]?.present != null; } -function databaseUrlFrom(config: HubMigrateConfig): string { +export function databaseUrlFrom(config: HubMigrateConfig): string { return ( `postgres://${encodeURIComponent(config.user)}:${encodeURIComponent(config.password)}` + `@${config.host}:${String(config.port)}/${config.database}` diff --git a/apps/hub/src/provisioners/process.ts b/apps/hub/src/provisioners/process.ts index e0fe214257..e800c9f061 100644 --- a/apps/hub/src/provisioners/process.ts +++ b/apps/hub/src/provisioners/process.ts @@ -1,24 +1,33 @@ -import { isAbsolute, resolve, dirname } from "node:path"; -import { mkdir, readFile, readdir, rm, rmdir, writeFile } from "node:fs/promises"; +import { randomBytes } from "node:crypto"; +import { + mkdir, + open, + readFile, + readdir, + rename, + rm, + rmdir, + unlink, + writeFile, +} from "node:fs/promises"; +import { dirname, isAbsolute, resolve } from "node:path"; import { reportError } from "@corbits/error-sink"; +import type { + DestroySidecarRequest, + DestroySidecarResult, + EnsureSidecarRequest, + EnsureSidecarResult, + SidecarProvisioner, +} from "@intx/hub-sessions"; +import { getLogger } from "@intx/log"; +import type { SidecarCapabilityDeclaration } from "@intx/types"; import { type } from "arktype"; -import { - BackendOperationError, - createAllocationStateStore, - createSidecarProvisioner, - sidecarCapabilityDeclarations, - type AllocationStateStore, - type SidecarBackend, - type StartUnitArgs, -} from "./sandbox-sidecar"; -import type { SidecarProvisioner } from "@intx/hub-sessions"; - -// The sidecar entry this provisioner spawns, relative to this file's own -// directory: the same `apps/sidecar/src/index.ts` that `bun run dev` -// starts. Resolved from `import.meta.dir` rather than the process cwd so -// the hub can be launched from anywhere on the host. +const log = getLogger(["hub", "process-provisioner"]); + +// Resolved from `import.meta.dir` rather than cwd so the hub can be launched +// from any working directory on the host. const DEFAULT_SIDECAR_ENTRY = resolve( import.meta.dir, "..", @@ -38,37 +47,20 @@ const Environment = type({ }); export type ProcessProvisionerConfig = { - /** The executable that runs the sidecar entry — `bun` by default. */ readonly runtimePath: string; - /** Absolute path to the sidecar entry point this backend spawns. */ readonly sidecarEntryPath: string; - /** - * Hub-side root for per-allocation directories. Each allocation gets - * `//`, holding the pid file this backend - * recovers units from after a hub restart plus the sidecar's own - * `SIDECAR_DATA_DIR`. Destroying an allocation removes that directory. - */ readonly allocationsDir: string; - /** Where the shared allocation state store keeps generation fences. */ readonly stateFilePath: string; - /** The ws(s):// URL provisioned sidecars dial back on. */ readonly hubWebSocketUrl: string; - /** The operator key every spawned sidecar seals its at-rest credentials under. */ readonly sidecarCredentialEncryptionKey: string; }; export type ReadProcessProvisionerConfigArgs = { readonly env: Record; - /** - * The HUB's own state directory for this backend, derived from - * `HUB_DATA_DIR` by the caller — never an environment variable an - * operator could point somewhere unrelated. - */ readonly dataDir: string; readonly hubWebSocketUrl: string; }; -/** The entry and runtime keys are optional: unconfigured, this spawns the repo's own apps/sidecar with the running bun. */ export function readProcessProvisionerConfig( args: ReadProcessProvisionerConfigArgs, ): ProcessProvisionerConfig { @@ -100,18 +92,12 @@ export type SpawnSidecarProcessArgs = { readonly env: Record; }; -/** Injected so tests exercise the full ensure/destroy lifecycle without starting a real sidecar. */ export interface SidecarProcessRunner { spawn(args: SpawnSidecarProcessArgs): number; isAlive(pid: number): boolean; signal(pid: number, signal: "SIGTERM" | "SIGKILL"): void; } -/** - * Stdio inherited so a local operator sees sidecar logs in the same - * terminal; unref()ed so a hub shutdown doesn't wait on it — a still-running - * sidecar is reconciled from its pid file on the next boot. - */ export function createBunSidecarProcessRunner(): SidecarProcessRunner { return { spawn(args) { @@ -122,6 +108,8 @@ export function createBunSidecarProcessRunner(): SidecarProcessRunner { stdout: "inherit", stderr: "inherit", }); + // Unref so hub shutdown does not block on running sidecars; surviving + // sidecars are recovered from their pid files on the next boot. child.unref(); return child.pid; }, @@ -131,10 +119,6 @@ export function createBunSidecarProcessRunner(): SidecarProcessRunner { process.kill(pid, 0); return true; } catch (error) { - // A signal-0 probe answers the liveness question through its - // errno: ESRCH means no such process, EPERM means the process - // exists but belongs to another user. Anything else is a real - // failure and must not be read as "dead". if (isErrnoCode(error, "ESRCH")) return false; if (isErrnoCode(error, "EPERM")) return true; throw error; @@ -147,154 +131,273 @@ export function createBunSidecarProcessRunner(): SidecarProcessRunner { }; } -const PID_FILE_NAME = "sidecar.pid"; -const SIDECAR_DATA_DIR_NAME = "data"; -const UNIT_DIR_PREFIX = "gen-"; -// An allocation id names a directory on the hub host, so it may only be -// the id-shaped text Interchange actually issues — never a traversal. -const ALLOCATION_ID_PATTERN = /^[A-Za-z0-9._-]+$/; -const TERMINATION_GRACE_MS = 5_000; -const TERMINATION_POLL_MS = 100; +class ProvisionerOperationError extends Error { + readonly code: string; + readonly retryable: boolean; -/** - * One apps/sidecar process per allocation on the hub host — the only - * backend. Scoping each unit's directory by generation (not one pid file - * per allocation) is what lets this backend survive a hub restart: - * findUnitsByAllocation recovers every live unit from disk by pid file, - * and a new generation's pid never overwrites the one it supersedes. - */ -export function createProcessBackend( - runner: SidecarProcessRunner, - config: ProcessProvisionerConfig, -): SidecarBackend { - function allocationDirOf(allocationId: string): string { - if (!ALLOCATION_ID_PATTERN.test(allocationId)) { - throw new BackendOperationError( - "invalid_allocation_id", - `allocationId ${JSON.stringify(allocationId)} is not a valid directory name`, - false, - ); - } - return resolve(config.allocationsDir, allocationId); + constructor(code: string, message: string, retryable: boolean) { + super(message); + this.name = "ProvisionerOperationError"; + this.code = code; + this.retryable = retryable; } +} - function unitDirOf(allocationId: string, generation: number): string { - return resolve(allocationDirOf(allocationId), `${UNIT_DIR_PREFIX}${String(generation)}`); - } +const AllocationRecord = type({ + allocationId: "string > 0", + sidecarId: "string > 0", + generation: "number.integer >= 0", + desiredState: "'ensured' | 'destroyed'", + externalRef: "string | null", + updatedAt: "string > 0", +}); +export type AllocationRecord = typeof AllocationRecord.infer; - return { - async startUnit(args: StartUnitArgs): Promise { - const unitDir = unitDirOf(args.allocationId, args.generation); - const sidecarDataDir = resolve(unitDir, SIDECAR_DATA_DIR_NAME); - const env = sidecarEnvFor(args, sidecarDataDir, config.sidecarCredentialEncryptionKey); - try { - await mkdir(sidecarDataDir, { recursive: true, mode: 0o700 }); - } catch (error) { - throw operationalFailure( - error, - "allocation_dir_failed", - `unable to create unit directory ${unitDir}`, - args.allocationId, - ); - } +const StateFile = type({ + version: "1", + records: AllocationRecord.array(), +}); - let pid: number; - try { - pid = runner.spawn({ - command: [config.runtimePath, config.sidecarEntryPath], - cwd: dirname(config.sidecarEntryPath), - env, - }); - } catch (error) { - throw operationalFailure( - error, - "sidecar_spawn_failed", - `unable to spawn ${config.sidecarEntryPath}`, - args.allocationId, - ); - } +type ObserveEnsureResult = + | { readonly kind: "observed"; readonly record: AllocationRecord } + | { + readonly kind: "rejected"; + readonly code: "stale_generation" | "generation_destroyed" | "request_conflict"; + readonly message: string; + }; + +type ObserveDestroyResult = + | { readonly kind: "observed"; readonly record: AllocationRecord } + | { + readonly kind: "rejected"; + readonly code: "stale_generation" | "request_conflict"; + readonly message: string; + }; + +export interface AllocationStateStore { + observeEnsure(args: { + allocationId: string; + sidecarId: string; + generation: number; + }): Promise; + observeDestroy(args: { + allocationId: string; + sidecarId: string; + generation: number; + }): Promise; + recordUnit(args: { + allocationId: string; + generation: number; + externalRef: string; + }): Promise; +} - try { - await writeFile(resolve(unitDir, PID_FILE_NAME), `${String(pid)}\n`, { - mode: 0o600, - }); - } catch (error) { - await terminate(runner, pid); - throw operationalFailure( - error, - "pid_file_write_failed", - `unable to record pid ${String(pid)} for allocation ${args.allocationId}`, - args.allocationId, - ); - } - return externalRefOf(args.allocationId, args.generation, pid); - }, +export function createAllocationStateStore(statePath: string): AllocationStateStore { + const records = new Map(); + let initialized = false; + let tail = Promise.resolve(); - async stopUnit(externalRef: string): Promise { - const unit = parseExternalRef(externalRef); - if (unit === null) { - throw new BackendOperationError( - "invalid_external_ref", - `external ref ${JSON.stringify(externalRef)} is not ::`, - false, - ); - } - try { - await terminate(runner, unit.pid); - } catch (error) { - throw operationalFailure( - error, - "sidecar_terminate_failed", - `unable to terminate pid ${String(unit.pid)}`, - unit.allocationId, - ); + async function runExclusive(operation: () => Promise): Promise { + const result = tail.then(operation, operation); + tail = result.then( + () => undefined, + () => undefined, + ); + return await result; + } + + async function initialize(): Promise { + if (initialized) return; + await mkdir(dirname(statePath), { recursive: true, mode: 0o700 }); + let raw: string; + try { + raw = await readFile(statePath, "utf8"); + } catch (cause) { + if (isErrnoCode(cause, "ENOENT")) { + initialized = true; + return; } - const unitDir = unitDirOf(unit.allocationId, unit.generation); - try { - await rm(unitDir, { recursive: true, force: true }); - // The allocation's own directory goes once its last unit does; - // a still-running newer generation keeps it, which is why this - // is an rmdir and not a recursive remove. - await rmdir(allocationDirOf(unit.allocationId)).catch(ignoreNonEmptyOrMissing); - } catch (error) { - throw operationalFailure( - error, - "allocation_dir_cleanup_failed", - `unable to remove unit directory ${unitDir}`, - unit.allocationId, - ); + throw new Error(`Unable to read sidecar allocation state ${statePath}`, { + cause, + }); + } + let parsedJSON: unknown; + try { + parsedJSON = JSON.parse(raw); + } catch (cause) { + throw new Error(`Invalid JSON in sidecar allocation state ${statePath}`, { cause }); + } + const parsed = StateFile(parsedJSON); + if (parsed instanceof type.errors) { + throw new Error(`Invalid sidecar allocation state: ${parsed.summary}`); + } + for (const record of parsed.records) { + if (records.has(record.allocationId)) { + throw new Error(`Duplicate sidecar allocation state for ${record.allocationId}`); } - }, + records.set(record.allocationId, record); + } + initialized = true; + } - async findUnitsByAllocation(allocationId: string): Promise { - const allocationDir = allocationDirOf(allocationId); - let entries: string[]; - try { - entries = await readdir(allocationDir); - } catch (error) { - if (isErrnoCode(error, "ENOENT")) return []; - throw operationalFailure( - error, - "allocation_dir_read_failed", - `unable to list units under ${allocationDir}`, - allocationId, - ); - } + async function persist(nextRecords: ReadonlyMap): Promise { + const contents = `${JSON.stringify( + { version: 1, records: Array.from(nextRecords.values()) }, + null, + 2, + )}\n`; + await writeFileAtomic(statePath, contents); + } - const refs: string[] = []; - for (const entry of entries) { - const generation = generationOf(entry); - if (generation === null) continue; - const pid = await readPidFile(resolve(allocationDir, entry), allocationId); - if (pid === null || !runner.isAlive(pid)) continue; - refs.push(externalRefOf(allocationId, generation, pid)); - } - return refs; - }, + return { + observeEnsure: (args) => + runExclusive(async () => { + await initialize(); + const existing = records.get(args.allocationId); + if (existing !== undefined) { + if (existing.sidecarId !== args.sidecarId) { + return { + kind: "rejected", + code: "request_conflict", + message: `Allocation ${args.allocationId} is already bound to another sidecar`, + }; + } + if (existing.generation > args.generation) { + return { + kind: "rejected", + code: "stale_generation", + message: `Generation ${String(args.generation)} is older than ${String(existing.generation)}`, + }; + } + if (existing.generation === args.generation) { + if (existing.desiredState === "destroyed") { + return { + kind: "rejected", + code: "generation_destroyed", + message: `Generation ${String(args.generation)} has already been destroyed`, + }; + } + return { kind: "observed", record: existing }; + } + } + const record: AllocationRecord = { + allocationId: args.allocationId, + sidecarId: args.sidecarId, + generation: args.generation, + desiredState: "ensured", + externalRef: null, + updatedAt: new Date().toISOString(), + }; + const nextRecords = new Map(records); + nextRecords.set(args.allocationId, record); + await persist(nextRecords); + records.set(args.allocationId, record); + return { kind: "observed", record }; + }), + + observeDestroy: (args) => + runExclusive(async () => { + await initialize(); + const existing = records.get(args.allocationId); + if (existing !== undefined) { + if (existing.sidecarId !== args.sidecarId) { + return { + kind: "rejected", + code: "request_conflict", + message: `Allocation ${args.allocationId} is already bound to another sidecar`, + }; + } + if (existing.generation > args.generation) { + return { + kind: "rejected", + code: "stale_generation", + message: `Generation ${String(args.generation)} is older than ${String(existing.generation)}`, + }; + } + if (existing.generation === args.generation && existing.desiredState === "destroyed") { + return { kind: "observed", record: existing }; + } + } + const record: AllocationRecord = { + allocationId: args.allocationId, + sidecarId: args.sidecarId, + generation: args.generation, + desiredState: "destroyed", + externalRef: existing?.externalRef ?? null, + updatedAt: new Date().toISOString(), + }; + const nextRecords = new Map(records); + nextRecords.set(args.allocationId, record); + await persist(nextRecords); + records.set(args.allocationId, record); + return { kind: "observed", record }; + }), + + recordUnit: (args) => + runExclusive(async () => { + await initialize(); + const record = records.get(args.allocationId); + if ( + record === undefined || + record.generation !== args.generation || + record.desiredState !== "ensured" + ) { + return false; + } + const updated: AllocationRecord = { + ...record, + externalRef: args.externalRef, + updatedAt: new Date().toISOString(), + }; + const nextRecords = new Map(records); + nextRecords.set(args.allocationId, updated); + await persist(nextRecords); + records.set(args.allocationId, updated); + return true; + }), }; } -/** A sidecar learns everything else over the wire; nothing else of the hub's env is inherited. */ +async function writeFileAtomic(path: string, contents: string): Promise { + await mkdir(dirname(path), { recursive: true, mode: 0o700 }); + const temporaryPath = `${path}.tmp.${String(process.pid)}.${randomBytes(8).toString("hex")}`; + try { + const handle = await open(temporaryPath, "wx", 0o600); + try { + await handle.writeFile(contents); + await handle.sync(); + } finally { + await handle.close(); + } + await rename(temporaryPath, path); + const directory = await open(dirname(path), "r"); + try { + await directory.sync(); + } finally { + await directory.close(); + } + } catch (cause) { + await unlink(temporaryPath).catch(() => undefined); + throw cause; + } +} + +const PID_FILE_NAME = "sidecar.pid"; +const SIDECAR_DATA_DIR_NAME = "data"; +const UNIT_DIR_PREFIX = "gen-"; +// Allocation ids name directories on disk, so reject path traversal characters. +const ALLOCATION_ID_PATTERN = /^[A-Za-z0-9._-]+$/; +const TERMINATION_GRACE_MS = 5_000; +const TERMINATION_POLL_MS = 100; + +type StartUnitArgs = { + readonly allocationId: string; + readonly sidecarId: string; + readonly generation: number; + readonly token: string; + readonly hubWebSocketUrl: string; +}; + function sidecarEnvFor( args: StartUnitArgs, sidecarDataDir: string, @@ -309,12 +412,10 @@ function sidecarEnvFor( operation: "process-provisioner.missing_path", extra: { allocationId: args.allocationId }, }); - throw new BackendOperationError("missing_path", error.message, false); + throw new ProvisionerOperationError("missing_path", error.message, false); } const home = process.env["HOME"]; const tmpdir = process.env["TMPDIR"]; - // Operator config, not tenant data: without it a sidecar has no adapter - // for a provider the stock runtime does not serve (Codex, xAI). const adapterManifest = process.env["SIDECAR_ADAPTER_MANIFEST"]; return { SIDECAR_DATA_DIR: sidecarDataDir, @@ -329,25 +430,16 @@ function sidecarEnvFor( }; } -/** - * Sends SIGTERM and waits out the grace period so the sidecar can flush - * its own state, escalating to SIGKILL only if it is still alive. A pid - * that is already gone is success — destroy has to stay idempotent. - */ async function terminate(runner: SidecarProcessRunner, pid: number): Promise { if (!runner.isAlive(pid)) return; runner.signal(pid, "SIGTERM"); const deadline = Date.now() + TERMINATION_GRACE_MS; while (runner.isAlive(pid) && Date.now() < deadline) { - await sleep(TERMINATION_POLL_MS); + await new Promise((done) => setTimeout(done, TERMINATION_POLL_MS)); } if (runner.isAlive(pid)) runner.signal(pid, "SIGKILL"); } -function sleep(ms: number): Promise { - return new Promise((done) => setTimeout(done, ms)); -} - async function readPidFile(unitDir: string, allocationId: string): Promise { let raw: string; try { @@ -402,24 +494,19 @@ function ignoreNonEmptyOrMissing(error: unknown): void { throw error; } -/** - * Reports a caught OS failure with its allocation context and returns the - * classified error to throw, so the shared provisioner core turns it into - * a retryable rejection carrying a `refId` support can quote. - */ function operationalFailure( error: unknown, code: string, message: string, allocationId: string, -): BackendOperationError { - if (error instanceof BackendOperationError) return error; +): ProvisionerOperationError { + if (error instanceof ProvisionerOperationError) return error; const refId = reportError(error, { operation: `process-provisioner.${code}`, extra: { allocationId }, }); const detail = error instanceof Error ? error.message : String(error); - return new BackendOperationError(code, `${message}: ${detail} (${refId})`, true); + return new ProvisionerOperationError(code, `${message}: ${detail} (${refId})`, true); } function isErrnoCode(error: unknown, code: string): boolean { @@ -430,12 +517,16 @@ const PROVISIONER_API_VERSION = 2 as const; export const PROCESS_PROVISIONER_ID = "process"; -/** - * Interchange adopts a probe's allocation for the deployment when both - * provisioners share id/apiVersion/fingerprint, which skips redeploying at - * the current pin — so the hub runs a probe instance whose fingerprint - * never matches. - */ +const PROCESS_CAPABILITIES: readonly SidecarCapabilityDeclaration[] = [ + { capability: "runtime:sidecar", state: "available" }, + { capability: "isolation:process", state: "available" }, + { capability: "isolation:container", state: "blocked" }, + { capability: "isolation:vm", state: "blocked" }, +]; + +// Interchange adopts a probe's allocation for the deployment when both +// provisioners share id/apiVersion/fingerprint, which skips redeploying at +// the current pin — so the hub runs a probe instance with a distinct role. export type ProcessProvisionerRole = "deployment" | "probe"; export type CreateProcessSidecarProvisionerOpts = { @@ -445,12 +536,8 @@ export type CreateProcessSidecarProvisionerOpts = { readonly store?: AllocationStateStore; }; -/** - * The binding fingerprint names the backend kind and role only. Hub port, - * install path, and sidecar entry location change across restarts without - * changing what the backend can host; pinning them strands every existing - * allocation, which the reconciler can neither release nor replace. - */ +// The binding fingerprint names the backend kind and role only; pinning port +// or install path would strand existing allocations across hub restarts. export function createProcessSidecarProvisioner( opts: CreateProcessSidecarProvisionerOpts, ): SidecarProvisioner { @@ -458,12 +545,294 @@ export function createProcessSidecarProvisioner( const runner = opts.runner ?? createBunSidecarProcessRunner(); const store = opts.store ?? createAllocationStateStore(config.stateFilePath); - return createSidecarProvisioner({ + function allocationDirOf(allocationId: string): string { + if (!ALLOCATION_ID_PATTERN.test(allocationId)) { + throw new ProvisionerOperationError( + "invalid_allocation_id", + `allocationId ${JSON.stringify(allocationId)} is not a valid directory name`, + false, + ); + } + return resolve(config.allocationsDir, allocationId); + } + + function unitDirOf(allocationId: string, generation: number): string { + return resolve(allocationDirOf(allocationId), `${UNIT_DIR_PREFIX}${String(generation)}`); + } + + async function startUnit(args: StartUnitArgs): Promise { + const unitDir = unitDirOf(args.allocationId, args.generation); + const sidecarDataDir = resolve(unitDir, SIDECAR_DATA_DIR_NAME); + const env = sidecarEnvFor(args, sidecarDataDir, config.sidecarCredentialEncryptionKey); + try { + await mkdir(sidecarDataDir, { recursive: true, mode: 0o700 }); + } catch (error) { + throw operationalFailure( + error, + "allocation_dir_failed", + `unable to create unit directory ${unitDir}`, + args.allocationId, + ); + } + + let pid: number; + try { + pid = runner.spawn({ + command: [config.runtimePath, config.sidecarEntryPath], + cwd: dirname(config.sidecarEntryPath), + env, + }); + } catch (error) { + throw operationalFailure( + error, + "sidecar_spawn_failed", + `unable to spawn ${config.sidecarEntryPath}`, + args.allocationId, + ); + } + + try { + await writeFile(resolve(unitDir, PID_FILE_NAME), `${String(pid)}\n`, { + mode: 0o600, + }); + } catch (error) { + await terminate(runner, pid); + throw operationalFailure( + error, + "pid_file_write_failed", + `unable to record pid ${String(pid)} for allocation ${args.allocationId}`, + args.allocationId, + ); + } + return externalRefOf(args.allocationId, args.generation, pid); + } + + async function stopUnit(externalRef: string): Promise { + const unit = parseExternalRef(externalRef); + if (unit === null) { + throw new ProvisionerOperationError( + "invalid_external_ref", + `external ref ${JSON.stringify(externalRef)} is not ::`, + false, + ); + } + try { + await terminate(runner, unit.pid); + } catch (error) { + throw operationalFailure( + error, + "sidecar_terminate_failed", + `unable to terminate pid ${String(unit.pid)}`, + unit.allocationId, + ); + } + const unitDir = unitDirOf(unit.allocationId, unit.generation); + try { + await rm(unitDir, { recursive: true, force: true }); + // Non-recursive rmdir so a still-running newer generation under the + // same allocation keeps the parent directory. + await rmdir(allocationDirOf(unit.allocationId)).catch(ignoreNonEmptyOrMissing); + } catch (error) { + throw operationalFailure( + error, + "allocation_dir_cleanup_failed", + `unable to remove unit directory ${unitDir}`, + unit.allocationId, + ); + } + } + + async function findUnitsByAllocation(allocationId: string): Promise { + const allocationDir = allocationDirOf(allocationId); + let entries: string[]; + try { + entries = await readdir(allocationDir); + } catch (error) { + if (isErrnoCode(error, "ENOENT")) return []; + throw operationalFailure( + error, + "allocation_dir_read_failed", + `unable to list units under ${allocationDir}`, + allocationId, + ); + } + + const refs: string[] = []; + for (const entry of entries) { + const generation = generationOf(entry); + if (generation === null) continue; + const pid = await readPidFile(resolve(allocationDir, entry), allocationId); + if (pid === null || !runner.isAlive(pid)) continue; + refs.push(externalRefOf(allocationId, generation, pid)); + } + return refs; + } + + async function sweepObsoleteUnits(allocationId: string, keepExternalRef: string): Promise { + const candidates = await findUnitsByAllocation(allocationId); + const obsolete = candidates.filter((externalRef) => externalRef !== keepExternalRef); + for (const externalRef of obsolete) { + try { + await stopUnit(externalRef); + } catch (error) { + reportError(error, { + operation: "process-provisioner.sweep_obsolete_unit", + extra: { allocationId, externalRef }, + }); + log.error`failed to sweep obsolete unit ${externalRef} for allocation ${allocationId}: ${ + error instanceof Error ? error.message : String(error) + }`; + } + } + } + + // Coalesce concurrent ensure() calls for the same allocation so racing + // reconciler retries cannot spawn duplicate processes. + const inFlightEnsures = new Map>(); + + async function ensureOnce(request: EnsureSidecarRequest): Promise { + const validation = validateEnsureRequest(request); + if (validation !== null) { + return rejected("invalid_ensure_request", validation, false); + } + + const observation = await store.observeEnsure({ + allocationId: request.allocationId, + sidecarId: request.sidecarId, + generation: request.generation, + }); + if (observation.kind === "rejected") { + return rejected(observation.code, observation.message, false); + } + if (observation.record.externalRef !== null) { + await sweepObsoleteUnits(request.allocationId, observation.record.externalRef); + return { + kind: "accepted", + externalRef: observation.record.externalRef, + }; + } + + let externalRef: string; + try { + externalRef = await startUnit({ + allocationId: request.allocationId, + sidecarId: request.sidecarId, + generation: request.generation, + token: request.token, + hubWebSocketUrl: request.hubWebSocketUrl, + }); + } catch (error) { + return rejected(...classify(error, "start_unit_failed")); + } + + const recorded = await store.recordUnit({ + allocationId: request.allocationId, + generation: request.generation, + externalRef, + }); + if (!recorded) { + await stopUnit(externalRef); + return rejected( + "stale_generation", + `Generation ${String(request.generation)} was superseded while the unit was starting`, + false, + ); + } + await sweepObsoleteUnits(request.allocationId, externalRef); + return { kind: "accepted", externalRef }; + } + + return { id: PROCESS_PROVISIONER_ID, apiVersion: PROVISIONER_API_VERSION, bindingFingerprint: `process:v1:${opts.role}`, - capabilities: sidecarCapabilityDeclarations("process"), - backend: createProcessBackend(runner, config), - store, - }); + capabilities: PROCESS_CAPABILITIES, + + ensure(request: EnsureSidecarRequest): Promise { + const key = request.allocationId; + const inFlight = inFlightEnsures.get(key); + if (inFlight !== undefined) { + return inFlight.then(() => ensureOnce(request)); + } + const operation = ensureOnce(request); + inFlightEnsures.set(key, operation); + const clear = () => { + if (inFlightEnsures.get(key) === operation) { + inFlightEnsures.delete(key); + } + }; + void operation.then(clear, clear); + return operation; + }, + + async destroy(request: DestroySidecarRequest): Promise { + const validation = validateDestroyRequest(request); + if (validation !== null) { + return rejected("invalid_destroy_request", validation, false); + } + + const observation = await store.observeDestroy({ + allocationId: request.allocationId, + sidecarId: request.sidecarId, + generation: request.generation, + }); + if (observation.kind === "rejected") { + return rejected(observation.code, observation.message, false); + } + + const externalRefs = new Set(); + if (request.externalRef !== undefined) { + externalRefs.add(request.externalRef); + } else if (observation.record.externalRef !== null) { + externalRefs.add(observation.record.externalRef); + } else { + for (const externalRef of await findUnitsByAllocation(request.allocationId)) { + externalRefs.add(externalRef); + } + } + + for (const externalRef of externalRefs) { + try { + await stopUnit(externalRef); + } catch (error) { + return rejected(...classify(error, "destroy_unit_failed")); + } + } + return { kind: "destroyed", cleanup: "confirmed" }; + }, + }; +} + +function validateEnsureRequest(request: EnsureSidecarRequest): string | null { + if (request.allocationId === "") return "allocationId must not be empty"; + if (request.sidecarId === "") return "sidecarId must not be empty"; + if (request.token === "") return "token must not be empty"; + if (request.hubWebSocketUrl === "") return "hubWebSocketUrl must not be empty"; + if (!Number.isInteger(request.generation) || request.generation < 0) { + return "generation must be a non-negative integer"; + } + return null; +} + +function validateDestroyRequest(request: DestroySidecarRequest): string | null { + if (request.allocationId === "") return "allocationId must not be empty"; + if (request.sidecarId === "") return "sidecarId must not be empty"; + if (!Number.isInteger(request.generation) || request.generation < 0) { + return "generation must be a non-negative integer"; + } + return null; +} + +function rejected(code: string, message: string, retryable: boolean) { + return { kind: "rejected" as const, code, message, retryable }; +} + +function classify( + error: unknown, + defaultCode: string, +): readonly [code: string, message: string, retryable: boolean] { + if (error instanceof ProvisionerOperationError) { + return [error.code, error.message, error.retryable]; + } + return [defaultCode, error instanceof Error ? error.message : String(error), true]; } diff --git a/apps/hub/src/provisioners/sandbox-sidecar.ts b/apps/hub/src/provisioners/sandbox-sidecar.ts deleted file mode 100644 index c14854ec01..0000000000 --- a/apps/hub/src/provisioners/sandbox-sidecar.ts +++ /dev/null @@ -1,555 +0,0 @@ -// Backend-agnostic lifecycle: a backend supplies only start/stop/find and -// identity; this module owns correctness under concurrent and -// out-of-order ensure()/destroy() calls. -import { randomBytes } from "node:crypto"; -import { mkdir, open, readFile, rename, unlink } from "node:fs/promises"; -import { dirname } from "node:path"; - -import { sha256 } from "@intx/crypto"; -import { getLogger } from "@intx/log"; -import type { - DestroySidecarRequest, - DestroySidecarResult, - EnsureSidecarRequest, - EnsureSidecarResult, - SidecarProvisioner, -} from "@intx/hub-sessions"; -import type { SidecarCapabilityDeclaration } from "@intx/types"; -import { type } from "arktype"; - -const log = getLogger(["hub", "sandbox-sidecar"]); - -export type StartUnitArgs = { - readonly allocationId: string; - readonly sidecarId: string; - readonly generation: number; - readonly token: string; - readonly hubWebSocketUrl: string; -}; - -export interface SidecarBackend { - /** Starts a compute unit for this allocation, returning its external ref. */ - startUnit(args: StartUnitArgs): Promise; - /** Stops and removes a unit, treating "already gone" as success. */ - stopUnit(externalRef: string): Promise; - /** Lists the external refs of every unit still labeled for this allocation. */ - findUnitsByAllocation(allocationId: string): Promise; -} - -/** Lets a backend report a classified, retryable-or-not failure instead of a generic Error. */ -export class BackendOperationError extends Error { - readonly code: string; - readonly retryable: boolean; - - constructor(code: string, message: string, retryable: boolean) { - super(message); - this.name = "BackendOperationError"; - this.code = code; - this.retryable = retryable; - } -} - -/** - * A ladder: a container also isolates the process, a VM also isolates the - * container. A backend declares every rung it reaches as available so a - * deployment requiring isolation:vm fails closed rather than landing quietly - * on a shared kernel. - */ -export const SIDECAR_ISOLATION_LEVELS = ["process", "container", "vm"] as const; - -export type SidecarIsolationLevel = (typeof SIDECAR_ISOLATION_LEVELS)[number]; - -/** Every shipped backend runs a workbench sidecar; nothing else does. */ -export const SIDECAR_RUNTIME_CAPABILITY = "runtime:sidecar"; - -export function sidecarCapabilityDeclarations( - isolation: SidecarIsolationLevel, -): readonly SidecarCapabilityDeclaration[] { - const reached = SIDECAR_ISOLATION_LEVELS.indexOf(isolation); - return [ - { capability: SIDECAR_RUNTIME_CAPABILITY, state: "available" }, - ...SIDECAR_ISOLATION_LEVELS.map((level, index) => ({ - capability: `isolation:${level}`, - state: index <= reached ? ("available" as const) : ("blocked" as const), - })), - ]; -} - -const AllocationRecord = type({ - allocationId: "string > 0", - sidecarId: "string > 0", - generation: "number.integer >= 0", - desiredState: "'ensured' | 'destroyed'", - externalRef: "string | null", - tokenHashSha256: "string | null", - updatedAt: "string > 0", -}); -export type AllocationRecord = typeof AllocationRecord.infer; - -const StateFile = type({ - version: "1", - records: AllocationRecord.array(), -}); - -type ObserveEnsureResult = - | { readonly kind: "observed"; readonly record: AllocationRecord } - | { - readonly kind: "rejected"; - readonly code: "stale_generation" | "generation_destroyed" | "request_conflict"; - readonly message: string; - }; - -type ObserveDestroyResult = - | { readonly kind: "observed"; readonly record: AllocationRecord } - | { - readonly kind: "rejected"; - readonly code: "stale_generation" | "request_conflict"; - readonly message: string; - }; - -export interface AllocationStateStore { - /** - * Records the intent to ensure this generation. Rejects a generation - * older than one already observed, and rejects if this allocationId's - * current generation was already tombstoned as destroyed. - */ - observeEnsure(args: { - allocationId: string; - sidecarId: string; - generation: number; - }): Promise; - /** - * Records the intent to destroy this generation as a tombstone, so a - * delayed/older ensure() observed afterward is rejected. - */ - observeDestroy(args: { - allocationId: string; - sidecarId: string; - generation: number; - }): Promise; - /** - * Persists the compute unit started for an ensured generation. Returns - * false (without writing) if the allocation was superseded or - * destroyed while the unit was starting. - */ - recordUnit(args: { - allocationId: string; - generation: number; - externalRef: string; - tokenHashSha256: string; - }): Promise; - getRecord(allocationId: string): Promise; -} - -type CreateAllocationStateStoreOpts = { - readonly now?: () => Date; - readonly writeStateFile?: (path: string, contents: string) => Promise; -}; - -export function createAllocationStateStore( - statePath: string, - opts: CreateAllocationStateStoreOpts = {}, -): AllocationStateStore { - const now = opts.now ?? (() => new Date()); - const writeStateFile = opts.writeStateFile ?? writeFileAtomic; - const records = new Map(); - let initialized = false; - let tail = Promise.resolve(); - - async function runExclusive(operation: () => Promise): Promise { - const result = tail.then(operation, operation); - tail = result.then( - () => undefined, - () => undefined, - ); - return await result; - } - - async function initialize(): Promise { - if (initialized) return; - await mkdir(dirname(statePath), { recursive: true, mode: 0o700 }); - let raw: string; - try { - raw = await readFile(statePath, "utf8"); - } catch (cause) { - if (isErrorCode(cause, "ENOENT")) { - initialized = true; - return; - } - throw new Error(`Unable to read sidecar allocation state ${statePath}`, { - cause, - }); - } - let parsedJSON: unknown; - try { - parsedJSON = JSON.parse(raw); - } catch (cause) { - throw new Error(`Invalid JSON in sidecar allocation state ${statePath}`, { - cause, - }); - } - const parsed = StateFile(parsedJSON); - if (parsed instanceof type.errors) { - throw new Error(`Invalid sidecar allocation state: ${parsed.summary}`); - } - for (const record of parsed.records) { - if (records.has(record.allocationId)) { - throw new Error(`Duplicate sidecar allocation state for ${record.allocationId}`); - } - records.set(record.allocationId, record); - } - initialized = true; - } - - async function persist(nextRecords: ReadonlyMap): Promise { - const contents = `${JSON.stringify( - { version: 1, records: Array.from(nextRecords.values()) }, - null, - 2, - )}\n`; - await writeStateFile(statePath, contents); - } - - return { - observeEnsure: (args) => - runExclusive(async () => { - await initialize(); - const existing = records.get(args.allocationId); - if (existing !== undefined) { - if (existing.sidecarId !== args.sidecarId) { - return { - kind: "rejected", - code: "request_conflict", - message: `Allocation ${args.allocationId} is already bound to another sidecar`, - }; - } - if (existing.generation > args.generation) { - return { - kind: "rejected", - code: "stale_generation", - message: `Generation ${String(args.generation)} is older than ${String(existing.generation)}`, - }; - } - if (existing.generation === args.generation) { - if (existing.desiredState === "destroyed") { - return { - kind: "rejected", - code: "generation_destroyed", - message: `Generation ${String(args.generation)} has already been destroyed`, - }; - } - return { kind: "observed", record: existing }; - } - } - const record: AllocationRecord = { - allocationId: args.allocationId, - sidecarId: args.sidecarId, - generation: args.generation, - desiredState: "ensured", - externalRef: null, - tokenHashSha256: null, - updatedAt: now().toISOString(), - }; - const nextRecords = new Map(records); - nextRecords.set(args.allocationId, record); - await persist(nextRecords); - records.set(args.allocationId, record); - return { kind: "observed", record }; - }), - - observeDestroy: (args) => - runExclusive(async () => { - await initialize(); - const existing = records.get(args.allocationId); - if (existing !== undefined) { - if (existing.sidecarId !== args.sidecarId) { - return { - kind: "rejected", - code: "request_conflict", - message: `Allocation ${args.allocationId} is already bound to another sidecar`, - }; - } - if (existing.generation > args.generation) { - return { - kind: "rejected", - code: "stale_generation", - message: `Generation ${String(args.generation)} is older than ${String(existing.generation)}`, - }; - } - if (existing.generation === args.generation && existing.desiredState === "destroyed") { - return { kind: "observed", record: existing }; - } - } - const record: AllocationRecord = { - allocationId: args.allocationId, - sidecarId: args.sidecarId, - generation: args.generation, - desiredState: "destroyed", - externalRef: existing?.externalRef ?? null, - tokenHashSha256: existing?.tokenHashSha256 ?? null, - updatedAt: now().toISOString(), - }; - const nextRecords = new Map(records); - nextRecords.set(args.allocationId, record); - await persist(nextRecords); - records.set(args.allocationId, record); - return { kind: "observed", record }; - }), - - recordUnit: (args) => - runExclusive(async () => { - await initialize(); - const record = records.get(args.allocationId); - if ( - record === undefined || - record.generation !== args.generation || - record.desiredState !== "ensured" - ) { - return false; - } - const updated: AllocationRecord = { - ...record, - externalRef: args.externalRef, - tokenHashSha256: args.tokenHashSha256, - updatedAt: now().toISOString(), - }; - const nextRecords = new Map(records); - nextRecords.set(args.allocationId, updated); - await persist(nextRecords); - records.set(args.allocationId, updated); - return true; - }), - - getRecord: (allocationId) => - runExclusive(async () => { - await initialize(); - return records.get(allocationId) ?? null; - }), - }; -} - -async function writeFileAtomic(path: string, contents: string): Promise { - await mkdir(dirname(path), { recursive: true, mode: 0o700 }); - const temporaryPath = `${path}.tmp.${String(process.pid)}.${randomBytes(8).toString("hex")}`; - try { - const handle = await open(temporaryPath, "wx", 0o600); - try { - await handle.writeFile(contents); - await handle.sync(); - } finally { - await handle.close(); - } - await rename(temporaryPath, path); - const directory = await open(dirname(path), "r"); - try { - await directory.sync(); - } finally { - await directory.close(); - } - } catch (cause) { - await unlink(temporaryPath).catch(() => undefined); - throw cause; - } -} - -function isErrorCode(error: unknown, code: string): boolean { - return error instanceof Error && "code" in error && error.code === code; -} - -export type CreateSidecarProvisionerOpts = { - readonly id: string; - readonly apiVersion: 2; - readonly bindingFingerprint: string; - /** - * Empty matches any deployment stating no requirement — the behaviour - * every allocation had before capability selection replaced placement. - */ - readonly capabilities: readonly SidecarCapabilityDeclaration[]; - readonly backend: SidecarBackend; - readonly store: AllocationStateStore; -}; - -export function createSidecarProvisioner(opts: CreateSidecarProvisionerOpts): SidecarProvisioner { - const { backend, store } = opts; - - // Coalesces concurrent ensure() calls for the same allocation so two - // callers racing the reconciler's ~1s retry loop cannot both pass - // observeEnsure() before either has recorded a unit, each starting its - // own compute unit. - const inFlightEnsures = new Map>(); - - async function ensureOnce(request: EnsureSidecarRequest): Promise { - const validation = validateEnsureRequest(request); - if (validation !== null) { - return rejected("invalid_ensure_request", validation, false); - } - - const observation = await store.observeEnsure({ - allocationId: request.allocationId, - sidecarId: request.sidecarId, - generation: request.generation, - }); - if (observation.kind === "rejected") { - return rejected(observation.code, observation.message, false); - } - if (observation.record.externalRef !== null) { - await sweepObsoleteUnits(backend, request.allocationId, observation.record.externalRef); - return { - kind: "accepted", - externalRef: observation.record.externalRef, - }; - } - - const tokenHashSha256 = await hashToken(request.token); - let externalRef: string; - try { - externalRef = await backend.startUnit({ - allocationId: request.allocationId, - sidecarId: request.sidecarId, - generation: request.generation, - token: request.token, - hubWebSocketUrl: request.hubWebSocketUrl, - }); - } catch (error) { - return rejected(...classify(error, "start_unit_failed")); - } - - const recorded = await store.recordUnit({ - allocationId: request.allocationId, - generation: request.generation, - externalRef, - tokenHashSha256, - }); - if (!recorded) { - await backend.stopUnit(externalRef); - return rejected( - "stale_generation", - `Generation ${String(request.generation)} was superseded while the unit was starting`, - false, - ); - } - // Covers three cases indistinguishable from here (superseded generation, - // a losing concurrent ensure(), a crash between startUnit and - // recordUnit) with one sweep by allocation. - await sweepObsoleteUnits(backend, request.allocationId, externalRef); - return { kind: "accepted", externalRef }; - } - - return { - id: opts.id, - apiVersion: opts.apiVersion, - bindingFingerprint: opts.bindingFingerprint, - capabilities: opts.capabilities, - - ensure(request: EnsureSidecarRequest): Promise { - const key = request.allocationId; - const inFlight = inFlightEnsures.get(key); - if (inFlight !== undefined) { - return inFlight.then(() => ensureOnce(request)); - } - const operation = ensureOnce(request); - inFlightEnsures.set(key, operation); - const clear = () => { - if (inFlightEnsures.get(key) === operation) { - inFlightEnsures.delete(key); - } - }; - void operation.then(clear, clear); - return operation; - }, - - async destroy(request: DestroySidecarRequest): Promise { - const validation = validateDestroyRequest(request); - if (validation !== null) { - return rejected("invalid_destroy_request", validation, false); - } - - const observation = await store.observeDestroy({ - allocationId: request.allocationId, - sidecarId: request.sidecarId, - generation: request.generation, - }); - if (observation.kind === "rejected") { - return rejected(observation.code, observation.message, false); - } - - const externalRefs = new Set(); - if (request.externalRef !== undefined) { - externalRefs.add(request.externalRef); - } else if (observation.record.externalRef !== null) { - externalRefs.add(observation.record.externalRef); - } else { - for (const externalRef of await backend.findUnitsByAllocation(request.allocationId)) { - externalRefs.add(externalRef); - } - } - - for (const externalRef of externalRefs) { - try { - await backend.stopUnit(externalRef); - } catch (error) { - return rejected(...classify(error, "destroy_unit_failed")); - } - } - return { kind: "destroyed", cleanup: "confirmed" }; - }, - }; -} - -/** Best-effort: a sweep failure only leaks a unit, so it's logged rather than failing ensure(). */ -async function sweepObsoleteUnits( - backend: SidecarBackend, - allocationId: string, - keepExternalRef: string, -): Promise { - const candidates = await backend.findUnitsByAllocation(allocationId); - const obsolete = candidates.filter((externalRef) => externalRef !== keepExternalRef); - for (const externalRef of obsolete) { - try { - await backend.stopUnit(externalRef); - } catch (error) { - log.error`failed to sweep obsolete unit ${externalRef} for allocation ${allocationId}: ${ - error instanceof Error ? error.message : String(error) - }`; - } - } -} - -async function hashToken(token: string): Promise { - const digest = await sha256(token); - return Buffer.from(digest).toString("hex"); -} - -function validateEnsureRequest(request: EnsureSidecarRequest): string | null { - if (request.allocationId === "") return "allocationId must not be empty"; - if (request.sidecarId === "") return "sidecarId must not be empty"; - if (request.token === "") return "token must not be empty"; - if (request.hubWebSocketUrl === "") return "hubWebSocketUrl must not be empty"; - if (!Number.isInteger(request.generation) || request.generation < 0) { - return "generation must be a non-negative integer"; - } - return null; -} - -function validateDestroyRequest(request: DestroySidecarRequest): string | null { - if (request.allocationId === "") return "allocationId must not be empty"; - if (request.sidecarId === "") return "sidecarId must not be empty"; - if (!Number.isInteger(request.generation) || request.generation < 0) { - return "generation must be a non-negative integer"; - } - return null; -} - -function rejected(code: string, message: string, retryable: boolean) { - return { kind: "rejected" as const, code, message, retryable }; -} - -function classify( - error: unknown, - defaultCode: string, -): readonly [code: string, message: string, retryable: boolean] { - if (error instanceof BackendOperationError) { - return [error.code, error.message, error.retryable]; - } - return [defaultCode, error instanceof Error ? error.message : String(error), true]; -} diff --git a/apps/hub/src/server.ts b/apps/hub/src/server.ts index 9da062819c..704d58da54 100644 --- a/apps/hub/src/server.ts +++ b/apps/hub/src/server.ts @@ -1,3 +1,5 @@ +import path from "node:path"; +import { createEnvKeyCredentialCipher, generateKeyPair } from "@intx/crypto"; import { createDB, createGrantStore, @@ -9,30 +11,7 @@ import { resolveSenderKey, withExecutableWorkflowRun, } from "@intx/db"; -import { - asset as assetTable, - principal as principalTable, - tenant as tenantTable, -} from "@intx/db/schema"; -import { and, eq } from "drizzle-orm"; -import { createEnvKeyCredentialCipher } from "@intx/crypto"; -import { - type AdapterManifest, - hexDecode, - hexEncode, - LifecycleDuration, - lifecycleDurationMs, - parseAdapterManifestEnv, - type ResolvedWorkflowLifecyclePolicy, - type SidecarCapabilityRule, -} from "@intx/types"; -import { - createApp, - createAuth, - createMailTriggeredRunGrantsMaterializer, - createRequireGrant, - type TenantEnv, -} from "@intx/hub-api"; +import { createApp, createAuth, createMailTriggeredRunGrantsMaterializer } from "@intx/hub-api"; import { attachSidecarProvisioners, createAgentRepoStore, @@ -40,107 +19,49 @@ import { createEventCollectorRegistry, createHubSessionLookups, createHubSessionOrchestrator, + createParkedRunClassifier, + createReconciliationScheduler, createSessionService, createSidecarAllocationReconciler, + createSidecarCredentialResolver, createSidecarPluginRegistry, createSidecarRouter, - createSidecarCredentialResolver, createWorkflowAllocationService, + createWorkflowDispatchProjection, createWorkflowDispatchService, - createReconciliationScheduler, + createWorkflowHistoryReceiveTracker, createWorkflowLifecycleService, - createWorkflowDispatchProjection, - DEFAULT_WORKFLOW_PROJECTION_CONCURRENCY, createWorkflowRunReader, - createParkedRunClassifier, - createWorkflowHistoryReceiveTracker, - recoverSenderDeploy, DEFAULT_SIDECAR_ALLOCATION_CONCURRENCY, + DEFAULT_WORKFLOW_PROJECTION_CONCURRENCY, pushCredentialReconcile, - pushSourceUpdates, + recoverSenderDeploy, WORKSPACE_BUILTINS_REGISTRY, type SidecarLookups, type SidecarProvisioner, type SidecarProvisionerChooser, type WsHandle, } from "@intx/hub-sessions"; -import { generateKeyPair } from "@intx/crypto"; -import { timeWindowEvaluator } from "@intx/authz"; -import type { ConditionRegistry } from "@intx/types/authz"; +import { getLogger, setup } from "@intx/log"; +import { + type AdapterManifest, + hexDecode, + hexEncode, + LifecycleDuration, + lifecycleDurationMs, + parseAdapterManifestEnv, + type ResolvedWorkflowLifecyclePolicy, + type SidecarCapabilityRule, +} from "@intx/types"; import { MAX_SIDECAR_FRAME_BYTES } from "@intx/types/sidecar"; import { upgradeWebSocket, websocket } from "hono/bun"; -import { setup, getLogger } from "@intx/log"; -import { Hono } from "hono"; - -// Everything above this line is upstream Interchange's server.ts, verbatim; -// see AGENTS.md's "plain Interchange tenant" ruling. -import { - createInMemoryMailboxEventBus, - createMailboxDb, - createMailboxPersist, - mountMailbox, -} from "@corbits/mailbox"; -import { - createMemory, - loadMemoryConfig, - mountWorkflowMemory, - type WorkflowMemoryEnv, -} from "@corbits/memory"; -import { - createOAuthLoginStore, - createOAuthTokenRefresher, - mountOAuthCallback, - mountOAuthLogin, - type OAuthLoginProviders, -} from "@corbits/oauth-core/hub"; -import { mountMcpDiscovery } from "@corbits/mcp/hub"; -import { - CODEX_PROVIDER, - codexOAuthConfig, - exchangeCodexCode, - refreshCodexTokens, -} from "@corbits/codex-provider"; -import { - XAI_PROVIDER, - xaiOAuthConfig, - exchangeXaiCode, - refreshXaiTokens, -} from "@corbits/xai-provider"; -import { createAgentTokenVerifier, mountAgentTokens } from "@corbits/agent-token"; -import { createCronTicker, createCronRoutes, createRunTriggerCronDeliver } from "@corbits/cron"; -import { createHubMailboxAuthorizeSender } from "./mailbox-persist"; -import { captureMailboxRequest, createMailboxDeliver } from "./mailbox-send"; -import { reportError } from "@corbits/error-sink"; -import { - createRunTriggerDeliverer, - createTenantSystemSender, - installWebhooks, - type HookMailRouter, -} from "@corbits/webhooks"; +import { mountCorbits } from "./corbits-mount"; +import { databaseUrlFrom, migrateHub } from "./migrate"; import { createProcessSidecarProvisioner, readProcessProvisionerConfig, type ProcessProvisionerRole, } from "./provisioners/process"; -import { - InlineContentStore, - mountArtifacts, - mountWorkflowArtifacts, - type WorkflowArtifactEnv, -} from "@corbits/artifacts"; -import { workflowRun } from "@intx/db/schema"; -import path from "node:path"; -import { migrateHub } from "./migrate"; -import { createWorkflowRunStreamRoutes } from "./run-stream"; -import { createWorkflowRunAuthenticator } from "./workflow-run-authenticator"; - -// The same condition registry `mountHubRoutes` builds by default when no -// registry is supplied -- kept as one local constant so every Corbits -// route factory below shares the identical registry rather than each -// re-deriving stock's own default. -const grantConditionRegistry: ConditionRegistry = { - time_window: timeWindowEvaluator, -}; function buildProcessSidecarProvisioner( hubDataDir: string, @@ -161,20 +82,13 @@ function buildProcessSidecarProvisioner( } export type CreateHubServerOpts = { - /** Provisioners eligible to host frozen workflow deployments. */ readonly sidecarProvisioners?: readonly SidecarProvisioner[]; - /** Selects among matching deployment provisioners. Defaults to the first. */ readonly sidecarProvisionerChooser?: SidecarProvisionerChooser; - /** Provisioners eligible to evaluate workflow source code. */ readonly probeSidecarProvisioners?: readonly SidecarProvisioner[]; - /** Selects among matching probe provisioners. Defaults to the first. */ readonly probeSidecarProvisionerChooser?: SidecarProvisionerChooser; readonly probeSidecarCapabilityRules?: readonly SidecarCapabilityRule[]; - /** Maximum simultaneous allocation reconciliations. Defaults to eight. */ readonly sidecarAllocationConcurrency?: number; - /** Deadline for provider calls, allocation claims, lease validation, and connection waits. Defaults to 120 seconds. */ readonly sidecarOperationTimeoutMs?: number; - /** Custom inference adapters every provisioned sidecar must load, forwarded on each ensure request. */ readonly sidecarAdapterManifest?: AdapterManifest; }; @@ -207,10 +121,6 @@ export async function createHubServer({ ...(pgSchema !== undefined && { schema: pgSchema }), }; const { db, close: closeDb } = createDB(dbConfig); - - // Platform schema plus every mounted Corbits package's own migration, - // applied once at boot before anything below reads or writes the - // database. await migrateHub(dbConfig, db); const auth = createAuth(db); @@ -220,14 +130,12 @@ export async function createHubServer({ throw new Error("HUB_DATA_DIR environment variable is required"); } - // Credential secrets are encrypted at rest under this operator-provided key. const credentialEncryptionKeyHex = process.env["CREDENTIAL_ENCRYPTION_KEY"]; if (credentialEncryptionKeyHex === undefined || credentialEncryptionKeyHex.trim() === "") { throw new Error("CREDENTIAL_ENCRYPTION_KEY environment variable is required"); } const credentialCipher = createEnvKeyCredentialCipher(hexDecode(credentialEncryptionKeyHex)); - // Per-principal signing keys are sealed at rest under their own operator key. const principalKeyEncryptionKeyHex = process.env["PRINCIPAL_KEY_ENCRYPTION_KEY"]; if (principalKeyEncryptionKeyHex === undefined || principalKeyEncryptionKeyHex.trim() === "") { throw new Error("PRINCIPAL_KEY_ENCRYPTION_KEY environment variable is required"); @@ -284,8 +192,9 @@ export async function createHubServer({ cancelled: readLifecycleDurationEnv("WORKFLOW_DEFAULT_RETENTION_CANCELLED", "1h"), }, }; - if (lifecycleDurationMs(defaultLifecyclePolicy.maxLifetime) === 0) + if (lifecycleDurationMs(defaultLifecyclePolicy.maxLifetime) === 0) { throw new Error("WORKFLOW_DEFAULT_MAX_LIFETIME must be greater than zero"); + } if (lifecycleDurationMs(defaultLifecyclePolicy.maxDisconnected) === 0) { throw new Error("WORKFLOW_DEFAULT_MAX_DISCONNECTED must be greater than zero"); } @@ -306,12 +215,13 @@ export async function createHubServer({ const grantStore = createGrantStore(db); const workflowHistoryReceives = createWorkflowHistoryReceiveTracker(); + const baseSessionLookups = createHubSessionLookups({ + db, + agentRepoStore, + historyReceives: workflowHistoryReceives, + }); const lookups: SidecarLookups = { - ...createHubSessionLookups({ - db, - agentRepoStore, - historyReceives: workflowHistoryReceives, - }), + ...baseSessionLookups, materializeMailTriggeredRunGrants: createMailTriggeredRunGrantsMaterializer({ db, principalKeyStore, @@ -608,377 +518,18 @@ export async function createHubServer({ }), }); - // Corbits mount block -- everything Workbench adds to the stock hub. - // See AGENTS.md: any other hub mount is cutover debt, never a pattern to extend. - const TENANT_PREFIX = "/api/tenants/:tenantId"; - const corbitsDatabaseUrl = `postgres://${encodeURIComponent(process.env["DB_USER"] ?? "postgres")}:${encodeURIComponent(process.env["DB_PASSWORD"] ?? "postgres")}@${process.env["DB_HOST"] ?? "localhost"}:${String(Number(process.env["DB_PORT"] ?? 5432))}/${process.env["DB_NAME"] ?? "interchange"}`; - const { db: mailboxDb } = createMailboxDb(corbitsDatabaseUrl); - const mailboxBus = createInMemoryMailboxEventBus(); - lookups.persistMail = createMailboxPersist(mailboxDb, { - upstream: async () => [], - authorizeSender: createHubMailboxAuthorizeSender(db), - bus: mailboxBus, - }); - - const artifactContentStore = InlineContentStore; - - { - const artifactsApi = new Hono(); - mountArtifacts(artifactsApi, { - db, - contentStore: artifactContentStore, - requireGrant: createRequireGrant({ grantStore, conditionRegistry: grantConditionRegistry }), - }); - app.route(TENANT_PREFIX, artifactsApi); - } - { - app.route( - TENANT_PREFIX, - createWorkflowRunStreamRoutes({ - db, - sidecarRouter, - requireGrant: createRequireGrant({ - grantStore, - conditionRegistry: grantConditionRegistry, - }), - }), - ); - } - { - // Minting an agent token is minting a credential, so it is gated by the - // same stock grant the credential routes are, not by tenant membership. - const agentTokensApp = new Hono(); - const requireAgentTokenGrant = createRequireGrant({ - grantStore, - conditionRegistry: grantConditionRegistry, - }); - mountAgentTokens(agentTokensApp, { - db, - requireGrant: requireAgentTokenGrant("credential:*", "create"), - resolveTenantId: (ctx) => (ctx as { get(key: "tenant"): { id: string } }).get("tenant").id, - // A token is scoped to the agent's `workflow` source asset: the - // tenant-owned thing that already exists when the workbench mints the - // token, before the deploy that would create a run. - resolveDefinition: async (tenantId, definitionId) => { - const row = await db.query.asset.findFirst({ - where: and( - eq(assetTable.id, definitionId), - eq(assetTable.tenantId, tenantId), - eq(assetTable.kind, "workflow"), - ), - }); - return row !== undefined; - }, - }); - app.route(TENANT_PREFIX, agentTokensApp); - } - // A deployed agent has no sidecar token; it presents the bearer this hub - // minted for its definition and is scoped to the run it names. One - // verifier and one run lookup serve every run-scoped Corbits mount. - const verifyAgentToken = createAgentTokenVerifier({ db }); - const resolveAgentRun = async (runAddress: string) => { - if (runAddress === "") return null; - const run = await db.query.workflowRun.findFirst({ - where: eq(workflowRun.address, runAddress), - }); - if (run === undefined || run.principalId === null) return null; - return { tenantId: run.tenantId, principalId: run.principalId, runId: run.id }; - }; - - { - const workflowArtifactsApi = new Hono(); - const workflowRunAuthenticator = createWorkflowRunAuthenticator({ db }); - mountWorkflowArtifacts(workflowArtifactsApi, { - db, - contentStore: artifactContentStore, - resolveRunScope: (token, runAddress) => workflowRunAuthenticator.resolve(token, runAddress), - agentToken: { verify: (ctx) => verifyAgentToken(ctx), resolveRun: resolveAgentRun }, - }); - app.route("/api/workflow-artifacts", workflowArtifactsApi); - } - { - const mailboxApp = new Hono(); - // Registered before the routes: Hono runs handlers in registration order. - mailboxApp.use("/me/inbox/send", captureMailboxRequest()); - mountMailbox(mailboxApp, { - db: mailboxDb, - bus: mailboxBus, - resolvePrincipal: (ctx) => { - const c = ctx as { get(key: "tenant" | "principal"): { id: string } }; - return { tenantId: c.get("tenant").id, principalId: c.get("principal").id }; - }, - // The address stock itself stamps on a person's outbound mail, so a - // Sent copy and the agent's reply share one identity. - senderAddressFor: async (principal) => { - const [tenantRow] = await db - .select({ domain: tenantTable.domain }) - .from(tenantTable) - .where(eq(tenantTable.id, principal.tenantId)) - .limit(1); - if (tenantRow === undefined) { - throw new Error(`no tenant "${principal.tenantId}" to address a mailbox sender from`); - } - const [principalRow] = await db - .select({ refId: principalTable.refId }) - .from(principalTable) - .where(eq(principalTable.id, principal.principalId)) - .limit(1); - if (principalRow === undefined) { - throw new Error(`no principal "${principal.principalId}" to address a mailbox sender as`); - } - // A person's refId is mixed case; a small model lowercases an address - // before replying, so the canonical stamped form is lowercase. - return `${principalRow.refId}@${tenantRow.domain}`.toLowerCase(); - }, - deliver: createMailboxDeliver({ app, persistMail: lookups.persistMail }), - }); - app.route(`${TENANT_PREFIX}/mailbox`, mailboxApp); - } - - // The router every system-originated trigger (webhook, cron) goes - // through. `HookMailRouter` types its payloads as `unknown` at the - // package boundary; this just narrows them back to `sidecarRouter`'s own - // types on the way through, with no behavior change. - const systemTriggerMailRouter: HookMailRouter = { - routeMail: (address, rawMessage, authenticatedSender, messageId) => { - if (!sidecarRouter.getRoutableAddresses().includes(address)) { - return false; - } - void sidecarRouter - .routeMail(address, rawMessage, authenticatedSender, messageId) - .catch((error: unknown) => { - reportError(error, { - operation: "hub.systemTriggerMailRouter.routeMail", - extra: { address, authenticatedSender }, - }); - }); - return true; - }, - sendRunGrants: (address, runId, stepGrants, senderIdentities) => - sidecarRouter.sendRunGrants( - address, - runId, - stepGrants as Parameters[2], - senderIdentities as Parameters[3], - ), - }; - - let cronTicker: { start(): void; stop(): void } | undefined; - let oauthTokenRefresher: { start(): void; stop(): void } | undefined; - { - app.route( - `${TENANT_PREFIX}/cron`, - createCronRoutes({ - db, - requireGrant: createRequireGrant({ grantStore, conditionRegistry: grantConditionRegistry }), - }), - ); - - cronTicker = createCronTicker({ - db, - intervalMs: 60_000, - // A due schedule fires with nobody signed in, so it cannot ride the - // mailbox persist path, which authorizes its sender against a live - // routable endpoint. It is a system trigger like an inbound webhook, - // so it takes the same route: the run is the authenticated sender of - // its own signed trigger mail, with its grants materialized first. - deliver: createRunTriggerCronDeliver( - createRunTriggerDeliverer({ - router: systemTriggerMailRouter, - materialize: createMailTriggeredRunGrantsMaterializer({ - db, - principalKeyStore, - grantStore, - }), - tenantDomain: async (tenantId) => { - const [tenantRow] = await db - .select({ domain: tenantTable.domain }) - .from(tenantTable) - .where(eq(tenantTable.id, tenantId)) - .limit(1); - if (tenantRow === undefined) { - throw new Error(`no tenant "${tenantId}" to address cron mail from`); - } - return tenantRow.domain; - }, - senderLocalPart: "cron", - // A durable per-tenant key the recipient can verify against; - // the sidecar rejects trigger mail from an unknown sender. - systemSender: createTenantSystemSender({ db, principalKeyStore }), - }), - ), - onDeliveryError: (error, schedule) => { - reportError(error, { - operation: "hub.cron.deliver", - extra: { scheduleId: schedule.id, tenantId: schedule.tenantId }, - }); - }, - // Waiting is the ordinary gap between an agent's runs, not a fault — - // it resolves itself the moment the agent comes back. - onScheduleWaiting: (schedule) => { - log.info("Cron schedule waiting for agent {definitionName}", { - scheduleId: schedule.id, - tenantId: schedule.tenantId, - definitionName: schedule.definitionName, - }); - }, - // A stopped schedule never fires again and no redeploy resumes it, so - // the one report it gets has to be durable. - onScheduleStopped: (schedule) => { - reportError( - new Error(`cron schedule stopped: ${schedule.reason} (${schedule.definitionName})`), - { - operation: "hub.cron.schedule_stopped", - extra: { - scheduleId: schedule.id, - tenantId: schedule.tenantId, - definitionName: schedule.definitionName, - reason: schedule.reason, - }, - }, - ); - }, - }); - cronTicker.start(); - } - - { - const memoryApp = new Hono(); - const memory = createMemory({ - app: memoryApp, - config: loadMemoryConfig(), - grantStore, - conditionRegistry: grantConditionRegistry, - }); - app.route("/", memoryApp); - - // The same plane, reached by a deployed agent's bearer rather than a - // browser session, and scoped to the run the bearer names. - const workflowMemoryApi = new Hono(); - mountWorkflowMemory(workflowMemoryApi, { - memory, - agentToken: { verify: (ctx) => verifyAgentToken(ctx), resolveRun: resolveAgentRun }, - }); - app.route("/api/workflow-memory", workflowMemoryApi); - } - - const hubPublicUrl = process.env["HUB_PUBLIC_URL"] ?? `http://127.0.0.1:${String(port)}`; - // A loopback hub is local development, where MCP servers may be plain-http - // loopback too; a public hub keeps discovery https-only. - const hubHost = new URL(hubPublicUrl).hostname; - const localDevHub = hubHost === "localhost" || hubHost.startsWith("127.") || hubHost === "[::1]"; - - { - // "Continue with Codex"/"Continue with xAI": the whole loopback PKCE - // flow runs here, so the verifier and the callback listener never - // leave this process and the browser only learns a credential id. - const oauthLoginApi = new Hono(); - const requireGrant = createRequireGrant({ - grantStore, - conditionRegistry: grantConditionRegistry, - }); - const oauthProviders: OAuthLoginProviders = { - [CODEX_PROVIDER]: { - oauthConfig: codexOAuthConfig, - exchange: (code: string, verifier: string, now: number) => - exchangeCodexCode(code, verifier, now), - // The account id the refresher carries forward lives on the - // credential, so the prior tokens need only supply the secret. - refresh: (refreshSecret: string, now: number) => - refreshCodexTokens(refreshSecret, now, { access: "", refresh: refreshSecret }), - // The Codex backend rejects inference without this header value. - metadata: (tokens) => - "accountId" in tokens && typeof tokens.accountId === "string" - ? { accountId: tokens.accountId } - : {}, - }, - [XAI_PROVIDER]: { - oauthConfig: xaiOAuthConfig, - exchange: (code: string, verifier: string, now: number) => - exchangeXaiCode(code, verifier, now), - refresh: (refreshSecret: string, now: number) => refreshXaiTokens(refreshSecret, now), - }, - }; - // Resource-URL logins (MCP servers) redirect back to a hub-root route - // outside any tenant; it shares the login store with the tenant routes. - const oauthLoginStore = createOAuthLoginStore(); - // The mount takes a plain Hono, so it rides its own router on the root. - const oauthCallbackApi = new Hono(); - mountOAuthCallback(oauthCallbackApi, { store: oauthLoginStore, path: "/api/oauth/callback" }); - app.route("/", oauthCallbackApi); - mountOAuthLogin(oauthLoginApi, { - store: oauthLoginStore, - callbackUrl: `${hubPublicUrl.replace(/\/+$/, "")}/api/oauth/callback`, - clientName: "Workbench", - db, - cipher: credentialCipher, - requireGrant: requireGrant("credential:*", "create"), - providers: oauthProviders, - onError: (error, { provider }) => { - reportError(error, { operation: "hub.oauth-login", extra: { provider } }); - }, - }); - app.route(TENANT_PREFIX, oauthLoginApi); - - // Stock Interchange has no serving-time refresh hook, so a subscription - // token that lapses between inference calls would simply fail the next - // one; this renews it a little before expiry instead. - oauthTokenRefresher = createOAuthTokenRefresher({ - db, - cipher: credentialCipher, - providers: oauthProviders, - intervalMs: 60_000, - onRefreshed: ({ tenantId, credentialId }) => { - // The same push the stock credentials route fires after a secret - // rotation, so running sidecars get the new material. - void pushSourceUpdates(db, sidecarRouter, tenantId, credentialCipher).catch( - (error: unknown) => { - reportError(error, { - operation: "hub.oauth-refresh.push", - extra: { tenantId, credentialId }, - }); - }, - ); - }, - onError: (error, { provider, credentialId }) => { - reportError(error, { - operation: "hub.oauth-refresh", - extra: { provider: provider ?? "", credentialId: credentialId ?? "" }, - }); - }, - }); - oauthTokenRefresher.start(); - } - - { - // The browser never holds an MCP server's token, so the one place a - // catalog can be read is here, with the secret decrypted in-process. - const mcpApi = new Hono(); - mountMcpDiscovery(mcpApi, { - allowLoopback: localDevHub, - db, - cipher: credentialCipher, - requireGrant: createRequireGrant({ - grantStore, - conditionRegistry: grantConditionRegistry, - })("credential:*", "read"), - onError: (error, { url }) => { - reportError(error, { operation: "hub.mcp-discover", extra: { url } }); - }, - }); - app.route(TENANT_PREFIX, mcpApi); - } - - await installWebhooks({ + const corbits = await mountCorbits({ app, db, - credentialCipher, + databaseUrl: databaseUrlFrom(dbConfig), + port, + grantStore, principalKeyStore, - router: systemTriggerMailRouter, + credentialCipher, + sidecarRouter, + lookups, }); - // End of Corbits mount block. - log.info("Starting server on port {port}", { port }); const sidecarWebsocket: typeof websocket & { maxPayloadLength: number } = { @@ -991,7 +542,6 @@ export async function createHubServer({ websocket: sidecarWebsocket, port, idleTimeout: 0, - /** Stops every background ticker and the database pool; spawned sidecars are left to their pid files. */ async shutdown(): Promise { allocationScheduler.stop(); probeCleanupScheduler.stop(); @@ -999,8 +549,7 @@ export async function createHubServer({ dispatchProjectionScheduler.stop(); dispatchScheduler.stop(); connectionRepairScheduler.stop(); - cronTicker?.stop(); - oauthTokenRefresher?.stop(); + corbits.stop(); await closeDb(); }, }; diff --git a/apps/sidecar/README.md b/apps/sidecar/README.md index b5725a09d8..875231d6e8 100644 --- a/apps/sidecar/README.md +++ b/apps/sidecar/README.md @@ -11,16 +11,25 @@ with an in-memory mail transport (`@intx/mail-memory`), the Web Crypto provider (`@intx/crypto`), and a deploy router; on each inbound deploy the router creates an `@intx/workflow-host` supervisor for the deployment and spawns a supervised workflow-process child. -The child assembles the step's runtime across two seams: -`src/step-agent-tools.ts` materializes the pinned tool-package -closure and composes the POSIX and LSP plugin chain, and -`src/workflow-substrate-factory.ts` builds the per-step environment — -the isomorphic-git context store and the supervisor-backed mail -transport the tools bind to. `src/default-harness.ts` provides only +The child assembles the step's runtime across two seams. The app's +`src/step-tool-materialization.ts` materializes the pinned tool-package +closure. `@intx/workflow-host` composes the POSIX and LSP plugin chain +(`packages/workflow-host/src/child/step-tools.ts`) and builds the +per-step environment +(`packages/workflow-host/src/child/substrate-factory.ts`) — the +isomorphic-git context store and the supervisor-backed mail transport +the tools bind to. The binary closes that factory over the materializer +and `@intx/workflow-deploy`'s grant cap in +`src/workflow-child-bindings.ts`. `src/default-harness.ts` provides only the `HarnessBuilder` source-admission check (`canBuildSource`) the deploy router uses to reject an unbuildable inference source before spawning. +The sidecar imports the repository substrate and workflow inspection entry +points without loading Hub database services or the agent execution runtime. +See the [workflow-host documentation](../../vendor/intx/workflow-host/README.md) +for channel validation and schema initialization. + Run it under Bun. Startup requires `SIDECAR_DATA_DIR`, `HUB_WS_URL`, `SIDECAR_ID`, and `SIDECAR_TOKEN`. Optional `SIDECAR_CACHE_DIR` relocates the tarball cache (defaults to a subdirectory of the data diff --git a/apps/sidecar/package.json b/apps/sidecar/package.json index e0e95959b5..3f66d67174 100644 --- a/apps/sidecar/package.json +++ b/apps/sidecar/package.json @@ -14,21 +14,13 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@intx/agent": "workspace:*", - "@intx/authz": "workspace:*", "@intx/crypto": "0.5.0", - "@intx/harness": "workspace:*", "@intx/hub-agent": "0.5.0", "@intx/hub-sessions": "workspace:*", "@intx/inference": "workspace:*", "@intx/log": "0.5.0", "@intx/mail-memory": "0.5.0", - "@intx/pack-transport": "0.5.0", - "@intx/storage-isogit": "0.5.0", "@intx/tool-packaging": "0.5.0", - "@intx/tools-lsp": "0.5.0", - "@intx/tools-mail": "0.5.0", - "@intx/tools-posix": "0.5.0", "@intx/types": "workspace:*", "@intx/workflow": "workspace:*", "@intx/workflow-deploy": "workspace:*", @@ -37,7 +29,6 @@ }, "devDependencies": { "@types/bun": "catalog:", - "isomorphic-git": "catalog:", "typescript": "catalog:" } } diff --git a/apps/sidecar/tsconfig.json b/apps/sidecar/tsconfig.json index bc44447a9a..c574d7cb01 100644 --- a/apps/sidecar/tsconfig.json +++ b/apps/sidecar/tsconfig.json @@ -13,12 +13,6 @@ "include": ["src"], "exclude": [], "references": [ - { - "path": "../../vendor/intx/agent/tsconfig.src.json" - }, - { - "path": "../../vendor/intx/harness/tsconfig.src.json" - }, { "path": "../../vendor/intx/hub-sessions/tsconfig.src.json" }, diff --git a/apps/sidecar/tsconfig.src.json b/apps/sidecar/tsconfig.src.json index 616549a035..e3ff6b4d72 100644 --- a/apps/sidecar/tsconfig.src.json +++ b/apps/sidecar/tsconfig.src.json @@ -10,12 +10,6 @@ "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo" }, "references": [ - { - "path": "../../vendor/intx/agent/tsconfig.src.json" - }, - { - "path": "../../vendor/intx/harness/tsconfig.src.json" - }, { "path": "../../vendor/intx/hub-sessions/tsconfig.src.json" }, diff --git a/bun.lock b/bun.lock index 6588dbd4a1..5d5aebaeae 100644 --- a/bun.lock +++ b/bun.lock @@ -40,7 +40,6 @@ "name": "@workbench/hub", "version": "0.0.1", "dependencies": { - "@chat-adapter/state-memory": "^4.35.0", "@corbits/agent-token": "github:corbitsdev/corbits-agent-token#ed286f4592190f911de480ef5330ce03d8605f00", "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", "@corbits/codex-provider": "0.1.2", @@ -57,20 +56,13 @@ "@intx/crypto": "0.5.0", "@intx/db": "workspace:*", "@intx/hub-api": "workspace:*", - "@intx/hub-common": "0.5.0", "@intx/hub-sessions": "workspace:*", "@intx/log": "0.5.0", - "@intx/mime": "0.5.0", "@intx/types": "workspace:*", - "@intx/workflow": "workspace:*", - "@intx/workflow-deploy": "workspace:*", - "@modelcontextprotocol/sdk": "catalog:", "arktype": "catalog:", - "better-auth": "catalog:", "drizzle-orm": "catalog:", "hono": "catalog:", "postgres": "catalog:", - "simple-icons": "^16.28.0", }, "devDependencies": { "@types/bun": "catalog:", @@ -84,21 +76,13 @@ "workflow-child": "./bin/workflow-child", }, "dependencies": { - "@intx/agent": "workspace:*", - "@intx/authz": "workspace:*", "@intx/crypto": "0.5.0", - "@intx/harness": "workspace:*", "@intx/hub-agent": "0.5.0", "@intx/hub-sessions": "workspace:*", "@intx/inference": "workspace:*", "@intx/log": "0.5.0", "@intx/mail-memory": "0.5.0", - "@intx/pack-transport": "0.5.0", - "@intx/storage-isogit": "0.5.0", "@intx/tool-packaging": "0.5.0", - "@intx/tools-lsp": "0.5.0", - "@intx/tools-mail": "0.5.0", - "@intx/tools-posix": "0.5.0", "@intx/types": "workspace:*", "@intx/workflow": "workspace:*", "@intx/workflow-deploy": "workspace:*", @@ -107,7 +91,6 @@ }, "devDependencies": { "@types/bun": "catalog:", - "isomorphic-git": "catalog:", "typescript": "catalog:", }, }, @@ -517,10 +500,6 @@ "@better-fetch/fetch": ["@better-fetch/fetch@1.3.1", "", {}, "sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g=="], - "@chat-adapter/catalog": ["@chat-adapter/catalog@4.43.0", "", {}, "sha512-JRH40HwYIXdIiwSc/gNw65q5T/gaf691n4//2YOhjRE4YO9KzSFJ/N+RrTD3M8iGz2jI4Jg92wb6OAqfYsAkTQ=="], - - "@chat-adapter/state-memory": ["@chat-adapter/state-memory@4.43.0", "", { "dependencies": { "chat": "4.43.0" } }, "sha512-VG9ZbKAIMPh0tcLN/BM/sJ/DQNiwQJhtwvq0IlHEI32mDfjnrZbXKNI6j8kRB1hbU2Xw6eesLue2N6uNsVm5xg=="], - "@copilotkit/aimock": ["@copilotkit/aimock@1.45.1", "", { "peerDependencies": { "jest": ">=29", "vitest": ">=3" }, "optionalPeers": ["jest", "vitest"], "bin": { "aimock": "dist/aimock-cli.js", "llmock": "dist/cli.js" } }, "sha512-JYf6ThCKD8eNMVm0PYsfu1wASy2E2T8VCVoWnEpWHNV2QiSv4yMJ6JHTPm5Gp76azKqme2F43L6siFA1gU7UKQ=="], "@corbits/agent-token": ["@corbits/agent-token@github:corbitsdev/corbits-agent-token#ed286f4", { "dependencies": { "@intx/db": "^0.3.0", "arktype": "^2.2.0", "drizzle-orm": "^0.45.2", "hono": "^4.11.9", "postgres": "^3.4.8" } }, "corbitsdev-corbits-agent-token-ed286f4", "sha512-k7puSwtCC2ZF7W0lzswtBoKxu4n+VjWwtzf8oa1OWe72FEI4TDdQ5gybqq/oW9XJAUHeCkjPL9bdwxG6C2TfpQ=="], @@ -631,8 +610,6 @@ "@gilbarbara/deep-equal": ["@gilbarbara/deep-equal@0.3.1", "", {}, "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw=="], - "@hono/node-server": ["@hono/node-server@2.1.5", "", { "peerDependencies": { "hono": "^4 || ^5.0.0-0" } }, "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q=="], - "@hono/standard-validator": ["@hono/standard-validator@0.2.3", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "hono": ">=3.9.0" } }, "sha512-bp9vHu6Va6SfMHC3D4ZLBbT/woi+AZ9CRdTXQu3kLJuLh2W/Gb9UO4hijS+BQAGFXi4EGpXdetxpzwTAawSVeg=="], "@intx/agent": ["@intx/agent@workspace:vendor/intx/agent"], @@ -669,8 +646,6 @@ "@intx/tool-packaging": ["@intx/tool-packaging@0.5.0", "", { "dependencies": { "@intx/agent": "0.5.0", "@intx/log": "0.5.0", "@intx/storage-isogit": "0.5.0", "@intx/types": "0.5.0", "arktype": "^2.1.29", "isomorphic-git": "^1.27.2", "npm-package-arg": "^12.0.2", "npm-pick-manifest": "^10.0.0", "npm-registry-fetch": "^19.0.0", "semver": "^7.7.2", "ssri": "^12.0.0", "tar": "^7.5.1" } }, "sha512-Jf1XskGsN41q2/mBQNQaf410YeqAXNdXI9+hEP3aq3vmjtSUpNr4juRoRdd9p6otDxMcP9oLLZd8FdfP/dYCrA=="], - "@intx/tools-lsp": ["@intx/tools-lsp@0.5.0", "", { "dependencies": { "@intx/agent": "0.5.0", "@intx/log": "0.5.0", "@intx/tools-posix": "0.5.0", "@intx/types": "0.5.0", "vscode-jsonrpc": "^9.0.1", "vscode-languageserver-types": "^3.17.5", "which": "^4.0.0" } }, "sha512-opGvja6O8Y95SjfrnDAlWdm/ZRZBat+OpuBAgNoUJUfIEvvZNUvxRzivrekofSG/Gy0p6fPdSd8uUAKBRaixKw=="], - "@intx/tools-mail": ["@intx/tools-mail@0.5.0", "", { "dependencies": { "@intx/agent": "0.5.0", "@intx/log": "0.5.0", "@intx/types": "0.5.0", "arktype": "^2.1.29" } }, "sha512-N6iYsyPDnOes3270a74JZHGjSVdWEgSfKc0twbujJhAG63Cjz1FkaQh6VmqxSqboJ5Epku6ixIvUgcYci1GOcA=="], "@intx/tools-posix": ["@intx/tools-posix@0.5.0", "", { "dependencies": { "@intx/agent": "0.5.0", "@intx/types": "0.5.0" } }, "sha512-Qv6Wb+4X0+/fOpnfzzkKV4Z9qAVBOSEWKmBd4BsqAO62qAAIMlwg8XHzd3b/DCdY6PorXy5k2GBK1Xq0LeyCzg=="], @@ -703,8 +678,6 @@ "@logtape/logtape": ["@logtape/logtape@2.4.2", "", {}, "sha512-YJyC1gEMgWmTqJTRtKkjgasEbi8/0gP7Dj8n6bL46gQIAhpTY8uXJ+1QYMOgF5asotlGMvy3G7ejXUe45Yd1MQ=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.32.1", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "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-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw=="], - "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], "@noble/ciphers": ["@noble/ciphers@2.4.0", "", {}, "sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw=="], @@ -923,16 +896,10 @@ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], - "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], - "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@26.6.5", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg=="], "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], @@ -943,8 +910,6 @@ "@types/ssri": ["@types/ssri@7.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw=="], - "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260707.2", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260707.2", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260707.2" }, "bin": { "tsgo": "bin/tsgo" } }, "sha512-oUGp+Rep/hqMhPunyinsALUwSlzHINSxitifPiSaeqoKOKD2OlR9NE3TaPqwsl4NlGslsOSUXI1JotWQzpYCPg=="], "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260707.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wny2pgKjGbiZtnOIHVa3tXC1UfDqxNEFzyPGmiqybedG8hipG2Nfp0l5UxbaKCjkLacUpH/W5bP2hBOMVhCOzg=="], @@ -969,18 +934,10 @@ "@workbench/web": ["@workbench/web@workspace:apps/web"], - "@workflow/serde": ["@workflow/serde@4.1.0-beta.2", "", {}, "sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww=="], - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], - - "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - "ansi-regex": ["ansi-regex@6.4.0", "", {}, "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw=="], "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -995,8 +952,6 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], - "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -1007,8 +962,6 @@ "better-call": ["better-call@1.4.0", "", { "dependencies": { "@better-auth/utils": "^0.5.0", "@better-fetch/fetch": "^1.3.1", "rou3": "^0.9.1", "set-cookie-parser": "^3.1.2" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA=="], - "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], - "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], "browserslist": ["browserslist@4.29.4", "", { "dependencies": { "baseline-browser-mapping": "^2.11.29", "caniuse-lite": "^1.0.30001816", "electron-to-chromium": "^1.5.459", "node-releases": "^2.0.59", "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA=="], @@ -1019,8 +972,6 @@ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], @@ -1031,12 +982,6 @@ "caniuse-lite": ["caniuse-lite@1.0.30001817", "", {}, "sha512-uZ1JG80BwhtMalIAGPxPfmNhHyd/B/nTdr3hITWlRamSyAdUStP1Gop01e7SD5q6J90ixJvWDbvmiFVfDgn4bw=="], - "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], - - "chat": ["chat@4.43.0", "", { "dependencies": { "@chat-adapter/catalog": "4.43.0", "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" }, "peerDependencies": { "ai": "^6.0.182 || ^7.0.0", "workflow": "^5.0.0-beta.35", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai", "workflow", "zod"] }, "sha512-9ICxMewK1+4J0pEbR7eMBTh3JCL3NkyDHAgGrU/gblyyw/+QUcwFHCXIoQMt0Xlyup1c5smS0/gYytTt8NVc6w=="], - "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "chromium-bidi": ["chromium-bidi@157.0.8090-0", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^4.6.5" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag=="], @@ -1049,30 +994,18 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - "content-disposition": ["content-disposition@2.0.1", "", {}, "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog=="], - - "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - - "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], - - "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], - "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], "cronstrue": ["cronstrue@3.37.0", "", { "bin": { "cronstrue": "bin/cli.js" } }, "sha512-Nc5PL23nz3HjRcHRc6O7ikCc3lnFM2SQg5nwSDMGOi0jv0g178mUMClgfT6WuOevrEsPOTQCWOaGFV5PKFYymg=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], - "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], "deep-diff": ["deep-diff@1.0.2", "", {}, "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg=="], @@ -1083,14 +1016,8 @@ "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], - "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], - "devtools-protocol": ["devtools-protocol@0.0.1696802", "", {}, "sha512-FO5/SENfvc2K8wRnWQflKcwuoTH4dBkXCWTQ9ebIWtuX8JM5+ZvCotaEmkfh21pO7TvIKGTM3hG4xFiq9WTwmg=="], "diff3": ["diff3@0.0.3", "", {}, "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g=="], @@ -1101,14 +1028,10 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.462", "", {}, "sha512-9qpCBlXPy5v7n1Q7WTO5rW2StI4WNQhl96HpgXRZLPlNoXAD0LbsQUflovQYAO44Yia073ISusqohncvGSMbGA=="], "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -1119,44 +1042,18 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - - "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], - - "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - - "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], - - "express": ["express@5.3.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.3.0", "content-disposition": "^2.0.1", "content-type": "^2.0.0", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.8", "qs": "^6.16.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.1.0", "vary": "^1.1.2" } }, "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g=="], - - "express-rate-limit": ["express-rate-limit@8.7.1", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA=="], - - "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], - "fake-indexeddb": ["fake-indexeddb@6.2.6", "", {}, "sha512-ihhmr0KoET51jT8R4YhbUYuLLvr1qqO6INISygtN16i8loR8kAxtAUr4+EKHTSMjL+lggaAn4OC7DRqzrNgnuQ=="], - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - "fast-text-encoding": ["fast-text-encoding@1.0.6", "", {}, "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="], - "fast-uri": ["fast-uri@3.1.9", "", {}, "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ=="], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1197,8 +1094,6 @@ "http-cache-semantics": ["http-cache-semantics@4.3.0", "", {}, "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA=="], - "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], @@ -1213,22 +1108,14 @@ "ip-address": ["ip-address@10.7.4", "", {}, "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg=="], - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], "is-lite": ["is-lite@1.2.1", "", {}, "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - - "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], - "isomorphic-git": ["isomorphic-git@1.44.3", "", { "dependencies": { "async-lock": "^1.4.1", "clean-git-ref": "^2.0.1", "crc-32": "^1.2.0", "diff3": "0.0.3", "ignore": "^5.1.4", "minimisted": "^2.0.0", "pako": "^1.0.10", "pify": "^4.0.1", "readable-stream": "^4.0.0", "sha.js": "^2.4.12", "simple-get": "^4.0.1" }, "bin": { "isogit": "cli.cjs" } }, "sha512-xlEIMsVUrMfOR25F4jWB9He9s/rUHWsgfJJJ3zpdwFssv9X2QfoIUyYH9CEFok9yk2tx2UDipj2gJ+KVvV/qGA=="], "isomorphic-textencoder": ["isomorphic-textencoder@1.0.1", "", { "dependencies": { "fast-text-encoding": "^1.0.0" } }, "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ=="], @@ -1241,10 +1128,6 @@ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "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=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], @@ -1255,8 +1138,6 @@ "kysely": ["kysely@0.29.7", "", {}, "sha512-fmIq+7kv4WbFm2tcfznRzikqB4cuNpgZ40PSU2fAJ2/OO5XhojqnTPrmgrCjYSRJVwBln03XcRXJGnw9H099IQ=="], - "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -1265,98 +1146,8 @@ "make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], - "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw=="], - - "mdast-util-from-markdown": ["mdast-util-from-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A=="], - - "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], - - "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], - - "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], - - "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw=="], - - "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], - - "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], - - "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], - - "mdast-util-to-markdown": ["mdast-util-to-markdown@2.2.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-character": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA=="], - - "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], - - "media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], - - "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], - - "micromark": ["micromark@4.0.3", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw=="], - - "micromark-core-commonmark": ["micromark-core-commonmark@2.0.4", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.1.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.3" } }, "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w=="], - - "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], - - "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], - - "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], - - "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], - - "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.2", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA=="], - - "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], - - "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], - - "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], - - "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], - - "micromark-factory-space": ["micromark-factory-space@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q=="], - - "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], - - "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], - - "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], - - "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], - - "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], - - "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], - - "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], - - "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], - - "micromark-util-edit-map": ["micromark-util-edit-map@1.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q=="], - - "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], - - "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], - - "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], - - "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], - - "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], - - "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], - - "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - - "micromark-util-types": ["micromark-util-types@2.0.3", "", {}, "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg=="], - - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], - "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], @@ -1405,10 +1196,6 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], @@ -1421,22 +1208,14 @@ "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], - "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], - "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], - "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], - "popper.js": ["popper.js@1.16.1", "", {}, "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], @@ -1451,18 +1230,10 @@ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - "proxy-addr": ["proxy-addr@2.0.8", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ=="], - "puppeteer-core": ["puppeteer-core@25.15.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.5", "chromium-bidi": "157.0.8090-0", "devtools-protocol": "0.0.1696802", "typed-query-selector": "^2.12.3", "webdriver-bidi-protocol": "1.0.1", "ws": "^8.22.0" } }, "sha512-DEk7S2tlBDp4+iXdnHjFmMofoqVftlz1JO4eyE+oBSiQIfIVTYq8hWF1EsjmRvD49chxGX3oKQT5hX2dnIIDdg=="], - "qs": ["qs@6.16.1", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg=="], - "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], - "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], - - "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - "react": ["react@19.3.1", "", {}, "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw=="], "react-dom": ["react-dom@19.3.1", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.1" } }, "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ=="], @@ -1485,24 +1256,12 @@ "readable-stream": ["readable-stream@4.8.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ=="], - "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], - - "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], - - "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], - - "remend": ["remend@1.4.0", "", {}, "sha512-mHiYhhwphs1IgIZmkToKLwJn0U99IjPXuutkA2acrNcG0vlEq2G4TGnd2eT0saIHwh2KjaCpvleHcsd5SAz96A=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "rollup": ["rollup@4.64.12", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.64.12", "@rollup/rollup-android-arm64": "4.64.12", "@rollup/rollup-darwin-arm64": "4.64.12", "@rollup/rollup-darwin-x64": "4.64.12", "@rollup/rollup-freebsd-arm64": "4.64.12", "@rollup/rollup-freebsd-x64": "4.64.12", "@rollup/rollup-linux-arm-gnueabihf": "4.64.12", "@rollup/rollup-linux-arm-musleabihf": "4.64.12", "@rollup/rollup-linux-arm64-gnu": "4.64.12", "@rollup/rollup-linux-arm64-musl": "4.64.12", "@rollup/rollup-linux-loong64-gnu": "4.64.12", "@rollup/rollup-linux-loong64-musl": "4.64.12", "@rollup/rollup-linux-ppc64-gnu": "4.64.12", "@rollup/rollup-linux-ppc64-musl": "4.64.12", "@rollup/rollup-linux-riscv64-gnu": "4.64.12", "@rollup/rollup-linux-riscv64-musl": "4.64.12", "@rollup/rollup-linux-s390x-gnu": "4.64.12", "@rollup/rollup-linux-x64-gnu": "4.64.12", "@rollup/rollup-linux-x64-musl": "4.64.12", "@rollup/rollup-openbsd-x64": "4.64.12", "@rollup/rollup-openharmony-arm64": "4.64.12", "@rollup/rollup-win32-arm64-msvc": "4.64.12", "@rollup/rollup-win32-ia32-msvc": "4.64.12", "@rollup/rollup-win32-x64-gnu": "4.64.12", "@rollup/rollup-win32-x64-msvc": "4.64.12", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GhAu8ZglZ61VWSX/ZW3K/qkazgURBIQL0/Yy51QsS8DPyebR5Cf/Z2PANJFpTU0aVZ9byJcyqR6xvBbkXhdGYQ=="], "rou3": ["rou3@0.9.2", "", {}, "sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ=="], - "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], @@ -1515,36 +1274,16 @@ "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], - "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - - "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], - "set-cookie-parser": ["set-cookie-parser@3.1.4", "", {}, "sha512-iEmdEew+Tn1BwMGMP8LzGBEPC7Mf1sySjglCeEPb/Vchm9bnGezePvacYcId+fGprQzIng+TBkzqfJ/qS7AsBg=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], - - "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - "simple-icons": ["simple-icons@16.35.0", "", {}, "sha512-TCfl/2+GTrx0nCo4pEWGvxhOb8c6NsqOU1OpwjtF4lwJAthB8IYVD0fuIMjLMg6JFuG1YBO0t1iETQqJLqkoJA=="], - "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], "socks": ["socks@2.8.10", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ=="], @@ -1561,8 +1300,6 @@ "ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], - "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "string-width": ["string-width@8.3.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ=="], "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], @@ -1579,20 +1316,14 @@ "to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="], - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "tree-changes": ["tree-changes@0.11.3", "", { "dependencies": { "@gilbarbara/deep-equal": "^0.3.1", "is-lite": "^1.2.1" } }, "sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag=="], - "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tsx": ["tsx@4.23.15", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw=="], "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], - "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-query-selector": ["typed-query-selector@2.12.3", "", {}, "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg=="], @@ -1601,18 +1332,6 @@ "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], - "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], - - "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], - - "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], - - "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], - - "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], - - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - "update-browserslist-db": ["update-browserslist-db@1.3.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A=="], "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], @@ -1621,22 +1340,10 @@ "validate-npm-package-name": ["validate-npm-package-name@6.0.2", "", {}, "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ=="], - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], - - "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@7.3.7", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA=="], - "vscode-jsonrpc": ["vscode-jsonrpc@9.0.3", "", {}, "sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ=="], - - "vscode-languageserver-types": ["vscode-languageserver-types@3.18.4", "", {}, "sha512-nQsacoiijvl0fRf2q28uFDrAVwRhdCa3QveiF7/CX0r18mLR5FU/1WskSsCKOjmEbtteKUnY8x8Yzxop+k0zwg=="], - "webdriver-bidi-protocol": ["webdriver-bidi-protocol@1.0.1", "", {}, "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw=="], - "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], - "which-typed-array": ["which-typed-array@1.1.24", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ=="], "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], @@ -1655,10 +1362,6 @@ "zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], - "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - - "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -1671,9 +1374,13 @@ "@npmcli/agent/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], - "better-call/@better-auth/utils": ["@better-auth/utils@0.5.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA=="], + "@workbench/hub/@corbits/artifacts": ["@corbits/artifacts@github:corbitsdev/corbits-artifacts#4a2a3c2", { "dependencies": { "@hono/standard-validator": "^0.2.3" }, "peerDependencies": { "@intx/agent": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "^2.1.29", "drizzle-orm": "^0.45.2", "hono": "^4.12.32", "hono-openapi": "^1.2.0", "postgres": "^3.4.9" } }, "corbitsdev-corbits-artifacts-4a2a3c2", "sha512-UY5nkQ31kGmYgqgIOSlbigwvm7c/s2kpqEbF6Oqo0rzoS1RejpPp+w/Yim6h2MRpYCl5CXgAET1MhkDboHuWBA=="], + + "@workbench/hub/@corbits/mailbox": ["@corbits/mailbox@github:corbitsdev/corbits-mailbox#65590a8", { "dependencies": { "@hono/standard-validator": "0.2.3", "@intx/crypto": "0.3.0", "@standard-community/standard-json": "0.3.5", "@standard-community/standard-openapi": "0.2.9", "arktype": "2.1.29", "hono-openapi": "1.3.1" }, "peerDependencies": { "@intx/log": "^0.2.2", "drizzle-orm": "^0.45.2", "hono": "^4.12.0", "postgres": "^3.4.0" } }, "corbitsdev-corbits-mailbox-65590a8", "sha512-tK7xXcL780iX3I+5R68gmyqgEA0uwYO1qdTDNn/LF6tOiFIm0lt2szjspJ8JMKMTzKC6SSai03p723rEGM1jOg=="], + + "@workbench/hub/@corbits/memory": ["@corbits/memory@github:corbitsdev/corbits-memory#8be608b", { "dependencies": { "@intx/agent": "0.2.2", "@intx/authz": "0.2.2", "@intx/hub-api": "0.2.2", "@intx/log": "0.2.2", "@intx/types": "^0.3.0", "@intx/workflow": "0.2.2", "arktype": "^2.1.29", "drizzle-orm": "^0.45.1", "hono": "^4.9.0", "hono-openapi": "^1.3.1", "postgres": "^3.4.7" } }, "corbitsdev-corbits-memory-8be608b", "sha512-7NcVcz3IHFTfx6ICQT+4gjX1PBNLqjlCqoEOPDFVW7wc4e2+Ueg9wOZQ+0MVttpQCaDg6MObl4kie410UQm/sQ=="], - "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + "better-call/@better-auth/utils": ["@better-auth/utils@0.5.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA=="], "cacache/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], @@ -1681,10 +1388,6 @@ "cliui/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=="], - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "express/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "hono-openapi/@hono/standard-validator": ["@hono/standard-validator@0.4.0", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "hono": ">=4.11.2" } }, "sha512-MxOm1asDh9j7c1D0KiWwppSbFgAQxTRO4YEhjMrJn3lF1BIcXenPdgjSGKDRfQmZdaTaMA8slQLETgVOKyJxFw=="], "hono-openapi/@standard-community/standard-json": ["@standard-community/standard-json@0.3.7", "", { "dependencies": { "quansync": "^0.2.11" }, "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.20.0", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.4.2", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-9reHaeW6k99yMHCgqMnLZgrKGT/fxfwYCdzd8nM4gHL4ez8MO0AlC9seGXoJNVEaO5LiE7DVCKl46wNE4wVFRA=="], @@ -1699,8 +1402,6 @@ "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "negotiator/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "npm-registry-fetch/npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" } }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="], "npm-registry-fetch/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], @@ -1713,8 +1414,6 @@ "tsx/esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], - "type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "vite/esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], "wrap-ansi/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=="], @@ -1765,7 +1464,7 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "@workbench/hub/@corbits/mailbox/hono-openapi": ["hono-openapi@1.3.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.11.2", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-NLVeVkhKZ3drmQNEIPac8HX8Y54uf1hJAgIM/7MfDsaeVVmB+QILWQxx5x3R3NvRHgedcbEbOCGY2uR7WQYyMw=="], "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], diff --git a/docs/sidecar-conversation-durability.md b/docs/sidecar-conversation-durability.md deleted file mode 100644 index c82406976a..0000000000 --- a/docs/sidecar-conversation-durability.md +++ /dev/null @@ -1,44 +0,0 @@ -# Warm-agent conversation durability - -`apps/sidecar/src/conversation-state.ts` makes a warm single-step agent's -multi-turn conversation durable across a child respawn by mirroring it into -the workflow-run substrate instead of the agent's per-run isogit store. - -## On-disk layout - -``` -agent-state// - checkpoint.json compacted full snapshot (turns + metadata) - checkpoint.meta.json { checkpointSeq, turnCount, tokenUsage, ... } - wal//.json one per-boundary delta blob -``` - -The WAL is keyed by mirror boundary, not by turn, so a turnless -metadata-only boundary still commits an entry — keying by turn dropped -metadata on those boundaries, regressing the metadata-equivalence -invariant. - -`bucket = floor(boundarySeq / WAL_BUCKET_SIZE)` bounds a directory's tree -size so no commit re-hashes a tree that grows with turn count. Compaction -every `CHECKPOINT_INTERVAL` boundaries folds the WAL into a fresh -checkpoint and truncates it, keeping per-boundary durable cost ~O(1) -amortized. Restore loads the checkpoint then replays the WAL tail, -concatenating turns and taking the latest entry's metadata. - -## Substrate-merge constraint - -`writeTreePreservingPrefix`'s `merge` callback receives only the direct -children of `preservePrefix`, and the substrate's `clearPrefix` recursively -removes the whole prefix subtree before writing the merge's return set. -WAL append uses `preservePrefix = agent-state//wal//` (no -isogit side-read needed); checkpoint write + WAL truncate uses -`preservePrefix = agent-state//` and simply omits `wal/...` paths from -the returned set to truncate atomically in the same commit. - -## Timing and failure semantics - -The mirror is still awaited synchronously at each run boundary — this is a -structure-only change to what the write does (O(1) append vs O(N) blob), -not when it happens. A restore that cannot parse/replay a checkpoint or WAL -throws rather than silently starting fresh; a mirror write failure -surfaces rather than leaving the next respawn to read a stale snapshot. diff --git a/docs/sidecar-tool-package-durability.md b/docs/sidecar-tool-package-durability.md deleted file mode 100644 index e74e38d50a..0000000000 --- a/docs/sidecar-tool-package-durability.md +++ /dev/null @@ -1,32 +0,0 @@ -# Sidecar tool-package apply durability - -`apps/sidecar/src/tool-materialization.ts` applies a workflow's tool-package -manifest atomically and records which deploy is active. This covers the -durability protocol around that record. - -## active-deploy-id file - -Written as `v1:`. The `v1:` prefix lets a future format change be -rejected loudly instead of misread; pre-versioning files with no prefix are -still accepted for one upgrade cycle, then that branch should be deleted -once every sidecar has restarted on a version that writes the prefix. - -## Persist ladder - -Persisting the new id is the commit for a staged apply. If the fsync'd write -fails, a no-fsync fallback write runs; if that also fails, a sibling -`.dirty` marker records the id instead. On boot, the dirty marker (if -present) is read in preference to the recorded id, since it means a prior -apply committed on disk but the id was not durably flushed. Each stage is -best-effort past the first: a degraded write is logged, not escalated, -since the deploy itself is already staged on disk. - -## Rejected-apply audit - -A rejected manifest (JSON parse failure, schema failure, or loader -rejection) is written verbatim to -`/audit/rejected-applies//manifest.json` alongside an -`error.json` failure payload, fsynced before the caller throws. This -preserves the original bytes (whitespace, key order, unknown fields) for -replay against a newer validator, independent of anything the arktype -schema narrowed away.