@cat-factory/app 0.47.0 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ import { computed, ref, watch } from 'vue'
9
9
  import type { SubscriptionVendor } from '~/types/domain'
10
10
  import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
11
11
 
12
+ const { t, n } = useI18n()
12
13
  const ui = useUiStore()
13
14
  const workspace = useWorkspaceStore()
14
15
  const creds = useVendorCredentialsStore()
@@ -25,27 +26,51 @@ const back = useIntegrationBack(open)
25
26
  // Initialised from the ui store so a caller can deep-link to a tab — the user-scoped
26
27
  // "My subscriptions" entry opens straight onto the `personal` tab.
27
28
  const activeTab = ref(ui.vendorCredentialsTab)
28
- const tabs = [
29
- { value: 'pool', label: 'Workspace pool', icon: 'i-lucide-users', slot: 'pool' },
30
- { value: 'direct', label: 'Direct providers', icon: 'i-lucide-key-round', slot: 'direct' },
31
- { value: 'proxy', label: 'Proxies', icon: 'i-lucide-route', slot: 'proxy' },
29
+ const tabs = computed(() => [
30
+ {
31
+ value: 'pool',
32
+ label: t('providers.vendorCredentials.tabs.pool'),
33
+ icon: 'i-lucide-users',
34
+ slot: 'pool',
35
+ },
36
+ {
37
+ value: 'direct',
38
+ label: t('providers.vendorCredentials.tabs.direct'),
39
+ icon: 'i-lucide-key-round',
40
+ slot: 'direct',
41
+ },
42
+ {
43
+ value: 'proxy',
44
+ label: t('providers.vendorCredentials.tabs.proxy'),
45
+ icon: 'i-lucide-route',
46
+ slot: 'proxy',
47
+ },
32
48
  {
33
49
  value: 'personal',
34
- label: 'Personal subscriptions',
50
+ label: t('providers.vendorCredentials.tabs.personal'),
35
51
  icon: 'i-lucide-user',
36
52
  slot: 'personal',
37
53
  },
38
- ]
54
+ ])
39
55
 
40
56
  // Only commercial coding-plan vendors that permit team/organization use are poolable here.
41
57
  // Claude, GLM and ChatGPT/Codex are licensed for individual use only, so they are connected
42
58
  // per-user in the "Personal subscriptions" section below (PersonalSubscriptionSection).
43
- const VENDORS: { value: SubscriptionVendor; label: string; harness: string }[] = [
44
- { value: 'kimi', label: 'Kimi — Moonshot coding plan', harness: 'Claude Code' },
45
- { value: 'deepseek', label: 'DeepSeek — coding plan', harness: 'Claude Code' },
46
- ]
59
+ // Labels resolve through i18n (reactive to the locale) via literal keys.
60
+ const VENDORS = computed<{ value: SubscriptionVendor; label: string; harness: string }[]>(() => [
61
+ {
62
+ value: 'kimi',
63
+ label: t('providers.vendorCredentials.vendors.kimi.label'),
64
+ harness: 'Claude Code',
65
+ },
66
+ {
67
+ value: 'deepseek',
68
+ label: t('providers.vendorCredentials.vendors.deepseek.label'),
69
+ harness: 'Claude Code',
70
+ },
71
+ ])
47
72
 
48
- const visibleVendors = computed(() => VENDORS)
73
+ const visibleVendors = computed(() => VENDORS.value)
49
74
 
50
75
  const vendor = ref<SubscriptionVendor>('kimi')
51
76
  const label = ref('')
@@ -68,22 +93,22 @@ const steps = computed<string[]>(() => {
68
93
  switch (vendor.value) {
69
94
  case 'kimi':
70
95
  return [
71
- 'Open your Moonshot (Kimi) coding-plan console and create an API key for the Anthropic-compatible endpoint.',
72
- 'Copy the API key. Agent steps will run via Claude Code against Moonshot’s Anthropic endpoint with full context.',
73
- 'Paste the key below.',
96
+ t('providers.vendorCredentials.vendors.kimi.step1'),
97
+ t('providers.vendorCredentials.vendors.kimi.step2'),
98
+ t('providers.vendorCredentials.vendors.kimi.step3'),
74
99
  ]
75
100
  case 'deepseek':
76
101
  return [
77
- 'Open your DeepSeek coding-plan console and create an API key for the Anthropic-compatible endpoint.',
78
- 'Copy the API key. Agent steps will run via Claude Code against DeepSeek’s Anthropic endpoint with full context.',
79
- 'Paste the key below.',
102
+ t('providers.vendorCredentials.vendors.deepseek.step1'),
103
+ t('providers.vendorCredentials.vendors.deepseek.step2'),
104
+ t('providers.vendorCredentials.vendors.deepseek.step3'),
80
105
  ]
81
106
  default:
82
107
  return []
83
108
  }
84
109
  })
