⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

voiceover-narration-studio

@beatra-ai⭐ 7 stars

Use Voiceover & Narration Studio as an AI voice generator, text-to-speech workspace, and AI voiceover generator. Choose from the current voice library, turn scripts into ready-to-edit AI narration and voiceover, or create and reuse a custom brand voice through voice cloning. It supports short-video voiceover, script-to-voiceover, course narration, ordered audiobook narration, supplied multilingual text to speech, Cantonese text to speech, and recurring brand audio, with current price estimates, clear output planning, and delivery organized by chapter, language, and use case.

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐ 7 on GitHubGitHub ↗

New / niche

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

AI Voiceover Generator Skill

English | 简体中文 | 日本語

👉 See also AI Voice Cloning Skill

Pick or reuse a voice, turn scripts into narration ready for editing, plan ordered long-form or multilingual audio, and create a reusable custom voice, from inside Claude Code, Cursor, Codex, Windsurf, Cline, or OpenClaw.

[!IMPORTANT] Rendering needs a Beatra account and uses credits. The skill itself is free to install.

QuestionAnswer
What it doesChoose or reuse a voice, turn scripts into ready-to-edit narration, plan ordered long-form or multilingual audio, and create a reusable custom voice.
ModelsMiniMax Voice Clone, MiniMax Speech 2.8 HD, MiniMax Speech 2.8 Turbo
RequirementsPython 3.10+ and an agent that loads SKILL.md
CostFree to install. Each render uses credits on your Beatra account, and paid steps run only when you ask for that exact render or approve its card.
Works withClaude Code, Cursor, Codex, Windsurf, Cline, OpenClaw

A narration studio cover with a microphone and three colored waveforms, one for each voice in the sample. AI-generated with Beatra.

SkillEntry pointVersion
voiceover-narration-studioSKILL.md0.2.9

This repository is published automatically from beatra-ai/beatra-skills. Report issues there.

Install

With the skills CLI:

npx skills add beatra-ai/ai-voiceover-generator-skill

As a Claude Code plugin:

claude plugin marketplace add beatra-ai/ai-voiceover-generator-skill
claude plugin install voiceover-narration-studio@ai-voiceover-generator-skill

As a Codex plugin:

codex plugin marketplace add beatra-ai/ai-voiceover-generator-skill
codex plugin add voiceover-narration-studio@ai-voiceover-generator-skill

As a GitHub Copilot CLI plugin:

copilot plugin marketplace add beatra-ai/ai-voiceover-generator-skill
copilot plugin install voiceover-narration-studio@ai-voiceover-generator-skill

As a Gemini CLI extension:

gemini extensions install https://github.com/beatra-ai/ai-voiceover-generator-skill

With the GitHub CLI:

gh skill install beatra-ai/ai-voiceover-generator-skill voiceover-narration-studio

Or clone this repository and copy skills/voiceover-narration-studio into ~/.claude/skills/ for Claude Code, ~/.cursor/skills/ for Cursor, ~/.agents/skills/ for Codex, ~/.codeium/windsurf/skills/ for Windsurf, ~/.cline/skills/ for Cline, or ~/.openclaw/skills/ for OpenClaw.

Or paste this into your agent:

Install the voiceover-narration-studio skill from https://github.com/beatra-ai/ai-voiceover-generator-skill (folder skills/voiceover-narration-studio), then follow its SKILL.md to connect my Beatra account.

Examples

▶ Listen (MP3)

Three short reads in three preset voices, in order: a calm product explainer, an upbeat short-video ad, and a documentary line, 41 seconds in total. AI-generated with Beatra.

Ask your agent:

Read these three short scripts in three different preset voices: a calm product explainer for the Tidewell desk lamp, an upbeat short-video ad for Crunchlane chips, and one documentary line. Deliver the three reads in that order.

Prompt:

Meet Tidewell, a desk lamp that follows your day. In the morning, it glows cool and bright for focus. After sunset, it turns warmer, so your eyes can rest.

Okay, stop scrolling! Crunchlane spicy mango chips are here: sweet, hot, and seriously crunchy. Grab a bag this Saturday, and tell us how fast it disappeared!

High in the northern mountains, the first snow falls without a sound. A red fox stops and listens. Something small is moving beneath the snow. She waits. Then she leaps.

Technical Specifications & Output Limits

As of 2026-10-18. Live list: https://docs.beatra.ai/en/docs/models

  • Uploads — up to 100 MB per file; the per-model input limits below also apply
  • Voice cloning — 1 model (MiniMax Voice Clone); sample 10–300 s, up to 20 MB; consent attestation required
  • Text to speech — 2 models (MiniMax Speech 2.8 HD, MiniMax Speech 2.8 Turbo); FLAC, MP3, OPUS, PCM, WAV; 40 languages; sample rates 8, 16, 22.05, 24, 32, 44.1 kHz; up to 50000 characters per request
    • Languages (ISO 639 codes): zh, yue, en, es, fr, ru, de, pt, ar, it, ja, ko, id, vi, tr, nl, uk, th, pl, ro, el, cs, fi, hi, bg, da, he, ms, fa, sk, sv, hr, fil, hu, no, sl, ca, nn, ta, af

Why use Beatra instead of calling model APIs directly

  • One account for every model this skill uses — Voice cloning 1; Text to speech 2 (MiniMax Voice Clone, MiniMax Speech 2.8 HD, MiniMax Speech 2.8 Turbo) (as of 2026-10-18), behind one Beatra sign-in; no keys from individual model providers to manage.
  • You decide what gets paid for — Paid renders run only when you ask for that exact render or approve its card, which shows the live credit estimate.
  • Direction before rendering — the skill plans, directs, and reviews the work, then renders it on Beatra.
  • Files you can verify — every file under skills/ matches the release archive on cdn.beatra.ai byte for byte, checked by SHA-256 before each push.
  • Updates you control — no self-updater; update with the tool you installed it with.
  • Runs in your agent — Claude Code, Cursor, Codex, Windsurf, Cline, OpenClaw.

How it compares

Beatra (this skill)scenario-labs/skillsvargHQ/skills
Account or key neededA Beatra account; no model provider keysA Scenario account (OAuth) or a Scenario API key (source)A VARG_API_KEY, required for all skills (source)
ModelsVoice cloning 1; Text to speech 2 (MiniMax Voice Clone, MiniMax Speech 2.8 HD, MiniMax Speech 2.8 Turbo)Voice and speech generation through its scenario-audio skill (source)Kling, Flux, ElevenLabs, Sora and more through the varg platform (source)
Before spendingPaid renders run only when you ask for that exact render or approve its card, which shows the live credit estimateDry-run price estimates in its quality-gate and model-comparison skills (source)A free --preview render before the full render (source)
AgentsClaude Code, Cursor, Codex, Windsurf, Cline, OpenClawClaude Code, Cursor, Codex, Copilot and 70+ others (source)Claude Code, Cursor, Windsurf, OpenCode,

// HOW IT'S BUILT

// FILES

