@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.
- package/app/components/layout/IntegrationsHub.vue +44 -23
- package/app/components/settings/InfrastructureWindow.vue +221 -0
- package/app/components/settings/ProviderConnectionTab.vue +358 -0
- package/app/components/settings/ProviderManifestEditor.vue +286 -0
- package/app/pages/index.vue +3 -3
- package/app/stores/ui.ts +12 -6
- package/i18n/locales/en.json +23 -10
- package/i18n/locales/es.json +23 -10
- package/i18n/locales/fr.json +23 -10
- package/i18n/locales/pl.json +23 -10
- package/i18n/locales/uk.json +23 -10
- package/package.json +2 -1
- package/app/components/settings/ProviderConnectionPanel.vue +0 -524
|
@@ -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>
|
package/app/pages/index.vue
CHANGED
|
@@ -69,8 +69,8 @@ const AccountSettingsPanel = defineAsyncComponent(
|
|
|
69
69
|
const ObservabilityConnectionPanel = defineAsyncComponent(
|
|
70
70
|
() => import('~/components/settings/ObservabilityConnectionPanel.vue'),
|
|
71
71
|
)
|
|
72
|
-
const
|
|
73
|
-
() => import('~/components/settings/
|
|
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
|
-
<
|
|
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
|
-
//
|
|
129
|
-
// runner pool
|
|
130
|
-
|
|
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
|
-
|
|
484
|
+
infrastructureTab.value = kind
|
|
485
|
+
infrastructureOpen.value = true
|
|
481
486
|
}
|
|
482
487
|
function closeProviderConnection() {
|
|
483
|
-
|
|
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
|
-
|
|
670
|
+
infrastructureOpen,
|
|
671
|
+
infrastructureTab,
|
|
666
672
|
modelConfigOpen,
|
|
667
673
|
vendorCredentialsOpen,
|
|
668
674
|
vendorCredentialsTab,
|
package/i18n/locales/en.json
CHANGED
|
@@ -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
|
-
"
|
|
1180
|
-
"label": "
|
|
1181
|
-
"description": "
|
|
1182
|
-
|
|
1183
|
-
|
|
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",
|
package/i18n/locales/es.json
CHANGED
|
@@ -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
|
-
"
|
|
1141
|
-
"label": "
|
|
1142
|
-
"description": "
|
|
1143
|
-
|
|
1144
|
-
|
|
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",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -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
|
-
"
|
|
1141
|
-
"label": "
|
|
1142
|
-
"description": "
|
|
1143
|
-
|
|
1144
|
-
|
|
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",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -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
|
-
"
|
|
1141
|
-
"label": "
|
|
1142
|
-
"description": "
|
|
1143
|
-
|
|
1144
|
-
|
|
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",
|