vibe-coding-master 0.7.44 → 0.7.45

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
@@ -243,18 +243,18 @@ Open `Usage Analytics` in the sidebar `Task` section to inspect native Claude
243
243
  Code usage for the active task. The report shows task totals and breakdowns by
244
244
  role and model for input, output, cache-read, and cache-creation tokens plus
245
245
  estimated USD cost. It combines every restart and resumed Claude session for
246
- all seven roles. CCR/GPT usage is excluded.
246
+ all seven roles. Codex Bridge usage is excluded.
247
247
 
248
248
  VCM retains only aggregate task data in
249
249
  `<task-worktree>/.ai/vcm/telemetry/usage.json`. The file is temporary runtime
250
250
  state and is removed with the task worktree when the task is closed.
251
251
 
252
- ### GPT Through Claude Code Router
252
+ ### GPT Through Codex Bridge
253
253
 
254
- VCM can launch its normal Claude Code sessions with `GPT-5.6 Sol (CCR)` through
255
- a host-running [Claude Code Router](https://github.com/musistudio/claude-code-router).
256
- CCR must already be installed, authenticated, configured, and running on the
257
- host. VCM does not manage the CCR process.
254
+ VCM can launch its normal Claude Code sessions with models provided by a
255
+ host-running Codex Bridge. The Bridge must already be installed and running,
256
+ and Codex must be logged in on the host. VCM does not manage the Bridge process
257
+ or Codex login.
258
258
 
259
259
  VCM automatically checks the local-host and DevContainer endpoints:
260
260
 
@@ -263,36 +263,34 @@ http://127.0.0.1:3456
263
263
  http://host.docker.internal:3456
264
264
  ```
265
265
 
266
- Configure CCR to listen on port `3456` with an API key. When VCM runs in a
267
- DevContainer, make the second endpoint reachable from the container. In the VCM
268
- `Settings` section:
266
+ Configure Codex Bridge to listen on port `3456`. When VCM runs in a
267
+ DevContainer, start it with a container-reachable bind address such as
268
+ `codex-bridge serve --host 0.0.0.0 --port 3456`. In the VCM `Settings` section:
269
269
 
270
- 1. enter and save the CCR API key;
271
- 2. enable `CCR GPT models`;
270
+ 1. enter and save the Codex Bridge API key;
271
+ 2. enable `Codex Bridge models`;
272
272
  3. confirm the status is `available`;
273
- 4. select `GPT-5.6 Sol (CCR)` in any Session model control.
273
+ 4. select any discovered Codex Bridge model in a Session model control.
274
274
 
275
275
  The key is stored in global VCM state (`~/.vcm/settings.json`) with owner-only
276
- permissions and is never returned by the settings API. It is used for CCR
277
- checks, model discovery, and the GPT-only `apiKeyHelper`. GPT sessions receive a
276
+ permissions and is never returned by the settings API. It is used for Bridge
277
+ checks, model discovery, and the Bridge-only `apiKeyHelper`. Bridge sessions receive a
278
278
  child-only `--settings` override and use the isolated Claude configuration root
279
- `~/.vcm/claude/ccr`. VCM never edits `~/.claude/settings.json`. Native Claude
279
+ `~/.vcm/claude/codex-bridge`. VCM never edits `~/.claude/settings.json`. Native Claude
280
280
  sessions keep their normal configuration and account authentication; VCM only
281
- removes inherited environment variables that clearly point at the local CCR
282
- gateway from the native child process. Configure CCR without enabling its
283
- global Claude Code or Claude App takeover if those clients should remain on
284
- Anthropic.
281
+ removes inherited environment variables that clearly point at the local Bridge
282
+ from the native child process.
285
283
 
286
- CCR/GPT child processes use a hard context limit and auto-compaction window of
284
+ Codex Bridge child processes use a hard context limit and auto-compaction window of
287
285
  `258400` tokens. VCM sets Claude Code's proactive compaction threshold to `90%`
288
- (about `232560` tokens), leaving headroom before the gateway limit. Native
286
+ (about `232560` tokens), leaving headroom before the Codex API limit. Native
289
287
  Claude sessions receive no VCM-owned context override.
290
288
 
291
289
  Resume keeps the provider recorded by the existing Session. Use Restart when
292
- switching between a native Claude model and `GPT-5.6 Sol (CCR)`. If CCR is
293
- disabled, unreachable, rejects the key, or does not expose
294
- `Codex API/gpt-5.6-sol`, VCM blocks the new Start, Resume, or Restart and does
295
- not fall back to another model.
290
+ switching between native Claude and Codex Bridge. If the Bridge is disabled,
291
+ unreachable, rejects the key, has unavailable Codex credentials, or no longer
292
+ exposes the selected model, VCM blocks the new Start, Resume, or Restart and
293
+ does not fall back to another model.
296
294
 
297
295
  ## Launch Template
298
296
 
@@ -482,7 +480,7 @@ Use it to:
482
480
  appropriate
483
481
 
484
482
  VCM bundles the Claude Code LSP bridge and loads it only for Architect sessions,
485
- including CCR launches. The project environment must still
483
+ including Codex Bridge launches. The project environment must still
486
484
  provide the language server for each detected language: `rust-analyzer`,
487
485
  `typescript-language-server`, `pyright-langserver`, `gopls`, `clangd`, or
488
486
  `jdtls`. Architect preloads `vcm-code-navigation` and uses LSP for semantic
@@ -1,5 +1,5 @@
1
1
  import { roleRuntimeDisallowedTools, roleUsesLsp } from "../role-tool-policy.js";
2
- import { isCcrSessionModel } from "../../shared/types/session.js";
2
+ import { isCodexBridgeSessionModel } from "../../shared/types/session.js";
3
3
  import { VcmError } from "../errors.js";
4
4
  export function createClaudeAdapter(runner) {
5
5
  return {
@@ -33,7 +33,7 @@ export function createClaudeAdapter(runner) {
33
33
  if (claudeSessionId) {
34
34
  args.push(resume ? "--resume" : "--session-id", claudeSessionId);
35
35
  }
36
- if (!isCcrSessionModel(model)) {
36
+ if (!isCodexBridgeSessionModel(model)) {
37
37
  args.push("--model", model);
38
38
  }
39
39
  if (effort === "ultracode") {
@@ -0,0 +1,213 @@
1
+ import { CODEX_BRIDGE_BASE_URLS } from "../../shared/types/session.js";
2
+ const DEFAULT_TIMEOUT_MS = 3_000;
3
+ export function createCodexBridgeAdapter(deps = {}) {
4
+ const baseUrls = (deps.baseUrl ? [deps.baseUrl] : CODEX_BRIDGE_BASE_URLS)
5
+ .map((baseUrl) => baseUrl.replace(/\/+$/, ""));
6
+ const fetchImpl = deps.fetch ?? globalThis.fetch;
7
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8
+ return {
9
+ async probe(apiKey) {
10
+ const failures = [];
11
+ for (const baseUrl of baseUrls) {
12
+ const result = await probeEndpoint(fetchImpl, baseUrl, apiKey, timeoutMs);
13
+ if (result.baseUrl) {
14
+ return result;
15
+ }
16
+ failures.push(result);
17
+ }
18
+ return combineProbeFailures(baseUrls, failures);
19
+ }
20
+ };
21
+ }
22
+ async function probeEndpoint(fetchImpl, baseUrl, apiKey, timeoutMs) {
23
+ try {
24
+ const health = await requestJson(fetchImpl, `${baseUrl}/health`, timeoutMs);
25
+ if (!health.response.ok) {
26
+ return invalidResponse(`Codex Bridge health check at ${baseUrl} returned HTTP ${health.response.status}.`);
27
+ }
28
+ if (!isCodexBridgeHealth(health.payload)) {
29
+ return {
30
+ connectionState: "not-codex-bridge",
31
+ modelAvailable: false,
32
+ models: [],
33
+ error: `${baseUrl} did not identify as Codex Bridge.`
34
+ };
35
+ }
36
+ const headers = {
37
+ authorization: `Bearer ${apiKey}`,
38
+ "user-agent": "VibeCodingMaster"
39
+ };
40
+ const auth = await requestJson(fetchImpl, `${baseUrl}/auth/status`, timeoutMs, headers);
41
+ if (auth.response.status === 401 || auth.response.status === 403) {
42
+ return {
43
+ connectionState: "unauthorized",
44
+ modelAvailable: false,
45
+ models: [],
46
+ baseUrl,
47
+ error: "Codex Bridge rejected the configured API key."
48
+ };
49
+ }
50
+ if (!auth.response.ok) {
51
+ return invalidResponse(responseError("Codex Bridge auth status", baseUrl, auth.response, auth.payload), baseUrl);
52
+ }
53
+ const authState = readAuthState(auth.payload);
54
+ if (!authState) {
55
+ return invalidResponse("Codex Bridge returned an invalid /auth/status response.", baseUrl);
56
+ }
57
+ if (authState.state !== "ready") {
58
+ return {
59
+ connectionState: "codex-auth-unavailable",
60
+ modelAvailable: false,
61
+ models: [],
62
+ baseUrl,
63
+ error: authState.message
64
+ ?? `Codex authentication is ${authState.state}. Open Codex or run codex login, then retry.`
65
+ };
66
+ }
67
+ const modelsResponse = await requestJson(fetchImpl, `${baseUrl}/v1/models`, timeoutMs, headers);
68
+ if (!modelsResponse.response.ok) {
69
+ const code = readErrorCode(modelsResponse.payload);
70
+ const connectionState = code?.startsWith("CODEX_AUTH_")
71
+ ? "codex-auth-unavailable"
72
+ : modelsResponse.response.status === 401 || modelsResponse.response.status === 403
73
+ ? "unauthorized"
74
+ : "invalid-response";
75
+ return {
76
+ connectionState,
77
+ modelAvailable: false,
78
+ models: [],
79
+ baseUrl,
80
+ error: responseError("Codex Bridge model discovery", baseUrl, modelsResponse.response, modelsResponse.payload)
81
+ };
82
+ }
83
+ const models = readModelDescriptors(modelsResponse.payload);
84
+ if (!models) {
85
+ return invalidResponse("Codex Bridge returned an invalid /v1/models response.", baseUrl);
86
+ }
87
+ return {
88
+ connectionState: "available",
89
+ modelAvailable: models.length > 0,
90
+ models,
91
+ baseUrl,
92
+ ...(models.length > 0 ? {} : { error: "Codex Bridge did not expose any models." })
93
+ };
94
+ }
95
+ catch (error) {
96
+ const timedOut = isAbortError(error);
97
+ return {
98
+ connectionState: "unreachable",
99
+ modelAvailable: false,
100
+ models: [],
101
+ error: timedOut
102
+ ? `Codex Bridge connection timed out after ${timeoutMs} ms at ${baseUrl}.`
103
+ : `Codex Bridge could not be reached from the VCM backend at ${baseUrl}. Reason: ${errorMessage(error)}`
104
+ };
105
+ }
106
+ }
107
+ function combineProbeFailures(baseUrls, failures) {
108
+ const lastFailure = failures.at(-1);
109
+ return {
110
+ connectionState: lastFailure?.connectionState ?? "unreachable",
111
+ modelAvailable: false,
112
+ models: [],
113
+ error: `Codex Bridge was not found at ${baseUrls.join(" or ")}. ${failures
114
+ .map((failure) => failure.error)
115
+ .filter(Boolean)
116
+ .join(" ")}`.trim()
117
+ };
118
+ }
119
+ async function requestJson(fetchImpl, url, timeoutMs, headers = {}) {
120
+ const controller = new AbortController();
121
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
122
+ try {
123
+ const response = await fetchImpl(url, {
124
+ method: "GET",
125
+ headers: {
126
+ accept: "application/json",
127
+ ...headers
128
+ },
129
+ signal: controller.signal
130
+ });
131
+ let payload;
132
+ try {
133
+ payload = await response.json();
134
+ }
135
+ catch {
136
+ payload = undefined;
137
+ }
138
+ return { response, payload };
139
+ }
140
+ finally {
141
+ clearTimeout(timeout);
142
+ }
143
+ }
144
+ function isCodexBridgeHealth(value) {
145
+ return isObject(value)
146
+ && value.status === "ok"
147
+ && value.service === "codex-bridge";
148
+ }
149
+ function readAuthState(value) {
150
+ if (!isObject(value) || typeof value.state !== "string" || !value.state.trim()) {
151
+ return undefined;
152
+ }
153
+ return {
154
+ state: value.state.trim(),
155
+ ...(typeof value.message === "string" && value.message.trim()
156
+ ? { message: value.message.trim() }
157
+ : {})
158
+ };
159
+ }
160
+ function readModelDescriptors(value) {
161
+ if (!isObject(value) || !Array.isArray(value.data)) {
162
+ return undefined;
163
+ }
164
+ const models = [];
165
+ for (const item of value.data) {
166
+ if (!isObject(item) || typeof item.id !== "string" || !item.id.trim()) {
167
+ return undefined;
168
+ }
169
+ models.push({
170
+ id: item.id.trim(),
171
+ ...(typeof item.display_name === "string" && item.display_name.trim()
172
+ ? { displayName: item.display_name.trim() }
173
+ : {})
174
+ });
175
+ }
176
+ return models;
177
+ }
178
+ function responseError(operation, baseUrl, response, payload) {
179
+ const message = readErrorMessage(payload);
180
+ return `${operation} at ${baseUrl} returned HTTP ${response.status}.${message ? ` Reason: ${message}` : ""}`;
181
+ }
182
+ function readErrorCode(value) {
183
+ return isObject(value)
184
+ && isObject(value.error)
185
+ && typeof value.error.code === "string"
186
+ ? value.error.code
187
+ : undefined;
188
+ }
189
+ function readErrorMessage(value) {
190
+ return isObject(value)
191
+ && isObject(value.error)
192
+ && typeof value.error.message === "string"
193
+ ? value.error.message
194
+ : undefined;
195
+ }
196
+ function invalidResponse(error, baseUrl) {
197
+ return {
198
+ connectionState: "invalid-response",
199
+ modelAvailable: false,
200
+ models: [],
201
+ ...(baseUrl ? { baseUrl } : {}),
202
+ error
203
+ };
204
+ }
205
+ function isAbortError(error) {
206
+ return error instanceof Error && error.name === "AbortError";
207
+ }
208
+ function errorMessage(error) {
209
+ return error instanceof Error ? error.message : String(error);
210
+ }
211
+ function isObject(value) {
212
+ return typeof value === "object" && value !== null && !Array.isArray(value);
213
+ }
@@ -5,13 +5,13 @@ export function registerAppSettingsRoutes(app, deps) {
5
5
  app.put("/api/settings/preferences", async (request) => {
6
6
  return deps.appSettings.updatePreferences(request.body ?? {});
7
7
  });
8
- app.get("/api/settings/ccr", async () => {
9
- return deps.ccrIntegration.getStatus();
8
+ app.get("/api/settings/codex-bridge", async () => {
9
+ return deps.codexBridgeIntegration.getStatus();
10
10
  });
11
- app.put("/api/settings/ccr", async (request) => {
12
- return deps.ccrIntegration.updateSettings(request.body ?? {});
11
+ app.put("/api/settings/codex-bridge", async (request) => {
12
+ return deps.codexBridgeIntegration.updateSettings(request.body ?? {});
13
13
  });
14
- app.post("/api/settings/ccr/check", async () => {
15
- return deps.ccrIntegration.checkConnection();
14
+ app.post("/api/settings/codex-bridge/check", async () => {
15
+ return deps.codexBridgeIntegration.checkConnection();
16
16
  });
17
17
  }
@@ -4,13 +4,13 @@ import Fastify from "fastify";
4
4
  import fastifyStatic from "@fastify/static";
5
5
  import { createArtifactService } from "./services/artifact-service.js";
6
6
  import { createClaudeAdapter } from "./adapters/claude-adapter.js";
7
- import { createCcrGatewayAdapter } from "./adapters/ccr-gateway-adapter.js";
7
+ import { createCodexBridgeAdapter } from "./adapters/codex-bridge-adapter.js";
8
8
  import { createCommandRunner } from "./adapters/command-runner.js";
9
9
  import { createCommandDispatcher } from "./services/command-dispatcher.js";
10
10
  import { createClaudeHookService } from "./services/claude-hook-service.js";
11
11
  import { createGitAdapter } from "./adapters/git-adapter.js";
12
12
  import { createAppSettingsService } from "./services/app-settings-service.js";
13
- import { createCcrIntegrationService } from "./services/ccr-integration-service.js";
13
+ import { createCodexBridgeIntegrationService } from "./services/codex-bridge-integration-service.js";
14
14
  import { createAutoMemoryService } from "./services/auto-memory-service.js";
15
15
  import { createArchitectRestartService } from "./services/architect-restart-service.js";
16
16
  import { createRoleStallDetectorService } from "./services/role-stall-detector-service.js";
@@ -88,7 +88,7 @@ export async function createServer(deps, options = {}) {
88
88
  registerDiagnosticsRoutes(app, { diagnosticsService: deps.diagnosticsService });
89
89
  registerAppSettingsRoutes(app, {
90
90
  appSettings: deps.appSettings,
91
- ccrIntegration: deps.ccrIntegration
91
+ codexBridgeIntegration: deps.codexBridgeIntegration
92
92
  });
93
93
  registerClaudeHookRoutes(app, { claudeHookService: deps.claudeHookService });
94
94
  registerGateReviewRoutes(app, {
@@ -186,7 +186,7 @@ export async function createServer(deps, options = {}) {
186
186
  onManualInterrupt: (sessionId) => deps.terminalInterruptService.handleManualInterrupt(sessionId)
187
187
  });
188
188
  app.addHook("onReady", async () => {
189
- await deps.ccrIntegration.initialize();
189
+ await deps.codexBridgeIntegration.initialize();
190
190
  await cleanupRecentTranslationRuntime(deps);
191
191
  deps.terminalProcessExitService.start();
192
192
  deps.runtimeCoordinator.start();
@@ -235,9 +235,9 @@ export function createDefaultServerDeps(options = {}) {
235
235
  const git = createGitAdapter(runner);
236
236
  const claude = createClaudeAdapter(runner);
237
237
  const appSettings = createAppSettingsService({ fs });
238
- const ccrIntegration = createCcrIntegrationService({
238
+ const codexBridgeIntegration = createCodexBridgeIntegrationService({
239
239
  settings: appSettings,
240
- gateway: createCcrGatewayAdapter()
240
+ bridge: createCodexBridgeAdapter()
241
241
  });
242
242
  const runtime = createNodePtyTerminalRuntime({ fs });
243
243
  const registry = createSessionRegistry();
@@ -255,7 +255,7 @@ export function createDefaultServerDeps(options = {}) {
255
255
  projectService,
256
256
  taskService,
257
257
  taskWorkflowService,
258
- ccrIntegration,
258
+ codexBridgeIntegration,
259
259
  apiUrl: options.apiUrl
260
260
  });
261
261
  const harnessService = createHarnessService({
@@ -458,7 +458,7 @@ export function createDefaultServerDeps(options = {}) {
458
458
  });
459
459
  return {
460
460
  appSettings,
461
- ccrIntegration,
461
+ codexBridgeIntegration,
462
462
  projectService,
463
463
  taskService,
464
464
  taskCloseService,
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
3
3
  import { VCM_ROLE_NAMES } from "../../shared/constants.js";
4
4
  import { GATE_REVIEW_GATES } from "../../shared/types/gate-review.js";
5
5
  import { createDefaultLaunchTemplate, createDefaultToolSessionDefaults, DEFAULT_TRANSLATION_OUTPUT_MODE, DEFAULT_TRANSLATION_TARGET_LANGUAGE, TRANSLATION_OUTPUT_MODE_OPTIONS, TRANSLATION_TARGET_LANGUAGE_OPTIONS } from "../../shared/types/app-settings.js";
6
- import { CLAUDE_MODEL_OPTIONS, CCR_GPT_SESSION_MODEL, SESSION_EFFORT_OPTIONS } from "../../shared/types/session.js";
6
+ import { CLAUDE_MODEL_OPTIONS, isCodexBridgeSessionModel, SESSION_EFFORT_OPTIONS } from "../../shared/types/session.js";
7
7
  import { resolveVcmDataDir } from "../vcm-data-dir.js";
8
8
  const MAX_RECENT_REPOSITORIES = 5;
9
9
  export function createAppSettingsService(deps) {
@@ -161,20 +161,20 @@ export function createAppSettingsService(deps) {
161
161
  requiredGates: normalizedRequiredGates
162
162
  };
163
163
  },
164
- async getCcrIntegrationSettings() {
165
- return normalizeCcrIntegrationSettings((await loadSettings()).ccr);
164
+ async getCodexBridgeIntegrationSettings() {
165
+ return normalizeCodexBridgeIntegrationSettings((await loadSettings()).codexBridge);
166
166
  },
167
- async updateCcrIntegrationSettings(input) {
167
+ async updateCodexBridgeIntegrationSettings(input) {
168
168
  const current = await loadSettings();
169
- const ccr = normalizeCcrIntegrationSettings({
170
- ...current.ccr,
169
+ const codexBridge = normalizeCodexBridgeIntegrationSettings({
170
+ ...current.codexBridge,
171
171
  ...input
172
172
  });
173
173
  await saveSettings({
174
174
  ...current,
175
- ccr
175
+ codexBridge
176
176
  });
177
- return ccr;
177
+ return codexBridge;
178
178
  },
179
179
  getSettingsPath() {
180
180
  return settingsPath;
@@ -261,10 +261,10 @@ function normalizeSettingsFile(input) {
261
261
  if (gateReview) {
262
262
  settings.gateReview = gateReview;
263
263
  }
264
- settings.ccr = normalizeCcrIntegrationSettings(input.ccr);
264
+ settings.codexBridge = normalizeCodexBridgeIntegrationSettings(input.codexBridge);
265
265
  return settings;
266
266
  }
267
- function normalizeCcrIntegrationSettings(input) {
267
+ function normalizeCodexBridgeIntegrationSettings(input) {
268
268
  const candidate = isObject(input) ? input : {};
269
269
  const apiKey = typeof candidate.apiKey === "string" ? candidate.apiKey.trim() : "";
270
270
  return {
@@ -355,7 +355,7 @@ function normalizeClaudeModel(input, fallback) {
355
355
  if (typeof input !== "string") {
356
356
  return fallback;
357
357
  }
358
- if (input === CCR_GPT_SESSION_MODEL) {
358
+ if (isCodexBridgeSessionModel(input)) {
359
359
  return input;
360
360
  }
361
361
  const model = CLAUDE_MODEL_OPTIONS.find((option) => option.value === input);