tweek 0.4.1__py3-none-any.whl → 0.4.2__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.
Files changed (37) hide show
  1. tweek/__init__.py +1 -1
  2. tweek/cli_core.py +23 -6
  3. tweek/cli_install.py +361 -91
  4. tweek/cli_uninstall.py +119 -36
  5. tweek/config/families.yaml +13 -0
  6. tweek/config/models.py +31 -3
  7. tweek/config/patterns.yaml +126 -2
  8. tweek/diagnostics.py +124 -1
  9. tweek/hooks/break_glass.py +70 -47
  10. tweek/hooks/overrides.py +19 -1
  11. tweek/hooks/post_tool_use.py +6 -2
  12. tweek/hooks/pre_tool_use.py +19 -2
  13. tweek/hooks/wrapper_post_tool_use.py +121 -0
  14. tweek/hooks/wrapper_pre_tool_use.py +121 -0
  15. tweek/integrations/openclaw.py +70 -60
  16. tweek/integrations/openclaw_detection.py +140 -0
  17. tweek/integrations/openclaw_server.py +359 -86
  18. tweek/logging/security_log.py +22 -0
  19. tweek/memory/safety.py +7 -3
  20. tweek/memory/store.py +31 -10
  21. tweek/plugins/base.py +9 -1
  22. tweek/plugins/detectors/openclaw.py +31 -92
  23. tweek/plugins/screening/heuristic_scorer.py +12 -1
  24. tweek/plugins/screening/local_model_reviewer.py +9 -0
  25. tweek/security/language.py +2 -1
  26. tweek/security/llm_reviewer.py +45 -18
  27. tweek/security/local_model.py +21 -0
  28. tweek/security/model_registry.py +2 -2
  29. tweek/security/rate_limiter.py +99 -1
  30. tweek/skills/guard.py +30 -7
  31. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/METADATA +1 -1
  32. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/RECORD +37 -34
  33. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/WHEEL +0 -0
  34. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/entry_points.txt +0 -0
  35. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/licenses/LICENSE +0 -0
  36. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/licenses/NOTICE +0 -0
  37. {tweek-0.4.1.dist-info → tweek-0.4.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ """
2
+ Shared OpenClaw detection utilities.
3
+
4
+ Centralizes OpenClaw detection logic used by both the integrations module
5
+ and the detector plugin to eliminate code duplication.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import socket
11
+ import subprocess
12
+ from pathlib import Path
13
+ from typing import Any, Dict, Optional
14
+
15
+
16
+ # OpenClaw default paths and ports
17
+ OPENCLAW_DEFAULT_PORT = 18789
18
+ OPENCLAW_HOME = Path.home() / ".openclaw"
19
+ OPENCLAW_CONFIG = OPENCLAW_HOME / "openclaw.json"
20
+ OPENCLAW_SKILLS_DIR = OPENCLAW_HOME / "workspace" / "skills"
21
+
22
+
23
+ def check_npm_installation() -> Optional[Dict[str, str]]:
24
+ """Check if openclaw is installed via npm.
25
+
26
+ Returns:
27
+ Dict with 'version' and/or 'path' keys if found, None otherwise.
28
+ """
29
+ # Try npm list -g
30
+ try:
31
+ proc = subprocess.run(
32
+ ["npm", "list", "-g", "openclaw", "--json"],
33
+ capture_output=True,
34
+ text=True,
35
+ timeout=10,
36
+ )
37
+ if proc.returncode == 0:
38
+ data = json.loads(proc.stdout)
39
+ deps = data.get("dependencies", {})
40
+ if "openclaw" in deps:
41
+ return {
42
+ "version": deps["openclaw"].get("version", "unknown"),
43
+ "path": data.get("path", ""),
44
+ }
45
+ except subprocess.TimeoutExpired:
46
+ pass
47
+ except json.JSONDecodeError:
48
+ pass
49
+ except FileNotFoundError:
50
+ pass
51
+
52
+ # Fallback: try which/where
53
+ try:
54
+ cmd = ["which", "openclaw"] if os.name != "nt" else ["where", "openclaw"]
55
+ proc = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
56
+ if proc.returncode == 0 and proc.stdout.strip():
57
+ return {"path": proc.stdout.strip().split("\n")[0]}
58
+ except (subprocess.TimeoutExpired, FileNotFoundError):
59
+ pass
60
+
61
+ return None
62
+
63
+
64
+ def check_running_process() -> Optional[Dict[str, Any]]:
65
+ """Check if an openclaw process is running.
66
+
67
+ Returns:
68
+ Dict with process info if found, None otherwise.
69
+ On Unix: may contain 'pid' key.
70
+ On Windows: contains 'running': True.
71
+ """
72
+ try:
73
+ if os.name == "nt":
74
+ proc = subprocess.run(
75
+ ["tasklist", "/FI", "IMAGENAME eq node.exe", "/FO", "CSV"],
76
+ capture_output=True,
77
+ text=True,
78
+ timeout=10,
79
+ )
80
+ if "openclaw" in proc.stdout.lower():
81
+ return {"running": True}
82
+ else:
83
+ proc = subprocess.run(
84
+ ["pgrep", "-f", "openclaw"],
85
+ capture_output=True,
86
+ text=True,
87
+ timeout=5,
88
+ )
89
+ if proc.returncode == 0 and proc.stdout.strip():
90
+ pids = proc.stdout.strip().split("\n")
91
+ return {"pid": pids[0]}
92
+
93
+ # Also check for node process with openclaw
94
+ proc = subprocess.run(
95
+ ["pgrep", "-af", "node.*openclaw"],
96
+ capture_output=True,
97
+ text=True,
98
+ timeout=5,
99
+ )
100
+ if proc.returncode == 0 and proc.stdout.strip():
101
+ return {"running": True}
102
+
103
+ except (subprocess.TimeoutExpired, FileNotFoundError):
104
+ pass
105
+
106
+ return None
107
+
108
+
109
+ def check_gateway_active(port: int) -> bool:
110
+ """Check if OpenClaw gateway is listening on port.
111
+
112
+ Args:
113
+ port: TCP port number to check.
114
+
115
+ Returns:
116
+ True if a service is listening on the port.
117
+ """
118
+ try:
119
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
120
+ sock.settimeout(1)
121
+ result = sock.connect_ex(("127.0.0.1", port))
122
+ return result == 0
123
+ except (socket.error, OSError):
124
+ return False
125
+
126
+
127
+ def read_config_port() -> Optional[int]:
128
+ """Read the gateway port from openclaw.json config.
129
+
130
+ Returns:
131
+ The configured port, or None if not found.
132
+ """
133
+ if not OPENCLAW_CONFIG.exists():
134
+ return None
135
+ try:
136
+ with open(OPENCLAW_CONFIG) as f:
137
+ config = json.load(f)
138
+ return config.get("gateway", {}).get("port")
139
+ except (json.JSONDecodeError, IOError):
140
+ return None