diff --git a/.cursor/rules/no-upstream-pr.mdc b/.cursor/rules/no-upstream-pr.mdc new file mode 100644 index 0000000000..4f03bfb8c7 --- /dev/null +++ b/.cursor/rules/no-upstream-pr.mdc @@ -0,0 +1,24 @@ +--- +description: Fork PRs must target SanHsien origin; upstream PRs need explicit contribute-back consent +alwaysApply: true +--- + +# 禁止對上游開 PR + +本 repo 是 `lidge-jun/opencodex` 的 fork。日常 PR 只能打進 `SanHsien/opencodex`。 + +**根因是機制不是粗心:** `gh` 在 fork clone 的預設 repo 就是上游。 + +```powershell +gh repo set-default SanHsien/opencodex # 每個 clone 先跑一次 +gh repo set-default --view # 必須回 SanHsien/opencodex +gh pr create --repo SanHsien/opencodex --base main --head <分支> +``` + +建完核對 URL 必須是 `https://github.com/SanHsien/opencodex/pull/...`。 + +裸跑 `gh pr create` 會打到上游。2026-08-22 一天內兩個工具各誤開一次(`lidge-jun/opencodex#2373`、 +`hamanpaul/paulsha-cortex#787`),禁止再犯。兩次都是「指令成功了」的錯覺——開錯的 owner 就寫在 +輸出的 URL 裡;批次跑多個 repo 時最容易略過那一眼。 + +**唯一例外:** 使用者在這次對話明確同意回貢上游。fork、建環境、開 PR、比照其他 repo、合併回 main 都不是例外。 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..b45b7c440e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + # 不幫上游 bun.lock / package.json 開 npm Dependabot:上游幾乎每天發版, + # lockfile PR 會跟 merge 持續打架。本線只看 workflow 釘住的 actions。 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "07:00" + timezone: "Asia/Taipei" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "github-actions" + groups: + codeql-action: + patterns: + - "github/codeql-action*" diff --git a/.github/workflows/cleanup-orphaned-workflows.yml b/.github/workflows/cleanup-orphaned-workflows.yml index 3c33c21dc8..0a1b08d94c 100644 --- a/.github/workflows/cleanup-orphaned-workflows.yml +++ b/.github/workflows/cleanup-orphaned-workflows.yml @@ -25,6 +25,7 @@ concurrency: jobs: cleanup: + if: github.repository == 'lidge-jun/opencodex' name: Delete orphaned workflow runs runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..3b2663be00 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,46 @@ +name: CodeQL + +# The proxy handles provider tokens, ChatGPT account-pool credentials, and +# request bodies. Fork overlay tools also parse Markdown and shell out to git. +# Weekly CodeQL asks the questions unit tests do not: injection, secret leak, +# and unsafe workflow interpolation. + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "41 4 * * 6" + workflow_dispatch: + +permissions: + contents: read + security-events: write + +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: JavaScript security scan + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + languages: javascript-typescript + queries: security-extended + + - name: Analyze + uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + category: "/language:javascript-typescript" diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 94c962193a..5ad6726f68 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -19,6 +19,7 @@ concurrency: jobs: build: + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -33,6 +34,7 @@ jobs: deploy: needs: build + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest timeout-minutes: 10 environment: diff --git a/.github/workflows/enforce-issue-quality.yml b/.github/workflows/enforce-issue-quality.yml index a983da5d78..e2d85df962 100644 --- a/.github/workflows/enforce-issue-quality.yml +++ b/.github/workflows/enforce-issue-quality.yml @@ -34,10 +34,11 @@ jobs: translate: name: Translate non-English issues if: > - github.event_name == 'issues' || + github.repository == 'lidge-jun/opencodex' && + (github.event_name == 'issues' || (github.event_name == 'workflow_dispatch' && inputs.backfill_open_areas != true && - inputs.issue_number != '') + inputs.issue_number != '')) runs-on: ubuntu-latest # Serialize per-issue control-state RMW; workflow concurrency still cancels # superseded runs, but this queue avoids interleaved comment upserts. @@ -442,7 +443,7 @@ jobs: translate-comment: name: Translate non-English issue comments - if: github.event_name == 'issue_comment' && github.event.issue.pull_request == null && github.event.comment.user.type != 'Bot' + if: github.repository == 'lidge-jun/opencodex' && github.event_name == 'issue_comment' && github.event.issue.pull_request == null && github.event.comment.user.type != 'Bot' runs-on: ubuntu-latest concurrency: # Shares the per-issue queue with the `translate` job: both jobs RMW the @@ -750,6 +751,7 @@ jobs: # quality closure must not depend on translation success. needs: translate if: > + github.repository == 'lidge-jun/opencodex' && always() && needs.translate.result != 'cancelled' && (github.event_name == 'issues' || @@ -1187,7 +1189,7 @@ jobs: backfill-open-areas: name: Backfill open issue area labels - if: github.event_name == 'workflow_dispatch' && inputs.backfill_open_areas == true + if: github.repository == 'lidge-jun/opencodex' && github.event_name == 'workflow_dispatch' && inputs.backfill_open_areas == true runs-on: ubuntu-latest permissions: # Read-only checkout of trusted scripts from the default branch. diff --git a/.github/workflows/enforce-pr-target.yml b/.github/workflows/enforce-pr-target.yml index a26ff02273..495a6c0ace 100644 --- a/.github/workflows/enforce-pr-target.yml +++ b/.github/workflows/enforce-pr-target.yml @@ -31,7 +31,8 @@ jobs: # before the write-capable job starts. The PR number is the stable identity # used by both gate writers even when a contributor pushes a new head SHA. if: >- - (github.event_name == 'status' && + github.repository == 'lidge-jun/opencodex' && + ((github.event_name == 'status' && github.event.context == 'CodeRabbit' && github.event.state == 'success' && github.event.sender.login == 'coderabbitai[bot]' && @@ -44,7 +45,7 @@ jobs: github.event.label.name == 'test-exception-approved' || github.event.label.name == 'suppression-approved' || github.event.label.name == 'generated-change-approved' || - github.event.label.name == 'dependency-change-approved')) + github.event.label.name == 'dependency-change-approved'))) runs-on: ubuntu-latest permissions: contents: read @@ -138,7 +139,7 @@ jobs: enforce-target: needs: resolve-pr - if: needs.resolve-pr.outputs.pull-number != '' + if: github.repository == 'lidge-jun/opencodex' && needs.resolve-pr.outputs.pull-number != '' runs-on: ubuntu-latest # Job-scoped permissions replace, rather than extend, the workflow default. permissions: diff --git a/.github/workflows/fork-maintenance.yml b/.github/workflows/fork-maintenance.yml new file mode 100644 index 0000000000..c4ddf90449 --- /dev/null +++ b/.github/workflows/fork-maintenance.yml @@ -0,0 +1,61 @@ +name: Fork maintenance + +on: + push: + branches: [main] + paths: + - FORK.md + - NOTICE.md + - CLAUDE.md + - SKILL.md + - REVIEW.md + - AGENTS.md + - docs/fork/** + - tools/** + - tests/fork-hygiene.test.ts + - .github/workflows/fork-maintenance.yml + - .github/workflows/upstream-check.yml + - .github/workflows/codeql.yml + - .github/workflows/*.yml + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: fork-maintenance-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: fork gate (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup project Bun + uses: ./.github/actions/setup-project-bun + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Canonical fork gate + if: runner.os == 'Windows' + shell: pwsh + run: pwsh -NoProfile -File tools/dev_check.ps1 + + - name: Canonical fork gate + if: runner.os != 'Windows' + run: | + bun test tests/fork-hygiene.test.ts + bun tools/check-links.ts diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index c3485054b7..cffd6da5a0 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -10,6 +10,7 @@ concurrency: jobs: find-duplicates: + if: github.repository == 'lidge-jun/opencodex' name: Find similar issues runs-on: ubuntu-latest permissions: @@ -161,7 +162,7 @@ jobs: post-duplicates: name: Post duplicate result needs: find-duplicates - if: needs.find-duplicates.outputs.matches + if: github.repository == 'lidge-jun/opencodex' && needs.find-duplicates.outputs.matches runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/pr-hygiene.yml b/.github/workflows/pr-hygiene.yml index 8d6cb3b1a4..6f0d45b16c 100644 --- a/.github/workflows/pr-hygiene.yml +++ b/.github/workflows/pr-hygiene.yml @@ -19,6 +19,7 @@ concurrency: jobs: hygiene: + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest # contents: read for the trusted script checkout; issues/pull-requests write # maintain the blocked label and one bot comment. diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 309fd40b8f..04c8939fbf 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -29,6 +29,7 @@ permissions: jobs: label: + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest steps: - name: Checkout labeler script (default-branch trusted code) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31ede9ab9d..bc91991ec7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,7 @@ concurrency: jobs: validate-dispatch: + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest permissions: contents: read @@ -71,6 +72,7 @@ jobs: NODE publish: needs: validate-dispatch + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest timeout-minutes: 15 permissions: diff --git a/.github/workflows/service-lifecycle.yml b/.github/workflows/service-lifecycle.yml index 8e0513b459..0ab45434e2 100644 --- a/.github/workflows/service-lifecycle.yml +++ b/.github/workflows/service-lifecycle.yml @@ -35,6 +35,7 @@ concurrency: jobs: linux-systemd: + if: github.repository == 'lidge-jun/opencodex' runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -155,6 +156,7 @@ jobs: fi macos-launchd: + if: github.repository == 'lidge-jun/opencodex' runs-on: macos-latest timeout-minutes: 10 steps: @@ -233,6 +235,7 @@ jobs: fi windows-schtasks: + if: github.repository == 'lidge-jun/opencodex' runs-on: windows-latest timeout-minutes: 10 steps: diff --git a/.github/workflows/stale-needs-info.yml b/.github/workflows/stale-needs-info.yml index d33fb204a5..3ec7e5e438 100644 --- a/.github/workflows/stale-needs-info.yml +++ b/.github/workflows/stale-needs-info.yml @@ -23,6 +23,7 @@ concurrency: jobs: stale: + if: github.repository == 'lidge-jun/opencodex' name: Stale needs-info issues runs-on: ubuntu-latest steps: diff --git a/.github/workflows/upstream-check.yml b/.github/workflows/upstream-check.yml new file mode 100644 index 0000000000..052155ed1f --- /dev/null +++ b/.github/workflows/upstream-check.yml @@ -0,0 +1,49 @@ +name: Upstream check + +on: + push: + branches: [main] + paths: + - tools/check-upstream-updates.ts + - tools/upstream_baseline.json + - .github/workflows/upstream-check.yml + schedule: + - cron: "0 3 * * 1" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: upstream-check + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Check out full history + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup project Bun + uses: ./.github/actions/setup-project-bun + + - name: Check upstream + id: upstream + continue-on-error: true + run: bun tools/check-upstream-updates.ts --strict + + - name: Publish report + if: always() + run: cat upstream-review-report.md >> "$GITHUB_STEP_SUMMARY" + + - name: Require review when upstream changed + if: steps.upstream.outcome == 'failure' + run: | + echo "::error::Upstream changed or the check failed. Review the workflow summary." + exit 1 diff --git a/.gitignore b/.gitignore index 19ab5348b8..270ce7a73e 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ tests/.tmp-* # `git add` three separate times and reached `dev` once — see # tests/repo-hygiene.test.ts, which fails if any path here becomes tracked again. go/ + +# SanHsien fork generated reports +upstream-review-report.md diff --git a/.npmignore b/.npmignore index acf3a0c4d0..ced4cf9281 100644 --- a/.npmignore +++ b/.npmignore @@ -3,7 +3,14 @@ # are explicit guards so dev-only content never leaks into the published tarball. devlog/ docs-site/ +docs/fork/ .github/ +tools/ +FORK.md +NOTICE.md +CLAUDE.md +SKILL.md +REVIEW.md .codex/ # GUI: ship only the built gui/dist (via "files"); never the source/deps/config. diff --git a/AGENTS.md b/AGENTS.md index 8b0fcf01e3..32ba271675 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,10 @@ # AGENTS.md +> **SanHsien 維護型 fork overlay。** `origin` 是 [`SanHsien/opencodex`](https://github.com/SanHsien/opencodex),`upstream` 是 [`lidge-jun/opencodex`](https://github.com/lidge-jun/opencodex)。 +> 本 fork 的維護規則以 [`FORK.md`](FORK.md) 為準;與下文衝突時以 FORK.md 優先。 +> 不要推 `upstream`、不要對上游開 PR(除非維護者在這次對話明確同意回貢)、不要在本 fork 發 npm、不要部署 `docs-site` 到 GitHub Pages。 +> 產品行為(proxy、帳號池、provider、測試)仍遵守下文上游規則。 + Guidance for AI agents (and humans) working on or reviewing this repository. ## What this project is @@ -24,6 +29,7 @@ Bun-native TypeScript with no separate server compile step. release authority. - `devlog/` — planning and investigation notes, tracked in this repository. See "The `devlog` directory" below for what may and may not go there. +- `FORK.md` / `docs/fork/` — SanHsien maintenance overlay (not upstream product docs). Read the nearest nested `AGENTS.md` before changing files in a scoped directory (`src/`, `gui/`, `docs-site/`, `scripts/`, `.github/`). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..785c9a3496 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,12 @@ +# CLAUDE.md + +請先完整閱讀並遵守 [`FORK.md`](FORK.md)。產品規則見上游內容為主的 [`AGENTS.md`](AGENTS.md);衝突時以 FORK.md 為準。本檔只補充 Claude Code 的最小入口: + +- 這是保留上游歷史的 fork;不要移除 `upstream`、原作者或 MIT 授權標示。 +- `README.md` 以繁體中文為主;英文在 `README.en.md`。不要改寫成維護索引。 +- 不要在本 fork 跑 npm publish、不要部署 GitHub Pages、不要把 fork-only 檔案送進上游。 +- 開 PR 必須 `gh pr create --repo SanHsien/opencodex`;對上游開 PR 只在維護者這次對話明確同意回貢時才可以。 +- 修改驗證腳本前,先跑對應測試;提交前跑 `pwsh -NoProfile -File tools\dev_check.ps1`。 +- API key、ChatGPT / Codex token、cookie 與帳號資料一律不可提交。 +- 帳號池只做路由與韌性,不把它做成規避 provider 條款的工具。 +- 使用繁體中文,直接交付可驗證結果,避免冗長背景鋪陳。 diff --git a/FORK.md b/FORK.md new file mode 100644 index 0000000000..4d3f215eca --- /dev/null +++ b/FORK.md @@ -0,0 +1,78 @@ +# Fork 維護說明 + +本 repo fork 自 [`lidge-jun/opencodex`](https://github.com/lidge-jun/opencodex), +沿用 MIT License 與完整 Git 歷史。這是**可管理多個 ChatGPT / Codex 帳號**的那一個 OpenCodex:本機 proxy + dashboard 的 account pool,不是遠端桌面中介,也不是 OpenCode 的改名 fork。 + +## 為什麼維護 fork + +- 在 Windows 11 本機跑 `ocx`:把 Claude、Gemini、Grok、DeepSeek、Ollama 等接到 Codex / Claude Code。 +- 使用並審查 **ChatGPT account pool**(多帳號、配額、thread affinity)。 +- 上游仍在快速發版;本線要能逐筆審查 `upstream/main`,而不是盲目跟。 +- 不當成第二個官方產品站,也不在本 fork 發 `@bitkyc08/opencodex`。 + +**回貢判準:修的是上游 proxy / 帳號池 / 測試的 bug 就送回 `lidge-jun/opencodex`;這裡獨創的文件/Windows 維護骨架留在這裡。** + +## 與上游的差異 + +| 項目 | 說明 | +|---|---| +| `README.md` | 繁體中文產品說明(主文件);英文在 `README.en.md` | +| GUI / docs-site 語系 | 只保留英文與繁體中文;其餘語系刪除 | +| `AGENTS.md` 開頭 overlay | 指向本檔;下文仍是上游產品規則 | +| `FORK.md` / `NOTICE.md` / `CLAUDE.md` / `SKILL.md` / `REVIEW.md` | 本 fork 的維護入口 | +| `docs/fork/` | Windows 開發、上游審查、決策 | +| `tools/dev_check.ps1` | Windows 本機一鍵 fork gate | +| `.github/workflows/fork-maintenance.yml` | fork 文件與連結檢查 | +| `.github/workflows/upstream-check.yml` | 每週對 `upstream/main` 做未審查 commit 檢查 | +| 上游 `release.yml` / `deploy-docs.yml` / issue·PR 治理 workflow | 加上只在官方 `lidge-jun/opencodex` 執行的 guard | +| 上游 `ci.yml` | **保留並在本 fork 跑**,這是產品回歸 | + +上游 `CONTRIBUTING.md`、`MAINTAINERS.md`、`SECURITY.md`、`docs-site/`、`src/` 以上游為準,除非有已記錄的 fork 修正。 + +## 分支與 remote + +- `origin/main`:SanHsien 維護主線(fork 當下對齊上游 release `main`)。 +- `upstream/main`:上游發版線;`upstream/dev` 是上游 PR 整合線,需要時再 fetch。 +- 本 fork 的一般修改從 `main` 建短期 branch,開 PR、讀完整 diff,等 CI 通過後再 squash merge 回 `main`。 +- 不要把 fork-only 的維護差異送到 upstream。上游的 `dev` 整合政策只適用於回貢,不適用本 fork 的日常 PR。 + +### 開 PR 的硬規則 + +日常 PR **只能**打進 `SanHsien/opencodex`: + +```powershell +git remote -v +gh pr create --repo SanHsien/opencodex --base main +``` + +建完後核對印出的 URL 必須是 `https://github.com/SanHsien/opencodex/pull/...`。 +裸跑 `gh pr create`(不加 `--repo`)會打到上游 `lidge-jun/opencodex`——2026-08-22 已發生過(`#2373`,已關閉),禁止再犯。 + +對上游開 PR 的**唯一例外**:維護者在這次對話明確同意回貢。下列都不是例外:fork、建置開發環境、開 PR、比照其他 repo、合併回 main。 + +不要 `git push upstream`。同步方式見 [`docs/fork/UPSTREAM.md`](docs/fork/UPSTREAM.md)。 + +## 換一台電腦怎麼開發 + +```powershell +git clone https://github.com/SanHsien/opencodex.git +cd opencodex +bun install --frozen-lockfile +pwsh -NoProfile -File tools\dev_check.ps1 +``` + +完整產品回歸(較久): + +```powershell +bun run typecheck +bun run test +``` + +只想當使用者跑 proxy、不開發時: + +```powershell +npm install -g @bitkyc08/opencodex +ocx start +``` + +然後打開 http://localhost:10100 設定 provider 與 ChatGPT 帳號池。 diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000000..b21feb64bc --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,41 @@ +# NOTICE + +opencodex (SanHsien maintenance fork) +Copyright 2026 SanHsien + +This project is derived from [`lidge-jun/opencodex`](https://github.com/lidge-jun/opencodex), originally licensed under the MIT License. + +Original work: + +- Project: `opencodex` (`@bitkyc08/opencodex`, CLI `ocx`) +- Author / organization: `lidge-jun` and opencodex contributors +- License: MIT +- Original copyright notice: `Copyright (c) 2026 opencodex contributors` + +This repository keeps the original MIT license text in [`LICENSE`](LICENSE). Modifications, documentation, and future project-specific changes in this fork are maintained by SanHsien unless otherwise noted. + +## License Notes + +The MIT License allows use, copying, modification, merging, publication, distribution, sublicensing, and commercial use, provided that the original copyright notice and permission notice are included in all copies or substantial portions of the software. + +When redistributing this project or substantial parts of it: + +- Keep [`LICENSE`](LICENSE) with the original MIT text. +- Keep attribution to `lidge-jun/opencodex`. +- Add separate attribution for new third-party libraries when their licenses require it. + +## Project Scope + +This fork ships a local Bun-native provider proxy for OpenAI Codex, Claude Code, Claude Desktop, and Grok Build. It can route requests across many LLM providers and can manage a ChatGPT / Codex **account pool** for Codex auth (quota-aware selection, thread affinity, failover). + +It does not replace OpenAI, Anthropic, Google, xAI, or any other provider. Listing or proxying a provider is not an endorsement and does not grant a license to that service. + +Account pooling is for routing and operational resilience only. This project does not endorse using additional accounts to circumvent provider limits or sharing account credentials between people. Operators must follow each provider's current terms. + +## Credits + +`opencodex` belongs to the upstream project. The proxy runtime, GUI, docs-site, tests, and release tooling in this tree come from `lidge-jun/opencodex` unless a file in `docs/fork/` or the SanHsien overlay documents otherwise. + +This project is not affiliated with, endorsed by, or sponsored by OpenAI, Anthropic, Google, xAI, or any named provider. + +Do not commit secrets, API keys, cookies, ChatGPT / Codex tokens, OAuth credentials, or account data. diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000000..bf6526d090 --- /dev/null +++ b/README.en.md @@ -0,0 +1,305 @@ +> This is the [SanHsien/opencodex](https://github.com/SanHsien/opencodex) maintenance fork. Product behavior follows upstream. Maintainer rules: [FORK.md](FORK.md). + +

