gaoding-cli 1.0.0-alpha.8 → 1.0.0-alpha.9
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/CHANGELOG.md +18 -0
- package/README.md +47 -64
- package/dist/bin/gd-cli.js +2 -2
- package/dist/src/api/app-runner.d.ts +1 -4
- package/dist/src/api/app-runner.js +10 -42
- package/dist/src/api/dam-client.d.ts +0 -7
- package/dist/src/api/dam-client.js +0 -17
- package/dist/src/commands/auth.js +5 -41
- package/dist/src/commands/creative.js +37 -45
- package/dist/src/commands/dam.js +248 -110
- package/dist/src/commands/org.js +17 -39
- package/dist/src/commands/skills.js +24 -114
- package/dist/src/commands/update.js +10 -85
- package/dist/src/commands/usage.js +4 -17
- package/dist/src/commands/workflows.d.ts +1 -1
- package/dist/src/commands/workflows.js +283 -199
- package/dist/src/core/output/envelope.d.ts +1 -0
- package/dist/src/core/output/envelope.js +76 -6
- package/dist/src/creative/contract.d.ts +28 -80
- package/dist/src/creative/contract.js +23 -44
- package/dist/src/creative/protocol.js +6 -6
- package/dist/src/io.d.ts +0 -2
- package/dist/src/io.js +0 -8
- package/dist/src/middleware/error-handler.d.ts +7 -1
- package/dist/src/middleware/error-handler.js +59 -54
- package/dist/src/program.d.ts +1 -0
- package/dist/src/program.js +138 -41
- package/dist/src/registry/agent-contracts.d.ts +14 -26
- package/dist/src/registry/agent-contracts.js +214 -64
- package/dist/src/utils/capability-presenter.js +6 -29
- package/dist/src/utils/config.d.ts +2 -2
- package/dist/src/utils/config.js +1 -1
- package/dist/src/utils/sensitive-path.js +1 -4
- package/package.json +2 -1
- package/skills/gd-cli/SKILL.md +6 -6
- package/skills/gd-cli/references/auth.md +3 -10
- package/skills/gd-cli/references/creative.md +11 -11
- package/skills/gd-cli/references/entitlement.md +2 -2
- package/skills/gd-cli/references/errors.md +4 -4
- package/skills/gd-cli/references/org.md +3 -3
- package/skills/gd-cli/references/sop.md +5 -5
- package/skills/gd-cli/references/update.md +4 -3
- package/skills/gd-cli/references/workflows.md +6 -5
- package/dist/src/utils/list-output-format.d.ts +0 -10
- package/dist/src/utils/list-output-format.js +0 -34
|
@@ -3,7 +3,6 @@ export const CREATIVE_SCHEMA_VERSION = "creative/v1";
|
|
|
3
3
|
export const CREATIVE_INPUT_SCHEMA = {
|
|
4
4
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
5
5
|
$id: "https://www.gaoding.com/cli/schemas/creative-input-v1.json",
|
|
6
|
-
title: "Creative Input",
|
|
7
6
|
description: "发起或继续一次稿定 Creative 创作所需的完整输入。",
|
|
8
7
|
type: "object",
|
|
9
8
|
additionalProperties: false,
|
|
@@ -19,7 +18,6 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
19
18
|
description: "本次创作使用的输入素材;媒体不能脱离 prompt 或 answers 单独提交。",
|
|
20
19
|
items: {
|
|
21
20
|
type: "object",
|
|
22
|
-
description: "一份带有明确用途的输入素材。",
|
|
23
21
|
additionalProperties: false,
|
|
24
22
|
required: ["source", "role"],
|
|
25
23
|
properties: {
|
|
@@ -63,7 +61,7 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
63
61
|
minLength: 1,
|
|
64
62
|
description: "继续已有创作或回答追问时使用的会话标识;首次请求和独立的新创作不传。",
|
|
65
63
|
},
|
|
66
|
-
|
|
64
|
+
question_message_id: {
|
|
67
65
|
type: "string",
|
|
68
66
|
minLength: 1,
|
|
69
67
|
description: "正在回答或跳过的追问消息标识,原样使用上一次 requires_input 输出中的值。",
|
|
@@ -74,7 +72,6 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
74
72
|
description: "对上一次 requires_input 输出中一个或多个问题的结构化回答。",
|
|
75
73
|
items: {
|
|
76
74
|
type: "object",
|
|
77
|
-
description: "对一条 GD Agent 追问的回答。",
|
|
78
75
|
additionalProperties: false,
|
|
79
76
|
required: ["question", "values"],
|
|
80
77
|
properties: {
|
|
@@ -90,7 +87,6 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
90
87
|
items: {
|
|
91
88
|
type: "string",
|
|
92
89
|
minLength: 1,
|
|
93
|
-
description: "一项非空的选择值或自定义回答。",
|
|
94
90
|
},
|
|
95
91
|
},
|
|
96
92
|
},
|
|
@@ -98,22 +94,15 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
98
94
|
},
|
|
99
95
|
},
|
|
100
96
|
anyOf: [
|
|
101
|
-
{
|
|
102
|
-
|
|
103
|
-
required: ["prompt"],
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
description: "回答追问时 answers 可以替代 prompt,但必须同时携带两个连续对话标识。",
|
|
107
|
-
required: ["answers", "conversation_id", "last_tool_message_id"],
|
|
108
|
-
},
|
|
97
|
+
{ required: ["prompt"] },
|
|
98
|
+
{ required: ["answers"] },
|
|
109
99
|
],
|
|
110
100
|
dependentRequired: {
|
|
111
|
-
answers: ["conversation_id", "
|
|
112
|
-
|
|
101
|
+
answers: ["conversation_id", "question_message_id"],
|
|
102
|
+
question_message_id: ["conversation_id"],
|
|
113
103
|
},
|
|
114
104
|
allOf: [
|
|
115
105
|
{
|
|
116
|
-
description: "first_frame 最多出现一次。",
|
|
117
106
|
properties: {
|
|
118
107
|
media: {
|
|
119
108
|
description: "用于检查首帧数量的素材列表。",
|
|
@@ -133,7 +122,6 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
133
122
|
},
|
|
134
123
|
},
|
|
135
124
|
{
|
|
136
|
-
description: "last_frame 最多出现一次。",
|
|
137
125
|
properties: {
|
|
138
126
|
media: {
|
|
139
127
|
description: "用于检查尾帧数量的素材列表。",
|
|
@@ -157,7 +145,6 @@ export const CREATIVE_INPUT_SCHEMA = {
|
|
|
157
145
|
export const CREATIVE_OUTPUT_SCHEMA = {
|
|
158
146
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
159
147
|
$id: "https://www.gaoding.com/cli/schemas/creative-output-v1.json",
|
|
160
|
-
title: "Creative Output",
|
|
161
148
|
description: "creative run 的统一最终输出;中间事件和内部调用过程不属于本 Schema。",
|
|
162
149
|
type: "object",
|
|
163
150
|
additionalProperties: false,
|
|
@@ -170,11 +157,9 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
170
157
|
},
|
|
171
158
|
data: {
|
|
172
159
|
description: "status 为 completed 时存在的 Creative 最终结果;failed 时为 null。",
|
|
173
|
-
oneOf: [{ $ref: "#/$defs/result" }, { type: "null" }],
|
|
174
160
|
},
|
|
175
161
|
error: {
|
|
176
162
|
description: "status 为 failed 时存在的结构化错误;completed 时为 null。",
|
|
177
|
-
oneOf: [{ $ref: "#/$defs/error" }, { type: "null" }],
|
|
178
163
|
},
|
|
179
164
|
meta: {
|
|
180
165
|
type: "object",
|
|
@@ -195,7 +180,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
195
180
|
},
|
|
196
181
|
oneOf: [
|
|
197
182
|
{
|
|
198
|
-
description: "成功完成或需要使用者继续回答的结果。",
|
|
199
183
|
properties: {
|
|
200
184
|
status: { const: "completed", description: "成功状态。" },
|
|
201
185
|
data: { $ref: "#/$defs/result", description: "Creative 结果。" },
|
|
@@ -203,7 +187,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
203
187
|
},
|
|
204
188
|
},
|
|
205
189
|
{
|
|
206
|
-
description: "校验、认证、业务或网络失败的结果。",
|
|
207
190
|
properties: {
|
|
208
191
|
status: { const: "failed", description: "失败状态。" },
|
|
209
192
|
data: { type: "null", description: "失败时没有 Creative 结果。" },
|
|
@@ -214,7 +197,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
214
197
|
$defs: {
|
|
215
198
|
result: {
|
|
216
199
|
type: "object",
|
|
217
|
-
description: "GD Agent 本轮的最终结果。",
|
|
218
200
|
additionalProperties: false,
|
|
219
201
|
required: ["conversation_id", "finish_reason", "message", "outputs"],
|
|
220
202
|
properties: {
|
|
@@ -235,9 +217,9 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
235
217
|
outputs: {
|
|
236
218
|
type: "array",
|
|
237
219
|
description: "本轮最终产出的文本、结构化数据或媒体。",
|
|
238
|
-
items: { $ref: "#/$defs/output"
|
|
220
|
+
items: { $ref: "#/$defs/output" },
|
|
239
221
|
},
|
|
240
|
-
|
|
222
|
+
question_message_id: {
|
|
241
223
|
type: "string",
|
|
242
224
|
minLength: 1,
|
|
243
225
|
description: "requires_input 时必填,并在下一次请求中原样回传。",
|
|
@@ -257,11 +239,11 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
257
239
|
},
|
|
258
240
|
},
|
|
259
241
|
},
|
|
260
|
-
then: { required: ["
|
|
242
|
+
then: { required: ["question_message_id", "questions"] },
|
|
261
243
|
else: {
|
|
262
244
|
not: {
|
|
263
245
|
anyOf: [
|
|
264
|
-
{ required: ["
|
|
246
|
+
{ required: ["question_message_id"] },
|
|
265
247
|
{ required: ["questions"] },
|
|
266
248
|
],
|
|
267
249
|
},
|
|
@@ -271,7 +253,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
271
253
|
},
|
|
272
254
|
question_group: {
|
|
273
255
|
type: "object",
|
|
274
|
-
description: "GD Agent 在本轮提出的一组问题。",
|
|
275
256
|
additionalProperties: false,
|
|
276
257
|
required: ["title", "questions"],
|
|
277
258
|
properties: {
|
|
@@ -285,7 +266,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
285
266
|
description: "需要使用者回答的一个或多个问题。",
|
|
286
267
|
items: {
|
|
287
268
|
type: "object",
|
|
288
|
-
description: "一条 GD Agent 追问。",
|
|
289
269
|
additionalProperties: false,
|
|
290
270
|
required: ["question", "options", "allow_multiple"],
|
|
291
271
|
properties: {
|
|
@@ -299,7 +279,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
299
279
|
description: "推荐选项,可以为空;label 不是严格枚举。",
|
|
300
280
|
items: {
|
|
301
281
|
type: "object",
|
|
302
|
-
description: "一项推荐回答。",
|
|
303
282
|
additionalProperties: false,
|
|
304
283
|
required: ["label"],
|
|
305
284
|
properties: {
|
|
@@ -321,11 +300,9 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
321
300
|
},
|
|
322
301
|
},
|
|
323
302
|
output: {
|
|
324
|
-
description: "文本、JSON、图片、视频、音频或文件结果。",
|
|
325
303
|
oneOf: [
|
|
326
304
|
{
|
|
327
305
|
type: "object",
|
|
328
|
-
description: "文本结果。",
|
|
329
306
|
additionalProperties: false,
|
|
330
307
|
required: ["type", "text"],
|
|
331
308
|
properties: {
|
|
@@ -335,7 +312,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
335
312
|
},
|
|
336
313
|
{
|
|
337
314
|
type: "object",
|
|
338
|
-
description: "可机器解析的结构化结果。",
|
|
339
315
|
additionalProperties: false,
|
|
340
316
|
required: ["type", "value"],
|
|
341
317
|
properties: {
|
|
@@ -345,7 +321,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
345
321
|
},
|
|
346
322
|
{
|
|
347
323
|
type: "object",
|
|
348
|
-
description: "最终媒体或文件结果。",
|
|
349
324
|
additionalProperties: false,
|
|
350
325
|
required: ["type", "url", "mime_type"],
|
|
351
326
|
properties: {
|
|
@@ -396,7 +371,6 @@ export const CREATIVE_OUTPUT_SCHEMA = {
|
|
|
396
371
|
},
|
|
397
372
|
error: {
|
|
398
373
|
type: "object",
|
|
399
|
-
description: "Creative 失败时的稳定错误信息。",
|
|
400
374
|
additionalProperties: false,
|
|
401
375
|
required: ["code", "type", "message"],
|
|
402
376
|
properties: {
|
|
@@ -440,6 +414,7 @@ export const CREATIVE_EXAMPLES = {
|
|
|
440
414
|
{
|
|
441
415
|
name: "text_creation",
|
|
442
416
|
description: "纯文本创作",
|
|
417
|
+
command: 'gd-cli creative run --prompt "为一款轻量缓震跑鞋写三条电商主图标题,语言简洁有力。" --json',
|
|
443
418
|
input: {
|
|
444
419
|
prompt: "为一款轻量缓震跑鞋写三条电商主图标题,语言简洁有力。",
|
|
445
420
|
},
|
|
@@ -447,6 +422,7 @@ export const CREATIVE_EXAMPLES = {
|
|
|
447
422
|
{
|
|
448
423
|
name: "image_with_reference",
|
|
449
424
|
description: "使用参考图创作图片",
|
|
425
|
+
command: "gd-cli creative run --input creative-request.json --json",
|
|
450
426
|
input: {
|
|
451
427
|
prompt: "以产品为主体生成一张户外晨跑场景广告图,保持鞋款外观和配色。",
|
|
452
428
|
media: [
|
|
@@ -464,6 +440,7 @@ export const CREATIVE_EXAMPLES = {
|
|
|
464
440
|
{
|
|
465
441
|
name: "video_with_first_and_last_frames",
|
|
466
442
|
description: "使用首尾帧创作视频",
|
|
443
|
+
command: "gd-cli creative run --input creative-request.json --json",
|
|
467
444
|
input: {
|
|
468
445
|
prompt: "生成从清晨城市街道过渡到终点冲线的运动广告视频,镜头运动自然。",
|
|
469
446
|
media: [
|
|
@@ -486,6 +463,7 @@ export const CREATIVE_EXAMPLES = {
|
|
|
486
463
|
{
|
|
487
464
|
name: "continue_creation",
|
|
488
465
|
description: "继续修改已有创作",
|
|
466
|
+
command: "gd-cli creative run --input creative-request.json --json",
|
|
489
467
|
input: {
|
|
490
468
|
conversation_id: "conversation_123",
|
|
491
469
|
prompt: "保留主体和构图,把整体风格调整得更克制,并降低背景饱和度。",
|
|
@@ -494,9 +472,10 @@ export const CREATIVE_EXAMPLES = {
|
|
|
494
472
|
{
|
|
495
473
|
name: "answer_questions",
|
|
496
474
|
description: "回答 GD Agent 的结构化追问",
|
|
475
|
+
command: "gd-cli creative run --input creative-request.json --json",
|
|
497
476
|
input: {
|
|
498
477
|
conversation_id: "conversation_123",
|
|
499
|
-
|
|
478
|
+
question_message_id: "message_456",
|
|
500
479
|
answers: [
|
|
501
480
|
{
|
|
502
481
|
question: "希望采用什么视觉风格?",
|
|
@@ -519,7 +498,7 @@ const INPUT_KEYS = new Set([
|
|
|
519
498
|
"media",
|
|
520
499
|
"parameters",
|
|
521
500
|
"conversation_id",
|
|
522
|
-
"
|
|
501
|
+
"question_message_id",
|
|
523
502
|
"answers",
|
|
524
503
|
]);
|
|
525
504
|
const PARAMETER_KEYS = new Set(["ratio", "resolution", "duration_seconds"]);
|
|
@@ -573,14 +552,14 @@ export function validateCreativeInput(value) {
|
|
|
573
552
|
const conversationId = Object.hasOwn(input, "conversation_id")
|
|
574
553
|
? requireNonEmptyString(input.conversation_id, "conversation_id")
|
|
575
554
|
: undefined;
|
|
576
|
-
const
|
|
577
|
-
? requireNonEmptyString(input.
|
|
555
|
+
const questionMessageId = Object.hasOwn(input, "question_message_id")
|
|
556
|
+
? requireNonEmptyString(input.question_message_id, "question_message_id")
|
|
578
557
|
: undefined;
|
|
579
558
|
if (!hasPrompt && !hasAnswers) {
|
|
580
559
|
invalid("prompt", "普通创作必须提供非空 prompt;回答追问时可以改为提供 answers");
|
|
581
560
|
}
|
|
582
|
-
if (
|
|
583
|
-
invalid("
|
|
561
|
+
if (questionMessageId && !conversationId) {
|
|
562
|
+
invalid("question_message_id", "question_message_id 必须与 conversation_id 一起使用");
|
|
584
563
|
}
|
|
585
564
|
if (Object.hasOwn(input, "media"))
|
|
586
565
|
validateMedia(input.media);
|
|
@@ -588,8 +567,8 @@ export function validateCreativeInput(value) {
|
|
|
588
567
|
validateParameters(input.parameters);
|
|
589
568
|
if (hasAnswers) {
|
|
590
569
|
validateAnswers(input.answers);
|
|
591
|
-
if (!conversationId || !
|
|
592
|
-
invalid("answers", "answers 必须同时携带 conversation_id 和
|
|
570
|
+
if (!conversationId || !questionMessageId) {
|
|
571
|
+
invalid("answers", "answers 必须同时携带 conversation_id 和 question_message_id");
|
|
593
572
|
}
|
|
594
573
|
}
|
|
595
574
|
if (prompt === undefined && !hasAnswers) {
|
|
@@ -736,7 +715,7 @@ function invalid(path, message) {
|
|
|
736
715
|
code: "PARAM_INVALID",
|
|
737
716
|
type: "validation",
|
|
738
717
|
message,
|
|
739
|
-
hint: "请运行 gd-cli creative schema --
|
|
718
|
+
hint: "请运行 gd-cli creative schema --json 查看完整输入结构。",
|
|
740
719
|
retryable: false,
|
|
741
720
|
detail: { path },
|
|
742
721
|
});
|
|
@@ -29,9 +29,9 @@ export function buildCreativeAgentRequest(input, media = [], ids) {
|
|
|
29
29
|
? buildAnswerContent(input, promptItems)
|
|
30
30
|
: buildCreationContent(input, promptItems);
|
|
31
31
|
const extra = {};
|
|
32
|
-
if (input.conversation_id && input.
|
|
33
|
-
extra.lastToolMessageId = input.
|
|
34
|
-
extra.last_tool_message_id = input.
|
|
32
|
+
if (input.conversation_id && input.question_message_id) {
|
|
33
|
+
extra.lastToolMessageId = input.question_message_id;
|
|
34
|
+
extra.last_tool_message_id = input.question_message_id;
|
|
35
35
|
}
|
|
36
36
|
if (isAnswer) {
|
|
37
37
|
const questions = input.answers.map(toEditorAnswer);
|
|
@@ -145,9 +145,9 @@ export function normalizeCreativeMessages(messages, fallbackConversationId = "")
|
|
|
145
145
|
}
|
|
146
146
|
if (ask) {
|
|
147
147
|
const questionData = parseAskUserQuestion(ask);
|
|
148
|
-
const
|
|
148
|
+
const questionMessageId = stringValue(ask.message_id ?? ask.messageId) ||
|
|
149
149
|
stringValue(ask.extra?.last_tool_message_id ?? ask.extra?.lastToolMessageId);
|
|
150
|
-
if (!
|
|
150
|
+
if (!questionMessageId) {
|
|
151
151
|
invalidAgentResponse("GD Agent 追问结果缺少追问消息标识");
|
|
152
152
|
}
|
|
153
153
|
if (questionData.questions.length === 0 ||
|
|
@@ -159,7 +159,7 @@ export function normalizeCreativeMessages(messages, fallbackConversationId = "")
|
|
|
159
159
|
finish_reason: "requires_input",
|
|
160
160
|
message: textParts.at(-1) ?? questionData.title,
|
|
161
161
|
outputs: dedupeOutputs(outputs),
|
|
162
|
-
|
|
162
|
+
question_message_id: questionMessageId,
|
|
163
163
|
questions: questionData,
|
|
164
164
|
};
|
|
165
165
|
}
|
package/dist/src/io.d.ts
CHANGED
|
@@ -6,8 +6,6 @@ export declare class IOStreams {
|
|
|
6
6
|
readonly stdoutIsTTY: boolean;
|
|
7
7
|
readonly stderrIsTTY: boolean;
|
|
8
8
|
constructor();
|
|
9
|
-
/** Agent / CI:stdout 非 TTY 时默认 JSON */
|
|
10
|
-
prefersJsonOutput(explicitFormat?: string): boolean;
|
|
11
9
|
/** 授权等人机交互场景 */
|
|
12
10
|
isInteractiveAuth(): boolean;
|
|
13
11
|
}
|
package/dist/src/io.js
CHANGED
|
@@ -10,14 +10,6 @@ export class IOStreams {
|
|
|
10
10
|
this.stdoutIsTTY = !!process.stdout.isTTY;
|
|
11
11
|
this.stderrIsTTY = !!process.stderr.isTTY;
|
|
12
12
|
}
|
|
13
|
-
/** Agent / CI:stdout 非 TTY 时默认 JSON */
|
|
14
|
-
prefersJsonOutput(explicitFormat) {
|
|
15
|
-
if (explicitFormat && explicitFormat !== "json")
|
|
16
|
-
return false;
|
|
17
|
-
if (explicitFormat === "json")
|
|
18
|
-
return true;
|
|
19
|
-
return !this.stdoutIsTTY;
|
|
20
|
-
}
|
|
21
13
|
/** 授权等人机交互场景 */
|
|
22
14
|
isInteractiveAuth() {
|
|
23
15
|
return this.stdoutIsTTY && this.stdinIsTTY;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type CliErrorBody, type ResultEnvelope } from "../core/output/envelope.js";
|
|
2
3
|
type ActionHandler = (...args: any[]) => Promise<void>;
|
|
4
|
+
type JsonFailureProjector = (error: CliErrorBody) => ResultEnvelope<null>;
|
|
3
5
|
export declare function withErrorHandler(handler: ActionHandler): ActionHandler;
|
|
6
|
+
export declare function shouldEmitJsonError(args: unknown[]): boolean;
|
|
7
|
+
export declare function setJsonFailureProjector(command: Command, projector: JsonFailureProjector): void;
|
|
8
|
+
export declare function jsonFailureEnvelopeFor(command: Command | undefined, error: unknown): ResultEnvelope<null>;
|
|
9
|
+
export declare function emitJsonFailure(command: Command | undefined, error: unknown): void;
|
|
4
10
|
export declare function toCliErrorBody(error: unknown): CliErrorBody;
|
|
5
11
|
export {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ApiError } from "../api/hub-client.js";
|
|
2
2
|
import { DirectApiError } from "../api/direct-client.js";
|
|
3
|
-
import { failedEnvelope, emitJson } from "../core/output/envelope.js";
|
|
3
|
+
import { failedEnvelope, emitJson, publicCliErrorBody, } from "../core/output/envelope.js";
|
|
4
4
|
import { CliUsageError } from "../core/output/cli-error.js";
|
|
5
5
|
import { isVerbose, isDebug } from "../utils/config.js";
|
|
6
6
|
import { isTransientNetworkError } from "../utils/transient-network-error.js";
|
|
7
7
|
import { detectEntitlementIssue, entitlementCliError, ENTITLEMENT_PRICING_URL, resolveEntitlementPricingUrl, } from "../utils/entitlement.js";
|
|
8
8
|
import { redactSensitiveText, redactSensitiveValue } from "../utils/redact.js";
|
|
9
|
+
const jsonFailureProjectors = new WeakMap();
|
|
9
10
|
const ERROR_GUIDES = {
|
|
10
11
|
AUTH_MISSING: "请先登录:\n gd-cli auth login",
|
|
11
12
|
AUTH_EXPIRED: "凭证已过期,请重新登录:\n gd-cli auth login",
|
|
@@ -28,7 +29,7 @@ export function withErrorHandler(handler) {
|
|
|
28
29
|
}
|
|
29
30
|
catch (error) {
|
|
30
31
|
if (shouldEmitJsonError(args)) {
|
|
31
|
-
|
|
32
|
+
emitJsonFailure(commandFromArgs(args), error);
|
|
32
33
|
process.exit(1);
|
|
33
34
|
}
|
|
34
35
|
if (error instanceof CliUsageError) {
|
|
@@ -80,22 +81,26 @@ export function withErrorHandler(handler) {
|
|
|
80
81
|
}
|
|
81
82
|
};
|
|
82
83
|
}
|
|
83
|
-
function shouldEmitJsonError(args) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
export function shouldEmitJsonError(args) {
|
|
85
|
+
const command = commandFromArgs(args);
|
|
86
|
+
return command?.opts().json === true;
|
|
87
|
+
}
|
|
88
|
+
export function setJsonFailureProjector(command, projector) {
|
|
89
|
+
jsonFailureProjectors.set(command, projector);
|
|
90
|
+
}
|
|
91
|
+
export function jsonFailureEnvelopeFor(command, error) {
|
|
92
|
+
const body = redactSensitiveValue(toCliErrorBody(error));
|
|
93
|
+
const projector = command ? jsonFailureProjectors.get(command) : undefined;
|
|
94
|
+
if (projector)
|
|
95
|
+
return projector(body);
|
|
96
|
+
const commandName = command ? publicCommandName(command) : "";
|
|
97
|
+
return failedEnvelope(body, commandName ? { command: commandName } : undefined);
|
|
98
|
+
}
|
|
99
|
+
export function emitJsonFailure(command, error) {
|
|
100
|
+
emitJson(jsonFailureEnvelopeFor(command, error));
|
|
101
|
+
}
|
|
102
|
+
function commandFromArgs(args) {
|
|
103
|
+
return [...args].reverse().find(isCommandLike);
|
|
99
104
|
}
|
|
100
105
|
function isCommandLike(value) {
|
|
101
106
|
return (!!value &&
|
|
@@ -104,6 +109,9 @@ function isCommandLike(value) {
|
|
|
104
109
|
typeof value.opts === "function");
|
|
105
110
|
}
|
|
106
111
|
export function toCliErrorBody(error) {
|
|
112
|
+
return publicCliErrorBody(toCliErrorBodyInternal(error));
|
|
113
|
+
}
|
|
114
|
+
function toCliErrorBodyInternal(error) {
|
|
107
115
|
if (error instanceof CliUsageError) {
|
|
108
116
|
return error.body;
|
|
109
117
|
}
|
|
@@ -127,38 +135,40 @@ export function toCliErrorBody(error) {
|
|
|
127
135
|
});
|
|
128
136
|
if (entitlement)
|
|
129
137
|
return redactSensitiveValue(entitlementCliError(entitlement, error.body));
|
|
130
|
-
if (
|
|
131
|
-
return toolRouteUnavailableCliError(parsed.message
|
|
138
|
+
if (parsed.message && isToolRouteUnavailableMessage(parsed.message)) {
|
|
139
|
+
return toolRouteUnavailableCliError(parsed.message, redactSensitiveValue(error.body));
|
|
132
140
|
}
|
|
133
141
|
return {
|
|
134
142
|
code: parsed.code ?? `HTTP_${error.statusCode}`,
|
|
135
143
|
type: error.statusCode === 401 ? "auth" : "business",
|
|
136
144
|
message: parsed.message
|
|
137
145
|
? normalizeBusinessMessage(parsed.message)
|
|
138
|
-
:
|
|
146
|
+
: publicApiFailureMessage(error.statusCode),
|
|
139
147
|
hint: redactSensitiveText(parsed.hint ?? directApiHint(parsed, error.url) ?? ""),
|
|
140
148
|
retryable: false,
|
|
141
149
|
detail: redactSensitiveValue(error.body),
|
|
142
150
|
};
|
|
143
151
|
}
|
|
144
152
|
if (error instanceof ApiError) {
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
153
|
+
const parsedBody = parseJsonObject(error.body);
|
|
154
|
+
const parsed = parseDirectApiErrorBody(parsedBody ?? error.body);
|
|
155
|
+
const code = parsed.code ?? "API_ERROR";
|
|
156
|
+
const message = parsed.message ?? publicApiFailureMessage(error.statusCode);
|
|
148
157
|
const entitlement = detectEntitlementIssue({
|
|
149
158
|
code,
|
|
150
159
|
message,
|
|
151
|
-
detail:
|
|
160
|
+
detail: parsedBody ?? error.body,
|
|
152
161
|
});
|
|
153
|
-
if (entitlement)
|
|
154
|
-
return redactSensitiveValue(entitlementCliError(entitlement,
|
|
162
|
+
if (entitlement) {
|
|
163
|
+
return redactSensitiveValue(entitlementCliError(entitlement, parsedBody ?? error.body));
|
|
164
|
+
}
|
|
155
165
|
return {
|
|
156
166
|
code,
|
|
157
167
|
type: code.startsWith("AUTH") ? "auth" : "business",
|
|
158
168
|
message: redactSensitiveText(message),
|
|
159
|
-
hint: redactSensitiveText(
|
|
169
|
+
hint: redactSensitiveText(parsed.hint ?? errorGuide(code) ?? ""),
|
|
160
170
|
retryable: false,
|
|
161
|
-
detail: redactSensitiveValue(
|
|
171
|
+
detail: redactSensitiveValue(parsedBody ?? error.body),
|
|
162
172
|
};
|
|
163
173
|
}
|
|
164
174
|
if (error instanceof Error) {
|
|
@@ -209,32 +219,10 @@ function printCliUsageError(body) {
|
|
|
209
219
|
console.error(`\n 错误码: ${body.code}`);
|
|
210
220
|
if (body.reason)
|
|
211
221
|
console.error(` 原因: ${body.reason}`);
|
|
212
|
-
const detail = body.detail && typeof body.detail === "object" && !Array.isArray(body.detail)
|
|
213
|
-
? body.detail
|
|
214
|
-
: undefined;
|
|
215
|
-
const appId = body.app_id ?? stringField(detail, "app_id");
|
|
216
|
-
const toolId = body.tool_id ?? stringField(detail, "tool_id");
|
|
217
|
-
if (appId)
|
|
218
|
-
console.error(` Workflow: ${appId}`);
|
|
219
|
-
if (toolId)
|
|
220
|
-
console.error(` Tool: ${toolId}`);
|
|
221
|
-
const missingTools = Array.isArray(body.missing_tools)
|
|
222
|
-
? body.missing_tools
|
|
223
|
-
.map((item) => typeof item === "string"
|
|
224
|
-
? item
|
|
225
|
-
: stringField(item, "id") ?? "")
|
|
226
|
-
.filter(Boolean)
|
|
227
|
-
: [];
|
|
228
|
-
if (missingTools.length > 0) {
|
|
229
|
-
console.error(` 缺失内部能力: ${missingTools.join(", ")}`);
|
|
230
|
-
}
|
|
231
222
|
if (body.hint)
|
|
232
223
|
console.error(`\n 说明: ${body.hint}`);
|
|
233
224
|
if (body.next_command)
|
|
234
225
|
console.error(`\n 下一步: ${body.next_command}`);
|
|
235
|
-
const listCommand = stringField(detail, "list_command");
|
|
236
|
-
if (listCommand)
|
|
237
|
-
console.error(` 查看状态: ${listCommand}`);
|
|
238
226
|
console.error("");
|
|
239
227
|
}
|
|
240
228
|
function parseDirectApiErrorBody(body) {
|
|
@@ -249,12 +237,29 @@ function parseDirectApiErrorBody(body) {
|
|
|
249
237
|
code: stringField(nestedError, "error_code") ??
|
|
250
238
|
stringField(nestedError, "code") ??
|
|
251
239
|
stringField(value, "error_code") ??
|
|
252
|
-
stringField(value, "code")
|
|
240
|
+
stringField(value, "code") ??
|
|
241
|
+
stringField(value, "error"),
|
|
253
242
|
errorName: stringField(nestedError, "error_name") ??
|
|
254
243
|
stringField(value, "error_name"),
|
|
255
244
|
message: stringField(nestedError, "message") ?? stringField(value, "message"),
|
|
245
|
+
hint: stringField(nestedError, "suggestion") ??
|
|
246
|
+
stringField(nestedError, "hint") ??
|
|
247
|
+
stringField(value, "suggestion") ??
|
|
248
|
+
stringField(value, "hint"),
|
|
256
249
|
};
|
|
257
250
|
}
|
|
251
|
+
function publicApiFailureMessage(statusCode) {
|
|
252
|
+
return `业务请求失败(HTTP ${statusCode})`;
|
|
253
|
+
}
|
|
254
|
+
function publicCommandName(command) {
|
|
255
|
+
const names = [];
|
|
256
|
+
let current = command;
|
|
257
|
+
while (current.parent) {
|
|
258
|
+
names.unshift(current.name());
|
|
259
|
+
current = current.parent;
|
|
260
|
+
}
|
|
261
|
+
return names.join(" ");
|
|
262
|
+
}
|
|
258
263
|
function parseJsonObject(value) {
|
|
259
264
|
try {
|
|
260
265
|
const parsed = JSON.parse(value);
|
|
@@ -366,7 +371,7 @@ function toolRouteUnavailableCliError(message, detail) {
|
|
|
366
371
|
type: "business",
|
|
367
372
|
message: normalizeToolRouteUnavailableMessage(message),
|
|
368
373
|
reason: "tool-route-unavailable",
|
|
369
|
-
hint: "
|
|
374
|
+
hint: "当前能力的生成路由不可用。请停止重试,不要切换模型、环境或团队;请保留本次输入并联系平台确认能力配置。",
|
|
370
375
|
retryable: false,
|
|
371
376
|
detail,
|
|
372
377
|
};
|
|
@@ -385,7 +390,7 @@ function isToolRouteUnavailableMessage(message) {
|
|
|
385
390
|
function normalizeToolRouteUnavailableMessage(message) {
|
|
386
391
|
const normalized = normalizeBusinessMessage(message);
|
|
387
392
|
if (/没有可用的工具|tool-intent 没有返回|tool intent 没有返回/i.test(normalized)) {
|
|
388
|
-
return "
|
|
393
|
+
return "当前能力的生成路由不可用,未发起生成任务。";
|
|
389
394
|
}
|
|
390
395
|
return normalized;
|
|
391
396
|
}
|
package/dist/src/program.d.ts
CHANGED