@elvatis_com/openclaw-cli-bridge-elvatis 1.3.3 → 1.3.5
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/.ai/handoff/STATUS.md +28 -19
- package/README.md +91 -1
- package/index.ts +99 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/.ai/handoff/STATUS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# STATUS — openclaw-cli-bridge-elvatis
|
|
2
2
|
|
|
3
|
-
## Current Version: 1.
|
|
3
|
+
## Current Version: 1.3.3 (npm + ClawHub + GitHub) ✅ RELEASED
|
|
4
4
|
|
|
5
|
-
## All 4 Providers
|
|
5
|
+
## All 4 Providers Available — on-demand via /xxx-login
|
|
6
6
|
| Provider | Status | Models | Command |
|
|
7
7
|
|---|---|---|---|
|
|
8
8
|
| Grok | ✅ | web-grok/grok-3, grok-3-fast, grok-3-mini, grok-3-mini-fast | /grok-login |
|
|
@@ -10,26 +10,35 @@
|
|
|
10
10
|
| Gemini | ✅ | web-gemini/gemini-2-5-pro, gemini-2-5-flash, gemini-3-pro, gemini-3-flash | /gemini-login |
|
|
11
11
|
| ChatGPT | ✅ | web-chatgpt/gpt-4o, gpt-4o-mini, gpt-o3, gpt-o4-mini, gpt-5 | /chatgpt-login |
|
|
12
12
|
|
|
13
|
-
Live test: "What is the capital of France?"
|
|
14
|
-
- Grok: "Paris" ✅
|
|
15
|
-
- Claude: "Paris" ✅
|
|
16
|
-
- Gemini: "Paris" ✅
|
|
17
|
-
- ChatGPT: "Paris" ✅
|
|
18
|
-
|
|
19
13
|
## Stats
|
|
20
14
|
- 22 total models, 16 web-session models
|
|
21
15
|
- 96/96 tests green (8 test files)
|
|
22
|
-
- 0 zombie Chromium processes
|
|
16
|
+
- 0 zombie Chromium processes at startup (browsers on-demand only)
|
|
23
17
|
- Cookie expiry tracking for all 4 providers
|
|
18
|
+
- Singleton guard on ensureAllProviderContexts (no concurrent spawns)
|
|
19
|
+
|
|
20
|
+
## Architecture: Browser Lifecycle
|
|
21
|
+
- **On plugin start:** NO browser launched automatically
|
|
22
|
+
- **On /xxx-login:** launches persistent Chromium for that provider only
|
|
23
|
+
- **On request (no context):** returns null → caller sees "not logged in" error
|
|
24
|
+
- **On /xxx-logout:** closes context + deletes session file
|
|
25
|
+
|
|
26
|
+
## Known Issues
|
|
27
|
+
- Cloudflare may block headless Chromium for Claude/Gemini without a valid CDP session
|
|
28
|
+
→ Workaround: have OpenClaw browser open on the provider's page, then /xxx-login
|
|
24
29
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
## Release History
|
|
31
|
+
- v1.3.3 (2026-03-12): Remove startup auto-connect — browsers on-demand only (OOM fix)
|
|
32
|
+
- v1.3.2 (2026-03-12): Singleton guard on ensureAllProviderContexts (resource leak fix)
|
|
33
|
+
- v1.3.1 (2026-03-11): Cookie baking into persistent profiles on login
|
|
34
|
+
- v1.3.0 (2026-03-11): Browser auto-reconnect after gateway restart
|
|
35
|
+
- v1.2.0 (2026-03-11): Fresh page per request + ChatGPT model switching
|
|
36
|
+
- v1.1.0 (2026-03-11): Auto-connect on startup + /bridge-status
|
|
37
|
+
- v1.0.0 (2026-03-11): All 4 providers headless (Grok/Claude/Gemini/ChatGPT) — 96/96 tests
|
|
38
|
+
- v0.2.x: Grok (v0.2.26-28), Claude (v0.2.29), Gemini (v0.2.30)
|
|
39
|
+
- v0.2.25: Sleep-resilient token refresh + staged /cli-* switching
|
|
29
40
|
|
|
30
|
-
## Next Steps
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Handle model-switching within chatgpt.com (dropdown selector)
|
|
35
|
-
- Handle Gemini model switching (2.5 Pro vs Flash vs 3)
|
|
41
|
+
## Next Steps
|
|
42
|
+
- Context-window management for long conversations
|
|
43
|
+
- Gemini model switching (2.5 Pro vs Flash vs 3) via UI
|
|
44
|
+
- /bridge-status: show per-provider login state + cookie expiry
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> OpenClaw plugin that bridges locally installed AI CLIs (Codex, Gemini, Claude Code) as model providers — with slash commands for instant model switching, restore, health testing, and model listing.
|
|
4
4
|
|
|
5
|
-
**Current version:** `1.3.
|
|
5
|
+
**Current version:** `1.3.5`
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -81,6 +81,77 @@ All commands use gateway-level `commands.allowFrom` for authorization (`requireA
|
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
84
|
+
### Phase 4 — Web Browser Providers (headless, no API key needed)
|
|
85
|
+
|
|
86
|
+
Routes requests through real browser sessions on the provider's web UI. Requires a valid login session (free or paid tier). Uses persistent Chromium profiles — sessions survive gateway restarts.
|
|
87
|
+
|
|
88
|
+
**Grok** (grok.com — SuperGrok subscription):
|
|
89
|
+
|
|
90
|
+
| Model | Notes |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `web-grok/grok-3` | Full model |
|
|
93
|
+
| `web-grok/grok-3-fast` | Faster variant |
|
|
94
|
+
| `web-grok/grok-3-mini` | Lightweight |
|
|
95
|
+
| `web-grok/grok-3-mini-fast` | Fastest |
|
|
96
|
+
|
|
97
|
+
| Command | What it does |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `/grok-login` | Authenticate via X.com OAuth, save session to `~/.openclaw/grok-profile/` |
|
|
100
|
+
| `/grok-status` | Show session validity + cookie expiry |
|
|
101
|
+
| `/grok-logout` | Clear session |
|
|
102
|
+
|
|
103
|
+
**Claude** (claude.ai):
|
|
104
|
+
|
|
105
|
+
| Model | Notes |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `web-claude/claude-sonnet` | Sonnet |
|
|
108
|
+
| `web-claude/claude-opus` | Opus |
|
|
109
|
+
| `web-claude/claude-haiku` | Haiku |
|
|
110
|
+
|
|
111
|
+
| Command | What it does |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `/claude-login` | Authenticate, save cookies to `~/.openclaw/claude-profile/` |
|
|
114
|
+
| `/claude-status` | Show session validity + cookie expiry |
|
|
115
|
+
| `/claude-logout` | Clear session |
|
|
116
|
+
|
|
117
|
+
**Gemini** (gemini.google.com):
|
|
118
|
+
|
|
119
|
+
| Model | Notes |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `web-gemini/gemini-2-5-pro` | Gemini 2.5 Pro |
|
|
122
|
+
| `web-gemini/gemini-2-5-flash` | Gemini 2.5 Flash |
|
|
123
|
+
| `web-gemini/gemini-3-pro` | Gemini 3 Pro |
|
|
124
|
+
| `web-gemini/gemini-3-flash` | Gemini 3 Flash |
|
|
125
|
+
|
|
126
|
+
| Command | What it does |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `/gemini-login` | Authenticate, save cookies to `~/.openclaw/gemini-profile/` |
|
|
129
|
+
| `/gemini-status` | Show session validity + cookie expiry |
|
|
130
|
+
| `/gemini-logout` | Clear session |
|
|
131
|
+
|
|
132
|
+
**ChatGPT** (chatgpt.com):
|
|
133
|
+
|
|
134
|
+
| Model | Notes |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `web-chatgpt/gpt-4o` | GPT-4o |
|
|
137
|
+
| `web-chatgpt/gpt-4o-mini` | GPT-4o Mini |
|
|
138
|
+
| `web-chatgpt/gpt-o3` | o3 |
|
|
139
|
+
| `web-chatgpt/gpt-o4-mini` | o4-mini |
|
|
140
|
+
| `web-chatgpt/gpt-5` | GPT-5 |
|
|
141
|
+
|
|
142
|
+
| Command | What it does |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `/chatgpt-login` | Authenticate, save cookies to `~/.openclaw/chatgpt-profile/` |
|
|
145
|
+
| `/chatgpt-status` | Show session validity + cookie expiry |
|
|
146
|
+
| `/chatgpt-logout` | Clear session |
|
|
147
|
+
|
|
148
|
+
**Session lifecycle:**
|
|
149
|
+
- First use: run `/xxx-login` once (opens Chromium, authenticate in browser)
|
|
150
|
+
- After gateway restart: sessions are **automatically restored** from saved profiles on startup (sequential, ~25s after start)
|
|
151
|
+
- `/bridge-status` — shows all 4 providers at a glance with login state + expiry info
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
84
155
|
## Requirements
|
|
85
156
|
|
|
86
157
|
- [OpenClaw](https://openclaw.ai) gateway (tested with `2026.3.x`)
|
|
@@ -287,6 +358,25 @@ npm test # vitest run (45 tests)
|
|
|
287
358
|
|
|
288
359
|
## Changelog
|
|
289
360
|
|
|
361
|
+
### v1.3.5
|
|
362
|
+
- **fix:** Startup session restore now runs only once per process lifetime — `_startupRestoreDone` module-level guard prevents re-running on every hot-reload (SIGUSR1), which was triggered every ~60s by the openclaw-control-ui dashboard poll
|
|
363
|
+
- **root cause:** Gateway `reload.mode=hybrid` + dashboard status polling caused plugin to reinitialize every 60s → each reload spawned a new Gemini Chromium instance → RAM/CPU OOM loop
|
|
364
|
+
- **behavior:** First load after gateway start: sequential profile restore runs once. All subsequent hot-reloads: skip restore, reuse existing in-memory contexts
|
|
365
|
+
|
|
366
|
+
### v1.3.4
|
|
367
|
+
- **feat:** Safe sequential session restore on startup — if a saved profile exists, providers are reconnected automatically after gateway restart (one at a time, 3s delay between each, headless)
|
|
368
|
+
- **fix:** No manual `/xxx-login` needed after reboot if profile is already saved
|
|
369
|
+
- **safety:** Profile-gated — only restores if `~/.openclaw/<provider>-profile/` or cookie file exists; never spawns a browser for an uninitialized provider
|
|
370
|
+
|
|
371
|
+
### v1.3.3
|
|
372
|
+
- **fix:** Removed auto-connect of all browser providers on plugin startup — caused OOM (load 195, 30GB RAM) by spawning 4+ persistent Chromium instances on every gateway start
|
|
373
|
+
- **fix:** Removed Grok session restore on startup — same root cause
|
|
374
|
+
- **behavior change:** Browsers are now started **on-demand only** via `/grok-login`, `/claude-login`, `/gemini-login`, `/chatgpt-login`
|
|
375
|
+
|
|
376
|
+
### v1.3.2
|
|
377
|
+
- **fix:** Singleton promise guard on `ensureAllProviderContexts()` — concurrent requests no longer each spawn their own Chromium; extra callers await the existing run
|
|
378
|
+
- **fix:** Removed recursive `ensureAllProviderContexts()` fallback from all `connect*Context` proxy callbacks — no more exponential browser spawn on CDP failure
|
|
379
|
+
|
|
290
380
|
### v1.3.1
|
|
291
381
|
- **fix:** /claude-login, /gemini-login, /chatgpt-login now bake cookies into persistent profile dirs
|
|
292
382
|
- **fix:** After gateway restart, providers auto-reconnect from saved profile (no browser tabs needed)
|
package/index.ts
CHANGED
|
@@ -233,6 +233,10 @@ async function scanCookieExpiry(ctx: import("playwright").BrowserContext): Promi
|
|
|
233
233
|
let _cdpBrowser: import("playwright").Browser | null = null;
|
|
234
234
|
let _cdpBrowserLaunchPromise: Promise<import("playwright").BrowserContext | null> | null = null;
|
|
235
235
|
|
|
236
|
+
// Startup restore guard — module-level so it survives hot-reloads (SIGUSR1).
|
|
237
|
+
// Set to true after first run; hot-reloads see true and skip the restore loop.
|
|
238
|
+
let _startupRestoreDone = false;
|
|
239
|
+
|
|
236
240
|
/**
|
|
237
241
|
* Connect to the OpenClaw managed browser (CDP port 18800).
|
|
238
242
|
* Singleton: reuses the same connection. Falls back to persistent Chromium for Grok only.
|
|
@@ -746,10 +750,102 @@ const plugin = {
|
|
|
746
750
|
const codexAuthPath = cfg.codexAuthPath ?? DEFAULT_CODEX_AUTH_PATH;
|
|
747
751
|
const grokSessionPath = cfg.grokSessionPath ?? DEFAULT_SESSION_PATH;
|
|
748
752
|
|
|
749
|
-
//
|
|
753
|
+
// ── Session restore: only on first plugin load (not on hot-reloads) ──────
|
|
754
|
+
// The gateway polls every ~60s via openclaw status, which triggers a hot-reload
|
|
755
|
+
// (SIGUSR1 + hybrid mode). Module-level contexts (grokContext etc.) survive
|
|
756
|
+
// hot-reloads because Node keeps the module in memory — so we only need to
|
|
757
|
+
// restore once, on the very first load (when all contexts are null).
|
|
758
|
+
//
|
|
759
|
+
// Guard: _startupRestoreDone is module-level and persists across hot-reloads.
|
|
760
|
+
if (!_startupRestoreDone) {
|
|
761
|
+
_startupRestoreDone = true;
|
|
762
|
+
void (async () => {
|
|
763
|
+
await new Promise(r => setTimeout(r, 5000)); // wait for proxy + gateway to settle
|
|
764
|
+
const { chromium } = await import("playwright");
|
|
765
|
+
const { existsSync } = await import("node:fs");
|
|
766
|
+
|
|
767
|
+
const profileProviders: Array<{
|
|
768
|
+
name: string;
|
|
769
|
+
profileDir: string;
|
|
770
|
+
cookieFile: string;
|
|
771
|
+
verifySelector: string;
|
|
772
|
+
homeUrl: string;
|
|
773
|
+
setCtx: (c: BrowserContext) => void;
|
|
774
|
+
getCtx: () => BrowserContext | null;
|
|
775
|
+
}> = [
|
|
776
|
+
{
|
|
777
|
+
name: "grok",
|
|
778
|
+
profileDir: GROK_PROFILE_DIR,
|
|
779
|
+
cookieFile: join(homedir(), ".openclaw", "grok-session.json"),
|
|
780
|
+
verifySelector: "textarea",
|
|
781
|
+
homeUrl: "https://grok.com",
|
|
782
|
+
getCtx: () => grokContext,
|
|
783
|
+
setCtx: (c) => { grokContext = c; },
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
name: "claude",
|
|
787
|
+
profileDir: join(homedir(), ".openclaw", "claude-profile"),
|
|
788
|
+
cookieFile: join(homedir(), ".openclaw", "claude-cookie-expiry.json"),
|
|
789
|
+
verifySelector: ".ProseMirror",
|
|
790
|
+
homeUrl: "https://claude.ai/new",
|
|
791
|
+
getCtx: () => claudeContext,
|
|
792
|
+
setCtx: (c) => { claudeContext = c; },
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
name: "gemini",
|
|
796
|
+
profileDir: join(homedir(), ".openclaw", "gemini-profile"),
|
|
797
|
+
cookieFile: join(homedir(), ".openclaw", "gemini-cookie-expiry.json"),
|
|
798
|
+
verifySelector: ".ql-editor",
|
|
799
|
+
homeUrl: "https://gemini.google.com/app",
|
|
800
|
+
getCtx: () => geminiContext,
|
|
801
|
+
setCtx: (c) => { geminiContext = c; },
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
name: "chatgpt",
|
|
805
|
+
profileDir: join(homedir(), ".openclaw", "chatgpt-profile"),
|
|
806
|
+
cookieFile: join(homedir(), ".openclaw", "chatgpt-cookie-expiry.json"),
|
|
807
|
+
verifySelector: "#prompt-textarea",
|
|
808
|
+
homeUrl: "https://chatgpt.com",
|
|
809
|
+
getCtx: () => chatgptContext,
|
|
810
|
+
setCtx: (c) => { chatgptContext = c; },
|
|
811
|
+
},
|
|
812
|
+
];
|
|
750
813
|
|
|
751
|
-
|
|
752
|
-
|
|
814
|
+
for (const p of profileProviders) {
|
|
815
|
+
if (!existsSync(p.profileDir) && !existsSync(p.cookieFile)) {
|
|
816
|
+
api.logger.info(`[cli-bridge:${p.name}] no saved profile — skipping startup restore`);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (p.getCtx()) continue; // already connected
|
|
820
|
+
|
|
821
|
+
try {
|
|
822
|
+
api.logger.info(`[cli-bridge:${p.name}] restoring session from profile…`);
|
|
823
|
+
const ctx = await chromium.launchPersistentContext(p.profileDir, {
|
|
824
|
+
headless: true,
|
|
825
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
826
|
+
});
|
|
827
|
+
const page = await ctx.newPage();
|
|
828
|
+
await page.goto(p.homeUrl, { waitUntil: "domcontentloaded", timeout: 20_000 });
|
|
829
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
830
|
+
const ok = await page.locator(p.verifySelector).isVisible().catch(() => false);
|
|
831
|
+
await page.close().catch(() => {});
|
|
832
|
+
if (ok) {
|
|
833
|
+
p.setCtx(ctx);
|
|
834
|
+
ctx.on("close", () => { p.setCtx(null as unknown as BrowserContext); });
|
|
835
|
+
api.logger.info(`[cli-bridge:${p.name}] session restored from profile ✅`);
|
|
836
|
+
} else {
|
|
837
|
+
await ctx.close().catch(() => {});
|
|
838
|
+
api.logger.info(`[cli-bridge:${p.name}] profile exists but not logged in — needs /xxx-login`);
|
|
839
|
+
}
|
|
840
|
+
} catch (err) {
|
|
841
|
+
api.logger.warn(`[cli-bridge:${p.name}] startup restore failed: ${(err as Error).message}`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Sequential — never spawn all 4 Chromium instances at once
|
|
845
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
846
|
+
}
|
|
847
|
+
})();
|
|
848
|
+
}
|
|
753
849
|
|
|
754
850
|
// ── Phase 1: openai-codex auth bridge ─────────────────────────────────────
|
|
755
851
|
if (enableCodex) {
|
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.3.
|
|
4
|
+
"version": "1.3.5",
|
|
5
5
|
"description": "Phase 1: openai-codex auth bridge. Phase 2: local HTTP proxy routing model calls through gemini/claude CLIs (vllm provider).",
|
|
6
6
|
"providers": [
|
|
7
7
|
"openai-codex"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elvatis_com/openclaw-cli-bridge-elvatis",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
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": {
|