Skip to content

Commit 044d38d

Browse files
committed
feat(web): add a per-agent shortcut into CLI proxy settings
Settings → Execution → Local CLI hides per-agent proxy and custom-path configuration behind a collapsed "Advanced: proxy & custom paths" disclosure below the agent grid. Users routing Claude Code or Codex through a third-party endpoint had no signpost telling them those fields exist, so the common BYOK/proxy setup was effectively undiscoverable. Add a quiet shortcut to each installed agent card that opens that disclosure and scrolls it into view. Two properties are deliberate and are covered by tests: - It renders whether or not an endpoint is already configured. Gating it on "configured" inverts its own purpose, because the user who needs to find the proxy fields is exactly the one who has not filled them in. Configured state only changes how the shortcut reads, never whether it appears. - It is not Claude-specific. Claude and Codex both declare a base URL, auth keys and custom paths, so visibility is derived from AGENT_CLI_ENV_FIELDS rather than an agent id, and any agent that gains CLI env fields picks the shortcut up for free. Configured detection reads every stored key for the agent instead of the known field list, so values written out-of-band by `od` still count -- the daemon accepts ANTHROPIC_AUTH_TOKEN, which the panel has no field for. Presented as quiet inline text rather than a second pill: the card's action row already owns one affordance in Test, and Test performs work while this only navigates, so equal visual weight would read as two peer actions. It stays a real <button> outside the card-selection control, which is what keeps keyboard and assistive-technology activation working. i18n: replace the Claude-specific strings with settings.cliEnvShortcut and settings.cliEnvShortcutConfigured across all 19 locales, deriving each locale's wording from its own cliEnvTitle so the shortcut and the disclosure it opens read as the same thing.
1 parent a8ec578 commit 044d38d

23 files changed

Lines changed: 466 additions & 0 deletions

File tree

