groove-dev 0.27.65 → 0.27.67
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/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +84 -60
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +25 -2
- package/node_modules/@groove-dev/gui/dist/assets/index-MPNqazCA.js +8614 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-YeunozTU.css +1 -0
- 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/app.jsx +5 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +3 -1
- package/node_modules/@groove-dev/gui/src/components/agents/folder-browser.jsx +5 -4
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +1 -2
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +1 -9
- package/node_modules/@groove-dev/gui/src/components/layout/project-picker.jsx +3 -1
- package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +132 -0
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +3 -1
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-modal.jsx +1 -2
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +3 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +14 -2
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +14 -1
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +3 -1
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +84 -60
- package/packages/daemon/src/tunnel-manager.js +25 -2
- package/packages/gui/dist/assets/index-MPNqazCA.js +8614 -0
- package/packages/gui/dist/assets/index-YeunozTU.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.jsx +5 -1
- package/packages/gui/src/components/agents/agent-config.jsx +3 -1
- package/packages/gui/src/components/agents/folder-browser.jsx +5 -4
- package/packages/gui/src/components/layout/app-shell.jsx +1 -2
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -9
- package/packages/gui/src/components/layout/project-picker.jsx +3 -1
- package/packages/gui/src/components/layout/welcome-splash.jsx +132 -0
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +3 -1
- package/packages/gui/src/components/pro/upgrade-modal.jsx +1 -2
- package/packages/gui/src/components/settings/server-dialog.jsx +2 -0
- package/packages/gui/src/components/settings/ssh-wizard.jsx +3 -0
- package/packages/gui/src/stores/groove.js +14 -2
- package/packages/gui/src/views/agents.jsx +14 -1
- package/packages/gui/src/views/settings.jsx +3 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-DFp5IOnd.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-xZOvdpBT.js +0 -8614
- package/packages/gui/dist/assets/index-DFp5IOnd.css +0 -1
- package/packages/gui/dist/assets/index-xZOvdpBT.js +0 -8614
|
@@ -744,6 +744,7 @@ export function createApi(app, daemon) {
|
|
|
744
744
|
port: daemon.port,
|
|
745
745
|
projectDir: daemon.projectDir,
|
|
746
746
|
edition: sub.active ? 'pro' : 'community',
|
|
747
|
+
homedir: homedir(),
|
|
747
748
|
});
|
|
748
749
|
});
|
|
749
750
|
|
|
@@ -763,6 +764,7 @@ export function createApi(app, daemon) {
|
|
|
763
764
|
}
|
|
764
765
|
try {
|
|
765
766
|
daemon.setProjectDir(dirPath);
|
|
767
|
+
editorRootDir = daemon.projectDir;
|
|
766
768
|
res.json({ projectDir: daemon.projectDir, recentProjects: daemon.config.recentProjects || [] });
|
|
767
769
|
} catch (err) {
|
|
768
770
|
res.status(400).json({ error: err.message });
|
|
@@ -3635,11 +3637,11 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3635
3637
|
|
|
3636
3638
|
// --- Tunnels (Remote Access) ---
|
|
3637
3639
|
|
|
3638
|
-
app.get('/api/tunnels',
|
|
3640
|
+
app.get('/api/tunnels', (req, res) => {
|
|
3639
3641
|
res.json(daemon.tunnelManager.getSaved());
|
|
3640
3642
|
});
|
|
3641
3643
|
|
|
3642
|
-
app.post('/api/tunnels',
|
|
3644
|
+
app.post('/api/tunnels', (req, res) => {
|
|
3643
3645
|
try {
|
|
3644
3646
|
const { name, host, user, port, sshKeyPath, autoStart, autoConnect } = req.body;
|
|
3645
3647
|
if (!name || typeof name !== 'string') return res.status(400).json({ error: 'name is required (string)' });
|
|
@@ -3651,7 +3653,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3651
3653
|
}
|
|
3652
3654
|
});
|
|
3653
3655
|
|
|
3654
|
-
app.patch('/api/tunnels/:id',
|
|
3656
|
+
app.patch('/api/tunnels/:id', (req, res) => {
|
|
3655
3657
|
try {
|
|
3656
3658
|
const result = daemon.tunnelManager.update(req.params.id, req.body);
|
|
3657
3659
|
res.json(result);
|
|
@@ -3660,7 +3662,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3660
3662
|
}
|
|
3661
3663
|
});
|
|
3662
3664
|
|
|
3663
|
-
app.delete('/api/tunnels/:id',
|
|
3665
|
+
app.delete('/api/tunnels/:id', async (req, res) => {
|
|
3664
3666
|
try {
|
|
3665
3667
|
await daemon.tunnelManager.delete(req.params.id);
|
|
3666
3668
|
res.json({ ok: true });
|
|
@@ -3669,7 +3671,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3669
3671
|
}
|
|
3670
3672
|
});
|
|
3671
3673
|
|
|
3672
|
-
app.post('/api/tunnels/:id/test',
|
|
3674
|
+
app.post('/api/tunnels/:id/test', async (req, res) => {
|
|
3673
3675
|
try {
|
|
3674
3676
|
const result = await daemon.tunnelManager.test(req.params.id);
|
|
3675
3677
|
res.json(result);
|
|
@@ -3678,7 +3680,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3678
3680
|
}
|
|
3679
3681
|
});
|
|
3680
3682
|
|
|
3681
|
-
app.post('/api/tunnels/:id/connect',
|
|
3683
|
+
app.post('/api/tunnels/:id/connect', async (req, res) => {
|
|
3682
3684
|
try {
|
|
3683
3685
|
const opts = {};
|
|
3684
3686
|
if (req.body?.skipTest && req.body?.testResult) {
|
|
@@ -3694,7 +3696,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3694
3696
|
}
|
|
3695
3697
|
});
|
|
3696
3698
|
|
|
3697
|
-
app.post('/api/tunnels/:id/disconnect',
|
|
3699
|
+
app.post('/api/tunnels/:id/disconnect', async (req, res) => {
|
|
3698
3700
|
try {
|
|
3699
3701
|
await daemon.tunnelManager.disconnect(req.params.id);
|
|
3700
3702
|
res.json({ ok: true });
|
|
@@ -3703,7 +3705,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3703
3705
|
}
|
|
3704
3706
|
});
|
|
3705
3707
|
|
|
3706
|
-
app.post('/api/tunnels/:id/install',
|
|
3708
|
+
app.post('/api/tunnels/:id/install', async (req, res) => {
|
|
3707
3709
|
try {
|
|
3708
3710
|
const result = await daemon.tunnelManager.remoteInstall(req.params.id);
|
|
3709
3711
|
res.json(result);
|
|
@@ -3712,7 +3714,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3712
3714
|
}
|
|
3713
3715
|
});
|
|
3714
3716
|
|
|
3715
|
-
app.post('/api/tunnels/:id/start',
|
|
3717
|
+
app.post('/api/tunnels/:id/start', async (req, res) => {
|
|
3716
3718
|
try {
|
|
3717
3719
|
await daemon.tunnelManager.autoStart(req.params.id);
|
|
3718
3720
|
res.json({ ok: true });
|
|
@@ -3721,7 +3723,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3721
3723
|
}
|
|
3722
3724
|
});
|
|
3723
3725
|
|
|
3724
|
-
app.get('/api/tunnels/:id/status',
|
|
3726
|
+
app.get('/api/tunnels/:id/status', (req, res) => {
|
|
3725
3727
|
const s = daemon.tunnelManager.getStatus(req.params.id);
|
|
3726
3728
|
if (!s) return res.status(404).json({ error: 'Remote not found' });
|
|
3727
3729
|
res.json(s);
|
|
@@ -3869,27 +3871,6 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
3869
3871
|
res.json({ ok: true });
|
|
3870
3872
|
});
|
|
3871
3873
|
|
|
3872
|
-
// --- Project Directory ---
|
|
3873
|
-
|
|
3874
|
-
app.post('/api/project-dir', async (req, res) => {
|
|
3875
|
-
const { dir } = req.body;
|
|
3876
|
-
if (!dir || typeof dir !== 'string') return res.status(400).json({ error: 'dir required' });
|
|
3877
|
-
if (/[\0\n\r]/.test(dir)) return res.status(400).json({ error: 'Invalid characters in path' });
|
|
3878
|
-
const { existsSync, statSync } = await import('fs');
|
|
3879
|
-
const { resolve, isAbsolute } = await import('path');
|
|
3880
|
-
const resolved = resolve(dir);
|
|
3881
|
-
if (!isAbsolute(resolved)) return res.status(400).json({ error: 'Path must be absolute' });
|
|
3882
|
-
if (!existsSync(resolved) || !statSync(resolved).isDirectory()) {
|
|
3883
|
-
return res.status(400).json({ error: 'Directory does not exist' });
|
|
3884
|
-
}
|
|
3885
|
-
daemon.config.defaultWorkingDir = resolved;
|
|
3886
|
-
const { saveConfig } = await import('./firstrun.js');
|
|
3887
|
-
saveConfig(daemon.grooveDir, daemon.config);
|
|
3888
|
-
daemon.broadcast({ type: 'config:updated', data: { defaultWorkingDir: resolved } });
|
|
3889
|
-
daemon.audit.log('project.dir.change', { dir: resolved });
|
|
3890
|
-
res.json({ ok: true, dir: resolved });
|
|
3891
|
-
});
|
|
3892
|
-
|
|
3893
3874
|
// --- Config ---
|
|
3894
3875
|
|
|
3895
3876
|
app.get('/api/config', (req, res) => {
|
|
@@ -4145,9 +4126,36 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4145
4126
|
|
|
4146
4127
|
// Network node lifecycle (gated)
|
|
4147
4128
|
|
|
4129
|
+
let _localHwCache = null;
|
|
4130
|
+
function getLocalHardware() {
|
|
4131
|
+
if (!_localHwCache) {
|
|
4132
|
+
const sys = OllamaProvider.getSystemHardware();
|
|
4133
|
+
const vramGb = sys.gpu?.vram || 0;
|
|
4134
|
+
const ramGb = sys.totalRamGb || 0;
|
|
4135
|
+
const vramMb = vramGb * 1024;
|
|
4136
|
+
const ramMb = ramGb * 1024;
|
|
4137
|
+
const fmtGb = (gb) => gb > 0 ? `${gb} GB` : null;
|
|
4138
|
+
_localHwCache = {
|
|
4139
|
+
device: sys.gpu?.type === 'nvidia' ? 'cuda' : sys.gpu?.type === 'apple-silicon' ? 'metal' : 'cpu',
|
|
4140
|
+
gpu: sys.gpu?.name || null,
|
|
4141
|
+
memory: fmtGb(vramGb) || fmtGb(ramGb),
|
|
4142
|
+
vram: fmtGb(vramGb),
|
|
4143
|
+
ram: fmtGb(ramGb),
|
|
4144
|
+
cpuCores: sys.cores || null,
|
|
4145
|
+
ram_mb: ramMb,
|
|
4146
|
+
vram_mb: vramMb,
|
|
4147
|
+
gpu_model: sys.gpu?.name || null,
|
|
4148
|
+
cpu_cores: sys.cores || 0,
|
|
4149
|
+
bandwidth_mbps: 0,
|
|
4150
|
+
max_context_length: 0,
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
return _localHwCache;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4148
4156
|
function snapshotNode() {
|
|
4149
4157
|
const n = daemon.networkNode || {};
|
|
4150
|
-
const hw = n.hardware ||
|
|
4158
|
+
const hw = n.hardware || getLocalHardware();
|
|
4151
4159
|
return {
|
|
4152
4160
|
active: !!n.active,
|
|
4153
4161
|
status: n.status || 'stopped',
|
|
@@ -4155,7 +4163,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4155
4163
|
layers: n.layers || null,
|
|
4156
4164
|
model: n.model || null,
|
|
4157
4165
|
sessions: n.sessions || 0,
|
|
4158
|
-
hardware:
|
|
4166
|
+
hardware: hw,
|
|
4159
4167
|
installed: !!(daemon.config?.networkBeta?.installed),
|
|
4160
4168
|
ram_mb: Number(hw.ram_mb) || 0,
|
|
4161
4169
|
vram_mb: Number(hw.vram_mb) || 0,
|
|
@@ -4288,7 +4296,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4288
4296
|
layers: null,
|
|
4289
4297
|
model: null,
|
|
4290
4298
|
sessions: 0,
|
|
4291
|
-
hardware:
|
|
4299
|
+
hardware: getLocalHardware(),
|
|
4292
4300
|
startedAt: Date.now(),
|
|
4293
4301
|
events: [],
|
|
4294
4302
|
};
|
|
@@ -4460,13 +4468,15 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4460
4468
|
daemon.networkNode.layers = self.layers;
|
|
4461
4469
|
changed = true;
|
|
4462
4470
|
}
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
}
|
|
4471
|
+
const prev = daemon.networkNode.hardware || getLocalHardware();
|
|
4472
|
+
const enriched = { ...prev };
|
|
4473
|
+
if (self.device) enriched.device = self.device;
|
|
4474
|
+
if (self.gpu_model) { enriched.gpu = self.gpu_model; enriched.gpu_model = self.gpu_model; }
|
|
4475
|
+
if (Number(self.ram_mb) > 0) { enriched.ram_mb = Number(self.ram_mb); }
|
|
4476
|
+
if (Number(self.vram_mb) > 0) { enriched.vram_mb = Number(self.vram_mb); enriched.memory = enriched.vram_mb >= 1024 ? `${(enriched.vram_mb / 1024).toFixed(1)} GB` : `${enriched.vram_mb} MB`; }
|
|
4477
|
+
if (Number(self.cpu_cores) > 0) { enriched.cpu_cores = Number(self.cpu_cores); enriched.cpuCores = Number(self.cpu_cores); }
|
|
4478
|
+
daemon.networkNode.hardware = enriched;
|
|
4479
|
+
changed = true;
|
|
4470
4480
|
}
|
|
4471
4481
|
const availModel = Array.isArray(data.models)
|
|
4472
4482
|
? data.models.find((m) => m && m.available !== false)
|
|
@@ -4479,25 +4489,39 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4479
4489
|
}
|
|
4480
4490
|
|
|
4481
4491
|
const capStr = (s, max = 200) => (typeof s === 'string' ? s.slice(0, max) : s);
|
|
4482
|
-
const
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4492
|
+
const selfId = daemon.networkNode?.nodeId;
|
|
4493
|
+
const localHw = getLocalHardware();
|
|
4494
|
+
const safeNodes = (Array.isArray(data.nodes) ? data.nodes : []).map((n) => {
|
|
4495
|
+
const nid = n.node_id || n.nodeId || '';
|
|
4496
|
+
const isSelf = selfId && nid && (nid === selfId || (nid.length >= 6 && selfId.startsWith(nid.replace(/\.{2,}$/, ''))));
|
|
4497
|
+
const base = {
|
|
4498
|
+
node_id: capStr(nid),
|
|
4499
|
+
device: capStr(n.device),
|
|
4500
|
+
layers: Array.isArray(n.layers) ? n.layers.slice(0, 2) : n.layers,
|
|
4501
|
+
status: capStr(n.status, 50),
|
|
4502
|
+
active_sessions: n.active_sessions ?? 0,
|
|
4503
|
+
ram_mb: Number(n.ram_mb) || 0,
|
|
4504
|
+
vram_mb: Number(n.vram_mb) || 0,
|
|
4505
|
+
gpu_model: capStr(n.gpu_model || '', 200),
|
|
4506
|
+
cpu_cores: Number(n.cpu_cores) || 0,
|
|
4507
|
+
bandwidth_mbps: Number(n.bandwidth_mbps) || 0.0,
|
|
4508
|
+
max_context_length: Number(n.max_context_length) || 0,
|
|
4509
|
+
load: Number(n.load) || 0.0,
|
|
4510
|
+
gpu_utilization_pct: Number(n.gpu_utilization_pct) || 0,
|
|
4511
|
+
vram_used_mb: Number(n.vram_used_mb) || 0,
|
|
4512
|
+
ram_used_mb: Number(n.ram_used_mb) || 0,
|
|
4513
|
+
ram_pct: Number(n.ram_pct) || 0,
|
|
4514
|
+
uptime_seconds: Number(n.uptime_seconds) || 0,
|
|
4515
|
+
};
|
|
4516
|
+
if (isSelf) {
|
|
4517
|
+
if (!base.device) base.device = localHw.device;
|
|
4518
|
+
if (!base.gpu_model) base.gpu_model = localHw.gpu_model || '';
|
|
4519
|
+
if (!base.ram_mb) base.ram_mb = localHw.ram_mb;
|
|
4520
|
+
if (!base.vram_mb) base.vram_mb = localHw.vram_mb;
|
|
4521
|
+
if (!base.cpu_cores) base.cpu_cores = localHw.cpu_cores;
|
|
4522
|
+
}
|
|
4523
|
+
return base;
|
|
4524
|
+
});
|
|
4501
4525
|
|
|
4502
4526
|
return res.json({
|
|
4503
4527
|
nodes: safeNodes,
|
|
@@ -85,7 +85,7 @@ export class TunnelManager {
|
|
|
85
85
|
}));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
save({ name, host, user, port, sshKeyPath, autoStart, autoConnect }) {
|
|
88
|
+
save({ name, host, user, port, sshKeyPath, autoStart, autoConnect, projectDir }) {
|
|
89
89
|
validateField(name, 'name');
|
|
90
90
|
validateField(host, 'host');
|
|
91
91
|
validateField(user, 'user');
|
|
@@ -104,6 +104,15 @@ export class TunnelManager {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
if (projectDir) {
|
|
108
|
+
if (typeof projectDir !== 'string' || !projectDir.startsWith('/')) {
|
|
109
|
+
throw new Error('projectDir must be an absolute path');
|
|
110
|
+
}
|
|
111
|
+
if (/[;|&`$(){}[\]<>!#\n\r\\]/.test(projectDir)) {
|
|
112
|
+
throw new Error('Invalid characters in projectDir');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
107
116
|
const id = crypto.randomUUID().slice(0, 8);
|
|
108
117
|
const entry = {
|
|
109
118
|
id,
|
|
@@ -114,6 +123,7 @@ export class TunnelManager {
|
|
|
114
123
|
sshKeyPath: sshKeyPath || null,
|
|
115
124
|
autoStart: !!autoStart,
|
|
116
125
|
autoConnect: !!autoConnect,
|
|
126
|
+
projectDir: projectDir ? projectDir.trim() : null,
|
|
117
127
|
createdAt: new Date().toISOString(),
|
|
118
128
|
};
|
|
119
129
|
|
|
@@ -163,6 +173,19 @@ export class TunnelManager {
|
|
|
163
173
|
}
|
|
164
174
|
if (config.autoStart !== undefined) merged.autoStart = !!config.autoStart;
|
|
165
175
|
if (config.autoConnect !== undefined) merged.autoConnect = !!config.autoConnect;
|
|
176
|
+
if (config.projectDir !== undefined) {
|
|
177
|
+
if (config.projectDir) {
|
|
178
|
+
if (typeof config.projectDir !== 'string' || !config.projectDir.startsWith('/')) {
|
|
179
|
+
throw new Error('projectDir must be an absolute path');
|
|
180
|
+
}
|
|
181
|
+
if (/[;|&`$(){}[\]<>!#\n\r\\]/.test(config.projectDir)) {
|
|
182
|
+
throw new Error('Invalid characters in projectDir');
|
|
183
|
+
}
|
|
184
|
+
merged.projectDir = config.projectDir.trim();
|
|
185
|
+
} else {
|
|
186
|
+
merged.projectDir = null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
166
189
|
|
|
167
190
|
this.saved.set(id, merged);
|
|
168
191
|
this._save();
|
|
@@ -380,7 +403,7 @@ export class TunnelManager {
|
|
|
380
403
|
'-o', 'ConnectTimeout=10',
|
|
381
404
|
'-o', 'BatchMode=yes',
|
|
382
405
|
target,
|
|
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)'`,
|
|
406
|
+
`bash -lc '${config.projectDir ? `cd "${config.projectDir}" && ` : ''}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)'`,
|
|
384
407
|
], {
|
|
385
408
|
encoding: 'utf8',
|
|
386
409
|
timeout: 45000,
|