@burdenoff/microfe-vibecontrols 2026.525.1 → 2026.525.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.
@@ -1,14 +1,14 @@
1
1
  import { Card as e } from "../shared/Card.js";
2
- import { VIBECONTROLS_AGENT_PROFILES_QUERY as t, useActiveAgentProfile as n } from "../../hooks/useActiveAgentProfile.js";
2
+ import { useActiveAgentProfile as t } from "../../hooks/useActiveAgentProfile.js";
3
3
  import "../shared/index.js";
4
- import { useCallback as r, useState as i } from "react";
5
- import { CheckCircle2 as a, Loader2 as o, Plus as s, Star as c, Trash2 as l, X as u } from "lucide-react";
6
- import { gql as d } from "@apollo/client";
7
- import { useMutation as f } from "@apollo/client/react";
8
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
4
+ import { useCallback as n, useState as r } from "react";
5
+ import { CheckCircle2 as i, Loader2 as a, Plus as o, Star as s, Trash2 as c, X as l } from "lucide-react";
6
+ import { gql as u } from "@apollo/client";
7
+ import { useMutation as d } from "@apollo/client/react";
8
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
9
9
  //#region src/components/agent-manager/ProfilesPanel.tsx
10
- var h = d`
11
- mutation CreateVibecontrolsAgentProfile($input: CreateAgentProfileInput!) {
10
+ var m = u`
11
+ mutation CreateVibecontrolsAgentProfile($input: CreateVibecontrolsAgentProfileInput!) {
12
12
  createVibecontrolsAgentProfile(input: $input) {
13
13
  id
14
14
  name
@@ -16,11 +16,11 @@ var h = d`
16
16
  status
17
17
  }
18
18
  }
19
- `, g = d`
19
+ `, h = u`
20
20
  mutation DeleteVibecontrolsAgentProfile($id: ID!) {
21
21
  deleteVibecontrolsAgentProfile(id: $id)
22
22
  }
23
- `, _ = d`
23
+ `, g = u`
24
24
  mutation SetDefaultVibecontrolsAgentProfile($id: ID!) {
25
25
  setDefaultVibecontrolsAgentProfile(id: $id) {
26
26
  id
@@ -28,156 +28,147 @@ var h = d`
28
28
  }
29
29
  }
