aisubs 0.2.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/CHANGELOG.md +17 -0
- package/README.md +141 -46
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js +16 -0
- package/dist/compatibility.d.ts +14 -0
- package/dist/compatibility.js +1521 -0
- package/dist/dashboard/assets/index-CEDww1hA.css +2 -0
- package/dist/dashboard/assets/{index-V-AoOW2F.js → index-DrnM3oWy.js} +34 -33
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard.d.ts +4 -2
- package/dist/dashboard.js +152 -86
- package/dist/http.d.ts +9 -6
- package/dist/http.js +254 -283
- package/dist/providers/chatgpt.js +2 -0
- package/dist/providers/claude.js +2 -0
- package/dist/providers/copilot.js +0 -2
- package/dist/realtime.d.ts +6 -0
- package/dist/realtime.js +163 -0
- package/examples/direct.mjs +31 -8
- package/examples/server.mjs +11 -1
- package/package.json +9 -1
- package/public/aisubs-dashboard.png +0 -0
- package/dist/dashboard/assets/index-DYe3pr2a.css +0 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 - 2026-08-16
|
|
4
|
+
|
|
5
|
+
- Add a universal OpenAI-compatible Chat Completions surface with translation
|
|
6
|
+
to supported native Responses, Anthropic Messages, and Google
|
|
7
|
+
`generateContent` endpoints.
|
|
8
|
+
- Add native OpenAI Realtime WebSocket tunnelling and account-scoped model
|
|
9
|
+
discovery with capability metadata.
|
|
10
|
+
- Add Claude, OpenCode Go, and OpenCode Zen to the dashboard and examples;
|
|
11
|
+
improve provider model metadata and usage reporting.
|
|
12
|
+
- Document desktop-client and SDK setup, local-key handling, compatibility
|
|
13
|
+
behavior, and cross-protocol streaming limits.
|
|
14
|
+
|
|
15
|
+
## 0.2.0 - 2026-08-15
|
|
16
|
+
|
|
17
|
+
- Previous release.
|
package/README.md
CHANGED
|
@@ -6,13 +6,27 @@
|
|
|
6
6
|
</table>
|
|
7
7
|
|
|
8
8
|
AISubs connects provider accounts once and exposes each account through a local,
|
|
9
|
-
account-scoped API.
|
|
10
|
-
|
|
9
|
+
account-scoped API. Every generative account has an OpenAI-compatible surface,
|
|
10
|
+
while native Responses, Anthropic Messages, Google `generateContent`, Realtime,
|
|
11
|
+
and provider-specific endpoints remain available. Use it from an SDK, cURL, or
|
|
12
|
+
an app that accepts a custom API base URL.
|
|
11
13
|
|
|
12
14
|
<p align="center">
|
|
13
15
|
<img src="./public/aisubs-dashboard.png" alt="AI Subs dashboard with provider connections and local API access" width="100%" />
|
|
14
16
|
</p>
|
|
15
17
|
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="./public/aisubs-chatgpt-account.png" alt="AISubs ChatGPT demo account details" width="100%" />
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="./public/aisubs-copilot-account.png" alt="AISubs GitHub Copilot demo account details" width="100%" />
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="./public/aisubs-grok-account.png" alt="AISubs Grok demo account details" width="100%" />
|
|
28
|
+
</p>
|
|
29
|
+
|
|
16
30
|
> Credentials, API keys, and requests remain on your computer. AISubs collects no
|
|
17
31
|
> telemetry or analytics. The dashboard keeps up to 200 redacted account request logs in memory for debugging.
|
|
18
32
|
|
|
@@ -35,6 +49,11 @@ key: reveal it, copy it, or deliberately regenerate it there.
|
|
|
35
49
|
The default URL is `http://127.0.0.1:4319`. Credentials and the API key are stored
|
|
36
50
|
under `~/.aisubs` and reused on later starts.
|
|
37
51
|
|
|
52
|
+
Supported subscription/account providers are ChatGPT, Claude, GitHub Copilot,
|
|
53
|
+
Grok, OpenCode Go, and OpenCode Zen. Google `generateContent` is a supported
|
|
54
|
+
wire protocol only for Google models surfaced by one of those connected
|
|
55
|
+
providers; AISubs does not connect or claim a Google subscription account.
|
|
56
|
+
|
|
38
57
|
## Use an account from any compatible app
|
|
39
58
|
|
|
40
59
|
Open an account in the dashboard and copy its base URL:
|
|
@@ -61,37 +80,98 @@ export OPENAI_API_KEY="aisubs_..."
|
|
|
61
80
|
AISubs removes its local key before forwarding a request and adds only the
|
|
62
81
|
selected account's provider credential.
|
|
63
82
|
|
|
64
|
-
|
|
83
|
+
## Desktop app examples
|
|
84
|
+
|
|
85
|
+
Keep AISubs running, then give the app the three values from the connected
|
|
86
|
+
account: its base URL, the AISubs API key, and an exact model ID.
|
|
87
|
+
|
|
88
|
+
### [Handy](https://handy.computer/download) (macOS)
|
|
89
|
+
|
|
90
|
+
Open **Post Process** and set:
|
|
91
|
+
|
|
92
|
+
| Handy field | Value |
|
|
93
|
+
| ----------- | -------------------------------------------------------- |
|
|
94
|
+
| Provider | `Custom` |
|
|
95
|
+
| Base URL | `http://127.0.0.1:4319/aisubs/chatgpt/default/v1` |
|
|
96
|
+
| API Key | Your `aisubs_...` key from the AISubs dashboard |
|
|
97
|
+
| Model | An exact Dashboard model ID, for example `gpt-5.6-terra` |
|
|
98
|
+
|
|
99
|
+
Click Handy’s refresh button beside **Model**, select the model, then choose a
|
|
100
|
+
prompt and test it. Replace `chatgpt/default` with the provider and account
|
|
101
|
+
segment copied from your AISubs dashboard when using a different account.
|
|
102
|
+
|
|
103
|
+
### [Raycast](https://www.raycast.com/download) AI custom provider
|
|
104
|
+
|
|
105
|
+
Create or edit `~/.config/raycast/ai/providers.yaml` and add a provider like
|
|
106
|
+
this. Keep the API key private—use the value from your AISubs dashboard, never
|
|
107
|
+
one copied from an example or screenshot.
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
providers:
|
|
111
|
+
- id: ai-subs
|
|
112
|
+
name: AI Subs
|
|
113
|
+
base_url: http://127.0.0.1:4319/aisubs/chatgpt/default/v1
|
|
114
|
+
api_keys:
|
|
115
|
+
default: aisubs_REPLACE_WITH_YOUR_DASHBOARD_KEY
|
|
116
|
+
models:
|
|
117
|
+
- id: gpt-5.6-terra
|
|
118
|
+
name: AISubs GPT-5.6 Terra
|
|
119
|
+
context: 272000
|
|
120
|
+
description: ChatGPT subscription via AISubs
|
|
121
|
+
abilities:
|
|
122
|
+
vision:
|
|
123
|
+
supported: true
|
|
124
|
+
system_message:
|
|
125
|
+
supported: true
|
|
126
|
+
tools:
|
|
127
|
+
supported: true
|
|
128
|
+
reasoning_effort:
|
|
129
|
+
supported: true
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Restart Raycast after saving. Replace the base URL, model ID, context, and
|
|
133
|
+
abilities with the values shown for your connected account. Do not enable an
|
|
134
|
+
ability that the selected model does not report.
|
|
135
|
+
|
|
136
|
+
[Cline](https://cline.bot/) also accepts a custom OpenAI-compatible endpoint;
|
|
137
|
+
use the same base URL, local AISubs key, and model ID. Exact settings labels can
|
|
138
|
+
change between client releases, so the account dashboard remains the source of
|
|
139
|
+
truth for the three values.
|
|
65
140
|
|
|
66
|
-
|
|
67
|
-
provides a focused, non-streaming text Chat Completions adapter for ChatGPT
|
|
68
|
-
accounts so apps such as [Handy](https://github.com/cjpais/Handy) can use them.
|
|
69
|
-
|
|
70
|
-
| Provider/model protocol | Base URL | Supported request path |
|
|
71
|
-
| ---------------------------------- | --------------------------- | --------------------------------------- |
|
|
72
|
-
| OpenAI Responses | Account URL ending in `/v1` | `POST /responses` |
|
|
73
|
-
| OpenAI-compatible Chat Completions | Account URL ending in `/v1` | `POST /chat/completions` |
|
|
74
|
-
| Anthropic Messages | Account URL ending in `/v1` | `POST /messages` |
|
|
75
|
-
| Google generateContent | Account URL ending in `/v1` | `POST /models/MODEL_ID:generateContent` |
|
|
76
|
-
| Model discovery | Account URL ending in `/v1` | `GET /models` |
|
|
77
|
-
|
|
78
|
-
Embeddings are intentionally not exposed. The ChatGPT compatibility adapter
|
|
79
|
-
supports text messages and JSON-schema response formats; tools, images, audio,
|
|
80
|
-
and streaming Chat Completions remain native-Responses-only features.
|
|
81
|
-
|
|
82
|
-
### Handy
|
|
83
|
-
|
|
84
|
-
Choose **Custom** in Handy and copy these values from the ChatGPT account page:
|
|
85
|
-
|
|
86
|
-
```text
|
|
87
|
-
Base URL: http://127.0.0.1:4319/aisubs/chatgpt/ACCOUNT/v1
|
|
88
|
-
API key: the persistent key shown on the AISubs dashboard
|
|
89
|
-
Model: an exact model ID shown for the account
|
|
90
|
-
```
|
|
141
|
+
### Compatibility contract
|
|
91
142
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
143
|
+
Use Chat Completions when an app offers only an “OpenAI-compatible” provider.
|
|
144
|
+
AISubs discovers the selected model's native protocol and translates the
|
|
145
|
+
request and response when necessary. Calling a model's native protocol remains
|
|
146
|
+
a pass-through, which preserves provider-specific fields and streaming events.
|
|
147
|
+
|
|
148
|
+
| Integration path | Example full URL | Request example |
|
|
149
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
150
|
+
| OpenAI Responses | `http://127.0.0.1:4319/aisubs/chatgpt/default/v1/responses` | `POST /responses` |
|
|
151
|
+
| OpenAI-compatible Chat Completions | `http://127.0.0.1:4319/aisubs/chatgpt/default/v1/chat/completions` | `POST /chat/completions` |
|
|
152
|
+
| Anthropic Messages | `http://127.0.0.1:4319/aisubs/claude/default/v1/messages` | `POST /messages` |
|
|
153
|
+
| Google generateContent | `http://127.0.0.1:4319/aisubs/opencode-zen/default/v1/models/MODEL_ID:generateContent` | `POST /models/MODEL_ID:generateContent` |
|
|
154
|
+
| Model discovery | `http://127.0.0.1:4319/aisubs/chatgpt/default/v1/models` | `GET /models` |
|
|
155
|
+
| Model details | `http://127.0.0.1:4319/aisubs/chatgpt/default/v1/models/MODEL_ID` | `GET /models/MODEL_ID` |
|
|
156
|
+
| OpenAI Realtime | `ws://127.0.0.1:4319/aisubs/PROVIDER/ACCOUNT/v1/realtime?model=MODEL_ID` | WebSocket |
|
|
157
|
+
|
|
158
|
+
Replace the provider, account (`default`), and model ID with the values shown
|
|
159
|
+
for your connected account in the AISubs dashboard.
|
|
160
|
+
|
|
161
|
+
Cross-protocol translation covers text and system messages, streaming, function
|
|
162
|
+
tools and tool results, image/file/audio input where both protocols support it,
|
|
163
|
+
JSON-schema output, reasoning effort, stop conditions, and usage including
|
|
164
|
+
cached and reasoning tokens. If a feature has no safe equivalent, AISubs returns
|
|
165
|
+
an explicit `unsupported_feature` error instead of silently dropping it.
|
|
166
|
+
|
|
167
|
+
Provider-native routes—including embeddings, image/audio generation, batches,
|
|
168
|
+
files, and Realtime—are forwarded when that connected provider exposes them.
|
|
169
|
+
AISubs cannot add a capability that the provider, subscription, or selected
|
|
170
|
+
model does not have. Native requests preserve provider-specific cache controls;
|
|
171
|
+
translated requests preserve shared fields and cache-usage counters. Other than
|
|
172
|
+
Responses-to-Chat streaming, cross-protocol streams may be emitted after the
|
|
173
|
+
native response completes; use the native endpoint when event-level streaming
|
|
174
|
+
or provider-specific fields matter.
|
|
95
175
|
|
|
96
176
|
## SDK examples
|
|
97
177
|
|
|
@@ -102,7 +182,7 @@ export AISUBS_API_KEY="aisubs_..."
|
|
|
102
182
|
```
|
|
103
183
|
|
|
104
184
|
<details>
|
|
105
|
-
<summary><strong>Official OpenAI JavaScript SDK
|
|
185
|
+
<summary><strong>Official OpenAI JavaScript SDK: Responses and Chat Completions</strong></summary>
|
|
106
186
|
|
|
107
187
|
Install:
|
|
108
188
|
|
|
@@ -131,8 +211,8 @@ const stream = await client.responses.create({
|
|
|
131
211
|
for await (const event of stream) console.log(event);
|
|
132
212
|
```
|
|
133
213
|
|
|
134
|
-
Chat Completions uses the same client with
|
|
135
|
-
|
|
214
|
+
Chat Completions uses the same client with any generative model. AISubs
|
|
215
|
+
translates to the selected model's native protocol when needed:
|
|
136
216
|
|
|
137
217
|
```js
|
|
138
218
|
const response = await client.chat.completions.create({
|
|
@@ -206,16 +286,17 @@ const result = streamText({
|
|
|
206
286
|
for await (const text of result.textStream) process.stdout.write(text);
|
|
207
287
|
```
|
|
208
288
|
|
|
209
|
-
For Chat Completions, install
|
|
210
|
-
with the same account base URL,
|
|
289
|
+
For the universal Chat Completions surface, install
|
|
290
|
+
`@ai-sdk/openai-compatible`, create the provider with the same account base URL,
|
|
291
|
+
and select the model with `provider("MODEL_ID")`.
|
|
211
292
|
|
|
212
293
|
</details>
|
|
213
294
|
|
|
214
295
|
<details>
|
|
215
|
-
<summary><strong>TanStack AI
|
|
296
|
+
<summary><strong>TanStack AI: Chat Completions</strong></summary>
|
|
216
297
|
|
|
217
|
-
TanStack's generic compatibility adapter targets Chat Completions.
|
|
218
|
-
|
|
298
|
+
TanStack's generic compatibility adapter targets Chat Completions. AISubs
|
|
299
|
+
translates it to the selected model's native protocol.
|
|
219
300
|
|
|
220
301
|
```bash
|
|
221
302
|
nub install @tanstack/ai @tanstack/ai-openai
|
|
@@ -271,7 +352,7 @@ print(response.choices[0].message.content)
|
|
|
271
352
|
</details>
|
|
272
353
|
|
|
273
354
|
<details>
|
|
274
|
-
<summary><strong>cURL
|
|
355
|
+
<summary><strong>cURL: Responses, Chat Completions, Anthropic, and Google</strong></summary>
|
|
275
356
|
|
|
276
357
|
```bash
|
|
277
358
|
curl "http://127.0.0.1:4319/aisubs/chatgpt/personal/v1/responses" \
|
|
@@ -313,7 +394,9 @@ curl "http://127.0.0.1:4319/aisubs/opencode-zen/lab/v1/models/MODEL_ID_FROM_DASH
|
|
|
313
394
|
## Direct AISubs SDK
|
|
314
395
|
|
|
315
396
|
Use the in-process SDK when AISubs is part of your trusted Node.js backend. It
|
|
316
|
-
needs no local server
|
|
397
|
+
needs no local server, AISubs API key, or app-specific integration. Direct SDK
|
|
398
|
+
requests use the provider-native endpoint; the local server is the surface that
|
|
399
|
+
adds cross-protocol compatibility for third-party clients.
|
|
317
400
|
|
|
318
401
|
Install the package in a project:
|
|
319
402
|
|
|
@@ -337,8 +420,12 @@ if (!(await account.status()).authenticated) {
|
|
|
337
420
|
}
|
|
338
421
|
|
|
339
422
|
const catalog = await account.getModels();
|
|
340
|
-
const
|
|
341
|
-
if (!
|
|
423
|
+
const selected = catalog?.models.find((item) => item.selectable !== false);
|
|
424
|
+
if (!selected) throw new Error("No selectable model is available");
|
|
425
|
+
|
|
426
|
+
// ChatGPT models are native Responses models. Other providers report their
|
|
427
|
+
// native endpoint in selected.endpoints; use the matching native SDK adapter.
|
|
428
|
+
const model = selected.id;
|
|
342
429
|
|
|
343
430
|
const response = await account.proxy("responses", {
|
|
344
431
|
method: "POST",
|
|
@@ -354,6 +441,11 @@ Available provider factories are `chatGptProvider()`, `claudeProvider()`,
|
|
|
354
441
|
`copilotProvider()`, `grokProvider()`, `openCodeGoProvider()`, and
|
|
355
442
|
`openCodeZenProvider()`.
|
|
356
443
|
|
|
444
|
+
`getModels()` is cached for five minutes and `getUsage()` for 15 seconds by
|
|
445
|
+
default, with concurrent calls deduplicated. Account changes invalidate both.
|
|
446
|
+
Pass `modelsCacheTtlMs` or `usageCacheTtlMs` to `createSubscriptionAuth()` when
|
|
447
|
+
an in-process application needs different metadata freshness.
|
|
448
|
+
|
|
357
449
|
Useful account methods:
|
|
358
450
|
|
|
359
451
|
| Method | Purpose |
|
|
@@ -539,9 +631,12 @@ POST /v1/api-key/regenerate # dashboard session only
|
|
|
539
631
|
* /aisubs/:provider/:account/v1/*
|
|
540
632
|
```
|
|
541
633
|
|
|
542
|
-
The provider and account routes
|
|
543
|
-
or `x-api-key: AISUBS_API_KEY`.
|
|
544
|
-
|
|
634
|
+
The provider and account routes accept `Authorization: Bearer AISUBS_API_KEY`
|
|
635
|
+
or `x-api-key: AISUBS_API_KEY`. A Google-protocol client can instead put that
|
|
636
|
+
same local AISubs key in `x-goog-api-key` or `?key=`; this does not represent a
|
|
637
|
+
Google credential or subscription. Local credentials are removed from headers
|
|
638
|
+
and query strings before proxying. Regenerating the key immediately invalidates
|
|
639
|
+
the old key.
|
|
545
640
|
|
|
546
641
|
## Storage and security
|
|
547
642
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -66,6 +66,8 @@ export declare class SubscriptionAuth {
|
|
|
66
66
|
details(provider: ProviderId, account?: string, signal?: AbortSignal): Promise<SubscriptionAccountDetails>;
|
|
67
67
|
fetch(provider: ProviderId, input: string | URL | Request, init?: RequestInit, account?: string): Promise<Response>;
|
|
68
68
|
proxy(provider: ProviderId, account: string, path: string, init?: RequestInit): Promise<Response>;
|
|
69
|
+
/** Build an authorized direct-provider request for transports such as WebSocket. */
|
|
70
|
+
authorizeProxyRequest(provider: ProviderId, account: string, path: string, init?: RequestInit): Promise<Request>;
|
|
69
71
|
getUsage(provider: ProviderId, account?: string, callerSignal?: AbortSignal): Promise<ProviderUsage | null>;
|
|
70
72
|
getModels(provider: ProviderId, account?: string, callerSignal?: AbortSignal): Promise<ProviderModels | null>;
|
|
71
73
|
account(provider: ProviderId, account: string): SubscriptionAccount;
|
package/dist/auth.js
CHANGED
|
@@ -421,6 +421,22 @@ export class SubscriptionAuth {
|
|
|
421
421
|
const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
422
422
|
return this.fetch(provider, new URL(path, base), init, accountKey);
|
|
423
423
|
}
|
|
424
|
+
/** Build an authorized direct-provider request for transports such as WebSocket. */
|
|
425
|
+
async authorizeProxyRequest(provider, account, path, init) {
|
|
426
|
+
const adapter = this.adapter(provider);
|
|
427
|
+
const accountKey = normalizeAccountKey(account);
|
|
428
|
+
if (adapter.proxy) {
|
|
429
|
+
throw new Error(`${provider} does not expose a direct transport endpoint`);
|
|
430
|
+
}
|
|
431
|
+
const credential = await this.credential(provider, accountKey);
|
|
432
|
+
const baseUrl = typeof adapter.proxyBaseUrl === "function"
|
|
433
|
+
? adapter.proxyBaseUrl(credential)
|
|
434
|
+
: adapter.proxyBaseUrl;
|
|
435
|
+
if (!baseUrl)
|
|
436
|
+
throw new Error(`${provider} does not expose a direct API endpoint`);
|
|
437
|
+
const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
438
|
+
return adapter.authorize(createRequest(new URL(path, base), init), credential);
|
|
439
|
+
}
|
|
424
440
|
async getUsage(provider, account = DEFAULT_ACCOUNT, callerSignal) {
|
|
425
441
|
const adapter = this.adapter(provider);
|
|
426
442
|
if (!adapter.getUsage)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SubscriptionAuth } from "./auth.js";
|
|
2
|
+
import type { ProviderId } from "./types.js";
|
|
3
|
+
export type WireProtocol = "responses" | "chat/completions" | "messages" | "google";
|
|
4
|
+
export declare class CompatibilityError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly status: number;
|
|
7
|
+
constructor(message: string, code?: string, status?: number);
|
|
8
|
+
}
|
|
9
|
+
export declare function requestProtocol(path: string): {
|
|
10
|
+
protocol: WireProtocol;
|
|
11
|
+
model?: string;
|
|
12
|
+
stream?: boolean;
|
|
13
|
+
} | null;
|
|
14
|
+
export declare function proxyCompatible(auth: SubscriptionAuth, provider: ProviderId, account: string, path: string, body: Buffer, headers: Headers, signal?: AbortSignal): Promise<Response | null>;
|