@burdenoff/microfe-vibecontrols 2026.528.1 → 2026.528.3
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 +1 -0
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/security/SecurityScanList.js +308 -0
- package/dist/components/security/SecurityScanList.js.map +1 -0
- package/dist/components/security/SecurityTabPanel.js +22 -18
- package/dist/components/security/SecurityTabPanel.js.map +1 -1
- package/dist/components/security/agentClient.js +78 -0
- package/dist/components/security/agentClient.js.map +1 -0
- package/dist/components/security/hooks.js +33 -0
- package/dist/components/security/hooks.js.map +1 -0
- package/dist/components/security/index.js +3 -0
- package/dist/components/settings/WorkspaceDefaultsSection.js.map +1 -1
- package/dist/components/shared/ConfirmDialog.js.map +1 -1
- package/dist/components/shared/EmptyState.js.map +1 -1
- package/dist/components/targets/TargetCard.js +82 -74
- package/dist/components/targets/TargetCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/generated/wspace-operations.js +1 -0
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +10 -1
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +274 -259
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +570 -567
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityTabPanel.js","names":[],"sources":["../../../src/components/security/SecurityTabPanel.tsx"],"sourcesContent":["/**\n * Security tab on Vibe detail.\n *\n * Wave 1
|
|
1
|
+
{"version":3,"file":"SecurityTabPanel.js","names":[],"sources":["../../../src/components/security/SecurityTabPanel.tsx"],"sourcesContent":["/**\n * Security tab on Vibe detail.\n *\n * Wave 1: surface the security plugins so the user can find +\n * install them from the UI (same pattern as the AI page).\n *\n * Wave 2 (this commit): once the meta plugin is installed, mount\n * `SecurityScanList` which reads recent scan runs straight from the\n * agent over the tunnel (`/api/profiles/<profile>/security/*`). No\n * backend GraphQL involvement — all per-vibe scan history lives in\n * the agent's local SQLite cache.\n */\n\nimport { Shield } from 'lucide-react';\nimport { PluginGate } from '../shared/PluginGate';\nimport { PluginHarnessPicker } from '../ai/PluginHarnessPicker';\nimport { SECURITY_META_PACKAGE, SECURITY_PROVIDER_PACKAGES } from '@/constants/pluginCatalog';\nimport { SecurityScanList } from './SecurityScanList';\n\ninterface PanelProps {\n vibeId: string;\n agentId: string | null;\n /**\n * Canonical agent profile name. Threaded through from VibeDetailsPage's\n * `useActiveAgentProfile` so the tab targets the same profile the rest\n * of the page is talking to.\n */\n profile?: string;\n /** Agent tunnel URL. Null when the active agent is unreachable. */\n tunnelUrl?: string | null;\n /** Agent API key. Null when the agent hasn't published one yet. */\n apiKey?: string | null;\n}\n\nexport function SecurityTabPanel({\n vibeId,\n agentId,\n profile = 'default',\n tunnelUrl = null,\n apiKey = null,\n}: PanelProps): React.ReactElement {\n return (\n <div data-testid=\"security-tab-panel\" className=\"space-y-4\">\n <header className=\"flex items-start gap-3\">\n <div className=\"size-9 rounded-md bg-bg-sunken flex items-center justify-center shrink-0\">\n <Shield className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold text-text-primary\">Security</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Install + run security scans across the 14-stage lifecycle. Plugins run on the agent;\n findings live in its local SQLite cache.\n </p>\n </div>\n </header>\n\n <PluginGate\n agentId={agentId}\n required={[SECURITY_META_PACKAGE]}\n mode=\"all\"\n eyebrow=\"Security plugin required\"\n title=\"Install the Security Orchestrator\"\n description=\"@vibecontrols/vibe-plugin-security routes scans across per-stage providers. Required before installing individual stage providers.\"\n >\n <PluginHarnessPicker\n packages={SECURITY_PROVIDER_PACKAGES}\n agentId={agentId}\n title=\"Security Providers\"\n description=\"One plugin per stage in the security lifecycle. Install the providers you want to use; uninstalled stages stay skipped.\"\n />\n <SecurityScanList\n vibeId={vibeId}\n agentId={agentId}\n tunnelUrl={tunnelUrl}\n apiKey={apiKey}\n profile={profile}\n />\n </PluginGate>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAkCA,SAAgB,EAAiB,EAC/B,WACA,YACA,aAAU,WACV,eAAY,MACZ,YAAS,QACwB;AACjC,QACE,kBAAC,OAAD;EAAK,eAAY;EAAqB,WAAU;YAAhD,CACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAQ,WAAU,8BAA+B,CAAA;IAC7C,CAAA,EACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA4C;KAAa,CAAA,EACvE,kBAAC,KAAD;KAAG,WAAU;eAAqC;KAG9C,CAAA,CACA;MACC;MAET,kBAAC,GAAD;GACW;GACT,UAAU,CAAC,EAAsB;GACjC,MAAK;GACL,SAAQ;GACR,OAAM;GACN,aAAY;aANd,CAQE,kBAAC,GAAD;IACE,UAAU;IACD;IACT,OAAM;IACN,aAAY;IACZ,CAAA,EACF,kBAAC,GAAD;IACU;IACC;IACE;IACH;IACC;IACT,CAAA,CACS;KACT"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//#region src/components/security/agentClient.ts
|
|
2
|
+
var e = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
constructor(e, t, n) {
|
|
6
|
+
super(n), this.name = "SecurityAgentError", this.code = e, this.status = t;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
function t(e) {
|
|
10
|
+
return `${e.tunnelUrl.replace(/\/$/, "")}/api/profiles/${encodeURIComponent(e.profile)}/security`;
|
|
11
|
+
}
|
|
12
|
+
function n(e, t) {
|
|
13
|
+
return {
|
|
14
|
+
"x-agent-api-key": e.apiKey,
|
|
15
|
+
Accept: "application/json",
|
|
16
|
+
...t ?? {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function r(t, n) {
|
|
20
|
+
if (!t.ok) {
|
|
21
|
+
let r = "UPSTREAM", i = `security ${n} ${t.status}`;
|
|
22
|
+
try {
|
|
23
|
+
let e = await t.json();
|
|
24
|
+
e.code && (r = e.code), e.error && (i = e.error);
|
|
25
|
+
} catch {}
|
|
26
|
+
throw new e(r, t.status, i);
|
|
27
|
+
}
|
|
28
|
+
return await t.json();
|
|
29
|
+
}
|
|
30
|
+
async function i(e, i) {
|
|
31
|
+
return r(await fetch(`${t(e)}${i}`, { headers: n(e) }), i);
|
|
32
|
+
}
|
|
33
|
+
async function a(e, i, a) {
|
|
34
|
+
return r(await fetch(`${t(e)}${i}`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: n(e, { "Content-Type": "application/json" }),
|
|
37
|
+
body: a === void 0 ? void 0 : JSON.stringify(a)
|
|
38
|
+
}), i);
|
|
39
|
+
}
|
|
40
|
+
var o = {
|
|
41
|
+
listProviders(e) {
|
|
42
|
+
return i(e, "/providers");
|
|
43
|
+
},
|
|
44
|
+
listFindings(e, t = {}) {
|
|
45
|
+
let n = new URLSearchParams();
|
|
46
|
+
t.vibeId && n.set("vibeId", t.vibeId), t.severity && n.set("severity", t.severity), t.status && n.set("status", t.status), t.limit !== void 0 && n.set("limit", String(t.limit));
|
|
47
|
+
let r = n.toString();
|
|
48
|
+
return i(e, `/findings${r ? `?${r}` : ""}`);
|
|
49
|
+
},
|
|
50
|
+
async getScan(e, t) {
|
|
51
|
+
return (await i(e, `/scan/${encodeURIComponent(t)}`)).run;
|
|
52
|
+
},
|
|
53
|
+
startScan(e, t) {
|
|
54
|
+
return a(e, "/scan", t);
|
|
55
|
+
},
|
|
56
|
+
cancelScan(e, t) {
|
|
57
|
+
return a(e, `/scan/${encodeURIComponent(t)}/cancel`);
|
|
58
|
+
},
|
|
59
|
+
async listRecentScanRuns(t, n, r = 20) {
|
|
60
|
+
let i = await o.listFindings(t, {
|
|
61
|
+
vibeId: n,
|
|
62
|
+
limit: Math.min(1e3, Math.max(100, r * 25))
|
|
63
|
+
}), a = /* @__PURE__ */ new Set(), s = [];
|
|
64
|
+
for (let e of i.findings) if (!a.has(e.scanRunId) && (a.add(e.scanRunId), s.push(e.scanRunId), s.length >= r)) break;
|
|
65
|
+
return (await Promise.all(s.map(async (n) => {
|
|
66
|
+
try {
|
|
67
|
+
return await o.getScan(t, n);
|
|
68
|
+
} catch (t) {
|
|
69
|
+
if (t instanceof e && t.status === 404) return null;
|
|
70
|
+
throw t;
|
|
71
|
+
}
|
|
72
|
+
}))).filter((e) => e !== null);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { e as SecurityAgentError, o as securityAgent };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=agentClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentClient.js","names":[],"sources":["../../../src/components/security/agentClient.ts"],"sourcesContent":["/**\n * Agent-tunnel REST client for the security meta plugin.\n *\n * All calls go from the browser straight to the agent's\n * `/api/profiles/<profile>/security/*` routes via the tunnel URL.\n * No backend GraphQL hop — same direct-tunnel pattern as\n * `src/services/gitopsApi.ts`. Auth header: `x-agent-api-key`.\n *\n * The agent mounts `vibe-plugin-security` with `apiPrefix: \"/api/security\"`;\n * the agent's `dispatchProfileRequest` strips `/api/profiles/<profile>`\n * and prepends `/api`, so the canonical external path collapses to\n * `/api/profiles/<profile>/security/<endpoint>`.\n */\nimport type { SecuritySummary } from './types';\n\nexport interface SecurityAgentOpts {\n /** Agent tunnel URL (HTTPS), e.g. https://abc123.tunnels.example.com. */\n tunnelUrl: string;\n /** Agent API key (rotated + persisted per agent). */\n apiKey: string;\n /** Canonical profile name (e.g. \"default\"). */\n profile: string;\n}\n\nexport class SecurityAgentError extends Error {\n readonly code: string;\n readonly status: number;\n constructor(code: string, status: number, message: string) {\n super(message);\n this.name = 'SecurityAgentError';\n this.code = code;\n this.status = status;\n }\n}\n\n/** Agent-side severity enum (lowercase) — see vibe-plugin-security types.ts. */\nexport type AgentSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';\n\n/** Agent-side finding status enum (lowercase). */\nexport type AgentFindingStatus = 'open' | 'accepted' | 'fixed' | 'false_positive' | 'suppressed';\n\n/** Agent-side scan status enum (lowercase). */\nexport type AgentScanStatus =\n | 'queued'\n | 'running'\n | 'succeeded'\n | 'failed'\n | 'errored'\n | 'cancelled';\n\n/** Agent-side scan conclusion enum (lowercase). */\nexport type AgentScanConclusion = 'pass' | 'warn' | 'fail' | 'skipped';\n\n/** Agent-side policy level enum (lowercase). */\nexport type AgentPolicyLevel = 'advisory' | 'warn' | 'block';\n\n/** Agent-side stage enum — dotted form (e.g. `pull_request.fast`). */\nexport type AgentStage =\n | 'repo.onboard'\n | 'developer.local'\n | 'pull_request.fast'\n | 'pull_request.deep'\n | 'main.merge'\n | 'build'\n | 'package.publish'\n | 'deploy.preview'\n | 'deploy.alpha'\n | 'promote.prod'\n | 'runtime.continuous'\n | 'scheduled.rescan'\n | 'incident.response'\n | 'archive.offboard';\n\n/** Per-stage provider directory returned by GET /providers. */\nexport interface SecurityProvidersByStage {\n stages: Record<string, Array<{ name: string; isDefault: boolean }>>;\n}\n\n/** One scan run as surfaced by GET /scan/:runId. Mirrors `ScanRunRecord`. */\nexport interface SecurityScanRun {\n runId: string;\n vibeId: string;\n workspaceId: string;\n repoUrl: string;\n commit: string;\n stage: AgentStage;\n providerName: string;\n status: AgentScanStatus;\n conclusion?: AgentScanConclusion;\n startedAt?: number;\n finishedAt?: number;\n durationMs?: number;\n summary: SecuritySummary;\n policyLevel: AgentPolicyLevel;\n errorReason?: string;\n pushed?: boolean;\n}\n\n/** One finding as surfaced by GET /findings. Mirrors `FindingRecord`. */\nexport interface SecurityFinding {\n fingerprint: string;\n vibeId: string;\n scanRunId: string;\n ruleId: string;\n title: string;\n description?: string;\n severity: AgentSeverity;\n category: string;\n status: AgentFindingStatus;\n cwe?: string[];\n cve?: string;\n file?: string;\n line?: number;\n column?: number;\n packageName?: string;\n packageVersion?: string;\n fixedVersion?: string;\n remediation?: string;\n rawProviderRef?: string;\n redactedSample?: string;\n firstSeenAt: number;\n lastSeenAt: number;\n}\n\nfunction base(o: SecurityAgentOpts): string {\n return `${o.tunnelUrl.replace(/\\/$/, '')}/api/profiles/${encodeURIComponent(o.profile)}/security`;\n}\n\nfunction headers(o: SecurityAgentOpts, extra?: Record<string, string>): HeadersInit {\n return {\n 'x-agent-api-key': o.apiKey,\n Accept: 'application/json',\n ...(extra ?? {}),\n };\n}\n\nasync function handle<T>(res: Response, path: string): Promise<T> {\n if (!res.ok) {\n let code = 'UPSTREAM';\n let message = `security ${path} ${res.status}`;\n try {\n const body = (await res.json()) as { code?: string; error?: string };\n if (body.code) code = body.code;\n if (body.error) message = body.error;\n } catch {\n // body parse failure — keep default message\n }\n throw new SecurityAgentError(code, res.status, message);\n }\n return (await res.json()) as T;\n}\n\nasync function get<T>(o: SecurityAgentOpts, path: string): Promise<T> {\n const res = await fetch(`${base(o)}${path}`, { headers: headers(o) });\n return handle<T>(res, path);\n}\n\nasync function post<T>(o: SecurityAgentOpts, path: string, body?: unknown): Promise<T> {\n const res = await fetch(`${base(o)}${path}`, {\n method: 'POST',\n headers: headers(o, { 'Content-Type': 'application/json' }),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n return handle<T>(res, path);\n}\n\nexport interface FindingsFilter {\n vibeId?: string;\n severity?: AgentSeverity;\n status?: AgentFindingStatus;\n limit?: number;\n}\n\nexport interface FindingsPage {\n findings: SecurityFinding[];\n total: number;\n}\n\nexport interface StartScanInput {\n vibeId: string;\n workspaceId: string;\n repoUrl: string;\n repoLocalPath: string;\n commit: string;\n stage: AgentStage;\n providerName?: string;\n policyLevel?: AgentPolicyLevel;\n config?: Record<string, unknown>;\n profile?: { kind: string; languages: string[]; runtimes: string[] };\n}\n\nexport interface StartScanResult {\n runId: string;\n status: AgentScanStatus;\n}\n\nexport const securityAgent = {\n /** GET /providers — per-stage provider directory. */\n listProviders(o: SecurityAgentOpts): Promise<SecurityProvidersByStage> {\n return get(o, '/providers');\n },\n\n /** GET /findings — filtered finding list. */\n listFindings(o: SecurityAgentOpts, filter: FindingsFilter = {}): Promise<FindingsPage> {\n const q = new URLSearchParams();\n if (filter.vibeId) q.set('vibeId', filter.vibeId);\n if (filter.severity) q.set('severity', filter.severity);\n if (filter.status) q.set('status', filter.status);\n if (filter.limit !== undefined) q.set('limit', String(filter.limit));\n const qs = q.toString();\n return get(o, `/findings${qs ? `?${qs}` : ''}`);\n },\n\n /** GET /scan/:runId — single scan run by id. */\n async getScan(o: SecurityAgentOpts, runId: string): Promise<SecurityScanRun> {\n const res = await get<{ run: SecurityScanRun }>(o, `/scan/${encodeURIComponent(runId)}`);\n return res.run;\n },\n\n /** POST /scan — start a scan. */\n startScan(o: SecurityAgentOpts, input: StartScanInput): Promise<StartScanResult> {\n return post(o, '/scan', input);\n },\n\n /** POST /scan/:runId/cancel — cancel an in-flight scan. */\n cancelScan(o: SecurityAgentOpts, runId: string): Promise<{ cancelled: boolean }> {\n return post(o, `/scan/${encodeURIComponent(runId)}/cancel`);\n },\n\n /**\n * Aggregate recent scan runs for a vibe by fanning out from the\n * findings list (the agent doesn't expose a list-runs endpoint).\n *\n * Strategy: list findings filtered by vibeId, take the distinct\n * `scanRunId`s (preserving last-seen order from the agent's\n * `ORDER BY last_seen_at DESC`), then resolve each id via\n * GET /scan/:runId. Unresolvable runs are dropped silently.\n */\n async listRecentScanRuns(\n o: SecurityAgentOpts,\n vibeId: string,\n limit = 20\n ): Promise<SecurityScanRun[]> {\n // Pull enough findings to surface ~`limit` distinct runs even when a\n // single run produces many findings. The agent caps at 1000.\n const page = await securityAgent.listFindings(o, {\n vibeId,\n limit: Math.min(1000, Math.max(100, limit * 25)),\n });\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const f of page.findings) {\n if (!seen.has(f.scanRunId)) {\n seen.add(f.scanRunId);\n ids.push(f.scanRunId);\n if (ids.length >= limit) break;\n }\n }\n const runs = await Promise.all(\n ids.map(async (id) => {\n try {\n return await securityAgent.getScan(o, id);\n } catch (err) {\n if (err instanceof SecurityAgentError && err.status === 404) return null;\n throw err;\n }\n })\n );\n return runs.filter((r): r is SecurityScanRun => r !== null);\n },\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SecurityAgentError as e, securityAgent as t } from "./agentClient.js";
|
|
2
|
+
import { useQuery as n } from "@tanstack/react-query";
|
|
3
|
+
//#region src/components/security/hooks.ts
|
|
4
|
+
function r(e) {
|
|
5
|
+
return !e.tunnelUrl || !e.apiKey || !e.agentId ? null : {
|
|
6
|
+
tunnelUrl: e.tunnelUrl,
|
|
7
|
+
apiKey: e.apiKey,
|
|
8
|
+
profile: e.profile
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function i(e, ...t) {
|
|
12
|
+
return [
|
|
13
|
+
"security",
|
|
14
|
+
e.agentId ?? "_",
|
|
15
|
+
e.profile,
|
|
16
|
+
...t
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
function a(a, o, s = 20) {
|
|
20
|
+
return n({
|
|
21
|
+
queryKey: i(a, "recent-scans", o, s),
|
|
22
|
+
enabled: !!r(a) && !!o,
|
|
23
|
+
queryFn: async () => {
|
|
24
|
+
let n = r(a);
|
|
25
|
+
if (!n) throw new e("INVALID", 0, "no agent opts");
|
|
26
|
+
return t.listRecentScanRuns(n, o, s);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { a as useSecurityRecentScansQuery };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","names":[],"sources":["../../../src/components/security/hooks.ts"],"sourcesContent":["/**\n * React Query hooks over the security agent REST API.\n *\n * Cache keys include the agent identity (tunnelUrl proxy via agentId +\n * profile) so two simultaneously-active vibes against different agents\n * stay isolated. Mirrors the layout of `src/hooks/useGitops.ts`.\n */\nimport {\n useMutation,\n useQuery,\n useQueryClient,\n type UseMutationResult,\n type UseQueryResult,\n} from '@tanstack/react-query';\n\nimport {\n securityAgent,\n SecurityAgentError,\n type FindingsFilter,\n type FindingsPage,\n type SecurityAgentOpts,\n type SecurityProvidersByStage,\n type SecurityScanRun,\n type StartScanInput,\n type StartScanResult,\n} from './agentClient';\n\nexport interface UseSecurityAgentContext {\n /** Required: agent tunnel URL. */\n tunnelUrl: string | null;\n /** Required: agent API key. */\n apiKey: string | null;\n /** Required: canonical profile name. */\n profile: string;\n /** Agent id — cache scope, not sent to agent. */\n agentId: string | null;\n}\n\nfunction ensureOpts(ctx: UseSecurityAgentContext): SecurityAgentOpts | null {\n if (!ctx.tunnelUrl || !ctx.apiKey || !ctx.agentId) return null;\n return { tunnelUrl: ctx.tunnelUrl, apiKey: ctx.apiKey, profile: ctx.profile };\n}\n\nfunction key(ctx: UseSecurityAgentContext, ...rest: unknown[]): unknown[] {\n return ['security', ctx.agentId ?? '_', ctx.profile, ...rest];\n}\n\nexport function useSecurityProvidersQuery(\n ctx: UseSecurityAgentContext\n): UseQueryResult<SecurityProvidersByStage, SecurityAgentError> {\n return useQuery({\n queryKey: key(ctx, 'providers'),\n enabled: !!ensureOpts(ctx),\n queryFn: async () => {\n const opts = ensureOpts(ctx);\n if (!opts) throw new SecurityAgentError('INVALID', 0, 'no agent opts');\n return securityAgent.listProviders(opts);\n },\n });\n}\n\nexport function useSecurityFindingsQuery(\n ctx: UseSecurityAgentContext,\n filter: FindingsFilter\n): UseQueryResult<FindingsPage, SecurityAgentError> {\n return useQuery({\n queryKey: key(ctx, 'findings', filter.vibeId, filter.severity, filter.status, filter.limit),\n enabled: !!ensureOpts(ctx),\n queryFn: async () => {\n const opts = ensureOpts(ctx);\n if (!opts) throw new SecurityAgentError('INVALID', 0, 'no agent opts');\n return securityAgent.listFindings(opts, filter);\n },\n });\n}\n\nexport function useSecurityRecentScansQuery(\n ctx: UseSecurityAgentContext,\n vibeId: string,\n limit = 20\n): UseQueryResult<SecurityScanRun[], SecurityAgentError> {\n return useQuery({\n queryKey: key(ctx, 'recent-scans', vibeId, limit),\n enabled: !!ensureOpts(ctx) && !!vibeId,\n queryFn: async () => {\n const opts = ensureOpts(ctx);\n if (!opts) throw new SecurityAgentError('INVALID', 0, 'no agent opts');\n return securityAgent.listRecentScanRuns(opts, vibeId, limit);\n },\n });\n}\n\nexport function useStartSecurityScanMutation(\n ctx: UseSecurityAgentContext\n): UseMutationResult<StartScanResult, SecurityAgentError, StartScanInput> {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: StartScanInput) => {\n const opts = ensureOpts(ctx);\n if (!opts) throw new SecurityAgentError('INVALID', 0, 'no agent opts');\n return securityAgent.startScan(opts, input);\n },\n onSuccess: () => {\n void qc.invalidateQueries({ queryKey: key(ctx) });\n },\n });\n}\n"],"mappings":";;;AAsCA,SAAS,EAAW,GAAwD;AAE1E,QADI,CAAC,EAAI,aAAa,CAAC,EAAI,UAAU,CAAC,EAAI,UAAgB,OACnD;EAAE,WAAW,EAAI;EAAW,QAAQ,EAAI;EAAQ,SAAS,EAAI;EAAS;;AAG/E,SAAS,EAAI,GAA8B,GAAG,GAA4B;AACxE,QAAO;EAAC;EAAY,EAAI,WAAW;EAAK,EAAI;EAAS,GAAG;EAAK;;AAgC/D,SAAgB,EACd,GACA,GACA,IAAQ,IAC+C;AACvD,QAAO,EAAS;EACd,UAAU,EAAI,GAAK,gBAAgB,GAAQ,EAAM;EACjD,SAAS,CAAC,CAAC,EAAW,EAAI,IAAI,CAAC,CAAC;EAChC,SAAS,YAAY;GACnB,IAAM,IAAO,EAAW,EAAI;AAC5B,OAAI,CAAC,EAAM,OAAM,IAAI,EAAmB,WAAW,GAAG,gBAAgB;AACtE,UAAO,EAAc,mBAAmB,GAAM,GAAQ,EAAM;;EAE/D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceDefaultsSection.js","names":[],"sources":["../../../src/components/settings/WorkspaceDefaultsSection.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { OptionalFields } from '@burdenoff/fe-libs/ui';\nimport { SectionCard, SelectField, ToggleField, NumberField, TextAreaField } from './SettingsForm';\n\ninterface AgentOption {\n id: string;\n name: string;\n isActive: boolean;\n}\n\ninterface WorkspaceDefaultsData {\n defaultAgent: string | null;\n defaultSessionType: string;\n autoStartSessions: boolean;\n defaultVibeType: string;\n autoGitSync: boolean;\n agentHeartbeatInterval: number;\n agentAutoReconnect: boolean;\n agentMaxSessions: number;\n logLevel: string;\n telemetryEnabled: boolean;\n autoUpdate: boolean;\n defaultEnvironmentVariables: string | null;\n}\n\ninterface WorkspaceDefaultsSectionProps {\n data: WorkspaceDefaultsData;\n onChange: (data: WorkspaceDefaultsData) => void;\n agents: AgentOption[];\n}\n\nexport function WorkspaceDefaultsSection({\n data,\n onChange,\n agents,\n}: WorkspaceDefaultsSectionProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n },\n [t]\n );\n const defaultAgentOptions = useMemo(\n () => [\n { value: '', label: tr('settings.defaultAgentPlaceholder', 'Select default agent...') },\n ...agents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${\n agent.isActive\n ? `(${tr('settings.agentStateActive', 'Active')})`\n : `(${tr('settings.agentStateOffline', 'Offline')})`\n }`,\n })),\n ],\n [agents, tr]\n );\n const sessionTypeOptions = useMemo(\n () => [\n { value: 'TMUX', label: tr('settings.sessionTypeTmux', 'Tmux') },\n { value: 'WEZTERM', label: tr('settings.sessionTypeWezTerm', 'WezTerm') },\n { value: 'ZELLIJ', label: tr('settings.sessionTypeZellij', 'Zellij') },\n { value: 'SSH', label: tr('settings.sessionTypeSsh', 'SSH') },\n { value: 'TERMINAL', label: tr('settings.sessionTypeTerminal', 'Terminal') },\n { value: 'SCRIPT', label: tr('settings.sessionTypeScript', 'Script') },\n ],\n [tr]\n );\n const vibeTypeOptions = useMemo(\n () => [\n { value: 'PROJECT', label: tr('settings.vibeTypeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('settings.vibeTypeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('settings.vibeTypeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('settings.vibeTypeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('settings.vibeTypePackage', 'Package') },\n { value: 'CUSTOM', label: tr('settings.vibeTypeCustom', 'Custom') },\n ],\n [tr]\n );\n return (\n <div className=\"space-y-6\">\n <SectionCard\n title={tr('settings.generalDefaults', 'General Defaults')}\n description={tr(\n 'settings.generalDefaultsDescription',\n 'Defaults applied when you create a new session or vibe'\n )}\n >\n <SelectField\n label={tr('settings.defaultAgent', 'Default Agent')}\n value={data.defaultAgent || ''}\n onChange={(v) => onChange({ ...data, defaultAgent: v || null })}\n options={defaultAgentOptions}\n description={tr(\n 'settings.defaultAgentDescription',\n 'Agent used by default when creating new sessions'\n )}\n />\n <SelectField\n label={tr('settings.defaultSessionType', 'Default Session Type')}\n value={data.defaultSessionType}\n onChange={(v) => onChange({ ...data, defaultSessionType: v })}\n options={sessionTypeOptions}\n />\n <ToggleField\n label={tr('settings.autoStartSessions', 'Auto-start Sessions')}\n description={tr(\n 'settings.autoStartSessionsDescription',\n 'Automatically start sessions when a vibe is activated'\n )}\n value={data.autoStartSessions}\n onChange={(v) => onChange({ ...data, autoStartSessions: v })}\n />\n <SelectField\n label={tr('settings.defaultVibeType', 'Default Vibe Type')}\n value={data.defaultVibeType}\n onChange={(v) => onChange({ ...data, defaultVibeType: v })}\n options={vibeTypeOptions}\n />\n <ToggleField\n label={tr('settings.autoGitSync', 'Auto Git Sync')}\n description={tr(\n 'settings.autoGitSyncDescription',\n 'Automatically sync git status when a vibe is activated'\n )}\n value={data.autoGitSync}\n onChange={(v) => onChange({ ...data, autoGitSync: v })}\n />\n </SectionCard>\n\n <OptionalFields\n title={tr('settings.agentTuning', 'Agent tuning')}\n description={tr(\n 'settings.agentTuningDescription',\n 'Heartbeat, capacity, reconnect — most users keep the defaults'\n )}\n >\n <NumberField\n label={tr('settings.heartbeatInterval', 'Heartbeat Interval (seconds)')}\n value={data.agentHeartbeatInterval}\n onChange={(v) => onChange({ ...data, agentHeartbeatInterval: v })}\n min={10}\n max={300}\n description={tr(\n 'settings.heartbeatIntervalDescription',\n 'How often agents send heartbeat signals (10-300 seconds)'\n )}\n />\n <NumberField\n label={tr('settings.maxSessionsPerAgent', 'Max Sessions Per Agent')}\n value={data.agentMaxSessions}\n onChange={(v) => onChange({ ...data, agentMaxSessions: v })}\n min={1}\n max={100}\n />\n <ToggleField\n label={tr('settings.autoReconnect', 'Auto-reconnect')}\n description={tr(\n 'settings.autoReconnectDescription',\n 'Automatically reconnect agents after network interruptions'\n )}\n value={data.agentAutoReconnect}\n onChange={(v) => onChange({ ...data, agentAutoReconnect: v })}\n />\n <TextAreaField\n label={tr('settings.defaultEnvVars', 'Default Environment Variables')}\n value={data.defaultEnvironmentVariables || ''}\n onChange={(v) => onChange({ ...data, defaultEnvironmentVariables: v || null })}\n placeholder={tr(
|
|
1
|
+
{"version":3,"file":"WorkspaceDefaultsSection.js","names":[],"sources":["../../../src/components/settings/WorkspaceDefaultsSection.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { OptionalFields } from '@burdenoff/fe-libs/ui';\nimport { SectionCard, SelectField, ToggleField, NumberField, TextAreaField } from './SettingsForm';\n\ninterface AgentOption {\n id: string;\n name: string;\n isActive: boolean;\n}\n\ninterface WorkspaceDefaultsData {\n defaultAgent: string | null;\n defaultSessionType: string;\n autoStartSessions: boolean;\n defaultVibeType: string;\n autoGitSync: boolean;\n agentHeartbeatInterval: number;\n agentAutoReconnect: boolean;\n agentMaxSessions: number;\n logLevel: string;\n telemetryEnabled: boolean;\n autoUpdate: boolean;\n defaultEnvironmentVariables: string | null;\n}\n\ninterface WorkspaceDefaultsSectionProps {\n data: WorkspaceDefaultsData;\n onChange: (data: WorkspaceDefaultsData) => void;\n agents: AgentOption[];\n}\n\nexport function WorkspaceDefaultsSection({\n data,\n onChange,\n agents,\n}: WorkspaceDefaultsSectionProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n },\n [t]\n );\n const defaultAgentOptions = useMemo(\n () => [\n { value: '', label: tr('settings.defaultAgentPlaceholder', 'Select default agent...') },\n ...agents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${\n agent.isActive\n ? `(${tr('settings.agentStateActive', 'Active')})`\n : `(${tr('settings.agentStateOffline', 'Offline')})`\n }`,\n })),\n ],\n [agents, tr]\n );\n const sessionTypeOptions = useMemo(\n () => [\n { value: 'TMUX', label: tr('settings.sessionTypeTmux', 'Tmux') },\n { value: 'WEZTERM', label: tr('settings.sessionTypeWezTerm', 'WezTerm') },\n { value: 'ZELLIJ', label: tr('settings.sessionTypeZellij', 'Zellij') },\n { value: 'SSH', label: tr('settings.sessionTypeSsh', 'SSH') },\n { value: 'TERMINAL', label: tr('settings.sessionTypeTerminal', 'Terminal') },\n { value: 'SCRIPT', label: tr('settings.sessionTypeScript', 'Script') },\n ],\n [tr]\n );\n const vibeTypeOptions = useMemo(\n () => [\n { value: 'PROJECT', label: tr('settings.vibeTypeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('settings.vibeTypeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('settings.vibeTypeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('settings.vibeTypeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('settings.vibeTypePackage', 'Package') },\n { value: 'CUSTOM', label: tr('settings.vibeTypeCustom', 'Custom') },\n ],\n [tr]\n );\n return (\n <div className=\"space-y-6\">\n <SectionCard\n title={tr('settings.generalDefaults', 'General Defaults')}\n description={tr(\n 'settings.generalDefaultsDescription',\n 'Defaults applied when you create a new session or vibe'\n )}\n >\n <SelectField\n label={tr('settings.defaultAgent', 'Default Agent')}\n value={data.defaultAgent || ''}\n onChange={(v) => onChange({ ...data, defaultAgent: v || null })}\n options={defaultAgentOptions}\n description={tr(\n 'settings.defaultAgentDescription',\n 'Agent used by default when creating new sessions'\n )}\n />\n <SelectField\n label={tr('settings.defaultSessionType', 'Default Session Type')}\n value={data.defaultSessionType}\n onChange={(v) => onChange({ ...data, defaultSessionType: v })}\n options={sessionTypeOptions}\n />\n <ToggleField\n label={tr('settings.autoStartSessions', 'Auto-start Sessions')}\n description={tr(\n 'settings.autoStartSessionsDescription',\n 'Automatically start sessions when a vibe is activated'\n )}\n value={data.autoStartSessions}\n onChange={(v) => onChange({ ...data, autoStartSessions: v })}\n />\n <SelectField\n label={tr('settings.defaultVibeType', 'Default Vibe Type')}\n value={data.defaultVibeType}\n onChange={(v) => onChange({ ...data, defaultVibeType: v })}\n options={vibeTypeOptions}\n />\n <ToggleField\n label={tr('settings.autoGitSync', 'Auto Git Sync')}\n description={tr(\n 'settings.autoGitSyncDescription',\n 'Automatically sync git status when a vibe is activated'\n )}\n value={data.autoGitSync}\n onChange={(v) => onChange({ ...data, autoGitSync: v })}\n />\n </SectionCard>\n\n <OptionalFields\n title={tr('settings.agentTuning', 'Agent tuning')}\n description={tr(\n 'settings.agentTuningDescription',\n 'Heartbeat, capacity, reconnect — most users keep the defaults'\n )}\n >\n <NumberField\n label={tr('settings.heartbeatInterval', 'Heartbeat Interval (seconds)')}\n value={data.agentHeartbeatInterval}\n onChange={(v) => onChange({ ...data, agentHeartbeatInterval: v })}\n min={10}\n max={300}\n description={tr(\n 'settings.heartbeatIntervalDescription',\n 'How often agents send heartbeat signals (10-300 seconds)'\n )}\n />\n <NumberField\n label={tr('settings.maxSessionsPerAgent', 'Max Sessions Per Agent')}\n value={data.agentMaxSessions}\n onChange={(v) => onChange({ ...data, agentMaxSessions: v })}\n min={1}\n max={100}\n />\n <ToggleField\n label={tr('settings.autoReconnect', 'Auto-reconnect')}\n description={tr(\n 'settings.autoReconnectDescription',\n 'Automatically reconnect agents after network interruptions'\n )}\n value={data.agentAutoReconnect}\n onChange={(v) => onChange({ ...data, agentAutoReconnect: v })}\n />\n <TextAreaField\n label={tr('settings.defaultEnvVars', 'Default Environment Variables')}\n value={data.defaultEnvironmentVariables || ''}\n onChange={(v) => onChange({ ...data, defaultEnvironmentVariables: v || null })}\n placeholder={tr('settings.defaultEnvVarsPlaceholder', 'KEY=value\\nANOTHER_KEY=value')}\n description={tr(\n 'settings.defaultEnvVarsDescription',\n 'Environment variables applied to all new vibes (one per line, KEY=value format)'\n )}\n />\n </OptionalFields>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/components/shared/ConfirmDialog.tsx"],"sourcesContent":["import { AlertTriangle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ConfirmDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onConfirm: () => void;\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: 'danger' | 'warning' | 'default';\n isLoading?: boolean;\n}\n\nconst VARIANT_STYLES: Record<\n NonNullable<ConfirmDialogProps['variant']>,\n { iconWrap: string; confirmBtn: string }\n> = {\n danger: {\n iconWrap: 'bg-status-error-bg-subtle text-status-error-text',\n confirmBtn: 'bg-status-error-bg text-text-inverse hover:bg-status-error-text-hover',\n },\n warning: {\n iconWrap: 'bg-status-warning-bg-subtle text-status-warning-text',\n confirmBtn: 'bg-status-warning-bg text-text-primary hover:bg-status-warning-text-hover',\n },\n default: {\n iconWrap: 'bg-action-primary-bg text-action-primary-text',\n confirmBtn: 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover',\n },\n};\n\nexport function ConfirmDialog({\n isOpen,\n onClose,\n onConfirm,\n title,\n message,\n confirmLabel,\n cancelLabel,\n variant = 'default',\n isLoading = false,\n}: ConfirmDialogProps) {\n const tr = useTr();\n const resolvedConfirmLabel =\n confirmLabel ?? tr('vibecontrols.shared.confirmDialog.confirm', 'Confirm');\n const resolvedCancelLabel =\n cancelLabel ?? tr('vibecontrols.shared.confirmDialog.cancel', 'Cancel');\n const styles = VARIANT_STYLES[variant];\n\n return (\n <ResponsiveDialog\n open={isOpen}\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-10 rounded-full flex items-center justify-center ${styles.iconWrap}`}\n aria-hidden=\"true\"\n >\n <AlertTriangle className=\"size-5\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"mt-1\"
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/components/shared/ConfirmDialog.tsx"],"sourcesContent":["import { AlertTriangle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface ConfirmDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onConfirm: () => void;\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: 'danger' | 'warning' | 'default';\n isLoading?: boolean;\n}\n\nconst VARIANT_STYLES: Record<\n NonNullable<ConfirmDialogProps['variant']>,\n { iconWrap: string; confirmBtn: string }\n> = {\n danger: {\n iconWrap: 'bg-status-error-bg-subtle text-status-error-text',\n confirmBtn: 'bg-status-error-bg text-text-inverse hover:bg-status-error-text-hover',\n },\n warning: {\n iconWrap: 'bg-status-warning-bg-subtle text-status-warning-text',\n confirmBtn: 'bg-status-warning-bg text-text-primary hover:bg-status-warning-text-hover',\n },\n default: {\n iconWrap: 'bg-action-primary-bg text-action-primary-text',\n confirmBtn: 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover',\n },\n};\n\nexport function ConfirmDialog({\n isOpen,\n onClose,\n onConfirm,\n title,\n message,\n confirmLabel,\n cancelLabel,\n variant = 'default',\n isLoading = false,\n}: ConfirmDialogProps) {\n const tr = useTr();\n const resolvedConfirmLabel =\n confirmLabel ?? tr('vibecontrols.shared.confirmDialog.confirm', 'Confirm');\n const resolvedCancelLabel =\n cancelLabel ?? tr('vibecontrols.shared.confirmDialog.cancel', 'Cancel');\n const styles = VARIANT_STYLES[variant];\n\n return (\n <ResponsiveDialog\n open={isOpen}\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-10 rounded-full flex items-center justify-center ${styles.iconWrap}`}\n aria-hidden=\"true\"\n >\n <AlertTriangle className=\"size-5\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription className=\"mt-1\">{message}</ResponsiveDialogDescription>\n </div>\n </div>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isLoading}\n className=\"min-h-11 px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40\"\n >\n {resolvedCancelLabel}\n </button>\n <button\n type=\"button\"\n onClick={onConfirm}\n disabled={isLoading}\n className={`min-h-11 px-4 py-2 text-sm font-medium rounded-md transition-colors disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40 ${styles.confirmBtn}`}\n >\n {isLoading\n ? tr('vibecontrols.shared.confirmDialog.processing', 'Processing...')\n : resolvedConfirmLabel}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;AAuBA,IAAM,IAGF;CACF,QAAQ;EACN,UAAU;EACV,YAAY;EACb;CACD,SAAS;EACP,UAAU;EACV,YAAY;EACb;CACD,SAAS;EACP,UAAU;EACV,YAAY;EACb;CACF;AAED,SAAgB,EAAc,EAC5B,WACA,YACA,cACA,UACA,YACA,iBACA,gBACA,aAAU,WACV,eAAY,MACS;CACrB,IAAM,IAAK,GAAO,EACZ,IACJ,KAAgB,EAAG,6CAA6C,UAAU,EACtE,IACJ,KAAe,EAAG,4CAA4C,SAAS,EACnE,IAAS,EAAe;AAE9B,QACE,kBAAC,GAAD;EACE,MAAM;EACN,eAAe,MAAS;AACtB,GAAK,KAAM,GAAS;;YAGtB,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAW,uEAAuE,EAAO;KACzF,eAAY;eAEZ,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAChC,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAwB,GAA8B,CAAA,EACtD,kBAAC,GAAD;MAA6B,WAAU;gBAAQ;MAAsC,CAAA,CACjF;OACF;OACiB,CAAA,EACzB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU;IACV,WAAU;cAET;IACM,CAAA,EACT,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU;IACV,WAAW,2JAA2J,EAAO;cAE5K,IACG,EAAG,gDAAgD,gBAAgB,GACnE;IACG,CAAA,CACc,EAAA,CAAA,CACD;;EACT,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/components/shared/EmptyState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { EmptyState as CanonicalEmptyState } from '@burdenoff/fe-libs/ui';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n}\n\n/**\n * Local wrapper around `EmptyState` from `@burdenoff/fe-libs/ui`.\n *\n * Keeps the existing microfe-vibecontrols call sites stable while routing all\n * empty-state rendering through the canonical primitive.\n */\nexport function EmptyState({ icon, title, description, action }: EmptyStateProps) {\n return (\n <CanonicalEmptyState
|
|
1
|
+
{"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/components/shared/EmptyState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { EmptyState as CanonicalEmptyState } from '@burdenoff/fe-libs/ui';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n}\n\n/**\n * Local wrapper around `EmptyState` from `@burdenoff/fe-libs/ui`.\n *\n * Keeps the existing microfe-vibecontrols call sites stable while routing all\n * empty-state rendering through the canonical primitive.\n */\nexport function EmptyState({ icon, title, description, action }: EmptyStateProps) {\n return (\n <CanonicalEmptyState icon={icon} title={title} description={description} action={action} />\n );\n}\n"],"mappings":";;;AAgBA,SAAgB,EAAW,EAAE,SAAM,UAAO,gBAAa,aAA2B;AAChF,QACE,kBAAC,GAAD;EAA2B;EAAa;EAAoB;EAAqB;EAAU,CAAA"}
|
|
@@ -2,11 +2,11 @@ import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
|
2
2
|
import { StatusBadge as t } from "../shared/StatusBadge.js";
|
|
3
3
|
import "../shared/index.js";
|
|
4
4
|
import { useState as n } from "react";
|
|
5
|
-
import { CheckCircle as r, Download as i, FlaskConical as a,
|
|
6
|
-
import { AnimatePresence as
|
|
7
|
-
import { Fragment as
|
|
5
|
+
import { CheckCircle as r, Download as i, FlaskConical as a, Loader2 as o, MoreVertical as s, Pencil as c, Server as l, Terminal as u, Trash2 as d, X as f } from "lucide-react";
|
|
6
|
+
import { AnimatePresence as p, motion as m } from "framer-motion";
|
|
7
|
+
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
8
8
|
//#region src/components/targets/TargetCard.tsx
|
|
9
|
-
var
|
|
9
|
+
var v = {
|
|
10
10
|
connected: {
|
|
11
11
|
variant: "success",
|
|
12
12
|
label: "Connected"
|
|
@@ -19,15 +19,15 @@ var _ = {
|
|
|
19
19
|
variant: "neutral",
|
|
20
20
|
label: "Unknown"
|
|
21
21
|
}
|
|
22
|
-
},
|
|
22
|
+
}, y = {
|
|
23
23
|
DIRECT: "bg-accent-blue-subtle text-accent-blue",
|
|
24
24
|
SSH: "bg-status-success-bg-subtle text-status-success-text",
|
|
25
25
|
RDP: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
26
26
|
VNC: "bg-accent-purple-subtle text-accent-purple"
|
|
27
|
-
},
|
|
28
|
-
let
|
|
29
|
-
return /* @__PURE__ */
|
|
30
|
-
id: `target-row-${
|
|
27
|
+
}, b = ({ target: b, onTest: x, onTerminal: S, onOpenDetail: C, onInstallAgent: w, onUninstallAgent: T, onDelete: E, onEdit: D, selected: O = !1, onToggleSelect: k, highlighted: A = !1, isStartingTerminal: j = !1 }) => {
|
|
28
|
+
let M = e(), [N, P] = n(!1), F = b.agents.length > 0;
|
|
29
|
+
return /* @__PURE__ */ _(m.div, {
|
|
30
|
+
id: `target-row-${b.id}`,
|
|
31
31
|
whileHover: {
|
|
32
32
|
y: -1,
|
|
33
33
|
transition: {
|
|
@@ -41,78 +41,78 @@ var _ = {
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
whileTap: { scale: .99 },
|
|
44
|
-
className: `bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${
|
|
45
|
-
onClick: () => (
|
|
44
|
+
className: `bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${O ? "border-action-primary-bg ring-1 ring-action-primary-bg/30" : A ? "border-action-primary-bg bg-action-primary-bg/5 shadow-sm" : "border-border-default"}`,
|
|
45
|
+
onClick: () => (C ?? S)?.(b.id),
|
|
46
46
|
children: [
|
|
47
|
-
/* @__PURE__ */
|
|
47
|
+
/* @__PURE__ */ _("div", {
|
|
48
48
|
className: "flex items-start justify-between gap-2",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
49
|
+
children: [/* @__PURE__ */ _("div", {
|
|
50
50
|
className: "flex items-start gap-2 sm:gap-3 min-w-0 flex-1",
|
|
51
51
|
children: [
|
|
52
|
-
|
|
52
|
+
k && /* @__PURE__ */ g("button", {
|
|
53
53
|
type: "button",
|
|
54
54
|
onClick: (e) => {
|
|
55
|
-
e.stopPropagation(),
|
|
55
|
+
e.stopPropagation(), k();
|
|
56
56
|
},
|
|
57
|
-
className: `flex-shrink-0 size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${
|
|
58
|
-
children:
|
|
57
|
+
className: `flex-shrink-0 size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${O ? "bg-action-primary-bg border-action-primary-bg text-action-primary-text" : "border-border-default hover:border-border-strong bg-bg-surface"} flex items-center justify-center`,
|
|
58
|
+
children: O && /* @__PURE__ */ g("svg", {
|
|
59
59
|
className: "size-3",
|
|
60
60
|
fill: "none",
|
|
61
61
|
viewBox: "0 0 24 24",
|
|
62
62
|
stroke: "currentColor",
|
|
63
63
|
strokeWidth: 3,
|
|
64
|
-
children: /* @__PURE__ */
|
|
64
|
+
children: /* @__PURE__ */ g("path", {
|
|
65
65
|
strokeLinecap: "round",
|
|
66
66
|
strokeLinejoin: "round",
|
|
67
67
|
d: "M5 13l4 4L19 7"
|
|
68
68
|
})
|
|
69
69
|
})
|
|
70
70
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
71
|
+
/* @__PURE__ */ g("div", {
|
|
72
72
|
className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center",
|
|
73
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ g(l, { className: "size-4 sm:w-5 sm:h-5 text-text-secondary" })
|
|
74
74
|
}),
|
|
75
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ _("div", {
|
|
76
76
|
className: "min-w-0 flex-1",
|
|
77
|
-
children: [/* @__PURE__ */
|
|
77
|
+
children: [/* @__PURE__ */ _("div", {
|
|
78
78
|
className: "flex items-center gap-1.5 sm:gap-2 flex-wrap",
|
|
79
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ g("h3", {
|
|
80
80
|
className: "font-semibold text-text-primary truncate max-w-[120px] sm:max-w-none text-sm sm:text-base",
|
|
81
|
-
children:
|
|
82
|
-
}), /* @__PURE__ */
|
|
83
|
-
className: `inline-flex items-center px-1.5 py-0.5 text-xs font-semibold rounded ${
|
|
84
|
-
children:
|
|
81
|
+
children: b.name
|
|
82
|
+
}), /* @__PURE__ */ g("span", {
|
|
83
|
+
className: `inline-flex items-center px-1.5 py-0.5 text-xs font-semibold rounded ${y[b.type]}`,
|
|
84
|
+
children: b.type
|
|
85
85
|
})]
|
|
86
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ g("p", {
|
|
87
87
|
className: "text-xs sm:text-sm text-text-secondary truncate",
|
|
88
|
-
children:
|
|
88
|
+
children: b.host ? `${b.username ? `${b.username}@` : ""}${b.host}${b.port ? `:${b.port}` : ""}` : b.label || `ID: ${b.id.slice(0, 8)}`
|
|
89
89
|
})]
|
|
90
90
|
})
|
|
91
91
|
]
|
|
92
|
-
}), /* @__PURE__ */
|
|
92
|
+
}), /* @__PURE__ */ _("div", {
|
|
93
93
|
className: "flex items-center gap-1 sm:gap-2 flex-shrink-0",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
95
|
-
status:
|
|
96
|
-
variant:
|
|
97
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ g(t, {
|
|
95
|
+
status: v[b.status].label,
|
|
96
|
+
variant: v[b.status].variant
|
|
97
|
+
}), /* @__PURE__ */ _("div", {
|
|
98
98
|
className: "relative",
|
|
99
|
-
children: [/* @__PURE__ */
|
|
99
|
+
children: [/* @__PURE__ */ g("button", {
|
|
100
100
|
type: "button",
|
|
101
101
|
onClick: (e) => {
|
|
102
|
-
e.stopPropagation(),
|
|
102
|
+
e.stopPropagation(), P(!N);
|
|
103
103
|
},
|
|
104
104
|
className: "p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center",
|
|
105
105
|
"aria-label": "Target actions menu",
|
|
106
|
-
children: /* @__PURE__ */
|
|
107
|
-
}), /* @__PURE__ */
|
|
106
|
+
children: /* @__PURE__ */ g(s, { className: "size-4 text-text-primary opacity-70 hover:opacity-100" })
|
|
107
|
+
}), /* @__PURE__ */ g(p, { children: N && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
|
|
108
108
|
role: "button",
|
|
109
109
|
tabIndex: 0,
|
|
110
110
|
onKeyDown: (e) => {
|
|
111
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
111
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), P(!1));
|
|
112
112
|
},
|
|
113
113
|
className: "fixed inset-0 z-10",
|
|
114
|
-
onClick: () =>
|
|
115
|
-
}), /* @__PURE__ */
|
|
114
|
+
onClick: () => P(!1)
|
|
115
|
+
}), /* @__PURE__ */ _(m.div, {
|
|
116
116
|
initial: {
|
|
117
117
|
opacity: 0,
|
|
118
118
|
scale: .95,
|
|
@@ -131,97 +131,105 @@ var _ = {
|
|
|
131
131
|
transition: { duration: .15 },
|
|
132
132
|
className: "absolute right-0 mt-1 w-44 bg-bg-surface border border-border-default rounded-lg shadow-lg z-20",
|
|
133
133
|
children: [
|
|
134
|
-
|
|
134
|
+
x && /* @__PURE__ */ _("button", {
|
|
135
135
|
type: "button",
|
|
136
136
|
onClick: (e) => {
|
|
137
|
-
e.stopPropagation(), b
|
|
137
|
+
e.stopPropagation(), x(b.id), P(!1);
|
|
138
138
|
},
|
|
139
139
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg transition-colors",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
140
|
+
children: [/* @__PURE__ */ g(a, { className: "size-4" }), M("vibecontrols.targets.testConnection", "Test Connection")]
|
|
141
141
|
}),
|
|
142
|
-
|
|
142
|
+
S && /* @__PURE__ */ g("button", {
|
|
143
143
|
type: "button",
|
|
144
|
+
disabled: j,
|
|
144
145
|
onClick: (e) => {
|
|
145
|
-
e.stopPropagation(),
|
|
146
|
+
e.stopPropagation(), !j && (S(b.id), P(!1));
|
|
146
147
|
},
|
|
147
|
-
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors",
|
|
148
|
-
children: [/* @__PURE__ */ h(
|
|
148
|
+
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors disabled:opacity-60 disabled:cursor-not-allowed",
|
|
149
|
+
children: j ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(o, { className: "size-4 animate-spin" }), M("vibecontrols.targets.startingTerminal", "Starting…")] }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(u, { className: "size-4" }), M("vibecontrols.targets.openTerminal", "Open Terminal")] })
|
|
149
150
|
}),
|
|
150
|
-
|
|
151
|
+
D && /* @__PURE__ */ _("button", {
|
|
151
152
|
type: "button",
|
|
152
153
|
onClick: (e) => {
|
|
153
|
-
e.stopPropagation(),
|
|
154
|
+
e.stopPropagation(), D(b), P(!1);
|
|
154
155
|
},
|
|
155
156
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors",
|
|
156
|
-
children: [/* @__PURE__ */
|
|
157
|
+
children: [/* @__PURE__ */ g(c, { className: "size-4" }), M("vibecontrols.targets.editTarget", "Edit Target")]
|
|
157
158
|
}),
|
|
158
|
-
|
|
159
|
+
b.type !== "DIRECT" && !F && w && /* @__PURE__ */ _("button", {
|
|
159
160
|
type: "button",
|
|
160
161
|
onClick: (e) => {
|
|
161
|
-
e.stopPropagation(),
|
|
162
|
+
e.stopPropagation(), w(b), P(!1);
|
|
162
163
|
},
|
|
163
164
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-action-primary-bg hover:bg-bg-sunken transition-colors",
|
|
164
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ g(i, { className: "size-4" }), M("vibecontrols.targets.installAgent", "Install Agent")]
|
|
165
166
|
}),
|
|
166
|
-
|
|
167
|
+
b.type !== "DIRECT" && F && T && /* @__PURE__ */ _("button", {
|
|
167
168
|
type: "button",
|
|
168
169
|
onClick: (e) => {
|
|
169
|
-
e.stopPropagation(),
|
|
170
|
+
e.stopPropagation(), T(b), P(!1);
|
|
170
171
|
},
|
|
171
172
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken transition-colors",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ g(d, { className: "size-4" }), M("vibecontrols.targets.uninstallAgent", "Uninstall Agent")]
|
|
173
174
|
}),
|
|
174
|
-
|
|
175
|
+
E && /* @__PURE__ */ _("button", {
|
|
175
176
|
type: "button",
|
|
176
177
|
onClick: (e) => {
|
|
177
|
-
e.stopPropagation(),
|
|
178
|
+
e.stopPropagation(), E(b.id), P(!1);
|
|
178
179
|
},
|
|
179
180
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg transition-colors",
|
|
180
|
-
children: [/* @__PURE__ */
|
|
181
|
+
children: [/* @__PURE__ */ g(f, { className: "size-4" }), M("vibecontrols.targets.deleteTarget", "Delete Target")]
|
|
181
182
|
})
|
|
182
183
|
]
|
|
183
184
|
})] }) })]
|
|
184
185
|
})]
|
|
185
186
|
})]
|
|
186
187
|
}),
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
+
j && /* @__PURE__ */ _("div", {
|
|
189
|
+
className: "mt-3 inline-flex items-center gap-2 rounded-md border border-action-primary-border bg-action-primary-bg/10 px-2.5 py-1 text-xs text-action-primary-text",
|
|
190
|
+
children: [/* @__PURE__ */ g(o, {
|
|
191
|
+
size: 12,
|
|
192
|
+
className: "animate-spin"
|
|
193
|
+
}), M("vibecontrols.targets.startingTerminalHint", "Starting terminal session…")]
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ _("div", {
|
|
188
196
|
className: "mt-3 flex items-center gap-3 text-sm",
|
|
189
197
|
children: [
|
|
190
|
-
|
|
198
|
+
F ? /* @__PURE__ */ _("span", {
|
|
191
199
|
className: "inline-flex items-center gap-1 rounded-full border border-status-success-border px-2 py-0.5 text-xs bg-status-success-bg-subtle text-status-success-text",
|
|
192
200
|
children: [
|
|
193
|
-
/* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ g(r, { size: 10 }),
|
|
194
202
|
"Agent: ",
|
|
195
|
-
|
|
203
|
+
b.agents[0].name
|
|
196
204
|
]
|
|
197
|
-
}) : /* @__PURE__ */
|
|
205
|
+
}) : /* @__PURE__ */ g("span", {
|
|
198
206
|
className: "inline-flex items-center gap-1 rounded-full border border-border-default px-2 py-0.5 text-xs bg-bg-sunken text-text-tertiary",
|
|
199
207
|
children: "No agent"
|
|
200
208
|
}),
|
|
201
|
-
|
|
209
|
+
b.label && !b.host && /* @__PURE__ */ g("span", {
|
|
202
210
|
className: "text-xs text-text-tertiary",
|
|
203
|
-
children:
|
|
211
|
+
children: b.label
|
|
204
212
|
}),
|
|
205
|
-
|
|
213
|
+
b.host && b.label && /* @__PURE__ */ g("span", {
|
|
206
214
|
className: "text-xs text-text-tertiary",
|
|
207
|
-
children:
|
|
215
|
+
children: b.label
|
|
208
216
|
})
|
|
209
217
|
]
|
|
210
218
|
}),
|
|
211
|
-
|
|
219
|
+
b.tags.length > 0 && /* @__PURE__ */ _("div", {
|
|
212
220
|
className: "mt-2 flex flex-wrap gap-1",
|
|
213
|
-
children: [
|
|
221
|
+
children: [b.tags.slice(0, 4).map((e) => /* @__PURE__ */ g("span", {
|
|
214
222
|
className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded",
|
|
215
223
|
children: e
|
|
216
|
-
}, e)),
|
|
224
|
+
}, e)), b.tags.length > 4 && /* @__PURE__ */ _("span", {
|
|
217
225
|
className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded",
|
|
218
|
-
children: ["+",
|
|
226
|
+
children: ["+", b.tags.length - 4]
|
|
219
227
|
})]
|
|
220
228
|
})
|
|
221
229
|
]
|
|
222
230
|
});
|
|
223
231
|
};
|
|
224
232
|
//#endregion
|
|
225
|
-
export {
|
|
233
|
+
export { b as TargetCard };
|
|
226
234
|
|
|
227
235
|
//# sourceMappingURL=TargetCard.js.map
|