@cat-factory/app 0.17.2 → 0.18.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,6 +24,30 @@ const services = useServicesStore()
|
|
|
24
24
|
const composePath = computed(() => props.block.testComposePath ?? '')
|
|
25
25
|
const noInfra = computed(() => props.block.noInfraDependencies === true)
|
|
26
26
|
|
|
27
|
+
// The default test environment a task under this service is spawned with. `local`
|
|
28
|
+
// stands the dependencies up via docker-compose (or "no infra"); `ephemeral` runs
|
|
29
|
+
// against a provisioned environment. A task can override it per-task in its agent
|
|
30
|
+
// settings. Absent ⇒ the built-in `ephemeral`.
|
|
31
|
+
type TestEnvironment = 'local' | 'ephemeral'
|
|
32
|
+
const TEST_ENVIRONMENTS: { value: TestEnvironment; label: string; hint: string }[] = [
|
|
33
|
+
{
|
|
34
|
+
value: 'ephemeral',
|
|
35
|
+
label: 'Ephemeral environment',
|
|
36
|
+
hint: 'tests run against a provisioned env',
|
|
37
|
+
},
|
|
38
|
+
{ value: 'local', label: 'Local (docker-compose)', hint: 'the Tester stands deps up locally' },
|
|
39
|
+
]
|
|
40
|
+
const effectiveTestEnv = computed<TestEnvironment>(
|
|
41
|
+
() => props.block.defaultTestEnvironment ?? 'ephemeral',
|
|
42
|
+
)
|
|
43
|
+
function setDefaultTestEnv(value: TestEnvironment) {
|
|
44
|
+
board.updateBlock(props.block.id, { defaultTestEnvironment: value })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The provisioning hints (cloud provider + instance size) are advisory inputs to the
|
|
48
|
+
// ephemeral-environment provisioner, not commonly tuned — keep them collapsed by default.
|
|
49
|
+
const showProvisioning = ref(false)
|
|
50
|
+
|
|
27
51
|
// The repo + service subdirectory backing this frame, for the compose-file browser.
|
|
28
52
|
// A monorepo service isn't on the `github_repos` blockId link (that stays null), so
|
|
29
53
|
// fall back to the service catalog mapping, which carries the repo + directory.
|
|
@@ -93,6 +117,27 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
93
117
|
Test infrastructure
|
|
94
118
|
</div>
|
|
95
119
|
|
|
120
|
+
<div class="space-y-1">
|
|
121
|
+
<span class="text-[11px] text-slate-400">Default test environment</span>
|
|
122
|
+
<div class="flex flex-wrap gap-1">
|
|
123
|
+
<UButton
|
|
124
|
+
v-for="e in TEST_ENVIRONMENTS"
|
|
125
|
+
:key="e.value"
|
|
126
|
+
:color="effectiveTestEnv === e.value ? 'primary' : 'neutral'"
|
|
127
|
+
:variant="effectiveTestEnv === e.value ? 'soft' : 'ghost'"
|
|
128
|
+
size="xs"
|
|
129
|
+
:title="e.hint"
|
|
130
|
+
@click="setDefaultTestEnv(e.value)"
|
|
131
|
+
>
|
|
132
|
+
{{ e.label }}
|
|
133
|
+
</UButton>
|
|
134
|
+
</div>
|
|
135
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
136
|
+
The default tasks under this service are spawned with — each task can override it in its
|
|
137
|
+
agent settings.
|
|
138
|
+
</p>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
96
141
|
<div class="space-y-1">
|
|
97
142
|
<label class="text-[11px] text-slate-400">docker-compose path</label>
|
|
98
143
|
<div class="flex items-center gap-1">
|
|
@@ -163,35 +208,59 @@ const missingInfra = computed(() => !noInfra.value && composePath.value.trim() =
|
|
|
163
208
|
Tester won't start.
|
|
164
209
|
</p>
|
|
165
210
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
</
|
|
180
|
-
</div>
|
|
211
|
+
<!-- Provisioning hints: advisory inputs to the ephemeral-environment provisioner.
|
|
212
|
+
Collapsed by default — most services never tune them. -->
|
|
213
|
+
<div class="border-t border-slate-800 pt-2">
|
|
214
|
+
<button
|
|
215
|
+
type="button"
|
|
216
|
+
class="flex w-full items-center gap-1.5 text-left text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:text-slate-300"
|
|
217
|
+
@click="showProvisioning = !showProvisioning"
|
|
218
|
+
>
|
|
219
|
+
<UIcon
|
|
220
|
+
:name="showProvisioning ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
221
|
+
class="h-3.5 w-3.5"
|
|
222
|
+
/>
|
|
223
|
+
Ephemeral environment provisioning
|
|
224
|
+
</button>
|
|
181
225
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
226
|
+
<div v-if="showProvisioning" class="mt-2 space-y-3">
|
|
227
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
228
|
+
A hint for provisioning this service's ephemeral test environment — which cloud provider
|
|
229
|
+
to deploy to and how large an instance to request. Ignored for local (docker-compose)
|
|
230
|
+
testing.
|
|
231
|
+
</p>
|
|
232
|
+
|
|
233
|
+
<div class="space-y-1">
|
|
234
|
+
<span class="text-[11px] text-slate-400">Cloud provider</span>
|
|
235
|
+
<div class="flex flex-wrap gap-1">
|
|
236
|
+
<UButton
|
|
237
|
+
v-for="p in PROVIDERS"
|
|
238
|
+
:key="p.value"
|
|
239
|
+
:color="effectiveProvider === p.value ? 'primary' : 'neutral'"
|
|
240
|
+
:variant="effectiveProvider === p.value ? 'soft' : 'ghost'"
|
|
241
|
+
size="xs"
|
|
242
|
+
@click="setProvider(p.value)"
|
|
243
|
+
>
|
|
244
|
+
{{ p.label }}
|
|
245
|
+
</UButton>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div class="space-y-1">
|
|
250
|
+
<span class="text-[11px] text-slate-400">Instance size</span>
|
|
251
|
+
<div class="flex flex-wrap gap-1">
|
|
252
|
+
<UButton
|
|
253
|
+
v-for="s in SIZES"
|
|
254
|
+
:key="s.value"
|
|
255
|
+
:color="(block.instanceSize ?? 'medium') === s.value ? 'primary' : 'neutral'"
|
|
256
|
+
:variant="(block.instanceSize ?? 'medium') === s.value ? 'soft' : 'ghost'"
|
|
257
|
+
size="xs"
|
|
258
|
+
@click="setSize(s.value)"
|
|
259
|
+
>
|
|
260
|
+
{{ s.label }}
|
|
261
|
+
</UButton>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
195
264
|
</div>
|
|
196
265
|
</div>
|
|
197
266
|
</div>
|
|
@@ -29,6 +29,33 @@ const descriptors = computed(() => {
|
|
|
29
29
|
|
|
30
30
|
const run = computed(() => execution.getByBlock(props.block.id))
|
|
31
31
|
|
|
32
|
+
// The Tester's environment descriptor inherits its default from the service frame this
|
|
33
|
+
// task lives under (set in the service inspector); a task only overrides it by clicking.
|
|
34
|
+
// Walk up the parent chain (frame → module → task) to find that default.
|
|
35
|
+
const serviceDefaultTestEnv = computed<'local' | 'ephemeral' | undefined>(() => {
|
|
36
|
+
let cur: Block | undefined = props.block
|
|
37
|
+
for (let i = 0; i < 8 && cur; i++) {
|
|
38
|
+
if (cur.level === 'frame') return cur.defaultTestEnvironment
|
|
39
|
+
if (!cur.parentId) break
|
|
40
|
+
cur = board.getBlock(cur.parentId)
|
|
41
|
+
}
|
|
42
|
+
return undefined
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
/** The effective default for a descriptor — the inherited service value for the Tester's
|
|
46
|
+
* environment, otherwise the descriptor's own static default. */
|
|
47
|
+
function effectiveDefault(d: { id: string; default: string }): string {
|
|
48
|
+
if (d.id === 'tester.environment' && serviceDefaultTestEnv.value) {
|
|
49
|
+
return serviceDefaultTestEnv.value
|
|
50
|
+
}
|
|
51
|
+
return d.default
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether a descriptor's shown value is inherited (not explicitly pinned on this task). */
|
|
55
|
+
function isInherited(d: { id: string }): boolean {
|
|
56
|
+
return d.id === 'tester.environment' && props.block.agentConfig?.[d.id] === undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
32
59
|
/** A descriptor freezes once its contributing agent's step has left `pending`. */
|
|
33
60
|
function isFrozen(agentKind: string): boolean {
|
|
34
61
|
const steps = run.value?.steps
|
|
@@ -55,19 +82,24 @@ function setValue(id: string, value: string) {
|
|
|
55
82
|
<div v-for="d in descriptors" :key="d.id" class="space-y-1">
|
|
56
83
|
<div class="flex items-center justify-between">
|
|
57
84
|
<span class="text-[11px] text-slate-400">{{ d.label }}</span>
|
|
58
|
-
<
|
|
59
|
-
v-if="
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
<div class="flex items-center gap-1.5">
|
|
86
|
+
<span v-if="isInherited(d)" class="text-[10px] text-slate-500"
|
|
87
|
+
>inherited from service</span
|
|
88
|
+
>
|
|
89
|
+
<UIcon
|
|
90
|
+
v-if="isFrozen(d.agentKind)"
|
|
91
|
+
name="i-lucide-lock"
|
|
92
|
+
class="h-3 w-3 text-slate-500"
|
|
93
|
+
title="Frozen — the agent has started"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
64
96
|
</div>
|
|
65
97
|
<div class="flex flex-wrap gap-1">
|
|
66
98
|
<UButton
|
|
67
99
|
v-for="opt in d.options"
|
|
68
100
|
:key="opt.value"
|
|
69
|
-
:color="valueOf(d.id, d
|
|
70
|
-
:variant="valueOf(d.id, d
|
|
101
|
+
:color="valueOf(d.id, effectiveDefault(d)) === opt.value ? 'primary' : 'neutral'"
|
|
102
|
+
:variant="valueOf(d.id, effectiveDefault(d)) === opt.value ? 'soft' : 'ghost'"
|
|
71
103
|
size="xs"
|
|
72
104
|
:disabled="isFrozen(d.agentKind)"
|
|
73
105
|
@click="setValue(d.id, opt.value)"
|
|
@@ -203,12 +203,17 @@ async function clone(p: Pipeline) {
|
|
|
203
203
|
</script>
|
|
204
204
|
|
|
205
205
|
<template>
|
|
206
|
-
<USlideover
|
|
206
|
+
<USlideover
|
|
207
|
+
v-model:open="open"
|
|
208
|
+
title="Pipeline builder"
|
|
209
|
+
side="left"
|
|
210
|
+
:ui="{ content: 'max-w-[90vw] sm:max-w-2xl lg:max-w-5xl xl:max-w-6xl' }"
|
|
211
|
+
>
|
|
207
212
|
<template #body>
|
|
208
|
-
<div class="grid h-full grid-cols-
|
|
213
|
+
<div class="grid h-full grid-cols-1 gap-4 lg:grid-cols-3">
|
|
209
214
|
<!-- agent palette -->
|
|
210
|
-
<div class="
|
|
211
|
-
<div class="mb-2 flex items-center justify-between gap-2">
|
|
215
|
+
<div class="flex min-h-0 flex-col overflow-hidden">
|
|
216
|
+
<div class="mb-2 flex shrink-0 items-center justify-between gap-2">
|
|
212
217
|
<h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
|
|
213
218
|
Agent palette
|
|
214
219
|
</h3>
|
|
@@ -222,11 +227,13 @@ async function clone(p: Pipeline) {
|
|
|
222
227
|
Add agent
|
|
223
228
|
</UButton>
|
|
224
229
|
</div>
|
|
225
|
-
<
|
|
230
|
+
<div class="min-h-0 flex-1 overflow-y-auto pr-1">
|
|
231
|
+
<AgentPalette @add="add" />
|
|
232
|
+
</div>
|
|
226
233
|
</div>
|
|
227
234
|
|
|
228
235
|
<!-- draft chain -->
|
|
229
|
-
<div class="flex flex-col">
|
|
236
|
+
<div class="flex min-h-0 flex-col overflow-hidden">
|
|
230
237
|
<div class="mb-2 flex items-center justify-between gap-2">
|
|
231
238
|
<h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">Pipeline</h3>
|
|
232
239
|
<UButton
|
|
@@ -286,7 +293,7 @@ async function clone(p: Pipeline) {
|
|
|
286
293
|
Click agents on the left to assemble a linear pipeline.
|
|
287
294
|
</div>
|
|
288
295
|
|
|
289
|
-
<ol v-else class="flex-1 space-y-2 overflow-y-auto">
|
|
296
|
+
<ol v-else class="min-h-0 flex-1 space-y-2 overflow-y-auto pr-1">
|
|
290
297
|
<li
|
|
291
298
|
v-for="(unit, vi) in pipelines.units"
|
|
292
299
|
:key="unit.index"
|
|
@@ -576,160 +583,160 @@ async function clone(p: Pipeline) {
|
|
|
576
583
|
</div>
|
|
577
584
|
</li>
|
|
578
585
|
</ol>
|
|
586
|
+
</div>
|
|
579
587
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
588
|
+
<!-- Saved pipelines: review the library + delete (the run affordance
|
|
589
|
+
moved to the task card / inspector when the palettes were removed). -->
|
|
590
|
+
<div v-if="pipelines.pipelines.length" class="flex min-h-0 flex-col overflow-hidden">
|
|
591
|
+
<div class="mb-2 flex shrink-0 items-center justify-between gap-2">
|
|
592
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
|
|
593
|
+
Saved pipelines
|
|
594
|
+
</h3>
|
|
595
|
+
<UButton
|
|
596
|
+
v-if="archivedCount"
|
|
597
|
+
:icon="showArchived ? 'i-lucide-archive-restore' : 'i-lucide-archive'"
|
|
598
|
+
:color="showArchived ? 'primary' : 'neutral'"
|
|
599
|
+
variant="ghost"
|
|
600
|
+
size="xs"
|
|
601
|
+
@click="showArchived = !showArchived"
|
|
602
|
+
>
|
|
603
|
+
{{ showArchived ? 'Hide archived' : `Archived (${archivedCount})` }}
|
|
604
|
+
</UButton>
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
<!-- Label filter chips. -->
|
|
608
|
+
<div v-if="allLabels.length" class="mb-2 flex shrink-0 flex-wrap items-center gap-1">
|
|
609
|
+
<UBadge
|
|
610
|
+
:color="labelFilter === null ? 'primary' : 'neutral'"
|
|
611
|
+
variant="soft"
|
|
612
|
+
size="xs"
|
|
613
|
+
class="cursor-pointer"
|
|
614
|
+
@click="labelFilter = null"
|
|
615
|
+
>
|
|
616
|
+
All
|
|
617
|
+
</UBadge>
|
|
618
|
+
<UBadge
|
|
619
|
+
v-for="l in allLabels"
|
|
620
|
+
:key="l"
|
|
621
|
+
:color="labelFilter === l ? 'primary' : 'neutral'"
|
|
622
|
+
variant="soft"
|
|
623
|
+
size="xs"
|
|
624
|
+
class="cursor-pointer"
|
|
625
|
+
@click="labelFilter = labelFilter === l ? null : l"
|
|
626
|
+
>
|
|
627
|
+
{{ l }}
|
|
628
|
+
</UBadge>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
<ul class="min-h-0 flex-1 space-y-1.5 overflow-y-auto pr-1">
|
|
632
|
+
<li
|
|
633
|
+
v-for="p in visiblePipelines"
|
|
634
|
+
:key="p.id"
|
|
635
|
+
class="group rounded-lg border border-slate-700 bg-slate-800/40"
|
|
636
|
+
:class="{ 'opacity-60': p.archived }"
|
|
637
|
+
>
|
|
638
|
+
<div class="flex items-center gap-2 px-2 py-1.5">
|
|
639
|
+
<button
|
|
640
|
+
type="button"
|
|
641
|
+
class="flex min-w-0 flex-1 items-center gap-2 text-left"
|
|
642
|
+
@click="toggleSaved(p.id)"
|
|
643
|
+
>
|
|
644
|
+
<UIcon
|
|
645
|
+
:name="
|
|
646
|
+
expandedSaved.has(p.id) ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'
|
|
647
|
+
"
|
|
648
|
+
class="h-3.5 w-3.5 shrink-0 text-slate-500"
|
|
649
|
+
/>
|
|
650
|
+
<span class="min-w-0 flex-1 truncate text-xs text-slate-200">{{ p.name }}</span>
|
|
651
|
+
<UBadge
|
|
652
|
+
v-for="l in p.labels ?? []"
|
|
653
|
+
:key="l"
|
|
654
|
+
color="info"
|
|
655
|
+
variant="soft"
|
|
656
|
+
size="xs"
|
|
657
|
+
class="shrink-0"
|
|
635
658
|
>
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
v-for="l in p.labels ?? []"
|
|
645
|
-
:key="l"
|
|
646
|
-
color="info"
|
|
647
|
-
variant="soft"
|
|
648
|
-
size="xs"
|
|
649
|
-
class="shrink-0"
|
|
650
|
-
>
|
|
651
|
-
{{ l }}
|
|
652
|
-
</UBadge>
|
|
653
|
-
<UBadge
|
|
654
|
-
v-if="p.builtin"
|
|
655
|
-
color="neutral"
|
|
656
|
-
variant="soft"
|
|
657
|
-
size="xs"
|
|
658
|
-
class="shrink-0"
|
|
659
|
-
>
|
|
660
|
-
default
|
|
661
|
-
</UBadge>
|
|
662
|
-
<span class="shrink-0 text-[10px] text-slate-500">
|
|
663
|
-
{{ p.agentKinds.length }} {{ p.agentKinds.length === 1 ? 'step' : 'steps' }}
|
|
664
|
-
</span>
|
|
665
|
-
</button>
|
|
666
|
-
<div
|
|
667
|
-
class="flex shrink-0 items-center opacity-0 transition group-hover:opacity-100"
|
|
659
|
+
{{ l }}
|
|
660
|
+
</UBadge>
|
|
661
|
+
<UBadge
|
|
662
|
+
v-if="p.builtin"
|
|
663
|
+
color="neutral"
|
|
664
|
+
variant="soft"
|
|
665
|
+
size="xs"
|
|
666
|
+
class="shrink-0"
|
|
668
667
|
>
|
|
669
|
-
|
|
668
|
+
default
|
|
669
|
+
</UBadge>
|
|
670
|
+
<span class="shrink-0 text-[10px] text-slate-500">
|
|
671
|
+
{{ p.agentKinds.length }} {{ p.agentKinds.length === 1 ? 'step' : 'steps' }}
|
|
672
|
+
</span>
|
|
673
|
+
</button>
|
|
674
|
+
<div
|
|
675
|
+
class="flex shrink-0 items-center opacity-0 transition group-hover:opacity-100"
|
|
676
|
+
>
|
|
677
|
+
<!-- Archive/unarchive: organize the library without deleting. Works on
|
|
670
678
|
built-ins too (view metadata, not structure). -->
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
679
|
+
<UButton
|
|
680
|
+
:icon="p.archived ? 'i-lucide-archive-restore' : 'i-lucide-archive'"
|
|
681
|
+
color="neutral"
|
|
682
|
+
variant="ghost"
|
|
683
|
+
size="xs"
|
|
684
|
+
:title="p.archived ? 'Unarchive' : 'Archive (hide from the default view)'"
|
|
685
|
+
@click="toggleArchive(p)"
|
|
686
|
+
/>
|
|
687
|
+
<!-- Clone is available on every pipeline — it's how a read-only
|
|
680
688
|
built-in template becomes an editable copy. -->
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
689
|
+
<UButton
|
|
690
|
+
icon="i-lucide-copy"
|
|
691
|
+
color="neutral"
|
|
692
|
+
variant="ghost"
|
|
693
|
+
size="xs"
|
|
694
|
+
:title="p.builtin ? 'Clone this default into an editable copy' : 'Clone'"
|
|
695
|
+
@click="clone(p)"
|
|
696
|
+
/>
|
|
697
|
+
<!-- Built-in templates are read-only; only custom pipelines edit in place. -->
|
|
698
|
+
<UButton
|
|
699
|
+
v-if="!p.builtin"
|
|
700
|
+
icon="i-lucide-pencil"
|
|
701
|
+
color="neutral"
|
|
702
|
+
variant="ghost"
|
|
703
|
+
size="xs"
|
|
704
|
+
title="Edit this pipeline"
|
|
705
|
+
@click="edit(p)"
|
|
706
|
+
/>
|
|
707
|
+
<!-- Built-in templates are read-only — they can be cloned but not
|
|
700
708
|
deleted (the backend rejects it too); only custom ones delete. -->
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
</div>
|
|
709
|
+
<UButton
|
|
710
|
+
v-if="!p.builtin"
|
|
711
|
+
icon="i-lucide-trash-2"
|
|
712
|
+
color="neutral"
|
|
713
|
+
variant="ghost"
|
|
714
|
+
size="xs"
|
|
715
|
+
@click="pipelines.removePipeline(p.id)"
|
|
716
|
+
/>
|
|
710
717
|
</div>
|
|
718
|
+
</div>
|
|
711
719
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
720
|
+
<!-- Full ordered step list, revealed on click. -->
|
|
721
|
+
<ol
|
|
722
|
+
v-if="expandedSaved.has(p.id)"
|
|
723
|
+
class="space-y-1 border-t border-slate-800 px-2 py-2 pl-7"
|
|
724
|
+
>
|
|
725
|
+
<li
|
|
726
|
+
v-for="(k, i) in p.agentKinds"
|
|
727
|
+
:key="i"
|
|
728
|
+
class="flex items-center gap-2"
|
|
729
|
+
:class="{ 'opacity-50 line-through': p.enabled?.[i] === false }"
|
|
730
|
+
:title="p.enabled?.[i] === false ? 'Disabled — skipped at run' : undefined"
|
|
716
731
|
>
|
|
717
|
-
<
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
i + 1
|
|
726
|
-
}}</span>
|
|
727
|
-
<AgentKindIcon :kind="k" show-label />
|
|
728
|
-
</li>
|
|
729
|
-
</ol>
|
|
730
|
-
</li>
|
|
731
|
-
</ul>
|
|
732
|
-
</div>
|
|
732
|
+
<span class="w-4 shrink-0 text-center text-[10px] text-slate-500">{{
|
|
733
|
+
i + 1
|
|
734
|
+
}}</span>
|
|
735
|
+
<AgentKindIcon :kind="k" show-label />
|
|
736
|
+
</li>
|
|
737
|
+
</ol>
|
|
738
|
+
</li>
|
|
739
|
+
</ul>
|
|
733
740
|
</div>
|
|
734
741
|
</div>
|
|
735
742
|
</template>
|
package/app/types/domain.ts
CHANGED
|
@@ -143,6 +143,13 @@ export interface Block {
|
|
|
143
143
|
testComposePath?: string
|
|
144
144
|
/** service-only (frame): the service has no infra dependencies to stand up. */
|
|
145
145
|
noInfraDependencies?: boolean
|
|
146
|
+
/**
|
|
147
|
+
* service-only (frame): the default test environment a task under this service is
|
|
148
|
+
* spawned with — `local` (docker-compose infra) or `ephemeral` (provisioned env).
|
|
149
|
+
* Tasks inherit it unless they override via their `tester.environment` config.
|
|
150
|
+
* absent = the built-in `ephemeral`.
|
|
151
|
+
*/
|
|
152
|
+
defaultTestEnvironment?: 'local' | 'ephemeral'
|
|
146
153
|
/** service-only (frame): cloud provider the service's jobs run on; absent = account default. */
|
|
147
154
|
cloudProvider?: CloudProvider
|
|
148
155
|
/** service-only (frame): abstract instance size for the service's jobs; absent = default. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|