@n24q02m/hull 0.4.0-beta.9 → 0.5.0-beta.1

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.
Files changed (169) hide show
  1. package/README.md +47 -16
  2. package/build/cli/build-cli.d.ts +5 -6
  3. package/build/cli/build-cli.d.ts.map +1 -1
  4. package/build/cli/build-cli.js +4 -61
  5. package/build/cli/build-cli.js.map +1 -1
  6. package/build/crypto/index.d.ts +1 -1
  7. package/build/crypto/index.d.ts.map +1 -1
  8. package/build/crypto/index.js +1 -1
  9. package/build/crypto/index.js.map +1 -1
  10. package/build/crypto/kdf.d.ts +0 -13
  11. package/build/crypto/kdf.d.ts.map +1 -1
  12. package/build/crypto/kdf.js +3 -18
  13. package/build/crypto/kdf.js.map +1 -1
  14. package/build/index.d.ts +1 -11
  15. package/build/index.d.ts.map +1 -1
  16. package/build/index.js +4 -16
  17. package/build/index.js.map +1 -1
  18. package/build/storage/account-registry.d.ts +53 -0
  19. package/build/storage/account-registry.d.ts.map +1 -0
  20. package/build/storage/account-registry.js +148 -0
  21. package/build/storage/account-registry.js.map +1 -0
  22. package/build/storage/backends.d.ts +0 -26
  23. package/build/storage/backends.d.ts.map +1 -1
  24. package/build/storage/backends.js +0 -65
  25. package/build/storage/backends.js.map +1 -1
  26. package/build/storage/config-file.d.ts +2 -19
  27. package/build/storage/config-file.d.ts.map +1 -1
  28. package/build/storage/config-file.js +2 -23
  29. package/build/storage/config-file.js.map +1 -1
  30. package/build/storage/credential-store.d.ts +1 -9
  31. package/build/storage/credential-store.d.ts.map +1 -1
  32. package/build/storage/credential-store.js +4 -16
  33. package/build/storage/credential-store.js.map +1 -1
  34. package/build/storage/data-dir.d.ts +2 -2
  35. package/build/storage/data-dir.js +2 -2
  36. package/build/storage/encryption.d.ts.map +1 -1
  37. package/build/storage/encryption.js +4 -0
  38. package/build/storage/encryption.js.map +1 -1
  39. package/build/storage/index.d.ts +5 -3
  40. package/build/storage/index.d.ts.map +1 -1
  41. package/build/storage/index.js +5 -3
  42. package/build/storage/index.js.map +1 -1
  43. package/build/storage/key-pool-crypto.d.ts +44 -0
  44. package/build/storage/key-pool-crypto.d.ts.map +1 -0
  45. package/build/storage/key-pool-crypto.js +132 -0
  46. package/build/storage/key-pool-crypto.js.map +1 -0
  47. package/build/storage/key-pool-store.d.ts +134 -0
  48. package/build/storage/key-pool-store.d.ts.map +1 -0
  49. package/build/storage/key-pool-store.js +470 -0
  50. package/build/storage/key-pool-store.js.map +1 -0
  51. package/build/storage/key-pool.d.ts +172 -0
  52. package/build/storage/key-pool.d.ts.map +1 -0
  53. package/build/storage/key-pool.js +374 -0
  54. package/build/storage/key-pool.js.map +1 -0
  55. package/build/storage/resolver.js +1 -1
  56. package/build/storage/resolver.js.map +1 -1
  57. package/build/token-auth/index.d.ts +1 -1
  58. package/build/token-auth/index.js +1 -1
  59. package/build/transport/index.d.ts +1 -2
  60. package/build/transport/index.d.ts.map +1 -1
  61. package/build/transport/index.js +1 -2
  62. package/build/transport/index.js.map +1 -1
  63. package/build/transport/local-server.d.ts +12 -128
  64. package/build/transport/local-server.d.ts.map +1 -1
  65. package/build/transport/local-server.js +20 -262
  66. package/build/transport/local-server.js.map +1 -1
  67. package/build/transport/streamable-http.d.ts +6 -7
  68. package/build/transport/streamable-http.d.ts.map +1 -1
  69. package/build/transport/streamable-http.js +6 -6
  70. package/build/transport/streamable-http.js.map +1 -1
  71. package/build/util/browser.d.ts.map +1 -0
  72. package/build/{relay → util}/browser.js +2 -2
  73. package/build/util/browser.js.map +1 -0
  74. package/package.json +4 -21
  75. package/build/auth/credential-form.d.ts +0 -163
  76. package/build/auth/credential-form.d.ts.map +0 -1
  77. package/build/auth/credential-form.js +0 -1869
  78. package/build/auth/credential-form.js.map +0 -1
  79. package/build/auth/delegated-oauth-app.d.ts +0 -90
  80. package/build/auth/delegated-oauth-app.d.ts.map +0 -1
  81. package/build/auth/delegated-oauth-app.js +0 -881
  82. package/build/auth/delegated-oauth-app.js.map +0 -1
  83. package/build/auth/index.d.ts +0 -12
  84. package/build/auth/index.d.ts.map +0 -1
  85. package/build/auth/index.js +0 -12
  86. package/build/auth/index.js.map +0 -1
  87. package/build/auth/local-oauth-app.d.ts +0 -152
  88. package/build/auth/local-oauth-app.d.ts.map +0 -1
  89. package/build/auth/local-oauth-app.js +0 -975
  90. package/build/auth/local-oauth-app.js.map +0 -1
  91. package/build/auth/relay-login.d.ts +0 -46
  92. package/build/auth/relay-login.d.ts.map +0 -1
  93. package/build/auth/relay-login.js +0 -197
  94. package/build/auth/relay-login.js.map +0 -1
  95. package/build/auth/router.d.ts +0 -18
  96. package/build/auth/router.d.ts.map +0 -1
  97. package/build/auth/router.js +0 -62
  98. package/build/auth/router.js.map +0 -1
  99. package/build/auth/session-store.d.ts +0 -49
  100. package/build/auth/session-store.d.ts.map +0 -1
  101. package/build/auth/session-store.js +0 -94
  102. package/build/auth/session-store.js.map +0 -1
  103. package/build/auth/stable-sub.d.ts +0 -27
  104. package/build/auth/stable-sub.d.ts.map +0 -1
  105. package/build/auth/stable-sub.js +0 -38
  106. package/build/auth/stable-sub.js.map +0 -1
  107. package/build/auth/well-known.d.ts +0 -4
  108. package/build/auth/well-known.d.ts.map +0 -1
  109. package/build/auth/well-known.js +0 -26
  110. package/build/auth/well-known.js.map +0 -1
  111. package/build/oauth/cache.d.ts +0 -22
  112. package/build/oauth/cache.d.ts.map +0 -1
  113. package/build/oauth/cache.js +0 -24
  114. package/build/oauth/cache.js.map +0 -1
  115. package/build/oauth/index.d.ts +0 -6
  116. package/build/oauth/index.d.ts.map +0 -1
  117. package/build/oauth/index.js +0 -6
  118. package/build/oauth/index.js.map +0 -1
  119. package/build/oauth/jwt-issuer.d.ts +0 -87
  120. package/build/oauth/jwt-issuer.d.ts.map +0 -1
  121. package/build/oauth/jwt-issuer.js +0 -205
  122. package/build/oauth/jwt-issuer.js.map +0 -1
  123. package/build/oauth/pkce.d.ts +0 -10
  124. package/build/oauth/pkce.d.ts.map +0 -1
  125. package/build/oauth/pkce.js +0 -34
  126. package/build/oauth/pkce.js.map +0 -1
  127. package/build/oauth/provider.d.ts +0 -52
  128. package/build/oauth/provider.d.ts.map +0 -1
  129. package/build/oauth/provider.js +0 -98
  130. package/build/oauth/provider.js.map +0 -1
  131. package/build/oauth/user-store.d.ts +0 -25
  132. package/build/oauth/user-store.d.ts.map +0 -1
  133. package/build/oauth/user-store.js +0 -85
  134. package/build/oauth/user-store.js.map +0 -1
  135. package/build/relay/browser.d.ts.map +0 -1
  136. package/build/relay/browser.js.map +0 -1
  137. package/build/relay/client.d.ts +0 -36
  138. package/build/relay/client.d.ts.map +0 -1
  139. package/build/relay/client.js +0 -135
  140. package/build/relay/client.js.map +0 -1
  141. package/build/relay/session.d.ts +0 -12
  142. package/build/relay/session.d.ts.map +0 -1
  143. package/build/relay/session.js +0 -55
  144. package/build/relay/session.js.map +0 -1
  145. package/build/relay/tool-helpers.d.ts +0 -85
  146. package/build/relay/tool-helpers.d.ts.map +0 -1
  147. package/build/relay/tool-helpers.js +0 -103
  148. package/build/relay/tool-helpers.js.map +0 -1
  149. package/build/relay/wordlist.d.ts +0 -2
  150. package/build/relay/wordlist.d.ts.map +0 -1
  151. package/build/relay/wordlist.js +0 -7782
  152. package/build/relay/wordlist.js.map +0 -1
  153. package/build/schema/types.d.ts +0 -67
  154. package/build/schema/types.d.ts.map +0 -1
  155. package/build/schema/types.js +0 -2
  156. package/build/schema/types.js.map +0 -1
  157. package/build/storage/mode.d.ts +0 -23
  158. package/build/storage/mode.d.ts.map +0 -1
  159. package/build/storage/mode.js +0 -46
  160. package/build/storage/mode.js.map +0 -1
  161. package/build/storage/session-lock.d.ts +0 -19
  162. package/build/storage/session-lock.d.ts.map +0 -1
  163. package/build/storage/session-lock.js +0 -105
  164. package/build/storage/session-lock.js.map +0 -1
  165. package/build/transport/oauth-middleware.d.ts +0 -45
  166. package/build/transport/oauth-middleware.d.ts.map +0 -1
  167. package/build/transport/oauth-middleware.js +0 -86
  168. package/build/transport/oauth-middleware.js.map +0 -1
  169. /package/build/{relay → util}/browser.d.ts +0 -0
