@watermarkws/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/SKILL.md +106 -0
- package/package.json +2 -2
- package/src/api.js +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,15 @@ npx @watermarkws/cli image process https://example.com/photo.jpg \
|
|
|
12
12
|
|
|
13
13
|
Node 18+, no dependencies.
|
|
14
14
|
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
brew install watermarkws/tap/watermark # Homebrew, macOS and Linux
|
|
19
|
+
npm install -g @watermarkws/cli # npm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Either way the command is `watermark`. Homebrew brings its own Node; `npx @watermarkws/cli` runs it without installing.
|
|
23
|
+
|
|
15
24
|
## Commands
|
|
16
25
|
|
|
17
26
|
```
|
|
@@ -59,6 +68,10 @@ The CLI passes every error code through as the API sends it: `Error 422 validati
|
|
|
59
68
|
|
|
60
69
|
Rate limits (429) and server errors are retried twice with the same `Idempotency-Key`, so a retry never runs a request twice.
|
|
61
70
|
|
|
71
|
+
## For AI agents
|
|
72
|
+
|
|
73
|
+
`SKILL.md` is an [Agent Skill](https://agentskills.io) for this CLI: when to use it, the commands, the output shape, exit codes and the rules an agent should follow (ask before a big batch, never paste file bytes). Save it as `.claude/skills/watermark/SKILL.md` in a project for Claude Code, or paste it into any agent's instructions. The API's own guide for agents, with its MCP server and plain HTTP, is at https://api.watermark.ws/docs/agents.md.
|
|
74
|
+
|
|
62
75
|
## Development
|
|
63
76
|
|
|
64
77
|
```bash
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: watermark
|
|
3
|
+
description: Watermark, resize, crop, convert and stamp text on images, videos and PDF or Word documents with the Watermark API from the shell, through the `watermark` CLI. Use it when a user wants a logo or text placed on files, a batch of photos resized or converted (JPEG, PNG, WebP, HEIC, camera RAW and more), a video watermarked or trimmed to a preview, a frame or a page saved as an image, or every page of a PDF stamped while it stays a PDF. Sources can be public URLs, local files (uploaded automatically) or files an earlier request made. Needs a WATERMARK_API_KEY from https://watermark.ws/developers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Watermark CLI
|
|
7
|
+
|
|
8
|
+
The `watermark` command sends a file and a list of operations to the Watermark API, waits for the result and downloads it. Use it instead of a local image library for batches, video, PDF/DOCX, camera formats (HEIC, CR3, NEF, ARW, DNG), text in real fonts, and when the result should be a link the user can download.
|
|
9
|
+
|
|
10
|
+
## Install and set the key
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g @watermarkws/cli # or: brew install watermarkws/tap/watermark
|
|
14
|
+
npx @watermarkws/cli --version # or run it once without installing (Node 18+)
|
|
15
|
+
export WATERMARK_API_KEY=wm_test_… # keys: https://watermark.ws/developers
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`wm_test_…` keys are free and return real results within small limits (10 requests a minute, 100 a day, sources up to 10 MB, no video). `wm_live_…` keys spend the account's prepaid credit. If the user has no key, send them to https://watermark.ws/developers; new accounts start with $1 of free credit.
|
|
19
|
+
|
|
20
|
+
## Core commands
|
|
21
|
+
|
|
22
|
+
A `<source>` is a public http(s) URL, a local file path (uploaded first, automatically), an upload id `upl_…`, a file an earlier request made `file_…`, or `stg_7a:path/key` in a connected bucket. Operations run in the order given: `--op type:param=value,param=value`; numbers and `true`/`false` are typed, everything else is text.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
watermark image process photo.jpg --op resize:width=1600,height=1600 --op watermark:image_url=https://example.com/logo.png,x=40,y=40 --format webp --out photo.webp
|
|
26
|
+
watermark image read photo.jpg --json # width, height, format; add --level for the angle that levels the horizon
|
|
27
|
+
watermark image create --width 1200 --height 300 --op 'text:text=Lena Hart,font=inter-bold,size=96,color=#202020,position=center' --op trim --format png --out logo.png
|
|
28
|
+
watermark video process clip.mp4 --op watermark:image_url=https://example.com/logo.png,x=24,y=24 --max-duration 30 --out preview.mp4
|
|
29
|
+
watermark video screenshot clip.mp4 --seconds 5 --format png --out frame.png
|
|
30
|
+
watermark document process contract.pdf --op watermark:image_url=https://example.com/stamp.png,x=20,y=230 --out contract-stamped.pdf
|
|
31
|
+
watermark document snapshot contract.pdf --page 1 --format png --out page-1.png
|
|
32
|
+
watermark operations image # every operation and parameter, live from the API (also video, document)
|
|
33
|
+
watermark fonts # font names a text operation accepts
|
|
34
|
+
watermark request get 1234 # follow a request started with --no-wait
|
|
35
|
+
watermark account # balance and limits
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Useful options on processing commands: `--out <file>` (wait, then download), `--no-wait` (print the queued request and return), `--timeout <seconds>` (default 300), `--format <fmt>`, `--ops-json '[…]'` instead of `--op`, `--max-duration <s>` (video), `--max-pages <n>` (documents), `--customer <id>`, `--metadata key=value`, `--json`.
|
|
39
|
+
|
|
40
|
+
Operations that exist: images `resize`, `crop`, `rotate`, `flip`, `grayscale`, `border`, `watermark`, `thumbnail`, `compress`, `sepia`, `exposure`, `filter`, `remove_background`, `orient`, `text`, `trim`, `color`, `frame`, `protect`, `shave`, `line`, `adjust`, `color_profile`; videos `resize`, `crop`, `rotate`, `flip`, `grayscale`, `border`, `watermark`; documents `watermark` only (an image on every page; make a text stamp with `image create` first). `watermark` takes `image_url` or `image` (a `file_…` id), `x`, `y` from the top left, `tile_spacing_x`/`tile_spacing_y` to repeat it, `diagonal_lines_color`/`diagonal_lines_opacity`. Run `watermark operations image` before using a parameter you are not sure of.
|
|
41
|
+
|
|
42
|
+
## A folder of files
|
|
43
|
+
|
|
44
|
+
Prepare the watermark once and reuse its `file_…` id; one request per file after that.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
logo=$(watermark image process logo.png --op orient --format png --json | jq -r '.result.files[0].id')
|
|
48
|
+
mkdir -p out
|
|
49
|
+
for f in photos/*; do
|
|
50
|
+
name=$(basename "${f%.*}")
|
|
51
|
+
watermark image process "$f" --op resize:width=1600,height=1600 --op "watermark:image=$logo,x=40,y=40" --format webp --out "out/$name.webp"
|
|
52
|
+
done
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Stamp text across every page of a PDF: make the stamp, then watermark the document with it.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
stamp=$(watermark image create --width 800 --height 800 --op 'text:text=CONFIDENTIAL,font=inter-bold,size=80,color=#c0392b,opacity=35,position=center,rotation=-30' --op trim --format png --json | jq -r '.result.files[0].id')
|
|
59
|
+
watermark document process contract.pdf --op "watermark:image=$stamp,x=20,y=230" --out contract-confidential.pdf
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Output and exit codes
|
|
63
|
+
|
|
64
|
+
With `--json`, stdout is only the finished request as the API returns it (progress goes to stderr):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{"id": 1234, "object": "request", "endpoint": "images/process", "status": "succeeded",
|
|
68
|
+
"result": {"data": {"width": 1600, "height": 1067, "format": "WEBP"},
|
|
69
|
+
"files": [{"id": "file_…", "url": "https://…", "url_expires_at": "2026-10-18T15:00:04Z", "format": "webp", "size": 183021}]},
|
|
70
|
+
"price": {"amount": "0.002000", "currency": "usd"}, "saved_to": "photo.webp"}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A failed request has `"status": "failed"` and `error.code` / `error.message`. An API error with `--json` prints `{"error": {"status": 422, "type": "…", "code": "…", "message": "…", "errors": {…}}}` on stdout. Without `--json`, the result is human-readable lines (`Request 1234 (images/process): succeeded`, `File: https://…`, `Saved to …`, `Price: $0.002000`).
|
|
74
|
+
|
|
75
|
+
| Exit | Meaning |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| 0 | The request succeeded (or was queued, with `--no-wait`). |
|
|
78
|
+
| 1 | The API refused the call, or the request failed or was aborted. The error code is printed. |
|
|
79
|
+
| 2 | The command was used wrongly (bad source, missing `--op`, unknown command). |
|
|
80
|
+
|
|
81
|
+
## Rules
|
|
82
|
+
|
|
83
|
+
- Never paste file contents or base64 into a command; give the path and the CLI uploads it. Never ask the user to host a local file first.
|
|
84
|
+
- Credit is prepaid and every finished request is charged ($0.002 an image, $0.004 a document page, $0.02 a started minute of video). Before more than about 20 files, a video over about 10 minutes, or a document with hundreds of pages, tell the user the cost and ask.
|
|
85
|
+
- Failed and aborted requests are free; fix the cause (the message names the field) and run again. Rate limits and server errors are retried by the CLI itself.
|
|
86
|
+
- Always pass `--out` when the user wants the file, and `--json` when a script reads the result. Download links in `result.files[].url` last 7 days; `watermark file get <file_id>` gives a fresh one while the file is kept.
|
|
87
|
+
- Video needs a live key on an account that has bought credit; a test key gets `402 paid_account_required`.
|
|
88
|
+
- Check a file before guessing parameters: `watermark image read`, `watermark video read` and `watermark document read` give dimensions, duration and page count.
|
|
89
|
+
|
|
90
|
+
## Common errors
|
|
91
|
+
|
|
92
|
+
| Error | Fix |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `Set WATERMARK_API_KEY or pass --api-key`, `401 missing_api_key`, `401 invalid_api_key` | No key, or a wrong or revoked key. Export `WATERMARK_API_KEY` from https://watermark.ws/developers. |
|
|
95
|
+
| `402 insufficient_credits` | The account has no credit left. Buy credit at https://watermark.ws/developers. |
|
|
96
|
+
| `402 paid_account_required` | Video, or writing to your own storage, needs an account that has bought credit; a test key cannot. |
|
|
97
|
+
| `422 validation_failed` | A parameter is wrong; the invalid fields are listed under the message. Check `watermark operations <media>`. |
|
|
98
|
+
| `429 rate_limited` / `429 concurrency_limited` | Too many requests a minute, or too many running at once. The CLI retries twice; otherwise wait and run fewer in parallel. |
|
|
99
|
+
| `is not a URL, an id, stg_…:key, or a file here` | The source path does not exist or is not a file. |
|
|
100
|
+
| `Request … was still processing after 300 seconds` | A long video. Rerun with `--timeout 1800`, or `--no-wait` and later `watermark request get <id>`. |
|
|
101
|
+
|
|
102
|
+
## More
|
|
103
|
+
|
|
104
|
+
- Agent guide, with MCP server, HTTP and worked examples: https://api.watermark.ws/docs/agents.md
|
|
105
|
+
- Every operation and parameter: https://api.watermark.ws/docs/operations.md
|
|
106
|
+
- Every command and option: `watermark --help`, or https://api.watermark.ws/docs/cli.md
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@watermarkws/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Watermark, resize and convert images, videos and PDFs from the command line with the Watermark API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"watermark": "bin/watermark.js"
|
|
8
8
|
},
|
|
9
|
-
"files": ["bin", "src", "README.md", "LICENSE"],
|
|
9
|
+
"files": ["bin", "src", "README.md", "SKILL.md", "LICENSE"],
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=18"
|
|
12
12
|
},
|
package/src/api.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// retries for 429 (after Retry-After), 5xx and dropped connections, with an
|
|
3
3
|
// Idempotency-Key on every POST so a retried POST never runs twice.
|
|
4
4
|
|
|
5
|
-
export const VERSION = '0.1.
|
|
5
|
+
export const VERSION = '0.1.1';
|
|
6
6
|
|
|
7
7
|
export class ApiError extends Error {
|
|
8
8
|
constructor(status, error = {}) {
|