groove-dev 0.16.3 → 0.17.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.
Files changed (38) hide show
  1. package/README.md +18 -16
  2. package/node_modules/@groove-dev/daemon/integrations-registry.json +321 -0
  3. package/node_modules/@groove-dev/daemon/src/api.js +152 -0
  4. package/node_modules/@groove-dev/daemon/src/index.js +13 -1
  5. package/node_modules/@groove-dev/daemon/src/integrations.js +389 -0
  6. package/node_modules/@groove-dev/daemon/src/introducer.js +23 -0
  7. package/node_modules/@groove-dev/daemon/src/process.js +59 -0
  8. package/node_modules/@groove-dev/daemon/src/registry.js +2 -1
  9. package/node_modules/@groove-dev/daemon/src/scheduler.js +336 -0
  10. package/node_modules/@groove-dev/daemon/src/terminal-pty.js +119 -54
  11. package/node_modules/@groove-dev/daemon/src/validate.js +10 -0
  12. package/node_modules/@groove-dev/gui/dist/assets/index-C5k-qSwi.js +153 -0
  13. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  14. package/node_modules/@groove-dev/gui/src/App.jsx +6 -0
  15. package/node_modules/@groove-dev/gui/src/components/SpawnPanel.jsx +98 -7
  16. package/node_modules/@groove-dev/gui/src/components/Terminal.jsx +29 -12
  17. package/node_modules/@groove-dev/gui/src/views/IntegrationsStore.jsx +954 -0
  18. package/node_modules/@groove-dev/gui/src/views/ScheduleManager.jsx +614 -0
  19. package/package.json +2 -2
  20. package/packages/daemon/integrations-registry.json +321 -0
  21. package/packages/daemon/src/api.js +152 -0
  22. package/packages/daemon/src/index.js +13 -1
  23. package/packages/daemon/src/integrations.js +389 -0
  24. package/packages/daemon/src/introducer.js +23 -0
  25. package/packages/daemon/src/process.js +59 -0
  26. package/packages/daemon/src/registry.js +2 -1
  27. package/packages/daemon/src/scheduler.js +336 -0
  28. package/packages/daemon/src/terminal-pty.js +119 -54
  29. package/packages/daemon/src/validate.js +10 -0
  30. package/packages/gui/dist/assets/index-C5k-qSwi.js +153 -0
  31. package/packages/gui/dist/index.html +1 -1
  32. package/packages/gui/src/App.jsx +6 -0
  33. package/packages/gui/src/components/SpawnPanel.jsx +98 -7
  34. package/packages/gui/src/components/Terminal.jsx +29 -12
  35. package/packages/gui/src/views/IntegrationsStore.jsx +954 -0
  36. package/packages/gui/src/views/ScheduleManager.jsx +614 -0
  37. package/node_modules/@groove-dev/gui/dist/assets/index-CFeltwTB.js +0 -153
  38. package/packages/gui/dist/assets/index-CFeltwTB.js +0 -153
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # GROOVE
1
+ # groove
2
2
 
3
3
  **Orchestrate your AI coding agents. Stop losing context.**
4
4
 
@@ -12,7 +12,7 @@ npm i -g groove-dev
12
12
  groove start
13
13
  ```
14
14
 
15
- The GUI opens at `http://localhost:31415`. On a VPS? GROOVE detects it and tells you exactly what to do.
15
+ The GUI opens at `http://localhost:31415`. On a VPS? groove detects it and tells you exactly what to do.
16
16
 
17
17
  ---
18
18
 
@@ -27,7 +27,7 @@ AI coding agents waste your money and lose their way:
27
27
 
28
28
  ## The Solution
29
29
 
30
- GROOVE sits between you and your AI coding agents. It doesn't replace them — it makes them work together.
30
+ groove sits between you and your AI coding agents. It doesn't replace them — it makes them work together.
31
31
 
32
32
  ### Zero Cold-Start (The Journalist)
33
33
 
@@ -35,7 +35,7 @@ A background AI continuously watches all agent activity and synthesizes it into
35
35
 
36
36
  ### Infinite Sessions (Context Rotation)
37
37
 