make codex open!

+

Universal provider proxy for OpenAI Codex, Claude Code, Claude Desktop & Grok Build
+Two commands, and every one of them runs any LLM you point it at.

+ +

+ Follow @claudeebum on X + npm version + license + node version +

+ +```bash +npm install -g @bitkyc08/opencodex +ocx start # proxy + dashboard on localhost:10100 +``` + + + + + + + + + + +
+ Claude Code running a routed model through opencodex — the status bar shows gpt-5.6-luna-medium as the active model
+ Claude Code, running any model.
The picker is stock Claude Code. The brain behind it isn't.
+
+ opencodex demo — running a task in the Codex app on a routed non-OpenAI model
+ Codex, running any model.
Pick a provider and go — same workflow, different brain.
+
+ Claude Desktop answering as Claude Opus 4.8, then dispatching a GPT-5.6 Sol subagent through opencodex
+ Claude Desktop, running any model.
Opus answers, then hands the task to a GPT-5.6 Sol subagent.
+
+ Grok Build running GPT-5.6 Sol through opencodex and calling a Kimi K3 subagent
+ Grok Build, running any model.
Sol drives the session and calls a Kimi K3 subagent.
+
+ +

+ 繁體中文 · English · 📖 Full documentation → +

+ +opencodex is a lightweight local proxy that translates Codex's Responses API into whatever your +provider speaks — streaming, tool calls, reasoning tokens, images, in both directions. Use Claude, +Gemini, Grok, GLM, DeepSeek, Kimi, Qwen, Ollama, or any other LLM with Codex, Claude Code, Claude +Desktop, and Grok Build. It can also manage a **ChatGPT account pool** for Codex auth: add accounts, +refresh their quotas in the dashboard, and let new sessions auto-route to the lowest-usage healthy +account while existing threads stay pinned to the account that started them. + +## Quick start + +### For humans + +```bash +npm install -g @bitkyc08/opencodex # Node 18+; the Bun runtime is bundled automatically +ocx start # or `ocx service` to run it in the background +``` + +
+Install from source (latest dev) + +**macOS / Linux:** + +```bash +curl -fsSL https://bun.sh/install | bash +git clone https://github.com/lidge-jun/opencodex.git +cd opencodex && ~/.bun/bin/bun install +~/.bun/bin/bun run src/cli/index.ts start +``` + +**Windows (PowerShell):** + +```powershell +irm bun.sh/install.ps1 | iex +git clone https://github.com/lidge-jun/opencodex.git +cd opencodex; bun install +bun run src/cli/index.ts start +``` + +Source install runs the latest `dev` branch. Memory ownership +patches, runtime GC improvements, and unreleased fixes are available here before +they reach the npm package. + +
+ +Open **http://localhost:10100** and configure everything in the web dashboard — add providers +(40+ built-ins, or any OpenAI-compatible endpoint), pick models, manage accounts. `ocx gui` +re-opens the dashboard at any time. +It can also manage a **ChatGPT account pool** for Codex auth. Add multiple ChatGPT / Codex accounts, +refresh their 5h / weekly / 30d quota in the dashboard. Under quota routing, new sessions can use +the lowest-usage healthy account; round-robin and fill-first use their own policies. Existing Codex +threads normally retain affinity to the account that started them, so long SSH, tmux, or +mobile-connected sessions do not jump accounts mid-conversation — but quota re-evaluation, failover, +account exclusion, affinity expiry, or 401/403 and 429 recovery can rebind them. Give the accounts a +selection order when one of them — usually your Codex Desktop login — should only be reached for +once the others are drained. + +### For agents + +```bash +npm install -g @bitkyc08/opencodex +ocx start # or `ocx service` +ocx init # interactive setup: writes ~/.opencodex/config.json and wires Codex +``` + +`ocx init` never starts the proxy; start it first (or after — either order works, but headless +commands like `ocx provider add` and `ocx combo set` talk to the **live** proxy and exit nonzero +when it is unreachable). `ocx status` / `ocx doctor` / `ocx health` report the running state. + +> **Agents installing or running opencodex:** read +> [`AGENTS_INSTALL.md`](./AGENTS_INSTALL.md). An interactive `ocx start` may ask once whether to +> star this repository — that is the user's decision, never an agent's. The CLI suppresses the +> prompt for agent-driven runs and the API refuses them with `403 agent_consent_required`. + +## Supported platforms + +| OS | Status | Service manager | +|---|---|---| +| macOS (arm64 / x64) | Fully supported | launchd | +| Linux (x64 / arm64) | Fully supported | systemd (user unit) | +| Windows (x64) | Fully supported | Task Scheduler (hidden) / opt-in native service (`--native`, WinSW) | + +Requires [Node](https://nodejs.org) 18+. The Bun runtime is bundled on `npm install` — no separate +Bun install needed, no WSL needed on Windows. If npm blocked the bundled runtime's install scripts, +see the [installation docs](https://opencodex.me/getting-started/installation/). + +## Highlights + +- **Use any LLM with Codex, Claude Code, Claude Desktop, and Grok Build** — 40+ providers out of + the box, each keeping its own native UI. +- **Pool ChatGPT accounts** — thread affinity, quota-aware auto-switching, cooldown and + fail-closed auth handling. + + > **Provider-policy note:** Account pooling is for routing and operational resilience only; it does + > not guarantee protection from provider rate limits, enforcement, suspension, or other account + > actions. OpenCodex does not endorse using additional accounts to circumvent provider limits or + > sharing account credentials between people. You are responsible for complying with each + > provider's current terms. See the + > [Codex Auth account-pool guidance](https://opencodex.me/guides/web-dashboard/#codex-auth-and-account-pools) + > and [OpenAI's current Terms of Use](https://openai.com/policies/terms-of-use/). +- **Combos** — one virtual model id with failover or weighted round-robin across providers. See + the [combo guide](https://opencodex.me/guides/combos/). +- **Sub-agents on any model** — feature routed models in Codex's sub-agent picker, with v1/v2 + surface control and fallback chains. See the + [sub-agent guide](https://opencodex.me/guides/sub-agent-surface/). +- **Log in once, skip the API key** — OAuth for xAI, Anthropic, and Kimi; or forward + `codex login`, paste a key, or use `${ENV_VAR}` references. +- **Web search & vision sidecars** — non-OpenAI models get real web search and image understanding + through a sidecar over your ChatGPT login. +- **See what's happening** — the dashboard shows providers, OAuth status, model selection, and a + live request log with cache token counts. +- **Clean exit, zero residue** — `ocx stop` restores Codex to its original configuration. +- **Bounded memory ownership** — every long-lived cache, ring buffer, and protocol-translation + store has a finite cap, byte budget, or active reconciliation. No unbounded `Map` or `Set` + survives a config reload. + +
+Memory ownership details + +OpenCodex tracks 36 categories of process-retained state. Each has a documented bound: + +- **12 retained stores** (request log, debug rings, image cache, model cache, vision + descriptions, cursor blobs, responses continuation, etc.) are byte-accounted and + evicted by the app-owned memory budget (default 256 MiB). +- **4 observed buffers** (translator accumulators, image/OAuth/Grok tails) are + monitored for in-flight byte pressure without eviction. +- **24 state-store registrations** handle expiry sweeps (60 s interval) and + config-generation reconciliation so stale provider/account keys are removed. +- **Path and fingerprint memos** (workspace metadata, hardened identities, installation + salts, mode-hint capabilities) use insertion-order LRU caps (8–128 entries). +- **Model-cache generation tombstones** are deleted after reconciliation; a global + generation increment prevents stale in-flight discoveries from repopulating removed + providers. +- **Lab event-id deduplication** runs under a ledger lock from disk, with no + process-level RAM index. + +Run `GET /api/system/memory` (with the admin token) to inspect live retained bytes, +eviction counters, and watchdog samples. + +
+ +## Model routing + +Target any configured provider and model with the `provider/model` syntax: + +```bash +codex -m "anthropic/claude-opus-5" "Explain this stack trace" +codex -m "google/gemini-3-pro" "Write unit tests for auth.ts" +codex -m "ollama/llama3" "Refactor this function" +``` + +Omit the `provider/` prefix to use the default provider or auto-match by model name pattern. +Provider model ids containing `/` are exposed with inner slashes aliased to `-`; the raw +full-slash form keeps working too. Details: [model routing docs](https://opencodex.me/guides/model-routing/). + +## Providers & adapters + +OpenAI (ChatGPT login or API key), Anthropic, Google Gemini, xAI, Kimi, Azure OpenAI, Ollama +(local + Cloud), Cursor (experimental), and every OpenAI-compatible endpoint — plus DeepSeek, +Groq, OpenRouter, Together, Fireworks, Cerebras, Mistral, Hugging Face, NVIDIA NIM, MiniMax, +Qwen Cloud, SiliconFlow, and more. Full list: `ocx init` or the +[provider docs](https://opencodex.me/guides/providers/). + +## CLI + +```bash +ocx init # interactive setup (writes config, wires Codex, offers the shim) +ocx start [--port 10100] # start the proxy in the foreground +ocx stop # stop + restore native Codex +ocx service [install|repair|restart|start|stop|status|uninstall|remove] # background service +ocx codex-shim install # start the proxy on demand whenever `codex` launches +ocx health [--json] # check immediate proxy liveness +ocx ready [--json] [--wait [--timeout ]] # check post-sync readiness +ocx status # is the proxy running? +ocx gui # open the web dashboard +ocx provider <...> # manage providers (list/add/edit/test/remove) +ocx account <...> # manage ChatGPT accounts & API-key pools +ocx combo <...> # manage failover / round-robin combos +ocx v2 <...> # multi-agent v1/v2 surface controls +ocx update [--tag preview] # update opencodex +``` + +Unpinned starts may pick another free port if the preferred one is busy; an explicit `--port` +never hops. Full reference: [CLI docs](https://opencodex.me/reference/cli/). + +### Health and readiness + +`GET /healthz` reports immediate proxy liveness. The unauthenticated `GET /readyz` endpoint reports +post-sync readiness with the sanitized JSON identity `{service, version, uptime, pid, port, status}`. +It returns `200` when `status` is `ready`; `pending` and terminal `failed` return `503` with +`Retry-After: 1`. + +`ocx ready [--json] [--wait [--timeout ]]` performs one probe by default. `--wait` polls +for up to 45 seconds by default, but exits immediately when it observes terminal `failed`; +`--timeout ` sets a 1–300 second limit, requires `--wait`, and accepts only positive integers. CLI `--json` output is +`{ready, status, pid, port}`, where `status` is `ready`, `pending`, `failed`, or `unreachable`. + +| Exit | Result | +| --- | --- | +| `0` | Ready | +| `1` | Not ready: pending, failed, timeout, or unreachable | +| `64` | Invalid arguments | + +An older proxy without `/readyz` fails closed as `unreachable` with exit 1, while `ocx health` +remains compatible. + +### Autostart: service vs shim + +Use the **service** (`ocx service`) for an always-on proxy that restarts on crash. Use the +**shim** (`ocx codex-shim install`) for lightweight, on-demand startup without a background +daemon. Remove them with `ocx service uninstall` / `ocx codex-shim uninstall`. + +### Uninstall + +```bash +ocx uninstall # stop, remove service/shim, restore native Codex, clean up state +npm uninstall -g @bitkyc08/opencodex +``` + +## Remote access + +By default opencodex binds to `127.0.0.1` and needs no extra authentication. Binding beyond +loopback (`"hostname": "0.0.0.0"`) **requires** a bearer token — the proxy refuses to start +without `OPENCODEX_API_AUTH_TOKEN`, and every client request must carry it as +`x-opencodex-api-key`. Details: [configuration reference](https://opencodex.me/reference/configuration/). + +## Documentation + +The public docs — install, providers, routing, combos, sub-agents, sidecars, integrations, and +the CLI/config/management-API references — are built from [`docs-site/`](./docs-site) and +published to **[opencodex.me](https://opencodex.me/)**. + +Maintainer source-of-truth notes live under [`structure/`](./structure), contributor setup in +[`CONTRIBUTING.md`](./CONTRIBUTING.md), and security reporting in [`SECURITY.md`](./SECURITY.md). +Report undisclosed vulnerabilities privately through +[GitHub private vulnerability reporting](https://github.com/lidge-jun/opencodex/security/advisories/new), +not a public issue. + +## Development + +Source development requires the `bun` CLI on your `PATH`. This is separate from the published npm +package's bundled Bun runtime, which is used only by installed `ocx` commands. + +```bash +git clone https://github.com/lidge-jun/opencodex.git +cd opencodex +bun install +bun run typecheck +bun run test +``` + +See **[Contributing](./CONTRIBUTING.md)**. + +## Disclaimer + +opencodex is an independent, community-maintained project and is **not affiliated with or endorsed by OpenAI, Anthropic, or any other provider**. + +Some providers — notably Anthropic (Claude) — may suspend or restrict accounts that route API traffic through third-party proxies. **Use at your own risk (UAYOR).** Before connecting a provider, review its Terms of Service to confirm that proxy-based access is permitted. The opencodex maintainers are not responsible for any account actions taken by upstream providers. + +## License + +MIT diff --git a/README.md b/README.md index 380510496d..5011677880 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ +> 這是 [SanHsien/opencodex](https://github.com/SanHsien/opencodex) 維護型 fork。產品行為與帳號池條款以上游為準;維護規則見 [FORK.md](FORK.md)。 +

