feat: openclaw-style secrets (env.vars + \) and per-task model routing
- Replace python-dotenv with config.json env.vars block + \ substitution - Add models section for per-task model routing (heartbeat, subagent, default) - Heartbeat/subagent tasks can use different models/providers than main chat - Remove python-dotenv from dependencies - Update all docs to reflect new config approach - Reorganize docs into project/ and research/ subdirectories
This commit is contained in:
60
config.py
60
config.py
@@ -583,6 +583,7 @@ def save_default_config() -> str:
|
||||
os.makedirs(CONFIG_DIR, exist_ok=True)
|
||||
|
||||
if os.path.isfile(CONFIG_PATH):
|
||||
migrate_config()
|
||||
return CONFIG_PATH
|
||||
|
||||
default = {
|
||||
@@ -689,6 +690,65 @@ def save_default_config() -> str:
|
||||
return CONFIG_PATH
|
||||
|
||||
|
||||
def migrate_config() -> bool:
|
||||
"""Patch an existing config.json with new sections added in later versions.
|
||||
|
||||
Called on every startup (via ``save_default_config``). Only touches the
|
||||
file when keys are actually missing — existing values are never
|
||||
overwritten. Returns ``True`` if the file was modified.
|
||||
"""
|
||||
if not os.path.isfile(CONFIG_PATH):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
return False
|
||||
|
||||
changed = False
|
||||
|
||||
# --- env.vars block (added when secrets moved from .env) ---------------
|
||||
if "env" not in data:
|
||||
data["env"] = {
|
||||
"vars": {
|
||||
"SLACK_BOT_TOKEN": "",
|
||||
"SLACK_APP_TOKEN": "",
|
||||
"TELEGRAM_BOT_TOKEN": "",
|
||||
"DISCORD_BOT_TOKEN": "",
|
||||
"ANTHROPIC_API_KEY": "",
|
||||
"OPENCODE_SERVER_PASSWORD": "",
|
||||
}
|
||||
}
|
||||
changed = True
|
||||
elif "vars" not in data.get("env", {}):
|
||||
data["env"]["vars"] = {}
|
||||
changed = True
|
||||
|
||||
# --- bot_token fields in adapter sections ------------------------------
|
||||
for section, token_key, env_ref in [
|
||||
("slack", "bot_token", "${SLACK_BOT_TOKEN}"),
|
||||
("slack", "app_token", "${SLACK_APP_TOKEN}"),
|
||||
("telegram", "bot_token", "${TELEGRAM_BOT_TOKEN}"),
|
||||
("discord", "bot_token", "${DISCORD_BOT_TOKEN}"),
|
||||
]:
|
||||
if section in data and token_key not in data[section]:
|
||||
data[section][token_key] = env_ref
|
||||
changed = True
|
||||
|
||||
# --- models section (per-task model routing) ---------------------------
|
||||
if "models" not in data:
|
||||
data["models"] = {"heartbeat": None, "subagent": None, "default": None}
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
logger.info(f"Config migrated with new sections: {CONFIG_PATH}")
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
def write_mcp_config(mcp_config: MCPConfig, workspace_dir: str, use_claude: bool) -> None:
|
||||
"""Write MCP server config to the appropriate file for the runtime.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user