@lingjingai/lj-awb-cli-pre 0.3.17 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -12
- package/package.json +5 -1
- package/packages/awb-cli/README.md +0 -3
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +6 -2
- package/packages/awb-core/src/auth.js +1 -1
- package/packages/awb-core/src/commands.js +2 -2
- package/packages/awb-core/src/common.js +1 -1
- package/packages/awb-core/src/output.js +2011 -80
- package/packages/awb-core/src/services.js +150 -3
- package/packages/awb-core/src/standalone.js +433 -133
- package/skills/lj-awb/SKILL.md +10 -4
- 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/auth.md +1 -1
- package/skills/lj-awb/modules/create-contract.md +1 -1
- package/skills/lj-awb/modules/driver.md +20 -4
- package/skills/lj-awb/modules/model.md +17 -6
- package/skills/lj-awb/modules/task-manual.md +3 -2
- package/skills/lj-awb/references/error-codes.md +1 -1
- package/skills/lj-awb/references/model-options-read.md +17 -10
- 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,43 @@ 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(' ');
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function recordScalarKeys(record, preferredKeys = [], excludedKeys = []) {
|
|
705
|
+
const excluded = new Set(excludedKeys);
|
|
706
|
+
return [
|
|
707
|
+
...preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined),
|
|
708
|
+
...Object.keys(record).filter((key) => !excluded.has(key) && !preferredKeys.includes(key) && !Array.isArray(record[key]) && !isPlainObject(record[key])),
|
|
709
|
+
];
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function pushSectionRecord(lines, title, record, preferredKeys = []) {
|
|
713
|
+
const compacted = compactRecord(record);
|
|
714
|
+
lines.push(`${title}:`);
|
|
715
|
+
for (const key of recordScalarKeys(compacted, preferredKeys)) {
|
|
716
|
+
lines.push(` ${key}=${shortRecordValue(key, compacted[key])}`);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function pushSectionCount(lines, title, count) {
|
|
721
|
+
lines.push(`${title}:`);
|
|
722
|
+
lines.push(` count=${count}`);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function pushSectionKeyValue(lines, key, value) {
|
|
726
|
+
if (value === undefined || value === null || value === '') return;
|
|
727
|
+
lines.push(` ${key}=${value}`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function pushSectionItem(lines, index, value) {
|
|
731
|
+
if (value === undefined || value === null || value === '') return;
|
|
732
|
+
lines.push(` [${index}]=${value}`);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function pushSectionRecordItem(lines, index, record, preferredKeys = []) {
|
|
736
|
+
const rendered = renderRecord(compactRecord(record), preferredKeys);
|
|
737
|
+
if (rendered) lines.push(` [${index}]: ${rendered}`);
|
|
682
738
|
}
|
|
683
739
|
|
|
684
740
|
function firstList(data) {
|
|
@@ -704,6 +760,7 @@ function rowSummary(row) {
|
|
|
704
760
|
'modelDesc',
|
|
705
761
|
'modelStatus',
|
|
706
762
|
'taskQueueNum',
|
|
763
|
+
'successRate',
|
|
707
764
|
'feeCalcType',
|
|
708
765
|
'inputModes',
|
|
709
766
|
'params',
|
|
@@ -835,7 +892,7 @@ function rowSummary(row) {
|
|
|
835
892
|
if (Array.isArray(value) && value.length === 0) return false;
|
|
836
893
|
return true;
|
|
837
894
|
})
|
|
838
|
-
.map((key) => `${key}=${
|
|
895
|
+
.map((key) => `${key}=${shortRecordValue(key, row[key])}`)
|
|
839
896
|
.join(' ');
|
|
840
897
|
}
|
|
841
898
|
|
|
@@ -872,8 +929,30 @@ function textItemCount(item = {}) {
|
|
|
872
929
|
return undefined;
|
|
873
930
|
}
|
|
874
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
|
+
|
|
875
954
|
function textAllowValues(values = []) {
|
|
876
|
-
return Array.isArray(values) && values.length ? valueList(values) :
|
|
955
|
+
return Array.isArray(values) && values.length ? valueList(values) : undefined;
|
|
877
956
|
}
|
|
878
957
|
|
|
879
958
|
function textConditionValue(value) {
|
|
@@ -887,6 +966,7 @@ function textConstraintConditions(conditions = []) {
|
|
|
887
966
|
return conditions
|
|
888
967
|
.map((item) => {
|
|
889
968
|
const key = item.key || item.configCode;
|
|
969
|
+
if (item.meaning === 'count') return key ? `${key}=${textCountRange(item)}` : null;
|
|
890
970
|
return key ? `${key}=${textConditionValue(item.value)}` : null;
|
|
891
971
|
})
|
|
892
972
|
.filter(Boolean)
|
|
@@ -925,70 +1005,72 @@ function formatModelOptionsOutput(data = {}) {
|
|
|
925
1005
|
const resources = Array.isArray(data.resources) ? data.resources : [];
|
|
926
1006
|
const constraints = Array.isArray(data.constraints) ? data.constraints : [];
|
|
927
1007
|
const lines = ['ok model options'];
|
|
928
|
-
lines
|
|
1008
|
+
pushSectionRecord(lines, 'summary', {
|
|
929
1009
|
modelGroupCode: data.modelGroupCode,
|
|
930
1010
|
taskKind: data.taskKind,
|
|
931
|
-
}
|
|
932
|
-
lines
|
|
1011
|
+
}, ['modelGroupCode', 'taskKind']);
|
|
1012
|
+
pushSectionCount(lines, 'params', params.length);
|
|
933
1013
|
for (const [index, param] of params.entries()) {
|
|
934
|
-
lines
|
|
1014
|
+
pushSectionRecordItem(lines, index, {
|
|
935
1015
|
key: param.key,
|
|
936
1016
|
type: param.valueType,
|
|
937
1017
|
values: valueList(param.values),
|
|
938
1018
|
default: param.defaultValue,
|
|
939
1019
|
maxLength: param.maxLength,
|
|
940
1020
|
required: param.required,
|
|
941
|
-
}
|
|
1021
|
+
}, ['key', 'type', 'values', 'default', 'maxLength', 'required']);
|
|
942
1022
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
])}`);
|
|
977
|
-
}
|
|
1023
|
+
pushSectionCount(lines, 'resources', resources.length);
|
|
1024
|
+
for (const [index, item] of resources.entries()) {
|
|
1025
|
+
pushSectionRecordItem(lines, index, {
|
|
1026
|
+
mode: item.mode,
|
|
1027
|
+
media: item.mediaType,
|
|
1028
|
+
usage: item.usage,
|
|
1029
|
+
valueShapes: textValueShapes(item.valueShapes),
|
|
1030
|
+
formats: textResourceFormats(item),
|
|
1031
|
+
webpInput: textWebpInput(item),
|
|
1032
|
+
autoConvert: textResourceAutoConvert(item),
|
|
1033
|
+
files: textFileCount(item),
|
|
1034
|
+
maxSizeKB: item.maxSizeKB,
|
|
1035
|
+
durationMs: textDurationRange(item),
|
|
1036
|
+
totalDurationMs: item.maxTotalDurationMs != null ? `<=${item.maxTotalDurationMs}` : undefined,
|
|
1037
|
+
items: textItemCount(item),
|
|
1038
|
+
maxPromptLength: item.maxPromptLength,
|
|
1039
|
+
lastFrameOnly: item.supportLastFrameOnly,
|
|
1040
|
+
}, [
|
|
1041
|
+
'mode',
|
|
1042
|
+
'media',
|
|
1043
|
+
'usage',
|
|
1044
|
+
'valueShapes',
|
|
1045
|
+
'formats',
|
|
1046
|
+
'webpInput',
|
|
1047
|
+
'autoConvert',
|
|
1048
|
+
'files',
|
|
1049
|
+
'maxSizeKB',
|
|
1050
|
+
'durationMs',
|
|
1051
|
+
'totalDurationMs',
|
|
1052
|
+
'items',
|
|
1053
|
+
'maxPromptLength',
|
|
1054
|
+
'lastFrameOnly',
|
|
1055
|
+
]);
|
|
978
1056
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1057
|
+
pushSectionCount(lines, 'constraints', constraints.length);
|
|
1058
|
+
for (const [index, item] of constraints.entries()) {
|
|
1059
|
+
pushSectionRecordItem(lines, index, {
|
|
1060
|
+
target: item.target,
|
|
1061
|
+
targetConfig: item.targetConfigCode && item.targetConfigCode !== item.target ? item.targetConfigCode : undefined,
|
|
1062
|
+
allowedValues: textAllowValues(item.allowValues),
|
|
1063
|
+
when: textConstraintConditions(item.conditions),
|
|
1064
|
+
effect: item.effect,
|
|
1065
|
+
limits: textLimitSummary(item.limits),
|
|
1066
|
+
priority: item.priority,
|
|
1067
|
+
name: item.name,
|
|
1068
|
+
}, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'limits', 'priority', 'name']);
|
|
1069
|
+
}
|
|
1070
|
+
if (data.modelGroupCode) {
|
|
1071
|
+
lines.push('next:');
|
|
1072
|
+
pushSectionKeyValue(lines, 'createSpec', `${commandPrefix()} model create-spec --model-group-code ${data.modelGroupCode}`);
|
|
1073
|
+
pushSectionKeyValue(lines, 'reason', '读取创建命令写法、资源语法、fee/dry-run 前置步骤和示例');
|
|
992
1074
|
}
|
|
993
1075
|
return `${lines.join('\n')}\n`;
|
|
994
1076
|
}
|
|
@@ -1000,44 +1082,136 @@ function textKeyBinding(value) {
|
|
|
1000
1082
|
return value;
|
|
1001
1083
|
}
|
|
1002
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
|
+
|
|
1158
|
+
function modelFeeNextCommand(data = {}) {
|
|
1159
|
+
if (!data.feeCommand || !data.modelGroupCode) return null;
|
|
1160
|
+
const args = [
|
|
1161
|
+
`${commandPrefix()} ${data.feeCommand}`,
|
|
1162
|
+
`--model-group-code ${data.modelGroupCode}`,
|
|
1163
|
+
'--prompt "<prompt>"',
|
|
1164
|
+
];
|
|
1165
|
+
if (data.taskKind === 'video') args.push('--duration <duration>', '--ratio <ratio>', '--quality <quality>');
|
|
1166
|
+
if (data.taskKind === 'image') args.push('--ratio <ratio>', '--quality <quality>');
|
|
1167
|
+
return args.join(' ');
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function modelCreateExampleWithCode(example, modelGroupCode) {
|
|
1171
|
+
if (!example || !modelGroupCode) return rewriteCommandPrefix(example);
|
|
1172
|
+
return rewriteCommandPrefix(String(example).replaceAll('--model-group-code <code>', `--model-group-code ${modelGroupCode}`));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1003
1175
|
function formatModelCreateSpecOutput(data = {}) {
|
|
1004
1176
|
const intents = Array.isArray(data.supportedIntents) ? data.supportedIntents : [];
|
|
1005
1177
|
const examples = Array.isArray(data.examples) ? data.examples : [];
|
|
1006
1178
|
const lines = ['ok model create-spec'];
|
|
1007
|
-
lines
|
|
1179
|
+
pushSectionRecord(lines, 'summary', {
|
|
1008
1180
|
modelGroupCode: data.modelGroupCode,
|
|
1009
1181
|
taskKind: data.taskKind,
|
|
1010
1182
|
create: data.createCommand,
|
|
1011
1183
|
fee: data.feeCommand,
|
|
1012
1184
|
statusTaskType: data.statusCommandTaskType,
|
|
1013
|
-
}
|
|
1014
|
-
lines
|
|
1015
|
-
|
|
1185
|
+
}, ['modelGroupCode', 'taskKind', 'create', 'fee', 'statusTaskType']);
|
|
1186
|
+
pushSectionRecord(lines, 'input', {
|
|
1187
|
+
summary: data.inputRequirement?.summary,
|
|
1016
1188
|
acceptedModes: valueList(data.inputRequirement?.acceptedModes),
|
|
1017
|
-
}
|
|
1018
|
-
lines
|
|
1189
|
+
}, ['summary', 'acceptedModes']);
|
|
1190
|
+
pushSectionCount(lines, 'intents', intents.length);
|
|
1019
1191
|
for (const [index, intent] of intents.entries()) {
|
|
1020
|
-
lines
|
|
1192
|
+
pushSectionRecordItem(lines, index, {
|
|
1021
1193
|
mode: intent.mode,
|
|
1022
1194
|
intent: intent.intent,
|
|
1023
1195
|
args: valueList(intent.requiredArgs),
|
|
1024
1196
|
usage: valueList(intent.resourceUsages),
|
|
1025
1197
|
resources: valueList(intent.resourceSyntax),
|
|
1026
1198
|
key: textKeyBinding(intent.promptBinding),
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
if (examples.length) {
|
|
1030
|
-
lines.push(`examples.count=${examples.length}`);
|
|
1031
|
-
for (const [index, example] of examples.entries()) lines.push(`example[${index}]=${example}`);
|
|
1199
|
+
}, ['mode', 'intent', 'args', 'usage', 'resources', 'key']);
|
|
1032
1200
|
}
|
|
1033
|
-
|
|
1201
|
+
pushSectionCount(lines, 'examples', examples.length);
|
|
1202
|
+
for (const [index, example] of examples.entries()) pushSectionItem(lines, index, rewriteCommandPrefix(example));
|
|
1203
|
+
lines.push('next:');
|
|
1204
|
+
if (data.optionsCommand) pushSectionKeyValue(lines, 'options', `${commandPrefix()} ${data.optionsCommand}`);
|
|
1205
|
+
const feeNext = modelFeeNextCommand(data);
|
|
1206
|
+
if (feeNext) pushSectionKeyValue(lines, 'fee', feeNext);
|
|
1207
|
+
if (examples.length) pushSectionKeyValue(lines, 'dryRun', modelCreateExampleWithCode(examples[0], data.modelGroupCode));
|
|
1034
1208
|
return `${lines.join('\n')}\n`;
|
|
1035
1209
|
}
|
|
1036
1210
|
|
|
1037
1211
|
function formatGuideTableRows(rows = []) {
|
|
1038
1212
|
return (Array.isArray(rows) ? rows : []).map((item, index) => {
|
|
1039
1213
|
const values = valueList(item.values);
|
|
1040
|
-
return `
|
|
1214
|
+
return ` [${index}]: ${renderRecord(compactRecord({
|
|
1041
1215
|
name: item.field,
|
|
1042
1216
|
values,
|
|
1043
1217
|
desc: item.description,
|
|
@@ -1047,20 +1221,20 @@ function formatGuideTableRows(rows = []) {
|
|
|
1047
1221
|
|
|
1048
1222
|
function formatModelInputGuideOutput(data = {}) {
|
|
1049
1223
|
const lines = ['ok model input-guide'];
|
|
1050
|
-
lines
|
|
1224
|
+
pushSectionCount(lines, 'commonFields', Array.isArray(data.commonFields) ? data.commonFields.length : 0);
|
|
1051
1225
|
lines.push(...formatGuideTableRows(data.commonFields));
|
|
1052
|
-
lines
|
|
1226
|
+
pushSectionCount(lines, 'resourceFields', Array.isArray(data.resourceFields) ? data.resourceFields.length : 0);
|
|
1053
1227
|
lines.push(...formatGuideTableRows(data.resourceFields));
|
|
1054
1228
|
const rules = Array.isArray(data.resourceRules) ? data.resourceRules : [];
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
for (const [index, rule] of rules.entries()) lines.push(`rule[${index}]=${rule}`);
|
|
1058
|
-
}
|
|
1229
|
+
pushSectionCount(lines, 'resourceRules', rules.length);
|
|
1230
|
+
for (const [index, rule] of rules.entries()) pushSectionItem(lines, index, rule);
|
|
1059
1231
|
const referenceKeyGuide = Array.isArray(data.referenceKeyGuide) ? data.referenceKeyGuide : [];
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1232
|
+
pushSectionCount(lines, 'referenceKey', referenceKeyGuide.length);
|
|
1233
|
+
for (const [index, item] of referenceKeyGuide.entries()) pushSectionItem(lines, index, item);
|
|
1234
|
+
lines.push('next:');
|
|
1235
|
+
pushSectionKeyValue(lines, 'imageModels', `${commandPrefix()} model image-models --model <keyword>`);
|
|
1236
|
+
pushSectionKeyValue(lines, 'videoModels', `${commandPrefix()} model video-models --model <keyword>`);
|
|
1237
|
+
pushSectionKeyValue(lines, 'options', `${commandPrefix()} model options --model-group-code <modelGroupCode>`);
|
|
1064
1238
|
return `${lines.join('\n')}\n`;
|
|
1065
1239
|
}
|
|
1066
1240
|
|
|
@@ -1072,9 +1246,17 @@ function modelListNextCommand(commandName) {
|
|
|
1072
1246
|
if (commandName === 'model image-models' || commandName === 'model video-models') {
|
|
1073
1247
|
return `${commandPrefix()} model options --model-group-code <modelGroupCode>`;
|
|
1074
1248
|
}
|
|
1249
|
+
if (commandName === 'model asset-review-models') {
|
|
1250
|
+
return `${commandPrefix()} create asset-groups --platform <platform> --name "<keyword>"`;
|
|
1251
|
+
}
|
|
1075
1252
|
return null;
|
|
1076
1253
|
}
|
|
1077
1254
|
|
|
1255
|
+
function modelListAgentHint(commandName) {
|
|
1256
|
+
if (commandName !== 'model image-models' && commandName !== 'model video-models') return null;
|
|
1257
|
+
return '对每个候选运行 model options,并把候选模型、参数取值、默认值和资源能力展示给用户后再推荐或进入 fee/dry-run。';
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1078
1260
|
function statusNextCommand(commandName, normalized) {
|
|
1079
1261
|
if (!isPlainObject(normalized) || normalized.isTerminal !== false || !normalized.taskId) return null;
|
|
1080
1262
|
const taskType = normalized.taskType || (commandName === 'task video-status' ? 'VIDEO_GROUP' : 'IMAGE_CREATE');
|
|
@@ -1090,6 +1272,1753 @@ function subtitleRemoveNextCommand(commandName, normalized) {
|
|
|
1090
1272
|
return `${commandPrefix()} create video-subtitle-removal --source-task-id ${normalized.taskId}${projectGroupPart} --dry-run`;
|
|
1091
1273
|
}
|
|
1092
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
|
+
|
|
1093
3022
|
export function formatTextOutput(commandName, data, context = {}) {
|
|
1094
3023
|
if (commandName === 'model options') return formatModelOptionsOutput(data);
|
|
1095
3024
|
if (commandName === 'model create-spec') return formatModelCreateSpecOutput(data);
|
|
@@ -1206,6 +3135,8 @@ export function formatTextOutput(commandName, data, context = {}) {
|
|
|
1206
3135
|
if (normalized.dryRun && !context.confirmCommand && context.executeCommand) lines.push(`next=${context.executeCommand}`);
|
|
1207
3136
|
const modelNext = modelListNextCommand(commandName);
|
|
1208
3137
|
if (modelNext) lines.push(`next=${modelNext}`);
|
|
3138
|
+
const modelHint = modelListAgentHint(commandName);
|
|
3139
|
+
if (modelHint) lines.push(`agentHint=${shortValue(modelHint)}`);
|
|
1209
3140
|
const waitNext = statusNextCommand(commandName, normalized);
|
|
1210
3141
|
if (waitNext) lines.push(`next=${waitNext}`);
|
|
1211
3142
|
const subtitleNext = subtitleRemoveNextCommand(commandName, normalized);
|