groove-dev 0.27.27 → 0.27.28

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 (32) hide show
  1. package/.groove-staging/state.json +3 -0
  2. package/.groove-staging/timeline.json +13 -0
  3. package/DECENTRALIZED_NET_WP_V1.md +871 -0
  4. package/README.md +28 -0
  5. package/decentralized-net/ACTION_PLAN.md +422 -0
  6. package/node_modules/@groove-dev/cli/package.json +1 -1
  7. package/node_modules/@groove-dev/daemon/package.json +1 -1
  8. package/node_modules/@groove-dev/daemon/src/api.js +99 -0
  9. package/node_modules/@groove-dev/daemon/src/process.js +12 -0
  10. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +26 -1
  11. package/node_modules/@groove-dev/gui/dist/assets/{index-DieCV-v1.js → index-Ch1N9G4Z.js} +1728 -1728
  12. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  13. package/node_modules/@groove-dev/gui/package.json +1 -1
  14. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +147 -21
  15. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +206 -44
  16. package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +11 -24
  17. package/node_modules/@groove-dev/gui/src/components/marketplace/marketplace-card.jsx +1 -36
  18. package/node_modules/@groove-dev/gui/src/lib/integration-logos.js +39 -0
  19. package/package.json +1 -1
  20. package/packages/cli/package.json +1 -1
  21. package/packages/daemon/package.json +1 -1
  22. package/packages/daemon/src/api.js +99 -0
  23. package/packages/daemon/src/process.js +12 -0
  24. package/packages/daemon/src/providers/claude-code.js +26 -1
  25. package/packages/gui/dist/assets/{index-DieCV-v1.js → index-Ch1N9G4Z.js} +1728 -1728
  26. package/packages/gui/dist/index.html +1 -1
  27. package/packages/gui/package.json +1 -1
  28. package/packages/gui/src/components/agents/agent-config.jsx +147 -21
  29. package/packages/gui/src/components/agents/spawn-wizard.jsx +206 -44
  30. package/packages/gui/src/components/marketplace/integration-wizard.jsx +11 -24
  31. package/packages/gui/src/components/marketplace/marketplace-card.jsx +1 -36
  32. package/packages/gui/src/lib/integration-logos.js +39 -0
