@cat-factory/app 0.64.0 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,9 @@ const configuredProviders = computed<PatProvider[]>(
32
32
  )
33
33
  const isLocalMode = computed(() => auth.localMode?.enabled === true)
34
34
  const hasConfiguredPat = computed(() => configuredProviders.value.length > 0)
35
+ // Mothership mode: identity + org data live on a hosted mothership, so the primary sign-in is a
36
+ // round-trip to the mothership's OAuth (the node then exchanges the session for a machine token).
37
+ const isMothership = computed(() => auth.localMode?.mothership === true)
35
38
 
36
39
  const patBusy = ref(false)
37
40
  const patError = ref<string | null>(null)
@@ -186,6 +189,29 @@ const noSignInMethod = computed(
186
189
  </p>
187
190
  </div>
188
191
 
192
+ <!-- Mothership mode: sign in through the hosted mothership (it owns identity + the
193
+ allowlist). The node exchanges the returned session for a machine token. -->
194
+ <div v-if="isMothership && mode !== 'forgot'" class="mb-4 space-y-2">
195
+ <UButton
196
+ block
197
+ size="lg"
198
+ color="primary"
199
+ icon="i-lucide-cloud"
200
+ data-testid="mothership-signin"
201
+ @click="auth.signInViaMothership()"
202
+ >
203
+ {{ t('auth.mothership.signIn') }}
204
+ </UButton>
205
+ <p class="px-1 text-xs text-slate-400">{{ t('auth.mothership.hint') }}</p>
206
+ <p
207
+ v-if="auth.mothershipError"
208
+ class="px-1 text-xs text-rose-400"
209
+ data-testid="mothership-error"
210
+ >
211
+ {{ t('auth.mothership.error') }}
212
+ </p>
213
+ </div>
214
+
189
215
  <!-- Local mode: sign in with the env-configured source-control PAT. The token lives
190
216
  server-side (GITHUB_PAT / GITLAB_PAT); we only pick a provider here. -->
191
217
  <div v-if="isLocalMode && mode !== 'forgot'" class="space-y-3">
@@ -29,6 +29,7 @@ import CustomManifestTypeEditor from '~/components/settings/CustomManifestTypeEd
29
29
  const { t } = useI18n()
30
30
  const infra = useInfraConfigStore()
31
31
  const auth = useAuthStore()
32
+ const ui = useUiStore()
32
33
  const toast = useToast()
33
34
 
34
35
  const isLocal = computed(() => auth.localMode?.enabled === true)
@@ -77,6 +78,18 @@ watch(
77
78
  { immediate: true },
78
79
  )
79
80
 
81
+ // A `cat-factory k3s` CLI deep-link (captured by the ui store on app load) always targets the
82
+ // `local-k3s` engine — select it so the workspace form below seeds from the prefill, provided the
83
+ // mode offers it (it's local-mode only). Runs after the handler/engine watcher above so the CLI
84
+ // hand-off wins over the saved-handler default.
85
+ watch(
86
+ () => ui.k3sSetupPrefill,
87
+ (prefill) => {
88
+ if (prefill && kubeEngines.value.includes('local-k3s')) selectedKubeEngine.value = 'local-k3s'
89
+ },
90
+ { immediate: true },
91
+ )
92
+
80
93
  const busy = ref(false)
81
94
 
82
95
  // Connection-probe state for the kube engine forms (workspace + per-user override kept
@@ -384,6 +397,7 @@ function notifyError(e: unknown) {
384
397
  :testing="kubeTesting"
385
398
  :busy="busy"
386
399
  :test-result="kubeTestResult"
400
+ :prefill="ui.k3sSetupPrefill"
387
401
  @test="testKube"
388
402
  @save="saveKube"
389
403
  />
@@ -13,6 +13,7 @@ import type {
13
13
  InfraEngine,
14
14
  InfraHandlerConfig,
15
15
  } from '@cat-factory/contracts'
16
+ import type { K3sSetupPrefill } from '~/stores/ui'
16
17
 
17
18
  // The kube branch of the discriminated handler config this form produces (the `local-k3s` /
18
19
  // `remote-kubernetes` engines share `kubernetesEngineConfigSchema`). Emitting this typed
@@ -30,6 +31,11 @@ const props = defineProps<{
30
31
  testing: boolean
31
32
  busy: boolean
32
33
  testResult: { ok: boolean; message?: string } | null
34
+ /**
35
+ * Non-secret values from a `cat-factory k3s` CLI deep-link, seeded into a FRESH `local-k3s`
36
+ * form so the user only pastes the token + saves. Ignored when editing a saved handler.
37
+ */
38
+ prefill?: K3sSetupPrefill | null
33
39
  }>()
34
40
 
35
41
  const emit = defineEmits<{
@@ -144,6 +150,27 @@ watch(
144
150
  { immediate: true },
145
151
  )
146
152
 
153
+ // Seed a FRESH `local-k3s` form from a `cat-factory k3s` CLI deep-link (see the ui store's
154
+ // `consumeK3sSetupDeepLink`). Applied AFTER the engine-default seed above so the CLI's concrete
155
+ // values win, but never over a saved handler (an edit is authoritative) and only for the engine
156
+ // the link targets. Non-empty fields only, so a partial link falls back to the loopback defaults.
157
+ watch(
158
+ () => props.prefill,
159
+ (prefill) => {
160
+ if (!prefill || props.handler || props.engine !== 'local-k3s') return
161
+ if (prefill.label.trim()) form.label = prefill.label.trim()
162
+ if (prefill.apiServerUrl.trim()) form.apiServerUrl = prefill.apiServerUrl.trim()
163
+ if (prefill.insecureSkipTlsVerify !== undefined)
164
+ form.insecureSkipTlsVerify = prefill.insecureSkipTlsVerify
165
+ if (prefill.namespaceTemplate.trim()) form.namespaceTemplate = prefill.namespaceTemplate.trim()
166
+ if (prefill.hostTemplate.trim()) {
167
+ form.urlSource = 'ingressTemplate'
168
+ form.hostTemplate = prefill.hostTemplate.trim()
169
+ }
170
+ },
171
+ { immediate: true },
172
+ )
173
+
147
174
  const servicePortValid = computed(() => {
148
175
  const raw = form.servicePort.trim()
149
176
  if (!raw) return true
@@ -202,6 +229,14 @@ function buildPayload(): KubeHandlerPayload {
202
229
  function optional(label: string): string {
203
230
  return t('settings.providerConnection.form.optionalLabel', { label })
204
231
  }
232
+
233
+ // The guided-setup CLI command shown in the local-k3s "Auto-setup" affordance. A literal command
234
+ // example (not prose), so it stays inline rather than in the i18n catalog — mirroring the format
235
+ // examples the i18n rules keep out of message bodies.
236
+ const AUTO_SETUP_COMMAND = 'cat-factory k3s'
237
+ async function copyAutoSetupCommand() {
238
+ await navigator.clipboard?.writeText(AUTO_SETUP_COMMAND)
239
+ }
205
240
  </script>
206
241
 
207
242
  <template>
@@ -221,6 +256,37 @@ function optional(label: string): string {
221
256
  {{ t('settings.infrastructure.kubernetesEngine.localK3sHint') }}
222
257
  </p>
223
258
 
259
+ <!-- Auto-setup: point the user at the `cat-factory k3s` CLI, which probes/provisions a local
260
+ cluster, mints the ServiceAccount token, and deep-links back here to pre-fill this form
261
+ (the token is pasted, never in the link). -->
262
+ <div
263
+ v-if="engine === 'local-k3s'"
264
+ class="rounded-md border border-slate-700 bg-slate-900/40 p-2 space-y-1.5"
265
+ >
266
+ <p class="flex items-center gap-1.5 text-[11px] font-semibold text-slate-300">
267
+ <UIcon name="i-lucide-wand-2" class="h-3.5 w-3.5 text-slate-400" />
268
+ {{ t('settings.infrastructure.kubernetesEngine.autoSetup.title') }}
269
+ </p>
270
+ <p class="text-[11px] text-slate-400">
271
+ {{ t('settings.infrastructure.kubernetesEngine.autoSetup.description') }}
272
+ </p>
273
+ <div class="flex items-center gap-1.5">
274
+ <code
275
+ class="flex-1 rounded bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200 select-all"
276
+ >
277
+ {{ AUTO_SETUP_COMMAND }}
278
+ </code>
279
+ <UButton
280
+ icon="i-lucide-copy"
281
+ color="neutral"
282
+ variant="ghost"
283
+ size="xs"
284
+ :aria-label="t('common.copy')"
285
+ @click="copyAutoSetupCommand"
286
+ />
287
+ </div>
288
+ </div>
289
+
224
290
  <UFormField :label="t('settings.infrastructure.kubernetesEngine.label')">
225
291
  <UInput
226
292
  v-model="form.label"
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  acceptInvitationContract,
3
3
  authConfigContract,
4
+ connectMothershipContract,
4
5
  forgotPasswordContract,
5
6
  logoutContract,
6
7
  meContract,
@@ -51,6 +52,11 @@ export function authApi({ http, send, ws }: ApiContext) {
51
52
 
52
53
  logout: () => send(logoutContract, { pathPrefix: '/auth' }),
53
54
 
55
+ // Mothership mode (local facade): hand the local node a mothership SESSION token (captured
56
+ // from the mothership OAuth redirect fragment). The node exchanges it for a cached machine
57
+ // token and returns a LOCAL session for the same user. Mounted at the app root (no prefix).
58
+ connectMothership: (session: string) => send(connectMothershipContract, { body: { session } }),
59
+
54
60
  // Mint a short-lived, workspace-scoped ticket for the events WebSocket. A
55
61
  // browser can't set Authorization on a WS handshake, so the socket auths from
56
62
  // this `?ticket=` instead of the long-lived session token. Empty string when
@@ -114,7 +114,12 @@ const ui = useUiStore()
114
114
  const aiReadiness = useAiReadiness()
115
115
 
116
116
  // Load the board from the backend before rendering it.
117
- onMounted(() => workspace.init())
117
+ onMounted(() => {
118
+ void workspace.init()
119
+ // Honour a `cat-factory k3s` CLI hand-off (`?infraSetup=local-k3s&…`): open the Infrastructure
120
+ // window pre-seeded with the provisioned connection so the user only pastes the token + saves.
121
+ ui.consumeK3sSetupDeepLink()
122
+ })
118
123
 
119
124
  // Per-session guards so each AI-onboarding dialog auto-opens at most once (later opens are
120
125
  // user-driven from the banner). Reset on workspace switch by the catalog watcher below.
@@ -60,6 +60,12 @@ export const useAuthStore = defineStore(
60
60
  const autoLoginProvider = ref<'github' | 'gitlab' | null>(null)
61
61
  /** True once the initial auth handshake has settled. */
62
62
  const ready = ref(false)
63
+ /**
64
+ * Mothership mode: the last mothership sign-in failure (node unreachable / rejected session),
65
+ * or null. Set when the post-OAuth connect exchange fails, so the login screen can tell the
66
+ * user the click didn't take instead of silently returning them to the sign-in button.
67
+ */
68
+ const mothershipError = ref<string | null>(null)
63
69
  /**
64
70
  * True only once `getAuthConfig()` has resolved successfully. Distinguishes "the backend
65
71
  * told us auth is off" from "we never reached the backend" (the bootstrap catch path),
@@ -109,9 +115,59 @@ export const useAuthStore = defineStore(
109
115
  history.replaceState(null, '', window.location.pathname + window.location.search)
110
116
  }
111
117
 
118
+ /**
119
+ * Mothership mode: when the mothership OAuth redirect returns here (flagged
120
+ * `?mothership_connect=1`), the URL fragment carries a MOTHERSHIP session — not a local one.
121
+ * Hand it to our OWN node, which exchanges it for a cached machine token and returns a LOCAL
122
+ * session for the same user. Returns true when it handled the redirect (so the caller skips
123
+ * the normal `consumeRedirectToken`, which would wrongly store the mothership session locally).
124
+ */
125
+ async function maybeConnectMothership(): Promise<boolean> {
126
+ if (typeof window === 'undefined') return false
127
+ const params = new URLSearchParams(window.location.search)
128
+ if (params.get('mothership_connect') !== '1') return false
129
+ const match = /(?:^#|[#&])token=([^&]+)/.exec(window.location.hash)
130
+ const session = match ? decodeURIComponent(match[1]!) : null
131
+ // Clean the flag + fragment from the URL regardless of outcome, so it isn't left in history.
132
+ params.delete('mothership_connect')
133
+ const qs = params.toString()
134
+ history.replaceState(null, '', window.location.pathname + (qs ? `?${qs}` : ''))
135
+ if (!session) return true
136
+ try {
137
+ const result = await api.connectMothership(session)
138
+ applySession({ token: result.session, user: result.user })
139
+ mothershipError.value = null
140
+ } catch (err) {
141
+ // Surface the failure so the login screen shows it, rather than silently dropping the
142
+ // user back on the sign-in button as if the click did nothing. The captured session is
143
+ // already stripped from the URL, so recovery is a fresh "Sign in via mothership".
144
+ mothershipError.value =
145
+ err instanceof Error ? err.message : 'Could not connect to the mothership'
146
+ }
147
+ return true
148
+ }
149
+
150
+ /**
151
+ * Mothership mode: sign in through the hosted mothership. The mothership owns identity + the
152
+ * allowlist, so we send the browser to ITS OAuth and return here flagged for the connect
153
+ * exchange (`maybeConnectMothership`). No-op if the mothership URL isn't known.
154
+ */
155
+ function signInViaMothership() {
156
+ if (typeof window === 'undefined') return
157
+ const base = localMode.value?.mothershipUrl
158
+ if (!base) return
159
+ mothershipError.value = null
160
+ const here = new URL(window.location.origin + window.location.pathname)
161
+ here.searchParams.set('mothership_connect', '1')
162
+ const redirect = new URLSearchParams({ redirect: here.toString() })
163
+ window.location.href = `${base.replace(/\/$/, '')}/auth/login?${redirect}`
164
+ }
165
+
112
166
  /** Resolve auth state: capture any redirect token, then check the backend. */
113
167
  async function bootstrap() {
114
- consumeRedirectToken()
168
+ // A returning mothership-connect redirect is handled first (it carries a mothership session,
169
+ // which must be exchanged — not stored as a local token by `consumeRedirectToken`).
170
+ if (!(await maybeConnectMothership())) consumeRedirectToken()
115
171
  try {
116
172
  const config = await api.getAuthConfig()
117
173
  required.value = config.enabled
@@ -277,6 +333,7 @@ export const useAuthStore = defineStore(
277
333
  infrastructure,
278
334
  autoLoginProvider,
279
335
  ready,
336
+ mothershipError,
280
337
  configLoaded,
281
338
  isLocalFacade,
282
339
  isAuthenticated,
@@ -284,6 +341,7 @@ export const useAuthStore = defineStore(
284
341
  bootstrap,
285
342
  login,
286
343
  loginWithGoogle,
344
+ signInViaMothership,
287
345
  signup,
288
346
  passwordLogin,
289
347
  patLogin,
package/app/stores/ui.ts CHANGED
@@ -14,6 +14,21 @@ export interface AddTaskPrefill {
14
14
  context?: PendingContext[]
15
15
  }
16
16
 
17
+ /**
18
+ * Non-secret `local-k3s` connection values captured from the `cat-factory k3s` CLI deep-link
19
+ * (`?infraSetup=local-k3s&…`). Mirrors the params `buildK3sSetupUrl` emits (the CLI-side
20
+ * `k3s-handler.ts`); the ServiceAccount token is intentionally absent — the user pastes it.
21
+ */
22
+ export interface K3sSetupPrefill {
23
+ label: string
24
+ apiServerUrl: string
25
+ namespaceTemplate: string
26
+ hostTemplate: string
27
+ // Absent when the link omitted the param, so the form keeps its engine default rather than
28
+ // forcing verification back on (which would break a self-signed local cluster).
29
+ insecureSkipTlsVerify?: boolean
30
+ }
31
+
17
32
  /** Transient UI state: selection, panels, zoom level. */
18
33
  export const useUiStore = defineStore('ui', () => {
19
34
  const selectedBlockId = ref<string | null>(null)
@@ -143,6 +158,11 @@ export const useUiStore = defineStore('ui', () => {
143
158
  // `openProviderConnection(kind)` remains for deep-links (a banner's "Configure…" button).
144
159
  const infrastructureOpen = ref(false)
145
160
  const infrastructureTab = ref<'environment' | 'runner-pool'>('runner-pool')
161
+ // Non-secret prefill captured from the `cat-factory k3s` CLI deep-link (see
162
+ // `consumeK3sSetupDeepLink`). When set, the Test-environments tab's kube engine form seeds the
163
+ // `local-k3s` connection from it; the ServiceAccount token is deliberately NOT in the link (a
164
+ // secret in a URL leaks into history/logs), so the user still pastes it before Test → Save.
165
+ const k3sSetupPrefill = ref<K3sSetupPrefill | null>(null)
146
166
  const modelConfigOpen = ref(false)
147
167
  // LLM-vendor subscription credentials (the token pool powering the Claude Code
148
168
  // / Codex harnesses). `vendorCredentialsTab` lets a caller deep-link to one tab —
@@ -530,6 +550,44 @@ export const useUiStore = defineStore('ui', () => {
530
550
  }
531
551
  function closeProviderConnection() {
532
552
  infrastructureOpen.value = false
553
+ // Drop any consumed CLI prefill so re-opening the window normally doesn't re-seed the form.
554
+ k3sSetupPrefill.value = null
555
+ }
556
+ // Capture a `cat-factory k3s` deep-link (`?infraSetup=local-k3s&…`) on app load: stash the
557
+ // non-secret connection values, open the Infrastructure window on the Test-environments tab so
558
+ // the kube engine form seeds from them, then strip the params from the URL (mirrors the
559
+ // `?invite=` handling in the auth store) so a reload doesn't re-trigger and the link isn't left
560
+ // in history. No-op when the query param is absent.
561
+ function consumeK3sSetupDeepLink() {
562
+ if (typeof window === 'undefined') return
563
+ const params = new URLSearchParams(window.location.search)
564
+ if (params.get('infraSetup') !== 'local-k3s') return
565
+ k3sSetupPrefill.value = {
566
+ label: params.get('label') ?? 'Local k3s',
567
+ apiServerUrl: params.get('apiServerUrl') ?? '',
568
+ namespaceTemplate: params.get('namespaceTemplate') ?? '',
569
+ hostTemplate: params.get('hostTemplate') ?? '',
570
+ // Only carry the flag the link actually set — a missing param leaves the form's engine
571
+ // default (skip-TLS on for a local self-signed cluster) untouched.
572
+ insecureSkipTlsVerify: params.has('insecureSkipTlsVerify')
573
+ ? params.get('insecureSkipTlsVerify') === '1'
574
+ : undefined,
575
+ }
576
+ resetHubReturn()
577
+ infrastructureTab.value = 'environment'
578
+ infrastructureOpen.value = true
579
+ for (const key of [
580
+ 'infraSetup',
581
+ 'label',
582
+ 'apiServerUrl',
583
+ 'namespaceTemplate',
584
+ 'hostTemplate',
585
+ 'insecureSkipTlsVerify',
586
+ ]) {
587
+ params.delete(key)
588
+ }
589
+ const qs = params.toString()
590
+ history.replaceState(null, '', window.location.pathname + (qs ? `?${qs}` : ''))
533
591
  }
534
592
  function openModelConfig() {
535
593
  modelConfigOpen.value = true
@@ -792,6 +850,8 @@ export const useUiStore = defineStore('ui', () => {
792
850
  closeObservabilityConnection,
793
851
  openProviderConnection,
794
852
  closeProviderConnection,
853
+ k3sSetupPrefill,
854
+ consumeK3sSetupDeepLink,
795
855
  openModelConfig,
796
856
  closeModelConfig,
797
857
  openVendorCredentials,
@@ -12,6 +12,7 @@
12
12
  "save": "Save",
13
13
  "cancel": "Cancel",
14
14
  "retry": "Retry",
15
+ "copy": "Copy",
15
16
  "block": "Block",
16
17
  "@block": {
17
18
  "description": "Generic fallback NOUN for a board item whose title is unknown (a service / module / task node). Not the verb 'to block'."
@@ -872,6 +873,11 @@
872
873
  "userMenu": {
873
874
  "mySetup": "My setup",
874
875
  "signOut": "Sign out"
876
+ },
877
+ "mothership": {
878
+ "signIn": "Sign in via mothership",
879
+ "hint": "Your projects and identity live on the hosted mothership. Sign in there to connect this node.",
880
+ "error": "Could not sign in via the mothership. Try again."
875
881
  }
876
882
  },
877
883
  "layout": {
@@ -1348,6 +1354,10 @@
1348
1354
  },
1349
1355
  "kubernetesEngine": {
1350
1356
  "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.",
1357
+ "autoSetup": {
1358
+ "title": "Auto-setup with the CLI",
1359
+ "description": "Run this in your terminal to probe or provision a local cluster, mint a ServiceAccount token, and open this form pre-filled. Paste the token it prints, then Test and Save."
1360
+ },
1351
1361
  "label": "Connection label",
1352
1362
  "labelPlaceholder": "Preview cluster",
1353
1363
  "apiServerUrl": "API server URL",
@@ -12,6 +12,7 @@
12
12
  "save": "Guardar",
13
13
  "cancel": "Cancelar",
14
14
  "retry": "Reintentar",
15
+ "copy": "Copiar",
15
16
  "actionFailed": "La acción falló",
16
17
  "close": "Cerrar",
17
18
  "block": "Bloque"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "Mi configuración",
834
835
  "signOut": "Cerrar sesión"
836
+ },
837
+ "mothership": {
838
+ "signIn": "Iniciar sesión con la central",
839
+ "hint": "Tus proyectos e identidad están en la central alojada: inicia sesión allí para conectar este nodo.",
840
+ "error": "No se pudo iniciar sesión con la central. Inténtalo de nuevo."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1757,6 +1763,10 @@
1757
1763
  },
1758
1764
  "kubernetesEngine": {
1759
1765
  "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.",
1766
+ "autoSetup": {
1767
+ "title": "Configuración automática con la CLI",
1768
+ "description": "Ejecútalo en tu terminal para detectar o aprovisionar un clúster local, generar un token de ServiceAccount y abrir este formulario ya rellenado. Pega el token que muestra y luego pulsa Probar y Guardar."
1769
+ },
1760
1770
  "label": "Etiqueta de la conexión",
1761
1771
  "labelPlaceholder": "Clúster de vista previa",
1762
1772
  "apiServerUrl": "URL del API server",
@@ -12,6 +12,7 @@
12
12
  "save": "Enregistrer",
13
13
  "cancel": "Annuler",
14
14
  "retry": "Réessayer",
15
+ "copy": "Copier",
15
16
  "actionFailed": "Échec de l’action",
16
17
  "close": "Fermer",
17
18
  "block": "Bloc"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "Ma configuration",
834
835
  "signOut": "Se déconnecter"
836
+ },
837
+ "mothership": {
838
+ "signIn": "Se connecter via le serveur central",
839
+ "hint": "Vos projets et votre identité sont sur le serveur central hébergé. Connectez-vous là-bas pour relier ce nœud.",
840
+ "error": "Impossible de se connecter via le serveur central. Réessayez."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1757,6 +1763,10 @@
1757
1763
  },
1758
1764
  "kubernetesEngine": {
1759
1765
  "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.",
1766
+ "autoSetup": {
1767
+ "title": "Configuration automatique avec la CLI",
1768
+ "description": "Exécutez-le dans votre terminal pour détecter ou provisionner un cluster local, générer un jeton de ServiceAccount et ouvrir ce formulaire prérempli. Collez le jeton affiché, puis cliquez sur Tester et Enregistrer."
1769
+ },
1760
1770
  "label": "Libellé de la connexion",
1761
1771
  "labelPlaceholder": "Cluster de prévisualisation",
1762
1772
  "apiServerUrl": "URL de l'API server",
@@ -12,6 +12,7 @@
12
12
  "save": "שמור",
13
13
  "cancel": "ביטול",
14
14
  "retry": "נסה שוב",
15
+ "copy": "העתק",
15
16
  "block": "בלוק",
16
17
  "actionFailed": "הפעולה נכשלה",
17
18
  "close": "סגור"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "ההגדרות שלי",
834
835
  "signOut": "התנתק"
836
+ },
837
+ "mothership": {
838
+ "signIn": "התחברות דרך שרת המרכז",
839
+ "hint": "הפרויקטים והזהות שלך נמצאים בשרת המרכז המתארח. התחבר שם כדי לחבר את הצומת הזה.",
840
+ "error": "לא ניתן להתחבר דרך שרת המרכז. נסה שוב."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1306,6 +1312,10 @@
1306
1312
  },
1307
1313
  "kubernetesEngine": {
1308
1314
  "localK3sHint": "מולא מראש עבור אשכול k3s/k3d/kind מקומי במחשב הזה. קשרו ServiceAccount לתפקיד, הנפיקו עבורו token באמצעות `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) והדביקו אותו למטה. לאחר מכן בחרו כיצד נגזרת כתובת ה-URL של הסביבה, וערכו את כתובת ה-API server אם האשכול שלכם מאזין ביציאה אחרת.",
1315
+ "autoSetup": {
1316
+ "title": "הגדרה אוטומטית באמצעות ה-CLI",
1317
+ "description": "הרץ זאת בטרמינל כדי לזהות או להקצות אשכול מקומי, ליצור אסימון ServiceAccount ולפתוח טופס זה כשהוא ממולא מראש. הדבק את האסימון המוצג, ולאחר מכן בצע בדיקה ושמירה."
1318
+ },
1309
1319
  "label": "תווית החיבור",
1310
1320
  "labelPlaceholder": "אשכול תצוגה מקדימה",
1311
1321
  "apiServerUrl": "כתובת ה-API server",
@@ -12,6 +12,7 @@
12
12
  "save": "保存",
13
13
  "cancel": "キャンセル",
14
14
  "retry": "再試行",
15
+ "copy": "コピー",
15
16
  "block": "ブロック",
16
17
  "actionFailed": "操作に失敗しました",
17
18
  "close": "閉じる"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "マイセットアップ",
834
835
  "signOut": "サインアウト"
836
+ },
837
+ "mothership": {
838
+ "signIn": "マザーシップ経由でサインイン",
839
+ "hint": "プロジェクトと認証情報はホスト型のマザーシップにあります。そこでサインインしてこのノードを接続してください。",
840
+ "error": "マザーシップ経由でサインインできませんでした。もう一度お試しください。"
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1308,6 +1314,10 @@
1308
1314
  },
1309
1315
  "kubernetesEngine": {
1310
1316
  "localK3sHint": "このマシン上のローカル k3s/k3d/kind クラスター向けにあらかじめ入力されています。ServiceAccount をロールにバインドし、`kubectl create token NAME -n NAMESPACE`(Kubernetes 1.24 以降)でトークンを発行して下記に貼り付けてください。その後、環境 URL の導出方法を選択し、クラスターが別のポートで待ち受けている場合は API サーバー URL を編集してください。",
1317
+ "autoSetup": {
1318
+ "title": "CLI による自動セットアップ",
1319
+ "description": "これをターミナルで実行すると、ローカルクラスターを検出またはプロビジョニングし、ServiceAccount トークンを生成して、このフォームを事前入力した状態で開きます。表示されたトークンを貼り付けてから、テストして保存してください。"
1320
+ },
1311
1321
  "label": "接続ラベル",
1312
1322
  "labelPlaceholder": "プレビュークラスター",
1313
1323
  "apiServerUrl": "API サーバー URL",
@@ -12,6 +12,7 @@
12
12
  "save": "Zapisz",
13
13
  "cancel": "Anuluj",
14
14
  "retry": "Ponów",
15
+ "copy": "Kopiuj",
15
16
  "actionFailed": "Akcja nie powiodła się",
16
17
  "close": "Zamknij",
17
18
  "block": "Blok"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "Moja konfiguracja",
834
835
  "signOut": "Wyloguj się"
836
+ },
837
+ "mothership": {
838
+ "signIn": "Zaloguj się przez serwer centralny",
839
+ "hint": "Twoje projekty i tożsamość znajdują się na hostowanym serwerze centralnym. Zaloguj się tam, aby połączyć ten węzeł.",
840
+ "error": "Nie udało się zalogować przez serwer centralny. Spróbuj ponownie."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1757,6 +1763,10 @@
1757
1763
  },
1758
1764
  "kubernetesEngine": {
1759
1765
  "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.",
1766
+ "autoSetup": {
1767
+ "title": "Automatyczna konfiguracja przez CLI",
1768
+ "description": "Uruchom to w terminalu, aby wykryć lub udostępnić lokalny klaster, wygenerować token ServiceAccount i otworzyć ten formularz wstępnie wypełniony. Wklej wyświetlony token, a następnie kliknij Przetestuj i Zapisz."
1769
+ },
1760
1770
  "label": "Etykieta połączenia",
1761
1771
  "labelPlaceholder": "Klaster podglądu",
1762
1772
  "apiServerUrl": "URL serwera API",
@@ -12,6 +12,7 @@
12
12
  "save": "Kaydet",
13
13
  "cancel": "İptal",
14
14
  "retry": "Yeniden dene",
15
+ "copy": "Kopyala",
15
16
  "block": "Blok",
16
17
  "actionFailed": "İşlem başarısız oldu",
17
18
  "close": "Kapat"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "Kurulumum",
834
835
  "signOut": "Oturumu kapat"
836
+ },
837
+ "mothership": {
838
+ "signIn": "Ana sunucu üzerinden oturum aç",
839
+ "hint": "Projeleriniz ve kimliğiniz barındırılan ana sunucuda tutulur. Bu düğümü bağlamak için orada oturum açın.",
840
+ "error": "Ana sunucu üzerinden oturum açılamadı. Tekrar deneyin."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1308,6 +1314,10 @@
1308
1314
  },
1309
1315
  "kubernetesEngine": {
1310
1316
  "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.",
1317
+ "autoSetup": {
1318
+ "title": "CLI ile otomatik kurulum",
1319
+ "description": "Yerel bir kümeyi algılamak veya sağlamak, bir ServiceAccount belirteci oluşturmak ve bu formu önceden doldurulmuş olarak açmak için bunu terminalinizde çalıştırın. Yazdırdığı belirteci yapıştırın, ardından Test edin ve Kaydedin."
1320
+ },
1311
1321
  "label": "Bağlantı etiketi",
1312
1322
  "labelPlaceholder": "Önizleme kümesi",
1313
1323
  "apiServerUrl": "API sunucu URL'si",
@@ -12,6 +12,7 @@
12
12
  "save": "Зберегти",
13
13
  "cancel": "Скасувати",
14
14
  "retry": "Повторити",
15
+ "copy": "Копіювати",
15
16
  "actionFailed": "Не вдалося виконати дію",
16
17
  "close": "Закрити",
17
18
  "block": "Блок"
@@ -832,6 +833,11 @@
832
833
  "userMenu": {
833
834
  "mySetup": "Моє налаштування",
834
835
  "signOut": "Вийти"
836
+ },
837
+ "mothership": {
838
+ "signIn": "Увійти через центральний сервер",
839
+ "hint": "Ваші проєкти та обліковий запис зберігаються на центральному сервері. Увійдіть там, щоб приєднати цей вузол.",
840
+ "error": "Не вдалося увійти через центральний сервер. Спробуйте ще раз."
835
841
  }
836
842
  },
837
843
  "layout": {
@@ -1757,6 +1763,10 @@
1757
1763
  },
1758
1764
  "kubernetesEngine": {
1759
1765
  "localK3sHint": "Попередньо заповнено для локального кластера k3s/k3d/kind на цьому комп'ютері. Прив'яжіть ServiceAccount до ролі, згенеруйте його токен командою `kubectl create token NAME -n NAMESPACE` (Kubernetes 1.24+) і вставте його нижче. Потім виберіть, як визначається URL середовища, і змініть URL сервера API, якщо ваш кластер слухає на іншому порту.",
1766
+ "autoSetup": {
1767
+ "title": "Автоматичне налаштування через CLI",
1768
+ "description": "Запустіть це в терміналі, щоб виявити або підготувати локальний кластер, згенерувати токен ServiceAccount і відкрити цю форму заздалегідь заповненою. Вставте показаний токен, потім натисніть «Перевірити» та «Зберегти»."
1769
+ },
1760
1770
  "label": "Мітка з'єднання",
1761
1771
  "labelPlaceholder": "Кластер попереднього перегляду",
1762
1772
  "apiServerUrl": "URL сервера API",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.64.0",
3
+ "version": "0.66.0",
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.71.0"
37
+ "@cat-factory/contracts": "0.72.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",