cawcut-ad-video

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CawCut Ad Video

CawCut 广告视频

Create a complete product ad video through the 5-stage AD Video wizard: Product → Config → Idea → Storyboard → Video. Each stage maps to
cawcut ad-video
commands against the developer API. Full command flag tables live in
references/api.md
; stage semantics and the invalidation matrix live in
references/stages.md
; authoring a script edit lives in
references/script-editing.md
; error recovery lives in
references/troubleshooting.md
.
Read
them at
<base_dir>/references/<file>.md
using the base directory printed at the top of this skill body — do not
find
/
grep
for them.
Reply language. All live replies — prose, status updates,
AskUserQuestion
/
AskQuestion
questions and option labels — follow the user's language for that turn. This is judged from the user's actual chat message content for that turn — not from whether the
Skill
tool call itself carried an
args
parameter, which is an unrelated, optional pass-through field. This includes implicit triggers: if the skill was invoked because the user's message matched a trigger phrase (e.g. "广告视频", "商品广告", "make an ad for …") rather than the literal
/cawcut-ad-video
command, that trigger phrase is the user's chat message content for the turn — reply in its language from the very first response, including when asking for the missing product URL. Only default to English when the invocation carries no language signal at all (e.g. literally just
/cawcut-ad-video
alone with no other text); once the user adds a real request in another language, follow that language for the rest of the session. Keep CLI commands/flags, JSON keys, URLs, and raw error codes in their original form regardless of reply language, and never paste raw English CLI output as the user-facing answer — summarize it in
reply_language
.
通过 5 阶段广告视频向导创建完整的产品广告视频:产品 → 配置 → 创意 → 分镜 → 视频。每个阶段都对应一组针对开发者 API 的
cawcut ad-video
命令。完整命令参数表位于
references/api.md
;各阶段语义和失效矩阵位于
references/stages.md
;脚本编辑方法位于
references/script-editing.md
;错误恢复方法位于
references/troubleshooting.md
。使用此 skill 正文顶部打印的基础目录,通过
Read
读取
<base_dir>/references/<file>.md
——切勿使用
find
或
grep
查找这些文件。
回复语言。 所有实时回复——说明文字、状态更新、
AskUserQuestion
/
AskQuestion
问题及选项标签——都应遵循用户本轮使用的语言。判断依据是用户本轮实际聊天消息的内容,而不是
Skill
工具调用是否携带
args
参数;后者只是一个无关的可选透传字段。这也包括隐式触发:如果 skill 是因为用户消息匹配了触发短语(例如“广告视频”、“商品广告”、“make an ad for …”)而被调用,那么该触发短语就是本轮用户的聊天消息内容——从第一次回复开始就应使用该语言,包括询问缺失的产品 URL 时。只有在调用完全没有语言信号时(例如仅输入
/cawcut-ad-video
且没有其他文本),才默认使用英语;一旦用户用另一种语言提出实际请求,本会话余下时间都应使用该语言。无论回复语言如何,CLI 命令和参数、JSON 键、URL 及原始错误代码都应保持原样;绝不能将原始英文 CLI 输出直接作为面向用户的答案,而应在
reply_language
中进行总结。

Step 0 — Bootstrap

第 0 步 — 启动

Choose the login mode by execution environment

根据执行环境选择登录模式

Choose by where commands actually run. On the user's own computer, run
cawcut auth login --browser --json
; the CLI opens the browser and waits for approval. In a remote environment, VM, or container without access to the user's browser, run
cawcut auth login --device-auth --json
and send the user
authorization_url
to open and approve on their own computer. Both modes support JSON; needing structured output is not a reason to choose device authorization. Agent names, non-TTY shells, and localhost URLs alone do not establish where commands run. Use the known execution context and explicit mode flags to override CLI auto-detection.
For device login, then run
cawcut auth login --complete --wait --timeout 60 --json
. If your tool has a short execution timeout, use single
--complete --json
calls instead, following
next_poll_after_seconds
. After device login is interrupted or returns
wait_timeout
, resume the saved current request rather than creating a new link. Browser login uses the legacy loopback callback; if interrupted, restart
cawcut auth login --browser --json
instead of using
--complete
.
status=pending
is not authentication success. Only retry the original operation after
status=authenticated
. Denial/expiry ends this request; do not silently create an endless sequence of authorization links. Never ask for a password, token, or a code copied back from the web page.
Run bootstrap as a silent guardrail, not as a user-facing phase.
  1. At most once per AI session. If any
    cawcut
    command has already succeeded in this AI session, skip all bootstrap checks and continue.
  2. Use only one explicit check command:
    cawcut upgrade check --json
    .
    • If
      "update_available": true
      , run
      cawcut upgrade
      yourself in the host shell
      (do not ask the user to type it). After it succeeds, continue this skill from Step 1.
    • If
      "ahead_of_registry": true
      , continue without upgrading.
    • If
      cawcut
      is missing, ask the user to install it:
      npm install -g @ubnt/cawcut
      . Then continue bootstrap.
    • For other check failures, continue and mention the warning only if a later CLI command fails.
  3. Do not print "bootstrap checks passed" — move directly to Step 1.
  4. Let the first real command validate auth. If it fails with token/auth errors (including
    Token expired
    ), follow the login steps above. Use the mode appropriate to the execution environment, and retry the failed command once only after
    status=authenticated
    . Run the CLI commands yourself; the user only approves in their browser.
  5. Structured user-ask tool (check every session). In Claude Code, use
    AskUserQuestion
    for every enumerable choice; in Cursor, use
    AskQuestion
    . If either is available, you must use it for all enumerable decisions in this skill — platform, resolution, duration, video type, storyboard style, language, idea candidate, credit confirmation — including when the candidate list is longer than the popup holds: a long list still opens the tool, with its leading entries as the options and the full numbered list beside them so any later entry can be typed back (see Interactive selection).
根据命令实际运行的位置选择模式。在用户自己的计算机上,运行
cawcut auth login --browser --json
;CLI 会打开浏览器并等待批准。在无法访问用户浏览器的远程环境、VM 或容器中,运行
cawcut auth login --device-auth --json
,并将
authorization_url
发给用户,让用户在自己的计算机上打开并批准。两种模式都支持 JSON;需要结构化输出并不构成选择设备授权的理由。Agent 名称、非 TTY shell 和 localhost URL 本身不能说明命令的实际运行位置。应使用已知的执行上下文和显式模式参数覆盖 CLI 的自动检测。
完成设备登录后,运行
cawcut auth login --complete --wait --timeout 60 --json
。如果工具的执行超时时间较短,则改为单次调用
--complete --json
,并遵循
next_poll_after_seconds
。设备登录被中断或返回
wait_timeout
后,应恢复已保存的当前请求,而不是创建新链接。浏览器登录使用旧版 loopback callback;如果被中断,应重新运行
cawcut auth login --browser --json
,而不是使用
--complete
。
status=pending
不代表认证成功。只有在
status=authenticated
后,才能重试原始操作。拒绝或过期意味着本次请求结束;不要无休止地创建授权链接。绝不能要求用户提供密码、token,或从网页复制验证码。
将启动过程作为静默护栏执行,而不是面向用户的阶段。
  1. 每个 AI 会话最多执行一次。 如果本 AI 会话中已有任何
    cawcut
    命令成功执行,则跳过所有启动检查并继续。
  2. 只使用一条显式检查命令:
    cawcut upgrade check --json
    。
    • 如果
      "update_available": true
      ,应在宿主 shell 中亲自运行
      cawcut upgrade
      (不要让用户手动输入)。成功后,从第 1 步继续。
    • 如果
      "ahead_of_registry": true
      ,无需升级,直接继续。
    • 如果缺少
      cawcut
      ,请用户安装:
      npm install -g @ubnt/cawcut
      。然后继续启动流程。
    • 如果发生其他检查失败,继续执行;仅当后续 CLI 命令失败时再提示该警告。
  3. 不要输出“启动检查已通过”——直接进入第 1 步。
  4. 让第一条真实命令验证认证。如果命令因 token 或认证错误而失败(包括
    Token expired
    ),执行上述登录步骤。选用适合当前执行环境的模式,并且仅在
    status=authenticated
    后重试失败命令一次。应亲自运行 CLI 命令;用户只需在浏览器中批准。
  5. 结构化用户询问工具(每个会话都要检查)。 在 Claude Code 中,对每个可枚举选项都使用
    AskUserQuestion
    ;在 Cursor 中使用
    AskQuestion
    。如果任一工具可用,则本 skill 中所有可枚举决策都必须使用它——包括平台、分辨率、时长、视频类型、分镜风格、语言、创意候选项和 credits 确认——即使候选项数量超过弹窗容量也一样:长列表仍应打开工具,前几项作为选项显示,并在完整编号列表旁展示所有候选项,以便用户输入后续条目的编号(参见“交互式选择”)。

Wizard overview

向导概览

① Product    create → analyze run → poll → review/confirm analysis
② Config     options → pick settings → config set
③ Idea       idea run → poll → pick candidate (or custom) → idea select
④ Storyboard storyboard run → poll → (optional edit / regenerate) → image quote → confirm → image run (×2 if hand-drawn)
⑤ Video      video quote → confirm → video run → poll → download
One project = one
video_ad_id
. Every long stage is triggered without
--wait
, then polled with the bundled script (see Polling).
cawcut ad-video detail <id> --json
is the single polling point
— only it returns the
outputs
map.
① Product    create → analyze run → poll → review/confirm analysis
② Config     options → pick settings → config set
③ Idea       idea run → poll → pick candidate (or custom) → idea select
④ Storyboard storyboard run → poll → (optional edit / regenerate) → image quote → confirm → image run (×2 if hand-drawn)
⑤ Video      video quote → confirm → video run → poll → download
一个项目对应一个
video_ad_id
。每个长阶段都应先不带
--wait
触发,再使用内置脚本轮询(参见“轮询”)。
cawcut ad-video detail <id> --json
是唯一的轮询点
——只有它会返回
outputs
映射。

