@cat-factory/app 0.147.4 → 0.147.5
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/README.md +11 -0
- package/app/components/panels/StepRunMeta.vue +14 -1
- package/app/composables/useStepTimer.spec.ts +22 -1
- package/app/composables/useStepTimer.ts +25 -1
- package/app/docs/consumer-extensions.md +171 -0
- package/i18n/locales/de.json +2 -0
- package/i18n/locales/en.json +2 -0
- package/i18n/locales/es.json +2 -0
- package/i18n/locales/fr.json +2 -0
- package/i18n/locales/he.json +2 -0
- package/i18n/locales/it.json +2 -0
- package/i18n/locales/ja.json +2 -0
- package/i18n/locales/pl.json +2 -0
- package/i18n/locales/tr.json +2 -0
- package/i18n/locales/uk.json +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -52,6 +52,17 @@ over the WebSocket. How that sync works is written up in
|
|
|
52
52
|
| `types/` | TypeScript domain unions (`domain.ts`) and wire types mirroring the contracts. |
|
|
53
53
|
| `utils/` | Small pure helpers. |
|
|
54
54
|
|
|
55
|
+
## Extending the layer (consumer modules)
|
|
56
|
+
|
|
57
|
+
A deployment can contribute its own components — result windows, nav entries, inspector
|
|
58
|
+
panels, agent-kind palette data — **without forking**, through the auto-imported
|
|
59
|
+
`registerAppModule` seam (the frontend analogue of the backend's `registerAgentKind` /
|
|
60
|
+
`registerGate` registries). The authoring walkthrough, the reusable shared building blocks
|
|
61
|
+
(`ResultWindowShell`, the `StepRunMeta` run-metadata block, `useResultView`, …), and the
|
|
62
|
+
namespacing / degradation rules are in
|
|
63
|
+
[`app/docs/consumer-extensions.md`](./app/docs/consumer-extensions.md); a full worked
|
|
64
|
+
example ships in [`deploy/frontend`](../../deploy/frontend) (the `acme:security` module).
|
|
65
|
+
|
|
55
66
|
## Key UI surfaces
|
|
56
67
|
|
|
57
68
|
- **Board canvas** (`components/board`) — `BoardCanvas` + `nodes/` (`BlockNode`,
|
|
@@ -27,7 +27,7 @@ const props = defineProps<{
|
|
|
27
27
|
const models = useModelsStore()
|
|
28
28
|
const { t, d } = useI18n()
|
|
29
29
|
|
|
30
|
-
const { isRunning, durationLabel } = useStepTimer({
|
|
30
|
+
const { isRunning, durationLabel, activityAgoLabel } = useStepTimer({
|
|
31
31
|
step: () => props.step,
|
|
32
32
|
runFailed: () => props.runFailed ?? false,
|
|
33
33
|
failureAt: () => props.failureAt,
|
|
@@ -74,6 +74,19 @@ async function copyRunId() {
|
|
|
74
74
|
</p>
|
|
75
75
|
</div>
|
|
76
76
|
|
|
77
|
+
<!-- Liveness: time since the agent's last sign of life (the harness heartbeat), distinct from
|
|
78
|
+
the elapsed clock above — a long, quiet phase keeps this small while elapsed climbs, so a
|
|
79
|
+
genuinely-active-but-quiet run reads apart from a wedged one. Only while actively running. -->
|
|
80
|
+
<div v-if="isRunning && activityAgoLabel" data-testid="step-activity">
|
|
81
|
+
<h4 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
82
|
+
{{ t('panels.stepMeta.activity') }}
|
|
83
|
+
</h4>
|
|
84
|
+
<p class="flex items-center gap-1.5 text-[12px] tabular-nums text-slate-300">
|
|
85
|
+
<span class="h-1.5 w-1.5 rounded-full bg-emerald-400" />
|
|
86
|
+
{{ t('panels.stepMeta.activityAgo', { duration: activityAgoLabel }) }}
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
77
90
|
<div v-if="formatClock(step.startedAt)">
|
|
78
91
|
<h4 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
79
92
|
{{ t('panels.stepMeta.started') }}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import type { PipelineStep } from '~/types/execution'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
stepActivityAgoMs,
|
|
5
|
+
stepDurationLabel,
|
|
6
|
+
stepDurationMs,
|
|
7
|
+
stepIsRunning,
|
|
8
|
+
} from '~/composables/useStepTimer'
|
|
4
9
|
|
|
5
10
|
// The pure helpers encode one freeze rule shared by the list surfaces (pipeline timeline,
|
|
6
11
|
// inspector run list) and the single-step overlay, so pin the precedence here:
|
|
@@ -66,6 +71,22 @@ describe('stepDurationMs', () => {
|
|
|
66
71
|
})
|
|
67
72
|
})
|
|
68
73
|
|
|
74
|
+
describe('stepActivityAgoMs (liveness clock, distinct from elapsed)', () => {
|
|
75
|
+
it('is null when the step has no heartbeat (non-container / not yet polled / old image)', () => {
|
|
76
|
+
expect(stepActivityAgoMs(step({ startedAt: 4000 }), NOW)).toBeNull()
|
|
77
|
+
expect(stepActivityAgoMs(null, NOW)).toBeNull()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('measures time since the last heartbeat, not since the step started', () => {
|
|
81
|
+
// Started long ago (elapsed 6s) but active 1s ago — the whole point: a quiet run reads active.
|
|
82
|
+
expect(stepActivityAgoMs(step({ startedAt: 4000, lastActivityAt: 9000 }), NOW)).toBe(1000)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('clamps to 0 so container/server clock skew never renders a negative value', () => {
|
|
86
|
+
expect(stepActivityAgoMs(step({ startedAt: 4000, lastActivityAt: NOW + 5000 }), NOW)).toBe(0)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
69
90
|
describe('stepDurationLabel', () => {
|
|
70
91
|
it('is null until the step has started', () => {
|
|
71
92
|
expect(stepDurationLabel(step({ startedAt: undefined }), NOW, false, null)).toBeNull()
|
|
@@ -39,6 +39,21 @@ export function stepDurationLabel(
|
|
|
39
39
|
return ms == null ? null : formatDuration(ms)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Milliseconds since the container agent's last observed sign of life (`lastActivityAt`, the
|
|
44
|
+
* harness liveness heartbeat), at wall-clock `nowMs` — or null when the step has none (a
|
|
45
|
+
* non-container step, one not yet polled, or an older harness image). Clamped at 0 so minor
|
|
46
|
+
* container/server clock skew never renders a negative "active in the future" value. This is the
|
|
47
|
+
* LIVENESS clock (time since the agent last did anything), distinct from {@link stepDurationMs}'s
|
|
48
|
+
* ELAPSED clock (total time the step has been running) — a long, quiet phase keeps the elapsed
|
|
49
|
+
* clock climbing while this stays small, which is exactly how a genuinely-active-but-quiet run is
|
|
50
|
+
* told apart from a wedged one.
|
|
51
|
+
*/
|
|
52
|
+
export function stepActivityAgoMs(step: PipelineStep | null, nowMs: number): number | null {
|
|
53
|
+
if (step?.lastActivityAt == null) return null
|
|
54
|
+
return Math.max(0, nowMs - step.lastActivityAt)
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
/**
|
|
43
58
|
* A shared 1s wall-clock tick for surfaces that render many steps' live durations
|
|
44
59
|
* at once (the pipeline timeline, the inspector run list). One interval drives every
|
|
@@ -84,7 +99,16 @@ export function useStepTimer(opts: {
|
|
|
84
99
|
durationMs.value == null ? null : formatDuration(durationMs.value),
|
|
85
100
|
)
|
|
86
101
|
|
|
87
|
-
|
|
102
|
+
// Time since the agent's last sign of life (the liveness heartbeat), ticking only while the
|
|
103
|
+
// step is actively running — a finished/parked/failed step isn't "active", so it reads null.
|
|
104
|
+
const activityAgoMs = computed(() =>
|
|
105
|
+
isRunning.value ? stepActivityAgoMs(opts.step(), nowTick.value) : null,
|
|
106
|
+
)
|
|
107
|
+
const activityAgoLabel = computed(() =>
|
|
108
|
+
activityAgoMs.value == null ? null : formatDuration(activityAgoMs.value),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return { isRunning, durationMs, durationLabel, activityAgoMs, activityAgoLabel }
|
|
88
112
|
}
|
|
89
113
|
|
|
90
114
|
export function formatDuration(ms: number): string {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Extending the SPA from a consumer deployment
|
|
2
|
+
|
|
3
|
+
A deployment that consumes this layer (`extends: ['@cat-factory/app']`) can contribute
|
|
4
|
+
its own **components** — result windows, navigation entries, inspector panels, agent-kind
|
|
5
|
+
palette data — **without forking the layer**. This is the frontend counterpart of the
|
|
6
|
+
backend's public registries (`registerAgentKind`, `registerGate`; see
|
|
7
|
+
[`backend/docs/custom-agents.md`](../../../backend/docs/custom-agents.md)). The governing
|
|
8
|
+
principle is the same: **zero host edits for a consumer extension**.
|
|
9
|
+
|
|
10
|
+
A worked, end-to-end example ships in the template deployment —
|
|
11
|
+
[`deploy/frontend/app/`](../../../deploy/frontend) (the `acme:security` module) — the
|
|
12
|
+
frontend analogue of the backend
|
|
13
|
+
[`@cat-factory/example-custom-agent`](../../../backend/internal/example-custom-agent)
|
|
14
|
+
package. Read this guide alongside it.
|
|
15
|
+
|
|
16
|
+
> This is the **landed** surface (modular-vue adoption slices 1–5). The larger consumer
|
|
17
|
+
> extension programme (custom task types, generic interactive phases, overlays, consumer
|
|
18
|
+
> notification kinds, stream events, and the hardened public export surface) is tracked in
|
|
19
|
+
> [`docs/initiatives/frontend-extension-mechanism.md`](../../../docs/initiatives/frontend-extension-mechanism.md).
|
|
20
|
+
|
|
21
|
+
## The one seam: `registerAppModule`
|
|
22
|
+
|
|
23
|
+
Everything is one call from your own Nuxt plugin. A module is a plain descriptor; each
|
|
24
|
+
capability is a slot contribution.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// deploy/frontend/app/plugins/acme.client.ts
|
|
28
|
+
import { defineModule } from '@modular-vue/core'
|
|
29
|
+
import AcmeSecurityReport from '../components/acme/AcmeSecurityReport.vue'
|
|
30
|
+
|
|
31
|
+
export default defineNuxtPlugin(() => {
|
|
32
|
+
registerAppModule(
|
|
33
|
+
defineModule({
|
|
34
|
+
id: 'acme:security', // namespaced — see "Rules" below
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
slots: {
|
|
37
|
+
resultViews: [{ id: 'acme:security-report', component: AcmeSecurityReport }],
|
|
38
|
+
agentKinds: [
|
|
39
|
+
/* palette entries — see "Agent kinds" */
|
|
40
|
+
],
|
|
41
|
+
nav: [
|
|
42
|
+
/* sidebar / command-palette destinations — see "Navigation" */
|
|
43
|
+
],
|
|
44
|
+
inspectorPanels: [
|
|
45
|
+
/* per-block detail panels — see "Inspector panels" */
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- **`registerAppModule` is auto-imported** from the layer (`app/utils/modular.ts`), so you
|
|
54
|
+
need no deep import into the layer's internals.
|
|
55
|
+
- **`enforce: 'post'` is load-bearing.** The layer's own install plugin is `enforce:
|
|
56
|
+
'post'`, and Nuxt runs layer plugins before the consuming app's plugins within one
|
|
57
|
+
enforce bucket. So your registration plugin must run in the **default** (or `pre`) bucket
|
|
58
|
+
— i.e. **do not** put `enforce: 'post'` on it, or it registers too late and is silently
|
|
59
|
+
missed.
|
|
60
|
+
- **`defineModule` / the slot-entry types come from `@modular-vue/core`** — add it to your
|
|
61
|
+
deployment's `dependencies`.
|
|
62
|
+
|
|
63
|
+
## The landed seams
|
|
64
|
+
|
|
65
|
+
| Seam | Slot key | Entry shape | Host |
|
|
66
|
+
| ----------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------- |
|
|
67
|
+
| Run-detail windows | `resultViews` | `{ id: '<ns>:<name>', component }` | `StepResultViewHost` via `dispatchStepView` |
|
|
68
|
+
| Agent kinds (palette data) | `agentKinds` | `{ kind, container, presentation: { label, icon, color, description, category?, resultView? } }` | agents store merge → `agentKindMeta` |
|
|
69
|
+
| Sidebar / command-palette / toolbar | `nav` | `{ id, labelKey, icon, surfaces, gate?, run, sidebar?, command?, toolbar? }` | the three shells via `useNavContributions` |
|
|
70
|
+
| Inspector body panels | `inspectorPanels` | `{ id, component, when(block), order }` (`PanelEntry<Block>`) | `<PanelsOutlet>` in `InspectorPanel` |
|
|
71
|
+
| Multi-step wizards | (journeys) | `registerJourney` + step modules | `<JourneyHost>` / `<JourneyOutlet>` |
|
|
72
|
+
| Locale strings | (i18n) | `i18n/locales/*.json` in the deployment | `@nuxtjs/i18n` layer deep-merge |
|
|
73
|
+
|
|
74
|
+
### Run-detail windows (`resultViews` + `agentKinds`)
|
|
75
|
+
|
|
76
|
+
Backend data selects a frontend component, joined by a namespaced id:
|
|
77
|
+
|
|
78
|
+
1. A backend agent kind (registered on `AgentKindRegistry`, e.g.
|
|
79
|
+
`@cat-factory/example-custom-agent`'s `security-auditor`) arrives in the workspace
|
|
80
|
+
snapshot with `presentation.resultView: '<ns>:<name>'` — **or** you code-ship the kind's
|
|
81
|
+
palette entry via the `agentKinds` slot (as the example does, to give an existing kind a
|
|
82
|
+
bespoke window).
|
|
83
|
+
2. You contribute the component to `resultViews` under the SAME id.
|
|
84
|
+
3. When a step of that kind is opened, `dispatchStepView` resolves the kind's `resultView`
|
|
85
|
+
id, and `StepResultViewHost` mounts your paired component.
|
|
86
|
+
|
|
87
|
+
An unpaired id degrades to the generic prose panel (a dev-console warning names the dangling
|
|
88
|
+
id); a structured kind with no bespoke window gets the built-in `generic-structured` viewer
|
|
89
|
+
for free.
|
|
90
|
+
|
|
91
|
+
### Navigation
|
|
92
|
+
|
|
93
|
+
A consumer nav item carries its own `run` closure (first-party items use a typed `action`
|
|
94
|
+
id instead). Optional `gate: (g) => g.canManageIntegrations` hides it reactively without the
|
|
95
|
+
permission. `surfaces` picks which shells render it (`'sidebar' | 'command' | 'toolbar'`),
|
|
96
|
+
and `sidebar` / `command` / `toolbar` place it within each.
|
|
97
|
+
|
|
98
|
+
### Inspector panels
|
|
99
|
+
|
|
100
|
+
Contribute `PanelEntry<Block>` entries; each `when(block)` predicate decides which blocks
|
|
101
|
+
show the panel, and `order` places it among the built-ins. Your panel component reads the
|
|
102
|
+
selected block via `usePanelSubject<Block>()` (`@modular-vue/core`). `when` must tolerate a
|
|
103
|
+
nullish subject (the boot-time validation resolve passes `null`).
|
|
104
|
+
|
|
105
|
+
## Reuse the shared building blocks — don't reinvent them
|
|
106
|
+
|
|
107
|
+
The layer ships window/inspector primitives you compose instead of hand-rolling chrome or
|
|
108
|
+
re-deriving the "which run is this / how did the model do" facts. **Composables** (and the
|
|
109
|
+
`registerAppModule` helper) are auto-imported into your consumer code with zero imports;
|
|
110
|
+
**components** must be named through the `#components` virtual module (see the boxed note
|
|
111
|
+
below). Compose these:
|
|
112
|
+
|
|
113
|
+
| Building block | Reference it as | What it gives you |
|
|
114
|
+
| ---------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| `ResultWindowShell` | `#components` → `PanelsResultWindowShell` | The shared modal chrome for a result window — backdrop, header (icon/title/subtitle), a `#header-extras` slot, close button, and the modal _behaviour_ (focus-trap + return, body-scroll lock, shared-stack Escape via `useModalBehavior`). Pass `stepRef` to surface the shared "restart from here" control. |
|
|
116
|
+
| `StepRunMeta` | `#components` → `PanelsStepRunMeta` | **The shared run-details metadata block** every agent window reuses: step position, live duration, model, run id, and the LLM model-activity rollup. Drop it into your window's sidebar — never reinvent run metadata. |
|
|
117
|
+
| `MarkdownProse` | `#components` → `CommonMarkdownProse` | Render an agent's prose output as markdown. |
|
|
118
|
+
| `CopyButton` | `#components` → `CommonCopyButton` | The shared copy-to-clipboard affordance. |
|
|
119
|
+
| `InspectorSection` | `#components` → `PanelsInspectorSection` | The collapsible inspector-section shell (chevron header, count, hint) so a consumer panel reads like a built-in one. |
|
|
120
|
+
| `useResultView(id)` | auto-imported | The window seam contract: `{ open, blockId, instanceId, stepIndex, close }` (+ an `onOpen` loader for windows that fetch, and an `onClose` flush). Escape is owned by the shell, not here. |
|
|
121
|
+
| `usePanelSubject<T>()` | `@modular-vue/core` | Read the block injected into an inspector panel by `<PanelsOutlet>`. |
|
|
122
|
+
|
|
123
|
+
> **Reference layer components through `#components`, not bare tags.** Nuxt auto-registers a
|
|
124
|
+
> layer's components under a **path-derived** name (`components/panels/ResultWindowShell.vue`
|
|
125
|
+
> → `PanelsResultWindowShell`), and only rewrites bare `<ResultWindowShell>` tags inside the
|
|
126
|
+
> layer's own SFCs. A bare tag in a **consumer** SFC resolves to nothing and silently renders
|
|
127
|
+
> as an unknown element — its `<slot>` children still appear, so a shallow test can pass while
|
|
128
|
+
> the shared chrome (and its `data-testid`) never mounts. Import the ones you use from
|
|
129
|
+
> `#components` (Nuxt's stable virtual registry — **not** a deep path into the layer's
|
|
130
|
+
> `app/components/*`), aliasing them back to the short names for readable templates:
|
|
131
|
+
>
|
|
132
|
+
> ```ts
|
|
133
|
+
> import {
|
|
134
|
+
> PanelsResultWindowShell as ResultWindowShell,
|
|
135
|
+
> PanelsStepRunMeta as StepRunMeta,
|
|
136
|
+
> CommonMarkdownProse as MarkdownProse,
|
|
137
|
+
> } from '#components'
|
|
138
|
+
> ```
|
|
139
|
+
>
|
|
140
|
+
> Composables (`useResultView`, `useI18n`, the Pinia stores) and `registerAppModule` are
|
|
141
|
+
> auto-imported across layers by Nuxt's separate imports mechanism, so those need no import.
|
|
142
|
+
> Hardening these building blocks into an explicitly exported, location-independent public
|
|
143
|
+
> surface is slice G of the initiative.
|
|
144
|
+
|
|
145
|
+
The example `AcmeSecurityReport.vue` window is a full demonstration: it imports
|
|
146
|
+
`ResultWindowShell` + `StepRunMeta` + `MarkdownProse` from `#components`, composes them with
|
|
147
|
+
the auto-imported `useResultView`, adds only its own bespoke body (the security findings), and
|
|
148
|
+
reads the auditor's structured assessment straight off `step.custom`.
|
|
149
|
+
|
|
150
|
+
## i18n
|
|
151
|
+
|
|
152
|
+
Ship your strings under your own namespace in the deployment's `i18n/locales/*.json` (e.g.
|
|
153
|
+
`acme.*`). `@nuxtjs/i18n` is layer-aware and **deep-merges** them into the layer catalog, so
|
|
154
|
+
`t('acme.securityReport.title')` resolves in your components with no config change. The
|
|
155
|
+
layer's typed-key and locale-parity guards govern only the layer's own keys — your namespace
|
|
156
|
+
is yours.
|
|
157
|
+
|
|
158
|
+
## Rules that hold across every seam
|
|
159
|
+
|
|
160
|
+
- **Namespacing.** Every consumer-authored id is `<ns>:<name>`. Built-ins are never
|
|
161
|
+
shadowable — the merge logic drops a consumer entry whose id collides with a built-in
|
|
162
|
+
(see the agents store).
|
|
163
|
+
- **Fail fast at boot, degrade at runtime.** Duplicate ids across first-party + consumer
|
|
164
|
+
modules throw when the layer resolves the merged slots at startup; missing pairings and
|
|
165
|
+
unknown wire ids degrade with a dev-console warning, never a crash.
|
|
166
|
+
- **Never crash on stale data.** An id that arrives on the wire (a `resultView`, an agent
|
|
167
|
+
kind) after its extension was removed must degrade to a defined rendering — extensions get
|
|
168
|
+
uninstalled while persisted rows outlive them.
|
|
169
|
+
- **The remote manifest is DATA only.** Components never travel the wire; per-workspace
|
|
170
|
+
variability comes from which capabilities the snapshot lists, not from which modules are
|
|
171
|
+
registered (registration is boot-static).
|
package/i18n/locales/de.json
CHANGED
|
@@ -1386,6 +1386,8 @@
|
|
|
1386
1386
|
"stateLabel": "Status",
|
|
1387
1387
|
"duration": "Dauer",
|
|
1388
1388
|
"elapsed": "vergangen",
|
|
1389
|
+
"activity": "Letzte Aktivität",
|
|
1390
|
+
"activityAgo": "vor {duration} aktiv",
|
|
1389
1391
|
"step": "Schritt",
|
|
1390
1392
|
"stepOf": "{number} von {total}",
|
|
1391
1393
|
"started": "Gestartet",
|
package/i18n/locales/en.json
CHANGED
|
@@ -1161,6 +1161,8 @@
|
|
|
1161
1161
|
"stateLabel": "State",
|
|
1162
1162
|
"duration": "Duration",
|
|
1163
1163
|
"elapsed": "elapsed",
|
|
1164
|
+
"activity": "Last activity",
|
|
1165
|
+
"activityAgo": "active {duration} ago",
|
|
1164
1166
|
"step": "Step",
|
|
1165
1167
|
"stepOf": "{number} of {total}",
|
|
1166
1168
|
"started": "Started",
|
package/i18n/locales/es.json
CHANGED
|
@@ -1104,6 +1104,8 @@
|
|
|
1104
1104
|
"stateLabel": "Estado",
|
|
1105
1105
|
"duration": "Duración",
|
|
1106
1106
|
"elapsed": "transcurrido",
|
|
1107
|
+
"activity": "Última actividad",
|
|
1108
|
+
"activityAgo": "activo hace {duration}",
|
|
1107
1109
|
"step": "Paso",
|
|
1108
1110
|
"stepOf": "{number} de {total}",
|
|
1109
1111
|
"started": "Iniciado",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1104,6 +1104,8 @@
|
|
|
1104
1104
|
"stateLabel": "État",
|
|
1105
1105
|
"duration": "Durée",
|
|
1106
1106
|
"elapsed": "écoulé",
|
|
1107
|
+
"activity": "Dernière activité",
|
|
1108
|
+
"activityAgo": "actif il y a {duration}",
|
|
1107
1109
|
"step": "Étape",
|
|
1108
1110
|
"stepOf": "{number} sur {total}",
|
|
1109
1111
|
"started": "Démarré",
|
package/i18n/locales/he.json
CHANGED
package/i18n/locales/it.json
CHANGED
|
@@ -1386,6 +1386,8 @@
|
|
|
1386
1386
|
"stateLabel": "Stato",
|
|
1387
1387
|
"duration": "Durata",
|
|
1388
1388
|
"elapsed": "trascorso",
|
|
1389
|
+
"activity": "Ultima attività",
|
|
1390
|
+
"activityAgo": "attivo {duration} fa",
|
|
1389
1391
|
"step": "Passaggio",
|
|
1390
1392
|
"stepOf": "{number} di {total}",
|
|
1391
1393
|
"started": "Iniziato",
|
package/i18n/locales/ja.json
CHANGED
package/i18n/locales/pl.json
CHANGED
|
@@ -1104,6 +1104,8 @@
|
|
|
1104
1104
|
"stateLabel": "Stan",
|
|
1105
1105
|
"duration": "Czas trwania",
|
|
1106
1106
|
"elapsed": "upłynęło",
|
|
1107
|
+
"activity": "Ostatnia aktywność",
|
|
1108
|
+
"activityAgo": "aktywny {duration} temu",
|
|
1107
1109
|
"step": "Krok",
|
|
1108
1110
|
"stepOf": "{number} z {total}",
|
|
1109
1111
|
"started": "Rozpoczęto",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -1104,6 +1104,8 @@
|
|
|
1104
1104
|
"stateLabel": "Durum",
|
|
1105
1105
|
"duration": "Süre",
|
|
1106
1106
|
"elapsed": "geçen süre",
|
|
1107
|
+
"activity": "Son etkinlik",
|
|
1108
|
+
"activityAgo": "{duration} önce etkindi",
|
|
1107
1109
|
"step": "Adım",
|
|
1108
1110
|
"stepOf": "{total} adımdan {number}.",
|
|
1109
1111
|
"started": "Başladı",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1104,6 +1104,8 @@
|
|
|
1104
1104
|
"stateLabel": "Стан",
|
|
1105
1105
|
"duration": "Тривалість",
|
|
1106
1106
|
"elapsed": "минуло",
|
|
1107
|
+
"activity": "Остання активність",
|
|
1108
|
+
"activityAgo": "активний {duration} тому",
|
|
1107
1109
|
"step": "Крок",
|
|
1108
1110
|
"stepOf": "{number} з {total}",
|
|
1109
1111
|
"started": "Розпочато",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.147.
|
|
3
|
+
"version": "0.147.5",
|
|
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",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.40",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.154.
|
|
43
|
+
"@cat-factory/contracts": "0.154.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|