Skill: hitpawdev/hitpaw-image-enhancer (version 1.0.4)

--- name: hitpaw-image-enhancer description: Enhance images and videos using HitPaw's AI enhancement API version: "1.0.3" author: Nova (HitPaw-Official) type: cli entry: dist/cli.js repository: https://github.com/HitPaw-Official/openclaw-skill-hitpaw-enhancer keywords: - image - video - enhancement - upscale - hitpaw - ai license: MIT capabilities: - image_enhancement - image_upscaling - photo_enhance - video_enhancement - video_upscaling requirements: node: ">=18" packages: - axios - commander - ora - chalk - fs-extra environment: variables: - name: HITPAW_API_KEY description: Your HitPaw API key required: true --- # HitPaw Image & Video Enhancer Skill A powerful OpenClaw skill that integrates HitPaw's state-of-the-art AI enhancement technology for both **images** and **videos**. Enhance, upscale, restore, and denoise with multiple AI models. --- ## 🎯 Features Based on the official [HitPaw API Documentation](https://developer.hitpaw.com/image/Introduction), this skill leverages industrial-grade AI models developed in-house by HitPaw's expert R&D team. ### Core Strengths - **Quality**: Industry-defining quality fit for professional use cases, from commercial photography to archival restoration - **Fidelity**: Preserves the original details and identities in the source images, ensuring the output remains true to the input - **Efficiency**: Optimized for low latency and high throughput, capable of processing distinct enhancement tasks at scale --- ## 📸 Image Enhancement According to the [Image API Introduction](https://developer.hitpaw.com/image/Introduction), our image processing services offer world-class capabilities designed to handle a wide variety of restoration scenarios: ### Key Capabilities - **Upscale**: Output high-resolution images from low-resolution input files using standard or high-fidelity models - **Face Recovery**: Ensure high-quality facial details, offering both "Clear" (soft/beauty) and "Natural" (textured/realistic) restoration options - **Sharpen & Denoise**: Bring images into focus by removing blur and sensor noise while preserving the original structure - **Generative Restoration**: Leverage Diffusion technology to reconstruct details in severely degraded portraits or general images ### Model Classes The Image API offers two classes of AI models to suit different needs: - **Standard Models**: Fast and efficient, prioritizing preserving original fidelity and details. Recommended for most professional and general restoration use cases - **Generative Models**: Utilize Stable Diffusion to produce the highest quality outputs, capable of "imagining" missing details. Ideal for extremely low-quality inputs where traditional upscaling fails #### Standard Models As detailed in the [Available Models](https://developer.hitpaw.com/image/available-models) documentation: | Model | Multiplier | Description | Best For | |-------|------------|-------------|----------| | `general_2x` / `general_4x` | 2x / 4x | General Enhance Model | General photos, landscapes | | `face_2x` / `face_4x` | 2x / 4x | Portrait Model (Clear) | Soft/beauty style portrait enhancement | | `face_v2_2x` / `face_v2_4x` | 2x / 4x | Portrait Model (Natural) | Natural/realistic portrait enhancement | | `high_fidelity_2x` / `high_fidelity_4x` | 2x / 4x | High Fidelity Model | Professional photography, conservatively upscaling high-quality sources | | `sharpen_denoise_1x` | 1x | Sharp Denoise Model | Aggressive denoising with sharpening | | `detail_denoise_1x` | 1x | Detail Denoise Model | Gentle denoising with texture preservation | #### Generative Models Powered by Stable Diffusion technology: | Model | Multiplier | Description | Best For | |-------|------------|-------------|----------| | `generative_portrait_1x/2x/4x` | 1x/2x/4x | Generative Portrait Model | Extremely low-quality portraits, "re-imagines" details | | `generative_1x/2x/4x` | 1x/2x/4x | Generative Enhance Model | Heavily compressed or very low-resolution general images | **Technical Highlights**: - Generative models excel at texture generation and sharpening - They can fill in missing details that traditional upscalers cannot recover - Ideal for restoration tasks where source data is severely degraded #### Example Image Use Cases ```bash # General photo upscaling (landscape, architecture) enhance-image -u landscape.jpg -m general_4x -o hd_landscape.jpg # Portrait beautification (soft skin) enhance-image -u selfie.jpg -m face_4x -o portrait_beautified.jpg # Professional archival restoration (natural look) enhance-image -u old_photo.png -m face_v2_2x -o restored.png --keep-exif # Denoise grainy low-light photo enhance-image -u night_photo.jpg -m sharpen_denoise_1x -o clean.jpg # Generative reconstruction for severely degraded image enhance-image -u blurry_face.jpg -m generative_portrait_2x -o ai_face.jpg ``` --- ## 🎬 Video Enhancement According to the [Video API Introduction](https://developer.hitpaw.com/video/Introduction), our video processing services provide industrial-grade solutions for restoring and upscaling video content: ### Key Capabilities - **Video Upscale**: Convert SD or HD footage to 4K Ultra HD clarity using deep convolution and feature learning technologies - **Portrait Restoration**: Specialized models to detect, stabilize, and enhance faces in video streams, removing motion blur and noise while maintaining identity - **General Restoration**: A comprehensive solution based on GAN technology to de-noise, de-blur, and enhance details in general video content - **Generative Reconstruction**: Utilizing Stable Diffusion for video to reconstruct textures and details in extremely low-quality footage ### Core Pillars - **Temporal Stability**: Unlike image-only models, our video engines ensure smooth transitions between frames, eliminating flickering and jitter - **Clarity**: Recovering fine details and removing compression artifacts common in streaming or legacy media - **Performance**: Optimized inference times to handle heavy video processing workloads efficiently ### Model Classes - **Restoration & Upscale (Standard)**: Models like Ultra HD and General Restore focus on cleaning up the footage and increasing resolution without altering the fundamental content. They rely on pixel-perfect accuracy and temporal consistency - **Generative Video**: Uses advanced logic-based reconstruction. Designed for "impossible" restoration tasks where the source video lacks sufficient data, generating realistic textures and details to fill the gaps #### Available Video Models From the [Video Models Documentation](https://developer.hitpaw.com/video/available-models): | Model | Description | Use Case | |-------|-------------|----------| | `ultrahd_restore_2x` | Ultra HD Model | High-definition upscale; natural-looking 1080p→4K | | `general_restore_1x` / `2x` / `4x` | General Restore Model | General video restoration, de-noising, de-blurring | | `portrait_restore_1x` / `2x` | Portrait Restore Model | Multi-face restoration with temporal stability | | `face_soft_2x` | Video Face Soft Model | Facial beautification with consistent appearance | | `generative_1x` | Generative Video Model | Extreme restoration of heavily degraded footage | **Technical Highlights**: - Generates realistic textures and eliminates flickering via multi-frame SD architecture - Handles heavy compression, high-ISO noise, and complex motion blur - Maintains identity consistency across frames #### Example Video Use Cases ```bash # Convert old 720p footage to 4K enhance-video -u old_clip.mp4 -m ultrahd_restore_2x -r 3840x2160 -o 4k_remastered.mp4 # Restore grainy, noisy home video enhance-video -u home_movie.avi -m general_restore_2x -r 1920x1080 -o cleaned.mp4 # Beautify faces in vlog/interview enhance-video -u interview.mp4 -m face_soft_2x -r 1920x1080 -o soft_faces.mp4 # Stabilize and restore old family footage with multiple faces enhance-video -u family_reunion.mov -m portrait_restore_2x -r 1920x1080 -o restored.mp4 # Generative AI restoration for severely degraded source enhance-video -u heavily_compressed.mp4 -m generative_1x -r 1920x1080 -o regenerated.mp4 ``` --- ## 🚀 Why Choose HitPaw API? **Industry-Leading Quality**: Professional-grade output suitable for commercial photography, archival restoration, and broadcast-quality video remastering **Unparalleled Fidelity**: Strictly retains original details and subject identity, ensuring outputs remain true to inputs **Comprehensive Model Catalog**: 16 specialized models covering virtually every restoration scenario **Scalable Performance**: Optimized for low-latency, high-throughput workloads --- ## 📊 Quick Reference ### Image Model Selection Guide | Scenario | Recommended Model | |----------|-------------------| | General photo upscale | `general_2x` or `general_4x` | | Portrait beautification | `face_2x` or `face_4x` | | Portrait natural look | `face_v2_2x` or `face_v2_4x` | | Professional archival | `high_fidelity_2x` / `high_fidelity_4x` | | Grainy low-light | `sharpen_denoise_1x` | | Subtle denoise | `detail_denoise_1x` | | Severely degraded | `generative_portrait_*` or `generative_*` | ### Video Model Selection Guide | Scenario | Recommended Model | |----------|-------------------| | SD → 4K upscale | `ultrahd_restore_2x` | | General cleanup | `general_restore_2x` | | Interview/vlog beautification | `face_soft_2x` | | Old home movies (multiple faces) | `portrait_restore_2x` | | Severely compressed/ degraded | `generative_1x` | --- ## Installation ```bash clawhub install hitpaw-image-enhancer ``` ## Configuration Set your HitPaw API key: ```bash export HITPAW_API_KEY="your_api_key_here" ``` Or create a `.env` file in your OpenClaw workspace: ``` HITPAW_API_KEY=your_api_key_here ``` Get your API key at: https://playground.hitpaw.com/ Test the API directly in the browser: [HitPaw Playground →](https://playground.hitpaw.com/) --- ## 📸 Examples & Gallery > **Note**: All screenshots below are from official HitPaw website (hitpaw.com), showcasing real enhancement results. Place additional examples in the `images/` folder. ### Image Enhancement Examples #### General Upscale (2x/4x) From official HitPaw documentation: | Before | After | |--------|-------| | ![Before](images/image-official-before.jpg) | ![After](images/image-official-after.jpg) | *Demonstrates general image enhancement and upscaling capabilities* **Use case**: Landscape photos, architecture, general photography #### Unblur / Motion Blur Removal | Before | After | |--------|-------| | ![Blurry](images/unblur-official-before.jpg) | ![Sharp](images/unblur-official-after.jpg) | *Shows blur removal and sharpening effects* **Use case**: Action shots, low-light photos, camera shake recovery --- ## 🎬 Video Enhancement Gallery > Video screenshots coming soon. Currently using placeholder references. | Scenario | Original Frame | Enhanced Frame | |----------|----------------|----------------| | General Upscale (1080p → 4K) | ![Original](images/video-original.jpg) | ![Enhanced](images/video-enhanced.jpg) | | Portrait Restoration | ![Before](images/portrait-video-before.jpg) | ![After](images/portrait-video-after.jpg) | | Denoise & Cleanup | ![Noisy](images/video-noisy.jpg) | ![Clean](images/video-clean.jpg) | See [images/README.md](images/README.md) for screenshot guidelines and recommended sources. --- ## 📊 Model Comparison Examples ### Face Enhancement: Clear vs Natural | Clear (Soft/Beauty) | Natural (Realistic) | |---------------------|---------------------| | ![Face Clear](images/face-clear.jpg) | ![Face Natural](images/face-natural.jpg) | - **Face Clear Model**: Soft skin, beautification style - **Face Natural Model**: Preserves natural texture and pores ### Generative vs Standard Models | Standard (general_4x) | Generative (generative_portrait_2x) | |-----------------------|------------------------------------| | ![Standard](images/general-upscale.jpg) | ![Generative](images/generative-portrait.jpg) | - **Standard models**: Fast, preserves original details - **Generative models**: Reconstructs missing details, ideal for severely degraded inputs --- # IMAGE COMMAND ## Usage: `enhance-image` ### Command Line Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `--url`, `-u` | string | **required** | URL of the image to enhance | | `--output`, `-o` | string | `output.jpg` | Output file path | | `--model`, `-m` | string | `general_2x` | Image model (see below) | | `--extension`, `-e` | string | `.jpg` | Output extension (`.jpg`, `.png`, `.webp`) | | `--dpi` | number | original | Target DPI for metadata | | `--keep-exif` | boolean | false | Preserve EXIF data from original | | `--poll-interval` | number | 5 | Polling interval in seconds | | `--timeout` | number | 300 | Maximum wait time in seconds | #### Available Image Models | Model | Multiplier | Best For | DPI Support | |-------|------------|----------|-------------| | `general_2x` / `general_4x` | 2x / 4x | General photos, landscapes | ✅ | | `face_2x` / `face_4x` | 2x / 4x | Portrait & face enhancement | ✅ | | `face_v2_2x` / `face_v2_4x` | 2x / 4x | Improved face model | ✅ | | `high_fidelity_2x` / `high_fidelity_4x` | 2x / 4x | High quality preservation | ✅ | | `sharpen_denoise_1x` | 1x | Denoise & sharpen | ✅ | | `detail_denoise_1x` | 1x | Detail preservation | ✅ | | `generative_1x/2x/4x` | — | Generative Enhance Model | ❌ | #### Examples ```bash # Simple 2x upscale with general model enhance-image -u photo.jpg -o enhanced.jpg -m general_2x # Face enhancement 4x enhance-image -u portrait.jpg -m face_4x -o portrait_4x.jpg --keep-exif # High fidelity with custom DPI enhance-image -u old-photo.png -m high_fidelity_2x -dpi 300 -o hd.png # Batch processing for img in *.jpg; do enhance-image -u "$img" -o "upscaled/$img" -m general_4x done ``` --- # VIDEO COMMAND ## Usage: `enhance-video` ### ⚠️ Important Notes - **Resolution is required** (`--resolution` or `-r`). Must be in `WIDTHxHEIGHT` format (e.g., `1920x1080`). - Ensure target resolution does **not exceed max output resolution** (36 MP total pixels) per API limits. - Video processing can take **minutes to hours** depending on length. Use `--timeout` to extend if needed. - Input video must be at a **publicly accessible URL** (local files not directly supported). ### Command Line Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `--url`, `-u` | string | **required** | URL of the video to enhance | | `--output`, `-o` | string | `output.mp4` | Output file path | | `--model`, `-m` | string | `general_restore_2x` | Video model (see below) | | `--resolution`, `-r` | string | **required** | Target resolution in WxH (e.g., `1920x1080`) | | `--original-resolution` | string | — | Original resolution (e.g., `1280x720`) - optional | | `--extension`, `-e` | string | `.mp4` | Output extension (`.mp4`, `.mov`, `.avi`) | | `--fps` | number | — | Target FPS (preserves original if omitted) | | `--keep-audio` | boolean | true | Preserve audio track | | `--poll-interval` | number | 10 | Polling interval in seconds | | `--timeout` | number | 600 | Maximum wait time in seconds | #### Available Video Models | Model | Description | Use Case | |-------|-------------|----------| | `general_restore_1x` / `2x` / `4x` | General video restoration | General upscaling | | `face_soft_2x` | Face-softening enhancement | Portrait videos | | `portrait_restore_1x` / `2x` | Portrait restoration | Face-focused content | | `ultrahd_restore_2x` | Ultra HD upscaling | Highest quality upscale | | `generative_1x` | Generative fill | AI-powered restoration | #### Examples ```bash # Upscale to 1080p using general_restore_2x enhance-video -u input.mp4 -o output_1080p.mp4 -m general_restore_2x -r 1920x1080 # Upscale to 4K with specific original resolution enhance-video -u clip.mov -o 4k.mov -m general_restore_4x -r 3840x2160 --original-resolution 1920x1080 # Denoise with portrait model enhance-video -u portrait_video.avi -m portrait_restore_2x -r 1920x1080 -o clean_portrait.mp4 # Add color to B&W (if generative model supports) enhance-video -u bw_vintage.mp4 -m generative_1x -r 1920x1080 -o colorized.mp4 ``` --- ## Coin Consumption ### Image Enhancement - 2x/4x models: **~75 coins** per image - 1x models: **~50 coins** per image - Generative models: **~100+ coins** per image ### Video Enhancement Coin costs depend on video length, model, and resolution. Approximate rates: - Upscale models: **~200-400 coins** per minute - Restoration models: **~150-300 coins** per minute **Always check current rates** at: https://playground.hitpaw.com/ --- ## Error Handling Common errors and solutions: | Error | Cause | Fix | |-------|-------|-----| | `Invalid API key` | Wrong or expired key | Update `HITPAW_API_KEY` | | `Insufficient coins` | Account balance too low | Top up at HitPaw Playground | | `Unsupported model` | Model name typo or not available | Check model table above | | `Invalid extension` | Output format not supported | Use `.jpg/.png/.webp` for images; `.mp4/.mov/.avi` for videos | | `Invalid video URL` | URL not publicly accessible | Ensure video is reachable via HTTPS | | `Input/target resolution over limit` | Exceeds 36 MP total pixels (e.g., 7680x4320 = ~33 MP) | Reduce resolution | | `Video duration over limit` | Video longer than 1 hour | Trim video first | | `Rate limit exceeded` | Too many requests | Wait and retry with exponential backoff | | `Video processing failed` | Corrupt video or unsupported codec | Try different input format or re-encode | --- ## Error Codes The API returns structured error codes. Always check both HTTP status and the `error_code` field. ### General Errors | error_code | HTTP Status | Message | |------------|-------------|---------| | 100400000 | 400 | No access | | 100400001 | 400 | Invalid URL | | 100400002 | 400 | Bad Request | | 100401000 | 401 | Token is expired | | 100403000 | 403 | Invalid request parameters | | 100403001 | 403 | Access denied | | 100403002 | 403 | You don't have permission to access this resource | | 100429000 | 429 | Too many requests, please try again later | | 100500000 | 500 | Internal error | | 100500001 | 500 | Database error | | 100500002 | 500 | Cache error | | 100500003 | 500 | Failed to create file | | 100500004 | 500 | Signature verification failed | | 100500005 | 500 | Configuration error | | 100500006 | 500 | Unknown error | | 100500007 | 500 | Operation timeout | ### API-Specific Errors | error_code | HTTP Status | Message | |------------|-------------|---------| | 110400000 | 400 | api_key is not valid | | 110400002 | 400 | The task does not exist | | 110400003 | 400 | The task failed, please try again | | 110400005 | 400 | The model is not supported, please try again | | 110400007 | 400 | The extension is not valid | | 110400008 | 400 | The video URL is not valid | | 110400009 | 400 | The input resolution is over limit | | 110400010 | 400 | The target resolution is over limit | | 110400011 | 400 | The video duration is over limit | | 110402000 | 402 | The coins are not enough | | 110402001 | 402 | The coins are not enough | | 110402004 | 402 | The Demo try times exceeded | ### Error Response Format ```json { "error_code": 110400000, "message": "api_key is not valid" } ``` ## Rate Limiting - The API implements rate limiting to ensure fair usage - Error code `100429000` will be returned if you exceed the rate limit - Implement **exponential backoff** in your retry logic ## Best Practices ### Polling for Job Status - Poll the `/api/task-status` endpoint at reasonable intervals (recommended: every 5–10 seconds) - Implement exponential backoff for failed requests - Set a maximum number of polling attempts to avoid infinite loops - Check status values: `CONVERTING` (keep polling), `COMPLETED` (success), `ERROR` (failed) ### Error Handling - Always check the HTTP status code AND `error_code` in the response - Implement retry logic for transient errors (5xx errors) - Do NOT retry 4xx client errors without first fixing the request - Log error responses for debugging ### Resource Limits - **Images**: Max input resolution 70 MP (Enhancement/Denoise) / 34 MP (Generative); Max output 432 MP (Enhancement/Denoise) - **Videos**: Max output 36 MP total pixels; Duration 0.5s – 1 hour - Verify file extension validity before submitting ### API Key Management - Store API keys securely using environment variables - Never commit API keys to version control - Rotate API keys periodically ### File URL Requirements - Ensure image and video URLs are publicly accessible - Use HTTPS URLs - Use **Pre-sign OSS Upload** for unreliable URLs: https://developer.hitpaw.com/common/oss-presign-put-api ## Technical Details ### API Compatibility This skill implements the official HitPaw API as documented: - **Base URL**: `https://api-base.hitpaw.com` - **Image endpoint**: `POST /api/photo-enhancer` - **Video endpoint**: `POST /api/video-enhancer` - **Status endpoint**: `POST /api/task-status` Both endpoints return a `job_id`. Use the status endpoint to poll until `COMPLETED`, then download from `res_url`. ### Polling Strategy - **Images**: default poll every 5s, timeout 300s (5 min) - **Videos**: default poll every 10s, timeout 600s (10 min) - Implement **exponential backoff** when encountering 5xx errors or rate limit (429) For longer videos, increase `--timeout` as needed (e.g., `--timeout 3600` for 1 hour). ### Resolution & Format Limits **Image Model Specs**: | Model Category | DPI Support | Max Input | Max Output | Formats | |----------------|-----------|----------|----------|--------| | Enhancement & Denoise (`general_*`, `face_*`, `high_fidelity_*`, `*_denoise_*`) | ✅ | 70 MP | 432 MP | bmp, jpeg, jpg, png, jfif, tga, tiff, webp, heif | | Generative (`generative_*`, `generative_portrait_*`) | ❌ | 34 MP | 34 MP | Same formats | **Video Model Specs**: | Property | Limit | |----------|-------| | Max Input Resolution | No limit | | Max Output | 36 MP total pixels | | Duration | 0.5 seconds – 1 hour | | Input Formats | dv, mlv, m2ts, m2t, m2v, nut, ser, 3g2, 3gp, asf, avi, divx, f4v, flv, h261, h263, m4v, mkv, mov, mp4, mpeg, mpeg4, mpg, mxf, ogv, rm, rmvb, webm, wmv, gif | | Output Formats | mp4, mov, mkv, m4v, avi, gif | **Examples**: 3840×2160 = 8.3 MP ✅, 7680×4320 = 33.2 MP ✅, 8192×4608 = 37.7 MP ❌ For videos, `resolution` is **required**. Choose based on your needs: - Keep original size? Set `resolution` to original dimensions (use `--original-resolution` for better quality). - Upscale? Multiply original width/height by factor (2x, 4x). - Downscale? Rare but possible; just specify smaller dimensions. ### Audio Preservation By default, `enhance-video` keeps the audio track (`--keep-audio`, default true). Use `--no-keep-audio` to strip audio. --- ## Support - Image API Docs: https://developer.hitpaw.com/image/API-reference - Video API Docs: https://developer.hitpaw.com/video/API-reference - Playground: https://playground.hitpaw.com/ - Contact: [REDACTED_SECRET] This skill is an **unofficial integration** with HitPaw API. You must have a valid API key and comply with HitPaw's terms. The skill author is not responsible for any charges incurred. ## License MIT © HitPaw-Official

