@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.
- package/app/components/auth/LoginScreen.vue +26 -0
- package/app/components/settings/InfraHandlersConfigurator.vue +14 -0
- package/app/components/settings/KubernetesEngineForm.vue +66 -0
- package/app/composables/api/auth.ts +6 -0
- package/app/pages/index.vue +6 -1
- package/app/stores/auth.ts +59 -1
- package/app/stores/ui.ts +60 -0
- package/i18n/locales/en.json +10 -0
- package/i18n/locales/es.json +10 -0
- package/i18n/locales/fr.json +10 -0
- package/i18n/locales/he.json +10 -0
- package/i18n/locales/ja.json +10 -0
- package/i18n/locales/pl.json +10 -0
- package/i18n/locales/tr.json +10 -0
- package/i18n/locales/uk.json +10 -0
- package/package.json +2 -2
|
@@ -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
|
package/app/pages/index.vue
CHANGED
|
@@ -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(() =>
|
|
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.
|
package/app/stores/auth.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/i18n/locales/en.json
CHANGED
|
@@ -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",
|
package/i18n/locales/es.json
CHANGED
|
@@ -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",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -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",
|
package/i18n/locales/he.json
CHANGED
|
@@ -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",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -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",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -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",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -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",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -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.
|
|
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.
|
|
37
|
+
"@cat-factory/contracts": "0.72.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@toad-contracts/testing": "0.3.2",
|