Bundled-script runner

内置脚本运行器

Use
cawcut
CLI commands directly for project operations and detail inspection. Choose bundled helpers for the host platform. Windows helpers require PowerShell and the installed
cawcut
CLI, not Python, jq, bash, or external curl; do not ask Windows users to install those tools for this skill. macOS/Linux
.sh
helpers require jq; neither platform uses Python.
  • macOS/Linux:
    bash <base_dir>/scripts/<name>.sh …
  • Windows PowerShell:
    powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\<name>.ps1" …
The three helpers (
poll
,
script_plan
,
download_media
) have equivalent
.sh
and
.ps1
implementations. Use the matching extension and platform-specific flags throughout one run; for example,
--kind video
on bash is
-Kind video
on PowerShell.
项目操作和详情检查应直接使用
cawcut
CLI 命令。根据宿主平台选择内置辅助脚本。Windows 辅助脚本需要 PowerShell 和已安装的
cawcut
CLI,而不是 Python、jq、bash 或外部 curl;不要要求 Windows 用户为此安装这些工具。macOS/Linux 的
.sh
辅助脚本需要 jq;两个平台都不使用 Python。
  • macOS/Linux:
    bash <base_dir>/scripts/<name>.sh …
  • Windows PowerShell:
    powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\<name>.ps1" …
三个辅助脚本(
poll
、
script_plan
、
download_media
)均提供等效的
.sh
和
.ps1
实现。一次运行期间应始终使用匹配的扩展名和平台专用参数;例如,bash 中的
--kind video
在 PowerShell 中对应
-Kind video
。

Stage ① — Product

第 ① 阶段 — 产品

  1. Collect the product URL (Amazon/Shopify/official page all work). If the user didn't give one, ask in plain text (open-ended). Do NOT ask for a project name — the backend auto-names the project from the product info after analysis/export.
  2. cawcut ad-video create --url "<url>" --json
    → capture
    id
    .
    --url
    is optional at create (an empty draft is allowed), but pass it whenever the user gave one —
    analyze run
    requires it later regardless.
    --category
    is optional; omit it and the backend auto-detects after analysis. Only pass
    --name
    if the user explicitly asked for a specific title.
  3. cawcut ad-video analyze run <id> --url "<the product URL from step 1>" --json
    (no
    --wait
    ; the API requires
    url
    in the body), then poll with the platform runner:
    poll.sh <id> '.status' stage_completed 600 15
    on macOS/Linux, or
    poll.ps1 <id> '.status' stage_completed 600 15
    on Windows.
  4. Read
    detail <id> --json
    and present the analysis result:
    • Product info verbatim — every populated
      product_info
      field:
      name
      ,
      description
      ,
      selling_points[]
      (each in full, numbered),
      target_audience
      ,
      brand_style
      ,
      promotional_info
      ,
      source_platform
      ,
      category
      . Do not summarize, truncate, or translate the product content itself — it is the user's marketing copy and must be shown as returned; only your surrounding labels and prose follow reply_language.
    • Product images — first download them with the platform-specific
      download_media
      helper:
      <id> --kind product
      . Then present by category (labels in reply_language):
      • Logo (
        logo_images
        ) and Primary images (
        primary_images
        ): preview each inline via the host's send-file tool (e.g. Claude's
        SendUserFile
        ) with the local path just downloaded (e.g.
        ~/Downloads/ad-video-<name>/product/logo-1.jpg
        ,
        primary-1.jpg
        ) — a markdown link only renders as a clickable line, not a visible image.
      • Additional product images (
        images[]
        ): if inline previews are supported, list them one by one, each labeled
        Product Image #n
        with the image sent inline; cap at the first 9 — if there are more, say so (e.g.
        …and N–9 more
        ) and tell the user the rest are in the
        <dest>/product/
        folder. If inline previews are not supported, list them as a table of local paths (
        #
        + path) and point to the same folder.
      • No
        asset_id
        column in either form — the local path is a preview reference only. These files exist so the user can see the images; never hand them back to the CLI. Then ask the user to confirm or correct via AskUserQuestion:
    • ✅ Looks good
      → continue to Stage ②
    • ✏️ Edit product info
      → collect the changes and pass only those fields to
      cawcut ad-video product set <id> …
      : the command reads the project first and carries every field you did not pass forward. (The API replaces
      product_info
      wholesale, so a bare
      --product-name
      would otherwise erase the description, the images and the selling points.) Re-run
      analyze run
      afterwards if the URL or the product changed materially.
      • An image list is replaced by passing it, so
        --logo
        /
        --primary-image
        /
        --asset
        take the complete kept list — that is also how an image is removed. A replaced list drops its own selection, and the backend falls back to its defaults (every candidate for
        --asset
        , the first candidate for logo/primary). Never re-send the files you downloaded: the persisted refs already come back as
        asset_id
        s.
      • Pass a local path only for an image the user supplies from outside the project; the CLI uploads it as a brand-new asset (7-day TTL).
      • --description ""
        clears the description and
        --clear-selling-points
        empties the selling points — an empty list is a real state, not "unset".
  5. If the user wants to abort a running analysis:
    cawcut ad-video analyze cancel <id>
    .
Material rights (state this once, when the user supplies their own image). Images uploaded through the CLI are not run through the material compliance check and are accepted as-is — there is no rights attestation step to walk through, unlike the web portal. That check exists to catch third-party logos and celebrity likeness; on this path the responsibility for holding the rights to the material sits with the user. Say so plainly in one sentence before uploading, so nobody assumes the tool vetted it. This applies only to new files the user supplies: images already in the project (crawler-extracted product images) were never subject to it.
  1. 收集产品 URL(Amazon、Shopify 或官网均可)。如果用户没有提供,应使用纯文本开放式提问。不要询问项目名称——后端会在分析或导出后根据产品信息自动命名项目。
  2. cawcut ad-video create --url "<url>" --json
    → 捕获
    id
    。创建时
    --url
    是可选项(允许创建空草稿),但只要用户提供了 URL 就应传入——后续的
    analyze run
    无论如何都要求提供 URL。
    --category
    是可选项;应将其省略,由后端在分析后自动检测。只有用户明确要求特定标题时,才传入
    --name
    。
  3. cawcut ad-video analyze run <id> --url "<the product URL from step 1>" --json
    (不要使用
    --wait
    ;API 要求请求体中包含
    url
    ),然后使用平台对应的运行器轮询:macOS/Linux 使用
    poll.sh <id> '.status' stage_completed 600 15
    ,Windows 使用
    poll.ps1 <id> '.status' stage_completed 600 15
    。
  4. 读取
    detail <id> --json
    并展示分析结果:
    • 逐字展示产品信息——每个已填充的
      product_info
      字段:
      name
      、
      description
      、
      selling_points[]
      (每项完整展示并编号)、
      target_audience
      、
      brand_style
      、
      promotional_info
      、
      source_platform
      、
      category
      。不要总结、截断或翻译产品内容本身——这是用户的营销文案,必须按返回结果原样展示;只有周边标签和说明应遵循
      reply_language
      。
    • 产品图片——先使用平台对应的
      download_media
      辅助脚本下载:
      <id> --kind product
      。然后按类别展示(标签遵循
      reply_language
      ):
      • Logo(
        logo_images
        )和主图(
        primary_images
        ):使用宿主的文件发送工具(例如 Claude 的
        SendUserFile
        )逐个以内联方式预览,并提供刚下载的本地路径(例如
        ~/Downloads/ad-video-<name>/product/logo-1.jpg
        、
        primary-1.jpg
        )。Markdown 链接只会显示为可点击文本,无法呈现可见图片。
      • 其他产品图片(
        images[]
        ):如果支持内联预览,应逐张列出,每张标记为
        Product Image #n
        并以内联方式发送图片;最多展示前 9 张。如果图片更多,应说明剩余数量(例如
        …and N–9 more
        ),并告知其余图片位于
        <dest>/product/
        文件夹。如果不支持内联预览,则以本地路径表格(
        #
        加路径)列出,并指向同一文件夹。
      • 两种形式都不得包含
        asset_id
        列——本地路径只是预览引用。这些文件用于让用户查看图片;绝不能将其回传给 CLI。 然后通过 AskUserQuestion 请求用户确认或修正:
    • ✅ 看起来没问题
      → 继续第 ② 阶段。
    • ✏️ 编辑产品信息
      → 收集修改内容,并且仅将发生变化的字段传给
      cawcut ad-video product set <id> …
      :该命令会先读取项目,并保留所有未传入的字段。(API 会整体替换
      product_info
      ,因此只传
      --product-name
      会错误地清除描述、图片和卖点。)如果 URL 或产品发生实质性变化,之后应重新运行
      analyze run
      。
      • 图片列表会因传入而被替换,因此
        --logo
        /
        --primary-image
        /
        --asset
        必须传入完整的保留列表——这也是删除图片的方式。替换后的列表会丢弃自身选择,后端随后回退到默认值(
        --asset
        使用所有候选项,logo 和主图使用第一个候选项)。绝不能重新发送下载的文件:已持久化的引用会以
        asset_id
        形式返回。
      • 只有当用户提供项目之外的新图片时,才传入本地路径;CLI 会将其作为全新素材上传(TTL 为 7 天)。
      • --description ""
        会清空描述,
        --clear-selling-points
        会清空卖点——空列表是一种真实状态,并不表示“未设置”。
  5. 如果用户希望中止正在运行的分析:
    cawcut ad-video analyze cancel <id>
    。
