@elvatis_com/openclaw-cli-bridge-elvatis 2.1.2 → 2.2.0

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.
@@ -1,13 +1,13 @@
1
1
  # DASHBOARD.md — openclaw-cli-bridge-elvatis
2
2
 
3
- _Last updated: 2026-03-13_
3
+ _Last updated: 2026-04-09_
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 | 1.7.3 | ✅ | ✅ 96/96 | ✅ Stable |
10
+ | openclaw-cli-bridge-elvatis | 2.2.0 | ✅ | ✅ | ✅ 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 | v1.3.5 | ✅ Tagged + Release |
19
- | Local | 1.7.3 | ⏳ Built, pending push |
18
+ | GitHub | v2.2.0 | ✅ 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,14 @@ _No open tasks._
30
31
 
31
32
  | Task | Title | Version |
32
33
  |------|-------|---------|
33
- | T-013 | Fix cookie expiry tracking longest-lived auth cookie for all 4 providers | 1.7.3 |
34
- | T-012 | Persistent browser fallback for Claude/Gemini/ChatGPT (no CDP required) | 1.4.0 |
34
+ | T-017 | Fix log spam, restart loops, CLI blocking | 2.2.0 |
35
+ | T-016 | Issue #2: Codex auth auto-import into agent auth store | 2.1.0 |
36
+ | T-015 | Issue #4: Background session mgmt with workdir isolation | 2.1.0 |
37
+ | T-014 | Issue #6: Workdir isolation (createIsolatedWorkdir, cleanup, sweep) | 2.1.0 |
38
+ | T-013 | Fix cookie expiry tracking — longest-lived auth cookie (all 4) | 1.7.3 |
39
+ | T-012 | Persistent browser fallback for Claude/Gemini/ChatGPT (no CDP) | 1.4.0 |
35
40
  | T-011 | Session-safe staged model switching (/cli-apply, /cli-pending, --now) | 0.2.25 |
36
41
  | 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
42
  | T-008 | Validate proxy endpoints + vllm model calls end-to-end | 0.2.21 |
41
43
  | 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
44
  <!-- /SECTION: completed_tasks -->
@@ -4,6 +4,35 @@ _Last 10 sessions. Older entries in LOG-ARCHIVE.md._
4
4
 
5
5
  ---
6
6
 
7
+ ## 2026-04-09 — Session 9 (Claude Opus 4.6)
8
+
9
+ > **Agent:** claude-opus-4-6
10
+ > **Phase:** fix
11
+ > **Commit before:** v2.1.3
12
+ > **Commit after:** v2.2.0
13
+
14
+ **T-017: Fix log spam, restart loops, CLI blocking**
15
+
16
+ ### Problems
17
+ 1. `register()` called per-agent (~11×) — every call logged Chrome check, provider registration, and all 32 commands
18
+ 2. `fuser -k` port cleanup killed the gateway process itself (proxy runs in-process), causing systemd restart loops
19
+ 3. `openclaw models status` (and other CLI commands) hung indefinitely because session restore launched 4 Chromium instances
20
+
21
+ ### Fixes (index.ts, src/proxy-server.ts)
22
+ - Module-level `_registerLoggedOnce` guard — Chrome/provider/commands logged once per process start
23
+ - Module-level `_proxyStarted` guard — proxy start runs once, not per-agent
24
+ - Shortened command log: count + `/cli-list` reference instead of listing all 32 names
25
+ - Removed `fuser -k` — EADDRINUSE handled gracefully (skip + log)
26
+ - `_proxyOwnedByThisProcess` flag — session restore only runs in gateway mode, skipped for CLI commands
27
+ - Codex auth import runs once per startup, not per-agent
28
+
29
+ ### Also in this session
30
+ - Installed cli-bridge plugin on production server (chef-linux@192.168.177.6)
31
+ - Set default model to `vllm/cli-claude/claude-sonnet-4-6` (Claude CLI, no API costs)
32
+ - Merged 3 Dependabot PRs (#13, #17, #19)
33
+
34
+ ---
35
+
7
36
  ## 2026-03-13 — Session 8 (Claude Opus 4.6)
8
37
 
9
38
  > **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-sonnet-4-6",
6
- "session_id": "akido-2026-03-11-v0.2.25",
7
- "timestamp": "2026-03-11T17:39:00Z",
8
- "commit": "pending (v0.2.25 not yet committed)",
9
- "phase": "implementation",
10
- "duration_minutes": 70
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": {
@@ -20,7 +20,7 @@
20
20
  "checksum": "sha256:2a2c1696de45106e01063afa35859955f0d8827c1ea3bbc117d7b46769f89a6c",
21
21
  "updated": "2026-03-11T17:39:00Z",
22
22
  "lines": 48,
23
- "summary": "1 task ready: T-010 Publish v0.2.25. No blockers."
23
+ "summary": "1 task ready: T-010 \u2014 Publish v0.2.25. No blockers."
24
24
  },