make codex open!

-

Universal provider proxy for OpenAI Codex, Claude Code, Claude Desktop & Grok Build
-Two commands, and every one of them runs any LLM you point it at.

+

適用於 OpenAI Codex 與 Claude Code 的通用供應商代理
+兩條命令,Codex 和 Claude Code 就能用任何 LLM 跑起來。

- Follow @claudeebum on X + 在 X 上關注 @claudeebum npm version license node version @@ -11,293 +13,431 @@ Two commands, and every one of them runs any LLM you point it at.

```bash npm install -g @bitkyc08/opencodex -ocx start # proxy + dashboard on localhost:10100 +ocx start # 代理 + 儀表板: localhost:10100 ``` - - - - - - - - - -
- Claude Code running a routed model through opencodex — the status bar shows gpt-5.6-luna-medium as the active model
- Claude Code, running any model.
The picker is stock Claude Code. The brain behind it isn't.
-
- opencodex demo — running a task in the Codex app on a routed non-OpenAI model
- Codex, running any model.
Pick a provider and go — same workflow, different brain.
-
- Claude Desktop answering as Claude Opus 4.8, then dispatching a GPT-5.6 Sol subagent through opencodex
- Claude Desktop, running any model.
Opus answers, then hands the task to a GPT-5.6 Sol subagent.
-
- Grok Build running GPT-5.6 Sol through opencodex and calling a Kimi K3 subagent
- Grok Build, running any model.
Sol drives the session and calls a Kimi K3 subagent.
-
+

+ 透過 opencodex 執行路由模型的 Claude Code —— 狀態列顯示 gpt-5.6-luna-medium 為目前模型
+ Claude Code 可以用任何模型。選擇器是原生 Claude Code,跑起來的模型隨你選。 +

- English · Français · 한국어 · 简体中文 · 繁體中文 · Русский · 日本語 · Türkçe · 📖 Full documentation → + opencodex 示範 —— 在 Codex 應用中用路由的非 OpenAI 模型執行任務
+ Codex 可以用任何模型。選好 provider 直接開跑 —— 同樣的 Codex 工作流,換個大腦。

-opencodex is a lightweight local proxy that translates Codex's Responses API into whatever your -provider speaks — streaming, tool calls, reasoning tokens, images, in both directions. Use Claude, -Gemini, Grok, GLM, DeepSeek, Kimi, Qwen, Ollama, or any other LLM with Codex, Claude Code, Claude -Desktop, and Grok Build. It can also manage a **ChatGPT account pool** for Codex auth: add accounts, -refresh their quotas in the dashboard, and let new sessions auto-route to the lowest-usage healthy -account while existing threads stay pinned to the account that started them. +

+ 繁體中文 · English · 📖 完整文件 → +

-## Quick start +

+ opencodex 架構 — Codex CLI 透過 opencodex 代理路由到任意 LLM 供應商 +

