@cat-factory/app 0.49.1 → 0.50.0

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.
@@ -11,6 +11,7 @@ import type { ProviderConnectionKind } from '~/types/providerConnections'
11
11
  import ProvisioningLogsDrawer from '~/components/provisioning/ProvisioningLogsDrawer.vue'
12
12
  import ProviderManifestEditor from '~/components/settings/ProviderManifestEditor.vue'
13
13
  import KubernetesRunnerForm from '~/components/settings/KubernetesRunnerForm.vue'
14
+ import KubernetesEnvironmentForm from '~/components/settings/KubernetesEnvironmentForm.vue'
14
15
 
15
16
  const props = defineProps<{ kind: ProviderConnectionKind }>()
16
17
 
@@ -185,18 +186,32 @@ async function saveManifest(payload: {
185
186
  }
186
187
  }
187
188
 
188
- // --- Runner-backend selector (runner-pool only) -------------------------------------
189
- // The runner-pool tab can configure either the manifest pool OR a native Kubernetes
190
- // cluster; environments are manifest-only. Defaults to the saved connection's kind.
191
- const RUNNER_BACKEND_KINDS = ['manifest', 'kubernetes'] as const
192
- type RunnerBackendKind = (typeof RUNNER_BACKEND_KINDS)[number]
193
- const backendKind = ref<RunnerBackendKind>('manifest')
194
- const showBackendSelector = computed(() => props.kind === 'runner-pool')
189
+ // --- Backend selector -----------------------------------------------------------------
190
+ // Both infrastructure tabs can configure either the BYO manifest backend OR a native
191
+ // Kubernetes backend (a runner cluster for runner-pool, per-PR namespaces for environment).
192
+ // The two K8s backends have different config shapes, so each kind renders its own form.
193
+ // Defaults to the saved connection's kind.
194
+ const BACKEND_KINDS = ['manifest', 'kubernetes'] as const
195
+ type BackendKind = (typeof BACKEND_KINDS)[number]
196
+ const backendKind = ref<BackendKind>('manifest')
197
+ const showBackendSelector = computed(() => true)
198
+ const backendSelectorLabel = computed(() =>
199
+ t(
200
+ props.kind === 'environment'
201
+ ? 'settings.providerConnection.backend.environmentSelectorLabel'
202
+ : 'settings.providerConnection.backend.selectorLabel',
203
+ ),
204
+ )
205
+ function backendKindLabel(k: BackendKind): string {
206
+ if (k === 'kubernetes') return t('settings.providerConnection.backend.kubernetes')
207
+ return t(
208
+ props.kind === 'environment'
209
+ ? 'settings.providerConnection.backend.environmentManifest'
210
+ : 'settings.providerConnection.backend.manifest',
211
+ )
212
+ }
195
213
  const backendKindItems = computed(() =>
196
- RUNNER_BACKEND_KINDS.map((k) => ({
197
- label: t(`settings.providerConnection.backend.${k}`),
198
- value: k,
199
- })),
214
+ BACKEND_KINDS.map((k) => ({ label: backendKindLabel(k), value: k })),
200
215
  )
201
216
  watch(
202
217
  () => connection.value,
@@ -317,17 +332,26 @@ function fieldHelp(key: string): string | undefined {
317
332
  }}
318
333
  </div>
319
334
 
320
- <!-- Runner-backend selector: the manifest pool or a native Kubernetes cluster. -->
321
- <UFormField
322
- v-if="showBackendSelector"
323
- :label="t('settings.providerConnection.backend.selectorLabel')"
324
- >
335
+ <!-- Backend selector: the BYO manifest backend or a native Kubernetes backend. -->
336
+ <UFormField v-if="showBackendSelector" :label="backendSelectorLabel">
325
337
  <USelect v-model="backendKind" :items="backendKindItems" />
326
338
  </UFormField>
327
339
 
328
- <!-- Native Kubernetes runner backend. -->
340
+ <!-- Native Kubernetes runner backend (runner-pool). -->
329
341
  <KubernetesRunnerForm
330
- v-if="showBackendSelector && backendKind === 'kubernetes'"
342
+ v-if="kind === 'runner-pool' && backendKind === 'kubernetes'"
343
+ :connection="connection"
344
+ :supports-test="descriptor.supportsTest"
345
+ :testing="testing"
346
+ :busy="busy"
347
+ :test-result="testResult"
348
+ @test="testConfig"
349
+ @save="saveConfig"
350
+ />
351
+
352
+ <!-- Native Kubernetes ephemeral-environment backend (environment). -->
353
+ <KubernetesEnvironmentForm
354
+ v-else-if="kind === 'environment' && backendKind === 'kubernetes'"
331
355
  :connection="connection"
