update shell_env

This commit is contained in:
朱潮 2026-03-17 10:09:48 +08:00
parent 8c0e0cf187
commit a05da928f0
2 changed files with 82 additions and 2 deletions

View File

@ -10,6 +10,7 @@ import secrets
import os import os
import shutil import shutil
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, List from typing import Optional, List
from fastapi import APIRouter, HTTPException, Header from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel from pydantic import BaseModel
@ -1803,6 +1804,23 @@ async def get_bot_settings(bot_uuid: str, authorization: Optional[str] = Header(
elif not dataset_ids: elif not dataset_ids:
dataset_ids = None dataset_ids = None
# 扫描 skills 需要的环境变量并合并到 shell_env
shell_env = settings.get('shell_env') or {}
skills_list = settings.get('skills')
if skills_list:
from utils.multi_project_manager import scan_skill_env_keys
# skills 可能是逗号分隔的字符串,需要拆分成列表
if isinstance(skills_list, str):
skills_names = [s.strip() for s in skills_list.split(',') if s.strip()]
else:
skills_names = skills_list
required_keys = scan_skill_env_keys(bot_uuid, skills_names, Path("projects"))
for key in required_keys:
if key not in shell_env:
shell_env[key] = ''
# 清理不在 skill 所需变量中且值为空的环境变量
shell_env = {k: v for k, v in shell_env.items() if v or k in required_keys}
return BotSettingsResponse( return BotSettingsResponse(
bot_id=str(bot_id), bot_id=str(bot_id),
name=bot_name, name=bot_name,
@ -1818,8 +1836,8 @@ async def get_bot_settings(bot_uuid: str, authorization: Optional[str] = Header(
enable_memori=settings.get('enable_memori', False), enable_memori=settings.get('enable_memori', False),
enable_thinking=settings.get('enable_thinking', False), enable_thinking=settings.get('enable_thinking', False),
tool_response=settings.get('tool_response', False), tool_response=settings.get('tool_response', False),
skills=settings.get('skills'), skills=skills_list,
shell_env=settings.get('shell_env'), shell_env=shell_env,
is_published=is_published if is_published else False, is_published=is_published if is_published else False,
is_owner=is_owner, is_owner=is_owner,
copied_from=str(copied_from) if copied_from else None, copied_from=str(copied_from) if copied_from else None,

View File

@ -4,6 +4,7 @@
""" """
import os import os
import re
import shutil import shutil
import json import json
import logging import logging
@ -426,3 +427,64 @@ def _extract_skills_to_robot(bot_id: str, skills: List[str], project_path: Path)
logger.info(f" Copied: {source_dir} -> {target_dir}") logger.info(f" Copied: {source_dir} -> {target_dir}")
except Exception as e: except Exception as e:
logger.error(f" Failed to copy {source_dir}: {e}") logger.error(f" Failed to copy {source_dir}: {e}")
_COMMON_ENV_KEYS = frozenset({
'TMPDIR', 'PATH', 'HOME', 'USER', 'SHELL', 'LANG', 'TERM',
'PWD', 'OLDPWD', 'NODE_ENV',
})
_ENV_PATTERNS = [
re.compile(r'process\.env\.(\w+)'),
re.compile(r'os\.getenv\([\'"](\w+)'),
re.compile(r'os\.environ\.get\([\'"](\w+)'),
re.compile(r'os\.environ\[[\'"](\w+)'),
]
_SCAN_EXTENSIONS = {'.js', '.py', '.ts', '.sh', '.md', '.jsx', '.tsx', '.mjs', '.cjs'}
def scan_skill_env_keys(bot_id: str, skills: List[str], project_path: Path) -> set:
"""
扫描 skills 目录下所有文件提取引用的环境变量 KEY
Args:
bot_id: 机器人 ID
skills: 技能名称列表
project_path: 项目路径 Path("projects")
Returns:
set: 环境变量 KEY 集合排除通用变量
"""
skills_source_dirs = [
project_path / "uploads" / bot_id / "skills",
Path("skills"),
]
env_keys = set()
for skill in skills:
source_dir = None
for base_dir in skills_source_dirs:
candidate = base_dir / skill
if candidate.exists():
source_dir = candidate
break
if source_dir is None or not source_dir.exists():
continue
for file_path in source_dir.rglob('*'):
if not file_path.is_file():
continue
if file_path.suffix.lower() not in _SCAN_EXTENSIONS:
continue
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
except Exception:
continue
for pattern in _ENV_PATTERNS:
env_keys.update(pattern.findall(content))
env_keys -= _COMMON_ENV_KEYS
return env_keys