feat: 将 47 个 Operit TypeScript 脚本全面翻译为 Python

- 新增 operit_runtime.py: 跨平台 Python 运行时
  - 实现 Tools.System/Files/Net/UI/Memory/FFmpeg/Workflow/Chat/SoftwareSettings/Tasker
  - 实现 OkHttp 链式 HTTP 客户端兼容层
  - 实现 complete()/exports/console 全局对象
  - 实现 OperitScriptLoader 脚本加载器
  - 支持 Windows/macOS/Linux 三平台

- 新增 operit_py/ 目录: 47 个 Python 翻译脚本
  - 搜索类: duckduckgo, google_search, tavily, zhipu_search, various_search, crossref
  - 绘图类: openai_draw, qwen_draw, xai_draw, minimax_draw, siliconflow_draw, zhipu_draw, nanobanana_draw
  - 媒体类: ffmpeg, file_converter, douyin_download, bilibili_tools, jmcomic
  - 文件类: extended_file_tools, hex_editor, reader, beeimg_image_uploader
  - 网络类: extended_http_tools, network_test
  - 自动化类: browser, automatic_ui_base, automatic_ui_subagent, automatic_bilibili_assistant, automatic_xiaohongshu_assistant, automatic_baidu_map_assistant
  - 系统类: system_tools, super_admin, daily_life, operit_editor
  - 对话类: ai_chat, extended_chat, qq_intelligent
  - 工作流类: workflow, vflow_trigger, tasker
  - 工具类: time, code_runner, quick_start, extended_memory_tools, baidu_map, 12306, pdf_vision_parser

- 测试结果: 47/47 语法检查通过, 38 脚本加载成功, 280 个工具注册
- Android 专属功能已降级处理(保留接口,返回不支持提示)
This commit is contained in:
shuiyu1123 committed 2026-06-23 12:27:18 +00:00
1 parent 6fa759665d
commit dc77dfd304
48 files changed
+22273

No files matched your search

