@remixmate/cli 0.1.2 → 0.9.2

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 (53) hide show
  1. package/README.md +46 -0
  2. package/README.zh-CN.md +22 -0
  3. package/dist/auth/auth-lock.d.ts +26 -0
  4. package/dist/auth/auth-lock.js +100 -0
  5. package/dist/auth/auto-login.d.ts +20 -0
  6. package/dist/auth/auto-login.js +66 -0
  7. package/dist/auth/commands.d.ts +8 -0
  8. package/dist/auth/commands.js +130 -0
  9. package/dist/auth/credential-store.d.ts +44 -0
  10. package/dist/auth/credential-store.js +126 -0
  11. package/dist/auth/device-flow-runner.d.ts +43 -0
  12. package/dist/auth/device-flow-runner.js +62 -0
  13. package/dist/auth/device-flow.d.ts +52 -0
  14. package/dist/auth/device-flow.js +115 -0
  15. package/dist/auth/environment.d.ts +25 -0
  16. package/dist/auth/environment.js +48 -0
  17. package/dist/auth/resolve.d.ts +30 -0
  18. package/dist/auth/resolve.js +44 -0
  19. package/dist/cli.js +11 -0
  20. package/dist/handlers/gen-digital-human.js +1 -1
  21. package/dist/handlers/gen-image.js +1 -1
  22. package/dist/handlers/gen-video.js +1 -1
  23. package/dist/handlers/gen-voice.js +2 -2
  24. package/dist/http.d.ts +9 -5
  25. package/dist/http.js +24 -10
  26. package/dist/manifest.json +3 -3
  27. package/dist/runner.d.ts +1 -1
  28. package/package.json +1 -1
  29. package/skills/export-jianying/SKILL.md +9 -3
  30. package/skills/export-jianying/version.json +1 -1
  31. package/skills/gen-digital-human/SKILL.md +11 -11
  32. package/skills/gen-digital-human/version.json +1 -1
  33. package/skills/gen-image/SKILL.md +6 -6
  34. package/skills/gen-image/version.json +1 -1
  35. package/skills/gen-script/scripts/gen_script.py +66 -31
  36. package/skills/gen-script/version.json +1 -1
  37. package/skills/gen-video/SKILL.md +6 -6
  38. package/skills/gen-video/version.json +1 -1
  39. package/skills/gen-voice/SKILL.md +6 -6
  40. package/skills/gen-voice/version.json +1 -1
  41. package/skills/render-video/SKILL.md +20 -1
  42. package/skills/render-video/scripts/remote_renderer_client.py +9 -7
  43. package/skills/render-video/scripts/render_video.py +119 -15
  44. package/skills/render-video/version.json +1 -1
  45. package/skills/template-registry/README.md +12 -13
  46. package/skills/template-registry/SKILL.md +11 -12
  47. package/skills/template-registry/scripts/registry_loader.py +117 -96
  48. package/skills/template-registry/scripts/render_job_client.py +12 -0
  49. package/skills/template-registry/skill.json +1 -1
  50. package/skills/template-registry/version.json +1 -1
  51. package/skills/template-registry/video_dsl/runtime/dsl_validator.py +2 -2
  52. package/skills/web-capture/skill.json +0 -1
  53. package/skills/web-capture/version.json +1 -1
@@ -21,7 +21,7 @@ triggers:
21
21
 
22
22
  Wraps ab-api's digital-human endpoints (the same ones the web studio uses), authenticated with the **Tianyan privateToken**. Two providers: **Jimeng** (image-driven) and **HiFly** (video-driven). Generation is async — the handler submits the task and polls `/digital-human/video/status` until completion.
23
23
 
24
- > This skill was migrated from a Python script to an ab-skill CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_digital_human`, same params as in `skill.json`); local repro goes through `ab-skill gen-digital-human ...`.
24
+ > This skill was migrated from a Python script to an remixmate CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_digital_human`, same params as in `skill.json`); local repro goes through `remixmate gen-digital-human ...`.
25
25
 
26
26
  ## Capabilities
