@casatwy/deyo 0.1.1 → 0.1.4
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 +26 -1
- package/deyo.mjs +279 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,12 @@ deyo -O ./transcript.txt 'https://www.bilibili.com/video/BVxxxx'
|
|
|
93
93
|
deyo --format srt -O ./transcript.srt 'https://www.youtube.com/watch?v=xxxx'
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
为 AI 或日志系统输出结构化进度:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
deyo --progress-format jsonl -O ./transcript.txt 'https://www.youtube.com/watch?v=xxxx'
|
|
100
|
+
```
|
|
101
|
+
|
|
96
102
|
如果输出文件名本身就是 `.srt`,也可以省略 `--format srt`,CLI 会自动识别格式。
|
|
97
103
|
|
|
98
104
|
指定输出英文转写结果:
|
|
@@ -131,12 +137,31 @@ deyo --api-key 'deyo_sk_other' 'https://www.youtube.com/watch?v=xxxx'
|
|
|
131
137
|
- `.vtt` 会自动使用 `vtt`
|
|
132
138
|
- 其他情况默认使用 `text`
|
|
133
139
|
|
|
140
|
+
`--progress-format <value>`
|
|
141
|
+
|
|
142
|
+
指定进度输出格式,支持:
|
|
143
|
+
|
|
144
|
+
- `auto`
|
|
145
|
+
- `text`
|
|
146
|
+
- `jsonl`
|
|
147
|
+
|
|
148
|
+
默认值是 `auto`:
|
|
149
|
+
|
|
150
|
+
- 当 stderr 是 TTY 时,会保留当前的单行原地刷新进度体验
|
|
151
|
+
- 当 stderr 不是 TTY 时,会退化成逐行文本进度,避免控制字符污染日志或代理输出
|
|
152
|
+
|
|
153
|
+
如果你希望 AI 或脚本稳定读取进度事件,建议使用 `--progress-format jsonl`。此时 CLI 会继续把最终结果输出到 stdout 或文件,只把进度事件按 JSON Lines 写到 stderr。
|
|
154
|
+
|
|
134
155
|
`--api-key <key>`
|
|
135
156
|
|
|
136
157
|
为当前这一次命令临时指定 API Key。
|
|
137
158
|
如果你已经执行过 `deyo auth login`,平时通常不需要再传。
|
|
138
159
|
当它和本地已保存配置同时存在时,会优先使用这次命令里传入的 `--api-key`。
|
|
139
160
|
|
|
161
|
+
`-v, --version`
|
|
162
|
+
|
|
163
|
+
打印当前 CLI 版本号并退出,适合快速确认本机装的是哪个版本。
|
|
164
|
+
|
|
140
165
|
## 在 AI 工具中使用
|
|
141
166
|
|
|
142
167
|
如果你希望让 AI 助手在你的本机环境里调用 `deyo`,可以参考官方安装页:
|
|
@@ -150,7 +175,7 @@ deyo --api-key 'deyo_sk_other' 'https://www.youtube.com/watch?v=xxxx'
|
|
|
150
175
|
2. 如何向你索取一次 API Key
|
|
151
176
|
3. 如何在你的本地环境执行 `deyo auth login`
|
|
152
177
|
|
|
153
|
-
完成后,你就可以让 AI 直接帮你调用 `deyo`
|
|
178
|
+
完成后,你就可以让 AI 直接帮你调用 `deyo` 提交转写任务。对于需要在对话里持续同步进度的场景,AI 应优先使用 `--progress-format jsonl`,读取 stderr 里的结构化事件,再把任务创建、状态切换、关键百分比和最终结果转述给你。
|
|
154
179
|
|
|
155
180
|
## 更多帮助
|
|
156
181
|
|
package/deyo.mjs
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { createRequire } from 'node:module'
|
|
4
5
|
import { dirname, extname, resolve } from 'node:path'
|
|
5
6
|
import { homedir } from 'node:os'
|
|
6
7
|
import process from 'node:process'
|
|
7
8
|
|
|
9
|
+
const CLI_VERSION = createRequire(import.meta.url)('./package.json').version
|
|
10
|
+
|
|
8
11
|
const DEFAULT_BASE_URL = 'https://deyo.miaobi.fun'
|
|
9
12
|
const DEFAULT_LANGUAGE = 'zh'
|
|
10
13
|
const POLL_INTERVAL_MS = 1500
|
|
14
|
+
const PROGRESS_FORMATS = new Set(['auto', 'text', 'jsonl'])
|
|
11
15
|
const RESULT_FORMATS = new Set(['text', 'srt', 'vtt', 'json', 'verbose_json'])
|
|
12
16
|
const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled'])
|
|
13
17
|
|
|
18
|
+
class ReportedCliError extends Error {}
|
|
19
|
+
|
|
14
20
|
function safeJsonParse(value) {
|
|
15
21
|
try {
|
|
16
22
|
return JSON.parse(value)
|
|
@@ -35,8 +41,10 @@ function printUsage() {
|
|
|
35
41
|
--source <name> 可选,显式指定来源
|
|
36
42
|
--language <value> 可选,转写语言,默认 ${DEFAULT_LANGUAGE}
|
|
37
43
|
--format <value> 可选,text | srt | vtt | json | verbose_json
|
|
44
|
+
--progress-format <v> 可选,auto | text | jsonl,默认 auto
|
|
38
45
|
--base-url <url> 可选,默认 ${DEFAULT_BASE_URL}
|
|
39
46
|
-h, --help 显示帮助
|
|
47
|
+
-v, --version 显示 CLI 版本
|
|
40
48
|
|
|
41
49
|
auth 子命令:
|
|
42
50
|
deyo auth login --api-key <key> [--base-url <url>]
|
|
@@ -70,6 +78,7 @@ function parseTranscriptionArgs(argv) {
|
|
|
70
78
|
source: '',
|
|
71
79
|
language: '',
|
|
72
80
|
format: '',
|
|
81
|
+
progressFormat: 'auto',
|
|
73
82
|
baseUrl: '',
|
|
74
83
|
url: '',
|
|
75
84
|
}
|
|
@@ -112,6 +121,12 @@ function parseTranscriptionArgs(argv) {
|
|
|
112
121
|
continue
|
|
113
122
|
}
|
|
114
123
|
|
|
124
|
+
if (current === '--progress-format') {
|
|
125
|
+
args.progressFormat = argv[index + 1] || ''
|
|
126
|
+
index += 1
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
115
130
|
if (current === '--base-url') {
|
|
116
131
|
args.baseUrl = argv[index + 1] || ''
|
|
117
132
|
index += 1
|
|
@@ -452,6 +467,20 @@ function createOutputState() {
|
|
|
452
467
|
}
|
|
453
468
|
}
|
|
454
469
|
|
|
470
|
+
function resolveProgressFormat(progressFormat) {
|
|
471
|
+
const normalized = String(progressFormat || 'auto').trim() || 'auto'
|
|
472
|
+
|
|
473
|
+
if (!PROGRESS_FORMATS.has(normalized)) {
|
|
474
|
+
fail(`--progress-format 不支持:${normalized}`)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (normalized === 'auto') {
|
|
478
|
+
return process.stderr.isTTY ? 'interactive' : 'text'
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return normalized
|
|
482
|
+
}
|
|
483
|
+
|
|
455
484
|
function clearInline(outputState) {
|
|
456
485
|
if (!outputState.inlineActive) {
|
|
457
486
|
return
|
|
@@ -480,6 +509,26 @@ function finishInline(outputState) {
|
|
|
480
509
|
outputState.inlineActive = false
|
|
481
510
|
}
|
|
482
511
|
|
|
512
|
+
function buildStatusChangedMessage(status) {
|
|
513
|
+
return `状态更新:${formatStatus(status)}`
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function buildTerminalMessage(status, errorMessage) {
|
|
517
|
+
if (status === 'completed') {
|
|
518
|
+
return '任务已完成'
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (status === 'failed') {
|
|
522
|
+
return errorMessage ? `任务失败:${errorMessage}` : '任务失败'
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (status === 'cancelled') {
|
|
526
|
+
return errorMessage ? `任务已取消:${errorMessage}` : '任务已取消'
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return errorMessage ? `任务结束:${errorMessage}` : `任务结束:${formatStatus(status)}`
|
|
530
|
+
}
|
|
531
|
+
|
|
483
532
|
function normalizeProgressValue(progress, finishedChunks, totalChunks) {
|
|
484
533
|
if (Number.isFinite(progress) && progress >= 0) {
|
|
485
534
|
return Math.max(0, Math.min(1, progress))
|
|
@@ -517,6 +566,142 @@ function buildTranscribingProgressMessage(input = {}) {
|
|
|
517
566
|
return `转写中 ${percent}%${chunksPart}`
|
|
518
567
|
}
|
|
519
568
|
|
|
569
|
+
function createProgressReporter(progressFormat) {
|
|
570
|
+
const resolvedMode = resolveProgressFormat(progressFormat)
|
|
571
|
+
const outputState = createOutputState()
|
|
572
|
+
let lastTextProgressMessage = ''
|
|
573
|
+
let lastTerminalSignature = ''
|
|
574
|
+
|
|
575
|
+
function writeTextMessage(message) {
|
|
576
|
+
writeStatusLine(outputState, message)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function writeJsonEvent(payload) {
|
|
580
|
+
clearInline(outputState)
|
|
581
|
+
process.stderr.write(`${JSON.stringify(payload)}\n`)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function emitTextOrJson(message, payload) {
|
|
585
|
+
if (resolvedMode === 'jsonl') {
|
|
586
|
+
writeJsonEvent({
|
|
587
|
+
...payload,
|
|
588
|
+
message,
|
|
589
|
+
})
|
|
590
|
+
return
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
writeTextMessage(message)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
mode: resolvedMode,
|
|
598
|
+
reportTaskCreated(task) {
|
|
599
|
+
if (resolvedMode === 'jsonl') {
|
|
600
|
+
writeJsonEvent({
|
|
601
|
+
event: 'task.created',
|
|
602
|
+
message: `任务已创建:${task.taskId}`,
|
|
603
|
+
taskId: task.taskId,
|
|
604
|
+
title: task.title,
|
|
605
|
+
sourceType: task.sourceType,
|
|
606
|
+
status: task.status,
|
|
607
|
+
statusLabel: formatStatus(task.status),
|
|
608
|
+
consumedMinutes: task.consumedMinutes,
|
|
609
|
+
remainingMinutes: task.remainingMinutes,
|
|
610
|
+
mode: task.mode,
|
|
611
|
+
resultReady: task.resultReady,
|
|
612
|
+
})
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
writeTextMessage(`任务已创建:${task.taskId}`)
|
|
617
|
+
writeTextMessage(`标题:${task.title}`)
|
|
618
|
+
writeTextMessage(`来源:${task.sourceType}`)
|
|
619
|
+
writeTextMessage(`状态:${formatStatus(task.status)}`)
|
|
620
|
+
writeTextMessage(`本次消耗分钟:${task.consumedMinutes}`)
|
|
621
|
+
writeTextMessage(`剩余分钟:${task.remainingMinutes}`)
|
|
622
|
+
},
|
|
623
|
+
reportStatusChanged(status) {
|
|
624
|
+
emitTextOrJson(buildStatusChangedMessage(status), {
|
|
625
|
+
event: 'task.status_changed',
|
|
626
|
+
status,
|
|
627
|
+
statusLabel: formatStatus(status),
|
|
628
|
+
})
|
|
629
|
+
},
|
|
630
|
+
reportProgress(input = {}) {
|
|
631
|
+
const finishedChunks = Number(input.finishedChunks)
|
|
632
|
+
const totalChunks = Number(input.totalChunks)
|
|
633
|
+
const normalizedProgress = normalizeProgressValue(
|
|
634
|
+
Number(input.progress),
|
|
635
|
+
finishedChunks,
|
|
636
|
+
totalChunks,
|
|
637
|
+
)
|
|
638
|
+
const message = buildTranscribingProgressMessage(input)
|
|
639
|
+
|
|
640
|
+
lastTerminalSignature = ''
|
|
641
|
+
|
|
642
|
+
if (resolvedMode === 'interactive') {
|
|
643
|
+
writeInline(outputState, message)
|
|
644
|
+
return
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (resolvedMode === 'jsonl') {
|
|
648
|
+
writeJsonEvent({
|
|
649
|
+
event: 'task.progress',
|
|
650
|
+
message,
|
|
651
|
+
status: 'transcribing',
|
|
652
|
+
progress: normalizedProgress,
|
|
653
|
+
percent: normalizedProgress === null ? null : Math.round(normalizedProgress * 100),
|
|
654
|
+
finishedChunks: Number.isFinite(finishedChunks) ? Math.max(0, finishedChunks) : null,
|
|
655
|
+
totalChunks: Number.isFinite(totalChunks) && totalChunks > 0 ? totalChunks : null,
|
|
656
|
+
})
|
|
657
|
+
return
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (message === lastTextProgressMessage) {
|
|
661
|
+
return
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
lastTextProgressMessage = message
|
|
665
|
+
writeTextMessage(message)
|
|
666
|
+
},
|
|
667
|
+
reportTerminal(status, errorMessage) {
|
|
668
|
+
if (!status) {
|
|
669
|
+
return
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
finishInline(outputState)
|
|
673
|
+
const signature = `${status}:${errorMessage || ''}`
|
|
674
|
+
|
|
675
|
+
if (lastTerminalSignature === signature) {
|
|
676
|
+
return
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
lastTerminalSignature = signature
|
|
680
|
+
emitTextOrJson(buildTerminalMessage(status, errorMessage), {
|
|
681
|
+
event: `task.${status}`,
|
|
682
|
+
status,
|
|
683
|
+
statusLabel: formatStatus(status),
|
|
684
|
+
error: errorMessage || null,
|
|
685
|
+
})
|
|
686
|
+
},
|
|
687
|
+
reportResultWritten(outputPath, format) {
|
|
688
|
+
emitTextOrJson(`结果已写入:${outputPath}`, {
|
|
689
|
+
event: 'task.result_written',
|
|
690
|
+
outputPath,
|
|
691
|
+
format,
|
|
692
|
+
})
|
|
693
|
+
},
|
|
694
|
+
reportNotice(message) {
|
|
695
|
+
emitTextOrJson(message, {
|
|
696
|
+
event: 'task.notice',
|
|
697
|
+
})
|
|
698
|
+
},
|
|
699
|
+
close() {
|
|
700
|
+
finishInline(outputState)
|
|
701
|
+
},
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
520
705
|
function parseSseBlock(block) {
|
|
521
706
|
const lines = block.split('\n')
|
|
522
707
|
let event = 'message'
|
|
@@ -543,7 +728,7 @@ function parseSseBlock(block) {
|
|
|
543
728
|
}
|
|
544
729
|
}
|
|
545
730
|
|
|
546
|
-
async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask,
|
|
731
|
+
async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, reporter) {
|
|
547
732
|
const response = await fetch(`${baseUrl}/api/cli/transcriptions/${encodeURIComponent(taskId)}/events`, {
|
|
548
733
|
method: 'GET',
|
|
549
734
|
headers: {
|
|
@@ -573,7 +758,13 @@ async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, outputSta
|
|
|
573
758
|
...task,
|
|
574
759
|
status: nextStatus,
|
|
575
760
|
}
|
|
576
|
-
|
|
761
|
+
|
|
762
|
+
if (TERMINAL_STATUSES.has(nextStatus)) {
|
|
763
|
+
reporter.reportTerminal(nextStatus, task.error || null)
|
|
764
|
+
return
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
reporter.reportStatusChanged(nextStatus)
|
|
577
768
|
}
|
|
578
769
|
|
|
579
770
|
function applyTerminal(nextStatus, errorMessage) {
|
|
@@ -583,7 +774,7 @@ async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, outputSta
|
|
|
583
774
|
status: nextStatus,
|
|
584
775
|
error: errorMessage || task.error || null,
|
|
585
776
|
}
|
|
586
|
-
|
|
777
|
+
reporter.reportTerminal(nextStatus, task.error || null)
|
|
587
778
|
}
|
|
588
779
|
|
|
589
780
|
function renderProgress(data) {
|
|
@@ -592,7 +783,7 @@ async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, outputSta
|
|
|
592
783
|
...task,
|
|
593
784
|
status: 'transcribing',
|
|
594
785
|
}
|
|
595
|
-
|
|
786
|
+
reporter.reportProgress(data)
|
|
596
787
|
}
|
|
597
788
|
|
|
598
789
|
while (true) {
|
|
@@ -621,6 +812,8 @@ async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, outputSta
|
|
|
621
812
|
if (event === 'job.snapshot') {
|
|
622
813
|
if (data?.status === 'transcribing') {
|
|
623
814
|
renderProgress(data)
|
|
815
|
+
} else if (TERMINAL_STATUSES.has(data?.status)) {
|
|
816
|
+
applyTerminal(data.status, data?.error || data?.message || null)
|
|
624
817
|
} else {
|
|
625
818
|
applyStatus(data?.status)
|
|
626
819
|
}
|
|
@@ -654,7 +847,7 @@ async function consumeTaskEvents(baseUrl, apiKey, taskId, initialTask, outputSta
|
|
|
654
847
|
}
|
|
655
848
|
}
|
|
656
849
|
|
|
657
|
-
|
|
850
|
+
reporter.close()
|
|
658
851
|
return task
|
|
659
852
|
}
|
|
660
853
|
|
|
@@ -739,85 +932,95 @@ async function handleAuthCommand(argv) {
|
|
|
739
932
|
|
|
740
933
|
async function handleTranscription(argv) {
|
|
741
934
|
const args = parseTranscriptionArgs(argv)
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
printUsage()
|
|
747
|
-
fail('缺少 API key。请传 --api-key、设置 DEYO_API_KEY,或先执行 deyo auth login')
|
|
748
|
-
}
|
|
935
|
+
const reporter = createProgressReporter(args.progressFormat)
|
|
936
|
+
try {
|
|
937
|
+
const storedConfig = await readStoredConfig()
|
|
938
|
+
const resolvedApiKey = resolveApiKey(args.apiKey, storedConfig)
|
|
749
939
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
940
|
+
if (!resolvedApiKey) {
|
|
941
|
+
printUsage()
|
|
942
|
+
fail('缺少 API key。请传 --api-key、设置 DEYO_API_KEY,或先执行 deyo auth login')
|
|
943
|
+
}
|
|
754
944
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
language: args.language || DEFAULT_LANGUAGE,
|
|
760
|
-
}
|
|
945
|
+
if (!args.url) {
|
|
946
|
+
printUsage()
|
|
947
|
+
fail('缺少内容链接')
|
|
948
|
+
}
|
|
761
949
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
950
|
+
const { baseUrl } = resolveBaseUrl(args.baseUrl, storedConfig)
|
|
951
|
+
const format = inferFormat(args.output, args.format)
|
|
952
|
+
const createPayload = {
|
|
953
|
+
url: args.url,
|
|
954
|
+
language: args.language || DEFAULT_LANGUAGE,
|
|
955
|
+
}
|
|
765
956
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
})
|
|
770
|
-
const outputState = createOutputState()
|
|
957
|
+
if (args.source) {
|
|
958
|
+
createPayload.source = args.source
|
|
959
|
+
}
|
|
771
960
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
961
|
+
const created = await requestJson(baseUrl, resolvedApiKey.apiKey, '/api/cli/transcriptions', {
|
|
962
|
+
method: 'POST',
|
|
963
|
+
body: createPayload,
|
|
964
|
+
})
|
|
965
|
+
reporter.reportTaskCreated(created)
|
|
966
|
+
|
|
967
|
+
let task = created
|
|
968
|
+
let lastStatus = created.status
|
|
969
|
+
|
|
970
|
+
if (task.mode === 'transcription' && !TERMINAL_STATUSES.has(task.status)) {
|
|
971
|
+
try {
|
|
972
|
+
task = await consumeTaskEvents(baseUrl, resolvedApiKey.apiKey, task.taskId, task, reporter)
|
|
973
|
+
lastStatus = task.status
|
|
974
|
+
} catch (error) {
|
|
975
|
+
reporter.reportNotice(`事件流中断,回退到轮询状态:${error instanceof Error ? error.message : String(error)}`)
|
|
976
|
+
}
|
|
977
|
+
}
|
|
778
978
|
|
|
779
|
-
|
|
780
|
-
|
|
979
|
+
while (!TERMINAL_STATUSES.has(task.status)) {
|
|
980
|
+
await sleep(POLL_INTERVAL_MS)
|
|
981
|
+
task = await requestJson(baseUrl, resolvedApiKey.apiKey, `/api/cli/transcriptions/${encodeURIComponent(task.taskId)}`)
|
|
781
982
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
983
|
+
if (task.status !== lastStatus) {
|
|
984
|
+
if (TERMINAL_STATUSES.has(task.status)) {
|
|
985
|
+
reporter.reportTerminal(task.status, task.error || null)
|
|
986
|
+
} else {
|
|
987
|
+
reporter.reportStatusChanged(task.status)
|
|
988
|
+
}
|
|
989
|
+
lastStatus = task.status
|
|
990
|
+
}
|
|
789
991
|
}
|
|
790
|
-
}
|
|
791
992
|
|
|
792
|
-
|
|
793
|
-
await sleep(POLL_INTERVAL_MS)
|
|
794
|
-
task = await requestJson(baseUrl, resolvedApiKey.apiKey, `/api/cli/transcriptions/${encodeURIComponent(task.taskId)}`)
|
|
993
|
+
reporter.reportTerminal(task.status, task.error || null)
|
|
795
994
|
|
|
796
|
-
if (task.status !==
|
|
797
|
-
|
|
798
|
-
process.stderr.write(`状态更新:${formatStatus(task.status)}\n`)
|
|
799
|
-
lastStatus = task.status
|
|
995
|
+
if (task.status !== 'completed') {
|
|
996
|
+
throw new ReportedCliError(task.error || `任务未成功完成:${formatStatus(task.status)}`)
|
|
800
997
|
}
|
|
801
|
-
}
|
|
802
998
|
|
|
803
|
-
|
|
999
|
+
const result = await requestResult(baseUrl, resolvedApiKey.apiKey, task.taskId, format)
|
|
804
1000
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1001
|
+
if (args.output) {
|
|
1002
|
+
const outputPath = await writeOutput(args.output, result)
|
|
1003
|
+
reporter.reportResultWritten(outputPath, format)
|
|
1004
|
+
return
|
|
1005
|
+
}
|
|
808
1006
|
|
|
809
|
-
|
|
1007
|
+
reporter.close()
|
|
1008
|
+
process.stdout.write(result)
|
|
810
1009
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1010
|
+
if (!result.endsWith('\n')) {
|
|
1011
|
+
process.stdout.write('\n')
|
|
1012
|
+
}
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
if (error instanceof ReportedCliError) {
|
|
1015
|
+
throw error
|
|
1016
|
+
}
|
|
816
1017
|
|
|
817
|
-
|
|
1018
|
+
if (reporter.mode === 'jsonl') {
|
|
1019
|
+
reporter.reportNotice(`执行失败:${error instanceof Error ? error.message : String(error)}`)
|
|
1020
|
+
throw new ReportedCliError('执行失败')
|
|
1021
|
+
}
|
|
818
1022
|
|
|
819
|
-
|
|
820
|
-
process.stdout.write('\n')
|
|
1023
|
+
throw error
|
|
821
1024
|
}
|
|
822
1025
|
}
|
|
823
1026
|
|
|
@@ -825,6 +1028,11 @@ async function main() {
|
|
|
825
1028
|
const argv = process.argv.slice(2)
|
|
826
1029
|
const primaryCommand = argv[0]
|
|
827
1030
|
|
|
1031
|
+
if (primaryCommand === '-v' || primaryCommand === '--version') {
|
|
1032
|
+
process.stdout.write(`${CLI_VERSION}\n`)
|
|
1033
|
+
return
|
|
1034
|
+
}
|
|
1035
|
+
|
|
828
1036
|
if (!primaryCommand || primaryCommand === '-h' || primaryCommand === '--help') {
|
|
829
1037
|
printUsage()
|
|
830
1038
|
return
|
|
@@ -839,6 +1047,10 @@ async function main() {
|
|
|
839
1047
|
}
|
|
840
1048
|
|
|
841
1049
|
main().catch((error) => {
|
|
1050
|
+
if (error instanceof ReportedCliError) {
|
|
1051
|
+
process.exit(1)
|
|
1052
|
+
}
|
|
1053
|
+
|
|
842
1054
|
const message = error instanceof Error ? error.message : String(error)
|
|
843
1055
|
fail(`执行失败:${message}`)
|
|
844
1056
|
})
|