@cat-factory/app 0.63.0 → 0.63.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,6 +79,51 @@ watch(
79
79
 
80
80
  const busy = ref(false)
81
81
 
82
+ // Connection-probe state for the kube engine forms (workspace + per-user override kept
83
+ // separate so a probe result lands on the form it came from). The probe reaches the apiserver
84
+ // with the supplied config + token via the per-type handler test endpoint — nothing persisted.
85
+ type TestResult = { ok: boolean; message?: string } | null
86
+ const kubeTesting = ref(false)
87
+ const kubeTestResult = ref<TestResult>(null)
88
+ const kubeOverrideTesting = ref(false)
89
+ const kubeOverrideTestResult = ref<TestResult>(null)
90
+
91
+ async function testKube(payload: { config: KubeHandlerConfig; secrets: Record<string, string> }) {
92
+ kubeTesting.value = true
93
+ kubeTestResult.value = null
94
+ try {
95
+ kubeTestResult.value = await infra.testHandler({
96
+ config: payload.config,
97
+ secrets: payload.secrets,
98
+ })
99
+ } catch (e) {
100
+ kubeTestResult.value = { ok: false, message: e instanceof Error ? e.message : String(e) }
101
+ } finally {
102
+ kubeTesting.value = false
103
+ }
104
+ }
105
+
106
+ async function testKubeOverride(payload: {
107
+ config: KubeHandlerConfig
108
+ secrets: Record<string, string>
109
+ }) {
110
+ kubeOverrideTesting.value = true
111
+ kubeOverrideTestResult.value = null
112
+ try {
113
+ kubeOverrideTestResult.value = await infra.testHandler({
114
+ config: payload.config,
115
+ secrets: payload.secrets,
116
+ })
117
+ } catch (e) {
118
+ kubeOverrideTestResult.value = {
119
+ ok: false,
120
+ message: e instanceof Error ? e.message : String(e),
121
+ }
122
+ } finally {
123
+ kubeOverrideTesting.value = false
124
+ }
125
+ }
126
+
82
127
  async function saveKube(payload: { config: KubeHandlerConfig; secrets: Record<string, string> }) {
83
128
  busy.value = true
84
129
  try {
@@ -170,6 +215,58 @@ const customSavedManifest = computed<Record<string, unknown> | undefined>(() =>
170
215
  : undefined
171
216
  })
172
217
 
218
+ // The registry backend that builds the `remote-custom` handler's provider. The generic
219
+ // built-in `manifest` (BYO HTTP API) is the default; a deployment that registered a native
220
+ // custom env backend (e.g. Kargo) can be picked here so the handler is pinned to it instead of
221
+ // silently resolving to the generic manifest provider. Only backends that serve the
222
+ // `remote-custom` engine are offered (the snapshot advertises each backend's engines).
223
+ const providerConnections = useProviderConnectionsStore()
224
+ const customBackendOptions = computed(() =>
225
+ providerConnections
226
+ .backendKindsFor('environment')
227
+ .filter((o) => o.engines?.includes('remote-custom'))
228
+ .map((o) => ({ label: o.label, value: o.kind })),
229
+ )
230
+ const selectedBackendKind = ref<string>('manifest')
231
+ // When editing a saved handler, reflect the backend it was registered with; when switching to a
232
+ // custom type with no handler yet, fall back to the first offered backend (the generic manifest).
233
+ watch(
234
+ [customHandler, customBackendOptions],
235
+ ([handler, options]) => {
236
+ const valid = (k: string) => options.some((o) => o.value === k)
237
+ if (handler?.backendKind && valid(handler.backendKind)) {
238
+ selectedBackendKind.value = handler.backendKind
239
+ } else if (!valid(selectedBackendKind.value)) {
240
+ selectedBackendKind.value = options.find((o) => o.value === 'manifest')?.value
241
+ ? 'manifest'
242
+ : (options[0]?.value ?? 'manifest')
243
+ }
244
+ },
245
+ { immediate: true },
246
+ )
247
+
248
+ // For a NEW handler on a NON-generic backend, prefill the editor from that backend's manifest
249
+ // template (its self-described skeleton + secret refs) so the operator isn't faced with the
250
+ // generic starter. A saved handler's own manifest always takes precedence.
251
+ const templateManifest = ref<Record<string, unknown> | undefined>(undefined)
252
+ watch(
253
+ [selectedBackendKind, customHandler],
254
+ async ([kind, handler]) => {
255
+ if (handler || kind === 'manifest') {
256
+ templateManifest.value = undefined
257
+ return
258
+ }
259
+ const descriptor = await providerConnections.fetchDescriptor('environment', kind)
260
+ templateManifest.value = descriptor?.manifestTemplate
261
+ },
262
+ { immediate: true },
263
+ )
264
+ // The manifest to seed the editor with: a saved handler's stored manifest, else the picked
265
+ // backend's template (custom kinds), else undefined (the editor's generic starter).
266
+ const customEditorManifest = computed<Record<string, unknown> | undefined>(
267
+ () => customSavedManifest.value ?? templateManifest.value,
268
+ )
269
+
173
270
  async function saveCustom(payload: {
174
271
  manifest: Record<string, unknown>
175
272
  secrets: Record<string, string>
@@ -186,6 +283,9 @@ async function saveCustom(payload: {
186
283
  provisionType: 'custom',
187
284
  manifestId: selectedCustomId.value,
188
285
  config,
286
+ // Pin the chosen registry backend so a native custom backend (e.g. Kargo) builds the
287
+ // provider — absent, the engine would resolve to the generic manifest provider.
288
+ backendKind: selectedBackendKind.value,
189
289
  secrets: payload.secrets,
190
290
  })
191
291
  toastSaved()
@@ -280,10 +380,11 @@ function notifyError(e: unknown) {
280
380
  <KubernetesEngineForm
281
381
  :engine="selectedKubeEngine"
282
382
  :handler="kubeHandler"
283
- :supports-test="false"
284
- :testing="false"
383
+ :supports-test="true"
384
+ :testing="kubeTesting"
285
385
  :busy="busy"
286
- :test-result="null"
386
+ :test-result="kubeTestResult"
387
+ @test="testKube"
287
388
  @save="saveKube"
288
389
  />
289
390
 
@@ -322,10 +423,11 @@ function notifyError(e: unknown) {
322
423
  <KubernetesEngineForm
323
424
  :engine="selectedKubeEngine"
324
425
  :handler="kubeUserHandler"
325
- :supports-test="false"
326
- :testing="false"
426
+ :supports-test="true"
427
+ :testing="kubeOverrideTesting"
327
428
  :busy="busy"
328
- :test-result="null"
429
+ :test-result="kubeOverrideTestResult"
430
+ @test="testKubeOverride"
329
431
  @save="saveKubeOverride"
330
432
  />
331
433
  </div>
@@ -355,6 +457,15 @@ function notifyError(e: unknown) {
355
457
  <UFormField :label="t('settings.infrastructure.handler.customTypeLabel')">
356
458
  <USelect v-model="selectedCustomId" :items="customTypeItems" />
357
459
  </UFormField>
460
+ <!-- Which registered backend builds this custom handler's provider. Shown only when a
461
+ deployment registered a custom backend beyond the generic manifest. -->
462
+ <UFormField
463
+ v-if="customBackendOptions.length > 1"
464
+ :label="t('settings.infrastructure.handler.customBackendLabel')"
465
+ :help="t('settings.infrastructure.handler.customBackendHelp')"
466
+ >
467
+ <USelect v-model="selectedBackendKind" :items="customBackendOptions" />
468
+ </UFormField>
358
469
  <p
359
470
  v-if="customHandler"
360
471
  class="flex items-center justify-between gap-2 text-[12px] text-slate-300"
@@ -373,9 +484,9 @@ function notifyError(e: unknown) {
373
484
  </p>
374
485
  <ProviderManifestEditor
375
486
  v-if="selectedCustomId"
376
- :key="selectedCustomId"
487
+ :key="`${selectedCustomId}:${selectedBackendKind}`"
377
488
  kind="environment"
378
- :saved-manifest="customSavedManifest"
489
+ :saved-manifest="customEditorManifest"
379
490
  :connected="!!customHandler"
380
491
  :stored-secret-keys="customHandler?.secretKeys ?? []"
381
492
  :supports-test="false"
@@ -115,6 +115,35 @@ watch(
115
115
  { immediate: true },
116
116
  )
117
117
 
118
+ // The local-cluster apiserver address every loopback distro (k3s / k3d / kind / minikube)
119
+ // exposes by default — see `seedForEngine`.
120
+ const LOCAL_K3S_API_SERVER = 'https://127.0.0.1:6443'
121
+
122
+ // Seed the form for the SELECTED engine, so picking an engine gives immediate feedback instead
123
+ // of a dead toggle. `local-k3s` is a low-config local cluster (k3s / k3d / kind all expose a
124
+ // loopback apiserver with a self-signed cert), so prefill its loopback defaults + flag insecure
125
+ // TLS — the operator then only pastes a ServiceAccount token and picks the URL source.
126
+ // `remote-kubernetes` starts clean. Only seeds a FRESH form (never clobbers an edit — a saved
127
+ // handler is prefilled from its stored config by the watch above).
128
+ watch(
129
+ () => props.engine,
130
+ (engine) => {
131
+ if (props.handler) return
132
+ if (engine === 'local-k3s') {
133
+ if (!form.label.trim()) form.label = 'Local k3s'
134
+ if (!form.apiServerUrl.trim()) form.apiServerUrl = LOCAL_K3S_API_SERVER
135
+ form.insecureSkipTlsVerify = true
136
+ } else {
137
+ // Clear the local-only loopback defaults so a remote engine isn't misleadingly prefilled,
138
+ // but leave anything the operator has actually typed.
139
+ if (form.apiServerUrl === LOCAL_K3S_API_SERVER) form.apiServerUrl = ''
140
+ if (form.label === 'Local k3s') form.label = ''
141
+ form.insecureSkipTlsVerify = false
142
+ }
143
+ },
144
+ { immediate: true },
145
+ )
146
+
118
147
  const servicePortValid = computed(() => {
119
148
  const raw = form.servicePort.trim()
120
149
  if (!raw) return true
@@ -185,6 +214,13 @@ function optional(label: string): string {
185
214
  }}
186
215
  </p>
187
216
 
217
+ <p
218
+ v-if="engine === 'local-k3s'"
219
+ class="rounded-md border border-sky-500/30 bg-sky-500/10 p-2 text-[11px] text-sky-200"
220
+ >
221
+ {{ t('settings.infrastructure.kubernetesEngine.localK3sHint') }}
222
+ </p>
223
+
188
224
  <UFormField :label="t('settings.infrastructure.kubernetesEngine.label')">
189
225
  <UInput
190
226
  v-model="form.label"
@@ -5,6 +5,7 @@ import {
5
5
  registerEnvironmentHandlerContract,
6
6
  removeCustomManifestTypeContract,
7
7
  removeEnvironmentUserHandlerContract,
8
+ testEnvironmentHandlerContract,
8
9
  unregisterEnvironmentHandlerContract,
9
10
  upsertCustomManifestTypeContract,
10
11
  upsertEnvironmentUserHandlerContract,
@@ -13,6 +14,7 @@ import type {
13
14
  DetectServiceProvisioningInput,
14
15
  ProvisionType,
15
16
  RegisterEnvironmentHandlerInput,
17
+ TestEnvironmentHandlerInput,
16
18
  UpsertCustomManifestTypeInput,
17
19
  UpsertEnvironmentUserHandlerBody,
18
20
  } from '@cat-factory/contracts'
@@ -35,6 +37,10 @@ export function infraHandlersApi({ send, ws }: ApiContext) {
35
37
  registerEnvironmentHandler: (workspaceId: string, body: RegisterEnvironmentHandlerInput) =>
36
38
  send(registerEnvironmentHandlerContract, { pathPrefix: ws(workspaceId), body }),
37
39
 
40
+ // Probe a candidate handler connection before saving (nothing persisted).
41
+ testEnvironmentHandler: (workspaceId: string, body: TestEnvironmentHandlerInput) =>
42
+ send(testEnvironmentHandlerContract, { pathPrefix: ws(workspaceId), body }),
43
+
38
44
  // Auto-detect a non-binding recommended provisioning config from a service's repo.
39
45
  detectServiceProvisioning: (workspaceId: string, body: DetectServiceProvisioningInput) =>
40
46
  send(detectServiceProvisioningContract, { pathPrefix: ws(workspaceId), body }),
@@ -1,32 +1,133 @@
1
1
  <!-- Early SPA loading shell painted before the JS bundle parses + Vue mounts
2
2
  (Nuxt removes it on mount). Self-contained: main.css/icon fonts aren't loaded
3
- yet, so styles + spinner are inline pure-CSS. Spinner-only keeps it
4
- locale-neutral. Wired via app.spaLoadingTemplate in nuxt.config.ts. -->
5
- <div class="cf-spa-loader" role="status" aria-label="Loading">
6
- <div class="cf-spa-loader__spinner"></div>
3
+ yet, so styles + markup are inline pure-CSS. The only copy is the "Cat Factory"
4
+ brand name (a proper noun, identical in every locale) so it stays locale-neutral.
5
+ Wired via app.spaLoadingTemplate in nuxt.config.ts. -->
6
+ <div class="cf-spa-loader" role="status" aria-label="Cat Factory is starting">
7
+ <div class="cf-spa-loader__badge">
8
+ <div class="cf-spa-loader__ring"></div>
9
+ <!-- Cat face, drawn inline so it paints without the icon font. -->
10
+ <svg class="cf-spa-loader__cat" viewBox="0 0 24 24" fill="none" aria-hidden="true">
11
+ <path
12
+ d="M4 4l3 3.2A7.5 7.5 0 0 1 12 6c1.9 0 3.6.5 5 1.2L20 4v8a8 8 0 0 1-16 0V4z"
13
+ fill="currentColor"
14
+ />
15
+ <circle cx="9" cy="11.5" r="1.1" fill="#0b1020" />
16
+ <circle cx="15" cy="11.5" r="1.1" fill="#0b1020" />
17
+ <path
18
+ d="M10.6 14.4c.4.5 2.4.5 2.8 0"
19
+ stroke="#0b1020"
20
+ stroke-width="1"
21
+ stroke-linecap="round"
22
+ />
23
+ </svg>
24
+ </div>
25
+ <div class="cf-spa-loader__text">
26
+ <span class="cf-spa-loader__brand">Cat Factory</span>
27
+ <span class="cf-spa-loader__status">
28
+ is starting<span class="cf-spa-loader__dots"><i>.</i><i>.</i><i>.</i></span>
29
+ </span>
30
+ </div>
7
31
  </div>
8
32
  <style>
9
33
  .cf-spa-loader {
10
34
  position: fixed;
11
35
  inset: 0;
12
36
  display: flex;
37
+ flex-direction: column;
13
38
  align-items: center;
14
39
  justify-content: center;
40
+ gap: 1.5rem;
15
41
  /* --board-bg / slate-950, matching the board surface AuthGate renders on. */
16
42
  background-color: #0b1020;
43
+ font-family:
44
+ ui-sans-serif,
45
+ system-ui,
46
+ -apple-system,
47
+ 'Segoe UI',
48
+ Roboto,
49
+ sans-serif;
17
50
  }
18
- .cf-spa-loader__spinner {
19
- width: 2rem;
20
- height: 2rem;
51
+ .cf-spa-loader__badge {
52
+ position: relative;
53
+ width: 5rem;
54
+ height: 5rem;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ }
59
+ .cf-spa-loader__ring {
60
+ position: absolute;
61
+ inset: 0;
21
62
  border-radius: 9999px;
22
63
  /* Track in slate-700, the active arc in indigo-500 (the app's primary). */
23
- border: 3px solid #334155;
64
+ border: 3px solid #1e293b;
24
65
  border-top-color: #6366f1;
25
66
  animation: cf-spa-spin 1s linear infinite;
26
67
  }
68
+ .cf-spa-loader__cat {
69
+ width: 2.5rem;
70
+ height: 2.5rem;
71
+ color: #818cf8;
72
+ animation: cf-spa-bob 1.6s ease-in-out infinite;
73
+ }
74
+ .cf-spa-loader__text {
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ gap: 0.25rem;
79
+ }
80
+ .cf-spa-loader__brand {
81
+ font-size: 1.35rem;
82
+ font-weight: 700;
83
+ letter-spacing: 0.01em;
84
+ background: linear-gradient(90deg, #818cf8, #6366f1);
85
+ -webkit-background-clip: text;
86
+ background-clip: text;
87
+ color: transparent;
88
+ }
89
+ .cf-spa-loader__status {
90
+ font-size: 0.875rem;
91
+ color: #94a3b8;
92
+ }
93
+ .cf-spa-loader__dots i {
94
+ font-style: normal;
95
+ animation: cf-spa-blink 1.4s infinite both;
96
+ }
97
+ .cf-spa-loader__dots i:nth-child(2) {
98
+ animation-delay: 0.2s;
99
+ }
100
+ .cf-spa-loader__dots i:nth-child(3) {
101
+ animation-delay: 0.4s;
102
+ }
27
103
  @keyframes cf-spa-spin {
28
104
  to {
29
105
  transform: rotate(360deg);
30
106
  }
31
107
  }
108
+ @keyframes cf-spa-bob {
109
+ 0%,
110
+ 100% {
111
+ transform: translateY(0);
112
+ }
113
+ 50% {
114
+ transform: translateY(-0.2rem);
115
+ }
116
+ }
117
+ @keyframes cf-spa-blink {
118
+ 0%,
119
+ 100% {
120
+ opacity: 0.2;
121
+ }
122
+ 50% {
123
+ opacity: 1;
124
+ }
125
+ }
126
+ @media (prefers-reduced-motion: reduce) {
127
+ .cf-spa-loader__ring,
128
+ .cf-spa-loader__cat,
129
+ .cf-spa-loader__dots i {
130
+ animation: none;
131
+ }
132
+ }
32
133
  </style>
@@ -6,6 +6,7 @@ import type {
6
6
  EnvironmentHandlerView,
7
7
  ProvisionType,
8
8
  RegisterEnvironmentHandlerInput,
9
+ TestEnvironmentHandlerInput,
9
10
  UpsertCustomManifestTypeInput,
10
11
  UpsertEnvironmentUserHandlerBody,
11
12
  } from '@cat-factory/contracts'
@@ -95,6 +96,12 @@ export const useInfraConfigStore = defineStore('infraConfig', () => {
95
96
  return saved
96
97
  }
97
98
 
99
+ /** Probe a candidate handler connection before saving (nothing persisted). */
100
+ async function testHandler(input: TestEnvironmentHandlerInput) {
101
+ const ws = useWorkspaceStore()
102
+ return api.testEnvironmentHandler(ws.requireId(), input)
103
+ }
104
+
98
105
  /**
99
106
  * Auto-detect a NON-BINDING recommended provisioning config from a service's repo. The SPA
100
107
  * prefills the confirm form from the result; nothing is persisted server-side. Detection is
@@ -172,6 +179,7 @@ export const useInfraConfigStore = defineStore('infraConfig', () => {
172
179
  ensureLoaded,
173
180
  handlerFor,
174
181
  registerHandler,
182
+ testHandler,
175
183
  detectProvisioning,
176
184
  unregisterHandler,
177
185
  upsertCustomType,
@@ -18,8 +18,8 @@ const KINDS: ProviderConnectionKind[] = ['environment', 'runner-pool']
18
18
  // additionally carry a deployment's programmatically-registered CUSTOM kinds.
19
19
  const BUILTIN_BACKEND_KINDS: Record<ProviderConnectionKind, BackendKindOption[]> = {
20
20
  environment: [
21
- { kind: 'manifest', label: 'HTTP manifest' },
22
- { kind: 'kubernetes', label: 'Kubernetes' },
21
+ { kind: 'manifest', label: 'HTTP manifest', engines: ['remote-custom'] },
22
+ { kind: 'kubernetes', label: 'Kubernetes', engines: ['local-k3s', 'remote-kubernetes'] },
23
23
  ],
24
24
  'runner-pool': [
25
25
  { kind: 'manifest', label: 'HTTP manifest pool' },
@@ -111,6 +111,23 @@ export const useProviderConnectionsStore = defineStore('providerConnections', ()
111
111
  }
112
112
  }
113
113
 
114
+ /**
115
+ * Fetch (without mutating shared state) the descriptor for a specific backend kind — used by
116
+ * the per-type infra configurator to prefill a custom backend's manifest template/secret
117
+ * fields when the operator picks it. Returns null on a transient describe failure.
118
+ */
119
+ async function fetchDescriptor(
120
+ kind: ProviderConnectionKind,
121
+ backendKind?: string,
122
+ ): Promise<ProviderDescriptor | null> {
123
+ const ws = useWorkspaceStore()
124
+ try {
125
+ return await api.describeProvider(ws.requireId(), kind, backendKind)
126
+ } catch {
127
+ return null
128
+ }
129
+ }
130
+
114
131
  /** Refresh both providers (used by the banner + after a save/remove). */
115
132
  async function load() {
116
133
  await Promise.all(KINDS.map((k) => loadKind(k)))
@@ -177,6 +194,7 @@ export const useProviderConnectionsStore = defineStore('providerConnections', ()
177
194
  load,
178
195
  loadKind,
179
196
  loadDescriptor,
197
+ fetchDescriptor,
180
198
  ensureLoaded,
181
199
  descriptorFor,
182
200
  connectionFor,
@@ -1344,6 +1344,7 @@
1344
1344
  "remote-kubernetes": "Remote Kubernetes"
1345
1345
  },
1346
1346
  "kubernetesEngine": {
1347
+ "localK3sHint": "Prefilled for a local k3s/k3d/kind cluster on this machine. Bind a ServiceAccount to a role, mint its token with `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+), and paste it below. Then choose how the environment URL is derived, and edit the API server URL if your cluster listens on a different port.",
1347
1348
  "label": "Connection label",
1348
1349
  "labelPlaceholder": "Preview cluster",
1349
1350
  "apiServerUrl": "API server URL",
@@ -1400,6 +1401,8 @@
1400
1401
  "engineLabel": "Engine",
1401
1402
  "customHandlerTitle": "Remote-custom handler",
1402
1403
  "customTypeLabel": "Custom type",
1404
+ "customBackendLabel": "Backend",
1405
+ "customBackendHelp": "Which registered backend provisions this custom environment.",
1403
1406
  "customConnected": "Handler connected.",
1404
1407
  "saved": "Handler saved",
1405
1408
  "removed": "Handler removed",
@@ -1753,6 +1753,7 @@
1753
1753
  "remote-kubernetes": "Kubernetes remoto"
1754
1754
  },
1755
1755
  "kubernetesEngine": {
1756
+ "localK3sHint": "Precargado para un clúster local k3s/k3d/kind en esta máquina. Vincula una ServiceAccount a un rol, genera su token con `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) y pégalo abajo. Luego elige cómo se deriva la URL del entorno y edita la URL del API server si tu clúster escucha en otro puerto.",
1756
1757
  "label": "Etiqueta de la conexión",
1757
1758
  "labelPlaceholder": "Clúster de vista previa",
1758
1759
  "apiServerUrl": "URL del API server",
@@ -1809,6 +1810,8 @@
1809
1810
  "engineLabel": "Motor",
1810
1811
  "customHandlerTitle": "Gestor remote-custom",
1811
1812
  "customTypeLabel": "Tipo personalizado",
1813
+ "customBackendLabel": "Backend",
1814
+ "customBackendHelp": "Qué backend registrado aprovisiona este entorno personalizado.",
1812
1815
  "customConnected": "Gestor conectado.",
1813
1816
  "saved": "Gestor guardado",
1814
1817
  "removed": "Gestor eliminado",
@@ -1753,6 +1753,7 @@
1753
1753
  "remote-kubernetes": "Kubernetes distant"
1754
1754
  },
1755
1755
  "kubernetesEngine": {
1756
+ "localK3sHint": "Prérempli pour un cluster local k3s/k3d/kind sur cette machine. Liez un ServiceAccount à un rôle, générez son token avec `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) et collez-le ci-dessous. Choisissez ensuite comment l'URL de l'environnement est dérivée, et modifiez l'URL de l'API server si votre cluster écoute sur un autre port.",
1756
1757
  "label": "Libellé de la connexion",
1757
1758
  "labelPlaceholder": "Cluster de prévisualisation",
1758
1759
  "apiServerUrl": "URL de l'API server",
@@ -1809,6 +1810,8 @@
1809
1810
  "engineLabel": "Moteur",
1810
1811
  "customHandlerTitle": "Gestionnaire remote-custom",
1811
1812
  "customTypeLabel": "Type personnalisé",
1813
+ "customBackendLabel": "Backend",
1814
+ "customBackendHelp": "Quel backend enregistré provisionne cet environnement personnalisé.",
1812
1815
  "customConnected": "Gestionnaire connecté.",
1813
1816
  "saved": "Gestionnaire enregistré",
1814
1817
  "removed": "Gestionnaire supprimé",
@@ -1302,6 +1302,7 @@
1302
1302
  "remote-kubernetes": "Kubernetes מרוחק"
1303
1303
  },
1304
1304
  "kubernetesEngine": {
1305
+ "localK3sHint": "מולא מראש עבור אשכול k3s/k3d/kind מקומי במחשב הזה. קשרו ServiceAccount לתפקיד, הנפיקו עבורו token באמצעות `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) והדביקו אותו למטה. לאחר מכן בחרו כיצד נגזרת כתובת ה-URL של הסביבה, וערכו את כתובת ה-API server אם האשכול שלכם מאזין ביציאה אחרת.",
1305
1306
  "label": "תווית החיבור",
1306
1307
  "labelPlaceholder": "אשכול תצוגה מקדימה",
1307
1308
  "apiServerUrl": "כתובת ה-API server",
@@ -1358,6 +1359,8 @@
1358
1359
  "engineLabel": "מנוע",
1359
1360
  "customHandlerTitle": "מטפל remote-custom",
1360
1361
  "customTypeLabel": "סוג מותאם",
1362
+ "customBackendLabel": "Backend",
1363
+ "customBackendHelp": "איזה backend רשום מקצה את הסביבה המותאמת אישית הזו.",
1361
1364
  "customConnected": "המטפל מחובר.",
1362
1365
  "saved": "המטפל נשמר",
1363
1366
  "removed": "המטפל הוסר",
@@ -1304,6 +1304,7 @@
1304
1304
  "remote-kubernetes": "リモート Kubernetes"
1305
1305
  },
1306
1306
  "kubernetesEngine": {
1307
+ "localK3sHint": "このマシン上のローカル k3s/k3d/kind クラスター向けにあらかじめ入力されています。ServiceAccount をロールにバインドし、`kubectl create token NAME -n NAMESPACE`(Kubernetes 1.24 以降)でトークンを発行して下記に貼り付けてください。その後、環境 URL の導出方法を選択し、クラスターが別のポートで待ち受けている場合は API サーバー URL を編集してください。",
1307
1308
  "label": "接続ラベル",
1308
1309
  "labelPlaceholder": "プレビュークラスター",
1309
1310
  "apiServerUrl": "API サーバー URL",
@@ -1360,6 +1361,8 @@
1360
1361
  "engineLabel": "エンジン",
1361
1362
  "customHandlerTitle": "remote-custom ハンドラー",
1362
1363
  "customTypeLabel": "カスタムタイプ",
1364
+ "customBackendLabel": "バックエンド",
1365
+ "customBackendHelp": "このカスタム環境をプロビジョニングする登録済みバックエンド。",
1363
1366
  "customConnected": "ハンドラーが接続されました。",
1364
1367
  "saved": "ハンドラーを保存しました",
1365
1368
  "removed": "ハンドラーを削除しました",
@@ -1753,6 +1753,7 @@
1753
1753
  "remote-kubernetes": "Zdalny Kubernetes"
1754
1754
  },
1755
1755
  "kubernetesEngine": {
1756
+ "localK3sHint": "Wstępnie wypełnione dla lokalnego klastra k3s/k3d/kind na tym komputerze. Powiąż ServiceAccount z rolą, wygeneruj jego token poleceniem `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) i wklej go poniżej. Następnie wybierz sposób ustalania adresu URL środowiska i zmień URL serwera API, jeśli Twój klaster nasłuchuje na innym porcie.",
1756
1757
  "label": "Etykieta połączenia",
1757
1758
  "labelPlaceholder": "Klaster podglądu",
1758
1759
  "apiServerUrl": "URL serwera API",
@@ -1809,6 +1810,8 @@
1809
1810
  "engineLabel": "Silnik",
1810
1811
  "customHandlerTitle": "Handler remote-custom",
1811
1812
  "customTypeLabel": "Typ niestandardowy",
1813
+ "customBackendLabel": "Backend",
1814
+ "customBackendHelp": "Który zarejestrowany backend obsługuje to niestandardowe środowisko.",
1812
1815
  "customConnected": "Handler połączony.",
1813
1816
  "saved": "Handler zapisany",
1814
1817
  "removed": "Handler usunięty",
@@ -1304,6 +1304,7 @@
1304
1304
  "remote-kubernetes": "Uzak Kubernetes"
1305
1305
  },
1306
1306
  "kubernetesEngine": {
1307
+ "localK3sHint": "Bu makinedeki yerel bir k3s/k3d/kind kümesi için önceden dolduruldu. Bir ServiceAccount'u bir role bağlayın, `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) ile token'ını oluşturun ve aşağıya yapıştırın. Ardından ortam URL'sinin nasıl türetileceğini seçin ve kümeniz farklı bir bağlantı noktasını dinliyorsa API sunucu URL'sini düzenleyin.",
1307
1308
  "label": "Bağlantı etiketi",
1308
1309
  "labelPlaceholder": "Önizleme kümesi",
1309
1310
  "apiServerUrl": "API sunucu URL'si",
@@ -1360,6 +1361,8 @@
1360
1361
  "engineLabel": "Motor",
1361
1362
  "customHandlerTitle": "remote-custom işleyici",
1362
1363
  "customTypeLabel": "Özel tür",
1364
+ "customBackendLabel": "Arka uç",
1365
+ "customBackendHelp": "Bu özel ortamı hangi kayıtlı arka ucun sağlayacağı.",
1363
1366
  "customConnected": "İşleyici bağlandı.",
1364
1367
  "saved": "İşleyici kaydedildi",
1365
1368
  "removed": "İşleyici kaldırıldı",
@@ -1753,6 +1753,7 @@
1753
1753
  "remote-kubernetes": "Віддалений Kubernetes"
1754
1754
  },
1755
1755
  "kubernetesEngine": {
1756
+ "localK3sHint": "Попередньо заповнено для локального кластера k3s/k3d/kind на цьому комп'ютері. Прив'яжіть ServiceAccount до ролі, згенеруйте його токен командою `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) і вставте його нижче. Потім виберіть, як визначається URL середовища, і змініть URL сервера API, якщо ваш кластер слухає на іншому порту.",
1756
1757
  "label": "Мітка з'єднання",
1757
1758
  "labelPlaceholder": "Кластер попереднього перегляду",
1758
1759
  "apiServerUrl": "URL сервера API",
@@ -1809,6 +1810,8 @@
1809
1810
  "engineLabel": "Рушій",
1810
1811
  "customHandlerTitle": "Обробник remote-custom",
1811
1812
  "customTypeLabel": "Власний тип",
1813
+ "customBackendLabel": "Бекенд",
1814
+ "customBackendHelp": "Який зареєстрований бекенд забезпечує це користувацьке середовище.",
1812
1815
  "customConnected": "Обробник підключено.",
1813
1816
  "saved": "Обробник збережено",
1814
1817
  "removed": "Обробник видалено",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.63.0",
3
+ "version": "0.63.1",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "valibot": "^1.4.2",
35
35
  "vue": "^3.5.39",
36
36
  "wretch": "^3.0.9",
37
- "@cat-factory/contracts": "0.70.0"
37
+ "@cat-factory/contracts": "0.70.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",