18 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • skills/voiceover-narration-studio/SKILL.md13 KBe5b341f12d4dupstream ↗
    ---
    name: "voiceover-narration-studio"
    description: "Use Voiceover & Narration Studio as an AI voice generator, text-to-speech workspace, and AI voiceover generator. Choose from the current voice library, turn scripts into ready-to-edit AI narration and voiceover, or create and reuse a custom brand voice through voice cloning. It supports short-video voiceover, script-to-voiceover, course narration, ordered audiobook narration, supplied multilingual text to speech, Cantonese text to speech, and recurring brand audio, with current price estimates, clear output planning, and delivery organized by chapter, language, and use case."
    ---
    
    # Voiceover & Narration Studio
    
    Choose the smallest voice workflow that completes the user's approved text:
    one voiceover, an ordered long-form production, supplied multilingual versions,
    or an authorized reusable voice. Keep this Studio as the coordinating route
    when a request mixes those outcomes or will continue across several sessions.
    
    ## Scope and routes
    
    At the entrance, lock the medium first: long narration, multilingual, or brand
    clone. Route an audiobook to `ai-audiobook-narration`, a podcast episode to
    `ai-podcast-voiceover`, and a short social or promo read to
    `short-form-voiceover-audio`.
    
    Reuse every decision already present in the conversation. Route a focused,
    standalone request to the narrower package when it is available:
    
    | User outcome | Best route |
    | --- | --- |
    | One short-video, ad, social, or promo read | `short-form-voiceover-audio` |
    | An audiobook or other ordered long-form narration | `ai-audiobook-narration` |
    | A single-host podcast episode or recurring show | `ai-podcast-voiceover` |
    | Multilingual dubbing with the target-language scripts already supplied and approved | `ai-multilingual-dubbing` |
    | Creation of one authorized reusable voice | `voice-cloning-studio` |
    | Long narration, multilingual, brand clone, or mixed work those routes do not cover | Continue here and maintain one production ledger |
    
    This Studio creates speech from supplied or approved text. It does not make a
    finished video, lip-sync a speaker, publish media, transcribe a recording, or
    edit an existing waveform. Preserve the useful voice subtask and route those
    adjacent outcomes to their focused workflows.
    
    Read [intent and routing](references/intent-and-routing.md) when deciding
    between a focused package and a mixed Studio project.
    
    ## Inputs and defaults
    
    Hard inputs include consent status. Do not imitate a voice without
    authorization.
    
    For synthesis, the hard inputs are:
    
    - final supplied text, or permission to prepare a speakable version and show
      it for approval;
    - an exact current `voice_id`, looked up again with `beatra.voices.list`
      whether it came from the user, the conversation, or a new recommendation;
    - the target language only when the user specifies one; otherwise omit
      `language` and do not interrupt merely to ask for it.
    
    For voice cloning, also require a sample, a display name, and the user's plain
    confirmation that it is their own voice or that the speaker explicitly
    authorized cloning. Consent must be confirmed before a local sample is
    uploaded.
    
    Use restrained defaults when the destination does not require another choice:
    `model: "auto"`, `format: "mp3"`, `speed: 1.0`, `volume: 1.0`,
    `pitch: 0`, no `emotion`, and no `sample_rate`. Prepare numbers, names,
    abbreviations, units, and pauses for speech, but show any material rewrite
    before paid work.
    
    ## Golden path
    
    1. Identify one route or an ordered set of routes. For mixed work, keep
       clone, language, chapter, and revision steps distinct in the ledger.
    2. Make a best-effort non-billable installation registration on first use or
       after the package version changes. Registration failure never blocks the
       user's voice work.
    3. Call `beatra.voices.list` even when a voice ID was supplied earlier. Match
       the opaque ID exactly and save its current `status`, `language`,
       `languages_supported`, and `compatible_models`. If it is absent or not
       `ready`, do not submit; select another current ready voice or stop. When a
       voice still needs to be chosen, recommend only current ready candidates.
       A `preview_url` is an optional free aid only when the returned voice actually
       has one; never synthesize a paid audition.
    4. Freeze that exact current `voice_id` and its saved compatibility facts. Call
       `beatra.models.list` for `text_to_speech` before comparing models,
       validating a requested language, or estimating price.
    5. Prepare an exact production card: approved text or pilot text, voice ID,
       language when supplied, controls, weighted-character count, current model
       choice, estimate, paid-call count, delivery order, and review target.
    6. Obtain approval for that card, create one new opaque local
       `client_request_id` per logical paid request, submit each request once,
       and poll only its returned task.
    7. Deliver actual artifacts and facts in ledger order. A changed text, voice,
       model, language, or control is new paid work with a new card, approval, and
       request identity.
    
    Use the bundled `scripts/mcp_client.py` for every Beatra operation:
    
    ```text
    python3 scripts/mcp_client.py call <tool-name>
    ```
    
    Pass one JSON object to standard input. Use
    `python3 scripts/mcp_client.py upload <path> --mime-type <exact MIME>` for a
    local sample. Never configure or call a host Beatra Connector. Never use
    REST/OpenAPI as a fallback, and never imitate the upload protocol.
    
    ## Voice, language, and price truth
    
    Validate a supplied language as a BCP-47 tag before using it. Lowercase its
    primary tag, map the documented aliases `nb` to `no` and `tl` to `fil`, and
    apply the same normalization to catalog values; for example, `yue-HK` becomes
    `yue`. The current voice supports the target only when either `voice.language`
    or one entry in `voice.languages_supported` has the same normalized primary
    tag.
    
    For `model: "auto"`, derive the live possible model set as:
    
    ```text
    models.list.auto.candidate_order ∩ selected_voice.compatible_models
    ```
    
    preserving candidate order and keeping only live available cards. Separately
    build the explicit pool from every live card in the exact current voice's
    `compatible_models`; a compatible card outside `candidate_order` is
    explicit-only and never enters the auto set. When the user supplied a language,
    check its normalized primary tag against `constraints.supported_languages` on
    every auto candidate. Keep `auto` only when every candidate supports it. If the
    auto set is empty or any candidate fails, propose one language-compatible card
    from the explicit pool and obtain approval. Without a supplied language, keep a
    nonempty auto set; if it is empty, propose one card from the explicit pool. If
    no applicable explicit card exists, offer another current voice, a user-chosen
    supported language when relevant, or no submission. Language is not a hidden
    auto-selection input.
    
    Speech is estimated from each applicable live card's `estimate_formula`,
    `unit_price_credits`, `scale`, and
    `billing_basis: "beatra_weighted_characters"`. Each Han ideograph counts as
    2 and every other character as 1. Quote one exact estimate when the frozen
    explicit model—or every possible auto model—has the same result; otherwise
    quote the live range. Never invent an auto-resolved model or use remembered
    prices.
    
    Read [voice casting and delivery](references/voice-casting-and-delivery.md)
    for exact model, language, pricing, request, and output handling.
    
    ## Pilots and paid confirmation
    
    Planning, script preparation, voice listing, preview links, model listing, and
    estimates are non-billable. `beatra.speech.synthesize` and
    `beatra.voices.clone` are billable.
    
    - A single approved script uses one approved synthesis card.
    - Long-form work uses one supplied and approved pilot passage before the
      separately approved remainder.
    - Multilingual work uses one supplied and approved pilot for every distinct
      voice/language/model route before a separately approved remainder.
    - A clone is one paid operation. Before any `client_request_id` or
      `beatra.voices.clone` call, show the clone admission card: route
      `voice_clone`, tool `beatra.voices.clone`, live `beatra.models.list` price
      for one successful activated voice, the provisional estimate, the fact that
      the 600-credit signup gift usually cannot start this clone, and what
      happens if the balance is short. Do not create `client_request_id` or
      submit until the user confirms they have topped up or already have enough
      credits for this estimate. Do not offer a free clone or a free sample that
      replaces the clone. On `insufficient_balance`, relay the returned public
      message, keep the URL exact, translate the rest, and retry the same frozen
      `client_request_id` only after the user says they have topped up. Any
      spoken proof is a separate synthesis operation with its own approved text,
      card, approval, and request identity.
    
    A current explicit “generate” or “make it” instruction is approval when it
    fully covers the frozen card. Do not ask for redundant confirmation in that
    case. Price-only, comparison, preparation, general permission to “handle it,”
    and an unfrozen batch or variant set are not approval. Cancellation is
    requested only when the user asks; a conflict or unconfirmed remote stop means
    continue polling the same task.
    
    Read [long-form and multilingual
    production](references/long-form-and-multilingual.md) before any ordered batch,
    and [voice cloning and review](references/voice-cloning-and-review.md) before
    sample upload or clone submission.
    
    ## Execution, recovery, and delivery
    
    Store locally, before submission, the route, exact JSON, production-card
    approval, `client_request_id`, and later `task_id`. The request identity is a
    local recovery key; `tasks.list` and `tasks.get` do not filter by or return it.
    
    If a response or task ID is lost, call `beatra.tasks.list` with the relevant
    capability and follow every `next_cursor` needed to cover the time window.
    Verify plausible candidates with `beatra.tasks.get` using returned task facts.
    Do not submit while a matching task may be queued or running. Reuse the same
    request identity only for a field-for-field identical replay after task
    creation remains genuinely uncertain; any changed field needs new approval
    and a new identity. A slow, failed, canceled, authorization, or update result
    does not authorize an automatic replacement.
    
    For successful speech, report only returned facts: `type`, `model`,
    `voice_id`, `audio.url`, `audio.artifact_id`,
    `audio.duration_seconds`, `audio.mime_type`, `audio.size_bytes`, optional
    `audio.sample_rate`, `characters`, optional `subtitles`, task status,
    resolved model, and billing when present. For a successful clone, preserve the
    exact four-field output: `type`, `voice_id`, `status`, and `display_name`.
    Never claim listening, synchronization, publication, accuracy, durability,
    deletion, or a refund that the returned result does not establish.
    
    Deliver long-form and multilingual results in the approved route, language,
    and segment order. Keep accepted audio unchanged. A correction re-synthesizes
    only an explicitly approved affected segment.
    
    ## Account balance
    
    When the user asks how many credits remain or whether a live estimate fits,
    call `beatra.wallet.get`. When they ask what was charged, call
    `beatra.wallet.ledger`. Both are read-only. Do not invent an account-balance or
    top-up tool. Do not make `wallet.get` a required step before every paid submit.
    
    When a model card comes back carrying a `top_up` block, relay its tiers as the
    card lists them and in that order. Do not rank them, do not talk one down, and
    do not pick one for the user. Which tier suits them is their call, made on
    the wallet page with the whole list in front of them. Never quote a tier from
    memory.
    
    ## References by task
    
    - Read [intent and routing](references/intent-and-routing.md) for the Studio
      matrix, minimum inputs, free planning, and paid pilot boundaries.
    - Read [voice casting and delivery](references/voice-casting-and-delivery.md)
      for exact voice identity, live model/language checks, price math, synthesis
      JSON, and result review.
    - Read [long-form and multilingual
      production](references/long-form-and-multilingual.md) for route ledgers,
      pilot coverage, ordered batches, and interruption recovery.
    - Read [voice cloning and review](references/voice-cloning-and-review.md) for
      consent, sample transport, clone JSON, exact output, and optional proof.
    - Read [bundled MCP Client diagnostics](references/mcp-connection.md) only for
      bundled-client verification and transport troubleshooting.
    - Read [installation and authentication](references/installation-and-auth.md),
      [installation registration](references/installation-registration.md),
      [tasks and results](references/tasks-and-results.md), [billing, errors, and
      recovery](references/billing-errors-and-recovery.md), and [uninstall and
      disconnect](references/uninstall-and-disconnect.md) only when that
      operational branch is active.
    
    ## Updates and security
    
    This Skill runs only the files installed with it and leaves them unchanged while
    it runs; it never downloads or executes other code. It talks to Beatra's official
    services over HTTPS, and a file you choose to upload goes only to the one-time
    upload address Beatra issues for it.
    
    Once a day at most, the bundled client reads Beatra's public version list. When
    the store or tool you installed from has a newer version, it prints one line with
    the command that updates it there. Updating stays your choice, through that store
    or tool. See [updates and security](references/updates-and-security.md).
    
  • skills/voiceover-narration-studio/scripts/authorize.py61 KB70a5b0a1945dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/voiceover-narration-studio/scripts/mcp_client.py86 KB5316b3895e61upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/voiceover-narration-studio/scripts/uninstall.py13 KB6a2afac93cd6upstream ↗
    #!/usr/bin/env python3
    """Uninstall one Beatra Skill package without stranding the shared connection.
    
    The device connection (~/.beatra) is shared by every installed Beatra skill.
    Removing one skill must therefore never touch the shared credential while any
    other skill still uses it — even when the user asks for a complete removal.
    Only when this device's skill inventory says nothing else is left does the
    credential revoke itself and the local state leave with it.
    
    Every uncertain situation resolves to "keep the connection": a wrongly kept
    credential idles out server-side after 15 days and can be revoked from the
    Console at any time, while a wrongly revoked one breaks every remaining skill.
    
    This script never deletes the package directory it runs from — it reports the
    directory so the calling agent can remove it as the final step.
    """
    
    from __future__ import annotations
    
    import argparse
    import json
    import os
    import sys
    import urllib.error
    import urllib.request
    from pathlib import Path
    from typing import Any, Callable
    
    sys.dont_write_bytecode = True  # never leave __pycache__ inside the installed Skill
    
    REVOKE_PATH = "/oauth/device/revoke"
    PACKAGE_SLUG = "voiceover-narration-studio"
    PACKAGE_DISPLAY_NAME = "Voiceover & Narration Studio"
    PACKAGE_VERSION = "0.2.9"
    HTTP_USER_AGENT = f"Beatra-Skill/{PACKAGE_SLUG}/{PACKAGE_VERSION}"
    
    #: Everything the connection owns inside ~/.beatra. Removal unlinks exactly
    #: these and the update records below, then removes each directory only if it
    #: is empty — the script never recursively deletes a directory it does not
    #: fully understand.
    _STATE_FILES = (
        "credentials.json",
        "installation.json",
        "host.json",
        "skills.json",
        "registrations.json",
        "network.json",
        "balance-holds.json",
        "pending_authorization.json",
        ".authorize.lock",
    )
    
    #: The per-install records in ~/.beatra/updates/<id>/: the version check,
    #: install source and registration, and a domestic channel's update lock. Only
    #: these names leave; an interrupted update's transaction stays, and so does
    #: every directory that still holds something.
    _UPDATE_RECORD_FILES = ("state.json", "source.json", "registration.json", "update.lock")
    _UPDATE_RECORD_DIGITS = frozenset("0123456789abcdef")
    
    PostRevoke = Callable[[str, str], int]
    
    
    class _RejectRedirectHandler(urllib.request.HTTPRedirectHandler):
        def redirect_request(
            self,
            _request: urllib.request.Request,
            _file_pointer: Any,
            code: int,
            _message: str,
            _headers: Any,
            _new_url: str,
        ) -> None:
            raise RuntimeError(f"Beatra revocation refused HTTP redirect ({code})")
    
    
    def _revoke_url(state_dir: Path) -> str:
        """Revoke over this computer's recorded network route. Uninstall never asks
        Beatra which route to use: revocation is best effort, and the record it
        would write is about to be removed."""
    
        import mcp_client  # the bundled client beside this file owns the route
    
        return f"{mcp_client.route_api_origin(mcp_client.current_route(state_dir))}{REVOKE_PATH}"
    
    
    def _default_post_revoke(token: str, url: str) -> int:
        request = urllib.request.Request(
            url,
            data=b"",
            headers={
                "Accept": "application/json",
                "Authorization": f"Bearer {token}",
                "User-Agent": HTTP_USER_AGENT,
            },
            method="POST",
        )
        opener = urllib.request.build_opener(_RejectRedirectHandler())
        try:
            with opener.open(request, timeout=30) as response:
                return int(response.status)
        except urllib.error.HTTPError as exc:
            return int(exc.code)
        except urllib.error.URLError as exc:
            raise RuntimeError("Beatra authorization service is unreachable") from exc
    
    
    def _own_skill_root() -> Path | None:
        """The installed package root this script runs from, when knowable."""
        try:
            return Path(__file__).resolve().parent.parent
        except NameError:
            return None
    
    
    def _load_inventory(state_dir: Path) -> tuple[list[dict[str, Any]] | None, str]:
        """Return (entries, state) where state names why entries may be None."""
    
        path = state_dir / "skills.json"
        if not path.exists():
            return None, "inventory_missing"
        try:
            value = json.loads(path.read_text(encoding="utf-8"))
        except (OSError, ValueError):
            return None, "inventory_unreadable"
        loaded = value.get("skills") if isinstance(value, dict) else None
        if not isinstance(loaded, list):
            return None, "inventory_unreadable"
        return [entry for entry in loaded if isinstance(entry, dict)], "ok"
    
    
    def _device_token(state_dir: Path) -> str | None:
        path = state_dir / "credentials.json"
        try:
            value = json.loads(path.read_text(encoding="utf-8"))
        except (OSError, ValueError):
            return None
        token = value.get("access_token") if isinstance(value, dict) else None
        if not isinstance(token, str) or not token:
            return None
        return token
    
    
    def _provably_deleted(install_path: str) -> bool:
        """True only when the entry's directory is verifiably gone.
    
        "Not currently visible" is not "uninstalled": an unmounted volume, a
        permission-restricted parent, or any stat error must count the skill as
        alive — the rule resolves every uncertainty to keeping the connection.
        Only a clean missing entry under a reachable parent is proof of removal.
        """
    
        try:
            os.stat(install_path)
            return False
        except FileNotFoundError:
            pass
        except OSError:
            return False
        parent = os.path.dirname(install_path.rstrip("/\\")) or os.sep
        try:
            os.stat(parent)
        except OSError:
            return False
        return True
    
    
    def _remove_update_records(state_dir: Path) -> None:
        updates = state_dir / "updates"
        try:
            if updates.is_symlink() or not updates.is_dir():
                return
            records = [
                child
                for child in updates.iterdir()
                if len(child.name) == 24
                and set(child.name) <= _UPDATE_RECORD_DIGITS
                and not child.is_symlink()
                and child.is_dir()
            ]
        except OSError:
            return
        for record in records:
            for name in _UPDATE_RECORD_FILES:
                try:
                    (record / name).unlink(missing_ok=True)
                except OSError:
                    pass
            try:
                record.rmdir()
            except OSError:
                pass
        try:
            updates.rmdir()
        except OSError:
            pass
    
    
    def _remove_local_state(state_dir: Path) -> None:
        for name in _STATE_FILES:
            try:
                (state_dir / name).unlink(missing_ok=True)
            except OSError:
                pass
        _remove_update_records(state_dir)
        try:
            state_dir.rmdir()
        except OSError:
            pass
    
    
    def uninstall(
        *,
        state_dir: Path | None = None,
        skill_root: Path | None = None,
        post_revoke: PostRevoke = _default_post_revoke,
        dry_run: bool = False,
    ) -> dict[str, Any]:
        """Apply the last-skill rule and return a machine-readable decision."""
    
        state_dir = (state_dir or Path.home() / ".beatra").expanduser()
        root = skill_root or _own_skill_root()
        resolved_root = str(Path(root).expanduser().resolve()) if root is not None else None
    
        result: dict[str, Any] = {
            "package": PACKAGE_SLUG,
            "dry_run": dry_run,
            "delete_next": resolved_root,
        }
    
        if not state_dir.is_dir():
            # Nothing local to retire; the package files are all that is left.
            result.update({"decision": "disconnected", "revoked": False, "reason": "no_local_state"})
            return result
    
        if resolved_root is None:
            result.update({"decision": "keep_connection", "reason": "unknown_install_path"})
            return result
    
        try:
            entries, inventory_state = _load_inventory(state_dir)
            if entries is None:
                # No trustworthy inventory means no proof this is the last skill —
                # the connection stays, and the 15-day idle expiry backstops it.
                result.update({"decision": "keep_connection", "reason": inventory_state})
                return result
    
            survivors: list[dict[str, Any]] = []
            for entry in entries:
                if (
                    entry.get("slug") == PACKAGE_SLUG
                    and entry.get("install_path") == resolved_root
                ):
                    continue  # the package being uninstalled
                install_path = entry.get("install_path")
                if isinstance(install_path, str) and install_path and _provably_deleted(install_path):
                    continue  # verifiably removed outside this flow — self-heal
                survivors.append(entry)
        except Exception:  # noqa: BLE001 -- any surprise resolves to keeping the
            # connection; a crash here must never read as permission to revoke.
            result.update({"decision": "keep_connection", "reason": "inventory_error"})
            return result
    
        if survivors:
            # The inventory is deliberately not rewritten here: this package's own
            # entry disappears through the provably-deleted check once the agent
            # finishes removing the directory, so an interrupted uninstall leaves
            # a still-installed skill visible rather than silently forgotten.
            result.update(
                {
                    "decision": "keep_connection",
                    "reason": "other_skills_still_installed",
                    "remaining_skills": [
                        {"slug": entry.get("slug"), "platform": entry.get("platform")}
                        for entry in survivors
                    ],
                }
            )
            return result
    
        if dry_run:
            result.update({"decision": "disconnected", "revoked": False, "reason": "dry_run"})
            return result
        token = _device_token(state_dir)
        revoked = False
        revoke_state = "no_credential"
        if token is not None:
            try:
                status = post_revoke(token, _revoke_url(state_dir))
                if status == 200:
                    revoked = True
                    revoke_state = "revoked"
                elif status == 401:
                    # The server no longer recognises this token; the local copy
                    # is worthless either way, but nothing was confirmed revoked.
                    revoke_state = "not_recognized"
                else:
                    # Reached but refused (rate limit, server error): keep every
                    # local file so a retry — or a Console revoke followed by a
                    # rerun — can still finish the job cleanly.
                    result.update(
                        {
                            "decision": "revoke_retry",
                            "revoked": False,
                            "reason": f"http_{status}",
                        }
                    )
                    return result
            except RuntimeError:
                revoke_state = "unreachable"
        token = None
        _remove_local_state(state_dir)
        result.update({"decision": "disconnected", "revoked": revoked, "reason": revoke_state})
        return result
    
    
    def main() -> int:
        parser = argparse.ArgumentParser(
            description=f"Uninstall the {PACKAGE_DISPLAY_NAME} Skill from this device"
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Report the decision without revoking or changing any file.",
        )
        args = parser.parse_args()
        result = uninstall(dry_run=args.dry_run)
        print(json.dumps(result, ensure_ascii=False, indent=2))
        if args.dry_run:
            print("Dry run: nothing was changed. Run without --dry-run to uninstall.")
            return 0
        if result["decision"] == "keep_connection":
            print(
                "The shared Beatra connection stays in place"
                + (
                    " because other installed skills still use it."
                    if result.get("reason") == "other_skills_still_installed"
                    else " because this device's skill inventory could not prove it is unused."
                )
            )
        elif result["decision"] == "revoke_retry":
            print(
                "Nothing was changed: the Beatra server refused the revocation just now. "
                "Run this script again shortly, or revoke the device in the Beatra "
                "Console and rerun to finish removing local state."
            )
            return 0
        elif result.get("revoked"):
            print("The Beatra device authorization is revoked and local state is removed.")
        else:
            print(
                "Local state is removed. The server-side authorization could not be "
                "confirmed revoked from here; it expires after 15 days of inactivity "
                "and can be revoked in the Beatra Console at any time."
            )
        if result.get("delete_next"):
            print(f"Finish by deleting the package directory: {result['delete_next']}")
        return 0
    
    
    if __name__ == "__main__":
        sys.exit(main())
    
  • skills/voiceover-narration-studio/references/billing-errors-and-recovery.md4.8 KB4492c70c65d8upstream ↗
    # Billing, errors, and recovery
    
    Every generation tool consumes Beatra credits and creates an asynchronous task.
    Use one logical request identity across transport uncertainty so recovery cannot
    accidentally create or charge a duplicate task.
    
    ## Submit a billable request
    
    1. Assemble the final generation arguments.
    2. Generate one opaque `client_request_id`.
    3. Submit exactly once.
    4. Store both `client_request_id` and the returned `task_id`.
    5. Poll with `beatra.tasks.get`; do not resubmit while the task is queued or
       running.
    
    The prepaid amount accepted with the request can be an estimate. A successful
    task is settled against authoritative measured usage. Read the terminal
    `usage` and `billing` fields, and report net credits as
    `billing.net_charged_credits`. Do not promise that the initial estimate
    is the final charge.
    
    ## Run a batch
    
    When one request becomes many paid calls (a list of items, a loop, or a
    script):
    
    1. Before the first paid call, price the whole batch from the live prices
       (`beatra.models.list`, or `beatra.social.tools.get` for social data) and
       tell the user the estimated total.
    2. Let the first `insufficient_balance` stop the whole batch. Do not go on to
       the next item; report which items finished and which did not.
    3. Resume only the unfinished items, and only after the user says they have
       topped up. A refused item keeps its `client_request_id` and arguments.
    
    ## Recover an uncertain response
    
    - If no response arrived, make an identical retry with the same
      `client_request_id` and exactly the same generation arguments.
    - If any prompt, input artifact, model, output setting, or other generation
      argument changes, treat it as new work and use a new `client_request_id`.
    - If the task ID was lost, use `beatra.tasks.list` before considering a retry.
    - If remote execution failed, report the terminal error. Start changed or newly
      requested work only with a new request ID.
    
    ## Handle structured failures
    
    - HTTP 401 authentication failure: do not replay the paid call automatically.
      Run `python3 scripts/authorize.py` and follow its output
      until it prints `Beatra is ready`, then recover with the original request
      identity and unchanged arguments.
    - Timeout, DNS/TLS, HTTP 429, HTTP 5xx, or another transport failure: preserve
      the credential. If delivery of a paid call is uncertain, retry only with the
      same `client_request_id` and exactly the same arguments after connectivity
      recovers.
    - Insufficient scope: explain that the active connection lacks required access;
      run `python3 scripts/authorize.py --force` only after the
      user explicitly chooses to reconnect the full Beatra authorization. Never ask
      for a media-specific grant.
    - Insufficient balance (error code `insufficient_balance`): relay the returned
      message to the user — it already carries the facts that matter: nothing was
      charged, the top-up link, credits take effect immediately, and the same
      request can be retried as-is. Translate into the user's language but keep
      the URL exactly as returned. Stop all remaining paid work, including the
      rest of a batch, and do not retry until the user says they have topped up.
      A successful task can also leave a negative balance when actual measured
      usage exceeded the estimate; the output remains valid, but new billable
      work must wait for a top-up.
    - After that refusal, `scripts/mcp_client.py` holds further calls to the same
      tool on this computer: it answers them with the same message and sends
      nothing (`details.local_hold`). Its `details.agent_note` names the balance
      check that lifts the hold once the user says they have topped up. If the user
      chose a cheaper request instead, run that cheaper call once with
      `--recheck-balance`. Never put `--recheck-balance` in a loop or script.
    - Idempotency conflict: the request identity was reused with different
      arguments. Do not retry it. Keep the original task if it exists, or use a new
      `client_request_id` only for user-approved changed work.
    - Rate or concurrency limit: honor the returned retry delay, then retry the
      exact same arguments with the same request identity. Do not create a
      replacement task.
    - Validation failure or model incompatibility: call `beatra.models.list` for current
      constraints, correct the invalid arguments, and use a new
      `client_request_id` because the generation inputs changed.
    - Upload expiration, MIME mismatch, or byte-length mismatch: request a fresh
      grant and upload the unchanged file again. Never reuse a consumed or expired
      grant.
    - Remote execution failure: preserve the Beatra error code and task ID;
      report only the failure classification and billing/refund fields returned by
      Beatra. Do not infer a different cause or promise a refund that is not shown.
    
    Never expose authentication tokens, complete private prompts, or sensitive
    input content while explaining recovery.
    
  • skills/voiceover-narration-studio/references/installation-and-auth.md9.3 KB149c77b01f1aupstream ↗
    # Installation and authentication
    
    Use this reference when Beatra is newly installed, unavailable, or needs its
    authorization recovered. This flow is owned by the Skill and works the same way
    in any agent that can run Python 3.10 or newer and make HTTPS requests. The
    helpers use only the Python standard library; do not install packages for them.
    
    ## Keep one private local identity
    
    The Skill uses two user-only files:
    
    - `~/.beatra/installation.json` contains one stable, non-secret installation
      reference.
    - `~/.beatra/credentials.json` contains the single Device Token.
    
    On POSIX systems the directory must be mode `0700` and both files mode `0600`.
    On Windows the current user must be the only principal granted access through
    the file ACL. Never copy the Device Token into conversation context, stdout,
    stderr, a command argument, an environment variable, a log, a backup, a diff,
    or another file. A completed authorization leaves the token only in
    `~/.beatra/credentials.json`.
    
    ## Ensure Beatra is ready
    
    From the installed `beatra` Skill directory, run:
    
    ```text
    python3 scripts/authorize.py
    ```
    
    Use `python` instead when that is the local Python 3.10+ command. Always run this
    idempotent helper after installing or replacing a Beatra Skill and when
    recovering its connection. It validates the candidate credential with one
    non-billable live MCP request. A valid credential becomes Ready without opening
    the browser, so installing another Beatra package does not require a second
    approval.
    
    When no valid credential exists, or the live MCP endpoint definitively returns
    HTTP 401, the helper:
    
    1. reuses or creates `~/.beatra/installation.json`;
    2. continues an approval that is already open on this computer, or requests a
       new Device Authorization from Beatra. When the user already selected Allow
       on this computer and nothing collected it, Beatra connects at once — no
       second approval;
    3. prints the HTTPS approval link at once and opens it in the user's browser
       (only once for each approval). The URL fragment carries the approval code,
       so the page verifies the code by itself;
    4. waits while it checks every few seconds whether the user selected Allow,
       and keeps checking through brief network failures. One run waits up to
       about 15 minutes; an approval page stays open for up to an hour;
    5. atomically saves the returned Device Token to
       `~/.beatra/credentials.json` without printing an HTTP response body;
    6. validates the new credential with the same non-billable MCP request and
       prints `Beatra is ready` only after it succeeds.
    
    ### Wait for Allow
    
    The user usually needs a minute or more to sign in or create an account before
    selecting Allow. The helper waits for it: leave it running. If your host moves
    the command to the background, let it keep running and do not start a second
    one.
    
    Act on how each run ends:
    
    - `Beatra is ready` — the connection works; continue with the user's request.
    - `BEATRA_AUTH_PENDING` (exit status 3) — the run ended before the user selected
      Allow: the approval page had not been opened yet, the run's wait ran out, or
      the run was stopped. Show the approval link to the user as described below if
      you have not already, and again if the link is different. Run the same command
      at most twice in one turn; it continues the same approval, so the user
      approves only once. When it is still pending after that, deliver what needs
      no connection (below) and stop. When the message says Beatra could not be
      reached, tell the user the connection is being retried instead of asking them
      to approve again.
    - `BEATRA_AUTH_IN_PROGRESS` (exit status 3) — another run on this computer is
      starting an approval right now. Run the same command again in a moment; it
      continues that approval.
    - `Beatra authorization stopped (access_denied)` — the user declined on the
      approval page. A decline is often a mis-click: if the user still wants to
      connect, run the same command again and it opens a new approval page.
    - `Beatra authorization stopped (expired_token)` — the approval page expired
      before the user selected Allow. Tell the user; if they still want to
      connect, run the same command again and share the new approval link.
    
    A run stopped by a timeout or an interrupted tool call counts as one of the
    two runs in the turn: run the same command again once, since it continues the
    approval that is already open, and after a second stop deliver what needs no
    connection and stop.
    
    An approval does not depend on a run being active when the user selects Allow.
    Beatra keeps it, and every Beatra command collects it first: when the user
    writes again after approving, run the helper once (or go on with their
    request) and the connection completes without a second approval. So if the
    user interrupts to ask something else, stop the helper and answer them; and if
    they do not want to connect now, stop waiting — the link stays valid for a
    while.
    
    #### Deliver while the approval is pending
    
    When the approval is still pending at the end of a turn, finish and deliver
    everything in the request that needs no connection, in the format the user
    asked for — the Skill's root names it for each kind of work: the prepared
    text, prompt, lyrics, shot list, or selection criteria. Say in one line that
    the paid step continues on their next message after Allow, because the next
    Beatra command collects the approval. Do not fill the gap with another tool or
    service, and do not name voices, models, prices, or results that no live call
    returned.
    
    When telling the user what to do:
    
    - present the approval page as a link and say the only decision on it is
      selecting Allow;
    - never quote, display, or read out the approval code. It travels inside the
      link fragment, the approval page does not show it, and the user never types
      or compares it — a code surfaced in conversation is noise, not a step;
    - say prominently that a signed-out browser lands on Beatra sign-in first:
      the user signs in or creates their account there, and the approval page
      continues automatically after sign-in;
    - say once that the connection continues automatically after Allow — never
      ask the user to confirm in chat that they approved.
    - on the mainland China network, follow the sign-in and console-link guidance
      in [MCP connection](mcp-connection.md#network-route).
    
    The helper opens no local listener and requires no inbound connection. One
    approval covers image, video, music, speech, upload, model, and task tools.
    
    HTTP 403 or insufficient scope does not prove the credential is invalid. Keep
    the credential and run `python3 scripts/authorize.py --force`
    only when the user explicitly wants to reconnect or switch accounts. A timeout,
    DNS/TLS failure, HTTP 429, or HTTP 5xx is transient: keep the credential, do
    not start Device Authorization, and retry after connectivity recovers.
    
    ### Record where this Skill was installed from
    
    After this Skill first connects, if this conversation shows how this
    Skill was installed — for example an install command or link that names a
    store or site — record it once:
    
    ```text
    python3 scripts/mcp_client.py install-source --guess <source>
    ```
    
    Use one of `website`, `github`, `skillhub`, `skillatlas`, `modelscope`,
    `workbuddy`, `clawhub`, or `other`. If the conversation does not show it,
    skip this and do not ask the user. If the command is not recognized, skip it.
    
    ## Use the bundled MCP client
    
    Every Beatra Skill tool runs through the bundled `scripts/mcp_client.py`, which
    connects to `https://mcp.beatra.ai/mcp`. Do not add, enable, trust, or configure
    a host Beatra Connector. The bundled client reads the shared credential file
    and never accepts the token through argv or the environment:
    
    ```text
    python3 scripts/mcp_client.py verify
    python3 scripts/mcp_client.py tools
    python3 scripts/mcp_client.py call beatra.tasks.list
    ```
    
    The host follows this computer's network route; see [MCP connection](mcp-connection.md).
    
    For `call`, provide the tool arguments as one JSON object on stdin. Do not put
    user content or credentials in the command arguments. The authorization helper
    already performs this verification. Run the explicit `verify` command only for
    diagnostics; it invokes `beatra.tasks.list` with `{}` and is non-billable.
    
    The direct client follows the Streamable HTTP sequence:
    
    1. send `initialize` with the MCP protocol version and client information;
    2. retain an `MCP-Session-Id` response header when the server provides one;
    3. send the `notifications/initialized` notification;
    4. use `tools/list` for discovery and `tools/call` for execution;
    5. include the protocol version, optional session ID, and Device Token bearer
       header on every request.
    
    ## Recover or revoke access
    
    The single Device Token has a sliding 15-day idle lifetime. Every accepted MCP
    use extends that idle deadline without user action. If Beatra returns HTTP 401
    after the token was idle too long or was revoked, run
    `python3 scripts/authorize.py` and act on its output as
    described above; the helper replaces and validates the credential. Do not start another
    authorization for a media-type change and do not delete a credential because of
    a transient connection failure.
    
    The user can revoke a connected agent from the Beatra Console at any time.
    Revocation immediately invalidates the credential. Rerun Device Authorization
    only when the user wants to reconnect that installation.
    
    After first installing or replacing the Skill, start a new agent session when
    the host discovers Skills only at session startup.
    
  • skills/voiceover-narration-studio/references/installation-registration.md2.0 KB7da7c1c9b57cupstream ↗
    # Installation registration
    
    On first use, and again when the version, agent environment, channel, network
    route, install source or installation reference changes or 24 hours pass, the
    bundled client makes one best-effort, non-billable
    `beatra.installations.register` call for this package. It is the only reporting call
    the Skill makes, it does not replace Device Authorization, it never asks for
    narrower package scopes, and its failure never blocks the user's requested
    task. It records:
    
    - the package, its version, the agent environment (`platform`), and the
      stable installation reference;
    - the distribution channel and locale fixed when the package was built;
    - the network route this computer uses and why (see the MCP connection
      reference);
    - where this copy was installed from, when known: a source file Beatra adds to
      copies it hands to a store or its website, a store client's own record in
      the Skill directory, or the agent's `install-source --guess`;
    - coarse runtime facts: operating system and version, processor architecture,
      Python version, interface language, and time-zone offset.
    
    It never sends user names, file paths, environment variables, prompts, or tool
    details. Beatra uses these fields to understand which stores and networks
    people install from, as its privacy policy describes, and clears the runtime
    facts when the account is deleted.
    
    A per-installation cache in `~/.beatra/updates/` suppresses another
    registration until the version, agent environment, channel, network route, install
    source or installation reference changes, or 24 hours pass. An unknown but well-formed package
    slug is accepted and recorded as unrecognized, so package release never
    depends on a synchronized backend registry deployment.
    
    The `platform` field is the real agent environment, resolved once per process
    from environment signatures (or `~/.beatra/host.json`, written at
    authorization time) — never detected per request. When authorizing manually,
    pass `--platform <environment-name>` to `scripts/authorize.py` if automatic
    detection cannot identify the host.
    
  • skills/voiceover-narration-studio/references/intent-and-routing.md4.1 KB51d2828f275cupstream ↗
    # Intent and routing
    
    ## Keep the Studio central only when it adds coordination
    
    Choose one primary route, reuse known facts, and ask only for a missing hard
    input or a choice that changes paid work.
    
    | Request shape | Route | Minimum before a paid call |
    | --- | --- | --- |
    | One short-video, advertisement, social, or promo voiceover | `short-form-voiceover-audio` | Final supplied or approved text and an exact current voice |
    | Audiobook, course, serialized narration, or other ordered long-form work | `ai-audiobook-narration` | Approved segment plan, pilot text, and narrator |
    | Supplied target-language scripts for a multilingual production | `ai-multilingual-dubbing` | Approved text per language and one valid voice route per language |
    | One authorized reusable voice | `voice-cloning-studio` | Explicit speaker authorization, valid sample, and display name |
    | Mixed, ongoing, or cross-route production | This Studio | An ordered route ledger and the first exact paid card |
    
    Do not move a mixed request wholesale into a focused package. Sequence its
    parts. For example, create and finish an authorized clone before resolving its
    returned `voice_id` for later synthesis; pilot each distinct multilingual
    route before approving its remainder.
    
    ## Minimum questions and positive defaults
    
    For synthesis, ask for final text or permission to prepare a speakable version,
    and ask for a target language only when the user intends a language not already
    clear from the supplied text or context. Do not ask for language merely because
    the request omitted it. Ask for voice preferences only when no voice has been
    selected and “choose for me” is not already an answer.
    
    Use `model: "auto"`, MP3, speed 1.0, volume 1.0, pitch 0, no emotion, and no
    sample rate unless the user's destination changes one of those choices. Treat
    duration as a review target, not a guarantee. Preserve supplied claims,
    pronunciations, names, ordering, and target-language text.
    
    For cloning, the one non-negotiable first fact is consent. A clear statement
    that it is the user's own voice is enough; otherwise obtain a clear statement
    that the speaker authorized cloning. Do not turn that confirmation into
    paperwork. Stop before upload while it is missing.
    
    ## Free planning and paid units
    
    Free planning includes:
    
    - routing and ledger preparation;
    - speakable edits shown for approval;
    - `beatra.voices.list`, returned preview links, and casting comparison;
    - `beatra.models.list`, compatibility and language checks, and price estimates;
    - search and reconciliation of already-created tasks.
    
    Each `beatra.speech.synthesize` and `beatra.voices.clone` submission is a
    separate billable request. A card must state the exact approved text or sample,
    voice and model decision, controls, count, current estimate, delivery position,
    and what the user will review.
    
    ## Pilot coverage
    
    - For one supplied and approved short script, the one synthesis is the complete
      approved unit; do not add alternate takes.
    - For long-form work, select one representative supplied passage and obtain
      approval for that pilot alone. Review it, then present the remaining ordered
      segments and total estimate as a separate approval.
    - For multilingual work, define a distinct route by its voice, BCP-47 language,
      and possible or explicit model set. Run one supplied and approved pilot for
      every distinct route. Only after all required pilots are accepted may the
      remaining language-by-segment matrix receive a separate approval.
    - A clone does not include a spoken proof. If the user wants one, it begins
      after clone success as a separate text-to-speech card.
    
    Never use invented text for a paid sample. General permission, earlier pilot
    approval, or clone approval does not approve a remainder, another language,
    an alternate take, or a spoken proof.
    
    ## Adjacent outcomes
    
    Keep this package's result at ready-to-edit speech audio or a returned reusable
    voice. A request for transcription needs text first. Existing-audio repair,
    mixing, synchronization, video, avatar, lip-sync, music, and publication use
    their own workflows. Preserve and complete the voice component rather than
    claiming those adjacent deliverables.
    
  • skills/voiceover-narration-studio/references/long-form-and-multilingual.md4.0 KB2b96d0c33435upstream ↗
    # Long-form and multilingual production
    
    ## Build one ordered ledger
    
    Split only at natural chapter, lesson, scene, or paragraph boundaries. One
    request accepts at most 50,000 characters, but the approved editorial
    boundaries and review cost normally justify smaller segments. Record:
    
    - route, language, segment number, title, and exact approved text;
    - exact `voice_id`, possible auto set or explicit model, format, and controls;
    - weighted-character count, current formula, price or range, and approval;
    - local `client_request_id`, returned `task_id`, terminal state, artifact,
      and billing facts when they exist.
    
    Every segment is one logical paid request. Generate IDs only after approval.
    Do not submit the next batch merely because the ledger has been prepared.
    
    ## Pilot a long-form route
    
    Choose one representative passage from text the user supplied and approved.
    The pilot card covers only that passage. After the returned audio is reviewed
    for narrator fit, readings, pacing, tone, and destination, freeze any accepted
    settings.
    
    Then present the remaining ordered segment count, exact or ranged total
    estimate, paid-call count, and delivery order for a separate approval. A pilot
    approval never authorizes the remainder. Keep a single accepted narrator and
    settings unless the user explicitly approves a route change.
    
    ## Pilot every distinct multilingual route
    
    The target-language texts must already be supplied or shown and approved.
    This workflow does not silently translate, invent, or merge scripts.
    
    Define a route by:
    
    - exact voice ID;
    - supplied BCP-47 language;
    - ordered possible auto set or explicit model;
    - controls and output format.
    
    Validate both layers: the selected voice must list the language, and the live
    model decision must follow `constraints.supported_languages`. One voice may
    serve several languages only when each route passes independently.
    
    Run one approved pilot for every distinct route. A pilot in one language or
    with one voice/model route does not authorize another. After all required
    pilots are accepted, present the complete language-by-segment remainder,
    weighted totals, price per route, total range or upper bound, paid-call count,
    and delivery order for separate approval.
    
    ## Submit in controlled order
    
    Submit each approved JSON once and poll its task to terminal before moving
    beyond the concurrency and order the user approved. Keep results grouped by
    route and language, then ordered by segment:
    
    ```text
    <language-or-route>/01-<segment>
    <language-or-route>/02-<segment>
    ```
    
    Do not represent the audio as synchronized, mixed, merged, or published.
    Deliver ready-to-edit artifacts with their actual media facts.
    
    ## Continue after interruption
    
    Start from the ledger. Poll every known `task_id` before looking for more work.
    For a lost task ID, call `beatra.tasks.list` with
    `capability: "text_to_speech"` and paginate through every `next_cursor` needed
    to cover the relevant window. Verify candidates with `beatra.tasks.get`,
    comparing capability, time, returned input, voice, settings, and task facts.
    
    The local `client_request_id` is not a remote filter or returned task field.
    Do not resubmit while the original may exist. Reuse it only for an identical
    JSON replay when task creation remains genuinely uncertain after
    reconciliation. Any changed text, language, voice, model, or control is a new
    paid request with a new card, approval, and ID.
    
    Resume only ledger rows with no completed artifact and no known queued or
    running task. Accepted rows remain final. If the user requests cancellation,
    call `beatra.tasks.cancel` for the exact task; a conflict or unconfirmed stop
    means continue polling that same task rather than create a replacement.
    
    ## Correct only the approved segment
    
    For a misread word, correct the supplied reading and show the changed segment.
    For pace or tone, adjust the smallest relevant control. Present the affected
    text, new settings, incremental estimate, and one new paid request. After
    approval, use a new identity and preserve every accepted segment.
    
  • skills/voiceover-narration-studio/references/mcp-connection.md3.3 KB9365c7be5051upstream ↗
    # MCP connection
    
    All Beatra Skill packages use one connection path:
    
    ```text
    scripts/mcp_client.py -> https://mcp.beatra.ai/mcp
    ```
    
    The host follows this computer's network route, described below.
    
    They share the one full-scope Device Token stored in
    `~/.beatra/credentials.json`. Installing or switching packages must not trigger
    another authorization when that credential is already valid. Do not add,
    enable, trust, or configure a host Beatra Connector. Never print or move the
    token into command arguments, environment variables, logs, chat, or another
    package directory.
    
    The bundled client performs MCP `initialize`, retains `MCP-Session-Id` when the
    server returns one, sends `notifications/initialized`, and then uses
    `tools/list` or `tools/call`. Its `verify` command runs the non-billable
    `beatra.tasks.list` operation. It supplies the private `Authorization: Bearer`
    header on every request without exposing its value.
    
    Source attribution: every `tools/call` may carry two optional string
    arguments, `source_package_slug` (this package's slug) and `source_platform`
    (the agent environment, for example `claude-code` or `codex`). The bundled
    client adds them automatically. They are telemetry only: missing values never
    block a call, and the server treats them as `unknown`.
    
    ## Network route
    
    Beatra serves two networks: the international hosts, and the mainland China
    hosts `mcp.beatra.cn`, `api.beatra.cn`, `www.beatra.cn`, `cdn.beatra.cn` and
    `console.beatra.cn`. The saved credential works on both. Packages of the
    mainland China (`cn`) channel always use the mainland hosts and cannot change
    network (`network` shows `"policy":"cn"`). Other packages choose once a day,
    before their first command: the client asks Beatra which network suits this computer,
    in a small request that carries no credential and no installation identity,
    and records the answer in `~/.beatra/network.json`. Approval pages, console and
    top-up links, uploads, and result downloads then follow the chosen network.
    
    ```text
    python3 scripts/mcp_client.py network
    python3 scripts/mcp_client.py network --use auto
    ```
    
    `network` shows the current choice. Only when the user asks for a specific
    network, run `network --use cn` (mainland China) or `network --use ai`
    (international): it first checks that Beatra answers on that network and
    refuses otherwise; packages of the `cn` channel refuse `--use`. If commands start failing to connect after a network was
    chosen this way, run `network --use auto`. A request that failed is never
    resent on the other network; the next command decides again unless a network was chosen with
    `network --use`. A chosen network also keeps the version-list check on it.
    
    On the mainland China network the approval page offers WeChat, phone and email
    sign-in. If the user cannot find their sign-in method, or wants to sign in with
    Google, run `python3 scripts/authorize.py --approval-link ai`: it shows the same
    approval on Beatra's international site, and the network does not change;
    `--approval-link auto` goes back. When you give the user a console or top-up
    link on the mainland network, remind them to sign in the way they did before
    (phone, email, or the same WeChat): scanning with a different WeChat creates a
    separate account. If credits do not appear after a top-up, first check that
    they signed in to the same account.
    
  • skills/voiceover-narration-studio/references/tasks-and-results.md2.2 KB34977869c667upstream ↗
    # Tasks and results
    
    Generation returns an asynchronous task. Keep its `task_id` and use
    `beatra.tasks.get` to poll it with bounded backoff: wait 2 seconds before the
    first poll, then increase the interval toward 5, 10, and at most 15 seconds.
    Honor `deadline_at` when the task response provides it. Otherwise stop active
    polling after 30 minutes, report the task ID and current status, and explain
    that the user or a later agent session can resume polling the same task.
    
    ## Interpret state
    
    - `queued` and `running` are nonterminal. Continue polling; do not submit a
      replacement generation.
    - `succeeded`, `failed`, and `canceled` are terminal. Stop polling.
    - On success, present returned artifacts, resolved model, actual usage, and
      billing truth. For prepaid work, report `billing.net_charged_credits`; the gross charged value
      may include a supplemental actual-usage debit and the refunded value may
      return an unused request-time estimate.
    - For a settled video task, `usage.video_seconds` is the authoritative
      billable total. When present, `usage.input_video_seconds` and
      `usage.output_video_seconds` are the authoritative components of that total.
      This distinction matters for reference-video generation and video editing,
      where both input and output can be billable. Do not infer either component
      when Beatra returns it as `null`.
    - On failure, preserve the structured error and apply the recovery reference.
    
    Use `beatra.tasks.list` to recover a lost task ID or inspect recent work from
    this connection. Filter only when doing so helps identify the task.
    
    Call `beatra.tasks.cancel` only when the user requests cancellation. Before
    remote execution starts, cancellation stops the task. After remote execution
    starts, Beatra returns `canceled` only when the stop is confirmed. A `409`
    means the task continues: do not promise a stop or refund, do not submit a
    replacement, and continue polling the same task.
    
    Do not infer an artifact URL, completion, usage, or charged credits from
    elapsed time. Return only fields present in Beatra's task response.
    For MCP task reads, prefer the direct `structuredContent.usage` and
    `structuredContent.billing` objects; the complete envelope remains available
    under `structuredContent.task`.
    
  • skills/voiceover-narration-studio/references/uninstall-and-disconnect.md2.7 KB5435c8ecc181upstream ↗
    # Uninstall and disconnect
    
    Use this workflow when the user asks to remove, uninstall, or delete this
    package, or to disconnect this device from Beatra entirely.
    
    ## The one rule
    
    The device connection (`~/.beatra`) is shared by every installed Beatra skill.
    It may only be retired when no other Beatra skill remains on this device — and
    `scripts/uninstall.py` is the only component that can decide that. Never delete
    `~/.beatra` or any file inside it yourself, and never skip the script, even
    when the user asks for a complete removal: if another installed skill still
    uses the connection, the connection must survive this uninstall.
    
    ## Steps
    
    1. Run the bundled decision script first, while the package files still exist:
    
       ```
       python3 scripts/uninstall.py
       ```
    
       `--dry-run` prints the same JSON decision without changing anything or
       contacting the server. Never act on a dry-run report — rerun without the
       flag to actually uninstall.
    
    2. Read the JSON decision it prints:
    
       - `"decision": "keep_connection"` — another installed skill still uses the
         shared connection (`remaining_skills` lists them), or the inventory could
         not prove this is the last one. The credential and `~/.beatra` stay
         untouched. Tell the user which skills keep the connection alive.
       - `"decision": "disconnected"` — this was the last Beatra skill. The script
         has removed the local connection state. `"revoked": true` means the
         server confirmed the revocation; `"revoked": false` means it could not be
         confirmed from here (server unreachable, or the token was no longer
         recognised) — the authorization then expires on its own after 15 days of
         inactivity, and the user can revoke it immediately in the Beatra Console
         under Agents.
       - `"decision": "revoke_retry"` — the server was reached but refused just
         now (for example rate limiting). Nothing was changed. Run the script
         again shortly, or have the user revoke the device in the Beatra Console
         and then rerun it to finish removing local state.
    
    3. After a `keep_connection` or `disconnected` decision, finish by deleting
       only the package directory the script names in `delete_next`. Do not delete
       anything else. (After `keep_connection`, the inventory notices the deleted
       directory by itself — no further bookkeeping is needed.)
    
    ## Disconnecting everything
    
    There is no separate "disconnect all" action. Uninstall each installed Beatra
    skill with its own bundled script; the last one automatically revokes the
    authorization and clears `~/.beatra`. The user can always revoke the device
    from the Beatra Console as well — that invalidates the credential server-side
    immediately, whatever remains on disk.
    
  • skills/voiceover-narration-studio/references/updates-and-security.md5.9 KB5c0172b54189upstream ↗
    # Updates and security
    
    Since the October 2026 releases, Beatra Skill packages contain no self-updater.
    Earlier releases could replace their own files from Beatra's CDN; that code has
    been removed. Updates now come only from the store or tool you installed the
    Skill from.
    
    ## What runs
    
    Every operation runs one of the bundled scripts: `scripts/mcp_client.py`,
    `scripts/authorize.py`, or `scripts/uninstall.py`. They use only the Python
    standard library. `scripts/authorize.py` opens the Beatra approval page in
    this computer's web browser when approval is still needed. They never
    download, write, or execute other code, and the files of this installed Skill
    stay exactly as installed.
    
    ## Where requests go
    
    Requests use HTTPS and follow this computer's network route, described in
    [MCP connection](mcp-connection.md): the international hosts, or the mainland
    China hosts `mcp.beatra.cn`, `api.beatra.cn`, and `www.beatra.cn`. When you
    choose to upload a local file, it goes only to the one-time upload address
    Beatra issues for that upload. Choosing the network may send a small request
    to the other network, and so may retrying the version list once, unless a
    network was chosen with `network --use`.
    
    ## What is stored on this computer
    
    These files are written under `~/.beatra/`. On POSIX that directory is mode
    0700. Nothing is written inside the installed Skill directory.
    
    - `credentials.json` (POSIX mode 0600), written by `scripts/authorize.py`: the device credential.
    - `installation.json` (POSIX mode 0600), written by `scripts/authorize.py`: the stable installation reference.
    - `host.json` (POSIX mode 0600), written by `scripts/authorize.py`: the agent environment, and a device name when one is known.
    - `network.json` (POSIX mode 0600), written by `scripts/mcp_client.py`: this computer's network route.
    - `skills.json` (POSIX mode 0600), written by `scripts/authorize.py` and `scripts/mcp_client.py`: which Beatra skills are installed here, with the slug, agent environment, install path, and time.
    - `pending_authorization.json` (POSIX mode 0600), written by `scripts/authorize.py`: an approval that is still open. Removed when that approval is collected or discarded.
    - `.authorize.lock` (POSIX mode 0600), written by `scripts/authorize.py` while authorization is in progress, then removed.
    - `balance-holds.json` (POSIX mode 0600), written by `scripts/mcp_client.py`: a local record after a tool is refused for insufficient credits. Removed when no such record remains.
    
    `scripts/mcp_client.py` also keeps one directory per installation under
    `~/.beatra/updates/`. Its name is the first 24 hexadecimal characters of the
    SHA-256 of the resolved installation path, and on POSIX the directory is mode
    0700. The files inside it are POSIX mode 0600:
    
    - `state.json`: the time of the last version check.
    - `registration.json`: the cache of the last installation registration.
    - `source.json`: where this copy was installed from, when that is known.
    
    Replacing one of these files writes a temporary file in the same directory and
    then renames it over the old file.
    
    When `scripts/uninstall.py` decides this is the last Beatra skill on the
    computer, it first asks Beatra to revoke the device credential when one is
    stored. If Beatra refuses, it changes nothing so the run can be repeated.
    Otherwise it unlinks exactly these names in `~/.beatra/`: `credentials.json`,
    `installation.json`, `host.json`, `skills.json`, `registrations.json`,
    `network.json`, `balance-holds.json`, `pending_authorization.json`, and
    `.authorize.lock`. It also unlinks `state.json`, `registration.json`, and
    `source.json` in each installation directory under `~/.beatra/updates/`. It
    then removes each of those directories, `~/.beatra/updates/`, and `~/.beatra/`
    only when that directory is empty. These
    scripts do not create `registrations.json`; uninstall still unlinks that name
    if it is present. The script never deletes the installed Skill directory. It
    names that directory in `delete_next` for the caller to delete. If another
    skill remains, or the inventory cannot prove this is the last one, it leaves
    `~/.beatra/` unchanged.
    
    ## What is reported
    
    Beyond the requests a command makes, the only report is the installation
    registration described in [installation registration](installation-registration.md).
    No prompts, file contents, file paths, or tool-call details are sent with it.
    Each tool call itself names this package and the agent environment (for example
    `claude-code`), as source attribution in [MCP connection](mcp-connection.md)
    describes, so a task can be traced to the Skill that made it.
    
    ## Version notice
    
    At most once every 24 hours, before an ordinary command, the bundled client
    reads Beatra's public version list for this package
    (`/skills/<package>/sources.json` on the site host of the current network). It
    works out from local records which store or tool installed this copy. When that
    place offers a newer version, it prints one line on standard error with the
    command that updates it there. It never installs anything. If the check fails,
    nothing is printed and the command runs as usual. A copy with no such record
    gets no notice.
    
    | Installed with | Update with |
    | --- | --- |
    | ClawHub CLI | `clawhub update <package> --workdir <dir> --dir <skills-dir>` (add `--force` if it reports local changes) |
    | SkillHub CLI | `skillhub install <package> --force --dir <skills-dir>` |
    | GitHub CLI | `gh skill update <package>`, run in the project directory for a project install |
    | skills CLI (GitHub or ModelScope) | `npx skills update <package> -p -y`, run in the project directory; or `npx skills update <package> -g -y` for a global install |
    | WorkBuddy | Update the Skill in WorkBuddy |
    | Beatra website (English packages) | Reinstall from the package's install page |
    
    ## Check by hand
    
        python3 scripts/mcp_client.py update --check
    
    It reports what the store or tool you installed from offers now. It installs
    nothing and leaves the Skill's files unchanged. If `python3` is not the local
    Python command, use the available Python 3.10+ command.
    
  • skills/voiceover-narration-studio/references/voice-casting-and-delivery.md5.9 KBc2d42f34b4daupstream ↗
    # Voice casting and delivery
    
    ## Resolve an exact current voice
    
    Always call `beatra.voices.list`, even when the user or conversation already
    contains a `voice_id`. Match that opaque ID exactly and save its current
    `status`, `language`, `languages_supported`, and `compatible_models`. If the
    ID is absent or not `ready`, do not submit; offer another returned current ready
    voice or stop. Use `language`, `gender`, `category`, or `query` only when the
    user's preferences justify the filter. A returned voice may
    contain `display_name`, `category`, `language`, `languages_supported`,
    `gender`, `age`, `accent`, `use_case`, `preview_url`,
    `compatible_models`, `status`, and `created_at`.
    
    Recommend only ready candidates. Keep the opaque `voice_id` exactly; a display
    name, description, or previously remembered voice fact is not a substitute for
    the fresh exact-ID lookup. Offer
    `preview_url` only when it is returned, and never synthesize a paid audition.
    
    ## Build the possible auto set
    
    Call `beatra.models.list` with:
    
    ```json
    {"capability":"text_to_speech"}
    ```
    
    Define the possible auto set as the ordered intersection of
    `auto.candidate_order` and the exact current voice's `compatible_models`, using
    only currently available model cards. Separately build an explicit pool from
    every live available card in `compatible_models`. A compatible card outside
    `candidate_order` is explicit-only and must never be mixed into the auto set.
    Do not infer an auto result before admission.
    
    Validate a supplied language against the BCP-47 shape used by the current
    contract: a 2-to-8-letter primary tag followed by optional 1-to-8-character
    alphanumeric subtags. Lowercase the primary tag and normalize the documented
    aliases `nb` to `no` and `tl` to `fil`; normalize catalog values the same way,
    so `yue-HK` compares as `yue`.
    
    When the user supplied a valid BCP-47 language:
    
    1. Confirm either the returned `voice.language` or one entry in
       `voice.languages_supported` has the same normalized primary tag.
    2. Read `constraints.supported_languages` from every model in the possible
       auto set and compare their normalized primary tags.
    3. Keep `model: "auto"` only when every candidate supports the target.
    4. If the auto set is empty or any candidate fails, select one qualifying card
       from the full live voice-compatible explicit pool and obtain confirmation.
    5. If the explicit pool has no qualifying card, propose another current ready
       voice, let the user voluntarily choose another supported target, or make no
       submission.
    
    If no language was supplied, omit `language`. Do not ask for one only to make
    the catalog decision. Keep a nonempty voice-first auto set; if it is empty,
    propose one live card from the explicit pool. If that pool is also empty, offer
    another current ready voice or no submission. Do not treat language as an
    undocumented auto selection input.
    
    ## Controls and approved text
    
    The text must be supplied or shown and approved. Preserve names, claims,
    pronunciations, and meaning. Convert written constructions into speakable
    phrasing only with approval when the edit is material. Use punctuation to
    express breaths and pauses.
    
    Current synthesis controls are `format` (`mp3`, `wav`, `flac`, `opus`, or
    `pcm`), optional `sample_rate`, `speed` from 0.5 to 2.0, `volume` greater
    than 0 and at most 10, `pitch` from -1 to 1 (normalized: 0 keeps the
    voice's original pitch; use decimals like 0.25 for subtle tuning), and
    optional `emotion` from the live supported values. Keep defaults unless the
    destination or user requires a change.
    
    ## Calculate the current estimate
    
    Count `beatra_weighted_characters` exactly: each Han ideograph weighs 2 and
    every other character weighs 1. For every applicable model, use the card's
    `estimate_formula`:
    
    ```text
    (unit_price_credits * billable_quantity) / scale
    ```
    
    Preserve its current `meter`, `unit`, `billing_basis`,
    `unit_price_credits`, and `scale`. An explicit model produces one estimate.
    Auto produces one exact estimate only if all possible candidates have the same
    result; otherwise show the minimum and maximum. Never quote a remembered price
    or raw character count.
    
    ## Freeze and submit one request
    
    The production card contains the exact approved text, exact current `voice_id`,
    language if supplied, model or honest auto set, format and controls, weighted
    quantity, live formula and estimate, one paid call, and delivery position.
    An explicit current “generate” or “make it” that fully covers this frozen card
    is approval; do not ask again. A price question, comparison, preparation
    request, or unfrozen batch is not. After approval, create one new local
    `client_request_id` and send exactly:
    
    ```json
    {
      "voice": "voice_...",
      "input": "The exact approved text.",
      "model": "auto",
      "format": "mp3",
      "speed": 1.0,
      "volume": 1.0,
      "pitch": 0,
      "client_request_id": "local-opaque-id"
    }
    ```
    
    Include `language`, `emotion`, or `sample_rate` only when frozen in the card.
    Pass this JSON on standard input to:
    
    ```text
    python3 scripts/mcp_client.py call beatra.speech.synthesize
    ```
    
    Submit once and poll the returned `task_id` with `beatra.tasks.get` until a
    terminal state. Do not create an alternate take or automatic retry.
    
    ## Review and deliver returned facts
    
    A successful `SpeechOutput` contains:
    
    - `type: "text_to_speech"`, concrete `model`, and `voice_id`;
    - `audio.url`, `audio.artifact_id`, `audio.duration_seconds`,
      `audio.mime_type`, `audio.size_bytes`, and optional `audio.sample_rate`;
    - `characters` and optional `subtitles`.
    
    Return those facts with the task's actual status, resolved model, usage, and
    billing when present. Compare returned duration with a target without claiming
    exact timing. Ask the user to listen for readings, pauses, pace, tone, and
    destination fit; do not claim to have heard audio unless it was actually
    available for review.
    
    A revision changes the production card, needs a new approval and request
    identity, and re-synthesizes only the approved affected segment. Keep every
    accepted artifact unchanged.
    
  • skills/voiceover-narration-studio/references/voice-cloning-and-review.md5.4 KBd33d2f760e1bupstream ↗
    # Voice cloning and review
    
    ## Confirm consent before transport
    
    Accept a plain statement that the sample is the user's own voice. For another
    speaker, require a plain statement that the speaker explicitly authorized
    cloning. A file, contract access, publication, public-figure recording, or
    suggestive filename is not that confirmation. Do not upload the sample and do
    not set `consent_attested: true` while the fact is missing.
    
    ## Prepare one supported sample
    
    Read the current `voice_clone` model card before judging technical readiness.
    The current card accepts `artifact`, `data_uri`, and `https_url` samples,
    requires 10 to 300 seconds, limits the sample to 20 MiB (20,971,520 bytes),
    requires consent attestation, and activates a successful voice automatically.
    Treat these as live card facts, not permanent prose promises.
    
    Prefer one speaker talking naturally without music, other voices, heavy noise,
    or pronounced echo. For a local file, preserve its exact filename, MIME type,
    and byte count, then run only after consent:
    
    ```text
    python3 scripts/mcp_client.py upload <path> --mime-type <exact MIME>
    ```
    
    Record the returned `artifact_id`. Do not call `beatra.assets.upload` directly
    for the file body and do not teach a raw PUT. The shared data-URI transport cap
    is 256 KiB encoded; use the bundled upload helper or an HTTPS URL for a larger
    otherwise-valid sample.
    
    The three exact `MediaInputRef` shapes are:
    
    ```json
    {"type":"artifact","artifact_id":"artifact_..."}
    {"type":"data_uri","data":"data:audio/...;base64,..."}
    {"type":"url","url":"https://example.com/authorized-sample.wav"}
    ```
    
    ## Freeze one clone card
    
    Call `beatra.models.list` for `voice_clone`. The card must include the exact
    sample reference and observed readiness, display name, optional BCP-47 language
    hint, model decision, current task price, `meter: "task"`, `unit: "task"`,
    the live `unit_price_credits`, `scale: 1`,
    `billing_basis: "successful_activated_voice"`, quantity 1, the live estimate
    formula, one paid clone, and the review plan.
    
    `language` is only a clone hint; the clone card does not publish supported
    languages. Do not claim language coverage from it. Resolve `auto` according to
    the current card. If it is deterministic, quote the one current default price;
    otherwise preserve every possible live price as a range.
    
    Before creating `client_request_id` or submitting `beatra.voices.clone`, show
    the clone admission card: route `voice_clone`, tool `beatra.voices.clone`,
    live `beatra.models.list` price for one successful activated voice, the
    provisional estimate, the fact that the 600-credit signup gift usually cannot
    start this clone, and what happens if the balance is short. Do not create
    `client_request_id` or submit until the user confirms they have topped up or
    already have enough credits for this estimate. Do not offer a free clone or a
    free sample that replaces the clone. On `insufficient_balance`, relay the
    returned public message, keep the URL exact, and retry the same frozen
    `client_request_id` only after the user says they have topped up.
    
    After that top-up or balance confirmation, create one local request identity
    and pass:
    
    ```json
    {
      "sample": {"type":"artifact","artifact_id":"artifact_..."},
      "display_name": "Approved display name",
      "model": "auto",
      "consent_attested": true,
      "client_request_id": "local-opaque-id"
    }
    ```
    
    Include `language` only when the user supplied the hint. Send the object on
    standard input to:
    
    ```text
    python3 scripts/mcp_client.py call beatra.voices.clone
    ```
    
    Submit once and poll only its returned task.
    
    ## Preserve the exact clone result
    
    Successful `VoiceCloneOutput` has exactly:
    
    ```json
    {
      "type": "voice_clone",
      "voice_id": "voice_...",
      "status": "ready",
      "display_name": "Approved display name"
    }
    ```
    
    Lead with the named ready voice and preserve the opaque `voice_id` for later
    selection. Do not invent similarity, language coverage, sample retention,
    deletion, duration, artifact, or client-request fields that the output does
    not contain.
    
    ## Make a spoken proof only by separate choice
    
    Clone approval does not authorize speech synthesis. If the user requests a
    proof after success:
    
    1. Call `beatra.voices.list` and locate the exact returned `voice_id`.
    2. Use its current `compatible_models` and the live text-to-speech cards.
    3. If a language was supplied, validate both the voice and every possible auto
       model; use auto only when all candidates support it.
    4. Use text the user supplied or approved, count weighted characters, and
       prepare a separate synthesis card and estimate.
    5. Obtain separate approval, create a new request identity, submit
       `beatra.speech.synthesize` once, and review the actual returned audio.
    
    ## Recover without duplicating either paid step
    
    Record clone and optional proof as distinct ledger rows. If a clone task ID is
    lost, paginate `beatra.tasks.list` with `capability: "voice_clone"` through
    every relevant `next_cursor` and verify candidates with `beatra.tasks.get`.
    The request identity remains local-only. Replay the same identity only when
    creation remains genuinely uncertain and the JSON is field-for-field
    identical.
    
    For a lost proof, use the same process with
    `capability: "text_to_speech"`. A new display name, sample, text, voice, model,
    language, or control is changed paid work requiring a new card, approval, and
    identity. Never automatically retry a failed or canceled task, and never
    replace missing charged, refunded, or net values with zero.
    
  • skills/voiceover-narration-studio/install-source.json39 B71bdc5669a3bupstream ↗
    {"schema_version":1,"source":"github"}
    
  • skills/voiceover-narration-studio/manifest.json4.8 KB82ac383408ffupstream ↗
    {
      "changelog": "The helper shared by all Beatra Skills now runs its connection step at most twice in one turn. While your approval is still pending, the Skill delivers the part of your request that needs no connection and continues the paid step on your next message after Allow.",
      "display_name": "Voiceover & Narration Studio",
      "entry": "SKILL.md",
      "homepage": "https://beatra.ai/skills/voiceover-narration-studio",
      "icon": "https://beatra.ai/textures/icons/voiceover-narration-studio-0.2.8.png",
      "localized": {
        "en": {
          "description": "Use Voiceover & Narration Studio as an AI voice generator, text-to-speech workspace, and AI voiceover generator. Choose from the current voice library, turn scripts into ready-to-edit AI narration and voiceover, or create and reuse a custom brand voice through voice cloning. It supports short-video voiceover, script-to-voiceover, course narration, ordered audiobook narration, supplied multilingual text to speech, Cantonese text to speech, and recurring brand audio, with current price estimates, clear output planning, and delivery organized by chapter, language, and use case.",
          "display_name": "Voiceover & Narration Studio",
          "summary": "Choose or reuse a voice, turn scripts into ready-to-edit narration, plan ordered long-form or multilingual audio, and create a reusable custom voice.",
          "tags": [
            "AI voice studio",
            "AI voice generator",
            "text to speech",
            "AI voiceover generator",
            "AI narration",
            "voice cloning",
            "multilingual text to speech",
            "voice library",
            "script to voiceover",
            "short video voiceover",
            "audiobook narration",
            "course narration",
            "Cantonese text to speech",
            "reusable brand voice"
          ],
          "title": "Voiceover & Narration Studio – Text to Speech and Voice Cloning"
        },
        "ja": {
          "description": "ナレーション制作スタジオは、AI音声生成、テキスト読み上げ、AIナレーション、音声クローンを一つにまとめた制作入口です。現在の音声ライブラリから用途に合う声を選び、原稿から動画ナレーション、教材ナレーション、順番を保ったオーディオブック朗読、外国語ナレーション、多言語音声、広東語の読み上げを制作。カスタム音声やブランド音声も再利用でき、出力計画を確認しながら、用途・言語・章ごとに整理された音声を受け取れます。",
          "display_name": "ナレーション制作スタジオ",
          "summary": "用途に合う声を選び、原稿から編集に使えるナレーションを作成。長編や多言語の読み上げ、カスタム音声の作成と再利用も一つの入口で進めます。",
          "tags": [
            "AI音声スタジオ",
            "AIナレーション",
            "テキスト読み上げ",
            "AI音声合成",
            "ナレーション作成",
            "多言語音声",
            "音声クローン",
            "音声作成",
            "動画ナレーション",
            "オーディオブック朗読",
            "教材ナレーション",
            "読み上げ音声",
            "外国語ナレーション"
          ],
          "title": "ナレーション制作スタジオ:テキスト読み上げ・多言語対応・音声クローン"
        },
        "zh": {
          "description": "用配音与旁白工作室统筹 AI配音、文字转语音和 AI语音生成:从当前声音库选择合适音色,把文稿制作成可直接剪辑的语音合成音频,或通过声音克隆创建并复用品牌专属声音。适合文稿转语音、短视频配音、有声书旁白、课程配音、多语言配音和粤语配音,也可作为 AI语音生成器与品牌声音工作台;长篇内容按章节、多语言内容按语言清晰交付。",
          "display_name": "配音与旁白工作室",
          "summary": "从同一个入口选声音、把文稿做成可剪辑配音、安排长篇或多语言音频,并创建可复用的品牌音色。",
          "tags": [
            "AI语音工作室",
            "AI配音",
            "文字转语音",
            "AI语音生成器",
            "语音合成",
            "声音克隆",
            "多语言配音",
            "声音库",
            "短视频配音",
            "有声书旁白",
            "课程配音",
            "粤语配音",
            "品牌声音",
            "文稿转语音"
          ],
          "title": "配音与旁白工作室:AI配音、文字转语音与声音克隆"
        }
      },
      "mcp": {
        "authentication": "device-bearer",
        "credential_file": "~/.beatra/credentials.json",
        "name": "beatra",
        "transport": "streamable-http",
        "url": "https://mcp.beatra.ai/mcp"
      },
      "name": "voiceover-narration-studio",
      "schema_version": 1,
      "version": "0.2.9"
    }
    
  • README.mdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/beatra-ai-ai-voiceover-generator-skill-skills-voiceover-narration-studio.md

// REPO STATS

7 stars