
Image Tryon
Run virtual image try-on through Starchild’s sc-proxy with per-turn cost attribution and ledger rows for agent billing summaries.
Overview
Image Tryon is an agent skill for the Build phase that runs metered virtual try-on calls through sc-proxy while attributing API credits to the correct user turn via a stdlib cost ledger helper.
Install
npx skills add https://github.com/starchild-ai-agent/official-skills --skill image-tryonWhat is this skill?
- Tags sc-proxy calls with SC-CALLER-ID tied to the user turn for correct cost-card attribution
- Parses X-Credits-Used and X-Credits-Api-Type from sc-proxy responses into a cost ledger
- Stdlib-only cost helper drops into any skill folder without starchild-clawd coupling
- Degrades gracefully when STARCHILD_* env vars are missing (synthetic caller-id, audit-only ledger)
- Optional STARCHILD_COST_LEDGER_DIR override for ledger directory placement
- Stdlib-only cost helper with fcntl-backed ledger writes under default /data/.starchild/cost_ledger
Adoption & trust: 1 installs on skills.sh; 13 GitHub stars; trending (+100% hot-view momentum).
What problem does it solve?
Your agent can call a try-on API, but paid sc-proxy usage disappears from per-turn cost cards because subprocess calls lack caller-id tagging and ledger writes.
Who is it for?
Solo builders on Starchild who need ecommerce or creative try-on inside agent workflows with auditable credit usage.
Skip if: Teams not using sc-proxy or Starchild env conventions who only need a one-off local image script without billing attribution.
When should I use this skill?
Dispatching a bash skill subprocess that calls sc-proxy for paid image try-on and you need per-turn cost_summary alignment.
What do I get? / Deliverables
After dispatch, each try-on call is tagged, credited in the ledger from response headers, and surfaced in the agent’s cost_summary for that turn.
- Try-on API result
- Cost ledger row per paid sc-proxy response
Recommended Skills
Journey fit
Try-on is implemented as an agent-callable integration during product build, not a cross-journey methodology. Hooks paid generative-media APIs into the agent stack with caller-id tagging and credit headers—classic third-party integration work.
How it compares
Integration skill with explicit cost ledger hooks—not a generic image MCP or ad-hoc requests snippet without turn-level billing.
Common Questions / FAQ
Who is image-tryon for?
Indie agent builders shipping try-on or visual commerce flows on Starchild who must reconcile sc-proxy credits with each user turn.
When should I use image-tryon?
Use it in Build → integrations when wiring virtual try-on into an agent subprocess; pair cost helpers whenever STARCHILD_TOOL_CALLER_ID is set for a paid media call.
Is image-tryon safe to install?
Review the Security Audits panel on this Prism page and confirm sc-proxy endpoints, ledger paths, and secrets handling in your deployment before trusting it with production API keys.
SKILL.md
READMESKILL.md - Image Tryon
"""Cost tracking helper for skill subprocesses. Skills that call sc-proxy via plain `requests` need to: 1. Tag every paid call with a SC-CALLER-ID that ties it back to the user turn that triggered the skill (so the agent's per-turn cost summary shows the cost in the right cost card). 2. After each call, parse the sc-proxy response headers (`X-Credits-Used`, `X-Credits-Api-Type`) and write a row to the cost ledger that the agent reads back when it builds the SSE `cost_summary` event. This file is intentionally zero-dependency (stdlib only) so it can be dropped into any skill folder without coupling to starchild-clawd internals. Env vars consumed (set by the agent before dispatching the bash subprocess): - STARCHILD_TOOL_CALLER_ID — opaque tag for the current tool call - STARCHILD_USER_TURN_ID — uuid of the current user turn - STARCHILD_COST_LEDGER_DIR — optional override for ledger directory When env vars are absent (e.g. running the script outside an agent), the helpers degrade gracefully: caller-id falls back to a synthetic string so the call still goes through, and ledger writes still happen for audit but the user-turn reader will skip them. """ from __future__ import annotations import fcntl import json import os import time from datetime import datetime, timezone from pathlib import Path from typing import Any, Dict, Optional from urllib.parse import urlparse _DEFAULT_LEDGER_DIR = "/data/.starchild/cost_ledger" # Allowlisted request payload keys we forward into the ledger row's # `details` field. MUST stay in sync with starchild-clawd's # core/http_client._record_cost_to_ledger allowlist — anything not in # that allowlist won't be picked up by the agent and won't render in # the frontend cost card. _PAYLOAD_ALLOWLIST = ( # Identity "model", "provider", # Image geometry "aspect_ratio", "quality", "resolution", "image_size", "size", # Video / motion "duration", "duration_s", "fps", "motion_strength", # Quantity "n", "count", # Generation knobs "seed", "steps", "guidance_scale", "cfg_scale", "strength", "scheduler", "sampler", # Reference / mode hints "image_to_image", "image_to_video", "use_reference", "reference_count", ) def caller_headers(extra: Optional[Dict[str, str]] = None, tool_default: str = "skill") -> Dict[str, str]: """Return an HTTP-headers dict with SC-CALLER-ID filled in. Resolution order: 1. `extra["SC-CALLER-ID"]` (case-insensitive) — caller wins. 2. STARCHILD_TOOL_CALLER_ID env (set by the agent) 3. Synthetic `f"{tool_default}:{int(time.time())}"` — tags the call so charges are attributable to *some* identifier even when the agent didn't inject one (standalone CLI runs, tests, cron). """ merged: Dict[str, str] = dict(extra or {}) has_caller = any(k.lower() == "sc-caller-id" for k in merged) if not has_caller: cid = os.environ.get("STARCHILD_TOOL_CALLER_ID") \ or f"{tool_default}:{int(time.time())}" merged["SC-CALLER-ID"] = cid return merged def record_response(response, request_url: str, request_payload: Optional[Dict[str, Any]] = None, api_type_hint: Optional[str] = None) -> None: """Inspect a sc-proxy response and append a ledger row when paid. Best-effort. Silently no-ops when: - response carries no X-Credits-Used / X-Credits-Api-Type - cost is 0 or unparseable - file write fails Never raises — must not break a real request flow. """ try: headers = getattr(response, "headers", None) or {} used = headers.get("X-Credits-Used") or headers.get("x-credits-used") api_type = (headers.get("X-Credits-Api-Type") or headers.get("x-credits-api-type") or api_type_hint) if not used or not api_type: return try: