sophhub 0.4.52 → 0.4.53

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sophhub",
3
- "version": "0.4.52",
3
+ "version": "0.4.53",
4
4
  "description": "SophHub CLI - Manage and download AI Agent skills and agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +1,20 @@
1
1
  {
2
2
  "name": "claw-agent-get-send",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "types": ["store"],
5
5
  "displayName": "Claw Agent Get/Send",
6
- "description": "Appia(IM 即时通讯)侧 claw.agent.groups.get / claw.agent.message.send:查询机器人在哪些群聊、向群发纯文本或 Markdown;脚本封装 + HTTP/curl 参考(JWT、msg/md、错误码)",
6
+ "description": "Appia(IM 即时通讯)侧 claw.agent.groups.get / claw.agent.message.send:查询机器人在哪些群聊、向群发纯文本或 Markdown、发送文件附件;脚本封装 + HTTP/curl 参考(JWT、msg/md、错误码)",
7
7
  "changelog": [
8
+ {
9
+ "version": "1.2.0",
10
+ "date": "2026-07-06",
11
+ "changes": [
12
+ "appia_claw.py:新增 send-file 子命令(内部 upload × N → send,一步发送文件附件,支持多文件)",
13
+ "appia_claw.py:删除 upload 子命令与 send --file-ids 参数,从源头杜绝『只上传不发消息』的失败模式",
14
+ "SKILL.md:用法 #8/#9 合并为单条 send-file 示例,注意事项更新",
15
+ "design.md:命令表、脚本职责、注意事项、TC-032–TC-039 同步改为 send-file"
16
+ ]
17
+ },
8
18
  {
9
19
  "version": "1.1.0",
10
20
  "date": "2026-06-16",
@@ -39,5 +49,5 @@
39
49
  }
40
50
  ],
41
51
  "createdAt": "2026-04-28",
42
- "updatedAt": "2026-06-16"
52
+ "updatedAt": "2026-07-06"
43
53
  }
@@ -33,11 +33,10 @@ uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-md --
33
33
  # 7. 从 JSON 文件读 md 数组
34
34
  uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-md --md-file /tmp/md.json --rid "<rid>"
35
35
 
36
- # 8. 上传文件到群聊(不发消息,返回 file._id)
37
- uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json upload report.pdf --rid "<rid>"
38
-
39
- # 9. 上传后带附件发消息(两步:先 upload _id,再 send --file-ids)
40
- uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send --text "请查收附件" --rid "<rid>" --file-ids "<file._id>"
36
+ # 8. 发送文件到群聊(一步到位:内部自动 upload + send,支持一个或多个文件)
37
+ uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-file report.pdf --rid "<rid>" --text "请查收附件"
38
+ # 多文件 + 无正文:
39
+ uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-file a.pdf b.pdf --rid "<rid>"
41
40
  ```
42
41
 
43
42
  不用凭证文件时,可在环境中设置 `CLAW_JWT`、`APP_AGENT_ID`、`CLAW_USER_ID`(及场景需要的 `TARGET_RID` 等),命令相同,省略 `--cred-file …` 即可。
@@ -47,3 +46,4 @@ uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send --tex
47
46
  - 最小凭证字段(JSON 或环境变量):JWT、机器人 `agentId`、创建者 `userId`;勿将填好真实值的文件提交 Git。
48
47
  - 未设置 `APPIA_BASE_URL` 时,脚本默认 `https://sophgo.appia.cn`。
49
48
  - HTTP / curl / 错误码:技能根目录 **`../reference-http.md`**(相对本文件)。