-### For humans +在 Codex 中 —— 以及在 **Claude Code** 中 —— 使用 Claude、Gemini、Grok、GLM、DeepSeek、Kimi、Qwen、Ollama 或任意其他 LLM,無需等待官方新增支援。 -```bash -npm install -g @bitkyc08/opencodex # Node 18+; the Bun runtime is bundled automatically -ocx start # or `ocx service` to run it in the background +opencodex 是一個輕量級本機代理,把 Codex 的 Responses API 翻譯成你的 provider 所講的協議。streaming、tool 呼叫、reasoning token、圖片 —— 全部雙向工作。 + +它還能為 Codex 認證管理一個 **ChatGPT 帳號池**。新增多個 ChatGPT / Codex 帳號,在儀表板中重新整理它們的 +5 小時 / 每週 / 30 天配額,並讓新會話自動路由到使用量最低的健康帳號。現有 Codex 執行緒會固定在啟動它的 +帳號上,因此長時間的 SSH、tmux 或行動裝置連線的會話不會在對話中途切換帳號。 + +``` +Codex CLI / App / SDK ──/v1/responses──▶ opencodex ──▶ Any provider + │ + Anthropic · Google · xAI · Kimi · Ollama Cloud · Groq + OpenRouter · Azure · DeepSeek · GLM · …and OpenAI itself ``` -
-Install from source (latest dev) +```mermaid +flowchart LR + codex[Codex 會話
CLI, App, SSH, 行動端] --> proxy[opencodex] + proxy --> existing{已有執行緒?} + existing -->|是| pinned[保持同一
ChatGPT 帳號] + existing -->|新會話| quota[重新整理配額
5h, 每週, 30d] + quota --> pick[選擇使用量最低
的健康帳號] + pick --> upstream[ChatGPT / Codex 後端] + pinned --> upstream + upstream --> outcomes[配額 / 認證結果] + outcomes -->|429| cooldown[冷卻 + failover] + outcomes -->|401 / 403| reauth[標記需重新認證] + cooldown --> quota +``` -**macOS / Linux:** +## 支援平台 + +| 作業系統 | 狀態 | 服務管理 | +|---|---|---| +| macOS (arm64 / x64) | 完整支援 | launchd | +| Linux (x64 / arm64) | 完整支援 | systemd(使用者層級) | +| Windows (x64) | 完整支援 | Task Scheduler | + +需要 [Node](https://nodejs.org) 18+。Bun 執行環境會在 `npm install` 時自動打包,不必另外安裝。三個平台皆可原生執行(Windows 不需要 WSL)。 + +## 快速開始 ```bash -curl -fsSL https://bun.sh/install | bash -git clone https://github.com/lidge-jun/opencodex.git -cd opencodex && ~/.bun/bin/bun install -~/.bun/bin/bun run src/cli/index.ts start +# 安裝(自動打包 Bun 執行時 —— 只需 Node 18+) +# 建議使用自己的 Node(nvm/fnm)—— 避免使用 `sudo npm install -g …` +npm install -g @bitkyc08/opencodex + +# 互動式初始化(寫入設定並注入 Codex) +ocx init + +# 啟動代理 +ocx start + +# 照常使用 Codex —— 請求已由 opencodex 路由 +codex "Write a hello world in Rust" ``` -**Windows (PowerShell):** +
+遇到 "bundled Bun runtime is missing" 錯誤 / npm 攔截了 Bun 安裝腳本? + +
-```powershell -irm bun.sh/install.ps1 | iex -git clone https://github.com/lidge-jun/opencodex.git -cd opencodex; bun install -bun run src/cli/index.ts start +opencodex 把 Bun 執行時作為依賴打包,並透過 Node 啟動器執行,因此你**不必**自己安裝 Bun。如果看到 "bundled Bun runtime is missing" 錯誤,代表安裝時略過了 lifecycle 腳本(包括 npm 透過 `allowScripts` 攔截 bun postinstall 的情況)或 optional 依賴。請允許 bun 安裝腳本後再重裝: + +```bash +npm install -g --allow-scripts=bun @bitkyc08/opencodex # 不要加 --ignore-scripts、--omit=optional + +# 若一開始用 sudo 安裝,請繼續用 sudo: +sudo npm install -g --allow-scripts=bun @bitkyc08/opencodex ``` -Source install runs the latest `dev` branch. Memory ownership -patches, runtime GC improvements, and unreleased fixes are available here before -they reach the npm package. +npm 警告給的縮寫指令少了套件名,會把目前目錄重裝進去, +請務必明確寫上 `@bitkyc08/opencodex`。 + +如果之前用 sudo 安裝到了 root 字首,上面的 sudo 重灌可以解除該字首的攔截 —— +但條件允許時建議改用自己的 Node(nvm、fnm 或使用者層級的 npm prefix)。
-Open **http://localhost:10100** and configure everything in the web dashboard — add providers -(40+ built-ins, or any OpenAI-compatible endpoint), pick models, manage accounts. `ocx gui` -re-opens the dashboard at any time. -It can also manage a **ChatGPT account pool** for Codex auth. Add multiple ChatGPT / Codex accounts, -refresh their 5h / weekly / 30d quota in the dashboard. Under quota routing, new sessions can use -the lowest-usage healthy account; round-robin and fill-first use their own policies. Existing Codex -threads normally retain affinity to the account that started them, so long SSH, tmux, or -mobile-connected sessions do not jump accounts mid-conversation — but quota re-evaluation, failover, -account exclusion, affinity expiry, or 401/403 and 429 recovery can rebind them. Give the accounts a -selection order when one of them — usually your Codex Desktop login — should only be reached for -once the others are drained. +## 亮點 -### For agents +- **在 Codex 中使用任意 LLM。** 5 種協議 adapter 覆蓋 Anthropic Messages、Google Gemini、Azure、OpenAI Responses 直通,以及一切 OpenAI 相容 Chat Completions 端點 —— 即開箱即用的 **40+ provider**。 +- **在 Claude 中也能使用任意 LLM。** `ocx claude` 可透過代理啟動 Claude Code。Claude 儀表板還提供獨立的 Desktop 配置,可管理 Opus、Fable、Sonnet、Haiku 四個系列,並支援拖放、鍵盤操作和 JSON 匯入/匯出。 +- **安全地池化 ChatGPT 帳號。** 現有 Codex 執行緒保持在一個帳號上,而新會話可以從池中自動挑選使用量更低的帳號,並帶有配額重新整理和非 PII 請求標籤。 +- **登入一次,不必填 API key。** xAI、Anthropic、Kimi 支援 OAuth,可用現有帳號認證,token 自動重新整理。也可以轉發 `codex login`、貼上 API key,或使用 `${ENV_VAR}` 引用 —— 隨你選擇。 +- **Codex 在哪裡能用,它就在哪裡能用。** 自動注入 Codex CLI、TUI、App 和 SDK。路由模型像原生模型一樣出現在 Codex 的模型選擇器裡。 +- **委派給合適的模型。** 在儀表板或 config 中把最多 5 個路由/原生模型放進 Codex 的 subagent 選擇器 —— 複雜任務交給 reasoning 模型,快速任務交給便宜模型。在 v2 多智慧體表面(GPT-5.6 Sol/Terra)上,代理會注入精簡的委派指引:首選子智慧體模型與 effort(`injectionModel` / `injectionEffort`)、featured 模型清單及各自支援的 effort 階梯,以及讓跨模型 `spawn_agent` 覆蓋得以應用的 `fork_turns` 規則。已知限制:原生父代理 spawn 路由子代理時,任務本文可能以後端加密形式到達而丟失([#92](https://github.com/lidge-jun/opencodex/issues/92))—— 需要可靠的跨 provider 委派請使用 v1 表面。想自訂文案,可在 `injectionPrompt` 中使用 `{{model}}` / `{{effort}}` / `{{roster}}` 預留位置。 +- **為 preview-gated OpenAI rollout 做好準備。** GPT-5.6 Sol/Terra/Luna 保留 upstream effort 階梯。Direct/Multi 使用 372k Codex 契約,OpenAI API 與 OpenRouter 使用 1.05M 後設資料。 +- **給任意模型超能力。** 非 OpenAI 模型可透過 `gpt-5.4-mini` sidecar(使用你的 ChatGPT 登入)獲得真正的網頁搜尋與圖片理解。 +- **原生生成圖片。** Codex 的獨立 `image_gen` 工具透過 `POST /v1/images/generations` 生成圖片、透過 `POST /v1/images/edits` 編輯圖片;它獨立於 hosted Responses 的 `image_generation` 工具。 +- **看清正在發生什麼。** Web 儀表板展示 provider、OAuth 狀態、模型選擇和即時請求日誌;當上遊回傳時,也會包含 cached/cache-write token 計數 —— 不用再猜請求為何失敗。 +- **背景執行。** 安裝為系統服務(launchd / systemd / Task Scheduler)後開機自啟,無需操心。 +- **乾淨退出,零殘留。** `ocx stop`(或儀表板的 Stop 按鈕)會關閉代理、停止已安裝的背景服務,並將 Codex 恢復為原始配置。之後 `codex` 就像從沒安裝過 opencodex 一樣工作 —— 無殘留配置,無殭屍程序。 + +## 新增供應商 + +最簡單的做法:用 Web 儀表板。 ```bash -npm install -g @bitkyc08/opencodex -ocx start # or `ocx service` -ocx init # interactive setup: writes ~/.opencodex/config.json and wires Codex +ocx gui ``` -`ocx init` never starts the proxy; start it first (or after — either order works, but headless -commands like `ocx provider add` and `ocx combo set` talk to the **live** proxy and exit nonzero -when it is unreachable). `ocx status` / `ocx doctor` / `ocx health` report the running state. +這會開啟 `http://localhost:10100` 儀表板。在這裡: -> **Agents installing or running opencodex:** read -> [`AGENTS_INSTALL.md`](./AGENTS_INSTALL.md). An interactive `ocx start` may ask once whether to -> star this repository — that is the user's decision, never an agent's. The CLI suppresses the -> prompt for agent-driven runs and the API refuses them with `403 agent_consent_required`. +1. 點選 **"Add Provider"**。 +2. 從 **40+ 內建 provider** 中選擇,或輸入自訂的 OpenAI 相容端點。 +3. 貼上 API key(Anthropic、xAI、Kimi 也可用 OAuth 登入)。 +4. 模型會從 provider 的 `/v1/models` 端點**自動發現**。 -## Supported platforms +新 provider 立即可用,無需重新啟動。 -| OS | Status | Service manager | -|---|---|---| -| macOS (arm64 / x64) | Fully supported | launchd | -| Linux (x64 / arm64) | Fully supported | systemd (user unit) | -| Windows (x64) | Fully supported | Task Scheduler (hidden) / opt-in native service (`--native`, WinSW) | - -Requires [Node](https://nodejs.org) 18+. The Bun runtime is bundled on `npm install` — no separate -Bun install needed, no WSL needed on Windows. If npm blocked the bundled runtime's install scripts, -see the [installation docs](https://opencodex.me/getting-started/installation/). - -## Highlights - -- **Use any LLM with Codex, Claude Code, Claude Desktop, and Grok Build** — 40+ providers out of - the box, each keeping its own native UI. -- **Pool ChatGPT accounts** — thread affinity, quota-aware auto-switching, cooldown and - fail-closed auth handling. - - > **Provider-policy note:** Account pooling is for routing and operational resilience only; it does - > not guarantee protection from provider rate limits, enforcement, suspension, or other account - > actions. OpenCodex does not endorse using additional accounts to circumvent provider limits or - > sharing account credentials between people. You are responsible for complying with each - > provider's current terms. See the - > [Codex Auth account-pool guidance](https://opencodex.me/guides/web-dashboard/#codex-auth-and-account-pools) - > and [OpenAI's current Terms of Use](https://openai.com/policies/terms-of-use/). -- **Combos** — one virtual model id with failover or weighted round-robin across providers. See - the [combo guide](https://opencodex.me/guides/combos/). -- **Sub-agents on any model** — feature routed models in Codex's sub-agent picker, with v1/v2 - surface control and fallback chains. See the - [sub-agent guide](https://opencodex.me/guides/sub-agent-surface/). -- **Log in once, skip the API key** — OAuth for xAI, Anthropic, and Kimi; or forward - `codex login`, paste a key, or use `${ENV_VAR}` references. -- **Web search & vision sidecars** — non-OpenAI models get real web search and image understanding - through a sidecar over your ChatGPT login. -- **See what's happening** — the dashboard shows providers, OAuth status, model selection, and a - live request log with cache token counts. -- **Clean exit, zero residue** — `ocx stop` restores Codex to its original configuration. -- **Bounded memory ownership** — every long-lived cache, ring buffer, and protocol-translation - store has a finite cap, byte budget, or active reconciliation. No unbounded `Map` or `Set` - survives a config reload. +也可以用 `ocx init`(互動式 CLI)或直接編輯 `~/.opencodex/config.json` 來新增 provider。 -
-Memory ownership details - -OpenCodex tracks 36 categories of process-retained state. Each has a documented bound: - -- **12 retained stores** (request log, debug rings, image cache, model cache, vision - descriptions, cursor blobs, responses continuation, etc.) are byte-accounted and - evicted by the app-owned memory budget (default 256 MiB). -- **4 observed buffers** (translator accumulators, image/OAuth/Grok tails) are - monitored for in-flight byte pressure without eviction. -- **24 state-store registrations** handle expiry sweeps (60 s interval) and - config-generation reconciliation so stale provider/account keys are removed. -- **Path and fingerprint memos** (workspace metadata, hardened identities, installation - salts, mode-hint capabilities) use insertion-order LRU caps (8–128 entries). -- **Model-cache generation tombstones** are deleted after reconciliation; a global - generation increment prevents stale in-flight discoveries from repopulating removed - providers. -- **Lab event-id deduplication** runs under a ledger lock from disk, with no - process-level RAM index. - -Run `GET /api/system/memory` (with the admin token) to inspect live retained bytes, -eviction counters, and watchdog samples. +## 模型路由 -
+透過 `provider/model` 格式指定路由模型,在 Codex 中直接使用: -## Model routing +```bash +# 透過 Anthropic 使用 Claude Opus +codex -m "anthropic/claude-opus-5" "解釋這個 stack trace" -Target any configured provider and model with the `provider/model` syntax: +# 透過 Google 使用 Gemini +codex -m "google/gemini-3-pro" "為 auth.ts 寫單元測試" -```bash -codex -m "anthropic/claude-opus-5" "Explain this stack trace" -codex -m "google/gemini-3-pro" "Write unit tests for auth.ts" -codex -m "ollama/llama3" "Refactor this function" +# 透過 Ollama Cloud 使用 GLM +codex -m "ollama-cloud/glm-5.2" "寫一個 SQL migration" + +# 透過 Ollama 使用本機模型 +codex -m "ollama/llama3" "重構這個函式" ``` -Omit the `provider/` prefix to use the default provider or auto-match by model name pattern. -Provider model ids containing `/` are exposed with inner slashes aliased to `-`; the raw -full-slash form keeps working too. Details: [model routing docs](https://opencodex.me/guides/model-routing/). +省略 `provider/` 字首時,opencodex 會路由到預設 provider,或根據模型名模式自動匹配(例如 `claude-*` +路由到 Anthropic,`gpt-*` 路由到 OpenAI)。 + +路由模型也會出現在 **Codex App** 模型選擇器中,並帶有按模型的 reasoning effort 控制: + +目前 Codex 建置在模型宣告支援時可顯示 `low`、`medium`、`high`、`xhigh`、`max` 和 `ultra` reasoning 控制。 +除非 provider config 明確設定 alias,opencodex 會把 `xhigh` 與 `max` 保持為不同檔位。`ultra` 與上游 +Codex 語義一致:客戶端啟用最大 reasoning 並主動委派多智慧體,實際請求會轉換為 `max` 傳送。 +路由模型僅在 provider config 透過 `reasoningEfforts` 顯式開啟時才會宣告 `ultra`。 -## Providers & adapters +GPT-5.6 Sol/Terra/Luna 已在 OpenAI API key 和 OpenRouter 預設中作為 rollout-ready 目錄條目預先配置 +(`gpt-5.6-sol`、`gpt-5.6-terra`、`gpt-5.6-luna`;OpenRouter 使用 `openai/...`)。 +規格與上游 models.json 快照一致 —— Sol/Terra 提供到 `ultra`,Luna 到 `max`,Sol 預設 +reasoning 為 `low`。可用性仍受上游 +preview gate 限制;opencodex 只是準備好你的帳號/provider 可存取時所需的路由和目錄後設資料。 -OpenAI (ChatGPT login or API key), Anthropic, Google Gemini, xAI, Kimi, Azure OpenAI, Ollama -(local + Cloud), Cursor (experimental), and every OpenAI-compatible endpoint — plus DeepSeek, -Groq, OpenRouter, Together, Fireworks, Cerebras, Mistral, Hugging Face, NVIDIA NIM, MiniMax, -Qwen Cloud, SiliconFlow, and more. Full list: `ocx init` or the -[provider docs](https://opencodex.me/guides/providers/). +

+ Codex App 展示 opencodex 路由模型及 reasoning effort 選擇器 +

