⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
product-photo-studio
Transform a real product photo into a studio-quality ecommerce image, lifestyle scene, or marketplace-ready hero shot. This AI product photography tool replaces backgrounds, improves lighting, and stages scenes while using the source photo and confirmed product details as the visual anchor. Create clean white-background listings, contextual lifestyle compositions, and premium ad visuals from a single phone snap for Amazon, Taobao, Shopify, and social media. Start from one product photo, combine several references, or refine a selected draft toward a polished listing image.
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 Product Photography Skill
👉 See also Ecommerce Product Images Skill
Turn a real product photo into ecommerce studio photos and lifestyle scenes, 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 | Transform a real product photo into a studio-quality ecommerce image, lifestyle scene, or marketplace-ready hero shot with clean backgrounds, professional lighting, and composition guided by confirmed product details. |
| Models | Qwen Image 2.0 Fast, Qwen Image 3.0, Qwen Image 3.0 Pro, Wan Image 2.7 Fast, Qwen Image 2.0 Pro, Wan Image 2.7 Pro, GPT Image 2.5 Flare, GPT Image 2.5 Sunburst, GPT Image 2, MiniMax Image 01 Live |
| 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 cluttered kitchen-counter phone snapshot of a fictional rosehip face oil, OLUNEA (itself AI-generated as the demo input), turned into a white-background studio listing image with the label text and bottle shape unchanged. AI-generated with Beatra.
| Skill | Entry point | Version |
|---|---|---|
product-photo-studio | SKILL.md | 0.3.1 |
This repository is published automatically from beatra-ai/beatra-skills. Report issues there.
Install
With the skills CLI:
npx skills add beatra-ai/ai-product-photography-skill
As a Claude Code plugin:
claude plugin marketplace add beatra-ai/ai-product-photography-skill
claude plugin install product-photo-studio@ai-product-photography-skill
As a Codex plugin:
codex plugin marketplace add beatra-ai/ai-product-photography-skill
codex plugin add product-photo-studio@ai-product-photography-skill
As a GitHub Copilot CLI plugin:
copilot plugin marketplace add beatra-ai/ai-product-photography-skill
copilot plugin install product-photo-studio@ai-product-photography-skill
As a Gemini CLI extension:
gemini extensions install https://github.com/beatra-ai/ai-product-photography-skill
With the GitHub CLI:
gh skill install beatra-ai/ai-product-photography-skill product-photo-studio
Or clone this repository and copy skills/product-photo-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 product-photo-studio skill from https://github.com/beatra-ai/ai-product-photography-skill (folder skills/product-photo-studio), then follow its SKILL.md to connect my Beatra account.
Examples
The same OLUNEA bottle from the phone snapshot placed in a marble bathroom-vanity lifestyle scene with soft window light, label and shape kept as in the source. AI-generated with Beatra.
Ask your agent:
Using ./olunea-counter.jpg as the product reference, place the same dropper bottle on a marble bathroom vanity with soft window light for a social post. Keep the label text, shape and color exactly as in the photo.
Prompt:
Professional lifestyle product photo for social media. Image 1 is the product reference: the dropper bottle standing on the counter. Match its shape, color, label, label text, and proportions closely, and keep the label text exactly as it appears in Image 1. Leave out the kitchen counter and all other objects from Image 1. Place only the bottle upright on a white marble bathroom vanity, facing the camera, slightly right of center, with a folded linen hand towel and a small sprig of dried rose hips softly out of focus in the background, smaller than the bottle. Soft diffused window light from the left, warm neutral color temperature, gentle highlight on the glass, shallow depth of field with the bottle sharp and the background soft, natural contact shadow falling to the right, clean product edges, consistent lighting direction, no halo or fringe. No added text, logos, or watermarks.
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
- Image to image — 10 models (Qwen Image 2.0 Fast, Qwen Image 3.0, Qwen Image 3.0 Pro, Wan Image 2.7 Fast, Qwen Image 2.0 Pro, Wan Image 2.7 Pro, GPT Image 2.5 Flare, GPT Image 2.5 Sunburst, GPT Image 2, MiniMax Image 01 Live); PNG; up to 4 images per request; canvas up to 16.8 MP
- 16.8 MP (4K tier): GPT Image 2.5 Flare, GPT Image 2.5 Sunburst, GPT Image 2 (1 image per request)
- Input images: up to 4 per request, 10–20 MB each; BMP, GIF, JPEG, PNG, TIFF, WEBP
- Image editing — 6 models (Qwen Image 2.0 Fast, Qwen Image 3.0, Qwen Image 3.0 Pro, Wan Image 2.7 Fast, Qwen Image 2.0 Pro, Wan Image 2.7 Pro); PNG; up to 4 images per request; canvas up to 4.2 MP
- 4.2 MP (2K tier): Qwen Image 2.0 Fast, Qwen Image 3.0, Qwen Image 3.0 Pro, Wan Image 2.7 Fast, Qwen Image 2.0 Pro, Wan Image 2.7 Pro (up to 4 images per request)
- Input images: up to 4 per request, 10–20 MB each; BMP, GIF, JPEG, PNG, TIFF, WEBP
Why use Beatra instead of calling model APIs directly
- One account for every model this skill uses — Image to image 10; Image editing 6 (Qwen Image 2.0 Fast, Qwen Image 3.0, Qwen Image 3.0 Pro, Wan Image 2.7 Fast, Qwen Image 2.0 Pro, Wan Image 2.7 Pro, GPT Image 2.5 Flare, GPT Image 2.5 Sunburst, GPT Image 2, MiniMax Image 01 Live) (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.
What you get
- The source product as the visual anchor — Carry the confirmed shape, color, label, proportions, materials, and included pieces from the source photo into each new background or scene.
- Marketplace-oriented backgrounds — Create pure-white Amazon main images, clean Taobao listings, consistent Shopify catalogs, and lifestyle social posts from the same product photo.
- Scene and lifestyle staging — Place the product in a contextual scene—kitchen counter, wooden table, marble shelf, or seas
// 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/product-photo-studio/SKILL.md8.9 KBbf64bf0c9adeupstream ↗
--- name: "product-photo-studio" description: "Transform a real product photo into a studio-quality ecommerce image, lifestyle scene, or marketplace-ready hero shot. This AI product photography tool replaces backgrounds, improves lighting, and stages scenes while using the source photo and confirmed product details as the visual anchor. Create clean white-background listings, contextual lifestyle compositions, and premium ad visuals from a single phone snap for Amazon, Taobao, Shopify, and social media. Start from one product photo, combine several references, or refine a selected draft toward a polished listing image." --- # AI Product Photo Studio Transform one real product photo into a studio-quality listing image, lifestyle scene, or marketplace-ready hero shot. Reuse decisions already present in the conversation and move by the shortest route that completes the requested image. ## Choose the route - **Clean background:** with one product photo, remove the original background and place the product on a clean white, light-gray, or studio-gradient background using `beatra.images.transform`. This is the default for marketplace main images. - **Scene and lifestyle:** with one product photo and a scene description, place the product in a contextual lifestyle setting—on a kitchen counter, a wooden table, a marble shelf, or a seasonal backdrop—using `beatra.images.transform`. - **Refine an accepted draft:** use `beatra.images.edit` with the accepted image as `images[0]` to fix a shadow, remove a reflection, adjust color temperature, or clean up a small defect without changing the composition. Follow [product routing](references/product-routing.md) for the precise branch and [scene craft](references/scene-craft.md) when turning the request into a visual specification anchored to the source product. ## Shape one product brief The hard input is a real product photo. Scene and lighting must not rewrite a confirmed color, accessory, or quantity. Reuse the user's product type, intended marketplace, background preference, and any style references. Ask only when a missing decision materially changes the result. For a standard marketplace main image, propose a clean white background as the default; for a lifestyle request, propose a scene that matches the product's category. Build the brief around: - the product itself—what it is, its category, and any key visual details (label text, brand logo, shape, color); - one target marketplace or use (Amazon, Taobao, Shopify, social media, ad campaign) when it determines format rules; - one background or scene direction (clean white, studio gradient, lifestyle context, seasonal); - ordered visual references when available (style inspiration, background reference, angle reference). If the user has already stated the target marketplace or background type, reuse it. If that choice is genuinely missing, propose the best default and include it in the single paid-call confirmation. ## Prepare the call Use only this Skill's bundled `scripts/mcp_client.py` for every remote MCP operation. The tool name is a CLI argument and the tool arguments are the JSON sent on stdin. Do not configure or call a host Beatra Connector, and do not use REST/OpenAPI as a fallback. For exact commands and troubleshooting, use [Bundled MCP Client diagnostics](references/mcp-connection.md). - Upload the product photo through the bundled client helpers first, then call `beatra.images.transform` with the uploaded artifact as the first ordered reference. Label the product image's role explicitly in the prompt so the model treats it as the visual anchor. - For a clean background, set an explicit square or marketplace-ratio canvas. - For a lifestyle scene, describe the scene, lighting direction, and surface material in the prompt while identifying the source product details that should carry into the result. - For an accepted draft, call `beatra.images.edit`. Use at most two normalized `edit_regions` on `image_index=0` for localized fixes; omit regions for a whole-image adjustment. Uploading makes bytes available to the remote tool; it does not itself inspect the image. Review only visual facts the host can actually see. Keep `model=auto` and `count=1` unless the user explicitly chooses otherwise. Call `beatra.models.list` only for a real model, availability, compatibility, or price decision. The detailed request shapes and examples are in [workflow](references/workflow.md). ## Confirm and execute once Planning and brief preparation are free. Before the paid image call, show and freeze the final prompt, ordered references, canvas, background or scene direction, model, controls, and output count. Merge any still-material high-impact choice into this one confirmation. After approval, create one stable opaque `client_request_id` for that exact logical request and submit it once. A changed prompt, reference or order, canvas, scene direction, model, count, or control is new paid work and needs a new confirmation and a new ID. ## Track, review, and deliver After receiving a `task_id`, poll only that task with `beatra.tasks.get`. If the ID is lost, use `beatra.tasks.list` to find candidates and verify the selected one with `tasks.get`. Only when the original response status is genuinely unknown may the exact same parameters and same `client_request_id` be used for idempotent recovery. Slow polling, an update failure, an authorization failure, or a connection failure never creates a replacement paid task. Use `beatra.tasks.cancel` only when the user asks. If cancellation returns `409`, continue tracking the original task. See [review and recovery](references/review-and-recovery.md) for the full recovery contract. When the result is visible, review product fidelity against the source photo, background quality (clean edges, consistent lighting, natural shadow), color accuracy (do product colors match the original?), canvas fit, and the marketplace's current image guidance if applicable. Deliver the artifact links, observed dimensions, task ID, and `billing.net_charged_credits`. Offer at most one focused, unexecuted revision. Generated assets can also be viewed and managed at [beatra.ai](https://beatra.ai). If the tool returns insufficient credits: give the user the tool message in full. Do not change the URL. You may translate the other words. Do not replace it with your own top-up copy. Point the user to pay using that message. When the user asks how many credits remain, 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. ## References by task - Choosing among clean background, lifestyle scene, and detail edit, or planning for a specific marketplace: [product routing](references/product-routing.md) - Turning a request into a scene specification anchored to the source product: [scene craft](references/scene-craft.md) - Exact request shapes, ordered-reference labeling, and JSON examples for each route: [workflow](references/workflow.md) - Lost task, slow task, cancellation, result review, or planning a revision: [review and recovery](references/review-and-recovery.md) - First install or expired authorization: [installation and authentication](references/installation-and-auth.md) - Bundled MCP Client commands and diagnostics: [Bundled MCP Client diagnostics](references/mcp-connection.md) - Installation registration: [installation registration](references/installation-registration.md) - Task lookup, polling, and result fields: [tasks and results](references/tasks-and-results.md) - Balance, validation, and structured errors: [billing, errors, and recovery](references/billing-errors-and-recovery.md) - Disconnecting the installation: [uninstall and disconnect](references/uninstall-and-disconnect.md) - Updates and security: [updates and security](references/updates-and-security.md) ## Installation and account operations For first use and shared operations, follow [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). ## 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/product-photo-studio/scripts/authorize.py61 KB72b2c4ca652dupstream ↗
Contents are not inlined in the catalog. Open the upstream file.
skills/product-photo-studio/scripts/mcp_client.py86 KBce9e04eb42b3upstream ↗
Contents are not inlined in the catalog. Open the upstream file.
skills/product-photo-studio/scripts/uninstall.py13 KB97f477fade16upstream ↗
#!/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 = "product-photo-studio" PACKAGE_DISPLAY_NAME = "AI Product Photo Studio" PACKAGE_VERSION = "0.3.1" 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/product-photo-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/product-photo-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/product-photo-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/product-photo-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/product-photo-studio/references/product-routing.md3.9 KB9fd3e689139eupstream ↗
# Product routing ## Start from the seller's actual starting point A product photo request arrives in one of three shapes. Identify which one before choosing a tool. | Starting point | What is enough to start | Route | | --- | --- | --- | | One product photo + background or scene goal | The product image plus one target look | `beatra.images.transform` | | One product photo + multiple style references | Ordered references (product first, style/background after) | `beatra.images.transform` | | Accepted draft + specific fix | The draft plus the requested change | `beatra.images.edit` | A single product photo with no explicit background preference is enough to propose a clean white background and include it in the single paid-call confirmation. ## Extract the product brief Collect what is already known and fill gaps only when they materially change the result. - **Product identity.** What the product is, its category, and any key visual details (brand label, shape, texture, color, packaging). Treat the source photo and confirmed details as the visual reference, then review the result for fidelity. - **Target marketplace.** Amazon, Taobao, Shopify, Rakuten, social media, or ad campaign—when it determines format or background rules. - **Background or scene direction.** Clean white, light-gray gradient, studio sweep, lifestyle context (kitchen, bathroom, office, outdoor), seasonal (holiday, summer). - **Visual references.** Style inspiration, desired background, angle reference, or a competitor listing to match—ordered with the product photo first. ## Marketplace image guidance Marketplace requirements change. When a named platform determines the output, ask for or consult its current image guidance before the paid call. Use these common patterns only as starting points: - **Amazon main image** — typically a pure-white background with the product prominent and without added text or watermarks. - **Taobao main image** — often a clean light background and square composition. - **Shopify product image** — consistent background across the catalog, square or 4:3 ratio preferred. - **Social media** — lifestyle or contextual scene with the product as hero. - **Ad campaign** — dramatic lighting, premium composition, may include text overlay (added downstream, not in the generated image). When the user names a marketplace, apply the current supplied guidance to the prompt. When absent, propose clean white as the default. ## Product category scene suggestions When the user requests a lifestyle scene but does not specify the setting, infer from the product category: - **Food and beverage** — wooden table, marble countertop, restaurant setting, natural light. - **Cosmetics and skincare** — bathroom shelf, vanity table, silk backdrop, soft diffused light. - **Electronics and tech** — desk surface, modern interior, dark dramatic background, cool blue accent light. - **Fashion and accessories** — model or mannequin context, fabric backdrop, neutral studio. - **Home and living** — styled room interior, bookshelf, windowsill, cozy warm light. - **Baby and kids** — nursery, soft pastel background, playful props. These are starting points, not constraints. Override with the user's stated preference whenever available. ## Defaults that avoid unnecessary questions - Canvas: square `1:1` for marketplace main images; `4:3` or `16:9` when the user specifies a banner or hero format. - Count: 1 (product photography requires precision, not exploration). - Model: `auto`. - Background: clean white when no preference is stated. ## Visual access Local files enter the workflow through `beatra.assets.upload`. Upload makes the bytes available to the remote tool; it does not itself inspect the image. Review only visual facts the host can actually see. When the host cannot view an image, state that visual verification was not possible and proceed on the user's declared intent.skills/product-photo-studio/references/review-and-recovery.md3.7 KB41559738962cupstream ↗
# Review and recovery ## Before the paid boundary Present one final direction. Freeze the prompt, ordered references, canvas, background or scene direction, count, model, and controls in a single confirmation. After approval, create one stable opaque `client_request_id` for that exact logical request and submit once. Any change—prompt, reference or order, canvas, scene direction, model, count, or control—is new paid work with a new confirmation and a new ID. ## Monitor and recover After receiving a `task_id`, poll only that task with `beatra.tasks.get`. Use bounded backoff: first poll around 2 seconds, increasing toward 5 and 10 seconds, maximum 15 seconds. Honor `deadline_at`. Stop active polling after 30 minutes and report the task ID and status. - **Lost create response:** if the response to the original submission is genuinely unknown, retry the exact same parameters with the same `client_request_id`. - **Lost task ID:** call `beatra.tasks.list` to find candidates, then verify the selected one with `beatra.tasks.get`. Match by capability, input, and timing. If the match is ambiguous, do not submit a replacement. - **Slow task:** a queued or running task never authorizes a replacement. - **Authorization failure:** run `python3 scripts/authorize.py` and follow its output until it prints `Beatra is ready`. The original `client_request_id` remains valid for idempotent recovery. - **Connection failure:** preserve the credential and the original `client_request_id`. Retry only the exact same parameters. Use `beatra.tasks.cancel` only when the user asks. If cancellation returns `409`, the task continues. See the shared [billing, errors, and recovery](billing-errors-and-recovery.md) for the full structured-failure reference. ## Review only what is visible When the result is visible, review in this order: 1. **Product fidelity** — is the product itself recognizable, correctly shaped, and undistorted? Compare against the original product photo. 2. **Color accuracy** — do the product's colors match the original? Look for color casts introduced by the scene lighting. 3. **Edge quality** — are the product's edges clean where they meet the background? No halo, fringe, or ghosting. 4. **Shadow and grounding** — does the product cast a natural contact shadow? A floating product looks artificial. 5. **Lighting consistency** — does the light direction on the product match the scene's implied light source? 6. **Marketplace guidance** — if targeting a specific marketplace, compare the image with the platform's current background, size, and framing guidance. If the host cannot view the artifact, state that visual verification was not possible and deliver the result links for the user to review. ## Deliver Present the returned image. Include the artifact links, observed dimensions, task ID, and `billing.net_charged_credits`. Do not infer credits from elapsed time. Offer at most one focused, unexecuted revision. When the user accepts a result but wants a specific fix, describe the revision as a new `beatra.images.edit` request with the accepted image as `images[0]`. ## Product image variant set A complete ecommerce listing typically uses several image variants: - **main image** — clean white background, product centered (marketplace requirement); - **lifestyle scene** — product in a real-world context; - **detail or infographic** — close-up of a feature or annotated callouts (text added downstream); - **scale or dimension** — product next to a common object for size reference; - **seasonal or campaign** — holiday or promotion-themed background. Each variant is a distinct paid request with its own confirmation and `client_request_id`. Review each result independently for product fidelity.skills/product-photo-studio/references/scene-craft.md3.2 KB876e4f928e98upstream ↗
# Scene craft ## Anchor the result to the product The product is the subject. Build the prompt around the source photo and confirmed product details, then verify fidelity in the result: - state explicitly that Image 1 is the product reference and ask the model to match its shape, color, label, and proportions closely; - describe only the background, lighting, and scene around it; - do not request changes to the product itself in a transform call—use `beatra.images.edit` for localized product fixes. ## Background quality A professional product background has three properties: - **clean edges** — the product's outline must have no halo, fringe, or ghosting where it meets the background; - **consistent lighting** — the light direction on the product must match the light direction implied by the background (shadows fall the same way); - **natural shadow** — the product should cast a subtle contact shadow on the surface beneath it; a floating product with no shadow looks artificial. Include these constraints in the prompt: "clean product edges, consistent lighting direction, natural contact shadow, no halo or fringe." ## Clean white background For marketplace main images requiring pure white: - state "pure white background, RGB 255 255 255" explicitly; - request even, shadowless lighting for the background while keeping a subtle contact shadow under the product for grounding; - avoid gradients, textures, or patterns unless the user explicitly requests them. ## Lifestyle scene composition For lifestyle and contextual scenes: - match the surface material to the product category (wood for food, marble for cosmetics, metal for tech); - keep the product as the focal point—larger and more prominent than any prop or background element; - use depth of field to separate the product from the background (sharp product, slightly soft background); - ensure color temperature consistency (warm scene = warm product lighting, cool scene = cool product lighting). ## Lighting direction State the light direction in the prompt to avoid conflicting shadows: - **front light** — even, flat illumination; good for catalog accuracy; - **side light** — dimensional, shows texture; good for food and crafts; - **backlight** — dramatic silhouette rim; good for glass and transparent products; - **top light** — natural overhead; good for flat-lay and top-down shots. The chosen direction must be consistent between the product lighting and the scene's implied light source. ## Color accuracy Use the original photo as the color reference, then review the result for color fidelity: - do not describe the product's color in the prompt (let the reference image determine it); - if the original photo has a color cast, note it and advise the user that color correction may be needed; - structured `palette` is optional—use it only when the user provides exact brand or background color values. ## Canvas and output - Default canvas for marketplace main images: square `1:1` at `2K` tier. - For banner or hero images: `16:9` or `4:3` at `2K`. - For edit: the default is `source`, which inherits the base image's ratio. - A different ratio is a separate paid request with its own confirmation and `client_request_id`.skills/product-photo-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/product-photo-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/product-photo-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/product-photo-studio/references/workflow.md3.5 KB025f662a60b9upstream ↗
# Workflow ## Build one product image brief Translate the product type, target marketplace, background or scene direction, and any style references into a single coherent prompt. Keep the prompt focused on the background and scene, not on changing the product. ## Prepare the selected route ### Transform to clean background Use `beatra.images.transform` with the product photo as the first ordered reference. Label the product's role explicitly. ```json { "prompt": "Professional ecommerce product photo. Image 1 is the product reference; match its shape, color, label, and proportions closely. Place it on a pure white background, with even studio lighting, a subtle contact shadow, and clean edges with no halo.", "model": "auto", "count": 1, "canvas": { "type": "preset", "tier": "2K", "aspect": "1:1" }, "images": [ {"type": "artifact", "artifact_id": "<product-photo-artifact-id>"} ], "client_request_id": "product-clean-bg-001" } ``` ### Transform to lifestyle scene Use `beatra.images.transform` with the product photo first and optional style or background references after. Label each image's role. ```json { "prompt": "Professional lifestyle product photo. Image 1 is the product reference; match its shape, color, label, and proportions closely. Image 2 guides only the desired scene style and color palette. Place the product on a warm wooden kitchen counter, soft morning window light from the left, shallow depth of field, natural contact shadow.", "model": "auto", "count": 1, "canvas": { "type": "preset", "tier": "2K", "aspect": "1:1" }, "images": [ {"type": "artifact", "artifact_id": "<product-photo-artifact-id>"}, {"type": "artifact", "artifact_id": "<scene-style-artifact-id>"} ], "client_request_id": "product-lifestyle-001" } ``` When more than one reference is supplied, their order matters. The product photo should always be `images[0]`. Later images guide style, background, or angle only. The last image anchors the output ratio when canvas aspect is `source`. ### Edit an accepted draft Use `beatra.images.edit` with the accepted image as `images[0]`. Use at most two normalized `edit_regions` for targeted fixes; omit regions for a whole-image adjustment. ```json { "prompt": "Remove the small reflection on the lower left of the product bottle. Keep everything else unchanged.", "model": "auto", "count": 1, "images": [ {"type": "artifact", "artifact_id": "<accepted-draft-artifact-id>"} ], "edit_regions": [ { "image_index": 0, "x": 0.1, "y": 0.6, "width": 0.25, "height": 0.2 } ], "client_request_id": "product-fix-reflection-001" } ``` ## Apply model controls Keep `model=auto` unless the user explicitly requests a concrete model. Keep `count=1`—product photography requires precision, not variation. Call `beatra.models.list` with the relevant capability (`image_to_image` or `image_edit`) only when the user asks about model availability, compatibility, or price. ## Confirm, submit once, and monitor Present one final confirmation card containing the complete prompt, ordered references, canvas, background or scene direction, count, and model. After approval, create one stable opaque `client_request_id` and submit once. Record the returned `task_id` and poll with `beatra.tasks.get`. A changed prompt, reference set or order, canvas, scene direction, model, count, or control value is new paid work requiring a new confirmation and a new `client_request_id`.skills/product-photo-studio/install-source.json39 B71bdc5669a3bupstream ↗
{"schema_version":1,"source":"github"}skills/product-photo-studio/manifest.json5.1 KB5d8fa80676b6upstream ↗
{ "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": "AI Product Photo Studio", "entry": "SKILL.md", "homepage": "https://beatra.ai/skills/product-photo-studio", "icon": "https://beatra.ai/textures/icons/product-photo-studio-0.2.9.png", "localized": { "en": { "description": "Transform a real product photo into a studio-quality ecommerce image, lifestyle scene, or marketplace-ready hero shot. This AI product photography tool replaces backgrounds, improves lighting, and stages scenes while using the source photo and confirmed product details as the visual anchor. Create clean white-background listings, contextual lifestyle compositions, and premium ad visuals from a single phone snap for Amazon, Taobao, Shopify, and social media. Start from one product photo, combine several references, or refine a selected draft toward a polished listing image.", "display_name": "AI Product Photo Studio", "summary": "Transform a real product photo into a studio-quality ecommerce image, lifestyle scene, or marketplace-ready hero shot with clean backgrounds, professional lighting, and composition guided by confirmed product details.", "tags": [ "AI product photography", "product photo editor", "product image background", "ecommerce product photo", "product photo background remover", "AI product shoot", "white background product photo", "lifestyle product photography", "product hero image", "amazon product image", "product photo studio", "product listing photo" ], "title": "AI Product Photo Studio: Ecommerce Product Photography & Background Remover" }, "ja": { "description": "実際の製品写真をスタジオ品質のEC画像、ライフスタイルシーン、または販売プラットフォーム向けのヒーローショットに変換します。このAI商品写真ツールは、元写真と確認済みの商品外観を基準に、背景の置換、照明の改善、シーンの演出を行います。スマホで撮った1枚から、白背景のメイン画像、ライフスタイルシーン、上質な広告ビジュアルを作成し、Amazon、楽天、Shopify、SNS向けの商品画像に活用できます。1枚の製品写真、複数の参考画像、または選んだドラフトから、目的に合う商品画像へ仕上げます。", "display_name": "AI 商品写真スタジオ", "summary": "実際の製品写真をスタジオ品質のEC画像、ライフスタイルシーン、または販売プラットフォーム向けのヒーローショットに変換。背景置換、照明改善、シーン演出に対応し、確認済みの商品外観を基準に構成します。", "tags": [ "AI商品写真", "商品画像作成", "背景切り抜き", "製品画像編集", "商品写真 白背景", "楽天 商品画像", "EC用画像作成", "商品写真 レタッチ", "メルカリ 写真", "商品ライフスタイル写真", "製品ヒーロー画像", "商品画像 背景" ], "title": "AI 商品写真スタジオ:EC製品画像作成と背景切り抜き" }, "zh": { "description": "将真实产品照片制作成影棚级电商主图、场景图或面向市场平台的高清商品图。这款 AI 商品图工作室用于商品图片处理和电商图片编辑,可替换背景、改善光效、搭建场景。它以原图中的商品外观特征为依据,从一张手机随拍就能做出白底主图、生活场景图和高级广告图。适用于亚马逊白底产品图、淘宝主图、Shopify 产品大图和社交媒体商品图。从一张产品照片出发,组合多张参考图,或在选定草稿上做定向精修,完成适合上架使用的成品;生活场景图和广告图也能直接用作商品详情页图片。", "display_name": "商品图", "summary": "将真实产品照片转化为影棚级电商主图、场景图或面向市场平台的高清商品图,支持换背景、改善光效和搭建场景,并延续已确认的商品外观特征。", "tags": [ "AI商品图", "产品图制作", "商品图片处理", "电商产品图", "商品白底图", "产品主图", "淘宝主图制作", "商品场景图", "产品精修图", "商品详情页图片", "电商图片编辑", "产品拍照", "产品图" ], "title": "产品图工作室:AI 商品图、电商主图与背景替换" } }, "mcp": { "authentication": "device-bearer", "credential_file": "~/.beatra/credentials.json", "name": "beatra", "transport": "streamable-http", "url": "https://mcp.beatra.ai/mcp" }, "name": "product-photo-studio", "schema_version": 1, "version": "0.3.1" }README.mdupstream ↗
Contents are not inlined in the catalog. Open the upstream file.
Plain-text copy, no login: /skill-bundles/beatra-ai-ai-product-photography-skill-skills-product-photo-studio.md