@elvatis_com/openclaw-cli-bridge-elvatis 1.7.1 → 1.7.3

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/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.7.0`
5
+ **Current version:** `1.7.3`
6
6
 
7
7
  ---
8
8
 
@@ -362,6 +362,16 @@ npm test # vitest run (83 tests)
362
362
 
363
363
  ## Changelog
364
364
 
365
+ ### v1.7.3
366
+ - **fix:** Claude cookie expiry now uses the longest-lived auth cookie (`sessionKey`, ~1 year) instead of the earliest-expiring one. `__cf_bm` (Cloudflare, ~30 min) was previously causing false "session expired" alerts every 30 minutes.
367
+
368
+ ### v1.7.2
369
+ - **fix:** Startup restore now uses cookie expiry file as primary check — if cookies are still valid (>1h left), the persistent context is launched immediately without a fragile browser selector check. This eliminates false "not logged in" errors for Grok/Claude/ChatGPT caused by slow page loads or DOM selector changes.
370
+ - **fix:** Grok cookie file path corrected to `grok-cookie-expiry.json` (was `grok-session.json`).
371
+
372
+ ### v1.7.1
373
+ - **feat:** `/status` HTML dashboard — browser-accessible health page at `http://127.0.0.1:31337/status`. Shows all 4 web providers with live status badge (Connected / Logged in / Expired / Never logged in), cookie expiry per provider, CLI and web model list. Auto-refreshes every 30s.
374
+
365
375
  ### v1.7.0
366
376
  - **fix:** Startup restore timeout 3s → 6s with one retry, eliminates false "not logged in" for slow-loading pages (Grok)
367
377
  - **feat:** Auto-relogin on startup — if cookies truly expired, attempt headless relogin before sending WhatsApp alert
package/SKILL.md CHANGED
@@ -56,6 +56,8 @@ Sessions survive gateway restarts. `/bridge-status` shows all 4 at a glance.
56
56
 
57
57
  On gateway restart, if any session has expired, a **WhatsApp alert** is sent automatically with the exact `/xxx-login` commands needed — no guessing required.
58
58
 
59
+ **Browser health dashboard:** `http://127.0.0.1:31337/status` — live overview of all 4 providers, cookie expiry, and model list. Auto-refreshes every 30s.
60
+
59
61
  ## Setup
60
62
 
61
63
  1. Enable plugin + restart gateway
@@ -64,4 +66,4 @@ On gateway restart, if any session has expired, a **WhatsApp alert** is sent aut
64
66
 
65
67
  See `README.md` for full configuration reference and architecture diagram.
66
68
 
