apteva 0.4.41 → 0.4.44
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/ActivityPage.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/{App.7fb3e7mp.js → App.qzbx5wtj.js} +1 -1
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -8
- package/src/db.ts +19 -9
- package/src/integrations/agentdojo.ts +1 -0
- package/src/mcp-platform.ts +418 -63
- package/src/openapi.ts +96 -0
- package/src/providers.ts +50 -24
- package/src/routes/api/agent-utils.ts +0 -1
- package/src/routes/api/agents.ts +19 -1
- package/src/routes/api/meta-agent.ts +2 -0
- package/src/routes/api/system.ts +90 -1
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +12 -0
- package/src/web/App.tsx +89 -11
- package/src/web/components/activity/ActivityPage.tsx +14 -14
- package/src/web/components/agents/AgentCard.tsx +14 -14
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +9 -9
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +32 -32
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +34 -15
- package/src/web/components/mcp/IntegrationsPanel.tsx +40 -40
- package/src/web/components/mcp/McpPage.tsx +208 -208
- package/src/web/components/meta-agent/MetaAgent.tsx +12 -10
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +258 -175
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +339 -54
- package/src/web/components/telemetry/TelemetryPage.tsx +135 -64
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +23 -21
- package/src/web/context/ProjectContext.tsx +6 -1
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +2 -0
- package/src/web/styles.css +5 -3
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +0 -4
- package/dist/ActivityPage.7907h64p.js +0 -3
- package/dist/ApiDocsPage.k3jjenpq.js +0 -4
- package/dist/App.01nq20st.js +0 -4
- package/dist/App.1maqvamf.js +0 -4
- package/dist/App.2yjrh32f.js +0 -4
- package/dist/App.3qw8nben.js +0 -20
- package/dist/App.7sy3wq8c.js +0 -4
- package/dist/App.apjrmctz.js +0 -57
- package/dist/App.av6t2yhe.js +0 -4
- package/dist/App.jqj5a094.js +0 -46
- package/dist/App.mc7xf85h.js +0 -4
- package/dist/App.myxqcj9x.js +0 -4
- package/dist/App.nm91r1mp.js +0 -13
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.qcknavjz.js +0 -221
- package/dist/App.vc7vfhg4.js +0 -4
- package/dist/App.z4s9zkw5.js +0 -4
- package/dist/ConnectionsPage.z1pw5xe2.js +0 -3
- package/dist/McpPage.8vc97z0b.js +0 -3
- package/dist/SettingsPage.p61bz8kd.js +0 -3
- package/dist/SkillsPage.r9x43g3g.js +0 -3
- package/dist/TasksPage.1e0zkye4.js +0 -3
- package/dist/TelemetryPage.p9vbe4gf.js +0 -3
- package/dist/TestsPage.d4xy504e.js +0 -3
- package/dist/ThreadsPage.m016am3x.js +0 -3
|
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react";
|
|
|
2
2
|
import { CheckIcon, CloseIcon, PlusIcon } from "../common/Icons";
|
|
3
3
|
import { Modal, useConfirm } from "../common/Modal";
|
|
4
4
|
import { Select } from "../common/Select";
|
|
5
|
-
import { useProjects, useAuth, type Project } from "../../context";
|
|
5
|
+
import { useProjects, useAuth, useTheme, type Project } from "../../context";
|
|
6
|
+
import type { ThemeMode } from "../../themes";
|
|
6
7
|
import type { Provider } from "../../types";
|
|
7
8
|
|
|
8
9
|
type SettingsTab = "general" | "providers" | "projects" | "channels" | "api-keys" | "account" | "updates" | "data" | "assistant";
|
|
@@ -26,7 +27,7 @@ export function SettingsPage() {
|
|
|
26
27
|
return (
|
|
27
28
|
<div className="flex-1 flex flex-col md:flex-row overflow-hidden">
|
|
28
29
|
{/* Mobile: Horizontal scrolling tabs */}
|
|
29
|
-
<div className="md:hidden border-b border-[
|
|
30
|
+
<div className="md:hidden border-b border-[var(--color-border)] bg-[var(--color-bg)]">
|
|
30
31
|
<div className="flex overflow-x-auto" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
|
|
31
32
|
{tabs.map(tab => (
|
|
32
33
|
<button
|
|
@@ -34,8 +35,8 @@ export function SettingsPage() {
|
|
|
34
35
|
onClick={() => setActiveTab(tab.key)}
|
|
35
36
|
className={`flex-shrink-0 px-4 py-3 text-sm font-medium border-b-2 transition ${
|
|
36
37
|
activeTab === tab.key
|
|
37
|
-
? "border-[
|
|
38
|
-
: "border-transparent text-[
|
|
38
|
+
? "border-[var(--color-accent)] text-[var(--color-accent)]"
|
|
39
|
+
: "border-transparent text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
|
|
39
40
|
}`}
|
|
40
41
|
>
|
|
41
42
|
{tab.label}
|
|
@@ -45,8 +46,8 @@ export function SettingsPage() {
|
|
|
45
46
|
</div>
|
|
46
47
|
|
|
47
48
|
{/* Desktop: Settings Sidebar */}
|
|
48
|
-
<div className="hidden md:block w-48 border-r border-[
|
|
49
|
-
<h2 className="text-sm font-medium text-[
|
|
49
|
+
<div className="hidden md:block w-48 border-r border-[var(--color-border)] p-4 flex-shrink-0">
|
|
50
|
+
<h2 className="text-sm font-medium text-[var(--color-text-muted)] uppercase tracking-wider mb-3">Settings</h2>
|
|
50
51
|
<nav className="space-y-1">
|
|
51
52
|
{tabs.map(tab => (
|
|
52
53
|
<SettingsNavItem
|
|
@@ -89,8 +90,8 @@ function SettingsNavItem({
|
|
|
89
90
|
onClick={onClick}
|
|
90
91
|
className={`w-full text-left px-3 py-2 rounded text-sm transition ${
|
|
91
92
|
active
|
|
92
|
-
? "bg-[
|
|
93
|
-
: "text-[
|
|
93
|
+
? "bg-[var(--color-surface-raised)] text-[var(--color-text)]"
|
|
94
|
+
: "text-[var(--color-text-muted)] hover:bg-[var(--color-surface)] hover:text-[var(--color-text-secondary)]"
|
|
94
95
|
}`}
|
|
95
96
|
>
|
|
96
97
|
{label}
|
|
@@ -100,6 +101,7 @@ function SettingsNavItem({
|
|
|
100
101
|
|
|
101
102
|
function GeneralSettings() {
|
|
102
103
|
const { authFetch } = useAuth();
|
|
104
|
+
const { mode, setMode } = useTheme();
|
|
103
105
|
const [instanceUrl, setInstanceUrl] = useState("");
|
|
104
106
|
const [loading, setLoading] = useState(true);
|
|
105
107
|
const [saving, setSaving] = useState(false);
|
|
@@ -141,21 +143,58 @@ function GeneralSettings() {
|
|
|
141
143
|
setSaving(false);
|
|
142
144
|
};
|
|
143
145
|
|
|
146
|
+
const themeOptions: { value: ThemeMode; label: string; description: string }[] = [
|
|
147
|
+
{ value: "auto", label: "Auto", description: "Follow system preference" },
|
|
148
|
+
{ value: "dark", label: "Dark", description: "Dark background" },
|
|
149
|
+
{ value: "light", label: "Light", description: "Light background" },
|
|
150
|
+
];
|
|
151
|
+
|
|
144
152
|
return (
|
|
145
153
|
<div className="max-w-4xl w-full">
|
|
146
154
|
<div className="mb-6">
|
|
147
155
|
<h1 className="text-2xl font-semibold mb-1">General</h1>
|
|
148
|
-
<p className="text-[
|
|
156
|
+
<p className="text-[var(--color-text-muted)]">Instance configuration and appearance.</p>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
{/* Theme */}
|
|
160
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-4">
|
|
161
|
+
<h3 className="font-medium mb-2">Theme</h3>
|
|
162
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-4">
|
|
163
|
+
Choose your preferred color scheme. Auto follows your operating system setting.
|
|
164
|
+
</p>
|
|
165
|
+
<div className="flex gap-3">
|
|
166
|
+
{themeOptions.map(opt => (
|
|
167
|
+
<button
|
|
168
|
+
key={opt.value}
|
|
169
|
+
onClick={() => setMode(opt.value)}
|
|
170
|
+
className={`flex-1 max-w-[160px] px-4 py-3 rounded-lg border text-left transition ${
|
|
171
|
+
mode === opt.value
|
|
172
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)]"
|
|
173
|
+
: "border-[var(--color-border-light)] bg-[var(--color-bg)] hover:border-[var(--color-scrollbar)]"
|
|
174
|
+
}`}
|
|
175
|
+
>
|
|
176
|
+
<div className="flex items-center gap-2 mb-1">
|
|
177
|
+
<div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${
|
|
178
|
+
mode === opt.value ? "border-[var(--color-accent)]" : "border-[var(--color-scrollbar)]"
|
|
179
|
+
}`}>
|
|
180
|
+
{mode === opt.value && <div className="w-2 h-2 rounded-full bg-[var(--color-accent)]" />}
|
|
181
|
+
</div>
|
|
182
|
+
<span className="text-sm font-medium">{opt.label}</span>
|
|
183
|
+
</div>
|
|
184
|
+
<p className="text-xs text-[var(--color-text-muted)] ml-6">{opt.description}</p>
|
|
185
|
+
</button>
|
|
186
|
+
))}
|
|
187
|
+
</div>
|
|
149
188
|
</div>
|
|
150
189
|
|
|
151
|
-
<div className="bg-[
|
|
190
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
152
191
|
<h3 className="font-medium mb-2">Instance URL</h3>
|
|
153
|
-
<p className="text-sm text-[
|
|
192
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-4">
|
|
154
193
|
The public HTTPS URL for this instance. Used for webhook callbacks from external services like Composio.
|
|
155
194
|
</p>
|
|
156
195
|
|
|
157
196
|
{loading ? (
|
|
158
|
-
<div className="text-[
|
|
197
|
+
<div className="text-[var(--color-text-muted)] text-sm">Loading...</div>
|
|
159
198
|
) : (
|
|
160
199
|
<div className="space-y-3 max-w-lg">
|
|
161
200
|
<input
|
|
@@ -163,7 +202,7 @@ function GeneralSettings() {
|
|
|
163
202
|
value={instanceUrl}
|
|
164
203
|
onChange={e => setInstanceUrl(e.target.value)}
|
|
165
204
|
placeholder="https://your-domain.com"
|
|
166
|
-
className="w-full bg-[
|
|
205
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)] font-mono text-sm"
|
|
167
206
|
/>
|
|
168
207
|
|
|
169
208
|
{message && (
|
|
@@ -179,7 +218,7 @@ function GeneralSettings() {
|
|
|
179
218
|
<button
|
|
180
219
|
onClick={handleSave}
|
|
181
220
|
disabled={saving}
|
|
182
|
-
className="px-4 py-2 bg-[
|
|
221
|
+
className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black rounded text-sm font-medium transition"
|
|
183
222
|
>
|
|
184
223
|
{saving ? "Saving..." : "Save"}
|
|
185
224
|
</button>
|
|
@@ -320,7 +359,7 @@ function ProvidersSettings() {
|
|
|
320
359
|
<div>
|
|
321
360
|
<div className="mb-6">
|
|
322
361
|
<h1 className="text-2xl font-semibold mb-1">AI Providers</h1>
|
|
323
|
-
<p className="text-[
|
|
362
|
+
<p className="text-[var(--color-text-muted)]">
|
|
324
363
|
Manage your API keys for AI providers. {llmConfiguredCount} of {llmProviders.length} configured.
|
|
325
364
|
</p>
|
|
326
365
|
</div>
|
|
@@ -358,7 +397,7 @@ function ProvidersSettings() {
|
|
|
358
397
|
<div>
|
|
359
398
|
<div className="mb-6">
|
|
360
399
|
<h2 className="text-xl font-semibold mb-1">MCP Integrations</h2>
|
|
361
|
-
<p className="text-[
|
|
400
|
+
<p className="text-[var(--color-text-muted)]">
|
|
362
401
|
Connect to MCP gateways for tool integrations. {intConfiguredCount} of {integrations.length} configured.
|
|
363
402
|
</p>
|
|
364
403
|
</div>
|
|
@@ -399,7 +438,7 @@ function ProvidersSettings() {
|
|
|
399
438
|
<div>
|
|
400
439
|
<div className="mb-6">
|
|
401
440
|
<h2 className="text-xl font-semibold mb-1">Browser Providers</h2>
|
|
402
|
-
<p className="text-[
|
|
441
|
+
<p className="text-[var(--color-text-muted)]">
|
|
403
442
|
Configure browser environments for operator mode (computer use). {browserConfiguredCount} of {browserProviders.length} configured.
|
|
404
443
|
</p>
|
|
405
444
|
</div>
|
|
@@ -485,13 +524,13 @@ function ProjectsSettings() {
|
|
|
485
524
|
<div className="mb-6 flex items-center justify-between gap-4">
|
|
486
525
|
<div>
|
|
487
526
|
<h1 className="text-2xl font-semibold mb-1">Projects</h1>
|
|
488
|
-
<p className="text-[
|
|
527
|
+
<p className="text-[var(--color-text-muted)]">
|
|
489
528
|
Organize agents into projects for better management.
|
|
490
529
|
</p>
|
|
491
530
|
</div>
|
|
492
531
|
<button
|
|
493
532
|
onClick={openCreate}
|
|
494
|
-
className="flex items-center gap-2 bg-[
|
|
533
|
+
className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
|
|
495
534
|
>
|
|
496
535
|
<PlusIcon className="w-4 h-4" />
|
|
497
536
|
New Project
|
|
@@ -500,7 +539,7 @@ function ProjectsSettings() {
|
|
|
500
539
|
|
|
501
540
|
{/* Project List */}
|
|
502
541
|
{projects.length === 0 ? (
|
|
503
|
-
<div className="text-center py-12 text-[
|
|
542
|
+
<div className="text-center py-12 text-[var(--color-text-muted)]">
|
|
504
543
|
<p className="text-lg mb-2">No projects yet</p>
|
|
505
544
|
<p className="text-sm">Create a project to organize your agents.</p>
|
|
506
545
|
</div>
|
|
@@ -509,7 +548,7 @@ function ProjectsSettings() {
|
|
|
509
548
|
{projects.map(project => (
|
|
510
549
|
<div
|
|
511
550
|
key={project.id}
|
|
512
|
-
className="bg-[
|
|
551
|
+
className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 flex items-center gap-4"
|
|
513
552
|
>
|
|
514
553
|
<div
|
|
515
554
|
className="w-4 h-4 rounded-full flex-shrink-0"
|
|
@@ -518,16 +557,16 @@ function ProjectsSettings() {
|
|
|
518
557
|
<div className="flex-1 min-w-0">
|
|
519
558
|
<h3 className="font-medium">{project.name}</h3>
|
|
520
559
|
{project.description && (
|
|
521
|
-
<p className="text-sm text-[
|
|
560
|
+
<p className="text-sm text-[var(--color-text-muted)] truncate">{project.description}</p>
|
|
522
561
|
)}
|
|
523
|
-
<p className="text-xs text-[
|
|
562
|
+
<p className="text-xs text-[var(--color-text-muted)] mt-1">
|
|
524
563
|
{project.agentCount} agent{project.agentCount !== 1 ? "s" : ""}
|
|
525
564
|
</p>
|
|
526
565
|
</div>
|
|
527
566
|
<div className="flex items-center gap-2">
|
|
528
567
|
<button
|
|
529
568
|
onClick={() => openEdit(project)}
|
|
530
|
-
className="text-sm text-[
|
|
569
|
+
className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] px-2 py-1"
|
|
531
570
|
>
|
|
532
571
|
Edit
|
|
533
572
|
</button>
|
|
@@ -601,30 +640,30 @@ function ProjectModal({ project, onSave, onClose }: ProjectModalProps) {
|
|
|
601
640
|
|
|
602
641
|
<div className="space-y-4">
|
|
603
642
|
<div>
|
|
604
|
-
<label className="block text-sm text-[
|
|
643
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Name</label>
|
|
605
644
|
<input
|
|
606
645
|
type="text"
|
|
607
646
|
value={name}
|
|
608
647
|
onChange={e => setName(e.target.value)}
|
|
609
|
-
className="w-full bg-[
|
|
648
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
610
649
|
placeholder="My Project"
|
|
611
650
|
autoFocus
|
|
612
651
|
/>
|
|
613
652
|
</div>
|
|
614
653
|
|
|
615
654
|
<div>
|
|
616
|
-
<label className="block text-sm text-[
|
|
655
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Description (optional)</label>
|
|
617
656
|
<input
|
|
618
657
|
type="text"
|
|
619
658
|
value={description}
|
|
620
659
|
onChange={e => setDescription(e.target.value)}
|
|
621
|
-
className="w-full bg-[
|
|
660
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
622
661
|
placeholder="A short description"
|
|
623
662
|
/>
|
|
624
663
|
</div>
|
|
625
664
|
|
|
626
665
|
<div>
|
|
627
|
-
<label className="block text-sm text-[
|
|
666
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Color</label>
|
|
628
667
|
<div className="flex gap-3 flex-wrap">
|
|
629
668
|
{DEFAULT_PROJECT_COLORS.map(c => (
|
|
630
669
|
<button
|
|
@@ -646,14 +685,14 @@ function ProjectModal({ project, onSave, onClose }: ProjectModalProps) {
|
|
|
646
685
|
<div className="flex gap-3 mt-6">
|
|
647
686
|
<button
|
|
648
687
|
onClick={onClose}
|
|
649
|
-
className="flex-1 border border-[
|
|
688
|
+
className="flex-1 border border-[var(--color-border-light)] hover:border-[var(--color-accent)] hover:text-[var(--color-accent)] px-4 py-2 rounded font-medium transition"
|
|
650
689
|
>
|
|
651
690
|
Cancel
|
|
652
691
|
</button>
|
|
653
692
|
<button
|
|
654
693
|
onClick={handleSubmit}
|
|
655
694
|
disabled={saving || !name.trim()}
|
|
656
|
-
className="flex-1 bg-[
|
|
695
|
+
className="flex-1 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black px-4 py-2 rounded font-medium transition"
|
|
657
696
|
>
|
|
658
697
|
{saving ? "Saving..." : project ? "Update" : "Create"}
|
|
659
698
|
</button>
|
|
@@ -754,13 +793,13 @@ function UpdatesSettings() {
|
|
|
754
793
|
<div className="max-w-4xl w-full">
|
|
755
794
|
<div className="mb-6">
|
|
756
795
|
<h1 className="text-2xl font-semibold mb-1">Updates</h1>
|
|
757
|
-
<p className="text-[
|
|
796
|
+
<p className="text-[var(--color-text-muted)]">
|
|
758
797
|
Check for new versions of apteva and the agent binary.
|
|
759
798
|
</p>
|
|
760
799
|
</div>
|
|
761
800
|
|
|
762
801
|
{checking && !versions ? (
|
|
763
|
-
<div className="text-[
|
|
802
|
+
<div className="text-[var(--color-text-muted)]">Checking version info...</div>
|
|
764
803
|
) : error && !versions ? (
|
|
765
804
|
<div className="text-red-400">{error}</div>
|
|
766
805
|
) : versions?.isDocker ? (
|
|
@@ -773,17 +812,17 @@ function UpdatesSettings() {
|
|
|
773
812
|
</svg>
|
|
774
813
|
<span className="font-medium">Docker Environment</span>
|
|
775
814
|
</div>
|
|
776
|
-
<p className="text-sm text-[
|
|
815
|
+
<p className="text-sm text-[var(--color-text-secondary)]">
|
|
777
816
|
Updates are automatic when you pull a new image version.
|
|
778
817
|
</p>
|
|
779
818
|
</div>
|
|
780
819
|
|
|
781
820
|
{/* Current Version */}
|
|
782
|
-
<div className="bg-[
|
|
821
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
|
|
783
822
|
<div className="flex items-center justify-between mb-4">
|
|
784
823
|
<div>
|
|
785
824
|
<h3 className="font-medium text-lg">Current Version</h3>
|
|
786
|
-
<p className="text-sm text-[
|
|
825
|
+
<p className="text-sm text-[var(--color-text-muted)]">apteva + agent binary</p>
|
|
787
826
|
</div>
|
|
788
827
|
<div className="text-right">
|
|
789
828
|
<div className="text-xl font-mono">v{versions.apteva.installed || "?"}</div>
|
|
@@ -791,15 +830,15 @@ function UpdatesSettings() {
|
|
|
791
830
|
</div>
|
|
792
831
|
|
|
793
832
|
{hasAnyUpdate ? (
|
|
794
|
-
<div className="bg-[
|
|
795
|
-
<p className="text-sm text-[
|
|
833
|
+
<div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
|
|
834
|
+
<p className="text-sm text-[var(--color-text-secondary)] mb-3">
|
|
796
835
|
A newer version (v{versions.apteva.latest}) is available. To update:
|
|
797
836
|
</p>
|
|
798
837
|
<div className="space-y-2">
|
|
799
|
-
<code className="block bg-[
|
|
838
|
+
<code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
|
|
800
839
|
docker pull apteva/apteva:latest
|
|
801
840
|
</code>
|
|
802
|
-
<code className="block bg-[
|
|
841
|
+
<code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
|
|
803
842
|
docker compose up -d
|
|
804
843
|
</code>
|
|
805
844
|
</div>
|
|
@@ -809,7 +848,7 @@ function UpdatesSettings() {
|
|
|
809
848
|
setCopied("docker");
|
|
810
849
|
setTimeout(() => setCopied(null), 2000);
|
|
811
850
|
}}
|
|
812
|
-
className="mt-3 px-3 py-1.5 bg-[
|
|
851
|
+
className="mt-3 px-3 py-1.5 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm"
|
|
813
852
|
>
|
|
814
853
|
{copied === "docker" ? "Copied!" : "Copy commands"}
|
|
815
854
|
</button>
|
|
@@ -822,7 +861,7 @@ function UpdatesSettings() {
|
|
|
822
861
|
)}
|
|
823
862
|
</div>
|
|
824
863
|
|
|
825
|
-
<p className="text-xs text-[
|
|
864
|
+
<p className="text-xs text-[var(--color-text-faint)]">
|
|
826
865
|
Your data is stored in a Docker volume and persists across updates.
|
|
827
866
|
</p>
|
|
828
867
|
</div>
|
|
@@ -842,32 +881,32 @@ function UpdatesSettings() {
|
|
|
842
881
|
)}
|
|
843
882
|
|
|
844
883
|
{/* Apteva App Version */}
|
|
845
|
-
<div className="bg-[
|
|
884
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
|
|
846
885
|
<div className="flex items-center justify-between mb-4">
|
|
847
886
|
<div>
|
|
848
887
|
<h3 className="font-medium text-lg">apteva</h3>
|
|
849
|
-
<p className="text-sm text-[
|
|
888
|
+
<p className="text-sm text-[var(--color-text-muted)]">The app you're running</p>
|
|
850
889
|
</div>
|
|
851
890
|
<div className="text-right">
|
|
852
891
|
<div className="text-xl font-mono">v{versions.apteva.installed || "?"}</div>
|
|
853
892
|
{versions.apteva.updateAvailable && (
|
|
854
|
-
<div className="text-sm text-[
|
|
893
|
+
<div className="text-sm text-[var(--color-accent)]">→ v{versions.apteva.latest}</div>
|
|
855
894
|
)}
|
|
856
895
|
</div>
|
|
857
896
|
</div>
|
|
858
897
|
|
|
859
898
|
{versions.apteva.updateAvailable ? (
|
|
860
|
-
<div className="bg-[
|
|
861
|
-
<p className="text-sm text-[
|
|
899
|
+
<div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
|
|
900
|
+
<p className="text-sm text-[var(--color-text-secondary)] mb-3">
|
|
862
901
|
Update by running:
|
|
863
902
|
</p>
|
|
864
903
|
<div className="flex items-center gap-2">
|
|
865
|
-
<code className="flex-1 bg-[
|
|
904
|
+
<code className="flex-1 bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text-secondary)]">
|
|
866
905
|
npx apteva@latest
|
|
867
906
|
</code>
|
|
868
907
|
<button
|
|
869
908
|
onClick={() => copyCommand("npx apteva@latest", "apteva")}
|
|
870
|
-
className="px-3 py-2 bg-[
|
|
909
|
+
className="px-3 py-2 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm"
|
|
871
910
|
>
|
|
872
911
|
{copied === "apteva" ? "Copied!" : "Copy"}
|
|
873
912
|
</button>
|
|
@@ -882,30 +921,30 @@ function UpdatesSettings() {
|
|
|
882
921
|
</div>
|
|
883
922
|
|
|
884
923
|
{/* Agent Binary Version */}
|
|
885
|
-
<div className="bg-[
|
|
924
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-5">
|
|
886
925
|
<div className="flex items-center justify-between mb-4">
|
|
887
926
|
<div>
|
|
888
927
|
<h3 className="font-medium text-lg">Agent Binary</h3>
|
|
889
|
-
<p className="text-sm text-[
|
|
928
|
+
<p className="text-sm text-[var(--color-text-muted)]">The Go binary that runs agents</p>
|
|
890
929
|
</div>
|
|
891
930
|
<div className="text-right">
|
|
892
931
|
<div className="text-xl font-mono">v{versions.agent.installed || "?"}</div>
|
|
893
932
|
{versions.agent.updateAvailable && (
|
|
894
|
-
<div className="text-sm text-[
|
|
933
|
+
<div className="text-sm text-[var(--color-accent)]">→ v{versions.agent.latest}</div>
|
|
895
934
|
)}
|
|
896
935
|
</div>
|
|
897
936
|
</div>
|
|
898
937
|
|
|
899
938
|
{versions.agent.updateAvailable ? (
|
|
900
|
-
<div className="bg-[
|
|
901
|
-
<p className="text-sm text-[
|
|
939
|
+
<div className="bg-[var(--color-accent-10)] border border-[var(--color-accent-30)] rounded-lg p-4">
|
|
940
|
+
<p className="text-sm text-[var(--color-text-secondary)] mb-3">
|
|
902
941
|
A new version is available. Stop all agents before updating.
|
|
903
942
|
</p>
|
|
904
943
|
<div className="flex items-center gap-2">
|
|
905
944
|
<button
|
|
906
945
|
onClick={updateAgent}
|
|
907
946
|
disabled={updatingAgent}
|
|
908
|
-
className="px-4 py-2 bg-[
|
|
947
|
+
className="px-4 py-2 bg-[var(--color-accent)] text-black rounded font-medium text-sm disabled:opacity-50"
|
|
909
948
|
>
|
|
910
949
|
{updatingAgent ? "Updating..." : "Update Agent"}
|
|
911
950
|
</button>
|
|
@@ -929,7 +968,7 @@ function UpdatesSettings() {
|
|
|
929
968
|
<button
|
|
930
969
|
onClick={checkForUpdates}
|
|
931
970
|
disabled={checking}
|
|
932
|
-
className="text-sm text-[
|
|
971
|
+
className="text-sm text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] disabled:opacity-50"
|
|
933
972
|
>
|
|
934
973
|
{checking ? "Checking..." : "Check for updates"}
|
|
935
974
|
</button>
|
|
@@ -999,13 +1038,13 @@ function ProviderKeyCard({
|
|
|
999
1038
|
};
|
|
1000
1039
|
|
|
1001
1040
|
return (
|
|
1002
|
-
<div className={`bg-[
|
|
1003
|
-
provider.hasKey ? 'border-green-500/20' : 'border-[
|
|
1041
|
+
<div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
|
|
1042
|
+
provider.hasKey ? 'border-green-500/20' : 'border-[var(--color-border)]'
|
|
1004
1043
|
}`}>
|
|
1005
1044
|
<div className="flex items-start justify-between gap-2 mb-2">
|
|
1006
1045
|
<div className="min-w-0">
|
|
1007
1046
|
<h3 className="font-medium">{provider.name}</h3>
|
|
1008
|
-
<p className="text-sm text-[
|
|
1047
|
+
<p className="text-sm text-[var(--color-text-muted)] truncate">
|
|
1009
1048
|
{isBrowser
|
|
1010
1049
|
? (provider.description || "Browser automation")
|
|
1011
1050
|
: provider.type === "integration"
|
|
@@ -1036,36 +1075,36 @@ function ProviderKeyCard({
|
|
|
1036
1075
|
)}
|
|
1037
1076
|
</span>
|
|
1038
1077
|
) : (
|
|
1039
|
-
<span className="text-[
|
|
1078
|
+
<span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded whitespace-nowrap flex-shrink-0">
|
|
1040
1079
|
Not configured
|
|
1041
1080
|
</span>
|
|
1042
1081
|
)}
|
|
1043
1082
|
</div>
|
|
1044
1083
|
|
|
1045
|
-
<div className="mt-3 pt-3 border-t border-[
|
|
1084
|
+
<div className="mt-3 pt-3 border-t border-[var(--color-border)]">
|
|
1046
1085
|
{isEditing ? (
|
|
1047
1086
|
<div className="space-y-3">
|
|
1048
1087
|
{isMultiField ? (
|
|
1049
1088
|
<>
|
|
1050
1089
|
<div>
|
|
1051
|
-
<label className="block text-xs text-[
|
|
1090
|
+
<label className="block text-xs text-[var(--color-text-secondary)] mb-1">API Key</label>
|
|
1052
1091
|
<input
|
|
1053
1092
|
type="password"
|
|
1054
1093
|
value={apiKey}
|
|
1055
1094
|
onChange={e => onApiKeyChange(e.target.value)}
|
|
1056
1095
|
placeholder={provider.hasKey ? "Enter new API key..." : "Enter API key..."}
|
|
1057
1096
|
autoFocus
|
|
1058
|
-
className="w-full bg-[
|
|
1097
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1059
1098
|
/>
|
|
1060
1099
|
</div>
|
|
1061
1100
|
<div>
|
|
1062
|
-
<label className="block text-xs text-[
|
|
1101
|
+
<label className="block text-xs text-[var(--color-text-secondary)] mb-1">Project ID</label>
|
|
1063
1102
|
<input
|
|
1064
1103
|
type="text"
|
|
1065
1104
|
value={extraField || ""}
|
|
1066
1105
|
onChange={e => onExtraFieldChange?.(e.target.value)}
|
|
1067
1106
|
placeholder="Enter your Browserbase project ID..."
|
|
1068
|
-
className="w-full bg-[
|
|
1107
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1069
1108
|
/>
|
|
1070
1109
|
</div>
|
|
1071
1110
|
</>
|
|
@@ -1079,11 +1118,11 @@ function ProviderKeyCard({
|
|
|
1079
1118
|
: isCDP ? "ws://localhost:9222"
|
|
1080
1119
|
: provider.hasKey ? "Enter new API key..." : "Enter API key..."}
|
|
1081
1120
|
autoFocus
|
|
1082
|
-
className="w-full bg-[
|
|
1121
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1083
1122
|
/>
|
|
1084
1123
|
)}
|
|
1085
1124
|
{isUrlBased && (
|
|
1086
|
-
<p className="text-xs text-[
|
|
1125
|
+
<p className="text-xs text-[var(--color-text-muted)]">
|
|
1087
1126
|
{isCDP
|
|
1088
1127
|
? "Enter the CDP URL of your browser (e.g., ws://localhost:9222)"
|
|
1089
1128
|
: "Enter your Ollama server URL. Default is http://localhost:11434"}
|
|
@@ -1094,14 +1133,14 @@ function ProviderKeyCard({
|
|
|
1094
1133
|
<div className="flex gap-2">
|
|
1095
1134
|
<button
|
|
1096
1135
|
onClick={onCancelEdit}
|
|
1097
|
-
className="flex-1 px-3 py-1.5 border border-[
|
|
1136
|
+
className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
|
|
1098
1137
|
>
|
|
1099
1138
|
Cancel
|
|
1100
1139
|
</button>
|
|
1101
1140
|
<button
|
|
1102
1141
|
onClick={onSave}
|
|
1103
1142
|
disabled={!apiKey || saving}
|
|
1104
|
-
className="flex-1 px-3 py-1.5 bg-[
|
|
1143
|
+
className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
1105
1144
|
>
|
|
1106
1145
|
{testing ? "Validating..." : saving ? "Saving..." : isUrlBased ? "Connect" : "Save"}
|
|
1107
1146
|
</button>
|
|
@@ -1141,7 +1180,7 @@ function ProviderKeyCard({
|
|
|
1141
1180
|
<div className="flex items-center gap-3">
|
|
1142
1181
|
<button
|
|
1143
1182
|
onClick={onStartEdit}
|
|
1144
|
-
className="text-sm text-[
|
|
1183
|
+
className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
|
|
1145
1184
|
>
|
|
1146
1185
|
{isUrlBased ? "Change URL" : "Update key"}
|
|
1147
1186
|
</button>
|
|
@@ -1187,7 +1226,7 @@ function ProviderKeyCard({
|
|
|
1187
1226
|
)}
|
|
1188
1227
|
<button
|
|
1189
1228
|
onClick={onStartEdit}
|
|
1190
|
-
className="text-sm text-[
|
|
1229
|
+
className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
|
|
1191
1230
|
>
|
|
1192
1231
|
{isUrlBased ? "Configure" : "+ Add key"}
|
|
1193
1232
|
</button>
|
|
@@ -1332,27 +1371,27 @@ function IntegrationKeyCard({
|
|
|
1332
1371
|
// Simple view when projects not enabled
|
|
1333
1372
|
if (!projectsEnabled) {
|
|
1334
1373
|
return (
|
|
1335
|
-
<div className={`bg-[
|
|
1336
|
-
provider.hasKey ? 'border-[
|
|
1374
|
+
<div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
|
|
1375
|
+
provider.hasKey ? 'border-[var(--color-accent-20)]' : 'border-[var(--color-border)]'
|
|
1337
1376
|
}`}>
|
|
1338
1377
|
<div className="flex items-center justify-between mb-2">
|
|
1339
1378
|
<div>
|
|
1340
1379
|
<h3 className="font-medium">{provider.name}</h3>
|
|
1341
|
-
<p className="text-sm text-[
|
|
1380
|
+
<p className="text-sm text-[var(--color-text-muted)]">{provider.description || "MCP integration"}</p>
|
|
1342
1381
|
</div>
|
|
1343
1382
|
{provider.hasKey ? (
|
|
1344
|
-
<span className="text-[
|
|
1383
|
+
<span className="text-[var(--color-accent)] text-xs flex items-center gap-1 bg-[var(--color-accent-10)] px-2 py-1 rounded">
|
|
1345
1384
|
<CheckIcon className="w-3 h-3" />
|
|
1346
1385
|
{provider.keyHint}
|
|
1347
1386
|
</span>
|
|
1348
1387
|
) : (
|
|
1349
|
-
<span className="text-[
|
|
1388
|
+
<span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded">
|
|
1350
1389
|
Not configured
|
|
1351
1390
|
</span>
|
|
1352
1391
|
)}
|
|
1353
1392
|
</div>
|
|
1354
1393
|
|
|
1355
|
-
<div className="mt-3 pt-3 border-t border-[
|
|
1394
|
+
<div className="mt-3 pt-3 border-t border-[var(--color-border)]">
|
|
1356
1395
|
{isEditing ? (
|
|
1357
1396
|
<div className="space-y-3">
|
|
1358
1397
|
<input
|
|
@@ -1361,21 +1400,21 @@ function IntegrationKeyCard({
|
|
|
1361
1400
|
onChange={e => onApiKeyChange(e.target.value)}
|
|
1362
1401
|
placeholder={provider.hasKey ? `Enter new ${isUrlBased ? "URL" : "API key"}...` : inputPlaceholder}
|
|
1363
1402
|
autoFocus
|
|
1364
|
-
className="w-full bg-[
|
|
1403
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1365
1404
|
/>
|
|
1366
1405
|
{error && <p className="text-red-400 text-sm">{error}</p>}
|
|
1367
1406
|
{success && <p className="text-green-400 text-sm">{success}</p>}
|
|
1368
1407
|
<div className="flex gap-2">
|
|
1369
1408
|
<button
|
|
1370
1409
|
onClick={onCancelEdit}
|
|
1371
|
-
className="flex-1 px-3 py-1.5 border border-[
|
|
1410
|
+
className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
|
|
1372
1411
|
>
|
|
1373
1412
|
Cancel
|
|
1374
1413
|
</button>
|
|
1375
1414
|
<button
|
|
1376
1415
|
onClick={onSave}
|
|
1377
1416
|
disabled={!apiKey || saving}
|
|
1378
|
-
className="flex-1 px-3 py-1.5 bg-[
|
|
1417
|
+
className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
1379
1418
|
>
|
|
1380
1419
|
{testing ? "Validating..." : saving ? "Saving..." : "Save"}
|
|
1381
1420
|
</button>
|
|
@@ -1394,7 +1433,7 @@ function IntegrationKeyCard({
|
|
|
1394
1433
|
<div className="flex items-center gap-3">
|
|
1395
1434
|
<button
|
|
1396
1435
|
onClick={onStartEdit}
|
|
1397
|
-
className="text-sm text-[
|
|
1436
|
+
className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
|
|
1398
1437
|
>
|
|
1399
1438
|
Update key
|
|
1400
1439
|
</button>
|
|
@@ -1418,7 +1457,7 @@ function IntegrationKeyCard({
|
|
|
1418
1457
|
</a>
|
|
1419
1458
|
<button
|
|
1420
1459
|
onClick={onStartEdit}
|
|
1421
|
-
className="text-sm text-[
|
|
1460
|
+
className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
|
|
1422
1461
|
>
|
|
1423
1462
|
+ Add key
|
|
1424
1463
|
</button>
|
|
@@ -1440,21 +1479,21 @@ function IntegrationKeyCard({
|
|
|
1440
1479
|
return (
|
|
1441
1480
|
<>
|
|
1442
1481
|
{ConfirmDialog}
|
|
1443
|
-
<div className={`bg-[
|
|
1444
|
-
keys.length > 0 ? 'border-[
|
|
1482
|
+
<div className={`bg-[var(--color-surface)] border rounded-lg p-4 ${
|
|
1483
|
+
keys.length > 0 ? 'border-[var(--color-accent-20)]' : 'border-[var(--color-border)]'
|
|
1445
1484
|
}`}>
|
|
1446
1485
|
<div className="flex items-center justify-between mb-2">
|
|
1447
1486
|
<div>
|
|
1448
1487
|
<h3 className="font-medium">{provider.name}</h3>
|
|
1449
|
-
<p className="text-sm text-[
|
|
1488
|
+
<p className="text-sm text-[var(--color-text-muted)]">{provider.description || "MCP integration"}</p>
|
|
1450
1489
|
</div>
|
|
1451
1490
|
{keys.length > 0 ? (
|
|
1452
|
-
<span className="text-[
|
|
1491
|
+
<span className="text-[var(--color-accent)] text-xs flex items-center gap-1 bg-[var(--color-accent-10)] px-2 py-1 rounded">
|
|
1453
1492
|
<CheckIcon className="w-3 h-3" />
|
|
1454
1493
|
{keys.length} key{keys.length !== 1 ? "s" : ""}
|
|
1455
1494
|
</span>
|
|
1456
1495
|
) : (
|
|
1457
|
-
<span className="text-[
|
|
1496
|
+
<span className="text-[var(--color-text-muted)] text-xs bg-[var(--color-surface-raised)] px-2 py-1 rounded">
|
|
1458
1497
|
Not configured
|
|
1459
1498
|
</span>
|
|
1460
1499
|
)}
|
|
@@ -1465,11 +1504,11 @@ function IntegrationKeyCard({
|
|
|
1465
1504
|
<div className="mt-3 space-y-2">
|
|
1466
1505
|
{/* Global Key */}
|
|
1467
1506
|
{globalKey && (
|
|
1468
|
-
<div className="flex items-center justify-between text-sm bg-[
|
|
1507
|
+
<div className="flex items-center justify-between text-sm bg-[var(--color-bg)] rounded px-3 py-2">
|
|
1469
1508
|
<div className="flex items-center gap-2">
|
|
1470
|
-
<span className="text-[
|
|
1471
|
-
<span className="text-[
|
|
1472
|
-
<span className="text-[
|
|
1509
|
+
<span className="text-[var(--color-text-secondary)]">Global</span>
|
|
1510
|
+
<span className="text-[var(--color-text-faint)]">·</span>
|
|
1511
|
+
<span className="text-[var(--color-text-muted)] font-mono text-xs">{globalKey.key_hint}</span>
|
|
1473
1512
|
</div>
|
|
1474
1513
|
<button
|
|
1475
1514
|
onClick={() => handleDeleteKey(globalKey.id, "Global")}
|
|
@@ -1482,15 +1521,15 @@ function IntegrationKeyCard({
|
|
|
1482
1521
|
|
|
1483
1522
|
{/* Project Keys - show first 2, expand for more */}
|
|
1484
1523
|
{projectKeys.slice(0, expanded ? undefined : 2).map(key => (
|
|
1485
|
-
<div key={key.id} className="flex items-center justify-between text-sm bg-[
|
|
1524
|
+
<div key={key.id} className="flex items-center justify-between text-sm bg-[var(--color-bg)] rounded px-3 py-2">
|
|
1486
1525
|
<div className="flex items-center gap-2 min-w-0">
|
|
1487
1526
|
<span
|
|
1488
1527
|
className="w-2 h-2 rounded-full flex-shrink-0"
|
|
1489
1528
|
style={{ backgroundColor: getProjectColor(key.project_id!) }}
|
|
1490
1529
|
/>
|
|
1491
|
-
<span className="text-[
|
|
1492
|
-
<span className="text-[
|
|
1493
|
-
<span className="text-[
|
|
1530
|
+
<span className="text-[var(--color-text-secondary)] truncate">{key.name || getProjectName(key.project_id!)}</span>
|
|
1531
|
+
<span className="text-[var(--color-text-faint)]">·</span>
|
|
1532
|
+
<span className="text-[var(--color-text-muted)] font-mono text-xs">{key.key_hint}</span>
|
|
1494
1533
|
</div>
|
|
1495
1534
|
<button
|
|
1496
1535
|
onClick={() => handleDeleteKey(key.id, key.name || getProjectName(key.project_id!))}
|
|
@@ -1504,7 +1543,7 @@ function IntegrationKeyCard({
|
|
|
1504
1543
|
{projectKeys.length > 2 && !expanded && (
|
|
1505
1544
|
<button
|
|
1506
1545
|
onClick={() => setExpanded(true)}
|
|
1507
|
-
className="text-xs text-[
|
|
1546
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] w-full text-center py-1"
|
|
1508
1547
|
>
|
|
1509
1548
|
Show {projectKeys.length - 2} more...
|
|
1510
1549
|
</button>
|
|
@@ -1512,7 +1551,7 @@ function IntegrationKeyCard({
|
|
|
1512
1551
|
</div>
|
|
1513
1552
|
)}
|
|
1514
1553
|
|
|
1515
|
-
<div className="mt-3 pt-3 border-t border-[
|
|
1554
|
+
<div className="mt-3 pt-3 border-t border-[var(--color-border)]">
|
|
1516
1555
|
{isEditing ? (
|
|
1517
1556
|
<div className="space-y-3">
|
|
1518
1557
|
<input
|
|
@@ -1521,7 +1560,7 @@ function IntegrationKeyCard({
|
|
|
1521
1560
|
onChange={e => onApiKeyChange(e.target.value)}
|
|
1522
1561
|
placeholder={inputPlaceholder}
|
|
1523
1562
|
autoFocus
|
|
1524
|
-
className="w-full bg-[
|
|
1563
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1525
1564
|
/>
|
|
1526
1565
|
|
|
1527
1566
|
{isBrowserbase && (
|
|
@@ -1530,7 +1569,7 @@ function IntegrationKeyCard({
|
|
|
1530
1569
|
value={bbProjectId}
|
|
1531
1570
|
onChange={e => setBbProjectId(e.target.value)}
|
|
1532
1571
|
placeholder="Browserbase Project ID (optional)"
|
|
1533
|
-
className="w-full bg-[
|
|
1572
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)] text-sm"
|
|
1534
1573
|
/>
|
|
1535
1574
|
)}
|
|
1536
1575
|
|
|
@@ -1553,14 +1592,14 @@ function IntegrationKeyCard({
|
|
|
1553
1592
|
setSelectedProjectId("");
|
|
1554
1593
|
setLocalError(null);
|
|
1555
1594
|
}}
|
|
1556
|
-
className="flex-1 px-3 py-1.5 border border-[
|
|
1595
|
+
className="flex-1 px-3 py-1.5 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
|
|
1557
1596
|
>
|
|
1558
1597
|
Cancel
|
|
1559
1598
|
</button>
|
|
1560
1599
|
<button
|
|
1561
1600
|
onClick={handleSaveWithProject}
|
|
1562
1601
|
disabled={!apiKey || localSaving}
|
|
1563
|
-
className="flex-1 px-3 py-1.5 bg-[
|
|
1602
|
+
className="flex-1 px-3 py-1.5 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
1564
1603
|
>
|
|
1565
1604
|
{localSaving ? "Saving..." : "Save"}
|
|
1566
1605
|
</button>
|
|
@@ -1578,7 +1617,7 @@ function IntegrationKeyCard({
|
|
|
1578
1617
|
</a>
|
|
1579
1618
|
<button
|
|
1580
1619
|
onClick={onStartEdit}
|
|
1581
|
-
className="text-sm text-[
|
|
1620
|
+
className="text-sm text-[var(--color-accent)] hover:text-[var(--color-accent-hover)]"
|
|
1582
1621
|
>
|
|
1583
1622
|
+ Add key
|
|
1584
1623
|
</button>
|
|
@@ -1697,14 +1736,14 @@ function ApiKeysSettings() {
|
|
|
1697
1736
|
<div className="mb-6 flex items-center justify-between gap-4">
|
|
1698
1737
|
<div>
|
|
1699
1738
|
<h1 className="text-2xl font-semibold mb-1">API Keys</h1>
|
|
1700
|
-
<p className="text-[
|
|
1701
|
-
Create personal API keys for programmatic access. Use them with the <code className="text-[
|
|
1739
|
+
<p className="text-[var(--color-text-muted)]">
|
|
1740
|
+
Create personal API keys for programmatic access. Use them with the <code className="text-[var(--color-text-secondary)] bg-[var(--color-surface-raised)] px-1 rounded text-xs">X-API-Key</code> header.
|
|
1702
1741
|
</p>
|
|
1703
1742
|
</div>
|
|
1704
1743
|
{!showCreate && !newKey && (
|
|
1705
1744
|
<button
|
|
1706
1745
|
onClick={() => { setShowCreate(true); setError(null); }}
|
|
1707
|
-
className="flex items-center gap-2 bg-[
|
|
1746
|
+
className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-4 py-2 rounded font-medium transition flex-shrink-0"
|
|
1708
1747
|
>
|
|
1709
1748
|
<PlusIcon className="w-4 h-4" />
|
|
1710
1749
|
New Key
|
|
@@ -1719,23 +1758,23 @@ function ApiKeysSettings() {
|
|
|
1719
1758
|
<CheckIcon className="w-5 h-5" />
|
|
1720
1759
|
<span className="font-medium">API key created</span>
|
|
1721
1760
|
</div>
|
|
1722
|
-
<p className="text-sm text-[
|
|
1761
|
+
<p className="text-sm text-[var(--color-text-secondary)] mb-3">
|
|
1723
1762
|
Copy this key now. You won't be able to see it again.
|
|
1724
1763
|
</p>
|
|
1725
1764
|
<div className="flex items-center gap-2">
|
|
1726
|
-
<code className="flex-1 bg-[
|
|
1765
|
+
<code className="flex-1 bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-sm text-[var(--color-text)] break-all select-all">
|
|
1727
1766
|
{newKey}
|
|
1728
1767
|
</code>
|
|
1729
1768
|
<button
|
|
1730
1769
|
onClick={copyKey}
|
|
1731
|
-
className="px-3 py-2 bg-[
|
|
1770
|
+
className="px-3 py-2 bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] rounded text-sm flex-shrink-0"
|
|
1732
1771
|
>
|
|
1733
1772
|
{copied ? "Copied!" : "Copy"}
|
|
1734
1773
|
</button>
|
|
1735
1774
|
</div>
|
|
1736
1775
|
<button
|
|
1737
1776
|
onClick={() => { setNewKey(null); setShowCreate(false); }}
|
|
1738
|
-
className="mt-3 text-sm text-[
|
|
1777
|
+
className="mt-3 text-sm text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"
|
|
1739
1778
|
>
|
|
1740
1779
|
Done
|
|
1741
1780
|
</button>
|
|
@@ -1744,26 +1783,26 @@ function ApiKeysSettings() {
|
|
|
1744
1783
|
|
|
1745
1784
|
{/* Create Form */}
|
|
1746
1785
|
{showCreate && !newKey && (
|
|
1747
|
-
<div className="bg-[
|
|
1786
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-6">
|
|
1748
1787
|
<h3 className="font-medium mb-4">Create new API key</h3>
|
|
1749
1788
|
<div className="space-y-4 max-w-md">
|
|
1750
1789
|
<div>
|
|
1751
|
-
<label className="block text-sm text-[
|
|
1790
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Name</label>
|
|
1752
1791
|
<input
|
|
1753
1792
|
type="text"
|
|
1754
1793
|
value={name}
|
|
1755
1794
|
onChange={e => setName(e.target.value)}
|
|
1756
1795
|
placeholder="e.g. CI Pipeline, My Script"
|
|
1757
1796
|
autoFocus
|
|
1758
|
-
className="w-full bg-[
|
|
1797
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1759
1798
|
/>
|
|
1760
1799
|
</div>
|
|
1761
1800
|
<div>
|
|
1762
|
-
<label className="block text-sm text-[
|
|
1801
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Expiration</label>
|
|
1763
1802
|
<select
|
|
1764
1803
|
value={expiresInDays}
|
|
1765
1804
|
onChange={e => setExpiresInDays(e.target.value)}
|
|
1766
|
-
className="w-full bg-[
|
|
1805
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1767
1806
|
>
|
|
1768
1807
|
<option value="30">30 days</option>
|
|
1769
1808
|
<option value="90">90 days</option>
|
|
@@ -1778,14 +1817,14 @@ function ApiKeysSettings() {
|
|
|
1778
1817
|
<div className="flex gap-2">
|
|
1779
1818
|
<button
|
|
1780
1819
|
onClick={() => { setShowCreate(false); setError(null); setName(""); }}
|
|
1781
|
-
className="flex-1 px-3 py-2 border border-[
|
|
1820
|
+
className="flex-1 px-3 py-2 border border-[var(--color-border-light)] rounded text-sm hover:border-[var(--color-text-muted)]"
|
|
1782
1821
|
>
|
|
1783
1822
|
Cancel
|
|
1784
1823
|
</button>
|
|
1785
1824
|
<button
|
|
1786
1825
|
onClick={handleCreate}
|
|
1787
1826
|
disabled={creating || !name.trim()}
|
|
1788
|
-
className="flex-1 px-3 py-2 bg-[
|
|
1827
|
+
className="flex-1 px-3 py-2 bg-[var(--color-accent)] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
1789
1828
|
>
|
|
1790
1829
|
{creating ? "Creating..." : "Create Key"}
|
|
1791
1830
|
</button>
|
|
@@ -1796,7 +1835,7 @@ function ApiKeysSettings() {
|
|
|
1796
1835
|
|
|
1797
1836
|
{/* Keys List */}
|
|
1798
1837
|
{keys.length === 0 ? (
|
|
1799
|
-
<div className="text-center py-12 text-[
|
|
1838
|
+
<div className="text-center py-12 text-[var(--color-text-muted)]">
|
|
1800
1839
|
<p className="text-lg mb-2">No API keys yet</p>
|
|
1801
1840
|
<p className="text-sm">Create an API key to access apteva programmatically.</p>
|
|
1802
1841
|
</div>
|
|
@@ -1805,8 +1844,8 @@ function ApiKeysSettings() {
|
|
|
1805
1844
|
{keys.map(key => (
|
|
1806
1845
|
<div
|
|
1807
1846
|
key={key.id}
|
|
1808
|
-
className={`bg-[
|
|
1809
|
-
!key.is_active || isExpired(key.expires_at) ? "border-[
|
|
1847
|
+
className={`bg-[var(--color-surface)] border rounded-lg p-4 flex items-center gap-4 ${
|
|
1848
|
+
!key.is_active || isExpired(key.expires_at) ? "border-[var(--color-border)] opacity-60" : "border-[var(--color-border)]"
|
|
1810
1849
|
}`}
|
|
1811
1850
|
>
|
|
1812
1851
|
<div className="flex-1 min-w-0">
|
|
@@ -1819,8 +1858,8 @@ function ApiKeysSettings() {
|
|
|
1819
1858
|
<span className="text-xs text-yellow-400 bg-yellow-500/10 px-2 py-0.5 rounded">Expired</span>
|
|
1820
1859
|
)}
|
|
1821
1860
|
</div>
|
|
1822
|
-
<div className="flex items-center gap-3 text-sm text-[
|
|
1823
|
-
<code className="font-mono text-xs bg-[
|
|
1861
|
+
<div className="flex items-center gap-3 text-sm text-[var(--color-text-muted)]">
|
|
1862
|
+
<code className="font-mono text-xs bg-[var(--color-bg)] px-2 py-0.5 rounded">{key.prefix}...</code>
|
|
1824
1863
|
<span>Created {formatDate(key.created_at)}</span>
|
|
1825
1864
|
{key.expires_at && <span>Expires {formatDate(key.expires_at)}</span>}
|
|
1826
1865
|
{key.last_used_at && <span>Last used {formatDate(key.last_used_at)}</span>}
|
|
@@ -1841,9 +1880,9 @@ function ApiKeysSettings() {
|
|
|
1841
1880
|
|
|
1842
1881
|
{/* Usage Info */}
|
|
1843
1882
|
{keys.length > 0 && (
|
|
1844
|
-
<div className="mt-6 bg-[
|
|
1883
|
+
<div className="mt-6 bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
1845
1884
|
<h3 className="font-medium mb-2 text-sm">Usage</h3>
|
|
1846
|
-
<code className="block bg-[
|
|
1885
|
+
<code className="block bg-[var(--color-bg)] px-3 py-2 rounded font-mono text-xs text-[var(--color-text-secondary)]">
|
|
1847
1886
|
curl -H "X-API-Key: apt_..." http://localhost:4280/api/agents
|
|
1848
1887
|
</code>
|
|
1849
1888
|
</div>
|
|
@@ -1909,26 +1948,26 @@ function AccountSettings() {
|
|
|
1909
1948
|
<div className="max-w-4xl w-full">
|
|
1910
1949
|
<div className="mb-6">
|
|
1911
1950
|
<h1 className="text-2xl font-semibold mb-1">Account Settings</h1>
|
|
1912
|
-
<p className="text-[
|
|
1951
|
+
<p className="text-[var(--color-text-muted)]">Manage your account and security.</p>
|
|
1913
1952
|
</div>
|
|
1914
1953
|
|
|
1915
1954
|
{/* User Info */}
|
|
1916
1955
|
{user && (
|
|
1917
|
-
<div className="bg-[
|
|
1956
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 mb-6">
|
|
1918
1957
|
<h3 className="font-medium mb-3">Profile</h3>
|
|
1919
1958
|
<div className="space-y-2 text-sm">
|
|
1920
1959
|
<div className="flex justify-between">
|
|
1921
|
-
<span className="text-[
|
|
1960
|
+
<span className="text-[var(--color-text-muted)]">Username</span>
|
|
1922
1961
|
<span>{user.username}</span>
|
|
1923
1962
|
</div>
|
|
1924
1963
|
{user.email && (
|
|
1925
1964
|
<div className="flex justify-between">
|
|
1926
|
-
<span className="text-[
|
|
1965
|
+
<span className="text-[var(--color-text-muted)]">Email</span>
|
|
1927
1966
|
<span>{user.email}</span>
|
|
1928
1967
|
</div>
|
|
1929
1968
|
)}
|
|
1930
1969
|
<div className="flex justify-between">
|
|
1931
|
-
<span className="text-[
|
|
1970
|
+
<span className="text-[var(--color-text-muted)]">Role</span>
|
|
1932
1971
|
<span className="capitalize">{user.role}</span>
|
|
1933
1972
|
</div>
|
|
1934
1973
|
</div>
|
|
@@ -1936,37 +1975,37 @@ function AccountSettings() {
|
|
|
1936
1975
|
)}
|
|
1937
1976
|
|
|
1938
1977
|
{/* Change Password */}
|
|
1939
|
-
<div className="bg-[
|
|
1978
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
1940
1979
|
<h3 className="font-medium mb-4">Change Password</h3>
|
|
1941
1980
|
|
|
1942
1981
|
<div className="space-y-4 max-w-md">
|
|
1943
1982
|
<div>
|
|
1944
|
-
<label className="block text-sm text-[
|
|
1983
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Current Password</label>
|
|
1945
1984
|
<input
|
|
1946
1985
|
type="password"
|
|
1947
1986
|
value={currentPassword}
|
|
1948
1987
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
|
1949
|
-
className="w-full bg-[
|
|
1988
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1950
1989
|
/>
|
|
1951
1990
|
</div>
|
|
1952
1991
|
|
|
1953
1992
|
<div>
|
|
1954
|
-
<label className="block text-sm text-[
|
|
1993
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">New Password</label>
|
|
1955
1994
|
<input
|
|
1956
1995
|
type="password"
|
|
1957
1996
|
value={newPassword}
|
|
1958
1997
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
1959
|
-
className="w-full bg-[
|
|
1998
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1960
1999
|
/>
|
|
1961
2000
|
</div>
|
|
1962
2001
|
|
|
1963
2002
|
<div>
|
|
1964
|
-
<label className="block text-sm text-[
|
|
2003
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Confirm New Password</label>
|
|
1965
2004
|
<input
|
|
1966
2005
|
type="password"
|
|
1967
2006
|
value={confirmPassword}
|
|
1968
2007
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
1969
|
-
className="w-full bg-[
|
|
2008
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
1970
2009
|
/>
|
|
1971
2010
|
</div>
|
|
1972
2011
|
|
|
@@ -1983,7 +2022,7 @@ function AccountSettings() {
|
|
|
1983
2022
|
<button
|
|
1984
2023
|
onClick={handleChangePassword}
|
|
1985
2024
|
disabled={saving || !currentPassword || !newPassword || !confirmPassword}
|
|
1986
|
-
className="px-4 py-2 bg-[
|
|
2025
|
+
className="px-4 py-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black rounded text-sm font-medium transition"
|
|
1987
2026
|
>
|
|
1988
2027
|
{saving ? "Updating..." : "Update Password"}
|
|
1989
2028
|
</button>
|
|
@@ -2044,12 +2083,12 @@ function DataSettings() {
|
|
|
2044
2083
|
<div className="max-w-4xl w-full">
|
|
2045
2084
|
<div className="mb-6">
|
|
2046
2085
|
<h1 className="text-2xl font-semibold mb-1">Data Management</h1>
|
|
2047
|
-
<p className="text-[
|
|
2086
|
+
<p className="text-[var(--color-text-muted)]">Manage stored data and telemetry.</p>
|
|
2048
2087
|
</div>
|
|
2049
2088
|
|
|
2050
|
-
<div className="bg-[
|
|
2089
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
2051
2090
|
<h3 className="font-medium mb-2">Telemetry Data</h3>
|
|
2052
|
-
<p className="text-sm text-[
|
|
2091
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-4">
|
|
2053
2092
|
{eventCount !== null
|
|
2054
2093
|
? `${eventCount.toLocaleString()} events stored`
|
|
2055
2094
|
: "Loading..."}
|
|
@@ -2197,7 +2236,7 @@ function ChannelsSettings() {
|
|
|
2197
2236
|
|
|
2198
2237
|
const statusColors: Record<string, string> = {
|
|
2199
2238
|
running: "bg-green-500/20 text-green-400",
|
|
2200
|
-
stopped: "bg-[
|
|
2239
|
+
stopped: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]",
|
|
2201
2240
|
error: "bg-red-500/20 text-red-400",
|
|
2202
2241
|
};
|
|
2203
2242
|
|
|
@@ -2212,11 +2251,11 @@ function ChannelsSettings() {
|
|
|
2212
2251
|
<div className="flex items-center justify-between mb-6">
|
|
2213
2252
|
<div>
|
|
2214
2253
|
<h2 className="text-xl font-semibold mb-1">Channels</h2>
|
|
2215
|
-
<p className="text-sm text-[
|
|
2254
|
+
<p className="text-sm text-[var(--color-text-muted)]">Connect agents to external messaging platforms</p>
|
|
2216
2255
|
</div>
|
|
2217
2256
|
<button
|
|
2218
2257
|
onClick={() => setShowForm(!showForm)}
|
|
2219
|
-
className="flex items-center gap-2 bg-[
|
|
2258
|
+
className="flex items-center gap-2 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-3 py-1.5 rounded text-sm font-medium transition"
|
|
2220
2259
|
>
|
|
2221
2260
|
<PlusIcon /> Add Channel
|
|
2222
2261
|
</button>
|
|
@@ -2233,22 +2272,22 @@ function ChannelsSettings() {
|
|
|
2233
2272
|
|
|
2234
2273
|
{/* Create form */}
|
|
2235
2274
|
{showForm && (
|
|
2236
|
-
<div className="mb-6 bg-[
|
|
2237
|
-
<h3 className="text-sm font-medium text-[
|
|
2275
|
+
<div className="mb-6 bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 space-y-3">
|
|
2276
|
+
<h3 className="text-sm font-medium text-[var(--color-text-secondary)] mb-2">New Telegram Channel</h3>
|
|
2238
2277
|
|
|
2239
2278
|
<div>
|
|
2240
|
-
<label className="block text-xs text-[
|
|
2279
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Channel Name</label>
|
|
2241
2280
|
<input
|
|
2242
2281
|
type="text"
|
|
2243
2282
|
value={formData.name}
|
|
2244
2283
|
onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
2245
2284
|
placeholder="e.g. My Telegram Bot"
|
|
2246
|
-
className="w-full bg-[
|
|
2285
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--color-accent)]"
|
|
2247
2286
|
/>
|
|
2248
2287
|
</div>
|
|
2249
2288
|
|
|
2250
2289
|
<div>
|
|
2251
|
-
<label className="block text-xs text-[
|
|
2290
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Agent</label>
|
|
2252
2291
|
<Select
|
|
2253
2292
|
value={formData.agent_id}
|
|
2254
2293
|
options={agents.map(a => ({ value: a.id, label: a.name }))}
|
|
@@ -2258,16 +2297,16 @@ function ChannelsSettings() {
|
|
|
2258
2297
|
</div>
|
|
2259
2298
|
|
|
2260
2299
|
<div>
|
|
2261
|
-
<label className="block text-xs text-[
|
|
2300
|
+
<label className="block text-xs text-[var(--color-text-muted)] mb-1">Bot Token</label>
|
|
2262
2301
|
<input
|
|
2263
2302
|
type="password"
|
|
2264
2303
|
value={formData.botToken}
|
|
2265
2304
|
onChange={e => setFormData(prev => ({ ...prev, botToken: e.target.value }))}
|
|
2266
2305
|
placeholder="From @BotFather on Telegram"
|
|
2267
|
-
className="w-full bg-[
|
|
2306
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--color-accent)]"
|
|
2268
2307
|
/>
|
|
2269
|
-
<p className="text-xs text-[
|
|
2270
|
-
Create a bot via <a href="https://t.me/BotFather" target="_blank" className="text-[
|
|
2308
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-1">
|
|
2309
|
+
Create a bot via <a href="https://t.me/BotFather" target="_blank" className="text-[var(--color-accent)] hover:underline">@BotFather</a> on Telegram to get a token.
|
|
2271
2310
|
</p>
|
|
2272
2311
|
</div>
|
|
2273
2312
|
|
|
@@ -2275,13 +2314,13 @@ function ChannelsSettings() {
|
|
|
2275
2314
|
<button
|
|
2276
2315
|
onClick={createChannel}
|
|
2277
2316
|
disabled={creating || !formData.name || !formData.agent_id || !formData.botToken}
|
|
2278
|
-
className="bg-[
|
|
2317
|
+
className="bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-black px-4 py-1.5 rounded text-sm font-medium transition"
|
|
2279
2318
|
>
|
|
2280
2319
|
{creating ? "Creating..." : "Create"}
|
|
2281
2320
|
</button>
|
|
2282
2321
|
<button
|
|
2283
2322
|
onClick={() => { setShowForm(false); setFormData({ name: "", agent_id: "", botToken: "" }); }}
|
|
2284
|
-
className="border border-[
|
|
2323
|
+
className="border border-[var(--color-border-light)] hover:border-[var(--color-scrollbar)] px-4 py-1.5 rounded text-sm transition"
|
|
2285
2324
|
>
|
|
2286
2325
|
Cancel
|
|
2287
2326
|
</button>
|
|
@@ -2291,16 +2330,16 @@ function ChannelsSettings() {
|
|
|
2291
2330
|
|
|
2292
2331
|
{/* Channel list */}
|
|
2293
2332
|
{loading ? (
|
|
2294
|
-
<p className="text-[
|
|
2333
|
+
<p className="text-[var(--color-text-muted)] text-sm">Loading channels...</p>
|
|
2295
2334
|
) : channels.length === 0 ? (
|
|
2296
|
-
<div className="text-center py-12 text-[
|
|
2335
|
+
<div className="text-center py-12 text-[var(--color-text-muted)]">
|
|
2297
2336
|
<p className="text-lg mb-2">No channels configured</p>
|
|
2298
2337
|
<p className="text-sm">Add a Telegram channel to let users message your agents directly.</p>
|
|
2299
2338
|
</div>
|
|
2300
2339
|
) : (
|
|
2301
2340
|
<div className="space-y-3">
|
|
2302
2341
|
{channels.map(channel => (
|
|
2303
|
-
<div key={channel.id} className="bg-[
|
|
2342
|
+
<div key={channel.id} className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4">
|
|
2304
2343
|
<div className="flex items-start justify-between">
|
|
2305
2344
|
<div className="flex-1 min-w-0">
|
|
2306
2345
|
<div className="flex items-center gap-2 mb-1">
|
|
@@ -2309,7 +2348,7 @@ function ChannelsSettings() {
|
|
|
2309
2348
|
{channel.status}
|
|
2310
2349
|
</span>
|
|
2311
2350
|
</div>
|
|
2312
|
-
<p className="text-sm text-[
|
|
2351
|
+
<p className="text-sm text-[var(--color-text-muted)]">
|
|
2313
2352
|
{channel.type === "telegram" ? "Telegram" : channel.type} → {getAgentName(channel.agent_id)}
|
|
2314
2353
|
</p>
|
|
2315
2354
|
{channel.status === "error" && channel.error && (
|
|
@@ -2321,7 +2360,7 @@ function ChannelsSettings() {
|
|
|
2321
2360
|
onClick={() => toggleChannel(channel)}
|
|
2322
2361
|
className={`px-3 py-1 rounded text-xs font-medium transition ${
|
|
2323
2362
|
channel.status === "running"
|
|
2324
|
-
? "bg-[
|
|
2363
|
+
? "bg-[var(--color-accent-20)] text-[var(--color-accent)] hover:bg-[var(--color-accent-30)]"
|
|
2325
2364
|
: "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
|
|
2326
2365
|
}`}
|
|
2327
2366
|
>
|
|
@@ -2329,7 +2368,7 @@ function ChannelsSettings() {
|
|
|
2329
2368
|
</button>
|
|
2330
2369
|
<button
|
|
2331
2370
|
onClick={() => deleteChannel(channel)}
|
|
2332
|
-
className="text-[
|
|
2371
|
+
className="text-[var(--color-text-muted)] hover:text-red-400 transition text-sm"
|
|
2333
2372
|
>
|
|
2334
2373
|
×
|
|
2335
2374
|
</button>
|
|
@@ -2355,9 +2394,11 @@ function AssistantSettings() {
|
|
|
2355
2394
|
const [saving, setSaving] = useState(false);
|
|
2356
2395
|
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
|
|
2357
2396
|
const [starting, setStarting] = useState(false);
|
|
2397
|
+
const [webSearch, setWebSearch] = useState(false);
|
|
2398
|
+
const [webFetch, setWebFetch] = useState(false);
|
|
2358
2399
|
|
|
2359
2400
|
// Original values for change detection
|
|
2360
|
-
const [original, setOriginal] = useState({ provider: "", model: "", systemPrompt: "" });
|
|
2401
|
+
const [original, setOriginal] = useState({ provider: "", model: "", systemPrompt: "", webSearch: false, webFetch: false });
|
|
2361
2402
|
|
|
2362
2403
|
useEffect(() => {
|
|
2363
2404
|
const fetchData = async () => {
|
|
@@ -2376,7 +2417,11 @@ function AssistantSettings() {
|
|
|
2376
2417
|
setModel(a.model || "");
|
|
2377
2418
|
setSystemPrompt(a.systemPrompt || "");
|
|
2378
2419
|
setStatus(a.status || "stopped");
|
|
2379
|
-
|
|
2420
|
+
const ws = a.features?.builtinTools?.webSearch || false;
|
|
2421
|
+
const wf = a.features?.builtinTools?.webFetch || false;
|
|
2422
|
+
setWebSearch(ws);
|
|
2423
|
+
setWebFetch(wf);
|
|
2424
|
+
setOriginal({ provider: a.provider || "", model: a.model || "", systemPrompt: a.systemPrompt || "", webSearch: ws, webFetch: wf });
|
|
2380
2425
|
}
|
|
2381
2426
|
} catch {
|
|
2382
2427
|
setMessage({ type: "error", text: "Failed to load assistant config" });
|
|
@@ -2397,7 +2442,7 @@ function AssistantSettings() {
|
|
|
2397
2442
|
setModel(defaultModel);
|
|
2398
2443
|
};
|
|
2399
2444
|
|
|
2400
|
-
const hasChanges = provider !== original.provider || model !== original.model || systemPrompt !== original.systemPrompt;
|
|
2445
|
+
const hasChanges = provider !== original.provider || model !== original.model || systemPrompt !== original.systemPrompt || webSearch !== original.webSearch || webFetch !== original.webFetch;
|
|
2401
2446
|
|
|
2402
2447
|
const handleSave = async () => {
|
|
2403
2448
|
setSaving(true);
|
|
@@ -2406,10 +2451,10 @@ function AssistantSettings() {
|
|
|
2406
2451
|
const res = await authFetch("/api/agents/apteva-assistant", {
|
|
2407
2452
|
method: "PUT",
|
|
2408
2453
|
headers: { "Content-Type": "application/json" },
|
|
2409
|
-
body: JSON.stringify({ provider, model, systemPrompt }),
|
|
2454
|
+
body: JSON.stringify({ provider, model, systemPrompt, features: { builtinTools: { webSearch, webFetch } } }),
|
|
2410
2455
|
});
|
|
2411
2456
|
if (res.ok) {
|
|
2412
|
-
setOriginal({ provider, model, systemPrompt });
|
|
2457
|
+
setOriginal({ provider, model, systemPrompt, webSearch, webFetch });
|
|
2413
2458
|
setMessage({ type: "success", text: "Assistant settings saved" });
|
|
2414
2459
|
setTimeout(() => setMessage(null), 3000);
|
|
2415
2460
|
} else {
|
|
@@ -2443,13 +2488,13 @@ function AssistantSettings() {
|
|
|
2443
2488
|
};
|
|
2444
2489
|
|
|
2445
2490
|
if (loading) {
|
|
2446
|
-
return <div className="text-[
|
|
2491
|
+
return <div className="text-[var(--color-text-muted)]">Loading assistant settings...</div>;
|
|
2447
2492
|
}
|
|
2448
2493
|
|
|
2449
2494
|
return (
|
|
2450
2495
|
<div className="max-w-2xl">
|
|
2451
2496
|
<h2 className="text-lg font-medium mb-1">Apteva Assistant</h2>
|
|
2452
|
-
<p className="text-sm text-[
|
|
2497
|
+
<p className="text-sm text-[var(--color-text-muted)] mb-6">Configure the built-in AI assistant that manages your agents and platform.</p>
|
|
2453
2498
|
|
|
2454
2499
|
{message && (
|
|
2455
2500
|
<div className={`mb-4 px-3 py-2 rounded text-sm ${
|
|
@@ -2461,9 +2506,9 @@ function AssistantSettings() {
|
|
|
2461
2506
|
|
|
2462
2507
|
{/* Status */}
|
|
2463
2508
|
<div className="mb-6 flex items-center gap-3">
|
|
2464
|
-
<span className="text-sm text-[
|
|
2509
|
+
<span className="text-sm text-[var(--color-text-muted)]">Status:</span>
|
|
2465
2510
|
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
|
2466
|
-
status === "running" ? "bg-[#3b82f6]/20 text-[#3b82f6]" : "bg-[
|
|
2511
|
+
status === "running" ? "bg-[#3b82f6]/20 text-[#3b82f6]" : "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
2467
2512
|
}`}>
|
|
2468
2513
|
{status}
|
|
2469
2514
|
</span>
|
|
@@ -2472,7 +2517,7 @@ function AssistantSettings() {
|
|
|
2472
2517
|
disabled={starting}
|
|
2473
2518
|
className={`px-3 py-1.5 rounded text-sm font-medium transition ${
|
|
2474
2519
|
status === "running"
|
|
2475
|
-
? "bg-[
|
|
2520
|
+
? "bg-[var(--color-accent-20)] text-[var(--color-accent)] hover:bg-[var(--color-accent-30)]"
|
|
2476
2521
|
: "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
|
|
2477
2522
|
} disabled:opacity-50`}
|
|
2478
2523
|
>
|
|
@@ -2482,7 +2527,7 @@ function AssistantSettings() {
|
|
|
2482
2527
|
|
|
2483
2528
|
{/* Provider */}
|
|
2484
2529
|
<div className="mb-4">
|
|
2485
|
-
<label className="block text-sm text-[
|
|
2530
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Provider</label>
|
|
2486
2531
|
<Select
|
|
2487
2532
|
value={provider}
|
|
2488
2533
|
onChange={handleProviderChange}
|
|
@@ -2493,7 +2538,7 @@ function AssistantSettings() {
|
|
|
2493
2538
|
|
|
2494
2539
|
{/* Model */}
|
|
2495
2540
|
<div className="mb-4">
|
|
2496
|
-
<label className="block text-sm text-[
|
|
2541
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Model</label>
|
|
2497
2542
|
<Select
|
|
2498
2543
|
value={model}
|
|
2499
2544
|
onChange={setModel}
|
|
@@ -2502,14 +2547,52 @@ function AssistantSettings() {
|
|
|
2502
2547
|
/>
|
|
2503
2548
|
</div>
|
|
2504
2549
|
|
|
2550
|
+
{/* Built-in Tools - Anthropic only */}
|
|
2551
|
+
{provider === "anthropic" && (
|
|
2552
|
+
<div className="mb-4">
|
|
2553
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">Built-in Tools</label>
|
|
2554
|
+
<div className="flex flex-wrap gap-2">
|
|
2555
|
+
<button
|
|
2556
|
+
type="button"
|
|
2557
|
+
onClick={() => setWebSearch(!webSearch)}
|
|
2558
|
+
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
2559
|
+
webSearch
|
|
2560
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
|
|
2561
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
|
|
2562
|
+
}`}
|
|
2563
|
+
>
|
|
2564
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2565
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
2566
|
+
</svg>
|
|
2567
|
+
<span className="text-sm">Web Search</span>
|
|
2568
|
+
</button>
|
|
2569
|
+
<button
|
|
2570
|
+
type="button"
|
|
2571
|
+
onClick={() => setWebFetch(!webFetch)}
|
|
2572
|
+
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
2573
|
+
webFetch
|
|
2574
|
+
? "border-[var(--color-accent)] bg-[var(--color-accent-10)] text-[var(--color-accent)]"
|
|
2575
|
+
: "border-[var(--color-border-light)] hover:border-[var(--color-border-light)] text-[var(--color-text-secondary)]"
|
|
2576
|
+
}`}
|
|
2577
|
+
>
|
|
2578
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2579
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
|
2580
|
+
</svg>
|
|
2581
|
+
<span className="text-sm">Web Fetch</span>
|
|
2582
|
+
</button>
|
|
2583
|
+
</div>
|
|
2584
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-2">Provider-native tools for real-time web access</p>
|
|
2585
|
+
</div>
|
|
2586
|
+
)}
|
|
2587
|
+
|
|
2505
2588
|
{/* System Prompt */}
|
|
2506
2589
|
<div className="mb-6">
|
|
2507
|
-
<label className="block text-sm text-[
|
|
2590
|
+
<label className="block text-sm text-[var(--color-text-muted)] mb-1">System Prompt</label>
|
|
2508
2591
|
<textarea
|
|
2509
2592
|
value={systemPrompt}
|
|
2510
2593
|
onChange={e => setSystemPrompt(e.target.value)}
|
|
2511
2594
|
rows={12}
|
|
2512
|
-
className="w-full bg-[
|
|
2595
|
+
className="w-full bg-[var(--color-surface)] border border-[var(--color-border)] rounded px-3 py-2 text-sm font-mono focus:outline-none focus:border-[var(--color-accent)] resize-y"
|
|
2513
2596
|
/>
|
|
2514
2597
|
</div>
|
|
2515
2598
|
|
|
@@ -2517,13 +2600,13 @@ function AssistantSettings() {
|
|
|
2517
2600
|
<button
|
|
2518
2601
|
onClick={handleSave}
|
|
2519
2602
|
disabled={!hasChanges || saving}
|
|
2520
|
-
className="bg-[
|
|
2603
|
+
className="bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
|
|
2521
2604
|
>
|
|
2522
2605
|
{saving ? "Saving..." : "Save Changes"}
|
|
2523
2606
|
</button>
|
|
2524
2607
|
|
|
2525
2608
|
{status === "running" && hasChanges && (
|
|
2526
|
-
<p className="text-xs text-[
|
|
2609
|
+
<p className="text-xs text-[var(--color-text-muted)] mt-2">Changes will be applied to the running assistant</p>
|
|
2527
2610
|
)}
|
|
2528
2611
|
</div>
|
|
2529
2612
|
);
|