Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ Codex CLI / App / SDK ──/v1/responses──▶ opencodex ──▶ Any provi
# 설치
npm install -g @bitkyc08/opencodex # 또는: bun install -g @bitkyc08/opencodex

# 대화형 설정 (config 작성 + Codex에 자동 주입)
# 대화형 설정 (config 작성 + Codex 주입 + 자동 시작 shim 설치 선택)
ocx init

# 프록시 시작
ocx start

# init에서 건너뛰었다면 나중에 온디맨드 자동 시작 shim 설치
ocx codex-shim install

# Codex를 평소처럼 사용하세요 — opencodex를 통해 라우팅됩니다
codex "Write a hello world in Rust"
```
Expand Down Expand Up @@ -122,15 +125,17 @@ codex -m "xai/grok-4" "이 PR을 리뷰해 줘"

```bash
ocx init # 대화형 설정
ocx start [--port 10100] # 프록시 시작
ocx start [--port 10100] # 프록시 시작; 포트가 사용 중이면 빈 포트로 자동 전환
ocx stop # 프록시 중지 + Codex 원래 설정 복원
ocx restore # 중지 없이 복원 (별칭: ocx eject)
ocx uninstall # service/shim/config 제거 + Codex 원본 복원
ocx ensure # 필요 시 시작 + Codex config/cache 갱신
ocx sync # 모델 갱신 + Codex에 재주입
ocx status # 프록시 실행 중인지 확인
ocx login <xai|anthropic|kimi> # OAuth 로그인
ocx logout <provider> # 저장된 로그인 정보 삭제
ocx gui # 웹 대시보드 열기
ocx codex-shim install # codex 실행 시 프록시 자동 시작
ocx codex-shim install # codex 실행 시 `ocx ensure` 실행
ocx service <install|start|stop|status|uninstall> # 백그라운드 서비스 (launchd/systemd/schtasks)
ocx update # opencodex를 최신 버전으로 업데이트
```
Expand All @@ -142,12 +147,26 @@ opencodex에는 프록시를 자동 시작하는 두 가지 방법이 있습니
| | `ocx service install` | `ocx codex-shim install` |
|---|---|---|
| **방식** | OS 서비스 관리자 (launchd / systemd / schtasks) | `codex` 바이너리를 래퍼 스크립트로 교체 |
| **시점** | 로그인 후 항상 실행 | 온디맨드 — `codex` 실행 시에만 시작 |
| **시점** | 로그인 후 항상 실행 | 온디맨드 — `codex` 실행 시 `ocx ensure` 실행 |
| **재시작** | 크래시 시 자동 재시작 | `codex` 호출마다 한 번 시작 |
| **Codex 업데이트** | 영향 없음 | `ocx codex-shim install` 또는 `ocx update` 시 복구 |
| **제거** | `ocx service uninstall` | `ocx codex-shim uninstall` |

항상 프록시를 켜두려면 **service** (개발 머신 권장), 가볍게 온디맨드로 쓰려면 **shim**을 사용하세요.
shim 자동 시작은 기본으로 켜져 있으며 GUI 대시보드에서 끌 수 있습니다. 설정된 프록시 포트가 이미 사용
중이면 `ocx start`가 자동으로 다른 빈 로컬 포트를 고르고 Codex 설정도 그 포트로 갱신합니다.

### 삭제

npm/bun 패키지를 지우기 전에 로컬 상태를 먼저 정리하세요:

```bash
ocx uninstall
npm uninstall -g @bitkyc08/opencodex # 또는: bun remove -g @bitkyc08/opencodex
```

`ocx uninstall`은 프록시 중지, 설치된 service 제거, Codex shim 제거, Codex config/catalog/history
원복, `~/.opencodex` 삭제를 처리합니다.

## 설정

Expand Down
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ Requires [Bun](https://bun.sh) 1.1+. All three platforms work natively (no WSL n
# Install
npm install -g @bitkyc08/opencodex # or: bun install -g @bitkyc08/opencodex

# Interactive setup (writes config + injects into Codex)
# Interactive setup (writes config, injects into Codex, and offers autostart shim install)
ocx init

# Start the proxy
ocx start

# If you skipped it during init, install the on-demand autostart shim later
ocx codex-shim install

# Use Codex normally — it now routes through opencodex
codex "Write a hello world in Rust"
```
Expand Down Expand Up @@ -144,11 +147,13 @@ Plus DeepSeek, Groq, OpenRouter, Together, Fireworks, Cerebras, Mistral, Hugging

