@tvbs-ai/news-rd 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.
- package/README.md +74 -0
- package/dist/cli.js +457 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @tvbs-ai/news-rd
|
|
2
|
+
|
|
3
|
+
TVBS News Rundown AI CLI — query rundowns, news candidates, Google Trends, and Langfuse prompts.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @tvbs-ai/news-rd
|
|
9
|
+
# or
|
|
10
|
+
npx @tvbs-ai/news-rd --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
news-rd <group> <command> [args] [options]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Groups
|
|
20
|
+
|
|
21
|
+
| Group | Description |
|
|
22
|
+
|-------|-------------|
|
|
23
|
+
| `config` | 時段與環境設定 |
|
|
24
|
+
| `rundown` | Rundown 查詢與版本比對 |
|
|
25
|
+
| `news` | 候選新聞與趨勢關鍵字 |
|
|
26
|
+
| `prompt` | Prompt 模板、變數與編譯 |
|
|
27
|
+
| `token` | JWT Token 管理 |
|
|
28
|
+
|
|
29
|
+
### Examples
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# List timeslots
|
|
33
|
+
news-rd config timeslots
|
|
34
|
+
|
|
35
|
+
# Get rundown
|
|
36
|
+
news-rd rundown get 2026-03-10 0600
|
|
37
|
+
|
|
38
|
+
# Get news candidates
|
|
39
|
+
news-rd news candidates 2026-03-10 0600 --category 政治
|
|
40
|
+
|
|
41
|
+
# Get Google Trends
|
|
42
|
+
news-rd news trends 2026-03-10 0600
|
|
43
|
+
|
|
44
|
+
# List prompts
|
|
45
|
+
news-rd prompt list
|
|
46
|
+
|
|
47
|
+
# Get compiled prompt
|
|
48
|
+
news-rd prompt compile 2026-03-10 0600
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Global Options
|
|
52
|
+
|
|
53
|
+
| Option | Description | Default |
|
|
54
|
+
|--------|-------------|---------|
|
|
55
|
+
| `--base-url URL` | API base URL | `http://localhost:3000` |
|
|
56
|
+
| `--token TOKEN` | JWT token (or `RD_TOKEN` env) | — |
|
|
57
|
+
| `--json` | Raw JSON output | — |
|
|
58
|
+
| `--help` | Show help | — |
|
|
59
|
+
|
|
60
|
+
### Environment Variables
|
|
61
|
+
|
|
62
|
+
| Variable | Description |
|
|
63
|
+
|----------|-------------|
|
|
64
|
+
| `RD_BASE_URL` | API base URL |
|
|
65
|
+
| `RD_TOKEN` | JWT authentication token |
|
|
66
|
+
|
|
67
|
+
## Requirements
|
|
68
|
+
|
|
69
|
+
- Node.js >= 18 (uses built-in fetch)
|
|
70
|
+
- A running TVBS News Rundown AI server
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
UNLICENSED
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
var args = process.argv.slice(2);
|
|
5
|
+
function parseArgs() {
|
|
6
|
+
const options = {
|
|
7
|
+
baseUrl: process.env.RD_BASE_URL || "http://localhost:3000",
|
|
8
|
+
token: process.env.RD_TOKEN || "",
|
|
9
|
+
json: false
|
|
10
|
+
};
|
|
11
|
+
const positional = [];
|
|
12
|
+
let group = "";
|
|
13
|
+
let command = "";
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
switch (arg) {
|
|
17
|
+
case "--base-url":
|
|
18
|
+
options.baseUrl = args[++i] || options.baseUrl;
|
|
19
|
+
break;
|
|
20
|
+
case "--token":
|
|
21
|
+
options.token = args[++i] || "";
|
|
22
|
+
break;
|
|
23
|
+
case "--json":
|
|
24
|
+
options.json = true;
|
|
25
|
+
break;
|
|
26
|
+
case "--category":
|
|
27
|
+
options.category = args[++i];
|
|
28
|
+
break;
|
|
29
|
+
case "--content-type":
|
|
30
|
+
options.contentType = args[++i];
|
|
31
|
+
break;
|
|
32
|
+
case "--limit":
|
|
33
|
+
options.limit = args[++i];
|
|
34
|
+
break;
|
|
35
|
+
case "--version":
|
|
36
|
+
options.version = args[++i];
|
|
37
|
+
break;
|
|
38
|
+
case "--variables":
|
|
39
|
+
options.variables = args[++i];
|
|
40
|
+
break;
|
|
41
|
+
case "--help":
|
|
42
|
+
case "-h":
|
|
43
|
+
if (group) {
|
|
44
|
+
showGroupHelp(group);
|
|
45
|
+
} else {
|
|
46
|
+
showHelp();
|
|
47
|
+
}
|
|
48
|
+
process.exit(0);
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
if (!group) {
|
|
52
|
+
group = arg;
|
|
53
|
+
} else if (!command) {
|
|
54
|
+
command = arg;
|
|
55
|
+
} else {
|
|
56
|
+
positional.push(arg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { group, command, positional, options };
|
|
61
|
+
}
|
|
62
|
+
function showHelp() {
|
|
63
|
+
console.log(`
|
|
64
|
+
tvbs-news-rd \u2014 TVBS News Rundown AI CLI
|
|
65
|
+
|
|
66
|
+
Usage: news-rd <group> <command> [args] [options]
|
|
67
|
+
|
|
68
|
+
Groups:
|
|
69
|
+
config \u6642\u6BB5\u8207\u74B0\u5883\u8A2D\u5B9A
|
|
70
|
+
rundown Rundown \u67E5\u8A62\u8207\u7248\u672C\u6BD4\u5C0D
|
|
71
|
+
news \u5019\u9078\u65B0\u805E\u8207\u8DA8\u52E2\u95DC\u9375\u5B57
|
|
72
|
+
prompt Prompt \u6A21\u677F\u3001\u8B8A\u6578\u8207\u7DE8\u8B6F
|
|
73
|
+
token JWT Token \u7BA1\u7406
|
|
74
|
+
|
|
75
|
+
Global Options:
|
|
76
|
+
--base-url URL API base URL (default: http://localhost:3000)
|
|
77
|
+
--token TOKEN JWT token (or RD_TOKEN env)
|
|
78
|
+
--json Raw JSON output
|
|
79
|
+
--help, -h Show help
|
|
80
|
+
|
|
81
|
+
Run 'news-rd <group> --help' for group-specific commands.
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
84
|
+
var groupHelps = {
|
|
85
|
+
config: `
|
|
86
|
+
config \u2014 \u6642\u6BB5\u8207\u74B0\u5883\u8A2D\u5B9A
|
|
87
|
+
|
|
88
|
+
Commands:
|
|
89
|
+
timeslots List all available timeslots
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
news-rd config timeslots
|
|
93
|
+
`,
|
|
94
|
+
rundown: `
|
|
95
|
+
rundown \u2014 Rundown \u67E5\u8A62\u8207\u7248\u672C\u6BD4\u5C0D
|
|
96
|
+
|
|
97
|
+
Commands:
|
|
98
|
+
get <date> <timeslot> Get rundown content
|
|
99
|
+
history <date> <timeslot> Get version history (initial + edits)
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
news-rd rundown get 2026-03-10 0600
|
|
103
|
+
news-rd rundown history 2026-03-10 0600
|
|
104
|
+
`,
|
|
105
|
+
news: `
|
|
106
|
+
news \u2014 \u5019\u9078\u65B0\u805E\u8207\u8DA8\u52E2\u95DC\u9375\u5B57
|
|
107
|
+
|
|
108
|
+
Commands:
|
|
109
|
+
candidates <date> <timeslot> Get scored candidates
|
|
110
|
+
trends <date> <timeslot> Get Google Trends (4hr/24hr/48hr)
|
|
111
|
+
keywords <date> <timeslot> Get NewsRadar hot keywords
|
|
112
|
+
|
|
113
|
+
Options (candidates):
|
|
114
|
+
--category CAT Filter by category (\u653F\u6CBB, \u793E\u6703, \u570B\u969B, ...)
|
|
115
|
+
--content-type TYPE Filter by content type (feature, ...)
|
|
116
|
+
--limit N Result limit (default 50, max 500)
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
news-rd news candidates 2026-03-10 0600 --category \u653F\u6CBB --limit 20
|
|
120
|
+
news-rd news trends 2026-03-10 0600
|
|
121
|
+
news-rd news keywords 2026-03-10 0600
|
|
122
|
+
`,
|
|
123
|
+
prompt: `
|
|
124
|
+
prompt \u2014 Prompt \u6A21\u677F\u3001\u8B8A\u6578\u8207\u7DE8\u8B6F
|
|
125
|
+
|
|
126
|
+
Commands:
|
|
127
|
+
list List all Langfuse prompts
|
|
128
|
+
show <name> [--version N] Get prompt template
|
|
129
|
+
variables List available template variables
|
|
130
|
+
resolve <date> <timeslot> Resolve variables to actual values
|
|
131
|
+
compile <date> <timeslot> Get fully compiled prompt
|
|
132
|
+
|
|
133
|
+
Options:
|
|
134
|
+
--version N Specific prompt version (show, compile)
|
|
135
|
+
--variables VARS Comma-separated variable names (resolve)
|
|
136
|
+
|
|
137
|
+
Examples:
|
|
138
|
+
news-rd prompt list
|
|
139
|
+
news-rd prompt show rundown-generation-0600
|
|
140
|
+
news-rd prompt show rundown-generation-0600 --version 5
|
|
141
|
+
news-rd prompt variables
|
|
142
|
+
news-rd prompt resolve 2026-03-10 0600 --variables GOOGLE_TRENDS_FOUR_HR
|
|
143
|
+
news-rd prompt compile 2026-03-10 0600
|
|
144
|
+
`,
|
|
145
|
+
token: `
|
|
146
|
+
token \u2014 JWT Token \u7BA1\u7406
|
|
147
|
+
|
|
148
|
+
Commands:
|
|
149
|
+
generate Generate a new admin JWT token (requires Clerk session)
|
|
150
|
+
|
|
151
|
+
Examples:
|
|
152
|
+
news-rd token generate
|
|
153
|
+
`
|
|
154
|
+
};
|
|
155
|
+
function showGroupHelp(group) {
|
|
156
|
+
const help = groupHelps[group];
|
|
157
|
+
if (help) {
|
|
158
|
+
console.log(help);
|
|
159
|
+
} else {
|
|
160
|
+
console.error(`Unknown group: ${group}`);
|
|
161
|
+
showHelp();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function apiCall(path, options, method = "GET") {
|
|
165
|
+
const url = `${options.baseUrl}/api/admin${path}`;
|
|
166
|
+
const headers = {};
|
|
167
|
+
if (options.token) {
|
|
168
|
+
headers["Authorization"] = `Bearer ${options.token}`;
|
|
169
|
+
}
|
|
170
|
+
const response = await fetch(url, { method, headers });
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
const errorMsg = data?.error?.message || `HTTP ${response.status}`;
|
|
174
|
+
throw new Error(`API Error: ${errorMsg}`);
|
|
175
|
+
}
|
|
176
|
+
return data;
|
|
177
|
+
}
|
|
178
|
+
function qs(params) {
|
|
179
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
180
|
+
if (entries.length === 0) return "";
|
|
181
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
|
|
182
|
+
}
|
|
183
|
+
function printJson(data) {
|
|
184
|
+
console.log(JSON.stringify(data, null, 2));
|
|
185
|
+
}
|
|
186
|
+
function printTable(rows, columns) {
|
|
187
|
+
if (rows.length === 0) {
|
|
188
|
+
console.log("(no results)");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const cols = columns || Object.keys(rows[0]);
|
|
192
|
+
const widths = cols.map(
|
|
193
|
+
(col) => Math.max(col.length, ...rows.map((r) => String(r[col] ?? "").length))
|
|
194
|
+
);
|
|
195
|
+
console.log(cols.map((c, i) => c.padEnd(widths[i])).join(" "));
|
|
196
|
+
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
197
|
+
for (const row of rows) {
|
|
198
|
+
console.log(cols.map((c, i) => String(row[c] ?? "").padEnd(widths[i])).join(" "));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function requireDateTimeslot(positional, group, command) {
|
|
202
|
+
const [date, timeslot] = positional;
|
|
203
|
+
if (!date || !timeslot) {
|
|
204
|
+
console.error(`Usage: news-rd ${group} ${command} <date> <timeslot>`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
return [date, timeslot];
|
|
208
|
+
}
|
|
209
|
+
async function handleConfig(command, _positional, options) {
|
|
210
|
+
switch (command) {
|
|
211
|
+
case "timeslots": {
|
|
212
|
+
const data = await apiCall("/timeslots", options);
|
|
213
|
+
if (options.json) {
|
|
214
|
+
printJson(data);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
printTable(data.timeslots, ["slot_code", "description", "include_google_trends"]);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
default:
|
|
221
|
+
showGroupHelp("config");
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function handleRundown(command, positional, options) {
|
|
226
|
+
switch (command) {
|
|
227
|
+
case "get": {
|
|
228
|
+
const [date, timeslot] = requireDateTimeslot(positional, "rundown", "get");
|
|
229
|
+
const data = await apiCall(`/rundown${qs({ date, timeslot })}`, options);
|
|
230
|
+
if (options.json) {
|
|
231
|
+
printJson(data);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
console.log(`Rundown: ${data.rundown_id}`);
|
|
235
|
+
const items = data.rundown.items || [];
|
|
236
|
+
console.log(`Items: ${items.length}`);
|
|
237
|
+
printJson(data);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
case "history": {
|
|
241
|
+
const [date, timeslot] = requireDateTimeslot(positional, "rundown", "history");
|
|
242
|
+
const data = await apiCall(`/rundown/changes${qs({ date, timeslot })}`, options);
|
|
243
|
+
if (options.json) {
|
|
244
|
+
printJson(data);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
console.log(`Total changes: ${data.total_changes}
|
|
248
|
+
`);
|
|
249
|
+
printTable(data.versions.map((v) => ({
|
|
250
|
+
version: v.version,
|
|
251
|
+
label: v.label,
|
|
252
|
+
modified_at: v.modified_at || "-"
|
|
253
|
+
})), ["version", "label", "modified_at"]);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
default:
|
|
257
|
+
showGroupHelp("rundown");
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function handleNews(command, positional, options) {
|
|
262
|
+
switch (command) {
|
|
263
|
+
case "candidates": {
|
|
264
|
+
const [date, timeslot] = requireDateTimeslot(positional, "news", "candidates");
|
|
265
|
+
const data = await apiCall(`/news/candidates${qs({
|
|
266
|
+
date,
|
|
267
|
+
timeslot,
|
|
268
|
+
category: options.category,
|
|
269
|
+
content_type: options.contentType,
|
|
270
|
+
limit: options.limit
|
|
271
|
+
})}`, options);
|
|
272
|
+
if (options.json) {
|
|
273
|
+
printJson(data);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
console.log(`Statistics: total=${data.statistics.total}, with_slug=${data.statistics.with_slug}, without_slug=${data.statistics.without_slug}
|
|
277
|
+
`);
|
|
278
|
+
printTable(data.candidates.map((c) => ({
|
|
279
|
+
score: c.final_score?.toFixed(1),
|
|
280
|
+
title: String(c.title).substring(0, 50),
|
|
281
|
+
category: c.category,
|
|
282
|
+
slug: c.slug || "-",
|
|
283
|
+
video_id: c.video_id
|
|
284
|
+
})), ["score", "category", "title", "slug", "video_id"]);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
case "trends": {
|
|
288
|
+
const [date, timeslot] = requireDateTimeslot(positional, "news", "trends");
|
|
289
|
+
const data = await apiCall(`/trends${qs({ date, timeslot })}`, options);
|
|
290
|
+
if (options.json) {
|
|
291
|
+
printJson(data);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
for (const [window, trends] of Object.entries(data.google_trends)) {
|
|
295
|
+
console.log(`
|
|
296
|
+
${window} keywords (${trends.keywords.length}):`);
|
|
297
|
+
console.log(` ${trends.keywords.join(", ") || "(none)"}`);
|
|
298
|
+
}
|
|
299
|
+
console.log("");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
case "keywords": {
|
|
303
|
+
const [date, timeslot] = requireDateTimeslot(positional, "news", "keywords");
|
|
304
|
+
const data = await apiCall(`/newsradar${qs({ date, timeslot })}`, options);
|
|
305
|
+
if (options.json) {
|
|
306
|
+
printJson(data);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
console.log(`Hot keywords (${data.keywords.length}):`);
|
|
310
|
+
console.log(` ${data.keywords.join(", ") || "(none)"}`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
default:
|
|
314
|
+
showGroupHelp("news");
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function handlePrompt(command, positional, options) {
|
|
319
|
+
switch (command) {
|
|
320
|
+
case "list": {
|
|
321
|
+
const data = await apiCall("/prompt/list", options);
|
|
322
|
+
if (options.json) {
|
|
323
|
+
printJson(data);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
console.log(`Total: ${data.total}
|
|
327
|
+
`);
|
|
328
|
+
printTable(data.prompts, ["name", "category", "latest_version", "version_count", "labels"]);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
case "show": {
|
|
332
|
+
const name = positional[0];
|
|
333
|
+
if (!name) {
|
|
334
|
+
console.error("Usage: news-rd prompt show <name> [--version N]");
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
const data = await apiCall(`/prompt/template${qs({ name, version: options.version })}`, options);
|
|
338
|
+
if (options.json) {
|
|
339
|
+
printJson(data);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
console.log(`Prompt: ${data.name} (v${data.version})`);
|
|
343
|
+
console.log(`Labels: ${data.labels?.join(", ") || "(none)"}`);
|
|
344
|
+
console.log(`Length: ${data.prompt_length} chars`);
|
|
345
|
+
console.log(`Variables: ${data.variables_in_template.join(", ")}`);
|
|
346
|
+
console.log(`
|
|
347
|
+
--- Template ---
|
|
348
|
+
${data.template}
|
|
349
|
+
`);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
case "variables": {
|
|
353
|
+
const data = await apiCall("/prompt/rundown/variables/available", options);
|
|
354
|
+
if (options.json) {
|
|
355
|
+
printJson(data);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
console.log(`Available variables (${data.total}):
|
|
359
|
+
`);
|
|
360
|
+
printTable(data.variables, ["name", "value_type", "description"]);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
case "resolve": {
|
|
364
|
+
const [date, timeslot] = requireDateTimeslot(positional, "prompt", "resolve");
|
|
365
|
+
const data = await apiCall(`/prompt/rundown/variables/resolve${qs({
|
|
366
|
+
date,
|
|
367
|
+
timeslot,
|
|
368
|
+
variables: options.variables
|
|
369
|
+
})}`, options);
|
|
370
|
+
if (options.json) {
|
|
371
|
+
printJson(data);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
for (const v of data.resolved) {
|
|
375
|
+
console.log(`
|
|
376
|
+
${v.name} (${v.value_type}, ${v.value_length} chars${v.item_count !== void 0 ? `, ${v.item_count} items` : ""}):`);
|
|
377
|
+
const preview = v.value.length > 200 ? v.value.substring(0, 200) + "..." : v.value;
|
|
378
|
+
console.log(` ${preview}`);
|
|
379
|
+
}
|
|
380
|
+
console.log("");
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
case "compile": {
|
|
384
|
+
const [date, timeslot] = requireDateTimeslot(positional, "prompt", "compile");
|
|
385
|
+
const data = await apiCall(`/prompt/rundown/compiled${qs({ date, timeslot, version: options.version })}`, options);
|
|
386
|
+
if (options.json) {
|
|
387
|
+
printJson(data);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
console.log(`Prompt: ${data.prompt_name} (v${data.prompt_version})`);
|
|
391
|
+
console.log(`Length: ${data.prompt_length} chars`);
|
|
392
|
+
console.log(`Variables resolved: ${data.variables_resolved.join(", ")}`);
|
|
393
|
+
console.log(`Candidates: ${data.candidates_count}`);
|
|
394
|
+
console.log(`
|
|
395
|
+
--- Compiled Prompt ---
|
|
396
|
+
${data.compiled_prompt}
|
|
397
|
+
`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
default:
|
|
401
|
+
showGroupHelp("prompt");
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function handleToken(command, _positional, options) {
|
|
406
|
+
switch (command) {
|
|
407
|
+
case "generate": {
|
|
408
|
+
const data = await apiCall("/token", options, "POST");
|
|
409
|
+
if (options.json) {
|
|
410
|
+
printJson(data);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
console.log(`Token: ${data.token}`);
|
|
414
|
+
console.log(`Expires: ${data.expires_at}`);
|
|
415
|
+
console.log(`
|
|
416
|
+
Set env: export RD_TOKEN="${data.token}"`);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
default:
|
|
420
|
+
showGroupHelp("token");
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function main() {
|
|
425
|
+
const { group, command, positional, options } = parseArgs();
|
|
426
|
+
if (!group) {
|
|
427
|
+
showHelp();
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
switch (group) {
|
|
432
|
+
case "config":
|
|
433
|
+
await handleConfig(command, positional, options);
|
|
434
|
+
break;
|
|
435
|
+
case "rundown":
|
|
436
|
+
await handleRundown(command, positional, options);
|
|
437
|
+
break;
|
|
438
|
+
case "news":
|
|
439
|
+
await handleNews(command, positional, options);
|
|
440
|
+
break;
|
|
441
|
+
case "prompt":
|
|
442
|
+
await handlePrompt(command, positional, options);
|
|
443
|
+
break;
|
|
444
|
+
case "token":
|
|
445
|
+
await handleToken(command, positional, options);
|
|
446
|
+
break;
|
|
447
|
+
default:
|
|
448
|
+
console.error(`Unknown group: ${group}`);
|
|
449
|
+
showHelp();
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tvbs-ai/news-rd",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TVBS News Rundown AI CLI — query rundowns, candidates, trends, and prompts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"news-rd": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"start": "node dist/cli.js",
|
|
15
|
+
"dev": "tsx src/cli.ts",
|
|
16
|
+
"lint": "tsc --noEmit",
|
|
17
|
+
"prepublishOnly": "pnpm build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["tvbs", "news", "rundown", "ai", "cli"],
|
|
20
|
+
"license": "UNLICENSED",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"tsx": "^4.0.0",
|
|
27
|
+
"typescript": "^5.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|