@tvbs-ai/news-rd 0.1.2 → 0.2.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.
Files changed (2) hide show
  1. package/dist/cli.js +238 -67
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- var args = process.argv.slice(2);
4
+ var CLI_VERSION = "0.2.0";
5
+ var rawArgs = process.argv.slice(2);
5
6
  function parseArgs() {
6
7
  const options = {
7
8
  baseUrl: process.env.RD_BASE_URL || "https://news-rundown.tvbs.ai",
@@ -11,57 +12,87 @@ function parseArgs() {
11
12
  const positional = [];
12
13
  let group = "";
13
14
  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
- }
15
+ for (let i = 0; i < rawArgs.length; i++) {
16
+ const arg = rawArgs[i];
17
+ if (arg.startsWith("-")) {
18
+ switch (arg) {
19
+ case "--base-url":
20
+ options.baseUrl = rawArgs[++i] || options.baseUrl;
21
+ break;
22
+ case "--token":
23
+ options.token = rawArgs[++i] || "";
24
+ break;
25
+ case "--json":
26
+ options.json = true;
27
+ break;
28
+ case "--category":
29
+ options.category = rawArgs[++i];
30
+ break;
31
+ case "--content-type":
32
+ options.contentType = rawArgs[++i];
33
+ break;
34
+ case "--limit":
35
+ options.limit = rawArgs[++i];
36
+ break;
37
+ case "--version":
38
+ case "-v":
39
+ console.log(`@tvbs-ai/news-rd v${CLI_VERSION}`);
40
+ process.exit(0);
41
+ case "--variables":
42
+ options.variables = rawArgs[++i];
43
+ break;
44
+ case "--help":
45
+ case "-h":
46
+ break;
47
+ default:
48
+ console.error(`Unknown option: ${arg}`);
49
+ process.exit(1);
50
+ }
51
+ continue;
52
+ }
53
+ if (!group) {
54
+ group = arg;
55
+ } else if (!command) {
56
+ command = arg;
57
+ } else {
58
+ positional.push(arg);
58
59
  }
59
60
  }
61
+ if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
62
+ if (group) {
63
+ showGroupHelp(group);
64
+ } else {
65
+ showHelp();
66
+ }
67
+ process.exit(0);
68
+ }
60
69
  return { group, command, positional, options };
61
70
  }
