From df2d47723177df75d8a63e6040264d49ff8648ce Mon Sep 17 00:00:00 2001 From: Hugo Richard Date: Wed, 26 Aug 2026 10:34:57 +0100 Subject: [PATCH] fix(sdk): pass VERCEL_OIDC_TOKEN into Connect getToken Workflow snapshots have no .vercel directory, so @vercel/oidc's project-root refresh fails on write-scoped mint. Forward the pulled env token as vercelToken instead. --- .changeset/connect-oidc-env-token.md | 5 ++++ .../content/docs/4.guide/5.vercel-connect.md | 2 ++ examples/eve/README.md | 2 +- .../github-tools/src/connect/token.test.ts | 30 +++++++++++++++++++ packages/github-tools/src/connect/token.ts | 20 ++++++++++++- 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 .changeset/connect-oidc-env-token.md diff --git a/.changeset/connect-oidc-env-token.md b/.changeset/connect-oidc-env-token.md new file mode 100644 index 0000000..d7157cf --- /dev/null +++ b/.changeset/connect-oidc-env-token.md @@ -0,0 +1,5 @@ +--- +'@github-tools/sdk': patch +--- + +`connectGithubToken` now passes `VERCEL_OIDC_TOKEN` to Connect as `vercelToken` so local eve/workflow steps do not fail looking for a `.vercel` project root. diff --git a/apps/docs/content/docs/4.guide/5.vercel-connect.md b/apps/docs/content/docs/4.guide/5.vercel-connect.md index a26a1f2..c08b088 100644 --- a/apps/docs/content/docs/4.guide/5.vercel-connect.md +++ b/apps/docs/content/docs/4.guide/5.vercel-connect.md @@ -97,6 +97,8 @@ export default connectGithubTools('github/my-connector', { `connectGithubTools` mints the Connect token **lazily** (inside each tool `execute`). Do not `await getToken(...)` at module top level in `agent/tools/`, that runs at import/build time and fails without the Vercel OIDC header. +When `VERCEL_OIDC_TOKEN` is set (`vercel env pull` for local eve/workflow), `connectGithubToken` passes it to `getToken` as `vercelToken`. That uses the pulled token instead of `@vercel/oidc` walking the filesystem for `.vercel` — which fails inside workflow snapshots that have no project root. + There is no dedicated starter for this path; new agents should use the [eve extension](#eve-extension) above. ## Token provider only diff --git a/examples/eve/README.md b/examples/eve/README.md index 8bcea90..92c5c08 100644 --- a/examples/eve/README.md +++ b/examples/eve/README.md @@ -28,7 +28,7 @@ vercel link # select the github-tools-docs project (or your linked project) vercel env pull ``` -This writes `VERCEL_OIDC_TOKEN` into `.env`. Re-run `vercel env pull` when the token expires. +This writes `VERCEL_OIDC_TOKEN` into `.env`. Re-run `vercel env pull` when the token expires. The SDK forwards that value to Connect as `vercelToken`, so write tools inside workflow steps do not need a `.vercel` directory on disk. Requires Node 24+ and `ai` v7 (peer of `eve` v0.19+). diff --git a/packages/github-tools/src/connect/token.test.ts b/packages/github-tools/src/connect/token.test.ts index 1e0fb2e..333a8a3 100644 --- a/packages/github-tools/src/connect/token.test.ts +++ b/packages/github-tools/src/connect/token.test.ts @@ -24,6 +24,7 @@ function resolveConnectToken( describe('connectGithubToken', () => { beforeEach(() => { getToken.mockClear() + vi.stubEnv('VERCEL_OIDC_TOKEN', '') }) it('calls getToken with app subject and preset-derived scopes', async () => { @@ -101,6 +102,35 @@ describe('connectGithubToken', () => { }, undefined) }) + it('passes VERCEL_OIDC_TOKEN as vercelToken so getToken does not walk cwd', async () => { + vi.stubEnv('VERCEL_OIDC_TOKEN', 'oidc_from_env') + const resolve = resolveConnectToken('github/my-connector', { preset: 'repo-explorer' }) + + await resolve() + expect(getToken).toHaveBeenCalledWith( + 'github/my-connector', + expect.objectContaining({ subject: { type: 'app' } }), + { vercelToken: 'oidc_from_env' }, + ) + vi.unstubAllEnvs() + }) + + it('does not override an explicit connectOptions.vercelToken', async () => { + vi.stubEnv('VERCEL_OIDC_TOKEN', 'oidc_from_env') + const resolve = resolveConnectToken('github/my-connector', { + preset: 'repo-explorer', + connectOptions: { vercelToken: 'explicit_oidc' }, + }) + + await resolve() + expect(getToken).toHaveBeenCalledWith( + 'github/my-connector', + expect.objectContaining({ subject: { type: 'app' } }), + { vercelToken: 'explicit_oidc' }, + ) + vi.unstubAllEnvs() + }) + it('forwards connectOptions to getToken', async () => { const connectOptions = { forceRefresh: true } const resolve = resolveConnectToken('github/my-connector', { diff --git a/packages/github-tools/src/connect/token.ts b/packages/github-tools/src/connect/token.ts index 89f81a8..938ba91 100644 --- a/packages/github-tools/src/connect/token.ts +++ b/packages/github-tools/src/connect/token.ts @@ -20,7 +20,25 @@ export function connectGithubToken( const { connectOptions } = options const tokenParams = resolveGithubConnectTokenParams(options) - return async () => getToken(await resolveGithubConnector(connector), tokenParams, connectOptions) + return async () => getToken( + await resolveGithubConnector(connector), + tokenParams, + resolveConnectOptions(connectOptions), + ) +} + +/** + * Prefer an explicit `vercelToken`, then `VERCEL_OIDC_TOKEN` from the environment. + * Passing the env token into `getToken` skips `@vercel/oidc`'s project-root walk, + * which fails inside eve/workflow snapshots that have no `.vercel` directory. + */ +function resolveConnectOptions(connectOptions?: ConnectOptions): ConnectOptions | undefined { + const vercelToken = connectOptions?.vercelToken ?? process.env.VERCEL_OIDC_TOKEN + if (!vercelToken && !connectOptions) return undefined + return { + ...vercelToken ? { vercelToken } : {}, + ...connectOptions, + } } export type { ConnectOptions }