{"id":"0319fdf1-5a18-4c7f-8fed-c246bd5d2312","entityType":"agent","slug":"clawhub-boxjod-box2robot-skills","name":"box2robot-skills","canonicalUrl":"https://www.xpersona.co/agent/clawhub-boxjod-box2robot-skills","canonicalPath":"/agent/clawhub-boxjod-box2robot-skills","generatedAt":"2026-10-10T14:45:02.607Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":null},"description":"Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, download datasets, generate videos, and orchestrate AI training/...","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.4K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s171nn27976h1jed4s95as3n01852kk1:box2robot-skills","sourceUrl":"https://clawhub.ai/boxjod/box2robot-skills","homepage":"https://clawhub.ai/boxjod/skills/box2robot-skills","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/boxjod/box2robot-skills","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/boxjod/skills/box2robot-skills","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"box2robot-skills technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":null},"stars":null,"forks":null,"downloads":1448,"packageName":null,"latestVersion":"1.0.9","tractionLabel":"1.4K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T11:58:28.239Z","lastCrawledAt":"2026-10-10T11:58:28.239Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T11:58:28.239Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.9","createdAt":"2026-05-05T04:01:48.832Z","changelog":"- Removed the ACT Skill Store commands and related documentation from the skill description. - Dropped the 'stream' camera command details from the documentation. - Removed reference to the `CHANGELOG.md` file from the project. - No changes to code functionality; only documentation clarified and simplified.","fileCount":7,"zipByteSize":22354},{"version":"1.0.8","createdAt":"2026-05-05T03:43:05.477Z","changelog":"**box2robot-skills 1.0.8** - Updated documentation to reflect expanded capabilities, including camera-integrated trajectory recording, the ACT skill store, live video streaming, and enhanced dataset/video download features. - SKILL.md version increased from 0.3.0 to 0.7.0 with significantly more detailed usage instructions. - Clarified environment variable behavior and reduced required dependencies. - Added new documentation files: `.clawhubignore` and `CHANGELOG.md`.","fileCount":7,"zipByteSize":23082},{"version":"1.0.7","createdAt":"2026-04-18T08:13:18.942Z","changelog":"Box2Robot skill v1.0.7 — changelog - Updated SKILL.md with new formatting and metadata, including YAML front matter and OpenCLAW requirements. - Expanded and reorganized documentation for improved clarity on safety, credential management, and privacy-sensitive operations. - Streamlined action descriptions and grouped commands more clearly by functionality. - Added warnings for destructive and privacy-sensitive commands to highlight the need for explicit user confirmation and consent. - Enhanced setup and usage instructions for easier configuration and onboarding.","fileCount":6,"zipByteSize":18828},{"version":"1.0.6","createdAt":"2026-04-18T05:12:17.230Z","changelog":"Version 1.0.6 - No file or documentation changes detected. - No feature updates, fixes, or content modifications in this release.","fileCount":6,"zipByteSize":14729},{"version":"1.0.5","createdAt":"2026-04-18T05:01:45.312Z","changelog":"Version 1.0.5 - No code or documentation changes detected in this release. - The SKILL.md reference manual remains unchanged from the previous version.","fileCount":6,"zipByteSize":13894},{"version":"1.0.4","createdAt":"2026-04-18T03:08:41.006Z","changelog":"**Changelog for box2robot-skills 1.0.4** - Added .clawhubignore for repo/file exclusion. - Replaced previous skill manual with new SKILL.md, adopting OpenClaw/AI agent metadata format. - SKILL.md is now more structured: includes YAML metadata, install instructions, and clear API/CLI/natural language mapping. - Manual content is streamlined, emphasizing setup, environment variables, common actions, and orchestration rationale. - No core application/API changes—documentation and repo structure update only.","fileCount":6,"zipByteSize":13276},{"version":"1.0.3","createdAt":"2026-04-18T03:03:28.422Z","changelog":"No file or documentation changes detected in this release. - No updates, bugfixes, or content edits in version 1.0.3. - Documentation and files remain identical to previous version.","fileCount":5,"zipByteSize":10322},{"version":"1.0.2","createdAt":"2026-04-18T02:51:42.411Z","changelog":"Version 1.0.2 - Added pyproject.toml to the project. - No changes to core skill content or logic.","fileCount":5,"zipByteSize":11208}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s171nn27976h1jed4s95as3n01852kk1:box2robot-skills","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s171nn27976h1jed4s95as3n01852kk1:box2robot-skills` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/boxjod/box2robot-skills before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T14:45:02.603Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-boxjod-box2robot-skills/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":null},"readme":"Skill: box2robot-skills\n\nOwner: boxjod\n\nSummary: Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, download datasets, generate videos, and orchestrate AI training/...\n\nTags: latest:1.0.9\n\nVersion history:\n\nv1.0.9 | 2026-05-05T04:01:48.832Z | user\n\n- Removed the ACT Skill Store commands and related documentation from the skill description.\n- Dropped the 'stream' camera command details from the documentation.\n- Removed reference to the `CHANGELOG.md` file from the project.\n- No changes to code functionality; only documentation clarified and simplified.\n\nv1.0.8 | 2026-05-05T03:43:05.477Z | user\n\n**box2robot-skills 1.0.8**\n\n- Updated documentation to reflect expanded capabilities, including camera-integrated trajectory recording, the ACT skill store, live video streaming, and enhanced dataset/video download features.\n- SKILL.md version increased from 0.3.0 to 0.7.0 with significantly more detailed usage instructions.\n- Clarified environment variable behavior and reduced required dependencies.\n- Added new documentation files: `.clawhubignore` and `CHANGELOG.md`.\n\nv1.0.7 | 2026-04-18T08:13:18.942Z | user\n\nBox2Robot skill v1.0.7 — changelog\n\n- Updated SKILL.md with new formatting and metadata, including YAML front matter and OpenCLAW requirements.\n- Expanded and reorganized documentation for improved clarity on safety, credential management, and privacy-sensitive operations.\n- Streamlined action descriptions and grouped commands more clearly by functionality.\n- Added warnings for destructive and privacy-sensitive commands to highlight the need for explicit user confirmation and consent.\n- Enhanced setup and usage instructions for easier configuration and onboarding.\n\nv1.0.6 | 2026-04-18T05:12:17.230Z | user\n\nVersion 1.0.6\n\n- No file or documentation changes detected.\n- No feature updates, fixes, or content modifications in this release.\n\nv1.0.5 | 2026-04-18T05:01:45.312Z | user\n\nVersion 1.0.5\n\n- No code or documentation changes detected in this release.\n- The SKILL.md reference manual remains unchanged from the previous version.\n\nv1.0.4 | 2026-04-18T03:08:41.006Z | user\n\n**Changelog for box2robot-skills 1.0.4**\n\n- Added .clawhubignore for repo/file exclusion.\n- Replaced previous skill manual with new SKILL.md, adopting OpenClaw/AI agent metadata format.\n- SKILL.md is now more structured: includes YAML metadata, install instructions, and clear API/CLI/natural language mapping.\n- Manual content is streamlined, emphasizing setup, environment variables, common actions, and orchestration rationale.\n- No core application/API changes—documentation and repo structure update only.\n\nv1.0.3 | 2026-04-18T03:03:28.422Z | user\n\nNo file or documentation changes detected in this release.\n\n- No updates, bugfixes, or content edits in version 1.0.3.\n- Documentation and files remain identical to previous version.\n\nv1.0.2 | 2026-04-18T02:51:42.411Z | user\n\nVersion 1.0.2\n\n- Added pyproject.toml to the project.\n- No changes to core skill content or logic.\n\nv1.0.1 | 2026-04-18T02:44:46.184Z | user\n\nDeclare environment variables and credentials for ~/.b2r_token,and change language to english\n\nv1.0.0 | 2026-04-18T02:32:25.641Z | auto\n\nBox2Robot Skills 1.0.0\n\n- Initial release providing a complete reference for Box2Robot control and orchestration via CLI and HTTP API.\n- Covers device authentication, command line usage, action definitions (79 actions), and typical scene workflows.\n- Maps natural language instructions to underlying commands for LLM agents.\n- Includes comprehensive pre-check rules for action execution and error handling.\n- Outlines available LLM/API tools for chat-based robotic control.\n- Provides HTTP API example usage and endpoints for programmatic access.\n\nArchive index:\n\nArchive v1.0.9: 7 files, 22354 bytes\n\nFiles: b2r.py (43330b), pyproject.toml (853b), README.md (3067b), skill-card.md (2778b), SKILL.md (7946b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.9:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, download datasets, generate videos, and orchestrate AI training/inference.\nversion: 0.7.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      anyBins: [python3, python]\n      config: [~/.b2r_token]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories with camera, download datasets, generate replay videos, and orchestrate AI training/inference — all from a single CLI (`b2r.py`).\n\n> **Official skill** published by the Box2Robot team (https://robot.box2ai.com).\n\n## Safety & Supervision\n\n> **This skill controls physical robotic hardware and camera/microphone peripherals.**\n>\n> - **Human supervision required**: Do NOT run autonomously without operator oversight. Servo torque and motion commands cause physical movement that could injure people or damage objects.\n> - **Destructive operations** (`calibrate`) modify hardware state and require explicit user confirmation.\n> - **Privacy-sensitive operations** (`snapshot`, `frame`, `record start --cam`) access camera hardware — only invoke with user consent.\n> - **No OS shell access**: All operations are HTTP requests to `B2R_SERVER`. No arbitrary OS commands are executed. The only local subprocess is `ffmpeg` (optional, for video generation from downloaded JPEG frames).\n> - **Token sensitivity**: `~/.b2r_token` stores a JWT that grants device control. Created with mode 0600 (owner-only). Treat like an SSH key. Delete when no longer needed.\n\n## Credential Flow\n\n```\nlogin → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke\n```\n\nAll network calls go exclusively to `B2R_SERVER` (default: `https://robot.box2ai.com`). No other endpoints are contacted.\n\n## Environment Variables\n\n| Variable | Required | Description | Default |\n|----------|----------|-------------|---------|\n| `B2R_SERVER` | No | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | No | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | No | Default device ID (overrides auto-select) | — |\n\nNone are strictly required at install time. The `login` command handles authentication interactively and persists the token to `~/.b2r_token`. `B2R_TOKEN` is the primary credential variable and can be set to skip interactive login.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n```\n\n## Commands\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (home varies per joint), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording (servo data only)\nb2r.py record start --cam CAM-xxx  # Record with camera (servo + images)\nb2r.py record stop [name]          # Stop and save\nb2r.py record status               # Current recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\nWhen starting a recording, if online cameras are detected, the CLI offers an interactive prompt to select one. Camera recording captures synchronized JPEG frames alongside servo position data.\n\n### Camera\n```bash\nb2r.py snapshot                    # Request camera snapshot\nb2r.py frame [cam_id] [out.jpg]   # Download latest JPEG frame to local file\n```\n\n> **Privacy note**: These commands access camera hardware. Only invoke with user consent.\n\n### Data Download\n```bash\nb2r.py download <traj_id> [dir]    # Download trajectory images only\nb2r.py dataset <traj_id> [dir]     # Download full dataset (JSON + images)\nb2r.py video <traj_id> [out.mp4]   # Generate MP4 video from trajectory images\nb2r.py video <traj_id> out.mp4 --fps 5  # Custom frame rate\n```\n\n`dataset` downloads the trajectory JSON (all frames with positions, timestamps, calibration snapshots) plus all camera images into a local directory.\n\n`video` downloads images to a temp directory and encodes them using `ffmpeg` (preferred) or `opencv-python` (fallback). Neither is required at install time — the command reports a clear error if both are missing.\n\n### Calibration\n```bash\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all servos)\n```\n\n> **Hardware note**: Calibration physically moves servos to their limits. Ensure the arm is clear of obstacles.\n\n### Training & Inference\n```bash\nb2r.py train                       # Submit training job (interactive)\nb2r.py train --steps 50000 --name my_model\nb2r.py jobs                        # List training jobs and status\nb2r.py deploy <job_id>             # Deploy inference (interactive device selection)\nb2r.py stop-infer <job_id>         # Stop inference\n```\n\n`train` interactively lists available trajectories, lets you select datasets (e.g., `1,3,5` or `1-5` or `all`), confirms parameters, then submits to the server.\n\n`deploy` interactively selects GPU device, arm device, camera (optional), and execution mode (original/fixed/adaptive/overlap), then deploys.\n\n## API Endpoints Used\n\nAll commands are thin wrappers over HTTP API calls to `B2R_SERVER`:\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record start | POST | `/api/device/{id}/record/start` |\n| record stop | POST | `/api/device/{id}/record/stop` |\n| record status | GET | `/api/device/{id}/record/status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| frame | GET | `/api/camera/{id}/frame` |\n| download | GET | `.../trajectory/{id}/images`, `/api/traj-image/{id}/{idx}` |\n| dataset | GET | `.../trajectory/{id}/data`, `.../trajectory/{id}/images` |\n| video | GET | `.../trajectory/{id}/images`, `/api/traj-image/{id}/{idx}` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n| train | POST | `/api/training/jobs` |\n| jobs | GET | `/api/training/jobs` |\n| deploy | POST | `/api/training/jobs/{id}/deploy` |\n| stop-infer | POST | `/api/training/jobs/{id}/stop-inference` |\n\n## Preflight Checks (for AI Agents)\n\nAgents should verify before executing servo/recording commands:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Calibration exists | Run `calibrate` first |\n\n## Orchestration Examples\n\n### Record training data with camera\n```\n1. b2r torque off\n2. b2r record start --cam CAM-xxx\n3. [user demonstrates task by hand]\n4. b2r record stop my_dataset\n5. b2r dataset <traj_id>           # download locally\n6. b2r video <traj_id> demo.mp4    # generate preview video\n```\n\n### Train and deploy\n```\n1. b2r train                       # select datasets, submit job\n2. b2r jobs                        # monitor progress\n3. b2r deploy <job_id>             # deploy to GPU + arm\n4. [robot executes learned skill]\n5. b2r stop-infer <job_id>         # stop when done\n```\n\nFile v1.0.9:README.md\n\n# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agent reference manual (for Claude / GPT / other LLMs)\n\nFile v1.0.9:_meta.json\n\n{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1777953708832\n}\n\nFile v1.0.9:skill-card.md\n\n## Description:\n\nControl Box2Robot robotic arms through a cloud API for servo movement, camera-assisted trajectory recording, dataset download, video generation, and AI training or inference orchestration.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[boxjod](https://clawhub.ai/user/boxjod)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to let an agent operate Box2Robot robotic arms, collect camera-linked trajectory data, prepare datasets and videos, and coordinate training or deployed inference. It is intended for supervised hardware workflows where the operator approves motion, calibration, camera use, deployment, purchases, and third-party task execution.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Physical robot control can move servos, run calibration, and deploy inference in ways that may injure people or damage objects.\n\nMitigation: Use only with human supervision, a cleared workspace, trusted device selection, and explicit approval before motion, calibration, or inference deployment.\n\nRisk: The B2R_TOKEN environment variable and ~/.b2r_token file grant device-control access.\n\nMitigation: Treat the token as a sensitive credential, prefer HTTPS server settings, avoid command-line password exposure, and delete or rotate the token when no longer needed.\n\nRisk: Camera and microphone related operations can capture private information.\n\nMitigation: Run camera, microphone, recording, and dataset commands only with informed user consent and in an approved environment.\n\nRisk: Purchases and third-party task execution may have financial or operational consequences.\n\nMitigation: Require explicit human approval before purchases, store execution, or other third-party task actions.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/boxjod/skills/box2robot-skills)\n- [Box2Robot homepage](https://robot.box2ai.com)\n- [README.md](README.md)\n- [SKILLS.md](SKILLS.md)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, API calls, Files]\n\n**Output Format:** [Markdown guidance with CLI examples, configuration notes, and JSON-compatible API responses]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create or read local token, dataset, image, and video files during agent-directed Box2Robot workflows.]\n\n## Skill Version(s):\n\n1.0.9 (source: ClawHub release metadata; artifact frontmatter and pyproject.toml list 0.7.0)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.9:SKILLS.md\n\n# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `record.status` | device_id | Recording status |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n| `trajectory.images` | device_id, trajectory_id | Trajectory images |\n\n### pairing (Leader-Follower Pairing)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.list` | — | List pairings |\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect (keep record) |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n| `espnow.peers` | device_id | View neighbors |\n\n### calibration (Calibration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.status` | device_id | Calibration progress |\n| `calibrate.cancel` | device_id | Cancel calibration |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.status` | device_id | Camera status |\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.frame` | device_id | Get latest JPEG frame |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n| `camera.play_sound` | device_id, sound | Play sound effect |\n| `camera.record_audio_start` | device_id | Start audio recording |\n| `camera.record_audio_stop` | device_id | Stop audio recording |\n| `camera.recordings` | device_id | List recordings |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type], [train_steps] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.status` | job_id | Training progress |\n| `training.cancel` | job_id | Cancel job |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n| `training.rate` | job_id, rating(1-5) | Rate result |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n| `store.purchase` | task_id | Purchase |\n| `store.favorite` | task_id | Favorite |\n| `store.like` | task_id | Like |\n| `store.rate` | task_id, rating(1-5) | Rate |\n\n### config (Configuration & OTA)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.led_brightness` | device_id, brightness(0-255) | LED brightness |\n| `config.volume` | device_id, volume(0-255) | Volume |\n| `config.camera_resolution` | device_id, resolution | Resolution |\n| `config.bluetooth` | device_id, enable:bool | Bluetooth toggle |\n| `ota.check` | — | Check firmware update |\n| `ota.update` | device_id | Push update |\n\n---\n\n## Natural Language Mapping\n\nVia `b2r.py say \"text\"` or directly in the interactive shell:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"return home\" | servo.go_home |\n| \"release torque\" / \"let go\" | servo.torque(False) |\n| \"lock\" / \"hold\" | servo.torque(True) |\n| \"start recording\" / \"teach me\" | record.start |\n| \"stop recording\" / \"done recording\" | record.stop |\n| \"take a photo\" / \"snapshot\" | camera.snapshot |\n| \"open camera\" | camera.stream_mode(preview) |\n| \"auto calibrate\" | calibrate.auto |\n| \"louder\" / \"turn up volume\" | config.volume(200) |\n| \"faster\" / \"speed up\" | config.speed(2000) |\n| \"brighter\" | config.led_brightness(200) |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n| \"record 5 datasets\" | workflow.batch_record(5) |\n| \"teach you to dance\" | workflow.teach_single |\n\n> Chinese natural language is also fully supported (e.g., \"回零位\", \"释放力矩\", \"拍照\").\n\n---\n\n## Preflight Checks\n\nAgents must verify before executing actions:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control to free |\n| 4 | Calibration data exists | calibrate.auto to calibrate |\n| 5 | Servo count matches | Trajectory needs N axes vs device has M |\n| 6 | Calibration range compatible | ratio < 0.5 = incompatible |\n| 7 | Vision tasks: cam + gpu online | Report which device is missing |\n\n---\n\n## Typical Orchestration Scenarios\n\n### Scenario 1: \"Wave your hand\"\n```\n1. say \"wave\" → search local trajectories / store\n2. Found → preflight → trajectory.play or store.execute\n3. Not found → suggest \"You can record one: teach me to wave\"\n```\n\n### Scenario 2: \"Teach you to pour water, record 5 demos\"\n```\n1. torque off                              # Release torque\n2. camera.stream_mode(cam_id, \"preview\")   # Open camera\n3. Loop 5 times:\n   record.start(mode=\"single\", camera_id)  # Record\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")      # Save\n4. training.submit(name=\"pour_water\", dataset_ids)  # Submit training\n```\n\n### Scenario 3: Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU Worker auto-loop: capture image → inference → send command → arm executes\n3. training.stop_inference(job_id)  # Stop\n```\n\n---\n\n## HTTP API Direct Access\n\nYou can also call the HTTP API directly without the CLI:\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n# → {\"token\": \"eyJ...\"}\n\n# List devices\ncurl https://robot.box2ai.com/api/devices \\\n  -H \"Authorization: Bearer <token>\"\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n\n# Go home\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/go_home \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nFull endpoint list: see `box2robot_server/box2robot-cli/box2robot.md`\n\n---\n\n## LLM Conversational Control (chat.py)\n\nControl the arm through natural language via Zhipu AI function calling — the LLM decides when to invoke APIs.\n\n```bash\ncd box2robot_audio\n\n# Chat only\npython chat.py\n\n# Chat + arm control\npython chat.py --robot\n\n# First time: login required\npython chat.py --robot --login <user> <password>\n\n# Specify device\npython chat.py --robot --device B2R-XXXXXXXXXXXX\n```\n\n### Available LLM Tools (9)\n\n| Tool | Parameters | Description |\n|------|------------|-------------|\n| `list_devices` | — | List all devices with online status and type |\n| `servo_status` | — | Current servo position/load/temp + torque state |\n| `move_servo` | servo_id, position, [speed] | Move single servo (ID 1-6, pos 0-4095) |\n| `go_home` | — | All servos return to calibrated center |\n| `set_torque` | enable:bool | Torque on (lock) / off (release) |\n| `record_start` | — | Start trajectory recording |\n| `record_stop` | — | Stop recording and save |\n| `list_trajectories` | — | List saved trajectories |\n| `play_trajectory` | traj_id | Play trajectory by ID |\n| `search_and_play` | keyword | Search by name and play (e.g., \"nod\", \"wave\") |\n\n### API Feedback Format\n\nEach tool returns structured feedback for the LLM to generate responses:\n\n**Success** — prefixed with `OK:`:\n```\nOK: servo 1 moving to 2500 at speed 500. status=queued\nOK: torque=ON, stale=False, age=353ms, 6 servos\n  ID1: position=2494, load=36, temp=35\n  ...\nOK: Returning home in 15 steps (max_delta=342)\nOK: Recording saved. id=abc123, frames=150, duration=5000ms\nOK: 4 devices | active=B2R-XXXX\n  White Follower | id=B2R-XXXX | type=arm | ONLINE\n  Black Leader   | id=B2R-YYYY | type=arm | OFFLINE\n```\n\n**Failure** — prefixed with `ERROR:`:\n```\nERROR: Device not connected              → Device offline, check power/WiFi\nERROR: No servo data (stale=True)        → Device just booted, retry in a few seconds\nERROR: Cannot disable torque while Virtual Serial is active → Close VSerial first\nERROR: Already recording                 → record_stop before starting new recording\nERROR: Not recording                     → No need to stop\nERROR: unauthorized                      → Token expired, re-login required\nERROR: No device selected                → Select a device first\nERROR: Invalid servo_id (0-253)          → Servo ID out of range\nERROR: Invalid position (0-4095)         → Position value out of range\nERROR: Trajectory not found              → Trajectory ID does not exist\nERROR: Cannot connect to server          → Network issue, check connection\nERROR: Request timeout                   → Server may be busy\n```\n\n### Multi-Device Handling\n\n- On startup, the first online arm device is auto-selected\n- If multiple arms are online, the user is prompted to choose\n- Users can specify via `--device B2R-XXXX`, or say \"switch to the black leader\" in conversation\n- `list_devices` output marks `active=` for the currently controlled device\n\n### LLM Behavior Rules\n\n1. **Before execution**: Confirm when user intent is ambiguous (\"Which servo do you want to move?\")\n2. **After execution**: Generate response based on feedback (success → report result, failure → explain + suggest)\n3. **Multi-step operations**: Recording requires releasing torque first (set_torque false → record_start)\n4. **Safety**: Never proactively execute dangerous operations in conversation (factory_reset, calibrate, etc.)\n5. **Small talk**: Conversations unrelated to the arm should not trigger any tool calls\n\nFile v1.0.9:pyproject.toml\n\n[project]\nname = \"box2robot-skills\"\nversion = \"0.7.0\"\ndescription = \"Standalone CLI for controlling Box2Robot robotic arms via server HTTP API\"\nreadme = \"README.md\"\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.8\"\ndependencies = [\n    \"aiohttp>=3.8\",\n]\n\n[project.scripts]\nb2r = \"b2r:main\"\n\n# ── Runtime configuration ─────────────────────────────────────────────\n# Environment variables (all optional):\n#   B2R_SERVER   Server URL          Default: https://robot.box2ai.com\n#   B2R_TOKEN    JWT bearer token    Overrides ~/.b2r_token\n#   B2R_DEVICE   Default device ID   Overrides auto-select\n#\n# Credential storage:\n#   ~/.b2r_token   JSON file created on login with mode 0600.\n#                  Contains {token, server, device}. Treat like an SSH key.\n\nArchive v1.0.8: 7 files, 23082 bytes\n\nFiles: b2r.py (43330b), CHANGELOG.md (2485b), pyproject.toml (853b), README.md (3067b), SKILL.md (9832b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.8:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, stream live frames, browse the ACT skill store, download datasets, generate videos, and orchestrate AI training/inference.\nversion: 0.7.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      anyBins: [python3, python]\n      config: [~/.b2r_token]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories with camera, download datasets, generate replay videos, and orchestrate AI training/inference — all from a single CLI (`b2r.py`).\n\n> **Official skill** published by the Box2Robot team (https://robot.box2ai.com).\n\n## Safety & Supervision\n\n> **This skill controls physical robotic hardware and camera/microphone peripherals.**\n>\n> - **Human supervision required**: Do NOT run autonomously without operator oversight. Servo torque and motion commands cause physical movement that could injure people or damage objects.\n> - **Destructive operations** (`calibrate`) modify hardware state and require explicit user confirmation.\n> - **Privacy-sensitive operations** (`snapshot`, `frame`, `record start --cam`) access camera hardware — only invoke with user consent.\n> - **No OS shell access**: All operations are HTTP requests to `B2R_SERVER`. No arbitrary OS commands are executed. The only local subprocess is `ffmpeg` (optional, for video generation from downloaded JPEG frames).\n> - **Token sensitivity**: `~/.b2r_token` stores a JWT that grants device control. Created with mode 0600 (owner-only). Treat like an SSH key. Delete when no longer needed.\n\n## Credential Flow\n\n```\nlogin → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke\n```\n\nAll network calls go exclusively to `B2R_SERVER` (default: `https://robot.box2ai.com`). No other endpoints are contacted.\n\n## Environment Variables\n\n| Variable | Required | Description | Default |\n|----------|----------|-------------|---------|\n| `B2R_SERVER` | No | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | No | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | No | Default device ID (overrides auto-select) | — |\n\nNone are strictly required at install time. The `login` command handles authentication interactively and persists the token to `~/.b2r_token`. `B2R_TOKEN` is the primary credential variable and can be set to skip interactive login.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n```\n\n## Commands\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (home varies per joint), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording (servo data only)\nb2r.py record start --cam CAM-xxx  # Record with camera (servo + images)\nb2r.py record stop [name]          # Stop and save\nb2r.py record status               # Current recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\nWhen starting a recording, if online cameras are detected, the CLI offers an interactive prompt to select one. Camera recording captures synchronized JPEG frames alongside servo position data.\n\n### Camera\n```bash\nb2r.py snapshot                    # Request camera snapshot\nb2r.py frame [cam_id] [out.jpg]   # Download latest JPEG frame to local file\nb2r.py stream <cam_id> [--out DIR] [--latest FILE] [--duration SEC]\n                                   # Live MJPEG-over-WebSocket stream @ ~10Hz\n# Default: writes overwriting ./frame.jpg until Ctrl-C\n# --out DIR     : save every frame as DIR/000001.jpg, 000002.jpg, ...\n# --latest FILE : overwrite a single rolling file (default: ./frame.jpg)\n# --duration SEC: auto-stop after N seconds (0 = unlimited)\n```\n\n`stream` connects to `/ws/camera/{cam_id}`. The server auto-switches the camera into 10fps preview mode on first viewer and back to idle when all viewers disconnect — no manual mode toggle needed.\n\n> **Privacy note**: These commands access camera hardware. Only invoke with user consent.\n\n### ACT Skill Store\n```bash\nb2r.py store list [keyword] [--type T] [--cat C]   # Browse community-uploaded skills\nb2r.py store info <task>                            # Skill detail (TASK-... code or task_id)\nb2r.py store buy <task>                             # Purchase a paid skill (deducts credits)\nb2r.py store run <task> [device]                    # Execute a purchased skill on a device\nb2r.py store mine                                   # List skills you've purchased\nb2r.py store meta                                   # List available categories / types / tags\n```\n\nThe ACT Store is a marketplace of reusable, pre-trained robot skills (e.g. \"wave\", \"pour water\"). Free skills can be run directly; paid skills require `store buy` first. Execution sends the skill to the selected arm device and runs server-side inference — physical movement still requires human supervision.\n\n### Data Download\n```bash\nb2r.py download <traj_id> [dir]    # Download trajectory images only\nb2r.py dataset <traj_id> [dir]     # Download full dataset (JSON + images)\nb2r.py video <traj_id> [out.mp4]   # Generate MP4 video from trajectory images\nb2r.py video <traj_id> out.mp4 --fps 5  # Custom frame rate\n```\n\n`dataset` downloads the trajectory JSON (all frames with positions, timestamps, calibration snapshots) plus all camera images into a local directory.\n\n`video` downloads images to a temp directory and encodes them using `ffmpeg` (preferred) or `opencv-python` (fallback). Neither is required at install time — the command reports a clear error if both are missing.\n\n### Calibration\n```bash\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all servos)\n```\n\n> **Hardware note**: Calibration physically moves servos to their limits. Ensure the arm is clear of obstacles.\n\n### Training & Inference\n```bash\nb2r.py train                       # Submit training job (interactive)\nb2r.py train --steps 50000 --name my_model\nb2r.py jobs                        # List training jobs and status\nb2r.py deploy <job_id>             # Deploy inference (interactive device selection)\nb2r.py stop-infer <job_id>         # Stop inference\n```\n\n`train` interactively lists available trajectories, lets you select datasets (e.g., `1,3,5` or `1-5` or `all`), confirms parameters, then submits to the server.\n\n`deploy` interactively selects GPU device, arm device, camera (optional), and execution mode (original/fixed/adaptive/overlap), then deploys.\n\n## API Endpoints Used\n\nAll commands are thin wrappers over HTTP API calls to `B2R_SERVER`:\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record start | POST | `/api/device/{id}/record/start` |\n| record stop | POST | `/api/device/{id}/record/stop` |\n| record status | GET | `/api/device/{id}/record/status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| frame | GET | `/api/camera/{id}/frame` |\n| stream | WS | `/ws/camera/{id}` (binary JPEG frames @ ~10Hz) |\n| store list | GET | `/api/act/tasks` |\n| store info | GET | `/api/act/tasks/{ref}` |\n| store buy | POST | `/api/act/tasks/{ref}/purchase` |\n| store run | POST | `/api/act/tasks/{ref}/execute` |\n| store mine | GET | `/api/act/my-purchases` |\n| store meta | GET | `/api/act/meta` |\n| download | GET | `.../trajectory/{id}/images`, `/api/traj-image/{id}/{idx}` |\n| dataset | GET | `.../trajectory/{id}/data`, `.../trajectory/{id}/images` |\n| video | GET | `.../trajectory/{id}/images`, `/api/traj-image/{id}/{idx}` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n| train | POST | `/api/training/jobs` |\n| jobs | GET | `/api/training/jobs` |\n| deploy | POST | `/api/training/jobs/{id}/deploy` |\n| stop-infer | POST | `/api/training/jobs/{id}/stop-inference` |\n\n## Preflight Checks (for AI Agents)\n\nAgents should verify before executing servo/recording commands:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Calibration exists | Run `calibrate` first |\n\n## Orchestration Examples\n\n### Record training data with camera\n```\n1. b2r torque off\n2. b2r record start --cam CAM-xxx\n3. [user demonstrates task by hand]\n4. b2r record stop my_dataset\n5. b2r dataset <traj_id>           # download locally\n6. b2r video <traj_id> demo.mp4    # generate preview video\n```\n\n### Train and deploy\n```\n1. b2r train                       # select datasets, submit job\n2. b2r jobs                        # monitor progress\n3. b2r deploy <job_id>             # deploy to GPU + arm\n4. [robot executes learned skill]\n5. b2r stop-infer <job_id>         # stop when done\n```\n\nFile v1.0.8:README.md\n\n# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agent reference manual (for Claude / GPT / other LLMs)\n\nFile v1.0.8:_meta.json\n\n{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.8\",\n  \"publishedAt\": 1777952585477\n}\n\nFile v1.0.8:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to the `box2robot` ClawHub skill are documented here. This\nproject adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.7.0] — 2026-05-05\n\n### Added\n- `b2r stream <cam_id>` — live MJPEG-over-WebSocket camera stream via\n  `/ws/camera/{id}`. Supports `--out DIR` (per-frame archive),\n  `--latest FILE` (rolling single-file overwrite, default `./frame.jpg`),\n  and `--duration SEC` (auto-stop). Server auto-switches the camera\n  into 10fps preview mode while a viewer is connected.\n- `b2r store` subcommands for the ACT Skill Store marketplace:\n  - `store list [keyword] [--type T] [--cat C]` — browse community skills\n  - `store info <task>` — skill detail by code (`TASK-...`) or `task_id`\n  - `store buy <task>` — purchase a paid skill (credit deduction)\n  - `store run <task> [device]` — execute a skill on an arm device\n  - `store mine` — list purchased skills\n  - `store meta` — list categories / types / tags\n- SKILL.md documents the new commands, the `/ws/camera/{id}` endpoint,\n  and all `/api/act/*` HTTP endpoints used by the store subcommands.\n\n### Changed\n- Bumped skill version from `0.4.0` → `0.7.0` so it tracks the CLI's\n  actual capability surface (previously documented capabilities lagged\n  the implementation by several minor releases).\n- `description` field updated to mention the new stream and store\n  capabilities so vector search surfaces them.\n\n### Notes for reviewers\n- No new credentials are required. `B2R_TOKEN` (the existing\n  `primaryEnv`) covers the new `/api/act/*` and `/ws/camera/{id}`\n  endpoints — they reuse the same JWT.\n- The new `stream` command opens a long-lived WebSocket. It only writes\n  to user-specified local paths (`--out` / `--latest`) and never to\n  arbitrary system locations.\n- All network traffic still goes exclusively to `B2R_SERVER`\n  (default `https://robot.box2ai.com`). No third-party endpoints are\n  contacted.\n- `store run` and `store buy` invoke physical robot motion / credit\n  spend respectively — both are explicit user-driven commands, not\n  autonomous actions.\n\n## [0.4.0] — initial ClawHub publication\n\n- First public release on ClawHub.\n- Commands: `login`, `devices`, `status`, `move`, `home`, `torque`,\n  `record start/stop/status`, `play`, `snapshot`, `frame`, `download`,\n  `dataset`, `video`, `calibrate`, `train`, `jobs`, `deploy`,\n  `stop-infer`.\n- Token storage at `~/.b2r_token` (mode 0600).\n- Single dependency: `aiohttp`.\n\nFile v1.0.8:SKILLS.md\n\n# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `record.status` | device_id | Recording status |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n| `trajectory.images` | device_id, trajectory_id | Trajectory images |\n\n### pairing (Leader-Follower Pairing)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.list` | — | List pairings |\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect (keep record) |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n| `espnow.peers` | device_id | View neighbors |\n\n### calibration (Calibration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.status` | device_id | Calibration progress |\n| `calibrate.cancel` | device_id | Cancel calibration |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.status` | device_id | Camera status |\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.frame` | device_id | Get latest JPEG frame |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n| `camera.play_sound` | device_id, sound | Play sound effect |\n| `camera.record_audio_start` | device_id | Start audio recording |\n| `camera.record_audio_stop` | device_id | Stop audio recording |\n| `camera.recordings` | device_id | List recordings |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type], [train_steps] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.status` | job_id | Training progress |\n| `training.cancel` | job_id | Cancel job |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n| `training.rate` | job_id, rating(1-5) | Rate result |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n| `store.purchase` | task_id | Purchase |\n| `store.favorite` | task_id | Favorite |\n| `store.like` | task_id | Like |\n| `store.rate` | task_id, rating(1-5) | Rate |\n\n### config (Configuration & OTA)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.led_brightness` | device_id, brightness(0-255) | LED brightness |\n| `config.volume` | device_id, volume(0-255) | Volume |\n| `config.camera_resolution` | device_id, resolution | Resolution |\n| `config.bluetooth` | device_id, enable:bool | Bluetooth toggle |\n| `ota.check` | — | Check firmware update |\n| `ota.update` | device_id | Push update |\n\n---\n\n## Natural Language Mapping\n\nVia `b2r.py say \"text\"` or directly in the interactive shell:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"return home\" | servo.go_home |\n| \"release torque\" / \"let go\" | servo.torque(False) |\n| \"lock\" / \"hold\" | servo.torque(True) |\n| \"start recording\" / \"teach me\" | record.start |\n| \"stop recording\" / \"done recording\" | record.stop |\n| \"take a photo\" / \"snapshot\" | camera.snapshot |\n| \"open camera\" | camera.stream_mode(preview) |\n| \"auto calibrate\" | calibrate.auto |\n| \"louder\" / \"turn up volume\" | config.volume(200) |\n| \"faster\" / \"speed up\" | config.speed(2000) |\n| \"brighter\" | config.led_brightness(200) |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n| \"record 5 datasets\" | workflow.batch_record(5) |\n| \"teach you to dance\" | workflow.teach_single |\n\n> Chinese natural language is also fully supported (e.g., \"回零位\", \"释放力矩\", \"拍照\").\n\n---\n\n## Preflight Checks\n\nAgents must verify before executing actions:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control to free |\n| 4 | Calibration data exists | calibrate.auto to calibrate |\n| 5 | Servo count matches | Trajectory needs N axes vs device has M |\n| 6 | Calibration range compatible | ratio < 0.5 = incompatible |\n| 7 | Vision tasks: cam + gpu online | Report which device is missing |\n\n---\n\n## Typical Orchestration Scenarios\n\n### Scenario 1: \"Wave your hand\"\n```\n1. say \"wave\" → search local trajectories / store\n2. Found → preflight → trajectory.play or store.execute\n3. Not found → suggest \"You can record one: teach me to wave\"\n```\n\n### Scenario 2: \"Teach you to pour water, record 5 demos\"\n```\n1. torque off                              # Release torque\n2. camera.stream_mode(cam_id, \"preview\")   # Open camera\n3. Loop 5 times:\n   record.start(mode=\"single\", camera_id)  # Record\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")      # Save\n4. training.submit(name=\"pour_water\", dataset_ids)  # Submit training\n```\n\n### Scenario 3: Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU Worker auto-loop: capture image → inference → send command → arm executes\n3. training.stop_inference(job_id)  # Stop\n```\n\n---\n\n## HTTP API Direct Access\n\nYou can also call the HTTP API directly without the CLI:\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n# → {\"token\": \"eyJ...\"}\n\n# List devices\ncurl https://robot.box2ai.com/api/devices \\\n  -H \"Authorization: Bearer <token>\"\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n\n# Go home\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/go_home \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nFull endpoint list: see `box2robot_server/box2robot-cli/box2robot.md`\n\n---\n\n## LLM Conversational Control (chat.py)\n\nControl the arm through natural language via Zhipu AI function calling — the LLM decides when to invoke APIs.\n\n```bash\ncd box2robot_audio\n\n# Chat only\npython chat.py\n\n# Chat + arm control\npython chat.py --robot\n\n# First time: login required\npython chat.py --robot --login <user> <password>\n\n# Specify device\npython chat.py --robot --device B2R-XXXXXXXXXXXX\n```\n\n### Available LLM Tools (9)\n\n| Tool | Parameters | Description |\n|------|------------|-------------|\n| `list_devices` | — | List all devices with online status and type |\n| `servo_status` | — | Current servo position/load/temp + torque state |\n| `move_servo` | servo_id, position, [speed] | Move single servo (ID 1-6, pos 0-4095) |\n| `go_home` | — | All servos return to calibrated center |\n| `set_torque` | enable:bool | Torque on (lock) / off (release) |\n| `record_start` | — | Start trajectory recording |\n| `record_stop` | — | Stop recording and save |\n| `list_trajectories` | — | List saved trajectories |\n| `play_trajectory` | traj_id | Play trajectory by ID |\n| `search_and_play` | keyword | Search by name and play (e.g., \"nod\", \"wave\") |\n\n### API Feedback Format\n\nEach tool returns structured feedback for the LLM to generate responses:\n\n**Success** — prefixed with `OK:`:\n```\nOK: servo 1 moving to 2500 at speed 500. status=queued\nOK: torque=ON, stale=False, age=353ms, 6 servos\n  ID1: position=2494, load=36, temp=35\n  ...\nOK: Returning home in 15 steps (max_delta=342)\nOK: Recording saved. id=abc123, frames=150, duration=5000ms\nOK: 4 devices | active=B2R-XXXX\n  White Follower | id=B2R-XXXX | type=arm | ONLINE\n  Black Leader   | id=B2R-YYYY | type=arm | OFFLINE\n```\n\n**Failure** — prefixed with `ERROR:`:\n```\nERROR: Device not connected              → Device offline, check power/WiFi\nERROR: No servo data (stale=True)        → Device just booted, retry in a few seconds\nERROR: Cannot disable torque while Virtual Serial is active → Close VSerial first\nERROR: Already recording                 → record_stop before starting new recording\nERROR: Not recording                     → No need to stop\nERROR: unauthorized                      → Token expired, re-login required\nERROR: No device selected                → Select a device first\nERROR: Invalid servo_id (0-253)          → Servo ID out of range\nERROR: Invalid position (0-4095)         → Position value out of range\nERROR: Trajectory not found              → Trajectory ID does not exist\nERROR: Cannot connect to server          → Network issue, check connection\nERROR: Request timeout                   → Server may be busy\n```\n\n### Multi-Device Handling\n\n- On startup, the first online arm device is auto-selected\n- If multiple arms are online, the user is prompted to choose\n- Users can specify via `--device B2R-XXXX`, or say \"switch to the black leader\" in conversation\n- `list_devices` output marks `active=` for the currently controlled device\n\n### LLM Behavior Rules\n\n1. **Before execution**: Confirm when user intent is ambiguous (\"Which servo do you want to move?\")\n2. **After execution**: Generate response based on feedback (success → report result, failure → explain + suggest)\n3. **Multi-step operations**: Recording requires releasing torque first (set_torque false → record_start)\n4. **Safety**: Never proactively execute dangerous operations in conversation (factory_reset, calibrate, etc.)\n5. **Small talk**: Conversations unrelated to the arm should not trigger any tool calls\n\nFile v1.0.8:pyproject.toml\n\n[project]\nname = \"box2robot-skills\"\nversion = \"0.7.0\"\ndescription = \"Standalone CLI for controlling Box2Robot robotic arms via server HTTP API\"\nreadme = \"README.md\"\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.8\"\ndependencies = [\n    \"aiohttp>=3.8\",\n]\n\n[project.scripts]\nb2r = \"b2r:main\"\n\n# ── Runtime configuration ─────────────────────────────────────────────\n# Environment variables (all optional):\n#   B2R_SERVER   Server URL          Default: https://robot.box2ai.com\n#   B2R_TOKEN    JWT bearer token    Overrides ~/.b2r_token\n#   B2R_DEVICE   Default device ID   Overrides auto-select\n#\n# Credential storage:\n#   ~/.b2r_token   JSON file created on login with mode 0600.\n#                  Contains {token, server, device}. Treat like an SSH key.\n\nArchive v1.0.7: 6 files, 18828 bytes\n\nFiles: b2r.py (32040b), pyproject.toml (853b), README.md (3067b), SKILL.md (9251b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.7:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories, calibrate, camera, voice, and orchestrate AI training/inference.\nversion: 0.3.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      env: [B2R_TOKEN, B2R_SERVER, B2R_DEVICE]\n      bins: [python3]\n      anyBins: [python3, python]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories, calibrate, capture images, voice interaction, and orchestrate AI training/inference — all from a single CLI.\n\n> **Official skill** published by the Box2Robot team (https://robot.box2ai.com).\n\n## Safety & Supervision\n\n> **This skill controls physical robotic hardware and camera/microphone peripherals.**\n>\n> - **Human supervision required**: Do NOT run autonomously without operator oversight. Servo torque and motion commands cause physical movement that could injure people or damage objects.\n> - **Destructive operations** (`device.factory_reset`, `device.wifi_clear`, `calibrate.center_offset`) require explicit user confirmation before execution.\n> - **Privacy-sensitive operations** (`camera.snapshot`, `camera.voice_start`, `camera.record_audio_start`) access camera and microphone — only invoke with user consent.\n> - **No OS shell access**: The `exec` command routes named Actions through the HTTP API (e.g., `exec servo.move`). The `shell` command is an interactive REPL that dispatches CLI subcommands — neither executes arbitrary OS commands. All operations are HTTP requests to `B2R_SERVER`.\n> - **Token sensitivity**: `~/.b2r_token` stores a JWT that grants device control. Treat it like an SSH key. Delete it when no longer needed.\n\n## Credential Flow\n\n```\nlogin → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke\n```\n\nAll network calls go exclusively to `B2R_SERVER` (default: `https://robot.box2ai.com`). No other endpoints are contacted.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\n## CLI Quick Reference\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop and save\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Camera & Data\n```bash\nb2r.py snapshot                    # Camera snapshot\nb2r.py frame [cam_id] [output.jpg] # Download latest camera frame (JPEG)\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all)\nb2r.py download <traj_id> [dir]    # Download trajectory images (HTTP GET only)\n```\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.factory_reset` | **[DESTRUCTIVE]** Factory reset — requires user confirmation |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n\n### pairing (Leader-Follower)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n\n### calibration\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n\n> **Privacy note**: Snapshot and voice commands access camera/microphone hardware. Only invoke with user consent.\n\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.snapshot` | device_id | **[PRIVACY]** Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.voice_start` | device_id | **[PRIVACY]** Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n\n### config (Configuration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.volume` | device_id, volume(0-255) | Volume |\n\n## Natural Language\n\nVia `b2r.py say \"text\"`:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"回零位\" | servo.go_home |\n| \"release torque\" / \"释放力矩\" | servo.torque(False) |\n| \"start recording\" / \"开始录制\" | record.start |\n| \"take a photo\" / \"拍照\" | camera.snapshot |\n| \"auto calibrate\" / \"自动校准\" | calibrate.auto |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n\n## Preflight Checks\n\nAgents must verify before executing:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control first |\n| 4 | Calibration exists | calibrate.auto first |\n| 5 | Servo count matches | Trajectory vs device axis count |\n\n## Orchestration Examples\n\n### \"Teach you to pour water, record 5 demos\"\n```\n1. torque off\n2. camera.stream_mode(cam_id, \"preview\")\n3. Loop 5x:\n   record.start(mode=\"single\", camera_id)\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")\n4. training.submit(name=\"pour_water\", dataset_ids)\n```\n\n### Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU auto-loop: image → inference → command → arm\n3. training.stop_inference(job_id)\n```\n\n## HTTP API Direct Access\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n```\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID | — |\n\nFile v1.0.7:README.md\n\n# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agent reference manual (for Claude / GPT / other LLMs)\n\nFile v1.0.7:_meta.json\n\n{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.7\",\n  \"publishedAt\": 1776499998942\n}\n\nFile v1.0.7:SKILLS.md\n\n# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `record.status` | device_id | Recording status |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n| `trajectory.images` | device_id, trajectory_id | Trajectory images |\n\n### pairing (Leader-Follower Pairing)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.list` | — | List pairings |\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect (keep record) |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n| `espnow.peers` | device_id | View neighbors |\n\n### calibration (Calibration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.status` | device_id | Calibration progress |\n| `calibrate.cancel` | device_id | Cancel calibration |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.status` | device_id | Camera status |\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.frame` | device_id | Get latest JPEG frame |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n| `camera.play_sound` | device_id, sound | Play sound effect |\n| `camera.record_audio_start` | device_id | Start audio recording |\n| `camera.record_audio_stop` | device_id | Stop audio recording |\n| `camera.recordings` | device_id | List recordings |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type], [train_steps] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.status` | job_id | Training progress |\n| `training.cancel` | job_id | Cancel job |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n| `training.rate` | job_id, rating(1-5) | Rate result |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n| `store.purchase` | task_id | Purchase |\n| `store.favorite` | task_id | Favorite |\n| `store.like` | task_id | Like |\n| `store.rate` | task_id, rating(1-5) | Rate |\n\n### config (Configuration & OTA)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.led_brightness` | device_id, brightness(0-255) | LED brightness |\n| `config.volume` | device_id, volume(0-255) | Volume |\n| `config.camera_resolution` | device_id, resolution | Resolution |\n| `config.bluetooth` | device_id, enable:bool | Bluetooth toggle |\n| `ota.check` | — | Check firmware update |\n| `ota.update` | device_id | Push update |\n\n---\n\n## Natural Language Mapping\n\nVia `b2r.py say \"text\"` or directly in the interactive shell:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"return home\" | servo.go_home |\n| \"release torque\" / \"let go\" | servo.torque(False) |\n| \"lock\" / \"hold\" | servo.torque(True) |\n| \"start recording\" / \"teach me\" | record.start |\n| \"stop recording\" / \"done recording\" | record.stop |\n| \"take a photo\" / \"snapshot\" | camera.snapshot |\n| \"open camera\" | camera.stream_mode(preview) |\n| \"auto calibrate\" | calibrate.auto |\n| \"louder\" / \"turn up volume\" | config.volume(200) |\n| \"faster\" / \"speed up\" | config.speed(2000) |\n| \"brighter\" | config.led_brightness(200) |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n| \"record 5 datasets\" | workflow.batch_record(5) |\n| \"teach you to dance\" | workflow.teach_single |\n\n> Chinese natural language is also fully supported (e.g., \"回零位\", \"释放力矩\", \"拍照\").\n\n---\n\n## Preflight Checks\n\nAgents must verify before executing actions:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control to free |\n| 4 | Calibration data exists | calibrate.auto to calibrate |\n| 5 | Servo count matches | Trajectory needs N axes vs device has M |\n| 6 | Calibration range compatible | ratio < 0.5 = incompatible |\n| 7 | Vision tasks: cam + gpu online | Report which device is missing |\n\n---\n\n## Typical Orchestration Scenarios\n\n### Scenario 1: \"Wave your hand\"\n```\n1. say \"wave\" → search local trajectories / store\n2. Found → preflight → trajectory.play or store.execute\n3. Not found → suggest \"You can record one: teach me to wave\"\n```\n\n### Scenario 2: \"Teach you to pour water, record 5 demos\"\n```\n1. torque off                              # Release torque\n2. camera.stream_mode(cam_id, \"preview\")   # Open camera\n3. Loop 5 times:\n   record.start(mode=\"single\", camera_id)  # Record\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")      # Save\n4. training.submit(name=\"pour_water\", dataset_ids)  # Submit training\n```\n\n### Scenario 3: Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU Worker auto-loop: capture image → inference → send command → arm executes\n3. training.stop_inference(job_id)  # Stop\n```\n\n---\n\n## HTTP API Direct Access\n\nYou can also call the HTTP API directly without the CLI:\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n# → {\"token\": \"eyJ...\"}\n\n# List devices\ncurl https://robot.box2ai.com/api/devices \\\n  -H \"Authorization: Bearer <token>\"\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n\n# Go home\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/go_home \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nFull endpoint list: see `box2robot_server/box2robot-cli/box2robot.md`\n\n---\n\n## LLM Conversational Control (chat.py)\n\nControl the arm through natural language via Zhipu AI function calling — the LLM decides when to invoke APIs.\n\n```bash\ncd box2robot_audio\n\n# Chat only\npython chat.py\n\n# Chat + arm control\npython chat.py --robot\n\n# First time: login required\npython chat.py --robot --login <user> <password>\n\n# Specify device\npython chat.py --robot --device B2R-XXXXXXXXXXXX\n```\n\n### Available LLM Tools (9)\n\n| Tool | Parameters | Description |\n|------|------------|-------------|\n| `list_devices` | — | List all devices with online status and type |\n| `servo_status` | — | Current servo position/load/temp + torque state |\n| `move_servo` | servo_id, position, [speed] | Move single servo (ID 1-6, pos 0-4095) |\n| `go_home` | — | All servos return to calibrated center |\n| `set_torque` | enable:bool | Torque on (lock) / off (release) |\n| `record_start` | — | Start trajectory recording |\n| `record_stop` | — | Stop recording and save |\n| `list_trajectories` | — | List saved trajectories |\n| `play_trajectory` | traj_id | Play trajectory by ID |\n| `search_and_play` | keyword | Search by name and play (e.g., \"nod\", \"wave\") |\n\n### API Feedback Format\n\nEach tool returns structured feedback for the LLM to generate responses:\n\n**Success** — prefixed with `OK:`:\n```\nOK: servo 1 moving to 2500 at speed 500. status=queued\nOK: torque=ON, stale=False, age=353ms, 6 servos\n  ID1: position=2494, load=36, temp=35\n  ...\nOK: Returning home in 15 steps (max_delta=342)\nOK: Recording saved. id=abc123, frames=150, duration=5000ms\nOK: 4 devices | active=B2R-XXXX\n  White Follower | id=B2R-XXXX | type=arm | ONLINE\n  Black Leader   | id=B2R-YYYY | type=arm | OFFLINE\n```\n\n**Failure** — prefixed with `ERROR:`:\n```\nERROR: Device not connected              → Device offline, check power/WiFi\nERROR: No servo data (stale=True)        → Device just booted, retry in a few seconds\nERROR: Cannot disable torque while Virtual Serial is active → Close VSerial first\nERROR: Already recording                 → record_stop before starting new recording\nERROR: Not recording                     → No need to stop\nERROR: unauthorized                      → Token expired, re-login required\nERROR: No device selected                → Select a device first\nERROR: Invalid servo_id (0-253)          → Servo ID out of range\nERROR: Invalid position (0-4095)         → Position value out of range\nERROR: Trajectory not found              → Trajectory ID does not exist\nERROR: Cannot connect to server          → Network issue, check connection\nERROR: Request timeout                   → Server may be busy\n```\n\n### Multi-Device Handling\n\n- On startup, the first online arm device is auto-selected\n- If multiple arms are online, the user is prompted to choose\n- Users can specify via `--device B2R-XXXX`, or say \"switch to the black leader\" in conversation\n- `list_devices` output marks `active=` for the currently controlled device\n\n### LLM Behavior Rules\n\n1. **Before execution**: Confirm when user intent is ambiguous (\"Which servo do you want to move?\")\n2. **After execution**: Generate response based on feedback (success → report result, failure → explain + suggest)\n3. **Multi-step operations**: Recording requires releasing torque first (set_torque false → record_start)\n4. **Safety**: Never proactively execute dangerous operations in conversation (factory_reset, calibrate, etc.)\n5. **Small talk**: Conversations unrelated to the arm should not trigger any tool calls\n\nFile v1.0.7:pyproject.toml\n\n[project]\nname = \"box2robot-skills\"\nversion = \"0.3.0\"\ndescription = \"Standalone CLI for controlling Box2Robot robotic arms via server HTTP API\"\nreadme = \"README.md\"\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.8\"\ndependencies = [\n    \"aiohttp>=3.8\",\n]\n\n[project.scripts]\nb2r = \"b2r:main\"\n\n# ── Runtime configuration ─────────────────────────────────────────────\n# Environment variables (all optional):\n#   B2R_SERVER   Server URL          Default: https://robot.box2ai.com\n#   B2R_TOKEN    JWT bearer token    Overrides ~/.b2r_token\n#   B2R_DEVICE   Default device ID   Overrides auto-select\n#\n# Credential storage:\n#   ~/.b2r_token   JSON file created on login with mode 0600.\n#                  Contains {token, server, device}. Treat like an SSH key.\n\nArchive v1.0.6: 6 files, 14729 bytes\n\nFiles: b2r.py (13524b), pyproject.toml (853b), README.md (3067b), SKILL.md (9251b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.6:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories, calibrate, camera, voice, and orchestrate AI training/inference.\nversion: 0.3.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      env: [B2R_TOKEN, B2R_SERVER, B2R_DEVICE]\n      bins: [python3]\n      anyBins: [python3, python]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories, calibrate, capture images, voice interaction, and orchestrate AI training/inference — all from a single CLI.\n\n> **Official skill** published by the Box2Robot team (https://robot.box2ai.com).\n\n## Safety & Supervision\n\n> **This skill controls physical robotic hardware and camera/microphone peripherals.**\n>\n> - **Human supervision required**: Do NOT run autonomously without operator oversight. Servo torque and motion commands cause physical movement that could injure people or damage objects.\n> - **Destructive operations** (`device.factory_reset`, `device.wifi_clear`, `calibrate.center_offset`) require explicit user confirmation before execution.\n> - **Privacy-sensitive operations** (`camera.snapshot`, `camera.voice_start`, `camera.record_audio_start`) access camera and microphone — only invoke with user consent.\n> - **No OS shell access**: The `exec` command routes named Actions through the HTTP API (e.g., `exec servo.move`). The `shell` command is an interactive REPL that dispatches CLI subcommands — neither executes arbitrary OS commands. All operations are HTTP requests to `B2R_SERVER`.\n> - **Token sensitivity**: `~/.b2r_token` stores a JWT that grants device control. Treat it like an SSH key. Delete it when no longer needed.\n\n## Credential Flow\n\n```\nlogin → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke\n```\n\nAll network calls go exclusively to `B2R_SERVER` (default: `https://robot.box2ai.com`). No other endpoints are contacted.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\n## CLI Quick Reference\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop and save\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Camera & Data\n```bash\nb2r.py snapshot                    # Camera snapshot\nb2r.py frame [cam_id] [output.jpg] # Download latest camera frame (JPEG)\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all)\nb2r.py download <traj_id> [dir]    # Download trajectory images (HTTP GET only)\n```\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.factory_reset` | **[DESTRUCTIVE]** Factory reset — requires user confirmation |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n\n### pairing (Leader-Follower)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n\n### calibration\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n\n> **Privacy note**: Snapshot and voice commands access camera/microphone hardware. Only invoke with user consent.\n\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.snapshot` | device_id | **[PRIVACY]** Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.voice_start` | device_id | **[PRIVACY]** Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n\n### config (Configuration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.volume` | device_id, volume(0-255) | Volume |\n\n## Natural Language\n\nVia `b2r.py say \"text\"`:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"回零位\" | servo.go_home |\n| \"release torque\" / \"释放力矩\" | servo.torque(False) |\n| \"start recording\" / \"开始录制\" | record.start |\n| \"take a photo\" / \"拍照\" | camera.snapshot |\n| \"auto calibrate\" / \"自动校准\" | calibrate.auto |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n\n## Preflight Checks\n\nAgents must verify before executing:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control first |\n| 4 | Calibration exists | calibrate.auto first |\n| 5 | Servo count matches | Trajectory vs device axis count |\n\n## Orchestration Examples\n\n### \"Teach you to pour water, record 5 demos\"\n```\n1. torque off\n2. camera.stream_mode(cam_id, \"preview\")\n3. Loop 5x:\n   record.start(mode=\"single\", camera_id)\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")\n4. training.submit(name=\"pour_water\", dataset_ids)\n```\n\n### Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU auto-loop: image → inference → command → arm\n3. training.stop_inference(job_id)\n```\n\n## HTTP API Direct Access\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n```\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID | — |\n\nFile v1.0.6:README.md\n\n# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agent reference manual (for Claude / GPT / other LLMs)\n\nFile v1.0.6:_meta.json\n\n{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.6\",\n  \"publishedAt\": 1776489137230\n}\n\nFile v1.0.6:SKILLS.md\n\n# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `record.status` | device_id | Recording status |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n| `trajectory.images` | device_id, trajectory_id | Trajectory images |\n\n### pairing (Leader-Follower Pairing)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.list` | — | List pairings |\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect (keep record) |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n| `espnow.peers` | device_id | View neighbors |\n\n### calibration (Calibration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.status` | device_id | Calibration progress |\n| `calibrate.cancel` | device_id | Cancel calibration |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.status` | device_id | Camera status |\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.frame` | device_id | Get latest JPEG frame |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n| `camera.play_sound` | device_id, sound | Play sound effect |\n| `camera.record_audio_start` | device_id | Start audio recording |\n| `camera.record_audio_stop` | device_id | Stop audio recording |\n| `camera.recordings` | device_id | List recordings |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type], [train_steps] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.status` | job_id | Training progress |\n| `training.cancel` | job_id | Cancel job |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n| `training.rate` | job_id, rating(1-5) | Rate result |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n| `store.purchase` | task_id | Purchase |\n| `store.favorite` | task_id | Favorite |\n| `store.like` | task_id | Like |\n| `store.rate` | task_id, rating(1-5) | Rate |\n\n### config (Configuration & OTA)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.led_brightness` | device_id, brightness(0-255) | LED brightness |\n| `config.volume` | device_id, volume(0-255) | Volume |\n| `config.camera_resolution` | device_id, resolution | Resolution |\n| `config.bluetooth` | device_id, enable:bool | Bluetooth toggle |\n| `ota.check` | — | Check firmware update |\n| `ota.update` | device_id | Push update |\n\n---\n\n## Natural Language Mapping\n\nVia `b2r.py say \"text\"` or directly in the interactive shell:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"return home\" | servo.go_home |\n| \"release torque\" / \"let go\" | servo.torque(False) |\n| \"lock\" / \"hold\" | servo.torque(True) |\n| \"start recording\" / \"teach me\" | record.start |\n| \"stop recording\" / \"done recording\" | record.stop |\n| \"take a photo\" / \"snapshot\" | camera.snapshot |\n| \"open camera\" | camera.stream_mode(preview) |\n| \"auto calibrate\" | calibrate.auto |\n| \"louder\" / \"turn up volume\" | config.volume(200) |\n| \"faster\" / \"speed up\" | config.speed(2000) |\n| \"brighter\" | config.led_brightness(200) |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n| \"record 5 datasets\" | workflow.batch_record(5) |\n| \"teach you to dance\" | workflow.teach_single |\n\n> Chinese natural language is also fully supported (e.g., \"回零位\", \"释放力矩\", \"拍照\").\n\n---\n\n## Preflight Checks\n\nAgents must verify before executing actions:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control to free |\n| 4 | Calibration data exists | calibrate.auto to calibrate |\n| 5 | Servo count matches | Trajectory needs N axes vs device has M |\n| 6 | Calibration range compatible | ratio < 0.5 = incompatible |\n| 7 | Vision tasks: cam + gpu online | Report which device is missing |\n\n---\n\n## Typical Orchestration Scenarios\n\n### Scenario 1: \"Wave your hand\"\n```\n1. say \"wave\" → search local trajectories / store\n2. Found → preflight → trajectory.play or store.execute\n3. Not found → suggest \"You can record one: teach me to wave\"\n```\n\n### Scenario 2: \"Teach you to pour water, record 5 demos\"\n```\n1. torque off                              # Release torque\n2. camera.stream_mode(cam_id, \"preview\")   # Open camera\n3. Loop 5 times:\n   record.start(mode=\"single\", camera_id)  # Record\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")      # Save\n4. training.submit(name=\"pour_water\", dataset_ids)  # Submit training\n```\n\n### Scenario 3: Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU Worker auto-loop: capture image → inference → send command → arm executes\n3. training.stop_inference(job_id)  # Stop\n```\n\n---\n\n## HTTP API Direct Access\n\nYou can also call the HTTP API directly without the CLI:\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n# → {\"token\": \"eyJ...\"}\n\n# List devices\ncurl https://robot.box2ai.com/api/devices \\\n  -H \"Authorization: Bearer <token>\"\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n\n# Go home\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/go_home \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nFull endpoint list: see `box2robot_server/box2robot-cli/box2robot.md`\n\n---\n\n## LLM Conversational Control (chat.py)\n\nControl the arm through natural language via Zhipu AI function calling — the LLM decides when to invoke APIs.\n\n```bash\ncd box2robot_audio\n\n# Chat only\npython chat.py\n\n# Chat + arm control\npython chat.py --robot\n\n# First time: login required\npython chat.py --robot --login <user> <password>\n\n# Specify device\npython chat.py --robot --device B2R-XXXXXXXXXXXX\n```\n\n### Available LLM Tools (9)\n\n| Tool | Parameters | Description |\n|------|------------|-------------|\n| `list_devices` | — | List all devices with online status and type |\n| `servo_status` | — | Current servo position/load/temp + torque state |\n| `move_servo` | servo_id, position, [speed] | Move single servo (ID 1-6, pos 0-4095) |\n| `go_home` | — | All servos return to calibrated center |\n| `set_torque` | enable:bool | Torque on (lock) / off (release) |\n| `record_start` | — | Start trajectory recording |\n| `record_stop` | — | Stop recording and save |\n| `list_trajectories` | — | List saved trajectories |\n| `play_trajectory` | traj_id | Play trajectory by ID |\n| `search_and_play` | keyword | Search by name and play (e.g., \"nod\", \"wave\") |\n\n### API Feedback Format\n\nEach tool returns structured feedback for the LLM to generate responses:\n\n**Success** — prefixed with `OK:`:\n```\nOK: servo 1 moving to 2500 at speed 500. status=queued\nOK: torque=ON, stale=False, age=353ms, 6 servos\n  ID1: position=2494, load=36, temp=35\n  ...\nOK: Returning home in 15 steps (max_delta=342)\nOK: Recording saved. id=abc123, frames=150, duration=5000ms\nOK: 4 devices | active=B2R-XXXX\n  White Follower | id=B2R-XXXX | type=arm | ONLINE\n  Black Leader   | id=B2R-YYYY | type=arm | OFFLINE\n```\n\n**Failure** — prefixed with `ERROR:`:\n```\nERROR: Device not connected              → Device offline, check power/WiFi\nERROR: No servo data (stale=True)        → Device just booted, retry in a few seconds\nERROR: Cannot disable torque while Virtual Serial is active → Close VSerial first\nERROR: Already recording                 → record_stop before starting new recording\nERROR: Not recording                     → No need to stop\nERROR: unauthorized                      → Token expired, re-login required\nERROR: No device selected                → Select a device first\nERROR: Invalid servo_id (0-253)          → Servo ID out of range\nERROR: Invalid position (0-4095)         → Position value out of range\nERROR: Trajectory not found              → Trajectory ID does not exist\nERROR: Cannot connect to server          → Network issue, check connection\nERROR: Request timeout                   → Server may be busy\n```\n\n### Multi-Device Handling\n\n- On startup, the first online arm device is auto-selected\n- If multiple arms are online, the user is prompted to choose\n- Users can specify via `--device B2R-XXXX`, or say \"switch to the black leader\" in conversation\n- `list_devices` output marks `active=` for the currently controlled device\n\n### LLM Behavior Rules\n\n1. **Before execution**: Confirm when user intent is ambiguous (\"Which servo do you want to move?\")\n2. **After execution**: Generate response based on feedback (success → report result, failure → explain + suggest)\n3. **Multi-step operations**: Recording requires releasing torque first (set_torque false → record_start)\n4. **Safety**: Never proactively execute dangerous operations in conversation (factory_reset, calibrate, etc.)\n5. **Small talk**: Conversations unrelated to the arm should not trigger any tool calls\n\nFile v1.0.6:pyproject.toml\n\n[project]\nname = \"box2robot-skills\"\nversion = \"0.3.0\"\ndescription = \"Standalone CLI for controlling Box2Robot robotic arms via server HTTP API\"\nreadme = \"README.md\"\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.8\"\ndependencies = [\n    \"aiohttp>=3.8\",\n]\n\n[project.scripts]\nb2r = \"b2r:main\"\n\n# ── Runtime configuration ─────────────────────────────────────────────\n# Environment variables (all optional):\n#   B2R_SERVER   Server URL          Default: https://robot.box2ai.com\n#   B2R_TOKEN    JWT bearer token    Overrides ~/.b2r_token\n#   B2R_DEVICE   Default device ID   Overrides auto-select\n#\n# Credential storage:\n#   ~/.b2r_token   JSON file created on login with mode 0600.\n#                  Contains {token, server, device}. Treat like an SSH key.\n\nArchive v1.0.5: 6 files, 13894 bytes\n\nFiles: b2r.py (13524b), pyproject.toml (853b), README.md (3067b), SKILL.md (7364b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.5:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories, calibrate, camera, voice, and orchestrate AI training/inference.\nversion: 0.2.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      env: [B2R_TOKEN]\n      bins: [python3]\n      anyBins: [python3, python]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories, calibrate, capture images, voice interaction, and orchestrate AI training/inference — all from a single CLI.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\n## CLI Quick Reference\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop and save\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Camera & Calibration\n```bash\nb2r.py snapshot                    # Camera snapshot\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all)\n```\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n\n### pairing (Leader-Follower)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n\n### calibration\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n\n### config (Configuration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.volume` | device_id, volume(0-255) | Volume |\n\n## Natural Language\n\nVia `b2r.py say \"text\"`:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"回零位\" | servo.go_home |\n| \"release torque\" / \"释放力矩\" | servo.torque(False) |\n| \"start recording\" / \"开始录制\" | record.start |\n| \"take a photo\" / \"拍照\" | camera.snapshot |\n| \"auto calibrate\" / \"自动校准\" | calibrate.auto |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n\n## Preflight Checks\n\nAgents must verify before executing:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control first |\n| 4 | Calibration exists | calibrate.auto first |\n| 5 | Servo count matches | Trajectory vs device axis count |\n\n## Orchestration Examples\n\n### \"Teach you to pour water, record 5 demos\"\n```\n1. torque off\n2. camera.stream_mode(cam_id, \"preview\")\n3. Loop 5x:\n   record.start(mode=\"single\", camera_id)\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")\n4. training.submit(name=\"pour_water\", dataset_ids)\n```\n\n### Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU auto-loop: image → inference → command → arm\n3. training.stop_inference(job_id)\n```\n\n## HTTP API Direct Access\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n```\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID | — |\n\nFile v1.0.5:README.md\n\n# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agent reference manual (for Claude / GPT / other LLMs)\n\nFile v1.0.5:_meta.json\n\n{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1776488505312\n}\n\nFile v1.0.5:SKILLS.md\n\n# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `record.status` | device_id | Recording status |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n| `trajectory.images` | device_id, trajectory_id | Trajectory images |\n\n### pairing (Leader-Follower Pairing)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.list` | — | List pairings |\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect (keep record) |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n| `espnow.peers` | device_id | View neighbors |\n\n### calibration (Calibration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.status` | device_id | Calibration progress |\n| `calibrate.cancel` | device_id | Cancel calibration |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.status` | device_id | Camera status |\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.frame` | device_id | Get latest JPEG frame |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n| `camera.play_sound` | device_id, sound | Play sound effect |\n| `camera.record_audio_start` | device_id | Start audio recording |\n| `camera.record_audio_stop` | device_id | Stop audio recording |\n| `camera.recordings` | device_id | List recordings |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type], [train_steps] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.status` | job_id | Training progress |\n| `training.cancel` | job_id | Cancel job |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n| `training.rate` | job_id, rating(1-5) | Rate result |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n| `store.purchase` | task_id | Purchase |\n| `store.favorite` | task_id | Favorite |\n| `store.like` | task_id | Like |\n| `store.rate` | task_id, rating(1-5) | Rate |\n\n### config (Configuration & OTA)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | Update config |\n| `config.speed` | device_id, speed(100-4000) | Set speed |\n| `config.led_brightness` | device_id, brightness(0-255) | LED brightness |\n| `config.volume` | device_id, volume(0-255) | Volume |\n| `config.camera_resolution` | device_id, resolution | Resolution |\n| `config.bluetooth` | device_id, enable:bool | Bluetooth toggle |\n| `ota.check` | — | Check firmware update |\n| `ota.update` | device_id | Push update |\n\n---\n\n## Natural Language Mapping\n\nVia `b2r.py say \"text\"` or directly in the interactive shell:\n\n| Input | Maps to |\n|-------|---------|\n| \"go home\" / \"return home\" | servo.go_home |\n| \"release torque\" / \"let go\" | servo.torque(False) |\n| \"lock\" / \"hold\" | servo.torque(True) |\n| \"start recording\" / \"teach me\" | record.start |\n| \"stop recording\" / \"done recording\" | record.stop |\n| \"take a photo\" / \"snapshot\" | camera.snapshot |\n| \"open camera\" | camera.stream_mode(preview) |\n| \"auto calibrate\" | calibrate.auto |\n| \"louder\" / \"turn up volume\" | config.volume(200) |\n| \"faster\" / \"speed up\" | config.speed(2000) |\n| \"brighter\" | config.led_brightness(200) |\n| \"move servo 1 to 2048\" | servo.move(1, 2048) |\n| \"record 5 datasets\" | workflow.batch_record(5) |\n| \"teach you to dance\" | workflow.teach_single |\n\n> Chinese natural language is also fully supported (e.g., \"回零位\", \"释放力矩\", \"拍照\").\n\n---\n\n## Preflight Checks\n\nAgents must verify before executing actions:\n\n| Step | Check | On Failure |\n|------|-------|------------|\n| 1 | Device online | \"Device offline — check power\" |\n| 2 | Device type = arm | \"Not a robotic arm\" |\n| 3 | Control mode = idle | release_control to free |\n| 4 | Calibration data exists | calibrate.auto to calibrate |\n| 5 | Servo count matches | Trajectory needs N axes vs device has M |\n| 6 | Calibration range compatible | ratio < 0.5 = incompatible |\n| 7 | Vision tasks: cam + gpu online | Report which device is missing |\n\n---\n\n## Typical Orchestration Scenarios\n\n### Scenario 1: \"Wave your hand\"\n```\n1. say \"wave\" → search local trajectories / store\n2. Found → preflight → trajectory.play or store.execute\n3. Not found → suggest \"You can record one: teach me to wave\"\n```\n\n### Scenario 2: \"Teach you to pour water, record 5 demos\"\n```\n1. torque off                              # Release torque\n2. camera.stream_mode(cam_id, \"preview\")   # Open camera\n3. Loop 5 times:\n   record.start(mode=\"single\", camera_id)  # Record\n   [user demonstrates]\n   record.stop(name=\"pour_water_{n}\")      # Save\n4. training.submit(name=\"pour_water\", dataset_ids)  # Submit training\n```\n\n### Scenario 3: Visual Inference Loop\n```\n1. training.deploy(job_id, arm_id, gpu_id, cam_id)\n2. GPU Worker auto-loop: capture image → inference → send command → arm executes\n3. training.stop_inference(job_id)  # Stop\n```\n\n---\n\n## HTTP API Direct Access\n\nYou can also call the HTTP API directly without the CLI:\n\n```bash\n# Login\ncurl -X POST https://robot.box2ai.com/api/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"xxx\",\"password\":\"xxx\"}'\n# → {\"token\": \"eyJ...\"}\n\n# List devices\ncurl https://robot.box2ai.com/api/devices \\\n  -H \"Authorization: Bearer <token>\"\n\n# Move servo\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/command \\\n  -H \"Authorization: Bearer <token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"id\":1,\"position\":2048,\"speed\":1000}'\n\n# Go home\ncurl -X POST https://robot.box2ai.com/api/device/B2R-xxx/go_home \\\n  -H \"Authorization: Bearer <token>\"\n```\n\nFull endpoint list: see `box2robot_server/box2robot-cli/box2robot.md`\n\n---\n\n## LLM Conversational Control (chat.py)\n\nControl the arm through natural language via Zhipu AI function calling — the LLM decides when to invoke APIs.\n\n```bash\ncd box2robot_audio\n\n# Chat only\npython chat.py\n\n# Chat + arm control\npython chat.py --robot\n\n# First time: login required\npython chat.py --robot --login <user> <password>\n\n# Specify device\npython chat.py --robot --device B2R-XXXXXXXXXXXX\n```\n\n### Available LLM Tools (9)\n\n| Tool | Parameters | Description |\n|------|------------|-------------|\n| `list_devices` | — | List all devices with online status and type |\n| `servo_status` | — | Current servo position/load/temp + torque state |\n| `move_servo` | servo_id, position, [speed] | Move single servo (ID 1-6, pos 0-4095) |\n| `go_home` | — | All servos return to calibrated center |\n| `set_torque` | enable:bool | Torque on (lock) / off (release) |\n| `record_start` | — | Start trajectory recording |\n| `record_stop` | — | Stop recording and save |\n| `list_trajectories` | — | List saved trajectories |\n| `play_trajectory` | traj_id | Play trajectory by ID |\n| `search_and_play` | keyword | Search by name and play (e.g., \"nod\", \"wave\") |\n\n### API Feedback Format\n\nEach tool returns structured feedback for the LLM to generate responses:\n\n**Success** — prefixed with `OK:`:\n```\nOK: servo 1 moving to 2500 at speed 500. status=queued\nOK: torque=ON, stale=False, age=353ms, 6 servos\n  ID1: position=2494, load=36, temp=35\n  ...\nOK: Returning home in 15 steps (max_delta=342)\nOK: Recording saved. id=abc123, frames=150, duration=5000ms\nOK: 4 devices | active=B2R-XXXX\n  White Follower | id=B2R-XXXX | type=arm | ONLINE\n  Black Leader   | id=B2R-YYYY | type=arm | OFFLINE\n```\n\n**Failure** — prefixed with `ERROR:`:\n```\nERROR: Device not connected              → Device offline, check power/WiFi\nERROR: No servo data (stale=True)        → Device just booted, retry in a few seconds\nERROR: Cannot disable torque while Virtual Serial is active → Close VSerial first\nERROR: Already recording                 → record_stop before starting new recording\nERROR: Not recording                     → No need to stop\nERROR: unauthorized                      → Token expired, re-login required\nERROR: No device selected                → Select a device first\nERROR: Invalid servo_id (0-253)          → Servo ID out of range\nERROR: Invalid position (0-4095)         → Position value out of range\nERROR: Trajectory not found              → Trajectory ID does not exist\nERROR: Cannot connect to server          → Network issue, check connection\nERROR: Request timeout                   → Server may be busy\n```\n\n### Multi-Device Handling\n\n- On startup, the first online arm device is auto-selected\n- If multiple arms are online, the user is prompted to choose\n- Users can specify via `--device B2R-XXXX`, or say \"switch to the black leader\" in conversation\n- `list_devices` output marks `active=` for the currently controlled device\n\n### LLM Behavior Rules\n\n1. **Before execution**: Confirm when user intent is ambiguous (\"Which servo do you want to move?\")\n2. **After execution**: Generate response based on feedback (success → report result, failure → explain + suggest)\n3. **Multi-step operations**: Recording requires releasing torque first (set_torque false → record_start)\n4. **Safety**: Never proactively execute dangerous operations in conversation (factory_reset, calibrate, etc.)\n5. **Small talk**: Conversations unrelated to the arm should not trigger any tool calls\n\nFile v1.0.5:pyproject.toml\n\n[project]\nname = \"box2robot-skills\"\nversion = \"0.2.0\"\ndescription = \"Standalone CLI for controlling Box2Robot robotic arms via server HTTP API\"\nreadme = \"README.md\"\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.8\"\ndependencies = [\n    \"aiohttp>=3.8\",\n]\n\n[project.scripts]\nb2r = \"b2r:main\"\n\n# ── Runtime configuration ─────────────────────────────────────────────\n# Environment variables (all optional):\n#   B2R_SERVER   Server URL          Default: https://robot.box2ai.com\n#   B2R_TOKEN    JWT bearer token    Overrides ~/.b2r_token\n#   B2R_DEVICE   Default device ID   Overrides auto-select\n#\n# Credential storage:\n#   ~/.b2r_token   JSON file created on login with mode 0600.\n#                  Contains {token, server, device}. Treat like an SSH key.\n\nArchive v1.0.4: 6 files, 13276 bytes\n\nFiles: b2r.py (10947b), pyproject.toml (853b), README.md (3067b), SKILL.md (7364b), SKILLS.md (13368b), _meta.json (135b)\n\nFile v1.0.4:SKILL.md\n\n---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories, calibrate, camera, voice, and orchestrate AI training/inference.\nversion: 0.2.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      env: [B2R_TOKEN]\n      bins: [python3]\n      anyBins: [python3, python]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories, calibrate, capture images, voice interaction, and orchestrate AI training/inference — all from a single CLI.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\n## CLI Quick Reference\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop and save\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Camera & Calibration\n```bash\nb2r.py snapshot                    # Camera snapshot\nb2r.py calibrate [servo_id]        # Auto-calibrate (0 = all)\n```\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camera_id] | Start recording (single/dual/phone) |\n| `record.stop` | device_id, [name] | Stop recording |\n| `trajectory.list` | device_id | List trajectories |\n| `trajectory.play` | device_id, trajectory_id | Play trajectory |\n| `trajectory.stop` | device_id, trajectory_id | Stop playback |\n| `trajectory.delete` | device_id, trajectory_id | Delete trajectory |\n\n### pairing (Leader-Follower)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `pairing.create` | leader_id, follower_ids, [use_espnow] | Create pairing |\n| `pairing.disconnect` | leader_id | Disconnect |\n| `pairing.connect` | leader_id | Reconnect |\n| `pairing.delete` | leader_id | Delete pairing |\n| `espnow.discover` | — | Scan nearby devices |\n\n### calibration\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `calibrate.auto` | device_id, [servo_id=0] | Auto-calibrate (0 = all) |\n| `calibrate.manual` | device_id, servos:[{id,min,max,mid}] | Manual calibration |\n| `calibrate.center_offset` | device_id | Write EEPROM center offset |\n\n### camera (Camera & Voice)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `camera.snapshot` | device_id | Take photo |\n| `camera.stream_mode` | device_id, mode | Stream mode (idle/preview/inference) |\n| `camera.voice_start` | device_id | Enable microphone |\n| `camera.voice_stop` | device_id | Disable microphone |\n| `camera.tts` | device_id, prompt | Text-to-speech |\n\n### training (Training & Inference)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `training.submit` | name, dataset_ids, [model_type] | Submit training job |\n| `training.list` | [page] | Job list |\n| `training.deploy` | job_id, arm_device_id, gpu_device_id | Deploy inference |\n| `training.stop_inference` | job_id | Stop inference |\n\n### store (Skill Store)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `store.list` | [search], [category] | Browse store |\n| `store.execute` | task_id, device_id | Execute skill |\n\n### config (Configuration)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `config.get` | device_id | View config |\n| `config.set` | device_id, params:{key:val} | \n\nArchive v1.0.3: 5 files, 10322 bytes\n\nFiles: b2r.py (10947b), pyproject.toml (853b), README.md (2976b), SKILLS.md (13368b), _meta.json (135b)\n\nArchive v1.0.2: 5 files, 11208 bytes\n\nFiles: b2r.py (11001b), pyproject.toml (1659b), README.md (2624b), SKILLS.md (13368b), _meta.json (135b)\n\nArchive v1.0.1: 4 files, 9990 bytes\n\nFiles: b2r.py (9873b), README.md (2624b), SKILLS.md (13368b), _meta.json (135b)\n\nArchive v1.0.0: 4 files, 9982 bytes\n\nFiles: b2r.py (9873b), README.md (1953b), SKILLS.md (12280b), _meta.json (135b)","readmeExcerpt":"Skill: box2robot-skills Owner: boxjod Summary: Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, download datasets, generate videos, and orchestrate AI training/... Tags: latest:1.0.9 Version history: v1.0.9 | 2026-05-05T04:01:48.832Z | user - Removed the ACT Skill Store commands and related documentation from the skill description. - Dropped the 'stream' camera command deta","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"login → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke"},{"language":"bash","snippet":"# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>"},{"language":"bash","snippet":"b2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, temperature"},{"language":"bash","snippet":"b2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (home varies per joint), Speed: 0-4000 (default 1000)"},{"language":"bash","snippet":"b2r.py record start                # Start recording (servo data only)\nb2r.py record start --cam CAM-xxx  # Record with camera (servo + images)\nb2r.py record stop [name]          # Stop and save\nb2r.py record status               # Current recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory"},{"language":"bash","snippet":"b2r.py snapshot                    # Request camera snapshot\nb2r.py frame [cam_id] [out.jpg]   # Download latest JPEG frame to local file"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: box2robot\ndescription: Control Box2Robot robotic arms via cloud API — move servos, record trajectories with camera, download datasets, generate videos, and orchestrate AI training/inference.\nversion: 0.7.0\nhomepage: https://robot.box2ai.com\nemoji: \"\\U0001F916\"\nmetadata:\n  openclaw:\n    requires:\n      anyBins: [python3, python]\n      config: [~/.b2r_token]\n    primaryEnv: B2R_TOKEN\n    install:\n      - kind: uv\n        package: aiohttp\n        bins: []\n---\n\n# Box2Robot — Robotic Arm Control Skill\n\nControl ESP32-based robotic arms through a cloud server API. Move servos, record trajectories with camera, download datasets, generate replay videos, and orchestrate AI training/inference — all from a single CLI (`b2r.py`).\n\n> **Official skill** published by the Box2Robot team (https://robot.box2ai.com).\n\n## Safety & Supervision\n\n> **This skill controls physical robotic hardware and camera/microphone peripherals.**\n>\n> - **Human supervision required**: Do NOT run autonomously without operator oversight. Servo torque and motion commands cause physical movement that could injure people or damage objects.\n> - **Destructive operations** (`calibrate`) modify hardware state and require explicit user confirmation.\n> - **Privacy-sensitive operations** (`snapshot`, `frame`, `record start --cam`) access camera hardware — only invoke with user consent.\n> - **No OS shell access**: All operations are HTTP requests to `B2R_SERVER`. No arbitrary OS commands are executed. The only local subprocess is `ffmpeg` (optional, for video generation from downloaded JPEG frames).\n> - **Token sensitivity**: `~/.b2r_token` stores a JWT that grants device control. Created with mode 0600 (owner-only). Treat like an SSH key. Delete when no longer needed.\n\n## Credential Flow\n\n```\nlogin → POST /api/auth/login → JWT token\n  → saved to ~/.b2r_token (mode 0600, owner-only)\n  → all subsequent commands use this token automatically\n  → override with B2R_TOKEN env var\n  → delete ~/.b2r_token to revoke\n```\n\nAll network calls go exclusively to `B2R_SERVER` (default: `https://robot.box2ai.com`). No other endpoints are contacted.\n\n## Environment Variables\n\n| Variable | Required | Description | Default |\n|----------|----------|-------------|---------|\n| `B2R_SERVER` | No | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | No | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | No | Default device ID (overrides auto-select) | — |\n\nNone are strictly required at install time. The `login` command handles authentication interactively and persists the token to `~/.b2r_token`. `B2R_TOKEN` is the primary credential variable and can be set to skip interactive login.\n\n## Setup\n\n```bash\n# Install dependency\npip install aiohttp\n\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n```\n\n## Commands\n\n### Device & Status\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Servo positions, load, "},{"path":"README.md","content":"# Box2Robot Skills CLI\n\nStandalone single-file CLI for controlling Box2Robot robotic arms. Talks directly to the server HTTP API — no monorepo dependency required.\n\n## Install\n\n### Via ClawHub (recommended for AI agents)\n```bash\nclaw install box2robot\n```\n\n### Manual\n```bash\npip install aiohttp\n```\n\n## Quick Start\n\n```bash\n# Login (token saved to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# List devices\npython b2r.py devices\n\n# Control\npython b2r.py home                    # Go to home position\npython b2r.py move 1 2048             # Move servo #1 to position 2048\npython b2r.py move 1 2048 500         # With speed\npython b2r.py torque off              # Release torque\npython b2r.py status                  # Servo positions/load/temp\npython b2r.py snapshot                # Camera snapshot\npython b2r.py calibrate               # Auto-calibrate all servos\n```\n\n## Commands\n\n| Command | Description | Example |\n|---------|-------------|---------|\n| `login [user] [pass]` | Login and save token | `b2r.py login user pass` |\n| `devices` | List all devices | `b2r.py devices` |\n| `status [device_id]` | Servo status | `b2r.py status` |\n| `move <id> <pos> [speed]` | Move a servo | `b2r.py move 1 2048 500` |\n| `home` | Go to home position | `b2r.py home` |\n| `torque on/off` | Toggle torque | `b2r.py torque off` |\n| `record start/stop [name]` | Recording control | `b2r.py record start` |\n| `play [traj_id]` | Play trajectory (no args = list) | `b2r.py play` |\n| `snapshot [cam_id]` | Camera snapshot | `b2r.py snapshot` |\n| `calibrate [servo_id]` | Auto-calibrate (0 = all) | `b2r.py calibrate` |\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `B2R_SERVER` | Server URL | `https://robot.box2ai.com` |\n| `B2R_TOKEN` | JWT token (overrides ~/.b2r_token) | — |\n| `B2R_DEVICE` | Default device ID (overrides auto-select) | — |\n\n## Authentication & Credential Storage\n\n```\nlogin → POST /api/auth/login → JWT token → ~/.b2r_token (JSON)\n```\n\n- **`~/.b2r_token`** stores `{ \"token\": \"...\", \"server\": \"...\", \"device\": \"...\" }`.\n- Created with owner-only permissions (0600).\n- Subsequent commands read this file automatically — no repeated login.\n- Override with `B2R_TOKEN` env var. Delete file to revoke.\n\n> **Security note:** The token grants device control access. Treat it like an SSH key.\n\n## API Endpoints Used\n\n| Command | Method | Endpoint |\n|---------|--------|----------|\n| login | POST | `/api/auth/login` |\n| devices | GET | `/api/devices` |\n| status | GET | `/api/device/{id}/servos` |\n| move | POST | `/api/device/{id}/command` |\n| home | POST | `/api/device/{id}/go_home` |\n| torque | POST | `/api/device/{id}/torque` |\n| record | POST/GET | `/api/device/{id}/record/start\\|stop\\|status` |\n| play | GET/POST | `/api/device/{id}/trajectories`, `.../trajectory/{id}/play` |\n| snapshot | POST | `/api/camera/{id}/snapshot` |\n| calibrate | POST | `/api/device/{id}/calibrate` |\n\n## Documentation\n\n- `SKILLS.md` — AI Agen"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn777etwfpf26jc93enns6ewrd82jp3y\",\n  \"slug\": \"box2robot-skills\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1777953708832\n}"},{"path":"skill-card.md","content":"## Description:\n\nControl Box2Robot robotic arms through a cloud API for servo movement, camera-assisted trajectory recording, dataset download, video generation, and AI training or inference orchestration.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[boxjod](https://clawhub.ai/user/boxjod)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to let an agent operate Box2Robot robotic arms, collect camera-linked trajectory data, prepare datasets and videos, and coordinate training or deployed inference. It is intended for supervised hardware workflows where the operator approves motion, calibration, camera use, deployment, purchases, and third-party task execution.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Physical robot control can move servos, run calibration, and deploy inference in ways that may injure people or damage objects.\n\nMitigation: Use only with human supervision, a cleared workspace, trusted device selection, and explicit approval before motion, calibration, or inference deployment.\n\nRisk: The B2R_TOKEN environment variable and ~/.b2r_token file grant device-control access.\n\nMitigation: Treat the token as a sensitive credential, prefer HTTPS server settings, avoid command-line password exposure, and delete or rotate the token when no longer needed.\n\nRisk: Camera and microphone related operations can capture private information.\n\nMitigation: Run camera, microphone, recording, and dataset commands only with informed user consent and in an approved environment.\n\nRisk: Purchases and third-party task execution may have financial or operational consequences.\n\nMitigation: Require explicit human approval before purchases, store execution, or other third-party task actions.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/boxjod/skills/box2robot-skills)\n- [Box2Robot homepage](https://robot.box2ai.com)\n- [README.md](README.md)\n- [SKILLS.md](SKILLS.md)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, API calls, Files]\n\n**Output Format:** [Markdown guidance with CLI examples, configuration notes, and JSON-compatible API responses]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create or read local token, dataset, image, and video files during agent-directed Box2Robot workflows.]\n\n## Skill Version(s):\n\n1.0.9 (source: ClawHub release metadata; artifact frontmatter and pyproject.toml list 0.7.0)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."},{"path":"SKILLS.md","content":"# Box2Robot Skills — AI Agent Reference Manual\n\n> This document is for LLM Agents (Claude / GPT / CLAW etc.) to reference when orchestrating Box2Robot.\n> Agents control the robotic arm via the `b2r.py` CLI or HTTP API.\n\n---\n\n## Authentication\n\n```bash\n# Login (one-time, token cached to ~/.b2r_token)\npython b2r.py login <username> <password>\n\n# Or set environment variables directly\nexport B2R_SERVER=\"https://robot.box2ai.com\"\nexport B2R_TOKEN=\"<jwt_token>\"\nexport B2R_DEVICE=\"B2R-XXXXXXXXXXXX\"\n```\n\nOnce logged in, all commands carry the token automatically — no repeated authentication needed.\n\n---\n\n## CLI Quick Reference\n\n### Devices\n```bash\nb2r.py devices                     # List devices (* = online)\nb2r.py status                      # Current servo positions and torque\n```\n\n### Servo Control\n```bash\nb2r.py torque on                   # Lock servos\nb2r.py torque off                  # Release (allows manual dragging)\nb2r.py home                        # Safe return to home position\nb2r.py move <servo_id> <pos> [spd] # Move a single servo\n# Position: 0-4095 (center 2048), Speed: 0-4000 (default 1000)\n# 10 degrees ~ 114 counts\n```\n\n### Recording & Playback\n```bash\nb2r.py record start                # Start recording\nb2r.py record stop [name]          # Stop recording\nb2r.py record status               # Recording status\nb2r.py play                        # List all trajectories\nb2r.py play <traj_id>              # Play a trajectory\n```\n\n### Advanced\n```bash\nb2r.py exec <action> [params]      # Call any Action directly\nb2r.py say \"natural language\"      # Natural language execution\nb2r.py shell                       # Interactive shell\n```\n\n---\n\n## Action System (79 Actions)\n\nInvoke via `b2r.py exec <action_name>`. Parameters in JSON or key=value format.\n\n### device (Device Management)\n| Action | Description |\n|--------|-------------|\n| `device.list` | List devices |\n| `device.status` | Device status (control mode + servos) |\n| `device.bind` | Bind device (requires bind_code) |\n| `device.unbind` | Unbind device |\n| `device.rename` | Rename device |\n| `device.wifi_add` | Add WiFi network |\n| `device.wifi_clear` | Clear WiFi config |\n| `device.factory_reset` | Factory reset |\n\n### servo (Servo Control)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `servo.status` | device_id | Read position/load/temperature |\n| `servo.move` | device_id, servo_id, position, [speed] | Move single servo |\n| `servo.move_batch` | device_id, commands:[{id,position,speed}] | Batch move |\n| `servo.torque` | device_id, enable:bool | Toggle torque |\n| `servo.go_home` | device_id | Return to home position |\n| `servo.release_control` | device_id | Release current control |\n| `servo.control_mode` | device_id | Query control mode |\n| `servo.set_id` | device_id, old_id, new_id | Burn servo ID |\n\n### recording (Recording & Playback)\n| Action | Parameters | Description |\n|--------|------------|-------------|\n| `record.start` | device_id, [mode], [camer"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1419,"uniquenessScore":43,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T11:58:28.239Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:45:02.607Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}