67
- **Version:** 1.7.0
69
+ **Version:** 1.7.3
package/index.ts CHANGED
@@ -149,11 +149,13 @@ function loadClaudeExpiry(): ClaudeExpiryInfo | null {
149
149
  async function scanClaudeCookieExpiry(ctx: BrowserContext): Promise<ClaudeExpiryInfo | null> {
150
150
  try {
151
151
  const cookies = await ctx.cookies(["https://claude.ai"]);
152
- const auth = cookies.filter(c => ["sessionKey", "__cf_bm", "lastActiveOrg"].includes(c.name) && c.expires && c.expires > 0);
152
+ const auth = cookies.filter(c => ["sessionKey", "lastActiveOrg"].includes(c.name) && c.expires && c.expires > 0);
153
153
  if (!auth.length) return null;
154
- auth.sort((a, b) => (a.expires ?? 0) - (b.expires ?? 0));
155
- const earliest = auth[0];
156
- return { expiresAt: (earliest.expires ?? 0) * 1000, loginAt: Date.now(), cookieName: earliest.name };
154
+ // Use the LONGEST-lived auth cookie (sessionKey) for expiry tracking,
155
+ // not short-lived Cloudflare cookies like __cf_bm (~30 min).
156
+ auth.sort((a, b) => (b.expires ?? 0) - (a.expires ?? 0));
157
+ const longest = auth[0];
158
+ return { expiresAt: (longest.expires ?? 0) * 1000, loginAt: Date.now(), cookieName: longest.name };
157
159
  } catch { return null; }
158
160
  }
159
161
 
@@ -912,7 +914,7 @@ function proxyTestRequest(
912
914
  const plugin = {
913
915
  id: "openclaw-cli-bridge-elvatis",
914
916
  name: "OpenClaw CLI Bridge",
915
- version: "1.7.0",
917
+ version: "1.7.3",
916
918
  description:
917
919
  "Phase 1: openai-codex auth bridge. " +
918
920
  "Phase 2: HTTP proxy for gemini/claude CLIs. " +
@@ -959,7 +961,7 @@ const plugin = {
959
961
  {
960
962
  name: "grok",
961
963
  profileDir: GROK_PROFILE_DIR,
962
- cookieFile: join(homedir(), ".openclaw", "grok-session.json"),
964
+ cookieFile: GROK_EXPIRY_FILE,
963
965
  verifySelector: "textarea",
964
966
  homeUrl: "https://grok.com",
965
967
  loginCmd: "/grok-login",
@@ -1005,8 +1007,44 @@ const plugin = {
1005
1007
  }
1006
1008
  if (p.getCtx()) continue; // already connected
1007
1009
 
1010
+ // ── Cookie-first check ────────────────────────────────────────────
1011
+ // If a cookie expiry file exists and is still valid (>1h left),
1012
+ // launch the persistent context immediately without a browser-based
1013
+ // selector check. Selector checks are fragile (slow pages, DOM changes).
1014
+ // The keep-alive (20h) will verify the session properly later.
1015
+ let cookiesValid = false;
1016
+ if (existsSync(p.cookieFile)) {
1017
+ try {
1018
+ const expInfo = JSON.parse(readFileSync(p.cookieFile, "utf-8")) as { expiresAt: number };
1019
+ const msLeft = expInfo.expiresAt - Date.now();
1020
+ if (msLeft > 3_600_000) { // >1h remaining
1021
+ cookiesValid = true;
1022
+ api.logger.info(`[cli-bridge:${p.name}] cookie valid (${Math.floor(msLeft / 86_400_000)}d left) — restoring context without browser check`);
1023
+ }
1024
+ } catch { /* ignore parse errors */ }
1025
+ }
1026
+
1027
+ if (cookiesValid) {
1028
+ try {
1029
+ const ctx = await chromium.launchPersistentContext(p.profileDir, {
1030
+ headless: true,
1031
+ args: ["--no-sandbox", "--disable-setuid-sandbox"],
1032
+ });
1033
+ p.setCtx(ctx);
1034
+ ctx.on("close", () => { p.setCtx(null as unknown as BrowserContext); });
1035
+ api.logger.info(`[cli-bridge:${p.name}] session restored from profile ✅`);
1036
+ } catch (err) {
1037
+ api.logger.warn(`[cli-bridge:${p.name}] context launch failed: ${(err as Error).message}`);
1038
+ needsLogin.push(p.loginCmd);
1039
+ }
1040
+ // Sequential — never spawn all 4 Chromium instances at once
1041
+ await new Promise(r => setTimeout(r, 1000));
1042
+ continue;
1043
+ }
1044
+
1045
+ // ── Fallback: cookies expired or missing — try browser check ─────
1008
1046
  try {
1009
- api.logger.info(`[cli-bridge:${p.name}] restoring session from profile…`);
1047
+ api.logger.info(`[cli-bridge:${p.name}] cookies expired/missing verifying via browser…`);
1010
1048
  const ctx = await chromium.launchPersistentContext(p.profileDir, {
1011
1049
  headless: true,
1012
1050
  args: ["--no-sandbox", "--disable-setuid-sandbox"],
@@ -1014,46 +1052,16 @@ const plugin = {
1014
1052
  const page = await ctx.newPage();
1015
1053
  await page.goto(p.homeUrl, { waitUntil: "domcontentloaded", timeout: 20_000 });
1016
1054
  await new Promise(r => setTimeout(r, 6000));
1017
- let ok = await page.locator(p.verifySelector).isVisible().catch(() => false);
1018
- // Retry once — some pages (Grok) load slowly
1019
- if (!ok) {
1020
- api.logger.info(`[cli-bridge:${p.name}] verifySelector not visible after 6s, retrying (3s)…`);
1021
- await new Promise(r => setTimeout(r, 3000));
1022
- ok = await page.locator(p.verifySelector).isVisible().catch(() => false);
1023
- }
1055
+ const ok = await page.locator(p.verifySelector).isVisible().catch(() => false);
1024
1056
  await page.close().catch(() => {});
1025
1057
  if (ok) {
1026
1058
  p.setCtx(ctx);
1027
1059
  ctx.on("close", () => { p.setCtx(null as unknown as BrowserContext); });
1028
1060
  api.logger.info(`[cli-bridge:${p.name}] session restored from profile ✅`);
1029
1061
  } else {
1030
- // Session may be truly expired or just slow — attempt auto-relogin:
1031
- // re-navigate in the same context and check once more
1032
- api.logger.info(`[cli-bridge:${p.name}] not logged in after restore — attempting auto-relogin…`);
1033
- try {
1034
- const reloginPage = await ctx.newPage();
1035
- await reloginPage.goto(p.homeUrl, { waitUntil: "domcontentloaded", timeout: 20_000 });
1036
- await new Promise(r => setTimeout(r, 6000));
1037
- let reloginOk = await reloginPage.locator(p.verifySelector).isVisible().catch(() => false);
1038
- if (!reloginOk) {
1039
- await new Promise(r => setTimeout(r, 3000));
1040
- reloginOk = await reloginPage.locator(p.verifySelector).isVisible().catch(() => false);
1041
- }
1042
- await reloginPage.close().catch(() => {});
1043
- if (reloginOk) {
1044
- p.setCtx(ctx);
1045
- ctx.on("close", () => { p.setCtx(null as unknown as BrowserContext); });
1046
- api.logger.info(`[cli-bridge:${p.name}] session restored (slow load) ✅`);
1047
- } else {
1048
- await ctx.close().catch(() => {});
1049
- api.logger.info(`[cli-bridge:${p.name}] auto-relogin failed, needs manual ${p.loginCmd}`);
1050
- needsLogin.push(p.loginCmd);
1051
- }
1052
- } catch (reloginErr) {
1053
- await ctx.close().catch(() => {});
1054
- api.logger.warn(`[cli-bridge:${p.name}] auto-relogin error: ${(reloginErr as Error).message}`);
1055
- needsLogin.push(p.loginCmd);
1056
- }
1062
+ await ctx.close().catch(() => {});
1063
+ api.logger.info(`[cli-bridge:${p.name}] session expired needs ${p.loginCmd}`);
1064
+ needsLogin.push(p.loginCmd);
1057
1065
  }
1058
1066
  } catch (err) {
1059
1067
  api.logger.warn(`[cli-bridge:${p.name}] startup restore failed: ${(err as Error).message}`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-cli-bridge-elvatis",
3
3
  "name": "OpenClaw CLI Bridge",
4
- "version": "1.7.1",
4
+ "version": "1.7.3",
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.1",
3
+ "version": "1.7.3",
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": {