@wavespeed/cli 0.4.0 → 0.4.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 CHANGED
@@ -133,11 +133,13 @@ wavespeed models [query] Browse the live catalog (cached 1h)
133
133
  wavespeed aliases List aliases from wavespeed.json + user config
134
134
 
135
135
  # files
136
- wavespeed upload <file...> Upload local file(s) → CDN URLs
136
+ wavespeed upload <file...> Upload local file(s) → CDN URLs (24h dedup cache)
137
137
  wavespeed download <url...> Save URLs to disk
138
138
 
139
139
  # account & history
140
140
  wavespeed balance Show your current credit balance
141
+ wavespeed usage [--since --until] Spend + request counts, per-model (last 7d)
142
+ wavespeed billings [--type --since …] Itemized charges and refunds
141
143
  wavespeed price <model> -i k=v Estimate the cost of a run (no charge)
142
144
  wavespeed top-up Open https://wavespeed.ai/top-up
143
145
  wavespeed history [--limit --status …] List recent predictions (last 24h)
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { fetchBillings } from '../lib/api.js';
4
+ import { toIso } from '../lib/time.js';
4
5
  function money(v) {
5
6
  if (v === undefined)
6
7
  return '';
@@ -35,8 +36,8 @@ export function registerBillings(program) {
35
36
  type: opts.type,
36
37
  model: opts.model,
37
38
  accessKey: opts.key,
38
- startTime: opts.since,
39
- endTime: opts.until,
39
+ startTime: opts.since ? toIso(opts.since) : undefined,
40
+ endTime: opts.until ? toIso(opts.until, true) : undefined,
40
41
  });
41
42
  spinner?.stop();
