From 0e76b20bd953fbb91739c54511bc657db07e409e Mon Sep 17 00:00:00 2001 From: jun Date: Fri, 4 Sep 2026 10:44:20 +0900 Subject: [PATCH 1/2] revert(gui): restore the dashboard affordances the minimal pass collapsed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 260904 dashboard-minimal roadmap traded working controls for visual quiet, and the result cost real function. Reverts #3382 (sidebar footer), #3387 (dashboard home), #3390 (models catalog), #3395 (usage) and #3399 (the i18n prune that removed those surfaces keys). What comes back: the labelled sidebar footer rows instead of two rows of unlabelled 28px orbs; the v1/base/v2 subagent surface switch inline on Models, which is a primary control and not an advanced disclosure; the ultra-mode effort controls; the sidecar and memory cards without their closed disclosure; and the Usage active-days card with its heatmap inline rather than behind a 일별 활동 details. The v1/base/v2 switch is kept in BOTH homes: the revert restores Models, and UltraModeState/UltraModePatch keep multiAgentMode so the Subagents copy added by #3390 still reads and writes /api/v2. Three imports the revert left dangling (Tooltip, IconInfo, TKey) are restored alongside it. dashboard-tabs.test.ts anchored its .page-tabs CSS lookup on a bare substring, which now matches an earlier descendant rule added after it was written; it reads the base rule at line start instead. --- .../content/docs/fr/guides/web-dashboard.md | 24 +- .../src/content/docs/guides/web-dashboard.md | 25 +- .../content/docs/ja/guides/web-dashboard.md | 16 +- .../content/docs/ko/guides/web-dashboard.md | 18 +- .../content/docs/ru/guides/web-dashboard.md | 16 +- .../content/docs/tr/guides/web-dashboard.md | 22 +- .../docs/zh-cn/guides/web-dashboard.md | 16 +- .../docs/zh-tw/guides/web-dashboard.md | 32 +-- gui/scripts/find-orphan-keys.mjs | 73 ------ gui/src/App.tsx | 60 ++--- gui/src/app-routing.ts | 16 +- .../components/MemoryObservabilityCard.tsx | 12 +- gui/src/components/github-star-button.tsx | 108 -------- gui/src/components/sidebar-github-row.tsx | 130 +++++++-- .../subagents-workspace/EffortCapSection.tsx | 78 ------ .../SubagentDelegationSection.tsx | 3 +- .../SubagentsWorkspace.tsx | 11 - gui/src/i18n/de.ts | 38 ++- gui/src/i18n/en.ts | 38 ++- gui/src/i18n/fr.ts | 38 ++- gui/src/i18n/ja.ts | 38 ++- gui/src/i18n/ko.ts | 38 ++- gui/src/i18n/ru.ts | 40 ++- gui/src/i18n/tr.ts | 38 ++- gui/src/i18n/zh-TW.ts | 38 ++- gui/src/i18n/zh.ts | 39 ++- gui/src/pages/Dashboard.tsx | 96 ++++++- gui/src/pages/Models.tsx | 116 +++++--- gui/src/pages/Startup.tsx | 3 - gui/src/pages/Subagents.tsx | 1 - gui/src/pages/Usage.tsx | 67 ++--- gui/src/pages/dashboard-core-poll.ts | 142 +++++++++- gui/src/pages/dashboard-dialogs.tsx | 86 +++++- gui/src/pages/dashboard-models-section.tsx | 83 ++++++ gui/src/pages/dashboard-overview-head.tsx | 69 +++-- gui/src/pages/dashboard-overview-panels.tsx | 20 +- gui/src/pages/dashboard-overview-sections.tsx | 197 +++++++++++++- gui/src/pages/dashboard-providers-section.tsx | 38 +++ gui/src/pages/dashboard-shared.ts | 14 + gui/src/pages/startup-sections.tsx | 20 -- gui/src/pages/use-codex-autostart.ts | 45 ---- gui/src/pages/use-dashboard-data.ts | 247 +++++++++++++++++- gui/src/pages/use-subagent-delegation.ts | 4 +- gui/src/styles.css | 84 +++--- gui/tests/app-sidebar-actions.test.ts | 5 +- gui/tests/codex-stale-banner.test.ts | 15 +- gui/tests/dashboard-contracts.test.ts | 48 ++-- gui/tests/dashboard-legacy-hashes.test.ts | 31 --- gui/tests/dashboard-rehome.test.tsx | 133 ---------- gui/tests/dashboard-tabs.test.ts | 73 ++++++ gui/tests/fr-localization.test.ts | 2 - gui/tests/github-star-button.test.tsx | 122 --------- gui/tests/i18n-orphans-baseline.ts | 162 ------------ gui/tests/i18n-orphans.test.ts | 25 -- gui/tests/locale-dropdown-bounds.test.ts | 4 +- gui/tests/models-advanced-disclosure.test.ts | 75 ------ .../models-keep-native-v1-placement.test.ts | 9 +- gui/tests/multi-agent-guidance.test.tsx | 2 +- gui/tests/providers-hash-history.test.tsx | 3 +- gui/tests/shadow-call-model-options.test.ts | 5 +- gui/tests/sidebar-rows.test.ts | 52 ++-- gui/tests/startup-revisit-cache.test.tsx | 5 +- gui/tests/subagents-ultra-mode.test.tsx | 47 ---- gui/tests/usage-coverage-tooltip.test.tsx | 72 ----- gui/tests/usage-minimal.test.ts | 43 --- 65 files changed, 1707 insertions(+), 1563 deletions(-) delete mode 100644 gui/scripts/find-orphan-keys.mjs delete mode 100644 gui/src/components/github-star-button.tsx delete mode 100644 gui/src/components/subagents-workspace/EffortCapSection.tsx create mode 100644 gui/src/pages/dashboard-models-section.tsx create mode 100644 gui/src/pages/dashboard-providers-section.tsx delete mode 100644 gui/src/pages/use-codex-autostart.ts delete mode 100644 gui/tests/dashboard-legacy-hashes.test.ts delete mode 100644 gui/tests/dashboard-rehome.test.tsx create mode 100644 gui/tests/dashboard-tabs.test.ts delete mode 100644 gui/tests/github-star-button.test.tsx delete mode 100644 gui/tests/i18n-orphans-baseline.ts delete mode 100644 gui/tests/i18n-orphans.test.ts delete mode 100644 gui/tests/models-advanced-disclosure.test.ts delete mode 100644 gui/tests/usage-coverage-tooltip.test.tsx delete mode 100644 gui/tests/usage-minimal.test.ts 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 2b8c3973a4..9970b26bda 100644 --- a/docs-site/src/content/docs/fr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/fr/guides/web-dashboard.md @@ -40,18 +40,18 @@ gestionnaire de mots de passe. | Zone | Fonction | | --- | --- | -| **Résumé du tableau de bord** | État en ligne, version avec durée de fonctionnement, nombre de fournisseurs et total de jetons sur 30 jours, suivis de la barre de protection au redémarrage, de la synchronisation des modèles, d'une section **Services auxiliaires** repliée et de la pression mémoire. Les anciens onglets fournisseurs et modèles ont disparu ; utilisez **Fournisseurs** et **Modèles**. | -| **Délégation de sous-agent** | Sur **Sous-agents** : choisissez un modèle natif ou routé et, facultativement, un effort de raisonnement partagés entre les consignes de délégation OpenCodex et l'option distincte de valeurs par défaut natives. Il ne s'agit pas d'un routeur par création de sous-agent côté proxy ; voir ci-dessous. | -| **Services auxiliaires** | Section repliée du tableau de bord : choisissez le modèle et l'effort de recherche web, ainsi que le modèle de description visuelle. Les modifications s'appliquent à la requête suivante. | +| **Résumé du tableau de bord** | Mode multi-agent, état en ligne, version, durée de fonctionnement, nombre de fournisseurs, total de jetons sur 30 jours, fournisseurs actifs et modèles natifs/routés disponibles. | +| **Délégation de sous-agent** | Choisissez un modèle natif ou routé et, facultativement, un effort de raisonnement partagés entre les consignes de délégation OpenCodex et l'option distincte de valeurs par défaut natives. Il ne s'agit pas d'un routeur par création de sous-agent côté proxy ; voir ci-dessous. | +| **Services auxiliaires** | Choisissez le modèle et l'effort de recherche web, ainsi que le modèle de description visuelle. Les modifications s'appliquent à la requête suivante. | | **Maintenance** | Resynchronisez le catalogue de modèles Codex, examinez les avertissements de contournement par une configuration locale au projet, recherchez la dernière version stable ou préliminaire et lancez une mise à jour avec redémarrage facultatif du proxy. | | **Sécurité au démarrage** | Vérifiez si le routage Codex injecté résiste à un redémarrage, avec des états distincts pour le service et le lanceur intermédiaire, ainsi que les commandes de réparation exactes. | | **Zone de notification Windows** | Installez au niveau de l'utilisateur un contrôleur lancé à la connexion pour démarrer, arrêter ou redémarrer le proxy en un clic, ouvrir le tableau de bord et consulter l'état. Ce contrôleur n'est pas un service de redémarrage du proxy. | -| **Démarrage automatique de Codex** | Sur **Démarrage**, dans les détails de protection : autorisez un lanceur intermédiaire Codex déjà installé à exécuter `ocx ensure`. Ce commutateur n'installe ni lanceur ni service d'arrière-plan. | +| **Démarrage automatique de Codex** | Autorisez un lanceur intermédiaire Codex déjà installé à exécuter `ocx ensure`. Ce commutateur n'installe ni lanceur ni service d'arrière-plan. | | **Fournisseurs** | Ajoutez, modifiez, activez, désactivez ou supprimez des fournisseurs, définissez le fournisseur par défaut parmi ceux activés et gérez, lorsqu'ils sont pris en charge, les groupes de comptes OAuth et de clés API. Si le fournisseur par défaut actuel est supprimé, le premier fournisseur activé restant prend sa place ; s'il n'en reste aucun, la suppression est refusée et le fournisseur par défaut actuel est conservé. Les réglages d'un fournisseur peuvent désactiver la découverte dynamique pour les points de terminaison dont le catalogue `/models` est absent, lent ou trop volumineux. Pour les groupes OAuth Claude (Anthropic), chaque compte connecté affiche ses propres barres de limites sur 5 heures et une semaine — l'utilisation est propre à chaque identifiant. En cas d'échec d'une sonde, les dernières barres connues sont conservées et marquées indisponibles jusqu'à la prochaine actualisation réussie. | | **Ajouter un fournisseur** | Recherchez dans les préréglages du registre une connexion par compte, un service à clé API, un serveur local ou un point de terminaison personnalisé. | | **Authentification Codex** | Ajoutez des comptes ChatGPT/Codex au groupe, sélectionnez le compte de la prochaine session, actualisez les quotas sur 5 h, une semaine et 30 jours, activez ou désactivez le changement automatique selon les quotas, réglez son seuil de 1 à 100 % et configurez le basculement en cas de défaillance transitoire. | -| **Sous-agents** | Mettez en avant jusqu'à cinq modèles natifs non qualifiés ou modèles routés avec espace de noms dans la liste des remplacements de `spawn_agent`, et choisissez v1/base/v2 ainsi que la limite de fils v2 dans la section **Avancé** repliée de la délégation. | -| **Modèles** | Activez ou désactivez les modèles GPT natifs et routés, définissez les listes d'autorisation et les plafonds de contexte des fournisseurs. La politique des nouveaux modèles, les alias, l'interception des appels fantômes et les plafonds de fenêtre par défaut se trouvent dans une section **Avancé** repliée ; les réglages supplémentaires par fournisseur (alias par défaut, modèles personnalisés, plafonds de fenêtre) s'ouvrent depuis le bouton ⋯ de l'en-tête du fournisseur. Les fournisseurs configurés restent visibles sous forme de groupes sans modèle lorsque la découverte est désactivée ou ne renvoie aucune ligne. | +| **Sous-agents** | Mettez en avant jusqu'à cinq modèles natifs non qualifiés ou modèles routés avec espace de noms dans la liste des remplacements de `spawn_agent`. | +| **Modèles** | Activez ou désactivez les modèles GPT natifs et routés, définissez les listes d'autorisation et les plafonds de contexte des fournisseurs, choisissez v1/base/v2 et configurez la limite de fils v2. Les fournisseurs configurés restent visibles sous forme de groupes sans modèle lorsque la découverte est désactivée ou ne renvoie aucune ligne. | | **Journaux** | Actualisez automatiquement les requêtes récentes et consultez les jetons, l'effort demandé et, lorsqu'il est disponible, l'effort sortant effectif, le modèle résolu, le fournisseur, l'état, l'identifiant de requête, la durée et les détails de l'erreur. La vue détaillée inclut le champ exact de raisonnement transmis lorsque l'adaptateur en émet un. Filtrez par identifiant opaque de conversation ou de session — si le client en fournit un — afin d'obtenir le total des jetons et le coût estimé au tarif catalogue pour l'anneau de journaux actuellement chargé. | | **Utilisation / Débogage** | Examinez la couverture et les tendances d'utilisation des jetons, ou activez à la demande les diagnostics de transport et d'extraction de l'utilisation propres aux fournisseurs. | | **Stockage** | Consultez en lecture seule la répartition du disque de CODEX_HOME — sessions, archives, bases de données et pièces jointes. Pour le nettoyage facultatif des archives, prévisualisez les N % les plus anciennes, puis placez-les en quarantaine dans `CODEX_HOME/.trash` (par défaut) ou supprimez-les définitivement après avoir coché une case explicite. **La stratégie de nettoyage automatique** est facultative et **désactivée par défaut** (`storageCleanupPolicy.enabled`) ; configurez son seuil, sa cible, sa planification et son mode sur la page **Stockage**, ou lancez **Exécuter maintenant**. Les entrées mises en quarantaine peuvent être restaurées depuis cette page (JSONL et fils). Les sessions actives restent en lecture seule. Le nettoyage et la restauration sont refusés tant que Codex verrouille le fichier `state_*.sqlite` le plus récent ou actif. | @@ -59,10 +59,10 @@ gestionnaire de mots de passe. ### Liens directs vers une section -Il n'existe qu'une seule mise en page, donc aucun commutateur de disposition n'est à configurer. -`#dashboard` ouvre **Vue d'ensemble** ; les anciens favoris `#dashboard/providers` et `#dashboard/models` -redirigent vers `#providers` et `#models`. Le rechargement, les favoris -et le bouton **Précédent** conservent la page affichée. **Journaux** fonctionne de la même manière avec +Il n'existe qu'une seule mise en page, donc aucun commutateur de disposition n'est à configurer. Les sections +du tableau de bord possèdent plutôt leur propre adresse : `#dashboard` ouvre **Vue d'ensemble**, tandis que +`#dashboard/providers` et `#dashboard/models` ouvrent les deux autres sections. Le rechargement, les favoris +et le bouton **Précédent** conservent la section affichée. **Journaux** fonctionne de la même manière avec `#logs` et `#logs/debug`. Un ancien favori `#providers/workspace` ouvre désormais `#providers`. Les coûts affichés dans **Journaux** et **Utilisation** sont des équivalents au tarif catalogue de l'API, @@ -78,7 +78,7 @@ la liste d'autorisation du fournisseur afin que les modèles découverts ultéri ## Sélecteur de délégation et routage des créations de sous-agents -Le sélecteur **Délégation de sous-agent** de la page **Sous-agents** enregistre `injectionModel` et, facultativement, +Le sélecteur **Délégation de sous-agent** du tableau de bord enregistre `injectionModel` et, facultativement, `injectionEffort`. L'option **Consignes multi-agents OpenCodex** contrôle indépendamment les instructions de délégation qui emploient ces valeurs. Pendant les tours v2 admissibles, ces consignes indiquent à l'agent parent le modèle exact et l'effort de raisonnement à transmettre à `spawn_agent` ; effacer le modèle efface @@ -139,7 +139,7 @@ pour les champs affichés, la signification d'une couverture incomplète et la l ## Mettre le dépôt en vedette relève de votre choix, pas de celui d'un agent -Le bouton étoile de la boîte de dialogue de mise à jour (ouverte depuis le bouton de mise à jour de la barre latérale) — et la question unique posée par `ocx start` dans un terminal +Le bouton étoile de la barre latérale — et la question unique posée par `ocx start` dans un terminal interactif — utilise **votre propre connexion `gh`**. opencodex ne détient aucun jeton GitHub et apprend uniquement votre réponse affirmative ou négative. diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index 48e8ca614f..a8cc50225e 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -38,18 +38,18 @@ the browser or password manager's decision. | Area | What it does | | --- | --- | -| **Dashboard summary** | Online state, version with uptime, provider count, and 30-day token total, followed by the reboot-protection bar, model sync, a collapsed **Sidecars** disclosure, and memory pressure. The old provider and model tabs are gone; use **Providers** and **Models**. | -| **Sub-agent delegation** | On **Subagents**: choose a native or routed model and optional reasoning effort shared by OpenCodex delegation guidance and the separate native-default opt-in. This is not a proxy-side per-spawn router; see below. | -| **Sidecars** | A collapsed disclosure on the dashboard: choose the web-search model and effort plus the vision-description model. Changes apply on the next request. | +| **Dashboard summary** | Multi-agent mode, online state, version, uptime, provider count, 30-day token total, active providers, and available native/routed models. | +| **Sub-agent delegation** | Choose a native or routed model and optional reasoning effort shared by OpenCodex delegation guidance and the separate native-default opt-in. This is not a proxy-side per-spawn router; see below. | +| **Sidecars** | Choose the web-search model and effort plus the vision-description model. Changes apply on the next request. | | **Maintenance** | Resync the Codex model catalog, inspect project-local config bypass warnings, check the latest or preview release, and run an update with optional proxy restart. | | **Startup safety** | Show whether injected Codex routing survives a restart, with separate service and launcher-shim health plus exact repair commands. | | **Windows tray** | Install a per-user login tray for one-click proxy start, stop, restart, dashboard access, and status. The tray is a controller, not a proxy restart service. | -| **Codex autostart** | On **Startup**, inside the protection details: allow an already-installed Codex launcher shim to run `ocx ensure`. This toggle does not install a shim or background service. | +| **Codex autostart** | Allow an already-installed Codex launcher shim to run `ocx ensure`. This toggle does not install a shim or background service. | | **Providers** | Add, edit, set the default (enabled providers only), enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Removing the current default switches to the first remaining enabled provider when one exists; otherwise deletion is refused and the current default is kept. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. For Claude (Anthropic) OAuth pools, each logged-in account shows its own 5-hour and weekly rate-limit bars (usage is per credential); a failed probe keeps the last-known bars and marks them unavailable until the next successful refresh. | | **Add provider** | Search registry-backed presets for account login, API-key services, local servers, or a custom endpoint. | | **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, enable or disable quota auto-switch, set its 1–100% threshold, and configure transient-failure failover. | -| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list, and choose v1/base/v2 plus the v2 thread limit from the delegation section's **Advanced** disclosure. | -| **Models** | Toggle native GPT and routed models, set provider allowlists and context caps. New-model policy, aliases, shadow-call intercept, and default window caps sit in a collapsed **Advanced** disclosure; per-provider extras (alias defaults, custom models, window caps) open from a ⋯ button on the provider header. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. | +| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. | +| **Models** | Toggle native GPT and routed models, set provider allowlists and context caps, choose v1/base/v2, and configure the v2 thread limit. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. | | **Logs** | Auto-refresh recent requests with tokens, requested effort and (when available) effective outbound effort, resolved model, provider, status, request id, duration, and error details. The detail view includes the exact reasoning wire field when the adapter emits one. Filter by opaque conversation/session id (when the client sends one) to total tokens and estimated list-price cost for the currently loaded Logs ring. | | **Usage / Debug** | Inspect token-usage coverage and trends, or enable opt-in provider transport and usage-extraction diagnostics. | | **Storage** | Read-only CODEX_HOME disk breakdown (sessions, archives, DBs, attachments). Optional archived cleanup: preview the oldest N%, then quarantine to `CODEX_HOME/.trash` (default) or permanently delete behind an explicit checkbox. **Auto-cleanup policy** is opt-in and **default OFF** (`storageCleanupPolicy.enabled`); configure threshold/target/schedule/mode on the Storage page, or trigger **Run now**. Quarantined entries can be restored from the Storage page (JSONL + threads). Active sessions stay read-only. Cleanup and restore are refused while Codex holds the newest/active `state_*.sqlite` locked. | @@ -57,10 +57,11 @@ the browser or password manager's decision. ### Linking to a section -There is a single layout, so there is no layout switch to configure. `#dashboard` is the overview; -older `#dashboard/providers` and `#dashboard/models` bookmarks redirect to `#providers` and -`#models`. Reload, bookmark, and Back all keep the page you were on. **Logs** has `#logs` and -`#logs/debug`. An older `#providers/workspace` bookmark now lands on `#providers`. +There is a single layout, so there is no layout switch to configure. Dashboard sections are +addressable instead: `#dashboard` opens Overview, and `#dashboard/providers` and +`#dashboard/models` open the other two. Reload, bookmark, and Back all keep the section you were +on. **Logs** works the same way with `#logs` and `#logs/debug`. An older `#providers/workspace` +bookmark now lands on `#providers`. Cost values in **Logs** and **Usage** are API list-price equivalents calculated from reported tokens. They are not billing receipts or evidence of an actual charge; subscription usage or provider credits @@ -72,7 +73,7 @@ The **Models** switches show final Codex visibility: a routed model is on only w ## Delegation picker vs spawn routing -The **Subagents** page's **Sub-agent delegation** picker stores `injectionModel` and, optionally, +The Dashboard's **Sub-agent delegation** picker stores `injectionModel` and, optionally, `injectionEffort`. **OpenCodex multi-agent guidance** independently controls the delegation instructions that use those values. On eligible v2 turns, that guidance tells the parent agent which exact model and reasoning effort to pass to `spawn_agent`; clearing the model also clears @@ -172,7 +173,7 @@ visible fields, incomplete-coverage meaning, and routing boundary. ## Starring is yours to decide, not an agent's -The star button in the update dialog (opened from the sidebar's update orb) — and the one-time question `ocx start` asks in an interactive +The sidebar's star button — and the one-time question `ocx start` asks in an interactive terminal — goes through **your own `gh` login**. opencodex holds no GitHub token, and the only thing it learns is your yes or no. 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 49537dff4f..4b7cbd70ad 100644 --- a/docs-site/src/content/docs/ja/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ja/guides/web-dashboard.md @@ -31,18 +31,18 @@ bun run dev:gui | 領域 | 機能 | --- | --- | -| **ダッシュボード要約** | オンライン状態、バージョンと稼働時間、プロバイダー数、直近30日トークン合計を表示し、その下に再起動保護バー、モデル同期、折りたたまれた **サイドカー** セクション、メモリ圧力が続きます。以前のプロバイダー/モデルタブはなくなったので **プロバイダー** と **モデル** を使ってください。 | -| **サブエージェント委任** | **サブエージェント** ページで、OpenCodex の委任ガイダンスとオプションの Codex ネイティブサブエージェント既定値で共有するネイティブ/ルーティングモデルと任意の推論強度を選びます。スポーンごとのルーターではありません。下記を参照してください。 | -| **サイドカー** | ダッシュボードの折りたたみセクションで、ウェブ検索モデルと強度、画像説明モデルを選択します。次回リクエストから適用されます。 | +| **ダッシュボード要約** | マルチエージェントモード、オンライン状態、バージョン、稼働時間、プロバイダー数、直近30日トークン合計、アクティブプロバイダーと利用可能なネイティブ/ルーティングモデルを表示します。 | +| **サブエージェント委任** | OpenCodex の委任ガイダンスとオプションの Codex ネイティブサブエージェント既定値で共有するネイティブ/ルーティングモデルと任意の推論強度を選びます。スポーンごとのルーターではありません。下記を参照してください。 | +| **サイドカー** | ウェブ検索モデルと強度、画像説明モデルを選択します。次回リクエストから適用されます。 | | **メンテナンス** | Codex モデルカタログを再同期し、プロジェクトローカル設定のバイパス警告を確認し、latest/preview 更新を照会またはオプションのプロキシ再起動と共にインストールします。 | | **起動安全性** | 注入された Codex ルーティングが再起動後も機能するか、サービスと launcher shim の状態、正確な修復コマンドと共に表示します。 | | **Windows トレイ** | ユーザーのログイントレイを導入し、プロキシ開始・停止・再起動・ダッシュボード・状態をクリックで操作します。トレイは再起動サービスではありません。 | -| **Codex 自動起動** | **起動** ページの保護状態の詳細で、インストール済み Codex launcher shim に `ocx ensure` の実行を許可します。このトグルは shim やバックグラウンドサービスをインストールしません。 | +| **Codex 自動起動** | インストール済み Codex launcher shim に `ocx ensure` の実行を許可します。このトグルは shim やバックグラウンドサービスをインストールしません。 | | **プロバイダー** | プロバイダーを追加、編集、既定に設定(有効なプロバイダーのみ)、有効化/無効化、削除し、対応する OAuth アカウントプールと API キープールを管理します。現在の既定を削除すると、残っている最初の有効なプロバイダーに切り替わります(存在する場合)。なければ削除は拒否され、現在の既定は保持されます。Claude(Anthropic)OAuth プールでは、ログイン済みの各アカウントに独自の 5 時間・週間レート制限バーが表示され(利用量は資格情報単位)、取得失敗時は直近の値を保持して一時利用不可と表示します。 | | **プロバイダー追加** | レジストリベースのプリセットからアカウントログイン、API キーサービス、ローカルサーバー、custom エンドポイントを検索します。 | | **Codex 認証** | ChatGPT/Codex プールアカウントを追加し、次回セッションアカウントを選び、5 時間 / 週間 / 30 日クォータを更新し、クォータ自動切り替えのオン/オフと 1~100% のしきい値、一時的失敗フェイルオーバーを設定します。 | -| **サブエージェント** | `spawn_agent` オーバーライド一覧にネイティブまたはルーティングモデルを最大 5 つまで優先公開し、委任セクションの **詳細設定** 折りたたみで v1/base/v2 と v2 スレッド数を設定します。 | -| **モデル** | ネイティブ GPT とルーティングモデルをオン/オフし、プロバイダー許可リストとコンテキスト上限を設定します。新規モデルポリシー、エイリアス、シャドウ呼び出しの横取り、既定ウィンドウ上限は折りたたまれた **詳細設定** に、プロバイダーごとの追加設定(既定エイリアス、カスタムモデル、ウィンドウ上限)はプロバイダーヘッダーの ⋯ ボタンにあります。 | +| **サブエージェント** | `spawn_agent` オーバーライド一覧にネイティブまたはルーティングモデルを最大 5 つまで優先公開します。 | +| **モデル** | ネイティブ GPT とルーティングモデルをオン/オフし、プロバイダー許可リストとコンテキスト上限、v1/base/v2、v2 スレッド数を設定します。 | | **ログ** | トークン、要求された強度と(利用可能な場合は)実際に送信された強度、実際のモデル、プロバイダー、状態、リクエスト ID、所要時間、エラー詳細を含む最近のリクエストを自動更新します。アダプターが reasoning パラメーターを送信した場合、詳細表示に正確な wire field も表示されます。 | | **使用量 / デバッグ** | トークン使用量の測定範囲と推移を見るか、オプションのプロバイダートランスポート/使用量抽出診断をオンにします。 | | **ストレージ** | CODEX_HOME のディスク内訳(セッション、アーカイブ、DB、添付)を読み取り専用で表示。任意のアーカイブクリーンアップ: 最古 N% をプレビューし、既定では `CODEX_HOME/.trash` へ隔離、または明示チェックで完全削除。**自動クリーンアップ方針**はオプトインで**既定 OFF**(`storageCleanupPolicy.enabled`)。Storage ページでしきい値/目標/スケジュール/モードを設定するか **今すぐ実行**。隔離エントリは Storage ページから復元可能(JSONL + スレッド)。アクティブセッションは読み取り専用。最新/アクティブな `state_*.sqlite` がロック中はクリーンアップと復元を拒否。 | @@ -50,7 +50,7 @@ bun run dev:gui ### セクションへのリンク -レイアウトは 1 つだけなので、切り替える設定はありません。`#dashboard` は Overview で、以前の `#dashboard/providers` と `#dashboard/models` のブックマークは `#providers` と `#models` に移動します。再読み込み・ブックマーク・戻る操作のいずれでも、表示していたページが保たれます。**Logs** も `#logs` と `#logs/debug` で同じように動作します。以前の `#providers/workspace` のブックマークは `#providers` に移動します。 +レイアウトは 1 つだけなので、切り替える設定はありません。代わりに Dashboard の各セクションに URL があります。`#dashboard` は Overview、`#dashboard/providers` と `#dashboard/models` は残りの 2 つです。再読み込み・ブックマーク・戻る操作のいずれでも、表示していたセクションが保たれます。**Logs** も `#logs` と `#logs/debug` で同じように動作します。以前の `#providers/workspace` のブックマークは `#providers` に移動します。 **ログ**と**使用量**のコスト値は報告されたトークンで計算した API 定価換算値です。請求明細や 実際の請求証拠ではなく、サブスクリプション使用量またはプロバイダークレジットが代わりに適用される場合があります。 @@ -61,7 +61,7 @@ bun run dev:gui ## 委任セレクターとスポーンルーティングの違い -**サブエージェント** ページの **サブエージェント委任** セレクターは `injectionModel` とオプションの `injectionEffort` を +ダッシュボードの **サブエージェント委任** セレクターは `injectionModel` とオプションの `injectionEffort` を 保存します。選択値は OpenCodex が作成する委任ガイダンスで使われ、そのガイダンスは `multiAgentGuidanceEnabled` で別に制御されます。モデルを消去すると保存済み effort も消去され、 ネイティブ既定値の同期も無効になります。 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 124e720e63..62fb808853 100644 --- a/docs-site/src/content/docs/ko/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ko/guides/web-dashboard.md @@ -31,18 +31,18 @@ bun run dev:gui | 영역 | 기능 | | --- | --- | -| **Dashboard 요약** | 온라인 상태, 버전과 가동 시간, 프로바이더 수, 최근 30일 토큰 합계를 보여주고, 그 아래에 재부팅 보호 바, 모델 sync, 접힌 **사이드카** 섹션, 메모리 압력이 이어집니다. 예전의 프로바이더/모델 탭은 없어졌으니 **Providers**와 **Models**를 쓰세요. | -| **Sub-agent delegation** | **Subagents** 페이지에서 OpenCodex 위임 가이드와 선택적인 Codex 네이티브 서브에이전트 기본값이 함께 사용할 네이티브/라우팅 모델과 선택적 reasoning 강도를 고릅니다. 스폰별 라우터는 아닙니다. 아래 설명을 확인하세요. | -| **사이드카** | Dashboard의 접힌 섹션에서 웹 검색 모델과 강도, 이미지 설명 모델을 선택합니다. 다음 요청부터 적용됩니다. | +| **Dashboard 요약** | Multi-agent 모드, 온라인 상태, 버전, 가동 시간, 프로바이더 수, 최근 30일 토큰 합계, 활성 프로바이더와 사용 가능한 네이티브/라우팅 모델을 보여줍니다. | +| **Sub-agent delegation** | OpenCodex 위임 가이드와 선택적인 Codex 네이티브 서브에이전트 기본값이 함께 사용할 네이티브/라우팅 모델과 선택적 reasoning 강도를 고릅니다. 스폰별 라우터는 아닙니다. 아래 설명을 확인하세요. | +| **사이드카** | 웹 검색 모델과 강도, 이미지 설명 모델을 선택합니다. 다음 요청부터 적용됩니다. | | **Maintenance** | Codex 모델 카탈로그를 다시 동기화하고, 프로젝트 로컬 설정의 우회 경고를 확인하고, latest/preview 업데이트를 조회하거나 선택적 프록시 재시작과 함께 설치합니다. | | **시작 안전성** | 주입된 Codex 라우팅이 재부팅 후에도 유지되는지 서비스와 launcher shim 상태, 정확한 복구 명령과 함께 표시합니다. | | **Windows 트레이** | 로그인할 때 사용자 전용 트레이를 시작하고 프록시 시작·중지·재시작·대시보드·상태를 클릭으로 제어합니다. 트레이는 재시작 서비스가 아닙니다. | -| **Codex 자동 시작** | **Startup** 페이지의 보호 상태 상세에서, 이미 설치된 Codex launcher shim이 `ocx ensure`를 실행하도록 허용합니다. 이 토글은 shim이나 백그라운드 서비스를 설치하지 않습니다. | +| **Codex 자동 시작** | 이미 설치된 Codex launcher shim이 `ocx ensure`를 실행하도록 허용합니다. 이 토글은 shim이나 백그라운드 서비스를 설치하지 않습니다. | | **Providers** | 프로바이더를 추가, 편집, 기본으로 설정(활성만), 활성화/비활성화, 제거하고, 지원되는 OAuth 계정 풀과 API key 풀을 관리합니다. 현재 기본 프로바이더를 제거하면 남아 있는 첫 번째 활성 프로바이더로 전환됩니다(있는 경우); 없으면 삭제가 거부되고 현재 기본이 유지됩니다. Claude(Anthropic) OAuth 풀에서는 로그인한 계정마다 자체 5시간·주간 한도 막대가 표시되며(사용량은 자격 증명 단위), 조회 실패 시 마지막 값을 유지하고 일시 불가 상태로 표시합니다. | | **Add provider** | 레지스트리 기반 프리셋에서 계정 로그인, API key 서비스, 로컬 서버, custom endpoint를 검색합니다. | | **Codex Auth** | ChatGPT/Codex 풀 계정을 추가하고, 다음 세션 계정을 선택하고, 5시간 / 주간 / 30일 할당량을 갱신하며, 할당량 자동 전환을 켜거나 끄고 1~100% 임계값과 일시적 실패 failover를 설정합니다. | -| **Subagents** | `spawn_agent` override 목록에 네이티브 또는 라우팅 모델을 최대 5개까지 우선 노출하고, 위임 섹션의 **고급** 접기에서 v1/base/v2와 v2 thread 수를 설정합니다. | -| **Models** | 네이티브 GPT와 라우팅 모델을 켜고 끄고, 프로바이더 allowlist와 컨텍스트 상한을 설정합니다. 새 모델 정책, 별칭, 쉐도우 호출 가로채기, 기본 창 상한은 접힌 **고급** 섹션에, 프로바이더별 추가 설정(기본 별칭, 커스텀 모델, 창 상한)은 프로바이더 헤더의 ⋯ 버튼에 있습니다. | +| **Subagents** | `spawn_agent` override 목록에 네이티브 또는 라우팅 모델을 최대 5개까지 우선 노출합니다. | +| **Models** | 네이티브 GPT와 라우팅 모델을 켜고 끄고, 프로바이더 allowlist와 컨텍스트 상한, v1/base/v2, v2 thread 수를 설정합니다. | | **Logs** | 토큰, 요청한 강도와 (사용 가능한 경우) 실제 전송 강도, 실제 모델, 프로바이더, 상태, 요청 id, 소요 시간, 오류 상세가 포함된 최근 요청을 자동 갱신합니다. 어댑터가 reasoning 매개변수를 전송한 경우 상세 보기에 정확한 wire field도 표시됩니다. 클라이언트가 보낸 불투명 대화/세션 id로 필터하면 현재 로드된 Logs 링의 토큰·추정 정가 합계를 볼 수 있습니다. | | **Usage / Debug** | 토큰 사용량의 측정 범위와 추이를 보거나, 선택적 프로바이더 전송/사용량 추출 진단을 켭니다. | | **Storage** | CODEX_HOME 디스크 사용량(세션, 보관, DB, 첨부)을 읽기 전용으로 표시합니다. 선택적 보관 정리: 가장 오래된 N%를 미리본 뒤 기본으로 `CODEX_HOME/.trash`에 격리하거나, 명시 체크 후 영구 삭제합니다. **자동 정리 정책**은 opt-in이며 **기본 OFF**(`storageCleanupPolicy.enabled`)입니다. Storage 페이지에서 임계값/목표/일정/모드를 설정하거나 **지금 실행**하세요. Storage 페이지에서 격리 항목을 복원할 수 있습니다(JSONL + 스레드). 활성 세션은 읽기 전용입니다. Codex가 최신/활성 `state_*.sqlite`를 잠그면 정리와 복원을 거절합니다. | @@ -50,7 +50,7 @@ bun run dev:gui ### 섹션으로 바로 가기 -레이아웃은 하나뿐이라 전환할 설정이 없습니다. `#dashboard`는 Overview이고, 예전 `#dashboard/providers`와 `#dashboard/models` 북마크는 `#providers`와 `#models`로 넘어갑니다. 새로고침하거나 북마크해도, 뒤로 가도 보던 페이지가 그대로 유지됩니다. **Logs**도 `#logs`와 `#logs/debug`로 똑같이 동작합니다. 예전 `#providers/workspace` 북마크는 `#providers`로 넘어갑니다. +레이아웃은 하나뿐이라 전환할 설정이 없습니다. 대신 Dashboard의 섹션마다 주소가 있습니다. `#dashboard`는 Overview, `#dashboard/providers`와 `#dashboard/models`는 나머지 두 섹션입니다. 새로고침하거나 북마크해도, 뒤로 가도 보던 섹션이 그대로 유지됩니다. **Logs**도 `#logs`와 `#logs/debug`로 똑같이 동작합니다. 예전 `#providers/workspace` 북마크는 `#providers`로 넘어갑니다. **Logs**와 **Usage**의 비용 값은 보고된 토큰으로 계산한 API 정가 환산치입니다. 결제 영수증이나 실제 청구 증거가 아니며, 구독 사용량 또는 프로바이더 크레딧이 대신 적용될 수 있습니다. @@ -61,7 +61,7 @@ bun run dev:gui ## 위임 선택기와 스폰 라우팅의 차이 -**Subagents** 페이지의 **Sub-agent delegation** 선택기는 `injectionModel`과 선택적인 `injectionEffort`를 +Dashboard의 **Sub-agent delegation** 선택기는 `injectionModel`과 선택적인 `injectionEffort`를 저장합니다. 선택한 값은 OpenCodex가 작성하는 위임 가이드에 사용되고, 이 가이드는 `multiAgentGuidanceEnabled`가 별도로 제어합니다. 모델을 지우면 저장된 강도도 지워지고 네이티브 기본값 동기화도 꺼집니다. @@ -128,7 +128,7 @@ Providers 개요는 Pool 모드 사용량을 표시 전용 가중 용량 추정 ## 스타는 에이전트가 아니라 사용자가 결정합니다 -업데이트 대화상자(사이드바의 업데이트 버튼으로 엽니다)의 스타 버튼, 그리고 `ocx start`가 대화형 터미널에서 한 번 묻는 질문은 모두 +사이드바의 스타 버튼, 그리고 `ocx start`가 대화형 터미널에서 한 번 묻는 질문은 모두 **사용자 본인의 `gh` 로그인**을 씁니다. opencodex는 GitHub 토큰을 따로 갖고 있지 않고, 예/아니오 답만 알게 됩니다. 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 a4227988ba..b92ce3d450 100644 --- a/docs-site/src/content/docs/ru/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ru/guides/web-dashboard.md @@ -31,18 +31,18 @@ bun run dev:gui | Раздел | Что делает | | --- | --- | -| **Сводка Dashboard** | Состояние онлайн, версия с временем работы, число провайдеров и сумма токенов за 30 дней; ниже — полоса защиты от перезагрузки, синхронизация моделей, свёрнутый блок **Сайдкары** и давление памяти. Старые вкладки провайдеров и моделей убраны — используйте **Providers** и **Models**. | -| **Sub-agent delegation** | На странице **Subagents**: выбор нативной/маршрутизируемой модели и необязательного уровня рассуждений, общих для руководства OpenCodex по делегированию и опциональных нативных значений подагентов Codex по умолчанию. Это не маршрутизатор отдельных порождений; см. ниже. | -| **Сайдкары** | Свёрнутый блок на дашборде: выбор модели и уровня рассуждений для веб-поиска, а также модели описания изображений. Изменения применяются со следующего запроса. | +| **Сводка Dashboard** | Мультиагентный режим, состояние онлайн, версия, время работы, число провайдеров, сумма токенов за 30 дней, активные провайдеры и доступные нативные/маршрутизируемые модели. | +| **Sub-agent delegation** | Выбор нативной/маршрутизируемой модели и необязательного уровня рассуждений, общих для руководства OpenCodex по делегированию и опциональных нативных значений подагентов Codex по умолчанию. Это не маршрутизатор отдельных порождений; см. ниже. | +| **Сайдкары** | Выбор модели и уровня рассуждений для веб-поиска, а также модели описания изображений. Изменения применяются со следующего запроса. | | **Maintenance** | Пересинхронизация каталога моделей Codex, просмотр предупреждений об обходе через проектную локальную конфигурацию, проверка последнего или предварительного выпуска и запуск обновления с необязательным перезапуском прокси. | | **Безопасность запуска** | Показывает, сохранит ли внедрённая маршрутизация Codex работоспособность после перезагрузки, отдельно отображая службу, launcher shim и точные команды исправления. | | **Трей Windows** | Устанавливает пользовательский значок входа для запуска, остановки, перезапуска, панели и состояния прокси одним щелчком. Трей не является службой перезапуска. | -| **Автозапуск Codex** | На странице **Startup**, в деталях защиты: разрешает уже установленному launcher shim Codex выполнять `ocx ensure`. Переключатель не устанавливает shim или фоновую службу. | +| **Автозапуск Codex** | Разрешает уже установленному launcher shim Codex выполнять `ocx ensure`. Переключатель не устанавливает shim или фоновую службу. | | **Providers** | Добавление, редактирование, назначение провайдера по умолчанию (только включённые), включение/отключение и удаление провайдеров; управление пулами OAuth-аккаунтов и пулами API-ключей там, где они поддерживаются. При удалении текущего провайдера по умолчанию выбирается первый оставшийся включённый провайдер, если он есть; иначе удаление отклоняется и текущий default сохраняется. Для пулов Claude (Anthropic) OAuth у каждого вошедшего аккаунта свои полосы 5-часового и недельного лимита (использование по учётным данным); при сбое опроса сохраняются последние известные значения с пометкой недоступности. | | **Add provider** | Поиск по пресетам из реестра: вход по аккаунту, сервисы с API-ключом, локальные серверы или пользовательская конечная точка. | | **Codex Auth** | Добавление аккаунтов пула ChatGPT/Codex, выбор аккаунта для следующей сессии, обновление квот 5 ч / недельных / 30-дневных, включение или отключение автопереключения, настройка его порога 1–100% и failover при временных сбоях. | -| **Subagents** | Выделение до пяти «голых» нативных или маршрутизируемых моделей с пространством имён в списке переопределений `spawn_agent`, а также выбор v1/base/v2 и лимита потоков v2 в свёрнутом блоке **Дополнительно** секции делегирования. | -| **Models** | Включение и отключение нативных GPT и маршрутизируемых моделей, настройка списков разрешённых провайдеров и лимитов контекста. Политика новых моделей, псевдонимы, перехват shadow-вызовов и лимиты окна по умолчанию находятся в свёрнутом блоке **Дополнительно**; дополнительные настройки провайдера (псевдонимы по умолчанию, пользовательские модели, лимиты окна) открываются кнопкой ⋯ в заголовке провайдера. | +| **Subagents** | Выделение до пяти «голых» нативных или маршрутизируемых моделей с пространством имён в списке переопределений `spawn_agent`. | +| **Models** | Включение и отключение нативных GPT и маршрутизируемых моделей, настройка списков разрешённых провайдеров и лимитов контекста, выбор v1/base/v2 и настройка лимита потоков v2. | | **Logs** | Автообновляемый список недавних запросов: токены, запрошенный и, когда доступен, фактически отправленный уровень рассуждений, фактическая модель, провайдер, статус, id запроса, длительность и подробности ошибок. Если адаптер отправляет параметр рассуждений, в подробностях также отображается точное wire-поле. Можно фильтровать по непрозрачному id диалога/сессии (если клиент его передаёт) и суммировать токены и оценочную стоимость по прайс-листу в пределах загруженного кольца Logs. | | **Usage / Debug** | Просмотр покрытия и трендов расхода токенов либо включение опциональной диагностики транспорта провайдеров и извлечения данных об использовании. | | **Storage** | Только чтение разбивки диска CODEX_HOME (сессии, архивы, БД, вложения). Опциональная очистка архива: предпросмотр самых старых N%, затем карантин в `CODEX_HOME/.trash` (по умолчанию) или безвозвратное удаление по явному флажку. **Политика автоочистки** — opt-in и **по умолчанию ВЫКЛ** (`storageCleanupPolicy.enabled`); порог/цель/расписание/режим на странице Storage или **Запустить сейчас**. Записи карантина можно восстановить со страницы Storage (JSONL + threads). Активные сессии только для чтения. Очистка и восстановление отклоняются, пока Codex держит блокировку новейшего/активного `state_*.sqlite`. | @@ -50,7 +50,7 @@ bun run dev:gui ### Ссылки на разделы -Макет теперь один, поэтому переключать нечего. `#dashboard` — это Overview; старые закладки `#dashboard/providers` и `#dashboard/models` ведут на `#providers` и `#models`. Перезагрузка, закладка и кнопка «Назад» сохраняют выбранную страницу. **Logs** работает так же — `#logs` и `#logs/debug`. Старая закладка `#providers/workspace` теперь ведёт на `#providers`. +Макет теперь один, поэтому переключать нечего. Зато у разделов Dashboard есть собственные адреса: `#dashboard` открывает Overview, а `#dashboard/providers` и `#dashboard/models` — два других раздела. Перезагрузка, закладка и кнопка «Назад» сохраняют выбранный раздел. **Logs** работает так же — `#logs` и `#logs/debug`. Старая закладка `#providers/workspace` теперь ведёт на `#providers`. Значения стоимости в **Logs** и **Usage** — это эквиваленты стоимости по прайс-листу API, рассчитанные на основе сообщённых токенов. Они не являются счётом и не подтверждают фактическое @@ -62,7 +62,7 @@ bun run dev:gui ## Селектор делегирования и маршрутизация порождений -Селектор **Sub-agent delegation** на странице **Subagents** сохраняет `injectionModel` и, при желании, +Селектор **Sub-agent delegation** в дашборде сохраняет `injectionModel` и, при желании, `injectionEffort`. Выбранные значения используются в добавляемом OpenCodex руководстве по делегированию, которое отдельно управляется полем `multiAgentGuidanceEnabled`. Очистка модели также очищает сохранённый уровень и отключает синхронизацию нативных значений по умолчанию. 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 a17b26bfc0..282c1e4106 100644 --- a/docs-site/src/content/docs/tr/guides/web-dashboard.md +++ b/docs-site/src/content/docs/tr/guides/web-dashboard.md @@ -43,18 +43,18 @@ kararıdır. | Alan | Ne yapar | | --- | --- | -| **Kontrol paneli özeti** | Çevrimiçi durum, çalışma süresiyle birlikte sürüm, sağlayıcı sayısı ve 30 günlük token toplamı; ardından yeniden başlatma koruma çubuğu, model eşitleme, daraltılmış bir **Sidecar'lar** bölümü ve bellek baskısı gelir. Eski sağlayıcı ve model sekmeleri kaldırıldı; **Sağlayıcılar** ve **Modeller** sayfalarını kullanın. | -| **Alt ajan yetkilendirmesi** | **Alt Ajanlar** sayfasında: OpenCodex yetkilendirme rehberliği ve ayrı yerel varsayılan katılımı tarafından paylaşılan yerel veya yönlendirilen bir model ve isteğe bağlı akıl yürütme çabası seçin. Bu, proxy tarafında spawn başına bir yönlendirici değildir; aşağıya bakın. | -| **Sidecar'lar** | Kontrol panelinde daraltılmış bir bölüm: web arama modelini ve çabasını artı vizyon açıklama modelini seçin. Değişiklikler bir sonraki istekte geçerli olur. | +| **Kontrol paneli özeti** | Çoklu ajan modu, çevrimiçi durum, sürüm, çalışma süresi, sağlayıcı sayısı, 30 günlük token toplamı, aktif sağlayıcılar ve kullanılabilir yerel/yönlendirilen modeller. | +| **Alt ajan yetkilendirmesi** | OpenCodex yetkilendirme rehberliği ve ayrı yerel varsayılan katılımı tarafından paylaşılan yerel veya yönlendirilen bir model ve isteğe bağlı akıl yürütme çabası seçin. Bu, proxy tarafında spawn başına bir yönlendirici değildir; aşağıya bakın. | +| **Sidecar'lar** | Web arama modelini ve çabasını artı vizyon açıklama modelini seçin. Değişiklikler bir sonraki istekte geçerli olur. | | **Bakım** | Codex model kataloğunu yeniden senkronize edin, projeye özel yapılandırma atlama uyarılarını inceleyin, en son veya önizleme sürümünü kontrol edin ve isteğe bağlı proxy yeniden başlatmasıyla bir güncelleme çalıştırın. | | **Başlangıç güvenliği** | Ayrı servis ve başlatıcı dolgu sağlığı artı tam onarım komutlarıyla enjekte edilen Codex yönlendirmesinin yeniden başlatmada hayatta kalıp kalmadığını gösterin. | | **Windows sistem tepsisi** | Tek tıklamayla proxy başlatma, durdurma, yeniden başlatma, kontrol paneli erişimi ve durum için kullanıcı başına bir oturum açma tepsisi yükleyin. Tepsi bir kontrolördür, bir proxy yeniden başlatma servisi değildir. | -| **Codex otomatik başlatma** | **Başlangıç** sayfasında, koruma ayrıntılarının içinde: zaten kurulu bir Codex başlatıcı dolgusunun `ocx ensure` çalıştırmasına izin verin. Bu anahtar bir dolgu veya arka plan servisi kurmaz. | +| **Codex otomatik başlatma** | Zaten kurulu bir Codex başlatıcı dolgusunun `ocx ensure` çalıştırmasına izin verin. Bu anahtar bir dolgu veya arka plan servisi kurmaz. | | **Sağlayıcılar** | Sağlayıcıları ekleyin, düzenleyin, varsayılanı ayarlayın (yalnızca etkin sağlayıcılar), etkinleştirin/devre dışı bırakın ve kaldırın; desteklendiğinde OAuth hesap havuzlarını ve API anahtarı havuzlarını yönetin. Geçerli varsayılanı kaldırmak, bir tane varsa kalan ilk etkin sağlayıcıya geçer; aksi takdirde silme reddedilir ve geçerli varsayılan tutulur. Sağlayıcı Ayarları, eksik, yavaş veya aşırı büyük `/models` kataloglarına sahip uç noktalar için canlı model keşfini devre dışı bırakabilir. Claude (Anthropic) OAuth havuzları için oturum açmış her hesap kendi 5 saatlik ve haftalık hız sınırı çubuklarını gösterir (kullanım kimlik bilgisi başınadır); başarısız bir araştırma bilinen son çubukları tutar ve bir sonraki başarılı yenilemeye kadar bunları kullanılamaz olarak işaretler. | | **Sağlayıcı ekle** | Hesap girişi, API anahtarı hizmetleri, yerel sunucular veya özel bir uç nokta için kayıt defteri destekli önayarları arayın. | | **Codex Auth** | ChatGPT/Codex havuz hesapları ekleyin, sonraki oturum hesabını seçin, 5 saatlik / haftalık / 30 günlük kotaları yenileyin, kota otomatik geçişini etkinleştirin veya devre dışı bırakın, %1–100 eşiğini ayarlayın ve geçici arıza yük devretmesini yapılandırın. | -| **Alt Ajanlar** | `spawn_agent` geçersiz kılma listesinde en fazla beş yalın yerel veya ad alanlı yönlendirilen modeli öne çıkarın ve yetkilendirme bölümünün daraltılmış **Gelişmiş** kısmından v1/base/v2 ile v2 iş parçacığı sınırını seçin. | -| **Modeller** | Yerel GPT ve yönlendirilen modelleri açıp kapatın, sağlayıcı izin listelerini ve bağlam sınırlarını ayarlayın. Yeni model politikası, takma adlar, gölge çağrı yakalama ve varsayılan pencere sınırları daraltılmış bir **Gelişmiş** bölümündedir; sağlayıcı başına ek ayarlar (varsayılan takma adlar, özel modeller, pencere sınırları) sağlayıcı başlığındaki ⋯ düğmesinden açılır. Yapılandırılmış sağlayıcılar, keşif kapalı olduğunda veya hiçbir satır döndürmediğinde sıfır modelli gruplar olarak görünür kalır. | +| **Alt Ajanlar** | `spawn_agent` geçersiz kılma listesinde en fazla beş yalın yerel veya ad alanlı yönlendirilen modeli öne çıkarın. | +| **Modeller** | Yerel GPT ve yönlendirilen modelleri açıp kapatın, sağlayıcı izin listelerini ve bağlam sınırlarını ayarlayın, v1/base/v2'yi seçin ve v2 iş parçacığı sınırını yapılandırın. Yapılandırılmış sağlayıcılar, keşif kapalı olduğunda veya hiçbir satır döndürmediğinde sıfır modelli gruplar olarak görünür kalır. | | **Günlükler** | Belirteçler, talep edilen çaba ve (varsa) etkili giden çaba, çözümlenen model, sağlayıcı, durum, istek kimliği, süre ve hata ayrıntılarıyla son istekleri otomatik yenileyin. Ayrıntı görünümü, adaptör bir tane yaydığında tam akıl yürütme hat alanını içerir. Yüklenen Günlükler halkası için toplam belirteçleri ve tahmini liste fiyatı maliyetini görmek üzere donuk görüşme/oturum kimliğine göre (istemci bir tane gönderdiğinde) filtreleyin. | | **Kullanım / Hata Ayıklama** | Belirteç kullanımı kapsamını ve eğilimlerini inceleyin veya isteğe bağlı sağlayıcı aktarımı ve kullanım çıkarma tanılamalarını etkinleştirin. | | **Depolama** | Salt okunur CODEX_HOME disk dökümü (oturumlar, arşivler, DB'ler, ekler). İsteğe bağlı arşivlenmiş temizleme: en eski %N'yi önizleyin, ardından `CODEX_HOME/.trash` konumuna karantinaya alın (varsayılan) veya açık bir onay kutusu arkasında kalıcı olarak silin. **Otomatik temizleme politikası** isteğe bağlıdır ve **varsayılan olarak KAPALIDIR** (`storageCleanupPolicy.enabled`); Depolama sayfasında eşik/hedef/zamanlama/mod yapılandırın veya **Şimdi çalıştır (Run now)**'ı tetikleyin. Karantinaya alınan girdiler Depolama sayfasından geri yüklenebilir (JSONL + iş parçacıkları). Aktif oturumlar salt okunur kalır. Codex en yeni/aktif `state_*.sqlite` dosyasını kilitli tuttuğu sürece temizleme ve geri yükleme reddedilir. | @@ -63,9 +63,9 @@ kararıdır. ### Bir bölüme bağlantı verme Tek bir düzen vardır, bu nedenle yapılandırılacak bir düzen anahtarı yoktur. -`#dashboard` Genel Bakış'ı açar; eski `#dashboard/providers` ve `#dashboard/models` -yer imleri `#providers` ve `#models` üzerine yönlendirilir. -Yeniden Yükle, yer imi ve Geri işlemlerinin tümü bulunduğunuz sayfayı korur. +Bunun yerine kontrol paneli bölümleri adreslenebilirdir: `#dashboard` Genel +Bakış'ı açar, `#dashboard/providers` ve `#dashboard/models` diğer ikisini açar. +Yeniden Yükle, yer imi ve Geri işlemlerinin tümü bulunduğunuz bölümü korur. **Günlükler** `#logs` ve `#logs/debug` ile aynı şekilde çalışır. Daha eski bir `#providers/workspace` yer imi artık `#providers` üzerine iner. @@ -84,7 +84,7 @@ listesini temizler, böylece yeni keşfedilen modeller de açık olur. ## Yetkilendirme seçicisi ve spawn yönlendirmesi -**Alt Ajanlar** sayfasının **Alt ajan yetkilendirmesi** seçicisi `injectionModel`'i ve +Kontrol Panelinin **Alt ajan yetkilendirmesi** seçicisi `injectionModel`'i ve isteğe bağlı olarak `injectionEffort`'ı saklar. **OpenCodex çoklu ajan rehberliği**, bu değerleri kullanan yetkilendirme talimatlarını bağımsız olarak denetler. Uygun v2 turlarında bu rehberlik ana ajana `spawn_agent`'a hangi tam @@ -196,7 +196,7 @@ bölümüne bakın. ## Yıldız vermek ajanın değil sizin kararınızdır -Güncelleme iletişim kutusundaki (kenar çubuğunun güncelleme düğmesinden açılır) yıldız düğmesi — ve etkileşimli bir terminalde `ocx start`'ın +Kenar çubuğunun yıldız düğmesi — ve etkileşimli bir terminalde `ocx start`'ın sorduğu tek seferlik soru — **kendi `gh` girişinizden** geçer. opencodex hiçbir GitHub belirteci tutmaz ve öğrendiği tek şey evet veya hayır cevabınızdır. 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 b7771d4d47..56603ba703 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 @@ -30,18 +30,18 @@ bun run dev:gui | 区域 | 作用 | | --- | --- | -| **Dashboard 摘要** | 显示在线状态、版本与运行时间、provider 数量和 30 天 token 总量,下方依次是重启保护栏、模型同步、折叠的 **Sidecar** 区块和内存压力。旧的 provider/模型标签页已移除,请使用 **Providers** 和 **Models**。 | -| **Sub-agent delegation** | 在 **Subagents** 页面选择供 OpenCodex 委派指引与可选的 Codex 原生子代理默认值共用的原生/路由模型和可选 reasoning 强度。它不是逐次生成的路由器,详见下文。 | -| **Sidecar** | 在 Dashboard 的折叠区块中选择 web-search 模型及强度,以及图像描述模型;更改从下一次请求开始生效。 | +| **Dashboard 摘要** | 显示 multi-agent 模式、在线状态、版本、运行时间、provider 数量、30 天 token 总量、活动 provider 和可用的原生/路由模型。 | +| **Sub-agent delegation** | 选择供 OpenCodex 委派指引与可选的 Codex 原生子代理默认值共用的原生/路由模型和可选 reasoning 强度。它不是逐次生成的路由器,详见下文。 | +| **Sidecar** | 选择 web-search 模型及强度,以及图像描述模型;更改从下一次请求开始生效。 | | **Maintenance** | 重新同步 Codex 模型目录,查看项目级配置绕过警告,检查 latest/preview 版本,并可在更新后重启代理。 | | **启动安全** | 显示注入的 Codex 路由能否在重启后继续工作,并分别显示服务、launcher shim 状态和准确的修复命令。 | | **Windows 托盘** | 安装用户登录托盘,一键控制代理启动、停止、重启、面板和状态。托盘不是代理重启服务。 | -| **Codex 自动启动** | 在 **Startup** 页面的保护状态详情中,允许已安装的 Codex launcher shim 运行 `ocx ensure`。此开关不会安装 shim 或后台服务。 | +| **Codex 自动启动** | 允许已安装的 Codex launcher shim 运行 `ocx ensure`。此开关不会安装 shim 或后台服务。 | | **Providers** | 添加、编辑、设为默认(仅已启用)、启用/禁用、删除 provider,并在支持时管理 OAuth 账号池和 API key 池。删除当前默认时,会切换到剩余的第一个已启用 provider(若存在);否则拒绝删除并保留当前默认。Claude(Anthropic)OAuth 池中,每个已登录账号显示各自的 5 小时与周限额条(用量按凭证计);探测失败时保留上次已知数值并标记为暂时不可用。 | | **Add provider** | 搜索 registry preset,选择账号登录、API key 服务、本地服务器或自定义 endpoint。 | | **Codex Auth** | 添加 ChatGPT/Codex 池账号,选择下一 session 的账号,刷新 5h / 每周 / 30d 配额,启用或停用配额自动切换,设置其 1–100% 阈值和临时故障 failover。 | -| **Subagents** | 在 `spawn_agent` override 列表中置顶最多五个原生或路由模型,并在委派区块的 **高级** 折叠中选择 v1/base/v2 和 v2 thread 数量。 | -| **Models** | 开关原生 GPT 与路由模型,配置 provider allowlist 和上下文上限。新模型策略、别名、shadow-call 拦截和默认窗口上限位于折叠的 **高级** 区块;每个 provider 的附加设置(默认别名、自定义模型、窗口上限)通过 provider 标题上的 ⋯ 按钮打开。 | +| **Subagents** | 在 `spawn_agent` override 列表中置顶最多五个原生或路由模型。 | +| **Models** | 开关原生 GPT 与路由模型,配置 provider allowlist、上下文上限、v1/base/v2 以及 v2 thread 数量。 | | **Logs** | 自动刷新近期请求,显示 token、请求强度以及(可用时)实际发送强度、实际模型、provider、状态、request id、耗时和错误详情。适配器发送 reasoning 参数时,详情中还会显示准确的 wire field。可按不透明会话/对话 ID(客户端提供时)筛选,并对当前已加载的 Logs 环形缓冲合计 token 与估算标价成本。 | | **Usage / Debug** | 查看 token usage 覆盖率与趋势,或启用可选的 provider transport 和 usage 提取诊断。 | | **Storage** | 只读查看 CODEX_HOME 磁盘占用(会话、归档、数据库、附件)。可选归档清理:预览最旧 N%,默认隔离到 `CODEX_HOME/.trash`,或勾选后永久删除。**自动清理策略**为可选且**默认关闭**(`storageCleanupPolicy.enabled`);可在 Storage 页配置阈值/目标/计划/模式,或点「立即运行」。可在 Storage 页从隔离区恢复(JSONL + 线程)。活动会话保持只读。Codex 锁定最新/活动的 `state_*.sqlite` 时拒绝清理与恢复。 | @@ -49,7 +49,7 @@ bun run dev:gui ### 链接到某个部分 -布局只有一种,无需切换。`#dashboard` 是 Overview;旧的 `#dashboard/providers` 与 `#dashboard/models` 书签会跳转到 `#providers` 和 `#models`。刷新、收藏和后退都会保留当前所在的页面。**Logs** 同理,使用 `#logs` 与 `#logs/debug`。旧的 `#providers/workspace` 书签现在会跳转到 `#providers`。 +布局只有一种,无需切换。Dashboard 的各个部分都有自己的地址:`#dashboard` 打开 Overview,`#dashboard/providers` 与 `#dashboard/models` 打开另外两个。刷新、收藏和后退都会保留当前所在的部分。**Logs** 同理,使用 `#logs` 与 `#logs/debug`。旧的 `#providers/workspace` 书签现在会跳转到 `#providers`。 **Logs** 和 **Usage** 中的费用是根据已报告 token 计算的 API 标价折算值,不是账单,也不能证明 实际发生了扣费;实际可能计入订阅用量或消耗服务商额度。 @@ -60,7 +60,7 @@ bun run dev:gui ## 委派选择器与生成路由的区别 -**Subagents** 页面的 **Sub-agent delegation** 选择器会保存 `injectionModel`,以及可选的 +Dashboard 的 **Sub-agent delegation** 选择器会保存 `injectionModel`,以及可选的 `injectionEffort`。所选值会用于由 OpenCodex 编写的委派指引,而该指引由 `multiAgentGuidanceEnabled` 单独控制。清除模型时也会清除已保存的强度,并关闭原生默认值同步。 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 47e47354f8..ccc126a2df 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 @@ -35,25 +35,25 @@ GUI session 簽發到服務的頁面中,並在到期或代理重啟時靜默 | 區域 | 作用 | | --- | --- | -| **Dashboard 摘要** | 顯示線上狀態、版本與運行時間、provider 數量和 30 天 token 總量,下方依序是重啟保護列、模型同步、摺疊的 **Sidecar** 區塊和記憶體壓力。舊的 provider/模型分頁已移除,請使用 **Providers** 和 **Models**。 | -| **Sub-agent delegation** | 在 **Subagents** 頁面選擇供 OpenCodex 委派指引與可選的 Codex 原生子代理預設值共用的原生/路由模型和可選 reasoning 強度。它不是逐次生成的路由器,詳見下文。 | -| **Sidecar** | 在 Dashboard 的摺疊區塊中選擇 web-search 模型及強度,以及圖像描述模型;更改從下一次請求開始生效。 | +| **Dashboard 摘要** | 顯示 multi-agent 模式、線上狀態、版本、運行時間、provider 數量、30 天 token 總量、活動 provider 和可用的原生/路由模型。 | +| **Sub-agent delegation** | 為 v1 委派 prompt 選擇原生或路由模型,並可指定 reasoning 強度。它不是逐次生成的路由器,詳見下文。 | +| **Sidecar** | 選擇 web-search 模型及強度,以及圖像描述模型;更改從下一次請求開始生效。 | | **Maintenance** | 重新同步 Codex 模型目錄,檢視專案級設定繞過警告,檢查 latest/preview 版本,並可在更新後重啟代理。 | | **啟動安全** | 顯示注入的 Codex 路由能否在重啟後繼續工作,並分別顯示服務、launcher shim 狀態和準確的修復命令。 | | **Windows 托盤** | 安裝使用者登入托盤,一鍵控制代理啟動、停止、重啟、面板和狀態。托盤不是代理重啟服務。 | -| **Codex 自動啟動** | 在 **Startup** 頁面的保護狀態詳情中,允許已安裝的 Codex launcher shim 執行 `ocx ensure`。此開關不會安裝 shim 或後臺服務。 | +| **Codex 自動啟動** | 允許已安裝的 Codex launcher shim 執行 `ocx ensure`。此開關不會安裝 shim 或後臺服務。 | | **Providers** | 新增、編輯、啟用/停用、刪除 provider,並在支援時管理 OAuth 帳號池和 API key 池。 | | **Add provider** | 搜尋 registry preset,選擇帳號登入、API key 服務、本機伺服器或自訂 endpoint。 | | **Codex Auth** | 新增 ChatGPT/Codex 池帳號,選擇下一 session 的帳號,重新整理 5h / 每週 / 30d 配額,啟用或停用配額自動切換,設定其 1–100% 閾值和臨時故障 failover。 | -| **Subagents** | 在 `spawn_agent` override 列表中置頂最多五個原生或路由模型,並在委派區塊的 **進階** 摺疊中選擇 v1/base/v2 和 v2 thread 數量。 | -| **Models** | 開關原生 GPT 與路由模型,設定 provider allowlist 和上下文上限。新模型策略、別名、shadow-call 攔截和預設視窗上限位於摺疊的 **進階** 區塊;每個 provider 的附加設定(預設別名、自訂模型、視窗上限)透過 provider 標題上的 ⋯ 按鈕開啟。 | +| **Subagents** | 在 `spawn_agent` override 列表中置頂最多五個原生或路由模型。 | +| **Models** | 開關原生 GPT 與路由模型,設定 provider allowlist、上下文上限、v1/base/v2 以及 v2 thread 數量。 | | **Logs** | 自動重新整理近期請求,顯示 token、請求強度、實際模型、provider、狀態、request id、耗時和錯誤詳情。 | | **Usage / Debug** | 檢視 token usage 覆蓋率與趨勢,或啟用可選的 provider transport 和 usage 提取診斷。 | | **Stop** | 優雅地停止代理和已安裝的後臺服務,恢復原生 Codex 並退出(`POST /api/stop`)。在使用工作排程器後端的 Windows 上,儀表板會拒絕並提示改用 `ocx stop`:工作結束後包裝程序仍可能重新啟動 Proxy,只有執行在 Proxy 之外的 stop 才能在還原用戶端設定前確認這個重啟視窗。被拒絕時不會做任何變更。 | ### 連結到某個部分 -佈局只有一種,無需切換。`#dashboard` 是 Overview;舊的 `#dashboard/providers` 與 `#dashboard/models` 書籤會跳轉到 `#providers` 和 `#models`。重新整理、收藏和後退都會保留目前所在的頁面。**Logs** 同理,使用 `#logs` 與 `#logs/debug`。舊的 `#providers/workspace` 書籤現在會跳轉到 `#providers`。 +佈局只有一種,無需切換。Dashboard 的各個部分都有自己的地址:`#dashboard` 開啟 Overview,`#dashboard/providers` 與 `#dashboard/models` 開啟另外兩個。重新整理、收藏和後退都會保留目前所在的部分。**Logs** 同理,使用 `#logs` 與 `#logs/debug`。舊的 `#providers/workspace` 書籤現在會跳轉到 `#providers`。 **Logs** 和 **Usage** 中的費用是根據已報告 token 計算的 API 標價折算值,不是帳單,也不能證明 實際發生了扣費;實際可能計入訂閱用量或消耗服務商額度。 @@ -64,18 +64,14 @@ GUI session 簽發到服務的頁面中,並在到期或代理重啟時靜默 ## 委派選擇器與生成路由的區別 -**Subagents** 頁面的 **Sub-agent delegation** 選擇器會儲存 `injectionModel`,以及可選的 -`injectionEffort`。所選值會用於由 OpenCodex 編寫的委派指引,而該指引由 -`multiAgentGuidanceEnabled` 單獨控制。在符合條件的 v2 turn 中,這段指引會告訴父代理呼叫 -`spawn_agent` 時應傳入哪個精確模型和 reasoning 強度。清除模型時也會清除已儲存的強度,並關閉原生預設值同步。 - -啟用 **用作原生 Codex 子代理預設值** 後,當 OpenCodex 管理目前的 Codex 路由時,下一次同步或重新啟動會 -把所選模型和強度套用為原生 `[agents]` 預設值;外部使用者管理的 provider 設定不會被修改。這些預設值只影響新建的 Codex 任務,該選項本身不會觸發委派。既有的使用者自有 -`[agents]` 預設值會保留而不會被覆寫,因此要求的預設值可能與 Codex 實際使用的預設值不同。 +Dashboard 的 **Sub-agent delegation** 選擇器會儲存 `injectionModel`,以及可選的 +`injectionEffort`。在 v1 turn 中,opencodex 會注入一段指引,告訴父代理呼叫 `spawn_agent` 時應 +傳入哪個精確模型和 reasoning 強度。只要選定模型,無論父代理目前使用何種 reasoning 強度,都會 +啟用這段指引;清除模型時也會清除已儲存的強度。 :::caution -兩個開關相互獨立:關閉 OpenCodex 委派指引不會關閉原生預設值同步;啟用原生預設值同步也不會 -啟用委派指引或觸發委派。兩者都不是代理側的逐次跨模型路由器。v1/base/v2 的 +該選擇器是面向 v1 相容介面的委派指引。在 `multi_agent_v2` 中,目前代理不會附加 v1 注入訊息, +而且所有生成的子代理都會繼承父 session 的模型。它不是代理側的跨模型路由器。v1/base/v2 的 權威說明見 [子代理介面](/zh-tw/guides/sub-agent-surface/)。 ::: @@ -102,7 +98,7 @@ GUI session 簽發到服務的頁面中,並在到期或代理重啟時靜默 ## 星標是你的決定,不是 agent 的 -更新對話框(從側邊欄的更新按鈕開啟)中的星標按鈕——以及 `ocx start` 在互動式終端機中詢問的一次性問題——都透過 **你自己的 +側邊欄的星標按鈕——以及 `ocx start` 在互動式終端機中詢問的一次性問題——都透過 **你自己的 `gh` 登入** 執行。opencodex 不持有任何 GitHub token,它唯一得知的是你的 yes 或 no。 由於這會寫入你的 GitHub 帳號,agent 驅動的呼叫者會被拒絕,而不是被允許替你回答: diff --git a/gui/scripts/find-orphan-keys.mjs b/gui/scripts/find-orphan-keys.mjs deleted file mode 100644 index f06e97fbed..0000000000 --- a/gui/scripts/find-orphan-keys.mjs +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bun -/** - * Find i18n keys defined in src/i18n/en.ts that no source file consumes. - * - * A key is "consumed" when its exact string appears outside src/i18n/, or when a - * dynamic prefix that source builds with a template literal covers it. The dynamic - * prefixes are listed explicitly: a scanner that guessed them would hide true orphans. - * - * Usage: bun scripts/find-orphan-keys.mjs # prints orphans, exit 1 if any - * bun scripts/find-orphan-keys.mjs --json # JSON array - */ -import { readdirSync, readFileSync, statSync } from "node:fs"; -import { join, relative } from "node:path"; - -const root = new URL("..", import.meta.url).pathname; -const srcDir = join(root, "src"); -const i18nDir = join(srcDir, "i18n"); - -/** - * Template-literal key families in source (`t(\`prefix${x}\`)`). Every entry must be - * backed by a real dynamic construction; a namespace-wide entry would exempt the whole - * family and hide true orphans. Where the interpolated set is a closed literal union in - * source, list the concrete keys instead of the prefix (`models.newPolicy_` builds only - * `off` and `on`; `inherit` is a genuine orphan). Regenerate the evidence with: - * rg -o --no-filename '\`[a-zA-Z][a-zA-Z0-9_.]*\.[a-zA-Z0-9_.]*\$\{' src -g '!i18n/**' | sort -u - * `src/i18n/lab-translations.ts` mirrors the `lab.*` namespace in a typed Record, but a - * translation is not a consumer: Lab keys the UI renders appear literally in - * CompatibilityMatrix.tsx, so `lab.` is not exempted. - */ -export const DYNAMIC_PREFIXES = [ - "claude.authSource.", - "cws.err.", - "cws.quota.", - "debug.", - "logs.detail.source.", - "logs.filter.surface.", - "logs.tokens.", - "models.newPolicy_off", - "models.newPolicy_on", - "models.presetMode_", - "models.reasoningEffort.", - "models.v2Mode_", - "routing.compatibility.layer.", - "routing.unknownEvidence.", - "usage.range.", -]; - -function walk(dir, out = []) { - for (const name of readdirSync(dir)) { - const p = join(dir, name); - if (statSync(p).isDirectory()) { if (p !== i18nDir) walk(p, out); } - else if (/\.(tsx?|mjs|js)$/.test(name)) out.push(p); - } - return out; -} - -export function findOrphanKeys() { - const en = readFileSync(join(i18nDir, "en.ts"), "utf8"); - const keys = [...en.matchAll(/^\s*"([^"]+)":\s*"/gm)].map(m => m[1]); - const sources = walk(srcDir).map(p => readFileSync(p, "utf8")).join("\n"); - return keys.filter(key => - !sources.includes(`"${key}"`) - && !sources.includes(`'${key}'`) - && !DYNAMIC_PREFIXES.some(prefix => key.startsWith(prefix)), - ); -} - -if (import.meta.main) { - const orphans = findOrphanKeys(); - if (process.argv.includes("--json")) console.log(JSON.stringify(orphans)); - else for (const key of orphans) console.log(key); - process.exit(orphans.length ? 1 : 0); -} diff --git a/gui/src/App.tsx b/gui/src/App.tsx index 8dea0d50b4..ae83d0f916 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -320,40 +320,25 @@ export default function App() { })}
- {/* - Two rows of orbs, no text. Language, theme, GitHub and update are preferences - and links; stop and restart are the runtime controls. They used to be five - labelled rows that together outweighed the nine navigation entries above them. - Every orb keeps aria-label + title, so nothing is lost to assistive tech. - */} -
-
- - ({ value: l.code, label: localeDisplayName(l.code) }))} + onChange={v => setLocale(v as Locale)} + label={t("lang.label")} + placement="right" + portal={false} + style={{ flex: 1, minWidth: 0, width: "100%" }} />
-
+ +
+ {t("dash.actions")} +
{targets.connected && sharedSessionReady && ( +
+ { + // The update dialog lives on the dashboard maintenance panel. Deep-link to + // `#dashboard/update` and let the dashboard own the check/run flow — no + // cross-component event bus, and the link survives a refresh. + setNavOpen(false); + navigateToPage("dashboard", "update"); + }} + />
diff --git a/gui/src/app-routing.ts b/gui/src/app-routing.ts index 05ceaf4418..bab41b1eee 100644 --- a/gui/src/app-routing.ts +++ b/gui/src/app-routing.ts @@ -55,13 +55,11 @@ export function readPageFromHash(hash?: string): Page { } /** - * The dashboard used to have Providers and Models tabs at these hashes. They were read-only - * copies of the real pages, so the bookmarks now redirect there (see resolveAppHashChange). + * Dashboard section tabs live in the hash so refresh/bookmark/back-forward keep the + * choice, mirroring Logs (`#logs` / `#logs/debug`). Overview is the bare `#dashboard`, + * so it has no suffix entry here. */ -export const LEGACY_DASHBOARD_TAB_REDIRECTS: Readonly> = { - "dashboard/providers": "providers", - "dashboard/models": "models", -}; +export const DASHBOARD_TAB_HASHES = ["dashboard/providers", "dashboard/models"] as const; /** * Models owns four tabs: the catalog, Combos, Routing, and Compatibility. The catalog @@ -109,7 +107,8 @@ export function hashBelongsToPage(rawHash: string, page: Page): boolean { || (page === "logs" && rawHash === "logs/debug") || (page === "codex-set" && rawHash === "codex-set/prompt") || (page === "models" && (MODELS_TAB_HASHES as readonly string[]).includes(rawHash)) - || (page === "dashboard" && rawHash === DASHBOARD_UPDATE_HASH) + || (page === "dashboard" + && (rawHash === DASHBOARD_UPDATE_HASH || (DASHBOARD_TAB_HASHES as readonly string[]).includes(rawHash))) || (page === "integrations" && (INTEGRATION_TAB_HASHES as readonly string[]).includes(rawHash)); } @@ -133,9 +132,6 @@ export function resolveAppHashChange(rawHash: string): AppHashChangeAction { if (rawHash === "debug" || rawHash.startsWith("debug/")) { return { page: "logs", replaceTo: "logs/debug" }; } - // Legacy: the dashboard's Providers / Models tabs. Passive replace, like the others. - const legacyDashboardTarget = LEGACY_DASHBOARD_TAB_REDIRECTS[rawHash]; - if (legacyDashboardTarget) return { page: legacyDashboardTarget, replaceTo: legacyDashboardTarget }; /* Legacy: Codex Auth is now the Multi-auth tab of Codex Set. */ if (rawHash === "codex-auth" || rawHash.startsWith("codex-auth/")) { diff --git a/gui/src/components/MemoryObservabilityCard.tsx b/gui/src/components/MemoryObservabilityCard.tsx index 3e052a7681..ebf12a4ad7 100644 --- a/gui/src/components/MemoryObservabilityCard.tsx +++ b/gui/src/components/MemoryObservabilityCard.tsx @@ -448,13 +448,7 @@ export default function MemoryObservabilityCard({ apiBase }: { apiBase: string } t={t} /> - {/* Everything numeric is collapsed by default: the pressure bar above is the headline; four byte - counts refreshing every few seconds are diagnosis, not a decision. */} -
- {t("dash.mem.details")} -
{t("dash.mem.hint")}
- -
+
{/* heapUsed can exceed heapTotal on Bun (external buffers are counted in used but not in the JS-object arena), so a "used / total" label reads @@ -472,6 +466,10 @@ export default function MemoryObservabilityCard({ apiBase }: { apiBase: string } />
+ {/* Secondary diagnostics collapsed by default: only the headline stats stay visible. */} +
+ {t("dash.mem.details")} +
{t("dash.mem.hint")}
{t("dash.mem.runtime")}
diff --git a/gui/src/components/github-star-button.tsx b/gui/src/components/github-star-button.tsx deleted file mode 100644 index 895f1c1610..0000000000 --- a/gui/src/components/github-star-button.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/** - * "Star on GitHub" as an explicit, human-clicked button. - * - * It used to be a sidebar orb on every page: a promotion ask polling `gh` every five - * minutes at the same visual weight as the proxy kill switch. It now lives inside the - * update dialog, which is the one place the dashboard already talks about the project - * itself. The behaviour is unchanged: an already-starred repo renders a settled marker, - * an unauthenticated `gh` falls back to opening the repository page (a POST could not - * succeed there), and a `gh` failure mid-click does the same fallback instead of - * leaving a dead button. The consent rule in AGENTS_INSTALL.md is untouched — nothing - * here stars on the user's behalf. - * - * Mount it only while the dialog is open: the poll starts on mount and stops on unmount. - */ -import { useState } from "react"; -import { useKeyedClientResource } from "../client-resource"; -import { IconStar } from "../icons"; -import { useT } from "../i18n/shared"; - -type StarState = "starred" | "not-starred" | "unauthenticated"; - -interface StarStatus { - state?: StarState; - url?: string; -} - -const STAR_POLL_MS = 5 * 60_000; -export const REPO_URL = "https://github.com/lidge-jun/opencodex"; - -async function readJson(url: string, signal: AbortSignal): Promise { - const res = await fetch(url, { signal }); - if (!res.ok) return null; - return await res.json() as T; -} - -export function GithubStarButton({ apiBase }: { apiBase: string }) { - const t = useT(); - const [starring, setStarring] = useState(false); - /* - * Optimistic result of this session's own click, tagged with the polled state it was - * taken against. Once the poll reports something different from that baseline the - * server has caught up (or the repo was unstarred elsewhere), so the override is - * ignored — derived during render rather than cleared in an effect. - */ - const [starOverride, setStarOverride] = useState<{ state: StarState; basedOn: StarState | null } | null>(null); - - const starPoll = useKeyedClientResource( - `github-star:${apiBase}`, - [apiBase], - (signal) => readJson(`${apiBase}/api/github/star`, signal), - { pollMs: STAR_POLL_MS }, - ); - - const polledState = starPoll.data?.state ?? null; - const overrideStillApplies = starOverride !== null && starOverride.basedOn === polledState; - const starState: StarState = overrideStillApplies - ? starOverride.state - : polledState ?? "not-starred"; - const repoUrl = starPoll.data?.url ?? REPO_URL; - const starred = starState === "starred"; - - const openRepo = () => window.open(repoUrl, "_blank", "noopener,noreferrer"); - - const handleStar = async () => { - if (starred || starring) return; - if (starState === "unauthenticated") { openRepo(); return; } - setStarring(true); - try { - const res = await fetch(`${apiBase}/api/github/star`, { method: "POST" }); - // A proxy running older code has no such route. Falling through to the repo page - // keeps the button useful instead of failing silently. - const data = res.ok ? await res.json() as StarStatus & { ok?: boolean } : null; - if (data?.ok === true) { - setStarOverride({ state: "starred", basedOn: polledState }); - return; - } - // Also covers the 403 the proxy returns when it is running under an agent - // session and this click carried no dashboard session (`agent_consent_required`): - // the repo page is exactly where the user can star it themselves. - if (data?.state) setStarOverride({ state: data.state, basedOn: polledState }); - openRepo(); - } catch { - openRepo(); - } finally { - setStarring(false); - starPoll.refresh(); - } - }; - - const starLabel = starred - ? t("sidebar.starred") - : starState === "unauthenticated" - ? t("sidebar.starUnauthenticated") - : t("sidebar.star"); - - return ( - - ); -} diff --git a/gui/src/components/sidebar-github-row.tsx b/gui/src/components/sidebar-github-row.tsx index 0f641a0be8..350cf0cb43 100644 --- a/gui/src/components/sidebar-github-row.tsx +++ b/gui/src/components/sidebar-github-row.tsx @@ -1,9 +1,11 @@ /** - * Sidebar footer: two circular satellite buttons, GitHub and Update. + * Sidebar footer row: the GitHub link plus two circular satellite buttons. * - * GitHub: a plain link orb. The star action that used to sit beside it moved into the - * update dialog (`GithubStarButton`) — a promotion ask does not belong at the same - * weight as the proxy kill switch on every page. + * Star: reflects the real state from `gh` rather than always inviting a click. + * Already starred renders a filled, non-interactive marker; an unauthenticated + * `gh` falls back to opening the repository page, because a POST could not + * succeed there. A `gh` failure mid-click does the same fallback instead of + * leaving a dead button. * * Update: always present, so "am I current?" is answerable at any time. It reads the * cached badge endpoint (no npm spawn per poll) purely to decide emphasis — an @@ -11,10 +13,17 @@ * Either way a click runs a fresh check and opens the dashboard update dialog, which * already owns the install/cancel decision. */ +import { useState } from "react"; import { useKeyedClientResource } from "../client-resource"; -import { IconDownload, IconGithub } from "../icons"; +import { IconDownload, IconGithub, IconStar } from "../icons"; import { useT } from "../i18n/shared"; -import { REPO_URL } from "./github-star-button"; + +type StarState = "starred" | "not-starred" | "unauthenticated"; + +interface StarStatus { + state?: StarState; + url?: string; +} interface UpdateBadge { updateAvailable?: boolean; @@ -23,7 +32,9 @@ interface UpdateBadge { unknown?: boolean; } +const STAR_POLL_MS = 5 * 60_000; const BADGE_POLL_MS = 10 * 60_000; +const REPO_URL = "https://github.com/lidge-jun/opencodex"; async function readJson(url: string, signal: AbortSignal): Promise { const res = await fetch(url, { signal }); @@ -40,6 +51,21 @@ export function SidebarGithubRow({ onOpenUpdate: () => void; }) { const t = useT(); + const [starring, setStarring] = useState(false); + /* + * Optimistic result of this session's own click, tagged with the polled state it was + * taken against. Once the poll reports something different from that baseline the + * server has caught up (or the repo was unstarred elsewhere), so the override is + * ignored — derived during render rather than cleared in an effect. + */ + const [starOverride, setStarOverride] = useState<{ state: StarState; basedOn: StarState | null } | null>(null); + + const starPoll = useKeyedClientResource( + `sidebar-star:${apiBase}`, + [apiBase], + (signal) => readJson(`${apiBase}/api/github/star`, signal), + { pollMs: STAR_POLL_MS }, + ); const badgePoll = useKeyedClientResource( `sidebar-update-badge:${apiBase}`, [apiBase], @@ -47,10 +73,54 @@ export function SidebarGithubRow({ { pollMs: BADGE_POLL_MS }, ); + const polledState = starPoll.data?.state ?? null; + const overrideStillApplies = starOverride !== null && starOverride.basedOn === polledState; + const starState: StarState = overrideStillApplies + ? starOverride.state + : polledState ?? "not-starred"; + const repoUrl = starPoll.data?.url ?? REPO_URL; + const starred = starState === "starred"; const badge = badgePoll.data; const updateAvailable = badge?.updateAvailable === true; const latestVersion = badge?.latestVersion ?? null; + const openRepo = () => window.open(repoUrl, "_blank", "noopener,noreferrer"); + + const handleStar = async () => { + if (starred || starring) return; + if (starState === "unauthenticated") { openRepo(); return; } + setStarring(true); + try { + const res = await fetch(`${apiBase}/api/github/star`, { method: "POST" }); + // A proxy running older code has no such route. Falling through to the repo page + // keeps the button useful instead of failing silently. + const data = res.ok ? await res.json() as StarStatus & { ok?: boolean } : null; + if (data?.ok === true) { + setStarOverride({ state: "starred", basedOn: polledState }); + return; + } + // Also covers the 403 the proxy returns when it is running under an agent + // session and this click carried no dashboard session (`agent_consent_required`): + // the repo page is exactly where the user can star it themselves. + // + // gh refused (logged out, revoked scope, network). The server reports a fixed code + // rather than gh's output, so there is nothing to show — hand over the page instead. + if (data?.state) setStarOverride({ state: data.state, basedOn: polledState }); + openRepo(); + } catch { + openRepo(); + } finally { + setStarring(false); + starPoll.refresh(); + } + }; + + const starLabel = starred + ? t("sidebar.starred") + : starState === "unauthenticated" + ? t("sidebar.starUnauthenticated") + : t("sidebar.star"); + // With an update pending the label names the version; otherwise it describes the // action, so the button never reads as "update available" when nothing is waiting. const updateLabel = updateAvailable && latestVersion @@ -58,27 +128,33 @@ export function SidebarGithubRow({ : t("sidebar.checkUpdate"); return ( - <> - -
+ + {t("common.github")} - - +
+ + +
+
); } diff --git a/gui/src/components/subagents-workspace/EffortCapSection.tsx b/gui/src/components/subagents-workspace/EffortCapSection.tsx deleted file mode 100644 index 5d718d31da..0000000000 --- a/gui/src/components/subagents-workspace/EffortCapSection.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Effort caps for the main model and for subagents (`/api/effort-caps`). - * - * This was a dashboard card. It only means something once multi-agent mode is on, and - * it is a delegation setting, so it lives with the other delegation settings on the - * Subagents page. The read goes through the shared client-resource layer; a save is an - * optimistic override until the server answers. - */ -import { useCallback, useState } from "react"; -import { useKeyedClientResource } from "../../client-resource"; -import { useT } from "../../i18n/shared"; -import { Select } from "../../ui"; -import { EFFORT_CAP_LEVELS, requireJson } from "../../pages/dashboard-shared"; - -type Caps = { effortCap: string; subagentEffortCap: string }; - -async function readCaps(apiBase: string, signal: AbortSignal): Promise { - const res = await fetch(`${apiBase}/api/effort-caps`, { signal }); - const data = await requireJson<{ effortCap?: string | null; subagentEffortCap?: string | null }>(res); - return { effortCap: data.effortCap ?? "", subagentEffortCap: data.subagentEffortCap ?? "" }; -} - -export function EffortCapSection({ apiBase }: { apiBase: string }) { - const t = useT(); - const read = useKeyedClientResource(`effort-caps:${apiBase}`, [apiBase], (signal) => readCaps(apiBase, signal)); - const [override, setOverride] = useState(null); - const [saving, setSaving] = useState(false); - const caps: Caps = override ?? read.data ?? { effortCap: "", subagentEffortCap: "" }; - - const save = useCallback(async (patch: { effortCap?: string | null; subagentEffortCap?: string | null }) => { - if (saving) return; - setSaving(true); - try { - const res = await fetch(`${apiBase}/api/effort-caps`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(patch), - }); - const data = await requireJson<{ ok: boolean; effortCap?: string | null; subagentEffortCap?: string | null }>(res); - setOverride({ effortCap: data.effortCap ?? "", subagentEffortCap: data.subagentEffortCap ?? "" }); - } catch { /* ignore */ } - finally { setSaving(false); } - }, [apiBase, saving]); - - const options = [ - { value: "", label: t("dash.effortCapNone") }, - ...EFFORT_CAP_LEVELS.map(e => ({ value: e, label: e })), - ]; - - return ( -
-
-
- {t("dash.effortCapLabel")} - {t("dash.effortCapHelp")} -
-
- { void save({ subagentEffortCap: v || null }); }} - disabled={saving} - label={t("dash.subagentEffortCapLabel")} - align="right" - /> -
-
-
- ); -} diff --git a/gui/src/components/subagents-workspace/SubagentDelegationSection.tsx b/gui/src/components/subagents-workspace/SubagentDelegationSection.tsx index 362cbec6fc..46c0447a7c 100644 --- a/gui/src/components/subagents-workspace/SubagentDelegationSection.tsx +++ b/gui/src/components/subagents-workspace/SubagentDelegationSection.tsx @@ -9,8 +9,7 @@ import { useState } from "react"; import { Select, Tooltip } from "../../ui"; import { IconInfo } from "../../icons"; -import type { TKey } from "../../i18n/shared"; -import { useT } from "../../i18n/shared"; +import { useT, type TKey } from "../../i18n/shared"; import { formatNamespacedModelId } from "../../provider-icons"; import type { DelegationPatch, DelegationModelOption } from "../../pages/use-subagent-delegation"; import type { UltraModePatch, UltraModeState } from "../../pages/use-subagent-delegation"; diff --git a/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx index 68280f48d4..a22bd2a305 100644 --- a/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx +++ b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx @@ -28,12 +28,9 @@ import { modelLabel } from "../../model-display"; import { SectionTabs } from "../section-tabs"; import { sectionAnchorId } from "../../section-anchors"; import SubagentDelegationSection from "./SubagentDelegationSection"; -import { EffortCapSection } from "./EffortCapSection"; import type { DelegationPatch, DelegationModelOption, UltraModePatch, UltraModeState } from "../../pages/use-subagent-delegation"; export interface SubagentsWorkspaceProps { - /** Needed by sections that own their own fetch (effort caps). */ - apiBase?: string; available: string[]; chosen: string[]; busy?: boolean; @@ -60,7 +57,6 @@ export interface SubagentsWorkspaceProps { export const FEATURED_MAX = 5; export default function SubagentsWorkspace({ - apiBase, available, chosen, busy = false, @@ -242,13 +238,6 @@ export default function SubagentsWorkspace({ ultraLoadFailed={delegation.ultraLoadFailed} onUltraModeRetry={delegation.onUltraModeRetry} /> - {/* - Effort caps only apply outside v1 (there are no ultra-mode turns to cap), which - is the same condition the dashboard used before this card moved here. - */} - {apiBase && delegation.ultraMode.multiAgentMode !== "v1" && ( - - )}
diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 94ce7e9103..529e00ea49 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -93,7 +93,6 @@ export const de: Record = { "sidebar.starFailed": "Markieren über gh fehlgeschlagen. GitHub wird stattdessen geöffnet.", "sidebar.updateAvailable": "Update verfügbar: {version}", "sidebar.checkUpdate": "Nach Updates suchen", - "sidebar.preferences": "Einstellungen", "common.save": "Speichern", "common.saving": "Speichern…", "common.cancel": "Abbrechen", @@ -125,6 +124,7 @@ export const de: Record = { "startup.title": "Startsicherheit", "startup.subtitle": "Prüft, ob Codex opencodex nach einem Neustart erreicht, bevor lokales Proxy-Routing in einer Wiederverbindungsschleife endet.", "startup.refresh": "Aktualisieren", + "startup.backToDashboard": "Zurück zum Dashboard", "startup.loading": "Startschutz wird geprüft…", "startup.error": "Startschutz konnte nicht gelesen werden.", "startup.staleData": "Die aktuelle Prüfung ist fehlgeschlagen. Die Werte unten sind veraltet und kein Nachweis für Schutz.", @@ -138,11 +138,14 @@ export const de: Record = { "startup.riskDetailCustomLocal": "Codex verwendet ein benutzerdefiniertes lokales Gateway. opencodex kann dessen Neustart-Lebenszyklus weder verwalten noch prüfen.", "startup.riskDetailWindowsShim": "Der Launcher-Shim schützt unterstützte CLI-Skripte, aber Codex Desktop und direkte codex.exe-Aufrufe können ihn unter Windows umgehen.", "startup.safeDetail": "Routing und Startmechanismus stimmen überein. Nach einem Neustart sollte kein manuelles ocx start nötig sein.", + "startup.routing": "Codex-Routing", "startup.routing.proxy": "Lokaler Proxy", "startup.routing.native": "Natives OpenAI", "startup.routing.customLocal": "Benutzerdefiniertes lokales Gateway", "startup.routing.customRemote": "Benutzerdefiniertes Remote-Gateway", "startup.routing.unknown": "Unbekanntes oder ungültiges Routing", + "startup.restartProtection": "Neustartschutz", + "startup.preference": "Start bei Bedarf", "startup.enabled": "Aktiviert", "startup.disabled": "Deaktiviert", "startup.protection.service": "Hintergrunddienst", @@ -199,9 +202,14 @@ export const de: Record = { "startup.codexRuntime.clampHidden": "Einige Reasoning-Effort-Optionen wurden ausgeblendet, weil OpenCodex Codex {version} verwendet hat.", "startup.codexRuntime.clampHiddenWithEfforts": "Einige Reasoning-Effort-Optionen wurden ausgeblendet, weil OpenCodex Codex {version} verwendet hat (entfernt: {efforts}).", "startup.codexRuntime.olderBinary": "OpenCodex verwendet eine ältere Codex-Binary ({version}). Eine neuere Installation ist verfügbar.", + "dash.subtitle": "Live-Status des lokalen opencodex-Proxys, seiner Anbieter und der in Codex gerouteten Modelle.", + "dash.workspace.overview": "Übersicht", + "dash.workspace.sections": "Abschnitte", "dash.status": "Status", "dash.online": "Online", "dash.offline": "Offline", + "dash.version": "Version", + "dash.uptime": "Laufzeit", "dash.providers": "Anbieter", "dash.tokens30d": "Tokens (30d)", "dash.coverage": "{pct} Abdeckung", @@ -238,11 +246,22 @@ export const de: Record = { "dash.mem.reconnecting": "Proxy wird neu gestartet… warte auf Verbindung", "dash.mem.restartFailed": "Abwarten & Neustart fehlgeschlagen. Prüfen Sie, ob der Proxy läuft.", "dash.mem.restartNoSupervisor": "Kein Neustartschutz erkannt. Der Proxy bleibt nach dem Neustart möglicherweise aus, bis Sie ihn erneut starten.", + "dash.activeProviders": "Aktive Anbieter", + "dash.noProviders": "Keine Anbieter konfiguriert. Führe {cmd} aus.", + "dash.col.name": "Name", + "dash.col.adapter": "Adapter", + "dash.col.baseUrl": "Basis-URL", + "dash.col.model": "Modell", + "dash.modelsNoResults": "Keine Modelle entsprechen deiner Suche.", + "dash.availableModels": "Verfügbare Modelle", + "dash.noModels": "Keine Modelle gefunden. Prüfe die API-Schlüssel des Anbieters.", "dash.cannotConnect": "Keine Verbindung zum Proxy. Läuft er?", "dash.runStart": "Führe {cmd} aus, um den Proxy zu starten.", "dash.stop": "Proxy stoppen", "dash.stopConfirm": "Proxy stoppen und natives Codex wiederherstellen?", "dash.stopFailed": "Proxy konnte nicht gestoppt werden (HTTP {status}).", + "dash.maSwitchFailed": "Moduswechsel fehlgeschlagen (HTTP {status}).", + "dash.maNetworkError": "Netzwerkfehler — läuft der Proxy?", "dash.stopping": "Wird gestoppt…", "dash.actions": "Proxy", "dash.codexRestart": "Codex-Modelle neu laden", @@ -264,7 +283,6 @@ export const de: Record = { "dash.searchReasoning": "Such-Reasoning-Aufwand", "dash.visionModel": "Vision-Sidecar-Modell", "dash.visionModelHint": "Modell zur Beschreibung von Bildern für nur-Text-Routen. Erfordert ChatGPT-Login.", - "dash.sidecars": "Sidecars", "dash.webSearchSidecar": "Websuche-Sidecar", "dash.webSearchSidecarHint": "Backend und Modell für die Websuche gerouteter Modelle auswählen.", "dash.webSearchStream": "Antworten live streamen", @@ -272,9 +290,12 @@ export const de: Record = { "dash.visionSidecar": "Vision-Sidecar", "dash.visionSidecarHint": "Backend und Modell zur Bildbeschreibung für reine Textmodelle auswählen.", "dash.visionOff": "Aus", + "dash.shadowCallIntercept": "Shadow-Call-Abfangen", "dash.shadowCallInterceptHint": "Fängt die Hintergrund-Hilfsaufrufe der Codex-App ({models}) ab und leitet sie an das gewählte Modell um.", "dash.shadowCallWarning": "⚠ Bei Aktivierung werden ALLE Anfragen an {models} durch das gewählte Modell ersetzt.", "dash.shadowCallOriginal": "Original", + "dash.shadowCallModel": "Ersatzmodell", + "dash.shadowCallTooltip": "Die Codex-App ruft im Hintergrund ein Hilfsmodell für Titelgenerierung, Commit-Nachrichten und Skill-Orchestrierung auf. Das Modell wechselt zwischen Client-Versionen, daher fängt opencodex diesen Satz ab: {models}.", "models.shadowCallIntercept": "Shadow-Call-Abfangen", "models.shadowCallInterceptHint": "Fängt die Hintergrund-Hilfsaufrufe der Codex-App ({models}) ab und leitet sie an das gewählte Modell um.", "dash.sidecarBackend": "Backend", @@ -462,10 +483,6 @@ export const de: Record = { "models.subtitle.combos": "Geordnete Modellgruppen, die unter einer id antworten. Ziele mit Failover verketten oder die Last mit einer Balancing-Strategie verteilen.", "models.subtitle.compatibility": "Schreibgeschützte Kompatibilitätsmatrix aus der Lab-Projektion.", "models.subtitle.routing": "Policy-Profile, Dry-Run-Auswertung und quellenbasierte Routing-Analysen.", - "models.advanced": "Erweitert", - "models.groupMore": "Weitere Anbieteraktionen anzeigen", - "models.subtitleAria": "So funktioniert der Katalog", - "models.orderHintAria": "So wird die Reihenfolge im Picker bestimmt", "models.subtitle": "Steuere, welche Modelle Codex sieht — natives GPT-Passthrough und geroutete Anbieter, nach Anbieter gruppiert (Kopfzeile zum Einklappen anklicken). Ausgeblendete Modelle fehlen in Katalog und Auswahl, bleiben aber per genauer ID aufrufbar. Änderungen gelten bei der nächsten Codex-Runde — opencodex invalidiert Codex 5-Minuten-Modell-Cache, kein Neustart nötig.", "models.nativeGroupLabel": "OpenAI nativ", "models.nativeHint": "Passthrough-Modelle verwenden die unter Anbieter gewählte Pool- oder Direkt-Option. Ausblenden entfernt sie aus der Codex-Auswahl (Katalogeintrag bleibt, Reaktivierung stellt exakt wieder her). Ein hier hinzugefügtes Modell wird als gerouteter `openai/`-Selektor registriert, nicht als neue reine Passthrough-ID.", @@ -502,6 +519,7 @@ export const de: Record = { "models.keepNativeOnV1": "ChatGPT auf v1 lassen", "models.keepNativeOnV1Hint": "ChatGPT-native Eltern verschlüsseln v2-Kindaufgaben — Grok und Claude können sie nicht lesen. An lassen, wenn Sol/Terra weiterhin geroutete Modelle starten sollen. Geroutete Eltern bleiben auf v2.", "models.v2Help": "Steuert die Multi-Agent-Oberfläche für alle Modelle.\n\nv1: Klassischer Single-Thread-Agent. Jedes Modell nutzt die v1-Collab-Oberfläche.\nbase: Upstream-Standard — sol/terra nutzen v2, luna v1, andere folgen dem Codex-Feature-Flag.\nv2: Multi-Thread-Agent mit spawn_agent. Jedes Modell nutzt die v2-Collab-Oberfläche.\n\nUnter v2 lässt „ChatGPT auf v1 lassen“ Sol/Terra auf v1, damit sie weiter Grok oder Claude starten können. ChatGPT verschlüsselt v2-Kindaufgaben; geroutete Modelle können sie nicht lesen. Geroutete Eltern bleiben auf v2.\n\nÄnderungen gelten für neue Sitzungen.", + "dash.multiAgent": "Sub-Agent", "models.v2Conflict": "[agents] max_threads ist gesetzt — codex verweigert den Start; entferne es aus config.toml", "models.v2Applied": "Sub-Agent-Modus aktualisiert — gilt für neue Sitzungen (Codex-App neu starten, um die Auswahl zu aktualisieren)", "models.v2ThreadsLabel": "Max. Threads", @@ -626,6 +644,7 @@ export const de: Record = { "logs.title": "Anfrage-Protokolle", "logs.tabLogs": "Protokolle", "logs.tabDebug": "Diagnose", + "logs.subtitle": "Letzte Anfragen über den lokalen opencodex-Proxy, neueste zuerst.", "logs.autoRefresh": "Auto-Aktualisierung", "logs.noRequests": "Noch keine Anfragen.", "logs.loadError": "Anfrageprotokolle konnten nicht geladen werden.", @@ -771,7 +790,6 @@ export const de: Record = { "debug.noLines.usage": "Nutzungserfassung ist an, aber es wurde noch nichts erfasst. Sende einen Chat/eine Anfrage über Codex, dann erscheint es hier.", "debug.noLines.injection": "Injektions-Log ist an, aber es wurde noch nichts erfasst. Es erfasst Multi-Agent-Guidance-Injektion und Effort-Cap-Entscheidungen bei Collab- und Sub-Agent-Turns.", "usage.title": "Nutzung", - "usage.subtitleAria": "So wird die Nutzung gezählt", "usage.subtitle": "Lokale Token-Buchhaltung deines Proxys. Fehlende Nutzung wird nie als Null angezeigt.", "usage.loading": "Lade Nutzungsdaten…", "usage.empty": "Noch keine Nutzung erfasst. Sende eine Anfrage über den Proxy, um Aktivität hier zu sehen.", @@ -790,6 +808,7 @@ export const de: Record = { "usage.card.cachedTokensHint": "Prompt-Tokens aus dem Provider-Cache (Treffer). Cache-Schreibvorgänge werden darunter separat angezeigt.", "usage.card.cacheWriteTokens": "Cache-Schreiben", "usage.card.coverage": "Abdeckung", + "usage.card.activeDays": "Aktive Tage", "usage.section.heatmap": "Tägliche Aktivität", "usage.section.overview": "Übersicht", "usage.section.models": "Modelle", @@ -986,6 +1005,7 @@ export const de: Record = { "integrations.moreClients": "Weitere Clients ({count})", "integrations.fewerClients": "Nicht installierte Clients ausblenden", "integrations.notInstalled": "Nicht installiert ({count})", + "integrations.subtitle": "Clients mit opencodex verbinden, Zugangsdaten verwalten und Client-Konfigurationen wiederherstellen.", "integrations.tabsLabel": "Integrationsbereiche", "integrations.tab.overview": "Übersicht", "integrations.tab.keys": "API-Schlüssel", @@ -1059,6 +1079,7 @@ export const de: Record = { "integrations.summary.detected": "Clients erkannt", "integrations.summary.applied": "Konfigurierte Clients", "integrations.summary.stale": "Aktualisierung erforderlich", + "integrations.summary.lastChange": "Letzte Änderung", "integrations.summary.disableAll": "Alle deaktivieren…", "integrations.onboarding": "Beim Anwenden wird nach dem Speichern einer Sicherung genau ein opencodex-Anbieterblock geschrieben. Beim Deaktivieren wird nur dieser Block entfernt; eine aufbewahrte Momentaufnahme kann wiederhergestellt werden.", "integrations.empty.title": "Keine installierten Clients erkannt", @@ -1099,6 +1120,7 @@ export const de: Record = { "integrations.status.appliedAt": "Angewendet", "integrations.status.backup": "Sicherung", "integrations.status.lastRestore": "Letzte Wiederherstellung", + "integrations.status.unknown": "Unbekannt", "integrations.bulk.title": "Angewendete Client-Integrationen deaktivieren?", "integrations.bulk.body": "Nur der opencodex-eigene Block wird entfernt. Für jeden Client wird vorher eine Momentaufnahme gespeichert.", "integrations.bulk.partial": "Einige Clients konnten nicht deaktiviert werden: {clients}", @@ -1955,6 +1977,7 @@ export const de: Record = { "pws.editJsonDesc": "Rohe Proxy-Konfiguration als JSON bearbeiten", "pws.updatesUnavailable": "Anbieter-Updates sind nicht verfügbar.", "pws.dashboard.title": "Anbieterübersicht", + "pws.dashboard.subtitle": "Verwalten Sie alle Ihre Modellanbieter an einem Ort.", "pws.dashboard.rateLimits": "RATE LIMITS", "pws.capacity.estimate": "Pool-Schätzung anhand konfigurierter Gewichtungen", "pws.capacity.currentAccount": "Aktuelles effektives Konto", @@ -2245,6 +2268,7 @@ export const de: Record = { "claudeDesktop.health.stats": "{count} Anf. / {errors} Fehl.", "claudeDesktop.effort.supported": "effort", "claudeDesktop.effort.displayOnly": "effort (nur Anzeige)", + "dash.injectionManage": "Einstellungen öffnen", "sub.settings": "Einstellungen", "sub.sections": "Subagent-Abschnitte", "sub.delegation.model": "Zuerst aufgerufenes Modell", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 917aba8e2b..177c717afb 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -26,7 +26,6 @@ export const en = { "sidebar.starFailed": "Could not star through gh. Opening GitHub instead.", "sidebar.updateAvailable": "Update available: {version}", "sidebar.checkUpdate": "Check for updates", - "sidebar.preferences": "Preferences", "common.save": "Save", "common.saving": "Saving…", "common.cancel": "Cancel", @@ -134,6 +133,7 @@ export const en = { "startup.title": "Startup safety", "startup.subtitle": "Verify that Codex can reach opencodex after a restart, before local proxy routing becomes a reconnect loop.", "startup.refresh": "Refresh", + "startup.backToDashboard": "Back to Dashboard", "startup.loading": "Checking startup protection…", "startup.error": "Could not read startup protection.", "startup.staleData": "The latest startup check failed. The values below are stale and must not be treated as proof of protection.", @@ -147,11 +147,14 @@ export const en = { "startup.riskDetailCustomLocal": "Codex points to a custom local gateway. opencodex cannot manage or verify that gateway's restart lifecycle.", "startup.riskDetailWindowsShim": "The launcher shim protects supported CLI scripts, but Codex Desktop and direct codex.exe launches can bypass it on Windows.", "startup.safeDetail": "The current routing and startup mechanism are consistent. No manual ocx start should be required after restart.", + "startup.routing": "Codex routing", "startup.routing.proxy": "Local proxy", "startup.routing.native": "Native OpenAI", "startup.routing.customLocal": "Custom local gateway", "startup.routing.customRemote": "Custom remote gateway", "startup.routing.unknown": "Unknown or invalid routing", + "startup.restartProtection": "Restart protection", + "startup.preference": "On-demand startup", "startup.enabled": "Enabled", "startup.disabled": "Disabled", "startup.protection.service": "Background service", @@ -210,9 +213,14 @@ export const en = { "startup.codexRuntime.olderBinary": "OpenCodex is using an older Codex binary ({version}). A newer installation is available.", // dashboard + "dash.subtitle": "Live status of the local opencodex proxy, its providers, and the models routed into Codex.", + "dash.workspace.overview": "Overview", + "dash.workspace.sections": "Sections", "dash.status": "Status", "dash.online": "Online", "dash.offline": "Offline", + "dash.version": "Version", + "dash.uptime": "Uptime", "dash.providers": "Providers", "dash.tokens30d": "Tokens (30d)", "dash.coverage": "{pct} coverage", @@ -250,11 +258,22 @@ export const en = { "dash.mem.reconnecting": "Proxy restarting… waiting to reconnect", "dash.mem.restartFailed": "Drain & restart failed. Check that the proxy is running.", "dash.mem.restartNoSupervisor": "No restart protection detected. The proxy may stay down after restart unless you start it again.", + "dash.activeProviders": "Active providers", + "dash.noProviders": "No providers configured. Run {cmd}.", + "dash.col.name": "Name", + "dash.col.adapter": "Adapter", + "dash.col.baseUrl": "Base URL", + "dash.col.model": "Model", + "dash.modelsNoResults": "No models match your search.", + "dash.availableModels": "Available models", + "dash.noModels": "No models found. Check provider API keys.", "dash.cannotConnect": "Cannot connect to proxy. Is it running?", "dash.runStart": "Run {cmd} to start the proxy.", "dash.stop": "Stop Proxy", "dash.stopConfirm": "Stop the proxy and restore native Codex?", "dash.stopFailed": "Failed to stop proxy (HTTP {status}).", + "dash.maSwitchFailed": "Mode switch failed (HTTP {status}).", + "dash.maNetworkError": "Network error — is the proxy running?", "dash.stopping": "Stopping…", "dash.actions": "Proxy", "dash.codexRestart": "Reload Codex models", @@ -276,7 +295,6 @@ export const en = { "dash.searchReasoning": "Search reasoning effort", "dash.visionModel": "Vision sidecar model", "dash.visionModelHint": "Model used to describe images for text-only routed models. Requires ChatGPT login.", - "dash.sidecars": "Sidecars", "dash.webSearchSidecar": "Web search sidecar", "dash.webSearchSidecarHint": "Choose the backend and model used for web search on routed models.", "dash.webSearchStream": "Stream answers live", @@ -290,9 +308,12 @@ export const en = { "dash.visionTimeout": "Timeout", "dash.visionTimeoutInvalid": "Enter an integer from {min} to {max} milliseconds.", "dash.visionAdvancedPopover": "Advanced vision settings", + "dash.shadowCallIntercept": "Shadow Call Intercept", "dash.shadowCallInterceptHint": "Intercepts Codex App's background helper calls ({models}) for title generation and commit messages and redirects them to your chosen model.", "dash.shadowCallWarning": "⚠ When enabled, ALL requests for {models} will be replaced with the selected model.", "dash.shadowCallOriginal": "Original", + "dash.shadowCallModel": "Replacement model", + "dash.shadowCallTooltip": "Codex App makes background helper calls for thread title generation, commit message generation, and skill orchestration. The helper model changed across client versions, so opencodex intercepts every model in this set: {models}. Enable this to redirect those calls to your chosen model.", "models.shadowCallIntercept": "Shadow Call Intercept", "models.shadowCallInterceptHint": "Intercepts Codex App's background helper calls ({models}) for titles and commit messages and redirects them to your chosen model.", "dash.sidecarBackend": "Backend", @@ -304,6 +325,7 @@ export const en = { "dash.sidecarSaveFailed": "Failed to save sidecar settings.", "dash.injectionLabel": "Sub-agent delegation", "dash.injectionHint": "Pick the model Codex should hand sub-agent work to. The two switches below decide where that pick is used.", + "dash.injectionManage": "Open settings", "dash.syncCodexSubagentDefaults": "Also save as a Codex default", "dash.syncCodexSubagentDefaultsHint": "On, the pick above is written into Codex's own config, so new tasks start with that model too. Off, it is remembered only here. It takes effect on the next sync or restart, and your hand-written [agents] settings are left alone.", "dash.multiAgentGuidance": "Tell Codex how to split work", @@ -486,10 +508,6 @@ export const en = { "models.subtitle.combos": "Ordered groups of models that answer as one id. Chain targets with failover or spread the load with a balancing strategy.", "models.subtitle.compatibility": "Read-only compatibility verdict matrix from lab projection evidence.", "models.subtitle.routing": "Policy profiles, dry-run evaluation, and source-backed routing analytics.", - "models.advanced": "Advanced", - "models.groupMore": "Show more provider actions", - "models.subtitleAria": "How the catalog works", - "models.orderHintAria": "How picker order is decided", "models.subtitle": "Toggle which models Codex sees — native GPT passthrough and routed providers, grouped by provider (click a header to collapse). Hidden models stay off the catalog + model picker but remain directly callable by exact id. Changes apply on the next Codex turn — opencodex invalidates Codex's 5-min model cache so no restart is needed.", "models.nativeGroupLabel": "OpenAI native", "models.nativeHint": "Passthrough models use the Pool or Direct account option selected on Providers. Toggling one off hides it from the Codex picker (the catalog entry is kept, so re-enabling restores it exactly). Adding a model here registers a routed `openai/` selector, not a new bare passthrough id.", @@ -526,6 +544,7 @@ export const en = { "models.keepNativeOnV1": "Keep ChatGPT on v1", "models.keepNativeOnV1Hint": "ChatGPT encrypts v2 child tasks only when a ChatGPT-native parent stays on v2, so Grok and Claude cannot read them. Turn this on to keep Sol/Terra on v1 and avoid that encryption. Routed parents keep v2.", "models.v2Help": "Controls the multi-agent surface for all models.\n\nv1: Classic single-thread agent. Every model uses the v1 collab surface.\nbase: Upstream defaults — sol/terra use v2, luna uses v1, others follow the codex feature flag.\nv2: Multi-thread agent with spawn_agent. Every model uses the v2 collab surface.\n\nOn v2, Keep ChatGPT on v1 leaves Sol/Terra on the v1 surface so they can still spawn Grok or Claude. ChatGPT encrypts v2 child tasks; routed models cannot read them. Routed parents stay on v2.\n\nChanges apply to new sessions.", + "dash.multiAgent": "Sub-agent", "models.v2Conflict": "[agents] max_threads is set — codex will refuse to start; remove it from config.toml", "models.v2Applied": "Sub-agent mode updated — applies to new sessions (restart the Codex app to refresh the picker)", "models.v2ThreadsLabel": "Max threads", @@ -658,6 +677,7 @@ export const en = { "logs.title": "Request Logs", "logs.tabLogs": "Logs", "logs.tabDebug": "Debug", + "logs.subtitle": "Recent requests routed through the local opencodex proxy, newest first.", "logs.autoRefresh": "Auto-refresh", "logs.noRequests": "No requests yet.", "logs.loadError": "Could not load request logs.", @@ -807,7 +827,6 @@ export const en = { // usage page "usage.title": "Usage", - "usage.subtitleAria": "How usage is counted", "usage.subtitle": "Local token accounting from your proxy. Missing usage is never shown as zero.", "usage.loading": "Loading usage data…", "usage.empty": "No usage recorded yet. Send a request through the proxy to see activity here.", @@ -826,6 +845,7 @@ export const en = { "usage.card.cachedTokensHint": "Prompt tokens served from the provider cache (reads). Cache writes are shown below when present.", "usage.card.cacheWriteTokens": "cache writes", "usage.card.coverage": "Coverage", + "usage.card.activeDays": "Active days", "usage.section.heatmap": "Daily activity", "usage.section.overview": "Overview", "usage.section.models": "Models", @@ -1261,6 +1281,7 @@ export const en = { "pws.editJsonDesc": "Edit the raw proxy config as JSON", "pws.updatesUnavailable": "Provider updates are not available.", "pws.dashboard.title": "Providers overview", + "pws.dashboard.subtitle": "Manage all your model providers in one place.", "pws.dashboard.rateLimits": "RATE LIMITS", "pws.capacity.estimate": "Configured-weight pool estimate", "pws.capacity.currentAccount": "Current effective account", @@ -1485,6 +1506,7 @@ export const en = { "integrations.moreClients": "More clients ({count})", "integrations.fewerClients": "Hide uninstalled clients", "integrations.notInstalled": "Not installed ({count})", + "integrations.subtitle": "Connect clients to opencodex, manage credentials, and restore client configuration.", "integrations.tabsLabel": "Integration surfaces", "integrations.tab.overview": "Overview", "integrations.tab.keys": "API Keys", @@ -1599,6 +1621,7 @@ export const en = { "integrations.summary.detected": "Clients detected", "integrations.summary.applied": "Configured clients", "integrations.summary.stale": "Update needed", + "integrations.summary.lastChange": "Last change", "integrations.summary.disableAll": "Disable all…", "integrations.onboarding": "Applying writes one opencodex provider block after saving a backup. Disable removes only that block, and a retained snapshot can be restored.", "integrations.empty.title": "No installed clients were detected", @@ -1639,6 +1662,7 @@ export const en = { "integrations.status.appliedAt": "Applied", "integrations.status.backup": "Backup", "integrations.status.lastRestore": "Last restore", + "integrations.status.unknown": "Unknown", "integrations.bulk.title": "Disable applied client integrations?", "integrations.bulk.body": "Only the opencodex-owned block is removed. A pre-write snapshot is kept for each client.", "integrations.bulk.partial": "Some clients could not be disabled: {clients}", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index c7ad8b6ee5..4859657d65 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -23,7 +23,6 @@ export const fr: Record = { "sidebar.starFailed": "Impossible d’ajouter une étoile avec gh. Ouverture de GitHub à la place.", "sidebar.updateAvailable": "Mise à jour disponible : {version}", "sidebar.checkUpdate": "Rechercher des mises à jour", - "sidebar.preferences": "Préférences", "common.save": "Enregistrer", "common.saving": "Enregistrement…", "common.cancel": "Annuler", @@ -127,6 +126,7 @@ export const fr: Record = { "startup.title": "Sécurité du démarrage", "startup.subtitle": "Vérifiez que Codex peut joindre opencodex après un redémarrage, avant que le routage du proxy local n’entre dans une boucle de reconnexion.", "startup.refresh": "Actualiser", + "startup.backToDashboard": "Retour au tableau de bord", "startup.loading": "Vérification de la protection au démarrage…", "startup.error": "Impossible de lire la protection au démarrage.", "startup.staleData": "La dernière vérification du démarrage a échoué. Les valeurs ci-dessous sont obsolètes et ne doivent pas être considérées comme une preuve de protection.", @@ -140,11 +140,14 @@ export const fr: Record = { "startup.riskDetailCustomLocal": "Codex pointe vers une passerelle locale personnalisée. opencodex ne peut ni gérer ni vérifier le cycle de redémarrage de cette passerelle.", "startup.riskDetailWindowsShim": "Le mécanisme de lancement protège les scripts CLI pris en charge, mais Codex Desktop et les lancements directs de codex.exe peuvent le contourner sous Windows.", "startup.safeDetail": "Le routage et le mécanisme de démarrage actuels sont cohérents. Aucun démarrage manuel avec ocx ne devrait être nécessaire après un redémarrage.", + "startup.routing": "Routage de Codex", "startup.routing.proxy": "Proxy local", "startup.routing.native": "OpenAI natif", "startup.routing.customLocal": "Passerelle locale personnalisée", "startup.routing.customRemote": "Passerelle distante personnalisée", "startup.routing.unknown": "Routage inconnu ou non valide", + "startup.restartProtection": "Protection au redémarrage", + "startup.preference": "Démarrage à la demande", "startup.enabled": "Activé", "startup.disabled": "Désactivé", "startup.protection.service": "Service en arrière-plan", @@ -201,9 +204,14 @@ export const fr: Record = { "startup.codexRuntime.clampHidden": "Certaines options d’effort de raisonnement ont été masquées, car OpenCodex utilisait Codex {version}.", "startup.codexRuntime.clampHiddenWithEfforts": "Certaines options d’effort de raisonnement ont été masquées, car OpenCodex utilisait Codex {version} (supprimées : {efforts}).", "startup.codexRuntime.olderBinary": "OpenCodex utilise un binaire Codex plus ancien ({version}). Une installation plus récente est disponible.", + "dash.subtitle": "État en direct du proxy opencodex local, de ses fournisseurs et des modèles routés vers Codex.", + "dash.workspace.overview": "Vue d’ensemble", + "dash.workspace.sections": "Sections", "dash.status": "État", "dash.online": "En ligne", "dash.offline": "Hors ligne", + "dash.version": "Version", + "dash.uptime": "Durée de fonctionnement", "dash.providers": "Fournisseurs", "dash.tokens30d": "Jetons (30 j)", "dash.coverage": "Couverture : {pct}", @@ -240,11 +248,22 @@ export const fr: Record = { "dash.mem.reconnecting": "Redémarrage du proxy… attente de la reconnexion", "dash.mem.restartFailed": "Échec du drainage et du redémarrage. Vérifiez que le proxy est en cours d’exécution.", "dash.mem.restartNoSupervisor": "Aucune protection au redémarrage détectée. Le proxy peut rester arrêté après le redémarrage, sauf si vous le relancez.", + "dash.activeProviders": "Fournisseurs actifs", + "dash.noProviders": "Aucun fournisseur configuré. Exécutez {cmd}.", + "dash.col.name": "Nom", + "dash.col.adapter": "Adaptateur", + "dash.col.baseUrl": "URL de base", + "dash.col.model": "Modèle", + "dash.modelsNoResults": "Aucun modèle ne correspond à votre recherche.", + "dash.availableModels": "Modèles disponibles", + "dash.noModels": "Aucun modèle trouvé. Vérifiez les clés API des fournisseurs.", "dash.cannotConnect": "Impossible de se connecter au proxy. Est-il en cours d’exécution ?", "dash.runStart": "Exécutez {cmd} pour démarrer le proxy.", "dash.stop": "Arrêter le proxy", "dash.stopConfirm": "Arrêter le proxy et restaurer Codex natif ?", "dash.stopFailed": "Échec de l’arrêt du proxy (HTTP {status}).", + "dash.maSwitchFailed": "Échec du changement de mode (HTTP {status}).", + "dash.maNetworkError": "Erreur réseau — le proxy est-il en cours d’exécution ?", "dash.stopping": "Arrêt…", "dash.actions": "Proxy", "dash.codexRestart": "Recharger les modèles Codex", @@ -266,7 +285,6 @@ export const fr: Record = { "dash.searchReasoning": "Effort de raisonnement pour la recherche", "dash.visionModel": "Modèle auxiliaire de vision", "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.sidecars": "Sidecars", "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.webSearchStream": "Diffuser les réponses en direct", @@ -280,9 +298,12 @@ export const fr: Record = { "dash.visionTimeout": "Délai d’expiration", "dash.visionTimeoutInvalid": "Saisissez un entier compris entre {min} et {max} millisecondes.", "dash.visionAdvancedPopover": "Paramètres de vision avancés", + "dash.shadowCallIntercept": "Interception des appels fantômes", "dash.shadowCallInterceptHint": "Intercepte les appels auxiliaires en arrière-plan de l’application Codex ({models}) pour générer les titres et les messages de commit, puis les redirige vers le modèle choisi.", "dash.shadowCallWarning": "⚠ Lorsque cette option est activée, TOUTES les requêtes destinées à {models} sont remplacées par le modèle sélectionné.", "dash.shadowCallOriginal": "Original", + "dash.shadowCallModel": "Modèle de remplacement", + "dash.shadowCallTooltip": "L’application Codex effectue des appels auxiliaires en arrière-plan pour générer les titres de fils, les messages de commit et orchestrer les compétences. Le modèle auxiliaire ayant changé selon les versions du client, opencodex intercepte tous les modèles de cet ensemble : {models}. Activez cette option pour rediriger ces appels vers le modèle choisi.", "models.shadowCallIntercept": "Interception des appels fantômes", "models.shadowCallInterceptHint": "Intercepte les appels auxiliaires en arrière-plan de l’application Codex ({models}) pour les titres et les messages de commit, puis les redirige vers le modèle choisi.", "dash.sidecarBackend": "Moteur", @@ -294,6 +315,7 @@ export const fr: Record = { "dash.sidecarSaveFailed": "Échec de l’enregistrement des paramètres des services auxiliaires.", "dash.injectionLabel": "Délégation aux sous-agents", "dash.injectionHint": "Choisissez le modèle auquel Codex doit confier le travail des sous-agents. Les deux options ci-dessous déterminent où ce choix est utilisé.", + "dash.injectionManage": "Ouvrir les paramètres", "dash.syncCodexSubagentDefaults": "Enregistrer aussi comme valeur par défaut de Codex", "dash.syncCodexSubagentDefaultsHint": "Si cette option est activée, le choix ci-dessus est inscrit dans la configuration de Codex afin que les nouvelles tâches commencent aussi avec ce modèle. Si elle est désactivée, il n’est mémorisé qu’ici. Il prend effet à la prochaine synchronisation ou au prochain redémarrage, sans modifier vos paramètres [agents] saisis manuellement.", "dash.multiAgentGuidance": "Indiquer à Codex comment répartir le travail", @@ -471,10 +493,6 @@ export const fr: Record = { "models.subtitle.combos": "Groupes ordonnés de modèles qui répondent sous un même identifiant. Enchaînez les cibles avec le repli ou répartissez la charge avec une stratégie d’équilibrage.", "models.subtitle.compatibility": "Matrice en lecture seule des verdicts de compatibilité issus des preuves de projection du laboratoire.", "models.subtitle.routing": "Profils de stratégie, évaluation à blanc et analyses de routage fondées sur les sources.", - "models.advanced": "Avancé", - "models.groupMore": "Afficher plus d’actions du fournisseur", - "models.subtitleAria": "Fonctionnement du catalogue", - "models.orderHintAria": "Comment l’ordre du sélecteur est décidé", "models.subtitle": "Choisissez les modèles visibles par Codex — accès direct aux GPT natifs et fournisseurs routés, regroupés par fournisseur (cliquez sur un en-tête pour le réduire). Les modèles masqués sont retirés du catalogue et du sélecteur, mais restent directement accessibles par leur identifiant exact. Les modifications s’appliquent au prochain tour Codex — opencodex invalide le cache de modèles de Codex de 5 min, sans nécessiter de redémarrage.", "models.nativeGroupLabel": "OpenAI natif", "models.nativeHint": "Les modèles en accès direct utilisent l’option de compte Groupe ou Direct sélectionnée dans Fournisseurs. La désactivation d’un modèle le masque dans le sélecteur Codex (son entrée de catalogue est conservée afin que sa réactivation la restaure à l’identique). Ajouter un modèle ici enregistre un sélecteur routé `openai/`, et non un nouvel identifiant passthrough brut.", @@ -511,6 +529,7 @@ export const fr: Record = { "models.keepNativeOnV1": "Garder ChatGPT sur v1", "models.keepNativeOnV1Hint": "ChatGPT chiffre les tâches enfants v2 uniquement lorsqu’un parent natif ChatGPT reste sur v2, de sorte que Grok et Claude ne peuvent pas les lire. Activez cette option pour garder Sol/Terra sur v1 et éviter ce chiffrement. Les parents routés restent sur v2.", "models.v2Help": "Contrôle l’interface multi-agent pour tous les modèles.\n\nv1 : agent classique à fil unique. Tous les modèles utilisent l’interface collab v1.\nbase : valeurs par défaut en amont — sol/terra utilisent v2, luna utilise v1 et les autres suivent l’indicateur de fonctionnalité codex.\nv2 : agent multifil avec spawn_agent. Tous les modèles utilisent l’interface collab v2.\n\nEn v2, « Garder ChatGPT sur v1 » laisse Sol/Terra sur l’interface v1 afin qu’ils puissent encore lancer Grok ou Claude. ChatGPT chiffre les tâches enfants v2 ; les modèles routés ne peuvent pas les lire. Les parents routés restent sur v2.\n\nLes modifications s’appliquent aux nouvelles sessions.", + "dash.multiAgent": "Sous-agent", "models.v2Conflict": "[agents] max_threads est défini — codex refusera de démarrer ; supprimez-le de config.toml", "models.v2Applied": "Mode sous-agent mis à jour — s’applique aux nouvelles sessions (redémarrez l’application Codex pour actualiser le sélecteur)", "models.v2ThreadsLabel": "Nombre maximal de fils", @@ -639,6 +658,7 @@ export const fr: Record = { "logs.title": "Journaux des requêtes", "logs.tabLogs": "Journaux", "logs.tabDebug": "Débogage", + "logs.subtitle": "Requêtes récentes routées par le proxy opencodex local, de la plus récente à la plus ancienne.", "logs.autoRefresh": "Actualisation automatique", "logs.noRequests": "Aucune requête pour le moment.", "logs.loadError": "Impossible de charger les journaux des requêtes.", @@ -784,7 +804,6 @@ export const fr: Record = { "debug.noLines.usage": "L’extraction de l’utilisation est activée, mais rien n’a encore été capturé. Envoyez une conversation ou une requête par Codex pour qu’elle apparaisse ici.", "debug.noLines.injection": "Le journal des injections est activé, mais rien n’a encore été capturé. Il consigne l’injection des directives multi-agents et les décisions de plafonnement du niveau lors des tours collab et des sous-agents.", "usage.title": "Utilisation", - "usage.subtitleAria": "Comment l’utilisation est comptée", "usage.subtitle": "Comptabilisation locale des jetons par votre proxy. Une utilisation manquante n’est jamais affichée comme nulle.", "usage.loading": "Chargement des données d’utilisation…", "usage.empty": "Aucune utilisation enregistrée pour le moment. Envoyez une requête par le proxy pour voir l’activité ici.", @@ -803,6 +822,7 @@ export const fr: Record = { "usage.card.cachedTokensHint": "Jetons de prompt servis depuis le cache du fournisseur (lectures). Les écritures dans le cache sont indiquées ci-dessous lorsqu’elles sont présentes.", "usage.card.cacheWriteTokens": "écritures dans le cache", "usage.card.coverage": "Couverture", + "usage.card.activeDays": "Jours actifs", "usage.section.heatmap": "Activité quotidienne", "usage.section.overview": "Vue d’ensemble", "usage.section.models": "Modèles", @@ -1234,6 +1254,7 @@ export const fr: Record = { "pws.editJsonDesc": "Modifier la configuration brute du proxy au format JSON", "pws.updatesUnavailable": "Les mises à jour du fournisseur ne sont pas disponibles.", "pws.dashboard.title": "Vue d’ensemble des fournisseurs", + "pws.dashboard.subtitle": "Gérez tous vos fournisseurs de modèles au même endroit.", "pws.dashboard.rateLimits": "LIMITES DE DÉBIT", "pws.capacity.estimate": "Estimation du groupe pondérée selon la configuration", "pws.capacity.currentAccount": "Compte effectif actuel", @@ -1458,6 +1479,7 @@ export const fr: Record = { "integrations.moreClients": "Autres clients ({count})", "integrations.fewerClients": "Masquer les clients non installés", "integrations.notInstalled": "Non installés ({count})", + "integrations.subtitle": "Connectez des clients à opencodex, gérez les identifiants et restaurez la configuration des clients.", "integrations.tabsLabel": "Surfaces d’intégration", "integrations.tab.overview": "Vue d’ensemble", "integrations.tab.keys": "Clés API", @@ -1531,6 +1553,7 @@ export const fr: Record = { "integrations.summary.detected": "Clients détectés", "integrations.summary.applied": "Clients configurés", "integrations.summary.stale": "Mise à jour requise", + "integrations.summary.lastChange": "Dernière modification", "integrations.summary.disableAll": "Tout désactiver…", "integrations.onboarding": "L’application écrit un bloc de fournisseur opencodex après avoir créé une sauvegarde. La désactivation supprime uniquement ce bloc, et un instantané conservé peut être restauré.", "integrations.empty.title": "Aucun client installé n’a été détecté", @@ -1571,6 +1594,7 @@ export const fr: Record = { "integrations.status.appliedAt": "Appliqué", "integrations.status.backup": "Sauvegarde", "integrations.status.lastRestore": "Dernière restauration", + "integrations.status.unknown": "Inconnu", "integrations.bulk.title": "Désactiver les intégrations client appliquées ?", "integrations.bulk.body": "Seul le bloc appartenant à opencodex est supprimé. Un instantané antérieur est conservé pour chaque client.", "integrations.bulk.partial": "Certains clients n’ont pas pu être désactivés : {clients}", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index c26bfc7902..522b03e3a1 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -93,7 +93,6 @@ export const ja: Record = { "sidebar.starFailed": "gh でスターを付けられませんでした。代わりに GitHub を開きます。", "sidebar.updateAvailable": "更新あり: {version}", "sidebar.checkUpdate": "更新を確認", - "sidebar.preferences": "環境設定", "common.save": "保存", "common.saving": "保存中…", "common.cancel": "キャンセル", @@ -132,6 +131,7 @@ export const ja: Record = { "startup.title": "起動安全性", "startup.subtitle": "再起動後にローカルプロキシへの接続が再接続ループになる前に、Codex が opencodex へ到達できるか確認します。", "startup.refresh": "更新", + "startup.backToDashboard": "ダッシュボードに戻る", "startup.loading": "起動保護を確認中…", "startup.error": "起動保護を読み取れませんでした。", "startup.staleData": "最新の確認に失敗しました。以下は古い値であり、保護の証明にはなりません。", @@ -145,11 +145,14 @@ export const ja: Record = { "startup.riskDetailCustomLocal": "Codex はカスタムローカルゲートウェイを参照しています。opencodex はその再起動ライフサイクルを管理・検証できません。", "startup.riskDetailWindowsShim": "Launcher shim は対応する CLI スクリプトのみを保護し、Windows の Codex Desktop と codex.exe の直接起動はこれを迂回できます。", "startup.safeDetail": "現在のルーティングと起動方式は整合しています。再起動後に ocx start を手動実行する必要はありません。", + "startup.routing": "Codex ルーティング", "startup.routing.proxy": "ローカルプロキシ", "startup.routing.native": "OpenAI ネイティブ", "startup.routing.customLocal": "カスタムローカルゲートウェイ", "startup.routing.customRemote": "カスタム遠隔ゲートウェイ", "startup.routing.unknown": "不明または無効なルーティング", + "startup.restartProtection": "再起動保護", + "startup.preference": "オンデマンド起動", "startup.enabled": "有効", "startup.disabled": "無効", "startup.protection.service": "バックグラウンドサービス", @@ -208,9 +211,14 @@ export const ja: Record = { "startup.codexRuntime.olderBinary": "OpenCodex は古い Codex バイナリ({version})を使用しています。より新しいインストールが利用可能です。", // dashboard + "dash.subtitle": "ローカル opencodex プロキシ、そのプロバイダー、Codex にルーティングされるモデルのライブ状態です。", + "dash.workspace.overview": "概要", + "dash.workspace.sections": "セクション", "dash.status": "状態", "dash.online": "オンライン", "dash.offline": "オフライン", + "dash.version": "バージョン", + "dash.uptime": "稼働時間", "dash.providers": "プロバイダー", "dash.tokens30d": "トークン (30日)", "dash.coverage": "{pct} カバレッジ", @@ -247,11 +255,22 @@ export const ja: Record = { "dash.mem.reconnecting": "プロキシを再起動中… 再接続を待機", "dash.mem.restartFailed": "完了後の再起動に失敗しました。プロキシが起動しているか確認してください。", "dash.mem.restartNoSupervisor": "再起動保護がありません。再起動後、プロキシが自動で戻らない可能性があります。", + "dash.activeProviders": "アクティブなプロバイダー", + "dash.noProviders": "プロバイダーが設定されていません。{cmd} を実行してください。", + "dash.col.name": "名前", + "dash.col.adapter": "アダプター", + "dash.col.baseUrl": "ベース URL", + "dash.col.model": "モデル", + "dash.modelsNoResults": "検索に一致するモデルはありません。", + "dash.availableModels": "利用可能なモデル", + "dash.noModels": "モデルが見つかりません。プロバイダーの API キーを確認してください。", "dash.cannotConnect": "プロキシに接続できません。起動していますか?", "dash.runStart": "{cmd} を実行してプロキシを起動してください。", "dash.stop": "プロキシを停止", "dash.stopConfirm": "プロキシを停止してネイティブの Codex に戻しますか?", "dash.stopFailed": "プロキシを停止できませんでした (HTTP {status})。", + "dash.maSwitchFailed": "モードの切り替えに失敗しました (HTTP {status})。", + "dash.maNetworkError": "ネットワークエラー — プロキシは起動していますか?", "dash.stopping": "停止中…", "dash.actions": "プロキシ", "dash.codexRestart": "Codex のモデル一覧を再読み込み", @@ -273,7 +292,6 @@ export const ja: Record = { "dash.searchReasoning": "検索の推論負荷", "dash.visionModel": "ビジョンサイドカーモデル", "dash.visionModelHint": "テキスト専用ルーティングモデルで画像を説明するために使うモデル。ChatGPT ログインが必要です。", - "dash.sidecars": "サイドカー", "dash.webSearchSidecar": "ウェブ検索サイドカー", "dash.webSearchSidecarHint": "ルーティングモデルでウェブ検索に使うバックエンドとモデルを選択します。", "dash.webSearchStream": "回答をライブ配信", @@ -281,9 +299,12 @@ export const ja: Record = { "dash.visionSidecar": "ビジョンサイドカー", "dash.visionSidecarHint": "テキスト専用ルーティングモデルで画像を説明するために使うバックエンドとモデルを選択します。", "dash.visionOff": "オフ", + "dash.shadowCallIntercept": "シャドウコール傍受", "dash.shadowCallInterceptHint": "Codex App のバックグラウンドヘルパー呼び出し({models}: タイトル生成、コミットメッセージ)を傍受し、選択したモデルにリダイレクトします。", "dash.shadowCallWarning": "⚠ オンにすると、{models} へのリクエストがすべて選択したモデルに置き換えられます。", "dash.shadowCallOriginal": "元のモデル", + "dash.shadowCallModel": "差し替えモデル", + "dash.shadowCallTooltip": "Codex App はスレッドタイトル生成、コミットメッセージ生成、スキルオーケストレーションをバックグラウンドで呼び出します。使われるモデルはクライアントのバージョンによって変わるため、opencodex は {models} をまとめて傍受します。これをオンにすると、それらの呼び出しを選択したモデルにリダイレクトします。", "models.shadowCallIntercept": "シャドウコール傍受", "models.shadowCallInterceptHint": "Codex App のバックグラウンドヘルパー呼び出し({models}: タイトル、コミットメッセージ)を傍受し、選択したモデルにリダイレクトします。", "dash.sidecarBackend": "バックエンド", @@ -470,10 +491,6 @@ export const ja: Record = { "models.subtitle.combos": "複数のモデルを 1 つの id にまとめ、順に応答させます。failover でターゲットを連鎖させるか、分散戦略で負荷を分散します。", "models.subtitle.compatibility": "ラボ投影証拠の読み取り専用互換性判定マトリクス。", "models.subtitle.routing": "ポリシープロファイル、dry-run 評価、そして根拠の残るルーティング分析です。", - "models.advanced": "詳細設定", - "models.groupMore": "プロバイダーのその他の操作を表示", - "models.subtitleAria": "カタログの仕組み", - "models.orderHintAria": "ピッカー順序の決まり方", "models.subtitle": "Codex に表示するモデルを切り替えます — ネイティブ GPT パススルーとルーティングプロバイダー、プロバイダー別(ヘッダーをクリックで折りたたみ)。非表示モデルはカタログとピッカーから外れますが、正確な id での直接呼び出しは可能です。変更は次回の Codex ターンで適用 — opencodex は Codex の 5 分間モデルキャッシュを無効化するので再起動は不要です。", "models.nativeGroupLabel": "OpenAI ネイティブ", "models.nativeHint": "パススルーモデルはプロバイダーで選択したプールまたはダイレクトアカウントオプションを使用します。一つオフにすると Codex ピッカーから隠します(カタログエントリは保持されるので、再有効化で正確に復元されます)。 ここでモデルを追加すると、bare passthrough id ではなくルーティングされた `openai/` セレクタとして登録されます。", @@ -510,6 +527,7 @@ export const ja: Record = { "models.keepNativeOnV1": "ChatGPT を v1 のまま", "models.keepNativeOnV1Hint": "ChatGPT ネイティブの親は v2 子タスクを暗号化するため、Grok や Claude は読めません。Sol/Terra から routed モデルを spawn するならオンのまま。routed 親は v2 のままです。", "models.v2Help": "すべてのモデルのマルチエージェントサーフェスを制御します。\n\nv1: クラシックな単一スレッドエージェント。すべてのモデルが v1 コラボサーフェスを使います。\nベース: 上流のデフォルト — sol/terra は v2、luna は v1、それ以外は codex のフィーチャーフラグに従います。\nv2: spawn_agent を備えたマルチスレッドエージェント。すべてのモデルが v2 コラボサーフェスを使います。\n\nv2 では「ChatGPT を v1 のまま」にすると Sol/Terra が v1 に留まり、Grok や Claude を spawn できます。ChatGPT は v2 子タスクを暗号化するため routed モデルは読めません。routed 親は v2 のままです。\n\n変更は新規セッションに適用されます。", + "dash.multiAgent": "サブエージェント", "models.v2Conflict": "[agents] max_threads が設定されています — codex は起動を拒否します; config.toml から削除してください", "models.v2Applied": "サブエージェントモードを更新しました — 新規セッションに適用(ピッカーを更新するには Codex アプリを再起動)", "models.v2ThreadsLabel": "最大スレッド数", @@ -602,6 +620,7 @@ export const ja: Record = { "logs.title": "リクエストログ", "logs.tabLogs": "ログ", "logs.tabDebug": "デバッグ", + "logs.subtitle": "ローカル opencodex プロキシを経由した最近のリクエスト(新しい順)。", "logs.autoRefresh": "自動更新", "logs.noRequests": "まだリクエストがありません。", "logs.loadError": "リクエストログを読み込めませんでした。", @@ -751,7 +770,6 @@ export const ja: Record = { // usage page "usage.title": "使用量", - "usage.subtitleAria": "使用量の集計方法", "usage.subtitle": "プロキシからのローカルトークン会計です。欠損した使用量はゼロとして表示されることはありません。", "usage.loading": "使用量データを読み込み中…", "usage.empty": "まだ使用量が記録されていません。プロキシ経由でリクエストを送信するとここにアクティビティが表示されます。", @@ -770,6 +788,7 @@ export const ja: Record = { "usage.card.cachedTokensHint": "プロバイダーキャッシュから提供されたプロンプトトークン(読み取り)。キャッシュ書き込みは存在する場合、下に表示されます。", "usage.card.cacheWriteTokens": "キャッシュ書き込み", "usage.card.coverage": "カバレッジ", + "usage.card.activeDays": "アクティブ日数", "usage.section.heatmap": "日のアクティビティ", "usage.section.overview": "概要", "usage.section.models": "モデル", @@ -1195,6 +1214,7 @@ export const ja: Record = { "pws.editJsonDesc": "生のプロキシ設定を JSON として編集", "pws.updatesUnavailable": "プロバイダーの更新は利用できません。", "pws.dashboard.title": "プロバイダー概要", + "pws.dashboard.subtitle": "すべてのモデルプロバイダーを一か所で管理します。", "pws.dashboard.rateLimits": "レート制限", "pws.capacity.estimate": "設定済み重みによるプール推定", "pws.capacity.currentAccount": "現在の有効アカウント", @@ -1419,6 +1439,7 @@ export const ja: Record = { "integrations.moreClients": "その他のクライアント ({count})", "integrations.fewerClients": "未インストールのクライアントを隠す", "integrations.notInstalled": "未インストール ({count})", + "integrations.subtitle": "クライアントを opencodex に接続し、認証情報の管理とクライアント設定の復元を行います。", "integrations.tabsLabel": "連携画面", "integrations.tab.overview": "概要", "integrations.tab.keys": "API キー", @@ -1492,6 +1513,7 @@ export const ja: Record = { "integrations.summary.detected": "検出されたクライアント", "integrations.summary.applied": "設定済みクライアント", "integrations.summary.stale": "更新が必要", + "integrations.summary.lastChange": "最終変更", "integrations.summary.disableAll": "すべて無効にする…", "integrations.onboarding": "適用時は、先にバックアップを保存してから opencodex プロバイダーブロックを 1 つだけ書き込みます。無効化時はそのブロックだけを削除し、保存されたスナップショットから復元できます。", "integrations.empty.title": "インストール済みのクライアントが検出されませんでした", @@ -1532,6 +1554,7 @@ export const ja: Record = { "integrations.status.appliedAt": "適用", "integrations.status.backup": "バックアップ", "integrations.status.lastRestore": "最終復元", + "integrations.status.unknown": "不明", "integrations.bulk.title": "適用済みのクライアント連携を無効にしますか?", "integrations.bulk.body": "opencodex が所有するブロックだけを削除します。各クライアントについて、変更前のスナップショットを保存します。", "integrations.bulk.partial": "一部のクライアントを無効にできませんでした: {clients}", @@ -2266,6 +2289,7 @@ export const ja: Record = { "pws.col.share": "Share", "pws.tokenInput": "Input", "pws.tokenOutput": "Output", + "dash.injectionManage": "設定を開く", "sub.settings": "設定", "sub.sections": "サブエージェントのセクション", "sub.delegation.model": "最初に呼ぶモデル", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 709bfeb3ca..3c579a52aa 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -93,7 +93,6 @@ export const ko: Record = { "sidebar.starFailed": "gh로 스타를 누르지 못했습니다. GitHub를 대신 엽니다.", "sidebar.updateAvailable": "업데이트 있음: {version}", "sidebar.checkUpdate": "업데이트 확인", - "sidebar.preferences": "환경설정", "common.save": "저장", "common.saving": "저장 중…", "common.cancel": "취소", @@ -127,6 +126,7 @@ export const ko: Record = { "startup.title": "시작 안전성", "startup.subtitle": "재부팅 후 로컬 프록시 라우팅이 재연결 반복으로 이어지기 전에 Codex가 opencodex에 연결될 수 있는지 확인합니다.", "startup.refresh": "새로고침", + "startup.backToDashboard": "대시보드로 돌아가기", "startup.loading": "시작 보호 상태 확인 중…", "startup.error": "시작 보호 상태를 읽지 못했습니다.", "startup.staleData": "최신 시작 상태 확인에 실패했습니다. 아래 값은 이전 결과이며 보호 증거로 사용하면 안 됩니다.", @@ -140,11 +140,14 @@ export const ko: Record = { "startup.riskDetailCustomLocal": "Codex가 사용자 지정 로컬 게이트웨이를 바라봅니다. opencodex는 해당 게이트웨이의 재시작 수명주기를 관리하거나 검증할 수 없습니다.", "startup.riskDetailWindowsShim": "Launcher shim은 지원되는 CLI 스크립트만 보호하며 Windows의 Codex Desktop과 직접 codex.exe 실행은 이를 우회할 수 있습니다.", "startup.safeDetail": "현재 라우팅과 시작 방식이 일치합니다. 재부팅 후 ocx start를 수동으로 실행할 필요가 없습니다.", + "startup.routing": "Codex 라우팅", "startup.routing.proxy": "로컬 프록시", "startup.routing.native": "OpenAI 네이티브", "startup.routing.customLocal": "사용자 지정 로컬 게이트웨이", "startup.routing.customRemote": "사용자 지정 원격 게이트웨이", "startup.routing.unknown": "알 수 없거나 잘못된 라우팅", + "startup.restartProtection": "재부팅 보호", + "startup.preference": "필요 시 자동 시작", "startup.enabled": "켜짐", "startup.disabled": "꺼짐", "startup.protection.service": "백그라운드 서비스", @@ -203,9 +206,14 @@ export const ko: Record = { "startup.codexRuntime.olderBinary": "OpenCodex가 더 오래된 Codex 바이너리({version})를 사용 중입니다. 더 새 설치를 사용할 수 있습니다.", // dashboard + "dash.subtitle": "로컬 opencodex 프록시와 프로바이더, 그리고 Codex로 라우팅되는 모델의 실시간 상태입니다.", + "dash.workspace.overview": "개요", + "dash.workspace.sections": "섹션", "dash.status": "상태", "dash.online": "온라인", "dash.offline": "오프라인", + "dash.version": "버전", + "dash.uptime": "가동 시간", "dash.providers": "프로바이더", "dash.tokens30d": "토큰 (30일)", "dash.coverage": "커버리지 {pct}", @@ -242,11 +250,22 @@ export const ko: Record = { "dash.mem.reconnecting": "프록시 재시작 중… 다시 연결하는 중", "dash.mem.restartFailed": "작업 완료 후 재시작에 실패했습니다. 프록시가 실행 중인지 확인하세요.", "dash.mem.restartNoSupervisor": "재시작 보호가 없습니다. 재시작 후 프록시가 자동으로 올라오지 않을 수 있습니다.", + "dash.activeProviders": "활성 프로바이더", + "dash.noProviders": "설정된 프로바이더가 없습니다. {cmd} 를 실행하세요.", + "dash.col.name": "이름", + "dash.col.adapter": "어댑터", + "dash.col.baseUrl": "Base URL", + "dash.col.model": "모델", + "dash.modelsNoResults": "검색과 일치하는 모델이 없습니다.", + "dash.availableModels": "사용 가능한 모델", + "dash.noModels": "모델을 찾을 수 없습니다. 프로바이더 API 키를 확인하세요.", "dash.cannotConnect": "프록시에 연결할 수 없습니다. 실행 중인가요?", "dash.runStart": "{cmd} 를 실행해 프록시를 시작하세요.", "dash.stop": "프록시 중지", "dash.stopConfirm": "프록시를 중지하고 Codex 원본 설정을 복원할까요?", "dash.stopFailed": "프록시를 중지하지 못했습니다 (HTTP {status}).", + "dash.maSwitchFailed": "모드 전환에 실패했습니다 (HTTP {status}).", + "dash.maNetworkError": "네트워크 오류 — 프록시가 실행 중인지 확인하세요.", "dash.stopping": "중지 중…", "dash.actions": "프록시", "dash.codexRestart": "Codex 모델 목록 새로고침", @@ -268,7 +287,6 @@ export const ko: Record = { "dash.searchReasoning": "서치 추론 강도", "dash.visionModel": "비전 사이드카 모델", "dash.visionModelHint": "텍스트 전용 라우팅 모델에 이미지를 설명하는 데 사용되는 모델입니다. ChatGPT 로그인 필요.", - "dash.sidecars": "사이드카", "dash.webSearchSidecar": "웹 검색 사이드카", "dash.webSearchSidecarHint": "라우팅 모델의 웹 검색에 쓸 백엔드와 모델을 고릅니다.", "dash.webSearchStream": "응답 실시간 스트리밍", @@ -276,9 +294,12 @@ export const ko: Record = { "dash.visionSidecar": "비전 사이드카", "dash.visionSidecarHint": "텍스트 전용 라우팅 모델이 이미지를 읽을 때 쓸 백엔드와 모델을 고릅니다.", "dash.visionOff": "끔", + "dash.shadowCallIntercept": "쉐도우 호출 가로채기", "dash.shadowCallInterceptHint": "Codex 앱이 제목·커밋 메시지 생성에 쓰는 백그라운드 호출({models})을 가로채 선택한 모델로 바꿉니다.", "dash.shadowCallWarning": "⚠ 활성화하면 {models} 요청이 모두 선택한 모델로 대체됩니다.", "dash.shadowCallOriginal": "원본", + "dash.shadowCallModel": "대체 모델", + "dash.shadowCallTooltip": "Codex 앱은 스레드 제목 자동 생성, 커밋 메시지 생성, 스킬 오케스트레이션 같은 내부 작업을 백그라운드로 호출합니다. 이때 쓰는 모델은 클라이언트 버전마다 달라서 opencodex는 {models}를 모두 가로챕니다. 이 설정을 켜면 해당 호출이 선택한 모델로 넘어갑니다.", "models.shadowCallIntercept": "쉐도우 호출 가로채기", "models.shadowCallInterceptHint": "Codex 앱의 백그라운드 호출({models}, 제목·커밋 메시지)을 가로채 선택한 모델로 바꿉니다.", "dash.sidecarBackend": "백엔드", @@ -290,6 +311,7 @@ export const ko: Record = { "dash.sidecarSaveFailed": "사이드카 설정 저장에 실패했습니다.", "dash.injectionLabel": "서브에이전트 위임", "dash.injectionHint": "Codex가 서브에이전트에게 일을 넘길 때 쓸 모델을 고릅니다. 이 선택을 어디에 적용할지는 아래 두 스위치가 정합니다.", + "dash.injectionManage": "설정 열기", "dash.syncCodexSubagentDefaults": "Codex 설정에도 기본값으로 저장", "dash.syncCodexSubagentDefaultsHint": "켜면 위에서 고른 모델이 Codex 설정 파일에 저장돼, 새로 시작하는 작업도 처음부터 그 모델을 씁니다. 끄면 여기서만 기억합니다. 반영은 다음 동기화나 재시작 때이고, 직접 적어둔 [agents] 설정은 그대로 둡니다.", "dash.multiAgentGuidance": "일 나누는 방법 알려주기", @@ -472,10 +494,6 @@ export const ko: Record = { "models.subtitle.combos": "여러 모델을 하나의 id로 묶어 순서대로 응답하게 합니다. failover로 대상을 연결하거나 분산 전략으로 부하를 나눕니다.", "models.subtitle.compatibility": "랩 프로젝션 증거의 읽기 전용 호환성 판정 행렬.", "models.subtitle.routing": "정책 프로필, dry-run 평가, 그리고 근거가 남는 라우팅 분석입니다.", - "models.advanced": "고급", - "models.groupMore": "프로바이더 추가 작업 표시", - "models.subtitleAria": "카탈로그 동작 방식", - "models.orderHintAria": "피커 순서가 정해지는 방식", "models.subtitle": "Codex가 보는 모델을 켜고 끕니다 — 네이티브 GPT passthrough와 라우팅된 모델을 프로바이더별로 묶어 보여줍니다(헤더를 클릭하면 접힘). 숨긴 모델은 카탈로그와 선택기에서 빠지지만 정확한 id로 직접 호출할 수 있습니다. 변경 사항은 다음 Codex 턴에 적용됩니다 — opencodex가 Codex의 5분 모델 캐시를 무효화하므로 재시작이 필요 없습니다.", "models.nativeGroupLabel": "OpenAI 네이티브", "models.nativeHint": "프로바이더에서 선택한 풀 또는 직접 계정 옵션으로 서빙되는 passthrough 모델입니다. 끄면 Codex 선택기에서 숨겨지고, 카탈로그 항목은 유지되므로 다시 켜면 그대로 복원됩니다. 여기서 모델을 추가하면 bare passthrough id가 아니라 라우팅된 `openai/` selector로 등록됩니다.", @@ -512,6 +530,7 @@ export const ko: Record = { "models.keepNativeOnV1Hint": "ChatGPT 네이티브 부모는 v2 자식 작업을 암호화해서 Grok/Claude가 읽지 못합니다. Sol/Terra가 routed 모델을 spawn해야 하면 켜 두세요. routed 부모는 v2를 유지합니다.", "models.v2Help": "모든 모델의 멀티에이전트 서피스를 제어합니다.\n\nv1: 단일 스레드 에이전트. 모든 모델이 v1 서피스를 사용합니다.\nbase: 업스트림 기본값 — sol/terra는 v2, luna는 v1, 나머지는 codex 플래그를 따릅니다.\nv2: 멀티 스레드 에이전트(spawn_agent). 모든 모델이 v2 서피스를 사용합니다.\n\nv2에서 ChatGPT는 v1 유지를 켜면 Sol/Terra가 v1에 남아 Grok이나 Claude를 spawn할 수 있습니다. ChatGPT는 v2 자식 작업을 암호화하므로 routed 모델은 읽지 못합니다. routed 부모는 v2를 유지합니다.\n\n새 세션부터 적용됩니다.", "models.v2DocsLink": "v1 / v2가 뭔가요?", + "dash.multiAgent": "서브에이전트", "models.v2Conflict": "[agents] max_threads가 남아 있어 codex가 부팅을 거부합니다 — config.toml에서 제거하세요", "models.v2Applied": "서브에이전트 모드 변경됨 — 새 세션부터 적용 (피커 갱신은 Codex 앱 재시작)", "models.v2ThreadsLabel": "최대 스레드", @@ -644,6 +663,7 @@ export const ko: Record = { "logs.title": "요청 로그", "logs.tabLogs": "로그", "logs.tabDebug": "디버그", + "logs.subtitle": "로컬 opencodex 프록시를 거친 최근 요청입니다. 최신순.", "logs.autoRefresh": "자동 새로고침", "logs.noRequests": "아직 요청이 없습니다.", "logs.loadError": "요청 로그를 불러오지 못했습니다.", @@ -792,7 +812,6 @@ export const ko: Record = { // usage page "usage.title": "사용량", - "usage.subtitleAria": "사용량 집계 방식", "usage.subtitle": "프록시의 로컬 토큰 집계입니다. 누락된 사용량은 0으로 표시하지 않습니다.", "usage.loading": "사용량 데이터를 불러오는 중…", "usage.empty": "아직 기록된 사용량이 없습니다. 프록시로 요청을 보내면 여기에 표시됩니다.", @@ -811,6 +830,7 @@ export const ko: Record = { "usage.card.cachedTokensHint": "프로바이더 캐시에서 읽어온 프롬프트 토큰(히트)입니다. 캐시 생성(쓰기)은 아래에 별도 표시됩니다.", "usage.card.cacheWriteTokens": "캐시 생성", "usage.card.coverage": "커버리지", + "usage.card.activeDays": "활동일", "usage.section.heatmap": "일별 활동", "usage.section.overview": "개요", "usage.section.models": "모델", @@ -1009,6 +1029,7 @@ export const ko: Record = { "integrations.moreClients": "다른 클라이언트 ({count})", "integrations.fewerClients": "설치되지 않은 클라이언트 숨기기", "integrations.notInstalled": "설치되지 않음 ({count})", + "integrations.subtitle": "클라이언트를 opencodex에 연결하고 자격 증명과 설정 복원을 관리합니다.", "integrations.tabsLabel": "연동 화면", "integrations.tab.overview": "개요", "integrations.tab.keys": "API 키", @@ -1082,6 +1103,7 @@ export const ko: Record = { "integrations.summary.detected": "감지된 클라이언트", "integrations.summary.applied": "설정된 클라이언트", "integrations.summary.stale": "업데이트 필요", + "integrations.summary.lastChange": "마지막 변경", "integrations.summary.disableAll": "모두 해제…", "integrations.onboarding": "적용하면 먼저 백업을 보관한 뒤 opencodex 제공자 블록 하나만 씁니다. 해제는 그 블록만 제거하며 보관된 스냅샷으로 복원할 수 있습니다.", "integrations.empty.title": "설치된 클라이언트가 감지되지 않았습니다", @@ -1122,6 +1144,7 @@ export const ko: Record = { "integrations.status.appliedAt": "적용", "integrations.status.backup": "백업", "integrations.status.lastRestore": "마지막 복원", + "integrations.status.unknown": "알 수 없음", "integrations.bulk.title": "적용된 클라이언트 연동을 해제할까요?", "integrations.bulk.body": "opencodex가 소유한 블록만 제거합니다. 각 클라이언트의 변경 전 스냅샷을 보관합니다.", "integrations.bulk.partial": "일부 클라이언트를 해제하지 못했습니다: {clients}", @@ -1981,6 +2004,7 @@ export const ko: Record = { "pws.editJsonDesc": "프록시 설정을 JSON으로 편집", "pws.updatesUnavailable": "프로바이더 업데이트를 사용할 수 없습니다.", "pws.dashboard.title": "프로바이더 개요", + "pws.dashboard.subtitle": "모든 모델 프로바이더를 한곳에서 관리합니다.", "pws.dashboard.rateLimits": "사용량 제한", "pws.capacity.estimate": "설정 가중치 기반 풀 추정치", "pws.capacity.currentAccount": "현재 유효 계정", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index bc699216e9..045e3093a6 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -93,7 +93,6 @@ export const ru: Record = { "sidebar.starFailed": "Не удалось поставить звезду через gh. Открываем GitHub.", "sidebar.updateAvailable": "Доступно обновление: {version}", "sidebar.checkUpdate": "Проверить обновления", - "sidebar.preferences": "Настройки", "common.save": "Сохранить", "common.saving": "Сохранение…", "common.cancel": "Отмена", @@ -132,6 +131,7 @@ export const ru: Record = { "startup.title": "Безопасность запуска", "startup.subtitle": "Проверьте, сможет ли Codex подключиться к opencodex после перезагрузки, прежде чем локальный прокси вызовет бесконечное переподключение.", "startup.refresh": "Обновить", + "startup.backToDashboard": "Назад к панели", "startup.loading": "Проверка защиты запуска…", "startup.error": "Не удалось прочитать состояние защиты запуска.", "startup.staleData": "Последняя проверка не удалась. Значения ниже устарели и не подтверждают защиту.", @@ -145,11 +145,14 @@ export const ru: Record = { "startup.riskDetailCustomLocal": "Codex направлен на пользовательский локальный шлюз. opencodex не может управлять или проверять его перезапуск.", "startup.riskDetailWindowsShim": "Launcher shim защищает поддерживаемые CLI-скрипты, но Codex Desktop и прямой запуск codex.exe в Windows могут обходить его.", "startup.safeDetail": "Маршрутизация и механизм запуска согласованы. После перезагрузки ручной запуск ocx start не требуется.", + "startup.routing": "Маршрутизация Codex", "startup.routing.proxy": "Локальный прокси", "startup.routing.native": "Нативный OpenAI", "startup.routing.customLocal": "Пользовательский локальный шлюз", "startup.routing.customRemote": "Пользовательский удалённый шлюз", "startup.routing.unknown": "Неизвестная или недопустимая маршрутизация", + "startup.restartProtection": "Защита перезапуска", + "startup.preference": "Запуск по требованию", "startup.enabled": "Включён", "startup.disabled": "Выключен", "startup.protection.service": "Фоновая служба", @@ -208,9 +211,14 @@ export const ru: Record = { "startup.codexRuntime.olderBinary": "OpenCodex использует более старый бинарник Codex ({version}). Доступна более новая установка.", // dashboard + "dash.subtitle": "Актуальное состояние локального прокси opencodex, его провайдеров и моделей, маршрутизируемых в Codex.", + "dash.workspace.overview": "Обзор", + "dash.workspace.sections": "Разделы", "dash.status": "Статус", "dash.online": "В сети", "dash.offline": "Не в сети", + "dash.version": "Версия", + "dash.uptime": "Время работы", "dash.providers": "Провайдеры", "dash.tokens30d": "Токены (30 дн.)", "dash.coverage": "{pct} покрытия", @@ -247,11 +255,22 @@ export const ru: Record = { "dash.mem.reconnecting": "Прокси перезапускается… ожидание подключения", "dash.mem.restartFailed": "Не удалось дождаться и перезапустить. Проверьте, что прокси запущен.", "dash.mem.restartNoSupervisor": "Защита перезапуска не обнаружена. После перезапуска прокси может остаться выключенным, пока вы не запустите его снова.", + "dash.activeProviders": "Активные провайдеры", + "dash.noProviders": "Провайдеры не настроены. Выполните {cmd}.", + "dash.col.name": "Название", + "dash.col.adapter": "Адаптер", + "dash.col.baseUrl": "Базовый URL", + "dash.col.model": "Модель", + "dash.modelsNoResults": "Нет моделей, соответствующих поиску.", + "dash.availableModels": "Доступные модели", + "dash.noModels": "Модели не найдены. Проверьте API-ключи провайдеров.", "dash.cannotConnect": "Не удаётся подключиться к прокси. Он запущен?", "dash.runStart": "Выполните {cmd}, чтобы запустить прокси.", "dash.stop": "Остановить прокси", "dash.stopConfirm": "Остановить прокси и восстановить нативный Codex?", "dash.stopFailed": "Не удалось остановить прокси (HTTP {status}).", + "dash.maSwitchFailed": "Не удалось переключить режим (HTTP {status}).", + "dash.maNetworkError": "Ошибка сети — прокси запущен?", "dash.stopping": "Остановка…", "dash.actions": "Прокси", "dash.codexRestart": "Обновить список моделей Codex", @@ -273,7 +292,6 @@ export const ru: Record = { "dash.searchReasoning": "Уровень рассуждений для поиска", "dash.visionModel": "Модель сайдкара для изображений", "dash.visionModelHint": "Модель, которая описывает изображения для маршрутизируемых моделей, работающих только с текстом. Требуется вход в аккаунт ChatGPT.", - "dash.sidecars": "Сайдкары", "dash.webSearchSidecar": "Сайдкар веб-поиска", "dash.webSearchSidecarHint": "Выберите бэкенд и модель, используемые для веб-поиска на маршрутизируемых моделях.", "dash.webSearchStream": "Стримить ответы вживую", @@ -281,9 +299,12 @@ export const ru: Record = { "dash.visionSidecar": "Сайдкар для изображений", "dash.visionSidecarHint": "Выберите бэкенд и модель, которые описывают изображения для маршрутизируемых моделей, работающих только с текстом.", "dash.visionOff": "Выкл", + "dash.shadowCallIntercept": "Перехват теневых вызовов", "dash.shadowCallInterceptHint": "Перехватывает фоновые служебные вызовы Codex App ({models}: генерация заголовков, сообщений коммитов) и перенаправляет их на выбранную вами модель.", "dash.shadowCallWarning": "⚠ Когда функция включена, ВСЕ запросы к {models} будут заменены выбранной моделью.", "dash.shadowCallOriginal": "Оригинал", + "dash.shadowCallModel": "Модель-замена", + "dash.shadowCallTooltip": "Codex App в фоновом режиме вызывает служебную модель для генерации заголовков тредов, сообщений коммитов и оркестрации навыков. Эта модель менялась между версиями клиента, поэтому opencodex перехватывает весь набор: {models}. Включите функцию, чтобы перенаправлять такие вызовы на выбранную вами модель.", "models.shadowCallIntercept": "Перехват теневых вызовов", "models.shadowCallInterceptHint": "Перехватывает фоновые служебные вызовы Codex App ({models}: заголовки, сообщения коммитов) и перенаправляет их на выбранную вами модель.", "dash.sidecarBackend": "Бэкенд", @@ -475,10 +496,6 @@ export const ru: Record = { "models.subtitle.combos": "Упорядоченные группы моделей, отвечающие под одним id. Связывайте цели через failover или распределяйте нагрузку стратегией балансировки.", "models.subtitle.compatibility": "Матрица совместимости только для чтения из проекции лаборатории.", "models.subtitle.routing": "Профили политик, оценка в режиме dry-run и аналитика маршрутизации с подтверждением источников.", - "models.advanced": "Дополнительно", - "models.groupMore": "Показать дополнительные действия провайдера", - "models.subtitleAria": "Как работает каталог", - "models.orderHintAria": "Как определяется порядок в выборе", "models.subtitle": "Управляйте тем, какие модели видит Codex — нативные GPT (сквозной проброс) и модели маршрутизируемых провайдеров, сгруппированные по провайдеру (нажмите на заголовок, чтобы свернуть группу). Скрытые модели исчезают из каталога и селектора, но остаются вызываемыми по точному id. Изменения применяются на следующем ходе Codex — opencodex сбрасывает 5-минутный кэш моделей Codex, поэтому перезапуск не требуется.", "models.nativeGroupLabel": "Нативные OpenAI", "models.nativeHint": "Модели сквозного проброса используют режим аккаунта (пул или прямое подключение), выбранный на странице «Провайдеры». Отключение модели скрывает её из селектора Codex (запись в каталоге сохраняется, поэтому при повторном включении она восстанавливается в точности). Добавление модели здесь регистрирует маршрутизируемый селектор `openai/`, а не новый «голый» passthrough-идентификатор.", @@ -515,6 +532,7 @@ export const ru: Record = { "models.keepNativeOnV1": "Оставить ChatGPT на v1", "models.keepNativeOnV1Hint": "Нативные родители ChatGPT шифруют дочерние задачи v2 — Grok и Claude их не читают. Оставьте включённым, если Sol/Terra должны порождать routed-модели. Routed-родители остаются на v2.", "models.v2Help": "Управляет мультиагентной поверхностью для всех моделей.\n\nv1: Классический однопоточный агент. Каждая модель использует поверхность взаимодействия v1.\nbase: Вышестоящие значения по умолчанию — sol/terra используют v2, luna использует v1, остальные следуют функциональному флагу codex.\nv2: Многопоточный агент со spawn_agent. Каждая модель использует поверхность взаимодействия v2.\n\nНа v2 «Оставить ChatGPT на v1» оставляет Sol/Terra на v1, чтобы они могли порождать Grok или Claude. ChatGPT шифрует дочерние задачи v2 — routed-модели их не читают. Routed-родители остаются на v2.\n\nИзменения применяются к новым сессиям.", + "dash.multiAgent": "Подагент", "models.v2Conflict": "Задан [agents] max_threads — codex откажется запускаться; удалите его из config.toml", "models.v2Applied": "Режим подагента обновлён — применяется к новым сессиям (перезапустите приложение Codex, чтобы обновить селектор моделей)", "models.v2ThreadsLabel": "Макс. потоков", @@ -643,6 +661,7 @@ export const ru: Record = { "logs.title": "Журнал запросов", "logs.tabLogs": "Логи", "logs.tabDebug": "Отладка", + "logs.subtitle": "Недавние запросы через локальный прокси opencodex, новые сверху.", "logs.autoRefresh": "Автообновление", "logs.noRequests": "Запросов пока нет.", "logs.loadError": "Не удалось загрузить журнал запросов.", @@ -792,7 +811,6 @@ export const ru: Record = { // usage page "usage.title": "Использование", - "usage.subtitleAria": "Как учитывается использование", "usage.subtitle": "Локальный учёт токенов вашего прокси. Отсутствующие данные никогда не показываются как ноль.", "usage.loading": "Загрузка данных об использовании…", "usage.empty": "Данных об использовании пока нет. Отправьте запрос через прокси, чтобы увидеть здесь активность.", @@ -811,6 +829,7 @@ export const ru: Record = { "usage.card.cachedTokensHint": "Токены промпта, отданные из кэша провайдера (чтения). Записи в кэш показаны ниже, если они есть.", "usage.card.cacheWriteTokens": "записи в кэш", "usage.card.coverage": "Покрытие", + "usage.card.activeDays": "Активные дни", "usage.section.heatmap": "Активность по дням", "usage.section.overview": "Обзор", "usage.section.models": "Модели", @@ -1246,6 +1265,7 @@ export const ru: Record = { "pws.editJsonDesc": "Редактировать конфигурацию прокси в формате JSON", "pws.updatesUnavailable": "Обновление провайдера недоступно.", "pws.dashboard.title": "Обзор провайдеров", + "pws.dashboard.subtitle": "Управляйте всеми провайдерами моделей в одном месте.", "pws.dashboard.rateLimits": "Лимиты запросов", "pws.capacity.estimate": "Оценка пула по настроенным весам", "pws.capacity.currentAccount": "Текущая активная учётная запись", @@ -1470,6 +1490,7 @@ export const ru: Record = { "integrations.moreClients": "Другие клиенты ({count})", "integrations.fewerClients": "Скрыть неустановленные клиенты", "integrations.notInstalled": "Не установлено ({count})", + "integrations.subtitle": "Подключайте клиенты к opencodex, управляйте учётными данными и восстанавливайте конфигурацию клиентов.", "integrations.tabsLabel": "Разделы интеграций", "integrations.tab.overview": "Обзор", "integrations.tab.keys": "Ключи API", @@ -1543,6 +1564,7 @@ export const ru: Record = { "integrations.summary.detected": "Клиентов найдено", "integrations.summary.applied": "Настроено клиентов", "integrations.summary.stale": "Требуется обновление", + "integrations.summary.lastChange": "Последнее изменение", "integrations.summary.disableAll": "Отключить все…", "integrations.onboarding": "При применении сначала сохраняется резервная копия, а затем записывается один блок провайдера opencodex. При отключении удаляется только этот блок, а сохранённый снимок можно восстановить.", "integrations.empty.title": "Установленные клиенты не обнаружены", @@ -1583,6 +1605,7 @@ export const ru: Record = { "integrations.status.appliedAt": "Применено", "integrations.status.backup": "Резервная копия", "integrations.status.lastRestore": "Последнее восстановление", + "integrations.status.unknown": "Неизвестно", "integrations.bulk.title": "Отключить применённые интеграции клиентов?", "integrations.bulk.body": "Будет удалён только блок, принадлежащий opencodex. Для каждого клиента предварительно сохраняется снимок исходного состояния.", "integrations.bulk.partial": "Не удалось отключить некоторые клиенты: {clients}", @@ -2266,6 +2289,9 @@ export const ru: Record = { "cws.err.invalidStickyLimit": "Число успешных запросов до ротации должно быть целым от 1 до 100.", "cws.err.invalidWeight": "Каждый вес round-robin должен быть целым числом от 1 до 10000.", "cws.err.noEnabledTarget": "Хотя бы одна цель должна использовать включённого провайдера.", + + + "dash.injectionManage": "Открыть настройки", "sub.settings": "Настройки", "sub.sections": "Разделы подагентов", "sub.delegation.model": "Модель, которую вызывать первой", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index 622ebb3c19..8eb7e13513 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -25,7 +25,6 @@ export const tr: Record = { "sidebar.starFailed": "gh üzerinden yıldız verilemedi. Bunun yerine GitHub açılıyor.", "sidebar.updateAvailable": "Güncelleme mevcut: {version}", "sidebar.checkUpdate": "Güncellemeleri kontrol et", - "sidebar.preferences": "Tercihler", "common.save": "Kaydet", "common.saving": "Kaydediliyor…", "common.cancel": "İptal", @@ -133,6 +132,7 @@ export const tr: Record = { "startup.title": "Başlatma güvenliği", "startup.subtitle": "Yerel proxy yönlendirmesi bir yeniden bağlanma döngüsüne girmeden önce Codex'in yeniden başlatmanın ardından opencodex'e erişebildiğini doğrulayın.", "startup.refresh": "Yenile", + "startup.backToDashboard": "Gösterge Paneline Dön", "startup.loading": "Başlatma koruması kontrol ediliyor…", "startup.error": "Başlatma koruması okunamadı.", "startup.staleData": "Son başlatma kontrolü başarısız oldu. Aşağıdaki değerler güncel değildir ve koruma kanıtı olarak kabul edilmemelidir.", @@ -146,11 +146,14 @@ export const tr: Record = { "startup.riskDetailCustomLocal": "Codex özel bir yerel ağ geçidine işaret ediyor. opencodex bu ağ geçidinin yeniden başlatma yaşam döngüsünü yönetemez veya doğrulayamaz.", "startup.riskDetailWindowsShim": "Başlatıcı shim desteklenen CLI betiklerini korur, ancak Codex Desktop ve doğrudan codex.exe başlatmaları Windows'ta bunu atlayabilir.", "startup.safeDetail": "Mevcut yönlendirme ve başlatma mekanizması tutarlıdır. Yeniden başlatmanın ardından manuel `ocx start` komutuna gerek yoktur.", + "startup.routing": "Codex yönlendirmesi", "startup.routing.proxy": "Yerel proxy", "startup.routing.native": "Yerel OpenAI", "startup.routing.customLocal": "Özel yerel ağ geçidi", "startup.routing.customRemote": "Özel uzak ağ geçidi", "startup.routing.unknown": "Bilinmeyen veya geçersiz yönlendirme", + "startup.restartProtection": "Yeniden başlatma koruması", + "startup.preference": "İsteğe bağlı başlatma", "startup.enabled": "Etkin", "startup.disabled": "Devre dışı", "startup.protection.service": "Arka plan servisi", @@ -209,9 +212,14 @@ export const tr: Record = { "startup.codexRuntime.olderBinary": "OpenCodex eski bir Codex ikili dosyası ({version}) kullanıyor. Daha yeni bir kurulum mevcut.", // dashboard + "dash.subtitle": "Yerel opencodex proxy'sinin, sağlayıcılarının ve Codex'e yönlendirilen modellerin canlı durumu.", + "dash.workspace.overview": "Genel Bakış", + "dash.workspace.sections": "Bölümler", "dash.status": "Durum", "dash.online": "Çevrimiçi", "dash.offline": "Çevrimdışı", + "dash.version": "Sürüm", + "dash.uptime": "Çalışma Süresi", "dash.providers": "Sağlayıcılar", "dash.tokens30d": "Jetonlar (30 gün)", "dash.coverage": "%{pct} kapsam", @@ -248,11 +256,22 @@ export const tr: Record = { "dash.mem.reconnecting": "Proxy yeniden başlatılıyor… yeniden bağlanması bekleniyor", "dash.mem.restartFailed": "Boşaltma ve yeniden başlatma başarısız oldu. Proxy'nin çalıştığını kontrol edin.", "dash.mem.restartNoSupervisor": "Yeniden başlatma koruması algılanmadı.", + "dash.activeProviders": "Aktif sağlayıcılar", + "dash.noProviders": "Yapılandırılmış sağlayıcı yok. {cmd} komutunu çalıştırın.", + "dash.col.name": "İsim", + "dash.col.adapter": "Adaptör", + "dash.col.baseUrl": "Taban URL", + "dash.col.model": "Model", + "dash.modelsNoResults": "Aramanızla eşleşen model bulunamadı.", + "dash.availableModels": "Kullanılabilir modeller", + "dash.noModels": "Model bulunamadı. Sağlayıcı API anahtarlarını kontrol edin.", "dash.cannotConnect": "Proxy'ye bağlanılamıyor. Çalışıyor mu?", "dash.runStart": "Proxy'yi başlatmak için {cmd} çalıştırın.", "dash.stop": "Proxy'yi Durdur", "dash.stopConfirm": "Proxy durdurulsun ve yerel Codex geri yüklensin mi?", "dash.stopFailed": "Proxy durdurulamadı (HTTP {status}).", + "dash.maSwitchFailed": "Mod değiştirme başarısız oldu (HTTP {status}).", + "dash.maNetworkError": "Ağ hatası — proxy çalışıyor mu?", "dash.stopping": "Durduruluyor…", "dash.actions": "Proxy", "dash.codexRestart": "Codex model listesini yenile", @@ -274,7 +293,6 @@ export const tr: Record = { "dash.searchReasoning": "Arama akıl yürütme çabası", "dash.visionModel": "Görsel yan araç modeli", "dash.visionModelHint": "Salt metin yönlendirilen modeller için görselleri tanımlamakta kullanılan model. ChatGPT girişi gerektirir.", - "dash.sidecars": "Yan araçlar", "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.webSearchStream": "Yanıtları canlı akıt", @@ -282,9 +300,12 @@ export const tr: Record = { "dash.visionSidecar": "Görsel yan aracı (sidecar)", "dash.visionSidecarHint": "Salt metin modeller için görselleri tanımlamakta kullanılan arka ucu ve modeli seçin.", "dash.visionOff": "Kapalı", + "dash.shadowCallIntercept": "Gölge Çağrı Yakalama", "dash.shadowCallInterceptHint": "Codex App'in arka plan yardımcı çağrılarını ({models}) başlık oluşturma ve commit mesajları için yakalar ve seçtiğiniz modele yönlendirir.", "dash.shadowCallWarning": "⚠ Etkinleştirildiğinde, {models} için olan TÜM istekler seçilen modelle değiştirilecektir.", "dash.shadowCallOriginal": "Orijinal", + "dash.shadowCallModel": "Yedek model", + "dash.shadowCallTooltip": "Codex App arka planda başlık ve commit mesajı oluşturmak için yardımcı çağrılar yapar (orijinal {models}). Etkinleştirerek bunları seçtiğiniz modele yönlendirebilirsiniz.", "models.shadowCallIntercept": "Gölge Çağrı Yakalama", "models.shadowCallInterceptHint": "Codex App'in başlıklar ve commit mesajları için yaptığı arka plan çağrılarını ({models}) yakalar ve seçtiğiniz modele yönlendirir.", "dash.sidecarBackend": "Arka uç", @@ -296,6 +317,7 @@ export const tr: Record = { "dash.sidecarSaveFailed": "Yan araç ayarları kaydedilemedi.", "dash.injectionLabel": "Alt ajan devri", "dash.injectionHint": "Codex'in alt ajan işlerini devredeceği modeli seçin.", + "dash.injectionManage": "Ayarları aç", "dash.syncCodexSubagentDefaults": "Ayrıca Codex varsayılanı olarak kaydet", "dash.syncCodexSubagentDefaultsHint": "Açık olduğunda, yukarıdaki seçim Codex'in kendi yapılandırmasına yazılır.", "dash.multiAgentGuidance": "Codex'e işi nasıl böleceğini söyle", @@ -477,10 +499,6 @@ export const tr: Record = { "models.subtitle.combos": "Tek bir kimlik olarak yanıt veren sıralı model grupları. Hedefleri failover ile zincirleyin veya yükü dengeleme stratejisiyle dağıtın.", "models.subtitle.compatibility": "Lab projeksiyon kanıtından salt okunur uyumluluk matrisi.", "models.subtitle.routing": "Politika profilleri ve simülasyon değerlendirmesi.", - "models.advanced": "Gelişmiş", - "models.groupMore": "Daha fazla sağlayıcı işlemi göster", - "models.subtitleAria": "Katalog nasıl çalışır", - "models.orderHintAria": "Seçici sırası nasıl belirlenir", "models.subtitle": "Codex'in göreceği modelleri açıp kapatın.", "models.nativeGroupLabel": "Yerel OpenAI", "models.nativeHint": "Doğrudan geçiş modelleri Sağlayıcılar bölümünde seçilen Havuz veya Doğrudan seçeneğini kullanır. Buradan model eklemek, yeni bir düz passthrough kimliği değil, yönlendirilmiş bir `openai/` seçici kaydeder.", @@ -517,6 +535,7 @@ export const tr: Record = { "models.keepNativeOnV1": "ChatGPT v1'de kalsın", "models.keepNativeOnV1Hint": "ChatGPT yerel ebeveynleri v2 çocuk görevlerini şifreler; Grok ve Claude okuyamaz. Sol/Terra yönlendirilmiş modelleri spawn edecekse açık bırakın. Yönlendirilmiş ebeveynler v2'de kalır.", "models.v2Help": "v1 alt ajanları birincil modelle sınırlandırır; taban standart ajan sınırlarını devralır; v2 tam çoklu ajan orkestrasyonunu etkinleştirir.\n\nv2'de ChatGPT v1'de kalsın, Sol/Terra'yı v1'de bırakır; böylece Grok veya Claude spawn edebilirler. ChatGPT v2 çocuk görevlerini şifreler; yönlendirilmiş modeller okuyamaz. Yönlendirilmiş ebeveynler v2'de kalır.\n\nDeğişiklikler yeni oturumlara uygulanır.", + "dash.multiAgent": "Alt Ajan", "models.v2Conflict": "[agents] max_threads ayarlanmış — config.toml dosyasından kaldırın", "models.v2Applied": "Çoklu ajan modu güncellendi. Yeni oturumlar bu modu kullanacaktır; model seçiciyi yenilemek için Codex uygulamasını yeniden başlatın.", "models.v2ThreadsLabel": "Maksimum iş parçacığı", @@ -649,6 +668,7 @@ export const tr: Record = { "logs.title": "İstek Günlükleri", "logs.tabLogs": "Günlükler", "logs.tabDebug": "Hata Ayıklama", + "logs.subtitle": "Proxy üzerinden yönlendirilen son istekler.", "logs.autoRefresh": "Otomatik yenile", "logs.noRequests": "Henüz istek yok.", "logs.loadError": "İstek günlükleri yüklenemedi.", @@ -798,7 +818,6 @@ export const tr: Record = { // usage page "usage.title": "Kullanım", - "usage.subtitleAria": "Kullanım nasıl sayılır", "usage.subtitle": "Proxy'nizden yerel jeton muhasebesi.", "usage.loading": "Kullanım verileri yükleniyor…", "usage.empty": "Henüz kullanım kaydedilmedi.", @@ -817,6 +836,7 @@ export const tr: Record = { "usage.card.cachedTokensHint": "Sağlayıcı önbelleğinden sunulan jetonlar.", "usage.card.cacheWriteTokens": "önbellek yazma", "usage.card.coverage": "Kapsam", + "usage.card.activeDays": "Aktif günler", "usage.section.heatmap": "Günlük aktivite", "usage.section.overview": "Genel bakış", "usage.section.models": "Modeller", @@ -1252,6 +1272,7 @@ export const tr: Record = { "pws.editJsonDesc": "Proxy konfigürasyonunu JSON olarak düzenleyin", "pws.updatesUnavailable": "Sağlayıcı güncellemeleri mevcut değil.", "pws.dashboard.title": "Sağlayıcılara genel bakış", + "pws.dashboard.subtitle": "Tüm model sağlayıcılarınızı tek bir yerden yönetin.", "pws.dashboard.rateLimits": "ORAN LİMİTLERİ", "pws.capacity.estimate": "Havuz kapasite tahmini", "pws.capacity.currentAccount": "Mevcut geçerli hesap", @@ -1476,6 +1497,7 @@ export const tr: Record = { "integrations.moreClients": "Diğer istemciler ({count})", "integrations.fewerClients": "Yüklü olmayan istemcileri gizle", "integrations.notInstalled": "Yüklü değil ({count})", + "integrations.subtitle": "İstemcileri opencodex'e bağlayın, kimlik bilgilerini yönetin.", "integrations.tabsLabel": "Entegrasyon yüzeyleri", "integrations.tab.overview": "Genel Bakış", "integrations.tab.keys": "API Anahtarları", @@ -1549,6 +1571,7 @@ export const tr: Record = { "integrations.summary.detected": "Algılanan istemciler", "integrations.summary.applied": "Yapılandırılan istemciler", "integrations.summary.stale": "Güncelleme gerekiyor", + "integrations.summary.lastChange": "Son değişiklik", "integrations.summary.disableAll": "Tümünü devre dışı bırak…", "integrations.onboarding": "Uygulama işlemi bir yedek aldıktan sonra yazar.", "integrations.empty.title": "Yüklü istemci algılanmadı", @@ -1589,6 +1612,7 @@ export const tr: Record = { "integrations.status.appliedAt": "Uygulandı", "integrations.status.backup": "Yedek", "integrations.status.lastRestore": "Son geri yükleme", + "integrations.status.unknown": "Bilinmiyor", "integrations.bulk.title": "Uygulanan istemci entegrasyonları devre dışı bırakılsın mı?", "integrations.bulk.body": "Yalnızca opencodex bloğu kaldırılır.", "integrations.bulk.partial": "Bazı istemciler devre dışı bırakılamadı: {clients}", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index f5f94c4e31..2f338ec09f 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -49,11 +49,14 @@ export const zhTW: Record = { "startup.riskDetailCustomLocal": "Codex 指向自訂本地閘道器。opencodex 無法管理或驗證該閘道器的重新啟動生命週期。", "startup.riskDetailWindowsShim": "Launcher shim 僅保護受支援的 CLI 指令碼;Windows 上的 Codex Desktop 和直接 codex.exe 啟動可以繞過它。", "startup.safeDetail": "當前路由與啟動機制一致。重新啟動後無需手動執行 ocx start。", + "startup.routing": "Codex 路由", "startup.routing.proxy": "本機代理", "startup.routing.native": "OpenAI 原生", "startup.routing.customLocal": "自訂本地閘道器", "startup.routing.customRemote": "自訂遠端閘道器", "startup.routing.unknown": "未知或無效的路由", + "startup.restartProtection": "重新啟動保護", + "startup.preference": "按需啟動", "startup.enabled": "已啟用", "startup.disabled": "已停用", "startup.protection.service": "背景服務", @@ -106,9 +109,14 @@ export const zhTW: Record = { "startup.codexRuntime.clampHidden": "部分推理強度選項已隱藏,因為 OpenCodex 正在使用 Codex {version}。", "startup.codexRuntime.clampHiddenWithEfforts": "部分推理強度選項已隱藏,因為 OpenCodex 正在使用 Codex {version}(已移除:{efforts})。", "startup.codexRuntime.olderBinary": "OpenCodex 正在使用較舊的 Codex 二進位制檔案({version})。檢測到可用的較新安裝。", + "dash.subtitle": "本地 opencodex 代理、其供應商以及路由到 Codex 的模型的即時狀態。", + "dash.workspace.overview": "總覽", + "dash.workspace.sections": "板塊", "dash.status": "狀態", "dash.online": "線上", "dash.offline": "離線", + "dash.version": "版本", + "dash.uptime": "執行時間", "dash.providers": "供應商", "dash.tokens30d": "Token (30 天)", "dash.coverage": "覆蓋率 {pct}", @@ -141,11 +149,22 @@ export const zhTW: Record = { "dash.mem.reconnecting": "代理正在重啟… 等待重新連線", "dash.mem.restartFailed": "排空並重啟失敗。請確認代理正在執行。", "dash.mem.restartNoSupervisor": "未檢測到重啟保護。重啟後代理可能不會自動恢復,需手動啟動。", + "dash.activeProviders": "活躍供應商", + "dash.noProviders": "尚未配置供應商。請執行 {cmd}。", + "dash.col.name": "名稱", + "dash.col.adapter": "介面卡", + "dash.col.baseUrl": "Base URL", + "dash.col.model": "模型", + "dash.modelsNoResults": "沒有符合搜尋的模型。", + "dash.availableModels": "可用模型", + "dash.noModels": "未找到模型。請檢查供應商 API 金鑰。", "dash.cannotConnect": "無法連線到代理。它在執行嗎?", "dash.runStart": "執行 {cmd} 以啟動代理。", "dash.stop": "停止代理", "dash.stopConfirm": "停止代理並恢復原生 Codex 配置?", "dash.stopFailed": "無法停止代理 (HTTP {status})。", + "dash.maSwitchFailed": "模式切換失敗 (HTTP {status})。", + "dash.maNetworkError": "網路錯誤 — 代理是否正在執行?", "dash.stopping": "正在停止…", "dash.actions": "代理", "dash.codexRestart": "重新載入 Codex 模型清單", @@ -167,7 +186,6 @@ export const zhTW: Record = { "dash.searchReasoning": "搜尋推理強度", "dash.visionModel": "視覺附屬模型", "dash.visionModelHint": "為純文字路由模型描述圖像的模型。需要 ChatGPT 登入。", - "dash.sidecars": "側車", "dash.webSearchSidecar": "網頁搜尋附屬服務", "dash.webSearchSidecarHint": "選擇路由模型進行網頁搜尋時使用的後端和模型。", "dash.webSearchStream": "即時串流輸出回答", @@ -175,9 +193,12 @@ export const zhTW: Record = { "dash.visionSidecar": "視覺附屬服務", "dash.visionSidecarHint": "選擇純文字路由模型描述圖像時使用的後端和模型。", "dash.visionOff": "關閉", + "dash.shadowCallIntercept": "影子呼叫攔截", "dash.shadowCallInterceptHint": "攔截 Codex 應用的背景 helper 呼叫({models})以生成標題與提交訊息,並將它們重定向到您選擇的模型。", "dash.shadowCallWarning": "⚠ 啟用後,{models} 的所有請求將被替換為所選模型。", "dash.shadowCallOriginal": "原始", + "dash.shadowCallModel": "替代模型", + "dash.shadowCallTooltip": "Codex 應用會為執行緒標題生成、提交訊息生成與技能編排發出背景 helper 呼叫。helper 模型因客戶端版本而異,因此 opencodex 攔截此集合中的每個模型:{models}。啟用此選項可將這些呼叫重定向到您選擇的模型。", "models.shadowCallIntercept": "影子呼叫攔截", "models.shadowCallInterceptHint": "攔截 Codex 應用的背景 helper 呼叫({models})以生成標題與提交訊息,並將它們重定向到您選擇的模型。", "dash.sidecarBackend": "後端", @@ -354,10 +375,6 @@ export const zhTW: Record = { "prov.manageCodexAccounts": "管理 Codex 帳號", "prov.openaiApiMissing": "需要 API 金鑰", "prov.openaiApiSetup": "設定 API 金鑰", - "models.advanced": "進階", - "models.groupMore": "顯示更多供應商操作", - "models.subtitleAria": "目錄的運作方式", - "models.orderHintAria": "選擇器順序的決定方式", "models.subtitle": "開關 Codex 可見的模型 — 原生 GPT passthrough 與已路由模型按供應商分組(點選標題可摺疊)。已停用的模型會從目錄和模型選擇器中隱藏。更改在下一個 Codex 回合生效 — opencodex 會使 Codex 的 5 分鐘模型快取失效,因此無需重新啟動。", "models.nativeGroupLabel": "OpenAI 原生", "models.nativeHint": "Passthrough 模型使用在供應商頁面選擇的帳號池或直連選項。關閉後會從 Codex 選擇器中隱藏(目錄條目保留,重新開啟即可完整恢復)。 在此新增模型會註冊為路由的 `openai/` 選擇器,而非新的裸 passthrough id。", @@ -394,6 +411,7 @@ export const zhTW: Record = { "models.keepNativeOnV1": "ChatGPT 維持 v1", "models.keepNativeOnV1Hint": "ChatGPT 原生父代理會加密 v2 子任務,Grok/Claude 無法讀取。若 Sol/Terra 仍需派發路由模型,請保持開啟。路由父代理仍使用 v2。", "models.v2Help": "控制所有模型的多代理介面。\n\nv1: 經典單執行緒代理。所有模型使用 v1 協作介面。\nbase: 上游預設值 — sol/terra 使用 v2,luna 使用 v1,其餘跟隨 codex 功能標誌。\nv2: 多執行緒代理(spawn_agent)。所有模型使用 v2 協作介面。\n\n在 v2 下,「ChatGPT 維持 v1」會讓 Sol/Terra 留在 v1,以便繼續派發 Grok 或 Claude。ChatGPT 會加密 v2 子任務,路由模型無法讀取;路由父代理仍留在 v2。\n\n更改在新會話中生效。", + "dash.multiAgent": "子代理", "models.v2Conflict": "[agents] max_threads 仍存在 — codex 將拒絕啟動,請從 config.toml 移除", "models.v2Applied": "子代理模式已更新 — 新會話生效(重新啟動 Codex 應用以重新整理選擇器)", "models.v2ThreadsLabel": "最大執行緒", @@ -497,6 +515,7 @@ export const zhTW: Record = { "logs.title": "請求日誌", "logs.tabLogs": "日誌", "logs.tabDebug": "除錯", + "logs.subtitle": "經過本地 opencodex 代理的最近請求,最新在前。", "logs.autoRefresh": "自動重新整理", "logs.noRequests": "暫無請求。", "logs.loadError": "無法載入請求日誌。", @@ -621,7 +640,6 @@ export const zhTW: Record = { "debug.noLines.usage": "用量提取已開啟但尚未捕獲任何內容。請透過 Codex 傳送請求,隨後會顯示在此處。", "debug.noLines.injection": "注入日誌已開啟但尚未捕獲任何內容。它紀錄協作和子代理回合中的多代理指導注入與 effort-cap 決策。", "usage.title": "用量", - "usage.subtitleAria": "用量的統計方式", "usage.subtitle": "代理本地的 Token 用量統計。缺失的用量不會顯示為零。", "usage.loading": "正在載入用量資料…", "usage.empty": "尚無用量紀錄。透過代理傳送請求後將在此顯示。", @@ -637,6 +655,7 @@ export const zhTW: Record = { "usage.card.cachedTokensHint": "從供應商快取讀取的提示 Token(命中)。快取寫入在下方單獨顯示。", "usage.card.cacheWriteTokens": "快取寫入", "usage.card.coverage": "覆蓋率", + "usage.card.activeDays": "活躍天數", "usage.section.heatmap": "每日活動", "usage.section.overview": "總覽", "usage.section.models": "模型", @@ -1038,6 +1057,7 @@ export const zhTW: Record = { "pws.editJsonDesc": "以 JSON 編輯原始代理配置", "pws.updatesUnavailable": "供應商更新不可用。", "pws.dashboard.title": "供應商總覽", + "pws.dashboard.subtitle": "在一個地方管理所有模型供應商。", "pws.dashboard.rateLimits": "速率限制", "pws.dashboard.recentlyUsed": "最近使用", "pws.dashboard.requests": "{count} 個請求", @@ -1776,6 +1796,7 @@ export const zhTW: Record = { "claudeDesktop.health.stats": "{count} 請求 / {errors} 錯誤", "claudeDesktop.effort.supported": "effort", "claudeDesktop.effort.displayOnly": "effort (僅顯示)", + "startup.backToDashboard": "返回儀表板", "startup.repair": "修復", "startup.repairing": "正在修復…", "startup.serviceRepaired": "背景服務修復成功。", @@ -1835,11 +1856,11 @@ export const zhTW: Record = { "sidebar.starFailed": "無法透過 gh 加星,改為開啟 GitHub。", "sidebar.updateAvailable": "有可用更新:{version}", "sidebar.checkUpdate": "檢查更新", - "sidebar.preferences": "偏好設定", "dash.mem.jsHeapArena": "arena {total}", "dash.mem.pressure": "相對於警告閾值", "dash.mem.pressureOf": "警告閾值的 {pct}%", "dash.mem.pressureUnknown": "未回報閾值", + "dash.injectionManage": "開啟設定", "dash.syncModelsHint": "根據已連接的供應商重寫 Codex 的模型目錄。", "dash.syncRun": "立即同步", "sub.settings": "設定", @@ -2040,6 +2061,7 @@ export const zhTW: Record = { "integrations.moreClients": "更多用戶端 ({count})", "integrations.fewerClients": "隱藏未安裝的用戶端", "integrations.notInstalled": "未安裝 ({count})", + "integrations.subtitle": "將客戶端連線到 opencodex、管理憑證,並還原客戶端設定。", "integrations.tabsLabel": "整合表面", "integrations.tab.overview": "總覽", "integrations.tab.keys": "API 金鑰", @@ -2113,6 +2135,7 @@ export const zhTW: Record = { "integrations.summary.detected": "偵測到的用戶端", "integrations.summary.applied": "已設定的用戶端", "integrations.summary.stale": "需要更新", + "integrations.summary.lastChange": "上次變更", "integrations.summary.disableAll": "全部停用…", "integrations.onboarding": "套用時會先儲存備份,再寫入一個 opencodex 供應商區塊。停用只會移除該區塊,且可從保留的快照還原。", "integrations.empty.title": "未偵測到已安裝的用戶端", @@ -2153,6 +2176,7 @@ export const zhTW: Record = { "integrations.status.appliedAt": "已套用", "integrations.status.backup": "備份", "integrations.status.lastRestore": "上次還原", + "integrations.status.unknown": "未知", "integrations.bulk.title": "要停用已套用的用戶端整合?", "integrations.bulk.body": "只會移除屬於 opencodex 的區塊。每個用戶端都會先保留一份寫入前快照。", "integrations.bulk.partial": "部分用戶端無法停用:{clients}", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index c1a4ceaa25..cb5904068a 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -93,7 +93,6 @@ export const zh: Record = { "sidebar.starFailed": "无法通过 gh 加星,改为打开 GitHub。", "sidebar.updateAvailable": "有可用更新:{version}", "sidebar.checkUpdate": "检查更新", - "sidebar.preferences": "偏好设置", "common.save": "保存", "common.saving": "保存中…", "common.cancel": "取消", @@ -127,6 +126,7 @@ export const zh: Record = { "startup.title": "启动安全", "startup.subtitle": "检查重启后 Codex 是否仍能连接 opencodex,避免本地代理路由陷入重复重连。", "startup.refresh": "刷新", + "startup.backToDashboard": "返回仪表盘", "startup.loading": "正在检查启动保护…", "startup.error": "无法读取启动保护状态。", "startup.staleData": "最新启动检查失败。以下数据已过期,不得视为已受保护的证明。", @@ -140,11 +140,14 @@ export const zh: Record = { "startup.riskDetailCustomLocal": "Codex 指向自定义本地网关。opencodex 无法管理或验证该网关的重启生命周期。", "startup.riskDetailWindowsShim": "Launcher shim 仅保护受支持的 CLI 脚本;Windows 上的 Codex Desktop 和直接 codex.exe 启动可以绕过它。", "startup.safeDetail": "当前路由与启动机制一致。重启后无需手动运行 ocx start。", + "startup.routing": "Codex 路由", "startup.routing.proxy": "本地代理", "startup.routing.native": "OpenAI 原生", "startup.routing.customLocal": "自定义本地网关", "startup.routing.customRemote": "自定义远程网关", "startup.routing.unknown": "未知或无效的路由", + "startup.restartProtection": "重启保护", + "startup.preference": "按需启动", "startup.enabled": "已启用", "startup.disabled": "已禁用", "startup.protection.service": "后台服务", @@ -203,9 +206,14 @@ export const zh: Record = { "startup.codexRuntime.olderBinary": "OpenCodex 正在使用较旧的 Codex 二进制文件({version})。检测到可用的较新安装。", // dashboard + "dash.subtitle": "本地 opencodex 代理、其提供方以及路由到 Codex 的模型的实时状态。", + "dash.workspace.overview": "概览", + "dash.workspace.sections": "板块", "dash.status": "状态", "dash.online": "在线", "dash.offline": "离线", + "dash.version": "版本", + "dash.uptime": "运行时间", "dash.providers": "提供方", "dash.tokens30d": "Token (30 天)", "dash.coverage": "覆盖率 {pct}", @@ -242,11 +250,22 @@ export const zh: Record = { "dash.mem.reconnecting": "代理正在重启… 等待重新连接", "dash.mem.restartFailed": "排空并重启失败。请确认代理正在运行。", "dash.mem.restartNoSupervisor": "未检测到重启保护。重启后代理可能不会自动恢复,需手动启动。", + "dash.activeProviders": "活跃提供方", + "dash.noProviders": "尚未配置提供方。请运行 {cmd}。", + "dash.col.name": "名称", + "dash.col.adapter": "适配器", + "dash.col.baseUrl": "Base URL", + "dash.col.model": "模型", + "dash.modelsNoResults": "没有符合搜索的模型。", + "dash.availableModels": "可用模型", + "dash.noModels": "未找到模型。请检查提供方 API 密钥。", "dash.cannotConnect": "无法连接到代理。它在运行吗?", "dash.runStart": "运行 {cmd} 以启动代理。", "dash.stop": "停止代理", "dash.stopConfirm": "停止代理并恢复原生 Codex 配置?", "dash.stopFailed": "无法停止代理 (HTTP {status})。", + "dash.maSwitchFailed": "模式切换失败 (HTTP {status})。", + "dash.maNetworkError": "网络错误 — 代理是否正在运行?", "dash.stopping": "正在停止…", "dash.actions": "代理", "dash.codexRestart": "重新加载 Codex 模型列表", @@ -268,7 +287,6 @@ export const zh: Record = { "dash.searchReasoning": "搜索推理强度", "dash.visionModel": "视觉附属模型", "dash.visionModelHint": "为纯文本路由模型描述图像的模型。需要 ChatGPT 登录。", - "dash.sidecars": "边车", "dash.webSearchSidecar": "网页搜索附属服务", "dash.webSearchSidecarHint": "选择路由模型进行网页搜索时使用的后端和模型。", "dash.webSearchStream": "实时流式输出回答", @@ -276,9 +294,12 @@ export const zh: Record = { "dash.visionSidecar": "视觉附属服务", "dash.visionSidecarHint": "选择纯文本路由模型描述图像时使用的后端和模型。", "dash.visionOff": "关闭", + "dash.shadowCallIntercept": "影子调用拦截", "dash.shadowCallInterceptHint": "拦截 Codex 应用的后台辅助调用({models}:标题生成、提交消息)并重定向到所选模型。", "dash.shadowCallWarning": "⚠ 启用后,所有对 {models} 的请求都将被替换为所选模型。", "dash.shadowCallOriginal": "原始", + "dash.shadowCallModel": "替代模型", + "dash.shadowCallTooltip": "Codex 应用会在后台调用辅助模型来生成线程标题、提交消息以及进行技能编排。该模型随客户端版本变化,因此 opencodex 会同时拦截这些模型:{models}。启用此选项可将这些调用重定向到您选择的模型。", "models.shadowCallIntercept": "影子调用拦截", "models.shadowCallInterceptHint": "拦截 Codex 应用的后台辅助调用({models})并重定向到所选模型。", "dash.sidecarBackend": "后端", @@ -470,10 +491,6 @@ export const zh: Record = { "models.subtitle.combos": "把多个模型合成一个 id 依次应答。用 failover 串联目标,或用均衡策略分摊负载。", "models.subtitle.compatibility": "来自实验室投影证据的只读兼容性判定矩阵。", "models.subtitle.routing": "策略配置、dry-run 评估,以及有据可查的路由分析。", - "models.advanced": "高级", - "models.groupMore": "显示更多提供商操作", - "models.subtitleAria": "目录的工作方式", - "models.orderHintAria": "选择器顺序的决定方式", "models.subtitle": "开关 Codex 可见的模型 — 原生 GPT passthrough 与已路由模型按提供方分组(点击标题可折叠)。隐藏的模型不会出现在目录和模型选择器中,但仍可按精确 id 直接调用。更改在下一个 Codex 回合生效 — opencodex 会使 Codex 的 5 分钟模型缓存失效,因此无需重启。", "models.nativeGroupLabel": "OpenAI 原生", "models.nativeHint": "Passthrough 模型使用在提供方页面选择的账户池或直连选项。关闭后会从 Codex 选择器中隐藏(目录条目保留,重新开启即可完整恢复)。 在此添加模型将注册为路由的 `openai/` 选择器,而不是新的裸 passthrough id。", @@ -510,6 +527,7 @@ export const zh: Record = { "models.keepNativeOnV1Hint": "仅当 ChatGPT 原生父代理仍留在 v2 时,才会加密 v2 子任务,Grok/Claude 无法读取。开启此选项可让 Sol/Terra 留在 v1,从而避免该加密。路由父代理仍使用 v2。", "models.v2Help": "控制所有模型的多代理界面。\n\nv1: 经典单线程代理。所有模型使用 v1 协作界面。\nbase: 上游默认值 — sol/terra 使用 v2,luna 使用 v1,其余跟随 codex 功能标志。\nv2: 多线程代理(spawn_agent)。所有模型使用 v2 协作界面。\n\n在 v2 下,「ChatGPT 保持 v1」会让 Sol/Terra 留在 v1,以便继续派发 Grok 或 Claude。ChatGPT 会加密 v2 子任务,路由模型无法读取;路由父代理仍留在 v2。\n\n更改在新会话中生效。", "models.v2DocsLink": "v1 / v2 是什么?", + "dash.multiAgent": "子代理", "models.v2Conflict": "[agents] max_threads 仍存在 — codex 将拒绝启动,请从 config.toml 移除", "models.v2Applied": "子代理模式已更新 — 新会话生效(重启 Codex 应用以刷新选择器)", "models.v2ThreadsLabel": "最大线程", @@ -638,6 +656,7 @@ export const zh: Record = { "logs.title": "请求日志", "logs.tabLogs": "日志", "logs.tabDebug": "调试", + "logs.subtitle": "经过本地 opencodex 代理的最近请求,最新在前。", "logs.autoRefresh": "自动刷新", "logs.noRequests": "暂无请求。", "logs.loadError": "无法加载请求日志。", @@ -786,7 +805,6 @@ export const zh: Record = { // usage page "usage.title": "用量", - "usage.subtitleAria": "用量的统计方式", "usage.subtitle": "代理本地的 Token 用量统计。缺失的用量不会显示为零。", "usage.loading": "正在加载用量数据…", "usage.empty": "尚无用量记录。通过代理发送请求后将在此显示。", @@ -805,6 +823,7 @@ export const zh: Record = { "usage.card.cachedTokensHint": "从提供商缓存读取的提示 Token(命中)。缓存写入在下方单独显示。", "usage.card.cacheWriteTokens": "缓存写入", "usage.card.coverage": "覆盖率", + "usage.card.activeDays": "活跃天数", "usage.section.heatmap": "每日活动", "usage.section.overview": "概览", "usage.section.models": "模型", @@ -1003,6 +1022,7 @@ export const zh: Record = { "integrations.moreClients": "更多客户端 ({count})", "integrations.fewerClients": "隐藏未安装的客户端", "integrations.notInstalled": "未安装 ({count})", + "integrations.subtitle": "将客户端连接到 opencodex,管理凭据并恢复客户端配置。", "integrations.tabsLabel": "集成页面", "integrations.tab.overview": "概览", "integrations.tab.keys": "API 密钥", @@ -1076,6 +1096,7 @@ export const zh: Record = { "integrations.summary.detected": "已检测客户端", "integrations.summary.applied": "已配置客户端", "integrations.summary.stale": "需要更新", + "integrations.summary.lastChange": "上次更改", "integrations.summary.disableAll": "全部禁用…", "integrations.onboarding": "应用时会先保存备份,再写入一个 opencodex 提供方配置块。禁用时只移除该配置块,并可从保留的快照恢复。", "integrations.empty.title": "未检测到已安装的客户端", @@ -1116,6 +1137,7 @@ export const zh: Record = { "integrations.status.appliedAt": "已应用", "integrations.status.backup": "备份", "integrations.status.lastRestore": "上次恢复", + "integrations.status.unknown": "未知", "integrations.bulk.title": "禁用已应用的客户端集成?", "integrations.bulk.body": "只会移除归 opencodex 所有的配置块。每个客户端都会先保留一份写入前快照。", "integrations.bulk.partial": "部分客户端无法禁用:{clients}", @@ -1975,6 +1997,7 @@ export const zh: Record = { "pws.editJsonDesc": "以 JSON 编辑原始代理配置", "pws.updatesUnavailable": "提供商更新不可用。", "pws.dashboard.title": "提供商概览", + "pws.dashboard.subtitle": "在一个地方管理所有模型提供商。", "pws.dashboard.rateLimits": "速率限制", "pws.capacity.estimate": "按配置权重估算的账户池", "pws.capacity.currentAccount": "当前有效账户", @@ -2265,6 +2288,8 @@ export const zh: Record = { "claudeDesktop.health.stats": "{count} 请求 / {errors} 错误", "claudeDesktop.effort.supported": "effort", "claudeDesktop.effort.displayOnly": "effort (仅显示)", + + "dash.injectionManage": "打开设置", "sub.settings": "设置", "sub.sections": "子代理分区", "sub.delegation.model": "优先调用的模型", diff --git a/gui/src/pages/Dashboard.tsx b/gui/src/pages/Dashboard.tsx index 3d6d567e35..e32a671ec8 100644 --- a/gui/src/pages/Dashboard.tsx +++ b/gui/src/pages/Dashboard.tsx @@ -1,22 +1,30 @@ +import { type ReactNode } from "react"; import { IconAlert } from "../icons"; import { Trans } from "../i18n/provider"; +import { navigateHash } from "../hash-routing"; import { EmptyState } from "../ui"; import { DashboardDialogs } from "./dashboard-dialogs"; +import { DashboardModelsSection } from "./dashboard-models-section"; import { DashboardOverviewSection } from "./dashboard-overview-section"; +import { DashboardProvidersSection } from "./dashboard-providers-section"; +import { + dashboardHashForSection, + type DashboardSection, +} from "./dashboard-shared"; import { useDashboardData } from "./use-dashboard-data"; -/** - * The dashboard is one surface: health, the reboot-protection bar, model sync, the sidecar - * disclosure and memory pressure. It used to carry two more tabs — read-only copies of the - * Providers and Models pages with no way to act on them — and the settings that have a home - * elsewhere (subagent mode, delegation model, shadow-call intercept, Codex autostart). Each - * of those was a second editor for one server value, which is a state-divergence bug - * waiting to be filed. The old `#dashboard/providers` and `#dashboard/models` bookmarks - * redirect to the real pages (app-routing.ts). - */ +function selectDashboardTab(next: DashboardSection) { + // Deliberate navigation: push a history entry so Back/Forward restore the tab. + navigateHash(dashboardHashForSection(next)); +} + export default function Dashboard({ apiBase }: { apiBase: string }) { const d = useDashboardData(apiBase); - const { t, error } = d; + const { + t, error, selectedSection, + providers, models, modelsLoading, modelQuery, setModelQuery, + filteredGroups, expandedProviders, setExpandedProviders, + } = d; if (error) { return ( @@ -27,15 +35,77 @@ export default function Dashboard({ apiBase }: { apiBase: string }) { ); } + const overviewSection = ; + const providersSection = ; + const modelsSection = ( + + ); + const updateDialog = ; + + const sections: { id: DashboardSection; label: string; body: ReactNode }[] = [ + { id: "overview", label: t("dash.workspace.overview"), body: overviewSection }, + { id: "providers", label: t("dash.activeProviders"), body: providersSection }, + { id: "models", label: t("dash.availableModels"), body: modelsSection }, + ]; + const selected = sections.find(s => s.id === selectedSection) ?? sections[0]; + const selectTab = selectDashboardTab; + const onTabKeyDown = (e: React.KeyboardEvent) => { + const index = sections.findIndex(s => s.id === selectedSection); + let next = -1; + if (e.key === "ArrowRight") next = (index + 1) % sections.length; + else if (e.key === "ArrowLeft") next = (index - 1 + sections.length) % sections.length; + else if (e.key === "Home") next = 0; + else if (e.key === "End") next = sections.length - 1; + if (next < 0) return; + e.preventDefault(); + const target = sections[next]!; + selectTab(target.id); + document.getElementById(`dashboard-tab-${target.id}`)?.focus(); + }; + return (

{t("nav.dashboard")}

-
- +

{t("dash.subtitle")}

+
+ {sections.map(s => ( + + ))} +
+
+ {selected.body}
- + {updateDialog}
); } diff --git a/gui/src/pages/Models.tsx b/gui/src/pages/Models.tsx index 4c00641422..d849780e49 100644 --- a/gui/src/pages/Models.tsx +++ b/gui/src/pages/Models.tsx @@ -4,7 +4,7 @@ import type { AppServerStateOutcome } from "../codex-app-server-state"; import { useCodexRestart } from "../use-codex-restart"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Switch, Notice, EmptyState, Select, Tooltip } from "../ui"; -import { IconChevron, IconBoxes, IconInfo, IconCheck, IconAlert, IconPencil } from "../icons"; +import { IconChevron, IconBoxes, IconInfo, IconCheck, IconAlert, IconRefresh, IconPencil } from "../icons"; import { useT } from "../i18n/shared"; import type { TFn, TKey } from "../i18n/shared"; import { modelLabel } from "../model-display"; @@ -255,12 +255,13 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; const [aliases, setAliases] = useState({ providers: {}, models: {}, defaults: { global: false, providers: {} } }); const [showAliases, setShowAliases] = useState(false); const [presetBusy, setPresetBusy] = useState(null); - const [, setV2Loading] = useState(true); + const [v2Loading, setV2Loading] = useState(true); const [v2Busy, setV2Busy] = useState(false); const [v2Note, setV2Note] = useState(""); const v2BusyRef = useRef(false); const [threadsCustom, setThreadsCustom] = useState(""); const [showThreadsCustom, setShowThreadsCustom] = useState(false); + const [v2HelpOpen, setV2HelpOpen] = useState(false); const [customModalOpen, setCustomModalOpen] = useState(false); const reloadAliases = useCallback(async (signal?: AbortSignal) => { @@ -682,11 +683,6 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; * panels report theirs up once mounted, and a tab that has never been opened has * nothing truthful to say. */ - // Combos/routing show their onboarding sentence only while the workspace is empty; the - // compatibility tab keeps its one line (it is a caveat, not onboarding). - const tabIsEmpty = tab === "combos" ? comboCount === 0 - : tab === "routing" ? routingCount === 0 - : true; const tabMeta = useMemo(() => ({ catalog: catalogCountReady ? t("models.active", { active: effectiveVisibleCount, total: models.length }) @@ -930,6 +926,10 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; } }; + const setMultiAgentMode = async (mode: "v1" | "default" | "v2") => { + if (!v2 || v2.multiAgentMode === mode) return; + await putV2Setting({ multiAgentMode: mode }); + }; /** @@ -1246,14 +1246,6 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string;
- - - {/* Rarely used per-provider actions (aliases, custom model, presets, context - cap) fold into a labelled disclosure so the header row reads as - identity + on/off instead of a six-control wall. */} -
- -
void setDefaultAliases(!(aliases.defaults.providers[provider] ?? aliases.defaults.global), provider)} @@ -1347,6 +1339,8 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; ); })()} + +
{/* The label names the FUNCTION. It used to be `models.capValue` - "기본 128k" - which is a value masquerading as a name: even a @@ -1418,8 +1412,6 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; aria-haspopup="dialog" >{t("models.contextSettings")}
-
-
{!isCollapsed && ( @@ -1608,6 +1600,38 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string;
+ {(v2Loading || v2) && ( +
+ {t("models.v2Label")} +
+ {(["v1", "default", "v2"] as const).map(mode => ( + + ))} +
+ +
+ )} {v2 && v2.multiAgentMode === "v2" && (
@@ -1725,15 +1749,15 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; ); })()} +
+
); const collapseControls = (
- - @@ -1755,6 +1779,27 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; const modalsBlock = ( <> + {v2HelpOpen && ( +
setV2HelpOpen(false)} onKeyDown={e => { if (e.key === "Escape") setV2HelpOpen(false); }}> +
e.stopPropagation()}> +
+

{t("models.v2Label")}

+ +
+
+ {t("models.v2Help")} +
+ +
+ +
+
+
+ )} {contextModalProvider && (
- {/* - Catalog-wide policy (new-model default, aliases, shadow-call, context cap) is - set once and rarely revisited; it used to be a wall of switches above the list. - One closed disclosure keeps every control reachable and the list first. - */} -
- {t("models.advanced")} - {controlsBlock} -
+ {controlsBlock} {collapseControls} {showAliases && (
@@ -2166,15 +2203,14 @@ export default function Models({ apiBase, restartEpoch = 0 }: { apiBase: string; <>

{t("nav.models")}

- {/* - The catalog's four-line explanation (cache semantics, hidden ids still resolve) is - reference, not a decision; it lives on a focusable info button instead of above - the controls. Other tabs keep a one-line subtitle only in their empty state. - */} - - +
+ +
{t(SUBTITLE_TKEY[tab])}

} +

{t(SUBTITLE_TKEY[tab])}

{/* Panels mount lazily and then stay mounted, hidden — a half-typed combo draft diff --git a/gui/src/pages/Startup.tsx b/gui/src/pages/Startup.tsx index c5c531f5bc..9cc51aea75 100644 --- a/gui/src/pages/Startup.tsx +++ b/gui/src/pages/Startup.tsx @@ -5,7 +5,6 @@ import { readSessionListCache, writeSessionListCache } from "../session-list-cac import { Notice } from "../ui"; import { useDataSurface } from "../data-surface"; import { DataSurfaceSkeleton } from "../components/data-surface"; -import { useCodexAutostart } from "./use-codex-autostart"; import { StartupDetailsSection, StartupHeroSection, @@ -72,7 +71,6 @@ function deriveCodexRuntimeNotice( export default function Startup({ apiBase, machineApiBase = apiBase, connected = false }: { apiBase: string; machineApiBase?: string; connected?: boolean }) { const { t } = useI18n(); - const autostart = useCodexAutostart(apiBase); const cacheKey = `${STARTUP_PAGE_CACHE_PREFIX}${apiBase}`; const cached = useMemo(() => readSessionListCache(cacheKey), [cacheKey]); const startupResourceKey = `startup-page:${apiBase}`; @@ -381,7 +379,6 @@ export default function Startup({ apiBase, machineApiBase = apiBase, connected = installBusy={installBusy} installResult={installResult} onInstall={(action, opts) => { void runInstallAction(action, opts); }} - autostart={autostart} /> {data.platform === "win32" && ( {status}} {state.showError && {t("sub.loadFail")}} - {/* - Five cards. "Active days" was the sixth — on a 7/30-day range with a heatmap below it - said nothing the range did not. The page's counting caveat rides on the coverage - card as a focusable info button rather than a paragraph above everything. - */} -
+
{t("usage.card.requests")}
{summary.requests}
{t("usage.card.measured")}
{summary.measuredRequests}
{t("usage.card.totalTokens")}
{formatTokens(summary.totalTokens, locale)}
@@ -300,21 +296,13 @@ function UsageSummaryCards({
)}
-
-
- {t("usage.card.coverage")} - - -
-
{formatPct(summary.coverageRatio)}
-
+
{t("usage.card.coverage")}
{formatPct(summary.coverageRatio)}
+
{t("usage.card.activeDays")}
{activeDays}
{summary.estimatedCostUsd !== undefined && (
{t("usage.cost.total")} - + {formatUsdEstimate(summary.estimatedCostUsd, locale)} {t("usage.cost.disclaimer")} @@ -399,40 +387,22 @@ function UsageHeatmapPanel({ const heatmapRef = useRef(null); const [hoverCell, setHoverCell] = useState<{ weekIndex: number; dayIndex: number; x: number; y: number } | null>(null); - // Component-scoped so both the effect and the details' onToggle can call it: a closed - // details has zero width, so the effect's run is a no-op until the user opens it. - const pinRight = useCallback(() => { - const element = heatmapRef.current; - if (element) element.scrollLeft = element.scrollWidth; - }, []); - useEffect(() => { const element = heatmapRef.current; if (!element) return; + const pinRight = () => { element.scrollLeft = element.scrollWidth; }; pinRight(); const observer = new ResizeObserver(pinRight); observer.observe(element); return () => observer.disconnect(); - }, [heatmap, range, pinRight]); - - if (range === "7d") { - return ( -
-

{t("usage.section.heatmap")}

- -
- ); - } + }, [heatmap, range]); - /* - The year heatmap is GitHub-style chrome for a 30-day range: mostly empty and tall. It - stays available behind a closed disclosure; the seven-day bars above are small enough - to keep inline. - */ return ( -
- {t("usage.section.heatmap")} - {( +
+

{t("usage.section.heatmap")}

+ {range === "7d" ? ( + + ) : (
@@ -483,7 +453,7 @@ function UsageHeatmapPanel({
)} -
+
); } @@ -684,6 +654,7 @@ function UsageWorkspaceBody({ data, heatmap, weekBars, + activeDays, filteredModels, modelQuery, onModelQuery, @@ -695,6 +666,7 @@ function UsageWorkspaceBody({ data: UsageResponse | null; heatmap: ReturnType; weekBars: UsageDay[]; + activeDays: number; filteredModels: UsageModel[]; modelQuery: string; onModelQuery: (query: string) => void; @@ -711,7 +683,7 @@ function UsageWorkspaceBody({ meta: data ? `${data.summary.requests}` : "—", body: data ? ( <> - + ) : null, @@ -816,6 +788,7 @@ export default function Usage({ apiBase, connected = false, apiKeyId }: { apiBas const heatmap = useMemo(() => buildHeatmap(data?.days ?? []), [data?.days]); const weekBars = useMemo(() => lastSevenDays(data?.days ?? []), [data?.days]); + const activeDays = useMemo(() => (data?.days ?? []).filter(d => d.requests > 0).length, [data?.days]); const filteredModels = useMemo(() => { const q = modelQuery.trim().toLowerCase(); const models = data?.models ?? []; @@ -839,6 +812,7 @@ export default function Usage({ apiBase, connected = false, apiKeyId }: { apiBas

{t("usage.title")}

+

{t("usage.subtitle")}

{/* Only shown when connected. Naming the source is a two-plane concept: it answers "which store served these numbers", and that question only exists once there are @@ -890,6 +864,7 @@ export default function Usage({ apiBase, connected = false, apiKeyId }: { apiBas data={data} heatmap={heatmap} weekBars={weekBars} + activeDays={activeDays} filteredModels={filteredModels} modelQuery={modelQuery} onModelQuery={setModelQuery} diff --git a/gui/src/pages/dashboard-core-poll.ts b/gui/src/pages/dashboard-core-poll.ts index 59f4ff6015..f6bb653452 100644 --- a/gui/src/pages/dashboard-core-poll.ts +++ b/gui/src/pages/dashboard-core-poll.ts @@ -12,10 +12,19 @@ import { type ProjectCodexConfigGroup, type ProviderInfo, type SettingsData, + type ShadowCallData, type SidecarData, type UsageSummary30d, } from "./dashboard-shared"; +export type InjectionPoll = { + multiAgentGuidanceEnabled: boolean; + syncCodexSubagentDefaults: boolean; + injectionModel: string; + injectionEffort: string; + injectionEfforts: string[]; + injectionAvailable: Array<{ provider: string; model: string; namespaced: string }>; +}; export type InjectionSelectionResponse = { multiAgentGuidanceEnabled?: boolean; @@ -33,6 +42,10 @@ export function normalizeInjectionSelection(data: InjectionSelectionResponse) { }; } +export type EffortCapPoll = { + effortCap: string; + subagentEffortCap: string; +}; export type DashboardOverviewPoll = { health: HealthData | null; @@ -40,10 +53,21 @@ export type DashboardOverviewPoll = { error: boolean; }; +/** Multi-agent extras — slower peers must not gate status/uptime/provider counts. */ +export type DashboardMultiAgentPoll = { + /** Absent when the optional endpoint failed — callers must keep prior UI state. */ + injection: InjectionPoll | undefined; + effortCaps: EffortCapPoll | undefined; +}; -/** Sidecar only — must not wait on /api/settings (startup-health). */ +/** Sidecar + shadow only — must not wait on /api/settings (startup-health). */ export type DashboardSidecarPoll = { sidecar: SidecarData; + /** + * `null` = authoritative endpoint failure (clear UI). + * `undefined` = lost poll authority (epoch gate) — do not commit. + */ + shadowCall: ShadowCallData | null | undefined; }; export type DashboardSettingsPoll = { @@ -52,11 +76,17 @@ export type DashboardSettingsPoll = { startupHealthSeed: SettingsData["startupHealth"] | null | undefined; }; +export type DashboardMaModePoll = { + maMode: "v1" | "default" | "v2"; +}; export type DashboardEpochRefs = { settingsRequestEpochRef: { current: number }; settingsMutationEpochRef: { current: number }; settingsMutationInFlightRef: { current: boolean }; + shadowCallRequestEpochRef: { current: number }; + shadowCallMutationEpochRef: { current: number }; + shadowCallMutationInFlightRef: { current: boolean }; }; function isAbortError(error: unknown, signal: AbortSignal): boolean { @@ -111,14 +141,62 @@ export async function fetchDashboardUsage(apiBase: string, signal: AbortSignal): return requireJson(response); } -/** Web-search / vision sidecar — a config read, typically sub-10ms. */ +/** Web-search / vision sidecar + shadow-call — config reads, typically sub-10ms. */ export async function fetchDashboardSidecars( apiBase: string, signal: AbortSignal, + epochs: DashboardEpochRefs, ): Promise { - const scRes = await fetch(`${apiBase}/api/sidecar-settings`, { signal }); + // Only bump shadow epochs — settings has its own poll and must not be invalidated here. + const { request: shadowRequestEpoch, mutation: shadowMutationEpoch } = beginPollEpoch( + epochs.shadowCallRequestEpochRef, + epochs.shadowCallMutationEpochRef, + ); + + const [scRes, shRes] = await Promise.all([ + fetch(`${apiBase}/api/sidecar-settings`, { signal }), + fetch(`${apiBase}/api/shadow-call-settings`, { signal }), + ]); + const sidecar = await requireJson(scRes); - return { sidecar }; + let shadowCall: ShadowCallData | null | undefined = undefined; + try { + if (shRes.ok) { + const nextShadow = await shRes.json() as ShadowCallData; + if (settingsPollMayCommit( + { request: shadowRequestEpoch, mutation: shadowMutationEpoch }, + { + request: epochs.shadowCallRequestEpochRef.current, + mutation: epochs.shadowCallMutationEpochRef.current, + mutationInFlight: epochs.shadowCallMutationInFlightRef.current, + }, + )) { + shadowCall = nextShadow; + } + } else if (settingsPollMayCommit( + { request: shadowRequestEpoch, mutation: shadowMutationEpoch }, + { + request: epochs.shadowCallRequestEpochRef.current, + mutation: epochs.shadowCallMutationEpochRef.current, + mutationInFlight: epochs.shadowCallMutationInFlightRef.current, + }, + )) { + shadowCall = null; + } + } catch { + if (settingsPollMayCommit( + { request: shadowRequestEpoch, mutation: shadowMutationEpoch }, + { + request: epochs.shadowCallRequestEpochRef.current, + mutation: epochs.shadowCallMutationEpochRef.current, + mutationInFlight: epochs.shadowCallMutationInFlightRef.current, + }, + )) { + shadowCall = null; + } + } + + return { sidecar, shadowCall }; } /** Codex auto-start + startup-health seed — can be slower because settings embeds startup probe. */ @@ -151,6 +229,25 @@ export async function fetchDashboardSettings( return { settings, startupHealthSeed }; } +/** Multi-agent mode toggle only — must not wait on injection-model / effort-caps. */ +export async function fetchDashboardMaMode( + apiBase: string, + signal: AbortSignal, +): Promise { + try { + const v2Res = await fetch(`${apiBase}/api/v2`, { signal }); + if (!v2Res.ok) return { maMode: "default" }; + const v2Data = await v2Res.json() as { multiAgentMode?: unknown }; + if (v2Data.multiAgentMode === "v1" || v2Data.multiAgentMode === "v2") { + return { maMode: v2Data.multiAgentMode }; + } + return { maMode: "default" }; + } catch (error) { + if (isAbortError(error, signal)) throw error; + return { maMode: "default" }; + } +} + export async function fetchDashboardOverview( apiBase: string, signal: AbortSignal, @@ -168,3 +265,40 @@ export async function fetchDashboardOverview( } } +export async function fetchDashboardMultiAgent( + apiBase: string, + signal: AbortSignal, +): Promise { + const [imRes, ecRes] = await Promise.all([ + fetch(`${apiBase}/api/injection-model`, { signal }).catch(() => null), + fetch(`${apiBase}/api/effort-caps`, { signal }).catch(() => null), + ]); + + let injection: InjectionPoll | undefined; + try { + if (imRes?.ok) { + const imData = await imRes.json() as InjectionSelectionResponse & { + efforts?: string[]; + available?: InjectionPoll["injectionAvailable"]; + }; + injection = { + ...normalizeInjectionSelection(imData), + injectionEfforts: imData.efforts ?? [], + injectionAvailable: imData.available ?? [], + }; + } + } catch { /* old server / malformed — keep prior UI state */ } + + let effortCaps: EffortCapPoll | undefined; + try { + if (ecRes?.ok) { + const ecData = await ecRes.json() as { effortCap?: string | null; subagentEffortCap?: string | null }; + effortCaps = { + effortCap: ecData.effortCap ?? "", + subagentEffortCap: ecData.subagentEffortCap ?? "", + }; + } + } catch { /* old server */ } + + return { injection, effortCaps }; +} diff --git a/gui/src/pages/dashboard-dialogs.tsx b/gui/src/pages/dashboard-dialogs.tsx index 35957bd8b1..7a53bb0ff8 100644 --- a/gui/src/pages/dashboard-dialogs.tsx +++ b/gui/src/pages/dashboard-dialogs.tsx @@ -1,11 +1,11 @@ import { IconAlert, IconRefresh, IconX } from "../icons"; import { EmptyState, Select } from "../ui"; -import { GithubStarButton } from "../components/github-star-button"; import { updateReasonLabel, type UpdateChannel, } from "./dashboard-shared"; import type { useDashboardData } from "./use-dashboard-data"; +import { shadowSourceModelLabel } from "./shadow-call-source"; type Dash = ReturnType; @@ -15,6 +15,10 @@ export function DashboardDialogs(d: Dash) { updateOpen, closeUpdateDialog, updateDialogRef, updateChannel, changeUpdateChannel, updateLoading, updateError, updateCheck, fetchUpdateCheck, updateRestart, setUpdateRestart, runUpdate, + maHelpOpen, setMaHelpOpen, maHelpDialogRef, + effortCapHelpOpen, setEffortCapHelpOpen, effortCapHelpDialogRef, + shadowCallHelpOpen, setShadowCallHelpOpen, shadowCallHelpDialogRef, + shadowCall, } = d; return ( @@ -118,12 +122,6 @@ export function DashboardDialogs(d: Dash) {
)}
- {/* - Mounted only while the dialog is open (the itself is always in the - tree and only toggles display), so the star poll runs only while the user is - looking at the project's own screen. - */} - {updateOpen && }
+ + { event.preventDefault(); setMaHelpOpen(false); }} + > + +
+
+ {t("models.v2Help")} +
+
+ + {t("models.v2DocsLink")} + +
+
+ +
+
+ + + { event.preventDefault(); setEffortCapHelpOpen(false); }} + > + + +
+ {t("dash.effortCapHelp")} +
+
+ +
+ +
+ + { event.preventDefault(); setShadowCallHelpOpen(false); }} + > + + +
+ {t("dash.shadowCallTooltip", { models: shadowSourceModelLabel(shadowCall?.sourceModels) })} +
+
+ +
+ +
); } diff --git a/gui/src/pages/dashboard-models-section.tsx b/gui/src/pages/dashboard-models-section.tsx new file mode 100644 index 0000000000..b839828e59 --- /dev/null +++ b/gui/src/pages/dashboard-models-section.tsx @@ -0,0 +1,83 @@ +import { type Dispatch, type SetStateAction } from "react"; +import { IconChevron, IconSearch } from "../icons"; +import type { TFn } from "../i18n/shared"; +import { EmptyState } from "../ui"; +import { formatProviderDisplayName } from "../provider-icons"; +import type { ModelInfo } from "./dashboard-shared"; + +export function DashboardModelsSection({ + t, + models, + modelsLoading, + modelQuery, + setModelQuery, + filteredGroups, + expandedProviders, + setExpandedProviders, +}: { + t: TFn; + models: ModelInfo[]; + modelsLoading: boolean; + modelQuery: string; + setModelQuery: (v: string) => void; + filteredGroups: Array<[string, ModelInfo[]]>; + expandedProviders: Set; + setExpandedProviders: Dispatch>>; +}) { + return ( + <> +
+ {t("dash.availableModels")} {models.length} + {modelsLoading && } +
+ {models.length === 0 && !modelsLoading ? ( + + ) : ( + <> +
+
+ {filteredGroups.length === 0 ? ( +

{t("dash.modelsNoResults")}

+ ) : ( +
+ {filteredGroups.map(([provider, rows]) => { + const q = modelQuery.trim().toLowerCase(); + const open = q !== "" || expandedProviders.has(provider); + return ( +
+ + {open && ( +
+ {rows.map(m => ( + {m.id} + ))} +
+ )} +
+ ); + })} +
+ )} + + )} + + ); +} diff --git a/gui/src/pages/dashboard-overview-head.tsx b/gui/src/pages/dashboard-overview-head.tsx index a768dee561..9cccc0e26c 100644 --- a/gui/src/pages/dashboard-overview-head.tsx +++ b/gui/src/pages/dashboard-overview-head.tsx @@ -1,5 +1,5 @@ -import { IconAlert } from "../icons"; -import { useT } from "../i18n/shared"; +import { IconAlert, IconInfo } from "../icons"; +import { type TKey, useT } from "../i18n/shared"; import { formatTokens } from "../format-tokens"; import { formatUptime } from "../formatUptime"; import { navigateHash } from "../hash-routing"; @@ -16,29 +16,68 @@ export function DashboardOverviewHead({ healthLoading, startupHealth, projectConfigWarnings, -}: Pick) { - const t = useT(); + maMode, + maBusy, + maHelpTriggerRef, + maHelpOpen, + setMaHelpOpen, + switchMaMode, + maError, +}: Pick) { + const t = useT(); const online = health?.status === "ok"; return ( <>
- {/* - Three cards: reachability, capacity, volume. Version and uptime used to be cards - of their own; they are diagnosis facts, not decisions, so they ride as the status - card's sub-line (visible, not a title attribute). The subagent v1/base/v2 switch - that opened this row is a Subagents setting and lives there now. - */}
-
-
{t("dash.status")}
+
+
+ {t("dash.multiAgent")} + +
+
+
+ {(["v1", "default", "v2"] as const).map(mode => ( + + ))} +
+
+ {maError && ( +
+ {maError} +
+ )} +
+
+
{t("dash.status")}
{online ? t("dash.online") : t("dash.offline")}
-
- {health ? `v${health.version} · ${formatUptime(health.uptime, locale)}` : "\u00a0"} -
+
{t("dash.version")}
{health?.version ?? "—"}
+
{t("dash.uptime")}
{health ? formatUptime(health.uptime, locale) : "—"}
{t("dash.providers")}
{providers.length}
{t("dash.tokens30d")}
diff --git a/gui/src/pages/dashboard-overview-panels.tsx b/gui/src/pages/dashboard-overview-panels.tsx index adfae2326a..8009d04423 100644 --- a/gui/src/pages/dashboard-overview-panels.tsx +++ b/gui/src/pages/dashboard-overview-panels.tsx @@ -1,7 +1,8 @@ import MemoryObservabilityCard from "../components/MemoryObservabilityCard"; -import { useT } from "../i18n/shared"; import type { useDashboardData } from "./use-dashboard-data"; import { + DashboardEffortCapPanel, + DashboardInjectionPanel, DashboardMaintenancePanel, DashboardSidecarPanels, } from "./dashboard-overview-sections"; @@ -9,19 +10,14 @@ import { type Dash = ReturnType; export function DashboardOverviewPanels(props: Dash) { - const t = useT(); return ( <> - - {/* - Web-search and vision sidecars are routing configuration, not health. They stay on - the dashboard (there is no better home yet) but closed by default, so the first - viewport is the state of the proxy rather than two model pickers. - */} -
- {t("dash.sidecars")} - -
+ +
+ + +
+ ); diff --git a/gui/src/pages/dashboard-overview-sections.tsx b/gui/src/pages/dashboard-overview-sections.tsx index d1a86b3f65..7bdf282931 100644 --- a/gui/src/pages/dashboard-overview-sections.tsx +++ b/gui/src/pages/dashboard-overview-sections.tsx @@ -1,15 +1,20 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from "react"; import { createPortal } from "react-dom"; -import { IconAlert, IconCheck, IconChevron, IconRefresh, IconX } from "../icons"; +import { IconAlert, IconCheck, IconChevron, IconInfo, IconRefresh, IconX } from "../icons"; import { Trans } from "../i18n/provider"; import type { TFn } from "../i18n/shared"; import { Select } from "../ui"; import { computeSelectMenuStyle } from "../select-position"; +import { formatNamespacedModelId } from "../provider-icons"; +import { navigateHash } from "../hash-routing"; import { clampVisionReasoningToLadder, + EFFORT_CAP_LEVELS, parsePositiveInteger, parseVisionTimeoutMs, + requireJson, type SidecarPatch, + shadowCallModelOptions, webSearchSidecarSelectionForModel, updateJobLabel, visionEnabledPatch, @@ -24,10 +29,136 @@ import { VISION_TIMEOUT_MS_MAX, VISION_TIMEOUT_MS_MIN, } from "./dashboard-shared"; +import { shadowSourceModelBadge } from "./shadow-call-source"; import type { useDashboardData } from "./use-dashboard-data"; type Dash = ReturnType; +export function DashboardEffortCapPanel({ apiBase, d }: { apiBase: string; d: Dash }) { + const { + t, maMode, maModeResolved, + effortCapHelpTriggerRef, effortCapHelpOpen, setEffortCapHelpOpen, + effortCap, subagentEffortCap, effortCapSaving, setEffortCap, setSubagentEffortCap, setEffortCapSaving, + } = d; + + if (!maModeResolved || maMode === "v1") return null; + + return ( +
+
+ + {t("dash.effortCapLabel")} + + + ({ value: e, label: e })), + ]} + onChange={async (v) => { + if (effortCapSaving) return; + setEffortCapSaving(true); + try { + const res = await fetch(`${apiBase}/api/effort-caps`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ subagentEffortCap: v || null }), + }); + const data = await requireJson<{ ok: boolean; effortCap?: string | null; subagentEffortCap?: string | null }>(res); + setEffortCap(data.effortCap ?? ""); + setSubagentEffortCap(data.subagentEffortCap ?? ""); + } catch { /* ignore */ } + finally { setEffortCapSaving(false); } + }} + disabled={effortCapSaving} + label={t("dash.subagentEffortCapLabel")} + align="right" + /> +
+
+ ); +} + +export function DashboardInjectionPanel({ d }: { apiBase: string; d: Dash }) { + const { + t, injectionModel, injectionEffort, injectionEfforts, injectionAvailable, injectionSaving, + saveInjection, + } = d; + + return ( +
+
{t("dash.injectionLabel")}
+
+ ({ value: e, label: e })), + ]} + onChange={(v) => { void saveInjection({ model: injectionModel || null, effort: v || null }); }} + disabled={injectionSaving} + label={t("dash.injectionEffortLabel")} + align="right" + /> + )} + +
+
+ ); +} + export function DashboardMaintenancePanel({ d }: { d: Dash }) { const { t, runSync, syncing, updateTriggerRef, openUpdateDialog, updateLoading, updateOpen, @@ -305,8 +436,9 @@ function VisionAdvancedPopover({ t, open, triggerRef, onClose, maxValue, maxInva export function DashboardSidecarPanels({ d }: { d: Dash }) { const { - t, + t, settings, settingsSaving, toggleCodexAutoStart, sidecar, sidecarSaving, sidecarModels, visionModels, models, saveSidecar, + shadowCall, shadowCallSaving, shadowCallHelpTriggerRef, shadowCallHelpOpen, setShadowCallHelpOpen, saveShadowCall, } = d; const visionEnabled = sidecar?.vision.enabled !== false; const visionModel = visionEnabled ? (sidecar?.vision.model ?? "gpt-5.4-mini") : ""; @@ -352,6 +484,25 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) { return ( <> +
+
+
+
{t("dash.codexAutoStart")}
+
{t("dash.codexAutoStartHint")}
+
+ +
+
+
{/* Both sidecar cards wear the DashboardInjectionPanel shell: the PANEL is the flex row, copy left, controls right. */} @@ -471,6 +622,48 @@ export function DashboardSidecarPanels({ d }: { d: Dash }) { )}
+ +
+
+
+ {t("dash.shadowCallIntercept")} + + {`⚠ ${shadowSourceModelBadge(shadowCall?.sourceModels)}`} +
+
+ +