@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.
Files changed (35) hide show
  1. package/README.md +57 -8
  2. package/build/_shared.mjs +54 -5
  3. package/build/prod.mjs +12 -3
  4. package/package.json +6 -2
  5. package/packages/awb-cli/package.json +2 -2
  6. package/packages/awb-core/package.json +6 -2
  7. package/packages/awb-core/src/api.js +22 -0
  8. package/packages/awb-core/src/commands.js +112 -39
  9. package/packages/awb-core/src/common.js +8 -0
  10. package/packages/awb-core/src/output.js +2030 -8
  11. package/packages/awb-core/src/services.js +1835 -205
  12. package/packages/awb-core/src/standalone.js +472 -136
  13. package/packages/awb-core/src/update.js +327 -0
  14. package/skills/lj-awb/SKILL.md +35 -12
  15. package/skills/lj-awb/VERSION +1 -1
  16. package/skills/lj-awb/compat.json +3 -3
  17. package/skills/lj-awb/modules/artifact/asset.md +1 -1
  18. package/skills/lj-awb/modules/artifact/clip.md +1 -1
  19. package/skills/lj-awb/modules/artifact/script.md +1 -1
  20. package/skills/lj-awb/modules/artifact/video.md +1 -1
  21. package/skills/lj-awb/modules/asset.md +10 -1
  22. package/skills/lj-awb/modules/auth.md +9 -1
  23. package/skills/lj-awb/modules/create-contract.md +5 -2
  24. package/skills/lj-awb/modules/create.md +4 -2
  25. package/skills/lj-awb/modules/driver.md +12 -6
  26. package/skills/lj-awb/modules/image.md +3 -1
  27. package/skills/lj-awb/modules/model.md +12 -9
  28. package/skills/lj-awb/modules/task.md +4 -1
  29. package/skills/lj-awb/modules/upload.md +1 -1
  30. package/skills/lj-awb/modules/video.md +11 -2
  31. package/skills/lj-awb/modules/workflows.md +3 -1
  32. package/skills/lj-awb/references/error-codes.md +24 -0
  33. package/skills/lj-awb/references/model-options-read.md +16 -10
  34. package/skills/lj-awb/references/output-fields.md +10 -7
  35. package/skills/lj-awb/scripts/resolve-lj-awb-cmd.sh +106 -4
@@ -1,3 +1,8 @@
1
+ import { Chalk } from 'chalk';
2
+ import Table from 'cli-table3';
3
+
4
+ const CHALK = new Chalk({ level: 1 });
5
+
1
6
  function isPlainObject(value) {
2
7
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
8
  }
@@ -47,6 +52,7 @@ function requestSummary(request = {}) {
47
52
  const promptParams = request.promptParams || {};
48
53
  return compactRecord({
49
54
  requestSource: request.requestSource,
55
+ objectName: request.objectName,
50
56
  modelCode: request.modelCode,
51
57
  modelGroupCode: request.modelGroupCode,
52
58
  projectGroupNo: request.projectGroupNo,
@@ -116,16 +122,38 @@ function normalizeAuthStatus(data = {}) {
116
122
  }
117
123
 
118
124
  function normalizeAuthLogin(data = {}) {
125
+ const userId = data.user?.userId;
126
+ const userName = data.user?.userName;
127
+ const userDisplay = userName
128
+ ? (userId ? `${userName} (${userId})` : userName)
129
+ : (userId || undefined);
119
130
  return compactRecord({
131
+ loginMethod: data.loginMethod,
132
+ status: data.status,
120
133
  saved: data.saved ?? Boolean(data.auth || data.accessKey),
121
134
  verified: data.verified,
122
135
  accessKey: data.accessKey || data.auth?.accessKey,
123
136
  source: data.auth ? [data.auth.accessKeySource, data.auth.accessKeySourceName].filter(Boolean).join('/') : undefined,
137
+ userDisplay,
138
+ userId,
139
+ userName,
140
+ groupId: data.user?.groupId,
141
+ groupName: data.user?.groupName,
124
142
  user: data.user,
143
+ flowId: data.flowId,
144
+ verifyUrl: data.verifyUrl,
145
+ nextCommand: data.nextCommand,
125
146
  dryRun: data.dryRun,
126
147
  });
127
148
  }
128
149
 
150
+ function normalizeAuthLogout(data = {}) {
151
+ return compactRecord({
152
+ loggedOut: data.loggedOut,
153
+ authPath: data.authPath,
154
+ });
155
+ }
156
+
129
157
  function normalizeDryRunResult(row = {}) {
130
158
  if (!isPlainObject(row) || !row.dryRun) return cleanNested(row);
131
159
  const localFiles = Array.isArray(row.localFiles) ? row.localFiles : (row.localFile ? [row.localFile] : []);
@@ -153,6 +181,26 @@ function normalizeDryRun(data = {}) {
153
181
  resourceConversion: Array.isArray(resourceConversions) && resourceConversions.length === 1
154
182
  ? `${resourceConversions[0].resource || 'resource'} ${resourceConversions[0].fromFormat}->${resourceConversions[0].toFormat}`
155
183
  : undefined,
184
+ validationLegal: data.validation?.legal,
185
+ conversionRequired: Boolean(data.conversionPlan),
186
+ converted: Boolean(data.conversion?.converted),
187
+ conversionReason: data.conversionPlan?.reason ?? data.conversion?.reason ?? (Array.isArray(data.validation?.violations) ? data.validation.violations.map((item) => item.message).join(';') : undefined),
188
+ conversionTargetFormat: data.conversionPlan?.toFormat ?? data.conversion?.toFormat,
189
+ conversionTargetWidth: data.conversionPlan?.targetWidth ?? data.conversion?.targetWidth,
190
+ conversionTargetHeight: data.conversionPlan?.targetHeight ?? data.conversion?.targetHeight,
191
+ conversionTargetPixels: data.conversionPlan?.targetPixels ?? data.conversion?.targetPixels,
192
+ conversionTargetFps: data.conversionPlan?.targetFps ?? data.conversion?.targetFps,
193
+ conversionTargetDuration: data.conversionPlan?.targetDuration ?? data.conversion?.targetDuration,
194
+ conversionTarget: data.conversionPlan
195
+ ? compactRecord({
196
+ format: data.conversionPlan.toFormat,
197
+ width: data.conversionPlan.targetWidth,
198
+ height: data.conversionPlan.targetHeight,
199
+ pixels: data.conversionPlan.targetPixels,
200
+ fps: data.conversionPlan.targetFps,
201
+ duration: data.conversionPlan.targetDuration,
202
+ })
203
+ : undefined,
156
204
  fileCount: Array.isArray(data.files) ? data.files.length : undefined,
157
205
  assetCount: Array.isArray(data.assets) ? data.assets.length : undefined,
158
206
  files: data.files,
@@ -163,10 +211,19 @@ function normalizeDryRun(data = {}) {
163
211
  });
164
212
  }
165
213
 
166
- function normalizeTaskSubmission(data = {}) {
214
+ function submissionStatusCommand(commandName, data = {}) {
215
+ if (!data?.taskId) return null;
216
+ if (commandName === 'create image') return `${commandPrefix()} task image-status --task-id ${data.taskId}`;
217
+ if (commandName === 'create video') return `${commandPrefix()} task video-status --task-id ${data.taskId}`;
218
+ return null;
219
+ }
220
+
221
+ function normalizeTaskSubmission(commandName, data = {}) {
222
+ const statusCommand = rewriteCommandPrefix(data.statusCommand || submissionStatusCommand(commandName, data));
167
223
  return compactRecord({
168
224
  taskId: data.taskId,
169
225
  taskType: data.taskType,
226
+ objectName: data.objectName,
170
227
  modelGroupCode: data.modelGroupCode,
171
228
  projectGroupNo: data.projectGroupNo,
172
229
  pointNo: data.pointNo,
@@ -179,6 +236,7 @@ function normalizeTaskSubmission(data = {}) {
179
236
  projectBudgetRemainingAfter: data.projectBudgetRemainingAfter ?? data.projectPointRemainingAfter,
180
237
  uploadCount: Array.isArray(data.uploads) ? data.uploads.length : undefined,
181
238
  nextCommand: rewriteCommandPrefix(data.nextCommand),
239
+ statusCommand,
182
240
  waited: data.waited ? normalizeTaskStatus(data.waited) : undefined,
183
241
  });
184
242
  }
@@ -202,6 +260,7 @@ function normalizeSubtitleSubmission(data = {}) {
202
260
 
203
261
  function normalizeFee(data = {}) {
204
262
  return compactRecord({
263
+ objectName: data.objectName,
205
264
  modelGroupCode: data.modelGroupCode,
206
265
  projectGroupNo: data.projectGroupNo,
207
266
  pointCost: data.pointCost,
@@ -329,6 +388,7 @@ function normalizeSubjectVoice(data = {}) {
329
388
  }
330
389
 
331
390
  function normalizeAsset(data = {}) {
391
+ const conversion = data.conversionPlan ?? data.conversion ?? null;
332
392
  return compactRecord({
333
393
  created: data.created,
334
394
  updated: data.updated,
@@ -336,9 +396,30 @@ function normalizeAsset(data = {}) {
336
396
  groupId: data.groupId,
337
397
  assetId: data.assetId,
338
398
  platform: data.platform,
399
+ assetType: data.assetType,
339
400
  name: data.name,
340
401
  projectName: data.projectName,
341
402
  assetPath: data.assetPath,
403
+ validationLegal: data.validation?.legal ?? (data.conversion ? true : undefined),
404
+ conversionRequired: Boolean(data.conversionPlan ?? data.conversion),
405
+ converted: Boolean(data.conversion?.converted),
406
+ conversionReason: conversion?.reason,
407
+ conversionTargetFormat: conversion?.toFormat,
408
+ conversionTargetWidth: conversion?.targetWidth,
409
+ conversionTargetHeight: conversion?.targetHeight,
410
+ conversionTargetPixels: conversion?.targetPixels,
411
+ conversionTargetFps: conversion?.targetFps,
412
+ conversionTargetDuration: conversion?.targetDuration,
413
+ conversionTarget: conversion
414
+ ? compactRecord({
415
+ format: conversion.toFormat,
416
+ width: conversion.targetWidth,
417
+ height: conversion.targetHeight,
418
+ pixels: conversion.targetPixels,
419
+ fps: conversion.targetFps,
420
+ duration: conversion.targetDuration,
421
+ })
422
+ : undefined,
342
423
  uploadBackendPath: data.upload?.backendPath,
343
424
  uploadUrl: data.upload?.url,
344
425
  });
@@ -492,6 +573,8 @@ export function normalizeOutputData(commandName, data) {
492
573
  return normalizeAuthStatus(data);
493
574
  case 'auth login':
494
575
  return normalizeAuthLogin(data);
576
+ case 'auth logout':
577
+ return normalizeAuthLogout(data);
495
578
  default:
496
579
  break;
497
580
  }
@@ -499,11 +582,13 @@ export function normalizeOutputData(commandName, data) {
499
582
  switch (commandName) {
500
583
  case 'create image':
501
584
  case 'create video':
502
- return normalizeTaskSubmission(data);
585
+ case 'create video-super-resolution':
586
+ return normalizeTaskSubmission(commandName, data);
503
587
  case 'create video-subtitle-removal':
504
588
  return normalizeSubtitleSubmission(data);
505
589
  case 'create image-fee':
506
590
  case 'create video-fee':
591
+ case 'create video-super-resolution-fee':
507
592
  return normalizeFee(data);
508
593
  case 'account info':
509
594
  return normalizeAccountInfo(data);
@@ -521,6 +606,7 @@ export function normalizeOutputData(commandName, data) {
521
606
  case 'task video-status':
522
607
  case 'task wait':
523
608
  case 'task video-subtitle-status':
609
+ case 'task video-super-resolution-status':
524
610
  return normalizeTaskStatus(data);
525
611
  case 'create subject':
526
612
  case 'create subject-wait':
@@ -554,6 +640,8 @@ export function normalizeOutputData(commandName, data) {
554
640
  return normalizeList(data, ['subjects']);
555
641
  case 'create subject-voice-list':
556
642
  return normalizeList(data, ['voices']);
643
+ case 'update':
644
+ return normalizeUpdateResult(data);
557
645
  default:
558
646
  break;
559
647
  }
@@ -642,6 +730,22 @@ function normalizeArtifactByCommand(commandName, data) {
642
730
  return cleanNested(data);
643
731
  }
644
732
 
733
+ function normalizeUpdateResult(data = {}) {
734
+ return compactRecord({
735
+ checked: data.checked,
736
+ updated: data.updated,
737
+ updateAvailable: data.updateAvailable,
738
+ packageName: data.packageName,
739
+ previousVersion: data.previousVersion,
740
+ currentVersion: data.currentVersion,
741
+ latestVersion: data.latestVersion,
742
+ command: rewriteCommandPrefix(data.command),
743
+ skillUpdated: data.skillUpdated,
744
+ restartRecommended: data.restartRecommended,
745
+ message: data.message,
746
+ });
747
+ }
748
+
645
749
  export function normalizeJsonData(commandName, data) {
646
750
  if (!isPlainObject(data)) return rewriteNested(data);
647
751
  if (data.dryRun) return rewriteNested(data);
@@ -659,6 +763,21 @@ function shortValue(value) {
659
763
  return text.length > 120 ? `${text.slice(0, 117)}...` : text;
660
764
  }
661
765
 
766
+ function formatPercent(value) {
767
+ if (value === undefined || value === null || value === '') return '';
768
+ const number = Number(value);
769
+ if (!Number.isFinite(number)) return '';
770
+ const percent = number > 1 ? number : number * 100;
771
+ const rounded = Math.round(percent * 10) / 10;
772
+ const text = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
773
+ return `${text}%`;
774
+ }
775
+
776
+ function shortRecordValue(key, value) {
777
+ if (key === 'successRate') return formatPercent(value) || shortValue(value);
778
+ return shortValue(value);
779
+ }
780
+
662
781
  function valueList(values, limit = 12) {
663
782
  if (!Array.isArray(values) || !values.length) return undefined;
664
783
  const items = values
@@ -678,7 +797,7 @@ function renderRecord(record, preferredKeys = [], excludedKeys = []) {
678
797
  ...preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined),
679
798
  ...Object.keys(record).filter((key) => !excluded.has(key) && !preferredKeys.includes(key) && !Array.isArray(record[key]) && !isPlainObject(record[key])),
680
799
  ];
681
- return keys.map((key) => `${key}=${shortValue(record[key])}`).join(' ');
800
+ return keys.map((key) => `${key}=${shortRecordValue(key, record[key])}`).join(' ');
682
801
  }
683
802
 
684
803
  function recordScalarKeys(record, preferredKeys = [], excludedKeys = []) {
@@ -693,7 +812,7 @@ function pushSectionRecord(lines, title, record, preferredKeys = []) {
693
812
  const compacted = compactRecord(record);
694
813
  lines.push(`${title}:`);
695
814
  for (const key of recordScalarKeys(compacted, preferredKeys)) {
696
- lines.push(` ${key}=${shortValue(compacted[key])}`);
815
+ lines.push(` ${key}=${shortRecordValue(key, compacted[key])}`);
697
816
  }
698
817
  }
699
818
 
@@ -740,6 +859,7 @@ function rowSummary(row) {
740
859
  'modelDesc',
741
860
  'modelStatus',
742
861
  'taskQueueNum',
862
+ 'successRate',
743
863
  'feeCalcType',
744
864
  'inputModes',
745
865
  'params',
@@ -871,7 +991,7 @@ function rowSummary(row) {
871
991
  if (Array.isArray(value) && value.length === 0) return false;
872
992
  return true;
873
993
  })
874
- .map((key) => `${key}=${shortValue(row[key])}`)
994
+ .map((key) => `${key}=${shortRecordValue(key, row[key])}`)
875
995
  .join(' ');
876
996
  }
877
997
 
@@ -908,8 +1028,30 @@ function textItemCount(item = {}) {
908
1028
  return undefined;
909
1029
  }
910
1030
 
1031
+ function textCountRange(item = {}) {
1032
+ if (item.minCount != null && item.maxCount != null && item.minCount === item.maxCount) return String(item.maxCount);
1033
+ if (item.minCount != null && item.maxCount != null) return `${item.minCount}..${item.maxCount}`;
1034
+ if (item.minCount != null) return `>=${item.minCount}`;
1035
+ if (item.maxCount != null) return `<=${item.maxCount}`;
1036
+ return item.value;
1037
+ }
1038
+
1039
+ function textLimitSummary(item = {}) {
1040
+ if (!isPlainObject(item)) return undefined;
1041
+ const parts = [];
1042
+ const files = textFileCount(item);
1043
+ const duration = textDurationRange(item);
1044
+ const items = textItemCount(item);
1045
+ if (files) parts.push(`files${files}`);
1046
+ if (duration) parts.push(`durationMs${duration}`);
1047
+ if (item.maxTotalDurationMs != null) parts.push(`totalDurationMs<=${item.maxTotalDurationMs}`);
1048
+ if (item.maxSizeKB != null) parts.push(`maxSizeKB<=${item.maxSizeKB}`);
1049
+ if (items) parts.push(`items${items}`);
1050
+ return parts.length ? parts.join('|') : undefined;
1051
+ }
1052
+
911
1053
  function textAllowValues(values = []) {
912
- return Array.isArray(values) && values.length ? valueList(values) : '<none>';
1054
+ return Array.isArray(values) && values.length ? valueList(values) : undefined;
913
1055
  }
914
1056
 
915
1057
  function textConditionValue(value) {
@@ -923,6 +1065,7 @@ function textConstraintConditions(conditions = []) {
923
1065
  return conditions
924
1066
  .map((item) => {
925
1067
  const key = item.key || item.configCode;
1068
+ if (item.meaning === 'count') return key ? `${key}=${textCountRange(item)}` : null;
926
1069
  return key ? `${key}=${textConditionValue(item.value)}` : null;
927
1070
  })
928
1071
  .filter(Boolean)
@@ -1018,9 +1161,10 @@ function formatModelOptionsOutput(data = {}) {
1018
1161
  allowedValues: textAllowValues(item.allowValues),
1019
1162
  when: textConstraintConditions(item.conditions),
1020
1163
  effect: item.effect,
1164
+ limits: textLimitSummary(item.limits),
1021
1165
  priority: item.priority,
1022
1166
  name: item.name,
1023
- }, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'priority', 'name']);
1167
+ }, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'limits', 'priority', 'name']);
1024
1168
  }
1025
1169
  if (data.modelGroupCode) {
1026
1170
  lines.push('next:');
@@ -1037,6 +1181,79 @@ function textKeyBinding(value) {
1037
1181
  return value;
1038
1182
  }
1039
1183
 
1184
+ function localizedIntentMode(value, taskKind = '') {
1185
+ const mode = String(value || '');
1186
+ const labels = {
1187
+ prompt_only: taskKind === 'video' ? '文本生成视频' : '文本生成图片',
1188
+ reference: taskKind === 'video' ? '参考素材生成' : '参考图生成',
1189
+ frames: '首帧/尾帧生成',
1190
+ storyboard: '关键帧/故事板生成',
1191
+ keyframe: '关键帧生成',
1192
+ };
1193
+ return labels[mode] || localizedInputMode(mode, taskKind);
1194
+ }
1195
+
1196
+ function localizedIntentText(value, mode, taskKind = '') {
1197
+ const text = String(value || '');
1198
+ if (!text) return localizedIntentMode(mode, taskKind);
1199
+ const normalized = text.trim();
1200
+ if (normalized === '参考模式') return taskKind === 'video' ? '参考素材生成' : '参考图生成';
1201
+ if (normalized === 'frames 模式') return '首帧/尾帧生成';
1202
+ if (normalized === 'prompt_only 模式') return localizedIntentMode('prompt_only', taskKind);
1203
+ if (normalized === '多帧 / 故事板模式') return '关键帧/故事板生成';
1204
+ return normalized
1205
+ .replaceAll('frames', '首帧/尾帧')
1206
+ .replaceAll('first_frame', '首帧')
1207
+ .replaceAll('last_frame', '尾帧')
1208
+ .replaceAll('reference', '参考素材')
1209
+ .replaceAll('prompt_only', '纯文本');
1210
+ }
1211
+
1212
+ function localizedPromptBinding(value) {
1213
+ const labels = {
1214
+ key_required: '必须绑定 key',
1215
+ required: '必须绑定 key',
1216
+ optional_key: '可选 key',
1217
+ optional: '可选 key',
1218
+ none: '不需要',
1219
+ };
1220
+ return labels[value] || prettyValue(value);
1221
+ }
1222
+
1223
+ function localizedModeList(values = [], taskKind = '') {
1224
+ if (!Array.isArray(values) || !values.length) return '';
1225
+ return values.map((item) => localizedIntentMode(item, taskKind)).join('、');
1226
+ }
1227
+
1228
+ function localizedCreateSpecSummary(value) {
1229
+ return String(value || '')
1230
+ .replaceAll('prompt_only', '纯文本')
1231
+ .replaceAll('reference', '参考素材')
1232
+ .replaceAll('frames', '首帧/尾帧')
1233
+ .replaceAll('storyboard', '关键帧/故事板');
1234
+ }
1235
+
1236
+ function localizedGuideValues(row = {}) {
1237
+ const values = Array.isArray(row.values) ? row.values : [];
1238
+ if (!values.length) return '';
1239
+ if (row.field === 'resources[].usage') {
1240
+ return values.map((value) => `${value}(${localizedUsage(value)})`).join('、');
1241
+ }
1242
+ if (row.field === 'resources[].type') {
1243
+ return values.map((value) => `${value}(${localizedMedia(value)})`).join('、');
1244
+ }
1245
+ if (row.field === 'resources[].source.kind') {
1246
+ const labels = { url: '文件/URL/平台素材路径', asset_id: '资产 ID' };
1247
+ return values.map((value) => `${value}(${labels[value] || value})`).join('、');
1248
+ }
1249
+ return joinValueList(values, 8);
1250
+ }
1251
+
1252
+ function commandArgLines(values = []) {
1253
+ if (!Array.isArray(values) || !values.length) return '';
1254
+ return values.map((value) => prettyValue(value)).join('\n');
1255
+ }
1256
+
1040
1257
  function modelFeeNextCommand(data = {}) {
1041
1258
  if (!data.feeCommand || !data.modelGroupCode) return null;
1042
1259
  const args = [
@@ -1082,6 +1299,16 @@ function formatModelCreateSpecOutput(data = {}) {
1082
1299
  }
1083
1300
  pushSectionCount(lines, 'examples', examples.length);
1084
1301
  for (const [index, example] of examples.entries()) pushSectionItem(lines, index, rewriteCommandPrefix(example));
1302
+ const userParams = Array.isArray(data.parameterControls?.userParams) ? data.parameterControls.userParams : [];
1303
+ pushSectionCount(lines, 'params', userParams.length);
1304
+ for (const [index, param] of userParams.entries()) {
1305
+ pushSectionRecordItem(lines, index, {
1306
+ key: param.key,
1307
+ cliArg: param.cliArg,
1308
+ values: valueList(param.allowedValues),
1309
+ generic: param.genericModelParam,
1310
+ }, ['key', 'cliArg', 'values', 'generic']);
1311
+ }
1085
1312
  lines.push('next:');
1086
1313
  if (data.optionsCommand) pushSectionKeyValue(lines, 'options', `${commandPrefix()} ${data.optionsCommand}`);
1087
1314
  const feeNext = modelFeeNextCommand(data);
@@ -1154,6 +1381,1794 @@ function subtitleRemoveNextCommand(commandName, normalized) {
1154
1381
  return `${commandPrefix()} create video-subtitle-removal --source-task-id ${normalized.taskId}${projectGroupPart} --dry-run`;
1155
1382
  }
1156
1383
 
1384
+ const OUTPUT_KIND_BY_COMMAND = {
1385
+ doctor: 'environment_report',
1386
+ schema: 'command_schema',
1387
+ 'auth status': 'auth_status',
1388
+ 'auth verify': 'auth_status',
1389
+ 'auth login': 'auth_result',
1390
+ 'auth clear': 'generic',
1391
+ 'account info': 'account_summary',
1392
+ 'account teams': 'team_list',
1393
+ 'account switch-team': 'generic',
1394
+ 'project list': 'project_list',
1395
+ 'project current': 'project_summary',
1396
+ 'project use': 'generic',
1397
+ 'project users': 'project_user_list',
1398
+ 'project create': 'generic',
1399
+ 'project update': 'generic',
1400
+ 'project ensure': 'generic',
1401
+ 'credits balance': 'credits_balance',
1402
+ 'credits usage': 'credits_usage',
1403
+ 'model image-models': 'model_list',
1404
+ 'model video-models': 'model_list',
1405
+ 'model asset-review-models': 'asset_review_model_list',
1406
+ 'model options': 'model_options',
1407
+ 'model create-spec': 'model_create_spec',
1408
+ 'model input-guide': 'model_input_guide',
1409
+ 'upload files': 'upload_result',
1410
+ 'create image-fee': 'fee_estimate',
1411
+ 'create image': 'task_submission',
1412
+ 'create image-batch': 'batch_task_submission',
1413
+ 'task image-status': 'task_status',
1414
+ 'create video-fee': 'fee_estimate',
1415
+ 'create video': 'task_submission',
1416
+ 'create video-batch': 'batch_task_submission',
1417
+ 'create video-super-resolution-fee': 'fee_estimate',
1418
+ 'create video-super-resolution': 'task_submission',
1419
+ 'task video-status': 'task_status',
1420
+ 'create video-subtitle-removal': 'subtitle_task_submission',
1421
+ 'task video-subtitle-status': 'subtitle_task_status',
1422
+ 'task video-super-resolution-status': 'task_status',
1423
+ 'task list': 'task_list',
1424
+ 'task wait': 'task_status',
1425
+ 'task records': 'local_task_records',
1426
+ 'task record-poll': 'batch_task_status',
1427
+ 'create asset-match-actor': 'asset_match_list',
1428
+ 'create asset-groups': 'asset_group_list',
1429
+ 'create asset-group-get': 'asset_group_detail',
1430
+ 'create asset-group': 'asset_group_write_result',
1431
+ 'create asset-group-update': 'asset_group_write_result',
1432
+ 'create asset': 'asset_register_result',
1433
+ 'artifact script get': 'artifact_full',
1434
+ 'artifact script document': 'artifact_record',
1435
+ 'artifact script rows': 'artifact_list',
1436
+ 'artifact script row': 'artifact_record',
1437
+ 'artifact script children': 'artifact_list',
1438
+ 'artifact script upsert-row': 'artifact_write_result',
1439
+ 'artifact script delete-row': 'artifact_delete_result',
1440
+ 'artifact script import': 'artifact_import_result',
1441
+ 'artifact asset get': 'artifact_full',
1442
+ 'artifact asset actors': 'artifact_list',
1443
+ 'artifact asset actor': 'artifact_record',
1444
+ 'artifact asset props': 'artifact_list',
1445
+ 'artifact asset prop': 'artifact_record',
1446
+ 'artifact asset locations': 'artifact_list',
1447
+ 'artifact asset location': 'artifact_record',
1448
+ 'artifact asset upsert-actor': 'artifact_write_result',
1449
+ 'artifact asset upsert-prop': 'artifact_write_result',
1450
+ 'artifact asset upsert-location': 'artifact_write_result',
1451
+ 'artifact asset upsert-actor-state': 'artifact_write_result',
1452
+ 'artifact asset upsert-prop-state': 'artifact_write_result',
1453
+ 'artifact asset upsert-location-state': 'artifact_write_result',
1454
+ 'artifact asset delete-actor': 'artifact_delete_result',
1455
+ 'artifact asset delete-prop': 'artifact_delete_result',
1456
+ 'artifact asset delete-location': 'artifact_delete_result',
1457
+ 'artifact asset delete-actor-state': 'artifact_delete_result',
1458
+ 'artifact asset delete-prop-state': 'artifact_delete_result',
1459
+ 'artifact asset delete-location-state': 'artifact_delete_result',
1460
+ 'artifact asset import': 'artifact_import_result',
1461
+ 'artifact video get': 'artifact_full',
1462
+ 'artifact video episodes': 'artifact_list',
1463
+ 'artifact video episode': 'artifact_record',
1464
+ 'artifact video scenes': 'artifact_list',
1465
+ 'artifact video scene': 'artifact_record',
1466
+ 'artifact video clips': 'artifact_list',
1467
+ 'artifact video clip': 'artifact_record',
1468
+ 'artifact video upsert-episode': 'artifact_write_result',
1469
+ 'artifact video upsert-scene': 'artifact_write_result',
1470
+ 'artifact video upsert-clip': 'artifact_write_result',
1471
+ 'artifact video update-clip-urls': 'artifact_write_result',
1472
+ 'artifact video delete-episode': 'artifact_delete_result',
1473
+ 'artifact video delete-scene': 'artifact_delete_result',
1474
+ 'artifact video delete-clip': 'artifact_delete_result',
1475
+ 'artifact video import-storyboard': 'artifact_import_result',
1476
+ 'artifact clip get': 'artifact_full',
1477
+ 'artifact clip episodes': 'artifact_list',
1478
+ 'artifact clip episode': 'artifact_record',
1479
+ 'artifact clip episode-by-id': 'artifact_record',
1480
+ 'artifact clip upsert-episode': 'artifact_write_result',
1481
+ 'artifact clip upsert-batch': 'artifact_import_result',
1482
+ 'artifact clip update-status': 'artifact_status_update',
1483
+ 'artifact clip delete-episode': 'artifact_delete_result',
1484
+ 'create subject-list': 'subject_list',
1485
+ 'create subject': 'subject_publish_result',
1486
+ 'create subject-wait': 'subject_status',
1487
+ 'create subject-voice-list': 'subject_voice_list',
1488
+ 'create subject-voice': 'subject_voice_create_result',
1489
+ 'create subject-voice-wait': 'subject_voice_status',
1490
+ 'create subject-batch': 'batch_subject_publish_result',
1491
+ update: 'update_result',
1492
+ };
1493
+
1494
+ const KIND_TITLES = {
1495
+ account_summary: '账号概览',
1496
+ artifact_delete_result: '最终产物删除结果',
1497
+ artifact_full: '最终产物详情',
1498
+ artifact_import_result: '最终产物导入结果',
1499
+ artifact_list: '最终产物列表',
1500
+ artifact_record: '最终产物记录',
1501
+ artifact_status_update: '剪辑状态更新结果',
1502
+ artifact_write_result: '最终产物写入结果',
1503
+ asset_group_detail: '素材组详情',
1504
+ asset_group_list: '素材组列表',
1505
+ asset_group_write_result: '素材组写入结果',
1506
+ asset_match_list: '素材匹配结果',
1507
+ asset_review_model_list: '素材审核模型',
1508
+ asset_register_result: '素材注册结果',
1509
+ auth_result: '认证结果',
1510
+ auth_status: '认证状态',
1511
+ batch_subject_publish_result: '批量主体提交结果',
1512
+ batch_task_status: '批量任务状态',
1513
+ batch_task_submission: '批量任务提交结果',
1514
+ command_schema: '命令 Schema',
1515
+ credits_balance: '积分余额',
1516
+ credits_usage: '积分用量',
1517
+ environment_report: '环境体检',
1518
+ fee_estimate: '费用预估',
1519
+ generic: '命令结果',
1520
+ local_task_records: '本地任务台账',
1521
+ model_create_spec: '模型创建写法',
1522
+ model_input_guide: '模型输入指南',
1523
+ model_list: '模型列表',
1524
+ model_options: '模型参数与素材约束',
1525
+ project_list: '项目组列表',
1526
+ project_summary: '项目组摘要',
1527
+ project_user_list: '项目组成员',
1528
+ subject_list: '主体列表',
1529
+ subject_publish_result: '主体创建结果',
1530
+ subject_status: '主体状态',
1531
+ subject_voice_create_result: '主体音色创建结果',
1532
+ subject_voice_list: '主体音色列表',
1533
+ subject_voice_status: '主体音色状态',
1534
+ subtitle_task_status: '去字幕任务状态',
1535
+ subtitle_task_submission: '去字幕任务提交结果',
1536
+ task_list: '任务列表',
1537
+ task_status: '任务状态',
1538
+ task_submission: '任务提交结果',
1539
+ team_list: '团队列表',
1540
+ update_result: 'CLI 更新',
1541
+ upload_result: '上传结果',
1542
+ };
1543
+
1544
+ const COMMAND_TITLES = {
1545
+ doctor: '环境体检',
1546
+ schema: '命令 Schema',
1547
+ update: 'CLI 更新',
1548
+ 'auth clear': '清空认证结果',
1549
+ 'auth logout': '退出登录结果',
1550
+ 'account switch-team': '团队切换结果',
1551
+ 'project use': '项目组切换结果',
1552
+ 'project create': '项目组创建结果',
1553
+ 'project update': '项目组更新结果',
1554
+ 'project ensure': '项目组确保结果',
1555
+ 'create image': '生图任务提交结果',
1556
+ 'create video': '生视频任务提交结果',
1557
+ 'create image-batch': '批量生图提交结果',
1558
+ 'create video-batch': '批量生视频提交结果',
1559
+ 'create video-super-resolution-fee': '视频超分积分预估',
1560
+ 'create video-super-resolution': '视频超分任务提交结果',
1561
+ 'create video-subtitle-removal': '去字幕任务提交结果',
1562
+ 'create subject': '主体创建结果',
1563
+ 'create subject-wait': '主体状态',
1564
+ 'create subject-voice': '主体音色创建结果',
1565
+ 'create subject-voice-wait': '主体音色状态',
1566
+ };
1567
+
1568
+ const FIELD_LABELS = {
1569
+ acceptedModes: '可用输入模式',
1570
+ accessKey: 'Access Key',
1571
+ action: '动作',
1572
+ actorCount: '角色数',
1573
+ actorKey: '角色 Key',
1574
+ arch: '架构',
1575
+ assetCount: '素材数',
1576
+ assetId: '素材 ID',
1577
+ assetKey: '素材 Key',
1578
+ assetKind: '素材类型',
1579
+ assetName: '素材名',
1580
+ assetPath: '素材路径',
1581
+ assetType: '素材类型',
1582
+ converted: '已转码',
1583
+ conversionReason: '转码原因',
1584
+ conversionRequired: '需要转码',
1585
+ conversionTargetDuration: '目标时长',
1586
+ conversionTargetFormat: '目标格式',
1587
+ conversionTargetFps: '目标帧率',
1588
+ conversionTargetHeight: '目标高度',
1589
+ conversionTargetPixels: '目标像素',
1590
+ conversionTargetWidth: '目标宽度',
1591
+ validationLegal: '规格合法',
1592
+ authType: '认证方式',
1593
+ backendPath: '后端路径',
1594
+ batchCount: '批次数',
1595
+ billingPointBalance: '可扣积分余额',
1596
+ billingPointRemainingAfter: '扣除后可扣积分',
1597
+ checkedAt: '检查时间',
1598
+ cleared: '已清空',
1599
+ loggedOut: '已退出登录',
1600
+ authPath: '认证文件',
1601
+ clipCount: 'Clip 数',
1602
+ clipId: 'Clip ID',
1603
+ command: '命令',
1604
+ configured: '已配置',
1605
+ count: '数量',
1606
+ created: '已创建',
1607
+ createCommand: '创建命令',
1608
+ customBizId: '业务追踪 ID',
1609
+ deleted: '已删除',
1610
+ description: '描述',
1611
+ displayName: '展示名',
1612
+ taskKind: '任务类型',
1613
+ doctorStatus: '体检状态',
1614
+ dryRun: '预览模式',
1615
+ duration: '时长',
1616
+ elementId: '主体元素 ID',
1617
+ enabled: '启用',
1618
+ entityKey: '实体 Key',
1619
+ episodeCount: '集数',
1620
+ episodeId: '集 ID',
1621
+ errorMessage: '错误信息',
1622
+ externalId: '外部 ID',
1623
+ fileCount: '文件数',
1624
+ feeCommand: '估价命令',
1625
+ format: '格式',
1626
+ generateNum: '生成数量',
1627
+ gmtCreate: '创建时间',
1628
+ groupId: '团队 ID',
1629
+ groupName: '团队名称',
1630
+ id: 'ID',
1631
+ imported: '已导入',
1632
+ inputDir: '输入目录',
1633
+ inputFile: '输入文件',
1634
+ inputSummary: '输入摘要',
1635
+ isSelected: '当前选中',
1636
+ isTerminal: '终态',
1637
+ key: 'Key',
1638
+ localFileCount: '本地文件数',
1639
+ locationCount: '场景数',
1640
+ locationKey: '场景 Key',
1641
+ memberCount: '成员数',
1642
+ messageCount: '消息数',
1643
+ modelCode: '模型编码',
1644
+ modelDesc: '模型说明',
1645
+ modelGroupCode: '模型组编码',
1646
+ modelStatus: '模型状态',
1647
+ name: '名称',
1648
+ needAudio: '需要音频',
1649
+ nextCommand: '下一步命令',
1650
+ nextRefSubject: '主体引用参数',
1651
+ nextVoiceArg: '音色参数',
1652
+ objectName: '对象路径',
1653
+ originUrls: '原始素材',
1654
+ parentKey: '父级 Key',
1655
+ platform: '平台',
1656
+ point: '预算点数',
1657
+ pointCost: '预计消耗积分',
1658
+ pointNo: '积分规格',
1659
+ pointTotal: '积分合计',
1660
+ projectBudgetBalance: '项目预算余额',
1661
+ projectBudgetMax: '项目预算上限',
1662
+ projectBudgetRemainingAfter: '扣除后项目预算',
1663
+ projectGroupName: '项目组名称',
1664
+ projectGroupNo: '项目组编号',
1665
+ projectId: 'AWB 项目 ID',
1666
+ prompt: '提示词',
1667
+ propCount: '道具数',
1668
+ propKey: '道具 Key',
1669
+ publicId: '公开任务 ID',
1670
+ quality: '清晰度',
1671
+ ratio: '宽高比',
1672
+ registered: '已注册',
1673
+ remoteTaskId: '远端任务 ID',
1674
+ requestSource: '请求来源',
1675
+ resourceConversion: '素材转换',
1676
+ resourceConversionCount: '素材转换数',
1677
+ resourceCount: '素材数',
1678
+ resultCount: '结果数',
1679
+ resultUrls: '结果链接',
1680
+ reused: '已复用',
1681
+ rowCount: '行数',
1682
+ rowKind: '行类型',
1683
+ saved: '已保存',
1684
+ sceneCount: '场数',
1685
+ sceneId: '场 ID',
1686
+ selected: '已选择',
1687
+ sortOrder: '排序',
1688
+ source: '来源',
1689
+ sourceFile: '来源文件',
1690
+ sourceTaskId: '来源任务 ID',
1691
+ stateKey: '状态 Key',
1692
+ status: '状态',
1693
+ statusCommand: '状态查询命令',
1694
+ statusCommandTaskType: '状态任务类型',
1695
+ submitted: '已提交',
1696
+ successRate: '成功率',
1697
+ subjectId: '主体 ID',
1698
+ taskCount: '任务数',
1699
+ taskId: '任务 ID',
1700
+ taskQueueNum: '排队数',
1701
+ taskStatus: '任务状态',
1702
+ taskType: '任务类型',
1703
+ timedOut: '已超时',
1704
+ title: '标题',
1705
+ updated: '已更新',
1706
+ userDisplay: '用户',
1707
+ userId: '用户 ID',
1708
+ userName: '用户名',
1709
+ verification: '验证方式',
1710
+ verified: '已验证',
1711
+ verify: '联网验证',
1712
+ verifyCommand: '验证命令',
1713
+ videoEpisodeId: '视频集 ID',
1714
+ voiceId: '音色 ID',
1715
+ voiceName: '音色名称',
1716
+ voiceRecordId: '音色记录 ID',
1717
+ voiceUrl: '音频地址',
1718
+ apiOrigin: 'API 地址',
1719
+ authState: '认证状态',
1720
+ cli: 'CLI',
1721
+ commandCount: '命令数',
1722
+ commandFilter: '命令过滤',
1723
+ commonFieldCount: '通用字段数',
1724
+ currentVersion: '当前版本',
1725
+ domainFilter: '领域过滤',
1726
+ latestVersion: '最新版本',
1727
+ nodeVersion: 'Node 版本',
1728
+ packageName: '包名',
1729
+ previousVersion: '更新前版本',
1730
+ resourceFieldCount: '资源字段数',
1731
+ resourceRuleCount: '资源规则数',
1732
+ restartRecommended: '建议重启',
1733
+ schemaVersion: 'Schema 版本',
1734
+ skillUpdated: 'Skill 已更新',
1735
+ updateAvailable: '有新版本',
1736
+ version: '版本',
1737
+ type: '类型',
1738
+ message: '消息',
1739
+ hint: '提示',
1740
+ unknownOptions: '未知参数',
1741
+ note: '说明',
1742
+ commandPrefix: '命令前缀',
1743
+ elementDescription: '主体描述',
1744
+ elementFrontalImage: '主体正面图',
1745
+ elementName: '主体名称',
1746
+ referenceType: '引用类型',
1747
+ remoteUpload: '远程上传',
1748
+ remoteUrl: '远程地址',
1749
+ };
1750
+
1751
+ const DETAIL_FIELDS_BY_KIND = {
1752
+ account_summary: ['userId', 'userName', 'groupId', 'groupName', 'currentProjectGroupNo', 'currentProjectGroupName', 'billingPointBalance', 'projectBudgetBalance', 'projectBudgetMax'],
1753
+ artifact_delete_result: ['deleted', 'action', 'projectId', 'rowKind', 'entityKey', 'key', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId'],
1754
+ artifact_full: ['projectId', 'id', 'title', 'status', 'rowCount', 'actorCount', 'propCount', 'locationCount', 'episodeCount', 'sceneCount', 'clipCount'],
1755
+ artifact_import_result: ['imported', 'dryRun', 'action', 'projectId', 'sourceFile', 'inputDir', 'inputFile', 'rowCount', 'actorCount', 'propCount', 'locationCount', 'episodeCount', 'sceneCount', 'clipCount', 'batchCount'],
1756
+ artifact_record: ['id', 'projectId', 'rowKind', 'entityKey', 'parentKey', 'actorKey', 'propKey', 'locationKey', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId', 'status', 'title', 'name', 'displayName', 'description'],
1757
+ artifact_status_update: ['updated', 'videoEpisodeId', 'status', 'messageCount'],
1758
+ artifact_write_result: ['id', 'projectId', 'rowKind', 'entityKey', 'parentKey', 'actorKey', 'propKey', 'locationKey', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId', 'status', 'title', 'name', 'displayName'],
1759
+ asset_group_detail: ['groupId', 'platform', 'name', 'projectName', 'description', 'status', 'assetCount'],
1760
+ asset_group_write_result: ['created', 'updated', 'groupId', 'platform', 'name', 'projectName', 'assetPath'],
1761
+ asset_register_result: ['created', 'updated', 'registered', 'assetId', 'groupId', 'platform', 'assetType', 'name', 'projectName', 'assetPath', 'validationLegal', 'conversionRequired', 'converted', 'conversionReason', 'conversionTargetFormat', 'conversionTargetWidth', 'conversionTargetHeight', 'conversionTargetPixels', 'conversionTargetFps', 'conversionTargetDuration', 'uploadBackendPath', 'uploadUrl'],
1762
+ auth_result: ['userDisplay', 'loggedOut', 'saved', 'verified', 'accessKey', 'source', 'dryRun', 'groupName', 'authPath'],
1763
+ auth_status: ['configured', 'authType', 'accessKey', 'source', 'verified', 'verification', 'verifyCommand', 'userId', 'userName', 'groupId', 'groupName'],
1764
+ batch_subject_publish_result: ['dryRun', 'submitted', 'count', 'successCount', 'failureCount', 'modelCode', 'projectGroupNo'],
1765
+ batch_task_status: ['count', 'taskCount', 'successTaskCount', 'resultCount', 'pointTotal', 'timedOut', 'waitedMs'],
1766
+ batch_task_submission: ['dryRun', 'submitted', 'count', 'taskCount', 'successCount', 'failureCount', 'modelGroupCode', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'projectBudgetBalance'],
1767
+ credits_balance: ['billingPointBalance', 'currentProjectGroupNo', 'currentProjectGroupName', 'projectBudgetBalance', 'projectBudgetMax'],
1768
+ credits_usage: ['projectGroupNo', 'sinceText', 'untilText', 'scannedTaskCount', 'pointTotal'],
1769
+ environment_report: ['doctorStatus', 'verify'],
1770
+ fee_estimate: ['objectName', 'modelGroupCode', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter'],
1771
+ generic: ['cleared', 'selected', 'created', 'updated', 'reused', 'dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'groupId', 'groupName', 'projectGroupName', 'point', 'memberCount', 'note'],
1772
+ local_task_records: ['count', 'taskCount', 'successTaskCount', 'resultCount', 'pointTotal'],
1773
+ project_summary: ['selected', 'created', 'updated', 'reused', 'projectGroupNo', 'projectGroupName', 'isSelected', 'projectBudgetBalance', 'projectBudgetMax', 'memberCount'],
1774
+ subject_publish_result: ['created', 'name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'nextRefSubject'],
1775
+ subject_status: ['name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'isTerminal', 'nextRefSubject'],
1776
+ subject_voice_create_result: ['created', 'name', 'voiceRecordId', 'reqTaskId', 'voiceId', 'externalId', 'status', 'taskStatus', 'errorMessage', 'nextVoiceArg'],
1777
+ subject_voice_status: ['name', 'voiceRecordId', 'reqTaskId', 'voiceId', 'externalId', 'status', 'taskStatus', 'errorMessage', 'isTerminal', 'nextVoiceArg'],
1778
+ subtitle_task_status: ['taskId', 'publicId', 'remoteTaskId', 'taskType', 'taskStatus', 'isTerminal', 'projectGroupNo', 'resultCount', 'errorMessage', 'timedOut', 'waitedMs'],
1779
+ subtitle_task_submission: ['submitted', 'taskId', 'taskType', 'sourceTaskId', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter', 'nextCommand'],
1780
+ task_status: ['taskId', 'publicId', 'remoteTaskId', 'taskType', 'taskStatus', 'isTerminal', 'modelGroupCode', 'projectGroupNo', 'pointNo', 'gmtCreate', 'resultCount', 'errorMessage', 'timedOut', 'waitedMs'],
1781
+ task_submission: ['taskId', 'taskType', 'objectName', 'modelGroupCode', 'projectGroupNo', 'pointNo', 'points', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter', 'uploadCount', 'nextCommand', 'statusCommand'],
1782
+ update_result: ['checked', 'updated', 'updateAvailable', 'packageName', 'previousVersion', 'currentVersion', 'latestVersion', 'command', 'skillUpdated', 'restartRecommended', 'message'],
1783
+ upload_result: ['dryRun', 'fileCount', 'assetCount', 'localFileCount', 'resourceConversionCount'],
1784
+ };
1785
+
1786
+ const DRY_RUN_FIELDS_BY_COMMAND = {
1787
+ 'auth login': ['dryRun', 'saved', 'verified', 'accessKey'],
1788
+ 'auth clear': ['dryRun', 'action', 'cleared'],
1789
+ 'account switch-team': ['dryRun', 'action', 'groupId', 'selected'],
1790
+ 'project use': ['dryRun', 'action', 'projectGroupNo', 'selected'],
1791
+ 'project create': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
1792
+ 'project update': ['dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'point', 'memberCount'],
1793
+ 'project ensure': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
1794
+ 'upload files': ['dryRun', 'fileCount', 'localFileCount', 'resourceConversionCount'],
1795
+ 'create image': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'ratio', 'quality', 'generateNum', 'resourceCount', 'localFileCount', 'assetCount'],
1796
+ 'create video': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'duration', 'ratio', 'quality', 'needAudio', 'resourceCount', 'localFileCount', 'assetCount'],
1797
+ 'create image-batch': ['dryRun', 'count', 'modelGroupCode', 'projectGroupNo'],
1798
+ 'create video-batch': ['dryRun', 'count', 'modelGroupCode', 'projectGroupNo'],
1799
+ 'create video-super-resolution-fee': ['dryRun', 'action', 'objectName', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter'],
1800
+ 'create video-super-resolution': ['dryRun', 'action', 'objectName', 'projectGroupNo', 'taskId', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter', 'nextCommand'],
1801
+ 'create video-subtitle-removal': ['dryRun', 'action', 'sourceTaskId', 'projectGroupNo'],
1802
+ 'create asset-group': ['dryRun', 'action', 'platform', 'name', 'projectName'],
1803
+ 'create asset-group-update': ['dryRun', 'action', 'groupId', 'platform', 'name', 'projectName'],
1804
+ 'create asset': ['dryRun', 'action', 'groupId', 'platform', 'assetType', 'name', 'assetPath', 'validationLegal', 'conversionRequired', 'converted', 'conversionReason', 'conversionTargetFormat', 'conversionTargetWidth', 'conversionTargetHeight', 'conversionTargetPixels', 'conversionTargetFps', 'conversionTargetDuration', 'localFileCount', 'fileCount'],
1805
+ 'create subject': ['dryRun', 'action', 'name', 'modelCode', 'elementFrontalImage', 'assetCount', 'localFileCount', 'nextRefSubject'],
1806
+ 'create subject-voice': ['dryRun', 'action', 'name', 'voiceName', 'voiceUrl', 'remoteUrl', 'remoteUpload', 'localFileCount', 'nextVoiceArg'],
1807
+ 'create subject-batch': ['dryRun', 'count', 'modelCode'],
1808
+ 'artifact script import': ['dryRun', 'action', 'projectId', 'sourceFile', 'inputFile', 'rowCount', 'actorCount', 'episodeCount', 'sceneCount', 'clipCount'],
1809
+ 'artifact asset import': ['dryRun', 'action', 'projectId', 'inputDir', 'actorCount', 'propCount', 'locationCount'],
1810
+ 'artifact video import-storyboard': ['dryRun', 'action', 'projectId', 'inputFile', 'episodeId', 'sceneCount', 'clipCount'],
1811
+ 'artifact clip upsert-batch': ['dryRun', 'action', 'projectId', 'episodeCount'],
1812
+ 'artifact clip update-status': ['dryRun', 'action', 'projectId', 'videoEpisodeId', 'status', 'messageCount'],
1813
+ };
1814
+
1815
+ const LIST_KEY_BY_KIND = {
1816
+ artifact_list: 'items',
1817
+ asset_group_list: 'groups',
1818
+ asset_match_list: 'matches',
1819
+ asset_review_model_list: 'models',
1820
+ batch_subject_publish_result: 'results',
1821
+ batch_task_status: 'records',
1822
+ batch_task_submission: 'results',
1823
+ command_schema: 'commands',
1824
+ credits_usage: 'buckets',
1825
+ local_task_records: 'records',
1826
+ model_list: 'models',
1827
+ project_list: 'projectGroups',
1828
+ project_user_list: 'users',
1829
+ subject_list: 'subjects',
1830
+ subject_voice_list: 'voices',
1831
+ task_list: 'tasks',
1832
+ team_list: 'teams',
1833
+ upload_result: 'files',
1834
+ };
1835
+
1836
+ const PRETTY_KIND_PROFILES = Object.fromEntries(Object.keys(KIND_TITLES).map((kind) => [kind, {
1837
+ title: KIND_TITLES[kind],
1838
+ fields: DETAIL_FIELDS_BY_KIND[kind] || [],
1839
+ listKey: LIST_KEY_BY_KIND[kind],
1840
+ }]));
1841
+
1842
+ export const PRETTY_OUTPUT_KIND_COVERAGE = Object.freeze(Object.keys(PRETTY_KIND_PROFILES));
1843
+
1844
+ function outputKindForCommand(commandName, context = {}) {
1845
+ return context.outputKind || OUTPUT_KIND_BY_COMMAND[commandName] || 'generic';
1846
+ }
1847
+
1848
+ function prettyTitle(commandName, kind, data) {
1849
+ const base = COMMAND_TITLES[commandName] || PRETTY_KIND_PROFILES[kind]?.title || KIND_TITLES[kind] || '命令结果';
1850
+ const title = commandName === 'schema' && data?.kind === 'agent_brief' ? 'Agent 能力摘要' : base;
1851
+ return titleText(data?.dryRun ? `${title}(预览)` : title);
1852
+ }
1853
+
1854
+ function stripAnsi(value) {
1855
+ return String(value ?? '').replace(/\u001b\[[0-9;]*m/g, '');
1856
+ }
1857
+
1858
+ function charDisplayWidth(char) {
1859
+ const code = char.codePointAt(0);
1860
+ if (code === 0) return 0;
1861
+ if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
1862
+ return (
1863
+ code >= 0x1100
1864
+ && (code <= 0x115f
1865
+ || code === 0x2329
1866
+ || code === 0x232a
1867
+ || (code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f)
1868
+ || (code >= 0xac00 && code <= 0xd7a3)
1869
+ || (code >= 0xf900 && code <= 0xfaff)
1870
+ || (code >= 0xfe10 && code <= 0xfe19)
1871
+ || (code >= 0xfe30 && code <= 0xfe6f)
1872
+ || (code >= 0xff00 && code <= 0xff60)
1873
+ || (code >= 0xffe0 && code <= 0xffe6))
1874
+ ) ? 2 : 1;
1875
+ }
1876
+
1877
+ function displayWidth(value) {
1878
+ let width = 0;
1879
+ for (const char of stripAnsi(value)) width += charDisplayWidth(char);
1880
+ return width;
1881
+ }
1882
+
1883
+ function truncateDisplay(value, maxWidth = 32) {
1884
+ const text = String(value ?? '');
1885
+ if (displayWidth(text) <= maxWidth) return text;
1886
+ const marker = '...';
1887
+ const markerWidth = displayWidth(marker);
1888
+ let width = 0;
1889
+ let out = '';
1890
+ for (const char of text) {
1891
+ const charWidth = charDisplayWidth(char);
1892
+ if (width + charWidth + markerWidth > maxWidth) break;
1893
+ out += char;
1894
+ width += charWidth;
1895
+ }
1896
+ return `${out}${marker}`;
1897
+ }
1898
+
1899
+ function wrapDisplay(value, width) {
1900
+ const maxWidth = Math.max(1, width);
1901
+ const text = String(value ?? '');
1902
+ const lines = [];
1903
+ const pushHardWrapped = (chunk, state) => {
1904
+ for (const char of chunk) {
1905
+ const charWidth = charDisplayWidth(char);
1906
+ if (state.lineWidth > 0 && state.lineWidth + charWidth > maxWidth) {
1907
+ lines.push(state.line);
1908
+ state.line = char;
1909
+ state.lineWidth = charWidth;
1910
+ } else {
1911
+ state.line += char;
1912
+ state.lineWidth += charWidth;
1913
+ }
1914
+ }
1915
+ };
1916
+ const tokenBoundary = (char) => /[\s、,,;;|/\\_-]/u.test(char);
1917
+ for (const segment of text.split(/\r?\n/)) {
1918
+ if (!segment) {
1919
+ lines.push('');
1920
+ continue;
1921
+ }
1922
+ const state = { line: '', lineWidth: 0 };
1923
+ let token = '';
1924
+ const flushToken = () => {
1925
+ if (!token) return;
1926
+ const tokenWidth = displayWidth(token);
1927
+ if (tokenWidth > maxWidth) {
1928
+ pushHardWrapped(token, state);
1929
+ } else if (state.lineWidth > 0 && state.lineWidth + tokenWidth > maxWidth) {
1930
+ lines.push(state.line);
1931
+ state.line = token.trimStart();
1932
+ state.lineWidth = displayWidth(state.line);
1933
+ } else {
1934
+ state.line += token;
1935
+ state.lineWidth += tokenWidth;
1936
+ }
1937
+ token = '';
1938
+ };
1939
+ for (const char of segment) {
1940
+ token += char;
1941
+ if (tokenBoundary(char)) flushToken();
1942
+ }
1943
+ flushToken();
1944
+ lines.push(state.line);
1945
+ }
1946
+ return lines.length ? lines : [''];
1947
+ }
1948
+
1949
+ function padDisplay(value, width) {
1950
+ const text = String(value ?? '');
1951
+ return `${text}${' '.repeat(Math.max(0, width - displayWidth(text)))}`;
1952
+ }
1953
+
1954
+ function shouldColorOutput() {
1955
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '0') return false;
1956
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'false') return false;
1957
+ if (String(process.env.FORCE_COLOR || '').trim() && process.env.FORCE_COLOR !== '0') return true;
1958
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '1') return true;
1959
+ if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'true') return true;
1960
+ if (process.env.NO_COLOR) return false;
1961
+ return Boolean(process.stdout.isTTY);
1962
+ }
1963
+
1964
+ function colorText(value, code) {
1965
+ const text = String(value ?? '');
1966
+ if (!shouldColorOutput() || !text) return text;
1967
+ const colorMatch = String(code).match(/38;5;(\d+)/);
1968
+ let rendered = colorMatch ? CHALK.ansi256(Number(colorMatch[1]))(text) : text;
1969
+ if (String(code).includes('36')) rendered = CHALK.cyan(text);
1970
+ if (String(code).split(';').includes('1')) rendered = CHALK.bold(rendered);
1971
+ if (String(code).split(';').includes('2')) rendered = CHALK.dim(rendered);
1972
+ return rendered;
1973
+ }
1974
+
1975
+ const COLOR = {
1976
+ title: '1;38;5;39',
1977
+ accent: '1;38;5;208',
1978
+ section: '1;38;5;75',
1979
+ border: '38;5;238',
1980
+ header: '1;38;5;81',
1981
+ label: '38;5;215',
1982
+ value: '38;5;250',
1983
+ command: '38;5;82',
1984
+ code: '38;5;111',
1985
+ success: '38;5;82',
1986
+ warning: '38;5;220',
1987
+ danger: '38;5;203',
1988
+ muted: '2;38;5;245',
1989
+ };
1990
+
1991
+ function titleText(value) {
1992
+ if (!shouldColorOutput()) return String(value ?? '');
1993
+ return `${colorText('◆', COLOR.accent)} ${colorText(value, COLOR.title)}`;
1994
+ }
1995
+
1996
+ function errorTitleText(value) {
1997
+ if (!shouldColorOutput()) return String(value ?? '');
1998
+ return `${colorText('◆', COLOR.danger)} ${colorText(value, '1;38;5;203')}`;
1999
+ }
2000
+
2001
+ function sectionText(value) {
2002
+ if (!shouldColorOutput()) return String(value ?? '');
2003
+ return `${colorText('▸', COLOR.accent)} ${colorText(value, COLOR.section)}`;
2004
+ }
2005
+
2006
+ function borderText(value) {
2007
+ return colorText(value, COLOR.border);
2008
+ }
2009
+
2010
+ function headerText(value) {
2011
+ return colorText(value, COLOR.header);
2012
+ }
2013
+
2014
+ function labelText(value) {
2015
+ return colorText(value, COLOR.label);
2016
+ }
2017
+
2018
+ function mutedText(value) {
2019
+ return colorText(value, COLOR.muted);
2020
+ }
2021
+
2022
+ function commandText(value) {
2023
+ return colorText(value, COLOR.command);
2024
+ }
2025
+
2026
+ function codeText(value) {
2027
+ return colorText(value, COLOR.code);
2028
+ }
2029
+
2030
+ function semanticText(value) {
2031
+ const text = String(value ?? '');
2032
+ if (!shouldColorOutput() || !text) return text;
2033
+ const plain = stripAnsi(text).trim().toLowerCase();
2034
+ if (/^(是|true|success|succeeded|completed|done|ok|healthy|passed|enabled|active|已配置|已登录|成功)$/.test(plain)) {
2035
+ return colorText(text, COLOR.success);
2036
+ }
2037
+ if (/^(否|false|pending|running|processing|queued|warning|warn|dry-run|dryrun|预览|缓存过期|未登录|未选择|未知)$/.test(plain)) {
2038
+ return colorText(text, COLOR.warning);
2039
+ }
2040
+ if (/^(failed|failure|error|errored|fatal|invalid|denied|rejected|deleted|失败|错误|无效|删除)$/.test(plain)) {
2041
+ return colorText(text, COLOR.danger);
2042
+ }
2043
+ return text;
2044
+ }
2045
+
2046
+ function prettyCellText(value, columnInfo = {}) {
2047
+ const text = String(value ?? '');
2048
+ if (!shouldColorOutput() || !text) return text;
2049
+ const header = stripAnsi(columnInfo.header || '');
2050
+ if (header === '#' || columnInfo.index) return mutedText(text);
2051
+ if (['状态', '当前', '必填', '验证', 'WebP'].includes(header)) return semanticText(text);
2052
+ if (['CLI', '命令', '继续查询', '生成创建写法'].includes(header) || stripAnsi(text).startsWith('--')) return codeText(text);
2053
+ if (stripAnsi(text).startsWith(commandPrefix())) return commandText(text);
2054
+ if (['队列', '排队数', '成功率', '结果数', '任务数', '积分', '数量', '文件数'].includes(header)) return colorText(text, COLOR.accent);
2055
+ return text;
2056
+ }
2057
+
2058
+ function prettyDetailValue(value, label = '') {
2059
+ const text = String(value ?? '');
2060
+ if (!shouldColorOutput() || !text) return text;
2061
+ if (/命令|写法|CLI|JSON|Agent|Dry-run|继续|确认|执行/.test(label) || stripAnsi(text).startsWith(commandPrefix())) {
2062
+ return commandText(text);
2063
+ }
2064
+ if (/状态|验证|当前|必填|已/.test(label)) return semanticText(text);
2065
+ return colorText(text, COLOR.value);
2066
+ }
2067
+
2068
+ function humanizeKey(key) {
2069
+ if (FIELD_LABELS[key]) return FIELD_LABELS[key];
2070
+ return String(key || '')
2071
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
2072
+ .replace(/[_-]+/g, ' ')
2073
+ .replace(/\b\w/g, (char) => char.toUpperCase());
2074
+ }
2075
+
2076
+ function prettyBoolean(value) {
2077
+ return value ? '是' : '否';
2078
+ }
2079
+
2080
+ function prettyValue(value, options = {}) {
2081
+ if (value === undefined || value === null || value === '') return '';
2082
+ if (typeof value === 'boolean') return prettyBoolean(value);
2083
+ if (Array.isArray(value)) {
2084
+ if (!value.length) return '无';
2085
+ if (value.every((item) => !isPlainObject(item) && !Array.isArray(item))) return value.map((item) => prettyValue(item)).join('、');
2086
+ return `${value.length} 项`;
2087
+ }
2088
+ if (isPlainObject(value)) {
2089
+ const summary = renderRecord(compactRecord(value), options.objectKeys || []);
2090
+ return summary || JSON.stringify(value);
2091
+ }
2092
+ return rewriteCommandPrefix(value);
2093
+ }
2094
+
2095
+ function resolveColumnValue(row, column) {
2096
+ if (typeof column.value === 'function') return column.value(row);
2097
+ if (typeof column.key === 'function') return column.key(row);
2098
+ if (Array.isArray(column.key)) {
2099
+ for (const key of column.key) {
2100
+ if (row?.[key] !== undefined && row?.[key] !== null && row?.[key] !== '') return row[key];
2101
+ }
2102
+ return undefined;
2103
+ }
2104
+ return row?.[column.key];
2105
+ }
2106
+
2107
+ function column(header, key, options = {}) {
2108
+ return { header, key, ...options };
2109
+ }
2110
+
2111
+ function joinValueList(value, limit = 6) {
2112
+ if (!Array.isArray(value)) return prettyValue(value);
2113
+ return valueList(value, limit)?.replaceAll('|', '、') || '';
2114
+ }
2115
+
2116
+ const TABLE_COLUMNS = {
2117
+ assets: [
2118
+ column('标签', 'label', { maxWidth: 14 }),
2119
+ column('引用类型', ['referenceType', 'type'], { maxWidth: 16 }),
2120
+ column('用途', 'usage', { maxWidth: 16 }),
2121
+ column('路径', 'assetPath', { maxWidth: 42 }),
2122
+ column('素材 ID', 'assetId', { maxWidth: 22 }),
2123
+ ],
2124
+ buckets: [
2125
+ column('任务类型', 'taskType', { maxWidth: 24 }),
2126
+ column('任务数', 'taskCount', { maxWidth: 10 }),
2127
+ column('成功数', 'successTaskCount', { maxWidth: 10 }),
2128
+ column('结果数', 'resultCount', { maxWidth: 10 }),
2129
+ column('积分', 'pointTotal', { maxWidth: 10 }),
2130
+ ],
2131
+ checks: [
2132
+ column('检查项', 'name', { maxWidth: 18 }),
2133
+ column('状态', 'status', { maxWidth: 12 }),
2134
+ column('说明', 'message', { maxWidth: 58 }),
2135
+ ],
2136
+ commands: [
2137
+ column('命令', 'name', { maxWidth: 34 }),
2138
+ column('领域', 'domain', { maxWidth: 12 }),
2139
+ column('输出', (row) => row.workflow?.outputKind, { maxWidth: 24 }),
2140
+ column('安全', (row) => (row.safety?.safeToAutoRun ? '可自动运行' : '需确认/写入'), { maxWidth: 14 }),
2141
+ column('说明', 'summary', { maxWidth: 44 }),
2142
+ ],
2143
+ domains: [
2144
+ column('领域', 'name', { maxWidth: 14 }),
2145
+ column('命令数', 'commandCount', { maxWidth: 10 }),
2146
+ column('说明', 'description', { maxWidth: 60 }),
2147
+ ],
2148
+ files: [
2149
+ column('文件', ['filePath', 'path', 'fileName'], { maxWidth: 38 }),
2150
+ column('场景', 'sceneType', { maxWidth: 24 }),
2151
+ column('类型', ['mimeType', 'mime'], { maxWidth: 18 }),
2152
+ column('大小', 'size', { maxWidth: 10 }),
2153
+ column('后端路径', ['backendPath', 'url'], { maxWidth: 42 }),
2154
+ ],
2155
+ groups: [
2156
+ column('组 ID', 'groupId', { maxWidth: 24 }),
2157
+ column('平台', 'platform', { maxWidth: 12 }),
2158
+ column('名称', 'name', { maxWidth: 24 }),
2159
+ column('项目名', 'projectName', { maxWidth: 24 }),
2160
+ column('状态', 'status', { maxWidth: 14 }),
2161
+ ],
2162
+ items: [
2163
+ column('ID', ['id', 'entityKey', 'key'], { maxWidth: 24 }),
2164
+ column('类型', ['rowKind', 'assetKind'], { maxWidth: 16 }),
2165
+ column('名称', ['title', 'name', 'displayName'], { maxWidth: 30 }),
2166
+ column('父级', 'parentKey', { maxWidth: 22 }),
2167
+ column('状态', 'status', { maxWidth: 16 }),
2168
+ column('说明', 'description', { maxWidth: 42 }),
2169
+ ],
2170
+ localFiles: [
2171
+ column('文件', ['path', 'filePath', 'fileName'], { maxWidth: 42 }),
2172
+ column('存在', 'exists', { maxWidth: 8 }),
2173
+ column('类型', ['mimeType', 'mime'], { maxWidth: 18 }),
2174
+ column('大小', 'size', { maxWidth: 10 }),
2175
+ ],
2176
+ matches: [
2177
+ column('素材 ID', 'assetId', { maxWidth: 22 }),
2178
+ column('名称', ['name', 'assetName'], { maxWidth: 24 }),
2179
+ column('分数', 'score', { maxWidth: 8 }),
2180
+ column('图片', 'imageUrl', { maxWidth: 42 }),
2181
+ ],
2182
+ models: [
2183
+ column('模型组', 'modelGroupCode', { maxWidth: 34 }),
2184
+ column('名称', ['displayName', 'modelName'], { maxWidth: 24 }),
2185
+ column('说明', 'modelDesc', { maxWidth: 34 }),
2186
+ column('排队数', 'taskQueueNum', { maxWidth: 8 }),
2187
+ column('成功率', (row) => formatPercent(row.successRate), { maxWidth: 8 }),
2188
+ column('输入', (row) => joinValueList(row.inputModes), { maxWidth: 28 }),
2189
+ column('参数', (row) => joinValueList(row.params), { maxWidth: 24 }),
2190
+ column('素材', (row) => joinValueList(row.resourceParams), { maxWidth: 24 }),
2191
+ ],
2192
+ params: [
2193
+ column('参数', 'key', { maxWidth: 22 }),
2194
+ column('类型', 'valueType', { maxWidth: 12 }),
2195
+ column('可选值', (row) => joinValueList(row.values, 8), { maxWidth: 42 }),
2196
+ column('默认', 'defaultValue', { maxWidth: 14 }),
2197
+ column('必填', 'required', { maxWidth: 8 }),
2198
+ ],
2199
+ projectGroups: [
2200
+ column('项目组编号', 'projectGroupNo', { maxWidth: 24 }),
2201
+ column('名称', 'projectGroupName', { maxWidth: 28 }),
2202
+ column('当前', 'isSelected', { maxWidth: 8 }),
2203
+ column('预算余额', ['projectBudgetBalance', 'projectPointBalance'], { maxWidth: 12 }),
2204
+ column('成员', 'memberCount', { maxWidth: 8 }),
2205
+ ],
2206
+ records: [
2207
+ column('任务 ID', ['taskId', 'publicId', 'remoteTaskId'], { maxWidth: 28 }),
2208
+ column('类型', 'taskType', { maxWidth: 20 }),
2209
+ column('状态', ['taskStatus', 'status'], { maxWidth: 16 }),
2210
+ column('模型', 'modelGroupCode', { maxWidth: 30 }),
2211
+ column('结果数', 'resultCount', { maxWidth: 8 }),
2212
+ column('记录时间', ['recordedAt', 'gmtCreate', 'createdAt'], { maxWidth: 22 }),
2213
+ ],
2214
+ resources: [
2215
+ column('模式', 'mode', { maxWidth: 16 }),
2216
+ column('媒体', 'mediaType', { maxWidth: 10 }),
2217
+ column('用途', (row) => joinValueList(row.usage), { maxWidth: 26 }),
2218
+ column('输入形态', (row) => textValueShapes(row.valueShapes)?.replaceAll('|', '、'), { maxWidth: 28 }),
2219
+ column('格式', (row) => textResourceFormats(row)?.replaceAll('|', '、'), { maxWidth: 28 }),
2220
+ column('文件数', (row) => textFileCount(row), { maxWidth: 10 }),
2221
+ column('WebP', (row) => textWebpInput(row), { maxWidth: 18 }),
2222
+ ],
2223
+ results: [
2224
+ column('序号', 'inputIndex', { maxWidth: 8 }),
2225
+ column('状态', ['status', 'taskStatus'], { maxWidth: 16 }),
2226
+ column('任务 ID', 'taskId', { maxWidth: 28 }),
2227
+ column('模型/主体', ['modelGroupCode', 'modelCode', 'name'], { maxWidth: 30 }),
2228
+ column('说明', ['message', 'errorMessage', 'prompt'], { maxWidth: 42 }),
2229
+ ],
2230
+ subjects: [
2231
+ column('主体 ID', ['elementId', 'subjectId'], { maxWidth: 24 }),
2232
+ column('名称', 'name', { maxWidth: 24 }),
2233
+ column('模型', 'modelCode', { maxWidth: 14 }),
2234
+ column('外部 ID', 'externalId', { maxWidth: 24 }),
2235
+ column('状态', ['status', 'taskStatus'], { maxWidth: 14 }),
2236
+ ],
2237
+ tasks: [
2238
+ column('任务 ID', ['taskId', 'publicId', 'remoteTaskId'], { maxWidth: 28 }),
2239
+ column('类型', 'taskType', { maxWidth: 20 }),
2240
+ column('状态', 'taskStatus', { maxWidth: 16 }),
2241
+ column('模型', 'modelGroupCode', { maxWidth: 30 }),
2242
+ column('结果数', 'resultCount', { maxWidth: 8 }),
2243
+ column('创建时间', 'gmtCreate', { maxWidth: 22 }),
2244
+ ],
2245
+ teams: [
2246
+ column('团队 ID', 'groupId', { maxWidth: 22 }),
2247
+ column('团队名称', 'groupName', { maxWidth: 28 }),
2248
+ column('当前', ['currentGroup', 'isSelected'], { maxWidth: 8 }),
2249
+ column('角色', 'role', { maxWidth: 14 }),
2250
+ ],
2251
+ users: [
2252
+ column('用户 ID', 'userId', { maxWidth: 22 }),
2253
+ column('用户名', 'userName', { maxWidth: 24 }),
2254
+ column('角色', 'role', { maxWidth: 14 }),
2255
+ column('当前', ['currentGroup', 'isCheck'], { maxWidth: 8 }),
2256
+ ],
2257
+ voices: [
2258
+ column('音色记录 ID', ['voiceRecordId', 'reqTaskId'], { maxWidth: 26 }),
2259
+ column('名称', 'name', { maxWidth: 24 }),
2260
+ column('音色 ID', ['voiceId', 'externalId'], { maxWidth: 24 }),
2261
+ column('状态', ['status', 'taskStatus'], { maxWidth: 14 }),
2262
+ ],
2263
+ };
2264
+
2265
+ function inferTableColumns(rows = []) {
2266
+ const first = rows.find((row) => isPlainObject(row));
2267
+ if (!first) return [column('值', (row) => prettyValue(row), { maxWidth: 70 })];
2268
+ const preferred = [
2269
+ 'id',
2270
+ 'taskId',
2271
+ 'publicId',
2272
+ 'modelGroupCode',
2273
+ 'projectGroupNo',
2274
+ 'projectId',
2275
+ 'rowKind',
2276
+ 'entityKey',
2277
+ 'name',
2278
+ 'displayName',
2279
+ 'title',
2280
+ 'status',
2281
+ 'taskStatus',
2282
+ 'resultCount',
2283
+ 'errorMessage',
2284
+ ];
2285
+ const keys = [
2286
+ ...preferred.filter((key) => first[key] !== undefined),
2287
+ ...Object.keys(first).filter((key) => !preferred.includes(key) && !Array.isArray(first[key]) && !isPlainObject(first[key])),
2288
+ ].slice(0, 6);
2289
+ return keys.map((key) => column(humanizeKey(key), key, { maxWidth: key.toLowerCase().includes('url') ? 42 : 28 }));
2290
+ }
2291
+
2292
+ function renderPrettyTable(rows = [], columns = [], options = {}) {
2293
+ const tableRows = Array.isArray(rows) ? rows : [];
2294
+ if (!tableRows.length) return [` ${mutedText(options.emptyText || '无记录')}`];
2295
+ const rawCols = columns.length ? columns : inferTableColumns(tableRows);
2296
+ const rawRows = tableRows.map((row, index) => rawCols.map((col) => {
2297
+ const raw = col.index ? index + 1 : resolveColumnValue(row, col);
2298
+ const value = String(prettyValue(raw)).replace(/\r\n/g, '\n');
2299
+ return value;
2300
+ }));
2301
+ const visibleColumnIndexes = rawCols
2302
+ .map((col, index) => ({ col, index }))
2303
+ .filter(({ col, index }) => col.always || rawCols.length === 1 || rawRows.some((row) => row[index] !== ''))
2304
+ .map(({ index }) => index);
2305
+ if (!visibleColumnIndexes.length) {
2306
+ visibleColumnIndexes.push(...rawCols.map((_, index) => index));
2307
+ }
2308
+ const cols = visibleColumnIndexes.map((index) => rawCols[index]);
2309
+ const headerCells = cols.map((col) => col.header);
2310
+ const bodyRows = rawRows.map((row) => visibleColumnIndexes.map((index) => row[index]));
2311
+ const widths = cols.map((col, colIndex) => {
2312
+ const values = [headerCells[colIndex], ...bodyRows.map((row) => row[colIndex])];
2313
+ const max = Math.max(...values.map(displayWidth));
2314
+ return Math.max(col.minWidth || 4, Math.min(col.maxWidth || 30, max));
2315
+ });
2316
+ const renderedRows = bodyRows.map((row, rowIndex) => row.map((cell, colIndex) => {
2317
+ const wrapped = wrapDisplay(cell, widths[colIndex]).join('\n');
2318
+ return prettyCellText(wrapped, { ...cols[colIndex], rowIndex });
2319
+ }));
2320
+ const table = new Table({
2321
+ head: headerCells.map((cell) => headerText(cell)),
2322
+ colWidths: widths.map((width) => width + 2),
2323
+ wordWrap: false,
2324
+ truncate: '',
2325
+ style: {
2326
+ head: [],
2327
+ border: [],
2328
+ compact: false,
2329
+ 'padding-left': 1,
2330
+ 'padding-right': 1,
2331
+ },
2332
+ });
2333
+ table.push(...renderedRows);
2334
+ return table.toString()
2335
+ .split('\n')
2336
+ .map((line) => line.replace(/[┌┬┐├┼┤└┴┘│─]/g, (char) => borderText(char)));
2337
+ }
2338
+
2339
+ function sectionTitle(lines, title) {
2340
+ if (lines.length && lines[lines.length - 1] !== '') lines.push('');
2341
+ lines.push(sectionText(title));
2342
+ }
2343
+
2344
+ function detailRows(record, preferredKeys = [], excludedKeys = [], options = {}) {
2345
+ if (!isPlainObject(record)) return [];
2346
+ const excluded = new Set(excludedKeys);
2347
+ const preferred = preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined && record[key] !== null && record[key] !== '');
2348
+ const fallback = options.includeRest
2349
+ ? Object.keys(record).filter((key) => {
2350
+ if (excluded.has(key) || preferredKeys.includes(key)) return false;
2351
+ const value = record[key];
2352
+ if (value === undefined || value === null || value === '') return false;
2353
+ if (Array.isArray(value) || isPlainObject(value)) return false;
2354
+ return true;
2355
+ })
2356
+ : [];
2357
+ const keys = [...preferred, ...fallback];
2358
+ return keys.map((key) => ({ label: humanizeKey(key), value: record[key] }));
2359
+ }
2360
+
2361
+ function renderDetails(lines, title, record, preferredKeys = [], excludedKeys = [], options = {}) {
2362
+ const rows = detailRows(record, preferredKeys, excludedKeys, options);
2363
+ if (!rows.length) return;
2364
+ sectionTitle(lines, title);
2365
+ const labelWidth = Math.min(22, Math.max(...rows.map((row) => displayWidth(row.label))));
2366
+ for (const row of rows) {
2367
+ const value = String(prettyValue(row.value)).replace(/\s*\r?\n\s*/g, ' ');
2368
+ const valueLines = wrapDisplay(value, options.valueWidth || 96);
2369
+ for (const [index, valueLine] of valueLines.entries()) {
2370
+ const label = index === 0 ? row.label : '';
2371
+ lines.push(` ${labelText(padDisplay(label, labelWidth))} ${prettyDetailValue(valueLine, row.label)}`);
2372
+ }
2373
+ }
2374
+ }
2375
+
2376
+ function renderArraySection(lines, title, rows, listKey, limit = Number.POSITIVE_INFINITY) {
2377
+ if (!Array.isArray(rows) || !rows.length) return;
2378
+ const visibleRows = rows.slice(0, Number.isFinite(limit) ? limit : rows.length);
2379
+ sectionTitle(lines, `${title}(${rows.length})`);
2380
+ const columns = Array.isArray(listKey) ? listKey : (TABLE_COLUMNS[listKey] || inferTableColumns(visibleRows));
2381
+ lines.push(...renderPrettyTable(visibleRows, columns));
2382
+ if (rows.length > visibleRows.length) lines.push(` ${mutedText(`还有 ${rows.length - visibleRows.length} 条未展示;使用 --all 或 -f json 查看完整数据。`)}`);
2383
+ }
2384
+
2385
+ function renderScalarList(lines, title, values = [], limit = 8) {
2386
+ if (!Array.isArray(values) || !values.length) return;
2387
+ sectionTitle(lines, `${title}(${values.length})`);
2388
+ for (const [index, value] of values.slice(0, limit).entries()) {
2389
+ lines.push(` ${mutedText(`${index + 1}.`)} ${prettyDetailValue(prettyValue(value), title)}`);
2390
+ }
2391
+ if (values.length > limit) lines.push(` ${mutedText(`还有 ${values.length - limit} 项未展示;使用 -f json 查看完整数据。`)}`);
2392
+ }
2393
+
2394
+ function namedListTitle(listKey) {
2395
+ return {
2396
+ assets: '素材',
2397
+ buckets: '用量分组',
2398
+ checks: '检查结果',
2399
+ commands: '命令',
2400
+ domains: '命令领域',
2401
+ files: '文件',
2402
+ groups: '素材组',
2403
+ items: '记录',
2404
+ localFiles: '本地文件',
2405
+ matches: '匹配项',
2406
+ models: '模型',
2407
+ params: '参数',
2408
+ projectGroups: '项目组',
2409
+ records: '记录',
2410
+ resources: '素材约束',
2411
+ results: '结果',
2412
+ subjects: '主体',
2413
+ tasks: '任务',
2414
+ teams: '团队',
2415
+ users: '成员',
2416
+ voices: '音色',
2417
+ }[listKey] || humanizeKey(listKey);
2418
+ }
2419
+
2420
+ function localizedTaskKind(value) {
2421
+ if (value === 'image' || value === 'IMAGE_CREATE') return '图片生成';
2422
+ if (value === 'video' || value === 'VIDEO_CREATE') return '视频生成';
2423
+ return prettyValue(value);
2424
+ }
2425
+
2426
+ function localizedInputMode(value, taskKind = '') {
2427
+ const mode = String(value || '');
2428
+ const isVideo = taskKind === 'video' || taskKind === 'VIDEO_CREATE';
2429
+ const labels = {
2430
+ prompt_only: isVideo ? '文本生成视频' : '文本生成图片',
2431
+ reference: isVideo ? '参考素材生成' : '参考图生成',
2432
+ frames: '首帧/尾帧生成',
2433
+ storyboard: '分镜多镜头',
2434
+ keyframe: '关键帧生成',
2435
+ };
2436
+ return labels[mode] || mode;
2437
+ }
2438
+
2439
+ function localizedParamKey(value) {
2440
+ const labels = {
2441
+ ratio: '宽高比',
2442
+ quality: '清晰度',
2443
+ generateNum: '生成数量',
2444
+ generate_num: '生成数量',
2445
+ duration: '时长',
2446
+ generated_time: '时长',
2447
+ needAudio: '生成音频',
2448
+ need_audio: '生成音频',
2449
+ prompt_optimizer: '提示词增强',
2450
+ };
2451
+ return labels[value] || value;
2452
+ }
2453
+
2454
+ function localizedParamType(value) {
2455
+ const labels = {
2456
+ enum: '枚举选择',
2457
+ text: '文本',
2458
+ string: '文本',
2459
+ number: '数字',
2460
+ integer: '整数',
2461
+ boolean: '开关',
2462
+ bool: '开关',
2463
+ };
2464
+ return labels[String(value || '').toLowerCase()] || value;
2465
+ }
2466
+
2467
+ function cliFlagForParam(key) {
2468
+ const flags = {
2469
+ ratio: '--ratio',
2470
+ quality: '--quality',
2471
+ generateNum: '--generate-num',
2472
+ generate_num: '--generate-num',
2473
+ duration: '--duration',
2474
+ generated_time: '--duration',
2475
+ needAudio: '--need-audio',
2476
+ need_audio: '--need-audio',
2477
+ prompt: '--prompt',
2478
+ };
2479
+ return flags[key] || (key ? `--model-param ${key}=...` : '');
2480
+ }
2481
+
2482
+ function localizedResourceParam(value) {
2483
+ const labels = {
2484
+ iref: '参考图',
2485
+ cref: '角色参考',
2486
+ sref: '风格参考',
2487
+ resources: '统一素材',
2488
+ frames: '首帧/尾帧',
2489
+ multi_param: '多帧参数',
2490
+ multi_prompt: '多段提示词',
2491
+ };
2492
+ return labels[value] || value;
2493
+ }
2494
+
2495
+ function localizedMedia(value) {
2496
+ const labels = {
2497
+ IMAGE: '图片',
2498
+ VIDEO: '视频',
2499
+ AUDIO: '音频',
2500
+ SUBJECT: '主体',
2501
+ };
2502
+ return labels[String(value || '').toUpperCase()] || prettyValue(value);
2503
+ }
2504
+
2505
+ function localizedUsage(value) {
2506
+ const labels = {
2507
+ reference: '参考素材',
2508
+ first_frame: '首帧',
2509
+ last_frame: '尾帧',
2510
+ keyframe: '关键帧',
2511
+ prompt_only: '纯文本',
2512
+ storyboard: '故事板',
2513
+ };
2514
+ if (Array.isArray(value)) return value.map((item) => localizedUsage(item)).join('、');
2515
+ return labels[value] || prettyValue(value);
2516
+ }
2517
+
2518
+ function localizedResourcePurpose(item = {}, taskKind = '') {
2519
+ const modeText = localizedInputMode(item.mode, taskKind);
2520
+ const usageText = localizedUsage(item.usage);
2521
+ if (!usageText || usageText === '参考素材') return modeText;
2522
+ if (!modeText) return usageText;
2523
+ return `${modeText}(${usageText})`;
2524
+ }
2525
+
2526
+ function localizedResourceTarget(value, limits = {}) {
2527
+ const text = String(value || '');
2528
+ const match = text.match(/^resource\.([^.]+)\.([^.]+)(?:\.count)?$/);
2529
+ if (!match) return '';
2530
+ const [, media, usage] = match;
2531
+ const mediaType = String(limits.mediaType || media).toUpperCase();
2532
+ const usageText = localizedUsage(limits.usage || usage);
2533
+ const mediaText = localizedMedia(mediaType);
2534
+ if (usage === 'first_frame' || usage === 'last_frame') return `${usageText}${mediaText}`;
2535
+ if (usage === 'frame') return `首帧/尾帧${mediaText}`;
2536
+ if (usage === 'reference') return `参考${mediaText}`;
2537
+ return `${usageText}${mediaText}`;
2538
+ }
2539
+
2540
+ function localizedValueShape(value) {
2541
+ const labels = {
2542
+ local_file: '本地文件',
2543
+ file: '本地文件',
2544
+ http_url: 'URL',
2545
+ url: 'URL',
2546
+ backendPath: '平台素材路径',
2547
+ platformPath: '平台素材路径',
2548
+ asset_id: '资产 ID',
2549
+ asset: '资产 ID',
2550
+ };
2551
+ return labels[value] || value;
2552
+ }
2553
+
2554
+ function localizedInputShapes(values = []) {
2555
+ const items = Array.isArray(values) ? values : [];
2556
+ if (!items.length) return '';
2557
+ return items.map(localizedValueShape).join('、');
2558
+ }
2559
+
2560
+ function localizedFormats(item = {}) {
2561
+ const formats = textResourceFormats(item);
2562
+ return formats ? formats.replaceAll('|', '、') : '';
2563
+ }
2564
+
2565
+ function localizedFileCount(item = {}) {
2566
+ const value = textFileCount(item);
2567
+ if (!value) return '';
2568
+ if (value.startsWith('<=')) return `最多 ${value.slice(2)} 个`;
2569
+ if (value.startsWith('>=')) return `至少 ${value.slice(2)} 个`;
2570
+ if (value.includes('..')) return `${value.replace('..', '-')} 个`;
2571
+ return `${value} 个`;
2572
+ }
2573
+
2574
+ function localizedItemCount(item = {}) {
2575
+ const value = textItemCount(item);
2576
+ if (!value) return '';
2577
+ if (value.startsWith('<=')) return `最多 ${value.slice(2)} 项`;
2578
+ if (value.startsWith('>=')) return `至少 ${value.slice(2)} 项`;
2579
+ if (value.includes('..')) return `${value.replace('..', '-')} 项`;
2580
+ return `${value} 项`;
2581
+ }
2582
+
2583
+ function formatKb(value) {
2584
+ const kb = Number(value);
2585
+ if (!Number.isFinite(kb)) return '';
2586
+ if (kb >= 1024) {
2587
+ const mb = kb / 1024;
2588
+ return `${Number.isInteger(mb) ? mb : mb.toFixed(1)} MB`;
2589
+ }
2590
+ return `${kb} KB`;
2591
+ }
2592
+
2593
+ function formatMs(value) {
2594
+ const ms = Number(value);
2595
+ if (!Number.isFinite(ms)) return '';
2596
+ const seconds = ms / 1000;
2597
+ if (seconds >= 1) return `${Number.isInteger(seconds) ? seconds : seconds.toFixed(1)} 秒`;
2598
+ return `${ms} 毫秒`;
2599
+ }
2600
+
2601
+ function localizedDurationRange(item = {}) {
2602
+ if (item.minDurationMs != null && item.maxDurationMs != null) return `${formatMs(item.minDurationMs)}-${formatMs(item.maxDurationMs)}`;
2603
+ if (item.maxDurationMs != null) return `最多 ${formatMs(item.maxDurationMs)}`;
2604
+ if (item.minDurationMs != null) return `至少 ${formatMs(item.minDurationMs)}`;
2605
+ return '';
2606
+ }
2607
+
2608
+ function localizedTotalDuration(item = {}) {
2609
+ if (item.maxTotalDurationMs == null) return '';
2610
+ return `最多 ${formatMs(item.maxTotalDurationMs)}`;
2611
+ }
2612
+
2613
+ function localizedWebpPolicy(item = {}) {
2614
+ const value = textWebpInput(item);
2615
+ if (value === 'accepted') return '支持 WebP';
2616
+ if (String(value || '').startsWith('autoConvertTo')) return `WebP 自动转 ${String(value).replace('autoConvertTo', '')}`;
2617
+ if (value === 'unsupported') return '不支持 WebP';
2618
+ return '';
2619
+ }
2620
+
2621
+ function localizedConditionKey(value) {
2622
+ const resourceTarget = localizedResourceTarget(value);
2623
+ if (resourceTarget && String(value || '').endsWith('.count')) return `${resourceTarget}数量`;
2624
+ if (resourceTarget) return resourceTarget;
2625
+ const labels = {
2626
+ 'resource.image.frame': '使用首帧/尾帧图片',
2627
+ generatedMode: '生成方式',
2628
+ ratio: '宽高比',
2629
+ quality: '清晰度',
2630
+ duration: '时长',
2631
+ generated_time: '时长',
2632
+ };
2633
+ return labels[value] || localizedParamKey(value);
2634
+ }
2635
+
2636
+ function localizedConditionValue(value) {
2637
+ if (value === '__NOT_EMPTY__') return '已提供';
2638
+ if (value === '__EMPTY__') return '未提供';
2639
+ const labels = {
2640
+ multi_param: '参考素材生成',
2641
+ frames: '首帧/尾帧生成',
2642
+ multi_prompt: '分镜多镜头',
2643
+ prompt_only: '纯文本生成',
2644
+ };
2645
+ if (labels[value]) return labels[value];
2646
+ return prettyValue(value);
2647
+ }
2648
+
2649
+ function localizedCountRange(item = {}) {
2650
+ const value = textCountRange(item);
2651
+ if (!value) return '';
2652
+ if (String(value).startsWith('>=')) return `至少 ${String(value).slice(2)} 个`;
2653
+ if (String(value).startsWith('<=')) return `最多 ${String(value).slice(2)} 个`;
2654
+ if (String(value).includes('..')) return `${String(value).replace('..', '-')} 个`;
2655
+ return `${value} 个`;
2656
+ }
2657
+
2658
+ function localizedConditions(conditions = []) {
2659
+ if (!Array.isArray(conditions) || !conditions.length) return '';
2660
+ return conditions
2661
+ .map((item) => {
2662
+ const key = item.key || item.configCode;
2663
+ if (!key) return null;
2664
+ if (item.meaning === 'count') return `${localizedConditionKey(key)}:${localizedCountRange(item)}`;
2665
+ if (key === 'resource.image.frame' && item.meaning === 'present') return '首尾帧模式';
2666
+ if (key === 'generatedMode' && item.value === 'multi_param') return '参考素材生成模式';
2667
+ if (key === 'generatedMode' && item.value === 'frames') return '首帧/尾帧模式';
2668
+ if (key === 'generatedMode' && item.value === 'multi_prompt') return '分镜多镜头模式';
2669
+ return `${localizedConditionKey(key)}${localizedConditionValue(item.value) ? `:${localizedConditionValue(item.value)}` : ''}`;
2670
+ })
2671
+ .filter(Boolean)
2672
+ .join(';');
2673
+ }
2674
+
2675
+ function constraintHasCondition(item = {}, key, value = undefined) {
2676
+ const conditions = Array.isArray(item.conditions) ? item.conditions : [];
2677
+ return conditions.some((condition) => (
2678
+ (condition.key || condition.configCode) === key
2679
+ && (value === undefined || condition.value === value)
2680
+ ));
2681
+ }
2682
+
2683
+ function localizedConstraintResult(item = {}) {
2684
+ const target = item.target || item.targetConfigCode;
2685
+ const flag = cliFlagForParam(target);
2686
+ if (item.effect === 'resource_limit_overrides') {
2687
+ const limits = item.limits || {};
2688
+ const subject = localizedResourceTarget(target, limits) || '素材';
2689
+ const parts = [];
2690
+ const count = localizedFileCount(limits);
2691
+ const duration = localizedDurationRange(limits);
2692
+ const totalDuration = localizedTotalDuration(limits);
2693
+ const itemCount = localizedItemCount(limits);
2694
+ if (count) parts.push(`${subject}${count}`);
2695
+ if (duration) parts.push(`${subject}单个素材${duration}`);
2696
+ if (totalDuration) parts.push(`${subject}总时长${totalDuration}`);
2697
+ if (limits.maxSizeKB != null) parts.push(`${subject}单文件最多 ${formatKb(limits.maxSizeKB)}`);
2698
+ if (itemCount) parts.push(`${subject}${itemCount}`);
2699
+ return parts.join(';') || '素材限制随条件变化';
2700
+ }
2701
+ if (item.effect === 'no_selectable_values') {
2702
+ if (target === 'ratio' && constraintHasCondition(item, 'resource.image.frame')) {
2703
+ return `首尾帧模式不能手选比例;比例由输入图片决定,勿传 ${flag || localizedParamKey(target)}。`;
2704
+ }
2705
+ if (target === 'ratio') return `${flag || localizedParamKey(target)} 由素材决定,创建命令里不要再传。`;
2706
+ return `${flag || localizedParamKey(target)} 触发后没有可选值,创建命令里不要再传。`;
2707
+ }
2708
+ if (Array.isArray(item.allowValues) && item.allowValues.length) {
2709
+ if (target === 'ratio' && constraintHasCondition(item, 'generatedMode', 'multi_param')) {
2710
+ return `比例只能选:${item.allowValues.join('、')}`;
2711
+ }
2712
+ return `只能选择:${item.allowValues.join('、')}`;
2713
+ }
2714
+ return item.effect;
2715
+ }
2716
+
2717
+ function localizedList(values = [], mapper = (value) => value) {
2718
+ if (!Array.isArray(values) || !values.length) return '';
2719
+ return values.map(mapper).filter(Boolean).join('、');
2720
+ }
2721
+
2722
+ function pushReadableRecord(lines, record = {}, fields = [], options = {}) {
2723
+ const rows = fields
2724
+ .map(([label, value]) => [label, value])
2725
+ .filter(([, value]) => value !== undefined && value !== null && value !== '');
2726
+ if (!rows.length) return;
2727
+ const labelWidth = Math.max(...rows.map(([label]) => displayWidth(label)));
2728
+ const indent = options.indent || ' ';
2729
+ for (const [label, value] of rows) {
2730
+ lines.push(`${indent}${padDisplay(label, labelWidth)} ${prettyValue(value)}`);
2731
+ }
2732
+ }
2733
+
2734
+ function formatPrettyModelList(commandName, data = {}, context = {}) {
2735
+ const normalized = normalizeOutputData(commandName, data);
2736
+ const rows = Array.isArray(normalized.models) ? normalized.models : [];
2737
+ const taskKind = commandName === 'model video-models' ? 'video' : 'image';
2738
+ const limit = listLimitFor(commandName, context, rows);
2739
+ const visibleRows = rows.slice(0, Number.isFinite(limit) ? limit : rows.length);
2740
+ const lines = [prettyTitle(commandName, 'model_list', normalized), ''];
2741
+ sectionTitle(lines, `模型(${rows.length})`);
2742
+ if (!visibleRows.length) {
2743
+ lines.push(` ${mutedText('无匹配模型')}`);
2744
+ } else {
2745
+ const tableRows = visibleRows.map((row, index) => ({
2746
+ index: index + 1,
2747
+ displayName: row.displayName,
2748
+ note: row.modelDesc && row.modelDesc !== row.displayName ? row.modelDesc : row.provider,
2749
+ modelGroupCode: row.modelGroupCode,
2750
+ inputModesText: localizedList(row.inputModes, (item) => localizedInputMode(item, taskKind)),
2751
+ paramsText: localizedList(row.params, localizedParamKey),
2752
+ resourceText: localizedList(row.resourceParams, localizedResourceParam),
2753
+ queue: row.taskQueueNum ?? '',
2754
+ successRateText: formatPercent(row.successRate),
2755
+ }));
2756
+ lines.push(...renderPrettyTable(tableRows, [
2757
+ column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
2758
+ column('模型', 'displayName', { minWidth: 10, maxWidth: 18, always: true }),
2759
+ column('来源/备注', 'note', { minWidth: 10, maxWidth: 20 }),
2760
+ column('模型组编码', 'modelGroupCode', { minWidth: 24, maxWidth: 36, always: true }),
2761
+ column('支持生成', 'inputModesText', { minWidth: 12, maxWidth: 22 }),
2762
+ column('可调参数', 'paramsText', { minWidth: 12, maxWidth: 22 }),
2763
+ column('素材能力', 'resourceText', { minWidth: 8, maxWidth: 16 }),
2764
+ column('排队数', 'queue', { minWidth: 4, maxWidth: 6 }),
2765
+ column('成功率', 'successRateText', { minWidth: 4, maxWidth: 8 }),
2766
+ ]));
2767
+ }
2768
+ if (rows.length > visibleRows.length) {
2769
+ lines.push(` ${mutedText(`还有 ${rows.length - visibleRows.length} 个模型未展示;运行 ${context.moreCommand || `${commandPrefix()} ${commandName} --all`} 查看全部。`)}`);
2770
+ }
2771
+ renderNextActions(lines, collectPrettyActions(commandName, normalized, context), '先查看候选模型参数和素材约束,再推荐或进入 fee/dry-run。');
2772
+ return `${lines.join('\n')}\n`;
2773
+ }
2774
+
2775
+ function renderModelParams(lines, params = []) {
2776
+ const rows = Array.isArray(params) ? params : [];
2777
+ sectionTitle(lines, `参数(${rows.length})`);
2778
+ if (!rows.length) {
2779
+ lines.push(` ${mutedText('无可调参数')}`);
2780
+ return;
2781
+ }
2782
+ const tableRows = rows.map((param) => ({
2783
+ name: param.label || param.name || localizedParamKey(param.key),
2784
+ flag: cliFlagForParam(param.key),
2785
+ type: localizedParamType(param.valueType || param.type),
2786
+ values: Array.isArray(param.values) ? param.values.join('、') : '',
2787
+ defaultValue: param.defaultValue,
2788
+ required: param.required === undefined ? '' : prettyBoolean(param.required),
2789
+ limit: param.maxLength ? `最长 ${param.maxLength} 字符` : '',
2790
+ }));
2791
+ lines.push(...renderPrettyTable(tableRows, [
2792
+ column('参数', 'name', { minWidth: 8, maxWidth: 18, always: true }),
2793
+ column('CLI', 'flag', { minWidth: 8, maxWidth: 16 }),
2794
+ column('类型', 'type', { maxWidth: 10 }),
2795
+ column('可选值', 'values', { minWidth: 12, maxWidth: 36 }),
2796
+ column('默认', 'defaultValue', { maxWidth: 12 }),
2797
+ column('必填', 'required', { maxWidth: 6 }),
2798
+ column('限制', 'limit', { maxWidth: 16 }),
2799
+ ]));
2800
+ }
2801
+
2802
+ function renderModelResources(lines, resources = [], taskKind = '') {
2803
+ const rows = Array.isArray(resources) ? resources : [];
2804
+ sectionTitle(lines, `素材约束(${rows.length})`);
2805
+ if (!rows.length) {
2806
+ lines.push(` ${mutedText('不需要素材')}`);
2807
+ return;
2808
+ }
2809
+ const tableRows = rows.map((item, index) => ({
2810
+ index: index + 1,
2811
+ purpose: localizedResourcePurpose(item, taskKind),
2812
+ media: localizedMedia(item.mediaType),
2813
+ input: localizedInputShapes(item.valueShapes || item.sources),
2814
+ formats: localizedFormats(item),
2815
+ count: localizedFileCount(item),
2816
+ limits: [
2817
+ item.maxSizeKB != null ? `单文件最多 ${formatKb(item.maxSizeKB)}` : '',
2818
+ localizedDurationRange(item) ? `单个素材 ${localizedDurationRange(item)}` : '',
2819
+ localizedTotalDuration(item) ? `总时长 ${localizedTotalDuration(item)}` : '',
2820
+ localizedItemCount(item) ? `条目 ${localizedItemCount(item)}` : '',
2821
+ localizedWebpPolicy(item),
2822
+ item.supportLastFrameOnly === undefined ? '' : `仅尾帧:${prettyBoolean(item.supportLastFrameOnly)}`,
2823
+ ].filter(Boolean).join(';'),
2824
+ }));
2825
+ lines.push(...renderPrettyTable(tableRows, [
2826
+ column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
2827
+ column('用途', 'purpose', { minWidth: 14, maxWidth: 24, always: true }),
2828
+ column('媒体', 'media', { maxWidth: 6, always: true }),
2829
+ column('输入方式', 'input', { minWidth: 16, maxWidth: 40 }),
2830
+ column('文件格式', 'formats', { minWidth: 10, maxWidth: 28 }),
2831
+ column('数量', 'count', { maxWidth: 10 }),
2832
+ column('其他限制', 'limits', { minWidth: 12, maxWidth: 30 }),
2833
+ ]));
2834
+ }
2835
+
2836
+ function renderModelConstraints(lines, constraints = []) {
2837
+ const rows = Array.isArray(constraints) ? constraints : [];
2838
+ sectionTitle(lines, `联动约束(${rows.length})`);
2839
+ if (!rows.length) {
2840
+ lines.push(` ${mutedText('无联动约束')}`);
2841
+ return;
2842
+ }
2843
+ const tableRows = rows.map((item, index) => ({
2844
+ index: index + 1,
2845
+ target: localizedResourceTarget(item.target, item.limits) || localizedParamKey(item.target || item.targetConfigCode || '约束'),
2846
+ condition: localizedConditions(item.conditions),
2847
+ result: localizedConstraintResult(item),
2848
+ note: item.name,
2849
+ }));
2850
+ lines.push(...renderPrettyTable(tableRows, [
2851
+ column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
2852
+ column('影响参数', 'target', { minWidth: 8, maxWidth: 14, always: true }),
2853
+ column('条件', 'condition', { minWidth: 16, maxWidth: 28 }),
2854
+ column('结果', 'result', { minWidth: 18, maxWidth: 42, always: true }),
2855
+ column('说明', 'note', { maxWidth: 24 }),
2856
+ ]));
2857
+ }
2858
+
2859
+ function listLimitFor(commandName, context, rows) {
2860
+ if (context.listLimit === Number.POSITIVE_INFINITY) return rows.length;
2861
+ if (Number.isFinite(context.listLimit)) return Math.max(0, context.listLimit);
2862
+ if (commandName === 'schema') return Number.POSITIVE_INFINITY;
2863
+ return Number.POSITIVE_INFINITY;
2864
+ }
2865
+
2866
+ function collectListKeys(normalized, primaryKey) {
2867
+ const keys = [];
2868
+ if (primaryKey && Array.isArray(normalized?.[primaryKey])) keys.push(primaryKey);
2869
+ for (const [key, value] of Object.entries(normalized || {})) {
2870
+ if (keys.includes(key)) continue;
2871
+ if (['resultUrls', 'originUrls'].includes(key)) continue;
2872
+ if (Array.isArray(value)) keys.push(key);
2873
+ }
2874
+ return keys;
2875
+ }
2876
+
2877
+ function collectPrettyActions(commandName, normalized, context = {}) {
2878
+ const actions = [];
2879
+ const add = (label, value) => {
2880
+ if (value === undefined || value === null || value === '') return;
2881
+ actions.push({ label, value: rewriteCommandPrefix(value) });
2882
+ };
2883
+ if (normalized?.nextCommand) add('继续查询', normalized.nextCommand);
2884
+ if (normalized?.statusCommand) add('状态查询', normalized.statusCommand);
2885
+ if (normalized?.nextRefSubject) add('主体引用', normalized.nextRefSubject);
2886
+ if (normalized?.nextVoiceArg) add('音色参数', normalized.nextVoiceArg);
2887
+ if (normalized?.dryRun && context.confirmCommand) add('确认执行', context.confirmCommand);
2888
+ if (normalized?.dryRun && !context.confirmCommand && context.executeCommand) add('正式执行', context.executeCommand);
2889
+ const modelNext = modelListNextCommand(commandName);
2890
+ if (modelNext) add(commandName === 'model asset-review-models' ? '查询素材组' : '查看参数', modelNext);
2891
+ const waitNext = statusNextCommand(commandName, normalized);
2892
+ if (waitNext) add('继续等待', waitNext);
2893
+ const subtitleNext = subtitleRemoveNextCommand(commandName, normalized);
2894
+ if (subtitleNext) add('去字幕预览', subtitleNext);
2895
+ if (context.moreCommand) add('显示更多', context.moreCommand);
2896
+ return actions;
2897
+ }
2898
+
2899
+ function renderNextActions(lines, actions = [], hint = null) {
2900
+ if (!actions.length && !hint) return;
2901
+ sectionTitle(lines, '下一步');
2902
+ const labelWidth = actions.length ? Math.max(...actions.map((item) => displayWidth(item.label))) : 0;
2903
+ for (const item of actions) {
2904
+ lines.push(` ${labelText(padDisplay(item.label, labelWidth))} ${commandText(prettyValue(item.value))}`);
2905
+ }
2906
+ if (hint) lines.push(` ${labelText(padDisplay('提示', labelWidth || 2))} ${mutedText(hint)}`);
2907
+ }
2908
+
2909
+ function renderUrlLists(lines, normalized) {
2910
+ renderScalarList(lines, '结果链接', normalized?.resultUrls, 6);
2911
+ renderScalarList(lines, '原始素材', normalized?.originUrls, 6);
2912
+ }
2913
+
2914
+ function formatPrettySchema(data = {}, context = {}) {
2915
+ const lines = [prettyTitle('schema', 'command_schema', data), ''];
2916
+ const isBrief = data.kind === 'agent_brief';
2917
+ renderDetails(lines, '摘要', {
2918
+ schemaVersion: data.schemaVersion,
2919
+ kind: data.kind,
2920
+ cli: data.cli?.name,
2921
+ version: data.cli?.version,
2922
+ commandPrefix: data.cli?.commandPrefix,
2923
+ domainFilter: data.filters?.domain,
2924
+ commandFilter: data.filters?.command,
2925
+ commandCount: data.commandCount ?? (Array.isArray(data.commands) ? data.commands.length : undefined),
2926
+ }, ['schemaVersion', 'kind', 'cli', 'version', 'commandPrefix', 'domainFilter', 'commandFilter', 'commandCount']);
2927
+ renderArraySection(lines, '命令领域', data.domains, 'domains');
2928
+ if (!isBrief) renderArraySection(lines, '命令', data.commands, 'commands');
2929
+ renderNextActions(lines, [
2930
+ ...(isBrief ? [
2931
+ { label: '精确 Schema', value: `${commandPrefix()} schema --domain <domain> --command <command> -f json` },
2932
+ { label: '完整 Schema', value: `${commandPrefix()} schema -f json` },
2933
+ ] : [
2934
+ { label: 'Agent Brief', value: `${commandPrefix()} schema --brief -f json` },
2935
+ { label: 'Schema JSON', value: `${commandPrefix()} schema -f json` },
2936
+ ]),
2937
+ ...(context.jsonCommand ? [{ label: '当前筛选 JSON', value: context.jsonCommand }] : []),
2938
+ ], isBrief ? '先读 brief 建立能力地图;执行具体命令前按需读取精确 schema。' : undefined);
2939
+ return `${lines.join('\n')}\n`;
2940
+ }
2941
+
2942
+ function formatPrettyEnvironment(data = {}, context = {}) {
2943
+ const lines = [prettyTitle('doctor', 'environment_report', data), ''];
2944
+ renderDetails(lines, '摘要', {
2945
+ doctorStatus: data.doctorStatus,
2946
+ verify: data.verify,
2947
+ nodeVersion: data.runtime?.nodeVersion,
2948
+ platform: data.runtime?.platform,
2949
+ arch: data.runtime?.arch,
2950
+ apiOrigin: data.origins?.apiOrigin,
2951
+ authState: data.auth?.loginState,
2952
+ authType: data.auth?.authType,
2953
+ accessKey: data.auth?.accessKey,
2954
+ currentProjectGroupNo: data.projectGroup?.projectGroupNo,
2955
+ currentProjectGroupName: data.projectGroup?.projectGroupName,
2956
+ }, ['doctorStatus', 'verify', 'nodeVersion', 'platform', 'arch', 'apiOrigin', 'authState', 'authType', 'accessKey', 'currentProjectGroupNo', 'currentProjectGroupName']);
2957
+ renderArraySection(lines, '检查结果', data.checks, 'checks');
2958
+ renderNextActions(lines, collectPrettyActions('doctor', data, context));
2959
+ return `${lines.join('\n')}\n`;
2960
+ }
2961
+
2962
+ function formatPrettyModelOptions(data = {}, context = {}) {
2963
+ const lines = [prettyTitle('model options', 'model_options', data), ''];
2964
+ renderDetails(lines, '摘要', {
2965
+ modelGroupCode: data.modelGroupCode,
2966
+ taskKind: localizedTaskKind(data.taskKind),
2967
+ displayName: data.model?.displayName,
2968
+ modelDesc: data.model?.modelDesc,
2969
+ }, ['modelGroupCode', 'displayName', 'taskKind', 'modelDesc']);
2970
+ renderModelParams(lines, data.params);
2971
+ renderModelResources(lines, data.resources, data.taskKind);
2972
+ renderModelConstraints(lines, data.constraints);
2973
+ renderNextActions(lines, [
2974
+ { label: '生成创建写法', value: `${commandPrefix()} model create-spec --model-group-code ${data.modelGroupCode || '<modelGroupCode>'}` },
2975
+ ...(context.textCommand ? [{ label: 'Agent 读取', value: context.textCommand }] : []),
2976
+ ...(context.jsonCommand ? [{ label: '完整 JSON', value: context.jsonCommand }] : []),
2977
+ ]);
2978
+ return `${lines.join('\n')}\n`;
2979
+ }
2980
+
2981
+ function formatPrettyModelCreateSpec(data = {}, context = {}) {
2982
+ const taskKind = data.taskKind;
2983
+ const lines = [prettyTitle('model create-spec', 'model_create_spec', data), ''];
2984
+ renderDetails(lines, '摘要', {
2985
+ modelGroupCode: data.modelGroupCode,
2986
+ taskKind: localizedTaskKind(data.taskKind),
2987
+ createCommand: data.createCommand,
2988
+ feeCommand: data.feeCommand,
2989
+ statusCommandTaskType: data.statusCommandTaskType,
2990
+ inputSummary: localizedCreateSpecSummary(data.inputRequirement?.summary),
2991
+ acceptedModes: localizedModeList(data.inputRequirement?.acceptedModes, taskKind),
2992
+ }, ['modelGroupCode', 'taskKind', 'createCommand', 'feeCommand', 'statusCommandTaskType', 'inputSummary', 'acceptedModes']);
2993
+ renderArraySection(lines, '支持意图', data.supportedIntents, [
2994
+ column('输入方式', (row) => localizedIntentMode(row.mode, taskKind), { minWidth: 12, maxWidth: 22 }),
2995
+ column('意图', (row) => localizedIntentText(row.intent, row.mode, taskKind), { minWidth: 10, maxWidth: 24 }),
2996
+ column('必需写法', (row) => commandArgLines(row.requiredArgs), { minWidth: 22, maxWidth: 62 }),
2997
+ column('素材用途', (row) => localizedList(row.resourceUsages, localizedUsage), { minWidth: 10, maxWidth: 24 }),
2998
+ column('Prompt 绑定', (row) => localizedPromptBinding(row.promptBinding), { maxWidth: 14 }),
2999
+ ]);
3000
+ renderArraySection(lines, '可控参数', data.parameterControls?.userParams, [
3001
+ column('参数', (row) => localizedParamKey(row.key), { minWidth: 10, maxWidth: 20 }),
3002
+ column('写法', 'cliArg', { minWidth: 12, maxWidth: 34 }),
3003
+ column('可选值', (row) => Array.isArray(row.allowedValues) ? row.allowedValues.join('、') : '', { minWidth: 12, maxWidth: 36 }),
3004
+ ]);
3005
+ renderScalarList(lines, '示例', (data.examples || []).map((example) => modelCreateExampleWithCode(example, data.modelGroupCode)).map((item) => item.replaceAll('--model-group-code <code>', `--model-group-code ${data.modelGroupCode || '<code>'}`)), 8);
3006
+ const actions = [];
3007
+ if (data.optionsCommand) actions.push({ label: '查看参数', value: `${commandPrefix()} ${data.optionsCommand}` });
3008
+ const feeNext = modelFeeNextCommand(data);
3009
+ if (feeNext) actions.push({ label: '费用预估', value: feeNext });
3010
+ if (data.examples?.[0]) actions.push({ label: 'Dry-run', value: modelCreateExampleWithCode(data.examples[0], data.modelGroupCode) });
3011
+ if (context.textCommand) actions.push({ label: 'Agent 读取', value: context.textCommand });
3012
+ if (context.jsonCommand) actions.push({ label: '完整 JSON', value: context.jsonCommand });
3013
+ renderNextActions(lines, actions);
3014
+ return `${lines.join('\n')}\n`;
3015
+ }
3016
+
3017
+ function formatPrettyModelInputGuide(data = {}, context = {}) {
3018
+ const lines = [prettyTitle('model input-guide', 'model_input_guide', data), ''];
3019
+ renderDetails(lines, '摘要', {
3020
+ schemaVersion: data.schemaVersion,
3021
+ commonFieldCount: Array.isArray(data.commonFields) ? data.commonFields.length : undefined,
3022
+ resourceFieldCount: Array.isArray(data.resourceFields) ? data.resourceFields.length : undefined,
3023
+ resourceRuleCount: Array.isArray(data.resourceRules) ? data.resourceRules.length : undefined,
3024
+ }, ['schemaVersion', 'commonFieldCount', 'resourceFieldCount', 'resourceRuleCount']);
3025
+ const guideColumns = [
3026
+ column('字段', 'field', { maxWidth: 32 }),
3027
+ column('可选值', localizedGuideValues, { maxWidth: 46 }),
3028
+ column('说明', 'description', { maxWidth: 70 }),
3029
+ ];
3030
+ renderArraySection(lines, '通用字段', data.commonFields, guideColumns);
3031
+ renderArraySection(lines, '资源字段', data.resourceFields, guideColumns);
3032
+ renderScalarList(lines, '资源规则', data.resourceRules, 12);
3033
+ renderScalarList(lines, 'Reference Key 指南', data.referenceKeyGuide, 12);
3034
+ renderNextActions(lines, [
3035
+ { label: '生图模型', value: `${commandPrefix()} model image-models --model <keyword>` },
3036
+ { label: '生视频模型', value: `${commandPrefix()} model video-models --model <keyword>` },
3037
+ { label: '模型参数', value: `${commandPrefix()} model options --model-group-code <modelGroupCode>` },
3038
+ ...(context.textCommand ? [{ label: 'Agent 读取', value: context.textCommand }] : []),
3039
+ ...(context.jsonCommand ? [{ label: '完整 JSON', value: context.jsonCommand }] : []),
3040
+ ]);
3041
+ return `${lines.join('\n')}\n`;
3042
+ }
3043
+
3044
+ function formatPrettyGeneric(commandName, data, context = {}) {
3045
+ const normalized = normalizeOutputData(commandName, data);
3046
+ const kind = outputKindForCommand(commandName, context);
3047
+ if (!isPlainObject(normalized)) {
3048
+ const lines = [prettyTitle(commandName, kind, data), ''];
3049
+ renderDetails(lines, '结果', { value: normalized }, ['value']);
3050
+ return `${lines.join('\n')}\n`;
3051
+ }
3052
+ const profile = PRETTY_KIND_PROFILES[kind] || PRETTY_KIND_PROFILES.generic;
3053
+ const lines = [prettyTitle(commandName, kind, normalized), ''];
3054
+ const listKeys = collectListKeys(normalized, profile.listKey);
3055
+ const excluded = new Set(['raw', ...listKeys, 'resultUrls', 'originUrls', 'waited']);
3056
+ const summaryFields = normalized.dryRun
3057
+ ? (DRY_RUN_FIELDS_BY_COMMAND[commandName] || ['dryRun', 'action', ...(profile.fields || [])])
3058
+ : (profile.fields || []);
3059
+ renderDetails(lines, normalized.dryRun ? '预览摘要' : '摘要', normalized, summaryFields, [...excluded]);
3060
+ if (normalized.waited) {
3061
+ renderDetails(lines, '等待结果', normalized.waited, DETAIL_FIELDS_BY_KIND.task_status || []);
3062
+ }
3063
+ for (const key of listKeys) {
3064
+ const rows = normalized[key];
3065
+ if (!Array.isArray(rows)) continue;
3066
+ const limit = listLimitFor(commandName, context, rows);
3067
+ renderArraySection(lines, namedListTitle(key), rows, key, limit);
3068
+ }
3069
+ renderUrlLists(lines, normalized);
3070
+ renderNextActions(lines, collectPrettyActions(commandName, normalized, context), modelListAgentHint(commandName));
3071
+ return `${lines.join('\n')}\n`;
3072
+ }
3073
+
3074
+ export function formatPrettyOutput(commandName, data, context = {}) {
3075
+ if (commandName === 'schema') return formatPrettySchema(data, context);
3076
+ if (commandName === 'doctor') return formatPrettyEnvironment(data, context);
3077
+ if (commandName === 'model image-models' || commandName === 'model video-models') return formatPrettyModelList(commandName, data, context);
3078
+ if (commandName === 'model options') return formatPrettyModelOptions(data, context);
3079
+ if (commandName === 'model create-spec') return formatPrettyModelCreateSpec(data, context);
3080
+ if (commandName === 'model input-guide') return formatPrettyModelInputGuide(data, context);
3081
+ return formatPrettyGeneric(commandName, data, context);
3082
+ }
3083
+
3084
+ function yamlScalar(value) {
3085
+ if (value === null) return 'null';
3086
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
3087
+ const text = String(value ?? '');
3088
+ return JSON.stringify(text);
3089
+ }
3090
+
3091
+ function formatYamlValue(value, indent = 0) {
3092
+ const pad = ' '.repeat(indent);
3093
+ if (Array.isArray(value)) {
3094
+ if (!value.length) return '[]';
3095
+ return value.map((item) => {
3096
+ if (isPlainObject(item) || Array.isArray(item)) {
3097
+ const rendered = formatYamlValue(item, indent + 2);
3098
+ return `${pad}- ${rendered.includes('\n') ? `\n${rendered}` : rendered}`;
3099
+ }
3100
+ return `${pad}- ${yamlScalar(item)}`;
3101
+ }).join('\n');
3102
+ }
3103
+ if (isPlainObject(value)) {
3104
+ const entries = Object.entries(value).filter(([, item]) => item !== undefined);
3105
+ if (!entries.length) return '{}';
3106
+ return entries.map(([key, item]) => {
3107
+ if (isPlainObject(item) || Array.isArray(item)) {
3108
+ return `${pad}${key}:\n${formatYamlValue(item, indent + 2)}`;
3109
+ }
3110
+ return `${pad}${key}: ${yamlScalar(item)}`;
3111
+ }).join('\n');
3112
+ }
3113
+ return yamlScalar(value);
3114
+ }
3115
+
3116
+ export function formatYamlEnvelope(payload) {
3117
+ return `${formatYamlValue(rewriteNested(payload), 0)}\n`;
3118
+ }
3119
+
3120
+ function csvEscape(value) {
3121
+ const text = String(prettyValue(value));
3122
+ return /[",\n\r]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
3123
+ }
3124
+
3125
+ function csvColumnsForRows(rows = []) {
3126
+ const cols = inferTableColumns(rows);
3127
+ return cols.map((col) => ({
3128
+ header: col.header,
3129
+ key: col.key,
3130
+ value: col.value,
3131
+ }));
3132
+ }
3133
+
3134
+ export function formatCsvOutput(commandName, data, context = {}) {
3135
+ const normalized = normalizeJsonData(commandName, data);
3136
+ const kind = outputKindForCommand(commandName, context);
3137
+ const primaryKey = LIST_KEY_BY_KIND[kind];
3138
+ const list = primaryKey && Array.isArray(normalized?.[primaryKey])
3139
+ ? { key: primaryKey, rows: normalized[primaryKey] }
3140
+ : firstList(normalized);
3141
+ const rows = list?.rows?.length ? list.rows : (isPlainObject(normalized) ? [normalized] : [{ value: normalized }]);
3142
+ const columns = TABLE_COLUMNS[list?.key] || csvColumnsForRows(rows);
3143
+ const headers = columns.map((col) => col.header);
3144
+ const lines = [headers.map(csvEscape).join(',')];
3145
+ for (const row of rows) {
3146
+ lines.push(columns.map((col) => csvEscape(resolveColumnValue(row, col))).join(','));
3147
+ }
3148
+ return `${lines.join('\n')}\n`;
3149
+ }
3150
+
3151
+ export function formatPrettyError(error) {
3152
+ const lines = [errorTitleText('命令失败'), ''];
3153
+ renderDetails(lines, '错误', {
3154
+ type: error.type || 'error',
3155
+ message: error.message || String(error),
3156
+ hint: error.hint ? rewriteCommandPrefix(error.hint) : undefined,
3157
+ }, ['type', 'message', 'hint']);
3158
+ if (isPlainObject(error.details)) {
3159
+ const payload = isPlainObject(error.details.payload) ? error.details.payload : {};
3160
+ renderDetails(lines, '详情', compactRecord({
3161
+ status: error.details.status ?? payload.status,
3162
+ code: error.details.code ?? payload.code,
3163
+ msg: error.details.msg ?? payload.msg ?? payload.message,
3164
+ traceId: error.details.traceId ?? payload.traceId,
3165
+ causeCode: error.details.causeCode,
3166
+ unknownOptions: Array.isArray(error.details.unknownOptions) ? error.details.unknownOptions.join(', ') : undefined,
3167
+ }), ['status', 'code', 'msg', 'traceId', 'causeCode', 'unknownOptions']);
3168
+ }
3169
+ return `${lines.join('\n')}\n`;
3170
+ }
3171
+
1157
3172
  export function formatTextOutput(commandName, data, context = {}) {
1158
3173
  if (commandName === 'model options') return formatModelOptionsOutput(data);
1159
3174
  if (commandName === 'model create-spec') return formatModelCreateSpecOutput(data);
@@ -1216,6 +3231,12 @@ export function formatTextOutput(commandName, data, context = {}) {
1216
3231
  'parentKey',
1217
3232
  'assetKind',
1218
3233
  'assetKey',
3234
+ 'assetType',
3235
+ 'assetPath',
3236
+ 'validationLegal',
3237
+ 'conversionRequired',
3238
+ 'converted',
3239
+ 'conversionReason',
1219
3240
  'actorKey',
1220
3241
  'propKey',
1221
3242
  'locationKey',
@@ -1238,7 +3259,7 @@ export function formatTextOutput(commandName, data, context = {}) {
1238
3259
  'dryRun',
1239
3260
  'action',
1240
3261
  ],
1241
- ['nextCommand', 'nextRefSubject', ...(list ? ['count'] : []), ...(isModelList ? ['usage'] : [])],
3262
+ ['nextCommand', 'statusCommand', 'nextRefSubject', ...(list ? ['count'] : []), ...(isModelList ? ['usage'] : [])],
1242
3263
  );
1243
3264
  if (scalarLine) lines.push(scalarLine);
1244
3265
  if (list) {
@@ -1265,6 +3286,7 @@ export function formatTextOutput(commandName, data, context = {}) {
1265
3286
  }
1266
3287
  }
1267
3288
  if (normalized.nextCommand) lines.push(`next=${normalized.nextCommand}`);
3289
+ if (normalized.statusCommand) lines.push(`statusCommand=${normalized.statusCommand}`);
1268
3290
  if (normalized.nextRefSubject) lines.push(`nextRefSubject=${normalized.nextRefSubject}`);
1269
3291
  if (normalized.dryRun && context.confirmCommand) lines.push(`nextAfterConfirm=${context.confirmCommand}`);
1270
3292
  if (normalized.dryRun && !context.confirmCommand && context.executeCommand) lines.push(`next=${context.executeCommand}`);