27
27
 
@@ -54,24 +54,24 @@ By default only the URL is printed; generation is async and the handler polls un
54
54
  ### 1. List avatars
55
55
 
56
56
  ```bash
57
- ab-skill gen-digital-human --list-avatars
57
+ remixmate gen-digital-human --list-avatars
58
58
  ```
59
59
 
60
60
  Optional filters:
61
61
 
62
62
  ```bash
63
63
  # Filter by source (jimeng = image-driven, hifly = video-driven)
64
- ab-skill gen-digital-human --list-avatars --source jimeng
65
- ab-skill gen-digital-human --list-avatars --source hifly
64
+ remixmate gen-digital-human --list-avatars --source jimeng
65
+ remixmate gen-digital-human --list-avatars --source hifly
66
66
 
67
67
  # Filter by gender
68
- ab-skill gen-digital-human --list-avatars --gender female
68
+ remixmate gen-digital-human --list-avatars --gender female
69
69
 
70
70
  # Show your custom avatars
71
- ab-skill gen-digital-human --list-avatars --mine
71
+ remixmate gen-digital-human --list-avatars --mine
72
72
 
73
73
  # Filter by name
74
- ab-skill gen-digital-human --list-avatars --name "alice"
74
+ remixmate gen-digital-human --list-avatars --name "alice"
75
75
  ```
76
76
 
77
77
  > Custom avatars require `--mine`. When generating a video the handler auto-falls back from the public list to `mine: true` if needed.
@@ -81,7 +81,7 @@ ab-skill gen-digital-human --list-avatars --name "alice"
81
81
  #### TTS mode
82
82
 
83
83
  ```bash
84
- ab-skill gen-digital-human \
84
+ remixmate gen-digital-human \
85
85
  --avatar-id 7 \
86
86
  --text "Hi everyone, welcome to the live stream — today I'll introduce a new product." \
87
87
  --voice-id "male-qn-qingse"
@@ -90,7 +90,7 @@ ab-skill gen-digital-human \
90
90
  #### TTS + custom voice + aspect ratio
91
91
 
92
92
  ```bash
93
- ab-skill gen-digital-human \
93
+ remixmate gen-digital-human \
94
94
  --avatar-id 7 \
95
95
  --text "Hi class, here's our brand-new course." \
96
96
  --voice-id "female-shaonv" \
@@ -100,7 +100,7 @@ ab-skill gen-digital-human \
100
100
  #### Audio-driven mode
101
101
 
102
102
  ```bash
103
- ab-skill gen-digital-human \
103
+ remixmate gen-digital-human \
104
104
  --avatar-id 39 \
105
105
  --source hifly \
106
106
  --audio-url "https://example.com/voice.mp3" \
@@ -110,7 +110,7 @@ ab-skill gen-digital-human \
110
110
  ### 3. Check job status
111
111
 
112
112
  ```bash
113
- ab-skill gen-digital-human --check-status --generation-id 123
113
+ remixmate gen-digital-human --check-status --generation-id 123
114
114
  ```
115
115
 
116
116
  ## Common CLI flags
@@ -2,6 +2,6 @@
2
2
  "skillName": "gen-digital-human",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "336",
5
- "version": "V7",
5
+ "version": "V8",
6
6
  "skillDescription": "数字人口播视频技能,支持查询形象、TTS 口播、音频驱动口播和查询生成状态(调用 ab-api 数字人接口,即梦 / 飞影)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 数字人、数字人视频、数字人口播、生成数字人视频\n- 用户提供已有音频 URL(MP3 等)、用外链音频驱动数字人口型\n- AI 主播、虚拟主播、虚拟人物视频、口播视频\n- 让数字人说话、让虚拟人说一段话、让 AI 人物播报\n- 查看/列出数字人形象、有哪些数字人可以用\n- 使用即梦/飞影数字人\n\n即使用户没有明确说「使用 AI」,只要他们想要让一个虚拟人物朗读/播报一段文字并生成视频,也要使用本 skill。"
7
7
  }
@@ -51,7 +51,7 @@ No skill-local env file — the executing process inherits the system environmen
51
51
 
52
52
  ## Operations
53
53
 
54
- > This skill was migrated from a Python script to an ab-skill CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_image`, same params as in `skill.json`); local repro goes through `ab-skill gen-image ...`.
54
+ > This skill was migrated from a Python script to an remixmate CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_image`, same params as in `skill.json`); local repro goes through `remixmate gen-image ...`.
55
55
 
56
56
  1. **Prompt**: be specific about subject, style, lighting, composition. Either English or Chinese works.
57
57
  2. By default only the URL is printed (good for showing to the user); the legacy local-download flag has been dropped — image URLs are persisted in the cloud.
@@ -59,13 +59,13 @@ No skill-local env file — the executing process inherits the system environmen
59
59
  ### Text-to-image
60
60
 
61
61
  ```bash
