diff --git a/.changeset/connect-per-call-token.md b/.changeset/connect-per-call-token.md new file mode 100644 index 0000000..f0bb991 --- /dev/null +++ b/.changeset/connect-per-call-token.md @@ -0,0 +1,12 @@ +--- +"@github-tools/sdk": minor +"@github-tools/eve-extension": minor +--- + +Mint Vercel Connect tokens per tool call, for the call's target repository. This covers GitHub Apps installed on several accounts. + +- `connect` on `githubExtension` accepts a `(ctx, call) => params` resolver, where `call` is `{ toolName, input, owner?, repo? }`. `owner` / `repo` are the tool's inputs after `context` defaults, and are undefined for tools without a repository target (search, gists, notifications). The static shape and the `connect.subject` resolver keep working unchanged. +- New `perRepository(params?)` in `@github-tools/sdk/connect`: `connect: perRepository()` mints each token with `authorizationDetails: [{ type: 'github_app_installation', org: owner, repositories: [repo] }]`, and falls back to the static `params` for calls without a repository target. It works with `githubExtension`, `connectGithubTools`, and `connectGithubToken`. Using it from an eve agent requires `@github-tools/sdk` as a direct dependency. +- `connectGithubTools` / `connectGithubToken` accept a `(call) => params` resolver as `connect` / `params`. Scopes still derive from `preset` / `include` / `exclude` unless the resolved params set `scopes`. Connect caches tokens per connector and params, so calls on the same repository reuse one token. +- Token providers (`GithubTokenInput`) now receive an optional `GithubTokenCall` argument on every tool call, from both `createGithubTools` and the eve runtime. Existing `() => Promise` providers are unaffected. +- `CONNECT_INSTALLATION_REQUIRED` names the target account ("The connector's GitHub App is not installed on ") when the token targets an org or repository owner. diff --git a/apps/docs/content/docs/2.frameworks/1.eve-extension.md b/apps/docs/content/docs/2.frameworks/1.eve-extension.md index 767ce7d..2f6f408 100644 --- a/apps/docs/content/docs/2.frameworks/1.eve-extension.md +++ b/apps/docs/content/docs/2.frameworks/1.eve-extension.md @@ -186,7 +186,7 @@ export default githubExtension({ |---|---|---| | `token` | `string \| (() => Promise)` | PAT string, or an async provider for rotating tokens (e.g. a GitHub App installation token) — the same `GithubTokenInput` the SDK accepts; falls back to `GITHUB_TOKEN` when omitted and `connector` is not set | | `connector` | `string \| (() => string \| Promise)` | Vercel Connect connector name, or a resolver to pick one dynamically (e.g. per environment/tenant); takes priority over `token` | -| `connect` | `record?` | Passed through to `getToken` when `connector` is set; `connect.subject` defaults to `{ type: 'app' }` and also accepts a per-caller resolver, see [Per-user tokens](#per-user-tokens) | +| `connect` | `record \| ((ctx, call) => record)` | Passed through to `getToken` when `connector` is set; `connect.subject` defaults to `{ type: 'app' }` and also accepts a per-caller resolver, see [Per-user tokens](#per-user-tokens). A resolver picks params per tool call, see [Multiple GitHub App installations](#multiple-github-app-installations) | | `preset` | preset name or array | `code-review`, `issue-triage`, `ci-ops`, `repo-explorer`, `security-audit`, `release-manager`, `discussion-moderator`, `notification-inbox`, `pr-author`, `maintainer`, see [Presets](/guide/presets) | | `include` | `string[]?` | Tool names to add on top of `preset` (union), or the full set standalone, see [Pick exact tools](#pick-exact-tools) | | `exclude` | `string[]?` | Tool names to remove from the resolved `preset` + `include` set | @@ -266,6 +266,52 @@ export default githubExtension({ `ctx.session.auth.current` is the authenticated caller of the active turn — eve restores it durably across workflow replay, so the resolver stays correct in multi-turn sessions. A caller without an active GitHub connection gets a `UserAuthorizationRequiredError` from Connect instead of silently falling back to the app installation. A static `subject` value (e.g. `{ type: 'user', id }` fixed at mount time) also works when the agent serves a single known user. +### Multiple GitHub App installations + +Connect picks the GitHub App installation from the token's `authorizationDetails`. It checks an explicit `installationId` first, then the `org` (or the owner of a qualified repository), then the connector default. With static `connect` params, every tool call gets a token for one installation. When the App is installed on several accounts, a call to a repository on another account then fails with a 403. Pass `perRepository()` as `connect` to mint each token for the installation that owns the call's target repository: + +```ts [agent/extensions/github.ts] +import githubExtension from '@github-tools/eve-extension' +import { perRepository } from '@github-tools/sdk/connect' + +export default githubExtension({ + connector: 'github/my-connector', + preset: 'pr-author', + context: { owner: 'evloghq', repo: 'evlog' }, + connect: perRepository(), +}) +``` + +`perRepository` is exported by the SDK, so add `@github-tools/sdk` to the agent's dependencies: + +:::code-group +```bash [pnpm] +pnpm add @github-tools/sdk +``` +```bash [npm] +npm install @github-tools/sdk +``` +```bash [yarn] +yarn add @github-tools/sdk +``` +```bash [bun] +bun add @github-tools/sdk +``` +::: + +The target is the tool's `owner` / `repo` input after `context` defaults are applied, so a call that omits them uses the configured home repository. Tools without a repository target (`searchCode`, gist and notification tools) get the static params instead. Pass extra static params to merge them, e.g. `perRepository({ validityBufferMs: 60_000 })`. Scopes still derive from `preset` / `include` / `exclude` unless you set `scopes`. Connect caches tokens per connector and params, so repeated calls on the same repository reuse one token. When the App is not installed on the target account, the tool returns `CONNECT_INSTALLATION_REQUIRED` naming that account instead of a GitHub 403. + +For other per-call rules, pass your own resolver. It receives the eve tool execution context and the call (`{ toolName, input, owner?, repo? }`), and may return a `subject` resolver too: + +```ts [agent/extensions/github.ts] +export default githubExtension({ + connector: 'github/my-connector', + connect: (ctx, call) => ({ + installationId: call.owner === 'hugorcd' ? 'inst_personal' : undefined, + }), +}) +``` + ## Idempotency eve replays completed steps but re-runs steps interrupted mid-execution: 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 aa0e466..ec67891 100644 --- a/apps/docs/content/docs/4.guide/5.vercel-connect.md +++ b/apps/docs/content/docs/4.guide/5.vercel-connect.md @@ -138,6 +138,21 @@ const tools = connectGithubTools('github/my-connector', { `subject` defaults to `{ type: 'app' }`, same as `connectGitHubAdapter` from `@vercel/connect`. +### One App installed on several accounts + +Static `connect` params pin every token to one installation. When the connector's GitHub App is installed on several orgs or users, pass `perRepository()` to mint each token for the installation that owns the tool call's target repository: + +```ts [connect-per-repository.ts] +import { connectGithubTools, perRepository } from '@github-tools/sdk/connect' + +const tools = connectGithubTools('github/my-connector', { + preset: 'pr-author', + connect: perRepository(), +}) +``` + +Calls that target `owner/repo` (after `context` defaults) get `authorizationDetails: [{ type: 'github_app_installation', org: owner, repositories: [repo] }]`. Calls without a repository target get the static params passed to `perRepository({ ... })`. `connect` also accepts any `(call) => params` resolver. Connect caches tokens per connector and params, so repeated calls on one repository reuse the token. For the eve extension, see [Multiple GitHub App installations](/frameworks/eve-extension#multiple-github-app-installations). + ## Per-user tokens The default `{ type: 'app' }` subject mints the project's GitHub App **installation** token — one identity shared by every caller. In multi-user apps where each user connects their own GitHub account (an integrations panel, a personal agent template), that default silently gives every signed-in user the project-level access. Pass a `{ type: 'user' }` subject to mint the caller's own connection token instead: diff --git a/apps/docs/content/docs/4.guide/7.errors.md b/apps/docs/content/docs/4.guide/7.errors.md index a974f1c..82745e1 100644 --- a/apps/docs/content/docs/4.guide/7.errors.md +++ b/apps/docs/content/docs/4.guide/7.errors.md @@ -42,8 +42,8 @@ The SDK maps every failure it can classify to a structured error from an [evlog] | `OIDC_TOKEN_EXPIRED` | `VERCEL_OIDC_TOKEN` is past its `exp` claim — thrown before any request is made, with the exact expiry time. Run `vercel env pull` locally | | `CONNECT_NOT_AUTHORIZED` | Connect rejected the calling process's identity (403). The request never reached GitHub — this is not a GitHub permission problem | | `CONNECT_USER_NOT_CONNECTED` | A `{ type: 'user' }` subject was requested but that user has no active GitHub connection | -| `CONNECT_INSTALLATION_REQUIRED` | The connector's GitHub App is not installed on the target account | -| `SUBJECT_CONTEXT_REQUIRED` | A `connect.subject` resolver ran outside a tool execution (no eve context) | +| `CONNECT_INSTALLATION_REQUIRED` | The connector's GitHub App is not installed on the target account — named in the message when the token targets an `org` or repository owner (e.g. with `perRepository()`) | +| `SUBJECT_CONTEXT_REQUIRED` | A `connect` or `connect.subject` resolver ran outside a tool execution (no eve context) | ### GitHub API diff --git a/apps/docs/content/docs/5.api/2.reference.md b/apps/docs/content/docs/5.api/2.reference.md index bd802fe..47843f2 100644 --- a/apps/docs/content/docs/5.api/2.reference.md +++ b/apps/docs/content/docs/5.api/2.reference.md @@ -55,7 +55,15 @@ type GithubToolsOptions = { coAuthors?: CommitIdentity[] } -type GithubTokenInput = string | (() => Promise) +// Providers receive the tool call the token is requested for (undefined outside a tool call). +type GithubTokenInput = string | ((call?: GithubTokenCall) => Promise) + +type GithubTokenCall = { + toolName: GithubToolName + input: unknown // after context defaults + owner?: string // undefined for tools without a repository target + repo?: string +} type CommitIdentity = { name: string @@ -360,7 +368,7 @@ const tools = connectGithubTools('github/my-connector', { ```ts [types-connect.ts] type ConnectGithubToolsOptions = GithubToolsOptions & { - connect?: GithubConnectParams + connect?: GithubConnectParams | GithubConnectParamsResolver } type GithubConnectParams = Omit & { @@ -368,11 +376,28 @@ type GithubConnectParams = Omit & { repositories?: string[] } +type GithubConnectParamsResolver = (call?: GithubTokenCall) => GithubConnectParams | Promise + type GithubConnectorInput = string | (() => string | Promise) ``` `subject` defaults to `{ type: 'app' }` (the project's GitHub App installation). Pass `{ type: 'user', id }` to mint a token for that user's own connection — see [per-user tokens](/guide/vercel-connect#per-user-tokens). See the [Vercel Connect guide](/guide/vercel-connect#dynamic-connector-selection) for the dynamic connector example. +A `connect` resolver runs on every tool call with its `GithubTokenCall`, so the token can target the call's repository. Scopes still derive from `preset` unless the resolved params set `scopes`. + +## `perRepository(params?)` + +Import from `@github-tools/sdk/connect`. Returns a `connect` resolver that mints each token for the GitHub App installation owning the call's target repository: `{ ...params, authorizationDetails: [{ type: 'github_app_installation', org: owner, repositories: [repo] }] }` when the call has `owner` / `repo`, and `params` unchanged otherwise. Works as `connect` for `connectGithubTools`, `connectGithubToken` (`params`), and [`githubExtension`](/frameworks/eve-extension#multiple-github-app-installations): + +```ts [connect-per-repository.ts] +import { connectGithubTools, perRepository } from '@github-tools/sdk/connect' + +const tools = connectGithubTools('github/my-connector', { + preset: 'pr-author', + connect: perRepository({ validityBufferMs: 60_000 }), +}) +``` + ## `connectGithubTools(connector, options?)`: eve (deprecated) ::callout{icon="i-lucide-alert-triangle"} diff --git a/apps/docs/skills/github-tools-agents/references/eve-extension.md b/apps/docs/skills/github-tools-agents/references/eve-extension.md index 0309267..abbb449 100644 --- a/apps/docs/skills/github-tools-agents/references/eve-extension.md +++ b/apps/docs/skills/github-tools-agents/references/eve-extension.md @@ -86,6 +86,8 @@ export default githubExtension({ }) ``` +When one GitHub App is installed on several accounts, static `connect` params pin every token to one installation, so calls to repos on other accounts 403. Pass `connect: perRepository()` (import from `@github-tools/sdk/connect`; the agent must depend on `@github-tools/sdk`). Each token then targets the installation owning the call's `owner/repo`, after `context` defaults. Tools without a repository target (search, gists, notifications) use the static params passed to `perRepository({ ... })`. `connect` also accepts a custom `(ctx, call) => params` resolver, where `call` is `{ toolName, input, owner?, repo? }`. A missing installation surfaces as `CONNECT_INSTALLATION_REQUIRED` naming the account. + ## Docs - `/frameworks/eve-extension` diff --git a/packages/github-tools-eve-extension/README.md b/packages/github-tools-eve-extension/README.md index 996ba4e..57b3ce5 100644 --- a/packages/github-tools-eve-extension/README.md +++ b/packages/github-tools-eve-extension/README.md @@ -54,6 +54,19 @@ export default githubExtension({ }) ``` +When the connector's GitHub App is installed on several accounts, pass `perRepository()` (from `@github-tools/sdk/connect`, so add `@github-tools/sdk` to the agent) as `connect`. Each token is then minted for the installation that owns the call's target `owner/repo`, after `context` defaults. Tools without a repository target use the static params you pass to `perRepository({ ... })`. If the App is not installed on the target account, the tool returns `CONNECT_INSTALLATION_REQUIRED` naming that account: + +```ts +import { perRepository } from '@github-tools/sdk/connect' + +export default githubExtension({ + connector: 'github/my-connector', + preset: 'pr-author', + context: { owner: 'evloghq', repo: 'evlog' }, + connect: perRepository(), +}) +``` + Tools are exposed to the model as `__`, where `` comes from the mount file's name: `agent/extensions/github.ts` yields `github__listPullRequests`, `github__createIssue`, and so on. To hand-pick an exact set of tools instead of a preset, pass `include`: @@ -90,7 +103,7 @@ extension/ |---|---|---| | `token` | `string \| (() => Promise)` (optional) | PAT string, or an async provider for rotating tokens (e.g. a GitHub App installation token) — the same `GithubTokenInput` the SDK accepts; falls back to `GITHUB_TOKEN` when omitted and `connector` is not set | | `connector` | `string \| (() => string \| Promise)` (optional) | Vercel Connect connector name, or a resolver to pick one dynamically (e.g. per environment/tenant); takes priority over `token` | -| `connect` | `record?` | Passed through to `getToken` when `connector` is set. `connect.subject` defaults to `{ type: 'app' }` (the project's GitHub App installation); pass `{ type: 'user', id }` or a per-caller resolver `(ctx) => subject` to mint each caller's own connection token in multi-user apps | +| `connect` | `record \| ((ctx, call) => record)` (optional) | Passed through to `getToken` when `connector` is set. `connect.subject` defaults to `{ type: 'app' }` (the project's GitHub App installation); pass `{ type: 'user', id }` or a per-caller resolver `(ctx) => subject` to mint each caller's own connection token in multi-user apps. Pass a resolver (e.g. `perRepository()`) to pick params per tool call | | `preset` | preset name or array | `code-review`, `issue-triage`, `ci-ops`, `repo-explorer`, `security-audit`, `release-manager`, `discussion-moderator`, `notification-inbox`, `pr-author`, `maintainer` | | `include` | `string[]?` | Tool names to add on top of `preset` (union), or the full set standalone | | `exclude` | `string[]?` | Tool names to remove from the resolved `preset` + `include` set | diff --git a/packages/github-tools-eve-extension/extension/extension.ts b/packages/github-tools-eve-extension/extension/extension.ts index 63ba887..8826e44 100644 --- a/packages/github-tools-eve-extension/extension/extension.ts +++ b/packages/github-tools-eve-extension/extension/extension.ts @@ -1,4 +1,4 @@ -import type { GithubTokenInput } from '@github-tools/sdk' +import type { GithubTokenCall, GithubTokenInput } from '@github-tools/sdk' import type { ConnectTokenSubject, GithubConnectorInput, GithubConnectParams } from '@github-tools/sdk/connect' import type { ToolContext } from 'eve/tools' import { @@ -49,6 +49,18 @@ export type GithubExtensionConnectParams = Omit subject?: GithubConnectSubjectInput } +/** + * Resolves Connect token params for each tool call. Receives the eve tool + * execution context and the call — `owner` / `repo` are the tool's resolved + * inputs after `context` defaults, undefined for tools without a repository + * target. Use `perRepository()` from `@github-tools/sdk/connect` for the + * common case of selecting the GitHub App installation per target repository. + */ +export type GithubExtensionConnectResolver = ( + ctx: ToolContext, + call: GithubTokenCall, +) => GithubExtensionConnectParams | Promise + /** * Config passed to `githubExtension({ ... })` at the agent mount site. * Declared as an interface (not only a Zod schema) so IDE hovers show JSDoc. @@ -72,8 +84,13 @@ export interface GithubExtensionConfig { * is set. `subject` defaults to `{ type: 'app' }` (the project's GitHub App * installation, shared by every caller); pass a value or a per-caller * resolver to mint per-user tokens instead. + * + * Pass a resolver to pick params per tool call, e.g. `perRepository()` from + * `@github-tools/sdk/connect` when the GitHub App is installed on several + * accounts. Scopes still derive from `preset` / `include` / `exclude` unless + * the resolved params set `scopes`. */ - connect?: GithubExtensionConnectParams + connect?: GithubExtensionConnectParams | GithubExtensionConnectResolver /** Restrict tools to a preset (or array of presets). Prefer a focused preset; omit or use `maintainer` for the full catalog. */ preset?: GithubToolPreset | GithubToolPreset[] /** @@ -130,7 +147,10 @@ const configSchema = z.object({ connector: z.custom( value => typeof value === 'string' || typeof value === 'function', ).optional(), - connect: z.record(z.string(), z.unknown()).optional(), + connect: z.union([ + z.record(z.string(), z.unknown()), + z.custom(value => typeof value === 'function'), + ]).optional(), preset: z.union([presetNameSchema, z.array(presetNameSchema)]).optional(), include: z.array(toolNameSchema).optional(), exclude: z.array(toolNameSchema).optional(), diff --git a/packages/github-tools-eve-extension/extension/tools/github.ts b/packages/github-tools-eve-extension/extension/tools/github.ts index ce113b0..0e3a821 100644 --- a/packages/github-tools-eve-extension/extension/tools/github.ts +++ b/packages/github-tools-eve-extension/extension/tools/github.ts @@ -8,11 +8,11 @@ import { listEveToolDescriptors, mapEveApprovalValue, resolveEveApproval, - resolveGithubToken, type EveApprovalConfig, type EveApprovalValue, type EveGithubToolsOptions, type EveToolOverrides, + type GithubTokenCall, type GithubToolName, type GithubWriteToolName, } from '@github-tools/sdk/eve-runtime' @@ -45,21 +45,23 @@ function buildSessionOptions(ctx?: ToolContext): EveGithubToolsOptions { const includeNames = include as GithubToolName[] | undefined const excludeNames = exclude as GithubToolName[] | undefined - // `connect.subject` may be a per-caller resolver; it needs the execution - // context, so the token is minted lazily, per tool call. + // `connect` and `connect.subject` may be resolvers over the execution + // context and tool call, so params resolve per tool call. const resolvedToken = connector - ? async () => { - const { subject, ...params } = connect ?? {} - const resolvedSubject = typeof subject === 'function' - ? await subject(requireToolContext(ctx)) - : subject - return resolveGithubToken(connectGithubToken(connector, { - preset, - include: includeNames, - exclude: excludeNames, - params: { ...params, ...(resolvedSubject && { subject: resolvedSubject }) }, - })) - } + ? connectGithubToken(connector, { + preset, + include: includeNames, + exclude: excludeNames, + params: async (call) => { + const { subject, ...params } = typeof connect === 'function' + ? await connect(requireToolContext(ctx), requireToolCall(call)) + : connect ?? {} + const resolvedSubject = typeof subject === 'function' + ? await subject(requireToolContext(ctx)) + : subject + return { ...params, ...(resolvedSubject && { subject: resolvedSubject }) } + }, + }) : token return { @@ -102,6 +104,13 @@ function requireToolContext(ctx: ToolContext | undefined): ToolContext { return ctx } +function requireToolCall(call: GithubTokenCall | undefined): GithubTokenCall { + if (!call) { + throw githubToolsErrors.SUBJECT_CONTEXT_REQUIRED() + } + return call +} + async function runGithubEveTool(name: GithubToolName, input: unknown, ctx: ToolContext) { return executeGithubEveTool(name, input as Record, buildSessionOptions(ctx)) } diff --git a/packages/github-tools/README.md b/packages/github-tools/README.md index 8b4c9a7..62405fa 100644 --- a/packages/github-tools/README.md +++ b/packages/github-tools/README.md @@ -334,6 +334,14 @@ connectGithubTools('github/my-connector', { }) ``` +When the GitHub App is installed on several accounts, `connect: perRepository()` mints each token for the installation that owns the tool call's target `owner/repo`. `connect` also accepts any `(call) => params` resolver: + +```ts +import { connectGithubTools, perRepository } from '@github-tools/sdk/connect' + +connectGithubTools('github/my-connector', { preset: 'pr-author', connect: perRepository() }) +``` + > `@vercel/connect` is an optional peer dependency, install it only when using the `/connect` subpath. `connector` accepts a `() => string | Promise` resolver instead of a static name, re-resolved on every call. Useful to pick a connector per environment or tenant: diff --git a/packages/github-tools/src/connect/index.ts b/packages/github-tools/src/connect/index.ts index 9e21a18..a4d7133 100644 --- a/packages/github-tools/src/connect/index.ts +++ b/packages/github-tools/src/connect/index.ts @@ -10,10 +10,15 @@ export { export type { ConnectScopeSelection } from './scopes' export { connectGithubToken } from './token' export { connectGithubTools } from './tools' +export { perRepository } from './per-repository' +export type { PerRepositoryConnectResolver } from './per-repository' export type { ConnectGithubEveToolsOptions, ConnectGithubTokenOptions, ConnectGithubToolsOptions, GithubConnectParams, + GithubConnectParamsInput, + GithubConnectParamsResolver, } from './types' +export type { GithubTokenCall } from '../core/token' export type { ConnectTokenSubject } from '@vercel/connect' diff --git a/packages/github-tools/src/connect/params.ts b/packages/github-tools/src/connect/params.ts index d08baea..63063b8 100644 --- a/packages/github-tools/src/connect/params.ts +++ b/packages/github-tools/src/connect/params.ts @@ -3,7 +3,7 @@ import { connectGithubScopesForSelection } from './scopes' import type { ConnectGithubTokenOptions, GithubConnectParams } from './types' export function resolveGithubConnectTokenParams( - options: ConnectGithubTokenOptions = {}, + options: Omit & { params?: GithubConnectParams } = {}, ): ConnectTokenParams { const { preset, include, exclude, params } = options const scopes = params?.scopes ?? connectGithubScopesForSelection({ preset, include, exclude }) diff --git a/packages/github-tools/src/connect/per-repository.ts b/packages/github-tools/src/connect/per-repository.ts new file mode 100644 index 0000000..3cff905 --- /dev/null +++ b/packages/github-tools/src/connect/per-repository.ts @@ -0,0 +1,38 @@ +import type { GithubTokenCall } from '../core/token' +import type { GithubConnectParams } from './types' + +/** + * Connect params resolver returned by {@link perRepository}. Callable as an + * SDK `connect` resolver (`(call)`) and as an `@github-tools/eve-extension` + * `connect` resolver (`(ctx, call)`). + */ +export type PerRepositoryConnectResolver = { + (call?: GithubTokenCall): GithubConnectParams + (ctx: unknown, call: GithubTokenCall): GithubConnectParams +} + +/** + * Mint each Connect token for the GitHub App installation that owns the tool + * call's target repository. When the call targets `owner/repo`, returns + * `params` with `authorizationDetails: [{ type: 'github_app_installation', org: owner, repositories: [repo] }]`; + * calls without a repository target (search, gists, notifications) get `params` unchanged. + * + * Use it when one GitHub App is installed on several accounts. `params` merges + * extra static Connect params (`scopes`, `validityBufferMs`, `subject`, …). + * + * @example + * ```ts + * githubExtension({ connector: 'github/my-connector', connect: perRepository() }) + * connectGithubTools('github/my-connector', { connect: perRepository({ validityBufferMs: 60_000 }) }) + * ``` + */ +export function perRepository(params: GithubConnectParams = {}): PerRepositoryConnectResolver { + return (...args: [call?: GithubTokenCall] | [ctx: unknown, call: GithubTokenCall]) => { + const call = args.length === 2 ? args[1] : args[0] + if (!call?.owner || !call.repo) return params + return { + ...params, + authorizationDetails: [{ type: 'github_app_installation', org: call.owner, repositories: [call.repo] }], + } + } +} diff --git a/packages/github-tools/src/connect/token.test.ts b/packages/github-tools/src/connect/token.test.ts index 539ed1d..55cb268 100644 --- a/packages/github-tools/src/connect/token.test.ts +++ b/packages/github-tools/src/connect/token.test.ts @@ -25,6 +25,9 @@ vi.mock('@vercel/connect', () => ({ ConnectorInstallationRequiredError, })) +import type { GithubTokenCall } from '../core/token' +import { perRepository } from './per-repository' +import { connectGithubScopesForPreset } from './scopes' import { connectGithubToken } from './token' function fakeJwt(expiresAt: Date): string { @@ -274,6 +277,53 @@ describe('connectGithubToken', () => { }) }) + it('resolves function params on every call and keeps preset-derived scopes', async () => { + const params = vi.fn((call?: GithubTokenCall) => ({ repositories: call?.repo ? [call.repo] : undefined })) + const resolve = resolveConnectToken('github/my-connector', { preset: 'repo-explorer', params }) + + await resolve({ toolName: 'getRepository', input: {}, owner: 'evloghq', repo: 'evlog' }) + await resolve({ toolName: 'getRepository', input: {}, owner: 'hugorcd', repo: 'hr-folio' }) + + expect(params).toHaveBeenCalledTimes(2) + expect(getToken).toHaveBeenLastCalledWith('github/my-connector', { + subject: { type: 'app' }, + scopes: connectGithubScopesForPreset('repo-explorer'), + authorizationDetails: [{ type: 'github_app_installation', repositories: ['hr-folio'] }], + }, undefined) + }) + + it('selects the installation per target repository with perRepository', async () => { + const resolve = resolveConnectToken('github/my-connector', { + preset: 'pr-author', + params: perRepository({ validityBufferMs: 60_000 }), + }) + + await resolve({ toolName: 'createPullRequest', input: {}, owner: 'hugorcd', repo: 'hr-folio' }) + expect(getToken).toHaveBeenLastCalledWith('github/my-connector', { + subject: { type: 'app' }, + validityBufferMs: 60_000, + scopes: connectGithubScopesForPreset('pr-author'), + authorizationDetails: [{ type: 'github_app_installation', org: 'hugorcd', repositories: ['hr-folio'] }], + }, undefined) + + await resolve({ toolName: 'searchCode', input: { query: 'evlog' } }) + expect(getToken).toHaveBeenLastCalledWith('github/my-connector', { + subject: { type: 'app' }, + validityBufferMs: 60_000, + scopes: connectGithubScopesForPreset('pr-author'), + }, undefined) + }) + + it('names the target owner when the App is not installed there', async () => { + getToken.mockRejectedValueOnce(new ConnectorInstallationRequiredError('installation required')) + const resolve = resolveConnectToken('github/my-connector', { preset: 'pr-author', params: perRepository() }) + + await expect(resolve({ toolName: 'createPullRequest', input: {}, owner: 'hugorcd', repo: 'hr-folio' })).rejects.toMatchObject({ + code: 'github_tools.CONNECT_INSTALLATION_REQUIRED', + message: 'The connector\'s GitHub App is not installed on hugorcd: installation required', + }) + }) + it('re-resolves a function connector on every call', async () => { let env = 'preview' const connectorFn = vi.fn(() => `github/${env}-connector`) diff --git a/packages/github-tools/src/connect/token.ts b/packages/github-tools/src/connect/token.ts index f4b7a3c..1306a0d 100644 --- a/packages/github-tools/src/connect/token.ts +++ b/packages/github-tools/src/connect/token.ts @@ -7,7 +7,7 @@ import { type ConnectTokenParams, } from '@vercel/connect' import { githubToolsErrors } from '../core/errors' -import type { GithubTokenInput } from '../core/token' +import type { GithubTokenCall, GithubTokenInput } from '../core/token' import { resolveGithubConnector, type GithubConnectorInput } from './connector' import { resolveGithubConnectTokenParams } from './params' import type { ConnectGithubTokenOptions } from './types' @@ -20,15 +20,21 @@ import type { ConnectGithubTokenOptions } from './types' * `connector` may be a static name or a resolver function — e.g. to pick a * different connector per environment (production vs. preview) or tenant. * It's re-resolved on every call, alongside the token itself. + * + * `params` may be a resolver called with the tool call on every token + * request — e.g. {@link perRepository} to select the GitHub App installation + * that owns the call's target repository. Connect caches tokens per + * `(connector, params)`, so repeated calls on the same repository reuse one token. */ export function connectGithubToken( connector: GithubConnectorInput, options: ConnectGithubTokenOptions = {}, ): GithubTokenInput { const { connectOptions } = options - const tokenParams = resolveGithubConnectTokenParams(options) + const tokenParamsFor = createTokenParamsResolver(options) - return async () => { + return async (call) => { + const tokenParams = await tokenParamsFor(call) try { return await getToken( await resolveGithubConnector(connector), @@ -37,23 +43,52 @@ export function connectGithubToken( ) } catch (error) { - throw toConnectCatalogError(error, tokenParams) + throw toConnectCatalogError(error, tokenParams, call) } } } +/** + * Static params resolve once; a params resolver runs on every call. Both go + * through the same scope derivation, so per-call params keep the + * preset/include/exclude scope narrowing unless they set `scopes` explicitly. + */ +function createTokenParamsResolver( + options: ConnectGithubTokenOptions, +): (call?: GithubTokenCall) => Promise { + const { params } = options + if (typeof params === 'function') { + return async call => resolveGithubConnectTokenParams({ ...options, params: await params(call) }) + } + const tokenParams = resolveGithubConnectTokenParams({ ...options, params }) + return async () => tokenParams +} + +/** Account Connect selects the installation for: the detail's `org`, else the owner of a qualified repository. */ +function installationTarget(tokenParams: ConnectTokenParams): string | undefined { + for (const detail of tokenParams.authorizationDetails ?? []) { + if (detail.type !== 'github_app_installation') continue + if (typeof detail.org === 'string') return detail.org + const repositories = [detail.repositories].flat() + const qualified = repositories.find((repository): repository is string => typeof repository === 'string' && repository.includes('/')) + if (qualified) return qualified.split('/')[0] + } + return undefined +} + /** * Map @vercel/connect failures to catalog errors so models get the actual * cause (process identity, missing user connection, missing installation) * instead of a bare "Not authorized" they misread as GitHub permissions. */ -function toConnectCatalogError(error: unknown, tokenParams: ConnectTokenParams): unknown { +function toConnectCatalogError(error: unknown, tokenParams: ConnectTokenParams, call: GithubTokenCall | undefined): unknown { if (error instanceof UserAuthorizationRequiredError) { const subjectId = tokenParams.subject.type === 'user' ? tokenParams.subject.id : tokenParams.subject.type return githubToolsErrors.CONNECT_USER_NOT_CONNECTED({ subjectId, cause: error }) } if (error instanceof ConnectorInstallationRequiredError) { - return githubToolsErrors.CONNECT_INSTALLATION_REQUIRED({ detail: error.message, cause: error }) + const owner = installationTarget(tokenParams) ?? call?.owner + return githubToolsErrors.CONNECT_INSTALLATION_REQUIRED({ detail: error.message, ...(owner && { owner }), cause: error }) } if (error instanceof ConnectError && error.status === 403) { return githubToolsErrors.CONNECT_NOT_AUTHORIZED({ detail: error.message, cause: error }) diff --git a/packages/github-tools/src/connect/types.ts b/packages/github-tools/src/connect/types.ts index d4584d5..5dc0322 100644 --- a/packages/github-tools/src/connect/types.ts +++ b/packages/github-tools/src/connect/types.ts @@ -1,5 +1,6 @@ import type { ConnectOptions, ConnectTokenParams } from '@vercel/connect' import type { GithubToolPreset } from '../core/presets' +import type { GithubTokenCall } from '../core/token' import type { GithubToolName } from '../core/tool-names' import type { GithubToolsBaseOptions } from '../core/tool-types' import type { EveGithubToolsOptions } from '../eve/types' @@ -22,13 +23,27 @@ export type GithubConnectParams = Omit & { repositories?: string[] } +/** + * Resolves Connect token params for each tool call, e.g. to select the GitHub + * App installation that owns the call's target repository (see + * {@link perRepository}). `call` is undefined when the token is resolved + * outside a tool call. Unless the returned params set `scopes`, scopes are + * still derived from `preset` / `include` / `exclude`. + */ +export type GithubConnectParamsResolver = ( + call?: GithubTokenCall, +) => GithubConnectParams | Promise + +/** Static Connect token params, or a per-call {@link GithubConnectParamsResolver}. */ +export type GithubConnectParamsInput = GithubConnectParams | GithubConnectParamsResolver + export type ConnectGithubToolsOptions = GithubToolsBaseOptions & { preset?: GithubToolPreset | GithubToolPreset[] - connect?: GithubConnectParams + connect?: GithubConnectParamsInput } export type ConnectGithubEveToolsOptions = Omit & { - connect?: GithubConnectParams + connect?: GithubConnectParamsInput } export type ConnectGithubTokenOptions = { @@ -37,6 +52,6 @@ export type ConnectGithubTokenOptions = { include?: readonly GithubToolName[] /** Same deny-list semantics as eve `exclude` — scopes derive from the resolved tools when set. */ exclude?: readonly GithubToolName[] - params?: GithubConnectParams + params?: GithubConnectParamsInput connectOptions?: ConnectOptions } diff --git a/packages/github-tools/src/core/catalog.ts b/packages/github-tools/src/core/catalog.ts index 8c95bc8..a6e1e0d 100644 --- a/packages/github-tools/src/core/catalog.ts +++ b/packages/github-tools/src/core/catalog.ts @@ -1,4 +1,4 @@ -import type { z } from 'zod' +import { z } from 'zod' import * as bundles from './bundles' import * as checks from './checks' import * as commits from './commits' @@ -710,3 +710,13 @@ export const GITHUB_WRITE_TOOL_NAMES = ALL_GITHUB_TOOL_NAMES.filter( export function isGithubWriteToolName(name: GithubToolName): name is GithubWriteToolName { return 'write' in GITHUB_TOOL_CATALOG[name] } + +const REPOSITORY_TARGET_TOOL_NAMES = new Set(ALL_GITHUB_TOOL_NAMES.filter((name) => { + const schema: z.ZodType = GITHUB_TOOL_CATALOG[name].inputSchema + return schema instanceof z.ZodObject && 'owner' in schema.shape && 'repo' in schema.shape +})) + +/** Whether a tool's input names a target repository (`owner` + `repo`). */ +export function hasRepositoryTarget(name: GithubToolName): boolean { + return REPOSITORY_TARGET_TOOL_NAMES.has(name) +} diff --git a/packages/github-tools/src/core/errors.ts b/packages/github-tools/src/core/errors.ts index a6a7106..9932b56 100644 --- a/packages/github-tools/src/core/errors.ts +++ b/packages/github-tools/src/core/errors.ts @@ -42,17 +42,19 @@ export const githubToolsErrors = defineErrorCatalog('github_tools', { }, CONNECT_INSTALLATION_REQUIRED: { status: 401, - message: ({ detail }: { detail: string }) => - `The Connect connector has no usable GitHub App installation: ${detail}`, - why: 'The connector exists but its GitHub App is not installed on the target org or user account.', - fix: 'Install the connector\'s GitHub App on the org or account the agent needs, from the Vercel Connect dashboard.', + message: ({ detail, owner }: { detail: string, owner?: string }) => + owner + ? `The connector's GitHub App is not installed on ${owner}: ${detail}` + : `The Connect connector has no usable GitHub App installation: ${detail}`, + why: 'The connector exists but its GitHub App is not installed on the target org or user account, so Connect cannot mint a token for it.', + fix: 'Install the connector\'s GitHub App on the org or account named in the message (from the Vercel Connect dashboard), or target a repository on an account where it is installed.', link: 'https://github-tools.com/guide/vercel-connect#create-a-github-connector', }, SUBJECT_CONTEXT_REQUIRED: { status: 500, - message: 'connect.subject resolver needs the tool execution context — it is only available while a tool call executes.', - why: 'The per-caller subject resolver was invoked without an eve ToolContext, which only exists during tool execution.', - fix: 'Keep `connect.subject` resolution on the tool execute path; use a static subject when no execution context is available.', + message: 'A `connect` or `connect.subject` resolver needs the tool execution context — it is only available while a tool call executes.', + why: 'A per-call Connect resolver was invoked without an eve ToolContext and tool call, which only exist during tool execution.', + fix: 'Keep Connect resolver calls on the tool execute path; use static `connect` params when no execution context is available.', link: 'https://github-tools.com/frameworks/eve-extension#per-user-tokens', }, UNAUTHORIZED: { diff --git a/packages/github-tools/src/core/token.ts b/packages/github-tools/src/core/token.ts index 3580ecd..e343577 100644 --- a/packages/github-tools/src/core/token.ts +++ b/packages/github-tools/src/core/token.ts @@ -1,19 +1,57 @@ +import { hasRepositoryTarget } from './catalog' import { githubToolsErrors } from './errors' +import type { GithubToolName } from './tool-names' -export type GithubTokenInput = string | (() => Promise) -export type GithubTokenResolver = () => Promise +/** + * The tool call a token is requested for. Passed to token providers so they + * can mint a token for the call's target — e.g. the GitHub App installation + * that owns `owner/repo`. + */ +export type GithubTokenCall = { + toolName: GithubToolName + /** Tool input after context defaults are applied. */ + input: unknown + /** Target repository owner. Undefined for tools without a repository target (search, gists, notifications). */ + owner?: string + /** Target repository name. Undefined for tools without a repository target. */ + repo?: string +} + +/** + * A token string, or an async provider. Providers receive the tool call the + * token is requested for; `call` is undefined when the token is resolved + * outside a tool call. + */ +export type GithubTokenInput = string | ((call?: GithubTokenCall) => Promise) +export type GithubTokenResolver = (call?: GithubTokenCall) => Promise + +/** + * Build the {@link GithubTokenCall} for a tool from its resolved input. + * Context merging also adds `owner` / `repo` to tools without those inputs, + * so they only count as a target when the tool's schema declares them. + */ +export function githubTokenCall(toolName: GithubToolName, input: Record): GithubTokenCall { + if (!hasRepositoryTarget(toolName)) return { toolName, input } + const { owner, repo } = input + return { + toolName, + input, + ...(typeof owner === 'string' && { owner }), + ...(typeof repo === 'string' && { repo }), + } +} /** * Normalizes a token string, async token provider, or `process.env.GITHUB_TOKEN` - * into a `() => Promise` resolver. + * into a `(call?) => Promise` resolver. * * Throws immediately for static inputs when no token is available. Provider * functions are validated lazily, each time the resolver is invoked. */ export function createGithubTokenResolver(token?: GithubTokenInput): GithubTokenResolver { if (typeof token === 'function') { - return async () => { - const resolvedToken = await token() + return async (call) => { + const resolvedToken = await token(call) if (!resolvedToken) { throw githubToolsErrors.TOKEN_REQUIRED() } @@ -28,7 +66,7 @@ export function createGithubTokenResolver(token?: GithubTokenInput): GithubToken return async () => resolvedToken } -/** Resolves a {@link GithubTokenInput} to a token string. */ -export function resolveGithubToken(token?: GithubTokenInput): Promise { - return createGithubTokenResolver(token)() +/** Resolves a {@link GithubTokenInput} to a token string for an optional tool call. */ +export function resolveGithubToken(token?: GithubTokenInput, call?: GithubTokenCall): Promise { + return createGithubTokenResolver(token)(call) } diff --git a/packages/github-tools/src/eve-runtime.ts b/packages/github-tools/src/eve-runtime.ts index 3e1b760..9661849 100644 --- a/packages/github-tools/src/eve-runtime.ts +++ b/packages/github-tools/src/eve-runtime.ts @@ -17,6 +17,7 @@ export { } from './eve/build' export { mapEveApprovalValue, resolveEveApproval, resolveEveToolApproval, isEveApprovalDisabled } from './eve/approval' export { resolveGithubToken } from './core/token' +export type { GithubTokenCall } from './core/token' export { githubToolsErrors } from './core/errors' export type { EveApprovalConfig, diff --git a/packages/github-tools/src/eve/steps.ts b/packages/github-tools/src/eve/steps.ts index 78a9576..5f1adaa 100644 --- a/packages/github-tools/src/eve/steps.ts +++ b/packages/github-tools/src/eve/steps.ts @@ -1,5 +1,6 @@ +import { mergeContextArgs } from '../core/context' import { EvlogError, toModelErrorPayload } from '../core/errors' -import { resolveGithubToken } from '../core/token' +import { githubTokenCall, resolveGithubToken } from '../core/token' import { createToolRegistry, type GithubToolName, type ToolBuildContext } from './registry' async function executeGithubToolStep( @@ -22,7 +23,8 @@ export async function runGithubToolStep( ) { try { // Resolve the token before entering the step so only a serializable string crosses the boundary. - const token = await resolveGithubToken(ctx.token) + const call = githubTokenCall(name, mergeContextArgs(input, ctx.context ?? {})) + const token = await resolveGithubToken(ctx.token, call) return await executeGithubToolStep(name, input, { ...ctx, token }) } catch (error) { // Eve's tool-loop logs thrown execute errors but does not always append a diff --git a/packages/github-tools/src/index.ts b/packages/github-tools/src/index.ts index 6717fb5..47d0e01 100644 --- a/packages/github-tools/src/index.ts +++ b/packages/github-tools/src/index.ts @@ -256,7 +256,7 @@ export { getPullRequestContext, getIssueContext, getReleaseContext, getCiFailure export type { GithubRateLimit } from './core/rate-limit' export { githubToolsErrors } from './core/errors' export type { CommitIdentity, CommitToolOptions, GithubTool, Octokit, ToolOptions, ToolOverrides } from './types' -export type { GithubTokenInput } from './core/token' +export type { GithubTokenCall, GithubTokenInput } from './core/token' export { resolveGithubToken } from './core/token' export { createGithubAgent } from './agents' export type { CreateGithubAgentOptions } from './agents' diff --git a/packages/github-tools/src/token-call.test.ts b/packages/github-tools/src/token-call.test.ts new file mode 100644 index 0000000..acbbfa8 --- /dev/null +++ b/packages/github-tools/src/token-call.test.ts @@ -0,0 +1,77 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { perRepository } from './connect/per-repository' +import * as repositoryCore from './core/repository' +import * as searchCore from './core/search' +import { githubTokenCall } from './core/token' +import { runGithubToolStep } from './eve/steps' +import { createGithubTools } from './index' + +const toolOptions = { toolCallId: 'call_1', messages: [], context: {} } + +afterEach(() => { + vi.restoreAllMocks() +}) + +describe('githubTokenCall', () => { + it('reads owner and repo from string inputs only', () => { + expect(githubTokenCall('getRepository', { owner: 'evloghq', repo: 'evlog' })) + .toEqual({ toolName: 'getRepository', input: { owner: 'evloghq', repo: 'evlog' }, owner: 'evloghq', repo: 'evlog' }) + expect(githubTokenCall('searchCode', { query: 'evlog', perPage: 10 })) + .toEqual({ toolName: 'searchCode', input: { query: 'evlog', perPage: 10 } }) + }) + + it('ignores context-merged owner and repo on tools without a repository target', () => { + expect(githubTokenCall('listNotifications', { owner: 'evloghq', repo: 'evlog', all: false })) + .toEqual({ toolName: 'listNotifications', input: { owner: 'evloghq', repo: 'evlog', all: false } }) + }) +}) + +describe('token call threading', () => { + it('passes the context-defaulted repository to the AI SDK token provider', async () => { + vi.spyOn(repositoryCore, 'getRepositoryCore').mockResolvedValue({} as never) + const token = vi.fn(async () => 'ghp_test') + const tools = createGithubTools({ token, context: { owner: 'evloghq', repo: 'evlog' } }) + + await tools.getRepository.execute!({}, toolOptions) + await tools.getRepository.execute!({ owner: 'hugorcd', repo: 'hr-folio' }, toolOptions) + + expect(token).toHaveBeenNthCalledWith(1, expect.objectContaining({ toolName: 'getRepository', owner: 'evloghq', repo: 'evlog' })) + expect(token).toHaveBeenNthCalledWith(2, expect.objectContaining({ toolName: 'getRepository', owner: 'hugorcd', repo: 'hr-folio' })) + }) + + it('passes no repository for tools without a repository target', async () => { + vi.spyOn(searchCore, 'searchCodeCore').mockResolvedValue({} as never) + const token = vi.fn(async () => 'ghp_test') + const tools = createGithubTools({ token, context: { owner: 'evloghq', repo: 'evlog' } }) + + await tools.searchCode.execute!({ query: 'evlog', perPage: 10 }, toolOptions) + + expect(token).toHaveBeenCalledWith({ toolName: 'searchCode', input: expect.objectContaining({ query: 'evlog' }) }) + }) + + it('passes the context-defaulted repository to the eve token provider', async () => { + vi.spyOn(repositoryCore, 'getRepositoryCore').mockResolvedValue({} as never) + const token = vi.fn(async () => 'ghp_test') + + await runGithubToolStep('getRepository', {}, { token, context: { owner: 'evloghq', repo: 'evlog' } }) + + expect(token).toHaveBeenCalledWith(expect.objectContaining({ toolName: 'getRepository', owner: 'evloghq', repo: 'evlog' })) + }) +}) + +describe('perRepository', () => { + const call = { toolName: 'createPullRequest', input: {}, owner: 'hugorcd', repo: 'hr-folio' } as const + + it('reads the call from the eve extension (ctx, call) form', () => { + expect(perRepository({ scopes: ['pull_requests:write'] })({ session: {} }, call)).toEqual({ + scopes: ['pull_requests:write'], + authorizationDetails: [{ type: 'github_app_installation', org: 'hugorcd', repositories: ['hr-folio'] }], + }) + }) + + it('returns the static params when the call has no repository target', () => { + const params = { installationId: 'inst_default' } + expect(perRepository(params)()).toBe(params) + expect(perRepository(params)({ toolName: 'listNotifications', input: {} })).toBe(params) + }) +}) diff --git a/packages/github-tools/src/tools/bundles.ts b/packages/github-tools/src/tools/bundles.ts index df9a486..8b0b517 100644 --- a/packages/github-tools/src/tools/bundles.ts +++ b/packages/github-tools/src/tools/bundles.ts @@ -14,7 +14,7 @@ import { getCiFailureContextCore, } from '../core/bundles' import { getPullRequestContextToModelOutput } from '../core/model-output' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { GithubTool } from '../types' async function getPullRequestContextStep(args: Parameters[0]) { @@ -28,7 +28,7 @@ export const getPullRequestContext = (token: GithubTokenInput): GithubTool => description: getPullRequestContextDescription, inputSchema: getPullRequestContextInputSchema, toModelOutput: getPullRequestContextToModelOutput, - execute: async args => getPullRequestContextStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getPullRequestContextStep({ token: await resolveGithubToken(token, githubTokenCall('getPullRequestContext', args)), ...args }), }) async function getIssueContextStep(args: Parameters[0]) { @@ -41,7 +41,7 @@ export const getIssueContext = (token: GithubTokenInput): GithubTool => tool({ description: getIssueContextDescription, inputSchema: getIssueContextInputSchema, - execute: async args => getIssueContextStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getIssueContextStep({ token: await resolveGithubToken(token, githubTokenCall('getIssueContext', args)), ...args }), }) async function getReleaseContextStep(args: Parameters[0]) { @@ -54,7 +54,7 @@ export const getReleaseContext = (token: GithubTokenInput): GithubTool => tool({ description: getReleaseContextDescription, inputSchema: getReleaseContextInputSchema, - execute: async args => getReleaseContextStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getReleaseContextStep({ token: await resolveGithubToken(token, githubTokenCall('getReleaseContext', args)), ...args }), }) async function getCiFailureContextStep(args: Parameters[0]) { @@ -67,5 +67,5 @@ export const getCiFailureContext = (token: GithubTokenInput): GithubTool => tool({ description: getCiFailureContextDescription, inputSchema: getCiFailureContextInputSchema, - execute: async args => getCiFailureContextStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getCiFailureContextStep({ token: await resolveGithubToken(token, githubTokenCall('getCiFailureContext', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/checks.ts b/packages/github-tools/src/tools/checks.ts index cf497eb..a090e84 100644 --- a/packages/github-tools/src/tools/checks.ts +++ b/packages/github-tools/src/tools/checks.ts @@ -7,7 +7,7 @@ import { getCombinedStatusDescription, getCombinedStatusCore, } from '../core/checks' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { GithubTool } from '../types' async function listCheckRunsStep(args: Parameters[0]) { @@ -20,7 +20,7 @@ export const listCheckRuns = (token: GithubTokenInput): GithubTool => tool({ description: listCheckRunsDescription, inputSchema: listCheckRunsInputSchema, - execute: async args => listCheckRunsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listCheckRunsStep({ token: await resolveGithubToken(token, githubTokenCall('listCheckRuns', args)), ...args }), }) async function getCombinedStatusStep(args: Parameters[0]) { @@ -33,5 +33,5 @@ export const getCombinedStatus = (token: GithubTokenInput): GithubTool => tool({ description: getCombinedStatusDescription, inputSchema: getCombinedStatusInputSchema, - execute: async args => getCombinedStatusStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getCombinedStatusStep({ token: await resolveGithubToken(token, githubTokenCall('getCombinedStatus', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/commits.ts b/packages/github-tools/src/tools/commits.ts index 7dba9af..f4b9335 100644 --- a/packages/github-tools/src/tools/commits.ts +++ b/packages/github-tools/src/tools/commits.ts @@ -1,5 +1,5 @@ import { tool } from 'ai' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { GithubTool } from '../types' import { listCommitsInputSchema, @@ -27,7 +27,7 @@ export const listCommits = (token: GithubTokenInput): GithubTool => tool({ description: listCommitsDescription, inputSchema: listCommitsInputSchema, - execute: async args => listCommitsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listCommitsStep({ token: await resolveGithubToken(token, githubTokenCall('listCommits', args)), ...args }), }) async function getCommitStep(args: Parameters[0]) { @@ -41,7 +41,7 @@ export const getCommit = (token: GithubTokenInput): GithubTool => description: getCommitDescription, inputSchema: getCommitInputSchema, toModelOutput: getCommitToModelOutput, - execute: async args => getCommitStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getCommitStep({ token: await resolveGithubToken(token, githubTokenCall('getCommit', args)), ...args }), }) async function getBlameStep(args: Parameters[0]) { @@ -54,7 +54,7 @@ export const getBlame = (token: GithubTokenInput): GithubTool => tool({ description: getBlameDescription, inputSchema: getBlameInputSchema, - execute: async args => getBlameStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getBlameStep({ token: await resolveGithubToken(token, githubTokenCall('getBlame', args)), ...args }), }) async function compareCommitsStep(args: Parameters[0]) { @@ -68,5 +68,5 @@ export const compareCommits = (token: GithubTokenInput): GithubTool => description: compareCommitsDescription, inputSchema: compareCommitsInputSchema, toModelOutput: compareCommitsToModelOutput, - execute: async args => compareCommitsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => compareCommitsStep({ token: await resolveGithubToken(token, githubTokenCall('compareCommits', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/discussions.ts b/packages/github-tools/src/tools/discussions.ts index 8c56002..5629b8d 100644 --- a/packages/github-tools/src/tools/discussions.ts +++ b/packages/github-tools/src/tools/discussions.ts @@ -10,7 +10,7 @@ import { addDiscussionCommentDescription, addDiscussionCommentCore, } from '../core/discussions' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listDiscussionsStep(args: Parameters[0]) { @@ -23,7 +23,7 @@ export const listDiscussions = (token: GithubTokenInput): GithubTool => tool({ description: listDiscussionsDescription, inputSchema: listDiscussionsInputSchema, - execute: async args => listDiscussionsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listDiscussionsStep({ token: await resolveGithubToken(token, githubTokenCall('listDiscussions', args)), ...args }), }) async function getDiscussionStep(args: Parameters[0]) { @@ -36,7 +36,7 @@ export const getDiscussion = (token: GithubTokenInput): GithubTool => tool({ description: getDiscussionDescription, inputSchema: getDiscussionInputSchema, - execute: async args => getDiscussionStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getDiscussionStep({ token: await resolveGithubToken(token, githubTokenCall('getDiscussion', args)), ...args }), }) async function addDiscussionCommentStep(args: Parameters[0]) { @@ -50,5 +50,5 @@ export const addDiscussionComment = (token: GithubTokenInput, { needsApproval = description: addDiscussionCommentDescription, needsApproval, inputSchema: addDiscussionCommentInputSchema, - execute: async args => addDiscussionCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addDiscussionCommentStep({ token: await resolveGithubToken(token, githubTokenCall('addDiscussionComment', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/gists.ts b/packages/github-tools/src/tools/gists.ts index 676a387..fefa8fb 100644 --- a/packages/github-tools/src/tools/gists.ts +++ b/packages/github-tools/src/tools/gists.ts @@ -22,7 +22,7 @@ import { createGistCommentDescription, createGistCommentCore, } from '../core/gists' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listGistsStep(args: Parameters[0]) { @@ -35,7 +35,7 @@ export const listGists = (token: GithubTokenInput): GithubTool => tool({ description: listGistsDescription, inputSchema: listGistsInputSchema, - execute: async args => listGistsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listGistsStep({ token: await resolveGithubToken(token, githubTokenCall('listGists', args)), ...args }), }) async function getGistStep(args: Parameters[0]) { @@ -48,7 +48,7 @@ export const getGist = (token: GithubTokenInput): GithubTool => tool({ description: getGistDescription, inputSchema: getGistInputSchema, - execute: async args => getGistStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getGistStep({ token: await resolveGithubToken(token, githubTokenCall('getGist', args)), ...args }), }) async function listGistCommentsStep(args: Parameters[0]) { @@ -61,7 +61,7 @@ export const listGistComments = (token: GithubTokenInput): GithubTool => tool({ description: listGistCommentsDescription, inputSchema: listGistCommentsInputSchema, - execute: async args => listGistCommentsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listGistCommentsStep({ token: await resolveGithubToken(token, githubTokenCall('listGistComments', args)), ...args }), }) async function createGistStep(args: Parameters[0]) { @@ -75,7 +75,7 @@ export const createGist = (token: GithubTokenInput, { needsApproval = true }: To description: createGistDescription, needsApproval, inputSchema: createGistInputSchema, - execute: async args => createGistStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createGistStep({ token: await resolveGithubToken(token, githubTokenCall('createGist', args)), ...args }), }) async function updateGistStep(args: Parameters[0]) { @@ -89,7 +89,7 @@ export const updateGist = (token: GithubTokenInput, { needsApproval = true }: To description: updateGistDescription, needsApproval, inputSchema: updateGistInputSchema, - execute: async args => updateGistStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updateGistStep({ token: await resolveGithubToken(token, githubTokenCall('updateGist', args)), ...args }), }) async function deleteGistStep(args: Parameters[0]) { @@ -103,7 +103,7 @@ export const deleteGist = (token: GithubTokenInput, { needsApproval = true }: To description: deleteGistDescription, needsApproval, inputSchema: deleteGistInputSchema, - execute: async args => deleteGistStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deleteGistStep({ token: await resolveGithubToken(token, githubTokenCall('deleteGist', args)), ...args }), }) async function createGistCommentStep(args: Parameters[0]) { @@ -117,5 +117,5 @@ export const createGistComment = (token: GithubTokenInput, { needsApproval = tru description: createGistCommentDescription, needsApproval, inputSchema: createGistCommentInputSchema, - execute: async args => createGistCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createGistCommentStep({ token: await resolveGithubToken(token, githubTokenCall('createGistComment', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/issues.ts b/packages/github-tools/src/tools/issues.ts index a425e67..1836e24 100644 --- a/packages/github-tools/src/tools/issues.ts +++ b/packages/github-tools/src/tools/issues.ts @@ -52,7 +52,7 @@ import { removeAssigneesDescription, removeAssigneesCore, } from '../core/issues' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listIssuesStep(args: Parameters[0]) { @@ -65,7 +65,7 @@ export const listIssues = (token: GithubTokenInput): GithubTool => tool({ description: listIssuesDescription, inputSchema: listIssuesInputSchema, - execute: async args => listIssuesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listIssuesStep({ token: await resolveGithubToken(token, githubTokenCall('listIssues', args)), ...args }), }) async function getIssueStep(args: Parameters[0]) { @@ -78,7 +78,7 @@ export const getIssue = (token: GithubTokenInput): GithubTool => tool({ description: getIssueDescription, inputSchema: getIssueInputSchema, - execute: async args => getIssueStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getIssueStep({ token: await resolveGithubToken(token, githubTokenCall('getIssue', args)), ...args }), }) async function listIssueCommentsStep(args: Parameters[0]) { @@ -91,7 +91,7 @@ export const listIssueComments = (token: GithubTokenInput): GithubTool => tool({ description: listIssueCommentsDescription, inputSchema: listIssueCommentsInputSchema, - execute: async args => listIssueCommentsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listIssueCommentsStep({ token: await resolveGithubToken(token, githubTokenCall('listIssueComments', args)), ...args }), }) async function createIssueStep(args: Parameters[0]) { @@ -105,7 +105,7 @@ export const createIssue = (token: GithubTokenInput, { needsApproval = true }: T description: createIssueDescription, needsApproval, inputSchema: createIssueInputSchema, - execute: async args => createIssueStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createIssueStep({ token: await resolveGithubToken(token, githubTokenCall('createIssue', args)), ...args }), }) async function addIssueCommentStep(args: Parameters[0]) { @@ -119,7 +119,7 @@ export const addIssueComment = (token: GithubTokenInput, { needsApproval = true description: addIssueCommentDescription, needsApproval, inputSchema: addIssueCommentInputSchema, - execute: async args => addIssueCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addIssueCommentStep({ token: await resolveGithubToken(token, githubTokenCall('addIssueComment', args)), ...args }), }) async function closeIssueStep(args: Parameters[0]) { @@ -133,7 +133,7 @@ export const closeIssue = (token: GithubTokenInput, { needsApproval = true }: To description: closeIssueDescription, needsApproval, inputSchema: closeIssueInputSchema, - execute: async args => closeIssueStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => closeIssueStep({ token: await resolveGithubToken(token, githubTokenCall('closeIssue', args)), ...args }), }) async function updateIssueStep(args: Parameters[0]) { @@ -147,7 +147,7 @@ export const updateIssue = (token: GithubTokenInput, { needsApproval = true }: T description: updateIssueDescription, needsApproval, inputSchema: updateIssueInputSchema, - execute: async args => updateIssueStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updateIssueStep({ token: await resolveGithubToken(token, githubTokenCall('updateIssue', args)), ...args }), }) async function updateIssueCommentStep(args: Parameters[0]) { @@ -161,7 +161,7 @@ export const updateIssueComment = (token: GithubTokenInput, { needsApproval = tr description: updateIssueCommentDescription, needsApproval, inputSchema: updateIssueCommentInputSchema, - execute: async args => updateIssueCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updateIssueCommentStep({ token: await resolveGithubToken(token, githubTokenCall('updateIssueComment', args)), ...args }), }) async function deleteIssueCommentStep(args: Parameters[0]) { @@ -175,7 +175,7 @@ export const deleteIssueComment = (token: GithubTokenInput, { needsApproval = tr description: deleteIssueCommentDescription, needsApproval, inputSchema: deleteIssueCommentInputSchema, - execute: async args => deleteIssueCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deleteIssueCommentStep({ token: await resolveGithubToken(token, githubTokenCall('deleteIssueComment', args)), ...args }), }) async function listLabelsStep(args: Parameters[0]) { @@ -188,7 +188,7 @@ export const listLabels = (token: GithubTokenInput): GithubTool => tool({ description: listLabelsDescription, inputSchema: listLabelsInputSchema, - execute: async args => listLabelsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listLabelsStep({ token: await resolveGithubToken(token, githubTokenCall('listLabels', args)), ...args }), }) async function addLabelsStep(args: Parameters[0]) { @@ -202,7 +202,7 @@ export const addLabels = (token: GithubTokenInput, { needsApproval = true }: Too description: addLabelsDescription, needsApproval, inputSchema: addLabelsInputSchema, - execute: async args => addLabelsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addLabelsStep({ token: await resolveGithubToken(token, githubTokenCall('addLabels', args)), ...args }), }) async function removeLabelStep(args: Parameters[0]) { @@ -216,7 +216,7 @@ export const removeLabel = (token: GithubTokenInput, { needsApproval = true }: T description: removeLabelDescription, needsApproval, inputSchema: removeLabelInputSchema, - execute: async args => removeLabelStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => removeLabelStep({ token: await resolveGithubToken(token, githubTokenCall('removeLabel', args)), ...args }), }) async function createLabelStep(args: Parameters[0]) { @@ -230,7 +230,7 @@ export const createLabel = (token: GithubTokenInput, { needsApproval = true }: T description: createLabelDescription, needsApproval, inputSchema: createLabelInputSchema, - execute: async args => createLabelStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createLabelStep({ token: await resolveGithubToken(token, githubTokenCall('createLabel', args)), ...args }), }) async function updateLabelStep(args: Parameters[0]) { @@ -244,7 +244,7 @@ export const updateLabel = (token: GithubTokenInput, { needsApproval = true }: T description: updateLabelDescription, needsApproval, inputSchema: updateLabelInputSchema, - execute: async args => updateLabelStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updateLabelStep({ token: await resolveGithubToken(token, githubTokenCall('updateLabel', args)), ...args }), }) async function deleteLabelStep(args: Parameters[0]) { @@ -258,7 +258,7 @@ export const deleteLabel = (token: GithubTokenInput, { needsApproval = true }: T description: deleteLabelDescription, needsApproval, inputSchema: deleteLabelInputSchema, - execute: async args => deleteLabelStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deleteLabelStep({ token: await resolveGithubToken(token, githubTokenCall('deleteLabel', args)), ...args }), }) async function addAssigneesStep(args: Parameters[0]) { @@ -272,7 +272,7 @@ export const addAssignees = (token: GithubTokenInput, { needsApproval = true }: description: addAssigneesDescription, needsApproval, inputSchema: addAssigneesInputSchema, - execute: async args => addAssigneesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addAssigneesStep({ token: await resolveGithubToken(token, githubTokenCall('addAssignees', args)), ...args }), }) async function removeAssigneesStep(args: Parameters[0]) { @@ -286,5 +286,5 @@ export const removeAssignees = (token: GithubTokenInput, { needsApproval = true description: removeAssigneesDescription, needsApproval, inputSchema: removeAssigneesInputSchema, - execute: async args => removeAssigneesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => removeAssigneesStep({ token: await resolveGithubToken(token, githubTokenCall('removeAssignees', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/notifications.ts b/packages/github-tools/src/tools/notifications.ts index ee60873..ab04143 100644 --- a/packages/github-tools/src/tools/notifications.ts +++ b/packages/github-tools/src/tools/notifications.ts @@ -7,7 +7,7 @@ import { markNotificationReadDescription, markNotificationReadCore, } from '../core/notifications' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listNotificationsStep(args: Parameters[0]) { @@ -20,7 +20,7 @@ export const listNotifications = (token: GithubTokenInput): GithubTool => tool({ description: listNotificationsDescription, inputSchema: listNotificationsInputSchema, - execute: async args => listNotificationsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listNotificationsStep({ token: await resolveGithubToken(token, githubTokenCall('listNotifications', args)), ...args }), }) async function markNotificationReadStep(args: Parameters[0]) { @@ -34,5 +34,5 @@ export const markNotificationRead = (token: GithubTokenInput, { needsApproval = description: markNotificationReadDescription, needsApproval, inputSchema: markNotificationReadInputSchema, - execute: async args => markNotificationReadStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => markNotificationReadStep({ token: await resolveGithubToken(token, githubTokenCall('markNotificationRead', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/pull-requests.ts b/packages/github-tools/src/tools/pull-requests.ts index eff2625..20f4f54 100644 --- a/packages/github-tools/src/tools/pull-requests.ts +++ b/packages/github-tools/src/tools/pull-requests.ts @@ -47,7 +47,7 @@ import { requestReviewersCore, } from '../core/pull-requests' import { listPullRequestFilesToModelOutput } from '../core/model-output' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { CommitIdentity, ToolOptions, GithubTool } from '../types' export type MergeToolOptions = ToolOptions & { @@ -64,7 +64,7 @@ export const listPullRequests = (token: GithubTokenInput): GithubTool => tool({ description: listPullRequestsDescription, inputSchema: listPullRequestsInputSchema, - execute: async args => listPullRequestsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listPullRequestsStep({ token: await resolveGithubToken(token, githubTokenCall('listPullRequests', args)), ...args }), }) async function getPullRequestStep(args: Parameters[0]) { @@ -77,7 +77,7 @@ export const getPullRequest = (token: GithubTokenInput): GithubTool => tool({ description: getPullRequestDescription, inputSchema: getPullRequestInputSchema, - execute: async args => getPullRequestStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getPullRequestStep({ token: await resolveGithubToken(token, githubTokenCall('getPullRequest', args)), ...args }), }) async function createPullRequestStep(args: Parameters[0]) { @@ -91,7 +91,7 @@ export const createPullRequest = (token: GithubTokenInput, { needsApproval = tru description: createPullRequestDescription, needsApproval, inputSchema: createPullRequestInputSchema, - execute: async args => createPullRequestStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createPullRequestStep({ token: await resolveGithubToken(token, githubTokenCall('createPullRequest', args)), ...args }), }) async function mergePullRequestStep(args: Parameters[0]) { @@ -105,7 +105,7 @@ export const mergePullRequest = (token: GithubTokenInput, { needsApproval = true description: mergePullRequestDescription, needsApproval, inputSchema: mergePullRequestInputSchema, - execute: async args => mergePullRequestStep({ token: await resolveGithubToken(token), coAuthors, ...args }), + execute: async args => mergePullRequestStep({ token: await resolveGithubToken(token, githubTokenCall('mergePullRequest', args)), coAuthors, ...args }), }) async function updatePullRequestStep(args: Parameters[0]) { @@ -119,7 +119,7 @@ export const updatePullRequest = (token: GithubTokenInput, { needsApproval = tru description: updatePullRequestDescription, needsApproval, inputSchema: updatePullRequestInputSchema, - execute: async args => updatePullRequestStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updatePullRequestStep({ token: await resolveGithubToken(token, githubTokenCall('updatePullRequest', args)), ...args }), }) async function addPullRequestCommentStep(args: Parameters[0]) { @@ -133,7 +133,7 @@ export const addPullRequestComment = (token: GithubTokenInput, { needsApproval = description: addPullRequestCommentDescription, needsApproval, inputSchema: addPullRequestCommentInputSchema, - execute: async args => addPullRequestCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addPullRequestCommentStep({ token: await resolveGithubToken(token, githubTokenCall('addPullRequestComment', args)), ...args }), }) async function updatePullRequestCommentStep(args: Parameters[0]) { @@ -147,7 +147,7 @@ export const updatePullRequestComment = (token: GithubTokenInput, { needsApprova description: updatePullRequestCommentDescription, needsApproval, inputSchema: updatePullRequestCommentInputSchema, - execute: async args => updatePullRequestCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updatePullRequestCommentStep({ token: await resolveGithubToken(token, githubTokenCall('updatePullRequestComment', args)), ...args }), }) async function deletePullRequestCommentStep(args: Parameters[0]) { @@ -161,7 +161,7 @@ export const deletePullRequestComment = (token: GithubTokenInput, { needsApprova description: deletePullRequestCommentDescription, needsApproval, inputSchema: deletePullRequestCommentInputSchema, - execute: async args => deletePullRequestCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deletePullRequestCommentStep({ token: await resolveGithubToken(token, githubTokenCall('deletePullRequestComment', args)), ...args }), }) async function listPullRequestFilesStep(args: Parameters[0]) { @@ -175,7 +175,7 @@ export const listPullRequestFiles = (token: GithubTokenInput): GithubTool => description: listPullRequestFilesDescription, inputSchema: listPullRequestFilesInputSchema, toModelOutput: listPullRequestFilesToModelOutput, - execute: async args => listPullRequestFilesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listPullRequestFilesStep({ token: await resolveGithubToken(token, githubTokenCall('listPullRequestFiles', args)), ...args }), }) async function listPullRequestReviewsStep(args: Parameters[0]) { @@ -188,7 +188,7 @@ export const listPullRequestReviews = (token: GithubTokenInput): GithubTool => tool({ description: listPullRequestReviewsDescription, inputSchema: listPullRequestReviewsInputSchema, - execute: async args => listPullRequestReviewsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listPullRequestReviewsStep({ token: await resolveGithubToken(token, githubTokenCall('listPullRequestReviews', args)), ...args }), }) async function createPullRequestReviewStep(args: Parameters[0]) { @@ -202,7 +202,7 @@ export const createPullRequestReview = (token: GithubTokenInput, { needsApproval description: createPullRequestReviewDescription, needsApproval, inputSchema: createPullRequestReviewInputSchema, - execute: async args => createPullRequestReviewStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createPullRequestReviewStep({ token: await resolveGithubToken(token, githubTokenCall('createPullRequestReview', args)), ...args }), }) async function listPullRequestReviewThreadsStep(args: Parameters[0]) { @@ -215,7 +215,7 @@ export const listPullRequestReviewThreads = (token: GithubTokenInput): GithubToo tool({ description: listPullRequestReviewThreadsDescription, inputSchema: listPullRequestReviewThreadsInputSchema, - execute: async args => listPullRequestReviewThreadsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listPullRequestReviewThreadsStep({ token: await resolveGithubToken(token, githubTokenCall('listPullRequestReviewThreads', args)), ...args }), }) async function replyToReviewCommentStep(args: Parameters[0]) { @@ -229,7 +229,7 @@ export const replyToReviewComment = (token: GithubTokenInput, { needsApproval = description: replyToReviewCommentDescription, needsApproval, inputSchema: replyToReviewCommentInputSchema, - execute: async args => replyToReviewCommentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => replyToReviewCommentStep({ token: await resolveGithubToken(token, githubTokenCall('replyToReviewComment', args)), ...args }), }) async function resolveReviewThreadStep(args: Parameters[0]) { @@ -243,7 +243,7 @@ export const resolveReviewThread = (token: GithubTokenInput, { needsApproval = t description: resolveReviewThreadDescription, needsApproval, inputSchema: resolveReviewThreadInputSchema, - execute: async args => resolveReviewThreadStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => resolveReviewThreadStep({ token: await resolveGithubToken(token, githubTokenCall('resolveReviewThread', args)), ...args }), }) async function requestReviewersStep(args: Parameters[0]) { @@ -257,5 +257,5 @@ export const requestReviewers = (token: GithubTokenInput, { needsApproval = true description: requestReviewersDescription, needsApproval, inputSchema: requestReviewersInputSchema, - execute: async args => requestReviewersStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => requestReviewersStep({ token: await resolveGithubToken(token, githubTokenCall('requestReviewers', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/reactions.ts b/packages/github-tools/src/tools/reactions.ts index ab79a2a..13e772d 100644 --- a/packages/github-tools/src/tools/reactions.ts +++ b/packages/github-tools/src/tools/reactions.ts @@ -13,7 +13,7 @@ import { addCommentReactionDescription, addCommentReactionCore, } from '../core/reactions' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listIssueReactionsStep(args: Parameters[0]) { @@ -26,7 +26,7 @@ export const listIssueReactions = (token: GithubTokenInput): GithubTool => tool({ description: listIssueReactionsDescription, inputSchema: listIssueReactionsInputSchema, - execute: async args => listIssueReactionsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listIssueReactionsStep({ token: await resolveGithubToken(token, githubTokenCall('listIssueReactions', args)), ...args }), }) async function addIssueReactionStep(args: Parameters[0]) { @@ -40,7 +40,7 @@ export const addIssueReaction = (token: GithubTokenInput, { needsApproval = true description: addIssueReactionDescription, needsApproval, inputSchema: addIssueReactionInputSchema, - execute: async args => addIssueReactionStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addIssueReactionStep({ token: await resolveGithubToken(token, githubTokenCall('addIssueReaction', args)), ...args }), }) async function listCommentReactionsStep(args: Parameters[0]) { @@ -53,7 +53,7 @@ export const listCommentReactions = (token: GithubTokenInput): GithubTool => tool({ description: listCommentReactionsDescription, inputSchema: listCommentReactionsInputSchema, - execute: async args => listCommentReactionsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listCommentReactionsStep({ token: await resolveGithubToken(token, githubTokenCall('listCommentReactions', args)), ...args }), }) async function addCommentReactionStep(args: Parameters[0]) { @@ -67,5 +67,5 @@ export const addCommentReaction = (token: GithubTokenInput, { needsApproval = tr description: addCommentReactionDescription, needsApproval, inputSchema: addCommentReactionInputSchema, - execute: async args => addCommentReactionStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => addCommentReactionStep({ token: await resolveGithubToken(token, githubTokenCall('addCommentReaction', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/releases.ts b/packages/github-tools/src/tools/releases.ts index cede3a9..0271c62 100644 --- a/packages/github-tools/src/tools/releases.ts +++ b/packages/github-tools/src/tools/releases.ts @@ -19,7 +19,7 @@ import { deleteReleaseDescription, deleteReleaseCore, } from '../core/releases' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listReleasesStep(args: Parameters[0]) { @@ -32,7 +32,7 @@ export const listReleases = (token: GithubTokenInput): GithubTool => tool({ description: listReleasesDescription, inputSchema: listReleasesInputSchema, - execute: async args => listReleasesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listReleasesStep({ token: await resolveGithubToken(token, githubTokenCall('listReleases', args)), ...args }), }) async function getLatestReleaseStep(args: Parameters[0]) { @@ -45,7 +45,7 @@ export const getLatestRelease = (token: GithubTokenInput): GithubTool => tool({ description: getLatestReleaseDescription, inputSchema: getLatestReleaseInputSchema, - execute: async args => getLatestReleaseStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getLatestReleaseStep({ token: await resolveGithubToken(token, githubTokenCall('getLatestRelease', args)), ...args }), }) async function getReleaseStep(args: Parameters[0]) { @@ -58,7 +58,7 @@ export const getRelease = (token: GithubTokenInput): GithubTool => tool({ description: getReleaseDescription, inputSchema: getReleaseInputSchema, - execute: async args => getReleaseStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getReleaseStep({ token: await resolveGithubToken(token, githubTokenCall('getRelease', args)), ...args }), }) async function createReleaseStep(args: Parameters[0]) { @@ -72,7 +72,7 @@ export const createRelease = (token: GithubTokenInput, { needsApproval = true }: description: createReleaseDescription, needsApproval, inputSchema: createReleaseInputSchema, - execute: async args => createReleaseStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createReleaseStep({ token: await resolveGithubToken(token, githubTokenCall('createRelease', args)), ...args }), }) async function updateReleaseStep(args: Parameters[0]) { @@ -86,7 +86,7 @@ export const updateRelease = (token: GithubTokenInput, { needsApproval = true }: description: updateReleaseDescription, needsApproval, inputSchema: updateReleaseInputSchema, - execute: async args => updateReleaseStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => updateReleaseStep({ token: await resolveGithubToken(token, githubTokenCall('updateRelease', args)), ...args }), }) async function deleteReleaseStep(args: Parameters[0]) { @@ -100,5 +100,5 @@ export const deleteRelease = (token: GithubTokenInput, { needsApproval = true }: description: deleteReleaseDescription, needsApproval, inputSchema: deleteReleaseInputSchema, - execute: async args => deleteReleaseStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deleteReleaseStep({ token: await resolveGithubToken(token, githubTokenCall('deleteRelease', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/repository.ts b/packages/github-tools/src/tools/repository.ts index cedc647..55b25de 100644 --- a/packages/github-tools/src/tools/repository.ts +++ b/packages/github-tools/src/tools/repository.ts @@ -30,7 +30,7 @@ import { composeCommitMessage, } from '../core/repository' import { getFileContentToModelOutput, getRepositoryTreeToModelOutput } from '../core/model-output' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { CommitToolOptions, ToolOptions, GithubTool } from '../types' export { composeCommitMessage } @@ -45,7 +45,7 @@ export const getRepository = (token: GithubTokenInput): GithubTool => tool({ description: getRepositoryDescription, inputSchema: getRepositoryInputSchema, - execute: async args => getRepositoryStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getRepositoryStep({ token: await resolveGithubToken(token, githubTokenCall('getRepository', args)), ...args }), }) async function listBranchesStep(args: Parameters[0]) { @@ -58,7 +58,7 @@ export const listBranches = (token: GithubTokenInput): GithubTool => tool({ description: listBranchesDescription, inputSchema: listBranchesInputSchema, - execute: async args => listBranchesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listBranchesStep({ token: await resolveGithubToken(token, githubTokenCall('listBranches', args)), ...args }), }) async function getFileContentStep(args: Parameters[0]) { @@ -72,7 +72,7 @@ export const getFileContent = (token: GithubTokenInput): GithubTool => description: getFileContentDescription, inputSchema: getFileContentInputSchema, toModelOutput: getFileContentToModelOutput, - execute: async args => getFileContentStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getFileContentStep({ token: await resolveGithubToken(token, githubTokenCall('getFileContent', args)), ...args }), }) async function getRepositoryTreeStep(args: Parameters[0]) { @@ -86,7 +86,7 @@ export const getRepositoryTree = (token: GithubTokenInput): GithubTool => description: getRepositoryTreeDescription, inputSchema: getRepositoryTreeInputSchema, toModelOutput: getRepositoryTreeToModelOutput, - execute: async args => getRepositoryTreeStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getRepositoryTreeStep({ token: await resolveGithubToken(token, githubTokenCall('getRepositoryTree', args)), ...args }), }) async function createBranchStep(args: Parameters[0]) { @@ -100,7 +100,7 @@ export const createBranch = (token: GithubTokenInput, { needsApproval = true }: description: createBranchDescription, needsApproval, inputSchema: createBranchInputSchema, - execute: async args => createBranchStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createBranchStep({ token: await resolveGithubToken(token, githubTokenCall('createBranch', args)), ...args }), }) async function deleteBranchStep(args: Parameters[0]) { @@ -114,7 +114,7 @@ export const deleteBranch = (token: GithubTokenInput, { needsApproval = true }: description: deleteBranchDescription, needsApproval, inputSchema: deleteBranchInputSchema, - execute: async args => deleteBranchStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => deleteBranchStep({ token: await resolveGithubToken(token, githubTokenCall('deleteBranch', args)), ...args }), }) async function forkRepositoryStep(args: Parameters[0]) { @@ -128,7 +128,7 @@ export const forkRepository = (token: GithubTokenInput, { needsApproval = true } description: forkRepositoryDescription, needsApproval, inputSchema: forkRepositoryInputSchema, - execute: async args => forkRepositoryStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => forkRepositoryStep({ token: await resolveGithubToken(token, githubTokenCall('forkRepository', args)), ...args }), }) async function createRepositoryStep(args: Parameters[0]) { @@ -142,7 +142,7 @@ export const createRepository = (token: GithubTokenInput, { needsApproval = true description: createRepositoryDescription, needsApproval, inputSchema: createRepositoryInputSchema, - execute: async args => createRepositoryStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => createRepositoryStep({ token: await resolveGithubToken(token, githubTokenCall('createRepository', args)), ...args }), }) async function createOrUpdateFileStep(args: Parameters[0]) { @@ -159,5 +159,5 @@ export const createOrUpdateFile = ( description: createOrUpdateFileDescription, needsApproval, inputSchema: createOrUpdateFileInputSchema, - execute: async args => createOrUpdateFileStep({ token: await resolveGithubToken(token), author, committer, coAuthors, ...args }), + execute: async args => createOrUpdateFileStep({ token: await resolveGithubToken(token, githubTokenCall('createOrUpdateFile', args)), author, committer, coAuthors, ...args }), }) diff --git a/packages/github-tools/src/tools/search.ts b/packages/github-tools/src/tools/search.ts index e57c5c6..ceb679f 100644 --- a/packages/github-tools/src/tools/search.ts +++ b/packages/github-tools/src/tools/search.ts @@ -1,5 +1,5 @@ import { tool } from 'ai' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { GithubTool } from '../types' import { searchCodeInputSchema, @@ -23,7 +23,7 @@ export const searchCode = (token: GithubTokenInput): GithubTool => tool({ description: searchCodeDescription, inputSchema: searchCodeInputSchema, - execute: async args => searchCodeStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => searchCodeStep({ token: await resolveGithubToken(token, githubTokenCall('searchCode', args)), ...args }), }) async function searchRepositoriesStep(args: Parameters[0]) { @@ -36,7 +36,7 @@ export const searchRepositories = (token: GithubTokenInput): GithubTool => tool({ description: searchRepositoriesDescription, inputSchema: searchRepositoriesInputSchema, - execute: async args => searchRepositoriesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => searchRepositoriesStep({ token: await resolveGithubToken(token, githubTokenCall('searchRepositories', args)), ...args }), }) async function searchIssuesStep(args: Parameters[0]) { @@ -49,5 +49,5 @@ export const searchIssues = (token: GithubTokenInput): GithubTool => tool({ description: searchIssuesDescription, inputSchema: searchIssuesInputSchema, - execute: async args => searchIssuesStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => searchIssuesStep({ token: await resolveGithubToken(token, githubTokenCall('searchIssues', args)), ...args }), }) diff --git a/packages/github-tools/src/tools/workflows.ts b/packages/github-tools/src/tools/workflows.ts index 027bc6d..bace958 100644 --- a/packages/github-tools/src/tools/workflows.ts +++ b/packages/github-tools/src/tools/workflows.ts @@ -25,7 +25,7 @@ import { rerunWorkflowRunDescription, rerunWorkflowRunCore, } from '../core/workflows' -import { resolveGithubToken, type GithubTokenInput } from '../core/token' +import { githubTokenCall, resolveGithubToken, type GithubTokenInput } from '../core/token' import type { ToolOptions, GithubTool } from '../types' async function listWorkflowsStep(args: Parameters[0]) { @@ -38,7 +38,7 @@ export const listWorkflows = (token: GithubTokenInput): GithubTool => tool({ description: listWorkflowsDescription, inputSchema: listWorkflowsInputSchema, - execute: async args => listWorkflowsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listWorkflowsStep({ token: await resolveGithubToken(token, githubTokenCall('listWorkflows', args)), ...args }), }) async function listWorkflowRunsStep(args: Parameters[0]) { @@ -51,7 +51,7 @@ export const listWorkflowRuns = (token: GithubTokenInput): GithubTool => tool({ description: listWorkflowRunsDescription, inputSchema: listWorkflowRunsInputSchema, - execute: async args => listWorkflowRunsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listWorkflowRunsStep({ token: await resolveGithubToken(token, githubTokenCall('listWorkflowRuns', args)), ...args }), }) async function getWorkflowRunStep(args: Parameters[0]) { @@ -64,7 +64,7 @@ export const getWorkflowRun = (token: GithubTokenInput): GithubTool => tool({ description: getWorkflowRunDescription, inputSchema: getWorkflowRunInputSchema, - execute: async args => getWorkflowRunStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getWorkflowRunStep({ token: await resolveGithubToken(token, githubTokenCall('getWorkflowRun', args)), ...args }), }) async function listWorkflowJobsStep(args: Parameters[0]) { @@ -77,7 +77,7 @@ export const listWorkflowJobs = (token: GithubTokenInput): GithubTool => tool({ description: listWorkflowJobsDescription, inputSchema: listWorkflowJobsInputSchema, - execute: async args => listWorkflowJobsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => listWorkflowJobsStep({ token: await resolveGithubToken(token, githubTokenCall('listWorkflowJobs', args)), ...args }), }) async function getWorkflowJobLogsStep(args: Parameters[0]) { @@ -90,7 +90,7 @@ export const getWorkflowJobLogs = (token: GithubTokenInput): GithubTool => tool({ description: getWorkflowJobLogsDescription, inputSchema: getWorkflowJobLogsInputSchema, - execute: async args => getWorkflowJobLogsStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => getWorkflowJobLogsStep({ token: await resolveGithubToken(token, githubTokenCall('getWorkflowJobLogs', args)), ...args }), }) async function triggerWorkflowStep(args: Parameters[0]) { @@ -104,7 +104,7 @@ export const triggerWorkflow = (token: GithubTokenInput, { needsApproval = true description: triggerWorkflowDescription, needsApproval, inputSchema: triggerWorkflowInputSchema, - execute: async args => triggerWorkflowStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => triggerWorkflowStep({ token: await resolveGithubToken(token, githubTokenCall('triggerWorkflow', args)), ...args }), }) async function cancelWorkflowRunStep(args: Parameters[0]) { @@ -118,7 +118,7 @@ export const cancelWorkflowRun = (token: GithubTokenInput, { needsApproval = tru description: cancelWorkflowRunDescription, needsApproval, inputSchema: cancelWorkflowRunInputSchema, - execute: async args => cancelWorkflowRunStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => cancelWorkflowRunStep({ token: await resolveGithubToken(token, githubTokenCall('cancelWorkflowRun', args)), ...args }), }) async function rerunWorkflowRunStep(args: Parameters[0]) { @@ -132,5 +132,5 @@ export const rerunWorkflowRun = (token: GithubTokenInput, { needsApproval = true description: rerunWorkflowRunDescription, needsApproval, inputSchema: rerunWorkflowRunInputSchema, - execute: async args => rerunWorkflowRunStep({ token: await resolveGithubToken(token), ...args }), + execute: async args => rerunWorkflowRunStep({ token: await resolveGithubToken(token, githubTokenCall('rerunWorkflowRun', args)), ...args }), })