@bitkyc08/opencodex 1.9.3 → 1.9.5
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 +16 -4
- package/README.md +17 -9
- package/README.zh-CN.md +15 -4
- package/package.json +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/src/cli.ts +7 -2
- package/src/codex-inject.ts +46 -13
package/README.ko.md
CHANGED
|
@@ -77,8 +77,13 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration"
|
|
|
77
77
|
- **OAuth, API 키, 또는 ChatGPT forward.** xAI / Anthropic / Kimi 계정으로 로그인하거나(토큰은
|
|
78
78
|
자동 갱신됩니다), `codex login`을 forward 하거나, 키를 붙여넣으세요(`${ENV_VARS}` 지원). 18개 프로바이더의
|
|
79
79
|
API 키 카탈로그(**Ollama Cloud** 포함)가 기본 내장되어 있습니다.
|
|
80
|
-
- **Codex에 바로 통합.** `$CODEX_HOME/config.toml`(기본값 `~/.codex/config.toml`)에
|
|
81
|
-
|
|
80
|
+
- **Codex CLI, TUI, App, SDK에 바로 통합.** `$CODEX_HOME/config.toml`(기본값 `~/.codex/config.toml`)에
|
|
81
|
+
`[model_providers.opencodex]` 테이블을 주입하고 공유 Codex 모델 카탈로그를 작성하여 라우팅된 모델이
|
|
82
|
+
Codex 모델 선택기에 표시되게 합니다.
|
|
83
|
+
- **서브에이전트 제어.** `subagentModels` 또는 웹 대시보드에서 최대 5개의 라우팅/네이티브 모델을
|
|
84
|
+
Codex `spawn_agent` 선택기에 우선 노출할 수 있습니다.
|
|
85
|
+
- **기본은 HTTP/SSE, WebSocket은 opt-in.** 프록시는 Responses WebSocket 엔드포인트를 갖고 있지만,
|
|
86
|
+
`"websockets": true`일 때만 `supports_websockets`를 광고합니다.
|
|
82
87
|
- **Sidecar.** OpenAI가 아닌 모델에도 ChatGPT 로그인을 통한 `gpt-5.4-mini`를 사용해 실제 **웹 검색**과
|
|
83
88
|
**이미지 이해** 기능을 제공합니다.
|
|
84
89
|
- 프로바이더, OAuth 로그인, 모델 선택, 요청 로그를 위한 **웹 대시보드**.
|
|
@@ -111,6 +116,7 @@ ocx login <xai|anthropic|kimi> # OAuth login
|
|
|
111
116
|
ocx logout <provider> # remove a stored login
|
|
112
117
|
ocx gui # open the web dashboard
|
|
113
118
|
ocx service <install|start|stop|status|uninstall> # run as a background service
|
|
119
|
+
ocx update # update opencodex to the latest published version
|
|
114
120
|
```
|
|
115
121
|
|
|
116
122
|
## 설정
|
|
@@ -138,15 +144,21 @@ ocx service <install|start|stop|status|uninstall> # run as a background servic
|
|
|
138
144
|
}
|
|
139
145
|
```
|
|
140
146
|
|
|
147
|
+
WebSocket 전송은 기본적으로 꺼져 있습니다. Codex가 HTTP/SSE 대신 Responses WebSocket 경로를 광고하고
|
|
148
|
+
사용하게 하려면 `"websockets": true`를 명시하세요.
|
|
149
|
+
|
|
141
150
|
모든 필드에 대한 자세한 내용은 **[설정 레퍼런스](https://lidge-jun.github.io/opencodex/ko/reference/configuration/)**
|
|
142
151
|
를 참고하세요.
|
|
143
152
|
|
|
144
153
|
## 문서
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
공개 문서 — 설치, 프로바이더, 라우팅, sidecar, Codex 통합, Codex App 모델 선택기,
|
|
156
|
+
CLI/설정 레퍼런스 — 는 [`docs-site/`](./docs-site) 아래의 Astro 사이트이며
|
|
148
157
|
**[lidge-jun.github.io/opencodex](https://lidge-jun.github.io/opencodex/ko/)**에 게시됩니다.
|
|
149
158
|
|
|
159
|
+
유지보수용 source of truth는 [`structure/`](./structure)에 있고, 과거 조사/진단 노트는
|
|
160
|
+
[`docs/`](./docs)에 남아 있습니다.
|
|
161
|
+
|
|
150
162
|
## 개발
|
|
151
163
|
|
|
152
164
|
```bash
|
package/README.md
CHANGED
|
@@ -87,9 +87,13 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration"
|
|
|
87
87
|
- **OAuth, API key, or ChatGPT forward.** Log in with your xAI / Anthropic / Kimi account (tokens
|
|
88
88
|
auto-refresh), forward your `codex login`, or paste a key (`${ENV_VARS}` supported). An 18-provider
|
|
89
89
|
API-key catalog (incl. **Ollama Cloud**) is built in.
|
|
90
|
-
- **Drops into Codex.** Injects a `[model_providers.opencodex]` table into
|
|
91
|
-
`$CODEX_HOME/config.toml` (default `~/.codex/config.toml`) and
|
|
92
|
-
|
|
90
|
+
- **Drops into Codex CLI, TUI, App, and SDK.** Injects a `[model_providers.opencodex]` table into
|
|
91
|
+
`$CODEX_HOME/config.toml` (default `~/.codex/config.toml`) and writes a shared Codex model catalog
|
|
92
|
+
so routed models appear in Codex model pickers.
|
|
93
|
+
- **Subagent control.** Feature up to five routed or native models in Codex's `spawn_agent` picker
|
|
94
|
+
from `subagentModels` or the web dashboard.
|
|
95
|
+
- **HTTP/SSE by default, WebSocket opt-in.** The proxy has a Responses WebSocket endpoint, but it
|
|
96
|
+
only advertises `supports_websockets` when `"websockets": true` is set.
|
|
93
97
|
- **Sidecars.** Give non-OpenAI models real **web search** and **image understanding** via a
|
|
94
98
|
`gpt-5.4-mini` over your ChatGPT login.
|
|
95
99
|
- **Web dashboard** for providers, OAuth login, model selection, and request logs.
|
|
@@ -104,7 +108,7 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration"
|
|
|
104
108
|
| xAI Grok | `openai-chat` | oauth / key |
|
|
105
109
|
| Kimi (Moonshot) | `openai-chat` | oauth / key |
|
|
106
110
|
| Google Gemini | `google` | key |
|
|
107
|
-
| Azure OpenAI | `azure
|
|
111
|
+
| Azure OpenAI | `azure` | key |
|
|
108
112
|
| Ollama Cloud + 17-provider catalog | `openai-chat` | key |
|
|
109
113
|
| Ollama / vLLM / LM Studio (local) | `openai-chat` | key (usually blank) |
|
|
110
114
|
| Any OpenAI-compatible endpoint | `openai-chat` | key |
|
|
@@ -150,16 +154,20 @@ Config lives at `~/.opencodex/config.json`. Minimal example:
|
|
|
150
154
|
}
|
|
151
155
|
```
|
|
152
156
|
|
|
157
|
+
WebSocket transport is off by default. Set `"websockets": true` only if you want Codex to advertise
|
|
158
|
+
and use the Responses WebSocket path instead of HTTP/SSE.
|
|
159
|
+
|
|
153
160
|
See the **[Configuration reference](https://lidge-jun.github.io/opencodex/reference/configuration/)**
|
|
154
161
|
for every field.
|
|
155
162
|
|
|
156
163
|
## Documentation
|
|
157
164
|
|
|
158
|
-
The
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
The public docs — install, providers, routing, sidecars, Codex integration, Codex App model picker,
|
|
166
|
+
and CLI/config reference — are built from [`docs-site/`](./docs-site) and published to
|
|
167
|
+
**[lidge-jun.github.io/opencodex](https://lidge-jun.github.io/opencodex/)**.
|
|
168
|
+
|
|
169
|
+
Maintainer source-of-truth notes live under [`structure/`](./structure). Historical investigations
|
|
170
|
+
remain under [`docs/`](./docs).
|
|
163
171
|
|
|
164
172
|
## Development
|
|
165
173
|
|
package/README.zh-CN.md
CHANGED
|
@@ -77,8 +77,13 @@ codex -m "ollama-cloud/glm-5.2" "Write a SQL migration"
|
|
|
77
77
|
- **OAuth、API key 或 ChatGPT 转发(forward)。** 用你的 xAI / Anthropic / Kimi 账户登录(token
|
|
78
78
|
自动刷新)、转发你的 `codex login`,或直接粘贴一个 key(支持 `${ENV_VARS}`)。内置一份 18 家 provider 的
|
|
79
79
|
API-key 目录(含 **Ollama Cloud**)。
|
|
80
|
-
- **无缝接入 Codex。** 向 `$CODEX_HOME/config.toml`(默认 `~/.codex/config.toml
|
|
81
|
-
|
|
80
|
+
- **无缝接入 Codex CLI、TUI、App 和 SDK。** 向 `$CODEX_HOME/config.toml`(默认 `~/.codex/config.toml`)
|
|
81
|
+
注入一个 `[model_providers.opencodex]` 表,并写入共享的 Codex 模型目录,让已路由的模型出现在
|
|
82
|
+
Codex 模型选择器中。
|
|
83
|
+
- **Subagent 控制。** 通过 `subagentModels` 或 Web 仪表盘,将最多 5 个路由或原生模型优先展示在
|
|
84
|
+
Codex 的 `spawn_agent` 选择器中。
|
|
85
|
+
- **默认 HTTP/SSE,WebSocket 需要显式开启。** 代理有 Responses WebSocket 端点,但只有设置
|
|
86
|
+
`"websockets": true` 时才会广告 `supports_websockets`。
|
|
82
87
|
- **Sidecars。** 通过基于你 ChatGPT 登录的 `gpt-5.4-mini`,为非 OpenAI 模型提供真正的**网页搜索**和**图像理解**能力。
|
|
83
88
|
- **Web 仪表盘**,用于管理 provider、OAuth 登录、模型选择和请求日志。
|
|
84
89
|
|
|
@@ -110,6 +115,7 @@ ocx login <xai|anthropic|kimi> # OAuth login
|
|
|
110
115
|
ocx logout <provider> # remove a stored login
|
|
111
116
|
ocx gui # open the web dashboard
|
|
112
117
|
ocx service <install|start|stop|status|uninstall> # run as a background service
|
|
118
|
+
ocx update # update opencodex to the latest published version
|
|
113
119
|
```
|
|
114
120
|
|
|
115
121
|
## 配置
|
|
@@ -137,14 +143,19 @@ ocx service <install|start|stop|status|uninstall> # run as a background servic
|
|
|
137
143
|
}
|
|
138
144
|
```
|
|
139
145
|
|
|
146
|
+
WebSocket 传输默认关闭。只有当你希望 Codex 广告并使用 Responses WebSocket 路径而不是 HTTP/SSE 时,
|
|
147
|
+
才需要设置 `"websockets": true`。
|
|
148
|
+
|
|
140
149
|
每个字段的说明请参阅 **[配置参考](https://lidge-jun.github.io/opencodex/zh-cn/reference/configuration/)**。
|
|
141
150
|
|
|
142
151
|
## 文档
|
|
143
152
|
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
公开文档 —— 安装、providers、路由、sidecars、Codex 集成、Codex App 模型选择器,
|
|
154
|
+
以及 CLI/配置参考 —— 是位于 [`docs-site/`](./docs-site) 下的一个 Astro 站点,
|
|
146
155
|
并发布于 **[lidge-jun.github.io/opencodex](https://lidge-jun.github.io/opencodex/zh-cn/)**。
|
|
147
156
|
|
|
157
|
+
维护者 source of truth 位于 [`structure/`](./structure),历史调查/诊断笔记保留在 [`docs/`](./docs)。
|
|
158
|
+
|
|
148
159
|
## 开发
|
|
149
160
|
|
|
150
161
|
```bash
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
3
4
|
import { restoreNativeCodex } from "./codex-inject";
|
|
4
5
|
import { loadConfig, readPid, removePid, writePid } from "./config";
|
|
5
6
|
import { serviceCommand } from "./service";
|
|
@@ -35,18 +36,22 @@ Examples:
|
|
|
35
36
|
async function syncModelsToCodex(port?: number) {
|
|
36
37
|
const config = loadConfig();
|
|
37
38
|
const p = port ?? config.port ?? 10100;
|
|
38
|
-
|
|
39
|
-
const result = await injectCodexConfig(p, config);
|
|
39
|
+
let catalogPath: string | null | undefined;
|
|
40
40
|
try {
|
|
41
41
|
const { invalidateCodexModelsCache, syncCatalogModels } = await import("./codex-catalog");
|
|
42
42
|
const cat = await syncCatalogModels(config);
|
|
43
|
+
catalogPath = existsSync(cat.path) ? cat.path : null;
|
|
43
44
|
if (cat.added > 0) {
|
|
44
45
|
invalidateCodexModelsCache();
|
|
45
46
|
console.log(` + ${cat.added} models appended to Codex catalog (${cat.path})`);
|
|
47
|
+
} else if (catalogPath === null) {
|
|
48
|
+
console.error("catalog sync skipped: no Codex catalog source found; keeping Codex's native catalog.");
|
|
46
49
|
}
|
|
47
50
|
} catch (e) {
|
|
48
51
|
console.error("catalog sync skipped:", e instanceof Error ? e.message : String(e));
|
|
49
52
|
}
|
|
53
|
+
const { injectCodexConfig } = await import("./codex-inject");
|
|
54
|
+
const result = await injectCodexConfig(p, config, { catalogPath });
|
|
50
55
|
console.log(result.message);
|
|
51
56
|
return result;
|
|
52
57
|
}
|
package/src/codex-inject.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { atomicWriteFile, websocketsEnabled } from "./config";
|
|
3
3
|
import { restoreCodexCatalog } from "./codex-catalog";
|
|
4
|
-
import { CODEX_CONFIG_PATH, CODEX_PROFILE_PATH, DEFAULT_CATALOG_PATH, parseTomlString, readRootTomlString, tomlString } from "./codex-paths";
|
|
4
|
+
import { CODEX_CONFIG_PATH, CODEX_PROFILE_PATH, DEFAULT_CATALOG_PATH, parseTomlString, readRootTomlString, resolveCodexConfigPath, tomlString } from "./codex-paths";
|
|
5
5
|
import type { OcxConfig } from "./types";
|
|
6
6
|
|
|
7
7
|
const OCX_SECTION_MARKER = "# Auto-injected by opencodex";
|
|
8
8
|
|
|
9
|
+
export interface InjectCodexOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Absolute or CODEX_HOME-relative catalog path to advertise to Codex. Pass `null` only when the
|
|
12
|
+
* opencodex catalog could not be materialized; Codex will then keep its native catalog instead of
|
|
13
|
+
* failing on a missing model_catalog_json file.
|
|
14
|
+
*/
|
|
15
|
+
catalogPath?: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
/**
|
|
10
19
|
* The `[model_providers.opencodex]` TABLE only. A table is position-independent in TOML, so it is
|
|
11
20
|
* safe to append at EOF. The bare root key `model_provider = "opencodex"` is NOT included here —
|
|
@@ -83,10 +92,20 @@ function readRootModelCatalogPath(content: string): string | null {
|
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
function setRootModelCatalogPath(content: string, catalogPath: string): string {
|
|
86
|
-
if (readRootModelCatalogPath(content)) return content;
|
|
87
95
|
const lines = content.split("\n");
|
|
88
96
|
const firstTable = lines.findIndex(l => /^\s*\[/.test(l));
|
|
89
97
|
const key = `model_catalog_json = ${tomlString(catalogPath)}`;
|
|
98
|
+
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
99
|
+
for (let i = 0; i < rootEnd; i++) {
|
|
100
|
+
const m = lines[i].match(/^\s*model_catalog_json\s*=\s*("(?:\\.|[^"])*"|'[^']*')\s*$/);
|
|
101
|
+
if (!m) continue;
|
|
102
|
+
const existing = parseTomlString(m[1]);
|
|
103
|
+
if (isOpencodexCatalogPath(existing)) {
|
|
104
|
+
lines[i] = key;
|
|
105
|
+
return lines.join("\n");
|
|
106
|
+
}
|
|
107
|
+
return content;
|
|
108
|
+
}
|
|
90
109
|
if (firstTable === -1) {
|
|
91
110
|
return content.replace(/\n+$/, "") + "\n" + key + "\n";
|
|
92
111
|
}
|
|
@@ -157,20 +176,30 @@ function stripOpencodexCatalogPath(content: string): string {
|
|
|
157
176
|
.join("\n");
|
|
158
177
|
}
|
|
159
178
|
|
|
160
|
-
function buildProfileFile(port: number, catalogPath
|
|
161
|
-
|
|
179
|
+
export function buildProfileFile(port: number, catalogPath?: string | null): string {
|
|
180
|
+
const lines = [
|
|
162
181
|
"# OpenCodex proxy profile — use with: codex --profile opencodex",
|
|
163
182
|
`# Routes all model requests through the opencodex proxy at localhost:${port}`,
|
|
164
183
|
'model_provider = "opencodex"',
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
184
|
+
];
|
|
185
|
+
if (catalogPath) lines.push(`model_catalog_json = ${tomlString(catalogPath)}`);
|
|
186
|
+
lines.push("", "[features]", "fast_mode = true", "");
|
|
187
|
+
return lines.join("\n");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function chooseCatalogPathForInjection(content: string, requested?: string | null): string | null {
|
|
191
|
+
if (requested !== undefined) return requested;
|
|
192
|
+
|
|
193
|
+
const existing = readRootModelCatalogPath(content);
|
|
194
|
+
if (existing) {
|
|
195
|
+
const resolved = resolveCodexConfigPath(existing);
|
|
196
|
+
if (!isOpencodexCatalogPath(resolved) || existsSync(resolved)) return existing;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return existsSync(DEFAULT_CATALOG_PATH) ? DEFAULT_CATALOG_PATH : null;
|
|
171
200
|
}
|
|
172
201
|
|
|
173
|
-
export async function injectCodexConfig(port: number, config?: OcxConfig): Promise<{ success: boolean; message: string }> {
|
|
202
|
+
export async function injectCodexConfig(port: number, config?: OcxConfig, options: InjectCodexOptions = {}): Promise<{ success: boolean; message: string }> {
|
|
174
203
|
if (!existsSync(CODEX_CONFIG_PATH)) {
|
|
175
204
|
return { success: false, message: `Codex config not found at ${CODEX_CONFIG_PATH}. Is Codex installed?` };
|
|
176
205
|
}
|
|
@@ -189,8 +218,8 @@ export async function injectCodexConfig(port: number, config?: OcxConfig): Promi
|
|
|
189
218
|
content = normalizeServiceTier(content);
|
|
190
219
|
content = ensureFastModeFeature(content);
|
|
191
220
|
|
|
192
|
-
const catalogPath =
|
|
193
|
-
content = setRootModelCatalogPath(content, catalogPath);
|
|
221
|
+
const catalogPath = chooseCatalogPathForInjection(content, options.catalogPath);
|
|
222
|
+
content = catalogPath ? setRootModelCatalogPath(content, catalogPath) : stripOpencodexCatalogPath(content);
|
|
194
223
|
|
|
195
224
|
// 1) Root key BEFORE the first table header (must be a global, not nested under a table).
|
|
196
225
|
content = setRootModelProvider(content);
|
|
@@ -200,9 +229,13 @@ export async function injectCodexConfig(port: number, config?: OcxConfig): Promi
|
|
|
200
229
|
writeFileSync(CODEX_CONFIG_PATH, content, "utf-8");
|
|
201
230
|
writeFileSync(CODEX_PROFILE_PATH, buildProfileFile(port, catalogPath), "utf-8");
|
|
202
231
|
|
|
232
|
+
const catalogMessage = catalogPath
|
|
233
|
+
? ` Codex model catalog: ${catalogPath}\n`
|
|
234
|
+
: ` Codex model catalog not injected because no opencodex catalog file exists yet.\n`;
|
|
203
235
|
return {
|
|
204
236
|
success: true,
|
|
205
237
|
message: `Injected opencodex as default provider into Codex config.\n` +
|
|
238
|
+
catalogMessage +
|
|
206
239
|
` All models now route through opencodex proxy (like OpenRouter).\n` +
|
|
207
240
|
` OpenAI models (gpt-5.5, etc.) are passed through to OpenAI.\n` +
|
|
208
241
|
` Custom models route to their configured providers.\n` +
|