@@ -0,0 +1,470 @@
1
+ /**
2
+ * SQLite-backed provider key pool and account registry (`~/.hull/hull.db`).
3
+ *
4
+ * Mirrors `packages/core-py/src/hull_core/credentials/store.py`, including the
5
+ * table DDL and the shared `v1:` secret envelope, so either stack can open a
6
+ * pool the other created. The tables are additive and migration-safe:
7
+ * `CREATE TABLE/INDEX IF NOT EXISTS` only, no `ALTER`, so a database written by
8
+ * an older hull (only the `kv` table) keeps working and older hull ignores the
9
+ * new tables.
10
+ *
11
+ * Scope rules:
12
+ * - A pool is one provider identity; `account_id IS NULL` is that provider's
13
+ * default pool and a named account has its own pool. Rotation never hops
14
+ * across accounts.
15
+ * - Entries are ordered by `priority` then insertion order.
16
+ * - Rotation state lives in the database, not process memory, so two tasks (or
17
+ * two processes) never reuse a credential that is cooling down or leased.
18
+ */
19
+ import { mkdirSync } from 'node:fs';
20
+ import { dirname, join } from 'node:path';
21
+ import Database from 'better-sqlite3';
22
+ import { AccountRegistry } from './account-registry.js';
23
+ import { getHomeDir } from './home-dir.js';
24
+ import { AccountError, CredentialExistsError, CredentialNotFoundError, KeyPool, providerFromBaseUrl } from './key-pool.js';
25
+ import { CredentialCryptoError, keyHint, SecretCipher, secretsEqual } from './key-pool-crypto.js';
26
+ export const KEY_POOL_SCHEMA = `
27
+ CREATE TABLE IF NOT EXISTS provider_accounts (
28
+ account_id TEXT PRIMARY KEY,
29
+ provider TEXT NOT NULL,
30
+ name TEXT NOT NULL,
31
+ email TEXT,
32
+ workspace TEXT,
33
+ labels TEXT NOT NULL DEFAULT '{}',
34
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
35
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
36
+ );
37
+
38
+ CREATE UNIQUE INDEX IF NOT EXISTS provider_accounts_name
39
+ ON provider_accounts(provider, name);
40
+
41
+ CREATE TABLE IF NOT EXISTS provider_credentials (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ provider TEXT NOT NULL,
44
+ account_id TEXT,
45
+ label TEXT,
46
+ base_url TEXT NOT NULL,
47
+ model TEXT,
48
+ secret TEXT NOT NULL,
49
+ key_hint TEXT NOT NULL,
50
+ priority INTEGER NOT NULL DEFAULT 0,
51
+ disabled INTEGER NOT NULL DEFAULT 0,
52
+ last_status INTEGER,
53
+ failure_count INTEGER NOT NULL DEFAULT 0,
54
+ cooldown_until REAL NOT NULL DEFAULT 0,
55
+ lease_until REAL NOT NULL DEFAULT 0,
56
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
57
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
58
+ );
59
+
60
+
61
+ CREATE INDEX IF NOT EXISTS provider_credentials_pool
62
+ ON provider_credentials(provider, account_id, priority, id);
63
+ `;
64
+ const COLUMNS = 'id, provider, account_id, label, base_url, model, secret, key_hint, ' +
65
+ 'priority, disabled, last_status, failure_count, cooldown_until, lease_until';
66
+ const TOUCH = "updated_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')";
67
+ /** The instance database every hull surface already uses: `~/.hull/hull.db`. */
68
+ export function defaultHullDbPath() {
69
+ return join(getHomeDir(), '.hull', 'hull.db');
70
+ }
71
+ /** Key file that seals this database's credentials (same directory as the DB). */
72
+ export function credentialKeyPath(dbPath) {
73
+ return join(dirname(dbPath), '.credential-key');
74
+ }
75
+ function required(value, field) {
76
+ if (typeof value !== 'string' || !value.trim())
77
+ throw new Error(`${field} must be a non-empty string`);
78
+ return value.trim();
79
+ }
80
+ function optional(value) {
81
+ if (value === null || value === undefined)
82
+ return null;
83
+ const text = value.trim();
84
+ return text || null;
85
+ }
86
+ function makeCredential(row, apiKey) {
87
+ const owner = row.account_id ?? row.label ?? String(row.id);
88
+ const masked = `${row.provider}/${owner}:${row.key_hint}`;
89
+ return {
90
+ id: row.id,
91
+ provider: row.provider,
92
+ accountId: row.account_id,
93
+ label: row.label,
94
+ baseUrl: row.base_url,
95
+ model: row.model,
96
+ apiKey,
97
+ priority: row.priority,
98
+ keyHint: row.key_hint,
99
+ masked,
100
+ toString() {
101
+ return `Credential(${masked}, base_url=${row.base_url})`;
102
+ },
103
+ toJSON() {
104
+ return {
105
+ id: row.id,
106
+ provider: row.provider,
107
+ accountId: row.account_id,
108
+ label: row.label,
109
+ baseUrl: row.base_url,
110
+ model: row.model,
111
+ priority: row.priority,
112
+ keyHint: row.key_hint
113
+ };
114
+ }
115
+ };
116
+ }
117
+ /** Ordered provider credential pool plus named accounts, on local SQLite. */
118
+ export class CredentialStore {
119
+ constructor(dbPath = defaultHullDbPath(), options = {}) {
120
+ this.path = dbPath;
121
+ this.clock = options.now ?? (() => Date.now() / 1000);
122
+ this.cipher = options.cipher ?? new SecretCipher(credentialKeyPath(dbPath));
123
+ mkdirSync(dirname(dbPath), { recursive: true, mode: 0o700 });
124
+ this.db = new Database(dbPath);
125
+ this.db.pragma('journal_mode = WAL');
126
+ this.db.pragma('busy_timeout = 5000');
127
+ this.db.exec(KEY_POOL_SCHEMA);
128
+ }
129
+ now() {
130
+ return this.clock();
131
+ }
132
+ close() {
133
+ this.db.close();
134
+ }
135
+ // -- row mapping ------------------------------------------------------
136
+ toCredential(row) {
137
+ return makeCredential(row, this.cipher.decrypt(row.secret));
138
+ }
139
+ toHealth(row, now) {
140
+ const cooldownUntil = row.cooldown_until;
141
+ const leaseUntil = row.lease_until;
142
+ return {
143
+ id: row.id,
144
+ provider: row.provider,
145
+ accountId: row.account_id,
146
+ label: row.label,
147
+ baseUrl: row.base_url,
148
+ model: row.model,
149
+ keyHint: row.key_hint,
150
+ priority: row.priority,
151
+ disabled: Boolean(row.disabled),
152
+ lastStatus: row.last_status,
153
+ failureCount: row.failure_count,
154
+ cooldownUntil,
155
+ leaseUntil,
156
+ cooldownRemaining: Math.max(0, cooldownUntil - now),
157
+ usable: !row.disabled && cooldownUntil <= now && leaseUntil <= now
158
+ };
159
+ }
160
+ row(credentialId) {
161
+ const row = this.db
162
+ .prepare(`SELECT ${COLUMNS} FROM provider_credentials WHERE id = ?`)
163
+ .get(credentialId);
164
+ if (!row)
165
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
166
+ return row;
167
+ }
168
+ requireAccount(accountId, provider) {
169
+ const row = this.db
170
+ .prepare('SELECT * FROM provider_accounts WHERE account_id = ?')
171
+ .get(accountId);
172
+ if (!row)
173
+ throw new AccountError(`unknown account ${JSON.stringify(accountId)}; add it first`);
174
+ if (row.provider !== provider) {
175
+ throw new AccountError(`account ${JSON.stringify(accountId)} belongs to provider ${JSON.stringify(row.provider)}, not ${JSON.stringify(provider)}`);
176
+ }
177
+ return row;
178
+ }
179
+ rejectDuplicate(provider, accountId, apiKey) {
180
+ const rows = this.db
181
+ .prepare('SELECT secret FROM provider_credentials WHERE provider = ? AND account_id IS ?')
182
+ .all(provider, accountId);
183
+ for (const row of rows) {
184
+ let existing;
185
+ try {
186
+ existing = this.cipher.decrypt(row.secret);
187
+ }
188
+ catch (error) {
189
+ if (error instanceof CredentialCryptoError)
190
+ continue;
191
+ throw error;
192
+ }
193
+ if (secretsEqual(existing, apiKey)) {
194
+ throw new CredentialExistsError(`provider ${JSON.stringify(provider)} already has this credential` +
195
+ (accountId ? ` for account ${JSON.stringify(accountId)}` : ''));
196
+ }
197
+ }
198
+ }
199
+ // -- writes -----------------------------------------------------------
200
+ addCredential(input) {
201
+ const provider = required(input.provider, 'provider');
202
+ const apiKey = required(input.apiKey, 'apiKey');
203
+ const baseUrl = required(input.baseUrl, 'baseUrl').replace(/\/+$/, '');
204
+ const accountId = optional(input.accountId);
205
+ const label = optional(input.label);
206
+ const model = optional(input.model);
207
+ const secret = this.cipher.encrypt(apiKey);
208
+ const hint = keyHint(apiKey);
209
+ const write = this.db.transaction(() => {
210
+ if (accountId !== null)
211
+ this.requireAccount(accountId, provider);
212
+ this.rejectDuplicate(provider, accountId, apiKey);
213
+ let priority = input.priority ?? null;
214
+ if (priority === null || priority === undefined) {
215
+ const next = this.db
216
+ .prepare('SELECT COALESCE(MAX(priority), -1) + 1 AS next FROM provider_credentials WHERE provider = ? AND account_id IS ?')
217
+ .get(provider, accountId);
218
+ priority = next?.next ?? 0;
219
+ }
220
+ const result = this.db
221
+ .prepare('INSERT INTO provider_credentials ' +
222
+ '(provider, account_id, label, base_url, model, secret, key_hint, priority) ' +
223
+ 'VALUES (?, ?, ?, ?, ?, ?, ?, ?)')
224
+ .run(provider, accountId, label, baseUrl, model, secret, hint, priority);
225
+ return this.row(Number(result.lastInsertRowid));
226
+ });
227
+ return this.toCredential(write.immediate());
228
+ }
229
+ removeCredential(credentialId) {
230
+ const result = this.db.prepare('DELETE FROM provider_credentials WHERE id = ?').run(credentialId);
231
+ if (result.changes === 0)
232
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
233
+ }
234
+ setPriority(credentialId, priority) {
235
+ const result = this.db
236
+ .prepare(`UPDATE provider_credentials SET priority = ?, ${TOUCH} WHERE id = ?`)
237
+ .run(priority, credentialId);
238
+ if (result.changes === 0)
239
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
240
+ }
241
+ /** Clear disabled/cooldown/failure state after an operator fixes the key. */
242
+ enable(credentialId) {
243
+ const result = this.db
244
+ .prepare(`UPDATE provider_credentials SET disabled = 0, last_status = NULL, failure_count = 0, ` +
245
+ `cooldown_until = 0, lease_until = 0, ${TOUCH} WHERE id = ?`)
246
+ .run(credentialId);
247
+ if (result.changes === 0)
248
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
249
+ }
250
+ disable(credentialId) {
251
+ const result = this.db
252
+ .prepare(`UPDATE provider_credentials SET disabled = 1, lease_until = 0, ${TOUCH} WHERE id = ?`)
253
+ .run(credentialId);
254
+ if (result.changes === 0)
255
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
256
+ }
257
+ /** Clear rotation state for one provider (or every provider); returns rows matched. */
258
+ resetHealth(provider) {
259
+ const sql = 'UPDATE provider_credentials SET disabled = 0, last_status = NULL, failure_count = 0, ' +
260
+ `cooldown_until = 0, lease_until = 0, ${TOUCH}`;
261
+ const result = provider
262
+ ? this.db.prepare(`${sql} WHERE provider = ?`).run(provider)
263
+ : this.db.prepare(sql).run();
264
+ return result.changes;
265
+ }
266
+ // -- reads ------------------------------------------------------------
267
+ /** Every decrypted entry (all accounts), ordered by provider/priority/id. */
268
+ credentials(provider) {
269
+ const sql = `SELECT ${COLUMNS} FROM provider_credentials`;
270
+ const rows = provider
271
+ ? this.db
272
+ .prepare(`${sql} WHERE provider = ? ORDER BY provider, priority, id`)
273
+ .all(provider)
274
+ : this.db.prepare(`${sql} ORDER BY provider, priority, id`).all();
275
+ return rows.map((row) => this.toCredential(row));
276
+ }
277
+ /** The rotation pool for one provider/account scope, in priority order. */
278
+ poolCredentials(provider, accountId = null) {
279
+ const rows = this.db
280
+ .prepare(`SELECT ${COLUMNS} FROM provider_credentials WHERE provider = ? AND account_id IS ? ORDER BY priority, id`)
281
+ .all(required(provider, 'provider'), optional(accountId));
282
+ return rows.map((row) => this.toCredential(row));
283
+ }
284
+ /** Masked state of every entry, including disabled and cooling ones. */
285
+ health(provider) {
286
+ const now = this.clock();
287
+ const sql = `SELECT ${COLUMNS} FROM provider_credentials`;
288
+ const rows = provider
289
+ ? this.db
290
+ .prepare(`${sql} WHERE provider = ? ORDER BY provider, priority, id`)
291
+ .all(provider)
292
+ : this.db.prepare(`${sql} ORDER BY provider, priority, id`).all();
293
+ return rows.map((row) => this.toHealth(row, now));
294
+ }
295
+ poolHealth(provider, accountId = null) {
296
+ const now = this.clock();
297
+ const rows = this.db
298
+ .prepare(`SELECT ${COLUMNS} FROM provider_credentials WHERE provider = ? AND account_id IS ? ORDER BY priority, id`)
299
+ .all(required(provider, 'provider'), optional(accountId));
300
+ return rows.map((row) => this.toHealth(row, now));
301
+ }
302
+ getCredential(credentialId) {
303
+ return this.toCredential(this.row(credentialId));
304
+ }
305
+ diagnose(provider, accountId = null) {
306
+ const now = this.clock();
307
+ const rows = this.db
308
+ .prepare('SELECT disabled, cooldown_until, lease_until FROM provider_credentials WHERE provider = ? AND account_id IS ?')
309
+ .all(required(provider, 'provider'), optional(accountId));
310
+ const disabled = rows.filter((row) => row.disabled).length;
311
+ const cooling = rows.filter((row) => !row.disabled && row.cooldown_until > now).map((row) => row.cooldown_until);
312
+ const leased = rows.filter((row) => !row.disabled && row.cooldown_until <= now && row.lease_until > now).length;
313
+ return {
314
+ total: rows.length,
315
+ disabled,
316
+ coolingDown: cooling.length,
317
+ leased,
318
+ retryAfter: cooling.length ? Math.min(...cooling) - now : null
319
+ };
320
+ }
321
+ // -- rotation state (used by KeyPool) ---------------------------------
322
+ /** Atomically lease the highest-priority usable entry, or return null. */
323
+ leaseFirst(provider, accountId, options) {
324
+ const excluded = [...new Set(options.exclude ?? [])];
325
+ let sql = `SELECT ${COLUMNS} FROM provider_credentials WHERE provider = ? AND account_id IS ? AND disabled = 0 AND cooldown_until <= ? AND lease_until <= ?`;
326
+ const params = [required(provider, 'provider'), optional(accountId), options.now, options.now];
327
+ if (excluded.length > 0) {
328
+ sql += ` AND id NOT IN (${excluded.map(() => '?').join(',')})`;
329
+ params.push(...excluded);
330
+ }
331
+ sql += ' ORDER BY priority, id LIMIT 1';
332
+ const lease = this.db.transaction(() => {
333
+ const row = this.db.prepare(sql).get(...params);
334
+ if (!row)
335
+ return null;
336
+ this.db
337
+ .prepare(`UPDATE provider_credentials SET lease_until = ?, ${TOUCH} WHERE id = ?`)
338
+ .run(options.now + options.leaseSeconds, row.id);
339
+ return this.toCredential(row);
340
+ });
341
+ return lease.immediate();
342
+ }
343
+ releaseLease(credentialId) {
344
+ const result = this.db
345
+ .prepare(`UPDATE provider_credentials SET lease_until = 0, ${TOUCH} WHERE id = ?`)
346
+ .run(credentialId);
347
+ if (result.changes === 0)
348
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
349
+ }
350
+ recordSuccess(credentialId) {
351
+ const result = this.db
352
+ .prepare(`UPDATE provider_credentials SET last_status = 200, failure_count = 0, cooldown_until = 0, lease_until = 0, ${TOUCH} WHERE id = ?`)
353
+ .run(credentialId);
354
+ if (result.changes === 0)
355
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
356
+ }
357
+ recordFailure(credentialId, status, options = {}) {
358
+ const result = this.db
359
+ .prepare('UPDATE provider_credentials SET last_status = ?, failure_count = failure_count + 1, ' +
360
+ 'cooldown_until = COALESCE(?, cooldown_until), ' +
361
+ `disabled = CASE WHEN ? THEN 1 ELSE disabled END, lease_until = 0, ${TOUCH} WHERE id = ?`)
362
+ .run(status, options.cooldownUntil ?? null, options.disable ? 1 : 0, credentialId);
363
+ if (result.changes === 0)
364
+ throw new CredentialNotFoundError(`no provider credential with id ${credentialId}`);
365
+ }
366
+ // -- accounts ---------------------------------------------------------
367
+ accounts() {
368
+ return new AccountRegistry(this);
369
+ }
370
+ accountRows() {
371
+ return this.db.prepare('SELECT * FROM provider_accounts ORDER BY provider, name').all();
372
+ }
373
+ accountRow(accountId) {
374
+ return this.db.prepare('SELECT * FROM provider_accounts WHERE account_id = ?').get(accountId);
375
+ }
376
+ accountRowsByName(name) {
377
+ return this.db.prepare('SELECT * FROM provider_accounts WHERE name = ?').all(name);
378
+ }
379
+ insertAccount(account) {
380
+ try {
381
+ this.db
382
+ .prepare('INSERT INTO provider_accounts (account_id, provider, name, email, workspace, labels) VALUES (?, ?, ?, ?, ?, ?)')
383
+ .run(account.accountId, account.provider, account.name, account.email, account.workspace, JSON.stringify(account.labels));
384
+ }
385
+ catch (error) {
386
+ if (error instanceof Error && error.message.includes('UNIQUE')) {
387
+ throw new AccountError(`account ${JSON.stringify(account.accountId)} or the name ${JSON.stringify(account.name)} ` +
388
+ `for provider ${JSON.stringify(account.provider)} already exists`);
389
+ }
390
+ throw error;
391
+ }
392
+ }
393
+ updateAccountRow(accountId, values) {
394
+ const result = this.db
395
+ .prepare(`UPDATE provider_accounts SET name = ?, email = ?, workspace = ?, labels = ?, ${TOUCH} WHERE account_id = ?`)
396
+ .run(values.name, values.email, values.workspace, values.labelsJson, accountId);
397
+ if (result.changes === 0)
398
+ throw new AccountError(`unknown account ${JSON.stringify(accountId)}`);
399
+ }
400
+ deleteAccountRow(accountId, options = {}) {
401
+ const remove = this.db.transaction(() => {
402
+ const count = this.db
403
+ .prepare('SELECT COUNT(*) AS n FROM provider_credentials WHERE account_id = ?')
404
+ .get(accountId);
405
+ const total = count?.n ?? 0;
406
+ if (total > 0 && !options.dropCredentials) {
407
+ throw new AccountError(`account ${JSON.stringify(accountId)} still has ${total} credential(s); pass dropCredentials: true ` +
408
+ 'to delete them with the account');
409
+ }
410
+ this.db.prepare('DELETE FROM provider_credentials WHERE account_id = ?').run(accountId);
411
+ const result = this.db.prepare('DELETE FROM provider_accounts WHERE account_id = ?').run(accountId);
412
+ if (result.changes === 0)
413
+ throw new AccountError(`unknown account ${JSON.stringify(accountId)}`);
414
+ });
415
+ remove.immediate();
416
+ }
417
+ // -- pools ------------------------------------------------------------
418
+ pool(provider, options = {}) {
419
+ return new KeyPool(this, provider, options);
420
+ }
421
+ /** Resolve a `[models.<task>]`-shaped cell's `provider`/`account` pointers. */
422
+ poolForCell(cell, options = {}) {
423
+ const provider = optional(cell.provider) ?? providerFromBaseUrl(cell.baseUrl);
424
+ return this.pool(provider, { ...options, accountId: optional(cell.account) });
425
+ }
426
+ /**
427
+ * Seed the pool from the pre-pool single-key config, idempotently. Every
428
+ * distinct `(baseUrl, apiKey)` becomes one entry in insertion order; running
429
+ * it again skips what is already stored.
430
+ */
431
+ migrateModelCells(cells, options = {}) {
432
+ const items = [...cells];
433
+ if (items.length === 0)
434
+ return { provider: options.provider ?? 'default', added: 0, skipped: 0 };
435
+ let provider = options.provider ?? null;
436
+ if (provider === null) {
437
+ const keyed = items.filter((cell) => (cell.apiKey ?? '').length > 0);
438
+ const names = new Set((keyed.length > 0 ? keyed : items).map((cell) => cell.provider || providerFromBaseUrl(cell.baseUrl)));
439
+ if (names.size !== 1) {
440
+ throw new Error(`model cells span providers [${[...names].sort().join(', ')}]; pass provider explicitly`);
441
+ }
442
+ provider = [...names][0];
443
+ }
444
+ let added = 0;
445
+ let skipped = 0;
446
+ for (const cell of items) {
447
+ const apiKey = cell.apiKey ?? '';
448
+ if (!apiKey)
449
+ continue;
450
+ const target = options.accountId ?? optional(cell.account);
451
+ try {
452
+ this.addCredential({
453
+ provider,
454
+ apiKey,
455
+ baseUrl: cell.baseUrl,
456
+ model: cell.model ?? null,
457
+ accountId: target
458
+ });
459
+ added += 1;
460
+ }
461
+ catch (error) {
462
+ if (!(error instanceof CredentialExistsError))
463
+ throw error;
464
+ skipped += 1;
465
+ }
466
+ }
467
+ return { provider, added, skipped };
468
+ }
469
+ }
470
+ //# sourceMappingURL=key-pool-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-pool-store.js","sourceRoot":"","sources":["../../src/storage/key-pool-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Credential rotation over one provider pool (TypeScript mirror of
3
+ * `hull_core/credentials/pool.py`).
4
+ *
5
+ * Failure policy:
6
+ *
7
+ * | status | action |
8
+ * |---------------|-----------------------------------------------------------|
9
+ * | 401 / 403 | disable the credential and move to the next one |
10
+ * | 429 | cool it down (`Retry-After` / `rate_limit_reset`, 1s..1h) |
11
+ * | 5xx | retry the same credential once, then move to the next one |
12
+ * | anything else | release the lease and re-raise (request problem, not key) |
13
+ *
14
+ * The pool is bounded at `credentials * (1 + max5xxRetries)` calls, and the
15
+ * final error carries the redacted last error text — a provider that echoes a
16
+ * key back cannot leak it through hull.
17
+ */
18
+ import type { CredentialStore } from './key-pool-store.js';
19
+ export declare const DEFAULT_COOLDOWN_SECONDS = 60;
20
+ export declare const MIN_COOLDOWN_SECONDS = 1;
21
+ export declare const MAX_COOLDOWN_SECONDS = 3600;
22
+ /** Host -> provider identity used when config does not name one explicitly. */
23
+ export declare const KNOWN_PROVIDER_HOSTS: Readonly<Record<string, string>>;
24
+ /**
25
+ * Derive the provider identity a base URL belongs to. Known hosts map to their
26
+ * short name; anything else becomes the lowercased host with dots replaced by
27
+ * hyphens. An unparseable URL falls back to `default`.
28
+ */
29
+ export declare function providerFromBaseUrl(baseUrl: string): string;
30
+ /** One decrypted pool entry. `apiKey` is never part of `toString`/`toJSON`. */
31
+ export interface Credential {
32
+ readonly id: number;
33
+ readonly provider: string;
34
+ readonly accountId: string | null;
35
+ readonly label: string | null;
36
+ readonly baseUrl: string;
37
+ readonly model: string | null;
38
+ readonly apiKey: string;
39
+ readonly priority: number;
40
+ readonly keyHint: string;
41
+ readonly masked: string;
42
+ toString(): string;
43
+ toJSON(): Record<string, unknown>;
44
+ }
45
+ /** Masked state of one pool entry — no key material at all. */
46
+ export interface CredentialHealth {
47
+ readonly id: number;
48
+ readonly provider: string;
49
+ readonly accountId: string | null;
50
+ readonly label: string | null;
51
+ readonly baseUrl: string;
52
+ readonly model: string | null;
53
+ readonly keyHint: string;
54
+ readonly priority: number;
55
+ readonly disabled: boolean;
56
+ readonly lastStatus: number | null;
57
+ readonly failureCount: number;
58
+ readonly cooldownUntil: number;
59
+ readonly leaseUntil: number;
60
+ readonly cooldownRemaining: number;
61
+ readonly usable: boolean;
62
+ }
63
+ /** A named provider identity (one email/workspace per row). */
64
+ export interface Account {
65
+ readonly accountId: string;
66
+ readonly provider: string;
67
+ readonly name: string;
68
+ readonly email: string | null;
69
+ readonly workspace: string | null;
70
+ readonly labels: Record<string, string>;
71
+ }
72
+ /** Outcome of seeding a pool from the pre-pool single-key config. */
73
+ export interface MigrationResult {
74
+ readonly provider: string;
75
+ readonly added: number;
76
+ readonly skipped: number;
77
+ }
78
+ export type FailureAction = 'disable' | 'cooldown' | 'retry' | 'fatal';
79
+ export declare class CredentialPoolError extends Error {
80
+ constructor(message: string);
81
+ }
82
+ export declare class PoolExhaustedError extends CredentialPoolError {
83
+ readonly provider: string;
84
+ readonly accountId: string | null;
85
+ readonly total: number;
86
+ readonly disabled: number;
87
+ readonly coolingDown: number;
88
+ readonly leased: number;
89
+ readonly retryAfter: number | null;
90
+ readonly lastError: string | null;
91
+ constructor(message: string, details: {
92
+ provider: string;
93
+ accountId?: string | null;
94
+ total?: number;
95
+ disabled?: number;
96
+ coolingDown?: number;
97
+ leased?: number;
98
+ retryAfter?: number | null;
99
+ lastError?: string | null;
100
+ });
101
+ }
102
+ export declare class CredentialExistsError extends CredentialPoolError {
103
+ constructor(message: string);
104
+ }
105
+ export declare class CredentialNotFoundError extends CredentialPoolError {
106
+ constructor(message: string);
107
+ }
108
+ export declare class AccountError extends CredentialPoolError {
109
+ constructor(message: string);
110
+ }
111
+ /** Map an HTTP status to `disable` / `cooldown` / `retry` / `fatal`. */
112
+ export declare function classifyStatus(status: number | null | undefined): FailureAction;
113
+ /** `Retry-After` as delay seconds (numeric form or HTTP-date). */
114
+ export declare function parseRetryAfter(value: unknown, now?: number): number | null;
115
+ /**
116
+ * `rate_limit_reset`/`x-ratelimit-reset` as delay seconds. Providers use two
117
+ * shapes: an epoch second (above 1e9) and a delta already in seconds.
118
+ */
119
+ export declare function parseRateLimitReset(value: unknown, now?: number): number | null;
120
+ export interface CooldownOptions {
121
+ retryAfter?: unknown;
122
+ rateLimitReset?: unknown;
123
+ now?: number;
124
+ defaultSeconds?: number;
125
+ minimum?: number;
126
+ maximum?: number;
127
+ }
128
+ /** Cooldown for a 429: provider hint first, then the default, always clamped. */
129
+ export declare function cooldownSeconds(options?: CooldownOptions): number;
130
+ export interface KeyPoolOptions {
131
+ accountId?: string | null;
132
+ now?: () => number;
133
+ max5xxRetries?: number;
134
+ leaseSeconds?: number;
135
+ }
136
+ /**
137
+ * Ordered, health-aware rotation over one provider's credentials. A pool is
138
+ * either the provider's default pool (`accountId` null) or one named account's.
139
+ */
140
+ export declare class KeyPool {
141
+ readonly store: CredentialStore;
142
+ readonly provider: string;
143
+ readonly accountId: string | null;
144
+ readonly max5xxRetries: number;
145
+ readonly leaseSeconds: number;
146
+ private readonly clock;
147
+ constructor(store: CredentialStore, provider: string, options?: KeyPoolOptions);
148
+ credentials(): Credential[];
149
+ health(): CredentialHealth[];
150
+ toString(): string;
151
+ acquire(options?: {
152
+ exclude?: number[];
153
+ }): Credential;
154
+ reportSuccess(credential: Credential): void;
155
+ reportFailure(credential: Credential, status: number | null, options?: {
156
+ retryAfter?: unknown;
157
+ rateLimitReset?: unknown;
158
+ }): FailureAction;
159
+ enable(credentialId: number): void;
160
+ /** Replace every stored key of this provider in `text` with `[redacted]`. */
161
+ redact(text: string | null | undefined): string;
162
+ /** HTTP status carried by a provider error (`ProviderError.status`). */
163
+ static statusOf(error: unknown): number | null;
164
+ exhaustedError(lastError?: string | null): PoolExhaustedError;
165
+ run<T>(call: (credential: Credential) => T, options?: {
166
+ statusOf?: (error: unknown) => number | null;
167
+ }): T;
168
+ runAsync<T>(call: (credential: Credential) => Promise<T>, options?: {
169
+ statusOf?: (error: unknown) => number | null;
170
+ }): Promise<T>;
171
+ }
172
+ //# sourceMappingURL=key-pool.d.ts.map