diff --git a/apps/hub/package.json b/apps/hub/package.json index c42448870..3f36699df 100644 --- a/apps/hub/package.json +++ b/apps/hub/package.json @@ -17,7 +17,7 @@ "@corbits/approvals": "workspace:*", "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", "@corbits/codex-provider": "github:corbitsdev/corbits-codex-provider#a1f475f341261c05fa8dade0c82f1246c175b5b3", - "@corbits/cron": "github:corbitsdev/corbits-cron#872360f4bb55f140e6462208bfb0888f4a7e395a", + "@corbits/cron": "0.2.1", "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/mcp": "github:corbitsdev/corbits-mcp#9df1ab81d2f50d798bca2d2f44eb4759c92fac0e", diff --git a/apps/hub/src/migrate.ts b/apps/hub/src/migrate.ts index ccd55eb18..f2295663c 100644 --- a/apps/hub/src/migrate.ts +++ b/apps/hub/src/migrate.ts @@ -9,7 +9,7 @@ import { runMigrations } from "@intx/db"; import { sql } from "drizzle-orm"; import postgres from "postgres"; import { applyAgentTokenMigrations } from "@corbits/agent-token/migrations"; -import { applyCronMigrations } from "@corbits/cron"; +import { runCronMigrations } from "@corbits/cron/migrations"; import { createMailboxDb, runMailboxMigrations } from "@corbits/mailbox"; import { runArtifactMigrations } from "@corbits/artifacts"; import { runMemoryMigrations } from "@corbits/memory"; @@ -83,7 +83,7 @@ async function applyMigrations( if (!(await platformSchemaPresent(db, schema))) { await runMigrations(config, { schema }); } - await applyCronMigrations(databaseUrl, { tenantSchema: schema }); + await runCronMigrations(config, { schema }); await applyAgentTokenMigrations(databaseUrl, { tenantSchema: schema }); const mailboxDb = createMailboxDb(databaseUrl); diff --git a/apps/hub/src/server.ts b/apps/hub/src/server.ts index 95b20dbd2..2aab7c29e 100644 --- a/apps/hub/src/server.ts +++ b/apps/hub/src/server.ts @@ -86,7 +86,7 @@ import { refreshXaiTokens, } from "@corbits/xai-provider"; import { createAgentTokenVerifier, mountAgentTokens } from "@corbits/agent-token"; -import { createCronTicker, createRunTriggerCronDeliver, mountCron } from "@corbits/cron"; +import { createCronTicker, createCronRoutes, createRunTriggerCronDeliver } from "@corbits/cron"; import { createHubMailboxAuthorizeSender, createHubPersistMailWithSessionEnsure, @@ -642,15 +642,13 @@ export async function createHubServer({ let cronTicker: { start(): void; stop(): void } | undefined; let oauthTokenRefresher: { start(): void; stop(): void } | undefined; { - const cronApp = new Hono(); - mountCron(cronApp, { - db, - requireTenantMember: (ctx, tenantId) => { - const c = ctx as { get(key: "tenant"): { id: string } }; - return c.get("tenant").id === tenantId; - }, - }); - app.route("/", cronApp); + app.route( + `${TENANT_PREFIX}/cron`, + createCronRoutes({ + db, + requireGrant: createRequireGrant({ grantStore, conditionRegistry: grantConditionRegistry }), + }), + ); cronTicker = createCronTicker({ db, diff --git a/apps/web/src/bench/workflows-tab.tsx b/apps/web/src/bench/workflows-tab.tsx index 3de717ead..0ee8e1133 100644 --- a/apps/web/src/bench/workflows-tab.tsx +++ b/apps/web/src/bench/workflows-tab.tsx @@ -44,6 +44,9 @@ export function WorkflowsTab({ workbenchTenantId }: { readonly workbenchTenantId {definition.name} {scheduleSentence(definition.schedule)} + {definition.status === "deployed" && !definition.scheduleEnabled ? ( + Paused + ) : null} {definition.status === "deployed" ? ( + } diff --git a/apps/web/src/pages/routine-ui.tsx b/apps/web/src/pages/routine-ui.tsx index 1bfa76e27..8b9565a20 100644 --- a/apps/web/src/pages/routine-ui.tsx +++ b/apps/web/src/pages/routine-ui.tsx @@ -1,6 +1,13 @@ import "./routines.css"; +import { Button } from "@corbits/react-ui"; +import { toast } from "@corbits/react-ui/ui/toast"; +import { reportError } from "@corbits/error-sink"; import { WorkflowRunResponse, paginatedSchema } from "@intx/types"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { describeApiError } from "@/lib/api-query"; +import { tenantKeys } from "../query-client"; +import { pauseSchedule, resumeSchedule } from "../routines-api"; import { useAPIQuery } from "../api"; import type { APIQuery } from "@/lib/api-query"; import type { GlobalRoutineRow } from "../global-routines"; @@ -40,7 +47,8 @@ export function routineState( lastRun: RunRow | undefined, ): { readonly tone: Tone; readonly label: string } { if (lastRun?.status === "error") return { tone: "failed", label: "failed" }; - if (row.definition.status === "stopped") return { tone: "idle", label: "paused" }; + if (row.definition.status === "stopped" || !row.definition.scheduleEnabled) + return { tone: "idle", label: "paused" }; if (lastRun?.status === "running" || lastRun?.status === "updating") { return { tone: "info", label: "running" }; } @@ -60,3 +68,25 @@ export function RoutinePill({ ); } + +/** Pauses or resumes the routine's cron schedule; absent when none exists. */ +export function PauseResumeButton({ row }: { readonly row: GlobalRoutineRow }) { + const queryClient = useQueryClient(); + const { scheduleId, scheduleEnabled, name } = row.definition; + const toggle = useMutation({ + mutationFn: () => + (scheduleEnabled ? pauseSchedule : resumeSchedule)(row.tenantId, scheduleId ?? ""), + onSuccess: () => toast(`${name} ${scheduleEnabled ? "paused" : "resumed"}`), + onError: (cause) => { + reportError(cause, { operation: "scheduled_workflow_toggle_pause", tenantId: row.tenantId }); + toast(`Couldn't update ${name}: ${describeApiError(cause, "updating this routine")}`); + }, + onSettled: () => queryClient.invalidateQueries({ queryKey: tenantKeys.routines(row.tenantId) }), + }); + if (scheduleId === null || row.definition.status === "stopped") return null; + return ( + + ); +} diff --git a/apps/web/src/pages/routines-page.tsx b/apps/web/src/pages/routines-page.tsx index eb1b8744b..f5f80762c 100644 --- a/apps/web/src/pages/routines-page.tsx +++ b/apps/web/src/pages/routines-page.tsx @@ -12,7 +12,13 @@ import { Link } from "../navigation"; import { benchLink, useFromBench } from "../shell/page-crumbs"; import { PageLayout } from "../shell/page-layout"; import { StageTopBar } from "../shell/stage-top-bar"; -import { RoutinePill, formatWhen, routineState, useRoutineRuns } from "./routine-ui"; +import { + PauseResumeButton, + RoutinePill, + formatWhen, + routineState, + useRoutineRuns, +} from "./routine-ui"; export type { GlobalRoutineRow } from "../global-routines"; @@ -52,7 +58,10 @@ function RoutineListRow({ {runs.kind === "ready" ? formatWhen(lastRun?.createdAt) : "…"} {state.label} - onRunNow(row)} /> +
+ + onRunNow(row)} /> +
); } diff --git a/apps/web/src/routines-api.ts b/apps/web/src/routines-api.ts index 1a5f1fade..d77532155 100644 --- a/apps/web/src/routines-api.ts +++ b/apps/web/src/routines-api.ts @@ -21,6 +21,10 @@ export const ScheduledWorkflowDefinition = type({ /** The cron expression firing this deployment's live run, or null when no * `@corbits/cron` row is addressed at it. */ schedule: "string | null", + /** The cron row's id, or null when none is addressed at this deployment. */ + scheduleId: "string | null", + /** False while the cron row is paused; true when it fires or none exists. */ + scheduleEnabled: "boolean", }); export type ScheduledWorkflowDefinition = typeof ScheduledWorkflowDefinition.infer; @@ -42,6 +46,8 @@ export const CronSchedule = type({ * never fires again, and a later redeploy does not resume it. */ stoppedAt: "string | null", stoppedReason: "string | null", + /** False while the schedule is paused: the ticker skips it until resumed. */ + enabled: "boolean", createdAt: "string", }); @@ -94,6 +100,32 @@ export async function createCronSchedule( return parsed.schedule; } +async function setScheduleEnabled( + tenantId: string, + id: string, + action: "pause" | "resume", +): Promise { + const path = `${cronPath(tenantId)}/${encodeURIComponent(id)}/${action}`; + const response = await fetch(path, { method: "POST", headers: { accept: "application/json" } }); + if (response.status === 401) throw new UnauthenticatedError(); + if (!response.ok) { + throw new ApiQueryError(`The server answered ${response.status}.`, response.status, path); + } + const parsed = CreatedCronSchedule(await response.json()); + if (parsed instanceof type.errors) { + throw new ApiQueryError(`Unexpected response shape: ${parsed.summary}`, undefined, path); + } + return parsed.schedule; +} + +export function pauseSchedule(tenantId: string, id: string): Promise { + return setScheduleEnabled(tenantId, id, "pause"); +} + +export function resumeSchedule(tenantId: string, id: string): Promise { + return setScheduleEnabled(tenantId, id, "resume"); +} + export async function deleteCronSchedule(tenantId: string, id: string): Promise { const path = `${cronPath(tenantId)}/${encodeURIComponent(id)}`; const response = await fetch(path, { method: "DELETE" }); @@ -158,10 +190,8 @@ export async function listScheduledWorkflows( const nameByAssetId = new Map(assets.map((asset) => [asset.id, asset.name])); // A schedule names its target by definition name, which is the source // asset's name — so a redeploy keeps the row joined to the same row here. - const expressionByDefinitionName = new Map( - schedules - .filter((row) => row.stoppedAt === null) - .map((row) => [row.definitionName, row.expression]), + const scheduleByDefinitionName = new Map( + schedules.filter((row) => row.stoppedAt === null).map((row) => [row.definitionName, row]), ); return deployments .filter((deployment) => { @@ -170,6 +200,7 @@ export async function listScheduledWorkflows( }) .map((deployment) => { const name = nameByAssetId.get(deployment.definitionAssetId); + const row = name === undefined ? undefined : scheduleByDefinitionName.get(name); return { definitionId: deployment.id, assetId: deployment.definitionAssetId, @@ -178,7 +209,9 @@ export async function listScheduledWorkflows( status: deployment.status === "deployed" ? "deployed" : "stopped", createdAt: deployment.createdAt, updatedAt: deployment.createdAt, - schedule: name === undefined ? null : (expressionByDefinitionName.get(name) ?? null), + schedule: row?.expression ?? null, + scheduleId: row?.id ?? null, + scheduleEnabled: row?.enabled ?? true, }; }); } diff --git a/bun.lock b/bun.lock index 400af337d..9692746c9 100644 --- a/bun.lock +++ b/bun.lock @@ -62,7 +62,7 @@ "@corbits/approvals": "workspace:*", "@corbits/artifacts": "github:corbitsdev/corbits-artifacts#4a2a3c22187991a6ce598faf794908fedd252e98", "@corbits/codex-provider": "github:corbitsdev/corbits-codex-provider#a1f475f341261c05fa8dade0c82f1246c175b5b3", - "@corbits/cron": "github:corbitsdev/corbits-cron#872360f4bb55f140e6462208bfb0888f4a7e395a", + "@corbits/cron": "0.2.1", "@corbits/error-sink": "workspace:*", "@corbits/mailbox": "github:corbitsdev/corbits-mailbox#65590a85fa143251b3ac20ba2eca92fc35e70e51", "@corbits/mcp": "github:corbitsdev/corbits-mcp#9df1ab81d2f50d798bca2d2f44eb4759c92fac0e", @@ -641,7 +641,7 @@ "@corbits/credential-mcp": ["@corbits/credential-mcp@github:corbitsdev/corbits-credential-mcp#fb9ec3a", { "peerDependencies": { "@intx/harness": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-credential-mcp-fb9ec3a", "sha512-Co4jR+L3pfBbVYY7I6jRftoHeSb5+LCxzEEaAhVLYA3aY2PrPHZV37+DWxj3fcK/KdzboqbT3XSnqU8DyHRU9Q=="], - "@corbits/cron": ["@corbits/cron@github:corbitsdev/corbits-cron#872360f", { "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-cron-872360f", "sha512-+ZPHEy8j64vK/LuD3gUOGQQLk/8zPbq5NpgOid2b9dnk1bI2odc9UV/oUD+29IlYnEUB4n00C+MjCxdjembt/w=="], + "@corbits/cron": ["@corbits/cron@0.2.1", "", { "dependencies": { "arktype": "^2.2.3" }, "peerDependencies": { "@intx/db": "^0.4.0", "@intx/hub-api": "^0.4.0", "drizzle-orm": "^0.45.3", "hono": "^4.13.8", "postgres": "^3.4.9" } }, "sha512-eJm1nD9YSf9ehgkpE+1+9oiVfhZFfgb+Ck3jeHw2koTIeh9cwQ5eoLXigHlmDN+hAqqGeE+v/GBaHOuDy6uYbQ=="], "@corbits/deferred-tools": ["@corbits/deferred-tools@workspace:packages/deferred-tools"], @@ -1837,11 +1837,11 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@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=="], + "@corbits/codex-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#3028f86", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-3028f86", "sha512-KMxRZgEi7S99tqoWhXqAAm5Bv4ef5tn3eYFcfbiHHX/E+GPnbedF44/ISbGs2gedoR2U9EtA1gnmh+q+Qfg00w=="], - "@corbits/mcp/@corbits/credential-mcp": ["@corbits/credential-mcp@github:corbitsdev/corbits-credential-mcp#fb9ec3a", { "peerDependencies": { "@intx/harness": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-credential-mcp-fb9ec3a", "sha512-Co4jR+L3pfBbVYY7I6jRftoHeSb5+LCxzEEaAhVLYA3aY2PrPHZV37+DWxj3fcK/KdzboqbT3XSnqU8DyHRU9Q=="], + "@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=="], - "@corbits/myra/@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=="], + "@corbits/xai-provider/@corbits/oauth-core": ["@corbits/oauth-core@github:corbitsdev/corbits-oauth-core#3028f86", { "dependencies": { "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/hub-common": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.2.3", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" } }, "corbitsdev-corbits-oauth-core-3028f86", "sha512-KMxRZgEi7S99tqoWhXqAAm5Bv4ef5tn3eYFcfbiHHX/E+GPnbedF44/ISbGs2gedoR2U9EtA1gnmh+q+Qfg00w=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], @@ -1859,11 +1859,17 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@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/codex-provider": ["@corbits/codex-provider@github:corbitsdev/corbits-codex-provider#a1f475f", { "dependencies": { "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core", "@corbits/openai-responses": "github:corbitsdev/corbits-openai-responses", "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-codex-provider-a1f475f", "sha512-D6d0lblWx/4KudfLwQoQGrLB8GKRLP/HVp1cx08L2/hOGZAgH6txLPpeEtBqjl+R520qXSSPfM0kDC+3XNaAfw=="], + + "@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/mcp": ["@corbits/mcp@github:corbitsdev/corbits-mcp#9df1ab8", { "dependencies": { "@corbits/credential-mcp": "github:corbitsdev/corbits-credential-mcp#fb9ec3a6754348cd33c388c70c0830b8844c83de", "@intx/db": "^0.3.0", "@intx/hub-api": "^0.3.0", "@intx/types": "^0.3.0", "arktype": "2.1.20", "drizzle-orm": "^0.45.2", "hono": "^4.11.9" }, "peerDependencies": { "@intx/agent": ">=0.3.0" } }, "corbitsdev-corbits-mcp-9df1ab8", "sha512-5ILvgNI4J/U2kWitFKZmIJO1qybCdIkKZ73ZELu6PWrbw6tARxO8/ffaGoK0nIMrT4EcBZaMd6YkR0vLdPOWnw=="], "@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=="], - "@workbench/web/@corbits/credential-mcp": ["@corbits/credential-mcp@github:corbitsdev/corbits-credential-mcp#fb9ec3a", { "peerDependencies": { "@intx/harness": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-credential-mcp-fb9ec3a", "sha512-Co4jR+L3pfBbVYY7I6jRftoHeSb5+LCxzEEaAhVLYA3aY2PrPHZV37+DWxj3fcK/KdzboqbT3XSnqU8DyHRU9Q=="], + "@workbench/hub/@corbits/xai-provider": ["@corbits/xai-provider@github:corbitsdev/corbits-xai-provider#9f2d4ba", { "dependencies": { "@corbits/oauth-core": "github:corbitsdev/corbits-oauth-core", "@corbits/openai-responses": "github:corbitsdev/corbits-openai-responses", "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-xai-provider-9f2d4ba", "sha512-ZjvKxGcp+QzCh8zeXHg7kNbtkpR/itZkmQAcdG1aOAOjHaNwfGVpkPc/c/XtwPNsQyaDjF8W9CHb4rH1xI2uCA=="], "better-call/@better-auth/utils": ["@better-auth/utils@0.5.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA=="], @@ -1959,6 +1965,14 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + "@workbench/hub/@corbits/codex-provider/@corbits/openai-responses": ["@corbits/openai-responses@github:corbitsdev/corbits-openai-responses#7efe000", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-openai-responses-7efe000", "sha512-pH6C8T1WDJjqM+YbnQpofd7HgP5krEj48JCCQK16KI98a6kaz+gffpgdOdQpU3aNVZnFlyADXod1Z4tqUG/3ww=="], + + "@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=="], + + "@workbench/hub/@corbits/mcp/@corbits/credential-mcp": ["@corbits/credential-mcp@github:corbitsdev/corbits-credential-mcp#fb9ec3a", { "peerDependencies": { "@intx/harness": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-credential-mcp-fb9ec3a", "sha512-Co4jR+L3pfBbVYY7I6jRftoHeSb5+LCxzEEaAhVLYA3aY2PrPHZV37+DWxj3fcK/KdzboqbT3XSnqU8DyHRU9Q=="], + + "@workbench/hub/@corbits/xai-provider/@corbits/openai-responses": ["@corbits/openai-responses@github:corbitsdev/corbits-openai-responses#7efe000", { "dependencies": { "arktype": "2.2.3" }, "peerDependencies": { "@intx/inference": ">=0.3.0", "@intx/types": ">=0.3.0" } }, "corbitsdev-corbits-openai-responses-7efe000", "sha512-pH6C8T1WDJjqM+YbnQpofd7HgP5krEj48JCCQK16KI98a6kaz+gffpgdOdQpU3aNVZnFlyADXod1Z4tqUG/3ww=="], + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],