sophhub 0.1.0

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.
Files changed (125) hide show
  1. package/bin/sophhub.js +21 -0
  2. package/package.json +32 -0
  3. package/skills/VERSIONS.md +27 -0
  4. package/skills/builtin/clawhub/SKILL.md +77 -0
  5. package/skills/builtin/flight-booking/SKILL.md +288 -0
  6. package/skills/builtin/flight-booking/scripts/flight_booking.py +1232 -0
  7. package/skills/builtin/inventory-management/SKILL.md +241 -0
  8. package/skills/builtin/inventory-management/scripts/inventory.py +1844 -0
  9. package/skills/builtin/schedule-reminder/SKILL.md +619 -0
  10. package/skills/builtin/schedule-reminder/schedule_template.md +68 -0
  11. package/skills/builtin/schedule-reminder/scripts/append_event.py +204 -0
  12. package/skills/builtin/schedule-reminder/scripts/create_reminders.sh +163 -0
  13. package/skills/builtin/schedule-reminder/scripts/daily_activate.sh +175 -0
  14. package/skills/builtin/schedule-reminder/scripts/parse_schedule.py +704 -0
  15. package/skills/builtin/schedule-reminder/scripts/setup.sh +242 -0
  16. package/skills/builtin/schedule-reminder//347/224/250/346/210/267/346/214/207/345/215/227.md +311 -0
  17. package/skills/builtin/skill-creator/SKILL.md +370 -0
  18. package/skills/builtin/skill-creator/license.txt +202 -0
  19. package/skills/builtin/skill-creator/scripts/init_skill.py +378 -0
  20. package/skills/builtin/skill-creator/scripts/package_skill.py +111 -0
  21. package/skills/builtin/skill-creator/scripts/quick_validate.py +101 -0
  22. package/skills/builtin/sophnet-customer-management/SKILL.md +271 -0
  23. package/skills/builtin/sophnet-customer-management/pyproject.toml +15 -0
  24. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/__init__.py +2 -0
  25. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/__main__.py +5 -0
  26. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/cli.py +67 -0
  27. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/__init__.py +2 -0
  28. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/customer.py +60 -0
  29. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/export_file.py +18 -0
  30. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/import_file.py +15 -0
  31. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/reminder.py +26 -0
  32. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/schema.py +28 -0
  33. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/config.py +54 -0
  34. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/__init__.py +2 -0
  35. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/exporter.py +85 -0
  36. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/models.py +84 -0
  37. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/normalizer.py +144 -0
  38. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/parser.py +241 -0
  39. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/query.py +109 -0
  40. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/reminder.py +121 -0
  41. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/repository.py +397 -0
  42. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/schema.py +106 -0
  43. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/service.py +565 -0
  44. package/skills/builtin/sophnet-customer-management/uv.lock +48 -0
  45. package/skills/builtin/sophnet-customized-marketing/SKILL.md +144 -0
  46. package/skills/builtin/sophnet-customized-marketing/playbooks/campaign-planning.md +187 -0
  47. package/skills/builtin/sophnet-customized-marketing/playbooks/content-generation.md +124 -0
  48. package/skills/builtin/sophnet-customized-marketing/playbooks/marketing-calendar.md +59 -0
  49. package/skills/builtin/sophnet-customized-marketing/playbooks/multi-channel-bundle.md +94 -0
  50. package/skills/builtin/sophnet-customized-marketing/playbooks/poster-generation.md +182 -0
  51. package/skills/builtin/sophnet-customized-marketing/playbooks/style-profile-workflow.md +103 -0
  52. package/skills/builtin/sophnet-customized-marketing/pyproject.toml +9 -0
  53. package/skills/builtin/sophnet-customized-marketing/references/campaign-mechanics.md +168 -0
  54. package/skills/builtin/sophnet-customized-marketing/references/content-safety.md +26 -0
  55. package/skills/builtin/sophnet-customized-marketing/references/marketing-date-checklist.md +99 -0
  56. package/skills/builtin/sophnet-customized-marketing/references/platform-writing-guidelines.md +88 -0
  57. package/skills/builtin/sophnet-customized-marketing/references/quality-checklist.md +44 -0
  58. package/skills/builtin/sophnet-customized-marketing/scripts/generate_poster.py +585 -0
  59. package/skills/builtin/sophnet-customized-marketing/scripts/style_profile.py +215 -0
  60. package/skills/builtin/sophnet-face-search/SKILL.md +115 -0
  61. package/skills/builtin/sophnet-face-search/pyproject.toml +11 -0
  62. package/skills/builtin/sophnet-face-search/scripts/face_search.py +336 -0
  63. package/skills/builtin/sophnet-face-search/uv.lock +508 -0
  64. package/skills/builtin/sophnet-image-edit/SKILL.md +140 -0
  65. package/skills/builtin/sophnet-image-edit/pyproject.toml +9 -0
  66. package/skills/builtin/sophnet-image-edit/scripts/edit_and_preview.sh +68 -0
  67. package/skills/builtin/sophnet-image-edit/scripts/edit_image.py +279 -0
  68. package/skills/builtin/sophnet-image-edit/uv.lock +234 -0
  69. package/skills/builtin/sophnet-image-generate/SKILL.md +62 -0
  70. package/skills/builtin/sophnet-image-generate/pyproject.toml +9 -0
  71. package/skills/builtin/sophnet-image-generate/scripts/generate_image.py +156 -0
  72. package/skills/builtin/sophnet-image-generate/uv.lock +234 -0
  73. package/skills/builtin/sophnet-image-ocr/SKILL.md +167 -0
  74. package/skills/builtin/sophnet-image-ocr/pyproject.toml +13 -0
  75. package/skills/builtin/sophnet-image-ocr/scripts/ocr.py +226 -0
  76. package/skills/builtin/sophnet-image-ocr/uv.lock +234 -0
  77. package/skills/builtin/sophnet-infinite-talk/SKILL.md +140 -0
  78. package/skills/builtin/sophnet-infinite-talk/pyproject.toml +9 -0
  79. package/skills/builtin/sophnet-infinite-talk/scripts/gen.py +172 -0
  80. package/skills/builtin/sophnet-oss/SKILL.md +109 -0
  81. package/skills/builtin/sophnet-oss/pyproject.toml +8 -0
  82. package/skills/builtin/sophnet-oss/scripts/upload_file.py +43 -0
  83. package/skills/builtin/sophnet-qa-install/SKILL.md +210 -0
  84. package/skills/builtin/sophnet-qa-install/pyproject.toml +6 -0
  85. package/skills/builtin/sophnet-qa-install/scripts/backup_md.py +35 -0
  86. package/skills/builtin/sophnet-qa-install/scripts/check_installed.py +143 -0
  87. package/skills/builtin/sophnet-qa-install/scripts/update_config.py +142 -0
  88. package/skills/builtin/sophnet-qa-install/scripts/update_md.py +73 -0
  89. package/skills/builtin/sophnet-training-install/SKILL.md +211 -0
  90. package/skills/builtin/sophnet-training-install/pyproject.toml +6 -0
  91. package/skills/builtin/sophnet-training-install/scripts/backup_md.py +35 -0
  92. package/skills/builtin/sophnet-training-install/scripts/check_installed.py +144 -0
  93. package/skills/builtin/sophnet-training-install/scripts/update_config.py +142 -0
  94. package/skills/builtin/sophnet-training-install/scripts/update_md.py +73 -0
  95. package/skills/builtin/sophnet-tts/SKILL.md +79 -0
  96. package/skills/builtin/sophnet-tts/pyproject.toml +9 -0
  97. package/skills/builtin/sophnet-tts/scripts/gen_tts.py +130 -0
  98. package/skills/builtin/sophnet-video-generate/SKILL.md +116 -0
  99. package/skills/builtin/sophnet-video-generate/scripts/gen_video.py +304 -0
  100. package/skills/builtin/video-understand/SKILL.md +79 -0
  101. package/skills/builtin/video-understand/scripts/video_understand.py +204 -0
  102. package/skills/builtin/weather/SKILL.md +112 -0
  103. package/skills/builtin/web-scraper/SKILL.md +101 -0
  104. package/skills/builtin/web-scraper/scripts/scrape.py +270 -0
  105. package/skills/builtin/website-builder/SKILL.md +266 -0
  106. package/skills/builtin/website-builder/scripts/deploy_site.sh +46 -0
  107. package/skills/store/didi-ride/SKILL.md +309 -0
  108. package/skills/store/didi-ride/_meta.json +6 -0
  109. package/skills/store/didi-ride/assets/PREFERENCE.md +58 -0
  110. package/skills/store/didi-ride/package.json +15 -0
  111. package/skills/store/didi-ride/references/api_references.md +171 -0
  112. package/skills/store/didi-ride/references/error_handling.md +68 -0
  113. package/skills/store/didi-ride/references/setup.md +73 -0
  114. package/skills/store/didi-ride/references/workflow.md +150 -0
  115. package/skills/store/flyai/SKILL.md +119 -0
  116. package/skills/store/flyai/references/fliggy-fast-search.md +53 -0
  117. package/skills/store/flyai/references/search-flight.md +89 -0
  118. package/skills/store/flyai/references/search-hotels.md +57 -0
  119. package/skills/store/flyai/references/search-poi.md +49 -0
  120. package/src/commands/download.js +103 -0
  121. package/src/commands/list.js +67 -0
  122. package/src/utils/config.js +24 -0
  123. package/src/utils/gitlab.js +67 -0
  124. package/src/utils/paths.js +19 -0
  125. package/src/utils/versions.js +38 -0
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: sophnet-infinite-talk
3
+ description: 当用户需要生成数字人视频(照片 + 音频驱动说话)时使用。接收人物形象图片 URL 和驱动音频 URL,在主会话中创建任务,然后在子会话中轮询查询任务进度,任务完成或失败后将结果报回主会话。
4
+ ---
5
+
6
+ # Sophnet Infinite Talk(数字人)
7
+
8
+ ## 概述
9
+
10
+ 使用 `scripts/gen.py` 调用 Sophnet InfiniteTalk 接口,通过图片 + 音频驱动生成数字人说话视频。
11
+
12
+ 脚本路径:`{baseDir}/scripts/gen.py`
13
+
14
+ ## 何时使用
15
+
16
+ - 用户要求"生成数字人视频"、"让照片说话"、"图片 + 音频合成视频"
17
+ - 用户提供了人物照片和音频,希望生成说话视频
18
+ - 用户想要数字人、虚拟人、照片驱动等功能
19
+
20
+ ## 输入参数
21
+
22
+ | 参数 | 必填 | 说明 |
23
+ |------|------|------|
24
+ | `--image-url` | 是 | 人物形象图片 URL 或本地文件路径(本地文件会自动上传 OSS 转为 URL) |
25
+ | `--audio-url` | 是 | 驱动音频 URL 或本地文件路径(本地文件会自动上传 OSS 转为 URL) |
26
+ | `--prompt` | 否 | 动作/表情提示词(默认: "自信的面对镜头") |
27
+
28
+ ## 提取本地文件路径(如适用)
29
+
30
+ 当用户在聊天中上传图片或音频时,文件通常保存在工作区的 `media/inbound/images/` 或 `media/inbound/audio/` 目录下。可通过 Media Understanding 日志获取确切的绝对路径。
31
+
32
+ 典型日志格式:
33
+
34
+ ```
35
+ [Media Understanding] Resolved relative path: "media/inbound/images/xxx.jpg" -> "/absolute/path/to/workspace/media/inbound/images/xxx.jpg"
36
+ ```
37
+
38
+ 使用解析后的绝对路径作为 `--image-url` 或 `--audio-url` 参数(脚本会自动上传 OSS 转为 URL)。
39
+
40
+ ## 处理流程(必须遵循)
41
+
42
+ ### 第一步:主会话 — 创建任务
43
+
44
+ 在主会话中调用 `create` 子命令提交任务:
45
+
46
+ ```bash
47
+ # 使用 URL
48
+ uv run {baseDir}/scripts/gen.py create \
49
+ --image-url "https://example.com/photo.png" \
50
+ --audio-url "https://example.com/audio.mp3" \
51
+ --prompt "自信的面对镜头"
52
+
53
+ # 使用本地文件(自动上传 OSS)
54
+ uv run {baseDir}/scripts/gen.py create \
55
+ --image-url "/path/to/photo.png" \
56
+ --audio-url "/path/to/audio.mp3"
57
+ ```
58
+
59
+ 成功输出示例:
60
+
61
+ ```json
62
+ {"task_id": "7iOckhZZjgx6gzbESAqL1n"}
63
+ ```
64
+
65
+ - 记录 `task_id`,向用户回复:**任务创建成功,已经创建子会话进行任务监控,完成后会通知您**
66
+ - 如果返回 `error` 字段,直接告知用户失败原因。
67
+
68
+ ### 第二步:创建子会话 — 轮询查询任务
69
+
70
+ 脚本的 `query` 子命令执行**单次查询**并返回结果,**轮询循环由调用方(子会话)负责实现**。每 10 秒调用一次 `query`,最多轮询 360 次(约 1 小时超时):
71
+
72
+ ```bash
73
+ uv run {baseDir}/scripts/gen.py query --task-id "<task_id>"
74
+ ```
75
+
76
+ 可能的返回值:
77
+
78
+ - 进行中:`{"status": "processing", "video_url": null}`
79
+ - 成功:`{"status": "succeeded", "video_url": "https://..."}`
80
+ - 失败:`{"status": "failed", "error": "失败原因"}`
81
+
82
+ 轮询规则:
83
+ - `status` 为 `succeeded` 且 `video_url` 非空 → 停止轮询,获取 `video_url`
84
+ - `status` 为 `failed` 或包含 `error` → 停止轮询,获取错误信息
85
+ - 单次查询网络错误(返回 `{"error": "..."}` 无 `status` 字段)→ 视为临时失败,继续轮询(连续 3 次网络错误再终止)
86
+ - 其他状态(如 `processing`、`queued`)→ 等待 10 秒后继续查询
87
+
88
+ ### 第三步:报告结果到主会话
89
+
90
+ 将子会话获取的最终结果报回主会话:
91
+
92
+ - **成功**:将 `video_url` 展示给用户
93
+ - **失败**:将错误信息展示给用户,引导重试
94
+
95
+ ### 第四步:自动清理子会话(必须执行)
96
+
97
+ 在子会话完成结果回传后,**必须立即自动删除该子会话**,避免会话资源残留。
98
+
99
+ 清理规则:
100
+ - 成功回传主会话后,立即删除子会话。
101
+ - 失败回传主会话后,立即删除子会话。
102
+ - 超时回传主会话后,立即删除子会话。
103
+ - 删除失败时,向主会话报告清理失败原因,并重试 1 次。
104
+
105
+ ## 输出格式
106
+
107
+ 成功:
108
+
109
+ ```text
110
+ 🎬 数字人视频生成成功!
111
+ 📸 使用图片: ![image](image_url)
112
+ 🎵 使用音频: <audio controls src="audio_url"></audio>
113
+ 🎭 动作提示: [prompt]
114
+ ✅ 生成视频: <video controls width="100%" src="video_url"></video>
115
+ ⚠️ 提示: 本内容由AI生成,视频保存24小时,请及时下载保存!
116
+ ```
117
+
118
+ 失败:
119
+
120
+ ```text
121
+ ❌ 数字人视频生成失败
122
+ 原因: [error]
123
+ 💡 建议: 请检查图片和音频链接是否有效,或稍后重试
124
+ ```
125
+
126
+ ## 失败处理
127
+
128
+ - 若创建任务失败,直接告知用户失败原因。
129
+ - 若轮询超时(1 小时),告知用户任务仍在处理中,可稍后重试。
130
+ - 若单次查询网络错误,继续重试;连续 3 次网络错误再终止轮询并告知用户。
131
+ - 引导用户检查图片和音频链接是否有效。
132
+
133
+ ## 注意事项
134
+
135
+ - `--image-url` 和 `--audio-url` 必填,缺少任何一个都无法创建任务。
136
+ - `--prompt` 可选,有默认值 "自信的面对镜头",用于控制数字人的动作和表情。
137
+ - 任务创建(主会话)和轮询查询(子会话)分两步执行,避免主会话长时间阻塞。
138
+ - 子会话在最终结果回传主会话后必须自动删除,不能保留空闲子会话。
139
+ - 脚本所有输出均为 JSON 格式(包括错误),便于程序化解析。
140
+ - 视频生成可能需要较长时间,轮询超时上限为 1 小时,链接有效期 24 小时。
@@ -0,0 +1,9 @@
1
+ [project]
2
+ name = "sophnet-infinite-talk"
3
+ version = "0.1.0"
4
+ description = "Digital human video generation using Sophnet InfiniteTalk API"
5
+ requires-python = ">=3.10"
6
+ dependencies = [
7
+ "requests>=2.28.0",
8
+ "sophnet-tools>=0.0.1",
9
+ ]
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Sophnet Infinite Talk (Digital Human) Script
4
+
5
+ Two-phase workflow:
6
+ create: submit a task, return task_id
7
+ query: poll task status by task_id
8
+
9
+ Usage:
10
+ python gen.py create --image-url <url> --audio-url <url> [--prompt <text>]
11
+ python gen.py query --task-id <id>
12
+ """
13
+ import os
14
+ import sys
15
+ import argparse
16
+ import json
17
+ import requests
18
+ from typing import Optional, Any, Dict
19
+ import sophnet_tools
20
+
21
+ TASK_URL = "https://www.sophnet.com/api/open-apis/projects/easyllms/videogenerator/task"
22
+ DEFAULT_PROMPT = "自信的面对镜头"
23
+
24
+
25
+ def _is_url(path: str) -> bool:
26
+ return path.startswith(("http://", "https://"))
27
+
28
+
29
+ def _resolve_to_url(path: str, label: str) -> str:
30
+ """本地文件上传 OSS 转为 URL;已经是 URL 则直接返回。失败时抛出 ValueError。"""
31
+ if _is_url(path):
32
+ return path
33
+ if not os.path.isfile(path):
34
+ raise ValueError(f"{label}文件不存在: {path}")
35
+ url = sophnet_tools.upload_oss(path)
36
+ if not url:
37
+ raise ValueError(f"{label}文件上传 OSS 失败: {path}")
38
+ return url
39
+
40
+
41
+ def _headers(api_key: str) -> dict:
42
+ return {
43
+ "Authorization": f"Bearer {api_key}",
44
+ "Content-Type": "application/json",
45
+ }
46
+
47
+
48
+ def create_task(
49
+ api_key: str, image_url: str, audio_url: str, prompt: str = DEFAULT_PROMPT
50
+ ) -> Dict[str, Any]:
51
+ """提交数字人视频任务,返回 {"task_id": "..."} 或 {"error": "..."}。"""
52
+ payload = {
53
+ "model": "InfiniteTalk",
54
+ "content": [
55
+ {"type": "text", "text": prompt},
56
+ {
57
+ "type": "image_url",
58
+ "image_url": {"url": image_url},
59
+ "role": "first_frame",
60
+ },
61
+ {"type": "audio_url", "audio_url": audio_url},
62
+ ],
63
+ }
64
+ try:
65
+ resp = requests.post(
66
+ TASK_URL, headers=_headers(api_key), json=payload, timeout=60
67
+ )
68
+ resp.raise_for_status()
69
+ task_id = resp.text.strip()
70
+ if task_id:
71
+ return {"task_id": task_id}
72
+ return {"error": f"未能从响应中解析 task_id: {task_id}"}
73
+ except requests.exceptions.RequestException as e:
74
+ return {"error": f"HTTP 请求失败: {e}"}
75
+
76
+
77
+ def _normalize_video_url(raw: Any) -> Optional[str]:
78
+ """API 在排队时可能返回字符串 'None',视为无效。"""
79
+ if raw is None:
80
+ return None
81
+ s = str(raw).strip()
82
+ return None if s in ("", "None", "none") else s
83
+
84
+
85
+ def get_task(api_key: str, task_id: str) -> Dict[str, Any]:
86
+ """
87
+ 查询任务状态。
88
+
89
+ 返回:
90
+ 进行中: {"status": "processing", "video_url": null}
91
+ 成功: {"status": "succeeded", "video_url": "<url>"}
92
+ 失败: {"status": "failed", "error": "<reason>"}
93
+ """
94
+ try:
95
+ resp = requests.get(
96
+ f"{TASK_URL}/{task_id}", headers=_headers(api_key), timeout=30
97
+ )
98
+ resp.raise_for_status()
99
+ data = resp.json()
100
+ except requests.exceptions.RequestException as e:
101
+ return {"error": f"HTTP 请求失败: {e}"}
102
+
103
+ status = (data.get("status") or "").strip()
104
+ video_url = _normalize_video_url((data.get("content") or {}).get("video_url"))
105
+
106
+ if status == "succeeded":
107
+ if video_url:
108
+ return {"status": "succeeded", "video_url": video_url}
109
+ return {"status": "failed", "error": "任务标记为成功但未返回有效视频地址"}
110
+
111
+ if "failed" in status.lower():
112
+ err = (
113
+ data.get("message")
114
+ or data.get("error")
115
+ or (data.get("content") or {}).get("error")
116
+ or status
117
+ )
118
+ return {"status": "failed", "error": str(err)}
119
+
120
+ return {"status": status, "video_url": video_url}
121
+
122
+
123
+ def _json_out(obj: Dict[str, Any]) -> None:
124
+ print(json.dumps(obj, ensure_ascii=False))
125
+
126
+
127
+ if __name__ == "__main__":
128
+ api_key = sophnet_tools.get_api_key()
129
+ if api_key is None:
130
+ _json_out(
131
+ {"error": "未找到 Sophnet API Key,请联系客户支持"}
132
+ )
133
+ sys.exit(1)
134
+
135
+ parser = argparse.ArgumentParser(description="Sophnet Infinite Talk (数字人)")
136
+ sub = parser.add_subparsers(dest="command", required=True)
137
+
138
+ p_create = sub.add_parser("create", help="创建数字人视频任务")
139
+ p_create.add_argument(
140
+ "--image-url", required=True, help="人物形象图片 URL 或本地路径"
141
+ )
142
+ p_create.add_argument(
143
+ "--audio-url", required=True, help="驱动音频 URL 或本地路径"
144
+ )
145
+ p_create.add_argument(
146
+ "--prompt",
147
+ default=DEFAULT_PROMPT,
148
+ help=f"动作/表情提示词(默认: {DEFAULT_PROMPT})",
149
+ )
150
+
151
+ p_query = sub.add_parser("query", help="查询数字人任务状态")
152
+ p_query.add_argument("--task-id", required=True, help="任务 ID")
153
+
154
+ args = parser.parse_args()
155
+
156
+ if args.command == "create":
157
+ try:
158
+ image_url = _resolve_to_url(args.image_url, "图片")
159
+ audio_url = _resolve_to_url(args.audio_url, "音频")
160
+ except ValueError as e:
161
+ _json_out({"error": str(e)})
162
+ sys.exit(1)
163
+ result = create_task(api_key, image_url, audio_url, args.prompt)
164
+ _json_out(result)
165
+ if "error" in result:
166
+ sys.exit(1)
167
+
168
+ elif args.command == "query":
169
+ result = get_task(api_key, args.task_id)
170
+ _json_out(result)
171
+ if "error" in result:
172
+ sys.exit(1)
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: sophnet-oss
3
+ description: Convert a local file path to a downloadable URL (valid for 24 hours). Use when users want to turn a file path into a shareable link, get a URL for a file, or need a temporary URL to access a local file.
4
+ ---
5
+
6
+ # File Path to URL
7
+
8
+ 将本地文件路径转换为可访问的下载 URL,链接有效期为 24 小时。
9
+
10
+ ## Processing Mode
11
+
12
+ **STRICT SERIAL PROCESSING ONLY** - This skill must execute in a single, sequential operation:
13
+
14
+ - **NO sub-tasks** - Never spawn background sessions or use sessions_spawn
15
+ - **NO task splitting** - Complete the conversion in one continuous run
16
+ - **NO parallel execution** - Convert one file at a time
17
+
18
+ ## Prerequisites
19
+
20
+ - Python packages: sophnet-tools
21
+
22
+ ## Usage
23
+
24
+ When a user wants to convert a file path to a URL:
25
+
26
+ 1. **Extract file path** from user input or recent logs (see Path Extraction below).
27
+ 2. **Run the script** using uv from workspace or skill directory:
28
+
29
+ ```bash
30
+ uv run --with sophnet-tools \
31
+ python {baseDir}/scripts/upload_file.py \
32
+ --file /absolute/path/to/file \
33
+ --timeout 120
34
+ ```
35
+
36
+ ## Parameters
37
+
38
+ - `--file`: 要转换为 URL 的本地文件路径(必填)
39
+ - `--timeout`: 请求超时秒数(默认 120,大文件可适当增大)
40
+
41
+ ## Output
42
+
43
+ The script outputs structured key-value pairs:
44
+
45
+ ```
46
+ FILE_PATH=/absolute/path/to/file
47
+ UPLOAD_STATUS=uploaded
48
+ DOWNLOAD_URL=https://...signed-url...
49
+ ```
50
+
51
+ On failure:
52
+
53
+ ```
54
+ FILE_PATH=/absolute/path/to/file
55
+ UPLOAD_STATUS=failed
56
+ ERROR=error description
57
+ ```
58
+
59
+ **转换成功后,向用户展示为:**
60
+
61
+ ```
62
+ 文件路径已转换为可访问链接:
63
+
64
+ 文件路径:/absolute/path/to/file
65
+ 下载链接:https://...signed-url...
66
+
67
+ ⏰ 链接有效期:24小时
68
+ ```
69
+
70
+ ## Path Extraction
71
+
72
+ When users provide files through chat, look for log patterns like:
73
+
74
+ ```
75
+ Resolved relative path: "media/inbound/images/xxx.pdf" -> "/absolute/path/to/workspace/media/inbound/images/xxx.pdf"
76
+ ```
77
+
78
+ Extract the absolute path (right side of `->`) for use with the script.
79
+
80
+ 对于用户提供或引用的文件,优先从上述日志中取解析后的绝对路径,再传入脚本做路径转 URL。
81
+
82
+ ## Example Workflow
83
+
84
+ User: "把这个文件转成链接" / "给我这个文件的 URL"
85
+
86
+ 1. Extract file path from logs (pattern: `Resolved relative path: "..." -> "/absolute/..."`) or user input
87
+ 2. Run: `uv run --with sophnet-tools python {baseDir}/scripts/upload_file.py --file /absolute/path/to/file --timeout 120`
88
+ 3. Parse the output and display the URL to the user
89
+
90
+ ## Notes
91
+
92
+ - 返回的 URL 有效期为 **24 小时**
93
+ - 任意类型文件均可转换(无扩展名限制)
94
+ - 大文件可增大 `--timeout`(默认 120 秒)
95
+ - 始终使用绝对路径以保证可靠
96
+
97
+ ## ⚠️ FORBIDDEN OPERATIONS
98
+
99
+ **Do NOT:**
100
+
101
+ - Spawn sub-agent sessions (sessions_spawn)
102
+ - Run multiple parallel conversions
103
+ - Split the task into separate operations
104
+ - Use background processes
105
+
106
+ **ONLY:**
107
+
108
+ - Run the script once with the file path
109
+ - Return the URL to the user in a single response
@@ -0,0 +1,8 @@
1
+ [project]
2
+ name = "sophnet-oss"
3
+ version = "0.1.0"
4
+ description = "Skill-local runtime dependencies for sophnet-oss"
5
+ requires-python = ">=3.10"
6
+ dependencies = [
7
+ "sophnet-tools>=0.0.1",
8
+ ]
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ """Upload a file to Sophnet OSS and print the signed download URL."""
3
+
4
+ import argparse
5
+ import sys
6
+ import os
7
+
8
+ def main():
9
+ parser = argparse.ArgumentParser(description="Upload a file to Sophnet OSS")
10
+ parser.add_argument("--file", required=True, help="Path to the file to upload")
11
+ parser.add_argument("--timeout", type=int, default=120, help="Request timeout in seconds (default: 120)")
12
+ args = parser.parse_args()
13
+
14
+ file_path = os.path.abspath(os.path.expanduser(args.file))
15
+
16
+ if not os.path.isfile(file_path):
17
+ print(f"FILE_PATH={file_path}")
18
+ print("UPLOAD_STATUS=failed")
19
+ print(f"ERROR=file_not_found: {file_path}")
20
+ sys.exit(1)
21
+
22
+ try:
23
+ import sophnet_tools
24
+ except ImportError:
25
+ print(f"FILE_PATH={file_path}")
26
+ print("UPLOAD_STATUS=failed")
27
+ print("ERROR=sophnet-tools package not installed")
28
+ sys.exit(1)
29
+
30
+ signed_url = sophnet_tools.upload_oss(file_path, timeout=args.timeout)
31
+
32
+ if signed_url:
33
+ print(f"FILE_PATH={file_path}")
34
+ print("UPLOAD_STATUS=uploaded")
35
+ print(f"DOWNLOAD_URL={signed_url}")
36
+ else:
37
+ print(f"FILE_PATH={file_path}")
38
+ print("UPLOAD_STATUS=failed")
39
+ print("ERROR=upload_failed_no_url_returned")
40
+ sys.exit(1)
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,210 @@
1
+ ---
2
+ name: sophnet-qa-install
3
+ description: 创建或更新智能客服系统(知识管理员 + 问答助手),包括工作空间初始化、模板下载、角色配置和配置文件注入。当用户要求创建客服系统、更新客服系统、智能问答、知识库客服时使用。
4
+ version: 1.0.3
5
+ ---
6
+
7
+ # 智能客服系统创建与更新
8
+
9
+ 创建或更新包含「知识管理员」和「问答助手」两个 Agent 的智能客服系统。创建和更新共用同一套流程(Step 2~6),区别仅在于:新安装需收集参数(Step 1),更新则复用已有配置并需用户确认。
10
+
11
+ ## Processing Mode
12
+
13
+ **STRICT SERIAL PROCESSING ONLY** — 按顺序逐步执行,不要拆分子任务或并行处理。
14
+
15
+ ## Prerequisites
16
+
17
+ - Python 3.10+
18
+ - unzip
19
+
20
+ ## 流程
21
+
22
+ ### Step 0: 校验安装状态
23
+
24
+ 执行校验脚本,根据返回状态决定后续流程。`{version}` 取自本文件 frontmatter 中的 `version` 字段:
25
+
26
+ ```bash
27
+ uv run {baseDir}/scripts/check_installed.py --version {version}
28
+ ```
29
+
30
+ 脚本输出三种状态:
31
+
32
+ #### 退出码 0 — `INSTALLED`(已安装,无需操作)
33
+
34
+ 向用户展示以下提示,并**终止流程**(不再执行 Step 1~6)。其中「客服子系统的API Secret」从 `openclaw.json` 的 `channels.bot-api.accounts["qa-agent"].apiSecret` 读取(与 `scripts/update_config.py` 中 `get_api_secret` 一致)。
35
+
36
+ ```
37
+ 智能客服系统已经安装,无需重复安装。
38
+
39
+ 📂 知识库目录:/home/node/.openclaw/workspace-knowledge/knowledge
40
+ 🤖 管理员会话:knowledge-admin
41
+ 💁 客服助手会话:qa-agent
42
+ 🔑 客服子系统的API Secret: <api_secret>
43
+
44
+ 📝 常用命令(在 knowledge-admin 会话中使用):
45
+ • 通过附件按钮上传文件,并输入:将文档加入知识库
46
+ • 将 https://xxx 中的内容加入到知识库
47
+ • 将 xxx 写入到知识库
48
+ • 昨天客户都问了哪些问题
49
+ • 昨天客户都有哪些反馈
50
+ • 删除 xxx 知识库
51
+ ```
52
+
53
+ #### 退出码 2 — `UPDATABLE`(可更新)
54
+
55
+ 文件已存在但版本低于目标版本,有新版本可用。脚本会额外输出已有配置中的 `ADMIN_NAME` 和 `QA_NAME`(如 `ADMIN_NAME=客服管理员`、`QA_NAME=客服助手`)。
56
+
57
+ **必须先向用户确认,禁止直接更新。** 向用户展示以下提示并等待回复。其中 `{current_version}` 从 `check_installed.py` 输出或 `workspace-knowledge/git_version` 文件获取:
58
+
59
+ ```
60
+ 智能客服系统检测到新版本可用({current_version} → {version})。
61
+
62
+ ⚠️ 更新说明:
63
+ • 更新不会修改知识库内容
64
+ • 更新会重置 Agent 设置(角色配置等将恢复为模板默认值)
65
+ • 更新前会自动备份当前 Agent 配置到 backup_v{current_version} 目录
66
+
67
+ 是否确认更新?请回复「确认更新」或「取消」。
68
+ ```
69
+
70
+ - 若用户回复确认(如"确认更新"、"确认"、"更新"、"好的"、"是"等肯定回复):先执行配置备份,再**跳过 Step 1**,使用脚本输出的 `admin_name` 和 `qa_name`,从 Step 2 开始执行更新流程。
71
+ - 若用户回复取消或其他非肯定回复:**终止流程**,不执行任何更新操作。
72
+
73
+ ##### 备份配置(用户确认后、Step 2 前执行)
74
+
75
+ 依次备份主工作空间和 workspace-qa 目录下的 `.md` 配置文件:
76
+
77
+ ```bash
78
+ uv run {baseDir}/scripts/backup_md.py \
79
+ --current-version {current_version} \
80
+ --dir /home/node/.openclaw/workspace-knowledge
81
+ ```
82
+
83
+ ```bash
84
+ uv run {baseDir}/scripts/backup_md.py \
85
+ --current-version {current_version} \
86
+ --dir /home/node/.openclaw/workspace-knowledge/workspace-qa
87
+ ```
88
+
89
+ 脚本成功后会输出备份目录路径(如 `备份成功: /home/node/.openclaw/workspace-knowledge/backup_v1.0.1`)。向用户展示备份结果:
90
+
91
+ ```
92
+ ✅ 当前 Agent 配置已备份到:
93
+ • {backup_dir_1}
94
+ 正在开始更新...
95
+ ```
96
+
97
+ 备份完成后,继续执行 Step 2~6。
98
+
99
+ #### 退出码 1 — `NOT_INSTALLED`(未安装)
100
+
101
+ 未安装或未完整安装,继续执行 Step 1 开始完整安装流程。
102
+
103
+ > 校验逻辑:先检查 Step 2 目录与软链接是否存在;若存在则比对 `workspace-knowledge/git_version` 与目标版本号;再检查 openclaw.json 中是否已配置 knowledge-admin 与 qa-agent。详见 `scripts/check_installed.py`。
104
+
105
+ ### Step 1: 收集参数
106
+
107
+ 向用户确认以下两个角色名称(有默认值,用户可直接回车跳过):
108
+
109
+ | 参数 | 说明 | 默认值 |
110
+ | ------------ | ------------------ | ---------- |
111
+ | `admin_name` | 知识管理员显示名称 | 客服管理员 |
112
+ | `qa_name` | 客服助手显示名称 | 客服助手 |
113
+
114
+ ### Step 2: 创建目录结构
115
+
116
+ ```bash
117
+ mkdir -p /home/node/.openclaw/workspace-knowledge/workspace-qa/
118
+ mkdir -p /home/node/.openclaw/workspace-knowledge/knowledge/
119
+ ```
120
+
121
+ 在 workspace-qa 下创建指向上级 knowledge 的软链接:
122
+
123
+ ```bash
124
+ cd /home/node/.openclaw/workspace-knowledge/workspace-qa/
125
+ ln -sf ../knowledge knowledge
126
+ ```
127
+
128
+ ### Step 3: 下载模板文件
129
+
130
+ ```bash
131
+ cd /home/node/.openclaw/workspace-knowledge/
132
+ wget -O workspace-knowledge.zip https://oss-upload-temp.sophnet.com/workspace-knowledge_v{version}.zip
133
+ unzip -o workspace-knowledge.zip
134
+ rm -f workspace-knowledge.zip
135
+ ```
136
+
137
+ ### Step 4: 替换占位符
138
+
139
+ 使用脚本替换 .md 文件中的 `{{客服助手}}` 和 `{{知识库管理员}}` 占位符。
140
+
141
+ 先替换主工作空间目录(不含子文件夹):
142
+
143
+ ```bash
144
+ uv run {baseDir}/scripts/update_md.py \
145
+ --dir /home/node/.openclaw/workspace-knowledge \
146
+ --qa-name "{qa_name}" \
147
+ --admin-name "{admin_name}"
148
+ ```
149
+
150
+ 再替换 workspace-qa 目录(不含子文件夹):
151
+
152
+ ```bash
153
+ uv run {baseDir}/scripts/update_md.py \
154
+ --dir /home/node/.openclaw/workspace-knowledge/workspace-qa \
155
+ --qa-name "{qa_name}" \
156
+ --admin-name "{admin_name}"
157
+ ```
158
+
159
+ > `{baseDir}` 为本 skill 所在目录,`{qa_name}` 和 `{admin_name}` 替换为 Step 1 收集的值。
160
+
161
+ ### Step 5: 更新 openclaw.json
162
+
163
+ ```bash
164
+ uv run {baseDir}/scripts/update_config.py \
165
+ --admin-name "{admin_name}" \
166
+ --qa-name "{qa_name}"
167
+ ```
168
+
169
+ ### Step 6: 完成提示
170
+
171
+ 全部执行成功后,向用户展示以下信息。其中「客服子系统的API Secret」取 Step 5 中 `update_config.py` 输出里的「客服助手 API Secret」;若未保留该输出,可从 `openclaw.json` 的 `channels.bot-api.accounts["qa-agent"].apiSecret` 读取(与 `scripts/update_config.py` 中 `get_api_secret` 一致)。
172
+
173
+ ```
174
+ ✅ 您的智能客服系统已创建完成!
175
+
176
+ 📂 知识库目录:/home/node/.openclaw/workspace-knowledge/knowledge
177
+ 🤖 管理员会话:knowledge-admin
178
+ 💁 客服助手会话:qa-agent
179
+ 🔑 客服子系统的API Secret: <api_secret>
180
+
181
+ 📌 快速上手:
182
+ 1. 将知识库文档上传至 knowledge 目录
183
+ 2. 切换到 knowledge-admin 会话,输入:处理一下 knowledge 里面的文档
184
+ 3. 完成知识库初步创建后,即可通过 qa-agent 对外提供问答服务
185
+
186
+ 📝 常用管理命令(在 knowledge-admin 会话中使用):
187
+ • 通过附件按钮上传文件,并输入:将文档加入知识库
188
+ • 将 https://xxx 中的内容加入到知识库
189
+ • 将 xxx 写入到知识库
190
+ • 昨天客户都问了哪些问题
191
+ • 昨天客户都有哪些反馈
192
+ • 删除 xxx 知识库
193
+ ```
194
+
195
+ ## Error Handling
196
+
197
+ - **已安装检测**:Step 0 使用 `check_installed.py` 检测安装状态;`INSTALLED` 直接展示常用命令并结束,不执行 Step 1~6
198
+ - **可更新检测**:`UPDATABLE` 先提示用户有新版本并说明更新影响(不修改知识库、会重置 Agent 设置),等待用户明确确认后才执行 Step 2~6 完成更新;用户未确认则终止流程
199
+ - **目录已存在**:Step 2 使用 `mkdir -p`,已有目录不会报错
200
+ - **模板已下载**:Step 3 使用 `unzip -o` 覆盖已有文件
201
+ - **Agent 已存在**:Step 5 中 `update_config.py` 会检测重复并报错;若未执行 Step 0 而直接到 Step 5 报错,可提示用户「智能客服已安装」,并给出上述常用命令
202
+
203
+ ## ⚠️ FORBIDDEN OPERATIONS
204
+
205
+ **Do NOT:**
206
+
207
+ - 修改 knowledge/ 目录下的知识库内容
208
+ - 直接编辑 openclaw.json(必须通过脚本)
209
+ - 跳过占位符替换步骤
210
+ - 检测到可更新时未经用户确认直接执行更新(必须等用户明确回复确认)