@cat-factory/app 0.249.0 → 0.250.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.
@@ -1,18 +1,23 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import {
3
3
  appInstallationManageUrl,
4
+ branchWebUrl,
5
+ issueWebUrl,
4
6
  newRepoUrl,
7
+ pullWebUrl,
8
+ repoWebUrl,
5
9
  VCS_PROVIDER_ICONS,
6
10
  VCS_PROVIDER_LABELS,
7
- VCS_PROVIDER_TOKEN_URLS,
11
+ vcsTokenCreateUrl,
8
12
  } from './vcs'
9
13
  import type { GitHubConnection, VcsProvider } from '~/types/domain'
10
14
 
11
15
  /**
12
- * The one place VCS presentation switches on the provider. What is pinned here is the pair of
16
+ * The one place VCS presentation switches on the provider. What is pinned here is the set of
13
17
  * decisions a component must never make for itself: which affordances belong to a GitHub-App
14
- * installation (and therefore vanish on a pasted token), and which host page a manual
15
- * repo-creation link may open, including the hosts it must refuse to guess at.
18
+ * installation (and therefore vanish on a pasted token), how each provider addresses a merge
19
+ * request / issue / branch, and which links may fall back to a provider's public instance when
20
+ * the deployment could not name its own host — one may, the rest must withhold.
16
21
  */
17
22
  const connection = (over: Partial<GitHubConnection> = {}): GitHubConnection => ({
18
23
  installationId: 42,
@@ -21,6 +26,7 @@ const connection = (over: Partial<GitHubConnection> = {}): GitHubConnection => (
21
26
  connectedAt: 0,
22
27
  provider: 'github',
23
28
  method: 'app',
29
+ webUrl: 'https://github.com',
24
30
  canCreateRepos: false,
25
31
  canManageWorkflows: true,
26
32
  ...over,
@@ -39,6 +45,14 @@ describe('appInstallationManageUrl', () => {
39
45
  )
40
46
  })
41
47
 
48
+ // A GitHub Enterprise Server installation's settings live on that server, and its installation
49
+ // id means nothing anywhere else.
50
+ it('links an Enterprise installation to its own host', () => {
51
+ expect(appInstallationManageUrl(connection({ webUrl: 'https://ghe.acme.dev' }))).toBe(
52
+ 'https://ghe.acme.dev/settings/installations/42',
53
+ )
54
+ })
55
+
42
56
  // The whole point of the helper: a pasted token has no installation, so there is no page to
43
57
  // send the user to. Both modals used to build the github.com URL from the connection
44
58
  // unconditionally, which put a "Grant the App access" button that 404s in front of every
@@ -52,6 +66,10 @@ describe('appInstallationManageUrl', () => {
52
66
  ).toBeUndefined()
53
67
  })
54
68
 
69
+ it('has no URL when the deployment could not name the host', () => {
70
+ expect(appInstallationManageUrl(connection({ webUrl: null }))).toBeUndefined()
71
+ })
72
+
55
73
  it('has no URL when there is no connection', () => {
56
74
  expect(appInstallationManageUrl(null)).toBeUndefined()
57
75
  })
@@ -60,7 +78,8 @@ describe('appInstallationManageUrl', () => {
60
78
  describe('newRepoUrl', () => {
61
79
  it('prefills the GitHub new-repository form with everything the caller knows', () => {
62
80
  const url = new URL(
63
- newRepoUrl('github', { owner: 'acme', name: 'api', private: true }) ?? 'about:blank',
81
+ newRepoUrl('github', 'https://github.com', { owner: 'acme', name: 'api', private: true }) ??
82
+ 'about:blank',
64
83
  )
65
84
  expect(url.origin + url.pathname).toBe('https://github.com/new')
66
85
  expect(url.searchParams.get('owner')).toBe('acme')
@@ -69,22 +88,94 @@ describe('newRepoUrl', () => {
69
88
  })
70
89
 
71
90
  it('omits what the caller has not filled in yet, and marks a public repo public', () => {
72
- const url = new URL(newRepoUrl('github', { name: '', private: false }) ?? 'about:blank')
91
+ const url = new URL(
92
+ newRepoUrl('github', 'https://github.com', { name: '', private: false }) ?? 'about:blank',
93
+ )
73
94
  expect(url.searchParams.has('owner')).toBe(false)
74
95
  expect(url.searchParams.has('name')).toBe(false)
75
96
  expect(url.searchParams.get('visibility')).toBe('public')
76
97
  })
77
98
 
78
- // A deployment may be bound to any self-hosted GitLab and nothing on the wire names its web
79
- // host yet, so there is no page this can honestly open. Withheld rather than guessed at:
80
- // gitlab.com would look like it worked, and the user would create the project on a server
81
- // the bootstrap run never pushes to.
82
- it('withholds a page for GitLab, whose instance the SPA cannot name', () => {
83
- expect(newRepoUrl('gitlab', { name: 'api', private: false })).toBeUndefined()
99
+ // Now that the connection states its host, a self-managed GitLab gets the button back on its
100
+ // OWN instance. GitLab's form takes no prefill, so it is the bare page.
101
+ it('opens the new-project page on the GitLab instance the workspace is bound to', () => {
102
+ expect(newRepoUrl('gitlab', 'https://gitlab.acme.dev', { name: 'api', private: false })).toBe(
103
+ 'https://gitlab.acme.dev/projects/new',
104
+ )
105
+ })
106
+
107
+ // Withheld rather than guessed at: the public instance would look like it worked, and the user
108
+ // would create the project on a server the bootstrap run never pushes to.
109
+ it('withholds a page when the deployment could not name a host', () => {
110
+ expect(newRepoUrl('gitlab', null, { name: 'api', private: false })).toBeUndefined()
84
111
  })
85
112
 
86
113
  it('withholds a page when no provider is resolved', () => {
87
- expect(newRepoUrl(null, { name: 'api', private: false })).toBeUndefined()
114
+ expect(
115
+ newRepoUrl(null, 'https://gitlab.acme.dev', { name: 'api', private: false }),
116
+ ).toBeUndefined()
117
+ })
118
+ })
119
+
120
+ describe('repo / pull / issue / branch links', () => {
121
+ const repo = { owner: 'acme/platform', name: 'api' }
122
+
123
+ // The repo path itself is provider-neutral: `owner` already carries GitLab's full group path,
124
+ // nested groups included. Only what hangs off it differs.
125
+ it('builds a repo page under the connection’s own host', () => {
126
+ expect(repoWebUrl('https://gitlab.acme.dev', repo)).toBe(
127
+ 'https://gitlab.acme.dev/acme/platform/api',
128
+ )
129
+ expect(repoWebUrl('https://github.com/', { owner: 'acme', name: 'api' })).toBe(
130
+ 'https://github.com/acme/api',
131
+ )
132
+ })
133
+
134
+ it('addresses a pull request and a merge request the way each provider does', () => {
135
+ const base = 'https://gitlab.acme.dev/acme/api'
136
+ expect(pullWebUrl('gitlab', base, 7)).toBe(`${base}/-/merge_requests/7`)
137
+ expect(pullWebUrl('github', 'https://github.com/acme/api', 7)).toBe(
138
+ 'https://github.com/acme/api/pull/7',
139
+ )
140
+ })
141
+
142
+ it('addresses an issue and a branch the way each provider does', () => {
143
+ const base = 'https://gitlab.acme.dev/acme/api'
144
+ expect(issueWebUrl('gitlab', base, 3)).toBe(`${base}/-/issues/3`)
145
+ expect(branchWebUrl('gitlab', base, 'feat/sso')).toBe(`${base}/-/tree/feat/sso`)
146
+ expect(issueWebUrl('github', 'https://github.com/acme/api', 3)).toBe(
147
+ 'https://github.com/acme/api/issues/3',
148
+ )
149
+ expect(branchWebUrl('github', 'https://github.com/acme/api', 'feat/sso')).toBe(
150
+ 'https://github.com/acme/api/tree/feat/sso',
151
+ )
152
+ })
153
+
154
+ // With no host there is no repo page, and everything built on one goes with it rather than
155
+ // being rendered against a guessed instance.
156
+ it('withholds every link when the host is unknown', () => {
157
+ expect(repoWebUrl(null, repo)).toBeNull()
158
+ expect(pullWebUrl('gitlab', null, 7)).toBeNull()
159
+ expect(issueWebUrl('gitlab', null, 3)).toBeNull()
160
+ expect(branchWebUrl('gitlab', null, 'main')).toBeNull()
161
+ })
162
+ })
163
+
164
+ describe('vcsTokenCreateUrl', () => {
165
+ it('points at the token page on the instance being connected', () => {
166
+ expect(vcsTokenCreateUrl('gitlab', 'https://gitlab.acme.dev')).toBe(
167
+ 'https://gitlab.acme.dev/-/user_settings/personal_access_tokens',
168
+ )
169
+ })
170
+
171
+ // The one builder that may fall back: it renders during connect (and on the sign-in screen,
172
+ // where nothing is connected at all), and a settings page on the wrong instance costs a click
173
+ // rather than a run.
174
+ it('falls back to the provider’s public instance when no host is known', () => {
175
+ expect(vcsTokenCreateUrl('gitlab')).toBe(
176
+ 'https://gitlab.com/-/user_settings/personal_access_tokens',
177
+ )
178
+ expect(vcsTokenCreateUrl('github', null)).toBe('https://github.com/settings/tokens/new')
88
179
  })
89
180
  })
90
181
 
@@ -96,6 +187,6 @@ describe('provider presentation maps', () => {
96
187
  it.each(providers)('has a label, icon and token URL for %s', (provider) => {
97
188
  expect(VCS_PROVIDER_LABELS[provider]).toBeTruthy()
98
189
  expect(VCS_PROVIDER_ICONS[provider]).toMatch(/^i-lucide-/)
99
- expect(VCS_PROVIDER_TOKEN_URLS[provider]).toMatch(/^https:\/\//)
190
+ expect(vcsTokenCreateUrl(provider)).toMatch(/^https:\/\//)
100
191
  })
101
192
  })
package/app/utils/vcs.ts CHANGED
@@ -11,6 +11,13 @@ import type { GitHubConnection, VcsProvider } from '~/types/domain'
11
11
  //
12
12
  // Each map is an exhaustive `Record<VcsProvider, …>`: adding a provider to the union fails
13
13
  // the typecheck here instead of silently rendering a GitHub icon for it.
14
+ //
15
+ // EVERY link builder here takes the connection's `webUrl` — the browser-facing host the
16
+ // backend derived from the instance the workspace is actually bound to. The SPA used to
17
+ // hand-build `https://github.com/…`, which is right for exactly one deployment shape and sends
18
+ // a self-managed GitLab (or GitHub Enterprise) user to whatever lives at that path on the
19
+ // public instance. A null host means the deployment could not name one, and a builder that
20
+ // needs one answers `undefined` so its caller drops the affordance.
14
21
  // ---------------------------------------------------------------------------
15
22
 
16
23
  /** Brand name, as rendered in titles and buttons. */
@@ -24,30 +31,58 @@ export const VCS_PROVIDER_ICONS: Record<VcsProvider, string> = {
24
31
  gitlab: 'i-lucide-gitlab',
25
32
  }
26
33
 
27
- /** Where a user creates a personal access token for the provider (the PAT connect flow). */
28
- export const VCS_PROVIDER_TOKEN_URLS: Record<VcsProvider, string> = {
29
- github: 'https://github.com/settings/tokens/new',
30
- gitlab: 'https://gitlab.com/-/user_settings/personal_access_tokens',
34
+ /**
35
+ * The public instance of each provider, used ONLY where no host is known and a wrong link costs
36
+ * nothing but a click: the token-creation page during connect (see {@link vcsTokenCreateUrl}).
37
+ * Never for a link to a repo, a project or a namespace, where the public instance may well
38
+ * serve somebody else's page under the same path.
39
+ */
40
+ const VCS_PROVIDER_PUBLIC_WEB_URLS: Record<VcsProvider, string> = {
41
+ github: 'https://github.com',
42
+ gitlab: 'https://gitlab.com',
43
+ }
44
+
45
+ /** Where a user creates a personal access token, relative to the instance's web root. */
46
+ const TOKEN_SETTINGS_PATHS: Record<VcsProvider, string> = {
47
+ github: '/settings/tokens/new',
48
+ gitlab: '/-/user_settings/personal_access_tokens',
49
+ }
50
+
51
+ /** Where a user creates a repository/project by hand, relative to the instance's web root. */
52
+ const NEW_REPO_PATHS: Record<VcsProvider, string> = {
53
+ github: '/new',
54
+ gitlab: '/projects/new',
55
+ }
56
+
57
+ /** How each provider addresses a pull/merge request and an issue under a repo's web path. */
58
+ const PULL_PATHS: Record<VcsProvider, (n: number) => string> = {
59
+ github: (n) => `/pull/${n}`,
60
+ gitlab: (n) => `/-/merge_requests/${n}`,
61
+ }
62
+ const ISSUE_PATHS: Record<VcsProvider, (n: number) => string> = {
63
+ github: (n) => `/issues/${n}`,
64
+ gitlab: (n) => `/-/issues/${n}`,
65
+ }
66
+ const BRANCH_PATHS: Record<VcsProvider, (branch: string) => string> = {
67
+ github: (branch) => `/tree/${branch}`,
68
+ gitlab: (branch) => `/-/tree/${branch}`,
69
+ }
70
+
71
+ /** Strip a trailing slash so a host and a path never join into a double slash. */
72
+ function root(webUrl: string): string {
73
+ return webUrl.replace(/\/+$/, '')
31
74
  }
32
75
 
33
76
  /**
34
- * Where a user creates a repository by hand, for the flows that need one to exist before a run
35
- * can target it, or `null` where the SPA cannot name the instance the workspace is connected
36
- * to, in which case the affordance is WITHHELD rather than pointed somewhere plausible.
77
+ * Where a user creates a personal access token on the instance they are connecting.
37
78
  *
38
- * `gitlab` is null for that reason: a deployment may be bound to any self-hosted instance and
39
- * nothing on the wire carries its web host yet (the connection is the proposed carrier; see
40
- * the initiative tracker's slice 5). `https://gitlab.com/projects/new` would be a guess about
41
- * which server the user's projects live on, and the cost of being wrong is not a dead link: a
42
- * project created on the wrong instance looks like success until the bootstrap push cannot
43
- * find it. This is the same rule the callers already apply when no provider is resolved at
44
- * all, so the two cases collapse into {@link newRepoUrl} returning `undefined`.
45
- *
46
- * A `Record` rather than a switch so a provider joining the union has to state its answer.
79
+ * The one builder that FALLS BACK to the provider's public instance when no host is known,
80
+ * deliberately unlike its repo-facing siblings: this renders during connect, before anything is
81
+ * bound, and the cost of being wrong is a settings page that isn't theirs — a click, noticed
82
+ * immediately. A wrong REPOSITORY link is silent and can cost a run, so those withhold instead.
47
83
  */
48
- const NEW_REPO_PAGES: Record<VcsProvider, string | null> = {
49
- github: 'https://github.com/new',
50
- gitlab: null,
84
+ export function vcsTokenCreateUrl(provider: VcsProvider, webUrl?: string | null): string {
85
+ return `${root(webUrl || VCS_PROVIDER_PUBLIC_WEB_URLS[provider])}${TOKEN_SETTINGS_PATHS[provider]}`
51
86
  }
52
87
 
53
88
  /**
@@ -58,20 +93,27 @@ const NEW_REPO_PAGES: Record<VcsProvider, string | null> = {
58
93
  * token's scope and the user's project membership on the host, so there is nothing to link
59
94
  * to and the callers drop the affordance rather than pointing at a URL that 404s. Keyed on the
60
95
  * connection's own `method` (see the contract) rather than on `provider`, and asked as
61
- * `=== 'app'` so anything that is not an App installation withholds the link.
96
+ * `=== 'app'` so anything that is not an App installation withholds the link — as does an
97
+ * installation whose host the deployment could not name, since an installation id means nothing
98
+ * on an instance other than its own.
62
99
  */
63
100
  export function appInstallationManageUrl(connection: GitHubConnection | null): string | undefined {
64
- if (!connection || connection.method !== 'app') return undefined
101
+ if (!connection || connection.method !== 'app' || !connection.webUrl) return undefined
102
+ const base = root(connection.webUrl)
65
103
  return connection.targetType === 'Organization'
66
- ? `https://github.com/organizations/${connection.accountLogin}/settings/installations/${connection.installationId}`
67
- : `https://github.com/settings/installations/${connection.installationId}`
104
+ ? `${base}/organizations/${connection.accountLogin}/settings/installations/${connection.installationId}`
105
+ : `${base}/settings/installations/${connection.installationId}`
68
106
  }
69
107
 
70
108
  /**
71
- * The host's new-repository page for a manual create, or `undefined` where there is no page
72
- * this deployment can honestly send the user to (see {@link NEW_REPO_PAGES}), including a
73
- * null `provider`, which is what a surface rendering before anything is connected has when
74
- * the deployment offers several. A caller that gets `undefined` hides the affordance.
109
+ * The host's new-repository page for a manual create, or `undefined` when the deployment could
110
+ * not name the instance (a null/absent `webUrl`) or no provider is resolved at all, which is
111
+ * what a surface rendering before anything is connected has when the deployment offers several.
112
+ * A caller that gets `undefined` hides the affordance.
113
+ *
114
+ * Withholding is the whole point: a project created on the wrong instance looks like success
115
+ * until the bootstrap push cannot find it, which is a failed run rather than a dead link. Now
116
+ * that the host is on the wire, a GitLab deployment that HAS one gets the button back.
75
117
  *
76
118
  * GitHub's form is the only one that takes a prefill, so what the bootstrap flow already
77
119
  * knows is carried over and the user creates the right repo in one click. `visibility` is
@@ -79,10 +121,11 @@ export function appInstallationManageUrl(connection: GitHubConnection | null): s
79
121
  */
80
122
  export function newRepoUrl(
81
123
  provider: VcsProvider | null,
124
+ webUrl: string | null | undefined,
82
125
  prefill: { owner?: string; name?: string; description?: string; private: boolean },
83
126
  ): string | undefined {
84
- const page = provider ? NEW_REPO_PAGES[provider] : null
85
- if (page === null) return undefined
127
+ if (!provider || !webUrl) return undefined
128
+ const page = `${root(webUrl)}${NEW_REPO_PATHS[provider]}`
86
129
  if (provider !== 'github') return page
87
130
  const params = new URLSearchParams()
88
131
  if (prefill.owner) params.set('owner', prefill.owner)
@@ -91,3 +134,44 @@ export function newRepoUrl(
91
134
  params.set('visibility', prefill.private ? 'private' : 'public')
92
135
  return `${page}?${params.toString()}`
93
136
  }
137
+
138
+ /**
139
+ * A repository's page on the instance it lives on, or null when no host is known.
140
+ *
141
+ * `owner` is the full namespace on both providers (GitHub's `owner`, GitLab's group path,
142
+ * nested groups included), so the repo path itself needs no provider switch — only what hangs
143
+ * off it does.
144
+ */
145
+ export function repoWebUrl(
146
+ webUrl: string | null | undefined,
147
+ repo: { owner: string; name: string },
148
+ ): string | null {
149
+ return webUrl ? `${root(webUrl)}/${repo.owner}/${repo.name}` : null
150
+ }
151
+
152
+ /** A pull request (GitHub) / merge request (GitLab) under a repo's page. */
153
+ export function pullWebUrl(
154
+ provider: VcsProvider,
155
+ repoUrl: string | null,
156
+ pullNumber: number,
157
+ ): string | null {
158
+ return repoUrl ? `${repoUrl}${PULL_PATHS[provider](pullNumber)}` : null
159
+ }
160
+
161
+ /** An issue under a repo's page. */
162
+ export function issueWebUrl(
163
+ provider: VcsProvider,
164
+ repoUrl: string | null,
165
+ issueNumber: number,
166
+ ): string | null {
167
+ return repoUrl ? `${repoUrl}${ISSUE_PATHS[provider](issueNumber)}` : null
168
+ }
169
+
170
+ /** A branch's file listing under a repo's page. */
171
+ export function branchWebUrl(
172
+ provider: VcsProvider,
173
+ repoUrl: string | null,
174
+ branch: string,
175
+ ): string | null {
176
+ return repoUrl ? `${repoUrl}${BRANCH_PATHS[provider](branch)}` : null
177
+ }
@@ -3050,7 +3050,6 @@
3050
3050
  },
3051
3051
  "pr": "PR",
3052
3052
  "prNumber": "PR #{number}",
3053
- "openPrOnGithub": "{pr} auf GitHub öffnen",
3054
3053
  "readOutcome": "Ergebnis",
3055
3054
  "readOutcomeHint": "Lies nach, was sich geändert hat, mit den Belegen dafür",
3056
3055
  "review": "Überprüfen",
@@ -3064,7 +3063,8 @@
3064
3063
  "module": "Modul: {name}",
3065
3064
  "buildSteps": "Build-Schritte",
3066
3065
  "reviewAndApprove": "Überprüfen & freigeben",
3067
- "clickToViewStep": "Klicken, um Schrittdetails & Ausgabe anzuzeigen"
3066
+ "clickToViewStep": "Klicken, um Schrittdetails & Ausgabe anzuzeigen",
3067
+ "openPrLink": "{pr} öffnen"
3068
3068
  },
3069
3069
  "dryRunToast": {
3070
3070
  "title": "Probelauf gestartet",
@@ -3319,14 +3319,11 @@
3319
3319
  "loading": "Wird geladen…",
3320
3320
  "tabs": {
3321
3321
  "repos": "Repositorys",
3322
- "pulls": "Pull Requests",
3323
3322
  "issues": "Issues"
3324
3323
  },
3325
3324
  "linkedToBoard": "Mit diesem Board verknüpft",
3326
3325
  "manageRepos": "Repos verwalten",
3327
- "manageHint": "Wählen Sie die Repositorys aus, die dieses Board verfolgen soll. Die GitHub-Verbindung wird über den Account geteilt, und jedes Board verknüpft seine eigenen Repos.",
3328
3326
  "loadingRepos": "Repositorys werden geladen…",
3329
- "noAvailableRepos": "Die Installation kann noch auf keine Repositorys zugreifen.",
3330
3327
  "private": "privat",
3331
3328
  "saveSelection": "Auswahl speichern",
3332
3329
  "noLinkedRepos": "Noch keine Repositorys verknüpft. Verwenden Sie \"Repos verwalten\", um auszuwählen, welche Repositorys dieses Board verfolgt.",
@@ -3336,15 +3333,11 @@
3336
3333
  "fromSha": "Von SHA",
3337
3334
  "commitShaPlaceholder": "Commit-SHA",
3338
3335
  "createBranch": "Branch erstellen",
3339
- "openPr": "PR öffnen",
3340
3336
  "repository": "Repository",
3341
3337
  "chooseRepository": "Repository auswählen",
3342
3338
  "prTitle": "Titel",
3343
3339
  "headBranch": "Head-Branch",
3344
3340
  "baseBranch": "Base-Branch",
3345
- "openPullRequest": "Pull Request öffnen",
3346
- "noPulls": "Keine Pull Requests synchronisiert.",
3347
- "mergePr": "Pull Request mergen",
3348
3341
  "noIssues": "Keine Issues synchronisiert.",
3349
3342
  "prState": {
3350
3343
  "open": "offen",
@@ -3358,9 +3351,7 @@
3358
3351
  "toast": {
3359
3352
  "resync": "Neusynchronisierung {status}",
3360
3353
  "reposUpdated": "Verknüpfte Repositorys aktualisiert",
3361
- "branchCreated": "Branch {name} erstellt",
3362
- "prOpened": "Pull Request geöffnet",
3363
- "prMerged": "PR #{number} gemergt"
3354
+ "branchCreated": "Branch {name} erstellt"
3364
3355
  },
3365
3356
  "errors": {
3366
3357
  "disconnect": "Trennen nicht möglich",
@@ -3369,7 +3360,6 @@
3369
3360
  "updateRepos": "Repositorys konnten nicht aktualisiert werden",
3370
3361
  "loadBranches": "Branches konnten nicht geladen werden",
3371
3362
  "createBranch": "Branch konnte nicht erstellt werden",
3372
- "openPr": "Pull Request konnte nicht geöffnet werden",
3373
3363
  "merge": "Mergen nicht möglich"
3374
3364
  }
3375
3365
  },
@@ -5474,7 +5464,8 @@
5474
5464
  "unavailable": {
5475
5465
  "description": {
5476
5466
  "binary_generators_unreachable": "Die generativen Integrationen dieser Installation konnten gerade nicht gelesen werden, deshalb wurde der Lauf nicht gestartet. Es ist nichts falsch konfiguriert und keine Änderung nötig: versuchen Sie es erneut, sobald die Verbindung wieder steht.",
5477
- "foundational_builtins_unreachable": "Die integrierten Basisdienste dieser Installation konnten gerade nicht gelesen werden. Es ist nichts falsch konfiguriert und keine Änderung nötig: versuchen Sie es erneut, sobald die Verbindung wieder steht."
5467
+ "foundational_builtins_unreachable": "Die integrierten Basisdienste dieser Installation konnten gerade nicht gelesen werden. Es ist nichts falsch konfiguriert und keine Änderung nötig: versuchen Sie es erneut, sobald die Verbindung wieder steht.",
5468
+ "connection_credentials_unreadable": "Die gespeicherten Zugangsdaten dieser Verbindung konnten nicht gelesen werden. Wenn diese Installation den Dienst mit dem zugehörigen Schlüssel erreicht, verbinden Sie die Quelle neu, um sie zu ersetzen; andernfalls versuchen Sie es erneut, sobald diese Verbindung wiederhergestellt ist."
5478
5469
  }
5479
5470
  },
5480
5471
  "action": {
@@ -5546,7 +5537,7 @@
5546
5537
  "agent_backend_unconfigured": "Es ist kein Runner-Backend verfügbar, um Container-Agenten auszuführen. Registriere einen selbst gehosteten Runner-Pool oder aktiviere Cloudflare Containers in deinem Deployment.",
5547
5538
  "run_not_retryable": "Nur eine fehlgeschlagene oder abgeschlossene Ausführung kann wiederholt werden. Diese Ausführung ist noch aktiv oder in einem Zustand, der keine Wiederholung erlaubt.",
5548
5539
  "no_pr_to_merge": "Diese Ausführung hat noch keinen Pull Request geöffnet, es gibt also nichts zu mergen.",
5549
- "github_not_connected": "Verbinde diesen Workspace mit GitHub, damit Agenten das Repository klonen, Änderungen pushen und Pull Requests öffnen können.",
5540
+ "github_not_connected": "Verbinden Sie diesen Workspace mit einem Repository-Host, damit Agenten das Repository klonen, Änderungen pushen und Pull Requests öffnen können.",
5550
5541
  "bootstrap_not_retryable": "Dieser Bootstrap-Auftrag kann in seinem aktuellen Zustand nicht wiederholt werden (er läuft noch oder wurde bereits erfolgreich abgeschlossen).",
5551
5542
  "bootstrap_reference_missing": "Das Referenz-Repository, das dieser Bootstrap übernimmt, ist nicht mehr erreichbar. Es wurde gelöscht oder der Zugriff wurde entzogen. Wähle eine andere Referenz oder erstelle von Grund auf neu.",
5552
5543
  "preset_unsatisfiable": "Das Modell eines Inline-Schritts kann in diesem Deployment nicht inline laufen (ein reines Abo-Modell ohne Inline-Unterstützung). Wähle ein inline-fähiges Modell oder Preset.",
@@ -5736,7 +5727,7 @@
5736
5727
  "suffixFixing": "· Fixer bearbeitet Kommentare…",
5737
5728
  "suffixFailing": "· zu bearbeitende Review-Kommentare",
5738
5729
  "suffixAwaiting": "· wartet auf Review",
5739
- "reviewPr": "Pull Request auf GitHub prüfen",
5730
+ "reviewPr": "Pull Request prüfen",
5740
5731
  "requestFixHeading": "Eine Korrektur anfordern",
5741
5732
  "requestFixDescription": "Beschreibe eine Änderung, die der Fixer jetzt am PR-Branch vornehmen soll (zusätzlich zu etwaigen Review-Kommentaren, die er automatisch bearbeitet).",
5742
5733
  "requestFixPlaceholder": "z. B. benenne den Helfer um und füge einen Unit-Test für den Fall leerer Eingabe hinzu",
@@ -5750,12 +5741,12 @@
5750
5741
  },
5751
5742
  "conflicts": {
5752
5743
  "mergeability": "Zusammenführbarkeit",
5753
- "viewPr": "Pull Request auf GitHub ansehen"
5744
+ "viewPr": "Pull Request ansehen"
5754
5745
  },
5755
5746
  "docQuality": {
5756
5747
  "findings": "Dokumentprobleme",
5757
5748
  "findingsFallback": "Das Dokument hat die strukturellen Prüfungen bestanden.",
5758
- "viewPr": "Pull Request auf GitHub ansehen"
5749
+ "viewPr": "Pull Request ansehen"
5759
5750
  },
5760
5751
  "attemptsHeading": "{helper}-Versuche",
5761
5752
  "attempt": "Versuch {number}",
@@ -6906,7 +6897,33 @@
6906
6897
  },
6907
6898
  "toast": {
6908
6899
  "disconnected": "{provider} getrennt"
6909
- }
6900
+ },
6901
+ "pulls": {
6902
+ "github": {
6903
+ "tab": "Pull Requests",
6904
+ "open": "PR öffnen",
6905
+ "openSubmit": "Pull Request öffnen",
6906
+ "merge": "Diesen Pull Request mergen",
6907
+ "none": "Keine Pull Requests synchronisiert.",
6908
+ "opened": "Pull Request geöffnet",
6909
+ "mergedToast": "PR #{number} gemergt",
6910
+ "openFailed": "Pull Request konnte nicht geöffnet werden"
6911
+ },
6912
+ "gitlab": {
6913
+ "tab": "Merge Requests",
6914
+ "open": "MR öffnen",
6915
+ "openSubmit": "Merge Request öffnen",
6916
+ "merge": "Diesen Merge Request mergen",
6917
+ "none": "Keine Merge Requests synchronisiert.",
6918
+ "opened": "Merge Request geöffnet",
6919
+ "mergedToast": "MR !{number} gemergt",
6920
+ "openFailed": "Merge Request konnte nicht geöffnet werden"
6921
+ }
6922
+ },
6923
+ "manageHintApp": "Wählen Sie die Repositories aus, die dieses Board verfolgen soll. Die App-Installation wird kontoweit geteilt, und jedes Board verknüpft seine eigenen Repositories.",
6924
+ "manageHintToken": "Wählen Sie die Repositories aus, die dieses Board verfolgen soll. Die Liste zeigt, was das verbundene Token erreicht, und jedes Board verknüpft seine eigenen Repositories.",
6925
+ "noAvailableReposApp": "Die App-Installation hat noch auf keine Repositories Zugriff.",
6926
+ "noAvailableReposToken": "Das verbundene Token erreicht noch keine Repositories. Prüfen Sie seinen Scope und Ihren Zugriff darauf."
6910
6927
  },
6911
6928
  "connect": {
6912
6929
  "or": "oder",