@ank1015/llm-sdk-adapters 0.0.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.
@@ -0,0 +1,603 @@
1
+ /**
2
+ * Keys UI — A basic HTML interface for managing API keys.
3
+ *
4
+ * Starts a local HTTP server that serves a single-page UI and exposes
5
+ * a small REST API backed by FileKeysAdapter.
6
+ *
7
+ * Usage:
8
+ * import { startKeysUI } from './keys-ui.js';
9
+ * startKeysUI({ port: 7700 });
10
+ *
11
+ * Or run directly:
12
+ * node dist/keys-ui.js
13
+ */
14
+ import { execSync } from 'node:child_process';
15
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'node:fs';
16
+ import * as http from 'node:http';
17
+ import { createServer } from 'node:http';
18
+ import * as https from 'node:https';
19
+ import { homedir, tmpdir } from 'node:os';
20
+ import { join } from 'node:path';
21
+ import { KnownApis, isValidApi } from '@ank1015/llm-types';
22
+ import { FileKeysAdapter } from './file-keys.js';
23
+ // ────────────────────────────────────────────────────────────────────
24
+ // Credential reload logic per provider
25
+ // ────────────────────────────────────────────────────────────────────
26
+ /** Providers that support auto-reloading credentials from local config files. */
27
+ const RELOADABLE_APIS = new Set(['codex', 'claude-code']);
28
+ /**
29
+ * Reload credentials for a provider. Async because claude-code
30
+ * extraction requires starting a local proxy and an SDK call.
31
+ */
32
+ async function reloadCredentials(api) {
33
+ switch (api) {
34
+ case 'codex': {
35
+ const authPath = join(homedir(), '.codex', 'auth.json');
36
+ if (!existsSync(authPath)) {
37
+ throw new Error(`Codex auth file not found: ${authPath}`);
38
+ }
39
+ const auth = JSON.parse(readFileSync(authPath, 'utf8'));
40
+ const accessToken = auth.tokens?.access_token;
41
+ const accountId = auth.tokens?.account_id;
42
+ if (!accessToken) {
43
+ throw new Error('No access_token found in codex auth.json');
44
+ }
45
+ const creds = { apiKey: accessToken };
46
+ if (accountId) {
47
+ creds['chatgpt-account-id'] = accountId;
48
+ }
49
+ return creds;
50
+ }
51
+ case 'claude-code':
52
+ return extractClaudeCodeCredentials();
53
+ default:
54
+ throw new Error(`Reload not supported for ${api}`);
55
+ }
56
+ }
57
+ /**
58
+ * Extract Claude Code credentials (OAuth token, billing header, beta flags)
59
+ * by making one throwaway SDK call through a temp local proxy.
60
+ */
61
+ function extractClaudeCodeCredentials() {
62
+ return new Promise((resolve, reject) => {
63
+ const timeout = setTimeout(() => reject(new Error('Timeout: no credentials captured in 30s')), 30_000);
64
+ const server = http.createServer((req, res) => {
65
+ let body = '';
66
+ req.on('data', (chunk) => (body += chunk.toString()));
67
+ req.on('end', () => {
68
+ const isMainCall = req.url?.startsWith('/v1/messages') &&
69
+ !req.url?.includes('count_tokens') &&
70
+ req.method === 'POST' &&
71
+ body.includes('x-anthropic-billing-header');
72
+ if (isMainCall) {
73
+ const parsed = JSON.parse(body);
74
+ const billingBlock = parsed.system?.find((b) => b.text?.includes('x-anthropic-billing-header'));
75
+ const authRaw = req.headers['authorization'] || '';
76
+ const token = authRaw.replace('Bearer ', '');
77
+ const creds = {
78
+ oauthToken: token,
79
+ billingHeader: billingBlock?.text ?? '',
80
+ betaFlag: req.headers['anthropic-beta'] || '',
81
+ };
82
+ clearTimeout(timeout);
83
+ // SSE streaming response so CLI exits cleanly
84
+ const msgId = `msg_fake_${Date.now()}`;
85
+ const events = [
86
+ {
87
+ type: 'message_start',
88
+ message: {
89
+ id: msgId,
90
+ type: 'message',
91
+ role: 'assistant',
92
+ content: [],
93
+ model: 'claude-sonnet-4-5-20250929',
94
+ stop_reason: null,
95
+ stop_sequence: null,
96
+ usage: { input_tokens: 1, output_tokens: 0 },
97
+ },
98
+ },
99
+ { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } },
100
+ { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'ok' } },
101
+ { type: 'content_block_stop', index: 0 },
102
+ {
103
+ type: 'message_delta',
104
+ delta: { stop_reason: 'end_turn', stop_sequence: null },
105
+ usage: { output_tokens: 1 },
106
+ },
107
+ { type: 'message_stop' },
108
+ ];
109
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
110
+ for (const evt of events) {
111
+ res.write(`event: ${evt.type}\ndata: ${JSON.stringify(evt)}\n\n`);
112
+ }
113
+ res.end();
114
+ server.close();
115
+ resolve({
116
+ oauthToken: creds.oauthToken,
117
+ billingHeader: creds.billingHeader,
118
+ betaFlag: creds.betaFlag,
119
+ });
120
+ return;
121
+ }
122
+ // Forward non-main requests (quota check etc) to the real API
123
+ const headers = {};
124
+ for (const [key, val] of Object.entries(req.headers)) {
125
+ if (key !== 'host' && key !== 'content-length' && val) {
126
+ headers[key] = Array.isArray(val) ? val[0] : val;
127
+ }
128
+ }
129
+ headers['host'] = 'api.anthropic.com';
130
+ headers['content-length'] = Buffer.byteLength(body).toString();
131
+ const proxyReq = https.request({ hostname: 'api.anthropic.com', port: 443, path: req.url, method: req.method, headers }, (proxyRes) => {
132
+ res.writeHead(proxyRes.statusCode ?? 200, proxyRes.headers);
133
+ proxyRes.pipe(res);
134
+ });
135
+ proxyReq.on('error', () => {
136
+ res.writeHead(502);
137
+ res.end('error');
138
+ });
139
+ proxyReq.end(body);
140
+ });
141
+ });
142
+ server.listen(0, '127.0.0.1', async () => {
143
+ const addr = server.address();
144
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
145
+ // Find claude binary
146
+ let claudePath;
147
+ try {
148
+ claudePath = execSync('which claude', { encoding: 'utf8' }).trim();
149
+ }
150
+ catch {
151
+ clearTimeout(timeout);
152
+ server.close();
153
+ reject(new Error('Claude CLI not found. Install it first.'));
154
+ return;
155
+ }
156
+ const wrapperPath = join(tmpdir(), `claude-cred-extract-${port}.sh`);
157
+ writeFileSync(wrapperPath, [
158
+ '#!/bin/bash',
159
+ `export ANTHROPIC_BASE_URL="http://127.0.0.1:${port}"`,
160
+ 'unset CLAUDECODE',
161
+ `exec "${claudePath}" "$@"`,
162
+ ].join('\n'));
163
+ chmodSync(wrapperPath, 0o755);
164
+ try {
165
+ const { query } = await import('@anthropic-ai/claude-agent-sdk');
166
+ for await (const _ of query({
167
+ prompt: 'hi',
168
+ options: {
169
+ systemPrompt: 'Reply ok',
170
+ allowedTools: [],
171
+ maxTurns: 1,
172
+ model: 'sonnet',
173
+ pathToClaudeCodeExecutable: wrapperPath,
174
+ },
175
+ })) {
176
+ /* drain */
177
+ }
178
+ }
179
+ catch {
180
+ // Expected — proxy closes before SDK finishes cleanly
181
+ }
182
+ try {
183
+ unlinkSync(wrapperPath);
184
+ }
185
+ catch {
186
+ /* ignore */
187
+ }
188
+ });
189
+ });
190
+ }
191
+ // ────────────────────────────────────────────────────────────────────
192
+ // HTML template
193
+ // ────────────────────────────────────────────────────────────────────
194
+ function getHtml() {
195
+ return /* html */ `<!DOCTYPE html>
196
+ <html lang="en">
197
+ <head>
198
+ <meta charset="UTF-8" />
199
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
200
+ <title>LLM Keys Manager</title>
201
+ <style>
202
+ * { box-sizing: border-box; margin: 0; padding: 0; }
203
+ body {
204
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
205
+ background: #0f0f0f; color: #e0e0e0;
206
+ max-width: 640px; margin: 0 auto; padding: 24px 16px;
207
+ }
208
+ h1 { font-size: 1.4rem; margin-bottom: 4px; }
209
+ .subtitle { color: #888; font-size: 0.85rem; margin-bottom: 24px; }
210
+ .provider {
211
+ border: 1px solid #2a2a2a; border-radius: 8px;
212
+ margin-bottom: 8px; overflow: hidden;
213
+ transition: border-color 0.15s;
214
+ }
215
+ .provider:hover { border-color: #444; }
216
+ .provider-header {
217
+ display: flex; align-items: center; justify-content: space-between;
218
+ padding: 12px 16px; cursor: pointer; user-select: none;
219
+ }
220
+ .provider-header:hover { background: #1a1a1a; }
221
+ .provider-name { font-weight: 600; font-size: 0.95rem; }
222
+ .badge {
223
+ font-size: 0.7rem; padding: 2px 8px; border-radius: 9999px;
224
+ font-weight: 500; text-transform: uppercase; letter-spacing: 0.03em;
225
+ }
226
+ .badge-set { background: #064e3b; color: #6ee7b7; }
227
+ .badge-missing { background: #3b1313; color: #fca5a5; }
228
+ .provider-body {
229
+ display: none; padding: 0 16px 16px;
230
+ border-top: 1px solid #2a2a2a;
231
+ }
232
+ .provider-body.open { display: block; }
233
+ .cred-row {
234
+ display: flex; gap: 8px; margin-top: 10px; align-items: center;
235
+ }
236
+ .cred-row label {
237
+ min-width: 80px; font-size: 0.8rem; color: #aaa;
238
+ }
239
+ .cred-row input {
240
+ flex: 1; padding: 6px 10px; border-radius: 6px; border: 1px solid #333;
241
+ background: #1a1a1a; color: #e0e0e0; font-size: 0.85rem;
242
+ font-family: "SF Mono", "Fira Code", monospace;
243
+ }
244
+ .cred-row input:focus { outline: none; border-color: #555; }
245
+ .btn-eye {
246
+ background: none; border: 1px solid #333; border-radius: 6px;
247
+ color: #888; padding: 4px 7px; font-size: 0.85rem; cursor: pointer;
248
+ line-height: 1; flex-shrink: 0;
249
+ }
250
+ .btn-eye:hover { color: #e0e0e0; border-color: #555; }
251
+ .actions { display: flex; gap: 8px; margin-top: 14px; }
252
+ button {
253
+ padding: 6px 16px; border-radius: 6px; border: none;
254
+ font-size: 0.8rem; font-weight: 500; cursor: pointer;
255
+ transition: opacity 0.15s;
256
+ }
257
+ button:hover { opacity: 0.85; }
258
+ button:disabled { opacity: 0.4; cursor: not-allowed; }
259
+ .btn-save { background: #2563eb; color: #fff; }
260
+ .btn-reload { background: #7c3aed; color: #e9d5ff; }
261
+ .btn-delete { background: #7f1d1d; color: #fca5a5; }
262
+ .toast {
263
+ position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
264
+ padding: 8px 20px; border-radius: 8px; font-size: 0.8rem;
265
+ pointer-events: none; opacity: 0; transition: opacity 0.3s;
266
+ }
267
+ .toast.show { opacity: 1; }
268
+ .toast-ok { background: #064e3b; color: #6ee7b7; }
269
+ .toast-err { background: #7f1d1d; color: #fca5a5; }
270
+ </style>
271
+ </head>
272
+ <body>
273
+ <h1>LLM Keys Manager</h1>
274
+ <p class="subtitle">Set and manage API keys for your providers</p>
275
+ <div id="list"></div>
276
+ <div id="toast" class="toast"></div>
277
+
278
+ <script>
279
+ const PROVIDERS = ${JSON.stringify(KnownApis)};
280
+ const RELOADABLE = ${JSON.stringify([...RELOADABLE_APIS])};
281
+ let keysStatus = {};
282
+
283
+ function toast(msg, ok = true) {
284
+ const el = document.getElementById('toast');
285
+ el.textContent = msg;
286
+ el.className = 'toast show ' + (ok ? 'toast-ok' : 'toast-err');
287
+ setTimeout(() => el.className = 'toast', 2000);
288
+ }
289
+
290
+ async function loadKeys() {
291
+ const res = await fetch('/api/keys');
292
+ const data = await res.json();
293
+ keysStatus = {};
294
+ for (const p of data.providers) {
295
+ keysStatus[p.api] = p;
296
+ }
297
+ render();
298
+ }
299
+
300
+ function toggle(api) {
301
+ const body = document.getElementById('body-' + api);
302
+ body.classList.toggle('open');
303
+ }
304
+
305
+ function render() {
306
+ const list = document.getElementById('list');
307
+ list.innerHTML = PROVIDERS.map(api => {
308
+ const info = keysStatus[api];
309
+ const hasKey = info && info.hasKey;
310
+ const creds = (info && info.credentials) || {};
311
+ const credKeys = Object.keys(creds);
312
+ // Always show at least an apiKey field
313
+ const fields = credKeys.length > 0 ? credKeys : ['apiKey'];
314
+
315
+ return '<div class="provider">'
316
+ + '<div class="provider-header" onclick="toggle(\\'' + api + '\\')">'
317
+ + ' <span class="provider-name">' + api + '</span>'
318
+ + ' <span class="badge ' + (hasKey ? 'badge-set' : 'badge-missing') + '">'
319
+ + (hasKey ? 'set' : 'missing') + '</span>'
320
+ + '</div>'
321
+ + '<div class="provider-body" id="body-' + api + '">'
322
+ + fields.map(function(k) {
323
+ const val = creds[k] || '';
324
+ return '<div class="cred-row">'
325
+ + '<label>' + k + '</label>'
326
+ + '<input id="input-' + api + '-' + k + '" type="password"'
327
+ + ' value="' + val.replace(/"/g, '&quot;') + '"'
328
+ + ' placeholder="Enter ' + k + '" />'
329
+ + (hasKey ? '<button class="btn-eye" title="Reveal" onclick="event.stopPropagation();toggleReveal(\\'' + api + '\\',\\'' + k + '\\',this)">\u{1F441}</button>' : '')
330
+ + '</div>';
331
+ }).join('')
332
+ + '<div class="cred-row">'
333
+ + ' <label style="color:#666">+ field</label>'
334
+ + ' <input id="newfield-' + api + '" placeholder="field name" style="max-width:140px" />'
335
+ + ' <button class="btn-save" onclick="addField(\\'' + api + '\\')" style="padding:4px 10px">Add</button>'
336
+ + '</div>'
337
+ + '<div class="actions">'
338
+ + ' <button class="btn-save" onclick="save(\\'' + api + '\\')">Save</button>'
339
+ + (RELOADABLE.includes(api) ? ' <button id="reload-' + api + '" class="btn-reload" onclick="reload(\\'' + api + '\\')">Reload</button>' : '')
340
+ + ' <button class="btn-delete" onclick="del(\\'' + api + '\\')">Delete</button>'
341
+ + '</div>'
342
+ + '</div>'
343
+ + '</div>';
344
+ }).join('');
345
+ }
346
+
347
+ function addField(api) {
348
+ const input = document.getElementById('newfield-' + api);
349
+ const name = input.value.trim();
350
+ if (!name) return;
351
+ // Add to keysStatus so it renders
352
+ if (!keysStatus[api]) keysStatus[api] = { api: api, hasKey: false, credentials: {} };
353
+ if (!keysStatus[api].credentials) keysStatus[api].credentials = {};
354
+ keysStatus[api].credentials[name] = '';
355
+ render();
356
+ // Re-open the body
357
+ document.getElementById('body-' + api).classList.add('open');
358
+ }
359
+
360
+ async function save(api) {
361
+ const info = keysStatus[api];
362
+ const creds = (info && info.credentials) || {};
363
+ const fields = Object.keys(creds).length > 0 ? Object.keys(creds) : ['apiKey'];
364
+
365
+ const credentials = {};
366
+ for (const k of fields) {
367
+ const el = document.getElementById('input-' + api + '-' + k);
368
+ if (el && el.value.trim()) {
369
+ credentials[k] = el.value.trim();
370
+ }
371
+ }
372
+
373
+ if (Object.keys(credentials).length === 0) {
374
+ toast('Enter at least one value', false);
375
+ return;
376
+ }
377
+
378
+ const res = await fetch('/api/keys/' + api, {
379
+ method: 'PUT',
380
+ headers: { 'Content-Type': 'application/json' },
381
+ body: JSON.stringify({ credentials }),
382
+ });
383
+ if (res.ok) {
384
+ toast('Saved ' + api);
385
+ await loadKeys();
386
+ document.getElementById('body-' + api).classList.add('open');
387
+ } else {
388
+ toast('Failed to save', false);
389
+ }
390
+ }
391
+
392
+ async function del(api) {
393
+ if (!confirm('Delete all keys for ' + api + '?')) return;
394
+ const res = await fetch('/api/keys/' + api, { method: 'DELETE' });
395
+ if (res.ok) {
396
+ toast('Deleted ' + api);
397
+ await loadKeys();
398
+ } else {
399
+ toast('Failed to delete', false);
400
+ }
401
+ }
402
+
403
+ async function reload(api) {
404
+ const btn = document.getElementById('reload-' + api);
405
+ if (btn) { btn.disabled = true; btn.textContent = 'Reloading\u2026'; }
406
+ try {
407
+ const res = await fetch('/api/keys/' + api + '/reload', { method: 'POST' });
408
+ const data = await res.json();
409
+ if (res.ok && data.ok) {
410
+ toast('Reloaded ' + api);
411
+ await loadKeys();
412
+ document.getElementById('body-' + api).classList.add('open');
413
+ } else {
414
+ toast(data.error || 'Reload failed', false);
415
+ }
416
+ } finally {
417
+ if (btn) { btn.disabled = false; btn.textContent = 'Reload'; }
418
+ }
419
+ }
420
+
421
+ async function toggleReveal(api, field, btn) {
422
+ const input = document.getElementById('input-' + api + '-' + field);
423
+ if (!input) return;
424
+ if (input.type === 'text') {
425
+ // Hide — restore masked value
426
+ input.type = 'password';
427
+ const info = keysStatus[api];
428
+ const masked = (info && info.credentials && info.credentials[field]) || '';
429
+ input.value = masked;
430
+ input.dataset.revealed = '';
431
+ btn.style.color = '';
432
+ return;
433
+ }
434
+ // Reveal — fetch real value from server
435
+ try {
436
+ const res = await fetch('/api/keys/' + api);
437
+ if (!res.ok) { toast('Failed to fetch key', false); return; }
438
+ const data = await res.json();
439
+ const real = data.credentials && data.credentials[field];
440
+ if (real) {
441
+ input.type = 'text';
442
+ input.value = real;
443
+ input.dataset.revealed = '1';
444
+ btn.style.color = '#6ee7b7';
445
+ } else {
446
+ toast('No value found', false);
447
+ }
448
+ } catch { toast('Failed to fetch key', false); }
449
+ }
450
+
451
+ loadKeys();
452
+ </script>
453
+ </body>
454
+ </html>`;
455
+ }
456
+ // ────────────────────────────────────────────────────────────────────
457
+ // HTTP helpers
458
+ // ────────────────────────────────────────────────────────────────────
459
+ function json(res, data, status = 200) {
460
+ res.writeHead(status, { 'Content-Type': 'application/json' });
461
+ res.end(JSON.stringify(data));
462
+ }
463
+ function html(res, body) {
464
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
465
+ res.end(body);
466
+ }
467
+ function readBody(req) {
468
+ return new Promise((resolve, reject) => {
469
+ const chunks = [];
470
+ req.on('data', (c) => chunks.push(c));
471
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
472
+ req.on('error', reject);
473
+ });
474
+ }
475
+ /**
476
+ * Mask a credential value for safe display (show first 4 and last 4 chars).
477
+ */
478
+ function mask(value) {
479
+ if (value.length <= 10)
480
+ return '••••••••';
481
+ return value.slice(0, 4) + '••••' + value.slice(-4);
482
+ }
483
+ // ────────────────────────────────────────────────────────────────────
484
+ // Route handler
485
+ // ────────────────────────────────────────────────────────────────────
486
+ async function handleRequest(req, res, adapter) {
487
+ const url = req.url ?? '/';
488
+ const method = req.method ?? 'GET';
489
+ // Serve HTML page
490
+ if (url === '/' && method === 'GET') {
491
+ html(res, getHtml());
492
+ return;
493
+ }
494
+ // GET /api/keys — list all providers with key status
495
+ if (url === '/api/keys' && method === 'GET') {
496
+ const storedApis = await adapter.list();
497
+ const providers = await Promise.all(KnownApis.map(async (api) => {
498
+ const hasKey = storedApis.includes(api);
499
+ let credentials;
500
+ if (hasKey) {
501
+ const raw = await adapter.getCredentials(api);
502
+ if (raw) {
503
+ credentials = {};
504
+ for (const [k, v] of Object.entries(raw)) {
505
+ credentials[k] = mask(v);
506
+ }
507
+ }
508
+ }
509
+ return { api, hasKey, credentials };
510
+ }));
511
+ json(res, { ok: true, providers });
512
+ return;
513
+ }
514
+ // POST /api/keys/:api/reload — auto-fetch credentials from local config
515
+ const reloadMatch = url.match(/^\/api\/keys\/([a-z0-9-]+)\/reload$/);
516
+ if (reloadMatch && method === 'POST') {
517
+ const api = reloadMatch[1];
518
+ if (!isValidApi(api)) {
519
+ json(res, { ok: false, error: `Unknown provider: ${api}` }, 400);
520
+ return;
521
+ }
522
+ if (!RELOADABLE_APIS.has(api)) {
523
+ json(res, { ok: false, error: `Reload not supported for ${api}` }, 400);
524
+ return;
525
+ }
526
+ const credentials = await reloadCredentials(api);
527
+ await adapter.setCredentials(api, credentials);
528
+ json(res, { ok: true });
529
+ return;
530
+ }
531
+ // Routes with :api param
532
+ const apiMatch = url.match(/^\/api\/keys\/([a-z0-9-]+)$/);
533
+ if (apiMatch) {
534
+ const api = apiMatch[1];
535
+ if (!isValidApi(api)) {
536
+ json(res, { ok: false, error: `Unknown provider: ${api}` }, 400);
537
+ return;
538
+ }
539
+ // GET /api/keys/:api — get unmasked credentials
540
+ if (method === 'GET') {
541
+ const credentials = await adapter.getCredentials(api);
542
+ json(res, { ok: true, credentials: credentials ?? {} });
543
+ return;
544
+ }
545
+ // PUT /api/keys/:api — set key or credentials
546
+ if (method === 'PUT') {
547
+ const body = JSON.parse(await readBody(req));
548
+ if (body.credentials && typeof body.credentials === 'object') {
549
+ await adapter.setCredentials(api, body.credentials);
550
+ }
551
+ else if (typeof body.key === 'string') {
552
+ await adapter.set(api, body.key);
553
+ }
554
+ else {
555
+ json(res, { ok: false, error: 'Provide "key" or "credentials"' }, 400);
556
+ return;
557
+ }
558
+ json(res, { ok: true });
559
+ return;
560
+ }
561
+ // DELETE /api/keys/:api
562
+ if (method === 'DELETE') {
563
+ const deleted = await adapter.delete(api);
564
+ json(res, { ok: true, deleted });
565
+ return;
566
+ }
567
+ }
568
+ // 404
569
+ res.writeHead(404);
570
+ res.end('Not found');
571
+ }
572
+ /**
573
+ * Start the Keys UI server.
574
+ *
575
+ * @returns A handle with the HTTP server and URL.
576
+ */
577
+ export function startKeysUI(options = {}) {
578
+ const port = options.port ?? 7700;
579
+ const adapter = new FileKeysAdapter(options.keysDir);
580
+ const server = createServer((req, res) => {
581
+ handleRequest(req, res, adapter).catch((err) => {
582
+ const message = err instanceof Error ? err.message : 'Internal server error';
583
+ json(res, { ok: false, error: message }, 500);
584
+ });
585
+ });
586
+ server.listen(port, () => {
587
+ const url = `http://localhost:${port}`;
588
+ console.log(`Keys UI running at ${url}`);
589
+ });
590
+ return { server, url: `http://localhost:${port}` };
591
+ }
592
+ // ────────────────────────────────────────────────────────────────────
593
+ // CLI entry point — run directly with: node dist/keys-ui.js
594
+ // ────────────────────────────────────────────────────────────────────
595
+ const isDirectRun = typeof process !== 'undefined' &&
596
+ process.argv[1] &&
597
+ (process.argv[1].endsWith('keys-ui.js') || process.argv[1].endsWith('keys-ui.ts'));
598
+ if (isDirectRun) {
599
+ const port = parseInt(process.env.PORT ?? '7700', 10);
600
+ const keysDir = process.env.LLM_KEYS_DIR;
601
+ startKeysUI({ port, keysDir });
602
+ }
603
+ //# sourceMappingURL=keys-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys-ui.js","sourceRoot":"","sources":["../src/keys-ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * In-memory Keys Adapter
3
+ *
4
+ * Simple Map-based implementation for testing. No persistence, no encryption.
5
+ */
6
+ import type { KeysAdapter, Api } from '@ank1015/llm-types';
7
+ /**
8
+ * In-memory implementation of KeysAdapter for testing.
9
+ */
10
+ export declare class InMemoryKeysAdapter implements KeysAdapter {
11
+ private credentials;
12
+ get(api: Api): Promise<string | undefined>;
13
+ getCredentials(api: Api): Promise<Record<string, string> | undefined>;
14
+ set(api: Api, key: string): Promise<void>;
15
+ setCredentials(api: Api, credentials: Record<string, string>): Promise<void>;
16
+ delete(api: Api): Promise<boolean>;
17
+ deleteCredentials(api: Api): Promise<boolean>;
18
+ list(): Promise<Api[]>;
19
+ }
20
+ //# sourceMappingURL=memory-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-keys.d.ts","sourceRoot":"","sources":["../src/memory-keys.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAiC3D;;GAEG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,WAAW,CAA0C;IAEvD,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI1C,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAKrE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5E,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlC,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAI7C,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;CAG7B"}