@cat-factory/app 0.46.10 → 0.46.12
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/AuthGate.vue +2 -1
- package/app/components/auth/LoginScreen.vue +41 -31
- package/app/components/auth/ResetPasswordScreen.vue +16 -13
- package/app/components/auth/UserMenu.vue +3 -2
- package/app/components/layout/AccountDeploymentSettings.vue +75 -32
- package/app/components/layout/AccountFragmentSettings.vue +2 -3
- package/app/components/layout/AccountTeamSettings.vue +75 -45
- package/app/components/layout/AiProvidersBanner.vue +11 -11
- package/app/components/layout/BoardSwitcher.vue +63 -29
- package/app/components/layout/CommandBar.vue +68 -54
- package/app/components/layout/GitHubPatBanner.vue +13 -6
- package/app/components/layout/IntegrationBackTitle.vue +4 -1
- package/app/components/layout/IntegrationsHub.vue +67 -49
- package/app/components/layout/NotificationsInbox.vue +48 -19
- package/app/components/layout/PersonalSetupModal.vue +26 -16
- package/app/components/layout/ProviderConfigBanner.vue +14 -9
- package/app/components/layout/SideBar.vue +20 -18
- package/app/components/layout/SpendWarningBanner.vue +16 -16
- package/app/components/observability/StepMetricsBar.vue +31 -11
- package/app/components/observability/StepModelActivity.vue +3 -2
- package/app/components/panels/AgentStepDetail.vue +52 -39
- package/app/components/panels/DecisionModal.vue +12 -5
- package/app/components/panels/GenericStructuredResultView.vue +16 -6
- package/app/components/panels/InspectorPanel.vue +35 -28
- package/app/components/panels/ObservabilityPanel.vue +92 -49
- package/app/components/panels/StepMetadataCard.vue +87 -30
- package/app/components/panels/StepRestartControl.vue +4 -3
- package/app/components/panels/StepRunMeta.vue +23 -10
- package/app/components/panels/StepTestReport.vue +14 -11
- package/app/components/panels/inspector/ContainerSummary.vue +25 -14
- package/app/components/panels/inspector/EpicChildren.vue +7 -4
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +60 -19
- package/app/components/panels/inspector/ServiceFragments.vue +3 -2
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +18 -13
- package/app/components/panels/inspector/ServiceTestConfig.vue +50 -39
- package/app/components/panels/inspector/TaskAgentConfig.vue +6 -5
- package/app/components/panels/inspector/TaskDependencies.vue +9 -4
- package/app/components/panels/inspector/TaskEstimateBadge.vue +14 -13
- package/app/components/panels/inspector/TaskExecution.vue +65 -31
- package/app/components/panels/inspector/TaskRunSettings.vue +93 -56
- package/app/components/panels/inspector/TaskStructure.vue +5 -4
- package/app/components/providers/PersonalSubscriptionSection.vue +1 -2
- package/i18n/locales/en.json +814 -0
- package/i18n/locales/es.json +811 -0
- package/i18n/locales/fr.json +811 -0
- package/i18n/locales/pl.json +811 -0
- package/i18n/locales/uk.json +811 -0
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ const board = useBoardStore()
|
|
|
20
20
|
const accounts = useAccountsStore()
|
|
21
21
|
const github = useGitHubStore()
|
|
22
22
|
const services = useServicesStore()
|
|
23
|
+
const { t } = useI18n()
|
|
23
24
|
|
|
24
25
|
const composePath = computed(() => props.block.testComposePath ?? '')
|
|
25
26
|
const noInfra = computed(() => props.block.noInfraDependencies === true)
|
|
@@ -29,14 +30,20 @@ const noInfra = computed(() => props.block.noInfraDependencies === true)
|
|
|
29
30
|
// against a provisioned environment. A task can override it per-task in its agent
|
|
30
31
|
// settings. Absent ⇒ the built-in `ephemeral`.
|
|
31
32
|
type TestEnvironment = 'local' | 'ephemeral'
|
|
32
|
-
const TEST_ENVIRONMENTS
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const TEST_ENVIRONMENTS = computed<{ value: TestEnvironment; label: string; hint: string }[]>(
|
|
34
|
+
() => [
|
|
35
|
+
{
|
|
36
|
+
value: 'ephemeral',
|
|
37
|
+
label: t('inspector.testConfig.env.ephemeral'),
|
|
38
|
+
hint: t('inspector.testConfig.env.ephemeralHint'),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: 'local',
|
|
42
|
+
label: t('inspector.testConfig.env.local'),
|
|
43
|
+
hint: t('inspector.testConfig.env.localHint'),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
)
|
|
40
47
|
const effectiveTestEnv = computed<TestEnvironment>(
|
|
41
48
|
() => props.block.defaultTestEnvironment ?? 'ephemeral',
|
|
42
49
|
)
|
|
@@ -86,20 +93,20 @@ function toggleNoInfra(value: boolean) {
|
|
|
86
93
|
board.updateBlock(props.block.id, { noInfraDependencies: value })
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
const PROVIDERS
|
|
96
|
+
const PROVIDERS = computed<{ value: CloudProvider; label: string }[]>(() => [
|
|
90
97
|
{ value: 'cloudflare', label: 'Cloudflare' },
|
|
91
|
-
{ value: 'docker', label: '
|
|
98
|
+
{ value: 'docker', label: t('inspector.testConfig.providers.docker') },
|
|
92
99
|
{ value: 'aws', label: 'AWS' },
|
|
93
100
|
{ value: 'gcp', label: 'GCP' },
|
|
94
101
|
{ value: 'azure', label: 'Azure' },
|
|
95
|
-
{ value: 'custom', label: '
|
|
96
|
-
]
|
|
97
|
-
const SIZES
|
|
98
|
-
{ value: 'small', label: '
|
|
99
|
-
{ value: 'medium', label: '
|
|
100
|
-
{ value: 'large', label: '
|
|
101
|
-
{ value: 'xlarge', label: '
|
|
102
|
-
]
|
|
102
|
+
{ value: 'custom', label: t('inspector.testConfig.providers.custom') },
|
|
103
|
+
])
|
|
104
|
+
const SIZES = computed<{ value: InstanceSize; label: string }[]>(() => [
|
|
105
|
+
{ value: 'small', label: t('inspector.testConfig.sizes.small') },
|
|
106
|
+
{ value: 'medium', label: t('inspector.testConfig.sizes.medium') },
|
|
107
|
+
{ value: 'large', label: t('inspector.testConfig.sizes.large') },
|
|
108
|
+
{ value: 'xlarge', label: t('inspector.testConfig.sizes.xlarge') },
|
|
109
|
+
])
|
|
103
110
|
|
|
104
111
|
function setProvider(value: CloudProvider) {
|
|
105
112
|
board.updateBlock(props.block.id, { cloudProvider: value })
|
|
@@ -114,11 +121,11 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
114
121
|
<template>
|
|
115
122
|
<div class="space-y-3">
|
|
116
123
|
<div class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
117
|
-
|
|
124
|
+
{{ t('inspector.testConfig.title') }}
|
|
118
125
|
</div>
|
|
119
126
|
|
|
120
127
|
<div class="space-y-1">
|
|
121
|
-
<span class="text-[11px] text-slate-400">
|
|
128
|
+
<span class="text-[11px] text-slate-400">{{ t('inspector.testConfig.defaultEnv') }}</span>
|
|
122
129
|
<div class="flex flex-wrap gap-1">
|
|
123
130
|
<UButton
|
|
124
131
|
v-for="e in TEST_ENVIRONMENTS"
|
|
@@ -133,13 +140,12 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
133
140
|
</UButton>
|
|
134
141
|
</div>
|
|
135
142
|
<p class="text-[11px] leading-snug text-slate-500">
|
|
136
|
-
|
|
137
|
-
agent settings.
|
|
143
|
+
{{ t('inspector.testConfig.defaultEnvHint') }}
|
|
138
144
|
</p>
|
|
139
145
|
</div>
|
|
140
146
|
|
|
141
147
|
<div class="space-y-1">
|
|
142
|
-
<label class="text-[11px] text-slate-400">
|
|
148
|
+
<label class="text-[11px] text-slate-400">{{ t('inspector.testConfig.composePath') }}</label>
|
|
143
149
|
<div class="flex items-center gap-1">
|
|
144
150
|
<UInput
|
|
145
151
|
:model-value="composePath"
|
|
@@ -159,20 +165,20 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
159
165
|
color="neutral"
|
|
160
166
|
icon="i-lucide-folder-search"
|
|
161
167
|
:disabled="noInfra"
|
|
162
|
-
title="
|
|
168
|
+
:title="t('inspector.testConfig.browseRepo')"
|
|
163
169
|
@click="openBrowse"
|
|
164
170
|
/>
|
|
165
171
|
</div>
|
|
166
172
|
<p class="text-[11px] leading-snug text-slate-500">
|
|
167
|
-
|
|
173
|
+
{{ t('inspector.testConfig.composeHint') }}
|
|
168
174
|
</p>
|
|
169
175
|
</div>
|
|
170
176
|
|
|
171
|
-
<UModal v-model:open="browseOpen" title="
|
|
177
|
+
<UModal v-model:open="browseOpen" :title="t('inspector.testConfig.selectComposeTitle')">
|
|
172
178
|
<template #body>
|
|
173
179
|
<div v-if="repoContext" class="space-y-3">
|
|
174
180
|
<p class="text-xs text-slate-400">
|
|
175
|
-
|
|
181
|
+
{{ t('inspector.testConfig.selectComposeHint') }}
|
|
176
182
|
</p>
|
|
177
183
|
<RepoTreeBrowser
|
|
178
184
|
v-model="pickedPath"
|
|
@@ -183,12 +189,16 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
183
189
|
<div class="flex items-center justify-between gap-2">
|
|
184
190
|
<p class="truncate text-xs text-slate-400">
|
|
185
191
|
<template v-if="pickedPath">
|
|
186
|
-
|
|
192
|
+
<i18n-t keypath="inspector.testConfig.selected" tag="span" scope="global">
|
|
193
|
+
<template #path>
|
|
194
|
+
<code class="text-slate-200">{{ pickedPath }}</code>
|
|
195
|
+
</template>
|
|
196
|
+
</i18n-t>
|
|
187
197
|
</template>
|
|
188
|
-
<template v-else>
|
|
198
|
+
<template v-else>{{ t('inspector.testConfig.noFileSelected') }}</template>
|
|
189
199
|
</p>
|
|
190
200
|
<UButton size="xs" color="primary" :disabled="!pickedPath" @click="applyPicked">
|
|
191
|
-
|
|
201
|
+
{{ t('inspector.testConfig.useThisFile') }}
|
|
192
202
|
</UButton>
|
|
193
203
|
</div>
|
|
194
204
|
</div>
|
|
@@ -200,12 +210,11 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
200
210
|
:model-value="noInfra"
|
|
201
211
|
@update:model-value="(v: boolean | 'indeterminate') => toggleNoInfra(v === true)"
|
|
202
212
|
/>
|
|
203
|
-
|
|
213
|
+
{{ t('inspector.testConfig.noInfra') }}
|
|
204
214
|
</label>
|
|
205
215
|
|
|
206
216
|
<p v-if="missingInfra" class="text-[11px] leading-snug text-amber-500">
|
|
207
|
-
|
|
208
|
-
Tester won't start.
|
|
217
|
+
{{ t('inspector.testConfig.missingInfra') }}
|
|
209
218
|
</p>
|
|
210
219
|
|
|
211
220
|
<!-- Provisioning hints: advisory inputs to the ephemeral-environment provisioner.
|
|
@@ -220,18 +229,18 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
220
229
|
:name="showProvisioning ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
221
230
|
class="h-3.5 w-3.5"
|
|
222
231
|
/>
|
|
223
|
-
|
|
232
|
+
{{ t('inspector.testConfig.provisioningTitle') }}
|
|
224
233
|
</button>
|
|
225
234
|
|
|
226
235
|
<div v-if="showProvisioning" class="mt-2 space-y-3">
|
|
227
236
|
<p class="text-[11px] leading-snug text-slate-500">
|
|
228
|
-
|
|
229
|
-
to deploy to and how large an instance to request. Ignored for local (docker-compose)
|
|
230
|
-
testing.
|
|
237
|
+
{{ t('inspector.testConfig.provisioningHint') }}
|
|
231
238
|
</p>
|
|
232
239
|
|
|
233
240
|
<div class="space-y-1">
|
|
234
|
-
<span class="text-[11px] text-slate-400">
|
|
241
|
+
<span class="text-[11px] text-slate-400">{{
|
|
242
|
+
t('inspector.testConfig.cloudProvider')
|
|
243
|
+
}}</span>
|
|
235
244
|
<div class="flex flex-wrap gap-1">
|
|
236
245
|
<UButton
|
|
237
246
|
v-for="p in PROVIDERS"
|
|
@@ -247,7 +256,9 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
247
256
|
</div>
|
|
248
257
|
|
|
249
258
|
<div class="space-y-1">
|
|
250
|
-
<span class="text-[11px] text-slate-400">
|
|
259
|
+
<span class="text-[11px] text-slate-400">{{
|
|
260
|
+
t('inspector.testConfig.instanceSize')
|
|
261
|
+
}}</span>
|
|
251
262
|
<div class="flex flex-wrap gap-1">
|
|
252
263
|
<UButton
|
|
253
264
|
v-for="s in SIZES"
|
|
@@ -13,6 +13,7 @@ const props = defineProps<{ block: Block }>()
|
|
|
13
13
|
const board = useBoardStore()
|
|
14
14
|
const agentConfig = useAgentConfigStore()
|
|
15
15
|
const execution = useExecutionStore()
|
|
16
|
+
const { t } = useI18n()
|
|
16
17
|
|
|
17
18
|
// The descriptors that apply: those contributed by the task's pinned pipeline, plus
|
|
18
19
|
// any whose value is already set (so an existing choice always stays visible/editable
|
|
@@ -77,20 +78,20 @@ function setValue(id: string, value: string) {
|
|
|
77
78
|
<template>
|
|
78
79
|
<div v-if="descriptors.length" class="space-y-3">
|
|
79
80
|
<div class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
80
|
-
|
|
81
|
+
{{ t('inspector.agentConfig.title') }}
|
|
81
82
|
</div>
|
|
82
83
|
<div v-for="d in descriptors" :key="d.id" class="space-y-1">
|
|
83
84
|
<div class="flex items-center justify-between">
|
|
84
85
|
<span class="text-[11px] text-slate-400">{{ d.label }}</span>
|
|
85
86
|
<div class="flex items-center gap-1.5">
|
|
86
|
-
<span v-if="isInherited(d)" class="text-[10px] text-slate-500"
|
|
87
|
-
|
|
88
|
-
>
|
|
87
|
+
<span v-if="isInherited(d)" class="text-[10px] text-slate-500">{{
|
|
88
|
+
t('inspector.agentConfig.inherited')
|
|
89
|
+
}}</span>
|
|
89
90
|
<UIcon
|
|
90
91
|
v-if="isFrozen(d.agentKind)"
|
|
91
92
|
name="i-lucide-lock"
|
|
92
93
|
class="h-3 w-3 text-slate-500"
|
|
93
|
-
title="
|
|
94
|
+
:title="t('inspector.agentConfig.frozen')"
|
|
94
95
|
/>
|
|
95
96
|
</div>
|
|
96
97
|
</div>
|
|
@@ -5,6 +5,7 @@ const props = defineProps<{ block: Block }>()
|
|
|
5
5
|
|
|
6
6
|
const board = useBoardStore()
|
|
7
7
|
const { depLabel } = useDepLabels()
|
|
8
|
+
const { t } = useI18n()
|
|
8
9
|
|
|
9
10
|
const deps = computed(() =>
|
|
10
11
|
(props.block.dependsOn ?? []).map((id) => board.getBlock(id)).filter((b): b is Block => !!b),
|
|
@@ -35,7 +36,7 @@ function removeDep(depId: string) {
|
|
|
35
36
|
<div>
|
|
36
37
|
<div class="mb-1 flex items-center justify-between">
|
|
37
38
|
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
38
|
-
|
|
39
|
+
{{ t('inspector.dependencies.title') }}
|
|
39
40
|
</span>
|
|
40
41
|
<UDropdownMenu v-if="depMenu.length" :items="depMenu">
|
|
41
42
|
<UButton
|
|
@@ -55,16 +56,20 @@ function removeDep(depId: string) {
|
|
|
55
56
|
variant="subtle"
|
|
56
57
|
size="sm"
|
|
57
58
|
class="cursor-pointer"
|
|
58
|
-
:title="
|
|
59
|
+
:title="
|
|
60
|
+
d.status === 'done'
|
|
61
|
+
? t('inspector.dependencies.merged')
|
|
62
|
+
: t('inspector.dependencies.notMerged')
|
|
63
|
+
"
|
|
59
64
|
@click="removeDep(d.id)"
|
|
60
65
|
>
|
|
61
66
|
{{ label(d) }}
|
|
62
67
|
<UIcon name="i-lucide-x" class="ml-0.5 h-3 w-3" />
|
|
63
68
|
</UBadge>
|
|
64
69
|
</div>
|
|
65
|
-
<div v-else class="text-[11px] text-slate-500">
|
|
70
|
+
<div v-else class="text-[11px] text-slate-500">{{ t('inspector.dependencies.empty') }}</div>
|
|
66
71
|
<div v-if="!runnable" class="mt-1 text-[10px] text-amber-400">
|
|
67
|
-
|
|
72
|
+
{{ t('inspector.dependencies.blocked') }}
|
|
68
73
|
</div>
|
|
69
74
|
</div>
|
|
70
75
|
</template>
|
|
@@ -6,18 +6,19 @@ import { computed } from 'vue'
|
|
|
6
6
|
import type { Block } from '~/types/domain'
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{ block: Block }>()
|
|
9
|
+
const { t, n } = useI18n()
|
|
9
10
|
|
|
10
11
|
const estimate = computed(() => props.block.estimate ?? null)
|
|
11
12
|
|
|
12
|
-
const AXES =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const AXES = computed(
|
|
14
|
+
() =>
|
|
15
|
+
[
|
|
16
|
+
{ key: 'complexity', label: t('inspector.estimate.complexity') },
|
|
17
|
+
{ key: 'risk', label: t('inspector.estimate.risk') },
|
|
18
|
+
{ key: 'impact', label: t('inspector.estimate.impact') },
|
|
19
|
+
] as const,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
|
-
function pct(n: number): number {
|
|
19
|
-
return Math.round(n * 100)
|
|
20
|
-
}
|
|
21
22
|
/** Cool→hot bar colour by severity (low = sky, mid = amber, high = rose). */
|
|
22
23
|
function barClass(n: number): string {
|
|
23
24
|
if (n >= 0.66) return 'bg-rose-500'
|
|
@@ -32,7 +33,7 @@ function barClass(n: number): string {
|
|
|
32
33
|
class="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-slate-400"
|
|
33
34
|
>
|
|
34
35
|
<UIcon name="i-lucide-gauge" class="h-3.5 w-3.5" />
|
|
35
|
-
|
|
36
|
+
{{ t('inspector.estimate.title') }}
|
|
36
37
|
</div>
|
|
37
38
|
<div class="space-y-1.5 rounded-lg border border-slate-800 bg-slate-900/40 p-2.5">
|
|
38
39
|
<div v-for="axis in AXES" :key="axis.key" class="flex items-center gap-2">
|
|
@@ -41,12 +42,12 @@ function barClass(n: number): string {
|
|
|
41
42
|
<div
|
|
42
43
|
class="h-full rounded-full"
|
|
43
44
|
:class="barClass(estimate[axis.key])"
|
|
44
|
-
:style="{ width: `${
|
|
45
|
+
:style="{ width: `${Math.round(estimate[axis.key] * 100)}%` }"
|
|
45
46
|
/>
|
|
46
47
|
</div>
|
|
47
|
-
<span class="w-9 shrink-0 text-right text-xs tabular-nums text-slate-300"
|
|
48
|
-
|
|
49
|
-
>
|
|
48
|
+
<span class="w-9 shrink-0 text-right text-xs tabular-nums text-slate-300">{{
|
|
49
|
+
n(estimate[axis.key], { key: 'percent' })
|
|
50
|
+
}}</span>
|
|
50
51
|
</div>
|
|
51
52
|
<p v-if="estimate.rationale" class="pt-1 text-xs leading-relaxed text-slate-500">
|
|
52
53
|
{{ estimate.rationale }}
|
|
@@ -11,6 +11,7 @@ const agentRuns = useAgentRunsStore()
|
|
|
11
11
|
const ui = useUiStore()
|
|
12
12
|
const models = useModelsStore()
|
|
13
13
|
const reviews = useReviewStage()
|
|
14
|
+
const { t } = useI18n()
|
|
14
15
|
|
|
15
16
|
// The async stage this task's iterative reviewer gate (requirements-review / clarity-review)
|
|
16
17
|
// is mid-cycle in (folding the answers, then re-reviewing), or null. While set, the gate is
|
|
@@ -19,11 +20,11 @@ const reviews = useReviewStage()
|
|
|
19
20
|
const reviewStage = computed(() => reviews.stageForBlock(props.block.id))
|
|
20
21
|
const reviewStageLabel = computed(() =>
|
|
21
22
|
reviewStage.value === 'incorporating'
|
|
22
|
-
? '
|
|
23
|
+
? t('inspector.execution.stage.incorporating')
|
|
23
24
|
: reviewStage.value === 'reviewing'
|
|
24
|
-
? '
|
|
25
|
+
? t('inspector.execution.stage.reviewing')
|
|
25
26
|
: reviewStage.value === 'recommending'
|
|
26
|
-
? '
|
|
27
|
+
? t('inspector.execution.stage.recommending')
|
|
27
28
|
: null,
|
|
28
29
|
)
|
|
29
30
|
|
|
@@ -44,14 +45,16 @@ const pr = computed(() => props.block.pullRequest)
|
|
|
44
45
|
const prMerged = computed(() => props.block.status === 'done')
|
|
45
46
|
const prLabel = computed(() => {
|
|
46
47
|
const number = pr.value?.number
|
|
47
|
-
return number
|
|
48
|
+
return number
|
|
49
|
+
? t('inspector.execution.prNumber', { number })
|
|
50
|
+
: t('inspector.execution.pullRequest')
|
|
48
51
|
})
|
|
49
52
|
|
|
50
53
|
const stepLabel: Record<string, string> = {
|
|
51
|
-
pending: '
|
|
52
|
-
working: '
|
|
53
|
-
waiting_decision: '
|
|
54
|
-
done: '
|
|
54
|
+
pending: 'inspector.execution.stepState.pending',
|
|
55
|
+
working: 'inspector.execution.stepState.working',
|
|
56
|
+
waiting_decision: 'inspector.execution.stepState.waiting_decision',
|
|
57
|
+
done: 'inspector.execution.stepState.done',
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/** A step left mid-flight (`working`) on a failed run gave up — not still working. */
|
|
@@ -68,17 +71,19 @@ function labelForStep(s: {
|
|
|
68
71
|
startingContainer?: boolean
|
|
69
72
|
}) {
|
|
70
73
|
// A step left mid-flight on a failed run reads "Failed", not the misleading "Working".
|
|
71
|
-
if (stepFailed(s)) return '
|
|
74
|
+
if (stepFailed(s)) return t('inspector.execution.failed')
|
|
72
75
|
// A reviewer gate mid-cycle reads its working stage, not "Needs approval".
|
|
73
76
|
if (reviews.isBackground(s.agentKind, props.block.id) && reviewStageLabel.value)
|
|
74
77
|
return reviewStageLabel.value
|
|
75
78
|
// A companion that spent its rework budget needs a decision, not an approval.
|
|
76
|
-
if (s.approval?.status === 'pending' && s.companion?.exceeded)
|
|
77
|
-
|
|
79
|
+
if (s.approval?.status === 'pending' && s.companion?.exceeded)
|
|
80
|
+
return t('inspector.execution.needsDecision')
|
|
81
|
+
if (s.approval?.status === 'pending') return t('inspector.execution.needsApproval')
|
|
78
82
|
// A container-backed step whose container is still cold-booting (only while the
|
|
79
83
|
// run is live — a failed run's mid-flight step is no longer spinning up).
|
|
80
|
-
if (s.startingContainer && !runFailed.value) return '
|
|
81
|
-
|
|
84
|
+
if (s.startingContainer && !runFailed.value) return t('inspector.execution.spinningUp')
|
|
85
|
+
const key = stepLabel[s.state]
|
|
86
|
+
return key ? t(key) : s.state
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
function openDecisionFor(decisionId: string) {
|
|
@@ -138,11 +143,11 @@ async function resetRun() {
|
|
|
138
143
|
size="xs"
|
|
139
144
|
:loading="stopping"
|
|
140
145
|
:disabled="resetting"
|
|
141
|
-
title="
|
|
146
|
+
:title="t('inspector.execution.stopTooltip')"
|
|
142
147
|
data-testid="run-stop"
|
|
143
148
|
@click="stopRun"
|
|
144
149
|
>
|
|
145
|
-
|
|
150
|
+
{{ t('inspector.execution.stop') }}
|
|
146
151
|
</UButton>
|
|
147
152
|
<!-- Destructive: discard the run and return the task to planned. -->
|
|
148
153
|
<UButton
|
|
@@ -152,11 +157,11 @@ async function resetRun() {
|
|
|
152
157
|
size="xs"
|
|
153
158
|
:loading="resetting"
|
|
154
159
|
:disabled="stopping"
|
|
155
|
-
title="
|
|
160
|
+
:title="t('inspector.execution.resetTooltip')"
|
|
156
161
|
data-testid="run-reset"
|
|
157
162
|
@click="resetRun"
|
|
158
163
|
>
|
|
159
|
-
|
|
164
|
+
{{ t('inspector.execution.reset') }}
|
|
160
165
|
</UButton>
|
|
161
166
|
</div>
|
|
162
167
|
</div>
|
|
@@ -173,7 +178,11 @@ async function resetRun() {
|
|
|
173
178
|
<button
|
|
174
179
|
type="button"
|
|
175
180
|
class="flex min-w-0 cursor-pointer items-center gap-2 text-left transition hover:text-white"
|
|
176
|
-
:title="
|
|
181
|
+
:title="
|
|
182
|
+
s.output
|
|
183
|
+
? t('inspector.execution.viewDetailsOutput')
|
|
184
|
+
: t('inspector.execution.viewDetails')
|
|
185
|
+
"
|
|
177
186
|
@click="openStep(i)"
|
|
178
187
|
>
|
|
179
188
|
<UIcon
|
|
@@ -187,9 +196,9 @@ async function resetRun() {
|
|
|
187
196
|
<span
|
|
188
197
|
v-if="isCompanionKind(s.agentKind)"
|
|
189
198
|
class="shrink-0 rounded bg-slate-700/60 px-1 text-[9px] font-medium uppercase tracking-wide text-slate-300"
|
|
190
|
-
title="
|
|
199
|
+
:title="t('inspector.execution.companionTooltip')"
|
|
191
200
|
>
|
|
192
|
-
|
|
201
|
+
{{ t('inspector.execution.companion') }}
|
|
193
202
|
</span>
|
|
194
203
|
<UIcon
|
|
195
204
|
:name="s.output ? 'i-lucide-book-open-text' : 'i-lucide-info'"
|
|
@@ -201,8 +210,15 @@ async function resetRun() {
|
|
|
201
210
|
class="ml-auto font-mono text-[10px] tabular-nums text-slate-300"
|
|
202
211
|
:title="
|
|
203
212
|
s.subtasks.inProgress > 0
|
|
204
|
-
?
|
|
205
|
-
|
|
213
|
+
? t('inspector.execution.subtasksProgress', {
|
|
214
|
+
completed: s.subtasks.completed,
|
|
215
|
+
total: s.subtasks.total,
|
|
216
|
+
inProgress: s.subtasks.inProgress,
|
|
217
|
+
})
|
|
218
|
+
: t('inspector.execution.subtasksDone', {
|
|
219
|
+
completed: s.subtasks.completed,
|
|
220
|
+
total: s.subtasks.total,
|
|
221
|
+
})
|
|
206
222
|
"
|
|
207
223
|
>
|
|
208
224
|
{{ s.subtasks.completed }}/{{ s.subtasks.total }}
|
|
@@ -225,7 +241,7 @@ async function resetRun() {
|
|
|
225
241
|
icon="i-lucide-circle-help"
|
|
226
242
|
@click="openDecisionFor(s.decision.id)"
|
|
227
243
|
>
|
|
228
|
-
|
|
244
|
+
{{ t('inspector.execution.resolve') }}
|
|
229
245
|
</UButton>
|
|
230
246
|
<!-- reviewer gate folding/re-reviewing in the background: a working
|
|
231
247
|
indicator, NOT a "Review" gate (the human is summoned only if needed) -->
|
|
@@ -248,7 +264,7 @@ async function resetRun() {
|
|
|
248
264
|
icon="i-lucide-alert-triangle"
|
|
249
265
|
@click="openApprovalFor(s.approval.id)"
|
|
250
266
|
>
|
|
251
|
-
|
|
267
|
+
{{ t('inspector.execution.decide') }}
|
|
252
268
|
</UButton>
|
|
253
269
|
<UButton
|
|
254
270
|
v-else-if="s.approval && s.approval.status === 'pending'"
|
|
@@ -262,7 +278,11 @@ async function resetRun() {
|
|
|
262
278
|
"
|
|
263
279
|
@click="openApprovalFor(s.approval.id)"
|
|
264
280
|
>
|
|
265
|
-
{{
|
|
281
|
+
{{
|
|
282
|
+
agentKindMeta(s.agentKind).resultView
|
|
283
|
+
? t('inspector.execution.review')
|
|
284
|
+
: t('inspector.execution.approve')
|
|
285
|
+
}}
|
|
266
286
|
</UButton>
|
|
267
287
|
</div>
|
|
268
288
|
<div
|
|
@@ -286,10 +306,20 @@ async function resetRun() {
|
|
|
286
306
|
<div
|
|
287
307
|
v-if="s.selectedFragmentIds && s.selectedFragmentIds.length"
|
|
288
308
|
class="mt-0.5 flex flex-wrap items-center gap-1 pl-6 text-[10px] text-slate-500"
|
|
289
|
-
:title="
|
|
309
|
+
:title="
|
|
310
|
+
t('inspector.execution.fragmentsTooltip', {
|
|
311
|
+
fragments: s.selectedFragmentIds.join(', '),
|
|
312
|
+
})
|
|
313
|
+
"
|
|
290
314
|
>
|
|
291
315
|
<UIcon name="i-lucide-book-marked" class="h-3 w-3 shrink-0" />
|
|
292
|
-
<span>{{
|
|
316
|
+
<span>{{
|
|
317
|
+
t(
|
|
318
|
+
'inspector.execution.standardsApplied',
|
|
319
|
+
{ count: s.selectedFragmentIds.length },
|
|
320
|
+
s.selectedFragmentIds.length,
|
|
321
|
+
)
|
|
322
|
+
}}</span>
|
|
293
323
|
</div>
|
|
294
324
|
<!-- Conditionally-run companion (the Tester's fixer): possible/running/
|
|
295
325
|
completed/skipped, so it's clear whether a fix pass ran. -->
|
|
@@ -306,7 +336,11 @@ async function resetRun() {
|
|
|
306
336
|
]"
|
|
307
337
|
/>
|
|
308
338
|
<span class="text-slate-400">
|
|
309
|
-
{{
|
|
339
|
+
{{
|
|
340
|
+
t('inspector.execution.companionOf', {
|
|
341
|
+
label: agentKindMeta(gateCompanionFor(s, runFailed)!.kind).label,
|
|
342
|
+
})
|
|
343
|
+
}}
|
|
310
344
|
</span>
|
|
311
345
|
<span
|
|
312
346
|
class="ml-auto"
|
|
@@ -325,7 +359,7 @@ async function resetRun() {
|
|
|
325
359
|
<!-- Open PR: link straight to it on GitHub -->
|
|
326
360
|
<div v-if="pr" class="space-y-2">
|
|
327
361
|
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
328
|
-
|
|
362
|
+
{{ t('inspector.execution.pullRequest') }}
|
|
329
363
|
</span>
|
|
330
364
|
<UButton
|
|
331
365
|
:to="pr.url"
|
|
@@ -342,7 +376,7 @@ async function resetRun() {
|
|
|
342
376
|
<span class="flex w-full items-center gap-2">
|
|
343
377
|
{{ prLabel }}
|
|
344
378
|
<UBadge :color="prMerged ? 'success' : 'info'" variant="subtle" size="sm" class="ml-auto">
|
|
345
|
-
{{ prMerged ? '
|
|
379
|
+
{{ prMerged ? t('inspector.execution.merged') : t('inspector.execution.open') }}
|
|
346
380
|
</UBadge>
|
|
347
381
|
</span>
|
|
348
382
|
</UButton>
|
|
@@ -362,7 +396,7 @@ async function resetRun() {
|
|
|
362
396
|
block
|
|
363
397
|
@click="execution.mergePr(block.id)"
|
|
364
398
|
>
|
|
365
|
-
|
|
399
|
+
{{ t('inspector.execution.mergePr') }}
|
|
366
400
|
</UButton>
|
|
367
401
|
</div>
|
|
368
402
|
</template>
|