groove-dev 0.12.2 → 0.12.4
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/src/commands/start.js +1 -3
- package/node_modules/@groove-dev/daemon/src/api.js +12 -9
- package/node_modules/@groove-dev/daemon/src/firstrun.js +11 -11
- package/node_modules/@groove-dev/daemon/src/index.js +9 -11
- package/package.json +1 -1
- package/packages/cli/src/commands/start.js +1 -3
- package/packages/daemon/src/api.js +12 -9
- package/packages/daemon/src/firstrun.js +11 -11
- package/packages/daemon/src/index.js +9 -11
|
@@ -26,9 +26,7 @@ export async function start(options) {
|
|
|
26
26
|
process.on('SIGTERM', shutdown);
|
|
27
27
|
|
|
28
28
|
await daemon.start();
|
|
29
|
-
|
|
30
|
-
const guiUrl = `http://${isRemote ? daemon.host : 'localhost'}:${daemon.port}`;
|
|
31
|
-
console.log(chalk.green('Ready.') + ` Open ${guiUrl} for the GUI.`);
|
|
29
|
+
console.log(chalk.green('Ready.'));
|
|
32
30
|
} catch (err) {
|
|
33
31
|
console.error(chalk.red('Failed to start:'), err.message);
|
|
34
32
|
process.exit(1);
|
|
@@ -14,16 +14,19 @@ export function createApi(app, daemon) {
|
|
|
14
14
|
// CORS — restrict to localhost + bound interface origins
|
|
15
15
|
app.use((req, res, next) => {
|
|
16
16
|
const origin = req.headers.origin;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
let allowed = false;
|
|
18
|
+
if (!origin) {
|
|
19
|
+
allowed = true;
|
|
20
|
+
} else {
|
|
21
|
+
try {
|
|
22
|
+
const url = new URL(origin);
|
|
23
|
+
// Allow any localhost origin (any port — tunnels change the port)
|
|
24
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') allowed = true;
|
|
25
|
+
// Allow the bound interface (for Tailscale/LAN access)
|
|
26
|
+
if (daemon.host && daemon.host !== '127.0.0.1' && url.hostname === daemon.host) allowed = true;
|
|
27
|
+
} catch { /* invalid origin */ }
|
|
25
28
|
}
|
|
26
|
-
if (
|
|
29
|
+
if (allowed) {
|
|
27
30
|
res.header('Access-Control-Allow-Origin', origin || '*');
|
|
28
31
|
}
|
|
29
32
|
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -55,22 +55,25 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
55
55
|
console.log('');
|
|
56
56
|
const isRemote = host !== '127.0.0.1';
|
|
57
57
|
|
|
58
|
-
// Detect
|
|
59
|
-
|
|
60
|
-
const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
58
|
+
// Detect environment
|
|
59
|
+
const isVSCode = !!(process.env.VSCODE_GIT_IPC_HANDLE || process.env.VSCODE_IPC_HOOK_CLI || process.env.TERM_PROGRAM === 'vscode');
|
|
61
60
|
const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY);
|
|
61
|
+
const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
62
62
|
const isHeadless = !hasDisplay && !process.env.TERM_PROGRAM;
|
|
63
|
-
const isServer = isSSH || isHeadless;
|
|
63
|
+
const isServer = !isVSCode && (isSSH || isHeadless);
|
|
64
64
|
|
|
65
65
|
if (isRemote) {
|
|
66
66
|
// Bound to network interface (Tailscale/LAN)
|
|
67
67
|
console.log(` GUI: http://${host}:${port}`);
|
|
68
68
|
console.log(` Host: ${host} (network-accessible)`);
|
|
69
|
+
} else if (isVSCode) {
|
|
70
|
+
// VS Code Remote — port forwarding happens automatically
|
|
71
|
+
console.log(` GUI: http://localhost:${port}`);
|
|
72
|
+
console.log(` VS Code forwards this port automatically.`);
|
|
69
73
|
} else if (isServer) {
|
|
70
|
-
//
|
|
74
|
+
// Plain SSH / headless — need groove connect
|
|
71
75
|
const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
|
|
72
76
|
|
|
73
|
-
// Get server IP: try SSH_CONNECTION first, fall back to network interfaces
|
|
74
77
|
let serverIp = '';
|
|
75
78
|
const sshConn = process.env.SSH_CONNECTION || '';
|
|
76
79
|
if (sshConn) {
|
|
@@ -92,12 +95,9 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
92
95
|
|
|
93
96
|
console.log(` Daemon running on port ${port}`);
|
|
94
97
|
console.log('');
|
|
95
|
-
console.log(' To open the GUI,
|
|
96
|
-
console.log('');
|
|
97
|
-
console.log(` groove connect ${sshUser}@${serverIp}`);
|
|
98
|
+
console.log(' To open the GUI, open a terminal on your Mac/PC and run:');
|
|
98
99
|
console.log('');
|
|
99
|
-
console.log(`
|
|
100
|
-
console.log(` npm i -g groove-dev`);
|
|
100
|
+
console.log(` npx groove-dev connect ${sshUser}@${serverIp}`);
|
|
101
101
|
} else {
|
|
102
102
|
// Local machine with display
|
|
103
103
|
console.log(` GUI: http://localhost:${port}`);
|
|
@@ -123,18 +123,16 @@ export class Daemon {
|
|
|
123
123
|
maxPayload: 1024 * 1024, // 1MB max message
|
|
124
124
|
verifyClient: ({ req }) => {
|
|
125
125
|
const origin = req.headers.origin;
|
|
126
|
-
// Allow: no origin (CLI/native clients)
|
|
126
|
+
// Allow: no origin (CLI/native clients)
|
|
127
127
|
if (!origin) return true;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
'
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
return allowed.includes(origin);
|
|
128
|
+
try {
|
|
129
|
+
const url = new URL(origin);
|
|
130
|
+
// Allow any localhost origin (any port — tunnels change the port)
|
|
131
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') return true;
|
|
132
|
+
// Allow the bound interface (for Tailscale/LAN access)
|
|
133
|
+
if (this.host !== DEFAULT_HOST && url.hostname === this.host) return true;
|
|
134
|
+
} catch { /* invalid origin */ }
|
|
135
|
+
return false;
|
|
138
136
|
},
|
|
139
137
|
});
|
|
140
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"description": "Open-source agent orchestration layer for AI coding tools. GUI dashboard, multi-agent coordination, zero cold-start (Journalist), infinite sessions (adaptive context rotation), AI Project Manager, Quick Launch. Works with Claude Code, Codex, Gemini CLI, Ollama.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -26,9 +26,7 @@ export async function start(options) {
|
|
|
26
26
|
process.on('SIGTERM', shutdown);
|
|
27
27
|
|
|
28
28
|
await daemon.start();
|
|
29
|
-
|
|
30
|
-
const guiUrl = `http://${isRemote ? daemon.host : 'localhost'}:${daemon.port}`;
|
|
31
|
-
console.log(chalk.green('Ready.') + ` Open ${guiUrl} for the GUI.`);
|
|
29
|
+
console.log(chalk.green('Ready.'));
|
|
32
30
|
} catch (err) {
|
|
33
31
|
console.error(chalk.red('Failed to start:'), err.message);
|
|
34
32
|
process.exit(1);
|
|
@@ -14,16 +14,19 @@ export function createApi(app, daemon) {
|
|
|
14
14
|
// CORS — restrict to localhost + bound interface origins
|
|
15
15
|
app.use((req, res, next) => {
|
|
16
16
|
const origin = req.headers.origin;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
let allowed = false;
|
|
18
|
+
if (!origin) {
|
|
19
|
+
allowed = true;
|
|
20
|
+
} else {
|
|
21
|
+
try {
|
|
22
|
+
const url = new URL(origin);
|
|
23
|
+
// Allow any localhost origin (any port — tunnels change the port)
|
|
24
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') allowed = true;
|
|
25
|
+
// Allow the bound interface (for Tailscale/LAN access)
|
|
26
|
+
if (daemon.host && daemon.host !== '127.0.0.1' && url.hostname === daemon.host) allowed = true;
|
|
27
|
+
} catch { /* invalid origin */ }
|
|
25
28
|
}
|
|
26
|
-
if (
|
|
29
|
+
if (allowed) {
|
|
27
30
|
res.header('Access-Control-Allow-Origin', origin || '*');
|
|
28
31
|
}
|
|
29
32
|
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -55,22 +55,25 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
55
55
|
console.log('');
|
|
56
56
|
const isRemote = host !== '127.0.0.1';
|
|
57
57
|
|
|
58
|
-
// Detect
|
|
59
|
-
|
|
60
|
-
const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
58
|
+
// Detect environment
|
|
59
|
+
const isVSCode = !!(process.env.VSCODE_GIT_IPC_HANDLE || process.env.VSCODE_IPC_HOOK_CLI || process.env.TERM_PROGRAM === 'vscode');
|
|
61
60
|
const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY);
|
|
61
|
+
const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
62
62
|
const isHeadless = !hasDisplay && !process.env.TERM_PROGRAM;
|
|
63
|
-
const isServer = isSSH || isHeadless;
|
|
63
|
+
const isServer = !isVSCode && (isSSH || isHeadless);
|
|
64
64
|
|
|
65
65
|
if (isRemote) {
|
|
66
66
|
// Bound to network interface (Tailscale/LAN)
|
|
67
67
|
console.log(` GUI: http://${host}:${port}`);
|
|
68
68
|
console.log(` Host: ${host} (network-accessible)`);
|
|
69
|
+
} else if (isVSCode) {
|
|
70
|
+
// VS Code Remote — port forwarding happens automatically
|
|
71
|
+
console.log(` GUI: http://localhost:${port}`);
|
|
72
|
+
console.log(` VS Code forwards this port automatically.`);
|
|
69
73
|
} else if (isServer) {
|
|
70
|
-
//
|
|
74
|
+
// Plain SSH / headless — need groove connect
|
|
71
75
|
const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
|
|
72
76
|
|
|
73
|
-
// Get server IP: try SSH_CONNECTION first, fall back to network interfaces
|
|
74
77
|
let serverIp = '';
|
|
75
78
|
const sshConn = process.env.SSH_CONNECTION || '';
|
|
76
79
|
if (sshConn) {
|
|
@@ -92,12 +95,9 @@ export function printWelcome(port, host = '127.0.0.1') {
|
|
|
92
95
|
|
|
93
96
|
console.log(` Daemon running on port ${port}`);
|
|
94
97
|
console.log('');
|
|
95
|
-
console.log(' To open the GUI,
|
|
96
|
-
console.log('');
|
|
97
|
-
console.log(` groove connect ${sshUser}@${serverIp}`);
|
|
98
|
+
console.log(' To open the GUI, open a terminal on your Mac/PC and run:');
|
|
98
99
|
console.log('');
|
|
99
|
-
console.log(`
|
|
100
|
-
console.log(` npm i -g groove-dev`);
|
|
100
|
+
console.log(` npx groove-dev connect ${sshUser}@${serverIp}`);
|
|
101
101
|
} else {
|
|
102
102
|
// Local machine with display
|
|
103
103
|
console.log(` GUI: http://localhost:${port}`);
|
|
@@ -123,18 +123,16 @@ export class Daemon {
|
|
|
123
123
|
maxPayload: 1024 * 1024, // 1MB max message
|
|
124
124
|
verifyClient: ({ req }) => {
|
|
125
125
|
const origin = req.headers.origin;
|
|
126
|
-
// Allow: no origin (CLI/native clients)
|
|
126
|
+
// Allow: no origin (CLI/native clients)
|
|
127
127
|
if (!origin) return true;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
'
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
return allowed.includes(origin);
|
|
128
|
+
try {
|
|
129
|
+
const url = new URL(origin);
|
|
130
|
+
// Allow any localhost origin (any port — tunnels change the port)
|
|
131
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') return true;
|
|
132
|
+
// Allow the bound interface (for Tailscale/LAN access)
|
|
133
|
+
if (this.host !== DEFAULT_HOST && url.hostname === this.host) return true;
|
|
134
|
+
} catch { /* invalid origin */ }
|
|
135
|
+
return false;
|
|
138
136
|
},
|
|
139
137
|
});
|
|
140
138
|
|