@cat-factory/app 0.176.0 → 0.178.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/README.md +28 -1
- package/app/components/documents/DocumentImportModal.vue +3 -17
- package/app/components/documents/DocumentTemplatesModal.vue +1 -1
- package/app/components/documents/SpawnPreviewModal.vue +9 -19
- package/app/components/environments/steps/EnvPickStep.vue +3 -0
- package/app/components/layout/CommandBar.vue +1 -1
- package/app/components/layout/IntegrationBackTitle.vue +11 -8
- package/app/components/layout/IntegrationsHub.vue +22 -56
- package/app/components/layout/ModelProvidersHub.vue +250 -0
- package/app/components/panels/InspectorPanel.vue +2 -19
- package/app/components/panels/inspector/ServiceValidationConfig.vue +99 -1
- package/app/components/settings/ComposeEnvironmentSetupSection.vue +102 -0
- package/app/components/settings/InfrastructureWindow.vue +11 -1
- package/app/composables/api/validationChecks.ts +8 -0
- package/app/composables/useIntegrationBack.ts +5 -2
- package/app/composables/useNavContributions.ts +1 -1
- package/app/modular/nav-contributions.spec.ts +11 -3
- package/app/modular/nav-contributions.ts +35 -16
- package/app/pages/index.vue +4 -0
- package/app/stores/documents.ts +9 -6
- package/app/stores/ui/modals.ts +43 -27
- package/app/stores/validationChecks.ts +17 -2
- package/app/types/validationChecks.ts +3 -0
- package/app/utils/validationDetection.spec.ts +51 -0
- package/app/utils/validationDetection.ts +67 -0
- package/i18n/locales/de.json +103 -27
- package/i18n/locales/en.json +103 -27
- package/i18n/locales/es.json +103 -27
- package/i18n/locales/fr.json +103 -27
- package/i18n/locales/he.json +103 -27
- package/i18n/locales/it.json +103 -27
- package/i18n/locales/ja.json +103 -27
- package/i18n/locales/pl.json +103 -27
- package/i18n/locales/tr.json +103 -27
- package/i18n/locales/uk.json +103 -27
- package/package.json +2 -2
package/app/stores/ui/modals.ts
CHANGED
|
@@ -156,23 +156,20 @@ function createMiscModals() {
|
|
|
156
156
|
* came-from markers (they can be reached from the Integrations hub).
|
|
157
157
|
*/
|
|
158
158
|
function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
159
|
-
// Document-source integration modals, keyed by source.
|
|
160
|
-
//
|
|
161
|
-
// frame
|
|
162
|
-
//
|
|
163
|
-
// `documentImport`'s source may be null to let the modal pick a
|
|
159
|
+
// Document-source integration modals, keyed by source. A spawn always creates new
|
|
160
|
+
// top-level frames: the planner decomposes a document into services, so spawning
|
|
161
|
+
// into an existing frame could only flatten those away (discarding the frame titles
|
|
162
|
+
// and types the preview shows). `documentConnect` carries the source whose connect
|
|
163
|
+
// form to show; `documentImport`'s source may be null to let the modal pick a
|
|
164
|
+
// connected one.
|
|
164
165
|
const documentConnect = ref<{ source: DocumentSourceKind } | null>(null)
|
|
165
|
-
const documentImport = ref<{
|
|
166
|
-
source: DocumentSourceKind | null
|
|
167
|
-
targetFrameId: string | null
|
|
168
|
-
} | null>(null)
|
|
166
|
+
const documentImport = ref<{ source: DocumentSourceKind | null } | null>(null)
|
|
169
167
|
// The workspace+DocKind template / exemplar management modal (WS1). A single boolean —
|
|
170
168
|
// it manages every kind's links in one place.
|
|
171
169
|
const documentTemplates = ref(false)
|
|
172
170
|
const spawnPreview = ref<{
|
|
173
171
|
source: DocumentSourceKind
|
|
174
172
|
externalId: string
|
|
175
|
-
targetFrameId: string | null
|
|
176
173
|
} | null>(null)
|
|
177
174
|
|
|
178
175
|
// Task-source integration modals, keyed by source. `taskConnect` carries the
|
|
@@ -222,12 +219,9 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
222
219
|
function closeDocumentConnect() {
|
|
223
220
|
documentConnect.value = null
|
|
224
221
|
}
|
|
225
|
-
function openDocumentImport(
|
|
226
|
-
targetFrameId: string | null = null,
|
|
227
|
-
source: DocumentSourceKind | null = null,
|
|
228
|
-
) {
|
|
222
|
+
function openDocumentImport(source: DocumentSourceKind | null = null) {
|
|
229
223
|
resetHubReturn()
|
|
230
|
-
documentImport.value = { source
|
|
224
|
+
documentImport.value = { source }
|
|
231
225
|
}
|
|
232
226
|
function closeDocumentImport() {
|
|
233
227
|
documentImport.value = null
|
|
@@ -239,12 +233,8 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
239
233
|
function closeDocumentTemplates() {
|
|
240
234
|
documentTemplates.value = false
|
|
241
235
|
}
|
|
242
|
-
function openSpawnPreview(
|
|
243
|
-
source
|
|
244
|
-
externalId: string,
|
|
245
|
-
targetFrameId: string | null = null,
|
|
246
|
-
) {
|
|
247
|
-
spawnPreview.value = { source, externalId, targetFrameId }
|
|
236
|
+
function openSpawnPreview(source: DocumentSourceKind, externalId: string) {
|
|
237
|
+
spawnPreview.value = { source, externalId }
|
|
248
238
|
}
|
|
249
239
|
function closeSpawnPreview() {
|
|
250
240
|
spawnPreview.value = null
|
|
@@ -952,20 +942,28 @@ function createConsumerOverlayHost() {
|
|
|
952
942
|
* the shared hub came-from markers.
|
|
953
943
|
*/
|
|
954
944
|
export function createUiModals() {
|
|
955
|
-
// Integrations / My-setup hub came-from markers — the one piece of state
|
|
956
|
-
// (many `open*` handlers reset it), so it lives here and `resetHubReturn`
|
|
957
|
-
// slices that need it. `cameFromIntegrations` is true while an
|
|
958
|
-
// AND it was reached from the Integrations hub;
|
|
945
|
+
// Model-providers / Integrations / My-setup hub came-from markers — the one piece of state
|
|
946
|
+
// SHARED across slices (many `open*` handlers reset it), so it lives here and `resetHubReturn`
|
|
947
|
+
// is threaded into the slices that need it. `cameFromIntegrations` is true while an
|
|
948
|
+
// integration's own panel is showing AND it was reached from the Integrations hub;
|
|
949
|
+
// `cameFromModelProviders` and `cameFromPersonal` are the analogues for the other two hubs.
|
|
950
|
+
//
|
|
951
|
+
// Three hubs, not two, because the vendor-credentials / OpenRouter / local-runner panels are
|
|
952
|
+
// reachable from more than one of them and a Back control that lands somewhere the user was
|
|
953
|
+
// never at is worse than none.
|
|
959
954
|
const cameFromIntegrations = ref(false)
|
|
955
|
+
const cameFromModelProviders = ref(false)
|
|
960
956
|
const cameFromPersonal = ref(false)
|
|
961
957
|
const integrationsOpen = ref(false)
|
|
958
|
+
const modelProvidersOpen = ref(false)
|
|
962
959
|
const personalSetupOpen = ref(false)
|
|
963
960
|
|
|
964
|
-
// Clear
|
|
961
|
+
// Clear EVERY hub came-from marker. Every direct `open*` in the slices calls this so that a
|
|
965
962
|
// panel opened outside the hubs never grows a dead Back control, and so switching from one
|
|
966
|
-
// hub's panel to
|
|
963
|
+
// hub's panel to another's clears the stale marker.
|
|
967
964
|
function resetHubReturn() {
|
|
968
965
|
cameFromIntegrations.value = false
|
|
966
|
+
cameFromModelProviders.value = false
|
|
969
967
|
cameFromPersonal.value = false
|
|
970
968
|
}
|
|
971
969
|
function openIntegrations() {
|
|
@@ -977,6 +975,13 @@ export function createUiModals() {
|
|
|
977
975
|
function closeIntegrations() {
|
|
978
976
|
integrationsOpen.value = false
|
|
979
977
|
}
|
|
978
|
+
function openModelProviders() {
|
|
979
|
+
resetHubReturn()
|
|
980
|
+
modelProvidersOpen.value = true
|
|
981
|
+
}
|
|
982
|
+
function closeModelProviders() {
|
|
983
|
+
modelProvidersOpen.value = false
|
|
984
|
+
}
|
|
980
985
|
function openPersonalSetup() {
|
|
981
986
|
resetHubReturn()
|
|
982
987
|
personalSetupOpen.value = true
|
|
@@ -1000,6 +1005,12 @@ export function createUiModals() {
|
|
|
1000
1005
|
cameFromIntegrations.value = true
|
|
1001
1006
|
integrationsOpen.value = false
|
|
1002
1007
|
}
|
|
1008
|
+
// The Model-providers analogue of `openFromIntegrations`.
|
|
1009
|
+
function openFromModelProviders(open: () => void) {
|
|
1010
|
+
open()
|
|
1011
|
+
cameFromModelProviders.value = true
|
|
1012
|
+
modelProvidersOpen.value = false
|
|
1013
|
+
}
|
|
1003
1014
|
|
|
1004
1015
|
const health = createHealthAdvisoryModals()
|
|
1005
1016
|
const misc = createMiscModals()
|
|
@@ -1014,11 +1025,16 @@ export function createUiModals() {
|
|
|
1014
1025
|
return {
|
|
1015
1026
|
integrationsOpen,
|
|
1016
1027
|
cameFromIntegrations,
|
|
1028
|
+
modelProvidersOpen,
|
|
1029
|
+
cameFromModelProviders,
|
|
1017
1030
|
personalSetupOpen,
|
|
1018
1031
|
cameFromPersonal,
|
|
1019
1032
|
openIntegrations,
|
|
1020
1033
|
closeIntegrations,
|
|
1021
1034
|
openFromIntegrations,
|
|
1035
|
+
openModelProviders,
|
|
1036
|
+
closeModelProviders,
|
|
1037
|
+
openFromModelProviders,
|
|
1022
1038
|
openPersonalSetup,
|
|
1023
1039
|
closePersonalSetup,
|
|
1024
1040
|
openFromPersonal,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { ref } from 'vue'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
DetectedValidationChecks,
|
|
5
|
+
ServiceValidationConfig,
|
|
6
|
+
ValidationCheck,
|
|
7
|
+
} from '~/types/validationChecks'
|
|
4
8
|
import { VALIDATION_DEFAULT_MAX_ATTEMPTS } from '~/types/validationChecks'
|
|
5
9
|
import { useUpsertList } from '~/composables/useUpsertList'
|
|
6
10
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
@@ -107,5 +111,16 @@ export const useValidationChecksStore = defineStore('validationChecks', () => {
|
|
|
107
111
|
dropLocal(blockId)
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Ask the backend what this service's repo implies. Nothing is stored: the suggestion goes
|
|
116
|
+
* into the panel's unsaved rows, so the operator reviews and saves it like anything they
|
|
117
|
+
* typed. Deliberately NOT folded into `configs` — a suggestion that looked like saved
|
|
118
|
+
* config would report a service as configured when no run would run a single command.
|
|
119
|
+
*/
|
|
120
|
+
async function detect(blockId: string): Promise<DetectedValidationChecks> {
|
|
121
|
+
const ws = useWorkspaceStore()
|
|
122
|
+
return api.detectServiceValidationChecks(ws.requireId(), blockId)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { configs, loading, available, load, ensureLoaded, forBlock, save, remove, detect }
|
|
111
126
|
})
|
|
@@ -9,6 +9,9 @@ export type {
|
|
|
9
9
|
ValidationReport,
|
|
10
10
|
ServiceValidationConfig,
|
|
11
11
|
UpsertServiceValidationConfigInput,
|
|
12
|
+
DetectedValidationChecks,
|
|
13
|
+
ValidationDetectionStatus,
|
|
14
|
+
ValidationEcosystem,
|
|
12
15
|
} from '@cat-factory/contracts'
|
|
13
16
|
export {
|
|
14
17
|
VALIDATION_DEFAULT_MAX_ATTEMPTS,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { mergeDetectedChecks } from './validationDetection'
|
|
3
|
+
|
|
4
|
+
const check = (label: string, command: string) => ({ label, command })
|
|
5
|
+
|
|
6
|
+
describe('mergeDetectedChecks', () => {
|
|
7
|
+
it('appends suggestions to an empty (or blank-row) panel', () => {
|
|
8
|
+
const result = mergeDetectedChecks(
|
|
9
|
+
[check('', '')],
|
|
10
|
+
[check('install', 'pnpm install --frozen-lockfile'), check('lint', 'pnpm run lint')],
|
|
11
|
+
10,
|
|
12
|
+
)
|
|
13
|
+
expect(result.rows).toEqual([
|
|
14
|
+
check('install', 'pnpm install --frozen-lockfile'),
|
|
15
|
+
check('lint', 'pnpm run lint'),
|
|
16
|
+
])
|
|
17
|
+
expect(result.added).toBe(2)
|
|
18
|
+
expect(result.dropped).toBe(0)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('never rewrites or duplicates what the operator already has', () => {
|
|
22
|
+
// The same command with a hand-tuned label must survive untouched, and pressing Detect
|
|
23
|
+
// again must be a no-op rather than a second copy of every check.
|
|
24
|
+
const existing = [check('our lint', 'pnpm run lint')]
|
|
25
|
+
const result = mergeDetectedChecks(existing, [check('lint', 'pnpm run lint')], 10)
|
|
26
|
+
expect(result.rows).toEqual(existing)
|
|
27
|
+
expect(result.added).toBe(0)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('disambiguates a label that collides with an existing row', () => {
|
|
31
|
+
// A duplicate label is REJECTED by the write contract, so a collision the merge created
|
|
32
|
+
// would surface as a save error on a row the operator never typed.
|
|
33
|
+
const result = mergeDetectedChecks(
|
|
34
|
+
[check('test', 'make test')],
|
|
35
|
+
[check('test', 'go test ./...')],
|
|
36
|
+
10,
|
|
37
|
+
)
|
|
38
|
+
expect(result.rows.map((r) => r.label)).toEqual(['test', 'test 2'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('reports what the row cap dropped instead of silently truncating', () => {
|
|
42
|
+
const result = mergeDetectedChecks(
|
|
43
|
+
[check('lint', 'pnpm run lint')],
|
|
44
|
+
[check('test', 'pnpm run test'), check('build', 'pnpm run build')],
|
|
45
|
+
2,
|
|
46
|
+
)
|
|
47
|
+
expect(result.rows).toHaveLength(2)
|
|
48
|
+
expect(result.added).toBe(1)
|
|
49
|
+
expect(result.dropped).toBe(1)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ValidationCheck } from '~/types/validationChecks'
|
|
2
|
+
|
|
3
|
+
// Folding an autodetected suggestion into the pre-PR validation panel's UNSAVED rows.
|
|
4
|
+
//
|
|
5
|
+
// Pure, and separate from the panel, because the merge carries the two rules that make the
|
|
6
|
+
// button safe to press: it never destroys what the operator already typed, and it never
|
|
7
|
+
// produces a row set the write contract would reject.
|
|
8
|
+
|
|
9
|
+
/** What a merge did, so the panel can say something specific rather than "done". */
|
|
10
|
+
export interface MergedDetection {
|
|
11
|
+
rows: ValidationCheck[]
|
|
12
|
+
/** Suggestions appended as new rows. */
|
|
13
|
+
added: number
|
|
14
|
+
/** Suggestions dropped because the row cap was already reached. */
|
|
15
|
+
dropped: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Append detected checks to the rows already on screen.
|
|
20
|
+
*
|
|
21
|
+
* - A suggestion whose COMMAND is already present is skipped, so pressing Detect twice (or
|
|
22
|
+
* pressing it on a service that is already configured) adds nothing rather than
|
|
23
|
+
* duplicating every check.
|
|
24
|
+
* - Existing rows are never rewritten. The button is assistive; silently replacing an
|
|
25
|
+
* operator's hand-tuned command with the generic guess would be a worse failure than not
|
|
26
|
+
* detecting anything.
|
|
27
|
+
* - Wholly blank rows (an unused "Add check" click) are dropped first, so the suggestion
|
|
28
|
+
* lands in a contiguous list instead of after a gap.
|
|
29
|
+
* - Labels are made unique against the surviving rows, because the write contract REJECTS a
|
|
30
|
+
* duplicate label — a merge that produced one would surface as an unexplained save error
|
|
31
|
+
* on rows the operator did not write.
|
|
32
|
+
*/
|
|
33
|
+
export function mergeDetectedChecks(
|
|
34
|
+
existing: ValidationCheck[],
|
|
35
|
+
detected: ValidationCheck[],
|
|
36
|
+
maxRows: number,
|
|
37
|
+
): MergedDetection {
|
|
38
|
+
const rows = existing.filter((r) => r.label.trim() !== '' || r.command.trim() !== '')
|
|
39
|
+
const commands = new Set(rows.map((r) => r.command.trim()))
|
|
40
|
+
const labels = new Set(rows.map((r) => r.label.trim()).filter((l) => l !== ''))
|
|
41
|
+
|
|
42
|
+
let added = 0
|
|
43
|
+
let dropped = 0
|
|
44
|
+
for (const candidate of detected) {
|
|
45
|
+
const command = candidate.command.trim()
|
|
46
|
+
if (command === '' || commands.has(command)) continue
|
|
47
|
+
if (rows.length >= maxRows) {
|
|
48
|
+
dropped += 1
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
const label = uniqueLabel(candidate.label.trim() || command, labels)
|
|
52
|
+
rows.push({ label, command })
|
|
53
|
+
commands.add(command)
|
|
54
|
+
labels.add(label)
|
|
55
|
+
added += 1
|
|
56
|
+
}
|
|
57
|
+
return { rows, added, dropped }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** `lint` → `lint 2` → `lint 3` … until it no longer collides. Clamped to the contract's 80. */
|
|
61
|
+
function uniqueLabel(base: string, taken: Set<string>): string {
|
|
62
|
+
if (!taken.has(base)) return base.slice(0, 80)
|
|
63
|
+
for (let n = 2; ; n += 1) {
|
|
64
|
+
const candidate = `${base} ${n}`.slice(0, 80)
|
|
65
|
+
if (!taken.has(candidate)) return candidate
|
|
66
|
+
}
|
|
67
|
+
}
|
package/i18n/locales/de.json
CHANGED
|
@@ -658,6 +658,32 @@
|
|
|
658
658
|
"removeFailed": "Der gemeinsame Stack konnte nicht gelöscht werden"
|
|
659
659
|
}
|
|
660
660
|
},
|
|
661
|
+
"composeEnvSetup": {
|
|
662
|
+
"title": "Geführte Docker-Compose-Einrichtung für einen Service",
|
|
663
|
+
"lead": "Führt einen Service Schritt für Schritt durch das Erstellen des Docker-Compose-Rezepts, aus dem seine Testumgebungen aufgebaut werden, statt Compose-Pfad und Setup-Schritte von Hand zu schreiben.",
|
|
664
|
+
"how": {
|
|
665
|
+
"title": "So funktioniert es",
|
|
666
|
+
"scan": "Durchsucht das verknüpfte Repository des Service nach Compose-Dateien, Profilen, Env-Dateien und Seed-Dumps.",
|
|
667
|
+
"analyse": "Führt optional den Umgebungsanalysten aus, der Setup-Schritte und ein Health-Gate aus den Skripten des Repositorys entwirft.",
|
|
668
|
+
"preflight": "Führt die Preflight-Prüfungen des Rezepts auf der Maschine aus, die es bereitstellen wird.",
|
|
669
|
+
"save": "Speichert das Rezept am Service und registriert den Docker-Compose-Handler des Workspace.",
|
|
670
|
+
"outcome": "Danach stellt der Deployer genau diesen Stack bereit, sobald eine Pipeline eine laufende Umgebung braucht, und der Tester testet dagegen. Bis zum Speichern wird nichts übernommen."
|
|
671
|
+
},
|
|
672
|
+
"needed": {
|
|
673
|
+
"title": "Sie brauchen das, wenn",
|
|
674
|
+
"running": "Die Tests des Service die laufende Anwendung benötigen: API- oder UI-Tests, Migrationen, Seed-Daten.",
|
|
675
|
+
"compose": "Der Service über Docker Compose bereitgestellt wird und noch kein Rezept gespeichert hat.",
|
|
676
|
+
"setup": "Das Hochfahren des Stacks mehr als einen Befehl erfordert: Profile, Env-Dateien, Seed-Daten oder eine Bereitschaftsprüfung."
|
|
677
|
+
},
|
|
678
|
+
"notNeeded": {
|
|
679
|
+
"title": "Sie können es überspringen, wenn",
|
|
680
|
+
"inContainer": "Die Pipeline nur lintet, baut oder Unit-Tests ausführt. Das läuft als Pre-PR-Validierung im Agenten-Container, ohne dass eine Umgebung bereitgestellt wird.",
|
|
681
|
+
"otherBackend": "Der Service anders bereitgestellt wird (Kubernetes, eine Cloud-Vorschau oder gar keine Infrastruktur). Konfigurieren Sie das stattdessen oben.",
|
|
682
|
+
"noRepo": "Der Service kein verknüpftes Repository hat, es also nichts zu durchsuchen gibt."
|
|
683
|
+
},
|
|
684
|
+
"start": "Geführte Einrichtung starten",
|
|
685
|
+
"rerunHint": "Jederzeit erneut ausführbar, um ein gespeichertes Rezept zu ändern."
|
|
686
|
+
},
|
|
661
687
|
"localMode": {
|
|
662
688
|
"title": "Lokaler Modus",
|
|
663
689
|
"intro": "Feinabstimmung für den lokalen Container-Runner, gespeichert im Deployment dieser Maschine (er hat die Umgebungsvariablen {poolVars} / {harnessVars} ersetzt). Das Speichern passt den Warm-Pool live an, kein Neustart nötig; laufende Ausführungen behalten den Container, den sie bereits halten.",
|
|
@@ -1419,7 +1445,34 @@
|
|
|
1419
1445
|
"save": "Prüfungen speichern",
|
|
1420
1446
|
"savedToast": "Prüfungen gespeichert",
|
|
1421
1447
|
"saveFailed": "Prüfungen konnten nicht gespeichert werden",
|
|
1422
|
-
"clearFailed": "Prüfungen konnten nicht geleert werden"
|
|
1448
|
+
"clearFailed": "Prüfungen konnten nicht geleert werden",
|
|
1449
|
+
"detect": {
|
|
1450
|
+
"action": "Erkennen",
|
|
1451
|
+
"hint": "Prüfungen aus dem Repository dieses Dienstes vorschlagen",
|
|
1452
|
+
"added": "{count} Prüfung hinzugefügt | {count} Prüfungen hinzugefügt",
|
|
1453
|
+
"found": "Erkannt: {ecosystems}.",
|
|
1454
|
+
"capped": "Einige Vorschläge wurden ausgelassen — ein Dienst nimmt höchstens {max} Prüfungen auf.",
|
|
1455
|
+
"nothingNew": "Nichts hinzuzufügen",
|
|
1456
|
+
"alreadyPresent": "Alle erkannten Befehle stehen bereits in der Liste.",
|
|
1457
|
+
"unrecognised": "Im Wurzelverzeichnis des Repositorys wurde kein bekanntes Ökosystem erkannt. Bitte die Befehle von Hand eintragen.",
|
|
1458
|
+
"repo_unavailable": "Diesem Dienst ist kein Repository zugeordnet, es gibt also nichts zu untersuchen.",
|
|
1459
|
+
"failed": "Das Repository konnte nicht gelesen werden"
|
|
1460
|
+
},
|
|
1461
|
+
"ecosystem": {
|
|
1462
|
+
"node": "Node.js",
|
|
1463
|
+
"python": "Python",
|
|
1464
|
+
"go": "Go",
|
|
1465
|
+
"rust": "Rust",
|
|
1466
|
+
"maven": "Maven",
|
|
1467
|
+
"gradle": "Gradle",
|
|
1468
|
+
"dotnet": ".NET",
|
|
1469
|
+
"ruby": "Ruby",
|
|
1470
|
+
"php": "PHP",
|
|
1471
|
+
"elixir": "Elixir",
|
|
1472
|
+
"make": "Make",
|
|
1473
|
+
"just": "just",
|
|
1474
|
+
"task": "Task"
|
|
1475
|
+
}
|
|
1423
1476
|
}
|
|
1424
1477
|
},
|
|
1425
1478
|
"panels": {
|
|
@@ -1658,7 +1711,6 @@
|
|
|
1658
1711
|
"lastIncorporatedRequirements": "Zuletzt eingearbeitete Anforderungen",
|
|
1659
1712
|
"priorDocHint": "Aus der vorherigen Prüfung. Verwende es als Basis: bearbeite die Beschreibung oben und reiche erneut ein.",
|
|
1660
1713
|
"bootstrapping": "Wird initialisiert…",
|
|
1661
|
-
"spawnFromDocument": "Aus Dokument erzeugen",
|
|
1662
1714
|
"viewRequirements": "Anforderungen anzeigen",
|
|
1663
1715
|
"reRun": "Erneut ausführen",
|
|
1664
1716
|
"run": "Ausführen",
|
|
@@ -1975,7 +2027,8 @@
|
|
|
1975
2027
|
},
|
|
1976
2028
|
"integrationBack": {
|
|
1977
2029
|
"backToMySetup": "Zurück zu Meine Einrichtung",
|
|
1978
|
-
"backToIntegrations": "Zurück zu Integrationen"
|
|
2030
|
+
"backToIntegrations": "Zurück zu Integrationen",
|
|
2031
|
+
"backToModelProviders": "Zurück zu Modellanbieter"
|
|
1979
2032
|
},
|
|
1980
2033
|
"commandBar": {
|
|
1981
2034
|
"searchPlaceholder": "Suchen oder einen Befehl ausführen…",
|
|
@@ -2043,23 +2096,21 @@
|
|
|
2043
2096
|
},
|
|
2044
2097
|
"integrationsHub": {
|
|
2045
2098
|
"title": "Integrationen",
|
|
2046
|
-
"intro": "Verbinden und verwalten Sie die externen Systeme, die dieser Workspace einbinden kann.",
|
|
2099
|
+
"intro": "Verbinden und verwalten Sie die optionalen externen Systeme, die dieser Workspace einbinden kann. Die Modelle, auf denen Ihre Agenten laufen, finden Sie unter Modellanbieter.",
|
|
2047
2100
|
"searchPlaceholder": "Integrationen durchsuchen…",
|
|
2048
2101
|
"noMatches": "Keine Integrationen passen zu \"{query}\".",
|
|
2049
2102
|
"getStarted": {
|
|
2050
2103
|
"title": "Erste Schritte",
|
|
2051
|
-
"body": "Verbinden Sie eine Code-Quelle
|
|
2104
|
+
"body": "Verbinden Sie eine Code-Quelle, damit Ihre Pipelines etwas zu bearbeiten haben."
|
|
2052
2105
|
},
|
|
2053
2106
|
"status": {
|
|
2054
2107
|
"connected": "Verbunden",
|
|
2055
|
-
"keyConnected": "Schlüssel verbunden",
|
|
2056
2108
|
"notConnected": "Nicht verbunden",
|
|
2057
2109
|
"needsAttention": "Benötigt Aufmerksamkeit",
|
|
2058
2110
|
"disabled": "Deaktiviert",
|
|
2059
2111
|
"recommended": "Empfohlen"
|
|
2060
2112
|
},
|
|
2061
2113
|
"groups": {
|
|
2062
|
-
"models": "Modelle und Anbieter",
|
|
2063
2114
|
"sourceControl": "Versionsverwaltung",
|
|
2064
2115
|
"communication": "Kommunikation",
|
|
2065
2116
|
"documents": "Dokumente",
|
|
@@ -2069,16 +2120,6 @@
|
|
|
2069
2120
|
"personal": "Persönlich (nur Sie)"
|
|
2070
2121
|
},
|
|
2071
2122
|
"items": {
|
|
2072
|
-
"openrouter": {
|
|
2073
|
-
"description": "Ein Gateway zu über 300 Modellen. Fügen Sie Ihren Schlüssel hinzu und aktivieren Sie Modelle an einem Ort."
|
|
2074
|
-
},
|
|
2075
|
-
"vendors": {
|
|
2076
|
-
"label": "Anbieter und Schlüssel",
|
|
2077
|
-
"description": "Workspace-LLM-Abonnements und Anbieter-API-Schlüssel."
|
|
2078
|
-
},
|
|
2079
|
-
"personalSubs": {
|
|
2080
|
-
"label": "Persönliche Abonnements (Claude, ChatGPT/Codex, GLM)"
|
|
2081
|
-
},
|
|
2082
2123
|
"github": {
|
|
2083
2124
|
"description": "Verbinden Sie die GitHub-App des Workspace, durchsuchen Sie Repos, PRs und Issues."
|
|
2084
2125
|
},
|
|
@@ -2118,10 +2159,6 @@
|
|
|
2118
2159
|
"label": "Mein GitHub-Token",
|
|
2119
2160
|
"description": "Ein Personal Access Token, der für von Ihnen gestartete Läufe verwendet wird (Pushes, PRs, CI, Merge)."
|
|
2120
2161
|
},
|
|
2121
|
-
"localRunners": {
|
|
2122
|
-
"label": "Meine lokalen Runner",
|
|
2123
|
-
"description": "Modell-Runner auf Ihrer eigenen Maschine (Ollama, LM Studio, vLLM…)."
|
|
2124
|
-
},
|
|
2125
2162
|
"documentTemplates": {
|
|
2126
2163
|
"label": "Vorlagen & Beispiele"
|
|
2127
2164
|
},
|
|
@@ -2131,6 +2168,45 @@
|
|
|
2131
2168
|
}
|
|
2132
2169
|
}
|
|
2133
2170
|
},
|
|
2171
|
+
"modelProvidersHub": {
|
|
2172
|
+
"title": "Modellanbieter",
|
|
2173
|
+
"intro": "Die Engines, auf denen Ihre Agenten laufen. Mindestens ein Anbieter muss verbunden sein, bevor ein Pipeline-Schritt ausgeführt werden kann.",
|
|
2174
|
+
"searchPlaceholder": "Anbieter suchen…",
|
|
2175
|
+
"noMatches": "Keine Anbieter entsprechen \"{query}\".",
|
|
2176
|
+
"getStarted": {
|
|
2177
|
+
"title": "Noch kein Anbieter verbunden",
|
|
2178
|
+
"body": "Agentenschritte können erst laufen, wenn ein Modell erreichbar ist. Ein OpenRouter-Schlüssel ist der schnellste Weg zu einem großen Katalog, aber Anbieterschlüssel, ein persönliches Abonnement oder Ihr eigener lokaler Runner funktionieren genauso gut."
|
|
2179
|
+
},
|
|
2180
|
+
"status": {
|
|
2181
|
+
"connected": "Verbunden",
|
|
2182
|
+
"keyConnected": "Schlüssel verbunden",
|
|
2183
|
+
"notConnected": "Nicht verbunden",
|
|
2184
|
+
"recommended": "Empfohlen",
|
|
2185
|
+
"connectedCount": "keine verbunden | {count} verbunden | {count} verbunden"
|
|
2186
|
+
},
|
|
2187
|
+
"groups": {
|
|
2188
|
+
"workspace": "Workspace-Anbieter",
|
|
2189
|
+
"personal": "Persönlich (nur Sie)",
|
|
2190
|
+
"personalNote": "Diese gehören zu Ihrem Konto und nicht zum Workspace und werden nur für Läufe verwendet, die Sie starten. Sie sind auch unter Meine Einrichtung aufgeführt."
|
|
2191
|
+
},
|
|
2192
|
+
"items": {
|
|
2193
|
+
"openrouter": {
|
|
2194
|
+
"description": "Ein Gateway zu über 300 Modellen. Fügen Sie Ihren Schlüssel hinzu und aktivieren Sie Modelle an einem Ort."
|
|
2195
|
+
},
|
|
2196
|
+
"vendors": {
|
|
2197
|
+
"label": "Anbieter und Schlüssel",
|
|
2198
|
+
"description": "Workspace-LLM-Abonnements und Anbieter-API-Schlüssel."
|
|
2199
|
+
},
|
|
2200
|
+
"personalSubs": {
|
|
2201
|
+
"label": "Meine Abonnements",
|
|
2202
|
+
"description": "Pläne zur individuellen Nutzung, die Sie mit einem persönlichen Passwort freischalten (Claude, GLM, Codex)."
|
|
2203
|
+
},
|
|
2204
|
+
"localRunners": {
|
|
2205
|
+
"label": "Meine lokalen Runner",
|
|
2206
|
+
"description": "Modell-Runner auf Ihrer eigenen Maschine (Ollama, LM Studio, vLLM)."
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
},
|
|
2134
2210
|
"accountSkills": {
|
|
2135
2211
|
"intro": "Verknüpfe Repositorys, in denen dein Team Claude Skills erstellt (ein SKILL.md-Ordner plus Ressourcendateien). Synchronisierte Skills landen im Katalog dieses Kontos, der von allen Boards geteilt wird, und der Skill-Schritt einer Pipeline führt einen davon für eine Aufgabe aus."
|
|
2136
2212
|
},
|
|
@@ -3209,7 +3285,6 @@
|
|
|
3209
3285
|
"title": "Aus einer Dokumentquelle importieren",
|
|
3210
3286
|
"connectFirst": "Verbinden Sie zuerst eine Dokumentquelle.",
|
|
3211
3287
|
"connectSource": "{source} verbinden",
|
|
3212
|
-
"spawningInto": "Wird in {frame} erzeugt",
|
|
3213
3288
|
"sourceLabel": "Quelle",
|
|
3214
3289
|
"refLabel": "Seiten-URL oder -ID",
|
|
3215
3290
|
"importButton": "Importieren",
|
|
@@ -3234,7 +3309,6 @@
|
|
|
3234
3309
|
"title": "Struktur-Vorschau",
|
|
3235
3310
|
"plannerLlm": "KI-generiert",
|
|
3236
3311
|
"plannerHeadings": "Aus Überschriften",
|
|
3237
|
-
"intoFrame": "in {frame}",
|
|
3238
3312
|
"asTopLevel": "als neue Frames auf oberster Ebene",
|
|
3239
3313
|
"buildingPlan": "Plan wird erstellt…",
|
|
3240
3314
|
"spawnOntoBoard": "Auf dem Board erzeugen",
|
|
@@ -4356,6 +4430,8 @@
|
|
|
4356
4430
|
"repositories": "Repositories",
|
|
4357
4431
|
"addFromRepo": "Aus vorhandenem Repo hinzufügen",
|
|
4358
4432
|
"bootstrapRepo": "Repo bootstrappen",
|
|
4433
|
+
"models": "Modelle",
|
|
4434
|
+
"modelProviders": "Modellanbieter",
|
|
4359
4435
|
"integrations": "Integrationen",
|
|
4360
4436
|
"infrastructure": "Infrastruktur",
|
|
4361
4437
|
"sandbox": "Sandbox",
|
|
@@ -4367,8 +4443,7 @@
|
|
|
4367
4443
|
"modelConfiguration": "Modellkonfiguration",
|
|
4368
4444
|
"accountSettings": "Kontoeinstellungen",
|
|
4369
4445
|
"operatorDashboard": "Plattform-Observability",
|
|
4370
|
-
"reports": "Berichte"
|
|
4371
|
-
"environmentSetup": "Umgebungseinrichtung"
|
|
4446
|
+
"reports": "Berichte"
|
|
4372
4447
|
},
|
|
4373
4448
|
"errors": {
|
|
4374
4449
|
"action": {
|
|
@@ -5263,8 +5338,8 @@
|
|
|
5263
5338
|
},
|
|
5264
5339
|
"environmentWizard": {
|
|
5265
5340
|
"progress": "Schritt {index} von {total}",
|
|
5266
|
-
"title": "
|
|
5267
|
-
"subtitle": "
|
|
5341
|
+
"title": "Docker-Compose-Umgebungseinrichtung",
|
|
5342
|
+
"subtitle": "Erstellen Sie das Docker-Compose-Rezept, aus dem die Testumgebungen eines Service aufgebaut werden: Repository durchsuchen, prüfen, Preflight, speichern.",
|
|
5268
5343
|
"recommended": "empfohlen",
|
|
5269
5344
|
"steps": {
|
|
5270
5345
|
"pick": "Service auswählen",
|
|
@@ -5274,6 +5349,7 @@
|
|
|
5274
5349
|
},
|
|
5275
5350
|
"pick": {
|
|
5276
5351
|
"intro": "Wähle den Service, für den eine Docker-Compose-Umgebung eingerichtet werden soll.",
|
|
5352
|
+
"scope": "Nur der ausgewählte Service wird geändert. Führen Sie dies später erneut aus, um sein Rezept zu bearbeiten.",
|
|
5277
5353
|
"empty": "Noch keine Services. Füge zuerst einen Service hinzu."
|
|
5278
5354
|
},
|
|
5279
5355
|
"review": {
|