素材权利(仅在用户提供自己的图片时说明一次)。 通过 CLI 上传的图片不会经过素材合规检查,而是被原样接受——此路径没有需要完成的版权确认步骤,而 Web 门户存在该步骤。该检查用于发现第三方 logo 和名人肖像;在这条路径上,持有素材权利的责任由用户承担。上传前应清楚地用一句话说明这一点,以免用户误以为工具已经审核过素材。这仅适用于用户提供的新文件;项目中已有的图片(爬虫提取的产品图片)从未经过该检查。

Stage ② — Config (all options are runtime-fetched, never hardcoded)

第 ② 阶段 — 配置(所有选项均在运行时获取,绝不硬编码)

  1. cawcut ad-video options --video-ad-id <id> --json
    → read
    video_settings
    (publish_to, languages, aspect_ratios, resolutions, duration_ranges, video_types, storyboard_styles, video_models, defaults).
  2. Ask the axes in this order — video type first, because it decides both whether captions are a question at all and which model/resolution combinations are legal.
    • video type — one option per entry in
      video_types
      .
    • video model — only when
      video_models
      is non-empty.
      Candidates are
      video_models[].value
      , default
      defaults.video_model
      . The table that carries the list has three columns beyond
      #
      : the model, its
      supported_resolutions
      , and a default mark on
      defaults.video_model
      — the resolutions belong in view because picking a model is what decides which ones stay legal on the next axis. With exactly one candidate, state it in prose and carry on (one option is not a choice); with none, skip the axis entirely.
    • resolution — default
      defaults.resolution
      , then clamp to the chosen model's
      supported_resolutions
      (the same list the server validates against). Never offer a resolution the model does not support:
      config set
      rejects it. When no model was chosen, offer the full
      resolutions
      list.
    • auto captions — only for
      product_with_voiceover
      and
      talking_video
      .
      Captions are burned in from the voiceover/dialogue track, so
      product_showcase
      has nothing to caption; do not ask, and pass
      --auto-captions false
      explicitly (mirrors the web portal, which forces the value off for that type).
    • platform — show each option's
      default_aspect_ratio
      in its description (e.g.
      TikTok — 9:16
      ), language, duration, storyboard style (explain: hand-drawn adds a sketch pass before photo-realistic panels).
    • Aspect ratio is NOT a question. It is bound to the platform (mirrors the web portal, which has no ratio selector): take the picked
      publish_to
      option's
      default_aspect_ratio
      automatically. Never ask for a ratio separately; only if the user explicitly overrides should you deviate from the platform default.
    Batch the axes into one AskUserQuestion call — one question per axis, at most four questions per call. A long axis (platform, language, video model) follows Interactive selection's overflow shape: the full list is a numbered table in the message body —
    #
    plus what that axis actually carries: a platform's label with its
    default_aspect_ratio
    , a language's label, a model's label with its
    supported_resolutions
    and default mark. The question itself is one short sentence, the popup holds the leading entries as options labelled with their number, and the entries past them are answered by number or name in the free-text field. Each axis keeps its own table and its own numbering — the answer goes into that axis's own field, so never ask for two numbers at once (
    1 + 10
    ).
  3. cawcut ad-video config set <id> --publish-to … --language … --aspect-ratio <the platform's default_aspect_ratio> --resolution … --duration-seconds … --video-type … --video-model … --storyboard-style … --auto-captions … --json
video_model
is a real cost lever — say so when offering it (a cheaper model may be available), but never present a model the endpoint did not return.
  1. cawcut ad-video options --video-ad-id <id> --json
    → 读取
    video_settings
    (
    publish_to
    、
    languages
    、
    aspect_ratios
    、
    resolutions
    、
    duration_ranges
    、
    video_types
    、
    storyboard_styles
    、
    video_models
    、
    defaults
    )。
  2. 按以下顺序询问各维度——先询问视频类型,因为它既决定是否需要询问字幕,也决定哪些模型与分辨率组合合法。
    • 视频类型——
      video_types
      中的每一项对应一个选项。
    • 视频模型——**仅当
      video_models
      非空时询问。**候选项为
      video_models[].value
      ,默认值为
      defaults.video_model
      。承载该列表的表除
      #
      外还有三列:模型、
      supported_resolutions
      ,以及
      defaults.video_model
      中的默认标记
      ——分辨率必须显示,因为选择模型正是决定下一维度中哪些分辨率仍合法的原因。如果只有一个候选项,应在说明文字中直接给出并继续(一个选项不构成选择);如果没有候选项,则完全跳过该维度。
    • 分辨率——默认值为
      defaults.resolution
      ,然后限制到所选模型的
      supported_resolutions
      范围内(与服务器用于验证的列表相同)。绝不能提供模型不支持的分辨率,否则
      config set
      会拒绝请求。没有选择模型时,提供完整的
      resolutions
      列表。
    • 自动字幕——**仅适用于
      product_with_voiceover
      和
      talking_video
      。**字幕会从旁白或对话音轨中烧录进画面,因此
      product_showcase
      没有可添加字幕的内容;不要询问,并明确传入
      --auto-captions false
      (这与 Web 门户一致,后者会强制该类型关闭字幕)。
    • 平台——在每个选项的说明中显示其
      default_aspect_ratio
      (例如
      TikTok — 9:16
      ),以及语言、时长、分镜风格(说明:手绘风格会在照片级画面之前增加草图处理步骤)。
    • **宽高比不是问题项。**它与平台绑定(与 Web 门户一致,后者没有宽高比选择器):应自动采用所选
      publish_to
      选项的
      default_aspect_ratio
      。绝不能单独询问宽高比;只有用户明确覆盖时,才偏离平台默认值。
    将这些维度合并到一次 AskUserQuestion 调用中——每个维度一个问题,每次调用最多四个问题。长维度(平台、语言、视频模型)应遵循“交互式选择”的溢出形式:完整列表以编号表格显示在消息正文中——
    #
    加该维度实际包含的信息:平台的
    label
    和
    default_aspect_ratio
    、语言的
    label
    、模型的
    label
    、
    supported_resolutions
    和默认标记。问题本身应是一句简短的话;弹窗将靠前条目作为选项显示,弹窗无法容纳的条目可通过自由文本输入。每个维度都有自己的表格和编号——答案应进入该维度自己的字段,因此绝不能同时要求输入两个编号(如
    1 + 10
    )。
  3. cawcut ad-video config set <id> --publish-to … --language … --aspect-ratio <the platform's default_aspect_ratio> --resolution … --duration-seconds … --video-type … --video-model … --storyboard-style … --auto-captions … --json
video_model
是实际影响成本的杠杆——提供选项时应说明这一点(可能有价格更低的模型),但绝不能呈现 endpoint 未返回的模型。

Stage ③ — Idea

