@misterhuydo/sentinel 1.5.56 → 1.5.57
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/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-04-21T07:
|
|
3
|
-
"checkpoint_at": "2026-04-21T07:
|
|
2
|
+
"message": "Auto-checkpoint at 2026-04-21T07:32:04.414Z",
|
|
3
|
+
"checkpoint_at": "2026-04-21T07:32:04.416Z",
|
|
4
4
|
"active_files": [
|
|
5
5
|
"J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js",
|
|
6
6
|
"J:\\Projects\\Sentinel\\cli\\lib\\test.js",
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.5.
|
|
1
|
+
__version__ = "1.5.57"
|
|
@@ -377,8 +377,8 @@ reply with a grouped summary like this:
|
|
|
377
377
|
Minimum interval: 60 seconds. Allowed tools: fetch_logs, filter_logs, get_status,
|
|
378
378
|
ask_logs, list_recent_commits, check_health.
|
|
379
379
|
Always confirm to the user with the monitor ID and stop condition before creating.
|
|
380
|
-
• `stop_monitor` —
|
|
381
|
-
• `list_monitors` — show
|
|
380
|
+
• `stop_monitor` — delete a monitor by ID (stops it if active); pass "all" to delete all in this channel
|
|
381
|
+
• `list_monitors` — show active monitors plus completed/cancelled ones from the last 24 hours
|
|
382
382
|
|
|
383
383
|
*File sharing*
|
|
384
384
|
• `post_file` — upload any output as a Slack file (logs, diffs, reports)
|
|
@@ -1394,8 +1394,12 @@ _TOOLS = [
|
|
|
1394
1394
|
{
|
|
1395
1395
|
"name": "stop_monitor",
|
|
1396
1396
|
"description": (
|
|
1397
|
-
"
|
|
1398
|
-
"
|
|
1397
|
+
"Delete a monitor by ID — removes it from the list immediately. "
|
|
1398
|
+
"Works on any monitor regardless of status (active, done, or cancelled). "
|
|
1399
|
+
"For active monitors this also stops it from running. "
|
|
1400
|
+
"Pass 'all' to delete every monitor in this channel. "
|
|
1401
|
+
"Use for: 'delete monitor m-abc123', 'stop monitor m-abc123', 'delete all monitors', "
|
|
1402
|
+
"'clear monitors', 'cancel the log watch'."
|
|
1399
1403
|
),
|
|
1400
1404
|
"input_schema": {
|
|
1401
1405
|
"type": "object",
|
|
@@ -1411,8 +1415,9 @@ _TOOLS = [
|
|
|
1411
1415
|
{
|
|
1412
1416
|
"name": "list_monitors",
|
|
1413
1417
|
"description": (
|
|
1414
|
-
"List all active
|
|
1415
|
-
"Use for: 'what monitors are running?', 'show scheduled tasks', 'list
|
|
1418
|
+
"List all monitors — active ones plus completed/cancelled ones from the last 24 hours. "
|
|
1419
|
+
"Use for: 'what monitors are running?', 'show scheduled tasks', 'list monitors', "
|
|
1420
|
+
"'show recent monitors'. Each entry includes status (active/done/cancelled)."
|
|
1416
1421
|
),
|
|
1417
1422
|
"input_schema": {"type": "object", "properties": {}},
|
|
1418
1423
|
},
|
|
@@ -3589,32 +3594,33 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
3589
3594
|
if not mon_id:
|
|
3590
3595
|
return json.dumps({"error": "monitor_id is required"})
|
|
3591
3596
|
if mon_id.lower() == "all":
|
|
3592
|
-
count = store.
|
|
3593
|
-
return json.dumps({"
|
|
3594
|
-
ok = store.
|
|
3597
|
+
count = store.delete_all_monitors(channel=channel)
|
|
3598
|
+
return json.dumps({"deleted": count, "message": f"Deleted {count} monitor(s)."})
|
|
3599
|
+
ok = store.delete_monitor(mon_id)
|
|
3595
3600
|
if ok:
|
|
3596
|
-
return json.dumps({"status": "
|
|
3597
|
-
return json.dumps({"error": f"Monitor '{mon_id}' not found
|
|
3601
|
+
return json.dumps({"status": "deleted", "monitor_id": mon_id})
|
|
3602
|
+
return json.dumps({"error": f"Monitor '{mon_id}' not found."})
|
|
3598
3603
|
|
|
3599
3604
|
if name == "list_monitors":
|
|
3600
3605
|
monitors = store.list_active_monitors()
|
|
3601
3606
|
if not monitors:
|
|
3602
|
-
return json.dumps({"monitors": [], "message": "No
|
|
3607
|
+
return json.dumps({"monitors": [], "message": "No monitors in the last 24 hours."})
|
|
3603
3608
|
result = []
|
|
3604
3609
|
for _m in monitors:
|
|
3605
3610
|
_runs_left = None
|
|
3606
3611
|
if _m.get("max_runs"):
|
|
3607
3612
|
_runs_left = _m["max_runs"] - _m["runs_so_far"]
|
|
3608
3613
|
result.append({
|
|
3609
|
-
"id":
|
|
3610
|
-
"name":
|
|
3611
|
-
"status":
|
|
3612
|
-
"interval":
|
|
3613
|
-
"runs_so_far":
|
|
3614
|
-
"runs_left":
|
|
3615
|
-
"next_run_at":
|
|
3616
|
-
"stop_at":
|
|
3617
|
-
"
|
|
3614
|
+
"id": _m["id"],
|
|
3615
|
+
"name": _m.get("name") or "",
|
|
3616
|
+
"status": _m["status"],
|
|
3617
|
+
"interval": _format_duration(_m["interval_seconds"]),
|
|
3618
|
+
"runs_so_far": _m["runs_so_far"],
|
|
3619
|
+
"runs_left": _runs_left,
|
|
3620
|
+
"next_run_at": _m.get("next_run_at") or "",
|
|
3621
|
+
"stop_at": _m.get("stop_at") or "",
|
|
3622
|
+
"completed_at": _m.get("completed_at") or "",
|
|
3623
|
+
"steps": json.loads(_m.get("steps_json") or "[]"),
|
|
3618
3624
|
})
|
|
3619
3625
|
return json.dumps({"monitors": result})
|
|
3620
3626
|
|
|
@@ -450,8 +450,14 @@ class StateStore:
|
|
|
450
450
|
"channel TEXT, "
|
|
451
451
|
"user_id TEXT, "
|
|
452
452
|
"status TEXT DEFAULT 'active', "
|
|
453
|
+
"completed_at TEXT, "
|
|
453
454
|
"created_at TEXT)"
|
|
454
455
|
)
|
|
456
|
+
# Migration: add completed_at to existing tables
|
|
457
|
+
try:
|
|
458
|
+
conn.execute("ALTER TABLE monitors ADD COLUMN completed_at TEXT")
|
|
459
|
+
except Exception:
|
|
460
|
+
pass
|
|
455
461
|
|
|
456
462
|
def create_monitor(self, id: str, name: str, steps_json: str,
|
|
457
463
|
interval_seconds: int, stop_at, max_runs,
|
|
@@ -486,10 +492,21 @@ class StateStore:
|
|
|
486
492
|
return dict(row) if row else None
|
|
487
493
|
|
|
488
494
|
def list_active_monitors(self) -> list[dict]:
|
|
495
|
+
from datetime import datetime, timezone, timedelta
|
|
496
|
+
now = datetime.now(timezone.utc)
|
|
497
|
+
cutoff = (now - timedelta(hours=24)).isoformat()
|
|
489
498
|
with self._conn() as conn:
|
|
490
499
|
self._ensure_monitors_table(conn)
|
|
500
|
+
# Auto-clean completed/done monitors older than 24 h
|
|
501
|
+
conn.execute(
|
|
502
|
+
"DELETE FROM monitors WHERE status != 'active' AND completed_at < ?",
|
|
503
|
+
(cutoff,),
|
|
504
|
+
)
|
|
491
505
|
rows = conn.execute(
|
|
492
|
-
"SELECT * FROM monitors
|
|
506
|
+
"SELECT * FROM monitors "
|
|
507
|
+
"WHERE status = 'active' OR (status != 'active' AND completed_at >= ?) "
|
|
508
|
+
"ORDER BY created_at DESC",
|
|
509
|
+
(cutoff,),
|
|
493
510
|
).fetchall()
|
|
494
511
|
return [dict(r) for r in rows]
|
|
495
512
|
|
|
@@ -505,7 +522,12 @@ class StateStore:
|
|
|
505
522
|
with self._conn() as conn:
|
|
506
523
|
self._ensure_monitors_table(conn)
|
|
507
524
|
if done:
|
|
508
|
-
|
|
525
|
+
# Keep done monitors visible for 24 h; user can delete explicitly via stop_monitor
|
|
526
|
+
conn.execute(
|
|
527
|
+
"UPDATE monitors SET runs_so_far = runs_so_far + 1, last_run_at = ?, "
|
|
528
|
+
"status = 'done', completed_at = ? WHERE id = ?",
|
|
529
|
+
(_now(), _now(), id),
|
|
530
|
+
)
|
|
509
531
|
else:
|
|
510
532
|
conn.execute(
|
|
511
533
|
"UPDATE monitors SET runs_so_far = runs_so_far + 1, last_run_at = ?, "
|
|
@@ -513,26 +535,31 @@ class StateStore:
|
|
|
513
535
|
(_now(), next_run_at, id),
|
|
514
536
|
)
|
|
515
537
|
|
|
516
|
-
def
|
|
538
|
+
def delete_monitor(self, id: str) -> bool:
|
|
539
|
+
"""Delete any monitor regardless of status (cancel + remove for active ones)."""
|
|
517
540
|
with self._conn() as conn:
|
|
518
541
|
self._ensure_monitors_table(conn)
|
|
519
|
-
cur = conn.execute(
|
|
520
|
-
"DELETE FROM monitors WHERE id = ? AND status = 'active'", (id,)
|
|
521
|
-
)
|
|
542
|
+
cur = conn.execute("DELETE FROM monitors WHERE id = ?", (id,))
|
|
522
543
|
return cur.rowcount > 0
|
|
523
544
|
|
|
524
|
-
def
|
|
545
|
+
def delete_all_monitors(self, channel: str = "") -> int:
|
|
546
|
+
"""Delete all monitors in a channel (or globally if channel is empty)."""
|
|
525
547
|
with self._conn() as conn:
|
|
526
548
|
self._ensure_monitors_table(conn)
|
|
527
549
|
if channel:
|
|
528
|
-
cur = conn.execute(
|
|
529
|
-
"DELETE FROM monitors WHERE status = 'active' AND channel = ?",
|
|
530
|
-
(channel,),
|
|
531
|
-
)
|
|
550
|
+
cur = conn.execute("DELETE FROM monitors WHERE channel = ?", (channel,))
|
|
532
551
|
else:
|
|
533
|
-
cur = conn.execute("DELETE FROM monitors
|
|
552
|
+
cur = conn.execute("DELETE FROM monitors")
|
|
534
553
|
return cur.rowcount
|
|
535
554
|
|
|
555
|
+
def cancel_monitor(self, id: str) -> bool:
|
|
556
|
+
"""Alias for delete_monitor — kept for compatibility."""
|
|
557
|
+
return self.delete_monitor(id)
|
|
558
|
+
|
|
559
|
+
def cancel_all_monitors(self, channel: str = "") -> int:
|
|
560
|
+
"""Alias for delete_all_monitors — kept for compatibility."""
|
|
561
|
+
return self.delete_all_monitors(channel)
|
|
562
|
+
|
|
536
563
|
# ── Pending bot-message routing questions ─────────────────────────────────
|
|
537
564
|
|
|
538
565
|
def _ensure_pending_routings_table(self, conn):
|