@antseed/provider-claude-oauth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -0
- package/src/index.test.ts +69 -0
- package/src/index.ts +68 -0
- package/tsconfig.json +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @antseed/provider-claude-oauth
|
|
2
|
+
|
|
3
|
+
Third-party Anthropic Claude provider plugin with OAuth authentication for Antseed.
|
|
4
|
+
|
|
5
|
+
This plugin demonstrates how a third-party developer can build, test, and publish an Antseed provider plugin using `@antseed/provider-core`.
|
|
6
|
+
|
|
7
|
+
## How to Create a Plugin
|
|
8
|
+
|
|
9
|
+
1. Create a new directory under `plugins/` (or anywhere on disk).
|
|
10
|
+
2. Add a `package.json` with `@antseed/provider-core` as a dependency and `@antseed/node` as a peer dependency.
|
|
11
|
+
3. Implement and default-export an `AntseedProviderPlugin` object from `src/index.ts`.
|
|
12
|
+
4. Build with `tsc` and test with `vitest`.
|
|
13
|
+
|
|
14
|
+
## Plugin Manifest Format
|
|
15
|
+
|
|
16
|
+
Every provider plugin must default-export an object satisfying `AntseedProviderPlugin`:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import type { AntseedProviderPlugin, ConfigField } from '@antseed/node';
|
|
20
|
+
|
|
21
|
+
const plugin: AntseedProviderPlugin = {
|
|
22
|
+
name: 'my-provider', // unique plugin name
|
|
23
|
+
displayName: 'My Provider', // human-readable label
|
|
24
|
+
version: '0.1.0',
|
|
25
|
+
type: 'provider',
|
|
26
|
+
description: 'Description of the provider',
|
|
27
|
+
configSchema: [ // fields the user fills in
|
|
28
|
+
{ key: 'API_KEY', label: 'API Key', type: 'secret', required: true },
|
|
29
|
+
],
|
|
30
|
+
createProvider(config) {
|
|
31
|
+
// Return a Provider instance
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export default plugin;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### ConfigField Types
|
|
38
|
+
|
|
39
|
+
| Type | Description |
|
|
40
|
+
| ---------- | ---------------------------------- |
|
|
41
|
+
| `string` | Plain text input |
|
|
42
|
+
| `number` | Numeric input |
|
|
43
|
+
| `boolean` | Toggle / checkbox |
|
|
44
|
+
| `secret` | Masked input (tokens, keys) |
|
|
45
|
+
| `string[]` | Comma-separated list of strings |
|
|
46
|
+
|
|
47
|
+
## Using provider-core
|
|
48
|
+
|
|
49
|
+
The `@antseed/provider-core` package provides reusable building blocks:
|
|
50
|
+
|
|
51
|
+
- **`BaseProvider`** -- Implements the `Provider` interface and wires up `HttpRelay`.
|
|
52
|
+
- **`StaticTokenProvider`** -- Wraps a static API key with no refresh logic.
|
|
53
|
+
- **`OAuthTokenProvider`** -- Manages OAuth access/refresh token pairs with automatic renewal.
|
|
54
|
+
- **`HttpRelay`** -- Forwards requests to an upstream API with auth header swapping, model validation, and concurrency control.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { BaseProvider, OAuthTokenProvider, StaticTokenProvider } from '@antseed/provider-core';
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Testing Locally
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Install dependencies
|
|
64
|
+
npm install
|
|
65
|
+
|
|
66
|
+
# Build the plugin
|
|
67
|
+
npm run build
|
|
68
|
+
|
|
69
|
+
# Run tests
|
|
70
|
+
npm test
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To test the plugin end-to-end with a local Antseed node, link it:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cd plugins/provider-claude-oauth
|
|
77
|
+
npm link
|
|
78
|
+
|
|
79
|
+
cd /path/to/your/antseed-project
|
|
80
|
+
npm link @anthropic/provider-claude-oauth
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Then configure the plugin in your node's plugin config with the required config fields.
|
|
84
|
+
|
|
85
|
+
## Publishing to npm
|
|
86
|
+
|
|
87
|
+
1. Ensure `package.json` has the correct `name`, `version`, and `description`.
|
|
88
|
+
2. Build the plugin: `npm run build`
|
|
89
|
+
3. Login to npm: `npm login`
|
|
90
|
+
4. Publish: `npm publish --access public`
|
|
91
|
+
|
|
92
|
+
For scoped packages like `@anthropic/provider-claude-oauth`, use `--access public` to publish as a public package.
|
|
93
|
+
|
|
94
|
+
## Installing via antseed plugin add
|
|
95
|
+
|
|
96
|
+
Once published, users can install the plugin:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
antseed plugin add @anthropic/provider-claude-oauth
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This will download the plugin from npm, register it with the local Antseed node, and prompt the user to configure the required fields (access token, etc.).
|
|
103
|
+
|
|
104
|
+
## Configuration Reference
|
|
105
|
+
|
|
106
|
+
| Key | Type | Required | Default | Description |
|
|
107
|
+
| ------------------------------ | -------- | -------- | ------- | ------------------------------------ |
|
|
108
|
+
| `CLAUDE_ACCESS_TOKEN` | secret | Yes | -- | Claude OAuth access token |
|
|
109
|
+
| `CLAUDE_REFRESH_TOKEN` | secret | No | -- | OAuth refresh token for auto-renewal |
|
|
110
|
+
| `CLAUDE_TOKEN_EXPIRES_AT` | number | No | -- | Epoch ms when access token expires |
|
|
111
|
+
| `ANTSEED_INPUT_USD_PER_MILLION`| number | No | 10 | Input token price (USD per 1M) |
|
|
112
|
+
| `ANTSEED_OUTPUT_USD_PER_MILLION`| number | No | 10 | Output token price (USD per 1M) |
|
|
113
|
+
| `ANTSEED_MAX_CONCURRENCY` | number | No | 5 | Max concurrent requests |
|
|
114
|
+
| `ANTSEED_ALLOWED_MODELS` | string[] | No | -- | Comma-separated list of model IDs |
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
See the root repository LICENSE file.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAe,MAAM,eAAe,CAAC;AAaxE,QAAA,MAAM,MAAM,EAAE,qBAoDb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BaseProvider, OAuthTokenProvider, StaticTokenProvider } from '@antseed/provider-core';
|
|
2
|
+
const configSchema = [
|
|
3
|
+
{ key: 'CLAUDE_ACCESS_TOKEN', label: 'Access Token', type: 'secret', required: true, description: 'Claude OAuth access token' },
|
|
4
|
+
{ key: 'CLAUDE_REFRESH_TOKEN', label: 'Refresh Token', type: 'secret', required: false, description: 'OAuth refresh token for auto-renewal' },
|
|
5
|
+
{ key: 'CLAUDE_TOKEN_EXPIRES_AT', label: 'Token Expiry', type: 'number', required: false, description: 'Epoch ms when access token expires' },
|
|
6
|
+
{ key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 10 },
|
|
7
|
+
{ key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 10 },
|
|
8
|
+
{ key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 5 },
|
|
9
|
+
{ key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false },
|
|
10
|
+
];
|
|
11
|
+
const plugin = {
|
|
12
|
+
name: 'claude-oauth',
|
|
13
|
+
displayName: 'Claude (OAuth)',
|
|
14
|
+
version: '0.1.0',
|
|
15
|
+
type: 'provider',
|
|
16
|
+
description: 'Anthropic Claude with OAuth authentication (third-party example)',
|
|
17
|
+
configSchema,
|
|
18
|
+
configKeys: configSchema,
|
|
19
|
+
createProvider(config) {
|
|
20
|
+
const accessToken = config['CLAUDE_ACCESS_TOKEN'];
|
|
21
|
+
if (!accessToken)
|
|
22
|
+
throw new Error('CLAUDE_ACCESS_TOKEN is required');
|
|
23
|
+
const refreshToken = config['CLAUDE_REFRESH_TOKEN'];
|
|
24
|
+
const expiresAt = config['CLAUDE_TOKEN_EXPIRES_AT']
|
|
25
|
+
? parseInt(config['CLAUDE_TOKEN_EXPIRES_AT'], 10)
|
|
26
|
+
: undefined;
|
|
27
|
+
const tokenProvider = refreshToken
|
|
28
|
+
? new OAuthTokenProvider({
|
|
29
|
+
accessToken,
|
|
30
|
+
refreshToken,
|
|
31
|
+
expiresAt: expiresAt ?? Date.now() + 3600_000,
|
|
32
|
+
tokenEndpoint: 'https://console.anthropic.com/v1/oauth/token',
|
|
33
|
+
})
|
|
34
|
+
: new StaticTokenProvider(accessToken);
|
|
35
|
+
const inputPrice = parseFloat(config['ANTSEED_INPUT_USD_PER_MILLION'] ?? '10');
|
|
36
|
+
const outputPrice = parseFloat(config['ANTSEED_OUTPUT_USD_PER_MILLION'] ?? '10');
|
|
37
|
+
const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '5', 10);
|
|
38
|
+
const allowedModels = (config['ANTSEED_ALLOWED_MODELS'] ?? '')
|
|
39
|
+
.split(',').map(s => s.trim()).filter(Boolean);
|
|
40
|
+
return new BaseProvider({
|
|
41
|
+
name: 'claude-oauth',
|
|
42
|
+
models: allowedModels,
|
|
43
|
+
pricing: {
|
|
44
|
+
defaults: {
|
|
45
|
+
inputUsdPerMillion: inputPrice,
|
|
46
|
+
outputUsdPerMillion: outputPrice,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
relay: {
|
|
50
|
+
baseUrl: 'https://api.anthropic.com',
|
|
51
|
+
authHeaderName: 'authorization',
|
|
52
|
+
authHeaderValue: `Bearer ${accessToken}`,
|
|
53
|
+
tokenProvider,
|
|
54
|
+
extraHeaders: { 'anthropic-beta': 'oauth-2025-04-20' },
|
|
55
|
+
maxConcurrency,
|
|
56
|
+
allowedModels,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export default plugin;
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE/F,MAAM,YAAY,GAAkB;IAClC,EAAE,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC/H,EAAE,GAAG,EAAE,sBAAsB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,sCAAsC,EAAE;IAC7I,EAAE,GAAG,EAAE,yBAAyB,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,oCAAoC,EAAE;IAC7I,EAAE,GAAG,EAAE,+BAA+B,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;IAC5G,EAAE,GAAG,EAAE,gCAAgC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9G,EAAE,GAAG,EAAE,yBAAyB,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE;IACzG,EAAE,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE;CAC9F,CAAC;AAEF,MAAM,MAAM,GAA0B;IACpC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,gBAAgB;IAC7B,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,kEAAkE;IAC/E,YAAY;IACZ,UAAU,EAAE,YAAY;IACxB,cAAc,CAAC,MAA8B;QAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAErE,MAAM,YAAY,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,CAAC,yBAAyB,CAAC;YACjD,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,yBAAyB,CAAC,EAAE,EAAE,CAAC;YACjD,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,aAAa,GAAG,YAAY;YAChC,CAAC,CAAC,IAAI,kBAAkB,CAAC;gBACrB,WAAW;gBACX,YAAY;gBACZ,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;gBAC7C,aAAa,EAAE,8CAA8C;aAC9D,CAAC;YACJ,CAAC,CAAC,IAAI,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAEzC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,+BAA+B,CAAC,IAAI,IAAI,CAAC,CAAC;QAC/E,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,gCAAgC,CAAC,IAAI,IAAI,CAAC,CAAC;QACjF,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,yBAAyB,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;QAC9E,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,CAAC;aAC3D,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,IAAI,YAAY,CAAC;YACtB,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE;oBACR,kBAAkB,EAAE,UAAU;oBAC9B,mBAAmB,EAAE,WAAW;iBACjC;aACF;YACD,KAAK,EAAE;gBACL,OAAO,EAAE,2BAA2B;gBACpC,cAAc,EAAE,eAAe;gBAC/B,eAAe,EAAE,UAAU,WAAW,EAAE;gBACxC,aAAa;gBACb,YAAY,EAAE,EAAE,gBAAgB,EAAE,kBAAkB,EAAE;gBACtD,cAAc;gBACd,aAAa;aACd;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@antseed/provider-claude-oauth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Third-party Anthropic Claude provider with OAuth authentication",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"prebuild": "rm -rf dist",
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@antseed/provider-core": "workspace:*"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@antseed/node": ">=0.1.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.5.0",
|
|
22
|
+
"vitest": "^2.0.0",
|
|
23
|
+
"@antseed/node": "workspace:*"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import plugin from './index.js';
|
|
3
|
+
|
|
4
|
+
describe('provider-claude-oauth plugin manifest', () => {
|
|
5
|
+
it('has correct plugin metadata', () => {
|
|
6
|
+
expect(plugin.name).toBe('claude-oauth');
|
|
7
|
+
expect(plugin.displayName).toBe('Claude (OAuth)');
|
|
8
|
+
expect(plugin.version).toBe('0.1.0');
|
|
9
|
+
expect(plugin.type).toBe('provider');
|
|
10
|
+
expect(plugin.description).toBe('Anthropic Claude with OAuth authentication (third-party example)');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('exposes configSchema with required fields', () => {
|
|
14
|
+
expect(plugin.configSchema).toBeDefined();
|
|
15
|
+
const keys = plugin.configSchema!.map(f => f.key);
|
|
16
|
+
expect(keys).toContain('CLAUDE_ACCESS_TOKEN');
|
|
17
|
+
expect(keys).toContain('CLAUDE_REFRESH_TOKEN');
|
|
18
|
+
expect(keys).toContain('CLAUDE_TOKEN_EXPIRES_AT');
|
|
19
|
+
expect(keys).toContain('ANTSEED_INPUT_USD_PER_MILLION');
|
|
20
|
+
expect(keys).toContain('ANTSEED_OUTPUT_USD_PER_MILLION');
|
|
21
|
+
expect(keys).toContain('ANTSEED_MAX_CONCURRENCY');
|
|
22
|
+
expect(keys).toContain('ANTSEED_ALLOWED_MODELS');
|
|
23
|
+
const accessField = plugin.configSchema!.find(f => f.key === 'CLAUDE_ACCESS_TOKEN');
|
|
24
|
+
expect(accessField!.required).toBe(true);
|
|
25
|
+
expect(accessField!.type).toBe('secret');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('createProvider', () => {
|
|
30
|
+
it('creates provider with access token only (static)', () => {
|
|
31
|
+
const provider = plugin.createProvider({
|
|
32
|
+
CLAUDE_ACCESS_TOKEN: 'test-access-token',
|
|
33
|
+
});
|
|
34
|
+
expect(provider).toBeDefined();
|
|
35
|
+
expect(provider.name).toBe('claude-oauth');
|
|
36
|
+
expect(provider.maxConcurrency).toBe(5);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('creates provider with access + refresh token (OAuth)', () => {
|
|
40
|
+
const provider = plugin.createProvider({
|
|
41
|
+
CLAUDE_ACCESS_TOKEN: 'test-access-token',
|
|
42
|
+
CLAUDE_REFRESH_TOKEN: 'test-refresh-token',
|
|
43
|
+
CLAUDE_TOKEN_EXPIRES_AT: String(Date.now() + 3600_000),
|
|
44
|
+
});
|
|
45
|
+
expect(provider).toBeDefined();
|
|
46
|
+
expect(provider.name).toBe('claude-oauth');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('rejects missing access token', () => {
|
|
50
|
+
expect(() => plugin.createProvider({})).toThrow('CLAUDE_ACCESS_TOKEN is required');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('provider has correct name and pricing', () => {
|
|
54
|
+
const provider = plugin.createProvider({
|
|
55
|
+
CLAUDE_ACCESS_TOKEN: 'test-access-token',
|
|
56
|
+
ANTSEED_INPUT_USD_PER_MILLION: '15',
|
|
57
|
+
ANTSEED_OUTPUT_USD_PER_MILLION: '30',
|
|
58
|
+
ANTSEED_MAX_CONCURRENCY: '3',
|
|
59
|
+
});
|
|
60
|
+
expect(provider.name).toBe('claude-oauth');
|
|
61
|
+
expect(provider.pricing).toEqual({
|
|
62
|
+
defaults: {
|
|
63
|
+
inputUsdPerMillion: 15,
|
|
64
|
+
outputUsdPerMillion: 30,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
expect(provider.maxConcurrency).toBe(3);
|
|
68
|
+
});
|
|
69
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AntseedProviderPlugin, ConfigField } from '@antseed/node';
|
|
2
|
+
import { BaseProvider, OAuthTokenProvider, StaticTokenProvider } from '@antseed/provider-core';
|
|
3
|
+
|
|
4
|
+
const configSchema: ConfigField[] = [
|
|
5
|
+
{ key: 'CLAUDE_ACCESS_TOKEN', label: 'Access Token', type: 'secret', required: true, description: 'Claude OAuth access token' },
|
|
6
|
+
{ key: 'CLAUDE_REFRESH_TOKEN', label: 'Refresh Token', type: 'secret', required: false, description: 'OAuth refresh token for auto-renewal' },
|
|
7
|
+
{ key: 'CLAUDE_TOKEN_EXPIRES_AT', label: 'Token Expiry', type: 'number', required: false, description: 'Epoch ms when access token expires' },
|
|
8
|
+
{ key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 10 },
|
|
9
|
+
{ key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 10 },
|
|
10
|
+
{ key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 5 },
|
|
11
|
+
{ key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const plugin: AntseedProviderPlugin = {
|
|
15
|
+
name: 'claude-oauth',
|
|
16
|
+
displayName: 'Claude (OAuth)',
|
|
17
|
+
version: '0.1.0',
|
|
18
|
+
type: 'provider',
|
|
19
|
+
description: 'Anthropic Claude with OAuth authentication (third-party example)',
|
|
20
|
+
configSchema,
|
|
21
|
+
configKeys: configSchema,
|
|
22
|
+
createProvider(config: Record<string, string>) {
|
|
23
|
+
const accessToken = config['CLAUDE_ACCESS_TOKEN'];
|
|
24
|
+
if (!accessToken) throw new Error('CLAUDE_ACCESS_TOKEN is required');
|
|
25
|
+
|
|
26
|
+
const refreshToken = config['CLAUDE_REFRESH_TOKEN'];
|
|
27
|
+
const expiresAt = config['CLAUDE_TOKEN_EXPIRES_AT']
|
|
28
|
+
? parseInt(config['CLAUDE_TOKEN_EXPIRES_AT'], 10)
|
|
29
|
+
: undefined;
|
|
30
|
+
|
|
31
|
+
const tokenProvider = refreshToken
|
|
32
|
+
? new OAuthTokenProvider({
|
|
33
|
+
accessToken,
|
|
34
|
+
refreshToken,
|
|
35
|
+
expiresAt: expiresAt ?? Date.now() + 3600_000,
|
|
36
|
+
tokenEndpoint: 'https://console.anthropic.com/v1/oauth/token',
|
|
37
|
+
})
|
|
38
|
+
: new StaticTokenProvider(accessToken);
|
|
39
|
+
|
|
40
|
+
const inputPrice = parseFloat(config['ANTSEED_INPUT_USD_PER_MILLION'] ?? '10');
|
|
41
|
+
const outputPrice = parseFloat(config['ANTSEED_OUTPUT_USD_PER_MILLION'] ?? '10');
|
|
42
|
+
const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '5', 10);
|
|
43
|
+
const allowedModels = (config['ANTSEED_ALLOWED_MODELS'] ?? '')
|
|
44
|
+
.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
+
|
|
46
|
+
return new BaseProvider({
|
|
47
|
+
name: 'claude-oauth',
|
|
48
|
+
models: allowedModels,
|
|
49
|
+
pricing: {
|
|
50
|
+
defaults: {
|
|
51
|
+
inputUsdPerMillion: inputPrice,
|
|
52
|
+
outputUsdPerMillion: outputPrice,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
relay: {
|
|
56
|
+
baseUrl: 'https://api.anthropic.com',
|
|
57
|
+
authHeaderName: 'authorization',
|
|
58
|
+
authHeaderValue: `Bearer ${accessToken}`,
|
|
59
|
+
tokenProvider,
|
|
60
|
+
extraHeaders: { 'anthropic-beta': 'oauth-2025-04-20' },
|
|
61
|
+
maxConcurrency,
|
|
62
|
+
allowedModels,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default plugin;
|