@9000ai/cli 0.2.0 → 0.4.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.
- package/dist/client.d.ts +4 -1
- package/dist/client.js +89 -2
- package/dist/commands/monitor.js +10 -4
- package/dist/commands/search.js +3 -1
- package/dist/commands/task.js +6 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/package.json +1 -1
- package/skills/9000AI-hub/SKILL.md +43 -0
- package/skills/douyin-monitor/SKILL.md +42 -0
- package/skills/douyin-topic-discovery/SKILL.md +42 -0
- package/skills/video-transcription/SKILL.md +37 -0
package/dist/client.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ export interface RequestOptions {
|
|
|
7
7
|
timeout?: number;
|
|
8
8
|
}
|
|
9
9
|
export declare function request(opts: RequestOptions): Promise<unknown>;
|
|
10
|
-
export declare function printJson(data: unknown
|
|
10
|
+
export declare function printJson(data: unknown, opts?: {
|
|
11
|
+
fields?: string;
|
|
12
|
+
compact?: boolean;
|
|
13
|
+
}): void;
|
package/dist/client.js
CHANGED
|
@@ -40,6 +40,93 @@ export async function request(opts) {
|
|
|
40
40
|
clearTimeout(timer);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
export function printJson(data) {
|
|
44
|
-
|
|
43
|
+
export function printJson(data, opts) {
|
|
44
|
+
let output = data;
|
|
45
|
+
// --fields: extract specified fields from items/data
|
|
46
|
+
if (opts?.fields) {
|
|
47
|
+
const fieldList = opts.fields.split(",").map((f) => f.trim());
|
|
48
|
+
output = pickFields(output, fieldList);
|
|
49
|
+
}
|
|
50
|
+
// --compact: one-line-per-item for arrays
|
|
51
|
+
if (opts?.compact) {
|
|
52
|
+
const arr = extractArray(output);
|
|
53
|
+
if (arr) {
|
|
54
|
+
arr.forEach((item) => console.log(JSON.stringify(item)));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
console.log(JSON.stringify(output, null, 2));
|
|
59
|
+
}
|
|
60
|
+
/** Deep-pick fields from API response. Handles nested data.items / data.tasks structures. */
|
|
61
|
+
function pickFields(data, fields) {
|
|
62
|
+
if (!data || typeof data !== "object")
|
|
63
|
+
return data;
|
|
64
|
+
const obj = data;
|
|
65
|
+
// If response has code/message/data wrapper, dig into data
|
|
66
|
+
if ("code" in obj && "data" in obj) {
|
|
67
|
+
const inner = obj.data;
|
|
68
|
+
if (!inner)
|
|
69
|
+
return obj;
|
|
70
|
+
return pickFieldsFromInner(inner, fields);
|
|
71
|
+
}
|
|
72
|
+
// Already unwrapped (e.g. inner passed directly)
|
|
73
|
+
return pickFieldsFromInner(obj, fields);
|
|
74
|
+
}
|
|
75
|
+
function pickFieldsFromInner(inner, fields) {
|
|
76
|
+
// If has items array, pick from each item
|
|
77
|
+
if (Array.isArray(inner.items)) {
|
|
78
|
+
return {
|
|
79
|
+
...stripArrays(inner),
|
|
80
|
+
items: inner.items.map((item) => pick(item, fields)),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// If has tasks array with nested items
|
|
84
|
+
if (Array.isArray(inner.tasks)) {
|
|
85
|
+
return {
|
|
86
|
+
...stripArrays(inner),
|
|
87
|
+
tasks: inner.tasks.map((task) => {
|
|
88
|
+
const t = { ...task };
|
|
89
|
+
if (Array.isArray(t.items)) {
|
|
90
|
+
t.items = t.items.map((item) => pick(item, fields));
|
|
91
|
+
}
|
|
92
|
+
return t;
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Flat data object
|
|
97
|
+
return pick(inner, fields);
|
|
98
|
+
}
|
|
99
|
+
function pick(obj, fields) {
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const field of fields) {
|
|
102
|
+
if (field in obj) {
|
|
103
|
+
result[field] = obj[field];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function stripArrays(obj) {
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
111
|
+
if (!Array.isArray(v))
|
|
112
|
+
result[k] = v;
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
function extractArray(data) {
|
|
117
|
+
if (Array.isArray(data))
|
|
118
|
+
return data;
|
|
119
|
+
if (!data || typeof data !== "object")
|
|
120
|
+
return null;
|
|
121
|
+
const obj = data;
|
|
122
|
+
// Wrapped response: { code, data: { items: [...] } }
|
|
123
|
+
if ("code" in obj && "data" in obj) {
|
|
124
|
+
const inner = obj.data;
|
|
125
|
+
if (inner && Array.isArray(inner.items))
|
|
126
|
+
return inner.items;
|
|
127
|
+
}
|
|
128
|
+
// Already unwrapped: { items: [...] }
|
|
129
|
+
if (Array.isArray(obj.items))
|
|
130
|
+
return obj.items;
|
|
131
|
+
return null;
|
|
45
132
|
}
|
package/dist/commands/monitor.js
CHANGED
|
@@ -5,9 +5,11 @@ export function registerMonitorCommands(parent) {
|
|
|
5
5
|
cmd
|
|
6
6
|
.command("list-creators")
|
|
7
7
|
.description("List all monitoring targets")
|
|
8
|
-
.
|
|
8
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
9
|
+
.option("--compact", "One JSON object per line")
|
|
10
|
+
.action(async (opts) => {
|
|
9
11
|
const data = await request({ method: "GET", path: "/api/v1/douyin/monitor/creators" });
|
|
10
|
-
printJson(data);
|
|
12
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
11
13
|
});
|
|
12
14
|
cmd
|
|
13
15
|
.command("create-creator")
|
|
@@ -83,19 +85,23 @@ export function registerMonitorCommands(parent) {
|
|
|
83
85
|
.option("--page <n>", "Page number", "1")
|
|
84
86
|
.option("--page-size <n>", "Items per page", "20")
|
|
85
87
|
.option("--status <status>", "Filter by status")
|
|
88
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
89
|
+
.option("--compact", "One JSON object per line")
|
|
86
90
|
.action(async (opts) => {
|
|
87
91
|
const params = new URLSearchParams({ page: opts.page, page_size: opts.pageSize });
|
|
88
92
|
if (opts.status)
|
|
89
93
|
params.set("status", opts.status);
|
|
90
94
|
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/runs?${params}` });
|
|
91
|
-
printJson(data);
|
|
95
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
92
96
|
});
|
|
93
97
|
cmd
|
|
94
98
|
.command("run-detail")
|
|
95
99
|
.description("View single run details")
|
|
96
100
|
.requiredOption("--run-id <id>", "Run ID")
|
|
101
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
102
|
+
.option("--compact", "One JSON object per line")
|
|
97
103
|
.action(async (opts) => {
|
|
98
104
|
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/runs/${opts.runId}` });
|
|
99
|
-
printJson(data);
|
|
105
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
100
106
|
});
|
|
101
107
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -63,6 +63,8 @@ export function registerSearchCommands(parent) {
|
|
|
63
63
|
.command("batch-result")
|
|
64
64
|
.description("Query async search batch results")
|
|
65
65
|
.requiredOption("--batch-id <id>", "Batch ID")
|
|
66
|
+
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,author_name,likes,video_url)")
|
|
67
|
+
.option("--compact", "One JSON object per line, no indentation")
|
|
66
68
|
.action(async (opts) => {
|
|
67
69
|
const data = await request({
|
|
68
70
|
method: "GET",
|
|
@@ -79,7 +81,7 @@ export function registerSearchCommands(parent) {
|
|
|
79
81
|
writeTsv(`latest_search_${slug}.tsv`, items);
|
|
80
82
|
console.log(`${items.length} items → output/latest_search.json`);
|
|
81
83
|
}
|
|
82
|
-
printJson(inner);
|
|
84
|
+
printJson(inner, { fields: opts.fields, compact: opts.compact });
|
|
83
85
|
});
|
|
84
86
|
cmd
|
|
85
87
|
.command("list-output")
|
package/dist/commands/task.js
CHANGED
|
@@ -5,16 +5,20 @@ export function registerTaskCommands(parent) {
|
|
|
5
5
|
.command("status")
|
|
6
6
|
.description("Check task status")
|
|
7
7
|
.requiredOption("--task-id <id>", "Task ID")
|
|
8
|
+
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,video_url,likes)")
|
|
9
|
+
.option("--compact", "One JSON object per line, no indentation")
|
|
8
10
|
.action(async (opts) => {
|
|
9
11
|
const data = await request({ method: "GET", path: `/api/v1/tasks/${opts.taskId}` });
|
|
10
|
-
printJson(data);
|
|
12
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
11
13
|
});
|
|
12
14
|
cmd
|
|
13
15
|
.command("results")
|
|
14
16
|
.description("Get task results")
|
|
15
17
|
.requiredOption("--task-id <id>", "Task ID")
|
|
18
|
+
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,video_url,likes)")
|
|
19
|
+
.option("--compact", "One JSON object per line, no indentation")
|
|
16
20
|
.action(async (opts) => {
|
|
17
21
|
const data = await request({ method: "GET", path: `/api/v1/tasks/${opts.taskId}/results` });
|
|
18
|
-
printJson(data);
|
|
22
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
19
23
|
});
|
|
20
24
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { registerConfigCommands } from "./commands/config.js";
|
|
3
4
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
@@ -11,7 +12,7 @@ const program = new Command();
|
|
|
11
12
|
program
|
|
12
13
|
.name("9000ai")
|
|
13
14
|
.description("9000AI Toolbox CLI — unified interface for 9000AI platform")
|
|
14
|
-
.version("0.
|
|
15
|
+
.version("0.4.0");
|
|
15
16
|
registerConfigCommands(program);
|
|
16
17
|
registerAuthCommands(program);
|
|
17
18
|
registerSearchCommands(program);
|
package/package.json
CHANGED
|
@@ -184,6 +184,49 @@ $env:PYTHONIOENCODING = 'utf-8'
|
|
|
184
184
|
- 发现某个工作流可以改进或串联自动化
|
|
185
185
|
- 用户表达了不满、困惑或明确的改进意见
|
|
186
186
|
|
|
187
|
+
## 性能准则(必须遵守)
|
|
188
|
+
|
|
189
|
+
### 数据精简
|
|
190
|
+
|
|
191
|
+
所有查询命令都支持 `--fields` 和 `--compact` 参数。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# 错误 — 返回 83KB 全量数据,浪费上下文
|
|
195
|
+
9000ai task results --task-id <id>
|
|
196
|
+
|
|
197
|
+
# 正确 — 只取需要的 4 个字段
|
|
198
|
+
9000ai task results --task-id <id> --fields desc,video_url,likes,author_name
|
|
199
|
+
|
|
200
|
+
# 更紧凑 — 每条一行,适合批量处理
|
|
201
|
+
9000ai task results --task-id <id> --fields desc,video_url,likes --compact
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
常用字段组合:
|
|
205
|
+
- 搜索结果概览:`--fields desc,author_name,likes,duration`
|
|
206
|
+
- 提取转写链接:`--fields video_url,download_url,play_url,desc`
|
|
207
|
+
- 监控结果摘要:`--fields desc,author_name,likes,comments,create_time`
|
|
208
|
+
|
|
209
|
+
### 并行执行
|
|
210
|
+
|
|
211
|
+
独立的操作必须并行,不要串行等待。用 subagent 或并行工具调用。
|
|
212
|
+
|
|
213
|
+
可以并行的场景:
|
|
214
|
+
- 写 inbox 文件 + 提交转写任务
|
|
215
|
+
- 多个关键词的搜索结果查询
|
|
216
|
+
- 查多个 task_id 的状态
|
|
217
|
+
|
|
218
|
+
不能并行的场景:
|
|
219
|
+
- 搜索 → 等结果 → 筛选(有依赖关系)
|
|
220
|
+
- 创建监控对象 → 提交监控任务(需要 creator_id)
|
|
221
|
+
|
|
222
|
+
### 减少中间文件
|
|
223
|
+
|
|
224
|
+
CLI 支持 `--json-file` 也支持直接传参。简单请求直接传参,不要写中间 JSON 文件。
|
|
225
|
+
|
|
226
|
+
### 不暴露中间过程
|
|
227
|
+
|
|
228
|
+
用户要的是结果,不是过程。拿到数据后直接输出摘要或执行下一步,不要把原始 JSON 展示给用户。
|
|
229
|
+
|
|
187
230
|
## 一句话总结
|
|
188
231
|
|
|
189
232
|
中台只负责:
|
|
@@ -103,6 +103,48 @@ output-format: task-oriented
|
|
|
103
103
|
9000ai task results --task-id <task_id>
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## 性能准则(必须遵守)
|
|
107
|
+
|
|
108
|
+
### 数据精简
|
|
109
|
+
|
|
110
|
+
任务结果和监控详情都支持 `--fields` 和 `--compact`。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# 错误 — 返回全量 JSON,浪费上下文
|
|
114
|
+
9000ai task results --task-id <id>
|
|
115
|
+
|
|
116
|
+
# 正确 — 只取监控摘要字段
|
|
117
|
+
9000ai task results --task-id <id> --fields desc,author_name,likes,comments,create_time
|
|
118
|
+
|
|
119
|
+
# 更紧凑 — 每条一行
|
|
120
|
+
9000ai task results --task-id <id> --fields desc,author_name,likes --compact
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
常用字段组合:
|
|
124
|
+
- 监控结果摘要:`--fields desc,author_name,likes,comments,create_time`
|
|
125
|
+
- 提取视频链接:`--fields desc,video_url,download_url,play_url`
|
|
126
|
+
- 运行状态检查:`--fields status,total,success,failed`
|
|
127
|
+
|
|
128
|
+
### 并行执行
|
|
129
|
+
|
|
130
|
+
独立操作必须并行,不要串行等待。
|
|
131
|
+
|
|
132
|
+
可以并行:
|
|
133
|
+
- 查多个 task_id 的状态或结果
|
|
134
|
+
- 写 inbox 文件 + 提交转写任务
|
|
135
|
+
|
|
136
|
+
不能并行:
|
|
137
|
+
- 创建监控对象 → 提交监控任务(需要 creator_id)
|
|
138
|
+
- 提交任务 → 查结果(需要 task_id)
|
|
139
|
+
|
|
140
|
+
### 减少中间文件
|
|
141
|
+
|
|
142
|
+
CLI 支持 `--json-file` 也支持直接传参。简单操作(启停、重置、删除)直接传参,不要写 JSON 文件。
|
|
143
|
+
|
|
144
|
+
### 不暴露中间过程
|
|
145
|
+
|
|
146
|
+
拿到监控结果后直接输出摘要或执行下一步,不要把原始 JSON 全量展示给用户。
|
|
147
|
+
|
|
106
148
|
## 参考
|
|
107
149
|
|
|
108
150
|
接口和请求体示例见:
|
|
@@ -141,6 +141,48 @@ output/
|
|
|
141
141
|
- `search` 每个关键词默认抓取 `30` 条
|
|
142
142
|
- 后端会基于 `search_general_v3` 自动翻页聚合,不需要额外传分页参数
|
|
143
143
|
|
|
144
|
+
## 性能准则(必须遵守)
|
|
145
|
+
|
|
146
|
+
### 数据精简
|
|
147
|
+
|
|
148
|
+
搜索结果和任务结果都支持 `--fields` 和 `--compact`。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 错误 — 返回全量 JSON,浪费上下文
|
|
152
|
+
9000ai search batch-result --batch-id <id>
|
|
153
|
+
|
|
154
|
+
# 正确 — 只取选题需要的字段
|
|
155
|
+
9000ai search batch-result --batch-id <id> --fields desc,author_name,likes,duration
|
|
156
|
+
|
|
157
|
+
# 更紧凑 — 每条一行
|
|
158
|
+
9000ai search batch-result --batch-id <id> --fields desc,author_name,likes --compact
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
常用字段组合:
|
|
162
|
+
- 选题概览:`--fields desc,author_name,likes,duration,comments`
|
|
163
|
+
- 提取视频链接:`--fields desc,video_url,download_url,play_url`
|
|
164
|
+
- 热榜摘要:`--fields title,hot_value,label`
|
|
165
|
+
|
|
166
|
+
### 并行执行
|
|
167
|
+
|
|
168
|
+
独立操作必须并行,不要串行等待。
|
|
169
|
+
|
|
170
|
+
可以并行:
|
|
171
|
+
- 多个 `batch-result` 查询(不同 batch_id)
|
|
172
|
+
- 写输出文件 + 查询下一批结果
|
|
173
|
+
- 多关键词搜索提交后,同时查询各自的 batch_id
|
|
174
|
+
|
|
175
|
+
不能并行:
|
|
176
|
+
- 提交搜索 → 等 batch_id 返回 → 查询结果(有依赖)
|
|
177
|
+
|
|
178
|
+
### 减少中间文件
|
|
179
|
+
|
|
180
|
+
简单的热榜或搜索请求直接用命令行参数,不要写 JSON 文件。
|
|
181
|
+
|
|
182
|
+
### 不暴露中间过程
|
|
183
|
+
|
|
184
|
+
拿到搜索结果后直接输出摘要或执行下一步,不要把原始 JSON 全量展示给用户。
|
|
185
|
+
|
|
144
186
|
## 一句话总结
|
|
145
187
|
|
|
146
188
|
这是一个”热榜 + search_general_v3 搜索流”的抖音选题发现 skill。
|
|
@@ -103,6 +103,43 @@ timecodes.sentences[*].text
|
|
|
103
103
|
9000ai transcribe text --task-id <task_id>
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## 性能准则(必须遵守)
|
|
107
|
+
|
|
108
|
+
### 数据精简
|
|
109
|
+
|
|
110
|
+
任务结果支持 `--fields` 和 `--compact`。**默认必须用 `--fields` 只取需要的字段,禁止全量读取。**
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# 错误 — 返回全量转写 JSON,浪费上下文
|
|
114
|
+
9000ai task results --task-id <id>
|
|
115
|
+
|
|
116
|
+
# 正确 — 只取需要的字段
|
|
117
|
+
9000ai task results --task-id <id> --fields status,output,video_url
|
|
118
|
+
|
|
119
|
+
# 只要原文 — 用专用命令,不要读全量 JSON
|
|
120
|
+
9000ai transcribe text --task-id <task_id>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
常用字段组合:
|
|
124
|
+
- 状态检查:`--fields status,progress,message`
|
|
125
|
+
- 结果提取:`--fields status,output,video_url`
|
|
126
|
+
|
|
127
|
+
### 并行执行
|
|
128
|
+
|
|
129
|
+
独立操作必须并行,不要串行等待。
|
|
130
|
+
|
|
131
|
+
可以并行:
|
|
132
|
+
- 查多个 task_id 的状态
|
|
133
|
+
- 提取多个任务的原文(多个 `transcribe text` 并行)
|
|
134
|
+
- 写文件 + 提交下一批转写任务
|
|
135
|
+
|
|
136
|
+
不能并行:
|
|
137
|
+
- 提交任务 → 查结果(需要 task_id,且任务需时间完成)
|
|
138
|
+
|
|
139
|
+
### 不暴露中间过程
|
|
140
|
+
|
|
141
|
+
拿到转写结果后直接输出文案或执行下一步,不要把原始 JSON 全量展示给用户。
|
|
142
|
+
|
|
106
143
|
## 参考
|
|
107
144
|
|
|
108
145
|
接口细节和 JSON 示例见 `references/endpoints.md`。
|