30
30
  `;
31
- function v(e) {
31
+ function _(e) {
32
32
  let t = (e ?? "").toLowerCase();
33
33
  return t === "running" || t === "active" || t === "healthy" ? "bg-status-success-text" : t === "stopped" || t === "inactive" ? "bg-text-placeholder" : t === "error" || t === "failed" ? "bg-status-error-text" : "bg-status-warning-text";
34
34
  }
35
- function y({ agentId: d }) {
36
- let { profiles: y, active: b, setActive: x, loading: S, refetch: C } = n(d), [w, T] = i(!1), [E, D] = i("default"), [O, k] = i(""), [A, j] = i(null), M = { agentId: d }, [N, { loading: P }] = f(h, {
37
- refetchQueries: [{
38
- query: t,
39
- variables: M
40
- }],
41
- awaitRefetchQueries: !0
42
- }), [F, { loading: I }] = f(g, {
43
- refetchQueries: [{
44
- query: t,
45
- variables: M
46
- }],
47
- awaitRefetchQueries: !0
48
- }), [L, { loading: R }] = f(_, {
49
- refetchQueries: [{
50
- query: t,
51
- variables: M
52
- }],
53
- awaitRefetchQueries: !0
54
- }), z = r(async () => {
55
- j(null);
56
- let e = E.trim();
35
+ function v({ agentId: u }) {
36
+ let { profiles: v, active: y, setActive: b, loading: x, refetch: S } = t(u), [C, w] = r(!1), [T, E] = r("default"), [D, O] = r(""), [k, A] = r(null), [j, { loading: M }] = d(m), [N, { loading: P }] = d(h), [F, { loading: I }] = d(g), L = n(async () => {
37
+ A(null);
38
+ let e = T.trim();
57
39
  if (!e) {
58
- j("Profile name is required");
40
+ A("Profile name is required");
59
41
  return;
60
42
  }
61
- let t = O.trim() ? Number(O.trim()) : null;
62
- if (O.trim() && (Number.isNaN(t) || t === null || t <= 0)) {
63
- j("Port must be a positive number");
43
+ let t = D.trim() ? Number(D.trim()) : null;
44
+ if (D.trim() && (Number.isNaN(t) || t === null || t <= 0)) {
45
+ A("Port must be a positive number");
64
46
  return;
65
47
  }
66
48
  try {
67
- await N({ variables: { input: {
68
- agentId: d,
49
+ await j({ variables: { input: {
50
+ agentId: u,
69
51
  name: e,
70
52
  port: t
71
- } } }), T(!1), D("default"), k("");
53
+ } } });
72
54
  } catch (e) {
73
- j(e instanceof Error ? e.message : "Failed to create profile");
55
+ A(e instanceof Error ? e.message : "Failed to create profile");
56
+ return;
74
57
  }
58
+ w(!1), E("default"), O(""), await S();
75
59
  }, [
76
- d,
77
- N,
78
- E,
79
- O
80
- ]), B = r(async (e) => {
81
- if (j(null), window.confirm(`Delete profile "${e.name}"? This cannot be undone.`)) try {
82
- await F({ variables: { id: e.id } });
83
- } catch (e) {
84
- j(e instanceof Error ? e.message : "Failed to delete profile");
60
+ u,
61
+ j,
62
+ T,
63
+ D,
64
+ S
65
+ ]), R = n(async (e) => {
66
+ if (A(null), window.confirm(`Delete profile "${e.name}"? This cannot be undone.`)) {
67
+ try {
68
+ await N({ variables: { id: e.id } });
69
+ } catch (e) {
70
+ A(e instanceof Error ? e.message : "Failed to delete profile");
71
+ return;
72
+ }
73
+ await S();
85
74
  }
86
- }, [F]), V = r(async (e) => {
87
- j(null);
75
+ }, [N, S]), z = n(async (e) => {
76
+ A(null);
88
77
  try {
89
- await L({ variables: { id: e.id } });
78
+ await F({ variables: { id: e.id } });
90
79
  } catch (e) {
91
- j(e instanceof Error ? e.message : "Failed to set default profile");
80
+ A(e instanceof Error ? e.message : "Failed to set default profile");
81
+ return;
92
82
  }
93
- }, [L]), H = P || I || R;
94
- return /* @__PURE__ */ m(e, { children: [
95
- /* @__PURE__ */ m("div", {
83
+ await S();
84
+ }, [F, S]), B = M || P || I;
85
+ return /* @__PURE__ */ p(e, { children: [
86
+ /* @__PURE__ */ p("div", {
96
87
  className: "p-4 border-b border-border-default flex items-center justify-between",
97
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h3", {
88
+ children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h3", {
98
89
  className: "font-semibold text-text-primary",
99
90
  children: "Profiles"
100
- }), /* @__PURE__ */ m("p", {
91
+ }), /* @__PURE__ */ p("p", {
101
92
  className: "text-xs text-text-secondary mt-0.5",
102
93
  children: [
103
94
  "Each profile runs an isolated CLI on this agent. Active:",
104
95
  " ",
105
- /* @__PURE__ */ p("code", {
96
+ /* @__PURE__ */ f("code", {
106
97
  className: "font-mono text-text-primary",
107
- children: b
98
+ children: y
108
99
  })
109
100
  ]
110
- })] }), /* @__PURE__ */ m("div", {
101
+ })] }), /* @__PURE__ */ p("div", {
111
102
  className: "flex items-center gap-2",
112
- children: [/* @__PURE__ */ p("button", {
103
+ children: [/* @__PURE__ */ f("button", {
113
104
  type: "button",
114
- onClick: () => C(),
105
+ onClick: () => void S(),
115
106
  className: "px-2 py-1 text-xs text-text-secondary hover:text-text-primary",
116
107
  children: "Refresh"
117
- }), /* @__PURE__ */ m("button", {
108
+ }), /* @__PURE__ */ p("button", {
118
109
  type: "button",
119
- onClick: () => T((e) => !e),
110
+ onClick: () => w((e) => !e),
120
111
  className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90",
121
- children: [/* @__PURE__ */ p(s, { className: "w-4 h-4" }), "New profile"]
112
+ children: [/* @__PURE__ */ f(o, { className: "w-4 h-4" }), "New profile"]
122
113
  })]
123
114
  })]
124
115
  }),
125
- A && /* @__PURE__ */ p("div", {
116
+ k && /* @__PURE__ */ f("div", {
126
117
  className: "px-4 py-2 bg-status-error-bg/30 border-b border-status-error-text/20 text-sm text-status-error-text",
127
- children: A
118
+ children: k
128
119
  }),
129
- w && /* @__PURE__ */ m("div", {
120
+ C && /* @__PURE__ */ p("div", {
130
121
  className: "p-4 border-b border-border-default bg-bg-sunken/40 space-y-3",
131
- children: [/* @__PURE__ */ m("div", {
122
+ children: [/* @__PURE__ */ p("div", {
132
123
  className: "grid grid-cols-2 gap-3",
133
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
124
+ children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
134
125
  className: "block text-xs font-medium text-text-secondary mb-1",
135
126
  children: "Profile name"
136
- }), /* @__PURE__ */ p("input", {
127
+ }), /* @__PURE__ */ f("input", {
137
128
  type: "text",
138
- value: E,
139
- onChange: (e) => D(e.target.value),
129
+ value: T,
130
+ onChange: (e) => E(e.target.value),
140
131
  placeholder: "default",
141
132
  className: "w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono",
142
133
  autoFocus: !0
143
- })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
134
+ })] }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
144
135
  className: "block text-xs font-medium text-text-secondary mb-1",
145
136
  children: "Port (optional)"
146
- }), /* @__PURE__ */ p("input", {
137
+ }), /* @__PURE__ */ f("input", {
147
138
  type: "number",
148
- value: O,
149
- onChange: (e) => k(e.target.value),
139
+ value: D,
140
+ onChange: (e) => O(e.target.value),
150
141
  placeholder: "auto",
151
142
  className: "w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono"
152
143
  })] })]
153
- }), /* @__PURE__ */ m("div", {
144
+ }), /* @__PURE__ */ p("div", {
154
145
  className: "flex items-center gap-2",
155
- children: [/* @__PURE__ */ m("button", {
146
+ children: [/* @__PURE__ */ p("button", {
156
147
  type: "button",
157
- disabled: P,
158
- onClick: () => void z(),
148
+ disabled: M,
149
+ onClick: () => void L(),
159
150
  className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50",
160
- children: [P ? /* @__PURE__ */ p(o, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ p(a, { className: "w-3.5 h-3.5" }), "Create"]
161
- }), /* @__PURE__ */ m("button", {
151
+ children: [M ? /* @__PURE__ */ f(a, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ f(i, { className: "w-3.5 h-3.5" }), "Create"]
152
+ }), /* @__PURE__ */ p("button", {
162
153
  type: "button",
163
154
  onClick: () => {
164
- T(!1), j(null);
155
+ w(!1), A(null);
165
156
  },
166
157
  className: "inline-flex items-center gap-1 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary",
167
- children: [/* @__PURE__ */ p(u, { className: "w-3.5 h-3.5" }), "Cancel"]
158
+ children: [/* @__PURE__ */ f(l, { className: "w-3.5 h-3.5" }), "Cancel"]
168
159
  })]
169
160
  })]
170
161
  }),
171
- S && y.length === 0 ? /* @__PURE__ */ m("div", {
162
+ x && v.length === 0 ? /* @__PURE__ */ p("div", {
172
163
  className: "p-6 text-center text-text-secondary",
173
- children: [/* @__PURE__ */ p(o, { className: "w-5 h-5 mx-auto mb-2 animate-spin" }), "Loading profiles…"]
174
- }) : y.length === 0 ? /* @__PURE__ */ m("div", {
164
+ children: [/* @__PURE__ */ f(a, { className: "w-5 h-5 mx-auto mb-2 animate-spin" }), "Loading profiles…"]
165
+ }) : v.length === 0 ? /* @__PURE__ */ p("div", {
175
166
  className: "p-6 text-center text-text-secondary",
176
- children: [/* @__PURE__ */ p("p", { children: "No profiles configured yet." }), /* @__PURE__ */ m("p", {
167
+ children: [/* @__PURE__ */ f("p", { children: "No profiles configured yet." }), /* @__PURE__ */ p("p", {
177
168
  className: "text-xs mt-1",
178
169
  children: [
179
170
  "The agent will fall back to the implicit ",
180
- /* @__PURE__ */ p("code", {
171
+ /* @__PURE__ */ f("code", {
181
172
  className: "font-mono",
182
173
  children: "default"
183
174
  }),
@@ -185,36 +176,36 @@ function y({ agentId: d }) {
185
176
  "profile until you create one."
186
177
  ]
187
178
  })]
188
- }) : /* @__PURE__ */ p("div", {
179
+ }) : /* @__PURE__ */ f("div", {
189
180
  className: "divide-y divide-border-default",
190
- children: y.map((e) => {
191
- let t = e.name === b;
192
- return /* @__PURE__ */ m("div", {
181
+ children: v.map((e) => {
182
+ let t = e.name === y;
183
+ return /* @__PURE__ */ p("div", {
193
184
  className: `p-4 flex items-center justify-between gap-4 ${t ? "bg-action-primary-bg/5" : ""}`,
194
- children: [/* @__PURE__ */ m("div", {
185
+ children: [/* @__PURE__ */ p("div", {
195
186
  className: "flex items-center gap-3 min-w-0",
196
- children: [/* @__PURE__ */ p("span", {
197
- className: `h-2 w-2 rounded-full flex-shrink-0 ${v(e.status)}`,
187
+ children: [/* @__PURE__ */ f("span", {
188
+ className: `h-2 w-2 rounded-full flex-shrink-0 ${_(e.status)}`,
198
189
  "aria-hidden": !0
199
- }), /* @__PURE__ */ m("div", {
190
+ }), /* @__PURE__ */ p("div", {
200
191
  className: "min-w-0",
201
- children: [/* @__PURE__ */ m("div", {
192
+ children: [/* @__PURE__ */ p("div", {
202
193
  className: "flex items-center gap-2",
203
194
  children: [
204
- /* @__PURE__ */ p("code", {
195
+ /* @__PURE__ */ f("code", {
205
196
  className: "font-mono font-medium text-text-primary truncate",
206
197
  children: e.name
207
198
  }),
208
- e.isDefault && /* @__PURE__ */ m("span", {
199
+ e.isDefault && /* @__PURE__ */ p("span", {
209
200
  className: "inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-action-primary-bg/10 text-action-primary-bg rounded",
210
- children: [/* @__PURE__ */ p(c, { className: "w-3 h-3" }), "Default"]
201
+ children: [/* @__PURE__ */ f(s, { className: "w-3 h-3" }), "Default"]
211
202
  }),
212
- t && /* @__PURE__ */ p("span", {
203
+ t && /* @__PURE__ */ f("span", {
213
204
  className: "px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg/40 text-status-success-text rounded",
214
205
  children: "Active"
215
206
  })
216
207
  ]
217
- }), /* @__PURE__ */ m("p", {
208
+ }), /* @__PURE__ */ p("p", {
218
209
  className: "text-xs text-text-secondary mt-0.5 truncate",
219
210
  children: [
220
211
  e.status ?? "unknown",
@@ -223,29 +214,29 @@ function y({ agentId: d }) {
223
214
  ]
224
215
  })]
225
216
  })]
226
- }), /* @__PURE__ */ m("div", {
217
+ }), /* @__PURE__ */ p("div", {
227
218
  className: "flex items-center gap-2 flex-shrink-0",
228
219
  children: [
229
- !t && /* @__PURE__ */ p("button", {
220
+ !t && /* @__PURE__ */ f("button", {
230
221
  type: "button",
231
- onClick: () => x(e.name),
222
+ onClick: () => b(e.name),
232
223
  className: "px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded",
233
224
  children: "Use"
234
225
  }),
235
- !e.isDefault && /* @__PURE__ */ m("button", {
226
+ !e.isDefault && /* @__PURE__ */ p("button", {
236
227
  type: "button",
237
- disabled: H,
238
- onClick: () => void V(e),
228
+ disabled: B,
229
+ onClick: () => void z(e),
239
230
  className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50",
240
- children: [/* @__PURE__ */ p(c, { className: "w-3 h-3" }), "Set default"]
231
+ children: [/* @__PURE__ */ f(s, { className: "w-3 h-3" }), "Set default"]
241
232
  }),
242
- /* @__PURE__ */ m("button", {
233
+ /* @__PURE__ */ p("button", {
243
234
  type: "button",
244
- disabled: H || e.isDefault,
235
+ disabled: B || e.isDefault,
245
236
  title: e.isDefault ? "Cannot delete the default profile" : "Delete",
246
- onClick: () => void B(e),
237
+ onClick: () => void R(e),
247
238
  className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg/30 border border-status-error-text/20 rounded disabled:opacity-50",
248
- children: [/* @__PURE__ */ p(l, { className: "w-3 h-3" }), "Delete"]
239
+ children: [/* @__PURE__ */ f(c, { className: "w-3 h-3" }), "Delete"]
249
240
  })
250
241
  ]
251
242
  })]
@@ -255,6 +246,6 @@ function y({ agentId: d }) {
255
246
  ] });
256
247
  }
257
248
  //#endregion
258
- export { y as ProfilesPanel };
249
+ export { v as ProfilesPanel };
259
250
 
260
251
  //# sourceMappingURL=ProfilesPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProfilesPanel.js","names":[],"sources":["../../../src/components/agent-manager/ProfilesPanel.tsx"],"sourcesContent":["/**\n * ProfilesPanel — Phase 5B (multi-profile-per-daemon).\n *\n * Mounted on AgentDetailsPage as the \"Profiles\" tab. Lists every\n * `AgentProfile` row for the current agent and exposes full CRUDL:\n * - List with status indicators\n * - Create (autofilled name \"default\" but editable)\n * - Delete\n * - Set-default\n *\n * Apollo `gql` documents are inlined so the component compiles cleanly\n * before Phase 5A (svc) publishes the AgentProfile schema to Hive.\n */\n\nimport { useCallback, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { CheckCircle2, Loader2, Plus, Star, Trash2, X } from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport {\n useActiveAgentProfile,\n VIBECONTROLS_AGENT_PROFILES_QUERY,\n type AgentProfile,\n} from '@/hooks/useActiveAgentProfile';\n\ninterface ProfilesPanelProps {\n agentId: string;\n}\n\ninterface CreateAgentProfileInput {\n agentId: string;\n name: string;\n port?: number | null;\n}\n\ninterface CreateProfileData {\n createVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'name' | 'isDefault' | 'status'>;\n}\n\ninterface CreateProfileVars {\n input: CreateAgentProfileInput;\n}\n\ninterface DeleteProfileData {\n deleteVibecontrolsAgentProfile: boolean;\n}\n\ninterface DeleteProfileVars {\n id: string;\n}\n\ninterface SetDefaultProfileData {\n setDefaultVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'isDefault'>;\n}\n\ninterface SetDefaultProfileVars {\n id: string;\n}\n\nconst CREATE_PROFILE_MUTATION = gql`\n mutation CreateVibecontrolsAgentProfile($input: CreateAgentProfileInput!) {\n createVibecontrolsAgentProfile(input: $input) {\n id\n name\n isDefault\n status\n }\n }\n`;\n\nconst DELETE_PROFILE_MUTATION = gql`\n mutation DeleteVibecontrolsAgentProfile($id: ID!) {\n deleteVibecontrolsAgentProfile(id: $id)\n }\n`;\n\nconst SET_DEFAULT_PROFILE_MUTATION = gql`\n mutation SetDefaultVibecontrolsAgentProfile($id: ID!) {\n setDefaultVibecontrolsAgentProfile(id: $id) {\n id\n isDefault\n }\n }\n`;\n\nfunction statusDot(status: string | null): string {\n const s = (status ?? '').toLowerCase();\n if (s === 'running' || s === 'active' || s === 'healthy') return 'bg-status-success-text';\n if (s === 'stopped' || s === 'inactive') return 'bg-text-placeholder';\n if (s === 'error' || s === 'failed') return 'bg-status-error-text';\n return 'bg-status-warning-text';\n}\n\nexport function ProfilesPanel({ agentId }: ProfilesPanelProps) {\n const { profiles, active, setActive, loading, refetch } = useActiveAgentProfile(agentId);\n\n const [showCreate, setShowCreate] = useState(false);\n const [newName, setNewName] = useState('default');\n const [newPort, setNewPort] = useState<string>('');\n const [actionError, setActionError] = useState<string | null>(null);\n\n const refetchVars = { agentId };\n\n const [createProfile, { loading: creating }] = useMutation<CreateProfileData, CreateProfileVars>(\n CREATE_PROFILE_MUTATION,\n {\n refetchQueries: [{ query: VIBECONTROLS_AGENT_PROFILES_QUERY, variables: refetchVars }],\n awaitRefetchQueries: true,\n }\n );\n\n const [deleteProfile, { loading: deleting }] = useMutation<DeleteProfileData, DeleteProfileVars>(\n DELETE_PROFILE_MUTATION,\n {\n refetchQueries: [{ query: VIBECONTROLS_AGENT_PROFILES_QUERY, variables: refetchVars }],\n awaitRefetchQueries: true,\n }\n );\n\n const [setDefaultProfile, { loading: settingDefault }] = useMutation<\n SetDefaultProfileData,\n SetDefaultProfileVars\n >(SET_DEFAULT_PROFILE_MUTATION, {\n refetchQueries: [{ query: VIBECONTROLS_AGENT_PROFILES_QUERY, variables: refetchVars }],\n awaitRefetchQueries: true,\n });\n\n const handleCreate = useCallback(async () => {\n setActionError(null);\n const trimmedName = newName.trim();\n if (!trimmedName) {\n setActionError('Profile name is required');\n return;\n }\n const portNum = newPort.trim() ? Number(newPort.trim()) : null;\n if (newPort.trim() && (Number.isNaN(portNum) || portNum === null || portNum <= 0)) {\n setActionError('Port must be a positive number');\n return;\n }\n try {\n await createProfile({\n variables: {\n input: { agentId, name: trimmedName, port: portNum },\n },\n });\n setShowCreate(false);\n setNewName('default');\n setNewPort('');\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to create profile');\n }\n }, [agentId, createProfile, newName, newPort]);\n\n const handleDelete = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n if (!window.confirm(`Delete profile \"${profile.name}\"? This cannot be undone.`)) {\n return;\n }\n try {\n await deleteProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to delete profile');\n }\n },\n [deleteProfile]\n );\n\n const handleSetDefault = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n try {\n await setDefaultProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to set default profile');\n }\n },\n [setDefaultProfile]\n );\n\n const busy = creating || deleting || settingDefault;\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">Profiles</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Each profile runs an isolated CLI on this agent. Active:{' '}\n <code className=\"font-mono text-text-primary\">{active}</code>\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"w-4 h-4\" />\n New profile\n </button>\n </div>\n </div>\n\n {actionError && (\n <div className=\"px-4 py-2 bg-status-error-bg/30 border-b border-status-error-text/20 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {showCreate && (\n <div className=\"p-4 border-b border-border-default bg-bg-sunken/40 space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n Profile name\n </label>\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n autoFocus\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n Port (optional)\n </label>\n <input\n type=\"number\"\n value={newPort}\n onChange={(e) => setNewPort(e.target.value)}\n placeholder=\"auto\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={creating}\n onClick={() => void handleCreate()}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"w-3.5 h-3.5\" />\n )}\n Create\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n setActionError(null);\n }}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n <X className=\"w-3.5 h-3.5\" />\n Cancel\n </button>\n </div>\n </div>\n )}\n\n {loading && profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 mx-auto mb-2 animate-spin\" />\n Loading profiles…\n </div>\n ) : profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <p>No profiles configured yet.</p>\n <p className=\"text-xs mt-1\">\n The agent will fall back to the implicit <code className=\"font-mono\">default</code>{' '}\n profile until you create one.\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {profiles.map((profile) => {\n const isActive = profile.name === active;\n return (\n <div\n key={profile.id}\n className={`p-4 flex items-center justify-between gap-4 ${\n isActive ? 'bg-action-primary-bg/5' : ''\n }`}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span\n className={`h-2 w-2 rounded-full flex-shrink-0 ${statusDot(profile.status)}`}\n aria-hidden\n />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <code className=\"font-mono font-medium text-text-primary truncate\">\n {profile.name}\n </code>\n {profile.isDefault && (\n <span className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-action-primary-bg/10 text-action-primary-bg rounded\">\n <Star className=\"w-3 h-3\" />\n Default\n </span>\n )}\n {isActive && (\n <span className=\"px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg/40 text-status-success-text rounded\">\n Active\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 truncate\">\n {profile.status ?? 'unknown'}\n {profile.port ? ` · port ${profile.port}` : ''}\n {profile.tunnelUrl ? ` · ${profile.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {!isActive && (\n <button\n type=\"button\"\n onClick={() => setActive(profile.name)}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded\"\n >\n Use\n </button>\n )}\n {!profile.isDefault && (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void handleSetDefault(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50\"\n >\n <Star className=\"w-3 h-3\" />\n Set default\n </button>\n )}\n <button\n type=\"button\"\n disabled={busy || profile.isDefault}\n title={profile.isDefault ? 'Cannot delete the default profile' : 'Delete'}\n onClick={() => void handleDelete(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg/30 border border-status-error-text/20 rounded disabled:opacity-50\"\n >\n <Trash2 className=\"w-3 h-3\" />\n Delete\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProfilesPanel.js","names":[],"sources":["../../../src/components/agent-manager/ProfilesPanel.tsx"],"sourcesContent":["/**\n * ProfilesPanel — Phase 5B (multi-profile-per-daemon).\n *\n * Mounted on AgentDetailsPage as the \"Profiles\" tab. Lists every\n * `AgentProfile` row for the current agent and exposes full CRUDL:\n * - List with status indicators\n * - Create (autofilled name \"default\" but editable)\n * - Delete\n * - Set-default\n *\n * Apollo `gql` documents are inlined so the component compiles cleanly\n * before Phase 5A (svc) publishes the AgentProfile schema to Hive.\n */\n\nimport { useCallback, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { CheckCircle2, Loader2, Plus, Star, Trash2, X } from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport { useActiveAgentProfile, type AgentProfile } from '@/hooks/useActiveAgentProfile';\n\ninterface ProfilesPanelProps {\n agentId: string;\n}\n\ninterface CreateAgentProfileInput {\n agentId: string;\n name: string;\n port?: number | null;\n}\n\ninterface CreateProfileData {\n createVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'name' | 'isDefault' | 'status'>;\n}\n\ninterface CreateProfileVars {\n input: CreateAgentProfileInput;\n}\n\ninterface DeleteProfileData {\n deleteVibecontrolsAgentProfile: boolean;\n}\n\ninterface DeleteProfileVars {\n id: string;\n}\n\ninterface SetDefaultProfileData {\n setDefaultVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'isDefault'>;\n}\n\ninterface SetDefaultProfileVars {\n id: string;\n}\n\nconst CREATE_PROFILE_MUTATION = gql`\n mutation CreateVibecontrolsAgentProfile($input: CreateVibecontrolsAgentProfileInput!) {\n createVibecontrolsAgentProfile(input: $input) {\n id\n name\n isDefault\n status\n }\n }\n`;\n\nconst DELETE_PROFILE_MUTATION = gql`\n mutation DeleteVibecontrolsAgentProfile($id: ID!) {\n deleteVibecontrolsAgentProfile(id: $id)\n }\n`;\n\nconst SET_DEFAULT_PROFILE_MUTATION = gql`\n mutation SetDefaultVibecontrolsAgentProfile($id: ID!) {\n setDefaultVibecontrolsAgentProfile(id: $id) {\n id\n isDefault\n }\n }\n`;\n\nfunction statusDot(status: string | null): string {\n const s = (status ?? '').toLowerCase();\n if (s === 'running' || s === 'active' || s === 'healthy') return 'bg-status-success-text';\n if (s === 'stopped' || s === 'inactive') return 'bg-text-placeholder';\n if (s === 'error' || s === 'failed') return 'bg-status-error-text';\n return 'bg-status-warning-text';\n}\n\nexport function ProfilesPanel({ agentId }: ProfilesPanelProps) {\n const { profiles, active, setActive, loading, refetch } = useActiveAgentProfile(agentId);\n\n const [showCreate, setShowCreate] = useState(false);\n const [newName, setNewName] = useState('default');\n const [newPort, setNewPort] = useState<string>('');\n const [actionError, setActionError] = useState<string | null>(null);\n\n const [createProfile, { loading: creating }] = useMutation<CreateProfileData, CreateProfileVars>(\n CREATE_PROFILE_MUTATION\n );\n\n const [deleteProfile, { loading: deleting }] = useMutation<DeleteProfileData, DeleteProfileVars>(\n DELETE_PROFILE_MUTATION\n );\n\n const [setDefaultProfile, { loading: settingDefault }] = useMutation<\n SetDefaultProfileData,\n SetDefaultProfileVars\n >(SET_DEFAULT_PROFILE_MUTATION);\n\n const handleCreate = useCallback(async () => {\n setActionError(null);\n const trimmedName = newName.trim();\n if (!trimmedName) {\n setActionError('Profile name is required');\n return;\n }\n const portNum = newPort.trim() ? Number(newPort.trim()) : null;\n if (newPort.trim() && (Number.isNaN(portNum) || portNum === null || portNum <= 0)) {\n setActionError('Port must be a positive number');\n return;\n }\n try {\n await createProfile({\n variables: {\n input: { agentId, name: trimmedName, port: portNum },\n },\n });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to create profile');\n return;\n }\n setShowCreate(false);\n setNewName('default');\n setNewPort('');\n // The mutation already succeeded — refresh the list off the same observable\n // the panel renders from so the new profile shows without a manual\n // \"Refresh\". refetch() is non-throwing, so a refresh hiccup can never\n // surface as a \"Failed to create\" error; the list re-syncs on the next action.\n await refetch();\n }, [agentId, createProfile, newName, newPort, refetch]);\n\n const handleDelete = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n if (!window.confirm(`Delete profile \"${profile.name}\"? This cannot be undone.`)) {\n return;\n }\n try {\n await deleteProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to delete profile');\n return;\n }\n // Refresh after a confirmed delete; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed delete (see handleCreate).\n await refetch();\n },\n [deleteProfile, refetch]\n );\n\n const handleSetDefault = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n try {\n await setDefaultProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to set default profile');\n return;\n }\n // Refresh after a confirmed change; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed update (see handleCreate).\n await refetch();\n },\n [setDefaultProfile, refetch]\n );\n\n const busy = creating || deleting || settingDefault;\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">Profiles</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Each profile runs an isolated CLI on this agent. Active:{' '}\n <code className=\"font-mono text-text-primary\">{active}</code>\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n // Fire-and-forget from a click handler: refetch() is non-throwing,\n // so ignoring the returned promise can't leak an unhandled rejection.\n onClick={() => void refetch()}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"w-4 h-4\" />\n New profile\n </button>\n </div>\n </div>\n\n {actionError && (\n <div className=\"px-4 py-2 bg-status-error-bg/30 border-b border-status-error-text/20 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {showCreate && (\n <div className=\"p-4 border-b border-border-default bg-bg-sunken/40 space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n Profile name\n </label>\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n autoFocus\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n Port (optional)\n </label>\n <input\n type=\"number\"\n value={newPort}\n onChange={(e) => setNewPort(e.target.value)}\n placeholder=\"auto\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={creating}\n onClick={() => void handleCreate()}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"w-3.5 h-3.5\" />\n )}\n Create\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n setActionError(null);\n }}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n <X className=\"w-3.5 h-3.5\" />\n Cancel\n </button>\n </div>\n </div>\n )}\n\n {loading && profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 mx-auto mb-2 animate-spin\" />\n Loading profiles…\n </div>\n ) : profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <p>No profiles configured yet.</p>\n <p className=\"text-xs mt-1\">\n The agent will fall back to the implicit <code className=\"font-mono\">default</code>{' '}\n profile until you create one.\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {profiles.map((profile) => {\n const isActive = profile.name === active;\n return (\n <div\n key={profile.id}\n className={`p-4 flex items-center justify-between gap-4 ${\n isActive ? 'bg-action-primary-bg/5' : ''\n }`}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span\n className={`h-2 w-2 rounded-full flex-shrink-0 ${statusDot(profile.status)}`}\n aria-hidden\n />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <code className=\"font-mono font-medium text-text-primary truncate\">\n {profile.name}\n </code>\n {profile.isDefault && (\n <span className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-action-primary-bg/10 text-action-primary-bg rounded\">\n <Star className=\"w-3 h-3\" />\n Default\n </span>\n )}\n {isActive && (\n <span className=\"px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg/40 text-status-success-text rounded\">\n Active\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 truncate\">\n {profile.status ?? 'unknown'}\n {profile.port ? ` · port ${profile.port}` : ''}\n {profile.tunnelUrl ? ` · ${profile.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {!isActive && (\n <button\n type=\"button\"\n onClick={() => setActive(profile.name)}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded\"\n >\n Use\n </button>\n )}\n {!profile.isDefault && (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void handleSetDefault(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50\"\n >\n <Star className=\"w-3 h-3\" />\n Set default\n </button>\n )}\n <button\n type=\"button\"\n disabled={busy || profile.isDefault}\n title={profile.isDefault ? 'Cannot delete the default profile' : 'Delete'}\n onClick={() => void handleDelete(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg/30 border border-status-error-text/20 rounded disabled:opacity-50\"\n >\n <Trash2 className=\"w-3 h-3\" />\n Delete\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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"}
