codebee 0.1.62 → 0.1.63

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 (98) hide show
  1. package/CHANGELOG.md +499 -490
  2. package/LICENSE +21 -21
  3. package/README.md +484 -486
  4. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +88 -0
  5. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +126 -0
  6. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  7. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +210 -0
  8. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  9. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  10. package/app/.playwright-cli/run-detail-after.png +0 -0
  11. package/app/core/aiflavor.py +197 -197
  12. package/app/core/attachments.py +532 -532
  13. package/app/core/automation.py +758 -758
  14. package/app/core/backup.py +470 -470
  15. package/app/core/beekeeper.py +183 -183
  16. package/app/core/board.py +209 -209
  17. package/app/core/bookmeta_catalog.py +180 -180
  18. package/app/core/branching.py +137 -137
  19. package/app/core/builtin_agent.py +1191 -1191
  20. package/app/core/capability.py +144 -144
  21. package/app/core/catalog.py +19 -14
  22. package/app/core/chaptersafe.py +32 -32
  23. package/app/core/cleanup.py +303 -303
  24. package/app/core/compaction.py +186 -186
  25. package/app/core/covergen.py +356 -356
  26. package/app/core/diagnostics.py +114 -114
  27. package/app/core/dispatch.py +173 -173
  28. package/app/core/dispatch_log.py +113 -113
  29. package/app/core/env_scrub.py +85 -85
  30. package/app/core/error_codes.py +64 -64
  31. package/app/core/errorlog.py +137 -137
  32. package/app/core/flows.py +355 -355
  33. package/app/core/goal_service.py +167 -167
  34. package/app/core/health.py +341 -341
  35. package/app/core/history.py +38 -38
  36. package/app/core/hooks.py +156 -156
  37. package/app/core/jobs.py +1022 -1022
  38. package/app/core/manager.py +1996 -1996
  39. package/app/core/market.py +362 -362
  40. package/app/core/market_remote.py +66 -66
  41. package/app/core/mocks.py +64 -64
  42. package/app/core/modelhub.py +3266 -3266
  43. package/app/core/notify.py +106 -106
  44. package/app/core/paihang.py +109 -109
  45. package/app/core/paths.py +2 -2
  46. package/app/core/pipeline.py +4444 -4426
  47. package/app/core/planner.py +27 -27
  48. package/app/core/portguard.py +111 -111
  49. package/app/core/preview.py +293 -293
  50. package/app/core/publish/__init__.py +7 -7
  51. package/app/core/publish/auto.py +367 -367
  52. package/app/core/publish/browser.py +533 -533
  53. package/app/core/publish/fanqie.py +128 -128
  54. package/app/core/publish/flow.py +406 -406
  55. package/app/core/publish/ledger.py +198 -198
  56. package/app/core/publish/manager.py +590 -590
  57. package/app/core/publish/qimao.py +141 -141
  58. package/app/core/publish/ws.py +139 -139
  59. package/app/core/redact.py +38 -38
  60. package/app/core/repeat_guard.py +124 -124
  61. package/app/core/router.py +20 -3
  62. package/app/core/runner.py +2014 -1991
  63. package/app/core/selfupdate.py +384 -384
  64. package/app/core/session_log.py +161 -161
  65. package/app/core/sessions.py +312 -312
  66. package/app/core/settings.py +149 -149
  67. package/app/core/settings_schema.py +262 -262
  68. package/app/core/share_page.py +114 -114
  69. package/app/core/skill_scan.py +106 -106
  70. package/app/core/skillpacks/fanqie-novel.md +80 -80
  71. package/app/core/skillpacks/market/character-bible.md +66 -66
  72. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  73. package/app/core/skillpacks/market/git-workflow.md +57 -57
  74. package/app/core/skillpacks/market/release-notes.md +72 -72
  75. package/app/core/skillpacks/market/weekly-report.md +71 -71
  76. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  77. package/app/core/step_runner.py +132 -132
  78. package/app/core/task_compile.py +199 -199
  79. package/app/core/telemetry.py +292 -292
  80. package/app/core/tlsctx.py +64 -64
  81. package/app/core/token_meter.py +147 -147
  82. package/app/core/volumes.py +355 -355
  83. package/app/core/wxdigest.py +858 -858
  84. package/app/core/zentao.py +2090 -2090
  85. package/app/main.py +2826 -2826
  86. package/app/pick_dialog.py +110 -110
  87. package/app/ui/app.js +13977 -13977
  88. package/app/ui/board.html +162 -162
  89. package/app/ui/board.js +305 -305
  90. package/app/ui/i18n.js +2711 -2711
  91. package/app/ui/icons/bee.svg +79 -79
  92. package/app/ui/index.html +1 -1
  93. package/app/ui/manifest.json +16 -16
  94. package/app/ui/qrcode.js +2297 -2297
  95. package/app/ui/style.css +6125 -6125
  96. package/bin/tutti.js +147 -147
  97. package/package.json +41 -41
  98. package/tools/wx_reader.py +261 -261