71
+ var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
72
+ var VALID_TIMESLOTS = ["0600", "0700", "0800", "1400", "1500", "1600"];
73
+ function requireDateTimeslot(positional, group, command) {
74
+ const [date, timeslot] = positional;
75
+ if (!date || !timeslot) {
76
+ console.error(`Usage: news-rd ${group} ${command} <date> <timeslot>`);
77
+ console.error(` date: YYYY-MM-DD (e.g. 2026-03-10)`);
78
+ console.error(` timeslot: ${VALID_TIMESLOTS.join(" | ")}`);
79
+ process.exit(1);
80
+ }
81
+ if (!DATE_RE.test(date)) {
82
+ console.error(`Invalid date format: "${date}"`);
83
+ console.error(` Expected YYYY-MM-DD (e.g. 2026-03-10)`);
84
+ process.exit(1);
85
+ }
86
+ if (!VALID_TIMESLOTS.includes(timeslot)) {
87
+ console.error(`Invalid timeslot: "${timeslot}"`);
88
+ console.error(` Available: ${VALID_TIMESLOTS.join(", ")}`);
89
+ process.exit(1);
90
+ }
91
+ return [date, timeslot];
92
+ }
62
93
  function showHelp() {
63
94
  console.log(`
64
- tvbs-news-rd \u2014 TVBS News Rundown AI CLI
95
+ @tvbs-ai/news-rd v${CLI_VERSION} \u2014 TVBS News Rundown AI CLI
65
96
 
66
97
  Usage: news-rd <group> <command> [args] [options]
67
98
 
@@ -71,11 +102,13 @@ Groups:
71
102
  news \u5019\u9078\u65B0\u805E\u8207\u8DA8\u52E2\u95DC\u9375\u5B57
72
103
  prompt Prompt \u6A21\u677F\u3001\u8B8A\u6578\u8207\u7DE8\u8B6F
73
104
  token JWT Token \u7BA1\u7406
105
+ completion Shell auto-completion
74
106
 
75
107
  Global Options:
76
108
  --base-url URL API base URL (default: https://news-rundown.tvbs.ai)
77
109
  --token TOKEN JWT token (or RD_TOKEN env)
78
110
  --json Raw JSON output
111
+ --version, -v Show version
79
112
  --help, -h Show help
80
113
 
81
114
  Run 'news-rd <group> --help' for group-specific commands.
@@ -146,10 +179,25 @@ Examples:
146
179
  token \u2014 JWT Token \u7BA1\u7406
147
180
 
148
181
  Commands:
149
- generate Generate a new admin JWT token (requires Clerk session)
182
+ generate Generate a new admin JWT token
183
+
184
+ Note: Token generation requires a Clerk session cookie.
185
+ The easiest way is to use the Web UI \u2014 log in and click "API Token"
186
+ in the header. The CLI command is for scripting with an existing session.
150
187
 
151
188
  Examples:
152
189
  news-rd token generate
190
+ `,
191
+ completion: `
192
+ completion \u2014 Shell auto-completion
193
+
194
+ Commands:
195
+ bash Output bash completion script
196
+ zsh Output zsh completion script
197
+
198
+ Setup:
199
+ news-rd completion bash >> ~/.bashrc && source ~/.bashrc
200
+ news-rd completion zsh >> ~/.zshrc && source ~/.zshrc
153
201
  `
154
202
  };
155
203
  function showGroupHelp(group) {
@@ -223,8 +271,7 @@ async function apiCall(path, options, method = "GET") {
223
271
  console.error(` # or`);
224
272
  console.error(` export RD_TOKEN=YOUR_TOKEN
225
273
  `);
226
- console.error(` To generate a token, log in to the web UI and click "API Token",`);
227
- console.error(` or run: news-rd token generate (requires Clerk session).
274
+ console.error(` To generate a token, log in to the web UI and click "API Token".
228
275
  `);
229
276
  } else {
230
277
  console.error(` ${errorMsg}
@@ -259,14 +306,6 @@ function printTable(rows, columns) {
259
306
  console.log(cols.map((c, i) => String(row[c] ?? "").padEnd(widths[i])).join(" "));
260
307
  }
261
308
  }
262
- function requireDateTimeslot(positional, group, command) {
263
- const [date, timeslot] = positional;
264
- if (!date || !timeslot) {
265
- console.error(`Usage: news-rd ${group} ${command} <date> <timeslot>`);
266
- process.exit(1);
267
- }
268
- return [date, timeslot];
269
- }
270
309
  async function handleConfig(command, _positional, options) {
271
310
  switch (command) {
272
311
  case "timeslots": {
@@ -275,12 +314,12 @@ async function handleConfig(command, _positional, options) {
275
314
  printJson(data);
276
315
  return;
277
316
  }
278
- printTable(data.timeslots, ["slot_code", "description", "include_google_trends"]);
317
+ printTable(data.timeslots, ["slot_code", "description"]);
279
318
  return;
280
319
  }
281
320
  default:
282
321
  showGroupHelp("config");
283
- process.exit(1);
322
+ process.exit(0);
284
323
  }
285
324
  }
286
325
  async function handleRundown(command, positional, options) {
@@ -292,10 +331,20 @@ async function handleRundown(command, positional, options) {
292
331
  printJson(data);
293
332
  return;
294
333
  }
295
- console.log(`Rundown: ${data.rundown_id}`);
296
334
  const items = data.rundown.items || [];
297
- console.log(`Items: ${items.length}`);
298
- printJson(data);
335
+ console.log(`Rundown: ${data.rundown_id}`);
336
+ console.log(`Items: ${items.length}
337
+ `);
338
+ printTable(items.map((item, i) => {
339
+ const newsItem = item.news_item;
340
+ return {
341
+ "#": item.sequence ?? i + 1,
342
+ type: item.item_type,
343
+ title: newsItem ? String(newsItem.title ?? "").substring(0, 45) : item.placeholder?.caption ?? "-",
344
+ score: newsItem ? newsItem.scoring?.rundown_final_score ?? "-" : "-",
345
+ video_id: newsItem?.video_id ?? "-"
346
+ };
347
+ }), ["#", "type", "title", "score", "video_id"]);
299
348
  return;
300
349
  }
301
350
  case "history": {
@@ -316,7 +365,7 @@ async function handleRundown(command, positional, options) {
316
365
  }
317
366
  default:
318
367
  showGroupHelp("rundown");
319
- process.exit(1);
368
+ process.exit(0);
320
369
  }
321
370
  }
322
371
  async function handleNews(command, positional, options) {
@@ -373,7 +422,7 @@ ${window} keywords (${trends.keywords.length}):`);
373
422
  }
374
423
  default:
375
424
  showGroupHelp("news");
376
- process.exit(1);
425
+ process.exit(0);
377
426
  }
378
427
  }
379
428
  async function handlePrompt(command, positional, options) {
@@ -460,7 +509,7 @@ ${data.compiled_prompt}
460
509
  }
461
510
  default:
462
511
  showGroupHelp("prompt");
463
- process.exit(1);
512
+ process.exit(0);
464
513
  }
465
514
  }
466
515
  async function handleToken(command, _positional, options) {
@@ -479,14 +528,133 @@ Set env: export RD_TOKEN="${data.token}"`);
479
528
  }
480
529
  default:
481
530
  showGroupHelp("token");
482
- process.exit(1);
531
+ process.exit(0);
532
+ }
533
+ }
534
+ function handleCompletion(command) {
535
+ switch (command) {
536
+ case "bash":
537
+ console.log(generateBashCompletion());
538
+ return;
539
+ case "zsh":
540
+ console.log(generateZshCompletion());
541
+ return;
542
+ default:
543
+ showGroupHelp("completion");
544
+ process.exit(0);
483
545
  }
484
546
  }