49
+ - 发送文件用 `send-file` 一步完成(内部自动 upload + send);不存在单独的 `upload` 子命令,避免"只上传不发消息"的失败。
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Appia Claw HTTP API: agent.groups.get, agent.message.send (plaintext + md + chunking), agent.file.upload."""
2
+ """Appia Claw HTTP API: agent.groups.get, agent.message.send (plaintext + md + chunking), agent.file.upload + send-file."""
3
3
 
4
4
  from __future__ import annotations
5
5
 
@@ -324,15 +324,11 @@ def cmd_send(args: argparse.Namespace) -> int:
324
324
  else:
325
325
  print("⚠️ 需要 --file 或 --text", file=sys.stderr)
326
326
  return 1
327
- file_ids: list[str] = []
328
- if args.file_ids:
329
- for item in args.file_ids:
330
- file_ids.extend(fid.strip() for fid in item.split(",") if fid.strip())
331
327
  chunk_raw = _cfg("APPIA_MSG_CHUNK_SIZE")
332
328
  chunk_size = int(chunk_raw) if chunk_raw else DEFAULT_CHUNK_SIZE
333
329
  parts = _chunk_text(text, chunk_size)
334
330
  for idx, part in enumerate(parts):
335
- data = _send_plain(rid, part, args.timeout, file_ids=file_ids if file_ids else None)
331
+ data = _send_plain(rid, part, args.timeout)
336
332
  if args.json:
337
333
  print(json.dumps(data, ensure_ascii=False, indent=2))
338
334
  elif len(parts) > 1:
@@ -391,18 +387,31 @@ def cmd_send_md(args: argparse.Namespace) -> int:
391
387
  return 0
392
388
 
393
389
 
394
- def cmd_upload(args: argparse.Namespace) -> int:
395
- """上传文件到群聊(不发消息),返回 file._id send 的 --file-ids 使用。"""
390
+ def cmd_send_file(args: argparse.Namespace) -> int:
391
+ """发送文件到群聊:内部逐个 upload 收集 file._id,再一次性 send 带附件。"""
396
392
  rid = args.rid or _cfg("TARGET_RID")
397
393
  if not rid:
398
- print("⚠️ upload 需要 TARGET_RID 或 --rid", file=sys.stderr)
394
+ print("⚠️ send-file 需要 TARGET_RID 或 --rid", file=sys.stderr)
399
395
  return 1
400
- data = _file_upload(rid, args.file, args.timeout)
396
+ file_ids: list[str] = []
397
+ for fpath in args.files:
398
+ data = _file_upload(rid, fpath, args.timeout)
399
+ finfo = data.get("data", {}).get("file", {})
400
+ fid = finfo.get("_id")
401
+ if not fid:
402
+ print(f"⚠️ 上传失败:未返回 file._id({fpath}),已中止,不发送消息", file=sys.stderr)
403
+ return 1
404
+ file_ids.append(fid)
405
+ if args.json:
406
+ print(json.dumps(data, ensure_ascii=False, indent=2))
407
+ else:
408
+ print(f"uploaded: file._id={fid} name={finfo.get('name')} size={finfo.get('size')} type={finfo.get('type')}")
409
+ text = args.text if args.text is not None else ""
410
+ data = _send_plain(rid, text, args.timeout, file_ids=file_ids)
401
411
  if args.json:
402
412
  print(json.dumps(data, ensure_ascii=False, indent=2))
403
413
  else:
404
- finfo = data.get("data", {}).get("file", {})
405
- print(f"file._id={finfo.get('_id')} name={finfo.get('name')} size={finfo.get('size')} type={finfo.get('type')}")
414
+ print(f"sent: fileIds={file_ids} msg={text!r}")
406
415
  return 0
407
416
 
408
417
 
@@ -425,11 +434,10 @@ def main() -> int:
425
434
  vp = sub.add_parser("verify-target", help="校验 TARGET_RID + TARGET_GROUP_NAME")
426
435
  vp.set_defaults(_run=cmd_verify_target)
427
436
 
428
- sp = sub.add_parser("send", help="POST agent.message.send(纯文本 msg;超出 APPIA_MSG_CHUNK_SIZE 则分多条;可带 --file-ids 附件)")
437
+ sp = sub.add_parser("send", help="POST agent.message.send(纯文本 msg;超出 APPIA_MSG_CHUNK_SIZE 则分多条)")
429
438
  sp.add_argument("--file", "-f", help="长文本文件")
430
439
  sp.add_argument("--text", "-t", help="短文本")
431
440
  sp.add_argument("--rid", help="覆盖 TARGET_RID")
432
- sp.add_argument("--file-ids", nargs="*", help="已上传文件的 _id,多个可用逗号分隔或重复传参")
433
441
  sp.add_argument("--json", action="store_true")
434
442
  sp.set_defaults(_run=cmd_send)
435
443
 
@@ -441,11 +449,12 @@ def main() -> int:
441
449
  smp.add_argument("--json", action="store_true")
442
450
  smp.set_defaults(_run=cmd_send_md)
443
451
 
444
- up = sub.add_parser("upload", help="POST agent.file.upload(上传附件到群聊,返回 file._id)")
445
- up.add_argument("file", help="待上传的文件路径")
446
- up.add_argument("--rid", help="覆盖 TARGET_RID")
447
- up.add_argument("--json", action="store_true")
448
- up.set_defaults(_run=cmd_upload)
452
+ sfp = sub.add_parser("send-file", help="上传文件并作为附件发送到群聊(一步到位:内部 upload × N → send)")
453
+ sfp.add_argument("files", nargs="+", help="待发送的文件路径(一个或多个)")
454
+ sfp.add_argument("--text", "-t", help="消息正文(可选,默认空)")
455
+ sfp.add_argument("--rid", help="覆盖 TARGET_RID")
456
+ sfp.add_argument("--json", action="store_true")
457
+ sfp.set_defaults(_run=cmd_send_file)
449
458
 
450
459
  args = p.parse_args()
451
460
  if args.cred_file: