groove-dev 0.27.142 → 0.27.144

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.
Files changed (187) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  4. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  5. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  6. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  7. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  8. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  9. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  10. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  11. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  12. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  13. package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
  14. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  15. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  21. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  22. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  23. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  24. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  25. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  26. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  27. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  28. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  29. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  30. package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  31. package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  32. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  33. package/node_modules/@groove-dev/gui/package.json +1 -1
  34. package/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
  35. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  36. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  37. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  39. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  40. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  41. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  42. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  43. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  44. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  45. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  46. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  47. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  48. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  49. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  50. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  54. package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
  55. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  56. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  57. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
  58. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
  59. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  60. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  61. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  62. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  63. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  64. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  65. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  66. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  67. package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
  68. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  69. package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
  70. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  71. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
  72. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  73. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
  74. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  75. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  76. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  77. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  78. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  79. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  81. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  82. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  83. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  84. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
  85. package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
  86. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  87. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  88. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  89. package/package.json +1 -1
  90. package/packages/cli/package.json +1 -1
  91. package/packages/daemon/package.json +1 -1
  92. package/packages/daemon/src/api.js +1086 -6532
  93. package/packages/daemon/src/gateways/manager.js +35 -1
  94. package/packages/daemon/src/index.js +3 -0
  95. package/packages/daemon/src/journalist.js +23 -13
  96. package/packages/daemon/src/mlx-server.js +365 -0
  97. package/packages/daemon/src/model-lab.js +308 -12
  98. package/packages/daemon/src/pm.js +1 -1
  99. package/packages/daemon/src/process.js +2 -2
  100. package/packages/daemon/src/providers/local.js +36 -8
  101. package/packages/daemon/src/registry.js +21 -5
  102. package/packages/daemon/src/routes/agents.js +889 -0
  103. package/packages/daemon/src/routes/coordination.js +318 -0
  104. package/packages/daemon/src/routes/files.js +751 -0
  105. package/packages/daemon/src/routes/integrations.js +485 -0
  106. package/packages/daemon/src/routes/network.js +1784 -0
  107. package/packages/daemon/src/routes/providers.js +755 -0
  108. package/packages/daemon/src/routes/schedules.js +110 -0
  109. package/packages/daemon/src/routes/teams.js +650 -0
  110. package/packages/daemon/src/scheduler.js +456 -24
  111. package/packages/daemon/src/teams.js +1 -1
  112. package/packages/daemon/src/validate.js +38 -1
  113. package/packages/daemon/templates/mlx-setup.json +12 -0
  114. package/packages/daemon/templates/tgi-setup.json +1 -1
  115. package/packages/daemon/templates/vllm-setup.json +1 -1
  116. package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
  117. package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
  118. package/packages/gui/dist/index.html +2 -2
  119. package/packages/gui/package.json +1 -1
  120. package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
  121. package/packages/gui/src/app.css +35 -0
  122. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  123. package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
  124. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  125. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  126. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  127. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  128. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  129. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  130. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  131. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  132. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  133. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  134. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  135. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  136. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  137. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  138. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  139. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  140. package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
  141. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  142. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  143. package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
  144. package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
  145. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  146. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  147. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  148. package/packages/gui/src/components/network/network-health.jsx +2 -2
  149. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  150. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  151. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  152. package/packages/gui/src/lib/cron.js +64 -0
  153. package/packages/gui/src/lib/status.js +24 -24
  154. package/packages/gui/src/lib/theme-hex.js +1 -0
  155. package/packages/gui/src/stores/groove.js +34 -3144
  156. package/packages/gui/src/stores/helpers.js +10 -0
  157. package/packages/gui/src/stores/slices/agents-slice.js +452 -0
  158. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  159. package/packages/gui/src/stores/slices/chat-slice.js +227 -0
  160. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  161. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  162. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  163. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  164. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  165. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  166. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  167. package/packages/gui/src/views/agents.jsx +5 -5
  168. package/packages/gui/src/views/dashboard.jsx +12 -13
  169. package/packages/gui/src/views/marketplace.jsx +191 -3
  170. package/packages/gui/src/views/model-lab.jsx +17 -6
  171. package/packages/gui/src/views/models.jsx +410 -509
  172. package/packages/gui/src/views/network.jsx +3 -3
  173. package/packages/gui/src/views/settings.jsx +81 -94
  174. package/packages/gui/src/views/teams.jsx +40 -483
  175. package/SECURITY_SWEEP.md +0 -228
  176. package/TRAINING_DATA_v4.md +0 -6
  177. package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
  178. package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
  179. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
  180. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  181. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  182. package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
  183. package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
  184. package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
  185. package/packages/gui/src/views/preview.jsx +0 -6
  186. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  187. package/test.py +0 -571
