aisubs 0.3.6 → 0.3.8
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 +14 -0
- package/README.md +29 -29
- package/dist/auth.d.ts +4 -1
- package/dist/auth.js +143 -25
- package/dist/cli.js +5 -3
- package/dist/compatibility.js +157 -137
- package/dist/dashboard/assets/index-CePTUw7Z.css +2 -0
- package/dist/dashboard/assets/index-Vscu32Dz.js +84 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard.js +15 -26
- package/dist/http.d.ts +3 -0
- package/dist/http.js +13 -31
- package/dist/index.d.ts +2 -7
- package/dist/index.js +1 -6
- package/dist/memory-store.d.ts +9 -0
- package/dist/memory-store.js +34 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.js +2 -0
- package/dist/providers/chatgpt.d.ts +2 -1
- package/dist/providers/chatgpt.js +64 -2
- package/dist/providers/opencode.d.ts +6 -2
- package/dist/providers/opencode.js +65 -21
- package/dist/proxy-headers.d.ts +2 -0
- package/dist/proxy-headers.js +35 -0
- package/dist/realtime.js +6 -20
- package/dist/sqlite-store.d.ts +61 -0
- package/dist/sqlite-store.js +323 -0
- package/dist/store.d.ts +0 -23
- package/dist/store.js +1 -191
- package/dist/types.d.ts +40 -0
- package/dist/utils.js +15 -7
- package/examples/direct.mjs +8 -11
- package/examples/server.mjs +10 -15
- package/package.json +15 -6
- package/dist/dashboard/assets/index-BJDbjHnw.css +0 -2
- package/dist/dashboard/assets/index-BbN84qld.js +0 -84
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.8 - 2026-09-19
|
|
4
|
+
|
|
5
|
+
- Breaking: `createSubscriptionAuth` now requires an explicit credential store.
|
|
6
|
+
- Breaking: provider factories use the `aisubs/providers/*` entrypoints so the core `aisubs` import stays runtime-portable.
|
|
7
|
+
- Add `aisubs/node` SQLite credential and API-key stores with shared-connection support and cross-process refresh ownership.
|
|
8
|
+
- Persist standalone AISubs records in `~/.aisubs/aisubs.db` without retaining the legacy file-store implementation.
|
|
9
|
+
|
|
10
|
+
## 0.3.7 - 2026-09-16
|
|
11
|
+
|
|
12
|
+
- Improve provider compatibility, proxy handling, and request normalization.
|
|
13
|
+
- Harden dashboard account flows, modal interactions, and stale-load handling.
|
|
14
|
+
- Add regression coverage for compatibility, proxy headers, authentication, and
|
|
15
|
+
dashboard behavior.
|
|
16
|
+
|
|
3
17
|
## 0.3.6 - 2026-09-15
|
|
4
18
|
|
|
5
19
|
- Preserve provider cache controls and cache usage across OpenAI-compatible,
|
package/README.md
CHANGED
|
@@ -438,9 +438,14 @@ bun add aisubs
|
|
|
438
438
|
```
|
|
439
439
|
|
|
440
440
|
```js
|
|
441
|
-
import {
|
|
441
|
+
import { createSubscriptionAuth } from "aisubs";
|
|
442
|
+
import { SqliteCredentialStore } from "aisubs/node";
|
|
443
|
+
import { chatGptProvider } from "aisubs/providers/chatgpt";
|
|
442
444
|
|
|
443
|
-
const subscriptions = createSubscriptionAuth({
|
|
445
|
+
const subscriptions = createSubscriptionAuth({
|
|
446
|
+
store: new SqliteCredentialStore("./data/aisubs.db"),
|
|
447
|
+
providers: [chatGptProvider()],
|
|
448
|
+
});
|
|
444
449
|
const account = subscriptions.account("chatgpt", "personal");
|
|
445
450
|
|
|
446
451
|
if (!(await account.status()).authenticated) {
|
|
@@ -493,19 +498,16 @@ Useful account methods:
|
|
|
493
498
|
<summary><strong>Configure every provider and custom credential storage</strong></summary>
|
|
494
499
|
|
|
495
500
|
```js
|
|
496
|
-
import {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
openCodeGoProvider,
|
|
504
|
-
openCodeZenProvider,
|
|
505
|
-
} from "aisubs";
|
|
501
|
+
import { createSubscriptionAuth } from "aisubs";
|
|
502
|
+
import { SqliteCredentialStore } from "aisubs/node";
|
|
503
|
+
import { chatGptProvider } from "aisubs/providers/chatgpt";
|
|
504
|
+
import { claudeProvider } from "aisubs/providers/claude";
|
|
505
|
+
import { copilotProvider } from "aisubs/providers/copilot";
|
|
506
|
+
import { grokProvider } from "aisubs/providers/grok";
|
|
507
|
+
import { openCodeGoProvider, openCodeZenProvider } from "aisubs/providers/opencode";
|
|
506
508
|
|
|
507
509
|
const subscriptions = createSubscriptionAuth({
|
|
508
|
-
store: new
|
|
510
|
+
store: new SqliteCredentialStore("./data/aisubs.db"),
|
|
509
511
|
providers: [
|
|
510
512
|
chatGptProvider(),
|
|
511
513
|
claudeProvider(),
|
|
@@ -517,9 +519,10 @@ const subscriptions = createSubscriptionAuth({
|
|
|
517
519
|
});
|
|
518
520
|
```
|
|
519
521
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
522
|
+
Storage is always explicit. The core SDK never discovers a home directory,
|
|
523
|
+
creates files, starts a server, or opens a browser. The standalone CLI composes
|
|
524
|
+
the SQLite adapter at `~/.aisubs/aisubs.db`. Select an account with its provider
|
|
525
|
+
ID and a local account name:
|
|
523
526
|
|
|
524
527
|
```js
|
|
525
528
|
const chatgpt = subscriptions.account("chatgpt", "personal");
|
|
@@ -596,25 +599,23 @@ const response = await selected.proxy("responses", requestOptions);
|
|
|
596
599
|
<summary><strong>Run the local HTTP server from Node.js</strong></summary>
|
|
597
600
|
|
|
598
601
|
This is the programmatic equivalent of `aisubs dashboard`. The API key is
|
|
599
|
-
created once and reused across restarts;
|
|
600
|
-
|
|
602
|
+
created once and reused across restarts; regenerate it when clients should
|
|
603
|
+
receive a new key.
|
|
601
604
|
|
|
602
605
|
```js
|
|
603
606
|
import { homedir } from "node:os";
|
|
604
607
|
import { join } from "node:path";
|
|
605
|
-
import {
|
|
606
|
-
FileApiKeyStore,
|
|
607
|
-
FileCredentialStore,
|
|
608
|
-
chatGptProvider,
|
|
609
|
-
claudeProvider,
|
|
610
|
-
createSubscriptionAuth,
|
|
611
|
-
} from "aisubs";
|
|
608
|
+
import { createSubscriptionAuth } from "aisubs";
|
|
612
609
|
import { createSubscriptionAuthServer } from "aisubs/http";
|
|
610
|
+
import { SqliteApiKeyStore, SqliteCredentialStore } from "aisubs/node";
|
|
611
|
+
import { chatGptProvider } from "aisubs/providers/chatgpt";
|
|
612
|
+
import { claudeProvider } from "aisubs/providers/claude";
|
|
613
613
|
|
|
614
614
|
const directory = join(homedir(), ".aisubs");
|
|
615
|
-
const
|
|
615
|
+
const database = join(directory, "aisubs.db");
|
|
616
|
+
const apiKey = await new SqliteApiKeyStore(database).readOrCreate();
|
|
616
617
|
const auth = createSubscriptionAuth({
|
|
617
|
-
store: new
|
|
618
|
+
store: new SqliteCredentialStore(database),
|
|
618
619
|
providers: [chatGptProvider(), claudeProvider()],
|
|
619
620
|
});
|
|
620
621
|
|
|
@@ -674,8 +675,7 @@ the old key.
|
|
|
674
675
|
|
|
675
676
|
## Storage and security
|
|
676
677
|
|
|
677
|
-
- Credentials: `~/.aisubs/
|
|
678
|
-
- Persistent local API key: `~/.aisubs/api-key`.
|
|
678
|
+
- Credentials and persistent local API key: `~/.aisubs/aisubs.db`.
|
|
679
679
|
- Optional Codex catalog: `~/.codex/aisubs-catalog.json`.
|
|
680
680
|
- Codex integration stores the local AISubs key in the user-private Codex config.
|
|
681
681
|
- State directories and files use private permissions where the platform supports them.
|
package/dist/auth.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class SubscriptionAuth {
|
|
|
29
29
|
private readonly attempts;
|
|
30
30
|
private readonly generations;
|
|
31
31
|
private readonly refreshes;
|
|
32
|
+
private readonly coordinatedRefreshes;
|
|
32
33
|
private readonly usageCache;
|
|
33
34
|
private readonly modelsCache;
|
|
34
35
|
private readonly usageInflight;
|
|
@@ -53,6 +54,7 @@ export declare class SubscriptionAuth {
|
|
|
53
54
|
error: string | null;
|
|
54
55
|
} | null;
|
|
55
56
|
cancelLoginAttempt(id: string): boolean;
|
|
57
|
+
private cancelAttempt;
|
|
56
58
|
status(provider: ProviderId, options?: {
|
|
57
59
|
validate?: boolean;
|
|
58
60
|
account?: string;
|
|
@@ -61,6 +63,7 @@ export declare class SubscriptionAuth {
|
|
|
61
63
|
listAccounts(provider: ProviderId): Promise<Session[]>;
|
|
62
64
|
signOut(provider: ProviderId, account?: string): Promise<void>;
|
|
63
65
|
private credential;
|
|
66
|
+
private coordinatedCredential;
|
|
64
67
|
getAccessToken(provider: ProviderId, account?: string): Promise<string>;
|
|
65
68
|
credentialSummary(provider: ProviderId, account?: string): Promise<CredentialSummary>;
|
|
66
69
|
details(provider: ProviderId, account?: string, signal?: AbortSignal): Promise<SubscriptionAccountDetails>;
|
|
@@ -73,6 +76,6 @@ export declare class SubscriptionAuth {
|
|
|
73
76
|
account(provider: ProviderId, account: string): SubscriptionAccount;
|
|
74
77
|
}
|
|
75
78
|
export declare function createSubscriptionAuth(options: {
|
|
76
|
-
store
|
|
79
|
+
store: CredentialStore;
|
|
77
80
|
providers: readonly ProviderAdapter[];
|
|
78
81
|
} & SubscriptionAuthOptions): SubscriptionAuth;
|
package/dist/auth.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { defaultAiSubsDataDir, FileCredentialStore } from "./store.js";
|
|
2
1
|
import { errorMessage } from "./utils.js";
|
|
3
|
-
import { join } from "node:path";
|
|
4
2
|
export const DEFAULT_ACCOUNT = "default";
|
|
5
3
|
const ACCOUNT_STORAGE_PREFIX = "$subscription-account$";
|
|
6
4
|
const LOGIN_ATTEMPT_RETENTION_MS = 5 * 60_000;
|
|
5
|
+
function isCoordinatedStore(store) {
|
|
6
|
+
return ("readVersioned" in store &&
|
|
7
|
+
"replaceCredential" in store &&
|
|
8
|
+
"claimRefresh" in store &&
|
|
9
|
+
"commitRefresh" in store);
|
|
10
|
+
}
|
|
7
11
|
function normalizeAccountKey(value) {
|
|
8
12
|
if (value == null)
|
|
9
13
|
return DEFAULT_ACCOUNT;
|
|
@@ -63,6 +67,7 @@ export class SubscriptionAuth {
|
|
|
63
67
|
attempts = new Map();
|
|
64
68
|
generations = new Map();
|
|
65
69
|
refreshes = new Map();
|
|
70
|
+
coordinatedRefreshes = new Map();
|
|
66
71
|
usageCache = new Map();
|
|
67
72
|
modelsCache = new Map();
|
|
68
73
|
usageInflight = new Map();
|
|
@@ -157,31 +162,53 @@ export class SubscriptionAuth {
|
|
|
157
162
|
const accountKey = normalizeAccountKey(options?.account);
|
|
158
163
|
const scope = credentialKey(provider, accountKey);
|
|
159
164
|
const replace = options?.replace !== false;
|
|
160
|
-
|
|
165
|
+
const durable = isCoordinatedStore(this.store)
|
|
166
|
+
? await this.store.readVersioned(scope)
|
|
167
|
+
: undefined;
|
|
168
|
+
if (!replace && (durable?.credential ?? (await this.store.read(scope)))) {
|
|
161
169
|
throw new Error(`Account name ${accountKey} is already connected for ${provider}`);
|
|
162
170
|
}
|
|
163
171
|
const epoch = this.advance(scope);
|
|
164
172
|
for (const attempt of this.attempts.values()) {
|
|
165
|
-
if (attempt.scope === scope
|
|
166
|
-
|
|
173
|
+
if (attempt.scope === scope)
|
|
174
|
+
this.cancelAttempt(attempt);
|
|
167
175
|
}
|
|
168
176
|
const abort = new AbortController();
|
|
177
|
+
const id = crypto.randomUUID();
|
|
169
178
|
const providerOptions = { ...options };
|
|
170
179
|
delete providerOptions.account;
|
|
171
180
|
delete providerOptions.replace;
|
|
172
181
|
const login = await adapter.startLogin(abort.signal, providerOptions);
|
|
173
|
-
|
|
182
|
+
if (this.generation(scope) !== epoch) {
|
|
183
|
+
abort.abort();
|
|
184
|
+
void login.complete.catch(() => { });
|
|
185
|
+
throw new Error("Login cancelled");
|
|
186
|
+
}
|
|
174
187
|
let record;
|
|
175
188
|
const promise = login.complete
|
|
176
189
|
.then(async (credential) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
let saved;
|
|
191
|
+
if (isCoordinatedStore(this.store)) {
|
|
192
|
+
const replacement = await this.store.replaceCredential({
|
|
193
|
+
provider: scope,
|
|
194
|
+
credential,
|
|
195
|
+
expectedGeneration: durable?.generation ?? 0,
|
|
196
|
+
operationId: `login:${id}`,
|
|
197
|
+
});
|
|
198
|
+
if (!replacement.applied)
|
|
199
|
+
throw new Error("Login cancelled");
|
|
200
|
+
saved = replacement.record.credential;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
saved = await this.store.modify(scope, (current) => {
|
|
204
|
+
if (this.generation(scope) !== epoch)
|
|
205
|
+
return current;
|
|
206
|
+
if (current && !replace) {
|
|
207
|
+
throw new Error(`Account name ${accountKey} is already connected for ${provider}`);
|
|
208
|
+
}
|
|
209
|
+
return credential;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
185
212
|
if (this.generation(scope) !== epoch || !saved)
|
|
186
213
|
throw new Error("Login cancelled");
|
|
187
214
|
this.clearMetadata(scope);
|
|
@@ -199,6 +226,7 @@ export class SubscriptionAuth {
|
|
|
199
226
|
provider,
|
|
200
227
|
accountKey,
|
|
201
228
|
scope,
|
|
229
|
+
generation: epoch,
|
|
202
230
|
state: "pending",
|
|
203
231
|
error: null,
|
|
204
232
|
abort,
|
|
@@ -218,10 +246,7 @@ export class SubscriptionAuth {
|
|
|
218
246
|
return record.error;
|
|
219
247
|
},
|
|
220
248
|
wait: () => record.promise,
|
|
221
|
-
cancel: () =>
|
|
222
|
-
this.advance(scope);
|
|
223
|
-
abort.abort();
|
|
224
|
-
},
|
|
249
|
+
cancel: () => this.cancelAttempt(record),
|
|
225
250
|
};
|
|
226
251
|
}
|
|
227
252
|
expireLoginAttempt(id) {
|
|
@@ -243,10 +268,17 @@ export class SubscriptionAuth {
|
|
|
243
268
|
const attempt = this.attempts.get(id);
|
|
244
269
|
if (!attempt || attempt.state !== "pending")
|
|
245
270
|
return false;
|
|
246
|
-
this.
|
|
247
|
-
attempt.abort.abort();
|
|
271
|
+
this.cancelAttempt(attempt);
|
|
248
272
|
return true;
|
|
249
273
|
}
|
|
274
|
+
cancelAttempt(attempt) {
|
|
275
|
+
if (attempt.state !== "pending")
|
|
276
|
+
return;
|
|
277
|
+
if (this.generation(attempt.scope) === attempt.generation)
|
|
278
|
+
this.advance(attempt.scope);
|
|
279
|
+
attempt.state = "cancelled";
|
|
280
|
+
attempt.abort.abort();
|
|
281
|
+
}
|
|
250
282
|
async status(provider, options = {}) {
|
|
251
283
|
const accountKey = normalizeAccountKey(options.account);
|
|
252
284
|
if (options.validate)
|
|
@@ -277,15 +309,34 @@ export class SubscriptionAuth {
|
|
|
277
309
|
for (const refresh of this.refreshes.get(scope) ?? [])
|
|
278
310
|
refresh.abort();
|
|
279
311
|
for (const attempt of this.attempts.values()) {
|
|
280
|
-
if (attempt.scope === scope
|
|
281
|
-
|
|
312
|
+
if (attempt.scope === scope)
|
|
313
|
+
this.cancelAttempt(attempt);
|
|
314
|
+
}
|
|
315
|
+
if (isCoordinatedStore(this.store)) {
|
|
316
|
+
await this.store.deleteCredential({
|
|
317
|
+
provider: scope,
|
|
318
|
+
operationId: `logout:${crypto.randomUUID()}`,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
await this.store.delete(scope);
|
|
282
323
|
}
|
|
283
|
-
await this.store.delete(scope);
|
|
284
324
|
}
|
|
285
325
|
async credential(provider, account, forceRefresh = false) {
|
|
286
326
|
const adapter = this.adapter(provider);
|
|
287
327
|
const accountKey = normalizeAccountKey(account);
|
|
288
328
|
const scope = credentialKey(provider, accountKey);
|
|
329
|
+
if (isCoordinatedStore(this.store)) {
|
|
330
|
+
const pending = this.coordinatedRefreshes.get(scope);
|
|
331
|
+
if (pending)
|
|
332
|
+
return pending;
|
|
333
|
+
const operation = this.coordinatedCredential(this.store, adapter, provider, accountKey, scope, forceRefresh).finally(() => {
|
|
334
|
+
if (this.coordinatedRefreshes.get(scope) === operation)
|
|
335
|
+
this.coordinatedRefreshes.delete(scope);
|
|
336
|
+
});
|
|
337
|
+
this.coordinatedRefreshes.set(scope, operation);
|
|
338
|
+
return operation;
|
|
339
|
+
}
|
|
289
340
|
const observed = await this.store.read(scope);
|
|
290
341
|
if (!observed)
|
|
291
342
|
throw new Error(`Not authenticated with ${provider} account ${accountKey}`);
|
|
@@ -340,6 +391,74 @@ export class SubscriptionAuth {
|
|
|
340
391
|
this.clearMetadata(scope);
|
|
341
392
|
return refreshed;
|
|
342
393
|
}
|
|
394
|
+
async coordinatedCredential(store, adapter, provider, accountKey, scope, forceRefresh) {
|
|
395
|
+
const observed = await store.readVersioned(scope);
|
|
396
|
+
const credential = observed.credential;
|
|
397
|
+
if (!credential)
|
|
398
|
+
throw new Error(`Not authenticated with ${provider} account ${accountKey}`);
|
|
399
|
+
if (credential.metadata?.reauthRequired === true) {
|
|
400
|
+
throw new Error(`Session expired for ${provider} account ${accountKey}; sign in again`);
|
|
401
|
+
}
|
|
402
|
+
if (!forceRefresh && credential.expiresAt > Date.now())
|
|
403
|
+
return credential;
|
|
404
|
+
const claimId = crypto.randomUUID();
|
|
405
|
+
const claim = await store.claimRefresh({
|
|
406
|
+
provider: scope,
|
|
407
|
+
expectedVersion: observed.version,
|
|
408
|
+
expectedGeneration: observed.generation,
|
|
409
|
+
claimId,
|
|
410
|
+
operationId: `refresh-claim:${claimId}`,
|
|
411
|
+
});
|
|
412
|
+
if (claim === "missing") {
|
|
413
|
+
throw new Error(`Not authenticated with ${provider} account ${accountKey}`);
|
|
414
|
+
}
|
|
415
|
+
if (claim !== "claimed") {
|
|
416
|
+
const deadline = Date.now() + this.refreshTimeoutMs;
|
|
417
|
+
while (Date.now() < deadline) {
|
|
418
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
419
|
+
const latest = await store.readVersioned(scope);
|
|
420
|
+
if (latest.version === observed.version && latest.generation === observed.generation)
|
|
421
|
+
continue;
|
|
422
|
+
if (!latest.credential || latest.credential.metadata?.reauthRequired === true)
|
|
423
|
+
break;
|
|
424
|
+
if (latest.credential.expiresAt > Date.now())
|
|
425
|
+
return latest.credential;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
throw new Error(`Refresh outcome is uncertain for ${provider} account ${accountKey}; sign in again`);
|
|
429
|
+
}
|
|
430
|
+
let next;
|
|
431
|
+
try {
|
|
432
|
+
next = await adapter.refresh(credential, AbortSignal.timeout(this.refreshTimeoutMs));
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
if (!adapter.isPermanentRefreshError?.(error))
|
|
436
|
+
throw error;
|
|
437
|
+
next = {
|
|
438
|
+
accessToken: "",
|
|
439
|
+
expiresAt: 0,
|
|
440
|
+
account: credential.account,
|
|
441
|
+
metadata: { ...credential.metadata, reauthRequired: true },
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const committed = await store.commitRefresh({
|
|
445
|
+
provider: scope,
|
|
446
|
+
claimId,
|
|
447
|
+
expectedGeneration: observed.generation,
|
|
448
|
+
credential: next,
|
|
449
|
+
operationId: `refresh-commit:${claimId}`,
|
|
450
|
+
});
|
|
451
|
+
if (!committed.applied) {
|
|
452
|
+
throw new Error(`Session changed while refreshing ${provider} account ${accountKey}`);
|
|
453
|
+
}
|
|
454
|
+
const saved = committed.record.credential;
|
|
455
|
+
if (!saved || saved.metadata?.reauthRequired === true) {
|
|
456
|
+
this.clearMetadata(scope);
|
|
457
|
+
throw new Error(`Session expired for ${provider} account ${accountKey}; sign in again`);
|
|
458
|
+
}
|
|
459
|
+
this.clearMetadata(scope);
|
|
460
|
+
return saved;
|
|
461
|
+
}
|
|
343
462
|
async getAccessToken(provider, account = DEFAULT_ACCOUNT) {
|
|
344
463
|
const credential = await this.credential(provider, account);
|
|
345
464
|
if (credential.metadata?.delegatedCli === true) {
|
|
@@ -502,6 +621,5 @@ export class SubscriptionAuth {
|
|
|
502
621
|
}
|
|
503
622
|
}
|
|
504
623
|
export function createSubscriptionAuth(options) {
|
|
505
|
-
|
|
506
|
-
return new SubscriptionAuth(store, options.providers, options);
|
|
624
|
+
return new SubscriptionAuth(options.store, options.providers, options);
|
|
507
625
|
}
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,8 @@ import { claudeProvider } from "./providers/claude.js";
|
|
|
8
8
|
import { copilotProvider } from "./providers/copilot.js";
|
|
9
9
|
import { grokProvider } from "./providers/grok.js";
|
|
10
10
|
import { openCodeGoProvider, openCodeZenProvider } from "./providers/opencode.js";
|
|
11
|
-
import { defaultAiSubsDataDir
|
|
11
|
+
import { defaultAiSubsDataDir } from "./store.js";
|
|
12
|
+
import { SqliteApiKeyStore, SqliteCredentialStore } from "./sqlite-store.js";
|
|
12
13
|
const DEFAULT_DASHBOARD_PORT = 4319;
|
|
13
14
|
function usage() {
|
|
14
15
|
console.log(`AI Subs
|
|
@@ -61,8 +62,9 @@ async function main() {
|
|
|
61
62
|
}
|
|
62
63
|
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
63
64
|
throw new Error("Invalid port");
|
|
64
|
-
const
|
|
65
|
-
const
|
|
65
|
+
const database = join(dataDirectory, "aisubs.db");
|
|
66
|
+
const store = new SqliteCredentialStore(database);
|
|
67
|
+
const apiKeys = new SqliteApiKeyStore(database);
|
|
66
68
|
const auth = createSubscriptionAuth({
|
|
67
69
|
store,
|
|
68
70
|
providers: [
|