myagent-ai 1.23.54 → 1.23.55
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/agents/main_agent.py +10 -0
- package/core/tool_dispatcher.py +40 -9
- package/package.json +1 -1
- package/scripts/cli.py +137 -5
- package/start.js +2 -0
package/agents/main_agent.py
CHANGED
|
@@ -136,6 +136,13 @@ class MainAgent(BaseAgent):
|
|
|
136
136
|
|
|
137
137
|
【Agent间通信】
|
|
138
138
|
- 向其他Agent发送消息: myagent-ai chat --agent <Agent路径> --message "消息内容"
|
|
139
|
+
- 向其他Agent发送文件: myagent-ai chat --agent <Agent路径> --file "文件路径"
|
|
140
|
+
- 同时发送消息和文件: myagent-ai chat --agent <路径> -m "消息" -f "文件路径"
|
|
141
|
+
- 可同时发送多个文件: myagent-ai chat --agent <路径> -f "文件1|文件2|文件3" (用|分隔多个文件路径)
|
|
142
|
+
|
|
143
|
+
【压缩解压】
|
|
144
|
+
- 压缩文件/文件夹: myagent-ai zip 路径1 [路径2] ... [-o 输出.zip] [--exclude __pycache__ *.pyc .git]
|
|
145
|
+
- 解压zip文件: myagent-ai unzip 文件.zip [-o 输出目录]
|
|
139
146
|
|
|
140
147
|
【媒体播放】
|
|
141
148
|
- 播放音频: myagent-ai playaudio --url 音频URL [--title 标题] 或 myagent-ai playaudio --file 本地路径
|
|
@@ -153,6 +160,9 @@ class MainAgent(BaseAgent):
|
|
|
153
160
|
<tool><toolname>command</toolname><parms>{"command": "myagent-ai playaudio --url https://music.163.com/song?id=123 --title 歌曲名"}</parms><timeout>10</timeout></tool>
|
|
154
161
|
<tool><toolname>command</toolname><parms>{"command": "myagent-ai playvideo --url https://www.bilibili.com/video/BV123 --title 视频名"}</parms><timeout>10</timeout></tool>
|
|
155
162
|
<tool><toolname>command</toolname><parms>{"command": "myagent-ai chat --agent default/coder --message \"请帮我分析这段代码的时间复杂度\""}</parms><timeout>10</timeout></tool>
|
|
163
|
+
<tool><toolname>command</toolname><parms>{"command": "myagent-ai chat --agent default/coder -f \"/path/to/report.pdf\" -m \"请审阅这份报告\""}</parms><timeout>10</timeout></tool>
|
|
164
|
+
<tool><toolname>command</toolname><parms>{"command": "myagent-ai zip /path/to/project -o backup.zip --exclude __pycache__ .git"}</parms><timeout>30</timeout></tool>
|
|
165
|
+
<tool><toolname>command</toolname><parms>{"command": "myagent-ai unzip /path/to/archive.zip -o ./output"}</parms><timeout>30</timeout></tool>
|
|
156
166
|
|
|
157
167
|
多个命令可用 && 连接一次执行(强烈推荐,减少LLM回调次数):
|
|
158
168
|
<tool><toolname>command</toolname><parms>{"command": "myagent-ai search xxx && myagent-ai read-url https://..."}</parms><timeout>30</timeout></tool>
|
package/core/tool_dispatcher.py
CHANGED
|
@@ -236,31 +236,62 @@ class ToolDispatcher:
|
|
|
236
236
|
)
|
|
237
237
|
if not media_result.get("success"):
|
|
238
238
|
result["output"] += f"\n[视频播放失败: {media_result.get('error', '')}]"
|
|
239
|
-
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
|
|
239
|
+
# [v1.23.55] 检测 __CHAT_AGENT__ 标记 — CLI chat 命令输出此标记
|
|
240
|
+
# 新格式(4段): __CHAT_AGENT__agent_path|agent_name|message|files__END__
|
|
241
|
+
# 旧格式(3段): __CHAT_AGENT__agent_path|agent_name|message__END__
|
|
242
|
+
# files 为空时表示纯文本消息,非空时为 | 分隔的文件绝对路径列表
|
|
243
|
+
chat_markers = _re.findall(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)\|(.+?)__END__', clean_output)
|
|
243
244
|
if chat_markers:
|
|
244
245
|
clean_output = _re.sub(r'__CHAT_AGENT__.+?__END__\n?', '', clean_output).strip()
|
|
245
246
|
result["output"] = clean_output
|
|
246
|
-
for chat_agent_path, chat_agent_name, chat_msg in chat_markers:
|
|
247
|
+
for chat_agent_path, chat_agent_name, chat_msg, chat_files in chat_markers:
|
|
248
|
+
chat_files = chat_files.strip()
|
|
249
|
+
file_list = [f.strip() for f in chat_files.split("|") if f.strip()] if chat_files else []
|
|
247
250
|
# Store as a chat agent event in sent_files for persistence
|
|
248
251
|
if sent_files is not None:
|
|
249
|
-
|
|
252
|
+
chat_entry = {
|
|
250
253
|
"_type": "chat_agent",
|
|
251
254
|
"target_agent": chat_agent_path.strip(),
|
|
252
255
|
"target_name": chat_agent_name.strip(),
|
|
253
256
|
"message": chat_msg.strip(),
|
|
254
|
-
}
|
|
257
|
+
}
|
|
258
|
+
if file_list:
|
|
259
|
+
chat_entry["files"] = file_list
|
|
260
|
+
sent_files.append(chat_entry)
|
|
255
261
|
# Emit SSE event for frontend display
|
|
256
262
|
try:
|
|
257
|
-
|
|
263
|
+
event_data = {
|
|
258
264
|
"target_agent": chat_agent_path.strip(),
|
|
259
265
|
"target_name": chat_agent_name.strip(),
|
|
260
266
|
"message": chat_msg.strip(),
|
|
261
|
-
}
|
|
267
|
+
}
|
|
268
|
+
if file_list:
|
|
269
|
+
event_data["files"] = file_list
|
|
270
|
+
await self._emit_sse("v2_chat_agent", event_data, stream_callback)
|
|
262
271
|
except Exception:
|
|
263
272
|
pass
|
|
273
|
+
else:
|
|
274
|
+
# 兼容旧格式(3段): __CHAT_AGENT__agent_path|agent_name|message__END__
|
|
275
|
+
chat_markers_old = _re.findall(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)__END__', clean_output)
|
|
276
|
+
if chat_markers_old:
|
|
277
|
+
clean_output = _re.sub(r'__CHAT_AGENT__.+?__END__\n?', '', clean_output).strip()
|
|
278
|
+
result["output"] = clean_output
|
|
279
|
+
for chat_agent_path, chat_agent_name, chat_msg in chat_markers_old:
|
|
280
|
+
if sent_files is not None:
|
|
281
|
+
sent_files.append({
|
|
282
|
+
"_type": "chat_agent",
|
|
283
|
+
"target_agent": chat_agent_path.strip(),
|
|
284
|
+
"target_name": chat_agent_name.strip(),
|
|
285
|
+
"message": chat_msg.strip(),
|
|
286
|
+
})
|
|
287
|
+
try:
|
|
288
|
+
await self._emit_sse("v2_chat_agent", {
|
|
289
|
+
"target_agent": chat_agent_path.strip(),
|
|
290
|
+
"target_name": chat_agent_name.strip(),
|
|
291
|
+
"message": chat_msg.strip(),
|
|
292
|
+
}, stream_callback)
|
|
293
|
+
except Exception:
|
|
294
|
+
pass
|
|
264
295
|
|
|
265
296
|
# [v1.23.32] 确保 result 始终有 output 字段(to_dict 返回 stdout,V2 循环依赖 output)
|
|
266
297
|
if "output" not in result:
|
package/package.json
CHANGED
package/scripts/cli.py
CHANGED
|
@@ -138,6 +138,9 @@ async def _run():
|
|
|
138
138
|
"playvideo": cmd_playvideo,
|
|
139
139
|
# 通信
|
|
140
140
|
"chat": cmd_chat,
|
|
141
|
+
# 压缩解压
|
|
142
|
+
"zip": cmd_zip,
|
|
143
|
+
"unzip": cmd_unzip,
|
|
141
144
|
# 帮助
|
|
142
145
|
"help": cmd_help,
|
|
143
146
|
"-h": cmd_help,
|
|
@@ -1073,14 +1076,19 @@ async def cmd_playvideo(args):
|
|
|
1073
1076
|
# =============================================================================
|
|
1074
1077
|
|
|
1075
1078
|
async def cmd_chat(args):
|
|
1076
|
-
"""Agent间通信 — 向群内其他Agent
|
|
1079
|
+
"""Agent间通信 — 向群内其他Agent发送消息或文件"""
|
|
1077
1080
|
import argparse
|
|
1078
|
-
p = argparse.ArgumentParser(prog="myagent-ai chat", description="向群内其他Agent
|
|
1081
|
+
p = argparse.ArgumentParser(prog="myagent-ai chat", description="向群内其他Agent发送消息或文件,消息会出现在群聊记录中")
|
|
1079
1082
|
p.add_argument("--agent", "-a", required=True, help="目标Agent路径,如 default/coder")
|
|
1080
|
-
p.add_argument("--message", "-m",
|
|
1083
|
+
p.add_argument("--message", "-m", default="", help="要发送的消息内容")
|
|
1084
|
+
p.add_argument("--file", "-f", default="", help="要发送的文件路径(可多次使用: -f file1 -f file2)")
|
|
1081
1085
|
p.add_argument("--group", "-g", help="群ID(可选,默认使用最近活跃的群)")
|
|
1082
1086
|
a = p.parse_args(args)
|
|
1083
1087
|
|
|
1088
|
+
if not a.message and not a.file:
|
|
1089
|
+
print("错误: 必须指定 --message 或 --file", file=sys.stderr)
|
|
1090
|
+
sys.exit(1)
|
|
1091
|
+
|
|
1084
1092
|
# Find agent config
|
|
1085
1093
|
agent_dir = Path(__file__).parent.parent / "data" / "agents" / a.agent
|
|
1086
1094
|
cfg_file = agent_dir / "config.json"
|
|
@@ -1090,8 +1098,126 @@ async def cmd_chat(args):
|
|
|
1090
1098
|
cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
|
|
1091
1099
|
agent_name = cfg.get("name", a.agent)
|
|
1092
1100
|
|
|
1093
|
-
#
|
|
1094
|
-
|
|
1101
|
+
# 验证文件是否存在
|
|
1102
|
+
files_info = ""
|
|
1103
|
+
if a.file:
|
|
1104
|
+
import glob as _glob
|
|
1105
|
+
# 支持多个 -f 参数
|
|
1106
|
+
file_paths = a.file.split("|")
|
|
1107
|
+
valid_files = []
|
|
1108
|
+
for fp in file_paths:
|
|
1109
|
+
fp = fp.strip()
|
|
1110
|
+
if not fp:
|
|
1111
|
+
continue
|
|
1112
|
+
# 支持通配符
|
|
1113
|
+
matched = _glob.glob(fp)
|
|
1114
|
+
if not matched:
|
|
1115
|
+
print(f"错误: 文件不存在: {fp}", file=sys.stderr)
|
|
1116
|
+
sys.exit(1)
|
|
1117
|
+
for mf in matched:
|
|
1118
|
+
abs_mf = os.path.abspath(mf)
|
|
1119
|
+
if os.path.isfile(abs_mf):
|
|
1120
|
+
valid_files.append(abs_mf)
|
|
1121
|
+
if valid_files:
|
|
1122
|
+
files_info = "|".join(valid_files)
|
|
1123
|
+
|
|
1124
|
+
# Output chat marker
|
|
1125
|
+
# 格式: __CHAT_AGENT__agent_path|agent_name|message|files__END__
|
|
1126
|
+
# files 为空字符串时表示纯文本消息
|
|
1127
|
+
print(f"__CHAT_AGENT__{a.agent}|{agent_name}|{a.message}|{files_info}__END__")
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
async def cmd_zip(args):
|
|
1131
|
+
"""压缩文件或文件夹"""
|
|
1132
|
+
import argparse
|
|
1133
|
+
import zipfile
|
|
1134
|
+
p = argparse.ArgumentParser(prog="myagent-ai zip", description="压缩文件或文件夹为 zip")
|
|
1135
|
+
p.add_argument("paths", nargs="+", help="要压缩的文件或文件夹路径(支持多个)")
|
|
1136
|
+
p.add_argument("-o", "--output", default="", help="输出 zip 文件路径(默认: 第一个路径名.zip)")
|
|
1137
|
+
p.add_argument("--exclude", nargs="*", default=[], help="要排除的文件模式(如 __pycache__ *.pyc .git)")
|
|
1138
|
+
a = p.parse_args(args)
|
|
1139
|
+
|
|
1140
|
+
import fnmatch
|
|
1141
|
+
excluded = set(a.exclude) if a.exclude else set()
|
|
1142
|
+
|
|
1143
|
+
# 确定输出路径
|
|
1144
|
+
if a.output:
|
|
1145
|
+
out_path = os.path.abspath(a.output)
|
|
1146
|
+
else:
|
|
1147
|
+
first = a.paths[0].rstrip(os.sep)
|
|
1148
|
+
out_path = os.path.abspath(first + ".zip")
|
|
1149
|
+
|
|
1150
|
+
# 确保输出目录存在
|
|
1151
|
+
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
|
|
1152
|
+
|
|
1153
|
+
file_count = 0
|
|
1154
|
+
total_size = 0
|
|
1155
|
+
with zipfile.ZipFile(out_path, 'w', zipfile.ZIP_DEFLATED) as zf:
|
|
1156
|
+
for src in a.paths:
|
|
1157
|
+
src = os.path.abspath(src)
|
|
1158
|
+
if os.path.isfile(src):
|
|
1159
|
+
# 检查是否排除
|
|
1160
|
+
fname = os.path.basename(src)
|
|
1161
|
+
skip = False
|
|
1162
|
+
for pat in excluded:
|
|
1163
|
+
if fnmatch.fnmatch(fname, pat):
|
|
1164
|
+
skip = True
|
|
1165
|
+
break
|
|
1166
|
+
if skip:
|
|
1167
|
+
continue
|
|
1168
|
+
zf.write(src, fname)
|
|
1169
|
+
file_count += 1
|
|
1170
|
+
total_size += os.path.getsize(src)
|
|
1171
|
+
elif os.path.isdir(src):
|
|
1172
|
+
for root, dirs, files in os.walk(src):
|
|
1173
|
+
# 过滤排除的目录
|
|
1174
|
+
dirs[:] = [d for d in dirs if not any(fnmatch.fnmatch(d, p) for p in excluded)]
|
|
1175
|
+
for f in files:
|
|
1176
|
+
if any(fnmatch.fnmatch(f, p) for p in excluded):
|
|
1177
|
+
continue
|
|
1178
|
+
full = os.path.join(root, f)
|
|
1179
|
+
arcname = os.path.relpath(full, os.path.dirname(src))
|
|
1180
|
+
zf.write(full, arcname)
|
|
1181
|
+
file_count += 1
|
|
1182
|
+
total_size += os.path.getsize(full)
|
|
1183
|
+
else:
|
|
1184
|
+
print(f"错误: 路径不存在: {src}", file=sys.stderr)
|
|
1185
|
+
sys.exit(1)
|
|
1186
|
+
|
|
1187
|
+
size_str = f"{total_size / 1024:.1f} KB" if total_size < 1024 * 1024 else f"{total_size / (1024 * 1024):.1f} MB"
|
|
1188
|
+
print(f"压缩完成: {out_path} ({file_count} 个文件, {size_str})")
|
|
1189
|
+
# 输出文件标记,自动发送给用户
|
|
1190
|
+
print(f"__SEND_FILE__{out_path}|压缩文件__END__")
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
async def cmd_unzip(args):
|
|
1194
|
+
"""解压缩 zip 文件"""
|
|
1195
|
+
import argparse
|
|
1196
|
+
import zipfile
|
|
1197
|
+
p = argparse.ArgumentParser(prog="myagent-ai unzip", description="解压缩 zip 文件")
|
|
1198
|
+
p.add_argument("zipfile", help="zip 文件路径")
|
|
1199
|
+
p.add_argument("-o", "--output", default="", help="输出目录(默认: zip 文件所在目录,去除 .zip 后缀)")
|
|
1200
|
+
a = p.parse_args(args)
|
|
1201
|
+
|
|
1202
|
+
zip_path = os.path.abspath(a.zipfile)
|
|
1203
|
+
if not os.path.isfile(zip_path):
|
|
1204
|
+
print(f"错误: 文件不存在: {zip_path}", file=sys.stderr)
|
|
1205
|
+
sys.exit(1)
|
|
1206
|
+
|
|
1207
|
+
# 确定输出目录
|
|
1208
|
+
if a.output:
|
|
1209
|
+
out_dir = os.path.abspath(a.output)
|
|
1210
|
+
else:
|
|
1211
|
+
out_dir = os.path.splitext(zip_path)[0]
|
|
1212
|
+
|
|
1213
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
1214
|
+
|
|
1215
|
+
file_count = 0
|
|
1216
|
+
with zipfile.ZipFile(zip_path, 'r') as zf:
|
|
1217
|
+
zf.extractall(out_dir)
|
|
1218
|
+
file_count = len(zf.namelist())
|
|
1219
|
+
|
|
1220
|
+
print(f"解压完成: {out_dir} ({file_count} 个文件)")
|
|
1095
1221
|
|
|
1096
1222
|
|
|
1097
1223
|
# =============================================================================
|
|
@@ -1164,6 +1290,12 @@ GUI (仅 Windows/macOS):
|
|
|
1164
1290
|
|
|
1165
1291
|
通信:
|
|
1166
1292
|
myagent-ai chat --agent <路径> --message "消息" 向其他Agent发送消息
|
|
1293
|
+
myagent-ai chat --agent <路径> --file <文件路径> 向其他Agent发送文件
|
|
1294
|
+
myagent-ai chat --agent <路径> -m "消息" -f <文件> 同时发送消息和文件
|
|
1295
|
+
|
|
1296
|
+
压缩解压:
|
|
1297
|
+
myagent-ai zip <路径1> [路径2] ... [-o 输出.zip] 压缩文件或文件夹
|
|
1298
|
+
myagent-ai unzip <文件.zip> [-o 输出目录] 解压 zip 文件
|
|
1167
1299
|
|
|
1168
1300
|
媒体播放:
|
|
1169
1301
|
myagent-ai playaudio --url <URL> [--title] 播放在线音频
|