@bitkyc08/opencodex 2.1.5 → 2.1.6

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.
@@ -2,7 +2,8 @@ import * as readline from "node:readline";
2
2
  import { openUrl } from "../open-url";
3
3
  import { loadConfig, readPid, saveConfig } from "../config";
4
4
  import { OAUTH_PROVIDERS, runLogin } from "./index";
5
- import { KEY_LOGIN_PROVIDERS, isKeyLoginProvider, validateApiKey } from "./key-providers";
5
+ import { KEY_LOGIN_PROVIDERS, isKeyLoginProvider, validateApiKey, type KeyLoginProvider } from "./key-providers";
6
+ import type { OcxProviderConfig } from "../types";
6
7
 
7
8
  /** Push the new provider into a running proxy's live config so it routes without a restart. */
8
9
  async function notifyRunningProxy(name: string, provider: unknown): Promise<void> {
@@ -51,6 +52,28 @@ async function handleOAuthLogin(name: string): Promise<void> {
51
52
  console.log(`\n✅ Logged in to ${name}. Try: ocx sync`);
52
53
  }
53
54
 
55
+ export function providerConfigFromKeyLoginProvider(def: KeyLoginProvider, key: string): OcxProviderConfig {
56
+ return {
57
+ adapter: def.adapter,
58
+ baseUrl: def.baseUrl,
59
+ apiKey: key,
60
+ ...(def.defaultModel ? { defaultModel: def.defaultModel } : {}),
61
+ ...(def.models ? { models: [...def.models] } : {}),
62
+ ...(def.reasoningEfforts ? { reasoningEfforts: [...def.reasoningEfforts] } : {}),
63
+ ...(def.modelReasoningEfforts ? { modelReasoningEfforts: cloneRecordOfArrays(def.modelReasoningEfforts) } : {}),
64
+ ...(def.reasoningEffortMap ? { reasoningEffortMap: { ...def.reasoningEffortMap } } : {}),
65
+ ...(def.modelReasoningEffortMap ? { modelReasoningEffortMap: cloneNestedRecord(def.modelReasoningEffortMap) } : {}),
66
+ ...(def.noVisionModels ? { noVisionModels: [...def.noVisionModels] } : {}),
67
+ ...(def.noReasoningModels ? { noReasoningModels: [...def.noReasoningModels] } : {}),
68
+ ...(def.noTemperatureModels ? { noTemperatureModels: [...def.noTemperatureModels] } : {}),
69
+ ...(def.noTopPModels ? { noTopPModels: [...def.noTopPModels] } : {}),
70
+ ...(def.noPenaltyModels ? { noPenaltyModels: [...def.noPenaltyModels] } : {}),
71
+ ...(def.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...def.autoToolChoiceOnlyModels] } : {}),
72
+ ...(def.preserveReasoningContentModels ? { preserveReasoningContentModels: [...def.preserveReasoningContentModels] } : {}),
73
+ ...(def.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: def.escapeBuiltinToolNames } : {}),
74
+ };
75
+ }
76
+
54
77
  async function handleKeyLogin(name: string): Promise<void> {
55
78
  const def = KEY_LOGIN_PROVIDERS[name];
56
79
  console.log(`\n🔑 ${def.label} — opening ${def.dashboardUrl} so you can create/copy an API key...`);
@@ -63,22 +86,24 @@ async function handleKeyLogin(name: string): Promise<void> {
63
86
  process.exit(1);
64
87
  }
65
88
  process.stdout.write(" validating… ");
66
- const valid = await validateApiKey(def.baseUrl, key);
89
+ const valid = await validateApiKey(def, key);
67
90
  console.log(valid === true ? "valid ✅" : valid === false ? "INVALID ❌" : "couldn't validate (may still work)");
68
91
  if (valid === false) {
69
92
  console.error("Provider rejected the key. Not saved.");
70
93
  process.exit(1);
71
94
  }
72
- const provider = {
73
- adapter: def.adapter,
74
- baseUrl: def.baseUrl,
75
- apiKey: key,
76
- ...(def.defaultModel ? { defaultModel: def.defaultModel } : {}),
77
- ...(def.models ? { models: def.models } : {}),
78
- };
95
+ const provider = providerConfigFromKeyLoginProvider(def, key);
79
96
  const config = loadConfig();
80
97
  config.providers[name] = provider;
81
98
  saveConfig(config);
82
99
  await notifyRunningProxy(name, provider);
83
100
  console.log(`✅ ${def.label} added. Try: ocx sync`);
84
101
  }
102
+
103
+ function cloneRecordOfArrays(input: Record<string, string[]>): Record<string, string[]> {
104
+ return Object.fromEntries(Object.entries(input).map(([key, value]) => [key, [...value]]));
105
+ }
106
+
107
+ function cloneNestedRecord(input: Record<string, Record<string, string>>): Record<string, Record<string, string>> {
108
+ return Object.fromEntries(Object.entries(input).map(([key, value]) => [key, { ...value }]));
109
+ }
@@ -19,6 +19,7 @@ export interface DerivedKeyLoginProvider {
19
19
  noPenaltyModels?: string[];
20
20
  autoToolChoiceOnlyModels?: string[];
21
21
  preserveReasoningContentModels?: string[];
22
+ escapeBuiltinToolNames?: boolean;
22
23
  }
23
24
 
24
25
  export interface DerivedInitProvider {
@@ -73,6 +74,7 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
73
74
  ...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
74
75
  ...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
75
76
  ...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
77
+ ...(entry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: entry.escapeBuiltinToolNames } : {}),
76
78
  };
77
79
  }
78
80
 
@@ -99,6 +101,7 @@ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
99
101
  ...(entry.noPenaltyModels ? { noPenaltyModels: [...entry.noPenaltyModels] } : {}),
