sycommon-python-lib 0.2.2a3__py3-none-any.whl → 0.2.2a5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- command/cli.py +24 -3
- command/core/models.py +15 -0
- command/core/project.py +18 -7
- command/templates/cli/README.md.tpl +54 -0
- command/templates/cli/__init__.py +1 -0
- command/templates/cli/app.py.tpl +45 -0
- command/templates/cli/commands/__init__.py.tpl +1 -0
- command/templates/cli/commands/hello.py.tpl +6 -0
- command/templates/cli/pyproject.toml.tpl +14 -0
- command/templates/cli/tools/__init__.py.tpl +3 -0
- sycommon/agent/sandbox/file_ops.py +24 -53
- sycommon/agent/sandbox/http_sandbox_backend.py +316 -202
- sycommon/middleware/sandbox.py +641 -349
- sycommon/models/sandbox.py +91 -0
- {sycommon_python_lib-0.2.2a3.dist-info → sycommon_python_lib-0.2.2a5.dist-info}/METADATA +7 -7
- {sycommon_python_lib-0.2.2a3.dist-info → sycommon_python_lib-0.2.2a5.dist-info}/RECORD +19 -12
- {sycommon_python_lib-0.2.2a3.dist-info → sycommon_python_lib-0.2.2a5.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.2.2a3.dist-info → sycommon_python_lib-0.2.2a5.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.2a3.dist-info → sycommon_python_lib-0.2.2a5.dist-info}/top_level.txt +0 -0
command/cli.py
CHANGED
|
@@ -49,6 +49,12 @@ def create_init_parser(subparsers) -> None:
|
|
|
49
49
|
web - 标准 FastAPI Web 服务,适合 RESTful API 开发
|
|
50
50
|
agent - AI Agent 服务,基于 langgraph 实现状态图
|
|
51
51
|
|
|
52
|
+
CLI 项目结构:
|
|
53
|
+
commands/ - 子命令目录
|
|
54
|
+
model/ - 数据模型
|
|
55
|
+
client/ - 外部服务客户端
|
|
56
|
+
app.py - CLI 入口 (argparse)
|
|
57
|
+
|
|
52
58
|
Agent 项目结构:
|
|
53
59
|
agent/ - Agent 核心 (main_agent, nodes, states, enums)
|
|
54
60
|
api/sse/ - SSE 流式接口
|
|
@@ -79,7 +85,7 @@ Web 项目结构:
|
|
|
79
85
|
# 位置参数
|
|
80
86
|
init_parser.add_argument(
|
|
81
87
|
"project_type",
|
|
82
|
-
choices=["web", "agent"],
|
|
88
|
+
choices=["web", "agent", "cli"],
|
|
83
89
|
help="项目类型:web / agent"
|
|
84
90
|
)
|
|
85
91
|
init_parser.add_argument(
|
|
@@ -221,6 +227,8 @@ def _detect_project_type(project_path) -> str | None:
|
|
|
221
227
|
if (project_path / "agent" / "main_agent.py").exists() or \
|
|
222
228
|
(project_path / "agent" / "enums").is_dir():
|
|
223
229
|
return "agent"
|
|
230
|
+
if (project_path / "commands").is_dir() and (project_path / "app.py").exists():
|
|
231
|
+
return "cli"
|
|
224
232
|
if (project_path / "api").is_dir():
|
|
225
233
|
return "web"
|
|
226
234
|
return None
|
|
@@ -247,13 +255,19 @@ def handle_doctor_command(args) -> bool:
|
|
|
247
255
|
warnings.append("无法识别项目类型 (非 web / agent 标准结构)")
|
|
248
256
|
|
|
249
257
|
# 1. 检查必要文件
|
|
250
|
-
|
|
258
|
+
if project_type == "cli":
|
|
259
|
+
required_files = ["app.py", "pyproject.toml"]
|
|
260
|
+
else:
|
|
261
|
+
required_files = ["app.py", "requirements.txt"]
|
|
251
262
|
for file in required_files:
|
|
252
263
|
if not (project_path / file).exists():
|
|
253
264
|
issues.append(f"缺少必要文件: {file}")
|
|
254
265
|
|
|
255
266
|
# 2. 检查配置文件
|
|
256
|
-
|
|
267
|
+
if project_type == "cli":
|
|
268
|
+
config_files = [".env"]
|
|
269
|
+
else:
|
|
270
|
+
config_files = ["app.yaml", ".env"]
|
|
257
271
|
for file in config_files:
|
|
258
272
|
if not (project_path / file).exists():
|
|
259
273
|
warnings.append(f"缺少配置文件: {file}")
|
|
@@ -276,6 +290,8 @@ def handle_doctor_command(args) -> bool:
|
|
|
276
290
|
issues.append(f"缺少 Agent 核心文件: {file}")
|
|
277
291
|
elif project_type == "web":
|
|
278
292
|
required_dirs = ["api", "model", "tools", "client"]
|
|
293
|
+
elif project_type == "cli":
|
|
294
|
+
required_dirs = ["commands", "model", "tools"]
|
|
279
295
|
else:
|
|
280
296
|
required_dirs = ["api", "model", "tools"]
|
|
281
297
|
|
|
@@ -351,6 +367,11 @@ def handle_list_command(args) -> bool:
|
|
|
351
367
|
"api/sse/", "api/query.py", "tools/", "model/", "db/"],
|
|
352
368
|
"features": "nacos, kafka_log, llm, sentry (默认启用)",
|
|
353
369
|
},
|
|
370
|
+
"cli": {
|
|
371
|
+
"desc": "Python CLI 命令行工具 (argparse)",
|
|
372
|
+
"dirs": ["commands/", "model/", "client/"],
|
|
373
|
+
"features": "无默认依赖 (精简模板)",
|
|
374
|
+
},
|
|
354
375
|
}
|
|
355
376
|
|
|
356
377
|
for name, info in templates.items():
|
command/core/models.py
CHANGED
|
@@ -13,6 +13,19 @@ AGENT_DEFAULTS = {
|
|
|
13
13
|
"llm": True,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
# CLI 项目默认关闭所有服务依赖
|
|
17
|
+
CLI_DEFAULTS = {
|
|
18
|
+
"database": False,
|
|
19
|
+
"rabbitmq": False,
|
|
20
|
+
"nacos": False,
|
|
21
|
+
"kafka_log": False,
|
|
22
|
+
"sse": False,
|
|
23
|
+
"llm": False,
|
|
24
|
+
"redis": False,
|
|
25
|
+
"elasticsearch": False,
|
|
26
|
+
"sentry": False,
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
|
|
17
30
|
@dataclass
|
|
18
31
|
class FeatureOptions:
|
|
@@ -32,6 +45,8 @@ class FeatureOptions:
|
|
|
32
45
|
"""根据项目类型返回推荐默认配置"""
|
|
33
46
|
if project_type == "agent":
|
|
34
47
|
return cls(**AGENT_DEFAULTS)
|
|
48
|
+
if project_type == "cli":
|
|
49
|
+
return cls(**CLI_DEFAULTS)
|
|
35
50
|
return cls()
|
|
36
51
|
|
|
37
52
|
def to_template_vars(self) -> dict:
|
command/core/project.py
CHANGED
|
@@ -153,13 +153,19 @@ def init_project(
|
|
|
153
153
|
'db/', 'db/service.py', 'db/model/', 'db/model/entity.py',
|
|
154
154
|
],
|
|
155
155
|
}
|
|
156
|
+
# CLI 项目不需要的服务端文件
|
|
157
|
+
cli_skip_files = {'app.yaml', 'app.yaml.tpl', 'requirements.txt', 'requirements.txt.tpl', 'Dockerfile', 'Dockerfile.tpl'}
|
|
156
158
|
disabled_features = {
|
|
157
159
|
key for key, flag_val in features.to_template_vars().items()
|
|
158
160
|
if flag_val == 'false' and key in feature_map
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
def _should_skip(rel_path: str) -> bool:
|
|
162
|
-
"""
|
|
164
|
+
"""检查文件是否因功能关闭或项目类型而应跳过"""
|
|
165
|
+
# CLI 项目跳过不需要的服务端文件
|
|
166
|
+
if project_type == "cli":
|
|
167
|
+
if rel_path in cli_skip_files or rel_path.replace('.tpl', '') in cli_skip_files:
|
|
168
|
+
return True
|
|
163
169
|
for feat_key in disabled_features:
|
|
164
170
|
for pattern in feature_map[feat_key]:
|
|
165
171
|
if pattern in rel_path:
|
|
@@ -191,7 +197,7 @@ def init_project(
|
|
|
191
197
|
copied_files = _render_files(file_mappings, context, project_path, verbose)
|
|
192
198
|
|
|
193
199
|
if copied_files > 0:
|
|
194
|
-
_print_success_message(project_path, project_name, copied_files)
|
|
200
|
+
_print_success_message(project_path, project_name, copied_files, project_type)
|
|
195
201
|
return True
|
|
196
202
|
else:
|
|
197
203
|
print_warning("未创建任何文件,可能是缺少模板文件或模板路径配置错误")
|
|
@@ -284,7 +290,8 @@ def _render_files(
|
|
|
284
290
|
def _print_success_message(
|
|
285
291
|
project_path: Path,
|
|
286
292
|
project_name: str,
|
|
287
|
-
copied_files: int
|
|
293
|
+
copied_files: int,
|
|
294
|
+
project_type: str = "web",
|
|
288
295
|
) -> None:
|
|
289
296
|
"""打印成功信息"""
|
|
290
297
|
print_success(f"模板工程 {project_name} 创建完成!")
|
|
@@ -293,7 +300,11 @@ def _print_success_message(
|
|
|
293
300
|
print()
|
|
294
301
|
print_info("下一步操作:")
|
|
295
302
|
print(f" cd {project_name}")
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
303
|
+
if project_type == "cli":
|
|
304
|
+
print(f" uv sync")
|
|
305
|
+
print(f" uv run python app.py")
|
|
306
|
+
else:
|
|
307
|
+
print(f" # 建议先创建虚拟环境")
|
|
308
|
+
print(f" python -m venv .venv && source .venv/bin/activate")
|
|
309
|
+
print(f" pip install -r requirements.txt")
|
|
310
|
+
print(f" python app.py")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
{{ project_name }} - Python CLI 命令行工具
|
|
4
|
+
|
|
5
|
+
## 项目结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{{ project_name }}/
|
|
9
|
+
├── commands/ # 子命令目录
|
|
10
|
+
│ ├── __init__.py
|
|
11
|
+
│ └── hello.py # 示例子命令
|
|
12
|
+
├── tools/ # 工具函数
|
|
13
|
+
├── model/ # 数据模型
|
|
14
|
+
├── client/ # 外部服务客户端
|
|
15
|
+
├── app.py # CLI 入口
|
|
16
|
+
├── pyproject.toml # uv 项目配置
|
|
17
|
+
└── README.md
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
### 安装依赖
|
|
23
|
+
```bash
|
|
24
|
+
uv sync
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 使用
|
|
28
|
+
```bash
|
|
29
|
+
# 查看帮助
|
|
30
|
+
uv run python app.py -h
|
|
31
|
+
|
|
32
|
+
# hello 命令
|
|
33
|
+
uv run python app.py hello --name "World"
|
|
34
|
+
|
|
35
|
+
# 查看版本
|
|
36
|
+
uv run python app.py version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 添加新子命令
|
|
40
|
+
|
|
41
|
+
1. 在 `commands/` 目录下创建新模块,例如 `commands/xxx.py`
|
|
42
|
+
2. 实现 `handle_xxx(args)` 函数
|
|
43
|
+
3. 在 `app.py` 中注册子命令:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from commands.xxx import handle_xxx
|
|
47
|
+
|
|
48
|
+
# 在 create_parser() 中添加:
|
|
49
|
+
xxx_parser = subparsers.add_parser("xxx", help="xxx 说明")
|
|
50
|
+
|
|
51
|
+
# 在 main() 中添加:
|
|
52
|
+
elif args.command == "xxx":
|
|
53
|
+
handle_xxx(args)
|
|
54
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI 项目模板"""
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{ project_name }} - CLI 工具入口
|
|
3
|
+
创建时间: {{ create_time }}
|
|
4
|
+
作者: {{ author }}
|
|
5
|
+
"""
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from commands.hello import handle_hello
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
13
|
+
"""创建命令行解析器"""
|
|
14
|
+
parser = argparse.ArgumentParser(
|
|
15
|
+
prog="{{ project_name }}",
|
|
16
|
+
description="{{ project_name }} - CLI 命令行工具",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
|
20
|
+
|
|
21
|
+
# hello 子命令
|
|
22
|
+
hello_parser = subparsers.add_parser("hello", help="打招呼")
|
|
23
|
+
hello_parser.add_argument("--name", default="World", help="名字")
|
|
24
|
+
|
|
25
|
+
# version 子命令
|
|
26
|
+
subparsers.add_parser("version", help="显示版本")
|
|
27
|
+
|
|
28
|
+
return parser
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main() -> None:
|
|
32
|
+
"""CLI 主入口"""
|
|
33
|
+
parser = create_parser()
|
|
34
|
+
args = parser.parse_args()
|
|
35
|
+
|
|
36
|
+
if args.command == "hello":
|
|
37
|
+
handle_hello(args)
|
|
38
|
+
elif args.command == "version":
|
|
39
|
+
print("{{ project_name }} 1.0.0")
|
|
40
|
+
else:
|
|
41
|
+
parser.print_help()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""子命令模块"""
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{ project_name }}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "{{ project_name }} - CLI 命令行工具"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
{{ short_project_name }} = "app:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
@@ -149,11 +149,11 @@ class FileOperationsMixin:
|
|
|
149
149
|
def _check_workspace_exists_sync(self: "HTTPSandboxBackend") -> bool:
|
|
150
150
|
"""检查沙箱工作空间是否存在(同步版本)"""
|
|
151
151
|
try:
|
|
152
|
-
self._post_sync(f"{SANDBOX_API_PREFIX}/
|
|
152
|
+
result = self._post_sync(f"{SANDBOX_API_PREFIX}/stat", {
|
|
153
153
|
"path": "/",
|
|
154
154
|
"user_id": self.user_id
|
|
155
155
|
})
|
|
156
|
-
return
|
|
156
|
+
return result.get("exists", False)
|
|
157
157
|
except Exception as e:
|
|
158
158
|
SYLogger.warning(f"[Sandbox] 检查工作空间失败: {e}")
|
|
159
159
|
return False
|
|
@@ -161,11 +161,11 @@ class FileOperationsMixin:
|
|
|
161
161
|
async def _check_workspace_exists_async(self: "HTTPSandboxBackend") -> bool:
|
|
162
162
|
"""检查沙箱工作空间是否存在(异步版本)"""
|
|
163
163
|
try:
|
|
164
|
-
await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/
|
|
164
|
+
result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/stat", {
|
|
165
165
|
"path": "/",
|
|
166
166
|
"user_id": self.user_id
|
|
167
167
|
})
|
|
168
|
-
return
|
|
168
|
+
return result.get("exists", False)
|
|
169
169
|
except Exception as e:
|
|
170
170
|
SYLogger.warning(f"[Sandbox] 检查工作空间失败: {e}")
|
|
171
171
|
return False
|
|
@@ -184,47 +184,31 @@ class FileOperationsMixin:
|
|
|
184
184
|
"""列出目录内容"""
|
|
185
185
|
try:
|
|
186
186
|
self._ensure_synced_sync()
|
|
187
|
-
print(
|
|
188
|
-
f"[Sandbox-DEBUG] ls() called with path={repr(path)}, user_id={repr(self.user_id)}", flush=True)
|
|
189
187
|
SYLogger.info(f"[Sandbox] 列出目录: {path}")
|
|
190
188
|
result = self._post_sync(f"{SANDBOX_API_PREFIX}/ls", {
|
|
191
189
|
"path": path,
|
|
192
190
|
"user_id": self.user_id
|
|
193
191
|
})
|
|
194
|
-
print(
|
|
195
|
-
f"[Sandbox-DEBUG] ls() raw result type={type(result).__name__}, repr={repr(result)[:500]}", flush=True)
|
|
196
192
|
entries = [FileInfo(**item) for item in result]
|
|
197
|
-
print(
|
|
198
|
-
f"[Sandbox-DEBUG] ls() parsed entries={len(entries)}", flush=True)
|
|
199
193
|
SYLogger.info(f"[Sandbox] 目录内容: {len(entries)} 项")
|
|
200
194
|
return LsResult(entries=entries)
|
|
201
195
|
except Exception as e:
|
|
202
|
-
print(
|
|
203
|
-
f"[Sandbox-DEBUG] ls() EXCEPTION: {type(e).__name__}: {e}", flush=True)
|
|
204
196
|
SYLogger.error(f"[Sandbox] 列出目录失败: {e}")
|
|
205
197
|
return LsResult(error=str(e))
|
|
206
198
|
|
|
207
199
|
async def als(self: "HTTPSandboxBackend", path: str) -> LsResult:
|
|
208
|
-
"""
|
|
200
|
+
"""异步列出目录内容"""
|
|
209
201
|
try:
|
|
210
202
|
await self._ensure_synced_async()
|
|
211
|
-
print(
|
|
212
|
-
f"[Sandbox-DEBUG] als() called with path={repr(path)}, user_id={repr(self.user_id)}, base_url={repr(self._base_url if hasattr(self, '_base_url') else 'N/A')}", flush=True)
|
|
213
203
|
SYLogger.info(f"[Sandbox] 异步列出目录: {path}")
|
|
214
204
|
result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/ls", {
|
|
215
205
|
"path": path,
|
|
216
206
|
"user_id": self.user_id
|
|
217
207
|
})
|
|
218
|
-
print(
|
|
219
|
-
f"[Sandbox-DEBUG] als() raw result type={type(result).__name__}, repr={repr(result)[:500]}", flush=True)
|
|
220
208
|
entries = [FileInfo(**item) for item in result]
|
|
221
|
-
print(
|
|
222
|
-
f"[Sandbox-DEBUG] als() parsed entries={len(entries)}, first 3 paths={[e.get('path','?') if isinstance(e, dict) else getattr(e, 'path','?') for e in entries[:3]]}", flush=True)
|
|
223
209
|
SYLogger.info(f"[Sandbox] 目录内容: {len(entries)} 项")
|
|
224
210
|
return LsResult(entries=entries)
|
|
225
211
|
except Exception as e:
|
|
226
|
-
print(
|
|
227
|
-
f"[Sandbox-DEBUG] als() EXCEPTION: {type(e).__name__}: {e}", flush=True)
|
|
228
212
|
SYLogger.error(f"[Sandbox] 异步列出目录失败: {e}")
|
|
229
213
|
return LsResult(error=str(e))
|
|
230
214
|
|
|
@@ -251,15 +235,11 @@ class FileOperationsMixin:
|
|
|
251
235
|
SYLogger.error(f"[Sandbox] 读取文件失败: {result['error']}")
|
|
252
236
|
return ReadResult(error=result["error"])
|
|
253
237
|
content = result.get("content", "")
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
from datetime import datetime
|
|
257
|
-
now = datetime.now().isoformat()
|
|
238
|
+
encoding = result.get("encoding", "utf-8")
|
|
239
|
+
SYLogger.info(f"[Sandbox] 读取完成: {len(content)} 字符, encoding={encoding}")
|
|
258
240
|
file_data = FileData(
|
|
259
241
|
content=content,
|
|
260
|
-
encoding=
|
|
261
|
-
created_at=result.get("created_at", now),
|
|
262
|
-
modified_at=result.get("modified_at", now),
|
|
242
|
+
encoding=encoding,
|
|
263
243
|
)
|
|
264
244
|
return ReadResult(file_data=file_data)
|
|
265
245
|
except Exception as e:
|
|
@@ -287,14 +267,11 @@ class FileOperationsMixin:
|
|
|
287
267
|
SYLogger.error(f"[Sandbox] 异步读取文件失败: {result['error']}")
|
|
288
268
|
return ReadResult(error=result["error"])
|
|
289
269
|
content = result.get("content", "")
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
now = datetime.now().isoformat()
|
|
270
|
+
encoding = result.get("encoding", "utf-8")
|
|
271
|
+
SYLogger.info(f"[Sandbox] 异步读取完成: {len(content)} 字符, encoding={encoding}")
|
|
293
272
|
file_data = FileData(
|
|
294
273
|
content=content,
|
|
295
|
-
encoding=
|
|
296
|
-
created_at=result.get("created_at", now),
|
|
297
|
-
modified_at=result.get("modified_at", now),
|
|
274
|
+
encoding=encoding,
|
|
298
275
|
)
|
|
299
276
|
return ReadResult(file_data=file_data)
|
|
300
277
|
except Exception as e:
|
|
@@ -602,29 +579,23 @@ class FileOperationsMixin:
|
|
|
602
579
|
# ============== 文件存在检查 ==============
|
|
603
580
|
|
|
604
581
|
async def astat(self: "HTTPSandboxBackend", path: str) -> "StatResult":
|
|
605
|
-
"""
|
|
582
|
+
"""异步检查文件或目录状态(使用 /stat 端点)"""
|
|
606
583
|
try:
|
|
607
584
|
await self._ensure_synced_async()
|
|
608
|
-
SYLogger.info(f"[Sandbox]
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
"
|
|
612
|
-
"user_id": self.user_id,
|
|
613
|
-
"offset": 0,
|
|
614
|
-
"limit": 1,
|
|
585
|
+
SYLogger.info(f"[Sandbox] 异步查询文件状态: {path}")
|
|
586
|
+
result = await self._post_async_with_failover(f"{SANDBOX_API_PREFIX}/stat", {
|
|
587
|
+
"path": path,
|
|
588
|
+
"user_id": self.user_id
|
|
615
589
|
})
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
return StatResult(path=path, exists=True, is_dir=True, size=0)
|
|
624
|
-
|
|
625
|
-
return StatResult(path=path, exists=False)
|
|
590
|
+
return StatResult(
|
|
591
|
+
path=result.get("path", path),
|
|
592
|
+
exists=result.get("exists", False),
|
|
593
|
+
is_dir=result.get("is_dir", False),
|
|
594
|
+
size=result.get("size", 0),
|
|
595
|
+
error=result.get("error")
|
|
596
|
+
)
|
|
626
597
|
except Exception as e:
|
|
627
|
-
SYLogger.error(f"[Sandbox]
|
|
598
|
+
SYLogger.error(f"[Sandbox] 异步查询文件状态异常: {e}")
|
|
628
599
|
return StatResult(path=path, error=str(e))
|
|
629
600
|
|
|
630
601
|
|