forked from rookiestar28/ComfyUI-OpenClaw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
165 lines (137 loc) · 5.27 KB
/
config.py
File metadata and controls
165 lines (137 loc) · 5.27 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import logging
import os
import re
import time
from logging.handlers import RotatingFileHandler
from typing import Optional
# Pack metadata
PACK_NAME = "ComfyUI-OpenClaw"
PACK_START_TIME = time.time()
def _read_pyproject_version() -> Optional[str]:
"""
Read version from pyproject.toml ([project].version) as the single source of truth.
Uses a lightweight regex parse to avoid non-stdlib TOML dependencies.
"""
try:
pack_dir = os.path.dirname(os.path.abspath(__file__))
pyproject_path = os.path.join(pack_dir, "pyproject.toml")
if not os.path.exists(pyproject_path):
return None
text = ""
with open(pyproject_path, "r", encoding="utf-8") as f:
text = f.read()
# Prefer stdlib TOML parser if available (Python 3.11+), then fallback to regex.
try:
from tomllib import loads as _toml_loads # type: ignore
except Exception:
_toml_loads = None
if _toml_loads:
try:
data = _toml_loads(text)
ver = data.get("project", {}).get("version")
if ver:
return str(ver).strip() or None
except Exception:
pass
# Find the [project] section and parse `version = "..."` within it.
# IMPORTANT: tolerate BOM/CRLF so the UI version does not silently fall back to 0.1.0.
# This is intentionally conservative to avoid false matches in other sections.
m = re.search(
r"(?ms)^\ufeff?\\[project\\]\\s*(?:[^\\[]*?)^version\\s*=\\s*[\"']([^\"']+)[\"']\\s*$",
text,
)
if not m:
return None
ver = (m.group(1) or "").strip()
return ver or None
except Exception:
return None
# Version: single source of truth is pyproject.toml (line 4 in this repo).
PACK_VERSION = _read_pyproject_version() or "0.1.0"
# Environment variable for the API key
ENV_API_KEY = "OPENCLAW_LLM_API_KEY"
LEGACY_ENV_API_KEY = "MOLTBOT_LLM_API_KEY"
LEGACY2_ENV_API_KEY = "CLAWDBOT_LLM_API_KEY"
# Data directory (R11: use portable state directory)
try:
# Prefer package-relative import (ComfyUI loads custom nodes by file loader)
from .services.state_dir import get_log_path, get_state_dir # type: ignore
except Exception:
try:
# Fallback for unit tests / direct sys.path imports
from services.state_dir import get_log_path, get_state_dir
except Exception:
get_state_dir = None
get_log_path = None
if get_state_dir and get_log_path:
DATA_DIR = get_state_dir()
LOG_FILE = get_log_path()
else:
# Last-resort fallback during early import or if state_dir is unavailable
PACK_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(PACK_DIR, "data")
LOG_FILE = os.path.join(DATA_DIR, "openclaw.log")
class RedactedFormatter(logging.Formatter):
"""
Custom formatter to redact sensitive information (like API keys) from logs.
"""
def __init__(self, sensitive_strings: list[str], fmt=None, datefmt=None, style="%"):
super().__init__(fmt, datefmt, style)
self.sensitive_strings = sensitive_strings
def format(self, record):
original = super().format(record)
for s in self.sensitive_strings:
if s:
original = original.replace(s, "[REDACTED]")
return original
def get_api_key() -> Optional[str]:
"""
Retrieves the LLM API key from environment variables.
Preference order:
1) OPENCLAW_LLM_API_KEY
2) (legacy) MOLTBOT_LLM_API_KEY
3) (legacy) CLAWDBOT_LLM_API_KEY
"""
# Respect explicit empty string overrides by checking env var presence.
if ENV_API_KEY in os.environ:
return os.environ.get(ENV_API_KEY) or None
if LEGACY_ENV_API_KEY in os.environ:
return os.environ.get(LEGACY_ENV_API_KEY) or None
if LEGACY2_ENV_API_KEY in os.environ:
return os.environ.get(LEGACY2_ENV_API_KEY) or None
return None
def setup_logger(name: str = "ComfyUI-OpenClaw") -> logging.Logger:
"""
Sets up a logger with redaction for the API key.
Includes both console and file handlers with rotation.
"""
logger = logging.getLogger(name)
# Only add handler if not already added to avoid duplicates on reload
if not logger.handlers:
api_key = get_api_key()
sensitive = [api_key] if api_key else []
formatter = RedactedFormatter(
sensitive, fmt="[%(name)s] %(levelname)s: %(message)s"
)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# File handler with rotation (5MB, 3 backups)
try:
os.makedirs(DATA_DIR, exist_ok=True)
file_handler = RotatingFileHandler(
LOG_FILE,
maxBytes=5 * 1024 * 1024, # 5MB
backupCount=3,
encoding="utf-8",
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
except Exception:
# If file logging fails, continue with console only
pass
logger.setLevel(logging.INFO)
return logger
# Global config accessor if needed
logger = setup_logger()