@remixmate/cli 0.9.14 → 0.9.16

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 (36) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/billing.d.ts +44 -0
  4. package/dist/billing.js +76 -0
  5. package/dist/capabilities.d.ts +5 -1
  6. package/dist/cli.js +0 -0
  7. package/dist/http.d.ts +3 -0
  8. package/dist/http.js +4 -0
  9. package/dist/manifest.json +13 -13
  10. package/dist/runner.js +9 -0
  11. package/package.json +1 -1
  12. package/skills/export-jianying/version.json +1 -1
  13. package/skills/gen-digital-human/SKILL.md +12 -0
  14. package/skills/gen-digital-human/skill.json +9 -3
  15. package/skills/gen-digital-human/version.json +1 -1
  16. package/skills/gen-image/SKILL.md +51 -17
  17. package/skills/gen-image/skill.json +11 -3
  18. package/skills/gen-image/version.json +1 -1
  19. package/skills/gen-script/SKILL.md +17 -21
  20. package/skills/gen-script/version.json +1 -1
  21. package/skills/gen-video/SKILL.md +17 -3
  22. package/skills/gen-video/skill.json +10 -2
  23. package/skills/gen-video/version.json +1 -1
  24. package/skills/gen-voice/SKILL.md +13 -1
  25. package/skills/gen-voice/version.json +1 -1
  26. package/skills/prepare-video-assets/SKILL.md +12 -0
  27. package/skills/prepare-video-assets/version.json +1 -1
  28. package/skills/render-video/SKILL.md +12 -0
  29. package/skills/render-video/scripts/render_video.py +63 -0
  30. package/skills/render-video/version.json +1 -1
  31. package/skills/template-registry/scripts/render_job_client.py +27 -0
  32. package/skills/template-registry/version.json +1 -1
  33. package/skills/video-parser/version.json +1 -1
  34. package/skills/web-record/SKILL.md +131 -133
  35. package/skills/web-screenshot/SKILL.md +93 -96
  36. package/skills/web-screenshot/version.json +1 -1
@@ -23,7 +23,9 @@ Wraps ab-api's `POST /model/genVideo` (the same endpoint the web "Lingchuang AI
23
23
 
24
24
  ## Models
25
25
 
26
- Aligned with the handler's `MODEL_ALIASES` and the frontend `AI_VIDEO_MODELS`:
26
+ The authoritative roster ids, aliases and per-model limits — lives in the backend catalog
27
+ (`/model/capabilities`), which the CLI fetches at runtime. The table below mirrors it; when the
28
+ two disagree, the catalog wins.
27
29
 
28
30
  | LiteLLM `model` | Display name | Provider | Duration | Notes |
29
31
  |-----------------|--------------|----------|----------|-------|
@@ -33,8 +35,8 @@ Aligned with the handler's `MODEL_ALIASES` and the frontend `AI_VIDEO_MODELS`:
33
35
 
34
36
  **Model shortcuts** (`--model` / `-m` accepts these directly):
35
37
  - `seedance` / `seedance-1.5` / `seedance-1.5-pro` → Seedance 1.5 Pro
36
- - `veo` / `veo-3.1` → Veo 3.1
37
- - `veo-fast` / `veo-3.1-fast` → Veo 3.1 Fast
38
+ - `veo` / `veo-3.1` / `veo-3.1-generate` → Veo 3.1
39
+ - `veo-fast` / `veo-3.1-fast` / `veo-3.1-fast-generate` → Veo 3.1 Fast
38
40
 
39
41
  ### Per-model parameter ranges
40
42
 
@@ -150,6 +152,18 @@ remixmate gen-video \
150
152
  - Describe motion explicitly.
151
153
  - Example: `"At sunrise, an aerial drone shot of a futuristic city, golden light on glass facades, mist swirling, 4K ultra-clear"`.
152
154
 
