Skip to content

Commit 4324a37

Browse files
committed
fix some ci & e2e bugs
1 parent 9201c61 commit 4324a37

16 files changed

+407
-246
lines changed

.detect-secrets.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ exclude_lines:
3636
- "example.com"
3737
- "your-api-key"
3838
- "<token>"
39-
- "<your.*>"
39+
- "(?i)<your.*>"
4040
- "sk-xxx"
4141

4242
# Exclude files

.pre-commit-config.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
# Install: pip install pre-commit && pre-commit install
55
# Update baseline: detect-secrets scan --baseline .secrets.baseline
66

7+
exclude: |
8+
(?x)^(
9+
REFERENCE/.*|
10+
node_modules/.*|
11+
test-results/.*|
12+
playwright-report/.*|
13+
playwright/\\.cache/.*
14+
)$
15+
716
repos:
817
# Secret detection
918
- repo: https://github.com/Yelp/detect-secrets
1019
rev: v1.4.0
1120
hooks:
1221
- id: detect-secrets
1322
args:
14-
- --config
15-
- .detect-secrets.cfg
1623
- --baseline
1724
- .secrets.baseline
1825
exclude: |

api/checkpoints_handler.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Checkpoints API Handlers (R47).
33
Exposes endpoints for listing, creating, retrieving, and deleting workflow checkpoints.
44
"""
5+
56
from __future__ import annotations
67

78
import json
@@ -17,13 +18,23 @@
1718
if __package__ and "." in __package__:
1819
from ..models.schemas import MAX_BODY_SIZE
1920
from ..services.access_control import is_loopback, require_admin_token
20-
from ..services.checkpoints import create_checkpoint, delete_checkpoint, get_checkpoint, list_checkpoints
21+
from ..services.checkpoints import (
22+
create_checkpoint,
23+
delete_checkpoint,
24+
get_checkpoint,
25+
list_checkpoints,
26+
)
2127
from ..services.rate_limit import check_rate_limit
2228
from ..services.request_ip import get_client_ip
2329
else: # pragma: no cover (test-only import mode)
2430
from models.schemas import MAX_BODY_SIZE # type: ignore
2531
from services.access_control import is_loopback, require_admin_token # type: ignore
26-
from services.checkpoints import create_checkpoint, delete_checkpoint, get_checkpoint, list_checkpoints # type: ignore
32+
from services.checkpoints import ( # type: ignore
33+
create_checkpoint,
34+
delete_checkpoint,
35+
get_checkpoint,
36+
list_checkpoints,
37+
)
2738
from services.rate_limit import check_rate_limit # type: ignore
2839
from services.request_ip import get_client_ip # type: ignore
2940

@@ -34,12 +45,17 @@
3445
def _json_resp(data: Dict[str, Any], status: int = 200) -> web.Response:
3546
return web.json_response(data, status=status)
3647

48+
3749
def _remote_admin_allowed() -> bool:
3850
val = (
39-
os.environ.get("OPENCLAW_ALLOW_REMOTE_ADMIN")
40-
or os.environ.get("MOLTBOT_ALLOW_REMOTE_ADMIN")
41-
or ""
42-
).strip().lower()
51+
(
52+
os.environ.get("OPENCLAW_ALLOW_REMOTE_ADMIN")
53+
or os.environ.get("MOLTBOT_ALLOW_REMOTE_ADMIN")
54+
or ""
55+
)
56+
.strip()
57+
.lower()
58+
)
4359
return val in ("1", "true", "yes", "on")
4460

4561

@@ -60,8 +76,9 @@ def _deny_remote_admin_if_needed(request: web.Request) -> web.Response | None:
6076

6177
async def list_checkpoints_handler(request: web.Request) -> web.Response:
6278
"""GET /openclaw/checkpoints"""
63-
if web is None: raise RuntimeError("aiohttp not available")
64-
79+
if web is None:
80+
raise RuntimeError("aiohttp not available")
81+
6582
if not check_rate_limit(request, "admin"):
6683
return _json_resp({"ok": False, "error": "rate_limit_exceeded"}, 429)
6784

@@ -82,47 +99,53 @@ async def list_checkpoints_handler(request: web.Request) -> web.Response:
8299

83100
async def create_checkpoint_handler(request: web.Request) -> web.Response:
84101
"""POST /openclaw/checkpoints"""
85-
if web is None: raise RuntimeError("aiohttp not available")
102+
if web is None:
103+
raise RuntimeError("aiohttp not available")
86104

87105
if not check_rate_limit(request, "admin"):
88106
return _json_resp({"ok": False, "error": "rate_limit_exceeded"}, 429)
89107

90108
# Body Size Check
91109
if request.content_length and request.content_length > MAX_BODY_SIZE:
92-
return _json_resp({"ok": False, "error": "payload_too_large"}, 413)
110+
return _json_resp({"ok": False, "error": "payload_too_large"}, 413)
93111

94112
try:
95113
data = await request.json()
96114
except Exception:
97115
return _json_resp({"ok": False, "error": "invalid_json"}, 400)
98116

117+
# Admin boundary (localhost convenience mode if no token configured)
99118
allowed, error = require_admin_token(request)
100119
if not allowed:
101120
return _json_resp({"ok": False, "error": error or "unauthorized"}, 403)
102121
deny_resp = _deny_remote_admin_if_needed(request)
103122
if deny_resp:
104123
return deny_resp
105124

125+
# Extract info
106126
workflow = data.get("workflow") or data.get("prompt")
107127
name = data.get("name", "Untitled Snapshot")
108128
description = data.get("description", "")
109129

110130
if not workflow or not isinstance(workflow, dict):
111-
return _json_resp({"ok": False, "error": "missing_workflow"}, 400)
131+
return _json_resp({"ok": False, "error": "missing_workflow"}, 400)
112132

113133
try:
114134
meta = create_checkpoint(name, workflow, description)
115135
return _json_resp({"ok": True, "checkpoint": meta}, 201)
116136
except ValueError as e:
117-
return _json_resp({"ok": False, "error": "validation_error", "detail": str(e)}, 400)
137+
return _json_resp(
138+
{"ok": False, "error": "validation_error", "detail": str(e)}, 400
139+
)
118140
except Exception as e:
119141
logger.exception("Create checkpoint failed")
120142
return _json_resp({"ok": False, "error": str(e)}, 500)
121143

122144

123145
async def get_checkpoint_handler(request: web.Request) -> web.Response:
124146
"""GET /openclaw/checkpoints/{id}"""
125-
if web is None: raise RuntimeError("aiohttp not available")
147+
if web is None:
148+
raise RuntimeError("aiohttp not available")
126149

127150
checkpoint_id = request.match_info.get("id")
128151
if not checkpoint_id:
@@ -147,11 +170,12 @@ async def get_checkpoint_handler(request: web.Request) -> web.Response:
147170

148171
async def delete_checkpoint_handler(request: web.Request) -> web.Response:
149172
"""DELETE /openclaw/checkpoints/{id}"""
150-
if web is None: raise RuntimeError("aiohttp not available")
173+
if web is None:
174+
raise RuntimeError("aiohttp not available")
151175

152176
checkpoint_id = request.match_info.get("id")
153177
if not checkpoint_id:
154-
return _json_resp({"ok": False, "error": "missing_id"}, 400)
178+
return _json_resp({"ok": False, "error": "missing_id"}, 400)
155179

156180
allowed, error = require_admin_token(request)
157181
if not allowed:

api/config.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ async def llm_models_handler(request: web.Request) -> web.Response:
238238
provider_override = (request.query.get("provider") or "").strip().lower()
239239
effective, _sources = get_effective_config()
240240
provider = provider_override or (effective.get("provider") or "openai")
241-
241+
242242
# Resolve Base URL (Runtime config > Catalog Default)
243243
# Allows users to override base_url for standard providers (e.g. self-hosted OpenAI compat)
244244
runtime_base_url = (effective.get("base_url") or "").strip()
@@ -268,15 +268,18 @@ async def llm_models_handler(request: web.Request) -> web.Response:
268268
# Priority: Runtime URL -> Info Default
269269
base_url = runtime_base_url if runtime_base_url else info.base_url
270270
if not base_url:
271-
return web.json_response(
272-
{"ok": False, "error": f"No base URL configured for provider '{provider}'."},
271+
return web.json_response(
272+
{
273+
"ok": False,
274+
"error": f"No base URL configured for provider '{provider}'.",
275+
},
273276
status=400,
274277
)
275278

276279
# Cache Key: (provider, base_url)
277280
# This ensures switching base_url (e.g. from OpenAI to local proxy) doesn't use stale cache.
278281
cache_key = (provider, base_url)
279-
282+
280283
# Check Cache
281284
now = time.time()
282285
cached_entry = _MODEL_LIST_CACHE.get(cache_key)
@@ -336,20 +339,26 @@ async def llm_models_handler(request: web.Request) -> web.Response:
336339
body = resp.read(1_000_000)
337340
payload = json.loads(body.decode("utf-8", errors="replace"))
338341
models = _extract_models_from_payload(payload)
339-
342+
340343
# Update Cache
341344
_MODEL_LIST_CACHE[cache_key] = (now, models)
342-
345+
343346
return web.json_response(
344347
{"ok": True, "provider": provider, "models": models, "cached": False}
345348
)
346349
except urllib.error.HTTPError as e:
347350
# Fallback Check
348351
if cached_entry:
349-
ts, models = cached_entry
350-
warning = f"Using cached list (refresh failed: HTTP {e.code} {e.reason})"
351-
return web.json_response(
352-
{"ok": True, "provider": provider, "models": models, "cached": True, "warning": warning}
352+
ts, models = cached_entry
353+
warning = f"Using cached list (refresh failed: HTTP {e.code} {e.reason})"
354+
return web.json_response(
355+
{
356+
"ok": True,
357+
"provider": provider,
358+
"models": models,
359+
"cached": True,
360+
"warning": warning,
361+
}
353362
)
354363
return web.json_response(
355364
{"ok": False, "error": f"HTTP error {e.code}: {e.reason}"}, status=502
@@ -358,10 +367,16 @@ async def llm_models_handler(request: web.Request) -> web.Response:
358367
logger.exception("Failed to fetch model list")
359368
# Fallback Check
360369
if cached_entry:
361-
ts, models = cached_entry
362-
warning = f"Using cached list (refresh failed: {str(e)})"
363-
return web.json_response(
364-
{"ok": True, "provider": provider, "models": models, "cached": True, "warning": warning}
370+
ts, models = cached_entry
371+
warning = f"Using cached list (refresh failed: {str(e)})"
372+
return web.json_response(
373+
{
374+
"ok": True,
375+
"provider": provider,
376+
"models": models,
377+
"cached": True,
378+
"warning": warning,
379+
}
365380
)
366381
return web.json_response({"ok": False, "error": str(e)}, status=500)
367382

api/preflight_handler.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
Exposes POST /openclaw/preflight to run diagnostics on a workflow payload.
55
"""
6+
67
from __future__ import annotations
78

