@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.
- package/README.ko.md +1 -0
- package/README.md +17 -0
- package/README.zh-CN.md +1 -0
- package/gui/dist/assets/{index-DB2i6w5f.js → index-CBwrJA6W.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +27 -9
- package/src/cli.ts +82 -0
- package/src/codex-history-provider.ts +228 -21
- package/src/codex-inject.ts +28 -6
- package/src/oauth/key-providers.ts +26 -3
- package/src/oauth/login-cli.ts +34 -9
- package/src/providers/derive.ts +3 -0
- package/src/providers/registry.ts +50 -1
- package/src/types.ts +9 -0
package/src/oauth/login-cli.ts
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/src/providers/derive.ts
CHANGED
|
@@ -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.
|