@cat-factory/app 0.51.0 → 0.52.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.
@@ -24,11 +24,15 @@ const auth = useAuthStore()
24
24
  const providerConnections = useProviderConnectionsStore()
25
25
  const ui = useUiStore()
26
26
 
27
- // The Infrastructure menu (agent-container execution + test environments) is relevant when
28
- // either provider integration is available OR we're running the local-mode facade (which
29
- // always has host-Docker execution + the warm-pool/checkout settings to configure).
27
+ // The Infrastructure menu (agent-container execution + test environments) shows whenever the
28
+ // deployment reports its infrastructure capability every facade populates `auth.infrastructure`
29
+ // (it drives the execution-backend selector), so there is always an execution + test-env backend
30
+ // to view, even on a Worker/Node deployment with no runner-pool/environment connection registered.
31
+ // The old provider-availability/local-mode signals stay as a defensive fallback for a backend that
32
+ // (somehow) omits the descriptor.
30
33
  const showInfrastructure = computed(
31
34
  () =>
35
+ auth.infrastructure != null ||
32
36
  auth.localMode?.enabled === true ||
33
37
  providerConnections.isAvailable('runner-pool') ||
34
38
  providerConnections.isAvailable('environment'),
@@ -1,22 +1,26 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref, watch } from 'vue'
2
+ import { useLocalStorage } from '@vueuse/core'
3
+ import { computed } from 'vue'
3
4
 
4
5
  // Shown whenever the active locale is NOT English: the non-English catalogs are
5
6
  // community/AI-provided and may be inaccurate, so warn the user and point them at the
6
7
  // repository to report mistakes or open a fix PR. Rendered as a slim full-width strip at
7
8
  // the very top (distinct from the centered config-warning cards below it, so they don't
8
- // overlap). Dismissible per session; re-appears the next time the user switches locale.
9
+ // overlap). Dismissal is persisted per-locale in localStorage: once dismissed for a locale
10
+ // it stays hidden across reloads, but switching to a different (separately-translated)
11
+ // locale shows it again, since that catalog is a fresh, independently-translated context.
9
12
  const REPO_URL = 'https://github.com/kibertoad/cat-factory'
10
13
 
11
14
  const { t, locale } = useI18n()
12
15
 
13
- const dismissed = ref(false)
14
- const show = computed(() => locale.value !== 'en' && !dismissed.value)
16
+ const dismissedLocales = useLocalStorage<string[]>('cat-factory:translation-warning-dismissed', [])
17
+ const show = computed(() => locale.value !== 'en' && !dismissedLocales.value.includes(locale.value))
15
18
 
16
- // A fresh switch is a new context for the warning, so un-dismiss on every locale change.
17
- watch(locale, () => {
18
- dismissed.value = false
19
- })
19
+ function dismiss() {
20
+ if (!dismissedLocales.value.includes(locale.value)) {
21
+ dismissedLocales.value = [...dismissedLocales.value, locale.value]
22
+ }
23
+ }
20
24
  </script>
21
25
 
22
26
  <template>
