diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index e35224af54..5b34a44613 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -242,6 +242,19 @@ pub fn run() { // to the loopback dashboard by `capabilities/dashboard-zoom.json`. .zoom_hotkeys_enabled(true) .on_navigation(window::navigation_allowed(app.handle().clone())) + // A hidden window still loads pages: wry builds this one with WebView2 + // IsVisible=false, and the bootstrap page navigates to the dashboard URL + // afterwards, so the eval that a later show or hide would rely on has nowhere + // to land during a reload. Re-sending the current state here is what keeps the + // GUI's answer correct across navigation. + .on_page_load(|window, payload| { + if matches!(payload.event(), tauri::webview::PageLoadEvent::Finished) { + window::report_visibility( + &window, + window.is_visible().unwrap_or(false), + ); + } + }) .build()?; window::configure(&window); if startup::LaunchOrigin::detect() == startup::LaunchOrigin::User { @@ -260,6 +273,11 @@ pub fn run() { .build(tauri::generate_context!()) .expect("error while building OpenCodex desktop shell") .run(|app, event| { + // Dock/Finder reopening an existing macOS app does not launch a second instance. + #[cfg(target_os = "macos")] + if let tauri::RunEvent::Reopen { .. } = event { + show_dashboard(app.clone()); + } // Window close and the platform quit gesture arrive here as an exit request, and until // this handler existed they went straight through to a SIGKILL of the runtime. D2 makes // them hide; only the tray's Quit, and an update's coordinated restart, get past. diff --git a/desktop/src-tauri/src/window.rs b/desktop/src-tauri/src/window.rs index a886fb0776..c61887b06f 100644 --- a/desktop/src-tauri/src/window.rs +++ b/desktop/src-tauri/src/window.rs @@ -79,14 +79,37 @@ fn is_app_origin(url: &Url) -> bool { pub fn show(window: &WebviewWindow) { let _ = window.show(); let _ = window.set_focus(); + report_visibility(window, true); apply_tray_policy(window.app_handle(), true); } pub fn hide(window: &WebviewWindow) { let _ = window.hide(); + report_visibility(window, false); apply_tray_policy(window.app_handle(), false); } +/// Tell the main window's page whether its host window is visible. +/// +/// Windows WebView2 does not flip `document.visibilityState` when the host window is hidden +/// (tauri issues #10592 and #6864), so the dashboard's pollers keep running while the app sits in +/// the tray; macOS WKWebView does flip it. Publishing the host's own answer gives the GUI one +/// signal on every platform instead of one that is correct on only some of them. +/// +/// Only the `main` window publishes: `exit::hide_windows` hides every window through `hide`, +/// and the tray popup carries its own equivalent bridge, so an unguarded report would claim the +/// dashboard was hidden because a popup was. A page that has not loaded yet simply misses the eval; +/// the page-load hook re-sends the current state. +pub fn report_visibility(window: &WebviewWindow, visible: bool) { + if window.label() != "main" { + return; + } + let script = format!( + "window.__OPENCODEX_HOST_VISIBLE__ = {visible}; window.dispatchEvent(new CustomEvent('opencodex:host-visibility', {{detail: {visible}}}));" + ); + let _ = window.eval(script); +} + #[cfg(target_os = "macos")] fn apply_tray_policy(app: &AppHandle, visible: bool) { let policy = if visible { diff --git a/docs-site/src/content/docs/fr/guides/desktop-app.md b/docs-site/src/content/docs/fr/guides/desktop-app.md index ee633d9205..e5e42513de 100644 --- a/docs-site/src/content/docs/fr/guides/desktop-app.md +++ b/docs-site/src/content/docs/fr/guides/desktop-app.md @@ -44,6 +44,8 @@ L’application demande à son CLI intégré d’exécuter `ocx resolve --json` Utilisez l’action **Open dashboard** ou **Open in browser** de la zone de notification pour passer du tableau de bord intégré à votre navigateur habituel. Le menu permet aussi de rechercher les mises à jour. +Sur macOS, fermer le tableau de bord laisse l’application active dans la barre des menus. Ouvrez à nouveau OpenCodex depuis le Dock ou le Finder pour réafficher le tableau de bord sans redémarrer le proxy. + ## Utilisation dans la zone de notification Sur macOS et Windows, cliquez sur l’icône pour ouvrir un panneau compact d’utilisation. L’action **Show usage** l’ouvre également, notamment sous Linux lorsque la zone de notification ne transmet pas les clics. Sous Linux, le tableau de bord s’ouvre au démarrage, même si l’environnement de bureau n’affiche pas d’icône. diff --git a/docs-site/src/content/docs/fr/guides/integrations.md b/docs-site/src/content/docs/fr/guides/integrations.md index 1f3e04ada5..fa50e57a3d 100644 --- a/docs-site/src/content/docs/fr/guides/integrations.md +++ b/docs-site/src/content/docs/fr/guides/integrations.md @@ -134,6 +134,8 @@ Kimi Code, gjc, MiniMax Code et Raycast — documents YAML, JSON5 et TOML rééc d'opencodex ont été modifiées, le commutateur se verrouille et la désactivation est refusée plutôt que de deviner quelles modifications vous appartiennent. +Exception pour Hermes : l'ajout de `session_affinity_header: session-id` seul dans un bloc déjà géré peut être adopté via **Apply** ; toute autre modification d'un champ géré reste un conflit. Jusqu'à cette application, l'actualisation automatique de la liste des modèles est également suspendue. Le réglage concerne tous les modèles du provider et nécessite une version de Hermes qui le prend en charge ; il ne garantit aucun taux de succès du cache. Voir le [guide de mise à niveau en anglais](/guides/integrations/#hermes-session-affinity). + ## Prévisualiser et confirmer les modifications Appliquer, Remplacer, Désactiver et Restaurer commencent désormais par un aperçu. La boîte de dialogue diff --git a/docs-site/src/content/docs/fr/guides/sidecars.md b/docs-site/src/content/docs/fr/guides/sidecars.md index 367b98d9c3..1fcc91cfec 100644 --- a/docs-site/src/content/docs/fr/guides/sidecars.md +++ b/docs-site/src/content/docs/fr/guides/sidecars.md @@ -164,6 +164,16 @@ le délai d'attente et la limite précédemment choisis. les clés omises inchangées. `timeoutMs` utilise les limites entières de l'environnement d'exécution (1–2147483647 ms). +La carte du service auxiliaire de recherche web reprend la même forme de contrôle : la première +ligne du sélecteur de modèle est **Désactivé (Off)**. La désactivation arrête l'interception de +`web_search` par OpenCodex et l'intégration Codex écrit `web_search = "disabled"` dans +`~/.codex/config.toml`, car Codex continue sinon d'annoncer son propre outil hébergé +`web_search` natif, ce qu'il faut lorsqu'un serveur de recherche MCP doit être le seul chemin +de recherche. La réactivation supprime cette ligne et rétablit la ligne racine `web_search` +écrite par l'opérateur, enregistrée dans le journal Codex. L'écriture exige un +`~/.codex/config.toml` géré (`ocx sync`) ; la carte du tableau de bord vous avertit +lorsqu'elle n'a pas eu lieu et `ocx agent sidecar web --enabled off` indique si elle a réussi. + Vous pouvez toujours définir `enabled: false` dans `config.json` si vous préférez modifier le fichier directement. La recherche et la description d'images avec OAuth Anthropic réutilisent les identifiants Claude Code existants du magasin d'empreintes précédent. Testez néanmoins ce comportement avec le diff --git a/docs-site/src/content/docs/fr/guides/web-dashboard.md b/docs-site/src/content/docs/fr/guides/web-dashboard.md index 1e4c90f506..f82047f2ec 100644 --- a/docs-site/src/content/docs/fr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/fr/guides/web-dashboard.md @@ -37,6 +37,25 @@ remplissage automatique. Le tableau de bord lui-même ne conserve le jeton qu'en dans `localStorage` ni dans `sessionStorage` ; son enregistrement dépend entièrement du navigateur ou du gestionnaire de mots de passe. +## Barre de résumé des quotas + +Une ligne de résumé en haut de chaque page, sauf la page Sécurité au démarrage, indique +l'utilisation actuelle des quotas de chaque fournisseur, par exemple +`OpenAI 31% | Claude 54% | xAI 12% | Google 8%`. Elle lit les mêmes rapports de quotas que l'espace +fournisseur (`GET /api/provider-quotas`, toutes les 60 secondes tant que l'onglet est visible) et ne +force jamais d'actualisation en amont. + +- Chaque étiquette affiche la fenêtre signalée prioritaire : d'abord hebdomadaire, puis mensuelle, + puis 5 heures, puis une fenêtre nommée par le fournisseur ou des crédits prépayés. +- Une étiquette passe en ambre à 70 % d'utilisation et en rouge à 90 %. +- Survolez une étiquette ou cliquez dessus pour voir toutes les fenêtres signalées avec leur heure de + réinitialisation et l'heure de la lecture. Appuyez sur Échap ou cliquez ailleurs pour fermer une + étiquette épinglée. +- Les fournisseurs qui ne signalent aucune fenêtre de quota sont omis. La barre est masquée quand + aucun fournisseur n'en signale. +- Le bord droit indique quand le tableau de bord a lu les rapports pour la dernière fois. Il passe en + ambre lorsque la dernière lecture a échoué et que la lecture précédente est encore affichée. + ## Fonctions disponibles | Zone | Fonction | diff --git a/docs-site/src/content/docs/fr/reference/cli/agents.md b/docs-site/src/content/docs/fr/reference/cli/agents.md index 75e2777cec..b63eb62f9c 100644 --- a/docs-site/src/content/docs/fr/reference/cli/agents.md +++ b/docs-site/src/content/docs/fr/reference/cli/agents.md @@ -15,8 +15,18 @@ les modes de surface, la délégation, l'effort et le comportement de repli s'em ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off` est le même interrupteur que la ligne **Désactivé (Off)** du tableau de bord : +OpenCodex cesse d'exécuter le service auxiliaire et l'intégration Codex écrit +`web_search = "disabled"` dans `~/.codex/config.toml`, ce qui permet à un serveur de +recherche MCP d'être le seul chemin de recherche. `--enabled on` supprime à nouveau cette ligne. +Lorsque l'enregistrement déplace réellement l'interrupteur, la commande signale l'écriture côté Codex +qu'elle a déclenchée (`codexWebSearch` avec `--json`, une ligne `Codex config:` +sinon) et renvoie vers `ocx sync` quand elle n'a pas pu avoir lieu. L'option fonctionne aussi +pour `vision`. + ### `ocx v2 |threads |mode-hint >` Gérez l'indicateur de fonctionnalité Codex `multi_agent_v2` et le mode surface multi-agents à trois états. diff --git a/docs-site/src/content/docs/fr/reference/configuration/server.md b/docs-site/src/content/docs/fr/reference/configuration/server.md index ffa3e2e1d8..95c6db1198 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/server.md +++ b/docs-site/src/content/docs/fr/reference/configuration/server.md @@ -221,7 +221,7 @@ l'API Images d'OpenAI et la forme de réponse attendue par Codex. | Champ | Type | Par défaut | Signification | | --- | --- | --- | --- | -| `enabled?` | `boolean` | activé lorsqu'il est utilisable | Interrupteur principal. | +| `enabled?` | `boolean` | activé lorsqu'il est utilisable | Interrupteur principal. Avec `false`, OpenCodex cesse d'intercepter `web_search` et l'intégration Codex écrit `web_search = "disabled"` dans `~/.codex/config.toml`. | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | Une valeur explicite est prioritaire ; l'absence de valeur sélectionne toujours `openai`. `anthropic` et `xai` ne s'exécutent que s'ils sont configurés explicitement ; `gemini` et `exa` restent réservés jusqu'à la livraison de leur executor. | | `model?` | `string` | dépendant du backend | `gpt-5.6-luna` pour OpenAI, `claude-sonnet-5` pour Anthropic ou `grok-4.6` pour xAI. L'héritage explicite `gpt-5.4-mini` migre au démarrage. | | `exaApiKey?` | `string` | aucun | Clé opérateur pour le backend `exa`. Écriture seule : les lectures de gestion ne renvoient jamais la valeur stockée. | diff --git a/docs-site/src/content/docs/guides/desktop-app.md b/docs-site/src/content/docs/guides/desktop-app.md index df6541e21f..31ee4150d8 100644 --- a/docs-site/src/content/docs/guides/desktop-app.md +++ b/docs-site/src/content/docs/guides/desktop-app.md @@ -59,6 +59,8 @@ it from the tray or launch the app again. Use the tray's **Open dashboard** or **Open in browser** action to move between the embedded dashboard and your normal browser. The tray also provides update checks. +On macOS, closing the dashboard keeps the app running in the menu bar. Open OpenCodex again from Dock or Finder to restore the dashboard without restarting the proxy. + ## Usage in the tray On macOS and Windows, click the tray icon to open a compact usage window. The tray's diff --git a/docs-site/src/content/docs/guides/integrations.md b/docs-site/src/content/docs/guides/integrations.md index ee28e6e7ad..71549cf66e 100644 --- a/docs-site/src/content/docs/guides/integrations.md +++ b/docs-site/src/content/docs/guides/integrations.md @@ -200,6 +200,28 @@ undoable. The switch itself stays locked, because the switch cannot know which e you meant to keep — only you can say so. Nothing else is relaxed: a file we cannot parse, or one whose structure we cannot reason about, still refuses. +## Hermes session affinity + +The generated `providers.opencodex` block includes `session_affinity_header: session-id` for all +models. This names a header; Hermes supplies its dynamic conversation identifier. OpenCodex does +not write a shared static identifier or change `api_mode` to enable affinity. + +Use a Hermes version supporting [per-provider request options](https://hermes-agent.nousresearch.com/docs/user-guide/configuring-models#per-provider-request-options). +Older versions may ignore or discard the option; a valid configuration alone does not prove that +Hermes sends the header. Conversation isolation, compaction lineage and auxiliary/child requests +follow Hermes' affinity semantics. This setting does not guarantee a particular cache-hit rate. + +For an existing managed integration, open **Integrations → Hermes**, review **Apply**, and confirm +the update. Until then, it shows **Update needed** and implicit catalog refresh leaves it unchanged, +including its model list. Reading the page does not upgrade the configuration. After Apply, normal +catalog refresh resumes and retains the setting; **Replace** also includes it. + +If you already added exactly `session_affinity_header: session-id` inside the managed block, Apply +can adopt it when all other managed settings still match the ownership record. This is the narrow +exception to the conflict rule above: other edits, a different header name, or a block without a +matching ownership record still require conflict resolution. Unrelated YAML settings and comments +remain untouched, and the existing snapshot and Restore workflow applies to the upgrade. + ## Preview and confirm changes Apply, Replace, Disable, and Restore now begin with a preview. The dialog shows exactly which diff --git a/docs-site/src/content/docs/guides/sidecars.md b/docs-site/src/content/docs/guides/sidecars.md index 1f80f4fbc9..022b02faa3 100644 --- a/docs-site/src/content/docs/guides/sidecars.md +++ b/docs-site/src/content/docs/guides/sidecars.md @@ -207,6 +207,29 @@ timeout, and limit. omitted keys unchanged. `timeoutMs` uses the runtime integer bounds (1–2147483647 ms). +The web-search sidecar card carries the same control shape: the model picker's first row is +**Off**. Off does two things, and the second one is the reason the row exists. OpenCodex stops +intercepting `web_search`, and the Codex integration writes Codex's own +`web_search = "disabled"` mode into `~/.codex/config.toml` — because Codex keeps declaring its +native hosted `web_search` tool until its own mode says otherwise, and the tool a client +advertises is the one the model reaches for. An operator who wants an MCP search server to be +the only search path needs both halves; otherwise the model keeps calling the native tool. + +`web_search` is Codex's key with its own value space (`disabled`, `cached`, `indexed`, `live`). +OpenCodex only ever writes `disabled` while the sidecar is off, and removes its marker-owned line +again once the sidecar is back on — a re-enabled sidecar whose client still had the native tool +switched off would have nothing to intercept. The write needs a managed `~/.codex/config.toml` (`ocx +sync`); the management response reports it as `codexWebSearch`, and both surfaces that can show it +do: the Dashboard's web-search card warns when the write did not happen, and `ocx agent sidecar web +--enabled off` prints whether it happened. Only a save that moves the switch triggers the write, so +the ordinary "nothing changed" answer reports `not_requested` and prints nothing extra. A root +`web_search` line the operator set by hand is replaced while the sidecar is off, since two root keys +of the same name are not valid TOML. Its exact text is recorded in the Codex journal and put back in +its place when the sidecar is switched on again — including for a line added after the journal +snapshot was taken, which `ocx restore` alone cannot cover. The same record is what still +recognizes our own `disabled` line when the Codex app has rewritten `config.toml` and dropped the +comment that named its owner. + You can still set `enabled: false` in `config.json` if you prefer to edit the file directly. Anthropic-OAuth search and image description reuse the existing Claude Code OAuth fingerprint precedent, but should be soak-tested with the diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index 72c1a821d9..414c590274 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -76,6 +76,22 @@ one column and model/effort controls share another. On narrower screens, control labels in the same reading order. Long version labels are shortened visually; hover the version badge or the version value to read the full value. +### Quota summary bar + +A one-line summary at the top of every page except the Startup page shows each provider's current +quota usage, for example `OpenAI 31% | Claude 54% | xAI 12% | Google 8%`. It reads the same provider +quota reports as the Providers workspace (`GET /api/provider-quotas`, every 60 seconds while the tab +is visible) and never forces an upstream refresh. + +- Each chip shows the preferred reported window: weekly first, then monthly, then 5-hour, then a + provider-named window or prepaid credits. +- A chip turns amber at 70% used and red at 90% used. +- Hover or click a chip to see every reported window with its reset time and the time the reading + was taken. Press Escape or click elsewhere to close a pinned chip. +- Providers that report no quota window are left out. The bar is hidden when no provider reports one. +- The right edge shows when the dashboard last read the reports. It turns amber when the latest + read failed and the previous reading is still shown. + ## What you can do | Area | What it does | diff --git a/docs-site/src/content/docs/ja/guides/desktop-app.md b/docs-site/src/content/docs/ja/guides/desktop-app.md index c9ceaba268..ee829b46ed 100644 --- a/docs-site/src/content/docs/ja/guides/desktop-app.md +++ b/docs-site/src/content/docs/ja/guides/desktop-app.md @@ -44,6 +44,8 @@ sudo apt install ./OpenCodex--linux-amd64.deb トレイの **Open dashboard** または **Open in browser** で、埋め込みダッシュボードと通常のブラウザを切り替えられます。トレイから更新の確認もできます。 +macOS では、ダッシュボードを閉じてもアプリはメニューバーで動作し続けます。Dock または Finder から OpenCodex を再度開くと、プロキシを再起動せずにダッシュボードが再表示されます。 + ## トレイでの使用量表示 macOS と Windows ではトレイアイコンをクリックするとコンパクトな使用量ウィンドウが開きます。トレイの **Show usage** 操作でも開けます。これはトレイのクリックイベントを転送しない Linux デスクトップでも使えます。Linux では、デスクトップ環境にトレイアイコンが表示されなくても起動時にダッシュボードが開きます。 diff --git a/docs-site/src/content/docs/ja/guides/integrations.md b/docs-site/src/content/docs/ja/guides/integrations.md index 5fa241bf77..43ccd49d70 100644 --- a/docs-site/src/content/docs/ja/guides/integrations.md +++ b/docs-site/src/content/docs/ja/guides/integrations.md @@ -85,6 +85,8 @@ Disable は opencodex が自身のものとして記録した項目だけを削 ロックされても操作不能ではありません。競合したクライアントには、概要カードとクライアントページの両方で、スイッチの横に **Replace** が表示されます。管理対象設定が置かれている内容を opencodex のブロックで置き換える操作で、先に確認を求めます。ダイアログにはファイル名、失われる内容、元に戻すためのスナップショットが示されます。スイッチ自体はロックされたままです。どの編集を維持するか判断できるのは利用者だけだからです。それ以外の制約は緩めません。解析できないファイルや、構造を安全に判断できないファイルは引き続き拒否されます。 +Hermes のセッション識別設定には例外があります。管理対象の設定に `session_affinity_header: session-id` だけを追加した場合、**Apply** で取り込めます。他の管理対象フィールドの変更は引き続き競合になります。適用するまでバックグラウンドのモデル一覧更新も保留されます。この設定は provider 内の全モデルに適用され、対応する Hermes バージョンが必要です。キャッシュヒット率は保証されません。[英語のアップグレード説明](/guides/integrations/#hermes-session-affinity)を参照してください。 + ## 変更内容を確認して確定する Apply、Replace、Disable、Restore はプレビューから始まります。ダイアログには、変更対象の管理設定が、範囲を限定した変更パスと値の追加・更新・削除の区別とともに表示されます。確定前に内容を確認してください。 diff --git a/docs-site/src/content/docs/ja/guides/sidecars.md b/docs-site/src/content/docs/ja/guides/sidecars.md index 6e41dfdb40..4d623877c9 100644 --- a/docs-site/src/content/docs/ja/guides/sidecars.md +++ b/docs-site/src/content/docs/ja/guides/sidecars.md @@ -137,5 +137,14 @@ OpenAI 実行経路、ダッシュボード、管理 API は `gpt-5.6-luna` を `PUT /api/sidecar-settings` は同じフィールドを受け付けます。部分更新では省略したキーをそのまま残します。`timeoutMs` はランタイムの整数範囲(1–2147483647 ms)を使います。 +Web 検索サイドカーのカードも同じ構成です。モデルピッカーの先頭行が **オフ (Off)** です。オフにすると +OpenCodex は `web_search` への介入をやめ、Codex 統合は `~/.codex/config.toml` に +`web_search = "disabled"` を書き込みます。Codex は自身のモードがそうなるまでネイティブの +ホスト型 `web_search` ツールを広告し続けるためで、MCP 検索サーバーだけを検索経路にしたい +場合に必要です。再びオンにするとこの行は削除され、Codex ジャーナルに記録されたオペレーター自身の +ルート `web_search` 行が復元されます。この書き込みには管理対象の +`~/.codex/config.toml`(`ocx sync`)が必要で、書き込みが行われなかった場合は +ダッシュボードのカードが警告し、`ocx agent sidecar web --enabled off` が結果を報告します。 + ファイルを直接編集したい場合は、これまでどおり `config.json` で `enabled` を `false` にできます。Anthropic OAuth 検索と画像説明は既存の Claude Code OAuth fingerprint 先例に従いますが、実際のアカウントと作業量で十分 soak test するのが無難です。全 フィールドは[設定リファレンス](/ja/reference/configuration/server/#サイドカー)を参照してください。 diff --git a/docs-site/src/content/docs/ja/guides/web-dashboard.md b/docs-site/src/content/docs/ja/guides/web-dashboard.md index 022eb1b100..6191b3313f 100644 --- a/docs-site/src/content/docs/ja/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ja/guides/web-dashboard.md @@ -27,6 +27,16 @@ bun run dev:gui リモートダッシュボードでは標準のパスワードフォームが表示され、ブラウザのパスワードマネージャーで保存・自動入力できます。ダッシュボード自体はトークンをメモリ内だけに保持し、`localStorage` や `sessionStorage` には書き込みません。保存するかどうかはブラウザまたはパスワードマネージャーだけが決定します。 +## クォータ概要バー + +起動安全性ページを除くすべてのページ上部の 1 行の概要に、各プロバイダーの現在のクォータ使用率が表示されます。例: `OpenAI 31% | Claude 54% | xAI 12% | Google 8%`。プロバイダー画面と同じクォータレポート(`GET /api/provider-quotas`、タブが表示されている間は 60 秒ごと)を読み取り、上流への強制更新は行いません。 + +- 各チップには、報告されたウィンドウのうち優先されるものを表示します。週間、30 日、5 時間、プロバイダー固有のウィンドウ、前払いクレジットの順です。 +- 70% 使用でアンバー色、90% 使用で赤色になります。 +- チップにカーソルを合わせるかクリックすると、報告されたすべてのウィンドウとそのリセット時刻、読み取り時刻が表示されます。固定したチップは Escape キーか外側のクリックで閉じます。 +- クォータウィンドウを報告しないプロバイダーは表示しません。報告するプロバイダーがない場合はバー全体が隠れます。 +- 右端には、ダッシュボードが最後にレポートを読み取った時刻が表示されます。最新の読み取りに失敗し、前回の値が表示されたままのときはアンバー色になります。 + ## できること | 領域 | 機能 | diff --git a/docs-site/src/content/docs/ja/reference/cli/agents.md b/docs-site/src/content/docs/ja/reference/cli/agents.md index 933c5b41af..20d62ca3ec 100644 --- a/docs-site/src/content/docs/ja/reference/cli/agents.md +++ b/docs-site/src/content/docs/ja/reference/cli/agents.md @@ -13,8 +13,17 @@ description: マルチエージェント、コンボ、可観測性、アクセ ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off` はダッシュボードの **オフ (Off)** 行と同じスイッチです。OpenCodex はサイドカーを +実行しなくなり、Codex 統合は `~/.codex/config.toml` に +`web_search = "disabled"` を書き込むため、MCP 検索サーバーだけを検索経路にできます。 +`--enabled on` はその行を再び削除します。保存でスイッチが実際に切り替わったとき、コマンドは +Codex 側の書き込み(`--json` では `codexWebSearch`、それ以外では末尾の +`Codex config:` 行)を報告し、書き込みできなかった場合は `ocx sync` を案内します。 +このフラグは `vision` でも機能します。 + ### `ocx v2 |threads >` Codex `multi_agent_v2` 機能フラグとスリーステート マルチエージェント サーフェス モードを管理します。 diff --git a/docs-site/src/content/docs/ja/reference/configuration/server.md b/docs-site/src/content/docs/ja/reference/configuration/server.md index 7809e29485..faae3c179a 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/server.md +++ b/docs-site/src/content/docs/ja/reference/configuration/server.md @@ -145,7 +145,7 @@ Codex は、タイトルやコミット メッセージなどのタスクに小 |フィールド |タイプ |デフォルト |意味 | | --- | --- | --- | --- | -| `enabled?` | `boolean` |使用可能な場合はオン |マスタースイッチ。 | +| `enabled?` | `boolean` |使用可能な場合はオン |マスタースイッチ。`false` のとき OpenCodex は `web_search` への介入をやめ、Codex 統合は `~/.codex/config.toml` に `web_search = "disabled"` を書き込みます。 | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | 明示設定が優先され、未設定なら常に `openai` です。`anthropic` と `xai` は明示設定時のみ実行され、`gemini` と `exa` は executor が提供されるまで予約値です。 | | `model?` | `string` |バックエンド依存 | OpenAI は `gpt-5.6-luna`、Anthropic は `claude-sonnet-5`、xAI は `grok-4.6`。従来の明示的な `gpt-5.4-mini` は開始時に移行されます。 | | `exaApiKey?` | `string` | なし | `exa` バックエンドのオペレーターキー。書き込み専用で、管理 API の読み取りでは保存値を返しません。 | diff --git a/docs-site/src/content/docs/ko/guides/desktop-app.md b/docs-site/src/content/docs/ko/guides/desktop-app.md index d910373bb8..ee2c150da0 100644 --- a/docs-site/src/content/docs/ko/guides/desktop-app.md +++ b/docs-site/src/content/docs/ko/guides/desktop-app.md @@ -44,6 +44,8 @@ sudo apt install ./OpenCodex--linux-amd64.deb 트레이의 **Open dashboard** 또는 **Open in browser**를 사용하면 내장 대시보드와 일반 브라우저를 오갈 수 있습니다. 트레이에서는 업데이트도 확인할 수 있습니다. +macOS에서는 대시보드를 닫아도 앱이 메뉴 막대에서 계속 실행됩니다. Dock 또는 Finder에서 OpenCodex를 다시 열면 프록시를 재시작하지 않고 대시보드가 다시 표시됩니다. + ## 트레이에서 사용량 보기 macOS와 Windows에서는 트레이 아이콘을 클릭하면 작은 사용량 창이 열립니다. 트레이의 **Show usage**로도 열 수 있으며, 트레이 클릭 이벤트를 전달하지 않는 Linux 데스크톱에서도 사용할 수 있습니다. Linux에서는 트레이 아이콘이 표시되지 않는 환경을 포함해 시작할 때 대시보드가 열립니다. diff --git a/docs-site/src/content/docs/ko/guides/integrations.md b/docs-site/src/content/docs/ko/guides/integrations.md index a2377a3968..5d27c27ba6 100644 --- a/docs-site/src/content/docs/ko/guides/integrations.md +++ b/docs-site/src/content/docs/ko/guides/integrations.md @@ -85,6 +85,8 @@ Disable은 opencodex가 소유한다고 기록한 항목만 제거합니다. 이 잠겨도 해결 방법이 있습니다. 충돌한 클라이언트는 개요 카드와 클라이언트 페이지의 스위치 옆에 **Replace**를 표시합니다. opencodex 설정을 담은 부분을 새 블록으로 교체하기 전에 확인을 요청합니다. 대화 상자에 파일 이름, 잃게 될 내용, 되돌릴 수 있게 해 주는 스냅샷이 나옵니다. 스위치는 어떤 편집을 유지할지 판단할 수 없으므로 잠긴 채로 둡니다. 그 결정은 사용자가 해야 합니다. 그 밖의 거부 조건은 완화하지 않습니다. 파싱할 수 없거나 구조를 판단할 수 없는 파일은 여전히 거부합니다. +Hermes 세션 식별 설정에는 예외가 있습니다. 기존 관리 설정에 `session_affinity_header: session-id`만 추가했다면 **Apply**로 수용할 수 있습니다. 다른 관리 필드의 수정은 계속 충돌로 처리됩니다. 적용 전에는 백그라운드 모델 목록 갱신도 보류됩니다. 이 설정은 provider의 모든 모델에 적용되며 해당 기능을 지원하는 Hermes 버전이 필요합니다. 캐시 적중률은 보장하지 않습니다. [영문 업그레이드 안내](/guides/integrations/#hermes-session-affinity)를 참조하세요. + ## 변경 미리 보기와 확인 Apply, Replace, Disable, Restore는 미리 보기로 시작합니다. 대화 상자는 제한된 변경 경로와 각 값의 추가·갱신·삭제 여부를 포함해 어떤 관리 설정이 바뀔지 정확히 보여줍니다. 확인하기 전에 계획을 검토하세요. diff --git a/docs-site/src/content/docs/ko/guides/sidecars.md b/docs-site/src/content/docs/ko/guides/sidecars.md index 9bd1733985..b1aca7ee1b 100644 --- a/docs-site/src/content/docs/ko/guides/sidecars.md +++ b/docs-site/src/content/docs/ko/guides/sidecars.md @@ -139,6 +139,15 @@ OpenAI 실행 경로, Dashboard, 관리 API는 `gpt-5.6-luna`를 폴백으로 `PUT /api/sidecar-settings`는 같은 필드를 받습니다. 부분 업데이트는 보내지 않은 키를 유지합니다. `timeoutMs`는 런타임 정수 범위(1–2147483647 ms)를 사용합니다. +웹 검색 사이드카 카드도 같은 구성입니다. 모델 선택기의 첫 행은 **끔 (Off)** 행입니다. 끄면 +OpenCodex가 `web_search` 가로채기를 멈추고 Codex 통합이 `~/.codex/config.toml`에 +`web_search = "disabled"`를 씁니다. Codex는 자체 모드가 그렇게 될 때까지 네이티브 호스팅 +`web_search` 도구를 계속 광고하므로, MCP 검색 서버만 유일한 검색 경로가 되어야 할 때 +필요합니다. 다시 켜면 이 줄이 제거되고 Codex 저널에 기록된 운영자가 작성한 루트 +`web_search` 줄이 복원됩니다. 이 쓰기에는 관리되는 `~/.codex/config.toml` +(`ocx sync`)이 필요하며, 쓰기가 일어나지 않으면 대시보드 카드가 경고하고 +`ocx agent sidecar web --enabled off`가 결과를 보고합니다. + 파일을 직접 고치고 싶다면 이전처럼 `config.json`에서 `enabled`를 `false`로 두면 됩니다. Anthropic OAuth 검색과 이미지 설명은 기존 Claude Code OAuth fingerprint 선례를 따르지만, 실제 계정과 작업량으로 충분히 soak test하는 편이 좋습니다. 전체 필드는 [설정 레퍼런스](/ko/reference/configuration/server/#sidecars)를 참고하세요. diff --git a/docs-site/src/content/docs/ko/guides/web-dashboard.md b/docs-site/src/content/docs/ko/guides/web-dashboard.md index 0789923699..36b8942336 100644 --- a/docs-site/src/content/docs/ko/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ko/guides/web-dashboard.md @@ -27,6 +27,16 @@ bun run dev:gui 원격 대시보드는 표준 비밀번호 폼을 표시하므로 브라우저 비밀번호 관리자가 토큰 저장과 자동 완성을 제안할 수 있습니다. 대시보드 자체는 토큰을 메모리에만 보관하며 `localStorage`나 `sessionStorage`에 쓰지 않습니다. 저장 여부는 전적으로 브라우저 또는 비밀번호 관리자가 결정합니다. +## 사용량 요약 바 + +시작 안전성 페이지를 제외한 모든 페이지 상단의 한 줄 요약에 프로바이더별 현재 할당량 사용률이 표시됩니다. 예: `OpenAI 31% | Claude 54% | xAI 12% | Google 8%`. 프로바이더 작업 화면과 같은 할당량 보고(`GET /api/provider-quotas`)를 탭이 보이는 동안 60초마다 읽으며, 업스트림 강제 새로고침은 하지 않습니다. + +- 각 항목은 보고된 창 가운데 우선순위가 높은 창을 표시합니다. 주간, 월간, 5시간, 프로바이더 고유 창 또는 선불 크레딧 순서입니다. +- 70% 이상 사용하면 주황색, 90% 이상이면 빨간색으로 표시됩니다. +- 항목에 마우스를 올리거나 클릭하면 보고된 모든 창의 사용률, 초기화 시각, 데이터 기준 시각이 나옵니다. 고정된 항목은 Escape 키나 바깥 클릭으로 닫습니다. +- 할당량 창을 보고하지 않는 프로바이더는 표시하지 않습니다. 보고하는 프로바이더가 없으면 요약 바 전체가 숨겨집니다. +- 오른쪽 끝에 마지막으로 읽은 시각이 표시됩니다. 최근 읽기에 실패해 이전 값을 보여 주는 동안에는 주황색으로 바뀝니다. + ## 할 수 있는 일 | 영역 | 기능 | diff --git a/docs-site/src/content/docs/ko/reference/cli/agents.md b/docs-site/src/content/docs/ko/reference/cli/agents.md index 3f74c33e64..8dec1675fc 100644 --- a/docs-site/src/content/docs/ko/reference/cli/agents.md +++ b/docs-site/src/content/docs/ko/reference/cli/agents.md @@ -14,8 +14,17 @@ description: 멀티 에이전트, 콤보, 관측성, 접근, 통합, 시스템, ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off`는 대시보드의 **끔 (Off)** 행과 같은 스위치입니다. OpenCodex는 사이드카를 +실행하지 않고 Codex 통합은 `~/.codex/config.toml`에 +`web_search = "disabled"`를 쓰므로, MCP 검색 서버만 검색 경로로 쓸 수 있습니다. +`--enabled on`은 그 줄을 다시 제거합니다. 저장이 실제로 스위치를 옮기면 명령은 트리거된 +Codex 쪽 쓰기(`--json`에서는 `codexWebSearch`, 그 밖에는 마지막 +`Codex config:` 줄)를 보고하고, 쓰기가 불가능했다면 `ocx sync`를 안내합니다. +이 플래그는 `vision`에도 동작합니다. + ### `ocx effort [status|set|clear]` 실행 중인 프록시를 통해 메인·서브에이전트의 reasoning-effort 상한을 조회하거나 변경하며, diff --git a/docs-site/src/content/docs/ko/reference/configuration/server.md b/docs-site/src/content/docs/ko/reference/configuration/server.md index 277f8c40f8..62ade9f14c 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/server.md +++ b/docs-site/src/content/docs/ko/reference/configuration/server.md @@ -193,7 +193,7 @@ Codex는 제목과 커밋 메시지 같은 작업에 작은 보조 모델을 사 | 필드 | 형식 | 기본값 | 의미 | | --- | --- | --- | --- | -| `enabled?` | `boolean` | on when usable | 주 스위치입니다. | +| `enabled?` | `boolean` | on when usable | 주 스위치입니다. `false`이면 OpenCodex는 `web_search` 가로채기를 멈추고 Codex 통합이 `~/.codex/config.toml`에 `web_search = "disabled"`를 씁니다. | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | 명시값이 우선입니다. 생략하면 항상 `openai`입니다. `anthropic`과 `xai`는 명시적으로 설정할 때만 실행되며, `gemini`와 `exa`는 executor가 제공될 때까지 예약 상태입니다. | | `model?` | `string` | backend-dependent | OpenAI는 `gpt-5.6-luna`, Anthropic은 `claude-sonnet-5`, xAI는 `grok-4.6`입니다. 레거시로 명시된 `gpt-5.4-mini`는 시작 시 마이그레이션됩니다. | | `exaApiKey?` | `string` | 없음 | `exa` 백엔드용 운영자 키입니다. 쓰기 전용이며 관리 API 조회에서는 저장된 값을 반환하지 않습니다. | diff --git a/docs-site/src/content/docs/reference/cli/agents.md b/docs-site/src/content/docs/reference/cli/agents.md index 0f68be9f7a..166b83c8cb 100644 --- a/docs-site/src/content/docs/reference/cli/agents.md +++ b/docs-site/src/content/docs/reference/cli/agents.md @@ -31,8 +31,18 @@ stay writable). ```bash ocx agent sidecar web --list ocx agent sidecar web --model gpt-5.6-luna +ocx agent sidecar web --enabled off ``` +`--enabled off` is the same switch as the Dashboard's Off row: OpenCodex stops running the +sidecar and the Codex integration writes `web_search = "disabled"` into `~/.codex/config.toml`, +which is what lets an MCP search server be the only search path. `--enabled on` removes that +marker-owned line again. When the save actually moves the switch, the command reports the +Codex-side write it triggered (`codexWebSearch` in `--json`, a trailing `Codex config:` line +otherwise) and points at `ocx sync` when it could not happen; a save that leaves the switch +where it was has nothing to report and prints no `Codex config:` line. The flag works for +`vision` too. + ### `ocx effort [status|set|clear]` Inspect or change main and subagent reasoning-effort caps through the live proxy, or the local diff --git a/docs-site/src/content/docs/reference/configuration/server.md b/docs-site/src/content/docs/reference/configuration/server.md index 0394a17d5f..d3aee62c2c 100644 --- a/docs-site/src/content/docs/reference/configuration/server.md +++ b/docs-site/src/content/docs/reference/configuration/server.md @@ -657,7 +657,7 @@ Images API paths and response shape expected by Codex. | Field | Type | Default | Meaning | | --- | --- | --- | --- | -| `enabled?` | `boolean` | on when usable | Master switch. | +| `enabled?` | `boolean` | on when usable | Master switch. When false, OpenCodex stops intercepting `web_search` AND the Codex integration writes `web_search = "disabled"` into `~/.codex/config.toml`. | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | Explicit wins; unset always resolves to `openai`. `anthropic` and `xai` run only when explicitly configured; `gemini` and `exa` remain reserved until their executors ship. | | `model?` | `string` | backend-dependent | `gpt-5.6-luna` for OpenAI, `claude-sonnet-5` for Anthropic, or `grok-4.6` for xAI. Legacy explicit `gpt-5.4-mini` migrates on start. | | `exaApiKey?` | `string` | none | Operator key for the `exa` backend. Write-only: management reads never return the stored value. | diff --git a/docs-site/src/content/docs/ru/guides/desktop-app.md b/docs-site/src/content/docs/ru/guides/desktop-app.md index d52917226c..ce4f17b5c7 100644 --- a/docs-site/src/content/docs/ru/guides/desktop-app.md +++ b/docs-site/src/content/docs/ru/guides/desktop-app.md @@ -60,6 +60,8 @@ CLI подтвердил отсутствие прокси; неопределё Используйте действия **Open dashboard** или **Open in browser** в системной панели, чтобы переключаться между встроенным дашбордом и обычным браузером. Там же доступны проверки обновлений. +В macOS после закрытия дашборда приложение продолжает работать в строке меню. Откройте OpenCodex снова через Dock или Finder, чтобы вернуть дашборд без перезапуска прокси. + ## Использование в системной панели На macOS и Windows нажмите значок в системной панели, чтобы открыть компактное окно использования. diff --git a/docs-site/src/content/docs/ru/guides/integrations.md b/docs-site/src/content/docs/ru/guides/integrations.md index e63b5ba93f..e496ae9b70 100644 --- a/docs-site/src/content/docs/ru/guides/integrations.md +++ b/docs-site/src/content/docs/ru/guides/integrations.md @@ -231,6 +231,8 @@ JSON5 и TOML при записи всего документа либо обы это можете решить только вы. Другие ограничения не ослаблены: файл, который нельзя разобрать или безопасно понять, по-прежнему отклоняется. +Исключение для Hermes: если в управляемый блок добавлено только `session_affinity_header: session-id`, изменение можно принять через **Apply**. Другие изменения управляемых полей остаются конфликтами. До применения обновления фоновое обновление списка моделей также приостановлено. Настройка действует для всех моделей provider и требует версии Hermes с поддержкой этой функции; доля попаданий в кеш не гарантируется. См. [инструкцию по обновлению на английском](/guides/integrations/#hermes-session-affinity). + ## Предпросмотр и подтверждение изменений Apply, Replace, Disable и Restore теперь начинаются с предпросмотра. Диалог diff --git a/docs-site/src/content/docs/ru/guides/sidecars.md b/docs-site/src/content/docs/ru/guides/sidecars.md index 1a15066a9b..337c69062c 100644 --- a/docs-site/src/content/docs/ru/guides/sidecars.md +++ b/docs-site/src/content/docs/ru/guides/sidecars.md @@ -155,6 +155,16 @@ opencodex описывает каждое изображение **до** осн `PUT /api/sidecar-settings` принимает те же поля. Частичное обновление оставляет непереданные ключи без изменений. `timeoutMs` использует целочисленные границы рантайма (1–2147483647 мс). +Карточка сайдкара web-search устроена так же: первая строка выбора модели — **Выкл. (Off)**. +Выключение останавливает перехват `web_search` со стороны OpenCodex, а интеграция Codex +записывает `web_search = "disabled"` в `~/.codex/config.toml`: до этого Codex +продолжает объявлять свой нативный hosted-инструмент `web_search`, а выключить его нужно, +когда единственным путём поиска должен стать MCP-сервер. Обратное включение удаляет эту строку и +возвращает корневую строку `web_search`, заданную оператором и записанную в журнале Codex. +Запись требует управляемого `~/.codex/config.toml` (`ocx sync`); если она не +произошла, карточка дашборда предупреждает об этом, а +`ocx agent sidecar web --enabled off` сообщает результат. + Если удобнее править файл, по-прежнему можно поставить `enabled: false` в `config.json`. Поиск и описание изображений через Anthropic OAuth переиспользуют существующий прецедент OAuth-отпечатка Claude Code, но их стоит обкатать с целевым аккаунтом и нагрузкой. diff --git a/docs-site/src/content/docs/ru/guides/web-dashboard.md b/docs-site/src/content/docs/ru/guides/web-dashboard.md index 2e09211db1..458a5a16cf 100644 --- a/docs-site/src/content/docs/ru/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ru/guides/web-dashboard.md @@ -27,6 +27,16 @@ bun run dev:gui Удалённый дашборд показывает стандартную форму пароля, поэтому менеджер паролей браузера может предложить сохранить и автозаполнять токен. Сам дашборд хранит токен только в памяти и не записывает его в `localStorage` или `sessionStorage`; решение о сохранении полностью остаётся за браузером или менеджером паролей. +## Полоса сводки квот + +Одна строка в верхней части каждой страницы, кроме страницы «Безопасность запуска», показывает текущее использование квот каждым провайдером, например `OpenAI 31% | Claude 54% | xAI 12% | Google 8%`. Она читает те же отчёты о квотах провайдеров, что и рабочая область провайдера (`GET /api/provider-quotas`, каждые 60 секунд, пока вкладка видима), и никогда не форсирует обновление на стороне провайдера. + +- Каждая метка показывает приоритетное из сообщённых окон: сначала недельное, затем месячное, затем 5-часовое, затем окно с именем провайдера или предоплаченные кредиты. +- Метка становится янтарной при 70% использования и красной при 90%. +- Наведите курсор на метку или нажмите её, чтобы увидеть все сообщённые окна со временем сброса и временем снятия показаний. Нажмите Escape или щёлкните в другом месте, чтобы закрыть закреплённую метку. +- Провайдеры, не сообщающие ни одного окна квоты, не показываются. Полоса скрыта, когда ни один провайдер их не сообщает. +- Правый край показывает, когда дашборд последний раз читал отчёты. Он становится янтарным, если последнее чтение не удалось, а предыдущие значения всё ещё показаны. + ## Возможности | Раздел | Что делает | diff --git a/docs-site/src/content/docs/ru/reference/cli/agents.md b/docs-site/src/content/docs/ru/reference/cli/agents.md index ad02e88b17..e2ad3a31e2 100644 --- a/docs-site/src/content/docs/ru/reference/cli/agents.md +++ b/docs-site/src/content/docs/ru/reference/cli/agents.md @@ -17,8 +17,17 @@ surface mode, delegation, effort и fallback, описано в ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off` — тот же переключатель, что и строка **Выкл. (Off)** в дашборде: OpenCodex +перестаёт запускать сайдкар, а интеграция Codex записывает `web_search = "disabled"` в +`~/.codex/config.toml`, что и позволяет использовать MCP-сервер как единственный путь поиска. +`--enabled on` снова удаляет эту строку. Когда сохранение действительно переключает +состояние, команда сообщает о записи на стороне Codex (`codexWebSearch` в `--json`, +иначе завершающая строка `Codex config:`) и предлагает `ocx sync`, если запись не +удалась. Флаг работает и для `vision`. + ### `ocx v2 |threads >` Управляйте feature flag'ом Codex `multi_agent_v2` и трёхсостоянием multi-agent surface mode. diff --git a/docs-site/src/content/docs/ru/reference/configuration/server.md b/docs-site/src/content/docs/ru/reference/configuration/server.md index 971c1fa522..0521219481 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/server.md +++ b/docs-site/src/content/docs/ru/reference/configuration/server.md @@ -174,7 +174,7 @@ Codex использует маленькие helper-model'и для задач | Поле | Тип | По умолчанию | Значение | | --- | --- | --- | --- | -| `enabled?` | `boolean` | on when usable | Главный переключатель. | +| `enabled?` | `boolean` | on when usable | Главный переключатель. При `false` OpenCodex перестаёт перехватывать `web_search`, а интеграция Codex записывает `web_search = "disabled"` в `~/.codex/config.toml`. | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | Явный выбор выигрывает; отсутствие значения всегда означает `openai`. `anthropic` и `xai` запускаются только при явной настройке; `gemini` и `exa` зарезервированы до появления executor. | | `model?` | `string` | backend-dependent | `gpt-5.6-luna` для OpenAI, `claude-sonnet-5` для Anthropic или `grok-4.6` для xAI. Старый явный `gpt-5.4-mini` мигрирует при старте. | | `exaApiKey?` | `string` | отсутствует | Ключ оператора для backend `exa`. Только для записи: management-read никогда не возвращает сохранённое значение. | diff --git a/docs-site/src/content/docs/tr/guides/desktop-app.md b/docs-site/src/content/docs/tr/guides/desktop-app.md index d0c8c59320..b0b635909f 100644 --- a/docs-site/src/content/docs/tr/guides/desktop-app.md +++ b/docs-site/src/content/docs/tr/guides/desktop-app.md @@ -44,6 +44,8 @@ Uygulama, paketindeki CLI'dan `ocx resolve --json` çalıştırmasını ister ve Gömülü kontrol paneli ile normal tarayıcınız arasında geçmek için tepsideki **Open dashboard** veya **Open in browser** eylemini kullanın. Tepsi, güncelleme denetimlerini de sunar. +macOS’te kontrol panelini kapattığınızda uygulama menü çubuğunda çalışmaya devam eder. Proxy’yi yeniden başlatmadan kontrol panelini geri getirmek için OpenCodex’i Dock veya Finder üzerinden yeniden açın. + ## Tepside kullanım macOS ve Windows'ta küçük kullanım penceresini açmak için tepsi simgesine tıklayın. Tepsideki **Show usage** eylemi de pencereyi açar; tıklama olaylarını iletmeyen Linux tepsilerinde de çalışır. Linux'ta masaüstü ortamı tepsi simgesi göstermese bile kontrol paneli başlangıçta açılır. diff --git a/docs-site/src/content/docs/tr/guides/integrations.md b/docs-site/src/content/docs/tr/guides/integrations.md index 298930cd40..1c5496f4b4 100644 --- a/docs-site/src/content/docs/tr/guides/integrations.md +++ b/docs-site/src/content/docs/tr/guides/integrations.md @@ -155,6 +155,8 @@ Kimi Code, gjc, MiniMax Code, Raycast — bütün belge olarak yazılan YAML, JS kendi girdilerimiz düzenlenmişse, anahtar kilitlenir ve hangi düzenlemelerin size ait olduğunu tahmin etmek yerine devre dışı bırakmayı reddeder. +Hermes istisnası: yönetilen bloğa yalnızca `session_affinity_header: session-id` eklenmişse **Apply** ile benimsenebilir; diğer yönetilen alan değişiklikleri çakışma olarak kalır. Uygulanana kadar arka plandaki model listesi güncellemeleri de bekletilir. Ayar provider içindeki tüm modeller için geçerlidir ve bu özelliği destekleyen bir Hermes sürümü gerektirir; önbellek isabet oranı garanti edilmez. [İngilizce yükseltme açıklamasına](/guides/integrations/#hermes-session-affinity) bakın. + ## Değişiklikleri önizleyin ve onaylayın Uygula, Değiştir, Devre dışı bırak ve Geri yükle işlemleri artık bir önizlemeyle başlar. İletişim diff --git a/docs-site/src/content/docs/tr/guides/sidecars.md b/docs-site/src/content/docs/tr/guides/sidecars.md index 3c1f80e104..fdd53a327a 100644 --- a/docs-site/src/content/docs/tr/guides/sidecars.md +++ b/docs-site/src/content/docs/tr/guides/sidecars.md @@ -196,6 +196,15 @@ akıl yürütmeyi, zaman aşımını ve sınırı korur. atlanan anahtarları değiştirmeden bırakır. `timeoutMs` çalışma zamanı tamsayı sınırlarını kullanır (1–2147483647 ms). +Web arama sidecar kartı aynı denetim yapısını taşır: model seçicisinin ilk satırı **Kapalı (Off)**'dır. +Kapatmak OpenCodex'in `web_search` yakalamasını durdurur ve Codex entegrasyonu +`~/.codex/config.toml` dosyasına `web_search = "disabled"` yazar; çünkü Codex kendi +modu aksini söyleyene kadar yerleşik barındırılan `web_search` aracını bildirmeye devam eder ve +tek arama yolu bir MCP arama sunucusu olacaksa bu gerekir. Yeniden açmak bu satırı kaldırır ve Codex +günlüğüne kaydedilmiş operatörün kendi kök `web_search` satırını geri getirir. Yazma işlemi +yönetilen bir `~/.codex/config.toml` (`ocx sync`) gerektirir; gerçekleşmezse kontrol +paneli kartı uyarır ve `ocx agent sidecar web --enabled off` sonucu bildirir. + Dosyayı doğrudan düzenlemeyi tercih ediyorsanız `config.json` içinde yine de `enabled: false` ayarlayabilirsiniz. Anthropic-OAuth araması ve görsel açıklaması mevcut Claude Code OAuth parmak izi emsalini yeniden kullanır, ancak diff --git a/docs-site/src/content/docs/tr/guides/web-dashboard.md b/docs-site/src/content/docs/tr/guides/web-dashboard.md index 86d55eb3d8..11546c89a5 100644 --- a/docs-site/src/content/docs/tr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/tr/guides/web-dashboard.md @@ -39,6 +39,25 @@ yalnızca bellekte tutar ve `localStorage` veya `sessionStorage`'a yazmaz; kaydedilip kaydedilmeyeceği tamamen tarayıcının veya şifre yöneticisinin kararıdır. +## Kota özeti çubuğu + +Başlangıç güvenliği sayfası dışındaki her sayfanın üst kısmındaki tek satırlık özet, her +sağlayıcının geçerli kota kullanımını gösterir; örneğin +`OpenAI 31% | Claude 54% | xAI 12% | Google 8%`. Sağlayıcı çalışma alanıyla aynı kota +raporlarını okur (`GET /api/provider-quotas`, sekme görünürken 60 saniyede bir) ve hiçbir +zaman yukarı akışta yenilemeye zorlamaz. + +- Her etiket, bildirilen pencereler arasında tercih edileni gösterir: önce haftalık, sonra + aylık, sonra 5 saatlik, sonra sağlayıcı adlı bir pencere veya ön ödemeli krediler. +- Etiket %70 kullanımda amber rengine, %90 kullanımda kırmızıya döner. +- Bildirilen tüm pencereleri sıfırlama saati ve okuma zamanıyla görmek için etiketin + üzerine gelin veya tıklayın. Sabitlenmiş bir etiketi kapatmak için Escape'e basın veya + başka bir yere tıklayın. +- Kota penceresi bildirmeyen sağlayıcılar gösterilmez. Hiçbir sağlayıcı bildirmiyorsa + çubuk gizlenir. +- Sağ kenar, kontrol panelinin raporları en son ne zaman okuduğunu gösterir. Son okuma + başarısız olduğunda ve önceki değer hâlâ gösterildiğinde amber renge döner. + ## Neler yapabilirsiniz | Alan | Ne yapar | diff --git a/docs-site/src/content/docs/tr/reference/cli/agents.md b/docs-site/src/content/docs/tr/reference/cli/agents.md index cfc0d8525c..c1fb984961 100644 --- a/docs-site/src/content/docs/tr/reference/cli/agents.md +++ b/docs-site/src/content/docs/tr/reference/cli/agents.md @@ -18,8 +18,17 @@ yüzeyleri](/tr/guides/sub-agent-surface/) sayfasına bakın. ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off`, kontrol panelindeki **Kapalı (Off)** satırıyla aynı anahtardır: OpenCodex +sidecar'ı çalıştırmayı bırakır ve Codex entegrasyonu `~/.codex/config.toml` dosyasına +`web_search = "disabled"` yazar; tek arama yolu olarak bir MCP arama sunucusunun kullanılmasını bu +sağlar. `--enabled on` bu satırı yeniden kaldırır. Kaydetme anahtarı gerçekten değiştirdiğinde +komut tetiklediği Codex tarafı yazmayı bildirir (`--json` içinde `codexWebSearch`, aksi +halde son satırda `Codex config:`) ve yazma yapılamadığında `ocx sync` adresini gösterir. +Bayrak `vision` için de çalışır. + ### `ocx v2 |threads |mode-hint >` Codex `multi_agent_v2` özellik bayrağını ve üç durumlu çoklu ajan yüzey modunu diff --git a/docs-site/src/content/docs/tr/reference/configuration/server.md b/docs-site/src/content/docs/tr/reference/configuration/server.md index ca1c34024c..018cad76a6 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/server.md +++ b/docs-site/src/content/docs/tr/reference/configuration/server.md @@ -244,7 +244,7 @@ Images API yollarını ve yanıt şeklini uygulamalıdır. | Alan | Tip | Varsayılan | Anlamı | | --- | --- | --- | --- | -| `enabled?` | `boolean` | kullanılabilir olduğunda açık | Ana anahtar. | +| `enabled?` | `boolean` | kullanılabilir olduğunda açık | Ana anahtar. `false` olduğunda OpenCodex `web_search` yakalamayı bırakır ve Codex entegrasyonu `~/.codex/config.toml` dosyasına `web_search = "disabled"` yazar. | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | Açık değer kazanır; ayarlanmadığında her zaman `openai` seçilir. `anthropic` ve `xai` yalnızca açıkça yapılandırıldığında çalışır; `gemini` ve `exa` executor'ları sunulana kadar ayrılmıştır. | | `model?` | `string` | arka uca bağlı | OpenAI için `gpt-5.6-luna`, Anthropic için `claude-sonnet-5` veya xAI için `grok-4.6`. Eski açık `gpt-5.4-mini` başlangıçta geçirilir. | | `exaApiKey?` | `string` | yok | `exa` arka ucu için operatör anahtarı. Yalnızca yazılır; yönetim okumaları saklanan değeri asla döndürmez. | diff --git a/docs-site/src/content/docs/zh-cn/guides/desktop-app.md b/docs-site/src/content/docs/zh-cn/guides/desktop-app.md index 2e94868ce1..0342194eb0 100644 --- a/docs-site/src/content/docs/zh-cn/guides/desktop-app.md +++ b/docs-site/src/content/docs/zh-cn/guides/desktop-app.md @@ -44,6 +44,8 @@ sudo apt install ./OpenCodex--linux-amd64.deb 使用托盘中的 **Open dashboard** 或 **Open in browser**,可在内嵌仪表盘与常用浏览器之间切换。托盘也提供更新检查。 +在 macOS 上,关闭仪表盘后,应用会继续在菜单栏中运行。从 Dock 或 Finder 再次打开 OpenCodex 即可恢复仪表盘,无需重启代理。 + ## 在托盘中查看用量 在 macOS 和 Windows 上,点击托盘图标即可打开紧凑的用量窗口。托盘中的 **Show usage** 也能打开它,包括在不转发点击事件的 Linux 桌面上。在 Linux 上,仪表盘会在启动时打开,即使桌面环境没有显示托盘图标也是如此。 diff --git a/docs-site/src/content/docs/zh-cn/guides/integrations.md b/docs-site/src/content/docs/zh-cn/guides/integrations.md index 3ebc705dc3..2d7a03b7f7 100644 --- a/docs-site/src/content/docs/zh-cn/guides/integrations.md +++ b/docs-site/src/content/docs/zh-cn/guides/integrations.md @@ -85,6 +85,8 @@ Disable 只移除 opencodex 记录为自己管理的条目。如果文件在写 锁定状态并非无解。有冲突的客户端会在概览卡片和自身页面的开关旁显示 **Replace**。它会将占据我们设置位置的内容替换为 opencodex 将写入的配置块,并事先询问:对话框会显示文件名、说明会丢失什么,并指向可用于撤销的快照。开关本身仍锁定,因为它无法知道你希望保留哪些编辑;只有你能决定。其他限制没有放宽:无法解析或无法可靠理解结构的文件仍会拒绝处理。 +Hermes 的会话标识升级是上述冲突规则的特例:已有受管配置仅新增 `session_affinity_header: session-id` 时,可通过 **Apply** 接纳;其他受管字段的修改仍会冲突。升级前,后台刷新会同时暂停该集成的模型列表更新。此设置适用于该 provider 的所有模型,需要支持该能力的 Hermes 版本,且不保证缓存命中率。详见[英文升级说明](/guides/integrations/#hermes-session-affinity)。 + ## 预览并确认变更 Apply、Replace、Disable 和 Restore 都先显示预览。对话框准确列出会变化的托管设置,包括有界的变更路径及每项变更是添加、更新还是移除。确认前请检查计划。 diff --git a/docs-site/src/content/docs/zh-cn/guides/sidecars.md b/docs-site/src/content/docs/zh-cn/guides/sidecars.md index 598469e072..15605d2dcf 100644 --- a/docs-site/src/content/docs/zh-cn/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-cn/guides/sidecars.md @@ -126,5 +126,13 @@ Dashboard 和管理 API 都使用 `gpt-5.6-luna` 作为回退。启动时仍会 `PUT /api/sidecar-settings` 接受相同字段。部分更新会保留未提交的键。`timeoutMs` 使用运行时整数边界(1–2147483647 毫秒)。 +Web 搜索 sidecar 卡片采用相同的控件形态:模型选择器的第一行是 **关闭 (Off)**。关闭会停止 +OpenCodex 对 `web_search` 的拦截,同时 Codex 集成会把 `web_search = "disabled"` +写入 `~/.codex/config.toml`;因为 Codex 在自身模式如此声明前会一直声明其原生托管的 +`web_search` 工具,而当 MCP 搜索服务器需要成为唯一搜索路径时,这正是必需的。重新开启会 +移除该行,并恢复 Codex 日志中记录的、由操作者写入的根级 `web_search` 行。该写入需要受管理的 +`~/.codex/config.toml`(`ocx sync`);若未执行,仪表盘卡片会给出警告, +`ocx agent sidecar web --enabled off` 也会报告结果。 + 如果更想直接改文件,仍可在 `config.json` 中把 `enabled` 设为 `false`。Anthropic OAuth 搜索和图像描述沿用现有 Claude Code OAuth fingerprint 先例,但仍应使用目标账户和实际负载充分 soak test。所有字段见 [配置参考](/zh-cn/reference/configuration/server/#侧车)。 diff --git a/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md b/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md index c49221a332..78bd947d13 100644 --- a/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md +++ b/docs-site/src/content/docs/zh-cn/guides/web-dashboard.md @@ -26,6 +26,16 @@ bun run dev:gui 远程仪表盘会显示标准密码表单,浏览器密码管理器可以提示保存并自动填充 token。仪表盘本身只在内存中保存 token,不会写入 `localStorage` 或 `sessionStorage`;是否持久保存完全由浏览器或密码管理器决定。 +## 配额摘要栏 + +除启动安全页面外,每个页面顶部的一行摘要会显示各 provider 当前的配额用量,例如 `OpenAI 31% | Claude 54% | xAI 12% | Google 8%`。它读取与提供方工作区相同的配额报告(`GET /api/provider-quotas`,标签页可见时每 60 秒一次),并且绝不会强制刷新上游。 + +- 每个条目显示优先选用的已报告窗口:依次为每周、30 天、5 小时,然后是 provider 自命名窗口或预付额度。 +- 用量达到 70% 时变为琥珀色,达到 90% 时变为红色。 +- 悬停或点击条目可查看所有已报告的窗口及其重置时间和读取时间。按 Escape 或点击其他位置可关闭已固定的条目。 +- 不报告任何配额窗口的 provider 不会显示。所有 provider 都未报告时,整条栏会隐藏。 +- 右端显示仪表盘上次读取报告的时间。当最近一次读取失败且仍在显示上一次读数时,它会变为琥珀色。 + ## 可以完成哪些操作 | 区域 | 作用 | diff --git a/docs-site/src/content/docs/zh-cn/reference/cli/agents.md b/docs-site/src/content/docs/zh-cn/reference/cli/agents.md index 85fd3f4e71..b1a0d4306c 100644 --- a/docs-site/src/content/docs/zh-cn/reference/cli/agents.md +++ b/docs-site/src/content/docs/zh-cn/reference/cli/agents.md @@ -13,8 +13,15 @@ description: 多代理、combo、可观测性、访问、集成、系统和配 ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off` 与仪表盘中的 **关闭 (Off)** 行是同一个开关:OpenCodex 不再运行该 sidecar, +Codex 集成会把 `web_search = "disabled"` 写入 `~/.codex/config.toml`,这正是让 MCP +搜索服务器成为唯一搜索路径的前提。`--enabled on` 会再次移除该行。当保存确实改变了开关状态时, +命令会报告由此触发的 Codex 侧写入(`--json` 中的 `codexWebSearch`,否则为末尾的 +`Codex config:` 行),并在无法写入时提示 `ocx sync`。该标志对 `vision` 同样有效。 + ### `ocx v2 |threads >` 管理 Codex 的 `multi_agent_v2` 功能标志和三态多代理 surface 模式。 diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/server.md b/docs-site/src/content/docs/zh-cn/reference/configuration/server.md index 095c9aeda5..afdd4c517d 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/server.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/server.md @@ -157,7 +157,7 @@ Codex 会为标题、提交信息等任务使用较小的辅助模型。启用 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | -| `enabled?` | `boolean` | 在可用时启用 | 总开关。 | +| `enabled?` | `boolean` | 在可用时启用 | 总开关。为 `false` 时,OpenCodex 停止拦截 `web_search`,并且 Codex 集成会把 `web_search = "disabled"` 写入 `~/.codex/config.toml`。 | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | 显式配置优先;省略时始终使用 `openai`。`anthropic` 和 `xai` 仅在显式配置时运行;`gemini` 和 `exa` 在 executor 发布前仍为保留值。 | | `model?` | `string` | 依后端而定 | OpenAI 使用 `gpt-5.6-luna`,Anthropic 使用 `claude-sonnet-5`,xAI 使用 `grok-4.6`。旧的显式 `gpt-5.4-mini` 会在启动时迁移。 | | `exaApiKey?` | `string` | 无 | `exa` 后端的操作员密钥。仅可写入:管理读取绝不会返回已存储的值。 | diff --git a/docs-site/src/content/docs/zh-tw/guides/desktop-app.md b/docs-site/src/content/docs/zh-tw/guides/desktop-app.md index 111f96f84e..94febd97fa 100644 --- a/docs-site/src/content/docs/zh-tw/guides/desktop-app.md +++ b/docs-site/src/content/docs/zh-tw/guides/desktop-app.md @@ -44,6 +44,8 @@ sudo apt install ./OpenCodex--linux-amd64.deb 透過系統匣的 **Open dashboard** 或 **Open in browser**,可以在內嵌儀表板與一般瀏覽器間切換。系統匣也提供更新檢查。 +在 macOS 上,關閉儀表板後,應用程式會繼續在選單列中執行。從 Dock 或 Finder 再次開啟 OpenCodex 即可恢復儀表板,無須重新啟動代理。 + ## 系統匣中的用量資訊 在 macOS 與 Windows 上,點擊系統匣圖示可開啟精簡用量視窗。系統匣的 **Show usage** 也能開啟它,包括不會轉送點擊事件的 Linux 桌面環境。Linux 會在啟動時開啟儀表板,即使桌面環境不顯示系統匣圖示也一樣。 diff --git a/docs-site/src/content/docs/zh-tw/guides/integrations.md b/docs-site/src/content/docs/zh-tw/guides/integrations.md index 83f5cc7cf6..12dc51aff5 100644 --- a/docs-site/src/content/docs/zh-tw/guides/integrations.md +++ b/docs-site/src/content/docs/zh-tw/guides/integrations.md @@ -84,6 +84,8 @@ opencodex 從自己的環境讀取這些變數。如果你的 gateway 以 profil 停用只移除 opencodex 記錄為自己寫入的條目。如果你的檔案在我們寫入之後有變更,後續行為取決於我們自己的條目是否完好,以及檔案的格式。對於嚴格 JSON 設定檔(OpenCode、Pi),在我們的區塊**旁邊**進行的編輯——例如新增 MCP 伺服器或你自己的 provider——會顯示為**需要更新**:重新整理會在保留你的條目的前提下合併寫入,但格式可能會被正規化。例外情況是 JSON 無法精確重寫的內容——例如 `1e999` 這類非有限數字、重寫會被四捨五入的數字(極大的整數,或小到會塌縮成零的數字)、`-0`、同一個物件裡重複出現的鍵,或巢狀層數超過 1000 層——此時開關會鎖定,確保沒有任何值被悄悄改動或刪除。**OMP、DSH 與 Hermes** 同樣不受旁邊編輯影響,但原因不同:它們的 writer 只逐位元組修補自己的 `providers.opencodex` 範圍,檔案其餘部分從不會被重寫。至於其餘可以包含註解的格式(OpenClaw、Kimi Code、gjc、MiniMax Code、Raycast——以整份文件寫出的 YAML、JSON5 與 TOML),或當我們自己的條目被編輯過時,開關會鎖定,停用會拒絕執行,而不是猜測哪些編輯是你的。 +Hermes 的會話標識升級是上述衝突規則的特例:既有受管設定僅新增 `session_affinity_header: session-id` 時,可透過 **Apply** 接納;其他受管欄位的修改仍會衝突。升級前,背景重新整理也會暫停此整合的模型清單更新。此設定適用於該 provider 的所有模型,需要支援此能力的 Hermes 版本,且不保證快取命中率。詳見[英文升級說明](/guides/integrations/#hermes-session-affinity)。 + ## 預覽並確認變更 套用、取代、停用與回復現在都會先顯示預覽。對話框會明確列出哪些受管理的設定將會變更, diff --git a/docs-site/src/content/docs/zh-tw/guides/sidecars.md b/docs-site/src/content/docs/zh-tw/guides/sidecars.md index 8d0e3056da..fae3319f22 100644 --- a/docs-site/src/content/docs/zh-tw/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-tw/guides/sidecars.md @@ -121,5 +121,13 @@ OAuth 帳號時使用 `anthropic`,否則使用 `openai`。明確選擇 `anthro `PUT /api/sidecar-settings` 接受相同欄位。部分更新會保留未提交的鍵。`timeoutMs` 使用執行時整數邊界(1–2147483647 毫秒)。 +Web 搜尋 sidecar 卡片採用相同的控制項形態:模型選擇器的第一列是 **關閉 (Off)**。關閉會停止 +OpenCodex 對 `web_search` 的攔截,同時 Codex 整合會把 `web_search = "disabled"` +寫入 `~/.codex/config.toml`;因為 Codex 在自身模式如此宣告前會一直宣告其原生託管的 +`web_search` 工具,而當 MCP 搜尋伺服器需要成為唯一搜尋路徑時,這正是必要的。重新開啟會 +移除該行,並還原 Codex 日誌中記錄、由操作者寫入的根層級 `web_search` 行。該寫入需要受管理的 +`~/.codex/config.toml`(`ocx sync`);若未執行,儀表板卡片會提出警告, +`ocx agent sidecar web --enabled off` 也會回報結果。 + 如果更想直接改檔案,仍可在 `config.json` 中把 `enabled` 設為 `false`。Anthropic OAuth 搜尋和圖像描述沿用現有 Claude Code OAuth fingerprint 先例,但仍應使用目標帳號和實際負載充分 soak test。所有欄位見 [設定參考](/zh-tw/reference/configuration/server/#sidecar)。 diff --git a/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md b/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md index e1f171fc1f..5a8f521bf3 100644 --- a/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md +++ b/docs-site/src/content/docs/zh-tw/guides/web-dashboard.md @@ -31,6 +31,21 @@ GUI session 簽發到服務的頁面中,並在到期或代理重啟時靜默 儀表板本身仍然只在記憶體中保留 token,不會寫入 `localStorage` 或 `sessionStorage`;是否儲存完全 由瀏覽器或密碼管理員決定。 +## 配額摘要列 + +除啟動安全頁面外,每個頁面頂端的一行摘要會顯示各供應商目前的配額用量,例如 +`OpenAI 31% | Claude 54% | xAI 12% | Google 8%`。它讀取與供應商工作區相同的配額報告 +(`GET /api/provider-quotas`,分頁可見時每 60 秒一次),且絕不會強制重新整理上游。 + +- 每個項目顯示優先選用的已回報視窗:依序為每週、30 天、5 小時,然後是供應商自訂視窗或 + 預付額度。 +- 用量達 70% 時轉為琥珀色,達 90% 時轉為紅色。 +- 將游標移到項目上或點擊項目,可查看所有已回報的視窗及其重設時間和讀取時間。按 + Escape 或點擊其他位置可關閉已固定的項目。 +- 未回報任何配額視窗的供應商不會顯示。所有供應商都未回報時,整條列會隱藏。 +- 右端顯示儀表板上次讀取報告的時間。當最近一次讀取失敗且仍在顯示上一次讀數時,它 + 會轉為琥珀色。 + ## 可以完成哪些操作 | 區域 | 作用 | diff --git a/docs-site/src/content/docs/zh-tw/reference/cli/agents.md b/docs-site/src/content/docs/zh-tw/reference/cli/agents.md index 9a61042db2..9b06950e1a 100644 --- a/docs-site/src/content/docs/zh-tw/reference/cli/agents.md +++ b/docs-site/src/content/docs/zh-tw/reference/cli/agents.md @@ -13,8 +13,15 @@ description: 多代理、組合、可觀測性、存取、整合、系統與設 ```bash ocx agent subagents set ark/model-a,openai/gpt-5.5 +ocx agent sidecar web --enabled off ``` +`--enabled off` 與儀表板中的 **關閉 (Off)** 列是同一個開關:OpenCodex 不再執行該 sidecar, +Codex 整合會把 `web_search = "disabled"` 寫入 `~/.codex/config.toml`,這正是讓 MCP +搜尋伺服器成為唯一搜尋路徑的前提。`--enabled on` 會再次移除該行。當儲存確實改變開關狀態時, +指令會回報由此觸發的 Codex 端寫入(`--json` 中的 `codexWebSearch`,否則為結尾的 +`Codex config:` 行),並在無法寫入時提示 `ocx sync`。該旗標對 `vision` 同樣有效。 + ### `ocx v2 |threads >` 管理 Codex 的 `multi_agent_v2` 功能旗標與三態多代理介面模式。 diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/server.md b/docs-site/src/content/docs/zh-tw/reference/configuration/server.md index ad1aa7c670..a64472156f 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/server.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/server.md @@ -179,7 +179,7 @@ Codex 使用小型 helper 模型處理如標題與 commit 訊息等任務。啟 | 欄位 | 型別 | 預設值 | 意義 | | --- | --- | --- | --- | -| `enabled?` | `boolean` | 可用時開啟 | 主開關。 | +| `enabled?` | `boolean` | 可用時開啟 | 主開關。為 `false` 時,OpenCodex 停止攔截 `web_search`,且 Codex 整合會把 `web_search = "disabled"` 寫入 `~/.codex/config.toml`。 | | `backend?` | `"openai" \| "anthropic" \| "xai" \| "gemini" \| "exa"` | `openai` | 明確設定優先;省略時一律使用 `openai`。`anthropic` 與 `xai` 僅在明確設定時執行;`gemini` 與 `exa` 在 executor 推出前仍為保留值。 | | `model?` | `string` | 視 backend 而定 | OpenAI 為 `gpt-5.6-luna`、Anthropic 為 `claude-sonnet-5`、xAI 為 `grok-4.6`。舊版明確 `gpt-5.4-mini` 在啟動時遷移。 | | `exaApiKey?` | `string` | 無 | `exa` backend 的操作員金鑰。僅可寫入:管理讀取永遠不會傳回已儲存的值。 | diff --git a/gui/src/App.tsx b/gui/src/App.tsx index c9b2002ee3..fba76e0f44 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -12,6 +12,7 @@ import Integrations from "./pages/Integrations"; import Startup from "./pages/Startup"; import RemoteWorkspace from "./pages/RemoteWorkspace"; import ErrorBoundary from "./components/ErrorBoundary"; +import QuotaSummaryBar from "./components/quota-summary-bar/QuotaSummaryBar"; import { SidebarGithubRow } from "./components/sidebar-github-row"; import { IconGrid, IconServer, IconBoxes, IconBot, IconList, IconActivity, IconHardDrive, IconCodex, IconMenu, IconSun, IconMoon, IconMonitor, IconGlobe, IconPower, IconX, IconRefresh} from "./icons"; import { useI18n, useT, LOCALES, localeDisplayName, type Locale, type TKey } from "./i18n/shared"; @@ -464,6 +465,11 @@ export default function App() {
+ {targetsSettled && page !== "startup" && (!targets.connected || sharedSessionReady) && ( + + + + )} {/* Combos is full-bleed, unlike every other surface, and it is reachable only as a Models tab. `.main-inner` is App's element, so App is the only place that diff --git a/gui/src/client-resource.ts b/gui/src/client-resource.ts index 1ec40fc28f..bed565430d 100644 --- a/gui/src/client-resource.ts +++ b/gui/src/client-resource.ts @@ -1,4 +1,5 @@ import { useCallback, useLayoutEffect, useRef, useSyncExternalStore } from "react"; +import { hostDocumentHidden, onHostVisibilityChange } from "./host-visibility"; export type ResourceSnapshot = { data: T | undefined; @@ -196,7 +197,7 @@ function joinPollBucket(store: Store, intervalMs: number) { /** True when the document is currently hidden. Safe on non-browser runtimes. */ function documentIsHidden(): boolean { - return typeof document !== "undefined" && document.visibilityState === "hidden"; + return hostDocumentHidden(); } /** @@ -274,11 +275,14 @@ function recomputePoll(store: Store) { * * `replaceInflight: false` keeps this from cancelling work a visible-again mount just * started; if something is already loading, that request is the fresh answer. + * + * The subscription is host-visibility's deduped one, so a single hide (both the + * document event and the desktop host event on macOS) sweeps once, not twice. */ -let moduleVisibilityListener: (() => void) | null = null; +let moduleVisibilityUnsubscribe: (() => void) | null = null; function ensureVisibilityListener(_store: Store) { - if (typeof document === "undefined" || moduleVisibilityListener) return; + if (typeof document === "undefined" || moduleVisibilityUnsubscribe) return; const onVisibility = () => { syncAllBuckets(); if (documentIsHidden()) return; @@ -291,18 +295,15 @@ function ensureVisibilityListener(_store: Store) { } } }; - document.addEventListener("visibilitychange", onVisibility); - moduleVisibilityListener = onVisibility; + moduleVisibilityUnsubscribe = onHostVisibilityChange(onVisibility); } /** Drop the shared listener once nothing polls at all. */ function removeVisibilityListener(_store: Store) { - if (!moduleVisibilityListener) return; + if (!moduleVisibilityUnsubscribe) return; if (pollBuckets.size > 0) return; - if (typeof document !== "undefined") { - document.removeEventListener("visibilitychange", moduleVisibilityListener); - } - moduleVisibilityListener = null; + moduleVisibilityUnsubscribe(); + moduleVisibilityUnsubscribe = null; } async function runFetch( @@ -675,10 +676,8 @@ export function clearClientResourceStoresForTests(): void { } // The shared listener outlives individual stores, so the reset must drop it too or // a later suite's document would keep a handler bound to the previous one. - if (moduleVisibilityListener && typeof document !== "undefined") { - document.removeEventListener("visibilitychange", moduleVisibilityListener); - } - moduleVisibilityListener = null; + moduleVisibilityUnsubscribe?.(); + moduleVisibilityUnsubscribe = null; } /** diff --git a/gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx b/gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx index e478656baa..7f9091bae5 100644 --- a/gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx +++ b/gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx @@ -120,10 +120,12 @@ export default function ProviderWorkspaceShell({ /** * Called when a FORCED quota read settles, with whether it succeeded. * - * The shell owns the only `/api/provider-quotas` read, so it owns the only truthful - * completion signal. An operator-facing refresh button that resolved on its own would - * report success before the response landed — `fetchProviderQuotas(true)` is a - * synchronous state bump, not a request. + * The shell owns the only `/api/provider-quotas` read in this workspace, forced + * `?refresh=1` included — the header QuotaSummaryBar keeps a separate passive 60s read + * that never forces one — so the shell owns the only truthful completion signal for a + * forced refresh. An operator-facing refresh button that resolved on its own would report + * success before the response landed: `fetchProviderQuotas(true)` is a synchronous state + * bump, not a request. */ onQuotaRefreshSettled?: (ok: boolean, epoch: number) => void; /** True when the bump came from a mutation that needs the server to bypass its TTL. */ diff --git a/gui/src/components/quota-summary-bar/QuotaSummaryBar.tsx b/gui/src/components/quota-summary-bar/QuotaSummaryBar.tsx new file mode 100644 index 0000000000..655c7b459e --- /dev/null +++ b/gui/src/components/quota-summary-bar/QuotaSummaryBar.tsx @@ -0,0 +1,163 @@ +/** + * QuotaSummaryBar — always-visible provider quota strip above every page. + * + * Self-contained on purpose: App mounts it with one line, and it owns its own read of + * `/api/provider-quotas` (the same endpoint and 60s cadence Combos uses). It never forces + * `?refresh=1`, so it adds no upstream quota probes beyond the server's own TTL. + */ +import { useCallback, useEffect, useId, useRef, useState } from "react"; +import { useDataSurface } from "../../data-surface"; +import { useI18n, type Locale, type TFn } from "../../i18n/shared"; +import { formatProviderDisplayName } from "../../provider-icons"; +import { freshQuotaReportsFromResponse, type ProviderQuotaReportView } from "../../provider-workspace/report"; +import { buildQuotaSummary, formatQuotaPercent, type QuotaSummaryRow, type QuotaSummarySeverity, type QuotaSummaryWindow } from "../../quota-summary"; +import { formatResetFuture } from "../QuotaBars"; +import "./quota-summary-bar.css"; + +interface QuotaSummaryData { + fetchedAt: number; + reports: Record; +} + +const POLL_MS = 60_000; + +function formatClock(ms: number, locale: Locale): string { + try { + return new Intl.DateTimeFormat(locale, { hour: "2-digit", minute: "2-digit", hour12: false }).format(ms); + } catch { + return new Date(ms).toTimeString().slice(0, 5); + } +} + +function windowLabel(window: QuotaSummaryWindow, t: TFn): string { + return window.labelKey ? t(window.labelKey) : window.label ?? window.id; +} + +function severityText(severity: QuotaSummarySeverity, t: TFn): string { + if (severity === "critical") return t("quotaSummary.critical"); + if (severity === "warn") return t("quotaSummary.warn"); + return ""; +} + +function QuotaSummaryItem({ row, t, locale }: { row: QuotaSummaryRow; t: TFn; locale: Locale }) { + const [hovered, setHovered] = useState(false); + const [pinned, setPinned] = useState(false); + const rootRef = useRef(null); + const popoverId = useId(); + const open = hovered || pinned; + + useEffect(() => { + if (!open) return; + const onPointer = (event: PointerEvent) => { + if (rootRef.current && !rootRef.current.contains(event.target as Node)) setPinned(false); + }; + const onKey = (event: KeyboardEvent) => { + if (event.key === "Escape") { setPinned(false); setHovered(false); } + }; + document.addEventListener("pointerdown", onPointer); + document.addEventListener("keydown", onKey); + return () => { + document.removeEventListener("pointerdown", onPointer); + document.removeEventListener("keydown", onKey); + }; + }, [open]); + + const { headline } = row; + const warning = severityText(row.severity, t); + return ( +
  • setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + + {open && ( +
    +
    + {row.label} + {warning && {warning}} +
    + + + {row.windows.map(window => ( + + + + + + ))} + +
    {windowLabel(window, t)}{formatQuotaPercent(window.percent)} + {window.resetAt !== undefined ? formatResetFuture(window.resetAt, t, locale) : "-"} +
    + {row.updatedAt !== undefined && ( +
    + {t(row.observed ? "quotaSummary.observedAt" : "quotaSummary.dataAt", { time: formatClock(row.updatedAt, locale) })} +
    + )} +
    + )} +
  • + ); +} + +export default function QuotaSummaryBar({ apiBase }: { apiBase: string }) { + const { t, locale } = useI18n(); + const load = useCallback(async (signal: AbortSignal): Promise => { + const response = await fetch(`${apiBase}/api/provider-quotas`, { signal }); + if (!response.ok) throw new Error("quota summary load failed"); + const body = await response.json() as { reports?: unknown } | null; + return { fetchedAt: Date.now(), reports: freshQuotaReportsFromResponse(body?.reports) }; + }, [apiBase]); + const resource = useDataSurface( + `ocx.quota-summary.provider-quotas.v1:${apiBase}`, + [apiBase], + load, + { isEmpty: data => Object.keys(data.reports).length === 0, pollMs: POLL_MS, pauseWhenHidden: true }, + ); + + const data = resource.data; + if (!data) return null; + const rows = buildQuotaSummary(data.reports, provider => formatProviderDisplayName(provider, t)); + if (rows.length === 0) return null; + const stale = !resource.lastAttemptOk; + + return ( +
    +
      + {rows.map(row => )} +
    + + {t("quotaSummary.updated", { time: formatClock(data.fetchedAt, locale) })} + + {/* + Always mounted so the announcement survives the transition: an element that is + inserted already carrying its text is not reliably read out, so the failure and + the recovery would otherwise both go unannounced. Only this span is a live + region — the timestamp beside it changes every 60s and would not stop talking. + */} + + {stale ? t("quotaSummary.refreshFailed") : ""} + +
    + ); +} diff --git a/gui/src/components/quota-summary-bar/quota-summary-bar.css b/gui/src/components/quota-summary-bar/quota-summary-bar.css new file mode 100644 index 0000000000..307a93f519 --- /dev/null +++ b/gui/src/components/quota-summary-bar/quota-summary-bar.css @@ -0,0 +1,242 @@ +.quota-summary-bar { + position: sticky; + top: 0; + z-index: var(--z-sticky); + display: flex; + align-items: center; + gap: 12px; + min-height: 34px; + padding: 4px 16px; + border-bottom: 1px solid var(--border); + background: var(--bg); + font-size: var(--text-label); +} + +.quota-summary-list { + display: flex; + flex: 1; + min-width: 0; + flex-wrap: wrap; + align-items: center; + gap: 4px; + margin: 0; + padding: 0; + list-style: none; +} + +.quota-summary-item { + position: relative; +} + +.quota-summary-chip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 8px; + border: 1px solid transparent; + border-radius: 999px; + background: none; + color: var(--text); + font: inherit; + cursor: pointer; +} + +.quota-summary-chip:hover, +.quota-summary-chip[aria-expanded="true"] { + border-color: var(--border); +} + +.quota-summary-chip:focus-visible { + outline: 2px solid var(--text); + outline-offset: 1px; +} + +.quota-summary-name { + color: var(--muted); +} + +.quota-summary-pct { + font-weight: 600; + font-variant-numeric: tabular-nums; +} + +.quota-summary-flag { + display: inline-grid; + place-items: center; + width: 14px; + height: 14px; + border-radius: 50%; + color: var(--bg); + font-size: var(--text-micro); + font-weight: 700; +} + +.quota-summary-item--warn .quota-summary-chip { + background: var(--amber-soft); +} + +.quota-summary-item--warn .quota-summary-pct { + color: var(--amber); +} + +.quota-summary-item--warn .quota-summary-flag { + background: var(--amber); +} + +.quota-summary-item--critical .quota-summary-chip { + border-color: var(--red); + background: var(--red-soft); +} + +.quota-summary-item--critical .quota-summary-pct { + color: var(--red); +} + +.quota-summary-item--critical .quota-summary-flag { + background: var(--red); +} + +.quota-summary-popover { + position: absolute; + top: calc(100% + 4px); + left: 0; + z-index: var(--z-popover); + min-width: 260px; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14); +} + +.quota-summary-popover-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 6px; +} + +.quota-summary-badge { + padding: 1px 6px; + border-radius: 999px; + font-size: var(--text-caption); +} + +.quota-summary-badge--warn { + color: var(--amber); + background: var(--amber-soft); +} + +.quota-summary-badge--critical { + color: var(--red); + background: var(--red-soft); +} + +.quota-summary-table { + width: 100%; + border-collapse: collapse; +} + +.quota-summary-table th, +.quota-summary-table td { + padding: 3px 0; + font-weight: 400; + text-align: left; + white-space: nowrap; +} + +.quota-summary-table td.quota-summary-row-pct { + padding: 3px 12px; + font-weight: 600; + font-variant-numeric: tabular-nums; + text-align: right; + white-space: nowrap; +} + +.quota-summary-row-reset { + color: var(--muted); + white-space: nowrap; +} + +/* + Window labels are provider-named and arrive verbatim, so they can be longer than + the popover is wide. `nowrap` on the label cell pushed the percent and reset cells + out of a narrow mobile popover; let the label wrap and keep the numbers intact, so + the row stays readable instead of losing its figures off the right edge. +*/ +.quota-summary-table th[scope="row"] { + white-space: normal; + overflow-wrap: anywhere; +} + +.quota-summary-row--warn .quota-summary-row-pct { + color: var(--amber); +} + +.quota-summary-row--critical .quota-summary-row-pct { + color: var(--red); +} + +.quota-summary-popover-foot { + margin-top: 6px; + color: var(--muted); + font-size: var(--text-caption); +} + +.quota-summary-updated { + flex-shrink: 0; + color: var(--muted); + font-size: var(--text-caption); + white-space: nowrap; +} + +.quota-summary-updated--stale { + color: var(--amber); +} + +/* + The combos workspace is a fixed 100dvh shell. With the bar above it, let the shell take + the remaining height instead of pushing the page 1 bar-height past the viewport. +*/ +.main:has(> .quota-summary-bar):has(> .main-inner--combos .combos-workspace-shell) { + display: flex; + flex-direction: column; + height: 100dvh; +} + +.main:has(> .quota-summary-bar) > .main-inner.main-inner--combos:has(.combos-workspace-shell) { + flex: 1 1 auto; + min-height: 0; + height: auto; +} + +/* The mobile top bar is already sticky at top: 0; scroll the summary with the page there. */ +@media (max-width: 760px) { + .quota-summary-bar { + position: static; + padding: 4px 10px; + } + + .quota-summary-popover { + position: fixed; + top: auto; + left: 8px; + right: 8px; + min-width: 0; + margin-top: 4px; + } + + /* + The mobile app grid already reserves an `auto` row for `.mobile-topbar` above a + `1fr` main row (see the narrow-screen block in styles.css). A `100dvh` main row + therefore measures a full viewport *below* the bar: the document becomes bar + height + viewport, the page scrolls by exactly the bar, and the combos shell is + clipped at the fold. Fill the row the grid reserved, mirroring how + `.main-inner--combos` itself drops to `height: 100%` on mobile. + */ + .main:has(> .quota-summary-bar):has(> .main-inner--combos .combos-workspace-shell) { + height: 100%; + min-height: 0; + } +} diff --git a/gui/src/host-visibility.ts b/gui/src/host-visibility.ts new file mode 100644 index 0000000000..b8264b33e5 --- /dev/null +++ b/gui/src/host-visibility.ts @@ -0,0 +1,71 @@ +/** + * The single answer to "is the dashboard hidden right now?". + * + * A plain browser is answered by `document.visibilityState`. The desktop shell is not + * always: WebView2 on Windows is reported to keep "visible" while the Tauri window sits + * hidden in the tray (tauri issues #10592, #6864), so every dashboard poller went on + * fetching for a window nobody could see. macOS WKWebView does flip it (measured). + * The native side closes that gap by pushing the truth into the page — + * `window.__OPENCODEX_HOST_VISIBLE__` plus an `opencodex:host-visibility` event, on + * every show/hide and again after each page load — and this module folds both signals + * into one predicate. + * + * Consumers read {@link hostDocumentHidden} and subscribe through + * {@link onHostVisibilityChange} instead of touching `document.visibilityState`; + * the tray popup keeps its own equivalent bridge (`opencodex:tray-visibility`). + */ + +declare global { + interface Window { + /** + * Pushed by the desktop shell: `false` while the main dashboard window is hidden + * to the tray, `true` when it is shown. Absent in a browser, where the flag has no + * meaning and `undefined !== false` keeps the document the only signal. + */ + __OPENCODEX_HOST_VISIBLE__?: boolean; + } +} + +/** True when the dashboard is hidden — by the browser tab or by the desktop host. */ +export function hostDocumentHidden(): boolean { + if (typeof document !== "undefined" && document.visibilityState === "hidden") return true; + return typeof window !== "undefined" && window.__OPENCODEX_HOST_VISIBLE__ === false; +} + +/** + * Call `callback` on every real host-visibility transition, and return the unsubscribe. + * + * Both signals are watched: `visibilitychange` for browsers and macOS, the custom host + * event for the Windows case the standard event cannot see. On macOS both arrive for a + * single hide, and a consumer's visible-again path is a make-up fetch, so the + * transition is deduped against the last computed value per subscription — one hide is + * one callback, one show is one callback, and a duplicate signal costs nothing. + */ +export function onHostVisibilityChange(callback: () => void): () => void { + let last = hostDocumentHidden(); + + const notify = () => { + const next = hostDocumentHidden(); + if (next === last) return; + last = next; + callback(); + }; + + const onHostEvent = (event: Event) => { + const detail = (event as CustomEvent).detail; + // The flag must land before the reading below, or the event would evaluate against + // the previous state and the transition would be deduped away. + if (typeof window !== "undefined" && typeof detail === "boolean") { + window.__OPENCODEX_HOST_VISIBLE__ = detail; + } + notify(); + }; + + if (typeof document !== "undefined") document.addEventListener("visibilitychange", notify); + if (typeof window !== "undefined") window.addEventListener("opencodex:host-visibility", onHostEvent); + + return () => { + if (typeof document !== "undefined") document.removeEventListener("visibilitychange", notify); + if (typeof window !== "undefined") window.removeEventListener("opencodex:host-visibility", onHostEvent); + }; +} diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index b50da156f7..3d6318e2d7 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -356,6 +356,8 @@ export const de: Record = { "dash.visionModelHint": "Modell zur Beschreibung von Bildern für nur-Text-Routen. Erfordert ChatGPT-Login.", "dash.webSearchSidecar": "Websuche-Sidecar", "dash.webSearchSidecarHint": "Backend und Modell für die Websuche gerouteter Modelle auswählen.", + "dash.webSearchOff": "Aus", + "dash.webSearchCodexSync": "Gespeichert. Codex' Config wurde nicht neu geschrieben – nutze „Modelle synchronisieren“.", "dash.webSearchStream": "Antworten live streamen", "dash.webSearchStreamHint": "Führenden Text und Reasoning live streamen, bis das Modell über einen Tool-Aufruf entscheidet; der Rest bleibt für das Abfangen der Suche gepuffert. Text vor einer Suche kann sich teilweise wiederholen.", "dash.visionSidecar": "Vision-Sidecar", @@ -3177,4 +3179,12 @@ export const de: Record = { "remote.event.status": "Status", "remote.event.tool": "Remote-Werkzeug", "remote.event.error": "Fehler", + "quotaSummary.aria": "Anbieter-Kontingentübersicht", + "quotaSummary.updated": "Aktualisiert {time}", + "quotaSummary.dataAt": "Daten von {time}", + "quotaSummary.observedAt": "Beobachtet um {time}", + "quotaSummary.warn": "Über 70 % genutzt", + "quotaSummary.critical": "Über 90 % genutzt", + "quotaSummary.credits": "Guthaben", + "quotaSummary.refreshFailed": "Letzte Aktualisierung fehlgeschlagen; vorheriger Stand wird angezeigt", }; diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 199e6e9abb..b6a9c700e4 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -368,6 +368,8 @@ export const en = { "dash.visionModelHint": "Model used to describe images for text-only routed models. Requires ChatGPT login.", "dash.webSearchSidecar": "Web search sidecar", "dash.webSearchSidecarHint": "Choose the backend and model used for web search on routed models.", + "dash.webSearchOff": "Off", + "dash.webSearchCodexSync": "Saved. Codex's config was not rewritten — run Sync models to apply it.", "dash.webSearchStream": "Stream answers live", "dash.webSearchStreamHint": "Stream the model’s leading text and reasoning live until it decides on a tool call; the rest of the turn stays buffered for search interception. Text written before a search may partially repeat.", "dash.visionSidecar": "Vision sidecar", @@ -3211,6 +3213,14 @@ export const en = { "remote.event.status": "Status", "remote.event.tool": "Remote tool", "remote.event.error": "Error", + "quotaSummary.aria": "Provider quota summary", + "quotaSummary.updated": "Updated {time}", + "quotaSummary.dataAt": "Data from {time}", + "quotaSummary.observedAt": "Observed at {time}", + "quotaSummary.warn": "70%+ used", + "quotaSummary.critical": "90%+ used", + "quotaSummary.credits": "Credits", + "quotaSummary.refreshFailed": "Last refresh failed; showing the previous reading", } as const; export type TKey = keyof typeof en; diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index 4b87fe1474..3915f020b1 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -358,6 +358,8 @@ export const fr: Record = { "dash.visionModelHint": "Modèle utilisé pour décrire les images aux modèles routés en mode texte uniquement. Nécessite une connexion à ChatGPT.", "dash.webSearchSidecar": "Service auxiliaire de recherche Web", "dash.webSearchSidecarHint": "Choisissez le moteur et le modèle utilisés pour la recherche Web sur les modèles routés.", + "dash.webSearchOff": "Désactivé", + "dash.webSearchCodexSync": "Enregistré. La config de Codex n'a pas été réécrite — lancez « Synchroniser les modèles ».", "dash.webSearchStream": "Diffuser les réponses en direct", "dash.webSearchStreamHint": "Diffuse en direct le texte initial et le raisonnement du modèle jusqu’à ce qu’il décide d’appeler un outil ; le reste du tour demeure en mémoire tampon pour intercepter la recherche. Le texte produit avant une recherche peut être partiellement répété.", "dash.visionSidecar": "Service auxiliaire de vision", @@ -3166,4 +3168,12 @@ export const fr: Record = { "remote.event.status": "État", "remote.event.tool": "Outil distant", "remote.event.error": "Erreur", + "quotaSummary.aria": "Résumé des quotas des fournisseurs", + "quotaSummary.updated": "Mis à jour à {time}", + "quotaSummary.dataAt": "Données de {time}", + "quotaSummary.observedAt": "Observé à {time}", + "quotaSummary.warn": "Plus de 70 % utilisés", + "quotaSummary.critical": "Plus de 90 % utilisés", + "quotaSummary.credits": "Crédits", + "quotaSummary.refreshFailed": "Échec de la dernière actualisation ; affichage de la lecture précédente", }; diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 29a825d332..93d04f38a9 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -365,6 +365,8 @@ export const ja: Record = { "dash.visionModelHint": "テキスト専用ルーティングモデルで画像を説明するために使うモデル。ChatGPT ログインが必要です。", "dash.webSearchSidecar": "ウェブ検索サイドカー", "dash.webSearchSidecarHint": "ルーティングモデルでウェブ検索に使うバックエンドとモデルを選択します。", + "dash.webSearchOff": "オフ", + "dash.webSearchCodexSync": "保存しました。Codex の設定はまだ書き換えられていません —「モデルを同期」を実行してください。", "dash.webSearchStream": "回答をライブ配信", "dash.webSearchStreamHint": "モデルがツール呼び出しを決定するまで、先頭のテキストと推論をライブ配信します。以降は検索インターセプトのためバッファされます。検索前のテキストは一部繰り返される場合があります。", "dash.visionSidecar": "ビジョンサイドカー", @@ -3199,4 +3201,12 @@ export const ja: Record = { "remote.event.status": "状態", "remote.event.tool": "リモートツール", "remote.event.error": "エラー", + "quotaSummary.aria": "プロバイダークォータ概要", + "quotaSummary.updated": "{time} 更新", + "quotaSummary.dataAt": "{time} 時点のデータ", + "quotaSummary.observedAt": "{time} に観測", + "quotaSummary.warn": "70%以上使用", + "quotaSummary.critical": "90%以上使用", + "quotaSummary.credits": "クレジット", + "quotaSummary.refreshFailed": "最新の更新に失敗しました。前回の値を表示しています", }; diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 2e87cd5b5f..66a2f3300c 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -360,6 +360,8 @@ export const ko: Record = { "dash.visionModelHint": "텍스트 전용 라우팅 모델에 이미지를 설명하는 데 사용되는 모델입니다. ChatGPT 로그인 필요.", "dash.webSearchSidecar": "웹 검색 사이드카", "dash.webSearchSidecarHint": "라우팅 모델의 웹 검색에 쓸 백엔드와 모델을 고릅니다.", + "dash.webSearchOff": "끔", + "dash.webSearchCodexSync": "저장했습니다. Codex 설정이 아직 다시 작성되지 않았습니다 — “모델 동기화”를 실행하세요.", "dash.webSearchStream": "응답 실시간 스트리밍", "dash.webSearchStreamHint": "모델이 도구 호출을 결정할 때까지 앞부분 텍스트와 추론을 실시간 스트리밍합니다. 이후는 검색 가로채기를 위해 버퍼링됩니다. 검색 전 텍스트가 일부 반복될 수 있습니다.", "dash.visionSidecar": "비전 사이드카", @@ -3199,4 +3201,12 @@ export const ko: Record = { "remote.event.status": "상태", "remote.event.tool": "원격 도구", "remote.event.error": "오류", + "quotaSummary.aria": "프로바이더 사용량 요약", + "quotaSummary.updated": "{time} 갱신", + "quotaSummary.dataAt": "{time} 기준 데이터", + "quotaSummary.observedAt": "{time} 관측", + "quotaSummary.warn": "70% 이상 사용", + "quotaSummary.critical": "90% 이상 사용", + "quotaSummary.credits": "크레딧", + "quotaSummary.refreshFailed": "최근 갱신에 실패해 이전 값을 표시합니다", }; diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 1279c60b09..29428d8126 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -365,6 +365,8 @@ export const ru: Record = { "dash.visionModelHint": "Модель, которая описывает изображения для маршрутизируемых моделей, работающих только с текстом. Требуется вход в аккаунт ChatGPT.", "dash.webSearchSidecar": "Сайдкар веб-поиска", "dash.webSearchSidecarHint": "Выберите бэкенд и модель, используемые для веб-поиска на маршрутизируемых моделях.", + "dash.webSearchOff": "Выкл", + "dash.webSearchCodexSync": "Сохранено. Конфигурация Codex не перезаписана — запустите «Синхронизировать модели».", "dash.webSearchStream": "Стримить ответы вживую", "dash.webSearchStreamHint": "Транслировать начальный текст и рассуждения вживую, пока модель не решит вызвать инструмент; остальное буферизуется для перехвата поиска. Текст до поиска может частично повторяться.", "dash.visionSidecar": "Сайдкар для изображений", @@ -3200,4 +3202,12 @@ export const ru: Record = { "remote.event.status": "Состояние", "remote.event.tool": "Удалённый инструмент", "remote.event.error": "Ошибка", + "quotaSummary.aria": "Сводка квот провайдеров", + "quotaSummary.updated": "Обновлено в {time}", + "quotaSummary.dataAt": "Данные на {time}", + "quotaSummary.observedAt": "Замечено в {time}", + "quotaSummary.warn": "Использовано более 70%", + "quotaSummary.critical": "Использовано более 90%", + "quotaSummary.credits": "Кредиты", + "quotaSummary.refreshFailed": "Последнее обновление не удалось; показаны предыдущие данные", }; diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index cef6fa0703..8814780b1c 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -366,6 +366,8 @@ export const tr: Record = { "dash.visionModelHint": "Salt metin yönlendirilen modeller için görselleri tanımlamakta kullanılan model. ChatGPT girişi gerektirir.", "dash.webSearchSidecar": "Web arama yan aracı (sidecar)", "dash.webSearchSidecarHint": "Yönlendirilen modellerde web araması için kullanılan arka ucu ve modeli seçin.", + "dash.webSearchOff": "Kapalı", + "dash.webSearchCodexSync": "Kaydedildi. Codex yapılandırması henüz yeniden yazılmadı — “Modelleri senkronize et”i çalıştırın.", "dash.webSearchStream": "Yanıtları canlı akıt", "dash.webSearchStreamHint": "Model bir araç çağrısına karar verene kadar baştaki metni ve akıl yürütmeyi canlı akıtır; kalanı arama yakalama için arabelleğe alınır. Aramadan önce yazılan metin kısmen tekrarlanabilir.", "dash.visionSidecar": "Görsel yan aracı (sidecar)", @@ -3200,4 +3202,12 @@ export const tr: Record = { "remote.event.status": "Durum", "remote.event.tool": "Uzak araç", "remote.event.error": "Hata", + "quotaSummary.aria": "Sağlayıcı kota özeti", + "quotaSummary.updated": "{time} güncellendi", + "quotaSummary.dataAt": "{time} verisi", + "quotaSummary.observedAt": "{time} gözlemlendi", + "quotaSummary.warn": "%70+ kullanıldı", + "quotaSummary.critical": "%90+ kullanıldı", + "quotaSummary.credits": "Krediler", + "quotaSummary.refreshFailed": "Son yenileme başarısız; önceki değer gösteriliyor", }; diff --git a/gui/src/i18n/vi.ts b/gui/src/i18n/vi.ts index 099ce7cafb..78fd3b1e6d 100644 --- a/gui/src/i18n/vi.ts +++ b/gui/src/i18n/vi.ts @@ -358,6 +358,8 @@ export const vi: Record = { "dash.visionModelHint": "Model được sử dụng để mô tả hình ảnh cho các model định tuyến chỉ hỗ trợ văn bản. Yêu cầu đăng nhập ChatGPT.", "dash.webSearchSidecar": "Web search sidecar", "dash.webSearchSidecarHint": "Chọn backend và model được sử dụng cho tìm kiếm web trên các models định tuyến.", + "dash.webSearchOff": "Tắt", + "dash.webSearchCodexSync": "Đã lưu. Cấu hình Codex chưa được ghi lại — hãy chạy “Đồng bộ models”.", "dash.webSearchStream": "Phát trực tuyến (Stream) các câu trả lời trực tiếp", "dash.webSearchStreamHint": "Phát trực tuyến các văn bản dẫn dắt và quá trình lý luận của model cho đến khi nó quyết định gọi một công cụ; phần còn lại của lượt chạy sẽ được lưu đệm (buffered) để can thiệp tìm kiếm. Văn bản được viết trước một tìm kiếm có thể lặp lại một phần.", "dash.visionSidecar": "Vision sidecar", @@ -3169,4 +3171,12 @@ export const vi: Record = { "models.fastRows.disabled": "Đã tắt hàng model Fast.", "models.fastRows.loadFailed": "Không thể tải cài đặt hàng Fast.", "models.fastRows.updateFailed": "Không thể cập nhật cài đặt hàng Fast.", + "quotaSummary.aria": "Tóm tắt hạn mức nhà cung cấp", + "quotaSummary.updated": "Cập nhật lúc {time}", + "quotaSummary.dataAt": "Dữ liệu lúc {time}", + "quotaSummary.observedAt": "Ghi nhận lúc {time}", + "quotaSummary.warn": "Đã dùng trên 70%", + "quotaSummary.critical": "Đã dùng trên 90%", + "quotaSummary.credits": "Tín dụng", + "quotaSummary.refreshFailed": "Lần làm mới gần nhất thất bại; đang hiển thị số liệu trước đó", }; diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 1c8d62080b..583fe6c421 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -255,6 +255,8 @@ export const zhTW: Record = { "dash.visionModelHint": "為純文字路由模型描述圖像的模型。需要 ChatGPT 登入。", "dash.webSearchSidecar": "網頁搜尋附屬服務", "dash.webSearchSidecarHint": "選擇路由模型進行網頁搜尋時使用的後端和模型。", + "dash.webSearchOff": "關閉", + "dash.webSearchCodexSync": "已儲存。Codex 的設定尚未重寫 — 請執行「同步模型」。", "dash.webSearchStream": "即時串流輸出回答", "dash.webSearchStreamHint": "即時串流輸出開頭的文字和推理,直到模型決定呼叫工具;其餘部分為攔截搜尋而保持緩衝。搜尋前的文字可能會部分重複。", "dash.visionSidecar": "視覺附屬服務", @@ -3163,4 +3165,12 @@ export const zhTW: Record = { "remote.event.status": "狀態", "remote.event.tool": "遠端工具", "remote.event.error": "錯誤", + "quotaSummary.aria": "供應商配額概覽", + "quotaSummary.updated": "{time} 更新", + "quotaSummary.dataAt": "{time} 的資料", + "quotaSummary.observedAt": "{time} 觀測", + "quotaSummary.warn": "已用 70% 以上", + "quotaSummary.critical": "已用 90% 以上", + "quotaSummary.credits": "額度", + "quotaSummary.refreshFailed": "最近一次重新整理失敗,顯示上次資料", }; diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 6d05e3854a..5d222962c0 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -360,6 +360,8 @@ export const zh: Record = { "dash.visionModelHint": "为纯文本路由模型描述图像的模型。需要 ChatGPT 登录。", "dash.webSearchSidecar": "网页搜索附属服务", "dash.webSearchSidecarHint": "选择路由模型进行网页搜索时使用的后端和模型。", + "dash.webSearchOff": "关闭", + "dash.webSearchCodexSync": "已保存。Codex 的配置尚未重写 — 请运行“同步模型”。", "dash.webSearchStream": "实时流式输出回答", "dash.webSearchStreamHint": "实时流式输出开头的文本和推理,直到模型决定调用工具;其余部分为拦截搜索而保持缓冲。搜索前的文本可能会部分重复。", "dash.visionSidecar": "视觉附属服务", @@ -3198,4 +3200,12 @@ export const zh: Record = { "remote.event.status": "状态", "remote.event.tool": "远程工具", "remote.event.error": "错误", + "quotaSummary.aria": "提供商配额概览", + "quotaSummary.updated": "{time} 更新", + "quotaSummary.dataAt": "{time} 的数据", + "quotaSummary.observedAt": "{time} 观测", + "quotaSummary.warn": "已用 70% 以上", + "quotaSummary.critical": "已用 90% 以上", + "quotaSummary.credits": "额度", + "quotaSummary.refreshFailed": "最近一次刷新失败,显示上次数据", }; diff --git a/gui/src/pages/Combos.tsx b/gui/src/pages/Combos.tsx index 18b997a4b5..6d299c6c99 100644 --- a/gui/src/pages/Combos.tsx +++ b/gui/src/pages/Combos.tsx @@ -8,6 +8,7 @@ import { nextProviderQuotaStateExpiration, toPutBody, } from "../combo-workspace-data"; +import { hostDocumentHidden, onHostVisibilityChange } from "../host-visibility"; import { hideRedundantChatGptForwardProviders } from "../provider-workspace/catalog"; import { readSessionListCacheEntry, writeSessionListCacheEntry } from "../session-list-cache"; import { Notice } from "../ui"; @@ -252,11 +253,11 @@ export default function Combos({ // A new snapshot may be newer than this clock, so unknown state also gets one immediate check. const timer = window.setTimeout(recheck, quotaExpiry === undefined ? 0 : Math.max(0, quotaExpiry - Date.now())); - const onVisible = () => { if (document.visibilityState === "visible") recheck(); }; - document.addEventListener("visibilitychange", onVisible); + const onVisible = () => { if (!hostDocumentHidden()) recheck(); }; + const unsubscribeVisibility = onHostVisibilityChange(onVisible); return () => { window.clearTimeout(timer); - document.removeEventListener("visibilitychange", onVisible); + unsubscribeVisibility(); }; }, [active, apiBase, quotaResource.data, quotaResource.lastAttemptOk, quotaExpiry]); diff --git a/gui/src/pages/Providers.tsx b/gui/src/pages/Providers.tsx index dc86595793..87bf848cd2 100644 --- a/gui/src/pages/Providers.tsx +++ b/gui/src/pages/Providers.tsx @@ -448,7 +448,9 @@ export default function Providers({ apiBase }: { apiBase: string }) { // back empty on the next visit. A microtask cannot be cancelled, so the requests always go out. // Guarded per identity because StrictMode double-invokes this effect on mount and an // uncancellable microtask would otherwise bootstrap the page twice. - // Quotas: workspace shell owns /api/provider-quotas — do not double-fetch on mount. + // Quotas: the workspace shell owns this page's /api/provider-quotas read, including the + // forced ?refresh=1 fan-out — do not double-fetch on mount. The header QuotaSummaryBar + // keeps its own separate, passive 60s read of the same endpoint. if (bootstrapKeyRef.current === apiBase) return; bootstrapKeyRef.current = apiBase; void Promise.resolve().then(() => { diff --git a/gui/src/pages/dashboard-overview-sections.tsx b/gui/src/pages/dashboard-overview-sections.tsx index 2aa28acb10..8ea50ba758 100644 --- a/gui/src/pages/dashboard-overview-sections.tsx +++ b/gui/src/pages/dashboard-overview-sections.tsx @@ -17,6 +17,8 @@ import { shadowCallModelOptions, webSearchSidecarSelectionForModel, updateJobLabel, + webSearchEnabledPatch, + sidecarCodexWritePending, visionEnabledPatch, visionMaxDescriptionsPatch, visionReasoningLadder, @@ -441,10 +443,14 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) { t, settings, settingsSaving, syncing, toggleCodexAutoStart, toggleCodexDesktopAuthless, toggleCodexClientCompaction, sidecar, sidecarSaving, sidecarModels, visionModels, models, saveSidecar, + sidecarCodexApply, shadowCall, shadowCallSaving, shadowCallHelpTriggerRef, shadowCallHelpOpen, setShadowCallHelpOpen, saveShadowCall, } = d; const visionEnabled = sidecar?.vision?.enabled !== false; const visionModel = visionEnabled ? (sidecar?.vision?.model ?? "gpt-5.6-luna") : ""; + const webSearchEnabled = sidecar?.webSearch?.enabled !== false; + // Same shape as the Vision card: Off is a row in the picker, and choosing a model is the way back. + const webSearchModel = webSearchEnabled ? (sidecar?.webSearch?.model ?? "gpt-5.6-luna") : ""; const persistedVisionReasoning = sidecar?.vision?.reasoning ?? "low"; const visionLadder = visionReasoningLadder(models, visionModel); const visionReasoning = clampVisionReasoningToLadder(visionLadder, persistedVisionReasoning); @@ -553,6 +559,15 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) {
    {t("dash.webSearchSidecar")}
    {t("dash.webSearchSidecarHint")}
    + {/* The switch is stored even when Codex's own key was not rewritten. Saying nothing + here would read as "the native tool is off now", which is exactly the state the + operator asked for and may not have. */} + {sidecarCodexWritePending(sidecarCodexApply) && ( +
    + + {t("dash.webSearchCodexSync")} +
    + )}
    {/* Same two-row shape as the vision card: the model select owns the first row, and the secondary control sits right-aligned on its own row below. Sharing the @@ -561,10 +576,17 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) {