@geekbeer/minion 2.25.0 → 2.33.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.
Files changed (45) hide show
  1. package/{config.js → core/config.js} +4 -4
  2. package/{lib → core/lib}/llm-checker.js +9 -16
  3. package/{lib → core/lib}/log-manager.js +3 -2
  4. package/core/lib/platform.js +117 -0
  5. package/{routes → core/routes}/health.js +1 -1
  6. package/{routes → core/routes}/routines.js +3 -3
  7. package/{routes → core/routes}/skills.js +3 -3
  8. package/{routes → core/routes}/workflows.js +4 -4
  9. package/{chat-store.js → core/stores/chat-store.js} +5 -5
  10. package/{execution-store.js → core/stores/execution-store.js} +5 -5
  11. package/{routine-store.js → core/stores/routine-store.js} +6 -6
  12. package/{workflow-store.js → core/stores/workflow-store.js} +6 -7
  13. package/{minion-cli.sh → linux/minion-cli.sh} +63 -6
  14. package/{routes → linux/routes}/chat.js +3 -3
  15. package/{routes → linux/routes}/commands.js +1 -1
  16. package/{routes → linux/routes}/config.js +3 -3
  17. package/{routes → linux/routes}/directives.js +5 -5
  18. package/{routes → linux/routes}/files.js +2 -2
  19. package/{routes → linux/routes}/terminal.js +2 -2
  20. package/{routine-runner.js → linux/routine-runner.js} +4 -4
  21. package/{server.js → linux/server.js} +71 -36
  22. package/{workflow-runner.js → linux/workflow-runner.js} +4 -4
  23. package/package.json +16 -20
  24. package/win/bin/hq-win.js +18 -0
  25. package/win/bin/hq.ps1 +108 -0
  26. package/win/bin/minion-cli-win.js +20 -0
  27. package/win/lib/process-manager.js +112 -0
  28. package/win/minion-cli.ps1 +877 -0
  29. package/win/routes/chat.js +280 -0
  30. package/win/routes/commands.js +101 -0
  31. package/win/routes/config.js +227 -0
  32. package/win/routes/directives.js +136 -0
  33. package/win/routes/files.js +283 -0
  34. package/win/routes/terminal.js +335 -0
  35. package/win/routine-runner.js +324 -0
  36. package/win/server.js +230 -0
  37. package/win/terminal-server.js +242 -0
  38. package/win/workflow-runner.js +380 -0
  39. package/routes/index.js +0 -106
  40. /package/{api.js → core/api.js} +0 -0
  41. /package/{lib → core/lib}/auth.js +0 -0
  42. /package/{routes → core/routes}/auth.js +0 -0
  43. /package/{bin → linux/bin}/hq +0 -0
  44. /package/{lib → linux/lib}/process-manager.js +0 -0
  45. /package/{terminal-proxy.js → linux/terminal-proxy.js} +0 -0
@@ -19,13 +19,13 @@ const { execSync } = require('child_process')
19
19
 
20
20
  /**
21
21
  * Resolve the correct home directory for the minion user.
22
- * In supervisord environments, os.homedir() may return /root because
23
- * the HOME env var is not inherited. This function uses MINION_USER
24
- * (set in .env during setup) to look up the correct home via getent passwd.
22
+ * On Linux, supervisord environments may set HOME=/root incorrectly.
23
+ * This function uses MINION_USER + getent passwd to find the correct home.
24
+ * On Windows, os.homedir() is always correct (returns %USERPROFILE%).
25
25
  */
