museav-cli 2.4.0 → 2.5.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/AGENTS.md +9 -1
- package/CHANGELOG.md +11 -0
- package/README.md +27 -0
- package/dist/client.d.ts +7 -2
- package/dist/client.js +9 -3
- package/dist/commands/projects.js +4 -1
- package/dist/commands/templates.d.ts +7 -2
- package/dist/commands/templates.js +26 -3
- package/dist/commands/whoami.d.ts +7 -1
- package/dist/commands/whoami.js +15 -10
- package/dist/index.js +3 -0
- package/package.json +1 -1
- package/src/client.ts +11 -3
- package/src/commands/projects.ts +6 -1
- package/src/commands/templates.ts +26 -6
- package/src/commands/whoami.ts +40 -13
- package/src/index.ts +3 -0
package/AGENTS.md
CHANGED
|
@@ -64,8 +64,14 @@ museav upload ./face.png
|
|
|
64
64
|
# compress: resize/re-encode via sharp. Output <name>-min.<fmt>, never overwrites input.
|
|
65
65
|
# remove-bg: ISNet via onnxruntime-node → alpha PNG (~170MB model auto-downloaded
|
|
66
66
|
# to ~/.museav-models on first use; %USERPROFILE%\.museav-models on Windows).
|
|
67
|
+
# upscale: Real-ESRGAN via Vulkan GPU → 2M image becomes 10M+ (~65MB engine+model
|
|
68
|
+
# auto-downloaded once to ~/.museav-bin + ~/.museav-models).
|
|
69
|
+
# remove-watermark: pixel-heuristic auto-locate (zero model deps) + LaMa inpainting
|
|
70
|
+
# → clean PNG (~200MB model on-demand, freed after run).
|
|
67
71
|
museav compress ./photo.jpg --max-edge 800 --format webp --quality 70
|
|
68
72
|
museav remove-bg ./shoe.png # stdout: path to <name>-nobg.png
|
|
73
|
+
museav upscale ./photo.jpg --scale 4 # stdout: path to <name>-4x.png
|
|
74
|
+
museav remove-watermark ./poster.jpg # stdout: path to <name>-clean.png
|
|
69
75
|
|
|
70
76
|
# List your own (or, if using a tenant apiKey, your tenant's) recent jobs as JSON
|
|
71
77
|
museav jobs --limit 10 --status failed
|
|
@@ -81,7 +87,9 @@ museav products
|
|
|
81
87
|
museav assets
|
|
82
88
|
|
|
83
89
|
# Check who you're logged in as and whether the account is affiliated with a tenant
|
|
84
|
-
#
|
|
90
|
+
# Works for both personal login and apiKey (platform account or tenant). For apiKey:
|
|
91
|
+
# platform-account → 账户: nickname + 邮箱 + 累计出图 + credits
|
|
92
|
+
# tenant → 租户: tenant_id + name + logo
|
|
85
93
|
museav whoami
|
|
86
94
|
```
|
|
87
95
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.0 (2026-08-17)
|
|
4
|
+
|
|
5
|
+
### 修复
|
|
6
|
+
- **`whoami` 不再拒绝 apiKey 模式**:之前会直接抛错「只支持个人 login 身份」。实际上 `/api/me` 对两种 apiKey 都返回真实数据——
|
|
7
|
+
- 平台账户 apiKey(`STUDIO_API_KEY` 指向 `accounts.sk-*`)→ 账户身份:邮箱 + nickname + 累计出图次数 + credits
|
|
8
|
+
- 租户 apiKey(指向 `api_tenants.tenant_key`)→ 业务身份:tenant_id + name + nickname + logo
|
|
9
|
+
修复后三种身份都能 `museav whoami` 查清楚当前是谁、关联到哪个租户。
|
|
10
|
+
|
|
11
|
+
### 文档
|
|
12
|
+
- AGENTS.md `whoami` 说明:去掉「apiKey 不可用」的旧断言。
|
|
13
|
+
|
|
3
14
|
## 2.4.0 · 2026-08-17
|
|
4
15
|
|
|
5
16
|
**本地图像工具箱补全(免登录、零成本、macOS/Windows 通用)。** 均为轻量级工具,无大模型常驻内存。
|
package/README.md
CHANGED
|
@@ -85,6 +85,27 @@
|
|
|
85
85
|
|
|
86
86
|
两条路径互不依赖,选跟你身份匹配的那条,不用两个都配。下面「快速开始」走的是平台用户(login)这条路;如果你是租户,直接跳到下面「B 端 / CI 场景」那一节。
|
|
87
87
|
|
|
88
|
+
## 能力速览(小白先看这个)
|
|
89
|
+
|
|
90
|
+
`museav` 能干什么,一句话一组:
|
|
91
|
+
|
|
92
|
+
| 分类 | 能力 | 命令 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| **AI 生成**(中台) | 文字出图 / 垫图出图 / 技能出图 / 模板出图 | `gen`(含 `--ref` 垫图、`--transparent` 透明底) |
|
|
95
|
+
| | 文生视频 / 图生视频 | `gen --video` |
|
|
96
|
+
| | 从已有图片反推提示词 | `reverse` |
|
|
97
|
+
| | 一张图做成可复用模板 | `image-to-template` |
|
|
98
|
+
| | 建/查图片模板、视频模板 | `templates` / `video-templates` |
|
|
99
|
+
| **项目管理**(中台) | 工作区(项目)+ 每个项目自己的素材库 | `projects`、`projects assets` |
|
|
100
|
+
| | 素材直链垫图、作品归档进项目 | `gen --project`、`jobs --project` |
|
|
101
|
+
| **本地图像处理**(免登录/免费/离线) | 压缩图片 | `compress` |
|
|
102
|
+
| | 抠图去背景(输出透明 PNG) | `remove-bg` |
|
|
103
|
+
| | **放大清晰度**(2M → 10M+ 级) | `upscale` |
|
|
104
|
+
| | 去水印 | `remove-watermark` |
|
|
105
|
+
| **素材与统计** | 上传素材 / 查任务 / 查余额 / 查模型 / 查身份 | `upload` / `jobs` / `balance` / `models` / `whoami` |
|
|
106
|
+
|
|
107
|
+
> 本地工具(`compress` / `remove-bg` / `upscale` / `remove-watermark`)**不用登录、不花一分钱**,装了就能用;其余命令需要一个凭证(个人 `login` 或租户 apiKey)。
|
|
108
|
+
|
|
88
109
|
---
|
|
89
110
|
|
|
90
111
|
## 快速开始
|
|
@@ -193,6 +214,10 @@ curl -o poster.png "$URL"
|
|
|
193
214
|
# 先查有哪些模板(自己租户建的 + 平台共享的)
|
|
194
215
|
museav templates
|
|
195
216
|
museav templates --category 电商白底图 # 按分类过滤
|
|
217
|
+
museav templates --mine # 只看我这个人建的
|
|
218
|
+
museav templates --tenant # 只看本租户专属的
|
|
219
|
+
museav templates --platform # 只看平台共享的
|
|
220
|
+
museav templates --type image # 按类型过滤(image / article)
|
|
196
221
|
|
|
197
222
|
# 没有占位符的模板,直接用
|
|
198
223
|
museav gen --template <模板id>
|
|
@@ -476,6 +501,8 @@ console.log(r.sculpt.light) // 光影分析
|
|
|
476
501
|
| `upload <file>` | 上传素材(图片/音频/视频) | 公网直链 |
|
|
477
502
|
| `compress <file>` | 本地压缩图片(免登录,`--max-edge`/`--quality`/`--format`) | 产物路径 |
|
|
478
503
|
| `remove-bg <file>` | 本地抠图去背景(免登录,输出带 alpha 的 PNG) | 产物路径 |
|
|
504
|
+
| `upscale <file>` | 本地超分放大,2M 图变 10M+(免登录,Real-ESRGAN GPU,`--scale 2/3/4`) | 产物路径 |
|
|
505
|
+
| `remove-watermark <file>` | 本地去水印(免登录,自动定位 + LaMa 修复,`--mask` 手工兜底) | 产物路径 |
|
|
479
506
|
| `projects` | 工作区(项目)列表:一账户多项目,各带自己的素材库 | 项目 id 列表 |
|
|
480
507
|
| `projects assets --project` | 项目素材库:ls / add / rm(垫图母版,人像库/产品库各管各的) | `id<TAB>url` 行 |
|
|
481
508
|
| `models` | 可用模型 | 模型名列表 |
|
package/dist/client.d.ts
CHANGED
|
@@ -71,6 +71,10 @@ export interface TemplateOption {
|
|
|
71
71
|
sample_cover_image?: string | null;
|
|
72
72
|
/** 归属:自己租户建的 vs 平台共享的(tenant_id 为空) */
|
|
73
73
|
tenant_id: string | null;
|
|
74
|
+
/** 中台下发的归属标记:mine=本租户建的 / platform=平台共享 / personal=我这个人建的 */
|
|
75
|
+
source?: 'mine' | 'platform' | 'personal';
|
|
76
|
+
/** 创建人(平台管理员个人建的模板会带邮箱;租户建的为 null) */
|
|
77
|
+
created_by?: string | null;
|
|
74
78
|
generation_configs: Array<{
|
|
75
79
|
model: string;
|
|
76
80
|
prompt_template: string;
|
|
@@ -319,8 +323,9 @@ export declare class StudioClient {
|
|
|
319
323
|
/** 可用技能清单:私有 + 所属租户专属模板 + 公共库,服务端已按调用者权限过滤 */
|
|
320
324
|
skills(): Promise<SkillOption[]>;
|
|
321
325
|
/** 可用图片/文字模板清单:自己租户建的 + 平台共享的,服务端已按调用者权限过滤。
|
|
322
|
-
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
323
|
-
|
|
326
|
+
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
327
|
+
* source=mine|platform|personal|all(默认 all;mine=本租户,platform=平台共享,personal=我这个人建的)。 */
|
|
328
|
+
templates(type?: 'image' | 'article', source?: 'mine' | 'platform' | 'personal' | 'all'): Promise<TemplateOption[]>;
|
|
324
329
|
/** 视频模板清单(POST /api/videos 用 template_id)。结构同图片模板的 generation_configs 形态 */
|
|
325
330
|
videoTemplates(): Promise<TemplateOption[]>;
|
|
326
331
|
/** 新建图片模板。归属(是否关联租户)由服务端根据鉴权身份决定,见 CreateTemplateInput 注释 */
|
package/dist/client.js
CHANGED
|
@@ -90,9 +90,15 @@ export class StudioClient {
|
|
|
90
90
|
return Array.isArray(r) ? r : [];
|
|
91
91
|
}
|
|
92
92
|
/** 可用图片/文字模板清单:自己租户建的 + 平台共享的,服务端已按调用者权限过滤。
|
|
93
|
-
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
94
|
+
* source=mine|platform|personal|all(默认 all;mine=本租户,platform=平台共享,personal=我这个人建的)。 */
|
|
95
|
+
async templates(type, source) {
|
|
96
|
+
const params = new URLSearchParams();
|
|
97
|
+
if (type)
|
|
98
|
+
params.set('type', type);
|
|
99
|
+
if (source && source !== 'all')
|
|
100
|
+
params.set('source', source);
|
|
101
|
+
const qs = params.toString() ? `?${params}` : '';
|
|
96
102
|
const r = await this.request(`templates${qs}`);
|
|
97
103
|
return Array.isArray(r) ? r : [];
|
|
98
104
|
}
|
|
@@ -20,9 +20,12 @@ export async function projects(client) {
|
|
|
20
20
|
process.stderr.write('还没有工作区(museav projects create --name 新建)\n');
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
+
// 每个项目的素材数(模板不挂项目,这里统计的是项目素材库,不是模板)
|
|
24
|
+
const assetCounts = await Promise.all(list.map(async (w) => ({ id: w.id, n: (await client.workspaceAssets(w.id).catch(() => [])).length })));
|
|
25
|
+
const assetOf = Object.fromEntries(assetCounts.map((x) => [x.id, x.n]));
|
|
23
26
|
process.stderr.write(`工作区(${list.length} 个):\n`);
|
|
24
27
|
for (const w of list) {
|
|
25
|
-
process.stderr.write(` ${w.id} ${w.name.padEnd(16)} 出图 ${w.gen_done ?? 0}/${w.gen_total ?? 0}${w.brand ? ` brand:${w.brand}` : ''}\n`);
|
|
28
|
+
process.stderr.write(` ${w.id} ${w.name.padEnd(16)} 出图 ${w.gen_done ?? 0}/${w.gen_total ?? 0} 素材 ${assetOf[w.id] ?? 0}${w.brand ? ` brand:${w.brand}` : ''}\n`);
|
|
26
29
|
}
|
|
27
30
|
process.stderr.write(`\n素材库: museav projects assets --project <id|名>\n出图归档: museav gen --project <id|名> ...\n`);
|
|
28
31
|
// stdout 只出 id,便于脚本解析
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
/** museav templates ——
|
|
2
|
-
* --type image|article
|
|
1
|
+
/** museav templates —— 查可用图片/文字模板。
|
|
2
|
+
* --type image|article 按类型过滤
|
|
3
|
+
* --mine 只看本租户建的;--platform 只看平台共享的;都不传则全部列出
|
|
4
|
+
* --category 按分类过滤 */
|
|
3
5
|
import type { StudioClient } from '../client.js';
|
|
4
6
|
export declare function templates(client: StudioClient, opts?: {
|
|
5
7
|
category?: string;
|
|
6
8
|
type?: string;
|
|
9
|
+
mine?: boolean;
|
|
10
|
+
tenant?: boolean;
|
|
11
|
+
platform?: boolean;
|
|
7
12
|
}): Promise<void>;
|
|
8
13
|
interface CreateTemplateOpts {
|
|
9
14
|
name: string;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
export async function templates(client, opts = {}) {
|
|
2
|
-
|
|
2
|
+
const type = opts.type === 'image' || opts.type === 'article' ? opts.type : undefined;
|
|
3
|
+
// 三个归属维度都走服务端 source 参数(正式 API,不再客户端猜):
|
|
4
|
+
// --mine → source=personal(created_by = 当前账户邮箱,我这个人建的)
|
|
5
|
+
// --tenant → source=mine(本租户专属)
|
|
6
|
+
// --platform → source=platform(平台共享)
|
|
7
|
+
let list;
|
|
8
|
+
if (opts.mine) {
|
|
9
|
+
list = await client.templates(type, 'personal');
|
|
10
|
+
}
|
|
11
|
+
else if (opts.tenant) {
|
|
12
|
+
list = await client.templates(type, 'mine');
|
|
13
|
+
}
|
|
14
|
+
else if (opts.platform) {
|
|
15
|
+
list = await client.templates(type, 'platform');
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
list = await client.templates(type);
|
|
19
|
+
}
|
|
3
20
|
if (opts.category) {
|
|
4
21
|
const kw = opts.category.toLowerCase();
|
|
5
22
|
list = list.filter((t) => (t.category || '').toLowerCase().includes(kw));
|
|
@@ -8,7 +25,13 @@ export async function templates(client, opts = {}) {
|
|
|
8
25
|
process.stderr.write(opts.category ? `没有匹配「${opts.category}」的模板\n` : '没有可用模板\n');
|
|
9
26
|
return;
|
|
10
27
|
}
|
|
11
|
-
const tag = (t) =>
|
|
28
|
+
const tag = (t) => {
|
|
29
|
+
if (t.source === 'personal')
|
|
30
|
+
return '[个人]';
|
|
31
|
+
if (t.tenant_id)
|
|
32
|
+
return '[租户]';
|
|
33
|
+
return '[平台]';
|
|
34
|
+
};
|
|
12
35
|
const typeTag = (t) => (t.template_type === 'article' ? '[文字]' : t.template_type === 'image' ? '[图片]' : '');
|
|
13
36
|
process.stderr.write(`可用模板(${list.length} 个):\n`);
|
|
14
37
|
for (const t of list) {
|
|
@@ -19,7 +42,7 @@ export async function templates(client, opts = {}) {
|
|
|
19
42
|
process.stderr.write(` ${t.id.padEnd(38)} ${(t.zh_name || '').padEnd(16)} ${(t.category || '').padEnd(10)} ${(t.ratio || '').padEnd(6)} ${typeTag(t).padEnd(8)} ${fieldHint.padEnd(20)} ${tag(t)}\n`);
|
|
20
43
|
}
|
|
21
44
|
process.stderr.write(`\n出图: museav gen --template <模板id> [--fields '{"key":"值"}']\n`);
|
|
22
|
-
process.stderr.write(
|
|
45
|
+
process.stderr.write(`筛选: --mine(我建的) --tenant(本租户) --platform(平台共享) --type image|article --category <分类>\n`);
|
|
23
46
|
// stdout 只出 id,便于脚本与 agent 解析
|
|
24
47
|
console.log(list.map((t) => t.id).join('\n'));
|
|
25
48
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
/** museav whoami ——
|
|
1
|
+
/** museav whoami —— 查当前身份 + 关联信息。
|
|
2
|
+
*
|
|
3
|
+
* apiKey 模式下也支持(2026-08-17 修复):服务端 /api/me 对两种 apiKey 都返回真实数据——
|
|
4
|
+
* - 平台账户 apiKey(STUDIO_API_KEY 指向 accounts.sk-*) → 走个人身份,含 credits / gen 统计
|
|
5
|
+
* - 租户 apiKey(指向 api_tenants.tenant_key) → 走业务身份,只下发 tenant 信息
|
|
6
|
+
* 个人 login(museav login)走完整 me,输出不变。
|
|
7
|
+
*/
|
|
2
8
|
import type { StudioClient } from '../client.js';
|
|
3
9
|
export declare function whoami(client: StudioClient): Promise<void>;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { loadConfig } from '../config.js';
|
|
2
1
|
export async function whoami(client) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
const me = (await client.me());
|
|
3
|
+
if (me.identity === 'tenant') {
|
|
4
|
+
const t = me.tenant;
|
|
5
|
+
process.stderr.write(`身份: 租户 API Key\n`);
|
|
6
|
+
process.stderr.write(`租户: ${t.nickname || t.name}(${t.name})\n`);
|
|
7
|
+
process.stderr.write(`tenant_id: ${t.id}\n`);
|
|
8
|
+
if (t.logo)
|
|
9
|
+
process.stderr.write(`logo: ${t.logo}\n`);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
process.stderr.write(`账户: ${me.nickname || me.email}(${me.email})\n`);
|
|
13
|
+
process.stderr.write(me.brand ? `业务系统: ${me.brand.name}\n` : `身份: 平台账户 API Key\n`);
|
|
14
|
+
process.stderr.write(`出图: 累计 ${me.gen_total ?? 0} 次,成功 ${me.gen_done ?? 0} 次\n`);
|
|
15
|
+
if (me.credits != null)
|
|
16
|
+
process.stderr.write(`credits: ${me.credits}\n`);
|
|
8
17
|
}
|
|
9
|
-
const me = await client.me();
|
|
10
|
-
process.stderr.write(`账户: ${me.nickname || me.email}(${me.email})\n`);
|
|
11
|
-
process.stderr.write(me.brand ? `业务系统: ${me.brand.name}\n` : '业务系统: 未接入(个人用户)\n');
|
|
12
|
-
process.stderr.write(`出图: 累计 ${me.gen_total} 次,成功 ${me.gen_done} 次\n`);
|
|
13
18
|
console.log(JSON.stringify(me));
|
|
14
19
|
}
|
package/dist/index.js
CHANGED
|
@@ -262,6 +262,9 @@ const templatesCmd = program
|
|
|
262
262
|
.description('查可用图片/文字模板:自己租户建的 + 平台共享的(跟技能是两套不同的机制,见 gen --template)')
|
|
263
263
|
.option('--category <name>', '按分类过滤,如 电商白底图 / 演唱会')
|
|
264
264
|
.option('--type <type>', '按类型过滤:image(图片) / article(文字),不传则两类都列并标注')
|
|
265
|
+
.option('--mine', '只看我这个人建的模板(created_by 是我,排除系统种子和租户专属)')
|
|
266
|
+
.option('--tenant', '只看本租户建的模板')
|
|
267
|
+
.option('--platform', '只看平台共享的模板')
|
|
265
268
|
.action(withClient((client, opts) => templates(client, opts)));
|
|
266
269
|
const videoTemplatesCmd = program
|
|
267
270
|
.command('video-templates')
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -123,6 +123,10 @@ export interface TemplateOption {
|
|
|
123
123
|
sample_cover_image?: string | null
|
|
124
124
|
/** 归属:自己租户建的 vs 平台共享的(tenant_id 为空) */
|
|
125
125
|
tenant_id: string | null
|
|
126
|
+
/** 中台下发的归属标记:mine=本租户建的 / platform=平台共享 / personal=我这个人建的 */
|
|
127
|
+
source?: 'mine' | 'platform' | 'personal'
|
|
128
|
+
/** 创建人(平台管理员个人建的模板会带邮箱;租户建的为 null) */
|
|
129
|
+
created_by?: string | null
|
|
126
130
|
generation_configs: Array<{
|
|
127
131
|
model: string
|
|
128
132
|
prompt_template: string
|
|
@@ -386,9 +390,13 @@ export class StudioClient {
|
|
|
386
390
|
}
|
|
387
391
|
|
|
388
392
|
/** 可用图片/文字模板清单:自己租户建的 + 平台共享的,服务端已按调用者权限过滤。
|
|
389
|
-
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
390
|
-
|
|
391
|
-
|
|
393
|
+
* type=image|article 二选一(不传则图片+文字都返回,跟中台默认一致)。
|
|
394
|
+
* source=mine|platform|personal|all(默认 all;mine=本租户,platform=平台共享,personal=我这个人建的)。 */
|
|
395
|
+
async templates(type?: 'image' | 'article', source?: 'mine' | 'platform' | 'personal' | 'all'): Promise<TemplateOption[]> {
|
|
396
|
+
const params = new URLSearchParams()
|
|
397
|
+
if (type) params.set('type', type)
|
|
398
|
+
if (source && source !== 'all') params.set('source', source)
|
|
399
|
+
const qs = params.toString() ? `?${params}` : ''
|
|
392
400
|
const r = await this.request(`templates${qs}`)
|
|
393
401
|
return Array.isArray(r) ? r : []
|
|
394
402
|
}
|
package/src/commands/projects.ts
CHANGED
|
@@ -22,10 +22,15 @@ export async function projects(client: StudioClient): Promise<void> {
|
|
|
22
22
|
process.stderr.write('还没有工作区(museav projects create --name 新建)\n')
|
|
23
23
|
return
|
|
24
24
|
}
|
|
25
|
+
// 每个项目的素材数(模板不挂项目,这里统计的是项目素材库,不是模板)
|
|
26
|
+
const assetCounts = await Promise.all(
|
|
27
|
+
list.map(async (w) => ({ id: w.id, n: (await client.workspaceAssets(w.id).catch(() => [])).length })),
|
|
28
|
+
)
|
|
29
|
+
const assetOf = Object.fromEntries(assetCounts.map((x) => [x.id, x.n]))
|
|
25
30
|
process.stderr.write(`工作区(${list.length} 个):\n`)
|
|
26
31
|
for (const w of list) {
|
|
27
32
|
process.stderr.write(
|
|
28
|
-
` ${w.id} ${w.name.padEnd(16)} 出图 ${w.gen_done ?? 0}/${w.gen_total ?? 0}${w.brand ? ` brand:${w.brand}` : ''}\n`,
|
|
33
|
+
` ${w.id} ${w.name.padEnd(16)} 出图 ${w.gen_done ?? 0}/${w.gen_total ?? 0} 素材 ${assetOf[w.id] ?? 0}${w.brand ? ` brand:${w.brand}` : ''}\n`,
|
|
29
34
|
)
|
|
30
35
|
}
|
|
31
36
|
process.stderr.write(`\n素材库: museav projects assets --project <id|名>\n出图归档: museav gen --project <id|名> ...\n`)
|
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
/** museav templates ——
|
|
2
|
-
* --type image|article
|
|
1
|
+
/** museav templates —— 查可用图片/文字模板。
|
|
2
|
+
* --type image|article 按类型过滤
|
|
3
|
+
* --mine 只看本租户建的;--platform 只看平台共享的;都不传则全部列出
|
|
4
|
+
* --category 按分类过滤 */
|
|
3
5
|
import type { StudioClient } from '../client.js'
|
|
4
6
|
|
|
5
|
-
export async function templates(client: StudioClient, opts: { category?: string; type?: string } = {}): Promise<void> {
|
|
6
|
-
|
|
7
|
+
export async function templates(client: StudioClient, opts: { category?: string; type?: string; mine?: boolean; tenant?: boolean; platform?: boolean } = {}): Promise<void> {
|
|
8
|
+
const type = opts.type === 'image' || opts.type === 'article' ? opts.type : undefined
|
|
9
|
+
// 三个归属维度都走服务端 source 参数(正式 API,不再客户端猜):
|
|
10
|
+
// --mine → source=personal(created_by = 当前账户邮箱,我这个人建的)
|
|
11
|
+
// --tenant → source=mine(本租户专属)
|
|
12
|
+
// --platform → source=platform(平台共享)
|
|
13
|
+
let list: Awaited<ReturnType<StudioClient['templates']>>
|
|
14
|
+
if (opts.mine) {
|
|
15
|
+
list = await client.templates(type, 'personal')
|
|
16
|
+
} else if (opts.tenant) {
|
|
17
|
+
list = await client.templates(type, 'mine')
|
|
18
|
+
} else if (opts.platform) {
|
|
19
|
+
list = await client.templates(type, 'platform')
|
|
20
|
+
} else {
|
|
21
|
+
list = await client.templates(type)
|
|
22
|
+
}
|
|
7
23
|
if (opts.category) {
|
|
8
24
|
const kw = opts.category.toLowerCase()
|
|
9
25
|
list = list.filter((t) => (t.category || '').toLowerCase().includes(kw))
|
|
@@ -13,7 +29,11 @@ export async function templates(client: StudioClient, opts: { category?: string;
|
|
|
13
29
|
return
|
|
14
30
|
}
|
|
15
31
|
|
|
16
|
-
const tag = (t: (typeof list)[number]) =>
|
|
32
|
+
const tag = (t: (typeof list)[number]) => {
|
|
33
|
+
if (t.source === 'personal') return '[个人]'
|
|
34
|
+
if (t.tenant_id) return '[租户]'
|
|
35
|
+
return '[平台]'
|
|
36
|
+
}
|
|
17
37
|
const typeTag = (t: (typeof list)[number]) => (t.template_type === 'article' ? '[文字]' : t.template_type === 'image' ? '[图片]' : '')
|
|
18
38
|
|
|
19
39
|
process.stderr.write(`可用模板(${list.length} 个):\n`)
|
|
@@ -27,7 +47,7 @@ export async function templates(client: StudioClient, opts: { category?: string;
|
|
|
27
47
|
)
|
|
28
48
|
}
|
|
29
49
|
process.stderr.write(`\n出图: museav gen --template <模板id> [--fields '{"key":"值"}']\n`)
|
|
30
|
-
process.stderr.write(
|
|
50
|
+
process.stderr.write(`筛选: --mine(我建的) --tenant(本租户) --platform(平台共享) --type image|article --category <分类>\n`)
|
|
31
51
|
// stdout 只出 id,便于脚本与 agent 解析
|
|
32
52
|
console.log(list.map((t) => t.id).join('\n'))
|
|
33
53
|
}
|
package/src/commands/whoami.ts
CHANGED
|
@@ -1,19 +1,46 @@
|
|
|
1
|
-
/** museav whoami ——
|
|
1
|
+
/** museav whoami —— 查当前身份 + 关联信息。
|
|
2
|
+
*
|
|
3
|
+
* apiKey 模式下也支持(2026-08-17 修复):服务端 /api/me 对两种 apiKey 都返回真实数据——
|
|
4
|
+
* - 平台账户 apiKey(STUDIO_API_KEY 指向 accounts.sk-*) → 走个人身份,含 credits / gen 统计
|
|
5
|
+
* - 租户 apiKey(指向 api_tenants.tenant_key) → 走业务身份,只下发 tenant 信息
|
|
6
|
+
* 个人 login(museav login)走完整 me,输出不变。
|
|
7
|
+
*/
|
|
2
8
|
import type { StudioClient } from '../client.js'
|
|
3
|
-
|
|
9
|
+
|
|
10
|
+
/** /api/me 实际返回的形状:可能是账户身份也可能是租户身份(apiKey 模式) */
|
|
11
|
+
type MeResponse =
|
|
12
|
+
| {
|
|
13
|
+
identity?: 'account'
|
|
14
|
+
id: string
|
|
15
|
+
email: string
|
|
16
|
+
nickname?: string | null
|
|
17
|
+
bio?: string | null
|
|
18
|
+
role: string
|
|
19
|
+
credits?: number | null
|
|
20
|
+
gen_quota?: number | null
|
|
21
|
+
gen_total?: number
|
|
22
|
+
gen_done?: number
|
|
23
|
+
brand?: { name: string } | null
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
identity: 'tenant'
|
|
27
|
+
tenant: { id: string; name: string; nickname?: string | null; logo?: string | null }
|
|
28
|
+
}
|
|
4
29
|
|
|
5
30
|
export async function whoami(client: StudioClient): Promise<void> {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
31
|
+
const me = (await client.me()) as MeResponse
|
|
32
|
+
|
|
33
|
+
if (me.identity === 'tenant') {
|
|
34
|
+
const t = me.tenant
|
|
35
|
+
process.stderr.write(`身份: 租户 API Key\n`)
|
|
36
|
+
process.stderr.write(`租户: ${t.nickname || t.name}(${t.name})\n`)
|
|
37
|
+
process.stderr.write(`tenant_id: ${t.id}\n`)
|
|
38
|
+
if (t.logo) process.stderr.write(`logo: ${t.logo}\n`)
|
|
39
|
+
} else {
|
|
40
|
+
process.stderr.write(`账户: ${me.nickname || me.email}(${me.email})\n`)
|
|
41
|
+
process.stderr.write(me.brand ? `业务系统: ${me.brand.name}\n` : `身份: 平台账户 API Key\n`)
|
|
42
|
+
process.stderr.write(`出图: 累计 ${me.gen_total ?? 0} 次,成功 ${me.gen_done ?? 0} 次\n`)
|
|
43
|
+
if (me.credits != null) process.stderr.write(`credits: ${me.credits}\n`)
|
|
13
44
|
}
|
|
14
|
-
const me = await client.me()
|
|
15
|
-
process.stderr.write(`账户: ${me.nickname || me.email}(${me.email})\n`)
|
|
16
|
-
process.stderr.write(me.brand ? `业务系统: ${me.brand.name}\n` : '业务系统: 未接入(个人用户)\n')
|
|
17
|
-
process.stderr.write(`出图: 累计 ${me.gen_total} 次,成功 ${me.gen_done} 次\n`)
|
|
18
45
|
console.log(JSON.stringify(me))
|
|
19
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -281,6 +281,9 @@ const templatesCmd = program
|
|
|
281
281
|
.description('查可用图片/文字模板:自己租户建的 + 平台共享的(跟技能是两套不同的机制,见 gen --template)')
|
|
282
282
|
.option('--category <name>', '按分类过滤,如 电商白底图 / 演唱会')
|
|
283
283
|
.option('--type <type>', '按类型过滤:image(图片) / article(文字),不传则两类都列并标注')
|
|
284
|
+
.option('--mine', '只看我这个人建的模板(created_by 是我,排除系统种子和租户专属)')
|
|
285
|
+
.option('--tenant', '只看本租户建的模板')
|
|
286
|
+
.option('--platform', '只看平台共享的模板')
|
|
284
287
|
.action(withClient((client: StudioClient, opts: any) => templates(client, opts)))
|
|
285
288
|
|
|
286
289
|
const videoTemplatesCmd = program
|