| name | keentools-cloud |
| version | 0.1 |
| description | Unofficial CLI for KeenTools Cloud 3D Head Reconstruction API |
| disclaimer | Not affiliated with or endorsed by KeenTools. |
keentools-cloud Agent Skill
This file encodes invariants and usage patterns for AI agents.
Always read this file before using keentools-cloud in an automated pipeline.
Authentication
export KEENTOOLS_API_TOKEN=your_token_here
export KEENTOOLS_API_URL=https://your-api-endpoint.example.com
Or store the token once:
keentools-cloud auth login <token>
Invariants (ALWAYS follow these)
- Use
--dry-run before any mutating operation (init, process, ephemeral).
- Use
--output json in all automated pipelines. Non-TTY stdout defaults to JSON automatically.
- Use
status --poll to wait for completion — never manually loop status without --poll.
- Blendshapes are comma-separated, NOT repeated flags:
- Correct:
--blendshapes arkit,nose
- Wrong:
--blendshapes arkit --blendshapes nose ← causes API error
- OBJ format always returns a ZIP archive — save with
.zip extension, then extract.
- Ephemeral mode: after processing completes,
/info and /download return 404. Results are ONLY in the URLs you provided.
- Photos and upload URLs are matched by position — order matters.
- Photo count must be 2–15 for both standard and ephemeral pipelines.
| KEENTOOLS_API_TOKEN | API authentication token |
| KEENTOOLS_API_URL | API base URL (required) |
Simple Pipeline (use run for single-command convenience)
keentools-cloud run photo1.jpg photo2.jpg photo3.jpg \
--output-path head.glb \
--blendshapes arkit,nose \
--texture jpg
Step-by-Step Pipeline (for more control)
INIT=$(keentools-cloud init --count 3 --output json)
AVATAR_ID=$(echo "$INIT" | jq -r .avatar_id)
URLS=$(echo "$INIT" | jq -r '.upload_urls[]')
keentools-cloud upload \
--avatar-id "$AVATAR_ID" \
--urls "$(echo "$INIT" | jq -r '.upload_urls | join(",")')" \
photo1.jpg photo2.jpg photo3.jpg
keentools-cloud process \
--avatar-id "$AVATAR_ID" \
--focal-length-type estimate-per-image
keentools-cloud status --avatar-id "$AVATAR_ID" --poll
keentools-cloud download \
--avatar-id "$AVATAR_ID" \
--output-path head.glb \
--format glb \
--blendshapes arkit,nose \
--texture jpg \
--poll
Ephemeral Pipeline (zero data retention)
keentools-cloud ephemeral \
--image-url https://your-bucket.s3.amazonaws.com/photo1.jpg \
--image-url https://your-bucket.s3.amazonaws.com/photo2.jpg \
--result-url glb:https://your-bucket.s3.amazonaws.com/result.glb?<presigned-put-params> \
--focal-length-type estimate-per-image \
--callback-url https://your-server.com/webhook
keentools-cloud status --avatar-id <ID> --poll --output json
Schema Introspection (for agents)
keentools-cloud schema
keentools-cloud schema download
keentools-cloud schema run
Output Formats
| Scenario | Format |
|---|
| TTY (terminal) | Human-readable with colors |
| Non-TTY (pipe/redirect) | JSON (auto-detected) |
| Forced JSON | --output json |
JSON output is NDJSON (one object per line) when streaming multiple events (e.g., during run).
Focal Length Reference
| Mode | When to use |
|---|
estimate-per-image | Mixed cameras, different zoom levels (safe default) |
estimate-common | Same camera/lens, all same resolution |
manual | Known exact values; provide --focal-lengths 24,28,35 |
Typical smartphone 35mm-equivalent: 24–28mm.
Error Exit Codes
| Code | Meaning |
|---|
| 0 | Success |
| 1 | API or runtime error |
| 2 | Input validation error (bad avatar ID, path traversal, etc.) |
| 3 | Authentication error |