85
110
 
86
- const tokenPlaceholder = computed(() => 'your coding-plan API key')
111
+ const tokenPlaceholder = computed(() => t('providers.vendorCredentials.tokenPlaceholder'))
87
112
 
88
113
  async function add() {
89
114
  if (!token.value.trim()) return
@@ -91,15 +116,21 @@ async function add() {
91
116
  try {
92
117
  await creds.add({
93
118
  vendor: vendor.value,
94
- label: label.value.trim() || `${vendor.value} token`,
119
+ label:
120
+ label.value.trim() ||
121
+ t('providers.vendorCredentials.defaultLabel', { vendor: vendor.value }),
95
122
  token: token.value.trim(),
96
123
  })
97
124
  token.value = ''
98
125
  label.value = ''
99
- toast.add({ title: 'Token connected', icon: 'i-lucide-check', color: 'success' })
126
+ toast.add({
127
+ title: t('providers.vendorCredentials.toast.connected'),
128
+ icon: 'i-lucide-check',
129
+ color: 'success',
130
+ })
100
131
  } catch (e) {
101
132
  toast.add({
102
- title: 'Could not connect token',
133
+ title: t('providers.vendorCredentials.toast.connectFailed'),
103
134
  description: e instanceof Error ? e.message : String(e),
104
135
  color: 'error',
105
136
  })
@@ -113,7 +144,7 @@ async function remove(id: string) {
113
144
  await creds.remove(id)
114
145
  } catch (e) {
115
146
  toast.add({
116
- title: 'Could not remove token',
147
+ title: t('providers.vendorCredentials.toast.removeFailed'),
117
148
  description: e instanceof Error ? e.message : String(e),
118
149
  color: 'error',
119
150
  })
@@ -121,14 +152,18 @@ async function remove(id: string) {
121
152
  }
122
153
 
123
154
  function vendorLabel(v: SubscriptionVendor): string {
124
- return VENDORS.find((x) => x.value === v)?.label ?? v
155
+ return VENDORS.value.find((x) => x.value === v)?.label ?? v
125
156
  }
126
157
  </script>
127
158
 
128
159
  <template>
129
- <UModal v-model:open="open" title="LLM Vendors" :ui="{ content: 'max-w-2xl' }">
160
+ <UModal
161
+ v-model:open="open"
162
+ :title="t('providers.vendorCredentials.title')"
163
+ :ui="{ content: 'max-w-2xl' }"
164
+ >
130
165
  <template #title>
131
- <IntegrationBackTitle title="LLM Vendors" @back="back" />
166
+ <IntegrationBackTitle :title="t('providers.vendorCredentials.title')" @back="back" />
132
167
  </template>
133
168
  <template #body>
134
169
  <UTabs
@@ -141,17 +176,12 @@ function vendorLabel(v: SubscriptionVendor): string {
141
176
  <template #pool>
142
177
  <div class="space-y-5">
143
178
  <p class="text-sm text-slate-400">
144
- Connect a <strong>commercial</strong> coding-plan subscription (Kimi, DeepSeek) that
145
- permits team/organization use to run agent steps on the Claude Code harness instead of
146
- an API key. Tokens are stored encrypted, pooled, and rotated by usage. Subscription
147
- models are flat-rate quota — they don’t draw on your spend budget. Individual-use
148
- subscriptions (Claude, GLM, ChatGPT/Codex) are connected per-user in the Personal
149
- subscriptions tab.
179
+ {{ t('providers.vendorCredentials.poolIntro') }}
150
180
  </p>
151
181
 
152
182
  <!-- vendor picker -->
153
183
  <div class="flex flex-wrap items-end gap-3">
154
- <UFormField label="Vendor">
184
+ <UFormField :label="t('providers.vendorCredentials.vendorField')">
155
185
  <USelect
156
186
  v-model="vendor"
157
187
  :items="visibleVendors.map((v) => ({ label: v.label, value: v.value }))"
@@ -169,10 +199,13 @@ function vendorLabel(v: SubscriptionVendor): string {
169
199
 
170
200
  <!-- add form -->
171
201
  <div class="space-y-2">
172
- <UFormField label="Label (optional)">
173
- <UInput v-model="label" placeholder="e.g. work account" />
202
+ <UFormField :label="t('providers.vendorCredentials.labelField')">
203
+ <UInput
204
+ v-model="label"
205
+ :placeholder="t('providers.vendorCredentials.labelPlaceholder')"
206
+ />
174
207
  </UFormField>
175
- <UFormField label="Token">
208
+ <UFormField :label="t('providers.vendorCredentials.tokenField')">
176
209
  <UTextarea
177
210
  v-model="token"
178
211
  :rows="3"
@@ -187,7 +220,7 @@ function vendorLabel(v: SubscriptionVendor): string {
187
220
  icon="i-lucide-plus"
188
221
  @click="add()"
189
222
  >
190
- Connect
223
+ {{ t('providers.vendorCredentials.connect') }}
191
224
  </UButton>
192
225
  </div>
193
226
  </div>
@@ -195,7 +228,9 @@ function vendorLabel(v: SubscriptionVendor): string {
195
228
  <!-- connected pool -->
196
229
  <div v-if="creds.credentials.length" class="space-y-2">
197
230
  <h4 class="text-xs font-semibold uppercase tracking-wide text-slate-500">
198
- Connected ({{ creds.credentials.length }})
231
+ {{
232
+ t('providers.vendorCredentials.connected', { count: creds.credentials.length })
233
+ }}
199
234
  </h4>
200
235
  <div
201
236
  v-for="c in creds.credentials"
@@ -206,8 +241,16 @@ function vendorLabel(v: SubscriptionVendor): string {
206
241
  <span class="font-medium text-slate-200">{{ c.label }}</span>
207
242
  <span class="ml-2 text-xs text-slate-500">{{ vendorLabel(c.vendor) }}</span>
208
243
  <div class="text-[11px] tabular-nums text-slate-500">
209
- {{ (c.inputTokens + c.outputTokens).toLocaleString() }} tok this window ·
210
- {{ c.requestCount }} run{{ c.requestCount === 1 ? '' : 's' }}
244
+ {{
245
+ t(
246
+ 'providers.vendorCredentials.usage',
247
+ {
248
+ tokens: n(c.inputTokens + c.outputTokens, 'decimal'),
249
+ count: c.requestCount,
250
+ },
251
+ c.requestCount,
252
+ )
253
+ }}
211
254
  </div>
212
255
  </div>
213
256
  <UButton
@@ -1654,5 +1654,172 @@
1654
1654
  "removeFailed": "Could not remove runner"
1655
1655
  }
1656
1656
  }
1657
+ },
1658
+ "providers": {
1659
+ "presetMismatch": {
1660
+ "title": "Preset uses unavailable models",
1661
+ "intro": "The workspace default model preset \"{name}\" assigns models that aren't available under the current configuration. Tasks that use this preset would fail when they reach those steps.",
1662
+ "unavailable": "Unavailable",
1663
+ "advice": "Either repoint the preset at models you have configured, or add the missing provider.",
1664
+ "later": "Later",
1665
+ "configureVendors": "Configure vendors",
1666
+ "editPresets": "Edit presets",
1667
+ "defaultPresetName": "default preset"
1668
+ },
1669
+ "onboarding": {
1670
+ "title": "Set up an AI model provider",
1671
+ "noModelTitle": "No AI model is available on this workspace yet.",
1672
+ "noModelBody": "Agents need a model to run. AI works out of the box only on a Cloudflare deployment with Workers AI enabled; otherwise connect at least one source below.",
1673
+ "operatorNote": "AWS Bedrock and Cloudflare Workers AI are enabled by the deployment operator via environment configuration, not from this screen.",
1674
+ "routes": {
1675
+ "keys": {
1676
+ "title": "Provider keys & subscriptions",
1677
+ "body": "Add a direct provider API key (OpenAI, Anthropic, Qwen, …) or connect a commercial coding-plan subscription (Kimi, DeepSeek) or a personal one (Claude, GLM, Codex).",
1678
+ "cta": "Open LLM vendors"
1679
+ },
1680
+ "openrouter": {
1681
+ "title": "OpenRouter gateway",
1682
+ "body": "Enable models through the OpenRouter gateway with a single key. Browse and turn on the models you want.",
1683
+ "cta": "Browse OpenRouter models"
1684
+ },
1685
+ "local": {
1686
+ "title": "My local runners",
1687
+ "body": "Point cat-factory at a model you run yourself (Ollama, LM Studio, llama.cpp, vLLM, …). No API key, no spend.",
1688
+ "cta": "Configure local runners"
1689
+ }
1690
+ }
1691
+ },
1692
+ "personalCredential": {
1693
+ "titleWrongPassword": "Incorrect personal password",
1694
+ "titleNoSubscription": "Connect your {vendor} subscription",
1695
+ "titleExpired": "Your {vendor} subscription expired",
1696
+ "titleDefault": "Enter your personal password",
1697
+ "fallbackVendor": "subscription",
1698
+ "connectBody": "This task uses a {vendor} model, which runs on your own {vendor} subscription. Connect (or renew) it first, then start the run again.",
1699
+ "connectCta": "Connect subscription",
1700
+ "passwordBody": "Enter the personal password that protects your {vendor} subscription. It unlocks your credential for this run only and is cached in this browser so you won't be asked again for a while.",
1701
+ "passwordField": "Personal password",
1702
+ "passwordPlaceholder": "your personal password",
1703
+ "submit": "Unlock & run",
1704
+ "cancel": "Cancel",
1705
+ "toast": {
1706
+ "started": "Run started",
1707
+ "startFailed": "Could not start the run"
1708
+ },
1709
+ "vendors": {
1710
+ "claude": "Claude",
1711
+ "glm": "GLM (Z.ai)",
1712
+ "codex": "ChatGPT (Codex)"
1713
+ }
1714
+ },
1715
+ "apiKeys": {
1716
+ "directHeading": "Direct provider API keys",
1717
+ "proxyHeading": "Proxy / gateway API keys",
1718
+ "directAccountIntro": "Connect a vendor API key shared by every workspace in this account. Keys are stored encrypted, pooled, and rotated by usage. Account keys are admin-managed.",
1719
+ "directIntro": "Connect a vendor API key so models run directly on that provider. Keys are stored encrypted, pooled, and rotated by usage. Scope a key to this workspace (shared with the team) or to you (your own pool, usable anywhere).",
1720
+ "proxyAccountIntro": "Connect a proxy key (OpenRouter, LiteLLM) shared by every workspace in this account. A proxy is an intermediary that fronts many vendors behind a single key. Keys are stored encrypted, pooled, and rotated by usage.",
1721
+ "proxyIntro": "Connect a proxy key (OpenRouter, LiteLLM). A proxy is an intermediary that reaches many vendors' models through a single gateway, rather than one vendor directly. Keys are stored encrypted, pooled, and rotated by usage. Scope a key to this workspace (shared with the team) or to you (your own pool, usable anywhere).",
1722
+ "scopeField": "Scope",
1723
+ "scopeWorkspace": "This workspace",
1724
+ "scopeUser": "My keys (only me)",
1725
+ "providerField": "Provider",
1726
+ "proxyField": "Proxy",
1727
+ "openKeys": "Open {provider} keys ↗",
1728
+ "cachingNote": "Enables prompt caching for {provider} models, so a long multi-turn run reuses its cached prompt prefix instead of re-sending it every turn.",
1729
+ "labelField": "Label (optional)",
1730
+ "labelPlaceholder": "e.g. team key",
1731
+ "defaultLabel": "{provider} key",
1732
+ "keyField": "API key",
1733
+ "keyPlaceholder": "paste the API key",
1734
+ "connect": "Connect",
1735
+ "connected": "Connected ({count})",
1736
+ "usage": "{tokens} tok this window · {count} call | {tokens} tok this window · {count} calls",
1737
+ "@usage": {
1738
+ "description": "Usage readout for a connected API key. {tokens} is a preformatted token count; {count} is the number of API calls in the window and drives the plural. Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
1739
+ },
1740
+ "toast": {
1741
+ "connected": "API key connected",
1742
+ "connectFailed": "Could not connect key",
1743
+ "removeFailed": "Could not remove key"
1744
+ },
1745
+ "providers": {
1746
+ "openai": {
1747
+ "label": "OpenAI",
1748
+ "step1": "Open platform.openai.com → API keys and create a new secret key.",
1749
+ "step2": "Copy the key (starts with sk-…); it is shown only once."
1750
+ },
1751
+ "anthropic": {
1752
+ "label": "Anthropic (Claude API)",
1753
+ "step1": "Open console.anthropic.com → Settings → API Keys and create a key.",
1754
+ "step2": "Copy the key (starts with sk-ant-…)."
1755
+ },
1756
+ "qwen": {
1757
+ "label": "Qwen (Alibaba DashScope)",
1758
+ "step1": "Open the DashScope console (international) → API-KEY and create a key.",
1759
+ "step2": "Copy the key; it authenticates the OpenAI-compatible Qwen endpoint."
1760
+ },
1761
+ "deepseek": {
1762
+ "label": "DeepSeek",
1763
+ "step1": "Open platform.deepseek.com → API keys and create a key.",
1764
+ "step2": "Copy the key (starts with sk-…)."
1765
+ },
1766
+ "moonshot": {
1767
+ "label": "Moonshot (Kimi API)",
1768
+ "step1": "Open platform.moonshot.ai → API Keys and create a key.",
1769
+ "step2": "Copy the key; it authenticates the OpenAI-compatible Moonshot endpoint."
1770
+ },
1771
+ "openrouter": {
1772
+ "label": "OpenRouter",
1773
+ "step1": "Open openrouter.ai → Keys and create an API key.",
1774
+ "step2": "Copy the key (starts with sk-or-…); it reaches 300+ models through one gateway."
1775
+ },
1776
+ "litellm": {
1777
+ "label": "LiteLLM (self-hosted gateway)",
1778
+ "step1": "Generate a virtual key on your LiteLLM gateway (or use its master key).",
1779
+ "step2": "The gateway's base URL is set by your deployment operator (LITELLM_BASE_URL), not here."
1780
+ }
1781
+ }
1782
+ },
1783
+ "vendorCredentials": {
1784
+ "title": "LLM Vendors",
1785
+ "tabs": {
1786
+ "pool": "Workspace pool",
1787
+ "direct": "Direct providers",
1788
+ "proxy": "Proxies",
1789
+ "personal": "Personal subscriptions"
1790
+ },
1791
+ "poolIntro": "Connect a commercial coding-plan subscription (Kimi, DeepSeek) that permits team/organization use to run agent steps on the Claude Code harness instead of an API key. Tokens are stored encrypted, pooled, and rotated by usage. Subscription models are flat-rate quota, so they don't draw on your spend budget. Individual-use subscriptions (Claude, GLM, ChatGPT/Codex) are connected per-user in the Personal subscriptions tab.",
1792
+ "vendorField": "Vendor",
1793
+ "labelField": "Label (optional)",
1794
+ "labelPlaceholder": "e.g. work account",
1795
+ "defaultLabel": "{vendor} token",
1796
+ "tokenField": "Token",
1797
+ "tokenPlaceholder": "your coding-plan API key",
1798
+ "connect": "Connect",
1799
+ "connected": "Connected ({count})",
1800
+ "usage": "{tokens} tok this window · {count} run | {tokens} tok this window · {count} runs",
1801
+ "@usage": {
1802
+ "description": "Usage readout for a connected subscription token. {tokens} is a preformatted token count; {count} is the number of runs in the window and drives the plural. Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
1803
+ },
1804
+ "toast": {
1805
+ "connected": "Token connected",
1806
+ "connectFailed": "Could not connect token",
1807
+ "removeFailed": "Could not remove token"
1808
+ },
1809
+ "vendors": {
1810
+ "kimi": {
1811
+ "label": "Kimi (Moonshot coding plan)",
1812
+ "step1": "Open your Moonshot (Kimi) coding-plan console and create an API key for the Anthropic-compatible endpoint.",
1813
+ "step2": "Copy the API key. Agent steps will run via Claude Code against Moonshot's Anthropic endpoint with full context.",
1814
+ "step3": "Paste the key below."
1815
+ },
1816
+ "deepseek": {
1817
+ "label": "DeepSeek (coding plan)",
1818
+ "step1": "Open your DeepSeek coding-plan console and create an API key for the Anthropic-compatible endpoint.",
1819
+ "step2": "Copy the API key. Agent steps will run via Claude Code against DeepSeek's Anthropic endpoint with full context.",
1820
+ "step3": "Paste the key below."
1821
+ }
1822
+ }
1823
+ }
1657
1824
  }
1658
1825
  }
@@ -1609,5 +1609,166 @@
1609
1609
  "removeFailed": "No se pudo eliminar el runner"
1610
1610
  }
1611
1611
  }
1612
+ },
1613
+ "providers": {
1614
+ "presetMismatch": {
1615
+ "title": "El preajuste usa modelos no disponibles",
1616
+ "intro": "El preajuste de modelo predeterminado del espacio de trabajo \"{name}\" asigna modelos que no están disponibles con la configuración actual. Las tareas que usen este preajuste fallarían al llegar a esos pasos.",
1617
+ "unavailable": "No disponibles",
1618
+ "advice": "Reapunta el preajuste a modelos que tengas configurados o añade el proveedor que falta.",
1619
+ "later": "Más tarde",
1620
+ "configureVendors": "Configurar proveedores",
1621
+ "editPresets": "Editar preajustes",
1622
+ "defaultPresetName": "preajuste predeterminado"
1623
+ },
1624
+ "onboarding": {
1625
+ "title": "Configurar un proveedor de modelos de IA",
1626
+ "noModelTitle": "Todavía no hay ningún modelo de IA disponible en este espacio de trabajo.",
1627
+ "noModelBody": "Los agentes necesitan un modelo para ejecutarse. La IA funciona de inmediato solo en un despliegue de Cloudflare con Workers AI habilitado; de lo contrario, conecta al menos una fuente abajo.",
1628
+ "operatorNote": "AWS Bedrock y Cloudflare Workers AI los habilita el operador del despliegue mediante la configuración del entorno, no desde esta pantalla.",
1629
+ "routes": {
1630
+ "keys": {
1631
+ "title": "Claves y suscripciones de proveedores",
1632
+ "body": "Añade una clave de API directa de un proveedor (OpenAI, Anthropic, Qwen, …) o conecta una suscripción comercial de plan de programación (Kimi, DeepSeek) o una personal (Claude, GLM, Codex).",
1633
+ "cta": "Abrir proveedores de LLM"
1634
+ },
1635
+ "openrouter": {
1636
+ "title": "Pasarela OpenRouter",
1637
+ "body": "Habilita modelos a través de la pasarela OpenRouter con una sola clave. Explora y activa los modelos que quieras.",
1638
+ "cta": "Explorar modelos de OpenRouter"
1639
+ },
1640
+ "local": {
1641
+ "title": "Mis ejecutores locales",
1642
+ "body": "Apunta cat-factory a un modelo que ejecutes tú mismo (Ollama, LM Studio, llama.cpp, vLLM, …). Sin clave de API ni gasto.",
1643
+ "cta": "Configurar ejecutores locales"
1644
+ }
1645
+ }
1646
+ },
1647
+ "personalCredential": {
1648
+ "titleWrongPassword": "Contraseña personal incorrecta",
1649
+ "titleNoSubscription": "Conecta tu suscripción de {vendor}",
1650
+ "titleExpired": "Tu suscripción de {vendor} ha caducado",
1651
+ "titleDefault": "Introduce tu contraseña personal",
1652
+ "fallbackVendor": "suscripción",
1653
+ "connectBody": "Esta tarea usa un modelo de {vendor}, que se ejecuta con tu propia suscripción de {vendor}. Conéctala (o renuévala) primero y luego inicia la ejecución de nuevo.",
1654
+ "connectCta": "Conectar suscripción",
1655
+ "passwordBody": "Introduce la contraseña personal que protege tu suscripción de {vendor}. Desbloquea tu credencial solo para esta ejecución y se almacena en este navegador un tiempo para no volver a pedírtela enseguida.",
1656
+ "passwordField": "Contraseña personal",
1657
+ "passwordPlaceholder": "tu contraseña personal",
1658
+ "submit": "Desbloquear y ejecutar",
1659
+ "cancel": "Cancelar",
1660
+ "toast": {
1661
+ "started": "Ejecución iniciada",
1662
+ "startFailed": "No se pudo iniciar la ejecución"
1663
+ },
1664
+ "vendors": {
1665
+ "claude": "Claude",
1666
+ "glm": "GLM (Z.ai)",
1667
+ "codex": "ChatGPT (Codex)"
1668
+ }
1669
+ },
1670
+ "apiKeys": {
1671
+ "directHeading": "Claves de API de proveedores directos",
1672
+ "proxyHeading": "Claves de API de proxy / pasarela",
1673
+ "directAccountIntro": "Conecta una clave de API de proveedor compartida por todos los espacios de trabajo de esta cuenta. Las claves se almacenan cifradas, se agrupan y se rotan según el uso. Las claves de cuenta las gestiona un administrador.",
1674
+ "directIntro": "Conecta una clave de API de proveedor para que los modelos se ejecuten directamente en ese proveedor. Las claves se almacenan cifradas, se agrupan y se rotan según el uso. Asigna el alcance de una clave a este espacio de trabajo (compartida con el equipo) o a ti (tu propio grupo, usable en cualquier sitio).",
1675
+ "proxyAccountIntro": "Conecta una clave de proxy (OpenRouter, LiteLLM) compartida por todos los espacios de trabajo de esta cuenta. Un proxy es un intermediario que da acceso a muchos proveedores tras una sola clave. Las claves se almacenan cifradas, se agrupan y se rotan según el uso.",
1676
+ "proxyIntro": "Conecta una clave de proxy (OpenRouter, LiteLLM). Un proxy es un intermediario que llega a los modelos de muchos proveedores a través de una sola pasarela, en lugar de un proveedor directo. Las claves se almacenan cifradas, se agrupan y se rotan según el uso. Asigna el alcance de una clave a este espacio de trabajo (compartida con el equipo) o a ti (tu propio grupo, usable en cualquier sitio).",
1677
+ "scopeField": "Alcance",
1678
+ "scopeWorkspace": "Este espacio de trabajo",
1679
+ "scopeUser": "Mis claves (solo yo)",
1680
+ "providerField": "Proveedor",
1681
+ "proxyField": "Proxy",
1682
+ "openKeys": "Abrir claves de {provider} ↗",
1683
+ "cachingNote": "Habilita la caché de prompts para los modelos de {provider}, de modo que una ejecución larga de varios turnos reutiliza su prefijo de prompt en caché en lugar de reenviarlo cada turno.",
1684
+ "labelField": "Etiqueta (opcional)",
1685
+ "labelPlaceholder": "p. ej. clave del equipo",
1686
+ "defaultLabel": "Clave de {provider}",
1687
+ "keyField": "Clave de API",
1688
+ "keyPlaceholder": "pega la clave de API",
1689
+ "connect": "Conectar",
1690
+ "connected": "Conectadas ({count})",
1691
+ "usage": "{tokens} tok esta ventana · {count} llamada | {tokens} tok esta ventana · {count} llamadas",
1692
+ "toast": {
1693
+ "connected": "Clave de API conectada",
1694
+ "connectFailed": "No se pudo conectar la clave",
1695
+ "removeFailed": "No se pudo eliminar la clave"
1696
+ },
1697
+ "providers": {
1698
+ "openai": {
1699
+ "label": "OpenAI",
1700
+ "step1": "Abre platform.openai.com → API keys y crea una nueva clave secreta.",
1701
+ "step2": "Copia la clave (empieza por sk-…); solo se muestra una vez."
1702
+ },
1703
+ "anthropic": {
1704
+ "label": "Anthropic (API de Claude)",
1705
+ "step1": "Abre console.anthropic.com → Settings → API Keys y crea una clave.",
1706
+ "step2": "Copia la clave (empieza por sk-ant-…)."
1707
+ },
1708
+ "qwen": {
1709
+ "label": "Qwen (Alibaba DashScope)",
1710
+ "step1": "Abre la consola de DashScope (internacional) → API-KEY y crea una clave.",
1711
+ "step2": "Copia la clave; autentica el endpoint de Qwen compatible con OpenAI."
1712
+ },
1713
+ "deepseek": {
1714
+ "label": "DeepSeek",
1715
+ "step1": "Abre platform.deepseek.com → API keys y crea una clave.",
1716
+ "step2": "Copia la clave (empieza por sk-…)."
1717
+ },
1718
+ "moonshot": {
1719
+ "label": "Moonshot (API de Kimi)",
1720
+ "step1": "Abre platform.moonshot.ai → API Keys y crea una clave.",
1721
+ "step2": "Copia la clave; autentica el endpoint de Moonshot compatible con OpenAI."
1722
+ },
1723
+ "openrouter": {
1724
+ "label": "OpenRouter",
1725
+ "step1": "Abre openrouter.ai → Keys y crea una clave de API.",
1726
+ "step2": "Copia la clave (empieza por sk-or-…); llega a más de 300 modelos a través de una sola pasarela."
1727
+ },
1728
+ "litellm": {
1729
+ "label": "LiteLLM (pasarela autoalojada)",
1730
+ "step1": "Genera una clave virtual en tu pasarela LiteLLM (o usa su clave maestra).",
1731
+ "step2": "La URL base de la pasarela la establece el operador de tu despliegue (LITELLM_BASE_URL), no aquí."
1732
+ }
1733
+ }
1734
+ },
1735
+ "vendorCredentials": {
1736
+ "title": "Proveedores de LLM",
1737
+ "tabs": {
1738
+ "pool": "Grupo del espacio de trabajo",
1739
+ "direct": "Proveedores directos",
1740
+ "proxy": "Proxies",
1741
+ "personal": "Suscripciones personales"
1742
+ },
1743
+ "poolIntro": "Conecta una suscripción comercial de plan de programación (Kimi, DeepSeek) que permita uso de equipo/organización para ejecutar los pasos de agente en el harness de Claude Code en lugar de una clave de API. Los tokens se almacenan cifrados, se agrupan y se rotan según el uso. Los modelos de suscripción tienen cuota de tarifa plana, así que no consumen tu presupuesto de gasto. Las suscripciones de uso individual (Claude, GLM, ChatGPT/Codex) se conectan por usuario en la pestaña de Suscripciones personales.",
1744
+ "vendorField": "Proveedor",
1745
+ "labelField": "Etiqueta (opcional)",
1746
+ "labelPlaceholder": "p. ej. cuenta de trabajo",
1747
+ "defaultLabel": "Token de {vendor}",
1748
+ "tokenField": "Token",
1749
+ "tokenPlaceholder": "tu clave de API del plan de programación",
1750
+ "connect": "Conectar",
1751
+ "connected": "Conectados ({count})",
1752
+ "usage": "{tokens} tok esta ventana · {count} ejecución | {tokens} tok esta ventana · {count} ejecuciones",
1753
+ "toast": {
1754
+ "connected": "Token conectado",
1755
+ "connectFailed": "No se pudo conectar el token",
1756
+ "removeFailed": "No se pudo eliminar el token"
1757
+ },
1758
+ "vendors": {
1759
+ "kimi": {
1760
+ "label": "Kimi (plan de programación de Moonshot)",
1761
+ "step1": "Abre la consola de tu plan de programación de Moonshot (Kimi) y crea una clave de API para el endpoint compatible con Anthropic.",
1762
+ "step2": "Copia la clave de API. Los pasos de agente se ejecutarán vía Claude Code contra el endpoint de Anthropic de Moonshot con contexto completo.",
1763
+ "step3": "Pega la clave abajo."
1764
+ },
1765
+ "deepseek": {
1766
+ "label": "DeepSeek (plan de programación)",
1767
+ "step1": "Abre la consola de tu plan de programación de DeepSeek y crea una clave de API para el endpoint compatible con Anthropic.",
1768
+ "step2": "Copia la clave de API. Los pasos de agente se ejecutarán vía Claude Code contra el endpoint de Anthropic de DeepSeek con contexto completo.",
1769
+ "step3": "Pega la clave abajo."
1770
+ }
1771
+ }
1772
+ }
1612
1773
  }
1613
1774
  }