@elvatis_com/openclaw-cli-bridge-elvatis 1.7.0 → 1.7.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/index.ts +7 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/proxy-server.ts +114 -0
package/index.ts
CHANGED
|
@@ -1240,6 +1240,13 @@ const plugin = {
|
|
|
1240
1240
|
}
|
|
1241
1241
|
return chatgptContext;
|
|
1242
1242
|
},
|
|
1243
|
+
version: plugin.version,
|
|
1244
|
+
getExpiryInfo: () => ({
|
|
1245
|
+
grok: (() => { const e = loadGrokExpiry(); return e ? formatExpiryInfo(e) : null; })(),
|
|
1246
|
+
gemini: (() => { const e = loadGeminiExpiry(); return e ? formatGeminiExpiry(e) : null; })(),
|
|
1247
|
+
claude: (() => { const e = loadClaudeExpiry(); return e ? formatClaudeExpiry(e) : null; })(),
|
|
1248
|
+
chatgpt: (() => { const e = loadChatGPTExpiry(); return e ? formatChatGPTExpiry(e) : null; })(),
|
|
1249
|
+
}),
|
|
1243
1250
|
});
|
|
1244
1251
|
proxyServer = server;
|
|
1245
1252
|
api.logger.info(
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-cli-bridge-elvatis",
|
|
3
3
|
"name": "OpenClaw CLI Bridge",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Phase 1: openai-codex auth bridge. Phase 2: local HTTP proxy routing model calls through gemini/claude CLIs (vllm provider).",
|
|
7
7
|
"providers": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elvatis_com/openclaw-cli-bridge-elvatis",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Bridges gemini, claude, and codex CLI tools as OpenClaw model providers. Reads existing CLI auth without re-login.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"openclaw": {
|
package/src/proxy-server.ts
CHANGED
|
@@ -60,6 +60,15 @@ export interface ProxyServerOptions {
|
|
|
60
60
|
_chatgptComplete?: typeof chatgptComplete;
|
|
61
61
|
/** Override for testing — replaces chatgptCompleteStream */
|
|
62
62
|
_chatgptCompleteStream?: typeof chatgptCompleteStream;
|
|
63
|
+
/** Returns human-readable expiry string for each web provider (null = no login yet) */
|
|
64
|
+
getExpiryInfo?: () => {
|
|
65
|
+
grok: string | null;
|
|
66
|
+
gemini: string | null;
|
|
67
|
+
claude: string | null;
|
|
68
|
+
chatgpt: string | null;
|
|
69
|
+
};
|
|
70
|
+
/** Plugin version string for the status page */
|
|
71
|
+
version?: string;
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
/** Available CLI bridge models for GET /v1/models */
|
|
@@ -156,6 +165,111 @@ async function handleRequest(
|
|
|
156
165
|
return;
|
|
157
166
|
}
|
|
158
167
|
|
|
168
|
+
// Browser status page — human-readable HTML dashboard
|
|
169
|
+
if ((url === "/status" || url === "/") && req.method === "GET") {
|
|
170
|
+
const expiry = opts.getExpiryInfo?.() ?? { grok: null, gemini: null, claude: null, chatgpt: null };
|
|
171
|
+
const version = opts.version ?? "?";
|
|
172
|
+
|
|
173
|
+
const providers = [
|
|
174
|
+
{ name: "Grok", icon: "𝕏", expiry: expiry.grok, loginCmd: "/grok-login", ctx: opts.getGrokContext?.() ?? null },
|
|
175
|
+
{ name: "Gemini", icon: "✦", expiry: expiry.gemini, loginCmd: "/gemini-login", ctx: opts.getGeminiContext?.() ?? null },
|
|
176
|
+
{ name: "Claude", icon: "◆", expiry: expiry.claude, loginCmd: "/claude-login", ctx: opts.getClaudeContext?.() ?? null },
|
|
177
|
+
{ name: "ChatGPT", icon: "◉", expiry: expiry.chatgpt, loginCmd: "/chatgpt-login", ctx: opts.getChatGPTContext?.() ?? null },
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
function statusBadge(p: typeof providers[0]): { label: string; color: string; dot: string } {
|
|
181
|
+
if (p.ctx !== null) return { label: "Connected", color: "#22c55e", dot: "🟢" };
|
|
182
|
+
if (!p.expiry) return { label: "Never logged in", color: "#6b7280", dot: "⚪" };
|
|
183
|
+
if (p.expiry.startsWith("⚠️ EXPIRED")) return { label: "Expired", color: "#ef4444", dot: "🔴" };
|
|
184
|
+
if (p.expiry.startsWith("🚨")) return { label: "Expiring soon", color: "#f59e0b", dot: "🟡" };
|
|
185
|
+
return { label: "Logged in", color: "#3b82f6", dot: "🔵" };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const rows = providers.map(p => {
|
|
189
|
+
const badge = statusBadge(p);
|
|
190
|
+
const expiryText = p.expiry
|
|
191
|
+
? p.expiry.replace(/[⚠️🚨✅🕐]/gu, "").trim()
|
|
192
|
+
: `Not logged in — run <code>${p.loginCmd}</code>`;
|
|
193
|
+
return `
|
|
194
|
+
<tr>
|
|
195
|
+
<td style="padding:12px 16px;font-weight:600;font-size:15px">${p.icon} ${p.name}</td>
|
|
196
|
+
<td style="padding:12px 16px">
|
|
197
|
+
<span style="background:${badge.color}22;color:${badge.color};border:1px solid ${badge.color}44;
|
|
198
|
+
border-radius:6px;padding:3px 10px;font-size:13px;font-weight:600">
|
|
199
|
+
${badge.dot} ${badge.label}
|
|
200
|
+
</span>
|
|
201
|
+
</td>
|
|
202
|
+
<td style="padding:12px 16px;color:#9ca3af;font-size:13px">${expiryText}</td>
|
|
203
|
+
<td style="padding:12px 16px;color:#6b7280;font-size:12px;font-family:monospace">${p.loginCmd}</td>
|
|
204
|
+
</tr>`;
|
|
205
|
+
}).join("");
|
|
206
|
+
|
|
207
|
+
const cliModels = CLI_MODELS.filter(m => m.id.startsWith("cli-"));
|
|
208
|
+
const webModels = CLI_MODELS.filter(m => m.id.startsWith("web-"));
|
|
209
|
+
const modelList = (models: typeof CLI_MODELS) =>
|
|
210
|
+
models.map(m => `<li style="margin:2px 0;font-size:13px;color:#d1d5db"><code style="color:#93c5fd">${m.id}</code></li>`).join("");
|
|
211
|
+
|
|
212
|
+
const html = `<!DOCTYPE html>
|
|
213
|
+
<html lang="en">
|
|
214
|
+
<head>
|
|
215
|
+
<meta charset="UTF-8">
|
|
216
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
217
|
+
<title>CLI Bridge Status</title>
|
|
218
|
+
<meta http-equiv="refresh" content="30">
|
|
219
|
+
<style>
|
|
220
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
221
|
+
body { background: #0f1117; color: #e5e7eb; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; min-height: 100vh; padding: 32px 24px; }
|
|
222
|
+
h1 { font-size: 22px; font-weight: 700; color: #f9fafb; margin-bottom: 4px; }
|
|
223
|
+
.subtitle { color: #6b7280; font-size: 13px; margin-bottom: 28px; }
|
|
224
|
+
.card { background: #1a1d27; border: 1px solid #2d3148; border-radius: 12px; overflow: hidden; margin-bottom: 24px; }
|
|
225
|
+
.card-header { padding: 14px 16px; border-bottom: 1px solid #2d3148; font-size: 12px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; }
|
|
226
|
+
table { width: 100%; border-collapse: collapse; }
|
|
227
|
+
tr:not(:last-child) td { border-bottom: 1px solid #1f2335; }
|
|
228
|
+
.models { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
229
|
+
ul { list-style: none; padding: 12px 16px; }
|
|
230
|
+
.footer { color: #374151; font-size: 12px; text-align: center; margin-top: 16px; }
|
|
231
|
+
code { background: #1e2130; padding: 1px 5px; border-radius: 4px; }
|
|
232
|
+
</style>
|
|
233
|
+
</head>
|
|
234
|
+
<body>
|
|
235
|
+
<h1>🌉 CLI Bridge</h1>
|
|
236
|
+
<p class="subtitle">v${version} · Port ${opts.port} · Auto-refreshes every 30s</p>
|
|
237
|
+
|
|
238
|
+
<div class="card">
|
|
239
|
+
<div class="card-header">Web Session Providers</div>
|
|
240
|
+
<table>
|
|
241
|
+
<thead>
|
|
242
|
+
<tr style="background:#13151f">
|
|
243
|
+
<th style="padding:10px 16px;text-align:left;font-size:12px;color:#4b5563;font-weight:600">Provider</th>
|
|
244
|
+
<th style="padding:10px 16px;text-align:left;font-size:12px;color:#4b5563;font-weight:600">Status</th>
|
|
245
|
+
<th style="padding:10px 16px;text-align:left;font-size:12px;color:#4b5563;font-weight:600">Session</th>
|
|
246
|
+
<th style="padding:10px 16px;text-align:left;font-size:12px;color:#4b5563;font-weight:600">Login</th>
|
|
247
|
+
</tr>
|
|
248
|
+
</thead>
|
|
249
|
+
<tbody>${rows}</tbody>
|
|
250
|
+
</table>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="models">
|
|
254
|
+
<div class="card">
|
|
255
|
+
<div class="card-header">CLI Models (${cliModels.length})</div>
|
|
256
|
+
<ul>${modelList(cliModels)}</ul>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="card">
|
|
259
|
+
<div class="card-header">Web Session Models (${webModels.length})</div>
|
|
260
|
+
<ul>${modelList(webModels)}</ul>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<p class="footer">openclaw-cli-bridge-elvatis v${version} · <a href="/v1/models" style="color:#4b5563">/v1/models</a> · <a href="/health" style="color:#4b5563">/health</a></p>
|
|
265
|
+
</body>
|
|
266
|
+
</html>`;
|
|
267
|
+
|
|
268
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
269
|
+
res.end(html);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
159
273
|
// Model list
|
|
160
274
|
if (url === "/v1/models" && req.method === "GET") {
|
|
161
275
|
const now = Math.floor(Date.now() / 1000);
|