89
import json
@@ -18,13 +19,21 @@
1819
if __package__ and "." in __package__:
1920
from ..models.schemas import MAX_BODY_SIZE
2021
from ..services.access_control import is_loopback, require_admin_token
21-
from ..services.preflight import _get_model_inventory, _get_node_class_mappings, run_preflight_check
22+
from ..services.preflight import (
23+
_get_model_inventory,
24+
_get_node_class_mappings,
25+
run_preflight_check,
26+
)
2227
from ..services.rate_limit import check_rate_limit
2328
from ..services.request_ip import get_client_ip
2429
else: # pragma: no cover (test-only import mode)
2530
from models.schemas import MAX_BODY_SIZE # type: ignore
2631
from services.access_control import is_loopback, require_admin_token # type: ignore
27-
from services.preflight import _get_model_inventory, _get_node_class_mappings, run_preflight_check # type: ignore
32+
from services.preflight import ( # type: ignore
33+
_get_model_inventory,
34+
_get_node_class_mappings,
35+
run_preflight_check,
36+
)
2837
from services.rate_limit import check_rate_limit # type: ignore
2938
from services.request_ip import get_client_ip # type: ignore
3039

@@ -33,10 +42,14 @@
3342

3443
def _remote_admin_allowed() -> bool:
3544
val = (
36-
os.environ.get("OPENCLAW_ALLOW_REMOTE_ADMIN")
37-
or os.environ.get("MOLTBOT_ALLOW_REMOTE_ADMIN")
38-
or ""
39-
).strip().lower()
45+
(
46+
os.environ.get("OPENCLAW_ALLOW_REMOTE_ADMIN")
47+
or os.environ.get("MOLTBOT_ALLOW_REMOTE_ADMIN")
48+
or ""
49+
)
50+
.strip()
51+
.lower()
52+
)
4053
return val in ("1", "true", "yes", "on")
4154

