bark-agent-hook 0.1.1__py3-none-any.whl
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.
- bark_agent_hook/__init__.py +1 -0
- bark_agent_hook/__main__.py +3 -0
- bark_agent_hook/app.py +102 -0
- bark_agent_hook/audit.py +103 -0
- bark_agent_hook/cli.py +3 -0
- bark_agent_hook/commands.py +138 -0
- bark_agent_hook/constants.py +85 -0
- bark_agent_hook/hook.py +31 -0
- bark_agent_hook/install.py +3 -0
- bark_agent_hook/installer.py +298 -0
- bark_agent_hook/models.py +67 -0
- bark_agent_hook/notification.py +310 -0
- bark_agent_hook/output.py +119 -0
- bark_agent_hook/py.typed +0 -0
- bark_agent_hook/runtime.py +228 -0
- bark_agent_hook/settings.py +37 -0
- bark_agent_hook/summary.py +231 -0
- bark_agent_hook/utils.py +36 -0
- bark_agent_hook-0.1.1.dist-info/METADATA +89 -0
- bark_agent_hook-0.1.1.dist-info/RECORD +26 -0
- bark_agent_hook-0.1.1.dist-info/WHEEL +4 -0
- bark_agent_hook-0.1.1.dist-info/entry_points.txt +2 -0
- plugins/bark-agent-hook-openclaw/index.js +166 -0
- plugins/bark-agent-hook-openclaw/index.ts +168 -0
- plugins/bark-agent-hook-openclaw/openclaw.plugin.json +13 -0
- plugins/bark-agent-hook-openclaw/package.json +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""bark_agent_hook package."""
|
bark_agent_hook/app.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
helptext = """
|
|
9
|
+
Send Bark notifications from agent lifecycle hooks.
|
|
10
|
+
|
|
11
|
+
Install plugins:
|
|
12
|
+
bark-agent-hook install
|
|
13
|
+
bark-agent-hook install --agent codex
|
|
14
|
+
bark-agent-hook install --agent claude --agent openclaw
|
|
15
|
+
|
|
16
|
+
Uninstall plugins:
|
|
17
|
+
bark-agent-hook uninstall
|
|
18
|
+
bark-agent-hook uninstall --agent codex
|
|
19
|
+
|
|
20
|
+
Hook commands used by the plugins:
|
|
21
|
+
bark-agent-hook hook --runtime codex --event completion
|
|
22
|
+
bark-agent-hook hook --runtime claude --event approval_needed --summary-mode extract
|
|
23
|
+
bark-agent-hook hook --runtime openclaw --event completion --summary-mode extract
|
|
24
|
+
|
|
25
|
+
Direct plugin install commands:
|
|
26
|
+
codex plugin marketplace add qsoyq/bark-agent-hook
|
|
27
|
+
codex plugin add bark-agent-hook-codex@bark-agent-hook
|
|
28
|
+
claude plugin marketplace add qsoyq/bark-agent-hook
|
|
29
|
+
claude plugin install bark-agent-hook@bark-agent-hook --scope user
|
|
30
|
+
openclaw plugins install --link ./plugins/bark-agent-hook-openclaw
|
|
31
|
+
openclaw plugins enable bark-agent-hook-openclaw
|
|
32
|
+
|
|
33
|
+
Configuration:
|
|
34
|
+
BARK_DEVICE_KEY is required. Missing or empty means skip and exit 0.
|
|
35
|
+
BARK_GROUP is optional and overrides the computed Bark group.
|
|
36
|
+
BARK_SERVER defaults to https://api.day.app.
|
|
37
|
+
AGENT_BARK_NOTIFY_HOOK_URL optionally sets a Bark click URL template.
|
|
38
|
+
AGENT_BARK_NOTIFY_TITLE_TEMPLATE optionally sets a notification title template.
|
|
39
|
+
AGENT_BARK_NOTIFY_GROUP_MODE selects the computed group when BARK_GROUP is unset: agent, project, or project-branch.
|
|
40
|
+
AGENT_BARK_NOTIFY_AUDIT_LOG=1 enables local JSONL audit logging.
|
|
41
|
+
AGENT_BARK_NOTIFY_AUDIT_LOG_FILE overrides the audit log path.
|
|
42
|
+
|
|
43
|
+
Template variables:
|
|
44
|
+
AGENT_BARK_NOTIFY_TITLE_TEMPLATE supports:
|
|
45
|
+
{agent}, {event}, {project}, {branch}, {session}, {runtime}, {cwd_basename},
|
|
46
|
+
{LODY_ELECTRON_BOOTSTRAP}, {LODY_ELECTRON_SESSION_USER_ID}, {LODY_SESSION_ID},
|
|
47
|
+
{LODY_WORKSPACE_SESSION_ID}.
|
|
48
|
+
Example: AGENT_BARK_NOTIFY_TITLE_TEMPLATE='[{agent}][{event}][{LODY_SESSION_ID}]'
|
|
49
|
+
AGENT_BARK_NOTIFY_HOOK_URL supports:
|
|
50
|
+
{runtime}, {agent}, {event}, {project}, {branch}, {session}, {session_id},
|
|
51
|
+
{session_key}, {conversation_id}, {message_id}, {run_id}, {agent_id},
|
|
52
|
+
{workspace_dir}, {cwd_basename}, {LODY_ELECTRON_BOOTSTRAP},
|
|
53
|
+
{LODY_ELECTRON_SESSION_USER_ID}, {LODY_SESSION_ID}, {LODY_WORKSPACE_SESSION_ID}.
|
|
54
|
+
Example: AGENT_BARK_NOTIFY_HOOK_URL='https://lody.ai/users/{LODY_ELECTRON_SESSION_USER_ID}/sessions/{LODY_SESSION_ID}'
|
|
55
|
+
Hook URL variable values are percent-encoded; title variables are not URL-encoded.
|
|
56
|
+
Lody passthrough variables are read from LodySettings and limited to the four LODY_* keys above.
|
|
57
|
+
|
|
58
|
+
Audit log field sources:
|
|
59
|
+
Generated by the hook command:
|
|
60
|
+
1. time: UTC timestamp when the audit record is created.
|
|
61
|
+
2. status: final hook handling result, such as sent, skipped_duplicate, or skipped_missing_device_key.
|
|
62
|
+
CLI options:
|
|
63
|
+
1. runtime: agent runtime selected by --runtime or auto detection.
|
|
64
|
+
2. event: notification event selected by --event or inferred from the payload.
|
|
65
|
+
3. summary_mode: notification body mode selected by --summary-mode.
|
|
66
|
+
Hook payload-derived values:
|
|
67
|
+
1. hook_event_name: hook event name read from hook_event_name, event, event_name, or type.
|
|
68
|
+
2. project: project name derived from payload metadata or the current working directory.
|
|
69
|
+
3. session_id_hash: hashed session identifier; raw session IDs are not logged.
|
|
70
|
+
Built notification-derived values:
|
|
71
|
+
1. dedupe_key_hash: hashed notification dedupe key.
|
|
72
|
+
2. title: final notification title.
|
|
73
|
+
3. body_len: length of the final notification body; notification bodies are not logged.
|
|
74
|
+
Lody environment passthrough:
|
|
75
|
+
1. lody: non-empty whitelisted Lody environment values managed by LodySettings.
|
|
76
|
+
Keys: LODY_ELECTRON_BOOTSTRAP, LODY_ELECTRON_SESSION_USER_ID,
|
|
77
|
+
LODY_SESSION_ID, and LODY_WORKSPACE_SESSION_ID.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def version_callback(ctx: typer.Context, value: bool) -> None:
|
|
82
|
+
if not value:
|
|
83
|
+
return
|
|
84
|
+
name = ctx.find_root().info_name or "bark-agent-hook"
|
|
85
|
+
version = importlib.metadata.version("bark-agent-hook")
|
|
86
|
+
typer.echo(f"{name} cli version: {version}")
|
|
87
|
+
raise typer.Exit(0)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def default_invoke_without_command(
|
|
91
|
+
_: bool = typer.Option(False, "--version", "-v", "-V", callback=version_callback),
|
|
92
|
+
) -> None:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def make_typer(help: str, **kwargs: Any) -> typer.Typer:
|
|
97
|
+
app = typer.Typer(help=help, **kwargs)
|
|
98
|
+
app.callback(invoke_without_command=True)(default_invoke_without_command)
|
|
99
|
+
return app
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
cmd = make_typer(helptext)
|
bark_agent_hook/audit.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from bark_agent_hook.constants import (
|
|
9
|
+
AUDIT_LOG_ENV,
|
|
10
|
+
AUDIT_LOG_FILE_ENV,
|
|
11
|
+
BEARER_RE,
|
|
12
|
+
DEFAULT_AUDIT_LOG_PATH,
|
|
13
|
+
SENSITIVE_ASSIGNMENT_RE,
|
|
14
|
+
)
|
|
15
|
+
from bark_agent_hook.models import Notification, SummaryMode
|
|
16
|
+
from bark_agent_hook.runtime import project_name
|
|
17
|
+
from bark_agent_hook.settings import LodySettings
|
|
18
|
+
from bark_agent_hook.summary import _redact_url, _truncate_summary
|
|
19
|
+
from bark_agent_hook.utils import _env_value, _hash_value, _hook_event_name
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _session_id(payload: dict[str, Any]) -> str | None:
|
|
23
|
+
value = payload.get("session_id") or payload.get("sessionId") or payload.get("sessionKey") or payload.get("conversation_id") or payload.get("transcript_path")
|
|
24
|
+
return str(value) if value is not None else None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _audit_enabled(env: dict[str, str]) -> bool:
|
|
28
|
+
return _env_value(env, AUDIT_LOG_ENV).lower() in {"1", "true", "yes", "on"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _audit_log_path(env: dict[str, str]) -> Path:
|
|
32
|
+
configured = _env_value(env, AUDIT_LOG_FILE_ENV)
|
|
33
|
+
if configured:
|
|
34
|
+
return Path(configured).expanduser()
|
|
35
|
+
return DEFAULT_AUDIT_LOG_PATH.expanduser()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _safe_error_message(error: BaseException) -> str:
|
|
39
|
+
message = " ".join(str(error).split())
|
|
40
|
+
message = BEARER_RE.sub("Bearer [REDACTED]", message)
|
|
41
|
+
message = SENSITIVE_ASSIGNMENT_RE.sub(lambda m: f"{m.group(1)}=[REDACTED]", message)
|
|
42
|
+
message = _redact_url(message)
|
|
43
|
+
return _truncate_summary(message, 200)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _write_audit_record(env: dict[str, str], record: dict[str, Any]) -> None:
|
|
47
|
+
if not _audit_enabled(env):
|
|
48
|
+
return
|
|
49
|
+
try:
|
|
50
|
+
path = _audit_log_path(env)
|
|
51
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
with path.open("a", encoding="utf-8") as f:
|
|
53
|
+
f.write(json.dumps(record, ensure_ascii=False, sort_keys=True))
|
|
54
|
+
f.write("\n")
|
|
55
|
+
except OSError:
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _new_audit_record(
|
|
60
|
+
*,
|
|
61
|
+
runtime: str,
|
|
62
|
+
event: str | None,
|
|
63
|
+
payload: dict[str, Any],
|
|
64
|
+
summary_mode: SummaryMode,
|
|
65
|
+
lody_settings: LodySettings,
|
|
66
|
+
cwd: Path | None = None,
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
record: dict[str, Any] = {
|
|
69
|
+
"time": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
70
|
+
"runtime": runtime,
|
|
71
|
+
"event": event,
|
|
72
|
+
"hook_event_name": _hook_event_name(payload),
|
|
73
|
+
"status": None,
|
|
74
|
+
"project": project_name(payload, cwd),
|
|
75
|
+
"session_id_hash": _hash_value(_session_id(payload)),
|
|
76
|
+
"dedupe_key_hash": None,
|
|
77
|
+
"summary_mode": summary_mode,
|
|
78
|
+
"title": None,
|
|
79
|
+
"body_len": None,
|
|
80
|
+
}
|
|
81
|
+
lody_values = lody_settings.audit_values()
|
|
82
|
+
if lody_values:
|
|
83
|
+
record["lody"] = lody_values
|
|
84
|
+
return record
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _finish_audit_record(
|
|
88
|
+
env: dict[str, str],
|
|
89
|
+
record: dict[str, Any],
|
|
90
|
+
*,
|
|
91
|
+
status: str,
|
|
92
|
+
notification: Notification | None = None,
|
|
93
|
+
error: BaseException | None = None,
|
|
94
|
+
) -> None:
|
|
95
|
+
record["status"] = status
|
|
96
|
+
if notification is not None:
|
|
97
|
+
record["dedupe_key_hash"] = _hash_value(notification.dedupe_key)
|
|
98
|
+
record["title"] = notification.title
|
|
99
|
+
record["body_len"] = len(notification.body)
|
|
100
|
+
if error is not None:
|
|
101
|
+
record["error_class"] = error.__class__.__name__
|
|
102
|
+
record["error_message"] = _safe_error_message(error)
|
|
103
|
+
_write_audit_record(env, record)
|
bark_agent_hook/cli.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from bark_agent_hook.app import cmd
|
|
9
|
+
from bark_agent_hook.audit import _finish_audit_record, _new_audit_record
|
|
10
|
+
from bark_agent_hook.constants import DEFAULT_SUMMARY_MAX_CHARS
|
|
11
|
+
from bark_agent_hook.installer import _install_for_available_agents, _uninstall_for_available_agents
|
|
12
|
+
from bark_agent_hook.models import AgentOption, Event, GroupModeOption, Runtime, SummaryMode
|
|
13
|
+
from bark_agent_hook.notification import (
|
|
14
|
+
already_sent,
|
|
15
|
+
build_notification,
|
|
16
|
+
resolve_group_mode,
|
|
17
|
+
send_bark,
|
|
18
|
+
skip_notification_reason,
|
|
19
|
+
)
|
|
20
|
+
from bark_agent_hook.output import (
|
|
21
|
+
_found_cli_count,
|
|
22
|
+
_print_install_results,
|
|
23
|
+
_print_uninstall_results,
|
|
24
|
+
_succeeded,
|
|
25
|
+
)
|
|
26
|
+
from bark_agent_hook.runtime import _read_stdin, detect_event, detect_runtime, parse_hook_payload
|
|
27
|
+
from bark_agent_hook.settings import LodySettings
|
|
28
|
+
from bark_agent_hook.summary import extract_summary
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@cmd.command()
|
|
32
|
+
def install(
|
|
33
|
+
agents: list[AgentOption] | None = typer.Option(None, "--agent", help="Agent plugin to install. Repeat for multiple agents. Defaults to all supported agents."),
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Install bark-agent-hook plugins for locally available agents.
|
|
36
|
+
|
|
37
|
+
This command checks for codex, claude, and openclaw CLIs in PATH unless
|
|
38
|
+
one or more --agent options are passed. Missing CLIs are skipped.
|
|
39
|
+
|
|
40
|
+
Installed plugins:
|
|
41
|
+
Codex: bark-agent-hook-codex@bark-agent-hook
|
|
42
|
+
Claude Code: bark-agent-hook@bark-agent-hook --scope user
|
|
43
|
+
OpenClaw: local linked plugin from plugins/bark-agent-hook-openclaw
|
|
44
|
+
"""
|
|
45
|
+
results = _install_for_available_agents(agents)
|
|
46
|
+
_print_install_results(results, Console(highlight=False, width=120))
|
|
47
|
+
if _found_cli_count(results) > 0 and _succeeded(results) == 0:
|
|
48
|
+
raise typer.Exit(1)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@cmd.command()
|
|
52
|
+
def uninstall(
|
|
53
|
+
agents: list[AgentOption] | None = typer.Option(None, "--agent", help="Agent plugin to uninstall. Repeat for multiple agents. Defaults to all supported agents."),
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Uninstall bark-agent-hook plugins for locally available agents."""
|
|
56
|
+
results = _uninstall_for_available_agents(agents)
|
|
57
|
+
_print_uninstall_results(results, Console(highlight=False, width=120))
|
|
58
|
+
if _found_cli_count(results) > 0 and _succeeded(results) == 0:
|
|
59
|
+
raise typer.Exit(1)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@cmd.command()
|
|
63
|
+
def hook(
|
|
64
|
+
runtime: Runtime = typer.Option("auto", "--runtime", help="Hook runtime: codex, claude, openclaw, or auto."),
|
|
65
|
+
event: Event = typer.Option("auto", "--event", help="Notification event override."),
|
|
66
|
+
message: str | None = typer.Option(None, "--message", help="Override short notification body."),
|
|
67
|
+
group_mode: GroupModeOption | None = typer.Option(None, "--group-mode", help="Bark group mode: agent, project, or project-branch."),
|
|
68
|
+
summary_mode: SummaryMode = typer.Option("fixed", "--summary-mode", help="Notification summary mode: fixed or extract."),
|
|
69
|
+
summary_max_chars: int = typer.Option(DEFAULT_SUMMARY_MAX_CHARS, "--summary-max-chars", min=1, help="Maximum extractive summary length."),
|
|
70
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Print notification summary without sending Bark request."),
|
|
71
|
+
no_dedupe: bool = typer.Option(False, "--no-dedupe", help="Disable duplicate suppression."),
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Read hook JSON from stdin and send a best-effort Bark notification."""
|
|
74
|
+
env = dict(os.environ)
|
|
75
|
+
payload = parse_hook_payload(_read_stdin())
|
|
76
|
+
lody_settings = LodySettings()
|
|
77
|
+
resolved_runtime = detect_runtime(runtime, env, payload, lody_settings)
|
|
78
|
+
resolved_event = detect_event(event, payload)
|
|
79
|
+
resolved_group_mode = resolve_group_mode(group_mode, env)
|
|
80
|
+
audit_record = _new_audit_record(runtime=resolved_runtime, event=resolved_event, payload=payload, summary_mode=summary_mode, lody_settings=lody_settings)
|
|
81
|
+
try:
|
|
82
|
+
if resolved_event is None:
|
|
83
|
+
_finish_audit_record(env, audit_record, status="skipped_unsupported_event")
|
|
84
|
+
if dry_run:
|
|
85
|
+
typer.echo("skip: unsupported hook event")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
resolved_message = message
|
|
89
|
+
if resolved_message is None and summary_mode == "extract":
|
|
90
|
+
resolved_message = extract_summary(resolved_runtime, resolved_event, payload, summary_max_chars)
|
|
91
|
+
|
|
92
|
+
skip_reason = skip_notification_reason(resolved_runtime, resolved_event, payload, resolved_message)
|
|
93
|
+
if skip_reason is not None:
|
|
94
|
+
_finish_audit_record(env, audit_record, status=skip_reason)
|
|
95
|
+
if dry_run:
|
|
96
|
+
typer.echo("skip: OpenClaw event has no deliverable reply")
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
notification = build_notification(
|
|
100
|
+
runtime=resolved_runtime,
|
|
101
|
+
event=resolved_event,
|
|
102
|
+
message=resolved_message,
|
|
103
|
+
env=env,
|
|
104
|
+
payload=payload,
|
|
105
|
+
lody_settings=lody_settings,
|
|
106
|
+
group_mode=resolved_group_mode,
|
|
107
|
+
)
|
|
108
|
+
if notification is None:
|
|
109
|
+
_finish_audit_record(env, audit_record, status="skipped_missing_device_key")
|
|
110
|
+
if dry_run:
|
|
111
|
+
typer.echo("skip: BARK_DEVICE_KEY is missing")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
if not no_dedupe and already_sent(notification.dedupe_key, env):
|
|
115
|
+
_finish_audit_record(env, audit_record, status="skipped_duplicate", notification=notification)
|
|
116
|
+
if dry_run:
|
|
117
|
+
typer.echo("skip: duplicate notification")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if dry_run:
|
|
121
|
+
_finish_audit_record(env, audit_record, status="sent", notification=notification)
|
|
122
|
+
output = {"title": notification.title, "body": notification.body, "icon": notification.icon_url, "group": notification.group, "url": notification.bark_url}
|
|
123
|
+
if notification.click_url:
|
|
124
|
+
output["click_url"] = notification.click_url
|
|
125
|
+
typer.echo(json.dumps(output, ensure_ascii=False))
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
send_bark(notification)
|
|
130
|
+
except httpx.HTTPError as e:
|
|
131
|
+
_finish_audit_record(env, audit_record, status="bark_http_error", notification=notification, error=e)
|
|
132
|
+
typer.echo(f"Bark notification failed: {e}", err=True)
|
|
133
|
+
return
|
|
134
|
+
_finish_audit_record(env, audit_record, status="sent", notification=notification)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
_finish_audit_record(env, audit_record, status="hook_exception", error=e)
|
|
137
|
+
typer.echo(f"Bark hook failed: {e}", err=True)
|
|
138
|
+
return
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from bark_agent_hook.models import GroupMode
|
|
7
|
+
|
|
8
|
+
CODEX_ICON_URL = "https://raw.githubusercontent.com/lobehub/lobe-icons/refs/heads/master/packages/static-png/light/codex-color.png"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
CLAUDE_CODE_ICON_URL = "https://raw.githubusercontent.com/lobehub/lobe-icons/refs/heads/master/packages/static-png/light/claudecode-color.png"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
OPENCLAW_ICON_URL = "https://openclaw.ai/apple-touch-icon.png"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LODY_ICON_URL = "https://lody.ai/favicon.ico"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
DEFAULT_MESSAGES: dict[str, str] = {
|
|
21
|
+
"completion": "任务已完成",
|
|
22
|
+
"approval_needed": "需要你审批当前操作",
|
|
23
|
+
"failed": "本轮因错误停止",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
EVENT_LABELS: dict[str, str] = {
|
|
28
|
+
"completion": "Done",
|
|
29
|
+
"approval_needed": "Approval",
|
|
30
|
+
"failed": "Failed",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
MAX_MESSAGE_LENGTH = 80
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
DEFAULT_SUMMARY_MAX_CHARS = 120
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
MAX_TRANSCRIPT_BYTES = 1024 * 1024
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
DEDUP_TTL_SECONDS = 60 * 60
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
HOOK_URL_TEMPLATE_ENV = "AGENT_BARK_NOTIFY_HOOK_URL"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
TITLE_TEMPLATE_ENV = "AGENT_BARK_NOTIFY_TITLE_TEMPLATE"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
DEFAULT_TITLE_TEMPLATE = "[{agent}][{event}][{project}][{branch}][{session}]"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
GROUP_MODE_ENV = "AGENT_BARK_NOTIFY_GROUP_MODE"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
GROUP_MODE_CHOICES: tuple[GroupMode, ...] = ("agent", "project", "project-branch")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
AUDIT_LOG_ENV = "AGENT_BARK_NOTIFY_AUDIT_LOG"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
AUDIT_LOG_FILE_ENV = "AGENT_BARK_NOTIFY_AUDIT_LOG_FILE"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
DEFAULT_AUDIT_LOG_PATH = Path("~/.bark-agent-hook/bark-agent-hook.log")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
SENSITIVE_KEY_RE = re.compile(r"(?i)\b(authorization|cookie|set-cookie|x-api-key|api[_-]?key|token|secret|password|passwd|bearer)\b")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
SENSITIVE_ASSIGNMENT_RE = re.compile(r"(?i)\b([a-z0-9_.-]*(?:token|secret|password|passwd|cookie|authorization|api[_-]?key)[a-z0-9_.-]*)\s*[:=]\s*('[^']*'|\"[^\"]*\"|[^\s,;]+)")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
BEARER_RE = re.compile(r"(?i)\bbearer\s+[a-z0-9._~+/=-]+")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
FENCED_CODE_RE = re.compile(r"```.*?```", re.DOTALL)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
SHELL_PREFIX_RE = re.compile(r"^\s*(?:bash|zsh|sh|fish|python|python3|node|npm|pnpm|yarn|curl|ssh|scp|rsync)\b", re.IGNORECASE)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
OPENCLAW_CONVERSATION_ACCESS_PATCH = '{"plugins":{"entries":{"bark-agent-hook-openclaw":{"hooks":{"allowConversationAccess":true}}}}}'
|
bark_agent_hook/hook.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from bark_agent_hook.app import cmd
|
|
7
|
+
from bark_agent_hook.commands import hook, install, uninstall
|
|
8
|
+
from bark_agent_hook.constants import (
|
|
9
|
+
CLAUDE_CODE_ICON_URL,
|
|
10
|
+
CODEX_ICON_URL,
|
|
11
|
+
LODY_ICON_URL,
|
|
12
|
+
OPENCLAW_ICON_URL,
|
|
13
|
+
)
|
|
14
|
+
from bark_agent_hook.installer import _openclaw_plugin_dir
|
|
15
|
+
|
|
16
|
+
__all__ = (
|
|
17
|
+
"CLAUDE_CODE_ICON_URL",
|
|
18
|
+
"CODEX_ICON_URL",
|
|
19
|
+
"LODY_ICON_URL",
|
|
20
|
+
"OPENCLAW_ICON_URL",
|
|
21
|
+
"_openclaw_plugin_dir",
|
|
22
|
+
"cmd",
|
|
23
|
+
"hook",
|
|
24
|
+
"install",
|
|
25
|
+
"shutil",
|
|
26
|
+
"subprocess",
|
|
27
|
+
"uninstall",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
cmd()
|