groove-dev 0.17.5 → 0.17.6

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.
@@ -102,9 +102,9 @@
102
102
  "authType": "none",
103
103
  "envKeys": [],
104
104
  "setupSteps": [
105
- "Just install the server handles Google sign-in automatically",
106
- "On first use, a browser window will open for you to authorize",
107
- "That's it — no API keys or tokens needed"
105
+ "Click Install, then click 'Sign in with Google'",
106
+ "A browser window will open authorize Groove",
107
+ "Done — no API keys or tokens needed"
108
108
  ],
109
109
  "featured": false,
110
110
  "downloads": 0,
@@ -127,9 +127,9 @@
127
127
  "authType": "none",
128
128
  "envKeys": [],
129
129
  "setupSteps": [
130
- "Just install the server handles Google sign-in automatically",
131
- "On first use, a browser window will open for you to authorize",
132
- "That's it — no API keys or tokens needed"
130
+ "Click Install, then click 'Sign in with Google'",
131
+ "A browser window will open authorize Groove",
132
+ "Done — no API keys or tokens needed"
133
133
  ],
134
134
  "featured": false,
135
135
  "downloads": 0,
@@ -506,6 +506,16 @@ export function createApi(app, daemon) {
506
506
 
507
507
  // Parameterized :id routes (after specific routes above)
508
508
 
509
+ app.post('/api/integrations/:id/authenticate', (req, res) => {
510
+ try {
511
+ const handle = daemon.integrations.authenticate(req.params.id);
512
+ res.json({ ok: true, pid: handle.pid });
513
+ // Auto-cleanup tracked by the handle timeout
514
+ } catch (err) {
515
+ res.status(400).json({ error: err.message });
516
+ }
517
+ });
518
+
509
519
  app.post('/api/integrations/:id/install', async (req, res) => {
510
520
  try {
511
521
  const result = await daemon.integrations.install(req.params.id);
@@ -4,7 +4,7 @@
4
4
  import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'fs';
5
5
  import { resolve, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
- import { execFileSync } from 'child_process';
7
+ import { execFileSync, spawn as cpSpawn } from 'child_process';
8
8
 
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
 
@@ -457,6 +457,48 @@ export class IntegrationStore {
457
457
  return !!(clientId && clientSecret);
458
458
  }
459
459
 
460
+ /**
461
+ * Pre-authenticate an auto-auth integration by running its MCP server briefly.
462
+ * The server will open a browser for OAuth. Once auth completes, the server
463
+ * stores tokens locally so future agent spawns work without prompting.
464
+ * Returns a handle to track the auth process.
465
+ */
466
+ authenticate(integrationId) {
467
+ const entry = this.registry.find((s) => s.id === integrationId);
468
+ if (!entry) throw new Error(`Integration not found: ${integrationId}`);
469
+
470
+ const command = entry.command || 'npx';
471
+ const args = entry.args || ['-y', entry.npmPackage];
472
+
473
+ // Build env with any configured credentials
474
+ const env = {};
475
+ for (const ek of (entry.envKeys || [])) {
476
+ const val = this.getCredential(integrationId, ek.key);
477
+ if (val) env[ek.key] = val;
478
+ }
479
+
480
+ // Spawn the MCP server — it will trigger OAuth on startup
481
+ const proc = cpSpawn(command, args, {
482
+ env: { ...process.env, ...env },
483
+ stdio: ['pipe', 'pipe', 'pipe'],
484
+ detached: false,
485
+ });
486
+
487
+ // Auto-kill after 2 minutes (auth should complete well before that)
488
+ const timeout = setTimeout(() => {
489
+ try { proc.kill('SIGTERM'); } catch { /* ignore */ }
490
+ }, 120_000);
491
+
492
+ proc.on('exit', () => clearTimeout(timeout));
493
+
494
+ this.daemon.audit.log('integration.authenticate', { id: integrationId });
495
+
496
+ return {
497
+ pid: proc.pid,
498
+ kill: () => { clearTimeout(timeout); try { proc.kill('SIGTERM'); } catch { /* ignore */ } },
499
+ };
500
+ }
501
+
460
502
  // --- Internal ---
461
503
 
462
504
  _isInstalled(integrationId) {