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.
@@ -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>
@@ -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
- # [v1.23.29] 检测 __CHAT_AGENT__ 标记 — CLI chat 命令输出此标记
241
- # 格式: __CHAT_AGENT__agent_path|agent_name|message__END__
242
- chat_markers = _re.findall(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)__END__', clean_output)
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
- sent_files.append({
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
- await self._emit_sse("v2_chat_agent", {
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
- }, stream_callback)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.23.54",
3
+ "version": "1.23.55",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
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", required=True, help="要发送的消息内容")
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
- # Output chat marker (similar to __SEND_FILE__ pattern)
1094
- print(f"__CHAT_AGENT__{a.agent}|{agent_name}|{a.message}__END__")
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] 播放在线音频
package/start.js CHANGED
@@ -795,6 +795,8 @@ function main() {
795
795
  "playaudio", "playvideo",
796
796
  // Agent 通信
797
797
  "chat",
798
+ // 压缩解压
799
+ "zip", "unzip",
798
800
  // 帮助
799
801
  "help", "-h", "--help",
800
802
  ];