groove-dev 0.27.33 → 0.27.35
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/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/cli/src/commands/start.js +12 -9
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +46 -8
- package/node_modules/@groove-dev/gui/dist/assets/{index-BoU6IhQI.js → index-Df4O6yJI.js} +62 -62
- package/node_modules/@groove-dev/gui/dist/assets/{index-BnLiWvrh.css → index-Dx7i-7_K.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +33 -14
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +41 -16
- package/node_modules/@groove-dev/gui/src/stores/groove.js +11 -0
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/commands/start.js +12 -9
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/tunnel-manager.js +46 -8
- package/packages/gui/dist/assets/{index-BoU6IhQI.js → index-Df4O6yJI.js} +62 -62
- package/packages/gui/dist/assets/{index-BnLiWvrh.css → index-Dx7i-7_K.css} +1 -1
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/layout/status-bar.jsx +33 -14
- package/packages/gui/src/components/settings/quick-connect.jsx +41 -16
- package/packages/gui/src/stores/groove.js +11 -0
|
@@ -14,15 +14,18 @@ export async function start(options) {
|
|
|
14
14
|
// ── First-run interactive wizard ────────────────────────────
|
|
15
15
|
let setupKeys = {};
|
|
16
16
|
if (isFirstRun) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
if (!process.stdin.isTTY) {
|
|
18
|
+
console.log(chalk.dim(' Non-interactive mode — skipping setup wizard.'));
|
|
19
|
+
} else {
|
|
20
|
+
try {
|
|
21
|
+
const result = await runSetupWizard();
|
|
22
|
+
setupKeys = result.keys || {};
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (err.code === 'ERR_USE_AFTER_CLOSE') {
|
|
25
|
+
console.log(chalk.dim(' Non-interactive mode — skipping setup wizard.'));
|
|
26
|
+
} else {
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
}
|
|
@@ -3,10 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
import { execFileSync, spawn } from 'child_process';
|
|
5
5
|
import { existsSync, writeFileSync, readFileSync, statSync } from 'fs';
|
|
6
|
-
import { resolve } from 'path';
|
|
6
|
+
import { resolve, dirname, join } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
7
8
|
import { createConnection } from 'net';
|
|
8
9
|
import crypto from 'crypto';
|
|
9
10
|
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
function getLocalVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', '..', 'package.json'), 'utf8'));
|
|
15
|
+
return pkg.version || '0.0.0';
|
|
16
|
+
} catch { return '0.0.0'; }
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
const REMOTE_PORT = 31415;
|
|
11
20
|
const DEFAULT_LOCAL_PORT = 31416;
|
|
12
21
|
const MAX_PORT_ATTEMPTS = 10;
|
|
@@ -185,7 +194,7 @@ export class TunnelManager {
|
|
|
185
194
|
'-o', 'StrictHostKeyChecking=accept-new',
|
|
186
195
|
'-o', 'BatchMode=yes',
|
|
187
196
|
target,
|
|
188
|
-
`curl -sf http://localhost:${REMOTE_PORT}/api/health 2>/dev/null || (which groove >/dev/null 2>&1 && echo __GROOVE_STOPPED__ || echo __GROOVE_NOT_INSTALLED__)`,
|
|
197
|
+
`bash -lc 'curl -sf http://localhost:${REMOTE_PORT}/api/health 2>/dev/null || (which groove >/dev/null 2>&1 && echo __GROOVE_VER__$(groove --version 2>/dev/null || echo unknown)__GROOVE_STOPPED__ || echo __GROOVE_NOT_INSTALLED__)'`,
|
|
189
198
|
], {
|
|
190
199
|
encoding: 'utf8',
|
|
191
200
|
timeout: 20000,
|
|
@@ -196,7 +205,9 @@ export class TunnelManager {
|
|
|
196
205
|
return { reachable: true, daemonRunning: false, grooveInstalled: false };
|
|
197
206
|
}
|
|
198
207
|
if (result.includes('__GROOVE_STOPPED__')) {
|
|
199
|
-
|
|
208
|
+
const verMatch = result.match(/__GROOVE_VER__(.+?)__GROOVE_STOPPED__/);
|
|
209
|
+
const remoteVersion = verMatch ? verMatch[1].trim() : null;
|
|
210
|
+
return { reachable: true, daemonRunning: false, grooveInstalled: true, remoteVersion };
|
|
200
211
|
}
|
|
201
212
|
return { reachable: true, daemonRunning: true, grooveInstalled: true };
|
|
202
213
|
} catch (err) {
|
|
@@ -236,6 +247,11 @@ export class TunnelManager {
|
|
|
236
247
|
this.daemon.broadcast({ type: 'tunnel.status', data: { id, step: 'installing' } });
|
|
237
248
|
await this.remoteInstall(id);
|
|
238
249
|
} else if (!testResult.daemonRunning && testResult.grooveInstalled) {
|
|
250
|
+
const localVer = getLocalVersion();
|
|
251
|
+
if (testResult.remoteVersion && testResult.remoteVersion !== localVer) {
|
|
252
|
+
this.daemon.broadcast({ type: 'tunnel.status', data: { id, step: 'upgrading', from: testResult.remoteVersion, to: localVer } });
|
|
253
|
+
await this._remoteUpgrade(id, config);
|
|
254
|
+
}
|
|
239
255
|
this.daemon.broadcast({ type: 'tunnel.status', data: { id, step: 'starting' } });
|
|
240
256
|
await this.autoStart(id);
|
|
241
257
|
}
|
|
@@ -332,6 +348,24 @@ export class TunnelManager {
|
|
|
332
348
|
}
|
|
333
349
|
}
|
|
334
350
|
|
|
351
|
+
async _remoteUpgrade(id, config) {
|
|
352
|
+
const target = `${config.user}@${config.host}`;
|
|
353
|
+
const keyArgs = config.sshKeyPath ? ['-i', config.sshKeyPath] : [];
|
|
354
|
+
const sshBase = [...keyArgs, '-p', String(config.port || 22), '-o', 'ConnectTimeout=10', '-o', 'BatchMode=yes', target];
|
|
355
|
+
const installCmd = config.user === 'root' ? 'npm i -g groove-dev' : 'sudo npm i -g groove-dev';
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
execFileSync('ssh', [...sshBase, `bash -lc '${installCmd}'`], {
|
|
359
|
+
encoding: 'utf8',
|
|
360
|
+
timeout: 120000,
|
|
361
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
362
|
+
});
|
|
363
|
+
} catch (err) {
|
|
364
|
+
const output = err.stdout?.toString() || err.stderr?.toString() || err.message;
|
|
365
|
+
throw new Error(`Remote upgrade failed: ${output.slice(-400)}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
335
369
|
async autoStart(id) {
|
|
336
370
|
const config = this.saved.get(id);
|
|
337
371
|
if (!config) throw new Error(`Remote ${id} not found`);
|
|
@@ -346,7 +380,7 @@ export class TunnelManager {
|
|
|
346
380
|
'-o', 'ConnectTimeout=10',
|
|
347
381
|
'-o', 'BatchMode=yes',
|
|
348
382
|
target,
|
|
349
|
-
`bash -lc 'nohup groove start > /tmp/groove-daemon.log 2>&1 < /dev/null & disown; sleep
|
|
383
|
+
`bash -lc 'nohup groove start > /tmp/groove-daemon.log 2>&1 < /dev/null & disown; sleep 5; curl -sf http://localhost:${REMOTE_PORT}/api/health > /dev/null && echo __DAEMON_OK__ || (echo __DAEMON_FAIL__; tail -20 /tmp/groove-daemon.log 2>/dev/null)'`,
|
|
350
384
|
], {
|
|
351
385
|
encoding: 'utf8',
|
|
352
386
|
timeout: 45000,
|
|
@@ -354,10 +388,12 @@ export class TunnelManager {
|
|
|
354
388
|
});
|
|
355
389
|
|
|
356
390
|
if (result.includes('__DAEMON_FAIL__')) {
|
|
357
|
-
|
|
391
|
+
const logLines = result.split('__DAEMON_FAIL__')[1]?.trim() || '';
|
|
392
|
+
const detail = logLines ? `: ${logLines.slice(-300)}` : '';
|
|
393
|
+
throw new Error(`Remote daemon failed to start${detail}`);
|
|
358
394
|
}
|
|
359
395
|
} catch (err) {
|
|
360
|
-
if (err.message.includes('
|
|
396
|
+
if (err.message.includes('Remote daemon failed')) throw err;
|
|
361
397
|
const output = err.stdout?.toString() || err.stderr?.toString() || err.message;
|
|
362
398
|
throw new Error(`Failed to start remote daemon: ${output.slice(-300)}`);
|
|
363
399
|
}
|
|
@@ -423,7 +459,7 @@ export class TunnelManager {
|
|
|
423
459
|
try {
|
|
424
460
|
const result = execFileSync('ssh', [
|
|
425
461
|
...sshBase,
|
|
426
|
-
remoteCmd(`nohup groove start > /tmp/groove-daemon.log 2>&1 < /dev/null & disown; sleep
|
|
462
|
+
remoteCmd(`nohup groove start > /tmp/groove-daemon.log 2>&1 < /dev/null & disown; sleep 5; curl -sf http://localhost:${REMOTE_PORT}/api/health > /dev/null && echo __DAEMON_OK__ || (echo __DAEMON_FAIL__; tail -20 /tmp/groove-daemon.log 2>/dev/null)`),
|
|
427
463
|
], {
|
|
428
464
|
encoding: 'utf8',
|
|
429
465
|
timeout: 45000,
|
|
@@ -431,7 +467,9 @@ export class TunnelManager {
|
|
|
431
467
|
});
|
|
432
468
|
|
|
433
469
|
if (result.includes('__DAEMON_FAIL__')) {
|
|
434
|
-
|
|
470
|
+
const logLines = result.split('__DAEMON_FAIL__')[1]?.trim() || '';
|
|
471
|
+
const detail = logLines ? `: ${logLines.slice(-300)}` : '';
|
|
472
|
+
throw new Error(`Groove installed but daemon failed to start${detail}`);
|
|
435
473
|
}
|
|
436
474
|
} catch (err) {
|
|
437
475
|
if (err.message.includes('Groove installed')) throw err;
|