groove-dev 0.27.154 → 0.27.156

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.154",
3
+ "version": "0.27.156",
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.154",
3
+ "version": "0.27.156",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
2
  import { resolve, sep, isAbsolute, basename } from 'path';
3
3
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync, unlinkSync, renameSync, rmSync, createReadStream, realpathSync } from 'fs';
4
- import { execFile, execFileSync } from 'child_process';
4
+ import { execFile, execFileSync, spawn } from 'child_process';
5
5
  import { homedir } from 'os';
6
6
  import { lookup as mimeLookup } from '../mimetypes.js';
7
7
 
@@ -331,15 +331,37 @@ export function registerFileRoutes(app, daemon) {
331
331
  }
332
332
  });
333
333
 
334
- // Download a file (serves raw with Content-Disposition)
334
+ // Download a file or folder (folders are streamed as zip)
335
335
  app.get('/api/files/download', (req, res) => {
336
336
  const relPath = req.query.path;
337
337
  const result = validateFilePath(relPath, getEditorRoot(daemon));
338
338
  if (result.error) return res.status(400).json({ error: result.error });
339
- if (!existsSync(result.fullPath)) return res.status(404).json({ error: 'File not found' });
339
+ if (!existsSync(result.fullPath)) return res.status(404).json({ error: 'Not found' });
340
340
 
341
341
  const stat = statSync(result.fullPath);
342
- if (stat.isDirectory()) return res.status(400).json({ error: 'Cannot download a directory' });
342
+
343
+ if (stat.isDirectory()) {
344
+ const folderName = basename(result.fullPath);
345
+ res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(folderName)}.zip"`);
346
+ res.setHeader('Content-Type', 'application/zip');
347
+
348
+ const zipProc = spawn('zip', [
349
+ '-r', '-q', '-',
350
+ relPath,
351
+ '-x', `${relPath}/.git/*`,
352
+ '-x', `${relPath}/node_modules/*`,
353
+ ], {
354
+ cwd: getEditorRoot(daemon),
355
+ stdio: ['ignore', 'pipe', 'pipe'],
356
+ });
357
+
358
+ zipProc.stdout.pipe(res);
359
+ zipProc.stderr.on('data', () => {});
360
+ zipProc.on('error', () => {
361
+ if (!res.headersSent) res.status(500).json({ error: 'Failed to create zip' });
362
+ });
363
+ return;
364
+ }
343
365
 
344
366
  const name = basename(result.fullPath);
345
367
  const mime = mimeLookup(name) || 'application/octet-stream';
@@ -267,8 +267,6 @@ export class TunnelManager {
267
267
  let testResult;
268
268
  if (opts.skipTest && opts.testResult) {
269
269
  testResult = opts.testResult;
270
- } else if (config.lastConnected && opts.skipTest !== false) {
271
- testResult = { reachable: true, daemonRunning: true, grooveInstalled: true, remoteVersion: null };
272
270
  } else {
273
271
  testResult = await this.test(id);
274
272
  }