@misterhuydo/sentinel 1.0.53 → 1.0.55
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-
|
|
1
|
+
2026-03-22T12:03:28.852Z
|
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-
|
|
3
|
-
"checkpoint_at": "2026-03-
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-22T12:06:59.426Z",
|
|
3
|
+
"checkpoint_at": "2026-03-22T12:06:59.427Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -61,6 +61,7 @@ class SentinelConfig:
|
|
|
61
61
|
slack_app_token: str = "" # xapp-... (Socket Mode)
|
|
62
62
|
slack_channel: str = "" # optional: restrict to one channel ID or name
|
|
63
63
|
slack_watch_bot_ids: list[str] = field(default_factory=list) # pre-configured bot IDs to watch passively
|
|
64
|
+
slack_allowed_users: list[str] = field(default_factory=list) # if set, only these Slack user IDs can talk to Boss
|
|
64
65
|
project_name: str = "" # optional: friendly name used by Sentinel Boss (e.g. "1881")
|
|
65
66
|
|
|
66
67
|
|
|
@@ -155,6 +156,7 @@ class ConfigLoader:
|
|
|
155
156
|
c.slack_app_token = d.get("SLACK_APP_TOKEN", "")
|
|
156
157
|
c.slack_channel = d.get("SLACK_CHANNEL", "")
|
|
157
158
|
c.slack_watch_bot_ids = _csv(d.get("SLACK_WATCH_BOT_IDS", ""))
|
|
159
|
+
c.slack_allowed_users = _csv(d.get("SLACK_ALLOWED_USERS", ""))
|
|
158
160
|
c.project_name = d.get("PROJECT_NAME", "")
|
|
159
161
|
self.sentinel = c
|
|
160
162
|
|
|
@@ -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
|
|
|
@@ -293,6 +293,12 @@ async def _dispatch(event: dict, client, cfg_loader, store) -> None:
|
|
|
293
293
|
if not text:
|
|
294
294
|
return
|
|
295
295
|
|
|
296
|
+
# Allowlist check — if SLACK_ALLOWED_USERS is configured, silently ignore everyone else
|
|
297
|
+
allowed = cfg_loader.sentinel.slack_allowed_users
|
|
298
|
+
if allowed and user_id not in allowed:
|
|
299
|
+
logger.warning("Boss: ignoring message from unauthorised user %s", user_id)
|
|
300
|
+
return
|
|
301
|
+
|
|
296
302
|
user_name = await _resolve_name(client, user_id)
|
|
297
303
|
|
|
298
304
|
status, pos, session = await _queue.try_activate(user_id, user_name, channel)
|
|
@@ -36,6 +36,12 @@ WORKSPACE_DIR=./workspace
|
|
|
36
36
|
# Note: requires conversations:read scope on the Slack App if using channel name
|
|
37
37
|
# SLACK_CHANNEL=devops-sentinel
|
|
38
38
|
|
|
39
|
+
# Allowlist of Slack user IDs permitted to give Sentinel Boss commands (RECOMMENDED).
|
|
40
|
+
# If set, all other users are silently ignored — even in the configured channel.
|
|
41
|
+
# Find a user ID in Slack: click their profile → ⋯ More → Copy member ID (starts with U).
|
|
42
|
+
# Comma-separated. Leave unset to allow anyone who can reach the bot (less secure).
|
|
43
|
+
# SLACK_ALLOWED_USERS=U01AB2CD3EF, U09GH8IJ7KL
|
|
44
|
+
|
|
39
45
|
# Passive bot watcher — seed the watch list on startup with known bot IDs.
|
|
40
46
|
# Sentinel will passively queue every message from these bots as issues (no @mention needed).
|
|
41
47
|
# You can also add bots at runtime: "@Sentinel listen to @alertbot for project 1881"
|