@burdenoff/microfe-vibecontrols 2026.530.1 → 2026.530.2

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.
@@ -5,41 +5,37 @@ import "../shared/index.js";
5
5
  import { appendIframeTokenQuery as n, mintIframeToken as r, scheduleIframeTokenRefresh as i } from "../../services/iframeToken.js";
6
6
  import { RepoStatsTab as a } from "./RepoStatsTab.js";
7
7
  import { CITab as o } from "./CITab.js";
8
- import { useGitopsContributors as ee, useGitopsIssues as te, useGitopsLanguages as ne, useGitopsPipelineAnalytics as re, useGitopsProviders as ie, useGitopsPullRequests as ae, useGitopsRepos as s, useGitopsRuns as oe, useGitopsSecurityAlerts as c } from "../../hooks/useGitops.js";
9
- import { GitOpsSetup as se } from "./GitOpsSetup.js";
10
- import { GitOpsProviderPicker as ce } from "./GitOpsProviderPicker.js";
11
- import { composeCiStatus as le, composeRepoStats as ue } from "./types.js";
12
- import { useCallback as de, useEffect as l, useMemo as u, useState as d } from "react";
13
- import { BarChart3 as f, GitBranch as p, GitMerge as m, Play as h, RefreshCw as g } from "lucide-react";
14
- import { useQueryClient as fe } from "@tanstack/react-query";
15
- import { motion as pe } from "framer-motion";
16
- import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
8
+ import { useGitopsContributors as ee, useGitopsIssues as te, useGitopsLanguages as ne, useGitopsPipelineAnalytics as re, useGitopsProviders as ie, useGitopsPullRequests as ae, useGitopsRepos as s, useGitopsRuns as oe, useGitopsSecurityAlerts as se } from "../../hooks/useGitops.js";
9
+ import { GitOpsSetup as ce } from "./GitOpsSetup.js";
10
+ import { GitOpsProviderPicker as le } from "./GitOpsProviderPicker.js";
11
+ import { composeCiStatus as c, composeRepoStats as l } from "./types.js";
12
+ import { useCallback as u, useEffect as d, useMemo as f, useState as p } from "react";
13
+ import { BarChart3 as ue, GitBranch as de, GitMerge as m, Play as h, RefreshCw as g } from "lucide-react";
14
+ import { useQueryClient as _ } from "@tanstack/react-query";
15
+ import { motion as fe } from "framer-motion";
16
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
17
17
  //#region src/components/gitops/VibeGitOpsTab.tsx
