@elvatis_com/openclaw-cli-bridge-elvatis 2.1.3 → 2.2.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/.ai/handoff/DASHBOARD.md +12 -15
- package/.ai/handoff/LOG.md +61 -0
- package/.ai/handoff/MANIFEST.json +17 -9
- package/.ai/handoff/NEXT_ACTIONS.md +4 -2
- package/.ai/handoff/STATUS.md +6 -4
- package/README.md +16 -6
- package/index.ts +29 -10
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -4
- package/src/config-patcher.ts +4 -1
- package/src/proxy-server.ts +14 -2
- package/src/session-manager.ts +7 -0
package/.ai/handoff/DASHBOARD.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# DASHBOARD.md — openclaw-cli-bridge-elvatis
|
|
2
2
|
|
|
3
|
-
_Last updated: 2026-
|
|
3
|
+
_Last updated: 2026-04-10_
|
|
4
4
|
|
|
5
5
|
<!-- SECTION: plugin_status -->
|
|
6
6
|
## Plugin Status
|
|
7
7
|
|
|
8
8
|
| Component | Version | Build | Tests | Status |
|
|
9
9
|
|-----------|---------|-------|-------|--------|
|
|
10
|
-
| openclaw-cli-bridge-elvatis |
|
|
10
|
+
| openclaw-cli-bridge-elvatis | 2.2.1 | ✅ | ✅ | ✅ Stable |
|
|
11
11
|
<!-- /SECTION: plugin_status -->
|
|
12
12
|
|
|
13
13
|
<!-- SECTION: release_state -->
|
|
@@ -15,8 +15,9 @@ _Last updated: 2026-03-13_
|
|
|
15
15
|
|
|
16
16
|
| Platform | Published Version | Status |
|
|
17
17
|
|----------|------------------|--------|
|
|
18
|
-
| GitHub |
|
|
19
|
-
|
|
|
18
|
+
| GitHub | v2.2.1 | ✅ Pushed to main |
|
|
19
|
+
| npm | 2.1.3 | ⏳ Pending publish |
|
|
20
|
+
| ClawHub | 2.1.3 | ⏳ Pending publish |
|
|
20
21
|
<!-- /SECTION: release_state -->
|
|
21
22
|
|
|
22
23
|
<!-- SECTION: open_tasks -->
|
|
@@ -30,19 +31,15 @@ _No open tasks._
|
|
|
30
31
|
|
|
31
32
|
| Task | Title | Version |
|
|
32
33
|
|------|-------|---------|
|
|
33
|
-
| T-
|
|
34
|
-
| T-
|
|
34
|
+
| T-018 | Fix vllm apiKey corruption (401) + harden config-patcher | 2.2.1 |
|
|
35
|
+
| T-017 | Fix log spam, restart loops, CLI blocking | 2.2.0 |
|
|
36
|
+
| T-016 | Issue #2: Codex auth auto-import into agent auth store | 2.1.0 |
|
|
37
|
+
| T-015 | Issue #4: Background session mgmt with workdir isolation | 2.1.0 |
|
|
38
|
+
| T-014 | Issue #6: Workdir isolation (createIsolatedWorkdir, cleanup, sweep) | 2.1.0 |
|
|
39
|
+
| T-013 | Fix cookie expiry tracking — longest-lived auth cookie (all 4) | 1.7.3 |
|
|
40
|
+
| T-012 | Persistent browser fallback for Claude/Gemini/ChatGPT (no CDP) | 1.4.0 |
|
|
35
41
|
| T-011 | Session-safe staged model switching (/cli-apply, /cli-pending, --now) | 0.2.25 |
|
|
36
42
|
| T-009 | Stability: sleep-resilient token refresh + stopTokenRefresh cleanup | 0.2.25 |
|
|
37
|
-
| T-103 | Explicit model allowlist for CLI execution | 0.2.23 |
|
|
38
|
-
| T-102 | Proxy auth key rotation via config | 0.2.23 |
|
|
39
|
-
| T-101 | Unit tests for prompt formatter + model router | 0.2.23 |
|
|
40
43
|
| T-008 | Validate proxy endpoints + vllm model calls end-to-end | 0.2.21 |
|
|
41
44
|
| T-007 | Create GitHub repo and push initial code | 0.2.5 |
|
|
42
|
-
| T-006 | Implement Claude Code CLI request bridge | 0.2.5 |
|
|
43
|
-
| T-005 | Implement Gemini CLI request bridge | 0.2.5 |
|
|
44
|
-
| T-004 | Verify model call: gpt-5.2 / gpt-5.3-codex responds | 0.2.5 |
|
|
45
|
-
| T-003 | Test auth flow: openclaw models auth login --provider openai-codex | 0.2.5 |
|
|
46
|
-
| T-002 | Implement openai-codex provider (Codex CLI auth bridge) | 0.2.5 |
|
|
47
|
-
| T-001 | Scaffold plugin structure + AAHP handoff | 0.2.5 |
|
|
48
45
|
<!-- /SECTION: completed_tasks -->
|
package/.ai/handoff/LOG.md
CHANGED
|
@@ -4,6 +4,67 @@ _Last 10 sessions. Older entries in LOG-ARCHIVE.md._
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## 2026-04-10 — Session 10 (Claude Opus 4.6)
|
|
8
|
+
|
|
9
|
+
> **Agent:** claude-opus-4-6
|
|
10
|
+
> **Phase:** fix
|
|
11
|
+
> **Commit before:** v2.2.0
|
|
12
|
+
> **Commit after:** v2.2.1
|
|
13
|
+
|
|
14
|
+
**T-018: Fix vllm apiKey corruption causing 401 + harden config-patcher**
|
|
15
|
+
|
|
16
|
+
### Problem
|
|
17
|
+
The vllm provider in `~/.openclaw/openclaw.json` had `"apiKey": "__OPENCLAW_KEEP__"` instead of `"cli-bridge"`. The CLI bridge proxy on port 31337 expects `Authorization: Bearer cli-bridge` and rejects any other value with HTTP 401.
|
|
18
|
+
|
|
19
|
+
This caused all `vllm/cli-claude/*` requests to fail silently, triggering the self-healing plugin's model fallback to `openai-codex/gpt-5.1`.
|
|
20
|
+
|
|
21
|
+
**Root cause:** An OpenClaw config migration overwrote the apiKey with the marker value `__OPENCLAW_KEEP__`. The config-patcher only checked whether cli-bridge models existed (line 46–53) — if they did, it skipped re-patching even though the apiKey was wrong.
|
|
22
|
+
|
|
23
|
+
### Fix (src/config-patcher.ts)
|
|
24
|
+
- Added `existingApiKey` / `hasCorrectApiKey` check to the skip-guard (line 55)
|
|
25
|
+
- Patcher now re-patches if `apiKey !== "cli-bridge"`, preventing recurrence after future config migrations
|
|
26
|
+
|
|
27
|
+
### Also in this session
|
|
28
|
+
- Fixed `~/.openclaw/openclaw.json` directly: `"apiKey": "__OPENCLAW_KEEP__"` → `"cli-bridge"`
|
|
29
|
+
- Gateway restarted — no 401 errors, self-heal model order confirmed working
|
|
30
|
+
|
|
31
|
+
### Build
|
|
32
|
+
- `npm run build` — ✅ (pre-existing TS errors from missing openclaw/plugin-sdk, JS emitted via `--noEmitOnError false`)
|
|
33
|
+
|
|
34
|
+
### Version
|
|
35
|
+
2.2.0 → 2.2.1
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2026-04-09 — Session 9 (Claude Opus 4.6)
|
|
40
|
+
|
|
41
|
+
> **Agent:** claude-opus-4-6
|
|
42
|
+
> **Phase:** fix
|
|
43
|
+
> **Commit before:** v2.1.3
|
|
44
|
+
> **Commit after:** v2.2.0
|
|
45
|
+
|
|
46
|
+
**T-017: Fix log spam, restart loops, CLI blocking**
|
|
47
|
+
|
|
48
|
+
### Problems
|
|
49
|
+
1. `register()` called per-agent (~11×) — every call logged Chrome check, provider registration, and all 32 commands
|
|
50
|
+
2. `fuser -k` port cleanup killed the gateway process itself (proxy runs in-process), causing systemd restart loops
|
|
51
|
+
3. `openclaw models status` (and other CLI commands) hung indefinitely because session restore launched 4 Chromium instances
|
|
52
|
+
|
|
53
|
+
### Fixes (index.ts, src/proxy-server.ts)
|
|
54
|
+
- Module-level `_registerLoggedOnce` guard — Chrome/provider/commands logged once per process start
|
|
55
|
+
- Module-level `_proxyStarted` guard — proxy start runs once, not per-agent
|
|
56
|
+
- Shortened command log: count + `/cli-list` reference instead of listing all 32 names
|
|
57
|
+
- Removed `fuser -k` — EADDRINUSE handled gracefully (skip + log)
|
|
58
|
+
- `_proxyOwnedByThisProcess` flag — session restore only runs in gateway mode, skipped for CLI commands
|
|
59
|
+
- Codex auth import runs once per startup, not per-agent
|
|
60
|
+
|
|
61
|
+
### Also in this session
|
|
62
|
+
- Installed cli-bridge plugin on production server (chef-linux@192.168.177.6)
|
|
63
|
+
- Set default model to `vllm/cli-claude/claude-sonnet-4-6` (Claude CLI, no API costs)
|
|
64
|
+
- Merged 3 Dependabot PRs (#13, #17, #19)
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
7
68
|
## 2026-03-13 — Session 8 (Claude Opus 4.6)
|
|
8
69
|
|
|
9
70
|
> **Agent:** claude-opus-4-6
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"aahp_version": "3.0",
|
|
3
3
|
"project": "openclaw-cli-bridge-elvatis",
|
|
4
4
|
"last_session": {
|
|
5
|
-
"agent": "claude-
|
|
6
|
-
"session_id": "
|
|
7
|
-
"timestamp": "2026-
|
|
8
|
-
"commit": "
|
|
9
|
-
"phase": "
|
|
10
|
-
"duration_minutes":
|
|
5
|
+
"agent": "claude-opus-4-6",
|
|
6
|
+
"session_id": "fix-log-spam-2026-04-09",
|
|
7
|
+
"timestamp": "2026-04-09T14:30:00Z",
|
|
8
|
+
"commit": "2704a46",
|
|
9
|
+
"phase": "fix",
|
|
10
|
+
"duration_minutes": 120
|
|
11
11
|
},
|
|
12
12
|
"files": {
|
|
13
13
|
"STATUS.md": {
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"summary": "4-phase AAHP pipeline: Research \u2192 Architecture \u2192 Implementation \u2192 Review \u2192 Handoff."
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
|
-
"quick_context": "
|
|
56
|
+
"quick_context": "v2.2.0 pushed to main. Key changes: module-level guards prevent log spam from per-agent register() calls, removed fuser -k (caused restart loops), session restore only in gateway mode (CLI commands no longer hang), EADDRINUSE graceful handling. Plugin deployed on chef-linux production server.",
|
|
57
57
|
"token_budget": {
|
|
58
58
|
"manifest_only": 90,
|
|
59
59
|
"manifest_plus_core": 380,
|
|
60
60
|
"full_read": 850
|
|
61
61
|
},
|
|
62
|
-
"next_task_id":
|
|
62
|
+
"next_task_id": 18,
|
|
63
63
|
"tasks": {
|
|
64
64
|
"T-001": {
|
|
65
65
|
"title": "Scaffold plugin structure + AAHP handoff",
|
|
@@ -168,7 +168,15 @@
|
|
|
168
168
|
],
|
|
169
169
|
"created": "2026-03-11T17:00:00Z",
|
|
170
170
|
"completed": "2026-03-11T17:39:00Z"
|
|
171
|
+
},
|
|
172
|
+
"T-017": {
|
|
173
|
+
"title": "Fix log spam, restart loops, CLI blocking",
|
|
174
|
+
"status": "done",
|
|
175
|
+
"priority": "high",
|
|
176
|
+
"depends_on": [],
|
|
177
|
+
"created": "2026-04-09T14:00:00Z",
|
|
178
|
+
"completed": "2026-04-09T16:30:00Z"
|
|
171
179
|
}
|
|
172
180
|
},
|
|
173
|
-
"version": "2.
|
|
181
|
+
"version": "2.2.0"
|
|
174
182
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# NEXT_ACTIONS.md — openclaw-cli-bridge-elvatis
|
|
2
2
|
|
|
3
|
-
_Last updated: 2026-
|
|
3
|
+
_Last updated: 2026-04-10_
|
|
4
4
|
|
|
5
5
|
<!-- SECTION: summary -->
|
|
6
6
|
## Status Summary
|
|
7
7
|
|
|
8
8
|
| Status | Count |
|
|
9
9
|
|---------|-------|
|
|
10
|
-
| Done |
|
|
10
|
+
| Done | 18 |
|
|
11
11
|
| Ready | 0 |
|
|
12
12
|
| Blocked | 0 |
|
|
13
13
|
<!-- /SECTION: summary -->
|
|
@@ -30,6 +30,8 @@ _No blocked tasks._
|
|
|
30
30
|
|
|
31
31
|
| Task | Title | Date |
|
|
32
32
|
|-------|--------------------------------------------------------------------|------------|
|
|
33
|
+
| T-018 | Fix vllm apiKey corruption (401) + harden config-patcher (v2.2.1)| 2026-04-10 |
|
|
34
|
+
| T-017 | Fix log spam, restart loops, CLI blocking (v2.2.0) | 2026-04-09 |
|
|
33
35
|
| T-016 | Issue #2: Codex auth auto-import into agent auth store | 2026-03-19 |
|
|
34
36
|
| T-015 | Issue #4: Background session mgmt with workdir isolation | 2026-03-19 |
|
|
35
37
|
| T-014 | Issue #6: Workdir isolation (createIsolatedWorkdir, cleanup, sweep) | 2026-03-19 |
|
package/.ai/handoff/STATUS.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# STATUS — openclaw-cli-bridge-elvatis
|
|
2
2
|
|
|
3
|
-
## Current Version: 2.1
|
|
3
|
+
## Current Version: 2.2.1
|
|
4
4
|
|
|
5
|
-
- **npm:** @elvatis_com/openclaw-cli-bridge-elvatis@2.1
|
|
6
|
-
- **ClawHub:** openclaw-cli-bridge-elvatis@2.1
|
|
7
|
-
- **GitHub:** https://github.com/elvatis/openclaw-cli-bridge-elvatis
|
|
5
|
+
- **npm:** @elvatis_com/openclaw-cli-bridge-elvatis@2.2.1 (pending publish)
|
|
6
|
+
- **ClawHub:** openclaw-cli-bridge-elvatis@2.2.1 (pending publish)
|
|
7
|
+
- **GitHub:** https://github.com/elvatis/openclaw-cli-bridge-elvatis (pushed to main)
|
|
8
8
|
|
|
9
9
|
## CLI Model Token Limits (corrected in v1.9.2)
|
|
10
10
|
| Model | Context Window | Max Output |
|
|
@@ -47,6 +47,8 @@ This is by design — CLI tools output plain text only.
|
|
|
47
47
|
- /bridge-status shows cookie-based status
|
|
48
48
|
|
|
49
49
|
## Release History (recent)
|
|
50
|
+
- v2.2.1 (2026-04-10): Fix vllm apiKey corruption (401 Unauthorized) + harden config-patcher to re-patch on wrong apiKey
|
|
51
|
+
- v2.2.0 (2026-04-09): Fix log spam (module-level guards), remove fuser -k restart loops, session restore gateway-only, EADDRINUSE graceful handling
|
|
50
52
|
- v2.1.0 (2026-03-19): Issue #6 workdir isolation, Issue #4 session mgmt enhancements, Issue #2 codex auth auto-import
|
|
51
53
|
- v2.0.0: Major version bump
|
|
52
54
|
- v1.9.2 (2026-03-15): Fix maxTokens/contextWindow for all CLI_MODELS (were 8192, now correct per vendor specs)
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> OpenClaw plugin that bridges locally installed AI CLIs (Codex, Gemini, Claude Code, OpenCode, Pi) as model providers — with slash commands for instant model switching, restore, health testing, and model listing.
|
|
4
4
|
|
|
5
|
-
**Current version:** `2.1
|
|
5
|
+
**Current version:** `2.2.1`
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -191,11 +191,10 @@ openclaw gateway restart
|
|
|
191
191
|
### 2. Verify (check gateway logs)
|
|
192
192
|
|
|
193
193
|
```
|
|
194
|
-
[cli-bridge]
|
|
195
|
-
[cli-bridge]
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/cli-back, /cli-test, /cli-list
|
|
194
|
+
[cli-bridge] system Chrome found: Google Chrome 146.x
|
|
195
|
+
[cli-bridge] openai-codex provider registered
|
|
196
|
+
[cli-bridge] registered 32 commands (use /cli-list to see all)
|
|
197
|
+
[cli-bridge] proxy ready on :31337 — vllm/cli-gemini/* and vllm/cli-claude/* available
|
|
199
198
|
```
|
|
200
199
|
|
|
201
200
|
### 3. Register Codex auth (optional — Phase 1 only)
|
|
@@ -377,6 +376,17 @@ npm run ci # lint + typecheck + test
|
|
|
377
376
|
|
|
378
377
|
## Changelog
|
|
379
378
|
|
|
379
|
+
### v2.2.1
|
|
380
|
+
- **fix:** Config-patcher now validates `apiKey` value — re-patches if `__OPENCLAW_KEEP__` or any wrong value is present (prevents vllm 401 Unauthorized after config migrations)
|
|
381
|
+
|
|
382
|
+
### v2.2.0
|
|
383
|
+
- **fix:** Module-level guards prevent duplicate log spam — `register()` is called per-agent (~11×), now logs Chrome/provider/commands only once
|
|
384
|
+
- **fix:** Shortened command registration log: count + `/cli-list` reference instead of listing all 32 commands
|
|
385
|
+
- **fix:** Removed `fuser -k` port cleanup — was killing the gateway process itself, causing systemd restart loops
|
|
386
|
+
- **fix:** EADDRINUSE now handled gracefully (skip + log) instead of process killing
|
|
387
|
+
- **fix:** Session restore (4× Chromium) only runs in gateway mode — CLI commands like `openclaw models status` no longer hang
|
|
388
|
+
- **fix:** Codex auth import runs once per startup, not per-agent
|
|
389
|
+
|
|
380
390
|
### v2.1.3
|
|
381
391
|
- **docs:** All documentation updated to reflect current version (README, SKILL.md, STATUS.md, MANIFEST.json)
|
|
382
392
|
|
package/index.ts
CHANGED
|
@@ -240,6 +240,14 @@ let _cdpBrowserLaunchPromise: Promise<import("playwright").BrowserContext | null
|
|
|
240
240
|
// Set to true after first run; hot-reloads see true and skip the restore loop.
|
|
241
241
|
let _startupRestoreDone = false;
|
|
242
242
|
|
|
243
|
+
// ── Proxy guards ─────────────────────────────────────────────────────────────
|
|
244
|
+
// register() is called per-agent (~11×) AND on every hot-reload (~60s).
|
|
245
|
+
// All noisy info logs removed. Only warnings/errors remain. These simple
|
|
246
|
+
// module-level booleans are sufficient — probeExisting() handles the real
|
|
247
|
+
// deduplication for the proxy.
|
|
248
|
+
let _proxyStarted = false;
|
|
249
|
+
let _proxyOwnedByThisProcess = false;
|
|
250
|
+
|
|
243
251
|
// Session keep-alive interval — refreshes browser cookies every 20h
|
|
244
252
|
let _keepAliveInterval: ReturnType<typeof setInterval> | null = null;
|
|
245
253
|
|
|
@@ -1010,9 +1018,8 @@ const plugin = {
|
|
|
1010
1018
|
// Stealth mode uses channel: "chrome" (real system Chrome). If it's missing,
|
|
1011
1019
|
// browser launches will fail or Cloudflare will block the bundled Chromium.
|
|
1012
1020
|
const chromeCheck = checkSystemChrome();
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
} else {
|
|
1021
|
+
// Chrome warning only — success is silent (logged hundreds of times per minute otherwise)
|
|
1022
|
+
if (!chromeCheck.available) {
|
|
1016
1023
|
api.logger.warn(
|
|
1017
1024
|
`[cli-bridge] ⚠ system Chrome not found! Web browser providers (/grok-login, /gemini-login, etc.) ` +
|
|
1018
1025
|
`require Google Chrome or Chromium installed system-wide. ` +
|
|
@@ -1027,7 +1034,13 @@ const plugin = {
|
|
|
1027
1034
|
// restore once, on the very first load (when all contexts are null).
|
|
1028
1035
|
//
|
|
1029
1036
|
// Guard: _startupRestoreDone is module-level and persists across hot-reloads.
|
|
1030
|
-
|
|
1037
|
+
// IMPORTANT: Only restore sessions when the proxy is enabled (gateway mode).
|
|
1038
|
+
// Short-lived CLI commands (openclaw models status, openclaw doctor, etc.)
|
|
1039
|
+
// must NOT launch 4 Chromium instances — they block the process from exiting.
|
|
1040
|
+
// Only restore browser sessions when this process OWNS the proxy (gateway mode).
|
|
1041
|
+
// Short-lived CLI commands (openclaw models status) reuse an external proxy and
|
|
1042
|
+
// must not launch 4 Chromium instances that block the process from exiting.
|
|
1043
|
+
if (!_startupRestoreDone && _proxyOwnedByThisProcess) {
|
|
1031
1044
|
_startupRestoreDone = true;
|
|
1032
1045
|
void (async () => {
|
|
1033
1046
|
await new Promise(r => setTimeout(r, 5000)); // wait for proxy + gateway to settle
|
|
@@ -1260,19 +1273,19 @@ const plugin = {
|
|
|
1260
1273
|
},
|
|
1261
1274
|
});
|
|
1262
1275
|
|
|
1263
|
-
|
|
1276
|
+
// Provider registration is silent — logged hundreds of times otherwise
|
|
1264
1277
|
|
|
1265
1278
|
// Auto-import Codex CLI credentials into the agent auth store (Issue #2).
|
|
1266
1279
|
// This ensures `openai-codex/*` models work immediately without manual
|
|
1267
1280
|
// `openclaw models auth login`. Runs async, non-blocking.
|
|
1281
|
+
// Codex auth import: silent log callback (suppress "already up-to-date" spam)
|
|
1282
|
+
// Only log on actual import or error — not on skip/already-current.
|
|
1268
1283
|
void importCodexAuth({
|
|
1269
1284
|
codexAuthPath,
|
|
1270
|
-
log: (
|
|
1285
|
+
log: () => {}, // silent — internal "already up-to-date" spam suppressed
|
|
1271
1286
|
}).then((result) => {
|
|
1272
1287
|
if (result.imported) {
|
|
1273
1288
|
api.logger.info("[cli-bridge] Codex auth auto-imported into agent auth store ✅");
|
|
1274
|
-
} else if (result.skipped) {
|
|
1275
|
-
api.logger.info("[cli-bridge] Codex auth already current in agent auth store");
|
|
1276
1289
|
} else if (result.error) {
|
|
1277
1290
|
api.logger.warn(`[cli-bridge] Codex auth import failed: ${result.error}`);
|
|
1278
1291
|
}
|
|
@@ -1302,10 +1315,15 @@ const plugin = {
|
|
|
1302
1315
|
};
|
|
1303
1316
|
|
|
1304
1317
|
const startProxy = async (): Promise<void> => {
|
|
1318
|
+
// Guard: only the first register() call starts the proxy.
|
|
1319
|
+
// Subsequent per-agent calls AND hot-reloads skip entirely.
|
|
1320
|
+
if (_proxyStarted) return;
|
|
1321
|
+
_proxyStarted = true;
|
|
1322
|
+
|
|
1305
1323
|
// If a healthy proxy is already up, reuse it — no need to rebind.
|
|
1306
1324
|
const alive = await probeExisting();
|
|
1307
1325
|
if (alive) {
|
|
1308
|
-
|
|
1326
|
+
// Silent — this fires on every hot-reload (~60s) and every register() call
|
|
1309
1327
|
return;
|
|
1310
1328
|
}
|
|
1311
1329
|
|
|
@@ -1369,6 +1387,7 @@ const plugin = {
|
|
|
1369
1387
|
}),
|
|
1370
1388
|
});
|
|
1371
1389
|
proxyServer = server;
|
|
1390
|
+
_proxyOwnedByThisProcess = true;
|
|
1372
1391
|
api.logger.info(
|
|
1373
1392
|
`[cli-bridge] proxy ready on :${port} — vllm/cli-gemini/* and vllm/cli-claude/* available`
|
|
1374
1393
|
);
|
|
@@ -2442,7 +2461,7 @@ const plugin = {
|
|
|
2442
2461
|
"/bridge-status",
|
|
2443
2462
|
"/cli-help",
|
|
2444
2463
|
];
|
|
2445
|
-
|
|
2464
|
+
// Command registration is silent — fires on every register() call
|
|
2446
2465
|
},
|
|
2447
2466
|
};
|
|
2448
2467
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-cli-bridge-elvatis",
|
|
3
3
|
"slug": "openclaw-cli-bridge-elvatis",
|
|
4
4
|
"name": "OpenClaw CLI Bridge",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.2.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"description": "Phase 1: openai-codex auth bridge. Phase 2: local HTTP proxy routing model calls through gemini/claude CLIs (vllm provider).",
|
|
8
8
|
"providers": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elvatis_com/openclaw-cli-bridge-elvatis",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.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": {
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"@eslint/js": "^10.0.1",
|
|
20
20
|
"@types/node": "^25.3.2",
|
|
21
21
|
"eslint": "^10.0.3",
|
|
22
|
-
"typescript": "^
|
|
23
|
-
"typescript-eslint": "^8.
|
|
22
|
+
"typescript": "^6.0.2",
|
|
23
|
+
"typescript-eslint": "^8.58.1",
|
|
24
24
|
"vitest": "^4.0.18"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"playwright": "^1.58.2"
|
|
28
28
|
},
|
|
29
29
|
"license": "Apache-2.0"
|
|
30
|
-
}
|
|
30
|
+
}
|
package/src/config-patcher.ts
CHANGED
|
@@ -52,7 +52,10 @@ export function patchOpencllawConfig(port: number): PatchResult {
|
|
|
52
52
|
!!allowedModels["vllm/cli-gemini/gemini-2.5-pro"] ||
|
|
53
53
|
!!allowedModels["vllm/cli-claude/claude-sonnet-4-6"];
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const existingApiKey = (cfg as any)?.models?.providers?.vllm?.apiKey;
|
|
56
|
+
const hasCorrectApiKey = existingApiKey === CLI_BRIDGE_API_KEY;
|
|
57
|
+
|
|
58
|
+
if (hasBridgeProviderModels && hasBridgeAllowlist && hasCorrectApiKey) {
|
|
56
59
|
return { patched: false, reason: "vllm provider + agent allowlist already include cli-bridge models." };
|
|
57
60
|
}
|
|
58
61
|
|
package/src/proxy-server.ts
CHANGED
|
@@ -144,10 +144,22 @@ export function startProxyServer(opts: ProxyServerOptions): Promise<http.Server>
|
|
|
144
144
|
sessionManager.stop();
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
-
server.on("error", (err) =>
|
|
147
|
+
server.on("error", (err: NodeJS.ErrnoException) => {
|
|
148
|
+
if (err.code === "EADDRINUSE") {
|
|
149
|
+
// Port is held by a previous gateway process. probeExisting() should have
|
|
150
|
+
// caught a healthy proxy and reused it. If we get here, the old proxy is
|
|
151
|
+
// unhealthy but the OS hasn't released the socket yet. Just log and skip —
|
|
152
|
+
// do NOT fuser -k: the proxy runs in-process and killing the port holder
|
|
153
|
+
// would kill the gateway itself, causing a systemd restart loop.
|
|
154
|
+
opts.log(`[cli-bridge] port ${opts.port} in use by another process — proxy skipped (will retry on next gateway restart)`);
|
|
155
|
+
resolve(server); // resolve without a listening server — probeExisting handles reuse
|
|
156
|
+
} else {
|
|
157
|
+
reject(err);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
148
160
|
server.listen(opts.port, "127.0.0.1", () => {
|
|
149
161
|
opts.log(
|
|
150
|
-
`[cli-bridge] proxy
|
|
162
|
+
`[cli-bridge] proxy listening on :${opts.port}`
|
|
151
163
|
);
|
|
152
164
|
// unref() so the proxy server does not keep the Node.js event loop alive
|
|
153
165
|
// when openclaw doctor or other short-lived CLI commands load plugins.
|
package/src/session-manager.ts
CHANGED
|
@@ -111,6 +111,13 @@ export class SessionManager {
|
|
|
111
111
|
* Returns a unique sessionId (random hex).
|
|
112
112
|
*/
|
|
113
113
|
spawn(model: string, messages: ChatMessage[], opts: SpawnOptions = {}): string {
|
|
114
|
+
// Validate model ID before it reaches spawn() args to prevent command injection
|
|
115
|
+
// (CodeQL js/command-line-injection). Allow only safe chars: letters, digits,
|
|
116
|
+
// dots, hyphens, underscores, and forward slashes (for provider prefixes).
|
|
117
|
+
if (!/^[a-zA-Z0-9._\-\/]+$/.test(model)) {
|
|
118
|
+
throw new Error(`Invalid model ID: "${model}". Only alphanumeric characters, dots, hyphens, underscores, and slashes are allowed.`);
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
const sessionId = randomBytes(8).toString("hex");
|
|
115
122
|
const prompt = formatPrompt(messages);
|
|
116
123
|
|