62
- ab-skill gen-image \
62
+ remixmate gen-image \
63
63
  --prompt "<image description>" \
64
64
  --size "9:16"
65
65
  ```
66
66
 
67
67
  ```bash
68
- ab-skill gen-image \
68
+ remixmate gen-image \
69
69
  --prompt "<image description>" \
70
70
  --model gemini-3-pro-image \
71
71
  --size "16:9" \
@@ -81,14 +81,14 @@ Reference images accept local file paths, HTTPS URLs, or data URIs. Pass `--refe
81
81
 
82
82
  ```bash
83
83
  # URL reference
84
- ab-skill gen-image \
84
+ remixmate gen-image \
85
85
  --prompt "Convert this photo to an oil-painting style" \
86
86
  --reference "https://example.com/photo.jpg"
87
87
  ```
88
88
 
89
89
  ```bash
90
90
  # Local-file reference + reference strength
91
- ab-skill gen-image \
91
+ remixmate gen-image \
92
92
  --prompt "Match the style of this reference" \
93
93
  --reference ./ref.png \
94
94
  --image-strength 0.6
@@ -96,7 +96,7 @@ ab-skill gen-image \
96
96
 
97
97
  ```bash
98
98
  # Multiple references
99
- ab-skill gen-image \
99
+ remixmate gen-image \
100
100
  --prompt "Blend these styles" \
101
101
  --reference ./a.png \
102
102
  --reference ./b.png
@@ -2,6 +2,6 @@
2
2
  "skillName": "gen-image",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "337",
5
- "version": "V8",
5
+ "version": "V9",
6
6
  "skillDescription": "AI 生图技能,根据文字描述生成图片,也支持参考图进行图生图(调用 ab-api /model/genImg,支持 Seedream 与 Gemini)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- AI 生图、AI 画图、文生图、文字生成图片、生成图像、帮我画、生成一张图\n- 图生图、以图生图、参考图、风格迁移、图片变体\n- 使用 doubao / 豆包 / seedream、Gemini 等生成图片\n- 用户提供图片提示词并希望生成图片\n\n即使用户没有明确说「使用 AI」,只要他们想要根据描述生成图片,也要使用本 skill。"
7
7
  }
@@ -322,9 +322,9 @@ def generate_image_prompt(
322
322
  def _load_template_config(template_id: str) -> dict | None:
323
323
  """Load template definition for the given template ID.
324
324
 
325
- 解析顺序(与 match_template / template_binder 保持一致):
326
- 1. 通过 registry_loader 从 monorepo template-library/packages/metadata/registry.json
327
- (或 VIDEO_TEMPLATE_REGISTRY[_URL] 环境变量)加载,并按 templateId 匹配。
325
+ 解析顺序:
326
+ 1. 通过 registry_loader 从 ab-api(单一数据源,URL
327
+ VIDEO_TEMPLATE_REGISTRY_URL 或按 MM_API_BASE_URL 推导)加载,按 templateId 匹配。
328
328
  2. 兜底:旧路径 template-registry/video_dsl/templates/<id>/template.json
329
329
  (仅在仓库还残留旧目录时使用)。
330
330
  返回完整 template dict(含 supportedAspectRatios / assetRequirements / slotMapping 等),
@@ -384,6 +384,29 @@ def _template_primary_visual_type(template_config: dict | None) -> str:
384
384
  return "none"
385
385
 
386
386
 
387
+ def _template_needs_narration(template_config: dict | None) -> bool:
388
+ """模板是否需要旁白(TTS)。
389
+
390
+ 默认 True —— 保持历史行为:每个场景挂一个 gen-voice 素材 + 写入
391
+ ``audio.narration`` 骨架,由 agent 后续填真实文案。
392
+
393
+ 模板可通过 ``capabilities.needsNarration=false`` 声明"纯视觉 / 无旁白"
394
+ (如单图 Ken Burns、BGM-only 展示)。此时 gen_script:
395
+ - 不生成 gen-voice 素材;
396
+ - 场景不写 ``audio.narration``。
397
+ 于是骨架的 narrationSceneCount=0,ab-agent 的 prepare_video_assets 预校验
398
+ 会直接放行,不再强制用户为每个场景填旁白(与 spotlight-card 等无配音模板
399
+ 走的是同一条 pass-through 路径)。
400
+
401
+ 兼容历史脏数据:部分模板的 ``capabilities`` 是空列表 ``[]``(而非 dict),
402
+ 统一按"未声明"处理 → 返回 True。仅当显式为 JSON ``false`` 时才关闭旁白。
403
+ """
404
+ caps = (template_config or {}).get("capabilities")
405
+ if not isinstance(caps, dict):
406
+ return True
407
+ return caps.get("needsNarration", True) is not False
408
+
409
+
387
410
  def _template_needs_image(template_config: dict | None) -> bool:
388
411
  """Check if a template requires image assets.
389
412
 
@@ -786,6 +809,9 @@ def build_dsl(
786
809
  )
787
810
  primary_visual_type = _template_primary_visual_type(template_config)
788
811
  has_visual = primary_visual_type in ("image", "video")
812
+ # 旁白是模板能力(capabilities.needsNarration)。声明为 false 的模板(纯视觉 /
813
+ # BGM-only,如单图 Ken Burns)不分配 gen-voice 素材、场景不写 audio.narration。
814
+ needs_narration = _template_needs_narration(template_config)
789
815
 
790
816
  # ── Resolve template-driven output language + voice ────────────────────
791
817
  # outputLanguage is owned by the template (template.json). Missing/invalid
@@ -815,8 +841,11 @@ def build_dsl(
815
841
  visual_asset_id = f"video-{scene_id}"
816
842
  else:
817
843
  visual_asset_id = f"img-{scene_id}"
818
- narration_asset_id = f"narration-{scene_id}"
819
- narration_text = estimate_narration_text(plan["label"], plan["purpose"], plan["duration"], topic, language=output_language)
844
+ narration_asset_id = f"narration-{scene_id}" if needs_narration else None
845
+ narration_text = (
846
+ estimate_narration_text(plan["label"], plan["purpose"], plan["duration"], topic, language=output_language)
847
+ if needs_narration else ""
848
+ )
820
849
 
821
850
  # 根据模板 assetRequirements 生成对应类型的视觉素材
822
851
  if primary_visual_type == "image":
@@ -881,15 +910,17 @@ def build_dsl(
881
910
  # narration text 不再在 audio asset 的 payload 里冗余存放——
882
911
  # 唯一来源是下游 scenes[].audio.narration.text,render_video 在
883
912
  # 调用 TTS skill 前会按 assetRef 回查 scene 文本注入。
884
- assets.append({
885
- "assetId": narration_asset_id,
886
- "type": "audio",
887
- "source": "gen-voice",
888
- "status": "planned",
889
- "payload": {
890
- "voiceId": resolved_voice_id,
891
- },
892
- })
913
+ # 模板声明 needsNarration=false 时,完全不生成 gen-voice 素材。
914
+ if needs_narration:
915
+ assets.append({
916
+ "assetId": narration_asset_id,
917
+ "type": "audio",
918
+ "source": "gen-voice",
919
+ "status": "planned",
920
+ "payload": {
921
+ "voiceId": resolved_voice_id,
922
+ },
923
+ })
893
924
 
894
925
  if has_visual:
895
926
  layout = "text-overlay" if plan["purpose"] in ("opening", "cta") else "full-visual"
@@ -916,33 +947,35 @@ def build_dsl(
916
947
  if effective_subheadline:
917
948
  text_layers.append({"role": "subheadline", "content": effective_subheadline, "animation": "fade-in"})
918
949
 
919
- scenes.append({
950
+ scene = {
920
951
  "id": scene_id,
921
952
  "purpose": plan["purpose"],
922
953
  "duration": plan["duration"],
923
954
  "layout": layout,
924
955
  "visuals": {"background": {"assetRef": visual_asset_id}},
925
- "audio": {
956
+ "textLayers": text_layers,
957
+ "animationHints": {
958
+ "entrance": "fade",
959
+ "motion": "kenburns-in" if idx % 2 == 0 else "kenburns-out",
960
+ },
961
+ }
962
+ if needs_narration:
963
+ scene["audio"] = {
926
964
  "narration": {
927
965
  "text": narration_text,
928
966
  "assetRef": narration_asset_id,
929
967
  # 骨架标记:scenes[].audio.narration.text 同样需要被真实旁白替换
930
968
  "needsFill": True,
931
969
  },
932
- },
933
- "textLayers": text_layers,
934
- "animationHints": {
935
- "entrance": "fade",
936
- "motion": "kenburns-in" if idx % 2 == 0 else "kenburns-out",
937
- },
938
- })
970
+ }
971
+ scenes.append(scene)
939
972
  else:
940
973
  # The "no-visual" branch labels each scene with a layout hint.
941
974
  # Historical fallback was hardcoded "html-slide" — when no template
942
975
  # was specified, every audio-only scene defaulted to html-slide
943
976
  # styling. We replace the hardcoded fallback chain with:
944
977
  # 1. template.capabilities.defaultLayout ⇐ the template tells
945
- # ab-skill what layout name it wants on its no-visual scenes
978
+ # remixmate what layout name it wants on its no-visual scenes
946
979
  # 2. template_id ⇐ legacy: pass id as
947
980
  # layout name (existing behavior when no capability declared)
948
981
  # 3. "html-slide" ⇐ absolute fallback
@@ -966,22 +999,24 @@ def build_dsl(
966
999
 
967
1000
  custom_payload = _build_custom_payload(plan["purpose"], topic, plan["label"], idx, len(scene_plans), language=output_language)
968
1001
 
969
- scenes.append({
1002
+ scene = {
970
1003
  "id": scene_id,
971
1004
  "purpose": plan["purpose"],
972
1005
  "duration": plan["duration"],
973
1006
  "layout": layout,
974
- "audio": {
1007
+ "textLayers": text_layers,
1008
+ "customPayload": custom_payload,
1009
+ }
1010
+ if needs_narration:
1011
+ scene["audio"] = {
975
1012
  "narration": {
976
1013
  "text": narration_text,
977
1014
  "assetRef": narration_asset_id,
978
1015
  # 骨架标记:scenes[].audio.narration.text 同样需要被真实旁白替换
979
1016
  "needsFill": True,
980
1017
  },
981
- },
982
- "textLayers": text_layers,
983
- "customPayload": custom_payload,
984
- })
1018
+ }
1019
+ scenes.append(scene)
985
1020
 
986
1021
  dsl = {
987
1022
  "version": "v1alpha1",
@@ -1001,7 +1036,7 @@ def build_dsl(
1001
1036
  "aspectRatio": ratio,
1002
1037
  "resolution": resolution,
1003
1038
  "fps": 30,
1004
- "subtitle": {"enabled": True, "style": "bottom"},
1039
+ "subtitle": {"enabled": needs_narration, "style": "bottom"},
1005
1040
  "narration": {"voiceId": resolved_voice_id, "speed": 1.0},
1006
1041
  "bgm": {"enabled": True, "volume": 0.12},
1007
1042
  },
@@ -2,6 +2,6 @@
2
2
  "skillName": "gen-script",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "474",
5
- "version": "V8",
5
+ "version": "V10",
6
6
  "skillDescription": "视频脚本生成技能,将用户主题转化为结构化 Video DSL(JSON),描述视频的完整结构、素材需求与叙事逻辑。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 帮我写视频脚本、生成视频脚本、视频策划、写分镜脚本\n- 做一个短视频、帮我规划视频内容、生成视频 DSL\n- 把主题转成视频结构、视频内容规划\n\n即使用户没有明确说「生成 DSL」,只要他们想要把一个主题变成视频内容结构,也要使用本 skill。"
7
7
  }
@@ -19,7 +19,7 @@ triggers:
19
19
 
20
20
  Wraps ab-api's `POST /model/genVideo` (the same endpoint the web "Lingchuang AI Video" tool uses), authenticated with the **Tianyan privateToken**, routed through LiteLLM to **Seedance** or **Veo**. Generation is async — the handler submits the task and polls `/model/getVideoStatus` until completion.
21
21
 
22
- > This skill was migrated from a Python script to an ab-skill CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_video`, same params as in `skill.json`); local repro goes through `ab-skill gen-video ...`.
22
+ > This skill was migrated from a Python script to an remixmate CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_video`, same params as in `skill.json`); local repro goes through `remixmate gen-video ...`.
23
23
 
24
24
  ## Models
25
25
 
@@ -72,7 +72,7 @@ No skill-local env file — the executing process inherits the system environmen
72
72
  ### Seedance text-to-video (default model)
73
73
 
74
74
  ```bash
