tweek 0.1.0__py3-none-any.whl → 0.2.0__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.
- tweek/__init__.py +2 -2
- tweek/_keygen.py +53 -0
- tweek/audit.py +288 -0
- tweek/cli.py +5303 -2396
- tweek/cli_model.py +380 -0
- tweek/config/families.yaml +609 -0
- tweek/config/manager.py +42 -5
- tweek/config/patterns.yaml +1510 -8
- tweek/config/tiers.yaml +161 -11
- tweek/diagnostics.py +71 -2
- tweek/hooks/break_glass.py +163 -0
- tweek/hooks/feedback.py +223 -0
- tweek/hooks/overrides.py +531 -0
- tweek/hooks/post_tool_use.py +472 -0
- tweek/hooks/pre_tool_use.py +1024 -62
- tweek/integrations/openclaw.py +443 -0
- tweek/integrations/openclaw_server.py +385 -0
- tweek/licensing.py +14 -54
- tweek/logging/bundle.py +2 -2
- tweek/logging/security_log.py +56 -13
- tweek/mcp/approval.py +57 -16
- tweek/mcp/proxy.py +18 -0
- tweek/mcp/screening.py +5 -5
- tweek/mcp/server.py +4 -1
- tweek/memory/__init__.py +24 -0
- tweek/memory/queries.py +223 -0
- tweek/memory/safety.py +140 -0
- tweek/memory/schemas.py +80 -0
- tweek/memory/store.py +989 -0
- tweek/platform/__init__.py +4 -4
- tweek/plugins/__init__.py +40 -24
- tweek/plugins/base.py +1 -1
- tweek/plugins/detectors/__init__.py +3 -3
- tweek/plugins/detectors/{moltbot.py → openclaw.py} +30 -27
- tweek/plugins/git_discovery.py +16 -4
- tweek/plugins/git_registry.py +8 -2
- tweek/plugins/git_security.py +21 -9
- tweek/plugins/screening/__init__.py +10 -1
- tweek/plugins/screening/heuristic_scorer.py +477 -0
- tweek/plugins/screening/llm_reviewer.py +14 -6
- tweek/plugins/screening/local_model_reviewer.py +161 -0
- tweek/proxy/__init__.py +38 -37
- tweek/proxy/addon.py +22 -3
- tweek/proxy/interceptor.py +1 -0
- tweek/proxy/server.py +4 -2
- tweek/sandbox/__init__.py +11 -0
- tweek/sandbox/docker_bridge.py +143 -0
- tweek/sandbox/executor.py +9 -6
- tweek/sandbox/layers.py +97 -0
- tweek/sandbox/linux.py +1 -0
- tweek/sandbox/project.py +548 -0
- tweek/sandbox/registry.py +149 -0
- tweek/security/__init__.py +9 -0
- tweek/security/language.py +250 -0
- tweek/security/llm_reviewer.py +1146 -60
- tweek/security/local_model.py +331 -0
- tweek/security/local_reviewer.py +146 -0
- tweek/security/model_registry.py +371 -0
- tweek/security/rate_limiter.py +11 -6
- tweek/security/secret_scanner.py +70 -4
- tweek/security/session_analyzer.py +26 -2
- tweek/skill_template/SKILL.md +200 -0
- tweek/skill_template/__init__.py +0 -0
- tweek/skill_template/cli-reference.md +331 -0
- tweek/skill_template/overrides-reference.md +184 -0
- tweek/skill_template/scripts/__init__.py +0 -0
- tweek/skill_template/scripts/check_installed.py +170 -0
- tweek/skills/__init__.py +38 -0
- tweek/skills/config.py +150 -0
- tweek/skills/fingerprints.py +198 -0
- tweek/skills/guard.py +293 -0
- tweek/skills/isolation.py +469 -0
- tweek/skills/scanner.py +715 -0
- tweek/vault/__init__.py +0 -1
- tweek/vault/cross_platform.py +12 -1
- tweek/vault/keychain.py +87 -29
- tweek-0.2.0.dist-info/METADATA +281 -0
- tweek-0.2.0.dist-info/RECORD +121 -0
- {tweek-0.1.0.dist-info → tweek-0.2.0.dist-info}/entry_points.txt +8 -1
- {tweek-0.1.0.dist-info → tweek-0.2.0.dist-info}/licenses/LICENSE +80 -0
- tweek/integrations/moltbot.py +0 -243
- tweek-0.1.0.dist-info/METADATA +0 -335
- tweek-0.1.0.dist-info/RECORD +0 -85
- {tweek-0.1.0.dist-info → tweek-0.2.0.dist-info}/WHEEL +0 -0
- {tweek-0.1.0.dist-info → tweek-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tweek OpenClaw Integration - Gateway security plugin setup.
|
|
3
|
+
|
|
4
|
+
Detects OpenClaw Gateway, installs the Tweek security plugin, and
|
|
5
|
+
configures skill scanning, tool screening, and output scanning for
|
|
6
|
+
the OpenClaw ecosystem.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import subprocess
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import 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
|
+
OPENCLAW_PLUGIN_NAME = "@tweek/openclaw-plugin"
|
|
22
|
+
|
|
23
|
+
# Scanning server port (separate from Tweek proxy port)
|
|
24
|
+
SCANNER_SERVER_PORT = 9878
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class OpenClawSetupResult:
|
|
29
|
+
"""Result of OpenClaw protection setup."""
|
|
30
|
+
success: bool = False
|
|
31
|
+
openclaw_detected: bool = False
|
|
32
|
+
openclaw_version: Optional[str] = None
|
|
33
|
+
gateway_port: Optional[int] = None
|
|
34
|
+
gateway_running: bool = False
|
|
35
|
+
scanner_port: int = SCANNER_SERVER_PORT
|
|
36
|
+
preset: str = "cautious"
|
|
37
|
+
config_path: Optional[str] = None
|
|
38
|
+
plugin_installed: bool = False
|
|
39
|
+
error: Optional[str] = None
|
|
40
|
+
warnings: list = field(default_factory=list)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def detect_openclaw_installation() -> dict:
|
|
44
|
+
"""
|
|
45
|
+
Detect OpenClaw installation details.
|
|
46
|
+
|
|
47
|
+
Returns dict with:
|
|
48
|
+
installed: bool
|
|
49
|
+
version: str or None
|
|
50
|
+
config_path: Path or None
|
|
51
|
+
gateway_port: int
|
|
52
|
+
process_running: bool
|
|
53
|
+
gateway_active: bool
|
|
54
|
+
skills_dir: Path or None
|
|
55
|
+
"""
|
|
56
|
+
info = {
|
|
57
|
+
"installed": False,
|
|
58
|
+
"version": None,
|
|
59
|
+
"config_path": None,
|
|
60
|
+
"gateway_port": OPENCLAW_DEFAULT_PORT,
|
|
61
|
+
"process_running": False,
|
|
62
|
+
"gateway_active": False,
|
|
63
|
+
"skills_dir": None,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Check npm global installation
|
|
67
|
+
try:
|
|
68
|
+
proc = subprocess.run(
|
|
69
|
+
["npm", "list", "-g", "openclaw", "--json"],
|
|
70
|
+
capture_output=True,
|
|
71
|
+
text=True,
|
|
72
|
+
timeout=10,
|
|
73
|
+
)
|
|
74
|
+
if proc.returncode == 0:
|
|
75
|
+
data = json.loads(proc.stdout)
|
|
76
|
+
deps = data.get("dependencies", {})
|
|
77
|
+
if "openclaw" in deps:
|
|
78
|
+
info["installed"] = True
|
|
79
|
+
info["version"] = deps["openclaw"].get("version")
|
|
80
|
+
except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
# Check which/where
|
|
84
|
+
if not info["installed"]:
|
|
85
|
+
try:
|
|
86
|
+
import os
|
|
87
|
+
cmd = ["which", "openclaw"] if os.name != "nt" else ["where", "openclaw"]
|
|
88
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
|
|
89
|
+
if proc.returncode == 0 and proc.stdout.strip():
|
|
90
|
+
info["installed"] = True
|
|
91
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
# Check for OpenClaw home directory
|
|
95
|
+
if OPENCLAW_HOME.exists():
|
|
96
|
+
info["installed"] = True
|
|
97
|
+
|
|
98
|
+
# Check for skills directory
|
|
99
|
+
if OPENCLAW_SKILLS_DIR.exists():
|
|
100
|
+
info["skills_dir"] = OPENCLAW_SKILLS_DIR
|
|
101
|
+
|
|
102
|
+
# Check for config file and extract port
|
|
103
|
+
if OPENCLAW_CONFIG.exists():
|
|
104
|
+
info["config_path"] = OPENCLAW_CONFIG
|
|
105
|
+
try:
|
|
106
|
+
with open(OPENCLAW_CONFIG) as f:
|
|
107
|
+
config = json.load(f)
|
|
108
|
+
port = config.get("gateway", {}).get("port")
|
|
109
|
+
if port:
|
|
110
|
+
info["gateway_port"] = port
|
|
111
|
+
except (json.JSONDecodeError, IOError):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# Check for running process
|
|
115
|
+
try:
|
|
116
|
+
proc = subprocess.run(
|
|
117
|
+
["pgrep", "-f", "openclaw"],
|
|
118
|
+
capture_output=True,
|
|
119
|
+
text=True,
|
|
120
|
+
timeout=5,
|
|
121
|
+
)
|
|
122
|
+
if proc.returncode == 0 and proc.stdout.strip():
|
|
123
|
+
info["process_running"] = True
|
|
124
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
# Check if gateway port is active
|
|
128
|
+
import socket
|
|
129
|
+
try:
|
|
130
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
131
|
+
s.settimeout(1)
|
|
132
|
+
result = s.connect_ex(("127.0.0.1", info["gateway_port"]))
|
|
133
|
+
info["gateway_active"] = result == 0
|
|
134
|
+
except (socket.error, OSError):
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
return info
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _check_plugin_installed() -> bool:
|
|
141
|
+
"""Check if the Tweek OpenClaw plugin is already installed."""
|
|
142
|
+
try:
|
|
143
|
+
proc = subprocess.run(
|
|
144
|
+
["openclaw", "plugins", "list", "--json"],
|
|
145
|
+
capture_output=True,
|
|
146
|
+
text=True,
|
|
147
|
+
timeout=10,
|
|
148
|
+
)
|
|
149
|
+
if proc.returncode == 0:
|
|
150
|
+
data = json.loads(proc.stdout)
|
|
151
|
+
plugins = data.get("plugins", [])
|
|
152
|
+
return any(
|
|
153
|
+
p.get("name") == OPENCLAW_PLUGIN_NAME
|
|
154
|
+
or p.get("name") == "tweek-security"
|
|
155
|
+
for p in plugins
|
|
156
|
+
)
|
|
157
|
+
except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
|
|
158
|
+
pass
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _install_plugin() -> tuple:
|
|
163
|
+
"""
|
|
164
|
+
Install the Tweek plugin into OpenClaw.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
(success: bool, error: str or None)
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
proc = subprocess.run(
|
|
171
|
+
["openclaw", "plugins", "install", OPENCLAW_PLUGIN_NAME],
|
|
172
|
+
capture_output=True,
|
|
173
|
+
text=True,
|
|
174
|
+
timeout=60,
|
|
175
|
+
)
|
|
176
|
+
if proc.returncode == 0:
|
|
177
|
+
return True, None
|
|
178
|
+
return False, f"Plugin install failed: {proc.stderr.strip()}"
|
|
179
|
+
except subprocess.TimeoutExpired:
|
|
180
|
+
return False, "Plugin install timed out"
|
|
181
|
+
except FileNotFoundError:
|
|
182
|
+
return False, "openclaw CLI not found"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _write_openclaw_config(
|
|
186
|
+
gateway_port: int,
|
|
187
|
+
scanner_port: int,
|
|
188
|
+
preset: str,
|
|
189
|
+
) -> tuple:
|
|
190
|
+
"""
|
|
191
|
+
Write or update the Tweek plugin configuration in openclaw.json.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
(config_path: str or None, error: str or None)
|
|
195
|
+
"""
|
|
196
|
+
config = {}
|
|
197
|
+
if OPENCLAW_CONFIG.exists():
|
|
198
|
+
try:
|
|
199
|
+
with open(OPENCLAW_CONFIG) as f:
|
|
200
|
+
config = json.load(f)
|
|
201
|
+
except (json.JSONDecodeError, IOError):
|
|
202
|
+
config = {}
|
|
203
|
+
|
|
204
|
+
# Build preset-specific settings
|
|
205
|
+
preset_configs = {
|
|
206
|
+
"trusted": {
|
|
207
|
+
"skillGuard": {
|
|
208
|
+
"enabled": True,
|
|
209
|
+
"mode": "fingerprint_only",
|
|
210
|
+
"blockDangerous": False,
|
|
211
|
+
"promptSuspicious": False,
|
|
212
|
+
},
|
|
213
|
+
"toolScreening": {
|
|
214
|
+
"enabled": True,
|
|
215
|
+
"llmReview": False,
|
|
216
|
+
},
|
|
217
|
+
"outputScanning": {
|
|
218
|
+
"enabled": False,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
"cautious": {
|
|
222
|
+
"skillGuard": {
|
|
223
|
+
"enabled": True,
|
|
224
|
+
"mode": "auto",
|
|
225
|
+
"blockDangerous": True,
|
|
226
|
+
"promptSuspicious": True,
|
|
227
|
+
},
|
|
228
|
+
"toolScreening": {
|
|
229
|
+
"enabled": True,
|
|
230
|
+
"llmReview": True,
|
|
231
|
+
"tiers": {
|
|
232
|
+
"bash": "dangerous",
|
|
233
|
+
"file_write": "risky",
|
|
234
|
+
"web_fetch": "risky",
|
|
235
|
+
"mcp_tool": "default",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
"outputScanning": {
|
|
239
|
+
"enabled": True,
|
|
240
|
+
"secretDetection": True,
|
|
241
|
+
"exfiltrationDetection": True,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
"paranoid": {
|
|
245
|
+
"skillGuard": {
|
|
246
|
+
"enabled": True,
|
|
247
|
+
"mode": "manual",
|
|
248
|
+
"blockDangerous": True,
|
|
249
|
+
"promptSuspicious": True,
|
|
250
|
+
},
|
|
251
|
+
"toolScreening": {
|
|
252
|
+
"enabled": True,
|
|
253
|
+
"llmReview": True,
|
|
254
|
+
"tiers": {
|
|
255
|
+
"bash": "dangerous",
|
|
256
|
+
"file_write": "dangerous",
|
|
257
|
+
"web_fetch": "dangerous",
|
|
258
|
+
"mcp_tool": "risky",
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
"outputScanning": {
|
|
262
|
+
"enabled": True,
|
|
263
|
+
"secretDetection": True,
|
|
264
|
+
"exfiltrationDetection": True,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
preset_config = preset_configs.get(preset, preset_configs["cautious"])
|
|
270
|
+
|
|
271
|
+
# Merge into existing config
|
|
272
|
+
plugins = config.setdefault("plugins", {})
|
|
273
|
+
entries = plugins.setdefault("entries", {})
|
|
274
|
+
entries["tweek"] = {
|
|
275
|
+
"enabled": True,
|
|
276
|
+
"config": {
|
|
277
|
+
"preset": preset,
|
|
278
|
+
"scannerPort": scanner_port,
|
|
279
|
+
**preset_config,
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
OPENCLAW_HOME.mkdir(parents=True, exist_ok=True)
|
|
285
|
+
with open(OPENCLAW_CONFIG, "w") as f:
|
|
286
|
+
json.dump(config, f, indent=2)
|
|
287
|
+
return str(OPENCLAW_CONFIG), None
|
|
288
|
+
except IOError as e:
|
|
289
|
+
return None, f"Failed to write config: {e}"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def setup_openclaw_protection(
|
|
293
|
+
port: Optional[int] = None,
|
|
294
|
+
preset: str = "cautious",
|
|
295
|
+
skip_plugin_install: bool = False,
|
|
296
|
+
) -> OpenClawSetupResult:
|
|
297
|
+
"""
|
|
298
|
+
Configure Tweek to protect OpenClaw Gateway.
|
|
299
|
+
|
|
300
|
+
This is the main entry point for the Tweek-first installation path.
|
|
301
|
+
Detects OpenClaw, installs the plugin, writes configuration, and
|
|
302
|
+
prepares the scanning server.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
port: Override OpenClaw gateway port (default: auto-detect)
|
|
306
|
+
preset: Security preset to apply (paranoid, cautious, trusted)
|
|
307
|
+
skip_plugin_install: Skip npm plugin install (for ClawHub-first path)
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
OpenClawSetupResult with setup details
|
|
311
|
+
"""
|
|
312
|
+
result = OpenClawSetupResult(preset=preset)
|
|
313
|
+
|
|
314
|
+
# 1. Detect OpenClaw
|
|
315
|
+
openclaw = detect_openclaw_installation()
|
|
316
|
+
result.openclaw_detected = openclaw["installed"]
|
|
317
|
+
result.openclaw_version = openclaw["version"]
|
|
318
|
+
|
|
319
|
+
if not openclaw["installed"]:
|
|
320
|
+
result.error = "OpenClaw not detected on this system"
|
|
321
|
+
return result
|
|
322
|
+
|
|
323
|
+
# 2. Resolve gateway port
|
|
324
|
+
if port is not None:
|
|
325
|
+
result.gateway_port = port
|
|
326
|
+
else:
|
|
327
|
+
result.gateway_port = openclaw["gateway_port"]
|
|
328
|
+
|
|
329
|
+
result.gateway_running = openclaw["gateway_active"]
|
|
330
|
+
|
|
331
|
+
# 3. Install plugin (unless skipped for ClawHub-first path)
|
|
332
|
+
if not skip_plugin_install:
|
|
333
|
+
if _check_plugin_installed():
|
|
334
|
+
result.plugin_installed = True
|
|
335
|
+
result.warnings.append("Tweek plugin already installed in OpenClaw")
|
|
336
|
+
else:
|
|
337
|
+
success, error = _install_plugin()
|
|
338
|
+
if success:
|
|
339
|
+
result.plugin_installed = True
|
|
340
|
+
else:
|
|
341
|
+
result.warnings.append(f"Plugin install: {error}")
|
|
342
|
+
result.warnings.append(
|
|
343
|
+
"You can install manually: "
|
|
344
|
+
f"openclaw plugins install {OPENCLAW_PLUGIN_NAME}"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# 4. Check scanner port availability
|
|
348
|
+
import socket
|
|
349
|
+
try:
|
|
350
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
351
|
+
s.settimeout(1)
|
|
352
|
+
if s.connect_ex(("127.0.0.1", SCANNER_SERVER_PORT)) == 0:
|
|
353
|
+
result.warnings.append(
|
|
354
|
+
f"Port {SCANNER_SERVER_PORT} is already in use. "
|
|
355
|
+
"Scanner server may need a different port."
|
|
356
|
+
)
|
|
357
|
+
except (socket.error, OSError):
|
|
358
|
+
pass
|
|
359
|
+
|
|
360
|
+
# 5. Write OpenClaw config
|
|
361
|
+
config_path, error = _write_openclaw_config(
|
|
362
|
+
gateway_port=result.gateway_port,
|
|
363
|
+
scanner_port=result.scanner_port,
|
|
364
|
+
preset=preset,
|
|
365
|
+
)
|
|
366
|
+
if error:
|
|
367
|
+
result.error = error
|
|
368
|
+
return result
|
|
369
|
+
result.config_path = config_path
|
|
370
|
+
|
|
371
|
+
# 6. Update Tweek's own config to know about OpenClaw
|
|
372
|
+
tweek_dir = Path.home() / ".tweek"
|
|
373
|
+
tweek_dir.mkdir(parents=True, exist_ok=True)
|
|
374
|
+
tweek_config_path = tweek_dir / "config.yaml"
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
import yaml
|
|
378
|
+
except ImportError:
|
|
379
|
+
yaml = None
|
|
380
|
+
|
|
381
|
+
tweek_config = {}
|
|
382
|
+
if tweek_config_path.exists():
|
|
383
|
+
try:
|
|
384
|
+
if yaml:
|
|
385
|
+
with open(tweek_config_path) as f:
|
|
386
|
+
tweek_config = yaml.safe_load(f) or {}
|
|
387
|
+
else:
|
|
388
|
+
tweek_config = {}
|
|
389
|
+
except Exception:
|
|
390
|
+
tweek_config = {}
|
|
391
|
+
|
|
392
|
+
tweek_config["openclaw"] = {
|
|
393
|
+
"enabled": True,
|
|
394
|
+
"gateway_port": result.gateway_port,
|
|
395
|
+
"scanner_port": result.scanner_port,
|
|
396
|
+
"preset": preset,
|
|
397
|
+
"plugin_installed": result.plugin_installed,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
try:
|
|
401
|
+
if yaml:
|
|
402
|
+
with open(tweek_config_path, "w") as f:
|
|
403
|
+
yaml.dump(tweek_config, f, default_flow_style=False)
|
|
404
|
+
else:
|
|
405
|
+
# Manual YAML writing as fallback
|
|
406
|
+
existing_lines = []
|
|
407
|
+
if tweek_config_path.exists():
|
|
408
|
+
existing_content = tweek_config_path.read_text()
|
|
409
|
+
# Remove any existing openclaw section
|
|
410
|
+
in_openclaw = False
|
|
411
|
+
for line in existing_content.splitlines():
|
|
412
|
+
if line.startswith("openclaw:"):
|
|
413
|
+
in_openclaw = True
|
|
414
|
+
continue
|
|
415
|
+
if in_openclaw and (line.startswith(" ") or not line.strip()):
|
|
416
|
+
continue
|
|
417
|
+
in_openclaw = False
|
|
418
|
+
existing_lines.append(line)
|
|
419
|
+
|
|
420
|
+
openclaw_lines = [
|
|
421
|
+
"openclaw:",
|
|
422
|
+
" enabled: true",
|
|
423
|
+
f" gateway_port: {result.gateway_port}",
|
|
424
|
+
f" scanner_port: {result.scanner_port}",
|
|
425
|
+
f" preset: {preset}",
|
|
426
|
+
f" plugin_installed: {'true' if result.plugin_installed else 'false'}",
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
all_lines = existing_lines + openclaw_lines
|
|
430
|
+
tweek_config_path.write_text("\n".join(all_lines) + "\n")
|
|
431
|
+
except Exception as e:
|
|
432
|
+
result.warnings.append(f"Could not update Tweek config: {e}")
|
|
433
|
+
|
|
434
|
+
# 7. Apply security preset
|
|
435
|
+
try:
|
|
436
|
+
from tweek.config.manager import ConfigManager
|
|
437
|
+
cfg = ConfigManager()
|
|
438
|
+
cfg.apply_preset(preset)
|
|
439
|
+
except Exception as e:
|
|
440
|
+
result.warnings.append(f"Could not apply preset: {e}")
|
|
441
|
+
|
|
442
|
+
result.success = True
|
|
443
|
+
return result
|