@onlist/sdk 0.1.0 → 0.3.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 +207 -13
- package/dist/index.cjs +349 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -12
- package/dist/index.d.ts +524 -12
- package/dist/index.js +337 -28
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,13 +5,13 @@ The official JavaScript/TypeScript SDK for [Onlist](https://onlist.io), the AI A
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install onlist
|
|
8
|
+
npm install @onlist/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { Onlist } from "onlist";
|
|
14
|
+
import { Onlist } from "@onlist/sdk";
|
|
15
15
|
|
|
16
16
|
const client = new Onlist({ apiKey: "sk-..." });
|
|
17
17
|
|
|
@@ -35,9 +35,22 @@ The SDK looks for API keys in this order:
|
|
|
35
35
|
// Explicit key
|
|
36
36
|
const client = new Onlist({ apiKey: "sk-..." });
|
|
37
37
|
|
|
38
|
-
// From
|
|
38
|
+
// From ONLIST_API_KEY
|
|
39
39
|
// export ONLIST_API_KEY=sk-...
|
|
40
40
|
const client = new Onlist();
|
|
41
|
+
|
|
42
|
+
// Falls back to OPENAI_API_KEY if ONLIST_API_KEY is not set
|
|
43
|
+
// export OPENAI_API_KEY=sk-...
|
|
44
|
+
const client = new Onlist();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For the [Account API](#account-api) there is a second, optional credential —
|
|
48
|
+
a management key, read from the `managementKey` parameter or
|
|
49
|
+
`ONLIST_MANAGEMENT_KEY`, falling back to your API key:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const client = new Onlist({ managementKey: "mgmt_..." });
|
|
53
|
+
// export ONLIST_MANAGEMENT_KEY=mgmt_...
|
|
41
54
|
```
|
|
42
55
|
|
|
43
56
|
## Provider Routing
|
|
@@ -49,7 +62,6 @@ Route requests to specific providers on the Onlist marketplace:
|
|
|
49
62
|
const response = await client.chat.completions.create({
|
|
50
63
|
model: "anthropic/claude-sonnet-4",
|
|
51
64
|
messages: [{ role: "user", content: "Hello!" }],
|
|
52
|
-
// @ts-expect-error -- extra body fields
|
|
53
65
|
provider: { only: ["alice-shop"] },
|
|
54
66
|
});
|
|
55
67
|
|
|
@@ -57,7 +69,6 @@ const response = await client.chat.completions.create({
|
|
|
57
69
|
const response = await client.chat.completions.create({
|
|
58
70
|
model: "openai/gpt-4o",
|
|
59
71
|
messages: [{ role: "user", content: "Hello!" }],
|
|
60
|
-
// @ts-expect-error
|
|
61
72
|
provider: { sort: "price" },
|
|
62
73
|
});
|
|
63
74
|
|
|
@@ -65,7 +76,6 @@ const response = await client.chat.completions.create({
|
|
|
65
76
|
const response = await client.chat.completions.create({
|
|
66
77
|
model: "openai/gpt-4o",
|
|
67
78
|
messages: [{ role: "user", content: "Hello!" }],
|
|
68
|
-
// @ts-expect-error
|
|
69
79
|
provider: {
|
|
70
80
|
order: ["alice-shop", "bob-ai"],
|
|
71
81
|
allow_fallbacks: true,
|
|
@@ -119,13 +129,157 @@ for (const provider of providers.items) {
|
|
|
119
129
|
const profile = await client.marketplace.providers.get("alice-shop");
|
|
120
130
|
```
|
|
121
131
|
|
|
122
|
-
##
|
|
132
|
+
## Rankings API
|
|
133
|
+
|
|
134
|
+
Access model and app usage rankings:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Model usage leaderboard
|
|
138
|
+
const rankings = await client.marketplace.rankings.models({
|
|
139
|
+
sort: "popular",
|
|
140
|
+
window: "week",
|
|
141
|
+
});
|
|
142
|
+
for (const entry of rankings.leaderboard) {
|
|
143
|
+
console.log(`#${entry.rank} ${entry.model_name} (${entry.total_requests} requests)`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Trending models
|
|
147
|
+
const trending = await client.marketplace.rankings.models({
|
|
148
|
+
sort: "trending",
|
|
149
|
+
window: "month",
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// App rankings
|
|
153
|
+
const apps = await client.marketplace.rankings.apps({
|
|
154
|
+
sort: "popular",
|
|
155
|
+
window: "month",
|
|
156
|
+
limit: 10,
|
|
157
|
+
});
|
|
158
|
+
for (const app of apps.apps) {
|
|
159
|
+
console.log(`#${app.rank} ${app.title} (${app.domain})`);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Account API
|
|
164
|
+
|
|
165
|
+
Balance, API key management, per-call costs and daily usage. These endpoints
|
|
166
|
+
are OpenRouter-compatible: same paths, same field names.
|
|
167
|
+
|
|
168
|
+
Most of them need a **management key** (`mgmt_...`), which you create at
|
|
169
|
+
[onlist.io/management-keys](https://onlist.io/management-keys). It is a
|
|
170
|
+
separate credential from your inference key and cannot make model calls:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const client = new Onlist({ apiKey: "sk-...", managementKey: "mgmt_..." });
|
|
174
|
+
// or set ONLIST_API_KEY and ONLIST_MANAGEMENT_KEY
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
If you pass only `apiKey`, it is used for the account endpoints too. That
|
|
178
|
+
matches OpenRouter's single-keyhole shape, and the server returns
|
|
179
|
+
`PermissionDeniedError` where a management key is actually required.
|
|
180
|
+
|
|
181
|
+
### Credits
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const credits = await client.credits.get();
|
|
185
|
+
console.log(`$${(credits.total_credits - credits.total_usage).toFixed(4)} remaining`);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### API keys
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Create — `.key` is the plaintext secret, returned exactly once
|
|
192
|
+
const created = await client.apiKeys.create("ci-runner", { limit: 5, limit_reset: "daily" });
|
|
193
|
+
console.log(created.key);
|
|
194
|
+
|
|
195
|
+
// List — fixed pages of 100, no total; read until you get a short page
|
|
196
|
+
const keys = await client.apiKeys.list({ offset: 0, include_disabled: true });
|
|
197
|
+
|
|
198
|
+
// Read one
|
|
199
|
+
const key = await client.apiKeys.get(created.data.hash);
|
|
200
|
+
|
|
201
|
+
// Update — omitted fields are left unchanged, `null` clears the value
|
|
202
|
+
await client.apiKeys.update(key.hash, { limit: null }); // remove the spend cap
|
|
203
|
+
await client.apiKeys.update(key.hash, { disabled: true }); // stop it spending
|
|
204
|
+
|
|
205
|
+
await client.apiKeys.delete(key.hash);
|
|
206
|
+
|
|
207
|
+
// Describe the credential this client is holding
|
|
208
|
+
const me = await client.apiKeys.current();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`update()` distinguishes three states. An omitted (or `undefined`) field is
|
|
212
|
+
left alone; an explicit `null` clears the value. Because of that, `name` and
|
|
213
|
+
`disabled` do not accept `null` at all — the server reads `{"name": null}` as
|
|
214
|
+
an empty name and `{"disabled": null}` as `false`, which would re-enable a
|
|
215
|
+
key you only meant to leave alone.
|
|
216
|
+
|
|
217
|
+
`limit_reset` accepts `"daily"`, `"weekly"`, or `null` for a lifetime total.
|
|
218
|
+
|
|
219
|
+
### Generation
|
|
123
220
|
|
|
221
|
+
What one call cost, and where it went. This one also accepts a plain
|
|
222
|
+
inference key, which can look up the calls it made itself:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const { data, response } = await client.chat.completions
|
|
226
|
+
.create({ model: "deepseek/deepseek-chat", messages: [{ role: "user", content: "Hi" }] })
|
|
227
|
+
.withResponse();
|
|
228
|
+
|
|
229
|
+
const gen = await client.generations.get(response.headers.get("X-Oneapi-Request-Id")!);
|
|
230
|
+
console.log(`${gen.provider_name}: $${gen.total_cost}, ${gen.latency}ms to first token`);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Activity
|
|
234
|
+
|
|
235
|
+
Daily usage grouped by model and provider, covering the last 30 complete UTC
|
|
236
|
+
days. Today is excluded, so the same query always returns the same numbers:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
for (const row of await client.activity.list()) {
|
|
240
|
+
console.log(`${row.date} ${row.model} ${row.requests} req $${row.usage}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Narrow to one day or one key
|
|
244
|
+
await client.activity.list({ date: "2026-09-05" });
|
|
245
|
+
await client.activity.list({ api_key_hash: "42" });
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Sign in with Onlist
|
|
249
|
+
|
|
250
|
+
Let your users authorize your app and get their own inference key, without
|
|
251
|
+
ever pasting one. This is the OAuth PKCE flow, compatible with OpenRouter's:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { Onlist, exchangeAuthCode, generatePkce } from "@onlist/sdk";
|
|
255
|
+
|
|
256
|
+
const { verifier, challenge } = await generatePkce();
|
|
257
|
+
|
|
258
|
+
window.location.href =
|
|
259
|
+
"https://onlist.io/auth" +
|
|
260
|
+
"?callback_url=https://yourapp.com/callback" +
|
|
261
|
+
`&code_challenge=${challenge}&code_challenge_method=S256`;
|
|
262
|
+
|
|
263
|
+
// ...user approves, your callback receives ?code=...
|
|
264
|
+
|
|
265
|
+
const result = await exchangeAuthCode(code, { codeVerifier: verifier });
|
|
266
|
+
const client = new Onlist({ apiKey: result.key }); // scoped to that user
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`exchangeAuthCode()` is a standalone function, not a client method, because
|
|
270
|
+
an app running this flow has no API key yet — that is the whole point of it —
|
|
271
|
+
and constructing `Onlist` requires one. If you already have a client,
|
|
272
|
+
`client.oauth.exchange()` does the same thing.
|
|
273
|
+
|
|
274
|
+
The code is single-use and is consumed even when the verifier does not match,
|
|
275
|
+
so a failed exchange means restarting the browser flow.
|
|
276
|
+
|
|
277
|
+
## Error Handling
|
|
124
278
|
OpenAI-compatible calls (`chat.completions`, `embeddings`, etc.) throw standard `openai` errors. Marketplace calls throw `onlist` errors:
|
|
125
279
|
|
|
126
280
|
```typescript
|
|
127
281
|
import OpenAI from "openai";
|
|
128
|
-
import { AuthenticationError } from "onlist";
|
|
282
|
+
import { AuthenticationError, NotFoundError } from "@onlist/sdk";
|
|
129
283
|
|
|
130
284
|
// OpenAI-compatible endpoints throw openai errors
|
|
131
285
|
try {
|
|
@@ -138,21 +292,55 @@ try {
|
|
|
138
292
|
|
|
139
293
|
// Marketplace endpoints throw onlist errors
|
|
140
294
|
try {
|
|
141
|
-
await client.marketplace.models.
|
|
295
|
+
await client.marketplace.models.get("nonexistent/model");
|
|
142
296
|
} catch (e) {
|
|
297
|
+
if (e instanceof NotFoundError) {
|
|
298
|
+
console.log("Model not found");
|
|
299
|
+
}
|
|
143
300
|
if (e instanceof AuthenticationError) {
|
|
144
301
|
console.log("Invalid API key for marketplace");
|
|
145
302
|
}
|
|
146
303
|
}
|
|
147
304
|
```
|
|
148
305
|
|
|
306
|
+
Account endpoints throw the same family, plus `BadRequestError` (400) and
|
|
307
|
+
`PermissionDeniedError` (403). The server's message is passed through
|
|
308
|
+
unchanged:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { PermissionDeniedError } from "@onlist/sdk";
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
await client.credits.get();
|
|
315
|
+
} catch (e) {
|
|
316
|
+
if (e instanceof PermissionDeniedError) {
|
|
317
|
+
console.log(e.message); // "Only management keys can perform this operation"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Retry Configuration
|
|
323
|
+
|
|
324
|
+
Marketplace and account API calls automatically retry on transient failures (408, 429, 5xx) with exponential backoff:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const client = new Onlist({
|
|
328
|
+
apiKey: "sk-...",
|
|
329
|
+
maxRetries: 3, // default: 2
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Only `GET` requests are retried. Creating a key or exchanging an
|
|
334
|
+
authorization code is never replayed: a duplicate key or a burnt code is
|
|
335
|
+
worse than surfacing the transient error.
|
|
336
|
+
|
|
149
337
|
## Migration from OpenAI
|
|
150
338
|
|
|
151
339
|
Replace the `openai` import with `onlist`:
|
|
152
340
|
|
|
153
341
|
```diff
|
|
154
342
|
- import OpenAI from "openai";
|
|
155
|
-
+ import { Onlist } from "onlist";
|
|
343
|
+
+ import { Onlist } from "@onlist/sdk";
|
|
156
344
|
|
|
157
345
|
- const client = new OpenAI({ apiKey: "sk-..." });
|
|
158
346
|
+ const client = new Onlist({ apiKey: "sk-..." });
|
|
@@ -168,7 +356,7 @@ const response = await client.chat.completions.create({
|
|
|
168
356
|
|
|
169
357
|
```diff
|
|
170
358
|
- import OpenAI from "openai";
|
|
171
|
-
+ import { Onlist } from "onlist";
|
|
359
|
+
+ import { Onlist } from "@onlist/sdk";
|
|
172
360
|
|
|
173
361
|
- const client = new OpenAI({
|
|
174
362
|
- baseURL: "https://openrouter.ai/api/v1",
|
|
@@ -188,7 +376,13 @@ const response = await client.chat.completions.create({
|
|
|
188
376
|
The SDK is written in TypeScript and ships with full type definitions. All marketplace response types are exported:
|
|
189
377
|
|
|
190
378
|
```typescript
|
|
191
|
-
import type {
|
|
379
|
+
import type {
|
|
380
|
+
Model,
|
|
381
|
+
Provider,
|
|
382
|
+
ProviderRouting,
|
|
383
|
+
ModelRankingsResponse,
|
|
384
|
+
AppRankingsResponse,
|
|
385
|
+
} from "@onlist/sdk";
|
|
192
386
|
```
|
|
193
387
|
|
|
194
388
|
## Links
|
|
@@ -198,7 +392,7 @@ import type { Model, Provider, ProviderRouting } from "onlist";
|
|
|
198
392
|
- [Model Catalog](https://onlist.io/models)
|
|
199
393
|
- [Provider Directory](https://onlist.io/providers)
|
|
200
394
|
- [API Reference](https://onlist.io/docs/api)
|
|
201
|
-
- [GitHub](https://github.com/OnlistTeam/
|
|
395
|
+
- [GitHub](https://github.com/OnlistTeam/typescript-sdk)
|
|
202
396
|
- [Python SDK](https://pypi.org/project/onlist/)
|
|
203
397
|
|
|
204
398
|
## License
|