Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/connect-oidc-env-token.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions apps/docs/content/docs/4.guide/5.vercel-connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/eve/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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+).

Expand Down
30 changes: 30 additions & 0 deletions packages/github-tools/src/connect/token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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', {
Expand Down
20 changes: 19 additions & 1 deletion packages/github-tools/src/connect/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Loading