@misterhuydo/sentinel 1.5.43 → 1.5.45

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.5.43",
3
+ "version": "1.5.45",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -1 +1 @@
1
- __version__ = "1.5.43"
1
+ __version__ = "1.5.45"
@@ -563,27 +563,23 @@ When to act vs. when to ask:
563
563
  When filter_logs returns no hits after a recent release, always retry with search_logs before
564
564
  telling the user the log line isn't there.
565
565
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
566
- LOG RESULTS DO NOT PROVE DEPLOYMENT STATUS
566
+ LOG SEARCH RESULTS REPORT ONLY WHAT WAS FOUND
567
567
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
568
- You are FORBIDDEN from asserting "release X is not deployed" or "servers are still on old version"
569
- based solely on log search results not from zero hits, not from finding old lines without new ones.
568
+ When the user asks to filter, search, or fetch logs: report exactly what the tool returned.
569
+ Do NOT add deployment status, release status, version conclusions, or "next steps" about Jenkins.
570
+ The user asked for log lines — give them the log lines (or "no matches found"), nothing more.
570
571
 
571
- WHY: A log line only appears when that exact code path executes. Finding 0 hits or only old log
572
- lines says nothing about what code version is running. The new line simply hasn't been triggered yet.
573
-
574
- WRONG NEVER SAY THESE:
575
- "Zero hits for '<feature log pattern>'. Release X.Y.Z has not deployed."
576
- "Found old log entries but no new ones — servers are still on the previous version."
577
- "The new code from release X.Y.Z is still not deployed."
572
+ WRONG (user asked for log results, not deployment analysis):
573
+ "Found 3 matches. The new code from release X.Y.Z is still not deployed."
574
+ "No matches — the servers haven't picked up the new release yet."
575
+ "Zero hits. Check Jenkins to confirm the build status."
578
576
 
579
577
  CORRECT:
580
- "No '<feature log pattern>' lines yet — the code path hasn't been triggered since the last
581
- fetch. This says nothing about whether the release is deployed."
578
+ "3 matches found: [table of results]"
579
+ "No matches for '<pattern>' in <source>."
582
580
 
583
- To verify if a release is live:
584
- 1. FIRST: call check_health for the relevant source/repo it returns the live version directly.
585
- 2. If no HEALTH_URL is configured: search_logs with query "Starting|started in|version|initialized".
586
- NEVER state deployment status without calling check_health first.
581
+ Deployment/version questions are separate. If the user asks "is version X deployed?", THEN
582
+ call check_health (returns live version from HEALTH_URL) or search_logs for startup lines.
587
583
  - If a tool call will take a moment (search, fetch, pull), prefix your reply with a brief "working" line ending in "..." before the results, e.g. "Searching SSOLWA for TryDig activity..." then the actual output.
588
584
  Never just say a working line and stop — always follow it with the results in the same message.
589
585
 
@@ -2069,6 +2065,36 @@ def _filter_log_sources(props_files: list, source_hint: str) -> list:
2069
2065
  return matched
2070
2066
 
2071
2067
 
2068
+ def _auto_health_check(source_hint: str, cfg_loader) -> dict | None:
2069
+ """
2070
+ If any log source or repo matching source_hint has a HEALTH_URL, call it and return
2071
+ the result so it can be injected into fetch/filter responses automatically.
2072
+ Returns None if no health URL is configured or the call fails silently.
2073
+ """
2074
+ try:
2075
+ import requests as _req
2076
+ url = ""
2077
+ for src_name, src in cfg_loader.log_sources.items():
2078
+ if source_hint.lower() in src_name.lower() and getattr(src, "health_url", ""):
2079
+ url = src.health_url
2080
+ break
2081
+ if not url:
2082
+ for repo_name, repo in cfg_loader.repos.items():
2083
+ if source_hint.lower() in repo_name.lower() and repo.health_url:
2084
+ url = repo.health_url
2085
+ break
2086
+ if not url:
2087
+ return None
2088
+ resp = _req.get(url, timeout=8)
2089
+ try:
2090
+ data = resp.json()
2091
+ except Exception:
2092
+ data = resp.text[:500]
2093
+ return {"health_url": url, "status_code": resp.status_code, "health": data}
2094
+ except Exception:
2095
+ return None
2096
+
2097
+
2072
2098
  # ── Tool execution ────────────────────────────────────────────────────────────
2073
2099
 
2074
2100
  async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=None, user_id: str = "", channel: str = "", is_admin: bool = False) -> str:
@@ -2929,6 +2955,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2929
2955
  sources_searched = [d.name + (" [temp]" if is_temp else "") for d, is_temp in search_pairs]
2930
2956
  if total == 0:
2931
2957
  has_temp = bool(temp_dirs)
2958
+ health = _auto_health_check(source_f, cfg_loader) if source_f else None
2932
2959
  return json.dumps({
2933
2960
  "query": query_f,
2934
2961
  "total_matches": 0,
@@ -2939,6 +2966,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2939
2966
  + "If searching for a specific log line from a new feature, use fetch_logs "
2940
2967
  "with a matching grep_filter first — the default filter only captures WARN/ERROR."
2941
2968
  ),
2969
+ **({"service_health": health} if health else {}),
2942
2970
  })
2943
2971
 
2944
2972
  # Pattern grouping: count occurrences of each error signature
@@ -2980,6 +3008,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2980
3008
  except Exception:
2981
3009
  pass
2982
3010
 
3011
+ health = _auto_health_check(source_f, cfg_loader) if source_f else None
2983
3012
  return json.dumps({
2984
3013
  "query": query_f,
2985
3014
  "total_matches": total,
@@ -2989,6 +3018,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
2989
3018
  "sample_lines": sample_lines,
2990
3019
  "time_span": time_span,
2991
3020
  "capped": total >= max_matches,
3021
+ **({"service_health": health} if health else {}),
2992
3022
  })
2993
3023
 
2994
3024
  if name == "trigger_poll":
@@ -3141,7 +3171,12 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
3141
3171
  except Exception as e:
3142
3172
  results.append({"source": props.stem, "error": str(e)})
3143
3173
 
3144
- return json.dumps({"fetched": len(results), "results": results})
3174
+ health = _auto_health_check(source_filter, cfg_loader) if source_filter else None
3175
+ return json.dumps({
3176
+ "fetched": len(results),
3177
+ "results": results,
3178
+ **({"service_health": health} if health else {}),
3179
+ })
3145
3180
 
3146
3181
  if name == "watch_bot":
3147
3182
  if not is_admin: