@cat-factory/app 0.255.0 → 0.255.1
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/pipeline/PipelineBuilder.vue +50 -81
- package/app/composables/usePipelineDraftWarnings.ts +136 -0
- package/app/stores/pipelines/draftRetainEnvironment.spec.ts +62 -0
- package/app/stores/pipelines/draftStepConfig.ts +57 -27
- package/app/utils/catalog.ts +11 -6
- package/i18n/locales/de.json +7 -0
- package/i18n/locales/en.json +7 -0
- package/i18n/locales/es.json +7 -0
- package/i18n/locales/fr.json +7 -0
- package/i18n/locales/he.json +7 -0
- package/i18n/locales/it.json +7 -0
- package/i18n/locales/ja.json +7 -0
- package/i18n/locales/pl.json +7 -0
- package/i18n/locales/tr.json +7 -0
- package/i18n/locales/uk.json +7 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, watch } from 'vue'
|
|
3
|
-
import {
|
|
3
|
+
import { DEPLOYER_AGENT_KIND } from '@cat-factory/contracts'
|
|
4
4
|
import type { AgentKind, Pipeline } from '~/types/domain'
|
|
5
5
|
import AgentPalette from '~/components/palettes/AgentPalette.vue'
|
|
6
6
|
import AgentKindIcon from '~/components/pipeline/AgentKindIcon.vue'
|
|
@@ -189,31 +189,6 @@ const skills = useSkillsStore()
|
|
|
189
189
|
// skill picker. A `skill` step is parametrized by the picked skill (`stepOptions.skillId`).
|
|
190
190
|
const skillSelectItems = computed(() => skills.catalog.map((s) => ({ label: s.name, value: s.id })))
|
|
191
191
|
|
|
192
|
-
// An enabled `skill` step with no picked skill — mirrors the backend save/start rejection
|
|
193
|
-
// (`assertValidSkillSteps`), surfaced as an inline hint so the user fixes it before saving.
|
|
194
|
-
const skillStepNeedsPick = computed(() =>
|
|
195
|
-
pipelines.draft.some(
|
|
196
|
-
(k, i) => k === 'skill' && pipelines.draftEnabled[i] !== false && !pipelines.draftSkillId(i),
|
|
197
|
-
),
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
// Steps whose agent category the chosen purpose CONTRADICTS (a non-`build` purpose writes no code
|
|
201
|
-
// and runs no tests, so the Implementation/Testing categories are disallowed, see
|
|
202
|
-
// `purposeAllowsAgentCategory`). Only reachable by switching an existing draft to a non-`build`
|
|
203
|
-
// purpose AFTER such steps were added, since the palette offers neither. The backend has no
|
|
204
|
-
// kind→category map to gate on, so the builder is the enforcement point: save is blocked until the
|
|
205
|
-
// offending steps are removed (or the purpose set back to Build).
|
|
206
|
-
//
|
|
207
|
-
// Deliberately the compatibility predicate, not the palette's narrower relevance one: a purpose
|
|
208
|
-
// that merely stops SUGGESTING a category must not turn a pipeline somebody already built into
|
|
209
|
-
// one they cannot save.
|
|
210
|
-
const stepsDisallowedByPurpose = computed(() =>
|
|
211
|
-
pipelines.draft.filter((kind) => {
|
|
212
|
-
const category = agentKindMeta(kind).category
|
|
213
|
-
return !!category && !purposeAllowsAgentCategory(pipelines.draftPurpose, category)
|
|
214
|
-
}),
|
|
215
|
-
)
|
|
216
|
-
|
|
217
192
|
// The workspace's foundational-services catalog, for the binary-output storage/context picker.
|
|
218
193
|
const foundational = useFoundationalServicesStore()
|
|
219
194
|
|
|
@@ -231,18 +206,22 @@ function showBinaryOutputPicker(kind: AgentKind): boolean {
|
|
|
231
206
|
return agentKindMeta(kind).binaryOutput === true
|
|
232
207
|
}
|
|
233
208
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Whether to offer the Deployer's "keep this environment past the run" declaration on the step at
|
|
211
|
+
* `index`. An OVERRIDE in the `showOverrideField` sense — reclaiming what a run stood up is the
|
|
212
|
+
* default and the everyday shape, and a deliberately retained preview environment is not the
|
|
213
|
+
* everyday delivery loop — so it is `advanced`-tier, and a step that already CARRIES the
|
|
214
|
+
* declaration keeps showing it in either mode (never hide the way back).
|
|
215
|
+
*
|
|
216
|
+
* Hiding it strands nobody: the fault it answers (`deployer_without_disposer`) always has the
|
|
217
|
+
* tier-neutral fix of adding the Disposer back, which both the inline hint and the save refusal
|
|
218
|
+
* name FIRST. That is what makes this different from the binary-output picker above, where the
|
|
219
|
+
* selection is required and hiding it would leave a step with no savable form at all.
|
|
220
|
+
*/
|
|
221
|
+
function showRetainEnvironmentToggle(kind: AgentKind, index: number): boolean {
|
|
222
|
+
if (kind !== DEPLOYER_AGENT_KIND) return false
|
|
223
|
+
return showOverrideField(uiMode.isAdvanced, pipelines.draftRetainEnvironment(index) || null)
|
|
224
|
+
}
|
|
246
225
|
|
|
247
226
|
// A step's picked skill id is no longer in the account catalog (the source dir was renamed or
|
|
248
227
|
// unlinked). The step will fail cleanly at dispatch; flag it so the user re-picks.
|
|
@@ -370,22 +349,12 @@ function companionLabel(kind: string): string | null {
|
|
|
370
349
|
return companion ? agentKindMeta(companion).label : null
|
|
371
350
|
}
|
|
372
351
|
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
const hasEstimatorBefore = (i: number) =>
|
|
380
|
-
kinds.slice(0, i).some((k, j) => k === 'task-estimator' && pipelines.draftEnabled[j] !== false)
|
|
381
|
-
for (let i = 0; i < kinds.length; i++) {
|
|
382
|
-
if (pipelines.draftEnabled[i] === false) continue
|
|
383
|
-
const gated =
|
|
384
|
-
pipelines.draftGating[i]?.enabled || pipelines.draftTesterQuality[i]?.gating?.enabled
|
|
385
|
-
if (gated && !hasEstimatorBefore(i)) return true
|
|
386
|
-
}
|
|
387
|
-
return false
|
|
388
|
-
})
|
|
352
|
+
// Everything that is WRONG with the draft, as an ordered list of hints the template renders once,
|
|
353
|
+
// plus the purpose conflict that also disables Save. Lives in its own composable because each of
|
|
354
|
+
// these is one predicate beside one identically-styled line, and five of them had crowded out the
|
|
355
|
+
// component (`usePipelineDraftWarnings`).
|
|
356
|
+
const { hints: draftWarnings, stepsDisallowedByPurpose } =
|
|
357
|
+
usePipelineDraftWarnings(showBinaryOutputPicker)
|
|
389
358
|
|
|
390
359
|
// ---- draft labels ----------------------------------------------------------
|
|
391
360
|
const newLabel = ref('')
|
|
@@ -563,37 +532,16 @@ async function clone(p: Pipeline) {
|
|
|
563
532
|
/>
|
|
564
533
|
</div>
|
|
565
534
|
|
|
535
|
+
<!-- Every draft fault the builder can name, each mirroring a refusal the save boundary
|
|
536
|
+
makes, so the user fixes it before the round trip (`usePipelineDraftWarnings`). -->
|
|
566
537
|
<p
|
|
567
|
-
v-
|
|
568
|
-
|
|
569
|
-
>
|
|
570
|
-
<UIcon name="i-lucide-alert-triangle" class="h-3.5 w-3.5 shrink-0" />
|
|
571
|
-
{{ t('pipeline.builder.gatingNeedsEstimator') }}
|
|
572
|
-
</p>
|
|
573
|
-
|
|
574
|
-
<p
|
|
575
|
-
v-if="skillStepNeedsPick"
|
|
538
|
+
v-for="warning in draftWarnings"
|
|
539
|
+
:key="warning.key"
|
|
576
540
|
class="mb-2 flex items-center gap-1.5 rounded-md border border-amber-800/50 bg-amber-950/30 px-2 py-1 text-[11px] text-amber-300"
|
|
541
|
+
:data-testid="warning.testId"
|
|
577
542
|
>
|
|
578
543
|
<UIcon name="i-lucide-alert-triangle" class="h-3.5 w-3.5 shrink-0" />
|
|
579
|
-
{{ t(
|
|
580
|
-
</p>
|
|
581
|
-
|
|
582
|
-
<p
|
|
583
|
-
v-if="binaryOutputStepNeedsPick"
|
|
584
|
-
class="mb-2 flex items-center gap-1.5 rounded-md border border-amber-800/50 bg-amber-950/30 px-2 py-1 text-[11px] text-amber-300"
|
|
585
|
-
data-testid="binary-output-needs-pick"
|
|
586
|
-
>
|
|
587
|
-
<UIcon name="i-lucide-alert-triangle" class="h-3.5 w-3.5 shrink-0" />
|
|
588
|
-
{{ t('pipeline.builder.binaryOutputNeedsPick') }}
|
|
589
|
-
</p>
|
|
590
|
-
|
|
591
|
-
<p
|
|
592
|
-
v-if="stepsDisallowedByPurpose.length"
|
|
593
|
-
class="mb-2 flex items-center gap-1.5 rounded-md border border-amber-800/50 bg-amber-950/30 px-2 py-1 text-[11px] text-amber-300"
|
|
594
|
-
>
|
|
595
|
-
<UIcon name="i-lucide-alert-triangle" class="h-3.5 w-3.5 shrink-0" />
|
|
596
|
-
{{ t('pipeline.builder.purposeStepsConflict') }}
|
|
544
|
+
{{ t(warning.key) }}
|
|
597
545
|
</p>
|
|
598
546
|
|
|
599
547
|
<div
|
|
@@ -766,6 +714,27 @@ async function clone(p: Pipeline) {
|
|
|
766
714
|
"
|
|
767
715
|
@click="pipelines.toggleDraftAutoRecommend(unit.index)"
|
|
768
716
|
/>
|
|
717
|
+
<!-- Keep the environment this Deployer stands up past the end of the run: the
|
|
718
|
+
preview a reviewer pokes at after the PR is open. Off by default, and the
|
|
719
|
+
save boundary refuses a Deployer that neither reclaims nor declares this,
|
|
720
|
+
so the tick is how an unreclaimed environment says it is deliberate. -->
|
|
721
|
+
<UButton
|
|
722
|
+
v-if="showRetainEnvironmentToggle(unit.kind, unit.index)"
|
|
723
|
+
:icon="
|
|
724
|
+
pipelines.draftRetainEnvironment(unit.index)
|
|
725
|
+
? 'i-lucide-lock'
|
|
726
|
+
: 'i-lucide-cloud-off'
|
|
727
|
+
"
|
|
728
|
+
:color="pipelines.draftRetainEnvironment(unit.index) ? 'warning' : 'neutral'"
|
|
729
|
+
variant="ghost"
|
|
730
|
+
size="xs"
|
|
731
|
+
:title="
|
|
732
|
+
pipelines.draftRetainEnvironment(unit.index)
|
|
733
|
+
? t('pipeline.builder.retainEnvironmentClearTooltip')
|
|
734
|
+
: t('pipeline.builder.retainEnvironmentSetTooltip')
|
|
735
|
+
"
|
|
736
|
+
@click="pipelines.toggleDraftRetainEnvironment(unit.index)"
|
|
737
|
+
/>
|
|
769
738
|
<!-- System prompt: replace what this agent kind ships with, for every run in
|
|
770
739
|
this workspace, with the full revision history to switch back through. -->
|
|
771
740
|
<UButton
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { computed, type ComputedRef } from 'vue'
|
|
2
|
+
import {
|
|
3
|
+
pipelineEnvironmentProblems,
|
|
4
|
+
purposeAllowsAgentCategory,
|
|
5
|
+
type PipelineEnvironmentProblemReason,
|
|
6
|
+
} from '@cat-factory/contracts'
|
|
7
|
+
import type { AgentKind } from '~/types/domain'
|
|
8
|
+
import { agentKindMeta } from '~/utils/catalog'
|
|
9
|
+
import { usePipelinesStore } from '~/stores/pipelines'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Everything the pipeline builder can say is WRONG with the draft in front of it, as an ordered
|
|
13
|
+
* list of translation keys, plus the one finding that also blocks the save.
|
|
14
|
+
*
|
|
15
|
+
* Extracted from `PipelineBuilder.vue` because these grew into one concern wearing five copies of
|
|
16
|
+
* the same coat: each was a `computed` predicate beside a `<p>` with byte-identical classes, so
|
|
17
|
+
* every new rule cost another near-duplicate pair in a component that had run out of room for
|
|
18
|
+
* them. Here they are a list the template renders once.
|
|
19
|
+
*
|
|
20
|
+
* Every entry MIRRORS a refusal the backend already makes at save (or, for the purpose conflict,
|
|
21
|
+
* one only the builder can make, since the backend has no kind→category map). The point is to
|
|
22
|
+
* surface it before the round trip, never to be the sole enforcement: a hint that disagrees with
|
|
23
|
+
* the boundary would let an unsavable draft look clean, so each is derived from the same shared
|
|
24
|
+
* rule the boundary runs rather than from a hand-written restatement of it.
|
|
25
|
+
*/
|
|
26
|
+
export interface PipelineDraftWarnings {
|
|
27
|
+
/** One entry per distinct finding, in the order the builder shows them. */
|
|
28
|
+
hints: ComputedRef<PipelineDraftHint[]>
|
|
29
|
+
/**
|
|
30
|
+
* The draft steps whose agent category the chosen purpose CONTRADICTS. Exposed as the list
|
|
31
|
+
* rather than folded into {@link hints} alone because it is the one finding that also disables
|
|
32
|
+
* the Save button: the builder is the enforcement point, so an empty list is a precondition.
|
|
33
|
+
*/
|
|
34
|
+
stepsDisallowedByPurpose: ComputedRef<AgentKind[]>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** One rendered warning: its i18n key, plus the test id where a spec asserts on it. */
|
|
38
|
+
export interface PipelineDraftHint {
|
|
39
|
+
key: string
|
|
40
|
+
testId?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The environment-lifecycle faults, mapped onto the copy that names the fix for each. An
|
|
45
|
+
* exhaustive `Record` over the reason union, so a fault added to the shared rule fails the build
|
|
46
|
+
* here rather than rendering as a blank hint.
|
|
47
|
+
*/
|
|
48
|
+
const ENVIRONMENT_HINT_KEYS: Record<PipelineEnvironmentProblemReason, string> = {
|
|
49
|
+
consumer_without_deployer: 'pipeline.builder.envNeedsDeployer',
|
|
50
|
+
consumer_after_disposer: 'pipeline.builder.envConsumerAfterDisposer',
|
|
51
|
+
deployer_without_disposer: 'pipeline.builder.envNeedsDisposer',
|
|
52
|
+
disposer_without_deployer: 'pipeline.builder.envDisposerNeedsDeployer',
|
|
53
|
+
retained_deployer_reclaimed: 'pipeline.builder.envRetainedButReclaimed',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function usePipelineDraftWarnings(
|
|
57
|
+
showBinaryOutputPicker: (kind: AgentKind) => boolean,
|
|
58
|
+
): PipelineDraftWarnings {
|
|
59
|
+
const pipelines = usePipelinesStore()
|
|
60
|
+
|
|
61
|
+
const enabled = (i: number) => pipelines.draftEnabled[i] !== false
|
|
62
|
+
|
|
63
|
+
// A gated step with no task-estimator before it (mirrors `assertValidGating`, which rejects the
|
|
64
|
+
// save and the start). Both the step's own estimate gate (`draftGating`) and the Tester QC
|
|
65
|
+
// companion's (`draftTesterQuality[i].gating`) count.
|
|
66
|
+
const gatingNeedsEstimator = computed(() => {
|
|
67
|
+
const kinds = pipelines.draft
|
|
68
|
+
const hasEstimatorBefore = (i: number) =>
|
|
69
|
+
kinds.slice(0, i).some((k, j) => k === 'task-estimator' && enabled(j))
|
|
70
|
+
return kinds.some((_, i) => {
|
|
71
|
+
if (!enabled(i)) return false
|
|
72
|
+
const gated =
|
|
73
|
+
pipelines.draftGating[i]?.enabled || pipelines.draftTesterQuality[i]?.gating?.enabled
|
|
74
|
+
return !!gated && !hasEstimatorBefore(i)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// The environment lifecycle a draft has to spell out: provision (Deployer) → consume (a tester /
|
|
79
|
+
// acceptance / human-test step) → reclaim (Disposer, or a Deployer that declares its environment
|
|
80
|
+
// outlives the run). One message per DISTINCT fault, so a draft missing both a Deployer and a
|
|
81
|
+
// Disposer says so at once instead of over two rejected saves.
|
|
82
|
+
const environmentHintKeys = computed(() => {
|
|
83
|
+
const problems = pipelineEnvironmentProblems(
|
|
84
|
+
pipelines.draft,
|
|
85
|
+
pipelines.draftEnabled,
|
|
86
|
+
pipelines.draftStepOptions,
|
|
87
|
+
)
|
|
88
|
+
return [...new Set(problems.map((p) => ENVIRONMENT_HINT_KEYS[p.reason]))]
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// An enabled `skill` step with no picked skill (mirrors `assertValidSkillSteps`).
|
|
92
|
+
const skillStepNeedsPick = computed(() =>
|
|
93
|
+
pipelines.draft.some((k, i) => k === 'skill' && enabled(i) && !pipelines.draftSkillId(i)),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
// An enabled generator step with no storage selection (mirrors `assertValidBinaryOutputSteps`).
|
|
97
|
+
// Same disposition as `skillStepNeedsPick`: both are a step parametrized by a selection it
|
|
98
|
+
// cannot run without.
|
|
99
|
+
const binaryOutputStepNeedsPick = computed(() =>
|
|
100
|
+
pipelines.draft.some(
|
|
101
|
+
(kind, i) =>
|
|
102
|
+
showBinaryOutputPicker(kind) &&
|
|
103
|
+
enabled(i) &&
|
|
104
|
+
!pipelines.draftBinaryOutput(i)?.storageServiceId,
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
// Steps whose agent category the chosen purpose CONTRADICTS (a non-`build` purpose writes no
|
|
109
|
+
// code and runs no tests, so the Implementation/Testing categories are disallowed). Only
|
|
110
|
+
// reachable by switching an existing draft to a non-`build` purpose AFTER such steps were added,
|
|
111
|
+
// since the palette offers neither.
|
|
112
|
+
//
|
|
113
|
+
// Deliberately the COMPATIBILITY predicate, not the palette's narrower relevance one: a purpose
|
|
114
|
+
// that merely stops SUGGESTING a category must not turn a pipeline somebody already built into
|
|
115
|
+
// one they cannot save.
|
|
116
|
+
const stepsDisallowedByPurpose = computed(() =>
|
|
117
|
+
pipelines.draft.filter((kind) => {
|
|
118
|
+
const category = agentKindMeta(kind).category
|
|
119
|
+
return !!category && !purposeAllowsAgentCategory(pipelines.draftPurpose, category)
|
|
120
|
+
}),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
const hints = computed<PipelineDraftHint[]>(() => [
|
|
124
|
+
...(gatingNeedsEstimator.value ? [{ key: 'pipeline.builder.gatingNeedsEstimator' }] : []),
|
|
125
|
+
...environmentHintKeys.value.map((key) => ({ key, testId: 'env-lifecycle-hint' })),
|
|
126
|
+
...(skillStepNeedsPick.value ? [{ key: 'pipeline.builder.skillNeedsPick' }] : []),
|
|
127
|
+
...(binaryOutputStepNeedsPick.value
|
|
128
|
+
? [{ key: 'pipeline.builder.binaryOutputNeedsPick', testId: 'binary-output-needs-pick' }]
|
|
129
|
+
: []),
|
|
130
|
+
...(stepsDisallowedByPurpose.value.length
|
|
131
|
+
? [{ key: 'pipeline.builder.purposeStepsConflict' }]
|
|
132
|
+
: []),
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
return { hints, stepsDisallowedByPurpose }
|
|
136
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { pipelineEnvironmentProblems } from '@cat-factory/contracts'
|
|
3
|
+
import { usePipelinesStore } from '~/stores/pipelines'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The pipeline-builder draft's retain declaration (`stepOptions.retainEnvironment`): how an author
|
|
7
|
+
* says a Deployer's environment is MEANT to outlive the run, which is the only savable form of
|
|
8
|
+
* "deploy a preview and leave it up" (the reclaim rule refuses a Deployer that neither reclaims
|
|
9
|
+
* nor declares this). Same contract as the option helpers beside it: merge into the bag, normalize
|
|
10
|
+
* an emptied bag back to null.
|
|
11
|
+
*/
|
|
12
|
+
describe('pipelines store — per-step retain-environment declaration', () => {
|
|
13
|
+
it('stores only the opt-in, and normalizes the bag away when it is cleared', () => {
|
|
14
|
+
const pipelines = usePipelinesStore()
|
|
15
|
+
pipelines.addToDraft('deployer')
|
|
16
|
+
// OFF by default, and stored as ABSENCE rather than `false`: reclaiming is the default, so a
|
|
17
|
+
// step that never touched this persists exactly the shape it always did.
|
|
18
|
+
expect(pipelines.draftRetainEnvironment(0)).toBe(false)
|
|
19
|
+
expect(pipelines.draftStepOptions[0]).toBeNull()
|
|
20
|
+
|
|
21
|
+
pipelines.toggleDraftRetainEnvironment(0)
|
|
22
|
+
expect(pipelines.draftRetainEnvironment(0)).toBe(true)
|
|
23
|
+
expect(pipelines.draftStepOptions[0]).toEqual({ retainEnvironment: true })
|
|
24
|
+
|
|
25
|
+
pipelines.toggleDraftRetainEnvironment(0)
|
|
26
|
+
expect(pipelines.draftRetainEnvironment(0)).toBe(false)
|
|
27
|
+
expect(pipelines.draftStepOptions[0]).toBeNull()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('merges with the other options on the same step rather than clobbering them', () => {
|
|
31
|
+
const pipelines = usePipelinesStore()
|
|
32
|
+
pipelines.addToDraft('deployer')
|
|
33
|
+
pipelines.setDraftAgentVariantId(0, 'acme:fast')
|
|
34
|
+
|
|
35
|
+
pipelines.toggleDraftRetainEnvironment(0)
|
|
36
|
+
expect(pipelines.draftStepOptions[0]).toEqual({
|
|
37
|
+
agentVariantId: 'acme:fast',
|
|
38
|
+
retainEnvironment: true,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
pipelines.toggleDraftRetainEnvironment(0)
|
|
42
|
+
expect(pipelines.draftStepOptions[0]).toEqual({ agentVariantId: 'acme:fast' })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('turns a draft the save boundary refuses into one it accepts', () => {
|
|
46
|
+
// The point of the whole field, asserted against the SAME rule the backend refuses on rather
|
|
47
|
+
// than against a restatement of it: without the declaration this draft has no savable form,
|
|
48
|
+
// since dropping the Deployer instead just moves the fault onto the tester.
|
|
49
|
+
const pipelines = usePipelinesStore()
|
|
50
|
+
for (const kind of ['coder', 'deployer', 'human-test'] as const) pipelines.addToDraft(kind)
|
|
51
|
+
const problems = () =>
|
|
52
|
+
pipelineEnvironmentProblems(
|
|
53
|
+
pipelines.draft,
|
|
54
|
+
pipelines.draftEnabled,
|
|
55
|
+
pipelines.draftStepOptions,
|
|
56
|
+
).map((p) => p.reason)
|
|
57
|
+
|
|
58
|
+
expect(problems()).toEqual(['deployer_without_disposer'])
|
|
59
|
+
pipelines.toggleDraftRetainEnvironment(1)
|
|
60
|
+
expect(problems()).toEqual([])
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -15,6 +15,27 @@ import { defaultConsensusConfig, type PipelinesContext } from './context'
|
|
|
15
15
|
* touches nothing else, which is what makes the two independent; both are spread into the store,
|
|
16
16
|
* so the store's API is unchanged.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Write ONE field of the step's `StepOptions` bag at `index`, merging into whatever else that step
|
|
20
|
+
* carries and normalizing an emptied bag back to `null`. `undefined` CLEARS the field.
|
|
21
|
+
*
|
|
22
|
+
* Every per-step option below goes through this rather than repeating the clone/assign/normalize
|
|
23
|
+
* dance, because the two halves that are easy to get wrong are shared by all of them: replacing
|
|
24
|
+
* the bag loses the neighbouring options a step may also carry, and leaving a `{}` behind makes a
|
|
25
|
+
* step that is back on every default persist a shape it never had.
|
|
26
|
+
*/
|
|
27
|
+
function patchStepOption<K extends keyof StepOptions>(
|
|
28
|
+
draftStepOptions: PipelinesContext['draftStepOptions'],
|
|
29
|
+
index: number,
|
|
30
|
+
key: K,
|
|
31
|
+
value: StepOptions[K] | undefined,
|
|
32
|
+
) {
|
|
33
|
+
const next: StepOptions = { ...draftStepOptions.value[index] }
|
|
34
|
+
if (value === undefined) delete next[key]
|
|
35
|
+
else next[key] = value
|
|
36
|
+
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
19
40
|
const {
|
|
20
41
|
draftGates,
|
|
@@ -118,10 +139,27 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
118
139
|
* flag. Merges with any other future StepOptions fields rather than clobbering the whole bag.
|
|
119
140
|
*/
|
|
120
141
|
function toggleDraftAutoRecommend(index: number) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
142
|
+
const off = draftAutoRecommendEnabled(index) ? false : undefined
|
|
143
|
+
patchStepOption(draftStepOptions, index, 'autoRecommend', off)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Whether the draft `deployer` step at `index` declares that its environments outlive the run
|
|
148
|
+
* (its `stepOptions.retainEnvironment`). OFF by default: the everyday shape is a run that
|
|
149
|
+
* reclaims what it stood up, and the save boundary refuses a Deployer that does neither.
|
|
150
|
+
*/
|
|
151
|
+
function draftRetainEnvironment(index: number): boolean {
|
|
152
|
+
return draftStepOptions.value[index]?.retainEnvironment === true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Toggle the retain declaration on the draft `deployer` step at `index`. It is off by default,
|
|
157
|
+
* so we store ONLY the opt-in (the mirror of `toggleDraftAutoRecommend`, which stores only the
|
|
158
|
+
* opt-out); toggling back drops the flag and, if the bag empties, the whole entry.
|
|
159
|
+
*/
|
|
160
|
+
function toggleDraftRetainEnvironment(index: number) {
|
|
161
|
+
const on = draftRetainEnvironment(index) ? undefined : true
|
|
162
|
+
patchStepOption(draftStepOptions, index, 'retainEnvironment', on)
|
|
125
163
|
}
|
|
126
164
|
|
|
127
165
|
/** The skill picked for the draft `skill` step at `index` (its `stepOptions.skillId`). */
|
|
@@ -135,10 +173,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
135
173
|
* bag empties, the whole entry (so it normalizes away like the other options).
|
|
136
174
|
*/
|
|
137
175
|
function setDraftSkillId(index: number, skillId: string | undefined) {
|
|
138
|
-
|
|
139
|
-
if (skillId) next.skillId = skillId
|
|
140
|
-
else delete next.skillId
|
|
141
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
176
|
+
patchStepOption(draftStepOptions, index, 'skillId', skillId || undefined)
|
|
142
177
|
}
|
|
143
178
|
|
|
144
179
|
/**
|
|
@@ -156,10 +191,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
156
191
|
* shipped prompt persists nothing.
|
|
157
192
|
*/
|
|
158
193
|
function setDraftAgentVariantId(index: number, agentVariantId: string | undefined) {
|
|
159
|
-
|
|
160
|
-
if (agentVariantId) next.agentVariantId = agentVariantId
|
|
161
|
-
else delete next.agentVariantId
|
|
162
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
194
|
+
patchStepOption(draftStepOptions, index, 'agentVariantId', agentVariantId || undefined)
|
|
163
195
|
}
|
|
164
196
|
|
|
165
197
|
/**
|
|
@@ -188,17 +220,16 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
188
220
|
* wrong thing.
|
|
189
221
|
*/
|
|
190
222
|
function setDraftBinaryOutput(index: number, config: BinaryOutputConfig | undefined) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
223
|
+
const { storageServiceId, contextServiceIds, generatorIds, modalities } = config ?? {}
|
|
224
|
+
const selection = storageServiceId
|
|
225
|
+
? {
|
|
226
|
+
storageServiceId,
|
|
227
|
+
...(contextServiceIds?.length ? { contextServiceIds } : {}),
|
|
228
|
+
...(generatorIds?.length ? { generatorIds } : {}),
|
|
229
|
+
...(modalities?.length ? { modalities } : {}),
|
|
230
|
+
}
|
|
231
|
+
: undefined
|
|
232
|
+
patchStepOption(draftStepOptions, index, 'binaryOutput', selection)
|
|
202
233
|
}
|
|
203
234
|
|
|
204
235
|
/**
|
|
@@ -216,10 +247,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
216
247
|
* other fields here.
|
|
217
248
|
*/
|
|
218
249
|
function setDraftMaxOutputTokens(index: number, maxOutputTokens: number | undefined) {
|
|
219
|
-
|
|
220
|
-
if (maxOutputTokens != null) next.maxOutputTokens = maxOutputTokens
|
|
221
|
-
else delete next.maxOutputTokens
|
|
222
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
250
|
+
patchStepOption(draftStepOptions, index, 'maxOutputTokens', maxOutputTokens ?? undefined)
|
|
223
251
|
}
|
|
224
252
|
|
|
225
253
|
return {
|
|
@@ -234,6 +262,8 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
234
262
|
toggleDraftEnabled,
|
|
235
263
|
draftAutoRecommendEnabled,
|
|
236
264
|
toggleDraftAutoRecommend,
|
|
265
|
+
draftRetainEnvironment,
|
|
266
|
+
toggleDraftRetainEnvironment,
|
|
237
267
|
draftSkillId,
|
|
238
268
|
setDraftSkillId,
|
|
239
269
|
draftAgentVariantId,
|
package/app/utils/catalog.ts
CHANGED
|
@@ -210,12 +210,14 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
// Provisions the ephemeral environment the tester / human-test / playwright steps read, which
|
|
213
|
-
// is why it leads the testing group.
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
213
|
+
// is why it leads the testing group.
|
|
214
|
+
//
|
|
215
|
+
// `basic`, and it has to be: a pipeline that reaches an env consumer with no Deployer in
|
|
216
|
+
// front of it is refused at SAVE (`validatePipelineAuthoring`), and the API Tester it serves
|
|
217
|
+
// is itself `basic`. Leaving the Deployer out of the basic palette would leave a basic-mode
|
|
218
|
+
// user composing a pipeline they cannot save and cannot see the fix for.
|
|
217
219
|
kind: 'deployer',
|
|
218
|
-
tier: '
|
|
220
|
+
tier: 'basic',
|
|
219
221
|
label: 'Deployer',
|
|
220
222
|
icon: 'i-lucide-cloud-upload',
|
|
221
223
|
color: '#34d399',
|
|
@@ -276,8 +278,11 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
276
278
|
// is the point of it: after the automated tester, or after a human has finished with the live
|
|
277
279
|
// URL. Without one, the TTL sweep reclaims environments on a timer long after the run
|
|
278
280
|
// settled, which is a fine backstop and cannot close the run's own teardown proof.
|
|
281
|
+
//
|
|
282
|
+
// `basic` for the same reason the Deployer is: a chain that deploys and never reclaims is
|
|
283
|
+
// refused at save, so the fix has to be reachable wherever the fault can be composed.
|
|
279
284
|
kind: 'disposer',
|
|
280
|
-
tier: '
|
|
285
|
+
tier: 'basic',
|
|
281
286
|
label: 'Disposer',
|
|
282
287
|
icon: 'i-lucide-cloud-off',
|
|
283
288
|
color: '#34d399',
|
package/i18n/locales/de.json
CHANGED
|
@@ -4293,6 +4293,13 @@
|
|
|
4293
4293
|
"binaryOutputPlaceholder": "Speicherdienst wählen",
|
|
4294
4294
|
"binaryOutputContextPlaceholder": "Optional: Dienste, die den Umfang bestimmen",
|
|
4295
4295
|
"binaryOutputNeedsPick": "Für einen Schritt, der binäre Ausgaben erzeugt, ist kein Speicherdienst gewählt. Wähle einen vor dem Speichern.",
|
|
4296
|
+
"envNeedsDeployer": "Vor einem Tester-, manuellen Test- oder Playwright-Schritt wird ein Deployer benötigt. Füge einen hinzu, sonst lässt sich die Pipeline nicht speichern (bei einem Service ohne Provisionierung bleibt er wirkungslos).",
|
|
4297
|
+
"envNeedsDisposer": "Nach einem Deployer wird ein Disposer benötigt, der die bereitgestellte Umgebung wieder freigibt. Füge einen hinzu oder markiere den Deployer so, dass er seine Umgebung über den Lauf hinaus behält, sonst lässt sich die Pipeline nicht speichern.",
|
|
4298
|
+
"envDisposerNeedsDeployer": "Vor diesem Disposer steht kein Deployer, es gibt also nichts freizugeben. Füge einen Deployer hinzu oder entferne den Disposer.",
|
|
4299
|
+
"envConsumerAfterDisposer": "Ein Tester-, manueller Test- oder Playwright-Schritt läuft erst, nachdem der Disposer die Umgebung bereits freigegeben hat, es bliebe also nichts übrig, wogegen er laufen könnte. Verschiebe den Disposer hinter diesen Schritt oder füge davor einen weiteren Deployer ein.",
|
|
4300
|
+
"envRetainedButReclaimed": "Dieser Deployer ist so markiert, dass er seine Umgebung über den Lauf hinaus behält, aber ein nachfolgender Disposer gibt genau diese Umgebung frei. Entferne den Disposer oder hebe die Markierung auf.",
|
|
4301
|
+
"retainEnvironmentSetTooltip": "Wird am Ende des Laufs freigegeben. Klicke, um diese Umgebung danach weiterlaufen zu lassen (eine Vorschau, die Prüfende nach dem Öffnen des PR nutzen); die TTL oder ein Betreiber fährt sie dann herunter.",
|
|
4302
|
+
"retainEnvironmentClearTooltip": "Läuft nach dem Ende des Laufs weiter. Klicke, um sie wieder per Disposer-Schritt freizugeben.",
|
|
4296
4303
|
"binaryOutputNoStorage": "Kein Dienst im Katalog dieses Boards deklariert die Fähigkeit {capability}. Registriere einen, oder ergänze die Fähigkeit beim gemeinten Dienst unter den grundlegenden Diensten.",
|
|
4297
4304
|
"binaryOutputMissing": "Dieser Speicherdienst ist nicht mehr im Katalog; wähle einen anderen.",
|
|
4298
4305
|
"binaryOutputNotStorage": "Dieser Dienst deklariert die Fähigkeit {capability} nicht mehr, deshalb werden Läufe abgelehnt; wähle einen anderen.",
|
package/i18n/locales/en.json
CHANGED
|
@@ -4881,6 +4881,13 @@
|
|
|
4881
4881
|
"binaryOutputPlaceholder": "Pick a storage service",
|
|
4882
4882
|
"binaryOutputContextPlaceholder": "Optional: services that scope the generation",
|
|
4883
4883
|
"binaryOutputNeedsPick": "A step that generates binary outputs has no storage service selected. Pick one before saving.",
|
|
4884
|
+
"envNeedsDeployer": "A Tester, human-test or Playwright step needs a Deployer before it. Add one or the pipeline won't save (it is a no-op on a service that provisions nothing).",
|
|
4885
|
+
"envNeedsDisposer": "A Deployer needs a Disposer after it to reclaim the environment it stands up. Add one, or mark the Deployer as keeping its environment past the run, otherwise the pipeline won't save.",
|
|
4886
|
+
"envDisposerNeedsDeployer": "This Disposer has no Deployer before it, so there is nothing for it to reclaim. Add a Deployer, or remove the Disposer.",
|
|
4887
|
+
"envConsumerAfterDisposer": "A Tester, human-test or Playwright step runs after the Disposer has already reclaimed the environment, so nothing would be left to run against. Move the Disposer below that step, or add another Deployer before it.",
|
|
4888
|
+
"envRetainedButReclaimed": "This Deployer is marked as keeping its environment past the run, but a Disposer after it reclaims exactly that environment. Remove the Disposer, or clear the keep-environment setting.",
|
|
4889
|
+
"retainEnvironmentSetTooltip": "Reclaimed at the end of the run. Click to keep this environment running afterwards (a preview reviewers use once the PR is open); its TTL or an operator then takes it down.",
|
|
4890
|
+
"retainEnvironmentClearTooltip": "Kept running after the run ends. Click to go back to reclaiming it with a Disposer step.",
|
|
4884
4891
|
"binaryOutputNoStorage": "No service in this board's catalog declares the {capability} capability. Register one, or add the capability to the service you meant, under foundational services.",
|
|
4885
4892
|
"binaryOutputMissing": "This storage service is no longer in the catalog; pick another.",
|
|
4886
4893
|
"binaryOutputNotStorage": "This service no longer declares the {capability} capability, so runs will be refused; pick another.",
|
package/i18n/locales/es.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Elige un servicio de almacenamiento",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Opcional: servicios que delimitan la generación",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Un paso que genera salidas binarias no tiene servicio de almacenamiento elegido. Elige uno antes de guardar.",
|
|
4735
|
+
"envNeedsDeployer": "Un paso de Tester, prueba manual o Playwright necesita un Deployer antes. Añade uno o la canalización no se guardará (no hace nada en un servicio que no aprovisiona).",
|
|
4736
|
+
"envNeedsDisposer": "Un Deployer necesita un Disposer después para liberar el entorno que levanta. Añade uno, o marca el Deployer para que conserve su entorno más allá de la ejecución; de lo contrario la canalización no se guardará.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Este Disposer no tiene ningún Deployer antes, así que no hay nada que liberar. Añade un Deployer o quita el Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Un paso de Tester, prueba manual o Playwright se ejecuta después de que el Disposer ya haya liberado el entorno, así que no quedaría nada contra lo que ejecutarlo. Mueve el Disposer por debajo de ese paso, o añade otro Deployer antes.",
|
|
4739
|
+
"envRetainedButReclaimed": "Este Deployer está marcado para conservar su entorno más allá de la ejecución, pero un Disposer posterior libera exactamente ese entorno. Quita el Disposer o desmarca la opción de conservar el entorno.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Se libera al terminar la ejecución. Haz clic para mantener este entorno en marcha después (una vista previa que los revisores usan con el PR abierto); luego lo retirará su TTL o un operador.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Se mantiene en marcha tras la ejecución. Haz clic para volver a liberarlo con un paso Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Ningún servicio del catálogo de este tablero declara la capacidad {capability}. Registra uno, o añade la capacidad al servicio que tenías en mente, en servicios fundamentales.",
|
|
4736
4743
|
"binaryOutputMissing": "Este servicio de almacenamiento ya no está en el catálogo; elige otro.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Este servicio ya no declara la capacidad {capability}, así que las ejecuciones se rechazarán; elige otro.",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Choisir un service de stockage",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Facultatif : services qui délimitent la génération",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Une étape qui génère des sorties binaires n'a aucun service de stockage choisi. Choisissez-en un avant d'enregistrer.",
|
|
4735
|
+
"envNeedsDeployer": "Une étape Tester, de test manuel ou Playwright a besoin d'un Deployer avant elle. Ajoutez-en un, sinon le pipeline ne sera pas enregistré (il ne fait rien sur un service qui ne provisionne rien).",
|
|
4736
|
+
"envNeedsDisposer": "Un Deployer a besoin d'un Disposer après lui pour libérer l'environnement qu'il provisionne. Ajoutez-en un, ou marquez le Deployer comme conservant son environnement au-delà de l'exécution, sinon le pipeline ne sera pas enregistré.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Ce Disposer n'a aucun Deployer avant lui, il n'a donc rien à libérer. Ajoutez un Deployer ou retirez le Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Une étape Tester, de test manuel ou Playwright s'exécute après que le Disposer a déjà libéré l'environnement : il ne resterait rien pour l'exécuter. Déplacez le Disposer après cette étape, ou ajoutez un autre Deployer avant elle.",
|
|
4739
|
+
"envRetainedButReclaimed": "Ce Deployer est marqué comme conservant son environnement au-delà de l'exécution, mais un Disposer placé après lui libère précisément cet environnement. Retirez le Disposer, ou désactivez la conservation de l'environnement.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Libéré à la fin de l'exécution. Cliquez pour laisser cet environnement actif ensuite (un aperçu que les relecteurs utilisent une fois la PR ouverte) ; son TTL ou un opérateur l'arrêtera.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Reste actif après la fin de l'exécution. Cliquez pour le libérer de nouveau via une étape Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Aucun service du catalogue de ce tableau ne déclare la capacité {capability}. Enregistrez-en un, ou ajoutez la capacité au service que vous visiez, dans les services fondamentaux.",
|
|
4736
4743
|
"binaryOutputMissing": "Ce service de stockage n'est plus dans le catalogue ; choisissez-en un autre.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Ce service ne déclare plus la capacité {capability}, les exécutions seront donc refusées ; choisissez-en un autre.",
|
package/i18n/locales/he.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "בחר שירות אחסון",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "לא חובה: שירותים שמגדירים את היקף היצירה",
|
|
4734
4734
|
"binaryOutputNeedsPick": "לשלב שמייצר פלטים בינאריים לא נבחר שירות אחסון. בחר אחד לפני השמירה.",
|
|
4735
|
+
"envNeedsDeployer": "שלב Tester, בדיקה ידנית או Playwright דורש Deployer לפניו. הוסף אחד, אחרת הצינור לא יישמר (בשירות שאינו מקצה סביבה הוא לא עושה דבר).",
|
|
4736
|
+
"envNeedsDisposer": "אחרי Deployer נדרש Disposer שישחרר את הסביבה שהוקצתה. הוסף אחד, או סמן את ה-Deployer כשומר על הסביבה שלו גם אחרי הריצה, אחרת הצינור לא יישמר.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "לפני ה-Disposer הזה אין Deployer, ולכן אין מה לשחרר. הוסף Deployer או הסר את ה-Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "שלב Tester, בדיקה ידנית או Playwright רץ אחרי שה-Disposer כבר שחרר את הסביבה, ולכן לא יישאר דבר להריץ מולו. העבר את ה-Disposer מתחת לשלב הזה, או הוסף Deployer נוסף לפניו.",
|
|
4739
|
+
"envRetainedButReclaimed": "ה-Deployer הזה מסומן כשומר על הסביבה שלו גם אחרי הריצה, אך Disposer שאחריו משחרר בדיוק את אותה סביבה. הסר את ה-Disposer, או בטל את סימון שמירת הסביבה.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "משוחררת בסוף הריצה. לחץ כדי להשאיר את הסביבה פעילה אחריה (תצוגה מקדימה שסוקרים משתמשים בה אחרי פתיחת ה-PR); ה-TTL או מפעיל יורידו אותה בהמשך.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "נשארת פעילה אחרי סיום הריצה. לחץ כדי לחזור לשחרור שלה בשלב Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "אף שירות בקטלוג של לוח זה אינו מצהיר על יכולת {capability}. רשום שירות כזה, או הוסף את היכולת לשירות שהתכוונת אליו, תחת שירותי בסיס.",
|
|
4736
4743
|
"binaryOutputMissing": "שירות אחסון זה כבר אינו בקטלוג; בחר אחר.",
|
|
4737
4744
|
"binaryOutputNotStorage": "השירות הזה כבר אינו מצהיר על יכולת {capability}, ולכן הרצות יידחו; בחר אחר.",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4293,6 +4293,13 @@
|
|
|
4293
4293
|
"binaryOutputPlaceholder": "Scegli un servizio di archiviazione",
|
|
4294
4294
|
"binaryOutputContextPlaceholder": "Facoltativo: servizi che delimitano la generazione",
|
|
4295
4295
|
"binaryOutputNeedsPick": "Un passo che genera output binari non ha un servizio di archiviazione scelto. Scegline uno prima di salvare.",
|
|
4296
|
+
"envNeedsDeployer": "Un passaggio Tester, di test manuale o Playwright ha bisogno di un Deployer prima di sé. Aggiungine uno, altrimenti la pipeline non verrà salvata (su un servizio che non effettua provisioning non fa nulla).",
|
|
4297
|
+
"envNeedsDisposer": "Un Deployer ha bisogno di un Disposer dopo di sé, che liberi l'ambiente creato. Aggiungine uno, oppure contrassegna il Deployer come tale da mantenere il proprio ambiente oltre l'esecuzione, altrimenti la pipeline non verrà salvata.",
|
|
4298
|
+
"envDisposerNeedsDeployer": "Questo Disposer non ha alcun Deployer prima di sé, quindi non ha nulla da liberare. Aggiungi un Deployer oppure rimuovi il Disposer.",
|
|
4299
|
+
"envConsumerAfterDisposer": "Un passaggio Tester, di test manuale o Playwright viene eseguito dopo che il Disposer ha già liberato l'ambiente, quindi non resterebbe nulla su cui eseguirlo. Sposta il Disposer dopo quel passaggio, oppure aggiungi un altro Deployer prima di esso.",
|
|
4300
|
+
"envRetainedButReclaimed": "Questo Deployer è contrassegnato come tale da mantenere il proprio ambiente oltre l'esecuzione, ma un Disposer successivo libera proprio quell'ambiente. Rimuovi il Disposer, oppure togli il contrassegno di mantenimento dell'ambiente.",
|
|
4301
|
+
"retainEnvironmentSetTooltip": "Liberato al termine dell'esecuzione. Fai clic per lasciare questo ambiente attivo dopo (un'anteprima che i revisori usano a PR aperta); lo spegneranno poi il suo TTL o un operatore.",
|
|
4302
|
+
"retainEnvironmentClearTooltip": "Resta attivo dopo la fine dell'esecuzione. Fai clic per tornare a liberarlo con un passaggio Disposer.",
|
|
4296
4303
|
"binaryOutputNoStorage": "Nessun servizio nel catalogo di questa board dichiara la capacità {capability}. Registrane uno, oppure aggiungi la capacità al servizio che intendevi, nei servizi fondamentali.",
|
|
4297
4304
|
"binaryOutputMissing": "Questo servizio di archiviazione non è più nel catalogo; scegline un altro.",
|
|
4298
4305
|
"binaryOutputNotStorage": "Questo servizio non dichiara più la capacità {capability}, quindi le esecuzioni verranno rifiutate; scegline un altro.",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "保存サービスを選択",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "任意: 生成の範囲を定めるサービス",
|
|
4734
4734
|
"binaryOutputNeedsPick": "バイナリ成果物を生成するステップに保存サービスが選択されていません。保存前に選んでください。",
|
|
4735
|
+
"envNeedsDeployer": "Tester、手動テスト、または Playwright のステップの前には Deployer が必要です。追加しないとパイプラインは保存できません(プロビジョニングしないサービスでは何もしません)。",
|
|
4736
|
+
"envNeedsDisposer": "Deployer の後には、用意した環境を解放する Disposer が必要です。追加するか、Deployer に実行後も環境を残す設定を付けてください。どちらもない場合、パイプラインは保存できません。",
|
|
4737
|
+
"envDisposerNeedsDeployer": "この Disposer の前に Deployer がないため、解放する対象がありません。Deployer を追加するか、Disposer を削除してください。",
|
|
4738
|
+
"envConsumerAfterDisposer": "Tester、手動テスト、または Playwright のステップが、Disposer が環境を解放した後に実行されるため、実行対象が残りません。Disposer をそのステップより後ろに移すか、その前にもう一つ Deployer を追加してください。",
|
|
4739
|
+
"envRetainedButReclaimed": "この Deployer には実行後も環境を残す設定が付いていますが、後続の Disposer がまさにその環境を解放します。Disposer を削除するか、環境を残す設定を解除してください。",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "実行の終了時に解放されます。クリックすると実行後も環境を残します(PR を開いた後にレビュアーが使うプレビュー用)。その後は TTL または運用者が停止します。",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "実行の終了後も環境を残します。クリックすると Disposer ステップで解放する動作に戻ります。",
|
|
4735
4742
|
"binaryOutputNoStorage": "このボードのカタログに {capability} 機能を宣言するサービスがありません。基盤サービスで新たに登録するか、意図したサービスにこの機能を追加してください。",
|
|
4736
4743
|
"binaryOutputMissing": "この保存サービスはカタログに存在しません。別のサービスを選んでください。",
|
|
4737
4744
|
"binaryOutputNotStorage": "このサービスは {capability} 機能を宣言しなくなったため、実行は拒否されます。別のサービスを選んでください。",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Wybierz usługę przechowywania",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Opcjonalnie: usługi wyznaczające zakres generowania",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Krok generujący wyjścia binarne nie ma wybranej usługi przechowywania. Wybierz ją przed zapisaniem.",
|
|
4735
|
+
"envNeedsDeployer": "Krok Tester, testu manualnego lub Playwright wymaga Deployera przed sobą. Dodaj go, inaczej potok nie zostanie zapisany (przy usłudze bez provisioningu nic nie robi).",
|
|
4736
|
+
"envNeedsDisposer": "Deployer wymaga Disposera po sobie, który zwolni utworzone środowisko. Dodaj go albo oznacz Deployera jako zachowującego środowisko po zakończeniu przebiegu, inaczej potok nie zostanie zapisany.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Przed tym Disposerem nie ma Deployera, więc nie ma czego zwalniać. Dodaj Deployera albo usuń Disposera.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Krok Tester, testu manualnego lub Playwright wykonuje się po tym, jak Disposer zwolnił już środowisko, więc nie zostanie nic, na czym mógłby działać. Przesuń Disposera poniżej tego kroku albo dodaj przed nim kolejnego Deployera.",
|
|
4739
|
+
"envRetainedButReclaimed": "Ten Deployer jest oznaczony jako zachowujący środowisko po zakończeniu przebiegu, ale Disposer za nim zwalnia dokładnie to środowisko. Usuń Disposera albo wyłącz zachowywanie środowiska.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Zwalniane na koniec przebiegu. Kliknij, aby zostawić to środowisko działające po jego zakończeniu (podgląd, z którego recenzenci korzystają po otwarciu PR); później wyłączy je TTL lub operator.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Działa po zakończeniu przebiegu. Kliknij, aby wrócić do zwalniania go krokiem Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Żadna usługa w katalogu tej tablicy nie deklaruje możliwości {capability}. Zarejestruj taką usługę albo dodaj tę możliwość właściwej usłudze w usługach podstawowych.",
|
|
4736
4743
|
"binaryOutputMissing": "Tej usługi przechowywania nie ma już w katalogu; wybierz inną.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Ta usługa nie deklaruje już możliwości {capability}, więc uruchomienia będą odrzucane; wybierz inną.",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Bir depolama hizmeti seç",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "İsteğe bağlı: üretimin kapsamını belirleyen hizmetler",
|
|
4734
4734
|
"binaryOutputNeedsPick": "İkili çıktı üreten bir adımda depolama hizmeti seçilmemiş. Kaydetmeden önce birini seç.",
|
|
4735
|
+
"envNeedsDeployer": "Tester, manuel test veya Playwright adımının öncesinde bir Deployer gerekir. Bir tane ekle, yoksa işlem hattı kaydedilmez (hiçbir şey sağlamayan bir hizmette etkisizdir).",
|
|
4736
|
+
"envNeedsDisposer": "Bir Deployer'ın ardından, oluşturduğu ortamı geri alan bir Disposer gerekir. Bir tane ekle ya da Deployer'ı, ortamını çalıştırma sonrasında da koruyacak biçimde işaretle; aksi hâlde işlem hattı kaydedilmez.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Bu Disposer'dan önce bir Deployer yok, dolayısıyla geri alınacak bir şey de yok. Bir Deployer ekle ya da Disposer'ı kaldır.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Tester, manuel test veya Playwright adımı, Disposer ortamı geri aldıktan sonra çalışıyor; dolayısıyla üzerinde çalışılacak bir şey kalmaz. Disposer'ı bu adımın altına taşı ya da öncesine bir Deployer daha ekle.",
|
|
4739
|
+
"envRetainedButReclaimed": "Bu Deployer, ortamını çalıştırma sonrasında da koruyacak biçimde işaretli, ancak ardındaki bir Disposer tam olarak o ortamı geri alıyor. Disposer'ı kaldır ya da ortamı koruma işaretini temizle.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Çalıştırmanın sonunda geri alınır. Bu ortamı sonrasında da açık bırakmak için tıkla (PR açıldıktan sonra inceleyicilerin kullandığı bir önizleme); ardından TTL'i ya da bir operatör kapatır.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Çalıştırma bittikten sonra da açık kalır. Disposer adımıyla geri almaya dönmek için tıkla.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Bu panonun katalogundaki hiçbir hizmet {capability} yeteneğini bildirmiyor. Temel hizmetler altında bir hizmet kaydet ya da kastettiğin hizmete bu yeteneği ekle.",
|
|
4736
4743
|
"binaryOutputMissing": "Bu depolama hizmeti artık katalogda yok; başka birini seç.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Bu hizmet artık {capability} yeteneğini bildirmiyor, bu yüzden çalıştırmalar reddedilecek; başka birini seç.",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Оберіть службу зберігання",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Необовʼязково: служби, які визначають межі генерації",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Для кроку, який генерує бінарні результати, не обрано службу зберігання. Оберіть її перед збереженням.",
|
|
4735
|
+
"envNeedsDeployer": "Крок Tester, ручного тесту або Playwright потребує Deployer перед собою. Додай його, інакше конвеєр не збережеться (для служби без провізіювання він нічого не робить).",
|
|
4736
|
+
"envNeedsDisposer": "Після Deployer потрібен Disposer, який звільнить створене середовище. Додай його або познач Deployer як такий, що зберігає своє середовище після завершення прогону, інакше конвеєр не збережеться.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Перед цим Disposer немає Deployer, тож звільняти нічого. Додай Deployer або прибери Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Крок Tester, ручного тесту або Playwright виконується після того, як Disposer уже звільнив середовище, тож не залишиться нічого, на чому його запускати. Перемісти Disposer нижче за цей крок або додай ще один Deployer перед ним.",
|
|
4739
|
+
"envRetainedButReclaimed": "Цей Deployer позначено як такий, що зберігає своє середовище після прогону, але Disposer після нього звільняє саме це середовище. Прибери Disposer або зніми позначку збереження середовища.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Звільняється наприкінці прогону. Натисни, щоб залишити це середовище працювати після нього (попередній перегляд, яким рецензенти користуються після відкриття PR); згодом його вимкне TTL або оператор.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Працює після завершення прогону. Натисни, щоб повернутися до звільнення його кроком Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Жодна служба в каталозі цієї дошки не заявляє здатність {capability}. Зареєструйте таку службу або додайте цю здатність потрібній службі в базових службах.",
|
|
4736
4743
|
"binaryOutputMissing": "Цієї служби зберігання більше немає в каталозі; оберіть іншу.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Ця служба більше не заявляє здатність {capability}, тому запуски буде відхилено; оберіть іншу.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.255.
|
|
3
|
+
"version": "0.255.1",
|
|
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.
|
|
43
|
+
"@cat-factory/contracts": "0.282.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|