@misterhuydo/sentinel 1.0.53 → 1.0.54

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/.cairn/.hint-lock CHANGED
@@ -1 +1 @@
1
- 2026-03-22T11:21:17.183Z
1
+ 2026-03-22T12:03:28.852Z
@@ -1,6 +1,6 @@
1
1
  {
2
- "message": "Auto-checkpoint at 2026-03-22T11:32:35.191Z",
3
- "checkpoint_at": "2026-03-22T11:32:35.192Z",
2
+ "message": "Auto-checkpoint at 2026-03-22T11:59:57.164Z",
3
+ "checkpoint_at": "2026-03-22T11:59:57.165Z",
4
4
  "active_files": [],
5
5
  "notes": [],
6
6
  "mtime_snapshot": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.0.53",
3
+ "version": "1.0.54",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -95,6 +95,10 @@ What you can do (tools available):
95
95
  they are delivering to.
96
96
  e.g. "which bots are you watching?", "list monitored bots"
97
97
 
98
+ 18. upgrade_sentinel — Pull the latest Sentinel agent code, update Python deps, and restart the
99
+ process. Safe to run at any time — no restart if already up to date.
100
+ e.g. "upgrade sentinel", "update sentinel", "upgrade yourself"
101
+
98
102
  When someone asks what you can do, what you support, what your capabilities are, or how you can help,
99
103
  reply with a short summary grouped by category:
100
104
 
@@ -126,6 +130,9 @@ reply with a short summary grouped by category:
126
130
  • `unwatch_bot` — stop monitoring a bot — "stop watching @errorbot"
127
131
  • `list_watched_bots` — show all bots currently being monitored — "which bots are you watching?"
128
132
 
133
+ *Self-management*
134
+ • `upgrade_sentinel` — git pull + pip install + restart — "upgrade sentinel", "update yourself"
135
+
129
136
  Tone: direct, professional, like a senior engineer who owns the system.
130
137
  Don't pad responses. Don't say "Great question!" or "Certainly!".
131
138
  If you don't know something, use a tool to find out before saying you don't know.
@@ -409,6 +416,17 @@ _TOOLS = [
409
416
  ),
410
417
  "input_schema": {"type": "object", "properties": {}},
411
418
  },
419
+ {
420
+ "name": "upgrade_sentinel",
421
+ "description": (
422
+ "Upgrade the Sentinel agent itself: git pull the latest code, update Python deps, "
423
+ "then restart the process. Safe to call at any time — if already up to date, "
424
+ "no restart is triggered. "
425
+ "Use for: 'upgrade sentinel', 'update sentinel', 'upgrade yourself', "
426
+ "'pull latest sentinel code', 'restart sentinel after upgrade'."
427
+ ),
428
+ "input_schema": {"type": "object", "properties": {}},
429
+ },
412
430
  ]
413
431
 
414
432
 
@@ -854,6 +872,73 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
854
872
  ],
855
873
  })
856
874
 
875
+ if name == "upgrade_sentinel":
876
+ import threading
877
+ import signal as _sig
878
+
879
+ code_dir = Path(__file__).resolve().parent.parent # sentinel repo root
880
+ project_dir = Path(".").resolve()
881
+ steps: list[dict] = []
882
+
883
+ # Step 1: git pull the sentinel agent code
884
+ pull = _git_pull(code_dir)
885
+ steps.append({"step": "git_pull", **pull})
886
+ already_latest = "already up to date" in pull.get("detail", "").lower()
887
+
888
+ if pull["status"] == "error":
889
+ return json.dumps({"status": "error", "steps": steps,
890
+ "note": "git pull failed — check network / SSH key"})
891
+
892
+ # Step 2: pip install (update Python deps)
893
+ venv_pip = code_dir / ".venv" / "bin" / "pip"
894
+ pip_cmd = str(venv_pip) if venv_pip.exists() else "pip3"
895
+ req_file = code_dir / "requirements.txt"
896
+ if req_file.exists():
897
+ try:
898
+ r = subprocess.run(
899
+ [pip_cmd, "install", "-q", "-r", str(req_file)],
900
+ capture_output=True, text=True, timeout=120,
901
+ )
902
+ steps.append({
903
+ "step": "pip_install",
904
+ "status": "ok" if r.returncode == 0 else "warn",
905
+ "detail": (r.stderr or "").strip()[:200],
906
+ })
907
+ except Exception as e:
908
+ steps.append({"step": "pip_install", "status": "warn", "detail": str(e)})
909
+
910
+ if already_latest:
911
+ return json.dumps({
912
+ "status": "already_latest",
913
+ "steps": steps,
914
+ "note": "Sentinel is already up to date. No restart needed.",
915
+ })
916
+
917
+ # Step 3: restart — schedule after response is sent
918
+ stop_sh = project_dir / "stop.sh"
919
+ start_sh = project_dir / "start.sh"
920
+ if stop_sh.exists() and start_sh.exists():
921
+ def _restart_scripts():
922
+ import time; time.sleep(2)
923
+ subprocess.Popen(
924
+ f"bash {stop_sh} && sleep 2 && bash {start_sh}",
925
+ shell=True,
926
+ )
927
+ threading.Thread(target=_restart_scripts, daemon=True).start()
928
+ restart_method = "stop.sh + start.sh"
929
+ else:
930
+ # SIGTERM self — systemd (Restart=always) will bring it back up
931
+ threading.Timer(2.0, lambda: os.kill(os.getpid(), _sig.SIGTERM)).start()
932
+ restart_method = "SIGTERM → systemd restart"
933
+
934
+ steps.append({"step": "restart", "status": "scheduled", "method": restart_method})
935
+ logger.info("Boss: upgrade_sentinel complete, restarting via %s", restart_method)
936
+ return json.dumps({
937
+ "status": "ok",
938
+ "steps": steps,
939
+ "note": "Upgrade complete. Sentinel is restarting — give it a few seconds then I'll be back.",
940
+ })
941
+
857
942
  return json.dumps({"error": f"unknown tool: {name}"})
858
943
 
859
944