26
26
  function resolveHomeDir() {
27
27
  const minionUser = process.env.MINION_USER
28
- if (minionUser) {
28
+ if (minionUser && process.platform !== 'win32') {
29
29
  try {
30
30
  const entry = execSync(`getent passwd ${minionUser}`, { encoding: 'utf-8' }).trim()
31
31
  const home = entry.split(':')[5]
@@ -10,26 +10,13 @@ const fs = require('fs')
10
10
  const path = require('path')
11
11
  const { execSync } = require('child_process')
12
12
  const { config } = require('../config')
13
+ const { IS_WINDOWS, buildExtendedPath } = require('./platform')
13
14
 
14
15
  const CACHE_TTL_MS = 60000
15
16
 
16
17
  let cachedResult = null
17
18
  let cachedAt = 0
18
19
 
19
- /**
20
- * Build extended PATH that includes common Claude CLI installation locations
21
- */
22
- function getExtendedPath() {
23
- const additionalPaths = [
24
- path.join(config.HOME_DIR, '.local', 'bin'),
25
- path.join(config.HOME_DIR, 'bin'),
26
- path.join(config.HOME_DIR, '.npm-global', 'bin'),
27
- path.join(config.HOME_DIR, '.claude', 'bin'),
28
- '/usr/local/bin',
29
- ]
30
- return [...additionalPaths, process.env.PATH || '/usr/bin:/bin'].join(':')
31
- }
32
-
33
20
  /**
34
21
  * Check Claude Code authentication.
35
22
  * First checks known credential file locations (fast path),
@@ -57,14 +44,16 @@ function isClaudeAuthenticated() {
57
44
  // Fallback: check via claude CLI command (handles newer credential storage)
58
45
  try {
59
46
  const claudePath = path.join(config.HOME_DIR, '.local', 'bin', 'claude')
60
- const claudeBin = fs.existsSync(claudePath) ? claudePath : 'claude'
47
+ const claudeBin = (!IS_WINDOWS && fs.existsSync(claudePath)) ? claudePath : 'claude'
61
48
  execSync(`${claudeBin} auth whoami`, {
62
49
  encoding: 'utf-8',
63
50
  timeout: 5000,
64
51
  stdio: 'pipe',
65
52
  env: {
53
+ ...process.env,
66
54
  HOME: config.HOME_DIR,
67
- PATH: getExtendedPath(),
55
+ ...(IS_WINDOWS && { USERPROFILE: config.HOME_DIR }),
56
+ PATH: buildExtendedPath(config.HOME_DIR),
68
57
  },
69
58
  })
70
59
  return true
@@ -83,6 +72,10 @@ function isGeminiAuthenticated() {
83
72
  const possiblePaths = [
84
73
  path.join(config.HOME_DIR, '.config', 'gemini'),
85
74
  path.join(config.HOME_DIR, '.config', 'gcloud', 'application_default_credentials.json'),
75
+ // Windows-specific locations
76
+ ...(IS_WINDOWS ? [
77
+ path.join(config.HOME_DIR, 'AppData', 'Roaming', 'gcloud', 'application_default_credentials.json'),
78
+ ] : []),
86
79
  ]
87
80
  for (const p of possiblePaths) {
88
81
  try {
@@ -6,9 +6,10 @@
6
6
 
7
7
  const fs = require('fs').promises
8
8
  const path = require('path')
9
+ const platform = require('./platform')
9
10
 
10
- // Log storage configuration
11
- const LOG_DIR = '/opt/minion-agent/logs'
11
+ // Log storage configuration (platform-aware via platform.js)
12
+ const LOG_DIR = platform.LOG_DIR
12
13
  const MAX_LOG_FILES = 100
13
14
 
14
15
  /**
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Cross-platform utility module
3
+ *
4
+ * Provides platform-aware paths, separators, and helpers.
5
+ * Used by core/ modules (stores, log-manager, llm-checker) and win/ modules
6
+ * to provide consistent cross-platform behavior.
7
+ */
8
+
9
+ const os = require('os')
10
+ const path = require('path')
11
+ const fs = require('fs')
12
+
13
+ const IS_WINDOWS = process.platform === 'win32'
14
+ const PATH_SEPARATOR = path.delimiter // ';' on Windows, ':' on Unix
15
+ const TEMP_DIR = os.tmpdir()
16
+
17
+ /**
18
+ * Resolve the data directory for minion agent persistent files.
19
+ * Windows: %PROGRAMDATA%\minion-agent (or fallback to %USERPROFILE%\.minion-agent)
20
+ * Linux: /opt/minion-agent (existing behavior)
21
+ */
22
+ function resolveDataDir() {
23
+ if (IS_WINDOWS) {
24
+ const programData = process.env.PROGRAMDATA || process.env.ALLUSERSPROFILE
25
+ if (programData) {
26
+ const dir = path.join(programData, 'minion-agent')
27
+ try {
28
+ fs.mkdirSync(dir, { recursive: true })
29
+ return dir
30
+ } catch {
31
+ // Fall through to home-based path
32
+ }
33
+ }
34
+ return path.join(os.homedir(), '.minion-agent')
35
+ }
36
+ return '/opt/minion-agent'
37
+ }
38
+
39
+ const DATA_DIR = resolveDataDir()
40
+ const LOG_DIR = path.join(DATA_DIR, 'logs')
41
+ const MARKER_DIR = path.join(TEMP_DIR, 'minion-executions')
42
+
43
+ /**
44
+ * Build extended PATH including common CLI installation locations.
45
+ * Uses the correct platform separator.
46
+ * @param {string} homeDir - User home directory
47
+ * @returns {string} Extended PATH string
48
+ */
49
+ function buildExtendedPath(homeDir) {
50
+ const additionalPaths = IS_WINDOWS
51
+ ? [
52
+ path.join(homeDir, '.local', 'bin'),
53
+ path.join(homeDir, '.npm-global'),
54
+ path.join(homeDir, '.claude', 'bin'),
55
+ path.join(homeDir, 'AppData', 'Roaming', 'npm'),
56
+ path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'nodejs'),
57
+ ]
58
+ : [
59
+ path.join(homeDir, 'bin'),
60
+ path.join(homeDir, '.npm-global', 'bin'),
61
+ path.join(homeDir, '.local', 'bin'),
62
+ path.join(homeDir, '.claude', 'bin'),
63
+ '/usr/local/bin',
64
+ ]
65
+
66
+ const currentPath = process.env.PATH || ''
67
+ return [...additionalPaths, currentPath].join(PATH_SEPARATOR)
68
+ }
69
+
70
+ /**
71
+ * Get exit code file path for a session.
72
+ * @param {string} sessionName - Session identifier
73
+ * @returns {string} Path to exit code file
74
+ */
75
+ function getExitCodePath(sessionName) {
76
+ return path.join(TEMP_DIR, `minion-exit-${sessionName}`)
77
+ }
78
+
79
+ /**
80
+ * Get the default shell for spawning processes.
81
+ * @returns {string} Shell executable path
82
+ */
83
+ function getDefaultShell() {
84
+ if (IS_WINDOWS) {
85
+ return process.env.COMSPEC || 'cmd.exe'
86
+ }
87
+ return process.env.SHELL || '/bin/sh'
88
+ }
89
+
90
+ /**
91
+ * Resolve .env file path.
92
+ * Prefers DATA_DIR/.env, falls back to ~/minion.env.
93
+ * @param {string} homeDir - User home directory
94
+ * @returns {string} Path to .env file
95
+ */
96
+ function resolveEnvFilePath(homeDir) {
97
+ const dataEnv = path.join(DATA_DIR, '.env')
98
+ try {
99
+ fs.accessSync(path.dirname(dataEnv), fs.constants.W_OK)
100
+ return dataEnv
101
+ } catch {
102
+ return path.join(homeDir, 'minion.env')
103
+ }
104
+ }
105
+
106
+ module.exports = {
107
+ IS_WINDOWS,
108
+ PATH_SEPARATOR,
109
+ TEMP_DIR,
110
+ DATA_DIR,
111
+ LOG_DIR,
112
+ MARKER_DIR,
113
+ buildExtendedPath,
114
+ getExitCodePath,
115
+ getDefaultShell,
116
+ resolveEnvFilePath,
117
+ }
@@ -7,7 +7,7 @@
7
7
  * - POST /api/status - Update status
8
8
  */
9
9
 
10
- const { version } = require('../package.json')
10
+ const { version } = require('../../package.json')
11
11
  const { getLlmServices, isLlmCommandConfigured } = require('../lib/llm-checker')
12
12
 
13
13
  // Shared status state
@@ -12,8 +12,7 @@
12
12
  */
13
13
 
14
14
  const { verifyToken } = require('../lib/auth')
15
- const routineRunner = require('../routine-runner')
16
- const routineStore = require('../routine-store')
15
+ const routineStore = require('../stores/routine-store')
17
16
  const api = require('../api')
18
17
  const { isHqConfigured } = require('../config')
19
18
 
@@ -21,7 +20,8 @@ const { isHqConfigured } = require('../config')
21
20
  * Register routine routes as Fastify plugin
22
21
  * @param {import('fastify').FastifyInstance} fastify
23
22
  */
24
- async function routineRoutes(fastify) {
23
+ async function routineRoutes(fastify, opts) {
24
+ const { routineRunner } = opts
25
25
  // Receive routines from HQ (upsert: add new, update existing)
26
26
  fastify.post('/api/routines', async (request, reply) => {
27
27
  if (!verifyToken(request)) {
@@ -17,8 +17,7 @@ const crypto = require('crypto')
17
17
  const { verifyToken } = require('../lib/auth')
18
18
  const api = require('../api')
19
19
  const { config, isHqConfigured } = require('../config')
20
- const workflowRunner = require('../workflow-runner')
21
- const executionStore = require('../execution-store')
20
+ const executionStore = require('../stores/execution-store')
22
21
  const logManager = require('../lib/log-manager')
23
22
 
24
23
  /**
@@ -128,7 +127,8 @@ async function pushSkillToHQ(name) {
128
127
  * Register skill routes as Fastify plugin
129
128
  * @param {import('fastify').FastifyInstance} fastify
130
129
  */
131
- async function skillRoutes(fastify) {
130
+ async function skillRoutes(fastify, opts) {
131
+ const { workflowRunner } = opts
132
132
  // List deployed skills from local .claude/skills directory
133
133
  fastify.get('/api/list-skills', async (request, reply) => {
134
134
  if (!verifyToken(request)) {
@@ -20,9 +20,8 @@ const fs = require('fs').promises
20
20
  const path = require('path')
21
21
 
22
22
  const { verifyToken } = require('../lib/auth')
23
- const workflowRunner = require('../workflow-runner')
24
- const workflowStore = require('../workflow-store')
25
- const executionStore = require('../execution-store')
23
+ const workflowStore = require('../stores/workflow-store')
24
+ const executionStore = require('../stores/execution-store')
26
25
  const logManager = require('../lib/log-manager')
27
26
  const api = require('../api')
28
27
  const { config, isHqConfigured } = require('../config')
@@ -32,7 +31,8 @@ const { writeSkillToLocal, pushSkillToHQ } = require('./skills')
32
31
  * Register workflow routes as Fastify plugin
33
32
  * @param {import('fastify').FastifyInstance} fastify
34
33
  */
35
- async function workflowRoutes(fastify) {
34
+ async function workflowRoutes(fastify, opts) {
35
+ const { workflowRunner } = opts
36
36
  // Receive workflows from HQ (upsert: add new, update existing)
37
37
  fastify.post('/api/workflows', async (request, reply) => {
38
38
  if (!verifyToken(request)) {
@@ -7,19 +7,19 @@
7
7
  const fs = require('fs').promises
8
8
  const path = require('path')
9
9
 
10
- const { config } = require('./config')
10
+ const { config } = require('../config')
11
+ const { DATA_DIR } = require('../lib/platform')
11
12
 
12
13
  const MAX_MESSAGES = 100
13
14
 
14
15
  /**
15
16
  * Get chat session file path
16
- * Uses /opt/minion-agent/ if available, otherwise home dir
17
+ * Uses DATA_DIR if available (platform-aware), otherwise home dir
17
18
  */
18
19
  function getFilePath() {
19
- const optPath = '/opt/minion-agent/chat-session.json'
20
20
  try {
21
- require('fs').accessSync(path.dirname(optPath))
22
- return optPath
21
+ require('fs').accessSync(DATA_DIR)
22
+ return path.join(DATA_DIR, 'chat-session.json')
23
23
  } catch {
24
24
  return path.join(config.HOME_DIR, 'chat-session.json')
25
25
  }
@@ -7,20 +7,20 @@
7
7
  const fs = require('fs').promises
8
8
  const path = require('path')
9
9
 
10
- const { config } = require('./config')
10
+ const { config } = require('../config')
11
+ const { DATA_DIR } = require('../lib/platform')
11
12
 
12
13
  // Max executions to keep (older ones are pruned)
13
14
  const MAX_EXECUTIONS = 200
14
15
 
15
16
  /**
16
17
  * Get execution file path
17
- * Uses /opt/minion-agent/ if available, otherwise home dir
18
+ * Uses DATA_DIR if available (platform-aware), otherwise home dir
18
19
  */
19
20
  function getExecutionFilePath() {
20
- const optPath = '/opt/minion-agent/executions.json'
21
21
  try {
22
- require('fs').accessSync(path.dirname(optPath))
23
- return optPath
22
+ require('fs').accessSync(DATA_DIR)
23
+ return path.join(DATA_DIR, 'executions.json')
24
24
  } catch {
25
25
  return path.join(config.HOME_DIR, 'executions.json')
26
26
  }
@@ -7,15 +7,15 @@
7
7
  const fs = require('fs').promises
8
8
  const path = require('path')
9
9
 
10
- const { config } = require('./config')
10
+ const { config } = require('../config')
11
+ const { DATA_DIR } = require('../lib/platform')
11
12
 
12
- // Routine file location: /opt/minion-agent/routines.json (systemd/supervisord)
13
- // or ~/routines.json (standalone)
13
+ // Routine file location: DATA_DIR/routines.json (platform-aware)
14
+ // or ~/routines.json (fallback)
14
15
  function getRoutineFilePath() {
15
- const optPath = '/opt/minion-agent/routines.json'
16
16
  try {
17
- require('fs').accessSync(path.dirname(optPath))
18
- return optPath
17
+ require('fs').accessSync(DATA_DIR)
18
+ return path.join(DATA_DIR, 'routines.json')
19
19
  } catch {
20
20
  return path.join(config.HOME_DIR, 'routines.json')
21
21
  }
@@ -7,16 +7,15 @@
7
7
  const fs = require('fs').promises
8
8
  const path = require('path')
9
9
 
10
- const { config } = require('./config')
10
+ const { config } = require('../config')
11
+ const { DATA_DIR } = require('../lib/platform')
11
12
 
12
- // Workflow file location: /opt/minion-agent/workflows.json (systemd/supervisord)
13
- // or ~/workflows.json (standalone)
13
+ // Workflow file location: DATA_DIR/workflows.json (platform-aware)
14
+ // or ~/workflows.json (fallback)
14
15
  function getWorkflowFilePath() {
15
- const optPath = '/opt/minion-agent/workflows.json'
16
- // Use /opt path if it exists (production), otherwise home dir
17
16
  try {
18
- require('fs').accessSync(path.dirname(optPath))
19
- return optPath
17
+ require('fs').accessSync(DATA_DIR)
18
+ return path.join(DATA_DIR, 'workflows.json')
20
19
  } catch {
21
20
  return path.join(config.HOME_DIR, 'workflows.json')
22
21
  }
@@ -41,7 +41,7 @@ fi
41
41
 
42
42
  # Resolve version from package.json (installed location)
43
43
  CLI_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
44
- CLI_VERSION="$(node -p "require('${CLI_DIR}/package.json').version")"
44
+ CLI_VERSION="$(node -p "require('${CLI_DIR}/../package.json').version")"
45
45
 
46
46
  # Use sudo only when not running as root
47
47
  SUDO=""
@@ -89,6 +89,28 @@ svc_control() {
89
89
 
90
90
  AGENT_URL="${MINION_AGENT_URL:-http://localhost:8080}"
91
91
 
92
+ # Detect LAN IPv4 address (best-effort)
93
+ detect_lan_ip() {
94
+ local ip=""
95
+ # Method 1: Use ip route to find the source IP for default gateway
96
+ if command -v ip &>/dev/null; then
97
+ ip=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K[\d.]+' | head -1)
98
+ fi
99
+ # Method 2: Parse ip addr for non-loopback, non-docker, non-APIPA addresses
100
+ if [ -z "$ip" ] && command -v ip &>/dev/null; then
101
+ ip=$(ip -4 addr show scope global 2>/dev/null \
102
+ | grep -oP 'inet \K[\d.]+' \
103
+ | grep -v '^172\.17\.' \
104
+ | grep -v '^169\.254\.' \
105
+ | head -1)
106
+ fi
107
+ # Method 3: hostname -I (available on most Linux distros)
108
+ if [ -z "$ip" ] && command -v hostname &>/dev/null; then
109
+ ip=$(hostname -I 2>/dev/null | awk '{print $1}')
110
+ fi
111
+ echo "$ip"
112
+ }
113
+
92
114
  # Auto-load .env so that API_TOKEN etc. are available in interactive shells
93
115
  ENV_FILE="/opt/minion-agent/.env"
94
116
  if [ -f "$ENV_FILE" ] && [ -r "$ENV_FILE" ]; then
@@ -319,7 +341,7 @@ do_setup() {
319
341
  echo "[6/${TOTAL_STEPS}] Creating service configuration ($PROC_MGR)..."
320
342
  local NPM_ROOT
321
343
  NPM_ROOT="$(npm root -g)"
322
- local SERVER_PATH="${NPM_ROOT}/@geekbeer/minion/server.js"
344
+ local SERVER_PATH="${NPM_ROOT}/@geekbeer/minion/linux/server.js"
323
345
 
324
346
  if [ ! -f "$SERVER_PATH" ]; then
325
347
  echo " ERROR: server.js not found at $SERVER_PATH"
@@ -732,14 +754,33 @@ CFEOF
732
754
  echo "Notifying HQ of setup completion..."
733
755
  local NOTIFY_RESPONSE
734
756
  local HOSTNAME_VAL
757
+ local LAN_IP
735
758
  HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
759
+ LAN_IP=$(detect_lan_ip)
760
+
761
+ # Build request body with LAN IP detection
762
+ # Docker: use hostname (container name) for internal_ip_address (resolved via Docker DNS)
763
+ # Self-hosted: use LAN IP for both fields (hostname is not resolvable on LAN)
764
+ local BODY
765
+ if [ -f /.dockerenv ]; then
766
+ BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
767
+ elif [ -n "$LAN_IP" ]; then
768
+ BODY="{\"internal_ip_address\":\"${LAN_IP}\",\"ip_address\":\"${LAN_IP}\"}"
769
+ else
770
+ BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
771
+ fi
772
+
736
773
  NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
737
774
  -H "Content-Type: application/json" \
738
775
  -H "Authorization: Bearer ${API_TOKEN}" \
739
- -d "{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}" 2>&1) || true
776
+ -d "$BODY" 2>&1) || true
740
777
 
741
778
  if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
742
- echo " -> HQ notified successfully"
779
+ if [ -n "$LAN_IP" ] && [ ! -f /.dockerenv ]; then
780
+ echo " -> HQ notified successfully (LAN IP: ${LAN_IP})"
781
+ else
782
+ echo " -> HQ notified successfully"
783
+ fi
743
784
  else
744
785
  echo " -> HQ notification skipped (HQ may not be reachable)"
745
786
  fi
@@ -915,14 +956,30 @@ SUPEOF
915
956
  echo "[4/4] Notifying HQ..."
916
957
  local NOTIFY_RESPONSE
917
958
  local HOSTNAME_VAL
959
+ local LAN_IP
918
960
  HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
961
+ LAN_IP=$(detect_lan_ip)
962
+
963
+ local BODY
964
+ if [ -f /.dockerenv ]; then
965
+ BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
966
+ elif [ -n "$LAN_IP" ]; then
967
+ BODY="{\"internal_ip_address\":\"${LAN_IP}\",\"ip_address\":\"${LAN_IP}\"}"
968
+ else
969
+ BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
970
+ fi
971
+
919
972
  NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
920
973
  -H "Content-Type: application/json" \
921
974
  -H "Authorization: Bearer ${API_TOKEN}" \
922
- -d "{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}" 2>&1) || true
975
+ -d "$BODY" 2>&1) || true
923
976
 
924
977
  if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
925
- echo " -> HQ notified successfully"
978
+ if [ -n "$LAN_IP" ] && [ ! -f /.dockerenv ]; then
979
+ echo " -> HQ notified successfully (LAN IP: ${LAN_IP})"
980
+ else
981
+ echo " -> HQ notified successfully"
982
+ fi
926
983
  else
927
984
  echo " -> Skipped (heartbeat will notify HQ within 30s)"
928
985
  fi
@@ -17,9 +17,9 @@
17
17
  const { spawn } = require('child_process')
18
18
  const fs = require('fs')
19
19
  const path = require('path')
20
- const { verifyToken } = require('../lib/auth')
21
- const { config } = require('../config')
22
- const chatStore = require('../chat-store')
20
+ const { verifyToken } = require('../../core/lib/auth')
21
+ const { config } = require('../../core/config')
22
+ const chatStore = require('../../core/stores/chat-store')
23
23
 
24
24
  /** @type {import('child_process').ChildProcess | null} */
25
25
  let activeChatChild = null
@@ -10,7 +10,7 @@ const { exec } = require('child_process')
10
10
  const { promisify } = require('util')
11
11
  const execAsync = promisify(exec)
12
12
 
13
- const { verifyToken } = require('../lib/auth')
13
+ const { verifyToken } = require('../../core/lib/auth')
14
14
  const { detectProcessManager, buildAllowedCommands } = require('../lib/process-manager')
15
15
 
16
16
  const PROC_MGR = detectProcessManager()
@@ -10,9 +10,9 @@
10
10
  const { execSync } = require('child_process')
11
11
  const fs = require('fs')
12
12
  const path = require('path')
13
- const { verifyToken } = require('../lib/auth')
14
- const { clearLlmCache } = require('../lib/llm-checker')
15
- const { config } = require('../config')
13
+ const { verifyToken } = require('../../core/lib/auth')
14
+ const { clearLlmCache } = require('../../core/lib/llm-checker')
15
+ const { config } = require('../../core/config')
16
16
 
17
17
  /** Keys that can be read/written via the config API */
18
18
  const ALLOWED_ENV_KEYS = ['LLM_COMMAND']
@@ -12,12 +12,12 @@ const fs = require('fs').promises
12
12
  const path = require('path')
13
13
  const crypto = require('crypto')
14
14
 
15
- const { verifyToken } = require('../lib/auth')
16
- const { config } = require('../config')
17
- const { writeSkillToLocal } = require('./skills')
15
+ const { verifyToken } = require('../../core/lib/auth')
16
+ const { config } = require('../../core/config')
17
+ const { writeSkillToLocal } = require('../../core/routes/skills')
18
18
  const workflowRunner = require('../workflow-runner')
19
- const executionStore = require('../execution-store')
20
- const logManager = require('../lib/log-manager')
19
+ const executionStore = require('../../core/stores/execution-store')
20
+ const logManager = require('../../core/lib/log-manager')
21
21
 
22
22
  /**
23
23
  * Parse frontmatter from skill content to extract body
@@ -16,8 +16,8 @@ const fsSync = require('fs')
16
16
  const path = require('path')
17
17
  const { spawn } = require('child_process')
18
18
 
19
- const { verifyToken } = require('../lib/auth')
20
- const { config } = require('../config')
19
+ const { verifyToken } = require('../../core/lib/auth')
20
+ const { config } = require('../../core/config')
21
21
 
22
22
  /** Base directory for file storage */
23
23
  const FILES_DIR = path.join(config.HOME_DIR, 'files')
@@ -20,8 +20,8 @@ const path = require('path')
20
20
  const net = require('net')
21
21
  const execAsync = promisify(exec)
22
22
 
23
- const { verifyToken } = require('../lib/auth')
24
- const { config } = require('../config')
23
+ const { verifyToken } = require('../../core/lib/auth')
24
+ const { config } = require('../../core/config')
25
25
 
26
26
  // Ensure consistent HOME for tmux socket path
27
27
  const homeDir = config.HOME_DIR
@@ -18,10 +18,10 @@ const path = require('path')
18
18
  const fs = require('fs').promises
19
19
  const execAsync = promisify(exec)
20
20
 
21
- const { config } = require('./config')
22
- const executionStore = require('./execution-store')
23
- const routineStore = require('./routine-store')
24
- const logManager = require('./lib/log-manager')
21
+ const { config } = require('../core/config')
22
+ const executionStore = require('../core/stores/execution-store')
23
+ const routineStore = require('../core/stores/routine-store')
24
+ const logManager = require('../core/lib/log-manager')
25
25
 
26
26
  // Active cron jobs keyed by routine ID
27
27
  const activeJobs = new Map()