+485
View File
@@ -0,0 +1,485 @@
# METADATA
# {
# "name": "code_reader",
# "display_name": {"zh": "代码读取器", "en": "Code Reader"},
# "description": {"zh": "提供代码匹配抓取功能", "en": "Provide code searching and extraction utilities."},
# "enabledByDefault": True,
# "category": "File",
# "tools": [
# {"name": "search_code_in_folder", "description": {"zh": "在文件夹中搜索匹配的代码", "en": "Search for matching code in a folder."}, "parameters": [
# {"name": "folder_path", "description": {"zh": "要搜索的文件夹路径", "en": "Folder path to search."}, "type": "string", "required": True},
# {"name": "pattern", "description": {"zh": "要搜索的代码模式(字符串或正则表达式)", "en": "Pattern to search for (string or regular expression)."}, "type": "string", "required": True},
# {"name": "file_extensions", "description": {"zh": "要搜索的文件扩展名列表(如 \".ts,.js\")", "en": "File extension list to include (e.g. \".ts,.js\")."}, "type": "string", "required": False},
# {"name": "recursive", "description": {"zh": "是否递归搜索子文件夹(true/false)", "en": "Whether to search subfolders recursively (true/false)."}, "type": "string", "required": False}
# ]},
# {"name": "extract_functions", "description": {"zh": "提取文件夹中的所有函数定义", "en": "Extract all function definitions from a folder."}, "parameters": [
# {"name": "folder_path", "description": {"zh": "要搜索的文件夹路径", "en": "Folder path to search."}, "type": "string", "required": True},
# {"name": "function_name_pattern", "description": {"zh": "函数名匹配模式(可选)", "en": "Function name matching pattern (optional)."}, "type": "string", "required": False},
# {"name": "file_extensions", "description": {"zh": "要搜索的文件扩展名列表(如 \".ts,.js\")", "en": "File extension list to include (e.g. \".ts,.js\")."}, "type": "string", "required": False},
# {"name": "recursive", "description": {"zh": "是否递归搜索子文件夹(true/false)", "en": "Whether to search subfolders recursively (true/false)."}, "type": "string", "required": False}
# ]},
# {"name": "extract_function_block", "description": {"zh": "提取特定函数的代码块", "en": "Extract the code block for a specific function."}, "parameters": [
# {"name": "file_path", "description": {"zh": "要读取的文件路径", "en": "File path to read."}, "type": "string", "required": True},
# {"name": "function_name", "description": {"zh": "要提取的函数名称", "en": "Function name to extract."}, "type": "string", "required": True},
# {"name": "include_line_numbers", "description": {"zh": "是否在结果中包含行号(true/false)", "en": "Whether to include line numbers in the result (true/false)."}, "type": "string", "required": False}
# ]},
# {"name": "find_code_blocks", "description": {"zh": "查找文件中的代码块", "en": "Find code blocks in a file."}, "parameters": [
# {"name": "file_path", "description": {"zh": "要读取的文件路径", "en": "File path to read."}, "type": "string", "required": True},
# {"name": "block_pattern", "description": {"zh": "代码块匹配模式(如类定义、接口等)", "en": "Code block matching pattern (e.g., class definition, interface, etc.)."}, "type": "string", "required": True},
# {"name": "include_line_numbers", "description": {"zh": "是否在结果中包含行号(true/false)", "en": "Whether to include line numbers in the result (true/false)."}, "type": "string", "required": False}
# ]},
# {"name": "read_regex_matches", "description": {"zh": "读取文件中匹配正则表达式的内容", "en": "Read content in a file that matches a regular expression."}, "parameters": [
# {"name": "file_path", "description": {"zh": "要读取的文件路径", "en": "File path to read."}, "type": "string", "required": True},
# {"name": "regex", "description": {"zh": "正则表达式字符串", "en": "Regular expression string."}, "type": "string", "required": True},
# {"name": "include_line_numbers", "description": {"zh": "是否在结果中包含行号(true/false)", "en": "Whether to include line numbers in the result (true/false)."}, "type": "string", "required": False}
# ]}
# ]
# }
import json
import re
import time
import math
from urllib.parse import quote, unquote
Error = Exception
def convertParamTypes(params, paramTypes):
if not params or not paramTypes:
return params
result = {}
for key in params:
if params[key] is None:
result[key] = params[key]
continue
expectedType = paramTypes.get(key)
if not expectedType:
result[key] = params[key]
continue
value = params[key]
try:
et = expectedType.lower()
if et == 'number':
if isinstance(value, str):
if '.' in value:
result[key] = float(value)
else:
result[key] = int(value, 10)
if isinstance(result[key], float) and math.isnan(result[key]):
raise Error(f"参数 {key} 无法转换为数字: {value}")
else:
result[key] = value
elif et == 'boolean':
if isinstance(value, str):
lowerValue = value.lower()
if lowerValue in ('true', '1', 'yes'):
result[key] = True
elif lowerValue in ('false', '0', 'no'):
result[key] = False
else:
raise Error(f"参数 {key} 无法转换为布尔值: {value}")
else:
result[key] = value
elif et == 'array':
if isinstance(value, str):
try:
result[key] = json.loads(value)
if not isinstance(result[key], list):
raise Error('解析结果不是数组')
except Exception:
raise Error(f"参数 {key} 无法转换为数组: {value}")
else:
result[key] = value
elif et == 'object':
if isinstance(value, str):
try:
result[key] = json.loads(value)
if isinstance(result[key], list) or not isinstance(result[key], dict):
raise Error('解析结果不是对象')
except Exception:
raise Error(f"参数 {key} 无法转换为对象: {value}")
else:
result[key] = value
else:
result[key] = value
except Exception as error:
console.error(f"参数类型转换错误: {str(error)}")
result[key] = value
return result
async def writer_wrap(func, params, successMessage, failMessage):
try:
console.log(f"开始执行函数: {func.__name__ or '匿名函数'}")
console.log("参数:", json.dumps(params, indent=2, ensure_ascii=False))
result = await func(params)
console.log(f"函数 {func.__name__ or '匿名函数'} 执行结果:", json.dumps(result, indent=2, ensure_ascii=False, default=str))
if result is None:
return
if isinstance(result, bool):
complete({
"success": result,
"message": successMessage if result else failMessage,
})
else:
complete({
"success": True,
"message": successMessage,
"data": result,
})
except Exception as error:
console.error(f"函数 {func.__name__ or '匿名函数'} 执行失败!")
console.error(f"错误信息: {str(error)}")
import traceback
stack = traceback.format_exc()
console.error(f"错误堆栈: {stack}")
complete({
"success": False,
"message": f"{failMessage}: {str(error)}",
"error_stack": stack,
})
async def readFilesRecursively(dir_, fileList=None, extensions=None):
if fileList is None:
fileList = []
try:
listResult = await Tools.Files.list(dir_)
entries = getattr(listResult, "entries", None) or []
for entry in entries:
filePath = f"{dir_}/{entry.name}"
if getattr(entry, "isDirectory", False):
await readFilesRecursively(filePath, fileList, extensions)
else:
if not extensions or len(extensions) == 0 or any(entry.name.lower().endswith(ext.lower()) for ext in extensions):
fileList.append(filePath)
except Exception as error:
console.error(f"读取目录失败: {str(error)}")
return fileList
async def search_code_in_folder(params):
paramTypes = {"recursive": "boolean"}
params = convertParamTypes(params, paramTypes)
folder_path = params.get("folder_path")
pattern = params.get("pattern")
file_extensions = params.get("file_extensions", ".js,.ts,.jsx,.tsx,.java,.cs,.py")
recursive = params.get("recursive", True)
if not folder_path:
raise Error("文件夹路径不能为空")
if not pattern:
raise Error("搜索模式不能为空")
try:
patternRegex = re.compile(pattern, re.MULTILINE)
extensions = file_extensions.split(",")
if recursive:
files = await readFilesRecursively(folder_path, [], extensions)
else:
listResult = await Tools.Files.list(folder_path)
entries = getattr(listResult, "entries", None) or []
files = [
f"{folder_path}/{entry.name}"
for entry in entries
if not getattr(entry, "isDirectory", False)
and any(entry.name.lower().endswith(ext.lower()) for ext in extensions)
]
results = []
for file in files:
try:
fileResult = await Tools.Files.read(file)
content = getattr(fileResult, "content", "") or ""
matches = []
for match in patternRegex.finditer(content):
lineNumber = content[:match.start()].count("\n") + 1
lines = content.split("\n")
lineContent = lines[lineNumber - 1] if lineNumber - 1 < len(lines) else ""
matches.append({
"match": match.group(0),
"line": lineNumber,
"content": lineContent,
})
if len(matches) > 0:
results.append({
"file": file,
"matches": matches,
})
except Exception as error:
console.error(f"处理文件 {file} 时出错: {str(error)}")
return results
except Exception as error:
raise Error(f"搜索代码时出错: {str(error)}")
async def extract_functions(params):
paramTypes = {"recursive": "boolean"}
params = convertParamTypes(params, paramTypes)
folder_path = params.get("folder_path")
function_name_pattern = params.get("function_name_pattern", ".*")
file_extensions = params.get("file_extensions", ".js,.ts,.jsx,.tsx")
recursive = params.get("recursive", True)
if not folder_path:
raise Error("文件夹路径不能为空")
try:
functionPatterns = [
f"function\\s+({function_name_pattern})\\s*\\([^)]*\\)\\s*\\{{",
f"(?:const|let|var)\\s+({function_name_pattern})\\s*=\\s*function\\s*\\([^)]*\\)\\s*\\{{",
f"(?:const|let|var)\\s+({function_name_pattern})\\s*=\\s*\\([^)]*\\)\\s*=>\\s*\\{{",
f"(?:async\\s+)?({function_name_pattern})\\s*\\([^)]*\\)\\s*\\{{",
]
combinedPattern = "|".join(functionPatterns)
return await search_code_in_folder({
"folder_path": folder_path,
"pattern": combinedPattern,
"file_extensions": file_extensions,
"recursive": recursive,
})
except Exception as error:
raise Error(f"提取函数时出错: {str(error)}")
async def extract_function_block(params):
paramTypes = {"include_line_numbers": "boolean"}
params = convertParamTypes(params, paramTypes)
file_path = params.get("file_path")
function_name = params.get("function_name")
include_line_numbers = params.get("include_line_numbers", False)
if not file_path:
raise Error("文件路径不能为空")
if not function_name:
raise Error("函数名称不能为空")
try:
fileResult = await Tools.Files.read(file_path)
content = getattr(fileResult, "content", "") or ""
lines = content.split("\n")
functionPatterns = [
f"function\\s+{function_name}\\s*\\([^)]*\\)\\s*\\{{",
f"(?:const|let|var)\\s+{function_name}\\s*=\\s*function\\s*\\([^)]*\\)\\s*\\{{",
f"(?:const|let|var)\\s+{function_name}\\s*=\\s*\\([^)]*\\)\\s*=>\\s*\\{{",
f"(?:async\\s+)?{function_name}\\s*\\([^)]*\\)\\s*\\{{",
]
combinedPattern = "|".join(functionPatterns)
functionRegex = re.compile(combinedPattern)
startLine = -1
for i in range(len(lines)):
if functionRegex.search(lines[i]):
startLine = i
break
if startLine == -1:
return {"error": f"未找到函数: {function_name}"}
bracesCount = 0
endLine = startLine
openingBraces = len(re.findall(r"{", lines[startLine]))
closingBraces = len(re.findall(r"}", lines[startLine]))
bracesCount = openingBraces - closingBraces
for i in range(startLine + 1, len(lines)):
openBraces = len(re.findall(r"{", lines[i]))
closeBraces = len(re.findall(r"}", lines[i]))
bracesCount += openBraces
bracesCount -= closeBraces
if bracesCount == 0:
endLine = i
break
functionBlock = "\n".join(lines[startLine:endLine + 1])
result = {
"function_name": function_name,
"code": functionBlock,
}
if include_line_numbers:
result["start_line"] = startLine + 1
result["end_line"] = endLine + 1
return result
except Exception as error:
raise Error(f"提取函数代码块时出错: {str(error)}")
async def find_code_blocks(params):
paramTypes = {"include_line_numbers": "boolean"}
params = convertParamTypes(params, paramTypes)
file_path = params.get("file_path")
block_pattern = params.get("block_pattern")
include_line_numbers = params.get("include_line_numbers", False)
if not file_path:
raise Error("文件路径不能为空")
if not block_pattern:
raise Error("代码块匹配模式不能为空")
try:
fileResult = await Tools.Files.read(file_path)
content = getattr(fileResult, "content", "") or ""
blockRegex = re.compile(block_pattern, re.MULTILINE)
matches = []
for match in blockRegex.finditer(content):
matchText = match.group(0)
startIndex = match.start()
lines_before = content[:startIndex].split("\n")
startLine = len(lines_before)
endIndex = startIndex + len(matchText)
bracesCount = 0
if "{" in matchText:
initialOpenBraces = len(re.findall(r"{", matchText))
initialCloseBraces = len(re.findall(r"}", matchText))
bracesCount = initialOpenBraces - initialCloseBraces
if bracesCount > 0:
remainingContent = content[endIndex:]
currentPos = 0
for i in range(len(remainingContent)):
char = remainingContent[i]
if char == "{":
bracesCount += 1
elif char == "}":
bracesCount -= 1
if bracesCount == 0:
currentPos = i + 1
break
endIndex += currentPos
codeBlock = content[startIndex:endIndex]
blockLines = codeBlock.split("\n")
endLine = startLine + len(blockLines) - 1
result = {"block": codeBlock}
if include_line_numbers:
result["start_line"] = startLine
result["end_line"] = endLine
matches.append(result)
return {
"file": file_path,
"blocks": matches,
}
except Exception as error:
raise Error(f"查找代码块时出错: {str(error)}")
async def read_regex_matches(params):
paramTypes = {"include_line_numbers": "boolean"}
params = convertParamTypes(params, paramTypes)
file_path = params.get("file_path")
regex = params.get("regex")
include_line_numbers = params.get("include_line_numbers", False)
if not file_path:
raise Error("文件路径不能为空")
if not regex:
raise Error("正则表达式不能为空")
try:
fileResult = await Tools.Files.read(file_path)
content = getattr(fileResult, "content", "") or ""
lines = content.split("\n")
pattern = re.compile(regex, re.MULTILINE)
matches = []
for match in pattern.finditer(content):
lineNumber = content[:match.start()].count("\n") + 1
lineContent = lines[lineNumber - 1] if lineNumber - 1 < len(lines) else ""
result = {
"match": match.group(0),
"content": lineContent,
}
if include_line_numbers:
result["line"] = lineNumber
if match.lastindex and match.lastindex >= 1:
result["groups"] = [match.group(i) for i in range(1, match.lastindex + 1)]
matches.append(result)
return {
"file": file_path,
"matches": matches,
}
except Exception as error:
raise Error(f"匹配正则表达式时出错: {str(error)}")
async def _search_code_in_folder_wrapper(params):
await writer_wrap(search_code_in_folder, params, "代码搜索完成", "代码搜索失败")
async def _extract_functions_wrapper(params):
await writer_wrap(extract_functions, params, "函数提取完成", "函数提取失败")
async def _extract_function_block_wrapper(params):
await writer_wrap(extract_function_block, params, "函数代码块提取完成", "函数代码块提取失败")
async def _find_code_blocks_wrapper(params):
await writer_wrap(find_code_blocks, params, "代码块查找完成", "代码块查找失败")
async def _read_regex_matches_wrapper(params):
await writer_wrap(read_regex_matches, params, "正则表达式匹配完成", "正则表达式匹配失败")
exports.search_code_in_folder = _search_code_in_folder_wrapper
exports.extract_functions = _extract_functions_wrapper
exports.extract_function_block = _extract_function_block_wrapper
exports.find_code_blocks = _find_code_blocks_wrapper
exports.read_regex_matches = _read_regex_matches_wrapper