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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/link-eve-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@stripe/link-sdk': minor
'@stripe/link-integrations-eve': minor
---

Adds an integration for [Eve](https://eve.dev) via extension. `@stripe/link-sdk` now exports tools which integrations like Eve can import.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
node-version: 24
cache: pnpm

- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
Expand Down Expand Up @@ -82,4 +82,4 @@ jobs:
run: go test -race ./...

- name: Verify publishable
run: pnpm --filter @stripe/link-cli --filter @stripe/link-sdk --filter @stripe/link-integrations-better-auth publish --dry-run --no-git-checks
run: pnpm --filter @stripe/link-cli --filter @stripe/link-sdk --filter @stripe/link-integrations-better-auth --filter @stripe/link-integrations-eve publish --dry-run --no-git-checks
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Link CLI — lets agents get secure, one-time-use payment credentials from a Lin
- **Link Go SDK** (`packages/sdk-go`): Go equivalent of `@stripe/link-sdk`. It accepts `AccessToken` or `GetAccessToken`; it does not own OAuth state. Package name: `link`.
- **Link Python SDK** (`packages/sdk-python`): Python 3.11+ library covering the Go SDK's API resources with Python conventions. Distribution name: `link-sdk`; import name: `link`. HTTPX `Client` and `AsyncClient` expose typed keyword arguments and Pydantic response models. Uses uv for Python, dependencies, environments, builds, and development commands.
- **`@stripe/link-integrations-better-auth`** (`packages/integrations/better-auth`): Generic OAuth wrapper for Link sign-in and connecting wallets. Link's stable `/userinfo.id` identifies the provider account, using the SDK's `UserInfo` type through a development dependency. The `/client` export provides `linkClient()`: `link.connect()` wraps native `linkSocial`, while `link.disconnect()` checks an authoritative fresh session, ownership, provider, and last-account policy before revoking the stored refresh token and deleting the account. Revocation failures retain the account and credentials. Better Auth owns OAuth state, token storage, and refresh; wallet API calls remain in the SDK.
- **`@stripe/link-integrations-eve`** (`packages/integrations/eve`): Native Eve extension built with `eve extension build`. Static tool files wrap `@stripe/link-sdk/tools` and accept exactly one of `accessToken` or an Eve `auth` provider. OAuth tools use `ctx.getToken` and map Link 401s to `ctx.requireAuth`. The consuming application owns its OAuth provider, including token exchange, storage, refresh, and callback routing; this package supplies no OAuth client or storage abstraction. Static-token mode does not refresh. Interactive OAuth requires an authenticated Eve user. `create_spend_request` defaults to Eve approval via `always()`, which consumers can override; `request_approval: false` defers Link approval for a draft and does not authorize spending. Its `extension/skills/create-payment-credential/SKILL.md` and `extension/skills/financial-insights/SKILL.md` adapt the root skills to native tool calls and Eve auth; maintain these copies alongside shared wallet behavior and tool changes. Workspace development and CI require Node 24+.
- **`@stripe/link-cli`** (`packages/cli`): Commander.js + Ink/React CLI that consumes `@stripe/link-sdk`. Entry: `src/cli.tsx`.

## Commands
Expand Down Expand Up @@ -43,6 +44,11 @@ node packages/cli/dist/cli.js <command>

### SDK Resources

`packages/sdk/src/tools/` exports the framework-independent tool catalog and Zod
input schemas through `@stripe/link-sdk/tools`. Tools use API field names and
delegate to SDK resources. Do not put OAuth state, CLI flags, or Eve dependencies
in this entrypoint. Keep new tool schemas aligned with the SDK parameter types.

Defined in `packages/sdk/src/resources/interfaces.ts`:
- `IAttestationsResource` — Privacy Pass Blind RSA token issuance
- `IIdentityCredentialsResource` — signed user info issuance
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,11 @@ asynchronous clients covering the Go SDK's API resources with Python conventions
Authentication flows and credential persistence remain the embedding
application's responsibility.

The TypeScript SDK also exports reusable [agent tools](packages/sdk/README.md#agent-tools).
Use the [Eve extension](packages/integrations/eve/README.md) to mount them in an
Eve agent with a static access token or an application-provided Eve auth provider,
plus a wallet skill.

## Onboarding and Demos

Run the guided setup flow — authenticates, checks payment methods, shows the app download QR, and runs both demo flows:
Expand All @@ -660,6 +665,8 @@ link-cli demo --only-spt # machine payment (SPT) flow only

## Development

Workspace development requires Node.js 24+.

```bash
pnpm install
pnpm run build
Expand Down Expand Up @@ -699,7 +706,7 @@ pnpm biome check .
## Releasing

This project uses [Changesets](https://github.com/changesets/changesets) to
version and publish `@stripe/link-cli` and `@stripe/link-sdk`.
version and publish `@stripe/link-cli`, `@stripe/link-sdk`, and `@stripe/link-integrations-eve`.
`@stripe/link-typescript-config` is private and is not published.

### Add a changeset
Expand Down Expand Up @@ -729,7 +736,7 @@ To inspect the packages without publishing them:

```bash
pnpm turbo run build
pnpm --filter @stripe/link-cli --filter @stripe/link-sdk --filter @stripe/link-integrations-better-auth publish --dry-run --no-git-checks
pnpm --filter @stripe/link-cli --filter @stripe/link-sdk --filter @stripe/link-integrations-better-auth --filter @stripe/link-integrations-eve publish --dry-run --no-git-checks
```

CI runs the same publish dry-run for every pull request.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
]
},
"engines": {
"node": ">=22"
"node": ">=24"
}
}
5 changes: 5 additions & 0 deletions packages/integrations/eve/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.eve
.output
.nitro
.eve-extension-build-*
*.tsbuildinfo
21 changes: 21 additions & 0 deletions packages/integrations/eve/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Stripe, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
166 changes: 166 additions & 0 deletions packages/integrations/eve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Link for Eve

Use a Link wallet from an [Eve extension](https://eve.dev/docs/extensions).
Tools reuse `@stripe/link-sdk/tools`; the extension adds Eve discovery
and an Eve-specific wallet skill.

Requires Node.js 24+. Built with Eve 0.54.4; Eve checks the generated extension
compatibility metadata when a consumer builds.

## Install and mount

```sh
pnpm add @stripe/link-integrations-eve
```

Configure either a static access token or an [OAuth provider](#interactive-oauth).
For a static token, create `agent/extensions/link.ts`:

```ts
import link from '@stripe/link-integrations-eve';

export default link({
accessToken: process.env.LINK_ACCESS_TOKEN!,
});
```

Set `LINK_ACCESS_TOKEN` in your agent's server environment, such as `.env.local`
for local development. The token is required and must be nonempty. Every tool
call through this mount uses that token's wallet and permissions, regardless of
the Eve session's caller. Control access to the agent accordingly.

Static-token mode does not start OAuth, read CLI credentials, require a user
principal, or refresh the token. A 401 fails once
with an instruction to configure a new token. Tokens are configuration, never
model-supplied tool arguments.

## Tools

Mounting as `link` adds the `link__` prefix to these names:

| Tools | Purpose |
| --- | --- |
| `retrieve_user_info` | Profile, limits, and verification requirements |
| `list_payment_methods`, `list_shipping_addresses` | Saved wallet details |
| `list_spend_requests`, `create_spend_request`, `retrieve_spend_request`, `update_spend_request` | Purchase requests and their status |
| `request_spend_approval`, `cancel_spend_request` | Request approval or cancel a request |
| `list_transactions`, `list_sources`, `list_balances` | Financial data permitted by the user's OAuth grant |
| `create_report` | Record a purchase attempt's outcome |

Inputs use SDK/API field names, such as `payment_details`, `line_items`, and
`spend_request_id`. Financial-data tools may require additional scopes and source
permissions on the supplied token.

By default, `create_spend_request` requires Eve user approval on every call
(`always()`). Applications can [override this policy](#override-or-remove-a-tool).
Spend requests also default to requesting Link approval and return immediately.
Setting `request_approval: false` intentionally supports preparing a draft before
calling `request_spend_approval`; it does not authorize the purchase. Show the
approval URL to the user and retrieve the same request after approval. Follow
`status_details.requires_action.next_action` when further action is required.
Eve approval is separate from Link's purchase authorization.

Tool results are normal SDK responses. Requesting `include: ['card']` can return
payment credentials in Eve's tool output and stored events. The extension's
instructions tell the agent not to repeat them in conversation; applications
still control who can access the transcript and how results are retained.

## Skills

The extension includes [`create-payment-credential`](extension/skills/create-payment-credential/SKILL.md)
and [`financial-insights`](extension/skills/financial-insights/SKILL.md).
They adapt the root skills' guidance to native tool calls and Eve authorization.
Edit these copies directly and keep shared wallet behavior aligned with the root
skills. Eve bundles both under the extension's mount prefix.

## Interactive OAuth

Pass an application-owned Eve authorization provider as `auth` in
`agent/extensions/link.ts`:

```ts
import link from '@stripe/link-integrations-eve';
import { linkAuth } from '../lib/link-auth';

export default link({ auth: linkAuth });
```

Implement `linkAuth` in your application with Eve's
[`defineInteractiveAuthorization`](https://eve.dev/docs/connections#self-hosted-interactive-oauth).
It takes three methods:

- `getToken`: load or refresh the current principal's token; throw
`ConnectionAuthorizationRequiredError` when consent is needed.
- `startAuthorization`: return the Link consent URL and any serializable state
needed to finish authorization.
- `completeAuthorization`: validate the callback, exchange the code, persist the
grant, and return `{ token, expiresAt }` (expiration is milliseconds since epoch).

The extension calls `ctx.getToken(auth)` before a Link API call and
`ctx.requireAuth(auth)` when Link returns 401. Eve presents the authorization
challenge, suspends the turn, and resumes it after authorization. Interactive
providers require an authenticated user on the consuming agent's inbound channel.

Your provider owns Link's PKCE/state validation, token exchange, persistent
per-user grants, refresh, and revocation. Link requires an exactly registered
redirect URI; your application's callback routing must connect that URL to Eve's
per-attempt callback. See [Link's OAuth documentation](https://docs.stripe.com/agentic-commerce/link-cli/oauth)
and [Eve's lifecycle fixture](https://github.com/vercel/eve/blob/main/e2e/fixtures/agent-tools-hitl/agent/tools/auth-probe.ts).
The fixture uses a test token; it demonstrates the lifecycle, not a Link OAuth client.

Configure exactly one of `accessToken` or `auth`. The extension also accepts
Eve's `getToken`-only providers when your application already manages authorization.
Vercel Connect is not required.

## Override or remove a tool

Use Eve's standard directory mount and overrides:

```text
agent/extensions/link/
extension.ts
tools/create_spend_request.ts
```

To remove a tool:

```ts
import { disableTool } from 'eve/tools';

export default disableTool();
```

To disable Eve's confirmation prompt for this tool, create
`agent/extensions/link/tools/create_spend_request.ts`:

```ts
import { create_spend_request } from '@stripe/link-integrations-eve/tools';
import { defineTool } from 'eve/tools';
import { never } from 'eve/tools/approval';

export default defineTool({ ...create_spend_request, approval: never() });
```

Use `once()` to prompt once per session, or supply a custom approval policy.
These overrides control Eve's confirmation prompt; Link's purchase authorization
remains separate.

## Terminal example

See the [terminal OAuth example](example/README.md) for an agent scaffolded with
Eve's CLI that connects our Better Auth Link integration to the mounted extension.

## Development

From the repository root:

```sh
pnpm --filter @stripe/link-integrations-eve... build
pnpm --filter @stripe/link-integrations-eve typecheck
pnpm --filter @stripe/link-integrations-eve test
```

`eve extension build` emits the extension, mount factory, tool exports, and
compatibility manifest under `dist/`. The Eve runtime is a peer dependency;
the exact development dependency pins the compiler. The normal workspace build
builds the SDK first. Publish the built package, including `dist/`.
11 changes: 11 additions & 0 deletions packages/integrations/eve/example/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Set this to call the model through OpenRouter.
OPENROUTER_API_KEY=

# Register http://localhost:3000/api/auth/callback/link with Link.
LINK_CLIENT_ID=
LINK_CLIENT_SECRET=
STRIPE_PUBLISHABLE_KEY=

# Generate once: openssl rand -hex 32
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000
13 changes: 13 additions & 0 deletions packages/integrations/eve/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
.env*
.eve
.vercel
.next
.output
.nitro
dist
.DS_Store
*.tsbuildinfo

!.env.example
.data/
7 changes: 7 additions & 0 deletions packages/integrations/eve/example/.vercelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
.env*
.eve
.next
.output
.nitro
dist
26 changes: 26 additions & 0 deletions packages/integrations/eve/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Link + Eve example

Local terminal agent. Requires Node.js 24+ and pnpm.

Auth wiring is illustrative and assumes a single local user. Use your own
authentication and session integration when building your application.

From the repository root:

```sh
pnpm install
cd packages/integrations/eve/example
cp .env.example .env.local
```

Fill in `.env.local` with your OpenRouter API key, Link OAuth credentials, and
`BETTER_AUTH_SECRET` (generate once with `openssl rand -hex 32`).

Register `http://localhost:3000/api/auth/callback/link` as your Link OAuth redirect URI.

```sh
pnpm dev
```

Ask “List my payment methods.” Open the authorization link in your browser,
approve access, and return to the terminal.
11 changes: 11 additions & 0 deletions packages/integrations/eve/example/agent/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { defineAgent } from 'eve';

const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});

export default defineAgent({
model: openrouter('openai/gpt-6-luna'),
modelContextWindowTokens: 1_000_000,
});
17 changes: 17 additions & 0 deletions packages/integrations/eve/example/agent/channels/eve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { eveChannel } from 'eve/channels/eve';
import { getLocalDevCapability } from 'eve/local-dev';
import { config, getTerminalSession } from '../lib/auth';

export default eveChannel({
async auth() {
if (!getLocalDevCapability()) return null;
const { response } = await getTerminalSession();
return {
principalType: 'user' as const,
principalId: response.user.id,
issuer: config().origin,
authenticator: 'local-better-auth',
attributes: {},
};
},
});
Loading
Loading