25
25
  "LOG.md": {
26
26
  "checksum": "sha256:b23892518fd5a5faa0b402bd2553d1d1cbca7b46675ba6dd4d413e1cbf03e19d",
@@ -32,7 +32,7 @@
32
32
  "checksum": "sha256:cf1c47a18f52e0b94b932177d4e9b455a0e3ae7f29b1a34802088ffa7ad98cef",
33
33
  "updated": "2026-03-11T17:39:00Z",
34
34
  "lines": 50,
35
- "summary": "Build Tests 51/51 ✅. Local v0.2.25, last published v0.2.23. T-010 (publish) ready."
35
+ "summary": "Build \u2705 Tests 51/51 \u2705. Local v0.2.25, last published v0.2.23. T-010 (publish) ready."
36
36
  },
37
37
  "TRUST.md": {
38
38
  "checksum": "sha256:cbfc8dcc17c00f8da8e220eba106aac56c63782caac655c30ba60c463994adc1",
@@ -50,27 +50,133 @@
50
50
  "checksum": "sha256:f9224bf9e993863b2b9e17f36ee605950359ba43d420fa7b4c829a6fbdc92482",
51
51
  "updated": "2026-03-07T00:00:00Z",
52
52
  "lines": 152,
53
- "summary": "4-phase AAHP pipeline: Research Architecture Implementation Review Handoff."
53
+ "summary": "4-phase AAHP pipeline: Research \u2192 Architecture \u2192 Implementation \u2192 Review \u2192 Handoff."
54
54
  }
55
55
  },
