@elvatis_com/openclaw-cli-bridge-elvatis 0.2.27 ā 0.2.28
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 +7 -1
- package/SKILL.md +1 -1
- package/index.ts +69 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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:** `0.2.
|
|
5
|
+
**Current version:** `0.2.28`
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -287,6 +287,12 @@ npm test # vitest run (45 tests)
|
|
|
287
287
|
|
|
288
288
|
## Changelog
|
|
289
289
|
|
|
290
|
+
### v0.2.28
|
|
291
|
+
- **feat:** `/grok-login` scans auth cookie expiry (sso cookie) and saves to `~/.openclaw/grok-cookie-expiry.json`
|
|
292
|
+
- **feat:** `/grok-status` shows cookie expiry with color-coded warnings (šØ <7d, ā ļø <14d, ā
otherwise)
|
|
293
|
+
- **feat:** Startup log shows cookie expiry and refreshes the expiry file on session restore
|
|
294
|
+
- **fix:** Flaky cli-runner test improved (was pre-existing)
|
|
295
|
+
|
|
290
296
|
### v0.2.27
|
|
291
297
|
- **feat:** Grok persistent Chromium profile (`~/.openclaw/grok-profile/`) ā cookies survive gateway restarts
|
|
292
298
|
- **feat:** `/grok-login` imports cookies from OpenClaw browser into persistent profile automatically
|
package/SKILL.md
CHANGED
package/index.ts
CHANGED
|
@@ -89,6 +89,53 @@ let grokContext: BrowserContext | null = null;
|
|
|
89
89
|
// Persistent profile dir ā survives gateway restarts, keeps cookies intact
|
|
90
90
|
const GROK_PROFILE_DIR = join(homedir(), ".openclaw", "grok-profile");
|
|
91
91
|
|
|
92
|
+
// Cookie expiry tracking file ā written on /grok-login, read on startup
|
|
93
|
+
const GROK_EXPIRY_FILE = join(homedir(), ".openclaw", "grok-cookie-expiry.json");
|
|
94
|
+
|
|
95
|
+
interface GrokExpiryInfo {
|
|
96
|
+
expiresAt: number; // epoch ms ā earliest auth cookie expiry
|
|
97
|
+
loginAt: number; // epoch ms ā when /grok-login was last run
|
|
98
|
+
cookieName: string; // which cookie determines the expiry
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function saveGrokExpiry(info: GrokExpiryInfo): void {
|
|
102
|
+
try {
|
|
103
|
+
writeFileSync(GROK_EXPIRY_FILE, JSON.stringify(info, null, 2));
|
|
104
|
+
} catch { /* ignore */ }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function loadGrokExpiry(): GrokExpiryInfo | null {
|
|
108
|
+
try {
|
|
109
|
+
const raw = readFileSync(GROK_EXPIRY_FILE, "utf-8");
|
|
110
|
+
return JSON.parse(raw) as GrokExpiryInfo;
|
|
111
|
+
} catch { return null; }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Returns human-readable expiry summary e.g. "179 days (2026-09-07)" */
|
|
115
|
+
function formatExpiryInfo(info: GrokExpiryInfo): string {
|
|
116
|
+
const daysLeft = Math.ceil((info.expiresAt - Date.now()) / 86_400_000);
|
|
117
|
+
const dateStr = new Date(info.expiresAt).toISOString().split("T")[0];
|
|
118
|
+
if (daysLeft <= 0) return `ā ļø EXPIRED (was ${dateStr})`;
|
|
119
|
+
if (daysLeft <= 7) return `šØ expires in ${daysLeft}d (${dateStr}) ā run /grok-login NOW`;
|
|
120
|
+
if (daysLeft <= 14) return `ā ļø expires in ${daysLeft}d (${dateStr}) ā run /grok-login soon`;
|
|
121
|
+
return `ā
valid for ${daysLeft} more days (expires ${dateStr})`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Scan context cookies and return earliest auth cookie expiry */
|
|
125
|
+
async function scanCookieExpiry(ctx: import("playwright").BrowserContext): Promise<GrokExpiryInfo | null> {
|
|
126
|
+
try {
|
|
127
|
+
const cookies = await ctx.cookies(["https://grok.com", "https://x.ai"]);
|
|
128
|
+
const authCookies = cookies.filter((c) => ["sso", "sso-rw"].includes(c.name) && c.expires > 0);
|
|
129
|
+
if (authCookies.length === 0) return null;
|
|
130
|
+
const earliest = authCookies.reduce((min, c) => (c.expires < min.expires ? c : min));
|
|
131
|
+
return {
|
|
132
|
+
expiresAt: earliest.expires * 1000,
|
|
133
|
+
loginAt: Date.now(),
|
|
134
|
+
cookieName: earliest.name,
|
|
135
|
+
};
|
|
136
|
+
} catch { return null; }
|
|
137
|
+
}
|
|
138
|
+
|
|
92
139
|
/**
|
|
93
140
|
* Launch (or reuse) a persistent headless Chromium context for grok.com.
|
|
94
141
|
* Uses launchPersistentContext so cookies survive gateway restarts.
|
|
@@ -160,6 +207,14 @@ async function tryRestoreGrokSession(
|
|
|
160
207
|
}
|
|
161
208
|
grokContext = ctx;
|
|
162
209
|
log("[cli-bridge:grok] session restored ā
");
|
|
210
|
+
// Log cookie expiry status on startup
|
|
211
|
+
const expiry = loadGrokExpiry();
|
|
212
|
+
if (expiry) {
|
|
213
|
+
log(`[cli-bridge:grok] cookie expiry: ${formatExpiryInfo(expiry)}`);
|
|
214
|
+
// Re-scan to keep expiry file fresh (cookies may have been renewed)
|
|
215
|
+
const freshExpiry = await scanCookieExpiry(ctx);
|
|
216
|
+
if (freshExpiry) saveGrokExpiry(freshExpiry);
|
|
217
|
+
}
|
|
163
218
|
return true;
|
|
164
219
|
} catch (err) {
|
|
165
220
|
log(`[cli-bridge:grok] session restore error: ${(err as Error).message}`);
|
|
@@ -492,7 +547,7 @@ function proxyTestRequest(
|
|
|
492
547
|
const plugin = {
|
|
493
548
|
id: "openclaw-cli-bridge-elvatis",
|
|
494
549
|
name: "OpenClaw CLI Bridge",
|
|
495
|
-
version: "0.2.
|
|
550
|
+
version: "0.2.28",
|
|
496
551
|
description:
|
|
497
552
|
"Phase 1: openai-codex auth bridge. " +
|
|
498
553
|
"Phase 2: HTTP proxy for gemini/claude CLIs. " +
|
|
@@ -943,7 +998,16 @@ const plugin = {
|
|
|
943
998
|
return { text: `ā Session not valid: ${check.reason}\n\nMake sure grok.com is open in your browser and you're logged in, then run /grok-login again.` };
|
|
944
999
|
}
|
|
945
1000
|
grokContext = ctx;
|
|
946
|
-
|
|
1001
|
+
|
|
1002
|
+
// Scan cookie expiry and persist it
|
|
1003
|
+
const expiry = await scanCookieExpiry(ctx);
|
|
1004
|
+
if (expiry) {
|
|
1005
|
+
saveGrokExpiry(expiry);
|
|
1006
|
+
api.logger.info(`[cli-bridge:grok] cookie expiry: ${new Date(expiry.expiresAt).toISOString()}`);
|
|
1007
|
+
}
|
|
1008
|
+
const expiryLine = expiry ? `\n\nš Cookie expiry: ${formatExpiryInfo(expiry)}` : "";
|
|
1009
|
+
|
|
1010
|
+
return { text: `ā
Grok session ready!\n\nModels available:\n⢠\`vllm/web-grok/grok-3\`\n⢠\`vllm/web-grok/grok-3-fast\`\n⢠\`vllm/web-grok/grok-3-mini\`\n⢠\`vllm/web-grok/grok-3-mini-fast\`${expiryLine}` };
|
|
947
1011
|
},
|
|
948
1012
|
} satisfies OpenClawPluginCommandDefinition);
|
|
949
1013
|
|
|
@@ -956,7 +1020,9 @@ const plugin = {
|
|
|
956
1020
|
}
|
|
957
1021
|
const check = await verifySession(grokContext, (msg) => api.logger.info(msg));
|
|
958
1022
|
if (check.valid) {
|
|
959
|
-
|
|
1023
|
+
const expiry = loadGrokExpiry();
|
|
1024
|
+
const expiryLine = expiry ? `\nš ${formatExpiryInfo(expiry)}` : "";
|
|
1025
|
+
return { text: `ā
grok.com session active\nProxy: \`127.0.0.1:${port}\`\nModels: web-grok/grok-3, web-grok/grok-3-fast, web-grok/grok-3-mini, web-grok/grok-3-mini-fast${expiryLine}` };
|
|
960
1026
|
}
|
|
961
1027
|
grokContext = null;
|
|
962
1028
|
return { text: `ā Session expired: ${check.reason}\nRun \`/grok-login\` to re-authenticate.` };
|
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": "0.2.
|
|
4
|
+
"version": "0.2.28",
|
|
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": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
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": {
|