@cat-factory/app 0.141.1 → 0.142.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/AddTaskModal.vue +10 -7
- package/app/components/documents/RepoContextDocPicker.vue +6 -2
- package/app/components/fragments/FragmentLibraryManager.vue +139 -33
- package/app/components/github/GitHubRepoSearchSelect.vue +7 -2
- package/app/components/pipeline/PipelineHealthModal.vue +47 -6
- package/app/composables/usePipelineHealth.spec.ts +22 -0
- package/app/composables/usePipelineHealth.ts +36 -8
- package/app/utils/pipeline.spec.ts +17 -3
- package/i18n/locales/de.json +8 -2
- package/i18n/locales/en.json +8 -2
- package/i18n/locales/es.json +8 -2
- package/i18n/locales/fr.json +8 -2
- package/i18n/locales/he.json +8 -2
- package/i18n/locales/it.json +8 -2
- package/i18n/locales/ja.json +8 -2
- package/i18n/locales/pl.json +8 -2
- package/i18n/locales/tr.json +8 -2
- package/i18n/locales/uk.json +8 -2
- package/package.json +2 -2
|
@@ -289,8 +289,9 @@ const selectedModelPresetLabel = computed(() => {
|
|
|
289
289
|
// Hide UI-testing pipelines (`tester-ui` / `visual-confirmation`) when the target frame has no
|
|
290
290
|
// UI to exercise — they'd be refused server-side (see utils/pipeline + the backend gate). Also
|
|
291
291
|
// hide `'recurring'`-only pipelines (a one-off task start of one is refused at run start) and,
|
|
292
|
-
// for a `document` task, every
|
|
293
|
-
//
|
|
292
|
+
// for a `document` / `review` task, every pipeline whose purpose doesn't match (a doc task authors
|
|
293
|
+
// a doc, a review task reviews a PR — only document / review pipelines are relevant, per the
|
|
294
|
+
// `purpose` classifier). Re-filters as the chosen task type changes.
|
|
294
295
|
const selectablePipelines = computed(() =>
|
|
295
296
|
pipelines.pipelines.filter((p) =>
|
|
296
297
|
pipelineAllowedForManualStart(p, frame.value, board.blocks, taskType.value),
|
|
@@ -302,14 +303,16 @@ const selectablePipelines = computed(() =>
|
|
|
302
303
|
// must appear in the form BEFORE creation:
|
|
303
304
|
// - `ralph` needs its preset so the per-task validation command + iteration budget the `ralph`
|
|
304
305
|
// agent contributes surface for editing ("choose at run time" would be a dead end);
|
|
305
|
-
// - a `document` task defaults to `pl_document`
|
|
306
|
-
// hides every non-document pipeline)
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
306
|
+
// - a `document` task defaults to `pl_document` and a `review` task to `pl_review` so their
|
|
307
|
+
// purpose-narrowed picker (the `purpose` gate hides every non-document / non-review pipeline)
|
|
308
|
+
// is never rendered empty.
|
|
309
|
+
// The other typed default (spike) carries no up-front config and doesn't narrow its picker, so the
|
|
310
|
+
// modal leaves `pipelineId` unset and `BoardService` applies the backend type-default at creation.
|
|
311
|
+
// Keep these ids in step with the backend helper.
|
|
310
312
|
const DEFAULT_PIPELINE_FOR_TYPE: Partial<Record<TaskTypeChoice, string>> = {
|
|
311
313
|
ralph: 'pl_ralph',
|
|
312
314
|
document: 'pl_document',
|
|
315
|
+
review: 'pl_review',
|
|
313
316
|
}
|
|
314
317
|
watch(taskType, (next) => {
|
|
315
318
|
const preset = DEFAULT_PIPELINE_FOR_TYPE[next]
|
|
@@ -51,8 +51,12 @@ const selectedRepoId = ref<number | undefined>(undefined)
|
|
|
51
51
|
const selectedRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
52
52
|
|
|
53
53
|
function toRepoItem(r: GitHubAvailableRepo) {
|
|
54
|
-
const
|
|
55
|
-
|
|
54
|
+
const priv = r.private ? t('github.addService.repoLabel.private') : ''
|
|
55
|
+
// Badge a repo reachable only via the user's own PAT: a context doc from it resolves through
|
|
56
|
+
// the workspace App at run time, so on hosted a personal-only repo fails the link with a clear
|
|
57
|
+
// error. The badge flags that before the pick.
|
|
58
|
+
const personal = r.personal ? t('github.addService.repoLabel.personal') : ''
|
|
59
|
+
return { label: `${r.owner}/${r.name}${priv}${personal}`, value: r.githubId }
|
|
56
60
|
}
|
|
57
61
|
const queryMatches = computed(() => (belowMinChars.value ? [] : repoResults.value.map(toRepoItem)))
|
|
58
62
|
const repoMenuItems = computed(() => {
|
|
@@ -181,61 +181,131 @@ async function removeFragment(id: string) {
|
|
|
181
181
|
// Link a Confluence/Notion page or GitHub file as a fragment that is re-resolved
|
|
182
182
|
// from the source at run time (a living source of truth, not a frozen snapshot).
|
|
183
183
|
const docDraft = ref({ source: '' as DocumentSourceKind | '', ref: '', tags: '' })
|
|
184
|
-
const docDraftValid = computed(
|
|
185
|
-
(
|
|
186
|
-
|
|
184
|
+
const docDraftValid = computed(() => {
|
|
185
|
+
if (docLinkDisabled.value || !docDraft.value.source) return false
|
|
186
|
+
// The GitHub picker validates on staged files; every other path on the free-text ref.
|
|
187
|
+
return usingDocPicker.value ? docFilePaths.value.length > 0 : !!docDraft.value.ref.trim()
|
|
188
|
+
})
|
|
187
189
|
|
|
188
190
|
// ---- GitHub file picker (documents tab) -----------------------------------
|
|
189
|
-
// For a GitHub source, let the user search a repo + browse to
|
|
190
|
-
// hand-typing a `owner/repo:path` ref.
|
|
191
|
-
//
|
|
192
|
-
//
|
|
191
|
+
// For a GitHub source, let the user search a repo + browse to one or MORE files
|
|
192
|
+
// instead of hand-typing a `owner/repo:path` ref. Files are staged into a cart that
|
|
193
|
+
// spans folders (browsing away never drops earlier picks), and every staged file is
|
|
194
|
+
// linked as its own living fragment on submit. Only offered when the App/PAT is
|
|
195
|
+
// connected; other sources (Confluence/Notion) keep the single free-text ref field.
|
|
193
196
|
const isGithubDoc = computed(() => docDraft.value.source === 'github')
|
|
194
197
|
const showGithubDocPicker = computed(() => isGithubDoc.value && githubReady.value)
|
|
195
198
|
const docRepoId = ref<number | undefined>(undefined)
|
|
196
199
|
const docRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
197
|
-
|
|
200
|
+
// The staged files (repo-root-relative paths) for the selected repo. Multi-select so
|
|
201
|
+
// any number of files from any nesting level can be linked in one action.
|
|
202
|
+
const docFilePaths = ref<string[]>([])
|
|
198
203
|
|
|
199
|
-
// Reset the picker (and the
|
|
204
|
+
// Reset the picker (and the manual ref) whenever the selected source changes.
|
|
200
205
|
watch(
|
|
201
206
|
() => docDraft.value.source,
|
|
202
207
|
() => {
|
|
203
208
|
docRepoId.value = undefined
|
|
204
209
|
docRepo.value = undefined
|
|
205
|
-
|
|
210
|
+
docFilePaths.value = []
|
|
206
211
|
docDraft.value.ref = ''
|
|
207
212
|
},
|
|
208
213
|
)
|
|
209
214
|
|
|
210
|
-
// A new repo selection clears the previously-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
watch([docRepo, docFilePath], () => {
|
|
217
|
-
if (docRepo.value && docFilePath.value) {
|
|
218
|
-
docDraft.value.ref = `${docRepo.value.owner}/${docRepo.value.name}:${docFilePath.value}`
|
|
219
|
-
}
|
|
215
|
+
// A new repo selection clears the previously-staged files (they were repo-scoped).
|
|
216
|
+
// When the repo is fully deselected, drop the cached repo too so no stale `docRepo`
|
|
217
|
+
// lingers behind a now-empty picker.
|
|
218
|
+
watch(docRepoId, (id) => {
|
|
219
|
+
docFilePaths.value = []
|
|
220
|
+
if (id === undefined) docRepo.value = undefined
|
|
220
221
|
})
|
|
221
222
|
|
|
222
223
|
/** This tier's existing document-backed fragments. */
|
|
223
224
|
const documentFragments = computed(() => library.fragments.filter((f) => f.documentRef))
|
|
224
225
|
|
|
226
|
+
/** Add/remove a browsed file to/from the staged cart (the tree emits `toggle`). */
|
|
227
|
+
function toggleDocFile(path: string) {
|
|
228
|
+
const clean = normalizeRepoPath(path)
|
|
229
|
+
const i = docFilePaths.value.indexOf(clean)
|
|
230
|
+
if (i >= 0) docFilePaths.value.splice(i, 1)
|
|
231
|
+
else docFilePaths.value.push(clean)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Files of the selected repo already linked as fragments — shown "added"/not re-pickable. */
|
|
235
|
+
const docAddedPaths = computed<string[]>(() => {
|
|
236
|
+
const repo = docRepo.value
|
|
237
|
+
if (!repo) return []
|
|
238
|
+
const prefix = `${repo.owner}/${repo.name}:`
|
|
239
|
+
return documentFragments.value
|
|
240
|
+
.filter(
|
|
241
|
+
(f) => f.documentRef?.source === 'github' && f.documentRef.externalId.startsWith(prefix),
|
|
242
|
+
)
|
|
243
|
+
.map((f) => f.documentRef!.externalId.slice(prefix.length))
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
/** The canonical `owner/repo:path` refs for the staged files. */
|
|
247
|
+
const stagedDocRefs = computed(() =>
|
|
248
|
+
docRepo.value
|
|
249
|
+
? docFilePaths.value.map((path) => ({
|
|
250
|
+
path,
|
|
251
|
+
ref: `${docRepo.value!.owner}/${docRepo.value!.name}:${path}`,
|
|
252
|
+
}))
|
|
253
|
+
: [],
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
/** When the rich picker drives the ref(s); otherwise the free-text field does. */
|
|
257
|
+
const usingDocPicker = computed(() => showGithubDocPicker.value)
|
|
258
|
+
|
|
225
259
|
async function linkDocumentFragment() {
|
|
226
260
|
if (!docDraftValid.value) return
|
|
227
261
|
linkingDoc.value = true
|
|
228
262
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
263
|
+
const source = docDraft.value.source as DocumentSourceKind
|
|
264
|
+
const tags = docDraft.value.tags
|
|
265
|
+
.split(',')
|
|
266
|
+
.map((t) => t.trim())
|
|
267
|
+
.filter(Boolean)
|
|
268
|
+
|
|
269
|
+
if (!usingDocPicker.value) {
|
|
270
|
+
// Free-text field: exactly one ref.
|
|
271
|
+
await library.createDocumentFragment({ source, ref: docDraft.value.ref.trim(), tags })
|
|
272
|
+
docDraft.value = { source: '', ref: '', tags: '' }
|
|
273
|
+
toast.add({ title: t('fragments.toast.documentLinked'), icon: 'i-lucide-link' })
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// The picker stages many files (one fragment each). Link them serially — serial, not
|
|
278
|
+
// parallel, so one bad ref can't be masked by a burst of concurrent failures — and drop
|
|
279
|
+
// each from the cart the moment it succeeds. Every staged file is attempted even if an
|
|
280
|
+
// earlier one fails, so a single unlinkable ref never blocks the rest: the successes are
|
|
281
|
+
// linked and removed from the cart, and only the failures stay staged (with the first
|
|
282
|
+
// error surfaced) so a retry re-attempts just those, never re-linking what already went in.
|
|
283
|
+
let linked = 0
|
|
284
|
+
let firstError: unknown
|
|
285
|
+
for (const { path, ref } of stagedDocRefs.value) {
|
|
286
|
+
try {
|
|
287
|
+
await library.createDocumentFragment({ source, ref, tags })
|
|
288
|
+
const i = docFilePaths.value.indexOf(path)
|
|
289
|
+
if (i >= 0) docFilePaths.value.splice(i, 1)
|
|
290
|
+
linked++
|
|
291
|
+
} catch (e) {
|
|
292
|
+
firstError ??= e
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (linked > 0) {
|
|
296
|
+
// A vue-i18n plural message (count as the plural choice) so Slavic few/many forms
|
|
297
|
+
// render correctly, and the count==1 case reads naturally too.
|
|
298
|
+
toast.add({
|
|
299
|
+
title: t('fragments.toast.documentsLinked', { count: linked }, linked),
|
|
300
|
+
icon: 'i-lucide-link',
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
if (firstError) {
|
|
304
|
+
notifyError(t('fragments.toast.linkDocumentFailed'), firstError)
|
|
305
|
+
} else {
|
|
306
|
+
// Everything staged linked — clear the whole draft (also resets repo/source selection).
|
|
307
|
+
docDraft.value = { source: '', ref: '', tags: '' }
|
|
308
|
+
}
|
|
239
309
|
} catch (e) {
|
|
240
310
|
notifyError(t('fragments.toast.linkDocumentFailed'), e)
|
|
241
311
|
} finally {
|
|
@@ -584,7 +654,7 @@ async function unlinkSource(id: string) {
|
|
|
584
654
|
</UButton>
|
|
585
655
|
</div>
|
|
586
656
|
|
|
587
|
-
<!-- GitHub: search a repo + browse to
|
|
657
|
+
<!-- GitHub: search a repo + browse to one or more files instead of typing the ref -->
|
|
588
658
|
<template v-if="showGithubDocPicker">
|
|
589
659
|
<GitHubRepoSearchSelect v-model="docRepoId" @update:repo="docRepo = $event" />
|
|
590
660
|
<div
|
|
@@ -594,11 +664,47 @@ async function unlinkSource(id: string) {
|
|
|
594
664
|
<p class="mb-2 text-xs text-slate-400">
|
|
595
665
|
{{ t('fragments.documents.githubBrowseHint') }}
|
|
596
666
|
</p>
|
|
597
|
-
<RepoTreeBrowser
|
|
667
|
+
<RepoTreeBrowser
|
|
668
|
+
:repo-github-id="docRepoId"
|
|
669
|
+
mode="file"
|
|
670
|
+
multiple
|
|
671
|
+
:selected-paths="docFilePaths"
|
|
672
|
+
:added-paths="docAddedPaths"
|
|
673
|
+
@toggle="toggleDocFile"
|
|
674
|
+
/>
|
|
675
|
+
<div v-if="stagedDocRefs.length" class="mt-2 space-y-1">
|
|
676
|
+
<p class="text-xs font-medium text-slate-400">
|
|
677
|
+
{{ t('fragments.documents.selectedFiles', { count: stagedDocRefs.length }) }}
|
|
678
|
+
</p>
|
|
679
|
+
<div
|
|
680
|
+
v-for="staged in stagedDocRefs"
|
|
681
|
+
:key="staged.path"
|
|
682
|
+
class="flex items-center gap-1.5 rounded bg-slate-800/60 px-2 py-1 text-xs text-slate-300"
|
|
683
|
+
>
|
|
684
|
+
<UIcon
|
|
685
|
+
name="i-lucide-file-code-2"
|
|
686
|
+
class="h-3.5 w-3.5 shrink-0 text-indigo-400"
|
|
687
|
+
/>
|
|
688
|
+
<span class="truncate">{{ staged.path }}</span>
|
|
689
|
+
<UButton
|
|
690
|
+
class="ms-auto"
|
|
691
|
+
color="neutral"
|
|
692
|
+
variant="ghost"
|
|
693
|
+
size="xs"
|
|
694
|
+
icon="i-lucide-x"
|
|
695
|
+
:aria-label="t('fragments.documents.removeFile')"
|
|
696
|
+
@click="toggleDocFile(staged.path)"
|
|
697
|
+
/>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
598
700
|
</div>
|
|
599
701
|
</template>
|
|
600
702
|
|
|
601
|
-
<UInput
|
|
703
|
+
<UInput
|
|
704
|
+
v-if="!usingDocPicker"
|
|
705
|
+
v-model="docDraft.ref"
|
|
706
|
+
:placeholder="t('fragments.documents.refPlaceholder')"
|
|
707
|
+
/>
|
|
602
708
|
<UInput
|
|
603
709
|
v-model="docDraft.tags"
|
|
604
710
|
:placeholder="t('fragments.documents.tagsPlaceholder')"
|
|
@@ -34,8 +34,13 @@ const belowMinChars = computed(() => repoQueryRaw.value.length < MIN_SEARCH_LEN)
|
|
|
34
34
|
const selectedRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
35
35
|
|
|
36
36
|
function toRepoItem(r: GitHubAvailableRepo) {
|
|
37
|
-
const
|
|
38
|
-
|
|
37
|
+
const priv = r.private ? t('github.addService.repoLabel.private') : ''
|
|
38
|
+
// A repo surfaced only through the signed-in user's PAT (the workspace App can't reach it):
|
|
39
|
+
// badge it so the user knows this pick rides their personal token. On hosted, a doc/fragment
|
|
40
|
+
// linked from it resolves through the App at run time, so a personal-only repo will fail the
|
|
41
|
+
// link with a clear error — the badge sets that expectation up front.
|
|
42
|
+
const personal = r.personal ? t('github.addService.repoLabel.personal') : ''
|
|
43
|
+
return { label: `${r.owner}/${r.name}${priv}${personal}`, value: r.githubId }
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
const repoItems = computed(() => github.availableRepos.map(toRepoItem))
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// Startup advisory for unhealthy pipelines. Opened once per session from the board page when
|
|
3
3
|
// `usePipelineHealth` reports any issue. Lists:
|
|
4
|
+
// • new built-in pipelines the workspace doesn't have yet (ADD them);
|
|
4
5
|
// • invalid pipelines (unknown agent kind / bad shape) — DELETE a custom one, RESEED a built-in;
|
|
5
6
|
// • outdated built-ins (a newer catalog definition is available) — RESEED to adopt it.
|
|
6
|
-
//
|
|
7
|
+
// Adding a new built-in and reseeding an existing one are the same reseed call (it creates or
|
|
8
|
+
// updates by catalog id). Detection is client-side (see usePipelineHealth); the actions hit the
|
|
9
|
+
// pipelines store.
|
|
7
10
|
const { t } = useI18n()
|
|
8
11
|
const ui = useUiStore()
|
|
9
12
|
const pipelines = usePipelinesStore()
|
|
10
|
-
const { invalid, outdated, hasIssues } = usePipelineHealth()
|
|
13
|
+
const { invalid, outdated, newPipelines, hasIssues } = usePipelineHealth()
|
|
11
14
|
const toast = useToast()
|
|
12
15
|
|
|
13
16
|
const open = computed({
|
|
@@ -45,17 +48,20 @@ const reseed = (id: string) =>
|
|
|
45
48
|
const remove = (id: string) =>
|
|
46
49
|
run(id, () => pipelines.removePipeline(id), t('pipeline.health.toast.deleteFailed'))
|
|
47
50
|
|
|
48
|
-
/** Reseed every reseedable pipeline (outdated built-ins + invalid built-ins) in one go. */
|
|
51
|
+
/** Reseed every reseedable pipeline (new + outdated built-ins + invalid built-ins) in one go. */
|
|
49
52
|
async function reseedAll() {
|
|
50
|
-
const ids = [
|
|
51
|
-
(
|
|
52
|
-
|
|
53
|
+
const ids = [
|
|
54
|
+
...newPipelines.value.map((p) => p.id),
|
|
55
|
+
...invalid.value.filter((h) => h.pipeline.builtin).map((h) => h.pipeline.id),
|
|
56
|
+
...outdated.value.map((h) => h.pipeline.id),
|
|
57
|
+
]
|
|
53
58
|
for (const id of new Set(ids)) await reseed(id)
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
const reseedableCount = computed(
|
|
57
62
|
() =>
|
|
58
63
|
new Set([
|
|
64
|
+
...newPipelines.value.map((p) => p.id),
|
|
59
65
|
...invalid.value.filter((h) => h.pipeline.builtin).map((h) => h.pipeline.id),
|
|
60
66
|
...outdated.value.map((h) => h.pipeline.id),
|
|
61
67
|
]).size,
|
|
@@ -71,6 +77,41 @@ const reseedableCount = computed(
|
|
|
71
77
|
</div>
|
|
72
78
|
|
|
73
79
|
<div v-else class="space-y-5">
|
|
80
|
+
<!-- New built-in pipelines the workspace can add. -->
|
|
81
|
+
<section v-if="newPipelines.length" class="space-y-2">
|
|
82
|
+
<div class="flex items-center gap-2">
|
|
83
|
+
<UIcon name="i-lucide-sparkles" class="h-4 w-4 text-emerald-400" />
|
|
84
|
+
<h3 class="text-sm font-semibold text-slate-200">
|
|
85
|
+
{{ t('pipeline.health.newHeading') }}
|
|
86
|
+
</h3>
|
|
87
|
+
</div>
|
|
88
|
+
<p class="text-[11px] text-slate-500">{{ t('pipeline.health.newDescription') }}</p>
|
|
89
|
+
<ul class="space-y-2">
|
|
90
|
+
<li
|
|
91
|
+
v-for="p in newPipelines"
|
|
92
|
+
:key="p.id"
|
|
93
|
+
class="flex items-center justify-between gap-3 rounded-lg border border-slate-800 bg-slate-900/40 p-3"
|
|
94
|
+
>
|
|
95
|
+
<div class="min-w-0">
|
|
96
|
+
<span class="truncate text-sm font-medium text-slate-100 capitalize">{{
|
|
97
|
+
p.name
|
|
98
|
+
}}</span>
|
|
99
|
+
</div>
|
|
100
|
+
<UButton
|
|
101
|
+
size="xs"
|
|
102
|
+
color="primary"
|
|
103
|
+
variant="subtle"
|
|
104
|
+
icon="i-lucide-plus"
|
|
105
|
+
:loading="isBusy(p.id)"
|
|
106
|
+
:disabled="anyBusy"
|
|
107
|
+
@click="reseed(p.id)"
|
|
108
|
+
>
|
|
109
|
+
{{ t('pipeline.health.add') }}
|
|
110
|
+
</UButton>
|
|
111
|
+
</li>
|
|
112
|
+
</ul>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
74
115
|
<!-- Invalid: unknown agent kinds or a broken shape. -->
|
|
75
116
|
<section v-if="invalid.length" class="space-y-2">
|
|
76
117
|
<div class="flex items-center gap-2">
|
|
@@ -142,4 +142,26 @@ describe('usePipelineHealth', () => {
|
|
|
142
142
|
expect(invalid.value).toHaveLength(1)
|
|
143
143
|
expect(outdated.value).toHaveLength(0)
|
|
144
144
|
})
|
|
145
|
+
|
|
146
|
+
it('surfaces a brand-new built-in the workspace does not have yet (offer to add)', () => {
|
|
147
|
+
// A board seeded before `pl_review` shipped: the catalog versions advertise it, but no stored
|
|
148
|
+
// pipeline has that id — so it must appear as a "new" pipeline the user can add.
|
|
149
|
+
const stored = builtin(['coder', 'reviewer'], { id: 'pl_full', version: 1 })
|
|
150
|
+
const { newPipelines, hasIssues, invalid, outdated } = scan([stored], {
|
|
151
|
+
pl_full: 1,
|
|
152
|
+
pl_review: 4,
|
|
153
|
+
})
|
|
154
|
+
expect(newPipelines.value).toEqual([{ id: 'pl_review', name: 'review' }])
|
|
155
|
+
expect(hasIssues.value).toBe(true)
|
|
156
|
+
// A brand-new built-in is not "invalid" or "outdated" — those only concern STORED pipelines.
|
|
157
|
+
expect(invalid.value).toHaveLength(0)
|
|
158
|
+
expect(outdated.value).toHaveLength(0)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('reports no new pipelines when every catalog id is already stored', () => {
|
|
162
|
+
const stored = builtin(['coder', 'reviewer'], { id: 'pl_full', version: 1 })
|
|
163
|
+
const { newPipelines, hasIssues } = scan([stored], { pl_full: 1 })
|
|
164
|
+
expect(newPipelines.value).toHaveLength(0)
|
|
165
|
+
expect(hasIssues.value).toBe(false)
|
|
166
|
+
})
|
|
145
167
|
})
|
|
@@ -23,6 +23,23 @@ export interface PipelineHealth {
|
|
|
23
23
|
outdated: boolean
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** A brand-new built-in pipeline that appeared in the catalog but isn't in the workspace yet. */
|
|
27
|
+
export interface NewPipeline {
|
|
28
|
+
/** The catalog (built-in) id — what the reseed endpoint is keyed by (it creates the row). */
|
|
29
|
+
id: string
|
|
30
|
+
/** The built-in's display name, from the catalog versions' companion name map. */
|
|
31
|
+
name: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A built-in's display name for the "new pipeline" advisory, humanised from its catalog id
|
|
36
|
+
* (`pl_review` -> "review", rendered capitalised) — used only until the row is reseeded into
|
|
37
|
+
* existence, at which point its real catalog name is stored. Mirrors `useRiskPolicyHealth`.
|
|
38
|
+
*/
|
|
39
|
+
function builtinPipelineName(id: string): string {
|
|
40
|
+
return id.replace(/^pl_/, '').replace(/_/g, ' ')
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
/** Producers a companion kind is allowed to review (inverse of {@link COMPANION_FOR_PRODUCER}). */
|
|
27
44
|
function companionTargets(companion: string): string[] {
|
|
28
45
|
return Object.entries(COMPANION_FOR_PRODUCER)
|
|
@@ -88,12 +105,14 @@ function shapeProblem(p: Pipeline): string | null {
|
|
|
88
105
|
|
|
89
106
|
/**
|
|
90
107
|
* Detect pipelines in an unhealthy state for the startup advisory: those referencing an unknown
|
|
91
|
-
* agent kind or with an invalid shape (offer to delete a custom one / reseed a built-in),
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
108
|
+
* agent kind or with an invalid shape (offer to delete a custom one / reseed a built-in), built-ins
|
|
109
|
+
* whose seeded definition has moved ahead of the stored copy (offer to reseed), AND brand-new
|
|
110
|
+
* built-ins that appeared in the catalog but aren't in the workspace yet (offer to ADD them — a
|
|
111
|
+
* board seeded before the built-in shipped, e.g. `pl_review`). Reads the pipeline library + the
|
|
112
|
+
* snapshot's catalog versions from the pipelines store. Detection runs entirely client-side: the
|
|
113
|
+
* canonical agent-kind catalog lives here (`AGENT_BY_KIND` + `SYSTEM_AGENT_META` + registered custom
|
|
114
|
+
* kinds), and the catalog versions the snapshot ships ARE the set of built-in ids — a catalog id
|
|
115
|
+
* with no stored pipeline is a new built-in. Mirrors `useRiskPolicyHealth` / `useModelPresetHealth`.
|
|
97
116
|
*/
|
|
98
117
|
export function usePipelineHealth() {
|
|
99
118
|
const store = usePipelinesStore()
|
|
@@ -130,11 +149,20 @@ export function usePipelineHealth() {
|
|
|
130
149
|
return out
|
|
131
150
|
})
|
|
132
151
|
|
|
152
|
+
// Brand-new built-ins: a catalog id (a `catalogVersions` key) with no stored pipeline. Adding one
|
|
153
|
+
// is the same reseed call as adopting an update (it inserts the row when absent).
|
|
154
|
+
const newPipelines = computed<NewPipeline[]>(() => {
|
|
155
|
+
const storedIds = new Set(store.pipelines.map((p) => p.id))
|
|
156
|
+
return Object.keys(store.catalogVersions)
|
|
157
|
+
.filter((id) => !storedIds.has(id))
|
|
158
|
+
.map((id) => ({ id, name: builtinPipelineName(id) }))
|
|
159
|
+
})
|
|
160
|
+
|
|
133
161
|
// An invalid built-in is reseeded (not deleted) and that also clears any "outdated" flag, so
|
|
134
162
|
// exclude it from the outdated list to avoid offering the same fix twice.
|
|
135
163
|
const invalid = computed(() => health.value.filter((h) => h.invalid))
|
|
136
164
|
const outdated = computed(() => health.value.filter((h) => h.outdated && !h.invalid))
|
|
137
|
-
const hasIssues = computed(() => health.value.length > 0)
|
|
165
|
+
const hasIssues = computed(() => health.value.length > 0 || newPipelines.value.length > 0)
|
|
138
166
|
|
|
139
|
-
return { health, invalid, outdated, hasIssues }
|
|
167
|
+
return { health, invalid, outdated, newPipelines, hasIssues }
|
|
140
168
|
}
|
|
@@ -17,10 +17,19 @@ describe('pipelineAllowedForTaskType', () => {
|
|
|
17
17
|
expect(pipelineAllowedForTaskType(pipeline({ purpose: undefined }), 'document')).toBe(false)
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
it('
|
|
21
|
-
|
|
20
|
+
it('a review task offers ONLY review-purpose pipelines', () => {
|
|
21
|
+
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'review' }), 'review')).toBe(true)
|
|
22
|
+
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'build' }), 'review')).toBe(false)
|
|
23
|
+
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'document' }), 'review')).toBe(false)
|
|
24
|
+
// An unclassified pipeline is hidden from a review task (it requires the explicit classifier).
|
|
25
|
+
expect(pipelineAllowedForTaskType(pipeline({ purpose: undefined }), 'review')).toBe(false)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('every other task type is unrestricted (any purpose, and undefined type)', () => {
|
|
29
|
+
for (const type of ['feature', 'bug', 'spike', 'ralph', undefined] as const) {
|
|
22
30
|
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'build' }), type)).toBe(true)
|
|
23
31
|
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'document' }), type)).toBe(true)
|
|
32
|
+
expect(pipelineAllowedForTaskType(pipeline({ purpose: 'review' }), type)).toBe(true)
|
|
24
33
|
expect(pipelineAllowedForTaskType(pipeline({ purpose: undefined }), type)).toBe(true)
|
|
25
34
|
}
|
|
26
35
|
})
|
|
@@ -51,10 +60,15 @@ describe('pipelineAllowedForManualStart composes the task-type gate', () => {
|
|
|
51
60
|
const noFrame = undefined
|
|
52
61
|
const blocks: Block[] = []
|
|
53
62
|
|
|
54
|
-
it('drops a
|
|
63
|
+
it('drops a mismatched pipeline for a document / review task, keeps it for others', () => {
|
|
55
64
|
const build = pipeline({ purpose: 'build' })
|
|
56
65
|
expect(pipelineAllowedForManualStart(build, noFrame, blocks, 'document')).toBe(false)
|
|
66
|
+
expect(pipelineAllowedForManualStart(build, noFrame, blocks, 'review')).toBe(false)
|
|
57
67
|
expect(pipelineAllowedForManualStart(build, noFrame, blocks, 'feature')).toBe(true)
|
|
68
|
+
// A review pipeline is offered to a review task and hidden from a document task.
|
|
69
|
+
const review = pipeline({ purpose: 'review' })
|
|
70
|
+
expect(pipelineAllowedForManualStart(review, noFrame, blocks, 'review')).toBe(true)
|
|
71
|
+
expect(pipelineAllowedForManualStart(review, noFrame, blocks, 'document')).toBe(false)
|
|
58
72
|
// No task type supplied ⇒ no task-type restriction.
|
|
59
73
|
expect(pipelineAllowedForManualStart(build, noFrame, blocks)).toBe(true)
|
|
60
74
|
})
|
package/i18n/locales/de.json
CHANGED
|
@@ -3260,6 +3260,9 @@
|
|
|
3260
3260
|
"invalidHeading": "Ungültige Pipelines",
|
|
3261
3261
|
"invalidDescription": "Diese verweisen auf einen fehlenden Agenten oder sind falsch konfiguriert, sodass sie beim Start fehlschlagen (oder falsch laufen) würden. Löschen Sie eine benutzerdefinierte Pipeline oder setzen Sie eine integrierte neu auf, um ihre Katalogdefinition wiederherzustellen.",
|
|
3262
3262
|
"builtinBadge": "integriert",
|
|
3263
|
+
"newHeading": "Neue Pipelines verfügbar",
|
|
3264
|
+
"newDescription": "Neue integrierte Pipelines sind verfügbar. Füge sie zur Bibliothek dieses Boards hinzu.",
|
|
3265
|
+
"add": "Hinzufügen",
|
|
3263
3266
|
"reseed": "Neu aufsetzen",
|
|
3264
3267
|
"delete": "Löschen",
|
|
3265
3268
|
"updatesHeading": "Updates verfügbar",
|
|
@@ -3561,7 +3564,9 @@
|
|
|
3561
3564
|
"refPlaceholder": "Seiten-ID oder URL (z. B. eine Confluence-/Notion-Seite oder GitHub-Datei-URL)",
|
|
3562
3565
|
"tagsPlaceholder": "Tags, kommagetrennt (optional)",
|
|
3563
3566
|
"link": "Als lebendes Fragment verknüpfen",
|
|
3564
|
-
"githubBrowseHint": "Durchsuchen Sie das Repo und wählen Sie
|
|
3567
|
+
"githubBrowseHint": "Durchsuchen Sie das Repo und wählen Sie eine oder mehrere zu verknüpfende Dateien.",
|
|
3568
|
+
"selectedFiles": "Ausgewählte Dateien ({count})",
|
|
3569
|
+
"removeFile": "Datei entfernen"
|
|
3565
3570
|
},
|
|
3566
3571
|
"sources": {
|
|
3567
3572
|
"metaSynced": "synchronisiert · ref {ref}",
|
|
@@ -3595,7 +3600,8 @@
|
|
|
3595
3600
|
"upToDate": "Aktuell",
|
|
3596
3601
|
"checkSourceFailed": "Quelle konnte nicht geprüft werden",
|
|
3597
3602
|
"sourceUnlinked": "Quelle-Verknüpfung aufgehoben",
|
|
3598
|
-
"unlinkSourceFailed": "Quelle-Verknüpfung konnte nicht aufgehoben werden"
|
|
3603
|
+
"unlinkSourceFailed": "Quelle-Verknüpfung konnte nicht aufgehoben werden",
|
|
3604
|
+
"documentsLinked": "{count} Dokument als lebendes Fragment verknüpft | {count} Dokumente als lebende Fragmente verknüpft"
|
|
3599
3605
|
},
|
|
3600
3606
|
"confirmRemove": {
|
|
3601
3607
|
"title": "Dieses Fragment löschen?",
|
package/i18n/locales/en.json
CHANGED
|
@@ -3626,6 +3626,9 @@
|
|
|
3626
3626
|
"invalidHeading": "Invalid pipelines",
|
|
3627
3627
|
"invalidDescription": "These reference a missing agent or are misconfigured, so they would fail (or misrun) at start. Delete a custom pipeline, or reseed a built-in to restore its catalog definition.",
|
|
3628
3628
|
"builtinBadge": "built-in",
|
|
3629
|
+
"newHeading": "New pipelines available",
|
|
3630
|
+
"newDescription": "New built-in pipelines have shipped. Add them to this board's library.",
|
|
3631
|
+
"add": "Add",
|
|
3629
3632
|
"reseed": "Reseed",
|
|
3630
3633
|
"delete": "Delete",
|
|
3631
3634
|
"updatesHeading": "Updates available",
|
|
@@ -4561,7 +4564,9 @@
|
|
|
4561
4564
|
"refPlaceholder": "Page id or URL (e.g. a Confluence/Notion page or GitHub file URL)",
|
|
4562
4565
|
"tagsPlaceholder": "Tags, comma-separated (optional)",
|
|
4563
4566
|
"link": "Link as living fragment",
|
|
4564
|
-
"githubBrowseHint": "Browse the repo and pick
|
|
4567
|
+
"githubBrowseHint": "Browse the repo and pick one or more files to link.",
|
|
4568
|
+
"selectedFiles": "Selected files ({count})",
|
|
4569
|
+
"removeFile": "Remove file"
|
|
4565
4570
|
},
|
|
4566
4571
|
"sources": {
|
|
4567
4572
|
"metaSynced": "synced · ref {ref}",
|
|
@@ -4595,7 +4600,8 @@
|
|
|
4595
4600
|
"upToDate": "Up to date",
|
|
4596
4601
|
"checkSourceFailed": "Could not check source",
|
|
4597
4602
|
"sourceUnlinked": "Source unlinked",
|
|
4598
|
-
"unlinkSourceFailed": "Could not unlink source"
|
|
4603
|
+
"unlinkSourceFailed": "Could not unlink source",
|
|
4604
|
+
"documentsLinked": "Linked {count} document as a living fragment | Linked {count} documents as living fragments"
|
|
4599
4605
|
},
|
|
4600
4606
|
"confirmRemove": {
|
|
4601
4607
|
"title": "Delete this fragment?",
|
package/i18n/locales/es.json
CHANGED
|
@@ -3527,6 +3527,9 @@
|
|
|
3527
3527
|
"invalidHeading": "Pipelines no válidos",
|
|
3528
3528
|
"invalidDescription": "Estos hacen referencia a un agente inexistente o están mal configurados, por lo que fallarían (o se ejecutarían mal) al iniciar. Elimina un pipeline personalizado o vuelve a generar uno integrado para restaurar su definición del catálogo.",
|
|
3529
3529
|
"builtinBadge": "integrado",
|
|
3530
|
+
"newHeading": "Nuevos pipelines disponibles",
|
|
3531
|
+
"newDescription": "Hay nuevos pipelines integrados. Añádelos a la biblioteca de este tablero.",
|
|
3532
|
+
"add": "Añadir",
|
|
3530
3533
|
"reseed": "Regenerar",
|
|
3531
3534
|
"delete": "Eliminar",
|
|
3532
3535
|
"updatesHeading": "Actualizaciones disponibles",
|
|
@@ -4385,7 +4388,9 @@
|
|
|
4385
4388
|
"refPlaceholder": "Id o URL de página (p. ej., una página de Confluence/Notion o la URL de un archivo de GitHub)",
|
|
4386
4389
|
"tagsPlaceholder": "Etiquetas, separadas por comas (opcional)",
|
|
4387
4390
|
"link": "Vincular como fragmento vivo",
|
|
4388
|
-
"githubBrowseHint": "Explora el repositorio y elige
|
|
4391
|
+
"githubBrowseHint": "Explora el repositorio y elige uno o más archivos a enlazar.",
|
|
4392
|
+
"selectedFiles": "Archivos seleccionados ({count})",
|
|
4393
|
+
"removeFile": "Quitar archivo"
|
|
4389
4394
|
},
|
|
4390
4395
|
"sources": {
|
|
4391
4396
|
"metaSynced": "sincronizado · ref {ref}",
|
|
@@ -4419,7 +4424,8 @@
|
|
|
4419
4424
|
"upToDate": "Al día",
|
|
4420
4425
|
"checkSourceFailed": "No se pudo comprobar el origen",
|
|
4421
4426
|
"sourceUnlinked": "Origen desvinculado",
|
|
4422
|
-
"unlinkSourceFailed": "No se pudo desvincular el origen"
|
|
4427
|
+
"unlinkSourceFailed": "No se pudo desvincular el origen",
|
|
4428
|
+
"documentsLinked": "{count} documento vinculado como fragmento vivo | {count} documentos vinculados como fragmentos vivos"
|
|
4423
4429
|
},
|
|
4424
4430
|
"confirmRemove": {
|
|
4425
4431
|
"title": "¿Eliminar este fragmento?",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -3527,6 +3527,9 @@
|
|
|
3527
3527
|
"invalidHeading": "Pipelines non valides",
|
|
3528
3528
|
"invalidDescription": "Ceux-ci référencent un agent manquant ou sont mal configurés et échoueraient (ou s'exécuteraient mal) au démarrage. Supprimez un pipeline personnalisé, ou régénérez un pipeline intégré pour restaurer sa définition du catalogue.",
|
|
3529
3529
|
"builtinBadge": "intégré",
|
|
3530
|
+
"newHeading": "Nouveaux pipelines disponibles",
|
|
3531
|
+
"newDescription": "De nouveaux pipelines intégrés sont arrivés. Ajoutez-les à la bibliothèque de ce tableau.",
|
|
3532
|
+
"add": "Ajouter",
|
|
3530
3533
|
"reseed": "Régénérer",
|
|
3531
3534
|
"delete": "Supprimer",
|
|
3532
3535
|
"updatesHeading": "Mises à jour disponibles",
|
|
@@ -4385,7 +4388,9 @@
|
|
|
4385
4388
|
"refPlaceholder": "Id ou URL de page (par ex. une page Confluence/Notion ou l'URL d'un fichier GitHub)",
|
|
4386
4389
|
"tagsPlaceholder": "Étiquettes, séparées par des virgules (facultatif)",
|
|
4387
4390
|
"link": "Lier comme fragment vivant",
|
|
4388
|
-
"githubBrowseHint": "Parcourez le dépôt et choisissez
|
|
4391
|
+
"githubBrowseHint": "Parcourez le dépôt et choisissez un ou plusieurs fichiers à lier.",
|
|
4392
|
+
"selectedFiles": "Fichiers sélectionnés ({count})",
|
|
4393
|
+
"removeFile": "Retirer le fichier"
|
|
4389
4394
|
},
|
|
4390
4395
|
"sources": {
|
|
4391
4396
|
"metaSynced": "synchronisé · ref {ref}",
|
|
@@ -4419,7 +4424,8 @@
|
|
|
4419
4424
|
"upToDate": "À jour",
|
|
4420
4425
|
"checkSourceFailed": "Impossible de vérifier la source",
|
|
4421
4426
|
"sourceUnlinked": "Source dissociée",
|
|
4422
|
-
"unlinkSourceFailed": "Impossible de dissocier la source"
|
|
4427
|
+
"unlinkSourceFailed": "Impossible de dissocier la source",
|
|
4428
|
+
"documentsLinked": "{count} document lié comme fragment vivant | {count} documents liés comme fragments vivants"
|
|
4423
4429
|
},
|
|
4424
4430
|
"confirmRemove": {
|
|
4425
4431
|
"title": "Supprimer ce fragment ?",
|
package/i18n/locales/he.json
CHANGED
|
@@ -3538,6 +3538,9 @@
|
|
|
3538
3538
|
"invalidHeading": "צינורות לא תקפים",
|
|
3539
3539
|
"invalidDescription": "אלה מפנים לסוכן חסר או מוגדרים בצורה שגויה, ולכן ייכשלו (או ירוצו בצורה שגויה) בהתחלה. מחק צינור מותאם אישית, או זרע מחדש צינור מובנה כדי לשחזר את הגדרת הקטלוג שלו.",
|
|
3540
3540
|
"builtinBadge": "מובנה",
|
|
3541
|
+
"newHeading": "צינורות חדשים זמינים",
|
|
3542
|
+
"newDescription": "הגיעו צינורות מובנים חדשים. הוסף אותם לספריית הלוח הזה.",
|
|
3543
|
+
"add": "הוסף",
|
|
3541
3544
|
"reseed": "זרע מחדש",
|
|
3542
3545
|
"delete": "מחק",
|
|
3543
3546
|
"updatesHeading": "עדכונים זמינים",
|
|
@@ -4396,7 +4399,9 @@
|
|
|
4396
4399
|
"refPlaceholder": "מזהה עמוד או כתובת (למשל עמוד Confluence/Notion או כתובת קובץ GitHub)",
|
|
4397
4400
|
"tagsPlaceholder": "תגיות, מופרדות בפסיקים (אופציונלי)",
|
|
4398
4401
|
"link": "קשר כמקטע חי",
|
|
4399
|
-
"githubBrowseHint": "עיין במאגר ובחר
|
|
4402
|
+
"githubBrowseHint": "עיין במאגר ובחר קובץ אחד או יותר לקישור.",
|
|
4403
|
+
"selectedFiles": "קבצים שנבחרו ({count})",
|
|
4404
|
+
"removeFile": "הסר קובץ"
|
|
4400
4405
|
},
|
|
4401
4406
|
"sources": {
|
|
4402
4407
|
"metaSynced": "סונכרן · ref {ref}",
|
|
@@ -4430,7 +4435,8 @@
|
|
|
4430
4435
|
"upToDate": "מעודכן",
|
|
4431
4436
|
"checkSourceFailed": "לא ניתן היה לבדוק את המקור",
|
|
4432
4437
|
"sourceUnlinked": "קישור המקור בוטל",
|
|
4433
|
-
"unlinkSourceFailed": "לא ניתן היה לבטל את קישור המקור"
|
|
4438
|
+
"unlinkSourceFailed": "לא ניתן היה לבטל את קישור המקור",
|
|
4439
|
+
"documentsLinked": "{count} מסמך קושר כמקטע חי | {count} מסמכים קושרו כמקטעים חיים"
|
|
4434
4440
|
},
|
|
4435
4441
|
"confirmRemove": {
|
|
4436
4442
|
"title": "למחוק את המקטע הזה?",
|
package/i18n/locales/it.json
CHANGED
|
@@ -3260,6 +3260,9 @@
|
|
|
3260
3260
|
"invalidHeading": "Pipeline non valide",
|
|
3261
3261
|
"invalidDescription": "Queste fanno riferimento a un agente mancante o sono configurate male, quindi fallirebbero (o funzionerebbero male) all'avvio. Elimina una pipeline personalizzata, oppure ripristina una integrata per recuperare la sua definizione dal catalogo.",
|
|
3262
3262
|
"builtinBadge": "integrata",
|
|
3263
|
+
"newHeading": "Nuove pipeline disponibili",
|
|
3264
|
+
"newDescription": "Sono state rilasciate nuove pipeline integrate. Aggiungile alla libreria di questa board.",
|
|
3265
|
+
"add": "Aggiungi",
|
|
3263
3266
|
"reseed": "Ripristina",
|
|
3264
3267
|
"delete": "Elimina",
|
|
3265
3268
|
"updatesHeading": "Aggiornamenti disponibili",
|
|
@@ -3561,7 +3564,9 @@
|
|
|
3561
3564
|
"refPlaceholder": "Id o URL della pagina (es. una pagina Confluence/Notion o l'URL di un file GitHub)",
|
|
3562
3565
|
"tagsPlaceholder": "Tag, separati da virgola (opzionale)",
|
|
3563
3566
|
"link": "Collega come frammento vivente",
|
|
3564
|
-
"githubBrowseHint": "Sfoglia il repository e scegli
|
|
3567
|
+
"githubBrowseHint": "Sfoglia il repository e scegli uno o più file da collegare.",
|
|
3568
|
+
"selectedFiles": "File selezionati ({count})",
|
|
3569
|
+
"removeFile": "Rimuovi file"
|
|
3565
3570
|
},
|
|
3566
3571
|
"sources": {
|
|
3567
3572
|
"metaSynced": "sincronizzato · ref {ref}",
|
|
@@ -3595,7 +3600,8 @@
|
|
|
3595
3600
|
"upToDate": "Aggiornato",
|
|
3596
3601
|
"checkSourceFailed": "Impossibile controllare la sorgente",
|
|
3597
3602
|
"sourceUnlinked": "Sorgente scollegata",
|
|
3598
|
-
"unlinkSourceFailed": "Impossibile scollegare la sorgente"
|
|
3603
|
+
"unlinkSourceFailed": "Impossibile scollegare la sorgente",
|
|
3604
|
+
"documentsLinked": "{count} documento collegato come frammento vivente | {count} documenti collegati come frammenti viventi"
|
|
3599
3605
|
},
|
|
3600
3606
|
"confirmRemove": {
|
|
3601
3607
|
"title": "Eliminare questo frammento?",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -3539,6 +3539,9 @@
|
|
|
3539
3539
|
"invalidHeading": "無効なパイプライン",
|
|
3540
3540
|
"invalidDescription": "存在しないエージェントを参照しているか設定が誤っているため、開始時に失敗(または誤動作)します。カスタムパイプラインを削除するか、ビルトインを再シードしてカタログ定義を復元してください。",
|
|
3541
3541
|
"builtinBadge": "ビルトイン",
|
|
3542
|
+
"newHeading": "新しいパイプラインがあります",
|
|
3543
|
+
"newDescription": "新しい組み込みパイプラインが追加されました。このボードのライブラリに追加してください。",
|
|
3544
|
+
"add": "追加",
|
|
3542
3545
|
"reseed": "再シード",
|
|
3543
3546
|
"delete": "削除",
|
|
3544
3547
|
"updatesHeading": "更新あり",
|
|
@@ -4397,7 +4400,9 @@
|
|
|
4397
4400
|
"refPlaceholder": "ページ id または URL (例: Confluence/Notion ページや GitHub ファイル URL)",
|
|
4398
4401
|
"tagsPlaceholder": "タグ、カンマ区切り (任意)",
|
|
4399
4402
|
"link": "リビングフラグメントとしてリンク",
|
|
4400
|
-
"githubBrowseHint": "
|
|
4403
|
+
"githubBrowseHint": "リポジトリを参照してリンクするファイルを1つ以上選択します。",
|
|
4404
|
+
"selectedFiles": "選択したファイル ({count})",
|
|
4405
|
+
"removeFile": "ファイルを削除"
|
|
4401
4406
|
},
|
|
4402
4407
|
"sources": {
|
|
4403
4408
|
"metaSynced": "同期済み · ref {ref}",
|
|
@@ -4431,7 +4436,8 @@
|
|
|
4431
4436
|
"upToDate": "最新です",
|
|
4432
4437
|
"checkSourceFailed": "ソースを確認できませんでした",
|
|
4433
4438
|
"sourceUnlinked": "ソースのリンクを解除しました",
|
|
4434
|
-
"unlinkSourceFailed": "ソースのリンクを解除できませんでした"
|
|
4439
|
+
"unlinkSourceFailed": "ソースのリンクを解除できませんでした",
|
|
4440
|
+
"documentsLinked": "{count}件のドキュメントをリビングフラグメントとしてリンクしました | {count}件のドキュメントをリビングフラグメントとしてリンクしました"
|
|
4435
4441
|
},
|
|
4436
4442
|
"confirmRemove": {
|
|
4437
4443
|
"title": "このフラグメントを削除しますか?",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -3527,6 +3527,9 @@
|
|
|
3527
3527
|
"invalidHeading": "Nieprawidłowe pipeline'y",
|
|
3528
3528
|
"invalidDescription": "Odwołują się do brakującego agenta lub są źle skonfigurowane, więc zawiodłyby (lub wykonałyby się błędnie) przy starcie. Usuń pipeline niestandardowy albo zregeneruj wbudowany, aby przywrócić jego definicję z katalogu.",
|
|
3529
3529
|
"builtinBadge": "wbudowany",
|
|
3530
|
+
"newHeading": "Dostępne nowe pipeline'y",
|
|
3531
|
+
"newDescription": "Pojawiły się nowe wbudowane pipeline'y. Dodaj je do biblioteki tej tablicy.",
|
|
3532
|
+
"add": "Dodaj",
|
|
3530
3533
|
"reseed": "Zregeneruj",
|
|
3531
3534
|
"delete": "Usuń",
|
|
3532
3535
|
"updatesHeading": "Dostępne aktualizacje",
|
|
@@ -4385,7 +4388,9 @@
|
|
|
4385
4388
|
"refPlaceholder": "Id lub URL strony (np. strona Confluence/Notion lub URL pliku GitHub)",
|
|
4386
4389
|
"tagsPlaceholder": "Tagi, oddzielone przecinkami (opcjonalnie)",
|
|
4387
4390
|
"link": "Połącz jako żywy fragment",
|
|
4388
|
-
"githubBrowseHint": "Przeglądaj repozytorium i wybierz
|
|
4391
|
+
"githubBrowseHint": "Przeglądaj repozytorium i wybierz jeden lub więcej plików do połączenia.",
|
|
4392
|
+
"selectedFiles": "Wybrane pliki ({count})",
|
|
4393
|
+
"removeFile": "Usuń plik"
|
|
4389
4394
|
},
|
|
4390
4395
|
"sources": {
|
|
4391
4396
|
"metaSynced": "zsynchronizowano · ref {ref}",
|
|
@@ -4419,7 +4424,8 @@
|
|
|
4419
4424
|
"upToDate": "Aktualne",
|
|
4420
4425
|
"checkSourceFailed": "Nie udało się sprawdzić źródła",
|
|
4421
4426
|
"sourceUnlinked": "Odłączono źródło",
|
|
4422
|
-
"unlinkSourceFailed": "Nie udało się odłączyć źródła"
|
|
4427
|
+
"unlinkSourceFailed": "Nie udało się odłączyć źródła",
|
|
4428
|
+
"documentsLinked": "Połączono {count} dokument jako żywy fragment | Połączono {count} dokumenty jako żywe fragmenty | Połączono {count} dokumentów jako żywe fragmenty"
|
|
4423
4429
|
},
|
|
4424
4430
|
"confirmRemove": {
|
|
4425
4431
|
"title": "Usunąć ten fragment?",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -3539,6 +3539,9 @@
|
|
|
3539
3539
|
"invalidHeading": "Geçersiz pipeline'lar",
|
|
3540
3540
|
"invalidDescription": "Bunlar eksik bir agent'a referans veriyor veya yanlış yapılandırılmış, bu yüzden başlangıçta başarısız olur (veya hatalı çalışır). Özel bir pipeline'ı silin ya da katalog tanımını geri yüklemek için bir yerleşik pipeline'ı yeniden tohumlayın.",
|
|
3541
3541
|
"builtinBadge": "yerleşik",
|
|
3542
|
+
"newHeading": "Yeni pipeline'lar mevcut",
|
|
3543
|
+
"newDescription": "Yeni yerleşik pipeline'lar geldi. Bu panonun kütüphanesine ekleyin.",
|
|
3544
|
+
"add": "Ekle",
|
|
3542
3545
|
"reseed": "Yeniden tohumla",
|
|
3543
3546
|
"delete": "Sil",
|
|
3544
3547
|
"updatesHeading": "Güncellemeler mevcut",
|
|
@@ -4397,7 +4400,9 @@
|
|
|
4397
4400
|
"refPlaceholder": "Sayfa kimliği veya URL (örn. bir Confluence/Notion sayfası veya GitHub dosya URL'si)",
|
|
4398
4401
|
"tagsPlaceholder": "Etiketler, virgülle ayrılmış (isteğe bağlı)",
|
|
4399
4402
|
"link": "Canlı parça olarak bağla",
|
|
4400
|
-
"githubBrowseHint": "Depoya göz atın ve bağlanacak
|
|
4403
|
+
"githubBrowseHint": "Depoya göz atın ve bağlanacak bir veya daha fazla dosya seçin.",
|
|
4404
|
+
"selectedFiles": "Seçilen dosyalar ({count})",
|
|
4405
|
+
"removeFile": "Dosyayı kaldır"
|
|
4401
4406
|
},
|
|
4402
4407
|
"sources": {
|
|
4403
4408
|
"metaSynced": "eşitlendi · ref {ref}",
|
|
@@ -4431,7 +4436,8 @@
|
|
|
4431
4436
|
"upToDate": "Güncel",
|
|
4432
4437
|
"checkSourceFailed": "Kaynak denetlenemedi",
|
|
4433
4438
|
"sourceUnlinked": "Kaynak bağlantısı kaldırıldı",
|
|
4434
|
-
"unlinkSourceFailed": "Kaynak bağlantısı kaldırılamadı"
|
|
4439
|
+
"unlinkSourceFailed": "Kaynak bağlantısı kaldırılamadı",
|
|
4440
|
+
"documentsLinked": "{count} belge canlı parça olarak bağlandı | {count} belge canlı parça olarak bağlandı"
|
|
4435
4441
|
},
|
|
4436
4442
|
"confirmRemove": {
|
|
4437
4443
|
"title": "Bu parça silinsin mi?",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -3527,6 +3527,9 @@
|
|
|
3527
3527
|
"invalidHeading": "Недійсні пайплайни",
|
|
3528
3528
|
"invalidDescription": "Вони посилаються на відсутнього агента або неправильно налаштовані, тож зазнали б помилки (або виконалися б неправильно) під час старту. Видаліть власний пайплайн або перегенеруйте вбудований, щоб відновити його визначення з каталогу.",
|
|
3529
3529
|
"builtinBadge": "вбудований",
|
|
3530
|
+
"newHeading": "Доступні нові пайплайни",
|
|
3531
|
+
"newDescription": "З'явилися нові вбудовані пайплайни. Додайте їх до бібліотеки цієї дошки.",
|
|
3532
|
+
"add": "Додати",
|
|
3530
3533
|
"reseed": "Перегенерувати",
|
|
3531
3534
|
"delete": "Видалити",
|
|
3532
3535
|
"updatesHeading": "Доступні оновлення",
|
|
@@ -4385,7 +4388,9 @@
|
|
|
4385
4388
|
"refPlaceholder": "Id або URL сторінки (напр. сторінка Confluence/Notion чи URL файлу GitHub)",
|
|
4386
4389
|
"tagsPlaceholder": "Теги, розділені комами (необов'язково)",
|
|
4387
4390
|
"link": "Прив'язати як живий фрагмент",
|
|
4388
|
-
"githubBrowseHint": "Перегляньте репозиторій і виберіть
|
|
4391
|
+
"githubBrowseHint": "Перегляньте репозиторій і виберіть один або кілька файлів для зв’язування.",
|
|
4392
|
+
"selectedFiles": "Вибрані файли ({count})",
|
|
4393
|
+
"removeFile": "Видалити файл"
|
|
4389
4394
|
},
|
|
4390
4395
|
"sources": {
|
|
4391
4396
|
"metaSynced": "синхронізовано · ref {ref}",
|
|
@@ -4419,7 +4424,8 @@
|
|
|
4419
4424
|
"upToDate": "Актуально",
|
|
4420
4425
|
"checkSourceFailed": "Не вдалося перевірити джерело",
|
|
4421
4426
|
"sourceUnlinked": "Джерело відв'язано",
|
|
4422
|
-
"unlinkSourceFailed": "Не вдалося відв'язати джерело"
|
|
4427
|
+
"unlinkSourceFailed": "Не вдалося відв'язати джерело",
|
|
4428
|
+
"documentsLinked": "Прив’язано {count} документ як живий фрагмент | Прив’язано {count} документи як живі фрагменти | Прив’язано {count} документів як живі фрагменти"
|
|
4423
4429
|
},
|
|
4424
4430
|
"confirmRemove": {
|
|
4425
4431
|
"title": "Видалити цей фрагмент?",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.142.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",
|
|
@@ -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.151.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|