⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
voiceover-narration-studio
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.
// RATINGS
Not yet listed on ClawHub or SkillsMP
// README
AI Voiceover Generator Skill
👉 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.
| Question | Answer |
|---|---|
| What it does | 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. |
| Models | MiniMax Voice Clone, MiniMax Speech 2.8 HD, MiniMax Speech 2.8 Turbo |
| Requirements | Python 3.10+ and an agent that loads SKILL.md |
| Cost | Free 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 with | Claude 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.
| Skill | Entry point | Version |
|---|---|---|
voiceover-narration-studio | SKILL.md | 0.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
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/skills | vargHQ/skills | |
|---|---|---|---|
| Account or key needed | A Beatra account; no model provider keys | A Scenario account (OAuth) or a Scenario API key (source) | A VARG_API_KEY, required for all skills (source) |
| Models | Voice 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 spending | Paid renders run only when you ask for that exact render or approve its card, which shows the live credit estimate | Dry-run price estimates in its quality-gate and model-comparison skills (source) | A free --preview render before the full render (source) |
| Agents | Claude Code, Cursor, Codex, Windsurf, Cline, OpenClaw | Claude 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