path: DEV.md
content: # Development Guide

## Build

```bash
npm install
npm run build
```

This compiles TypeScript to JavaScript in the `dist/` directory.

## Test

### Images

```bash
export HITPAW_API_KEY=your_key
node dist/cli.js -u https://example.com/photo.jpg -o out.jpg -m general_2x
```

### Videos

```bash
export HITPAW_API_KEY=your_key
node dist/video-cli.js -u https://example.com/video.mp4 -o out.mp4 -m upscale_2x
```

## CLI Commands

- `enhance-image` - image enhancement (main entry)
- `enhance-video` - video enhancement

Options for both commands are similar; see `--help` for details.

## Publish to ClawHub

```bash
clawhub login
clawhub publish .
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make changes and ensure `npm run build` passes
4. Submit a Pull Request

---

## Notes

- Video API may not be fully documented; models listed based on HitPaw product pages.
- If API returns `Unsupported model`, check the official docs for latest model names.
- Video jobs can take a long time; adjust `--timeout` accordingly.
- Always test with a small video first to estimate coin consumption.

## Disclaimer

This skill integrates with HitPaw API. You must have a valid API key and comply with HitPaw's terms. The skill author is not responsible for any charges incurred.


path: README.md
content: # HitPaw Image & Video Enhancer

[![ClawHub](https://img.shields.io/badge/ClawHub-hitpaw--image--enhancer-blue)](https://clawhub.com/skill/hitpaw-image-enhancer)
[![GitHub](https://img.shields.io/badge/GitHub-HitPaw--Official%2Fopenclaw--skill--hitpaw--enhancer-181717?logo=github)](https://github.com/HitPaw-Official/openclaw-skill-hitpaw-enhancer)
[![Version](https://img.shields.io/badge/version-1.0.3-green)](https://clawhub.com/skill/hitpaw-image-enhancer)

> OpenClaw skill integrating HitPaw's AI-powered image and video enhancement API

## 🎯 Features

This skill brings HitPaw's professional-grade AI enhancement capabilities to OpenClaw, supporting 16 specialized models for images and videos.

### Core Capabilities

- **Image Enhancement**: 2x/4x upscaling, face recovery, denoise, generative restoration
- **Video Enhancement**: Upscale to 4K, portrait restoration, temporal stability
- **Async Processing**: Polling-based job tracking with real-time status
- **Coin Tracking**: Monitor API consumption per job

## 📸 Screenshots

<!-- Actual screenshots from HitPaw official website -->

### Image Enhancement Examples

| Before | After |
|--------|-------|
| ![Before Enhancement](images/image-official-before.jpg) | ![After Enhancement](images/image-official-after.jpg) |

*Official HitPaw enhancement example (source: hitpaw.com)*

### Unblur / Sharpen Examples

| Before | After |
|--------|-------|
| ![Before Unblur](images/unblur-official-before.jpg) | ![After Unblur](images/unblur-official-after.jpg) |

*Official HitPaw unblur example (source: hitpaw.com)*

### Video Enhancement Examples

| Original Frame | Enhanced Frame |
|----------------|----------------|
| Placeholder: add video frame comparison | Placeholder: add video frame comparison |

> **Note**: Video example placeholders. Replace with actual screenshots from HitPaw video enhancement results.

---

## 🎯 Feature Highlights

Based on official [HitPaw API Documentation](https://developer.hitpaw.com/image/Introduction):

- **Quality**: Industry-defining quality fit for professional use cases
- **Fidelity**: Preserves original details and identities
- **Efficiency**: Optimized for low latency and high throughput

### Supported Models

**Image Models** (16 total):
- General Enhance (2x/4x)
- Portrait Models (Clear/Natural)
- High Fidelity (2x/4x)
- Denoise & Sharpen models
- Generative Portrait & General

**Video Models**:
- Ultra HD Restore
- General Restore
- Portrait Restore
- Face Soft
- Generative Video

See [SKILL.md](SKILL.md) for complete model documentation and usage examples.

## 🚀 Quick Start

### Installation

```bash
clawhub install hitpaw-image-enhancer
```

### Configuration

Set your HitPaw API key:

```bash
export HITPAW_API_KEY="your_api_key_here"
```

Get your API key at: https://playground.hitpaw.com/

### Usage

```bash
# Enhance an image
enhance-image -u photo.jpg -m general_2x -o output.jpg