42
43
  if (opts.json) {
@@ -7,7 +7,7 @@ export function registerDelete(program) {
7
7
  .command('delete')
8
8
  .description('Delete one or more predictions from your history')
9
9
  .argument('<id...>', 'Prediction ID(s) to delete')
10
- .option('-y, --yes', 'Skip the confirmation prompt')
10
+ .option('-y, --yes', 'Skip the confirmation prompt (required in scripts / non-interactive use)')
11
11
  .option('--json', 'Emit JSON: {deleted: [...ids]}')
12
12
  .action(async (ids, opts) => {
13
13
  // --json controls output format only; it must never stand in for
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { fetchHistory, PREDICTION_STATUSES } from '../lib/api.js';
4
+ import { toIso } from '../lib/time.js';
4
5
  function statusBadge(status) {
5
6
  const map = {
6
7
  completed: chalk.bgGreen.black,
@@ -35,8 +36,8 @@ export function registerHistory(program) {
35
36
  .option('--limit <n>', 'Page size (1-100)', '20')
36
37
  .option('--model <id>', 'Filter by model ID')
37
38
  .option('--status <s>', 'Filter by status (created, processing, completed, failed, cancelled, timeout)')
38
- .option('--since <iso>', 'Show predictions created after this ISO timestamp')
39
- .option('--until <iso>', 'Show predictions created before this ISO timestamp')
39
+ .option('--since <time>', 'Predictions created after this time (ISO or YYYY-MM-DD)')
40
+ .option('--until <time>', 'Predictions created before this time (ISO or YYYY-MM-DD)')
40
41
  .option('--json', 'Emit JSON: {page, items: [...]}')
41
42
  .action(async (opts) => {
42
43
  if (opts.status && !PREDICTION_STATUSES.includes(opts.status)) {
@@ -54,8 +55,8 @@ export function registerHistory(program) {
54
55
  pageSize: parseInt(opts.limit, 10),
55
56
  model: opts.model,
56
57
  status: opts.status,
57
- createdAfter: opts.since,
58
- createdBefore: opts.until,
58
+ createdAfter: opts.since ? toIso(opts.since) : undefined,
59
+ createdBefore: opts.until ? toIso(opts.until, true) : undefined,
59
60
  });
60
61
  spinner?.stop();
61
62
  if (opts.json) {
@@ -13,7 +13,7 @@ export function registerPrice(program) {
13
13
  .argument('<model>', 'Model ID or alias')
14
14
  .option('-i, --input <pair...>', 'Inputs as key=value (same syntax as `wavespeed run`)', [])
15
15
  .option('-p, --prompt <text>', 'Shorthand for --input prompt=<text>')
16
- .option('--upload', 'Upload local file inputs first so media-based pricing is exact (quota applies; content-hash cached 24h)')
16
+ .option('--upload', 'Upload @file inputs first so media-based pricing is exact (quota applies; identical bytes cached 24h)')
17
17
  .option('--json', 'Emit JSON')
18
18
  .action(async (modelArg, opts) => {
19
19
  const resolved = resolveModelToken(modelArg);
@@ -6,7 +6,7 @@ import { uploadWithCache } from '../lib/upload-cache.js';
6
6
  export function registerUpload(program) {
7
7
  program
8
8
  .command('upload')
9
- .description('Upload one or more local files to Wavespeed and print their CDN URLs')
9
+ .description('Upload one or more local files to Wavespeed and print their CDN URLs (identical bytes reuse the same upload for 24h)')
10
10
  .argument('<file...>', 'Path(s) to file(s)')
11
11
  .option('--json', 'Emit a JSON object on stdout')
12
12
  .action(async (files, opts) => {
@@ -1,16 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { fetchUsageStats } from '../lib/api.js';
4
- /** Accepts ISO or YYYY-MM-DD; returns full RFC3339 the API requires. */
5
- function toIso(value, endOfDay = false) {
6
- if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
7
- return value + (endOfDay ? 'T23:59:59Z' : 'T00:00:00Z');
8
- }
9
- const d = new Date(value);
10
- if (isNaN(d.getTime()))
11
- throw new Error(`Invalid time "${value}". Use ISO 8601 or YYYY-MM-DD.`);
12
- return d.toISOString();
13
- }
4
+ import { toIso } from '../lib/time.js';
14
5
  export function registerUsage(program) {
15
6
  program
16
7
  .command('usage')
@@ -0,0 +1,16 @@
1
+ // The predictions-search API is strict RFC3339, so bare dates the CLI's own
2
+ // hints suggest ("--since 2026-01-01") must be widened here, not rejected
3
+ // there. Shared by history, billings, and usage so every time flag accepts
4
+ // the same two forms: full ISO 8601 or YYYY-MM-DD.
5
+ /** Accepts ISO 8601 or YYYY-MM-DD; returns RFC3339. Day-only values span the
6
+ * whole day: start-of-day normally, end-of-day when `endOfDay` is set. */
7
+ export function toIso(value, endOfDay = false) {
8
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
9
+ return value + (endOfDay ? 'T23:59:59Z' : 'T00:00:00Z');
10
+ }
11
+ const d = new Date(value);
12
+ if (isNaN(d.getTime())) {
13
+ throw new Error(`Invalid time "${value}". Use ISO 8601 or YYYY-MM-DD.`);
14
+ }
15
+ return d.toISOString();
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavespeed/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "WaveSpeed CLI — one command to run any WaveSpeed AI model from your terminal.",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/skills/SKILL.md CHANGED
@@ -23,7 +23,7 @@ wavespeed run google/nano-banana-2/text-to-image \
23
23
  -i aspect_ratio="16:9" -i resolution="2k" --json
24
24
  ```
25
25
 
26
- `run --json` returns `{ model, prompt, outputs: [url, ...], saved: [path, ...], elapsed_ms, raw }`. Use the URL when the user wants a link. Add `--download` if they need bytes on disk.
26
+ `run --json` returns `{ id, model, prompt, outputs: [url, ...], saved: [path, ...], elapsed_ms, raw }`. Keep `id` — it is the handle for `wavespeed show <id>` if anything is interrupted. Use the URL when the user wants a link. Add `--download` if they need bytes on disk.
27
27
 
28
28
  ## Recommended defaults
29
29
 
@@ -39,17 +39,18 @@ These are good starting points. Browse alternatives with `wavespeed models <quer
39
39
  ## Common recipes
40
40
 
41
41
  ```bash
42
- # Edit an existing image — upload first, then pass the URL
43
- URL=$(wavespeed upload ./input.jpg --json | jq -r .url)
42
+ # Edit an existing image — @path uploads the file and passes its URL (one step)
44
43
  wavespeed run google/nano-banana-2/edit \
45
44
  -p "replace the background with a sunlit kitchen" \
46
- -i images="[\"$URL\"]" --json
45
+ -i images='["@./input.jpg"]' --json
47
46
 
48
- # Image-to-video — same pattern
49
- URL=$(wavespeed upload ./hero.jpg --json | jq -r .url)
47
+ # Image-to-video — same @ marker for single-URL fields
50
48
  wavespeed run bytedance/seedance-2.0/image-to-video \
51
49
  -p "subtle parallax, gentle wind" \
52
- -i image="$URL" -i duration=5 --json
50
+ -i image=@./hero.jpg -i duration=5 --json
51
+
52
+ # Or upload separately when you need the URL itself
53
+ URL=$(wavespeed upload ./hero.jpg --json | jq -r .url)
53
54
 
54
55
  # Save outputs locally with a template
55
56
  wavespeed run ... -p "..." --download "./out/{index}.{ext}"
@@ -62,7 +63,7 @@ If `wavespeed.json` exists (created by `wavespeed init`):
62
63
  - **`defaultModel`** — lets `wavespeed run -p "…"` (no model arg) work.
63
64
  - **Aliases** — named shortcuts that bundle model + default inputs. Run `wavespeed aliases` to see what's defined. `wavespeed run <alias> -h` shows the resolved schema. CLI `-i k=v` overrides alias defaults.
64
65
 
65
- The CLI never modifies the user's prompt or inputs. What you typed is what hits the API.
66
+ The CLI never modifies the user's prompt or inputs. The single exception is explicit: an `@path` value uploads that file and substitutes its hosted URL. Bare paths are never uploaded.
66
67
 
67
68
  ## Auth
68
69
 
@@ -70,6 +71,8 @@ The CLI never modifies the user's prompt or inputs. What you typed is what hits
70
71
 
71
72
  ## Pitfalls
72
73
 
73
- - Local file paths don't auto-upload — call `wavespeed upload` first to get a CDN URL.
74
+ - Local files: use `@./file.jpg` in `-i` values. Bare paths are NOT uploaded and the model will reject them.
74
75
  - Don't invent model IDs. Always confirm via `wavespeed models` or `wavespeed schema <id>` before running.
75
76
  - Use `--json` on every `run` so you can read `outputs[0]` programmatically.
77
+ - `wavespeed delete` requires `--yes` when run non-interactively (that includes you).
78
+ - Spend questions: `wavespeed usage` (totals, per-model) and `wavespeed billings` (per-charge records).