@lingjingai/lj-awb-cli-pre 0.3.16 → 0.3.18
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 +10 -13
- package/package.json +1 -1
- package/packages/awb-cli/README.md +0 -3
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +1 -1
- package/packages/awb-core/src/api.js +9 -2
- package/packages/awb-core/src/auth.js +1 -1
- package/packages/awb-core/src/commands.js +42 -15
- package/packages/awb-core/src/common.js +3 -2
- package/packages/awb-core/src/output.js +147 -83
- package/packages/awb-core/src/services.js +118 -41
- package/packages/awb-core/src/standalone.js +114 -20
- package/skills/lj-awb/SKILL.md +5 -2
- package/skills/lj-awb/VERSION +1 -1
- package/skills/lj-awb/compat.json +3 -3
- package/skills/lj-awb/modules/asset.md +25 -18
- package/skills/lj-awb/modules/auth.md +1 -1
- package/skills/lj-awb/modules/create-contract.md +1 -1
- package/skills/lj-awb/modules/create.md +1 -0
- package/skills/lj-awb/modules/driver.md +17 -3
- package/skills/lj-awb/modules/model.md +12 -1
- package/skills/lj-awb/modules/subject.md +4 -4
- package/skills/lj-awb/modules/task-manual.md +4 -3
- package/skills/lj-awb/modules/upload.md +5 -3
- package/skills/lj-awb/modules/workflows.md +26 -21
- package/skills/lj-awb/references/error-codes.md +1 -1
- package/skills/lj-awb/references/model-options-read.md +8 -6
- package/skills/lj-awb/references/output-fields.md +6 -5
|
@@ -58,6 +58,9 @@ const REMOTE_VOICE_MIME_TYPES = new Set([
|
|
|
58
58
|
'video/mp4',
|
|
59
59
|
]);
|
|
60
60
|
const REMOTE_VOICE_EXTENSIONS = new Set(['.mp3', '.wav', '.m4a', '.aac', '.ogg', '.mp4']);
|
|
61
|
+
const ASSET_PLATFORM_CODES = ['JIMENG', 'BYTEPLUS'];
|
|
62
|
+
const ASSET_PLATFORM_CODE_SET = new Set(ASSET_PLATFORM_CODES);
|
|
63
|
+
const ASSET_PLATFORM_HINT = ASSET_PLATFORM_CODES.join('|');
|
|
61
64
|
|
|
62
65
|
export function normalizeUserInfo(payload) {
|
|
63
66
|
const data = payload && typeof payload === 'object' ? payload : {};
|
|
@@ -732,6 +735,44 @@ export async function listModels(kind, kwargs = {}) {
|
|
|
732
735
|
return { usage, models };
|
|
733
736
|
}
|
|
734
737
|
|
|
738
|
+
function requireAssetPlatform(value, options = {}) {
|
|
739
|
+
const flag = options.flag || '--platform';
|
|
740
|
+
const platform = trimToNull(value);
|
|
741
|
+
if (!platform) {
|
|
742
|
+
throw argumentError(`缺少参数:${flag}`, `素材加白平台必须显式传入 ${ASSET_PLATFORM_HINT},不要依赖默认平台。`);
|
|
743
|
+
}
|
|
744
|
+
if (!ASSET_PLATFORM_CODE_SET.has(platform)) {
|
|
745
|
+
throw argumentError(`不支持的平台:${platform}`, `只接受 ${ASSET_PLATFORM_HINT};请使用 model asset-review-models 查看模型支持的平台。`);
|
|
746
|
+
}
|
|
747
|
+
return platform;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function optionalAssetPlatform(value) {
|
|
751
|
+
if (value == null || value === '') return null;
|
|
752
|
+
return requireAssetPlatform(value);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function normalizeAssetReviewModelRecord(item = {}) {
|
|
756
|
+
return compactRecord({
|
|
757
|
+
modelGroupCode: item?.modelGroupCode ?? item?.groupCode ?? null,
|
|
758
|
+
platform: item?.platform ?? null,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export async function assetReviewModels(kwargs = {}) {
|
|
763
|
+
const modelGroupCode = trimToNull(kwargs.modelGroupCode);
|
|
764
|
+
const platform = optionalAssetPlatform(kwargs.platform);
|
|
765
|
+
const payload = await awbApi.fetchAssetReviewModels();
|
|
766
|
+
const models = normalizeRows(payload)
|
|
767
|
+
.map((item) => normalizeAssetReviewModelRecord(item))
|
|
768
|
+
.filter((item) => !modelGroupCode || item.modelGroupCode === modelGroupCode)
|
|
769
|
+
.filter((item) => !platform || item.platform === platform);
|
|
770
|
+
return {
|
|
771
|
+
models,
|
|
772
|
+
...(toBool(kwargs.includeRaw) ? { raw: payload } : {}),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
735
776
|
async function findModelGroup(modelGroupCode, options = {}) {
|
|
736
777
|
const includeRaw = Boolean(options.includeRaw);
|
|
737
778
|
const [imageResult, videoResult] = await Promise.allSettled([
|
|
@@ -2168,7 +2209,35 @@ async function readJsonMaybeFile(value, fallback) {
|
|
|
2168
2209
|
return JSON.parse(fileText);
|
|
2169
2210
|
}
|
|
2170
2211
|
|
|
2171
|
-
|
|
2212
|
+
const AUDIO_VIDEO_EXTENSIONS = new Set([
|
|
2213
|
+
'.aac',
|
|
2214
|
+
'.aiff',
|
|
2215
|
+
'.avi',
|
|
2216
|
+
'.flac',
|
|
2217
|
+
'.m4a',
|
|
2218
|
+
'.m4v',
|
|
2219
|
+
'.mkv',
|
|
2220
|
+
'.mov',
|
|
2221
|
+
'.mp3',
|
|
2222
|
+
'.mp4',
|
|
2223
|
+
'.mpeg',
|
|
2224
|
+
'.mpg',
|
|
2225
|
+
'.oga',
|
|
2226
|
+
'.ogg',
|
|
2227
|
+
'.ogv',
|
|
2228
|
+
'.wav',
|
|
2229
|
+
'.webm',
|
|
2230
|
+
]);
|
|
2231
|
+
|
|
2232
|
+
function defaultUploadSceneForFile(filePath, mimeType = '') {
|
|
2233
|
+
const normalizedMime = String(mimeType || '').toLowerCase();
|
|
2234
|
+
if (normalizedMime.startsWith('audio/') || normalizedMime.startsWith('video/')) return TASK_UPLOAD_SCENE.VIDEO_CREATE;
|
|
2235
|
+
const ext = path.extname(String(filePath || '')).toLowerCase();
|
|
2236
|
+
if (AUDIO_VIDEO_EXTENSIONS.has(ext)) return TASK_UPLOAD_SCENE.VIDEO_CREATE;
|
|
2237
|
+
return TASK_UPLOAD_SCENE.DEFAULT;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
async function collectFileSpecs(kwargs = {}, defaultSceneType = null) {
|
|
2172
2241
|
const specs = [];
|
|
2173
2242
|
for (const file of parseListArg(kwargs.file)) specs.push({ file });
|
|
2174
2243
|
for (const file of parseListArg(kwargs.files)) specs.push({ file });
|
|
@@ -2181,11 +2250,14 @@ async function collectFileSpecs(kwargs = {}, defaultSceneType = TASK_UPLOAD_SCEN
|
|
|
2181
2250
|
}
|
|
2182
2251
|
}
|
|
2183
2252
|
return specs
|
|
2184
|
-
.map((item) =>
|
|
2185
|
-
file
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2253
|
+
.map((item) => {
|
|
2254
|
+
const file = trimToNull(item.file ?? item.path ?? item.filePath);
|
|
2255
|
+
return {
|
|
2256
|
+
file,
|
|
2257
|
+
sceneType: trimToNull(item.sceneType ?? kwargs.sceneType) ?? defaultSceneType ?? defaultUploadSceneForFile(file),
|
|
2258
|
+
projectNo: trimToNull(item.projectNo ?? kwargs.projectNo) ?? '',
|
|
2259
|
+
};
|
|
2260
|
+
})
|
|
2189
2261
|
.filter((item) => item.file);
|
|
2190
2262
|
}
|
|
2191
2263
|
|
|
@@ -2194,7 +2266,7 @@ function dryRunBackendPath(filePath, sceneType) {
|
|
|
2194
2266
|
}
|
|
2195
2267
|
|
|
2196
2268
|
export async function uploadFilesCommand(kwargs = {}) {
|
|
2197
|
-
const specs = await collectFileSpecs(kwargs, trimToNull(kwargs.sceneType)
|
|
2269
|
+
const specs = await collectFileSpecs(kwargs, trimToNull(kwargs.sceneType));
|
|
2198
2270
|
if (!specs.length) throw argumentError('缺少上传文件', '传 --file <path> 或 --files a.png,b.mp4。');
|
|
2199
2271
|
if (toBool(kwargs.dryRun)) {
|
|
2200
2272
|
const files = [];
|
|
@@ -2224,7 +2296,7 @@ export async function uploadLocalFile(filePath, options = {}) {
|
|
|
2224
2296
|
throw argumentError(`文件不存在:${filePath}`);
|
|
2225
2297
|
}
|
|
2226
2298
|
const buffer = await fs.readFile(inspected.filePath);
|
|
2227
|
-
const sceneType = options.sceneType ??
|
|
2299
|
+
const sceneType = options.sceneType ?? defaultUploadSceneForFile(inspected.filePath, inspected.mimeType);
|
|
2228
2300
|
const groupId = crypto.randomUUID().replaceAll('-', '');
|
|
2229
2301
|
const secret = await awbApi.fetchUploadSecret({
|
|
2230
2302
|
sceneType,
|
|
@@ -3433,6 +3505,7 @@ function normalizeAssetGroup(item = {}) {
|
|
|
3433
3505
|
groupId: item?.id ?? item?.groupId ?? item?.assetGroupsId ?? null,
|
|
3434
3506
|
name: item?.name ?? item?.groupName ?? null,
|
|
3435
3507
|
description: item?.description ?? null,
|
|
3508
|
+
platform: item?.platform ?? null,
|
|
3436
3509
|
projectName: item?.projectName ?? null,
|
|
3437
3510
|
};
|
|
3438
3511
|
}
|
|
@@ -3640,43 +3713,53 @@ export async function assetMatchActor(kwargs = {}) {
|
|
|
3640
3713
|
}
|
|
3641
3714
|
|
|
3642
3715
|
export async function assetGroupList(kwargs = {}) {
|
|
3716
|
+
const platform = requireAssetPlatform(kwargs.platform);
|
|
3643
3717
|
const payload = await awbApi.listAssetGroups({
|
|
3644
3718
|
name: kwargs.name ?? '',
|
|
3719
|
+
platform,
|
|
3645
3720
|
pageNumber: toInt(kwargs.pageNumber, 1),
|
|
3646
3721
|
pageSize: toInt(kwargs.pageSize, 20),
|
|
3647
3722
|
...(parseListArg(kwargs.groupIds).length ? { groupIds: parseListArg(kwargs.groupIds) } : {}),
|
|
3648
3723
|
});
|
|
3649
|
-
return { groups: extractAssetGroupRows(payload), raw: toBool(kwargs.includeRaw) ? payload : undefined };
|
|
3724
|
+
return { platform, groups: extractAssetGroupRows(payload), raw: toBool(kwargs.includeRaw) ? payload : undefined };
|
|
3650
3725
|
}
|
|
3651
3726
|
|
|
3652
3727
|
export async function assetGroupGet(kwargs = {}) {
|
|
3653
3728
|
const groupId = requireValue(kwargs, 'groupId', 'group-id');
|
|
3654
|
-
const
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3729
|
+
const platform = requireAssetPlatform(kwargs.platform);
|
|
3730
|
+
const payload = await awbApi.getAssetGroup(groupId, { platform });
|
|
3731
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
3732
|
+
const group = normalizeAssetGroup(payload);
|
|
3733
|
+
return { ...group, platform: group.platform ?? platform };
|
|
3734
|
+
}
|
|
3735
|
+
return extractAssetGroupRows(payload)[0] ?? { groupId, platform };
|
|
3658
3736
|
}
|
|
3659
3737
|
|
|
3660
3738
|
export async function assetGroupCreate(kwargs = {}) {
|
|
3661
3739
|
const name = requireValue(kwargs, 'name');
|
|
3740
|
+
const platform = requireAssetPlatform(kwargs.platform);
|
|
3662
3741
|
const body = {
|
|
3663
3742
|
name,
|
|
3664
3743
|
description: kwargs.description ?? '',
|
|
3744
|
+
platform,
|
|
3665
3745
|
projectName: kwargs.projectName ?? 'default',
|
|
3666
3746
|
};
|
|
3667
3747
|
if (toBool(kwargs.dryRun)) return { dryRun: true, action: 'create asset-group', request: body };
|
|
3668
3748
|
ensureConfirmed(kwargs, '创建素材组是云端写入动作,需要确认', { action: 'create asset-group', body });
|
|
3669
3749
|
const payload = await awbApi.createAssetGroup(body);
|
|
3670
|
-
return { created: true, groupId: payload?.id ?? payload?.groupId ?? payload ?? null, name, projectName: body.projectName };
|
|
3750
|
+
return { created: true, groupId: payload?.id ?? payload?.groupId ?? payload ?? null, name, platform, projectName: body.projectName };
|
|
3671
3751
|
}
|
|
3672
3752
|
|
|
3673
3753
|
export async function assetGroupUpdate(kwargs = {}) {
|
|
3674
3754
|
const groupId = requireValue(kwargs, 'groupId', 'group-id');
|
|
3675
|
-
const
|
|
3755
|
+
const platform = requireAssetPlatform(kwargs.platform);
|
|
3756
|
+
const body = { platform };
|
|
3676
3757
|
if (kwargs.name != null) body.name = kwargs.name;
|
|
3677
3758
|
if (kwargs.description != null) body.description = kwargs.description;
|
|
3678
3759
|
if (kwargs.projectName != null) body.projectName = kwargs.projectName;
|
|
3679
|
-
if (!
|
|
3760
|
+
if (!['name', 'description', 'projectName'].some((key) => Object.prototype.hasOwnProperty.call(body, key))) {
|
|
3761
|
+
throw argumentError('缺少素材组更新字段', '至少传 --name、--description 或 --project-name;--platform 只用于定位平台资产组。');
|
|
3762
|
+
}
|
|
3680
3763
|
if (toBool(kwargs.dryRun)) return { dryRun: true, action: 'create asset-group-update', groupId, request: body };
|
|
3681
3764
|
ensureConfirmed(kwargs, '更新素材组是云端写入动作,需要确认', { action: 'create asset-group-update', groupId, body });
|
|
3682
3765
|
await awbApi.updateAssetGroup(groupId, body);
|
|
@@ -3692,6 +3775,7 @@ function extractAssetId(payload) {
|
|
|
3692
3775
|
export async function assetRegister(kwargs = {}) {
|
|
3693
3776
|
const groupId = requireValue(kwargs, 'groupId', 'group-id');
|
|
3694
3777
|
const name = requireValue(kwargs, 'name');
|
|
3778
|
+
const platform = requireAssetPlatform(kwargs.platform);
|
|
3695
3779
|
const localFile = trimToNull(kwargs.file);
|
|
3696
3780
|
const assetPath = trimToNull(kwargs.backendPath) ?? normalizeCosAssetPath(kwargs.url);
|
|
3697
3781
|
if (!localFile && !assetPath) throw argumentError('缺少素材路径', '传 --file、--backend-path 或 --url。');
|
|
@@ -3701,20 +3785,20 @@ export async function assetRegister(kwargs = {}) {
|
|
|
3701
3785
|
action: 'create asset',
|
|
3702
3786
|
request: {
|
|
3703
3787
|
assetGroupsId: groupId,
|
|
3704
|
-
url: localFile ? normalizeCosAssetPath(dryRunBackendPath(localFile, TASK_UPLOAD_SCENE.
|
|
3788
|
+
url: localFile ? normalizeCosAssetPath(dryRunBackendPath(localFile, TASK_UPLOAD_SCENE.ASSET_REVIEW)) : assetPath,
|
|
3705
3789
|
name,
|
|
3706
|
-
|
|
3790
|
+
platform,
|
|
3707
3791
|
},
|
|
3708
3792
|
localFile: localFile ? await inspectLocalFile(localFile) : null,
|
|
3709
3793
|
};
|
|
3710
3794
|
}
|
|
3711
3795
|
ensureConfirmed(kwargs, '注册素材是云端写入动作,需要确认', { action: 'create asset', groupId, name });
|
|
3712
|
-
const uploaded = localFile ? await uploadLocalFile(localFile, { sceneType: TASK_UPLOAD_SCENE.
|
|
3796
|
+
const uploaded = localFile ? await uploadLocalFile(localFile, { sceneType: TASK_UPLOAD_SCENE.ASSET_REVIEW }) : null;
|
|
3713
3797
|
const body = {
|
|
3714
3798
|
assetGroupsId: groupId,
|
|
3715
3799
|
url: normalizeCosAssetPath(uploaded?.backendPath ?? assetPath),
|
|
3716
3800
|
name,
|
|
3717
|
-
|
|
3801
|
+
platform,
|
|
3718
3802
|
};
|
|
3719
3803
|
const payload = await awbApi.registerAsset(body);
|
|
3720
3804
|
return {
|
|
@@ -3722,6 +3806,7 @@ export async function assetRegister(kwargs = {}) {
|
|
|
3722
3806
|
assetId: extractAssetId(payload),
|
|
3723
3807
|
groupId,
|
|
3724
3808
|
name,
|
|
3809
|
+
platform,
|
|
3725
3810
|
assetPath: body.url,
|
|
3726
3811
|
...(uploaded ? { upload: uploaded } : {}),
|
|
3727
3812
|
};
|
|
@@ -4008,37 +4093,32 @@ function tagListFromArg(value) {
|
|
|
4008
4093
|
.filter((item) => item.tagId);
|
|
4009
4094
|
}
|
|
4010
4095
|
|
|
4011
|
-
|
|
4012
|
-
const text = trimToNull(value);
|
|
4013
|
-
if (!text) return null;
|
|
4014
|
-
const normalized = text.toLowerCase();
|
|
4015
|
-
if (normalized === 'tx' || normalized === 'vidu') return normalized;
|
|
4016
|
-
return null;
|
|
4017
|
-
}
|
|
4096
|
+
const SUBJECT_MODEL_CODE_HINT = '传 --model-code tx|vidu;KeLing / 可灵主体明确传 tx,Vidu 主体明确传 vidu。';
|
|
4018
4097
|
|
|
4019
4098
|
function resolveSubjectCreateModelCode(kwargs = {}) {
|
|
4020
4099
|
const explicitModelCode = trimToNull(kwargs.modelCode ?? kwargs.model_code);
|
|
4021
|
-
|
|
4022
|
-
|
|
4100
|
+
if (explicitModelCode === 'tx' || explicitModelCode === 'vidu') {
|
|
4101
|
+
return { modelCode: explicitModelCode };
|
|
4102
|
+
}
|
|
4023
4103
|
if (explicitModelCode) {
|
|
4024
4104
|
throw argumentError(
|
|
4025
4105
|
`主体 modelCode 不支持:${explicitModelCode}`,
|
|
4026
|
-
|
|
4106
|
+
SUBJECT_MODEL_CODE_HINT,
|
|
4027
4107
|
);
|
|
4028
4108
|
}
|
|
4029
4109
|
|
|
4030
4110
|
throw argumentError(
|
|
4031
4111
|
'缺少主体 modelCode',
|
|
4032
|
-
|
|
4112
|
+
SUBJECT_MODEL_CODE_HINT,
|
|
4033
4113
|
);
|
|
4034
4114
|
}
|
|
4035
4115
|
|
|
4036
|
-
function buildSubjectCreateBody(kwargs, specs, assets) {
|
|
4116
|
+
function buildSubjectCreateBody(kwargs, specs, assets, modelCodeResolution = resolveSubjectCreateModelCode(kwargs)) {
|
|
4037
4117
|
const name = requireValue(kwargs, 'name');
|
|
4038
4118
|
const description = trimToNull(kwargs.description ?? kwargs.elementDescription) ?? name;
|
|
4039
4119
|
const primary = assets.find((item) => item.isPrimary);
|
|
4040
4120
|
const referAssets = assets.filter((item) => item.assetPath);
|
|
4041
|
-
const { modelCode } =
|
|
4121
|
+
const { modelCode } = modelCodeResolution;
|
|
4042
4122
|
return compactRecord({
|
|
4043
4123
|
reqTaskId: trimToNull(kwargs.reqTaskId),
|
|
4044
4124
|
modelCode,
|
|
@@ -4117,10 +4197,7 @@ export async function subjectPublish(kwargs = {}) {
|
|
|
4117
4197
|
dryRun: true,
|
|
4118
4198
|
action: 'create subject',
|
|
4119
4199
|
name,
|
|
4120
|
-
|
|
4121
|
-
resolvedFrom: modelCodeResolution.resolvedFrom,
|
|
4122
|
-
resolvedFromValue: modelCodeResolution.resolvedFromValue,
|
|
4123
|
-
request: buildSubjectCreateBody(kwargs, specs, assets),
|
|
4200
|
+
request: buildSubjectCreateBody(kwargs, specs, assets, modelCodeResolution),
|
|
4124
4201
|
assets,
|
|
4125
4202
|
localFiles: await inspectLocalFiles(specs.map((item) => item.file).filter(Boolean)),
|
|
4126
4203
|
nextRefSubject: `${name}=<externalId>`,
|
|
@@ -4140,7 +4217,7 @@ export async function subjectPublish(kwargs = {}) {
|
|
|
4140
4217
|
...(uploaded ? { upload: uploaded } : {}),
|
|
4141
4218
|
});
|
|
4142
4219
|
}
|
|
4143
|
-
const body = buildSubjectCreateBody(kwargs, specs, assets);
|
|
4220
|
+
const body = buildSubjectCreateBody(kwargs, specs, assets, modelCodeResolution);
|
|
4144
4221
|
const payload = await awbApi.createElement(body);
|
|
4145
4222
|
const elementId = extractCreatedElementId(payload);
|
|
4146
4223
|
if (!elementId) throw new LingjingAwbCliError('主体创建完成但未拿到 elementId', { type: 'api_error', exitCode: 1, details: payload });
|
|
@@ -4229,14 +4306,14 @@ export async function subtitleRemove(kwargs = {}) {
|
|
|
4229
4306
|
});
|
|
4230
4307
|
await appendTaskRecord(kwargs, {
|
|
4231
4308
|
taskId: result.taskId,
|
|
4232
|
-
taskType: '
|
|
4309
|
+
taskType: 'VIDEO_SUBTITLE_REMOVAL',
|
|
4233
4310
|
projectGroupNo: built.projectGroupNo,
|
|
4234
4311
|
promptSummary: `去字幕 sourceTaskId=${built.sourceTaskId}`,
|
|
4235
4312
|
});
|
|
4236
4313
|
return {
|
|
4237
4314
|
...result,
|
|
4238
4315
|
nextCommand: result.taskId
|
|
4239
|
-
? `lj-awb task
|
|
4316
|
+
? `lj-awb task video-subtitle-status --task-id ${result.taskId}${built.projectGroupNo ? ` --project-group-no ${built.projectGroupNo}` : ''} -f json`
|
|
4240
4317
|
: null,
|
|
4241
4318
|
};
|
|
4242
4319
|
}
|
|
@@ -4270,6 +4347,6 @@ export async function subtitleStatus(kwargs = {}) {
|
|
|
4270
4347
|
return taskStatus({
|
|
4271
4348
|
...kwargs,
|
|
4272
4349
|
taskId,
|
|
4273
|
-
taskType: '
|
|
4350
|
+
taskType: 'VIDEO_SUBTITLE_REMOVAL',
|
|
4274
4351
|
});
|
|
4275
4352
|
}
|
|
@@ -24,6 +24,7 @@ const OPTION_SYNONYMS = {
|
|
|
24
24
|
threads: ['concurrency'],
|
|
25
25
|
output: ['format'],
|
|
26
26
|
json: ['format'],
|
|
27
|
+
keyword: ['model'],
|
|
27
28
|
input: ['inputFile'],
|
|
28
29
|
in: ['inputFile'],
|
|
29
30
|
dry: ['dryRun'],
|
|
@@ -60,6 +61,16 @@ function suggestSimilarOptions(unknownKey, allowedKeys) {
|
|
|
60
61
|
for (const syn of synonyms) {
|
|
61
62
|
if (allowedKeys.includes(syn)) record(syn, -1);
|
|
62
63
|
}
|
|
64
|
+
if (unknownKey.length === 1) {
|
|
65
|
+
const ch = unknownKey.toLowerCase();
|
|
66
|
+
for (const allowed of allowedKeys) {
|
|
67
|
+
if (allowed.toLowerCase().startsWith(ch)) record(allowed, 1);
|
|
68
|
+
}
|
|
69
|
+
return [...scored.entries()]
|
|
70
|
+
.sort((x, y) => x[1] - y[1])
|
|
71
|
+
.slice(0, 3)
|
|
72
|
+
.map(([key]) => formatOptionName(key));
|
|
73
|
+
}
|
|
63
74
|
for (const allowed of allowedKeys) {
|
|
64
75
|
const a = unknownKey.toLowerCase();
|
|
65
76
|
const b = allowed.toLowerCase();
|
|
@@ -153,6 +164,7 @@ function assignKwarg(kwargs, key, value) {
|
|
|
153
164
|
function parseArgv(argv) {
|
|
154
165
|
const commandParts = [];
|
|
155
166
|
const kwargs = {};
|
|
167
|
+
const kwargRawFlags = {};
|
|
156
168
|
let format = 'text';
|
|
157
169
|
|
|
158
170
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -182,10 +194,27 @@ function parseArgv(argv) {
|
|
|
182
194
|
}
|
|
183
195
|
continue;
|
|
184
196
|
}
|
|
197
|
+
if (token.length > 1 && token.startsWith('-') && /^-[A-Za-z]/.test(token)) {
|
|
198
|
+
const raw = token.slice(1);
|
|
199
|
+
const eqIndex = raw.indexOf('=');
|
|
200
|
+
const keyPart = eqIndex >= 0 ? raw.slice(0, eqIndex) : raw;
|
|
201
|
+
const inlineValue = eqIndex >= 0 ? raw.slice(eqIndex + 1) : undefined;
|
|
202
|
+
const key = normalizeKey(keyPart);
|
|
203
|
+
if (!(key in kwargRawFlags)) kwargRawFlags[key] = token;
|
|
204
|
+
if (inlineValue !== undefined) {
|
|
205
|
+
assignKwarg(kwargs, key, inlineValue);
|
|
206
|
+
} else if (argv[index + 1] && !argv[index + 1].startsWith('-')) {
|
|
207
|
+
assignKwarg(kwargs, key, argv[index + 1]);
|
|
208
|
+
index += 1;
|
|
209
|
+
} else {
|
|
210
|
+
assignKwarg(kwargs, key, true);
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
185
214
|
commandParts.push(token);
|
|
186
215
|
}
|
|
187
216
|
|
|
188
|
-
return { commandName: commandParts.join(' '), kwargs, format };
|
|
217
|
+
return { commandName: commandParts.join(' '), kwargs, kwargRawFlags, format };
|
|
189
218
|
}
|
|
190
219
|
|
|
191
220
|
async function readVersion() {
|
|
@@ -217,6 +246,33 @@ const VIRTUAL_COMMANDS = [
|
|
|
217
246
|
];
|
|
218
247
|
|
|
219
248
|
const RENAMED_COMMAND_HINTS = {
|
|
249
|
+
image: 'create image',
|
|
250
|
+
'image fee': 'create image-fee',
|
|
251
|
+
'image create': 'create image',
|
|
252
|
+
'image create-batch': 'create image-batch',
|
|
253
|
+
'image status': 'task image-status',
|
|
254
|
+
video: 'create video',
|
|
255
|
+
'video fee': 'create video-fee',
|
|
256
|
+
'video create': 'create video',
|
|
257
|
+
'video create-batch': 'create video-batch',
|
|
258
|
+
'video status': 'task video-status',
|
|
259
|
+
'video subtitle-remove': 'create video-subtitle-removal',
|
|
260
|
+
'video subtitle-status': 'task video-subtitle-status',
|
|
261
|
+
asset: 'create asset',
|
|
262
|
+
'asset match-actor': 'create asset-match-actor',
|
|
263
|
+
'asset groups': 'create asset-groups',
|
|
264
|
+
'asset group': 'create asset-group-get',
|
|
265
|
+
'asset group-create': 'create asset-group',
|
|
266
|
+
'asset group-update': 'create asset-group-update',
|
|
267
|
+
'asset register': 'create asset',
|
|
268
|
+
subject: 'create subject',
|
|
269
|
+
'subject list': 'create subject-list',
|
|
270
|
+
'subject publish': 'create subject',
|
|
271
|
+
'subject wait': 'create subject-wait',
|
|
272
|
+
'subject publish-batch': 'create subject-batch',
|
|
273
|
+
'subject voice list': 'create subject-voice-list',
|
|
274
|
+
'subject voice create': 'create subject-voice',
|
|
275
|
+
'subject voice wait': 'create subject-voice-wait',
|
|
220
276
|
'workspace me': 'account info',
|
|
221
277
|
'workspace teams': 'account teams',
|
|
222
278
|
'workspace team-select': 'account switch-team',
|
|
@@ -367,6 +423,7 @@ const GROUP_EXAMPLES = {
|
|
|
367
423
|
model: [
|
|
368
424
|
'lj-awb model image-models --model Banana',
|
|
369
425
|
'lj-awb model video-models --model Seedance',
|
|
426
|
+
'lj-awb model asset-review-models --platform JIMENG',
|
|
370
427
|
'lj-awb model input-guide',
|
|
371
428
|
'lj-awb model options --model-group-code <code>',
|
|
372
429
|
'lj-awb model create-spec --model-group-code <code>',
|
|
@@ -381,8 +438,8 @@ const GROUP_EXAMPLES = {
|
|
|
381
438
|
'lj-awb create video-fee --model-group-code <code> --prompt "雨夜奔跑" --duration 5',
|
|
382
439
|
'lj-awb create subject --model-code tx --name 女主 --resource primary:./three-view.png --dry-run',
|
|
383
440
|
'lj-awb create subject-wait --element-id <elementId> --wait-seconds 300',
|
|
384
|
-
'lj-awb create asset --group-id <id> --file ./actor.png --name "女主正面" --dry-run',
|
|
385
|
-
'lj-awb create asset-groups --name "女主"',
|
|
441
|
+
'lj-awb create asset --group-id <id> --platform JIMENG --file ./actor.png --name "女主正面" --dry-run',
|
|
442
|
+
'lj-awb create asset-groups --platform JIMENG --name "女主"',
|
|
386
443
|
],
|
|
387
444
|
task: [
|
|
388
445
|
'lj-awb task list --task-type IMAGE_CREATE --project-group-no <no>',
|
|
@@ -417,10 +474,11 @@ const COMMAND_REQUIRED_OPTIONS = {
|
|
|
417
474
|
'task video-subtitle-status': ['taskId'],
|
|
418
475
|
'task wait': ['taskId', 'taskType'],
|
|
419
476
|
'create asset-match-actor': ['description'],
|
|
420
|
-
'create asset-
|
|
421
|
-
'create asset-group': ['
|
|
422
|
-
'create asset-group
|
|
423
|
-
'create asset': ['groupId', '
|
|
477
|
+
'create asset-groups': ['platform'],
|
|
478
|
+
'create asset-group-get': ['groupId', 'platform'],
|
|
479
|
+
'create asset-group': ['name', 'platform'],
|
|
480
|
+
'create asset-group-update': ['groupId', 'platform'],
|
|
481
|
+
'create asset': ['groupId', 'name', 'platform'],
|
|
424
482
|
'artifact script row': ['rowKind', 'entityKey'],
|
|
425
483
|
'artifact script children': ['parentKey'],
|
|
426
484
|
'artifact script delete-row': ['rowKind', 'entityKey'],
|
|
@@ -595,6 +653,7 @@ const OUTPUT_KIND_BY_COMMAND = {
|
|
|
595
653
|
'credits usage': 'credits_usage',
|
|
596
654
|
'model image-models': 'model_list',
|
|
597
655
|
'model video-models': 'model_list',
|
|
656
|
+
'model asset-review-models': 'asset_review_model_list',
|
|
598
657
|
'model options': 'model_options',
|
|
599
658
|
'model create-spec': 'model_create_spec',
|
|
600
659
|
'model input-guide': 'model_input_guide',
|
|
@@ -687,9 +746,9 @@ const PREFLIGHTS_BY_COMMAND = {
|
|
|
687
746
|
'create subject': ['doctor --verify', 'create subject --model-code tx|vidu --dry-run'],
|
|
688
747
|
'create subject-batch': ['doctor --verify', 'prepare JSONL input with modelCode', 'create subject-batch --dry-run'],
|
|
689
748
|
'create video-subtitle-removal': ['doctor --verify', 'create video-subtitle-removal --source-task-id <videoTaskId> --dry-run'],
|
|
690
|
-
'create asset-group': ['create asset-group --dry-run'],
|
|
691
|
-
'create asset-group-update': ['create asset-group-update --dry-run'],
|
|
692
|
-
'create asset': ['create asset --dry-run'],
|
|
749
|
+
'create asset-group': ['model asset-review-models', 'create asset-group --platform <platform> --dry-run'],
|
|
750
|
+
'create asset-group-update': ['create asset-group-update --platform <platform> --dry-run'],
|
|
751
|
+
'create asset': ['model asset-review-models', 'create asset --platform <platform> --dry-run'],
|
|
693
752
|
'upload files': ['upload files --dry-run'],
|
|
694
753
|
};
|
|
695
754
|
|
|
@@ -782,6 +841,23 @@ function buildCommandWorkflow(command) {
|
|
|
782
841
|
if (command.name === 'create asset') {
|
|
783
842
|
next.push('读取 data.assetPath', '若后端返回审核 taskId,再运行 task wait --task-type ASSET_REGISTER');
|
|
784
843
|
}
|
|
844
|
+
if (command.name === 'model image-models' || command.name === 'model video-models') {
|
|
845
|
+
next.push('读取 data.models[] 得到全部候选;用户给了模型口语名时保留同族全部候选,不只取第一个');
|
|
846
|
+
next.push('对每个候选运行 model options --model-group-code <modelGroupCode> 查看真实参数和素材约束');
|
|
847
|
+
next.push('先向用户展示候选模型 + 参数取值 + 资源能力,再推荐模型或进入 fee/dry-run');
|
|
848
|
+
}
|
|
849
|
+
if (command.name === 'model asset-review-models') {
|
|
850
|
+
next.push('读取 data.models[].platform', '用 create asset-groups --platform <platform> 查重或创建素材组');
|
|
851
|
+
}
|
|
852
|
+
if (command.name === 'model options') {
|
|
853
|
+
next.push('读取 params/resources/constraints 确认参数与素材约束', '运行 model create-spec --model-group-code <code> 查看创建写法和示例');
|
|
854
|
+
}
|
|
855
|
+
if (command.name === 'model create-spec') {
|
|
856
|
+
next.push('按 examples / supportedIntents 组装命令', '用户确认关键参数后先运行 create image-fee 或 create video-fee,再 dry-run');
|
|
857
|
+
}
|
|
858
|
+
if (command.name === 'model input-guide') {
|
|
859
|
+
next.push('运行 model image-models 或 model video-models 选择模型', '选定模型后运行 model options --model-group-code <code>');
|
|
860
|
+
}
|
|
785
861
|
if (['create image-batch', 'create video-batch', 'create subject-batch'].includes(command.name)) {
|
|
786
862
|
next.push('读取每项 status / taskId / error', '使用 task record-poll 或对应 wait 命令恢复批量结果');
|
|
787
863
|
}
|
|
@@ -803,6 +879,7 @@ function buildAgentContract() {
|
|
|
803
879
|
'schema version / domains / commands',
|
|
804
880
|
'auth / account / team / current projectGroup',
|
|
805
881
|
'modelCandidates by taskKind + keyword',
|
|
882
|
+
'assetReviewModels by modelGroupCode / platform',
|
|
806
883
|
'modelOptions / modelCreateSpec by modelGroupCode',
|
|
807
884
|
'uploaded backendPath by local file path or remote URL',
|
|
808
885
|
'taskId -> taskType / projectGroupNo / resultUrls / errorMessage',
|
|
@@ -813,10 +890,12 @@ function buildAgentContract() {
|
|
|
813
890
|
invalidation: '用户切换账号、团队、项目组、模型、素材、prompt 或关键参数后,只刷新受影响的缓存;不要重复跑未变化的 model options / create-spec / fee。',
|
|
814
891
|
},
|
|
815
892
|
workflowPolicy: [
|
|
893
|
+
'模型口语名命中后必须先展示候选模型、真实参数取值和资源能力;没有完成用户可见候选清单前,不得代选默认模型 / 参数,也不得进入 fee 或 dry-run。',
|
|
816
894
|
'模型探索阶段只读 model list / options / create-spec;fee 只在用户确认关键参数后跑一次。',
|
|
817
895
|
'supportsDryRun=true 的写入 / 扣费命令先 dry-run,确认后 yes;不要把 dry-run 当参数探索工具反复跑。',
|
|
818
896
|
'用户给出多条同模型同参数任务时优先 batch + task-record-file,不要单条循环 create。',
|
|
819
897
|
'命令返回 nextCommand / nextRefSubject / nextVoiceArg 时优先复用返回值,不手拼等价命令。',
|
|
898
|
+
'素材加白平台先通过 model asset-review-models 或用户明确输入确定;create asset-* 必须显式传 --platform,不要依赖默认平台。',
|
|
820
899
|
'旧根域 image / video / asset / subject 已移除;只使用 create / task / artifact 等 schema 暴露的 domain。',
|
|
821
900
|
],
|
|
822
901
|
canonicalFields: {
|
|
@@ -854,6 +933,12 @@ function buildAgentContract() {
|
|
|
854
933
|
},
|
|
855
934
|
],
|
|
856
935
|
},
|
|
936
|
+
modelCandidatePresentation: {
|
|
937
|
+
purpose: '把 model image-models / video-models 与每个候选的 model options 转成用户可见清单,避免 Agent 内部看完 options 后直接代选。',
|
|
938
|
+
trigger: '用户问有哪些模型、给出口语名、或模型列表返回候选时都触发;即使只有一个候选也要展示真实可选项。',
|
|
939
|
+
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'],
|
|
941
|
+
},
|
|
857
942
|
modelOptions: {
|
|
858
943
|
command: `${commandPrefix()} model options --model-group-code <code>`,
|
|
859
944
|
jsonCommand: `${commandPrefix()} model options --model-group-code <code> -f json`,
|
|
@@ -1150,28 +1235,37 @@ function printCommandHelp(command) {
|
|
|
1150
1235
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1151
1236
|
}
|
|
1152
1237
|
|
|
1153
|
-
function validateCommandOptions(command, kwargs) {
|
|
1238
|
+
function validateCommandOptions(command, kwargs, kwargRawFlags = {}) {
|
|
1154
1239
|
const allowedKeys = (command.args || []).map((arg) => normalizeKey(arg.name));
|
|
1155
1240
|
const allowed = new Set(allowedKeys);
|
|
1156
1241
|
const unknown = Object.keys(kwargs).filter((key) => !allowed.has(key));
|
|
1157
1242
|
if (!unknown.length) return;
|
|
1243
|
+
const displayFlag = (key) => kwargRawFlags[key] || formatOptionName(key);
|
|
1158
1244
|
const suggestions = {};
|
|
1159
1245
|
for (const unk of unknown) {
|
|
1160
1246
|
const matches = suggestSimilarOptions(unk, allowedKeys);
|
|
1161
|
-
if (matches.length) suggestions[
|
|
1247
|
+
if (matches.length) suggestions[displayFlag(unk)] = matches;
|
|
1162
1248
|
}
|
|
1249
|
+
const usedShortForm = unknown.some((key) => {
|
|
1250
|
+
const flag = kwargRawFlags[key];
|
|
1251
|
+
return flag && flag.startsWith('-') && !flag.startsWith('--');
|
|
1252
|
+
});
|
|
1163
1253
|
const baseHint = `运行 ${commandPrefix()} ${command.name} -h 查看可用参数。`;
|
|
1254
|
+
const shortHint = usedShortForm
|
|
1255
|
+
? `本 CLI 仅支持 -f / -h / -v 等少数短选项,业务参数请使用 --xxx 长形式。`
|
|
1256
|
+
: '';
|
|
1164
1257
|
const suggestionParts = Object.entries(suggestions).map(([opt, matches]) => `${opt} → ${matches.join(' / ')}`);
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1258
|
+
const hintParts = [];
|
|
1259
|
+
if (suggestionParts.length) hintParts.push(`你是不是想用:${suggestionParts.join(';')}?`);
|
|
1260
|
+
if (shortHint) hintParts.push(shortHint);
|
|
1261
|
+
hintParts.push(baseHint);
|
|
1262
|
+
throw new LingjingAwbCliError(`未知参数:${unknown.map(displayFlag).join(', ')}`, {
|
|
1169
1263
|
type: 'unknown_option',
|
|
1170
1264
|
exitCode: 2,
|
|
1171
|
-
hint,
|
|
1265
|
+
hint: hintParts.join(' '),
|
|
1172
1266
|
details: {
|
|
1173
1267
|
command: command.name,
|
|
1174
|
-
unknownOptions: unknown.map(
|
|
1268
|
+
unknownOptions: unknown.map(displayFlag),
|
|
1175
1269
|
...(Object.keys(suggestions).length ? { suggestions } : {}),
|
|
1176
1270
|
},
|
|
1177
1271
|
});
|
|
@@ -1233,7 +1327,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1233
1327
|
return;
|
|
1234
1328
|
}
|
|
1235
1329
|
|
|
1236
|
-
const { commandName: rawCommandName, kwargs, format } = parseArgv(argv);
|
|
1330
|
+
const { commandName: rawCommandName, kwargs, kwargRawFlags, format } = parseArgv(argv);
|
|
1237
1331
|
const commandName = resolveCommandName(rawCommandName, commands);
|
|
1238
1332
|
const command = commands.find((item) => item.name === commandName);
|
|
1239
1333
|
if (!command) {
|
|
@@ -1257,7 +1351,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1257
1351
|
|
|
1258
1352
|
const startedAt = Date.now();
|
|
1259
1353
|
try {
|
|
1260
|
-
validateCommandOptions(command, kwargs);
|
|
1354
|
+
validateCommandOptions(command, kwargs, kwargRawFlags);
|
|
1261
1355
|
const data = command.virtual === 'schema'
|
|
1262
1356
|
? buildCommandSchema(commands, kwargs, version)
|
|
1263
1357
|
: await command.func({ command }, kwargs);
|
package/skills/lj-awb/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lj-awb
|
|
3
|
-
version: 0.3.
|
|
3
|
+
version: 0.3.18
|
|
4
4
|
description: "灵境 AWB CLI skill。使用 `lj-awb` 命令调用动漫平台 / AWB 云端能力,覆盖认证、项目组、积分、模型发现、上传、统一 create 创建域、任务查询、最终产物 artifact CRUD 与本地 JSON 导入。用户说生图、生视频、主体、音色、素材加白、去字幕、artifact 写入或查询时使用。正式生成、切换项目组、清空认证、artifact 写入等写入或扣费动作前必须确认。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
@@ -43,6 +43,8 @@ LINGJING_AWB_CMD="$(bash "$(dirname "$0")/scripts/resolve-lj-awb-cmd.sh")"
|
|
|
43
43
|
lj-awb schema -f json
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
如果只是不确定某个命令参数,优先读精确契约,例如 `lj-awb schema --domain model --command video-models -f json`。schema 查询必须先返回,再组织业务命令;不要把 schema 查询和猜测命令放进同一批并行调用。
|
|
47
|
+
|
|
46
48
|
如果用户已经给出完整只读命令,且该命令在本 skill 已知范围内,可以直接执行,不必为了形式补跑 schema。命令名、参数名、requiredOptions、safety、workflow.nextActions 都以 schema 为准。旧根域 `image` / `video` / `asset` / `subject` 不存在,不要尝试旧入口。
|
|
47
49
|
|
|
48
50
|
## 能力地图
|
|
@@ -84,7 +86,8 @@ lj-awb schema -f json
|
|
|
84
86
|
- `fee` 是最终估价,不是参数探索工具。不要为多个 quality / duration / ratio / 渠道组合反复跑 fee。
|
|
85
87
|
- 正式图片 / 视频任务要带 `--project-group-no`,除非命令 schema 没有该参数。
|
|
86
88
|
- 命令返回 `nextCommand`、`nextRefSubject`、`nextVoiceArg` 时优先复用返回值,不手拼。
|
|
87
|
-
- compact text
|
|
89
|
+
- compact text 是默认输出;复杂模型命令按 `section:` + 缩进 `key=value` 输出,先按分区读 `summary` / `params` / `resources` / `constraints` / `intents` / `examples` / `next`。只有 schema、options、create-spec、完整嵌套结构或脚本解析时用 `-f json`。
|
|
90
|
+
- 模型口语名命中后必须过“候选展示门”:先把候选模型、真实参数取值和资源能力转成用户可见清单,再推荐或追问;不能只在内部读完 `model options` 就直接代选默认模型 / 参数。
|
|
88
91
|
- 积分口径只把 `billingPointBalance` 当可扣积分余额;`projectBudgetBalance` 是项目组预算,不要混说。
|
|
89
92
|
- 不直连 material / asset / 外部服务;所有业务能力统一通过 `lj-awb`。
|
|
90
93
|
|
package/skills/lj-awb/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.3.
|
|
1
|
+
0.3.18
|