@auxiora/dashboard 1.0.0
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/LICENSE +191 -0
- package/dist/auth.d.ts +13 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +69 -0
- package/dist/auth.js.map +1 -0
- package/dist/cloud-types.d.ts +71 -0
- package/dist/cloud-types.d.ts.map +1 -0
- package/dist/cloud-types.js +2 -0
- package/dist/cloud-types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +13 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +2250 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +314 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist-ui/assets/index-BfY0i5jw.css +1 -0
- package/dist-ui/assets/index-CXpk9mvw.js +60 -0
- package/dist-ui/icon.svg +59 -0
- package/dist-ui/index.html +20 -0
- package/package.json +32 -0
- package/src/auth.ts +83 -0
- package/src/cloud-types.ts +63 -0
- package/src/index.ts +5 -0
- package/src/router.ts +2494 -0
- package/src/types.ts +269 -0
- package/tests/auth.test.ts +51 -0
- package/tests/cloud-router.test.ts +249 -0
- package/tests/desktop-router.test.ts +151 -0
- package/tests/router.test.ts +388 -0
- package/tests/trust-router.test.ts +170 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/index.html +19 -0
- package/ui/node_modules/.bin/browserslist +17 -0
- package/ui/node_modules/.bin/tsc +17 -0
- package/ui/node_modules/.bin/tsserver +17 -0
- package/ui/node_modules/.bin/vite +17 -0
- package/ui/package.json +23 -0
- package/ui/public/icon.svg +59 -0
- package/ui/src/App.tsx +63 -0
- package/ui/src/api.ts +238 -0
- package/ui/src/components/ActivityFeed.tsx +123 -0
- package/ui/src/components/BehaviorHealth.tsx +105 -0
- package/ui/src/components/DataTable.tsx +39 -0
- package/ui/src/components/Layout.tsx +160 -0
- package/ui/src/components/PasswordStrength.tsx +31 -0
- package/ui/src/components/SetupProgress.tsx +26 -0
- package/ui/src/components/StatusBadge.tsx +12 -0
- package/ui/src/components/ThemeSelector.tsx +39 -0
- package/ui/src/contexts/ThemeContext.tsx +58 -0
- package/ui/src/hooks/useApi.ts +19 -0
- package/ui/src/hooks/usePolling.ts +8 -0
- package/ui/src/main.tsx +16 -0
- package/ui/src/pages/AuditLog.tsx +36 -0
- package/ui/src/pages/Behaviors.tsx +426 -0
- package/ui/src/pages/Chat.tsx +688 -0
- package/ui/src/pages/Login.tsx +64 -0
- package/ui/src/pages/Overview.tsx +56 -0
- package/ui/src/pages/Sessions.tsx +26 -0
- package/ui/src/pages/SettingsAmbient.tsx +185 -0
- package/ui/src/pages/SettingsConnections.tsx +201 -0
- package/ui/src/pages/SettingsNotifications.tsx +241 -0
- package/ui/src/pages/SetupAppearance.tsx +45 -0
- package/ui/src/pages/SetupChannels.tsx +143 -0
- package/ui/src/pages/SetupComplete.tsx +31 -0
- package/ui/src/pages/SetupConnections.tsx +80 -0
- package/ui/src/pages/SetupDashboardPassword.tsx +50 -0
- package/ui/src/pages/SetupIdentity.tsx +68 -0
- package/ui/src/pages/SetupPersonality.tsx +78 -0
- package/ui/src/pages/SetupProvider.tsx +65 -0
- package/ui/src/pages/SetupVault.tsx +50 -0
- package/ui/src/pages/SetupWelcome.tsx +19 -0
- package/ui/src/pages/UnlockVault.tsx +56 -0
- package/ui/src/pages/Webhooks.tsx +158 -0
- package/ui/src/pages/settings/Appearance.tsx +63 -0
- package/ui/src/pages/settings/Channels.tsx +138 -0
- package/ui/src/pages/settings/Identity.tsx +61 -0
- package/ui/src/pages/settings/Personality.tsx +54 -0
- package/ui/src/pages/settings/PersonalityEditor.tsx +577 -0
- package/ui/src/pages/settings/Provider.tsx +537 -0
- package/ui/src/pages/settings/Security.tsx +111 -0
- package/ui/src/styles/global.css +2308 -0
- package/ui/src/styles/themes/index.css +7 -0
- package/ui/src/styles/themes/monolith.css +125 -0
- package/ui/src/styles/themes/nebula.css +90 -0
- package/ui/src/styles/themes/neon.css +149 -0
- package/ui/src/styles/themes/polar.css +151 -0
- package/ui/src/styles/themes/signal.css +163 -0
- package/ui/src/styles/themes/terra.css +146 -0
- package/ui/tsconfig.json +14 -0
- package/ui/vite.config.ts +20 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import request from 'supertest';
|
|
4
|
+
import { createDashboardRouter } from '../src/router.js';
|
|
5
|
+
import type { DashboardDeps } from '../src/types.js';
|
|
6
|
+
|
|
7
|
+
function createMockDeps(): DashboardDeps {
|
|
8
|
+
return {
|
|
9
|
+
vault: {
|
|
10
|
+
get: vi.fn(() => undefined),
|
|
11
|
+
has: vi.fn(() => false),
|
|
12
|
+
add: vi.fn(),
|
|
13
|
+
},
|
|
14
|
+
getConnections: vi.fn().mockReturnValue([]),
|
|
15
|
+
getAuditEntries: vi.fn().mockResolvedValue([]),
|
|
16
|
+
setup: {
|
|
17
|
+
getAgentName: () => 'TestAgent',
|
|
18
|
+
hasSoulFile: async () => true,
|
|
19
|
+
},
|
|
20
|
+
desktop: {
|
|
21
|
+
getStatus: vi.fn().mockReturnValue({
|
|
22
|
+
status: 'running',
|
|
23
|
+
autoStart: false,
|
|
24
|
+
hotkey: 'CommandOrControl+Shift+A',
|
|
25
|
+
notificationsEnabled: true,
|
|
26
|
+
ollamaRunning: false,
|
|
27
|
+
updateChannel: 'stable',
|
|
28
|
+
}),
|
|
29
|
+
updateConfig: vi.fn().mockResolvedValue({
|
|
30
|
+
autoStart: true,
|
|
31
|
+
hotkey: 'CommandOrControl+Shift+A',
|
|
32
|
+
}),
|
|
33
|
+
sendNotification: vi.fn().mockResolvedValue(undefined),
|
|
34
|
+
checkUpdates: vi.fn().mockResolvedValue({
|
|
35
|
+
available: false,
|
|
36
|
+
currentVersion: '2.0.0',
|
|
37
|
+
channel: 'stable',
|
|
38
|
+
}),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createApp(deps: DashboardDeps) {
|
|
44
|
+
const app = express();
|
|
45
|
+
app.use(express.json());
|
|
46
|
+
const { router, auth } = createDashboardRouter({
|
|
47
|
+
deps,
|
|
48
|
+
config: { enabled: true, sessionTtlMs: 86_400_000 },
|
|
49
|
+
verifyPassword: (input: string) => input === 'correct-password',
|
|
50
|
+
});
|
|
51
|
+
app.use('/api/v1/dashboard', router);
|
|
52
|
+
return { app, auth };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function loginAndGetCookie(app: express.Express): Promise<string> {
|
|
56
|
+
return request(app)
|
|
57
|
+
.post('/api/v1/dashboard/auth/login')
|
|
58
|
+
.send({ password: 'correct-password' })
|
|
59
|
+
.then((res) => {
|
|
60
|
+
const cookie = res.headers['set-cookie'];
|
|
61
|
+
return Array.isArray(cookie) ? cookie[0] : cookie;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe('Desktop dashboard routes', () => {
|
|
66
|
+
let deps: DashboardDeps;
|
|
67
|
+
let app: express.Express;
|
|
68
|
+
let cookie: string;
|
|
69
|
+
|
|
70
|
+
beforeEach(async () => {
|
|
71
|
+
deps = createMockDeps();
|
|
72
|
+
const created = createApp(deps);
|
|
73
|
+
app = created.app;
|
|
74
|
+
cookie = await loginAndGetCookie(app);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('GET /desktop/status', () => {
|
|
78
|
+
it('should return desktop status', async () => {
|
|
79
|
+
const res = await request(app)
|
|
80
|
+
.get('/api/v1/dashboard/desktop/status')
|
|
81
|
+
.set('Cookie', cookie);
|
|
82
|
+
|
|
83
|
+
expect(res.status).toBe(200);
|
|
84
|
+
expect(res.body.data.status).toBe('running');
|
|
85
|
+
expect(res.body.data.hotkey).toBe('CommandOrControl+Shift+A');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return 503 when desktop not available', async () => {
|
|
89
|
+
const noDeps = createMockDeps();
|
|
90
|
+
delete noDeps.desktop;
|
|
91
|
+
const noDesktopApp = createApp(noDeps);
|
|
92
|
+
const noCookie = await loginAndGetCookie(noDesktopApp.app);
|
|
93
|
+
|
|
94
|
+
const res = await request(noDesktopApp.app)
|
|
95
|
+
.get('/api/v1/dashboard/desktop/status')
|
|
96
|
+
.set('Cookie', noCookie);
|
|
97
|
+
|
|
98
|
+
expect(res.status).toBe(503);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('POST /desktop/config', () => {
|
|
103
|
+
it('should update desktop config', async () => {
|
|
104
|
+
const res = await request(app)
|
|
105
|
+
.post('/api/v1/dashboard/desktop/config')
|
|
106
|
+
.set('Cookie', cookie)
|
|
107
|
+
.send({ autoStart: true });
|
|
108
|
+
|
|
109
|
+
expect(res.status).toBe(200);
|
|
110
|
+
expect(res.body.data).toBeDefined();
|
|
111
|
+
expect(deps.desktop!.updateConfig).toHaveBeenCalledWith({ autoStart: true });
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('POST /desktop/notification', () => {
|
|
116
|
+
it('should send a notification', async () => {
|
|
117
|
+
const res = await request(app)
|
|
118
|
+
.post('/api/v1/dashboard/desktop/notification')
|
|
119
|
+
.set('Cookie', cookie)
|
|
120
|
+
.send({ title: 'Test', body: 'Hello' });
|
|
121
|
+
|
|
122
|
+
expect(res.status).toBe(200);
|
|
123
|
+
expect(res.body.data.sent).toBe(true);
|
|
124
|
+
expect(deps.desktop!.sendNotification).toHaveBeenCalledWith({
|
|
125
|
+
title: 'Test',
|
|
126
|
+
body: 'Hello',
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should require title and body', async () => {
|
|
131
|
+
const res = await request(app)
|
|
132
|
+
.post('/api/v1/dashboard/desktop/notification')
|
|
133
|
+
.set('Cookie', cookie)
|
|
134
|
+
.send({ title: 'No body' });
|
|
135
|
+
|
|
136
|
+
expect(res.status).toBe(400);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('GET /desktop/updates', () => {
|
|
141
|
+
it('should check for updates', async () => {
|
|
142
|
+
const res = await request(app)
|
|
143
|
+
.get('/api/v1/dashboard/desktop/updates')
|
|
144
|
+
.set('Cookie', cookie);
|
|
145
|
+
|
|
146
|
+
expect(res.status).toBe(200);
|
|
147
|
+
expect(res.body.data.available).toBe(false);
|
|
148
|
+
expect(res.body.data.currentVersion).toBe('2.0.0');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import request from 'supertest';
|
|
4
|
+
import { createDashboardRouter } from '../src/router.js';
|
|
5
|
+
import type { DashboardDeps } from '../src/types.js';
|
|
6
|
+
|
|
7
|
+
function createMockDeps(): DashboardDeps {
|
|
8
|
+
return {
|
|
9
|
+
vault: {
|
|
10
|
+
get: vi.fn((name: string) => {
|
|
11
|
+
if (name === 'DASHBOARD_PASSWORD') return 'hashed-pw';
|
|
12
|
+
return undefined;
|
|
13
|
+
}),
|
|
14
|
+
has: vi.fn((name: string) => name === 'DASHBOARD_PASSWORD'),
|
|
15
|
+
add: vi.fn(),
|
|
16
|
+
},
|
|
17
|
+
behaviors: {
|
|
18
|
+
list: vi.fn().mockResolvedValue([
|
|
19
|
+
{ id: 'bh-1', type: 'scheduled', status: 'active', action: 'test', runCount: 5, failCount: 0 },
|
|
20
|
+
]),
|
|
21
|
+
update: vi.fn().mockResolvedValue({ id: 'bh-1', status: 'paused' }),
|
|
22
|
+
remove: vi.fn().mockResolvedValue(true),
|
|
23
|
+
},
|
|
24
|
+
webhooks: {
|
|
25
|
+
list: vi.fn().mockResolvedValue([
|
|
26
|
+
{ id: 'wh-1', name: 'hook-1', type: 'generic', enabled: true, secret: 'real-secret' },
|
|
27
|
+
]),
|
|
28
|
+
update: vi.fn().mockResolvedValue({ id: 'wh-1', name: 'hook-1', enabled: false, secret: 'real-secret' }),
|
|
29
|
+
delete: vi.fn().mockResolvedValue(true),
|
|
30
|
+
},
|
|
31
|
+
getConnections: vi.fn().mockReturnValue([
|
|
32
|
+
{ id: 'conn-1', authenticated: true, channelType: 'webchat', lastActive: Date.now(), voiceActive: false },
|
|
33
|
+
]),
|
|
34
|
+
getAuditEntries: vi.fn().mockResolvedValue([
|
|
35
|
+
{ timestamp: '2026-01-01T00:00:00Z', event: 'system.startup', details: {} },
|
|
36
|
+
]),
|
|
37
|
+
getPlugins: vi.fn().mockReturnValue([
|
|
38
|
+
{ name: 'test-plugin', version: '1.0.0', file: 'test.js', toolCount: 1, toolNames: ['test_tool'], behaviorNames: [], providerNames: [], permissions: ['NETWORK'], status: 'loaded' },
|
|
39
|
+
]),
|
|
40
|
+
pluginManager: {
|
|
41
|
+
enable: vi.fn().mockResolvedValue(true),
|
|
42
|
+
disable: vi.fn().mockResolvedValue(true),
|
|
43
|
+
remove: vi.fn().mockResolvedValue(true),
|
|
44
|
+
getConfig: vi.fn().mockReturnValue({ key: 'value' }),
|
|
45
|
+
setConfig: vi.fn().mockResolvedValue(true),
|
|
46
|
+
getPermissions: vi.fn().mockReturnValue(['NETWORK']),
|
|
47
|
+
setPermissions: vi.fn().mockResolvedValue(true),
|
|
48
|
+
},
|
|
49
|
+
marketplace: {
|
|
50
|
+
search: vi.fn().mockResolvedValue([{ name: 'cool-plugin', version: '1.0.0' }]),
|
|
51
|
+
getPlugin: vi.fn().mockResolvedValue({ name: 'cool-plugin', version: '1.0.0', description: 'Cool' }),
|
|
52
|
+
install: vi.fn().mockResolvedValue({ success: true }),
|
|
53
|
+
},
|
|
54
|
+
getMemories: vi.fn().mockResolvedValue([
|
|
55
|
+
{ id: 'mem-abc', content: 'Likes TypeScript', category: 'preference', source: 'explicit', createdAt: Date.now(), updatedAt: Date.now(), accessCount: 1 },
|
|
56
|
+
]),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createApp(deps: DashboardDeps) {
|
|
61
|
+
const app = express();
|
|
62
|
+
app.use(express.json());
|
|
63
|
+
const { router, auth } = createDashboardRouter({
|
|
64
|
+
deps,
|
|
65
|
+
config: { enabled: true, sessionTtlMs: 86_400_000 },
|
|
66
|
+
verifyPassword: (input: string) => input === 'correct-password',
|
|
67
|
+
});
|
|
68
|
+
app.use('/api/v1/dashboard', router);
|
|
69
|
+
return { app, auth };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function loginAndGetCookie(app: express.Express): Promise<string> {
|
|
73
|
+
return request(app)
|
|
74
|
+
.post('/api/v1/dashboard/auth/login')
|
|
75
|
+
.send({ password: 'correct-password' })
|
|
76
|
+
.then((res) => {
|
|
77
|
+
const cookie = res.headers['set-cookie'];
|
|
78
|
+
return Array.isArray(cookie) ? cookie[0] : cookie;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe('Dashboard Router', () => {
|
|
83
|
+
let deps: DashboardDeps;
|
|
84
|
+
let app: express.Express;
|
|
85
|
+
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
deps = createMockDeps();
|
|
88
|
+
({ app } = createApp(deps));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('auth', () => {
|
|
92
|
+
it('should login with correct password', async () => {
|
|
93
|
+
const res = await request(app)
|
|
94
|
+
.post('/api/v1/dashboard/auth/login')
|
|
95
|
+
.send({ password: 'correct-password' });
|
|
96
|
+
expect(res.status).toBe(200);
|
|
97
|
+
expect(res.body.success).toBe(true);
|
|
98
|
+
expect(res.headers['set-cookie']).toBeDefined();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should reject wrong password', async () => {
|
|
102
|
+
const res = await request(app)
|
|
103
|
+
.post('/api/v1/dashboard/auth/login')
|
|
104
|
+
.send({ password: 'wrong' });
|
|
105
|
+
expect(res.status).toBe(401);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should rate limit login attempts', async () => {
|
|
109
|
+
for (let i = 0; i < 5; i++) {
|
|
110
|
+
await request(app)
|
|
111
|
+
.post('/api/v1/dashboard/auth/login')
|
|
112
|
+
.send({ password: 'wrong' });
|
|
113
|
+
}
|
|
114
|
+
const res = await request(app)
|
|
115
|
+
.post('/api/v1/dashboard/auth/login')
|
|
116
|
+
.send({ password: 'wrong' });
|
|
117
|
+
expect(res.status).toBe(429);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should check auth status', async () => {
|
|
121
|
+
const cookie = await loginAndGetCookie(app);
|
|
122
|
+
const res = await request(app)
|
|
123
|
+
.get('/api/v1/dashboard/auth/check')
|
|
124
|
+
.set('Cookie', cookie);
|
|
125
|
+
expect(res.status).toBe(200);
|
|
126
|
+
expect(res.body.authenticated).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should logout and invalidate session', async () => {
|
|
130
|
+
const cookie = await loginAndGetCookie(app);
|
|
131
|
+
await request(app)
|
|
132
|
+
.post('/api/v1/dashboard/auth/logout')
|
|
133
|
+
.set('Cookie', cookie);
|
|
134
|
+
|
|
135
|
+
const res = await request(app)
|
|
136
|
+
.get('/api/v1/dashboard/auth/check')
|
|
137
|
+
.set('Cookie', cookie);
|
|
138
|
+
expect(res.body.authenticated).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should reject unauthenticated API requests', async () => {
|
|
142
|
+
const res = await request(app).get('/api/v1/dashboard/behaviors');
|
|
143
|
+
expect(res.status).toBe(401);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('behaviors API', () => {
|
|
148
|
+
it('should list behaviors', async () => {
|
|
149
|
+
const cookie = await loginAndGetCookie(app);
|
|
150
|
+
const res = await request(app)
|
|
151
|
+
.get('/api/v1/dashboard/behaviors')
|
|
152
|
+
.set('Cookie', cookie);
|
|
153
|
+
expect(res.status).toBe(200);
|
|
154
|
+
expect(res.body.data).toHaveLength(1);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should patch behavior status', async () => {
|
|
158
|
+
const cookie = await loginAndGetCookie(app);
|
|
159
|
+
const res = await request(app)
|
|
160
|
+
.patch('/api/v1/dashboard/behaviors/bh-1')
|
|
161
|
+
.set('Cookie', cookie)
|
|
162
|
+
.send({ status: 'paused' });
|
|
163
|
+
expect(res.status).toBe(200);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should delete a behavior', async () => {
|
|
167
|
+
const cookie = await loginAndGetCookie(app);
|
|
168
|
+
const res = await request(app)
|
|
169
|
+
.delete('/api/v1/dashboard/behaviors/bh-1')
|
|
170
|
+
.set('Cookie', cookie);
|
|
171
|
+
expect(res.status).toBe(200);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should return 401 without auth', async () => {
|
|
175
|
+
const res = await request(app).get('/api/v1/dashboard/behaviors');
|
|
176
|
+
expect(res.status).toBe(401);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('webhooks API', () => {
|
|
181
|
+
it('should list webhooks with redacted secrets', async () => {
|
|
182
|
+
const cookie = await loginAndGetCookie(app);
|
|
183
|
+
const res = await request(app)
|
|
184
|
+
.get('/api/v1/dashboard/webhooks')
|
|
185
|
+
.set('Cookie', cookie);
|
|
186
|
+
expect(res.status).toBe(200);
|
|
187
|
+
expect(res.body.data[0].secret).toBe('***');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should patch webhook enabled status', async () => {
|
|
191
|
+
const cookie = await loginAndGetCookie(app);
|
|
192
|
+
const res = await request(app)
|
|
193
|
+
.patch('/api/v1/dashboard/webhooks/wh-1')
|
|
194
|
+
.set('Cookie', cookie)
|
|
195
|
+
.send({ enabled: false });
|
|
196
|
+
expect(res.status).toBe(200);
|
|
197
|
+
expect(res.body.data.secret).toBe('***');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should delete a webhook', async () => {
|
|
201
|
+
const cookie = await loginAndGetCookie(app);
|
|
202
|
+
const res = await request(app)
|
|
203
|
+
.delete('/api/v1/dashboard/webhooks/wh-1')
|
|
204
|
+
.set('Cookie', cookie);
|
|
205
|
+
expect(res.status).toBe(200);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('sessions API', () => {
|
|
210
|
+
it('should list active connections', async () => {
|
|
211
|
+
const cookie = await loginAndGetCookie(app);
|
|
212
|
+
const res = await request(app)
|
|
213
|
+
.get('/api/v1/dashboard/sessions')
|
|
214
|
+
.set('Cookie', cookie);
|
|
215
|
+
expect(res.status).toBe(200);
|
|
216
|
+
expect(res.body.data).toHaveLength(1);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('audit API', () => {
|
|
221
|
+
it('should return audit entries', async () => {
|
|
222
|
+
const cookie = await loginAndGetCookie(app);
|
|
223
|
+
const res = await request(app)
|
|
224
|
+
.get('/api/v1/dashboard/audit')
|
|
225
|
+
.set('Cookie', cookie);
|
|
226
|
+
expect(res.status).toBe(200);
|
|
227
|
+
expect(res.body.data).toHaveLength(1);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('status API', () => {
|
|
232
|
+
it('should return system status', async () => {
|
|
233
|
+
const cookie = await loginAndGetCookie(app);
|
|
234
|
+
const res = await request(app)
|
|
235
|
+
.get('/api/v1/dashboard/status')
|
|
236
|
+
.set('Cookie', cookie);
|
|
237
|
+
expect(res.status).toBe(200);
|
|
238
|
+
expect(res.body.data.uptime).toBeDefined();
|
|
239
|
+
expect(res.body.data.connections).toBe(1);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('plugins API', () => {
|
|
244
|
+
it('should list loaded plugins', async () => {
|
|
245
|
+
const cookie = await loginAndGetCookie(app);
|
|
246
|
+
const res = await request(app)
|
|
247
|
+
.get('/api/v1/dashboard/plugins')
|
|
248
|
+
.set('Cookie', cookie);
|
|
249
|
+
expect(res.status).toBe(200);
|
|
250
|
+
expect(res.body.data).toHaveLength(1);
|
|
251
|
+
expect(res.body.data[0].name).toBe('test-plugin');
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('memories API', () => {
|
|
256
|
+
it('should list memories', async () => {
|
|
257
|
+
const cookie = await loginAndGetCookie(app);
|
|
258
|
+
const res = await request(app)
|
|
259
|
+
.get('/api/v1/dashboard/memories')
|
|
260
|
+
.set('Cookie', cookie);
|
|
261
|
+
expect(res.status).toBe(200);
|
|
262
|
+
expect(res.body.data).toHaveLength(1);
|
|
263
|
+
expect(res.body.data[0].content).toBe('Likes TypeScript');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('plugin management API', () => {
|
|
268
|
+
it('should get a single plugin by id', async () => {
|
|
269
|
+
const cookie = await loginAndGetCookie(app);
|
|
270
|
+
const res = await request(app)
|
|
271
|
+
.get('/api/v1/dashboard/plugins/test-plugin')
|
|
272
|
+
.set('Cookie', cookie);
|
|
273
|
+
expect(res.status).toBe(200);
|
|
274
|
+
expect(res.body.data.name).toBe('test-plugin');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should return 404 for unknown plugin', async () => {
|
|
278
|
+
const cookie = await loginAndGetCookie(app);
|
|
279
|
+
const res = await request(app)
|
|
280
|
+
.get('/api/v1/dashboard/plugins/nonexistent')
|
|
281
|
+
.set('Cookie', cookie);
|
|
282
|
+
expect(res.status).toBe(404);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should enable a plugin', async () => {
|
|
286
|
+
const cookie = await loginAndGetCookie(app);
|
|
287
|
+
const res = await request(app)
|
|
288
|
+
.post('/api/v1/dashboard/plugins/test-plugin/enable')
|
|
289
|
+
.set('Cookie', cookie);
|
|
290
|
+
expect(res.status).toBe(200);
|
|
291
|
+
expect(res.body.data.enabled).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should disable a plugin', async () => {
|
|
295
|
+
const cookie = await loginAndGetCookie(app);
|
|
296
|
+
const res = await request(app)
|
|
297
|
+
.post('/api/v1/dashboard/plugins/test-plugin/disable')
|
|
298
|
+
.set('Cookie', cookie);
|
|
299
|
+
expect(res.status).toBe(200);
|
|
300
|
+
expect(res.body.data.disabled).toBe(true);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should delete a plugin', async () => {
|
|
304
|
+
const cookie = await loginAndGetCookie(app);
|
|
305
|
+
const res = await request(app)
|
|
306
|
+
.delete('/api/v1/dashboard/plugins/test-plugin')
|
|
307
|
+
.set('Cookie', cookie);
|
|
308
|
+
expect(res.status).toBe(200);
|
|
309
|
+
expect(res.body.data.deleted).toBe(true);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should get plugin config', async () => {
|
|
313
|
+
const cookie = await loginAndGetCookie(app);
|
|
314
|
+
const res = await request(app)
|
|
315
|
+
.get('/api/v1/dashboard/plugins/test-plugin/config')
|
|
316
|
+
.set('Cookie', cookie);
|
|
317
|
+
expect(res.status).toBe(200);
|
|
318
|
+
expect(res.body.data.key).toBe('value');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should set plugin config', async () => {
|
|
322
|
+
const cookie = await loginAndGetCookie(app);
|
|
323
|
+
const res = await request(app)
|
|
324
|
+
.post('/api/v1/dashboard/plugins/test-plugin/config')
|
|
325
|
+
.set('Cookie', cookie)
|
|
326
|
+
.send({ newKey: 'newValue' });
|
|
327
|
+
expect(res.status).toBe(200);
|
|
328
|
+
expect(res.body.data.updated).toBe(true);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should get plugin permissions', async () => {
|
|
332
|
+
const cookie = await loginAndGetCookie(app);
|
|
333
|
+
const res = await request(app)
|
|
334
|
+
.get('/api/v1/dashboard/plugins/test-plugin/permissions')
|
|
335
|
+
.set('Cookie', cookie);
|
|
336
|
+
expect(res.status).toBe(200);
|
|
337
|
+
expect(res.body.data).toEqual(['NETWORK']);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should set plugin permissions', async () => {
|
|
341
|
+
const cookie = await loginAndGetCookie(app);
|
|
342
|
+
const res = await request(app)
|
|
343
|
+
.post('/api/v1/dashboard/plugins/test-plugin/permissions')
|
|
344
|
+
.set('Cookie', cookie)
|
|
345
|
+
.send({ permissions: ['NETWORK', 'FILESYSTEM'] });
|
|
346
|
+
expect(res.status).toBe(200);
|
|
347
|
+
expect(res.body.data.updated).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should reject invalid permissions body', async () => {
|
|
351
|
+
const cookie = await loginAndGetCookie(app);
|
|
352
|
+
const res = await request(app)
|
|
353
|
+
.post('/api/v1/dashboard/plugins/test-plugin/permissions')
|
|
354
|
+
.set('Cookie', cookie)
|
|
355
|
+
.send({ permissions: 'not-array' });
|
|
356
|
+
expect(res.status).toBe(400);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('marketplace API', () => {
|
|
361
|
+
it('should search marketplace', async () => {
|
|
362
|
+
const cookie = await loginAndGetCookie(app);
|
|
363
|
+
const res = await request(app)
|
|
364
|
+
.get('/api/v1/dashboard/marketplace')
|
|
365
|
+
.set('Cookie', cookie);
|
|
366
|
+
expect(res.status).toBe(200);
|
|
367
|
+
expect(res.body.data).toHaveLength(1);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('should get marketplace plugin details', async () => {
|
|
371
|
+
const cookie = await loginAndGetCookie(app);
|
|
372
|
+
const res = await request(app)
|
|
373
|
+
.get('/api/v1/dashboard/marketplace/cool-plugin')
|
|
374
|
+
.set('Cookie', cookie);
|
|
375
|
+
expect(res.status).toBe(200);
|
|
376
|
+
expect(res.body.data.name).toBe('cool-plugin');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should install from marketplace', async () => {
|
|
380
|
+
const cookie = await loginAndGetCookie(app);
|
|
381
|
+
const res = await request(app)
|
|
382
|
+
.post('/api/v1/dashboard/marketplace/cool-plugin/install')
|
|
383
|
+
.set('Cookie', cookie);
|
|
384
|
+
expect(res.status).toBe(200);
|
|
385
|
+
expect(res.body.data.success).toBe(true);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|