56
- "quick_context": "v0.2.25 built + 51/51 tests pass. Key changes: (1) staged model switching /cli-* now stages by default, /cli-apply to execute after session, --now for immediate; (2) sleep-resilient token refresh via setInterval; (3) stopTokenRefresh cleanup. Last published: v0.2.23. Next: T-010 publish to GitHub/npm/ClawHub.",
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": 12,
62
+ "next_task_id": 18,
63
63
  "tasks": {
64
- "T-001": { "title": "Scaffold plugin structure + AAHP handoff", "status": "done", "priority": "high", "depends_on": [], "created": "2026-03-07T20:40:00Z", "completed": "2026-03-07T20:56:00Z" },
65
- "T-002": { "title": "Implement openai-codex provider (Codex CLI auth bridge)", "status": "done", "priority": "high", "depends_on": ["T-001"], "created": "2026-03-07T20:40:00Z", "completed": "2026-03-07T20:56:00Z" },
66
- "T-003": { "title": "Test auth flow: openclaw models auth login --provider openai-codex", "status": "done", "priority": "high", "depends_on": ["T-002"], "created": "2026-03-07T20:56:00Z", "completed": "2026-03-07T21:01:00Z" },
67
- "T-004": { "title": "Verify model call: test gpt-5.2 or gpt-5.3-codex responds", "status": "done", "priority": "high", "depends_on": ["T-003"], "created": "2026-03-07T20:56:00Z", "completed": "2026-03-07T21:27:00Z" },
68
- "T-005": { "title": "Implement Gemini CLI request bridge", "status": "done", "priority": "medium", "depends_on": ["T-003"], "created": "2026-03-07T20:56:00Z", "completed": "2026-03-07T21:23:00Z" },
69
- "T-006": { "title": "Implement Claude Code CLI request bridge", "status": "done", "priority": "medium", "depends_on": ["T-003"], "created": "2026-03-07T20:56:00Z", "completed": "2026-03-07T21:23:00Z" },
70
- "T-007": { "title": "Create GitHub repo and push initial code", "status": "done", "priority": "high", "depends_on": ["T-004"], "created": "2026-03-07T21:20:00Z", "completed": "2026-03-07T21:24:00Z" },
71
- "T-008": { "title": "Validate proxy endpoints + vllm model calls end-to-end", "status": "done", "priority": "high", "depends_on": ["T-005", "T-006"], "created": "2026-03-07T21:29:00Z", "completed": "2026-03-08T08:08:44Z" },
72
- "T-009": { "title": "Stability: sleep-resilient token refresh + stopTokenRefresh cleanup", "status": "done", "priority": "high", "depends_on": ["T-008"], "created": "2026-03-11T15:00:00Z", "completed": "2026-03-11T16:30:00Z" },
73
- "T-010": { "title": "Publish v0.2.25 to GitHub + npm + ClawHub", "status": "ready", "priority": "medium", "depends_on": ["T-011"], "created": "2026-03-11T16:30:00Z" },
74
- "T-011": { "title": "Session-safe staged model switching (/cli-apply, /cli-pending, --now)", "status": "done", "priority": "high", "depends_on": ["T-009"], "created": "2026-03-11T17:00:00Z", "completed": "2026-03-11T17:39:00Z" }
75
- }
76
- }
64
+ "T-001": {
65
+ "title": "Scaffold plugin structure + AAHP handoff",
66
+ "status": "done",
67
+ "priority": "high",
68
+ "depends_on": [],
69
+ "created": "2026-03-07T20:40:00Z",
70
+ "completed": "2026-03-07T20:56:00Z"
71
+ },
72
+ "T-002": {
73
+ "title": "Implement openai-codex provider (Codex CLI auth bridge)",
74
+ "status": "done",
75
+ "priority": "high",
76
+ "depends_on": [
77
+ "T-001"
78
+ ],
79
+ "created": "2026-03-07T20:40:00Z",
80
+ "completed": "2026-03-07T20:56:00Z"
81
+ },
82
+ "T-003": {
83
+ "title": "Test auth flow: openclaw models auth login --provider openai-codex",
84
+ "status": "done",
85
+ "priority": "high",
86
+ "depends_on": [
87
+ "T-002"
88
+ ],
89
+ "created": "2026-03-07T20:56:00Z",
90
+ "completed": "2026-03-07T21:01:00Z"
91
+ },
92
+ "T-004": {
93
+ "title": "Verify model call: test gpt-5.2 or gpt-5.3-codex responds",
94
+ "status": "done",
95
+ "priority": "high",
96
+ "depends_on": [
97
+ "T-003"
98
+ ],
99
+ "created": "2026-03-07T20:56:00Z",
100
+ "completed": "2026-03-07T21:27:00Z"
101
+ },
102
+ "T-005": {
103
+ "title": "Implement Gemini CLI request bridge",
104
+ "status": "done",
105
+ "priority": "medium",
106
+ "depends_on": [
107
+ "T-003"
108
+ ],
109
+ "created": "2026-03-07T20:56:00Z",
110
+ "completed": "2026-03-07T21:23:00Z"
111
+ },
112
+ "T-006": {
113
+ "title": "Implement Claude Code CLI request bridge",
114
+ "status": "done",
115
+ "priority": "medium",
116
+ "depends_on": [
117
+ "T-003"
118
+ ],
119
+ "created": "2026-03-07T20:56:00Z",
120
+ "completed": "2026-03-07T21:23:00Z"
121
+ },
122
+ "T-007": {
123
+ "title": "Create GitHub repo and push initial code",
124
+ "status": "done",
125
+ "priority": "high",
126
+ "depends_on": [
127
+ "T-004"
128
+ ],
129
+ "created": "2026-03-07T21:20:00Z",
130
+ "completed": "2026-03-07T21:24:00Z"
131
+ },
132
+ "T-008": {
133
+ "title": "Validate proxy endpoints + vllm model calls end-to-end",
134
+ "status": "done",
135
+ "priority": "high",
136
+ "depends_on": [
137
+ "T-005",
138
+ "T-006"
139
+ ],
140
+ "created": "2026-03-07T21:29:00Z",
141
+ "completed": "2026-03-08T08:08:44Z"
142
+ },
143
+ "T-009": {
144
+ "title": "Stability: sleep-resilient token refresh + stopTokenRefresh cleanup",
145
+ "status": "done",
146
+ "priority": "high",
147
+ "depends_on": [
148
+ "T-008"
149
+ ],
150
+ "created": "2026-03-11T15:00:00Z",
151
+ "completed": "2026-03-11T16:30:00Z"
152
+ },
153
+ "T-010": {
154
+ "title": "Publish v0.2.25 to GitHub + npm + ClawHub",
155
+ "status": "ready",
156
+ "priority": "medium",
157
+ "depends_on": [
158
+ "T-011"
159
+ ],
160
+ "created": "2026-03-11T16:30:00Z"
161
+ },
162
+ "T-011": {
163
+ "title": "Session-safe staged model switching (/cli-apply, /cli-pending, --now)",
164
+ "status": "done",
165
+ "priority": "high",
166
+ "depends_on": [
167
+ "T-009"
168
+ ],
169
+ "created": "2026-03-11T17:00:00Z",
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"
179
+ }
180
+ },
181
+ "version": "2.2.0"
182
+ }
@@ -1,13 +1,13 @@
1
1
  # NEXT_ACTIONS.md — openclaw-cli-bridge-elvatis