4255

@@ -72,34 +85,42 @@ async def preflight_handler(request: web.Request) -> web.Response:
7285
# Body Size Check
7386
content_type = request.headers.get("Content-Type", "")
7487
if not content_type.startswith("application/json"):
75-
return web.json_response({"ok": False, "error": "unsupported_media_type"}, status=415)
88+
return web.json_response(
89+
{"ok": False, "error": "unsupported_media_type"}, status=415
90+
)
7691

7792
try:
7893
raw_body = await request.content.read(MAX_BODY_SIZE + 1)
7994
if len(raw_body) > MAX_BODY_SIZE:
8095
return web.json_response(
8196
{"ok": False, "error": "payload_too_large"}, status=413
8297
)
83-
data = json.loads(raw_body.decode("utf-8"))
98+
data = json.loads(raw_body)
8499
except Exception:
85100
return web.json_response({"ok": False, "error": "invalid_json"}, status=400)
86101

87102
# Admin boundary (localhost convenience mode if no token configured)
88103
allowed, error = require_admin_token(request)
89104
if not allowed:
90-
return web.json_response({"ok": False, "error": error or "unauthorized"}, status=403)
105+
return web.json_response(
106+
{"ok": False, "error": error or "unauthorized"}, status=403
107+
)
91108
deny_resp = _deny_remote_admin_if_needed(request)
92109
if deny_resp:
93110
return deny_resp
94111

