@lingjingai/lj-awb-cli-pre 0.4.5 → 0.4.7
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/package.json +1 -1
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +1 -1
- package/packages/awb-core/src/api.js +13 -3
- package/packages/awb-core/src/commands.js +29 -0
- package/packages/awb-core/src/common.js +12 -0
- package/packages/awb-core/src/output.js +10 -0
- package/packages/awb-core/src/services.js +121 -3
- package/packages/awb-core/src/standalone.js +8 -2
- package/skills/lj-awb/SKILL.md +4 -3
- package/skills/lj-awb/VERSION +1 -1
- package/skills/lj-awb/compat.json +3 -3
- package/skills/lj-awb/modules/account.md +3 -1
- package/skills/lj-awb/modules/credits.md +19 -2
- package/skills/lj-awb/references/error-codes.md +17 -0
- package/skills/lj-awb/references/output-fields.md +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingjingai/lj-awb-cli-pre",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Lingjing AWB CLI monorepo with shared core, standalone CLI, and agent skills (pre-release build pointing to https://animeworkbench-pre.lingjingai.cn)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingjingai/awb-cli-bin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Standalone CLI for Lingjing AWB",
|
|
5
5
|
"private": true,
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@lingjingai/awb-core": "0.4.
|
|
16
|
+
"@lingjingai/awb-core": "0.4.7"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -133,15 +133,25 @@ export async function fetchPoints() {
|
|
|
133
133
|
return await apiFetch('/api/anime/member/benefits/queryGroupPoint', { body: {} });
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
export async function redeemCode(code) {
|
|
137
|
+
// 后端 redeemCode 的 code 是无注解 Spring 参数(按 query / form 绑定,不读 JSON body),
|
|
138
|
+
// 且必须带 userId / groupId 请求头(由 buildHeaders 从环境变量注入)。
|
|
139
|
+
return await apiFetch('/api/anime/member/redemption/redeemCode', {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
query: { code },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
136
145
|
export async function selectProjectGroup(projectGroupNo) {
|
|
137
146
|
return await apiFetch('/api/anime/workbench/projectGroup/setLastProjectGroup', {
|
|
138
147
|
body: { projectGroupNo },
|
|
139
148
|
});
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
export async function
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
export async function fetchGroupAccessKey({ userId, groupId }) {
|
|
152
|
+
// 切换团队:用当前用户的 userId + 目标 groupId 换取该团队的 accessKey,再刷新本地认证文件。
|
|
153
|
+
return await apiFetch('/api/anime/user/account/groupAccessKey', {
|
|
154
|
+
body: { userId, groupId },
|
|
145
155
|
});
|
|
146
156
|
}
|
|
147
157
|
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
authLogin,
|
|
15
15
|
authLogout,
|
|
16
16
|
creditsBalance,
|
|
17
|
+
creditsBuy,
|
|
18
|
+
creditsRedeem,
|
|
17
19
|
creditsUsageSummary,
|
|
18
20
|
createProjectGroupCommand,
|
|
19
21
|
accountInfo,
|
|
@@ -398,6 +400,33 @@ export function registerAwbCommands(cli) {
|
|
|
398
400
|
func: async (_ctx, kwargs) => creditsUsageSummary(kwargs),
|
|
399
401
|
});
|
|
400
402
|
|
|
403
|
+
cli({
|
|
404
|
+
name: 'credits buy',
|
|
405
|
+
description: commandHelp('引导购买 / 充值:按当前环境(预发 / 正式)输出充值页面链接', {
|
|
406
|
+
examples: ['lj-awb credits buy'],
|
|
407
|
+
hint: '只输出充值链接,不发起网络请求;请在浏览器中打开链接完成购买。链接随构建环境写死,预发与正式各自指向对应工作台域名。',
|
|
408
|
+
}),
|
|
409
|
+
args: [],
|
|
410
|
+
func: async () => creditsBuy(),
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
cli({
|
|
414
|
+
name: 'credits redeem',
|
|
415
|
+
description: commandHelp('兑换码兑换:等待输入兑换码并调用平台兑换接口,成功后展示最新积分余额', {
|
|
416
|
+
examples: [
|
|
417
|
+
'lj-awb credits redeem',
|
|
418
|
+
'lj-awb credits redeem --code <code>',
|
|
419
|
+
],
|
|
420
|
+
hint: '不传 --code 时进入交互模式,最长等待约 10 分钟读取兑换码(自动去除空格 / 换行);非交互终端必须用 --code 传入。兑换成功后会顺带展示当前积分余额。',
|
|
421
|
+
}),
|
|
422
|
+
args: [
|
|
423
|
+
{ name: 'code', valueName: 'code', description: '兑换码;不传则交互式等待输入' },
|
|
424
|
+
{ name: 'wait-seconds', valueName: 'seconds', description: '交互模式下等待输入兑换码的最长秒数,默认 600' },
|
|
425
|
+
DRY_RUN_ARG,
|
|
426
|
+
],
|
|
427
|
+
func: async (_ctx, kwargs) => creditsRedeem(kwargs),
|
|
428
|
+
});
|
|
429
|
+
|
|
401
430
|
cli({
|
|
402
431
|
name: 'model image-models',
|
|
403
432
|
description: commandHelp('查询可用生图模型', {
|
|
@@ -11,6 +11,18 @@ export const API_ORIGIN = (
|
|
|
11
11
|
|| process.env.AWB_BASE_URL
|
|
12
12
|
|| DEFAULT_API_ORIGIN
|
|
13
13
|
).replace(/\/+$/, '');
|
|
14
|
+
// 充值 / 购买引导页面链接:随构建环境写死,预发和正式分别指向各自的工作台域名。
|
|
15
|
+
export const PURCHASE_URL_PRE = 'https://animeworkbench-pre.lingjingai.cn/platform/creation?openModal=RechargeModal';
|
|
16
|
+
export const PURCHASE_URL_PROD = 'https://animeworkbench.lingjingai.cn/platform/creation?openModal=RechargeModal';
|
|
17
|
+
|
|
18
|
+
export function resolvePurchaseUrl() {
|
|
19
|
+
const isPre = API_ORIGIN.includes('animeworkbench-pre') || API_ORIGIN.includes('-pre.');
|
|
20
|
+
return {
|
|
21
|
+
environment: isPre ? 'pre' : 'prod',
|
|
22
|
+
purchaseUrl: isPre ? PURCHASE_URL_PRE : PURCHASE_URL_PROD,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
export const APP_HOME_DIR = process.env.LINGJING_AWB_STATE_DIR || process.env.ANIME_STATE_DIR || path.join(os.homedir(), '.lingjingai', 'awb');
|
|
15
27
|
export const AUTH_PATH = process.env.LINGJING_AWB_AUTH_PATH || process.env.ANIME_AUTH_PATH || path.join(APP_HOME_DIR, 'auth.json');
|
|
16
28
|
export const STATE_PATH = process.env.LINGJING_AWB_STATE_PATH || process.env.ANIME_STATE_PATH || path.join(APP_HOME_DIR, 'state.json');
|
|
@@ -1400,6 +1400,8 @@ const OUTPUT_KIND_BY_COMMAND = {
|
|
|
1400
1400
|
'project ensure': 'generic',
|
|
1401
1401
|
'credits balance': 'credits_balance',
|
|
1402
1402
|
'credits usage': 'credits_usage',
|
|
1403
|
+
'credits buy': 'purchase_guide',
|
|
1404
|
+
'credits redeem': 'redeem_result',
|
|
1403
1405
|
'model image-models': 'model_list',
|
|
1404
1406
|
'model video-models': 'model_list',
|
|
1405
1407
|
'model asset-review-models': 'asset_review_model_list',
|
|
@@ -1525,6 +1527,8 @@ const KIND_TITLES = {
|
|
|
1525
1527
|
project_list: '项目组列表',
|
|
1526
1528
|
project_summary: '项目组摘要',
|
|
1527
1529
|
project_user_list: '项目组成员',
|
|
1530
|
+
purchase_guide: '购买引导',
|
|
1531
|
+
redeem_result: '兑换结果',
|
|
1528
1532
|
subject_list: '主体列表',
|
|
1529
1533
|
subject_publish_result: '主体创建结果',
|
|
1530
1534
|
subject_status: '主体状态',
|
|
@@ -1598,6 +1602,9 @@ const FIELD_LABELS = {
|
|
|
1598
1602
|
cleared: '已清空',
|
|
1599
1603
|
loggedOut: '已退出登录',
|
|
1600
1604
|
authPath: '认证文件',
|
|
1605
|
+
purchaseUrl: '购买链接',
|
|
1606
|
+
redeemed: '已兑换',
|
|
1607
|
+
redemptionCode: '兑换码',
|
|
1601
1608
|
clipCount: 'Clip 数',
|
|
1602
1609
|
clipId: 'Clip ID',
|
|
1603
1610
|
command: '命令',
|
|
@@ -1771,6 +1778,8 @@ const DETAIL_FIELDS_BY_KIND = {
|
|
|
1771
1778
|
generic: ['cleared', 'selected', 'created', 'updated', 'reused', 'dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'groupId', 'groupName', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1772
1779
|
local_task_records: ['count', 'taskCount', 'successTaskCount', 'resultCount', 'pointTotal'],
|
|
1773
1780
|
project_summary: ['selected', 'created', 'updated', 'reused', 'projectGroupNo', 'projectGroupName', 'isSelected', 'projectBudgetBalance', 'projectBudgetMax', 'memberCount'],
|
|
1781
|
+
purchase_guide: ['purchaseUrl'],
|
|
1782
|
+
redeem_result: ['redeemed', 'redemptionCode', 'billingPointBalance', 'currentProjectGroupNo', 'currentProjectGroupName', 'projectBudgetBalance', 'projectBudgetMax'],
|
|
1774
1783
|
subject_publish_result: ['created', 'name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'nextRefSubject'],
|
|
1775
1784
|
subject_status: ['name', 'elementId', 'subjectId', 'externalId', 'modelCode', 'status', 'taskStatus', 'errorMessage', 'isTerminal', 'nextRefSubject'],
|
|
1776
1785
|
subject_voice_create_result: ['created', 'name', 'voiceRecordId', 'reqTaskId', 'voiceId', 'externalId', 'status', 'taskStatus', 'errorMessage', 'nextVoiceArg'],
|
|
@@ -1791,6 +1800,7 @@ const DRY_RUN_FIELDS_BY_COMMAND = {
|
|
|
1791
1800
|
'project create': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1792
1801
|
'project update': ['dryRun', 'action', 'projectGroupNo', 'projectGroupName', 'point', 'memberCount'],
|
|
1793
1802
|
'project ensure': ['dryRun', 'action', 'projectGroupName', 'point', 'memberCount', 'note'],
|
|
1803
|
+
'credits redeem': ['dryRun', 'action', 'redemptionCode'],
|
|
1794
1804
|
'upload files': ['dryRun', 'fileCount', 'localFileCount', 'resourceConversionCount'],
|
|
1795
1805
|
'create image': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'ratio', 'quality', 'generateNum', 'resourceCount', 'localFileCount', 'assetCount'],
|
|
1796
1806
|
'create video': ['dryRun', 'action', 'modelGroupCode', 'projectGroupNo', 'prompt', 'duration', 'ratio', 'quality', 'needAudio', 'resourceCount', 'localFileCount', 'assetCount'],
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
nowIso,
|
|
29
29
|
parseJsonArg,
|
|
30
30
|
parseListArg,
|
|
31
|
+
resolvePurchaseUrl,
|
|
31
32
|
safeFileName,
|
|
32
33
|
saveState,
|
|
33
34
|
sleep,
|
|
@@ -471,6 +472,91 @@ export async function accountInfo() {
|
|
|
471
472
|
};
|
|
472
473
|
}
|
|
473
474
|
|
|
475
|
+
const REDEEM_CODE_WAIT_SECONDS_DEFAULT = 600;
|
|
476
|
+
|
|
477
|
+
// 去掉用户粘贴时可能带上的空格 / 制表符 / 换行,避免污染兑换码。
|
|
478
|
+
function sanitizeRedeemCode(value) {
|
|
479
|
+
return String(value ?? '').replace(/\s+/g, '');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export async function creditsBuy() {
|
|
483
|
+
const { purchaseUrl } = resolvePurchaseUrl();
|
|
484
|
+
return {
|
|
485
|
+
action: 'credits buy',
|
|
486
|
+
purchaseUrl,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function promptRedeemCode(waitSeconds) {
|
|
491
|
+
if (process.stdin.isTTY !== true || process.stderr.isTTY !== true) {
|
|
492
|
+
throw argumentError(
|
|
493
|
+
'未提供兑换码',
|
|
494
|
+
'非交互终端不会等待输入,请通过 --code 传入兑换码,例如 lj-awb credits redeem --code <code>。',
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
const controller = new AbortController();
|
|
498
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, waitSeconds) * 1000);
|
|
499
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
500
|
+
try {
|
|
501
|
+
const answer = await rl.question('请输入兑换码后回车确认:', { signal: controller.signal });
|
|
502
|
+
return sanitizeRedeemCode(answer);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
if (controller.signal.aborted) {
|
|
505
|
+
throw new LingjingAwbCliError('等待输入兑换码超时', {
|
|
506
|
+
type: 'redeem_timeout',
|
|
507
|
+
exitCode: 20,
|
|
508
|
+
hint: '重新运行 lj-awb credits redeem 并在限定时间内输入兑换码,或直接用 --code 传入。',
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
throw error;
|
|
512
|
+
} finally {
|
|
513
|
+
clearTimeout(timer);
|
|
514
|
+
rl.close();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export async function creditsRedeem(kwargs = {}) {
|
|
519
|
+
const waitSeconds = Math.max(0, toInt(kwargs.waitSeconds, REDEEM_CODE_WAIT_SECONDS_DEFAULT));
|
|
520
|
+
let code = sanitizeRedeemCode(kwargs.code);
|
|
521
|
+
if (!code) {
|
|
522
|
+
code = await promptRedeemCode(waitSeconds);
|
|
523
|
+
}
|
|
524
|
+
if (!code) {
|
|
525
|
+
throw argumentError('兑换码不能为空', '请提供有效兑换码,或通过 --code <code> 传入。');
|
|
526
|
+
}
|
|
527
|
+
if (toBool(kwargs.dryRun)) {
|
|
528
|
+
return { dryRun: true, action: 'credits redeem', redemptionCode: code };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
await awbApi.redeemCode(code);
|
|
533
|
+
} catch (error) {
|
|
534
|
+
// apiFetch 在平台返回 code != 200 时抛 api_error,并带上平台 msg;统一转成更友好的兑换失败提示。
|
|
535
|
+
if (error instanceof LingjingAwbCliError && (error.type === 'api_error' || error.type === 'http_error')) {
|
|
536
|
+
throw new LingjingAwbCliError(error.message || '兑换失败', {
|
|
537
|
+
type: 'redeem_failed',
|
|
538
|
+
exitCode: 1,
|
|
539
|
+
hint: '兑换处理失败,请确认兑换码是否正确或已被使用;若持续失败请联系平台处理。',
|
|
540
|
+
details: error.details,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
throw error;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 兑换成功后顺带拉取账号概览,展示最新积分余额(等同于 lj-awb account info)。
|
|
547
|
+
const account = await accountInfo().catch(() => null);
|
|
548
|
+
return {
|
|
549
|
+
redeemed: true,
|
|
550
|
+
action: 'credits redeem',
|
|
551
|
+
redemptionCode: code,
|
|
552
|
+
billingPointBalance: account?.billingPointBalance ?? null,
|
|
553
|
+
currentProjectGroupNo: account?.currentProjectGroupNo ?? null,
|
|
554
|
+
currentProjectGroupName: account?.currentProjectGroupName ?? null,
|
|
555
|
+
projectBudgetBalance: account?.projectBudgetBalance ?? null,
|
|
556
|
+
projectBudgetMax: account?.projectBudgetMax ?? null,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
474
560
|
async function pathExists(filePath) {
|
|
475
561
|
if (!filePath) return false;
|
|
476
562
|
try {
|
|
@@ -618,9 +704,41 @@ export async function selectTeam(kwargs = {}) {
|
|
|
618
704
|
action: 'account switch-team',
|
|
619
705
|
groupId,
|
|
620
706
|
});
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
707
|
+
// userId 取当前登录用户(与 account info 同源:fetchUserInfo)
|
|
708
|
+
const userId = normalizeUserInfo(await awbApi.fetchUserInfo()).userId;
|
|
709
|
+
if (!userId) {
|
|
710
|
+
throw new LingjingAwbCliError('无法获取当前用户 userId,切换团队失败', {
|
|
711
|
+
type: 'api_error',
|
|
712
|
+
exitCode: 1,
|
|
713
|
+
hint: '请先确认已登录(lj-awb auth status),再重试。',
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
// 用 userId + 目标 groupId 换取该团队的 accessKey
|
|
717
|
+
const accessKey = trimSecret(await awbApi.fetchGroupAccessKey({ userId, groupId }));
|
|
718
|
+
if (!accessKey) {
|
|
719
|
+
throw new LingjingAwbCliError('切换团队失败:未返回该团队的 accessKey', {
|
|
720
|
+
type: 'api_error',
|
|
721
|
+
exitCode: 1,
|
|
722
|
+
hint: '请确认该 groupId 属于当前用户可访问的团队;若持续失败请联系平台。',
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
// 刷新本地认证文件(等同登录写入),并用新 key 校验,拿到切换后的用户 / 团队上下文
|
|
726
|
+
const saved = await saveVerifiedAccessKey(accessKey, { loginMethod: 'switch-team' });
|
|
727
|
+
const switchedGroupId = saved.user?.groupId ?? groupId;
|
|
728
|
+
await saveState({
|
|
729
|
+
currentGroupId: switchedGroupId,
|
|
730
|
+
currentGroupName: saved.user?.groupName ?? null,
|
|
731
|
+
currentUserId: saved.user?.userId ?? userId,
|
|
732
|
+
}).catch(() => {});
|
|
733
|
+
return {
|
|
734
|
+
selected: true,
|
|
735
|
+
action: 'account switch-team',
|
|
736
|
+
groupId: switchedGroupId,
|
|
737
|
+
groupName: saved.user?.groupName ?? null,
|
|
738
|
+
userId: saved.user?.userId ?? userId,
|
|
739
|
+
userName: saved.user?.userName ?? null,
|
|
740
|
+
accessKey: saved.accessKey,
|
|
741
|
+
};
|
|
624
742
|
}
|
|
625
743
|
|
|
626
744
|
export async function selectProjectGroupCommand(kwargs = {}) {
|
|
@@ -534,6 +534,9 @@ const GROUP_EXAMPLES = {
|
|
|
534
534
|
credits: [
|
|
535
535
|
'lj-awb credits balance',
|
|
536
536
|
'lj-awb credits usage --project-group-no <no> --last-hours 24',
|
|
537
|
+
'lj-awb credits buy',
|
|
538
|
+
'lj-awb credits redeem',
|
|
539
|
+
'lj-awb credits redeem --code <code>',
|
|
537
540
|
],
|
|
538
541
|
model: [
|
|
539
542
|
'lj-awb model image-models --model Banana',
|
|
@@ -734,6 +737,7 @@ const REMOTE_WRITE_COMMANDS = new Set([
|
|
|
734
737
|
'project create',
|
|
735
738
|
'project update',
|
|
736
739
|
'project ensure',
|
|
740
|
+
'credits redeem',
|
|
737
741
|
'upload files',
|
|
738
742
|
'create image',
|
|
739
743
|
'create image-batch',
|
|
@@ -761,8 +765,8 @@ const LOCAL_STATE_WRITE_COMMANDS = new Set([
|
|
|
761
765
|
]);
|
|
762
766
|
|
|
763
767
|
const DESTRUCTIVE_COMMANDS = new Set(['auth clear', ...ARTIFACT_DELETE_COMMANDS]);
|
|
764
|
-
const LONG_RUNNING_COMMANDS = new Set(['auth login', 'task wait', 'task record-poll', 'create subject-wait', 'create subject-voice-wait']);
|
|
765
|
-
const NETWORK_NONE_COMMANDS = new Set(['schema', 'auth status', 'auth clear', 'auth logout', 'model input-guide', 'task records']);
|
|
768
|
+
const LONG_RUNNING_COMMANDS = new Set(['auth login', 'credits redeem', 'task wait', 'task record-poll', 'create subject-wait', 'create subject-voice-wait']);
|
|
769
|
+
const NETWORK_NONE_COMMANDS = new Set(['schema', 'auth status', 'auth clear', 'auth logout', 'credits buy', 'model input-guide', 'task records']);
|
|
766
770
|
const NETWORK_CONDITIONAL_COMMANDS = new Set(['doctor', 'auth login']);
|
|
767
771
|
|
|
768
772
|
const OUTPUT_KIND_BY_COMMAND = {
|
|
@@ -780,6 +784,8 @@ const OUTPUT_KIND_BY_COMMAND = {
|
|
|
780
784
|
'project users': 'project_user_list',
|
|
781
785
|
'credits balance': 'credits_balance',
|
|
782
786
|
'credits usage': 'credits_usage',
|
|
787
|
+
'credits buy': 'purchase_guide',
|
|
788
|
+
'credits redeem': 'redeem_result',
|
|
783
789
|
'model image-models': 'model_list',
|
|
784
790
|
'model video-models': 'model_list',
|
|
785
791
|
'model asset-review-models': 'asset_review_model_list',
|
package/skills/lj-awb/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lj-awb
|
|
3
|
-
version: 0.4.
|
|
4
|
-
description: "灵境 AWB CLI skill。使用 `lj-awb` 命令调用动漫平台 / AWB
|
|
3
|
+
version: 0.4.7
|
|
4
|
+
description: "灵境 AWB CLI skill。使用 `lj-awb` 命令调用动漫平台 / AWB 云端能力,覆盖认证、项目组、积分(含充值购买与兑换码兑换)、模型发现、上传、统一 create 创建域、任务查询、视频超分、最终产物 artifact CRUD 与本地 JSON 导入。用户说生图、生视频、视频超分、主体、音色、素材加白、去字幕、充值、购买积分、兑换码、artifact 写入或查询时使用。正式生成、切换项目组、清空认证、兑换码、artifact 写入等写入或扣费动作前必须确认。"
|
|
5
5
|
metadata:
|
|
6
6
|
bootstrap:
|
|
7
7
|
package: "@lingjingai/lj-awb-cli"
|
|
8
|
-
version: "0.4.
|
|
8
|
+
version: "0.4.7"
|
|
9
9
|
bin: "lj-awb"
|
|
10
10
|
cliHelp: "lj-awb --help"
|
|
11
11
|
---
|
|
@@ -84,6 +84,7 @@ schema 查询必须先返回,再组织业务命令;不要把 schema 查询
|
|
|
84
84
|
| 用户意图 | 先走 |
|
|
85
85
|
|----------|------|
|
|
86
86
|
| “能不能跑 / 认证 / 当前项目 / 余额” | `doctor --verify`,再按需 `account` / `project` / `credits` |
|
|
87
|
+
| “充值 / 购买积分 / 积分不够了 / 兑换码” | [`modules/credits.md`](modules/credits.md):购买走 `credits buy` 给链接,兑换走 `credits redeem`(非交互用 `--code <code>`)|
|
|
87
88
|
| “有哪些模型 / 推荐模型 / 用某某模型” | [`modules/model.md`](modules/model.md),模型口语名默认是平台模型,不是本地工具 |
|
|
88
89
|
| “生图 / 生成图片 / 参考图生图” | [`modules/driver.md`](modules/driver.md) 创作链条 + [`modules/create-contract.md`](modules/create-contract.md) |
|
|
89
90
|
| “生视频 / 首帧 / 首尾帧 / 音频参考 / 让图动起来” | [`modules/driver.md`](modules/driver.md) 创作链条 + [`modules/task-manual.md`](modules/task-manual.md) |
|
package/skills/lj-awb/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.4.
|
|
1
|
+
0.4.7
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| `lj-awb account info` | 当前用户、团队、项目组、积分摘要(启动后第一条信息) |
|
|
10
10
|
| `lj-awb account teams` | 列出当前账号可访问的团队 |
|
|
11
11
|
| `lj-awb account switch-team --group-id <id> --dry-run` | 预览切换团队 |
|
|
12
|
-
| `lj-awb account switch-team --group-id <id> --yes` |
|
|
12
|
+
| `lj-awb account switch-team --group-id <id> --yes` | 切换团队(换取该团队 accessKey 并刷新本地登录态) |
|
|
13
13
|
|
|
14
14
|
## 什么时候切团队
|
|
15
15
|
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
## 规则
|
|
26
26
|
|
|
27
27
|
- `account switch-team` 改变团队上下文,**必须确认**后追加 `--yes`。
|
|
28
|
+
- 切换原理:用当前用户 `userId` + 目标 `groupId` 调 `groupAccessKey` 换到该团队的 accessKey,再**写回本地认证文件**(等同重新登录到该团队)。
|
|
29
|
+
- 若用户是通过 `LINGJING_AWB_ACCESS_KEY` 环境变量登录的,写回文件**不生效**(env key 优先级高于文件);这种情况下要么 `unset LINGJING_AWB_ACCESS_KEY` 后再切,要么直接换该团队的 access key。
|
|
28
30
|
- 切换团队会**重置项目组上下文**:之后跑 `project current` 必然为空,需要让用户重新 `project use --project-group-no <no>` 选一个。
|
|
29
31
|
- 不要把 `account info.groupName`(团队名)当作项目组名报告给用户。
|
|
30
32
|
- 输出字段速查见 [`../references/output-fields.md`](../references/output-fields.md) 的"账号与项目组"小节。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Credits Module
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
积分模块用于预算确认、项目用量统计,以及积分的购买(充值)与兑换码兑换。
|
|
4
4
|
|
|
5
5
|
## 命令
|
|
6
6
|
|
|
@@ -10,6 +10,23 @@
|
|
|
10
10
|
| `lj-awb credits balance --project-group-no <no>` | 查看可扣积分余额和指定项目组预算 |
|
|
11
11
|
| `lj-awb credits usage --project-group-no <no> --last-hours 24` | 汇总最近任务消耗 |
|
|
12
12
|
| `lj-awb credits usage --project-group-no <no> --task-types IMAGE_CREATE,VIDEO_GROUP --include-tasks` | 汇总并附带任务明细 |
|
|
13
|
+
| `lj-awb credits buy` | 引导购买 / 充值:输出充值页面链接,让用户在浏览器打开完成购买(链接已自动匹配当前环境,无需告知用户环境)|
|
|
14
|
+
| `lj-awb credits redeem` | 兑换码兑换:交互式等待用户输入兑换码,兑换成功后展示最新积分余额 |
|
|
15
|
+
| `lj-awb credits redeem --code <code>` | 直接用 `--code` 传入兑换码,跳过交互(脚本 / 非交互终端必须这样用)|
|
|
16
|
+
|
|
17
|
+
## 购买(充值)
|
|
18
|
+
|
|
19
|
+
- `credits buy` **只输出链接、不发起网络请求**,输出里只有一个 `purchaseUrl`,已自动匹配当前 CLI 所在环境。
|
|
20
|
+
- **直接把 `purchaseUrl` 给用户即可**:不要向用户提及 / 区分"预发""正式"环境,不要主动追问或提供"另一个环境的链接",也不要展示 API 地址等环境细节——对用户而言只有"充值链接"这一个概念。
|
|
21
|
+
- CLI 无法代用户完成支付,把链接交给用户在浏览器打开即可;不要承诺"已充值"。完成后可建议 `credits balance` 查看余额。
|
|
22
|
+
|
|
23
|
+
## 兑换码
|
|
24
|
+
|
|
25
|
+
- 不传 `--code` 时进入交互模式,最长等待约 **10 分钟**(可用 `--wait-seconds` 调)读取用户输入;输入会**自动去除空格 / 换行 / 制表符**,避免粘贴时带上多余字符。
|
|
26
|
+
- **非交互终端 / AI agent 不会挂起**:未提供 `--code` 时直接报 `argument_error`,必须用 `--code <兑换码>` 传入。
|
|
27
|
+
- 兑换是会改账户积分的远端写操作(`safeToAutoRun=false`):执行前向用户确认兑换码;支持 `--dry-run` 预览将要提交的兑换码。
|
|
28
|
+
- 平台接口 `code != 200` 即视为失败,CLI 抛 `redeem_failed`(保留平台原始 `msg`),提示用户确认兑换码或联系平台处理。
|
|
29
|
+
- 兑换成功后会顺带调用 `account info` 拉取并展示最新 `billingPointBalance`,无需再单独跑 `credits balance`。
|
|
13
30
|
|
|
14
31
|
## 规则
|
|
15
32
|
|
|
@@ -25,4 +42,4 @@
|
|
|
25
42
|
## 下一步
|
|
26
43
|
|
|
27
44
|
- 积分够 → 进 [`model.md`](model.md) 选模型,或直接 [`image.md`](image.md) / [`video.md`](video.md) 估价。
|
|
28
|
-
- 积分不够 →
|
|
45
|
+
- 积分不够 → 用 `credits buy` 给出充值链接,或 `credits redeem` 走兑换码;不要默认换更便宜模型,先问用户偏好。
|
|
@@ -16,6 +16,7 @@ CLI 失败时 envelope 形如:
|
|
|
16
16
|
| `1` | `runtime_error` / `api_error` | 平台业务失败或一般运行异常 | 看 `message` / `details`;不要自动重试相同请求 |
|
|
17
17
|
| `1` | `subject_failed` | 主体创建任务已失败 | 看 `details.errorMessage`,调整参考资源 / 模型后重新创建 |
|
|
18
18
|
| `1` | `subject_voice_failed` | 主体音色创建任务已失败 | 看 `details.errorMessage`,调整音频 / 视频来源后重新创建 |
|
|
19
|
+
| `1` | `redeem_failed` | 兑换码兑换失败(平台 `code != 200`) | 看 `message`(平台原始提示),确认兑换码是否正确 / 已被使用;持续失败联系平台处理 |
|
|
19
20
|
| `2` | `argument_error` | 必填参数缺失、参数格式 / 枚举不合法 | 按 `hint` 补参数;用 `lj-awb schema --command <name> -f json` 查 `requiredOptions / requiredAnyOptions` |
|
|
20
21
|
| `2` | `unknown_command` / `unknown_option` | 命令名 / 参数名打错 | 跑 `lj-awb -h` 或对应子域 `lj-awb <domain> -h` 重查命令 |
|
|
21
22
|
| `3` | `auth_required` | 缺少 access key | 提示用户 `lj-awb auth login --access-key <key>` 或设置 `LINGJING_AWB_ACCESS_KEY` |
|
|
@@ -28,6 +29,7 @@ CLI 失败时 envelope 形如:
|
|
|
28
29
|
| `20` | `subject_still_pending` | 主体 externalId 尚未回填 | 续跑 `create subject-wait --element-id ... --wait-seconds <n>` |
|
|
29
30
|
| `20` | `subject_voice_still_pending` | 主体音色 externalId 尚未回填 | 续跑 `create subject-voice-wait --voice-record-id ... --wait-seconds <n>` |
|
|
30
31
|
| `20` | `auth_flow_pending` | 浏览器授权等待窗口结束但用户尚未授权 | 用 `details.flowId` 续跑 `auth login --flow-id <flowId>`,**不要重发新 login**,**不代表失败** |
|
|
32
|
+
| `20` | `redeem_timeout` | 交互式等待输入兑换码超时(默认约 10 分钟) | 重新跑 `credits redeem` 并及时输入,或直接 `credits redeem --code <code>` 传入 |
|
|
31
33
|
| `30` | `network_error` | TLS / DNS / 连接失败 / 平台 5xx | 检查网络;macOS 证书问题用 `lj-awb`(shell wrapper)而不是直接 `node bin/lj-awb.js` |
|
|
32
34
|
| `30` | `http_error` | 非 401 的 HTTP 错误(4xx / 5xx) | 看 `message`;429 等限流时退避重试 |
|
|
33
35
|
| `30` | `upload_failed` | COS 上传失败 | 重试一次;持续失败检查文件大小 / mimeType / 网络 |
|
|
@@ -131,3 +133,18 @@ AI agent / 无法实时看输出时的标准流程:
|
|
|
131
133
|
lj-awb auth login --no-wait --json # 拿 flowId + verifyUrl,发给用户去浏览器授权
|
|
132
134
|
lj-awb auth login --flow-id <flowId> # 用户授权后续轮询,拿到并保存 accessKey
|
|
133
135
|
```
|
|
136
|
+
|
|
137
|
+
### 场景 10:兑换码兑换(redeem_*)
|
|
138
|
+
|
|
139
|
+
`credits redeem` 的几种结果:
|
|
140
|
+
|
|
141
|
+
- `redeem_failed`(exit 1):平台返回 `code != 200`,兑换没成功。`message` 是平台原始提示,如实复述给用户,让其确认兑换码是否正确 / 已被使用;持续失败建议联系平台处理。**不要自动重试相同兑换码**。
|
|
142
|
+
- `redeem_timeout`(exit 20):交互式等待输入超时(默认约 10 分钟)。重新发起并提醒用户及时输入。
|
|
143
|
+
|
|
144
|
+
AI agent / 非交互终端**不要裸跑 `credits redeem`**(会因为无法读取输入直接报 `argument_error`),应让用户提供兑换码后用 `--code` 传入:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
lj-awb credits redeem --code <code> # 兑换成功后会顺带展示最新积分余额
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
兑换是会改账户积分的写操作,执行前先与用户确认;可先 `--dry-run` 预览将要提交的兑换码。
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
|------------|------|-----------|--------------|--------|
|
|
32
32
|
| `credits_balance` | `credits balance` | `billingPointBalance`、`projectBudgetBalance`、`projectBudgetMax` | — | `currentProjectGroupNo` |
|
|
33
33
|
| `credits_usage` | `credits usage` | `pointTotal`、`scannedTaskCount`、`buckets[]` 汇总 | `tasks[]`(若 `--include-tasks`) | sinceMs/untilMs 原值 |
|
|
34
|
+
| `purchase_guide` | `credits buy` | `purchaseUrl` | — | — |
|
|
35
|
+
| `redeem_result` | `credits redeem` | `redeemed`、`billingPointBalance` | `redemptionCode` | `currentProjectGroupNo` |
|
|
34
36
|
|
|
35
37
|
> 永远优先看 `billingPointBalance`(可扣积分);`projectBudgetBalance` 是项目预算 / ROI 约束,不要混为一谈。
|
|
36
38
|
|