2
2
 
3
- _Last updated: 2026-03-13_
3
+ _Last updated: 2026-04-09_
4
4
 
5
5
  <!-- SECTION: summary -->
6
6
  ## Status Summary
7
7
 
8
8
  | Status | Count |
9
9
  |---------|-------|
10
- | Done | 16 |
10
+ | Done | 17 |
11
11
  | Ready | 0 |
12
12
  | Blocked | 0 |
13
13
  <!-- /SECTION: summary -->
@@ -30,6 +30,7 @@ _No blocked tasks._
30
30
 
31
31
  | Task | Title | Date |
32
32
  |-------|--------------------------------------------------------------------|------------|
33
+ | T-017 | Fix log spam, restart loops, CLI blocking (v2.2.0) | 2026-04-09 |
33
34
  | T-016 | Issue #2: Codex auth auto-import into agent auth store | 2026-03-19 |
34
35
  | T-015 | Issue #4: Background session mgmt with workdir isolation | 2026-03-19 |
35
36
  | T-014 | Issue #6: Workdir isolation (createIsolatedWorkdir, cleanup, sweep) | 2026-03-19 |
@@ -1,10 +1,10 @@
1
1
  # STATUS — openclaw-cli-bridge-elvatis
2
2
 
3
- ## Current Version: 2.1.0
3
+ ## Current Version: 2.2.0
4
4
 
5
- - **npm:** @elvatis_com/openclaw-cli-bridge-elvatis (not yet published to npm)
6
- - **ClawHub:** openclaw-cli-bridge-elvatis@1.9.2
7
- - **GitHub:** https://github.com/elvatis/openclaw-cli-bridge-elvatis/releases/tag/v1.9.2
5
+ - **npm:** @elvatis_com/openclaw-cli-bridge-elvatis@2.2.0 (pending publish)
6
+ - **ClawHub:** openclaw-cli-bridge-elvatis@2.2.0 (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,7 @@ 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.0 (2026-04-09): Fix log spam (module-level guards), remove fuser -k restart loops, session restore gateway-only, EADDRINUSE graceful handling
50
51
  - v2.1.0 (2026-03-19): Issue #6 workdir isolation, Issue #4 session mgmt enhancements, Issue #2 codex auth auto-import
51
52
  - v2.0.0: Major version bump
52
53
  - 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:** `1.9.2`
5
+ **Current version:** `2.2.0`
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] proxy ready on :31337
195
- [cli-bridge] registered 14 commands: /cli-sonnet, /cli-opus, /cli-haiku,
196
- /cli-gemini, /cli-gemini-flash, /cli-gemini3, /cli-gemini3-flash,
197
- /cli-codex, /cli-codex-spark, /cli-codex52, /cli-codex54, /cli-codex-mini,
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,30 @@ npm run ci # lint + typecheck + test
377
376
 
378
377
  ## Changelog
379
378
 
379
+ ### v2.2.0
380
+ - **fix:** Module-level guards prevent duplicate log spam — `register()` is called per-agent (~11×), now logs Chrome/provider/commands only once
381
+ - **fix:** Shortened command registration log: count + `/cli-list` reference instead of listing all 32 commands
382
+ - **fix:** Removed `fuser -k` port cleanup — was killing the gateway process itself, causing systemd restart loops
383
+ - **fix:** EADDRINUSE now handled gracefully (skip + log) instead of process killing
384
+ - **fix:** Session restore (4× Chromium) only runs in gateway mode — CLI commands like `openclaw models status` no longer hang
385
+ - **fix:** Codex auth import runs once per startup, not per-agent
386
+
387
+ ### v2.1.3
388
+ - **docs:** All documentation updated to reflect current version (README, SKILL.md, STATUS.md, MANIFEST.json)
389
+
390
+ ### v2.1.2
391
+ - **fix:** Updated ChatGPT web session model list: gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, o3, o4-mini, gpt-5, gpt-5-mini
392
+ - **fix:** `server.unref()` — proxy server no longer keeps `openclaw doctor` hanging indefinitely
393
+
394
+ ### v2.1.1
395
+ - **fix:** `server.unref()` on proxy server so `openclaw doctor` (and short-lived CLI commands) exit cleanly
396
+
397
+ ### v2.1.0
398
+ - **feat:** Session manager for isolated per-request workdirs
399
+ - **feat:** Register OpenCode and Pi slash commands (`/cli-opencode`, `/cli-pi`)
400
+ - **feat:** Codex auth auto-import support
401
+ - **feat:** Workdir isolation for all CLI runners
402
+
380
403
  ### v1.9.2
