@burdenoff/microfe-vibecontrols 2026.530.3 → 2026.530.5
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/dist/components/agents/AgentCard.js +5 -1
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/agents/AgentForm.js +86 -81
- package/dist/components/agents/AgentForm.js.map +1 -1
- package/dist/components/agents/AgentList.js +10 -2
- package/dist/components/agents/AgentList.js.map +1 -1
- package/dist/components/agents/AgentStatusBadge.js +27 -13
- package/dist/components/agents/AgentStatusBadge.js.map +1 -1
- package/dist/components/sessions/TerminalPanel.js +426 -411
- package/dist/components/sessions/TerminalPanel.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +404 -328
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +9 -5
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/providers/VibeControlsProvider.js +94 -68
- package/dist/providers/VibeControlsProvider.js.map +1 -1
- package/package.json +1 -1
|
@@ -85,7 +85,11 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
|
|
|
85
85
|
className: "flex items-center gap-1 sm:gap-2 flex-shrink-0",
|
|
86
86
|
children: [/* @__PURE__ */ g(t, {
|
|
87
87
|
status: F,
|
|
88
|
-
reason: k ?? null
|
|
88
|
+
reason: k ?? null,
|
|
89
|
+
linkTo: `/vibecontrols/agents/${v.id}?tab=connection`,
|
|
90
|
+
onClick: (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
}
|
|
89
93
|
}), (M || S || C) && /* @__PURE__ */ _("div", {
|
|
90
94
|
className: "relative",
|
|
91
95
|
children: [/* @__PURE__ */ g("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Server,\n Clock,\n Cpu,\n MoreVertical,\n Play,\n Square,\n Settings,\n Globe,\n Link,\n RefreshCw,\n} from 'lucide-react';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent, AgentStatus } from './types';\nimport { getAgentStatus } from './agentStatus';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n const [showMenu, setShowMenu] = useState(false);\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const platformIcon =\n {\n darwin: '🍎',\n linux: '🐧',\n windows: '🪟',\n }[agent.platform || ''] || '💻';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30'\n : 'border-border-default'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`flex-shrink-0 size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n )}\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"size-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {agent.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">{platformIcon}</span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge status={status} reason={healthErrorMessage ?? null} />\n {(handleConfigure || onStart || onStop) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center\"\n aria-label=\"Agent actions menu\"\n >\n <MoreVertical className=\"size-4 text-text-primary opacity-70 hover:opacity-100\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {status === 'ACTIVE' && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Square className=\"size-4\" />\n {tr('agentsPage.stopAgent', 'Stop Agent')}\n </motion.button>\n )}\n {(status === 'STOPPED' || status === 'OFFLINE') && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"size-4\" />\n {tr('agentsPage.startAgent', 'Start Agent')}\n </motion.button>\n )}\n {status === 'DEGRADED' && onStart && (\n // Reconnect on a degraded agent fires the same\n // startVibecontrolsAgent mutation as a fresh start —\n // svc-side that POSTs lifecycle/start to the agent's\n // tunnel, which usually clears transient failures\n // (tunnel hiccup, finalize retry). Labelled\n // \"Reconnect\" not \"Start\" so the user knows it's a\n // recovery action, not creating a new instance.\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n title={\n healthErrorMessage\n ? `Retry lifecycle/start — last error: ${healthErrorMessage}`\n : 'Retry lifecycle/start'\n }\n >\n <RefreshCw className=\"size-4\" />\n {tr('agentsPage.reconnectAgent', 'Reconnect')}\n </motion.button>\n )}\n {(status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken\"\n >\n <Square className=\"size-4\" />\n {status === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n </motion.button>\n )}\n {onEditTunnelUrl && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEditTunnelUrl(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Link className=\"size-4\" />\n Edit Tunnel\n </motion.button>\n )}\n {handleConfigure && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n handleConfigure(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Settings className=\"size-4\" />\n Settings\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" />\n <span>{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-3 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"text-action-primary-text hover:underline truncate\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Server,\n Clock,\n Cpu,\n MoreVertical,\n Play,\n Square,\n Settings,\n Globe,\n Link,\n RefreshCw,\n} from 'lucide-react';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent, AgentStatus } from './types';\nimport { getAgentStatus } from './agentStatus';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n const [showMenu, setShowMenu] = useState(false);\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const platformIcon =\n {\n darwin: '🍎',\n linux: '🐧',\n windows: '🪟',\n }[agent.platform || ''] || '💻';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 h-full hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30'\n : 'border-border-default'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n className={`flex-shrink-0 size-5 min-w-[1.25rem] min-h-[1.25rem] rounded border transition-colors mt-0.5 ${\n selected\n ? 'bg-action-primary-bg border-action-primary-bg text-action-primary-text'\n : 'border-border-default hover:border-border-strong bg-bg-surface'\n } flex items-center justify-center`}\n >\n {selected && (\n <svg\n className=\"size-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </button>\n )}\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"size-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {agent.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">{platformIcon}</span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge\n status={status}\n reason={healthErrorMessage ?? null}\n linkTo={`/vibecontrols/agents/${agent.id}?tab=connection`}\n onClick={(event) => {\n // The card itself has a `onSelect` click target that\n // routes to the agent detail. Stop propagation so a\n // badge click doesn't *also* fire that handler — the\n // anchor's href already routes to the right tab and\n // React Router picks it up via the surrounding shell.\n event.stopPropagation();\n }}\n />\n {(handleConfigure || onStart || onStop) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors flex items-center justify-center\"\n aria-label=\"Agent actions menu\"\n >\n <MoreVertical className=\"size-4 text-text-primary opacity-70 hover:opacity-100\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {status === 'ACTIVE' && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Square className=\"size-4\" />\n {tr('agentsPage.stopAgent', 'Stop Agent')}\n </motion.button>\n )}\n {(status === 'STOPPED' || status === 'OFFLINE') && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"size-4\" />\n {tr('agentsPage.startAgent', 'Start Agent')}\n </motion.button>\n )}\n {status === 'DEGRADED' && onStart && (\n // Reconnect on a degraded agent fires the same\n // startVibecontrolsAgent mutation as a fresh start —\n // svc-side that POSTs lifecycle/start to the agent's\n // tunnel, which usually clears transient failures\n // (tunnel hiccup, finalize retry). Labelled\n // \"Reconnect\" not \"Start\" so the user knows it's a\n // recovery action, not creating a new instance.\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n title={\n healthErrorMessage\n ? `Retry lifecycle/start — last error: ${healthErrorMessage}`\n : 'Retry lifecycle/start'\n }\n >\n <RefreshCw className=\"size-4\" />\n {tr('agentsPage.reconnectAgent', 'Reconnect')}\n </motion.button>\n )}\n {(status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDisconnect(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-action-danger-text hover:bg-bg-sunken\"\n >\n <Square className=\"size-4\" />\n {status === 'OFFLINE' ? 'Clear Connection' : 'Disconnect'}\n </motion.button>\n )}\n {onEditTunnelUrl && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEditTunnelUrl(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Link className=\"size-4\" />\n Edit Tunnel\n </motion.button>\n )}\n {handleConfigure && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n handleConfigure(agent);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Settings className=\"size-4\" />\n Settings\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" />\n <span>{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-3 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"text-action-primary-text hover:underline truncate\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { SearchableSelect as t } from "../shared/SearchableSelect.js";
|
|
|
3
3
|
import "../shared/index.js";
|
|
4
4
|
import { useCallback as n, useEffect as r, useState as i } from "react";
|
|
5
5
|
import { useI18n as ee } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
-
import { Check as
|
|
6
|
+
import { Check as a, CheckCircle2 as o, ChevronDown as te, ChevronLeft as ne, ChevronRight as s, Copy as re, Download as ie, Eye as ae, EyeOff as oe, Loader2 as c, Plus as se, RefreshCw as l, Settings2 as ce, Terminal as le, X as ue, XCircle as u } from "lucide-react";
|
|
7
7
|
import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
8
8
|
//#region src/components/agents/AgentForm.tsx
|
|
9
9
|
function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g, targetsLoading: _, onCreateTarget: v, extraSection: y }) {
|
|
@@ -121,7 +121,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
121
121
|
onClick: () => Ae(e, t),
|
|
122
122
|
className: "p-1 rounded hover:bg-bg-elevated transition-colors",
|
|
123
123
|
title: b("agentsPage.copy", "Copy"),
|
|
124
|
-
children: ge === t ? /* @__PURE__ */ f(
|
|
124
|
+
children: ge === t ? /* @__PURE__ */ f(a, { className: "size-3.5 text-status-success-text" }) : /* @__PURE__ */ f(re, { className: "size-3.5 text-text-secondary" })
|
|
125
125
|
}), Q = [{
|
|
126
126
|
key: "details",
|
|
127
127
|
label: "Setup"
|
|
@@ -153,13 +153,13 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
153
153
|
children: [
|
|
154
154
|
/* @__PURE__ */ f("div", {
|
|
155
155
|
className: `flex items-center justify-center size-7 rounded-full text-xs font-semibold transition-colors ${t < $ ? "bg-status-success-bg text-status-success-text" : t === $ ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary"}`,
|
|
156
|
-
children: t < $ ? /* @__PURE__ */ f(
|
|
156
|
+
children: t < $ ? /* @__PURE__ */ f(o, { className: "size-4" }) : t + 1
|
|
157
157
|
}),
|
|
158
158
|
/* @__PURE__ */ f("span", {
|
|
159
159
|
className: `text-sm ${t === $ ? "font-medium text-text-primary" : "text-text-secondary"}`,
|
|
160
160
|
children: e.label
|
|
161
161
|
}),
|
|
162
|
-
t < Q.length - 1 && /* @__PURE__ */ f(
|
|
162
|
+
t < Q.length - 1 && /* @__PURE__ */ f(s, { className: "size-4 text-text-muted" })
|
|
163
163
|
]
|
|
164
164
|
}, e.key))
|
|
165
165
|
}),
|
|
@@ -176,11 +176,11 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
176
176
|
className: "w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors",
|
|
177
177
|
children: [/* @__PURE__ */ p("div", {
|
|
178
178
|
className: "flex items-center gap-2",
|
|
179
|
-
children: [/* @__PURE__ */ f(
|
|
179
|
+
children: [/* @__PURE__ */ f(ie, { className: "size-4 text-action-primary-bg" }), /* @__PURE__ */ f("span", {
|
|
180
180
|
className: "text-sm font-medium text-text-primary",
|
|
181
181
|
children: b("agentsPage.installInstructions", "Agent Install Instructions")
|
|
182
182
|
})]
|
|
183
|
-
}), /* @__PURE__ */ f(
|
|
183
|
+
}), /* @__PURE__ */ f(s, { className: `size-4 text-text-secondary transition-transform ${w ? "rotate-90" : ""}` })]
|
|
184
184
|
}), w && /* @__PURE__ */ f("div", {
|
|
185
185
|
className: "px-4 py-3 space-y-3 border-t border-border-default",
|
|
186
186
|
children: /* @__PURE__ */ p("div", {
|
|
@@ -211,6 +211,27 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
211
211
|
})
|
|
212
212
|
})]
|
|
213
213
|
}),
|
|
214
|
+
/* @__PURE__ */ p("div", { children: [
|
|
215
|
+
/* @__PURE__ */ f("label", {
|
|
216
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
217
|
+
children: b("agentsPage.name", "Name")
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ f("input", {
|
|
220
|
+
"aria-label": b("agentsPage.name", "Name"),
|
|
221
|
+
type: "text",
|
|
222
|
+
value: E,
|
|
223
|
+
onChange: (e) => {
|
|
224
|
+
D(e.target.value), _e(!0);
|
|
225
|
+
},
|
|
226
|
+
placeholder: b("agentsPage.namePlaceholderUpfront", "My laptop, prod jump-host, …"),
|
|
227
|
+
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
228
|
+
"data-testid": "agent-form-name-input"
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ f("p", {
|
|
231
|
+
className: "mt-1 text-xs text-text-muted",
|
|
232
|
+
children: b("agentsPage.nameHint", "How this agent shows up in the UI. Defaults to the target's name (or the machine's hostname after Validate) — leave blank to accept the autogen.")
|
|
233
|
+
})
|
|
234
|
+
] }),
|
|
214
235
|
/* @__PURE__ */ p("div", { children: [
|
|
215
236
|
/* @__PURE__ */ p("label", {
|
|
216
237
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
@@ -245,7 +266,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
245
266
|
F(!0), L(E.trim() || "");
|
|
246
267
|
},
|
|
247
268
|
className: "text-action-primary-bg hover:underline inline-flex items-center gap-1",
|
|
248
|
-
children: [/* @__PURE__ */ f(
|
|
269
|
+
children: [/* @__PURE__ */ f(se, { className: "size-3" }), b("agentsPage.createNewTarget", "Create a new target")]
|
|
249
270
|
})
|
|
250
271
|
]
|
|
251
272
|
})] }),
|
|
@@ -260,7 +281,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
260
281
|
F(!0), L(E.trim() || "");
|
|
261
282
|
},
|
|
262
283
|
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",
|
|
263
|
-
children: [/* @__PURE__ */ f(
|
|
284
|
+
children: [/* @__PURE__ */ f(se, { className: "size-4" }), b("agentsPage.createFirstTarget", "Create first target")]
|
|
264
285
|
})]
|
|
265
286
|
}),
|
|
266
287
|
P && v && /* @__PURE__ */ p("div", {
|
|
@@ -292,7 +313,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
292
313
|
onClick: () => void Re(),
|
|
293
314
|
disabled: !I.trim() || R,
|
|
294
315
|
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",
|
|
295
|
-
children: [R ? /* @__PURE__ */ f(
|
|
316
|
+
children: [R ? /* @__PURE__ */ f(c, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ f(a, { className: "size-3.5" }), b("agentsPage.create", "Create")]
|
|
296
317
|
}), /* @__PURE__ */ f("button", {
|
|
297
318
|
type: "button",
|
|
298
319
|
onClick: () => {
|
|
@@ -306,7 +327,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
306
327
|
}),
|
|
307
328
|
_ && !Z && /* @__PURE__ */ p("div", {
|
|
308
329
|
className: "p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary",
|
|
309
|
-
children: [/* @__PURE__ */ f(
|
|
330
|
+
children: [/* @__PURE__ */ f(c, { className: "size-4 animate-spin" }), b("agentsPage.loadingTargets", "Loading targets...")]
|
|
310
331
|
})
|
|
311
332
|
] }),
|
|
312
333
|
/* @__PURE__ */ p("div", { children: [
|
|
@@ -361,7 +382,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
361
382
|
type: "button",
|
|
362
383
|
onClick: () => me(!C),
|
|
363
384
|
className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-bg-elevated transition-colors",
|
|
364
|
-
children: f(C ?
|
|
385
|
+
children: f(C ? oe : ae, { className: "size-4 text-text-secondary" })
|
|
365
386
|
})]
|
|
366
387
|
}),
|
|
367
388
|
/* @__PURE__ */ p("p", {
|
|
@@ -392,7 +413,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
392
413
|
className: "text-sm font-medium text-text-primary",
|
|
393
414
|
children: b("agentsPage.advancedOptions", "Advanced options")
|
|
394
415
|
})]
|
|
395
|
-
}), /* @__PURE__ */ f(
|
|
416
|
+
}), /* @__PURE__ */ f(te, { className: `size-4 text-text-secondary transition-transform ${X ? "rotate-180" : ""}` })]
|
|
396
417
|
}), X && /* @__PURE__ */ f("div", {
|
|
397
418
|
className: "px-4 py-3 space-y-3 border-t border-border-default",
|
|
398
419
|
children: /* @__PURE__ */ p("div", { children: [
|
|
@@ -434,7 +455,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
434
455
|
S("validate"), Ne();
|
|
435
456
|
},
|
|
436
457
|
className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
437
|
-
children: [b("agentsPage.nextValidate", "Next: Validate"), /* @__PURE__ */ f(
|
|
458
|
+
children: [b("agentsPage.nextValidate", "Next: Validate"), /* @__PURE__ */ f(s, { className: "size-4" })]
|
|
438
459
|
})]
|
|
439
460
|
})
|
|
440
461
|
]
|
|
@@ -445,7 +466,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
445
466
|
className: "space-y-3",
|
|
446
467
|
children: [/* @__PURE__ */ p("div", {
|
|
447
468
|
className: `flex items-center gap-3 p-3 rounded-lg border ${W === null ? "border-border-default bg-bg-sunken" : W.ok ? "border-status-success-text/20 bg-status-success-bg/30" : "border-status-error-text/20 bg-status-error-bg/30"}`,
|
|
448
|
-
children: [H && !W ? /* @__PURE__ */ f(
|
|
469
|
+
children: [H && !W ? /* @__PURE__ */ f(c, { className: "size-5 text-text-secondary animate-spin" }) : W?.ok ? /* @__PURE__ */ f(o, { className: "size-5 text-status-success-text" }) : W ? /* @__PURE__ */ f(u, { className: "size-5 text-status-error-text" }) : /* @__PURE__ */ f("div", { className: "size-5 rounded-full border-2 border-border-default" }), /* @__PURE__ */ p("div", {
|
|
449
470
|
className: "flex-1",
|
|
450
471
|
children: [
|
|
451
472
|
/* @__PURE__ */ f("p", {
|
|
@@ -472,7 +493,7 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
472
493
|
})]
|
|
473
494
|
}), /* @__PURE__ */ p("div", {
|
|
474
495
|
className: `flex items-center gap-3 p-3 rounded-lg border ${K === null ? "border-border-default bg-bg-sunken" : K.ok ? "border-status-success-text/20 bg-status-success-bg/30" : "border-status-error-text/20 bg-status-error-bg/30"}`,
|
|
475
|
-
children: [H && !K ? /* @__PURE__ */ f(
|
|
496
|
+
children: [H && !K ? /* @__PURE__ */ f(c, { className: "size-5 text-text-secondary animate-spin" }) : K?.ok ? /* @__PURE__ */ f(o, { className: "size-5 text-status-success-text" }) : K ? /* @__PURE__ */ f(u, { className: "size-5 text-status-error-text" }) : /* @__PURE__ */ f("div", { className: "size-5 rounded-full border-2 border-border-default" }), /* @__PURE__ */ p("div", {
|
|
476
497
|
className: "flex-1",
|
|
477
498
|
children: [
|
|
478
499
|
/* @__PURE__ */ f("p", {
|
|
@@ -507,71 +528,55 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
507
528
|
}),
|
|
508
529
|
Fe && /* @__PURE__ */ p("div", {
|
|
509
530
|
className: "space-y-3 pt-2",
|
|
510
|
-
children: [
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
531
|
+
children: [/* @__PURE__ */ f("p", {
|
|
532
|
+
className: "text-sm font-medium text-text-primary",
|
|
533
|
+
children: b("agentsPage.detectedAgentInfo", "Detected Agent Info")
|
|
534
|
+
}), /* @__PURE__ */ p("div", {
|
|
535
|
+
className: "grid grid-cols-2 gap-3",
|
|
536
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
516
537
|
className: "block text-xs font-medium text-text-secondary mb-1",
|
|
517
|
-
children: b("agentsPage.
|
|
518
|
-
}), /* @__PURE__ */
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
/* @__PURE__ */ f("option", {
|
|
560
|
-
value: "x86_64",
|
|
561
|
-
children: "x86_64"
|
|
562
|
-
}),
|
|
563
|
-
/* @__PURE__ */ f("option", {
|
|
564
|
-
value: "arm64",
|
|
565
|
-
children: "arm64"
|
|
566
|
-
}),
|
|
567
|
-
/* @__PURE__ */ f("option", {
|
|
568
|
-
value: "aarch64",
|
|
569
|
-
children: "aarch64"
|
|
570
|
-
})
|
|
571
|
-
]
|
|
572
|
-
})] })]
|
|
573
|
-
})
|
|
574
|
-
]
|
|
538
|
+
children: b("agentsPage.platform", "Platform")
|
|
539
|
+
}), /* @__PURE__ */ p("select", {
|
|
540
|
+
value: be,
|
|
541
|
+
onChange: (e) => xe(e.target.value),
|
|
542
|
+
className: "w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
543
|
+
children: [
|
|
544
|
+
/* @__PURE__ */ f("option", {
|
|
545
|
+
value: "linux",
|
|
546
|
+
children: "Linux"
|
|
547
|
+
}),
|
|
548
|
+
/* @__PURE__ */ f("option", {
|
|
549
|
+
value: "darwin",
|
|
550
|
+
children: "macOS"
|
|
551
|
+
}),
|
|
552
|
+
/* @__PURE__ */ f("option", {
|
|
553
|
+
value: "windows",
|
|
554
|
+
children: "Windows"
|
|
555
|
+
})
|
|
556
|
+
]
|
|
557
|
+
})] }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
558
|
+
className: "block text-xs font-medium text-text-secondary mb-1",
|
|
559
|
+
children: b("agentsPage.architecture", "Architecture")
|
|
560
|
+
}), /* @__PURE__ */ p("select", {
|
|
561
|
+
value: Se,
|
|
562
|
+
onChange: (e) => Ce(e.target.value),
|
|
563
|
+
className: "w-full px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ f("option", {
|
|
566
|
+
value: "x86_64",
|
|
567
|
+
children: "x86_64"
|
|
568
|
+
}),
|
|
569
|
+
/* @__PURE__ */ f("option", {
|
|
570
|
+
value: "arm64",
|
|
571
|
+
children: "arm64"
|
|
572
|
+
}),
|
|
573
|
+
/* @__PURE__ */ f("option", {
|
|
574
|
+
value: "aarch64",
|
|
575
|
+
children: "aarch64"
|
|
576
|
+
})
|
|
577
|
+
]
|
|
578
|
+
})] })]
|
|
579
|
+
})]
|
|
575
580
|
}),
|
|
576
581
|
Ee && /* @__PURE__ */ p("div", {
|
|
577
582
|
className: "p-3 bg-status-error-bg/30 border border-status-error-text/20 rounded-lg",
|
|
@@ -604,13 +609,13 @@ function m({ onSubmit: m, onCancel: h, isLoading: de, onNavigate: fe, targets: g
|
|
|
604
609
|
type: "button",
|
|
605
610
|
onClick: () => S("details"),
|
|
606
611
|
className: "flex items-center gap-1 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
|
|
607
|
-
children: [/* @__PURE__ */ f(
|
|
612
|
+
children: [/* @__PURE__ */ f(ne, { className: "size-4" }), b("agentsPage.back", "Back")]
|
|
608
613
|
}), /* @__PURE__ */ f("button", {
|
|
609
614
|
type: "button",
|
|
610
615
|
disabled: !Fe || de || H,
|
|
611
616
|
onClick: Pe,
|
|
612
617
|
className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
613
|
-
children: de ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(
|
|
618
|
+
children: de ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(c, { className: "size-4 animate-spin" }), b("agentsPage.adding", "Adding...")] }) : /* @__PURE__ */ p(d, { children: [b("agentsPage.addAgent", "Add Agent"), /* @__PURE__ */ f(s, { className: "size-4" })] })
|
|
614
619
|
})]
|
|
615
620
|
})
|
|
616
621
|
]
|