38
- Instead of letting agents fill their context window until they degrade, GROOVE detects quality decline — error spikes, circular refactors, file churn — and automatically rotates: kill the session, spawn fresh, feed it the Journalist's context. The agent picks up exactly where it left off with a clean window. No compaction. No drift. Works at any codebase scale.
38
+ Instead of letting agents fill their context window until they degrade, groove detects quality decline — error spikes, circular refactors, file churn — and automatically rotates: kill the session, spawn fresh, feed it the Journalist's context. The agent picks up exactly where it left off with a clean window. No compaction. No drift. Works at any codebase scale.
39
39
 
40
40
  ### AI Project Manager
41
41
 
@@ -47,7 +47,7 @@ Spawn a planner, describe your project. The planner writes a detailed plan and r
47
47
 
48
48
  ### Workspaces (Large Codebase Support)
49
49
 
50
- GROOVE auto-detects monorepo workspaces (npm, pnpm, lerna) and lets you spawn each agent in its own subdirectory. A frontend agent only sees `packages/frontend/`. A backend agent only sees `packages/backend/`. No wasted context on irrelevant code.
50
+ groove auto-detects monorepo workspaces (npm, pnpm, lerna) and lets you spawn each agent in its own subdirectory. A frontend agent only sees `packages/frontend/`. A backend agent only sees `packages/backend/`. No wasted context on irrelevant code.
51
51
 
52
52
  - **Codebase indexer** — scans project structure on start, gives every agent instant orientation
53
53
  - **Architecture injection** — auto-detects `ARCHITECTURE.md` and injects it into every agent's context
@@ -63,16 +63,16 @@ GROOVE auto-detects monorepo workspaces (npm, pnpm, lerna) and lets you spawn ea
63
63
 
64
64
  ## Remote Access
65
65
 
66
- Run GROOVE on a VPS and manage your agents from anywhere. No ports exposed to the internet. No tokens. No custom auth code. Zero attack surface.
66
+ Run groove on a VPS and manage your agents from anywhere. No ports exposed to the internet. No tokens. No custom auth code. Zero attack surface.
67
67
 
68
68
  ### How It Works
69
69
 
70
- GROOVE never opens ports to the public internet. Instead, it uses battle-tested transport layers — SSH tunnels and WireGuard (Tailscale) — to keep your daemon private.
70
+ groove never opens ports to the public internet. Instead, it uses battle-tested transport layers — SSH tunnels and WireGuard (Tailscale) — to keep your daemon private.
71
71
 
72
72
  ```
73
73
  Your laptop Your VPS
74
74
  ┌──────────┐ SSH tunnel ┌──────────────────┐
75
- │ Browser │ ◄────────────────► │ GROOVE daemon │
75
+ │ Browser │ ◄────────────────► │ groove daemon │
76
76
  │ localhost│ encrypted │ 127.0.0.1:31415 │
77
77
  └──────────┘ └──────────────────┘
78
78
  Zero open ports
@@ -99,7 +99,7 @@ groove disconnect # close when done
99
99
 
100
100
  That's it. The GUI opens in your browser automatically.
101
101
 
102
- GROOVE auto-detects your environment — VS Code Remote, plain SSH, or local — and tells you exactly what to do. SSH config aliases work too: `groove connect my-vps`.
102
+ groove auto-detects your environment — VS Code Remote, plain SSH, or local — and tells you exactly what to do. SSH config aliases work too: `groove connect my-vps`.
103
103
 
104
104
  ### Tailscale / LAN Access
105
105
 
@@ -114,7 +114,7 @@ Open `http://<ip>:31415` from any device on the same network. Tailscale handles
114
114
 
115
115
  ### What's Blocked
116
116
 
117
- GROOVE will reject any attempt to expose the daemon directly to the internet:
117
+ groove will reject any attempt to expose the daemon directly to the internet:
118
118
 
119
119
  ```bash
120
120
  groove start --host 0.0.0.0 # REJECTED — not allowed
@@ -124,7 +124,7 @@ This is by design. Direct exposure requires custom auth, rate limiting, TLS mana
124
124
 
125
125
  ### Federation (Preview)
126
126
 
127
- Pair GROOVE daemons across machines with Ed25519 key exchange. The security layer is built — cross-server agent coordination (typed contracts, federated registry) is coming soon.
127
+ Pair groove daemons across machines with Ed25519 key exchange. The security layer is built — cross-server agent coordination (typed contracts, federated registry) is coming soon.
128
128
 
129
129
  ```bash
