@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.
- package/dist/file-keys.d.ts +68 -0
- package/dist/file-keys.d.ts.map +1 -0
- package/dist/file-keys.js +243 -0
- package/dist/file-keys.js.map +1 -0
- package/dist/file-sessions.d.ts +55 -0
- package/dist/file-sessions.d.ts.map +1 -0
- package/dist/file-sessions.js +376 -0
- package/dist/file-sessions.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/keys-ui.d.ts +30 -0
- package/dist/keys-ui.d.ts.map +1 -0
- package/dist/keys-ui.js +603 -0
- package/dist/keys-ui.js.map +1 -0
- package/dist/memory-keys.d.ts +20 -0
- package/dist/memory-keys.d.ts.map +1 -0
- package/dist/memory-keys.js +57 -0
- package/dist/memory-keys.js.map +1 -0
- package/dist/memory-sessions.d.ts +34 -0
- package/dist/memory-sessions.d.ts.map +1 -0
- package/dist/memory-sessions.js +236 -0
- package/dist/memory-sessions.js.map +1 -0
- package/dist/memory-usage.d.ts +18 -0
- package/dist/memory-usage.d.ts.map +1 -0
- package/dist/memory-usage.js +123 -0
- package/dist/memory-usage.js.map +1 -0
- package/dist/sqlite-usage.d.ts +36 -0
- package/dist/sqlite-usage.d.ts.map +1 -0
- package/dist/sqlite-usage.js +344 -0
- package/dist/sqlite-usage.js.map +1 -0
- package/package.json +48 -0
package/dist/keys-ui.js
ADDED
|
@@ -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, '"') + '"'
|
|
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"}
|