@cat-factory/app 0.47.2 → 0.47.3

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.
@@ -0,0 +1,286 @@
1
+ <script setup lang="ts">
2
+ // The in-app manifest editor for a MANIFEST-DRIVEN infrastructure provider (a runner pool
3
+ // or an environment provider without a native code adapter). It replaces the old
4
+ // "register it via the API" disclaimer: the operator authors the provider's full JSON
5
+ // manifest here, supplies the write-only secret values it references, and tests/saves —
6
+ // entirely in-app.
7
+ //
8
+ // The manifest is validated against the SAME Valibot wire contract the backend enforces
9
+ // (runner pool: RunnerPoolManifest; environment: EnvironmentManifest), imported from
10
+ // @cat-factory/contracts so the client check stays in lockstep with the server. The server
11
+ // remains authoritative — register re-validates — so a client that's behind still can't
12
+ // persist an invalid manifest.
13
+ //
14
+ // Secrets are write-only: never prefilled. Because register replaces the whole manifest +
15
+ // secret bundle, EVERY secret key the manifest references must be (re-)supplied on save —
16
+ // on an existing connection the amber hint says so.
17
+ import { computed, ref, watch } from 'vue'
18
+ import * as v from 'valibot'
19
+ import { environmentManifestSchema, runnerPoolManifestSchema } from '@cat-factory/contracts'
20
+ import type { ProviderConnectionKind } from '~/types/providerConnections'
21
+
22
+ const props = defineProps<{
23
+ kind: ProviderConnectionKind
24
+ /** The provider's current saved manifest (secret-ref keys only, no values). */
25
+ savedManifest?: Record<string, unknown>
26
+ /** Whether a connection already exists (drives the re-enter-secrets hint + button label). */
27
+ connected: boolean
28
+ /** Whether the provider exposes a connection test the UI can call. */
29
+ supportsTest: boolean
30
+ /** Bubbled-up busy state from the tab's store calls (so the editor shows loading). */
31
+ testing: boolean
32
+ busy: boolean
33
+ testResult: { ok: boolean; message?: string } | null
34
+ }>()
35
+
36
+ const emit = defineEmits<{
37
+ test: [payload: { manifest: Record<string, unknown>; secrets: Record<string, string> }]
38
+ save: [payload: { manifest: Record<string, unknown>; secrets: Record<string, string> }]
39
+ }>()
40
+
41
+ const { t } = useI18n()
42
+
43
+ // A minimal, valid starter manifest per kind (O1 option a: a static SPA example — no backend
44
+ // round-trip). Seeds the editor when there's no saved manifest to start from. The operator
45
+ // edits providerId/label/baseUrl and the request templates for their own scheduler/API.
46
+ const STARTERS: Record<ProviderConnectionKind, Record<string, unknown>> = {
47
+ 'runner-pool': {
48
+ providerId: 'my-pool',
49
+ label: 'My runner pool',
50
+ baseUrl: 'https://pool.example.com',
51
+ auth: { type: 'bearer', secretRef: { key: 'API_TOKEN' } },
52
+ dispatch: { method: 'POST', pathTemplate: '/jobs', bodyTemplate: '{{input.job}}' },
53
+ poll: { method: 'GET', pathTemplate: '/jobs/{{input.jobId}}' },
54
+ response: {
55
+ statusPath: 'state',
56
+ statusMap: [
57
+ { from: 'running', to: 'running' },
58
+ { from: 'completed', to: 'done' },
59
+ { from: 'error', to: 'failed' },
60
+ ],
61
+ resultPath: 'result',
62
+ },
63
+ },
64
+ environment: {
65
+ providerId: 'my-envs',
66
+ label: 'My environment provider',
67
+ baseUrl: 'https://envs.example.com',
68
+ auth: { type: 'bearer', secretRef: { key: 'API_TOKEN' } },
69
+ provision: { method: 'POST', pathTemplate: '/environments', bodyTemplate: '{}' },
70
+ status: { method: 'GET', pathTemplate: '/environments/{{provision.id}}' },
71
+ teardown: { method: 'DELETE', pathTemplate: '/environments/{{provision.id}}' },
72
+ response: {
73
+ urlPath: 'url',
74
+ statusPath: 'status',
75
+ statusMap: [
76
+ { from: 'building', to: 'provisioning' },
77
+ { from: 'ready', to: 'ready' },
78
+ ],
79
+ },
80
+ },
81
+ }
82
+
83
+ const schema = computed(() =>
84
+ props.kind === 'runner-pool' ? runnerPoolManifestSchema : environmentManifestSchema,
85
+ )
86
+
87
+ const text = ref('')
88
+ const secrets = ref<Record<string, string>>({})
89
+
90
+ /** Seed the editor from the saved manifest (an edit) or the starter (a first connect). */
91
+ function seed() {
92
+ const base = props.savedManifest ?? STARTERS[props.kind]
93
+ text.value = JSON.stringify(base, null, 2)
94
+ secrets.value = {}
95
+ }
96
+
97
+ // Re-seed on first mount and whenever the saved manifest changes (e.g. after a successful
98
+ // save reloads the descriptor) — the saved manifest is the new canonical text and the
99
+ // just-saved secrets are cleared from the write-only inputs.
100
+ watch(() => props.savedManifest, seed, { immediate: true })
101
+
102
+ /** Parse the textarea; null value on a JSON syntax error. */
103
+ const parsed = computed<{ ok: boolean; value?: Record<string, unknown> }>(() => {
104
+ const raw = text.value.trim()
105
+ if (!raw) return { ok: false }
106
+ try {
107
+ const value = JSON.parse(raw)
108
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) return { ok: false }
109
+ return { ok: true, value: value as Record<string, unknown> }
110
+ } catch {
111
+ return { ok: false }
112
+ }
113
+ })
114
+
115
+ const jsonError = computed(() => text.value.trim().length > 0 && !parsed.value.ok)
116
+
117
+ /** Validate the parsed object against the wire contract; surface the first issue. */
118
+ const schemaError = computed<string | null>(() => {
119
+ if (!parsed.value.ok || !parsed.value.value) return null
120
+ const result = v.safeParse(schema.value, parsed.value.value)
121
+ if (result.success) return null
122
+ const issue = result.issues[0]
123
+ if (!issue) return t('settings.providerConnection.manifestEditor.invalidShape')
124
+ const path = (issue.path ?? []).map((p) => String((p as { key?: unknown }).key ?? '')).join('.')
125
+ return path ? `${path}: ${issue.message}` : issue.message
126
+ })
127
+
128
+ const validManifest = computed<Record<string, unknown> | null>(() =>
129
+ parsed.value.ok && parsed.value.value && !schemaError.value ? parsed.value.value : null,
130
+ )
131
+
132
+ /**
133
+ * Every secret key the manifest's auth scheme references, discovered generically by walking
134
+ * the parsed object for any `*SecretRef` (or `secretRef`) with a string `key`. Covers bearer
135
+ * / api_key / basic / oauth2 / custom_headers without hard-coding each auth variant.
136
+ */
137
+ const secretKeys = computed<string[]>(() => {
138
+ const out = new Set<string>()
139
+ const walk = (node: unknown) => {
140
+ if (Array.isArray(node)) {
141
+ for (const item of node) walk(item)
142
+ return
143
+ }
144
+ if (node && typeof node === 'object') {
145
+ for (const [key, val] of Object.entries(node)) {
146
+ if (
147
+ /secretref$/i.test(key) &&
148
+ val &&
149
+ typeof val === 'object' &&
150
+ typeof (val as { key?: unknown }).key === 'string'
151
+ ) {
152
+ out.add((val as { key: string }).key)
153
+ } else {
154
+ walk(val)
155
+ }
156
+ }
157
+ }
158
+ }
159
+ if (parsed.value.value) walk(parsed.value.value)
160
+ return [...out]
161
+ })
162
+
163
+ // register() replaces the whole bundle, so every referenced secret must be supplied to save.
164
+ const allSecretsSupplied = computed(() =>
165
+ secretKeys.value.every((k) => (secrets.value[k] ?? '').trim().length > 0),
166
+ )
167
+ const canSave = computed(() => !!validManifest.value && allSecretsSupplied.value)
168
+ // A test can probe with whatever secrets are filled in (a partial probe is still useful).
169
+ const canTest = computed(() => !!validManifest.value)
170
+
171
+ function filledSecrets(): Record<string, string> {
172
+ const out: Record<string, string> = {}
173
+ for (const k of secretKeys.value) {
174
+ const val = (secrets.value[k] ?? '').trim()
175
+ if (val) out[k] = val
176
+ }
177
+ return out
178
+ }
179
+
180
+ function onTest() {
181
+ if (!validManifest.value) return
182
+ emit('test', { manifest: validManifest.value, secrets: filledSecrets() })
183
+ }
184
+
185
+ function onSave() {
186
+ if (!canSave.value || !validManifest.value) return
187
+ emit('save', { manifest: validManifest.value, secrets: filledSecrets() })
188
+ }
189
+ </script>
190
+
191
+ <template>
192
+ <div class="space-y-3 rounded-lg border border-dashed border-slate-700 p-3">
193
+ <p class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
194
+ {{ t('settings.providerConnection.manifestEditor.title') }}
195
+ </p>
196
+
197
+ <UFormField
198
+ :label="t('settings.providerConnection.manifestEditor.jsonLabel')"
199
+ :help="t('settings.providerConnection.manifestEditor.jsonHelp')"
200
+ >
201
+ <UTextarea
202
+ v-model="text"
203
+ :rows="16"
204
+ class="w-full font-mono text-xs"
205
+ data-testid="manifest-editor-json"
206
+ spellcheck="false"
207
+ />
208
+ </UFormField>
209
+
210
+ <p v-if="!savedManifest && !jsonError && !schemaError" class="text-[11px] text-slate-500">
211
+ {{ t('settings.providerConnection.manifestEditor.starterHint') }}
212
+ </p>
213
+
214
+ <!-- Parse + shape errors, validated against the same contract the backend enforces. -->
215
+ <p
216
+ v-if="jsonError"
217
+ class="rounded-md border border-rose-500/40 bg-rose-950/40 px-3 py-2 text-xs text-rose-200"
218
+ data-testid="manifest-editor-error"
219
+ >
220
+ {{ t('settings.providerConnection.manifestEditor.invalidJson') }}
221
+ </p>
222
+ <p
223
+ v-else-if="schemaError"
224
+ class="rounded-md border border-amber-500/40 bg-amber-950/40 px-3 py-2 text-xs text-amber-200"
225
+ data-testid="manifest-editor-error"
226
+ >
227
+ {{ t('settings.providerConnection.manifestEditor.schemaError', { message: schemaError }) }}
228
+ </p>
229
+
230
+ <!-- Secret sub-form: one write-only input per secret key the manifest references. -->
231
+ <div class="space-y-2">
232
+ <p class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
233
+ {{ t('settings.providerConnection.manifestEditor.secretsLabel') }}
234
+ </p>
235
+ <p v-if="!secretKeys.length" class="text-[11px] text-slate-500">
236
+ {{ t('settings.providerConnection.manifestEditor.noSecrets') }}
237
+ </p>
238
+ <p v-else-if="connected" class="text-[11px] text-amber-300/80">
239
+ {{ t('settings.providerConnection.manifestEditor.reenterSecrets') }}
240
+ </p>
241
+ <UFormField v-for="key in secretKeys" :key="key" :label="key">
242
+ <UInput
243
+ v-model="secrets[key]"
244
+ type="password"
245
+ class="w-full font-mono"
246
+ autocomplete="off"
247
+ :data-testid="`manifest-editor-secret-${key}`"
248
+ />
249
+ </UFormField>
250
+ </div>
251
+
252
+ <div v-if="supportsTest" class="flex items-center gap-2">
253
+ <UButton
254
+ color="neutral"
255
+ variant="soft"
256
+ size="sm"
257
+ icon="i-lucide-plug-zap"
258
+ :loading="testing"
259
+ :disabled="!canTest"
260
+ data-testid="manifest-editor-test"
261
+ @click="onTest()"
262
+ >
263
+ {{ t('settings.providerConnection.test.button') }}
264
+ </UButton>
265
+ <span v-if="testResult && testResult.ok" class="text-xs text-emerald-400">
266
+ {{ testResult.message ?? t('settings.providerConnection.test.ok') }}
267
+ </span>
268
+ <span v-else-if="testResult" class="text-xs text-rose-400">
269
+ {{ testResult.message ?? t('settings.providerConnection.test.failed') }}
270
+ </span>
271
+ </div>
272
+
273
+ <div class="flex justify-end">
274
+ <UButton
275
+ color="primary"
276
+ size="sm"
277
+ :loading="busy"
278
+ :disabled="!canSave"
279
+ data-testid="manifest-editor-save"
280
+ @click="onSave()"
281
+ >
282
+ {{ connected ? t('common.save') : t('settings.providerConnection.form.connect') }}
283
+ </UButton>
284
+ </div>
285
+ </div>
286
+ </template>
@@ -69,8 +69,8 @@ const AccountSettingsPanel = defineAsyncComponent(
69
69
  const ObservabilityConnectionPanel = defineAsyncComponent(
70
70
  () => import('~/components/settings/ObservabilityConnectionPanel.vue'),
71
71
  )
72
- const ProviderConnectionPanel = defineAsyncComponent(
73
- () => import('~/components/settings/ProviderConnectionPanel.vue'),
72
+ const InfrastructureWindow = defineAsyncComponent(
73
+ () => import('~/components/settings/InfrastructureWindow.vue'),
74
74
  )
75
75
  const ModelConfigurationPanel = defineAsyncComponent(
76
76
  () => import('~/components/settings/ModelConfigurationPanel.vue'),
@@ -283,7 +283,7 @@ watch(
283
283
  <WorkspaceSettingsPanel v-if="ui.workspaceSettingsOpen" />
284
284
  <AccountSettingsPanel v-if="ui.accountSettingsOpen" />
285
285
  <ObservabilityConnectionPanel v-if="ui.observabilityConnectionOpen" />
286
- <ProviderConnectionPanel v-if="ui.providerConnectionKind" />
286
+ <InfrastructureWindow v-if="ui.infrastructureOpen" />
287
287
  <ModelConfigurationPanel v-if="ui.modelConfigOpen" />
288
288
  <LocalModelEndpointsPanel v-if="ui.localModelsOpen" />
289
289
  <LocalModeSettingsPanel v-if="ui.localModeSettingsOpen" />
package/app/stores/ui.ts CHANGED
@@ -125,9 +125,13 @@ 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
- // Infrastructure provider connect panels (ephemeral-environment provider + self-hosted
129
- // runner pool). One panel renders whichever kind is open; null closed.
130
- const providerConnectionKind = ref<'environment' | 'runner-pool' | null>(null)
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.
133
+ const infrastructureOpen = ref(false)
134
+ const infrastructureTab = ref<'environment' | 'runner-pool'>('runner-pool')
131
135
  const modelConfigOpen = ref(false)
132
136
  // LLM-vendor subscription credentials (the token pool powering the Claude Code
133
137
  // / Codex harnesses). `vendorCredentialsTab` lets a caller deep-link to one tab —
@@ -477,10 +481,11 @@ export const useUiStore = defineStore('ui', () => {
477
481
  }
478
482
  function openProviderConnection(kind: 'environment' | 'runner-pool') {
479
483
  resetHubReturn()
480
- providerConnectionKind.value = kind
484
+ infrastructureTab.value = kind
485
+ infrastructureOpen.value = true
481
486
  }
482
487
  function closeProviderConnection() {
483
- providerConnectionKind.value = null
488
+ infrastructureOpen.value = false
484
489
  }
485
490
  function openModelConfig() {
486
491
  modelConfigOpen.value = true
@@ -662,7 +667,8 @@ export const useUiStore = defineStore('ui', () => {
662
667
  accountSettingsOpen,
663
668
  accountSettingsTab,
664
669
  observabilityConnectionOpen,
665
- providerConnectionKind,
670
+ infrastructureOpen,
671
+ infrastructureTab,
666
672
  modelConfigOpen,
667
673
  vendorCredentialsOpen,
668
674
  vendorCredentialsTab,
@@ -1176,13 +1176,11 @@
1176
1176
  "label": "Post-release health",
1177
1177
  "description": "Watch monitors and SLOs after a release ships (Datadog)."
1178
1178
  },
1179
- "environment": {
1180
- "label": "Ephemeral environments",
1181
- "description": "Where the Tester agent runs against a live preview environment."
1182
- },
1183
- "runnerPool": {
1184
- "label": "Self-hosted runner pool",
1185
- "description": "Where the coding agents run when not using Cloudflare Containers."
1179
+ "infrastructure": {
1180
+ "label": "Infrastructure",
1181
+ "description": "Self-hosted runner pool for container agents and ephemeral test environments.",
1182
+ "agents": "Agents: {state}",
1183
+ "envs": "Envs: {state}"
1186
1184
  },
1187
1185
  "localMode": {
1188
1186
  "label": "Local mode",
@@ -1251,6 +1249,12 @@
1251
1249
  },
1252
1250
  "providerConnection": {
1253
1251
  "fallbackTitle": "Provider",
1252
+ "windowTitle": "Infrastructure",
1253
+ "noneAvailable": "No infrastructure providers are enabled on this deployment.",
1254
+ "tabs": {
1255
+ "containerAgents": "Container agents",
1256
+ "testEnvironments": "Test environments"
1257
+ },
1254
1258
  "kind": {
1255
1259
  "environment": {
1256
1260
  "title": "Ephemeral environment provider",
@@ -1261,6 +1265,18 @@
1261
1265
  "blurb": "Where the coding agents run when not using Cloudflare Containers. Configure the pool scheduler endpoint and credentials."
1262
1266
  }
1263
1267
  },
1268
+ "manifestEditor": {
1269
+ "title": "Provider manifest",
1270
+ "jsonLabel": "Manifest (JSON)",
1271
+ "jsonHelp": "Describe your provider's API declaratively: the base URL, auth scheme, request templates, and the response mapping. Validated against the same contract the server enforces.",
1272
+ "invalidJson": "The manifest is not valid JSON.",
1273
+ "invalidShape": "The manifest does not match the expected shape.",
1274
+ "schemaError": "Manifest problem: {message}",
1275
+ "secretsLabel": "Secrets",
1276
+ "noSecrets": "This manifest references no secrets.",
1277
+ "reenterSecrets": "Re-enter every secret to save. Stored secrets are write-only and aren't shown.",
1278
+ "starterHint": "This is a starter example. Edit it to match your provider's API."
1279
+ },
1264
1280
  "delegation": {
1265
1281
  "title": "Local delegation",
1266
1282
  "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.",
@@ -1272,13 +1288,10 @@
1272
1288
  "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.",
1273
1289
  "updateFailed": "Could not update delegation"
1274
1290
  },
1275
- "runnerPoolLocalHint": "Register your pool here, then enable \"Run container agents on the runner pool\" on the {link} screen to route this workspace's agents to it.",
1276
- "ephemeralEnvironments": "Ephemeral environments",
1277
1291
  "viewLogs": "View logs",
1278
1292
  "hideLogs": "Hide logs",
1279
1293
  "connectedAt": "Connected · {baseUrl}",
1280
1294
  "missingConfig": "Missing required config: {fields}",
1281
- "manifestEditorUnavailable": "This provider is configured by authoring a manifest. The in-app manifest editor isn't available yet — register it via the API for now.",
1282
1295
  "form": {
1283
1296
  "updateConfiguration": "Update configuration",
1284
1297
  "connect": "Connect",
@@ -1137,13 +1137,11 @@
1137
1137
  "label": "Salud posterior al lanzamiento",
1138
1138
  "description": "Vigila los monitores y SLO después de publicar una versión (Datadog)."
1139
1139
  },
1140
- "environment": {
1141
- "label": "Entornos efímeros",
1142
- "description": "Donde el agente Tester se ejecuta contra un entorno de vista previa en vivo."
1143
- },
1144
- "runnerPool": {
1145
- "label": "Grupo de ejecutores autoalojado",
1146
- "description": "Donde se ejecutan los agentes de código cuando no se usan los Cloudflare Containers."
1140
+ "infrastructure": {
1141
+ "label": "Infraestructura",
1142
+ "description": "Grupo de ejecutores autoalojado para los agentes de contenedor y entornos de prueba efímeros.",
1143
+ "agents": "Agentes: {state}",
1144
+ "envs": "Entornos: {state}"
1147
1145
  },
1148
1146
  "localMode": {
1149
1147
  "label": "Modo local",
@@ -1212,6 +1210,12 @@
1212
1210
  },
1213
1211
  "providerConnection": {
1214
1212
  "fallbackTitle": "Proveedor",
1213
+ "windowTitle": "Infraestructura",
1214
+ "noneAvailable": "No hay proveedores de infraestructura habilitados en este despliegue.",
1215
+ "tabs": {
1216
+ "containerAgents": "Agentes de contenedor",
1217
+ "testEnvironments": "Entornos de prueba"
1218
+ },
1215
1219
  "kind": {
1216
1220
  "environment": {
1217
1221
  "title": "Proveedor de entornos efímeros",
@@ -1222,6 +1226,18 @@
1222
1226
  "blurb": "Donde se ejecutan los agentes de codificación cuando no se usan los contenedores de Cloudflare. Configura el endpoint del planificador del grupo y las credenciales."
1223
1227
  }
1224
1228
  },
1229
+ "manifestEditor": {
1230
+ "title": "Manifiesto del proveedor",
1231
+ "jsonLabel": "Manifiesto (JSON)",
1232
+ "jsonHelp": "Describe la API de tu proveedor de forma declarativa: la URL base, el esquema de autenticación, las plantillas de solicitud y el mapeo de la respuesta. Se valida con el mismo contrato que aplica el servidor.",
1233
+ "invalidJson": "El manifiesto no es JSON válido.",
1234
+ "invalidShape": "El manifiesto no coincide con la estructura esperada.",
1235
+ "schemaError": "Problema con el manifiesto — {message}",
1236
+ "secretsLabel": "Secretos",
1237
+ "noSecrets": "Este manifiesto no hace referencia a ningún secreto.",
1238
+ "reenterSecrets": "Vuelve a introducir cada secreto para guardar: los secretos almacenados son de solo escritura y no se muestran.",
1239
+ "starterHint": "Este es un ejemplo inicial. Edítalo para que coincida con la API de tu proveedor."
1240
+ },
1225
1241
  "delegation": {
1226
1242
  "title": "Delegación local",
1227
1243
  "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.",
@@ -1233,13 +1249,10 @@
1233
1249
  "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.",
1234
1250
  "updateFailed": "No se pudo actualizar la delegación"
1235
1251
  },
1236
- "runnerPoolLocalHint": "Registra tu grupo aquí y luego activa \"Ejecutar los agentes de contenedor en el grupo de ejecutores\" en la pantalla {link} para enrutar los agentes de este espacio de trabajo hacia él.",
1237
- "ephemeralEnvironments": "Entornos efímeros",
1238
1252
  "viewLogs": "Ver registros",
1239
1253
  "hideLogs": "Ocultar registros",
1240
1254
  "connectedAt": "Conectado · {baseUrl}",
1241
1255
  "missingConfig": "Falta configuración obligatoria: {fields}",
1242
- "manifestEditorUnavailable": "Este proveedor se configura redactando un manifiesto. El editor de manifiestos integrado aún no está disponible; regístralo a través de la API por ahora.",
1243
1256
  "form": {
1244
1257
  "updateConfiguration": "Actualizar configuración",
1245
1258
  "connect": "Conectar",
@@ -1137,13 +1137,11 @@
1137
1137
  "label": "Santé après publication",
1138
1138
  "description": "Surveillez les moniteurs et les SLO après la publication d'une version (Datadog)."
1139
1139
  },
1140
- "environment": {
1141
- "label": "Environnements éphémères",
1142
- "description": " où l'agent Tester s'exécute contre un environnement d'aperçu en direct."
1143
- },
1144
- "runnerPool": {
1145
- "label": "Pool d'exécuteurs auto-hébergé",
1146
- "description": "Là où les agents de code s'exécutent quand les Cloudflare Containers ne sont pas utilisés."
1140
+ "infrastructure": {
1141
+ "label": "Infrastructure",
1142
+ "description": "Pool d'exécuteurs auto-hébergé pour les agents de conteneur et environnements de test éphémères.",
1143
+ "agents": "Agents : {state}",
1144
+ "envs": "Environnements : {state}"
1147
1145
  },
1148
1146
  "localMode": {
1149
1147
  "label": "Mode local",
@@ -1212,6 +1210,12 @@
1212
1210
  },
1213
1211
  "providerConnection": {
1214
1212
  "fallbackTitle": "Fournisseur",
1213
+ "windowTitle": "Infrastructure",
1214
+ "noneAvailable": "Aucun fournisseur d'infrastructure n'est activé sur ce déploiement.",
1215
+ "tabs": {
1216
+ "containerAgents": "Agents de conteneur",
1217
+ "testEnvironments": "Environnements de test"
1218
+ },
1215
1219
  "kind": {
1216
1220
  "environment": {
1217
1221
  "title": "Fournisseur d'environnements éphémères",
@@ -1222,6 +1226,18 @@
1222
1226
  "blurb": "Là où les agents de codage s'exécutent lorsque les conteneurs Cloudflare ne sont pas utilisés. Configurez le point de terminaison du planificateur du pool et les identifiants."
1223
1227
  }
1224
1228
  },
1229
+ "manifestEditor": {
1230
+ "title": "Manifeste du fournisseur",
1231
+ "jsonLabel": "Manifeste (JSON)",
1232
+ "jsonHelp": "Décrivez l'API de votre fournisseur de manière déclarative : l'URL de base, le schéma d'authentification, les modèles de requête et le mappage des réponses. Validé par rapport au même contrat que celui appliqué par le serveur.",
1233
+ "invalidJson": "Le manifeste n'est pas un JSON valide.",
1234
+ "invalidShape": "Le manifeste ne correspond pas à la forme attendue.",
1235
+ "schemaError": "Problème de manifeste — {message}",
1236
+ "secretsLabel": "Secrets",
1237
+ "noSecrets": "Ce manifeste ne référence aucun secret.",
1238
+ "reenterSecrets": "Saisissez à nouveau chaque secret pour enregistrer : les secrets stockés sont en écriture seule et ne sont pas affichés.",
1239
+ "starterHint": "Ceci est un exemple de départ. Modifiez-le pour qu'il corresponde à l'API de votre fournisseur."
1240
+ },
1225
1241
  "delegation": {
1226
1242
  "title": "Délégation locale",
1227
1243
  "intro": "Par défaut, cette machine exécute tout en local : les agents de conteneur sur le Docker de l'hôte, l'infrastructure du Tester via docker-compose dans le conteneur. Activez les options ci-dessous pour déléguer l'une ou l'autre de ces tâches à un service externe. S'applique uniquement en mode local.",
@@ -1233,13 +1249,10 @@
1233
1249
  "envHint": "Montez l'environnement d'aperçu du Tester via le fournisseur d'environnements configuré ci-dessous plutôt que via docker-compose dans le conteneur. Connectez d'abord un fournisseur pour activer cette option.",
1234
1250
  "updateFailed": "Impossible de mettre à jour la délégation"
1235
1251
  },
1236
- "runnerPoolLocalHint": "Enregistrez votre pool ici, puis activez « Exécuter les agents de conteneur sur le pool d'exécuteurs » sur l'écran {link} pour y router les agents de cet espace de travail.",
1237
- "ephemeralEnvironments": "Environnements éphémères",
1238
1252
  "viewLogs": "Voir les journaux",
1239
1253
  "hideLogs": "Masquer les journaux",
1240
1254
  "connectedAt": "Connecté · {baseUrl}",
1241
1255
  "missingConfig": "Configuration requise manquante : {fields}",
1242
- "manifestEditorUnavailable": "Ce fournisseur se configure en rédigeant un manifeste. L'éditeur de manifeste intégré n'est pas encore disponible ; enregistrez-le via l'API pour le moment.",
1243
1256
  "form": {
1244
1257
  "updateConfiguration": "Mettre à jour la configuration",
1245
1258
  "connect": "Connecter",
@@ -1137,13 +1137,11 @@
1137
1137
  "label": "Kondycja po wydaniu",
1138
1138
  "description": "Obserwuj monitory i SLO po wdrożeniu wydania (Datadog)."
1139
1139
  },
1140
- "environment": {
1141
- "label": "Środowiska efemeryczne",
1142
- "description": "Gdzie agent Tester działa wobec działającego środowiska podglądu."
1143
- },
1144
- "runnerPool": {
1145
- "label": "Samodzielnie hostowana pula wykonawców",
1146
- "description": "Gdzie działają agenci kodu, gdy nie używają Cloudflare Containers."
1140
+ "infrastructure": {
1141
+ "label": "Infrastruktura",
1142
+ "description": "Samodzielnie hostowana pula wykonawców dla agentów kontenerowych oraz efemeryczne środowiska testowe.",
1143
+ "agents": "Agenci: {state}",
1144
+ "envs": "Środowiska: {state}"
1147
1145
  },
1148
1146
  "localMode": {
1149
1147
  "label": "Tryb lokalny",
@@ -1212,6 +1210,12 @@
1212
1210
  },
1213
1211
  "providerConnection": {
1214
1212
  "fallbackTitle": "Dostawca",
1213
+ "windowTitle": "Infrastruktura",
1214
+ "noneAvailable": "W tym wdrożeniu nie włączono żadnych dostawców infrastruktury.",
1215
+ "tabs": {
1216
+ "containerAgents": "Agenci kontenerowi",
1217
+ "testEnvironments": "Środowiska testowe"
1218
+ },
1215
1219
  "kind": {
1216
1220
  "environment": {
1217
1221
  "title": "Dostawca środowisk efemerycznych",
@@ -1222,6 +1226,18 @@
1222
1226
  "blurb": "Tam, gdzie działają agenci kodujący, gdy nie są używane kontenery Cloudflare. Skonfiguruj punkt końcowy harmonogramu puli oraz poświadczenia."
1223
1227
  }
1224
1228
  },
1229
+ "manifestEditor": {
1230
+ "title": "Manifest dostawcy",
1231
+ "jsonLabel": "Manifest (JSON)",
1232
+ "jsonHelp": "Opisz API swojego dostawcy deklaratywnie: bazowy adres URL, schemat uwierzytelniania, szablony żądań oraz mapowanie odpowiedzi. Walidowany według tego samego kontraktu, który egzekwuje serwer.",
1233
+ "invalidJson": "Manifest nie jest prawidłowym formatem JSON.",
1234
+ "invalidShape": "Manifest nie odpowiada oczekiwanemu kształtowi.",
1235
+ "schemaError": "Problem z manifestem — {message}",
1236
+ "secretsLabel": "Sekrety",
1237
+ "noSecrets": "Ten manifest nie odwołuje się do żadnych sekretów.",
1238
+ "reenterSecrets": "Wprowadź ponownie każdy sekret, aby zapisać — przechowywane sekrety są tylko do zapisu i nie są wyświetlane.",
1239
+ "starterHint": "To jest przykład startowy. Zmodyfikuj go, aby pasował do API Twojego dostawcy."
1240
+ },
1225
1241
  "delegation": {
1226
1242
  "title": "Delegowanie lokalne",
1227
1243
  "intro": "Domyślnie ta maszyna uruchamia wszystko lokalnie — agentów kontenerowych na Dockerze hosta, a infrastrukturę Testera przez docker-compose wewnątrz kontenera. Włącz poniższe opcje, aby przekazać dowolne z tych zadań do usługi zewnętrznej. Dotyczy wyłącznie trybu lokalnego.",
@@ -1233,13 +1249,10 @@
1233
1249
  "envHint": "Uruchom środowisko podglądowe Testera przez dostawcę środowisk skonfigurowanego poniżej zamiast przez docker-compose wewnątrz kontenera. Najpierw połącz dostawcę, aby to włączyć.",
1234
1250
  "updateFailed": "Nie udało się zaktualizować delegowania"
1235
1251
  },
1236
- "runnerPoolLocalHint": "Zarejestruj tutaj swoją pulę, a następnie włącz „Uruchamiaj agentów kontenerowych w puli wykonawców” na ekranie {link}, aby kierować do niej agentów tego obszaru roboczego.",
1237
- "ephemeralEnvironments": "Środowiska efemeryczne",
1238
1252
  "viewLogs": "Pokaż dzienniki",
1239
1253
  "hideLogs": "Ukryj dzienniki",
1240
1254
  "connectedAt": "Połączono · {baseUrl}",
1241
1255
  "missingConfig": "Brakuje wymaganej konfiguracji: {fields}",
1242
- "manifestEditorUnavailable": "Tego dostawcę konfiguruje się, redagując manifest. Wbudowany edytor manifestu nie jest jeszcze dostępny — na razie zarejestruj go przez API.",
1243
1256
  "form": {
1244
1257
  "updateConfiguration": "Zaktualizuj konfigurację",
1245
1258
  "connect": "Połącz",