-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhooks.py
More file actions
147 lines (121 loc) · 4.97 KB
/
hooks.py
File metadata and controls
147 lines (121 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""Plugin lifecycle hooks for the Telegram Integration plugin.
Called by Agent Zero's plugin system during install, uninstall, and update.
See: helpers/plugins.py -> call_plugin_hook()
"""
import logging
import os
import subprocess
import sys
from pathlib import Path
logger = logging.getLogger("telegram_hooks")
def _get_plugin_dir() -> Path:
"""Return the directory this hooks.py lives in."""
return Path(__file__).parent.resolve()
def _get_a0_root() -> Path:
"""Detect A0 root directory."""
if Path("/a0/plugins").is_dir():
return Path("/a0")
if Path("/git/agent-zero/plugins").is_dir():
return Path("/git/agent-zero")
return Path("/a0")
def _find_python() -> str:
"""Find the appropriate Python interpreter."""
candidates = ["/opt/venv-a0/bin/python3", sys.executable, "python3"]
for c in candidates:
if os.path.isfile(c) and os.access(c, os.X_OK):
return c
return "python3"
def install(**kwargs):
"""Post-install hook: set up data dir, deps, skills, toggle."""
plugin_dir = _get_plugin_dir()
a0_root = _get_a0_root()
plugin_name = "telegram"
logger.info("Running post-install hook...")
# 1. Enable plugin
toggle = plugin_dir / ".toggle-1"
if not toggle.exists():
toggle.touch()
logger.info("Created %s", toggle)
# 2. Create data directory with restrictive permissions
data_dir = plugin_dir / "data"
data_dir.mkdir(exist_ok=True)
os.chmod(str(data_dir), 0o700)
# 3. Pre-create config.json with restrictive permissions (0o600).
# The framework's write_file() preserves permissions on existing files,
# so subsequent saves via the settings UI will keep 0o600.
config_file = plugin_dir / "config.json"
if not config_file.exists():
import json
fd = os.open(str(config_file), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "w") as f:
json.dump({}, f)
logger.info("Created config.json with 0o600 permissions")
# 4. Install skills
skills_src = plugin_dir / "skills"
skills_dst = a0_root / "usr" / "skills"
if skills_src.is_dir():
for skill_dir in skills_src.iterdir():
if skill_dir.is_dir():
target = skills_dst / skill_dir.name
target.mkdir(parents=True, exist_ok=True)
for f in skill_dir.iterdir():
dest = target / f.name
if f.is_file():
dest.write_bytes(f.read_bytes())
logger.info("Installed skill: %s", skill_dir.name)
# 5. Install Python dependencies via initialize.py
init_script = plugin_dir / "initialize.py"
if init_script.is_file():
python = _find_python()
try:
subprocess.run(
[python, str(init_script)],
check=True,
capture_output=True,
text=True,
timeout=120,
)
logger.info("Dependencies installed")
except subprocess.CalledProcessError as e:
logger.warning("Dependency install failed: %s", e.stderr[:200])
except subprocess.TimeoutExpired:
logger.warning("Dependency install timed out")
# 6. Mirror to /git/agent-zero if running in /a0 runtime
if str(a0_root) == "/a0" and Path("/git/agent-zero/usr").is_dir():
git_plugin = Path("/git/agent-zero/usr/plugins") / plugin_name
if not git_plugin.exists():
try:
import shutil
shutil.copytree(str(plugin_dir), str(git_plugin))
except Exception:
pass
logger.info("Post-install hook complete")
def save_plugin_config(settings: dict, **kwargs) -> dict:
"""Called before the framework writes config.json.
If allow_elevated is enabled and no auth_key exists, generate one
immediately so the key is available in the UI without a bridge restart.
"""
bridge = settings.get("chat_bridge", {})
if bridge.get("allow_elevated", False) and not bridge.get("auth_key", ""):
try:
from usr.plugins.telegram.helpers.sanitize import generate_auth_key
bridge["auth_key"] = generate_auth_key()
settings["chat_bridge"] = bridge
logger.info("Auto-generated auth key on config save")
except Exception:
pass # Non-fatal; _get_auth_key() fallback still exists
return settings
def uninstall(**kwargs):
"""Pre-uninstall hook: clean up skills, bridge runner."""
a0_root = _get_a0_root()
plugin_name = "telegram"
logger.info("Running uninstall hook...")
# Remove skills
skills_dst = a0_root / "usr" / "skills"
for skill_name in ["telegram-chat", "telegram-communicate", "telegram-research"]:
skill_dir = skills_dst / skill_name
if skill_dir.is_dir():
import shutil
shutil.rmtree(str(skill_dir))
logger.info("Removed skill: %s", skill_name)
logger.info("Uninstall hook complete")