547
+ function generateBashCompletion() {
548
+ return `# bash completion for news-rd
549
+ _news_rd_completions() {
550
+ local cur prev groups
551
+ cur="\${COMP_WORDS[COMP_CWORD]}"
552
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
553
+ groups="config rundown news prompt token completion"
554
+
555
+ case "\${COMP_WORDS[1]}" in
556
+ config)
557
+ COMPREPLY=($(compgen -W "timeslots" -- "$cur"))
558
+ ;;
559
+ rundown)
560
+ COMPREPLY=($(compgen -W "get history" -- "$cur"))
561
+ ;;
562
+ news)
563
+ COMPREPLY=($(compgen -W "candidates trends keywords" -- "$cur"))
564
+ ;;
565
+ prompt)
566
+ COMPREPLY=($(compgen -W "list show variables resolve compile" -- "$cur"))
567
+ ;;
568
+ token)
569
+ COMPREPLY=($(compgen -W "generate" -- "$cur"))
570
+ ;;
571
+ completion)
572
+ COMPREPLY=($(compgen -W "bash zsh" -- "$cur"))
573
+ ;;
574
+ *)
575
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
576
+ COMPREPLY=($(compgen -W "$groups --help --version --json" -- "$cur"))
577
+ fi
578
+ ;;
579
+ esac
580
+
581
+ case "$prev" in
582
+ --base-url|--token|--category|--content-type|--limit|--version|--variables)
583
+ COMPREPLY=()
584
+ ;;
585
+ get|history|candidates|trends|keywords|resolve|compile)
586
+ # Suggest timeslots after date
587
+ if [[ \${COMP_CWORD} -ge 4 ]]; then
588
+ COMPREPLY=($(compgen -W "0600 0700 0800 1400 1500 1600" -- "$cur"))
589
+ fi
590
+ ;;
591
+ esac
592
+ }
593
+ complete -F _news_rd_completions news-rd`;
594
+ }
595
+ function generateZshCompletion() {
596
+ return `# zsh completion for news-rd
597
+ #compdef news-rd
598
+
599
+ _news_rd() {
600
+ local -a groups
601
+ groups=(
602
+ 'config:\u6642\u6BB5\u8207\u74B0\u5883\u8A2D\u5B9A'
603
+ 'rundown:Rundown \u67E5\u8A62\u8207\u7248\u672C\u6BD4\u5C0D'
604
+ 'news:\u5019\u9078\u65B0\u805E\u8207\u8DA8\u52E2\u95DC\u9375\u5B57'
605
+ 'prompt:Prompt \u6A21\u677F\u3001\u8B8A\u6578\u8207\u7DE8\u8B6F'
606
+ 'token:JWT Token \u7BA1\u7406'
607
+ 'completion:Shell auto-completion'
608
+ )
609
+
610
+ local -a timeslots
611
+ timeslots=(0600 0700 0800 1400 1500 1600)
612
+
613
+ _arguments -C \\
614
+ '--base-url[API base URL]:url:' \\
615
+ '--token[JWT token]:token:' \\
616
+ '--json[Raw JSON output]' \\
617
+ '--version[Show version]' \\
618
+ '--help[Show help]' \\
619
+ '1:group:->group' \\
620
+ '*::arg:->args'
621
+
622
+ case $state in
623
+ group)
624
+ _describe 'group' groups
625
+ ;;
626
+ args)
627
+ case \${words[1]} in
628
+ config)
629
+ _values 'command' 'timeslots[List timeslots]'
630
+ ;;
631
+ rundown)
632
+ _values 'command' 'get[Get rundown]' 'history[Version history]'
633
+ ;;
634
+ news)
635
+ _values 'command' 'candidates[Scored candidates]' 'trends[Google Trends]' 'keywords[NewsRadar keywords]'
636
+ ;;
637
+ prompt)
638
+ _values 'command' 'list[List prompts]' 'show[Get template]' 'variables[List variables]' 'resolve[Resolve variables]' 'compile[Compiled prompt]'
639
+ ;;
640
+ token)
641
+ _values 'command' 'generate[Generate JWT token]'
642
+ ;;
643
+ completion)
644
+ _values 'command' 'bash[Bash completion]' 'zsh[Zsh completion]'
645
+ ;;
646
+ esac
647
+ ;;
648
+ esac
649
+ }
650
+
651
+ _news_rd "$@"`;
652
+ }
485
653
  async function main() {
486
654
  const { group, command, positional, options } = parseArgs();
487
655
  if (!group) {
488
656
  showHelp();
489
- process.exit(1);
657
+ process.exit(0);
490
658
  }
491
659
  try {
492
660
  switch (group) {
@@ -505,6 +673,9 @@ async function main() {
505
673
  case "token":
506
674
  await handleToken(command, positional, options);
507
675
  break;
676
+ case "completion":
677
+ handleCompletion(command);
678
+ break;
508
679
  default:
509
680
  console.error(`Unknown group: ${group}`);
510
681
  showHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tvbs-ai/news-rd",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "TVBS News Rundown AI CLI — query rundowns, candidates, trends, and prompts",
5
5
  "type": "module",
6
6
  "bin": {