@9000ai/cli 0.3.0 → 0.5.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 +10 -1
- package/dist/client.js +111 -2
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +83 -0
- package/dist/commands/monitor.js +27 -4
- package/dist/commands/search.js +33 -5
- package/dist/commands/task.js +6 -2
- package/dist/commands/transcribe.js +19 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/postinstall.js +6 -1
- package/package.json +2 -2
- package/skills/9000AI-hub/SKILL.md +76 -148
- package/skills/9000AI-hub/references/usage-guidelines.md +75 -0
- package/skills/douyin-monitor/SKILL.md +54 -1
- package/skills/douyin-topic-discovery/SKILL.md +42 -0
- package/skills/video-transcription/SKILL.md +37 -0
- package/skills/9000AI-hub/configure.py +0 -56
- package/skills/9000AI-hub/init/SKILL.md +0 -130
- package/skills/9000AI-hub/init/templates/CLAUDE.md +0 -24
- package/skills/9000AI-hub/shared/__init__.py +0 -1
- package/skills/9000AI-hub/shared/runner.py +0 -135
- package/skills/douyin-monitor/agents/openai.yaml +0 -3
- package/skills/douyin-monitor/scripts/douyin_monitor_api.py +0 -273
- package/skills/douyin-topic-discovery/agents/openai.yaml +0 -3
- package/skills/douyin-topic-discovery/scripts/douyin_topic_discovery_api.py +0 -497
- package/skills/feedback/scripts/feedback_api.py +0 -93
- package/skills/video-transcription/agents/openai.yaml +0 -3
- package/skills/video-transcription/scripts/video_transcription_api.py +0 -183
package/dist/client.d.ts
CHANGED
|
@@ -7,4 +7,13 @@ export interface RequestOptions {
|
|
|
7
7
|
timeout?: number;
|
|
8
8
|
}
|
|
9
9
|
export declare function request(opts: RequestOptions): Promise<unknown>;
|
|
10
|
-
|
|
10
|
+
/** Poll a task/batch until terminal status. Returns final response data. */
|
|
11
|
+
export declare function pollUntilDone(path: string, opts?: {
|
|
12
|
+
interval?: number;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
quiet?: boolean;
|
|
15
|
+
}): Promise<unknown>;
|
|
16
|
+
export declare function printJson(data: unknown, opts?: {
|
|
17
|
+
fields?: string;
|
|
18
|
+
compact?: boolean;
|
|
19
|
+
}): void;
|
package/dist/client.js
CHANGED
|
@@ -40,6 +40,115 @@ export async function request(opts) {
|
|
|
40
40
|
clearTimeout(timer);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
/** Poll a task/batch until terminal status. Returns final response data. */
|
|
44
|
+
export async function pollUntilDone(path, opts) {
|
|
45
|
+
const interval = opts?.interval ?? 3_000;
|
|
46
|
+
const timeout = opts?.timeout ?? 600_000;
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
while (Date.now() - start < timeout) {
|
|
49
|
+
const data = await request({ method: "GET", path });
|
|
50
|
+
const resp = data;
|
|
51
|
+
const inner = (resp.data ?? resp);
|
|
52
|
+
const status = (inner.status ?? "").toUpperCase();
|
|
53
|
+
if (["SUCCESS", "COMPLETED", "FAILED", "ERROR"].includes(status)) {
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
if (!opts?.quiet) {
|
|
57
|
+
const elapsed = Math.round((Date.now() - start) / 1000);
|
|
58
|
+
console.error(`[${elapsed}s] status: ${status || "PENDING"} — polling...`);
|
|
59
|
+
}
|
|
60
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
61
|
+
}
|
|
62
|
+
console.error("Error: poll timed out");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
export function printJson(data, opts) {
|
|
66
|
+
let output = data;
|
|
67
|
+
// --fields: extract specified fields from items/data
|
|
68
|
+
if (opts?.fields) {
|
|
69
|
+
const fieldList = opts.fields.split(",").map((f) => f.trim());
|
|
70
|
+
output = pickFields(output, fieldList);
|
|
71
|
+
}
|
|
72
|
+
// --compact: one-line-per-item for arrays
|
|
73
|
+
if (opts?.compact) {
|
|
74
|
+
const arr = extractArray(output);
|
|
75
|
+
if (arr) {
|
|
76
|
+
arr.forEach((item) => console.log(JSON.stringify(item)));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
console.log(JSON.stringify(output, null, 2));
|
|
81
|
+
}
|
|
82
|
+
/** Deep-pick fields from API response. Handles nested data.items / data.tasks structures. */
|
|
83
|
+
function pickFields(data, fields) {
|
|
84
|
+
if (!data || typeof data !== "object")
|
|
85
|
+
return data;
|
|
86
|
+
const obj = data;
|
|
87
|
+
// If response has code/message/data wrapper, dig into data
|
|
88
|
+
if ("code" in obj && "data" in obj) {
|
|
89
|
+
const inner = obj.data;
|
|
90
|
+
if (!inner)
|
|
91
|
+
return obj;
|
|
92
|
+
return pickFieldsFromInner(inner, fields);
|
|
93
|
+
}
|
|
94
|
+
// Already unwrapped (e.g. inner passed directly)
|
|
95
|
+
return pickFieldsFromInner(obj, fields);
|
|
96
|
+
}
|
|
97
|
+
function pickFieldsFromInner(inner, fields) {
|
|
98
|
+
// If has items array, pick from each item
|
|
99
|
+
if (Array.isArray(inner.items)) {
|
|
100
|
+
return {
|
|
101
|
+
...stripArrays(inner),
|
|
102
|
+
items: inner.items.map((item) => pick(item, fields)),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// If has tasks array with nested items
|
|
106
|
+
if (Array.isArray(inner.tasks)) {
|
|
107
|
+
return {
|
|
108
|
+
...stripArrays(inner),
|
|
109
|
+
tasks: inner.tasks.map((task) => {
|
|
110
|
+
const t = { ...task };
|
|
111
|
+
if (Array.isArray(t.items)) {
|
|
112
|
+
t.items = t.items.map((item) => pick(item, fields));
|
|
113
|
+
}
|
|
114
|
+
return t;
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// Flat data object
|
|
119
|
+
return pick(inner, fields);
|
|
120
|
+
}
|
|
121
|
+
function pick(obj, fields) {
|
|
122
|
+
const result = {};
|
|
123
|
+
for (const field of fields) {
|
|
124
|
+
if (field in obj) {
|
|
125
|
+
result[field] = obj[field];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
function stripArrays(obj) {
|
|
131
|
+
const result = {};
|
|
132
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
133
|
+
if (!Array.isArray(v))
|
|
134
|
+
result[k] = v;
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
function extractArray(data) {
|
|
139
|
+
if (Array.isArray(data))
|
|
140
|
+
return data;
|
|
141
|
+
if (!data || typeof data !== "object")
|
|
142
|
+
return null;
|
|
143
|
+
const obj = data;
|
|
144
|
+
// Wrapped response: { code, data: { items: [...] } }
|
|
145
|
+
if ("code" in obj && "data" in obj) {
|
|
146
|
+
const inner = obj.data;
|
|
147
|
+
if (inner && Array.isArray(inner.items))
|
|
148
|
+
return inner.items;
|
|
149
|
+
}
|
|
150
|
+
// Already unwrapped: { items: [...] }
|
|
151
|
+
if (Array.isArray(obj.items))
|
|
152
|
+
return obj.items;
|
|
153
|
+
return null;
|
|
45
154
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join, relative } from "path";
|
|
4
|
+
const TEMPLATES_DIR = join(homedir(), ".9000ai", "skills", "9000AI-hub", "init", "templates");
|
|
5
|
+
/** Files under profile/ are never overwritten — user may have edited them. */
|
|
6
|
+
const NEVER_OVERWRITE_DIRS = ["profile"];
|
|
7
|
+
function copyTemplates(src, dest, relBase) {
|
|
8
|
+
const created = [];
|
|
9
|
+
const skipped = [];
|
|
10
|
+
if (!existsSync(src))
|
|
11
|
+
return { created, skipped };
|
|
12
|
+
const entries = readdirSync(src);
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const srcPath = join(src, entry);
|
|
15
|
+
const destPath = join(dest, entry);
|
|
16
|
+
const rel = relative(relBase, destPath);
|
|
17
|
+
const stat = statSync(srcPath);
|
|
18
|
+
if (stat.isDirectory()) {
|
|
19
|
+
if (!existsSync(destPath)) {
|
|
20
|
+
mkdirSync(destPath, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
const sub = copyTemplates(srcPath, destPath, relBase);
|
|
23
|
+
created.push(...sub.created);
|
|
24
|
+
skipped.push(...sub.skipped);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
if (existsSync(destPath)) {
|
|
28
|
+
// Never overwrite profile files
|
|
29
|
+
const topDir = rel.split(/[/\\]/)[0];
|
|
30
|
+
if (NEVER_OVERWRITE_DIRS.includes(topDir)) {
|
|
31
|
+
skipped.push(rel);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Other existing files: also skip (idempotent)
|
|
35
|
+
skipped.push(rel);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
mkdirSync(join(destPath, ".."), { recursive: true });
|
|
39
|
+
copyFileSync(srcPath, destPath);
|
|
40
|
+
created.push(rel);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { created, skipped };
|
|
45
|
+
}
|
|
46
|
+
export function registerInitCommand(program) {
|
|
47
|
+
program
|
|
48
|
+
.command("init")
|
|
49
|
+
.description("Initialize content workspace — copy templates to project directory")
|
|
50
|
+
.requiredOption("--path <dir>", "Project directory to initialize")
|
|
51
|
+
.action((opts) => {
|
|
52
|
+
const dest = opts.path;
|
|
53
|
+
// Check templates exist
|
|
54
|
+
if (!existsSync(TEMPLATES_DIR)) {
|
|
55
|
+
console.error(`Error: Templates not found at ${TEMPLATES_DIR}\n` +
|
|
56
|
+
`Run: npm install -g @9000ai/cli`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
// Create dest if needed
|
|
60
|
+
if (!existsSync(dest)) {
|
|
61
|
+
mkdirSync(dest, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
const { created, skipped } = copyTemplates(TEMPLATES_DIR, dest, dest);
|
|
64
|
+
// Output summary
|
|
65
|
+
if (created.length > 0) {
|
|
66
|
+
console.log(`Created ${created.length} files:`);
|
|
67
|
+
created.forEach((f) => console.log(` + ${f}`));
|
|
68
|
+
}
|
|
69
|
+
if (skipped.length > 0) {
|
|
70
|
+
console.log(`Skipped ${skipped.length} existing files:`);
|
|
71
|
+
skipped.forEach((f) => console.log(` - ${f}`));
|
|
72
|
+
}
|
|
73
|
+
if (created.length === 0 && skipped.length === 0) {
|
|
74
|
+
console.log("No template files found.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(`\nWorkspace ready: ${dest}`);
|
|
78
|
+
console.log(`\nNext steps:`);
|
|
79
|
+
console.log(` 1. Fill in profile/ (identity.md, voice.md, topics.md, product.md)`);
|
|
80
|
+
console.log(` 2. cd ${dest}`);
|
|
81
|
+
console.log(` 3. Open Claude Code and start working`);
|
|
82
|
+
});
|
|
83
|
+
}
|
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,40 @@ 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 });
|
|
106
|
+
});
|
|
107
|
+
cmd
|
|
108
|
+
.command("fetch")
|
|
109
|
+
.description("Fetch user homepage videos directly (sync, no queue)")
|
|
110
|
+
.requiredOption("--sec-user <id>", "User sec_user_id or homepage URL")
|
|
111
|
+
.option("--count <n>", "Number of videos to fetch", "20")
|
|
112
|
+
.option("--sort <n>", "Sort: 0=newest 1=hottest", "0")
|
|
113
|
+
.option("--fields <list>", "Comma-separated fields to extract")
|
|
114
|
+
.option("--compact", "One JSON object per line")
|
|
115
|
+
.action(async (opts) => {
|
|
116
|
+
const params = new URLSearchParams({
|
|
117
|
+
sec_user_id: opts.secUser,
|
|
118
|
+
count: opts.count,
|
|
119
|
+
sort_type: opts.sort,
|
|
120
|
+
});
|
|
121
|
+
const data = await request({ method: "GET", path: `/api/v1/douyin/monitor/user-posts?${params}` });
|
|
122
|
+
printJson(data, { fields: opts.fields, compact: opts.compact });
|
|
100
123
|
});
|
|
101
124
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request, printJson } from "../client.js";
|
|
1
|
+
import { request, printJson, pollUntilDone } from "../client.js";
|
|
2
2
|
import { writeJson, writeTsv, listOutputFiles, readOutputJson, timestampSlug } from "../output.js";
|
|
3
3
|
export function registerSearchCommands(parent) {
|
|
4
4
|
const cmd = parent.command("search").description("Douyin topic discovery — trending boards and keyword search");
|
|
@@ -36,6 +36,9 @@ export function registerSearchCommands(parent) {
|
|
|
36
36
|
.option("--filter-duration <v>", "Duration filter", "")
|
|
37
37
|
.option("--min-likes <n>", "Minimum likes", "0")
|
|
38
38
|
.option("--min-comments <n>", "Minimum comments", "0")
|
|
39
|
+
.option("--wait", "Poll until batch completes then print results")
|
|
40
|
+
.option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
|
|
41
|
+
.option("--compact", "One JSON object per line (used with --wait)")
|
|
39
42
|
.action(async (keywords, opts) => {
|
|
40
43
|
const payload = {
|
|
41
44
|
keywords,
|
|
@@ -55,14 +58,39 @@ export function registerSearchCommands(parent) {
|
|
|
55
58
|
});
|
|
56
59
|
const resp = data;
|
|
57
60
|
const inner = resp.data;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const batchId = inner?.batch_id;
|
|
62
|
+
if (!opts.wait || !batchId) {
|
|
63
|
+
console.log(`Batch submitted → batch_id: ${batchId}`);
|
|
64
|
+
console.log(`Use: 9000ai search batch-result --batch-id ${batchId}`);
|
|
65
|
+
printJson(inner);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// --wait: poll then fetch results
|
|
69
|
+
console.error(`Batch submitted → ${batchId}, waiting...`);
|
|
70
|
+
await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
|
|
71
|
+
const resultData = await request({
|
|
72
|
+
method: "GET",
|
|
73
|
+
path: `/api/v1/douyin/discovery/search-videos/batch/${batchId}/results`,
|
|
74
|
+
});
|
|
75
|
+
const resultResp = resultData;
|
|
76
|
+
const resultInner = resultResp.data;
|
|
77
|
+
const items = (resultInner?.items ?? []);
|
|
78
|
+
if (items.length > 0) {
|
|
79
|
+
const slug = timestampSlug();
|
|
80
|
+
writeJson("latest_search.json", resultInner);
|
|
81
|
+
writeJson(`latest_search_${slug}.json`, resultInner);
|
|
82
|
+
writeTsv("latest_search.tsv", items);
|
|
83
|
+
writeTsv(`latest_search_${slug}.tsv`, items);
|
|
84
|
+
console.error(`${items.length} items → output/latest_search.json`);
|
|
85
|
+
}
|
|
86
|
+
printJson(resultInner, { fields: opts.fields, compact: opts.compact });
|
|
61
87
|
});
|
|
62
88
|
cmd
|
|
63
89
|
.command("batch-result")
|
|
64
90
|
.description("Query async search batch results")
|
|
65
91
|
.requiredOption("--batch-id <id>", "Batch ID")
|
|
92
|
+
.option("--fields <list>", "Comma-separated fields to extract (e.g. desc,author_name,likes,video_url)")
|
|
93
|
+
.option("--compact", "One JSON object per line, no indentation")
|
|
66
94
|
.action(async (opts) => {
|
|
67
95
|
const data = await request({
|
|
68
96
|
method: "GET",
|
|
@@ -79,7 +107,7 @@ export function registerSearchCommands(parent) {
|
|
|
79
107
|
writeTsv(`latest_search_${slug}.tsv`, items);
|
|
80
108
|
console.log(`${items.length} items → output/latest_search.json`);
|
|
81
109
|
}
|
|
82
|
-
printJson(inner);
|
|
110
|
+
printJson(inner, { fields: opts.fields, compact: opts.compact });
|
|
83
111
|
});
|
|
84
112
|
cmd
|
|
85
113
|
.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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request, printJson } from "../client.js";
|
|
1
|
+
import { request, printJson, pollUntilDone } from "../client.js";
|
|
2
2
|
import { loadJsonFile } from "../utils/format.js";
|
|
3
3
|
export function registerTranscribeCommands(parent) {
|
|
4
4
|
const cmd = parent.command("transcribe").description("Video / audio transcription");
|
|
@@ -6,6 +6,9 @@ export function registerTranscribeCommands(parent) {
|
|
|
6
6
|
.command("submit")
|
|
7
7
|
.description("Submit batch video-to-text task")
|
|
8
8
|
.requiredOption("--json-file <path>", "JSON file with video task details")
|
|
9
|
+
.option("--wait", "Poll until task completes then print results")
|
|
10
|
+
.option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
|
|
11
|
+
.option("--compact", "One JSON object per line (used with --wait)")
|
|
9
12
|
.action(async (opts) => {
|
|
10
13
|
const payload = loadJsonFile(opts.jsonFile);
|
|
11
14
|
const data = await request({
|
|
@@ -13,7 +16,21 @@ export function registerTranscribeCommands(parent) {
|
|
|
13
16
|
path: "/api/v1/media/batch-video-to-text",
|
|
14
17
|
payload,
|
|
15
18
|
});
|
|
16
|
-
|
|
19
|
+
const resp = data;
|
|
20
|
+
const inner = resp.data;
|
|
21
|
+
const batchId = inner?.batch_id;
|
|
22
|
+
if (!opts.wait || !batchId) {
|
|
23
|
+
printJson(data);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// --wait: poll then fetch results
|
|
27
|
+
console.error(`Batch submitted → ${batchId}, waiting...`);
|
|
28
|
+
await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
|
|
29
|
+
const resultData = await request({
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: `/api/v1/tasks/batch/${batchId}`,
|
|
32
|
+
});
|
|
33
|
+
printJson(resultData, { fields: opts.fields, compact: opts.compact });
|
|
17
34
|
});
|
|
18
35
|
cmd
|
|
19
36
|
.command("text")
|
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";
|
|
@@ -7,11 +8,12 @@ import { registerTranscribeCommands } from "./commands/transcribe.js";
|
|
|
7
8
|
import { registerTaskCommands } from "./commands/task.js";
|
|
8
9
|
import { registerFeedbackCommands } from "./commands/feedback.js";
|
|
9
10
|
import { registerSkillCommands } from "./commands/skill.js";
|
|
11
|
+
import { registerInitCommand } from "./commands/init.js";
|
|
10
12
|
const program = new Command();
|
|
11
13
|
program
|
|
12
14
|
.name("9000ai")
|
|
13
15
|
.description("9000AI Toolbox CLI — unified interface for 9000AI platform")
|
|
14
|
-
.version("0.
|
|
16
|
+
.version("0.5.0");
|
|
15
17
|
registerConfigCommands(program);
|
|
16
18
|
registerAuthCommands(program);
|
|
17
19
|
registerSearchCommands(program);
|
|
@@ -20,6 +22,7 @@ registerTranscribeCommands(program);
|
|
|
20
22
|
registerTaskCommands(program);
|
|
21
23
|
registerFeedbackCommands(program);
|
|
22
24
|
registerSkillCommands(program);
|
|
25
|
+
registerInitCommand(program);
|
|
23
26
|
program.parseAsync(process.argv).catch((err) => {
|
|
24
27
|
console.error(err);
|
|
25
28
|
process.exit(1);
|
package/dist/postinstall.js
CHANGED
|
@@ -14,4 +14,9 @@ if (!existsSync(src)) {
|
|
|
14
14
|
}
|
|
15
15
|
mkdirSync(dest, { recursive: true });
|
|
16
16
|
cpSync(src, dest, { recursive: true, force: true });
|
|
17
|
-
console.log(
|
|
17
|
+
console.log(`\n9000AI CLI installed successfully!\n`);
|
|
18
|
+
console.log(`Skills installed: ${dest}\n`);
|
|
19
|
+
console.log(`Get started:`);
|
|
20
|
+
console.log(` 1. 9000ai config set --base-url <server-url> --api-key <key>`);
|
|
21
|
+
console.log(` 2. 9000ai init --path <your-project-dir>`);
|
|
22
|
+
console.log(` 3. cd <your-project-dir> → open Claude Code → start working\n`);
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@9000ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "9000AI Toolbox CLI — unified command-line interface for 9000AI platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"9000ai": "./dist/index"
|
|
7
|
+
"9000ai": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|