@@ -51,7 +55,7 @@ watch(locale, () => {
51
55
  size="xs"
52
56
  icon="i-lucide-x"
53
57
  :aria-label="t('language.warning.dismiss')"
54
- @click="dismissed = true"
58
+ @click="dismiss"
55
59
  />
56
60
  </div>
57
61
  </Transition>
@@ -29,9 +29,12 @@ const open = computed({
29
29
 
30
30
  const isLocal = computed(() => auth.localMode?.enabled === true)
31
31
 
32
- // Each concern gates on its own availability probe; an unavailable tab isn't offered.
33
- const agentsAvailable = computed(() => store.isAvailable('runner-pool'))
34
- const envsAvailable = computed(() => store.isAvailable('environment'))
32
+ // The tabs are driven by the deployment's infrastructure capability (every facade reports
33
+ // execution + test-env backends), NOT the optional provider-connection probes — the execution-
34
+ // backend selector must show even when no runner-pool / environment connection is registered.
35
+ // The connect form inside each tab still gates on its own probe (see the template).
36
+ const agentsAvailable = computed(() => (auth.infrastructure?.execution.available.length ?? 0) > 0)
37
+ const envsAvailable = computed(() => (auth.infrastructure?.testEnv.available.length ?? 0) > 0)
35
38
 
36
39
  const tabs = computed(() => {
37
40
  const out: { value: ProviderConnectionKind; label: string; icon: string; slot: string }[] = []
@@ -102,7 +105,8 @@ watch([tabs, () => store.loaded], () => {
102
105
  <div class="space-y-4">
103
106
  <!-- Where agent containers run (writable in local mode; read-only elsewhere). -->
104
107
  <ExecutionBackendSelector axis="execution" />
105
- <ProviderConnectionTab kind="runner-pool" />
108
+ <!-- The runner-pool connect form only when that integration is enabled. -->
109
+ <ProviderConnectionTab v-if="store.isAvailable('runner-pool')" kind="runner-pool" />
106
110
  <!-- Local mode: the warm-pool + checkout reuse ARE the host agent-container
107
111
  runtime, so they live here rather than in a separate menu. -->
108
112
  <section v-if="isLocal" class="border-t border-slate-800 pt-4">
@@ -117,7 +121,8 @@ watch([tabs, () => store.loaded], () => {
117
121
  <div class="space-y-4">
118
122
  <!-- Where Tester environments run (writable in local mode; read-only elsewhere). -->
119
123
  <ExecutionBackendSelector axis="testEnv" />
120
- <ProviderConnectionTab kind="environment" />
124
+ <!-- The environment-provider connect form only when that integration is enabled. -->
125
+ <ProviderConnectionTab v-if="store.isAvailable('environment')" kind="environment" />
121
126
  </div>
122
127
  </template>
123
128
  </UTabs>
@@ -23,7 +23,7 @@ import ProvisioningLogsDrawer from '~/components/provisioning/ProvisioningLogsDr
23
23
 
24
24
  const board = useBoardStore()
25
25
  const execution = useExecutionStore()
26
- const { t, d } = useI18n()
26
+ const { t, d, n } = useI18n()
27
27
 
28
28
  // Per-window blob cache for the captured screenshots; revoked on unmount.
29
29
  const blobs = useArtifactBlobs()
@@ -58,6 +58,14 @@ const executionId = computed(() => instance.value?.id ?? null)
58
58
  // The infra-attempts log drawer is opened on demand (it fetches the per-run log rows).
59
59
  const showProvisioning = ref(false)
60
60
 
61
+ // The in-container docker-compose dependency stand-up record (local-infra tester): whether
62
+ // the dependencies came up + the captured `docker compose up` logs. Unlike the provisioning
63
+ // drawer above (the orchestrator-side container/env spin-up), this is the stand-up that runs
64
+ // INSIDE the container — the highest-signal artifact when local infra fails to come up.
65
+ const infraSetup = computed(() => testState.value?.infraSetup ?? null)
66
+ // The captured stand-up logs are shown on demand (they can be long).
67
+ const showInfraSetupLogs = ref(false)
68
+
61
69
  const screenshots = computed<TestScreenshot[]>(() => report.value?.screenshots ?? [])
62
70
  // Resolve each capture into an object URL for the gallery + lightbox. The shared cache
63
71
  // dedupes, so the lightbox reuses what the thumbnails fetched. (The reference design is not
@@ -338,7 +346,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
338
346
  environment. A no-infra tester (no container, no env) has no infra attempts
339
347
  either, so we don't render an empty header + a log toggle over nothing. -->
340
348
  <section
341
- v-if="step && (step.container || stepEnvironment)"
349
+ v-if="step && (step.container || stepEnvironment || infraSetup)"
342
350
  data-testid="tester-infrastructure"
343
351
  class="space-y-3"
344
352
  >
@@ -347,6 +355,75 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
347
355
  </h3>
348
356
  <StepContainerStatus :step="step" :run-failed="runFailed" />
349
357
  <EnvironmentStatusPanel v-if="stepEnvironment" :environment="stepEnvironment" />
358
+
359
+ <!-- In-container docker-compose dependency stand-up (local-infra tester): the
360
+ outcome + the captured `docker compose up` logs. This is the stand-up that
361
+ runs INSIDE the container, so its output isn't in the provisioning drawer
362
+ below — it's the highest-signal artifact when local infra fails to start. -->
363
+ <div
364
+ v-if="infraSetup"
365
+ data-testid="tester-infra-setup"
366
+ class="rounded-lg border px-3 py-2"
367
+ :class="
368
+ infraSetup.started
369
+ ? 'border-slate-800 bg-slate-900/60'
370
+ : 'border-rose-500/40 bg-rose-500/10'
371
+ "
372
+ >
373
+ <div class="flex items-center gap-2">
374
+ <UIcon
375
+ :name="infraSetup.started ? 'i-lucide-container' : 'i-lucide-circle-x'"
376
+ class="h-3.5 w-3.5 shrink-0"
377
+ :class="infraSetup.started ? 'text-emerald-400' : 'text-rose-400'"
378
+ />
379
+ <span class="text-[13px] font-medium text-slate-200">
380
+ {{ infraSetup.started ? t('testing.standup.up') : t('testing.standup.failed') }}
381
+ </span>
382
+ <span
383
+ v-if="infraSetup.durationMs != null"
384
+ class="ml-auto text-[11px] text-slate-500"
385
+ >
386
+ {{
387
+ t('testing.standup.took', {
388
+ seconds: n(infraSetup.durationMs / 1000, 'decimal'),
389
+ })
390
+ }}
391
+ </span>
392
+ </div>
393
+ <p v-if="infraSetup.composePath" class="mt-1 font-mono text-[11px] text-slate-500">
394
+ {{ infraSetup.composePath }}
395
+ </p>
396
+ <p
397
+ v-if="infraSetup.error"
398
+ class="mt-1 text-[12px] leading-snug text-rose-300"
399
+ data-testid="tester-infra-setup-error"
400
+ >
401
+ {{ infraSetup.error }}
402
+ </p>
403
+ <template v-if="infraSetup.logs">
404
+ <UButton
405
+ :icon="showInfraSetupLogs ? 'i-lucide-chevron-up' : 'i-lucide-scroll-text'"
406
+ variant="ghost"
407
+ size="xs"
408
+ class="mt-1.5"
409
+ data-testid="tester-infra-setup-logs-toggle"
410
+ @click="showInfraSetupLogs = !showInfraSetupLogs"
411
+ >
412
+ {{
413
+ showInfraSetupLogs
414
+ ? t('testing.standup.hideLogs')
415
+ : t('testing.standup.showLogs')
416
+ }}
417
+ </UButton>
418
+ <pre
419
+ v-if="showInfraSetupLogs"
420
+ data-testid="tester-infra-setup-logs"
421
+ class="mt-2 max-h-64 overflow-auto rounded bg-slate-950/70 p-2 font-mono text-[11px] leading-relaxed text-slate-300"
422
+ >{{ infraSetup.logs }}</pre
423
+ >
424
+ </template>
425
+ </div>
426
+
350
427
  <div v-if="executionId">
351
428
  <UButton
352
429
  :icon="showProvisioning ? 'i-lucide-chevron-up' : 'i-lucide-scroll-text'"
@@ -2999,6 +2999,13 @@
2999
2999
  },
3000
3000
  "environment": "Environment",
3001
3001
  "infrastructure": "Infrastructure",
3002
+ "standup": {
3003
+ "up": "Dependencies started",
3004
+ "failed": "Dependencies failed to start",
3005
+ "took": "Took {seconds}s",
3006
+ "showLogs": "Show stand-up logs",
3007
+ "hideLogs": "Hide stand-up logs"
3008
+ },
3002
3009
  "footer": "Scenarios are the areas the Tester chose to exercise (its spec acceptance scenarios). Outcomes and concerns are grouped under them by name.",
3003
3010
  "@screenshotAlt": {
3004
3011
  "description": "Alt text for a captured screenshot thumbnail; {view} is the screen/view name. The literal word 'screenshot' should be localized."
@@ -2912,7 +2912,14 @@
2912
2912
  },
2913
2913
  "environment": "Entorno",
2914
2914
  "footer": "Los escenarios son las áreas que el Tester decidió ejercitar (sus escenarios de aceptación de la especificación). Los resultados y las incidencias se agrupan bajo ellos por nombre.",
2915
- "infrastructure": "Infraestructura"
2915
+ "infrastructure": "Infraestructura",
2916
+ "standup": {
2917
+ "up": "Dependencias iniciadas",
2918
+ "failed": "No se pudieron iniciar las dependencias",
2919
+ "took": "Tardó {seconds}s",
2920
+ "showLogs": "Mostrar registros de arranque",
2921
+ "hideLogs": "Ocultar registros de arranque"
2922
+ }
2916
2923
  },
2917
2924
  "visualConfirm": {
2918
2925
  "ariaLabel": "Confirmación visual",
@@ -2912,7 +2912,14 @@
2912
2912
  },
2913
2913
  "environment": "Environnement",
2914
2914
  "footer": "Les scénarios sont les domaines que le Testeur a choisi d'éprouver (ses scénarios d'acceptation de la spécification). Les résultats et les réserves y sont regroupés par nom.",
2915
- "infrastructure": "Infrastructure"
2915
+ "infrastructure": "Infrastructure",
2916
+ "standup": {
2917
+ "up": "Dépendances démarrées",
2918
+ "failed": "Échec du démarrage des dépendances",
2919
+ "took": "Durée : {seconds}s",
2920
+ "showLogs": "Afficher les journaux de démarrage",
2921
+ "hideLogs": "Masquer les journaux de démarrage"
2922
+ }
2916
2923
  },
2917
2924
  "visualConfirm": {
2918
2925
  "ariaLabel": "Confirmation visuelle",
@@ -2912,7 +2912,14 @@
2912
2912
  },
2913
2913
  "environment": "Środowisko",
2914
2914
  "footer": "Scenariusze to obszary, które Tester postanowił sprawdzić (jego scenariusze akceptacyjne ze specyfikacji). Wyniki i zastrzeżenia są pod nimi grupowane według nazwy.",
2915
- "infrastructure": "Infrastruktura"
2915
+ "infrastructure": "Infrastruktura",
2916
+ "standup": {
2917
+ "up": "Zależności uruchomione",
2918
+ "failed": "Nie udało się uruchomić zależności",
2919
+ "took": "Czas: {seconds}s",
2920
+ "showLogs": "Pokaż dzienniki uruchamiania",
2921
+ "hideLogs": "Ukryj dzienniki uruchamiania"
2922
+ }
2916
2923
  },
2917
2924
  "visualConfirm": {
2918
2925
  "ariaLabel": "Potwierdzenie wizualne",
@@ -2912,7 +2912,14 @@
2912
2912
  },
2913
2913
  "environment": "Середовище",
2914
2914
  "footer": "Сценарії — це області, які Тестувальник вирішив перевірити (його сценарії приймання зі специфікації). Результати та зауваження групуються під ними за назвою.",
2915
- "infrastructure": "Інфраструктура"
2915
+ "infrastructure": "Інфраструктура",
2916
+ "standup": {
2917
+ "up": "Залежності запущено",
2918
+ "failed": "Не вдалося запустити залежності",
2919
+ "took": "Час: {seconds}s",
2920
+ "showLogs": "Показати журнали запуску",
2921
+ "hideLogs": "Сховати журнали запуску"
2922
+ }
2916
2923
  },
2917
2924
  "visualConfirm": {
2918
2925
  "ariaLabel": "Візуальне підтвердження",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.51.0",
3
+ "version": "0.52.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.53.0"
37
+ "@cat-factory/contracts": "0.54.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",