@geekbeer/minion 2.16.5 → 2.25.0
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/.env.example +2 -2
- package/README.md +1 -1
- package/api.js +1 -1
- package/config.js +17 -3
- package/docs/api-reference.md +1 -1
- package/docs/task-guides.md +7 -7
- package/lib/llm-checker.js +49 -6
- package/minion-cli.sh +226 -11
- package/package.json +1 -1
- package/roles/pm.md +1 -1
- package/routes/auth.js +9 -409
- package/routes/chat.js +77 -16
- package/routes/config.js +252 -0
- package/routes/health.js +2 -1
- package/routes/index.js +7 -1
- package/routes/routines.js +41 -1
- package/routes/skills.js +22 -6
- package/routine-runner.js +6 -2
- package/rules/core.md +2 -2
- package/skills/execution-report/SKILL.md +1 -1
- package/workflow-runner.js +14 -5
package/.env.example
CHANGED
|
@@ -15,8 +15,8 @@ API_TOKEN=
|
|
|
15
15
|
# Minion ID (optional) - The minion's UUID assigned by HQ
|
|
16
16
|
MINION_ID=
|
|
17
17
|
|
|
18
|
-
# Agent port (optional, default:
|
|
19
|
-
AGENT_PORT=
|
|
18
|
+
# Agent port (optional, default: 8080)
|
|
19
|
+
AGENT_PORT=8080
|
|
20
20
|
|
|
21
21
|
# Heartbeat interval in seconds (optional, default: 30)
|
|
22
22
|
HEARTBEAT_INTERVAL=30
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ await reportIssue({
|
|
|
57
57
|
| `HQ_URL` | Minion HQ server URL | - |
|
|
58
58
|
| `API_TOKEN` | Authentication token | - |
|
|
59
59
|
| `MINION_ID` | Minion UUID | - |
|
|
60
|
-
| `AGENT_PORT` | Agent API listen port | `
|
|
60
|
+
| `AGENT_PORT` | Agent API listen port | `8080` |
|
|
61
61
|
| `HEARTBEAT_INTERVAL` | Heartbeat interval (ms) | `30000` |
|
|
62
62
|
|
|
63
63
|
## License
|
package/api.js
CHANGED
|
@@ -53,7 +53,7 @@ async function reportExecution(data) {
|
|
|
53
53
|
/**
|
|
54
54
|
* Report a single workflow step completion to HQ.
|
|
55
55
|
* Called by the post-execution hook after a dispatched skill finishes.
|
|
56
|
-
* @param {object} data - { workflow_execution_id, step_index, status }
|
|
56
|
+
* @param {object} data - { workflow_execution_id, step_index, status, output_summary? }
|
|
57
57
|
*/
|
|
58
58
|
async function reportStepComplete(data) {
|
|
59
59
|
return request('/step-complete', {
|
package/config.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* (no HQ communication).
|
|
11
11
|
*
|
|
12
12
|
* Other optional environment variables:
|
|
13
|
-
* - AGENT_PORT: Port for the local agent server (default:
|
|
13
|
+
* - AGENT_PORT: Port for the local agent server (default: 8080)
|
|
14
14
|
* - MINION_USER: System user running the agent (used to resolve home directory)
|
|
15
15
|
*/
|
|
16
16
|
|
|
@@ -44,10 +44,17 @@ const config = {
|
|
|
44
44
|
MINION_ID: process.env.MINION_ID || '',
|
|
45
45
|
|
|
46
46
|
// Server settings
|
|
47
|
-
AGENT_PORT: parseInt(process.env.AGENT_PORT, 10) ||
|
|
47
|
+
AGENT_PORT: parseInt(process.env.AGENT_PORT, 10) || 8080,
|
|
48
48
|
|
|
49
49
|
// Resolved home directory (safe for supervisord environments)
|
|
50
50
|
HOME_DIR: resolveHomeDir(),
|
|
51
|
+
|
|
52
|
+
// LLM command template (user-configured)
|
|
53
|
+
// Use {prompt} as placeholder for the prompt text.
|
|
54
|
+
// Examples:
|
|
55
|
+
// LLM_COMMAND="claude -p '{prompt}'"
|
|
56
|
+
// LLM_COMMAND="gemini-cli --prompt '{prompt}'"
|
|
57
|
+
LLM_COMMAND: process.env.LLM_COMMAND || '',
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/**
|
|
@@ -68,4 +75,11 @@ function validate() {
|
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Check if LLM command is configured
|
|
80
|
+
*/
|
|
81
|
+
function isLlmConfigured() {
|
|
82
|
+
return !!config.LLM_COMMAND
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { config, validate, isHqConfigured, isLlmConfigured }
|
package/docs/api-reference.md
CHANGED
package/docs/task-guides.md
CHANGED
|
@@ -87,11 +87,11 @@ curl -s -X POST "$HQ_URL/api/minion/workflows" \
|
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
# HQからワークフローを取得 (不足スキルも自動fetch)
|
|
90
|
-
curl -s -X POST "http://localhost:
|
|
90
|
+
curl -s -X POST "http://localhost:8080/api/workflows/fetch/<name>" \
|
|
91
91
|
-H "Authorization: Bearer $API_TOKEN"
|
|
92
92
|
|
|
93
93
|
# ローカルワークフローをHQにpush (スキルも自動push)
|
|
94
|
-
curl -s -X POST "http://localhost:
|
|
94
|
+
curl -s -X POST "http://localhost:8080/api/workflows/push/<name>" \
|
|
95
95
|
-H "Authorization: Bearer $API_TOKEN"
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -128,18 +128,18 @@ curl -s -X PUT "$HQ_URL/api/minion/me/project/<project-id>/context" \
|
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
130
|
# 最近の実行一覧
|
|
131
|
-
curl -s "http://localhost:
|
|
131
|
+
curl -s "http://localhost:8080/api/executions?limit=10" \
|
|
132
132
|
-H "Authorization: Bearer $API_TOKEN"
|
|
133
133
|
|
|
134
134
|
# 特定の実行ログ
|
|
135
|
-
curl -s "http://localhost:
|
|
135
|
+
curl -s "http://localhost:8080/api/executions/<id>/log" \
|
|
136
136
|
-H "Authorization: Bearer $API_TOKEN"
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
### 実行結果の報告
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
|
-
curl -s -X POST "http://localhost:
|
|
142
|
+
curl -s -X POST "http://localhost:8080/api/executions/<id>/outcome" \
|
|
143
143
|
-H "Content-Type: application/json" \
|
|
144
144
|
-d '{
|
|
145
145
|
"outcome": "success",
|
|
@@ -199,11 +199,11 @@ minion-cli skill fetch <name>
|
|
|
199
199
|
|
|
200
200
|
```bash
|
|
201
201
|
# ローカルワークフロー一覧 (next_run を確認)
|
|
202
|
-
curl -s "http://localhost:
|
|
202
|
+
curl -s "http://localhost:8080/api/workflows" \
|
|
203
203
|
-H "Authorization: Bearer $API_TOKEN"
|
|
204
204
|
|
|
205
205
|
# 手動トリガー
|
|
206
|
-
curl -s -X POST "http://localhost:
|
|
206
|
+
curl -s -X POST "http://localhost:8080/api/workflows/trigger" \
|
|
207
207
|
-H "Authorization: Bearer $API_TOKEN" \
|
|
208
208
|
-H "Content-Type: application/json" \
|
|
209
209
|
-d '{ "workflow_id": "<workflow-uuid>" }'
|
package/lib/llm-checker.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* LLM Service authentication checker
|
|
3
3
|
*
|
|
4
4
|
* Detects whether supported LLM CLIs are authenticated and ready to use.
|
|
5
|
-
*
|
|
5
|
+
* Also checks whether LLM_COMMAND is configured for workflow/routine execution.
|
|
6
6
|
* Results are cached in memory for 60 seconds to avoid excessive filesystem checks.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const fs = require('fs')
|
|
10
10
|
const path = require('path')
|
|
11
|
+
const { execSync } = require('child_process')
|
|
11
12
|
const { config } = require('../config')
|
|
12
13
|
|
|
13
14
|
const CACHE_TTL_MS = 60000
|
|
@@ -15,11 +16,28 @@ const CACHE_TTL_MS = 60000
|
|
|
15
16
|
let cachedResult = null
|
|
16
17
|
let cachedAt = 0
|
|
17
18
|
|
|
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
|
+
|
|
18
33
|
/**
|
|
19
34
|
* Check Claude Code authentication.
|
|
20
|
-
*
|
|
35
|
+
* First checks known credential file locations (fast path),
|
|
36
|
+
* then falls back to `claude auth whoami` CLI command for newer CLI versions
|
|
37
|
+
* that may store credentials differently.
|
|
21
38
|
*/
|
|
22
39
|
function isClaudeAuthenticated() {
|
|
40
|
+
// Fast path: check known credential file locations
|
|
23
41
|
const candidates = [
|
|
24
42
|
path.join(config.HOME_DIR, '.claude', '.credentials.json'),
|
|
25
43
|
path.join(config.HOME_DIR, '.claude', 'credentials.json'),
|
|
@@ -35,7 +53,24 @@ function isClaudeAuthenticated() {
|
|
|
35
53
|
// Invalid JSON or read error — not authenticated
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
|
-
|
|
56
|
+
|
|
57
|
+
// Fallback: check via claude CLI command (handles newer credential storage)
|
|
58
|
+
try {
|
|
59
|
+
const claudePath = path.join(config.HOME_DIR, '.local', 'bin', 'claude')
|
|
60
|
+
const claudeBin = fs.existsSync(claudePath) ? claudePath : 'claude'
|
|
61
|
+
execSync(`${claudeBin} auth whoami`, {
|
|
62
|
+
encoding: 'utf-8',
|
|
63
|
+
timeout: 5000,
|
|
64
|
+
stdio: 'pipe',
|
|
65
|
+
env: {
|
|
66
|
+
HOME: config.HOME_DIR,
|
|
67
|
+
PATH: getExtendedPath(),
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
return true
|
|
71
|
+
} catch {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
39
74
|
}
|
|
40
75
|
|
|
41
76
|
/**
|
|
@@ -89,10 +124,18 @@ const SERVICE_DEFINITIONS = [
|
|
|
89
124
|
{ name: 'codex', display_name: 'Codex', check: isCodexAuthenticated },
|
|
90
125
|
]
|
|
91
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Check if LLM_COMMAND is configured for workflow/routine execution.
|
|
129
|
+
*/
|
|
130
|
+
function isLlmCommandConfigured() {
|
|
131
|
+
return !!config.LLM_COMMAND
|
|
132
|
+
}
|
|
133
|
+
|
|
92
134
|
/**
|
|
93
135
|
* Get authenticated LLM services (cached for 60s).
|
|
94
|
-
* Returns all services with their authentication status
|
|
95
|
-
*
|
|
136
|
+
* Returns all services with their authentication status,
|
|
137
|
+
* plus whether LLM_COMMAND is configured.
|
|
138
|
+
* @returns {{ services: { name: string, display_name: string, authenticated: boolean }[], llm_command_configured: boolean }}
|
|
96
139
|
*/
|
|
97
140
|
function getLlmServices() {
|
|
98
141
|
const now = Date.now()
|
|
@@ -117,4 +160,4 @@ function clearLlmCache() {
|
|
|
117
160
|
cachedAt = 0
|
|
118
161
|
}
|
|
119
162
|
|
|
120
|
-
module.exports = { getLlmServices, clearLlmCache }
|
|
163
|
+
module.exports = { getLlmServices, clearLlmCache, isLlmCommandConfigured }
|
package/minion-cli.sh
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Usage:
|
|
7
7
|
# sudo minion-cli setup [options] # Set up minion agent service (root)
|
|
8
|
+
# sudo minion-cli reconfigure [options] # Re-register with new HQ credentials (root)
|
|
8
9
|
# sudo minion-cli start # Start agent service (root)
|
|
9
10
|
# sudo minion-cli stop # Stop agent service (root)
|
|
10
11
|
# sudo minion-cli restart # Restart agent service (root)
|
|
@@ -86,7 +87,7 @@ svc_control() {
|
|
|
86
87
|
esac
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
AGENT_URL="${MINION_AGENT_URL:-http://localhost:
|
|
90
|
+
AGENT_URL="${MINION_AGENT_URL:-http://localhost:8080}"
|
|
90
91
|
|
|
91
92
|
# Auto-load .env so that API_TOKEN etc. are available in interactive shells
|
|
92
93
|
ENV_FILE="/opt/minion-agent/.env"
|
|
@@ -279,11 +280,12 @@ do_setup() {
|
|
|
279
280
|
ENV_CONTENT+="MINION_ID=${MINION_ID}\n"
|
|
280
281
|
fi
|
|
281
282
|
|
|
282
|
-
ENV_CONTENT+="AGENT_PORT=
|
|
283
|
+
ENV_CONTENT+="AGENT_PORT=8080\n"
|
|
283
284
|
ENV_CONTENT+="HEARTBEAT_INTERVAL=30\n"
|
|
284
285
|
ENV_CONTENT+="MINION_USER=${TARGET_USER}\n"
|
|
285
286
|
|
|
286
287
|
echo -e "$ENV_CONTENT" | $SUDO tee /opt/minion-agent/.env > /dev/null
|
|
288
|
+
$SUDO chown "${TARGET_USER}:${TARGET_USER}" /opt/minion-agent/.env
|
|
287
289
|
echo " -> /opt/minion-agent/.env generated"
|
|
288
290
|
|
|
289
291
|
# Step 5: Configure sudoers for agent user
|
|
@@ -581,7 +583,7 @@ SUPEOF
|
|
|
581
583
|
local SUCCESS=false
|
|
582
584
|
|
|
583
585
|
for i in $(seq 1 $RETRIES); do
|
|
584
|
-
if curl -sf http://localhost:
|
|
586
|
+
if curl -sf http://localhost:8080/api/health > /dev/null 2>&1; then
|
|
585
587
|
SUCCESS=true
|
|
586
588
|
break
|
|
587
589
|
fi
|
|
@@ -649,10 +651,12 @@ SUPEOF
|
|
|
649
651
|
exit 1
|
|
650
652
|
fi
|
|
651
653
|
|
|
652
|
-
# Install cloudflared if not present
|
|
654
|
+
# Install cloudflared if not present (architecture-aware)
|
|
653
655
|
if ! command -v cloudflared &>/dev/null; then
|
|
654
656
|
echo " Installing cloudflared..."
|
|
655
|
-
|
|
657
|
+
local CF_ARCH
|
|
658
|
+
CF_ARCH=$(dpkg --print-architecture 2>/dev/null || echo "amd64")
|
|
659
|
+
curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${CF_ARCH}.deb" \
|
|
656
660
|
-o /tmp/cloudflared.deb
|
|
657
661
|
$SUDO dpkg -i /tmp/cloudflared.deb
|
|
658
662
|
rm -f /tmp/cloudflared.deb
|
|
@@ -688,10 +692,36 @@ SUPEOF
|
|
|
688
692
|
echo "$CONFIG_YML" | $SUDO tee /etc/cloudflared/config.yml > /dev/null
|
|
689
693
|
echo " -> /etc/cloudflared/config.yml saved"
|
|
690
694
|
|
|
691
|
-
# Install and start cloudflared service
|
|
692
|
-
$
|
|
693
|
-
|
|
694
|
-
|
|
695
|
+
# Install and start cloudflared service (process-manager aware)
|
|
696
|
+
case "$PROC_MGR" in
|
|
697
|
+
systemd)
|
|
698
|
+
$SUDO cloudflared service install 2>/dev/null || true
|
|
699
|
+
$SUDO systemctl enable cloudflared 2>/dev/null || true
|
|
700
|
+
$SUDO systemctl start cloudflared 2>/dev/null || true
|
|
701
|
+
;;
|
|
702
|
+
supervisord)
|
|
703
|
+
# Create supervisord config for cloudflared
|
|
704
|
+
$SUDO tee /etc/supervisor/conf.d/cloudflared.conf > /dev/null <<CFEOF
|
|
705
|
+
[program:cloudflared]
|
|
706
|
+
command=/usr/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
|
|
707
|
+
autorestart=true
|
|
708
|
+
priority=150
|
|
709
|
+
startsecs=5
|
|
710
|
+
stdout_logfile=/var/log/supervisor/cloudflared.log
|
|
711
|
+
stderr_logfile=/var/log/supervisor/cloudflared.log
|
|
712
|
+
CFEOF
|
|
713
|
+
# Reload supervisord if running
|
|
714
|
+
if $SUDO supervisorctl status &>/dev/null; then
|
|
715
|
+
$SUDO supervisorctl reread
|
|
716
|
+
$SUDO supervisorctl update
|
|
717
|
+
fi
|
|
718
|
+
;;
|
|
719
|
+
*)
|
|
720
|
+
echo " WARNING: No supported process manager for cloudflared"
|
|
721
|
+
echo " You may need to start cloudflared manually:"
|
|
722
|
+
echo " cloudflared tunnel --config /etc/cloudflared/config.yml run"
|
|
723
|
+
;;
|
|
724
|
+
esac
|
|
695
725
|
echo " -> cloudflared tunnel configured and started"
|
|
696
726
|
fi
|
|
697
727
|
fi
|
|
@@ -701,10 +731,12 @@ SUPEOF
|
|
|
701
731
|
echo ""
|
|
702
732
|
echo "Notifying HQ of setup completion..."
|
|
703
733
|
local NOTIFY_RESPONSE
|
|
734
|
+
local HOSTNAME_VAL
|
|
735
|
+
HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
|
|
704
736
|
NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
|
|
705
737
|
-H "Content-Type: application/json" \
|
|
706
738
|
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
707
|
-
-d "{}" 2>&1) || true
|
|
739
|
+
-d "{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}" 2>&1) || true
|
|
708
740
|
|
|
709
741
|
if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
|
|
710
742
|
echo " -> HQ notified successfully"
|
|
@@ -732,6 +764,177 @@ SUPEOF
|
|
|
732
764
|
fi
|
|
733
765
|
}
|
|
734
766
|
|
|
767
|
+
# ============================================================
|
|
768
|
+
# reconfigure subcommand
|
|
769
|
+
# ============================================================
|
|
770
|
+
do_reconfigure() {
|
|
771
|
+
local HQ_URL=""
|
|
772
|
+
local MINION_ID=""
|
|
773
|
+
local API_TOKEN=""
|
|
774
|
+
|
|
775
|
+
# Parse arguments
|
|
776
|
+
while [[ $# -gt 0 ]]; do
|
|
777
|
+
case "$1" in
|
|
778
|
+
--hq-url)
|
|
779
|
+
HQ_URL="$2"
|
|
780
|
+
shift 2
|
|
781
|
+
;;
|
|
782
|
+
--minion-id)
|
|
783
|
+
MINION_ID="$2"
|
|
784
|
+
shift 2
|
|
785
|
+
;;
|
|
786
|
+
--api-token)
|
|
787
|
+
API_TOKEN="$2"
|
|
788
|
+
shift 2
|
|
789
|
+
;;
|
|
790
|
+
*)
|
|
791
|
+
echo "Unknown option: $1"
|
|
792
|
+
echo "Usage: sudo minion-cli reconfigure --hq-url <URL> --minion-id <UUID> --api-token <TOKEN>"
|
|
793
|
+
exit 1
|
|
794
|
+
;;
|
|
795
|
+
esac
|
|
796
|
+
done
|
|
797
|
+
|
|
798
|
+
# Validate required arguments
|
|
799
|
+
if [ -z "$HQ_URL" ] || [ -z "$MINION_ID" ] || [ -z "$API_TOKEN" ]; then
|
|
800
|
+
echo "ERROR: All three options are required: --hq-url, --minion-id, --api-token"
|
|
801
|
+
echo "Usage: sudo minion-cli reconfigure --hq-url <URL> --minion-id <UUID> --api-token <TOKEN>"
|
|
802
|
+
exit 1
|
|
803
|
+
fi
|
|
804
|
+
|
|
805
|
+
# Check that .env exists (setup must have been run before)
|
|
806
|
+
if [ ! -f /opt/minion-agent/.env ]; then
|
|
807
|
+
echo "ERROR: /opt/minion-agent/.env not found."
|
|
808
|
+
echo "It looks like minion-cli setup has not been run on this server."
|
|
809
|
+
echo "Please run 'sudo minion-cli setup' first for initial installation."
|
|
810
|
+
exit 1
|
|
811
|
+
fi
|
|
812
|
+
|
|
813
|
+
echo "========================================="
|
|
814
|
+
echo " @geekbeer/minion Reconfigure"
|
|
815
|
+
echo "========================================="
|
|
816
|
+
echo "HQ: $HQ_URL"
|
|
817
|
+
echo "Minion ID: $MINION_ID"
|
|
818
|
+
echo ""
|
|
819
|
+
|
|
820
|
+
# Step 1: Read existing .env and preserve non-credential keys
|
|
821
|
+
echo "[1/4] Updating .env credentials..."
|
|
822
|
+
local ENV_CONTENT=""
|
|
823
|
+
ENV_CONTENT+="# Minion Agent Configuration\n"
|
|
824
|
+
ENV_CONTENT+="# Reconfigured by minion-cli reconfigure\n\n"
|
|
825
|
+
ENV_CONTENT+="HQ_URL=${HQ_URL}\n"
|
|
826
|
+
ENV_CONTENT+="API_TOKEN=${API_TOKEN}\n"
|
|
827
|
+
ENV_CONTENT+="MINION_ID=${MINION_ID}\n"
|
|
828
|
+
|
|
829
|
+
# Preserve non-credential keys from existing .env
|
|
830
|
+
while IFS='=' read -r key value; do
|
|
831
|
+
[[ -z "$key" || "$key" == \#* ]] && continue
|
|
832
|
+
case "$key" in
|
|
833
|
+
HQ_URL|API_TOKEN|MINION_ID) ;; # Skip — already set above
|
|
834
|
+
*) ENV_CONTENT+="${key}=${value}\n" ;;
|
|
835
|
+
esac
|
|
836
|
+
done < /opt/minion-agent/.env
|
|
837
|
+
|
|
838
|
+
echo -e "$ENV_CONTENT" | $SUDO tee /opt/minion-agent/.env > /dev/null
|
|
839
|
+
echo " -> /opt/minion-agent/.env updated"
|
|
840
|
+
|
|
841
|
+
# Step 2: Update process manager config & restart service
|
|
842
|
+
echo "[2/4] Restarting minion-agent service..."
|
|
843
|
+
if [ -z "$PROC_MGR" ]; then
|
|
844
|
+
echo " WARNING: No supported process manager found"
|
|
845
|
+
echo " Please restart the minion-agent service manually"
|
|
846
|
+
elif [ "$PROC_MGR" = "supervisord" ]; then
|
|
847
|
+
# supervisord bakes env vars into conf — must regenerate
|
|
848
|
+
local CONF_FILE="/etc/supervisor/conf.d/minion-agent.conf"
|
|
849
|
+
if [ -f "$CONF_FILE" ]; then
|
|
850
|
+
# Read current conf to preserve command, user, and other settings
|
|
851
|
+
local CURRENT_COMMAND
|
|
852
|
+
CURRENT_COMMAND=$(grep '^command=' "$CONF_FILE" | head -1)
|
|
853
|
+
local CURRENT_DIRECTORY
|
|
854
|
+
CURRENT_DIRECTORY=$(grep '^directory=' "$CONF_FILE" | head -1)
|
|
855
|
+
local CURRENT_USER_LINE
|
|
856
|
+
CURRENT_USER_LINE=$(grep '^user=' "$CONF_FILE" || echo "")
|
|
857
|
+
|
|
858
|
+
# Rebuild environment line from updated .env
|
|
859
|
+
local ENV_LINE="environment="
|
|
860
|
+
# Detect home dir for the service user
|
|
861
|
+
local SVC_USER
|
|
862
|
+
SVC_USER=$(echo "$CURRENT_USER_LINE" | sed 's/user=//')
|
|
863
|
+
local SVC_HOME="$HOME"
|
|
864
|
+
if [ -n "$SVC_USER" ]; then
|
|
865
|
+
SVC_HOME=$(getent passwd "$SVC_USER" | cut -d: -f6 || echo "$HOME")
|
|
866
|
+
fi
|
|
867
|
+
local ENV_PAIRS=("HOME=\"${SVC_HOME}\"" "DISPLAY=\":99\"")
|
|
868
|
+
while IFS='=' read -r key value; do
|
|
869
|
+
[[ -z "$key" || "$key" == \#* ]] && continue
|
|
870
|
+
ENV_PAIRS+=("${key}=\"${value}\"")
|
|
871
|
+
done < /opt/minion-agent/.env
|
|
872
|
+
ENV_LINE+="$(IFS=,; echo "${ENV_PAIRS[*]}")"
|
|
873
|
+
|
|
874
|
+
$SUDO tee "$CONF_FILE" > /dev/null <<SUPEOF
|
|
875
|
+
[program:minion-agent]
|
|
876
|
+
${CURRENT_COMMAND}
|
|
877
|
+
${CURRENT_DIRECTORY}
|
|
878
|
+
${CURRENT_USER_LINE}
|
|
879
|
+
${ENV_LINE}
|
|
880
|
+
autorestart=true
|
|
881
|
+
priority=500
|
|
882
|
+
startsecs=3
|
|
883
|
+
stdout_logfile=/var/log/supervisor/minion-agent.log
|
|
884
|
+
stderr_logfile=/var/log/supervisor/minion-agent.log
|
|
885
|
+
SUPEOF
|
|
886
|
+
echo " -> supervisord conf updated"
|
|
887
|
+
fi
|
|
888
|
+
$SUDO supervisorctl reread > /dev/null 2>&1
|
|
889
|
+
$SUDO supervisorctl update > /dev/null 2>&1
|
|
890
|
+
echo " -> minion-agent restarted (supervisord)"
|
|
891
|
+
else
|
|
892
|
+
# systemd reads .env via EnvironmentFile — just restart
|
|
893
|
+
svc_control restart
|
|
894
|
+
echo " -> minion-agent restarted (systemd)"
|
|
895
|
+
fi
|
|
896
|
+
|
|
897
|
+
# Step 3: Health check
|
|
898
|
+
echo "[3/4] Verifying agent health..."
|
|
899
|
+
local HEALTH_OK=false
|
|
900
|
+
for i in $(seq 1 5); do
|
|
901
|
+
if curl -sf http://localhost:8080/api/health > /dev/null 2>&1; then
|
|
902
|
+
HEALTH_OK=true
|
|
903
|
+
break
|
|
904
|
+
fi
|
|
905
|
+
sleep 2
|
|
906
|
+
done
|
|
907
|
+
if [ "$HEALTH_OK" = true ]; then
|
|
908
|
+
echo " -> Agent is healthy"
|
|
909
|
+
else
|
|
910
|
+
echo " WARNING: Agent health check failed after 5 attempts"
|
|
911
|
+
echo " Check logs for details"
|
|
912
|
+
fi
|
|
913
|
+
|
|
914
|
+
# Step 4: Notify HQ (best-effort — heartbeat will also notify automatically)
|
|
915
|
+
echo "[4/4] Notifying HQ..."
|
|
916
|
+
local NOTIFY_RESPONSE
|
|
917
|
+
local HOSTNAME_VAL
|
|
918
|
+
HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
|
|
919
|
+
NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
|
|
920
|
+
-H "Content-Type: application/json" \
|
|
921
|
+
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
922
|
+
-d "{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}" 2>&1) || true
|
|
923
|
+
|
|
924
|
+
if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
|
|
925
|
+
echo " -> HQ notified successfully"
|
|
926
|
+
else
|
|
927
|
+
echo " -> Skipped (heartbeat will notify HQ within 30s)"
|
|
928
|
+
fi
|
|
929
|
+
|
|
930
|
+
echo ""
|
|
931
|
+
echo "========================================="
|
|
932
|
+
echo " Reconfigure Complete!"
|
|
933
|
+
echo "========================================="
|
|
934
|
+
echo ""
|
|
935
|
+
echo "The minion should appear online in HQ shortly."
|
|
936
|
+
}
|
|
937
|
+
|
|
735
938
|
# ============================================================
|
|
736
939
|
# Main command dispatch
|
|
737
940
|
# ============================================================
|
|
@@ -745,6 +948,12 @@ case "${1:-}" in
|
|
|
745
948
|
do_setup "$@"
|
|
746
949
|
;;
|
|
747
950
|
|
|
951
|
+
reconfigure)
|
|
952
|
+
require_root reconfigure
|
|
953
|
+
shift
|
|
954
|
+
do_reconfigure "$@"
|
|
955
|
+
;;
|
|
956
|
+
|
|
748
957
|
status)
|
|
749
958
|
curl -s "$AGENT_URL/api/status" | jq .
|
|
750
959
|
;;
|
|
@@ -835,6 +1044,7 @@ case "${1:-}" in
|
|
|
835
1044
|
echo ""
|
|
836
1045
|
echo "Usage:"
|
|
837
1046
|
echo " sudo minion-cli setup [options] # Set up agent service (root)"
|
|
1047
|
+
echo " sudo minion-cli reconfigure [options] # Re-register with new HQ credentials (root)"
|
|
838
1048
|
echo " sudo minion-cli start # Start agent service (root)"
|
|
839
1049
|
echo " sudo minion-cli stop # Stop agent service (root)"
|
|
840
1050
|
echo " sudo minion-cli restart # Restart agent service (root)"
|
|
@@ -851,9 +1061,14 @@ case "${1:-}" in
|
|
|
851
1061
|
echo " --api-token <TOKEN> API token (optional)"
|
|
852
1062
|
echo " --setup-tunnel Set up cloudflared tunnel (requires --hq-url, --api-token)"
|
|
853
1063
|
echo ""
|
|
1064
|
+
echo "Reconfigure options:"
|
|
1065
|
+
echo " --hq-url <URL> HQ server URL (required)"
|
|
1066
|
+
echo " --minion-id <UUID> Minion ID (required)"
|
|
1067
|
+
echo " --api-token <TOKEN> API token (required)"
|
|
1068
|
+
echo ""
|
|
854
1069
|
echo "Status values: online, offline, busy"
|
|
855
1070
|
echo ""
|
|
856
1071
|
echo "Environment:"
|
|
857
|
-
echo " MINION_AGENT_URL Agent URL (default: http://localhost:
|
|
1072
|
+
echo " MINION_AGENT_URL Agent URL (default: http://localhost:8080)"
|
|
858
1073
|
;;
|
|
859
1074
|
esac
|
package/package.json
CHANGED
package/roles/pm.md
CHANGED