codex-copilot-dx 0.4.12 → 0.4.14

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/bin/cli.mjs CHANGED
@@ -6,7 +6,7 @@ import { ensureClaudeConfig } from "../src/claude-config.mjs";
6
6
  import { applyClaudeDesktopConfig, formatClaudeDesktopApplyResult, generatedClaudeDesktopApiKey } from "../src/claude-desktop-config.mjs";
7
7
  import { startAdapter } from "../src/adapter.mjs";
8
8
  import { listModels, refreshVSCodeVersion } from "../src/copilot.mjs";
9
- import { claudeDesktopModelDefsFromCopilotModels, claudeDesktopModelIds, parseModelAliasEnv } from "../src/models.mjs";
9
+ import { claudeDesktopModelDefsFromCopilotModels, claudeDesktopModelIds, gptModelIdsFromCopilotModels, parseModelAliasEnv } from "../src/models.mjs";
10
10
  import { status } from "../src/status.mjs";
11
11
  import { printUsageSummary } from "../src/usage.mjs";
12
12
  import { checkForUpdate, localPackageVersion } from "../src/version.mjs";
@@ -36,7 +36,15 @@ async function refreshClaudeDesktopModelDefs() {
36
36
  if (httpStatus < 200 || httpStatus >= 300) {
37
37
  throw new Error(`Copilot models returned HTTP ${httpStatus}`);
38
38
  }
39
- const modelDefs = claudeDesktopModelDefsFromCopilotModels(JSON.parse(body));
39
+ const models = JSON.parse(body);
40
+ const gptModelIds = gptModelIdsFromCopilotModels(models);
41
+ if (gptModelIds.length) {
42
+ console.log(status("ok", `Refreshed GPT models from GitHub Copilot: ${gptModelIds.join(", ")}`));
43
+ } else {
44
+ console.log(status("warn", "Copilot models response contained no GPT models"));
45
+ }
46
+
47
+ const modelDefs = claudeDesktopModelDefsFromCopilotModels(models);
40
48
  if (!modelDefs.length) throw new Error("Copilot models response contained no Claude models");
41
49
  console.log(status("ok", `Refreshed Claude models from GitHub Copilot: ${modelDefs.map((model) => model.id).join(", ")}`));
42
50
  return modelDefs;
@@ -113,9 +121,7 @@ try {
113
121
  openCodex();
114
122
 
115
123
  console.log(`
116
- ${status("ok", CONFIGURE_CLAUDE_DESKTOP
117
- ? "Ready. Codex, Claude Code, and Claude App are using your GitHub Copilot subscription."
118
- : "Ready. Codex and Claude Code are using your GitHub Copilot subscription. Claude App support is available with --configure-claude-desktop.")}
124
+ ${status("ok", "Ready, Claude Code, Claude App and Codex App are now ready to use")}
119
125
 
120
126
  Adapter: http://${ADAPTER_HOST}:${ADAPTER_PORT}
121
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
5
5
  "type": "module",
6
6
  "bin": {
package/src/adapter.mjs CHANGED
@@ -750,8 +750,18 @@ export function startAdapter(port = 2026, host = "127.0.0.1", options = {}) {
750
750
  socket.destroy();
751
751
  });
752
752
 
753
- return new Promise((resolve) => {
753
+ return new Promise((resolve, reject) => {
754
+ const onListenError = (e) => {
755
+ if (e?.code === "EADDRINUSE") {
756
+ reject(new Error(`Adapter address http://${host}:${port} is already in use. Stop the existing codex-copilot-dx process or set ADAPTER_PORT to another port.`));
757
+ return;
758
+ }
759
+ reject(e);
760
+ };
761
+
762
+ server.once("error", onListenError);
754
763
  server.listen(port, host, () => {
764
+ server.off("error", onListenError);
755
765
  const actualPort = server.address()?.port || port;
756
766
  console.log(status("ok", `Adapter listening on http://${host}:${actualPort}`));
757
767
  resolve(server);
package/src/models.mjs CHANGED
@@ -31,6 +31,9 @@ const DEFAULT_CLAUDE_DESKTOP_MODEL_DEFS = [
31
31
  maxInputTokens: 200000,
32
32
  maxOutputTokens: 8192,
33
33
  },
34
+ ];
35
+
36
+ const DEFAULT_CLAUDE_MODEL_ALIAS_DEFS = [
34
37
  {
35
38
  id: "claude-sonnet-4-6",
36
39
  upstream: "claude-sonnet-4.6",
@@ -94,6 +97,29 @@ function hasMessagesEndpoint(model) {
94
97
  return endpoints.includes("/v1/messages") || endpoints.includes("/chat/completions");
95
98
  }
96
99
 
100
+ function hasOpenAIEndpoint(model) {
101
+ const endpoints = Array.isArray(model?.supported_endpoints) ? model.supported_endpoints : [];
102
+ return endpoints.includes("/responses") || endpoints.includes("/v1/responses") || endpoints.includes("/chat/completions");
103
+ }
104
+
105
+ function copilotModelData(models) {
106
+ const data = Array.isArray(models) ? models : models?.data;
107
+ return Array.isArray(data) ? data : [];
108
+ }
109
+
110
+ function uniqueIds(models, predicate) {
111
+ const ids = [];
112
+ const seen = new Set();
113
+ for (const model of models) {
114
+ if (!predicate(model)) continue;
115
+ const id = String(model.id || "").trim();
116
+ if (!id || seen.has(id)) continue;
117
+ ids.push(id);
118
+ seen.add(id);
119
+ }
120
+ return ids;
121
+ }
122
+
97
123
  function isClaudeCopilotModel(model) {
98
124
  const id = String(model?.id || "").trim();
99
125
  const vendor = String(model?.vendor || "").toLowerCase();
@@ -103,9 +129,17 @@ function isClaudeCopilotModel(model) {
103
129
  && hasMessagesEndpoint(model);
104
130
  }
105
131
 
132
+ export function gptModelIdsFromCopilotModels(models) {
133
+ return uniqueIds(copilotModelData(models), (model) => {
134
+ const id = String(model?.id || "").trim();
135
+ return id.startsWith("gpt-")
136
+ && model?.model_picker_enabled !== false
137
+ && hasOpenAIEndpoint(model);
138
+ });
139
+ }
140
+
106
141
  export function claudeDesktopModelDefsFromCopilotModels(models) {
107
- const data = Array.isArray(models) ? models : models?.data;
108
- if (!Array.isArray(data)) return [];
142
+ const data = copilotModelData(models);
109
143
 
110
144
  const defs = [];
111
145
  const seen = new Set();
@@ -126,19 +160,6 @@ export function claudeDesktopModelDefsFromCopilotModels(models) {
126
160
  });
127
161
  }
128
162
 
129
- const upstreamIds = new Set(defs.map((model) => model.upstream));
130
- for (const alias of DEFAULT_CLAUDE_DESKTOP_MODEL_DEFS) {
131
- if (alias.id === alias.upstream || seen.has(alias.id) || !upstreamIds.has(alias.upstream)) continue;
132
- const upstream = defs.find((model) => model.upstream === alias.upstream);
133
- defs.push({
134
- ...alias,
135
- displayName: upstream?.displayName || alias.displayName,
136
- maxInputTokens: upstream?.maxInputTokens || alias.maxInputTokens,
137
- maxOutputTokens: upstream?.maxOutputTokens || alias.maxOutputTokens,
138
- });
139
- seen.add(alias.id);
140
- }
141
-
142
163
  return defs;
143
164
  }
144
165
 
@@ -153,9 +174,18 @@ export function claudeDesktopModelIds(env = process.env, options = {}) {
153
174
  return claudeDesktopModelDefs(env, options).map((model) => model.id);
154
175
  }
155
176
 
177
+ function claudeModelResolutionDefs(env = process.env, options = {}) {
178
+ const defs = claudeDesktopModelDefs(env, options);
179
+ if (parseModelAliasEnv(env.CCDX_CLAUDE_MODEL_ALIASES).length) return defs;
180
+
181
+ const upstreamIds = new Set(defs.map((model) => model.upstream));
182
+ const aliases = DEFAULT_CLAUDE_MODEL_ALIAS_DEFS.filter((alias) => upstreamIds.has(alias.upstream));
183
+ return [...defs, ...aliases];
184
+ }
185
+
156
186
  export function resolveAnthropicModel(model, env = process.env, options = {}) {
157
187
  const requestedModel = String(model || "");
158
- const match = claudeDesktopModelDefs(env, options).find((entry) => entry.id === requestedModel);
188
+ const match = claudeModelResolutionDefs(env, options).find((entry) => entry.id === requestedModel);
159
189
  return {
160
190
  requestedModel,
161
191
  upstreamModel: match?.upstream || requestedModel,