package/README.md CHANGED
@@ -7,6 +7,20 @@ The open-source orchestration layer for AI coding tools. Spawn teams of agents,
7
7
  [![npm](https://img.shields.io/npm/v/groove-dev)](https://www.npmjs.com/package/groove-dev)
8
8
  [![License](https://img.shields.io/badge/license-FSL--1.1--Apache--2.0-blue)](LICENSE)
9
9
 
10
+ ## Get Started
11
+
12
+ ### Download the App
13
+
14
+ No terminal needed. Download, install, open a project folder.
15
+
16
+ - [**macOS** (.dmg)](https://github.com/grooveai-dev/groove/releases/latest) — Intel and Apple Silicon
17
+ - [**Windows** (.exe)](https://github.com/grooveai-dev/groove/releases/latest) — one-click installer
18
+ - [**Linux** (.AppImage)](https://github.com/grooveai-dev/groove/releases/latest) — x64 and ARM
19
+
20
+ Everything is bundled — daemon, GUI, auto-updates. No Node.js required.
21
+
22
+ ### Developer Install
23
+
10
24
  ```bash
11
25
  npm i -g groove-dev
12
26
  groove start
@@ -16,6 +30,20 @@ The GUI opens at `http://localhost:31415`. On a VPS? groove detects it and tells
16
30
 
17
31
  ---
18
32
 
33
+ ## Desktop App
34
+
35
+ The desktop app is the easiest way to use groove — no terminal, no dependencies, no setup.
36
+
37
+ - **Full GUI dashboard** — agent tree, chat, editor, telemetry, marketplace, all in one window
38
+ - **Bundled daemon** — starts automatically when you open a project, stops when you close it
39
+ - **System tray** — quick access to recent projects, daemon status, and controls
40
+ - **Automatic updates** — new versions install silently in the background
41
+ - **Multi-workspace** — open any project folder; manages one daemon per project
42
+ - **Platform support** — macOS (Intel + Apple Silicon), Windows (x64 + ARM64), Linux (x64 + ARM64)
43
+ - **No dependencies** — everything is bundled. No Node.js, no terminal knowledge needed
44
+
45
+ ---
46
+
19
47
  ## The Problem
20
48
 
21
49
  AI coding agents waste your money and lose their way:
@@ -0,0 +1,422 @@
1
+ Groove Decentralized Net — Layer 1 Action Plan
2
+ Proving the Inference Pipeline on a Local Network
3
+ ================================================================================
4
+
5
+
6
+ HARDWARE MAP
7
+ ================================================================================
8
+
9
+ Machine 1 — MacBook Air M1 (8GB)
10
+ Role: Consumer node / "phone simulator"
11
+ Runs: 1B-3B draft model for speculative decoding
12
+ Also runs: The orchestration layer (session init, relay logic)
13
+ Why: 8GB is too small for large model shards but perfect for testing the
14
+ consumer experience — this IS the phone equivalent
15
+
16
+ Machine 2 — iMac (TBD specs, likely M-series with 16-32GB)
17
+ Role: Compute Node A
18
+ Runs: First half of model layers (e.g., Layers 0-15 of a 32-layer model)
19
+ Why: Apple Silicon is fast at inference via MLX/llama.cpp
20
+
21
+ Machine 3 — GPU PC (TBD specs, likely NVIDIA with 8-24GB VRAM)
22
+ Role: Compute Node B
23
+ Runs: Second half of model layers (e.g., Layers 16-31)
24
+ Why: CUDA is the fastest inference runtime for NVIDIA hardware
25
+
26
+ Action item: Document the exact specs of each machine before starting.
27
+ Run on iMac: system_profiler SPHardwareDataType
28
+ Run on GPU PC: nvidia-smi (for GPU) + systeminfo or lscpu (for CPU/RAM)
29
+
30
+
31
+ TECH STACK FOR THE POC
32
+ ================================================================================
33
+
34
+ Language: Python 3.11+
35
+ Why: PyTorch, transformers, and all ML tooling is Python-native. The
36
+ production Groove daemon is Node.js, but the inference layer will always
37
+ be Python. No point fighting that.
38
+
39
+ Model Loading: HuggingFace transformers + PyTorch
40
+ Why: Full control over which layers load on which machine. You can
41
+ load a subset of layers by manipulating the model's state_dict.
42
+ Alternative: llama.cpp (faster on Apple Silicon, but harder to split
43
+ layers programmatically). We may migrate to llama.cpp in Sprint 3.
44
+
45
+ Networking: WebSocket (websockets library)
46
+ Why: Simple, bidirectional, low overhead for a local network PoC.
47
+ Production will use QUIC, but WebSocket proves the concept without
48
+ the complexity of QUIC NAT traversal on a LAN.
49
+
50
+ Serialization: msgpack or pickle for tensor transfer
51
+ Why: Hidden states are PyTorch tensors (~13KB per token for a 7B model).
52
+ msgpack is fast. Pickle works for trusted local network. We are NOT
53
+ doing JSON — binary tensors need binary serialization.
54
+
55
+ Draft Model: Qwen2.5-0.5B or SmolLM2-1.7B (quantized)
56
+ Why: Fits easily in 8GB on the MacBook Air. Good enough acceptance
57
+ rate for code tasks. Available on HuggingFace.
58
+
59
+
60
+ MODEL SELECTION FOR POC
61
+ ================================================================================
62
+
63
+ Start small, prove the architecture, then scale up.
64
+
65
+ Phase A model: Llama 3.1 8B (or Qwen2.5-7B)
66
+ - 32 transformer layers
67
+ - FP16: ~16GB, Q4: ~4.5GB
68
+ - Split across 2 nodes: ~2.3GB per node at Q4
69
+ - This is trivially small for your hardware — the point is proving
70
+ the sharding and pipeline work, not stressing the GPUs
71
+
72
+ Phase B model (after pipeline works): Llama 3.1 70B or Qwen2.5-32B
73
+ - 80 layers (70B) or 64 layers (32B)
74
+ - This is where you actually NEED sharding — a single consumer machine
75
+ can't hold the full model
76
+ - Split across 2 nodes: 35-40 layers each, ~20GB per node at Q4
77
+
78
+
79
+ PROJECT STRUCTURE
80
+ ================================================================================
81
+
82
+ decentralized-net/
83
+ ACTION_PLAN.md — this file
84
+ DECENTRALIZED_NET_WP_V1.md — (symlink or copy from parent, reference only)
85
+
86
+ src/
87
+ node/ — compute node (runs on iMac + GPU PC)
88
+ server.py — WebSocket server, receives activations, runs layers, returns results
89
+ shard_loader.py — loads a specific layer range from a model
90
+ kv_cache.py — manages KV cache for assigned layers
91
+
92
+ consumer/ — consumer client (runs on MacBook Air)
93
+ client.py — session init, sends prompts, receives tokens
94
+ draft_model.py — local draft model for speculative decoding
95
+ speculative.py — speculative decode loop (generate candidates, verify, display)
96
+
97
+ relay/ — relay / orchestration (runs on MacBook Air for PoC)
98
+ relay.py — pipeline assembly, routing, session management
99
+ pipeline.py — manages the ordered list of nodes, activation forwarding
100
+
101
+ common/
102
+ protocol.py — message types, serialization, shared constants
103
+ tensor_transfer.py — efficient tensor packing/unpacking over WebSocket
104
+
105
+ tests/
106
+ test_shard_loading.py — verify a model can be split and reassembled
107
+ test_pipeline.py — verify activations flow correctly through 2 nodes
108
+ test_speculative.py — verify speculative decode acceptance logic
109
+
110
+ scripts/
111
+ start_node.sh — launch a compute node with specified layer range
112
+ start_consumer.sh — launch the consumer client
113
+ benchmark.py — measure tok/s, latency, acceptance rate
114
+
115
+ requirements.txt — torch, transformers, websockets, msgpack, etc.
116
+
117
+
118
+ ================================================================================
119
+ SPRINT 1 — Single Machine Baseline (2-3 days)
120
+ ================================================================================
121
+
122
+ Goal: Load a model, run inference, measure baseline tok/s.
123
+ Machine: GPU PC (fastest single machine)
124
+
125
+ Tasks:
126
+
127
+ 1.1 Set up Python environment
128
+ - Create venv, install torch + transformers + accelerate
129
+ - Verify CUDA works on GPU PC (torch.cuda.is_available())
130
+ - Verify MPS works on MacBook/iMac (torch.backends.mps.is_available())
131
+
132
+ 1.2 Baseline inference benchmark
133
+ - Load Llama 3.1 8B (or Qwen2.5-7B) in Q4 on GPU PC
134
+ - Run 10 prompts, measure:
135
+ * Time to first token (TTFT)
136
+ * Tokens per second (sustained)
137
+ * Total generation time for 200 tokens
138
+ - Record these numbers — they are your ceiling. Distributed inference
139
+ will always be slower than this. The question is how much slower.
140
+
141
+ 1.3 Understand the model internals
142
+ - Print model.config to see layer count, hidden_dim, num_heads
143
+ - Print model.model.layers to see the ModuleList of transformer layers
144
+ - Calculate hidden state size: hidden_dim * dtype_bytes
145
+ (e.g., 4096 * 2 = 8KB per token for a 7B model at fp16)
146
+ - This tells you exactly how much data transfers between nodes per token
147
+
148
+ 1.4 Manual layer split test (single machine)
149
+ - Load only layers 0-15 on one model instance
150
+ - Load only layers 16-31 on another model instance
151
+ - Run a forward pass: input -> layers 0-15 -> capture hidden state ->
152
+ feed into layers 16-31 -> output logits
153
+ - Verify the output matches a full-model forward pass
154
+ - This proves the model CAN be split. If the hidden states don't match,
155
+ something is wrong with how layers are being loaded.
156
+
157
+ Deliverable: Baseline numbers + confirmed layer splitting works on one machine.
158
+
159
+
160
+ ================================================================================
161
+ SPRINT 2 — Two-Node Sharded Inference (3-5 days)
162
+ ================================================================================
163
+
164
+ Goal: Run inference across two machines on the local network.
165
+ Machines: GPU PC (layers 0-15) + iMac (layers 16-31)
166
+
167
+ Tasks:
168
+
169
+ 2.1 Build the compute node server (node/server.py)
170
+ - WebSocket server that:
171
+ * On startup: loads its assigned layer range
172
+ * On receiving ACTIVATIONS message: runs forward pass through its layers
173
+ * Returns: output hidden states (or logits if it's the final node)
174
+ - Command line: python server.py --model llama-8b --layers 0-15 --port 8765
175
+
176
+ 2.2 Build the activation relay (common/tensor_transfer.py)
177
+ - Serialize PyTorch tensors to bytes efficiently
178
+ - Use torch.save() to BytesIO buffer, or raw .numpy().tobytes()
179
+ - Measure serialization overhead — should be <1ms for a 8KB tensor
180
+ - Deserialize on receiving end, move to correct device (CUDA/MPS/CPU)
181
+
182
+ 2.3 Build a simple consumer client (consumer/client.py)
183
+ - Connects to Node A (GPU PC) and Node B (iMac) via WebSocket
184
+ - Sends tokenized prompt to Node A
185
+ - Node A processes layers 0-15, sends hidden states to Node B
186
+ (for now, routed through the consumer — direct P2P comes later)
187
+ - Node B processes layers 16-31, sends logits back to consumer
188
+ - Consumer samples next token, repeats
189
+
190
+ 2.4 Run the 2-node pipeline
191
+ - Start server.py on GPU PC (layers 0-15)
192
+ - Start server.py on iMac (layers 16-31)
193
+ - Run client.py on MacBook Air
194
+ - Generate 200 tokens, measure:
195
+ * TTFT
196
+ * Tok/s
197
+ * Per-hop latency (time for hidden state transfer)
198
+ * Compare to Sprint 1 baseline
199
+
200
+ 2.5 Implement basic pipeline parallelism
201
+ - Modify the consumer to send token N+1's hidden states to Node A
202
+ while Node B is still processing token N
203
+ - This is the pipelining from Section 4.1 of the whitepaper
204
+ - Measure throughput improvement vs. sequential
205
+
206
+ Expected results:
207
+ - Sequential 2-node: 3-5 tok/s (down from 15-30 baseline, network overhead)
208
+ - Pipelined 2-node: 5-10 tok/s (pipeline fills, bounded by slower node)
209
+ - On a local LAN, hop latency should be 1-3ms — much better than internet
210
+
211
+ Deliverable: Working 2-node inference with measurable pipeline parallelism gain.
212
+
213
+
214
+ ================================================================================
215
+ SPRINT 3 — Three-Node Pipeline (2-3 days)
216
+ ================================================================================
217
+
218
+ Goal: Full 3-node pipeline matching the whitepaper architecture.
219
+ Machines: MacBook Air (consumer) + iMac (layers 0-15) + GPU PC (layers 16-31)
220
+
221
+ Tasks:
222
+
223
+ 3.1 Direct node-to-node activation transfer
224
+ - Currently activations route through the consumer (star topology)
225
+ - Change to: Node A sends directly to Node B (chain topology)
226
+ - Node A needs to know Node B's address — the consumer tells both
227
+ nodes the full pipeline at session start
228
+ - This halves the network hops for activation transfer
229
+
230
+ 3.2 Build the relay/pipeline manager (relay/pipeline.py)
231
+ - Assembles the pipeline: which node runs which layers
232
+ - Sends PIPELINE_CONFIG to each node at session start
233
+ - Monitors heartbeats from each node
234
+ - For now, runs on the MacBook Air alongside the consumer
235
+
236
+ 3.3 Three-node inference
237
+ - Split the 32-layer model into 3 shards:
238
+ * iMac: Layers 0-10
239
+ * GPU PC: Layers 11-21
240
+ * MacBook Air: Layers 22-31 (small shard, fits in 8GB)
241
+ OR if MacBook Air can't hold layers, use it as pure consumer
242
+ and split between iMac and GPU PC only (2 compute + 1 consumer)
243
+ - Run full pipeline with 3 participants
244
+ - Measure throughput vs 2-node pipeline
245
+
246
+ 3.4 Benchmark suite (scripts/benchmark.py)
247
+ - Automated benchmark that tests:
248
+ * Single node baseline
249
+ * 2-node sequential
250
+ * 2-node pipelined
251
+ * 3-node pipelined
252
+ - Outputs a comparison table with TTFT, tok/s, per-hop latency
253
+ - This becomes the evidence that Layer 1 works
254
+
255
+ Deliverable: 3-node pipeline running, benchmark comparison table.
256
+
257
+
258
+ ================================================================================
259
+ SPRINT 4 — Speculative Decoding (3-5 days)
260
+ ================================================================================
261
+
262
+ Goal: Add the draft model on the MacBook Air, prove the speed multiplier.
263
+ This is the "secret weapon" from the whitepaper.
264
+
265
+ Tasks:
266
+
267
+ 4.1 Load draft model on MacBook Air (consumer/draft_model.py)
268
+ - Load Qwen2.5-0.5B or SmolLM2-1.7B quantized to 4-bit
269
+ - Verify it runs on the M1 with MPS backend
270
+ - Measure draft generation speed (should be 50-100+ tok/s locally)
271
+
272
+ 4.2 Implement speculative decode loop (consumer/speculative.py)
273
+ - Draft model generates N candidate tokens
274
+ - All N candidates sent to the pipeline in a single batch
275
+ - Pipeline processes all N tokens in one forward pass
276
+ - Final node returns: acceptance mask + correction token
277
+ - Consumer accepts matching tokens, restarts from correction
278
+
279
+ 4.3 Verification logic on the final compute node
280
+ - Receives N candidate tokens + the prompt
281
+ - Runs a single forward pass for all positions
282
+ - Compares model's token distribution at each position to the
283
+ candidate token
284
+ - Returns: number of accepted tokens + first corrected token
285
+ - Standard speculative decoding math (rejection sampling)
286
+
287
+ 4.4 Adaptive window sizing
288
+ - Track acceptance rate over a rolling window of 10 verification rounds
289
+ - Expand window (N=12) when acceptance > 80%
290
+ - Shrink window (N=4) when acceptance < 50%
291
+ - Default window: N=8
292
+
293
+ 4.5 Full stack benchmark
294
+ - Run the 3-node pipeline WITH speculative decoding
295
+ - Compare to 3-node pipeline WITHOUT speculative decoding
296
+ - Measure:
297
+ * Effective tok/s (accepted tokens per second of wall time)
298
+ * Acceptance rate per domain (try code prompts vs. creative prompts)
299
+ * Overhead of draft model generation on MacBook Air
300
+ * Network round-trips saved
301
+
302
+ Expected results:
303
+ - Without speculative: 5-10 tok/s (from Sprint 3)
304
+ - With speculative (70% acceptance): 12-20 tok/s
305
+ - Code-specific prompts should see higher acceptance than creative
306
+
307
+ Deliverable: Speculative decoding working, measured speed multiplier,
308
+ acceptance rate data across prompt types.
309
+
310
+
311
+ ================================================================================
312
+ SPRINT 5 — Hardening & KV Cache (3-5 days)
313
+ ================================================================================
314
+
315
+ Goal: Handle failures and long conversations gracefully.
316
+
317
+ Tasks:
318
+
319
+ 5.1 KV cache management per node
320
+ - Each node maintains KV cache for its layers across tokens
321
+ - Implement cache eviction when context window fills
322
+ - Test: run a 2000-token conversation, verify cache doesn't OOM
323
+
324
+ 5.2 Node dropout handling
325
+ - Simulate Node B going offline mid-generation
326
+ - Consumer/relay detects via heartbeat timeout
327
+ - For PoC: restart the session (full re-prefill)
328
+ - Measure recovery time
329
+ - Later: implement standby promotion with KV cache streaming
330
+
331
+ 5.3 Session resumption
332
+ - Consumer disconnects and reconnects
333
+ - Compute nodes still hold KV cache for the session (within TTL)
334
+ - Consumer resumes generation without re-prefill
335
+ - Test: disconnect for 30s, reconnect, verify continuity
336
+
337
+ 5.4 Metrics dashboard
338
+ - Simple terminal UI or web page showing:
339
+ * Active pipeline topology
340
+ * Per-node GPU utilization
341
+ * Tok/s in real time
342
+ * Acceptance rate (if speculative decoding is on)
343
+ * Per-hop latency
344
+ - This becomes the foundation for the Groove GUI integration later
345
+
346
+ Deliverable: Resilient pipeline that handles failures, metrics visibility.
347
+
348
+
349
+ ================================================================================
350
+ WHAT SUCCESS LOOKS LIKE
351
+ ================================================================================
352
+
353
+ After Sprint 4, you should have hard data proving:
354
+
355
+ 1. Model sharding works — a model split across 2-3 machines produces
356
+ identical output to the same model on one machine
357
+
358
+ 2. Pipeline parallelism provides measurable throughput improvement —
359
+ 2-3x over naive sequential, bounded by slowest node not sum of nodes
360
+
361
+ 3. Speculative decoding provides measurable throughput improvement —
362
+ 2-4x over non-speculative, with acceptance rates by domain
363
+
364
+ 4. The full stack (sharding + pipeline + speculative) achieves
365
+ production-viable throughput — targeting 12-20 tok/s on a LAN,
366
+ which maps to 5-10 tok/s over internet (the "average case" from
367
+ the whitepaper)
368
+
369
+ This data is what turns the whitepaper from a thesis into a proof.
370
+ Every claim in the whitepaper Section 13 (Performance Expectations)
371
+ should be backed by real measurements from your 3-machine testbed.
372
+
373
+
374
+ ================================================================================
375
+ WHAT COMES AFTER LAYER 1
376
+ ================================================================================
377
+
378
+ Once Layer 1 is proven on the local network:
379
+
380
+ Layer 2 — Relay Network:
381
+ Move from hardcoded pipeline (you manually assign layers to machines)
382
+ to dynamic discovery via DHT. Implement the relay node as a separate
383
+ process. Test with machines on different networks (Tailscale).
384
+
385
+ Layer 3 — Proof & Settlement:
386
+ Implement proof of compute generation. Deploy $GROOVE token contract
387
+ on Base testnet. Wire up escrow and batch settlement.
388
+
389
+ Layer 4 — Training Loop:
390
+ Start capturing inference traces. Build the federated fine-tuning
391
+ pipeline. Train Savant v0.1.
392
+
393
+ Integration with Groove:
394
+ The decentralized inference pipeline becomes a new provider in
395
+ packages/daemon/src/providers/ — "groove-net.js". Groove agents can
396
+ route to the decentralized network just like they route to Claude,
397
+ Codex, or Gemini today.
398
+
399
+
400
+ ================================================================================
401
+ GETTING STARTED — LITERALLY THE FIRST COMMANDS
402
+ ================================================================================
403
+
404
+ On the MacBook Air (your dev machine):
405
+
406
+ cd ~/Desktop/groove/decentralized-net
407
+ python3 -m venv venv
408
+ source venv/bin/activate
409
+ pip install torch transformers accelerate websockets msgpack
410
+
411
+ mkdir -p src/{node,consumer,relay,common} tests scripts
412
+
413
+ # Verify PyTorch works with MPS
414
+ python3 -c "import torch; print(torch.backends.mps.is_available())"
415
+
416
+ # Download a small model to start
417
+ python3 -c "from transformers import AutoModelForCausalLM, AutoTokenizer; \
418
+ AutoModelForCausalLM.from_pretrained('Qwen/Qwen2.5-0.5B'); \
419
+ AutoTokenizer.from_pretrained('Qwen/Qwen2.5-0.5B')"
420
+
421
+ Then start Sprint 1, Task 1.2 — run a baseline benchmark on your fastest
422
+ machine.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.27",
3
+ "version": "0.27.28",
4
4
  "description": "GROOVE CLI — manage AI coding agents from your terminal",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/daemon",
3
- "version": "0.27.27",
3
+ "version": "0.27.28",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -9,7 +9,9 @@ import { spawn, execFile } from 'child_process';
9
9
  import { lookup as mimeLookup } from './mimetypes.js';
10
10
  import { listProviders, getProvider } from './providers/index.js';
11
11
  import { OllamaProvider } from './providers/ollama.js';
12
+ import { ClaudeCodeProvider } from './providers/claude-code.js';
12
13
  import { validateAgentConfig } from './validate.js';
14
+ import { ROLE_INTEGRATIONS } from './process.js';
13
15
 
14
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
17
  const pkgVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
@@ -179,6 +181,88 @@ export function createApi(app, daemon) {
179
181
  res.json({ ok: true });
180
182
  });
181
183
 
184
+ // --- Role-to-Integration Mapping ---
185
+
186
+ app.get('/api/roles/integrations', (req, res) => {
187
+ const roleFilter = req.query.role;
188
+ const entries = roleFilter ? { [roleFilter]: ROLE_INTEGRATIONS[roleFilter] || [] } : ROLE_INTEGRATIONS;
189
+ const result = {};
190
+ for (const [role, ids] of Object.entries(entries)) {
191
+ result[role] = (ids || []).map((id) => {
192
+ const status = daemon.integrations.getStatus(id);
193
+ const entry = daemon.integrations.registry.find((r) => r.id === id);
194
+ return {
195
+ id,
196
+ name: entry?.name || id,
197
+ installed: status?.installed || false,
198
+ configured: status?.configured || false,
199
+ authenticated: status?.authenticated || false,
200
+ };
201
+ });
202
+ }
203
+ if (roleFilter) return res.json(result[roleFilter] || []);
204
+ res.json(result);
205
+ });
206
+
207
+ app.post('/api/agents/preflight', (req, res) => {
208
+ const { role, integrations } = req.body || {};
209
+ if (!role || !Array.isArray(integrations)) {
210
+ return res.status(400).json({ error: 'role and integrations[] required' });
211
+ }
212
+ const issues = [];
213
+ for (const id of integrations) {
214
+ const status = daemon.integrations.getStatus(id);
215
+ const entry = daemon.integrations.registry.find((r) => r.id === id);
216
+ const name = entry?.name || id;
217
+ if (!status || !status.installed) {
218
+ issues.push({ integrationId: id, name, problem: 'not_installed' });
219
+ } else if (!status.configured) {
220
+ issues.push({ integrationId: id, name, problem: 'not_configured' });
221
+ } else if (!status.authenticated) {
222
+ issues.push({ integrationId: id, name, problem: 'not_authenticated' });
223
+ }
224
+ }
225
+ res.json({ ready: issues.length === 0, issues });
226
+ });
227
+
228
+ // --- Agent Integration Attach/Detach ---
229
+
230
+ app.post('/api/agents/:id/integrations/:integrationId', (req, res) => {
231
+ const agent = daemon.registry.get(req.params.id);
232
+ if (!agent) return res.status(404).json({ error: 'Agent not found' });
233
+
234
+ const integrationId = req.params.integrationId;
235
+ const status = daemon.integrations.getStatus(integrationId);
236
+ if (!status || !status.installed) {
237
+ return res.status(400).json({ error: `Integration not installed: ${integrationId}` });
238
+ }
239
+
240
+ const integrations = new Set(agent.integrations || []);
241
+ integrations.add(integrationId);
242
+ const updated = Array.from(integrations);
243
+
244
+ daemon.registry.update(req.params.id, { integrations: updated });
245
+ daemon.integrations.writeMcpJson(daemon.integrations.getActiveIntegrations());
246
+ daemon.integrations.refreshMcpJson();
247
+ daemon.audit.log('agent.integration.attach', { agentId: req.params.id, integrationId });
248
+ daemon.broadcast({ type: 'agent:integration:attach', agentId: req.params.id, integrationId });
249
+ res.json({ ok: true, integrations: updated });
250
+ });
251
+
252
+ app.delete('/api/agents/:id/integrations/:integrationId', (req, res) => {
253
+ const agent = daemon.registry.get(req.params.id);
254
+ if (!agent) return res.status(404).json({ error: 'Agent not found' });
255
+
256
+ const integrationId = req.params.integrationId;
257
+ const integrations = (agent.integrations || []).filter((id) => id !== integrationId);
258
+
259
+ daemon.registry.update(req.params.id, { integrations });
260
+ daemon.integrations.refreshMcpJson();
261
+ daemon.audit.log('agent.integration.detach', { agentId: req.params.id, integrationId });
262
+ daemon.broadcast({ type: 'agent:integration:detach', agentId: req.params.id, integrationId });
263
+ res.json({ ok: true, integrations });
264
+ });
265
+
182
266
  // Lock management
183
267
  app.get('/api/locks', (req, res) => {
184
268
  res.json(daemon.locks.getAll());
@@ -307,10 +391,25 @@ export function createApi(app, daemon) {
307
391
  // Enrich with credential status
308
392
  for (const p of providers) {
309
393
  p.hasKey = daemon.credentials.hasKey(p.id);
394
+ if (p.id === 'claude-code') {
395
+ p.authStatus = ClaudeCodeProvider.getAuthStatus();
396
+ }
310
397
  }
311
398
  res.json(providers);
312
399
  });
313
400
 
401
+ // --- Claude Code Auth ---
402
+
403
+ app.get('/api/providers/claude-code/auth', (req, res) => {
404
+ res.json(ClaudeCodeProvider.getAuthStatus());
405
+ });
406
+
407
+ app.post('/api/providers/claude-code/login', (req, res) => {
408
+ ClaudeCodeProvider.triggerLogin();
409
+ daemon.audit.log('claude-code.login.started', {});
410
+ res.json({ ok: true });
411
+ });
412
+
314
413
  // --- Ollama ---
315
414
 
316
415
  app.get('/api/providers/ollama/hardware', (req, res) => {
@@ -266,6 +266,18 @@ IMPORTANT: Do not use markdown formatting like ** or ### in your output. Write i
266
266
  `,
267
267
  };
268
268
 
269
+ // Role-to-integration mapping — recommended integrations per role for onboarding preflight
270
+ export const ROLE_INTEGRATIONS = {
271
+ ea: ['gmail', 'google-calendar'],
272
+ cmo: ['gmail', 'slack', 'hubspot'],
273
+ cfo: ['stripe', 'google-sheets'],
274
+ support: ['gmail', 'slack', 'zendesk'],
275
+ analyst: ['google-sheets', 'postgres', 'mixpanel'],
276
+ home: ['home-assistant'],
277
+ slides: ['google-slides'],
278
+ creative: ['google-docs'],
279
+ };
280
+
269
281
  // Permission-level prompt instructions
270
282
  // "auto" = PM reviews risky ops via API. "full" = no reviews, max speed.
271
283
  const PERMISSION_PROMPTS = {
@@ -1,7 +1,7 @@
1
1
  // GROOVE — Claude Code Provider
2
2
  // FSL-1.1-Apache-2.0 — see LICENSE
3
3
 
4
- import { execSync } from 'child_process';
4
+ import { execSync, spawn as cpSpawn } from 'child_process';
5
5
  import { writeFileSync, readFileSync, existsSync } from 'fs';
6
6
  import { resolve } from 'path';
7
7
  import { homedir } from 'os';
@@ -223,4 +223,29 @@ export class ClaudeCodeProvider extends Provider {
223
223
 
224
224
  return merged;
225
225
  }
226
+
227
+ static getAuthStatus() {
228
+ try {
229
+ const out = execSync('claude auth status --json', { encoding: 'utf8', timeout: 10_000, stdio: ['pipe', 'pipe', 'pipe'] });
230
+ const data = JSON.parse(out);
231
+ return {
232
+ authenticated: true,
233
+ authMethod: data.authMethod || data.auth_method || 'unknown',
234
+ email: data.email || null,
235
+ subscriptionType: data.subscriptionType || data.subscription_type || null,
236
+ orgName: data.orgName || data.org_name || null,
237
+ };
238
+ } catch (err) {
239
+ return { authenticated: false, error: err.message };
240
+ }
241
+ }
242
+
243
+ static triggerLogin() {
244
+ const child = cpSpawn('claude', ['auth', 'login', '--claudeai'], {
245
+ detached: true,
246
+ stdio: 'ignore',
247
+ });
248
+ child.unref();
249
+ return { pid: child.pid };
250
+ }
226
251
  }