@@ -11,15 +11,15 @@ import {
11
11
  } from 'lucide-react';
12
12
 
13
13
  const STEPS = [
14
- { id: 'details', label: 'Server Details', icon: Server },
15
- { id: 'auth', label: 'Authentication', icon: KeyRound },
14
+ { id: 'details', label: 'Server', icon: Server },
15
+ { id: 'auth', label: 'Auth', icon: KeyRound },
16
16
  { id: 'setup', label: 'Setup', icon: Settings },
17
17
  { id: 'connected', label: 'Connected', icon: Plug },
18
18
  ];
19
19
 
20
20
  function StepIndicator({ steps, currentStep, completedSteps, onStepClick }) {
21
21
  return (
22
- <div className="flex items-center gap-1 mb-4">
22
+ <div className="flex items-center mb-6">
23
23
  {steps.map((step, i) => {
24
24
  const isActive = currentStep === i;
25
25
  const isCompleted = completedSteps.includes(i);
@@ -27,35 +27,31 @@ function StepIndicator({ steps, currentStep, completedSteps, onStepClick }) {
27
27
  const Icon = step.icon;
28
28
 
29
29
  return (
30
- <div key={step.id} className="flex items-center gap-1 flex-1">
30
+ <div key={step.id} className="flex items-center flex-1 last:flex-initial">
31
31
  <button
32
32
  onClick={() => isClickable && onStepClick(i)}
33
33
  disabled={!isClickable}
34
34
  className={cn(
35
- 'flex items-center gap-1.5 px-2 py-1 rounded-md transition-colors text-2xs font-sans font-medium',
36
- isActive
37
- ? 'bg-accent/12 text-accent'
38
- : isCompleted
39
- ? 'text-success cursor-pointer hover:bg-surface-3'
40
- : 'text-text-4',
35
+ 'flex items-center gap-2 px-2.5 py-1.5 rounded-lg transition-all text-xs font-sans font-medium',
36
+ isActive && 'bg-accent/10 text-accent',
37
+ isCompleted && 'text-success cursor-pointer hover:bg-surface-3',
38
+ !isActive && !isCompleted && 'text-text-4',
41
39
  isClickable && !isActive && 'cursor-pointer',
42
40
  )}
43
41
  >
44
42
  <div className={cn(
45
- 'w-5 h-5 rounded-full flex items-center justify-center text-2xs font-semibold border transition-colors',
46
- isActive
47
- ? 'border-accent bg-accent/15 text-accent'
48
- : isCompleted
49
- ? 'border-success/40 bg-success/10 text-success'
50
- : 'border-border-subtle bg-surface-3 text-text-4',
43
+ 'w-7 h-7 rounded-full flex items-center justify-center text-2xs font-bold border-2 transition-all',
44
+ isActive && 'border-accent bg-accent/15 text-accent',
45
+ isCompleted && 'border-success/40 bg-success/10 text-success',
46
+ !isActive && !isCompleted && 'border-border-subtle bg-surface-3 text-text-4',
51
47
  )}>
52
- {isCompleted ? <Check size={10} /> : i + 1}
48
+ {isCompleted ? <Check size={11} /> : <Icon size={11} />}
53
49
  </div>
54
50
  <span className="hidden sm:inline">{step.label}</span>
55
51
  </button>
56
52
  {i < steps.length - 1 && (
57
53
  <div className={cn(
58
- 'flex-1 h-px mx-1',
54
+ 'flex-1 h-px mx-1.5',
59
55
  isCompleted ? 'bg-success/30' : 'bg-border-subtle',
60
56
  )} />
61
57
  )}
@@ -85,14 +81,31 @@ function ToggleSwitch({ value, onChange }) {
85
81
 
86
82
  function FieldCard({ icon: Icon, title, children }) {
87
83
  return (
88
- <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5 flex flex-col gap-2">
89
- <div className="flex items-center gap-2">
90
- <div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
91
- <Icon size={12} className="text-accent" />
84
+ <div className="rounded-xl border border-border-subtle bg-surface-1/80 px-5 py-4 flex flex-col gap-2.5">
85
+ <div className="flex items-center gap-2.5">
86
+ <div className="w-7 h-7 rounded-lg bg-accent/10 border border-accent/10 flex items-center justify-center flex-shrink-0">
87
+ <Icon size={13} className="text-accent" />
92
88
  </div>
93
- <span className="text-[13px] font-medium text-text-0 font-sans leading-tight">{title}</span>
89
+ <span className="text-sm font-semibold text-text-0 font-sans">{title}</span>
94
90
  </div>
95
- <div className="mt-1">{children}</div>
91
+ <div className="mt-0.5">{children}</div>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ function InfoCard({ icon: Icon, title, iconColor, children }) {
97
+ return (
98
+ <div className="rounded-xl border border-border-subtle bg-surface-1/80 px-5 py-4">
99
+ <div className="flex items-center gap-2.5 mb-3">
100
+ <div className={cn(
101
+ 'w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0',
102
+ iconColor || 'bg-accent/10',
103
+ )}>
104
+ <Icon size={13} className={iconColor ? undefined : 'text-accent'} />
105
+ </div>
106
+ <span className="text-sm font-semibold text-text-0 font-sans">{title}</span>
107
+ </div>
108
+ {children}
96
109
  </div>
97
110
  );
98
111
  }
@@ -216,11 +229,11 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
216
229
  setConnecting(false);
217
230
  }
218
231
 
219
- const inputCls = 'h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-sans placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent';
220
- const monoInputCls = 'h-8 px-2.5 text-xs bg-surface-0 border border-border-subtle rounded-md text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent';
232
+ const inputCls = 'h-9 px-3 text-xs bg-surface-0 border border-border-subtle rounded-lg text-text-0 font-sans placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/30 transition-colors';
233
+ const monoInputCls = 'h-9 px-3 text-xs bg-surface-0 border border-border-subtle rounded-lg text-text-0 font-mono placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/30 transition-colors';
221
234
 
222
235
  return (
223
- <div className="p-4">
236
+ <div className="p-5">
224
237
  <StepIndicator
225
238
  steps={STEPS}
226
239
  currentStep={step}
@@ -228,13 +241,12 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
228
241
  onStepClick={setStep}
229
242
  />
230
243
 
231
- {/* Step 0: Server Details */}
232
244
  {step === 0 && (
233
- <div className="grid grid-cols-2 gap-3">
245
+ <div className="grid grid-cols-2 gap-4">
234
246
  <FieldCard icon={Server} title="Server Info">
235
- <div className="space-y-2.5">
247
+ <div className="space-y-3">
236
248
  <div>
237
- <label className="text-2xs font-semibold text-text-2 font-sans mb-1 block">Name</label>
249
+ <label className="text-2xs font-semibold text-text-2 font-sans mb-1.5 block">Name</label>
238
250
  <input
239
251
  value={name}
240
252
  onChange={(e) => setName(e.target.value)}
@@ -244,7 +256,7 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
244
256
  />
245
257
  </div>
246
258
  <div>
247
- <label className="text-2xs font-semibold text-text-2 font-sans mb-1 block">Host</label>
259
+ <label className="text-2xs font-semibold text-text-2 font-sans mb-1.5 block">Host</label>
248
260
  <input
249
261
  value={host}
250
262
  onChange={(e) => setHost(e.target.value)}
@@ -256,9 +268,9 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
256
268
  </FieldCard>
257
269
 
258
270
  <FieldCard icon={Settings} title="Connection">
259
- <div className="space-y-2.5">
271
+ <div className="space-y-3">
260
272
  <div>
261
- <label className="text-2xs font-semibold text-text-2 font-sans mb-1 block">User</label>
273
+ <label className="text-2xs font-semibold text-text-2 font-sans mb-1.5 block">User</label>
262
274
  <input
263
275
  value={user}
264
276
  onChange={(e) => setUser(e.target.value)}
@@ -267,7 +279,7 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
267
279
  />
268
280
  </div>
269
281
  <div>
270
- <label className="text-2xs font-semibold text-text-2 font-sans mb-1 block">SSH Port</label>
282
+ <label className="text-2xs font-semibold text-text-2 font-sans mb-1.5 block">SSH Port</label>
271
283
  <input
272
284
  value={sshPort}
273
285
  onChange={(e) => setSshPort(Number(e.target.value) || 22)}
@@ -280,13 +292,12 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
280
292
  </div>
281
293
  )}
282
294
 
283
- {/* Step 1: Authentication */}
284
295
  {step === 1 && (
285
- <div className="grid grid-cols-2 gap-3">
296
+ <div className="grid grid-cols-2 gap-4">
286
297
  <FieldCard icon={KeyRound} title="SSH Key">
287
- <div className="space-y-2.5">
298
+ <div className="space-y-3">
288
299
  <div>
289
- <label className="text-2xs font-semibold text-text-2 font-sans mb-1 block">Key Path</label>
300
+ <label className="text-2xs font-semibold text-text-2 font-sans mb-1.5 block">Key Path</label>
290
301
  <div className="flex items-center gap-1.5">
291
302
  <input
292
303
  value={sshKeyPath}
@@ -299,12 +310,12 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
299
310
  variant="secondary"
300
311
  size="sm"
301
312
  onClick={() => setKeyBrowserOpen(true)}
302
- className="h-8 px-2 flex-shrink-0"
313
+ className="h-9 px-2.5 flex-shrink-0"
303
314
  >
304
- <FolderSearch size={12} />
315
+ <FolderSearch size={13} />
305
316
  </Button>
306
317
  </div>
307
- <p className="text-2xs text-text-4 font-sans mt-1">
318
+ <p className="text-2xs text-text-4 font-sans mt-1.5">
308
319
  Leave blank to use default SSH agent.
309
320
  </p>
310
321
  </div>
@@ -313,7 +324,7 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
313
324
  size="sm"
314
325
  onClick={handleTest}
315
326
  disabled={testLoading}
316
- className="h-7 text-2xs gap-1.5"
327
+ className="h-8 text-2xs gap-1.5"
317
328
  >
318
329
  {testLoading ? <Loader2 size={11} className="animate-spin" /> : <Plug size={11} />}
319
330
  Test Connection
@@ -321,15 +332,9 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
321
332
  </div>
322
333
  </FieldCard>
323
334
 
324
- <div className="space-y-3">
325
- <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
326
- <div className="flex items-center gap-2 mb-2">
327
- <div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
328
- <Server size={12} className="text-accent" />
329
- </div>
330
- <span className="text-[13px] font-medium text-text-0 font-sans">Target</span>
331
- </div>
332
- <div className="space-y-1.5 text-2xs font-sans">
335
+ <div className="space-y-4">
336
+ <InfoCard icon={Server} title="Target">
337
+ <div className="space-y-2 text-2xs font-sans">
333
338
  <div className="flex items-center justify-between">
334
339
  <span className="text-text-3">Host</span>
335
340
  <span className="text-text-1 font-mono">{host || '—'}</span>
@@ -343,11 +348,11 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
343
348
  <span className="text-text-1 font-mono">{sshPort}</span>
344
349
  </div>
345
350
  </div>
346
- </div>
351
+ </InfoCard>
347
352
 
348
353
  {testResult && (
349
354
  <div className={cn(
350
- 'px-3 py-2.5 rounded-lg text-2xs font-sans flex items-start gap-2',
355
+ 'px-4 py-3 rounded-xl text-2xs font-sans flex items-start gap-2',
351
356
  testResult.error
352
357
  ? 'bg-danger/8 border border-danger/20 text-danger'
353
358
  : testResult.reachable
@@ -375,11 +380,10 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
375
380
  </div>
376
381
  )}
377
382
 
378
- {/* Step 2: Setup */}
379
383
  {step === 2 && (
380
- <div className="grid grid-cols-2 gap-3">
384
+ <div className="grid grid-cols-2 gap-4">
381
385
  <FieldCard icon={Settings} title="Behavior">
382
- <div className="space-y-3">
386
+ <div className="space-y-4">
383
387
  <label className="flex items-center justify-between cursor-pointer">
384
388
  <div>
385
389
  <span className="text-xs text-text-1 font-sans block">Auto-start daemon</span>
@@ -397,15 +401,9 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
397
401
  </div>
398
402
  </FieldCard>
399
403
 
400
- {testResult && !testResult.error && (
401
- <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
402
- <div className="flex items-center gap-2 mb-3">
403
- <div className="w-6 h-6 rounded bg-success/10 flex items-center justify-center flex-shrink-0">
404
- <Check size={12} className="text-success" />
405
- </div>
406
- <span className="text-[13px] font-medium text-text-0 font-sans">Test Results</span>
407
- </div>
408
- <div className="space-y-2">
404
+ {testResult && !testResult.error ? (
405
+ <InfoCard icon={Check} title="Test Results" iconColor="bg-success/10 text-success">
406
+ <div className="space-y-2.5">
409
407
  <div className="flex items-center gap-2 text-2xs font-sans">
410
408
  <StatusDot status={testResult.reachable ? 'running' : 'crashed'} size="sm" />
411
409
  <span className="text-text-1">Reachable</span>
@@ -419,18 +417,10 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
419
417
  <span className="text-text-1">Daemon Running</span>
420
418
  </div>
421
419
  </div>
422
- </div>
423
- )}
424
-
425
- {(!testResult || testResult.error) && (
426
- <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
427
- <div className="flex items-center gap-2 mb-2">
428
- <div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
429
- <Server size={12} className="text-accent" />
430
- </div>
431
- <span className="text-[13px] font-medium text-text-0 font-sans">{name || 'Server'}</span>
432
- </div>
433
- <div className="space-y-1.5 text-2xs font-sans">
420
+ </InfoCard>
421
+ ) : (
422
+ <InfoCard icon={Server} title={name || 'Server'}>
423
+ <div className="space-y-2 text-2xs font-sans">
434
424
  <div className="flex items-center justify-between">
435
425
  <span className="text-text-3">Connection</span>
436
426
  <span className="text-text-1 font-mono">{user}@{host}:{sshPort}</span>
@@ -442,21 +432,20 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
442
432
  </div>
443
433
  )}
444
434
  </div>
445
- </div>
435
+ </InfoCard>
446
436
  )}
447
437
  </div>
448
438
  )}
449
439
 
450
- {/* Step 3: Connected */}
451
440
  {step === 3 && (
452
- <div className="grid grid-cols-2 gap-3">
453
- <div className="rounded-lg border border-success/30 bg-success/5 px-4 py-5 text-center">
454
- <div className="w-10 h-10 rounded-full bg-success/15 flex items-center justify-center mx-auto mb-3">
455
- <Check size={20} className="text-success" />
441
+ <div className="grid grid-cols-2 gap-4">
442
+ <div className="rounded-xl border border-success/25 bg-gradient-to-br from-success/[0.06] to-transparent px-5 py-6 text-center">
443
+ <div className="w-12 h-12 rounded-full bg-success/15 border border-success/20 flex items-center justify-center mx-auto mb-3">
444
+ <Check size={22} className="text-success" />
456
445
  </div>
457
- <h3 className="text-sm font-semibold text-text-0 font-sans mb-1">Connected</h3>
458
- <p className="text-2xs text-text-3 font-sans">
459
- Successfully connected to <span className="font-mono text-text-1">{name}</span>
446
+ <h3 className="text-base font-semibold text-text-0 font-sans mb-1">Connected</h3>
447
+ <p className="text-xs text-text-3 font-sans mb-4">
448
+ Successfully connected to <span className="font-mono text-text-1 font-medium">{name}</span>
460
449
  </p>
461
450
  <Button
462
451
  variant="primary"
@@ -466,21 +455,15 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
466
455
  const n = encodeURIComponent(name);
467
456
  window.open(`http://localhost:${port}?instance=${n}`, '_blank');
468
457
  }}
469
- className="h-8 text-xs gap-1.5 mt-4"
458
+ className="h-8 text-xs gap-1.5"
470
459
  >
471
460
  <ExternalLink size={12} />
472
461
  Open Remote GUI
473
462
  </Button>
474
463
  </div>
475
464
 
476
- <div className="rounded-lg border border-border-subtle bg-surface-1 px-4 py-3.5">
477
- <div className="flex items-center gap-2 mb-3">
478
- <div className="w-6 h-6 rounded bg-accent/8 flex items-center justify-center flex-shrink-0">
479
- <Server size={12} className="text-accent" />
480
- </div>
481
- <span className="text-[13px] font-medium text-text-0 font-sans">Connection Info</span>
482
- </div>
483
- <div className="space-y-1.5 text-2xs font-sans">
465
+ <InfoCard icon={Server} title="Connection Info">
466
+ <div className="space-y-2 text-2xs font-sans">
484
467
  <div className="flex items-center justify-between">
485
468
  <span className="text-text-3">Connection</span>
486
469
  <span className="text-text-1 font-mono">{user}@{host}:{sshPort}</span>
@@ -509,12 +492,11 @@ export function SSHWizard({ server, onSave, onTest, onConnect, onCancel }) {
509
492
  </div>
510
493
  )}
511
494
  </div>
512
- </div>
495
+ </InfoCard>
513
496
  </div>
514
497
  )}
515
498
 
516
- {/* Navigation footer */}
517
- <div className="flex items-center justify-between mt-4">
499
+ <div className="flex items-center justify-between mt-5">
518
500
  <Button
519
501
  variant="ghost"
520
502
  size="sm"
@@ -9,7 +9,7 @@ export function Sheet({ children, ...props }) {
9
9
 
10
10
  export const SheetTrigger = DialogPrimitive.Trigger;
11
11
 
12
- export function SheetContent({ children, className, title, side = 'right', width = 400, ...props }) {
12
+ export function SheetContent({ children, className, title, side = 'right', width = 400, onClose, ...props }) {
13
13
  return (
14
14
  <DialogPrimitive.Portal>
15
15
  <DialogPrimitive.Overlay className="fixed inset-0 z-40 bg-black/30" />
@@ -30,7 +30,10 @@ export function SheetContent({ children, className, title, side = 'right', width
30
30
  <DialogPrimitive.Title className="text-base font-semibold text-text-0 font-sans">
31
31
  {title}
32
32
  </DialogPrimitive.Title>
33
- <DialogPrimitive.Close className="p-1 rounded-md text-text-3 hover:text-text-0 hover:bg-surface-5 transition-colors cursor-pointer">
33
+ <DialogPrimitive.Close
34
+ onClick={onClose}
35
+ className="p-1 rounded-md text-text-3 hover:text-text-0 hover:bg-surface-5 transition-colors cursor-pointer"
36
+ >
34
37
  <X size={16} />
35
38
  </DialogPrimitive.Close>
36
39
  </div>
@@ -0,0 +1,64 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+
3
+ export const CRON_PRESETS = [
4
+ { label: 'Every hour', cron: '0 * * * *', description: 'Runs at the start of every hour' },
5
+ { label: 'Every morning', cron: '0 9 * * *', description: 'Daily at 9:00 AM' },
6
+ { label: 'Twice daily', cron: '0 9,17 * * *', description: '9:00 AM and 5:00 PM' },
7
+ { label: 'Every weekday', cron: '0 9 * * 1-5', description: 'Monday-Friday at 9:00 AM' },
8
+ { label: 'Mon & Thu', cron: '0 9 * * 1,4', description: 'Monday and Thursday at 9:00 AM' },
9
+ { label: 'Weekly', cron: '0 9 * * 1', description: 'Every Monday at 9:00 AM' },
10
+ { label: 'Monthly', cron: '0 9 1 * *', description: '1st of each month at 9:00 AM' },
11
+ ];
12
+
13
+ const presetMap = new Map(CRON_PRESETS.map((p) => [p.cron, p.description]));
14
+
15
+ export function cronToHuman(expr) {
16
+ if (!expr) return '';
17
+ const trimmed = expr.trim();
18
+ const match = presetMap.get(trimmed);
19
+ if (match) return match;
20
+ return trimmed;
21
+ }
22
+
23
+ const FIELD_RANGES = [
24
+ { name: 'minute', min: 0, max: 59 },
25
+ { name: 'hour', min: 0, max: 23 },
26
+ { name: 'day of month', min: 1, max: 31 },
27
+ { name: 'month', min: 1, max: 12 },
28
+ { name: 'day of week', min: 0, max: 7 },
29
+ ];
30
+
31
+ function validateField(value, range) {
32
+ if (value === '*') return null;
33
+ if (/^\*\/\d+$/.test(value)) {
34
+ const step = parseInt(value.slice(2), 10);
35
+ if (step < 1 || step > range.max) return `Invalid step ${step} for ${range.name}`;
36
+ return null;
37
+ }
38
+ const parts = value.split(',');
39
+ for (const part of parts) {
40
+ if (part.includes('-')) {
41
+ const [lo, hi] = part.split('-').map(Number);
42
+ if (isNaN(lo) || isNaN(hi) || lo < range.min || hi > range.max || lo > hi) {
43
+ return `Invalid range ${part} for ${range.name} (${range.min}-${range.max})`;
44
+ }
45
+ } else {
46
+ const n = Number(part);
47
+ if (isNaN(n) || n < range.min || n > range.max) {
48
+ return `Invalid value ${part} for ${range.name} (${range.min}-${range.max})`;
49
+ }
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
55
+ export function validateCron(expr) {
56
+ if (!expr || typeof expr !== 'string') return { valid: false, error: 'Expression is required' };
57
+ const fields = expr.trim().split(/\s+/);
58
+ if (fields.length !== 5) return { valid: false, error: `Expected 5 fields, got ${fields.length}` };
59
+ for (let i = 0; i < 5; i++) {
60
+ const err = validateField(fields[i], FIELD_RANGES[i]);
61
+ if (err) return { valid: false, error: err };
62
+ }
63
+ return { valid: true };
64
+ }
@@ -2,13 +2,13 @@
2
2
  // Single source of truth for agent status colors + role colors
3
3
 
4
4
  export const STATUS_COLORS = {
5
- running: 'var(--color-success)',
6
- starting: 'var(--color-warning)',
5
+ running: 'var(--color-accent)',
6
+ starting: 'var(--color-text-3)',
7
7
  stopped: 'var(--color-text-3)',
8
8
  crashed: 'var(--color-danger)',
9
9
  completed: 'var(--color-accent)',
10
10
  killed: 'var(--color-text-3)',
11
- rotating: 'var(--color-purple)',
11
+ rotating: 'var(--color-accent)',
12
12
  };
13
13
 
14
14
  export const STATUS_LABELS = {
@@ -25,24 +25,26 @@ export function statusColor(status) {
25
25
  return STATUS_COLORS[status] || 'var(--color-text-3)';
26
26
  }
27
27
 
28
+ const NEUTRAL_ROLE = { bg: 'rgba(139,146,158,0.08)', text: '#8b929e', border: '#8b929e' };
29
+
28
30
  export const ROLE_COLORS = {
29
- planner: { bg: 'rgba(74, 225, 104, 0.12)', text: '#4ae168', border: '#4ae168' },
30
- backend: { bg: 'rgba(51, 175, 188, 0.12)', text: '#33afbc', border: '#33afbc' },
31
- frontend: { bg: 'rgba(229, 192, 123, 0.12)', text: '#e5c07b', border: '#e5c07b' },
32
- fullstack: { bg: 'rgba(74, 225, 104, 0.12)', text: '#4ae168', border: '#4ae168' },
33
- testing: { bg: 'rgba(97, 175, 239, 0.12)', text: '#61afef', border: '#61afef' },
34
- devops: { bg: 'rgba(209, 154, 102, 0.12)', text: '#d19a66', border: '#d19a66' },
35
- docs: { bg: 'rgba(139, 146, 158, 0.12)', text: '#8b929e', border: '#8b929e' },
36
- security: { bg: 'rgba(224, 108, 117, 0.12)', text: '#e06c75', border: '#e06c75' },
37
- database: { bg: 'rgba(198, 120, 221, 0.12)', text: '#c678dd', border: '#c678dd' },
38
- cmo: { bg: 'rgba(229, 192, 123, 0.12)', text: '#e5c07b', border: '#e5c07b' },
39
- cfo: { bg: 'rgba(74, 225, 104, 0.12)', text: '#4ae168', border: '#4ae168' },
40
- ea: { bg: 'rgba(97, 175, 239, 0.12)', text: '#61afef', border: '#61afef' },
41
- support: { bg: 'rgba(51, 175, 188, 0.12)', text: '#33afbc', border: '#33afbc' },
42
- analyst: { bg: 'rgba(198, 120, 221, 0.12)', text: '#c678dd', border: '#c678dd' },
43
- creative: { bg: 'rgba(229, 192, 123, 0.12)', text: '#e5c07b', border: '#e5c07b' },
44
- slides: { bg: 'rgba(209, 154, 102, 0.12)', text: '#d19a66', border: '#d19a66' },
45
- chat: { bg: 'rgba(198, 120, 221, 0.12)', text: '#c678dd', border: '#c678dd' },
31
+ planner: NEUTRAL_ROLE,
32
+ backend: NEUTRAL_ROLE,
33
+ frontend: NEUTRAL_ROLE,
34
+ fullstack: NEUTRAL_ROLE,
35
+ testing: NEUTRAL_ROLE,
36
+ devops: NEUTRAL_ROLE,
37
+ docs: NEUTRAL_ROLE,
38
+ security: NEUTRAL_ROLE,
39
+ database: NEUTRAL_ROLE,
40
+ cmo: NEUTRAL_ROLE,
41
+ cfo: NEUTRAL_ROLE,
42
+ ea: NEUTRAL_ROLE,
43
+ support: NEUTRAL_ROLE,
44
+ analyst: NEUTRAL_ROLE,
45
+ creative: NEUTRAL_ROLE,
46
+ slides: NEUTRAL_ROLE,
47
+ chat: NEUTRAL_ROLE,
46
48
  };
47
49
 
48
50
  export function roleColor(role) {
@@ -50,8 +52,6 @@ export function roleColor(role) {
50
52
  return ROLE_COLORS[lower] || { bg: 'rgba(139, 146, 158, 0.12)', text: '#8b929e', border: '#8b929e' };
51
53
  }
52
54
 
53
- export function contextColor(pct) {
54
- if (pct >= 80) return 'var(--color-danger)';
55
- if (pct >= 60) return 'var(--color-warning)';
56
- return 'var(--color-success)';
55
+ export function contextColor() {
56
+ return 'var(--color-accent)';
57
57
  }
@@ -30,6 +30,7 @@ export const HEX = {
30
30
 
31
31
  // Semantic
32
32
  accent: '#33afbc',
33
+ accentMuted: '#33afbc99',
33
34
  success: '#4ae168',
34
35
  warning: '#e5c07b',
35
36
  danger: '#e06c75',