groove-dev 0.12.0 → 0.12.1

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.
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { existsSync, writeFileSync, readFileSync } from 'fs';
5
5
  import { resolve } from 'path';
6
+ import { networkInterfaces } from 'os';
6
7
  import { listProviders } from './providers/index.js';
7
8
 
8
9
  const DEFAULT_CONFIG = {
@@ -53,17 +54,42 @@ export function printWelcome(port, host = '127.0.0.1') {
53
54
 
54
55
  console.log('');
55
56
  const isRemote = host !== '127.0.0.1';
56
- const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT);
57
+
58
+ // Detect headless/server environment: no graphical display available
59
+ // Works even through sudo (which strips SSH_* env vars)
60
+ const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
61
+ const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY);
62
+ const isHeadless = !hasDisplay && !process.env.TERM_PROGRAM; // No GUI, no desktop terminal app
63
+ const isServer = isSSH || isHeadless;
57
64
 
58
65
  if (isRemote) {
59
66
  // Bound to network interface (Tailscale/LAN)
60
67
  console.log(` GUI: http://${host}:${port}`);
61
68
  console.log(` Host: ${host} (network-accessible)`);
62
- } else if (isSSH) {
63
- // Running on a VPS via SSH — print remote access instructions
64
- const sshUser = process.env.USER || 'user';
69
+ } else if (isServer) {
70
+ // VPS / headless server — print remote access instructions
71
+ const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
72
+
73
+ // Get server IP: try SSH_CONNECTION first, fall back to first public network interface
74
+ let serverIp = '';
65
75
  const sshConn = process.env.SSH_CONNECTION || '';
66
- const serverIp = sshConn.split(' ')[2] || '<this-server-ip>';
76
+ if (sshConn) {
77
+ serverIp = sshConn.split(' ')[2] || '';
78
+ }
79
+ if (!serverIp) {
80
+ // Find first non-internal IPv4 address
81
+ const nets = networkInterfaces();
82
+ for (const addrs of Object.values(nets)) {
83
+ for (const addr of addrs) {
84
+ if (addr.family === 'IPv4' && !addr.internal) {
85
+ serverIp = addr.address;
86
+ break;
87
+ }
88
+ }
89
+ if (serverIp) break;
90
+ }
91
+ }
92
+ serverIp = serverIp || '<this-server-ip>';
67
93
 
68
94
  console.log(` GUI: http://localhost:${port} (this server only)`);
69
95
  console.log('');
@@ -74,7 +100,7 @@ export function printWelcome(port, host = '127.0.0.1') {
74
100
  console.log(` ssh -L ${port + 1}:localhost:${port} ${sshUser}@${serverIp}`);
75
101
  console.log(` Then open http://localhost:${port + 1}`);
76
102
  } else {
77
- // Local machine
103
+ // Local machine with display
78
104
  console.log(` GUI: http://localhost:${port}`);
79
105
  }
80
106
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
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)",
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { existsSync, writeFileSync, readFileSync } from 'fs';
5
5
  import { resolve } from 'path';
6
+ import { networkInterfaces } from 'os';
6
7
  import { listProviders } from './providers/index.js';
7
8
 
8
9
  const DEFAULT_CONFIG = {
@@ -53,17 +54,42 @@ export function printWelcome(port, host = '127.0.0.1') {
53
54
 
54
55
  console.log('');
55
56
  const isRemote = host !== '127.0.0.1';
56
- const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT);
57
+
58
+ // Detect headless/server environment: no graphical display available
59
+ // Works even through sudo (which strips SSH_* env vars)
60
+ const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
61
+ const isSSH = !!(process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY);
62
+ const isHeadless = !hasDisplay && !process.env.TERM_PROGRAM; // No GUI, no desktop terminal app
63
+ const isServer = isSSH || isHeadless;
57
64
 
58
65
  if (isRemote) {
59
66
  // Bound to network interface (Tailscale/LAN)
60
67
  console.log(` GUI: http://${host}:${port}`);
61
68
  console.log(` Host: ${host} (network-accessible)`);
62
- } else if (isSSH) {
63
- // Running on a VPS via SSH — print remote access instructions
64
- const sshUser = process.env.USER || 'user';
69
+ } else if (isServer) {
70
+ // VPS / headless server — print remote access instructions
71
+ const sshUser = process.env.SUDO_USER || process.env.USER || 'user';
72
+
73
+ // Get server IP: try SSH_CONNECTION first, fall back to first public network interface
74
+ let serverIp = '';
65
75
  const sshConn = process.env.SSH_CONNECTION || '';
66
- const serverIp = sshConn.split(' ')[2] || '<this-server-ip>';
76
+ if (sshConn) {
77
+ serverIp = sshConn.split(' ')[2] || '';
78
+ }
79
+ if (!serverIp) {
80
+ // Find first non-internal IPv4 address
81
+ const nets = networkInterfaces();
82
+ for (const addrs of Object.values(nets)) {
83
+ for (const addr of addrs) {
84
+ if (addr.family === 'IPv4' && !addr.internal) {
85
+ serverIp = addr.address;
86
+ break;
87
+ }
88
+ }
89
+ if (serverIp) break;
90
+ }
91
+ }
92
+ serverIp = serverIp || '<this-server-ip>';
67
93
 
68
94
  console.log(` GUI: http://localhost:${port} (this server only)`);
69
95
  console.log('');
@@ -74,7 +100,7 @@ export function printWelcome(port, host = '127.0.0.1') {
74
100
  console.log(` ssh -L ${port + 1}:localhost:${port} ${sshUser}@${serverIp}`);
75
101
  console.log(` Then open http://localhost:${port + 1}`);
76
102
  } else {
77
- // Local machine
103
+ // Local machine with display
78
104
  console.log(` GUI: http://localhost:${port}`);
79
105
  }
80
106