+ +## OpenAI 供應商帳號模式 + +| Provider ID | 路徑 | 憑證 | 行為 | +|---|---|---|---| +| `openai` | Codex 登入 | 主帳號 + 新增的 Codex 帳號 | 預設 Pool,可選 Direct 模式 | +| `openai-apikey` | OpenAI API | API key/key pool | 不做 Codex 帳號路由 | + +- Pool 包含主登入和新增的帳號,並應用 affinity、配額、冷卻和 failover。 +- Direct 繞過池狀態,只使用目前 caller/主登入 bearer。 +- 新安裝和未儲存模式的配置預設使用 Pool。在儀表板 **Providers** 中切換模式時, + `gpt-5.6-sol` 等 bare 模型 id 保持不變。 +- `openai-apikey/gpt-5.6-sol` 選擇 API;Codex 登入與 API 憑證之間不會 fallback。 +- 目前 marker 為 `openaiProviderTierVersion: 2`,原配置備份到 + `~/.opencodex/config.json.pre-openai-tiers-v2.bak`。恢復命令: + `cp ~/.opencodex/config.json.pre-openai-tiers-v2.bak ~/.opencodex/config.json` +- 舊的 v1 三 供應商設定會自動遷移為單一 `openai` 行。 +- API 層 GPT-5.6 後設資料為 1,050,000 context / 922,000 max input。 + `gpt-5.6-sol-pro`、`terra-pro`、`luna-pro` 保留公開 virtual id,線上請求改寫為 base id 加 + `reasoning.mode: "pro"`。 + +### Pool 帳號行為 + +開啟儀表板中的 **Codex Auth** 來新增池帳號,並選擇由哪個帳號處理下一個 Codex 會話。 +opencodex 保持兩種獨立行為: + +- **現有會話保持 affinity。** 執行緒 id 綁定到所選帳號並在後續輪次複用,因此長請求或移動/SSH 連線的會話 + 會繼續使用同一帳號。 +- **新會話可自動路由。** 啟用自動切換後,opencodex 比較 5 小時、每週、30 天使用量中最熱的配額視窗, + 當活躍帳號越過閾值時,為新會話挑選使用量更低的合格帳號。 +- **內建配額查詢。** 儀表板可一鍵重新整理所有帳號配額,請求日誌用非 PII 的帳號序號標記池流量。 +- **失敗採 fail-closed。** token 失敗會標記需重新認證,而不是悄悄回退到另一個憑證;429 配額回應會讓帳號 + 進入冷卻,並可將後續工作 failover 到另一個合格的池帳號。 + +## 供應商與 adapter + +| Provider | Adapter | 認證方式 | +|---|---|---| +| OpenAI(ChatGPT 登入) | `openai-responses` | 轉發(無需 key) | +| OpenAI(API key) | `openai-responses` | key | +| Umans AI Coding Plan | `anthropic` | key | +| Anthropic Claude | `anthropic` | oauth / key | +| xAI Grok | `openai-chat` | oauth / key | +| Kimi(Moonshot) | `openai-chat` | oauth / key | +| Google Gemini | `google` | key | +| Azure OpenAI | `azure-openai` | key | +| Ollama Cloud + 17 家 provider 目錄 | `openai-chat` | key | +| Ollama / vLLM / LM Studio(本機) | `openai-chat` | key(通常留空) | +| 任意 OpenAI 相容端點 | `openai-chat` | key | + +此外還有 DeepSeek、Groq、OpenRouter、Together、Fireworks、Cerebras、Mistral、Hugging Face、NVIDIA NIM、MiniMax、Qwen Cloud、騰訊雲 Coding Plan、SiliconFlow 等等。完整清單可用 `ocx init` 檢視,或見[供應商文件](https://opencodex.me/zh-tw/reference/configuration/)。 ## CLI ```bash -ocx init # interactive setup (writes config, wires Codex, offers the shim) -ocx start [--port 10100] # start the proxy in the foreground -ocx stop # stop + restore native Codex -ocx service [install|repair|restart|start|stop|status|uninstall|remove] # background service -ocx codex-shim install # start the proxy on demand whenever `codex` launches -ocx health [--json] # check immediate proxy liveness -ocx ready [--json] [--wait [--timeout ]] # check post-sync readiness -ocx status # is the proxy running? -ocx gui # open the web dashboard -ocx provider <...> # manage providers (list/add/edit/test/remove) -ocx account <...> # manage ChatGPT accounts & API-key pools -ocx combo <...> # manage failover / round-robin combos -ocx v2 <...> # multi-agent v1/v2 surface controls -ocx update [--tag preview] # update opencodex +ocx init # 互動式初始化 +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 # OAuth 登入(xai、anthropic、kimi、cursor 等) +ocx logout # 移除已儲存的登入 +ocx account # 檢視/切換帳號與 API-key pool(脫敏;含 refresh/auto-switch/remove/add-key) +ocx gui # 開啟 Web 儀表板 +ocx claude [args...] # 啟動接入代理的 Claude Code(模型發現已開啟) +ocx claude desktop # 儲存並套用 Claude Desktop 四系列配置 +ocx codex-shim install # 執行 codex 時自動啟動代理 +ocx service [install|start|stop|status|uninstall] # 安裝/更新/啟動背景服務 +ocx update [--tag preview] # 更新 opencodex;preview 安裝保持 @preview ``` -Unpinned starts may pick another free port if the preferred one is busy; an explicit `--port` -never hops. Full reference: [CLI docs](https://opencodex.me/reference/cli/). +### Claude Desktop 配置 + +儀表板的 **Claude → Desktop** 頁面把路由分為 Opus、Fable、Sonnet、Haiku 四個系列。新路由 +預設放入 Opus,第一個 Opus 路由是應用的初始預設模型。每個非空系列都有一個預設路由。你可以 +拖動路由,也可以用滑鼠、觸控或鍵盤操作每一行中可見的移動控制元件。點選 **儲存並套用到 Desktop** +後,配置會寫入 Claude Desktop。還可以透過 JSON 匯入/匯出來備份配置,或遷移到另一臺機器。 + +```bash +ocx claude desktop [apply] # 儲存並套用目前設定 +ocx claude desktop show [--json] # 檢視路由、系列和預設值 +ocx claude desktop move [--default] +ocx claude desktop default +ocx claude desktop export # 使用 - 將 JSON 輸出到 stdout +ocx claude desktop import [--apply] # 驗證後儲存,可選擇立即套用 +``` -### Health and readiness +`family` 可取 `opus`、`fable`、`sonnet`、`haiku`。非 Anthropic 路由會獲得帶有合成 2026 日期 +槽位的穩定 Claude 格式別名;該日期是內部槽位,不是模型釋出日期。真正的 Anthropic Claude +路由保留原始模型 id。`none` 只能用於空系列;非空系列始終需要一個預設值。舊的套用方式 +`ocx claude desktop --static`、`--hybrid` 和 +`--discovery-only` 仍可使用。 -`GET /healthz` reports immediate proxy liveness. The unauthenticated `GET /readyz` endpoint reports -post-sync readiness with the sanitized JSON identity `{service, version, uptime, pid, port, status}`. -It returns `200` when `status` is `ready`; `pending` and terminal `failed` return `503` with -`Retry-After: 1`. +### 自動啟動:service vs shim -`ocx ready [--json] [--wait [--timeout ]]` performs one probe by default. `--wait` polls -for up to 45 seconds by default, but exits immediately when it observes terminal `failed`; -`--timeout ` sets a 1–300 second limit, requires `--wait`, and accepts only positive integers. CLI `--json` output is -`{ready, status, pid, port}`, where `status` is `ready`, `pending`, `failed`, or `unreachable`. +opencodex 提供兩種自動啟動代理的方式: -| Exit | Result | -| --- | --- | -| `0` | Ready | -| `1` | Not ready: pending, failed, timeout, or unreachable | -| `64` | Invalid arguments | +| | `ocx service` / `ocx service install` | `ocx codex-shim install` | +|---|---|---| +| **方式** | OS 服務管理器(launchd / systemd / schtasks) | 包裝 `codex` 腳本啟動器;不會改動真實 `codex.exe` | +| **時機** | 登入後會一直執行 | 按需——只在執行 `codex` 時啟動 | +| **重新啟動** | 崩潰後自動重啟 | 每次執行 `codex` 時啟動一次 | +| **Codex 更新** | 不受影響 | 已穩定的新啟動器若被取代,會在下一道一般 `ocx` 命令中修復 | +| **移除** | `ocx service uninstall` | `ocx codex-shim uninstall` | -An older proxy without `/readyz` fails closed as `unreachable` with exit 1, while `ocx health` -remains compatible. +若要常駐代理,用 **service**(開發環境建議)。輕量按需啟動則用 **shim**。 -### Autostart: service vs shim +如果外部 Codex 更新覆蓋了已安裝的 shim,下一道一般 `ocx` 命令會備份已穩定的新啟動器並恢復 +shim。仍在變動中的啟動器不會被改動,會在後續命令重試。修復失敗只會警告,不會讓請求的命令 +失敗;手動備援指令為 `ocx codex-shim install`。若要關閉自動還原,請將 +`codexShimAutoRestore` 設為 `false`,或為程序設定 +`OPENCODEX_CODEX_SHIM_AUTO_RESTORE=0`。 +如果配置的代理埠已被佔用,`ocx start` 會自動選擇另一個空閒本機埠並更新 Codex 使用它。 -Use the **service** (`ocx service`) for an always-on proxy that restarts on crash. Use the -**shim** (`ocx codex-shim install`) for lightweight, on-demand startup without a background -daemon. Remove them with `ocx service uninstall` / `ocx codex-shim uninstall`. +### 解除安裝 -### Uninstall +移除 npm 套件前,先清掉本機狀態: ```bash -ocx uninstall # stop, remove service/shim, restore native Codex, clean up state +ocx uninstall npm uninstall -g @bitkyc08/opencodex ``` -## Remote access +`ocx uninstall` 會停止代理、移除已安裝的 service、移除 Codex shim、恢復原生 Codex config/catalog/history,並刪除 `~/.opencodex`。 + +## 設定 + +配置檔案路徑:`~/.opencodex/config.json`。 + +**雲端供應商範例:** + +```json +{ + "port": 10100, + "defaultProvider": "anthropic", + "providers": { + "anthropic": { + "adapter": "anthropic", + "baseUrl": "https://api.anthropic.com", + "authMode": "oauth", + "defaultModel": "claude-sonnet-4-6" + }, + "ollama-cloud": { + "adapter": "openai-chat", + "baseUrl": "https://ollama.com/v1", + "apiKey": "${OLLAMA_API_KEY}", + "defaultModel": "glm-5.2" + } + } +} +``` -By default opencodex binds to `127.0.0.1` and needs no extra authentication. Binding beyond -loopback (`"hostname": "0.0.0.0"`) **requires** a bearer token — the proxy refuses to start -without `OPENCODEX_API_AUTH_TOKEN`, and every client request must carry it as -`x-opencodex-api-key`. Details: [configuration reference](https://opencodex.me/reference/configuration/). +provider 條目還可以標註路由目錄後設資料。`contextWindow` 設定供應商層級、對 Codex 可見的上下文上限, +`modelContextWindows` 設定按模型的上限,`modelInputModalities` 設定按模型的目錄輸入提示,例如 `["text"]` +或 `["text", "image"]`。這些值只會對即時 `/models` 後設資料設上限,絕不會把更小的即時上下文視窗抬高。內建 +GPT-5.6 Sol/Terra/Luna fallback 後設資料會為 OpenAI API key 和 OpenRouter 目錄條目使用 1,050,000 token 的 +usable context window;它不會繞過上游 preview access。完整欄位見設定參考。 + +> **透過 Z.AI 使用 GLM-5.2 1M 上下文:** 在 `openai-chat` adapter 下,`glm-5.2` 和 `glm-5.2[1m]` 都可用 —— +> opencodex 會在傳送請求前剝離末尾的 `[1m]` 字尾,因為 OpenAI 相容端點會拒絕帶方括號的 id(Z.AI 400 code +> 1211)。`[1m]` 字尾是 Claude-Code / Anthropic 端點的約定;若要原生使用,請把 `anthropic` adapter 指向 +> Z.AI 的 coding base(`https://api.z.ai/api/coding/paas/v4`)。1M 上下文視窗透過模型目錄 +> (`modelContextWindows`)設定,而不是模型名。 + +**本機供應商範例(Ollama / vLLM / LM Studio):** + +```json +{ + "port": 10100, + "defaultProvider": "local", + "providers": { + "local": { + "adapter": "openai-chat", + "baseUrl": "http://localhost:11434/v1", + "apiKey": "", + "defaultModel": "qwen3:32b" + } + } +} +``` -## Documentation +本機 provider 的 `apiKey` 通常留空。只要本機服務有提供 OpenAI 相容的 Chat Completions 端點,opencodex 就能直接接上。 -The public docs — install, providers, routing, combos, sub-agents, sidecars, integrations, and -the CLI/config/management-API references — are built from [`docs-site/`](./docs-site) and -published to **[opencodex.me](https://opencodex.me/)**. +WebSocket 傳輸預設關閉。只有當你希望 Codex 使用 Responses WebSocket 而不是 HTTP/SSE 時,才需要設定 `"websockets": true`。 -Maintainer source-of-truth notes live under [`structure/`](./structure), contributor setup in -[`CONTRIBUTING.md`](./CONTRIBUTING.md), and security reporting in [`SECURITY.md`](./SECURITY.md). -Report undisclosed vulnerabilities privately through -[GitHub private vulnerability reporting](https://github.com/lidge-jun/opencodex/security/advisories/new), -not a public issue. +### 遠端存取 -## Development +預設 opencodex 會綁定在 `127.0.0.1`(迴環),不必額外認證。 +若你設定 `"hostname": "0.0.0.0"` 把代理暴露到區網,opencodex 會要求 bearer token,同時保護管理 +API(`/api/*`)和資料平面(`/v1/responses`、`/v1/images/generations`、`/v1/images/edits`): -Source development requires the `bun` CLI on your `PATH`. This is separate from the published npm -package's bundled Bun runtime, which is used only by installed `ocx` commands. +```bash +export OPENCODEX_API_AUTH_TOKEN="your-secret-token" +ocx start +``` + +綁定到非迴環地址時若缺少該環境變數,代理會拒絕啟動。若為區網存取安裝背景服務,請在 `ocx service install` +前,於同一 shell 匯出同一個變數,讓服務管理器收得到。客戶端(腳本、遠端機器)每個請求都必須帶 token: + +``` +x-opencodex-api-key: your-secret-token +``` + +token 以常數時間比較,避免時序攻擊。 + +opencodex 會自動 remap Codex 的 resume 歷史,讓舊的 OpenAI 對話與 opencodex 建立的專案執行緒在代理運作期間仍能在 +Codex App 中可見。原始 provider/source 後設資料紀錄在 `~/.opencodex/codex-history-backup.json`。`ocx stop` / +`ocx restore` 會把備份的 OpenAI 列還原到 OpenAI,並把其餘 opencodex 使用者執行緒也 eject 到 OpenAI,讓原生 +Codex 不會去 resume 一個供應商已不在 `config.toml` 的執行緒。 + +若你測過備份功能出現前的舊開發版(`syncResumeHistory` 已經 remap 了歷史),可執行明確的還原命令: ```bash -git clone https://github.com/lidge-jun/opencodex.git +ocx recover-history --legacy-openai +``` + +各欄位詳細說明見 **[設定參考](https://opencodex.me/zh-tw/reference/configuration/)**。 + +## 文件 + +完整文件——安裝、供應商設定、路由、sidecar、Codex 整合、Codex App 模型選擇器、CLI/設定參考——由 [`docs-site/`](../docs-site) 目錄的 Astro 站點建置,發布於 **[opencodex.me](https://opencodex.me/zh-tw/)**。 + +維護者的 source of truth 在 [`structure/`](../structure),歷史調查與診斷筆記留在 [`docs/`](../docs)。 + +## 開發 + +```bash +git clone https://github.com/SanHsien/opencodex.git cd opencodex bun install -bun run typecheck -bun run test +bun run dev:proxy # 以開發模式啟動代理 API +bun run dev:gui # 在另一個終端機啟動儀表板 dev 伺服器 +bun x tsc --noEmit # 型別檢查 +``` + +`bun run dev` 仍保留為 `bun run dev:proxy` 的別名以相容舊用法。在原始碼 checkout 中,代理 API 暴露 `/healthz`、 +`/v1/responses`、`POST /v1/images/generations`、`POST /v1/images/edits`、`/api/*`;只有在 +`bun run build:gui` 產生 `gui/dist` 後,`GET /` 才會提供打包好的儀表板。開發前端時請另外執行: + +```bash +bun run dev:gui ``` -See **[Contributing](./CONTRIBUTING.md)**. +見 **[貢獻指南](https://opencodex.me/zh-tw/contributing/)**。 -## Disclaimer +## 免責聲明 -opencodex is an independent, community-maintained project and is **not affiliated with or endorsed by OpenAI, Anthropic, or any other provider**. +opencodex 是獨立的社群維護專案,**與 OpenAI、Anthropic 或其他任何供應商無關,也未獲其背書。** -Some providers — notably Anthropic (Claude) — may suspend or restrict accounts that route API traffic through third-party proxies. **Use at your own risk (UAYOR).** Before connecting a provider, review its Terms of Service to confirm that proxy-based access is permitted. The opencodex maintainers are not responsible for any account actions taken by upstream providers. +部分供應商——尤其是 Anthropic(Claude)——可能會暫停或限制經第三方代理路由 API 流量的帳號。**使用風險自負(UAYOR)。** 連線供應商前,請先查其服務條款,確認是否允許代理式存取。上游供應商對帳號採取的任何處置,opencodex 維護者概不負責。 -## License +## 授權 MIT diff --git a/REVIEW.md b/REVIEW.md new file mode 100644 index 0000000000..c462398625 --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,56 @@ +# Repository review(Windows-first) + +- Review date: 2026-08-22 +- Review baseline: 上游 `main` `6ae83b1f189c353935d4977bb01227484fbdb52b`(`release: v2.31.0`) +- Primary environment: Windows 11、PowerShell、Bun 1.3.14(本機)、產品宣告 Bun 1.4.0 +- Status: fork 維護骨架已建立;上游產品程式未改 + +## 結論 + +這個 fork 適合作為 Windows 本機使用與追蹤上游的維護線。產品是本機 provider proxy,含 ChatGPT / Codex **account pool**。 + +本輪只做 fork 骨架與危險 workflow 隔離。**沒有**改 `src/`、**沒有**改帳號池行為、**沒有**回貢、**沒有**發 npm。 + +## 已做 + +| ID | 項目 | 說明 | +|---|---|---| +| F-01 | GitHub fork | `SanHsien/opencodex`,`upstream` = `lidge-jun/opencodex` | +| F-02 | 維護文件 | `FORK.md`、`NOTICE.md`、`CLAUDE.md`、`SKILL.md`、`docs/fork/` | +| F-03 | Windows gate | `tools/dev_check.ps1` + `tests/fork-hygiene.test.ts` | +| F-04 | 上游追蹤 | `tools/check-upstream-updates.ts` + weekly workflow | +| F-05 | 發佈隔離 | `release.yml` / `deploy-docs.yml` / 治理 workflow 加官方 repo guard | + +## 刻意不修 + +| ID | 項目 | 理由 | +|---|---|---| +| U-01 | 上游 `dev` 整合線與 PR 模板 | 那是官方專案流程。本 fork 日常走 `main`。 | +| U-02 | 上游其它語系(法/日/韓/俄/土/簡中) | 本 fork 只留英文與繁中;上游新增語系不跟。 | +| U-03 | 本機 Bun 1.3.14 vs 套件宣告 1.4.0 | 先用現有 Bun 驗證 fork gate;產品 CI 會裝 `package.json` 宣告的版本。 | + +## 本輪實證 + +### 本機(Windows 11) + +```text +bun install --frozen-lockfile +→ 103 packages installed + +pwsh -NoProfile -File tools\dev_check.ps1 +→ fork-hygiene 14 pass / 0 fail +→ check-links:11 份文件,0 斷連結 +→ WINDOWS DEV CHECK GREEN + +bun run typecheck +→ bun x tsc --noEmit 成功(exit 0) + +bun tools/check-upstream-updates.ts +→ No new upstream commits(仍在 6ae83b1f) +``` + +### 尚未宣稱範圍 + +- **沒有**本機跑完整 `bun run test`(約數分鐘;交給 GitHub `ci.yml`)。 +- **沒有**用真實 ChatGPT 多帳號做 pool 端到端 smoke。 +- **不宣稱**本 fork 會發自己的 npm 套件。 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000000..cb1a9b36aa --- /dev/null +++ b/SKILL.md @@ -0,0 +1,45 @@ +--- +name: opencodex +description: 維護 SanHsien/opencodex:lidge-jun/opencodex 的 Windows-first fork。本機 Bun proxy,讓 Codex / Claude Code 使用任意 LLM,並管理 ChatGPT 多帳號 pool。 +--- + +# opencodex + +完整維護規則先讀 [`FORK.md`](FORK.md);產品行為再讀 [`AGENTS.md`](AGENTS.md)。 + +## 何時使用 + +使用者要維護 `SanHsien/opencodex` 時使用,例如: + +- 本機啟動 `ocx` / dashboard(`localhost:10100`)。 +- 審查或同步上游帳號池、provider、routing 變更。 +- 修 Windows 開發 gate、fork 文件或 workflow guard。 +- 調查 Codex / Claude Code 連到非 OpenAI 模型的行為。 + +## 核心邊界 + +- 這是**多帳號 ChatGPT pool** 的那一個 OpenCodex,不是遠端桌面中介,也不是 OpenCode fork。 +- 不發 `@bitkyc08/opencodex`,不部署官方 docs-site。 +- 開 PR 只用 `--repo SanHsien/opencodex`。對上游開 PR 必須這次對話明確同意回貢。 +- 不提交 token、cookie、OAuth、帳號池真實憑證。 +- 不把帳號池說成可以規避 provider 限額或條款。 +- `README.md` 以繁體中文為主;英文在 `README.en.md`。不要改寫成維護索引。 + +## 快速定位 + +- `src/`:proxy runtime、routing、provider adapters、帳號池 +- `gui/`:dashboard +- `tests/`:Bun 測試;fork 骨架在 `tests/fork-hygiene.test.ts` +- `docs-site/`:上游公開文件(本 fork 不部署) +- `docs/fork/`:本 fork 的開發、上游、決策 +- `tools/dev_check.ps1`:Windows 本機 fork gate +- `NOTICE.md` / `SECURITY.md`:授權與安全回報 + +## 驗證 + +```powershell +bun install --frozen-lockfile +pwsh -NoProfile -File tools\dev_check.ps1 +``` + +產品行為變更再追加 `bun run typecheck` 與 `bun run test`。沒有實機跑過 proxy / 帳號池時,不要宣稱端到端可用。 diff --git a/docs-site/AGENTS.md b/docs-site/AGENTS.md index eb3fa13fac..c4f493e799 100644 --- a/docs-site/AGENTS.md +++ b/docs-site/AGENTS.md @@ -6,7 +6,7 @@ This file applies to `docs-site/` and inherits the repository-wide rules in `/AG - `docs-site/` is the public user-documentation source. - Document current shipped or intentionally pending behavior. Do not copy claims from historical `docs/` or `devlog/` material without verifying them against current code and configuration. -- English documentation is the canonical source. Translated content must not contradict it. +- English documentation is the canonical product source. This fork ships only English and Traditional Chinese (`zh-tw`). Translated content must not contradict English. - Keep commands, paths, configuration keys, defaults, branch names, and URLs synchronized with the repository. - Do not edit generated build output. diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index fb790ff743..d297de0a89 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -58,107 +58,101 @@ export default defineConfig({ baseUrl: "https://github.com/lidge-jun/opencodex/edit/main/docs-site/", }, lastUpdated: true, - // English at the site root; French under /fr, Korean under /ko, Simplified Chinese under /zh-cn, Traditional Chinese under /zh-tw, Russian under /ru, Japanese under /ja, Turkish under /tr. + // English at the site root; Traditional Chinese under /zh-tw. defaultLocale: "root", locales: { root: { label: "English", lang: "en" }, - fr: { label: "Français", lang: "fr" }, - ko: { label: "한국어", lang: "ko" }, - "zh-cn": { label: "简体中文", lang: "zh-CN" }, "zh-tw": { label: "繁體中文", lang: "zh-TW" }, - ru: { label: "Русский", lang: "ru" }, - ja: { label: "日本語", lang: "ja" }, - tr: { label: "Türkçe", lang: "tr" }, }, sidebar: [ { label: "Getting Started", - translations: { fr: "Démarrage", ko: "시작하기", "zh-CN": "开始使用", "zh-TW": "開始使用", ru: "Начало работы", ja: "はじめに", tr: "Başlangıç" }, + translations: { "zh-TW": "開始使用" }, items: [ - { label: "Installation", translations: { fr: "Installation", ko: "설치", "zh-CN": "安装", "zh-TW": "安裝", ru: "Установка", ja: "インストール", tr: "Kurulum" }, slug: "getting-started/installation" }, - { label: "Quickstart", translations: { fr: "Démarrage rapide", ko: "빠른 시작", "zh-CN": "快速开始", "zh-TW": "快速入門", ru: "Быстрый старт", ja: "クイックスタート", tr: "Hızlı Başlangıç" }, slug: "getting-started/quickstart" }, - { label: "How It Works", translations: { fr: "Fonctionnement", ko: "동작 원리", "zh-CN": "工作原理", "zh-TW": "運作原理", ru: "Как это работает", ja: "仕組み", tr: "Nasıl Çalışır" }, slug: "getting-started/how-it-works" }, - { label: "Agent Quickstart", translations: { fr: "Démarrage rapide pour les agents", ko: "에이전트 퀵스타트", "zh-CN": "Agent 快速上手", "zh-TW": "Agent 快速上手", ru: "Быстрый старт для агентов", ja: "エージェント向けクイックスタート", tr: "Ajanlar İçin Hızlı Başlangıç" }, slug: "getting-started/for-agents" }, + { label: "Installation", translations: { "zh-TW": "安裝" }, slug: "getting-started/installation" }, + { label: "Quickstart", translations: { "zh-TW": "快速入門" }, slug: "getting-started/quickstart" }, + { label: "How It Works", translations: { "zh-TW": "運作原理" }, slug: "getting-started/how-it-works" }, + { label: "Agent Quickstart", translations: { "zh-TW": "Agent 快速上手" }, slug: "getting-started/for-agents" }, ], }, { label: "Guides", - translations: { fr: "Guides", ko: "가이드", "zh-CN": "指南", "zh-TW": "指南", ru: "Руководства", ja: "ガイド", tr: "Kılavuzlar" }, + translations: { "zh-TW": "指南" }, items: [ - { label: "Providers", translations: { fr: "Fournisseurs", ko: "프로바이더", "zh-CN": "提供商", "zh-TW": "供應商", ru: "Провайдеры", ja: "プロバイダー", tr: "Sağlayıcılar" }, slug: "guides/providers" }, - { label: "Factory Droid Bridge", translations: { fr: "Pont Factory Droid", ko: "Factory Droid 브리지" }, slug: "guides/factory-droid" }, - { label: "Model Routing", translations: { fr: "Routage des modèles", ko: "모델 라우팅", "zh-CN": "模型路由", "zh-TW": "模型路由", ru: "Маршрутизация моделей", ja: "モデルルーティング", tr: "Model Yönlendirme" }, slug: "guides/model-routing" }, - { label: "Codex Integration", translations: { fr: "Intégration de Codex", ko: "Codex 통합", "zh-CN": "Codex 集成", "zh-TW": "Codex 整合", ru: "Интеграция с Codex", ja: "Codex 連携", tr: "Codex Entegrasyonu" }, slug: "guides/codex-integration" }, - { label: "Codex App Model Picker", translations: { fr: "Sélecteur de modèles de Codex App", ko: "Codex App 모델 선택기", "zh-CN": "Codex App 模型选择器", "zh-TW": "Codex App 模型選擇器", ru: "Выбор модели в Codex App", ja: "Codex App モデルピッカー", tr: "Codex App Model Seçici" }, slug: "guides/codex-app-models" }, - { label: "Model Ordering", translations: { fr: "Ordre des modèles", ko: "모델 정렬에 관하여", "zh-CN": "模型排序", "zh-TW": "模型排序", ru: "Сортировка моделей", ja: "モデルの並び順", tr: "Model Sıralaması" }, slug: "guides/model-ordering" }, - { label: "Combos", translations: { fr: "Combinaisons", ko: "콤보", "zh-CN": "组合", "zh-TW": "組合", ru: "Комбо", ja: "コンボ", tr: "Kombolar" }, slug: "guides/combos" }, - { label: "Claude Code", translations: { fr: "Claude Code", ko: "Claude Code", "zh-CN": "Claude Code", "zh-TW": "Claude Code", ru: "Claude Code", ja: "Claude Code", tr: "Claude Code" }, slug: "guides/claude-code" }, - { label: "Grok Build", translations: { fr: "Grok Build", ko: "Grok Build", "zh-CN": "Grok Build", "zh-TW": "Grok Build", ru: "Grok Build", ja: "Grok Build", tr: "Grok Build" }, slug: "guides/grok-build" }, - { label: "opencode", translations: { fr: "opencode", ko: "opencode", "zh-CN": "opencode", "zh-TW": "opencode", ru: "opencode", ja: "opencode", tr: "opencode" }, slug: "guides/opencode" }, - { label: "Pi", translations: { fr: "Pi", ko: "Pi", "zh-CN": "Pi", "zh-TW": "Pi", ru: "Pi", ja: "Pi", tr: "Pi" }, slug: "guides/pi" }, - { label: "Integrations", translations: { fr: "Intégrations", ko: "연동", "zh-CN": "集成", "zh-TW": "整合", ru: "Интеграции", ja: "連携", tr: "Entegrasyonlar" }, slug: "guides/integrations" }, - { label: "MiniMax clients", translations: { fr: "Clients MiniMax", ko: "MiniMax 클라이언트", "zh-CN": "MiniMax 客户端", "zh-TW": "MiniMax 客戶端", ru: "Клиенты MiniMax", ja: "MiniMax クライアント", tr: "MiniMax İstemcileri" }, slug: "guides/minimax" }, - { label: "Sidecars: Web Search & Vision", translations: { fr: "Services auxiliaires : recherche web et vision", ko: "사이드카: 웹 검색 & 비전", "zh-CN": "边车:网络搜索与视觉", "zh-TW": "邊車:網路搜尋與視覺", ru: "Сайдкары: веб-поиск и зрение", ja: "サイドカー: ウェブ検索 & ビジョン", tr: "Sidecar'lar: Web Arama ve Görme" }, slug: "guides/sidecars" }, - { label: "Image Bridge", translations: { fr: "Pont d’images", ko: "이미지 브릿지", "zh-CN": "图像桥接", "zh-TW": "圖像橋接", ru: "Image Bridge", ja: "画像ブリッジ", tr: "Image Bridge" }, slug: "guides/image-bridge" }, - { label: "Video Bridge", translations: { fr: "Pont vidéo", ko: "비디오 브릿지", "zh-CN": "视频桥接", "zh-TW": "影片橋接", ru: "Video Bridge", ja: "動画ブリッジ", tr: "Video Bridge" }, slug: "guides/video-bridge" }, - { label: "Web Dashboard", translations: { fr: "Tableau de bord web", ko: "웹 대시보드", "zh-CN": "网页控制台", "zh-TW": "網頁儀表板", ru: "Веб-дашборд", ja: "ウェブダッシュボード", tr: "Web Kontrol Paneli" }, slug: "guides/web-dashboard" }, - { label: "Sub-agent Surface", translations: { fr: "Interface des sous-agents", ko: "서브에이전트 서피스", "zh-CN": "子代理界面", "zh-TW": "子代理介面", ru: "Интерфейс подагентов", ja: "サブエージェントサーフェス", tr: "Alt Ajan Arayüzü" }, slug: "guides/sub-agent-surface" }, + { label: "Providers", translations: { "zh-TW": "供應商" }, slug: "guides/providers" }, + { label: "Factory Droid Bridge", slug: "guides/factory-droid" }, + { label: "Model Routing", translations: { "zh-TW": "模型路由" }, slug: "guides/model-routing" }, + { label: "Codex Integration", translations: { "zh-TW": "Codex 整合" }, slug: "guides/codex-integration" }, + { label: "Codex App Model Picker", translations: { "zh-TW": "Codex App 模型選擇器" }, slug: "guides/codex-app-models" }, + { label: "Model Ordering", translations: { "zh-TW": "模型排序" }, slug: "guides/model-ordering" }, + { label: "Combos", translations: { "zh-TW": "組合" }, slug: "guides/combos" }, + { label: "Claude Code", translations: { "zh-TW": "Claude Code" }, slug: "guides/claude-code" }, + { label: "Grok Build", translations: { "zh-TW": "Grok Build" }, slug: "guides/grok-build" }, + { label: "opencode", translations: { "zh-TW": "opencode" }, slug: "guides/opencode" }, + { label: "Pi", translations: { "zh-TW": "Pi" }, slug: "guides/pi" }, + { label: "Integrations", translations: { "zh-TW": "整合" }, slug: "guides/integrations" }, + { label: "MiniMax clients", translations: { "zh-TW": "MiniMax 客戶端" }, slug: "guides/minimax" }, + { label: "Sidecars: Web Search & Vision", translations: { "zh-TW": "邊車:網路搜尋與視覺" }, slug: "guides/sidecars" }, + { label: "Image Bridge", translations: { "zh-TW": "圖像橋接" }, slug: "guides/image-bridge" }, + { label: "Video Bridge", translations: { "zh-TW": "影片橋接" }, slug: "guides/video-bridge" }, + { label: "Web Dashboard", translations: { "zh-TW": "網頁儀表板" }, slug: "guides/web-dashboard" }, + { label: "Sub-agent Surface", translations: { "zh-TW": "子代理介面" }, slug: "guides/sub-agent-surface" }, ], }, { label: "Benchmarks", - translations: { fr: "Bancs d’essai", ko: "벤치마크", "zh-CN": "基准测试", "zh-TW": "基準測試", ru: "Бенчмарки", ja: "ベンチマーク", tr: "Kıyaslamalar" }, + translations: { "zh-TW": "基準測試" }, collapsed: true, items: [ - { label: "Overview", translations: { fr: "Vue d’ensemble", ko: "개요", "zh-CN": "概览", "zh-TW": "概覽", ru: "Обзор", ja: "概要", tr: "Genel Bakış" }, slug: "benchmarks" }, - { label: "Coding", translations: { fr: "Programmation", ko: "코딩", "zh-CN": "编程", "zh-TW": "程式設計", ru: "Кодинг", ja: "コーディング", tr: "Kodlama" }, slug: "benchmarks/coding" }, - { label: "Frontend", translations: { fr: "Frontend", ko: "프론트엔드", "zh-CN": "前端", "zh-TW": "前端", ru: "Фронтенд", ja: "フロントエンド", tr: "Ön Yüz" }, slug: "benchmarks/frontend" }, - { label: "Terminal", translations: { fr: "Terminal", ko: "터미널", "zh-CN": "终端", "zh-TW": "終端", ru: "Терминал", ja: "ターミナル", tr: "Terminal" }, slug: "benchmarks/terminal" }, - { label: "Security", translations: { fr: "Sécurité", ko: "보안", "zh-CN": "安全", "zh-TW": "安全", ru: "Безопасность", ja: "セキュリティ", tr: "Güvenlik" }, slug: "benchmarks/security" }, - { label: "Intelligence", translations: { fr: "Intelligence", ko: "인텔리전스", "zh-CN": "智能", "zh-TW": "智慧", ru: "Интеллект", ja: "インテリジェンス", tr: "Zeka" }, slug: "benchmarks/intelligence" }, + { label: "Overview", translations: { "zh-TW": "概覽" }, slug: "benchmarks" }, + { label: "Coding", translations: { "zh-TW": "程式設計" }, slug: "benchmarks/coding" }, + { label: "Frontend", translations: { "zh-TW": "前端" }, slug: "benchmarks/frontend" }, + { label: "Terminal", translations: { "zh-TW": "終端" }, slug: "benchmarks/terminal" }, + { label: "Security", translations: { "zh-TW": "安全" }, slug: "benchmarks/security" }, + { label: "Intelligence", translations: { "zh-TW": "智慧" }, slug: "benchmarks/intelligence" }, ], }, { label: "Reference", - translations: { fr: "Référence", ko: "레퍼런스", "zh-CN": "参考", "zh-TW": "參考", ru: "Справочник", ja: "リファレンス", tr: "Referans" }, + translations: { "zh-TW": "參考" }, items: [ { label: "CLI", - translations: { fr: "CLI", ko: "CLI", "zh-CN": "命令行", "zh-TW": "命令列", ru: "CLI", ja: "CLI", tr: "CLI" }, + translations: { "zh-TW": "命令列" }, items: [ - { label: "Overview", translations: { fr: "Vue d’ensemble", ko: "개요", "zh-CN": "概览", "zh-TW": "概覽", ru: "Обзор", ja: "概要", tr: "Genel Bakış" }, slug: "reference/cli" }, - { label: "Lifecycle & Service", translations: { fr: "Cycle de vie et service", ko: "라이프사이클 & 서비스", "zh-CN": "生命周期与服务", "zh-TW": "生命週期與服務", ru: "Жизненный цикл и служба", ja: "ライフサイクル & サービス", tr: "Yaşam Döngüsü ve Servis" }, slug: "reference/cli/lifecycle" }, - { label: "Providers, Accounts & Models", translations: { fr: "Fournisseurs, comptes et modèles", ko: "프로바이더, 계정 & 모델", "zh-CN": "提供商、账户与模型", "zh-TW": "供應商、帳號與模型", ru: "Провайдеры, аккаунты и модели", ja: "プロバイダー・アカウント・モデル", tr: "Sağlayıcılar, Hesaplar ve Modeller" }, slug: "reference/cli/providers-accounts" }, - { label: "Agents, Routing & Integrations", translations: { fr: "Agents, routage et intégrations", ko: "에이전트, 라우팅 & 통합", "zh-CN": "代理、路由与集成", "zh-TW": "代理、路由與整合", ru: "Агенты, маршрутизация и интеграции", ja: "エージェント・ルーティング・連携", tr: "Ajanlar, Yönlendirme ve Entegrasyonlar" }, slug: "reference/cli/agents" }, + { label: "Overview", translations: { "zh-TW": "概覽" }, slug: "reference/cli" }, + { label: "Lifecycle & Service", translations: { "zh-TW": "生命週期與服務" }, slug: "reference/cli/lifecycle" }, + { label: "Providers, Accounts & Models", translations: { "zh-TW": "供應商、帳號與模型" }, slug: "reference/cli/providers-accounts" }, + { label: "Agents, Routing & Integrations", translations: { "zh-TW": "代理、路由與整合" }, slug: "reference/cli/agents" }, ], }, { label: "Configuration", - translations: { fr: "Configuration", ko: "설정", "zh-CN": "配置", "zh-TW": "設定", ru: "Конфигурация", ja: "設定", tr: "Yapılandırma" }, + translations: { "zh-TW": "設定" }, items: [ - { label: "Overview", translations: { fr: "Vue d’ensemble", ko: "개요", "zh-CN": "概览", "zh-TW": "概覽", ru: "Обзор", ja: "概要", tr: "Genel Bakış" }, slug: "reference/configuration" }, - { label: "Providers", translations: { fr: "Fournisseurs", ko: "프로바이더", "zh-CN": "提供商", "zh-TW": "供應商", ru: "Провайдеры", ja: "プロバイダー", tr: "Sağlayıcılar" }, slug: "reference/configuration/providers" }, - { label: "Routing", translations: { fr: "Routage", ko: "라우팅", "zh-CN": "路由", "zh-TW": "路由", ru: "Маршрутизация", ja: "ルーティング", tr: "Yönlendirme" }, slug: "reference/configuration/routing" }, - { label: "Agents", translations: { fr: "Agents", ko: "에이전트", "zh-CN": "代理", "zh-TW": "代理", ru: "Агенты", ja: "エージェント", tr: "Ajanlar" }, slug: "reference/configuration/agents" }, - { label: "Server & Runtime", translations: { fr: "Serveur et environnement d’exécution", ko: "서버 & 런타임", "zh-CN": "服务器与运行时", "zh-TW": "伺服器與執行階段", ru: "Сервер и рантайм", ja: "サーバー & ランタイム", tr: "Sunucu ve Çalışma Zamanı" }, slug: "reference/configuration/server" }, + { label: "Overview", translations: { "zh-TW": "概覽" }, slug: "reference/configuration" }, + { label: "Providers", translations: { "zh-TW": "供應商" }, slug: "reference/configuration/providers" }, + { label: "Routing", translations: { "zh-TW": "路由" }, slug: "reference/configuration/routing" }, + { label: "Agents", translations: { "zh-TW": "代理" }, slug: "reference/configuration/agents" }, + { label: "Server & Runtime", translations: { "zh-TW": "伺服器與執行階段" }, slug: "reference/configuration/server" }, ], }, - { label: "Adapters", translations: { fr: "Adaptateurs", ko: "어댑터", "zh-CN": "适配器", "zh-TW": "適配器", ru: "Адаптеры", ja: "アダプター", tr: "Adaptörler" }, slug: "reference/adapters" }, - { label: "Architecture", translations: { fr: "Architecture", ko: "아키텍처", "zh-CN": "架构", "zh-TW": "架構", ru: "Архитектура", ja: "アーキテクチャ", tr: "Mimari" }, slug: "reference/architecture" }, - { label: "Proxy API Formats", translations: { fr: "Formats de l’API proxy", ko: "프록시 API 형식", "zh-CN": "代理 API 格式", "zh-TW": "代理 API 格式", ru: "Форматы API прокси", ja: "プロキシAPI形式", tr: "Proxy API Formatları" }, slug: "reference/proxy-formats" }, - { label: "Management API", translations: { fr: "API de gestion", ko: "관리 API", "zh-CN": "管理 API", "zh-TW": "管理 API", ru: "API управления", ja: "管理API", tr: "Yönetim API'si" }, slug: "reference/management-api" }, + { label: "Adapters", translations: { "zh-TW": "適配器" }, slug: "reference/adapters" }, + { label: "Architecture", translations: { "zh-TW": "架構" }, slug: "reference/architecture" }, + { label: "Proxy API Formats", translations: { "zh-TW": "代理 API 格式" }, slug: "reference/proxy-formats" }, + { label: "Management API", translations: { "zh-TW": "管理 API" }, slug: "reference/management-api" }, ], }, { label: "Troubleshooting", - translations: { fr: "Dépannage", ko: "문제 해결", "zh-CN": "故障排除", "zh-TW": "疑難排解", ru: "Устранение неполадок", ja: "トラブルシューティング", tr: "Sorun Giderme" }, + translations: { "zh-TW": "疑難排解" }, collapsed: true, items: [ - { label: "Windows Memory Growth", translations: { fr: "Augmentation de la mémoire sous Windows", ko: "Windows 메모리 증가", "zh-CN": "Windows 内存增长", "zh-TW": "Windows 記憶體增長", ru: "Рост памяти в Windows", ja: "Windows メモリ増加", tr: "Windows Bellek Artışı" }, slug: "troubleshooting/windows-memory" }, - { label: "Disk Usage from Temp Files", translations: { fr: "Espace disque et fichiers temporaires", ko: "임시 파일 디스크 사용량", "zh-CN": "临时文件磁盘占用", "zh-TW": "暫存檔磁碟用量", ru: "Использование диска временными файлами", ja: "一時ファイルのディスク使用量", tr: "Geçici Dosya Disk Kullanımı" }, slug: "troubleshooting/disk-usage-temp-files" }, + { label: "Windows Memory Growth", translations: { "zh-TW": "Windows 記憶體增長" }, slug: "troubleshooting/windows-memory" }, + { label: "Disk Usage from Temp Files", translations: { "zh-TW": "暫存檔磁碟用量" }, slug: "troubleshooting/disk-usage-temp-files" }, ], }, - { label: "Contributing", translations: { fr: "Contribuer", ko: "기여하기", "zh-CN": "贡献", "zh-TW": "貢獻", ru: "Как внести вклад", ja: "コントリビュート", tr: "Katkıda Bulunma" }, slug: "contributing" }, + { label: "Contributing", translations: { "zh-TW": "貢獻" }, slug: "contributing" }, ], }), ], diff --git a/docs-site/src/components/FrontierBoards.astro b/docs-site/src/components/FrontierBoards.astro index 2cd29564cb..e478b361c4 100644 --- a/docs-site/src/components/FrontierBoards.astro +++ b/docs-site/src/components/FrontierBoards.astro @@ -10,7 +10,7 @@ import data from "../data/frontier-benchmarks.json"; import { FRONTIER_STRINGS } from "../data/frontier-i18n"; interface Props { - locale?: "en" | "fr" | "ko" | "zh-cn" | "zh-tw" | "ru" | "ja" | "tr"; + locale?: "en" | "zh-tw"; /** Comma-separated board ids to render; omit for all boards. */ boards?: string; /** Show the page-level subtitle (overview pages only). */ @@ -26,7 +26,7 @@ const fill = (key: string, vars: Record): string => Object.entries(vars).reduce((acc, [k, v]) => acc.replace(`{${k}}`, v), t(key)); // The PR i18n has no column-header key for "score" — keep a tiny local map. -const SCORE_HEADER: Record = { en: "Score", fr: "Score", ko: "점수", "zh-cn": "得分", "zh-tw": "得分", ru: "Балл", ja: "スコア", tr: "Puan" }; +const SCORE_HEADER: Record = { en: "Score", "zh-tw": "得分" }; /** Dataset tags are kebab-case; i18n keys are camelCase (cheap-subagent → cheapSubagent). */ const tagKey = (tag: string): string => tag.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); diff --git a/docs-site/src/components/Header.astro b/docs-site/src/components/Header.astro index 5bc9fc7435..e5ea2c2507 100644 --- a/docs-site/src/components/Header.astro +++ b/docs-site/src/components/Header.astro @@ -19,53 +19,12 @@ const prefix = locale ? `${base}${locale}/` : base; type NavLink = { label: string; href: string }; type NavGroup = { label: string; links: NavLink[] }; -const fr = { - 'Auto': 'Automatique', - 'Light': 'Clair', - 'Dark': 'Sombre', - 'Theme': 'Thème', - 'Click to use the next theme': 'Cliquez pour passer au thème suivant', - 'Getting Started': 'Démarrage', - 'Installation': 'Installation', - 'Quickstart': 'Démarrage rapide', - 'How It Works': 'Fonctionnement', - 'Agent Quickstart': 'Démarrage rapide pour les agents', - 'Guides': 'Guides', - 'Providers': 'Fournisseurs', - 'Model Routing': 'Routage des modèles', - 'Combos': 'Combinaisons', - 'Codex Integration': 'Intégration de Codex', - 'Codex App Model Picker': 'Sélecteur de modèles de Codex App', - 'Sidecars: Search & Vision': 'Services auxiliaires : recherche et vision', - 'Image Bridge': 'Pont d’images', - 'Video Bridge': 'Pont vidéo', - 'Web Dashboard': 'Tableau de bord web', - 'Sub-agent Surface': 'Interface des sous-agents', - 'Reference': 'Référence', - 'Configuration': 'Configuration', - 'Adapters': 'Adaptateurs', - 'Architecture': 'Architecture', - 'Proxy API Formats': 'Formats de l’API proxy', - 'Management API': 'API de gestion', - 'Contributing': 'Contribuer', - 'Main': 'Navigation principale', - 'Select language': 'Choisir la langue', - 'Menu': 'Menu', -} as const; +const t = (en: string, zhTW: string): string => locale === 'zh-tw' ? zhTW : en; -const t = (en: K, ko: string, zh: string, ru: string, ja: string, zhTW: string, tr: string): string => - locale === 'fr' ? fr[en] : locale === 'ko' ? ko : locale === 'zh-cn' ? zh : locale === 'zh-tw' ? zhTW : locale === 'ru' ? ru : locale === 'ja' ? ja : locale === 'tr' ? tr : en; - -type LanguageOption = { code: 'root' | 'fr' | 'ko' | 'zh-cn' | 'zh-tw' | 'ru' | 'ja' | 'tr'; label: string }; +type LanguageOption = { code: 'root' | 'zh-tw'; label: string }; const languages: LanguageOption[] = [ { code: 'root', label: 'English' }, - { code: 'fr', label: 'Français' }, - { code: 'ko', label: '한국어' }, - { code: 'zh-cn', label: '简体中文' }, { code: 'zh-tw', label: '繁體中文' }, - { code: 'ru', label: 'Русский' }, - { code: 'ja', label: '日本語' }, - { code: 'tr', label: 'Türkçe' }, ]; const currentLanguage = languages.find(({ code }) => code === (locale ?? 'root')) ?? languages[0]; @@ -79,62 +38,54 @@ const languageHref = (code: LanguageOption['code']) => `${code === 'root' ? base : `${base}${code}/`}${routeSuffix}`; const themeLabels = { - auto: t('Auto', '자동', '自动', 'Авто', '自動', '自動', 'Otomatik'), - light: t('Light', '라이트', '浅色', 'Светлая', 'ライト', '淺色', 'Açık'), - dark: t('Dark', '다크', '深色', 'Тёмная', 'ダーク', '深色', 'Koyu'), + auto: t('Auto', '自動'), + light: t('Light', '淺色'), + dark: t('Dark', '深色'), }; -const themeName = t('Theme', '테마', '主题', 'Тема', 'テーマ', '主題', 'Tema'); -const themeCycleHint = t( - 'Click to use the next theme', - '클릭하여 다음 테마로 전환', - '点击切换到下一个主题', - 'Нажмите, чтобы включить следующую тему', - 'クリックして次のテーマに切り替え', - '點選切換到下一個主題', - 'Sonraki temaya geçmek için tıklayın' -); +const themeName = t('Theme', '主題'); +const themeCycleHint = t('Click to use the next theme', '點選切換到下一個主題'); const groups: NavGroup[] = [ { - label: t('Getting Started', '시작하기', '开始使用', 'Начало работы', 'はじめに', '開始使用', 'Başlangıç'), + label: t('Getting Started', '開始使用'), links: [ - { label: t('Installation', '설치', '安装', 'Установка', 'インストール', '安裝', 'Kurulum'), href: `${prefix}getting-started/installation/` }, - { label: t('Quickstart', '빠른 시작', '快速开始', 'Быстрый старт', 'クイックスタート', '快速入門', 'Hızlı Başlangıç'), href: `${prefix}getting-started/quickstart/` }, - { label: t('How It Works', '동작 원리', '工作原理', 'Как это работает', '仕組み', '運作原理', 'Nasıl Çalışır'), href: `${prefix}getting-started/how-it-works/` }, - { label: t('Agent Quickstart', '에이전트 퀵스타트', 'Agent 快速上手', 'Быстрый старт для агентов', 'エージェント向けクイックスタート', 'Agent 快速上手', 'Ajanlar İçin Hızlı Başlangıç'), href: `${prefix}getting-started/for-agents/` }, + { label: t('Installation', '安裝'), href: `${prefix}getting-started/installation/` }, + { label: t('Quickstart', '快速入門'), href: `${prefix}getting-started/quickstart/` }, + { label: t('How It Works', '運作原理'), href: `${prefix}getting-started/how-it-works/` }, + { label: t('Agent Quickstart', 'Agent 快速上手'), href: `${prefix}getting-started/for-agents/` }, ], }, { - label: t('Guides', '가이드', '指南', 'Руководства', 'ガイド', '指南', 'Kılavuzlar'), + label: t('Guides', '指南'), links: [ - { label: t('Providers', '프로바이더', '提供商', 'Провайдеры', 'プロバイダー', '供應商', 'Sağlayıcılar'), href: `${prefix}guides/providers/` }, - { label: t('Model Routing', '모델 라우팅', '模型路由', 'Маршрутизация моделей', 'モデルルーティング', '模型路由', 'Model Yönlendirme'), href: `${prefix}guides/model-routing/` }, - { label: t('Combos', '콤보', '组合', 'Комбо', 'コンボ', '組合', 'Kombolar'), href: `${prefix}guides/combos/` }, - { label: t('Codex Integration', 'Codex 통합', 'Codex 集成', 'Интеграция с Codex', 'Codex 連携', 'Codex 整合', 'Codex Entegrasyonu'), href: `${prefix}guides/codex-integration/` }, - { label: t('Codex App Model Picker', 'Codex App 모델 선택기', 'Codex App 模型选择器', 'Селектор моделей Codex App', 'Codex App モデルピッカー', 'Codex App 模型選擇器', 'Codex App Model Seçici'), href: `${prefix}guides/codex-app-models/` }, + { label: t('Providers', '供應商'), href: `${prefix}guides/providers/` }, + { label: t('Model Routing', '模型路由'), href: `${prefix}guides/model-routing/` }, + { label: t('Combos', '組合'), href: `${prefix}guides/combos/` }, + { label: t('Codex Integration', 'Codex 整合'), href: `${prefix}guides/codex-integration/` }, + { label: t('Codex App Model Picker', 'Codex App 模型選擇器'), href: `${prefix}guides/codex-app-models/` }, { label: 'Claude Code', href: `${prefix}guides/claude-code/` }, - { label: t('Sidecars: Search & Vision', '사이드카: 검색 & 비전', '边车:搜索与视觉', 'Сайдкары: поиск и зрение', 'サイドカー: 検索 & ビジョン', '邊車:搜尋與視覺', "Sidecar'lar: Arama & Görme"), href: `${prefix}guides/sidecars/` }, - { label: t('Image Bridge', '이미지 브릿지', '图像桥接', 'Image Bridge', '画像ブリッジ', '圖像橋接', 'Image Bridge'), href: `${prefix}guides/image-bridge/` }, - { label: t('Video Bridge', '비디오 브릿지', '视频桥接', 'Video Bridge', '動画ブリッジ', '影片橋接', 'Video Bridge'), href: `${prefix}guides/video-bridge/` }, - { label: t('Web Dashboard', '웹 대시보드', '网页控制台', 'Веб-дашборд', 'ウェブダッシュボード', '網頁儀表板', 'Web Kontrol Paneli'), href: `${prefix}guides/web-dashboard/` }, - { label: t('Sub-agent Surface', '서브에이전트 서피스', '子代理界面', 'Интерфейс подагентов', 'サブエージェントサーフェス', '子代理介面', 'Alt Ajan Arayüzü'), href: `${prefix}guides/sub-agent-surface/` }, + { label: t('Sidecars: Search & Vision', '邊車:搜尋與視覺'), href: `${prefix}guides/sidecars/` }, + { label: t('Image Bridge', '圖像橋接'), href: `${prefix}guides/image-bridge/` }, + { label: t('Video Bridge', '影片橋接'), href: `${prefix}guides/video-bridge/` }, + { label: t('Web Dashboard', '網頁儀表板'), href: `${prefix}guides/web-dashboard/` }, + { label: t('Sub-agent Surface', '子代理介面'), href: `${prefix}guides/sub-agent-surface/` }, ], }, { - label: t('Reference', '레퍼런스', '参考', 'Справочник', 'リファレンス', '參考', 'Referans'), + label: t('Reference', '參考'), links: [ { label: 'CLI', href: `${prefix}reference/cli/` }, - { label: t('Configuration', '설정', '配置', 'Конфигурация', '設定', '設定', 'Yapılandırma'), href: `${prefix}reference/configuration/` }, - { label: t('Adapters', '어댑터', '适配器', 'Адаптеры', 'アダプター', '適配器', 'Adaptörler'), href: `${prefix}reference/adapters/` }, - { label: t('Architecture', '아키텍처', '架构', 'Архитектура', 'アーキテクチャ', '架構', 'Mimari'), href: `${prefix}reference/architecture/` }, - { label: t('Proxy API Formats', '프록시 API 형식', '代理 API 格式', 'Форматы API прокси', 'プロキシAPI形式', '代理 API 格式', 'Proxy API Formatları'), href: `${prefix}reference/proxy-formats/` }, - { label: t('Management API', '관리 API', '管理 API', 'API управления', '管理API', '管理 API', "Yönetim API'si"), href: `${prefix}reference/management-api/` }, + { label: t('Configuration', '設定'), href: `${prefix}reference/configuration/` }, + { label: t('Adapters', '適配器'), href: `${prefix}reference/adapters/` }, + { label: t('Architecture', '架構'), href: `${prefix}reference/architecture/` }, + { label: t('Proxy API Formats', '代理 API 格式'), href: `${prefix}reference/proxy-formats/` }, + { label: t('Management API', '管理 API'), href: `${prefix}reference/management-api/` }, ], }, ]; const contributing: NavLink = { - label: t('Contributing', '기여하기', '贡献', 'Участие в проекте', 'コントリビュート', '貢獻', 'Katkıda Bulunma'), + label: t('Contributing', '貢獻'), href: `${prefix}contributing/`, }; --- @@ -143,7 +94,7 @@ const contributing: NavLink = {
-