332
356
  :supports-test="descriptor.supportsTest"
333
357
  :testing="testing"
@@ -416,18 +440,31 @@ function fieldHelp(key: string): string | undefined {
416
440
  </div>
417
441
  </div>
418
442
 
419
- <!-- MANIFEST-driven provider: the full in-app manifest editor. -->
420
- <ProviderManifestEditor
443
+ <!-- MANIFEST-driven provider: the raw JSON manifest editor. Collapsed by default — it's
444
+ the advanced path, needed ONLY to integrate a custom API-based scheduler. The common
445
+ backends (local Docker, Cloudflare Containers, Kubernetes) don't need it. -->
446
+ <details
421
447
  v-else
422
- :kind="kind"
423
- :saved-manifest="descriptor.savedManifest"
424
- :connected="!!connection"
425
- :supports-test="descriptor.supportsTest"
426
- :testing="testing"
427
- :busy="busy"
428
- :test-result="testResult"
429
- @test="testManifest"
430
- @save="saveManifest"
431
- />
448
+ class="rounded-lg border border-slate-700 bg-slate-900/40 p-3"
449
+ :open="!!connection"
450
+ >
451
+ <summary class="cursor-pointer text-sm font-medium text-slate-200">
452
+ {{ t('settings.providerConnection.advancedManifest.summary') }}
453
+ </summary>
454
+ <p class="mt-2 mb-3 text-[11px] text-slate-400">
455
+ {{ t('settings.providerConnection.advancedManifest.intro') }}
456
+ </p>
457
+ <ProviderManifestEditor
458
+ :kind="kind"
459
+ :saved-manifest="descriptor.savedManifest"
460
+ :connected="!!connection"
461
+ :supports-test="descriptor.supportsTest"
462
+ :testing="testing"
463
+ :busy="busy"
464
+ :test-result="testResult"
465
+ @test="testManifest"
466
+ @save="saveManifest"
467
+ />
468
+ </details>
432
469
  </div>
433
470
  </template>
@@ -70,12 +70,15 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
70
70
  kind === 'environment'
71
71
  ? send(CONTRACTS.environment.register, {
72
72
  pathPrefix: ws(workspaceId),
73
- body: body as RegisterEnvironmentProviderInput,
73
+ body: {
74
+ config: backendConfig(body),
75
+ secrets: body.secrets,
76
+ } as RegisterEnvironmentProviderInput,
74
77
  })
75
78
  : send(CONTRACTS['runner-pool'].register, {
76
79
  pathPrefix: ws(workspaceId),
77
80
  body: {
78
- config: runnerBackendConfig(body),
81
+ config: backendConfig(body),
79
82
  secrets: body.secrets,
80
83
  } as RegisterRunnerPoolInput,
81
84
  }),
@@ -94,12 +97,15 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
94
97
  kind === 'environment'
95
98
  ? send(CONTRACTS.environment.test, {
96
99
  pathPrefix: ws(workspaceId),
97
- body: body as TestEnvironmentConnectionInput,
100
+ body: {
101
+ ...(body.manifest || body.config ? { config: backendConfig(body) } : {}),
102
+ ...(body.secrets ? { secrets: body.secrets } : {}),
103
+ } as TestEnvironmentConnectionInput,
98
104
  })
99
105
  : send(CONTRACTS['runner-pool'].test, {
100
106
  pathPrefix: ws(workspaceId),
101
107
  body: {
102
- ...(body.manifest || body.config ? { config: runnerBackendConfig(body) } : {}),
108
+ ...(body.manifest || body.config ? { config: backendConfig(body) } : {}),
103
109
  ...(body.secrets ? { secrets: body.secrets } : {}),
104
110
  } as TestRunnerPoolConnectionInput,
105
111
  }),
@@ -110,13 +116,12 @@ export function providerConnectionsApi({ send, ws }: ApiContext) {
110
116
  }
111
117
 
112
118
  /**
113
- * Resolve the discriminated runner-backend config from a connect-form payload: an
114
- * explicit `config` (the Kubernetes form) wins; otherwise a bare `manifest` (the manifest
115
- * editor) is wrapped into the manifest backend kind.
119
+ * Resolve the discriminated backend config (runner-pool OR environment) from a connect-form
120
+ * payload: an explicit `config` (the Kubernetes form) wins; otherwise a bare `manifest` (the
121
+ * manifest editor) is wrapped into the `manifest` backend kind. Both subsystems now take a
122
+ * discriminated `config`, so the same shape serves each.
116
123
  */
117
- function runnerBackendConfig(
118
- body: RegisterProviderInput | TestProviderInput,
119
- ): Record<string, unknown> {
124
+ function backendConfig(body: RegisterProviderInput | TestProviderInput): Record<string, unknown> {
120
125
  if (body.config) return body.config
121
126
  return { kind: 'manifest', manifest: body.manifest ?? {} }
122
127
  }
@@ -79,9 +79,6 @@ const ModelConfigurationPanel = defineAsyncComponent(
79
79
  const LocalModelEndpointsPanel = defineAsyncComponent(
80
80
  () => import('~/components/settings/LocalModelEndpointsPanel.vue'),
81
81
  )
82
- const LocalModeSettingsPanel = defineAsyncComponent(
83
- () => import('~/components/settings/LocalModeSettingsPanel.vue'),
84
- )
85
82
  const SandboxPanel = defineAsyncComponent(() => import('~/components/sandbox/SandboxPanel.vue'))
86
83
  const UserSecretsSection = defineAsyncComponent(
87
84
  () => import('~/components/settings/UserSecretsSection.vue'),
@@ -287,7 +284,6 @@ watch(
287
284
  <InfrastructureWindow v-if="ui.infrastructureOpen" />
288
285
  <ModelConfigurationPanel v-if="ui.modelConfigOpen" />
289
286
  <LocalModelEndpointsPanel v-if="ui.localModelsOpen" />
290
- <LocalModeSettingsPanel v-if="ui.localModeSettingsOpen" />
291
287
  <SandboxPanel v-if="ui.sandboxOpen" />
292
288
  <UserSecretsSection v-if="ui.userSecretsOpen" />
293
289
  <OpenRouterCatalogPanel v-if="ui.openRouterOpen" />
@@ -1,4 +1,4 @@
1
- import type { LocalModeConfig } from '@cat-factory/contracts'
1
+ import type { InfrastructureCapabilities, LocalModeConfig } from '@cat-factory/contracts'
2
2
  import { defineStore } from 'pinia'
3
3
  import { computed, ref } from 'vue'
4
4
  import type { AuthUser } from '~/types/domain'
@@ -31,6 +31,13 @@ export const useAuthStore = defineStore(
31
31
  * setup banner). Null on every other facade.
32
32
  */
33
33
  const localMode = ref<LocalModeConfig | null>(null)
34
+ /**
35
+ * The deployment's infrastructure execution backends (which agent-container runtime + test
36
+ * environment options exist, and the deployment default active one). Drives the
37
+ * Infrastructure window's backend selector. Null until the auth handshake resolves / on a
38
+ * facade that doesn't report it.
39
+ */
40
+ const infrastructure = ref<InfrastructureCapabilities | null>(null)
34
41
  /**
35
42
  * Local mode only: the source-control provider the user last chose to sign in with
36
43
  * (its PAT lives server-side in env — this is just the non-secret choice). Persisted, so
@@ -72,6 +79,7 @@ export const useAuthStore = defineStore(
72
79
  required.value = config.enabled
73
80
  if (config.providers) providers.value = config.providers
74
81
  localMode.value = config.localMode ?? null
82
+ infrastructure.value = config.infrastructure ?? null
75
83
  } catch {
76
84
  // Backend unreachable — let the board's own error UI handle it.
77
85
  required.value = false
@@ -222,6 +230,7 @@ export const useAuthStore = defineStore(
222
230
  required,
223
231
  providers,
224
232
  localMode,
233
+ infrastructure,
225
234
  autoLoginProvider,
226
235
  ready,
227
236
  isAuthenticated,
package/app/stores/ui.ts CHANGED
@@ -125,11 +125,12 @@ export const useUiStore = defineStore('ui', () => {
125
125
  // today, pluggable). NB: distinct from `observabilityInstanceId` below, which is the
126
126
  // LLM per-call observability panel.
127
127
  const observabilityConnectionOpen = ref(false)
128
- // The single tabbed Infrastructure window (ephemeral-environment provider + self-hosted
129
- // runner pool the same custom pool typically backs both jobs, so they're configured
130
- // together). `infrastructureOpen` is the modal flag; `infrastructureTab` selects which
131
- // provider's tab is shown. `openProviderConnection(kind)` stays the entry API but now
132
- // selects the matching tab instead of mounting a per-kind standalone panel.
128
+ // The single tabbed Infrastructure window — a TOP-LEVEL navbar destination (no longer
129
+ // reached via the Integrations hub). Two topical tabs: "Agent containers" (the execution
130
+ // backend + self-hosted runner pool, plus the local-mode warm pool/checkout) and "Test
131
+ // environments" (the ephemeral-environment provider). `infrastructureOpen` is the modal
132
+ // flag; `infrastructureTab` selects the tab. `openInfrastructure()` is the navbar entry;
133
+ // `openProviderConnection(kind)` remains for deep-links (a banner's "Configure…" button).
133
134
  const infrastructureOpen = ref(false)
134
135
  const infrastructureTab = ref<'environment' | 'runner-pool'>('runner-pool')
135
136
  const modelConfigOpen = ref(false)
@@ -140,9 +141,6 @@ export const useUiStore = defineStore('ui', () => {
140
141
  const vendorCredentialsTab = ref('pool')
141
142
  // Per-user settings panel: the signed-in user's own-machine local model runners.
142
143
  const localModelsOpen = ref(false)
143
- // Local-mode-only settings panel: the warm-container pool sizing + per-repo checkout reuse
144
- // (a per-deployment singleton that replaced the LOCAL_POOL_* / HARNESS_* env vars).
145
- const localModeSettingsOpen = ref(false)
146
144
  // The Sandbox (parallel prompt/model testing) surface — an opt-in, on-demand window.
147
145
  const sandboxOpen = ref(false)
148
146
  const userSecretsOpen = ref(false)
@@ -479,6 +477,13 @@ export const useUiStore = defineStore('ui', () => {
479
477
  function closeObservabilityConnection() {
480
478
  observabilityConnectionOpen.value = false
481
479
  }
480
+ // Top-level navbar entry into the Infrastructure window. No hub-return marker (it isn't
481
+ // reached from the Integrations hub), so the window shows no "Back to Integrations" control.
482
+ function openInfrastructure(tab: 'environment' | 'runner-pool' = 'runner-pool') {
483
+ resetHubReturn()
484
+ infrastructureTab.value = tab
485
+ infrastructureOpen.value = true
486
+ }
482
487
  function openProviderConnection(kind: 'environment' | 'runner-pool') {
483
488
  resetHubReturn()
484
489
  infrastructureTab.value = kind
@@ -511,13 +516,6 @@ export const useUiStore = defineStore('ui', () => {
511
516
  function closeLocalModels() {
512
517
  localModelsOpen.value = false
513
518
  }
514
- function openLocalModeSettings() {
515
- resetHubReturn()
516
- localModeSettingsOpen.value = true
517
- }
518
- function closeLocalModeSettings() {
519
- localModeSettingsOpen.value = false
520
- }
521
519
  function openSandbox() {
522
520
  sandboxOpen.value = true
523
521
  }
@@ -669,11 +667,11 @@ export const useUiStore = defineStore('ui', () => {
669
667
  observabilityConnectionOpen,
670
668
  infrastructureOpen,
671
669
  infrastructureTab,
670
+ openInfrastructure,
672
671
  modelConfigOpen,
673
672
  vendorCredentialsOpen,
674
673
  vendorCredentialsTab,
675
674
  localModelsOpen,
676
- localModeSettingsOpen,
677
675
  sandboxOpen,
678
676
  userSecretsOpen,
679
677
  openRouterOpen,
@@ -754,8 +752,6 @@ export const useUiStore = defineStore('ui', () => {
754
752
  closeVendorCredentials,
755
753
  openLocalModels,
756
754
  closeLocalModels,
757
- openLocalModeSettings,
758
- closeLocalModeSettings,
759
755
  openSandbox,
760
756
  closeSandbox,
761
757
  openUserSecrets,
@@ -33,6 +33,7 @@
33
33
  "addFromRepo": "Add from existing repo",
34
34
  "bootstrapRepo": "Bootstrap repo",
35
35
  "integrations": "Integrations",
36
+ "infrastructure": "Infrastructure",
36
37
  "sandbox": "Sandbox",
37
38
  "@sandbox": {
38
39
  "description": "Named feature area (a screen for trying prompt versions / models against graded fixtures). Fine to localize descriptively per locale - unlike nav.kaizen, this one is NOT kept verbatim."
@@ -1150,7 +1151,6 @@
1150
1151
  "documents": "Documents",
1151
1152
  "taskTrackers": "Task trackers",
1152
1153
  "observability": "Observability",
1153
- "infrastructure": "Infrastructure",
1154
1154
  "personal": "Personal (only you)"
1155
1155
  },
1156
1156
  "items": {
@@ -1188,16 +1188,6 @@
1188
1188
  "label": "Post-release health",
1189
1189
  "description": "Watch monitors and SLOs after a release ships (Datadog)."
1190
1190
  },
1191
- "infrastructure": {
1192
- "label": "Infrastructure",
1193
- "description": "Self-hosted runner pool for container agents and ephemeral test environments.",
1194
- "agents": "Agents: {state}",
1195
- "envs": "Envs: {state}"
1196
- },
1197
- "localMode": {
1198
- "label": "Local mode",
1199
- "description": "Warm container pool plus per-repo checkout reuse for the local runner."
1200
- },
1201
1191
  "githubPat": {
1202
1192
  "label": "My GitHub token",
1203
1193
  "description": "A personal access token used for runs you start (pushes, PRs, CI, merge)."
@@ -1259,12 +1249,28 @@
1259
1249
  "fragments": "Context fragments"
1260
1250
  }
1261
1251
  },
1252
+ "infrastructure": {
1253
+ "active": "Active: {backend}",
1254
+ "registerHint": "Register a runner pool below to enable this.",
1255
+ "updateFailed": "Could not update the execution backend",
1256
+ "executionBackend": {
1257
+ "label": "Where agents run",
1258
+ "local-docker": "Local Docker (host)",
1259
+ "cloudflare-containers": "Cloudflare Containers (built-in)",
1260
+ "runner-pool": "Self-hosted runner pool"
1261
+ },
1262
+ "testEnvBackend": {
1263
+ "label": "Where test environments run",
1264
+ "local-compose": "In-container docker-compose",
1265
+ "environment-provider": "Environment provider"
1266
+ }
1267
+ },
1262
1268
  "providerConnection": {
1263
1269
  "fallbackTitle": "Provider",
1264
1270
  "windowTitle": "Infrastructure",
1265
1271
  "noneAvailable": "No infrastructure providers are enabled on this deployment.",
1266
1272
  "tabs": {
1267
- "containerAgents": "Container agents",
1273
+ "agentContainers": "Agent containers",
1268
1274
  "testEnvironments": "Test environments"
1269
1275
  },
1270
1276
  "kind": {
@@ -1280,7 +1286,46 @@
1280
1286
  "backend": {
1281
1287
  "selectorLabel": "Runner backend",
1282
1288
  "manifest": "Self-hosted pool (manifest)",
1283
- "kubernetes": "Kubernetes"
1289
+ "kubernetes": "Kubernetes",
1290
+ "environmentSelectorLabel": "Environment backend",
1291
+ "environmentManifest": "Custom HTTP API (manifest)"
1292
+ },
1293
+ "kubernetesEnv": {
1294
+ "label": "Name",
1295
+ "labelPlaceholder": "Preview cluster",
1296
+ "apiServerUrl": "API server URL",
1297
+ "apiServerUrlHelp": "The kube-apiserver root, e.g. https://10.0.0.1:6443. The orchestrator applies each PR's manifests through the apiserver, so only this endpoint must be reachable.",
1298
+ "apiToken": "ServiceAccount token",
1299
+ "apiTokenHelp": "A bearer token with RBAC to create namespaces and apply the operator's resources. Stored encrypted; never shown again.",
1300
+ "manifestSourceLabel": "Manifest source",
1301
+ "sourceColocated": "Co-located in the PR repo",
1302
+ "sourceSeparate": "Separate repo",
1303
+ "repo": "Manifests repo",
1304
+ "repoHelp": "The owner/repo that holds the Kubernetes manifests, e.g. acme/preview-manifests.",
1305
+ "ref": "Ref",
1306
+ "refHelp": "Branch, tag or SHA to read the manifests at. Empty uses the repo's default branch.",
1307
+ "path": "Manifest path",
1308
+ "pathHelp": "File or directory within the repo holding the resources to apply.",
1309
+ "urlSourceLabel": "Environment URL",
1310
+ "urlIngressTemplate": "Ingress host template",
1311
+ "urlIngressStatus": "Read Ingress status",
1312
+ "urlServiceStatus": "Read Service status",
1313
+ "hostTemplate": "Host template",
1314
+ "hostTemplateHelp": "Host template rendered with the provision vars such as branch and pullNumber; the rendered host becomes the environment URL.",
1315
+ "ingressName": "Ingress name",
1316
+ "ingressNameHelp": "Ingress to read the load-balancer host from. Empty uses the only Ingress applied.",
1317
+ "serviceName": "Service name",
1318
+ "port": "Port",
1319
+ "scheme": "URL scheme",
1320
+ "schemeDefault": "https (default)",
1321
+ "namespaceTemplate": "Namespace template",
1322
+ "namespaceTemplateHelp": "Per-PR namespace name rendered from the provision vars such as pullNumber. Empty derives one from the PR number.",
1323
+ "imageTemplate": "Image template",
1324
+ "imageTemplateHelp": "Image reference exposed to the manifests, rendered over the provision vars such as branch and sha.",
1325
+ "caCertPem": "Cluster CA certificate (PEM)",
1326
+ "caCertPemHelp": "Paste the cluster CA bundle so the apiserver's TLS certificate verifies. Omit only for a publicly-trusted CA.",
1327
+ "insecureSkipTlsVerify": "Skip TLS verification",
1328
+ "insecureSkipTlsVerifyHelp": "Strongly discouraged. Disables apiserver TLS verification; use only for kind/dev clusters."
1284
1329
  },
1285
1330
  "kubernetes": {
1286
1331
  "label": "Name",
@@ -1309,16 +1354,9 @@
1309
1354
  "reenterSecrets": "Re-enter every secret to save. Stored secrets are write-only and aren't shown.",
1310
1355
  "starterHint": "This is a starter example. Edit it to match your provider's API."
1311
1356
  },
1312
- "delegation": {
1313
- "title": "Local delegation",
1314
- "intro": "By default this machine runs everything locally container agents on host Docker, the Tester's infrastructure via in-container docker-compose. Opt in below to delegate either concern to an external service instead. Applies only in local mode.",
1315
- "agentsToggle": "Run container agents on the runner pool",
1316
- "agentsHint": "Dispatch every container agent (coder, tester, merger, bootstrap, …) to this workspace's self-hosted runner pool instead of host Docker.",
1317
- "registerPoolPrompt": "{link} first to enable this.",
1318
- "registerPoolLink": "Register a runner pool",
1319
- "envToggle": "Provision Tester environments via the provider",
1320
- "envHint": "Stand the Tester's preview environment up through the environment provider configured below instead of in-container docker-compose. Connect a provider first to enable this.",
1321
- "updateFailed": "Could not update delegation"
1357
+ "advancedManifest": {
1358
+ "summary": "Advanced: custom API-based scheduler",
1359
+ "intro": "Only needed to integrate a custom API-based scheduler. The common backends (local Docker, Cloudflare Containers and Kubernetes) don't need this; describe your own scheduler's HTTP API here only if you run one."
1322
1360
  },
1323
1361
  "viewLogs": "View logs",
1324
1362
  "hideLogs": "Hide logs",
@@ -34,7 +34,8 @@
34
34
  "configuration": "Configuración",
35
35
  "workspaceSettings": "Ajustes del espacio de trabajo",
36
36
  "modelConfiguration": "Configuración del modelo",
37
- "accountSettings": "Ajustes de la cuenta"
37
+ "accountSettings": "Ajustes de la cuenta",
38
+ "infrastructure": "Infraestructura"
38
39
  },
39
40
  "board": {
40
41
  "toolbar": {
@@ -1111,7 +1112,6 @@
1111
1112
  "documents": "Documentos",
1112
1113
  "taskTrackers": "Rastreadores de tareas",
1113
1114
  "observability": "Observabilidad",
1114
- "infrastructure": "Infraestructura",
1115
1115
  "personal": "Personal (solo tú)"
1116
1116
  },
1117
1117
  "items": {
@@ -1149,16 +1149,6 @@
1149
1149
  "label": "Salud posterior al lanzamiento",
1150
1150
  "description": "Vigila los monitores y SLO después de publicar una versión (Datadog)."
1151
1151
  },
1152
- "infrastructure": {
1153
- "label": "Infraestructura",
1154
- "description": "Grupo de ejecutores autoalojado para los agentes de contenedor y entornos de prueba efímeros.",
1155
- "agents": "Agentes: {state}",
1156
- "envs": "Entornos: {state}"
1157
- },
1158
- "localMode": {
1159
- "label": "Modo local",
1160
- "description": "Grupo de contenedores en caliente y reutilización de checkout por repositorio para el ejecutor local."
1161
- },
1162
1152
  "githubPat": {
1163
1153
  "label": "Mi token de GitHub",
1164
1154
  "description": "Un token de acceso personal usado para las ejecuciones que inicias (pushes, PR, CI, fusión)."
@@ -1225,8 +1215,8 @@
1225
1215
  "windowTitle": "Infraestructura",
1226
1216
  "noneAvailable": "No hay proveedores de infraestructura habilitados en este despliegue.",
1227
1217
  "tabs": {
1228
- "containerAgents": "Agentes de contenedor",
1229
- "testEnvironments": "Entornos de prueba"
1218
+ "testEnvironments": "Entornos de prueba",
1219
+ "agentContainers": "Agentes de contenedor"
1230
1220
  },
1231
1221
  "kind": {
1232
1222
  "environment": {
@@ -1250,17 +1240,6 @@
1250
1240
  "reenterSecrets": "Vuelve a introducir cada secreto para guardar: los secretos almacenados son de solo escritura y no se muestran.",
1251
1241
  "starterHint": "Este es un ejemplo inicial. Edítalo para que coincida con la API de tu proveedor."
1252
1242
  },
1253
- "delegation": {
1254
- "title": "Delegación local",
1255
- "intro": "De forma predeterminada, esta máquina ejecuta todo localmente: los agentes de contenedor en Docker del host y la infraestructura del Tester mediante docker-compose dentro del contenedor. Activa las opciones de abajo para delegar cualquiera de estas tareas en un servicio externo. Solo se aplica en modo local.",
1256
- "agentsToggle": "Ejecutar los agentes de contenedor en el grupo de ejecutores",
1257
- "agentsHint": "Envía cada agente de contenedor (codificador, tester, fusionador, bootstrap, …) al grupo de ejecutores autoalojado de este espacio de trabajo en lugar de a Docker del host.",
1258
- "registerPoolPrompt": "{link} primero para habilitar esto.",
1259
- "registerPoolLink": "Registra un grupo de ejecutores",
1260
- "envToggle": "Aprovisionar los entornos del Tester mediante el proveedor",
1261
- "envHint": "Levanta el entorno de vista previa del Tester a través del proveedor de entornos configurado abajo en lugar de docker-compose dentro del contenedor. Conecta un proveedor primero para habilitar esto.",
1262
- "updateFailed": "No se pudo actualizar la delegación"
1263
- },
1264
1243
  "viewLogs": "Ver registros",
1265
1244
  "hideLogs": "Ocultar registros",
1266
1245
  "connectedAt": "Conectado · {baseUrl}",
@@ -1288,7 +1267,46 @@
1288
1267
  "backend": {
1289
1268
  "selectorLabel": "Backend de ejecución",
1290
1269
  "manifest": "Pool autohospedado (manifiesto)",
1291
- "kubernetes": "Kubernetes"
1270
+ "kubernetes": "Kubernetes",
1271
+ "environmentSelectorLabel": "Backend de entorno",
1272
+ "environmentManifest": "API HTTP personalizada (manifiesto)"
1273
+ },
1274
+ "kubernetesEnv": {
1275
+ "label": "Nombre",
1276
+ "labelPlaceholder": "Clúster de vista previa",
1277
+ "apiServerUrl": "URL del API server",
1278
+ "apiServerUrlHelp": "La raíz del kube-apiserver, p. ej. https://10.0.0.1:6443. El orquestador aplica los manifiestos de cada PR a través del apiserver, así que solo este endpoint debe ser accesible.",
1279
+ "apiToken": "Token de ServiceAccount",
1280
+ "apiTokenHelp": "Un token bearer con permisos RBAC para crear namespaces y aplicar los recursos del operador. Se almacena cifrado; no se vuelve a mostrar.",
1281
+ "manifestSourceLabel": "Origen de los manifiestos",
1282
+ "sourceColocated": "En el mismo repo de la PR",
1283
+ "sourceSeparate": "Repo aparte",
1284
+ "repo": "Repo de manifiestos",
1285
+ "repoHelp": "El owner/repo que contiene los manifiestos de Kubernetes, p. ej. acme/preview-manifests.",
1286
+ "ref": "Ref",
1287
+ "refHelp": "Rama, etiqueta o SHA donde leer los manifiestos. Vacío usa la rama por defecto del repo.",
1288
+ "path": "Ruta del manifiesto",
1289
+ "pathHelp": "Archivo o directorio dentro del repo que contiene los recursos a aplicar.",
1290
+ "urlSourceLabel": "URL del entorno",
1291
+ "urlIngressTemplate": "Plantilla de host del Ingress",
1292
+ "urlIngressStatus": "Leer estado del Ingress",
1293
+ "urlServiceStatus": "Leer estado del Service",
1294
+ "hostTemplate": "Plantilla de host",
1295
+ "hostTemplateHelp": "Plantilla de host renderizada con las variables de aprovisionamiento como branch y pullNumber; el host resultante se convierte en la URL del entorno.",
1296
+ "ingressName": "Nombre del Ingress",
1297
+ "ingressNameHelp": "Ingress del que leer el host del balanceador. Vacío usa el único Ingress aplicado.",
1298
+ "serviceName": "Nombre del Service",
1299
+ "port": "Puerto",
1300
+ "scheme": "Esquema de URL",
1301
+ "schemeDefault": "https (por defecto)",
1302
+ "namespaceTemplate": "Plantilla de namespace",
1303
+ "namespaceTemplateHelp": "Nombre del namespace por PR renderizado con las variables de aprovisionamiento como pullNumber. Vacío deriva uno del número de la PR.",
1304
+ "imageTemplate": "Plantilla de imagen",
1305
+ "imageTemplateHelp": "Referencia de imagen expuesta a los manifiestos, renderizada con las variables de aprovisionamiento como branch y sha.",
1306
+ "caCertPem": "Certificado CA del clúster (PEM)",
1307
+ "caCertPemHelp": "Pega el bundle CA del clúster para que el certificado TLS del apiserver se verifique. Omítelo solo para una CA de confianza pública.",
1308
+ "insecureSkipTlsVerify": "Omitir verificación TLS",
1309
+ "insecureSkipTlsVerifyHelp": "Muy desaconsejado. Desactiva la verificación TLS del apiserver; úsalo solo para clústeres kind/dev."
1292
1310
  },
1293
1311
  "kubernetes": {
1294
1312
  "label": "Nombre",
@@ -1304,6 +1322,10 @@
1304
1322
  "caCertPem": "Certificado CA del clúster (PEM)",
1305
1323
  "caCertPemHelp": "Pega el bundle CA del clúster para que el certificado TLS del apiserver se verifique. Omítelo solo para una CA de confianza pública.",
1306
1324
  "harnessPort": "Puerto del harness"
1325
+ },
1326
+ "advancedManifest": {
1327
+ "summary": "Avanzado: planificador personalizado basado en API",
1328
+ "intro": "Solo es necesario para integrar un planificador personalizado basado en API. Los backends habituales (Docker local, Cloudflare Containers y Kubernetes) no lo necesitan; describe aquí la API HTTP de tu propio planificador solo si usas uno."
1307
1329
  }
1308
1330
  },
1309
1331
  "serviceFragmentDefaults": {
@@ -1654,6 +1676,22 @@
1654
1676
  "removed": "Runner eliminado",
1655
1677
  "removeFailed": "No se pudo eliminar el runner"
1656
1678
  }
1679
+ },
1680
+ "infrastructure": {
1681
+ "active": "Activo: {backend}",
1682
+ "registerHint": "Registra un pool de ejecutores abajo para habilitarlo.",
1683
+ "updateFailed": "No se pudo actualizar el backend de ejecución",
1684
+ "executionBackend": {
1685
+ "label": "Dónde se ejecutan los agentes",
1686
+ "local-docker": "Docker local (host)",
1687
+ "cloudflare-containers": "Cloudflare Containers (integrado)",
1688
+ "runner-pool": "Pool de ejecutores autoalojado"
1689
+ },
1690
+ "testEnvBackend": {
1691
+ "label": "Dónde se ejecutan los entornos de prueba",
1692
+ "local-compose": "docker-compose en contenedor",
1693
+ "environment-provider": "Proveedor de entornos"
1694
+ }
1657
1695
  }
1658
1696
  },
1659
1697
  "providers": {