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,6 @@
1
+ [project]
2
+ name = "sophnet-qa-install"
3
+ version = "1.0.0"
4
+ description = "智能客服系统创建(知识管理员 + 问答助手)"
5
+ requires-python = ">=3.10"
6
+ dependencies = []
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 备份指定文件夹下面的Agent所有配置文件到指定版本目录。
4
+ """
5
+
6
+ import argparse
7
+ import os
8
+ import shutil
9
+
10
+
11
+ def backup_md_dir(
12
+ version: str,
13
+ dir_path: str = "/home/node/.openclaw/workspace-knowledge",
14
+ ) -> int:
15
+ backup_dir = os.path.join(dir_path, f"backup_v{version}")
16
+ if not os.path.exists(backup_dir):
17
+ os.makedirs(backup_dir)
18
+ for file in os.listdir(dir_path):
19
+ if file.endswith(".md"):
20
+ shutil.copy(os.path.join(dir_path, file), os.path.join(backup_dir, file))
21
+ return backup_dir
22
+
23
+
24
+ if __name__ == "__main__":
25
+ parser = argparse.ArgumentParser(description="备份Agent配置文件")
26
+ parser.add_argument("--current-version", help="当前的版本号")
27
+ parser.add_argument(
28
+ "--dir",
29
+ default="/home/node/.openclaw/workspace-knowledge",
30
+ help="要处理的目录路径(仅该目录下的 .md,不含子目录)",
31
+ )
32
+ args = parser.parse_args()
33
+ backup_dir = backup_md_dir(args.current_version, args.dir)
34
+ print(f"备份成功: {backup_dir}")
35
+ exit(0)
@@ -0,0 +1,143 @@
1
+ """
2
+ 校验智能客服系统安装状态。
3
+
4
+ 三种状态(退出码):
5
+ 0 — INSTALLED:已安装,无需操作
6
+ 1 — NOT_INSTALLED:未安装
7
+ 2 — UPDATABLE:可更新
8
+
9
+ 同时输出配置状态(CONFIG_FOUND / CONFIG_NOT_FOUND)。
10
+ 当状态为 UPDATABLE 且配置存在时,额外输出已有的 ADMIN_NAME / QA_NAME。
11
+ """
12
+
13
+ import argparse
14
+ import json
15
+ import os
16
+ import sys
17
+
18
+
19
+ def get_workspace_root(config_path: str) -> str:
20
+ """从 openclaw.json 所在目录推导 workspace 根目录(.openclaw)。"""
21
+ return os.path.dirname(os.path.abspath(config_path))
22
+
23
+
24
+ def step2_artifacts_exist(workspace_root: str) -> bool:
25
+ """检查 Step 2 要创建的目录和软链接是否已存在。"""
26
+ base = os.path.join(workspace_root, "workspace-knowledge")
27
+ workspace_qa = os.path.join(base, "workspace-qa")
28
+
29
+ if not os.path.isdir(workspace_qa):
30
+ return False
31
+ return True
32
+
33
+
34
+ def agents_already_in_config(config_path: str) -> bool:
35
+ """检查 openclaw.json 中是否已包含 knowledge-admin 和 qa-agent。"""
36
+ if not os.path.exists(config_path):
37
+ return False
38
+ try:
39
+ with open(config_path, "r", encoding="utf-8") as f:
40
+ config = json.load(f)
41
+ except (json.JSONDecodeError, OSError):
42
+ return False
43
+ agents = config.get("agents", {}).get("list", [])
44
+ ids = {a.get("id") for a in agents if isinstance(a, dict) and a.get("id")}
45
+ return "knowledge-admin" in ids and "qa-agent" in ids
46
+
47
+
48
+ def get_agent_names(config_path: str) -> tuple[str, str]:
49
+ """从 openclaw.json 读取 knowledge-admin 和 qa-agent 的显示名称。"""
50
+ admin_name, qa_name = "客服管理员", "客服助手"
51
+ try:
52
+ with open(config_path, "r", encoding="utf-8") as f:
53
+ config = json.load(f)
54
+ for agent in config.get("agents", {}).get("list", []):
55
+ if not isinstance(agent, dict):
56
+ continue
57
+ if agent.get("id") == "knowledge-admin" and agent.get("name"):
58
+ admin_name = agent["name"]
59
+ elif agent.get("id") == "qa-agent" and agent.get("name"):
60
+ qa_name = agent["name"]
61
+ except (json.JSONDecodeError, OSError):
62
+ pass
63
+ return admin_name, qa_name
64
+
65
+
66
+ def parse_version(v: str) -> tuple:
67
+ """将版本号字符串解析为可比较的整数元组。"""
68
+ try:
69
+ return tuple(int(x) for x in v.strip().split("."))
70
+ except (ValueError, AttributeError):
71
+ return ()
72
+
73
+
74
+ def get_installed_version(workspace_root: str) -> str | None:
75
+ """读取 workspace-knowledge/git_version 中的版本号。"""
76
+ version_file = os.path.join(workspace_root, "workspace-knowledge", "git_version")
77
+ if not os.path.isfile(version_file):
78
+ return None
79
+ try:
80
+ with open(version_file, "r", encoding="utf-8") as f:
81
+ return f.read().strip()
82
+ except OSError:
83
+ return None
84
+
85
+
86
+ def check_installed(config_path: str, target_version: str) -> int:
87
+ """
88
+ 判断安装状态。
89
+ 返回: 0=已安装, 1=未安装, 2=可更新
90
+ """
91
+ workspace_root = get_workspace_root(config_path)
92
+
93
+ if not step2_artifacts_exist(workspace_root):
94
+ return 1
95
+
96
+ installed_version = get_installed_version(workspace_root)
97
+ if installed_version is None or parse_version(installed_version) < parse_version(target_version):
98
+ return 2
99
+
100
+ if not agents_already_in_config(config_path):
101
+ return 1
102
+
103
+ return 0
104
+
105
+
106
+ def main():
107
+ parser = argparse.ArgumentParser(description="校验智能客服系统安装状态")
108
+ parser.add_argument(
109
+ "--config",
110
+ default="/home/node/.openclaw/openclaw.json",
111
+ help="openclaw.json 路径",
112
+ )
113
+ parser.add_argument(
114
+ "--version",
115
+ required=True,
116
+ help="目标版本号(例:1.0.1)",
117
+ )
118
+ args = parser.parse_args()
119
+
120
+ config_path = args.config
121
+ status = check_installed(config_path, args.version)
122
+ config_exists = agents_already_in_config(config_path)
123
+
124
+ if status == 0:
125
+ print("INSTALLED")
126
+ print("CONFIG_FOUND" if config_exists else "CONFIG_NOT_FOUND")
127
+ sys.exit(0)
128
+ elif status == 2:
129
+ print("UPDATABLE")
130
+ print("CONFIG_FOUND" if config_exists else "CONFIG_NOT_FOUND")
131
+ if config_exists:
132
+ admin_name, qa_name = get_agent_names(config_path)
133
+ print(f"ADMIN_NAME={admin_name}")
134
+ print(f"QA_NAME={qa_name}")
135
+ sys.exit(2)
136
+ else:
137
+ print("NOT_INSTALLED")
138
+ print("CONFIG_FOUND" if config_exists else "CONFIG_NOT_FOUND")
139
+ sys.exit(1)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()
@@ -0,0 +1,142 @@
1
+ """
2
+ OpenClaw 配置更新脚本
3
+
4
+ 向 openclaw.json 中追加 knowledge-admin、qa-agent 以及 main 三个 agent 的配置。
5
+ """
6
+
7
+ import argparse
8
+ import json
9
+ import os
10
+ import secrets
11
+ import sys
12
+
13
+ def get_api_secret(config_path: str = "/home/node/.openclaw/openclaw.json"):
14
+ if not os.path.exists(config_path):
15
+ raise FileNotFoundError(f"Config file not found: {config_path}")
16
+ try:
17
+ with open(config_path, "r", encoding="utf-8") as f:
18
+ config = json.load(f)
19
+ except json.JSONDecodeError:
20
+ raise ValueError(f"Invalid JSON in config file: {config_path}")
21
+ channels = config.get("channels", {})
22
+ bot_api_channel_config = channels.get("bot-api", {})
23
+ if not bot_api_channel_config:
24
+ return None
25
+ accounts_config = bot_api_channel_config.get("accounts", {})
26
+ if not accounts_config:
27
+ return None
28
+ qa_agent_config = accounts_config.get("qa-agent", {})
29
+ if not qa_agent_config:
30
+ return None
31
+ return qa_agent_config.get("apiSecret", None)
32
+
33
+
34
+ def update_config(admin_name: str, qa_name: str, config_path: str = "/home/node/.openclaw/openclaw.json"):
35
+ if not os.path.exists(config_path):
36
+ raise FileNotFoundError(f"Config file not found: {config_path}")
37
+
38
+ with open(config_path, "r", encoding="utf-8") as f:
39
+ config = json.load(f)
40
+
41
+ admin_agent_config = {
42
+ "id": "knowledge-admin",
43
+ "workspace": "/home/node/.openclaw/workspace-knowledge",
44
+ "identity": {
45
+ "name": admin_name,
46
+ "theme": admin_name,
47
+ "emoji": "🤖"
48
+ },
49
+ "subagents": {
50
+ "allowAgents": ["qa-agent"]
51
+ }
52
+ }
53
+ qa_agent_config = {
54
+ "id": "qa-agent",
55
+ "workspace": "/home/node/.openclaw/workspace-knowledge/workspace-qa",
56
+ "identity": {
57
+ "name": qa_name,
58
+ "theme": qa_name,
59
+ "emoji": "💁"
60
+ },
61
+ "tools": {
62
+ "deny": ["exec", "web_search", "web_fetch"]
63
+ }
64
+ }
65
+ main_agent_config = {
66
+ "id": "main",
67
+ "workspace": "/home/node/.openclaw/workspace",
68
+ "identity": {
69
+ "name": "main",
70
+ "theme": "main",
71
+ "emoji": "🦞"
72
+ }
73
+ }
74
+
75
+ qa_agent_account_config = {
76
+ "agentId": "qa-agent",
77
+ "name": qa_name,
78
+ "apiSecret": secrets.token_hex(32),
79
+ "enabled": True
80
+ }
81
+ config.setdefault("agents", {})
82
+ agents = config.get("agents", {}).get("list", [])
83
+ has_main = False
84
+ for agent in agents:
85
+ aid = agent.get("id") if isinstance(agent, dict) else None
86
+ if aid == "main":
87
+ has_main = True
88
+ elif aid == "knowledge-admin":
89
+ raise ValueError("知识管理员 agent 已存在")
90
+ elif aid == "qa-agent":
91
+ raise ValueError("客服助手 agent 已存在")
92
+
93
+ if not has_main:
94
+ agents.append(main_agent_config)
95
+ agents.append(admin_agent_config)
96
+ agents.append(qa_agent_config)
97
+ config["agents"]["list"] = agents
98
+
99
+ # 启用 bot-api 插件(用 setdefault 保证 plugins.entries 与 bot-api 存在,避免 KeyError)
100
+ plugins = config.get("plugins", {})
101
+ entries = plugins.setdefault("entries", {})
102
+ entries.setdefault("bot-api", {})["enabled"] = True
103
+ config["plugins"] = plugins
104
+
105
+ # 添加客服子系统的channel配置
106
+ channels = config.get("channels", {})
107
+ bot_api_channel_config = channels.get("bot-api", {})
108
+ if bot_api_channel_config:
109
+ accounts_config = bot_api_channel_config.get("accounts", {})
110
+ accounts_config["qa-agent"] = qa_agent_account_config
111
+ bot_api_channel_config["accounts"] = accounts_config
112
+ else:
113
+ bot_api_channel_config = {
114
+ "accounts": {
115
+ "qa-agent": qa_agent_account_config
116
+ }
117
+ }
118
+ channels["bot-api"] = bot_api_channel_config
119
+ config["channels"] = channels
120
+
121
+ with open(config_path, "w", encoding="utf-8") as f:
122
+ json.dump(config, f, indent=4, ensure_ascii=False)
123
+ return True
124
+
125
+
126
+ def main():
127
+ parser = argparse.ArgumentParser(description="向 openclaw.json 注入客服 agent 配置")
128
+ parser.add_argument("--admin-name", default="客服管理员", help="知识管理员显示名称")
129
+ parser.add_argument("--qa-name", default="客服助手", help="客服助手显示名称")
130
+ parser.add_argument("--config", default="/home/node/.openclaw/openclaw.json", help="openclaw.json 路径")
131
+ args = parser.parse_args()
132
+ try:
133
+ update_config(args.admin_name, args.qa_name, args.config)
134
+ print(f"✅ 配置更新成功:管理员={args.admin_name}, 客服={args.qa_name}")
135
+ print(f"客服助手 API Secret: {get_api_secret(args.config)}")
136
+ except Exception as e:
137
+ print(f"❌ 配置更新失败:{e}")
138
+ sys.exit(1)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 修改指定目录下所有 .md 文件中的占位符:
4
+ - {{客服助手}} -> qa_name
5
+ - {{知识库管理员}} -> admin_name
6
+ 仅处理目标目录本身,不递归子文件夹。
7
+ """
8
+
9
+ import argparse
10
+ from pathlib import Path
11
+
12
+
13
+ def update_md_in_dir(
14
+ dir_path: str | Path,
15
+ qa_name: str = "客服助手",
16
+ admin_name: str = "客服管理员",
17
+ ) -> int:
18
+ """
19
+ 修改指定目录下所有 .md 文件中的占位符(仅该目录,不含子文件夹)。
20
+ 返回 0 成功,1 目录不存在,2 读写文件失败。
21
+ """
22
+ root = Path(dir_path)
23
+ if not root.is_dir():
24
+ print(f"错误:目录不存在 {root}")
25
+ return 1
26
+
27
+ md_files = [f for f in root.iterdir() if f.is_file() and f.suffix == ".md"]
28
+ if not md_files:
29
+ print(f"在 {root} 下未找到 .md 文件")
30
+ return 0
31
+
32
+ replacements = [
33
+ ("{{客服助手}}", qa_name),
34
+ ("{{知识库管理员}}", admin_name),
35
+ ]
36
+
37
+ for path in md_files:
38
+ try:
39
+ text = path.read_text(encoding="utf-8")
40
+ except OSError as e:
41
+ print(f"错误:无法读取 {path}:{e}")
42
+ return 2
43
+ original = text
44
+ for old, new in replacements:
45
+ text = text.replace(old, new)
46
+ if text != original:
47
+ try:
48
+ path.write_text(text, encoding="utf-8")
49
+ except OSError as e:
50
+ print(f"错误:无法写入 {path}:{e}")
51
+ return 2
52
+ print(f"已更新: {path}")
53
+ else:
54
+ print(f"无需修改: {path}")
55
+
56
+ return 0
57
+
58
+
59
+ def main():
60
+ parser = argparse.ArgumentParser(description="替换 .md 文件中的占位符")
61
+ parser.add_argument("--qa-name", default="客服助手", help="替换 {{客服助手}} 的值")
62
+ parser.add_argument("--admin-name", default="客服管理员", help="替换 {{知识库管理员}} 的值")
63
+ parser.add_argument(
64
+ "--dir",
65
+ default="/home/node/.openclaw/workspace-knowledge",
66
+ help="要处理的目录路径(仅该目录下的 .md,不含子目录)",
67
+ )
68
+ args = parser.parse_args()
69
+ return update_md_in_dir(args.dir, qa_name=args.qa_name, admin_name=args.admin_name)
70
+
71
+
72
+ if __name__ == "__main__":
73
+ exit(main())
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: sophnet-training-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["training-qa"].apiSecret` 读取(与 `scripts/update_config.py` 中 `get_api_secret` 一致)。
35
+
36
+ ```
37
+ 智能培训系统已经安装,无需重复安装。
38
+
39
+ 📂 知识库目录:/home/node/.openclaw/workspace-training/knowledge
40
+ 🤖 管理员会话:training-admin
41
+ 💁 培训助手会话:training-qa
42
+ 🔑 培训子系统的API Secret: <api_secret>
43
+
44
+ 📝 常用命令(在 training-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-training/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-training
81
+ ```
82
+
83
+ ```bash
84
+ uv run {baseDir}/scripts/backup_md.py \
85
+ --current-version {current_version} \
86
+ --dir /home/node/.openclaw/workspace-training/workspace-qa
87
+ ```
88
+
89
+ 脚本成功后会输出备份目录路径(如 `备份成功: /home/node/.openclaw/workspace-training/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-training/git_version` 与目标版本号;再检查 openclaw.json 中是否已配置 training-admin 与 training-qa。详见 `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-training/workspace-qa/
118
+ mkdir -p /home/node/.openclaw/workspace-training/knowledge/
119
+ ```
120
+
121
+ 在 workspace-qa 下创建指向上级 knowledge 的软链接:
122
+
123
+ ```bash
124
+ cd /home/node/.openclaw/workspace-training/workspace-qa/
125
+ ln -sf ../knowledge knowledge
126
+ ```
127
+
128
+ ### Step 3: 下载模板文件
129
+
130
+ ```bash
131
+ cd /home/node/.openclaw/workspace-training/
132
+ wget -O workspace-training.zip https://oss-upload-temp.sophnet.com/workspace-training_v{version}.zip
133
+ unzip -o workspace-training.zip
134
+ rm -f workspace-training.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-training \
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-training/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["training-qa"].apiSecret` 读取(与 `scripts/update_config.py` 中 `get_api_secret` 一致)。
172
+
173
+ ```
174
+ ✅ 您的智能培训系统已创建完成!
175
+
176
+ 📂 知识库目录:/home/node/.openclaw/workspace-training/knowledge
177
+ 🤖 管理员会话:training-admin
178
+ 💁 培训助手会话:training-qa
179
+ 🔑 培训子系统的API Secret: <api_secret>
180
+
181
+ 📌 快速上手:
182
+ 1. 将知识库文档上传至 knowledge 目录
183
+ 2. 切换到 training-admin 会话,输入:处理一下 knowledge 里面的文档
184
+ 3. 完成知识库初步创建后,即可通过 training-qa 对外提供问答服务
185
+
186
+ 📝 常用管理命令(在 training-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
+ - **占位符替换失败**:Step 4 中 `update_md.py` 退出码非 0 表示失败(1=目录不存在,2=读写文件失败);应中止后续步骤并向用户报错,不要继续执行 Step 5~6
202
+ - **Agent 已存在**:Step 5 中 `update_config.py` 会检测重复并报错;若未执行 Step 0 而直接到 Step 5 报错,可提示用户「智能培训已安装」,并给出上述常用命令
203
+
204
+ ## ⚠️ FORBIDDEN OPERATIONS
205
+
206
+ **Do NOT:**
207
+
208
+ - 修改 knowledge/ 目录下的知识库内容
209
+ - 直接编辑 openclaw.json(必须通过脚本)
210
+ - 跳过占位符替换步骤
211
+ - 检测到可更新时未经用户确认直接执行更新(必须等用户明确回复确认)
@@ -0,0 +1,6 @@
1
+ [project]
2
+ name = "sophnet-training-install"
3
+ version = "1.0.0"
4
+ description = "智能培训系统创建(知识管理员 + 问答助手)"
5
+ requires-python = ">=3.10"
6
+ dependencies = []
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 备份指定文件夹下面的Agent所有配置文件到指定版本目录。
4
+ """
5
+
6
+ import argparse
7
+ import os
8
+ import shutil
9
+
10
+
11
+ def backup_md_dir(
12
+ version: str,
13
+ dir_path: str = "/home/node/.openclaw/workspace-training",
14
+ ) -> int:
15
+ backup_dir = os.path.join(dir_path, f"backup_v{version}")
16
+ if not os.path.exists(backup_dir):
17
+ os.makedirs(backup_dir)
18
+ for file in os.listdir(dir_path):
19
+ if file.endswith(".md"):
20
+ shutil.copy(os.path.join(dir_path, file), os.path.join(backup_dir, file))
21
+ return backup_dir
22
+
23
+
24
+ if __name__ == "__main__":
25
+ parser = argparse.ArgumentParser(description="备份Agent配置文件")
26
+ parser.add_argument("--current-version", help="当前的版本号")
27
+ parser.add_argument(
28
+ "--dir",
29
+ default="/home/node/.openclaw/workspace-training",
30
+ help="要处理的目录路径(仅该目录下的 .md,不含子目录)",
31
+ )
32
+ args = parser.parse_args()
33
+ backup_dir = backup_md_dir(args.current_version, args.dir)
34
+ print(f"备份成功: {backup_dir}")
35
+ exit(0)