18
- var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D }) => {
19
- let [O, me] = d("repo-stats"), [k, A] = d(!1), [j, M] = d(null), [N, he] = d(null), { workspaceId: P } = e(), F = fe(), I = u(() => ({
20
- tunnelUrl: T,
21
- apiKey: E,
22
- profile: w,
18
+ var x = ({ vibeId: x, agentId: w, profile: T, tunnelUrl: E, tr: D }) => {
19
+ let [O, pe] = p("repo-stats"), [k, A] = p(!1), [j, M] = p(null), [N, me] = p(null), { workspaceId: P } = e(), F = _(), I = f(() => ({
20
+ profile: T,
23
21
  workspaceId: P ?? "",
24
- agentId: C
22
+ agentId: w
25
23
  }), [
26
24
  T,
27
- E,
28
- w,
29
25
  P,
30
- C
31
- ]), L = ie(I), R = u(() => L.data?.filter((e) => e.registered && e.authenticated) ?? [], [L.data]), z = R.length > 0;
32
- l(() => {
26
+ w
27
+ ]), L = ie(I), R = f(() => L.data?.filter((e) => e.registered && e.authenticated) ?? [], [L.data]), z = R.length > 0;
28
+ d(() => {
33
29
  if (!j && R.length > 0) {
34
30
  let e = R[0];
35
31
  e && M(e.name);
36
32
  }
37
33
  }, [R, j]);
38
34
  let B = s(I, j, { limit: 30 });
39
- l(() => {
35
+ d(() => {
40
36
  if (!N && B.data?.items.length) {
41
37
  let e = B.data.items[0];
42
- e && he(e.fqn);
38
+ e && me(e.fqn);
43
39
  }
44
40
  }, [B.data, N]);
45
41
  let V = s(I, j, { limit: 1 });
@@ -50,11 +46,11 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
50
46
  }), U = te(I, j, N, {
51
47
  state: "open",
52
48
  limit: 100
53
- }), W = ne(I, j, N), G = ee(I, j, N, 20), K = c(I, j, N), q = u(() => N ? V.data?.items.find((e) => e.fqn === N) ?? B.data?.items.find((e) => e.fqn === N) ?? null : V.data?.items[0] ?? null, [
49
+ }), W = ne(I, j, N), G = ee(I, j, N, 20), K = se(I, j, N), q = f(() => N ? V.data?.items.find((e) => e.fqn === N) ?? B.data?.items.find((e) => e.fqn === N) ?? null : V.data?.items[0] ?? null, [
54
50
  V.data,
55
51
  B.data,
56
52
  N
57
- ]), ge = u(() => ue({
53
+ ]), he = f(() => l({
58
54
  repo: q,
59
55
  pulls: H.data,
60
56
  issues: U.data,
@@ -68,27 +64,27 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
68
64
  W.data,
69
65
  G.data,
70
66
  K.data
71
- ]), J = oe(I, j, N, { limit: 30 }), Y = re(I, j, N), _e = u(() => le({
67
+ ]), J = oe(I, j, N, { limit: 30 }), Y = re(I, j, N), ge = f(() => c({
72
68
  pipelines: void 0,
73
69
  runs: J.data,
74
70
  analytics: Y.data
75
- }), [J.data, Y.data]), ve = H.isLoading || U.isLoading || W.isLoading, ye = J.isLoading || Y.isLoading, be = (H.error || U.error || W.error)?.message, xe = (J.error || Y.error)?.message, Se = de(() => {
71
+ }), [J.data, Y.data]), _e = H.isLoading || U.isLoading || W.isLoading, ve = J.isLoading || Y.isLoading, ye = (H.error || U.error || W.error)?.message, be = (J.error || Y.error)?.message, xe = u(() => {
76
72
  F.invalidateQueries({ queryKey: [
77
73
  "gitops",
78
74
  P ?? "",
79
- C,
80
- w
75
+ w,
76
+ T
81
77
  ] });
82
78
  }, [
83
79
  F,
84
80
  P,
85
- C,
86
- w
87
- ]), X = H.isFetching || J.isFetching, Ce = [
81
+ w,
82
+ T
83
+ ]), X = H.isFetching || J.isFetching, Se = [
88
84
  {
89
85
  id: "repo-stats",
90
86
  label: D("gitops.repoStats", "Repo Stats"),
91
- icon: f
87
+ icon: ue
92
88
  },
93
89
  {
94
90
  id: "ci",
@@ -98,35 +94,35 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
98
94
  {
99
95
  id: "git-ui",
100
96
  label: D("gitops.gitUI", "Git UI"),
101
- icon: p
97
+ icon: de
102
98
  }
103
- ], [Z, Q] = d(null), [$, we] = d(null);
104
- return l(() => {
99
+ ], [Z, Q] = p(null), [$, Ce] = p(null);
100
+ return d(() => {
105
101
  let e = !1, t = null;
106
102
  return (async () => {
107
- if (!C || !T || O !== "git-ui") {
103
+ if (!w || !E || O !== "git-ui") {
108
104
  Q(null);
109
105
  return;
110
106
  }
111
107
  try {
112
- let a = await r(C, w, "/ungit");
108
+ let a = await r(w, T, "/ungit");
113
109
  if (e) return;
114
- let o = `${T.replace(/\/$/, "")}/ungit/`;
115
- Q(n(o, a.token)), t = i(C, w, "/ungit", a, (e) => {
110
+ let o = `${E.replace(/\/$/, "")}/ungit/`;
111
+ Q(n(o, a.token)), t = i(w, T, "/ungit", a, (e) => {
116
112
  Q(n(o, e.token));
117
113
  });
118
114
  } catch (t) {
119
- e || we(t instanceof Error ? t.message : "Failed to fetch iframe token");
115
+ e || Ce(t instanceof Error ? t.message : "Failed to fetch iframe token");
120
116
  }
121
117
  })(), () => {
122
118
  e = !0, t?.cancel();
123
119
  };
124
120
  }, [
125
- C,
126
121
  w,
127
122
  T,
123
+ E,
128
124
  O
129
- ]), /* @__PURE__ */ y(pe.div, {
125
+ ]), /* @__PURE__ */ b(fe.div, {
130
126
  initial: {
131
127
  opacity: 0,
132
128
  y: 8
@@ -141,32 +137,32 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
141
137
  },
142
138
  transition: { duration: .2 },
143
139
  className: "space-y-4",
144
- "data-vibe-id": b,
145
- children: [/* @__PURE__ */ y("div", {
140
+ "data-vibe-id": x,
141
+ children: [/* @__PURE__ */ b("div", {
146
142
  className: "flex items-center justify-between",
147
- children: [/* @__PURE__ */ y("div", {
143
+ children: [/* @__PURE__ */ b("div", {
148
144
  className: "flex items-center gap-2",
149
- children: [/* @__PURE__ */ v(m, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ v("h3", {
145
+ children: [/* @__PURE__ */ y(m, { className: "size-5 text-text-secondary" }), /* @__PURE__ */ y("h3", {
150
146
  className: "font-semibold text-text-primary",
151
147
  children: D("vibeDetails.gitopsTitle", "GitOps")
152
148
  })]
153
- }), /* @__PURE__ */ y("div", {
149
+ }), /* @__PURE__ */ b("div", {
154
150
  className: "flex items-center gap-2",
155
151
  children: [
156
- z && /* @__PURE__ */ v(ce, {
152
+ z && /* @__PURE__ */ y(le, {
157
153
  providers: R.map((e) => e.name),
158
154
  selected: j,
159
155
  onChange: M
160
156
  }),
161
- z && (O === "repo-stats" || O === "ci") && /* @__PURE__ */ y("button", {
157
+ z && (O === "repo-stats" || O === "ci") && /* @__PURE__ */ b("button", {
162
158
  type: "button",
163
- onClick: Se,
159
+ onClick: xe,
164
160
  disabled: X,
165
161
  className: "flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors",
166
162
  "data-testid": "gitops-refresh",
167
- children: [/* @__PURE__ */ v(g, { className: `size-3 ${X ? "animate-spin" : ""}` }), D("gitops.refresh", "Refresh")]
163
+ children: [/* @__PURE__ */ y(g, { className: `size-3 ${X ? "animate-spin" : ""}` }), D("gitops.refresh", "Refresh")]
168
164
  }),
169
- !k && /* @__PURE__ */ v("button", {
165
+ !k && /* @__PURE__ */ y("button", {
170
166
  type: "button",
171
167
  onClick: () => A(!0),
172
168
  className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
@@ -175,45 +171,45 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
175
171
  })
176
172
  ]
177
173
  })]
178
- }), /* @__PURE__ */ y("div", {
174
+ }), /* @__PURE__ */ b("div", {
179
175
  className: "flex flex-col gap-4 lg:flex-row lg:gap-6",
180
- children: [/* @__PURE__ */ v("nav", {
176
+ children: [/* @__PURE__ */ y("nav", {
181
177
  className: "w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56",
182
178
  role: "tablist",
183
179
  "aria-label": D("gitops.navigation", "GitOps navigation"),
184
- children: Ce.map((e) => {
180
+ children: Se.map((e) => {
185
181
  let t = e.icon, n = O === e.id;
186
- return /* @__PURE__ */ y("button", {
182
+ return /* @__PURE__ */ b("button", {
187
183
  type: "button",
188
184
  role: "tab",
189
185
  "aria-selected": n,
190
- onClick: () => me(e.id),
186
+ onClick: () => pe(e.id),
191
187
  className: `flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${n ? "bg-action-primary-bg text-action-primary-text shadow-sm" : "text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary"}`,
192
- children: [/* @__PURE__ */ v(t, { className: "size-4 shrink-0" }), /* @__PURE__ */ v("span", {
188
+ children: [/* @__PURE__ */ y(t, { className: "size-4 shrink-0" }), /* @__PURE__ */ y("span", {
193
189
  className: "truncate",
194
190
  children: e.label
195
191
  })]
196
192
  }, e.id);
197
193
  })
198
- }), /* @__PURE__ */ y("div", {
194
+ }), /* @__PURE__ */ b("div", {
199
195
  className: "min-w-0 flex-1 space-y-4",
200
196
  children: [
201
- k && /* @__PURE__ */ v(se, {
197
+ k && /* @__PURE__ */ y(ce, {
202
198
  ctx: I,
203
199
  onCreated: () => {
204
200
  A(!1), L.refetch();
205
201
  },
206
202
  onCancel: () => A(!1)
207
203
  }),
208
- O === "repo-stats" && !k && /* @__PURE__ */ v(_, { children: z ? !T || !E ? /* @__PURE__ */ v(S, {
204
+ O === "repo-stats" && !k && /* @__PURE__ */ y(v, { children: z ? w ? /* @__PURE__ */ y(a, {
205
+ stats: he,
206
+ loading: _e || L.isLoading,
207
+ error: ye
208
+ }) : /* @__PURE__ */ y(C, {
209
209
  tr: D,
210
210
  message: "gitops.noTunnel",
211
211
  fallback: "Waiting for agent tunnel. Make sure the agent is running and a tunnel is active."
212
- }) : /* @__PURE__ */ v(a, {
213
- stats: ge,
214
- loading: ve || L.isLoading,
215
- error: be
216
- }) : /* @__PURE__ */ v(x, {
212
+ }) : /* @__PURE__ */ y(S, {
217
213
  tr: D,
218
214
  icon: m,
219
215
  message: "gitops.noProvidersVibe",
@@ -221,15 +217,15 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
221
217
  ctaLabel: D("gitops.addProvider", "Add Provider"),
222
218
  onCta: () => A(!0)
223
219
  }) }),
224
- O === "ci" && !k && /* @__PURE__ */ v(_, { children: z ? !T || !E ? /* @__PURE__ */ v(S, {
220
+ O === "ci" && !k && /* @__PURE__ */ y(v, { children: z ? w ? /* @__PURE__ */ y(o, {
221
+ status: ge,
222
+ loading: ve || L.isLoading,
223
+ error: be
224
+ }) : /* @__PURE__ */ y(C, {
225
225
  tr: D,
226
226
  message: "gitops.noTunnelCi",
227
227
  fallback: "Waiting for agent tunnel."
228
- }) : /* @__PURE__ */ v(o, {
229
- status: _e,
230
- loading: ye || L.isLoading,
231
- error: xe
232
- }) : /* @__PURE__ */ v(x, {
228
+ }) : /* @__PURE__ */ y(S, {
233
229
  tr: D,
234
230
  icon: h,
235
231
  message: "gitops.noProvidersCi",
@@ -237,26 +233,26 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
237
233
  ctaLabel: D("gitops.addProvider", "Add Provider"),
238
234
  onCta: () => A(!0)
239
235
  }) }),
240
- O === "git-ui" && /* @__PURE__ */ v(t, {
241
- agentId: C || null,
236
+ O === "git-ui" && /* @__PURE__ */ y(t, {
237
+ agentId: w || null,
242
238
  required: ["@vibecontrols/vibe-plugin-tool-git"],
243
239
  mode: "all",
244
240
  eyebrow: D("gitops.ungitEyebrow", "Ungit plugin required"),
245
241
  title: D("gitops.ungitTitle", "Install the visual git client"),
246
242
  description: D("gitops.ungitDescription", "The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue."),
247
- children: $ ? /* @__PURE__ */ v("div", {
243
+ children: $ ? /* @__PURE__ */ y("div", {
248
244
  className: "rounded-lg border border-border-default bg-status-error-bg-subtle p-4",
249
- children: /* @__PURE__ */ v("p", {
245
+ children: /* @__PURE__ */ y("p", {
250
246
  className: "text-sm text-status-error-text",
251
247
  children: $
252
248
  })
253
- }) : Z ? /* @__PURE__ */ v("iframe", {
249
+ }) : Z ? /* @__PURE__ */ y("iframe", {
254
250
  src: Z,
255
251
  title: "Ungit",
256
252
  className: "w-full min-h-[600px] rounded-lg border border-border-default"
257
- }) : /* @__PURE__ */ v("div", {
253
+ }) : /* @__PURE__ */ y("div", {
258
254
  className: "rounded-lg border border-border-default bg-bg-sunken p-4",
259
- children: /* @__PURE__ */ v("p", {
255
+ children: /* @__PURE__ */ y("p", {
260
256
  className: "text-sm text-text-secondary",
261
257
  children: D("gitops.ungitLoading", "Loading Ungit…")
262
258
  })
@@ -266,29 +262,29 @@ var b = ({ vibeId: b, agentId: C, profile: w, tunnelUrl: T, apiKey: E, tr: D })
266
262
  })]
267
263
  })]
268
264
  }, "gitops");
269
- }, x = ({ tr: e, icon: t, message: n, fallback: r, ctaLabel: i, onCta: a }) => /* @__PURE__ */ y("div", {
265
+ }, S = ({ tr: e, icon: t, message: n, fallback: r, ctaLabel: i, onCta: a }) => /* @__PURE__ */ b("div", {
270
266
  className: "rounded-lg border border-border-default bg-bg-surface p-6 text-center",
271
267
  children: [
272
- /* @__PURE__ */ v(t, { className: "size-8 text-text-secondary mx-auto mb-3" }),
273
- /* @__PURE__ */ v("p", {
268
+ /* @__PURE__ */ y(t, { className: "size-8 text-text-secondary mx-auto mb-3" }),
269
+ /* @__PURE__ */ y("p", {
274
270
  className: "text-sm text-text-secondary",
275
271
  children: e(n, r)
276
272
  }),
277
- i && a && /* @__PURE__ */ v("button", {
273
+ i && a && /* @__PURE__ */ y("button", {
278
274
  type: "button",
279
275
  onClick: a,
280
276
  className: "mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
281
277
  children: i
282
278
  })
283
279
  ]
284
- }), S = ({ tr: e, message: t, fallback: n }) => /* @__PURE__ */ v("div", {
280
+ }), C = ({ tr: e, message: t, fallback: n }) => /* @__PURE__ */ y("div", {
285
281
  className: "rounded-lg border border-border-default bg-bg-sunken p-4",
286
- children: /* @__PURE__ */ v("p", {
282
+ children: /* @__PURE__ */ y("p", {
287
283
  className: "text-sm text-text-secondary",
288
284
  children: e(t, n)
289
285
  })
290
286
  });
291
287
  //#endregion
292
- export { b as VibeGitOpsTab };
288
+ export { x as VibeGitOpsTab };
293
289
 
294
290
  //# sourceMappingURL=VibeGitOpsTab.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n tunnelUrl: string | null;\n apiKey: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n apiKey,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ tunnelUrl, apiKey, profile, workspaceId: workspaceId ?? '', agentId }),\n [tunnelUrl, apiKey, profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // F5 residual — Ungit's reverse proxy lives at `/ungit/` (declared\n // by the external `vibe-plugin-tool-git` package's publicPaths +\n // meta.url override). The plugin's createUngitProxy validates the\n // entry-document auth via `?vt=<token>` (or `?apiKey=`), then\n // issues its own session cookie for sub-requests. Switching this\n // to a host-level hash→cookie shim (like F5 does for `/ui/<p>`)\n // would require an SDK contract change + coordinated republish of\n // the external plugin package. Until that ships, the iframe-token\n // sits in the URL once. Mitigations in place: F6 makes it single-\n // use (replayed token rejected); F7 strips the token before\n // upstream forwarding so Ungit never sees it; F10 enforces Origin\n // on non-GET/HEAD sub-requests. Residual surface: one query string\n // appears in CF logs + browser history for the initial document.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/ungit');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/ungit/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/ungit',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !tunnelUrl || !apiKey ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !tunnelUrl || !apiKey ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n /**\n * Tunnel URL — retained ONLY for the `git-ui` iframe handshake\n * (ungit subpath) which still bootstraps via a one-time iframe token.\n * GitOps REST calls no longer use this; they route through the svc\n * proxy (F4 Wave C).\n */\n tunnelUrl: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ profile, workspaceId: workspaceId ?? '', agentId }),\n [profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // F5 residual — Ungit's reverse proxy lives at `/ungit/` (declared\n // by the external `vibe-plugin-tool-git` package's publicPaths +\n // meta.url override). The plugin's createUngitProxy validates the\n // entry-document auth via `?vt=<token>` (or `?apiKey=`), then\n // issues its own session cookie for sub-requests. Switching this\n // to a host-level hash→cookie shim (like F5 does for `/ui/<p>`)\n // would require an SDK contract change + coordinated republish of\n // the external plugin package. Until that ships, the iframe-token\n // sits in the URL once. Mitigations in place: F6 makes it single-\n // use (replayed token rejected); F7 strips the token before\n // upstream forwarding so Ungit never sees it; F10 enforces Origin\n // on non-GET/HEAD sub-requests. Residual surface: one query string\n // appears in CF logs + browser history for the initial document.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/ungit');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/ungit/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/ungit',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
@@ -132,19 +132,12 @@ function y({ rowKey: e }) {
132
132
  ]
133
133
  }, e);
134
134
  }
135
- function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit: b = 20 }) {
136
- let { data: x, isLoading: S, error: C, isFetching: w, refetch: T } = e(t(() => ({
135
+ function b({ vibeId: r, agentId: i, profile: a, limit: o = 20 }) {
136
+ let { data: c, isLoading: b, error: x, isFetching: S, refetch: C } = e(t(() => ({
137
137
  agentId: i,
138
- tunnelUrl: a,
139
- apiKey: o,
140
- profile: c
141
- }), [
142
- i,
143
- a,
144
- o,
145
- c
146
- ]), r, b);
147
- return i && a && o ? /* @__PURE__ */ d("section", {
138
+ profile: a
139
+ }), [i, a]), r, o);
140
+ return i ? /* @__PURE__ */ d("section", {
148
141
  "data-testid": "security-scan-list",
149
142
  className: "rounded-lg border border-border-default bg-bg-surface",
150
143
  children: [
@@ -159,19 +152,19 @@ function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit:
159
152
  className: "text-xs text-text-secondary mt-0.5",
160
153
  children: [
161
154
  "Read live from the agent. Showing up to ",
162
- b,
155
+ o,
163
156
  " most recently active runs for this vibe."
164
157
  ]
165
158
  })]
166
159
  }), /* @__PURE__ */ u("button", {
167
160
  type: "button",
168
- onClick: () => void T(),
169
- disabled: w,
161
+ onClick: () => void C(),
162
+ disabled: S,
170
163
  className: "px-2.5 py-1 text-xs rounded-md border border-border-default bg-bg-sunken text-text-primary hover:bg-action-primary-bg-hover disabled:opacity-50",
171
- children: w ? "Refreshing…" : "Refresh"
164
+ children: S ? "Refreshing…" : "Refresh"
172
165
  })]
173
166
  }),
174
- C && /* @__PURE__ */ d("div", {
167
+ x && /* @__PURE__ */ d("div", {
175
168
  className: "m-4 flex items-start gap-2 rounded-md border border-status-error-border bg-status-error-bg/10 p-3 text-sm",
176
169
  children: [/* @__PURE__ */ u(n, { className: "size-4 text-status-error-text mt-0.5 shrink-0" }), /* @__PURE__ */ d("div", {
177
170
  className: "text-text-primary",
@@ -180,7 +173,7 @@ function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit:
180
173
  children: "Couldn't load scan runs"
181
174
  }), /* @__PURE__ */ u("p", {
182
175
  className: "mt-0.5 text-xs text-text-secondary",
183
- children: C.message
176
+ children: x.message
184
177
  })]
185
178
  })]
186
179
  }),
@@ -221,12 +214,12 @@ function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit:
221
214
  })
222
215
  ] })
223
216
  }), /* @__PURE__ */ d("tbody", { children: [
224
- S && x === void 0 && /* @__PURE__ */ d(l, { children: [
217
+ b && c === void 0 && /* @__PURE__ */ d(l, { children: [
225
218
  /* @__PURE__ */ u(y, { rowKey: 0 }),
226
219
  /* @__PURE__ */ u(y, { rowKey: 1 }),
227
220
  /* @__PURE__ */ u(y, { rowKey: 2 })
228
221
  ] }),
229
- !S && (x?.length ?? 0) === 0 && !C && /* @__PURE__ */ u("tr", { children: /* @__PURE__ */ u("td", {
222
+ !b && (c?.length ?? 0) === 0 && !x && /* @__PURE__ */ u("tr", { children: /* @__PURE__ */ u("td", {
230
223
  colSpan: 7,
231
224
  className: "px-3 py-8",
232
225
  children: /* @__PURE__ */ d("div", {
@@ -244,7 +237,7 @@ function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit:
244
237
  ]
245
238
  })
246
239
  }) }),
247
- (x ?? []).map((e) => /* @__PURE__ */ d("tr", {
240
+ (c ?? []).map((e) => /* @__PURE__ */ d("tr", {
248
241
  className: "border-t border-border-subtle align-middle",
249
242
  children: [
250
243
  /* @__PURE__ */ u("td", {
@@ -299,7 +292,7 @@ function b({ vibeId: r, agentId: i, tunnelUrl: a, apiKey: o, profile: c, limit:
299
292
  }) : /* @__PURE__ */ u("section", {
300
293
  "data-testid": "security-scan-list",
301
294
  className: "rounded-lg border border-border-default bg-bg-surface p-4 text-sm text-text-secondary",
302
- children: "Connect to a reachable agent to load scan history. Scan runs are read directly from the agent over the tunnel — there's no backend cache."
295
+ children: "Connect to a reachable agent to load scan history. Scan runs are read from the agent via the svc GraphQL proxy — there's no backend cache."
303
296
  });
304
297
  }
305
298
  //#endregion