381
404
  - **fix:** Correct `maxTokens` and `contextWindow` for all CLI_MODELS — were hardcoded to 8192 output tokens
382
405
  - Claude Opus 4.6: 1M context / 128k output (was 200k/8k)
package/SKILL.md CHANGED
@@ -68,4 +68,4 @@ On gateway restart, if any session has expired, a **WhatsApp alert** is sent aut
68
68
 
69
69
  See `README.md` for full configuration reference and architecture diagram.
70
70
 
71
- **Version:** 1.9.1
71
+ **Version:** 2.1.3
package/index.ts CHANGED
@@ -240,6 +240,22 @@ 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
+ // Registration guard — register() is called once per agent (~11 times on a
244
+ // typical gateway). Only log noisy one-time info (Chrome check, provider
245
+ // registration, command list, proxy status) on the FIRST call.
246
+ let _registerLoggedOnce = false;
247
+
248
+ // Proxy start guard — prevents multiple concurrent startProxy() calls from
249
+ // racing. The first call wins; subsequent calls detect the running proxy via
250
+ // probeExisting() and reuse it silently.
251
+ let _proxyStarted = false;
252
+
253
+ // Tracks whether THIS process owns the proxy (freshly started it, not just reusing
254
+ // an external one). Used to decide if heavyweight startup tasks (session restores,
255
+ // keep-alive intervals) should run. Short-lived CLI commands (models status, doctor)
256
+ // reuse the gateway's proxy but should NOT launch Chromium instances.
257
+ let _proxyOwnedByThisProcess = false;
258
+
243
259
  // Session keep-alive interval — refreshes browser cookies every 20h
244
260
  let _keepAliveInterval: ReturnType<typeof setInterval> | null = null;
245
261
 