155
+ ## Credits
156
+
157
+ Every run charges credits. The CLI prints a footer on stdout when it does:
158
+
159
+ ```
160
+ 💳 Charged 31 credits · balance 1,240
161
+ ```
162
+
163
+ Relay it to the user whenever it appears — it is the only signal they get about what a
164
+ generation cost, and the balance is the only warning before a run fails with
165
+ `insufficient_credits`. Do not drop it from your summary.
166
+
153
167
  ## Error handling
154
168
 
155
169
  - **401** / **token missing** (non-OpenClaw): set `PRIV_TOKEN`.
@@ -12,14 +12,22 @@
12
12
  "type": "object",
13
13
  "properties": {
14
14
  "prompt": { "type": "string", "description": "Video description" },
15
- "model": { "type": "string", "description": "Model: seedance / veo / veo-fast" },
15
+ "model": { "type": "string", "description": "Model: 'seedance' (default; 4-12s, adaptive/21:9 ratios, fixed camera), 'veo' (4/6/8s, native audio, up to 4k, reference images), or 'veo-fast' (faster Veo variant for iteration)" },
16
16
  "duration": { "type": "number", "description": "Duration in seconds" },
17
17
  "ratio": { "type": "string", "description": "Aspect ratio" },
18
18
  "resolution": { "type": "string", "description": "Resolution" },
19
19
  "first_frame": { "type": "string", "description": "First-frame image path or URL" },
20
20
  "last_frame": { "type": "string", "description": "Last-frame image path or URL" },
21
- "reference": { "type": "string", "description": "Reference image (Veo only)" },
21
+ "reference": {
22
+ "type": "array",
23
+ "items": { "type": "string" },
24
+ "description": "Reference images: local file path, https URL, or data URI. Veo only, max 3 — Seedance rejects them, use first_frame / last_frame instead."
25
+ },
22
26
  "generate_audio": { "type": "boolean", "description": "Generate native audio" },
27
+ "camera_fixed": { "type": "boolean", "description": "Lock the camera in place (Seedance)" },
28
+ "negative_prompt": { "type": "string", "description": "Content to steer away from (Veo)" },
29
+ "seed": { "type": "number", "description": "Random seed. Pass the same seed with the same prompt and model to make a run reproducible." },
30
+ "person_generation": { "type": "string", "enum": ["allow_all", "dont_allow"], "description": "Whether the model may render people (Veo). Omit to use the backend default." },
23
31
  "json_output": { "type": "boolean", "description": "Emit a JSON result ({ url }) instead of human-readable output" }
24
32
  },
25
33
  "required": ["prompt"]
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "339",
5
5
  "version": "V8",
6
- "skillDescription": "AI 生视频技能,根据文字描述生成素材视频(调用 ab-api /model/genVideo,支持 Seedance Veo)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- AI 生视频、文生视频、文字生成视频、生成一段视频、AI 制作视频\n- 使用 doubao / 豆包 / seedance、VeoGoogle 等生成视频\n- 用户提供视频提示词并希望生成视频\n- 图生视频、首帧生成视频、参考图生成视频\n\n即使用户没有明确说「使用 AI」,只要他们想要根据描述生成视频,也要使用本 skill。"
6
+ "skillDescription": "AI video generation skill: produce a short clip from a text prompt. Backed by ab-api's `/model/genVideo` (Seedance and Veo families).\n\nUse this skill immediately whenever the user asks for any of:\n- Text-to-video, AI-generated clip, \"make a short video of ...\"\n- Generate video with Doubao / Seedance / Veo / Google\n- Image-to-video, first-frame / last-frame, reference-image-to-video\n\nEven without an explicit \"use AI\", any request that turns a description into a moving clip should route here."
7
7
  }
@@ -21,7 +21,7 @@ Wraps ab-api's unified `POST /tool/tts` with `provider: "minimax"` (the same end
21
21
 
22
22
  ## Auth & environment
23
23
 
24
- There is no skill-local env file — the executing process inherits the system environment. Examples below say `python`; on macOS you may need `python3`.
24
+ There is no skill-local env file — the executing process inherits the system environment.
25
25
 
26
26
  - **Enterprise OpenClaw**: auth is already injected, **no need** to set `PRIV_TOKEN` / `--priv-token`.