apps/web/src/components/SettingsDialog.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,20 @@ export function SettingsDialog({
16731673
const settingsContentRef = useRef<HTMLDivElement | null>(null);
16741674
// AMR-card focus, driven by the failed-run nudge (`initialHighlight==='amr'`).
16751675
const amrCardRef = useRef<HTMLDivElement | null>(null);
1676+
// CLI env disclosure focus, driven by the per-agent card shortcut.
1677+
const agentCliEnvDetailsRef = useRef<HTMLDetailsElement | null>(null);
1678+
// Opens the "Advanced: proxy & custom paths" disclosure and brings it into
1679+
// view. The disclosure is a plain uncontrolled <details>, so `open` is set on
1680+
// the DOM node rather than through React state; scrolling waits a frame so it
1681+
// measures the expanded height instead of the collapsed one.
1682+
const revealAgentCliEnvSection = useCallback(() => {
1683+
const details = agentCliEnvDetailsRef.current;
1684+
if (!details) return;
1685+
details.open = true;
1686+
requestAnimationFrame(() => {
1687+
details.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
1688+
});
1689+
}, []);
16761690
// Card pulse: a brief attention flash that auto-clears after a few seconds.
16771691
const [amrHighlightActive, setAmrHighlightActive] = useState(false);
16781692
// Coachmark: persists (unlike the card pulse) until the real pointer reaches
@@ -4728,6 +4742,34 @@ export function SettingsDialog({
47284742
hoveredAgentCardId === a.id &&
47294743
!amrCardSignedIn &&
47304744
amrCardStatus?.loginInFlight === true;
4745+
// Shortcut into the "Advanced: proxy & custom paths"
4746+
// disclosure. Two things this must not do:
4747+
//
4748+
// 1. Gate on "already configured" — that inverted its
4749+
// own purpose, since the user who needs to *find*
4750+
// the proxy fields is exactly the one who has not
4751+
// filled them in yet.
4752+
// 2. Single out one agent. Claude and Codex are
4753+
// structurally identical here (both declare a base
4754+
// URL, auth keys, and custom paths), so visibility
4755+
// is derived from the field table rather than an
4756+
// agent id — any agent that gains CLI env fields
4757+
// gets the shortcut for free.
4758+
const showCliEnvShortcut =
4759+
active &&
4760+
AGENT_CLI_ENV_FIELDS.some(
4761+
(field) => field.agentId === a.id,
4762+
);
4763+
// Configured only changes how the shortcut reads
4764+
// (offer vs. status), never whether it renders. Reads
4765+
// every stored key rather than the known field list so
4766+
// values written out-of-band by `od` (e.g. the daemon
4767+
// also accepts ANTHROPIC_AUTH_TOKEN) still count.
4768+
const hasCliEnvOverrides =
4769+
showCliEnvShortcut &&
4770+
Object.values(cfg.agentCliEnv?.[a.id] ?? {}).some(
4771+
(value) => Boolean(value),
4772+
);
47314773
const cardEl = (
47324774
<div
47334775
key={a.id}
@@ -4973,6 +5015,48 @@ export function SettingsDialog({
49735015
/>
49745016
)
49755017
) : null}
5018+
{showCliEnvShortcut ? (
5019+
<Button
5020+
variant="ghost"
5021+
className={
5022+
'agent-card-cli-env-link' +
5023+
(hasCliEnvOverrides
5024+
? ' agent-card-cli-env-link--configured'
5025+
: '')
5026+
}
5027+
// Not `settings-agent-card-*`: that prefix
5028+
// is the agent cards' own namespace and is
5029+
// enumerated by regex elsewhere.
5030+
data-testid={`settings-cli-env-shortcut-${a.id}`}
5031+
data-configured={
5032+
hasCliEnvOverrides ? 'true' : 'false'
5033+
}
5034+
onClick={(e) => {
5035+
e.stopPropagation();
5036+
revealAgentCliEnvSection();
5037+
}}
5038+
>
5039+
{/* The label carries the hover underline on
5040+
its own: text and chevron are separate
5041+
flex items, so underlining the whole
5042+
control leaves a visible break across
5043+
the gap between them. */}
5044+
<span className="agent-card-cli-env-link__label">
5045+
{t('settings.cliEnvShortcut')}
5046+
</span>
5047+
{hasCliEnvOverrides ? (
5048+
<VisuallyHidden>
5049+
{`, ${t('settings.cliEnvShortcutConfigured')}`}
5050+
</VisuallyHidden>
5051+
) : null}
5052+
<span
5053+
className="agent-card-cli-env-link__chevron"
5054+
aria-hidden="true"
5055+
>
5056+
5057+
</span>
5058+
</Button>
5059+
) : null}
49765060
{active && !isAmrAgent ? (
49775061
<button
49785062
type="button"
@@ -5313,6 +5397,7 @@ export function SettingsDialog({
53135397
if (cliEnvFields.length === 0) return null;
53145398
return (
53155399
<details
5400+
ref={agentCliEnvDetailsRef}
53165401
className="agent-cli-env"
53175402
data-testid="settings-cli-env"
53185403
>

apps/web/src/i18n/locales/ar.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const ar: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'تم تحديث النماذج من CLI المثبت.',
610610
'settings.modelPickerFallbackHint': 'يتم عرض الإعدادات الافتراضية المضمنة. انقر على إعادة المسح لجلب النماذج المباشرة من CLI.',
611611
'settings.cliEnvTitle': 'متقدّم: الوكيل والمسارات المخصّصة',
612+
'settings.cliEnvShortcut': 'الوكيل والمسارات المخصّصة',
613+
'settings.cliEnvShortcutConfigured': 'مُهيّأ',
612614
'settings.cliEnvHint': 'استخدم هذه الخيارات لتجاوز بيئة CLI المحدّد: مفاتيح API، وعناوين base URL للوكيل، ومجلدات home المخصّصة، أو مسارات تنفيذ غير قياسية. عند عدم ضبط base URL، يستخدم CLI نقطة النهاية الافتراضية الخاصة به. تبقى الأسرار في إعدادات التطبيق المحلية ولا يراها سوى CLI المحدّد.',
613615
'settings.cliEnvClaudeConfigDir': 'دليل إعدادات Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'Base URL لوكيل Claude',

apps/web/src/i18n/locales/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const de: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'Modelle wurden aus der installierten CLI aktualisiert.',
610610
'settings.modelPickerFallbackHint': 'Integrierte Standardwerte werden angezeigt. Klicken Sie auf Neu scannen, um Live-Modelle aus der CLI abzurufen.',
611611
'settings.cliEnvTitle': 'Erweitert: Proxy und benutzerdefinierte Pfade',
612+
'settings.cliEnvShortcut': 'Proxy und benutzerdefinierte Pfade',
613+
'settings.cliEnvShortcutConfigured': 'Konfiguriert',
612614
'settings.cliEnvHint': 'Nutze diese Felder, um die Umgebung der ausgewählten CLI zu überschreiben: API-Keys, Proxy-Base-URLs, eigene Homes oder nicht standardmäßige Binary-Pfade. Ohne Base URL nutzt die CLI ihren Standard-Endpunkt. Secrets bleiben in der lokalen App-Konfiguration und werden nur an die ausgewählte CLI weitergegeben.',
613615
'settings.cliEnvClaudeConfigDir': 'Claude Code-Konfigurationsverzeichnis',
614616
'settings.cliEnvClaudeBaseUrl': 'Claude-Proxy-Base-URL',

apps/web/src/i18n/locales/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const en: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'Model list comes from this CLI.',
610610
'settings.modelPickerFallbackHint': 'Showing built-in defaults. Click Rescan to pull live models from the CLI.',
611611
'settings.cliEnvTitle': 'Advanced: proxy & custom paths',
612+
'settings.cliEnvShortcut': 'Proxy & custom paths',
613+
'settings.cliEnvShortcutConfigured': 'Configured',
612614
'settings.cliEnvHint': 'Use these to override the selected CLI environment: API keys, proxy base URLs, custom homes, or non-standard binary paths. Without a base URL, the CLI uses its default endpoint. Secrets stay in local app config and only the selected CLI sees them.',
613615
'settings.cliEnvClaudeConfigDir': 'Claude Code config directory',
614616
'settings.cliEnvClaudeBaseUrl': 'Claude proxy base URL',

apps/web/src/i18n/locales/es-ES.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const esES: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'Los modelos se actualizaron desde la CLI instalada.',
610610
'settings.modelPickerFallbackHint': 'Mostrando valores predeterminados integrados. Pulsa Reescanear para obtener modelos en vivo desde la CLI.',
611611
'settings.cliEnvTitle': 'Avanzado: proxy y rutas personalizadas',
612+
'settings.cliEnvShortcut': 'Proxy y rutas personalizadas',
613+
'settings.cliEnvShortcutConfigured': 'Configurado',
612614
'settings.cliEnvHint': 'Usa estos campos para sobrescribir el entorno de la CLI seleccionada: API keys, base URLs de proxy, homes personalizados o rutas de binarios no estándar. Sin base URL, la CLI usa su endpoint predeterminado. Los secretos permanecen en la configuración local de la app y solo los ve la CLI seleccionada.',
613615
'settings.cliEnvClaudeConfigDir': 'Directorio de configuración de Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'Base URL del proxy de Claude',

apps/web/src/i18n/locales/fa.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const fa: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'مدل‌ها از CLI نصب‌شده به‌روزرسانی شدند.',
610610
'settings.modelPickerFallbackHint': 'پیش‌فرض‌های داخلی نمایش داده می‌شوند. برای دریافت مدل‌های زنده از CLI روی اسکن مجدد کلیک کنید.',
611611
'settings.cliEnvTitle': 'پیشرفته: پراکسی و مسیرهای سفارشی',
612+
'settings.cliEnvShortcut': 'پراکسی و مسیرهای سفارشی',
613+
'settings.cliEnvShortcutConfigured': 'پیکربندی‌شده',
612614
'settings.cliEnvHint': 'از این گزینه‌ها برای بازنویسی محیط CLI انتخاب‌شده استفاده کنید: API key، base URL پراکسی، home سفارشی یا مسیر اجرایی غیر استاندارد. اگر base URL تنظیم نشود، CLI از endpoint پیش‌فرض خودش استفاده می‌کند. اطلاعات محرمانه در تنظیمات محلی برنامه می‌مانند و فقط CLI انتخاب‌شده آن‌ها را می‌بیند.',
613615
'settings.cliEnvClaudeConfigDir': 'پوشه پیکربندی Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'Base URL پروکسی Claude',

apps/web/src/i18n/locales/fr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const fr: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'Les modèles ont été actualisés depuis la CLI installée.',
610610
'settings.modelPickerFallbackHint': 'Valeurs par défaut intégrées affichées. Cliquez sur Réanalyser pour récupérer les modèles en direct depuis la CLI.',
611611
'settings.cliEnvTitle': 'Avancé : proxy et chemins personnalisés',
612+
'settings.cliEnvShortcut': 'Proxy et chemins personnalisés',
613+
'settings.cliEnvShortcutConfigured': 'Configuré',
612614
'settings.cliEnvHint': 'Utilisez ces champs pour remplacer l’environnement de la CLI sélectionnée : clés API, base URLs de proxy, homes personnalisés ou chemins de binaires non standard. Sans base URL, la CLI utilise son endpoint par défaut. Les secrets restent dans la configuration locale de l’app et seule la CLI sélectionnée les voit.',
613615
'settings.cliEnvClaudeConfigDir': 'Dossier de configuration Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'URL de base du proxy Claude',

apps/web/src/i18n/locales/hu.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const hu: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'A modellek frissültek a telepített CLI-ből.',
610610
'settings.modelPickerFallbackHint': 'A beépített alapértékek láthatók. Kattints az Újraellenőrzésre az élő CLI-modellek lekéréséhez.',
611611
'settings.cliEnvTitle': 'Speciális: proxy és egyéni útvonalak',
612+
'settings.cliEnvShortcut': 'Proxy és egyéni útvonalak',
613+
'settings.cliEnvShortcutConfigured': 'Beállítva',
612614
'settings.cliEnvHint': 'Ezekkel írhatod felül a kiválasztott CLI környezetét: API-kulcsok, proxy base URL-ek, egyéni home-ok vagy nem szabványos bináris útvonalak. Base URL nélkül a CLI a saját alapértelmezett endpointját használja. A titkok a helyi alkalmazás-konfigurációban maradnak, és csak a kiválasztott CLI kapja meg őket.',
613615
'settings.cliEnvClaudeConfigDir': 'Claude Code konfigurációs könyvtár',
614616
'settings.cliEnvClaudeBaseUrl': 'Claude proxy Base URL',

apps/web/src/i18n/locales/id.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const id: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'Model diperbarui dari CLI yang terpasang.',
610610
'settings.modelPickerFallbackHint': 'Menampilkan default bawaan. Klik Pindai ulang untuk mengambil model langsung dari CLI.',
611611
'settings.cliEnvTitle': 'Lanjutan: proxy & path kustom',
612+
'settings.cliEnvShortcut': 'Proxy & path kustom',
613+
'settings.cliEnvShortcutConfigured': 'Terkonfigurasi',
612614
'settings.cliEnvHint': 'Gunakan ini untuk menimpa environment CLI yang dipilih: API key, proxy base URL, home kustom, atau path binary non-standar. Tanpa base URL, CLI memakai endpoint defaultnya. Secret tetap berada di konfigurasi app lokal dan hanya dilihat oleh CLI yang dipilih.',
613615
'settings.cliEnvClaudeConfigDir': 'Direktori konfigurasi Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'Base URL proxy Claude',

apps/web/src/i18n/locales/it.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ export const it: Dict = {
609609
'settings.modelPickerLiveCatalogOnlyHint': 'I modelli sono stati aggiornati dalla CLI installata.',
610610
'settings.modelPickerFallbackHint': 'Mostra i valori predefiniti integrati. Clicca su Rianalizza per recuperare i modelli live dalla CLI.',
611611
'settings.cliEnvTitle': 'Avanzate: proxy e percorsi personalizzati',
612+
'settings.cliEnvShortcut': 'Proxy e percorsi personalizzati',
613+
'settings.cliEnvShortcutConfigured': 'Configurato',
612614
'settings.cliEnvHint': 'Usa questi campi per sovrascrivere l’ambiente della CLI selezionata: API key, base URL proxy, home personalizzate o percorsi di binari non standard. Senza base URL, la CLI usa il proprio endpoint predefinito. I segreti restano nella configurazione locale dell’app e li vede solo la CLI selezionata.',
613615
'settings.cliEnvClaudeConfigDir': 'Directory di configurazione Claude Code',
614616
'settings.cliEnvClaudeBaseUrl': 'Base URL del proxy Claude',

0 commit comments

Comments
 (0)