@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.
- package/{config.js → core/config.js} +4 -4
- package/{lib → core/lib}/llm-checker.js +9 -16
- package/{lib → core/lib}/log-manager.js +3 -2
- package/core/lib/platform.js +117 -0
- package/{routes → core/routes}/health.js +1 -1
- package/{routes → core/routes}/routines.js +3 -3
- package/{routes → core/routes}/skills.js +3 -3
- package/{routes → core/routes}/workflows.js +4 -4
- package/{chat-store.js → core/stores/chat-store.js} +5 -5
- package/{execution-store.js → core/stores/execution-store.js} +5 -5
- package/{routine-store.js → core/stores/routine-store.js} +6 -6
- package/{workflow-store.js → core/stores/workflow-store.js} +6 -7
- package/{minion-cli.sh → linux/minion-cli.sh} +63 -6
- package/{routes → linux/routes}/chat.js +3 -3
- package/{routes → linux/routes}/commands.js +1 -1
- package/{routes → linux/routes}/config.js +3 -3
- package/{routes → linux/routes}/directives.js +5 -5
- package/{routes → linux/routes}/files.js +2 -2
- package/{routes → linux/routes}/terminal.js +2 -2
- package/{routine-runner.js → linux/routine-runner.js} +4 -4
- package/{server.js → linux/server.js} +71 -36
- package/{workflow-runner.js → linux/workflow-runner.js} +4 -4
- package/package.json +16 -20
- package/win/bin/hq-win.js +18 -0
- package/win/bin/hq.ps1 +108 -0
- package/win/bin/minion-cli-win.js +20 -0
- package/win/lib/process-manager.js +112 -0
- package/win/minion-cli.ps1 +877 -0
- package/win/routes/chat.js +280 -0
- package/win/routes/commands.js +101 -0
- package/win/routes/config.js +227 -0
- package/win/routes/directives.js +136 -0
- package/win/routes/files.js +283 -0
- package/win/routes/terminal.js +335 -0
- package/win/routine-runner.js +324 -0
- package/win/server.js +230 -0
- package/win/terminal-server.js +242 -0
- package/win/workflow-runner.js +380 -0
- package/routes/index.js +0 -106
- /package/{api.js → core/api.js} +0 -0
- /package/{lib → core/lib}/auth.js +0 -0
- /package/{routes → core/routes}/auth.js +0 -0
- /package/{bin → linux/bin}/hq +0 -0
- /package/{lib → linux/lib}/process-manager.js +0 -0
- /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
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
-
|
|
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 =
|
|
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
|
+
}
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const { verifyToken } = require('../lib/auth')
|
|
15
|
-
const
|
|
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
|
|
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
|
|
24
|
-
const
|
|
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('
|
|
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
|
|
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(
|
|
22
|
-
return
|
|
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('
|
|
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
|
|
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(
|
|
23
|
-
return
|
|
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('
|
|
10
|
+
const { config } = require('../config')
|
|
11
|
+
const { DATA_DIR } = require('../lib/platform')
|
|
11
12
|
|
|
12
|
-
// Routine file location: /
|
|
13
|
-
// or ~/routines.json (
|
|
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(
|
|
18
|
-
return
|
|
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('
|
|
10
|
+
const { config } = require('../config')
|
|
11
|
+
const { DATA_DIR } = require('../lib/platform')
|
|
11
12
|
|
|
12
|
-
// Workflow file location: /
|
|
13
|
-
// or ~/workflows.json (
|
|
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(
|
|
19
|
-
return
|
|
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}
|
|
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 "
|
|
776
|
+
-d "$BODY" 2>&1) || true
|
|
740
777
|
|
|
741
778
|
if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
|
|
742
|
-
|
|
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 "
|
|
975
|
+
-d "$BODY" 2>&1) || true
|
|
923
976
|
|
|
924
977
|
if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
|
|
925
|
-
|
|
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('
|
|
21
|
-
const { config } = require('
|
|
22
|
-
const chatStore = require('
|
|
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('
|
|
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('
|
|
14
|
-
const { clearLlmCache } = require('
|
|
15
|
-
const { config } = require('
|
|
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('
|
|
16
|
-
const { config } = require('
|
|
17
|
-
const { writeSkillToLocal } = require('
|
|
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('
|
|
20
|
-
const logManager = require('
|
|
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('
|
|
20
|
-
const { config } = require('
|
|
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('
|
|
24
|
-
const { config } = require('
|
|
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('
|
|
22
|
-
const executionStore = require('
|
|
23
|
-
const routineStore = require('
|
|
24
|
-
const logManager = require('
|
|
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()
|