27
27
  - **Other environments**: configure the token. See the Tianyan privateToken doc internally. Without a token, non-interactive runs fail; interactive runs prompt.
@@ -95,6 +95,18 @@ remixmate gen-voice --text "<text-to-synthesize>" --json-output
95
95
  | Voice-over / narration | 1.0–1.2 |
96
96
  | Fast announcement | 1.2–1.5 |
97
97
 
98
+ ## Credits
99
+
100
+ Every run charges credits. The CLI prints a footer on stdout when it does:
101
+
102
+ ```
103
+ 💳 Charged 31 credits · balance 1,240
104
+ ```
105
+
106
+ Relay it to the user whenever it appears — it is the only signal they get about what a
107
+ generation cost, and the balance is the only warning before a run fails with
108
+ `insufficient_credits`. Do not drop it from your summary.
109
+
98
110
  ## Error handling
99
111
 
100
112
  - **401** / **token missing** (non-OpenClaw): set `PRIV_TOKEN`.
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "338",
5
5
  "version": "V7",
6
- "skillDescription": "语音合成(TTS)技能,将文字转为语音音频(调用 ab-api 统一 /tool/ttsprovider=minimax,基于 Minimax TTS)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- AI 配音、语音合成、文字转语音、TTS、文本转音频、生成语音\n- 使用 minimax 语音合成\n- 用户想要将一段文字朗读出来、生成音频\n\n即使用户没有明确说「使用 AI」,只要他们想要将文字转为语音,也要使用本 skill。"
6
+ "skillDescription": "Text-to-speech (TTS) skill: synthesize narration audio from text via ab-api's unified `/tool/tts` endpoint with `provider: \"minimax\"` (Minimax TTS).\n\nUse this skill immediately whenever the user asks for any of:\n- AI voice-over, TTS, text-to-speech, generate narration audio\n- Use Minimax for speech synthesis\n- Read a piece of text aloud / produce an audio file from text\n\nEven when the user does not explicitly say \"AI\", any request that turns text into speech should route here."
7
7
  }
@@ -195,6 +195,18 @@ The Asset Resolver handles each `AssetRef` in this order:
195
195
  4. **Parallel generation**: assets of the same type are generated in parallel; different types are sequenced by dependency.
196
196
  5. **Cache reuse**: assets with the same payload are checked against `asset-cache-dir` to avoid duplicate generation.
197
197
 
198
+ ## Credits
199
+
200
+ Every run charges credits. The CLI prints a footer on stdout when it does:
201
+
202
+ ```
203
+ 💳 Charged 31 credits · balance 1,240
204
+ ```
205
+
206
+ Relay it to the user whenever it appears — it is the only signal they get about what a
207
+ generation cost, and the balance is the only warning before a run fails with
208
+ `insufficient_credits`. Do not drop it from your summary.
209
+
198
210
  ## Error handling
199
211
 
200
212
  - **DSL validation failed**: pre-validate with `gen-script --validate`.
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "476",
5
5
  "version": "V1",
6
- "skillDescription": "Asset-preparation skill (Phase 1 of the two-phase video pipeline). Resolves and generates every asset (image / audio / video) referenced by a Video DSL, persists a RenderPlan to the database, and returns a job_id for the subsequent render_video call."
6
+ "skillDescription": "Asset-preparation skill: resolves and generates every asset (image / audio / video) referenced by a Video DSL, persists a RenderPlan to the database, and returns a `job_id` for the subsequent `render_video` call.\n\nUse this skill as soon as the user mentions any of these intents:\n- Generate / prepare video assets, resolve assets, render-ready\n- \"Make me a video about X\" (the agent calls gen_script → prepare_video_assets → render_video)\n- Regenerate one asset (image / audio) for a specific scene\n\nNext step: after the user confirms the resolved assets, call `render_video` with the `job_id` returned by this skill.\n\n⚠️ Stop-and-confirm gate: this skill runs only after the user has confirmed the script, and after it returns you must show the resolved assets and wait for the user's explicit confirmation. Never call `render_video` in the same turn."
7
7
  }
