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:
1 parent
6fa759665d
commit
dc77dfd304
48 files changed
+22273
No files matched your search
@@ -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
|
||||
Reference in new issue
Block a user