@lingjingai/lj-awb-cli-pre 0.3.17 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,15 @@ 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 { formatTextError, formatTextOutput, normalizeJsonData } from './output.js';
6
+ import {
7
+ formatCsvOutput,
8
+ formatPrettyError,
9
+ formatPrettyOutput,
10
+ formatTextError,
11
+ formatTextOutput,
12
+ formatYamlEnvelope,
13
+ normalizeJsonData,
14
+ } from './output.js';
7
15
 
8
16
  function normalizeKey(value) {
9
17
  return String(value || '').replace(/-([a-z])/g, (_, char) => char.toUpperCase());
@@ -24,6 +32,7 @@ const OPTION_SYNONYMS = {
24
32
  threads: ['concurrency'],
25
33
  output: ['format'],
26
34
  json: ['format'],
35
+ keyword: ['model'],
27
36
  input: ['inputFile'],
28
37
  in: ['inputFile'],
29
38
  dry: ['dryRun'],
@@ -60,6 +69,16 @@ function suggestSimilarOptions(unknownKey, allowedKeys) {
60
69
  for (const syn of synonyms) {
61
70
  if (allowedKeys.includes(syn)) record(syn, -1);
62
71
  }
72
+ if (unknownKey.length === 1) {
73
+ const ch = unknownKey.toLowerCase();
74
+ for (const allowed of allowedKeys) {
75
+ if (allowed.toLowerCase().startsWith(ch)) record(allowed, 1);
76
+ }
77
+ return [...scored.entries()]
78
+ .sort((x, y) => x[1] - y[1])
79
+ .slice(0, 3)
80
+ .map(([key]) => formatOptionName(key));
81
+ }
63
82
  for (const allowed of allowedKeys) {
64
83
  const a = unknownKey.toLowerCase();
65
84
  const b = allowed.toLowerCase();
@@ -85,6 +104,57 @@ function renderCliText(value) {
85
104
  return String(value ?? '').replace(/\blj-awb\b/g, commandPrefix());
86
105
  }
87
106
 
107
+ function shouldColorOutput() {
108
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '0') return false;
109
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'false') return false;
110
+ if (String(process.env.FORCE_COLOR || '').trim() && process.env.FORCE_COLOR !== '0') return true;
111
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '1') return true;
112
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'true') return true;
113
+ if (process.env.NO_COLOR) return false;
114
+ return Boolean(process.stdout.isTTY);
115
+ }
116
+
117
+ function colorText(value, code) {
118
+ const text = String(value ?? '');
119
+ return shouldColorOutput() && text ? `\u001b[${code}m${text}\u001b[0m` : text;
120
+ }
121
+
122
+ const HELP_COLOR = {
123
+ title: '1;38;5;39',
124
+ accent: '1;38;5;208',
125
+ heading: '1;38;5;75',
126
+ command: '38;5;82',
127
+ group: '38;5;215',
128
+ muted: '2;38;5;245',
129
+ option: '38;5;111',
130
+ };
131
+
132
+ function helpTitle(value) {
133
+ if (!shouldColorOutput()) return String(value ?? '');
134
+ return `${colorText('◆', HELP_COLOR.accent)} ${colorText(value, HELP_COLOR.title)}`;
135
+ }
136
+
137
+ function helpHeading(value) {
138
+ if (!shouldColorOutput()) return String(value ?? '');
139
+ return `${colorText('▸', HELP_COLOR.accent)} ${colorText(value, HELP_COLOR.heading)}`;
140
+ }
141
+
142
+ function helpCommand(value) {
143
+ return colorText(value, HELP_COLOR.command);
144
+ }
145
+
146
+ function helpGroup(value) {
147
+ return colorText(value, HELP_COLOR.group);
148
+ }
149
+
150
+ function helpMuted(value) {
151
+ return colorText(value, HELP_COLOR.muted);
152
+ }
153
+
154
+ function helpOption(value) {
155
+ return colorText(value, HELP_COLOR.option);
156
+ }
157
+
88
158
  function shellArg(value) {
89
159
  const text = String(value ?? '');
90
160
  return /^[A-Za-z0-9_./:=@,+-]+$/.test(text) ? text : JSON.stringify(text);
@@ -130,6 +200,8 @@ function buildOutputContext(command, kwargs = {}) {
130
200
  : null;
131
201
  return {
132
202
  jsonCommand: `${commandInvocation(command.name, kwargs)} -f json`,
203
+ textCommand: `${commandInvocation(command.name, kwargs)} -f text`,
204
+ outputKind: buildCommandWorkflow(command).outputKind,
133
205
  executeCommand: canSuggestDryRunExecution && toBool(kwargs.dryRun) ? commandInvocation(command.name, kwargs, withoutDryRun) : null,
134
206
  confirmCommand: canSuggestDryRunExecution && toBool(kwargs.dryRun) && optionKeys.has('yes')
135
207
  ? commandInvocation(command.name, kwargs, { omit: ['dryRun'], set: { yes: true } })
@@ -138,6 +210,28 @@ function buildOutputContext(command, kwargs = {}) {
138
210
  };
139
211
  }
140
212
 
213
+ const OUTPUT_FORMAT_ALIASES = {
214
+ agent: 'text',
215
+ table: 'pretty',
216
+ human: 'pretty',
217
+ compact: 'text',
218
+ 'compact-text': 'text',
219
+ };
220
+
221
+ const SUPPORTED_OUTPUT_FORMATS = new Set(['auto', 'pretty', 'text', 'json', 'yaml', 'csv']);
222
+
223
+ function resolveOutputFormat(format) {
224
+ const raw = String(format || 'pretty').trim().toLowerCase();
225
+ const normalized = OUTPUT_FORMAT_ALIASES[raw] || raw;
226
+ if (normalized === 'auto') return process.stdout.isTTY ? 'pretty' : 'text';
227
+ if (SUPPORTED_OUTPUT_FORMATS.has(normalized)) return normalized;
228
+ throw new LingjingAwbCliError(`未知输出格式:${format}`, {
229
+ type: 'argument_error',
230
+ exitCode: 2,
231
+ hint: '可用格式:pretty、text、json、yaml、csv、auto。Agent 建议使用 -f text;严格脚本解析再用 -f json。',
232
+ });
233
+ }
234
+
141
235
  function assignKwarg(kwargs, key, value) {
142
236
  if (kwargs[key] === undefined) {
143
237
  kwargs[key] = value;
@@ -153,7 +247,8 @@ function assignKwarg(kwargs, key, value) {
153
247
  function parseArgv(argv) {
154
248
  const commandParts = [];
155
249
  const kwargs = {};
156
- let format = 'text';
250
+ const kwargRawFlags = {};
251
+ let format = 'pretty';
157
252
 
158
253
  for (let index = 0; index < argv.length; index += 1) {
159
254
  const token = argv[index];
@@ -182,10 +277,27 @@ function parseArgv(argv) {
182
277
  }
183
278
  continue;
184
279
  }
280
+ if (token.length > 1 && token.startsWith('-') && /^-[A-Za-z]/.test(token)) {
281
+ const raw = token.slice(1);
282
+ const eqIndex = raw.indexOf('=');
283
+ const keyPart = eqIndex >= 0 ? raw.slice(0, eqIndex) : raw;
284
+ const inlineValue = eqIndex >= 0 ? raw.slice(eqIndex + 1) : undefined;
285
+ const key = normalizeKey(keyPart);
286
+ if (!(key in kwargRawFlags)) kwargRawFlags[key] = token;
287
+ if (inlineValue !== undefined) {
288
+ assignKwarg(kwargs, key, inlineValue);
289
+ } else if (argv[index + 1] && !argv[index + 1].startsWith('-')) {
290
+ assignKwarg(kwargs, key, argv[index + 1]);
291
+ index += 1;
292
+ } else {
293
+ assignKwarg(kwargs, key, true);
294
+ }
295
+ continue;
296
+ }
185
297
  commandParts.push(token);
186
298
  }
187
299
 
188
- return { commandName: commandParts.join(' '), kwargs, format };
300
+ return { commandName: commandParts.join(' '), kwargs, kwargRawFlags, format };
189
301
  }
190
302
 
191
303
  async function readVersion() {
@@ -203,13 +315,15 @@ const VIRTUAL_COMMANDS = [
203
315
  '输出机器可读命令 schema,供 Agent / 脚本理解命令、参数和示例',
204
316
  '',
205
317
  'Examples:',
318
+ ' lj-awb schema --brief -f json',
206
319
  ' lj-awb schema -f json',
207
320
  ' lj-awb schema --domain create -f json',
208
321
  ' lj-awb schema --domain create --command image -f json',
209
322
  '',
210
- 'Hint: Agent 应优先读取 schema,而不是解析自然语言 help。',
323
+ 'Hint: Agent 先读 --brief 掌握能力地图;执行具体命令前再读精确 schema。',
211
324
  ].join('\n'),
212
325
  args: [
326
+ { name: 'brief', description: '输出面向 Agent 的轻量能力摘要,不展开每个参数' },
213
327
  { name: 'domain', valueName: 'name', description: '按命令域过滤,如 create / task / artifact / system' },
214
328
  { name: 'command', valueName: 'name', description: '按子命令过滤,如 image / video / wait / doctor' },
215
329
  ],
@@ -366,6 +480,7 @@ const GROUP_EXAMPLES = {
366
480
  system: [
367
481
  'lj-awb doctor',
368
482
  'lj-awb doctor --verify',
483
+ 'lj-awb schema --brief -f json',
369
484
  'lj-awb schema -f json',
370
485
  'lj-awb schema --domain create -f json',
371
486
  ],
@@ -812,6 +927,23 @@ function buildCommandWorkflow(command) {
812
927
  if (command.name === 'create asset') {
813
928
  next.push('读取 data.assetPath', '若后端返回审核 taskId,再运行 task wait --task-type ASSET_REGISTER');
814
929
  }
930
+ if (command.name === 'model image-models' || command.name === 'model video-models') {
931
+ next.push('读取 data.models[] 得到全部候选;用户给了模型口语名时保留同族全部候选,不只取第一个');
932
+ next.push('对每个候选运行 model options --model-group-code <modelGroupCode> 查看真实参数和素材约束');
933
+ next.push('向用户展示候选模型 + 参数取值 + 资源能力后 STOP;等待用户选择或明确授权默认,未确认不得进入 create-spec/fee/dry-run/create');
934
+ }
935
+ if (command.name === 'model asset-review-models') {
936
+ next.push('读取 data.models[].platform', '用 create asset-groups --platform <platform> 查重或创建素材组');
937
+ }
938
+ if (command.name === 'model options') {
939
+ next.push('读取 params/resources/constraints 确认参数与素材约束', '运行 model create-spec --model-group-code <code> 查看创建写法和示例');
940
+ }
941
+ if (command.name === 'model create-spec') {
942
+ next.push('按 examples / supportedIntents 组装命令', '用户确认关键参数后先运行 create image-fee 或 create video-fee,再 dry-run');
943
+ }
944
+ if (command.name === 'model input-guide') {
945
+ next.push('运行 model image-models 或 model video-models 选择模型', '选定模型后运行 model options --model-group-code <code>');
946
+ }
815
947
  if (['create image-batch', 'create video-batch', 'create subject-batch'].includes(command.name)) {
816
948
  next.push('读取每项 status / taskId / error', '使用 task record-poll 或对应 wait 命令恢复批量结果');
817
949
  }
@@ -822,11 +954,167 @@ function buildCommandWorkflow(command) {
822
954
  };
823
955
  }
824
956
 
957
+ function buildDomainSummaries(commands) {
958
+ const domains = new Map();
959
+ for (const command of commands) {
960
+ const group = commandGroup(command);
961
+ domains.set(group, {
962
+ name: group,
963
+ description: GROUP_DESCRIPTIONS[group] || '',
964
+ commandCount: (domains.get(group)?.commandCount || 0) + 1,
965
+ });
966
+ }
967
+ return Array.from(domains.values()).sort((a, b) => a.name.localeCompare(b.name));
968
+ }
969
+
970
+ function requiredOptionsForCommand(command) {
971
+ const requiredOptions = new Set();
972
+ if (commandGroup(command) === 'artifact') requiredOptions.add('projectId');
973
+ for (const key of COMMAND_REQUIRED_OPTIONS[command.name] || []) requiredOptions.add(key);
974
+ return [...requiredOptions];
975
+ }
976
+
977
+ function requiredAnyOptionsForCommand(command) {
978
+ return COMMAND_REQUIRED_ANY_OPTIONS[command.name] || [];
979
+ }
980
+
981
+ function buildCommandSchemaEntry(command) {
982
+ const requiredOptions = requiredOptionsForCommand(command);
983
+ const requiredAnyOptions = requiredAnyOptionsForCommand(command);
984
+ const optionKeys = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
985
+ return {
986
+ name: command.name,
987
+ domain: commandGroup(command),
988
+ command: commandSubcommand(command),
989
+ invocation: `${commandPrefix()} ${command.name}`,
990
+ helpCommand: `${commandPrefix()} ${command.name} -h`,
991
+ summary: summaryOf(command),
992
+ description: renderCliText(summaryOf(command)),
993
+ examples: extractExamples(command).map(renderCliText),
994
+ jsonExamples: extractExamples(command).map(asJsonExample),
995
+ requiredOptions,
996
+ requiredAnyOptions,
997
+ safety: buildCommandSafety(command, optionKeys),
998
+ workflow: buildCommandWorkflow(command),
999
+ options: (command.args || []).map((arg) => {
1000
+ const key = normalizeKey(arg.name);
1001
+ return {
1002
+ name: arg.name,
1003
+ key,
1004
+ flag: formatOptionName(arg.name),
1005
+ valueName: arg.valueName || null,
1006
+ required: requiredOptions.includes(key),
1007
+ requiredAnyGroup: requiredAnyOptions.find((group) => group.includes(key)) || null,
1008
+ description: arg.description || '',
1009
+ };
1010
+ }),
1011
+ };
1012
+ }
1013
+
1014
+ function namesWhere(entries, predicate) {
1015
+ return entries.filter(predicate).map((entry) => entry.name);
1016
+ }
1017
+
1018
+ function buildAgentBrief(commands, version = 'unknown') {
1019
+ const contract = buildAgentContract();
1020
+ const entries = commands.map(buildCommandSchemaEntry);
1021
+ const domains = buildDomainSummaries(commands).map((domain) => ({
1022
+ ...domain,
1023
+ commands: entries
1024
+ .filter((entry) => entry.domain === domain.name)
1025
+ .map((entry) => entry.name),
1026
+ }));
1027
+ return {
1028
+ schemaVersion: 1,
1029
+ kind: 'agent_brief',
1030
+ cli: {
1031
+ name: 'lj-awb',
1032
+ version,
1033
+ commandPrefix: commandPrefix(),
1034
+ },
1035
+ commandCount: entries.length,
1036
+ domains,
1037
+ outputPolicy: {
1038
+ agentRecommendedOutputFormat: contract.agentRecommendedOutputFormat,
1039
+ scriptRecommendedOutputFormat: contract.scriptRecommendedOutputFormat,
1040
+ textFlag: contract.textFlag,
1041
+ jsonFlag: contract.jsonFlag,
1042
+ policy: contract.outputPolicy,
1043
+ },
1044
+ statePolicy: contract.statePolicy,
1045
+ workflowPolicy: contract.workflowPolicy,
1046
+ safetyPolicy: {
1047
+ confirmationPolicy: contract.confirmationPolicy,
1048
+ dryRunPolicy: contract.dryRunPolicy,
1049
+ remoteWriteCount: namesWhere(entries, (entry) => entry.safety.remoteWrite).length,
1050
+ costsPointsCount: namesWhere(entries, (entry) => entry.safety.costsPoints).length,
1051
+ localStateWriteCount: namesWhere(entries, (entry) => entry.safety.localStateWrite).length,
1052
+ destructiveCount: namesWhere(entries, (entry) => entry.safety.destructive).length,
1053
+ longRunningCount: namesWhere(entries, (entry) => entry.safety.longRunning).length,
1054
+ },
1055
+ commandClasses: {
1056
+ costsPoints: namesWhere(entries, (entry) => entry.safety.costsPoints),
1057
+ remoteWrite: namesWhere(entries, (entry) => entry.safety.remoteWrite),
1058
+ localStateWrite: namesWhere(entries, (entry) => entry.safety.localStateWrite),
1059
+ destructive: namesWhere(entries, (entry) => entry.safety.destructive),
1060
+ longRunning: namesWhere(entries, (entry) => entry.safety.longRunning),
1061
+ safeRead: namesWhere(entries, (entry) => entry.safety.safeToAutoRun && entry.safety.network !== 'none'),
1062
+ offlineRead: namesWhere(entries, (entry) => entry.safety.safeToAutoRun && entry.safety.network === 'none'),
1063
+ },
1064
+ routing: [
1065
+ {
1066
+ intent: '环境、认证、账号、团队、项目组、积分',
1067
+ start: ['doctor --verify', 'account info', 'project current', 'credits balance'],
1068
+ rule: '只读查询可自动运行;切换团队、切换/创建/更新项目组和清空认证按 safety dry-run/confirm 处理。',
1069
+ },
1070
+ {
1071
+ intent: '模型发现和创作任务',
1072
+ start: ['model image-models', 'model video-models', 'model options', 'model create-spec'],
1073
+ rule: '先展示候选模型、真实参数和资源能力并 STOP;用户选择或明确授权默认后,关键参数确认,再只跑一次 fee、dry-run 和最终 --yes。',
1074
+ },
1075
+ {
1076
+ intent: '上传、主体、音色、素材加白',
1077
+ start: ['upload files', 'create subject-list', 'create subject-voice-list', 'model asset-review-models'],
1078
+ rule: '复用已上传 backendPath、已有主体/音色/素材组;素材加白平台来自 model asset-review-models 或用户明确选择。',
1079
+ },
1080
+ {
1081
+ intent: '异步任务和批量恢复',
1082
+ start: ['task wait', 'task record-poll', 'task records'],
1083
+ rule: '优先复用 create 返回的 nextCommand;批量任务使用 task-record-file,失败项单独重试。',
1084
+ },
1085
+ {
1086
+ intent: '最终产物 artifact',
1087
+ start: ['artifact script', 'artifact asset', 'artifact video', 'artifact clip'],
1088
+ rule: 'artifact 使用 project-id,不是 project-group-no;写入/删除/导入先 dry-run,再一次性确认写入面。',
1089
+ },
1090
+ ],
1091
+ lookup: {
1092
+ preciseSchema: `${commandPrefix()} schema --domain <domain> --command <command> -f json`,
1093
+ fullSchema: `${commandPrefix()} schema -f json`,
1094
+ commandHelp: `${commandPrefix()} <domain> <command> -h`,
1095
+ modelInputGuide: contract.modelInputGuide.textCommand,
1096
+ schemaPolicy: contract.schemaPolicy,
1097
+ },
1098
+ canonicalFields: contract.canonicalFields,
1099
+ resourceShortcut: contract.resourceShortcut,
1100
+ resourcesJson: contract.resourcesJson,
1101
+ modelCandidatePresentation: contract.modelCandidatePresentation,
1102
+ taskTypes: contract.taskTypes,
1103
+ exitCodes: contract.exitCodes,
1104
+ };
1105
+ }
1106
+
825
1107
  function buildAgentContract() {
826
1108
  return {
827
- defaultOutputFormat: 'compact-text',
1109
+ defaultOutputFormat: 'pretty',
1110
+ humanDefaultOutputFormat: 'pretty',
1111
+ agentRecommendedOutputFormat: 'text',
1112
+ agentCompactOutputFormat: 'text',
1113
+ scriptRecommendedOutputFormat: 'json',
828
1114
  jsonFlag: '-f json',
829
- outputPolicy: '默认输出精简 key=value 文本,只保留决策字段;需要稳定 JSON、嵌套结构或脚本严格解析时显式传 -f json 或 --json。JSON envelope 字段已做归一化,见 canonicalFields。',
1115
+ textFlag: '-f text',
1116
+ outputFormats: ['pretty', 'text', 'json', 'yaml', 'csv', 'auto'],
1117
+ outputPolicy: '默认输出 pretty 人类友好视图,包含标题、摘要表格、列表表格和下一步建议;Agent 推荐显式传 -f text 读取稳定的分区 key=value 输出;脚本严格解析或需要完整嵌套结构时再传 -f json 或 --json。JSON envelope 字段已做归一化,见 canonicalFields。',
830
1118
  statePolicy: {
831
1119
  purpose: 'Agent 应在同一轮 / 同一项目中维护 AWB 上下文账本,减少重复查询和重复提交。',
832
1120
  cacheKeys: [
@@ -844,6 +1132,7 @@ function buildAgentContract() {
844
1132
  invalidation: '用户切换账号、团队、项目组、模型、素材、prompt 或关键参数后,只刷新受影响的缓存;不要重复跑未变化的 model options / create-spec / fee。',
845
1133
  },
846
1134
  workflowPolicy: [
1135
+ '模型口语名命中后必须先展示候选模型、真实参数取值和资源能力,并在清单后 STOP 等用户选择或明确授权默认;没有完成用户可见候选清单和确认前,不得代选默认模型 / 参数,也不得进入 create-spec、fee、dry-run 或 create。',
847
1136
  '模型探索阶段只读 model list / options / create-spec;fee 只在用户确认关键参数后跑一次。',
848
1137
  'supportsDryRun=true 的写入 / 扣费命令先 dry-run,确认后 yes;不要把 dry-run 当参数探索工具反复跑。',
849
1138
  '用户给出多条同模型同参数任务时优先 batch + task-record-file,不要单条循环 create。',
@@ -862,7 +1151,7 @@ function buildAgentContract() {
862
1151
  { canonical: 'pointCost', aliases: ['points', 'point', 'costPoint', 'estimatePoint'], meaning: '本次任务预计 / 实际消耗积分' },
863
1152
  ],
864
1153
  },
865
- schemaPolicy: 'schema 是机器契约,只给摘要、参数 key、安全规则和示例;查看长帮助用 schema.commands[].helpCommand 或 lj-awb <domain> <command> -h。',
1154
+ schemaPolicy: 'Agent 先读 schema --brief 建立能力地图;执行具体命令前再读精确 schema --domain <domain> --command <command>。完整 schema -f json 只用于覆盖校验、脚本生成或调试 schema 本身;查看长帮助用 schema.commands[].helpCommand 或 lj-awb <domain> <command> -h。',
866
1155
  confirmationPolicy: 'schema.commands[].safety.requiresConfirmation=true 的命令,Agent 必须先向用户确认,再追加 --yes。',
867
1156
  dryRunPolicy: 'schema.commands[].safety.supportsDryRun=true 的写入 / 扣费命令,正式执行前优先跑 --dry-run。',
868
1157
  resourceShortcut: {
@@ -886,24 +1175,36 @@ function buildAgentContract() {
886
1175
  },
887
1176
  ],
888
1177
  },
1178
+ modelCandidatePresentation: {
1179
+ purpose: '把 model image-models / video-models 与每个候选的 model options 转成用户可见清单,避免 Agent 内部看完 options 后直接代选。',
1180
+ trigger: '用户问有哪些模型、给出口语名、或模型列表返回候选时都触发;即使只有一个候选也要展示真实可选项。',
1181
+ gateType: 'hard_stop',
1182
+ stopAfterPresentation: true,
1183
+ resumeCondition: '用户选择模型和关键参数,或明确说“按默认 / 你来选”。',
1184
+ requiredVisibleFields: ['displayName', 'modelDesc', 'taskQueueNum', 'quality values/default', 'ratio values/default', 'duration or generateNum values/default', 'resource modes/media/usages', 'channel or fast/pro differences'],
1185
+ 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'],
1186
+ },
889
1187
  modelOptions: {
890
1188
  command: `${commandPrefix()} model options --model-group-code <code>`,
1189
+ textCommand: `${commandPrefix()} model options --model-group-code <code> -f text`,
891
1190
  jsonCommand: `${commandPrefix()} model options --model-group-code <code> -f json`,
892
- purpose: '查询指定模型支持的 CLI 参数、枚举值、默认值、素材约束和条件约束。',
1191
+ purpose: '查询指定模型支持的 CLI 参数、枚举值、默认值、素材约束和条件约束。Agent 默认读 textCommand;只有程序化校验完整嵌套结构时读 jsonCommand。',
893
1192
  useBefore: ['model create-spec', 'create image-fee', 'create image', 'create video-fee', 'create video'],
894
1193
  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'],
895
1194
  },
896
1195
  modelCreateSpec: {
897
1196
  command: `${commandPrefix()} model create-spec --model-group-code <code>`,
1197
+ textCommand: `${commandPrefix()} model create-spec --model-group-code <code> -f text`,
898
1198
  jsonCommand: `${commandPrefix()} model create-spec --model-group-code <code> -f json`,
899
- purpose: '查看指定模型如何组织 create 命令:输入模式、素材绑定规则、示例和创建前检查项。',
1199
+ purpose: '查看指定模型如何组织 create 命令:输入模式、素材绑定规则、示例和创建前检查项。Agent 默认读 textCommand;只有程序化校验完整嵌套结构时读 jsonCommand。',
900
1200
  useBefore: ['create image-fee', 'create image', 'create video-fee', 'create video'],
901
1201
  keyFields: ['inputRequirement', 'supportedIntents', 'validationRules', 'agentGuidance', 'preflight', 'examples'],
902
1202
  },
903
1203
  modelInputGuide: {
904
1204
  command: `${commandPrefix()} model input-guide`,
1205
+ textCommand: `${commandPrefix()} model input-guide -f text`,
905
1206
  jsonCommand: `${commandPrefix()} model input-guide -f json`,
906
- purpose: '查看跨模型统一创建参数、resources 字段和素材绑定规则。',
1207
+ purpose: '查看跨模型统一创建参数、resources 字段和素材绑定规则。Agent 默认读 textCommand;脚本严格解析时读 jsonCommand。',
907
1208
  useBefore: ['model options', 'model create-spec'],
908
1209
  },
909
1210
  taskTypes: {
@@ -929,17 +1230,10 @@ function buildAgentContract() {
929
1230
  }
930
1231
 
931
1232
  function buildCommandSchema(commands, kwargs = {}, version = 'unknown') {
1233
+ if (toBool(kwargs.brief)) return buildAgentBrief(commands, version);
932
1234
  const domainFilter = String(kwargs.domain ?? '').trim();
933
1235
  const commandFilter = String(kwargs.command ?? '').trim();
934
- const domains = new Map();
935
- for (const command of commands) {
936
- const group = commandGroup(command);
937
- domains.set(group, {
938
- name: group,
939
- description: GROUP_DESCRIPTIONS[group] || '',
940
- commandCount: (domains.get(group)?.commandCount || 0) + 1,
941
- });
942
- }
1236
+ const domains = buildDomainSummaries(commands);
943
1237
  const filteredCommands = commands
944
1238
  .filter((command) => !domainFilter || commandGroup(command) === domainFilter)
945
1239
  .filter((command) => !commandFilter || command.name === commandFilter || commandSubcommand(command) === commandFilter);
@@ -963,49 +1257,16 @@ function buildCommandSchema(commands, kwargs = {}, version = 'unknown') {
963
1257
  `${commandPrefix()} <domain> <command> -h`,
964
1258
  ],
965
1259
  globalOptions: [
966
- { flag: '-f, --format json', key: 'format', description: '输出 JSON;默认不传时输出精简文本' },
1260
+ { flag: '-f, --format <format>', key: 'format', description: '输出格式:pretty(默认)、text、json、yaml、csv、auto' },
967
1261
  { flag: '--json', key: 'format', value: 'json', description: '等同于 -f json' },
968
1262
  { flag: '-h, --help', key: 'help', description: '查看帮助' },
969
1263
  { flag: '-v, --version', key: 'version', description: '查看版本' },
970
1264
  ],
971
1265
  agentContract: buildAgentContract(),
972
- domains: Array.from(domains.values())
1266
+ domains: domains
973
1267
  .filter((domain) => !shouldFilterDomains || matchedDomains.has(domain.name))
974
1268
  .sort((a, b) => a.name.localeCompare(b.name)),
975
- commands: filteredCommands.map((command) => {
976
- const requiredOptions = new Set();
977
- if (commandGroup(command) === 'artifact') requiredOptions.add('projectId');
978
- for (const key of COMMAND_REQUIRED_OPTIONS[command.name] || []) requiredOptions.add(key);
979
- const requiredAnyOptions = COMMAND_REQUIRED_ANY_OPTIONS[command.name] || [];
980
- const optionKeys = new Set((command.args || []).map((arg) => normalizeKey(arg.name)));
981
- return {
982
- name: command.name,
983
- domain: commandGroup(command),
984
- command: commandSubcommand(command),
985
- invocation: `${commandPrefix()} ${command.name}`,
986
- helpCommand: `${commandPrefix()} ${command.name} -h`,
987
- summary: summaryOf(command),
988
- description: renderCliText(summaryOf(command)),
989
- examples: extractExamples(command).map(renderCliText),
990
- jsonExamples: extractExamples(command).map(asJsonExample),
991
- requiredOptions: [...requiredOptions],
992
- requiredAnyOptions,
993
- safety: buildCommandSafety(command, optionKeys),
994
- workflow: buildCommandWorkflow(command),
995
- options: (command.args || []).map((arg) => {
996
- const key = normalizeKey(arg.name);
997
- return {
998
- name: arg.name,
999
- key,
1000
- flag: formatOptionName(arg.name),
1001
- valueName: arg.valueName || null,
1002
- required: requiredOptions.has(key),
1003
- requiredAnyGroup: requiredAnyOptions.find((group) => group.includes(key)) || null,
1004
- description: arg.description || '',
1005
- };
1006
- }),
1007
- };
1008
- }),
1269
+ commands: filteredCommands.map(buildCommandSchemaEntry),
1009
1270
  };
1010
1271
  }
1011
1272
 
@@ -1018,43 +1279,44 @@ function printRootHelp(commands, version) {
1018
1279
  }
1019
1280
 
1020
1281
  const lines = [
1021
- `lj-awb v${version}`,
1282
+ helpTitle(`lj-awb v${version}`),
1022
1283
  '',
1023
- '灵境 AWB 命令行工具。默认输出精简文本;需要 JSON 时传 -f json。',
1284
+ '灵境 AWB 命令行工具。默认输出人类友好的 pretty 视图;Agent 建议显式传 -f text,脚本严格解析用 -f json。',
1024
1285
  '',
1025
- 'Usage:',
1026
- ` ${commandPrefix()} <domain> <command> [options]`,
1027
- ` ${commandPrefix()} <command> [options]`,
1028
- ` ${commandPrefix()} <domain> -h`,
1029
- ` ${commandPrefix()} <domain> <command> -h`,
1286
+ helpHeading('Usage:'),
1287
+ helpCommand(` ${commandPrefix()} <domain> <command> [options]`),
1288
+ helpCommand(` ${commandPrefix()} <command> [options]`),
1289
+ helpCommand(` ${commandPrefix()} <domain> -h`),
1290
+ helpCommand(` ${commandPrefix()} <domain> <command> -h`),
1030
1291
  '',
1031
- 'Quick start:',
1032
- ` ${commandPrefix()} auth status`,
1033
- ` ${commandPrefix()} auth login --access-key <access-key>`,
1034
- ` ${commandPrefix()} model image-models --model Banana`,
1035
- ` ${commandPrefix()} model input-guide`,
1036
- ` ${commandPrefix()} model options --model-group-code <code>`,
1037
- ` ${commandPrefix()} model create-spec --model-group-code <code>`,
1038
- ` ${commandPrefix()} create image --model-group-code <code> --prompt "一只小狗" --dry-run`,
1292
+ helpHeading('Quick start:'),
1293
+ helpCommand(` ${commandPrefix()} auth status`),
1294
+ helpCommand(` ${commandPrefix()} auth login --access-key <access-key>`),
1295
+ helpCommand(` ${commandPrefix()} model image-models --model Banana`),
1296
+ helpCommand(` ${commandPrefix()} model input-guide`),
1297
+ helpCommand(` ${commandPrefix()} model options --model-group-code <code>`),
1298
+ helpCommand(` ${commandPrefix()} model create-spec --model-group-code <code>`),
1299
+ helpCommand(` ${commandPrefix()} create image --model-group-code <code> --prompt "一只小狗" --dry-run`),
1039
1300
  '',
1040
- 'Command groups:',
1301
+ helpHeading('Command groups:'),
1041
1302
  ];
1042
1303
  for (const [group] of groups.entries()) {
1043
- lines.push(` ${group.padEnd(12)} ${GROUP_DESCRIPTIONS[group] || ''}`);
1304
+ lines.push(` ${helpGroup(group.padEnd(12))} ${GROUP_DESCRIPTIONS[group] || ''}`);
1044
1305
  }
1045
1306
  lines.push(
1046
1307
  '',
1047
- 'More help:',
1048
- ` ${commandPrefix()} <domain> -h`,
1049
- ` ${commandPrefix()} <domain> <command> -h`,
1050
- ` ${commandPrefix()} schema -f json`,
1051
- ` ${commandPrefix()} doctor -h`,
1308
+ helpHeading('More help:'),
1309
+ helpCommand(` ${commandPrefix()} <domain> -h`),
1310
+ helpCommand(` ${commandPrefix()} <domain> <command> -h`),
1311
+ helpCommand(` ${commandPrefix()} schema --brief -f json`),
1312
+ helpCommand(` ${commandPrefix()} schema -f json`),
1313
+ helpCommand(` ${commandPrefix()} doctor -h`),
1052
1314
  '',
1053
- 'Global options:',
1054
- ' -f, --format json 输出 JSON;默认不传时输出精简文本',
1055
- ' --json 等同于 -f json',
1056
- ' -h, --help 查看帮助',
1057
- ' -v, --version 查看版本',
1315
+ helpHeading('Global options:'),
1316
+ ` ${helpOption('-f, --format <format>')} 输出格式:pretty(默认)、text、json、yaml、csv、auto`,
1317
+ ` ${helpOption('--json'.padEnd(22))} 等同于 -f json`,
1318
+ ` ${helpOption('-h, --help'.padEnd(22))} 查看帮助`,
1319
+ ` ${helpOption('-v, --version'.padEnd(22))} 查看版本`,
1058
1320
  );
1059
1321
  process.stdout.write(`${lines.join('\n')}\n`);
1060
1322
  }
@@ -1066,27 +1328,27 @@ function printGroupHelp(group, commands, version) {
1066
1328
  const directItems = hasSubgroups ? items.filter((command) => !commandSubgroupTokens(command)) : [];
1067
1329
 
1068
1330
  const lines = [
1069
- `lj-awb v${version}`,
1331
+ helpTitle(`lj-awb v${version}`),
1070
1332
  '',
1071
1333
  GROUP_DESCRIPTIONS[group] || `${group} 命令组`,
1072
1334
  '',
1073
- 'Usage:',
1335
+ helpHeading('Usage:'),
1074
1336
  ];
1075
1337
  if (group === 'system') {
1076
- lines.push(` ${commandPrefix()} <command> [options]`);
1338
+ lines.push(helpCommand(` ${commandPrefix()} <command> [options]`));
1077
1339
  } else if (hasSubgroups) {
1078
- if (directItems.length) lines.push(` ${commandPrefix()} ${group} <command> [options]`);
1079
- lines.push(` ${commandPrefix()} ${group} <subdomain> <command> [options]`);
1340
+ if (directItems.length) lines.push(helpCommand(` ${commandPrefix()} ${group} <command> [options]`));
1341
+ lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> <command> [options]`));
1080
1342
  } else {
1081
- lines.push(` ${commandPrefix()} ${group} <command> [options]`);
1343
+ lines.push(helpCommand(` ${commandPrefix()} ${group} <command> [options]`));
1082
1344
  }
1083
1345
  lines.push('');
1084
1346
  if (group === 'system') {
1085
- lines.push(`提示:system 是命令组;下面的命令可直接运行,例如 ${commandPrefix()} doctor。`);
1347
+ lines.push(helpMuted(`提示:system 是命令组;下面的命令可直接运行,例如 ${commandPrefix()} doctor。`));
1086
1348
  } else if (hasSubgroups) {
1087
- lines.push(`提示:${group} 下按子领域分组;先选子领域再看具体命令。`);
1349
+ lines.push(helpMuted(`提示:${group} 下按子领域分组;先选子领域再看具体命令。`));
1088
1350
  } else {
1089
- lines.push(`提示:${group} 是命令组;请选择下面的子命令运行。`);
1351
+ lines.push(helpMuted(`提示:${group} 是命令组;请选择下面的子命令运行。`));
1090
1352
  }
1091
1353
  lines.push('');
1092
1354
 
@@ -1094,43 +1356,43 @@ function printGroupHelp(group, commands, version) {
1094
1356
  const presentSubgroups = new Set(
1095
1357
  items
1096
1358
  .map((command) => commandSubgroupTokens(command))
1097
- .filter(Boolean)
1098
- .map(([, subgroup]) => subgroup),
1359
+ .filter(Boolean)
1360
+ .map(([, subgroup]) => subgroup),
1099
1361
  );
1100
- lines.push('Subdomains:');
1362
+ lines.push(helpHeading('Subdomains:'));
1101
1363
  for (const [subgroup, description] of Object.entries(subgroupMap)) {
1102
1364
  if (!presentSubgroups.has(subgroup)) continue;
1103
- lines.push(` ${subgroup.padEnd(10)} ${description}`);
1365
+ lines.push(` ${helpGroup(subgroup.padEnd(10))} ${description}`);
1104
1366
  }
1105
1367
  if (directItems.length) {
1106
- lines.push('', 'Commands:');
1368
+ lines.push('', helpHeading('Commands:'));
1107
1369
  for (const command of directItems) {
1108
1370
  const subcommand = commandSubcommand(command);
1109
- lines.push(` ${subcommand.padEnd(28)} ${summaryOf(command)}`);
1371
+ lines.push(` ${helpCommand(subcommand.padEnd(28))} ${summaryOf(command)}`);
1110
1372
  }
1111
1373
  }
1112
1374
  } else {
1113
- lines.push('Commands:');
1375
+ lines.push(helpHeading('Commands:'));
1114
1376
  for (const command of items) {
1115
1377
  const subcommand = commandSubcommand(command);
1116
- lines.push(` ${subcommand.padEnd(28)} ${summaryOf(command)}`);
1378
+ lines.push(` ${helpCommand(subcommand.padEnd(28))} ${summaryOf(command)}`);
1117
1379
  }
1118
1380
  }
1119
1381
 
1120
1382
  if (GROUP_EXAMPLES[group]?.length) {
1121
- lines.push('', 'Examples:');
1383
+ lines.push('', helpHeading('Examples:'));
1122
1384
  for (const example of GROUP_EXAMPLES[group]) {
1123
- lines.push(` ${renderCliText(example)}`);
1385
+ lines.push(helpCommand(` ${renderCliText(example)}`));
1124
1386
  }
1125
1387
  }
1126
- lines.push('', 'More help:');
1388
+ lines.push('', helpHeading('More help:'));
1127
1389
  if (group === 'system') {
1128
- for (const command of items) lines.push(` ${commandPrefix()} ${command.name} -h`);
1390
+ for (const command of items) lines.push(helpCommand(` ${commandPrefix()} ${command.name} -h`));
1129
1391
  } else if (hasSubgroups) {
1130
- lines.push(` ${commandPrefix()} ${group} <subdomain> -h`);
1131
- lines.push(` ${commandPrefix()} ${group} <subdomain> <command> -h`);
1392
+ lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> -h`));
1393
+ lines.push(helpCommand(` ${commandPrefix()} ${group} <subdomain> <command> -h`));
1132
1394
  } else {
1133
- lines.push(` ${commandPrefix()} ${group} <command> -h`);
1395
+ lines.push(helpCommand(` ${commandPrefix()} ${group} <command> -h`));
1134
1396
  }
1135
1397
  process.stdout.write(`${lines.join('\n')}\n`);
1136
1398
  }
@@ -1142,90 +1404,127 @@ function printSubgroupHelp(group, subgroup, commands, version) {
1142
1404
  });
1143
1405
  const description = SUBGROUP_DESCRIPTIONS[group]?.[subgroup] || `${group} ${subgroup} 子领域`;
1144
1406
  const lines = [
1145
- `lj-awb v${version}`,
1407
+ helpTitle(`lj-awb v${version}`),
1146
1408
  '',
1147
1409
  description,
1148
1410
  '',
1149
- 'Usage:',
1150
- ` ${commandPrefix()} ${group} ${subgroup} <command> [options]`,
1411
+ helpHeading('Usage:'),
1412
+ helpCommand(` ${commandPrefix()} ${group} ${subgroup} <command> [options]`),
1151
1413
  '',
1152
- 'Commands:',
1414
+ helpHeading('Commands:'),
1153
1415
  ];
1154
1416
  for (const command of items) {
1155
1417
  const parts = String(command.name || '').split(' ').filter(Boolean);
1156
1418
  const tail = parts.slice(2).join(' ');
1157
- lines.push(` ${tail.padEnd(28)} ${summaryOf(command)}`);
1419
+ lines.push(` ${helpCommand(tail.padEnd(28))} ${summaryOf(command)}`);
1158
1420
  }
1159
1421
  const examples = SUBGROUP_EXAMPLES[`${group} ${subgroup}`];
1160
1422
  if (examples?.length) {
1161
- lines.push('', 'Examples:');
1423
+ lines.push('', helpHeading('Examples:'));
1162
1424
  for (const example of examples) {
1163
- lines.push(` ${renderCliText(example)}`);
1425
+ lines.push(helpCommand(` ${renderCliText(example)}`));
1164
1426
  }
1165
1427
  }
1166
- lines.push('', 'More help:');
1167
- lines.push(` ${commandPrefix()} ${group} ${subgroup} <command> -h`);
1428
+ lines.push('', helpHeading('More help:'));
1429
+ lines.push(helpCommand(` ${commandPrefix()} ${group} ${subgroup} <command> -h`));
1168
1430
  process.stdout.write(`${lines.join('\n')}\n`);
1169
1431
  }
1170
1432
 
1171
1433
  function printCommandHelp(command) {
1172
- const lines = [`Usage: ${commandPrefix()} ${command.name} [options]`, '', renderCliText(command.description || '')];
1434
+ const lines = [helpHeading(`Usage: ${commandPrefix()} ${command.name} [options]`), '', renderCliText(command.description || '')];
1173
1435
  if (command.args?.length) {
1174
- lines.push('', 'Options:');
1436
+ lines.push('', helpHeading('Options:'));
1175
1437
  for (const arg of command.args) {
1176
1438
  const value = arg.valueName ? ` <${arg.valueName}>` : '';
1177
1439
  const description = arg.description ? ` ${arg.description}` : '';
1178
- lines.push(` --${arg.name}${value}${description}`);
1440
+ lines.push(` ${helpOption(`--${arg.name}${value}`)}${description}`);
1179
1441
  }
1180
1442
  }
1181
- lines.push('', 'Global options:', ' -f, --format json 输出 JSON;默认不传时输出精简文本', ' --json 等同于 -f json');
1443
+ lines.push(
1444
+ '',
1445
+ helpHeading('Global options:'),
1446
+ ` ${helpOption('-f, --format <format>')} 输出格式:pretty(默认)、text、json、yaml、csv、auto`,
1447
+ ` ${helpOption('--json'.padEnd(22))} 等同于 -f json`,
1448
+ );
1182
1449
  process.stdout.write(`${lines.join('\n')}\n`);
1183
1450
  }
1184
1451
 
1185
- function validateCommandOptions(command, kwargs) {
1452
+ function validateCommandOptions(command, kwargs, kwargRawFlags = {}) {
1186
1453
  const allowedKeys = (command.args || []).map((arg) => normalizeKey(arg.name));
1187
1454
  const allowed = new Set(allowedKeys);
1188
1455
  const unknown = Object.keys(kwargs).filter((key) => !allowed.has(key));
1189
1456
  if (!unknown.length) return;
1457
+ const displayFlag = (key) => kwargRawFlags[key] || formatOptionName(key);
1190
1458
  const suggestions = {};
1191
1459
  for (const unk of unknown) {
1192
1460
  const matches = suggestSimilarOptions(unk, allowedKeys);
1193
- if (matches.length) suggestions[formatOptionName(unk)] = matches;
1461
+ if (matches.length) suggestions[displayFlag(unk)] = matches;
1194
1462
  }
1463
+ const usedShortForm = unknown.some((key) => {
1464
+ const flag = kwargRawFlags[key];
1465
+ return flag && flag.startsWith('-') && !flag.startsWith('--');
1466
+ });
1195
1467
  const baseHint = `运行 ${commandPrefix()} ${command.name} -h 查看可用参数。`;
1468
+ const shortHint = usedShortForm
1469
+ ? `本 CLI 仅支持 -f / -h / -v 等少数短选项,业务参数请使用 --xxx 长形式。`
1470
+ : '';
1196
1471
  const suggestionParts = Object.entries(suggestions).map(([opt, matches]) => `${opt} → ${matches.join(' / ')}`);
1197
- const hint = suggestionParts.length
1198
- ? `你是不是想用:${suggestionParts.join(';')}?否则 ${baseHint}`
1199
- : baseHint;
1200
- throw new LingjingAwbCliError(`未知参数:${unknown.map(formatOptionName).join(', ')}`, {
1472
+ const hintParts = [];
1473
+ if (suggestionParts.length) hintParts.push(`你是不是想用:${suggestionParts.join(';')}?`);
1474
+ if (shortHint) hintParts.push(shortHint);
1475
+ hintParts.push(baseHint);
1476
+ throw new LingjingAwbCliError(`未知参数:${unknown.map(displayFlag).join(', ')}`, {
1201
1477
  type: 'unknown_option',
1202
1478
  exitCode: 2,
1203
- hint,
1479
+ hint: hintParts.join(' '),
1204
1480
  details: {
1205
1481
  command: command.name,
1206
- unknownOptions: unknown.map(formatOptionName),
1482
+ unknownOptions: unknown.map(displayFlag),
1207
1483
  ...(Object.keys(suggestions).length ? { suggestions } : {}),
1208
1484
  },
1209
1485
  });
1210
1486
  }
1211
1487
 
1212
1488
  function printData(data, format, meta = {}, context = {}) {
1213
- if (format === 'json') {
1489
+ const outputFormat = resolveOutputFormat(format);
1490
+ if (outputFormat === 'json') {
1214
1491
  process.stdout.write(`${JSON.stringify(createEnvelope({ status: 'success', data: normalizeJsonData(meta.command, data), meta }), null, 2)}\n`);
1215
1492
  return;
1216
1493
  }
1217
- process.stdout.write(formatTextOutput(meta.command, data, context));
1494
+ if (outputFormat === 'yaml') {
1495
+ process.stdout.write(formatYamlEnvelope(createEnvelope({ status: 'success', data: normalizeJsonData(meta.command, data), meta })));
1496
+ return;
1497
+ }
1498
+ if (outputFormat === 'csv') {
1499
+ process.stdout.write(formatCsvOutput(meta.command, data, context));
1500
+ return;
1501
+ }
1502
+ if (outputFormat === 'text') {
1503
+ process.stdout.write(formatTextOutput(meta.command, data, context));
1504
+ return;
1505
+ }
1506
+ process.stdout.write(formatPrettyOutput(meta.command, data, context));
1218
1507
  }
1219
1508
 
1220
1509
  function printError(error, format, meta = {}) {
1510
+ let outputFormat = 'text';
1511
+ try {
1512
+ outputFormat = resolveOutputFormat(format);
1513
+ } catch {
1514
+ outputFormat = 'text';
1515
+ }
1221
1516
  const payload = {
1222
1517
  type: error.type || 'error',
1223
1518
  message: error.message || String(error),
1224
1519
  ...(error.hint ? { hint: error.hint } : {}),
1225
1520
  ...(error.details ? { details: error.details } : {}),
1226
1521
  };
1227
- if (format === 'json') {
1522
+ if (outputFormat === 'json') {
1228
1523
  process.stderr.write(`${JSON.stringify(createEnvelope({ status: 'error', error: payload, meta }), null, 2)}\n`);
1524
+ } else if (outputFormat === 'yaml') {
1525
+ process.stderr.write(formatYamlEnvelope(createEnvelope({ status: 'error', error: payload, meta })));
1526
+ } else if (outputFormat === 'pretty') {
1527
+ process.stderr.write(formatPrettyError(payload));
1229
1528
  } else {
1230
1529
  process.stderr.write(formatTextError(payload));
1231
1530
  }
@@ -1265,7 +1564,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
1265
1564
  return;
1266
1565
  }
1267
1566
 
1268
- const { commandName: rawCommandName, kwargs, format } = parseArgv(argv);
1567
+ const { commandName: rawCommandName, kwargs, kwargRawFlags, format } = parseArgv(argv);
1269
1568
  const commandName = resolveCommandName(rawCommandName, commands);
1270
1569
  const command = commands.find((item) => item.name === commandName);
1271
1570
  if (!command) {
@@ -1289,11 +1588,12 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
1289
1588
 
1290
1589
  const startedAt = Date.now();
1291
1590
  try {
1292
- validateCommandOptions(command, kwargs);
1591
+ const outputFormat = resolveOutputFormat(format);
1592
+ validateCommandOptions(command, kwargs, kwargRawFlags);
1293
1593
  const data = command.virtual === 'schema'
1294
1594
  ? buildCommandSchema(commands, kwargs, version)
1295
1595
  : await command.func({ command }, kwargs);
1296
- printData(data, format, { command: command.name, elapsedMs: Date.now() - startedAt }, buildOutputContext(command, kwargs));
1596
+ printData(data, outputFormat, { command: command.name, elapsedMs: Date.now() - startedAt }, buildOutputContext(command, kwargs));
1297
1597
  } catch (error) {
1298
1598
  const cliError = error instanceof LingjingAwbCliError
1299
1599
  ? error