@lingjingai/lj-awb-cli-pre 0.3.18 → 0.4.5
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 +57 -8
- package/build/_shared.mjs +54 -5
- package/build/prod.mjs +12 -3
- package/package.json +6 -2
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +6 -2
- package/packages/awb-core/src/api.js +22 -0
- package/packages/awb-core/src/commands.js +112 -39
- package/packages/awb-core/src/common.js +8 -0
- package/packages/awb-core/src/output.js +2030 -8
- package/packages/awb-core/src/services.js +1835 -205
- package/packages/awb-core/src/standalone.js +472 -136
- package/packages/awb-core/src/update.js +327 -0
- package/skills/lj-awb/SKILL.md +35 -12
- package/skills/lj-awb/VERSION +1 -1
- package/skills/lj-awb/compat.json +3 -3
- package/skills/lj-awb/modules/artifact/asset.md +1 -1
- package/skills/lj-awb/modules/artifact/clip.md +1 -1
- package/skills/lj-awb/modules/artifact/script.md +1 -1
- package/skills/lj-awb/modules/artifact/video.md +1 -1
- package/skills/lj-awb/modules/asset.md +10 -1
- package/skills/lj-awb/modules/auth.md +9 -1
- package/skills/lj-awb/modules/create-contract.md +5 -2
- package/skills/lj-awb/modules/create.md +4 -2
- package/skills/lj-awb/modules/driver.md +12 -6
- package/skills/lj-awb/modules/image.md +3 -1
- package/skills/lj-awb/modules/model.md +12 -9
- package/skills/lj-awb/modules/task.md +4 -1
- package/skills/lj-awb/modules/upload.md +1 -1
- package/skills/lj-awb/modules/video.md +11 -2
- package/skills/lj-awb/modules/workflows.md +3 -1
- package/skills/lj-awb/references/error-codes.md +24 -0
- package/skills/lj-awb/references/model-options-read.md +16 -10
- package/skills/lj-awb/references/output-fields.md +10 -7
- package/skills/lj-awb/scripts/resolve-lj-awb-cmd.sh +106 -4
|
@@ -3,7 +3,16 @@ import path from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { LingjingAwbCliError, createEnvelope, toBool, toInt } from './common.js';
|
|
5
5
|
import { registerAwbCommands } from './commands.js';
|
|
6
|
-
import {
|
|
6
|
+
import { checkCliUpdate } from './update.js';
|
|
7
|
+
import {
|
|
8
|
+
formatCsvOutput,
|
|
9
|
+
formatPrettyError,
|
|
10
|
+
formatPrettyOutput,
|
|
11
|
+
formatTextError,
|
|
12
|
+
formatTextOutput,
|
|
13
|
+
formatYamlEnvelope,
|
|
14
|
+
normalizeJsonData,
|
|
15
|
+
} from './output.js';
|
|
7
16
|
|
|
8
17
|
function normalizeKey(value) {
|
|
9
18
|
return String(value || '').replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
@@ -96,6 +105,57 @@ function renderCliText(value) {
|
|
|
96
105
|
return String(value ?? '').replace(/\blj-awb\b/g, commandPrefix());
|
|
97
106
|
}
|
|
98
107
|
|
|
108
|
+
function shouldColorOutput() {
|
|
109
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '0') return false;
|
|
110
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'false') return false;
|
|
111
|
+
if (String(process.env.FORCE_COLOR || '').trim() && process.env.FORCE_COLOR !== '0') return true;
|
|
112
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '1') return true;
|
|
113
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'true') return true;
|
|
114
|
+
if (process.env.NO_COLOR) return false;
|
|
115
|
+
return Boolean(process.stdout.isTTY);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function colorText(value, code) {
|
|
119
|
+
const text = String(value ?? '');
|
|
120
|
+
return shouldColorOutput() && text ? `\u001b[${code}m${text}\u001b[0m` : text;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const HELP_COLOR = {
|
|
124
|
+
title: '1;38;5;39',
|
|
125
|
+
accent: '1;38;5;208',
|
|
126
|
+
heading: '1;38;5;75',
|
|
127
|
+
command: '38;5;82',
|
|
128
|
+
group: '38;5;215',
|
|
129
|
+
muted: '2;38;5;245',
|
|
130
|
+
option: '38;5;111',
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function helpTitle(value) {
|
|
134
|
+
if (!shouldColorOutput()) return String(value ?? '');
|
|
135
|
+
return `${colorText('◆', HELP_COLOR.accent)} ${colorText(value, HELP_COLOR.title)}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function helpHeading(value) {
|
|
139
|
+
if (!shouldColorOutput()) return String(value ?? '');
|
|
140
|
+
return `${colorText('▸', HELP_COLOR.accent)} ${colorText(value, HELP_COLOR.heading)}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function helpCommand(value) {
|
|
144
|
+
return colorText(value, HELP_COLOR.command);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function helpGroup(value) {
|
|
148
|
+
return colorText(value, HELP_COLOR.group);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function helpMuted(value) {
|
|
152
|
+
return colorText(value, HELP_COLOR.muted);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function helpOption(value) {
|
|
156
|
+
return colorText(value, HELP_COLOR.option);
|
|
157
|
+
}
|
|
158
|
+
|
|
99
159
|
function shellArg(value) {
|
|
100
160
|
const text = String(value ?? '');
|
|
101
161
|
return /^[A-Za-z0-9_./:=@,+-]+$/.test(text) ? text : JSON.stringify(text);
|
|
@@ -127,10 +187,14 @@ function commandInvocation(commandName, kwargs = {}, options = {}) {
|
|
|
127
187
|
return parts.join(' ');
|
|
128
188
|
}
|
|
129
189
|
|
|
130
|
-
function buildOutputContext(command, kwargs = {}) {
|
|
190
|
+
function buildOutputContext(command, kwargs = {}, data = {}) {
|
|
131
191
|
const optionKeys = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
|
|
132
192
|
const canSuggestDryRunExecution = command.name !== 'auth login';
|
|
133
193
|
const withoutDryRun = { omit: ['dryRun'] };
|
|
194
|
+
const confirmSet = { yes: true };
|
|
195
|
+
if (command.name === 'create asset' && data?.conversionPlan && !toBool(kwargs.autoConvert)) {
|
|
196
|
+
confirmSet.autoConvert = true;
|
|
197
|
+
}
|
|
134
198
|
const isModelList = command.name === 'model image-models' || command.name === 'model video-models';
|
|
135
199
|
const modelListAll = isModelList && toBool(kwargs.all);
|
|
136
200
|
const modelListLimit = isModelList
|
|
@@ -141,14 +205,53 @@ function buildOutputContext(command, kwargs = {}) {
|
|
|
141
205
|
: null;
|
|
142
206
|
return {
|
|
143
207
|
jsonCommand: `${commandInvocation(command.name, kwargs)} -f json`,
|
|
208
|
+
textCommand: `${commandInvocation(command.name, kwargs)} -f text`,
|
|
209
|
+
outputKind: buildCommandWorkflow(command).outputKind,
|
|
144
210
|
executeCommand: canSuggestDryRunExecution && toBool(kwargs.dryRun) ? commandInvocation(command.name, kwargs, withoutDryRun) : null,
|
|
145
211
|
confirmCommand: canSuggestDryRunExecution && toBool(kwargs.dryRun) && optionKeys.has('yes')
|
|
146
|
-
? commandInvocation(command.name, kwargs, { omit: ['dryRun'], set:
|
|
212
|
+
? commandInvocation(command.name, kwargs, { omit: ['dryRun'], set: confirmSet })
|
|
147
213
|
: null,
|
|
148
214
|
...(isModelList ? { listLimit: modelListLimit, moreCommand: modelListMoreCommand } : {}),
|
|
149
215
|
};
|
|
150
216
|
}
|
|
151
217
|
|
|
218
|
+
function updateCheckDisabledByEnv() {
|
|
219
|
+
return ['1', 'true', 'yes', 'on'].includes(String(process.env.LINGJING_AWB_DISABLE_UPDATE_CHECK || '').trim().toLowerCase())
|
|
220
|
+
|| ['1', 'true', 'yes', 'on'].includes(String(process.env.AWB_DISABLE_UPDATE_CHECK || '').trim().toLowerCase());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function shouldSkipUpdateCheck(argv = [], commandName = '') {
|
|
224
|
+
if (updateCheckDisabledByEnv()) return true;
|
|
225
|
+
return commandName === 'update';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function resolveUpdateNotice(argv = [], commandName = '', version = 'unknown') {
|
|
229
|
+
if (shouldSkipUpdateCheck(argv, commandName)) return null;
|
|
230
|
+
return await checkCliUpdate({ currentVersion: version }).catch(() => null);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const OUTPUT_FORMAT_ALIASES = {
|
|
234
|
+
agent: 'text',
|
|
235
|
+
table: 'pretty',
|
|
236
|
+
human: 'pretty',
|
|
237
|
+
compact: 'text',
|
|
238
|
+
'compact-text': 'text',
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const SUPPORTED_OUTPUT_FORMATS = new Set(['auto', 'pretty', 'text', 'json', 'yaml', 'csv']);
|
|
242
|
+
|
|
243
|
+
function resolveOutputFormat(format) {
|
|
244
|
+
const raw = String(format || 'pretty').trim().toLowerCase();
|
|
245
|
+
const normalized = OUTPUT_FORMAT_ALIASES[raw] || raw;
|
|
246
|
+
if (normalized === 'auto') return process.stdout.isTTY ? 'pretty' : 'text';
|
|
247
|
+
if (SUPPORTED_OUTPUT_FORMATS.has(normalized)) return normalized;
|
|
248
|
+
throw new LingjingAwbCliError(`未知输出格式:${format}`, {
|
|
249
|
+
type: 'argument_error',
|
|
250
|
+
exitCode: 2,
|
|
251
|
+
hint: '可用格式:pretty、text、json、yaml、csv、auto。Agent 建议使用 -f text;严格脚本解析再用 -f json。',
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
152
255
|
function assignKwarg(kwargs, key, value) {
|
|
153
256
|
if (kwargs[key] === undefined) {
|
|
154
257
|
kwargs[key] = value;
|
|
@@ -165,7 +268,7 @@ function parseArgv(argv) {
|
|
|
165
268
|
const commandParts = [];
|
|
166
269
|
const kwargs = {};
|
|
167
270
|
const kwargRawFlags = {};
|
|
168
|
-
let format = '
|
|
271
|
+
let format = 'pretty';
|
|
169
272
|
|
|
170
273
|
for (let index = 0; index < argv.length; index += 1) {
|
|
171
274
|
const token = argv[index];
|
|
@@ -232,13 +335,15 @@ const VIRTUAL_COMMANDS = [
|
|
|
232
335
|
'输出机器可读命令 schema,供 Agent / 脚本理解命令、参数和示例',
|
|
233
336
|
'',
|
|
234
337
|
'Examples:',
|
|
338
|
+
' lj-awb schema --brief -f json',
|
|
235
339
|
' lj-awb schema -f json',
|
|
236
340
|
' lj-awb schema --domain create -f json',
|
|
237
341
|
' lj-awb schema --domain create --command image -f json',
|
|
238
342
|
'',
|
|
239
|
-
'Hint: Agent
|
|
343
|
+
'Hint: Agent 先读 --brief 掌握能力地图;执行具体命令前再读精确 schema。',
|
|
240
344
|
].join('\n'),
|
|
241
345
|
args: [
|
|
346
|
+
{ name: 'brief', description: '输出面向 Agent 的轻量能力摘要,不展开每个参数' },
|
|
242
347
|
{ name: 'domain', valueName: 'name', description: '按命令域过滤,如 create / task / artifact / system' },
|
|
243
348
|
{ name: 'command', valueName: 'name', description: '按子命令过滤,如 image / video / wait / doctor' },
|
|
244
349
|
],
|
|
@@ -257,6 +362,9 @@ const RENAMED_COMMAND_HINTS = {
|
|
|
257
362
|
'video create-batch': 'create video-batch',
|
|
258
363
|
'video status': 'task video-status',
|
|
259
364
|
'video subtitle-remove': 'create video-subtitle-removal',
|
|
365
|
+
'video super-resolution': 'create video-super-resolution',
|
|
366
|
+
'video super-resolution-fee': 'create video-super-resolution-fee',
|
|
367
|
+
'video super-resolution-status': 'task video-super-resolution-status',
|
|
260
368
|
'video subtitle-status': 'task video-subtitle-status',
|
|
261
369
|
asset: 'create asset',
|
|
262
370
|
'asset match-actor': 'create asset-match-actor',
|
|
@@ -395,14 +503,21 @@ const GROUP_EXAMPLES = {
|
|
|
395
503
|
system: [
|
|
396
504
|
'lj-awb doctor',
|
|
397
505
|
'lj-awb doctor --verify',
|
|
506
|
+
'lj-awb update --check',
|
|
507
|
+
'lj-awb update',
|
|
508
|
+
'lj-awb schema --brief -f json',
|
|
398
509
|
'lj-awb schema -f json',
|
|
399
510
|
'lj-awb schema --domain create -f json',
|
|
400
511
|
],
|
|
401
512
|
auth: [
|
|
402
513
|
'lj-awb auth status',
|
|
403
514
|
'lj-awb auth verify',
|
|
515
|
+
'lj-awb auth login',
|
|
516
|
+
'lj-awb auth login --no-wait --json',
|
|
517
|
+
'lj-awb auth login --flow-id <flowId>',
|
|
404
518
|
'lj-awb auth login --access-key <access-key>',
|
|
405
519
|
'LINGJING_AWB_ACCESS_KEY=<access-key> lj-awb auth login',
|
|
520
|
+
'lj-awb auth logout',
|
|
406
521
|
'lj-awb auth clear --dry-run',
|
|
407
522
|
],
|
|
408
523
|
account: [
|
|
@@ -436,6 +551,7 @@ const GROUP_EXAMPLES = {
|
|
|
436
551
|
'lj-awb create image-fee --model-group-code <code> --prompt "一只小狗"',
|
|
437
552
|
'lj-awb create video --model-group-code <code> --prompt "镜头推进" --resource image:first_frame=./actor.png --dry-run',
|
|
438
553
|
'lj-awb create video-fee --model-group-code <code> --prompt "雨夜奔跑" --duration 5',
|
|
554
|
+
'lj-awb create video-super-resolution --object-name material/video-super/example.mp4 --dry-run',
|
|
439
555
|
'lj-awb create subject --model-code tx --name 女主 --resource primary:./three-view.png --dry-run',
|
|
440
556
|
'lj-awb create subject-wait --element-id <elementId> --wait-seconds 300',
|
|
441
557
|
'lj-awb create asset --group-id <id> --platform JIMENG --file ./actor.png --name "女主正面" --dry-run',
|
|
@@ -446,6 +562,7 @@ const GROUP_EXAMPLES = {
|
|
|
446
562
|
'lj-awb task image-status --task-id <imageTaskId>',
|
|
447
563
|
'lj-awb task video-status --task-id <videoTaskId>',
|
|
448
564
|
'lj-awb task video-subtitle-status --task-id <subtitleTaskId>',
|
|
565
|
+
'lj-awb task video-super-resolution-status --task-id <superResolutionTaskId>',
|
|
449
566
|
'lj-awb task wait --task-id <id> --task-type IMAGE_CREATE --wait-seconds 180',
|
|
450
567
|
'lj-awb task records --task-record-file .awb/tasks.jsonl',
|
|
451
568
|
],
|
|
@@ -470,8 +587,11 @@ const COMMAND_REQUIRED_OPTIONS = {
|
|
|
470
587
|
'create video-fee': ['modelGroupCode'],
|
|
471
588
|
'create video': ['modelGroupCode'],
|
|
472
589
|
'create video-batch': ['inputFile', 'modelGroupCode'],
|
|
590
|
+
'create video-super-resolution-fee': ['objectName'],
|
|
591
|
+
'create video-super-resolution': ['objectName'],
|
|
473
592
|
'task video-status': ['taskId'],
|
|
474
593
|
'task video-subtitle-status': ['taskId'],
|
|
594
|
+
'task video-super-resolution-status': ['taskId'],
|
|
475
595
|
'task wait': ['taskId', 'taskType'],
|
|
476
596
|
'create asset-match-actor': ['description'],
|
|
477
597
|
'create asset-groups': ['platform'],
|
|
@@ -521,6 +641,8 @@ const COMMAND_REQUIRED_ANY_OPTIONS = {
|
|
|
521
641
|
'upload files': [['file', 'files', 'filesJson']],
|
|
522
642
|
'create video-fee': [['prompt', 'resource', 'resourcesJson']],
|
|
523
643
|
'create video': [['prompt', 'resource', 'resourcesJson']],
|
|
644
|
+
'create video-super-resolution-fee': [['objectName']],
|
|
645
|
+
'create video-super-resolution': [['objectName']],
|
|
524
646
|
'create video-subtitle-removal': [['sourceTaskId']],
|
|
525
647
|
'create asset-group-update': [['name', 'description', 'projectName']],
|
|
526
648
|
'create asset': [['file', 'url', 'backendPath']],
|
|
@@ -586,6 +708,7 @@ const CONFIRMATION_COMMANDS = new Set([
|
|
|
586
708
|
'create image-batch',
|
|
587
709
|
'create video',
|
|
588
710
|
'create video-batch',
|
|
711
|
+
'create video-super-resolution',
|
|
589
712
|
'create video-subtitle-removal',
|
|
590
713
|
'create asset-group',
|
|
591
714
|
'create asset-group-update',
|
|
@@ -601,6 +724,7 @@ const COST_COMMANDS = new Set([
|
|
|
601
724
|
'create image-batch',
|
|
602
725
|
'create video',
|
|
603
726
|
'create video-batch',
|
|
727
|
+
'create video-super-resolution',
|
|
604
728
|
'create video-subtitle-removal',
|
|
605
729
|
]);
|
|
606
730
|
|
|
@@ -615,6 +739,7 @@ const REMOTE_WRITE_COMMANDS = new Set([
|
|
|
615
739
|
'create image-batch',
|
|
616
740
|
'create video',
|
|
617
741
|
'create video-batch',
|
|
742
|
+
'create video-super-resolution',
|
|
618
743
|
'create video-subtitle-removal',
|
|
619
744
|
'create asset-group',
|
|
620
745
|
'create asset-group-update',
|
|
@@ -626,24 +751,28 @@ const REMOTE_WRITE_COMMANDS = new Set([
|
|
|
626
751
|
]);
|
|
627
752
|
|
|
628
753
|
const LOCAL_STATE_WRITE_COMMANDS = new Set([
|
|
754
|
+
'update',
|
|
629
755
|
'auth login',
|
|
630
756
|
'auth clear',
|
|
757
|
+
'auth logout',
|
|
631
758
|
'project use',
|
|
632
759
|
'project create',
|
|
633
760
|
'project ensure',
|
|
634
761
|
]);
|
|
635
762
|
|
|
636
763
|
const DESTRUCTIVE_COMMANDS = new Set(['auth clear', ...ARTIFACT_DELETE_COMMANDS]);
|
|
637
|
-
const LONG_RUNNING_COMMANDS = new Set(['task wait', 'task record-poll', 'create subject-wait', 'create subject-voice-wait']);
|
|
638
|
-
const NETWORK_NONE_COMMANDS = new Set(['schema', 'auth status', 'auth clear', 'model input-guide', 'task records']);
|
|
764
|
+
const LONG_RUNNING_COMMANDS = new Set(['auth login', 'task wait', 'task record-poll', 'create subject-wait', 'create subject-voice-wait']);
|
|
765
|
+
const NETWORK_NONE_COMMANDS = new Set(['schema', 'auth status', 'auth clear', 'auth logout', 'model input-guide', 'task records']);
|
|
639
766
|
const NETWORK_CONDITIONAL_COMMANDS = new Set(['doctor', 'auth login']);
|
|
640
767
|
|
|
641
768
|
const OUTPUT_KIND_BY_COMMAND = {
|
|
642
769
|
doctor: 'environment_report',
|
|
770
|
+
update: 'update_result',
|
|
643
771
|
schema: 'command_schema',
|
|
644
772
|
'auth status': 'auth_status',
|
|
645
773
|
'auth verify': 'auth_status',
|
|
646
774
|
'auth login': 'auth_result',
|
|
775
|
+
'auth logout': 'auth_result',
|
|
647
776
|
'account info': 'account_summary',
|
|
648
777
|
'account teams': 'team_list',
|
|
649
778
|
'project list': 'project_list',
|
|
@@ -665,9 +794,12 @@ const OUTPUT_KIND_BY_COMMAND = {
|
|
|
665
794
|
'create video-fee': 'fee_estimate',
|
|
666
795
|
'create video': 'task_submission',
|
|
667
796
|
'create video-batch': 'batch_task_submission',
|
|
797
|
+
'create video-super-resolution-fee': 'fee_estimate',
|
|
798
|
+
'create video-super-resolution': 'task_submission',
|
|
668
799
|
'task video-status': 'task_status',
|
|
669
800
|
'create video-subtitle-removal': 'subtitle_task_submission',
|
|
670
801
|
'task video-subtitle-status': 'subtitle_task_status',
|
|
802
|
+
'task video-super-resolution-status': 'task_status',
|
|
671
803
|
'task list': 'task_list',
|
|
672
804
|
'task wait': 'task_status',
|
|
673
805
|
'task records': 'local_task_records',
|
|
@@ -743,6 +875,8 @@ const PREFLIGHTS_BY_COMMAND = {
|
|
|
743
875
|
'create image-batch': ['doctor --verify', 'model image-models', 'model options', 'model create-spec', 'prepare JSONL input', 'create image-batch --dry-run'],
|
|
744
876
|
'create video': ['doctor --verify', 'model video-models', 'model options', 'model create-spec', 'confirm missing key params', 'create video-fee', 'create video --dry-run'],
|
|
745
877
|
'create video-batch': ['doctor --verify', 'model video-models', 'model options', 'model create-spec', 'prepare JSONL input', 'create video-batch --dry-run'],
|
|
878
|
+
'create video-super-resolution-fee': ['doctor --verify', 'create video-super-resolution-fee --object-name <objectName>'],
|
|
879
|
+
'create video-super-resolution': ['doctor --verify', 'create video-super-resolution --object-name <objectName> --dry-run'],
|
|
746
880
|
'create subject': ['doctor --verify', 'create subject --model-code tx|vidu --dry-run'],
|
|
747
881
|
'create subject-batch': ['doctor --verify', 'prepare JSONL input with modelCode', 'create subject-batch --dry-run'],
|
|
748
882
|
'create video-subtitle-removal': ['doctor --verify', 'create video-subtitle-removal --source-task-id <videoTaskId> --dry-run'],
|
|
@@ -805,9 +939,10 @@ function buildCommandSafety(command, optionKeys) {
|
|
|
805
939
|
const costsPoints = COST_COMMANDS.has(name);
|
|
806
940
|
const destructive = DESTRUCTIVE_COMMANDS.has(name);
|
|
807
941
|
const supportsDryRun = optionKeys.has('dryRun');
|
|
942
|
+
const updateInstall = name === 'update';
|
|
808
943
|
return {
|
|
809
|
-
safeToAutoRun: !(remoteWrite || localStateWrite || requiresConfirmation || costsPoints || destructive),
|
|
810
|
-
network: commandNetworkMode(name),
|
|
944
|
+
safeToAutoRun: !(remoteWrite || localStateWrite || requiresConfirmation || costsPoints || destructive || updateInstall),
|
|
945
|
+
network: updateInstall ? 'conditional' : commandNetworkMode(name),
|
|
811
946
|
remoteWrite,
|
|
812
947
|
localStateWrite,
|
|
813
948
|
costsPoints,
|
|
@@ -829,6 +964,9 @@ function buildCommandWorkflow(command) {
|
|
|
829
964
|
if (command.name === 'create video') {
|
|
830
965
|
next.push('读取 data.taskId 和 data.nextCommand', '立即运行响应里的 nextCommand,或 task wait --task-type VIDEO_GROUP 等待结果');
|
|
831
966
|
}
|
|
967
|
+
if (command.name === 'create video-super-resolution') {
|
|
968
|
+
next.push('读取 data.taskId 和 data.nextCommand', '运行响应里的 task video-super-resolution-status 命令查询结果');
|
|
969
|
+
}
|
|
832
970
|
if (command.name === 'create video-subtitle-removal') {
|
|
833
971
|
next.push('读取 data.taskId 和 data.nextCommand', '运行 task video-subtitle-status 或响应里的 nextCommand 等待结果');
|
|
834
972
|
}
|
|
@@ -839,12 +977,12 @@ function buildCommandWorkflow(command) {
|
|
|
839
977
|
next.push('读取 data.voiceRecordId / data.externalId', 'externalId 为空时立刻运行 create subject-voice-wait 获取 nextVoiceArg');
|
|
840
978
|
}
|
|
841
979
|
if (command.name === 'create asset') {
|
|
842
|
-
next.push('读取 data.assetPath', '若后端返回审核 taskId,再运行 task wait --task-type ASSET_REGISTER');
|
|
980
|
+
next.push('读取 data.assetType / data.assetPath', 'data.conversionRequired=true 时先确认是否转码;正式执行可交互确认或追加 --auto-convert', '若后端返回审核 taskId,再运行 task wait --task-type ASSET_REGISTER');
|
|
843
981
|
}
|
|
844
982
|
if (command.name === 'model image-models' || command.name === 'model video-models') {
|
|
845
983
|
next.push('读取 data.models[] 得到全部候选;用户给了模型口语名时保留同族全部候选,不只取第一个');
|
|
846
984
|
next.push('对每个候选运行 model options --model-group-code <modelGroupCode> 查看真实参数和素材约束');
|
|
847
|
-
next.push('
|
|
985
|
+
next.push('向用户展示候选模型 + 参数取值 + 资源能力后 STOP;等待用户选择或明确授权默认,未确认不得进入 create-spec/fee/dry-run/create');
|
|
848
986
|
}
|
|
849
987
|
if (command.name === 'model asset-review-models') {
|
|
850
988
|
next.push('读取 data.models[].platform', '用 create asset-groups --platform <platform> 查重或创建素材组');
|
|
@@ -868,11 +1006,167 @@ function buildCommandWorkflow(command) {
|
|
|
868
1006
|
};
|
|
869
1007
|
}
|
|
870
1008
|
|
|
1009
|
+
function buildDomainSummaries(commands) {
|
|
1010
|
+
const domains = new Map();
|
|
1011
|
+
for (const command of commands) {
|
|
1012
|
+
const group = commandGroup(command);
|
|
1013
|
+
domains.set(group, {
|
|
1014
|
+
name: group,
|
|
1015
|
+
description: GROUP_DESCRIPTIONS[group] || '',
|
|
1016
|
+
commandCount: (domains.get(group)?.commandCount || 0) + 1,
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
return Array.from(domains.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function requiredOptionsForCommand(command) {
|
|
1023
|
+
const requiredOptions = new Set();
|
|
1024
|
+
if (commandGroup(command) === 'artifact') requiredOptions.add('projectId');
|
|
1025
|
+
for (const key of COMMAND_REQUIRED_OPTIONS[command.name] || []) requiredOptions.add(key);
|
|
1026
|
+
return [...requiredOptions];
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function requiredAnyOptionsForCommand(command) {
|
|
1030
|
+
return COMMAND_REQUIRED_ANY_OPTIONS[command.name] || [];
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function buildCommandSchemaEntry(command) {
|
|
1034
|
+
const requiredOptions = requiredOptionsForCommand(command);
|
|
1035
|
+
const requiredAnyOptions = requiredAnyOptionsForCommand(command);
|
|
1036
|
+
const optionKeys = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
|
|
1037
|
+
return {
|
|
1038
|
+
name: command.name,
|
|
1039
|
+
domain: commandGroup(command),
|
|
1040
|
+
command: commandSubcommand(command),
|
|
1041
|
+
invocation: `${commandPrefix()} ${command.name}`,
|
|
1042
|
+
helpCommand: `${commandPrefix()} ${command.name} -h`,
|
|
1043
|
+
summary: summaryOf(command),
|
|
1044
|
+
description: renderCliText(summaryOf(command)),
|
|
1045
|
+
examples: extractExamples(command).map(renderCliText),
|
|
1046
|
+
jsonExamples: extractExamples(command).map(asJsonExample),
|
|
1047
|
+
requiredOptions,
|
|
1048
|
+
requiredAnyOptions,
|
|
1049
|
+
safety: buildCommandSafety(command, optionKeys),
|
|
1050
|
+
workflow: buildCommandWorkflow(command),
|
|
1051
|
+
options: (command.args || []).map((arg) => {
|
|
1052
|
+
const key = normalizeKey(arg.name);
|
|
1053
|
+
return {
|
|
1054
|
+
name: arg.name,
|
|
1055
|
+
key,
|
|
1056
|
+
flag: formatOptionName(arg.name),
|
|
1057
|
+
valueName: arg.valueName || null,
|
|
1058
|
+
required: requiredOptions.includes(key),
|
|
1059
|
+
requiredAnyGroup: requiredAnyOptions.find((group) => group.includes(key)) || null,
|
|
1060
|
+
description: arg.description || '',
|
|
1061
|
+
};
|
|
1062
|
+
}),
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function namesWhere(entries, predicate) {
|
|
1067
|
+
return entries.filter(predicate).map((entry) => entry.name);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function buildAgentBrief(commands, version = 'unknown') {
|
|
1071
|
+
const contract = buildAgentContract();
|
|
1072
|
+
const entries = commands.map(buildCommandSchemaEntry);
|
|
1073
|
+
const domains = buildDomainSummaries(commands).map((domain) => ({
|
|
1074
|
+
...domain,
|
|
1075
|
+
commands: entries
|
|
1076
|
+
.filter((entry) => entry.domain === domain.name)
|
|
1077
|
+
.map((entry) => entry.name),
|
|
1078
|
+
}));
|
|
1079
|
+
return {
|
|
1080
|
+
schemaVersion: 1,
|
|
1081
|
+
kind: 'agent_brief',
|
|
1082
|
+
cli: {
|
|
1083
|
+
name: 'lj-awb',
|
|
1084
|
+
version,
|
|
1085
|
+
commandPrefix: commandPrefix(),
|
|
1086
|
+
},
|
|
1087
|
+
commandCount: entries.length,
|
|
1088
|
+
domains,
|
|
1089
|
+
outputPolicy: {
|
|
1090
|
+
agentRecommendedOutputFormat: contract.agentRecommendedOutputFormat,
|
|
1091
|
+
scriptRecommendedOutputFormat: contract.scriptRecommendedOutputFormat,
|
|
1092
|
+
textFlag: contract.textFlag,
|
|
1093
|
+
jsonFlag: contract.jsonFlag,
|
|
1094
|
+
policy: contract.outputPolicy,
|
|
1095
|
+
},
|
|
1096
|
+
statePolicy: contract.statePolicy,
|
|
1097
|
+
workflowPolicy: contract.workflowPolicy,
|
|
1098
|
+
safetyPolicy: {
|
|
1099
|
+
confirmationPolicy: contract.confirmationPolicy,
|
|
1100
|
+
dryRunPolicy: contract.dryRunPolicy,
|
|
1101
|
+
remoteWriteCount: namesWhere(entries, (entry) => entry.safety.remoteWrite).length,
|
|
1102
|
+
costsPointsCount: namesWhere(entries, (entry) => entry.safety.costsPoints).length,
|
|
1103
|
+
localStateWriteCount: namesWhere(entries, (entry) => entry.safety.localStateWrite).length,
|
|
1104
|
+
destructiveCount: namesWhere(entries, (entry) => entry.safety.destructive).length,
|
|
1105
|
+
longRunningCount: namesWhere(entries, (entry) => entry.safety.longRunning).length,
|
|
1106
|
+
},
|
|
1107
|
+
commandClasses: {
|
|
1108
|
+
costsPoints: namesWhere(entries, (entry) => entry.safety.costsPoints),
|
|
1109
|
+
remoteWrite: namesWhere(entries, (entry) => entry.safety.remoteWrite),
|
|
1110
|
+
localStateWrite: namesWhere(entries, (entry) => entry.safety.localStateWrite),
|
|
1111
|
+
destructive: namesWhere(entries, (entry) => entry.safety.destructive),
|
|
1112
|
+
longRunning: namesWhere(entries, (entry) => entry.safety.longRunning),
|
|
1113
|
+
safeRead: namesWhere(entries, (entry) => entry.safety.safeToAutoRun && entry.safety.network !== 'none'),
|
|
1114
|
+
offlineRead: namesWhere(entries, (entry) => entry.safety.safeToAutoRun && entry.safety.network === 'none'),
|
|
1115
|
+
},
|
|
1116
|
+
routing: [
|
|
1117
|
+
{
|
|
1118
|
+
intent: '环境、认证、账号、团队、项目组、积分',
|
|
1119
|
+
start: ['doctor --verify', 'account info', 'project current', 'credits balance'],
|
|
1120
|
+
rule: '只读查询可自动运行;切换团队、切换/创建/更新项目组和清空认证按 safety dry-run/confirm 处理。',
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
intent: '模型发现和创作任务',
|
|
1124
|
+
start: ['model image-models', 'model video-models', 'model options', 'model create-spec'],
|
|
1125
|
+
rule: '先展示候选模型、真实参数和资源能力并 STOP;用户选择或明确授权默认后,关键参数确认,再只跑一次 fee、dry-run 和最终 --yes。',
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
intent: '上传、主体、音色、素材加白',
|
|
1129
|
+
start: ['upload files', 'create subject-list', 'create subject-voice-list', 'model asset-review-models'],
|
|
1130
|
+
rule: '复用已上传 backendPath、已有主体/音色/素材组;素材加白平台来自 model asset-review-models 或用户明确选择。',
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
intent: '异步任务和批量恢复',
|
|
1134
|
+
start: ['task wait', 'task record-poll', 'task records'],
|
|
1135
|
+
rule: '优先复用 create 返回的 nextCommand;批量任务使用 task-record-file,失败项单独重试。',
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
intent: '最终产物 artifact',
|
|
1139
|
+
start: ['artifact script', 'artifact asset', 'artifact video', 'artifact clip'],
|
|
1140
|
+
rule: 'artifact 使用 project-id,不是 project-group-no;写入/删除/导入先 dry-run,再一次性确认写入面。',
|
|
1141
|
+
},
|
|
1142
|
+
],
|
|
1143
|
+
lookup: {
|
|
1144
|
+
preciseSchema: `${commandPrefix()} schema --domain <domain> --command <command> -f json`,
|
|
1145
|
+
fullSchema: `${commandPrefix()} schema -f json`,
|
|
1146
|
+
commandHelp: `${commandPrefix()} <domain> <command> -h`,
|
|
1147
|
+
modelInputGuide: contract.modelInputGuide.textCommand,
|
|
1148
|
+
schemaPolicy: contract.schemaPolicy,
|
|
1149
|
+
},
|
|
1150
|
+
canonicalFields: contract.canonicalFields,
|
|
1151
|
+
resourceShortcut: contract.resourceShortcut,
|
|
1152
|
+
resourcesJson: contract.resourcesJson,
|
|
1153
|
+
modelCandidatePresentation: contract.modelCandidatePresentation,
|
|
1154
|
+
taskTypes: contract.taskTypes,
|
|
1155
|
+
exitCodes: contract.exitCodes,
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
|
|
871
1159
|
function buildAgentContract() {
|
|
872
1160
|
return {
|
|
873
|
-
defaultOutputFormat: '
|
|
1161
|
+
defaultOutputFormat: 'pretty',
|
|
1162
|
+
humanDefaultOutputFormat: 'pretty',
|
|
1163
|
+
agentRecommendedOutputFormat: 'text',
|
|
1164
|
+
agentCompactOutputFormat: 'text',
|
|
1165
|
+
scriptRecommendedOutputFormat: 'json',
|
|
874
1166
|
jsonFlag: '-f json',
|
|
875
|
-
|
|
1167
|
+
textFlag: '-f text',
|
|
1168
|
+
outputFormats: ['pretty', 'text', 'json', 'yaml', 'csv', 'auto'],
|
|
1169
|
+
outputPolicy: '默认输出 pretty 人类友好视图,包含标题、摘要表格、列表表格和下一步建议;Agent 推荐显式传 -f text 读取稳定的分区 key=value 输出;脚本严格解析或需要完整嵌套结构时再传 -f json 或 --json。JSON envelope 字段已做归一化,见 canonicalFields。',
|
|
876
1170
|
statePolicy: {
|
|
877
1171
|
purpose: 'Agent 应在同一轮 / 同一项目中维护 AWB 上下文账本,减少重复查询和重复提交。',
|
|
878
1172
|
cacheKeys: [
|
|
@@ -890,12 +1184,13 @@ function buildAgentContract() {
|
|
|
890
1184
|
invalidation: '用户切换账号、团队、项目组、模型、素材、prompt 或关键参数后,只刷新受影响的缓存;不要重复跑未变化的 model options / create-spec / fee。',
|
|
891
1185
|
},
|
|
892
1186
|
workflowPolicy: [
|
|
893
|
-
'
|
|
1187
|
+
'模型口语名命中后必须先展示候选模型、真实参数取值和资源能力,并在清单后 STOP 等用户选择或明确授权默认;没有完成用户可见候选清单和确认前,不得代选默认模型 / 参数,也不得进入 create-spec、fee、dry-run 或 create。',
|
|
894
1188
|
'模型探索阶段只读 model list / options / create-spec;fee 只在用户确认关键参数后跑一次。',
|
|
1189
|
+
'model options.params[] 中带 cliArg/genericModelParam 的新增模型配置参数可通过 --model-param key=value、--model-params-json 或同名下划线长参数传入;defaultValue 只能展示为候选默认,不能静默代选。',
|
|
895
1190
|
'supportsDryRun=true 的写入 / 扣费命令先 dry-run,确认后 yes;不要把 dry-run 当参数探索工具反复跑。',
|
|
896
1191
|
'用户给出多条同模型同参数任务时优先 batch + task-record-file,不要单条循环 create。',
|
|
897
1192
|
'命令返回 nextCommand / nextRefSubject / nextVoiceArg 时优先复用返回值,不手拼等价命令。',
|
|
898
|
-
'素材加白平台先通过 model asset-review-models 或用户明确输入确定;create asset-* 必须显式传 --platform,不要依赖默认平台。',
|
|
1193
|
+
'素材加白平台先通过 model asset-review-models 或用户明确输入确定;create asset-* 必须显式传 --platform,不要依赖默认平台。create asset 会自动判断 assetType=Image/Video/Audio,本地文件会用 ffprobe 校验图片/视频尺寸、视频时长/FPS/像素数和音频时长;不合法素材正式执行时会询问是否转码,非交互场景可追加 --auto-convert;macOS + Homebrew 环境缺失时 CLI 会自动安装 ffmpeg。',
|
|
899
1194
|
'旧根域 image / video / asset / subject 已移除;只使用 create / task / artifact 等 schema 暴露的 domain。',
|
|
900
1195
|
],
|
|
901
1196
|
canonicalFields: {
|
|
@@ -909,7 +1204,7 @@ function buildAgentContract() {
|
|
|
909
1204
|
{ canonical: 'pointCost', aliases: ['points', 'point', 'costPoint', 'estimatePoint'], meaning: '本次任务预计 / 实际消耗积分' },
|
|
910
1205
|
],
|
|
911
1206
|
},
|
|
912
|
-
schemaPolicy: 'schema
|
|
1207
|
+
schemaPolicy: 'Agent 先读 schema --brief 建立能力地图;执行具体命令前再读精确 schema --domain <domain> --command <command>。完整 schema -f json 只用于覆盖校验、脚本生成或调试 schema 本身;查看长帮助用 schema.commands[].helpCommand 或 lj-awb <domain> <command> -h。',
|
|
913
1208
|
confirmationPolicy: 'schema.commands[].safety.requiresConfirmation=true 的命令,Agent 必须先向用户确认,再追加 --yes。',
|
|
914
1209
|
dryRunPolicy: 'schema.commands[].safety.supportsDryRun=true 的写入 / 扣费命令,正式执行前优先跑 --dry-run。',
|
|
915
1210
|
resourceShortcut: {
|
|
@@ -936,32 +1231,39 @@ function buildAgentContract() {
|
|
|
936
1231
|
modelCandidatePresentation: {
|
|
937
1232
|
purpose: '把 model image-models / video-models 与每个候选的 model options 转成用户可见清单,避免 Agent 内部看完 options 后直接代选。',
|
|
938
1233
|
trigger: '用户问有哪些模型、给出口语名、或模型列表返回候选时都触发;即使只有一个候选也要展示真实可选项。',
|
|
1234
|
+
gateType: 'hard_stop',
|
|
1235
|
+
stopAfterPresentation: true,
|
|
1236
|
+
resumeCondition: '用户选择模型和关键参数,或明确说“按默认 / 你来选”。',
|
|
939
1237
|
requiredVisibleFields: ['displayName', 'modelDesc', 'taskQueueNum', 'quality values/default', 'ratio values/default', 'duration or generateNum values/default', 'resource modes/media/usages', 'channel or fast/pro differences'],
|
|
940
|
-
blockedBeforePresentation: ['create image-fee', 'create video-fee', 'create image --dry-run', 'create video --dry-run', 'default model recommendation', 'default quality/ratio/duration choice'],
|
|
1238
|
+
blockedBeforePresentation: ['model create-spec', 'create image-fee', 'create video-fee', 'create image-batch --dry-run', 'create video-batch --dry-run', 'create image --dry-run', 'create video --dry-run', 'create image', 'create video', 'default model recommendation', 'default quality/ratio/duration choice'],
|
|
941
1239
|
},
|
|
942
1240
|
modelOptions: {
|
|
943
1241
|
command: `${commandPrefix()} model options --model-group-code <code>`,
|
|
1242
|
+
textCommand: `${commandPrefix()} model options --model-group-code <code> -f text`,
|
|
944
1243
|
jsonCommand: `${commandPrefix()} model options --model-group-code <code> -f json`,
|
|
945
|
-
purpose: '查询指定模型支持的 CLI 参数、枚举值、默认值、素材约束和条件约束。',
|
|
1244
|
+
purpose: '查询指定模型支持的 CLI 参数、枚举值、默认值、素材约束和条件约束。Agent 默认读 textCommand;只有程序化校验完整嵌套结构时读 jsonCommand。',
|
|
946
1245
|
useBefore: ['model create-spec', 'create image-fee', 'create image', 'create video-fee', 'create video'],
|
|
947
|
-
keyFields: ['params[].values', 'params[].defaultValue', 'resources[].mediaType', 'resources[].usage', 'resources[].fileTypes', 'resources[].maxFiles', 'resources[].supportLastFrameOnly', 'resources[].minDurationMs', 'resources[].maxDurationMs', 'constraints[].target', 'constraints[].conditions', 'constraints[].effect'],
|
|
1246
|
+
keyFields: ['params[].key', 'params[].cliArg', 'params[].genericModelParam', 'params[].values', 'params[].defaultValue', 'resources[].mediaType', 'resources[].usage', 'resources[].fileTypes', 'resources[].maxFiles', 'resources[].supportLastFrameOnly', 'resources[].minDurationMs', 'resources[].maxDurationMs', 'constraints[].target', 'constraints[].conditions', 'constraints[].effect'],
|
|
948
1247
|
},
|
|
949
1248
|
modelCreateSpec: {
|
|
950
1249
|
command: `${commandPrefix()} model create-spec --model-group-code <code>`,
|
|
1250
|
+
textCommand: `${commandPrefix()} model create-spec --model-group-code <code> -f text`,
|
|
951
1251
|
jsonCommand: `${commandPrefix()} model create-spec --model-group-code <code> -f json`,
|
|
952
|
-
purpose: '查看指定模型如何组织 create 命令:输入模式、素材绑定规则、示例和创建前检查项。',
|
|
1252
|
+
purpose: '查看指定模型如何组织 create 命令:输入模式、素材绑定规则、示例和创建前检查项。Agent 默认读 textCommand;只有程序化校验完整嵌套结构时读 jsonCommand。',
|
|
953
1253
|
useBefore: ['create image-fee', 'create image', 'create video-fee', 'create video'],
|
|
954
1254
|
keyFields: ['inputRequirement', 'supportedIntents', 'validationRules', 'agentGuidance', 'preflight', 'examples'],
|
|
955
1255
|
},
|
|
956
1256
|
modelInputGuide: {
|
|
957
1257
|
command: `${commandPrefix()} model input-guide`,
|
|
1258
|
+
textCommand: `${commandPrefix()} model input-guide -f text`,
|
|
958
1259
|
jsonCommand: `${commandPrefix()} model input-guide -f json`,
|
|
959
|
-
purpose: '查看跨模型统一创建参数、resources 字段和素材绑定规则。',
|
|
1260
|
+
purpose: '查看跨模型统一创建参数、resources 字段和素材绑定规则。Agent 默认读 textCommand;脚本严格解析时读 jsonCommand。',
|
|
960
1261
|
useBefore: ['model options', 'model create-spec'],
|
|
961
1262
|
},
|
|
962
1263
|
taskTypes: {
|
|
963
1264
|
image: 'IMAGE_CREATE',
|
|
964
1265
|
video: 'VIDEO_GROUP',
|
|
1266
|
+
videoSuperResolution: 'VIDEO_SUPER_RESOLUTION',
|
|
965
1267
|
subtitleRemoval: 'VIDEO_SUBTITLE_REMOVAL',
|
|
966
1268
|
assetRegister: 'ASSET_REGISTER',
|
|
967
1269
|
},
|
|
@@ -982,17 +1284,10 @@ function buildAgentContract() {
|
|
|
982
1284
|
}
|
|
983
1285
|
|
|
984
1286
|
function buildCommandSchema(commands, kwargs = {}, version = 'unknown') {
|
|
1287
|
+
if (toBool(kwargs.brief)) return buildAgentBrief(commands, version);
|
|
985
1288
|
const domainFilter = String(kwargs.domain ?? '').trim();
|
|
986
1289
|
const commandFilter = String(kwargs.command ?? '').trim();
|
|
987
|
-
const domains =
|
|
988
|
-
for (const command of commands) {
|
|
989
|
-
const group = commandGroup(command);
|
|
990
|
-
domains.set(group, {
|
|
991
|
-
name: group,
|
|
992
|
-
description: GROUP_DESCRIPTIONS[group] || '',
|
|
993
|
-
commandCount: (domains.get(group)?.commandCount || 0) + 1,
|
|
994
|
-
});
|
|
995
|
-
}
|
|
1290
|
+
const domains = buildDomainSummaries(commands);
|
|
996
1291
|
const filteredCommands = commands
|
|
997
1292
|
.filter((command) => !domainFilter || commandGroup(command) === domainFilter)
|
|
998
1293
|
.filter((command) => !commandFilter || command.name === commandFilter || commandSubcommand(command) === commandFilter);
|
|
@@ -1016,49 +1311,16 @@ function buildCommandSchema(commands, kwargs = {}, version = 'unknown') {
|
|
|
1016
1311
|
`${commandPrefix()} <domain> <command> -h`,
|
|
1017
1312
|
],
|
|
1018
1313
|
globalOptions: [
|
|
1019
|
-
{ flag: '-f, --format
|
|
1314
|
+
{ flag: '-f, --format <format>', key: 'format', description: '输出格式:pretty(默认)、text、json、yaml、csv、auto' },
|
|
1020
1315
|
{ flag: '--json', key: 'format', value: 'json', description: '等同于 -f json' },
|
|
1021
1316
|
{ flag: '-h, --help', key: 'help', description: '查看帮助' },
|
|
1022
1317
|
{ flag: '-v, --version', key: 'version', description: '查看版本' },
|
|
1023
1318
|
],
|
|
1024
1319
|
agentContract: buildAgentContract(),
|
|
1025
|
-
domains:
|
|
1320
|
+
domains: domains
|
|
1026
1321
|
.filter((domain) => !shouldFilterDomains || matchedDomains.has(domain.name))
|
|
1027
1322
|
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
1028
|
-
commands: filteredCommands.map(
|
|
1029
|
-
const requiredOptions = new Set();
|
|
1030
|
-
if (commandGroup(command) === 'artifact') requiredOptions.add('projectId');
|
|
1031
|
-
for (const key of COMMAND_REQUIRED_OPTIONS[command.name] || []) requiredOptions.add(key);
|
|
1032
|
-
const requiredAnyOptions = COMMAND_REQUIRED_ANY_OPTIONS[command.name] || [];
|
|
1033
|
-
const optionKeys = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
|
|
1034
|
-
return {
|
|
1035
|
-
name: command.name,
|
|
1036
|
-
domain: commandGroup(command),
|
|
1037
|
-
command: commandSubcommand(command),
|
|
1038
|
-
invocation: `${commandPrefix()} ${command.name}`,
|
|
1039
|
-
helpCommand: `${commandPrefix()} ${command.name} -h`,
|
|
1040
|
-
summary: summaryOf(command),
|
|
1041
|
-
description: renderCliText(summaryOf(command)),
|
|
1042
|
-
examples: extractExamples(command).map(renderCliText),
|
|
1043
|
-
jsonExamples: extractExamples(command).map(asJsonExample),
|
|
1044
|
-
requiredOptions: [...requiredOptions],
|
|
1045
|
-
requiredAnyOptions,
|
|
1046
|
-
safety: buildCommandSafety(command, optionKeys),
|
|
1047
|
-
workflow: buildCommandWorkflow(command),
|
|
1048
|
-
options: (command.args || []).map((arg) => {
|
|
1049
|
-
const key = normalizeKey(arg.name);
|
|
1050
|
-
return {
|
|
1051
|
-
name: arg.name,
|
|
1052
|
-
key,
|
|
1053
|
-
flag: formatOptionName(arg.name),
|
|
1054
|
-
valueName: arg.valueName || null,
|
|
1055
|
-
required: requiredOptions.has(key),
|
|
1056
|
-
requiredAnyGroup: requiredAnyOptions.find((group) => group.includes(key)) || null,
|
|
1057
|
-
description: arg.description || '',
|
|
1058
|
-
};
|
|
1059
|
-
}),
|
|
1060
|
-
};
|
|
1061
|
-
}),
|
|
1323
|
+
commands: filteredCommands.map(buildCommandSchemaEntry),
|
|
1062
1324
|
};
|
|
1063
1325
|
}
|
|
1064
1326
|
|
|
@@ -1071,43 +1333,44 @@ function printRootHelp(commands, version) {
|
|
|
1071
1333
|
}
|
|
1072
1334
|
|
|
1073
1335
|
const lines = [
|
|
1074
|
-
`lj-awb v${version}
|
|
1336
|
+
helpTitle(`lj-awb v${version}`),
|
|
1075
1337
|
'',
|
|
1076
|
-
'灵境 AWB
|
|
1338
|
+
'灵境 AWB 命令行工具。默认输出人类友好的 pretty 视图;Agent 建议显式传 -f text,脚本严格解析用 -f json。',
|
|
1077
1339
|
'',
|
|
1078
|
-
'Usage:',
|
|
1079
|
-
` ${commandPrefix()} <domain> <command> [options]
|
|
1080
|
-
` ${commandPrefix()} <command> [options]
|
|
1081
|
-
` ${commandPrefix()} <domain> -h
|
|
1082
|
-
` ${commandPrefix()} <domain> <command> -h
|
|
1340
|
+
helpHeading('Usage:'),
|
|
1341
|
+
helpCommand(` ${commandPrefix()} <domain> <command> [options]`),
|
|
1342
|
+
helpCommand(` ${commandPrefix()} <command> [options]`),
|
|
1343
|
+
helpCommand(` ${commandPrefix()} <domain> -h`),
|
|
1344
|
+
helpCommand(` ${commandPrefix()} <domain> <command> -h`),
|
|
1083
1345
|
'',
|
|
1084
|
-
'Quick start:',
|
|
1085
|
-
` ${commandPrefix()} auth status
|
|
1086
|
-
` ${commandPrefix()} auth login --access-key <access-key
|
|
1087
|
-
` ${commandPrefix()} model image-models --model Banana
|
|
1088
|
-
` ${commandPrefix()} model input-guide
|
|
1089
|
-
` ${commandPrefix()} model options --model-group-code <code
|
|
1090
|
-
` ${commandPrefix()} model create-spec --model-group-code <code
|
|
1091
|
-
` ${commandPrefix()} create image --model-group-code <code> --prompt "一只小狗" --dry-run
|
|
1346
|
+
helpHeading('Quick start:'),
|
|
1347
|
+
helpCommand(` ${commandPrefix()} auth status`),
|
|
1348
|
+
helpCommand(` ${commandPrefix()} auth login --access-key <access-key>`),
|
|
1349
|
+
helpCommand(` ${commandPrefix()} model image-models --model Banana`),
|
|
1350
|
+
helpCommand(` ${commandPrefix()} model input-guide`),
|
|
1351
|
+
helpCommand(` ${commandPrefix()} model options --model-group-code <code>`),
|
|
1352
|
+
helpCommand(` ${commandPrefix()} model create-spec --model-group-code <code>`),
|
|
1353
|
+
helpCommand(` ${commandPrefix()} create image --model-group-code <code> --prompt "一只小狗" --dry-run`),
|
|
1092
1354
|
'',
|
|
1093
|
-
'Command groups:',
|
|
1355
|
+
helpHeading('Command groups:'),
|
|
1094
1356
|
];
|
|
1095
1357
|
for (const [group] of groups.entries()) {
|
|
1096
|
-
lines.push(` ${group.padEnd(12)} ${GROUP_DESCRIPTIONS[group] || ''}`);
|
|
1358
|
+
lines.push(` ${helpGroup(group.padEnd(12))} ${GROUP_DESCRIPTIONS[group] || ''}`);
|
|
1097
1359
|
}
|
|
1098
1360
|
lines.push(
|
|
1099
1361
|
'',
|
|
1100
|
-
'More help:',
|
|
1101
|
-
` ${commandPrefix()} <domain> -h
|
|
1102
|
-
` ${commandPrefix()} <domain> <command> -h
|
|
1103
|
-
` ${commandPrefix()} schema -f json
|
|
1104
|
-
` ${commandPrefix()}
|
|
1362
|
+
helpHeading('More help:'),
|
|
1363
|
+
helpCommand(` ${commandPrefix()} <domain> -h`),
|
|
1364
|
+
helpCommand(` ${commandPrefix()} <domain> <command> -h`),
|
|
1365
|
+
helpCommand(` ${commandPrefix()} schema --brief -f json`),
|
|
1366
|
+
helpCommand(` ${commandPrefix()} schema -f json`),
|
|
1367
|
+
helpCommand(` ${commandPrefix()} doctor -h`),
|
|
1105
1368
|
'',
|
|
1106
|
-
'Global options:',
|
|
1107
|
-
'
|
|
1108
|
-
'
|
|
1109
|
-
'
|
|
1110
|
-
'
|
|
1369
|
+
helpHeading('Global options:'),
|
|
1370
|
+
` ${helpOption('-f, --format <format>')} 输出格式:pretty(默认)、text、json、yaml、csv、auto`,
|
|
1371
|
+
` ${helpOption('--json'.padEnd(22))} 等同于 -f json`,
|
|
1372
|
+
` ${helpOption('-h, --help'.padEnd(22))} 查看帮助`,
|
|
1373
|
+
` ${helpOption('-v, --version'.padEnd(22))} 查看版本`,
|
|
1111
1374
|
);
|
|
1112
1375
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1113
1376
|
}
|
|
@@ -1119,27 +1382,27 @@ function printGroupHelp(group, commands, version) {
|
|
|
1119
1382
|
const directItems = hasSubgroups ? items.filter((command) => !commandSubgroupTokens(command)) : [];
|
|
1120
1383
|
|
|
1121
1384
|
const lines = [
|
|
1122
|
-
`lj-awb v${version}
|
|
1385
|
+
helpTitle(`lj-awb v${version}`),
|
|
1123
1386
|
'',
|
|
1124
1387
|
GROUP_DESCRIPTIONS[group] || `${group} 命令组`,
|
|
1125
1388
|
'',
|
|
1126
|
-
'Usage:',
|
|
1389
|
+
helpHeading('Usage:'),
|
|
1127
1390
|
];
|
|
1128
1391
|
if (group === 'system') {
|
|
1129
|
-
lines.push(` ${commandPrefix()} <command> [options]`);
|
|
1392
|
+
lines.push(helpCommand(` ${commandPrefix()} <command> [options]`));
|
|
1130
1393
|
} else if (hasSubgroups) {
|
|
1131
|
-
if (directItems.length) lines.push(` ${commandPrefix()} ${group} <command> [options]`);
|
|
1132
|
-
lines.push(` ${commandPrefix()} ${group} <subdomain> <command> [options]`);
|
|
1394
|
+
if (directItems.length) lines.push(helpCommand(` ${commandPrefix()} ${group} <command> [options]`));
|
|
1395
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> <command> [options]`));
|
|
1133
1396
|
} else {
|
|
1134
|
-
lines.push(` ${commandPrefix()} ${group} <command> [options]`);
|
|
1397
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} <command> [options]`));
|
|
1135
1398
|
}
|
|
1136
1399
|
lines.push('');
|
|
1137
1400
|
if (group === 'system') {
|
|
1138
|
-
lines.push(`提示:system 是命令组;下面的命令可直接运行,例如 ${commandPrefix()} doctor。`);
|
|
1401
|
+
lines.push(helpMuted(`提示:system 是命令组;下面的命令可直接运行,例如 ${commandPrefix()} doctor。`));
|
|
1139
1402
|
} else if (hasSubgroups) {
|
|
1140
|
-
lines.push(`提示:${group} 下按子领域分组;先选子领域再看具体命令。`);
|
|
1403
|
+
lines.push(helpMuted(`提示:${group} 下按子领域分组;先选子领域再看具体命令。`));
|
|
1141
1404
|
} else {
|
|
1142
|
-
lines.push(`提示:${group} 是命令组;请选择下面的子命令运行。`);
|
|
1405
|
+
lines.push(helpMuted(`提示:${group} 是命令组;请选择下面的子命令运行。`));
|
|
1143
1406
|
}
|
|
1144
1407
|
lines.push('');
|
|
1145
1408
|
|
|
@@ -1147,43 +1410,43 @@ function printGroupHelp(group, commands, version) {
|
|
|
1147
1410
|
const presentSubgroups = new Set(
|
|
1148
1411
|
items
|
|
1149
1412
|
.map((command) => commandSubgroupTokens(command))
|
|
1150
|
-
|
|
1151
|
-
|
|
1413
|
+
.filter(Boolean)
|
|
1414
|
+
.map(([, subgroup]) => subgroup),
|
|
1152
1415
|
);
|
|
1153
|
-
lines.push('Subdomains:');
|
|
1416
|
+
lines.push(helpHeading('Subdomains:'));
|
|
1154
1417
|
for (const [subgroup, description] of Object.entries(subgroupMap)) {
|
|
1155
1418
|
if (!presentSubgroups.has(subgroup)) continue;
|
|
1156
|
-
lines.push(` ${subgroup.padEnd(10)} ${description}`);
|
|
1419
|
+
lines.push(` ${helpGroup(subgroup.padEnd(10))} ${description}`);
|
|
1157
1420
|
}
|
|
1158
1421
|
if (directItems.length) {
|
|
1159
|
-
lines.push('', 'Commands:');
|
|
1422
|
+
lines.push('', helpHeading('Commands:'));
|
|
1160
1423
|
for (const command of directItems) {
|
|
1161
1424
|
const subcommand = commandSubcommand(command);
|
|
1162
|
-
lines.push(` ${subcommand.padEnd(28)} ${summaryOf(command)}`);
|
|
1425
|
+
lines.push(` ${helpCommand(subcommand.padEnd(28))} ${summaryOf(command)}`);
|
|
1163
1426
|
}
|
|
1164
1427
|
}
|
|
1165
1428
|
} else {
|
|
1166
|
-
lines.push('Commands:');
|
|
1429
|
+
lines.push(helpHeading('Commands:'));
|
|
1167
1430
|
for (const command of items) {
|
|
1168
1431
|
const subcommand = commandSubcommand(command);
|
|
1169
|
-
lines.push(` ${subcommand.padEnd(28)} ${summaryOf(command)}`);
|
|
1432
|
+
lines.push(` ${helpCommand(subcommand.padEnd(28))} ${summaryOf(command)}`);
|
|
1170
1433
|
}
|
|
1171
1434
|
}
|
|
1172
1435
|
|
|
1173
1436
|
if (GROUP_EXAMPLES[group]?.length) {
|
|
1174
|
-
lines.push('', 'Examples:');
|
|
1437
|
+
lines.push('', helpHeading('Examples:'));
|
|
1175
1438
|
for (const example of GROUP_EXAMPLES[group]) {
|
|
1176
|
-
lines.push(` ${renderCliText(example)}`);
|
|
1439
|
+
lines.push(helpCommand(` ${renderCliText(example)}`));
|
|
1177
1440
|
}
|
|
1178
1441
|
}
|
|
1179
|
-
lines.push('', 'More help:');
|
|
1442
|
+
lines.push('', helpHeading('More help:'));
|
|
1180
1443
|
if (group === 'system') {
|
|
1181
|
-
for (const command of items) lines.push(` ${commandPrefix()} ${command.name} -h`);
|
|
1444
|
+
for (const command of items) lines.push(helpCommand(` ${commandPrefix()} ${command.name} -h`));
|
|
1182
1445
|
} else if (hasSubgroups) {
|
|
1183
|
-
lines.push(` ${commandPrefix()} ${group} <subdomain> -h`);
|
|
1184
|
-
lines.push(` ${commandPrefix()} ${group} <subdomain> <command> -h`);
|
|
1446
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> -h`));
|
|
1447
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> <command> -h`));
|
|
1185
1448
|
} else {
|
|
1186
|
-
lines.push(` ${commandPrefix()} ${group} <command> -h`);
|
|
1449
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} <command> -h`));
|
|
1187
1450
|
}
|
|
1188
1451
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1189
1452
|
}
|
|
@@ -1195,50 +1458,58 @@ function printSubgroupHelp(group, subgroup, commands, version) {
|
|
|
1195
1458
|
});
|
|
1196
1459
|
const description = SUBGROUP_DESCRIPTIONS[group]?.[subgroup] || `${group} ${subgroup} 子领域`;
|
|
1197
1460
|
const lines = [
|
|
1198
|
-
`lj-awb v${version}
|
|
1461
|
+
helpTitle(`lj-awb v${version}`),
|
|
1199
1462
|
'',
|
|
1200
1463
|
description,
|
|
1201
1464
|
'',
|
|
1202
|
-
'Usage:',
|
|
1203
|
-
` ${commandPrefix()} ${group} ${subgroup} <command> [options]
|
|
1465
|
+
helpHeading('Usage:'),
|
|
1466
|
+
helpCommand(` ${commandPrefix()} ${group} ${subgroup} <command> [options]`),
|
|
1204
1467
|
'',
|
|
1205
|
-
'Commands:',
|
|
1468
|
+
helpHeading('Commands:'),
|
|
1206
1469
|
];
|
|
1207
1470
|
for (const command of items) {
|
|
1208
1471
|
const parts = String(command.name || '').split(' ').filter(Boolean);
|
|
1209
1472
|
const tail = parts.slice(2).join(' ');
|
|
1210
|
-
lines.push(` ${tail.padEnd(28)} ${summaryOf(command)}`);
|
|
1473
|
+
lines.push(` ${helpCommand(tail.padEnd(28))} ${summaryOf(command)}`);
|
|
1211
1474
|
}
|
|
1212
1475
|
const examples = SUBGROUP_EXAMPLES[`${group} ${subgroup}`];
|
|
1213
1476
|
if (examples?.length) {
|
|
1214
|
-
lines.push('', 'Examples:');
|
|
1477
|
+
lines.push('', helpHeading('Examples:'));
|
|
1215
1478
|
for (const example of examples) {
|
|
1216
|
-
lines.push(` ${renderCliText(example)}`);
|
|
1479
|
+
lines.push(helpCommand(` ${renderCliText(example)}`));
|
|
1217
1480
|
}
|
|
1218
1481
|
}
|
|
1219
|
-
lines.push('', 'More help:');
|
|
1220
|
-
lines.push(` ${commandPrefix()} ${group} ${subgroup} <command> -h`);
|
|
1482
|
+
lines.push('', helpHeading('More help:'));
|
|
1483
|
+
lines.push(helpCommand(` ${commandPrefix()} ${group} ${subgroup} <command> -h`));
|
|
1221
1484
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1222
1485
|
}
|
|
1223
1486
|
|
|
1224
1487
|
function printCommandHelp(command) {
|
|
1225
|
-
const lines = [`Usage: ${commandPrefix()} ${command.name} [options]
|
|
1488
|
+
const lines = [helpHeading(`Usage: ${commandPrefix()} ${command.name} [options]`), '', renderCliText(command.description || '')];
|
|
1226
1489
|
if (command.args?.length) {
|
|
1227
|
-
lines.push('', 'Options:');
|
|
1490
|
+
lines.push('', helpHeading('Options:'));
|
|
1228
1491
|
for (const arg of command.args) {
|
|
1229
1492
|
const value = arg.valueName ? ` <${arg.valueName}>` : '';
|
|
1230
1493
|
const description = arg.description ? ` ${arg.description}` : '';
|
|
1231
|
-
lines.push(`
|
|
1494
|
+
lines.push(` ${helpOption(`--${arg.name}${value}`)}${description}`);
|
|
1232
1495
|
}
|
|
1233
1496
|
}
|
|
1234
|
-
lines.push(
|
|
1497
|
+
lines.push(
|
|
1498
|
+
'',
|
|
1499
|
+
helpHeading('Global options:'),
|
|
1500
|
+
` ${helpOption('-f, --format <format>')} 输出格式:pretty(默认)、text、json、yaml、csv、auto`,
|
|
1501
|
+
` ${helpOption('--json'.padEnd(22))} 等同于 -f json`,
|
|
1502
|
+
);
|
|
1235
1503
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1236
1504
|
}
|
|
1237
1505
|
|
|
1238
1506
|
function validateCommandOptions(command, kwargs, kwargRawFlags = {}) {
|
|
1239
1507
|
const allowedKeys = (command.args || []).map((arg) => normalizeKey(arg.name));
|
|
1240
1508
|
const allowed = new Set(allowedKeys);
|
|
1241
|
-
const unknown = Object.keys(kwargs).filter((key) =>
|
|
1509
|
+
const unknown = Object.keys(kwargs).filter((key) => (
|
|
1510
|
+
!allowed.has(key)
|
|
1511
|
+
&& !(command.allowDynamicModelParams && isAllowedDynamicModelParamFlag(key))
|
|
1512
|
+
));
|
|
1242
1513
|
if (!unknown.length) return;
|
|
1243
1514
|
const displayFlag = (key) => kwargRawFlags[key] || formatOptionName(key);
|
|
1244
1515
|
const suggestions = {};
|
|
@@ -1271,23 +1542,78 @@ function validateCommandOptions(command, kwargs, kwargRawFlags = {}) {
|
|
|
1271
1542
|
});
|
|
1272
1543
|
}
|
|
1273
1544
|
|
|
1545
|
+
function normalizeDynamicModelParamFlags(command, kwargs = {}, kwargRawFlags = {}) {
|
|
1546
|
+
if (!command.allowDynamicModelParams) return;
|
|
1547
|
+
const allowed = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
|
|
1548
|
+
for (const key of Object.keys(kwargs)) {
|
|
1549
|
+
if (allowed.has(key) || key.includes('_')) continue;
|
|
1550
|
+
const flag = kwargRawFlags[key] || formatOptionName(key);
|
|
1551
|
+
if (!flag.startsWith('--') || !flag.includes('-')) continue;
|
|
1552
|
+
const normalized = flag.slice(2).replaceAll('-', '_');
|
|
1553
|
+
if (!normalized || normalized === key || allowed.has(normalized)) continue;
|
|
1554
|
+
if (kwargs[normalized] === undefined) kwargs[normalized] = kwargs[key];
|
|
1555
|
+
delete kwargs[key];
|
|
1556
|
+
if (kwargRawFlags[key] && !kwargRawFlags[normalized]) kwargRawFlags[normalized] = kwargRawFlags[key];
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function isAllowedDynamicModelParamFlag(key) {
|
|
1561
|
+
return /^(generation|prompt|negative|seed|style|mode|cfg|guidance|aspect|image|video|audio|subject|output|response|media|safety|watermark|camera|motion|strength|steps)_/i.test(String(key || ''));
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1274
1564
|
function printData(data, format, meta = {}, context = {}) {
|
|
1275
|
-
|
|
1565
|
+
const outputFormat = resolveOutputFormat(format);
|
|
1566
|
+
if (outputFormat === 'json') {
|
|
1276
1567
|
process.stdout.write(`${JSON.stringify(createEnvelope({ status: 'success', data: normalizeJsonData(meta.command, data), meta }), null, 2)}\n`);
|
|
1277
1568
|
return;
|
|
1278
1569
|
}
|
|
1279
|
-
|
|
1570
|
+
if (outputFormat === 'yaml') {
|
|
1571
|
+
process.stdout.write(formatYamlEnvelope(createEnvelope({ status: 'success', data: normalizeJsonData(meta.command, data), meta })));
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
if (outputFormat === 'csv') {
|
|
1575
|
+
process.stdout.write(formatCsvOutput(meta.command, data, context));
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
if (outputFormat === 'text') {
|
|
1579
|
+
process.stdout.write(formatTextOutput(meta.command, data, context));
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
process.stdout.write(formatPrettyOutput(meta.command, data, context));
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function printUpdateNotice(updateNotice) {
|
|
1586
|
+
if (!updateNotice?.message) return;
|
|
1587
|
+
process.stderr.write(`${updateNotice.message}\n`);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
function isStructuredOutputFormat(format) {
|
|
1591
|
+
try {
|
|
1592
|
+
return ['json', 'yaml'].includes(resolveOutputFormat(format));
|
|
1593
|
+
} catch {
|
|
1594
|
+
return false;
|
|
1595
|
+
}
|
|
1280
1596
|
}
|
|
1281
1597
|
|
|
1282
1598
|
function printError(error, format, meta = {}) {
|
|
1599
|
+
let outputFormat = 'text';
|
|
1600
|
+
try {
|
|
1601
|
+
outputFormat = resolveOutputFormat(format);
|
|
1602
|
+
} catch {
|
|
1603
|
+
outputFormat = 'text';
|
|
1604
|
+
}
|
|
1283
1605
|
const payload = {
|
|
1284
1606
|
type: error.type || 'error',
|
|
1285
1607
|
message: error.message || String(error),
|
|
1286
1608
|
...(error.hint ? { hint: error.hint } : {}),
|
|
1287
1609
|
...(error.details ? { details: error.details } : {}),
|
|
1288
1610
|
};
|
|
1289
|
-
if (
|
|
1611
|
+
if (outputFormat === 'json') {
|
|
1290
1612
|
process.stderr.write(`${JSON.stringify(createEnvelope({ status: 'error', error: payload, meta }), null, 2)}\n`);
|
|
1613
|
+
} else if (outputFormat === 'yaml') {
|
|
1614
|
+
process.stderr.write(formatYamlEnvelope(createEnvelope({ status: 'error', error: payload, meta })));
|
|
1615
|
+
} else if (outputFormat === 'pretty') {
|
|
1616
|
+
process.stderr.write(formatPrettyError(payload));
|
|
1291
1617
|
} else {
|
|
1292
1618
|
process.stderr.write(formatTextError(payload));
|
|
1293
1619
|
}
|
|
@@ -1330,6 +1656,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1330
1656
|
const { commandName: rawCommandName, kwargs, kwargRawFlags, format } = parseArgv(argv);
|
|
1331
1657
|
const commandName = resolveCommandName(rawCommandName, commands);
|
|
1332
1658
|
const command = commands.find((item) => item.name === commandName);
|
|
1659
|
+
const updateNotice = await resolveUpdateNotice(argv, commandName || rawCommandName, version);
|
|
1333
1660
|
if (!command) {
|
|
1334
1661
|
if (isCommandSubgroupName(rawCommandName, commands)) {
|
|
1335
1662
|
const [group, subgroup] = rawCommandName.split(' ');
|
|
@@ -1344,18 +1671,27 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1344
1671
|
type: 'unknown_command',
|
|
1345
1672
|
exitCode: 2,
|
|
1346
1673
|
hint: unknownCommandHint(rawCommandName),
|
|
1347
|
-
}), format, {
|
|
1674
|
+
}), format, {
|
|
1675
|
+
command: rawCommandName,
|
|
1676
|
+
...(updateNotice ? { _notice: { update: updateNotice } } : {}),
|
|
1677
|
+
});
|
|
1348
1678
|
process.exitCode = 2;
|
|
1679
|
+
if (!isStructuredOutputFormat(format)) printUpdateNotice(updateNotice);
|
|
1349
1680
|
return;
|
|
1350
1681
|
}
|
|
1351
1682
|
|
|
1352
1683
|
const startedAt = Date.now();
|
|
1353
1684
|
try {
|
|
1685
|
+
const outputFormat = resolveOutputFormat(format);
|
|
1686
|
+
normalizeDynamicModelParamFlags(command, kwargs, kwargRawFlags);
|
|
1354
1687
|
validateCommandOptions(command, kwargs, kwargRawFlags);
|
|
1355
1688
|
const data = command.virtual === 'schema'
|
|
1356
1689
|
? buildCommandSchema(commands, kwargs, version)
|
|
1357
1690
|
: await command.func({ command }, kwargs);
|
|
1358
|
-
|
|
1691
|
+
const meta = { command: command.name, elapsedMs: Date.now() - startedAt };
|
|
1692
|
+
if (updateNotice) meta._notice = { update: updateNotice };
|
|
1693
|
+
printData(data, outputFormat, meta, buildOutputContext(command, kwargs, data));
|
|
1694
|
+
if (outputFormat !== 'json' && outputFormat !== 'yaml') printUpdateNotice(updateNotice);
|
|
1359
1695
|
} catch (error) {
|
|
1360
1696
|
const cliError = error instanceof LingjingAwbCliError
|
|
1361
1697
|
? error
|