@cat-factory/app 0.76.0 → 0.77.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.
- package/app/components/board/TaskDependencyEdges.vue +64 -0
- package/app/components/panels/InspectorPanel.vue +4 -0
- package/app/components/panels/inspector/ServiceConnections.vue +151 -0
- package/app/components/panels/inspector/TaskRunSettings.vue +66 -0
- package/app/types/domain.ts +1 -0
- package/i18n/locales/en.json +13 -1
- package/i18n/locales/es.json +13 -1
- package/i18n/locales/fr.json +13 -1
- package/i18n/locales/he.json +13 -1
- package/i18n/locales/ja.json +13 -1
- package/i18n/locales/pl.json +13 -1
- package/i18n/locales/tr.json +13 -1
- package/i18n/locales/uk.json +13 -1
- package/package.json +2 -2
|
@@ -21,6 +21,9 @@ const memberSegments = ref<MemberSeg[]>([])
|
|
|
21
21
|
// Frontend frame → bound service frame links (from a frontend's backend bindings).
|
|
22
22
|
type FrontendSeg = { id: string; x1: number; y1: number; x2: number; y2: number }
|
|
23
23
|
const frontendSegments = ref<FrontendSeg[]>([])
|
|
24
|
+
// Service frame → connected provider service frame links (from serviceConnections).
|
|
25
|
+
type ConnectionSeg = { id: string; x1: number; y1: number; x2: number; y2: number }
|
|
26
|
+
const connectionSegments = ref<ConnectionSeg[]>([])
|
|
24
27
|
|
|
25
28
|
// task → its dependencies, both ends being tasks
|
|
26
29
|
const taskDeps = computed(() => {
|
|
@@ -66,6 +69,24 @@ const frontendLinks = computed(() => {
|
|
|
66
69
|
return out
|
|
67
70
|
})
|
|
68
71
|
|
|
72
|
+
// consumer service frame → each provider service it connects to (a serviceConnections
|
|
73
|
+
// entry, stored on the consumer end). Deduped; a target deleted out of band draws nothing.
|
|
74
|
+
const connectionLinks = computed(() => {
|
|
75
|
+
const out: { id: string; source: string; target: string }[] = []
|
|
76
|
+
for (const f of board.frames) {
|
|
77
|
+
if (f.type !== 'service') continue
|
|
78
|
+
const seen = new Set<string>()
|
|
79
|
+
for (const connection of f.serviceConnections ?? []) {
|
|
80
|
+
const providerId = connection.serviceBlockId
|
|
81
|
+
if (seen.has(providerId)) continue
|
|
82
|
+
seen.add(providerId)
|
|
83
|
+
if (board.getBlock(providerId))
|
|
84
|
+
out.push({ id: `${f.id}__conn__${providerId}`, source: f.id, target: providerId })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out
|
|
88
|
+
})
|
|
89
|
+
|
|
69
90
|
/** Resolve a task's anchor: walk up task → module → service to the first card
|
|
70
91
|
* that's actually rendered (a container may be collapsed). */
|
|
71
92
|
function anchorEl(taskId: string): HTMLElement | null {
|
|
@@ -152,6 +173,23 @@ function recompute() {
|
|
|
152
173
|
fes.push({ id: link.id, x1: start.x, y1: start.y, x2: end.x, y2: end.y })
|
|
153
174
|
}
|
|
154
175
|
frontendSegments.value = fes
|
|
176
|
+
|
|
177
|
+
const conns: ConnectionSeg[] = []
|
|
178
|
+
for (const link of connectionLinks.value) {
|
|
179
|
+
const a = anchorEl(link.source)
|
|
180
|
+
const b = anchorEl(link.target)
|
|
181
|
+
if (!a || !b || a === b) continue
|
|
182
|
+
const ra = a.getBoundingClientRect()
|
|
183
|
+
const rb = b.getBoundingClientRect()
|
|
184
|
+
const ax = ra.left + ra.width / 2 - origin.left
|
|
185
|
+
const ay = ra.top + ra.height / 2 - origin.top
|
|
186
|
+
const bx = rb.left + rb.width / 2 - origin.left
|
|
187
|
+
const by = rb.top + rb.height / 2 - origin.top
|
|
188
|
+
const start = border(ax, ay, ra.width / 2, ra.height / 2, bx, by)
|
|
189
|
+
const end = border(bx, by, rb.width / 2, rb.height / 2, ax, ay)
|
|
190
|
+
conns.push({ id: link.id, x1: start.x, y1: start.y, x2: end.x, y2: end.y })
|
|
191
|
+
}
|
|
192
|
+
connectionSegments.value = conns
|
|
155
193
|
}
|
|
156
194
|
|
|
157
195
|
const { pause, resume } = useRafFn(recompute, { immediate: false })
|
|
@@ -195,8 +233,34 @@ onBeforeUnmount(pause)
|
|
|
195
233
|
>
|
|
196
234
|
<path d="M0,0 L10,5 L0,10 z" fill="#22d3ee" />
|
|
197
235
|
</marker>
|
|
236
|
+
<marker
|
|
237
|
+
id="service-connection-arrow"
|
|
238
|
+
viewBox="0 0 10 10"
|
|
239
|
+
refX="8"
|
|
240
|
+
refY="5"
|
|
241
|
+
markerWidth="6"
|
|
242
|
+
markerHeight="6"
|
|
243
|
+
orient="auto-start-reverse"
|
|
244
|
+
>
|
|
245
|
+
<path d="M0,0 L10,5 L0,10 z" fill="#34d399" />
|
|
246
|
+
</marker>
|
|
198
247
|
</defs>
|
|
199
248
|
|
|
249
|
+
<!-- consumer service → provider service connection links (emerald, arrow toward the provider) -->
|
|
250
|
+
<line
|
|
251
|
+
v-for="s in connectionSegments"
|
|
252
|
+
:key="s.id"
|
|
253
|
+
:x1="s.x1"
|
|
254
|
+
:y1="s.y1"
|
|
255
|
+
:x2="s.x2"
|
|
256
|
+
:y2="s.y2"
|
|
257
|
+
stroke="#34d399"
|
|
258
|
+
:stroke-width="1.5"
|
|
259
|
+
stroke-dasharray="3 4"
|
|
260
|
+
:stroke-opacity="0.55"
|
|
261
|
+
marker-end="url(#service-connection-arrow)"
|
|
262
|
+
/>
|
|
263
|
+
|
|
200
264
|
<!-- frontend frame → bound service frame links (cyan, arrow toward the service under test) -->
|
|
201
265
|
<line
|
|
202
266
|
v-for="s in frontendSegments"
|
|
@@ -9,6 +9,7 @@ import ServiceTestConfig from '~/components/panels/inspector/ServiceTestConfig.v
|
|
|
9
9
|
import ServiceFragments from '~/components/panels/inspector/ServiceFragments.vue'
|
|
10
10
|
import ServiceReleaseHealthConfig from '~/components/panels/inspector/ServiceReleaseHealthConfig.vue'
|
|
11
11
|
import FrontendConfig from '~/components/panels/inspector/FrontendConfig.vue'
|
|
12
|
+
import ServiceConnections from '~/components/panels/inspector/ServiceConnections.vue'
|
|
12
13
|
import ContainerSummary from '~/components/panels/inspector/ContainerSummary.vue'
|
|
13
14
|
import TaskDependencies from '~/components/panels/inspector/TaskDependencies.vue'
|
|
14
15
|
import TaskStructure from '~/components/panels/inspector/TaskStructure.vue'
|
|
@@ -465,6 +466,9 @@ const showOriginalDescription = ref(false)
|
|
|
465
466
|
<!-- frontend (frame): build/serve/mock config + backend bindings (board links) -->
|
|
466
467
|
<FrontendConfig v-if="isFrame && block.type === 'frontend'" :block="block" />
|
|
467
468
|
|
|
469
|
+
<!-- service (frame): directed connections to the other services it uses (board links) -->
|
|
470
|
+
<ServiceConnections v-if="isFrame && block.type === 'service'" :block="block" />
|
|
471
|
+
|
|
468
472
|
<!-- service (frame): test infra + provisioning configuration -->
|
|
469
473
|
<ServiceTestConfig v-if="isFrame" :block="block" />
|
|
470
474
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { Block, ServiceConnection } from '~/types/domain'
|
|
4
|
+
|
|
5
|
+
// Service-frame (`type: 'service'`) connections: the other services this one USES
|
|
6
|
+
// (consumer→provider edges, stored on this frame — the consumer end). Each row picks a
|
|
7
|
+
// provider service frame and optionally describes the relationship (folded into agent
|
|
8
|
+
// prompts when the provider is involved in a task). The rows ARE the board's
|
|
9
|
+
// service→service links, and the source of a task's "involved services" choices.
|
|
10
|
+
// Persisted as serviceConnections on the block via the shared updateBlock PATCH.
|
|
11
|
+
// The read-only "Used by" list below is the reverse direction, computed from the
|
|
12
|
+
// OTHER frames' connections targeting this one.
|
|
13
|
+
const props = defineProps<{ block: Block }>()
|
|
14
|
+
|
|
15
|
+
const board = useBoardStore()
|
|
16
|
+
const { t } = useI18n()
|
|
17
|
+
|
|
18
|
+
const connections = computed<ServiceConnection[]>(() => props.block.serviceConnections ?? [])
|
|
19
|
+
|
|
20
|
+
function save(next: ServiceConnection[]) {
|
|
21
|
+
board.updateBlock(props.block.id, { serviceConnections: next })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Provider candidates: every OTHER service frame on the board. A frame already used by
|
|
25
|
+
// another row is excluded per row (duplicates are rejected server-side too).
|
|
26
|
+
const serviceFrames = computed(() =>
|
|
27
|
+
board.frames.filter((b) => b.type === 'service' && b.id !== props.block.id),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
function targetItems(index: number) {
|
|
31
|
+
const takenElsewhere = new Set(
|
|
32
|
+
connections.value.filter((_, i) => i !== index).map((c) => c.serviceBlockId),
|
|
33
|
+
)
|
|
34
|
+
return serviceFrames.value
|
|
35
|
+
.filter((f) => !takenElsewhere.has(f.id))
|
|
36
|
+
.map((f) => ({ label: f.title || f.id, value: f.id }))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function replaceConnection(index: number, next: ServiceConnection) {
|
|
40
|
+
save(connections.value.map((c, i) => (i === index ? next : c)))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setTarget(index: number, serviceBlockId: string) {
|
|
44
|
+
const c = connections.value[index]
|
|
45
|
+
if (c) replaceConnection(index, { ...c, serviceBlockId })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function setDescription(index: number, value: string) {
|
|
49
|
+
const c = connections.value[index]
|
|
50
|
+
if (c) replaceConnection(index, { ...c, description: value.trim() || undefined })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// A new row starts on the first still-available provider; with none available the add
|
|
54
|
+
// button is disabled, so a placeholder row never round-trips an invalid PATCH.
|
|
55
|
+
const nextAvailable = computed(() => {
|
|
56
|
+
const taken = new Set(connections.value.map((c) => c.serviceBlockId))
|
|
57
|
+
return serviceFrames.value.find((f) => !taken.has(f.id))
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
function addConnection() {
|
|
61
|
+
const target = nextAvailable.value
|
|
62
|
+
if (target) save([...connections.value, { serviceBlockId: target.id }])
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function removeConnection(index: number) {
|
|
66
|
+
save(connections.value.filter((_, i) => i !== index))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Reverse direction, read-only: the service frames whose own connections name this one.
|
|
70
|
+
const usedBy = computed(() =>
|
|
71
|
+
board.frames.filter(
|
|
72
|
+
(b) =>
|
|
73
|
+
b.type === 'service' &&
|
|
74
|
+
b.id !== props.block.id &&
|
|
75
|
+
(b.serviceConnections ?? []).some((c) => c.serviceBlockId === props.block.id),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<div class="space-y-2 border-t border-slate-800 pt-2" data-testid="service-connections">
|
|
82
|
+
<div class="flex items-center justify-between">
|
|
83
|
+
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
84
|
+
{{ t('inspector.serviceConnections.title') }}
|
|
85
|
+
</span>
|
|
86
|
+
<UButton
|
|
87
|
+
size="xs"
|
|
88
|
+
variant="ghost"
|
|
89
|
+
color="neutral"
|
|
90
|
+
icon="i-lucide-plus"
|
|
91
|
+
:disabled="!nextAvailable"
|
|
92
|
+
data-testid="service-connection-add"
|
|
93
|
+
@click="addConnection"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
97
|
+
{{ t('inspector.serviceConnections.hint') }}
|
|
98
|
+
</p>
|
|
99
|
+
|
|
100
|
+
<div v-if="connections.length" class="space-y-1.5">
|
|
101
|
+
<div
|
|
102
|
+
v-for="(c, i) in connections"
|
|
103
|
+
:key="c.serviceBlockId"
|
|
104
|
+
class="flex items-center gap-1"
|
|
105
|
+
data-testid="service-connection-row"
|
|
106
|
+
>
|
|
107
|
+
<USelect
|
|
108
|
+
:model-value="c.serviceBlockId"
|
|
109
|
+
:items="targetItems(i)"
|
|
110
|
+
size="xs"
|
|
111
|
+
class="flex-1"
|
|
112
|
+
data-testid="service-connection-target"
|
|
113
|
+
@update:model-value="(v: string) => setTarget(i, v)"
|
|
114
|
+
/>
|
|
115
|
+
<UInput
|
|
116
|
+
:model-value="c.description ?? ''"
|
|
117
|
+
size="xs"
|
|
118
|
+
class="flex-1"
|
|
119
|
+
maxlength="300"
|
|
120
|
+
:placeholder="t('inspector.serviceConnections.descriptionPlaceholder')"
|
|
121
|
+
data-testid="service-connection-description"
|
|
122
|
+
@blur="(e: FocusEvent) => setDescription(i, (e.target as HTMLInputElement).value)"
|
|
123
|
+
@keydown.enter="
|
|
124
|
+
(e: KeyboardEvent) => setDescription(i, (e.target as HTMLInputElement).value)
|
|
125
|
+
"
|
|
126
|
+
/>
|
|
127
|
+
<UButton
|
|
128
|
+
size="xs"
|
|
129
|
+
variant="ghost"
|
|
130
|
+
color="neutral"
|
|
131
|
+
icon="i-lucide-x"
|
|
132
|
+
:title="t('inspector.serviceConnections.remove')"
|
|
133
|
+
data-testid="service-connection-remove"
|
|
134
|
+
@click="removeConnection(i)"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div v-else class="text-[11px] text-slate-500">
|
|
139
|
+
{{ t('inspector.serviceConnections.empty') }}
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div v-if="usedBy.length" class="space-y-1" data-testid="service-connections-used-by">
|
|
143
|
+
<span class="text-[11px] text-slate-400">{{ t('inspector.serviceConnections.usedBy') }}</span>
|
|
144
|
+
<div class="flex flex-wrap gap-1">
|
|
145
|
+
<UBadge v-for="f in usedBy" :key="f.id" size="sm" variant="soft" color="neutral">
|
|
146
|
+
{{ f.title || f.id }}
|
|
147
|
+
</UBadge>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onMounted } from 'vue'
|
|
3
|
+
import { connectionNeighborIds } from '@cat-factory/contracts'
|
|
3
4
|
import type { Block } from '~/types/domain'
|
|
4
5
|
import type { WritebackOverride } from '~/types/tracker'
|
|
5
6
|
import { mergePresetOptionLabel, mergePresetThresholds } from '~/utils/mergePreset'
|
|
@@ -154,6 +155,33 @@ function setPipeline(id: string) {
|
|
|
154
155
|
board.updateBlock(props.block.id, { pipelineId: id })
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
// ---- involved services ------------------------------------------------------
|
|
159
|
+
// Which of the connected services are directly involved in this task (beyond its own
|
|
160
|
+
// service, which is always implicit): each involved service is spun up as an ephemeral
|
|
161
|
+
// environment alongside it, and the coding agent may change its repo too. Choices come
|
|
162
|
+
// from the frame's connection NEIGHBORS (either direction). An id whose connection was
|
|
163
|
+
// removed after selection is stale: badged, and dropped on the next toggle (the write
|
|
164
|
+
// gate would reject it).
|
|
165
|
+
const connectedServices = computed(() => {
|
|
166
|
+
const frame = taskFrame.value
|
|
167
|
+
if (!frame) return []
|
|
168
|
+
return [...connectionNeighborIds(board.blocks, frame.id)]
|
|
169
|
+
.map((id) => board.getBlock(id))
|
|
170
|
+
.filter((b): b is Block => !!b)
|
|
171
|
+
})
|
|
172
|
+
const involvedIds = computed(() => props.block.involvedServiceIds ?? [])
|
|
173
|
+
const staleInvolvedServices = computed(() => {
|
|
174
|
+
const connected = new Set(connectedServices.value.map((b) => b.id))
|
|
175
|
+
return involvedIds.value.filter((id) => !connected.has(id))
|
|
176
|
+
})
|
|
177
|
+
function toggleInvolved(serviceId: string, on: boolean) {
|
|
178
|
+
const connected = new Set(connectedServices.value.map((b) => b.id))
|
|
179
|
+
const kept = involvedIds.value.filter((id) => id !== serviceId && connected.has(id))
|
|
180
|
+
board.updateBlock(props.block.id, {
|
|
181
|
+
involvedServiceIds: on ? [...kept, serviceId] : kept,
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
157
185
|
// ---- issue-tracker writeback overrides -------------------------------------
|
|
158
186
|
// Per-task overrides for the two workspace writeback toggles (comment on PR open,
|
|
159
187
|
// close linked issue on merge). null override ⇒ inherit the workspace default.
|
|
@@ -400,6 +428,44 @@ const technicalLabel = computed(() => {
|
|
|
400
428
|
</div>
|
|
401
429
|
</div>
|
|
402
430
|
|
|
431
|
+
<!-- involved services: connected services this task spans (envs + possible code changes) -->
|
|
432
|
+
<div data-testid="involved-services">
|
|
433
|
+
<div class="mb-1 flex items-center justify-between">
|
|
434
|
+
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
435
|
+
{{ t('inspector.runSettings.involvedServices') }}
|
|
436
|
+
</span>
|
|
437
|
+
</div>
|
|
438
|
+
<div v-if="connectedServices.length" class="space-y-1">
|
|
439
|
+
<UCheckbox
|
|
440
|
+
v-for="s in connectedServices"
|
|
441
|
+
:key="s.id"
|
|
442
|
+
:model-value="involvedIds.includes(s.id)"
|
|
443
|
+
:label="s.title || s.id"
|
|
444
|
+
size="xs"
|
|
445
|
+
data-testid="involved-service-toggle"
|
|
446
|
+
@update:model-value="(v: boolean | 'indeterminate') => toggleInvolved(s.id, v === true)"
|
|
447
|
+
/>
|
|
448
|
+
</div>
|
|
449
|
+
<div v-else class="text-[11px] text-slate-500">
|
|
450
|
+
{{ t('inspector.runSettings.involvedServicesEmpty') }}
|
|
451
|
+
</div>
|
|
452
|
+
<div v-if="staleInvolvedServices.length" class="mt-1 flex flex-wrap gap-1">
|
|
453
|
+
<UBadge
|
|
454
|
+
v-for="id in staleInvolvedServices"
|
|
455
|
+
:key="id"
|
|
456
|
+
size="sm"
|
|
457
|
+
variant="soft"
|
|
458
|
+
color="warning"
|
|
459
|
+
:title="t('inspector.runSettings.involvedServiceStale')"
|
|
460
|
+
>
|
|
461
|
+
{{ board.getBlock(id)?.title ?? id }}
|
|
462
|
+
</UBadge>
|
|
463
|
+
</div>
|
|
464
|
+
<div class="mt-1 text-[11px] text-slate-500">
|
|
465
|
+
{{ t('inspector.runSettings.involvedServicesHint') }}
|
|
466
|
+
</div>
|
|
467
|
+
</div>
|
|
468
|
+
|
|
403
469
|
<!-- issue-tracker writeback overrides -->
|
|
404
470
|
<div>
|
|
405
471
|
<div class="mb-1 flex items-center justify-between">
|
package/app/types/domain.ts
CHANGED
package/i18n/locales/en.json
CHANGED
|
@@ -546,6 +546,14 @@
|
|
|
546
546
|
}
|
|
547
547
|
}
|
|
548
548
|
},
|
|
549
|
+
"serviceConnections": {
|
|
550
|
+
"title": "Service connections",
|
|
551
|
+
"hint": "The other services this one uses, e.g. a service that sends its emails. Connections draw edges on the board, and tasks can mark a connected service as directly involved.",
|
|
552
|
+
"descriptionPlaceholder": "How this service uses it, e.g. sends emails via it",
|
|
553
|
+
"remove": "Remove connection",
|
|
554
|
+
"empty": "No connections. Add one to link this service to another service it uses.",
|
|
555
|
+
"usedBy": "Used by"
|
|
556
|
+
},
|
|
549
557
|
"releaseHealth": {
|
|
550
558
|
"title": "Post-release health",
|
|
551
559
|
"clear": "Clear",
|
|
@@ -769,7 +777,11 @@
|
|
|
769
777
|
"responsibleProduct": "Responsible product",
|
|
770
778
|
"responsibleEmpty": "Unassigned. Set a product owner to notify them when requirement review flags this task.",
|
|
771
779
|
"autoStartDependents": "Auto-start dependents",
|
|
772
|
-
"autoStartHint": "When this task merges, automatically start the tasks that depend on it (once their other dependencies are also done)."
|
|
780
|
+
"autoStartHint": "When this task merges, automatically start the tasks that depend on it (once their other dependencies are also done).",
|
|
781
|
+
"involvedServices": "Involved services",
|
|
782
|
+
"involvedServicesHint": "Connected services directly involved in this task: each spins up as an ephemeral environment alongside this task's own service, and the coding agent may change their repositories too.",
|
|
783
|
+
"involvedServicesEmpty": "No connected services. Connect services on the service frame to select them here.",
|
|
784
|
+
"involvedServiceStale": "No longer connected to this task's service; it is dropped on the next change."
|
|
773
785
|
}
|
|
774
786
|
},
|
|
775
787
|
"panels": {
|
package/i18n/locales/es.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "Conexiones de servicios",
|
|
508
|
+
"hint": "Los otros servicios que este utiliza, p. ej. un servicio que envía sus correos. Las conexiones dibujan aristas en el tablero, y las tareas pueden marcar un servicio conectado como directamente involucrado.",
|
|
509
|
+
"descriptionPlaceholder": "Cómo lo usa este servicio, p. ej. envía correos a través de él",
|
|
510
|
+
"remove": "Eliminar conexión",
|
|
511
|
+
"empty": "Sin conexiones. Añade una para vincular este servicio con otro servicio que utiliza.",
|
|
512
|
+
"usedBy": "Usado por"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "Salud posterior al lanzamiento",
|
|
508
516
|
"clear": "Limpiar",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "Producto responsable",
|
|
727
735
|
"responsibleEmpty": "Sin asignar. Define un responsable de producto para notificarle cuando la revisión de requisitos marque esta tarea.",
|
|
728
736
|
"autoStartDependents": "Iniciar dependientes automáticamente",
|
|
729
|
-
"autoStartHint": "Cuando esta tarea se fusione, inicia automáticamente las tareas que dependen de ella (una vez que sus otras dependencias también estén completas)."
|
|
737
|
+
"autoStartHint": "Cuando esta tarea se fusione, inicia automáticamente las tareas que dependen de ella (una vez que sus otras dependencias también estén completas).",
|
|
738
|
+
"involvedServices": "Servicios involucrados",
|
|
739
|
+
"involvedServicesHint": "Servicios conectados directamente involucrados en esta tarea: cada uno se levanta como un entorno efímero junto al servicio propio de la tarea, y el agente de código puede modificar también sus repositorios.",
|
|
740
|
+
"involvedServicesEmpty": "No hay servicios conectados. Conecta servicios en el marco del servicio para seleccionarlos aquí.",
|
|
741
|
+
"involvedServiceStale": "Ya no está conectado al servicio de esta tarea; se eliminará con el próximo cambio."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/fr.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "Connexions de services",
|
|
508
|
+
"hint": "Les autres services que celui-ci utilise, p. ex. un service qui envoie ses e-mails. Les connexions tracent des liens sur le tableau, et les tâches peuvent marquer un service connecté comme directement impliqué.",
|
|
509
|
+
"descriptionPlaceholder": "Comment ce service l'utilise, p. ex. envoie des e-mails via lui",
|
|
510
|
+
"remove": "Supprimer la connexion",
|
|
511
|
+
"empty": "Aucune connexion. Ajoutez-en une pour relier ce service à un autre service qu'il utilise.",
|
|
512
|
+
"usedBy": "Utilisé par"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "Santé post-déploiement",
|
|
508
516
|
"clear": "Effacer",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "Produit responsable",
|
|
727
735
|
"responsibleEmpty": "Non assigné. Définissez un responsable produit pour le notifier lorsque la revue d'exigences signale cette tâche.",
|
|
728
736
|
"autoStartDependents": "Démarrer automatiquement les dépendants",
|
|
729
|
-
"autoStartHint": "Lorsque cette tâche est fusionnée, démarrer automatiquement les tâches qui en dépendent (une fois que leurs autres dépendances sont également terminées)."
|
|
737
|
+
"autoStartHint": "Lorsque cette tâche est fusionnée, démarrer automatiquement les tâches qui en dépendent (une fois que leurs autres dépendances sont également terminées).",
|
|
738
|
+
"involvedServices": "Services impliqués",
|
|
739
|
+
"involvedServicesHint": "Services connectés directement impliqués dans cette tâche : chacun démarre comme environnement éphémère aux côtés du service propre de la tâche, et l'agent de code peut aussi modifier leurs dépôts.",
|
|
740
|
+
"involvedServicesEmpty": "Aucun service connecté. Connectez des services sur le cadre du service pour les sélectionner ici.",
|
|
741
|
+
"involvedServiceStale": "N'est plus connecté au service de cette tâche ; il sera retiré au prochain changement."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/he.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "חיבורי שירותים",
|
|
508
|
+
"hint": "השירותים האחרים שהשירות הזה משתמש בהם, למשל שירות ששולח עבורו הודעות דואר. חיבורים מציירים קשתות על הלוח, ומשימות יכולות לסמן שירות מחובר כמעורב ישירות.",
|
|
509
|
+
"descriptionPlaceholder": "איך השירות הזה משתמש בו, למשל שולח דרכו הודעות דואר",
|
|
510
|
+
"remove": "הסר חיבור",
|
|
511
|
+
"empty": "אין חיבורים. הוסיפו אחד כדי לקשר שירות זה לשירות אחר שהוא משתמש בו.",
|
|
512
|
+
"usedBy": "בשימוש על ידי"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "תקינות לאחר שחרור",
|
|
508
516
|
"clear": "נקה",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "מוצר אחראי",
|
|
727
735
|
"responsibleEmpty": "לא משויך. הגדר בעלים של מוצר כדי ליידע אותו כאשר סקירת הדרישות מסמנת משימה זו.",
|
|
728
736
|
"autoStartDependents": "הפעלה אוטומטית של תלויות",
|
|
729
|
-
"autoStartHint": "כאשר משימה זו ממוזגת, הפעל אוטומטית את המשימות התלויות בה (לאחר שגם שאר התלויות שלהן הושלמו)."
|
|
737
|
+
"autoStartHint": "כאשר משימה זו ממוזגת, הפעל אוטומטית את המשימות התלויות בה (לאחר שגם שאר התלויות שלהן הושלמו).",
|
|
738
|
+
"involvedServices": "שירותים מעורבים",
|
|
739
|
+
"involvedServicesHint": "שירותים מחוברים המעורבים ישירות במשימה זו: כל אחד מהם מוקם כסביבה זמנית לצד השירות של המשימה, וסוכן הקוד עשוי לשנות גם את המאגרים שלהם.",
|
|
740
|
+
"involvedServicesEmpty": "אין שירותים מחוברים. חברו שירותים במסגרת השירות כדי לבחור אותם כאן.",
|
|
741
|
+
"involvedServiceStale": "כבר לא מחובר לשירות של משימה זו; הוא יוסר בשינוי הבא."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/ja.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "サービス接続",
|
|
508
|
+
"hint": "このサービスが利用する他のサービス(例: メール送信を担うサービス)。接続はボード上にエッジとして描画され、タスクは接続先サービスを直接関与としてマークできます。",
|
|
509
|
+
"descriptionPlaceholder": "このサービスの利用方法(例: これを介してメールを送信)",
|
|
510
|
+
"remove": "接続を削除",
|
|
511
|
+
"empty": "接続はありません。このサービスが利用する別のサービスとリンクするには追加してください。",
|
|
512
|
+
"usedBy": "利用元"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "リリース後の健全性",
|
|
508
516
|
"clear": "クリア",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "担当プロダクト",
|
|
727
735
|
"responsibleEmpty": "未割り当て。要件レビューがこのタスクをフラグしたときに通知するため、プロダクトオーナーを設定してください。",
|
|
728
736
|
"autoStartDependents": "依存タスクを自動開始",
|
|
729
|
-
"autoStartHint": "このタスクがマージされたら、それに依存するタスクを (他の依存関係も完了したら) 自動的に開始します。"
|
|
737
|
+
"autoStartHint": "このタスクがマージされたら、それに依存するタスクを (他の依存関係も完了したら) 自動的に開始します。",
|
|
738
|
+
"involvedServices": "関与するサービス",
|
|
739
|
+
"involvedServicesHint": "このタスクに直接関与する接続済みサービス。各サービスはタスク自身のサービスと並んで一時的な環境として起動され、コーディングエージェントがそのリポジトリを変更することもあります。",
|
|
740
|
+
"involvedServicesEmpty": "接続済みのサービスはありません。ここで選択するには、サービスフレームでサービスを接続してください。",
|
|
741
|
+
"involvedServiceStale": "このタスクのサービスとの接続が解除されています。次回の変更時に削除されます。"
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/pl.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "Połączenia usług",
|
|
508
|
+
"hint": "Inne usługi, z których korzysta ta usługa, np. usługa wysyłająca za nią e-maile. Połączenia rysują krawędzie na tablicy, a zadania mogą oznaczyć połączoną usługę jako bezpośrednio zaangażowaną.",
|
|
509
|
+
"descriptionPlaceholder": "Jak ta usługa z niej korzysta, np. wysyła przez nią e-maile",
|
|
510
|
+
"remove": "Usuń połączenie",
|
|
511
|
+
"empty": "Brak połączeń. Dodaj połączenie, aby powiązać tę usługę z inną usługą, z której korzysta.",
|
|
512
|
+
"usedBy": "Używana przez"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "Kondycja po wydaniu",
|
|
508
516
|
"clear": "Wyczyść",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "Odpowiedzialny produkt",
|
|
727
735
|
"responsibleEmpty": "Nieprzypisane. Ustaw właściciela produktu, aby powiadamiać go, gdy recenzja wymagań oznaczy to zadanie.",
|
|
728
736
|
"autoStartDependents": "Automatycznie uruchamiaj zależne",
|
|
729
|
-
"autoStartHint": "Gdy to zadanie zostanie scalone, automatycznie uruchom zadania od niego zależne (gdy ich pozostałe zależności również będą gotowe)."
|
|
737
|
+
"autoStartHint": "Gdy to zadanie zostanie scalone, automatycznie uruchom zadania od niego zależne (gdy ich pozostałe zależności również będą gotowe).",
|
|
738
|
+
"involvedServices": "Zaangażowane usługi",
|
|
739
|
+
"involvedServicesHint": "Połączone usługi bezpośrednio zaangażowane w to zadanie: każda z nich uruchamiana jest jako środowisko tymczasowe obok własnej usługi zadania, a agent kodujący może też zmieniać ich repozytoria.",
|
|
740
|
+
"involvedServicesEmpty": "Brak połączonych usług. Połącz usługi na ramce usługi, aby móc je tutaj wybrać.",
|
|
741
|
+
"involvedServiceStale": "Nie jest już połączona z usługą tego zadania; zostanie usunięta przy następnej zmianie."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/tr.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "Servis bağlantıları",
|
|
508
|
+
"hint": "Bu servisin kullandığı diğer servisler, örn. e-postalarını gönderen bir servis. Bağlantılar panoda kenar olarak çizilir ve görevler bağlı bir servisi doğrudan dahil olarak işaretleyebilir.",
|
|
509
|
+
"descriptionPlaceholder": "Bu servis onu nasıl kullanıyor, örn. e-postaları onun üzerinden gönderir",
|
|
510
|
+
"remove": "Bağlantıyı kaldır",
|
|
511
|
+
"empty": "Bağlantı yok. Bu servisi kullandığı başka bir servise bağlamak için bir bağlantı ekleyin.",
|
|
512
|
+
"usedBy": "Kullananlar"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "Sürüm sonrası sağlık",
|
|
508
516
|
"clear": "Temizle",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "Sorumlu ürün",
|
|
727
735
|
"responsibleEmpty": "Atanmamış. Gereksinim incelemesi bu görevi işaretlediğinde bildirim almak için bir ürün sahibi belirleyin.",
|
|
728
736
|
"autoStartDependents": "Bağımlıları otomatik başlat",
|
|
729
|
-
"autoStartHint": "Bu görev birleştirildiğinde, ona bağlı görevleri otomatik olarak başlat (diğer bağımlılıkları da tamamlandığında)."
|
|
737
|
+
"autoStartHint": "Bu görev birleştirildiğinde, ona bağlı görevleri otomatik olarak başlat (diğer bağımlılıkları da tamamlandığında).",
|
|
738
|
+
"involvedServices": "Dahil olan servisler",
|
|
739
|
+
"involvedServicesHint": "Bu göreve doğrudan dahil olan bağlı servisler: her biri görevin kendi servisiyle birlikte geçici bir ortam olarak ayağa kaldırılır ve kodlama ajanı onların depolarında da değişiklik yapabilir.",
|
|
740
|
+
"involvedServicesEmpty": "Bağlı servis yok. Burada seçebilmek için servis çerçevesinde servisleri bağlayın.",
|
|
741
|
+
"involvedServiceStale": "Artık bu görevin servisine bağlı değil; bir sonraki değişiklikte kaldırılacak."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/i18n/locales/uk.json
CHANGED
|
@@ -503,6 +503,14 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
|
+
"serviceConnections": {
|
|
507
|
+
"title": "З'єднання сервісів",
|
|
508
|
+
"hint": "Інші сервіси, якими користується цей сервіс, напр. сервіс, що надсилає за нього листи. З'єднання малюються як ребра на дошці, а завдання можуть позначити з'єднаний сервіс як безпосередньо залучений.",
|
|
509
|
+
"descriptionPlaceholder": "Як цей сервіс ним користується, напр. надсилає через нього листи",
|
|
510
|
+
"remove": "Видалити з'єднання",
|
|
511
|
+
"empty": "З'єднань немає. Додайте з'єднання, щоб пов'язати цей сервіс з іншим сервісом, яким він користується.",
|
|
512
|
+
"usedBy": "Використовується"
|
|
513
|
+
},
|
|
506
514
|
"releaseHealth": {
|
|
507
515
|
"title": "Стан після релізу",
|
|
508
516
|
"clear": "Очистити",
|
|
@@ -726,7 +734,11 @@
|
|
|
726
734
|
"responsibleProduct": "Відповідальний продукт",
|
|
727
735
|
"responsibleEmpty": "Не призначено. Призначте власника продукту, щоб сповіщати його, коли огляд вимог позначає це завдання.",
|
|
728
736
|
"autoStartDependents": "Автозапуск залежних",
|
|
729
|
-
"autoStartHint": "Коли це завдання зливається, автоматично запускати завдання, що залежать від нього (щойно їхні інші залежності також виконано)."
|
|
737
|
+
"autoStartHint": "Коли це завдання зливається, автоматично запускати завдання, що залежать від нього (щойно їхні інші залежності також виконано).",
|
|
738
|
+
"involvedServices": "Залучені сервіси",
|
|
739
|
+
"involvedServicesHint": "З'єднані сервіси, безпосередньо залучені до цього завдання: кожен розгортається як тимчасове середовище поряд із власним сервісом завдання, і агент кодування може змінювати також їхні репозиторії.",
|
|
740
|
+
"involvedServicesEmpty": "Немає з'єднаних сервісів. З'єднайте сервіси на рамці сервісу, щоб вибрати їх тут.",
|
|
741
|
+
"involvedServiceStale": "Більше не з'єднаний із сервісом цього завдання; буде видалений під час наступної зміни."
|
|
730
742
|
}
|
|
731
743
|
},
|
|
732
744
|
"panels": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.77.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"valibot": "^1.4.2",
|
|
35
35
|
"vue": "^3.5.39",
|
|
36
36
|
"wretch": "^3.0.9",
|
|
37
|
-
"@cat-factory/contracts": "0.
|
|
37
|
+
"@cat-factory/contracts": "0.83.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@toad-contracts/testing": "0.3.2",
|