95112
# Extract workflow
96113
# It might be in { "prompt": ... } or root
97114
workflow = data.get("prompt") or data
98-
115+
99116
if not isinstance(workflow, dict):
100117
return web.json_response(
101-
{"ok": False, "error": "invalid_payload", "detail": "Expected JSON object with workflow data"},
102-
status=400
118+
{
119+
"ok": False,
120+
"error": "invalid_payload",
121+
"detail": "Expected JSON object with workflow data",
122+
},
123+
status=400,
103124
)
104125

105126
# Run Diagnostics
@@ -130,24 +151,24 @@ async def inventory_handler(request: web.Request) -> web.Response:
130151
# Admin boundary (localhost convenience mode if no token configured)
131152
allowed, error = require_admin_token(request)
132153
if not allowed:
133-
return web.json_response({"ok": False, "error": error or "unauthorized"}, status=403)
154+
return web.json_response(
155+
{"ok": False, "error": error or "unauthorized"}, status=403
156+
)
134157
deny_resp = _deny_remote_admin_if_needed(request)
135158
if deny_resp:
136159
return deny_resp
137-
160+
138161
try:
139162
# Nodes (Classes only needed)
140163
nodes_map = _get_node_class_mappings()
141164
node_classes = sorted(list(nodes_map.keys()))
142-
165+
143166
# Models
144167
models_map = _get_model_inventory()
145-
146-
return web.json_response({
147-
"ok": True,
148-
"nodes": node_classes,
149-
"models": models_map
150-
})
168+
169+
return web.json_response(
170+
{"ok": True, "nodes": node_classes, "models": models_map}
171+
)
151172
except Exception as e:
152173
logger.exception("Inventory fetch failed")
153174
return web.json_response(

0 commit comments

Comments
 (0)