第 ③ 阶段 — 创意

  1. cawcut ad-video idea run <id> --json
    , poll until
    outputs.generated_marketing_messages.status === "succeeded"
    .
  2. Reveal up to eight, then stop. One run generates up to 16 candidates but
    detail
    hands back one page (4) at a time, so a fresh read shows only the first four. Read
    outputs.generated_marketing_messages
    , and call
    cawcut ad-video idea load-more <id> --json
    (re-reading
    detail
    each time) until eight are visible or
    has_more === false
    — whichever comes first:
    • load-more
      only reveals already-generated candidates — no LLM call, no credit cost.
    • Eight is the ceiling, not a target. A project holding fewer shows fewer; once eight are visible you stop revealing even if
      has_more
      is still true. This flow has no "one more batch" step.
    • State the number you are showing, and never imply the set is everything the project holds when it is not.
  3. Present them as a table, in full, verbatim — one row per candidate:
    #
    , title, content. No summarizing, no paraphrasing, no truncating, no translating: this is the user's marketing copy and they are choosing between the whole set. Lead with the count (e.g. "Showing 8 candidates:"). The
    #
    column is the popup's index — this table is what any number the popup could not hold is typed against.
  4. One decision, one question. The choice is a single AskUserQuestion — never a candidate question followed by a second one asking what to do with that answer. Four options, in this order:
    • 1. <title>
      ,
      2. <title>
      ,
      3. <title>
      — the first three candidates, each labelled with its number
    • ✏️ Write my own
      → submit the idea text the user or agent produced with
      idea select <id> --custom "<text>"
      (60–10000 chars, mirroring the web custom-idea limits). This follows the portal custom path: the CLI sends that text as
      custom_content
      with
      is_custom: true
      ; do not turn it into a generated-candidate selection or add a title wrapper.
    • a candidate from the fourth to the eighth is selected by typing its number or its title into the free-text field; name that in the question's own text, in
      reply_language
      , so the user answers from a set the question stated rather than guessing
  5. On a pick:
    cawcut ad-video idea select <id> --message-id <mm-N> --title "<title>" --content "<content>" --json
    . Offer light edits to title/content before submitting.
  6. A fresh batch is user-initiated only.
    idea regenerate
    is not a menu entry: when the user asks for one in their own words ("换一批" / "regenerate"), run
    cawcut ad-video idea regenerate <id> --json
    + poll — it clears the current selection, so warn first — then re-reveal (step 2) and re-ask (step 4).
  1. cawcut ad-video idea run <id> --json
    ,轮询直到
    outputs.generated_marketing_messages.status === "succeeded"
    。
  2. 最多展示八项,然后停止。一次运行最多生成 16 个候选项,但
    detail
    每次只返回一页(4 个),因此新读取时只能看到前四项。读取
    outputs.generated_marketing_messages
    ,并调用
    cawcut ad-video idea load-more <id> --json
    (每次都重新读取
    detail
    ),直到
    已显示八项
    或
    has_more === false
    ,以先达到者为准:
    • load-more
      只会显示已经生成的候选项——不会调用 LLM,也不会消耗 credits。
    • **八项是上限,而不是目标。**如果项目中只有更少的候选项,就显示实际数量;显示八项后,即使
      has_more
      仍为 true 也应停止。本流程没有“再加载一批”的步骤。
    • 应说明当前展示的数量;当项目仍持有更多候选项时,绝不能让用户误以为展示的是全部内容。
  3. 以表格形式完整、逐字展示候选项——每行一个候选项:
    #
    、标题、内容。不要总结、改写、截断或翻译:这是用户的营销文案,用户需要在完整候选内容之间进行选择。以数量开头,例如“正在展示 8 个候选项:”。
    #
    列是弹窗的索引——弹窗无法容纳的条目将以此表格中的编号作为输入依据。
  4. 一个决策,只问一个问题。选择必须是单次 AskUserQuestion——绝不能先询问候选问题,再用第二个问题询问如何处理第一个问题的答案。提供四个选项,顺序如下:
    • 1. <title>
      、
      2. <title>
      、
      3. <title>
      ——前三个候选项,每个都带编号。
    • ✏️ 自行编写
      → 提交用户或 Agent 生成的创意文本,使用
      idea select <id> --custom "<text>"
      (60–10000 个字符,与 Web 自定义创意的限制一致)。这遵循门户的自定义路径:CLI 会将该文本作为
      custom_content
      发送,并设置
      is_custom: true
      ;不要将其改为生成候选项选择,也不要添加标题包装。
    • 第四至第八个候选项可通过在自由文本字段中输入编号或标题进行选择。问题自身的文字应使用
      reply_language
      ,并说明用户应从已列出的候选项中回答,避免用户猜测。
  5. 选定后:
    cawcut ad-video idea select <id> --message-id <mm-N> --title "<title>" --content "<content>" --json
    。提交前可对标题或内容进行少量编辑。
  6. 新一批创意只能由用户主动触发。
    idea regenerate
    不是菜单项:当用户用自己的话提出要求时(“换一批”/“regenerate”),运行
    cawcut ad-video idea regenerate <id> --json
    并轮询——该操作会清除当前选择,因此必须先发出警告——然后重新显示候选项(第 2 步)并重新询问(第 4 步)。

Stage ④ — Storyboard

第 ④ 阶段 — 分镜

  1. cawcut ad-video storyboard run <id> --json
    (optional
    --style-id
    from the options storyboard section), poll until
    outputs.storyboard_plan.status === "succeeded"
    — the 3600 deadline from the polling table, which is a give-up line and never a figure to quote.
  2. Read
    detail <id> --json
    →
    outputs.storyboard_plan.value
    . Show the plan as a concise shot table. The table contains the user-reviewable fields only; prompts remain in the stored plan but are not displayed:
    • plan level, above the table:
      title
      ,
      synopsis
    • per shot, in this order:
      shot_no
      (Shot #),
      duration
      (Duration),
      shot_type
      (Shot Type),
      shot_description
      (Shot Description),
      dialogue
      (Voiceover — that is its business meaning),
      camera
      (Camera),
      light
      (Light),
      sfx
      (SFX)
    • Omit the Voiceover column for
      product_showcase
      (the web portal hides it too — that type has no spoken track).
    • Never show
      shot_prompt
      ,
      video_prompt
      , or their statuses in the table or elsewhere in the reply. They are implementation fields, not review content.
    Then ask via AskUserQuestion:
    • ✅ Looks good
      → continue
    • ✏️ Work on the script
      → the script is a file in the project folder; the table is rendered from it. Run the platform-specific
      script_plan
      helper with
      take <id>
      once — it writes the plan to
      <dest>/storyboard/script.json
      and prints the paths. Edit that file, never a plan retyped from context. After every change, write the fields you touched into a small edits JSON and run the same helper with
      show <plan.json> <edits.json>
      : it applies them, lists what it applied, and prints the whole current review table. Say what changed, then paste that table into your reply text — the script's stdout is a tool result the user does not see; only what you write in your own message counts as shown. A diff alone makes them hold the rest of the script in their head. Nothing reaches the server until they confirm, so a half-formed idea costs no revision, no invalidation and no credit. When you customize any user-visible shot field, you must also update the hidden
      shot_prompt
      and/or
      video_prompt
      in the same working copy according to the association table in
      references/script-editing.md
      ; submit them but do not display them.
      The platform's own Prompt Generator is the alternative when you would rather not write them. The edits-file format, the prompt shapes, the trigger mapping and the server's limits are in
      references/script-editing.md
      — read it before the first edit.
      • Editable per-shot fields:
        duration
        ,
        shot_type
        ,
        shot_description
        ,
        dialogue
        (the Voiceover column),
        camera
        ,
        light
        ,
        sfx
        ,
        shot_prompt
        ,
        video_prompt
        . Never add or remove shots — the count comes from the generated script, and the portal's editor has no such action either. Rewriting a shot's content is the way to change it.
        shot_type
        /
        camera
        option values come from
        cawcut ad-video options --json
        → storyboard section, never hardcoded. Everything else in the file (
        assets
        ,
        character_refs
        ,
        scene_ref
        , the prompt statuses) is carried through untouched — the script refuses to edit it.
      • Save once:
        cawcut ad-video storyboard set <id> --base-revision <value.revision> --plan-file <dest>/storyboard/script.json --json
        ; on
        409
        re-read detail, take a fresh copy and re-apply the same edits, save again — the edits file is your record, so nothing is lost. The CLI checks the whole plan against the server's own
        constraints
        before sending it: a refusal names the shot and field, nothing was sent, and the shot at fault is often one you did not edit — fix that field and save again, never resend the same plan. If
        --update-prompts
        was used, the save only starts asynchronous prompt generation: poll
        .outputs.storyboard_plan.prompt_status
        to
        succeeded
        before treating either prompt as ready;
        storyboard_plan.status: succeeded
        alone is not sufficient.
      • duration
        or the shot set changing invalidates the sketch, the panels and the final video: say so before the save, then run the invalidation protocol.
    • 🔁 Regenerate the script
      →
      cawcut ad-video storyboard run <id> --force
      + poll. Warn before running it: this discards every edit — the script LLM writes a new script from the same idea and config. It is the only way back to a platform-written script; a plain re-run will not do it (unchanged inputs make it a no-op). Re-show the table and ask again.
  3. Quote before every image run:
    cawcut ad-video image quote <id> --generation-type <type> --json
    → AskUserQuestion to confirm the credit cost → immediately
    cawcut ad-video image run <id> --generation-type <type> --confirmation-token <token> --json
    (token TTL is 5 minutes — do not pause between confirm and run). See Quote timing.
  4. Branch by storyboard style (from Stage ② config):
    • hand_drawn_then_photo
      : pass 1
      --generation-type hand_drawn
      → poll
      storyboard_sketch
      → preview the sketch (Media preview & downloads), AskUserQuestion
      ✅ Continue to photo-realistic
      /
      ✏️ Edit script
      → pass 2
      --generation-type photo_realistic
      → poll
      storyboard_image
    • photo_realistic_only
      : single
      --generation-type photo_realistic
      run → poll
      storyboard_image
  5. Generation-stage polls use the longest deadline in the polling table (3600). On success, preview the sketch/grid image per Media preview & downloads (platform-specific
    download_media <id> --kind storyboard
    ), then continue.
  1. cawcut ad-video storyboard run <id> --json
    (可从选项的分镜部分传入
    --style-id
    ),轮询直到
    outputs.storyboard_plan.status === "succeeded"
    ——轮询表中的 3600 是放弃等待的截止时间,只是停止线,绝不能作为时长向用户引用。
  2. 读取
    detail <id> --json
    →
    outputs.storyboard_plan.value
    。以简洁的镜头表格展示计划。表格只包含供用户审核的字段;提示词仍保存在计划中,但不予展示:
    • 在表格上方展示计划级信息:
      title
      、
      synopsis
      。
    • 每个镜头按以下顺序展示:
      shot_no
      (镜头 #)、
      duration
      (时长)、
      shot_type
      (镜头类型)、
      shot_description
      (镜头描述)、
      dialogue
      (旁白——这是其业务含义)、
      camera
      (镜头运动)、
      light
      (光线)、
      sfx
      (音效)。
    • 对于
      product_showcase
      ,省略旁白列(Web 门户也会隐藏该列——这种类型没有语音轨道)。
    • 绝不在回复中或其他位置展示
      shot_prompt
      、
      video_prompt
      及其状态。它们是实现字段,不是审核内容。
    然后通过 AskUserQuestion 询问:
    • ✅ 看起来没问题
      → 继续。
    • ✏️ 修改脚本
      → 脚本是项目文件夹中的文件;表格由该文件渲染。使用平台对应的
      script_plan
      辅助脚本执行一次
      take <id>
      ——它会将计划写入
      <dest>/storyboard/script.json
      并打印路径。应编辑
      该文件
      ,绝不能根据上下文重新键入计划。每次更改后,将修改的字段写入一个小型 edits JSON,并使用同一辅助脚本执行
      show <plan.json> <edits.json>
      :它会应用修改、列出已应用的内容,并打印当前完整审核表。说明修改内容后,将该表粘贴到回复正文中——脚本的 stdout 是用户看不到的工具结果,只有自行写入消息中的内容才算展示。在用户确认前,任何内容都不会发送到服务器,因此格式不完整的创意不会产生修订、失效或 credits 消耗。**修改任何用户可见的镜头字段时,还必须在同一工作副本中根据
      references/script-editing.md
      的关联表更新隐藏的
      shot_prompt
      和/或
      video_prompt
      ;可以提交,但不要展示。**edits 文件格式、提示词结构、触发映射和服务器限制位于
      references/script-editing.md
      ——首次编辑前必须读取。
      • 每个镜头可编辑的字段:
        duration
        、
        shot_type
        、
        shot_description
        、
        dialogue
        (旁白列)、
        camera
        、
        light
        、
        sfx
        、
        shot_prompt
        、
        video_prompt
        。绝不能添加或删除镜头——镜头数量来自生成的脚本,门户编辑器也没有此类操作。修改镜头内容是改变它的方式。
        shot_type
        /
        camera
        的可选值来自
        cawcut ad-video options --json
        的分镜部分,绝不能硬编码。文件中的其他所有内容(
        assets
        、
        character_refs
        、
        scene_ref
        、提示词状态)都会原样保留——脚本拒绝编辑这些内容。
      • 只保存一次:
        cawcut ad-video storyboard set <id> --base-revision <value.revision> --plan-file <dest>/storyboard/script.json --json
        ;遇到
        409
        时重新读取 detail,获取最新副本并重新应用相同修改,再次保存——edits 文件是修改记录,因此不会丢失任何内容。CLI 会在发送前根据服务器自身的
        constraints
        检查整个计划:如果被拒绝,会指出有问题的镜头和字段,而且不会发送任何内容;有问题的地方往往不是用户编辑过的镜头——应修复该字段后再次保存,绝不能重复发送同一计划。如果使用了
        --update-prompts
        ,保存只会启动异步提示词生成:必须轮询
        .outputs.storyboard_plan.prompt_status
        直到
        succeeded
        ,才能将任一提示词视为就绪;仅凭
        storyboard_plan.status: succeeded
        并不足够。
      • 修改
        duration
        或镜头集合会使草图、分镜画面和最终视频失效:保存前应告知用户,然后执行失效协议。
    • 🔁 重新生成脚本
      →
      cawcut ad-video storyboard run <id> --force
      并轮询。运行前必须警告:这会丢弃所有修改——脚本 LLM 会根据相同创意和配置编写新脚本。这是恢复平台生成脚本的唯一方式;普通重新运行不会有效果(输入未变时是 no-op)。重新展示表格并再次询问。
  3. 每次运行图片生成前都要先报价:
    cawcut ad-video image quote <id> --generation-type <type> --json
    → 使用 AskUserQuestion 确认 credits 费用 → 立即执行
    cawcut ad-video image run <id> --generation-type <type> --confirmation-token <token> --json
    (token 的 TTL 为 5 分钟——确认与运行之间不要暂停)。参见“报价时机”。
  4. 根据第 ② 阶段的分镜风格进行分支:
    • hand_drawn_then_photo
      :先使用
      --generation-type hand_drawn
      运行第 1 次 → 轮询
      storyboard_sketch
      → 预览草图(参见“媒体预览与下载”)→ 使用 AskUserQuestion 询问
      ✅ 继续生成照片级画面
      /
      ✏️ 修改脚本
      → 使用第 2 次运行和
      --generation-type photo_realistic
      → 轮询
      storyboard_image
      。
    • photo_realistic_only
      :使用
      --generation-type photo_realistic
      运行一次 → 轮询
      storyboard_image
      。
  5. 生成阶段应使用轮询表中的最长截止时间(3600)。成功后,按照“媒体预览与下载”预览草图或网格图片(使用平台对应的
    download_media <id> --kind storyboard
    ),然后继续。

Stage ⑤ — Video

第 ⑤ 阶段 — 视频

  1. cawcut ad-video video quote <id> --json
    → AskUserQuestion confirm credits → immediately
    cawcut ad-video video run <id> --confirmation-token <token> --json
    (add
    --force
    only when the user explicitly re-renders an unchanged result).
  2. Poll until one of
    final_video
    /
    voiceover_final_video
    /
    talking_video
    is
    succeeded
    — the 3600 deadline from the polling table.
  3. On success: download the video into the project folder with platform-specific
    download_media <id> --kind video
    and take the printed path (
    cawcut ad-video video run <id> --wait --download
    is only for when you haven't triggered the run yet). See Media preview & downloads.
  4. Report
    local_path
    and the result URL, then hand off unconditionally to
    cawcut-vn
    with
    <local_path>
    — no VN menu, no
    cawcut vn
    commands of your own (decide-once: the receiving skill owns the VN decision and you never re-check).
  1. cawcut ad-video video quote <id> --json
    → 使用 AskUserQuestion 确认 credits → 立即执行
    cawcut ad-video video run <id> --confirmation-token <token> --json
    (仅当用户明确要求重新渲染未更改的结果时,才添加
    --force
    )。
  2. 轮询,直到
    final_video
    /
    voiceover_final_video
    /
    talking_video
    中的一个为
    succeeded
    ——使用轮询表中的 3600 截止时间。
  3. 成功后:使用平台对应的
    download_media <id> --kind video
    将视频下载到项目文件夹,并获取打印出的路径(只有尚未触发运行时,才使用
    cawcut ad-video video run <id> --wait --download
    )。参见“媒体预览与下载”。
  4. 报告
    local_path
    和结果 URL,然后无条件移交给
    cawcut-vn
    ,并传入
    <local_path>
    ——不显示 VN 菜单,也不自行执行任何
    cawcut vn
    命令(一次决定原则:接收方 skill 负责 VN 决策,绝不重新检查)。

Media preview & downloads

媒体预览与下载

All media URLs the API returns (product images, storyboard sketch/grid, final video) are pre-signed — plain
curl
downloads them directly, no extra signing step. If one ever 403s, re-read
detail
and retry with the fresh URL (product image URLs are re-signed on every
detail
call).
Preview priority (product logo/primary images, storyboard sketch/grid):
  1. If the host has a send-file-to-user tool (e.g. Claude's
    SendUserFile
    ), download the file first, then send it so the user previews inline in the chat.
  2. Otherwise
    open
    the downloaded file (macOS) or just report the local path.
Always use the bundled downloader instead of hand-rolling curl/jq pipelines:
bash
undefined
API 返回的所有媒体 URL(产品图片、分镜草图或网格、最终视频)都是预签名的——可以直接使用普通
curl
下载,无需额外签名步骤。如果某个 URL 返回 403,应重新读取
detail
并使用最新 URL 重试(每次调用
detail
都会重新签署产品图片 URL)。
预览优先级(产品 logo 和主图、分镜草图或网格):
  1. 如果宿主提供向用户发送文件的工具(例如 Claude 的
    SendUserFile
    ),应先下载文件,再发送文件,让用户在聊天中内联预览。
  2. 否则,在 macOS 上使用
    open
    打开下载的文件,或仅报告本地路径。
始终使用内置下载器,不要自行拼接 curl 或 jq 管道:
bash
undefined

macOS/Linux

macOS/Linux

bash <base_dir>/scripts/download_media.sh <id> [--kind product|storyboard|video|all] [dest_dir]
bash <base_dir>/scripts/download_media.sh <id> [--kind product|storyboard|video|all] [dest_dir]

Windows PowerShell

Windows PowerShell

powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\download_media.ps1" <id> [-Kind product|storyboard|video|all] [-Destination <dir>]

Default dest: `~/Downloads/ad-video-<product-name>` (falls back to `ad-video-<id>` before the project is named). Layout: `product/` (logo + primary + additional images), `storyboard/` (sketch + panel grid), final video at the folder root. It prints every saved path — relay those paths to the user in `reply_language`.
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\download_media.ps1" <id> [-Kind product|storyboard|video|all] [-Destination <dir>]

默认目标目录:`~/Downloads/ad-video-<product-name>`(项目命名前会回退为 `ad-video-<id>`)。目录结构:`product/`(logo、主图和其他图片)、`storyboard/`(草图和分镜网格),最终视频位于文件夹根目录。脚本会打印每个保存路径——应使用 `reply_language` 将这些路径转达给用户。

Invalidation semantics (mandatory — mirrors the web portal)

失效语义(强制执行——与 Web 门户一致)

After every mutation (
product set
,
config set
,
idea select
,
storyboard set
):
  1. Immediately run
    cawcut ad-video detail <id> --json
    . First distinguish cleared artifacts from retained-but-stale artifacts; never infer either from the project history.
    • A mutation to any creative config field —
      --duration-seconds
      ,
      --language
      ,
      --video-type
      ,
      --storyboard-style
      , or
      --publish-to
      — clears downstream content. If
      outputs.generated_marketing_messages
      no longer has a usable succeeded
      value
      /
      selection
      , the previous Idea does not exist in the current project state. Do not ask whether to reuse, keep, or continue using that Idea; do not offer it as an option. Continue at Stage ③ with
      cawcut ad-video idea run <id>
      , poll it to success, select a fresh Idea, then generate a new storyboard.
    • Only an output that remains present with
      status: "succeeded"
      and
      need_regenerate === true
      is retained-but-stale and eligible for the regeneration/later decision. A cleared output is neither stale nor reusable.
  2. Collect every retained-but-stale
    outputs.<key>
    with
    need_regenerate === true
    . If any exist: list the affected artifacts to the user in one message, then a single AskUserQuestion:
    • 🔄 Regenerate affected
      → re-run the affected stages in pipeline order (
      storyboard run
      →
      image quote/run
      →
      video quote/run
      ), quoting + confirming credits again per generation stage
    • ⏸ Later
      → continue without regenerating; note the stale artifacts
  3. Never regenerate mid-edit — batch all user edits first, then regenerate once. A script save must only show affected outputs as needing regeneration; it must never trigger an image or video quote/run. Running
    image run
    /
    video run
    happens only after the user explicitly confirms that stage's fresh quote, and is rejected while an upstream output has
    need_regenerate=true
    (
    VIDEO_AD_NEEDS_REGENERATION
    ).
每次变更后(
product set
、
config set
、
idea select
、
storyboard set
):
  1. 立即运行
    cawcut ad-video detail <id> --json
    。首先区分已清除的产物与保留但已过期的产物;绝不能根据项目历史推断二者。
    • 修改任何创意配置字段——
      --duration-seconds
      、
      --language
      、
      --video-type
      、
      --storyboard-style
      或
      --publish-to
      ——都会清除下游内容。如果
      outputs.generated_marketing_messages
      不再包含可用的、状态为 succeeded 的
      value
      /
      selection
      ,则之前的 Idea 并不存在于当前项目状态中。**不要询问是否复用、保留或继续使用该 Idea;也不要将其作为选项提供。**继续执行第 ③ 阶段:运行
      cawcut ad-video idea run <id>
      ,轮询至成功,选择一个新 Idea,然后生成新的分镜。
    • 只有仍然存在、状态为
      succeeded
      且
      need_regenerate === true
      的输出,才属于保留但已过期,并可选择重新生成或稍后处理。已清除的输出既不是过期产物,也不能复用。
  2. 收集所有
    need_regenerate === true
    的
    outputs.<key>
    。如果存在,应在一条消息中列出受影响产物,然后发出一次 AskUserQuestion:
    • 🔄 重新生成受影响内容
      → 按管道顺序重新运行受影响阶段(
      storyboard run
      →
      image quote/run
      →
      video quote/run
      ),每个生成阶段都要重新报价并确认 credits。
    • ⏸ 稍后处理
      → 不重新生成,继续执行;同时记录这些产物已过期。
  3. 绝不能在中途编辑时重新生成——应先批量完成所有用户修改,然后只重新生成一次。脚本保存只能显示需要重新生成的产物;绝不能触发图片或视频报价和运行。只有用户明确确认相应阶段的新报价后,才能运行
    image run
    /
    video run
    ;当上游输出存在
    need_regenerate=true
    时,运行会被拒绝(
    VIDEO_AD_NEEDS_REGENERATION
    )。

Quote timing (mandatory)

报价时机(强制执行)

confirmation_token
expires 5 minutes after quote. Always: quote → AskUserQuestion confirm → run, back-to-back with no user discussion in between. If
run
fails with a token/confirmation error (expired or invalidated), re-quote, ask again, run again — once per attempt, no silent retries.
Check the balance before asking to spend (mandatory).
image quote
/
video quote
also report
credits_balance
,
credits_shortfall
and
credits_sufficient
alongside
estimated_credits
. Look at them before the confirmation question:
  • credits_sufficient === false
    → say the numbers plainly ("this needs X, your balance is Y, short by Z") and send the user to top up on the web portal. The confirmation menu should offer
    💳 Top up credits
    /
    ⏸ Not now
    instead of a routine confirm-and-run — do not walk a user into a charge they cannot pay for.
  • credits_sufficient === true
    , or the balance is unavailable (
    credits_balance_error
    — the lookup is best-effort and must never block) → normal confirmation.
  • Never present an insufficient balance as a generic failure after the fact. The whole point is to say it before the run.
  • Topping up takes longer than the token's 5 minutes: on the user's return, re-quote → re-confirm → run. Never reuse the stale token.
confirmation_token
在报价 5 分钟后过期。始终应按照“报价 → 使用 AskUserQuestion 确认 → 运行”的顺序连续执行,中间不与用户讨论。如果
run
因 token 或确认错误而失败(过期或失效),应重新报价、再次询问、再次运行——每次尝试仅执行一次,绝不能静默重试。
请求消费前必须检查余额(强制执行)。
image quote
/
video quote
还会同时报告
credits_balance
、
credits_shortfall
和
credits_sufficient
,以及
estimated_credits
。确认问题前应先查看这些字段:
  • credits_sufficient === false
    → 清楚说明数字(“本次需要 X,你的余额为 Y,还差 Z”),并让用户前往 Web 门户充值。确认菜单应提供
    💳 充值 credits
    /
    ⏸ 暂不
    ,而不是常规的确认并运行选项——绝不能让用户在无法支付的情况下进入扣费流程。
  • credits_sufficient === true
    ,或者余额不可用(
    credits_balance_error
    ——余额查询是尽力而为的,绝不能因此阻塞)→ 正常确认。
  • 绝不能将余额不足作为运行后的普通失败来呈现。重点是在运行前就明确告知用户。
  • 充值耗时可能超过 token 的 5 分钟有效期:用户返回后,应重新报价 → 重新确认 → 再次运行。绝不能复用过期 token。

Polling (bundled script — mandatory)

轮询(内置脚本——强制执行)

Trigger without
--wait
, then poll with the platform-specific bundled script. Never hand-roll polling loops — an ad-hoc
echo "$var" | jq
loop corrupts the JSON under zsh (echo interprets
\n
escapes) and spins forever without matching a terminal state.
bash
bash <base_dir>/scripts/poll.sh <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]
先不带
--wait
触发,然后使用平台对应的内置脚本轮询。绝不能自行编写轮询循环——临时的
echo "$var" | jq
循环在 zsh 下会因
echo
解释
\n
转义而破坏 JSON,并可能永远无法匹配到终止状态。
bash
bash <base_dir>/scripts/poll.sh <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]

Windows PowerShell

Windows PowerShell

powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\poll.ps1" <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]

| Stage | json_path | success_value | timeout_s |
|---|---|---|---|
| ① analyze | `.status` | `stage_completed` | 600 |
| ③ idea | `.outputs.generated_marketing_messages.status` | `succeeded` | 600 |
| ④ plan | `.outputs.storyboard_plan.status` | `succeeded` | 3600 |
| ④ prompts | `.outputs.storyboard_plan.prompt_status` | `succeeded` | 600 |
| ④ sketch | `.outputs.storyboard_sketch.status` | `succeeded` | 3600 |
| ④ panels | `.outputs.storyboard_image.status` | `succeeded` | 3600 |
| ⑤ video | `.outputs.final_video.status // .outputs.voiceover_final_video.status // .outputs.talking_video.status` | `succeeded` | 3600 |

The Windows helper parses these dotted paths and `//` fallbacks internally; `json_path` does not require jq on Windows.

Exit codes: `0` success · `2` failed (report the output's `error` from `detail --json`; see `references/troubleshooting.md`) · `3` timeout → AskUserQuestion `⏳ Continue waiting` (run the script again) / `❌ Stop` (project persists; resume later with `cawcut ad-video detail <id>`) · `4` output unparseable → stop polling, run `cawcut ad-video detail <id> --json` once and inspect (auth/CLI issue).

**The script does not notify — the host's tool does.** `poll.sh` ends on an exit code and nothing else. Never raise an OS notification from the shell (`osascript`, `notify-send`, BurntToast), never add one back into the script, and never register a notification permission on the user's machine on your own initiative: a toast fired from a shell command is a different channel from the app's own notification, and it is not what the user asked for.
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\poll.ps1" <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]

| 阶段 | json_path | success_value | timeout_s |
|---|---|---|---|
| ① 分析 | `.status` | `stage_completed` | 600 |
| ③ 创意 | `.outputs.generated_marketing_messages.status` | `succeeded` | 600 |
| ④ 计划 | `.outputs.storyboard_plan.status` | `succeeded` | 3600 |
| ④ 提示词 | `.outputs.storyboard_plan.prompt_status` | `succeeded` | 600 |
| ④ 草图 | `.outputs.storyboard_sketch.status` | `succeeded` | 3600 |
| ④ 分镜画面 | `.outputs.storyboard_image.status` | `succeeded` | 3600 |
| ⑤ 视频 | `.outputs.final_video.status // .outputs.voiceover_final_video.status // .outputs.talking_video.status` | `succeeded` | 3600 |

退出码:`0` 成功 · `2` 失败(报告 `detail --json` 输出中的 `error`;参见 `references/troubleshooting.md`)· `3` 超时 → 使用 AskUserQuestion 询问 `⏳ 继续等待`(再次运行脚本)/ `❌ 停止`(项目会保留;之后可通过 `cawcut ad-video detail <id>` 恢复)· `4` 输出无法解析 → 停止轮询,运行一次 `cawcut ad-video detail <id> --json` 并进行检查(认证或 CLI 问题)。

**脚本不发送通知——由宿主工具发送。**`poll.sh` 以退出码结束,不会产生其他输出。绝不能从 shell 发出操作系统通知(`osascript`、`notify-send`、BurntToast),也绝不能向脚本中添加此类通知,或主动在用户机器上注册通知权限:shell 命令触发的 toast 与应用自身的通知属于不同通道,而且这并不是用户所要求的。

Telling the user how long it will take

告知用户预计耗时

Never estimate generation time yourself — and never turn a deadline into a duration. The server computes the runtime, and
detail
carries it as
estimated_running_ms
(milliseconds, present while a stage is running).
poll.sh
reads it on its first tick and prints one
estimated: about Nm
line — relay that figure if there is one, and nothing more. If you need it directly, it is on any
detail --json
read.
When the API gives no figure (the field is absent or zero — the lookup is best-effort and can miss), fall back to the order of magnitude, never to the deadline: these stages run on the order of minutes (the node estimates behind them are seconds to a couple of minutes), so say "about a few minutes" / "大概几分钟" and add that you will say the moment it lands. Do not say the timing is unknown, and do not hedge upward.
The
timeout_s
column is a give-up deadline, not a runtime.
Saying "at most about an hour" because the row says
3600
is the same error as inventing a range: it is the point at which the script stops waiting, typically an order of magnitude past the real runtime. The deadline never appears in a reply, not even hedged with "最多" / "at most".
**绝不能自行估算生成时间,也绝不能把截止时间当作时长。**运行时间由服务器计算,
detail
中会在阶段运行期间提供
estimated_running_ms
字段(毫秒)。
poll.sh
会在首次轮询时读取该值,并打印一行
estimated: about Nm
——如果存在该值,应原样转达,不要添加其他内容。如需直接获取,可读取任意一次
detail --json
输出。
如果 API 没有提供数值(字段缺失或为 0——该查询是尽力而为的,可能会漏掉),应回退到数量级估算,绝不能使用截止时间:这些阶段通常耗时数分钟(其背后的节点可能需要数秒到几分钟),因此应表示为“大概几分钟”,并说明完成后会立即通知用户。不要说耗时未知,也不要向上扩大估算范围。
**
timeout_s
是放弃等待的截止时间,不是运行时长。**仅因为表中写着
3600
就说“最多大约一小时”,与自行编造时间范围同样错误:它只是脚本停止等待的时间,通常比实际运行时长高出一个数量级。截止时间绝不能出现在回复中,即使使用“最多”或“at most”也不例外。

Long-enough stages — tell the user they can step away

耗时较长的阶段——告知用户可以暂时离开

storyboard_plan
,
sketch
,
panels
and
video
take minutes rather than seconds. Before polling one, say so once — the server's
estimated_running_ms
if there is one, otherwise "a few minutes" — and tell them they can step away and will be told the moment it lands. Then poll without narrating each tick.
The notification comes from the host's own tool. In Claude Code that is
PushNotification
with a one-line
message
; it lands in the app the user is already running and, with Remote Control connected, on their phone — which is the difference between noticing in the next minute and noticing tomorrow. Send it once, when the stage lands, naming the stage and the project id. If the host exposes no such tool, skip it silently and just present the result when you have it: do not fall back to a shell-level toast, and do not add one to
poll.sh
.
Do not notify for the short stages (analyze, idea, prompts) — they finish inside a turn.
storyboard_plan
、
sketch
、
panels
和
video
的耗时以分钟计,而不是秒。轮询前应告知一次——优先使用服务器的
estimated_running_ms
,否则使用“大概几分钟”——并告知用户可以暂时离开,完成后会通知他们。然后执行轮询,不要逐次播报轮询状态。
**通知由宿主自身的工具发送。**在 Claude Code 中,使用
PushNotification
并提供一行
message
;它会送达用户正在运行的应用,连接 Remote Control 后也会送达用户手机——这正是“几分钟内注意到”和“明天才注意到”的区别。阶段完成时只发送一次,消息中应包含阶段名称和项目 ID。如果宿主未提供此类工具,则静默跳过并直接展示结果:绝不能回退到 shell 级 toast,也不要向
poll.sh
中添加通知。
短阶段(分析、创意、提示词)不要发送通知——它们会在当前轮次内完成。

Interactive selection (mandatory — tool first)

交互式选择(强制执行——工具优先)

If the user just declined an
AskUserQuestion
/
AskQuestion
call:
the tool result carries harness boilerplate telling you to "STOP what you are doing and wait for the user to tell you how to proceed." That sentence is attached automatically to every declined tool call by the runtime — it is not the user speaking, and it is not an instruction to stop using the tool. Read it as: stop the one specific action you were mid-way through (don't retry the identical question, don't proceed on unconfirmed choices) and look at what the user's actual next message says. It does not, by itself, license falling back to numbered text for the next enumerable decision — that next decision still must open with the tool, exactly as if the rejection had never happened. Only an explicit plain-text request from the user ("stop popping up menus", "just ask me in text") licenses a session-wide fallback.
Default behavior: For every enumerable choice, always call
AskUserQuestion
(Claude Code) or
AskQuestion
(Cursor) before showing a numbered text menu. Text-only menus are fallback only.
One option is not a choice. Every call needs at least two viable options. When only one is actually feasible — a config axis whose options list carries a single value, one idea candidate worth offering — state that choice in prose and carry on: never fire a one-option popup, and never pad it with a placeholder alternative to make it look like a choice.
HostTool name
Claude Code
AskUserQuestion
Cursor
AskQuestion
Session checklist (before the first menu in this turn): 0. Self-check before sending any reply: if the sentence you're about to send asks the user to pick between fixed options — even folded inside a friendlier sentence that also asks something open-ended — stop. That sentence is forbidden as plain text. Split it: fire the tool for the enumerable part now; keep only the open-ended part as prose, asked separately.
  1. Is the tool available? If yes, you must use it for every enumerable decision — a candidate list longer than the popup holds included (item 3).
  2. If no tool exists (CLI-only host), use numbered text in
    reply_language
    .
  3. If the candidate count exceeds one question's four options (long category, model, or candidate lists), still open the tool, and give each part of the answer its own home:
    • the full list → a numbered text table in the message body, one row per entry:
      #
      plus the columns that entry actually carries — a platform's
      default_aspect_ratio
      , a language's label, a model's
      supported_resolutions
      and default mark, a candidate's title and content. Never inline the list into the question text: a 22-model roster pasted into the question header is unreadable and buries the question. The table and the call ship together.
    • the question text → one short sentence naming the decision, plus the pointer for whatever the popup cannot hold (e.g. "entries 4–22 can be typed below by number or name", in
      reply_language
      ).
    • the popup → the leading entries as options, labelled with their number, as many as fit while keeping a slot for any action that must stay clickable beside them (e.g. a write-your-own).
    • the free-text field → whatever the popup could not hold: any later entry, by its number or its name.
    The numbering is the contract between table and popup. Anything the popup can hold stays clickable; a typed answer is only for what it cannot. One question per decision — never a second question asking what to do with the answer to the first.
Forbidden while the tool is available: a numbered text menu instead of the call, "reply with the number or name" with no popup behind it, or asking the user to type enum values from memory. A long list's numbered table is not a text menu — it is the call's index, and it never ships alone.
Never hardcode option values. Platform, resolution, duration, video type, video model, storyboard style, language, and style IDs come from
cawcut ad-video options --json
output this session — not from this document, not from training data.
如果用户刚刚拒绝了一次
AskUserQuestion
/
AskQuestion
调用:工具结果会附带一段固定提示,大意是“停止当前操作,等待用户告知如何继续”。这句话由运行时自动附加到每一次
被拒绝的工具调用中;它不是用户发言,也不是要求停止使用该工具。应将其理解为:停止当前正在执行的那一个具体操作(不要重试完全相同的问题,也不要在尚未确认选项时继续),然后查看用户实际发送的下一条消息。该提示本身并不允许在下一次可枚举决策中改用编号文本菜单;即使问题被拒绝,下一次决策仍必须像从未拒绝过一样先打开工具。只有用户明确以纯文本提出要求(“别再弹菜单了”、“直接用文字问我”)时,才允许在整个会话中使用纯文本回退。
**默认行为:**对于每个可枚举选项,始终在显示编号文本菜单之前调用
AskUserQuestion
(Claude Code)或
AskQuestion
(Cursor)。
**一个选项不构成选择。**每次调用至少需要两个可行选项。当实际上只有一个可行选项时——例如配置维度只有一个值,或只有一个值得提供的创意候选项——应在说明文字中直接给出该选项并继续:绝不能发起只有一个选项的弹窗,也绝不能为了凑数而添加无意义的替代选项。
宿主工具名称
Claude Code
AskUserQuestion
Cursor
AskQuestion
会话检查表(在本轮第一次显示菜单前): 0. **发送任何回复前进行自检:**如果即将发送的句子要求用户从固定选项中进行选择——即使它被包装在更友好的句子中,同时还提出了开放式问题——也应停止。禁止以纯文本发送这种句子。应将其拆分:立即为可枚举部分调用工具;仅将开放式部分保留为单独的说明文字。
  1. 工具是否可用?如果可用,则本 skill 中每个可枚举决策必须使用该工具——包括候选项数量超过弹窗容量的情况(第 3 项)。
  2. 如果没有工具(纯 CLI 宿主),则使用
    reply_language
    编写编号文本菜单。
  3. 如果候选项数量超过单个问题可容纳的四个选项(长类别、模型或候选项列表),仍应打开工具,并为答案的每个部分设置独立位置:
    • 完整列表 → 消息正文中的编号文本表格,每行一个条目:
      #
      加该条目实际包含的信息——平台的
      default_aspect_ratio
      、语言的
      label
      、模型的
      supported_resolutions
      和默认标记、候选项的标题和内容。绝不能将列表内联到问题文本中:将 22 个模型的清单粘贴到问题标题中既难读,又会掩盖真正的问题。表格和调用必须一起发出。
    • 问题文本 → 一句简短的话,说明需要做出的决定,并指出弹窗无法容纳的条目应如何输入(例如“可通过编号或名称输入第 4–22 项”,使用
      reply_language
      )。
    • 弹窗 → 将靠前条目作为可点击选项并标注编号,在不占用必须保留的操作项(例如自行编写)的情况下尽量填满。
    • 自由文本字段 → 容纳弹窗无法显示的任何后续条目,可按编号或名称输入。
编号是表格与弹窗之间的约定。弹窗能够容纳的条目应保持可点击;只有弹窗无法容纳的条目才使用文本输入。一个问题只处理一个决策——绝不能先问完一个问题,再用第二个问题询问如何处理第一个问题的答案。
**工具可用时禁止:**用编号文本菜单代替调用、发送“回复编号或名称”但背后没有弹窗,或要求用户凭记忆输入枚举值。长列表的编号表格不是文本菜单——它是调用的索引,绝不能单独发送。
绝不能硬编码选项值。平台、分辨率、时长、视频类型、视频模型、分镜风格、语言和 style ID 均来自本次会话中
cawcut ad-video options --json
的输出——不能来自本文档或训练数据。

UX Rules

UX 规则

  1. Be concise. Default output is the result URL and local path. No raw JSON dumps unless debugging (
    --json
    is for the polling script).
  2. Reply language: see the top-of-file rule — follow the user's language for every reply (including the first one and all AskUserQuestion questions/labels), and summarize CLI output in
    reply_language
    instead of pasting raw English.
  3. Do not call CawCut HTTP APIs with curl — the CLI handles auth and tokens.
  4. Choice-first — every enumerable decision (config axes, idea candidates, credit confirmations, regenerate confirmations) goes through AskUserQuestion/AskQuestion per Interactive selection.
  5. One project per request. If the user asks for several ad videos, finish one wizard before starting the next.
  6. V1 contract only. Do not consume or promise character/scene (V2) capabilities; they are not exposed to developer tokens.
  7. Credits: always quote + confirm before
    image run
    /
    video run
    . Never spend credits without an explicit user confirmation in this session, and read the quote's balance figures first — an insufficient balance is stated before the confirmation question, not discovered by a failed run (see Quote timing).
  8. Long stages (
    storyboard_plan
    ,
    sketch
    ,
    panels
    ,
    video
    ) take minutes, not seconds: say so once — quoting the server's
    estimated_running_ms
    , or "a few minutes" when there is none (see Polling); never the deadline — tell the user they can step away, and let the notification bring them back. Don't narrate each poll tick.
  1. 保持简洁。默认输出结果 URL 和本地路径。除非用于调试,否则不要输出原始 JSON dump(
    --json
    用于轮询脚本)。
  2. 回复语言:参见文件顶部的规则——每次回复都应遵循用户语言(包括第一次回复及所有 AskUserQuestion 问题和标签),并在
    reply_language
    中总结 CLI 输出,而不是粘贴原始英文。
  3. 不要使用 curl 调用 CawCut HTTP API——CLI 会处理认证和 token。
  4. 选择优先——每个可枚举决策(配置维度、创意候选项、credits 确认、重新生成确认)都应按照“交互式选择”使用 AskUserQuestion/AskQuestion。
  5. **每次请求只处理一个项目。**如果用户要求制作多个广告视频,应先完成一个向导,再开始下一个。
  6. **仅支持 V1 contract。**不得使用或承诺角色和场景(V2)能力;开发者 token 尚未开放这些能力。
  7. Credits:运行
    image run
    /
    video run
    前始终必须报价并确认。没有本次会话中的明确用户确认,绝不能消费 credits;并且必须先读取报价中的余额数据——余额不足应在确认问题之前告知用户,而不是运行失败后才发现(参见“报价时机”)。
  8. 长阶段(
    storyboard_plan
    、
    sketch
    、
    panels
    、
    video
    )耗时以分钟计,而不是秒:应告知一次——引用服务器的
    estimated_running_ms
    ,没有时使用“大概几分钟”(参见“轮询”);绝不能使用截止时间——告知用户可以暂时离开,并由通知召回。不要逐次播报轮询状态。

CLI quick reference

CLI 快速参考

bash
cawcut ad-video create [--url <u>] [--name <n>] [--category <c>] [--json]
cawcut ad-video detail <id> [--json]
cawcut ad-video options [--video-ad-id <id>] [--language <l>] [--json]
cawcut ad-video product set <id> [--product-name …] [--description …] [--selling-point …] [--clear-selling-points] [--logo <ref>] [--primary-image <ref>] [--asset <ref>…] [--target-audience …] [--promo-*] [--json]   # only the fields passed change
cawcut ad-video config set <id> [--publish-to …] [--language …] [--aspect-ratio …] [--resolution …] [--duration-seconds …] [--video-type …] [--video-model …] [--storyboard-style …] [--auto-captions …] [--json]
cawcut ad-video analyze run <id> --url <u> [--wait] [--json]   ·   analyze cancel <id>
cawcut ad-video idea run <id> [--wait] [--json]   ·   idea select <id> (--message-id <m> | --custom <text>)   ·   idea regenerate <id>   ·   idea load-more <id>
cawcut ad-video storyboard run <id> [--style-id <s>] [--force] [--wait] [--json]   ·   storyboard set <id> --base-revision <n> (--plan <json> | --plan-file <path>) [--update-prompts --prompt-target <shot_id>:<fields>…]   # run --force rewrites the script, discarding saved edits
cawcut ad-video image quote <id> --generation-type <hand_drawn|photo_realistic>   ·   image run <id> [--confirmation-token <t>] [--generation-type <t>] [--force] [--wait] [--download]
cawcut ad-video video quote <id>   ·   video run <id> [--confirmation-token <t>] [--force] [--wait] [--download]
bash
cawcut ad-video create [--url <u>] [--name <n>] [--category <c>] [--json]
cawcut ad-video detail <id> [--json]
cawcut ad-video options [--video-ad-id <id>] [--language <l>] [--json]
cawcut ad-video product set <id> [--product-name …] [--description …] [--selling-point …] [--clear-selling-points] [--logo <ref>] [--primary-image <ref>] [--asset <ref>…] [--target-audience …] [--promo-*] [--json]   # only the fields passed change
cawcut ad-video config set <id> [--publish-to …] [--language …] [--aspect-ratio …] [--resolution …] [--duration-seconds …] [--video-type …] [--video-model …] [--storyboard-style …] [--auto-captions …] [--json]
cawcut ad-video analyze run <id> --url <u> [--wait] [--json]   ·   analyze cancel <id>
cawcut ad-video idea run <id> [--wait] [--json]   ·   idea select <id> (--message-id <m> | --custom <text>)   ·   idea regenerate <id>   ·   idea load-more <id>
cawcut ad-video storyboard run <id> [--style-id <s>] [--force] [--wait] [--json]   ·   storyboard set <id> --base-revision <n> (--plan <json> | --plan-file <path>) [--update-prompts --prompt-target <shot_id>:<fields>…]   # run --force rewrites the script, discarding saved edits
cawcut ad-video image quote <id> --generation-type <hand_drawn|photo_realistic>   ·   image run <id> [--confirmation-token <t>] [--generation-type <t>] [--force] [--wait] [--download]
cawcut ad-video video quote <id>   ·   video run <id> [--confirmation-token <t>] [--force] [--wait] [--download]

Errors

错误

SymptomAction
Token expired
Follow the login steps in Step 0; retry the failed command once after
status=authenticated
VIDEO_AD_NEEDS_REGENERATION
/
308000414
An upstream output is stale — re-run the flagged stage first (see Invalidation semantics)
Confirmation token expired/invalidRe-quote, re-confirm with the user, run again (see Quote timing)
409
on
storyboard set
Revision mismatch — re-read
detail
, retry with the fresh
value.revision
409
concurrent run
Another stage run is in flight — poll
detail
until it settles, then retry
400
on
config set
Bad enum, unmet stage gate, or a resolution the chosen
video_model
does not support — re-read
ad-video options
Insufficient credits (from the quote)
credits_sufficient: false
— state the shortfall and send the user to top up; re-quote on their return (see Quote timing)
CLI not found
npm install -g @ubnt/cawcut
(Step 0)
Full recovery trees:
references/troubleshooting.md
.
症状操作
Token expired
执行第 0 步中的登录流程;在
status=authenticated
后重试失败命令一次
VIDEO_AD_NEEDS_REGENERATION
/
308000414
上游输出已过期——先重新运行被标记的阶段(参见“失效语义”)
确认 token 过期或无效重新报价、重新向用户确认,再次运行(参见“报价时机”)
storyboard set
返回
409
修订版本不匹配——重新读取
detail
,使用最新的
value.revision
重试
409
concurrent run
另一个阶段正在运行——轮询
detail
直至状态稳定,然后重试
config set
返回
400
无效枚举值、未满足阶段门槛,或所选
video_model
不支持该分辨率——重新读取
ad-video options
报价显示 credits 不足
credits_sufficient: false
——说明缺口并让用户充值;用户返回后重新报价(参见“报价时机”)
找不到 CLI
npm install -g @ubnt/cawcut
(第 0 步)
完整恢复流程见
references/troubleshooting.md
。