@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 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 environment variable
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
- ## Error Handling
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.list();
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 { Model, Provider, ProviderRouting } from "onlist";
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/onlist-js)
395
+ - [GitHub](https://github.com/OnlistTeam/typescript-sdk)
202
396
  - [Python SDK](https://pypi.org/project/onlist/)
203
397
 
204
398
  ## License