@vercel/factory 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +356 -0
- package/README.md +49 -261
- package/dist/agent-routes.d.mts +47 -3
- package/dist/agent-routes.mjs +28 -1
- package/dist/agent-routes.mjs.map +1 -1
- package/dist/api-contracts.d.mts +20 -1
- package/dist/api-contracts.mjs +2 -1
- package/dist/api-contracts.mjs.map +1 -1
- package/dist/api.d.mts +45 -2
- package/dist/api.mjs +199 -10
- package/dist/api.mjs.map +1 -1
- package/dist/approval-contracts.d.mts +6 -0
- package/dist/blob/index.d.mts +51 -14
- package/dist/blob/index.mjs +26 -10
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +7 -0
- package/dist/budget.mjs +6 -0
- package/dist/budget.mjs.map +1 -1
- package/dist/build-factory.d.mts +1 -0
- package/dist/change-verification/dispatch.d.mts +16 -3
- package/dist/change-verification/dispatch.mjs +45 -7
- package/dist/change-verification/dispatch.mjs.map +1 -1
- package/dist/change-verification/eve-tool.d.mts +2 -1
- package/dist/change-verification/eve-tool.mjs +35 -47
- package/dist/change-verification/eve-tool.mjs.map +1 -1
- package/dist/change-verification/result.mjs +130 -0
- package/dist/change-verification/result.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +2 -2
- package/dist/changes/eve-record-change.mjs +43 -9
- package/dist/changes/eve-record-change.mjs.map +1 -1
- package/dist/changes.d.mts +4 -3
- package/dist/changes.mjs +2 -2
- package/dist/changes.mjs.map +1 -1
- package/dist/client-events.d.mts +10 -3
- package/dist/client-events.mjs +6 -2
- package/dist/client-events.mjs.map +1 -1
- package/dist/client-stream.mjs +8 -2
- package/dist/client-stream.mjs.map +1 -1
- package/dist/client-transcript.mjs +5 -1
- package/dist/client-transcript.mjs.map +1 -1
- package/dist/client.d.mts +121 -12
- package/dist/client.mjs +117 -9
- package/dist/client.mjs.map +1 -1
- package/dist/code-review/contracts.d.mts +1 -0
- package/dist/code-review/eve-post-review.d.mts +4 -4
- package/dist/code-review/eve-post-review.mjs +29 -17
- package/dist/code-review/eve-post-review.mjs.map +1 -1
- package/dist/code-review/eve-review-comments.d.mts +13 -2
- package/dist/code-review/eve-review-comments.mjs +45 -11
- package/dist/code-review/eve-review-comments.mjs.map +1 -1
- package/dist/code-review/github-reporter.d.mts +2 -0
- package/dist/code-review/github-reporter.mjs +7 -5
- package/dist/code-review/github-reporter.mjs.map +1 -1
- package/dist/code-review.d.mts +3 -2
- package/dist/deepsec/eve-tool.mjs +3 -1
- package/dist/deepsec/eve-tool.mjs.map +1 -1
- package/dist/dispatch.d.mts +79 -8
- package/dist/dispatch.mjs +68 -9
- package/dist/dispatch.mjs.map +1 -1
- package/dist/eve/index.d.mts +70 -10
- package/dist/eve/index.mjs +93 -22
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.mjs +24 -11
- package/dist/eve/invoke.mjs.map +1 -1
- package/dist/eve/session-client.d.mts +122 -4
- package/dist/eve/session-client.mjs +127 -13
- package/dist/eve/session-client.mjs.map +1 -1
- package/dist/eve/task-execution.d.mts +380 -0
- package/dist/eve/task-execution.mjs +57 -2
- package/dist/eve/task-execution.mjs.map +1 -1
- package/dist/eve/task-session.d.mts +44 -2
- package/dist/eve/task-session.mjs +44 -2
- package/dist/eve/task-session.mjs.map +1 -1
- package/dist/eve/transcript.mjs +5 -1
- package/dist/eve/transcript.mjs.map +1 -1
- package/dist/execution.d.mts +117 -9
- package/dist/execution.mjs +76 -6
- package/dist/execution.mjs.map +1 -1
- package/dist/finding-remediation/admission.d.mts +2 -0
- package/dist/finding-remediation/admission.mjs +4 -1
- package/dist/finding-remediation/admission.mjs.map +1 -1
- package/dist/findings.d.mts +1 -0
- package/dist/github-publication.d.mts +1 -0
- package/dist/github-publication.mjs +97 -84
- package/dist/github-publication.mjs.map +1 -1
- package/dist/github-transfer.d.mts +15 -6
- package/dist/github-transfer.mjs +214 -66
- package/dist/github-transfer.mjs.map +1 -1
- package/dist/github.d.mts +51 -11
- package/dist/github.mjs +126 -24
- package/dist/github.mjs.map +1 -1
- package/dist/inbox-activity.d.mts +53 -0
- package/dist/inbox-activity.mjs +41 -0
- package/dist/inbox-activity.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -2
- package/dist/intake-contracts.d.mts +0 -1
- package/dist/integrations/deepsec.d.mts +1 -0
- package/dist/integrations/github.d.mts +2 -2
- package/dist/integrations/github.mjs +2 -2
- package/dist/integrations/slack.d.mts +3 -1
- package/dist/integrations/slack.mjs +3 -1
- package/dist/integrations/vercel.d.mts +4 -2
- package/dist/integrations/vercel.mjs +3 -2
- package/dist/merge-resolution/eve-tools.d.mts +1 -0
- package/dist/merge-resolution/eve-tools.mjs +7 -2
- package/dist/merge-resolution/eve-tools.mjs.map +1 -1
- package/dist/model-settings.d.mts +41 -0
- package/dist/model-settings.mjs +35 -0
- package/dist/model-settings.mjs.map +1 -0
- package/dist/planning/reconcile.mjs +6 -0
- package/dist/planning/reconcile.mjs.map +1 -1
- package/dist/postgres/index.d.mts +43 -2
- package/dist/postgres/index.mjs +40 -2
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/presets/software-development/dispatch.d.mts +4 -1
- package/dist/presets/software-development/dispatch.mjs +2 -1
- package/dist/presets/software-development/dispatch.mjs.map +1 -1
- package/dist/presets/software-development/task-communication.d.mts +1 -0
- package/dist/presets/software-development/task-communication.mjs +48 -11
- package/dist/presets/software-development/task-communication.mjs.map +1 -1
- package/dist/presets/software-development.d.mts +1 -0
- package/dist/pull-requests/github-publisher.d.mts +15 -1
- package/dist/pull-requests/github-publisher.mjs +61 -1
- package/dist/pull-requests/github-publisher.mjs.map +1 -1
- package/dist/pull-requests.d.mts +1 -0
- package/dist/sandbox/index.d.mts +1 -0
- package/dist/schema/agent-route.d.mts +19 -1
- package/dist/schema/agent-route.mjs +19 -1
- package/dist/schema/agent-route.mjs.map +1 -1
- package/dist/schema/factory-config.d.mts +27 -0
- package/dist/schema/factory-config.mjs +33 -3
- package/dist/schema/factory-config.mjs.map +1 -1
- package/dist/schema/repository.d.mts +4 -0
- package/dist/schema/repository.mjs +5 -1
- package/dist/schema/repository.mjs.map +1 -1
- package/dist/schema/session.d.mts +1 -0
- package/dist/schema/session.mjs +1 -0
- package/dist/schema/session.mjs.map +1 -1
- package/dist/schema/slack-pr-notifications.d.mts +12 -0
- package/dist/schema/slack-pr-notifications.mjs +11 -0
- package/dist/schema/slack-pr-notifications.mjs.map +1 -0
- package/dist/schema/task-graph.d.mts +39 -0
- package/dist/schema/task.d.mts +1 -0
- package/dist/schema/task.mjs +2 -1
- package/dist/schema/task.mjs.map +1 -1
- package/dist/schema/transcript.d.mts +6 -0
- package/dist/schema/transcript.mjs +2 -1
- package/dist/schema/transcript.mjs.map +1 -1
- package/dist/schema/work.d.mts +52 -3
- package/dist/schema/work.mjs.map +1 -1
- package/dist/session-previews.d.mts +76 -0
- package/dist/session-previews.mjs +55 -0
- package/dist/session-previews.mjs.map +1 -0
- package/dist/session-review.d.mts +120 -0
- package/dist/session-review.mjs +79 -0
- package/dist/session-review.mjs.map +1 -0
- package/dist/signal-triage.mjs +1 -1
- package/dist/signals.d.mts +1 -0
- package/dist/stall.d.mts +4 -1
- package/dist/stall.mjs +6 -2
- package/dist/stall.mjs.map +1 -1
- package/dist/store/driver.d.mts +1 -1
- package/dist/store/driver.mjs.map +1 -1
- package/dist/store/engine.d.mts +206 -8
- package/dist/store/engine.mjs +147 -13
- package/dist/store/engine.mjs.map +1 -1
- package/dist/store/memory.d.mts +18 -1
- package/dist/store/memory.mjs +18 -1
- package/dist/store/memory.mjs.map +1 -1
- package/dist/store/slack-pr-notifications.d.mts +44 -0
- package/dist/store/slack-pr-notifications.mjs +121 -0
- package/dist/store/slack-pr-notifications.mjs.map +1 -0
- package/dist/store/task-work.d.mts +121 -6
- package/dist/store/task-work.mjs +7 -4
- package/dist/store/task-work.mjs.map +1 -1
- package/dist/sweep.d.mts +28 -6
- package/dist/sweep.mjs +34 -6
- package/dist/sweep.mjs.map +1 -1
- package/dist/task-graph-view.d.mts +3 -0
- package/dist/tasks.d.mts +3 -3
- package/dist/tasks.mjs +3 -3
- package/dist/vercel-git.d.mts +35 -3
- package/dist/vercel-git.mjs +265 -33
- package/dist/vercel-git.mjs.map +1 -1
- package/dist/vercel-github-api.d.mts +103 -0
- package/dist/vercel-github-api.mjs +363 -0
- package/dist/vercel-github-api.mjs.map +1 -0
- package/dist/vercel.d.mts +3 -2
- package/dist/vercel.mjs +3 -2
- package/dist/vercel.mjs.map +1 -1
- package/dist/work-triage.d.mts +1 -0
- package/dist/workflows.d.mts +102 -4
- package/dist/workflows.mjs +55 -2
- package/dist/workflows.mjs.map +1 -1
- package/dist/workspace-files-git.d.mts +15 -0
- package/dist/workspace-files-git.mjs +61 -0
- package/dist/workspace-files-git.mjs.map +1 -0
- package/dist/workspace-files.d.mts +107 -0
- package/dist/workspace-files.mjs +74 -0
- package/dist/workspace-files.mjs.map +1 -0
- package/docs/getting-started.md +104 -0
- package/docs/index.md +100 -0
- package/docs/recipes/cancellation.md +215 -0
- package/docs/recipes/custom-workflow.md +153 -0
- package/docs/recipes/dependent-tasks.md +207 -0
- package/docs/recipes/eve-agent.md +277 -0
- package/docs/recipes/human-input.md +204 -0
- package/docs/recipes/persistence-recovery.md +268 -0
- package/docs/recipes/retry-recovery.md +241 -0
- package/docs/recipes/task-messaging.md +215 -0
- package/docs/recipes/typed-eve-result.md +161 -0
- package/docs/runtime-integration.md +137 -0
- package/package.json +17 -6
package/dist/vercel.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel.mjs","names":[],"sources":["../src/vercel.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n workNotificationSchema,\n type WorkNotification,\n type WorkNotifications,\n} from \"./store/work-notifications\";\n\n/** Provider delivery settings applied to one Vercel Queue message. */\nexport interface VercelQueueSendOptions {\n region: string;\n retentionSeconds: number;\n idempotencyKey: string;\n delaySeconds: number;\n}\n\n/** Injectable Vercel Queue send operation for one validated Factory work notification. */\nexport type VercelQueueSend = (\n topic: string,\n message: WorkNotification,\n options: VercelQueueSendOptions,\n) => Promise<unknown>;\n\n/** Configures Vercel Queue transport, failure reporting, retention, and runtime guards. */\nexport interface VercelWorkQueueOptions {\n send: VercelQueueSend;\n onError(error: unknown, event: WorkNotification): void;\n topic?: string;\n region?: string;\n retentionSeconds?: number;\n environment?: Readonly<Record<string, string | undefined>>;\n}\n\n/** Publishes graph wakeups and exposes the hosted-runtime notification adapter. */\nexport interface VercelWorkQueue {\n publish(event: WorkNotification, delaySeconds?: number): Promise<void>;\n hostedNotifications(): WorkNotifications | undefined;\n}\n\nexport {\n createVercelGitHubGitTransport,\n type VercelGitHubGitTransportOptions,\n type VercelGitHubTokenInput,\n} from \"./vercel-git\";\n\n/** Configure graph wakeups over Vercel Queues without granting queue messages execution authority. */\nexport function createVercelWorkQueue(options: VercelWorkQueueOptions): VercelWorkQueue {\n const environment = options.environment ?? process.env;\n const topic = options.topic ?? \"factory-work\";\n const region = options.region ?? \"iad1\";\n const retentionSeconds = options.retentionSeconds ?? 604_800;\n const hosted = () => environment.VERCEL === \"1\" && environment.FACTORY_LOCAL_TEST !== \"1\";\n const publish = async (event: WorkNotification, delaySeconds = 0): Promise<void> => {\n if (!hosted()) throw new Error(\"Vercel work queues require a hosted, non-fixture runtime\");\n const message = workNotificationSchema.parse(event);\n await options.send(topic, message, {\n region,\n retentionSeconds,\n idempotencyKey: message.key,\n delaySeconds,\n });\n };\n const notifications: WorkNotifications = { publish, onError: options.onError };\n return Object.freeze({\n publish,\n hostedNotifications: () => (hosted() ? notifications : undefined),\n });\n}\n\n/**\n * Verify a Vercel webhook signature (the x-vercel-signature header): an\n * HMAC-SHA1 hex digest of the raw request body. Returns false for missing or\n * malformed signatures instead of throwing.\n */\nexport function verifyVercelSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !/^[0-9a-f]{40}$/.test(signature)) {\n return false;\n }\n const expected = createHmac(\"sha1\", secret).update(payload).digest(\"hex\");\n return timingSafeEqual(Buffer.from(signature, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst observedDeploymentEvents = new Set([\"deployment.succeeded\", \"deployment.promoted\"]);\n\nconst deploymentEventSchema = z.looseObject({\n id: z.string().min(1),\n type: z.string(),\n payload: z.looseObject({\n target: z.string().nullable().optional(),\n project: z.looseObject({ id: z.string().min(1) }),\n deployment: z.looseObject({\n id: z.string().min(1),\n url: z.string().min(1),\n meta: z\n .looseObject({\n githubCommitSha: z.string().optional(),\n githubCommitOrg: z.string().optional(),\n githubCommitRepo: z.string().optional(),\n githubOrg: z.string().optional(),\n githubRepo: z.string().optional(),\n })\n .optional(),\n }),\n }),\n});\n\n/** A normalized production deployment observation with optional Git commit identity. */\nexport interface VercelProductionDeployment {\n source: \"vercel_production_deployment\";\n deliveryId: string;\n eventType: string;\n deploymentId: string;\n deploymentUrl: string;\n projectId: string;\n /** owner/name from the deployment's git metadata; null for non-git deployments. */\n repositorySlug: RepositorySlug | null;\n /** Commit the deployment was built from; null for non-git deployments. */\n commitSha: string | null;\n}\n\n/**\n * Normalize a deployment.succeeded or deployment.promoted webhook for a\n * production deployment so the factory can observe a merge reaching\n * production. Returns null for other events and non-production targets.\n */\nexport function normalizeVercelProductionDeployment(\n payload: unknown,\n): VercelProductionDeployment | null {\n const parsed = deploymentEventSchema.safeParse(payload);\n if (!parsed.success || !observedDeploymentEvents.has(parsed.data.type)) {\n return null;\n }\n const event = parsed.data.payload;\n if (event.target !== \"production\") {\n return null;\n }\n const meta = event.deployment.meta;\n const org = meta?.githubCommitOrg ?? meta?.githubOrg;\n const repo = meta?.githubCommitRepo ?? meta?.githubRepo;\n const repositorySlug =\n org === undefined || repo === undefined\n ? null\n : (repositorySlugSchema.safeParse(`${org}/${repo}`.toLowerCase()).data ?? null);\n return {\n source: \"vercel_production_deployment\",\n deliveryId: parsed.data.id,\n eventType: parsed.data.type,\n deploymentId: event.deployment.id,\n deploymentUrl: event.deployment.url,\n projectId: event.project.id,\n repositorySlug,\n commitSha: meta?.githubCommitSha ?? null,\n };\n}\n\n/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */\nexport async function authenticateVercelUserToken(\n request: Request,\n fetcher: typeof fetch = fetch,\n): Promise<string | null> {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization?.match(/^Bearer \\S+$/u)) return null;\n const response = await fetcher(\"https://api.vercel.com/v2/user\", {\n headers: { authorization },\n cache: \"no-store\",\n redirect: \"error\",\n signal: AbortSignal.timeout(10_000),\n });\n if (response.status === 401 || response.status === 403) return null;\n if (!response.ok) throw new Error(`Vercel identity lookup failed (${response.status})`);\n const result = z\n .object({ user: z.object({ id: z.string().min(1) }) })\n .safeParse(await response.json());\n return result.success ? `vercel:${result.data.user.id}` : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vercel.mjs","names":[],"sources":["../src/vercel.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n workNotificationSchema,\n type WorkNotification,\n type WorkNotifications,\n} from \"./store/work-notifications\";\n\n/** Provider delivery settings applied to one Vercel Queue message. */\nexport interface VercelQueueSendOptions {\n region: string;\n retentionSeconds: number;\n idempotencyKey: string;\n delaySeconds: number;\n}\n\n/** Injectable Vercel Queue send operation for one validated Factory work notification. */\nexport type VercelQueueSend = (\n topic: string,\n message: WorkNotification,\n options: VercelQueueSendOptions,\n) => Promise<unknown>;\n\n/** Configures Vercel Queue transport, failure reporting, retention, and runtime guards. */\nexport interface VercelWorkQueueOptions {\n send: VercelQueueSend;\n onError(error: unknown, event: WorkNotification): void;\n topic?: string;\n region?: string;\n retentionSeconds?: number;\n environment?: Readonly<Record<string, string | undefined>>;\n}\n\n/** Publishes graph wakeups and exposes the hosted-runtime notification adapter. */\nexport interface VercelWorkQueue {\n publish(event: WorkNotification, delaySeconds?: number): Promise<void>;\n hostedNotifications(): WorkNotifications | undefined;\n}\n\nexport {\n createVercelManagedGitHubApi,\n VercelManagedGitHubApiError,\n type VercelManagedGitHubApi,\n type VercelManagedGitHubApiAction,\n type VercelManagedGitHubApiOptions,\n type VercelManagedGitHubApiSandboxCreateInput,\n type VercelManagedGitHubIssue,\n type VercelManagedGitHubIssueInput,\n type VercelManagedGitHubPullRequest,\n type VercelManagedGitHubPullRequestInput,\n type VercelManagedGitHubPublication,\n type VercelManagedGitHubPublicationInput,\n} from \"./vercel-github-api\";\n\nexport {\n createVercelGitHubGitTransport,\n createVercelManagedGitHubGitTransport,\n type VercelGitHubGitTransportOptions,\n type VercelGitHubTokenInput,\n type VercelManagedGitHubGitTransportOptions,\n type VercelManagedGitHubSandboxCreateInput,\n} from \"./vercel-git\";\n\n/** Configure graph wakeups over Vercel Queues without granting queue messages execution authority. */\nexport function createVercelWorkQueue(options: VercelWorkQueueOptions): VercelWorkQueue {\n const environment = options.environment ?? process.env;\n const topic = options.topic ?? \"factory-work\";\n const region = options.region ?? \"iad1\";\n const retentionSeconds = options.retentionSeconds ?? 604_800;\n const hosted = () => environment.VERCEL === \"1\" && environment.FACTORY_LOCAL_TEST !== \"1\";\n const publish = async (event: WorkNotification, delaySeconds = 0): Promise<void> => {\n if (!hosted()) throw new Error(\"Vercel work queues require a hosted, non-fixture runtime\");\n const message = workNotificationSchema.parse(event);\n await options.send(topic, message, {\n region,\n retentionSeconds,\n idempotencyKey: message.key,\n delaySeconds,\n });\n };\n const notifications: WorkNotifications = { publish, onError: options.onError };\n return Object.freeze({\n publish,\n hostedNotifications: () => (hosted() ? notifications : undefined),\n });\n}\n\n/**\n * Verify a Vercel webhook signature (the x-vercel-signature header): an\n * HMAC-SHA1 hex digest of the raw request body. Returns false for missing or\n * malformed signatures instead of throwing.\n */\nexport function verifyVercelSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !/^[0-9a-f]{40}$/.test(signature)) {\n return false;\n }\n const expected = createHmac(\"sha1\", secret).update(payload).digest(\"hex\");\n return timingSafeEqual(Buffer.from(signature, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst observedDeploymentEvents = new Set([\"deployment.succeeded\", \"deployment.promoted\"]);\n\nconst deploymentEventSchema = z.looseObject({\n id: z.string().min(1),\n type: z.string(),\n payload: z.looseObject({\n target: z.string().nullable().optional(),\n project: z.looseObject({ id: z.string().min(1) }),\n deployment: z.looseObject({\n id: z.string().min(1),\n url: z.string().min(1),\n meta: z\n .looseObject({\n githubCommitSha: z.string().optional(),\n githubCommitOrg: z.string().optional(),\n githubCommitRepo: z.string().optional(),\n githubOrg: z.string().optional(),\n githubRepo: z.string().optional(),\n })\n .optional(),\n }),\n }),\n});\n\n/** A normalized production deployment observation with optional Git commit identity. */\nexport interface VercelProductionDeployment {\n source: \"vercel_production_deployment\";\n deliveryId: string;\n eventType: string;\n deploymentId: string;\n deploymentUrl: string;\n projectId: string;\n /** owner/name from the deployment's git metadata; null for non-git deployments. */\n repositorySlug: RepositorySlug | null;\n /** Commit the deployment was built from; null for non-git deployments. */\n commitSha: string | null;\n}\n\n/**\n * Normalize a deployment.succeeded or deployment.promoted webhook for a\n * production deployment so the factory can observe a merge reaching\n * production. Returns null for other events and non-production targets.\n */\nexport function normalizeVercelProductionDeployment(\n payload: unknown,\n): VercelProductionDeployment | null {\n const parsed = deploymentEventSchema.safeParse(payload);\n if (!parsed.success || !observedDeploymentEvents.has(parsed.data.type)) {\n return null;\n }\n const event = parsed.data.payload;\n if (event.target !== \"production\") {\n return null;\n }\n const meta = event.deployment.meta;\n const org = meta?.githubCommitOrg ?? meta?.githubOrg;\n const repo = meta?.githubCommitRepo ?? meta?.githubRepo;\n const repositorySlug =\n org === undefined || repo === undefined\n ? null\n : (repositorySlugSchema.safeParse(`${org}/${repo}`.toLowerCase()).data ?? null);\n return {\n source: \"vercel_production_deployment\",\n deliveryId: parsed.data.id,\n eventType: parsed.data.type,\n deploymentId: event.deployment.id,\n deploymentUrl: event.deployment.url,\n projectId: event.project.id,\n repositorySlug,\n commitSha: meta?.githubCommitSha ?? null,\n };\n}\n\n/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */\nexport async function authenticateVercelUserToken(\n request: Request,\n fetcher: typeof fetch = fetch,\n): Promise<string | null> {\n const authorization = request.headers.get(\"authorization\");\n if (!authorization?.match(/^Bearer \\S+$/u)) return null;\n const response = await fetcher(\"https://api.vercel.com/v2/user\", {\n headers: { authorization },\n cache: \"no-store\",\n redirect: \"error\",\n signal: AbortSignal.timeout(10_000),\n });\n if (response.status === 401 || response.status === 403) return null;\n if (!response.ok) throw new Error(`Vercel identity lookup failed (${response.status})`);\n const result = z\n .object({ user: z.object({ id: z.string().min(1) }) })\n .safeParse(await response.json());\n return result.success ? `vercel:${result.data.user.id}` : null;\n}\n"],"mappings":"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"}
|
package/dist/work-triage.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { ExistingWorkLookupOptions, ExistingWorkLookupResult, FindExistingWorkInput, findExistingWork } from "./work-triage/find-existing-work.mjs";
|
|
2
3
|
import { FindExistingWorkTool, FindExistingWorkToolInput, FindExistingWorkToolOptions, FindExistingWorkToolStores, createFindExistingWorkTool } from "./work-triage/eve-tool.mjs";
|
|
3
4
|
export { type ExistingWorkLookupOptions, type ExistingWorkLookupResult, type FindExistingWorkInput, type FindExistingWorkTool, type FindExistingWorkToolInput, type FindExistingWorkToolOptions, type FindExistingWorkToolStores, createFindExistingWorkTool, findExistingWork };
|
package/dist/workflows.d.mts
CHANGED
|
@@ -7,13 +7,23 @@ import { z } from "zod";
|
|
|
7
7
|
declare const defaultTaskWorkflow: Readonly<WorkflowDefinition<z.ZodCustom<JsonValue, JsonValue>, z.ZodCustom<JsonValue, JsonValue>, "task", 1>>;
|
|
8
8
|
/** Caller input accepted when defining one exact workflow contract. */
|
|
9
9
|
interface WorkflowDefinitionInput<Input extends z.ZodType = z.ZodType, Output extends z.ZodType = z.ZodType, Id extends string = string, Version extends number = number> {
|
|
10
|
+
/** Stable lowercase workflow identifier persisted on every Task. */
|
|
10
11
|
readonly id: Id;
|
|
12
|
+
/** Positive exact version; callers and recovery never select an implicit latest version. */
|
|
11
13
|
readonly version: Version;
|
|
14
|
+
/** Schema that validates caller input and supplies its static input type. */
|
|
12
15
|
readonly input: Input;
|
|
16
|
+
/** Schema that validates successful completion and supplies its static output type. */
|
|
13
17
|
readonly output: Output;
|
|
14
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Canonical persisted-input schema required when `input` transforms its value. It must validate
|
|
20
|
+
* without changing the stored JSON shape.
|
|
21
|
+
*/
|
|
15
22
|
readonly storedInput?: z.ZodType<NoInfer<z.output<Input>>>;
|
|
16
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Canonical persisted-output schema required when `output` transforms its value. It must
|
|
25
|
+
* validate without changing the stored JSON shape.
|
|
26
|
+
*/
|
|
17
27
|
readonly storedOutput?: z.ZodType<NoInfer<z.output<Output>>>;
|
|
18
28
|
}
|
|
19
29
|
/** Exact-version workflow contract with a typed binding for constructing work. */
|
|
@@ -32,16 +42,104 @@ type WorkflowForBinding<Definition extends WorkflowDefinition, Binding extends W
|
|
|
32
42
|
} ? Definition : never : never;
|
|
33
43
|
/** Immutable registry that validates data against exact workflow versions. */
|
|
34
44
|
interface WorkflowRegistry<Definition extends WorkflowDefinition = WorkflowDefinition> {
|
|
45
|
+
/** Immutable exact-version definitions in registration order. */
|
|
35
46
|
readonly definitions: readonly Readonly<Definition>[];
|
|
47
|
+
/**
|
|
48
|
+
* Resolves one exact binding, throwing with the registered alternatives when it is unknown.
|
|
49
|
+
*
|
|
50
|
+
* @param binding - Exact workflow ID and version.
|
|
51
|
+
* @returns The matching immutable workflow definition.
|
|
52
|
+
*/
|
|
36
53
|
resolve<const Binding extends WorkflowBinding>(binding: Binding): Readonly<WorkflowForBinding<Definition, Binding>>;
|
|
54
|
+
/**
|
|
55
|
+
* Validates caller input through the selected workflow and returns bounded canonical JSON.
|
|
56
|
+
*
|
|
57
|
+
* @param binding - Exact workflow ID and version.
|
|
58
|
+
* @param value - Untrusted caller input to validate and transform.
|
|
59
|
+
* @returns Validated bounded JSON produced by the input schema.
|
|
60
|
+
*/
|
|
37
61
|
validateInput(binding: WorkflowBinding, value: unknown): JsonValue;
|
|
62
|
+
/**
|
|
63
|
+
* Validates candidate completion output through the selected workflow.
|
|
64
|
+
*
|
|
65
|
+
* @param binding - Exact workflow ID and version.
|
|
66
|
+
* @param value - Untrusted candidate output to validate and transform.
|
|
67
|
+
* @returns Validated bounded JSON produced by the output schema.
|
|
68
|
+
*/
|
|
38
69
|
validateOutput(binding: WorkflowBinding, value: unknown): JsonValue;
|
|
70
|
+
/**
|
|
71
|
+
* Validates persisted input without permitting a schema transform to change its JSON shape.
|
|
72
|
+
*
|
|
73
|
+
* @param binding - Exact workflow ID and version stored on the Task.
|
|
74
|
+
* @param value - Canonical persisted JSON to validate.
|
|
75
|
+
* @returns A cloned validated value equal to the persisted JSON.
|
|
76
|
+
*/
|
|
39
77
|
validateStoredInput(binding: WorkflowBinding, value: unknown): JsonValue;
|
|
78
|
+
/**
|
|
79
|
+
* Validates persisted output without permitting a schema transform to change its JSON shape.
|
|
80
|
+
*
|
|
81
|
+
* @param binding - Exact workflow ID and version stored on the Task.
|
|
82
|
+
* @param value - Canonical persisted JSON to validate.
|
|
83
|
+
* @returns A cloned validated value equal to the persisted JSON.
|
|
84
|
+
*/
|
|
40
85
|
validateStoredOutput(binding: WorkflowBinding, value: unknown): JsonValue;
|
|
41
86
|
}
|
|
42
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Validates and freezes one exact workflow definition without executing it.
|
|
89
|
+
*
|
|
90
|
+
* @remarks
|
|
91
|
+
* A definition is a typed contract, not a registered runtime. Pass every definition that stored
|
|
92
|
+
* Tasks may reference to `defineWorkflows`, then supply that registry to `createStores`. Input and
|
|
93
|
+
* output schemas may transform caller values only when `storedInput` or `storedOutput` describes
|
|
94
|
+
* the unchanged canonical JSON representation used after persistence.
|
|
95
|
+
*
|
|
96
|
+
* @param definition - Stable ID, positive version, and Zod input/output contracts.
|
|
97
|
+
* @returns An immutable definition whose binding carries the input and output types.
|
|
98
|
+
* @throws When the binding is invalid or the input/output fields are not Zod schemas.
|
|
99
|
+
* @see The shipped `docs/recipes/custom-workflow.md` recipe for registration and completion.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { defineWorkflow, defineWorkflows } from "@vercel/factory/workflows";
|
|
104
|
+
* import { z } from "zod";
|
|
105
|
+
*
|
|
106
|
+
* const greeting = defineWorkflow({
|
|
107
|
+
* id: "greeting",
|
|
108
|
+
* version: 1,
|
|
109
|
+
* input: z.object({ name: z.string() }),
|
|
110
|
+
* output: z.object({ message: z.string() }),
|
|
111
|
+
* });
|
|
112
|
+
* const workflows = defineWorkflows([greeting]);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
43
115
|
declare function defineWorkflow<Input extends z.ZodType, Output extends z.ZodType, const Id extends string, const Version extends number>(definition: WorkflowDefinitionInput<Input, Output, Id, Version>): Readonly<WorkflowDefinition<Input, Output, Id, Version>>;
|
|
44
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Builds an immutable exact-version workflow registry and rejects duplicate definitions.
|
|
118
|
+
*
|
|
119
|
+
* @remarks
|
|
120
|
+
* Register old versions for as long as persisted Tasks can still reference them. Resolution uses
|
|
121
|
+
* both ID and version and never falls forward to another version. Creating a new registry has no
|
|
122
|
+
* persistence side effects.
|
|
123
|
+
*
|
|
124
|
+
* @param definitions - All exact workflow versions readable by this runtime.
|
|
125
|
+
* @returns A registry for resolving bindings and validating caller and persisted values.
|
|
126
|
+
* @throws When a definition is invalid or the same `id@version` appears more than once.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { defineWorkflow, defineWorkflows } from "@vercel/factory/workflows";
|
|
131
|
+
* import { z } from "zod";
|
|
132
|
+
*
|
|
133
|
+
* const v1 = defineWorkflow({
|
|
134
|
+
* id: "report",
|
|
135
|
+
* version: 1,
|
|
136
|
+
* input: z.object({ topic: z.string() }),
|
|
137
|
+
* output: z.object({ summary: z.string() }),
|
|
138
|
+
* });
|
|
139
|
+
* const workflows = defineWorkflows([v1]);
|
|
140
|
+
* workflows.resolve(v1.binding);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
45
143
|
declare function defineWorkflows<const Definition extends WorkflowDefinitionInput>(definitions: readonly Definition[]): WorkflowRegistry<RegisteredWorkflow<Definition>>;
|
|
46
144
|
//#endregion
|
|
47
145
|
export { type AgentRoute, type AgentRouteBinding, type AgentRouteBindingInput, type AgentRouteInput, type AgentRouteKey, type AgentRouteRegistry, type JsonValue, MAX_WORK_JSON_BYTES, MAX_WORK_JSON_DEPTH, type RouteTaskWorkOptions, type RoutedTaskWork, type TaskWork, type TaskWorkOptions, type TaskWorkResult, type WorkflowBinding, type WorkflowBindingInput, WorkflowDefinition, WorkflowDefinitionInput, WorkflowInput, WorkflowOutput, type WorkflowReference, type WorkflowReferenceInput, WorkflowRegistry, type WorkflowTaskWorkOptions, agentRouteBindingSchema, agentRouteSchema, defaultTaskWorkflow, defineAgentRoutes, defineWorkflow, defineWorkflows, jsonValueSchema, parseAgentRouteBinding, parseWorkflowBinding, routeKey, routeTaskWork, taskWork, taskWorkResultSchema, taskWorkSchema, workflowBindingSchema };
|
package/dist/workflows.mjs
CHANGED
|
@@ -11,7 +11,34 @@ const defaultTaskWorkflow = defineWorkflow({
|
|
|
11
11
|
input: jsonValueSchema,
|
|
12
12
|
output: jsonValueSchema
|
|
13
13
|
});
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Validates and freezes one exact workflow definition without executing it.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* A definition is a typed contract, not a registered runtime. Pass every definition that stored
|
|
19
|
+
* Tasks may reference to `defineWorkflows`, then supply that registry to `createStores`. Input and
|
|
20
|
+
* output schemas may transform caller values only when `storedInput` or `storedOutput` describes
|
|
21
|
+
* the unchanged canonical JSON representation used after persistence.
|
|
22
|
+
*
|
|
23
|
+
* @param definition - Stable ID, positive version, and Zod input/output contracts.
|
|
24
|
+
* @returns An immutable definition whose binding carries the input and output types.
|
|
25
|
+
* @throws When the binding is invalid or the input/output fields are not Zod schemas.
|
|
26
|
+
* @see The shipped `docs/recipes/custom-workflow.md` recipe for registration and completion.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { defineWorkflow, defineWorkflows } from "@vercel/factory/workflows";
|
|
31
|
+
* import { z } from "zod";
|
|
32
|
+
*
|
|
33
|
+
* const greeting = defineWorkflow({
|
|
34
|
+
* id: "greeting",
|
|
35
|
+
* version: 1,
|
|
36
|
+
* input: z.object({ name: z.string() }),
|
|
37
|
+
* output: z.object({ message: z.string() }),
|
|
38
|
+
* });
|
|
39
|
+
* const workflows = defineWorkflows([greeting]);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
15
42
|
function defineWorkflow(definition) {
|
|
16
43
|
const parsedBinding = parseWorkflowBinding({
|
|
17
44
|
id: definition.id,
|
|
@@ -30,7 +57,33 @@ function defineWorkflow(definition) {
|
|
|
30
57
|
storedOutput: definition.storedOutput
|
|
31
58
|
});
|
|
32
59
|
}
|
|
33
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Builds an immutable exact-version workflow registry and rejects duplicate definitions.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* Register old versions for as long as persisted Tasks can still reference them. Resolution uses
|
|
65
|
+
* both ID and version and never falls forward to another version. Creating a new registry has no
|
|
66
|
+
* persistence side effects.
|
|
67
|
+
*
|
|
68
|
+
* @param definitions - All exact workflow versions readable by this runtime.
|
|
69
|
+
* @returns A registry for resolving bindings and validating caller and persisted values.
|
|
70
|
+
* @throws When a definition is invalid or the same `id@version` appears more than once.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { defineWorkflow, defineWorkflows } from "@vercel/factory/workflows";
|
|
75
|
+
* import { z } from "zod";
|
|
76
|
+
*
|
|
77
|
+
* const v1 = defineWorkflow({
|
|
78
|
+
* id: "report",
|
|
79
|
+
* version: 1,
|
|
80
|
+
* input: z.object({ topic: z.string() }),
|
|
81
|
+
* output: z.object({ summary: z.string() }),
|
|
82
|
+
* });
|
|
83
|
+
* const workflows = defineWorkflows([v1]);
|
|
84
|
+
* workflows.resolve(v1.binding);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
34
87
|
function defineWorkflows(definitions) {
|
|
35
88
|
const registered = definitions.map((definition) => defineWorkflow(definition));
|
|
36
89
|
const byBinding = /* @__PURE__ */ new Map();
|
package/dist/workflows.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.mjs","names":[],"sources":["../src/workflows.ts"],"sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { z } from \"zod\";\nimport {\n jsonValueSchema,\n parseWorkflowBinding,\n workflowBindingSchema,\n type JsonValue,\n type WorkflowBinding,\n type WorkflowReference,\n} from \"./schema/work\";\n\nexport {\n jsonValueSchema,\n MAX_WORK_JSON_BYTES,\n MAX_WORK_JSON_DEPTH,\n taskWork,\n taskWorkResultSchema,\n taskWorkSchema,\n parseWorkflowBinding,\n workflowBindingSchema,\n} from \"./schema/work\";\nexport {\n agentRouteBindingSchema,\n agentRouteSchema,\n defineAgentRoutes,\n parseAgentRouteBinding,\n routeKey,\n routeTaskWork,\n type AgentRoute,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n type AgentRouteInput,\n type AgentRouteKey,\n type AgentRouteRegistry,\n type RouteTaskWorkOptions,\n type RoutedTaskWork,\n} from \"./agent-routes\";\n\n/** Generic `task@1` workflow accepting bounded JSON input and output. */\nexport const defaultTaskWorkflow = defineWorkflow({\n id: \"task\",\n version: 1,\n input: jsonValueSchema,\n output: jsonValueSchema,\n});\nexport type {\n JsonValue,\n TaskWork,\n TaskWorkOptions,\n TaskWorkResult,\n WorkflowBinding,\n WorkflowBindingInput,\n WorkflowReference,\n WorkflowReferenceInput,\n WorkflowTaskWorkOptions,\n} from \"./schema/work\";\n\n/** Caller input accepted when defining one exact workflow contract. */\nexport interface WorkflowDefinitionInput<\n Input extends z.ZodType = z.ZodType,\n Output extends z.ZodType = z.ZodType,\n Id extends string = string,\n Version extends number = number,\n> {\n readonly id: Id;\n readonly version: Version;\n readonly input: Input;\n readonly output: Output;\n /** Required when input transformation changes the stored shape; must preserve canonical JSON. */\n readonly storedInput?: z.ZodType<NoInfer<z.output<Input>>>;\n /** Required when output transformation changes the stored shape; must preserve canonical JSON. */\n readonly storedOutput?: z.ZodType<NoInfer<z.output<Output>>>;\n}\n\n/** Exact-version workflow contract with a typed binding for constructing work. */\nexport interface WorkflowDefinition<\n Input extends z.ZodType = z.ZodType,\n Output extends z.ZodType = z.ZodType,\n Id extends string = string,\n Version extends number = number,\n>\n extends\n WorkflowDefinitionInput<Input, Output, Id, Version>,\n WorkflowReference<\n Input,\n WorkflowBinding<z.input<Input> & JsonValue, z.input<Output> & JsonValue, Id, Version>\n > {}\n\n/** JSON input accepted by a workflow definition before its schema transforms it. */\nexport type WorkflowInput<Definition extends WorkflowDefinition> = z.input<Definition[\"input\"]> &\n JsonValue;\n\n/** JSON output accepted by a workflow definition before its schema transforms it. */\nexport type WorkflowOutput<Definition extends WorkflowDefinition> = z.input<Definition[\"output\"]> &\n JsonValue;\n\ntype RegisteredWorkflow<Definition extends WorkflowDefinitionInput> =\n Definition extends WorkflowDefinitionInput<infer Input, infer Output, infer Id, infer Version>\n ? WorkflowDefinition<Input, Output, Id, Version>\n : never;\n\ntype WorkflowForBinding<\n Definition extends WorkflowDefinition,\n Binding extends WorkflowBinding,\n> = Definition extends WorkflowDefinition\n ? Binding extends {\n readonly id: Definition[\"id\"];\n readonly version: Definition[\"version\"];\n }\n ? Definition\n : Definition extends {\n readonly id: Binding[\"id\"];\n readonly version: Binding[\"version\"];\n }\n ? Definition\n : never\n : never;\n\n/** Immutable registry that validates data against exact workflow versions. */\nexport interface WorkflowRegistry<Definition extends WorkflowDefinition = WorkflowDefinition> {\n readonly definitions: readonly Readonly<Definition>[];\n resolve<const Binding extends WorkflowBinding>(\n binding: Binding,\n ): Readonly<WorkflowForBinding<Definition, Binding>>;\n validateInput(binding: WorkflowBinding, value: unknown): JsonValue;\n validateOutput(binding: WorkflowBinding, value: unknown): JsonValue;\n validateStoredInput(binding: WorkflowBinding, value: unknown): JsonValue;\n validateStoredOutput(binding: WorkflowBinding, value: unknown): JsonValue;\n}\n\n/** Validates and freezes one exact workflow definition without executing it. */\nexport function defineWorkflow<\n Input extends z.ZodType,\n Output extends z.ZodType,\n const Id extends string,\n const Version extends number,\n>(\n definition: WorkflowDefinitionInput<Input, Output, Id, Version>,\n): Readonly<WorkflowDefinition<Input, Output, Id, Version>> {\n const parsedBinding = parseWorkflowBinding({\n id: definition.id,\n version: definition.version,\n });\n if (!(definition.input instanceof z.ZodType) || !(definition.output instanceof z.ZodType)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} requires Zod input and output schemas`,\n );\n }\n for (const field of [\"storedInput\", \"storedOutput\"] as const) {\n if (definition[field] !== undefined && !(definition[field] instanceof z.ZodType)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} requires a Zod ${field} schema`,\n );\n }\n }\n const binding: WorkflowBinding<\n z.input<Input> & JsonValue,\n z.input<Output> & JsonValue,\n Id,\n Version\n > = Object.freeze(parsedBinding) as WorkflowBinding<\n z.input<Input> & JsonValue,\n z.input<Output> & JsonValue,\n Id,\n Version\n >;\n return Object.freeze({\n id: definition.id,\n version: definition.version,\n binding,\n input: definition.input,\n output: definition.output,\n storedInput: definition.storedInput,\n storedOutput: definition.storedOutput,\n });\n}\n\n/** Builds an exact-version workflow registry and rejects duplicate definitions. */\nexport function defineWorkflows<const Definition extends WorkflowDefinitionInput>(\n definitions: readonly Definition[],\n): WorkflowRegistry<RegisteredWorkflow<Definition>> {\n const registered = definitions.map((definition) =>\n defineWorkflow(definition),\n ) as unknown as readonly Readonly<RegisteredWorkflow<Definition>>[];\n const byBinding = new Map<string, Readonly<RegisteredWorkflow<Definition>>>();\n for (const definition of registered) {\n const key = `${definition.id}@${definition.version}`;\n if (byBinding.has(key)) {\n throw new Error(\n `Duplicate workflow definition ${key}; register each id and version exactly once`,\n );\n }\n byBinding.set(key, definition);\n }\n\n function resolve<const Binding extends WorkflowBinding>(\n binding: Binding,\n ): Readonly<WorkflowForBinding<RegisteredWorkflow<Definition>, Binding>>;\n function resolve(binding: WorkflowBinding): Readonly<RegisteredWorkflow<Definition>> {\n const { id, version } = workflowBindingSchema.parse(binding);\n const key = `${id}@${version}`;\n const definition = byBinding.get(key);\n if (!definition) {\n const alternatives = [...byBinding.keys()].sort().join(\", \") || \"none\";\n throw new Error(`Unknown workflow ${key}. Registered workflows: ${alternatives}`);\n }\n return definition;\n }\n const validateStored = (\n binding: WorkflowBinding,\n value: unknown,\n field: \"input\" | \"output\",\n ): JsonValue => {\n const definition = resolve(binding);\n const storedField = field === \"input\" ? \"storedInput\" : \"storedOutput\";\n const storedSchema = definition[storedField];\n const json = structuredClone(jsonValueSchema.parse(value));\n let parsed: unknown;\n try {\n parsed = (storedSchema ?? definition[field]).parse(structuredClone(json));\n } catch (cause) {\n if (storedSchema !== undefined) throw cause;\n throw new Error(\n `Workflow ${definition.id}@${definition.version} cannot validate stored ${field}; provide ${storedField} when the ${field} transform changes shape`,\n { cause },\n );\n }\n const validated = jsonValueSchema.parse(parsed);\n if (!isDeepStrictEqual(json, validated)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} stored ${field} validation must preserve canonical JSON; provide a non-transforming ${storedField} schema`,\n );\n }\n return structuredClone(validated);\n };\n const validate = (\n binding: WorkflowBinding,\n value: unknown,\n field: \"input\" | \"output\",\n ): JsonValue => {\n const definition = resolve(binding);\n const json = jsonValueSchema.parse(value);\n const parsed = jsonValueSchema.parse(definition[field].parse(structuredClone(json)));\n return validateStored(binding, parsed, field);\n };\n\n return Object.freeze({\n definitions: Object.freeze(registered),\n resolve,\n validateInput: (binding: WorkflowBinding, value: unknown) => validate(binding, value, \"input\"),\n validateOutput: (binding: WorkflowBinding, value: unknown) =>\n validate(binding, value, \"output\"),\n validateStoredInput: (binding: WorkflowBinding, value: unknown) =>\n validateStored(binding, value, \"input\"),\n validateStoredOutput: (binding: WorkflowBinding, value: unknown) =>\n validateStored(binding, value, \"output\"),\n });\n}\n"],"mappings":";;;;;;;AAuCA,MAAa,sBAAsB,eAAe;CAChD,IAAI;CACJ,SAAS;CACT,OAAO;CACP,QAAQ;AACV,CAAC;;AAuFD,SAAgB,eAMd,YAC0D;CAC1D,MAAM,gBAAgB,qBAAqB;EACzC,IAAI,WAAW;EACf,SAAS,WAAW;CACtB,CAAC;CACD,IAAI,EAAE,WAAW,iBAAiB,EAAE,YAAY,EAAE,WAAW,kBAAkB,EAAE,UAC/E,MAAM,IAAI,MACR,YAAY,WAAW,GAAG,GAAG,WAAW,QAAQ,uCAClD;CAEF,KAAK,MAAM,SAAS,CAAC,eAAe,cAAc,GAChD,IAAI,WAAW,WAAW,KAAA,KAAa,EAAE,WAAW,kBAAkB,EAAE,UACtE,MAAM,IAAI,MACR,YAAY,WAAW,GAAG,GAAG,WAAW,QAAQ,kBAAkB,MAAM,QAC1E;CAGJ,MAAM,UAKF,OAAO,OAAO,aAAa;CAM/B,OAAO,OAAO,OAAO;EACnB,IAAI,WAAW;EACf,SAAS,WAAW;EACpB;EACA,OAAO,WAAW;EAClB,QAAQ,WAAW;EACnB,aAAa,WAAW;EACxB,cAAc,WAAW;CAC3B,CAAC;AACH;;AAGA,SAAgB,gBACd,aACkD;CAClD,MAAM,aAAa,YAAY,KAAK,eAClC,eAAe,UAAU,CAC3B;CACA,MAAM,4BAAY,IAAI,IAAsD;CAC5E,KAAK,MAAM,cAAc,YAAY;EACnC,MAAM,MAAM,GAAG,WAAW,GAAG,GAAG,WAAW;EAC3C,IAAI,UAAU,IAAI,GAAG,GACnB,MAAM,IAAI,MACR,iCAAiC,IAAI,4CACvC;EAEF,UAAU,IAAI,KAAK,UAAU;CAC/B;CAKA,SAAS,QAAQ,SAAoE;EACnF,MAAM,EAAE,IAAI,YAAY,sBAAsB,MAAM,OAAO;EAC3D,MAAM,MAAM,GAAG,GAAG,GAAG;EACrB,MAAM,aAAa,UAAU,IAAI,GAAG;EACpC,IAAI,CAAC,YAAY;GACf,MAAM,eAAe,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK;GAChE,MAAM,IAAI,MAAM,oBAAoB,IAAI,0BAA0B,cAAc;EAClF;EACA,OAAO;CACT;CACA,MAAM,kBACJ,SACA,OACA,UACc;EACd,MAAM,aAAa,QAAQ,OAAO;EAClC,MAAM,cAAc,UAAU,UAAU,gBAAgB;EACxD,MAAM,eAAe,WAAW;EAChC,MAAM,OAAO,gBAAgB,gBAAgB,MAAM,KAAK,CAAC;EACzD,IAAI;EACJ,IAAI;GACF,UAAU,gBAAgB,WAAW,OAAA,CAAQ,MAAM,gBAAgB,IAAI,CAAC;EAC1E,SAAS,OAAO;GACd,IAAI,iBAAiB,KAAA,GAAW,MAAM;GACtC,MAAM,IAAI,MACR,YAAY,WAAW,GAAG,GAAG,WAAW,QAAQ,0BAA0B,MAAM,YAAY,YAAY,YAAY,MAAM,2BAC1H,EAAE,MAAM,CACV;EACF;EACA,MAAM,YAAY,gBAAgB,MAAM,MAAM;EAC9C,IAAI,CAAC,kBAAkB,MAAM,SAAS,GACpC,MAAM,IAAI,MACR,YAAY,WAAW,GAAG,GAAG,WAAW,QAAQ,UAAU,MAAM,uEAAuE,YAAY,QACrJ;EAEF,OAAO,gBAAgB,SAAS;CAClC;CACA,MAAM,YACJ,SACA,OACA,UACc;EACd,MAAM,aAAa,QAAQ,OAAO;EAClC,MAAM,OAAO,gBAAgB,MAAM,KAAK;EACxC,MAAM,SAAS,gBAAgB,MAAM,WAAW,MAAM,CAAC,MAAM,gBAAgB,IAAI,CAAC,CAAC;EACnF,OAAO,eAAe,SAAS,QAAQ,KAAK;CAC9C;CAEA,OAAO,OAAO,OAAO;EACnB,aAAa,OAAO,OAAO,UAAU;EACrC;EACA,gBAAgB,SAA0B,UAAmB,SAAS,SAAS,OAAO,OAAO;EAC7F,iBAAiB,SAA0B,UACzC,SAAS,SAAS,OAAO,QAAQ;EACnC,sBAAsB,SAA0B,UAC9C,eAAe,SAAS,OAAO,OAAO;EACxC,uBAAuB,SAA0B,UAC/C,eAAe,SAAS,OAAO,QAAQ;CAC3C,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"workflows.mjs","names":[],"sources":["../src/workflows.ts"],"sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { z } from \"zod\";\nimport {\n jsonValueSchema,\n parseWorkflowBinding,\n workflowBindingSchema,\n type JsonValue,\n type WorkflowBinding,\n type WorkflowReference,\n} from \"./schema/work\";\n\nexport {\n jsonValueSchema,\n MAX_WORK_JSON_BYTES,\n MAX_WORK_JSON_DEPTH,\n taskWork,\n taskWorkResultSchema,\n taskWorkSchema,\n parseWorkflowBinding,\n workflowBindingSchema,\n} from \"./schema/work\";\nexport {\n agentRouteBindingSchema,\n agentRouteSchema,\n defineAgentRoutes,\n parseAgentRouteBinding,\n routeKey,\n routeTaskWork,\n type AgentRoute,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n type AgentRouteInput,\n type AgentRouteKey,\n type AgentRouteRegistry,\n type RouteTaskWorkOptions,\n type RoutedTaskWork,\n} from \"./agent-routes\";\n\n/** Generic `task@1` workflow accepting bounded JSON input and output. */\nexport const defaultTaskWorkflow = defineWorkflow({\n id: \"task\",\n version: 1,\n input: jsonValueSchema,\n output: jsonValueSchema,\n});\nexport type {\n JsonValue,\n TaskWork,\n TaskWorkOptions,\n TaskWorkResult,\n WorkflowBinding,\n WorkflowBindingInput,\n WorkflowReference,\n WorkflowReferenceInput,\n WorkflowTaskWorkOptions,\n} from \"./schema/work\";\n\n/** Caller input accepted when defining one exact workflow contract. */\nexport interface WorkflowDefinitionInput<\n Input extends z.ZodType = z.ZodType,\n Output extends z.ZodType = z.ZodType,\n Id extends string = string,\n Version extends number = number,\n> {\n /** Stable lowercase workflow identifier persisted on every Task. */\n readonly id: Id;\n /** Positive exact version; callers and recovery never select an implicit latest version. */\n readonly version: Version;\n /** Schema that validates caller input and supplies its static input type. */\n readonly input: Input;\n /** Schema that validates successful completion and supplies its static output type. */\n readonly output: Output;\n /**\n * Canonical persisted-input schema required when `input` transforms its value. It must validate\n * without changing the stored JSON shape.\n */\n readonly storedInput?: z.ZodType<NoInfer<z.output<Input>>>;\n /**\n * Canonical persisted-output schema required when `output` transforms its value. It must\n * validate without changing the stored JSON shape.\n */\n readonly storedOutput?: z.ZodType<NoInfer<z.output<Output>>>;\n}\n\n/** Exact-version workflow contract with a typed binding for constructing work. */\nexport interface WorkflowDefinition<\n Input extends z.ZodType = z.ZodType,\n Output extends z.ZodType = z.ZodType,\n Id extends string = string,\n Version extends number = number,\n>\n extends\n WorkflowDefinitionInput<Input, Output, Id, Version>,\n WorkflowReference<\n Input,\n WorkflowBinding<z.input<Input> & JsonValue, z.input<Output> & JsonValue, Id, Version>\n > {}\n\n/** JSON input accepted by a workflow definition before its schema transforms it. */\nexport type WorkflowInput<Definition extends WorkflowDefinition> = z.input<Definition[\"input\"]> &\n JsonValue;\n\n/** JSON output accepted by a workflow definition before its schema transforms it. */\nexport type WorkflowOutput<Definition extends WorkflowDefinition> = z.input<Definition[\"output\"]> &\n JsonValue;\n\ntype RegisteredWorkflow<Definition extends WorkflowDefinitionInput> =\n Definition extends WorkflowDefinitionInput<infer Input, infer Output, infer Id, infer Version>\n ? WorkflowDefinition<Input, Output, Id, Version>\n : never;\n\ntype WorkflowForBinding<\n Definition extends WorkflowDefinition,\n Binding extends WorkflowBinding,\n> = Definition extends WorkflowDefinition\n ? Binding extends {\n readonly id: Definition[\"id\"];\n readonly version: Definition[\"version\"];\n }\n ? Definition\n : Definition extends {\n readonly id: Binding[\"id\"];\n readonly version: Binding[\"version\"];\n }\n ? Definition\n : never\n : never;\n\n/** Immutable registry that validates data against exact workflow versions. */\nexport interface WorkflowRegistry<Definition extends WorkflowDefinition = WorkflowDefinition> {\n /** Immutable exact-version definitions in registration order. */\n readonly definitions: readonly Readonly<Definition>[];\n /**\n * Resolves one exact binding, throwing with the registered alternatives when it is unknown.\n *\n * @param binding - Exact workflow ID and version.\n * @returns The matching immutable workflow definition.\n */\n resolve<const Binding extends WorkflowBinding>(\n binding: Binding,\n ): Readonly<WorkflowForBinding<Definition, Binding>>;\n /**\n * Validates caller input through the selected workflow and returns bounded canonical JSON.\n *\n * @param binding - Exact workflow ID and version.\n * @param value - Untrusted caller input to validate and transform.\n * @returns Validated bounded JSON produced by the input schema.\n */\n validateInput(binding: WorkflowBinding, value: unknown): JsonValue;\n /**\n * Validates candidate completion output through the selected workflow.\n *\n * @param binding - Exact workflow ID and version.\n * @param value - Untrusted candidate output to validate and transform.\n * @returns Validated bounded JSON produced by the output schema.\n */\n validateOutput(binding: WorkflowBinding, value: unknown): JsonValue;\n /**\n * Validates persisted input without permitting a schema transform to change its JSON shape.\n *\n * @param binding - Exact workflow ID and version stored on the Task.\n * @param value - Canonical persisted JSON to validate.\n * @returns A cloned validated value equal to the persisted JSON.\n */\n validateStoredInput(binding: WorkflowBinding, value: unknown): JsonValue;\n /**\n * Validates persisted output without permitting a schema transform to change its JSON shape.\n *\n * @param binding - Exact workflow ID and version stored on the Task.\n * @param value - Canonical persisted JSON to validate.\n * @returns A cloned validated value equal to the persisted JSON.\n */\n validateStoredOutput(binding: WorkflowBinding, value: unknown): JsonValue;\n}\n\n/**\n * Validates and freezes one exact workflow definition without executing it.\n *\n * @remarks\n * A definition is a typed contract, not a registered runtime. Pass every definition that stored\n * Tasks may reference to `defineWorkflows`, then supply that registry to `createStores`. Input and\n * output schemas may transform caller values only when `storedInput` or `storedOutput` describes\n * the unchanged canonical JSON representation used after persistence.\n *\n * @param definition - Stable ID, positive version, and Zod input/output contracts.\n * @returns An immutable definition whose binding carries the input and output types.\n * @throws When the binding is invalid or the input/output fields are not Zod schemas.\n * @see The shipped `docs/recipes/custom-workflow.md` recipe for registration and completion.\n *\n * @example\n * ```ts\n * import { defineWorkflow, defineWorkflows } from \"@vercel/factory/workflows\";\n * import { z } from \"zod\";\n *\n * const greeting = defineWorkflow({\n * id: \"greeting\",\n * version: 1,\n * input: z.object({ name: z.string() }),\n * output: z.object({ message: z.string() }),\n * });\n * const workflows = defineWorkflows([greeting]);\n * ```\n */\nexport function defineWorkflow<\n Input extends z.ZodType,\n Output extends z.ZodType,\n const Id extends string,\n const Version extends number,\n>(\n definition: WorkflowDefinitionInput<Input, Output, Id, Version>,\n): Readonly<WorkflowDefinition<Input, Output, Id, Version>> {\n const parsedBinding = parseWorkflowBinding({\n id: definition.id,\n version: definition.version,\n });\n if (!(definition.input instanceof z.ZodType) || !(definition.output instanceof z.ZodType)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} requires Zod input and output schemas`,\n );\n }\n for (const field of [\"storedInput\", \"storedOutput\"] as const) {\n if (definition[field] !== undefined && !(definition[field] instanceof z.ZodType)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} requires a Zod ${field} schema`,\n );\n }\n }\n const binding: WorkflowBinding<\n z.input<Input> & JsonValue,\n z.input<Output> & JsonValue,\n Id,\n Version\n > = Object.freeze(parsedBinding) as WorkflowBinding<\n z.input<Input> & JsonValue,\n z.input<Output> & JsonValue,\n Id,\n Version\n >;\n return Object.freeze({\n id: definition.id,\n version: definition.version,\n binding,\n input: definition.input,\n output: definition.output,\n storedInput: definition.storedInput,\n storedOutput: definition.storedOutput,\n });\n}\n\n/**\n * Builds an immutable exact-version workflow registry and rejects duplicate definitions.\n *\n * @remarks\n * Register old versions for as long as persisted Tasks can still reference them. Resolution uses\n * both ID and version and never falls forward to another version. Creating a new registry has no\n * persistence side effects.\n *\n * @param definitions - All exact workflow versions readable by this runtime.\n * @returns A registry for resolving bindings and validating caller and persisted values.\n * @throws When a definition is invalid or the same `id@version` appears more than once.\n *\n * @example\n * ```ts\n * import { defineWorkflow, defineWorkflows } from \"@vercel/factory/workflows\";\n * import { z } from \"zod\";\n *\n * const v1 = defineWorkflow({\n * id: \"report\",\n * version: 1,\n * input: z.object({ topic: z.string() }),\n * output: z.object({ summary: z.string() }),\n * });\n * const workflows = defineWorkflows([v1]);\n * workflows.resolve(v1.binding);\n * ```\n */\nexport function defineWorkflows<const Definition extends WorkflowDefinitionInput>(\n definitions: readonly Definition[],\n): WorkflowRegistry<RegisteredWorkflow<Definition>> {\n const registered = definitions.map((definition) =>\n defineWorkflow(definition),\n ) as unknown as readonly Readonly<RegisteredWorkflow<Definition>>[];\n const byBinding = new Map<string, Readonly<RegisteredWorkflow<Definition>>>();\n for (const definition of registered) {\n const key = `${definition.id}@${definition.version}`;\n if (byBinding.has(key)) {\n throw new Error(\n `Duplicate workflow definition ${key}; register each id and version exactly once`,\n );\n }\n byBinding.set(key, definition);\n }\n\n function resolve<const Binding extends WorkflowBinding>(\n binding: Binding,\n ): Readonly<WorkflowForBinding<RegisteredWorkflow<Definition>, Binding>>;\n function resolve(binding: WorkflowBinding): Readonly<RegisteredWorkflow<Definition>> {\n const { id, version } = workflowBindingSchema.parse(binding);\n const key = `${id}@${version}`;\n const definition = byBinding.get(key);\n if (!definition) {\n const alternatives = [...byBinding.keys()].sort().join(\", \") || \"none\";\n throw new Error(`Unknown workflow ${key}. Registered workflows: ${alternatives}`);\n }\n return definition;\n }\n const validateStored = (\n binding: WorkflowBinding,\n value: unknown,\n field: \"input\" | \"output\",\n ): JsonValue => {\n const definition = resolve(binding);\n const storedField = field === \"input\" ? \"storedInput\" : \"storedOutput\";\n const storedSchema = definition[storedField];\n const json = structuredClone(jsonValueSchema.parse(value));\n let parsed: unknown;\n try {\n parsed = (storedSchema ?? definition[field]).parse(structuredClone(json));\n } catch (cause) {\n if (storedSchema !== undefined) throw cause;\n throw new Error(\n `Workflow ${definition.id}@${definition.version} cannot validate stored ${field}; provide ${storedField} when the ${field} transform changes shape`,\n { cause },\n );\n }\n const validated = jsonValueSchema.parse(parsed);\n if (!isDeepStrictEqual(json, validated)) {\n throw new Error(\n `Workflow ${definition.id}@${definition.version} stored ${field} validation must preserve canonical JSON; provide a non-transforming ${storedField} schema`,\n );\n }\n return structuredClone(validated);\n };\n const validate = (\n binding: WorkflowBinding,\n value: unknown,\n field: \"input\" | \"output\",\n ): JsonValue => {\n const definition = resolve(binding);\n const json = jsonValueSchema.parse(value);\n const parsed = jsonValueSchema.parse(definition[field].parse(structuredClone(json)));\n return validateStored(binding, parsed, field);\n };\n\n return Object.freeze({\n definitions: Object.freeze(registered),\n resolve,\n validateInput: (binding: WorkflowBinding, value: unknown) => validate(binding, value, \"input\"),\n validateOutput: (binding: WorkflowBinding, value: unknown) =>\n validate(binding, value, \"output\"),\n validateStoredInput: (binding: WorkflowBinding, value: unknown) =>\n validateStored(binding, value, \"input\"),\n validateStoredOutput: (binding: WorkflowBinding, value: unknown) =>\n validateStored(binding, value, \"output\"),\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/workspace-files-git.d.ts
|
|
2
|
+
/** Configurable bounds for the supplied committed-Git file reader; custom adapters own their limits. */
|
|
3
|
+
interface WorkspaceGitFilesOptions {
|
|
4
|
+
readonly maxFileBytes?: number;
|
|
5
|
+
readonly maxDirectoryEntries?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Reads committed directories or files using Node.js and Git, without changing the checkout.
|
|
9
|
+
* Pass a JSON file containing root, path, optional revision, maxFileBytes, and maxDirectoryEntries.
|
|
10
|
+
* The caller adds its authorized executionId and validates the resulting WorkspaceFiles response.
|
|
11
|
+
*/
|
|
12
|
+
declare const workspaceGitFilesScript: string;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { WorkspaceGitFilesOptions, workspaceGitFilesScript };
|
|
15
|
+
//# sourceMappingURL=workspace-files-git.d.mts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region src/workspace-files-git.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reads committed directories or files using Node.js and Git, without changing the checkout.
|
|
4
|
+
* Pass a JSON file containing root, path, optional revision, maxFileBytes, and maxDirectoryEntries.
|
|
5
|
+
* The caller adds its authorized executionId and validates the resulting WorkspaceFiles response.
|
|
6
|
+
*/
|
|
7
|
+
const workspaceGitFilesScript = String.raw`
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const { execFileSync } = require('node:child_process');
|
|
10
|
+
const input = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
|
|
11
|
+
const root = fs.realpathSync(input.root);
|
|
12
|
+
const maxBytes = input.maxFileBytes ?? 2000000;
|
|
13
|
+
const maxEntries = input.maxDirectoryEntries ?? 2000;
|
|
14
|
+
const git = (args, encoding = 'utf8') => execFileSync('git', ['--no-optional-locks', '--literal-pathspecs', '-c', 'core.fsmonitor=false', ...args], { cwd: root, encoding, timeout: 10000, maxBuffer: Math.max(16000000, maxBytes + 1024), env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' } });
|
|
15
|
+
const safe = (p) => p === '' || (!p.includes('\\') && !p.includes('\0') && p.split('/').every(v => v && v !== '.' && v !== '..' && v.toLowerCase() !== '.git'));
|
|
16
|
+
const unavailable = (reason) => ({ state: 'unavailable', path: input.path, reason });
|
|
17
|
+
const entryType = (mode, type) => mode === '120000' ? 'symlink' : type === 'tree' ? 'directory' : type === 'commit' ? 'submodule' : 'file';
|
|
18
|
+
function entries(ref, selected) {
|
|
19
|
+
return git(['ls-tree', '-z', '-l', ref, ...(selected ? ['--', selected] : [])]).split('\0').filter(Boolean).map(line => {
|
|
20
|
+
const m = /^(\d+) (\w+) ([a-f0-9]+)\s+(-|\d+)\t([\s\S]+)$/u.exec(line);
|
|
21
|
+
if (!m) throw new Error('Invalid tree entry');
|
|
22
|
+
return { path: m[5], type: entryType(m[1], m[2]), sha: m[3], size: m[4] === '-' ? null : Number(m[4]) };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function inspect() {
|
|
26
|
+
if (!safe(input.path) || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || !Number.isSafeInteger(maxEntries) || maxEntries < 1) return unavailable('Invalid file inspection options.');
|
|
27
|
+
if (fs.realpathSync(git(['rev-parse', '--show-toplevel']).trim()) !== root) return unavailable('The workspace checkout is unavailable.');
|
|
28
|
+
const head = git(['rev-parse', '--verify', 'HEAD']).trim();
|
|
29
|
+
const revision = input.revision ?? head;
|
|
30
|
+
if (!/^[a-f0-9]{40,64}$/u.test(revision)) return unavailable('Select an immutable commit from this workspace.');
|
|
31
|
+
try { git(['merge-base', '--is-ancestor', revision, head]); }
|
|
32
|
+
catch { return unavailable('This revision is no longer part of the workspace history. Refresh files.'); }
|
|
33
|
+
const entry = input.path ? entries(revision, input.path).find(e => e.path === input.path) : null;
|
|
34
|
+
if (input.path && !entry) return unavailable('This path does not exist at the selected revision.');
|
|
35
|
+
if (!entry || entry.type === 'directory') {
|
|
36
|
+
const all = entries(entry ? entry.sha : revision);
|
|
37
|
+
const children = all.slice(0, maxEntries).map(e => ({ name: e.path, path: input.path ? input.path + '/' + e.path : e.path, type: e.type, size: e.size }));
|
|
38
|
+
if (children.some(e => !safe(e.path))) return unavailable('The directory contains an unsupported path.');
|
|
39
|
+
return { state: 'directory', path: input.path, revision, entries: children, truncated: all.length > maxEntries };
|
|
40
|
+
}
|
|
41
|
+
const metadata = { name: input.path.split('/').at(-1), path: input.path, type: entry.type, size: entry.size };
|
|
42
|
+
const result = content => ({ state: 'file', path: input.path, revision, entry: metadata, content });
|
|
43
|
+
if (entry.type === 'submodule') return result({ kind: 'unavailable', reason: 'Submodule contents belong to a separate repository.' });
|
|
44
|
+
if (entry.size === null || entry.size > maxBytes) return result({ kind: 'unavailable', reason: 'This file exceeds the configured ' + maxBytes.toLocaleString('en-US') + '-byte preview limit.' });
|
|
45
|
+
const bytes = git(['cat-file', 'blob', entry.sha], null);
|
|
46
|
+
if (bytes.length > maxBytes) return result({ kind: 'unavailable', reason: 'This file exceeds the configured preview limit.' });
|
|
47
|
+
const ext = input.path.split('.').at(-1).toLowerCase();
|
|
48
|
+
const mediaType = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', avif: 'image/avif', svg: 'image/svg+xml' }[ext];
|
|
49
|
+
if (mediaType && entry.type === 'file') return result({ kind: 'image', mediaType, base64: bytes.toString('base64') });
|
|
50
|
+
try {
|
|
51
|
+
if (bytes.includes(0)) throw new Error('Binary');
|
|
52
|
+
return result({ kind: 'text', text: new TextDecoder('utf-8', { fatal: true }).decode(bytes) });
|
|
53
|
+
} catch { return result({ kind: 'unavailable', reason: 'This binary format cannot be previewed.' }); }
|
|
54
|
+
}
|
|
55
|
+
try { process.stdout.write(JSON.stringify(inspect())); }
|
|
56
|
+
catch { process.stdout.write(JSON.stringify(unavailable('The committed file could not be read. Refresh files after the current Git operation finishes.'))); }
|
|
57
|
+
`;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { workspaceGitFilesScript };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=workspace-files-git.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-files-git.mjs","names":[],"sources":["../src/workspace-files-git.ts"],"sourcesContent":["/** Configurable bounds for the supplied committed-Git file reader; custom adapters own their limits. */\nexport interface WorkspaceGitFilesOptions {\n readonly maxFileBytes?: number;\n readonly maxDirectoryEntries?: number;\n}\n\n/**\n * Reads committed directories or files using Node.js and Git, without changing the checkout.\n * Pass a JSON file containing root, path, optional revision, maxFileBytes, and maxDirectoryEntries.\n * The caller adds its authorized executionId and validates the resulting WorkspaceFiles response.\n */\nexport const workspaceGitFilesScript = String.raw`\nconst fs = require('node:fs');\nconst { execFileSync } = require('node:child_process');\nconst input = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));\nconst root = fs.realpathSync(input.root);\nconst maxBytes = input.maxFileBytes ?? 2000000;\nconst maxEntries = input.maxDirectoryEntries ?? 2000;\nconst git = (args, encoding = 'utf8') => execFileSync('git', ['--no-optional-locks', '--literal-pathspecs', '-c', 'core.fsmonitor=false', ...args], { cwd: root, encoding, timeout: 10000, maxBuffer: Math.max(16000000, maxBytes + 1024), env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' } });\nconst safe = (p) => p === '' || (!p.includes('\\\\') && !p.includes('\\0') && p.split('/').every(v => v && v !== '.' && v !== '..' && v.toLowerCase() !== '.git'));\nconst unavailable = (reason) => ({ state: 'unavailable', path: input.path, reason });\nconst entryType = (mode, type) => mode === '120000' ? 'symlink' : type === 'tree' ? 'directory' : type === 'commit' ? 'submodule' : 'file';\nfunction entries(ref, selected) {\n return git(['ls-tree', '-z', '-l', ref, ...(selected ? ['--', selected] : [])]).split('\\0').filter(Boolean).map(line => {\n const m = /^(\\d+) (\\w+) ([a-f0-9]+)\\s+(-|\\d+)\\t([\\s\\S]+)$/u.exec(line);\n if (!m) throw new Error('Invalid tree entry');\n return { path: m[5], type: entryType(m[1], m[2]), sha: m[3], size: m[4] === '-' ? null : Number(m[4]) };\n });\n}\nfunction inspect() {\n if (!safe(input.path) || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || !Number.isSafeInteger(maxEntries) || maxEntries < 1) return unavailable('Invalid file inspection options.');\n if (fs.realpathSync(git(['rev-parse', '--show-toplevel']).trim()) !== root) return unavailable('The workspace checkout is unavailable.');\n const head = git(['rev-parse', '--verify', 'HEAD']).trim();\n const revision = input.revision ?? head;\n if (!/^[a-f0-9]{40,64}$/u.test(revision)) return unavailable('Select an immutable commit from this workspace.');\n try { git(['merge-base', '--is-ancestor', revision, head]); }\n catch { return unavailable('This revision is no longer part of the workspace history. Refresh files.'); }\n const entry = input.path ? entries(revision, input.path).find(e => e.path === input.path) : null;\n if (input.path && !entry) return unavailable('This path does not exist at the selected revision.');\n if (!entry || entry.type === 'directory') {\n const all = entries(entry ? entry.sha : revision);\n const children = all.slice(0, maxEntries).map(e => ({ name: e.path, path: input.path ? input.path + '/' + e.path : e.path, type: e.type, size: e.size }));\n if (children.some(e => !safe(e.path))) return unavailable('The directory contains an unsupported path.');\n return { state: 'directory', path: input.path, revision, entries: children, truncated: all.length > maxEntries };\n }\n const metadata = { name: input.path.split('/').at(-1), path: input.path, type: entry.type, size: entry.size };\n const result = content => ({ state: 'file', path: input.path, revision, entry: metadata, content });\n if (entry.type === 'submodule') return result({ kind: 'unavailable', reason: 'Submodule contents belong to a separate repository.' });\n if (entry.size === null || entry.size > maxBytes) return result({ kind: 'unavailable', reason: 'This file exceeds the configured ' + maxBytes.toLocaleString('en-US') + '-byte preview limit.' });\n const bytes = git(['cat-file', 'blob', entry.sha], null);\n if (bytes.length > maxBytes) return result({ kind: 'unavailable', reason: 'This file exceeds the configured preview limit.' });\n const ext = input.path.split('.').at(-1).toLowerCase();\n const mediaType = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', avif: 'image/avif', svg: 'image/svg+xml' }[ext];\n if (mediaType && entry.type === 'file') return result({ kind: 'image', mediaType, base64: bytes.toString('base64') });\n try {\n if (bytes.includes(0)) throw new Error('Binary');\n return result({ kind: 'text', text: new TextDecoder('utf-8', { fatal: true }).decode(bytes) });\n } catch { return result({ kind: 'unavailable', reason: 'This binary format cannot be previewed.' }); }\n}\ntry { process.stdout.write(JSON.stringify(inspect())); }\ncatch { process.stdout.write(JSON.stringify(unavailable('The committed file could not be read. Refresh files after the current Git operation finishes.'))); }\n`;\n"],"mappings":";;;;;;AAWA,MAAa,0BAA0B,OAAO,GAAG"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/workspace-files.d.ts
|
|
3
|
+
/** Validates a workspace-relative path, with the empty string identifying the root. */
|
|
4
|
+
declare const workspacePathSchema: z.ZodString;
|
|
5
|
+
/** Validates a directory or file inspection at an optional provider-owned immutable revision. */
|
|
6
|
+
declare const workspaceFilesRequestSchema: z.ZodObject<{
|
|
7
|
+
path: z.ZodDefault<z.ZodString>;
|
|
8
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strict>;
|
|
10
|
+
/** A directory or file inspection at an optional provider-owned immutable revision. */
|
|
11
|
+
type WorkspaceFilesRequest = Readonly<z.input<typeof workspaceFilesRequestSchema>>;
|
|
12
|
+
/** Validates an immediate child of a workspace directory. */
|
|
13
|
+
declare const workspaceFileEntrySchema: z.ZodObject<{
|
|
14
|
+
name: z.ZodString;
|
|
15
|
+
path: z.ZodString;
|
|
16
|
+
type: z.ZodEnum<{
|
|
17
|
+
file: "file";
|
|
18
|
+
directory: "directory";
|
|
19
|
+
symlink: "symlink";
|
|
20
|
+
submodule: "submodule";
|
|
21
|
+
}>;
|
|
22
|
+
size: z.ZodNullable<z.ZodNumber>;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
/** An immediate child of a workspace directory. */
|
|
25
|
+
type WorkspaceFileEntry = Readonly<z.infer<typeof workspaceFileEntrySchema>>;
|
|
26
|
+
/** Validates text, an image, or an explicit explanation for an undisplayable file. */
|
|
27
|
+
declare const workspaceFileContentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
28
|
+
kind: z.ZodLiteral<"text">;
|
|
29
|
+
text: z.ZodString;
|
|
30
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
31
|
+
kind: z.ZodLiteral<"image">;
|
|
32
|
+
mediaType: z.ZodEnum<{
|
|
33
|
+
"image/png": "image/png";
|
|
34
|
+
"image/jpeg": "image/jpeg";
|
|
35
|
+
"image/webp": "image/webp";
|
|
36
|
+
"image/gif": "image/gif";
|
|
37
|
+
"image/avif": "image/avif";
|
|
38
|
+
"image/svg+xml": "image/svg+xml";
|
|
39
|
+
}>;
|
|
40
|
+
base64: z.ZodString;
|
|
41
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
42
|
+
kind: z.ZodLiteral<"unavailable">;
|
|
43
|
+
reason: z.ZodString;
|
|
44
|
+
}, z.core.$strict>], "kind">;
|
|
45
|
+
/** Validates a pinned directory listing, a complete file, or an unavailable workspace response. */
|
|
46
|
+
declare const workspaceFilesSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
47
|
+
state: z.ZodLiteral<"directory">;
|
|
48
|
+
revision: z.ZodString;
|
|
49
|
+
entries: z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
50
|
+
name: z.ZodString;
|
|
51
|
+
path: z.ZodString;
|
|
52
|
+
type: z.ZodEnum<{
|
|
53
|
+
file: "file";
|
|
54
|
+
directory: "directory";
|
|
55
|
+
symlink: "symlink";
|
|
56
|
+
submodule: "submodule";
|
|
57
|
+
}>;
|
|
58
|
+
size: z.ZodNullable<z.ZodNumber>;
|
|
59
|
+
}, z.core.$strict>>>;
|
|
60
|
+
truncated: z.ZodBoolean;
|
|
61
|
+
executionId: z.ZodNullable<z.ZodString>;
|
|
62
|
+
path: z.ZodString;
|
|
63
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
64
|
+
state: z.ZodLiteral<"file">;
|
|
65
|
+
revision: z.ZodString;
|
|
66
|
+
entry: z.ZodObject<{
|
|
67
|
+
name: z.ZodString;
|
|
68
|
+
path: z.ZodString;
|
|
69
|
+
type: z.ZodEnum<{
|
|
70
|
+
file: "file";
|
|
71
|
+
directory: "directory";
|
|
72
|
+
symlink: "symlink";
|
|
73
|
+
submodule: "submodule";
|
|
74
|
+
}>;
|
|
75
|
+
size: z.ZodNullable<z.ZodNumber>;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
content: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
78
|
+
kind: z.ZodLiteral<"text">;
|
|
79
|
+
text: z.ZodString;
|
|
80
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
81
|
+
kind: z.ZodLiteral<"image">;
|
|
82
|
+
mediaType: z.ZodEnum<{
|
|
83
|
+
"image/png": "image/png";
|
|
84
|
+
"image/jpeg": "image/jpeg";
|
|
85
|
+
"image/webp": "image/webp";
|
|
86
|
+
"image/gif": "image/gif";
|
|
87
|
+
"image/avif": "image/avif";
|
|
88
|
+
"image/svg+xml": "image/svg+xml";
|
|
89
|
+
}>;
|
|
90
|
+
base64: z.ZodString;
|
|
91
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
92
|
+
kind: z.ZodLiteral<"unavailable">;
|
|
93
|
+
reason: z.ZodString;
|
|
94
|
+
}, z.core.$strict>], "kind">;
|
|
95
|
+
executionId: z.ZodNullable<z.ZodString>;
|
|
96
|
+
path: z.ZodString;
|
|
97
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
98
|
+
state: z.ZodLiteral<"unavailable">;
|
|
99
|
+
reason: z.ZodString;
|
|
100
|
+
executionId: z.ZodNullable<z.ZodString>;
|
|
101
|
+
path: z.ZodString;
|
|
102
|
+
}, z.core.$strict>], "state">;
|
|
103
|
+
/** A pinned directory listing, complete file, or unavailable workspace response. */
|
|
104
|
+
type WorkspaceFiles = Readonly<z.infer<typeof workspaceFilesSchema>>;
|
|
105
|
+
//#endregion
|
|
106
|
+
export { WorkspaceFileEntry, WorkspaceFiles, WorkspaceFilesRequest, workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspacePathSchema };
|
|
107
|
+
//# sourceMappingURL=workspace-files.d.mts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/workspace-files.ts
|
|
3
|
+
/** Validates a workspace-relative path, with the empty string identifying the root. */
|
|
4
|
+
const workspacePathSchema = z.string().refine((path) => path === "" || !path.includes("\\") && !path.includes("\0") && path.split("/").every((part) => part !== "" && part !== "." && part !== ".." && part.toLowerCase() !== ".git"), "Use a workspace-relative path without traversal or Git metadata");
|
|
5
|
+
/** Validates a directory or file inspection at an optional provider-owned immutable revision. */
|
|
6
|
+
const workspaceFilesRequestSchema = z.strictObject({
|
|
7
|
+
path: workspacePathSchema.default("").describe("Workspace-relative file or directory; empty for root."),
|
|
8
|
+
revision: z.string().min(1).optional().describe("Opaque immutable revision; absent selects the current revision.")
|
|
9
|
+
});
|
|
10
|
+
/** Validates an immediate child of a workspace directory. */
|
|
11
|
+
const workspaceFileEntrySchema = z.strictObject({
|
|
12
|
+
name: z.string().min(1).describe("Display name within its parent directory."),
|
|
13
|
+
path: workspacePathSchema.describe("Workspace-relative entry path."),
|
|
14
|
+
type: z.enum([
|
|
15
|
+
"file",
|
|
16
|
+
"directory",
|
|
17
|
+
"symlink",
|
|
18
|
+
"submodule"
|
|
19
|
+
]).describe("Entry kind; links are never followed."),
|
|
20
|
+
size: z.number().int().nonnegative().nullable().describe("Byte length when known; null for directories or unknown sizes.")
|
|
21
|
+
});
|
|
22
|
+
/** Validates text, an image, or an explicit explanation for an undisplayable file. */
|
|
23
|
+
const workspaceFileContentSchema = z.discriminatedUnion("kind", [
|
|
24
|
+
z.strictObject({
|
|
25
|
+
kind: z.literal("text").describe("Complete UTF-8 text, including an empty file."),
|
|
26
|
+
text: z.string().describe("Complete file contents; never a silently truncated prefix.")
|
|
27
|
+
}),
|
|
28
|
+
z.strictObject({
|
|
29
|
+
kind: z.literal("image").describe("Image bytes rendered in an image element, never as HTML."),
|
|
30
|
+
mediaType: z.enum([
|
|
31
|
+
"image/png",
|
|
32
|
+
"image/jpeg",
|
|
33
|
+
"image/gif",
|
|
34
|
+
"image/webp",
|
|
35
|
+
"image/avif",
|
|
36
|
+
"image/svg+xml"
|
|
37
|
+
]).describe("Image media type."),
|
|
38
|
+
base64: z.string().regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u).describe("Base64-encoded image bytes.")
|
|
39
|
+
}),
|
|
40
|
+
z.strictObject({
|
|
41
|
+
kind: z.literal("unavailable").describe("File exists but its content cannot be displayed."),
|
|
42
|
+
reason: z.string().min(1).describe("Why content is unavailable, such as a size limit or binary format.")
|
|
43
|
+
})
|
|
44
|
+
]);
|
|
45
|
+
const common = {
|
|
46
|
+
executionId: z.string().min(1).nullable().describe("Authorized execution whose workspace is being inspected."),
|
|
47
|
+
path: workspacePathSchema.describe("Requested workspace-relative path.")
|
|
48
|
+
};
|
|
49
|
+
/** Validates a pinned directory listing, a complete file, or an unavailable workspace response. */
|
|
50
|
+
const workspaceFilesSchema = z.discriminatedUnion("state", [
|
|
51
|
+
z.strictObject({
|
|
52
|
+
...common,
|
|
53
|
+
state: z.literal("directory").describe("A directory listing at the returned revision."),
|
|
54
|
+
revision: z.string().min(1).describe("Immutable provider-owned revision for subsequent reads."),
|
|
55
|
+
entries: z.array(workspaceFileEntrySchema).readonly().describe("Immediate directory children."),
|
|
56
|
+
truncated: z.boolean().describe("The provider's configured directory limit omitted entries.")
|
|
57
|
+
}),
|
|
58
|
+
z.strictObject({
|
|
59
|
+
...common,
|
|
60
|
+
state: z.literal("file").describe("A file at the returned revision."),
|
|
61
|
+
revision: z.string().min(1).describe("Immutable provider-owned revision for subsequent reads."),
|
|
62
|
+
entry: workspaceFileEntrySchema.describe("Metadata for the requested file."),
|
|
63
|
+
content: workspaceFileContentSchema.describe("Complete contents or an explicit unavailability reason.")
|
|
64
|
+
}),
|
|
65
|
+
z.strictObject({
|
|
66
|
+
...common,
|
|
67
|
+
state: z.literal("unavailable").describe("The requested workspace, revision, or path cannot be read."),
|
|
68
|
+
reason: z.string().min(1).describe("Why the requested inspection is unavailable.")
|
|
69
|
+
})
|
|
70
|
+
]);
|
|
71
|
+
//#endregion
|
|
72
|
+
export { workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspacePathSchema };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=workspace-files.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-files.mjs","names":[],"sources":["../src/workspace-files.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/** Validates a workspace-relative path, with the empty string identifying the root. */\nexport const workspacePathSchema = z\n .string()\n .refine(\n (path) =>\n path === \"\" ||\n (!path.includes(\"\\\\\") &&\n !path.includes(\"\\0\") &&\n path\n .split(\"/\")\n .every(\n (part) => part !== \"\" && part !== \".\" && part !== \"..\" && part.toLowerCase() !== \".git\",\n )),\n \"Use a workspace-relative path without traversal or Git metadata\",\n );\n\n/** Validates a directory or file inspection at an optional provider-owned immutable revision. */\nexport const workspaceFilesRequestSchema = z.strictObject({\n path: workspacePathSchema\n .default(\"\")\n .describe(\"Workspace-relative file or directory; empty for root.\"),\n revision: z\n .string()\n .min(1)\n .optional()\n .describe(\"Opaque immutable revision; absent selects the current revision.\"),\n});\n\n/** A directory or file inspection at an optional provider-owned immutable revision. */\nexport type WorkspaceFilesRequest = Readonly<z.input<typeof workspaceFilesRequestSchema>>;\n\n/** Validates an immediate child of a workspace directory. */\nexport const workspaceFileEntrySchema = z.strictObject({\n name: z.string().min(1).describe(\"Display name within its parent directory.\"),\n path: workspacePathSchema.describe(\"Workspace-relative entry path.\"),\n type: z\n .enum([\"file\", \"directory\", \"symlink\", \"submodule\"])\n .describe(\"Entry kind; links are never followed.\"),\n size: z\n .number()\n .int()\n .nonnegative()\n .nullable()\n .describe(\"Byte length when known; null for directories or unknown sizes.\"),\n});\n\n/** An immediate child of a workspace directory. */\nexport type WorkspaceFileEntry = Readonly<z.infer<typeof workspaceFileEntrySchema>>;\n\n/** Validates text, an image, or an explicit explanation for an undisplayable file. */\nexport const workspaceFileContentSchema = z.discriminatedUnion(\"kind\", [\n z.strictObject({\n kind: z.literal(\"text\").describe(\"Complete UTF-8 text, including an empty file.\"),\n text: z.string().describe(\"Complete file contents; never a silently truncated prefix.\"),\n }),\n z.strictObject({\n kind: z.literal(\"image\").describe(\"Image bytes rendered in an image element, never as HTML.\"),\n mediaType: z\n .enum([\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\", \"image/avif\", \"image/svg+xml\"])\n .describe(\"Image media type.\"),\n base64: z\n .string()\n .regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u)\n .describe(\"Base64-encoded image bytes.\"),\n }),\n z.strictObject({\n kind: z.literal(\"unavailable\").describe(\"File exists but its content cannot be displayed.\"),\n reason: z\n .string()\n .min(1)\n .describe(\"Why content is unavailable, such as a size limit or binary format.\"),\n }),\n]);\n\nconst common = {\n executionId: z\n .string()\n .min(1)\n .nullable()\n .describe(\"Authorized execution whose workspace is being inspected.\"),\n path: workspacePathSchema.describe(\"Requested workspace-relative path.\"),\n};\n\n/** Validates a pinned directory listing, a complete file, or an unavailable workspace response. */\nexport const workspaceFilesSchema = z.discriminatedUnion(\"state\", [\n z.strictObject({\n ...common,\n state: z.literal(\"directory\").describe(\"A directory listing at the returned revision.\"),\n revision: z.string().min(1).describe(\"Immutable provider-owned revision for subsequent reads.\"),\n entries: z.array(workspaceFileEntrySchema).readonly().describe(\"Immediate directory children.\"),\n truncated: z.boolean().describe(\"The provider's configured directory limit omitted entries.\"),\n }),\n z.strictObject({\n ...common,\n state: z.literal(\"file\").describe(\"A file at the returned revision.\"),\n revision: z.string().min(1).describe(\"Immutable provider-owned revision for subsequent reads.\"),\n entry: workspaceFileEntrySchema.describe(\"Metadata for the requested file.\"),\n content: workspaceFileContentSchema.describe(\n \"Complete contents or an explicit unavailability reason.\",\n ),\n }),\n z.strictObject({\n ...common,\n state: z\n .literal(\"unavailable\")\n .describe(\"The requested workspace, revision, or path cannot be read.\"),\n reason: z.string().min(1).describe(\"Why the requested inspection is unavailable.\"),\n }),\n]);\n\n/** A pinned directory listing, complete file, or unavailable workspace response. */\nexport type WorkspaceFiles = Readonly<z.infer<typeof workspaceFilesSchema>>;\n"],"mappings":";;;AAGA,MAAa,sBAAsB,EAChC,OAAO,CAAC,CACR,QACE,SACC,SAAS,MACR,CAAC,KAAK,SAAS,IAAI,KAClB,CAAC,KAAK,SAAS,IAAI,KACnB,KACG,MAAM,GAAG,CAAC,CACV,OACE,SAAS,SAAS,MAAM,SAAS,OAAO,SAAS,QAAQ,KAAK,YAAY,MAAM,MACnF,GACN,iEACF;;AAGF,MAAa,8BAA8B,EAAE,aAAa;CACxD,MAAM,oBACH,QAAQ,EAAE,CAAC,CACX,SAAS,uDAAuD;CACnE,UAAU,EACP,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,iEAAiE;AAC/E,CAAC;;AAMD,MAAa,2BAA2B,EAAE,aAAa;CACrD,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,2CAA2C;CAC5E,MAAM,oBAAoB,SAAS,gCAAgC;CACnE,MAAM,EACH,KAAK;EAAC;EAAQ;EAAa;EAAW;CAAW,CAAC,CAAC,CACnD,SAAS,uCAAuC;CACnD,MAAM,EACH,OAAO,CAAC,CACR,IAAI,CAAC,CACL,YAAY,CAAC,CACb,SAAS,CAAC,CACV,SAAS,gEAAgE;AAC9E,CAAC;;AAMD,MAAa,6BAA6B,EAAE,mBAAmB,QAAQ;CACrE,EAAE,aAAa;EACb,MAAM,EAAE,QAAQ,MAAM,CAAC,CAAC,SAAS,+CAA+C;EAChF,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,4DAA4D;CACxF,CAAC;CACD,EAAE,aAAa;EACb,MAAM,EAAE,QAAQ,OAAO,CAAC,CAAC,SAAS,0DAA0D;EAC5F,WAAW,EACR,KAAK;GAAC;GAAa;GAAc;GAAa;GAAc;GAAc;EAAe,CAAC,CAAC,CAC3F,SAAS,mBAAmB;EAC/B,QAAQ,EACL,OAAO,CAAC,CACR,MAAM,mEAAmE,CAAC,CAC1E,SAAS,6BAA6B;CAC3C,CAAC;CACD,EAAE,aAAa;EACb,MAAM,EAAE,QAAQ,aAAa,CAAC,CAAC,SAAS,kDAAkD;EAC1F,QAAQ,EACL,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,oEAAoE;CAClF,CAAC;AACH,CAAC;AAED,MAAM,SAAS;CACb,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,0DAA0D;CACtE,MAAM,oBAAoB,SAAS,oCAAoC;AACzE;;AAGA,MAAa,uBAAuB,EAAE,mBAAmB,SAAS;CAChE,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,WAAW,CAAC,CAAC,SAAS,+CAA+C;EACtF,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,yDAAyD;EAC9F,SAAS,EAAE,MAAM,wBAAwB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+BAA+B;EAC9F,WAAW,EAAE,QAAQ,CAAC,CAAC,SAAS,4DAA4D;CAC9F,CAAC;CACD,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,MAAM,CAAC,CAAC,SAAS,kCAAkC;EACpE,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,yDAAyD;EAC9F,OAAO,yBAAyB,SAAS,kCAAkC;EAC3E,SAAS,2BAA2B,SAClC,yDACF;CACF,CAAC;CACD,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EACJ,QAAQ,aAAa,CAAC,CACtB,SAAS,4DAA4D;EACxE,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,8CAA8C;CACnF,CAAC;AACH,CAAC"}
|