```bash
ocx init # interactive setup
ocx start [--port 10100] # start the proxy
ocx start [--port 10100] # start the proxy; falls back to a free port if busy
ocx stop # stop + restore native Codex
ocx restore # restore without stopping (alias: ocx eject)
ocx uninstall # remove service/shim/config and restore native Codex
ocx ensure # start if needed + refresh Codex config/cache
ocx sync # refresh models + re-inject into Codex
ocx codex-shim install # auto-start the proxy whenever `codex` is launched
ocx codex-shim install # run `ocx ensure` whenever `codex` is launched
ocx status # is the proxy running?
ocx login <xai|anthropic|kimi> # OAuth login
ocx logout <provider> # remove a stored login
Expand All @@ -164,13 +169,27 @@ opencodex has two ways to auto-start the proxy:
| | `ocx service install` | `ocx codex-shim install` |
|---|---|---|
| **How** | OS service manager (launchd / systemd / schtasks) | Replaces the `codex` binary with a wrapper script |
| **When** | Always running after login | On-demand — starts only when `codex` is launched |
| **When** | Always running after login | On-demand — runs `ocx ensure` when `codex` is launched |
| **Restart** | Auto-restarts on crash | Starts once per `codex` invocation |
| **Codex updates** | Unaffected | Repairs on next `ocx codex-shim install` or `ocx update` |
| **Remove** | `ocx service uninstall` | `ocx codex-shim uninstall` |

Use the **service** for always-on proxy (recommended for development machines). Use the **shim** for
lightweight, on-demand proxy startup without a background daemon.
lightweight, on-demand proxy startup without a background daemon. Shim autostart is enabled by default
and can be disabled from the GUI dashboard. If the configured proxy port is already busy, `ocx start`
automatically picks another free local port and updates Codex to use it.

### Uninstall

Before removing the npm/bun package, clean up local state:

```bash
ocx uninstall
npm uninstall -g @bitkyc08/opencodex # or: bun remove -g @bitkyc08/opencodex
```

`ocx uninstall` stops the proxy, removes any installed service, removes the Codex shim, restores
native Codex config/catalog/history, and deletes `~/.opencodex`.

## Configuration

Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const en = {
"dash.stop": "Stop Proxy",
"dash.stopConfirm": "Stop the proxy and restore native Codex?",
"dash.stopping": "Stopping…",
"dash.codexAutoStart": "Start opencodex with Codex",
"dash.codexAutoStartHint": "When enabled, the Codex shim runs ocx ensure before every Codex CLI/App launch. Disable this to leave Codex untouched.",

// providers
"prov.subtitle": "Configure the upstream providers opencodex routes into Codex. Log in with an account, add a provider, or edit the raw config.",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const ko: Record<TKey, string> = {
"dash.stop": "프록시 중지",
"dash.stopConfirm": "프록시를 중지하고 Codex 원본 설정을 복원할까요?",
"dash.stopping": "중지 중…",
"dash.codexAutoStart": "Codex 실행 시 opencodex 시작",
"dash.codexAutoStartHint": "켜져 있으면 Codex CLI/App 실행 전 shim이 ocx ensure를 실행합니다. 끄면 Codex를 건드리지 않습니다.",

// providers
"prov.subtitle": "opencodex가 Codex로 라우팅하는 업스트림 프로바이더를 설정합니다. 계정으로 로그인하거나, 프로바이더를 추가하거나, 원본 설정을 편집하세요.",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const zh: Record<TKey, string> = {
"dash.stop": "停止代理",
"dash.stopConfirm": "停止代理并恢复原生 Codex 配置?",
"dash.stopping": "正在停止…",
"dash.codexAutoStart": "随 Codex 启动 opencodex",
"dash.codexAutoStartHint": "启用后,Codex shim 会在每次 Codex CLI/App 启动前运行 ocx ensure。关闭后不会改动 Codex。",

// providers
"prov.subtitle": "配置 opencodex 路由到 Codex 的上游提供方。使用账户登录、添加提供方,或编辑原始配置。",
Expand Down
46 changes: 45 additions & 1 deletion gui/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ import { useT, Trans } from "../i18n";
interface HealthData { status: string; version: string; uptime: number }
interface ProviderInfo { name: string; adapter: string; baseUrl: string; defaultModel?: string; hasApiKey: boolean }
interface ModelInfo { id: string; provider: string; owned_by?: string }
interface SettingsData { codexAutoStart: boolean; port: number; hostname: string }

export default function Dashboard({ apiBase }: { apiBase: string }) {
const t = useT();
const [health, setHealth] = useState<HealthData | null>(null);
const [providers, setProviders] = useState<ProviderInfo[]>([]);
const [models, setModels] = useState<ModelInfo[]>([]);
const [settings, setSettings] = useState<SettingsData | null>(null);
const [modelsLoading, setModelsLoading] = useState(false);
const [settingsSaving, setSettingsSaving] = useState(false);
const [error, setError] = useState(false);

useEffect(() => {
const fetchData = async () => {
try {
const [hRes, pRes] = await Promise.all([
const [hRes, pRes, sRes] = await Promise.all([
fetch(`${apiBase}/healthz`),
fetch(`${apiBase}/api/providers`),
fetch(`${apiBase}/api/settings`),
]);
setHealth(await hRes.json());
setProviders(await pRes.json());
setSettings(await sRes.json());
setError(false);
} catch {
setError(true);
Expand Down Expand Up @@ -61,6 +66,27 @@ export default function Dashboard({ apiBase }: { apiBase: string }) {

const online = health?.status === "ok";

const toggleCodexAutoStart = async () => {
if (!settings || settingsSaving) return;
const next = !settings.codexAutoStart;
setSettingsSaving(true);
setSettings({ ...settings, codexAutoStart: next });
try {
const res = await fetch(`${apiBase}/api/settings`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ codexAutoStart: next }),
});
if (!res.ok) throw new Error("save failed");
const data = await res.json();
setSettings(prev => prev ? { ...prev, codexAutoStart: data.codexAutoStart } : prev);
} catch {
setSettings(prev => prev ? { ...prev, codexAutoStart: !next } : prev);
} finally {
setSettingsSaving(false);
}
};

return (
<>
<div className="page-head"><h2>{t("nav.dashboard")}</h2></div>
Expand All @@ -78,6 +104,24 @@ export default function Dashboard({ apiBase }: { apiBase: string }) {
<div className="stat"><div className="label">{t("dash.providers")}</div><div className="value">{providers.length}</div></div>
</div>

<div className="panel" style={{ marginBottom: 24 }}>
<div className="spread">
<div>
<div style={{ fontWeight: 650 }}>{t("dash.codexAutoStart")}</div>
<div className="muted" style={{ fontSize: 13, marginTop: 3 }}>{t("dash.codexAutoStartHint")}</div>
</div>
<button
className={`switch ${settings?.codexAutoStart ?? true ? "on" : ""}`}
onClick={toggleCodexAutoStart}
disabled={!settings || settingsSaving}
aria-label={t("dash.codexAutoStart")}
aria-pressed={settings?.codexAutoStart ?? true}
>
<span className="knob" />
</button>
</div>
</div>

<div className="h-section">{t("dash.activeProviders")} <span className="count">{providers.length}</span></div>
{providers.length === 0 ? (
<div className="empty"><Trans k="dash.noProviders" cmd="ocx init" /></div>
Expand Down
Loading
Loading