75
- ab-skill gen-video \
75
+ remixmate gen-video \
76
76
  --prompt "<video description>" \
77
77
  --duration 5 \
78
78
  --ratio "16:9"
@@ -83,7 +83,7 @@ ab-skill gen-video \
83
83
  Frame images accept local file paths, HTTPS URLs, or data URIs (local files are base64-encoded into a data URI).
84
84
 
85
85
  ```bash
86
- ab-skill gen-video \
86
+ remixmate gen-video \
87
87
  --prompt "<transition description>" \
88
88
  --first-frame ./start.png \
89
89
  --last-frame ./end.png \
@@ -94,7 +94,7 @@ ab-skill gen-video \
94
94
  ### Veo 3.1 high-resolution
95
95
 
96
96
  ```bash
97
- ab-skill gen-video \
97
+ remixmate gen-video \
98
98
  --model veo \
99
99
  --prompt "<video description>" \
100
100
  --duration 8 \
@@ -105,7 +105,7 @@ ab-skill gen-video \
105
105
  ### Veo 3.1 Fast for rapid iteration
106
106
 
107
107
  ```bash
108
- ab-skill gen-video \
108
+ remixmate gen-video \
109
109
  --model veo-fast \
110
110
  --prompt "<video description>" \
111
111
  --duration 6
@@ -116,7 +116,7 @@ ab-skill gen-video \
116
116
  Pass `--reference` multiple times (Veo only, up to 3).
117
117
 
118
118
  ```bash
119
- ab-skill gen-video \
119
+ remixmate gen-video \
120
120
  --model veo \
121
121
  --prompt "<video description>" \
122
122
  --reference ./ref1.png \
@@ -2,6 +2,6 @@
2
2
  "skillName": "gen-video",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "339",
5
- "version": "V7",
5
+ "version": "V8",
6
6
  "skillDescription": "AI 生视频技能,根据文字描述生成素材视频(调用 ab-api /model/genVideo,支持 Seedance 与 Veo)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- AI 生视频、文生视频、文字生成视频、生成一段视频、AI 制作视频\n- 使用 doubao / 豆包 / seedance、Veo、Google 等生成视频\n- 用户提供视频提示词并希望生成视频\n- 图生视频、首帧生成视频、参考图生成视频\n\n即使用户没有明确说「使用 AI」,只要他们想要根据描述生成视频,也要使用本 skill。"
7
7
  }
@@ -34,7 +34,7 @@ There is no skill-local env file — the executing process inherits the system e
34
34
 
35
35
  ## Operations
36
36
 
37
- > This skill was migrated from a Python script to an ab-skill CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_voice`, same params as in `skill.json`); local repro goes through `ab-skill gen-voice ...`. The legacy `--download` flag has been removed — audio URLs are persisted in the cloud and play directly.
37
+ > This skill was migrated from a Python script to an remixmate CLI HTTP handler (`entry.type: http`). The agent invocation is unchanged (same tool name `gen_voice`, same params as in `skill.json`); local repro goes through `remixmate gen-voice ...`. The legacy `--download` flag has been removed — audio URLs are persisted in the cloud and play directly.
38
38
 
39
39
  1. **Text**: confirm what to synthesize. Punctuation drives pauses (commas short, periods long).
40
40
  2. **Voice**: if the user prefers a specific voice, run `--list-voices` first and pick a matching id.
@@ -43,13 +43,13 @@ There is no skill-local env file — the executing process inherits the system e
43
43
  ### List available voices
44
44
 
45
45
  ```bash
46
- ab-skill gen-voice --list-voices
46
+ remixmate gen-voice --list-voices
47
47
  ```
48
48
 
49
49
  To inspect the local language-tagged fallback catalog used by the voice resolver (no remote API call), add `--local`:
50
50
 
51
51
  ```bash
52
- ab-skill gen-voice --list-voices --local
52
+ remixmate gen-voice --list-voices --local
53
53
  ```
54
54
 
55
55
  The local catalog prints one voice per line as `<voice-id>\t<lang>\t<display-name>`.
@@ -57,13 +57,13 @@ The local catalog prints one voice per line as `<voice-id>\t<lang>\t<display-nam
57
57
  ### Default synthesis (URL output)
58
58
 
59
59
  ```bash
60
- ab-skill gen-voice --text "<text-to-synthesize>"
60
+ remixmate gen-voice --text "<text-to-synthesize>"
61
61
  ```
62
62
 
63
63
  ### With voice + speed
64
64
 
65
65
  ```bash
66
- ab-skill gen-voice \
66
+ remixmate gen-voice \
67
67
  --text "<text-to-synthesize>" \
68
68
  --voice-id "female-shaonv" \
69
69
  --speed 1.2
@@ -72,7 +72,7 @@ ab-skill gen-voice \
72
72
  ### JSON output (with subtitle timestamps)
73
73
 
74
74
  ```bash
75
- ab-skill gen-voice --text "<text-to-synthesize>" --json-output
75
+ remixmate gen-voice --text "<text-to-synthesize>" --json-output
76
76
  ```
77
77
 
78
78
  ## Common CLI flags
@@ -2,6 +2,6 @@
2
2
  "skillName": "gen-voice",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "338",
5
- "version": "V6",
5
+ "version": "V7",
6
6
  "skillDescription": "语音合成(TTS)技能,将文字转为语音音频(调用 ab-api /tool/minimaxT2aV2,基于 Minimax TTS)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- AI 配音、语音合成、文字转语音、TTS、文本转音频、生成语音\n- 使用 minimax 语音合成\n- 用户想要将一段文字朗读出来、生成音频\n\n即使用户没有明确说「使用 AI」,只要他们想要将文字转为语音,也要使用本 skill。"
7
7
  }
@@ -73,9 +73,28 @@ python3 <SkillDir>/scripts/render_video.py --job-id <jobId>
73
73
  When the DB is unavailable, the script accepts a pre-generated RenderPlan file:
74
74
 
75
75
  ```bash
76
- python3 <SkillDir>/scripts/render_video.py --render-plan output/render-plan.json
76
+ python3 <SkillDir>/scripts/render_video.py --render-plan <path>.render-plan.json
77
77
  ```
78
78
 
79
+ > ⚠️ **Do NOT blindly point `--render-plan` at a shared `output/render-plan.json`.**
80
+ > Under the default token-present path, `prepare_video_assets` / `render_video`
81
+ > persist the plan to the **database (jobId)** only — they do **not** write
82
+ > `output/render-plan.json`. Any file sitting there is likely a leftover from a
83
+ > previous, unrelated run (the `output/` dir is gitignored scratch), and rendering
84
+ > it silently produces the wrong video.
85
+ >
86
+ > To get a RenderPlan file on disk, generate it explicitly:
87
+ > ```bash
88
+ > python3 <SkillDir>/scripts/render_video.py --dsl video.dsl.json --template-id <id> \
89
+ > --resolve-only --save-render-plan --render-plan-output video.render-plan.json
90
+ > ```
91
+ > Or skip the file entirely and render in one shot:
92
+ > `render_video.py --dsl video.dsl.json --template-id <id>`.
93
+ >
94
+ > On load, the script now prints a `↳ plan: templateId=… composition=… duration=… title=…`
95
+ > summary, and warns if the plan file is older than a `--dsl` you also passed —
96
+ > check that line matches what you intend before the render proceeds.
97
+
79
98
  ### Render and upload to Alibaba Cloud OSS (default behavior)
80
99
 
81
100
  ```bash
@@ -121,11 +121,12 @@ def start_render(
121
121
  base_url: Optional[str] = None,
122
122
  timeout: float = 120.0,
123
123
  conversation_id: Optional[str] = None,
124
+ path: str = "/render",
124
125
  ) -> str:
125
- """POST /render;返回 taskId。"""
126
+ """POST <path>(默认 /render,私有模板动态渲染传 /renderDraft);返回 taskId。"""
126
127
  result = _request(
127
128
  "POST",
128
- "/render",
129
+ path,
129
130
  private_token=private_token,
130
131
  payload=payload,
131
132
  base_url=base_url,
@@ -135,7 +136,7 @@ def start_render(
135
136
  data = _check_code(result)
136
137
  task_id = data.get("taskId")
137
138
  if not task_id:
138
- raise RemoteRenderError(f"/render did not return a taskId: {result}")
139
+ raise RemoteRenderError(f"{path} did not return a taskId: {result}")
139
140
  return str(task_id)
140
141
 
141
142
 
@@ -150,8 +151,9 @@ def poll_render(
150
151
  max_consecutive_errors: int = 5,
151
152
  on_progress: Optional[Callable[[dict], None]] = None,
152
153
  adaptive_interval: bool = False,
154
+ status_path: str = "/renderStatus",
153
155
  ) -> dict:
154
- """POST /renderStatus 轮询直到完成/失败/超时。
156
+ """POST <status_path>(默认 /renderStatus,私有模板传 /renderDraftStatus)轮询直到完成/失败/超时。
155
157
 
156
158
  成功时返回 data dict(至少包含 fileUrl),否则抛 RemoteRenderError。
157
159
  on_progress 回调在每次成功请求后触发,参数为完整 data dict。
@@ -170,13 +172,13 @@ def poll_render(
170
172
  if elapsed > timeout:
171
173
  raise RemoteRenderError(
172
174
  f"remote render polling timed out (waited {elapsed:.0f}s, taskId={task_id}); "
173
- f"the job may still be running on the backend — query /renderStatus later"
175
+ f"the job may still be running on the backend — query {status_path} later"
174
176
  )
175
177
 
176
178
  try:
177
179
  result = _request(
178
180
  "POST",
179
- "/renderStatus",
181
+ status_path,
180
182
  private_token=private_token,
181
183
  payload={"taskId": task_id},
182
184
  base_url=base_url,
@@ -201,7 +203,7 @@ def poll_render(
201
203
  body = exc.read().decode("utf-8")
202
204
  except Exception:
203
205
  pass
204
- raise RemoteRenderError(f"HTTP {exc.code} /renderStatus: {body[:200]}") from exc
206
+ raise RemoteRenderError(f"HTTP {exc.code} {status_path}: {body[:200]}") from exc
205
207
 
206
208
  consecutive_errors = 0
207
209