# Enhance a video
enhance-video -u input.mp4 -m general_restore_2x -r 1920x1080 -o output.mp4
```

## 📚 Documentation

For comprehensive documentation, including all model options, configuration details, and troubleshooting, see [SKILL.md](SKILL.md).

## 🔗 Links

- **ClawHub Skill**: https://clawhub.com/skill/hitpaw-image-enhancer
- **GitHub Repository**: https://github.com/HitPaw-Official/openclaw-skill-hitpaw-enhancer
- **HitPaw API Docs**: https://developer.hitpaw.com/
- **HitPaw Playground**: https://playground.hitpaw.com/

## 📄 License

MIT © HitPaw-Official


path: images/README.md
content: # Screenshots for HitPaw Skill

This folder should contain before/after comparison screenshots showcasing the HitPaw API enhancement capabilities.

## Recommended Screenshots

### Image Enhancement
- `image-before.jpg` - Original low-quality image sample
- `image-after.jpg` - Enhanced result using HitPaw API

### Video Enhancement
- `video-original.jpg` - Video frame before enhancement
- `video-enhanced.jpg` - Video frame after enhancement

### Model Comparisons (Optional)
- `face-clear.jpg` / `face-natural.jpg` - Portrait model comparison
- `general-upscale.jpg` - General upscaling example
- `generative-portrait.jpg` - Generative model output

## Sources for Screenshots

1. **HitPaw Playground**: https://playground.hitpaw.com/
   - Upload test images and capture before/after
   - Try different models and save comparison screenshots

2. **Official Documentation**:
   - https://developer.hitpaw.com/image/available-models
   - https://developer.hitpaw.com/video/available-models
   - (Check for example images in the docs)

## Image Requirements

- Format: JPG or PNG
- Resolution: 800px+ width recommended
- Include watermarks or labels showing "Before" and "After"
- Compress for web (keep under 500KB each)

## Usage in Documentation

The README.md and SKILL.md reference these images using relative paths:

```markdown
![Before Example](images/image-before.jpg)
```

Please add actual screenshot files before publishing the skill documentation publicly.


path: package-lock.json
content: {
  "name": "openclaw-skill-hitpaw-enhancer",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "openclaw-skill-hitpaw-enhancer",
      "version": "1.0.0",
      "license": "MIT",
      "dependencies": {
        "axios": "^1.6.0",
        "chalk": "^5.3.0",
        "commander": "^11.1.0",
        "fs-extra": "^11.2.0",
        "ora": "^8.0.1"
      },
      "bin": {
        "enhance-image": "dist/cli.js",
        "enhance-video": "dist/video-cli.js"
      },
      "devDependencies": {
        "@types/fs-extra": "^11.0.4",
        "@types/node": "^20.0.0",
        "typescript": "^5.3.0"
      }
    },
    "node_modules/@types/fs-extra": {
      "version": "11.0.4",
      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/jsonfile": "*",
        "@types/node": "*"
      }
    },
    "node_modules/@types/jsonfile": {
      "version": "6.1.4",
      "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/@types/node": {
      "version": "20.19.35",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.35.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "undici-types": "~6.21.0"
      }
    },
    "node_modules/ansi-regex": {
      "version": "6.2.2",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
      }
    },
    "node_modules/asynckit": {
      "version": "0.4.0",
      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/axios": {
      "version": "1.13.6",
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "follow-redirects": "^1.15.11",
        "form-data": "^4.0.5",
        "proxy-from-env": "^1.1.0"
      }
    },
    "node_modules/call-bind-apply-helpers": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/chalk": {
      "version": "5.6.2",
      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": "^12.17.0 || ^14.13 || >=16.0.0"
      },
      "funding": {
        "url": "https://github.com/chalk/chalk?sponsor=1"
      }
    },
    "node_modules/cli-cursor": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "restore-cursor": "^5.0.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/cli-spinners": {
      "version": "2.9.2",
      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=6"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/combined-stream": {
      "version": "1.0.8",
      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "delayed-stream": "~1.0.0"
      },
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/commander": {
      "version": "11.1.0",
      "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=16"
      }
    },
    "node_modules/delayed-stream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=0.4.0"
      }
    },
    "node_modules/dunder-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.1",
        "es-errors": "^1.3.0",
        "gopd": "^1.2.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/emoji-regex": {
      "version": "10.6.0",
      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/es-define-property": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-errors": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-object-atoms": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-set-tostringtag": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "get-intrinsic": "^1.2.6",
        "has-tostringtag": "^1.0.2",
        "hasown": "^2.0.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/follow-redirects": {
      "version": "1.15.11",
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
      "integrity": [REDACTED_SECRET],
      "funding": [
        {
          "type": "individual",
          "url": "https://github.com/sponsors/RubenVerborgh"
        }
      ],
      "license": "MIT",
      "engines": {
        "node": ">=4.0"
      },
      "peerDependenciesMeta": {
        "debug": {
          "optional": true
        }
      }
    },
    "node_modules/form-data": {
      "version": "4.0.5",
      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "asynckit": "^0.4.0",
        "combined-stream": "^1.0.8",
        "es-set-tostringtag": "^2.1.0",
        "hasown": "^2.0.2",
        "mime-types": "^2.1.12"
      },
      "engines": {
        "node": ">= 6"
      }
    },
    "node_modules/fs-extra": {
      "version": "11.3.4",
      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "graceful-fs": "^4.2.0",
        "jsonfile": "^6.0.1",
        "universalify": "^2.0.0"
      },
      "engines": {
        "node": ">=14.14"
      }
    },
    "node_modules/function-bind": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-east-asian-width": {
      "version": "1.5.0",
      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/get-intrinsic": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.2",
        "es-define-property": "^1.0.1",
        "es-errors": "^1.3.0",
        "es-object-atoms": "^1.1.1",
        "function-bind": "^1.1.2",
        "get-proto": "^1.0.1",
        "gopd": "^1.2.0",
        "has-symbols": "^1.1.0",
        "hasown": "^2.0.2",
        "math-intrinsics": "^1.1.0"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "dunder-proto": "^1.0.1",
        "es-object-atoms": "^1.0.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/gopd": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/graceful-fs": {
      "version": "4.2.11",
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "ISC"
    },
    "node_modules/has-symbols": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/has-tostringtag": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "has-symbols": "^1.0.3"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/hasown": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/is-interactive": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/is-unicode-supported": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/jsonfile": {
      "version": "6.2.0",
      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "universalify": "^2.0.0"
      },
      "optionalDependencies": {
        "graceful-fs": "^4.1.6"
      }
    },
    "node_modules/log-symbols": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "chalk": "^5.3.0",
        "is-unicode-supported": "^1.3.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/log-symbols/node_modules/is-unicode-supported": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/math-intrinsics": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/mime-db": {
      "version": "1.52.0",
      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/mime-types": {
      "version": "2.1.35",
      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "mime-db": "1.52.0"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/mimic-function": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/onetime": {
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "mimic-function": "^5.0.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/ora": {
      "version": "8.2.0",
      "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "chalk": "^5.3.0",
        "cli-cursor": "^5.0.0",
        "cli-spinners": "^2.9.2",
        "is-interactive": "^2.0.0",
        "is-unicode-supported": "^2.0.0",
        "log-symbols": "^6.0.0",
        "stdin-discarder": "^0.2.2",
        "string-width": "^7.2.0",
        "strip-ansi": "^7.1.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/proxy-from-env": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/restore-cursor": {
      "version": "5.1.0",
      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "onetime": "^7.0.0",
        "signal-exit": "^4.1.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/signal-exit": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "ISC",
      "engines": {
        "node": ">=14"
      },
      "funding": {
        "url": "https://github.com/sponsors/isaacs"
      }
    },
    "node_modules/stdin-discarder": {
      "version": "0.2.2",
      "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/string-width": {
      "version": "7.2.0",
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "emoji-regex": "^10.3.0",
        "get-east-asian-width": "^1.0.0",
        "strip-ansi": "^7.1.0"
      },
      "engines": {
        "node": ">=18"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/strip-ansi": {
      "version": "7.2.0",
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "ansi-regex": "^6.2.2"
      },
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
      }
    },
    "node_modules/typescript": {
      "version": "5.9.3",
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "Apache-2.0",
      "bin": {
        "tsc": "bin/tsc",
        "tsserver": "bin/tsserver"
      },
      "engines": {
        "node": ">=14.17"
      }
    },
    "node_modules/undici-types": {
      "version": "6.21.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/universalify": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 10.0.0"
      }
    }
  }
}


path: package.json
content: {
  "name": "openclaw-skill-hitpaw-enhancer",
  "version": "1.0.3",
  "description": "HitPaw Image & Video Enhancement Skill for OpenClaw",
  "main": "dist/cli.js",
  "type": "module",
  "bin": {
    "enhance-image": "./dist/cli.js",
    "enhance-video": "./dist/video-cli.js"
  },
  "scripts": {
    "build": "tsc && cp src/cli.js dist/ && cp src/video-cli.js dist/",
    "prepublishOnly": "npm run build",
    "start": "node dist/cli.js",
    "start:video": "node dist/video-cli.js"
  },
  "keywords": [
    "openclaw",
    "skill",
    "hitpaw",
    "image",
    "video",
    "enhancement",
    "upscale",
    "ai"
  ],
  "author": "Nova",
  "license": "MIT",
  "dependencies": {
    "axios": "^1.6.0",
    "chalk": "^5.3.0",
    "commander": "^11.1.0",
    "fs-extra": "^11.2.0",
    "ora": "^8.0.1"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "@types/fs-extra": "^11.0.4",
    "typescript": "^5.3.0"
  }
}

path: src/cli.js
content: #!/usr/bin/env node
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import path from 'path';
import fs from 'fs-extra';
import { HitPawClient } from './client.js';

const program = new Command();

program
  .name('enhance-image')
  .description('Enhance images using HitPaw AI API')
  .version('1.0.0');

program
  .option('-u, --url <url>', 'URL or local file path of the image')
  .option('-o, --output <path>', 'Output file path', 'output.jpg')
  .option('-m, --model <model>', 'Enhancement model', 'general_2x')
  .option('-e, --extension <ext>', 'Output extension (e.g., .jpg, .png)', '.jpg')
  .option('--dpi <number>', 'Target DPI for metadata')
  .option('--keep-exif', 'Preserve EXIF data', false)
  .option('--poll-interval <seconds>', 'Polling interval', '5')
  .option('--timeout <seconds>', 'Max wait time', '300')
  .requiredOption('-u, --url <url>', 'Image URL or local file path')
  .action(async (options) => {
    const [REDACTED_SECRET];
    if (!apiKey) {
      console.error(chalk.red('Error: HITPAW_API_KEY environment variable is not set'));
      console.log(chalk.yellow('Set it with: export HITPAW_API_KEY="your_key"'));
      process.exit(1);
    }

    const { url, output, model, extension, dpi, keepExif, pollInterval, timeout } = options;

    // Handle local file: need to convert to public URL (not supported in this skill)
    // For now, only support URLs. For local files, user must upload somewhere first.
    if (fs.existsSync(url)) {
      console.error(chalk.red('Error: Local file paths are not directly supported.'));
      console.log(chalk.yellow('Please upload the image to a public URL first, or use a service like transfer.sh'));
      process.exit(1);
    }

    const client = new HitPawClient(apiKey);

    const spinner = ora('Enhancing image...').start();

    try {
      const result = await client.enhanceAndDownload(url, output, {
        model,
        extension,
        exif: keepExif,
        dpi: dpi ? parseInt(dpi) : undefined,
        pollInterval: parseInt(pollInterval),
        timeout: parseInt(timeout)
      });

      spinner.succeed(chalk.green('Enhancement complete!'));
      console.log(`Output: ${path.resolve(output)}`);
      console.log(`Coins consumed: ${result.coins}`);
    } catch (error) {
      spinner.fail(chalk.red('Enhancement failed'));
      console.error(chalk.red(error.message || 'Unknown error'));

      // Parse error response
      if (error.response?.data?.error_code) {
        const code = error.response.data.error_code;
        const messages = {
          110400000: 'Invalid API key',
          110402000: 'Insufficient coins',
          110400005: 'Unsupported model',
          110400007: 'Invalid extension',
          100429000: 'Rate limit exceeded'
        };
        console.error(chalk.yellow(`Error code ${code}: ${messages[code] || 'See documentation'}`));
      }

      process.exit(1);
    }
  });

program.parse();


path: src/client.ts
content: import axios, { AxiosInstance } from 'axios';
import fs from 'fs-extra';
import path from 'path';

export interface EnhanceRequest {
  model_name: string;
  img_url: string;
  extension: string;
  exif?: boolean;
  DPI?: number;
}

export interface VideoEnhanceRequest {
  model_name: string;
  video_url: string;
  resolution: [number, number];
  extension: string;
  original_resolution?: [number, number];
}

export interface EnhanceResponse {
  code: number;
  message: string;
  data: {
    job_id: string;
    consume_coins: number;
  };
}

export interface StatusResponse {
  code: number;
  message: string;
  data: {
    job_id: string;
    status: 'CONVERTING' | 'COMPLETED' | 'ERROR';
    res_url?: string;
    original_url?: string;
  };
}

export class HitPawClient {
  private client: AxiosInstance;
  private [REDACTED_SECRET];
  private baseUrl: string;

  constructor([REDACTED_SECRET], baseUrl = 'https://api-base.hitpaw.com') {
    this.[REDACTED_SECRET];
    this.baseUrl = baseUrl;
    this.client = axios.create({
      baseURL: baseUrl,
      headers: {
        'Content-Type': 'application/json',
        'Apikey': apiKey
      },
      timeout: 60000
    });
  }

  // === PHOTO ENHANCEMENT ===
  async enhancePhoto(params: EnhanceRequest): Promise<EnhanceResponse> {
    const response = await this.client.post('/api/photo-enhancer', params);
    return response.data as EnhanceResponse;
  }

  // === VIDEO ENHANCEMENT ===
  async enhanceVideo(params: VideoEnhanceRequest): Promise<EnhanceResponse> {
    const response = await this.client.post('/api/video-enhancer', params);
    return response.data as EnhanceResponse;
  }

  // === COMMON ===
  async checkStatus(jobId: string): Promise<StatusResponse> {
    const response = await this.client.post<StatusResponse>('/api/task-status', { job_id: jobId });
    return response.data;
  }

  async waitForCompletion(jobId: string, maxAttempts = 360, pollInterval = 10): Promise<StatusResponse> {
    let attempt = 0;
    while (attempt < maxAttempts) {
      const status = await this.checkStatus(jobId);
      console.log(`[Attempt ${attempt + 1}/${maxAttempts}] Status: ${status.data.status}`);

      if (status.data.status === 'COMPLETED') {
        return status;
      }
      if (status.data.status === 'ERROR') {
        throw new Error('Job failed with ERROR status');
      }
      await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
      attempt++;
    }
    throw new Error(`Job did not complete within ${maxAttempts * pollInterval} seconds`);
  }

  async downloadResult(url: string, outputPath: string): Promise<void> {
    const response = await axios.get(url, { responseType: 'stream' });
    await fs.ensureDir(path.dirname(outputPath));
    response.data.pipe(fs.createWriteStream(outputPath));
    await new Promise((resolve, reject) => {
      response.data.on('end', resolve);
      response.data.on('error', reject);
    });
  }

  // === PHOTO HELPER ===
  async enhanceAndDownload(
    inputUrl: string,
    outputPath: string,
    options: {
      model?: string;
      extension?: string;
      exif?: boolean;
      dpi?: number;
      pollInterval?: number;
      timeout?: number;
    } = {}
  ): Promise<{ coins: number }> {
    const {
      model = 'general_2x',
      extension = '.jpg',
      exif = true,
      dpi,
      pollInterval = 5,
      timeout = 300
    } = options;

    console.log(`Submitting photo enhancement with model: ${model}...`);
    const enhanceResp = await this.enhancePhoto({
      model_name: model,
      img_url: inputUrl,
      extension,
      exif,
      DPI: dpi
    });

    if (enhanceResp.code !== 200) {
      throw new Error(`Enhance request failed: ${enhanceResp.message}`);
    }

    console.log(`Job ID: ${enhanceResp.data.job_id}`);
    console.log(`Coins: ${enhanceResp.data.consume_coins}`);

    const status = await this.waitForCompletion(enhanceResp.data.job_id, Math.floor(timeout / pollInterval), pollInterval);

    if (!status.data.res_url) {
      throw new Error('No result URL in status response');
    }

    console.log(`Downloading to ${outputPath}...`);
    await this.downloadResult(status.data.res_url, outputPath);
    console.log('✅ Photo enhancement complete!');

    return { coins: enhanceResp.data.consume_coins };
  }

  // === VIDEO HELPER ===
  async enhanceVideoAndDownload(
    inputUrl: string,
    outputPath: string,
    options: {
      model?: string;
      resolution: [number, number];
      original_resolution?: [number, number];
      extension?: string;
      pollInterval?: number;
      timeout?: number;
    }
  ): Promise<{ coins: number }> {
    const DEFAULT_MODEL = 'general_restore_2x';
    const DEFAULT_EXTENSION = '.mp4';

    const {
      model = DEFAULT_MODEL,
      resolution,
      extension = DEFAULT_EXTENSION,
      original_resolution,
      pollInterval = 10,
      timeout = 600
    } = options;

    if (!resolution) {
      throw new Error('resolution is required for video enhancement (e.g., [1920, 1080])');
    }

    console.log(`Submitting video enhancement with model: ${model}...`);
    console.log(`Target resolution: ${resolution[0]}x${resolution[1]}`);

    const requestBody: VideoEnhanceRequest = {
      model_name: model,
      video_url: inputUrl,
      resolution,
      extension
    };
    if (original_resolution) {
      requestBody.original_resolution = original_resolution;
    }

    const enhanceResp = await this.enhanceVideo(requestBody);

    if (enhanceResp.code !== 200) {
      throw new Error(`Video enhance failed: ${enhanceResp.message}`);
    }

    console.log(`Job ID: ${enhanceResp.data.job_id}`);
    console.log(`Coins: ${enhanceResp.data.consume_coins}`);

    const status = await this.waitForCompletion(enhanceResp.data.job_id, Math.floor(timeout / pollInterval), pollInterval);

    if (!status.data.res_url) {
      throw new Error('No result URL in status response');
    }

    console.log(`Downloading enhanced video to ${outputPath}...`);
    await this.downloadResult(status.data.res_url, outputPath);
    console.log('✅ Video enhancement complete!');

    return { coins: enhanceResp.data.consume_coins };
  }
}

path: src/video-cli.js
content: #!/usr/bin/env node
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import path from 'path';
import fs from 'fs-extra';
import { HitPawClient } from './client.js';

const program = new Command();

program
  .name('enhance-video')
  .description('Enhance videos using HitPaw AI Video Enhancement API')
  .version('1.0.0');

program
  .option('-u, --url <url>', 'URL or local file path of the video')
  .option('-o, --output <path>', 'Output file path', 'output.mp4')
  .option('-m, --model <model>', 'Enhancement model (see models table below)', 'general_restore_2x')
  .option('-r, --resolution <wxh>', 'Target resolution (required, e.g., 1920x1080)')
  .option('-e, --extension <ext>', 'Output extension (e.g., .mp4, .mov, .avi)', '.mp4')
  .option('--original-resolution <wxh>', 'Original video resolution (optional, e.g., 1280x720)')
  .option('--poll-interval <seconds>', 'Polling interval (default 10s for video)', '10')
  .option('--timeout <seconds>', 'Max wait time (default 600s for video)', '600')
  .requiredOption('-u, --url <url>', 'Video URL or local file path')
  .action(async (options) => {
    const [REDACTED_SECRET];
    if (!apiKey) {
      console.error(chalk.red('Error: HITPAW_API_KEY environment variable is not set'));
      console.log(chalk.yellow('Set it with: export HITPAW_API_KEY="your_key"'));
      process.exit(1);
    }

    const { url, output, model, resolution, extension, original_resolution, pollInterval, timeout } = options;

    // Parse resolution strings
<<<<<<< HEAD
    const parseRes = (str: string): [number, number] => {
=======
    const parseRes = (str) => {
>>>>>>> 96b5b31 (docs: 丰富 HitPaw API 功能文档)
      const [w, h] = str.split('x').map(Number);
      if (!w || !h) throw new Error(`Invalid resolution format: ${str}. Use WxH (e.g., 1920x1080)`);
      return [w, h];
    };

    if (!resolution) {
      console.error(chalk.red('Error: --resolution (-r) is required for video enhancement'));
      console.log(chalk.yellow('Example: -r 1920x1080'));
      process.exit(1);
    }

    const targetRes = parseRes(resolution);
    const originalRes = original_resolution ? parseRes(original_resolution) : undefined;

    if (fs.existsSync(url)) {
      console.error(chalk.red('Error: Local file paths are not directly supported.'));
      console.log(chalk.yellow('Please upload the video to a public URL first.'));
      process.exit(1);
    }

    const client = new HitPawClient(apiKey);

    const spinner = ora('Enhancing video...').start();

    try {
      const result = await client.enhanceVideoAndDownload(url, output, {
        model,
        resolution: targetRes,
        extension,
        original_resolution: originalRes,
        pollInterval: parseInt(pollInterval),
        timeout: parseInt(timeout)
      });

      spinner.succeed(chalk.green('Video enhancement complete!'));
      console.log(`Output: ${path.resolve(output)}`);
      console.log(`Coins consumed: ${result.coins}`);
<<<<<<< HEAD
    } catch (error: any) {
=======
    } catch (error) {
>>>>>>> 96b5b31 (docs: 丰富 HitPaw API 功能文档)
      spinner.fail(chalk.red('Video enhancement failed'));
      console.error(chalk.red(error.message || 'Unknown error'));

      if (error.response?.data?.error_code) {
        const code = error.response.data.error_code;
<<<<<<< HEAD
        const messages: Record<number, string> = {
=======
        const messages = {
>>>>>>> 96b5b31 (docs: 丰富 HitPaw API 功能文档)
          110400000: 'Invalid API key',
          110402000: 'Insufficient coins',
          110400005: 'Unsupported model',
          110400007: 'Invalid extension',
          110400008: 'Invalid video URL',
          110400009: 'Input resolution over limit',
          110400010: 'Target resolution over limit',
          110400011: 'Video duration over limit',
          100429000: 'Rate limit exceeded'
        };
        console.error(chalk.yellow(`Error code ${code}: ${messages[code] || 'See documentation'}`));
      }

      process.exit(1);
    }
  });

program.parse();

path: tsconfig.json
content: {
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": false,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)