@vercel/factory 0.0.15 → 0.0.17
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 +362 -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 +20 -6
package/dist/vercel-git.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-git.mjs","names":[],"sources":["../src/vercel-git.ts"],"sourcesContent":["import type { Sandbox, NetworkPolicy } from \"@vercel/sandbox\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport { isSafeGitRef } from \"./github-publication\";\nimport { maxGitBundleBytes, validateGitBundle, type GitHubGitTransport } from \"./github-transfer\";\n\n/** Repository and access level used to mint one trusted Git transport token. */\nexport interface VercelGitHubTokenInput {\n repository: RepositorySlug;\n access: \"read\" | \"write\";\n}\n\n/** Configures fresh trusted sandboxes and repository-scoped Git tokens. */\nexport interface VercelGitHubGitTransportOptions {\n /** Reads must mint contents:read credentials; writes are confined to the supplied repository. */\n token(input: VercelGitHubTokenInput): Promise<string>;\n /** Creates a fresh sandbox, without a snapshot, repository, or running agent. */\n createSandbox?: () => Promise<\n Pick<Sandbox, \"runCommand\" | \"writeFiles\" | \"readFileToBuffer\" | \"updateNetworkPolicy\" | \"stop\">\n >;\n}\n\nfunction credentialPolicy(repository: RepositorySlug, token: string): NetworkPolicy {\n return {\n allow: {\n \"github.com\": [\n {\n match: { path: { startsWith: `/${repository}.git/` } },\n transform: [\n {\n headers: {\n authorization: `Basic ${Buffer.from(`x-access-token:${token}`).toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n },\n };\n}\n\n/** Transfer Git objects through fresh trusted sandboxes; credentials never reach an agent sandbox. */\nexport function createVercelGitHubGitTransport(\n options: VercelGitHubGitTransportOptions,\n): GitHubGitTransport {\n async function isolated<T>(\n repository: RepositorySlug,\n use: (\n sandbox: Awaited<ReturnType<NonNullable<VercelGitHubGitTransportOptions[\"createSandbox\"]>>>,\n git: (...args: string[]) => Promise<string>,\n repository: RepositorySlug,\n ) => Promise<T>,\n ): Promise<T> {\n const slug = repositorySlugSchema.parse(repository.toLowerCase());\n const sandbox = options.createSandbox\n ? await options.createSandbox()\n : await (\n await import(\"@vercel/sandbox\")\n ).Sandbox.create({ runtime: \"node24\", timeout: 180_000, networkPolicy: \"deny-all\" });\n async function git(...args: string[]): Promise<string> {\n const result = await sandbox.runCommand({\n cmd: \"/usr/bin/env\",\n args: [\n \"-i\",\n \"PATH=/usr/bin:/bin\",\n \"HOME=/tmp\",\n \"GIT_CONFIG_NOSYSTEM=1\",\n \"GIT_CONFIG_GLOBAL=/dev/null\",\n \"GIT_TERMINAL_PROMPT=0\",\n \"git\",\n \"-c\",\n \"core.hooksPath=/dev/null\",\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n \"protocol.ext.allow=never\",\n \"-c\",\n \"protocol.file.allow=always\",\n \"-c\",\n \"http.followRedirects=false\",\n ...args,\n ],\n timeoutMs: 120_000,\n });\n if (result.exitCode !== 0)\n throw new Error(`Trusted Git operation failed (${result.exitCode})`);\n return (await result.stdout()).trim();\n }\n try {\n await sandbox.updateNetworkPolicy(\"deny-all\");\n return await use(sandbox, git, slug);\n } finally {\n try {\n await sandbox.updateNetworkPolicy(\"deny-all\");\n } finally {\n await sandbox.stop();\n }\n }\n }\n return {\n async fetch(input) {\n if (input.refs.some((ref) => !ref.startsWith(\"refs/\") || !isSafeGitRef(ref)))\n throw new Error(\"Invalid Git fetch ref\");\n return isolated(input.repository, async (sandbox, git, slug) => {\n await sandbox.updateNetworkPolicy(\n credentialPolicy(slug, await options.token({ repository: slug, access: \"read\" })),\n );\n await git(\n \"clone\",\n \"--bare\",\n \"--no-hardlinks\",\n `https://github.com/${slug}.git`,\n \"/tmp/transfer.git\",\n );\n if (input.refs.length)\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"fetch\",\n \"--no-tags\",\n \"origin\",\n ...input.refs.map((ref) => `+${ref}:${ref}`),\n );\n await sandbox.updateNetworkPolicy(\"deny-all\");\n await git(\"-C\", \"/tmp/transfer.git\", \"bundle\", \"create\", \"/tmp/transfer.bundle\", \"--all\");\n const size = await sandbox.runCommand({\n cmd: \"stat\",\n args: [\"-c\", \"%s\", \"/tmp/transfer.bundle\"],\n });\n const byteLength = Number(await size.stdout());\n if (\n size.exitCode !== 0 ||\n !Number.isSafeInteger(byteLength) ||\n byteLength < 1 ||\n byteLength > maxGitBundleBytes\n )\n throw new Error(\"Git bundle exceeds transfer limit\");\n const data = await sandbox.readFileToBuffer({ path: \"/tmp/transfer.bundle\" });\n if (!data || data.length > maxGitBundleBytes) throw new Error(\"Invalid Git bundle size\");\n const bundle = { base64: data.toString(\"base64\") };\n validateGitBundle(bundle);\n return bundle;\n });\n },\n async push(input) {\n if (\n !isSafeGitRef(input.branch) ||\n !/^[a-f0-9]{40}$/.test(input.head) ||\n !/^[a-f0-9]{40}$/.test(input.expectedHead)\n )\n throw new Error(\"Invalid Git publication target\");\n const bundle = validateGitBundle(input.bundle);\n await isolated(input.repository, async (sandbox, git, slug) => {\n await git(\"init\", \"--bare\", \"/tmp/transfer.git\");\n await sandbox.writeFiles([{ path: \"/tmp/transfer.bundle\", content: bundle }]);\n await git(\"-C\", \"/tmp/transfer.git\", \"bundle\", \"verify\", \"/tmp/transfer.bundle\");\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"fetch\",\n \"/tmp/transfer.bundle\",\n \"HEAD:refs/heads/candidate\",\n );\n if (\n (await git(\"-C\", \"/tmp/transfer.git\", \"rev-parse\", \"refs/heads/candidate\")) !== input.head\n )\n throw new Error(\"Git bundle head does not match publication target\");\n await sandbox.updateNetworkPolicy(\n credentialPolicy(slug, await options.token({ repository: slug, access: \"write\" })),\n );\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"push\",\n `--force-with-lease=refs/heads/${input.branch}:${input.expectedHead}`,\n `https://github.com/${slug}.git`,\n `${input.head}:refs/heads/${input.branch}`,\n );\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vercel-git.mjs","names":[],"sources":["../src/vercel-git.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport type { Sandbox, NetworkPolicy } from \"@vercel/sandbox\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport { isSafeGitRef } from \"./github-publication\";\nimport {\n maxGitBundleBytes,\n withSandboxGitBundle,\n type GitHubGitTransport,\n type GitHubRepositoryBundle,\n} from \"./github-transfer\";\n\ntype TrustedGitSandbox = Pick<\n Sandbox,\n \"cwd\" | \"runCommand\" | \"writeFiles\" | \"readFile\" | \"updateNetworkPolicy\" | \"stop\"\n>;\n\n/** Exact creation request for a fresh Vercel-managed trusted Git publication Sandbox. */\nexport interface VercelManagedGitHubSandboxCreateInput {\n source: {\n type: \"git\";\n url: string;\n };\n __source_credentials: true;\n __commitAs: {\n name: string;\n email: string;\n };\n networkPolicy: NetworkPolicy;\n persistent: false;\n resources: { vcpus: 6 };\n runtime: \"node24\";\n timeout: number;\n}\n\nconst gitOidPattern = /^[a-f0-9]{40}$/u;\nconst absentGitOid = \"0\".repeat(40);\nconst managedGitTimeoutMs = 180_000;\n\n/** Repository and access level used to mint one trusted Git transport token. */\nexport interface VercelGitHubTokenInput {\n repository: RepositorySlug;\n access: \"read\" | \"write\";\n}\n\n/** Configures fresh trusted sandboxes and repository-scoped Git tokens. */\nexport interface VercelGitHubGitTransportOptions {\n /** Reads must mint contents:read credentials; writes are confined to the supplied repository. */\n token(input: VercelGitHubTokenInput): Promise<string>;\n /** Creates a fresh sandbox, without a snapshot, repository, or running agent. */\n createSandbox?: () => Promise<TrustedGitSandbox>;\n}\n\n/** Configures Vercel-managed Git reads and signed publication. */\nexport interface VercelManagedGitHubGitTransportOptions {\n /** Attribution metadata supplied for managed signed commits. */\n commitIdentity: { name: string; email: string };\n /** Creates one unnamed Sandbox from the exact managed credential request. */\n createSandbox?: (input: VercelManagedGitHubSandboxCreateInput) => Promise<TrustedGitSandbox>;\n}\n\nasync function createManagedSandbox(\n input: VercelManagedGitHubSandboxCreateInput,\n): Promise<TrustedGitSandbox> {\n const { Sandbox } = await import(\"@vercel/sandbox\");\n return Sandbox.create({ ...input });\n}\n\nfunction publicationBase(input: {\n branch: string;\n head: string;\n expectedHead: string;\n baseHead?: string;\n}): string {\n if (\n !isSafeGitRef(input.branch) ||\n !gitOidPattern.test(input.head) ||\n !gitOidPattern.test(input.expectedHead) ||\n (input.expectedHead === absentGitOid\n ? input.baseHead === undefined || !gitOidPattern.test(input.baseHead)\n : input.baseHead !== undefined)\n ) {\n throw new Error(\"Invalid Git publication target\");\n }\n return input.expectedHead === absentGitOid ? input.baseHead! : input.expectedHead;\n}\n\nasync function trustedCommand(\n sandbox: TrustedGitSandbox,\n input: { cmd: string; args: string[]; cwd?: string },\n): Promise<string> {\n const result = await sandbox.runCommand({ ...input, timeoutMs: 120_000 });\n if (result.exitCode !== 0) throw new Error(`Trusted Git operation failed (${result.exitCode})`);\n return (await result.stdout()).trim();\n}\n\nfunction bundleTransferSandbox(sandbox: TrustedGitSandbox) {\n return {\n async run({ command }: { command: string }) {\n const result = await sandbox.runCommand({ cmd: \"sh\", args: [\"-c\", command] });\n return { exitCode: result.exitCode, stdout: await result.stdout() };\n },\n async writeBinaryFile({ path, content }: { path: string; content: Uint8Array }) {\n await sandbox.writeFiles([\n {\n path,\n content: Buffer.from(content.buffer, content.byteOffset, content.byteLength),\n },\n ]);\n },\n };\n}\n\nasync function consumeSandboxBundle(\n sandbox: TrustedGitSandbox,\n path: string,\n use: (bundle: GitHubRepositoryBundle) => Promise<void>,\n): Promise<void> {\n const size = await sandbox.runCommand({ cmd: \"stat\", args: [\"-c\", \"%s\", path] });\n const byteLength = Number(await size.stdout());\n if (\n size.exitCode !== 0 ||\n !Number.isSafeInteger(byteLength) ||\n byteLength < 1 ||\n byteLength > maxGitBundleBytes\n ) {\n throw new Error(\"Git bundle exceeds transfer limit\");\n }\n const source = await sandbox.readFile({ path });\n if (!source) throw new Error(\"Git bundle was not created\");\n const readable =\n source instanceof Readable ? source : new Readable({ highWaterMark: 64 * 1024 }).wrap(source);\n const iterator = readable[Symbol.asyncIterator]();\n const stream = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n const { done, value } = await iterator.next();\n if (done) controller.close();\n else controller.enqueue(value);\n },\n async cancel() {\n await iterator.return?.();\n },\n },\n { highWaterMark: 0 },\n );\n try {\n await use({ stream });\n } finally {\n readable.destroy();\n if (!stream.locked) await stream.cancel();\n }\n}\n\nfunction credentialPolicy(repository: RepositorySlug, token: string): NetworkPolicy {\n return {\n allow: {\n \"github.com\": [\n {\n match: { path: { startsWith: `/${repository}.git/` } },\n transform: [\n {\n headers: {\n authorization: `Basic ${Buffer.from(`x-access-token:${token}`).toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n },\n };\n}\n\n/** Transfer Git objects through fresh trusted sandboxes; credentials never reach an agent sandbox. */\nexport function createVercelGitHubGitTransport(\n options: VercelGitHubGitTransportOptions,\n): GitHubGitTransport {\n async function isolated<T>(\n repository: RepositorySlug,\n use: (\n sandbox: Awaited<ReturnType<NonNullable<VercelGitHubGitTransportOptions[\"createSandbox\"]>>>,\n git: (...args: string[]) => Promise<string>,\n repository: RepositorySlug,\n ) => Promise<T>,\n ): Promise<T> {\n const slug = repositorySlugSchema.parse(repository.toLowerCase());\n const sandbox = options.createSandbox\n ? await options.createSandbox()\n : await (\n await import(\"@vercel/sandbox\")\n ).Sandbox.create({ runtime: \"node24\", timeout: 180_000, networkPolicy: \"deny-all\" });\n async function git(...args: string[]): Promise<string> {\n const result = await sandbox.runCommand({\n cmd: \"/usr/bin/env\",\n args: [\n \"-i\",\n \"PATH=/usr/bin:/bin\",\n \"HOME=/tmp\",\n \"GIT_CONFIG_NOSYSTEM=1\",\n \"GIT_CONFIG_GLOBAL=/dev/null\",\n \"GIT_TERMINAL_PROMPT=0\",\n \"git\",\n \"-c\",\n \"core.hooksPath=/dev/null\",\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n \"protocol.ext.allow=never\",\n \"-c\",\n \"protocol.file.allow=always\",\n \"-c\",\n \"http.followRedirects=false\",\n ...args,\n ],\n timeoutMs: 120_000,\n });\n if (result.exitCode !== 0)\n throw new Error(`Trusted Git operation failed (${result.exitCode})`);\n return (await result.stdout()).trim();\n }\n try {\n await sandbox.updateNetworkPolicy(\"deny-all\");\n return await use(sandbox, git, slug);\n } finally {\n try {\n await sandbox.updateNetworkPolicy(\"deny-all\");\n } finally {\n await sandbox.stop();\n }\n }\n }\n return {\n async fetch(input, use) {\n if (input.refs.some((ref) => !ref.startsWith(\"refs/\") || !isSafeGitRef(ref)))\n throw new Error(\"Invalid Git fetch ref\");\n return isolated(input.repository, async (sandbox, git, slug) => {\n await sandbox.updateNetworkPolicy(\n credentialPolicy(slug, await options.token({ repository: slug, access: \"read\" })),\n );\n await git(\n \"clone\",\n \"--bare\",\n \"--no-hardlinks\",\n `https://github.com/${slug}.git`,\n \"/tmp/transfer.git\",\n );\n if (input.refs.length)\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"fetch\",\n \"--no-tags\",\n \"origin\",\n ...input.refs.map((ref) => `+${ref}:${ref}`),\n );\n await sandbox.updateNetworkPolicy(\"deny-all\");\n await git(\"-C\", \"/tmp/transfer.git\", \"bundle\", \"create\", \"/tmp/transfer.bundle\", \"--all\");\n await consumeSandboxBundle(sandbox, \"/tmp/transfer.bundle\", use);\n });\n },\n async push(input) {\n try {\n const baseHead = publicationBase(input);\n return await isolated(input.repository, async (sandbox, git, slug) => {\n await git(\"init\", \"--bare\", \"/tmp/transfer.git\");\n return await withSandboxGitBundle({\n sandbox: bundleTransferSandbox(sandbox),\n bundle: input.bundle,\n async use(path) {\n await git(\"-C\", \"/tmp/transfer.git\", \"bundle\", \"verify\", path);\n await git(\"-C\", \"/tmp/transfer.git\", \"fetch\", path, \"HEAD:refs/heads/candidate\");\n if (\n (await git(\"-C\", \"/tmp/transfer.git\", \"rev-parse\", \"refs/heads/candidate\")) !==\n input.head\n )\n throw new Error(\"Git bundle head does not match publication target\");\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"merge-base\",\n \"--is-ancestor\",\n baseHead,\n \"refs/heads/candidate\",\n );\n await sandbox.updateNetworkPolicy(\n credentialPolicy(slug, await options.token({ repository: slug, access: \"write\" })),\n );\n await input.authorize();\n await git(\n \"-C\",\n \"/tmp/transfer.git\",\n \"push\",\n `--force-with-lease=refs/heads/${input.branch}:${input.expectedHead}`,\n `https://github.com/${slug}.git`,\n `${input.head}:refs/heads/${input.branch}`,\n );\n return input.head;\n },\n });\n });\n } finally {\n if (!input.bundle.stream.locked) await input.bundle.stream.cancel();\n }\n },\n };\n}\n\n/** Use fresh Vercel-managed trusted Sandboxes for Git reads and signed publication. */\nexport function createVercelManagedGitHubGitTransport(\n options: VercelManagedGitHubGitTransportOptions,\n): GitHubGitTransport {\n const createSandbox = options.createSandbox ?? createManagedSandbox;\n const createInput = (\n repository: RepositorySlug,\n access: \"read\" | \"write\",\n ): VercelManagedGitHubSandboxCreateInput => ({\n source: {\n type: \"git\",\n url: `https://github.com/${repository}.git`,\n },\n __source_credentials: true,\n __commitAs: options.commitIdentity,\n networkPolicy: { allow: access === \"read\" ? [\"github.com\"] : [\"github.com\", \"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: managedGitTimeoutMs,\n });\n return {\n async fetch(input, use) {\n if (input.refs.some((ref) => !ref.startsWith(\"refs/\") || !isSafeGitRef(ref))) {\n throw new Error(\"Invalid Git fetch ref\");\n }\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n const sandbox = await createSandbox(createInput(repository, \"read\"));\n const repositoryPath = sandbox.cwd;\n try {\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"clone\", \"--bare\", \"--no-local\", repositoryPath, \"/tmp/transfer.git\"],\n });\n if (input.refs.length > 0) {\n await trustedCommand(sandbox, {\n cmd: \"git\",\n args: [\n \"-C\",\n \"/tmp/transfer.git\",\n \"fetch\",\n \"--no-tags\",\n `https://github.com/${repository}.git`,\n ...input.refs.map((ref) => `+${ref}:${ref}`),\n ],\n });\n }\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"-C\", \"/tmp/transfer.git\", \"bundle\", \"create\", \"/tmp/transfer.bundle\", \"--all\"],\n });\n await consumeSandboxBundle(sandbox, \"/tmp/transfer.bundle\", use);\n } finally {\n await sandbox.stop();\n }\n },\n async push(input) {\n try {\n const baseHead = publicationBase(input);\n if (input.expectedHead === absentGitOid) {\n throw new Error(\"Vercel-managed signed publication requires an existing remote branch\");\n }\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n const sandbox = await createSandbox(createInput(repository, \"write\"));\n const repositoryPath = sandbox.cwd;\n try {\n const hooksPath = `/tmp/factory-hooks-${randomUUID()}`;\n await trustedCommand(sandbox, { cmd: \"mkdir\", args: [\"-m\", \"700\", hooksPath] });\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"config\", \"core.hooksPath\", hooksPath],\n cwd: repositoryPath,\n });\n await withSandboxGitBundle({\n sandbox: bundleTransferSandbox(sandbox),\n bundle: input.bundle,\n async use(path) {\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"bundle\", \"verify\", path],\n cwd: repositoryPath,\n });\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"fetch\", \"--no-tags\", path, `${input.head}:refs/factory/candidate`],\n cwd: repositoryPath,\n });\n if (\n (await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"rev-parse\", \"refs/factory/candidate\"],\n cwd: repositoryPath,\n })) !== input.head\n ) {\n throw new Error(\"Git bundle head does not match publication target\");\n }\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"merge-base\", \"--is-ancestor\", baseHead, \"refs/factory/candidate\"],\n cwd: repositoryPath,\n });\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"checkout\", \"-B\", input.branch, \"refs/factory/candidate\"],\n cwd: repositoryPath,\n });\n await input.authorize();\n await trustedCommand(sandbox, {\n cmd: \"/usr/bin/env\",\n args: [\n `VERCEL_GIT_EXPECTED_REMOTE_OID=${input.expectedHead}`,\n ...(input.baseHead === undefined\n ? []\n : [`VERCEL_GIT_BASE_OID=${input.baseHead}`]),\n \"git\",\n \"push\",\n \"origin\",\n input.branch,\n ],\n cwd: repositoryPath,\n });\n },\n });\n const signedHead = await trustedCommand(sandbox, {\n cmd: \"/usr/bin/git\",\n args: [\"rev-parse\", \"HEAD\"],\n cwd: repositoryPath,\n });\n if (!gitOidPattern.test(signedHead)) {\n throw new Error(\"Managed signed push returned an invalid Git head\");\n }\n return signedHead;\n } finally {\n await sandbox.stop();\n }\n } finally {\n if (!input.bundle.stream.locked) await input.bundle.stream.cancel();\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { RepositorySlug } from "./schema/repository.mjs";
|
|
2
|
+
import { GitHubTreeEntry } from "./github-publication.mjs";
|
|
3
|
+
import { NetworkPolicy, Sandbox } from "@vercel/sandbox";
|
|
4
|
+
//#region src/vercel-github-api.d.ts
|
|
5
|
+
type TrustedGitHubApiSandbox = Pick<Sandbox, "runCommand" | "stop">;
|
|
6
|
+
/** GitHub API actions supported by Vercel-managed Sandbox credentials. */
|
|
7
|
+
type VercelManagedGitHubApiAction = "github:issues:read" | "github:pull-request:create";
|
|
8
|
+
/** Exact creation request for one Vercel-managed GitHub API operation. */
|
|
9
|
+
interface VercelManagedGitHubApiSandboxCreateInput {
|
|
10
|
+
source?: {
|
|
11
|
+
type: "git";
|
|
12
|
+
url: string;
|
|
13
|
+
};
|
|
14
|
+
__source_credentials?: true;
|
|
15
|
+
__commitAs?: {
|
|
16
|
+
name: string;
|
|
17
|
+
email: string;
|
|
18
|
+
};
|
|
19
|
+
__credentials: {
|
|
20
|
+
github: [{
|
|
21
|
+
repositories: [RepositorySlug];
|
|
22
|
+
actions: [VercelManagedGitHubApiAction];
|
|
23
|
+
}];
|
|
24
|
+
};
|
|
25
|
+
networkPolicy: NetworkPolicy;
|
|
26
|
+
persistent: false;
|
|
27
|
+
resources: {
|
|
28
|
+
vcpus: 6;
|
|
29
|
+
};
|
|
30
|
+
runtime: "node24";
|
|
31
|
+
timeout: 60_000 | 180_000;
|
|
32
|
+
}
|
|
33
|
+
/** Configures fresh trusted Sandboxes for brokered GitHub API requests. */
|
|
34
|
+
interface VercelManagedGitHubApiOptions {
|
|
35
|
+
/** Attribution metadata supplied when managed publication creates a signed commit. */
|
|
36
|
+
commitIdentity?: {
|
|
37
|
+
name: string;
|
|
38
|
+
email: string;
|
|
39
|
+
};
|
|
40
|
+
createSandbox?: (input: VercelManagedGitHubApiSandboxCreateInput) => Promise<TrustedGitHubApiSandbox>;
|
|
41
|
+
}
|
|
42
|
+
/** Authorized request to read one exact GitHub issue. */
|
|
43
|
+
interface VercelManagedGitHubIssueInput {
|
|
44
|
+
repository: RepositorySlug;
|
|
45
|
+
issueNumber: number;
|
|
46
|
+
authorize(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** Issue fields returned by a Vercel-managed GitHub API request. */
|
|
49
|
+
interface VercelManagedGitHubIssue {
|
|
50
|
+
title: string;
|
|
51
|
+
body: string | null;
|
|
52
|
+
url: string;
|
|
53
|
+
pullRequest: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** Authorized request to create one draft or ready GitHub pull request. */
|
|
56
|
+
interface VercelManagedGitHubPullRequestInput {
|
|
57
|
+
repository: RepositorySlug;
|
|
58
|
+
title: string;
|
|
59
|
+
body: string;
|
|
60
|
+
head: string;
|
|
61
|
+
base: string;
|
|
62
|
+
draft: boolean;
|
|
63
|
+
authorize(): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
/** Pull-request identity returned by a Vercel-managed GitHub API request. */
|
|
66
|
+
interface VercelManagedGitHubPullRequest {
|
|
67
|
+
pullRequestNumber: number;
|
|
68
|
+
pullRequestUrl: string;
|
|
69
|
+
draft: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Git Data and pull-request inputs for one idempotent managed publication. */
|
|
72
|
+
interface VercelManagedGitHubPublicationInput {
|
|
73
|
+
repository: RepositorySlug;
|
|
74
|
+
baseCommit: string;
|
|
75
|
+
tree: readonly GitHubTreeEntry[];
|
|
76
|
+
commitMessage: string;
|
|
77
|
+
branch: string;
|
|
78
|
+
title: string;
|
|
79
|
+
body: string;
|
|
80
|
+
baseBranch: string;
|
|
81
|
+
draft: boolean;
|
|
82
|
+
authorize(): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/** Commit and pull-request identities returned after managed publication. */
|
|
85
|
+
interface VercelManagedGitHubPublication extends VercelManagedGitHubPullRequest {
|
|
86
|
+
commitSha: string;
|
|
87
|
+
}
|
|
88
|
+
/** Repository-scoped brokered GitHub API operations supplied by Vercel Sandbox. */
|
|
89
|
+
interface VercelManagedGitHubApi {
|
|
90
|
+
readIssue(input: VercelManagedGitHubIssueInput): Promise<VercelManagedGitHubIssue>;
|
|
91
|
+
createPullRequest(input: VercelManagedGitHubPullRequestInput): Promise<VercelManagedGitHubPullRequest>;
|
|
92
|
+
publishPullRequest(input: VercelManagedGitHubPublicationInput): Promise<VercelManagedGitHubPublication>;
|
|
93
|
+
}
|
|
94
|
+
/** GitHub API failure returned through the managed credential Sandbox. */
|
|
95
|
+
declare class VercelManagedGitHubApiError extends Error {
|
|
96
|
+
readonly status: number;
|
|
97
|
+
constructor(status: number, message: string);
|
|
98
|
+
}
|
|
99
|
+
/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */
|
|
100
|
+
declare function createVercelManagedGitHubApi(options?: VercelManagedGitHubApiOptions): VercelManagedGitHubApi;
|
|
101
|
+
//#endregion
|
|
102
|
+
export { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi };
|
|
103
|
+
//# sourceMappingURL=vercel-github-api.d.mts.map
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { repositorySlugSchema } from "./schema/repository.mjs";
|
|
2
|
+
import { isSafeGitRef } from "./github-publication.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/vercel-github-api.ts
|
|
5
|
+
/** GitHub API failure returned through the managed credential Sandbox. */
|
|
6
|
+
var VercelManagedGitHubApiError = class extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(`GitHub API request failed (${status}): ${message}`);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.name = "VercelManagedGitHubApiError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const sandboxResponseSchema = z.object({
|
|
15
|
+
status: z.number().int().min(100).max(599),
|
|
16
|
+
body: z.unknown()
|
|
17
|
+
});
|
|
18
|
+
const issueSchema = z.object({
|
|
19
|
+
title: z.string(),
|
|
20
|
+
body: z.string().nullable(),
|
|
21
|
+
html_url: z.url(),
|
|
22
|
+
pull_request: z.unknown().optional()
|
|
23
|
+
});
|
|
24
|
+
const pullRequestSchema = z.object({
|
|
25
|
+
number: z.number().int().positive(),
|
|
26
|
+
html_url: z.url(),
|
|
27
|
+
draft: z.boolean()
|
|
28
|
+
});
|
|
29
|
+
const shaSchema = z.string().regex(/^[a-f0-9]{40}$/u);
|
|
30
|
+
const commitSchema = z.object({
|
|
31
|
+
sha: shaSchema,
|
|
32
|
+
tree: z.object({ sha: shaSchema }),
|
|
33
|
+
parents: z.array(z.object({ sha: shaSchema }))
|
|
34
|
+
});
|
|
35
|
+
const treeSchema = z.object({ sha: shaSchema });
|
|
36
|
+
const refSchema = z.object({ object: z.object({ sha: shaSchema }) });
|
|
37
|
+
const publicationPullSchema = pullRequestSchema.extend({
|
|
38
|
+
state: z.enum(["open", "closed"]),
|
|
39
|
+
head: z.object({
|
|
40
|
+
ref: z.string(),
|
|
41
|
+
sha: shaSchema
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
const publicationPathSchema = z.string().min(1).max(1024).refine((path) => Buffer.byteLength(path, "utf8") <= 1024, { message: "Managed publication path exceeds byte limit" }).refine((path) => /^[A-Za-z0-9.][A-Za-z0-9._/-]*$/u.test(path) && !path.includes("..") && !path.includes("//") && !path.endsWith("/") && !path.split("/").some((part) => part === "."), { message: "Invalid managed publication path" });
|
|
45
|
+
const publicationBranchSchema = z.string().min(1).max(256).refine((branch) => isSafeGitRef(branch) && branch.split("/").every((component) => component.length > 0 && !component.startsWith(".") && !component.endsWith(".") && !component.endsWith(".lock")), { message: "Invalid managed publication branch" });
|
|
46
|
+
const publicationTextSchema = (maximum, field) => z.string().max(maximum).refine((value) => Buffer.byteLength(value, "utf8") <= maximum, { message: `Managed publication ${field} exceeds byte limit` });
|
|
47
|
+
const publicationCommitMessageSchema = publicationTextSchema(65536, "commit message").min(1);
|
|
48
|
+
const publicationTitleSchema = publicationTextSchema(256, "title").min(1);
|
|
49
|
+
const publicationBodySchema = publicationTextSchema(65536, "body");
|
|
50
|
+
const publicationBaseBranchSchema = z.string().min(1).max(256).refine((branch) => isSafeGitRef(branch), { message: "Invalid managed publication base branch" });
|
|
51
|
+
const publicationContentSchema = z.string().max(2e5).refine((content) => Buffer.byteLength(content, "utf8") <= 2e5, { message: "Managed publication content exceeds byte limit" });
|
|
52
|
+
const publicationTreeSchema = z.array(z.union([z.strictObject({
|
|
53
|
+
path: publicationPathSchema,
|
|
54
|
+
mode: z.enum(["100644", "100755"]),
|
|
55
|
+
type: z.literal("blob"),
|
|
56
|
+
content: publicationContentSchema
|
|
57
|
+
}), z.strictObject({
|
|
58
|
+
path: publicationPathSchema,
|
|
59
|
+
mode: z.enum(["100644", "100755"]),
|
|
60
|
+
type: z.literal("blob"),
|
|
61
|
+
sha: z.null()
|
|
62
|
+
})])).min(1).max(25).refine((entries) => new TextEncoder().encode(JSON.stringify({ tree: entries })).byteLength <= 1e6, "Managed publication tree exceeds transfer limit");
|
|
63
|
+
const githubRequestScript = `
|
|
64
|
+
const request = JSON.parse(process.argv[1]);
|
|
65
|
+
const response = await fetch(request.url, {
|
|
66
|
+
method: request.method,
|
|
67
|
+
headers: {
|
|
68
|
+
accept: "application/vnd.github+json",
|
|
69
|
+
"content-type": "application/json",
|
|
70
|
+
"x-github-api-version": "2022-11-28",
|
|
71
|
+
},
|
|
72
|
+
body: request.body === undefined ? undefined : JSON.stringify(request.body),
|
|
73
|
+
redirect: "error",
|
|
74
|
+
});
|
|
75
|
+
const reader = response.body?.getReader();
|
|
76
|
+
const chunks = [];
|
|
77
|
+
let byteLength = 0;
|
|
78
|
+
if (reader) {
|
|
79
|
+
for (;;) {
|
|
80
|
+
const { done, value } = await reader.read();
|
|
81
|
+
if (done) break;
|
|
82
|
+
byteLength += value.byteLength;
|
|
83
|
+
if (byteLength > 1_000_000) {
|
|
84
|
+
await reader.cancel();
|
|
85
|
+
throw new Error("GitHub API response exceeds transfer limit");
|
|
86
|
+
}
|
|
87
|
+
chunks.push(value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const bytes = new Uint8Array(byteLength);
|
|
91
|
+
let offset = 0;
|
|
92
|
+
for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }
|
|
93
|
+
const text = new TextDecoder().decode(bytes);
|
|
94
|
+
let body = null;
|
|
95
|
+
try { body = text === "" ? null : JSON.parse(text); } catch { body = { message: text }; }
|
|
96
|
+
process.stdout.write(JSON.stringify({ status: response.status, body }));
|
|
97
|
+
`;
|
|
98
|
+
async function createManagedSandbox(input) {
|
|
99
|
+
const { Sandbox } = await import("@vercel/sandbox");
|
|
100
|
+
return Sandbox.create({ ...input });
|
|
101
|
+
}
|
|
102
|
+
function createInput(repository, action) {
|
|
103
|
+
return {
|
|
104
|
+
__credentials: { github: [{
|
|
105
|
+
repositories: [repository],
|
|
106
|
+
actions: [action]
|
|
107
|
+
}] },
|
|
108
|
+
networkPolicy: { allow: ["api.github.com"] },
|
|
109
|
+
persistent: false,
|
|
110
|
+
resources: { vcpus: 6 },
|
|
111
|
+
runtime: "node24",
|
|
112
|
+
timeout: 6e4
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function createPublicationInput(repository, commitIdentity) {
|
|
116
|
+
return {
|
|
117
|
+
source: {
|
|
118
|
+
type: "git",
|
|
119
|
+
url: `https://github.com/${repository}.git`
|
|
120
|
+
},
|
|
121
|
+
__source_credentials: true,
|
|
122
|
+
__commitAs: commitIdentity,
|
|
123
|
+
__credentials: { github: [{
|
|
124
|
+
repositories: [repository],
|
|
125
|
+
actions: ["github:pull-request:create"]
|
|
126
|
+
}] },
|
|
127
|
+
networkPolicy: { allow: ["github.com", "api.github.com"] },
|
|
128
|
+
persistent: false,
|
|
129
|
+
resources: { vcpus: 6 },
|
|
130
|
+
runtime: "node24",
|
|
131
|
+
timeout: 18e4
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function request(createSandbox, input) {
|
|
135
|
+
const sandbox = await createSandbox(createInput(input.repository, input.action));
|
|
136
|
+
try {
|
|
137
|
+
await input.authorize();
|
|
138
|
+
const result = await sandbox.runCommand({
|
|
139
|
+
cmd: "node",
|
|
140
|
+
args: [
|
|
141
|
+
"--input-type=module",
|
|
142
|
+
"--eval",
|
|
143
|
+
githubRequestScript,
|
|
144
|
+
JSON.stringify({
|
|
145
|
+
method: input.method,
|
|
146
|
+
url: `https://api.github.com/repos/${input.repository}${input.path}`,
|
|
147
|
+
...input.body === void 0 ? {} : { body: input.body }
|
|
148
|
+
})
|
|
149
|
+
],
|
|
150
|
+
timeoutMs: 3e4
|
|
151
|
+
});
|
|
152
|
+
const stderr = (await result.stderr()).trim();
|
|
153
|
+
if (result.exitCode !== 0) throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);
|
|
154
|
+
const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));
|
|
155
|
+
if (response.status < 200 || response.status >= 300) {
|
|
156
|
+
const message = z.object({ message: z.string() }).safeParse(response.body);
|
|
157
|
+
throw new VercelManagedGitHubApiError(response.status, message.success ? message.data.message : "Unexpected response");
|
|
158
|
+
}
|
|
159
|
+
return response.body;
|
|
160
|
+
} finally {
|
|
161
|
+
await sandbox.stop();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function sandboxRequest(sandbox, input) {
|
|
165
|
+
const result = await sandbox.runCommand({
|
|
166
|
+
cmd: "node",
|
|
167
|
+
args: [
|
|
168
|
+
"--input-type=module",
|
|
169
|
+
"--eval",
|
|
170
|
+
githubRequestScript,
|
|
171
|
+
JSON.stringify({
|
|
172
|
+
method: input.method,
|
|
173
|
+
url: `https://api.github.com/repos/${input.repository}${input.path}`,
|
|
174
|
+
...input.body === void 0 ? {} : { body: input.body }
|
|
175
|
+
})
|
|
176
|
+
],
|
|
177
|
+
timeoutMs: 3e4
|
|
178
|
+
});
|
|
179
|
+
const stderr = (await result.stderr()).trim();
|
|
180
|
+
if (result.exitCode !== 0) throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);
|
|
181
|
+
const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));
|
|
182
|
+
if (response.status < 200 || response.status >= 300) {
|
|
183
|
+
const message = z.object({ message: z.string() }).safeParse(response.body);
|
|
184
|
+
throw new VercelManagedGitHubApiError(response.status, message.success ? message.data.message : "Unexpected response");
|
|
185
|
+
}
|
|
186
|
+
return response.body;
|
|
187
|
+
}
|
|
188
|
+
/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */
|
|
189
|
+
function createVercelManagedGitHubApi(options = {}) {
|
|
190
|
+
const createSandbox = options.createSandbox ?? createManagedSandbox;
|
|
191
|
+
return {
|
|
192
|
+
async readIssue(input) {
|
|
193
|
+
const repository = repositorySlugSchema.parse(input.repository.toLowerCase());
|
|
194
|
+
if (!Number.isSafeInteger(input.issueNumber) || input.issueNumber < 1) throw new Error("Invalid GitHub issue number");
|
|
195
|
+
const issue = issueSchema.parse(await request(createSandbox, {
|
|
196
|
+
repository,
|
|
197
|
+
action: "github:issues:read",
|
|
198
|
+
method: "GET",
|
|
199
|
+
path: `/issues/${input.issueNumber}`,
|
|
200
|
+
authorize: input.authorize
|
|
201
|
+
}));
|
|
202
|
+
return {
|
|
203
|
+
title: issue.title,
|
|
204
|
+
body: issue.body,
|
|
205
|
+
url: issue.html_url,
|
|
206
|
+
pullRequest: issue.pull_request !== void 0
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
async createPullRequest(input) {
|
|
210
|
+
const repository = repositorySlugSchema.parse(input.repository.toLowerCase());
|
|
211
|
+
const head = publicationBranchSchema.parse(input.head);
|
|
212
|
+
const base = publicationBaseBranchSchema.parse(input.base);
|
|
213
|
+
const title = publicationTitleSchema.parse(input.title);
|
|
214
|
+
const body = publicationBodySchema.parse(input.body);
|
|
215
|
+
const pull = pullRequestSchema.parse(await request(createSandbox, {
|
|
216
|
+
repository,
|
|
217
|
+
action: "github:pull-request:create",
|
|
218
|
+
method: "POST",
|
|
219
|
+
path: "/pulls",
|
|
220
|
+
body: {
|
|
221
|
+
title,
|
|
222
|
+
body,
|
|
223
|
+
head,
|
|
224
|
+
base,
|
|
225
|
+
draft: input.draft
|
|
226
|
+
},
|
|
227
|
+
authorize: input.authorize
|
|
228
|
+
}));
|
|
229
|
+
return {
|
|
230
|
+
pullRequestNumber: pull.number,
|
|
231
|
+
pullRequestUrl: pull.html_url,
|
|
232
|
+
draft: pull.draft
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
async publishPullRequest(input) {
|
|
236
|
+
const repository = repositorySlugSchema.parse(input.repository.toLowerCase());
|
|
237
|
+
if (!options.commitIdentity) throw new Error("Managed publication requires commit identity");
|
|
238
|
+
if (!/^[a-f0-9]{40}$/u.test(input.baseCommit) || input.commitMessage.trim() === "") throw new Error("Invalid managed GitHub publication");
|
|
239
|
+
const publicationTree = publicationTreeSchema.parse(input.tree);
|
|
240
|
+
const branch = publicationBranchSchema.parse(input.branch);
|
|
241
|
+
const baseBranch = publicationBaseBranchSchema.parse(input.baseBranch);
|
|
242
|
+
const title = publicationTitleSchema.parse(input.title);
|
|
243
|
+
const body = publicationBodySchema.parse(input.body);
|
|
244
|
+
const commitMessage = publicationCommitMessageSchema.parse(`${input.commitMessage}\n\nCo-authored-by: ${options.commitIdentity.name} <${options.commitIdentity.email}>`);
|
|
245
|
+
const sandbox = await createSandbox(createPublicationInput(repository, options.commitIdentity));
|
|
246
|
+
try {
|
|
247
|
+
const authorizedRequest = async (request) => {
|
|
248
|
+
await input.authorize();
|
|
249
|
+
return sandboxRequest(sandbox, {
|
|
250
|
+
repository,
|
|
251
|
+
...request
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
const base = commitSchema.parse(await authorizedRequest({
|
|
255
|
+
method: "GET",
|
|
256
|
+
path: `/git/commits/${input.baseCommit}`
|
|
257
|
+
}));
|
|
258
|
+
const tree = treeSchema.parse(await authorizedRequest({
|
|
259
|
+
method: "POST",
|
|
260
|
+
path: "/git/trees",
|
|
261
|
+
body: {
|
|
262
|
+
base_tree: base.tree.sha,
|
|
263
|
+
tree: publicationTree
|
|
264
|
+
}
|
|
265
|
+
}));
|
|
266
|
+
const pullPath = `/pulls?${new URLSearchParams({
|
|
267
|
+
state: "all",
|
|
268
|
+
head: `${repository.split("/")[0]}:${branch}`,
|
|
269
|
+
per_page: "100"
|
|
270
|
+
})}`;
|
|
271
|
+
const pulls = async () => z.array(publicationPullSchema).parse(await authorizedRequest({
|
|
272
|
+
method: "GET",
|
|
273
|
+
path: pullPath
|
|
274
|
+
}));
|
|
275
|
+
const result = (pull, commitSha) => ({
|
|
276
|
+
commitSha,
|
|
277
|
+
pullRequestNumber: pull.number,
|
|
278
|
+
pullRequestUrl: pull.html_url,
|
|
279
|
+
draft: pull.draft
|
|
280
|
+
});
|
|
281
|
+
const refPath = `/git/ref/heads/${branch.split("/").map(encodeURIComponent).join("/")}`;
|
|
282
|
+
let existingRef;
|
|
283
|
+
try {
|
|
284
|
+
existingRef = refSchema.parse(await authorizedRequest({
|
|
285
|
+
method: "GET",
|
|
286
|
+
path: refPath
|
|
287
|
+
}));
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;
|
|
290
|
+
existingRef = null;
|
|
291
|
+
}
|
|
292
|
+
let commitSha = existingRef?.object.sha;
|
|
293
|
+
if (commitSha) {
|
|
294
|
+
const existingPull = (await pulls()).find((pull) => pull.head.ref === branch);
|
|
295
|
+
if (existingPull) {
|
|
296
|
+
if (existingPull.state !== "open") throw new Error("Managed publication pull request is closed");
|
|
297
|
+
const existingCommit = commitSchema.parse(await authorizedRequest({
|
|
298
|
+
method: "GET",
|
|
299
|
+
path: `/git/commits/${existingPull.head.sha}`
|
|
300
|
+
}));
|
|
301
|
+
if (existingCommit.tree.sha !== tree.sha || existingCommit.parents.length !== 1 || existingCommit.parents[0]?.sha !== input.baseCommit) throw new Error("Managed publication pull request contains different changes");
|
|
302
|
+
return result(existingPull, existingPull.head.sha);
|
|
303
|
+
}
|
|
304
|
+
const existingCommit = commitSchema.parse(await authorizedRequest({
|
|
305
|
+
method: "GET",
|
|
306
|
+
path: `/git/commits/${commitSha}`
|
|
307
|
+
}));
|
|
308
|
+
if (existingCommit.tree.sha !== tree.sha || existingCommit.parents.length !== 1 || existingCommit.parents[0]?.sha !== input.baseCommit) throw new Error("Managed publication branch contains different changes");
|
|
309
|
+
} else {
|
|
310
|
+
commitSha = commitSchema.parse(await authorizedRequest({
|
|
311
|
+
method: "POST",
|
|
312
|
+
path: "/git/commits",
|
|
313
|
+
body: {
|
|
314
|
+
message: commitMessage,
|
|
315
|
+
tree: tree.sha,
|
|
316
|
+
parents: [input.baseCommit]
|
|
317
|
+
}
|
|
318
|
+
})).sha;
|
|
319
|
+
try {
|
|
320
|
+
await authorizedRequest({
|
|
321
|
+
method: "POST",
|
|
322
|
+
path: "/git/refs",
|
|
323
|
+
body: {
|
|
324
|
+
ref: `refs/heads/${branch}`,
|
|
325
|
+
sha: commitSha
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (refSchema.parse(await authorizedRequest({
|
|
330
|
+
method: "GET",
|
|
331
|
+
path: refPath
|
|
332
|
+
})).object.sha !== commitSha) throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
let pull;
|
|
336
|
+
try {
|
|
337
|
+
pull = pullRequestSchema.parse(await authorizedRequest({
|
|
338
|
+
method: "POST",
|
|
339
|
+
path: "/pulls",
|
|
340
|
+
body: {
|
|
341
|
+
title,
|
|
342
|
+
body,
|
|
343
|
+
head: branch,
|
|
344
|
+
base: baseBranch,
|
|
345
|
+
draft: input.draft
|
|
346
|
+
}
|
|
347
|
+
}));
|
|
348
|
+
} catch (error) {
|
|
349
|
+
const reconciled = (await pulls()).find((candidate) => candidate.head.ref === branch);
|
|
350
|
+
if (!reconciled || reconciled.state !== "open" || reconciled.head.sha !== commitSha) throw error;
|
|
351
|
+
pull = reconciled;
|
|
352
|
+
}
|
|
353
|
+
return result(pull, commitSha);
|
|
354
|
+
} finally {
|
|
355
|
+
await sandbox.stop();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
export { VercelManagedGitHubApiError, createVercelManagedGitHubApi };
|
|
362
|
+
|
|
363
|
+
//# sourceMappingURL=vercel-github-api.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-github-api.mjs","names":[],"sources":["../src/vercel-github-api.ts"],"sourcesContent":["import type { Sandbox, NetworkPolicy } from \"@vercel/sandbox\";\nimport { z } from \"zod\";\nimport { isSafeGitRef, type GitHubTreeEntry } from \"./github-publication\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\n\ntype TrustedGitHubApiSandbox = Pick<Sandbox, \"runCommand\" | \"stop\">;\n\n/** GitHub API actions supported by Vercel-managed Sandbox credentials. */\nexport type VercelManagedGitHubApiAction = \"github:issues:read\" | \"github:pull-request:create\";\n\n/** Exact creation request for one Vercel-managed GitHub API operation. */\nexport interface VercelManagedGitHubApiSandboxCreateInput {\n source?: {\n type: \"git\";\n url: string;\n };\n __source_credentials?: true;\n __commitAs?: {\n name: string;\n email: string;\n };\n __credentials: {\n github: [\n {\n repositories: [RepositorySlug];\n actions: [VercelManagedGitHubApiAction];\n },\n ];\n };\n networkPolicy: NetworkPolicy;\n persistent: false;\n resources: { vcpus: 6 };\n runtime: \"node24\";\n timeout: 60_000 | 180_000;\n}\n\n/** Configures fresh trusted Sandboxes for brokered GitHub API requests. */\nexport interface VercelManagedGitHubApiOptions {\n /** Attribution metadata supplied when managed publication creates a signed commit. */\n commitIdentity?: { name: string; email: string };\n createSandbox?: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>;\n}\n\n/** Authorized request to read one exact GitHub issue. */\nexport interface VercelManagedGitHubIssueInput {\n repository: RepositorySlug;\n issueNumber: number;\n authorize(): Promise<void>;\n}\n\n/** Issue fields returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubIssue {\n title: string;\n body: string | null;\n url: string;\n pullRequest: boolean;\n}\n\n/** Authorized request to create one draft or ready GitHub pull request. */\nexport interface VercelManagedGitHubPullRequestInput {\n repository: RepositorySlug;\n title: string;\n body: string;\n head: string;\n base: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Pull-request identity returned by a Vercel-managed GitHub API request. */\nexport interface VercelManagedGitHubPullRequest {\n pullRequestNumber: number;\n pullRequestUrl: string;\n draft: boolean;\n}\n\n/** Git Data and pull-request inputs for one idempotent managed publication. */\nexport interface VercelManagedGitHubPublicationInput {\n repository: RepositorySlug;\n baseCommit: string;\n tree: readonly GitHubTreeEntry[];\n commitMessage: string;\n branch: string;\n title: string;\n body: string;\n baseBranch: string;\n draft: boolean;\n authorize(): Promise<void>;\n}\n\n/** Commit and pull-request identities returned after managed publication. */\nexport interface VercelManagedGitHubPublication extends VercelManagedGitHubPullRequest {\n commitSha: string;\n}\n\n/** Repository-scoped brokered GitHub API operations supplied by Vercel Sandbox. */\nexport interface VercelManagedGitHubApi {\n readIssue(input: VercelManagedGitHubIssueInput): Promise<VercelManagedGitHubIssue>;\n createPullRequest(\n input: VercelManagedGitHubPullRequestInput,\n ): Promise<VercelManagedGitHubPullRequest>;\n publishPullRequest(\n input: VercelManagedGitHubPublicationInput,\n ): Promise<VercelManagedGitHubPublication>;\n}\n\n/** GitHub API failure returned through the managed credential Sandbox. */\nexport class VercelManagedGitHubApiError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(`GitHub API request failed (${status}): ${message}`);\n this.name = \"VercelManagedGitHubApiError\";\n }\n}\n\nconst sandboxResponseSchema = z.object({\n status: z.number().int().min(100).max(599),\n body: z.unknown(),\n});\nconst issueSchema = z.object({\n title: z.string(),\n body: z.string().nullable(),\n html_url: z.url(),\n pull_request: z.unknown().optional(),\n});\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n draft: z.boolean(),\n});\nconst shaSchema = z.string().regex(/^[a-f0-9]{40}$/u);\nconst commitSchema = z.object({\n sha: shaSchema,\n tree: z.object({ sha: shaSchema }),\n parents: z.array(z.object({ sha: shaSchema })),\n});\nconst treeSchema = z.object({ sha: shaSchema });\nconst refSchema = z.object({ object: z.object({ sha: shaSchema }) });\nconst publicationPullSchema = pullRequestSchema.extend({\n state: z.enum([\"open\", \"closed\"]),\n head: z.object({ ref: z.string(), sha: shaSchema }),\n});\nconst publicationPathSchema = z\n .string()\n .min(1)\n .max(1_024)\n .refine((path) => Buffer.byteLength(path, \"utf8\") <= 1_024, {\n message: \"Managed publication path exceeds byte limit\",\n })\n .refine(\n (path) =>\n /^[A-Za-z0-9.][A-Za-z0-9._/-]*$/u.test(path) &&\n !path.includes(\"..\") &&\n !path.includes(\"//\") &&\n !path.endsWith(\"/\") &&\n !path.split(\"/\").some((part) => part === \".\"),\n { message: \"Invalid managed publication path\" },\n );\nconst publicationBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine(\n (branch) =>\n isSafeGitRef(branch) &&\n branch\n .split(\"/\")\n .every(\n (component) =>\n component.length > 0 &&\n !component.startsWith(\".\") &&\n !component.endsWith(\".\") &&\n !component.endsWith(\".lock\"),\n ),\n { message: \"Invalid managed publication branch\" },\n );\nconst publicationTextSchema = (maximum: number, field: string) =>\n z\n .string()\n .max(maximum)\n .refine((value) => Buffer.byteLength(value, \"utf8\") <= maximum, {\n message: `Managed publication ${field} exceeds byte limit`,\n });\nconst publicationCommitMessageSchema = publicationTextSchema(65_536, \"commit message\").min(1);\nconst publicationTitleSchema = publicationTextSchema(256, \"title\").min(1);\nconst publicationBodySchema = publicationTextSchema(65_536, \"body\");\nconst publicationBaseBranchSchema = z\n .string()\n .min(1)\n .max(256)\n .refine((branch) => isSafeGitRef(branch), {\n message: \"Invalid managed publication base branch\",\n });\nconst publicationContentSchema = z\n .string()\n .max(200_000)\n .refine((content) => Buffer.byteLength(content, \"utf8\") <= 200_000, {\n message: \"Managed publication content exceeds byte limit\",\n });\nconst publicationTreeSchema = z\n .array(\n z.union([\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n content: publicationContentSchema,\n }),\n z.strictObject({\n path: publicationPathSchema,\n mode: z.enum([\"100644\", \"100755\"]),\n type: z.literal(\"blob\"),\n sha: z.null(),\n }),\n ]),\n )\n .min(1)\n .max(25)\n .refine(\n (entries) =>\n new TextEncoder().encode(JSON.stringify({ tree: entries })).byteLength <= 1_000_000,\n \"Managed publication tree exceeds transfer limit\",\n );\nconst githubRequestScript = `\nconst request = JSON.parse(process.argv[1]);\nconst response = await fetch(request.url, {\n method: request.method,\n headers: {\n accept: \"application/vnd.github+json\",\n \"content-type\": \"application/json\",\n \"x-github-api-version\": \"2022-11-28\",\n },\n body: request.body === undefined ? undefined : JSON.stringify(request.body),\n redirect: \"error\",\n});\nconst reader = response.body?.getReader();\nconst chunks = [];\nlet byteLength = 0;\nif (reader) {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n byteLength += value.byteLength;\n if (byteLength > 1_000_000) {\n await reader.cancel();\n throw new Error(\"GitHub API response exceeds transfer limit\");\n }\n chunks.push(value);\n }\n}\nconst bytes = new Uint8Array(byteLength);\nlet offset = 0;\nfor (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }\nconst text = new TextDecoder().decode(bytes);\nlet body = null;\ntry { body = text === \"\" ? null : JSON.parse(text); } catch { body = { message: text }; }\nprocess.stdout.write(JSON.stringify({ status: response.status, body }));\n`;\n\nasync function createManagedSandbox(\n input: VercelManagedGitHubApiSandboxCreateInput,\n): Promise<TrustedGitHubApiSandbox> {\n const { Sandbox } = await import(\"@vercel/sandbox\");\n return Sandbox.create({ ...input });\n}\n\nfunction createInput(\n repository: RepositorySlug,\n action: VercelManagedGitHubApiAction,\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n __credentials: { github: [{ repositories: [repository], actions: [action] }] },\n networkPolicy: { allow: [\"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 60_000,\n };\n}\n\nfunction createPublicationInput(\n repository: RepositorySlug,\n commitIdentity: { name: string; email: string },\n): VercelManagedGitHubApiSandboxCreateInput {\n return {\n source: { type: \"git\", url: `https://github.com/${repository}.git` },\n __source_credentials: true,\n __commitAs: commitIdentity,\n __credentials: {\n github: [\n {\n repositories: [repository],\n actions: [\"github:pull-request:create\"],\n },\n ],\n },\n networkPolicy: { allow: [\"github.com\", \"api.github.com\"] },\n persistent: false,\n resources: { vcpus: 6 },\n runtime: \"node24\",\n timeout: 180_000,\n };\n}\n\nasync function request(\n createSandbox: (\n input: VercelManagedGitHubApiSandboxCreateInput,\n ) => Promise<TrustedGitHubApiSandbox>,\n input: {\n repository: RepositorySlug;\n action: VercelManagedGitHubApiAction;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n authorize(): Promise<void>;\n },\n): Promise<unknown> {\n const sandbox = await createSandbox(createInput(input.repository, input.action));\n try {\n await input.authorize();\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n } finally {\n await sandbox.stop();\n }\n}\n\nasync function sandboxRequest(\n sandbox: TrustedGitHubApiSandbox,\n input: {\n repository: RepositorySlug;\n method: \"GET\" | \"POST\";\n path: string;\n body?: unknown;\n },\n): Promise<unknown> {\n const result = await sandbox.runCommand({\n cmd: \"node\",\n args: [\n \"--input-type=module\",\n \"--eval\",\n githubRequestScript,\n JSON.stringify({\n method: input.method,\n url: `https://api.github.com/repos/${input.repository}${input.path}`,\n ...(input.body === undefined ? {} : { body: input.body }),\n }),\n ],\n timeoutMs: 30_000,\n });\n const stderr = (await result.stderr()).trim();\n if (result.exitCode !== 0) {\n throw new Error(`Managed GitHub API operation failed (${result.exitCode}): ${stderr}`);\n }\n const response = sandboxResponseSchema.parse(JSON.parse((await result.stdout()).trim()));\n if (response.status < 200 || response.status >= 300) {\n const message = z.object({ message: z.string() }).safeParse(response.body);\n throw new VercelManagedGitHubApiError(\n response.status,\n message.success ? message.data.message : \"Unexpected response\",\n );\n }\n return response.body;\n}\n\n/** Create repository-scoped GitHub API operations backed by managed Sandbox credentials. */\nexport function createVercelManagedGitHubApi(\n options: VercelManagedGitHubApiOptions = {},\n): VercelManagedGitHubApi {\n const createSandbox = options.createSandbox ?? createManagedSandbox;\n return {\n async readIssue(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!Number.isSafeInteger(input.issueNumber) || input.issueNumber < 1) {\n throw new Error(\"Invalid GitHub issue number\");\n }\n const issue = issueSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:issues:read\",\n method: \"GET\",\n path: `/issues/${input.issueNumber}`,\n authorize: input.authorize,\n }),\n );\n return {\n title: issue.title,\n body: issue.body,\n url: issue.html_url,\n pullRequest: issue.pull_request !== undefined,\n };\n },\n async createPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n const head = publicationBranchSchema.parse(input.head);\n const base = publicationBaseBranchSchema.parse(input.base);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const pull = pullRequestSchema.parse(\n await request(createSandbox, {\n repository,\n action: \"github:pull-request:create\",\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head,\n base,\n draft: input.draft,\n },\n authorize: input.authorize,\n }),\n );\n return {\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n };\n },\n async publishPullRequest(input) {\n const repository = repositorySlugSchema.parse(input.repository.toLowerCase());\n if (!options.commitIdentity) throw new Error(\"Managed publication requires commit identity\");\n if (!/^[a-f0-9]{40}$/u.test(input.baseCommit) || input.commitMessage.trim() === \"\") {\n throw new Error(\"Invalid managed GitHub publication\");\n }\n const publicationTree = publicationTreeSchema.parse(input.tree);\n const branch = publicationBranchSchema.parse(input.branch);\n const baseBranch = publicationBaseBranchSchema.parse(input.baseBranch);\n const title = publicationTitleSchema.parse(input.title);\n const body = publicationBodySchema.parse(input.body);\n const commitMessage = publicationCommitMessageSchema.parse(\n `${input.commitMessage}\\n\\nCo-authored-by: ${options.commitIdentity.name} <${options.commitIdentity.email}>`,\n );\n const sandbox = await createSandbox(\n createPublicationInput(repository, options.commitIdentity),\n );\n try {\n const authorizedRequest = async (\n request: Omit<Parameters<typeof sandboxRequest>[1], \"repository\">,\n ) => {\n await input.authorize();\n return sandboxRequest(sandbox, { repository, ...request });\n };\n const base = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${input.baseCommit}`,\n }),\n );\n const tree = treeSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/trees\",\n body: { base_tree: base.tree.sha, tree: publicationTree },\n }),\n );\n const pullPath = `/pulls?${new URLSearchParams({\n state: \"all\",\n head: `${repository.split(\"/\")[0]}:${branch}`,\n per_page: \"100\",\n })}`;\n const pulls = async () =>\n z\n .array(publicationPullSchema)\n .parse(await authorizedRequest({ method: \"GET\", path: pullPath }));\n const result = (pull: z.infer<typeof pullRequestSchema>, commitSha: string) => ({\n commitSha,\n pullRequestNumber: pull.number,\n pullRequestUrl: pull.html_url,\n draft: pull.draft,\n });\n const refPath = `/git/ref/heads/${branch.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n let existingRef: z.infer<typeof refSchema> | null;\n try {\n existingRef = refSchema.parse(await authorizedRequest({ method: \"GET\", path: refPath }));\n } catch (error) {\n if (!(error instanceof VercelManagedGitHubApiError) || error.status !== 404) throw error;\n existingRef = null;\n }\n let commitSha = existingRef?.object.sha;\n if (commitSha) {\n const existingPull = (await pulls()).find((pull) => pull.head.ref === branch);\n if (existingPull) {\n if (existingPull.state !== \"open\") {\n throw new Error(\"Managed publication pull request is closed\");\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${existingPull.head.sha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication pull request contains different changes\");\n }\n return result(existingPull, existingPull.head.sha);\n }\n const existingCommit = commitSchema.parse(\n await authorizedRequest({\n method: \"GET\",\n path: `/git/commits/${commitSha}`,\n }),\n );\n if (\n existingCommit.tree.sha !== tree.sha ||\n existingCommit.parents.length !== 1 ||\n existingCommit.parents[0]?.sha !== input.baseCommit\n ) {\n throw new Error(\"Managed publication branch contains different changes\");\n }\n } else {\n const commit = commitSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/commits\",\n body: {\n message: commitMessage,\n tree: tree.sha,\n parents: [input.baseCommit],\n },\n }),\n );\n commitSha = commit.sha;\n try {\n await authorizedRequest({\n method: \"POST\",\n path: \"/git/refs\",\n body: { ref: `refs/heads/${branch}`, sha: commitSha },\n });\n } catch (error) {\n const reconciled = refSchema.parse(\n await authorizedRequest({ method: \"GET\", path: refPath }),\n );\n if (reconciled.object.sha !== commitSha) throw error;\n }\n }\n let pull: z.infer<typeof pullRequestSchema>;\n try {\n pull = pullRequestSchema.parse(\n await authorizedRequest({\n method: \"POST\",\n path: \"/pulls\",\n body: {\n title,\n body,\n head: branch,\n base: baseBranch,\n draft: input.draft,\n },\n }),\n );\n } catch (error) {\n const reconciled = (await pulls()).find((candidate) => candidate.head.ref === branch);\n if (!reconciled || reconciled.state !== \"open\" || reconciled.head.sha !== commitSha) {\n throw error;\n }\n pull = reconciled;\n }\n return result(pull, commitSha);\n } finally {\n await sandbox.stop();\n }\n },\n };\n}\n"],"mappings":";;;;;AA6GA,IAAa,8BAAb,cAAiD,MAAM;CAE1C;CADX,YACE,QACA,SACA;EACA,MAAM,8BAA8B,OAAO,KAAK,SAAS;EAHhD,KAAA,SAAA;EAIT,KAAK,OAAO;CACd;AACF;AAEA,MAAM,wBAAwB,EAAE,OAAO;CACrC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG;CACzC,MAAM,EAAE,QAAQ;AAClB,CAAC;AACD,MAAM,cAAc,EAAE,OAAO;CAC3B,OAAO,EAAE,OAAO;CAChB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,UAAU,EAAE,IAAI;CAChB,cAAc,EAAE,QAAQ,CAAC,CAAC,SAAS;AACrC,CAAC;AACD,MAAM,oBAAoB,EAAE,OAAO;CACjC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAClC,UAAU,EAAE,IAAI;CAChB,OAAO,EAAE,QAAQ;AACnB,CAAC;AACD,MAAM,YAAY,EAAE,OAAO,CAAC,CAAC,MAAM,iBAAiB;AACpD,MAAM,eAAe,EAAE,OAAO;CAC5B,KAAK;CACL,MAAM,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC;CACjC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,CAAC;AAC/C,CAAC;AACD,MAAM,aAAa,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC;AAC9C,MAAM,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC;AACnE,MAAM,wBAAwB,kBAAkB,OAAO;CACrD,OAAO,EAAE,KAAK,CAAC,QAAQ,QAAQ,CAAC;CAChC,MAAM,EAAE,OAAO;EAAE,KAAK,EAAE,OAAO;EAAG,KAAK;CAAU,CAAC;AACpD,CAAC;AACD,MAAM,wBAAwB,EAC3B,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,IAAK,CAAC,CACV,QAAQ,SAAS,OAAO,WAAW,MAAM,MAAM,KAAK,MAAO,EAC1D,SAAS,8CACX,CAAC,CAAC,CACD,QACE,SACC,kCAAkC,KAAK,IAAI,KAC3C,CAAC,KAAK,SAAS,IAAI,KACnB,CAAC,KAAK,SAAS,IAAI,KACnB,CAAC,KAAK,SAAS,GAAG,KAClB,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,SAAS,SAAS,GAAG,GAC9C,EAAE,SAAS,mCAAmC,CAChD;AACF,MAAM,0BAA0B,EAC7B,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,QACE,WACC,aAAa,MAAM,KACnB,OACG,MAAM,GAAG,CAAC,CACV,OACE,cACC,UAAU,SAAS,KACnB,CAAC,UAAU,WAAW,GAAG,KACzB,CAAC,UAAU,SAAS,GAAG,KACvB,CAAC,UAAU,SAAS,OAAO,CAC/B,GACJ,EAAE,SAAS,qCAAqC,CAClD;AACF,MAAM,yBAAyB,SAAiB,UAC9C,EACG,OAAO,CAAC,CACR,IAAI,OAAO,CAAC,CACZ,QAAQ,UAAU,OAAO,WAAW,OAAO,MAAM,KAAK,SAAS,EAC9D,SAAS,uBAAuB,MAAM,qBACxC,CAAC;AACL,MAAM,iCAAiC,sBAAsB,OAAQ,gBAAgB,CAAC,CAAC,IAAI,CAAC;AAC5F,MAAM,yBAAyB,sBAAsB,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC;AACxE,MAAM,wBAAwB,sBAAsB,OAAQ,MAAM;AAClE,MAAM,8BAA8B,EACjC,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,QAAQ,WAAW,aAAa,MAAM,GAAG,EACxC,SAAS,0CACX,CAAC;AACH,MAAM,2BAA2B,EAC9B,OAAO,CAAC,CACR,IAAI,GAAO,CAAC,CACZ,QAAQ,YAAY,OAAO,WAAW,SAAS,MAAM,KAAK,KAAS,EAClE,SAAS,iDACX,CAAC;AACH,MAAM,wBAAwB,EAC3B,MACC,EAAE,MAAM,CACN,EAAE,aAAa;CACb,MAAM;CACN,MAAM,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;CACjC,MAAM,EAAE,QAAQ,MAAM;CACtB,SAAS;AACX,CAAC,GACD,EAAE,aAAa;CACb,MAAM;CACN,MAAM,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;CACjC,MAAM,EAAE,QAAQ,MAAM;CACtB,KAAK,EAAE,KAAK;AACd,CAAC,CACH,CAAC,CACH,CAAC,CACA,IAAI,CAAC,CAAC,CACN,IAAI,EAAE,CAAC,CACP,QACE,YACC,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,cAAc,KAC5E,iDACF;AACF,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoC5B,eAAe,qBACb,OACkC;CAClC,MAAM,EAAE,YAAY,MAAM,OAAO;CACjC,OAAO,QAAQ,OAAO,EAAE,GAAG,MAAM,CAAC;AACpC;AAEA,SAAS,YACP,YACA,QAC0C;CAC1C,OAAO;EACL,eAAe,EAAE,QAAQ,CAAC;GAAE,cAAc,CAAC,UAAU;GAAG,SAAS,CAAC,MAAM;EAAE,CAAC,EAAE;EAC7E,eAAe,EAAE,OAAO,CAAC,gBAAgB,EAAE;EAC3C,YAAY;EACZ,WAAW,EAAE,OAAO,EAAE;EACtB,SAAS;EACT,SAAS;CACX;AACF;AAEA,SAAS,uBACP,YACA,gBAC0C;CAC1C,OAAO;EACL,QAAQ;GAAE,MAAM;GAAO,KAAK,sBAAsB,WAAW;EAAM;EACnE,sBAAsB;EACtB,YAAY;EACZ,eAAe,EACb,QAAQ,CACN;GACE,cAAc,CAAC,UAAU;GACzB,SAAS,CAAC,4BAA4B;EACxC,CACF,EACF;EACA,eAAe,EAAE,OAAO,CAAC,cAAc,gBAAgB,EAAE;EACzD,YAAY;EACZ,WAAW,EAAE,OAAO,EAAE;EACtB,SAAS;EACT,SAAS;CACX;AACF;AAEA,eAAe,QACb,eAGA,OAQkB;CAClB,MAAM,UAAU,MAAM,cAAc,YAAY,MAAM,YAAY,MAAM,MAAM,CAAC;CAC/E,IAAI;EACF,MAAM,MAAM,UAAU;EACtB,MAAM,SAAS,MAAM,QAAQ,WAAW;GACtC,KAAK;GACL,MAAM;IACJ;IACA;IACA;IACA,KAAK,UAAU;KACb,QAAQ,MAAM;KACd,KAAK,gCAAgC,MAAM,aAAa,MAAM;KAC9D,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;IACzD,CAAC;GACH;GACA,WAAW;EACb,CAAC;EACD,MAAM,UAAU,MAAM,OAAO,OAAO,EAAA,CAAG,KAAK;EAC5C,IAAI,OAAO,aAAa,GACtB,MAAM,IAAI,MAAM,wCAAwC,OAAO,SAAS,KAAK,QAAQ;EAEvF,MAAM,WAAW,sBAAsB,MAAM,KAAK,OAAO,MAAM,OAAO,OAAO,EAAA,CAAG,KAAK,CAAC,CAAC;EACvF,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;GACnD,MAAM,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU,SAAS,IAAI;GACzE,MAAM,IAAI,4BACR,SAAS,QACT,QAAQ,UAAU,QAAQ,KAAK,UAAU,qBAC3C;EACF;EACA,OAAO,SAAS;CAClB,UAAU;EACR,MAAM,QAAQ,KAAK;CACrB;AACF;AAEA,eAAe,eACb,SACA,OAMkB;CAClB,MAAM,SAAS,MAAM,QAAQ,WAAW;EACtC,KAAK;EACL,MAAM;GACJ;GACA;GACA;GACA,KAAK,UAAU;IACb,QAAQ,MAAM;IACd,KAAK,gCAAgC,MAAM,aAAa,MAAM;IAC9D,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;GACzD,CAAC;EACH;EACA,WAAW;CACb,CAAC;CACD,MAAM,UAAU,MAAM,OAAO,OAAO,EAAA,CAAG,KAAK;CAC5C,IAAI,OAAO,aAAa,GACtB,MAAM,IAAI,MAAM,wCAAwC,OAAO,SAAS,KAAK,QAAQ;CAEvF,MAAM,WAAW,sBAAsB,MAAM,KAAK,OAAO,MAAM,OAAO,OAAO,EAAA,CAAG,KAAK,CAAC,CAAC;CACvF,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;EACnD,MAAM,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU,SAAS,IAAI;EACzE,MAAM,IAAI,4BACR,SAAS,QACT,QAAQ,UAAU,QAAQ,KAAK,UAAU,qBAC3C;CACF;CACA,OAAO,SAAS;AAClB;;AAGA,SAAgB,6BACd,UAAyC,CAAC,GAClB;CACxB,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,OAAO;EACL,MAAM,UAAU,OAAO;GACrB,MAAM,aAAa,qBAAqB,MAAM,MAAM,WAAW,YAAY,CAAC;GAC5E,IAAI,CAAC,OAAO,cAAc,MAAM,WAAW,KAAK,MAAM,cAAc,GAClE,MAAM,IAAI,MAAM,6BAA6B;GAE/C,MAAM,QAAQ,YAAY,MACxB,MAAM,QAAQ,eAAe;IAC3B;IACA,QAAQ;IACR,QAAQ;IACR,MAAM,WAAW,MAAM;IACvB,WAAW,MAAM;GACnB,CAAC,CACH;GACA,OAAO;IACL,OAAO,MAAM;IACb,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,aAAa,MAAM,iBAAiB,KAAA;GACtC;EACF;EACA,MAAM,kBAAkB,OAAO;GAC7B,MAAM,aAAa,qBAAqB,MAAM,MAAM,WAAW,YAAY,CAAC;GAC5E,MAAM,OAAO,wBAAwB,MAAM,MAAM,IAAI;GACrD,MAAM,OAAO,4BAA4B,MAAM,MAAM,IAAI;GACzD,MAAM,QAAQ,uBAAuB,MAAM,MAAM,KAAK;GACtD,MAAM,OAAO,sBAAsB,MAAM,MAAM,IAAI;GACnD,MAAM,OAAO,kBAAkB,MAC7B,MAAM,QAAQ,eAAe;IAC3B;IACA,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;KACJ;KACA;KACA;KACA;KACA,OAAO,MAAM;IACf;IACA,WAAW,MAAM;GACnB,CAAC,CACH;GACA,OAAO;IACL,mBAAmB,KAAK;IACxB,gBAAgB,KAAK;IACrB,OAAO,KAAK;GACd;EACF;EACA,MAAM,mBAAmB,OAAO;GAC9B,MAAM,aAAa,qBAAqB,MAAM,MAAM,WAAW,YAAY,CAAC;GAC5E,IAAI,CAAC,QAAQ,gBAAgB,MAAM,IAAI,MAAM,8CAA8C;GAC3F,IAAI,CAAC,kBAAkB,KAAK,MAAM,UAAU,KAAK,MAAM,cAAc,KAAK,MAAM,IAC9E,MAAM,IAAI,MAAM,oCAAoC;GAEtD,MAAM,kBAAkB,sBAAsB,MAAM,MAAM,IAAI;GAC9D,MAAM,SAAS,wBAAwB,MAAM,MAAM,MAAM;GACzD,MAAM,aAAa,4BAA4B,MAAM,MAAM,UAAU;GACrE,MAAM,QAAQ,uBAAuB,MAAM,MAAM,KAAK;GACtD,MAAM,OAAO,sBAAsB,MAAM,MAAM,IAAI;GACnD,MAAM,gBAAgB,+BAA+B,MACnD,GAAG,MAAM,cAAc,sBAAsB,QAAQ,eAAe,KAAK,IAAI,QAAQ,eAAe,MAAM,EAC5G;GACA,MAAM,UAAU,MAAM,cACpB,uBAAuB,YAAY,QAAQ,cAAc,CAC3D;GACA,IAAI;IACF,MAAM,oBAAoB,OACxB,YACG;KACH,MAAM,MAAM,UAAU;KACtB,OAAO,eAAe,SAAS;MAAE;MAAY,GAAG;KAAQ,CAAC;IAC3D;IACA,MAAM,OAAO,aAAa,MACxB,MAAM,kBAAkB;KACtB,QAAQ;KACR,MAAM,gBAAgB,MAAM;IAC9B,CAAC,CACH;IACA,MAAM,OAAO,WAAW,MACtB,MAAM,kBAAkB;KACtB,QAAQ;KACR,MAAM;KACN,MAAM;MAAE,WAAW,KAAK,KAAK;MAAK,MAAM;KAAgB;IAC1D,CAAC,CACH;IACA,MAAM,WAAW,UAAU,IAAI,gBAAgB;KAC7C,OAAO;KACP,MAAM,GAAG,WAAW,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG;KACrC,UAAU;IACZ,CAAC;IACD,MAAM,QAAQ,YACZ,EACG,MAAM,qBAAqB,CAAC,CAC5B,MAAM,MAAM,kBAAkB;KAAE,QAAQ;KAAO,MAAM;IAAS,CAAC,CAAC;IACrE,MAAM,UAAU,MAAyC,eAAuB;KAC9E;KACA,mBAAmB,KAAK;KACxB,gBAAgB,KAAK;KACrB,OAAO,KAAK;IACd;IACA,MAAM,UAAU,kBAAkB,OAAO,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;IACpF,IAAI;IACJ,IAAI;KACF,cAAc,UAAU,MAAM,MAAM,kBAAkB;MAAE,QAAQ;MAAO,MAAM;KAAQ,CAAC,CAAC;IACzF,SAAS,OAAO;KACd,IAAI,EAAE,iBAAiB,gCAAgC,MAAM,WAAW,KAAK,MAAM;KACnF,cAAc;IAChB;IACA,IAAI,YAAY,aAAa,OAAO;IACpC,IAAI,WAAW;KACb,MAAM,gBAAgB,MAAM,MAAM,EAAA,CAAG,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM;KAC5E,IAAI,cAAc;MAChB,IAAI,aAAa,UAAU,QACzB,MAAM,IAAI,MAAM,4CAA4C;MAE9D,MAAM,iBAAiB,aAAa,MAClC,MAAM,kBAAkB;OACtB,QAAQ;OACR,MAAM,gBAAgB,aAAa,KAAK;MAC1C,CAAC,CACH;MACA,IACE,eAAe,KAAK,QAAQ,KAAK,OACjC,eAAe,QAAQ,WAAW,KAClC,eAAe,QAAQ,EAAE,EAAE,QAAQ,MAAM,YAEzC,MAAM,IAAI,MAAM,6DAA6D;MAE/E,OAAO,OAAO,cAAc,aAAa,KAAK,GAAG;KACnD;KACA,MAAM,iBAAiB,aAAa,MAClC,MAAM,kBAAkB;MACtB,QAAQ;MACR,MAAM,gBAAgB;KACxB,CAAC,CACH;KACA,IACE,eAAe,KAAK,QAAQ,KAAK,OACjC,eAAe,QAAQ,WAAW,KAClC,eAAe,QAAQ,EAAE,EAAE,QAAQ,MAAM,YAEzC,MAAM,IAAI,MAAM,uDAAuD;IAE3E,OAAO;KAYL,YAXe,aAAa,MAC1B,MAAM,kBAAkB;MACtB,QAAQ;MACR,MAAM;MACN,MAAM;OACJ,SAAS;OACT,MAAM,KAAK;OACX,SAAS,CAAC,MAAM,UAAU;MAC5B;KACF,CAAC,CAEc,CAAC,CAAC;KACnB,IAAI;MACF,MAAM,kBAAkB;OACtB,QAAQ;OACR,MAAM;OACN,MAAM;QAAE,KAAK,cAAc;QAAU,KAAK;OAAU;MACtD,CAAC;KACH,SAAS,OAAO;MAId,IAHmB,UAAU,MAC3B,MAAM,kBAAkB;OAAE,QAAQ;OAAO,MAAM;MAAQ,CAAC,CAE7C,CAAC,CAAC,OAAO,QAAQ,WAAW,MAAM;KACjD;IACF;IACA,IAAI;IACJ,IAAI;KACF,OAAO,kBAAkB,MACvB,MAAM,kBAAkB;MACtB,QAAQ;MACR,MAAM;MACN,MAAM;OACJ;OACA;OACA,MAAM;OACN,MAAM;OACN,OAAO,MAAM;MACf;KACF,CAAC,CACH;IACF,SAAS,OAAO;KACd,MAAM,cAAc,MAAM,MAAM,EAAA,CAAG,MAAM,cAAc,UAAU,KAAK,QAAQ,MAAM;KACpF,IAAI,CAAC,cAAc,WAAW,UAAU,UAAU,WAAW,KAAK,QAAQ,WACxE,MAAM;KAER,OAAO;IACT;IACA,OAAO,OAAO,MAAM,SAAS;GAC/B,UAAU;IACR,MAAM,QAAQ,KAAK;GACrB;EACF;CACF;AACF"}
|
package/dist/vercel.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RepositorySlug } from "./schema/repository.mjs";
|
|
2
2
|
import { WorkNotification, WorkNotifications } from "./store/work-notifications.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { VercelManagedGitHubApi, VercelManagedGitHubApiAction, VercelManagedGitHubApiError, VercelManagedGitHubApiOptions, VercelManagedGitHubApiSandboxCreateInput, VercelManagedGitHubIssue, VercelManagedGitHubIssueInput, VercelManagedGitHubPublication, VercelManagedGitHubPublicationInput, VercelManagedGitHubPullRequest, VercelManagedGitHubPullRequestInput, createVercelManagedGitHubApi } from "./vercel-github-api.mjs";
|
|
4
|
+
import { VercelGitHubGitTransportOptions, VercelGitHubTokenInput, VercelManagedGitHubGitTransportOptions, VercelManagedGitHubSandboxCreateInput, createVercelGitHubGitTransport, createVercelManagedGitHubGitTransport } from "./vercel-git.mjs";
|
|
4
5
|
//#region src/vercel.d.ts
|
|
5
6
|
/** Provider delivery settings applied to one Vercel Queue message. */
|
|
6
7
|
interface VercelQueueSendOptions {
|
|
@@ -59,5 +60,5 @@ declare function normalizeVercelProductionDeployment(payload: unknown): VercelPr
|
|
|
59
60
|
/** Verify a personal Vercel API token against Vercel; never infer a person from workload OIDC. */
|
|
60
61
|
declare function authenticateVercelUserToken(request: Request, fetcher?: typeof fetch): Promise<string | null>;
|
|
61
62
|
//#endregion
|
|
62
|
-
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, VercelProductionDeployment, VercelQueueSend, VercelQueueSendOptions, VercelWorkQueue, VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
63
|
+
export { type VercelGitHubGitTransportOptions, type VercelGitHubTokenInput, type VercelManagedGitHubApi, type VercelManagedGitHubApiAction, VercelManagedGitHubApiError, type VercelManagedGitHubApiOptions, type VercelManagedGitHubApiSandboxCreateInput, type VercelManagedGitHubGitTransportOptions, type VercelManagedGitHubIssue, type VercelManagedGitHubIssueInput, type VercelManagedGitHubPublication, type VercelManagedGitHubPublicationInput, type VercelManagedGitHubPullRequest, type VercelManagedGitHubPullRequestInput, type VercelManagedGitHubSandboxCreateInput, VercelProductionDeployment, VercelQueueSend, VercelQueueSendOptions, VercelWorkQueue, VercelWorkQueueOptions, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
63
64
|
//# sourceMappingURL=vercel.d.mts.map
|
package/dist/vercel.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { repositorySlugSchema } from "./schema/repository.mjs";
|
|
2
2
|
import { workNotificationSchema } from "./store/work-notifications.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { VercelManagedGitHubApiError, createVercelManagedGitHubApi } from "./vercel-github-api.mjs";
|
|
4
|
+
import { createVercelGitHubGitTransport, createVercelManagedGitHubGitTransport } from "./vercel-git.mjs";
|
|
4
5
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
//#region src/vercel.ts
|
|
@@ -102,6 +103,6 @@ async function authenticateVercelUserToken(request, fetcher = fetch) {
|
|
|
102
103
|
return result.success ? `vercel:${result.data.user.id}` : null;
|
|
103
104
|
}
|
|
104
105
|
//#endregion
|
|
105
|
-
export { authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
106
|
+
export { VercelManagedGitHubApiError, authenticateVercelUserToken, createVercelGitHubGitTransport, createVercelManagedGitHubApi, createVercelManagedGitHubGitTransport, createVercelWorkQueue, normalizeVercelProductionDeployment, verifyVercelSignature };
|
|
106
107
|
|
|
107
108
|
//# sourceMappingURL=vercel.mjs.map
|