@@ -1,114 +1,114 @@
1
- # -*- coding: utf-8 -*-
2
- """可分享报告页(借鉴 agency-orchestrator 的 ao report):把 run 成果渲染成
3
- 自包含单文件 HTML——零 JS、内联样式、离线可看,直接发群/发人。
4
-
5
- 纯函数无网络;数据全部来自调用方(store/paths 已读好的对象)。
6
- """
7
- from __future__ import annotations
8
-
9
- import html as _html
10
- import time
11
-
12
-
13
- def _esc(s):
14
- return _html.escape(str(s if s is not None else ""), quote=True)
15
-
16
-
17
- def _md_ish(text):
18
- """极简 Markdown 呈现:标题/列表/粗体/代码,够报告阅读即可,不做完整解析。"""
19
- out = []
20
- for ln in (text or "").splitlines():
21
- e = _esc(ln)
22
- if ln.startswith("# "):
23
- out.append("<h1>%s</h1>" % e[2:])
24
- elif ln.startswith("## "):
25
- out.append("<h2>%s</h2>" % e[3:])
26
- elif ln.startswith("### "):
27
- out.append("<h3>%s</h3>" % e[4:])
28
- elif ln.startswith("- "):
29
- out.append("<li>%s</li>" % e[2:])
30
- else:
31
- out.append("<p>%s</p>" % e)
32
- return "\n".join(out)
33
-
34
-
35
- _SHARE_TPL = """<!DOCTYPE html>
36
- <html lang="zh"><head><meta charset="utf-8">
37
- <meta name="viewport" content="width=device-width, initial-scale=1">
38
- <title>__TITLE__ · CodeBee 任务报告</title>
39
- <style>
40
- body{margin:0;background:#f5f6f8;color:#1c2330;font:15px/1.65 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
41
- .wrap{max-width:760px;margin:0 auto;padding:28px 18px 60px}
42
- .head{border-bottom:3px solid #2b6cb0;padding-bottom:14px;margin-bottom:22px}
43
- h1{margin:0 0 6px;font-size:24px}
44
- .meta{color:#5a6472;font-size:13px}
45
- .badge{display:inline-block;border-radius:999px;padding:2px 12px;font-size:13px;font-weight:600}
46
- .badge.ok{background:#e6f6ec;color:#177245}.badge.bad{background:#fdeaea;color:#b42318}
47
- .badge.off{background:#eef0f3;color:#5a6472}
48
- .card{background:#fff;border:1px solid #e3e6ea;border-radius:12px;padding:18px 20px;margin-bottom:18px}
49
- .card h2{font-size:16px;margin:0 0 12px;color:#2b6cb0}
50
- table{border-collapse:collapse;width:100%}
51
- td{padding:6px 10px;border-bottom:1px solid #eef0f3}
52
- tr.sum td{font-weight:700;border-top:2px solid #e3e6ea}
53
- .step{display:flex;gap:10px;padding:7px 0;border-bottom:1px dashed #eef0f3;font-size:13.5px;align-items:baseline}
54
- .step .n{color:#8a93a0;font-weight:700}.step .role{font-weight:600;min-width:88px}
55
- .step .who{color:#5a6472;min-width:90px}.step .sum{flex:1}.st{font-size:12px}
56
- .st.done{color:#177245}.st.failed{color:#b42318}
57
- h1,h2,h3{margin:14px 0 8px}li{margin:4px 0}.trow{padding:2px 0;border-bottom:1px dotted #eee}
58
- p{margin:8px 0}.mut{color:#8a93a0}code{background:#f0f2f5;padding:1px 6px;border-radius:4px}
59
- pre{background:#f0f2f5;padding:12px;border-radius:8px;overflow-x:auto;white-space:pre-wrap}
60
- </style></head><body><div class="wrap">
61
- <div class="head"><h1>__TITLE__</h1>
62
- <div class="meta">__META__ · __ST__ · CodeBee 生成于 __NOW__</div></div>
63
- <div class="card"><h2>结果</h2><span class="badge __STCLS__">__ST__</span>__OVERALL__</div>
64
- <div class="card"><h2>步骤</h2>__STEPS__</div>
65
- <div class="card"><h2>报告</h2>__REPORT__</div>
66
- <div class="meta">由 <b>CodeBee</b> 多智能体编排台生成</div>
67
- </div></body></html>"""
68
-
69
-
70
- def render_share_html(run, task, report_text):
71
- """渲染自包含分享页。run/task/report_text 缺失时优雅降级。"""
72
- run = run or {}
73
- task = task or {}
74
- status = str(run.get("status") or "")
75
- st_label = {"done": "已完成", "failed": "失败", "cancelled": "已取消",
76
- "running": "进行中"}.get(status, status)
77
- st_cls = {"done": "ok", "failed": "bad", "cancelled": "off"}.get(status, "off")
78
- verdict = run.get("verdict") or {}
79
- means = verdict.get("scores") or {}
80
-
81
- score_rows = ""
82
- for d, v in means.items():
83
- score_rows += "<tr><td>%s</td><td>%.1f</td></tr>" % (_esc(d), float(v))
84
- if verdict:
85
- score_rows += ('<tr class="sum"><td>综合</td>'
86
- '<td>%.1f</td></tr>' % float(verdict.get("overall") or 0))
87
-
88
- steps_rows = ""
89
- for s in run.get("steps") or []:
90
- who = s.get("agent_label") or s.get("agent") or ""
91
- if s.get("model"):
92
- who += " · " + str(s["model"])
93
- steps_rows += (
94
- '<div class="step"><span class="n">%02d</span>'
95
- '<span class="role">%s</span><span class="who">%s</span>'
96
- '<span class="sum">%s</span><span class="st %s">%s</span></div>'
97
- % (s.get("n") or 0, _esc(s.get("role") or ""),
98
- _esc(who),
99
- _esc((s.get("summary") or "")[:220]), _esc(s.get("status") or ""),
100
- _esc(s.get("status") or "")))
101
-
102
- report_html = _md_ish(report_text) if report_text else "<p class='mut'>(暂无报告)</p>"
103
- overall_note = ("综合 %.1f 分" % float(verdict.get("overall"))
104
- if verdict.get("overall") is not None else "")
105
-
106
- return (_SHARE_TPL
107
- .replace("__TITLE__", _esc(task.get("title") or run.get("title") or "任务报告"))
108
- .replace("__META__", _esc(task.get("goal") or ""))
109
- .replace("__ST__", _esc(st_label))
110
- .replace("__STCLS__", st_cls)
111
- .replace("__NOW__", _esc(time.strftime("%Y-%m-%d %H:%M")))
112
- .replace("__OVERALL__", _esc(overall_note))
113
- .replace("__STEPS__", steps_rows or "<p class='mut'>(无步骤)</p>")
114
- .replace("__REPORT__", report_html))
1
+ # -*- coding: utf-8 -*-
2
+ """可分享报告页(借鉴 agency-orchestrator 的 ao report):把 run 成果渲染成
3
+ 自包含单文件 HTML——零 JS、内联样式、离线可看,直接发群/发人。
4
+
5
+ 纯函数无网络;数据全部来自调用方(store/paths 已读好的对象)。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import html as _html
10
+ import time
11
+
12
+
13
+ def _esc(s):
14
+ return _html.escape(str(s if s is not None else ""), quote=True)
15
+
16
+
17
+ def _md_ish(text):
18
+ """极简 Markdown 呈现:标题/列表/粗体/代码,够报告阅读即可,不做完整解析。"""
19
+ out = []
20
+ for ln in (text or "").splitlines():
21
+ e = _esc(ln)
22
+ if ln.startswith("# "):
23
+ out.append("<h1>%s</h1>" % e[2:])
24
+ elif ln.startswith("## "):
25
+ out.append("<h2>%s</h2>" % e[3:])
26
+ elif ln.startswith("### "):
27
+ out.append("<h3>%s</h3>" % e[4:])
28
+ elif ln.startswith("- "):
29
+ out.append("<li>%s</li>" % e[2:])
30
+ else:
31
+ out.append("<p>%s</p>" % e)
32
+ return "\n".join(out)
33
+
34
+
35
+ _SHARE_TPL = """<!DOCTYPE html>
36
+ <html lang="zh"><head><meta charset="utf-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1">
38
+ <title>__TITLE__ · CodeBee 任务报告</title>
39
+ <style>
40
+ body{margin:0;background:#f5f6f8;color:#1c2330;font:15px/1.65 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
41
+ .wrap{max-width:760px;margin:0 auto;padding:28px 18px 60px}
42
+ .head{border-bottom:3px solid #2b6cb0;padding-bottom:14px;margin-bottom:22px}
43
+ h1{margin:0 0 6px;font-size:24px}
44
+ .meta{color:#5a6472;font-size:13px}
45
+ .badge{display:inline-block;border-radius:999px;padding:2px 12px;font-size:13px;font-weight:600}
46
+ .badge.ok{background:#e6f6ec;color:#177245}.badge.bad{background:#fdeaea;color:#b42318}
47
+ .badge.off{background:#eef0f3;color:#5a6472}
48
+ .card{background:#fff;border:1px solid #e3e6ea;border-radius:12px;padding:18px 20px;margin-bottom:18px}
49
+ .card h2{font-size:16px;margin:0 0 12px;color:#2b6cb0}
50
+ table{border-collapse:collapse;width:100%}
51
+ td{padding:6px 10px;border-bottom:1px solid #eef0f3}
52
+ tr.sum td{font-weight:700;border-top:2px solid #e3e6ea}
53
+ .step{display:flex;gap:10px;padding:7px 0;border-bottom:1px dashed #eef0f3;font-size:13.5px;align-items:baseline}
54
+ .step .n{color:#8a93a0;font-weight:700}.step .role{font-weight:600;min-width:88px}
55
+ .step .who{color:#5a6472;min-width:90px}.step .sum{flex:1}.st{font-size:12px}
56
+ .st.done{color:#177245}.st.failed{color:#b42318}
57
+ h1,h2,h3{margin:14px 0 8px}li{margin:4px 0}.trow{padding:2px 0;border-bottom:1px dotted #eee}
58
+ p{margin:8px 0}.mut{color:#8a93a0}code{background:#f0f2f5;padding:1px 6px;border-radius:4px}
59
+ pre{background:#f0f2f5;padding:12px;border-radius:8px;overflow-x:auto;white-space:pre-wrap}
60
+ </style></head><body><div class="wrap">
61
+ <div class="head"><h1>__TITLE__</h1>
62
+ <div class="meta">__META__ · __ST__ · CodeBee 生成于 __NOW__</div></div>
63
+ <div class="card"><h2>结果</h2><span class="badge __STCLS__">__ST__</span>__OVERALL__</div>
64
+ <div class="card"><h2>步骤</h2>__STEPS__</div>
65
+ <div class="card"><h2>报告</h2>__REPORT__</div>
66
+ <div class="meta">由 <b>CodeBee</b> 多智能体编排台生成</div>
67
+ </div></body></html>"""
68
+
69
+
70
+ def render_share_html(run, task, report_text):
71
+ """渲染自包含分享页。run/task/report_text 缺失时优雅降级。"""
72
+ run = run or {}
73
+ task = task or {}
74
+ status = str(run.get("status") or "")
75
+ st_label = {"done": "已完成", "failed": "失败", "cancelled": "已取消",
76
+ "running": "进行中"}.get(status, status)
77
+ st_cls = {"done": "ok", "failed": "bad", "cancelled": "off"}.get(status, "off")
78
+ verdict = run.get("verdict") or {}
79
+ means = verdict.get("scores") or {}
80
+
81
+ score_rows = ""
82
+ for d, v in means.items():
83
+ score_rows += "<tr><td>%s</td><td>%.1f</td></tr>" % (_esc(d), float(v))
84
+ if verdict:
85
+ score_rows += ('<tr class="sum"><td>综合</td>'
86
+ '<td>%.1f</td></tr>' % float(verdict.get("overall") or 0))
87
+
88
+ steps_rows = ""
89
+ for s in run.get("steps") or []:
90
+ who = s.get("agent_label") or s.get("agent") or ""
91
+ if s.get("model"):
92
+ who += " · " + str(s["model"])
93
+ steps_rows += (
94
+ '<div class="step"><span class="n">%02d</span>'
95
+ '<span class="role">%s</span><span class="who">%s</span>'
96
+ '<span class="sum">%s</span><span class="st %s">%s</span></div>'
97
+ % (s.get("n") or 0, _esc(s.get("role") or ""),
98
+ _esc(who),
99
+ _esc((s.get("summary") or "")[:220]), _esc(s.get("status") or ""),
100
+ _esc(s.get("status") or "")))
101
+
102
+ report_html = _md_ish(report_text) if report_text else "<p class='mut'>(暂无报告)</p>"
103
+ overall_note = ("综合 %.1f 分" % float(verdict.get("overall"))
104
+ if verdict.get("overall") is not None else "")
105
+
106
+ return (_SHARE_TPL
107
+ .replace("__TITLE__", _esc(task.get("title") or run.get("title") or "任务报告"))
108
+ .replace("__META__", _esc(task.get("goal") or ""))
109
+ .replace("__ST__", _esc(st_label))
110
+ .replace("__STCLS__", st_cls)
111
+ .replace("__NOW__", _esc(time.strftime("%Y-%m-%d %H:%M")))
112
+ .replace("__OVERALL__", _esc(overall_note))
113
+ .replace("__STEPS__", steps_rows or "<p class='mut'>(无步骤)</p>")
114
+ .replace("__REPORT__", report_html))
@@ -1,106 +1,106 @@
1
- # -*- coding: utf-8 -*-
2
- """skill 装前危险模式扫描(借鉴 NVIDIA SkillSpector 的静态扫描思路)。
3
-
4
- 研究数据(SkillSpector 对 31,132 个 skill 的分析):26.1% 含漏洞、5.2% 疑似
5
- 恶意。我们在「白名单闸门 + SSRF 防护 + 剥离式检查」之外补一道**内容静态扫描**:
6
- 装前扫 skill 正文里的危险模式,给风险提示——提示不是拦阻(用户仍可装),
7
- 但危险必须被看见。
8
-
9
- 纯内存函数:输入是文本,输出是发现列表,无网络无落盘。
10
- """
11
- from __future__ import annotations
12
-
13
- import re
14
-
15
- # 危险模式(静态正则;命中即在装前提示行展示)
16
- # —— 每条:(模式, 类别, 中文说明)
17
- _PATTERNS = [
18
- # 代码执行
19
- (r"\beval\s*\(", "代码执行", "动态 eval 执行任意代码"),
20
- (r"\bexec\s*\(", "代码执行", "exec 执行任意代码"),
21
- (r"subprocess|os\.system|Popen", "代码执行", "起子进程执行命令"),
22
- (r"child_process", "代码执行", "Node.js 子进程(可能执行任意命令)"),
23
- (r"(?:curl|wget)[^\n|]*\|\s*(?:ba|z|da)?sh\b", "代码执行",
24
- "下载内容直接管道进 shell 执行(dropper 特征)"),
25
- (r"Invoke-Expression|\biex\s*\(|DownloadString", "代码执行",
26
- "PowerShell 下载执行(dropper 特征)"),
27
- # 数据外发
28
- (r"https?://(?!api\.|docs\.|github\.com|raw\.githubusercontent)[a-z0-9.-]+/(upload|collect|track|ingest|webhook|callback)",
29
- "数据外发", "向非常规端点上传/回调数据"),
30
- (r"requests\.post|urllib\.request|fetch\s*\(", "网络请求", "发起网络请求(确认目标可信)"),
31
- (r"base64.{0,10}(decode|b64decode|atob)", "数据外发",
32
- "base64 解码(可能隐藏混淆载荷)"),
33
- (r"pastebin\.com|webhook\.site|requestbin|pipedream\.net|"
34
- r"ngrok\.(io|app|dev)|trycloudflare\.com", "数据外发",
35
- "常见外传/中转信道(pastebin、ngrok 隧道、webhook.site 等)"),
36
- # 持久化与挖矿(治理巡检 09-24 补:装后驻留与资源盗用的静态特征)
37
- (r"\bcrontab\b|schtasks|LaunchAgents|CurrentVersion\\Run|StartupItems",
38
- "持久化", "注册定时任务/自启动项(持久化驻留特征)"),
39
- (r"stratum\+tcp|xmrig|cryptonight", "可疑意图", "加密货币挖矿特征"),
40
- # 敏感信息读取
41
- (r"os\.environ|process\.env|getenv", "环境读取", "读取环境变量(可能带走密钥)"),
42
- (r"\.ssh/|\.aws/|\.npmrc|\.gitconfig|credentials|\.env\b", "敏感文件", "触碰凭据/密钥文件路径"),
43
- (r"keychain|credential manager|dpapi", "敏感文件", "访问系统凭据库"),
44
- (r"\.claude/|\.zcode/|\.kimi-code/|\.codebee/", "敏感文件",
45
- "触碰 AI 助手配置目录(可能篡改系统提示词或模型绑定)"),
46
- # 提示注入特征
47
- (r"(ignore|disregard|forget).{0,30}(previous|above|prior|all).{0,20}(instruction|prompt|rule)",
48
- "提示注入", "指令覆盖话术(试图无视既有规则)"),
49
- (r"system prompt|开发者指令|隐藏指令", "提示注入", "提及系统提示词/隐藏指令"),
50
- (r"do not (tell|reveal|mention).{0,20}(user|human|player)", "提示注入", "要求对用户隐瞒行为"),
51
- # 反拒答/越权
52
- (r"(you (are|must) (now|act as)|从此你是|你现在必须)", "越权人格", "试图重设助手人格"),
53
- (r"exfiltrat|渗透|后门|backdoor|keylog", "可疑意图", "包含可疑渗透/后门词汇"),
54
- ]
55
-
56
- _COMPILED = [(re.compile(p, re.I), cat, desc) for p, cat, desc in _PATTERNS]
57
-
58
-
59
- def scan_text(text, max_findings=12):
60
- """扫描一段 skill 文本。返回 [{category, detail, line}];空列表=干净。
61
-
62
- line 是 1 起的行号(供装前提示定位)。同一类别只报首个命中
63
- (提示行是给人看的,重复刷屏没有信息量)。"""
64
- findings, seen_cats = [], set()
65
- if not text:
66
- return []
67
- lines = text.splitlines()
68
- for rx, cat, desc in _COMPILED:
69
- if cat in seen_cats:
70
- continue
71
- for i, ln in enumerate(lines, 1):
72
- if rx.search(ln):
73
- findings.append({"category": cat, "detail": desc, "line": i})
74
- seen_cats.add(cat)
75
- break
76
- if len(findings) >= max_findings:
77
- break
78
- return findings
79
-
80
-
81
- def risk_label(findings):
82
- """发现列表 → 风险标签(装前提示行用)。
83
-
84
- 三级:高危(代码执行/敏感文件/可疑意图/数据外发/持久化)任一
85
- 命中 = 高风险;中危类(网络请求/环境读取/提示注入/越权人格)≥2 类
86
- 同时命中 = 中风险;有发现 = 注意;空 = 干净。"""
87
- if not findings:
88
- return ""
89
- cats = {f["category"] for f in findings}
90
- if cats & {"代码执行", "敏感文件", "可疑意图", "数据外发", "持久化"}:
91
- return "⚠ 高风险"
92
- medium_cats = cats & {"网络请求", "环境读取", "提示注入", "越权人格"}
93
- if len(medium_cats) >= 2:
94
- return "⚠ 中风险"
95
- return "△ 注意"
96
-
97
-
98
- def scan_summary(text):
99
- """一步到位:扫描+汇总成一行提示文案(空=干净返回空串)。"""
100
- fs = scan_text(text)
101
- if not fs:
102
- return ""
103
- label = risk_label(fs)
104
- top = "、".join("%s(行%d)" % (f["category"], f["line"]) for f in fs[:4])
105
- more = " 等 %d 项" % len(fs) if len(fs) > 4 else ""
106
- return "%s:%s%s" % (label, top, more)
1
+ # -*- coding: utf-8 -*-
2
+ """skill 装前危险模式扫描(借鉴 NVIDIA SkillSpector 的静态扫描思路)。
3
+
4
+ 研究数据(SkillSpector 对 31,132 个 skill 的分析):26.1% 含漏洞、5.2% 疑似
5
+ 恶意。我们在「白名单闸门 + SSRF 防护 + 剥离式检查」之外补一道**内容静态扫描**:
6
+ 装前扫 skill 正文里的危险模式,给风险提示——提示不是拦阻(用户仍可装),
7
+ 但危险必须被看见。
8
+
9
+ 纯内存函数:输入是文本,输出是发现列表,无网络无落盘。
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import re
14
+
15
+ # 危险模式(静态正则;命中即在装前提示行展示)
16
+ # —— 每条:(模式, 类别, 中文说明)
17
+ _PATTERNS = [
18
+ # 代码执行
19
+ (r"\beval\s*\(", "代码执行", "动态 eval 执行任意代码"),
20
+ (r"\bexec\s*\(", "代码执行", "exec 执行任意代码"),
21
+ (r"subprocess|os\.system|Popen", "代码执行", "起子进程执行命令"),
22
+ (r"child_process", "代码执行", "Node.js 子进程(可能执行任意命令)"),
23
+ (r"(?:curl|wget)[^\n|]*\|\s*(?:ba|z|da)?sh\b", "代码执行",
24
+ "下载内容直接管道进 shell 执行(dropper 特征)"),
25
+ (r"Invoke-Expression|\biex\s*\(|DownloadString", "代码执行",
26
+ "PowerShell 下载执行(dropper 特征)"),
27
+ # 数据外发
28
+ (r"https?://(?!api\.|docs\.|github\.com|raw\.githubusercontent)[a-z0-9.-]+/(upload|collect|track|ingest|webhook|callback)",
29
+ "数据外发", "向非常规端点上传/回调数据"),
30
+ (r"requests\.post|urllib\.request|fetch\s*\(", "网络请求", "发起网络请求(确认目标可信)"),
31
+ (r"base64.{0,10}(decode|b64decode|atob)", "数据外发",
32
+ "base64 解码(可能隐藏混淆载荷)"),
33
+ (r"pastebin\.com|webhook\.site|requestbin|pipedream\.net|"
34
+ r"ngrok\.(io|app|dev)|trycloudflare\.com", "数据外发",
35
+ "常见外传/中转信道(pastebin、ngrok 隧道、webhook.site 等)"),
36
+ # 持久化与挖矿(治理巡检 09-24 补:装后驻留与资源盗用的静态特征)
37
+ (r"\bcrontab\b|schtasks|LaunchAgents|CurrentVersion\\Run|StartupItems",
38
+ "持久化", "注册定时任务/自启动项(持久化驻留特征)"),
39
+ (r"stratum\+tcp|xmrig|cryptonight", "可疑意图", "加密货币挖矿特征"),
40
+ # 敏感信息读取
41
+ (r"os\.environ|process\.env|getenv", "环境读取", "读取环境变量(可能带走密钥)"),
42
+ (r"\.ssh/|\.aws/|\.npmrc|\.gitconfig|credentials|\.env\b", "敏感文件", "触碰凭据/密钥文件路径"),
43
+ (r"keychain|credential manager|dpapi", "敏感文件", "访问系统凭据库"),
44
+ (r"\.claude/|\.zcode/|\.kimi-code/|\.codebee/", "敏感文件",
45
+ "触碰 AI 助手配置目录(可能篡改系统提示词或模型绑定)"),
46
+ # 提示注入特征
47
+ (r"(ignore|disregard|forget).{0,30}(previous|above|prior|all).{0,20}(instruction|prompt|rule)",
48
+ "提示注入", "指令覆盖话术(试图无视既有规则)"),
49
+ (r"system prompt|开发者指令|隐藏指令", "提示注入", "提及系统提示词/隐藏指令"),
50
+ (r"do not (tell|reveal|mention).{0,20}(user|human|player)", "提示注入", "要求对用户隐瞒行为"),
51
+ # 反拒答/越权
52
+ (r"(you (are|must) (now|act as)|从此你是|你现在必须)", "越权人格", "试图重设助手人格"),
53
+ (r"exfiltrat|渗透|后门|backdoor|keylog", "可疑意图", "包含可疑渗透/后门词汇"),
54
+ ]
55
+
56
+ _COMPILED = [(re.compile(p, re.I), cat, desc) for p, cat, desc in _PATTERNS]
57
+
58
+
59
+ def scan_text(text, max_findings=12):
60
+ """扫描一段 skill 文本。返回 [{category, detail, line}];空列表=干净。
61
+
62
+ line 是 1 起的行号(供装前提示定位)。同一类别只报首个命中
63
+ (提示行是给人看的,重复刷屏没有信息量)。"""
64
+ findings, seen_cats = [], set()
65
+ if not text:
66
+ return []
67
+ lines = text.splitlines()
68
+ for rx, cat, desc in _COMPILED:
69
+ if cat in seen_cats:
70
+ continue
71
+ for i, ln in enumerate(lines, 1):
72
+ if rx.search(ln):
73
+ findings.append({"category": cat, "detail": desc, "line": i})
74
+ seen_cats.add(cat)
75
+ break
76
+ if len(findings) >= max_findings:
77
+ break
78
+ return findings
79
+
80
+
81
+ def risk_label(findings):
82
+ """发现列表 → 风险标签(装前提示行用)。
83
+
84
+ 三级:高危(代码执行/敏感文件/可疑意图/数据外发/持久化)任一
85
+ 命中 = 高风险;中危类(网络请求/环境读取/提示注入/越权人格)≥2 类
86
+ 同时命中 = 中风险;有发现 = 注意;空 = 干净。"""
87
+ if not findings:
88
+ return ""
89
+ cats = {f["category"] for f in findings}
90
+ if cats & {"代码执行", "敏感文件", "可疑意图", "数据外发", "持久化"}:
91
+ return "⚠ 高风险"
92
+ medium_cats = cats & {"网络请求", "环境读取", "提示注入", "越权人格"}
93
+ if len(medium_cats) >= 2:
94
+ return "⚠ 中风险"
95
+ return "△ 注意"
96
+
97
+
98
+ def scan_summary(text):
99
+ """一步到位:扫描+汇总成一行提示文案(空=干净返回空串)。"""
100
+ fs = scan_text(text)
101
+ if not fs:
102
+ return ""
103
+ label = risk_label(fs)
104
+ top = "、".join("%s(行%d)" % (f["category"], f["line"]) for f in fs[:4])
105
+ more = " 等 %d 项" % len(fs) if len(fs) > 4 else ""
106
+ return "%s:%s%s" % (label, top, more)
@@ -1,80 +1,80 @@
1
- # 番茄小说写作与流量守则(内置经验包)
2
-
3
- 来源:番茄作家社区与作者群体的通行经验汇编 + 平台公开运营机制(2025)。
4
- 签约字数、保底金额、全勤门槛、审核时长等**政策数字随年份调整,一律以「番茄作家后台」当前规则为准**,
5
- 本包不固化具体数值。这是写作参考,不是官方规则,也不是爆款预测;不以格式检查推断过签/起量概率。
6
-
7
- ## 〇、与传统编辑平台的根本区别:番茄是算法平台
8
- - 老牌免费站(编辑审稿制)= **编辑守门**(人审稿定签约);番茄 = **算法守门**(数据定生死)。
9
- 签约只是门票,签约之后靠完读率/追读率挣流量,才真正决定这本书的死活。
10
- - 读者画像:下沉市场、碎片时间、耐心极低——**弃书零成本**(免费,右上角一点就走)。
11
- 开局漏斗比任何平台都陡:书名 → 简介/标签 → 第一章 → 前三章,每关都在大量筛人。
12
- - 收入模型 = 免费阅读 + 广告分成(读者读得越久、追得越多,广告收益越高),
13
- 叠加签约保底/全勤(若合同有)。所以 **追读率 > 文笔 > 一切自我感动**。
14
-
15
- ## 一、新书生存线:流量池与数据指标
16
- - 通行理解的机制:新书发布后先进**小流量测试**,按数据逐级放量(书架/推荐/搜索);
17
- 数据不达标就被限流直至"饿死"。**前 30 天左右的新书期是定生死窗口**。
18
- - 后台核心指标(以作家后台实际展示为准):**完读率**(本章读完比例,决定能否进下一流量层)、
19
- **追读率**(读了开头还在追最新章的比例,决定推荐是否持续)、留存、平均阅读时长。
20
- - 数据异常按段落定位病因:第一章完读低 = 开头崩(改开篇);中段崩 = 节奏/爽点密度不足;
21
- 结尾追读崩 = 断章钩子或大坑久拖不兑现。**不要用"再等等"掩盖结构性问题**。
22
- - 反恐慌纪律:头几天数据小、噪声大,不要 48 小时一改动;到样本足够(约两周)仍差,
23
- **果断切书开新**——算法平台的沉没成本陷阱比编辑平台大得多。
24
-
25
- ## 二、开篇(黄金三章,番茄从严)
26
- - 番茄测的是**整前三章**的完读与追读,不是单看第一章:第一章钩人、第二章兑现金手指/核心设定、
27
- 第三章第一个完整爽点闭环,缺一章都撑不起数据。
28
- - 前 100 字:主角 + 具体冲突/异常直接进场;前 600 字:困境压到脸上。
29
- - 开篇禁忌(每条都有对应数据崩法):世界观说明文(第一章完读崩)、慢热日常(第二章崩)、
30
- 多视角群像(读者找不到主角,追读崩)、倒叙解谜(下沉读者直接弃)。
31
-
32
- ## 三、节奏与爽点
33
- - **每章至少一个爽点或情绪波动 + 章尾强钩子**;番茄章节奏建议 1800-2500 字/章(短平快,
34
- 卡阅读间隙),别写 4000 字大章考验耐心。
35
- - 主线必须是"一句话能讲清的直线":目标明确、单线推进、读者永远知道主角下一步要干什么。
36
- 多线叙事、慢热铺垫在番茄大概率数据判死刑。
37
- - 前 100 章要完成一次大爽点闭环(打脸/升级/身份跃迁的大兑现);过渡章连续两章以上无兑现,
38
- 追读率就会肉眼可见地掉。
39
- - 跨平台通用的铁律仍然有效:主角主观能动性红线、打压为爆发服务、反派不能无脑。
40
-
41
- ## 四、题材、书名、标签与简介
42
- - 番茄大盘热门题材带(男频):都市高武/都市脑洞、历史穿越、玄幻、悬疑脑洞(民俗/规则怪谈系在此,
43
- 守村人这类民俗悬疑素材适合往"悬疑脑洞"标签靠);女频:脑洞言情、年代、萌宝。题材热度轮动快,
44
- 开书前扫一次当季榜单。
45
- - **分类/标签是流量匹配的第一步**:挂错分类 = 推给错的人 = 数据全差且查不出原因。
46
- 书名、简介、标签三者对题材的表达必须一致(算法按这些做人群匹配)。
47
- - 书名策略:长尾、带情绪、带题材关键词;简介三要素:身份处境 + 核心冲突 + 悬念钩子。
48
-
49
- ## 五、更新纪律(算法平台命门)
50
- - **日更稳定 > 单日爆更**:断更打断追读链,数据直接塌方;全勤/推荐权重普遍与稳定日更挂钩
51
- (具体门槛看当前合同与后台规则)。
52
- - 存稿 ≥10 万字再发书;新书期若存稿允许,日双更冲数据——推荐系统吃"活跃 + 追读"信号。
53
- - 卡文宁可降纲不降质:拆未来 3 章的单元(目标→行动→阻碍→反转),别水字数糊弄——
54
- 水章的完读率会当场出卖你。
55
-
56
- ## 六、签约与合同
57
- - 申请门槛(字数)、审核时长以作家后台当前规则为准;常见审核材料 = 开篇正文 + 大纲,
58
- 大纲别只写一句"后面上架爆火",要有明确的阶段目标与兑现节点。
59
- - 类型以**分成为主**,优质作品可谈保底;签前重点看清 **影视/动漫/版权衍生授权范围**与独家条款、
60
- 违约与切书条款——免费平台合同中 IP 衍生让渡比例是历来争议集中点。
61
- - 拒签自救:先归因(题材带匹配?开篇数据思维?大纲兑现链缺失?),改完再申;
62
- 同一作品反复无修改重投没有意义。
63
-
64
- ## 七、文笔自查(跨平台通用,2026-09-14 编辑驳回语实证拆解)
65
- - **风格统一**:全篇定一个文风锚(短句+白描+口语但不过火),换场不换笔调;
66
- 文白/网络梗/书面长句不混用。
67
- - **流畅**:逐段读出声——长定语、多重倒装、连续同主语开头(她…她…她…)、动作与反应拆碎,
68
- 卡顿处就地删改;一句一动作,段尾留半句承接。
69
- - **细节刻画力度**:每个爽点/反转标记"特写镜头",五感 + 微动作 + 环境反馈把爆发那几秒写厚,
70
- 铺垫在前、放大在中、余波在后;过渡章才可一笔带过。**"每章一小爽"不配特写等于没写**。
71
-
72
- ## 八、开书/投稿前自检清单
73
- - [ ] 书名/标签/简介一致表达题材,分类挂对
74
- - [ ] 第 1 章前 100 字冲突进场;前三章 = 钩子 + 设定兑现 + 完整爽点闭环
75
- - [ ] 每章 1800-2500 字,含至少一个爽点 + 章尾钩子
76
- - [ ] 主线一句话讲得清,单线推进,无慢热铺垫章节
77
- - [ ] 前 100 章有一次大兑现闭环的规划
78
- - [ ] 存稿 ≥10 万字;日更承诺可执行(含请假预案)
79
- - [ ] 文笔三词(风格统一/流畅/细节力度)逐章自查过
80
- - [ ] 合同衍生授权与独家条款已看清
1
+ # 番茄小说写作与流量守则(内置经验包)
2
+
3
+ 来源:番茄作家社区与作者群体的通行经验汇编 + 平台公开运营机制(2025)。
4
+ 签约字数、保底金额、全勤门槛、审核时长等**政策数字随年份调整,一律以「番茄作家后台」当前规则为准**,
5
+ 本包不固化具体数值。这是写作参考,不是官方规则,也不是爆款预测;不以格式检查推断过签/起量概率。
6
+
7
+ ## 〇、与传统编辑平台的根本区别:番茄是算法平台
8
+ - 老牌免费站(编辑审稿制)= **编辑守门**(人审稿定签约);番茄 = **算法守门**(数据定生死)。
9
+ 签约只是门票,签约之后靠完读率/追读率挣流量,才真正决定这本书的死活。
10
+ - 读者画像:下沉市场、碎片时间、耐心极低——**弃书零成本**(免费,右上角一点就走)。
11
+ 开局漏斗比任何平台都陡:书名 → 简介/标签 → 第一章 → 前三章,每关都在大量筛人。
12
+ - 收入模型 = 免费阅读 + 广告分成(读者读得越久、追得越多,广告收益越高),
13
+ 叠加签约保底/全勤(若合同有)。所以 **追读率 > 文笔 > 一切自我感动**。
14
+
15
+ ## 一、新书生存线:流量池与数据指标
16
+ - 通行理解的机制:新书发布后先进**小流量测试**,按数据逐级放量(书架/推荐/搜索);
17
+ 数据不达标就被限流直至"饿死"。**前 30 天左右的新书期是定生死窗口**。
18
+ - 后台核心指标(以作家后台实际展示为准):**完读率**(本章读完比例,决定能否进下一流量层)、
19
+ **追读率**(读了开头还在追最新章的比例,决定推荐是否持续)、留存、平均阅读时长。
20
+ - 数据异常按段落定位病因:第一章完读低 = 开头崩(改开篇);中段崩 = 节奏/爽点密度不足;
21
+ 结尾追读崩 = 断章钩子或大坑久拖不兑现。**不要用"再等等"掩盖结构性问题**。
22
+ - 反恐慌纪律:头几天数据小、噪声大,不要 48 小时一改动;到样本足够(约两周)仍差,
23
+ **果断切书开新**——算法平台的沉没成本陷阱比编辑平台大得多。
24
+
25
+ ## 二、开篇(黄金三章,番茄从严)
26
+ - 番茄测的是**整前三章**的完读与追读,不是单看第一章:第一章钩人、第二章兑现金手指/核心设定、
27
+ 第三章第一个完整爽点闭环,缺一章都撑不起数据。
28
+ - 前 100 字:主角 + 具体冲突/异常直接进场;前 600 字:困境压到脸上。
29
+ - 开篇禁忌(每条都有对应数据崩法):世界观说明文(第一章完读崩)、慢热日常(第二章崩)、
30
+ 多视角群像(读者找不到主角,追读崩)、倒叙解谜(下沉读者直接弃)。
31
+
32
+ ## 三、节奏与爽点
33
+ - **每章至少一个爽点或情绪波动 + 章尾强钩子**;番茄章节奏建议 1800-2500 字/章(短平快,
34
+ 卡阅读间隙),别写 4000 字大章考验耐心。
35
+ - 主线必须是"一句话能讲清的直线":目标明确、单线推进、读者永远知道主角下一步要干什么。
36
+ 多线叙事、慢热铺垫在番茄大概率数据判死刑。
37
+ - 前 100 章要完成一次大爽点闭环(打脸/升级/身份跃迁的大兑现);过渡章连续两章以上无兑现,
38
+ 追读率就会肉眼可见地掉。
39
+ - 跨平台通用的铁律仍然有效:主角主观能动性红线、打压为爆发服务、反派不能无脑。
40
+
41
+ ## 四、题材、书名、标签与简介
42
+ - 番茄大盘热门题材带(男频):都市高武/都市脑洞、历史穿越、玄幻、悬疑脑洞(民俗/规则怪谈系在此,
43
+ 守村人这类民俗悬疑素材适合往"悬疑脑洞"标签靠);女频:脑洞言情、年代、萌宝。题材热度轮动快,
44
+ 开书前扫一次当季榜单。
45
+ - **分类/标签是流量匹配的第一步**:挂错分类 = 推给错的人 = 数据全差且查不出原因。
46
+ 书名、简介、标签三者对题材的表达必须一致(算法按这些做人群匹配)。
47
+ - 书名策略:长尾、带情绪、带题材关键词;简介三要素:身份处境 + 核心冲突 + 悬念钩子。
48
+
49
+ ## 五、更新纪律(算法平台命门)
50
+ - **日更稳定 > 单日爆更**:断更打断追读链,数据直接塌方;全勤/推荐权重普遍与稳定日更挂钩
51
+ (具体门槛看当前合同与后台规则)。
52
+ - 存稿 ≥10 万字再发书;新书期若存稿允许,日双更冲数据——推荐系统吃"活跃 + 追读"信号。
53
+ - 卡文宁可降纲不降质:拆未来 3 章的单元(目标→行动→阻碍→反转),别水字数糊弄——
54
+ 水章的完读率会当场出卖你。
55
+
56
+ ## 六、签约与合同
57
+ - 申请门槛(字数)、审核时长以作家后台当前规则为准;常见审核材料 = 开篇正文 + 大纲,
58
+ 大纲别只写一句"后面上架爆火",要有明确的阶段目标与兑现节点。
59
+ - 类型以**分成为主**,优质作品可谈保底;签前重点看清 **影视/动漫/版权衍生授权范围**与独家条款、
60
+ 违约与切书条款——免费平台合同中 IP 衍生让渡比例是历来争议集中点。
61
+ - 拒签自救:先归因(题材带匹配?开篇数据思维?大纲兑现链缺失?),改完再申;
62
+ 同一作品反复无修改重投没有意义。
63
+
64
+ ## 七、文笔自查(跨平台通用,2026-09-14 编辑驳回语实证拆解)
65
+ - **风格统一**:全篇定一个文风锚(短句+白描+口语但不过火),换场不换笔调;
66
+ 文白/网络梗/书面长句不混用。
67
+ - **流畅**:逐段读出声——长定语、多重倒装、连续同主语开头(她…她…她…)、动作与反应拆碎,
68
+ 卡顿处就地删改;一句一动作,段尾留半句承接。
69
+ - **细节刻画力度**:每个爽点/反转标记"特写镜头",五感 + 微动作 + 环境反馈把爆发那几秒写厚,
70
+ 铺垫在前、放大在中、余波在后;过渡章才可一笔带过。**"每章一小爽"不配特写等于没写**。
71
+
72
+ ## 八、开书/投稿前自检清单
73
+ - [ ] 书名/标签/简介一致表达题材,分类挂对
74
+ - [ ] 第 1 章前 100 字冲突进场;前三章 = 钩子 + 设定兑现 + 完整爽点闭环
75
+ - [ ] 每章 1800-2500 字,含至少一个爽点 + 章尾钩子
76
+ - [ ] 主线一句话讲得清,单线推进,无慢热铺垫章节
77
+ - [ ] 前 100 章有一次大兑现闭环的规划
78
+ - [ ] 存稿 ≥10 万字;日更承诺可执行(含请假预案)
79
+ - [ ] 文笔三词(风格统一/流畅/细节力度)逐章自查过
80
+ - [ ] 合同衍生授权与独家条款已看清