@geekbeer/minion 2.70.2 → 3.4.7
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/core/db.js +297 -0
- package/core/lib/thread-watcher.js +3 -6
- package/core/routes/daily-logs.js +17 -11
- package/core/routes/memory.js +16 -10
- package/core/stores/chat-store.js +138 -76
- package/core/stores/daily-log-store.js +84 -105
- package/core/stores/execution-store.js +59 -84
- package/core/stores/memory-store.js +84 -177
- package/core/stores/routine-store.js +55 -72
- package/core/stores/workflow-store.js +52 -69
- package/docs/api-reference.md +6 -2
- package/linux/minion-cli.sh +203 -254
- package/linux/routes/chat.js +54 -7
- package/linux/server.js +3 -1
- package/package.json +3 -2
- package/roles/engineer.md +12 -0
- package/roles/pm.md +16 -0
- package/rules/core.md +66 -0
- package/win/lib/process-manager.js +115 -220
- package/win/minion-cli.ps1 +882 -675
- package/win/routes/chat.js +54 -7
- package/win/server.js +2 -0
- package/win/vendor/README.md +13 -0
- package/win/vendor/nssm.exe +0 -0
package/linux/minion-cli.sh
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# CLI tool for interacting with and setting up the minion agent
|
|
5
5
|
#
|
|
6
6
|
# Usage:
|
|
7
|
-
# sudo minion-cli setup
|
|
8
|
-
# sudo minion-cli
|
|
7
|
+
# sudo minion-cli setup --user <USERNAME> # Install software & register services (root)
|
|
8
|
+
# sudo minion-cli configure [options] # Connect to HQ, deploy skills, start services (root)
|
|
9
9
|
# sudo minion-cli uninstall [--keep-data] # Remove agent and services (root)
|
|
10
10
|
# sudo minion-cli start # Start agent service (root)
|
|
11
11
|
# sudo minion-cli stop # Stop agent service (root)
|
|
@@ -19,10 +19,12 @@
|
|
|
19
19
|
#
|
|
20
20
|
# Setup options:
|
|
21
21
|
# --user <USERNAME> Target user for the agent (required when running as root)
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# --
|
|
25
|
-
# --
|
|
22
|
+
#
|
|
23
|
+
# Configure options:
|
|
24
|
+
# --hq-url <URL> HQ server URL (required)
|
|
25
|
+
# --minion-id <UUID> Minion ID (required)
|
|
26
|
+
# --api-token <TOKEN> API token (required)
|
|
27
|
+
# --setup-tunnel Set up cloudflared tunnel
|
|
26
28
|
|
|
27
29
|
set -euo pipefail
|
|
28
30
|
|
|
@@ -129,12 +131,7 @@ fi
|
|
|
129
131
|
# setup subcommand
|
|
130
132
|
# ============================================================
|
|
131
133
|
do_setup() {
|
|
132
|
-
local HQ_URL=""
|
|
133
|
-
local MINION_ID=""
|
|
134
|
-
local API_TOKEN=""
|
|
135
134
|
local CLI_USER=""
|
|
136
|
-
local SETUP_TUNNEL=false
|
|
137
|
-
local ARGS_PROVIDED=false
|
|
138
135
|
|
|
139
136
|
# Parse arguments
|
|
140
137
|
while [[ $# -gt 0 ]]; do
|
|
@@ -143,32 +140,13 @@ do_setup() {
|
|
|
143
140
|
CLI_USER="$2"
|
|
144
141
|
shift 2
|
|
145
142
|
;;
|
|
146
|
-
--hq-url)
|
|
147
|
-
HQ_URL="$2"
|
|
148
|
-
ARGS_PROVIDED=true
|
|
149
|
-
shift 2
|
|
150
|
-
;;
|
|
151
|
-
--minion-id)
|
|
152
|
-
MINION_ID="$2"
|
|
153
|
-
ARGS_PROVIDED=true
|
|
154
|
-
shift 2
|
|
155
|
-
;;
|
|
156
|
-
--api-token)
|
|
157
|
-
API_TOKEN="$2"
|
|
158
|
-
ARGS_PROVIDED=true
|
|
159
|
-
shift 2
|
|
160
|
-
;;
|
|
161
|
-
--setup-tunnel)
|
|
162
|
-
SETUP_TUNNEL=true
|
|
163
|
-
shift
|
|
164
|
-
;;
|
|
165
143
|
--non-interactive)
|
|
166
144
|
# Accepted for cloud-init compatibility (setup is always non-interactive)
|
|
167
145
|
shift
|
|
168
146
|
;;
|
|
169
147
|
*)
|
|
170
148
|
echo "Unknown option: $1"
|
|
171
|
-
echo "Usage: minion-cli setup --user <USERNAME>
|
|
149
|
+
echo "Usage: sudo minion-cli setup --user <USERNAME>"
|
|
172
150
|
exit 1
|
|
173
151
|
;;
|
|
174
152
|
esac
|
|
@@ -209,38 +187,13 @@ do_setup() {
|
|
|
209
187
|
fi
|
|
210
188
|
# Non-root case: TARGET_USER is already set to $(whoami)
|
|
211
189
|
|
|
212
|
-
# Preserve existing .env values if no arguments were provided (redeploy scenario)
|
|
213
|
-
if [ "$ARGS_PROVIDED" = false ] && [ -f /opt/minion-agent/.env ]; then
|
|
214
|
-
echo "Reading existing .env values (redeploy mode)..."
|
|
215
|
-
while IFS='=' read -r key value; do
|
|
216
|
-
[[ -z "$key" || "$key" == \#* ]] && continue
|
|
217
|
-
case "$key" in
|
|
218
|
-
HQ_URL) [ -z "$HQ_URL" ] && HQ_URL="$value" ;;
|
|
219
|
-
MINION_ID) [ -z "$MINION_ID" ] && MINION_ID="$value" ;;
|
|
220
|
-
API_TOKEN) [ -z "$API_TOKEN" ] && API_TOKEN="$value" ;;
|
|
221
|
-
esac
|
|
222
|
-
done < /opt/minion-agent/.env
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
190
|
echo "========================================="
|
|
226
191
|
echo " @geekbeer/minion Setup"
|
|
227
192
|
echo "========================================="
|
|
228
193
|
echo "User: $TARGET_USER ($TARGET_HOME)"
|
|
229
|
-
|
|
230
|
-
if [ -n "$HQ_URL" ]; then
|
|
231
|
-
echo "Mode: Connected to HQ ($HQ_URL)"
|
|
232
|
-
else
|
|
233
|
-
echo "Mode: Standalone (no HQ connection)"
|
|
234
|
-
fi
|
|
235
|
-
if [ "$SETUP_TUNNEL" = true ]; then
|
|
236
|
-
echo "Tunnel: Enabled"
|
|
237
|
-
fi
|
|
238
194
|
echo ""
|
|
239
195
|
|
|
240
196
|
local TOTAL_STEPS=9
|
|
241
|
-
if [ "$SETUP_TUNNEL" = true ]; then
|
|
242
|
-
TOTAL_STEPS=10
|
|
243
|
-
fi
|
|
244
197
|
|
|
245
198
|
# Step 1: Install Claude Code
|
|
246
199
|
echo "[1/${TOTAL_STEPS}] Installing Claude Code..."
|
|
@@ -289,30 +242,23 @@ do_setup() {
|
|
|
289
242
|
$SUDO chown "${TARGET_USER}:${TARGET_USER}" /opt/minion-agent 2>/dev/null || true
|
|
290
243
|
echo " -> /opt/minion-agent/ created"
|
|
291
244
|
|
|
292
|
-
# Step 4: Generate .env file
|
|
245
|
+
# Step 4: Generate default .env file (HQ credentials set later via 'configure')
|
|
293
246
|
echo "[4/${TOTAL_STEPS}] Generating .env file..."
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
ENV_CONTENT+="
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
ENV_CONTENT
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
247
|
+
if [ ! -f /opt/minion-agent/.env ]; then
|
|
248
|
+
local ENV_CONTENT=""
|
|
249
|
+
ENV_CONTENT+="# Minion Agent Configuration\n"
|
|
250
|
+
ENV_CONTENT+="# Generated by minion-cli setup\n\n"
|
|
251
|
+
ENV_CONTENT+="AGENT_PORT=8080\n"
|
|
252
|
+
ENV_CONTENT+="MINION_USER=${TARGET_USER}\n"
|
|
253
|
+
ENV_CONTENT+="REFLECTION_TIME=03:00\n"
|
|
254
|
+
|
|
255
|
+
echo -e "$ENV_CONTENT" | $SUDO tee /opt/minion-agent/.env > /dev/null
|
|
256
|
+
$SUDO chown "${TARGET_USER}:${TARGET_USER}" /opt/minion-agent/.env
|
|
257
|
+
echo " -> /opt/minion-agent/.env created (run 'configure' to set HQ credentials)"
|
|
258
|
+
else
|
|
259
|
+
echo " -> /opt/minion-agent/.env already exists, preserving"
|
|
306
260
|
fi
|
|
307
261
|
|
|
308
|
-
ENV_CONTENT+="AGENT_PORT=8080\n"
|
|
309
|
-
ENV_CONTENT+="MINION_USER=${TARGET_USER}\n"
|
|
310
|
-
ENV_CONTENT+="REFLECTION_TIME=03:00\n"
|
|
311
|
-
|
|
312
|
-
echo -e "$ENV_CONTENT" | $SUDO tee /opt/minion-agent/.env > /dev/null
|
|
313
|
-
$SUDO chown "${TARGET_USER}:${TARGET_USER}" /opt/minion-agent/.env
|
|
314
|
-
echo " -> /opt/minion-agent/.env generated"
|
|
315
|
-
|
|
316
262
|
# Step 5: Configure sudoers for agent user
|
|
317
263
|
echo "[5/${TOTAL_STEPS}] Configuring sudoers for agent user..."
|
|
318
264
|
if [ "$TARGET_USER" != "root" ]; then
|
|
@@ -682,147 +628,20 @@ SUPEOF
|
|
|
682
628
|
fi
|
|
683
629
|
fi
|
|
684
630
|
|
|
685
|
-
# Step 10 (optional): Cloudflare Tunnel setup
|
|
686
|
-
if [ "$SETUP_TUNNEL" = true ]; then
|
|
687
|
-
echo ""
|
|
688
|
-
echo "[10/${TOTAL_STEPS}] Setting up Cloudflare Tunnel..."
|
|
689
|
-
|
|
690
|
-
if [ -z "$HQ_URL" ] || [ -z "$API_TOKEN" ]; then
|
|
691
|
-
echo " ERROR: --setup-tunnel requires --hq-url and --api-token"
|
|
692
|
-
exit 1
|
|
693
|
-
fi
|
|
694
|
-
|
|
695
|
-
# Install cloudflared if not present (architecture-aware)
|
|
696
|
-
if ! command -v cloudflared &>/dev/null; then
|
|
697
|
-
echo " Installing cloudflared..."
|
|
698
|
-
local CF_ARCH
|
|
699
|
-
CF_ARCH=$(dpkg --print-architecture 2>/dev/null || echo "amd64")
|
|
700
|
-
curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${CF_ARCH}.deb" \
|
|
701
|
-
-o /tmp/cloudflared.deb
|
|
702
|
-
$SUDO dpkg -i /tmp/cloudflared.deb
|
|
703
|
-
rm -f /tmp/cloudflared.deb
|
|
704
|
-
else
|
|
705
|
-
echo " -> cloudflared already installed"
|
|
706
|
-
fi
|
|
707
|
-
|
|
708
|
-
# Fetch tunnel config from HQ API
|
|
709
|
-
# Response contains credentials_json and config_yml (generated server-side)
|
|
710
|
-
echo " Fetching tunnel configuration from HQ..."
|
|
711
|
-
local TUNNEL_DATA
|
|
712
|
-
TUNNEL_DATA=$(curl -sfL -H "Authorization: Bearer ${API_TOKEN}" \
|
|
713
|
-
"${HQ_URL}/api/minion/tunnel-credentials" 2>&1) || true
|
|
714
|
-
|
|
715
|
-
if [ -z "$TUNNEL_DATA" ] || ! echo "$TUNNEL_DATA" | jq -e '.tunnel_id' > /dev/null 2>&1; then
|
|
716
|
-
echo " ERROR: Failed to fetch tunnel credentials from HQ"
|
|
717
|
-
echo " Tunnel may not be configured for this minion yet"
|
|
718
|
-
echo " Skipping tunnel setup (agent is running without tunnel)"
|
|
719
|
-
else
|
|
720
|
-
local TUNNEL_ID CREDS_JSON CONFIG_YML
|
|
721
|
-
TUNNEL_ID=$(echo "$TUNNEL_DATA" | jq -r '.tunnel_id')
|
|
722
|
-
CREDS_JSON=$(echo "$TUNNEL_DATA" | jq -r '.credentials_json')
|
|
723
|
-
CONFIG_YML=$(echo "$TUNNEL_DATA" | jq -r '.config_yml')
|
|
724
|
-
|
|
725
|
-
# Save credentials file (content from HQ, saved as-is)
|
|
726
|
-
$SUDO mkdir -p /etc/cloudflared
|
|
727
|
-
echo "$CREDS_JSON" | $SUDO tee "/etc/cloudflared/${TUNNEL_ID}.json" > /dev/null
|
|
728
|
-
$SUDO chmod 600 "/etc/cloudflared/${TUNNEL_ID}.json"
|
|
729
|
-
$SUDO chown root:root "/etc/cloudflared/${TUNNEL_ID}.json"
|
|
730
|
-
echo " -> /etc/cloudflared/${TUNNEL_ID}.json saved"
|
|
731
|
-
|
|
732
|
-
# Save config file (content from HQ, saved as-is)
|
|
733
|
-
echo "$CONFIG_YML" | $SUDO tee /etc/cloudflared/config.yml > /dev/null
|
|
734
|
-
echo " -> /etc/cloudflared/config.yml saved"
|
|
735
|
-
|
|
736
|
-
# Install and start cloudflared service (process-manager aware)
|
|
737
|
-
case "$PROC_MGR" in
|
|
738
|
-
systemd)
|
|
739
|
-
$SUDO cloudflared service install 2>/dev/null || true
|
|
740
|
-
$SUDO systemctl enable cloudflared 2>/dev/null || true
|
|
741
|
-
$SUDO systemctl start cloudflared 2>/dev/null || true
|
|
742
|
-
;;
|
|
743
|
-
supervisord)
|
|
744
|
-
# Create supervisord config for cloudflared
|
|
745
|
-
$SUDO tee /etc/supervisor/conf.d/cloudflared.conf > /dev/null <<CFEOF
|
|
746
|
-
[program:cloudflared]
|
|
747
|
-
command=/usr/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
|
|
748
|
-
autorestart=true
|
|
749
|
-
priority=150
|
|
750
|
-
startsecs=5
|
|
751
|
-
stdout_logfile=/var/log/supervisor/cloudflared.log
|
|
752
|
-
stderr_logfile=/var/log/supervisor/cloudflared.log
|
|
753
|
-
CFEOF
|
|
754
|
-
# Reload supervisord if running
|
|
755
|
-
if $SUDO supervisorctl status &>/dev/null; then
|
|
756
|
-
$SUDO supervisorctl reread
|
|
757
|
-
$SUDO supervisorctl update
|
|
758
|
-
fi
|
|
759
|
-
;;
|
|
760
|
-
*)
|
|
761
|
-
echo " WARNING: No supported process manager for cloudflared"
|
|
762
|
-
echo " You may need to start cloudflared manually:"
|
|
763
|
-
echo " cloudflared tunnel --config /etc/cloudflared/config.yml run"
|
|
764
|
-
;;
|
|
765
|
-
esac
|
|
766
|
-
echo " -> cloudflared tunnel configured and started"
|
|
767
|
-
fi
|
|
768
|
-
fi
|
|
769
|
-
|
|
770
|
-
# Notify HQ if configured (after all steps including tunnel setup)
|
|
771
|
-
if [ -n "$HQ_URL" ] && [ -n "$API_TOKEN" ]; then
|
|
772
|
-
echo ""
|
|
773
|
-
echo "Notifying HQ of setup completion..."
|
|
774
|
-
local NOTIFY_RESPONSE
|
|
775
|
-
local HOSTNAME_VAL
|
|
776
|
-
local LAN_IP
|
|
777
|
-
HOSTNAME_VAL=$(hostname 2>/dev/null || echo "")
|
|
778
|
-
LAN_IP=$(detect_lan_ip)
|
|
779
|
-
|
|
780
|
-
# Build request body with LAN IP detection
|
|
781
|
-
# Docker: use hostname (container name) for internal_ip_address (resolved via Docker DNS)
|
|
782
|
-
# Self-hosted: use LAN IP for both fields (hostname is not resolvable on LAN)
|
|
783
|
-
local BODY
|
|
784
|
-
if [ -f /.dockerenv ]; then
|
|
785
|
-
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
|
|
786
|
-
elif [ -n "$LAN_IP" ]; then
|
|
787
|
-
BODY="{\"internal_ip_address\":\"${LAN_IP}\",\"ip_address\":\"${LAN_IP}\"}"
|
|
788
|
-
else
|
|
789
|
-
BODY="{\"internal_ip_address\":\"${HOSTNAME_VAL}\"}"
|
|
790
|
-
fi
|
|
791
|
-
|
|
792
|
-
NOTIFY_RESPONSE=$(curl -sfL -X POST "${HQ_URL}/api/minion/setup-complete" \
|
|
793
|
-
-H "Content-Type: application/json" \
|
|
794
|
-
-H "Authorization: Bearer ${API_TOKEN}" \
|
|
795
|
-
-d "$BODY" 2>&1) || true
|
|
796
|
-
|
|
797
|
-
if echo "$NOTIFY_RESPONSE" | grep -q '"success":true' 2>/dev/null; then
|
|
798
|
-
if [ -n "$LAN_IP" ] && [ ! -f /.dockerenv ]; then
|
|
799
|
-
echo " -> HQ notified successfully (LAN IP: ${LAN_IP})"
|
|
800
|
-
else
|
|
801
|
-
echo " -> HQ notified successfully"
|
|
802
|
-
fi
|
|
803
|
-
else
|
|
804
|
-
echo " -> HQ notification skipped (HQ may not be reachable)"
|
|
805
|
-
fi
|
|
806
|
-
fi
|
|
807
|
-
|
|
808
631
|
echo ""
|
|
809
632
|
echo "========================================="
|
|
810
633
|
echo " Setup Complete!"
|
|
811
634
|
echo "========================================="
|
|
812
635
|
echo ""
|
|
813
636
|
echo "Agent user: $TARGET_USER"
|
|
637
|
+
echo "Services registered (not yet started)."
|
|
638
|
+
echo ""
|
|
639
|
+
echo "Next step: Connect to HQ:"
|
|
640
|
+
echo " sudo minion-cli configure \\"
|
|
641
|
+
echo " --hq-url <HQ_URL> \\"
|
|
642
|
+
echo " --minion-id <MINION_ID> \\"
|
|
643
|
+
echo " --api-token <API_TOKEN>"
|
|
814
644
|
echo ""
|
|
815
|
-
echo "Useful commands:"
|
|
816
|
-
echo " minion-cli status # Agent status"
|
|
817
|
-
echo " minion-cli health # Health check"
|
|
818
|
-
echo " minion-cli daemons # Daemon status"
|
|
819
|
-
echo " sudo minion-cli restart # Restart agent"
|
|
820
|
-
echo " sudo minion-cli stop # Stop agent"
|
|
821
|
-
if [ "$PROC_MGR" = "systemd" ]; then
|
|
822
|
-
echo " journalctl -u minion-agent -f # View logs"
|
|
823
|
-
else
|
|
824
|
-
echo " tail -f /var/log/supervisor/minion-agent.log # View logs"
|
|
825
|
-
fi
|
|
826
645
|
}
|
|
827
646
|
|
|
828
647
|
# ============================================================
|
|
@@ -989,10 +808,11 @@ do_uninstall() {
|
|
|
989
808
|
# ============================================================
|
|
990
809
|
# reconfigure subcommand
|
|
991
810
|
# ============================================================
|
|
992
|
-
|
|
811
|
+
do_configure() {
|
|
993
812
|
local HQ_URL=""
|
|
994
813
|
local MINION_ID=""
|
|
995
814
|
local API_TOKEN=""
|
|
815
|
+
local SETUP_TUNNEL=false
|
|
996
816
|
|
|
997
817
|
# Parse arguments
|
|
998
818
|
while [[ $# -gt 0 ]]; do
|
|
@@ -1009,9 +829,16 @@ do_reconfigure() {
|
|
|
1009
829
|
API_TOKEN="$2"
|
|
1010
830
|
shift 2
|
|
1011
831
|
;;
|
|
832
|
+
--setup-tunnel)
|
|
833
|
+
SETUP_TUNNEL=true
|
|
834
|
+
shift
|
|
835
|
+
;;
|
|
836
|
+
--non-interactive)
|
|
837
|
+
shift
|
|
838
|
+
;;
|
|
1012
839
|
*)
|
|
1013
840
|
echo "Unknown option: $1"
|
|
1014
|
-
echo "Usage: sudo minion-cli
|
|
841
|
+
echo "Usage: sudo minion-cli configure --hq-url <URL> --minion-id <UUID> --api-token <TOKEN> [--setup-tunnel]"
|
|
1015
842
|
exit 1
|
|
1016
843
|
;;
|
|
1017
844
|
esac
|
|
@@ -1020,56 +847,172 @@ do_reconfigure() {
|
|
|
1020
847
|
# Validate required arguments
|
|
1021
848
|
if [ -z "$HQ_URL" ] || [ -z "$MINION_ID" ] || [ -z "$API_TOKEN" ]; then
|
|
1022
849
|
echo "ERROR: All three options are required: --hq-url, --minion-id, --api-token"
|
|
1023
|
-
echo "Usage: sudo minion-cli
|
|
850
|
+
echo "Usage: sudo minion-cli configure --hq-url <URL> --minion-id <UUID> --api-token <TOKEN> [--setup-tunnel]"
|
|
1024
851
|
exit 1
|
|
1025
852
|
fi
|
|
1026
853
|
|
|
1027
|
-
# Check that
|
|
1028
|
-
if [ ! -
|
|
1029
|
-
echo "ERROR: /opt/minion-agent
|
|
1030
|
-
echo "
|
|
1031
|
-
echo "Please run 'sudo minion-cli setup' first for initial installation."
|
|
854
|
+
# Check that setup has been run
|
|
855
|
+
if [ ! -d /opt/minion-agent ]; then
|
|
856
|
+
echo "ERROR: /opt/minion-agent not found."
|
|
857
|
+
echo "Please run 'sudo minion-cli setup --user <USERNAME>' first."
|
|
1032
858
|
exit 1
|
|
1033
859
|
fi
|
|
1034
860
|
|
|
861
|
+
local TOTAL_STEPS=5
|
|
862
|
+
if [ "$SETUP_TUNNEL" = true ]; then
|
|
863
|
+
TOTAL_STEPS=6
|
|
864
|
+
fi
|
|
865
|
+
|
|
1035
866
|
echo "========================================="
|
|
1036
|
-
echo " @geekbeer/minion
|
|
867
|
+
echo " @geekbeer/minion Configure"
|
|
1037
868
|
echo "========================================="
|
|
1038
869
|
echo "HQ: $HQ_URL"
|
|
1039
870
|
echo "Minion ID: $MINION_ID"
|
|
871
|
+
if [ "$SETUP_TUNNEL" = true ]; then
|
|
872
|
+
echo "Tunnel: Enabled"
|
|
873
|
+
fi
|
|
1040
874
|
echo ""
|
|
1041
875
|
|
|
1042
|
-
# Step 1:
|
|
1043
|
-
echo "[1
|
|
876
|
+
# Step 1: Write/update .env with HQ credentials
|
|
877
|
+
echo "[1/${TOTAL_STEPS}] Writing .env credentials..."
|
|
1044
878
|
local ENV_CONTENT=""
|
|
1045
879
|
ENV_CONTENT+="# Minion Agent Configuration\n"
|
|
1046
|
-
ENV_CONTENT+="#
|
|
880
|
+
ENV_CONTENT+="# Configured by minion-cli configure\n\n"
|
|
1047
881
|
ENV_CONTENT+="HQ_URL=${HQ_URL}\n"
|
|
1048
882
|
ENV_CONTENT+="API_TOKEN=${API_TOKEN}\n"
|
|
1049
883
|
ENV_CONTENT+="MINION_ID=${MINION_ID}\n"
|
|
1050
884
|
|
|
1051
885
|
# Preserve non-credential keys from existing .env
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
886
|
+
if [ -f /opt/minion-agent/.env ]; then
|
|
887
|
+
while IFS='=' read -r key value; do
|
|
888
|
+
[[ -z "$key" || "$key" == \#* ]] && continue
|
|
889
|
+
case "$key" in
|
|
890
|
+
HQ_URL|API_TOKEN|MINION_ID) ;; # Skip — already set above
|
|
891
|
+
*) ENV_CONTENT+="${key}=${value}\n" ;;
|
|
892
|
+
esac
|
|
893
|
+
done < /opt/minion-agent/.env
|
|
894
|
+
fi
|
|
1059
895
|
|
|
1060
896
|
echo -e "$ENV_CONTENT" | $SUDO tee /opt/minion-agent/.env > /dev/null
|
|
1061
897
|
echo " -> /opt/minion-agent/.env updated"
|
|
1062
898
|
|
|
1063
|
-
# Step 2:
|
|
1064
|
-
echo "[2
|
|
899
|
+
# Step 2: Deploy bundled skills and rules
|
|
900
|
+
echo "[2/${TOTAL_STEPS}] Deploying bundled assets..."
|
|
901
|
+
local NPM_ROOT
|
|
902
|
+
NPM_ROOT=$(npm root -g 2>/dev/null || echo "")
|
|
903
|
+
local BUNDLED_SKILLS_DIR="${NPM_ROOT}/@geekbeer/minion/skills"
|
|
904
|
+
# Detect target user from .env
|
|
905
|
+
local DEPLOY_USER
|
|
906
|
+
DEPLOY_USER=$(grep '^MINION_USER=' /opt/minion-agent/.env | cut -d= -f2 || echo "")
|
|
907
|
+
local DEPLOY_HOME
|
|
908
|
+
if [ -n "$DEPLOY_USER" ]; then
|
|
909
|
+
DEPLOY_HOME=$(getent passwd "$DEPLOY_USER" | cut -d: -f6 || echo "$HOME")
|
|
910
|
+
else
|
|
911
|
+
DEPLOY_HOME="$HOME"
|
|
912
|
+
fi
|
|
913
|
+
local DEPLOY_AS=""
|
|
914
|
+
if [ "$(id -u)" -eq 0 ] && [ -n "$DEPLOY_USER" ] && [ "$DEPLOY_USER" != "root" ]; then
|
|
915
|
+
DEPLOY_AS="sudo -u $DEPLOY_USER"
|
|
916
|
+
fi
|
|
917
|
+
|
|
918
|
+
local CLAUDE_SKILLS_DIR="${DEPLOY_HOME}/.claude/skills"
|
|
919
|
+
if [ -d "$BUNDLED_SKILLS_DIR" ]; then
|
|
920
|
+
$DEPLOY_AS mkdir -p "$CLAUDE_SKILLS_DIR"
|
|
921
|
+
for skill_dir in "$BUNDLED_SKILLS_DIR"/*/; do
|
|
922
|
+
if [ -d "$skill_dir" ]; then
|
|
923
|
+
local skill_name=$(basename "$skill_dir")
|
|
924
|
+
$DEPLOY_AS cp -r "$skill_dir" "${CLAUDE_SKILLS_DIR}/${skill_name}"
|
|
925
|
+
echo " -> Deployed skill: $skill_name"
|
|
926
|
+
fi
|
|
927
|
+
done
|
|
928
|
+
fi
|
|
929
|
+
|
|
930
|
+
local BUNDLED_RULES_DIR="${NPM_ROOT}/@geekbeer/minion/rules"
|
|
931
|
+
local CLAUDE_RULES_DIR="${DEPLOY_HOME}/.claude/rules"
|
|
932
|
+
if [ -d "$BUNDLED_RULES_DIR" ]; then
|
|
933
|
+
$DEPLOY_AS mkdir -p "$CLAUDE_RULES_DIR"
|
|
934
|
+
$DEPLOY_AS cp "$BUNDLED_RULES_DIR"/core.md "$CLAUDE_RULES_DIR"/core.md
|
|
935
|
+
echo " -> Deployed rules: core.md"
|
|
936
|
+
if [ -f "$CLAUDE_RULES_DIR/minion.md" ]; then
|
|
937
|
+
$DEPLOY_AS rm -f "$CLAUDE_RULES_DIR/minion.md"
|
|
938
|
+
echo " -> Removed legacy rules: minion.md"
|
|
939
|
+
fi
|
|
940
|
+
fi
|
|
941
|
+
|
|
942
|
+
# Step 3 (optional): Cloudflare Tunnel setup
|
|
943
|
+
local CURRENT_STEP=3
|
|
944
|
+
if [ "$SETUP_TUNNEL" = true ]; then
|
|
945
|
+
echo "[${CURRENT_STEP}/${TOTAL_STEPS}] Setting up Cloudflare Tunnel..."
|
|
946
|
+
|
|
947
|
+
# Install cloudflared if not present
|
|
948
|
+
if ! command -v cloudflared &>/dev/null; then
|
|
949
|
+
echo " Installing cloudflared..."
|
|
950
|
+
local CF_ARCH
|
|
951
|
+
CF_ARCH=$(dpkg --print-architecture 2>/dev/null || echo "amd64")
|
|
952
|
+
curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${CF_ARCH}.deb" \
|
|
953
|
+
-o /tmp/cloudflared.deb
|
|
954
|
+
$SUDO dpkg -i /tmp/cloudflared.deb
|
|
955
|
+
rm -f /tmp/cloudflared.deb
|
|
956
|
+
else
|
|
957
|
+
echo " -> cloudflared already installed"
|
|
958
|
+
fi
|
|
959
|
+
|
|
960
|
+
# Fetch tunnel config from HQ API
|
|
961
|
+
echo " Fetching tunnel configuration from HQ..."
|
|
962
|
+
local TUNNEL_DATA
|
|
963
|
+
TUNNEL_DATA=$(curl -sfL -H "Authorization: Bearer ${API_TOKEN}" \
|
|
964
|
+
"${HQ_URL}/api/minion/tunnel-credentials" 2>&1) || true
|
|
965
|
+
|
|
966
|
+
if [ -z "$TUNNEL_DATA" ] || ! echo "$TUNNEL_DATA" | jq -e '.tunnel_id' > /dev/null 2>&1; then
|
|
967
|
+
echo " ERROR: Failed to fetch tunnel credentials from HQ"
|
|
968
|
+
echo " Skipping tunnel setup"
|
|
969
|
+
else
|
|
970
|
+
local TUNNEL_ID CREDS_JSON CONFIG_YML
|
|
971
|
+
TUNNEL_ID=$(echo "$TUNNEL_DATA" | jq -r '.tunnel_id')
|
|
972
|
+
CREDS_JSON=$(echo "$TUNNEL_DATA" | jq -r '.credentials_json')
|
|
973
|
+
CONFIG_YML=$(echo "$TUNNEL_DATA" | jq -r '.config_yml')
|
|
974
|
+
|
|
975
|
+
$SUDO mkdir -p /etc/cloudflared
|
|
976
|
+
echo "$CREDS_JSON" | $SUDO tee "/etc/cloudflared/${TUNNEL_ID}.json" > /dev/null
|
|
977
|
+
$SUDO chmod 600 "/etc/cloudflared/${TUNNEL_ID}.json"
|
|
978
|
+
$SUDO chown root:root "/etc/cloudflared/${TUNNEL_ID}.json"
|
|
979
|
+
echo "$CONFIG_YML" | $SUDO tee /etc/cloudflared/config.yml > /dev/null
|
|
980
|
+
|
|
981
|
+
case "$PROC_MGR" in
|
|
982
|
+
systemd)
|
|
983
|
+
$SUDO cloudflared service install 2>/dev/null || true
|
|
984
|
+
$SUDO systemctl enable cloudflared 2>/dev/null || true
|
|
985
|
+
$SUDO systemctl start cloudflared 2>/dev/null || true
|
|
986
|
+
;;
|
|
987
|
+
supervisord)
|
|
988
|
+
$SUDO tee /etc/supervisor/conf.d/cloudflared.conf > /dev/null <<CFEOF
|
|
989
|
+
[program:cloudflared]
|
|
990
|
+
command=/usr/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
|
|
991
|
+
autorestart=true
|
|
992
|
+
priority=150
|
|
993
|
+
startsecs=5
|
|
994
|
+
stdout_logfile=/var/log/supervisor/cloudflared.log
|
|
995
|
+
stderr_logfile=/var/log/supervisor/cloudflared.log
|
|
996
|
+
CFEOF
|
|
997
|
+
if $SUDO supervisorctl status &>/dev/null; then
|
|
998
|
+
$SUDO supervisorctl reread
|
|
999
|
+
$SUDO supervisorctl update
|
|
1000
|
+
fi
|
|
1001
|
+
;;
|
|
1002
|
+
esac
|
|
1003
|
+
echo " -> cloudflared tunnel configured and started"
|
|
1004
|
+
fi
|
|
1005
|
+
CURRENT_STEP=$((CURRENT_STEP + 1))
|
|
1006
|
+
fi
|
|
1007
|
+
|
|
1008
|
+
# Start/restart services
|
|
1009
|
+
echo "[$(( TOTAL_STEPS - 1 ))/${TOTAL_STEPS}] Starting services..."
|
|
1065
1010
|
if [ -z "$PROC_MGR" ]; then
|
|
1066
1011
|
echo " WARNING: No supported process manager found"
|
|
1067
|
-
echo " Please restart the minion-agent service manually"
|
|
1068
1012
|
elif [ "$PROC_MGR" = "supervisord" ]; then
|
|
1069
1013
|
# supervisord bakes env vars into conf — must regenerate
|
|
1070
1014
|
local CONF_FILE="/etc/supervisor/conf.d/minion-agent.conf"
|
|
1071
1015
|
if [ -f "$CONF_FILE" ]; then
|
|
1072
|
-
# Read current conf to preserve command, user, and other settings
|
|
1073
1016
|
local CURRENT_COMMAND
|
|
1074
1017
|
CURRENT_COMMAND=$(grep '^command=' "$CONF_FILE" | head -1)
|
|
1075
1018
|
local CURRENT_DIRECTORY
|
|
@@ -1077,9 +1020,7 @@ do_reconfigure() {
|
|
|
1077
1020
|
local CURRENT_USER_LINE
|
|
1078
1021
|
CURRENT_USER_LINE=$(grep '^user=' "$CONF_FILE" || echo "")
|
|
1079
1022
|
|
|
1080
|
-
# Rebuild environment line from updated .env
|
|
1081
1023
|
local ENV_LINE="environment="
|
|
1082
|
-
# Detect home dir for the service user
|
|
1083
1024
|
local SVC_USER
|
|
1084
1025
|
SVC_USER=$(echo "$CURRENT_USER_LINE" | sed 's/user=//')
|
|
1085
1026
|
local SVC_HOME="$HOME"
|
|
@@ -1109,15 +1050,14 @@ SUPEOF
|
|
|
1109
1050
|
fi
|
|
1110
1051
|
$SUDO supervisorctl reread > /dev/null 2>&1
|
|
1111
1052
|
$SUDO supervisorctl update > /dev/null 2>&1
|
|
1112
|
-
echo " -> minion-agent
|
|
1053
|
+
echo " -> minion-agent started (supervisord)"
|
|
1113
1054
|
else
|
|
1114
|
-
# systemd reads .env via EnvironmentFile — just restart
|
|
1115
1055
|
svc_control restart
|
|
1116
|
-
echo " -> minion-agent
|
|
1056
|
+
echo " -> minion-agent started (systemd)"
|
|
1117
1057
|
fi
|
|
1118
1058
|
|
|
1119
|
-
#
|
|
1120
|
-
echo "[
|
|
1059
|
+
# Health check
|
|
1060
|
+
echo "[${TOTAL_STEPS}/${TOTAL_STEPS}] Verifying agent health..."
|
|
1121
1061
|
local HEALTH_OK=false
|
|
1122
1062
|
for i in $(seq 1 5); do
|
|
1123
1063
|
if curl -sf http://localhost:8080/api/health > /dev/null 2>&1; then
|
|
@@ -1133,8 +1073,9 @@ SUPEOF
|
|
|
1133
1073
|
echo " Check logs for details"
|
|
1134
1074
|
fi
|
|
1135
1075
|
|
|
1136
|
-
#
|
|
1137
|
-
echo "
|
|
1076
|
+
# Notify HQ
|
|
1077
|
+
echo ""
|
|
1078
|
+
echo "Notifying HQ..."
|
|
1138
1079
|
local NOTIFY_RESPONSE
|
|
1139
1080
|
local HOSTNAME_VAL
|
|
1140
1081
|
local LAN_IP
|
|
@@ -1167,8 +1108,19 @@ SUPEOF
|
|
|
1167
1108
|
|
|
1168
1109
|
echo ""
|
|
1169
1110
|
echo "========================================="
|
|
1170
|
-
echo "
|
|
1111
|
+
echo " Configure Complete!"
|
|
1171
1112
|
echo "========================================="
|
|
1113
|
+
echo ""
|
|
1114
|
+
echo "Useful commands:"
|
|
1115
|
+
echo " minion-cli status # Agent status"
|
|
1116
|
+
echo " minion-cli health # Health check"
|
|
1117
|
+
echo " sudo minion-cli restart # Restart agent"
|
|
1118
|
+
if [ "$PROC_MGR" = "systemd" ]; then
|
|
1119
|
+
echo " journalctl -u minion-agent -f # View logs"
|
|
1120
|
+
else
|
|
1121
|
+
echo " tail -f /var/log/supervisor/minion-agent.log # View logs"
|
|
1122
|
+
fi
|
|
1123
|
+
}
|
|
1172
1124
|
echo ""
|
|
1173
1125
|
echo "The minion should appear online in HQ shortly."
|
|
1174
1126
|
}
|
|
@@ -1192,10 +1144,10 @@ case "${1:-}" in
|
|
|
1192
1144
|
do_uninstall "$@"
|
|
1193
1145
|
;;
|
|
1194
1146
|
|
|
1195
|
-
reconfigure)
|
|
1196
|
-
require_root
|
|
1147
|
+
configure|reconfigure)
|
|
1148
|
+
require_root "$1"
|
|
1197
1149
|
shift
|
|
1198
|
-
|
|
1150
|
+
do_configure "$@"
|
|
1199
1151
|
;;
|
|
1200
1152
|
|
|
1201
1153
|
status)
|
|
@@ -1329,8 +1281,8 @@ case "${1:-}" in
|
|
|
1329
1281
|
echo "Minion Agent CLI (@geekbeer/minion) v${CLI_VERSION}"
|
|
1330
1282
|
echo ""
|
|
1331
1283
|
echo "Usage:"
|
|
1332
|
-
echo " sudo minion-cli setup
|
|
1333
|
-
echo " sudo minion-cli
|
|
1284
|
+
echo " sudo minion-cli setup --user <USERNAME> # Install software & register services (root)"
|
|
1285
|
+
echo " sudo minion-cli configure [options] # Connect to HQ, deploy skills, start services (root)"
|
|
1334
1286
|
echo " sudo minion-cli uninstall [options] # Remove agent and services (root)"
|
|
1335
1287
|
echo " sudo minion-cli start # Start agent service (root)"
|
|
1336
1288
|
echo " sudo minion-cli stop # Stop agent service (root)"
|
|
@@ -1344,15 +1296,12 @@ case "${1:-}" in
|
|
|
1344
1296
|
echo ""
|
|
1345
1297
|
echo "Setup options:"
|
|
1346
1298
|
echo " --user <USERNAME> Target user for the agent (required when running as root)"
|
|
1347
|
-
echo " --hq-url <URL> HQ server URL (optional)"
|
|
1348
|
-
echo " --minion-id <UUID> Minion ID (optional)"
|
|
1349
|
-
echo " --api-token <TOKEN> API token (optional)"
|
|
1350
|
-
echo " --setup-tunnel Set up cloudflared tunnel (requires --hq-url, --api-token)"
|
|
1351
1299
|
echo ""
|
|
1352
|
-
echo "
|
|
1300
|
+
echo "Configure options:"
|
|
1353
1301
|
echo " --hq-url <URL> HQ server URL (required)"
|
|
1354
1302
|
echo " --minion-id <UUID> Minion ID (required)"
|
|
1355
1303
|
echo " --api-token <TOKEN> API token (required)"
|
|
1304
|
+
echo " --setup-tunnel Set up cloudflared tunnel"
|
|
1356
1305
|
echo ""
|
|
1357
1306
|
echo "Uninstall options:"
|
|
1358
1307
|
echo " --keep-data Keep /opt/minion-agent/.env (preserve credentials)"
|