@@ -210,6 +210,18 @@ The Remotion render project lives at the monorepo root under `<monorepo-root>/re
210
210
  - Production (default): the client uses `https://api-render.remixmate.com` when `REMOTION_RENDER_API_URL` is unset — no config needed.
211
211
  - Local debugging: in the renderer directory run `npm install && npm run server:dev`, then point the client at it with `REMOTION_RENDER_API_URL=http://localhost:3000`.
212
212
 
213
+ ## Credits
214
+
215
+ Every run charges credits. The CLI prints a footer on stdout when it does:
216
+
217
+ ```
218
+ 💳 Charged 31 credits · balance 1,240
219
+ ```
220
+
221
+ Relay it to the user whenever it appears — it is the only signal they get about what a
222
+ generation cost, and the balance is the only warning before a run fails with
223
+ `insufficient_credits`. Do not drop it from your summary.
224
+
213
225
  ## Error handling
214
226
 
215
227
  - **`job_id` missing or not assets-ready**: the script exits non-zero with a message like "render job N is not in assets-ready state". The agent should call `prepare_video_assets` again with the updated DSL to regenerate, then re-call this skill with the new `job_id`.
@@ -24,6 +24,7 @@
24
24
  """
25
25
 
26
26
  import argparse
27
+ import atexit
27
28
  import builtins
28
29
  import json
29
30
  import os
@@ -356,6 +357,62 @@ def LogPrint(*args, sep=" ", end="\n", file=None, flush=False):
356
357
  builtins.print(f"[{stamp}] {message}", end=end, file=file, flush=flush)
357
358
 
358
359
 
360
+ # ─── 扣费汇总 ────────────────────────────────────────────────────────────────
361
+ # 一条视频管线的花费分散在多处:每个 gen-image / gen-voice / gen-video /
362
+ # gen-digital-human 子进程各扣一次,渲染再通过 saveManifest 扣一次。子进程是
363
+ # capture_output 起的,它们自己打的 💳 页脚会被吞掉,所以这里把子进程 stdout 里的
364
+ # billing 事件抓出来累加,最后统一报一次总账——否则用户跑完一整条管线,只知道视频
365
+ # 好了,不知道花了多少积分。
366
+ _BILLING: dict = {"credits": 0, "balance": None}
367
+
368
+
369
+ def absorb_child_billing(stdout: str) -> None:
370
+ """从子 skill 的 stdout 里收集 `__progress__` billing 事件(见 CLI src/billing.ts)。"""
371
+ for line in (stdout or "").splitlines():
372
+ line = line.strip()
373
+ if not line.startswith("{") or "__progress__" not in line:
374
+ continue
375
+ try:
376
+ event = json.loads(line)
377
+ except json.JSONDecodeError:
378
+ continue
379
+ if not isinstance(event, dict) or event.get("phase") != "billing":
380
+ continue
381
+ credits = event.get("credits")
382
+ if not isinstance(credits, (int, float)) or credits <= 0:
383
+ continue
384
+ _BILLING["credits"] += int(credits)
385
+ balance = event.get("balance")
386
+ if isinstance(balance, (int, float)):
387
+ _BILLING["balance"] = int(balance)
388
+
389
+
390
+ def print_billing_footer() -> None:
391
+ """收尾时报一次总账。通过 atexit 注册,所以 sys.exit / 中途失败也会打印
392
+ ——已经发生的扣费不该因为后面某一步失败就不告知用户。
393
+
394
+ 走 stdout 而不是 stderr:ab-agent 交给 LLM 的是 `result.stdout || result.stderr`
395
+ (mcp-tools.ts),只写 stderr 在托管 agent 里等于不可见。
396
+ """
397
+ credits = _BILLING["credits"]
398
+ balance = _BILLING["balance"]
399
+ try:
400
+ import render_job_client # type: ignore
401
+
402
+ job_billing = render_job_client.billing_summary()
403
+ if job_billing.get("credits"):
404
+ credits += int(job_billing["credits"])
405
+ # saveManifest(渲染扣费)在管线里排最后,它带回的余额最新。
406
+ if job_billing.get("balance") is not None:
407
+ balance = int(job_billing["balance"])
408
+ except Exception: # noqa: BLE001 — 报账失败绝不能影响已完成的渲染
409
+ pass
410
+ if credits <= 0:
411
+ return
412
+ suffix = f" · balance {balance:,}" if balance is not None else ""
413
+ builtins.print(f"\n💳 Charged {credits:,} credits{suffix}", flush=True)
414
+
415
+
359
416
  def sync_chrome_headless_vendor(renderer_dir: str, render_plan: dict) -> None:
360
417
  """Chrome Headless vendor 同步——实现已抽到独立模块 ``_chrome_vendor``。