@@ -1010,14 +1026,16 @@ const plugin = {
1010
1026
  // Stealth mode uses channel: "chrome" (real system Chrome). If it's missing,
1011
1027
  // browser launches will fail or Cloudflare will block the bundled Chromium.
1012
1028
  const chromeCheck = checkSystemChrome();
1013
- if (chromeCheck.available) {
1014
- api.logger.info(`[cli-bridge] system Chrome found: ${chromeCheck.version ?? chromeCheck.path}`);
1015
- } else {
1016
- api.logger.warn(
1017
- `[cli-bridge] ⚠ system Chrome not found! Web browser providers (/grok-login, /gemini-login, etc.) ` +
1018
- `require Google Chrome or Chromium installed system-wide. ` +
1019
- `Install with: sudo apt install google-chrome-stable (or chromium-browser)`
1020
- );
1029
+ if (!_registerLoggedOnce) {
1030
+ if (chromeCheck.available) {
1031
+ api.logger.info(`[cli-bridge] system Chrome found: ${chromeCheck.version ?? chromeCheck.path}`);
1032
+ } else {
1033
+ api.logger.warn(
1034
+ `[cli-bridge] system Chrome not found! Web browser providers (/grok-login, /gemini-login, etc.) ` +
1035
+ `require Google Chrome or Chromium installed system-wide. ` +
1036
+ `Install with: sudo apt install google-chrome-stable (or chromium-browser)`
1037
+ );
1038
+ }
1021
1039
  }
1022
1040
 
1023
1041
  // ── Session restore: only on first plugin load (not on hot-reloads) ──────
@@ -1027,7 +1045,13 @@ const plugin = {
1027
1045
  // restore once, on the very first load (when all contexts are null).
1028
1046
  //
1029
1047
  // Guard: _startupRestoreDone is module-level and persists across hot-reloads.
1030
- if (!_startupRestoreDone) {
1048
+ // IMPORTANT: Only restore sessions when the proxy is enabled (gateway mode).
1049
+ // Short-lived CLI commands (openclaw models status, openclaw doctor, etc.)
1050
+ // must NOT launch 4 Chromium instances — they block the process from exiting.
1051
+ // Only restore browser sessions when this process OWNS the proxy (gateway mode).
1052
+ // Short-lived CLI commands (openclaw models status) reuse an external proxy and
1053
+ // must not launch 4 Chromium instances that block the process from exiting.
1054
+ if (!_startupRestoreDone && _proxyOwnedByThisProcess) {
1031
1055
  _startupRestoreDone = true;
1032
1056
  void (async () => {
1033
1057
  await new Promise(r => setTimeout(r, 5000)); // wait for proxy + gateway to settle
@@ -1260,23 +1284,27 @@ const plugin = {
1260
1284
  },
1261
1285
  });
1262
1286
 
1263
- api.logger.info("[cli-bridge] openai-codex provider registered");
1287
+ if (!_registerLoggedOnce) {
1288
+ api.logger.info("[cli-bridge] openai-codex provider registered");
1289
+ }
1264
1290
 
1265
1291
  // Auto-import Codex CLI credentials into the agent auth store (Issue #2).
1266
1292
  // This ensures `openai-codex/*` models work immediately without manual
1267
1293
  // `openclaw models auth login`. Runs async, non-blocking.
1268
- void importCodexAuth({
1269
- codexAuthPath,
1270
- log: (msg) => api.logger.info(`[cli-bridge:codex-import] ${msg}`),
1271
- }).then((result) => {
1272
- if (result.imported) {
1273
- 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
- } else if (result.error) {
1277
- api.logger.warn(`[cli-bridge] Codex auth import failed: ${result.error}`);
1278
- }
1279
- });
1294
+ // Only run Codex auth import once — it fires per-agent and spams logs otherwise
1295
+ if (!_registerLoggedOnce) {
1296
+ void importCodexAuth({
1297
+ codexAuthPath,
1298
+ log: (msg) => api.logger.info(`[cli-bridge:codex-import] ${msg}`),
1299
+ }).then((result) => {
1300
+ if (result.imported) {
1301
+ api.logger.info("[cli-bridge] Codex auth auto-imported into agent auth store");
1302
+ } else if (result.error) {
1303
+ api.logger.warn(`[cli-bridge] Codex auth import failed: ${result.error}`);
1304
+ }
1305
+ // skipped = already current → no log needed
1306
+ });
1307
+ }
1280
1308
  }
1281
1309
 
1282
1310
  // ── Phase 2: CLI request proxy ─────────────────────────────────────────────
@@ -1302,6 +1330,11 @@ const plugin = {
1302
1330
  };
1303
1331
 
1304
1332
  const startProxy = async (): Promise<void> => {
1333
+ // Guard: only the first register() call starts the proxy.
1334
+ // Subsequent per-agent calls skip entirely.
1335
+ if (_proxyStarted) return;
1336
+ _proxyStarted = true;
1337
+
1305
1338
  // If a healthy proxy is already up, reuse it — no need to rebind.
1306
1339
  const alive = await probeExisting();
1307
1340
  if (alive) {
@@ -1369,6 +1402,7 @@ const plugin = {
1369
1402
  }),
1370
1403
  });
1371
1404
  proxyServer = server;
1405
+ _proxyOwnedByThisProcess = true;
1372
1406
  api.logger.info(
1373
1407
  `[cli-bridge] proxy ready on :${port} — vllm/cli-gemini/* and vllm/cli-claude/* available`
1374
1408
  );
@@ -2442,7 +2476,10 @@ const plugin = {
2442
2476
  "/bridge-status",
2443
2477
  "/cli-help",
2444
2478
  ];
2445
- api.logger.info(`[cli-bridge] registered ${allCommands.length} commands: ${allCommands.join(", ")}`);
2479
+ if (!_registerLoggedOnce) {
2480
+ api.logger.info(`[cli-bridge] registered ${allCommands.length} commands (use /cli-list to see all)`);
2481
+ }
2482
+ _registerLoggedOnce = true;
2446
2483
  },
2447
2484
  };
2448
2485
 
@@ -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.0.0",
5
+ "version": "2.2.0",
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.2",
3
+ "version": "2.2.0",
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": "^5.9.3",
23
- "typescript-eslint": "^8.57.0",
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
+ }
@@ -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) => reject(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 server listening on http://127.0.0.1:${opts.port}`
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.
@@ -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