@misterhuydo/sentinel 1.0.46 → 1.0.48
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-03-22T05:
|
|
3
|
-
"checkpoint_at": "2026-03-22T05:
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-22T05:57:18.189Z",
|
|
3
|
+
"checkpoint_at": "2026-03-22T05:57:18.190Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -60,6 +60,7 @@ class SentinelConfig:
|
|
|
60
60
|
slack_bot_token: str = "" # xoxb-...
|
|
61
61
|
slack_app_token: str = "" # xapp-... (Socket Mode)
|
|
62
62
|
slack_channel: str = "" # optional: restrict to one channel ID or name
|
|
63
|
+
project_name: str = "" # optional: friendly name used by Sentinel Boss (e.g. "1881")
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
@dataclass
|
|
@@ -152,6 +153,7 @@ class ConfigLoader:
|
|
|
152
153
|
c.slack_bot_token = d.get("SLACK_BOT_TOKEN", "")
|
|
153
154
|
c.slack_app_token = d.get("SLACK_APP_TOKEN", "")
|
|
154
155
|
c.slack_channel = d.get("SLACK_CHANNEL", "")
|
|
156
|
+
c.project_name = d.get("PROJECT_NAME", "")
|
|
155
157
|
self.sentinel = c
|
|
156
158
|
|
|
157
159
|
def _load_log_sources(self):
|
|
@@ -290,8 +290,24 @@ def _short_name(dir_name: str) -> str:
|
|
|
290
290
|
return dir_name[len("sentinel-"):]
|
|
291
291
|
return dir_name
|
|
292
292
|
|
|
293
|
+
def _read_project_name(project_dir: Path) -> str:
|
|
294
|
+
"""Return PROJECT_NAME from sentinel.properties if set, else fall back to _short_name(dir)."""
|
|
295
|
+
props = project_dir / "config" / "sentinel.properties"
|
|
296
|
+
if props.exists():
|
|
297
|
+
try:
|
|
298
|
+
for line in props.read_text(encoding="utf-8", errors="ignore").splitlines():
|
|
299
|
+
line = line.strip()
|
|
300
|
+
if line.startswith("PROJECT_NAME"):
|
|
301
|
+
_, _, val = line.partition("=")
|
|
302
|
+
val = val.partition("#")[0].strip()
|
|
303
|
+
if val:
|
|
304
|
+
return val
|
|
305
|
+
except Exception:
|
|
306
|
+
pass
|
|
307
|
+
return _short_name(project_dir.name)
|
|
308
|
+
|
|
293
309
|
def _find_project_dirs(target: str = "") -> list[Path]:
|
|
294
|
-
"""Return project dirs matching target (short or full name), or all if target empty."""
|
|
310
|
+
"""Return project dirs matching target (PROJECT_NAME, short name, or full dir name), or all if target empty."""
|
|
295
311
|
workspace = _workspace_dir()
|
|
296
312
|
results = []
|
|
297
313
|
try:
|
|
@@ -301,7 +317,10 @@ def _find_project_dirs(target: str = "") -> list[Path]:
|
|
|
301
317
|
if not (d / "config").exists():
|
|
302
318
|
continue
|
|
303
319
|
if target:
|
|
304
|
-
|
|
320
|
+
t = target.lower()
|
|
321
|
+
if (t not in d.name.lower()
|
|
322
|
+
and t not in _short_name(d.name).lower()
|
|
323
|
+
and t not in _read_project_name(d).lower()):
|
|
305
324
|
continue
|
|
306
325
|
results.append(d)
|
|
307
326
|
except Exception:
|
|
@@ -365,14 +384,14 @@ def _run_tool(name: str, inputs: dict, cfg_loader, store) -> str:
|
|
|
365
384
|
if project_arg:
|
|
366
385
|
project_dirs = _find_project_dirs(project_arg)
|
|
367
386
|
if not project_dirs:
|
|
368
|
-
all_names = [
|
|
387
|
+
all_names = [_read_project_name(d) for d in _find_project_dirs()]
|
|
369
388
|
return json.dumps({
|
|
370
389
|
"error": f"No project found matching '{project_arg}'",
|
|
371
390
|
"available_projects": all_names,
|
|
372
391
|
"action_needed": "Ask the user which project they meant.",
|
|
373
392
|
})
|
|
374
393
|
if len(project_dirs) > 1:
|
|
375
|
-
matches = [
|
|
394
|
+
matches = [_read_project_name(d) for d in project_dirs]
|
|
376
395
|
return json.dumps({
|
|
377
396
|
"error": f"Ambiguous project name '{project_arg}' — matches: {matches}",
|
|
378
397
|
"action_needed": "Ask the user to clarify which project they mean.",
|
|
@@ -390,7 +409,7 @@ def _run_tool(name: str, inputs: dict, cfg_loader, store) -> str:
|
|
|
390
409
|
# Touch SENTINEL_POLL_NOW so the target instance picks it up immediately
|
|
391
410
|
(project_dir / "SENTINEL_POLL_NOW").touch()
|
|
392
411
|
|
|
393
|
-
project_label =
|
|
412
|
+
project_label = _read_project_name(project_dir.resolve()) if project_arg else "this project"
|
|
394
413
|
logger.info("Boss created issue for %s: %s", project_label, fname)
|
|
395
414
|
return json.dumps({
|
|
396
415
|
"status": "queued",
|
|
@@ -451,7 +470,7 @@ def _run_tool(name: str, inputs: dict, cfg_loader, store) -> str:
|
|
|
451
470
|
break
|
|
452
471
|
repos_in_project.append({"repo": p.stem, "url": repo_url})
|
|
453
472
|
projects.append({
|
|
454
|
-
"project":
|
|
473
|
+
"project": _read_project_name(d),
|
|
455
474
|
"dir": d.name,
|
|
456
475
|
"running": (d / "sentinel.pid").exists(),
|
|
457
476
|
"this": d.resolve() == Path(".").resolve(),
|
|
@@ -561,7 +580,7 @@ def _run_tool(name: str, inputs: dict, cfg_loader, store) -> str:
|
|
|
561
580
|
results = []
|
|
562
581
|
for d in dirs:
|
|
563
582
|
res = _git_pull(d)
|
|
564
|
-
results.append({"project":
|
|
583
|
+
results.append({"project": _read_project_name(d), "dir": d.name, **res})
|
|
565
584
|
logger.info("Boss: pull_config %s → %s", d.name, res["status"])
|
|
566
585
|
return json.dumps({"results": results})
|
|
567
586
|
|
|
@@ -703,7 +722,7 @@ async def handle_message(
|
|
|
703
722
|
paused = Path("SENTINEL_PAUSE").exists()
|
|
704
723
|
repos = list(cfg_loader.repos.keys())
|
|
705
724
|
ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
|
706
|
-
known_projects = [
|
|
725
|
+
known_projects = [_read_project_name(d) for d in _find_project_dirs()]
|
|
707
726
|
system = (
|
|
708
727
|
_SYSTEM
|
|
709
728
|
+ f"\n\nCurrent time: {ts}"
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
# Shared settings (SMTP, schedule, etc.) live in the workspace-level sentinel.properties
|
|
3
3
|
# one directory above this project. Values here override workspace defaults.
|
|
4
4
|
|
|
5
|
+
# Friendly project name for Sentinel Boss (optional).
|
|
6
|
+
# Boss will match this name when you say "tell 1881 to do X". If omitted, derived from dir name.
|
|
7
|
+
# PROJECT_NAME=1881
|
|
8
|
+
|
|
5
9
|
# Who receives fix notifications and health reports for this project
|
|
6
10
|
MAILS=you@yourdomain.com
|
|
7
11
|
|
|
@@ -9,8 +13,10 @@ MAILS=you@yourdomain.com
|
|
|
9
13
|
SEND_HEALTH=disabled
|
|
10
14
|
REPORT_INTERVAL_HOURS=1
|
|
11
15
|
|
|
12
|
-
# GitHub token for opening PRs (
|
|
13
|
-
|
|
16
|
+
# GitHub token for opening PRs (when AUTO_PUBLISH=false).
|
|
17
|
+
# Usually set once in the workspace sentinel.properties and shared across all projects.
|
|
18
|
+
# Uncomment here only if this project needs a different token.
|
|
19
|
+
# GITHUB_TOKEN=<github-pat>
|
|
14
20
|
|
|
15
21
|
# State DB and workspace paths (relative to this project dir)
|
|
16
22
|
STATE_DB=./sentinel.db
|
|
@@ -10,6 +10,10 @@ SMTP_PORT=587
|
|
|
10
10
|
SMTP_USER=sentinel@yourdomain.com
|
|
11
11
|
SMTP_PASSWORD=<app-password>
|
|
12
12
|
|
|
13
|
+
# GitHub token for opening PRs (when AUTO_PUBLISH=false).
|
|
14
|
+
# Shared across all projects — override per-project in config/sentinel.properties if needed.
|
|
15
|
+
GITHUB_TOKEN=<github-pat>
|
|
16
|
+
|
|
13
17
|
# Fix confidence threshold (0.0 - 1.0)
|
|
14
18
|
FIX_CONFIDENCE_THRESHOLD=0.7
|
|
15
19
|
|