361
418
 
@@ -758,6 +815,7 @@ def resolve_asset_image(asset: dict, private_token: str, timeout: int) -> dict:
758
815
 
759
816
  try:
760
817
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, stdin=subprocess.DEVNULL)
818
+ absorb_child_billing(result.stdout)
761
819
  if result.returncode == 0:
762
820
  for line in reversed(result.stdout.strip().split("\n")):
763
821
  line = line.strip()
@@ -809,6 +867,7 @@ def resolve_asset_audio(asset: dict, private_token: str, timeout: int) -> dict:
809
867
 
810
868
  try:
811
869
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, stdin=subprocess.DEVNULL)
870
+ absorb_child_billing(result.stdout)
812
871
  if result.returncode == 0:
813
872
  for line in reversed(result.stdout.strip().split("\n")):
814
873
  line = line.strip()
@@ -854,6 +913,7 @@ def resolve_asset_video(asset: dict, private_token: str, timeout: int) -> dict:
854
913
 
855
914
  try:
856
915
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, stdin=subprocess.DEVNULL)
916
+ absorb_child_billing(result.stdout)
857
917
  if result.returncode == 0:
858
918
  for line in reversed(result.stdout.strip().split("\n")):
859
919
  line = line.strip()
@@ -893,6 +953,7 @@ def resolve_asset_digital_human(asset: dict, private_token: str, timeout: int) -
893
953
  dh_timeout = max(timeout, 660)
894
954
  try:
895
955
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=dh_timeout, stdin=subprocess.DEVNULL)
956
+ absorb_child_billing(result.stdout)
896
957
  if result.returncode == 0:
897
958
  for line in reversed(result.stdout.strip().split("\n")):
898
959
  line = line.strip()
@@ -1892,6 +1953,8 @@ def _log_render_plan_summary(render_plan: dict) -> None:
1892
1953
 
1893
1954
 
1894
1955
  def main():
1956
+ # 无论正常结束、sys.exit 还是中途失败,都在最后报一次积分账(无扣费时静默)。
1957
+ atexit.register(print_billing_footer)
1895
1958
  parser = argparse.ArgumentParser(
1896
1959
  description="Video render tool — DSL + TemplateBinding → Remotion video.",
1897
1960
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "473",
5
5
  "version": "V19",
6
- "skillDescription": "Final-render skill (Phase 3 of the two-phase video pipeline). Loads a persisted RenderPlan by job_id and drives Remotion to produce the final video. Assets must already be generated via prepare_video_assets."
6
+ "skillDescription": "Final-render skill: loads a persisted RenderPlan by `job_id` and drives the Remotion engine to produce the final video.\n\nUse this skill as soon as the user mentions any of these intents (after assets are already prepared):\n- Render the video, composite the video, export the video\n- Turn the prepared assets into the final clip\n- Render with Remotion\n\nPrerequisite: assets must already be generated via `prepare_video_assets`. This skill never resolves or regenerates assets — pass it a `job_id` from a previous `prepare_video_assets` call.\n\n⚠️ Stop-and-confirm gate: never call this skill until the user has explicitly confirmed the assets prepared by `prepare_video_assets`. If those assets were prepared in the current turn and the user has not replied since, stop and ask instead of rendering."
7
7
  }