130
130
  groove federation pair 100.64.1.5 # pair two daemons
@@ -137,7 +137,7 @@ Every cross-server message is signed with Ed25519 keys generated during a pairin
137
137
  ```
138
138
  Server A Server B
139
139
  ┌──────────────┐ signed contract ┌──────────────┐
140
- GROOVE daemon│ ◄────────────────►│ GROOVE daemon│
140
+ groove daemon│ ◄────────────────►│ groove daemon│
141
141
  │ Ed25519 key │ verify + audit │ Ed25519 key │
142
142
  └──────────────┘ └──────────────┘
143
143
  ```
@@ -176,7 +176,7 @@ Append-only, `0600` permissions, auto-rotates at 5MB. When team auth is added, e
176
176
 
177
177
  **What we explicitly don't defend against:** Compromised SSH keys, root access to VPS, malicious AI provider responses (out of scope — we're a process manager).
178
178
 
179
- **The principle:** "There's nothing to attack" is better than "we have a security system and here's why it's good." GROOVE has zero auth code. The transport layer does all the work.
179
+ **The principle:** "There's nothing to attack" is better than "we have a security system and here's why it's good." groove has zero auth code. The transport layer does all the work.
180
180
 
181
181
  ---
182
182
 
@@ -189,7 +189,7 @@ Append-only, `0600` permissions, auto-rotates at 5MB. When team auth is added, e
189
189
  | **Gemini CLI** | API Key | 3.1 Pro, 3 Flash, 3.1 Flash Lite, 2.5 Pro, 2.5 Flash |
190
190
  | **Ollama** | Local | Any |
191
191
 
192
- GROOVE is a process manager — it spawns actual AI tool binaries. It never proxies API calls, never touches OAuth tokens, never impersonates any client. Your AI tools talk directly to their servers.
192
+ groove is a process manager — it spawns actual AI tool binaries. It never proxies API calls, never touches OAuth tokens, never impersonates any client. Your AI tools talk directly to their servers.
193
193
 
194
194
  Works in any terminal, any IDE, any OS. Technical and non-technical users alike.
195
195
 
@@ -198,15 +198,17 @@ Works in any terminal, any IDE, any OS. Technical and non-technical users alike.
198
198
  Open the dashboard after starting the daemon (local or remote):
199
199
 
200
200
  - **Agent Tree** — visual node graph with Bezier spline connections, role badges, live status
201
+ - **File Editor** — CodeMirror 6 with syntax highlighting, file tree, tabs, media viewer, and embedded terminal
201
202
  - **Chat** — instruct agents, query without disrupting, continue completed agents, streaming text
202
203
  - **Command Center** — gauge charts, live telemetry, token savings, model routing, adaptive thresholds
203
204
  - **Quick Launch** — planner recommends team, one-click to spawn all
205
+ - **Skills Store** — app-store marketplace for agent skills with ratings and verification
204
206
  - **PM Review Log** — full audit trail of AI Project Manager decisions
205
207
  - **Team Management** — save, load, export, import agent configurations
206
208
 
207
209
  ## Adaptive Model Routing
208
210
 
209
- GROOVE routes tasks to the cheapest model that can handle them. Planners get Opus (deep reasoning). Backends get Sonnet (balanced). Docs get Haiku (fast and cheap). The classifier learns from agent activity and adjusts over time.
211
+ groove routes tasks to the cheapest model that can handle them. Planners get Opus (deep reasoning). Backends get Sonnet (balanced). Docs get Haiku (fast and cheap). The classifier learns from agent activity and adjusts over time.
210
212
 
211
213
  | Tier | Cost | Used For |
212
214
  |------|------|----------|
@@ -218,7 +220,7 @@ GROOVE routes tasks to the cheapest model that can handle them. Planners get Opu
218
220
 
219
221
  ```
220
222
  ┌──────────────────────────────────────────────┐
221
- GROOVE DAEMON (:31415) │
223
+ groove DAEMON (:31415) │
222
224
  │ │
223
225
  │ Registry · Introducer · Lock Manager │
224
226
  │ Journalist · Rotator · Adaptive · Indexer │
@@ -0,0 +1,321 @@
1
+ [
2
+ {
3
+ "id": "slack",
4
+ "name": "Slack",
5
+ "description": "Send and read messages, manage channels, search history, post reactions and thread replies",
6
+ "category": "communication",
7
+ "icon": "slack",
8
+ "tags": ["chat", "messaging", "notifications", "team"],
9
+ "roles": ["cmo", "ea", "support", "fullstack"],
10
+ "npmPackage": "@modelcontextprotocol/server-slack",
11
+ "transport": "stdio",
12
+ "command": "npx",
13
+ "args": ["-y", "@modelcontextprotocol/server-slack"],
14
+ "envKeys": [
15
+ { "key": "SLACK_BOT_TOKEN", "label": "Bot Token", "placeholder": "xoxb-...", "required": true },
16
+ { "key": "SLACK_TEAM_ID", "label": "Team ID", "placeholder": "T01234567", "required": true }
17
+ ],
18
+ "featured": true,
19
+ "downloads": 0,
20
+ "rating": 0,
21
+ "ratingCount": 0,
22
+ "verified": "mcp-official"
23
+ },
24
+ {
25
+ "id": "github",
26
+ "name": "GitHub",
27
+ "description": "Manage repos, issues, pull requests, code search, file operations, and workflows",
28
+ "category": "developer",
29
+ "icon": "github",
30
+ "tags": ["git", "repos", "issues", "pull-requests", "ci"],
31
+ "roles": ["backend", "frontend", "fullstack", "devops"],
32
+ "npmPackage": "@modelcontextprotocol/server-github",
33
+ "transport": "stdio",
34
+ "command": "npx",
35
+ "args": ["-y", "@modelcontextprotocol/server-github"],
36
+ "envKeys": [
37
+ { "key": "GITHUB_PERSONAL_ACCESS_TOKEN", "label": "Personal Access Token", "placeholder": "ghp_...", "required": true }
38
+ ],
39
+ "featured": true,
40
+ "downloads": 0,
41
+ "rating": 0,
42
+ "ratingCount": 0,
43
+ "verified": "mcp-official"
44
+ },
45
+ {
46
+ "id": "stripe",
47
+ "name": "Stripe",
48
+ "description": "Manage customers, payments, subscriptions, invoices, and financial data",
49
+ "category": "finance",
50
+ "icon": "stripe",
51
+ "tags": ["payments", "billing", "subscriptions", "finance"],
52
+ "roles": ["cfo", "analyst", "fullstack"],
53
+ "npmPackage": "@stripe/agent-toolkit",
54
+ "transport": "stdio",
55
+ "command": "npx",
56
+ "args": ["-y", "@stripe/agent-toolkit", "mcp"],
57
+ "envKeys": [
58
+ { "key": "STRIPE_SECRET_KEY", "label": "Secret Key", "placeholder": "sk_...", "required": true }
59
+ ],
60
+ "featured": true,
61
+ "downloads": 0,
62
+ "rating": 0,
63
+ "ratingCount": 0,
64
+ "verified": "verified"
65
+ },
66
+ {
67
+ "id": "google-calendar",
68
+ "name": "Google Calendar",
69
+ "description": "Create, list, and update calendar events, check availability, manage schedules",
70
+ "category": "productivity",
71
+ "icon": "calendar",
72
+ "tags": ["calendar", "scheduling", "events", "meetings"],
73
+ "roles": ["ea", "cmo"],
74
+ "npmPackage": "@anthropic-ai/mcp-server-google-calendar",
75
+ "transport": "stdio",
76
+ "command": "npx",
77
+ "args": ["-y", "@anthropic-ai/mcp-server-google-calendar"],
78
+ "envKeys": [
79
+ { "key": "GOOGLE_CLIENT_ID", "label": "OAuth Client ID", "required": true },
80
+ { "key": "GOOGLE_CLIENT_SECRET", "label": "OAuth Client Secret", "required": true },
81
+ { "key": "GOOGLE_REFRESH_TOKEN", "label": "Refresh Token", "required": true }
82
+ ],
83
+ "featured": false,
84
+ "downloads": 0,
85
+ "rating": 0,
86
+ "ratingCount": 0,
87
+ "verified": "community"
88
+ },
89
+ {
90
+ "id": "gmail",
91
+ "name": "Gmail",
92
+ "description": "Send, read, search, and draft emails via Gmail",
93
+ "category": "communication",
94
+ "icon": "email",
95
+ "tags": ["email", "inbox", "messaging", "notifications"],
96
+ "roles": ["ea", "cmo", "support"],
97
+ "npmPackage": "@anthropic-ai/mcp-server-gmail",
98
+ "transport": "stdio",
99
+ "command": "npx",
100
+ "args": ["-y", "@anthropic-ai/mcp-server-gmail"],
101
+ "envKeys": [
102
+ { "key": "GOOGLE_CLIENT_ID", "label": "OAuth Client ID", "required": true },
103
+ { "key": "GOOGLE_CLIENT_SECRET", "label": "OAuth Client Secret", "required": true },
104
+ { "key": "GOOGLE_REFRESH_TOKEN", "label": "Refresh Token", "required": true }
105
+ ],
106
+ "featured": false,
107
+ "downloads": 0,
108
+ "rating": 0,
109
+ "ratingCount": 0,
110
+ "verified": "community"
111
+ },
112
+ {
113
+ "id": "postgres",
114
+ "name": "PostgreSQL",
115
+ "description": "Query databases, inspect schemas, run SQL, and analyze data",
116
+ "category": "database",
117
+ "icon": "database",
118
+ "tags": ["sql", "database", "queries", "analytics"],
119
+ "roles": ["analyst", "backend", "cfo"],
120
+ "npmPackage": "@modelcontextprotocol/server-postgres",
121
+ "transport": "stdio",
122
+ "command": "npx",
123
+ "args": ["-y", "@modelcontextprotocol/server-postgres"],
124
+ "envKeys": [
125
+ { "key": "POSTGRES_CONNECTION_STRING", "label": "Connection String", "placeholder": "postgresql://user:pass@host:5432/db", "required": true }
126
+ ],
127
+ "featured": false,
128
+ "downloads": 0,
129
+ "rating": 0,
130
+ "ratingCount": 0,
131
+ "verified": "mcp-official"
132
+ },
133
+ {
134
+ "id": "brave-search",
135
+ "name": "Brave Search",
136
+ "description": "Web search and local search via Brave Search API",
137
+ "category": "analytics",
138
+ "icon": "search",
139
+ "tags": ["search", "web", "research", "data"],
140
+ "roles": ["cmo", "analyst", "planner"],
141
+ "npmPackage": "@modelcontextprotocol/server-brave-search",
142
+ "transport": "stdio",
143
+ "command": "npx",
144
+ "args": ["-y", "@modelcontextprotocol/server-brave-search"],
145
+ "envKeys": [
146
+ { "key": "BRAVE_API_KEY", "label": "API Key", "placeholder": "BSA...", "required": true }
147
+ ],
148
+ "featured": false,
149
+ "downloads": 0,
150
+ "rating": 0,
151
+ "ratingCount": 0,
152
+ "verified": "mcp-official"
153
+ },
154
+ {
155
+ "id": "google-drive",
156
+ "name": "Google Drive",
157
+ "description": "Search, read, and manage files in Google Drive",
158
+ "category": "productivity",
159
+ "icon": "drive",
160
+ "tags": ["files", "documents", "sheets", "storage"],
161
+ "roles": ["ea", "analyst", "cmo"],
162
+ "npmPackage": "@modelcontextprotocol/server-gdrive",
163
+ "transport": "stdio",
164
+ "command": "npx",
165
+ "args": ["-y", "@modelcontextprotocol/server-gdrive"],
166
+ "envKeys": [
167
+ { "key": "GOOGLE_CLIENT_ID", "label": "OAuth Client ID", "required": true },
168
+ { "key": "GOOGLE_CLIENT_SECRET", "label": "OAuth Client Secret", "required": true },
169
+ { "key": "GOOGLE_REFRESH_TOKEN", "label": "Refresh Token", "required": true }
170
+ ],
171
+ "featured": false,
172
+ "downloads": 0,
173
+ "rating": 0,
174
+ "ratingCount": 0,
175
+ "verified": "mcp-official"
176
+ },
177
+ {
178
+ "id": "linear",
179
+ "name": "Linear",
180
+ "description": "Create and manage issues, projects, and sprints in Linear",
181
+ "category": "productivity",
182
+ "icon": "linear",
183
+ "tags": ["issues", "project-management", "tickets", "sprints"],
184
+ "roles": ["fullstack", "backend", "devops"],
185
+ "npmPackage": "@ibraheem4/linear-mcp-server",
186
+ "transport": "stdio",
187
+ "command": "npx",
188
+ "args": ["-y", "@ibraheem4/linear-mcp-server"],
189
+ "envKeys": [
190
+ { "key": "LINEAR_API_KEY", "label": "API Key", "required": true }
191
+ ],
192
+ "featured": false,
193
+ "downloads": 0,
194
+ "rating": 0,
195
+ "ratingCount": 0,
196
+ "verified": "community"
197
+ },
198
+ {
199
+ "id": "notion",
200
+ "name": "Notion",
201
+ "description": "Read, create, and update Notion pages and databases",
202
+ "category": "productivity",
203
+ "icon": "notion",
204
+ "tags": ["wiki", "docs", "notes", "databases"],
205
+ "roles": ["ea", "cmo", "analyst"],
206
+ "npmPackage": "@notionhq/notion-mcp-server",
207
+ "transport": "stdio",
208
+ "command": "npx",
209
+ "args": ["-y", "@notionhq/notion-mcp-server"],
210
+ "envKeys": [
211
+ { "key": "NOTION_API_KEY", "label": "Integration Token", "placeholder": "ntn_...", "required": true }
212
+ ],
213
+ "featured": false,
214
+ "downloads": 0,
215
+ "rating": 0,
216
+ "ratingCount": 0,
217
+ "verified": "community"
218
+ },
219
+ {
220
+ "id": "discord",
221
+ "name": "Discord",
222
+ "description": "Send and read messages, manage channels and servers on Discord",
223
+ "category": "communication",
224
+ "icon": "discord",
225
+ "tags": ["chat", "community", "messaging", "voice"],
226
+ "roles": ["cmo", "support"],
227
+ "npmPackage": "mcp-discord",
228
+ "transport": "stdio",
229
+ "command": "npx",
230
+ "args": ["-y", "mcp-discord"],
231
+ "envKeys": [
232
+ { "key": "DISCORD_BOT_TOKEN", "label": "Bot Token", "required": true }
233
+ ],
234
+ "featured": false,
235
+ "downloads": 0,
236
+ "rating": 0,
237
+ "ratingCount": 0,
238
+ "verified": "community"
239
+ },
240
+ {
241
+ "id": "home-assistant",
242
+ "name": "Home Assistant",
243
+ "description": "Control smart home devices, query states, and manage automations",
244
+ "category": "smart-home",
245
+ "icon": "home",
246
+ "tags": ["iot", "smart-home", "automation", "devices"],
247
+ "roles": ["home"],
248
+ "npmPackage": "mcp-home-assistant",
249
+ "transport": "stdio",
250
+ "command": "npx",
251
+ "args": ["-y", "mcp-home-assistant"],
252
+ "envKeys": [
253
+ { "key": "HOME_ASSISTANT_URL", "label": "HA URL", "placeholder": "http://homeassistant.local:8123", "required": true },
254
+ { "key": "HOME_ASSISTANT_TOKEN", "label": "Long-Lived Access Token", "required": true }
255
+ ],
256
+ "featured": false,
257
+ "downloads": 0,
258
+ "rating": 0,
259
+ "ratingCount": 0,
260
+ "verified": "community"
261
+ },
262
+ {
263
+ "id": "filesystem",
264
+ "name": "Filesystem",
265
+ "description": "Read, write, search, and manage files on the local filesystem",
266
+ "category": "developer",
267
+ "icon": "folder",
268
+ "tags": ["files", "filesystem", "local", "storage"],
269
+ "roles": ["backend", "fullstack", "devops"],
270
+ "npmPackage": "@modelcontextprotocol/server-filesystem",
271
+ "transport": "stdio",
272
+ "command": "npx",
273
+ "args": ["-y", "@modelcontextprotocol/server-filesystem"],
274
+ "envKeys": [],
275
+ "featured": false,
276
+ "downloads": 0,
277
+ "rating": 0,
278
+ "ratingCount": 0,
279
+ "verified": "mcp-official"
280
+ },
281
+ {
282
+ "id": "google-maps",
283
+ "name": "Google Maps",
284
+ "description": "Geocoding, directions, place search, and distance calculations",
285
+ "category": "analytics",
286
+ "icon": "map",
287
+ "tags": ["maps", "location", "directions", "places"],
288
+ "roles": ["analyst", "ea"],
289
+ "npmPackage": "@modelcontextprotocol/server-google-maps",
290
+ "transport": "stdio",
291
+ "command": "npx",
292
+ "args": ["-y", "@modelcontextprotocol/server-google-maps"],
293
+ "envKeys": [
294
+ { "key": "GOOGLE_MAPS_API_KEY", "label": "API Key", "required": true }
295
+ ],
296
+ "featured": false,
297
+ "downloads": 0,
298
+ "rating": 0,
299
+ "ratingCount": 0,
300
+ "verified": "mcp-official"
301
+ },
302
+ {
303
+ "id": "sqlite",
304
+ "name": "SQLite",
305
+ "description": "Query and manage SQLite databases, inspect schemas, run SQL",
306
+ "category": "database",
307
+ "icon": "database",
308
+ "tags": ["sql", "database", "local", "lightweight"],
309
+ "roles": ["analyst", "backend"],
310
+ "npmPackage": "@modelcontextprotocol/server-sqlite",
311
+ "transport": "stdio",
312
+ "command": "npx",
313
+ "args": ["-y", "@modelcontextprotocol/server-sqlite"],
314
+ "envKeys": [],
315
+ "featured": false,
316
+ "downloads": 0,
317
+ "rating": 0,
318
+ "ratingCount": 0,
319
+ "verified": "mcp-official"
320
+ }
321
+ ]
@@ -444,6 +444,158 @@ export function createApi(app, daemon) {
444
444
  res.json({ id: agent.id, skills });
445
445
  });
446
446
 
447
+ // --- Integrations ---
448
+
449
+ app.get('/api/integrations/registry', async (req, res) => {
450
+ const integrations = await daemon.integrations.getRegistry({
451
+ search: req.query.search || '',
452
+ category: req.query.category || 'all',
453
+ });
454
+ res.json({
455
+ integrations,
456
+ categories: daemon.integrations.getCategories(),
457
+ });
458
+ });
459
+
460
+ app.get('/api/integrations/installed', (req, res) => {
461
+ res.json(daemon.integrations.getInstalled());
462
+ });
463
+
464
+ app.post('/api/integrations/:id/install', async (req, res) => {
465
+ try {
466
+ const result = await daemon.integrations.install(req.params.id);
467
+ res.json(result);
468
+ } catch (err) {
469
+ res.status(400).json({ error: err.message });
470
+ }
471
+ });
472
+
473
+ app.delete('/api/integrations/:id', async (req, res) => {
474
+ try {
475
+ const result = await daemon.integrations.uninstall(req.params.id);
476
+ res.json(result);
477
+ } catch (err) {
478
+ res.status(400).json({ error: err.message });
479
+ }
480
+ });
481
+
482
+ app.get('/api/integrations/:id/status', (req, res) => {
483
+ const status = daemon.integrations.getStatus(req.params.id);
484
+ if (!status) return res.status(404).json({ error: 'Integration not found' });
485
+ res.json(status);
486
+ });
487
+
488
+ app.post('/api/integrations/:id/credentials', (req, res) => {
489
+ try {
490
+ const { key, value } = req.body || {};
491
+ if (!key || !value) return res.status(400).json({ error: 'key and value are required' });
492
+ daemon.integrations.setCredential(req.params.id, key, value);
493
+ res.json({ ok: true });
494
+ } catch (err) {
495
+ res.status(400).json({ error: err.message });
496
+ }
497
+ });
498
+
499
+ app.delete('/api/integrations/:id/credentials/:key', (req, res) => {
500
+ try {
501
+ daemon.integrations.deleteCredential(req.params.id, req.params.key);
502
+ res.json({ ok: true });
503
+ } catch (err) {
504
+ res.status(400).json({ error: err.message });
505
+ }
506
+ });
507
+
508
+ // --- Agent Integrations (attach/detach) ---
509
+
510
+ app.post('/api/agents/:agentId/integrations/:integrationId', (req, res) => {
511
+ const agent = daemon.registry.get(req.params.agentId);
512
+ if (!agent) return res.status(404).json({ error: 'Agent not found' });
513
+ const integrationId = req.params.integrationId;
514
+ if (!daemon.integrations._isInstalled(integrationId)) {
515
+ return res.status(400).json({ error: 'Integration not installed. Install it first.' });
516
+ }
517
+ const integrations = agent.integrations || [];
518
+ if (integrations.includes(integrationId)) {
519
+ return res.json({ id: agent.id, integrations });
520
+ }
521
+ daemon.registry.update(agent.id, { integrations: [...integrations, integrationId] });
522
+ daemon.audit.log('integration.attach', { agentId: agent.id, integrationId });
523
+ res.json({ id: agent.id, integrations: [...integrations, integrationId] });
524
+ });
525
+
526
+ app.delete('/api/agents/:agentId/integrations/:integrationId', (req, res) => {
527
+ const agent = daemon.registry.get(req.params.agentId);
528
+ if (!agent) return res.status(404).json({ error: 'Agent not found' });
529
+ const integrations = (agent.integrations || []).filter((s) => s !== req.params.integrationId);
530
+ daemon.registry.update(agent.id, { integrations });
531
+ daemon.audit.log('integration.detach', { agentId: agent.id, integrationId: req.params.integrationId });
532
+ res.json({ id: agent.id, integrations });
533
+ });
534
+
535
+ // --- Schedules ---
536
+
537
+ app.get('/api/schedules', (req, res) => {
538
+ res.json(daemon.scheduler.list());
539
+ });
540
+
541
+ app.post('/api/schedules', (req, res) => {
542
+ try {
543
+ const schedule = daemon.scheduler.create(req.body);
544
+ res.status(201).json(schedule);
545
+ } catch (err) {
546
+ res.status(400).json({ error: err.message });
547
+ }
548
+ });
549
+
550
+ app.get('/api/schedules/:id', (req, res) => {
551
+ const schedule = daemon.scheduler.get(req.params.id);
552
+ if (!schedule) return res.status(404).json({ error: 'Schedule not found' });
553
+ res.json(schedule);
554
+ });
555
+
556
+ app.patch('/api/schedules/:id', (req, res) => {
557
+ try {
558
+ const schedule = daemon.scheduler.update(req.params.id, req.body);
559
+ res.json(schedule);
560
+ } catch (err) {
561
+ res.status(400).json({ error: err.message });
562
+ }
563
+ });
564
+
565
+ app.delete('/api/schedules/:id', (req, res) => {
566
+ try {
567
+ daemon.scheduler.delete(req.params.id);
568
+ res.json({ ok: true });
569
+ } catch (err) {
570
+ res.status(400).json({ error: err.message });
571
+ }
572
+ });
573
+
574
+ app.post('/api/schedules/:id/enable', (req, res) => {
575
+ try {
576
+ res.json(daemon.scheduler.enable(req.params.id));
577
+ } catch (err) {
578
+ res.status(400).json({ error: err.message });
579
+ }
580
+ });
581
+
582
+ app.post('/api/schedules/:id/disable', (req, res) => {
583
+ try {
584
+ res.json(daemon.scheduler.disable(req.params.id));
585
+ } catch (err) {
586
+ res.status(400).json({ error: err.message });
587
+ }
588
+ });
589
+
590
+ app.post('/api/schedules/:id/run', async (req, res) => {
591
+ try {
592
+ const agent = await daemon.scheduler.run(req.params.id);
593
+ res.json({ ok: true, agentId: agent.id });
594
+ } catch (err) {
595
+ res.status(400).json({ error: err.message });
596
+ }
597
+ });
598
+
447
599
  // --- Directory Browser ---
448
600
 
449
601
  app.get('/api/browse', (req, res) => {