@misterhuydo/sentinel 1.0.64 → 1.0.65
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 +2 -2
- package/package.json +1 -1
- package/python/sentinel/sentinel_boss.py +53 -12
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-22T15:
|
|
3
|
-
"checkpoint_at": "2026-03-22T15:
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-22T15:56:59.308Z",
|
|
3
|
+
"checkpoint_at": "2026-03-22T15:56:59.309Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -53,8 +53,9 @@ What you can do (tools available):
|
|
|
53
53
|
5. list_projects — List all configured repos and log sources in this Sentinel instance.
|
|
54
54
|
e.g. "what projects are you watching?", "list all repos"
|
|
55
55
|
|
|
56
|
-
6. search_logs —
|
|
57
|
-
|
|
56
|
+
6. search_logs — SSH live to servers and grep logs in real time (uses fetch_log.sh with
|
|
57
|
+
the query as GREP_FILTER). Falls back to cached files if unavailable.
|
|
58
|
+
e.g. "search logs for illegal PIN in 1881", "find X in SSOLWA", "grep logs for Z"
|
|
58
59
|
|
|
59
60
|
7. trigger_poll — Trigger an immediate poll cycle without waiting for the schedule.
|
|
60
61
|
e.g. "check now", "poll immediately", "don't wait, run now"
|
|
@@ -109,7 +110,7 @@ reply with a short summary grouped by category:
|
|
|
109
110
|
|
|
110
111
|
*Log management*
|
|
111
112
|
• `fetch_logs` — pull fresh logs from servers right now — "fetch logs for SSOLWA"
|
|
112
|
-
• `search_logs` —
|
|
113
|
+
• `search_logs` — live SSH grep on production servers — "search logs for illegal PIN in 1881"
|
|
113
114
|
|
|
114
115
|
*Fix management*
|
|
115
116
|
• `get_fix_details` — full details of a specific fix — "show fix abc123"
|
|
@@ -232,25 +233,28 @@ _TOOLS = [
|
|
|
232
233
|
{
|
|
233
234
|
"name": "search_logs",
|
|
234
235
|
"description": (
|
|
235
|
-
"Search
|
|
236
|
-
"
|
|
237
|
-
"
|
|
236
|
+
"Search production logs for a keyword or pattern. "
|
|
237
|
+
"When a project or source is specified (or can be inferred), performs a LIVE fetch "
|
|
238
|
+
"via fetch_log.sh with the query as the grep filter — SSHes directly to the server. "
|
|
239
|
+
"Falls back to searching locally-cached log files when no source can be determined. "
|
|
240
|
+
"Use for: 'search logs for illegal PIN in 1881', 'find X in SSOLWA logs', "
|
|
241
|
+
"'what did user Y do?', 'show entries for appid=Z', 'grep logs for X'."
|
|
238
242
|
),
|
|
239
243
|
"input_schema": {
|
|
240
244
|
"type": "object",
|
|
241
245
|
"properties": {
|
|
242
246
|
"query": {
|
|
243
247
|
"type": "string",
|
|
244
|
-
"description": "
|
|
248
|
+
"description": "Keyword or regex to grep for",
|
|
245
249
|
},
|
|
246
250
|
"source": {
|
|
247
251
|
"type": "string",
|
|
248
|
-
"description": "
|
|
252
|
+
"description": "Log source name to search (partial match against log-config filenames, e.g. 'SSOLWA', '1881'). Leave empty to search all sources.",
|
|
249
253
|
},
|
|
250
254
|
"max_matches": {
|
|
251
255
|
"type": "integer",
|
|
252
|
-
"description": "Max matching lines to return per
|
|
253
|
-
"default":
|
|
256
|
+
"description": "Max matching lines to return per source (default 30)",
|
|
257
|
+
"default": 30,
|
|
254
258
|
},
|
|
255
259
|
},
|
|
256
260
|
"required": ["query"],
|
|
@@ -634,10 +638,46 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
634
638
|
if name == "search_logs":
|
|
635
639
|
query = inputs.get("query", "")
|
|
636
640
|
source = inputs.get("source", "").lower()
|
|
637
|
-
max_matches = int(inputs.get("max_matches",
|
|
641
|
+
max_matches = int(inputs.get("max_matches", 30))
|
|
642
|
+
|
|
643
|
+
# ── Live fetch path: SSH to servers and grep in real time ──────────────
|
|
644
|
+
script = Path(__file__).resolve().parent.parent / "scripts" / "fetch_log.sh"
|
|
645
|
+
log_cfg_dir = Path("config") / "log-configs"
|
|
646
|
+
if script.exists() and log_cfg_dir.exists():
|
|
647
|
+
props_files = sorted(log_cfg_dir.glob("*.properties"))
|
|
648
|
+
if source:
|
|
649
|
+
props_files = [p for p in props_files if source in p.stem.lower()]
|
|
650
|
+
if props_files:
|
|
651
|
+
live_results = []
|
|
652
|
+
for props in props_files:
|
|
653
|
+
env = os.environ.copy()
|
|
654
|
+
env["GREP_FILTER"] = query
|
|
655
|
+
try:
|
|
656
|
+
r = subprocess.run(
|
|
657
|
+
["bash", str(script), str(props)],
|
|
658
|
+
capture_output=True, text=True, timeout=60, env=env,
|
|
659
|
+
)
|
|
660
|
+
lines = (r.stdout or "").strip().splitlines()
|
|
661
|
+
matches = [ln[:300] for ln in lines if ln.strip()][:max_matches]
|
|
662
|
+
if matches:
|
|
663
|
+
live_results.append({"source": props.stem, "matches": matches})
|
|
664
|
+
logger.info("Boss search_logs live %s rc=%d found=%d", props.stem, r.returncode, len(matches))
|
|
665
|
+
except subprocess.TimeoutExpired:
|
|
666
|
+
live_results.append({"source": props.stem, "error": "timed out"})
|
|
667
|
+
except Exception as e:
|
|
668
|
+
live_results.append({"source": props.stem, "error": str(e)})
|
|
669
|
+
total = sum(len(r.get("matches", [])) for r in live_results)
|
|
670
|
+
return json.dumps({
|
|
671
|
+
"query": query,
|
|
672
|
+
"mode": "live",
|
|
673
|
+
"total_matches": total,
|
|
674
|
+
"results": live_results,
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
# ── Fallback: search locally-cached log files ──────────────────────────
|
|
638
678
|
fetched_dir = Path("workspace/fetched")
|
|
639
679
|
if not fetched_dir.exists():
|
|
640
|
-
return json.dumps({"error": "No fetched logs found
|
|
680
|
+
return json.dumps({"error": "No fetched logs found and fetch_log.sh unavailable"})
|
|
641
681
|
try:
|
|
642
682
|
pattern = re.compile(query, re.IGNORECASE)
|
|
643
683
|
except re.error as e:
|
|
@@ -660,6 +700,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
660
700
|
total = sum(len(r["matches"]) for r in results)
|
|
661
701
|
return json.dumps({
|
|
662
702
|
"query": query,
|
|
703
|
+
"mode": "cached",
|
|
663
704
|
"total_matches": total,
|
|
664
705
|
"files_searched": len(list(fetched_dir.glob("*.log"))),
|
|
665
706
|
"results": results,
|