@wavespeed/cli 0.4.29 → 0.4.31
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 +3 -2
- package/dist/commands/price.js +23 -1
- package/dist/commands/run.js +21 -35
- package/dist/lib/media-models.js +2 -1
- package/dist/lib/validate-inputs.js +19 -0
- package/package.json +1 -1
- package/skills/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ wavespeed extend video "the camera keeps rising" --video @./clip.mp4 --json
|
|
|
120
120
|
|
|
121
121
|
| | Default | Alternatives (`-m`) |
|
|
122
122
|
|---|---|---|
|
|
123
|
-
| Image | `seedream-5-
|
|
123
|
+
| Image | `seedream-5-5` | `seedream-5-pro`, `gpt-image-2.6`, `gpt-image-2.5`, `nano-banana-2.1`, `nano-banana-pro`, `nano-banana-2`, `gpt-image-2` |
|
|
124
124
|
| Video | `seedance-2.5` | `seedance-2.5-turbo`, `kling-4.0`, `wan-3.0`, `wan-3.0-prime`, `minimax-h3` (cheap and fast, native stereo audio, 768p by default), Seedance 2.0 tiers |
|
|
125
125
|
|
|
126
126
|
For everything else (audio, 3D, upscaling, a specific endpoint), find the model with `wavespeed models <query>` and call it with `wavespeed run <model-id>`.
|
|
@@ -190,7 +190,8 @@ wavespeed skill install Drop SKILL.md for coding agents
|
|
|
190
190
|
models bill from an input — audio/video duration, frame count, text length. Pass
|
|
191
191
|
the same inputs you would pass to `wavespeed run`: a quote made without them can
|
|
192
192
|
be lower than the real charge. The amount actually charged for a run is
|
|
193
|
-
authoritative.
|
|
193
|
+
authoritative. Like `run`, `price` refuses an input the model does not accept (a typo would
|
|
194
|
+
otherwise be ignored by the quote); `--no-validate` skips the check.
|
|
194
195
|
|
|
195
196
|
## Auth
|
|
196
197
|
|
package/dist/commands/price.js
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { fetchPricing, modelPlaygroundUrl } from '../lib/api.js';
|
|
4
4
|
import { parseInputs } from '../lib/inputs.js';
|
|
5
|
-
import { modelInputProps } from '../lib/validate-inputs.js';
|
|
5
|
+
import { modelInputProps, unknownInputsFor } from '../lib/validate-inputs.js';
|
|
6
6
|
import { resolveModelToken } from '../lib/config.js';
|
|
7
7
|
import { collectLocalFiles, resolveLocalFiles } from '../lib/local-files.js';
|
|
8
8
|
import { uploadWithCache } from '../lib/upload-cache.js';
|
|
@@ -15,6 +15,7 @@ export function registerPrice(program) {
|
|
|
15
15
|
.option('-i, --input <pair...>', 'Inputs as key=value (same syntax as `wavespeed run`)', [])
|
|
16
16
|
.option('-p, --prompt <text>', 'Shorthand for --input prompt=<text>')
|
|
17
17
|
.option('--upload', 'Upload @file inputs first so media-based pricing is exact (quota applies; identical bytes cached 24h)')
|
|
18
|
+
.option('--no-validate', 'Skip checking the inputs against the model schema')
|
|
18
19
|
.option('--json', 'Emit JSON')
|
|
19
20
|
.action(async (modelArg, opts) => {
|
|
20
21
|
const resolved = resolveModelToken(modelArg);
|
|
@@ -30,6 +31,27 @@ export function registerPrice(program) {
|
|
|
30
31
|
Object.assign(inputs, parseInputs(opts.input ?? []));
|
|
31
32
|
if (opts.prompt)
|
|
32
33
|
inputs.prompt = opts.prompt;
|
|
34
|
+
// A key the model does not declare is dropped by the pricer, so a typo would quote a price that ignores it.
|
|
35
|
+
if (opts.validate !== false) {
|
|
36
|
+
const report = await unknownInputsFor(resolved.model, inputs);
|
|
37
|
+
if (report) {
|
|
38
|
+
const msg = `Model ${resolved.model} does not accept: ${report.unknown.join(', ')}. ` +
|
|
39
|
+
`Unknown inputs are ignored, so this quote would not account for them.`;
|
|
40
|
+
if (opts.json) {
|
|
41
|
+
process.stdout.write(JSON.stringify({ error: msg, unknown_inputs: report.unknown, accepted_inputs: report.known }, null, 2) + '\n');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.error(chalk.red('Error: ') + `unknown input${report.unknown.length > 1 ? 's' : ''} for ` + chalk.cyan(resolved.model) + ':');
|
|
45
|
+
for (const k of report.unknown) {
|
|
46
|
+
const hint = report.suggestions.get(k);
|
|
47
|
+
console.error(chalk.yellow(` ${k}` + (hint ? ` (did you mean \`${hint}\`?)` : '')));
|
|
48
|
+
}
|
|
49
|
+
console.error(chalk.gray(' Accepted inputs: ') + report.known.join(', '));
|
|
50
|
+
console.error(chalk.gray(' Bypass check: ') + chalk.cyan('--no-validate'));
|
|
51
|
+
}
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
33
55
|
// Pricing that depends on media (e.g. per-second video upscaling) is
|
|
34
56
|
// measured server-side from the URL, so a local path is useless to the
|
|
35
57
|
// pricing engine. Uploading is slow and quota-limited, so it is opt-in:
|
package/dist/commands/run.js
CHANGED
|
@@ -5,8 +5,8 @@ import fs from 'node:fs';
|
|
|
5
5
|
import { requireClient } from '../lib/client.js';
|
|
6
6
|
import { parseInputs, withInputSyntaxHint } from '../lib/inputs.js';
|
|
7
7
|
import { resolveLocalFiles } from '../lib/local-files.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { submitPrediction, waitForPrediction } from '../lib/api.js';
|
|
9
|
+
import { modelInputProps, unknownInputsFor } from '../lib/validate-inputs.js';
|
|
10
10
|
import { uploadWithCache } from '../lib/upload-cache.js';
|
|
11
11
|
import { downloadOutputs, printOutputs } from '../lib/output.js';
|
|
12
12
|
import { emitJson, emitJsonError, isJsonMode, log, setJsonMode } from '../lib/log.js';
|
|
@@ -106,41 +106,27 @@ export function registerRun(program) {
|
|
|
106
106
|
// avoid stale-cache false positives, re-fetches the catalog once
|
|
107
107
|
// before rejecting.
|
|
108
108
|
if (opts.validate !== false) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
const report = await unknownInputsFor(model, input);
|
|
110
|
+
if (report) {
|
|
111
|
+
const lines = report.unknown.map((k) => {
|
|
112
|
+
const hint = report.suggestions.get(k);
|
|
113
|
+
return ` ${k}` + (hint ? ` (did you mean \`${hint}\`?)` : '');
|
|
114
|
+
});
|
|
115
|
+
const msg = `Model ${model} does not accept: ${report.unknown.join(', ')}. ` +
|
|
116
|
+
`The API silently drops unknown inputs, so this would bill a run that ignores them.`;
|
|
117
|
+
if (isJsonMode()) {
|
|
118
|
+
emitJsonError(msg, { unknown_inputs: report.unknown, accepted_inputs: report.known });
|
|
117
119
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
emitJsonError(msg, { unknown_inputs: report.unknown, accepted_inputs: report.known });
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
console.error(chalk.red('Error: ') + `unknown input${report.unknown.length > 1 ? 's' : ''} for ` + chalk.cyan(model) + ':');
|
|
130
|
-
for (const l of lines)
|
|
131
|
-
console.error(chalk.yellow(l));
|
|
132
|
-
console.error(chalk.gray(' The API silently drops unknown inputs — the run would be billed with them ignored.'));
|
|
133
|
-
console.error(chalk.gray(' Accepted inputs: ') + report.known.join(', '));
|
|
134
|
-
console.error(chalk.gray(' Full schema: ') + chalk.cyan(`wavespeed schema ${model}`));
|
|
135
|
-
console.error(chalk.gray(' Bypass check: ') + chalk.cyan('--no-validate'));
|
|
136
|
-
}
|
|
137
|
-
process.exit(1);
|
|
120
|
+
else {
|
|
121
|
+
console.error(chalk.red('Error: ') + `unknown input${report.unknown.length > 1 ? 's' : ''} for ` + chalk.cyan(model) + ':');
|
|
122
|
+
for (const l of lines)
|
|
123
|
+
console.error(chalk.yellow(l));
|
|
124
|
+
console.error(chalk.gray(' The API silently drops unknown inputs — the run would be billed with them ignored.'));
|
|
125
|
+
console.error(chalk.gray(' Accepted inputs: ') + report.known.join(', '));
|
|
126
|
+
console.error(chalk.gray(' Full schema: ') + chalk.cyan(`wavespeed schema ${model}`));
|
|
127
|
+
console.error(chalk.gray(' Bypass check: ') + chalk.cyan('--no-validate'));
|
|
138
128
|
}
|
|
139
|
-
|
|
140
|
-
catch {
|
|
141
|
-
// Catalog unreachable — validation is best-effort, never a blocker.
|
|
142
|
-
// (The unknown-input rejection above exits the process directly and
|
|
143
|
-
// is not routed through here.)
|
|
129
|
+
process.exit(1);
|
|
144
130
|
}
|
|
145
131
|
}
|
|
146
132
|
// `@path` inputs become hosted URLs before submission. Only the
|
package/dist/lib/media-models.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The image and video models the dedicated generate/edit tools offer: the
|
|
2
2
|
// same list, in the same order, as the WaveSpeed /chat page (api-dashboard
|
|
3
|
-
// lib/media-models.ts, checked 2026-11-
|
|
3
|
+
// lib/media-models.ts, checked 2026-11-10). The first of each is the default.
|
|
4
4
|
// This is a vendored copy: when /chat changes its models, change this file and
|
|
5
5
|
// its test (src/lib/media-models.test.ts) with it. Capabilities mirror the
|
|
6
6
|
// endpoints' input schemas; the tools still fit every request to the live
|
|
@@ -14,6 +14,7 @@ const image = (id, label, base, generate, resolutions, aspectRatios, maxImages)
|
|
|
14
14
|
});
|
|
15
15
|
/** In menu order; the first is the default. */
|
|
16
16
|
export const IMAGE_MODELS = {
|
|
17
|
+
'seedream-5-5': image('seedream-5-5', 'Seedream 5.5', 'bytedance/seedream-v5.5', '', ['1k', '1.5k', '2k'], WIDE_RATIOS, 10),
|
|
17
18
|
'seedream-5-pro': image('seedream-5-pro', 'Seedream 5 Pro', 'bytedance/seedream-v5.0-pro', '', ['1k', '1.5k', '2k'], WIDE_RATIOS, 10),
|
|
18
19
|
'nano-banana-2.1': image('nano-banana-2.1', 'Nano Banana 2.1', 'google/nano-banana-2.1', 'text-to-image', ['1k', '2k', '4k'], NANO_BANANA_2_RATIOS, 14),
|
|
19
20
|
'nano-banana-2': image('nano-banana-2', 'Nano Banana 2', 'google/nano-banana-2', 'text-to-image', ['0.5k', '1k', '2k', '4k'], NANO_BANANA_2_RATIOS, 14),
|
|
@@ -109,3 +109,22 @@ export function findUnknownInputs(input, model) {
|
|
|
109
109
|
}
|
|
110
110
|
return { unknown, known, suggestions };
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Inputs the model's schema does not declare, checked against the live catalog. The API silently drops unknown
|
|
114
|
+
* keys, so a typo bills a run (or quotes a price) that ignores it. Fails open: null when the catalog has no schema
|
|
115
|
+
* or cannot be reached; re-fetches once before reporting, so a stale cache cannot block a valid input.
|
|
116
|
+
*/
|
|
117
|
+
export async function unknownInputsFor(model, input) {
|
|
118
|
+
try {
|
|
119
|
+
let { models } = await fetchModels();
|
|
120
|
+
let report = findUnknownInputs(input, models.find((m) => m.model_id === model));
|
|
121
|
+
if (report) {
|
|
122
|
+
({ models } = await fetchModels({ refresh: true }));
|
|
123
|
+
report = findUnknownInputs(input, models.find((m) => m.model_id === model));
|
|
124
|
+
}
|
|
125
|
+
return report;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ They use the same models as the WaveSpeed /chat page and pick the endpoint (text
|
|
|
33
33
|
|
|
34
34
|
| | Default | Alternatives (`-m` / `model`) |
|
|
35
35
|
|---|---|---|
|
|
36
|
-
| Image | `seedream-5-
|
|
36
|
+
| Image | `seedream-5-5` (Seedream 5.5) | `seedream-5-pro`, `gpt-image-2.6` (GPT Image 2.6, the model behind ChatGPT Images 2.6; strong at text and photorealism), `gpt-image-2.5`, `nano-banana-2.1`, `nano-banana-pro`, `nano-banana-2`, `gpt-image-2` |
|
|
37
37
|
| Video | `seedance-2.5` (Seedance 2.5) | `seedance-2.5-turbo`, `kling-4.0`, `wan-3.0`, `wan-3.0-prime`, `minimax-h3` (cheap and fast, native stereo audio; defaults to 768p, its counterpart of 720p), older Seedance 2.0 tiers |
|
|
38
38
|
|
|
39
39
|
Video resolution is 720p unless the user asks for another (MiniMax H3: 768p). Offer 480p for a cheaper draft.
|