@@ -40,12 +40,14 @@ function o(r) {
40
40
  },
41
41
  loading: l,
42
42
  error: u ?? null,
43
- refetch: () => {
44
- d();
43
+ refetch: async () => {
44
+ try {
45
+ await d();
46
+ } catch {}
45
47
  }
46
48
  };
47
49
  }
48
50
  //#endregion
49
- export { a as VIBECONTROLS_AGENT_PROFILES_QUERY, o as useActiveAgentProfile };
51
+ export { o as useActiveAgentProfile };
50
52
 
51
53
  //# sourceMappingURL=useActiveAgentProfile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useActiveAgentProfile.js","names":[],"sources":["../../src/hooks/useActiveAgentProfile.ts"],"sourcesContent":["/**\n * useActiveAgentProfile — Phase 5B (multi-profile-per-daemon).\n *\n * Lists `AgentProfile` rows for an agent and tracks the currently-active\n * profile name in the VibeControls Zustand store (`activeProfilePerAgent`).\n *\n * Active-selection rules:\n * 1. If the user has explicitly selected a profile for this agent, use it.\n * 2. Otherwise, fall back to the profile flagged `isDefault`.\n * 3. Otherwise, fall back to the first profile in the list.\n * 4. Otherwise, fall back to the literal string \"default\" so callers can\n * build canonical paths even before any AgentProfile rows exist\n * (server-side compatibility shim).\n *\n * NOTE: The Apollo `gql` document is inlined here (not generated) so this\n * hook compiles cleanly even before the Phase 5A svc work has published\n * the AgentProfile schema to Hive. Once published, the inline document\n * can be replaced with the generated `useVibecontrolsAgentProfilesQuery`\n * hook without touching callsites.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport interface AgentProfile {\n id: string;\n agentId: string;\n name: string;\n isDefault: boolean;\n port: number | null;\n status: string | null;\n tunnelUrl: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface VibecontrolsAgentProfilesData {\n vibecontrolsAgentProfiles: AgentProfile[];\n}\n\ninterface VibecontrolsAgentProfilesVars {\n agentId: string;\n}\n\nconst VIBECONTROLS_AGENT_PROFILES_QUERY = gql`\n query VibecontrolsAgentProfiles($agentId: ID!) {\n vibecontrolsAgentProfiles(agentId: $agentId) {\n id\n agentId\n name\n isDefault\n port\n status\n tunnelUrl\n createdAt\n updatedAt\n }\n }\n`;\n\nexport interface UseActiveAgentProfileResult {\n /** All profiles for the agent (empty until svc Phase 5A is live). */\n profiles: AgentProfile[];\n /** The currently-selected profile name for this agent. */\n active: string;\n /** Persist a new active profile for this agent into the store. */\n setActive: (profile: string) => void;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport function useActiveAgentProfile(\n agentId: string | null | undefined\n): UseActiveAgentProfileResult {\n const setActiveProfile = useVibeControlsStore((s) => s.setActiveProfile);\n const storedActive = useVibeControlsStore((s) =>\n agentId ? (s.activeProfilePerAgent[agentId] ?? null) : null\n );\n\n const { data, loading, error, refetch } = useQuery<\n VibecontrolsAgentProfilesData,\n VibecontrolsAgentProfilesVars\n >(VIBECONTROLS_AGENT_PROFILES_QUERY, {\n variables: { agentId: agentId ?? '' },\n skip: !agentId,\n fetchPolicy: 'cache-and-network',\n // The svc Phase 5A schema may not be published yet; swallow schema\n // errors so consumers still render with the canonical \"default\"\n // fallback instead of the whole tree blowing up.\n errorPolicy: 'ignore',\n });\n\n const profiles = useMemo<AgentProfile[]>(() => data?.vibecontrolsAgentProfiles ?? [], [data]);\n\n const defaultProfile = useMemo<AgentProfile | null>(() => {\n if (profiles.length === 0) return null;\n return profiles.find((p) => p.isDefault) ?? profiles[0] ?? null;\n }, [profiles]);\n\n const active: string = storedActive ?? defaultProfile?.name ?? 'default';\n\n // Auto-commit the resolved default into the store the first time we\n // see profiles for this agent so downstream readers (which prefer\n // store-first reads via `getActiveProfile`) match the hook's result.\n useEffect(() => {\n if (!agentId) return;\n if (storedActive) return;\n if (defaultProfile && defaultProfile.name !== 'default') {\n setActiveProfile(agentId, defaultProfile.name);\n }\n }, [agentId, storedActive, defaultProfile, setActiveProfile]);\n\n const setActive = (profile: string): void => {\n if (!agentId) return;\n setActiveProfile(agentId, profile);\n };\n\n return {\n profiles,\n active,\n setActive,\n loading,\n error: (error as Error | undefined) ?? null,\n refetch: () => {\n void refetch();\n },\n };\n}\n\nexport { VIBECONTROLS_AGENT_PROFILES_QUERY };\n"],"mappings":";;;;;AA8CA,IAAM,IAAoC,CAAG;;;;;;;;;;;;;;;AA4B7C,SAAgB,EACd,GAC6B;CAC7B,IAAM,IAAmB,GAAsB,MAAM,EAAE,iBAAiB,EAClE,IAAe,GAAsB,MACzC,IAAW,EAAE,sBAAsB,MAAY,OAAQ,KACxD,EAEK,EAAE,SAAM,YAAS,UAAO,eAAY,EAGxC,GAAmC;EACnC,WAAW,EAAE,SAAS,KAAW,IAAI;EACrC,MAAM,CAAC;EACP,aAAa;EAIb,aAAa;EACd,CAAC,EAEI,IAAW,QAA8B,GAAM,6BAA6B,EAAE,EAAE,CAAC,EAAK,CAAC,EAEvF,IAAiB,QACjB,EAAS,WAAW,IAAU,OAC3B,EAAS,MAAM,MAAM,EAAE,UAAU,IAAI,EAAS,MAAM,MAC1D,CAAC,EAAS,CAAC,EAER,IAAiB,KAAgB,GAAgB,QAAQ;AAkB/D,QAbA,QAAgB;AACT,QACD,KACA,KAAkB,EAAe,SAAS,aAC5C,EAAiB,GAAS,EAAe,KAAK;IAE/C;EAAC;EAAS;EAAc;EAAgB;EAAiB,CAAC,EAOtD;EACL;EACA;EACA,YARiB,MAA0B;AACtC,QACL,EAAiB,GAAS,EAAQ;;EAOlC;EACA,OAAQ,KAA+B;EACvC,eAAe;AACR,MAAS;;EAEjB"}
