aiconductor-cli 1.5.0 → 1.6.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.
@@ -81,4 +81,92 @@ function safeParseInt(value, defaultValue) {
81
81
  return Number.isNaN(n) ? defaultValue : n;
82
82
  }
83
83
 
84
- module.exports = { outputResult, outputError, resolveKeyOrExit, handleApiError, safeParseInt };
84
+ /**
85
+ * 从文本(分享口令/带链接的句子)中抽出第一个 http(s) 链接。
86
+ *
87
+ * 小红书用户复制的内容形如「标题 https://xhslink.cn/o/xxx 复制内容,然后进入【小红书】查看相关笔记。」
88
+ * 平台侧的 share_text 是**原样透传上游**的,带中文口令必失败(2026-11-05 实测),所以链接必须本地抽。
89
+ * @param {string} text
90
+ * @returns {string|null}
91
+ */
92
+ function extractUrlFromText(text) {
93
+ if (typeof text !== "string") return null;
94
+ const match = text.match(/https?:\/\/[^\s\u4e00-\u9fa5,。、;:!?()【】《》“”‘’]+/i);
95
+ if (!match) return null;
96
+ // 去掉 URL 尾部的英文标点(中文标点已在字符集里排除)
97
+ return match[0].replace(/[),.;:!?'"\]}>]+$/, "");
98
+ }
99
+
100
+ const XHS_ID_RE = /^[0-9a-f]{24}$/i;
101
+
102
+ /**
103
+ * 从用户输入里识别小红书笔记 ID(裸 ID / 长链 / 带链接的分享文案)。
104
+ * 短链(xhslink.cn)需先由 resolveXhsShortLink 换成长链。
105
+ * @param {string} text
106
+ * @returns {string|null}
107
+ */
108
+ function parseXhsNoteId(text) {
109
+ if (typeof text !== "string") return null;
110
+ const raw = text.trim();
111
+ if (XHS_ID_RE.test(raw)) return raw;
112
+ const url = extractUrlFromText(raw);
113
+ if (!url) return null;
114
+ const match = url.match(/(?:explore|discovery\/item|note)\/([0-9a-f]{24})/i);
115
+ return match ? match[1] : null;
116
+ }
117
+
118
+ /**
119
+ * 从用户输入里识别小红书用户 ID(裸 ID / 主页链接)。
120
+ * @param {string} text
121
+ * @returns {string|null}
122
+ */
123
+ function parseXhsUserId(text) {
124
+ if (typeof text !== "string") return null;
125
+ const raw = text.trim();
126
+ if (XHS_ID_RE.test(raw)) return raw;
127
+ const url = extractUrlFromText(raw);
128
+ if (!url) return null;
129
+ const match = url.match(/user\/profile\/([0-9a-f]{24})/i);
130
+ return match ? match[1] : null;
131
+ }
132
+
133
+ /**
134
+ * 小红书时间戳格式化:上游给的是**真 UTC epoch**(详情秒、笔记列表毫秒),展示需 +8 小时。
135
+ * 与抖音相反(抖音是「北京时间编码」,不要再加 8)。
136
+ * @param {number|string} value epoch 秒或毫秒
137
+ * @returns {string} `YYYY-MM-DD HH:mm:ss`,无效值返回空串
138
+ */
139
+ function formatXhsTime(value) {
140
+ const n = Number(value);
141
+ if (!n || Number.isNaN(n)) return "";
142
+ const ms = n > 1e12 ? n : n * 1000;
143
+ return new Date(ms + 8 * 3600 * 1000).toISOString().replace("T", " ").slice(0, 19);
144
+ }
145
+
146
+ /**
147
+ * 归一化为整数计数:上游同一平台里数字有 int、字符串、以及「1.2万」这类糊糊写法。
148
+ * @param {*} value
149
+ * @returns {number}
150
+ */
151
+ function toCount(value) {
152
+ if (typeof value === "number") return Number.isFinite(value) ? value : 0;
153
+ if (value === null || value === undefined) return 0;
154
+ const text = String(value).replace(/[,\s]/g, "");
155
+ const match = text.match(/^(\d+(?:\.\d+)?)(万|w)?\+?$/i);
156
+ if (!match) return 0;
157
+ const base = Number(match[1]);
158
+ return Math.round(match[2] ? base * 10000 : base);
159
+ }
160
+
161
+ module.exports = {
162
+ outputResult,
163
+ outputError,
164
+ resolveKeyOrExit,
165
+ handleApiError,
166
+ safeParseInt,
167
+ extractUrlFromText,
168
+ parseXhsNoteId,
169
+ parseXhsUserId,
170
+ formatXhsTime,
171
+ toCount,
172
+ };
@@ -1,161 +0,0 @@
1
- # 抖音三技能上架计划
2
-
3
- 目标:在 `douyin-script-extract` 之外再补 3 个可独立上架的流量入口,每个都能用一句话说清"用户为什么装它",且交付物比裸命令多出加工价值。
4
-
5
- ---
6
-
7
- ## 一、稳定层(承诺,入库)
8
-
9
- ### 1. 共同骨架(三个技能一致,不逐条重复)
10
-
11
- | 项 | 约定 |
12
- |---|---|
13
- | 目录 | `.codex/skills/<name>/`,同时镜像到 `~/.agents/skills/<name>/`,两处正文逐字一致 |
14
- | frontmatter | 8 项必填 + `author: "AI指挥家"` + `examples_zh/en` 各 3 条(用能跑通的真链接) |
15
- | 正文开头 | `## 概述`,口吻"默认已装好 CLI 与 API Key",故障才读 `references/aiconductor-skills-setup.md` |
16
- | 验真 | 外层 `success`/退出码不可信,一律落 `data.data.success`;失败给 `message` |
17
- | 不写价格 | 单价、扣费、"免费"不进 SKILL.md;余额不足这类故障信息可以写 |
18
- | 交付纪律 | 数字必须有出处(哪条数据算出来的),结论必须挂原话或原作品,不编造 |
19
- | 时区 | `create_time` 是 Unix 时间戳,统一显式转北京时间再展示,禁止把 UTC 当本地 |
20
- | 字段降级 | 上游透传字段可能缺(TikHub 侧变动),缺字段时降级展示,不报错也不猜 |
21
-
22
- ### 2. 三个技能的规格
23
-
24
- #### A. `douyin-topic-radar` — 抖音爆款选题
25
-
26
- - **用户话术**:最近什么在爆 / 这个赛道谁做起来了 / 找几个对标 / 我该拍什么
27
- - **命令**:`aiconductor-cli douyin video-search <keyword>`
28
- - **预设分档**(技能的核心价值,用户不需要知道参数存在)
29
-
30
- | 档位 | 参数 | 用途 |
31
- |---|---|---|
32
- | 近一周最热 | `--sort-type 1 --publish-time 7 --max-pages 1` | 默认档,看当下 |
33
- | 半年爆款池 | `--sort-type 1 --publish-time 180 --max-pages 2` | 找可复刻的长期爆款 |
34
- | 最新动向 | `--sort-type 2 --publish-time 1 --max-pages 1` | 追热点 |
35
- | 长视频对标 | `--sort-type 1 --filter-duration 5-10000` | 口播/知识类 |
36
-
37
- - **多关键词**:用户给一个赛道词时,自动扩 3–4 个同义词(AI编程 / AI写代码 / 程序员用AI),串行跑,按 `aweme_id` 合并去重
38
- - **交付契约**
39
- 1. Top N 表:点赞 / 评论 / 时长 / 发布时间(北京时间)/ 作者 / 标题
40
- 2. 共性:时长区间、发布时间段、标题钩子类型、内容形式(口播/实拍/图文)
41
- 3. 选题建议 3–5 条,每条挂"哪几条爆款支撑"
42
- 4. 数据口径一句:点赞是抓取时点快照,非实时
43
- - **过滤**:时长 < 10 秒(切片/图文)、重复搬运(同 desc)、点赞低于本批中位数
44
- - **边界**:不下载视频、不分析评论、不提取文案
45
- - **已知坑**
46
- - `max_seconds` 上限 **50 秒**,翻页会被截断且 `success` 仍为 `true`、`has_more=1` → 必须提示"未取全"
47
- - 搜索是关键词匹配不是赛道,关键词太泛会灌进无关内容,需要 `--filter-duration` 收窄
48
-
49
- #### B. `douyin-comment-insight` — 抖音评论洞察
50
-
51
- - **用户话术**:评论区在吵什么 / 用户对 XX 的真实反馈 / 这条视频为什么被骂
52
- - **命令**:`aiconductor-cli douyin video-comments <video_url> --max-count 50`
53
- - **交付契约**
54
- 1. 热评 Top 10:原话引用 + 点赞数(按 `digg_count` 降序,不按接口顺序)
55
- 2. 争议点:正反两派各自的原话,各 1–2 条
56
- 3. 用户诉求/高频提问:问得最多的问题
57
- 4. 情绪倾向:只给"按原话看,X 多于 Y",**不给编造的百分比**
58
- 5. 一句话结论
59
- - **过滤**:广告引流(私信/加V/主页)、纯表情、互喷、完全重复
60
- - **纪律**:结论必须挂原话;必须说明"样本是前 50 条,不代表全量民意"
61
- - **边界**:不抓二级评论(只用 `reply_comment_total` 给数量)、不爬作者主页
62
- - **已知坑**:`is_folded` 折叠评论、无评论/评论关闭、视频已删
63
-
64
- #### C. `douyin-account-audit` — 抖音账号拆解
65
-
66
- - **用户话术**:扒一下这个号 / 他为什么火 / 找几个这个领域的号
67
- - **命令链**:`user-detail` → `user-videos` →(可选)对 Top3 调 `video-info`
68
- - **找号模式**:用户只给赛道词时,先 `user-search <关键词> --fans-range … --user-type …`,再按简介/作品二次筛
69
- - **交付契约**
70
- 1. 账号画像:昵称 / 粉丝 / 获赞 / 作品数 / 认证 / 简介 / IP 属地
71
- 2. 更新频率:由 `create_time` 算近 N 条的周更条数
72
- 3. 内容结构:按主题分组,各组点赞中位数与爆款数
73
- 4. 爆款率:点赞 ≥ 该号中位数 × 3 的作品占比
74
- 5. 可复制点 3 条,每条挂具体作品
75
- 6. 口径一句:只分析了最近 N 条,不是全量
76
- - **脚本**:`scripts/profile.mjs`(纯统计,无网络)
77
- - 输入:`user-videos` 的原始 JSON;输出:结构化摘要(时间跨度、周更频率、中位数、爆款清单)
78
- - 理由:爆款率/频率这类口径每次手写必漂,固化成脚本才可复现
79
- - 三个技能里**只有它带脚本**;另外两个的排序过滤是单行逻辑,先不写,等 eval 发现重复劳动再抽
80
- - **已知坑**
81
- - `user-videos` 默认 1 页 / 50 秒;**`max_seconds` 上限 180**(已与服务方确认,`api_docs/douyin_user_videos.md` 同步为 180)。大号要取全就得显式调到 180,但仍必须在交付物里说明「只看了最近 N 条」
82
- - 大号一页约 20–30 条,必须一开始就说清"只看最近 N 条"
83
- - 私密/隐藏作品拿不到,粉丝数增长历史拿不到(只有当前值)
84
-
85
- ### 3. 验收标准(每个技能都要过)
86
-
87
- - [ ] `node bin/aiconductor-cli.js douyin <cmd> --help` 与技能里写的参数逐项一致
88
- - [ ] frontmatter 8 项齐全,`name == 目录名`,`references/` 路径可达
89
- - [ ] 真实调用至少 1 次:字段路径对得上、时间戳是北京时间、交付物符合契约
90
- - [ ] 失败分支实测 1 次(错链接/无评论/空结果),能识别且话术对
91
- - [ ] 全文零价格词(`元/计费/扣费/付费/免费/按次`)
92
- - [ ] 两个副本正文逐字一致;提交到项目仓库与技能库
93
- - [ ] **目录本身可直接上架 WorkBuddy**:根目录 `SKILL.md`、`references/` 随目录一起走、不依赖目录外任何文件
94
- - [ ] 交付时附一份可复制的上架清单(见第四节)
95
-
96
- ---
97
-
98
- ## 二、可变层(脚手架,随实现修订)
99
-
100
- ### 实施顺序与粒度
101
-
102
- | 步 | 动作 | 产出 |
103
- |---|---|---|
104
- | 1 | `douyin-topic-radar` 起草 + 真实跑 1 个关键词 | SKILL.md + references |
105
- | 2 | 按交付契约手工核一遍(Top N 表、共性、建议是否挂得住) | 修订记录 |
106
- | 3 | `douyin-comment-insight` 起草 + 真实跑 1 条视频 | SKILL.md + references |
107
- | 4 | `douyin-account-audit` 起草 + `scripts/profile.mjs` + 真实跑 1 个号 | SKILL.md + references + scripts |
108
- | 5 | 三技能交叉检查:命名/描述不互抢、边界互不重叠 | 一次 diff |
109
- | 6 | 提交(每个技能一个 commit) | 两个仓库 |
110
-
111
- ### 真实调用成本与授权
112
-
113
- 三个技能各需 1–2 次真实调用,合计约 1 元级。执行前单独确认(项目规矩:带 Key 的计费调用先问)。
114
-
115
- ### 风险
116
-
117
- 1. **同名技能互抢**:三个 description 必须写清边界(选题=找什么拍 / 评论=看反馈 / 拆号=看一个账号),否则平台内部排序自己打自己
118
- 2. **setup 副本增至 9 份**:改一次密钥说明要改 9 处。可选先做一件事——加一条比对命令(`md5sum` 全部副本),漂移立刻可见;不新增脚本
119
- 3. **字段漂移**:`video_search` 是 TikHub 透传,字段名可能变;技能里写成"缺字段降级",不硬依赖
120
- 4. **样本偏差**:搜索只返回前 N 页、评论只前 50 条、账号只最近 N 条;三个技能都必须把这句话写在交付物里,不能默认用户知道
121
-
122
- ---
123
-
124
- ## 四、WorkBuddy 交付格式(最终产物)
125
-
126
- 每个技能交出去的东西必须能直接上传,不靠二次解释:
127
-
128
- ```text
129
- <skill-name>/
130
- ├── SKILL.md # 8 项必填 frontmatter + 概述 + 执行/失败/限制
131
- └── references/
132
- └── aiconductor-skills-setup.md # 安装与密钥兜底,随目录走
133
- (douyin-account-audit 额外带 scripts/profile.mjs)
134
- ```
135
-
136
- 硬性要求:
137
-
138
- - 目录名 == `name`;根下必须有 `SKILL.md`
139
- - 引用的文件全在目录内,用 Markdown 链接 `[references/x.md](references/x.md)`
140
- - 不依赖仓库内的其他路径(技能是独立安装单元)
141
- - 不写价格(单价/扣费/"免费");余额不足这类故障提示可以写
142
- - 中文名、英文名、中英简介、中英案例各就各位(详情页直接读这些字段)
143
-
144
- 交付时额外给一份**上架清单**(可整段复制进 WorkBuddy 后台表单):
145
-
146
- | 项 | 内容 |
147
- |---|---|
148
- | 技能标识 / 目录 | `<name>` |
149
- | 中文名 / 英文名 | `display_name` / `display_name_en` |
150
- | 中文简介 / 英文简介 | `description_zh` / `description_en` |
151
- | 使用案例 ×3(中/英) | `examples_zh` / `examples_en`,用能真跑通的链接 |
152
- | 作者 | AI指挥家 |
153
- | 版本 | 1.0.0 |
154
-
155
- ---
156
-
157
- ## 五、未决问题(开工前请确认)
158
-
159
- 1. 命名是否接受:`douyin-topic-radar`(抖音爆款选题)、`douyin-comment-insight`(抖音评论洞察)、`douyin-account-audit`(抖音账号拆解)
160
- 2. 达人筛选并入账号拆解的"找号模式",不单独上架——是否同意
161
- 3. 是否先做第 1 个跑通再继续,还是三个一起起草完再统一直真实测