litellmts-core 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/LICENSE +674 -0
- package/README.md +266 -0
- package/dist/auth/anthropic.d.ts +7 -0
- package/dist/auth/anthropic.js +83 -0
- package/dist/auth/copilot.d.ts +9 -0
- package/dist/auth/copilot.js +138 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/refresh.d.ts +2 -0
- package/dist/auth/refresh.js +54 -0
- package/dist/auth/store.d.ts +19 -0
- package/dist/auth/store.js +91 -0
- package/dist/bin/litellm.d.ts +2 -0
- package/dist/bin/litellm.js +45 -0
- package/dist/completion.d.ts +24 -0
- package/dist/completion.js +15 -0
- package/dist/embedding.d.ts +15 -0
- package/dist/embedding.js +26 -0
- package/dist/handlers/ai21.d.ts +2 -0
- package/dist/handlers/ai21.js +87 -0
- package/dist/handlers/anthropic.d.ts +2 -0
- package/dist/handlers/anthropic.js +85 -0
- package/dist/handlers/cohere.d.ts +2 -0
- package/dist/handlers/cohere.js +85 -0
- package/dist/handlers/copilot.d.ts +2 -0
- package/dist/handlers/copilot.js +136 -0
- package/dist/handlers/deepinfra.d.ts +2 -0
- package/dist/handlers/deepinfra.js +56 -0
- package/dist/handlers/gemini.d.ts +2 -0
- package/dist/handlers/gemini.js +102 -0
- package/dist/handlers/geminiEmbedding.d.ts +2 -0
- package/dist/handlers/geminiEmbedding.js +29 -0
- package/dist/handlers/getHandler.d.ts +11 -0
- package/dist/handlers/getHandler.js +24 -0
- package/dist/handlers/index.d.ts +16 -0
- package/dist/handlers/index.js +18 -0
- package/dist/handlers/mistral.d.ts +2 -0
- package/dist/handlers/mistral.js +56 -0
- package/dist/handlers/mistralEmbedding.d.ts +2 -0
- package/dist/handlers/mistralEmbedding.js +31 -0
- package/dist/handlers/ollama.d.ts +2 -0
- package/dist/handlers/ollama.js +89 -0
- package/dist/handlers/ollamaEmbedding.d.ts +2 -0
- package/dist/handlers/ollamaEmbedding.js +36 -0
- package/dist/handlers/openai.d.ts +2 -0
- package/dist/handlers/openai.js +76 -0
- package/dist/handlers/openaiEmbedding.d.ts +2 -0
- package/dist/handlers/openaiEmbedding.js +18 -0
- package/dist/handlers/openaiLike.d.ts +3 -0
- package/dist/handlers/openaiLike.js +22 -0
- package/dist/handlers/openaiLikeEmbedding.d.ts +3 -0
- package/dist/handlers/openaiLikeEmbedding.js +22 -0
- package/dist/handlers/replicate.d.ts +2 -0
- package/dist/handlers/replicate.js +98 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +19 -0
- package/dist/mappings/openaiLike.d.ts +6 -0
- package/dist/mappings/openaiLike.js +190 -0
- package/dist/registry.d.ts +15 -0
- package/dist/registry.js +30 -0
- package/dist/types.d.ts +104 -0
- package/dist/types.js +2 -0
- package/dist/utils/combinePrompts.d.ts +10 -0
- package/dist/utils/combinePrompts.js +19 -0
- package/dist/utils/encoders.d.ts +2 -0
- package/dist/utils/encoders.js +9 -0
- package/dist/utils/getUnixTimestamp.d.ts +1 -0
- package/dist/utils/getUnixTimestamp.js +6 -0
- package/dist/utils/sse.d.ts +12 -0
- package/dist/utils/sse.js +41 -0
- package/dist/utils/toUsage.d.ts +12 -0
- package/dist/utils/toUsage.js +35 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/madkoding/litellmTS/main/.github/banner.png" alt="LiteLLM.ts" width="600"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Unified TypeScript interface for 45+ LLM providers</strong><br>
|
|
7
|
+
One API. Every model. Zero boilerplate.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://github.com/madkoding/litellmTS/blob/main/LICENSE">
|
|
12
|
+
<img src="https://img.shields.io/badge/license-GPLv3-blue.svg" alt="License"/>
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://github.com/madkoding/litellmTS">
|
|
15
|
+
<img src="https://img.shields.io/github/v/release/madkoding/litellmTS" alt="GitHub Release"/>
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://nodejs.org/">
|
|
18
|
+
<img src="https://img.shields.io/badge/node-%3E%3D22-brightgreen" alt="Node"/>
|
|
19
|
+
</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### From GitHub (recommended)
|
|
27
|
+
|
|
28
|
+
Add this to your `package.json`:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@litellmts/core": "github:madkoding/litellmTS"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then install:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Alternative — npm (when published)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install @litellmts/core
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { completion } from '@litellmts/core';
|
|
54
|
+
|
|
55
|
+
const response = await completion({
|
|
56
|
+
model: 'gpt-4o-mini',
|
|
57
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log(response.choices[0].message.content);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Swap providers by changing just the model string:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// Same code, different provider:
|
|
67
|
+
await completion({ model: 'claude-sonnet-4-20250514', ... });
|
|
68
|
+
await completion({ model: 'gemini/gemini-2.5-pro', ... });
|
|
69
|
+
await completion({ model: 'groq/llama-3.3-70b', ... });
|
|
70
|
+
await completion({ model: 'deepseek/deepseek-chat', ... });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Features
|
|
74
|
+
|
|
75
|
+
- **Unified API** — same `completion()` / `embedding()` for every provider
|
|
76
|
+
- **Streaming** — all providers support `stream: true`
|
|
77
|
+
- **TypeScript first** — full type safety with auto-completion
|
|
78
|
+
- **45+ providers** — from OpenAI to niche OpenAI-compatible APIs
|
|
79
|
+
- **No SDK sprawl** — one dependency replaces 10+ vendor SDKs
|
|
80
|
+
- **CLI auth** — built-in OAuth device flow for GitHub Copilot
|
|
81
|
+
- **Persistent auth store** — `~/.litellm/auth.json`
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
### Non-streaming
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { completion } from '@litellmts/core';
|
|
89
|
+
|
|
90
|
+
const response = await completion({
|
|
91
|
+
model: 'gpt-4o-mini',
|
|
92
|
+
messages: [
|
|
93
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
94
|
+
{ role: 'user', content: 'What is TypeScript?' },
|
|
95
|
+
],
|
|
96
|
+
temperature: 0.7,
|
|
97
|
+
max_tokens: 500,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(response.choices[0].message.content);
|
|
101
|
+
// { role: 'assistant', content: 'TypeScript is...' }
|
|
102
|
+
console.log(response.usage);
|
|
103
|
+
// { prompt_tokens: 25, completion_tokens: 120, total_tokens: 145 }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Streaming
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const stream = await completion({
|
|
110
|
+
model: 'claude-sonnet-4-20250514',
|
|
111
|
+
messages: [{ role: 'user', content: 'Write a poem' }],
|
|
112
|
+
stream: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
for await (const chunk of stream) {
|
|
116
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Embeddings
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { embedding } from '@litellmts/core';
|
|
124
|
+
|
|
125
|
+
const result = await embedding({
|
|
126
|
+
model: 'text-embedding-3-small',
|
|
127
|
+
input: 'Hello world',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
console.log(result.data[0].embedding); // number[]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### API Keys
|
|
134
|
+
|
|
135
|
+
Keys are read from environment variables by default:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
export OPENAI_API_KEY=sk-...
|
|
139
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
140
|
+
export GROQ_API_KEY=gsk_...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Or pass them inline:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
await completion({
|
|
147
|
+
model: 'groq/llama-3.3-70b',
|
|
148
|
+
messages: [],
|
|
149
|
+
apiKey: 'gsk_...',
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### CLI Auth
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# GitHub Copilot (OAuth device flow)
|
|
157
|
+
npx litellm login copilot
|
|
158
|
+
|
|
159
|
+
# Anthropic (API key prompt)
|
|
160
|
+
npx litellm login anthropic
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Supported Providers
|
|
164
|
+
|
|
165
|
+
### Dedicated Handlers
|
|
166
|
+
|
|
167
|
+
| Provider | Prefix | Completion | Streaming | Embedding | API Key Env |
|
|
168
|
+
|---|---|---|---|---|---|
|
|
169
|
+
| OpenAI | `gpt-*`, `openai/` | ✅ | ✅ | ✅ | `OPENAI_API_KEY` |
|
|
170
|
+
| Anthropic | `claude-*` | ✅ | ✅ | ❌ | `ANTHROPIC_API_KEY` |
|
|
171
|
+
| Google Gemini | `gemini/` | ✅ | ✅ | ✅ | `GEMINI_API_KEY` |
|
|
172
|
+
| GitHub Copilot | `copilot/` | ✅ | ✅ | ❌ | (OAuth) |
|
|
173
|
+
| Mistral | `mistral/` | ✅ | ✅ | ✅ | `MISTRAL_API_KEY` |
|
|
174
|
+
| Cohere | `command*` | ✅ | ✅ | ❌ | `COHERE_API_KEY` |
|
|
175
|
+
| DeepInfra | `deepinfra/` | ✅ | ✅ | ❌ | `DEEPINFRA_API_KEY` |
|
|
176
|
+
| Replicate | `replicate/` | ✅ | ✅ | ❌ | `REPLICATE_API_KEY` |
|
|
177
|
+
| AI21 Labs | `j2-*`, `ai21/` | ✅ | ✅ | ❌ | `AI21_API_KEY` |
|
|
178
|
+
| Ollama (local) | `ollama/` | ✅ | ✅ | ✅ | — |
|
|
179
|
+
|
|
180
|
+
### OpenAI-Compatible (38 providers)
|
|
181
|
+
|
|
182
|
+
| Provider | Prefix | API Key Env |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| Groq | `groq/` | `GROQ_API_KEY` |
|
|
185
|
+
| DeepSeek | `deepseek/` | `DEEPSEEK_API_KEY` |
|
|
186
|
+
| Perplexity | `perplexity/` | `PERPLEXITY_API_KEY` |
|
|
187
|
+
| X AI (Grok) | `xai/` | `XAI_API_KEY` |
|
|
188
|
+
| OpenRouter | `openrouter/` | `OPENROUTER_API_KEY` |
|
|
189
|
+
| Together AI | `together/` | `TOGETHER_API_KEY` |
|
|
190
|
+
| Fireworks AI | `fireworks/` | `FIREWORKS_API_KEY` |
|
|
191
|
+
| Cerebras | `cerebras/` | `CEREBRAS_API_KEY` |
|
|
192
|
+
| SambaNova | `sambanova/` | `SAMBANOVA_API_KEY` |
|
|
193
|
+
| Nebius AI | `nebius/` | `NEBIUS_API_KEY` |
|
|
194
|
+
| Hyperbolic | `hyperbolic/` | `HYPERBOLIC_API_KEY` |
|
|
195
|
+
| Novita AI | `novita/` | `NOVITA_API_KEY` |
|
|
196
|
+
| GitHub Models | `github/` | `GITHUB_TOKEN` |
|
|
197
|
+
| Anyscale | `anyscale/` | `ANYSCALE_API_KEY` |
|
|
198
|
+
| NVIDIA NIM | `nvidia_nim/` | `NVIDIA_API_KEY` |
|
|
199
|
+
| Codestral | `codestral/` | `CODESTRAL_API_KEY` |
|
|
200
|
+
| Moonshot | `moonshot/` | `MOONSHOT_API_KEY` |
|
|
201
|
+
| DashScope (Alibaba) | `dashscope/` | `DASHSCOPE_API_KEY` |
|
|
202
|
+
| Meta Llama | `meta_llama/` | `LLAMA_API_KEY` |
|
|
203
|
+
| Featherless AI | `featherless/` | `FEATHERLESS_API_KEY` |
|
|
204
|
+
| Nscale | `nscale/` | `NSCALE_API_KEY` |
|
|
205
|
+
| Inception Labs | `inception/` | `INCEPTION_API_KEY` |
|
|
206
|
+
| Morph LLM | `morph/` | `MORPH_API_KEY` |
|
|
207
|
+
| Lambda AI | `lambda/` | `LAMBDA_API_KEY` |
|
|
208
|
+
| AIML API | `aiml/` | `AIML_API_KEY` |
|
|
209
|
+
| Weights & Biases | `wandb/` | `WANDB_API_KEY` |
|
|
210
|
+
| Volcengine | `volcengine/` | `VOLCENGINE_API_KEY` |
|
|
211
|
+
| Galadriel | `galadriel/` | `GALADRIEL_API_KEY` |
|
|
212
|
+
| Empower | `empower/` | `EMPOWER_API_KEY` |
|
|
213
|
+
| Friendli AI | `friendliai/` | `FRIENDLI_API_KEY` |
|
|
214
|
+
| Helicone | `helicone/` | `HELICONE_API_KEY` |
|
|
215
|
+
| Vercel AI Gateway | `vercel_ai/` | `VERCEL_AI_GATEWAY_API_KEY` |
|
|
216
|
+
| Clarifai | `clarifai/` | `CLARIFAI_API_KEY` |
|
|
217
|
+
| Baseten | `baseten/` | `BASETEN_API_KEY` |
|
|
218
|
+
| PublicAI | `publicai/` | `PUBLICAI_API_KEY` |
|
|
219
|
+
| Venice AI | `venice/` | `VENICE_API_KEY` |
|
|
220
|
+
|
|
221
|
+
## Architecture
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
┌──────────────┐ ┌──────────────┐ ┌─────────────────┐
|
|
225
|
+
│ completion() │────▶│ getHandler() │────▶│ OpenAIHandler │
|
|
226
|
+
│ embedding() │ │ (prefix │ │ AnthropicHandler│
|
|
227
|
+
│ │ │ matching) │ │ GeminiHandler │
|
|
228
|
+
│ │ │ │ │ OpenAILikeHandler│
|
|
229
|
+
└──────────────┘ └──────────────┘ └─────────────────┘
|
|
230
|
+
│
|
|
231
|
+
┌──────┴──────┐
|
|
232
|
+
│ Model Map │
|
|
233
|
+
│ groq/ → ... │
|
|
234
|
+
│ claude- → . │
|
|
235
|
+
│ gpt- → ... │
|
|
236
|
+
└─────────────┘
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Clone
|
|
243
|
+
git clone https://github.com/madkoding/litellmTS.git
|
|
244
|
+
cd litellmTS
|
|
245
|
+
|
|
246
|
+
# Install
|
|
247
|
+
npm install
|
|
248
|
+
|
|
249
|
+
# Run unit tests
|
|
250
|
+
npm t
|
|
251
|
+
|
|
252
|
+
# Run E2E tests (requires API keys in .env)
|
|
253
|
+
cp .example.env .env
|
|
254
|
+
npm run test:e2e
|
|
255
|
+
|
|
256
|
+
# Build
|
|
257
|
+
npm run build
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
GNU General Public License v3.0 — see [LICENSE](LICENSE).
|
|
263
|
+
|
|
264
|
+
## Why not just use the Python version?
|
|
265
|
+
|
|
266
|
+
litellmTS is a native TypeScript implementation designed for Node.js/Deno/Bun environments. It has zero Python dependencies and integrates naturally with the TypeScript ecosystem (type safety, IDE autocompletion, etc.). Think of it as litellm, but for the JS/TS world.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI prompt to configure and validate an Anthropic API key.
|
|
3
|
+
*
|
|
4
|
+
* Prompts the user to paste a key, validates it with a lightweight API call,
|
|
5
|
+
* and persists to `~/.litellm/auth.json`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loginAnthropic(): Promise<void>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loginAnthropic = loginAnthropic;
|
|
37
|
+
/* eslint-disable no-console */
|
|
38
|
+
const readline = __importStar(require("node:readline/promises"));
|
|
39
|
+
const node_process_1 = require("node:process");
|
|
40
|
+
const store_1 = require("./store");
|
|
41
|
+
/**
|
|
42
|
+
* Interactive CLI prompt to configure and validate an Anthropic API key.
|
|
43
|
+
*
|
|
44
|
+
* Prompts the user to paste a key, validates it with a lightweight API call,
|
|
45
|
+
* and persists to `~/.litellm/auth.json`.
|
|
46
|
+
*/
|
|
47
|
+
async function loginAnthropic() {
|
|
48
|
+
console.log('\n🔑 Configurando clave API de Anthropic...\n');
|
|
49
|
+
console.log(' 1. Abre https://console.anthropic.com/ en tu navegador');
|
|
50
|
+
console.log(' 2. Ve a API Keys → Create Key');
|
|
51
|
+
console.log(' 3. Copia la key (comienza con "sk-ant-")\n');
|
|
52
|
+
const rl = readline.createInterface({ input: node_process_1.stdin, output: node_process_1.stdout });
|
|
53
|
+
const apiKey = await rl.question('✏️ Pega tu API key: ');
|
|
54
|
+
rl.close();
|
|
55
|
+
const trimmed = apiKey.trim();
|
|
56
|
+
if (!trimmed) {
|
|
57
|
+
throw new Error('No se ingresó ninguna key');
|
|
58
|
+
}
|
|
59
|
+
if (!trimmed.startsWith('sk-ant-')) {
|
|
60
|
+
console.warn('⚠️ La key no comienza con "sk-ant-". Asegúrate de haber copiado la key correcta.');
|
|
61
|
+
}
|
|
62
|
+
// Validate with a lightweight API call
|
|
63
|
+
console.log('🔄 Validando key...');
|
|
64
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'x-api-key': trimmed,
|
|
69
|
+
'anthropic-version': '2023-06-01',
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
model: 'claude-3-haiku-20240307',
|
|
73
|
+
max_tokens: 1,
|
|
74
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const text = await res.text();
|
|
79
|
+
throw new Error(`Key inválida. Respuesta de Anthropic: ${res.status} ${res.statusText}\n${text}`);
|
|
80
|
+
}
|
|
81
|
+
await (0, store_1.setAnthropicCredentials)({ apiKey: trimmed });
|
|
82
|
+
console.log('✅ Key de Anthropic guardada exitosamente en ~/.litellm/auth.json');
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start the GitHub Copilot OAuth device-code login flow.
|
|
3
|
+
*
|
|
4
|
+
* Opens the browser for user authorization, polls for the GitHub access token,
|
|
5
|
+
* exchanges it for a Copilot token, and persists credentials to `~/.litellm/auth.json`.
|
|
6
|
+
*
|
|
7
|
+
* @param deployment - GitHub deployment URL (default: `'github.com'`)
|
|
8
|
+
*/
|
|
9
|
+
export declare function login(deployment?: string): Promise<void>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.login = login;
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const store_1 = require("./store");
|
|
7
|
+
const CLIENT_ID = 'Iv1.b507a08c87ecfe98';
|
|
8
|
+
const SCOPES = 'read:user';
|
|
9
|
+
const COPILOT_API = 'https://api.githubcopilot.com';
|
|
10
|
+
const USER_AGENT = 'GitHubCopilotChat/0.35.0';
|
|
11
|
+
const EDITOR_VERSION = 'vscode/1.107.0';
|
|
12
|
+
const EDITOR_PLUGIN_VERSION = 'copilot-chat/0.35.0';
|
|
13
|
+
const COPILOT_INTEGRATION_ID = 'vscode-chat';
|
|
14
|
+
async function sleep(ms) {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
}
|
|
17
|
+
function openBrowser(url) {
|
|
18
|
+
const platform = process.platform;
|
|
19
|
+
try {
|
|
20
|
+
if (platform === 'darwin') {
|
|
21
|
+
(0, node_child_process_1.execSync)(`open "${url}"`, { stdio: 'ignore' });
|
|
22
|
+
}
|
|
23
|
+
else if (platform === 'win32') {
|
|
24
|
+
(0, node_child_process_1.execSync)(`start "" "${url}"`, { stdio: 'ignore' });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
(0, node_child_process_1.execSync)(`xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null || x-www-browser "${url}"`, { stdio: 'ignore' });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// browser open failed, user can open manually
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function requestDeviceCode() {
|
|
35
|
+
const res = await fetch('https://github.com/login/device/code', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
Accept: 'application/json',
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'User-Agent': USER_AGENT,
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
client_id: CLIENT_ID,
|
|
44
|
+
scope: SCOPES,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`Error al solicitar device code: ${res.status}`);
|
|
49
|
+
}
|
|
50
|
+
return res.json();
|
|
51
|
+
}
|
|
52
|
+
async function pollAccessToken(deviceCode, interval) {
|
|
53
|
+
const body = {
|
|
54
|
+
client_id: CLIENT_ID,
|
|
55
|
+
device_code: deviceCode,
|
|
56
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
57
|
+
};
|
|
58
|
+
let attempts = 0;
|
|
59
|
+
const maxAttempts = 120; // ~10 min max
|
|
60
|
+
while (attempts < maxAttempts) {
|
|
61
|
+
attempts++;
|
|
62
|
+
await sleep(interval * 1000);
|
|
63
|
+
const res = await fetch('https://github.com/login/oauth/access_token', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
Accept: 'application/json',
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'User-Agent': USER_AGENT,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
throw new Error(`Error en polling: ${res.status}`);
|
|
74
|
+
}
|
|
75
|
+
const data = (await res.json());
|
|
76
|
+
if (data.access_token) {
|
|
77
|
+
return data.access_token;
|
|
78
|
+
}
|
|
79
|
+
if (data.error === 'authorization_pending') {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (data.error === 'slow_down') {
|
|
83
|
+
interval += 5;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (data.error === 'expired_token' ||
|
|
87
|
+
data.error === 'access_denied') {
|
|
88
|
+
throw new Error(data.error_description ?? `Autenticación cancelada: ${data.error}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new Error('Tiempo de espera agotado para la autenticación');
|
|
92
|
+
}
|
|
93
|
+
async function exchangeCopilotToken(githubToken) {
|
|
94
|
+
const res = await fetch(`${COPILOT_API}/copilot_internal/v2/token`, {
|
|
95
|
+
headers: {
|
|
96
|
+
Authorization: `Bearer ${githubToken}`,
|
|
97
|
+
'User-Agent': USER_AGENT,
|
|
98
|
+
'Editor-Version': EDITOR_VERSION,
|
|
99
|
+
'Editor-Plugin-Version': EDITOR_PLUGIN_VERSION,
|
|
100
|
+
'Copilot-Integration-Id': COPILOT_INTEGRATION_ID,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
throw new Error(`Error al obtener token de Copilot: ${res.status} ${res.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const data = (await res.json());
|
|
107
|
+
if (data.error) {
|
|
108
|
+
throw new Error(data.error_description ?? `Error de Copilot: ${data.error}`);
|
|
109
|
+
}
|
|
110
|
+
return { token: data.token, expires_at: data.expires_at };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Start the GitHub Copilot OAuth device-code login flow.
|
|
114
|
+
*
|
|
115
|
+
* Opens the browser for user authorization, polls for the GitHub access token,
|
|
116
|
+
* exchanges it for a Copilot token, and persists credentials to `~/.litellm/auth.json`.
|
|
117
|
+
*
|
|
118
|
+
* @param deployment - GitHub deployment URL (default: `'github.com'`)
|
|
119
|
+
*/
|
|
120
|
+
async function login(deployment = 'github.com') {
|
|
121
|
+
console.log('\n🔐 Iniciando sesión en GitHub Copilot...\n');
|
|
122
|
+
const deviceCode = await requestDeviceCode();
|
|
123
|
+
console.log(`✏️ Código de verificación: ${deviceCode.user_code}`);
|
|
124
|
+
console.log(`🌐 Abriendo ${deviceCode.verification_uri} en tu navegador...\n`);
|
|
125
|
+
openBrowser(deviceCode.verification_uri);
|
|
126
|
+
console.log('⏳ Esperando autorización...');
|
|
127
|
+
const githubToken = await pollAccessToken(deviceCode.device_code, deviceCode.interval);
|
|
128
|
+
console.log('✅ Autorización de GitHub exitosa');
|
|
129
|
+
console.log('🔄 Obteniendo token de Copilot...');
|
|
130
|
+
const copilotToken = await exchangeCopilotToken(githubToken);
|
|
131
|
+
await (0, store_1.setCopilotCredentials)({
|
|
132
|
+
githubToken,
|
|
133
|
+
copilotToken: copilotToken.token,
|
|
134
|
+
expiresAt: copilotToken.expires_at * 1000,
|
|
135
|
+
enterpriseUrl: deployment !== 'github.com' ? deployment : undefined,
|
|
136
|
+
});
|
|
137
|
+
console.log('✅ Autenticación con GitHub Copilot completada exitosamente');
|
|
138
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { login } from './copilot';
|
|
2
|
+
export { loginAnthropic } from './anthropic';
|
|
3
|
+
export { getValidToken, getAnthropicKey } from './refresh';
|
|
4
|
+
export { clearCredentials, getProviderCredentials, setProviderCredentials, } from './store';
|
|
5
|
+
/** @deprecated Import named functions directly from '@litellmts/core' */
|
|
6
|
+
export type { CopilotCredentials, AnthropicCredentials } from './store';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setProviderCredentials = exports.getProviderCredentials = exports.clearCredentials = exports.getAnthropicKey = exports.getValidToken = exports.loginAnthropic = exports.login = void 0;
|
|
4
|
+
var copilot_1 = require("./copilot");
|
|
5
|
+
Object.defineProperty(exports, "login", { enumerable: true, get: function () { return copilot_1.login; } });
|
|
6
|
+
var anthropic_1 = require("./anthropic");
|
|
7
|
+
Object.defineProperty(exports, "loginAnthropic", { enumerable: true, get: function () { return anthropic_1.loginAnthropic; } });
|
|
8
|
+
var refresh_1 = require("./refresh");
|
|
9
|
+
Object.defineProperty(exports, "getValidToken", { enumerable: true, get: function () { return refresh_1.getValidToken; } });
|
|
10
|
+
Object.defineProperty(exports, "getAnthropicKey", { enumerable: true, get: function () { return refresh_1.getAnthropicKey; } });
|
|
11
|
+
var store_1 = require("./store");
|
|
12
|
+
Object.defineProperty(exports, "clearCredentials", { enumerable: true, get: function () { return store_1.clearCredentials; } });
|
|
13
|
+
Object.defineProperty(exports, "getProviderCredentials", { enumerable: true, get: function () { return store_1.getProviderCredentials; } });
|
|
14
|
+
Object.defineProperty(exports, "setProviderCredentials", { enumerable: true, get: function () { return store_1.setProviderCredentials; } });
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getValidToken = getValidToken;
|
|
4
|
+
exports.getAnthropicKey = getAnthropicKey;
|
|
5
|
+
const store_1 = require("./store");
|
|
6
|
+
const COPILOT_API = 'https://api.githubcopilot.com';
|
|
7
|
+
const USER_AGENT = 'GitHubCopilotChat/0.35.0';
|
|
8
|
+
const EDITOR_VERSION = 'vscode/1.107.0';
|
|
9
|
+
const EDITOR_PLUGIN_VERSION = 'copilot-chat/0.35.0';
|
|
10
|
+
const COPILOT_INTEGRATION_ID = 'vscode-chat';
|
|
11
|
+
async function exchangeCopilotToken(githubToken) {
|
|
12
|
+
const res = await fetch(`${COPILOT_API}/copilot_internal/v2/token`, {
|
|
13
|
+
headers: {
|
|
14
|
+
Authorization: `Bearer ${githubToken}`,
|
|
15
|
+
'User-Agent': USER_AGENT,
|
|
16
|
+
'Editor-Version': EDITOR_VERSION,
|
|
17
|
+
'Editor-Plugin-Version': EDITOR_PLUGIN_VERSION,
|
|
18
|
+
'Copilot-Integration-Id': COPILOT_INTEGRATION_ID,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`Error al refrescar token de Copilot: ${res.status} ${res.statusText}`);
|
|
23
|
+
}
|
|
24
|
+
const data = (await res.json());
|
|
25
|
+
if (data.error) {
|
|
26
|
+
throw new Error(`Error al refrescar token: ${data.error}`);
|
|
27
|
+
}
|
|
28
|
+
return { token: data.token, expires_at: data.expires_at };
|
|
29
|
+
}
|
|
30
|
+
async function getValidToken() {
|
|
31
|
+
const creds = await (0, store_1.getCopilotCredentials)();
|
|
32
|
+
if (!creds?.copilotToken || !creds?.githubToken) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (creds.expiresAt - 5 * 60 * 1000 < Date.now()) {
|
|
36
|
+
try {
|
|
37
|
+
const newToken = await exchangeCopilotToken(creds.githubToken);
|
|
38
|
+
await (0, store_1.setCopilotCredentials)({
|
|
39
|
+
...creds,
|
|
40
|
+
copilotToken: newToken.token,
|
|
41
|
+
expiresAt: newToken.expires_at * 1000,
|
|
42
|
+
});
|
|
43
|
+
return newToken.token;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return creds.copilotToken;
|
|
50
|
+
}
|
|
51
|
+
async function getAnthropicKey() {
|
|
52
|
+
const creds = await (0, store_1.getAnthropicCredentials)();
|
|
53
|
+
return creds?.apiKey ?? null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** GitHub Copilot OAuth credentials. */
|
|
2
|
+
export interface CopilotCredentials {
|
|
3
|
+
githubToken: string;
|
|
4
|
+
copilotToken: string;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
enterpriseUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Stored Anthropic API key. */
|
|
9
|
+
export interface AnthropicCredentials {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
}
|
|
12
|
+
export type ProviderCredentials = Record<string, unknown>;
|
|
13
|
+
export declare function getProviderCredentials<T>(provider: string): Promise<T | null>;
|
|
14
|
+
export declare function setProviderCredentials(provider: string, creds: Record<string, unknown>): Promise<void>;
|
|
15
|
+
export declare function getCopilotCredentials(): Promise<CopilotCredentials | null>;
|
|
16
|
+
export declare function setCopilotCredentials(creds: CopilotCredentials): Promise<void>;
|
|
17
|
+
export declare function getAnthropicCredentials(): Promise<AnthropicCredentials | null>;
|
|
18
|
+
export declare function setAnthropicCredentials(creds: AnthropicCredentials): Promise<void>;
|
|
19
|
+
export declare function clearCredentials(): Promise<void>;
|