100
102
  ...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
101
103
  ...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
104
+ ...(entry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: entry.escapeBuiltinToolNames } : {}),
102
105
  };
103
106
  }
104
107
  return out;
@@ -25,6 +25,7 @@ export interface ProviderRegistryEntry {
25
25
  noPenaltyModels?: string[];
26
26
  autoToolChoiceOnlyModels?: string[];
27
27
  preserveReasoningContentModels?: string[];
28
+ escapeBuiltinToolNames?: boolean;
28
29
  oauthId?: string;
29
30
  jawcodeBundle?: string;
30
31
  extraMetadataAliases?: string[];
@@ -36,7 +37,7 @@ export type ProviderConfigSeed = Pick<
36
37
  "adapter" | "baseUrl" | "authMode" | "defaultModel" | "models"
37
38
  | "reasoningEfforts" | "modelReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap"
38
39
  | "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noPenaltyModels"
39
- | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels"
40
+ | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "escapeBuiltinToolNames"
40
41
  >;
41
42
 
42
43
 
@@ -58,6 +59,27 @@ const NEURALWATT_REASONING_HISTORY_MODELS = [
58
59
  "moonshotai/Kimi-K2.5", "kimi-k2.6", "kimi-k2.7-code",
59
60
  "qwen3.5-397b", "qwen3.6-35b",
60
61
  ];
62
+ const UMANS_MODELS = [
63
+ "umans-coder",
64
+ "umans-kimi-k2.7",
65
+ "umans-kimi-k2.6",
66
+ "umans-flash",
67
+ "umans-glm-5.2",
68
+ "umans-glm-5.1",
69
+ "umans-qwen3.6-35b-a3b",
70
+ ];
71
+ const UMANS_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
72
+ const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh"];
73
+ const UMANS_GLM_REASONING_MAP: Record<string, string> = {
74
+ none: "high",
75
+ minimal: "high",
76
+ low: "high",
77
+ medium: "high",
78
+ high: "high",
79
+ xhigh: "max",
80
+ max: "max",
81
+ };
82
+ const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.2", "umans-glm-5.1"];
61
83
 
62
84
  export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
63
85
  {
@@ -119,6 +141,33 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
119
141
  preserveReasoningContentModels: KIMI_THINKING_MODELS,
120
142
  },
121
143
  { id: "openai-apikey", label: "OpenAI (API key)", adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", authKind: "key", featured: true, dashboardUrl: "https://platform.openai.com/api-keys", defaultModel: "gpt-5.5" },
144
+ {
145
+ id: "umans",
146
+ label: "Umans AI Coding Plan",
147
+ adapter: "anthropic",
148
+ baseUrl: "https://api.code.umans.ai",
149
+ authKind: "key",
150
+ featured: true,
151
+ dashboardUrl: "https://app.umans.ai/billing",
152
+ defaultModel: "umans-coder",
153
+ models: UMANS_MODELS,
154
+ note: "Coding plan via Anthropic Messages",
155
+ modelReasoningEfforts: {
156
+ "umans-coder": UMANS_REASONING_EFFORTS,
157
+ "umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
158
+ "umans-kimi-k2.6": UMANS_REASONING_EFFORTS,
159
+ "umans-flash": ["low", "medium", "high"],
160
+ "umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
161
+ "umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
162
+ "umans-qwen3.6-35b-a3b": ["low", "medium", "high"],
163
+ },
164
+ modelReasoningEffortMap: {
165
+ "umans-glm-5.2": UMANS_GLM_REASONING_MAP,
166
+ "umans-glm-5.1": UMANS_GLM_REASONING_MAP,
167
+ },
168
+ noVisionModels: UMANS_TEXT_ONLY_MODELS,
169
+ escapeBuiltinToolNames: true,
170
+ },
122
171
  {
123
172
  id: "opencode-go", label: "opencode go", adapter: "openai-chat", baseUrl: "https://opencode.ai/zen/go/v1",
124
173
  authKind: "key", featured: true, dashboardUrl: "https://opencode.ai/auth", defaultModel: "kimi-k2.7-code",
package/src/types.ts CHANGED
@@ -187,6 +187,13 @@ export interface OcxConfig {
187
187
  websockets?: boolean;
188
188
  /** Auto-start/sync the proxy from the Codex shim before launching Codex. Default true. */
189
189
  codexAutoStart?: boolean;
190
+ /**
191
+ * Compatibility mode: temporarily rewrite Codex resume-history metadata while the proxy is active
192
+ * so Codex App can show old OpenAI chats and opencodex-created exec chats under its default
193
+ * interactive-source/provider filters. Disabled by default because it mutates Codex's local
194
+ * thread index; originals are backed up and restored by `ocx stop` / `ocx restore`.
195
+ */
196
+ syncResumeHistory?: boolean;
190
197
  /** Freshness window (ms) for the per-provider live `/models` cache. Defaults to 5 min. */
191
198
  modelCacheTtlMs?: number;
192
199
  /** Web-search sidecar: route web_search for non-OpenAI models through a gpt-mini via ChatGPT passthrough. */
@@ -258,6 +265,8 @@ export interface OcxProviderConfig {
258
265
  autoToolChoiceOnlyModels?: string[];
259
266
  /** Model ids that expect prior assistant `reasoning_content` to be preserved in chat history. */
260
267
  preserveReasoningContentModels?: string[];
268
+ /** Anthropic-compatible gateways that need custom tool names escaped on the wire. */
269
+ escapeBuiltinToolNames?: boolean;
261
270
  /**
262
271
  * Model ids that do NOT accept image inputs. The proxy gives them "eyes" via the vision sidecar:
263
272
  * attached images are described by a gpt vision model and replaced with text before the call.