@@ -37,6 +37,31 @@ def _make_headers(priv_token: str) -> dict:
37
37
  return headers
38
38
 
39
39
 
40
+ # ─── 扣费回传 ────────────────────────────────────────────────────────────────
41
+ # ab-api 会在扣了积分的成功响应上挂 billing 字段(见 core.Success)。渲染积分是在
42
+ # /renderJob/saveManifest 里扣的,所以这里是渲染花费唯一的可见入口——累加起来交给
43
+ # render_video.py 在收尾时统一告知用户。
44
+ _BILLING: dict = {"credits": 0, "balance": None}
45
+
46
+
47
+ def _record_billing(result: dict) -> None:
48
+ billing = result.get("billing")
49
+ if not isinstance(billing, dict):
50
+ return
51
+ credits = billing.get("credits")
52
+ if not isinstance(credits, (int, float)) or credits <= 0:
53
+ return
54
+ _BILLING["credits"] += int(credits)
55
+ balance = billing.get("balance")
56
+ if isinstance(balance, (int, float)):
57
+ _BILLING["balance"] = int(balance)
58
+
59
+
60
+ def billing_summary() -> dict:
61
+ """本进程通过本模块产生的累计扣费:{"credits": int, "balance": int|None}。"""
62
+ return dict(_BILLING)
63
+
64
+
40
65
  def _post(path: str, payload: dict, priv_token: str, timeout: int = 30) -> dict:
41
66
  """发送 POST 请求并返回响应 data 字段;失败时抛出 RuntimeError。"""
42
67
  url = f"{_api_base()}{path}"
@@ -50,6 +75,8 @@ def _post(path: str, payload: dict, priv_token: str, timeout: int = 30) -> dict:
50
75
  except urllib.error.URLError as e:
51
76
  raise RuntimeError(f"network request failed ({path}): {e.reason}") from e
52
77
 
78
+ _record_billing(result)
79
+
53
80
  if result.get("code") != 0:
54
81
  msg = result.get("msg") or result.get("message") or "unknown error"
55
82
  raise RuntimeError(f"API returned an error [{path}]: {msg}")
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "475",
5
5
  "version": "V13",
6
- "skillDescription": "视频模板仓库(列表查询)。存储所有视频模板定义,对外只暴露「列出可用模板」一个能力;DSL→TemplateBinding 的绑定逻辑已内嵌进 prepare-video-assets,不再作为独立步骤暴露。同时是跨 skill 共享 Python 库(registry_loader / match_template / template_paths / video_dsl 等)的存放位置。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 查看可用模板、列出所有模板"
6
+ "skillDescription": "Video-template registry skill. Stores every video-template definition and lists the available templates (templateId / name / aspect ratio / style tags).\n\nUse this skill as soon as the user mentions any of these intents:\n- View available templates / list every template\n\nNote: DSL→TemplateBinding is no longer a separate exposed step — once prepare_video_assets receives a template_id it builds the binding internally."
7
7
  }
@@ -3,5 +3,5 @@
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "553",
5
5
  "version": "V2",
6
- "skillDescription": "视频解构技能,将视频拆解为可复用的内容资产(音频、ASR 文本、关键帧、场景分段)。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 视频解构、视频拆解、视频分析、解析视频\n- 提取关键帧、提取视频文案、视频转文字\n- 视频内容资产、视频素材提取\n\n即使用户没有明确说「解构」,只要他们想要从视频中提取文案、关键帧或结构化信息,也要使用本 skill"
6
+ "skillDescription": "Video deconstruction skill. Splits a video into reusable content assets — audio, ASR transcript, scene segments, keyframe images.\n\nUse this skill as soon as the user mentions any of these intents:\n- Deconstruct / split / analyze / parse a video\n- Extract keyframes, extract the script from a video, transcribe a video\n- Pull content assets or raw material out of a video\n\nEven when the user does not say \"deconstruct\" explicitly, use this skill whenever they want to extract script text, keyframes, or structured information from a video."
7
7
  }