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