myagent-ai 1.23.46 → 1.23.48
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.
- package/package.json +1 -1
- package/scripts/cli.py +74 -35
- package/web/api_server.py +25 -24
package/package.json
CHANGED
package/scripts/cli.py
CHANGED
|
@@ -153,6 +153,75 @@ async def _run():
|
|
|
153
153
|
sys.exit(1)
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
def _filter_unknown_args(args, allowed_prefixes=None):
|
|
157
|
+
"""[v1.23.46] 通用参数过滤器 — 去掉不认识的系统命令参数。
|
|
158
|
+
|
|
159
|
+
LLM 经常把系统命令的参数(如 ls -la, grep -n)传给 myagent-ai 子命令。
|
|
160
|
+
此函数只保留: 位置参数(不以 - 开头) + allowed_prefixes 匹配的选项。
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
args: 原始参数列表
|
|
164
|
+
allowed_prefixes: 允许的选项前缀集合(如 {"-p", "--pattern", "-r", "--max"})
|
|
165
|
+
Returns:
|
|
166
|
+
过滤后的参数列表
|
|
167
|
+
"""
|
|
168
|
+
if allowed_prefixes is None:
|
|
169
|
+
allowed_prefixes = set()
|
|
170
|
+
|
|
171
|
+
# 常见系统命令参数(标志,不带值)
|
|
172
|
+
_sys_flags = {
|
|
173
|
+
"-l", "-a", "-h", "-L", "-d", "-F", "-f", "-i", "-s", "-R",
|
|
174
|
+
"-S", "-t", "-r", "-u", "-c", "-1", "-m", "-x", "-o", "-g",
|
|
175
|
+
"-G", "-A", "-n", "-w", "-p", "-q", "-v", "-e", "-E", "-z",
|
|
176
|
+
"-b", "-B", "-T", "-H", "-I", "-k", "-N", "-W",
|
|
177
|
+
# GNU long options
|
|
178
|
+
"--color", "--group-directories-first", "--hide", "--indicator-style",
|
|
179
|
+
"--quoting-style", "--show-control-chars", "--sort", "--time",
|
|
180
|
+
"--time-style", "--format", "--human-readable", "-lh", "-la", "-al",
|
|
181
|
+
"-lr", "-lt", "-ls", "-ld", "-li", "-shal", "-lah",
|
|
182
|
+
}
|
|
183
|
+
# 带值的系统参数
|
|
184
|
+
_sys_opts = {
|
|
185
|
+
"--sort", "--time", "--time-style", "--format", "--hide",
|
|
186
|
+
"--indicator-style", "--quoting-style", "--context", "--width",
|
|
187
|
+
"--tabs", "--block-size",
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
filtered = []
|
|
191
|
+
skip_next = False
|
|
192
|
+
for arg in args:
|
|
193
|
+
if skip_next:
|
|
194
|
+
skip_next = False
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
# 以 - 开头的是选项参数
|
|
198
|
+
if arg.startswith("-"):
|
|
199
|
+
# 检查是否在允许列表中
|
|
200
|
+
matched = any(arg.startswith(prefix) for prefix in allowed_prefixes)
|
|
201
|
+
if matched:
|
|
202
|
+
filtered.append(arg)
|
|
203
|
+
# 如果参数不含 =,且不是标志模式,可能需要跳过下一个
|
|
204
|
+
if "=" not in arg and arg not in allowed_prefixes:
|
|
205
|
+
# 检查是否有对应的 long option 带 =
|
|
206
|
+
pass
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# 不在允许列表中,检查是否是系统参数
|
|
210
|
+
stripped = arg.lstrip("-").rstrip("=")
|
|
211
|
+
if arg in _sys_flags or stripped in _sys_flags:
|
|
212
|
+
continue # 标志,跳过
|
|
213
|
+
if arg in _sys_opts or stripped in _sys_opts:
|
|
214
|
+
skip_next = True # 带值选项,跳过下一个
|
|
215
|
+
continue
|
|
216
|
+
# 未知选项,跳过
|
|
217
|
+
continue
|
|
218
|
+
else:
|
|
219
|
+
# 位置参数,保留
|
|
220
|
+
filtered.append(arg)
|
|
221
|
+
|
|
222
|
+
return filtered if filtered else args
|
|
223
|
+
|
|
224
|
+
|
|
156
225
|
# =============================================================================
|
|
157
226
|
# 感知命令
|
|
158
227
|
# =============================================================================
|
|
@@ -308,6 +377,8 @@ async def cmd_write(args):
|
|
|
308
377
|
async def cmd_ls(args):
|
|
309
378
|
"""列出目录内容"""
|
|
310
379
|
import argparse
|
|
380
|
+
# [v1.23.46] 过滤系统 ls 参数(-l, -a, -la 等)
|
|
381
|
+
args = _filter_unknown_args(args, allowed_prefixes={"-p", "--pattern", "-r", "--recursive", "--max"})
|
|
311
382
|
p = argparse.ArgumentParser(prog="myagent-ai ls", description="列出目录内容")
|
|
312
383
|
p.add_argument("path", help="目录路径")
|
|
313
384
|
p.add_argument("-p", "--pattern", default="*", help="文件匹配模式 (如 *.py)")
|
|
@@ -340,47 +411,15 @@ async def cmd_rm(args):
|
|
|
340
411
|
async def cmd_grep(args):
|
|
341
412
|
"""搜索文件内容"""
|
|
342
413
|
import argparse
|
|
343
|
-
# [v1.23.
|
|
344
|
-
|
|
345
|
-
_unsupported_flags = {"-n", "--line-number", "-l", "--files-with-matches",
|
|
346
|
-
"-r", "-R", "--recursive", "-i", "--ignore-case",
|
|
347
|
-
"-c", "--count", "-w", "--word-regexp", "-v",
|
|
348
|
-
"--invert-match", "-E", "-F", "-P", "-m"}
|
|
349
|
-
_unsupported_opts = {"-e", "--regexp", "--include", "--exclude",
|
|
350
|
-
"--after-context", "--before-context", "--context",
|
|
351
|
-
"-A", "-B", "-C", "--max-count", "--color"}
|
|
352
|
-
_filtered = []
|
|
353
|
-
_skip_next = False
|
|
354
|
-
for i, a in enumerate(args):
|
|
355
|
-
if _skip_next:
|
|
356
|
-
_skip_next = False
|
|
357
|
-
continue
|
|
358
|
-
if a in _unsupported_flags:
|
|
359
|
-
# 标志参数,不跳过下一个
|
|
360
|
-
continue
|
|
361
|
-
if a in _unsupported_opts:
|
|
362
|
-
# 带参数选项,跳过下一个
|
|
363
|
-
_skip_next = True
|
|
364
|
-
continue
|
|
365
|
-
# 处理组合标志如 -rn、长选项 --name=Value
|
|
366
|
-
if any(a.startswith(k) for k in _unsupported_flags):
|
|
367
|
-
# -rn 或 -ni 等组合标志,不跳过下一个
|
|
368
|
-
continue
|
|
369
|
-
if any(a.startswith(k) for k in _unsupported_opts):
|
|
370
|
-
if "=" not in a:
|
|
371
|
-
_skip_next = True
|
|
372
|
-
continue
|
|
373
|
-
_filtered.append(a)
|
|
374
|
-
if not _filtered:
|
|
375
|
-
_filtered = args # fallback
|
|
376
|
-
|
|
414
|
+
# [v1.23.46] 使用通用过滤器,过滤系统 grep 参数(-n, -i, -r 等)
|
|
415
|
+
args = _filter_unknown_args(args, allowed_prefixes={"-p", "--pattern", "--max", "--depth"})
|
|
377
416
|
p = argparse.ArgumentParser(prog="myagent-ai grep", description="在目录中搜索文件内容")
|
|
378
417
|
p.add_argument("query", help="搜索关键词")
|
|
379
418
|
p.add_argument("path", help="搜索目录路径")
|
|
380
419
|
p.add_argument("-p", "--pattern", default="*", help="文件匹配模式 (如 *.py)")
|
|
381
420
|
p.add_argument("--max", type=int, default=50, help="最大结果数 (默认50)")
|
|
382
421
|
p.add_argument("--depth", type=int, default=10, help="最大递归深度 (默认10)")
|
|
383
|
-
a = p.parse_args(
|
|
422
|
+
a = p.parse_args(args)
|
|
384
423
|
|
|
385
424
|
from skills.file_skill import FileSearchSkill
|
|
386
425
|
skill = FileSearchSkill()
|
package/web/api_server.py
CHANGED
|
@@ -312,8 +312,6 @@ class ApiServer:
|
|
|
312
312
|
|
|
313
313
|
def _setup_routes(self):
|
|
314
314
|
r = self.app.router
|
|
315
|
-
print("DEBUG _setup_routes 被调用", flush=True)
|
|
316
|
-
print(f"DEBUG self.handle_test_llm = {self.handle_test_llm}", flush=True)
|
|
317
315
|
# ── 系统状态 ──
|
|
318
316
|
r.add_get("/api/status", self.handle_status)
|
|
319
317
|
r.add_post("/api/shutdown", self.handle_shutdown)
|
|
@@ -8096,28 +8094,7 @@ window.addEventListener('beforeunload', function() {{
|
|
|
8096
8094
|
except Exception:
|
|
8097
8095
|
pass
|
|
8098
8096
|
|
|
8099
|
-
#
|
|
8100
|
-
try:
|
|
8101
|
-
tp = self._get_task_persistence()
|
|
8102
|
-
interrupted = tp.get_all_tasks(status_filter=("running",))
|
|
8103
|
-
if interrupted:
|
|
8104
|
-
logger.warning(f"检测到 {len(interrupted)} 个上次未完成的任务,正在自动恢复...")
|
|
8105
|
-
for task in interrupted:
|
|
8106
|
-
try:
|
|
8107
|
-
await self._retry_interrupted_task(tp, task)
|
|
8108
|
-
except Exception as e:
|
|
8109
|
-
logger.error(f"自动恢复任务失败 ({task.get('task_id', '')}): {e}")
|
|
8110
|
-
# 恢复失败则标记为 failed
|
|
8111
|
-
tp.update_task_status(
|
|
8112
|
-
task.get("task_id", ""), "failed",
|
|
8113
|
-
metadata={"interrupted": True, "interrupt_reason": "自动恢复失败"},
|
|
8114
|
-
last_message=task.get("description", ""),
|
|
8115
|
-
)
|
|
8116
|
-
# 清理超过 7 天的旧已完成任务
|
|
8117
|
-
tp.cleanup_old_tasks(days=7)
|
|
8118
|
-
except Exception as e:
|
|
8119
|
-
logger.warning(f"任务持久化恢复检查失败(非关键): {e}")
|
|
8120
|
-
|
|
8097
|
+
# [v1.23.47] 先启动 Web 服务器,再恢复中断任务(避免任务恢复阻塞 Web 启动)
|
|
8121
8098
|
self._runner = web.AppRunner(self.app)
|
|
8122
8099
|
await self._runner.setup()
|
|
8123
8100
|
site = web.TCPSite(self._runner, host, port)
|
|
@@ -8133,6 +8110,30 @@ window.addEventListener('beforeunload', function() {{
|
|
|
8133
8110
|
raise
|
|
8134
8111
|
logger.info(f"管理后台: http://{host}:{port}/ui/")
|
|
8135
8112
|
|
|
8113
|
+
# [v1.23.47] 恢复被中断的任务:后台异步执行,不阻塞 Web 服务器
|
|
8114
|
+
async def _background_task_recovery():
|
|
8115
|
+
try:
|
|
8116
|
+
tp = self._get_task_persistence()
|
|
8117
|
+
interrupted = tp.get_all_tasks(status_filter=("running",))
|
|
8118
|
+
if interrupted:
|
|
8119
|
+
logger.warning(f"检测到 {len(interrupted)} 个上次未完成的任务,正在后台恢复...")
|
|
8120
|
+
for task in interrupted:
|
|
8121
|
+
try:
|
|
8122
|
+
await self._retry_interrupted_task(tp, task)
|
|
8123
|
+
except Exception as e:
|
|
8124
|
+
logger.error(f"自动恢复任务失败 ({task.get('task_id', '')}): {e}")
|
|
8125
|
+
tp.update_task_status(
|
|
8126
|
+
task.get("task_id", ""), "failed",
|
|
8127
|
+
metadata={"interrupted": True, "interrupt_reason": "自动恢复失败"},
|
|
8128
|
+
last_message=task.get("description", ""),
|
|
8129
|
+
)
|
|
8130
|
+
# 清理超过 7 天的旧已完成任务
|
|
8131
|
+
tp.cleanup_old_tasks(days=7)
|
|
8132
|
+
except Exception as e:
|
|
8133
|
+
logger.warning(f"任务持久化恢复检查失败(非关键): {e}")
|
|
8134
|
+
|
|
8135
|
+
asyncio.create_task(_background_task_recovery())
|
|
8136
|
+
|
|
8136
8137
|
async def stop(self):
|
|
8137
8138
|
if self._runner: await self._runner.cleanup()
|
|
8138
8139
|
|