byok-llm 1.0.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 +242 -0
- package/dist/index.cjs +879 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +279 -0
- package/dist/index.d.ts +279 -0
- package/dist/index.js +848 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# byok-llm
|
|
2
|
+
|
|
3
|
+
Unified API key management for AI-powered apps. Zero dependencies.
|
|
4
|
+
|
|
5
|
+
Resolve, store, validate, and manage API keys across **8 AI providers** with a single interface. Built for CLI tools, agentic apps, and any Node.js project that needs to work with multiple AI services.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install byok-llm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Every AI-powered app needs API keys. Most hardcode `process.env.OPENAI_API_KEY` and call it a day. But real apps need:
|
|
14
|
+
|
|
15
|
+
- **Multiple providers** — Anthropic, OpenAI, Google, Groq, OpenRouter, xAI, Mistral, DeepSeek
|
|
16
|
+
- **Fallback chains** — if one provider is down, try the next
|
|
17
|
+
- **Key validation** — catch bad keys before they cause 401s at runtime
|
|
18
|
+
- **User setup** — a wizard that walks users through key configuration
|
|
19
|
+
- **Secure storage** — file permissions, never logged, XDG-compliant paths
|
|
20
|
+
- **No lock-in** — returns raw keys/headers/URLs, works with any HTTP client or SDK
|
|
21
|
+
|
|
22
|
+
`byok-ai` does all of this in a single zero-dependency package.
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { init } from 'byok-llm';
|
|
28
|
+
|
|
29
|
+
// Finds a configured provider, or runs the setup wizard
|
|
30
|
+
const provider = await init({
|
|
31
|
+
providers: ['anthropic', 'openai', 'openrouter'],
|
|
32
|
+
appName: 'my-cli-tool',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (provider) {
|
|
36
|
+
// provider.key, provider.headers, provider.baseUrl — ready to use
|
|
37
|
+
const response = await fetch(`${provider.baseUrl}/v1/messages`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { ...provider.headers, 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
model: 'claude-sonnet-4-20250514',
|
|
42
|
+
max_tokens: 100,
|
|
43
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Works with Any SDK
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import OpenAI from 'openai';
|
|
53
|
+
import { getOpenAICompatibleConfig } from 'byok-llm';
|
|
54
|
+
|
|
55
|
+
// Works with OpenRouter, Groq, xAI, Mistral, DeepSeek — anything OpenAI-compatible
|
|
56
|
+
const client = new OpenAI(await getOpenAICompatibleConfig('groq'));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Key Resolution
|
|
60
|
+
|
|
61
|
+
Keys are resolved from multiple sources, highest priority first:
|
|
62
|
+
|
|
63
|
+
1. **Runtime options** — passed directly in code
|
|
64
|
+
2. **Environment variables** — `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.
|
|
65
|
+
3. **Project config** — `.byokrc.json`, `.byokrc`, `byok.config.json`, or `byok` field in `package.json`
|
|
66
|
+
4. **User config** — `~/.config/byok-ai/config.json`
|
|
67
|
+
5. **Stored keys** — `~/.config/byok-ai/keys.json` (0600 permissions)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { resolveKey } from 'byok-llm';
|
|
71
|
+
|
|
72
|
+
const key = await resolveKey('anthropic');
|
|
73
|
+
// Checks all layers, returns { key, source, providerId } or null
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Fallback Chains
|
|
77
|
+
|
|
78
|
+
Try multiple providers in order, use the first one with a valid key:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { resolveWithFallback } from 'byok-llm';
|
|
82
|
+
|
|
83
|
+
const provider = await resolveWithFallback(['anthropic', 'openai', 'openrouter']);
|
|
84
|
+
// Returns the first provider that has a key configured
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Key Validation
|
|
88
|
+
|
|
89
|
+
Validate keys before using them — format check + live API ping:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { validateKey } from 'byok-llm';
|
|
93
|
+
|
|
94
|
+
const result = await validateKey('openai', 'sk-...');
|
|
95
|
+
// { valid: true } or { valid: false, error: 'Invalid key format' }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Validation is lightweight (hits `/v1/models` or equivalent), times out after 10 seconds, and gracefully falls back to format-only checks if the network is unreachable.
|
|
99
|
+
|
|
100
|
+
## Setup Wizard
|
|
101
|
+
|
|
102
|
+
Interactive terminal wizard for first-run key configuration:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { setupWizard } from 'byok-llm';
|
|
106
|
+
|
|
107
|
+
await setupWizard({
|
|
108
|
+
providers: ['anthropic', 'openai', 'openrouter'],
|
|
109
|
+
required: ['anthropic'],
|
|
110
|
+
appName: 'my-app',
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The wizard shows already-configured providers, links to docs for getting keys, validates on entry, and stores keys securely.
|
|
115
|
+
|
|
116
|
+
## Config Files
|
|
117
|
+
|
|
118
|
+
Create a `.byokrc.json` in your project:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"defaultProvider": "anthropic",
|
|
123
|
+
"fallbackOrder": ["anthropic", "openai", "groq"],
|
|
124
|
+
"providers": {
|
|
125
|
+
"anthropic": {
|
|
126
|
+
"apiKey": "{env:MY_ANTHROPIC_KEY}"
|
|
127
|
+
},
|
|
128
|
+
"openrouter": {
|
|
129
|
+
"baseUrl": "https://openrouter.ai/api/v1"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Values support `{env:VAR_NAME}` and `{file:/path/to/key}` placeholders.
|
|
136
|
+
|
|
137
|
+
## Custom Providers
|
|
138
|
+
|
|
139
|
+
Register any OpenAI-compatible or custom provider:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { registerProvider, resolveKey } from 'byok-llm';
|
|
143
|
+
|
|
144
|
+
registerProvider({
|
|
145
|
+
id: 'my-ollama',
|
|
146
|
+
name: 'Local Ollama',
|
|
147
|
+
envKeys: ['OLLAMA_API_KEY'],
|
|
148
|
+
baseUrl: 'http://localhost:11434',
|
|
149
|
+
docsUrl: 'https://ollama.ai',
|
|
150
|
+
authStyle: 'bearer',
|
|
151
|
+
openaiCompatible: true,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const key = await resolveKey('my-ollama');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Status Report
|
|
158
|
+
|
|
159
|
+
Check which providers are configured:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { printStatus } from 'byok-llm';
|
|
163
|
+
|
|
164
|
+
await printStatus();
|
|
165
|
+
// Prints a colored table of all providers and their status
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Secure Storage
|
|
169
|
+
|
|
170
|
+
Keys stored via the wizard or `storeKey()` go to `~/.config/byok-ai/keys.json` with **0600 file permissions** (owner read/write only). The directory is created with 0700 permissions. Keys are never logged.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { storeKey, getKey, removeKey } from 'byok-llm';
|
|
174
|
+
|
|
175
|
+
await storeKey('anthropic', 'sk-ant-...');
|
|
176
|
+
const key = await getKey('anthropic');
|
|
177
|
+
await removeKey('anthropic');
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Built-in Providers
|
|
181
|
+
|
|
182
|
+
| Provider | ID | Env Variable | Auth Style |
|
|
183
|
+
|----------|-----|-------------|------------|
|
|
184
|
+
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | `x-api-key` |
|
|
185
|
+
| OpenAI | `openai` | `OPENAI_API_KEY` | `bearer` |
|
|
186
|
+
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | `bearer` |
|
|
187
|
+
| Google Gemini | `google` | `GOOGLE_API_KEY` | `query-param` |
|
|
188
|
+
| Groq | `groq` | `GROQ_API_KEY` | `bearer` |
|
|
189
|
+
| xAI | `xai` | `XAI_API_KEY` | `bearer` |
|
|
190
|
+
| Mistral | `mistral` | `MISTRAL_API_KEY` | `bearer` |
|
|
191
|
+
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `bearer` |
|
|
192
|
+
|
|
193
|
+
All providers except Anthropic and Google are OpenAI-SDK-compatible.
|
|
194
|
+
|
|
195
|
+
## API Reference
|
|
196
|
+
|
|
197
|
+
### Initialization
|
|
198
|
+
- **`init(options?)`** — All-in-one: load config, resolve key, run wizard if needed
|
|
199
|
+
- **`initConfig(options?)`** — Initialize config cache manually
|
|
200
|
+
|
|
201
|
+
### Key Resolution
|
|
202
|
+
- **`resolveKey(provider, runtimeKey?)`** — Resolve a key from all layers
|
|
203
|
+
- **`resolveWithFallback(providers?)`** — Try providers in order, return first match
|
|
204
|
+
- **`getConfiguredProviders()`** — List all providers with keys configured
|
|
205
|
+
|
|
206
|
+
### Validation
|
|
207
|
+
- **`validateKey(provider, key)`** — Format check + API ping
|
|
208
|
+
- **`validateKeyFormat(provider, key)`** — Format check only
|
|
209
|
+
- **`validateAllKeys()`** — Validate all configured keys
|
|
210
|
+
|
|
211
|
+
### Storage
|
|
212
|
+
- **`storeKey(provider, key)`** — Store a key securely
|
|
213
|
+
- **`getKey(provider)`** — Retrieve a stored key
|
|
214
|
+
- **`removeKey(provider)`** — Delete a stored key
|
|
215
|
+
- **`listStoredKeys()`** — List all stored provider IDs
|
|
216
|
+
- **`clearAllKeys()`** — Delete all stored keys
|
|
217
|
+
|
|
218
|
+
### Integration Helpers
|
|
219
|
+
- **`getProviderHeaders(provider)`** — Get auth headers for a provider
|
|
220
|
+
- **`getProviderBaseUrl(provider)`** — Get the base URL
|
|
221
|
+
- **`getOpenAICompatibleConfig(provider)`** — Get `{ apiKey, baseURL }` for the OpenAI SDK
|
|
222
|
+
|
|
223
|
+
### Provider Registry
|
|
224
|
+
- **`registerProvider(definition)`** — Register a custom provider
|
|
225
|
+
- **`getProvider(id)`** — Get a provider definition
|
|
226
|
+
- **`listProviders()`** — List all registered providers
|
|
227
|
+
- **`hasProvider(id)`** — Check if a provider is registered
|
|
228
|
+
|
|
229
|
+
### Environment
|
|
230
|
+
- **`hasEnvKey(provider)`** — Check if an env var is set
|
|
231
|
+
- **`getEnvKey(provider)`** — Get the env var value
|
|
232
|
+
- **`getEnvVarName(provider)`** — Get the primary env var name
|
|
233
|
+
- **`generateEnvTemplate(providers?)`** — Generate a `.env` template
|
|
234
|
+
|
|
235
|
+
### UI
|
|
236
|
+
- **`setupWizard(options?)`** — Run the interactive setup wizard
|
|
237
|
+
- **`getStatus(providers?)`** — Get provider statuses as data
|
|
238
|
+
- **`printStatus(providers?)`** — Print colored status to terminal
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT
|