1
+ {"version":3,"file":"useActiveAgentProfile.js","names":[],"sources":["../../src/hooks/useActiveAgentProfile.ts"],"sourcesContent":["/**\n * useActiveAgentProfile — Phase 5B (multi-profile-per-daemon).\n *\n * Lists `AgentProfile` rows for an agent and tracks the currently-active\n * profile name in the VibeControls Zustand store (`activeProfilePerAgent`).\n *\n * Active-selection rules:\n * 1. If the user has explicitly selected a profile for this agent, use it.\n * 2. Otherwise, fall back to the profile flagged `isDefault`.\n * 3. Otherwise, fall back to the first profile in the list.\n * 4. Otherwise, fall back to the literal string \"default\" so callers can\n * build canonical paths even before any AgentProfile rows exist\n * (server-side compatibility shim).\n *\n * NOTE: The Apollo `gql` document is inlined here (not generated) so this\n * hook compiles cleanly even before the Phase 5A svc work has published\n * the AgentProfile schema to Hive. Once published, the inline document\n * can be replaced with the generated `useVibecontrolsAgentProfilesQuery`\n * hook without touching callsites.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport interface AgentProfile {\n id: string;\n agentId: string;\n name: string;\n isDefault: boolean;\n port: number | null;\n status: string | null;\n tunnelUrl: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface VibecontrolsAgentProfilesData {\n vibecontrolsAgentProfiles: AgentProfile[];\n}\n\ninterface VibecontrolsAgentProfilesVars {\n agentId: string;\n}\n\nconst VIBECONTROLS_AGENT_PROFILES_QUERY = gql`\n query VibecontrolsAgentProfiles($agentId: ID!) {\n vibecontrolsAgentProfiles(agentId: $agentId) {\n id\n agentId\n name\n isDefault\n port\n status\n tunnelUrl\n createdAt\n updatedAt\n }\n }\n`;\n\nexport interface UseActiveAgentProfileResult {\n /** All profiles for the agent (empty until svc Phase 5A is live). */\n profiles: AgentProfile[];\n /** The currently-selected profile name for this agent. */\n active: string;\n /** Persist a new active profile for this agent into the store. */\n setActive: (profile: string) => void;\n loading: boolean;\n error: Error | null;\n /**\n * Re-fetch the profile list. Resolves when the round-trip settles and\n * never rejects (failures are swallowed), so it is safe to call\n * fire-and-forget.\n */\n refetch: () => Promise<void>;\n}\n\nexport function useActiveAgentProfile(\n agentId: string | null | undefined\n): UseActiveAgentProfileResult {\n const setActiveProfile = useVibeControlsStore((s) => s.setActiveProfile);\n const storedActive = useVibeControlsStore((s) =>\n agentId ? (s.activeProfilePerAgent[agentId] ?? null) : null\n );\n\n const { data, loading, error, refetch } = useQuery<\n VibecontrolsAgentProfilesData,\n VibecontrolsAgentProfilesVars\n >(VIBECONTROLS_AGENT_PROFILES_QUERY, {\n variables: { agentId: agentId ?? '' },\n skip: !agentId,\n fetchPolicy: 'cache-and-network',\n // The svc Phase 5A schema may not be published yet; swallow schema\n // errors so consumers still render with the canonical \"default\"\n // fallback instead of the whole tree blowing up.\n errorPolicy: 'ignore',\n });\n\n const profiles = useMemo<AgentProfile[]>(() => data?.vibecontrolsAgentProfiles ?? [], [data]);\n\n const defaultProfile = useMemo<AgentProfile | null>(() => {\n if (profiles.length === 0) return null;\n return profiles.find((p) => p.isDefault) ?? profiles[0] ?? null;\n }, [profiles]);\n\n const active: string = storedActive ?? defaultProfile?.name ?? 'default';\n\n // Auto-commit the resolved default into the store the first time we\n // see profiles for this agent so downstream readers (which prefer\n // store-first reads via `getActiveProfile`) match the hook's result.\n useEffect(() => {\n if (!agentId) return;\n if (storedActive) return;\n if (defaultProfile && defaultProfile.name !== 'default') {\n setActiveProfile(agentId, defaultProfile.name);\n }\n }, [agentId, storedActive, defaultProfile, setActiveProfile]);\n\n const setActive = (profile: string): void => {\n if (!agentId) return;\n setActiveProfile(agentId, profile);\n };\n\n return {\n profiles,\n active,\n setActive,\n loading,\n error: (error as Error | undefined) ?? null,\n // Non-throwing by contract: refetch failures are swallowed here so no\n // caller — including fire-and-forget callsites like the Profiles \"Refresh\"\n // button — can produce an unhandled promise rejection. The list simply\n // stays stale until the next successful fetch.\n refetch: async () => {\n try {\n await refetch();\n } catch {\n /* swallowed — see above */\n }\n },\n };\n}\n"],"mappings":";;;;;AA8CA,IAAM,IAAoC,CAAG;;;;;;;;;;;;;;;AAiC7C,SAAgB,EACd,GAC6B;CAC7B,IAAM,IAAmB,GAAsB,MAAM,EAAE,iBAAiB,EAClE,IAAe,GAAsB,MACzC,IAAW,EAAE,sBAAsB,MAAY,OAAQ,KACxD,EAEK,EAAE,SAAM,YAAS,UAAO,eAAY,EAGxC,GAAmC;EACnC,WAAW,EAAE,SAAS,KAAW,IAAI;EACrC,MAAM,CAAC;EACP,aAAa;EAIb,aAAa;EACd,CAAC,EAEI,IAAW,QAA8B,GAAM,6BAA6B,EAAE,EAAE,CAAC,EAAK,CAAC,EAEvF,IAAiB,QACjB,EAAS,WAAW,IAAU,OAC3B,EAAS,MAAM,MAAM,EAAE,UAAU,IAAI,EAAS,MAAM,MAC1D,CAAC,EAAS,CAAC,EAER,IAAiB,KAAgB,GAAgB,QAAQ;AAkB/D,QAbA,QAAgB;AACT,QACD,KACA,KAAkB,EAAe,SAAS,aAC5C,EAAiB,GAAS,EAAe,KAAK;IAE/C;EAAC;EAAS;EAAc;EAAgB;EAAiB,CAAC,EAOtD;EACL;EACA;EACA,YARiB,MAA0B;AACtC,QACL,EAAiB,GAAS,EAAQ;;EAOlC;EACA,OAAQ,KAA+B;EAKvC,SAAS,YAAY;AACnB,OAAI;AACF,UAAM,GAAS;WACT;;EAIX"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.525.1",
3
+ "version": "2026.525.2",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [