@lingjingai/lj-awb-cli-pre 0.3.18 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/package.json +5 -1
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +6 -2
- package/packages/awb-core/src/output.js +1870 -5
- package/packages/awb-core/src/services.js +150 -3
- package/packages/awb-core/src/standalone.js +363 -125
- package/skills/lj-awb/SKILL.md +8 -5
- package/skills/lj-awb/VERSION +1 -1
- package/skills/lj-awb/compat.json +3 -3
- package/skills/lj-awb/modules/artifact/asset.md +1 -1
- package/skills/lj-awb/modules/artifact/clip.md +1 -1
- package/skills/lj-awb/modules/artifact/script.md +1 -1
- package/skills/lj-awb/modules/artifact/video.md +1 -1
- package/skills/lj-awb/modules/driver.md +11 -6
- package/skills/lj-awb/modules/model.md +8 -6
- package/skills/lj-awb/references/model-options-read.md +10 -5
- package/skills/lj-awb/references/output-fields.md +3 -3
|
@@ -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
|
}
|
|
@@ -659,6 +664,21 @@ function shortValue(value) {
|
|
|
659
664
|
return text.length > 120 ? `${text.slice(0, 117)}...` : text;
|
|
660
665
|
}
|
|
661
666
|
|
|
667
|
+
function formatPercent(value) {
|
|
668
|
+
if (value === undefined || value === null || value === '') return '';
|
|
669
|
+
const number = Number(value);
|
|
670
|
+
if (!Number.isFinite(number)) return '';
|
|
671
|
+
const percent = number > 1 ? number : number * 100;
|
|
672
|
+
const rounded = Math.round(percent * 10) / 10;
|
|
673
|
+
const text = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
674
|
+
return `${text}%`;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function shortRecordValue(key, value) {
|
|
678
|
+
if (key === 'successRate') return formatPercent(value) || shortValue(value);
|
|
679
|
+
return shortValue(value);
|
|
680
|
+
}
|
|
681
|
+
|
|
662
682
|
function valueList(values, limit = 12) {
|
|
663
683
|
if (!Array.isArray(values) || !values.length) return undefined;
|
|
664
684
|
const items = values
|
|
@@ -678,7 +698,7 @@ function renderRecord(record, preferredKeys = [], excludedKeys = []) {
|
|
|
678
698
|
...preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined),
|
|
679
699
|
...Object.keys(record).filter((key) => !excluded.has(key) && !preferredKeys.includes(key) && !Array.isArray(record[key]) && !isPlainObject(record[key])),
|
|
680
700
|
];
|
|
681
|
-
return keys.map((key) => `${key}=${
|
|
701
|
+
return keys.map((key) => `${key}=${shortRecordValue(key, record[key])}`).join(' ');
|
|
682
702
|
}
|
|
683
703
|
|
|
684
704
|
function recordScalarKeys(record, preferredKeys = [], excludedKeys = []) {
|
|
@@ -693,7 +713,7 @@ function pushSectionRecord(lines, title, record, preferredKeys = []) {
|
|
|
693
713
|
const compacted = compactRecord(record);
|
|
694
714
|
lines.push(`${title}:`);
|
|
695
715
|
for (const key of recordScalarKeys(compacted, preferredKeys)) {
|
|
696
|
-
lines.push(` ${key}=${
|
|
716
|
+
lines.push(` ${key}=${shortRecordValue(key, compacted[key])}`);
|
|
697
717
|
}
|
|
698
718
|
}
|
|
699
719
|
|
|
@@ -740,6 +760,7 @@ function rowSummary(row) {
|
|
|
740
760
|
'modelDesc',
|
|
741
761
|
'modelStatus',
|
|
742
762
|
'taskQueueNum',
|
|
763
|
+
'successRate',
|
|
743
764
|
'feeCalcType',
|
|
744
765
|
'inputModes',
|
|
745
766
|
'params',
|
|
@@ -871,7 +892,7 @@ function rowSummary(row) {
|
|
|
871
892
|
if (Array.isArray(value) && value.length === 0) return false;
|
|
872
893
|
return true;
|
|
873
894
|
})
|
|
874
|
-
.map((key) => `${key}=${
|
|
895
|
+
.map((key) => `${key}=${shortRecordValue(key, row[key])}`)
|
|
875
896
|
.join(' ');
|
|
876
897
|
}
|
|
877
898
|
|
|
@@ -908,8 +929,30 @@ function textItemCount(item = {}) {
|
|
|
908
929
|
return undefined;
|
|
909
930
|
}
|
|
910
931
|
|
|
932
|
+
function textCountRange(item = {}) {
|
|
933
|
+
if (item.minCount != null && item.maxCount != null && item.minCount === item.maxCount) return String(item.maxCount);
|
|
934
|
+
if (item.minCount != null && item.maxCount != null) return `${item.minCount}..${item.maxCount}`;
|
|
935
|
+
if (item.minCount != null) return `>=${item.minCount}`;
|
|
936
|
+
if (item.maxCount != null) return `<=${item.maxCount}`;
|
|
937
|
+
return item.value;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function textLimitSummary(item = {}) {
|
|
941
|
+
if (!isPlainObject(item)) return undefined;
|
|
942
|
+
const parts = [];
|
|
943
|
+
const files = textFileCount(item);
|
|
944
|
+
const duration = textDurationRange(item);
|
|
945
|
+
const items = textItemCount(item);
|
|
946
|
+
if (files) parts.push(`files${files}`);
|
|
947
|
+
if (duration) parts.push(`durationMs${duration}`);
|
|
948
|
+
if (item.maxTotalDurationMs != null) parts.push(`totalDurationMs<=${item.maxTotalDurationMs}`);
|
|
949
|
+
if (item.maxSizeKB != null) parts.push(`maxSizeKB<=${item.maxSizeKB}`);
|
|
950
|
+
if (items) parts.push(`items${items}`);
|
|
951
|
+
return parts.length ? parts.join('|') : undefined;
|
|
952
|
+
}
|
|
953
|
+
|
|
911
954
|
function textAllowValues(values = []) {
|
|
912
|
-
return Array.isArray(values) && values.length ? valueList(values) :
|
|
955
|
+
return Array.isArray(values) && values.length ? valueList(values) : undefined;
|
|
913
956
|
}
|
|
914
957
|
|
|
915
958
|
function textConditionValue(value) {
|
|
@@ -923,6 +966,7 @@ function textConstraintConditions(conditions = []) {
|
|
|
923
966
|
return conditions
|
|
924
967
|
.map((item) => {
|
|
925
968
|
const key = item.key || item.configCode;
|
|
969
|
+
if (item.meaning === 'count') return key ? `${key}=${textCountRange(item)}` : null;
|
|
926
970
|
return key ? `${key}=${textConditionValue(item.value)}` : null;
|
|
927
971
|
})
|
|
928
972
|
.filter(Boolean)
|
|
@@ -1018,9 +1062,10 @@ function formatModelOptionsOutput(data = {}) {
|
|
|
1018
1062
|
allowedValues: textAllowValues(item.allowValues),
|
|
1019
1063
|
when: textConstraintConditions(item.conditions),
|
|
1020
1064
|
effect: item.effect,
|
|
1065
|
+
limits: textLimitSummary(item.limits),
|
|
1021
1066
|
priority: item.priority,
|
|
1022
1067
|
name: item.name,
|
|
1023
|
-
}, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'priority', 'name']);
|
|
1068
|
+
}, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'limits', 'priority', 'name']);
|
|
1024
1069
|
}
|
|
1025
1070
|
if (data.modelGroupCode) {
|
|
1026
1071
|
lines.push('next:');
|
|
@@ -1037,6 +1082,79 @@ function textKeyBinding(value) {
|
|
|
1037
1082
|
return value;
|
|
1038
1083
|
}
|
|
1039
1084
|
|
|
1085
|
+
function localizedIntentMode(value, taskKind = '') {
|
|
1086
|
+
const mode = String(value || '');
|
|
1087
|
+
const labels = {
|
|
1088
|
+
prompt_only: taskKind === 'video' ? '文本生成视频' : '文本生成图片',
|
|
1089
|
+
reference: taskKind === 'video' ? '参考素材生成' : '参考图生成',
|
|
1090
|
+
frames: '首帧/尾帧生成',
|
|
1091
|
+
storyboard: '关键帧/故事板生成',
|
|
1092
|
+
keyframe: '关键帧生成',
|
|
1093
|
+
};
|
|
1094
|
+
return labels[mode] || localizedInputMode(mode, taskKind);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function localizedIntentText(value, mode, taskKind = '') {
|
|
1098
|
+
const text = String(value || '');
|
|
1099
|
+
if (!text) return localizedIntentMode(mode, taskKind);
|
|
1100
|
+
const normalized = text.trim();
|
|
1101
|
+
if (normalized === '参考模式') return taskKind === 'video' ? '参考素材生成' : '参考图生成';
|
|
1102
|
+
if (normalized === 'frames 模式') return '首帧/尾帧生成';
|
|
1103
|
+
if (normalized === 'prompt_only 模式') return localizedIntentMode('prompt_only', taskKind);
|
|
1104
|
+
if (normalized === '多帧 / 故事板模式') return '关键帧/故事板生成';
|
|
1105
|
+
return normalized
|
|
1106
|
+
.replaceAll('frames', '首帧/尾帧')
|
|
1107
|
+
.replaceAll('first_frame', '首帧')
|
|
1108
|
+
.replaceAll('last_frame', '尾帧')
|
|
1109
|
+
.replaceAll('reference', '参考素材')
|
|
1110
|
+
.replaceAll('prompt_only', '纯文本');
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
function localizedPromptBinding(value) {
|
|
1114
|
+
const labels = {
|
|
1115
|
+
key_required: '必须绑定 key',
|
|
1116
|
+
required: '必须绑定 key',
|
|
1117
|
+
optional_key: '可选 key',
|
|
1118
|
+
optional: '可选 key',
|
|
1119
|
+
none: '不需要',
|
|
1120
|
+
};
|
|
1121
|
+
return labels[value] || prettyValue(value);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function localizedModeList(values = [], taskKind = '') {
|
|
1125
|
+
if (!Array.isArray(values) || !values.length) return '';
|
|
1126
|
+
return values.map((item) => localizedIntentMode(item, taskKind)).join('、');
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function localizedCreateSpecSummary(value) {
|
|
1130
|
+
return String(value || '')
|
|
1131
|
+
.replaceAll('prompt_only', '纯文本')
|
|
1132
|
+
.replaceAll('reference', '参考素材')
|
|
1133
|
+
.replaceAll('frames', '首帧/尾帧')
|
|
1134
|
+
.replaceAll('storyboard', '关键帧/故事板');
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
function localizedGuideValues(row = {}) {
|
|
1138
|
+
const values = Array.isArray(row.values) ? row.values : [];
|
|
1139
|
+
if (!values.length) return '';
|
|
1140
|
+
if (row.field === 'resources[].usage') {
|
|
1141
|
+
return values.map((value) => `${value}(${localizedUsage(value)})`).join('、');
|
|
1142
|
+
}
|
|
1143
|
+
if (row.field === 'resources[].type') {
|
|
1144
|
+
return values.map((value) => `${value}(${localizedMedia(value)})`).join('、');
|
|
1145
|
+
}
|
|
1146
|
+
if (row.field === 'resources[].source.kind') {
|
|
1147
|
+
const labels = { url: '文件/URL/平台素材路径', asset_id: '资产 ID' };
|
|
1148
|
+
return values.map((value) => `${value}(${labels[value] || value})`).join('、');
|
|
1149
|
+
}
|
|
1150
|
+
return joinValueList(values, 8);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function commandArgLines(values = []) {
|
|
1154
|
+
if (!Array.isArray(values) || !values.length) return '';
|
|
1155
|
+
return values.map((value) => prettyValue(value)).join('\n');
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1040
1158
|
function modelFeeNextCommand(data = {}) {
|
|
1041
1159
|
if (!data.feeCommand || !data.modelGroupCode) return null;
|
|
1042
1160
|
const args = [
|
|
@@ -1154,6 +1272,1753 @@ function subtitleRemoveNextCommand(commandName, normalized) {
|
|
|
1154
1272
|
return `${commandPrefix()} create video-subtitle-removal --source-task-id ${normalized.taskId}${projectGroupPart} --dry-run`;
|
|
1155
1273
|
}
|
|
1156
1274
|
|
|
1275
|
+
const OUTPUT_KIND_BY_COMMAND = {
|
|
1276
|
+
doctor: 'environment_report',
|
|
1277
|
+
schema: 'command_schema',
|
|
1278
|
+
'auth status': 'auth_status',
|
|
1279
|
+
'auth verify': 'auth_status',
|
|
1280
|
+
'auth login': 'auth_result',
|
|
1281
|
+
'auth clear': 'generic',
|
|
1282
|
+
'account info': 'account_summary',
|
|
1283
|
+
'account teams': 'team_list',
|
|
1284
|
+
'account switch-team': 'generic',
|
|
1285
|
+
'project list': 'project_list',
|
|
1286
|
+
'project current': 'project_summary',
|
|
1287
|
+
'project use': 'generic',
|
|
1288
|
+
'project users': 'project_user_list',
|
|
1289
|
+
'project create': 'generic',
|
|
1290
|
+
'project update': 'generic',
|
|
1291
|
+
'project ensure': 'generic',
|
|
1292
|
+
'credits balance': 'credits_balance',
|
|
1293
|
+
'credits usage': 'credits_usage',
|
|
1294
|
+
'model image-models': 'model_list',
|
|
1295
|
+
'model video-models': 'model_list',
|
|
1296
|
+
'model asset-review-models': 'asset_review_model_list',
|
|
1297
|
+
'model options': 'model_options',
|
|
1298
|
+
'model create-spec': 'model_create_spec',
|
|
1299
|
+
'model input-guide': 'model_input_guide',
|
|
1300
|
+
'upload files': 'upload_result',
|
|
1301
|
+
'create image-fee': 'fee_estimate',
|
|
1302
|
+
'create image': 'task_submission',
|
|
1303
|
+
'create image-batch': 'batch_task_submission',
|
|
1304
|
+
'task image-status': 'task_status',
|
|
1305
|
+
'create video-fee': 'fee_estimate',
|
|
1306
|
+
'create video': 'task_submission',
|
|
1307
|
+
'create video-batch': 'batch_task_submission',
|
|
1308
|
+
'task video-status': 'task_status',
|
|
1309
|
+
'create video-subtitle-removal': 'subtitle_task_submission',
|
|
1310
|
+
'task video-subtitle-status': 'subtitle_task_status',
|
|
1311
|
+
'task list': 'task_list',
|
|
1312
|
+
'task wait': 'task_status',
|
|
1313
|
+
'task records': 'local_task_records',
|
|
1314
|
+
'task record-poll': 'batch_task_status',
|
|
1315
|
+
'create asset-match-actor': 'asset_match_list',
|
|
1316
|
+
'create asset-groups': 'asset_group_list',
|
|
1317
|
+
'create asset-group-get': 'asset_group_detail',
|
|
1318
|
+
'create asset-group': 'asset_group_write_result',
|
|
1319
|
+
'create asset-group-update': 'asset_group_write_result',
|
|
1320
|
+
'create asset': 'asset_register_result',
|
|
1321
|
+
'artifact script get': 'artifact_full',
|
|
1322
|
+
'artifact script document': 'artifact_record',
|
|
1323
|
+
'artifact script rows': 'artifact_list',
|
|
1324
|
+
'artifact script row': 'artifact_record',
|
|
1325
|
+
'artifact script children': 'artifact_list',
|
|
1326
|
+
'artifact script upsert-row': 'artifact_write_result',
|
|
1327
|
+
'artifact script delete-row': 'artifact_delete_result',
|
|
1328
|
+
'artifact script import': 'artifact_import_result',
|
|
1329
|
+
'artifact asset get': 'artifact_full',
|
|
1330
|
+
'artifact asset actors': 'artifact_list',
|
|
1331
|
+
'artifact asset actor': 'artifact_record',
|
|
1332
|
+
'artifact asset props': 'artifact_list',
|
|
1333
|
+
'artifact asset prop': 'artifact_record',
|
|
1334
|
+
'artifact asset locations': 'artifact_list',
|
|
1335
|
+
'artifact asset location': 'artifact_record',
|
|
1336
|
+
'artifact asset upsert-actor': 'artifact_write_result',
|
|
1337
|
+
'artifact asset upsert-prop': 'artifact_write_result',
|
|
1338
|
+
'artifact asset upsert-location': 'artifact_write_result',
|
|
1339
|
+
'artifact asset upsert-actor-state': 'artifact_write_result',
|
|
1340
|
+
'artifact asset upsert-prop-state': 'artifact_write_result',
|
|
1341
|
+
'artifact asset upsert-location-state': 'artifact_write_result',
|
|
1342
|
+
'artifact asset delete-actor': 'artifact_delete_result',
|
|
1343
|
+
'artifact asset delete-prop': 'artifact_delete_result',
|
|
1344
|
+
'artifact asset delete-location': 'artifact_delete_result',
|
|
1345
|
+
'artifact asset delete-actor-state': 'artifact_delete_result',
|
|
1346
|
+
'artifact asset delete-prop-state': 'artifact_delete_result',
|
|
1347
|
+
'artifact asset delete-location-state': 'artifact_delete_result',
|
|
1348
|
+
'artifact asset import': 'artifact_import_result',
|
|
1349
|
+
'artifact video get': 'artifact_full',
|
|
1350
|
+
'artifact video episodes': 'artifact_list',
|
|
1351
|
+
'artifact video episode': 'artifact_record',
|
|
1352
|
+
'artifact video scenes': 'artifact_list',
|
|
1353
|
+
'artifact video scene': 'artifact_record',
|
|
1354
|
+
'artifact video clips': 'artifact_list',
|
|
1355
|
+
'artifact video clip': 'artifact_record',
|
|
1356
|
+
'artifact video upsert-episode': 'artifact_write_result',
|
|
1357
|
+
'artifact video upsert-scene': 'artifact_write_result',
|
|
1358
|
+
'artifact video upsert-clip': 'artifact_write_result',
|
|
1359
|
+
'artifact video update-clip-urls': 'artifact_write_result',
|
|
1360
|
+
'artifact video delete-episode': 'artifact_delete_result',
|
|
1361
|
+
'artifact video delete-scene': 'artifact_delete_result',
|
|
1362
|
+
'artifact video delete-clip': 'artifact_delete_result',
|
|
1363
|
+
'artifact video import-storyboard': 'artifact_import_result',
|
|
1364
|
+
'artifact clip get': 'artifact_full',
|
|
1365
|
+
'artifact clip episodes': 'artifact_list',
|
|
1366
|
+
'artifact clip episode': 'artifact_record',
|
|
1367
|
+
'artifact clip episode-by-id': 'artifact_record',
|
|
1368
|
+
'artifact clip upsert-episode': 'artifact_write_result',
|
|
1369
|
+
'artifact clip upsert-batch': 'artifact_import_result',
|
|
1370
|
+
'artifact clip update-status': 'artifact_status_update',
|
|
1371
|
+
'artifact clip delete-episode': 'artifact_delete_result',
|
|
1372
|
+
'create subject-list': 'subject_list',
|
|
1373
|
+
'create subject': 'subject_publish_result',
|
|
1374
|
+
'create subject-wait': 'subject_status',
|
|
1375
|
+
'create subject-voice-list': 'subject_voice_list',
|
|
1376
|
+
'create subject-voice': 'subject_voice_create_result',
|
|
1377
|
+
'create subject-voice-wait': 'subject_voice_status',
|
|
1378
|
+
'create subject-batch': 'batch_subject_publish_result',
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
const KIND_TITLES = {
|
|
1382
|
+
account_summary: '账号概览',
|
|
1383
|
+
artifact_delete_result: '最终产物删除结果',
|
|
1384
|
+
artifact_full: '最终产物详情',
|
|
1385
|
+
artifact_import_result: '最终产物导入结果',
|
|
1386
|
+
artifact_list: '最终产物列表',
|
|
1387
|
+
artifact_record: '最终产物记录',
|
|
1388
|
+
artifact_status_update: '剪辑状态更新结果',
|
|
1389
|
+
artifact_write_result: '最终产物写入结果',
|
|
1390
|
+
asset_group_detail: '素材组详情',
|
|
1391
|
+
asset_group_list: '素材组列表',
|
|
1392
|
+
asset_group_write_result: '素材组写入结果',
|
|
1393
|
+
asset_match_list: '素材匹配结果',
|
|
1394
|
+
asset_review_model_list: '素材审核模型',
|
|
1395
|
+
asset_register_result: '素材注册结果',
|
|
1396
|
+
auth_result: '认证结果',
|
|
1397
|
+
auth_status: '认证状态',
|
|
1398
|
+
batch_subject_publish_result: '批量主体提交结果',
|
|
1399
|
+
batch_task_status: '批量任务状态',
|
|
1400
|
+
batch_task_submission: '批量任务提交结果',
|
|
1401
|
+
command_schema: '命令 Schema',
|
|
1402
|
+
credits_balance: '积分余额',
|
|
1403
|
+
credits_usage: '积分用量',
|
|
1404
|
+
environment_report: '环境体检',
|
|
1405
|
+
fee_estimate: '费用预估',
|
|
1406
|
+
generic: '命令结果',
|
|
1407
|
+
local_task_records: '本地任务台账',
|
|
1408
|
+
model_create_spec: '模型创建写法',
|
|
1409
|
+
model_input_guide: '模型输入指南',
|
|
1410
|
+
model_list: '模型列表',
|
|
1411
|
+
model_options: '模型参数与素材约束',
|
|
1412
|
+
project_list: '项目组列表',
|
|
1413
|
+
project_summary: '项目组摘要',
|
|
1414
|
+
project_user_list: '项目组成员',
|
|
1415
|
+
subject_list: '主体列表',
|
|
1416
|
+
subject_publish_result: '主体创建结果',
|
|
1417
|
+
subject_status: '主体状态',
|
|
1418
|
+
subject_voice_create_result: '主体音色创建结果',
|
|
1419
|
+
subject_voice_list: '主体音色列表',
|
|
1420
|
+
subject_voice_status: '主体音色状态',
|
|
1421
|
+
subtitle_task_status: '去字幕任务状态',
|
|
1422
|
+
subtitle_task_submission: '去字幕任务提交结果',
|
|
1423
|
+
task_list: '任务列表',
|
|
1424
|
+
task_status: '任务状态',
|
|
1425
|
+
task_submission: '任务提交结果',
|
|
1426
|
+
team_list: '团队列表',
|
|
1427
|
+
upload_result: '上传结果',
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
const COMMAND_TITLES = {
|
|
1431
|
+
doctor: '环境体检',
|
|
1432
|
+
schema: '命令 Schema',
|
|
1433
|
+
'auth clear': '清空认证结果',
|
|
1434
|
+
'account switch-team': '团队切换结果',
|
|
1435
|
+
'project use': '项目组切换结果',
|
|
1436
|
+
'project create': '项目组创建结果',
|
|
1437
|
+
'project update': '项目组更新结果',
|
|
1438
|
+
'project ensure': '项目组确保结果',
|
|
1439
|
+
'create image': '生图任务提交结果',
|
|
1440
|
+
'create video': '生视频任务提交结果',
|
|
1441
|
+
'create image-batch': '批量生图提交结果',
|
|
1442
|
+
'create video-batch': '批量生视频提交结果',
|
|
1443
|
+
'create video-subtitle-removal': '去字幕任务提交结果',
|
|
1444
|
+
'create subject': '主体创建结果',
|
|
1445
|
+
'create subject-wait': '主体状态',
|
|
1446
|
+
'create subject-voice': '主体音色创建结果',
|
|
1447
|
+
'create subject-voice-wait': '主体音色状态',
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
const FIELD_LABELS = {
|
|
1451
|
+
acceptedModes: '可用输入模式',
|
|
1452
|
+
accessKey: 'Access Key',
|
|
1453
|
+
action: '动作',
|
|
1454
|
+
actorCount: '角色数',
|
|
1455
|
+
actorKey: '角色 Key',
|
|
1456
|
+
arch: '架构',
|
|
1457
|
+
assetCount: '素材数',
|
|
1458
|
+
assetId: '素材 ID',
|
|
1459
|
+
assetKey: '素材 Key',
|
|
1460
|
+
assetKind: '素材类型',
|
|
1461
|
+
assetName: '素材名',
|
|
1462
|
+
assetPath: '素材路径',
|
|
1463
|
+
authType: '认证方式',
|
|
1464
|
+
backendPath: '后端路径',
|
|
1465
|
+
batchCount: '批次数',
|
|
1466
|
+
billingPointBalance: '可扣积分余额',
|
|
1467
|
+
billingPointRemainingAfter: '扣除后可扣积分',
|
|
1468
|
+
checkedAt: '检查时间',
|
|
1469
|
+
cleared: '已清空',
|
|
1470
|
+
clipCount: 'Clip 数',
|
|
1471
|
+
clipId: 'Clip ID',
|
|
1472
|
+
command: '命令',
|
|
1473
|
+
configured: '已配置',
|
|
1474
|
+
count: '数量',
|
|
1475
|
+
created: '已创建',
|
|
1476
|
+
createCommand: '创建命令',
|
|
1477
|
+
customBizId: '业务追踪 ID',
|
|
1478
|
+
deleted: '已删除',
|
|
1479
|
+
description: '描述',
|
|
1480
|
+
displayName: '展示名',
|
|
1481
|
+
taskKind: '任务类型',
|
|
1482
|
+
doctorStatus: '体检状态',
|
|
1483
|
+
dryRun: '预览模式',
|
|
1484
|
+
duration: '时长',
|
|
1485
|
+
elementId: '主体元素 ID',
|
|
1486
|
+
enabled: '启用',
|
|
1487
|
+
entityKey: '实体 Key',
|
|
1488
|
+
episodeCount: '集数',
|
|
1489
|
+
episodeId: '集 ID',
|
|
1490
|
+
errorMessage: '错误信息',
|
|
1491
|
+
externalId: '外部 ID',
|
|
1492
|
+
fileCount: '文件数',
|
|
1493
|
+
feeCommand: '估价命令',
|
|
1494
|
+
format: '格式',
|
|
1495
|
+
generateNum: '生成数量',
|
|
1496
|
+
gmtCreate: '创建时间',
|
|
1497
|
+
groupId: '团队 ID',
|
|
1498
|
+
groupName: '团队名称',
|
|
1499
|
+
id: 'ID',
|
|
1500
|
+
imported: '已导入',
|
|
1501
|
+
inputDir: '输入目录',
|
|
1502
|
+
inputFile: '输入文件',
|
|
1503
|
+
inputSummary: '输入摘要',
|
|
1504
|
+
isSelected: '当前选中',
|
|
1505
|
+
isTerminal: '终态',
|
|
1506
|
+
key: 'Key',
|
|
1507
|
+
localFileCount: '本地文件数',
|
|
1508
|
+
locationCount: '场景数',
|
|
1509
|
+
locationKey: '场景 Key',
|
|
1510
|
+
memberCount: '成员数',
|
|
1511
|
+
messageCount: '消息数',
|
|
1512
|
+
modelCode: '模型编码',
|
|
1513
|
+
modelDesc: '模型说明',
|
|
1514
|
+
modelGroupCode: '模型组编码',
|
|
1515
|
+
modelStatus: '模型状态',
|
|
1516
|
+
name: '名称',
|
|
1517
|
+
needAudio: '需要音频',
|
|
1518
|
+
nextCommand: '下一步命令',
|
|
1519
|
+
nextRefSubject: '主体引用参数',
|
|
1520
|
+
nextVoiceArg: '音色参数',
|
|
1521
|
+
originUrls: '原始素材',
|
|
1522
|
+
parentKey: '父级 Key',
|
|
1523
|
+
platform: '平台',
|
|
1524
|
+
point: '预算点数',
|
|
1525
|
+
pointCost: '预计消耗积分',
|
|
1526
|
+
pointNo: '积分规格',
|
|
1527
|
+
pointTotal: '积分合计',
|
|
1528
|
+
projectBudgetBalance: '项目预算余额',
|
|
1529
|
+
projectBudgetMax: '项目预算上限',
|
|
1530
|
+
projectBudgetRemainingAfter: '扣除后项目预算',
|
|
1531
|
+
projectGroupName: '项目组名称',
|
|
1532
|
+
projectGroupNo: '项目组编号',
|
|
1533
|
+
projectId: 'AWB 项目 ID',
|
|
1534
|
+
prompt: '提示词',
|
|
1535
|
+
propCount: '道具数',
|
|
1536
|
+
propKey: '道具 Key',
|
|
1537
|
+
publicId: '公开任务 ID',
|
|
1538
|
+
quality: '清晰度',
|
|
1539
|
+
ratio: '宽高比',
|
|
1540
|
+
registered: '已注册',
|
|
1541
|
+
remoteTaskId: '远端任务 ID',
|
|
1542
|
+
requestSource: '请求来源',
|
|
1543
|
+
resourceConversion: '素材转换',
|
|
1544
|
+
resourceConversionCount: '素材转换数',
|
|
1545
|
+
resourceCount: '素材数',
|
|
1546
|
+
resultCount: '结果数',
|
|
1547
|
+
resultUrls: '结果链接',
|
|
1548
|
+
reused: '已复用',
|
|
1549
|
+
rowCount: '行数',
|
|
1550
|
+
rowKind: '行类型',
|
|
1551
|
+
saved: '已保存',
|
|
1552
|
+
sceneCount: '场数',
|
|
1553
|
+
sceneId: '场 ID',
|
|
1554
|
+
selected: '已选择',
|
|
1555
|
+
sortOrder: '排序',
|
|
1556
|
+
source: '来源',
|
|
1557
|
+
sourceFile: '来源文件',
|
|
1558
|
+
sourceTaskId: '来源任务 ID',
|
|
1559
|
+
stateKey: '状态 Key',
|
|
1560
|
+
status: '状态',
|
|
1561
|
+
statusCommandTaskType: '状态任务类型',
|
|
1562
|
+
submitted: '已提交',
|
|
1563
|
+
successRate: '成功率',
|
|
1564
|
+
subjectId: '主体 ID',
|
|
1565
|
+
taskCount: '任务数',
|
|
1566
|
+
taskId: '任务 ID',
|
|
1567
|
+
taskQueueNum: '排队数',
|
|
1568
|
+
taskStatus: '任务状态',
|
|
1569
|
+
taskType: '任务类型',
|
|
1570
|
+
timedOut: '已超时',
|
|
1571
|
+
title: '标题',
|
|
1572
|
+
updated: '已更新',
|
|
1573
|
+
userId: '用户 ID',
|
|
1574
|
+
userName: '用户名',
|
|
1575
|
+
verification: '验证方式',
|
|
1576
|
+
verified: '已验证',
|
|
1577
|
+
verify: '联网验证',
|
|
1578
|
+
verifyCommand: '验证命令',
|
|
1579
|
+
videoEpisodeId: '视频集 ID',
|
|
1580
|
+
voiceId: '音色 ID',
|
|
1581
|
+
voiceName: '音色名称',
|
|
1582
|
+
voiceRecordId: '音色记录 ID',
|
|
1583
|
+
voiceUrl: '音频地址',
|
|
1584
|
+
apiOrigin: 'API 地址',
|
|
1585
|
+
authState: '认证状态',
|
|
1586
|
+
cli: 'CLI',
|
|
1587
|
+
commandCount: '命令数',
|
|
1588
|
+
commandFilter: '命令过滤',
|
|
1589
|
+
commonFieldCount: '通用字段数',
|
|
1590
|
+
domainFilter: '领域过滤',
|
|
1591
|
+
nodeVersion: 'Node 版本',
|
|
1592
|
+
resourceFieldCount: '资源字段数',
|
|
1593
|
+
resourceRuleCount: '资源规则数',
|
|
1594
|
+
schemaVersion: 'Schema 版本',
|
|
1595
|
+
version: '版本',
|
|
1596
|
+
type: '类型',
|
|
1597
|
+
message: '消息',
|
|
1598
|
+
hint: '提示',
|
|
1599
|
+
unknownOptions: '未知参数',
|
|
1600
|
+
note: '说明',
|
|
1601
|
+
commandPrefix: '命令前缀',
|
|
1602
|
+
elementDescription: '主体描述',
|
|
1603
|
+
elementFrontalImage: '主体正面图',
|
|
1604
|
+
elementName: '主体名称',
|
|
1605
|
+
referenceType: '引用类型',
|
|
1606
|
+
remoteUpload: '远程上传',
|
|
1607
|
+
remoteUrl: '远程地址',
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
const DETAIL_FIELDS_BY_KIND = {
|
|
1611
|
+
account_summary: ['userId', 'userName', 'groupId', 'groupName', 'currentProjectGroupNo', 'currentProjectGroupName', 'billingPointBalance', 'projectBudgetBalance', 'projectBudgetMax'],
|
|
1612
|
+
artifact_delete_result: ['deleted', 'action', 'projectId', 'rowKind', 'entityKey', 'key', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId'],
|
|
1613
|
+
artifact_full: ['projectId', 'id', 'title', 'status', 'rowCount', 'actorCount', 'propCount', 'locationCount', 'episodeCount', 'sceneCount', 'clipCount'],
|
|
1614
|
+
artifact_import_result: ['imported', 'dryRun', 'action', 'projectId', 'sourceFile', 'inputDir', 'inputFile', 'rowCount', 'actorCount', 'propCount', 'locationCount', 'episodeCount', 'sceneCount', 'clipCount', 'batchCount'],
|
|
1615
|
+
artifact_record: ['id', 'projectId', 'rowKind', 'entityKey', 'parentKey', 'actorKey', 'propKey', 'locationKey', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId', 'status', 'title', 'name', 'displayName', 'description'],
|
|
1616
|
+
artifact_status_update: ['updated', 'videoEpisodeId', 'status', 'messageCount'],
|
|
1617
|
+
artifact_write_result: ['id', 'projectId', 'rowKind', 'entityKey', 'parentKey', 'actorKey', 'propKey', 'locationKey', 'stateKey', 'episodeId', 'sceneId', 'clipId', 'videoEpisodeId', 'status', 'title', 'name', 'displayName'],
|
|
1618
|
+
asset_group_detail: ['groupId', 'platform', 'name', 'projectName', 'description', 'status', 'assetCount'],
|
|
1619
|
+
asset_group_write_result: ['created', 'updated', 'groupId', 'platform', 'name', 'projectName', 'assetPath'],
|
|
1620
|
+
asset_register_result: ['created', 'updated', 'registered', 'assetId', 'groupId', 'platform', 'name', 'projectName', 'assetPath', 'uploadBackendPath', 'uploadUrl'],
|
|
1621
|
+
auth_result: ['saved', 'verified', 'accessKey', 'source', 'dryRun', 'userId', 'userName', 'groupId', 'groupName'],
|
|
1622
|
+
auth_status: ['configured', 'authType', 'accessKey', 'source', 'verified', 'verification', 'verifyCommand', 'userId', 'userName', 'groupId', 'groupName'],
|
|
1623
|
+
batch_subject_publish_result: ['dryRun', 'submitted', 'count', 'successCount', 'failureCount', 'modelCode', 'projectGroupNo'],
|
|
1624
|
+
batch_task_status: ['count', 'taskCount', 'successTaskCount', 'resultCount', 'pointTotal', 'timedOut', 'waitedMs'],
|
|
1625
|
+
batch_task_submission: ['dryRun', 'submitted', 'count', 'taskCount', 'successCount', 'failureCount', 'modelGroupCode', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'projectBudgetBalance'],
|
|
1626
|
+
credits_balance: ['billingPointBalance', 'currentProjectGroupNo', 'currentProjectGroupName', 'projectBudgetBalance', 'projectBudgetMax'],
|
|
1627
|
+
credits_usage: ['projectGroupNo', 'sinceText', 'untilText', 'scannedTaskCount', 'pointTotal'],
|
|
1628
|
+
environment_report: ['doctorStatus', 'verify'],
|
|
1629
|
+
fee_estimate: ['modelGroupCode', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter'],
|
|
1630
|
+
generic: ['cleared', 'selected', 'created', 'updated', 'reused', 'dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'groupId', 'groupName', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1631
|
+
local_task_records: ['count', 'taskCount', 'successTaskCount', 'resultCount', 'pointTotal'],
|
|
1632
|
+
project_summary: ['selected', 'created', 'updated', 'reused', 'projectGroupNo', 'projectGroupName', 'isSelected', 'projectBudgetBalance', 'projectBudgetMax', 'memberCount'],
|
|
1633
|
+
subject_publish_result: ['created', 'name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'nextRefSubject'],
|
|
1634
|
+
subject_status: ['name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'isTerminal', 'nextRefSubject'],
|
|
1635
|
+
subject_voice_create_result: ['created', 'name', 'voiceRecordId', 'reqTaskId', 'voiceId', 'externalId', 'status', 'taskStatus', 'errorMessage', 'nextVoiceArg'],
|
|
1636
|
+
subject_voice_status: ['name', 'voiceRecordId', 'reqTaskId', 'voiceId', 'externalId', 'status', 'taskStatus', 'errorMessage', 'isTerminal', 'nextVoiceArg'],
|
|
1637
|
+
subtitle_task_status: ['taskId', 'publicId', 'remoteTaskId', 'taskType', 'taskStatus', 'isTerminal', 'projectGroupNo', 'resultCount', 'errorMessage', 'timedOut', 'waitedMs'],
|
|
1638
|
+
subtitle_task_submission: ['submitted', 'taskId', 'taskType', 'sourceTaskId', 'projectGroupNo', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter', 'nextCommand'],
|
|
1639
|
+
task_status: ['taskId', 'publicId', 'remoteTaskId', 'taskType', 'taskStatus', 'isTerminal', 'modelGroupCode', 'projectGroupNo', 'pointNo', 'gmtCreate', 'resultCount', 'errorMessage', 'timedOut', 'waitedMs'],
|
|
1640
|
+
task_submission: ['taskId', 'taskType', 'modelGroupCode', 'projectGroupNo', 'pointNo', 'points', 'pointCost', 'billingPointBalance', 'billingPointRemainingAfter', 'projectBudgetBalance', 'projectBudgetMax', 'projectBudgetRemainingAfter', 'uploadCount', 'nextCommand'],
|
|
1641
|
+
upload_result: ['dryRun', 'fileCount', 'assetCount', 'localFileCount', 'resourceConversionCount'],
|
|
1642
|
+
};
|
|
1643
|
+
|
|
1644
|
+
const DRY_RUN_FIELDS_BY_COMMAND = {
|
|
1645
|
+
'auth login': ['dryRun', 'saved', 'verified', 'accessKey'],
|
|
1646
|
+
'auth clear': ['dryRun', 'action', 'cleared'],
|
|
1647
|
+
'account switch-team': ['dryRun', 'action', 'groupId', 'selected'],
|
|
1648
|
+
'project use': ['dryRun', 'action', 'projectGroupNo', 'selected'],
|
|
1649
|
+
'project create': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1650
|
+
'project update': ['dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'point', 'memberCount'],
|
|
1651
|
+
'project ensure': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1652
|
+
'upload files': ['dryRun', 'fileCount', 'localFileCount', 'resourceConversionCount'],
|
|
1653
|
+
'create image': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'ratio', 'quality', 'generateNum', 'resourceCount', 'localFileCount', 'assetCount'],
|
|
1654
|
+
'create video': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'duration', 'ratio', 'quality', 'needAudio', 'resourceCount', 'localFileCount', 'assetCount'],
|
|
1655
|
+
'create image-batch': ['dryRun', 'count', 'modelGroupCode', 'projectGroupNo'],
|
|
1656
|
+
'create video-batch': ['dryRun', 'count', 'modelGroupCode', 'projectGroupNo'],
|
|
1657
|
+
'create video-subtitle-removal': ['dryRun', 'action', 'sourceTaskId', 'projectGroupNo'],
|
|
1658
|
+
'create asset-group': ['dryRun', 'action', 'platform', 'name', 'projectName'],
|
|
1659
|
+
'create asset-group-update': ['dryRun', 'action', 'groupId', 'platform', 'name', 'projectName'],
|
|
1660
|
+
'create asset': ['dryRun', 'action', 'groupId', 'platform', 'name', 'assetPath', 'localFileCount', 'fileCount'],
|
|
1661
|
+
'create subject': ['dryRun', 'action', 'name', 'modelCode', 'elementFrontalImage', 'assetCount', 'localFileCount', 'nextRefSubject'],
|
|
1662
|
+
'create subject-voice': ['dryRun', 'action', 'name', 'voiceName', 'voiceUrl', 'remoteUrl', 'remoteUpload', 'localFileCount', 'nextVoiceArg'],
|
|
1663
|
+
'create subject-batch': ['dryRun', 'count', 'modelCode'],
|
|
1664
|
+
'artifact script import': ['dryRun', 'action', 'projectId', 'sourceFile', 'inputFile', 'rowCount', 'actorCount', 'episodeCount', 'sceneCount', 'clipCount'],
|
|
1665
|
+
'artifact asset import': ['dryRun', 'action', 'projectId', 'inputDir', 'actorCount', 'propCount', 'locationCount'],
|
|
1666
|
+
'artifact video import-storyboard': ['dryRun', 'action', 'projectId', 'inputFile', 'episodeId', 'sceneCount', 'clipCount'],
|
|
1667
|
+
'artifact clip upsert-batch': ['dryRun', 'action', 'projectId', 'episodeCount'],
|
|
1668
|
+
'artifact clip update-status': ['dryRun', 'action', 'projectId', 'videoEpisodeId', 'status', 'messageCount'],
|
|
1669
|
+
};
|
|
1670
|
+
|
|
1671
|
+
const LIST_KEY_BY_KIND = {
|
|
1672
|
+
artifact_list: 'items',
|
|
1673
|
+
asset_group_list: 'groups',
|
|
1674
|
+
asset_match_list: 'matches',
|
|
1675
|
+
asset_review_model_list: 'models',
|
|
1676
|
+
batch_subject_publish_result: 'results',
|
|
1677
|
+
batch_task_status: 'records',
|
|
1678
|
+
batch_task_submission: 'results',
|
|
1679
|
+
command_schema: 'commands',
|
|
1680
|
+
credits_usage: 'buckets',
|
|
1681
|
+
local_task_records: 'records',
|
|
1682
|
+
model_list: 'models',
|
|
1683
|
+
project_list: 'projectGroups',
|
|
1684
|
+
project_user_list: 'users',
|
|
1685
|
+
subject_list: 'subjects',
|
|
1686
|
+
subject_voice_list: 'voices',
|
|
1687
|
+
task_list: 'tasks',
|
|
1688
|
+
team_list: 'teams',
|
|
1689
|
+
upload_result: 'files',
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
const PRETTY_KIND_PROFILES = Object.fromEntries(Object.keys(KIND_TITLES).map((kind) => [kind, {
|
|
1693
|
+
title: KIND_TITLES[kind],
|
|
1694
|
+
fields: DETAIL_FIELDS_BY_KIND[kind] || [],
|
|
1695
|
+
listKey: LIST_KEY_BY_KIND[kind],
|
|
1696
|
+
}]));
|
|
1697
|
+
|
|
1698
|
+
export const PRETTY_OUTPUT_KIND_COVERAGE = Object.freeze(Object.keys(PRETTY_KIND_PROFILES));
|
|
1699
|
+
|
|
1700
|
+
function outputKindForCommand(commandName, context = {}) {
|
|
1701
|
+
return context.outputKind || OUTPUT_KIND_BY_COMMAND[commandName] || 'generic';
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
function prettyTitle(commandName, kind, data) {
|
|
1705
|
+
const base = COMMAND_TITLES[commandName] || PRETTY_KIND_PROFILES[kind]?.title || KIND_TITLES[kind] || '命令结果';
|
|
1706
|
+
const title = commandName === 'schema' && data?.kind === 'agent_brief' ? 'Agent 能力摘要' : base;
|
|
1707
|
+
return titleText(data?.dryRun ? `${title}(预览)` : title);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
function stripAnsi(value) {
|
|
1711
|
+
return String(value ?? '').replace(/\u001b\[[0-9;]*m/g, '');
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function charDisplayWidth(char) {
|
|
1715
|
+
const code = char.codePointAt(0);
|
|
1716
|
+
if (code === 0) return 0;
|
|
1717
|
+
if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
|
|
1718
|
+
return (
|
|
1719
|
+
code >= 0x1100
|
|
1720
|
+
&& (code <= 0x115f
|
|
1721
|
+
|| code === 0x2329
|
|
1722
|
+
|| code === 0x232a
|
|
1723
|
+
|| (code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f)
|
|
1724
|
+
|| (code >= 0xac00 && code <= 0xd7a3)
|
|
1725
|
+
|| (code >= 0xf900 && code <= 0xfaff)
|
|
1726
|
+
|| (code >= 0xfe10 && code <= 0xfe19)
|
|
1727
|
+
|| (code >= 0xfe30 && code <= 0xfe6f)
|
|
1728
|
+
|| (code >= 0xff00 && code <= 0xff60)
|
|
1729
|
+
|| (code >= 0xffe0 && code <= 0xffe6))
|
|
1730
|
+
) ? 2 : 1;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
function displayWidth(value) {
|
|
1734
|
+
let width = 0;
|
|
1735
|
+
for (const char of stripAnsi(value)) width += charDisplayWidth(char);
|
|
1736
|
+
return width;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
function truncateDisplay(value, maxWidth = 32) {
|
|
1740
|
+
const text = String(value ?? '');
|
|
1741
|
+
if (displayWidth(text) <= maxWidth) return text;
|
|
1742
|
+
const marker = '...';
|
|
1743
|
+
const markerWidth = displayWidth(marker);
|
|
1744
|
+
let width = 0;
|
|
1745
|
+
let out = '';
|
|
1746
|
+
for (const char of text) {
|
|
1747
|
+
const charWidth = charDisplayWidth(char);
|
|
1748
|
+
if (width + charWidth + markerWidth > maxWidth) break;
|
|
1749
|
+
out += char;
|
|
1750
|
+
width += charWidth;
|
|
1751
|
+
}
|
|
1752
|
+
return `${out}${marker}`;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
function wrapDisplay(value, width) {
|
|
1756
|
+
const maxWidth = Math.max(1, width);
|
|
1757
|
+
const text = String(value ?? '');
|
|
1758
|
+
const lines = [];
|
|
1759
|
+
const pushHardWrapped = (chunk, state) => {
|
|
1760
|
+
for (const char of chunk) {
|
|
1761
|
+
const charWidth = charDisplayWidth(char);
|
|
1762
|
+
if (state.lineWidth > 0 && state.lineWidth + charWidth > maxWidth) {
|
|
1763
|
+
lines.push(state.line);
|
|
1764
|
+
state.line = char;
|
|
1765
|
+
state.lineWidth = charWidth;
|
|
1766
|
+
} else {
|
|
1767
|
+
state.line += char;
|
|
1768
|
+
state.lineWidth += charWidth;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
};
|
|
1772
|
+
const tokenBoundary = (char) => /[\s、,,;;|/\\_-]/u.test(char);
|
|
1773
|
+
for (const segment of text.split(/\r?\n/)) {
|
|
1774
|
+
if (!segment) {
|
|
1775
|
+
lines.push('');
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
const state = { line: '', lineWidth: 0 };
|
|
1779
|
+
let token = '';
|
|
1780
|
+
const flushToken = () => {
|
|
1781
|
+
if (!token) return;
|
|
1782
|
+
const tokenWidth = displayWidth(token);
|
|
1783
|
+
if (tokenWidth > maxWidth) {
|
|
1784
|
+
pushHardWrapped(token, state);
|
|
1785
|
+
} else if (state.lineWidth > 0 && state.lineWidth + tokenWidth > maxWidth) {
|
|
1786
|
+
lines.push(state.line);
|
|
1787
|
+
state.line = token.trimStart();
|
|
1788
|
+
state.lineWidth = displayWidth(state.line);
|
|
1789
|
+
} else {
|
|
1790
|
+
state.line += token;
|
|
1791
|
+
state.lineWidth += tokenWidth;
|
|
1792
|
+
}
|
|
1793
|
+
token = '';
|
|
1794
|
+
};
|
|
1795
|
+
for (const char of segment) {
|
|
1796
|
+
token += char;
|
|
1797
|
+
if (tokenBoundary(char)) flushToken();
|
|
1798
|
+
}
|
|
1799
|
+
flushToken();
|
|
1800
|
+
lines.push(state.line);
|
|
1801
|
+
}
|
|
1802
|
+
return lines.length ? lines : [''];
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
function padDisplay(value, width) {
|
|
1806
|
+
const text = String(value ?? '');
|
|
1807
|
+
return `${text}${' '.repeat(Math.max(0, width - displayWidth(text)))}`;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function shouldColorOutput() {
|
|
1811
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '0') return false;
|
|
1812
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'false') return false;
|
|
1813
|
+
if (String(process.env.FORCE_COLOR || '').trim() && process.env.FORCE_COLOR !== '0') return true;
|
|
1814
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === '1') return true;
|
|
1815
|
+
if (String(process.env.LINGJING_AWB_COLOR || '').toLowerCase() === 'true') return true;
|
|
1816
|
+
if (process.env.NO_COLOR) return false;
|
|
1817
|
+
return Boolean(process.stdout.isTTY);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
function colorText(value, code) {
|
|
1821
|
+
const text = String(value ?? '');
|
|
1822
|
+
if (!shouldColorOutput() || !text) return text;
|
|
1823
|
+
const colorMatch = String(code).match(/38;5;(\d+)/);
|
|
1824
|
+
let rendered = colorMatch ? CHALK.ansi256(Number(colorMatch[1]))(text) : text;
|
|
1825
|
+
if (String(code).includes('36')) rendered = CHALK.cyan(text);
|
|
1826
|
+
if (String(code).split(';').includes('1')) rendered = CHALK.bold(rendered);
|
|
1827
|
+
if (String(code).split(';').includes('2')) rendered = CHALK.dim(rendered);
|
|
1828
|
+
return rendered;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
const COLOR = {
|
|
1832
|
+
title: '1;38;5;39',
|
|
1833
|
+
accent: '1;38;5;208',
|
|
1834
|
+
section: '1;38;5;75',
|
|
1835
|
+
border: '38;5;238',
|
|
1836
|
+
header: '1;38;5;81',
|
|
1837
|
+
label: '38;5;215',
|
|
1838
|
+
value: '38;5;250',
|
|
1839
|
+
command: '38;5;82',
|
|
1840
|
+
code: '38;5;111',
|
|
1841
|
+
success: '38;5;82',
|
|
1842
|
+
warning: '38;5;220',
|
|
1843
|
+
danger: '38;5;203',
|
|
1844
|
+
muted: '2;38;5;245',
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
function titleText(value) {
|
|
1848
|
+
if (!shouldColorOutput()) return String(value ?? '');
|
|
1849
|
+
return `${colorText('◆', COLOR.accent)} ${colorText(value, COLOR.title)}`;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
function errorTitleText(value) {
|
|
1853
|
+
if (!shouldColorOutput()) return String(value ?? '');
|
|
1854
|
+
return `${colorText('◆', COLOR.danger)} ${colorText(value, '1;38;5;203')}`;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
function sectionText(value) {
|
|
1858
|
+
if (!shouldColorOutput()) return String(value ?? '');
|
|
1859
|
+
return `${colorText('▸', COLOR.accent)} ${colorText(value, COLOR.section)}`;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function borderText(value) {
|
|
1863
|
+
return colorText(value, COLOR.border);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
function headerText(value) {
|
|
1867
|
+
return colorText(value, COLOR.header);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
function labelText(value) {
|
|
1871
|
+
return colorText(value, COLOR.label);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
function mutedText(value) {
|
|
1875
|
+
return colorText(value, COLOR.muted);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
function commandText(value) {
|
|
1879
|
+
return colorText(value, COLOR.command);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
function codeText(value) {
|
|
1883
|
+
return colorText(value, COLOR.code);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
function semanticText(value) {
|
|
1887
|
+
const text = String(value ?? '');
|
|
1888
|
+
if (!shouldColorOutput() || !text) return text;
|
|
1889
|
+
const plain = stripAnsi(text).trim().toLowerCase();
|
|
1890
|
+
if (/^(是|true|success|succeeded|completed|done|ok|healthy|passed|enabled|active|已配置|已登录|成功)$/.test(plain)) {
|
|
1891
|
+
return colorText(text, COLOR.success);
|
|
1892
|
+
}
|
|
1893
|
+
if (/^(否|false|pending|running|processing|queued|warning|warn|dry-run|dryrun|预览|缓存过期|未登录|未选择|未知)$/.test(plain)) {
|
|
1894
|
+
return colorText(text, COLOR.warning);
|
|
1895
|
+
}
|
|
1896
|
+
if (/^(failed|failure|error|errored|fatal|invalid|denied|rejected|deleted|失败|错误|无效|删除)$/.test(plain)) {
|
|
1897
|
+
return colorText(text, COLOR.danger);
|
|
1898
|
+
}
|
|
1899
|
+
return text;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
function prettyCellText(value, columnInfo = {}) {
|
|
1903
|
+
const text = String(value ?? '');
|
|
1904
|
+
if (!shouldColorOutput() || !text) return text;
|
|
1905
|
+
const header = stripAnsi(columnInfo.header || '');
|
|
1906
|
+
if (header === '#' || columnInfo.index) return mutedText(text);
|
|
1907
|
+
if (['状态', '当前', '必填', '验证', 'WebP'].includes(header)) return semanticText(text);
|
|
1908
|
+
if (['CLI', '命令', '继续查询', '生成创建写法'].includes(header) || stripAnsi(text).startsWith('--')) return codeText(text);
|
|
1909
|
+
if (stripAnsi(text).startsWith(commandPrefix())) return commandText(text);
|
|
1910
|
+
if (['队列', '排队数', '成功率', '结果数', '任务数', '积分', '数量', '文件数'].includes(header)) return colorText(text, COLOR.accent);
|
|
1911
|
+
return text;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
function prettyDetailValue(value, label = '') {
|
|
1915
|
+
const text = String(value ?? '');
|
|
1916
|
+
if (!shouldColorOutput() || !text) return text;
|
|
1917
|
+
if (/命令|写法|CLI|JSON|Agent|Dry-run|继续|确认|执行/.test(label) || stripAnsi(text).startsWith(commandPrefix())) {
|
|
1918
|
+
return commandText(text);
|
|
1919
|
+
}
|
|
1920
|
+
if (/状态|验证|当前|必填|已/.test(label)) return semanticText(text);
|
|
1921
|
+
return colorText(text, COLOR.value);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
function humanizeKey(key) {
|
|
1925
|
+
if (FIELD_LABELS[key]) return FIELD_LABELS[key];
|
|
1926
|
+
return String(key || '')
|
|
1927
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
1928
|
+
.replace(/[_-]+/g, ' ')
|
|
1929
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
function prettyBoolean(value) {
|
|
1933
|
+
return value ? '是' : '否';
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
function prettyValue(value, options = {}) {
|
|
1937
|
+
if (value === undefined || value === null || value === '') return '';
|
|
1938
|
+
if (typeof value === 'boolean') return prettyBoolean(value);
|
|
1939
|
+
if (Array.isArray(value)) {
|
|
1940
|
+
if (!value.length) return '无';
|
|
1941
|
+
if (value.every((item) => !isPlainObject(item) && !Array.isArray(item))) return value.map((item) => prettyValue(item)).join('、');
|
|
1942
|
+
return `${value.length} 项`;
|
|
1943
|
+
}
|
|
1944
|
+
if (isPlainObject(value)) {
|
|
1945
|
+
const summary = renderRecord(compactRecord(value), options.objectKeys || []);
|
|
1946
|
+
return summary || JSON.stringify(value);
|
|
1947
|
+
}
|
|
1948
|
+
return rewriteCommandPrefix(value);
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
function resolveColumnValue(row, column) {
|
|
1952
|
+
if (typeof column.value === 'function') return column.value(row);
|
|
1953
|
+
if (typeof column.key === 'function') return column.key(row);
|
|
1954
|
+
if (Array.isArray(column.key)) {
|
|
1955
|
+
for (const key of column.key) {
|
|
1956
|
+
if (row?.[key] !== undefined && row?.[key] !== null && row?.[key] !== '') return row[key];
|
|
1957
|
+
}
|
|
1958
|
+
return undefined;
|
|
1959
|
+
}
|
|
1960
|
+
return row?.[column.key];
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
function column(header, key, options = {}) {
|
|
1964
|
+
return { header, key, ...options };
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function joinValueList(value, limit = 6) {
|
|
1968
|
+
if (!Array.isArray(value)) return prettyValue(value);
|
|
1969
|
+
return valueList(value, limit)?.replaceAll('|', '、') || '';
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
const TABLE_COLUMNS = {
|
|
1973
|
+
assets: [
|
|
1974
|
+
column('标签', 'label', { maxWidth: 14 }),
|
|
1975
|
+
column('引用类型', ['referenceType', 'type'], { maxWidth: 16 }),
|
|
1976
|
+
column('用途', 'usage', { maxWidth: 16 }),
|
|
1977
|
+
column('路径', 'assetPath', { maxWidth: 42 }),
|
|
1978
|
+
column('素材 ID', 'assetId', { maxWidth: 22 }),
|
|
1979
|
+
],
|
|
1980
|
+
buckets: [
|
|
1981
|
+
column('任务类型', 'taskType', { maxWidth: 24 }),
|
|
1982
|
+
column('任务数', 'taskCount', { maxWidth: 10 }),
|
|
1983
|
+
column('成功数', 'successTaskCount', { maxWidth: 10 }),
|
|
1984
|
+
column('结果数', 'resultCount', { maxWidth: 10 }),
|
|
1985
|
+
column('积分', 'pointTotal', { maxWidth: 10 }),
|
|
1986
|
+
],
|
|
1987
|
+
checks: [
|
|
1988
|
+
column('检查项', 'name', { maxWidth: 18 }),
|
|
1989
|
+
column('状态', 'status', { maxWidth: 12 }),
|
|
1990
|
+
column('说明', 'message', { maxWidth: 58 }),
|
|
1991
|
+
],
|
|
1992
|
+
commands: [
|
|
1993
|
+
column('命令', 'name', { maxWidth: 34 }),
|
|
1994
|
+
column('领域', 'domain', { maxWidth: 12 }),
|
|
1995
|
+
column('输出', (row) => row.workflow?.outputKind, { maxWidth: 24 }),
|
|
1996
|
+
column('安全', (row) => (row.safety?.safeToAutoRun ? '可自动运行' : '需确认/写入'), { maxWidth: 14 }),
|
|
1997
|
+
column('说明', 'summary', { maxWidth: 44 }),
|
|
1998
|
+
],
|
|
1999
|
+
domains: [
|
|
2000
|
+
column('领域', 'name', { maxWidth: 14 }),
|
|
2001
|
+
column('命令数', 'commandCount', { maxWidth: 10 }),
|
|
2002
|
+
column('说明', 'description', { maxWidth: 60 }),
|
|
2003
|
+
],
|
|
2004
|
+
files: [
|
|
2005
|
+
column('文件', ['filePath', 'path', 'fileName'], { maxWidth: 38 }),
|
|
2006
|
+
column('场景', 'sceneType', { maxWidth: 24 }),
|
|
2007
|
+
column('类型', ['mimeType', 'mime'], { maxWidth: 18 }),
|
|
2008
|
+
column('大小', 'size', { maxWidth: 10 }),
|
|
2009
|
+
column('后端路径', ['backendPath', 'url'], { maxWidth: 42 }),
|
|
2010
|
+
],
|
|
2011
|
+
groups: [
|
|
2012
|
+
column('组 ID', 'groupId', { maxWidth: 24 }),
|
|
2013
|
+
column('平台', 'platform', { maxWidth: 12 }),
|
|
2014
|
+
column('名称', 'name', { maxWidth: 24 }),
|
|
2015
|
+
column('项目名', 'projectName', { maxWidth: 24 }),
|
|
2016
|
+
column('状态', 'status', { maxWidth: 14 }),
|
|
2017
|
+
],
|
|
2018
|
+
items: [
|
|
2019
|
+
column('ID', ['id', 'entityKey', 'key'], { maxWidth: 24 }),
|
|
2020
|
+
column('类型', ['rowKind', 'assetKind'], { maxWidth: 16 }),
|
|
2021
|
+
column('名称', ['title', 'name', 'displayName'], { maxWidth: 30 }),
|
|
2022
|
+
column('父级', 'parentKey', { maxWidth: 22 }),
|
|
2023
|
+
column('状态', 'status', { maxWidth: 16 }),
|
|
2024
|
+
column('说明', 'description', { maxWidth: 42 }),
|
|
2025
|
+
],
|
|
2026
|
+
localFiles: [
|
|
2027
|
+
column('文件', ['path', 'filePath', 'fileName'], { maxWidth: 42 }),
|
|
2028
|
+
column('存在', 'exists', { maxWidth: 8 }),
|
|
2029
|
+
column('类型', ['mimeType', 'mime'], { maxWidth: 18 }),
|
|
2030
|
+
column('大小', 'size', { maxWidth: 10 }),
|
|
2031
|
+
],
|
|
2032
|
+
matches: [
|
|
2033
|
+
column('素材 ID', 'assetId', { maxWidth: 22 }),
|
|
2034
|
+
column('名称', ['name', 'assetName'], { maxWidth: 24 }),
|
|
2035
|
+
column('分数', 'score', { maxWidth: 8 }),
|
|
2036
|
+
column('图片', 'imageUrl', { maxWidth: 42 }),
|
|
2037
|
+
],
|
|
2038
|
+
models: [
|
|
2039
|
+
column('模型组', 'modelGroupCode', { maxWidth: 34 }),
|
|
2040
|
+
column('名称', ['displayName', 'modelName'], { maxWidth: 24 }),
|
|
2041
|
+
column('说明', 'modelDesc', { maxWidth: 34 }),
|
|
2042
|
+
column('排队数', 'taskQueueNum', { maxWidth: 8 }),
|
|
2043
|
+
column('成功率', (row) => formatPercent(row.successRate), { maxWidth: 8 }),
|
|
2044
|
+
column('输入', (row) => joinValueList(row.inputModes), { maxWidth: 28 }),
|
|
2045
|
+
column('参数', (row) => joinValueList(row.params), { maxWidth: 24 }),
|
|
2046
|
+
column('素材', (row) => joinValueList(row.resourceParams), { maxWidth: 24 }),
|
|
2047
|
+
],
|
|
2048
|
+
params: [
|
|
2049
|
+
column('参数', 'key', { maxWidth: 22 }),
|
|
2050
|
+
column('类型', 'valueType', { maxWidth: 12 }),
|
|
2051
|
+
column('可选值', (row) => joinValueList(row.values, 8), { maxWidth: 42 }),
|
|
2052
|
+
column('默认', 'defaultValue', { maxWidth: 14 }),
|
|
2053
|
+
column('必填', 'required', { maxWidth: 8 }),
|
|
2054
|
+
],
|
|
2055
|
+
projectGroups: [
|
|
2056
|
+
column('项目组编号', 'projectGroupNo', { maxWidth: 24 }),
|
|
2057
|
+
column('名称', 'projectGroupName', { maxWidth: 28 }),
|
|
2058
|
+
column('当前', 'isSelected', { maxWidth: 8 }),
|
|
2059
|
+
column('预算余额', ['projectBudgetBalance', 'projectPointBalance'], { maxWidth: 12 }),
|
|
2060
|
+
column('成员', 'memberCount', { maxWidth: 8 }),
|
|
2061
|
+
],
|
|
2062
|
+
records: [
|
|
2063
|
+
column('任务 ID', ['taskId', 'publicId', 'remoteTaskId'], { maxWidth: 28 }),
|
|
2064
|
+
column('类型', 'taskType', { maxWidth: 20 }),
|
|
2065
|
+
column('状态', ['taskStatus', 'status'], { maxWidth: 16 }),
|
|
2066
|
+
column('模型', 'modelGroupCode', { maxWidth: 30 }),
|
|
2067
|
+
column('结果数', 'resultCount', { maxWidth: 8 }),
|
|
2068
|
+
column('记录时间', ['recordedAt', 'gmtCreate', 'createdAt'], { maxWidth: 22 }),
|
|
2069
|
+
],
|
|
2070
|
+
resources: [
|
|
2071
|
+
column('模式', 'mode', { maxWidth: 16 }),
|
|
2072
|
+
column('媒体', 'mediaType', { maxWidth: 10 }),
|
|
2073
|
+
column('用途', (row) => joinValueList(row.usage), { maxWidth: 26 }),
|
|
2074
|
+
column('输入形态', (row) => textValueShapes(row.valueShapes)?.replaceAll('|', '、'), { maxWidth: 28 }),
|
|
2075
|
+
column('格式', (row) => textResourceFormats(row)?.replaceAll('|', '、'), { maxWidth: 28 }),
|
|
2076
|
+
column('文件数', (row) => textFileCount(row), { maxWidth: 10 }),
|
|
2077
|
+
column('WebP', (row) => textWebpInput(row), { maxWidth: 18 }),
|
|
2078
|
+
],
|
|
2079
|
+
results: [
|
|
2080
|
+
column('序号', 'inputIndex', { maxWidth: 8 }),
|
|
2081
|
+
column('状态', ['status', 'taskStatus'], { maxWidth: 16 }),
|
|
2082
|
+
column('任务 ID', 'taskId', { maxWidth: 28 }),
|
|
2083
|
+
column('模型/主体', ['modelGroupCode', 'modelCode', 'name'], { maxWidth: 30 }),
|
|
2084
|
+
column('说明', ['message', 'errorMessage', 'prompt'], { maxWidth: 42 }),
|
|
2085
|
+
],
|
|
2086
|
+
subjects: [
|
|
2087
|
+
column('主体 ID', ['elementId', 'subjectId'], { maxWidth: 24 }),
|
|
2088
|
+
column('名称', 'name', { maxWidth: 24 }),
|
|
2089
|
+
column('模型', 'modelCode', { maxWidth: 14 }),
|
|
2090
|
+
column('外部 ID', 'externalId', { maxWidth: 24 }),
|
|
2091
|
+
column('状态', ['status', 'taskStatus'], { maxWidth: 14 }),
|
|
2092
|
+
],
|
|
2093
|
+
tasks: [
|
|
2094
|
+
column('任务 ID', ['taskId', 'publicId', 'remoteTaskId'], { maxWidth: 28 }),
|
|
2095
|
+
column('类型', 'taskType', { maxWidth: 20 }),
|
|
2096
|
+
column('状态', 'taskStatus', { maxWidth: 16 }),
|
|
2097
|
+
column('模型', 'modelGroupCode', { maxWidth: 30 }),
|
|
2098
|
+
column('结果数', 'resultCount', { maxWidth: 8 }),
|
|
2099
|
+
column('创建时间', 'gmtCreate', { maxWidth: 22 }),
|
|
2100
|
+
],
|
|
2101
|
+
teams: [
|
|
2102
|
+
column('团队 ID', 'groupId', { maxWidth: 22 }),
|
|
2103
|
+
column('团队名称', 'groupName', { maxWidth: 28 }),
|
|
2104
|
+
column('当前', ['currentGroup', 'isSelected'], { maxWidth: 8 }),
|
|
2105
|
+
column('角色', 'role', { maxWidth: 14 }),
|
|
2106
|
+
],
|
|
2107
|
+
users: [
|
|
2108
|
+
column('用户 ID', 'userId', { maxWidth: 22 }),
|
|
2109
|
+
column('用户名', 'userName', { maxWidth: 24 }),
|
|
2110
|
+
column('角色', 'role', { maxWidth: 14 }),
|
|
2111
|
+
column('当前', ['currentGroup', 'isCheck'], { maxWidth: 8 }),
|
|
2112
|
+
],
|
|
2113
|
+
voices: [
|
|
2114
|
+
column('音色记录 ID', ['voiceRecordId', 'reqTaskId'], { maxWidth: 26 }),
|
|
2115
|
+
column('名称', 'name', { maxWidth: 24 }),
|
|
2116
|
+
column('音色 ID', ['voiceId', 'externalId'], { maxWidth: 24 }),
|
|
2117
|
+
column('状态', ['status', 'taskStatus'], { maxWidth: 14 }),
|
|
2118
|
+
],
|
|
2119
|
+
};
|
|
2120
|
+
|
|
2121
|
+
function inferTableColumns(rows = []) {
|
|
2122
|
+
const first = rows.find((row) => isPlainObject(row));
|
|
2123
|
+
if (!first) return [column('值', (row) => prettyValue(row), { maxWidth: 70 })];
|
|
2124
|
+
const preferred = [
|
|
2125
|
+
'id',
|
|
2126
|
+
'taskId',
|
|
2127
|
+
'publicId',
|
|
2128
|
+
'modelGroupCode',
|
|
2129
|
+
'projectGroupNo',
|
|
2130
|
+
'projectId',
|
|
2131
|
+
'rowKind',
|
|
2132
|
+
'entityKey',
|
|
2133
|
+
'name',
|
|
2134
|
+
'displayName',
|
|
2135
|
+
'title',
|
|
2136
|
+
'status',
|
|
2137
|
+
'taskStatus',
|
|
2138
|
+
'resultCount',
|
|
2139
|
+
'errorMessage',
|
|
2140
|
+
];
|
|
2141
|
+
const keys = [
|
|
2142
|
+
...preferred.filter((key) => first[key] !== undefined),
|
|
2143
|
+
...Object.keys(first).filter((key) => !preferred.includes(key) && !Array.isArray(first[key]) && !isPlainObject(first[key])),
|
|
2144
|
+
].slice(0, 6);
|
|
2145
|
+
return keys.map((key) => column(humanizeKey(key), key, { maxWidth: key.toLowerCase().includes('url') ? 42 : 28 }));
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
function renderPrettyTable(rows = [], columns = [], options = {}) {
|
|
2149
|
+
const tableRows = Array.isArray(rows) ? rows : [];
|
|
2150
|
+
if (!tableRows.length) return [` ${mutedText(options.emptyText || '无记录')}`];
|
|
2151
|
+
const rawCols = columns.length ? columns : inferTableColumns(tableRows);
|
|
2152
|
+
const rawRows = tableRows.map((row, index) => rawCols.map((col) => {
|
|
2153
|
+
const raw = col.index ? index + 1 : resolveColumnValue(row, col);
|
|
2154
|
+
const value = String(prettyValue(raw)).replace(/\r\n/g, '\n');
|
|
2155
|
+
return value;
|
|
2156
|
+
}));
|
|
2157
|
+
const visibleColumnIndexes = rawCols
|
|
2158
|
+
.map((col, index) => ({ col, index }))
|
|
2159
|
+
.filter(({ col, index }) => col.always || rawCols.length === 1 || rawRows.some((row) => row[index] !== ''))
|
|
2160
|
+
.map(({ index }) => index);
|
|
2161
|
+
if (!visibleColumnIndexes.length) {
|
|
2162
|
+
visibleColumnIndexes.push(...rawCols.map((_, index) => index));
|
|
2163
|
+
}
|
|
2164
|
+
const cols = visibleColumnIndexes.map((index) => rawCols[index]);
|
|
2165
|
+
const headerCells = cols.map((col) => col.header);
|
|
2166
|
+
const bodyRows = rawRows.map((row) => visibleColumnIndexes.map((index) => row[index]));
|
|
2167
|
+
const widths = cols.map((col, colIndex) => {
|
|
2168
|
+
const values = [headerCells[colIndex], ...bodyRows.map((row) => row[colIndex])];
|
|
2169
|
+
const max = Math.max(...values.map(displayWidth));
|
|
2170
|
+
return Math.max(col.minWidth || 4, Math.min(col.maxWidth || 30, max));
|
|
2171
|
+
});
|
|
2172
|
+
const renderedRows = bodyRows.map((row, rowIndex) => row.map((cell, colIndex) => {
|
|
2173
|
+
const wrapped = wrapDisplay(cell, widths[colIndex]).join('\n');
|
|
2174
|
+
return prettyCellText(wrapped, { ...cols[colIndex], rowIndex });
|
|
2175
|
+
}));
|
|
2176
|
+
const table = new Table({
|
|
2177
|
+
head: headerCells.map((cell) => headerText(cell)),
|
|
2178
|
+
colWidths: widths.map((width) => width + 2),
|
|
2179
|
+
wordWrap: false,
|
|
2180
|
+
truncate: '',
|
|
2181
|
+
style: {
|
|
2182
|
+
head: [],
|
|
2183
|
+
border: [],
|
|
2184
|
+
compact: false,
|
|
2185
|
+
'padding-left': 1,
|
|
2186
|
+
'padding-right': 1,
|
|
2187
|
+
},
|
|
2188
|
+
});
|
|
2189
|
+
table.push(...renderedRows);
|
|
2190
|
+
return table.toString()
|
|
2191
|
+
.split('\n')
|
|
2192
|
+
.map((line) => line.replace(/[┌┬┐├┼┤└┴┘│─]/g, (char) => borderText(char)));
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
function sectionTitle(lines, title) {
|
|
2196
|
+
if (lines.length && lines[lines.length - 1] !== '') lines.push('');
|
|
2197
|
+
lines.push(sectionText(title));
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
function detailRows(record, preferredKeys = [], excludedKeys = [], options = {}) {
|
|
2201
|
+
if (!isPlainObject(record)) return [];
|
|
2202
|
+
const excluded = new Set(excludedKeys);
|
|
2203
|
+
const preferred = preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined && record[key] !== null && record[key] !== '');
|
|
2204
|
+
const fallback = options.includeRest
|
|
2205
|
+
? Object.keys(record).filter((key) => {
|
|
2206
|
+
if (excluded.has(key) || preferredKeys.includes(key)) return false;
|
|
2207
|
+
const value = record[key];
|
|
2208
|
+
if (value === undefined || value === null || value === '') return false;
|
|
2209
|
+
if (Array.isArray(value) || isPlainObject(value)) return false;
|
|
2210
|
+
return true;
|
|
2211
|
+
})
|
|
2212
|
+
: [];
|
|
2213
|
+
const keys = [...preferred, ...fallback];
|
|
2214
|
+
return keys.map((key) => ({ label: humanizeKey(key), value: record[key] }));
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
function renderDetails(lines, title, record, preferredKeys = [], excludedKeys = [], options = {}) {
|
|
2218
|
+
const rows = detailRows(record, preferredKeys, excludedKeys, options);
|
|
2219
|
+
if (!rows.length) return;
|
|
2220
|
+
sectionTitle(lines, title);
|
|
2221
|
+
const labelWidth = Math.min(22, Math.max(...rows.map((row) => displayWidth(row.label))));
|
|
2222
|
+
for (const row of rows) {
|
|
2223
|
+
const value = String(prettyValue(row.value)).replace(/\s*\r?\n\s*/g, ' ');
|
|
2224
|
+
const valueLines = wrapDisplay(value, options.valueWidth || 96);
|
|
2225
|
+
for (const [index, valueLine] of valueLines.entries()) {
|
|
2226
|
+
const label = index === 0 ? row.label : '';
|
|
2227
|
+
lines.push(` ${labelText(padDisplay(label, labelWidth))} ${prettyDetailValue(valueLine, row.label)}`);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
function renderArraySection(lines, title, rows, listKey, limit = Number.POSITIVE_INFINITY) {
|
|
2233
|
+
if (!Array.isArray(rows) || !rows.length) return;
|
|
2234
|
+
const visibleRows = rows.slice(0, Number.isFinite(limit) ? limit : rows.length);
|
|
2235
|
+
sectionTitle(lines, `${title}(${rows.length})`);
|
|
2236
|
+
const columns = Array.isArray(listKey) ? listKey : (TABLE_COLUMNS[listKey] || inferTableColumns(visibleRows));
|
|
2237
|
+
lines.push(...renderPrettyTable(visibleRows, columns));
|
|
2238
|
+
if (rows.length > visibleRows.length) lines.push(` ${mutedText(`还有 ${rows.length - visibleRows.length} 条未展示;使用 --all 或 -f json 查看完整数据。`)}`);
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
function renderScalarList(lines, title, values = [], limit = 8) {
|
|
2242
|
+
if (!Array.isArray(values) || !values.length) return;
|
|
2243
|
+
sectionTitle(lines, `${title}(${values.length})`);
|
|
2244
|
+
for (const [index, value] of values.slice(0, limit).entries()) {
|
|
2245
|
+
lines.push(` ${mutedText(`${index + 1}.`)} ${prettyDetailValue(prettyValue(value), title)}`);
|
|
2246
|
+
}
|
|
2247
|
+
if (values.length > limit) lines.push(` ${mutedText(`还有 ${values.length - limit} 项未展示;使用 -f json 查看完整数据。`)}`);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
function namedListTitle(listKey) {
|
|
2251
|
+
return {
|
|
2252
|
+
assets: '素材',
|
|
2253
|
+
buckets: '用量分组',
|
|
2254
|
+
checks: '检查结果',
|
|
2255
|
+
commands: '命令',
|
|
2256
|
+
domains: '命令领域',
|
|
2257
|
+
files: '文件',
|
|
2258
|
+
groups: '素材组',
|
|
2259
|
+
items: '记录',
|
|
2260
|
+
localFiles: '本地文件',
|
|
2261
|
+
matches: '匹配项',
|
|
2262
|
+
models: '模型',
|
|
2263
|
+
params: '参数',
|
|
2264
|
+
projectGroups: '项目组',
|
|
2265
|
+
records: '记录',
|
|
2266
|
+
resources: '素材约束',
|
|
2267
|
+
results: '结果',
|
|
2268
|
+
subjects: '主体',
|
|
2269
|
+
tasks: '任务',
|
|
2270
|
+
teams: '团队',
|
|
2271
|
+
users: '成员',
|
|
2272
|
+
voices: '音色',
|
|
2273
|
+
}[listKey] || humanizeKey(listKey);
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
function localizedTaskKind(value) {
|
|
2277
|
+
if (value === 'image' || value === 'IMAGE_CREATE') return '图片生成';
|
|
2278
|
+
if (value === 'video' || value === 'VIDEO_CREATE') return '视频生成';
|
|
2279
|
+
return prettyValue(value);
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
function localizedInputMode(value, taskKind = '') {
|
|
2283
|
+
const mode = String(value || '');
|
|
2284
|
+
const isVideo = taskKind === 'video' || taskKind === 'VIDEO_CREATE';
|
|
2285
|
+
const labels = {
|
|
2286
|
+
prompt_only: isVideo ? '文本生成视频' : '文本生成图片',
|
|
2287
|
+
reference: isVideo ? '参考素材生成' : '参考图生成',
|
|
2288
|
+
frames: '首帧/尾帧生成',
|
|
2289
|
+
storyboard: '分镜多镜头',
|
|
2290
|
+
keyframe: '关键帧生成',
|
|
2291
|
+
};
|
|
2292
|
+
return labels[mode] || mode;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
function localizedParamKey(value) {
|
|
2296
|
+
const labels = {
|
|
2297
|
+
ratio: '宽高比',
|
|
2298
|
+
quality: '清晰度',
|
|
2299
|
+
generateNum: '生成数量',
|
|
2300
|
+
generate_num: '生成数量',
|
|
2301
|
+
duration: '时长',
|
|
2302
|
+
generated_time: '时长',
|
|
2303
|
+
needAudio: '生成音频',
|
|
2304
|
+
need_audio: '生成音频',
|
|
2305
|
+
prompt_optimizer: '提示词增强',
|
|
2306
|
+
};
|
|
2307
|
+
return labels[value] || value;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
function localizedParamType(value) {
|
|
2311
|
+
const labels = {
|
|
2312
|
+
enum: '枚举选择',
|
|
2313
|
+
text: '文本',
|
|
2314
|
+
string: '文本',
|
|
2315
|
+
number: '数字',
|
|
2316
|
+
integer: '整数',
|
|
2317
|
+
boolean: '开关',
|
|
2318
|
+
bool: '开关',
|
|
2319
|
+
};
|
|
2320
|
+
return labels[String(value || '').toLowerCase()] || value;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
function cliFlagForParam(key) {
|
|
2324
|
+
const flags = {
|
|
2325
|
+
ratio: '--ratio',
|
|
2326
|
+
quality: '--quality',
|
|
2327
|
+
generateNum: '--generate-num',
|
|
2328
|
+
generate_num: '--generate-num',
|
|
2329
|
+
duration: '--duration',
|
|
2330
|
+
generated_time: '--duration',
|
|
2331
|
+
needAudio: '--need-audio',
|
|
2332
|
+
need_audio: '--need-audio',
|
|
2333
|
+
prompt: '--prompt',
|
|
2334
|
+
};
|
|
2335
|
+
return flags[key];
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
function localizedResourceParam(value) {
|
|
2339
|
+
const labels = {
|
|
2340
|
+
iref: '参考图',
|
|
2341
|
+
cref: '角色参考',
|
|
2342
|
+
sref: '风格参考',
|
|
2343
|
+
resources: '统一素材',
|
|
2344
|
+
frames: '首帧/尾帧',
|
|
2345
|
+
multi_param: '多帧参数',
|
|
2346
|
+
multi_prompt: '多段提示词',
|
|
2347
|
+
};
|
|
2348
|
+
return labels[value] || value;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
function localizedMedia(value) {
|
|
2352
|
+
const labels = {
|
|
2353
|
+
IMAGE: '图片',
|
|
2354
|
+
VIDEO: '视频',
|
|
2355
|
+
AUDIO: '音频',
|
|
2356
|
+
SUBJECT: '主体',
|
|
2357
|
+
};
|
|
2358
|
+
return labels[String(value || '').toUpperCase()] || prettyValue(value);
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
function localizedUsage(value) {
|
|
2362
|
+
const labels = {
|
|
2363
|
+
reference: '参考素材',
|
|
2364
|
+
first_frame: '首帧',
|
|
2365
|
+
last_frame: '尾帧',
|
|
2366
|
+
keyframe: '关键帧',
|
|
2367
|
+
prompt_only: '纯文本',
|
|
2368
|
+
storyboard: '故事板',
|
|
2369
|
+
};
|
|
2370
|
+
if (Array.isArray(value)) return value.map((item) => localizedUsage(item)).join('、');
|
|
2371
|
+
return labels[value] || prettyValue(value);
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
function localizedResourcePurpose(item = {}, taskKind = '') {
|
|
2375
|
+
const modeText = localizedInputMode(item.mode, taskKind);
|
|
2376
|
+
const usageText = localizedUsage(item.usage);
|
|
2377
|
+
if (!usageText || usageText === '参考素材') return modeText;
|
|
2378
|
+
if (!modeText) return usageText;
|
|
2379
|
+
return `${modeText}(${usageText})`;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
function localizedResourceTarget(value, limits = {}) {
|
|
2383
|
+
const text = String(value || '');
|
|
2384
|
+
const match = text.match(/^resource\.([^.]+)\.([^.]+)(?:\.count)?$/);
|
|
2385
|
+
if (!match) return '';
|
|
2386
|
+
const [, media, usage] = match;
|
|
2387
|
+
const mediaType = String(limits.mediaType || media).toUpperCase();
|
|
2388
|
+
const usageText = localizedUsage(limits.usage || usage);
|
|
2389
|
+
const mediaText = localizedMedia(mediaType);
|
|
2390
|
+
if (usage === 'first_frame' || usage === 'last_frame') return `${usageText}${mediaText}`;
|
|
2391
|
+
if (usage === 'frame') return `首帧/尾帧${mediaText}`;
|
|
2392
|
+
if (usage === 'reference') return `参考${mediaText}`;
|
|
2393
|
+
return `${usageText}${mediaText}`;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
function localizedValueShape(value) {
|
|
2397
|
+
const labels = {
|
|
2398
|
+
local_file: '本地文件',
|
|
2399
|
+
file: '本地文件',
|
|
2400
|
+
http_url: 'URL',
|
|
2401
|
+
url: 'URL',
|
|
2402
|
+
backendPath: '平台素材路径',
|
|
2403
|
+
platformPath: '平台素材路径',
|
|
2404
|
+
asset_id: '资产 ID',
|
|
2405
|
+
asset: '资产 ID',
|
|
2406
|
+
};
|
|
2407
|
+
return labels[value] || value;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
function localizedInputShapes(values = []) {
|
|
2411
|
+
const items = Array.isArray(values) ? values : [];
|
|
2412
|
+
if (!items.length) return '';
|
|
2413
|
+
return items.map(localizedValueShape).join('、');
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
function localizedFormats(item = {}) {
|
|
2417
|
+
const formats = textResourceFormats(item);
|
|
2418
|
+
return formats ? formats.replaceAll('|', '、') : '';
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
function localizedFileCount(item = {}) {
|
|
2422
|
+
const value = textFileCount(item);
|
|
2423
|
+
if (!value) return '';
|
|
2424
|
+
if (value.startsWith('<=')) return `最多 ${value.slice(2)} 个`;
|
|
2425
|
+
if (value.startsWith('>=')) return `至少 ${value.slice(2)} 个`;
|
|
2426
|
+
if (value.includes('..')) return `${value.replace('..', '-')} 个`;
|
|
2427
|
+
return `${value} 个`;
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
function localizedItemCount(item = {}) {
|
|
2431
|
+
const value = textItemCount(item);
|
|
2432
|
+
if (!value) return '';
|
|
2433
|
+
if (value.startsWith('<=')) return `最多 ${value.slice(2)} 项`;
|
|
2434
|
+
if (value.startsWith('>=')) return `至少 ${value.slice(2)} 项`;
|
|
2435
|
+
if (value.includes('..')) return `${value.replace('..', '-')} 项`;
|
|
2436
|
+
return `${value} 项`;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
function formatKb(value) {
|
|
2440
|
+
const kb = Number(value);
|
|
2441
|
+
if (!Number.isFinite(kb)) return '';
|
|
2442
|
+
if (kb >= 1024) {
|
|
2443
|
+
const mb = kb / 1024;
|
|
2444
|
+
return `${Number.isInteger(mb) ? mb : mb.toFixed(1)} MB`;
|
|
2445
|
+
}
|
|
2446
|
+
return `${kb} KB`;
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
function formatMs(value) {
|
|
2450
|
+
const ms = Number(value);
|
|
2451
|
+
if (!Number.isFinite(ms)) return '';
|
|
2452
|
+
const seconds = ms / 1000;
|
|
2453
|
+
if (seconds >= 1) return `${Number.isInteger(seconds) ? seconds : seconds.toFixed(1)} 秒`;
|
|
2454
|
+
return `${ms} 毫秒`;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
function localizedDurationRange(item = {}) {
|
|
2458
|
+
if (item.minDurationMs != null && item.maxDurationMs != null) return `${formatMs(item.minDurationMs)}-${formatMs(item.maxDurationMs)}`;
|
|
2459
|
+
if (item.maxDurationMs != null) return `最多 ${formatMs(item.maxDurationMs)}`;
|
|
2460
|
+
if (item.minDurationMs != null) return `至少 ${formatMs(item.minDurationMs)}`;
|
|
2461
|
+
return '';
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
function localizedTotalDuration(item = {}) {
|
|
2465
|
+
if (item.maxTotalDurationMs == null) return '';
|
|
2466
|
+
return `最多 ${formatMs(item.maxTotalDurationMs)}`;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
function localizedWebpPolicy(item = {}) {
|
|
2470
|
+
const value = textWebpInput(item);
|
|
2471
|
+
if (value === 'accepted') return '支持 WebP';
|
|
2472
|
+
if (String(value || '').startsWith('autoConvertTo')) return `WebP 自动转 ${String(value).replace('autoConvertTo', '')}`;
|
|
2473
|
+
if (value === 'unsupported') return '不支持 WebP';
|
|
2474
|
+
return '';
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
function localizedConditionKey(value) {
|
|
2478
|
+
const resourceTarget = localizedResourceTarget(value);
|
|
2479
|
+
if (resourceTarget && String(value || '').endsWith('.count')) return `${resourceTarget}数量`;
|
|
2480
|
+
if (resourceTarget) return resourceTarget;
|
|
2481
|
+
const labels = {
|
|
2482
|
+
'resource.image.frame': '使用首帧/尾帧图片',
|
|
2483
|
+
generatedMode: '生成方式',
|
|
2484
|
+
ratio: '宽高比',
|
|
2485
|
+
quality: '清晰度',
|
|
2486
|
+
duration: '时长',
|
|
2487
|
+
generated_time: '时长',
|
|
2488
|
+
};
|
|
2489
|
+
return labels[value] || localizedParamKey(value);
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
function localizedConditionValue(value) {
|
|
2493
|
+
if (value === '__NOT_EMPTY__') return '已提供';
|
|
2494
|
+
if (value === '__EMPTY__') return '未提供';
|
|
2495
|
+
const labels = {
|
|
2496
|
+
multi_param: '参考素材生成',
|
|
2497
|
+
frames: '首帧/尾帧生成',
|
|
2498
|
+
multi_prompt: '分镜多镜头',
|
|
2499
|
+
prompt_only: '纯文本生成',
|
|
2500
|
+
};
|
|
2501
|
+
if (labels[value]) return labels[value];
|
|
2502
|
+
return prettyValue(value);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
function localizedCountRange(item = {}) {
|
|
2506
|
+
const value = textCountRange(item);
|
|
2507
|
+
if (!value) return '';
|
|
2508
|
+
if (String(value).startsWith('>=')) return `至少 ${String(value).slice(2)} 个`;
|
|
2509
|
+
if (String(value).startsWith('<=')) return `最多 ${String(value).slice(2)} 个`;
|
|
2510
|
+
if (String(value).includes('..')) return `${String(value).replace('..', '-')} 个`;
|
|
2511
|
+
return `${value} 个`;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
function localizedConditions(conditions = []) {
|
|
2515
|
+
if (!Array.isArray(conditions) || !conditions.length) return '';
|
|
2516
|
+
return conditions
|
|
2517
|
+
.map((item) => {
|
|
2518
|
+
const key = item.key || item.configCode;
|
|
2519
|
+
if (!key) return null;
|
|
2520
|
+
if (item.meaning === 'count') return `${localizedConditionKey(key)}:${localizedCountRange(item)}`;
|
|
2521
|
+
if (key === 'resource.image.frame' && item.meaning === 'present') return '首尾帧模式';
|
|
2522
|
+
if (key === 'generatedMode' && item.value === 'multi_param') return '参考素材生成模式';
|
|
2523
|
+
if (key === 'generatedMode' && item.value === 'frames') return '首帧/尾帧模式';
|
|
2524
|
+
if (key === 'generatedMode' && item.value === 'multi_prompt') return '分镜多镜头模式';
|
|
2525
|
+
return `${localizedConditionKey(key)}${localizedConditionValue(item.value) ? `:${localizedConditionValue(item.value)}` : ''}`;
|
|
2526
|
+
})
|
|
2527
|
+
.filter(Boolean)
|
|
2528
|
+
.join(';');
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
function constraintHasCondition(item = {}, key, value = undefined) {
|
|
2532
|
+
const conditions = Array.isArray(item.conditions) ? item.conditions : [];
|
|
2533
|
+
return conditions.some((condition) => (
|
|
2534
|
+
(condition.key || condition.configCode) === key
|
|
2535
|
+
&& (value === undefined || condition.value === value)
|
|
2536
|
+
));
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
function localizedConstraintResult(item = {}) {
|
|
2540
|
+
const target = item.target || item.targetConfigCode;
|
|
2541
|
+
const flag = cliFlagForParam(target);
|
|
2542
|
+
if (item.effect === 'resource_limit_overrides') {
|
|
2543
|
+
const limits = item.limits || {};
|
|
2544
|
+
const subject = localizedResourceTarget(target, limits) || '素材';
|
|
2545
|
+
const parts = [];
|
|
2546
|
+
const count = localizedFileCount(limits);
|
|
2547
|
+
const duration = localizedDurationRange(limits);
|
|
2548
|
+
const totalDuration = localizedTotalDuration(limits);
|
|
2549
|
+
const itemCount = localizedItemCount(limits);
|
|
2550
|
+
if (count) parts.push(`${subject}${count}`);
|
|
2551
|
+
if (duration) parts.push(`${subject}单个素材${duration}`);
|
|
2552
|
+
if (totalDuration) parts.push(`${subject}总时长${totalDuration}`);
|
|
2553
|
+
if (limits.maxSizeKB != null) parts.push(`${subject}单文件最多 ${formatKb(limits.maxSizeKB)}`);
|
|
2554
|
+
if (itemCount) parts.push(`${subject}${itemCount}`);
|
|
2555
|
+
return parts.join(';') || '素材限制随条件变化';
|
|
2556
|
+
}
|
|
2557
|
+
if (item.effect === 'no_selectable_values') {
|
|
2558
|
+
if (target === 'ratio' && constraintHasCondition(item, 'resource.image.frame')) {
|
|
2559
|
+
return `首尾帧模式不能手选比例;比例由输入图片决定,勿传 ${flag || localizedParamKey(target)}。`;
|
|
2560
|
+
}
|
|
2561
|
+
if (target === 'ratio') return `${flag || localizedParamKey(target)} 由素材决定,创建命令里不要再传。`;
|
|
2562
|
+
return `${flag || localizedParamKey(target)} 触发后没有可选值,创建命令里不要再传。`;
|
|
2563
|
+
}
|
|
2564
|
+
if (Array.isArray(item.allowValues) && item.allowValues.length) {
|
|
2565
|
+
if (target === 'ratio' && constraintHasCondition(item, 'generatedMode', 'multi_param')) {
|
|
2566
|
+
return `比例只能选:${item.allowValues.join('、')}`;
|
|
2567
|
+
}
|
|
2568
|
+
return `只能选择:${item.allowValues.join('、')}`;
|
|
2569
|
+
}
|
|
2570
|
+
return item.effect;
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
function localizedList(values = [], mapper = (value) => value) {
|
|
2574
|
+
if (!Array.isArray(values) || !values.length) return '';
|
|
2575
|
+
return values.map(mapper).filter(Boolean).join('、');
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
function pushReadableRecord(lines, record = {}, fields = [], options = {}) {
|
|
2579
|
+
const rows = fields
|
|
2580
|
+
.map(([label, value]) => [label, value])
|
|
2581
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '');
|
|
2582
|
+
if (!rows.length) return;
|
|
2583
|
+
const labelWidth = Math.max(...rows.map(([label]) => displayWidth(label)));
|
|
2584
|
+
const indent = options.indent || ' ';
|
|
2585
|
+
for (const [label, value] of rows) {
|
|
2586
|
+
lines.push(`${indent}${padDisplay(label, labelWidth)} ${prettyValue(value)}`);
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
function formatPrettyModelList(commandName, data = {}, context = {}) {
|
|
2591
|
+
const normalized = normalizeOutputData(commandName, data);
|
|
2592
|
+
const rows = Array.isArray(normalized.models) ? normalized.models : [];
|
|
2593
|
+
const taskKind = commandName === 'model video-models' ? 'video' : 'image';
|
|
2594
|
+
const limit = listLimitFor(commandName, context, rows);
|
|
2595
|
+
const visibleRows = rows.slice(0, Number.isFinite(limit) ? limit : rows.length);
|
|
2596
|
+
const lines = [prettyTitle(commandName, 'model_list', normalized), ''];
|
|
2597
|
+
sectionTitle(lines, `模型(${rows.length})`);
|
|
2598
|
+
if (!visibleRows.length) {
|
|
2599
|
+
lines.push(` ${mutedText('无匹配模型')}`);
|
|
2600
|
+
} else {
|
|
2601
|
+
const tableRows = visibleRows.map((row, index) => ({
|
|
2602
|
+
index: index + 1,
|
|
2603
|
+
displayName: row.displayName,
|
|
2604
|
+
note: row.modelDesc && row.modelDesc !== row.displayName ? row.modelDesc : row.provider,
|
|
2605
|
+
modelGroupCode: row.modelGroupCode,
|
|
2606
|
+
inputModesText: localizedList(row.inputModes, (item) => localizedInputMode(item, taskKind)),
|
|
2607
|
+
paramsText: localizedList(row.params, localizedParamKey),
|
|
2608
|
+
resourceText: localizedList(row.resourceParams, localizedResourceParam),
|
|
2609
|
+
queue: row.taskQueueNum ?? '',
|
|
2610
|
+
successRateText: formatPercent(row.successRate),
|
|
2611
|
+
}));
|
|
2612
|
+
lines.push(...renderPrettyTable(tableRows, [
|
|
2613
|
+
column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
|
|
2614
|
+
column('模型', 'displayName', { minWidth: 10, maxWidth: 18, always: true }),
|
|
2615
|
+
column('来源/备注', 'note', { minWidth: 10, maxWidth: 20 }),
|
|
2616
|
+
column('模型组编码', 'modelGroupCode', { minWidth: 24, maxWidth: 36, always: true }),
|
|
2617
|
+
column('支持生成', 'inputModesText', { minWidth: 12, maxWidth: 22 }),
|
|
2618
|
+
column('可调参数', 'paramsText', { minWidth: 12, maxWidth: 22 }),
|
|
2619
|
+
column('素材能力', 'resourceText', { minWidth: 8, maxWidth: 16 }),
|
|
2620
|
+
column('排队数', 'queue', { minWidth: 4, maxWidth: 6 }),
|
|
2621
|
+
column('成功率', 'successRateText', { minWidth: 4, maxWidth: 8 }),
|
|
2622
|
+
]));
|
|
2623
|
+
}
|
|
2624
|
+
if (rows.length > visibleRows.length) {
|
|
2625
|
+
lines.push(` ${mutedText(`还有 ${rows.length - visibleRows.length} 个模型未展示;运行 ${context.moreCommand || `${commandPrefix()} ${commandName} --all`} 查看全部。`)}`);
|
|
2626
|
+
}
|
|
2627
|
+
renderNextActions(lines, collectPrettyActions(commandName, normalized, context), '先查看候选模型参数和素材约束,再推荐或进入 fee/dry-run。');
|
|
2628
|
+
return `${lines.join('\n')}\n`;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
function renderModelParams(lines, params = []) {
|
|
2632
|
+
const rows = Array.isArray(params) ? params : [];
|
|
2633
|
+
sectionTitle(lines, `参数(${rows.length})`);
|
|
2634
|
+
if (!rows.length) {
|
|
2635
|
+
lines.push(` ${mutedText('无可调参数')}`);
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
const tableRows = rows.map((param) => ({
|
|
2639
|
+
name: param.label || param.name || localizedParamKey(param.key),
|
|
2640
|
+
flag: cliFlagForParam(param.key),
|
|
2641
|
+
type: localizedParamType(param.valueType || param.type),
|
|
2642
|
+
values: Array.isArray(param.values) ? param.values.join('、') : '',
|
|
2643
|
+
defaultValue: param.defaultValue,
|
|
2644
|
+
required: param.required === undefined ? '' : prettyBoolean(param.required),
|
|
2645
|
+
limit: param.maxLength ? `最长 ${param.maxLength} 字符` : '',
|
|
2646
|
+
}));
|
|
2647
|
+
lines.push(...renderPrettyTable(tableRows, [
|
|
2648
|
+
column('参数', 'name', { minWidth: 8, maxWidth: 18, always: true }),
|
|
2649
|
+
column('CLI', 'flag', { minWidth: 8, maxWidth: 16 }),
|
|
2650
|
+
column('类型', 'type', { maxWidth: 10 }),
|
|
2651
|
+
column('可选值', 'values', { minWidth: 12, maxWidth: 36 }),
|
|
2652
|
+
column('默认', 'defaultValue', { maxWidth: 12 }),
|
|
2653
|
+
column('必填', 'required', { maxWidth: 6 }),
|
|
2654
|
+
column('限制', 'limit', { maxWidth: 16 }),
|
|
2655
|
+
]));
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
function renderModelResources(lines, resources = [], taskKind = '') {
|
|
2659
|
+
const rows = Array.isArray(resources) ? resources : [];
|
|
2660
|
+
sectionTitle(lines, `素材约束(${rows.length})`);
|
|
2661
|
+
if (!rows.length) {
|
|
2662
|
+
lines.push(` ${mutedText('不需要素材')}`);
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
const tableRows = rows.map((item, index) => ({
|
|
2666
|
+
index: index + 1,
|
|
2667
|
+
purpose: localizedResourcePurpose(item, taskKind),
|
|
2668
|
+
media: localizedMedia(item.mediaType),
|
|
2669
|
+
input: localizedInputShapes(item.valueShapes || item.sources),
|
|
2670
|
+
formats: localizedFormats(item),
|
|
2671
|
+
count: localizedFileCount(item),
|
|
2672
|
+
limits: [
|
|
2673
|
+
item.maxSizeKB != null ? `单文件最多 ${formatKb(item.maxSizeKB)}` : '',
|
|
2674
|
+
localizedDurationRange(item) ? `单个素材 ${localizedDurationRange(item)}` : '',
|
|
2675
|
+
localizedTotalDuration(item) ? `总时长 ${localizedTotalDuration(item)}` : '',
|
|
2676
|
+
localizedItemCount(item) ? `条目 ${localizedItemCount(item)}` : '',
|
|
2677
|
+
localizedWebpPolicy(item),
|
|
2678
|
+
item.supportLastFrameOnly === undefined ? '' : `仅尾帧:${prettyBoolean(item.supportLastFrameOnly)}`,
|
|
2679
|
+
].filter(Boolean).join(';'),
|
|
2680
|
+
}));
|
|
2681
|
+
lines.push(...renderPrettyTable(tableRows, [
|
|
2682
|
+
column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
|
|
2683
|
+
column('用途', 'purpose', { minWidth: 14, maxWidth: 24, always: true }),
|
|
2684
|
+
column('媒体', 'media', { maxWidth: 6, always: true }),
|
|
2685
|
+
column('输入方式', 'input', { minWidth: 16, maxWidth: 40 }),
|
|
2686
|
+
column('文件格式', 'formats', { minWidth: 10, maxWidth: 28 }),
|
|
2687
|
+
column('数量', 'count', { maxWidth: 10 }),
|
|
2688
|
+
column('其他限制', 'limits', { minWidth: 12, maxWidth: 30 }),
|
|
2689
|
+
]));
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
function renderModelConstraints(lines, constraints = []) {
|
|
2693
|
+
const rows = Array.isArray(constraints) ? constraints : [];
|
|
2694
|
+
sectionTitle(lines, `联动约束(${rows.length})`);
|
|
2695
|
+
if (!rows.length) {
|
|
2696
|
+
lines.push(` ${mutedText('无联动约束')}`);
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
const tableRows = rows.map((item, index) => ({
|
|
2700
|
+
index: index + 1,
|
|
2701
|
+
target: localizedResourceTarget(item.target, item.limits) || localizedParamKey(item.target || item.targetConfigCode || '约束'),
|
|
2702
|
+
condition: localizedConditions(item.conditions),
|
|
2703
|
+
result: localizedConstraintResult(item),
|
|
2704
|
+
note: item.name,
|
|
2705
|
+
}));
|
|
2706
|
+
lines.push(...renderPrettyTable(tableRows, [
|
|
2707
|
+
column('#', 'index', { minWidth: 1, maxWidth: 2, always: true }),
|
|
2708
|
+
column('影响参数', 'target', { minWidth: 8, maxWidth: 14, always: true }),
|
|
2709
|
+
column('条件', 'condition', { minWidth: 16, maxWidth: 28 }),
|
|
2710
|
+
column('结果', 'result', { minWidth: 18, maxWidth: 42, always: true }),
|
|
2711
|
+
column('说明', 'note', { maxWidth: 24 }),
|
|
2712
|
+
]));
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
function listLimitFor(commandName, context, rows) {
|
|
2716
|
+
if (context.listLimit === Number.POSITIVE_INFINITY) return rows.length;
|
|
2717
|
+
if (Number.isFinite(context.listLimit)) return Math.max(0, context.listLimit);
|
|
2718
|
+
if (commandName === 'schema') return Number.POSITIVE_INFINITY;
|
|
2719
|
+
return Number.POSITIVE_INFINITY;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
function collectListKeys(normalized, primaryKey) {
|
|
2723
|
+
const keys = [];
|
|
2724
|
+
if (primaryKey && Array.isArray(normalized?.[primaryKey])) keys.push(primaryKey);
|
|
2725
|
+
for (const [key, value] of Object.entries(normalized || {})) {
|
|
2726
|
+
if (keys.includes(key)) continue;
|
|
2727
|
+
if (['resultUrls', 'originUrls'].includes(key)) continue;
|
|
2728
|
+
if (Array.isArray(value)) keys.push(key);
|
|
2729
|
+
}
|
|
2730
|
+
return keys;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
function collectPrettyActions(commandName, normalized, context = {}) {
|
|
2734
|
+
const actions = [];
|
|
2735
|
+
const add = (label, value) => {
|
|
2736
|
+
if (value === undefined || value === null || value === '') return;
|
|
2737
|
+
actions.push({ label, value: rewriteCommandPrefix(value) });
|
|
2738
|
+
};
|
|
2739
|
+
if (normalized?.nextCommand) add('继续查询', normalized.nextCommand);
|
|
2740
|
+
if (normalized?.nextRefSubject) add('主体引用', normalized.nextRefSubject);
|
|
2741
|
+
if (normalized?.nextVoiceArg) add('音色参数', normalized.nextVoiceArg);
|
|
2742
|
+
if (normalized?.dryRun && context.confirmCommand) add('确认执行', context.confirmCommand);
|
|
2743
|
+
if (normalized?.dryRun && !context.confirmCommand && context.executeCommand) add('正式执行', context.executeCommand);
|
|
2744
|
+
const modelNext = modelListNextCommand(commandName);
|
|
2745
|
+
if (modelNext) add(commandName === 'model asset-review-models' ? '查询素材组' : '查看参数', modelNext);
|
|
2746
|
+
const waitNext = statusNextCommand(commandName, normalized);
|
|
2747
|
+
if (waitNext) add('继续等待', waitNext);
|
|
2748
|
+
const subtitleNext = subtitleRemoveNextCommand(commandName, normalized);
|
|
2749
|
+
if (subtitleNext) add('去字幕预览', subtitleNext);
|
|
2750
|
+
if (context.moreCommand) add('显示更多', context.moreCommand);
|
|
2751
|
+
return actions;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
function renderNextActions(lines, actions = [], hint = null) {
|
|
2755
|
+
if (!actions.length && !hint) return;
|
|
2756
|
+
sectionTitle(lines, '下一步');
|
|
2757
|
+
const labelWidth = actions.length ? Math.max(...actions.map((item) => displayWidth(item.label))) : 0;
|
|
2758
|
+
for (const item of actions) {
|
|
2759
|
+
lines.push(` ${labelText(padDisplay(item.label, labelWidth))} ${commandText(prettyValue(item.value))}`);
|
|
2760
|
+
}
|
|
2761
|
+
if (hint) lines.push(` ${labelText(padDisplay('提示', labelWidth || 2))} ${mutedText(hint)}`);
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
function renderUrlLists(lines, normalized) {
|
|
2765
|
+
renderScalarList(lines, '结果链接', normalized?.resultUrls, 6);
|
|
2766
|
+
renderScalarList(lines, '原始素材', normalized?.originUrls, 6);
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
function formatPrettySchema(data = {}, context = {}) {
|
|
2770
|
+
const lines = [prettyTitle('schema', 'command_schema', data), ''];
|
|
2771
|
+
const isBrief = data.kind === 'agent_brief';
|
|
2772
|
+
renderDetails(lines, '摘要', {
|
|
2773
|
+
schemaVersion: data.schemaVersion,
|
|
2774
|
+
kind: data.kind,
|
|
2775
|
+
cli: data.cli?.name,
|
|
2776
|
+
version: data.cli?.version,
|
|
2777
|
+
commandPrefix: data.cli?.commandPrefix,
|
|
2778
|
+
domainFilter: data.filters?.domain,
|
|
2779
|
+
commandFilter: data.filters?.command,
|
|
2780
|
+
commandCount: data.commandCount ?? (Array.isArray(data.commands) ? data.commands.length : undefined),
|
|
2781
|
+
}, ['schemaVersion', 'kind', 'cli', 'version', 'commandPrefix', 'domainFilter', 'commandFilter', 'commandCount']);
|
|
2782
|
+
renderArraySection(lines, '命令领域', data.domains, 'domains');
|
|
2783
|
+
if (!isBrief) renderArraySection(lines, '命令', data.commands, 'commands');
|
|
2784
|
+
renderNextActions(lines, [
|
|
2785
|
+
...(isBrief ? [
|
|
2786
|
+
{ label: '精确 Schema', value: `${commandPrefix()} schema --domain <domain> --command <command> -f json` },
|
|
2787
|
+
{ label: '完整 Schema', value: `${commandPrefix()} schema -f json` },
|
|
2788
|
+
] : [
|
|
2789
|
+
{ label: 'Agent Brief', value: `${commandPrefix()} schema --brief -f json` },
|
|
2790
|
+
{ label: 'Schema JSON', value: `${commandPrefix()} schema -f json` },
|
|
2791
|
+
]),
|
|
2792
|
+
...(context.jsonCommand ? [{ label: '当前筛选 JSON', value: context.jsonCommand }] : []),
|
|
2793
|
+
], isBrief ? '先读 brief 建立能力地图;执行具体命令前按需读取精确 schema。' : undefined);
|
|
2794
|
+
return `${lines.join('\n')}\n`;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
function formatPrettyEnvironment(data = {}, context = {}) {
|
|
2798
|
+
const lines = [prettyTitle('doctor', 'environment_report', data), ''];
|
|
2799
|
+
renderDetails(lines, '摘要', {
|
|
2800
|
+
doctorStatus: data.doctorStatus,
|
|
2801
|
+
verify: data.verify,
|
|
2802
|
+
nodeVersion: data.runtime?.nodeVersion,
|
|
2803
|
+
platform: data.runtime?.platform,
|
|
2804
|
+
arch: data.runtime?.arch,
|
|
2805
|
+
apiOrigin: data.origins?.apiOrigin,
|
|
2806
|
+
authState: data.auth?.loginState,
|
|
2807
|
+
authType: data.auth?.authType,
|
|
2808
|
+
accessKey: data.auth?.accessKey,
|
|
2809
|
+
currentProjectGroupNo: data.projectGroup?.projectGroupNo,
|
|
2810
|
+
currentProjectGroupName: data.projectGroup?.projectGroupName,
|
|
2811
|
+
}, ['doctorStatus', 'verify', 'nodeVersion', 'platform', 'arch', 'apiOrigin', 'authState', 'authType', 'accessKey', 'currentProjectGroupNo', 'currentProjectGroupName']);
|
|
2812
|
+
renderArraySection(lines, '检查结果', data.checks, 'checks');
|
|
2813
|
+
renderNextActions(lines, collectPrettyActions('doctor', data, context));
|
|
2814
|
+
return `${lines.join('\n')}\n`;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
function formatPrettyModelOptions(data = {}, context = {}) {
|
|
2818
|
+
const lines = [prettyTitle('model options', 'model_options', data), ''];
|
|
2819
|
+
renderDetails(lines, '摘要', {
|
|
2820
|
+
modelGroupCode: data.modelGroupCode,
|
|
2821
|
+
taskKind: localizedTaskKind(data.taskKind),
|
|
2822
|
+
displayName: data.model?.displayName,
|
|
2823
|
+
modelDesc: data.model?.modelDesc,
|
|
2824
|
+
}, ['modelGroupCode', 'displayName', 'taskKind', 'modelDesc']);
|
|
2825
|
+
renderModelParams(lines, data.params);
|
|
2826
|
+
renderModelResources(lines, data.resources, data.taskKind);
|
|
2827
|
+
renderModelConstraints(lines, data.constraints);
|
|
2828
|
+
renderNextActions(lines, [
|
|
2829
|
+
{ label: '生成创建写法', value: `${commandPrefix()} model create-spec --model-group-code ${data.modelGroupCode || '<modelGroupCode>'}` },
|
|
2830
|
+
...(context.textCommand ? [{ label: 'Agent 读取', value: context.textCommand }] : []),
|
|
2831
|
+
...(context.jsonCommand ? [{ label: '完整 JSON', value: context.jsonCommand }] : []),
|
|
2832
|
+
]);
|
|
2833
|
+
return `${lines.join('\n')}\n`;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
function formatPrettyModelCreateSpec(data = {}, context = {}) {
|
|
2837
|
+
const taskKind = data.taskKind;
|
|
2838
|
+
const lines = [prettyTitle('model create-spec', 'model_create_spec', data), ''];
|
|
2839
|
+
renderDetails(lines, '摘要', {
|
|
2840
|
+
modelGroupCode: data.modelGroupCode,
|
|
2841
|
+
taskKind: localizedTaskKind(data.taskKind),
|
|
2842
|
+
createCommand: data.createCommand,
|
|
2843
|
+
feeCommand: data.feeCommand,
|
|
2844
|
+
statusCommandTaskType: data.statusCommandTaskType,
|
|
2845
|
+
inputSummary: localizedCreateSpecSummary(data.inputRequirement?.summary),
|
|
2846
|
+
acceptedModes: localizedModeList(data.inputRequirement?.acceptedModes, taskKind),
|
|
2847
|
+
}, ['modelGroupCode', 'taskKind', 'createCommand', 'feeCommand', 'statusCommandTaskType', 'inputSummary', 'acceptedModes']);
|
|
2848
|
+
renderArraySection(lines, '支持意图', data.supportedIntents, [
|
|
2849
|
+
column('输入方式', (row) => localizedIntentMode(row.mode, taskKind), { minWidth: 12, maxWidth: 22 }),
|
|
2850
|
+
column('意图', (row) => localizedIntentText(row.intent, row.mode, taskKind), { minWidth: 10, maxWidth: 24 }),
|
|
2851
|
+
column('必需写法', (row) => commandArgLines(row.requiredArgs), { minWidth: 22, maxWidth: 62 }),
|
|
2852
|
+
column('素材用途', (row) => localizedList(row.resourceUsages, localizedUsage), { minWidth: 10, maxWidth: 24 }),
|
|
2853
|
+
column('Prompt 绑定', (row) => localizedPromptBinding(row.promptBinding), { maxWidth: 14 }),
|
|
2854
|
+
]);
|
|
2855
|
+
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);
|
|
2856
|
+
const actions = [];
|
|
2857
|
+
if (data.optionsCommand) actions.push({ label: '查看参数', value: `${commandPrefix()} ${data.optionsCommand}` });
|
|
2858
|
+
const feeNext = modelFeeNextCommand(data);
|
|
2859
|
+
if (feeNext) actions.push({ label: '费用预估', value: feeNext });
|
|
2860
|
+
if (data.examples?.[0]) actions.push({ label: 'Dry-run', value: modelCreateExampleWithCode(data.examples[0], data.modelGroupCode) });
|
|
2861
|
+
if (context.textCommand) actions.push({ label: 'Agent 读取', value: context.textCommand });
|
|
2862
|
+
if (context.jsonCommand) actions.push({ label: '完整 JSON', value: context.jsonCommand });
|
|
2863
|
+
renderNextActions(lines, actions);
|
|
2864
|
+
return `${lines.join('\n')}\n`;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
function formatPrettyModelInputGuide(data = {}, context = {}) {
|
|
2868
|
+
const lines = [prettyTitle('model input-guide', 'model_input_guide', data), ''];
|
|
2869
|
+
renderDetails(lines, '摘要', {
|
|
2870
|
+
schemaVersion: data.schemaVersion,
|
|
2871
|
+
commonFieldCount: Array.isArray(data.commonFields) ? data.commonFields.length : undefined,
|
|
2872
|
+
resourceFieldCount: Array.isArray(data.resourceFields) ? data.resourceFields.length : undefined,
|
|
2873
|
+
resourceRuleCount: Array.isArray(data.resourceRules) ? data.resourceRules.length : undefined,
|
|
2874
|
+
}, ['schemaVersion', 'commonFieldCount', 'resourceFieldCount', 'resourceRuleCount']);
|
|
2875
|
+
const guideColumns = [
|
|
2876
|
+
column('字段', 'field', { maxWidth: 32 }),
|
|
2877
|
+
column('可选值', localizedGuideValues, { maxWidth: 46 }),
|
|
2878
|
+
column('说明', 'description', { maxWidth: 70 }),
|
|
2879
|
+
];
|
|
2880
|
+
renderArraySection(lines, '通用字段', data.commonFields, guideColumns);
|
|
2881
|
+
renderArraySection(lines, '资源字段', data.resourceFields, guideColumns);
|
|
2882
|
+
renderScalarList(lines, '资源规则', data.resourceRules, 12);
|
|
2883
|
+
renderScalarList(lines, 'Reference Key 指南', data.referenceKeyGuide, 12);
|
|
2884
|
+
renderNextActions(lines, [
|
|
2885
|
+
{ label: '生图模型', value: `${commandPrefix()} model image-models --model <keyword>` },
|
|
2886
|
+
{ label: '生视频模型', value: `${commandPrefix()} model video-models --model <keyword>` },
|
|
2887
|
+
{ label: '模型参数', value: `${commandPrefix()} model options --model-group-code <modelGroupCode>` },
|
|
2888
|
+
...(context.textCommand ? [{ label: 'Agent 读取', value: context.textCommand }] : []),
|
|
2889
|
+
...(context.jsonCommand ? [{ label: '完整 JSON', value: context.jsonCommand }] : []),
|
|
2890
|
+
]);
|
|
2891
|
+
return `${lines.join('\n')}\n`;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
function formatPrettyGeneric(commandName, data, context = {}) {
|
|
2895
|
+
const normalized = normalizeOutputData(commandName, data);
|
|
2896
|
+
const kind = outputKindForCommand(commandName, context);
|
|
2897
|
+
if (!isPlainObject(normalized)) {
|
|
2898
|
+
const lines = [prettyTitle(commandName, kind, data), ''];
|
|
2899
|
+
renderDetails(lines, '结果', { value: normalized }, ['value']);
|
|
2900
|
+
return `${lines.join('\n')}\n`;
|
|
2901
|
+
}
|
|
2902
|
+
const profile = PRETTY_KIND_PROFILES[kind] || PRETTY_KIND_PROFILES.generic;
|
|
2903
|
+
const lines = [prettyTitle(commandName, kind, normalized), ''];
|
|
2904
|
+
const listKeys = collectListKeys(normalized, profile.listKey);
|
|
2905
|
+
const excluded = new Set(['raw', ...listKeys, 'resultUrls', 'originUrls', 'waited']);
|
|
2906
|
+
const summaryFields = normalized.dryRun
|
|
2907
|
+
? (DRY_RUN_FIELDS_BY_COMMAND[commandName] || ['dryRun', 'action', ...(profile.fields || [])])
|
|
2908
|
+
: (profile.fields || []);
|
|
2909
|
+
renderDetails(lines, normalized.dryRun ? '预览摘要' : '摘要', normalized, summaryFields, [...excluded]);
|
|
2910
|
+
if (normalized.waited) {
|
|
2911
|
+
renderDetails(lines, '等待结果', normalized.waited, DETAIL_FIELDS_BY_KIND.task_status || []);
|
|
2912
|
+
}
|
|
2913
|
+
for (const key of listKeys) {
|
|
2914
|
+
const rows = normalized[key];
|
|
2915
|
+
if (!Array.isArray(rows)) continue;
|
|
2916
|
+
const limit = listLimitFor(commandName, context, rows);
|
|
2917
|
+
renderArraySection(lines, namedListTitle(key), rows, key, limit);
|
|
2918
|
+
}
|
|
2919
|
+
renderUrlLists(lines, normalized);
|
|
2920
|
+
renderNextActions(lines, collectPrettyActions(commandName, normalized, context), modelListAgentHint(commandName));
|
|
2921
|
+
return `${lines.join('\n')}\n`;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
export function formatPrettyOutput(commandName, data, context = {}) {
|
|
2925
|
+
if (commandName === 'schema') return formatPrettySchema(data, context);
|
|
2926
|
+
if (commandName === 'doctor') return formatPrettyEnvironment(data, context);
|
|
2927
|
+
if (commandName === 'model image-models' || commandName === 'model video-models') return formatPrettyModelList(commandName, data, context);
|
|
2928
|
+
if (commandName === 'model options') return formatPrettyModelOptions(data, context);
|
|
2929
|
+
if (commandName === 'model create-spec') return formatPrettyModelCreateSpec(data, context);
|
|
2930
|
+
if (commandName === 'model input-guide') return formatPrettyModelInputGuide(data, context);
|
|
2931
|
+
return formatPrettyGeneric(commandName, data, context);
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
function yamlScalar(value) {
|
|
2935
|
+
if (value === null) return 'null';
|
|
2936
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
2937
|
+
const text = String(value ?? '');
|
|
2938
|
+
return JSON.stringify(text);
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
function formatYamlValue(value, indent = 0) {
|
|
2942
|
+
const pad = ' '.repeat(indent);
|
|
2943
|
+
if (Array.isArray(value)) {
|
|
2944
|
+
if (!value.length) return '[]';
|
|
2945
|
+
return value.map((item) => {
|
|
2946
|
+
if (isPlainObject(item) || Array.isArray(item)) {
|
|
2947
|
+
const rendered = formatYamlValue(item, indent + 2);
|
|
2948
|
+
return `${pad}- ${rendered.includes('\n') ? `\n${rendered}` : rendered}`;
|
|
2949
|
+
}
|
|
2950
|
+
return `${pad}- ${yamlScalar(item)}`;
|
|
2951
|
+
}).join('\n');
|
|
2952
|
+
}
|
|
2953
|
+
if (isPlainObject(value)) {
|
|
2954
|
+
const entries = Object.entries(value).filter(([, item]) => item !== undefined);
|
|
2955
|
+
if (!entries.length) return '{}';
|
|
2956
|
+
return entries.map(([key, item]) => {
|
|
2957
|
+
if (isPlainObject(item) || Array.isArray(item)) {
|
|
2958
|
+
return `${pad}${key}:\n${formatYamlValue(item, indent + 2)}`;
|
|
2959
|
+
}
|
|
2960
|
+
return `${pad}${key}: ${yamlScalar(item)}`;
|
|
2961
|
+
}).join('\n');
|
|
2962
|
+
}
|
|
2963
|
+
return yamlScalar(value);
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
export function formatYamlEnvelope(payload) {
|
|
2967
|
+
return `${formatYamlValue(rewriteNested(payload), 0)}\n`;
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
function csvEscape(value) {
|
|
2971
|
+
const text = String(prettyValue(value));
|
|
2972
|
+
return /[",\n\r]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
function csvColumnsForRows(rows = []) {
|
|
2976
|
+
const cols = inferTableColumns(rows);
|
|
2977
|
+
return cols.map((col) => ({
|
|
2978
|
+
header: col.header,
|
|
2979
|
+
key: col.key,
|
|
2980
|
+
value: col.value,
|
|
2981
|
+
}));
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
export function formatCsvOutput(commandName, data, context = {}) {
|
|
2985
|
+
const normalized = normalizeJsonData(commandName, data);
|
|
2986
|
+
const kind = outputKindForCommand(commandName, context);
|
|
2987
|
+
const primaryKey = LIST_KEY_BY_KIND[kind];
|
|
2988
|
+
const list = primaryKey && Array.isArray(normalized?.[primaryKey])
|
|
2989
|
+
? { key: primaryKey, rows: normalized[primaryKey] }
|
|
2990
|
+
: firstList(normalized);
|
|
2991
|
+
const rows = list?.rows?.length ? list.rows : (isPlainObject(normalized) ? [normalized] : [{ value: normalized }]);
|
|
2992
|
+
const columns = TABLE_COLUMNS[list?.key] || csvColumnsForRows(rows);
|
|
2993
|
+
const headers = columns.map((col) => col.header);
|
|
2994
|
+
const lines = [headers.map(csvEscape).join(',')];
|
|
2995
|
+
for (const row of rows) {
|
|
2996
|
+
lines.push(columns.map((col) => csvEscape(resolveColumnValue(row, col))).join(','));
|
|
2997
|
+
}
|
|
2998
|
+
return `${lines.join('\n')}\n`;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
export function formatPrettyError(error) {
|
|
3002
|
+
const lines = [errorTitleText('命令失败'), ''];
|
|
3003
|
+
renderDetails(lines, '错误', {
|
|
3004
|
+
type: error.type || 'error',
|
|
3005
|
+
message: error.message || String(error),
|
|
3006
|
+
hint: error.hint ? rewriteCommandPrefix(error.hint) : undefined,
|
|
3007
|
+
}, ['type', 'message', 'hint']);
|
|
3008
|
+
if (isPlainObject(error.details)) {
|
|
3009
|
+
const payload = isPlainObject(error.details.payload) ? error.details.payload : {};
|
|
3010
|
+
renderDetails(lines, '详情', compactRecord({
|
|
3011
|
+
status: error.details.status ?? payload.status,
|
|
3012
|
+
code: error.details.code ?? payload.code,
|
|
3013
|
+
msg: error.details.msg ?? payload.msg ?? payload.message,
|
|
3014
|
+
traceId: error.details.traceId ?? payload.traceId,
|
|
3015
|
+
causeCode: error.details.causeCode,
|
|
3016
|
+
unknownOptions: Array.isArray(error.details.unknownOptions) ? error.details.unknownOptions.join(', ') : undefined,
|
|
3017
|
+
}), ['status', 'code', 'msg', 'traceId', 'causeCode', 'unknownOptions']);
|
|
3018
|
+
}
|
|
3019
|
+
return `${lines.join('\n')}\n`;
|
|
3020
|
+
}
|
|
3021
|
+
|
|
1157
3022
|
export function formatTextOutput(commandName, data, context = {}) {
|
|
1158
3023
|
if (commandName === 'model options') return formatModelOptionsOutput(data);
|
|
1159
3024
|
if (commandName === 'model create-spec') return formatModelCreateSpecOutput(data);
|