@geekbeer/minion 2.23.0 → 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/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)
@@ -763,6 +764,177 @@ CFEOF
763
764
  fi
764
765
  }
765
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
+
766
938
  # ============================================================
767
939
  # Main command dispatch
768
940
  # ============================================================
@@ -776,6 +948,12 @@ case "${1:-}" in
776
948
  do_setup "$@"
777
949
  ;;
778
950
 
951
+ reconfigure)
952
+ require_root reconfigure
953
+ shift
954
+ do_reconfigure "$@"
955
+ ;;
956
+
779
957
  status)
780
958
  curl -s "$AGENT_URL/api/status" | jq .
781
959
  ;;
@@ -866,6 +1044,7 @@ case "${1:-}" in
866
1044
  echo ""
867
1045
  echo "Usage:"
868
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)"
869
1048
  echo " sudo minion-cli start # Start agent service (root)"
870
1049
  echo " sudo minion-cli stop # Stop agent service (root)"
871
1050
  echo " sudo minion-cli restart # Restart agent service (root)"
@@ -882,6 +1061,11 @@ case "${1:-}" in
882
1061
  echo " --api-token <TOKEN> API token (optional)"
883
1062
  echo " --setup-tunnel Set up cloudflared tunnel (requires --hq-url, --api-token)"
884
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 ""
885
1069
  echo "Status values: online, offline, busy"
886
1070
  echo ""
887
1071
  echo "Environment:"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "2.23.0",
3
+ "version": "2.25.0",
4
4
  "description": "AI Agent runtime for Minion - manages status and skill deployment on VPS",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -3,10 +3,11 @@
3
3
  *
4
4
  * Endpoints:
5
5
  * - GET /api/routines - List all routines with status
6
- * - POST /api/routines - Receive routines from HQ (upsert/additive)
6
+ * - POST /api/routines - Receive routines (upsert/additive)
7
7
  * - POST /api/routines/sync - Pull routines from HQ and sync locally
8
8
  * - PUT /api/routines/:id/schedule - Update a routine schedule (cron_expression, is_active)
9
9
  * - DELETE /api/routines/:id - Remove a routine
10
+ * - POST /api/routines/bulk-toggle - Set is_active for all routines at once
10
11
  * - POST /api/routines/trigger - Manual trigger for a routine
11
12
  */
12
13
 
@@ -218,6 +219,45 @@ async function routineRoutes(fastify) {
218
219
  }
219
220
  })
220
221
 
222
+ // Bulk toggle: set is_active for all routines at once
223
+ fastify.post('/api/routines/bulk-toggle', async (request, reply) => {
224
+ if (!verifyToken(request)) {
225
+ reply.code(401)
226
+ return { success: false, error: 'Unauthorized' }
227
+ }
228
+
229
+ const { is_active } = request.body || {}
230
+
231
+ if (typeof is_active !== 'boolean') {
232
+ reply.code(400)
233
+ return { success: false, error: 'is_active (boolean) is required' }
234
+ }
235
+
236
+ console.log(`[Routines] Bulk toggle: setting all routines is_active=${is_active}`)
237
+
238
+ try {
239
+ const routines = await routineStore.load()
240
+
241
+ for (const routine of routines) {
242
+ routine.is_active = is_active
243
+ }
244
+
245
+ await routineStore.save(routines)
246
+ routineRunner.loadRoutines(routines)
247
+
248
+ const count = routines.length
249
+ return {
250
+ success: true,
251
+ message: `${count} routines set to is_active=${is_active}`,
252
+ count,
253
+ }
254
+ } catch (error) {
255
+ console.error(`[Routines] Failed to bulk toggle: ${error.message}`)
256
+ reply.code(500)
257
+ return { success: false, error: error.message }
258
+ }
259
+ })
260
+
221
261
  // Manual trigger: run a routine immediately
222
262
  fastify.post('/api/routines/trigger', async (request, reply) => {
223
263
  if (!verifyToken(request)) {