@remixmate/cli 0.9.72 → 0.9.74

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.
Files changed (179) hide show
  1. package/LICENSE +45 -21
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/dist/argv.js +1 -51
  5. package/dist/auth/auth-lock.js +1 -100
  6. package/dist/auth/auto-login.js +7 -75
  7. package/dist/auth/commands.js +40 -290
  8. package/dist/auth/credential-store.js +1 -143
  9. package/dist/auth/device-flow-runner.js +13 -110
  10. package/dist/auth/device-flow.js +1 -132
  11. package/dist/auth/ensure.js +1 -62
  12. package/dist/auth/environment.js +1 -69
  13. package/dist/auth/pending-store.js +1 -45
  14. package/dist/auth/resolve.js +1 -44
  15. package/dist/billing.js +3 -76
  16. package/dist/capabilities.js +1 -75
  17. package/dist/cli.js +39 -211
  18. package/dist/diagnostics.js +2 -70
  19. package/dist/doctor.js +9 -188
  20. package/dist/errors.js +1 -33
  21. package/dist/exec.js +4 -47
  22. package/dist/handlers/gen-digital-human.js +33 -264
  23. package/dist/handlers/gen-image.js +9 -113
  24. package/dist/handlers/gen-post.js +15 -192
  25. package/dist/handlers/gen-video.js +7 -185
  26. package/dist/handlers/gen-voice.js +19 -133
  27. package/dist/handlers/index.js +1 -20
  28. package/dist/handlers/shared.js +1 -66
  29. package/dist/handlers/template-init.js +1 -21
  30. package/dist/http.js +3 -155
  31. package/dist/index.js +1 -8
  32. package/dist/install.js +40 -448
  33. package/dist/manifest.json +44 -3
  34. package/dist/progress.js +2 -15
  35. package/dist/project/commands.js +34 -156
  36. package/dist/project/host.js +1 -151
  37. package/dist/project/resolve.js +1 -65
  38. package/dist/project/store.js +2 -97
  39. package/dist/project/take.js +1 -106
  40. package/dist/project/trace.js +5 -278
  41. package/dist/registry.js +3 -70
  42. package/dist/runner.js +7 -225
  43. package/dist/skill-schema.js +1 -194
  44. package/dist/template/args.js +1 -69
  45. package/dist/template/check.js +2 -30
  46. package/dist/template/commands.js +34 -163
  47. package/dist/template/constants.js +1 -46
  48. package/dist/template/init.js +5 -133
  49. package/dist/template/list.js +1 -46
  50. package/dist/template/preview.js +7 -318
  51. package/dist/template/proc.js +1 -43
  52. package/dist/template/project.js +6 -161
  53. package/dist/template/publish.js +5 -185
  54. package/dist/template/render-client.js +2 -79
  55. package/dist/template/upgrade.js +4 -123
  56. package/dist/text.js +1 -15
  57. package/package.json +2 -35
  58. package/skills/doc-parse/scripts/extract_via_render.py +32 -32
  59. package/skills/export-jianying/scripts/gen_jianying_draft.py +188 -188
  60. package/skills/gen-script/SKILL.md +23 -1
  61. package/skills/gen-script/scripts/gen_script.py +644 -551
  62. package/skills/gen-script/skill.json +12 -0
  63. package/skills/gen-script/version.json +1 -1
  64. package/skills/prepare-video-assets/scripts/prepare_video_assets.py +21 -21
  65. package/skills/render-video/scripts/_chrome_vendor.py +17 -17
  66. package/skills/render-video/scripts/_digital_human_segments.py +34 -34
  67. package/skills/render-video/scripts/_video_probe.py +33 -33
  68. package/skills/render-video/scripts/_vod_polling.py +13 -13
  69. package/skills/render-video/scripts/remote_renderer_client.py +42 -42
  70. package/skills/render-video/scripts/render_video.py +639 -639
  71. package/skills/render-video/scripts/required_props.py +37 -37
  72. package/skills/render-video/scripts/upload_video.py +35 -35
  73. package/skills/render-video/version.json +1 -1
  74. package/skills/template-registry/SKILL.md +16 -0
  75. package/skills/template-registry/scripts/countdown_spec.py +92 -92
  76. package/skills/template-registry/scripts/doc_extract.py +40 -40
  77. package/skills/template-registry/scripts/list_templates.py +303 -191
  78. package/skills/template-registry/scripts/log_diagnostics.py +1 -1
  79. package/skills/template-registry/scripts/match_template.py +87 -87
  80. package/skills/template-registry/scripts/payload_hydrator.py +89 -89
  81. package/skills/template-registry/scripts/registry_loader.py +159 -159
  82. package/skills/template-registry/scripts/render_job_client.py +75 -75
  83. package/skills/template-registry/scripts/showcase_loader.py +44 -44
  84. package/skills/template-registry/skill.json +4 -1
  85. package/skills/template-registry/version.json +1 -1
  86. package/skills/template-registry/video_dsl/runtime/__init__.py +8 -8
  87. package/skills/template-registry/video_dsl/runtime/dsl_validator.py +329 -329
  88. package/skills/template-registry/video_dsl/runtime/narration_budget.py +36 -36
  89. package/skills/template-registry/video_dsl/runtime/payload_contract.py +7 -7
  90. package/skills/template-registry/video_dsl/runtime/prompt_enhancer.py +98 -98
  91. package/skills/template-registry/video_dsl/runtime/timeline_compiler.py +127 -127
  92. package/skills/template-registry/video_dsl/schema/video-dsl-v1alpha1.json +14 -1
  93. package/skills/video-parser/scripts/analyze_video.py +173 -173
  94. package/skills/video-parser/scripts/deconstruct_video.py +91 -91
  95. package/skills/video-parser/scripts/parse_via_render.py +21 -21
  96. package/skills/web-screenshot/scripts/_media_screenshot/__init__.py +4 -4
  97. package/skills/web-screenshot/scripts/_media_screenshot/bootstrap.py +7 -7
  98. package/skills/web-screenshot/scripts/_media_screenshot/browser.py +4 -4
  99. package/skills/web-screenshot/scripts/_media_screenshot/cli_args.py +4 -4
  100. package/skills/web-screenshot/scripts/_media_screenshot/js_loader.py +2 -2
  101. package/skills/web-screenshot/scripts/_media_screenshot/overlay.py +1 -1
  102. package/skills/web-screenshot/scripts/_media_screenshot/reader.py +40 -40
  103. package/skills/web-screenshot/scripts/_media_screenshot/recording.py +11 -11
  104. package/skills/web-screenshot/scripts/_media_screenshot/scenes/__init__.py +8 -8
  105. package/skills/web-screenshot/scripts/_media_screenshot/scenes/arrow.py +2 -2
  106. package/skills/web-screenshot/scripts/_media_screenshot/scenes/caption.py +1 -1
  107. package/skills/web-screenshot/scripts/_media_screenshot/scenes/click.py +1 -1
  108. package/skills/web-screenshot/scripts/_media_screenshot/scenes/code_lines.py +5 -5
  109. package/skills/web-screenshot/scripts/_media_screenshot/scenes/focus.py +1 -1
  110. package/skills/web-screenshot/scripts/_media_screenshot/scenes/highlight.py +3 -3
  111. package/skills/web-screenshot/scripts/_media_screenshot/scenes/hover.py +1 -1
  112. package/skills/web-screenshot/scripts/_media_screenshot/scenes/redact.py +1 -1
  113. package/skills/web-screenshot/scripts/_media_screenshot/scenes/scroll.py +1 -1
  114. package/skills/web-screenshot/scripts/_media_screenshot/scenes/sequence.py +1 -1
  115. package/skills/web-screenshot/scripts/_media_screenshot/scenes/title_card.py +1 -1
  116. package/skills/web-screenshot/scripts/_media_screenshot/scenes/type.py +1 -1
  117. package/skills/web-screenshot/scripts/_media_screenshot/scenes/wait.py +1 -1
  118. package/skills/web-screenshot/scripts/_media_screenshot/scenes/zoom.py +2 -2
  119. package/skills/web-screenshot/scripts/_media_screenshot/screenshot.py +15 -15
  120. package/skills/web-screenshot/scripts/_media_screenshot/storyboard.py +2 -2
  121. package/skills/web-screenshot/scripts/_media_screenshot/template.py +11 -11
  122. package/skills/web-screenshot/scripts/_media_screenshot/trim.py +10 -10
  123. package/skills/web-screenshot/scripts/_media_screenshot/urlguard.py +22 -22
  124. package/skills/web-screenshot/scripts/_media_upload.py +33 -33
  125. package/skills/web-screenshot/scripts/read_page.py +15 -15
  126. package/skills/web-screenshot/scripts/record.py +26 -26
  127. package/skills/web-screenshot/scripts/screenshot.py +5 -5
  128. package/dist/argv.d.ts +0 -15
  129. package/dist/auth/auth-lock.d.ts +0 -26
  130. package/dist/auth/auto-login.d.ts +0 -20
  131. package/dist/auth/commands.d.ts +0 -8
  132. package/dist/auth/credential-store.d.ts +0 -60
  133. package/dist/auth/device-flow-runner.d.ts +0 -58
  134. package/dist/auth/device-flow.d.ts +0 -52
  135. package/dist/auth/ensure.d.ts +0 -55
  136. package/dist/auth/environment.d.ts +0 -38
  137. package/dist/auth/pending-store.d.ts +0 -33
  138. package/dist/auth/resolve.d.ts +0 -30
  139. package/dist/billing.d.ts +0 -44
  140. package/dist/capabilities.d.ts +0 -63
  141. package/dist/cli.d.ts +0 -13
  142. package/dist/diagnostics.d.ts +0 -24
  143. package/dist/doctor.d.ts +0 -22
  144. package/dist/errors.d.ts +0 -29
  145. package/dist/exec.d.ts +0 -18
  146. package/dist/handlers/gen-digital-human.d.ts +0 -17
  147. package/dist/handlers/gen-image.d.ts +0 -20
  148. package/dist/handlers/gen-post.d.ts +0 -22
  149. package/dist/handlers/gen-video.d.ts +0 -19
  150. package/dist/handlers/gen-voice.d.ts +0 -26
  151. package/dist/handlers/index.d.ts +0 -20
  152. package/dist/handlers/shared.d.ts +0 -26
  153. package/dist/handlers/template-init.d.ts +0 -10
  154. package/dist/http.d.ts +0 -72
  155. package/dist/index.d.ts +0 -9
  156. package/dist/install.d.ts +0 -71
  157. package/dist/progress.d.ts +0 -23
  158. package/dist/project/commands.d.ts +0 -19
  159. package/dist/project/host.d.ts +0 -91
  160. package/dist/project/resolve.d.ts +0 -43
  161. package/dist/project/store.d.ts +0 -31
  162. package/dist/project/take.d.ts +0 -50
  163. package/dist/project/trace.d.ts +0 -98
  164. package/dist/registry.d.ts +0 -30
  165. package/dist/runner.d.ts +0 -39
  166. package/dist/skill-schema.d.ts +0 -138
  167. package/dist/template/args.d.ts +0 -25
  168. package/dist/template/check.d.ts +0 -23
  169. package/dist/template/commands.d.ts +0 -25
  170. package/dist/template/constants.d.ts +0 -39
  171. package/dist/template/init.d.ts +0 -28
  172. package/dist/template/list.d.ts +0 -23
  173. package/dist/template/preview.d.ts +0 -60
  174. package/dist/template/proc.d.ts +0 -26
  175. package/dist/template/project.d.ts +0 -47
  176. package/dist/template/publish.d.ts +0 -43
  177. package/dist/template/render-client.d.ts +0 -39
  178. package/dist/template/upgrade.d.ts +0 -44
  179. package/dist/text.d.ts +0 -8
@@ -1,264 +1,33 @@
1
- /**
2
- * gen-digital-human handler — talking-head video + custom avatars, five modes:
3
- * --list-avatars true → POST /digital-human/avatar/page (list)
4
- * --clone --clone-url <url> --name <n> → POST /digital-human/avatar/clone, then poll clone-status
5
- * --clone-status --avatar-id <id> → POST /digital-human/avatar/clone-status (one-shot)
6
- * --check-status --generation-id <id> → POST /digital-human/video/status (one-shot)
7
- * default (--avatar-id <id> ...) → generate: resolve avatar → submit → poll
8
- *
9
- * Ported from `scripts/gen_digital_human_video.py` (python → http) so it shares
10
- * auth / base-URL / business-code handling with the other generators via
11
- * http.ts. Endpoints and payloads match the backend DigitalHuman* DTOs.
12
- *
13
- * Avatar ids are the backend's opaque uids (`dh_...`); legacy numeric ids are
14
- * still accepted as strings.
15
- */
16
- import { mmPost, pollUntil, resolveHttpContext, SkillError } from '../http.js';
17
- import { getCapabilities } from '../capabilities.js';
18
- import { emitProgress } from '../progress.js';
19
- import { isTrue, toNumber } from './shared.js';
20
- const CLONE_TYPES = ['video', 'image'];
21
- /**
22
- * The only provider currently wired end to end (HiFly). HiFly's generate APIs
23
- * take no aspect ratio or action prompt — the output keeps the cloned source's
24
- * framing — so the handler sends neither.
25
- */
26
- const ACTIVE_SOURCE = 'hifly';
27
- function toId(value) {
28
- if (value === undefined || value === null)
29
- return '';
30
- return String(value).trim();
31
- }
32
- async function listAvatars(ctx, input) {
33
- // Only avatars of the active provider can be driven, so only those are listed.
34
- const payload = { current: 1, pageSize: 100, source: ACTIVE_SOURCE };
35
- if (input.gender)
36
- payload.gender = String(input.gender);
37
- if (isTrue(input.mine))
38
- payload.mine = true;
39
- if (input.name)
40
- payload.name = String(input.name);
41
- const data = await mmPost(ctx, '/digital-human/avatar/page', payload, { timeoutMs: 30_000 });
42
- const avatars = data.list ?? [];
43
- if (isTrue(input.json_output)) {
44
- process.stdout.write(JSON.stringify({ avatars }) + '\n');
45
- return;
46
- }
47
- if (avatars.length === 0) {
48
- process.stdout.write('⚠️ No avatars available\n');
49
- return;
50
- }
51
- process.stdout.write(`\n${'ID'.padEnd(24)} ${'Name'.padEnd(16)} ${'Source'.padEnd(14)} ${'Gender'.padEnd(8)} ${'Ratio'.padEnd(6)} Clone\n`);
52
- process.stdout.write(`${'-'.repeat(24)} ${'-'.repeat(16)} ${'-'.repeat(14)} ${'-'.repeat(8)} ${'-'.repeat(6)} ${'-'.repeat(8)}\n`);
53
- for (const a of avatars) {
54
- process.stdout.write(`${toId(a.id).padEnd(24)} ${String(a.name ?? '').padEnd(16)} ${String(a.source ?? '').padEnd(14)} ${String(a.gender ?? '').padEnd(8)} ${String(a.aspectRatio ?? '').padEnd(6)} ${a.cloneStatus || '-'}\n`);
55
- }
56
- const total = data.pager?.total ?? avatars.length;
57
- process.stdout.write(`\n${avatars.length} avatar(s) shown${total > avatars.length ? ` of ${total}; narrow with --name` : ''}\n`);
58
- process.stdout.write('💡 Use --avatar-id <ID> to pick an avatar for generation\n');
59
- }
60
- /**
61
- * Look up one avatar's metadata. Only the caller's own avatars are searched: they
62
- * are few, and they are the only ones that can be mid-clone or failed. The public
63
- * HiFly catalog has ~1000 entries, far beyond one page, so a public id that is not
64
- * among "mine" is passed through as-is and the backend validates it (unknown ids
65
- * come back as a business error).
66
- */
67
- async function fetchAvatarInfo(ctx, avatarId) {
68
- const data = await mmPost(ctx, '/digital-human/avatar/page', { current: 1, pageSize: 100, mine: true, source: ACTIVE_SOURCE }, { timeoutMs: 30_000 });
69
- const found = (data.list ?? []).find((a) => toId(a.id) === avatarId);
70
- return found ?? { id: avatarId, source: ACTIVE_SOURCE };
71
- }
72
- function resolveSource(avatar, validSources) {
73
- const src = (avatar.source ?? '').trim() || ACTIVE_SOURCE;
74
- if (!validSources.includes(src)) {
75
- throw new SkillError(`❌ avatar uses provider "${src}", which is not available (supported: ${validSources.join('/')}). Pick another avatar from --list-avatars.`);
76
- }
77
- return src;
78
- }
79
- // ── Avatar cloning (HiFly) ─────────────────────────────────────────────────
80
- async function cloneAvatar(ctx, input) {
81
- const fileUrl = toId(input.clone_url);
82
- if (!/^https?:\/\//i.test(fileUrl)) {
83
- throw new SkillError('❌ --clone needs --clone-url <public http(s) URL of a video or portrait image> (local files are not uploaded)');
84
- }
85
- const name = toId(input.name);
86
- if (!name)
87
- throw new SkillError('❌ --clone needs --name <avatar name>');
88
- const sourceType = toId(input.clone_type) || 'video';
89
- if (!CLONE_TYPES.includes(sourceType)) {
90
- throw new SkillError(`❌ --clone-type must be one of ${CLONE_TYPES.join(' / ')}, received: ${sourceType}`);
91
- }
92
- const payload = { name, sourceType, fileUrl };
93
- if (input.thumbnail_url)
94
- payload.thumbnailUrl = toId(input.thumbnail_url);
95
- if (input.gender)
96
- payload.gender = toId(input.gender);
97
- emitProgress({ phase: 'gen-digital-human:clone-request', sourceType });
98
- const data = await mmPost(ctx, '/digital-human/avatar/clone', payload, { timeoutMs: 60_000 });
99
- const avatarId = toId(data.id);
100
- if (!avatarId) {
101
- throw new SkillError(`❌ could not parse the new avatar id; API returned: ${JSON.stringify(data)}`);
102
- }
103
- emitProgress({ phase: 'gen-digital-human:clone-async', avatarId });
104
- const json = isTrue(input.json_output);
105
- if (input.wait !== undefined && !isTrue(input.wait)) {
106
- if (json) {
107
- process.stdout.write(JSON.stringify({ avatarId, cloneStatus: data.cloneStatus ?? 'cloning' }) + '\n');
108
- return;
109
- }
110
- process.stdout.write(`\n🧬 Clone submitted, avatar id: ${avatarId}\n`);
111
- process.stdout.write(`💡 Check progress with --clone-status --avatar-id ${avatarId}\n`);
112
- return;
113
- }
114
- let final;
115
- try {
116
- final = await pollUntil(async () => {
117
- const s = await mmPost(ctx, '/digital-human/avatar/clone-status', { id: avatarId }, { timeoutMs: 30_000 });
118
- const status = s.cloneStatus ?? 'unknown';
119
- emitProgress({ phase: 'gen-digital-human:clone-poll', avatarId, status });
120
- if (status === 'done')
121
- return s;
122
- if (status === 'failed')
123
- throw new SkillError(`❌ avatar clone failed: ${s.cloneError || 'unknown error'}`);
124
- return null;
125
- }, { intervalMs: 10_000, timeoutMs: 20 * 60_000 });
126
- }
127
- catch (err) {
128
- if (err instanceof SkillError && /polling timed out/.test(err.message)) {
129
- throw new SkillError(`❌ avatar ${avatarId} is still cloning; rerun --clone-status --avatar-id ${avatarId} later`);
130
- }
131
- throw err;
132
- }
133
- if (json) {
134
- process.stdout.write(JSON.stringify({ avatarId, cloneStatus: final.cloneStatus }) + '\n');
135
- return;
136
- }
137
- process.stdout.write(`\n✅ Avatar cloned, id: ${avatarId}\n`);
138
- process.stdout.write(`💡 Generate with --avatar-id ${avatarId} --text "..."\n`);
139
- }
140
- async function checkCloneStatus(ctx, avatarId, json) {
141
- const s = await mmPost(ctx, '/digital-human/avatar/clone-status', { id: avatarId }, { timeoutMs: 30_000 });
142
- const status = s.cloneStatus || 'unknown';
143
- if (json) {
144
- process.stdout.write(JSON.stringify({ avatarId, cloneStatus: status, error: s.cloneError || null }) + '\n');
145
- return;
146
- }
147
- process.stdout.write(`\nclone status: ${status}\n`);
148
- if (status === 'failed')
149
- process.stdout.write(` failure reason: ${s.cloneError || 'unknown error'}\n`);
150
- }
151
- // ── Video generation ───────────────────────────────────────────────────────
152
- async function checkStatus(ctx, generationId, json) {
153
- const data = await mmPost(ctx, '/digital-human/video/status', { generationId }, { timeoutMs: 30_000 });
154
- const status = data.status ?? 'unknown';
155
- if (json) {
156
- process.stdout.write(JSON.stringify({ status, url: data.videoUrl ?? null, generationId }) + '\n');
157
- return;
158
- }
159
- process.stdout.write(`\ntask status: ${status}\n`);
160
- if (data.videoUrl)
161
- process.stdout.write(` video URL: ${data.videoUrl}\n`);
162
- if (status === 'failed') {
163
- const err = data.errorMessage ?? data.error ?? data.message ?? 'unknown error';
164
- process.stdout.write(` failure reason: ${err}\n`);
165
- }
166
- }
167
- async function pollVideoStatus(ctx, generationId) {
168
- const url = await pollUntil(async () => {
169
- const data = await mmPost(ctx, '/digital-human/video/status', { generationId }, { timeoutMs: 30_000 });
170
- const status = data.status ?? 'unknown';
171
- emitProgress({ phase: 'gen-digital-human:poll', generationId, status });
172
- if (status === 'completed')
173
- return data.videoUrl ?? '';
174
- if (status === 'failed') {
175
- const err = data.errorMessage ?? data.error ?? data.message ?? 'unknown error';
176
- throw new SkillError(`❌ video generation failed: ${err}`);
177
- }
178
- return null;
179
- }, { intervalMs: 5_000, timeoutMs: 600_000 });
180
- return url;
181
- }
182
- export async function genDigitalHuman(input, ctxIn) {
183
- const ctx = await resolveHttpContext(ctxIn.skillName, {
184
- apiBaseUrl: input.api_base_url,
185
- privateToken: input.priv_token,
186
- preflightToken: ctxIn.auth?.token,
187
- });
188
- const json = isTrue(input.json_output);
189
- // ── Mode 1: list avatars ────────────────────────────────────────────────
190
- if (isTrue(input.list_avatars)) {
191
- return listAvatars(ctx, input);
192
- }
193
- // ── Mode 2: clone a custom avatar (HiFly) ───────────────────────────────
194
- if (isTrue(input.clone)) {
195
- return cloneAvatar(ctx, input);
196
- }
197
- // ── Mode 3: clone status ────────────────────────────────────────────────
198
- if (isTrue(input.clone_status)) {
199
- const avatarId = toId(input.avatar_id);
200
- if (!avatarId)
201
- throw new SkillError('❌ clone-status mode requires --avatar-id');
202
- return checkCloneStatus(ctx, avatarId, json);
203
- }
204
- // ── Mode 4: check generation status ─────────────────────────────────────
205
- if (isTrue(input.check_status)) {
206
- const generationId = toNumber(input.generation_id, 'generation_id');
207
- if (generationId === undefined) {
208
- throw new SkillError('❌ check-status mode requires --generation-id');
209
- }
210
- return checkStatus(ctx, generationId, json);
211
- }
212
- // ── Mode 5: generate (default) ──────────────────────────────────────────
213
- const avatarId = toId(input.avatar_id);
214
- if (!avatarId) {
215
- throw new SkillError('❌ provide --avatar-id to pick the avatar (use --list-avatars to see available avatars)');
216
- }
217
- const audioUrl = (input.audio_url ?? '').trim();
218
- const text = input.text ?? '';
219
- if (audioUrl && !/^https?:\/\//i.test(audioUrl)) {
220
- throw new SkillError(`❌ --audio-url needs a public http(s) URL (local files are not uploaded), received: ${audioUrl}`);
221
- }
222
- if (!audioUrl) {
223
- if (!text.trim()) {
224
- throw new SkillError('❌ in TTS mode provide --text, or use --audio-url for audio-driven mode');
225
- }
226
- }
227
- const dh = (await getCapabilities(input.api_base_url)).digitalHuman;
228
- emitProgress({ phase: 'gen-digital-human:resolve-avatar', avatarId });
229
- const avatar = await fetchAvatarInfo(ctx, avatarId);
230
- if (avatar.cloneStatus === 'cloning') {
231
- throw new SkillError(`❌ avatar ${avatarId} is still cloning; check with --clone-status --avatar-id ${avatarId}`);
232
- }
233
- if (avatar.cloneStatus === 'failed') {
234
- throw new SkillError(`❌ avatar ${avatarId} failed to clone (${avatar.cloneError || 'unknown error'}); clone it again`);
235
- }
236
- const source = resolveSource(avatar, dh.sources);
237
- const payload = { avatarId, source };
238
- if (audioUrl) {
239
- payload.audioUrl = audioUrl;
240
- }
241
- else {
242
- payload.text = text;
243
- payload.voiceId = input.voice_id ?? dh.defaultVoiceId;
244
- if (input.voice_name)
245
- payload.voiceName = String(input.voice_name);
246
- }
247
- emitProgress({ phase: 'gen-digital-human:request', avatarId, source, mode: audioUrl ? 'audio' : 'tts' });
248
- const data = await mmPost(ctx, '/digital-human/video/generate', payload, { timeoutMs: 60_000 });
249
- const generationId = data.generationId;
250
- if (!generationId) {
251
- throw new SkillError(`❌ could not parse generationId; API returned: ${JSON.stringify(data)}`);
252
- }
253
- emitProgress({ phase: 'gen-digital-human:async', generationId });
254
- const videoUrl = await pollVideoStatus(ctx, generationId);
255
- if (!videoUrl) {
256
- throw new SkillError(`❌ video generated but no URL was returned; rerun --check-status --generation-id ${generationId} later`);
257
- }
258
- if (json) {
259
- process.stdout.write(JSON.stringify({ url: videoUrl, generationId }) + '\n');
260
- return;
261
- }
262
- process.stdout.write(`\n🔗 Video URL (renders directly, no download needed):\n${videoUrl}\n`);
263
- process.stdout.write('\n🎉 Digital-human video generation done!\n');
264
- }
1
+ var E=Object.defineProperty;var l=(t,e)=>E(t,"name",{value:e,configurable:!0});import{mmPost as w,pollUntil as y,resolveHttpContext as U,SkillError as s}from"../http.js";import{getCapabilities as b}from"../capabilities.js";import{emitProgress as m}from"../progress.js";import{isTrue as f,toNumber as M}from"./shared.js";const k=["video","image"],_="hifly";function u(t){return t==null?"":String(t).trim()}l(u,"toId");async function N(t,e){const r={current:1,pageSize:100,source:_};e.gender&&(r.gender=String(e.gender)),f(e.mine)&&(r.mine=!0),e.name&&(r.name=String(e.name));const n=await w(t,"/digital-human/avatar/page",r,{timeoutMs:3e4}),a=n.list??[];if(f(e.json_output)){process.stdout.write(JSON.stringify({avatars:a})+`
2
+ `);return}if(a.length===0){process.stdout.write(`⚠️ No avatars available
3
+ `);return}process.stdout.write(`
4
+ ${"ID".padEnd(24)} ${"Name".padEnd(16)} ${"Source".padEnd(14)} ${"Gender".padEnd(8)} ${"Ratio".padEnd(6)} Clone
5
+ `),process.stdout.write(`${"-".repeat(24)} ${"-".repeat(16)} ${"-".repeat(14)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(8)}
6
+ `);for(const d of a)process.stdout.write(`${u(d.id).padEnd(24)} ${String(d.name??"").padEnd(16)} ${String(d.source??"").padEnd(14)} ${String(d.gender??"").padEnd(8)} ${String(d.aspectRatio??"").padEnd(6)} ${d.cloneStatus||"-"}
7
+ `);const o=n.pager?.total??a.length;process.stdout.write(`
8
+ ${a.length} avatar(s) shown${o>a.length?` of ${o}; narrow with --name`:""}
9
+ `),process.stdout.write(`💡 Use --avatar-id <ID> to pick an avatar for generation
10
+ `)}l(N,"listAvatars");async function I(t,e){return((await w(t,"/digital-human/avatar/page",{current:1,pageSize:100,mine:!0,source:_},{timeoutMs:3e4})).list??[]).find(a=>u(a.id)===e)??{id:e,source:_}}l(I,"fetchAvatarInfo");function O(t,e){const r=(t.source??"").trim()||_;if(!e.includes(r))throw new s(`❌ avatar uses provider "${r}", which is not available (supported: ${e.join("/")}). Pick another avatar from --list-avatars.`);return r}l(O,"resolveSource");async function C(t,e){const r=u(e.clone_url);if(!/^https?:\/\//i.test(r))throw new s("❌ --clone needs --clone-url <public http(s) URL of a video or portrait image> (local files are not uploaded)");const n=u(e.name);if(!n)throw new s("❌ --clone needs --name <avatar name>");const a=u(e.clone_type)||"video";if(!k.includes(a))throw new s(`❌ --clone-type must be one of ${k.join(" / ")}, received: ${a}`);const o={name:n,sourceType:a,fileUrl:r};e.thumbnail_url&&(o.thumbnailUrl=u(e.thumbnail_url)),e.gender&&(o.gender=u(e.gender)),m({phase:"gen-digital-human:clone-request",sourceType:a});const d=await w(t,"/digital-human/avatar/clone",o,{timeoutMs:6e4}),i=u(d.id);if(!i)throw new s(`❌ could not parse the new avatar id; API returned: ${JSON.stringify(d)}`);m({phase:"gen-digital-human:clone-async",avatarId:i});const g=f(e.json_output);if(e.wait!==void 0&&!f(e.wait)){if(g){process.stdout.write(JSON.stringify({avatarId:i,cloneStatus:d.cloneStatus??"cloning"})+`
11
+ `);return}process.stdout.write(`
12
+ 🧬 Clone submitted, avatar id: ${i}
13
+ `),process.stdout.write(`💡 Check progress with --clone-status --avatar-id ${i}
14
+ `);return}let v;try{v=await y(async()=>{const c=await w(t,"/digital-human/avatar/clone-status",{id:i},{timeoutMs:3e4}),h=c.cloneStatus??"unknown";if(m({phase:"gen-digital-human:clone-poll",avatarId:i,status:h}),h==="done")return c;if(h==="failed")throw new s(`❌ avatar clone failed: ${c.cloneError||"unknown error"}`);return null},{intervalMs:1e4,timeoutMs:20*6e4})}catch(c){throw c instanceof s&&/polling timed out/.test(c.message)?new s(`❌ avatar ${i} is still cloning; rerun --clone-status --avatar-id ${i} later`):c}if(g){process.stdout.write(JSON.stringify({avatarId:i,cloneStatus:v.cloneStatus})+`
15
+ `);return}process.stdout.write(`
16
+ ✅ Avatar cloned, id: ${i}
17
+ `),process.stdout.write(`💡 Generate with --avatar-id ${i} --text "..."
18
+ `)}l(C,"cloneAvatar");async function J(t,e,r){const n=await w(t,"/digital-human/avatar/clone-status",{id:e},{timeoutMs:3e4}),a=n.cloneStatus||"unknown";if(r){process.stdout.write(JSON.stringify({avatarId:e,cloneStatus:a,error:n.cloneError||null})+`
19
+ `);return}process.stdout.write(`
20
+ clone status: ${a}
21
+ `),a==="failed"&&process.stdout.write(` failure reason: ${n.cloneError||"unknown error"}
22
+ `)}l(J,"checkCloneStatus");async function R(t,e,r){const n=await w(t,"/digital-human/video/status",{generationId:e},{timeoutMs:3e4}),a=n.status??"unknown";if(r){process.stdout.write(JSON.stringify({status:a,url:n.videoUrl??null,generationId:e})+`
23
+ `);return}if(process.stdout.write(`
24
+ task status: ${a}
25
+ `),n.videoUrl&&process.stdout.write(` video URL: ${n.videoUrl}
26
+ `),a==="failed"){const o=n.errorMessage??n.error??n.message??"unknown error";process.stdout.write(` failure reason: ${o}
27
+ `)}}l(R,"checkStatus");async function T(t,e){return await y(async()=>{const n=await w(t,"/digital-human/video/status",{generationId:e},{timeoutMs:3e4}),a=n.status??"unknown";if(m({phase:"gen-digital-human:poll",generationId:e,status:a}),a==="completed")return n.videoUrl??"";if(a==="failed"){const o=n.errorMessage??n.error??n.message??"unknown error";throw new s(`❌ video generation failed: ${o}`)}return null},{intervalMs:5e3,timeoutMs:6e5})}l(T,"pollVideoStatus");async function q(t,e){const r=await U(e.skillName,{apiBaseUrl:t.api_base_url,privateToken:t.priv_token,preflightToken:e.auth?.token}),n=f(t.json_output);if(f(t.list_avatars))return N(r,t);if(f(t.clone))return C(r,t);if(f(t.clone_status)){const $=u(t.avatar_id);if(!$)throw new s("❌ clone-status mode requires --avatar-id");return J(r,$,n)}if(f(t.check_status)){const $=M(t.generation_id,"generation_id");if($===void 0)throw new s("❌ check-status mode requires --generation-id");return R(r,$,n)}const a=u(t.avatar_id);if(!a)throw new s("❌ provide --avatar-id to pick the avatar (use --list-avatars to see available avatars)");const o=(t.audio_url??"").trim(),d=t.text??"";if(o&&!/^https?:\/\//i.test(o))throw new s(`❌ --audio-url needs a public http(s) URL (local files are not uploaded), received: ${o}`);if(!o&&!d.trim())throw new s("❌ in TTS mode provide --text, or use --audio-url for audio-driven mode");const i=(await b(t.api_base_url)).digitalHuman;m({phase:"gen-digital-human:resolve-avatar",avatarId:a});const g=await I(r,a);if(g.cloneStatus==="cloning")throw new s(`❌ avatar ${a} is still cloning; check with --clone-status --avatar-id ${a}`);if(g.cloneStatus==="failed")throw new s(`❌ avatar ${a} failed to clone (${g.cloneError||"unknown error"}); clone it again`);const v=O(g,i.sources),c={avatarId:a,source:v};o?c.audioUrl=o:(c.text=d,c.voiceId=t.voice_id??i.defaultVoiceId,t.voice_name&&(c.voiceName=String(t.voice_name))),m({phase:"gen-digital-human:request",avatarId:a,source:v,mode:o?"audio":"tts"});const h=await w(r,"/digital-human/video/generate",c,{timeoutMs:6e4}),p=h.generationId;if(!p)throw new s(`❌ could not parse generationId; API returned: ${JSON.stringify(h)}`);m({phase:"gen-digital-human:async",generationId:p});const S=await T(r,p);if(!S)throw new s(`❌ video generated but no URL was returned; rerun --check-status --generation-id ${p} later`);if(n){process.stdout.write(JSON.stringify({url:S,generationId:p})+`
28
+ `);return}process.stdout.write(`
29
+ 🔗 Video URL (renders directly, no download needed):
30
+ ${S}
31
+ `),process.stdout.write(`
32
+ 🎉 Digital-human video generation done!
33
+ `)}l(q,"genDigitalHuman");export{q as genDigitalHuman};
@@ -1,113 +1,9 @@
1
- /**
2
- * gen-image handler — POST ab-api /model/genImg.
3
- *
4
- * Thin forwarder: the model roster, defaults, size presets and reference-image
5
- * caps all come from ab-api's capabilities catalog (see capabilities.ts), not
6
- * from this package. Behavior:
7
- * - --model accepts a friendly alias or a full id; resolved against the catalog
8
- * (ab-api also resolves aliases server-side, so an unknown value still passes
9
- * through and is validated by the backend).
10
- * - --size accepts a WxH string (→ width/height) or an aspect-ratio preset key
11
- * (→ aspectRatio; for Seedream models ab-api maps the preset to pixels).
12
- * - --resolution is accepted but inert: it only ever fed the Gemini `size` tier,
13
- * and Gemini is not wired up on the gateway, so we no longer send it.
14
- * - Reference images: HTTPS URLs and data: URIs pass through; local paths are
15
- * read and base64-encoded into a data: URI.
16
- * - Async response (generationId + status:'generating'/'pending') is polled
17
- * against /model/getImgStatus until completed/failed/timeout.
18
- */
19
- import { mmPost, pollUntil, resolveHttpContext, SkillError } from '../http.js';
20
- import { getCapabilities, matchModel, defaultModel } from '../capabilities.js';
21
- import { emitProgress } from '../progress.js';
22
- import { isTrue, resolveImageInput, toNumber, toStringArray } from './shared.js';
23
- async function pollImageStatus(ctx, generationId) {
24
- return pollUntil(async () => {
25
- const data = await mmPost(ctx, '/model/getImgStatus', { generationId }, { timeoutMs: 30_000 });
26
- const status = data.status ?? 'unknown';
27
- emitProgress({ phase: 'gen-image:poll', generationId, status });
28
- if (status === 'completed' || status === 'succeeded') {
29
- return (data.imageUrls ?? []).filter((u) => !!u);
30
- }
31
- if (status === 'failed') {
32
- const err = data.errorMsg ?? data.error ?? 'unknown error';
33
- throw new SkillError(`❌ image generation failed: ${err}`);
34
- }
35
- return null;
36
- }, { intervalMs: 5_000, timeoutMs: 120_000 });
37
- }
38
- export async function genImage(input, ctxIn) {
39
- const prompt = input.prompt;
40
- if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
41
- throw new SkillError('❌ Please provide --prompt to describe the image');
42
- }
43
- const apiBaseUrl = input.api_base_url;
44
- const caps = await getCapabilities(apiBaseUrl);
45
- // Resolve the model against the catalog: explicit alias/id, env override, or default.
46
- const requested = input.model ?? process.env.MM_IMAGE_MODEL;
47
- const descriptor = requested ? matchModel(caps.image, requested) : defaultModel(caps.image);
48
- // Send the resolved id when known; otherwise forward the raw value for the backend to handle.
49
- const model = descriptor?.id ?? requested ?? defaultModel(caps.image)?.id ?? '';
50
- const size = input.size ?? '1:1';
51
- const n = toNumber(input.n, 'n') ?? 1;
52
- const seed = toNumber(input.seed, 'seed');
53
- const imageStrength = toNumber(input.image_strength, 'image_strength');
54
- const guidanceScale = toNumber(input.guidance_scale, 'guidance_scale');
55
- const negativePrompt = input.negative_prompt;
56
- // --watermark / --no-watermark — parseArgv only sees `--watermark true` style;
57
- // when ab-agent's buildCliArgs passes booleans, only the truthy flag appears.
58
- // Treat presence as true; explicit `--no-watermark` isn't supported here.
59
- const watermark = input.watermark === true ? true : undefined;
60
- const references = toStringArray(input.reference).map(resolveImageInput).filter(Boolean);
61
- const refLimit = descriptor?.constraints.refImageMax;
62
- if (refLimit !== undefined && references.length > refLimit) {
63
- throw new SkillError(`❌ ${descriptor?.label ?? model} accepts at most ${refLimit} reference images, got ${references.length}`);
64
- }
65
- const payload = { model, prompt, imageCount: n };
66
- if (guidanceScale !== undefined)
67
- payload.guidanceScale = guidanceScale;
68
- if (references.length > 0)
69
- payload.referenceImages = references;
70
- if (imageStrength !== undefined)
71
- payload.imageStrength = imageStrength;
72
- if (negativePrompt)
73
- payload.negativePrompt = negativePrompt;
74
- if (seed !== undefined)
75
- payload.seed = seed;
76
- if (watermark !== undefined)
77
- payload.watermark = watermark;
78
- // --size as WxH → explicit pixels; otherwise treat as an aspect-ratio preset.
79
- // ab-api maps an aspect-ratio preset to that model's pixel table, so the CLI
80
- // carries no per-model size table.
81
- if (/^\d+x\d+$/i.test(size)) {
82
- const [w, h] = size.toLowerCase().split('x').map((v) => parseInt(v, 10));
83
- payload.width = w;
84
- payload.height = h;
85
- }
86
- else {
87
- payload.aspectRatio = size;
88
- }
89
- const ctx = await resolveHttpContext(ctxIn.skillName, {
90
- apiBaseUrl,
91
- privateToken: input.priv_token,
92
- preflightToken: ctxIn.auth?.token,
93
- });
94
- emitProgress({ phase: 'gen-image:request', model, size, n });
95
- const data = await mmPost(ctx, '/model/genImg', payload, { timeoutMs: 120_000 });
96
- let imageUrls = (data.imageUrls ?? []).filter((u) => !!u);
97
- if (imageUrls.length === 0 && data.generationId && (data.status === 'generating' || data.status === 'pending')) {
98
- emitProgress({ phase: 'gen-image:async', generationId: data.generationId });
99
- imageUrls = await pollImageStatus(ctx, data.generationId);
100
- }
101
- if (imageUrls.length === 0) {
102
- throw new SkillError(`❌ API response did not contain any image URL: ${JSON.stringify(data)}`);
103
- }
104
- if (isTrue(input.json_output)) {
105
- process.stdout.write(JSON.stringify({ urls: imageUrls }) + '\n');
106
- return;
107
- }
108
- process.stdout.write(`\n📸 Generated ${imageUrls.length} image(s)\n`);
109
- process.stdout.write(`\n🔗 Image URLs (renders directly, no download needed):\n`);
110
- for (const url of imageUrls)
111
- process.stdout.write(`${url}\n`);
112
- process.stdout.write(`\n🎉 Image generation done!\n`);
113
- }
1
+ var L=Object.defineProperty;var h=(e,r)=>L(e,"name",{value:r,configurable:!0});import{mmPost as U,pollUntil as C,resolveHttpContext as N,SkillError as c}from"../http.js";import{getCapabilities as A,matchModel as E,defaultModel as P}from"../capabilities.js";import{emitProgress as I}from"../progress.js";import{isTrue as O,resolveImageInput as R,toNumber as l,toStringArray as T}from"./shared.js";async function q(e,r){return C(async()=>{const n=await U(e,"/model/getImgStatus",{generationId:r},{timeoutMs:3e4}),o=n.status??"unknown";if(I({phase:"gen-image:poll",generationId:r,status:o}),o==="completed"||o==="succeeded")return(n.imageUrls??[]).filter(s=>!!s);if(o==="failed"){const s=n.errorMsg??n.error??"unknown error";throw new c(`❌ image generation failed: ${s}`)}return null},{intervalMs:5e3,timeoutMs:12e4})}h(q,"pollImageStatus");async function D(e,r){const n=e.prompt;if(!n||typeof n!="string"||!n.trim())throw new c("❌ Please provide --prompt to describe the image");const o=e.api_base_url,s=await A(o),f=e.model??process.env.MM_IMAGE_MODEL,p=f?E(s.image,f):P(s.image),u=p?.id??f??P(s.image)?.id??"",m=e.size??"1:1",_=l(e.n,"n")??1,M=l(e.seed,"seed"),k=l(e.image_strength,"image_strength"),y=l(e.guidance_scale,"guidance_scale"),v=e.negative_prompt,S=e.watermark===!0?!0:void 0,d=T(e.reference).map(R).filter(Boolean),w=p?.constraints.refImageMax;if(w!==void 0&&d.length>w)throw new c(`❌ ${p?.label??u} accepts at most ${w} reference images, got ${d.length}`);const t={model:u,prompt:n,imageCount:_};if(y!==void 0&&(t.guidanceScale=y),d.length>0&&(t.referenceImages=d),k!==void 0&&(t.imageStrength=k),v&&(t.negativePrompt=v),M!==void 0&&(t.seed=M),S!==void 0&&(t.watermark=S),/^\d+x\d+$/i.test(m)){const[g,b]=m.toLowerCase().split("x").map(x=>parseInt(x,10));t.width=g,t.height=b}else t.aspectRatio=m;const $=await N(r.skillName,{apiBaseUrl:o,privateToken:e.priv_token,preflightToken:r.auth?.token});I({phase:"gen-image:request",model:u,size:m,n:_});const a=await U($,"/model/genImg",t,{timeoutMs:12e4});let i=(a.imageUrls??[]).filter(g=>!!g);if(i.length===0&&a.generationId&&(a.status==="generating"||a.status==="pending")&&(I({phase:"gen-image:async",generationId:a.generationId}),i=await q($,a.generationId)),i.length===0)throw new c(`❌ API response did not contain any image URL: ${JSON.stringify(a)}`);if(O(e.json_output)){process.stdout.write(JSON.stringify({urls:i})+`
2
+ `);return}process.stdout.write(`
3
+ 📸 Generated ${i.length} image(s)
4
+ `),process.stdout.write(`
5
+ 🔗 Image URLs (renders directly, no download needed):
6
+ `);for(const g of i)process.stdout.write(`${g}
7
+ `);process.stdout.write(`
8
+ 🎉 Image generation done!
9
+ `)}h(D,"genImage");export{D as genImage};