@wavespeed/cli 0.4.13 → 0.4.15
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 +5 -0
- package/dist/cli.js +3 -11
- package/dist/commands/price.js +3 -3
- package/dist/commands/schema.js +1 -2
- package/dist/commands/skill.js +7 -2
- package/dist/commands/upgrade.js +200 -0
- package/dist/lib/api.js +3 -6
- package/dist/lib/client-headers.js +2 -17
- package/dist/lib/version.js +36 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -42,6 +42,10 @@ npm install -g @wavespeed/cli
|
|
|
42
42
|
# friendlier error path)
|
|
43
43
|
curl -fsSL https://raw.githubusercontent.com/WaveSpeedAI/wavespeed-cli/main/install.sh | bash
|
|
44
44
|
|
|
45
|
+
# no Node.js/npm? install.sh falls back to a standalone executable on macOS/Linux;
|
|
46
|
+
# on Windows (PowerShell):
|
|
47
|
+
irm https://raw.githubusercontent.com/WaveSpeedAI/wavespeed-cli/main/install.ps1 | iex
|
|
48
|
+
|
|
45
49
|
# sign in (opens the access-key page, paste to confirm)
|
|
46
50
|
wavespeed login
|
|
47
51
|
```
|
|
@@ -136,6 +140,7 @@ wavespeed login Browser + paste-key wizard (clipboard-a
|
|
|
136
140
|
wavespeed logout Clear stored API key
|
|
137
141
|
wavespeed status Show masked key, base URL, useful links
|
|
138
142
|
wavespeed config [--default-model …] View / update CLI defaults
|
|
143
|
+
wavespeed upgrade [--check] [--json] Upgrade to the latest version (--check only reports)
|
|
139
144
|
|
|
140
145
|
# generation
|
|
141
146
|
wavespeed run [model|alias] -p "…" Run any model or alias (uses defaultModel if omitted)
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
4
|
import { printBanner } from './lib/banner.js';
|
|
8
5
|
import { registerAuth } from './commands/auth.js';
|
|
9
6
|
import { registerRun } from './commands/run.js';
|
|
@@ -23,15 +20,9 @@ import { registerDelete } from './commands/delete.js';
|
|
|
23
20
|
import { registerOpen } from './commands/open.js';
|
|
24
21
|
import { registerBillings } from './commands/billings.js';
|
|
25
22
|
import { registerUsage } from './commands/usage.js';
|
|
23
|
+
import { registerUpgrade } from './commands/upgrade.js';
|
|
24
|
+
import { VERSION } from './lib/version.js';
|
|
26
25
|
import { detectRunHelp, printDynamicRunHelp } from './lib/dynamic-help.js';
|
|
27
|
-
// Read version from package.json at runtime so a bump in package.json is
|
|
28
|
-
// reflected automatically (no second place to keep in sync).
|
|
29
|
-
const VERSION = (() => {
|
|
30
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
-
// dist/cli.js → ../package.json (one level up because files: ["dist", ...])
|
|
32
|
-
const pkgPath = path.resolve(here, '..', 'package.json');
|
|
33
|
-
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
|
|
34
|
-
})();
|
|
35
26
|
const program = new Command();
|
|
36
27
|
program
|
|
37
28
|
.name('wavespeed')
|
|
@@ -70,6 +61,7 @@ registerBillings(program);
|
|
|
70
61
|
registerUsage(program);
|
|
71
62
|
registerInit(program);
|
|
72
63
|
registerSkill(program);
|
|
64
|
+
registerUpgrade(program);
|
|
73
65
|
program
|
|
74
66
|
.command('hello', { hidden: true })
|
|
75
67
|
.description('Print the WaveSpeed banner')
|
package/dist/commands/price.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { fetchPricing,
|
|
3
|
+
import { fetchPricing, modelPlaygroundUrl } from '../lib/api.js';
|
|
4
4
|
import { parseInputs } from '../lib/inputs.js';
|
|
5
5
|
import { resolveModelToken } from '../lib/config.js';
|
|
6
6
|
import { collectLocalFiles, resolveLocalFiles } from '../lib/local-files.js';
|
|
@@ -80,7 +80,7 @@ export function registerPrice(program) {
|
|
|
80
80
|
...data,
|
|
81
81
|
estimate: true,
|
|
82
82
|
disclaimer,
|
|
83
|
-
|
|
83
|
+
playground_url: modelPlaygroundUrl(resolved.model),
|
|
84
84
|
uploaded: uploadedUrls,
|
|
85
85
|
}, null, 2) + '\n');
|
|
86
86
|
return;
|
|
@@ -102,7 +102,7 @@ export function registerPrice(program) {
|
|
|
102
102
|
}
|
|
103
103
|
console.log();
|
|
104
104
|
console.log(chalk.gray('Run it: ') + chalk.cyan(`wavespeed run ${modelArg}` + (opts.prompt ? ` -p "${opts.prompt}"` : '')));
|
|
105
|
-
console.log(chalk.gray('
|
|
105
|
+
console.log(chalk.gray('Playground: ') + chalk.cyan(modelPlaygroundUrl(resolved.model)));
|
|
106
106
|
console.log(chalk.gray(disclaimer));
|
|
107
107
|
console.log();
|
|
108
108
|
}
|
package/dist/commands/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { fetchModels
|
|
3
|
+
import { fetchModels } from '../lib/api.js';
|
|
4
4
|
function getRequestSchema(model) {
|
|
5
5
|
return model.api_schema?.api_schemas?.[0]?.request_schema;
|
|
6
6
|
}
|
|
@@ -116,7 +116,6 @@ export function registerSchema(program) {
|
|
|
116
116
|
chalk.white(model.type ?? 'unknown') +
|
|
117
117
|
(model.base_price !== undefined ? chalk.gray(' base price: ') + chalk.green(`$${model.base_price}`) : ''));
|
|
118
118
|
console.log(chalk.gray(' page: ') + chalk.cyan(`https://wavespeed.ai/models/${model.model_id}`));
|
|
119
|
-
console.log(chalk.gray(' docs: ') + chalk.cyan(modelDocsUrl(model.model_id)));
|
|
120
119
|
if (model.description) {
|
|
121
120
|
console.log(wrap(model.description, ' '));
|
|
122
121
|
}
|
package/dist/commands/skill.js
CHANGED
|
@@ -2,6 +2,11 @@ import chalk from 'chalk';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
function skillBody() {
|
|
6
|
+
if (typeof __WS_SKILL__ === 'string')
|
|
7
|
+
return __WS_SKILL__;
|
|
8
|
+
return fs.readFileSync(skillSourcePath(), 'utf8');
|
|
9
|
+
}
|
|
5
10
|
function skillSourcePath() {
|
|
6
11
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
7
12
|
// dist/commands/skill.js -> ../../skills/SKILL.md
|
|
@@ -16,7 +21,7 @@ export function writeSkills(cwd = process.cwd()) {
|
|
|
16
21
|
return SKILL_ROOTS.map((root) => {
|
|
17
22
|
const dest = path.join(cwd, root, 'skills', 'wavespeed', 'SKILL.md');
|
|
18
23
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
19
|
-
fs.
|
|
24
|
+
fs.writeFileSync(dest, skillBody());
|
|
20
25
|
return dest;
|
|
21
26
|
});
|
|
22
27
|
}
|
|
@@ -34,7 +39,7 @@ export function registerSkill(program) {
|
|
|
34
39
|
.option('--print', 'Print the skill body to stdout instead of writing a file')
|
|
35
40
|
.action((opts) => {
|
|
36
41
|
if (opts.print) {
|
|
37
|
-
process.stdout.write(
|
|
42
|
+
process.stdout.write(skillBody());
|
|
38
43
|
return;
|
|
39
44
|
}
|
|
40
45
|
const dests = writeSkills();
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { IS_STANDALONE, PACKAGE_NAME, VERSION, assetName, isNewer } from '../lib/version.js';
|
|
8
|
+
import { emitJson, emitJsonError, info, setJsonMode } from '../lib/log.js';
|
|
9
|
+
const REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;
|
|
10
|
+
const RELEASES_URL = 'https://github.com/WaveSpeedAI/wavespeed-cli/releases';
|
|
11
|
+
// The standalone executable is distributed through GitHub Releases (the npm
|
|
12
|
+
// publish workflow creates the release right after publishing).
|
|
13
|
+
async function fetchLatestReleaseVersion() {
|
|
14
|
+
const res = await fetch('https://api.github.com/repos/WaveSpeedAI/wavespeed-cli/releases/latest', {
|
|
15
|
+
headers: { accept: 'application/vnd.github+json', 'user-agent': 'wavespeed-cli' },
|
|
16
|
+
signal: AbortSignal.timeout(15_000),
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
throw new Error(`GitHub releases returned HTTP ${res.status}`);
|
|
20
|
+
const tag = (await res.json()).tag_name;
|
|
21
|
+
if (!tag)
|
|
22
|
+
throw new Error('GitHub release response had no tag');
|
|
23
|
+
return tag.replace(/^v/, '');
|
|
24
|
+
}
|
|
25
|
+
async function download(url) {
|
|
26
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(300_000), redirect: 'follow' });
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`download of ${url} failed: HTTP ${res.status}`);
|
|
29
|
+
return Buffer.from(await res.arrayBuffer());
|
|
30
|
+
}
|
|
31
|
+
/** Replace the running executable with the release asset, after checking its SHA-256. */
|
|
32
|
+
async function replaceExecutable(version) {
|
|
33
|
+
const asset = assetName();
|
|
34
|
+
const base = `${RELEASES_URL}/download/v${version}`;
|
|
35
|
+
const [bin, sums] = await Promise.all([download(`${base}/${asset}`), download(`${base}/SHA256SUMS`)]);
|
|
36
|
+
const expected = sums
|
|
37
|
+
.toString('utf8')
|
|
38
|
+
.split('\n')
|
|
39
|
+
.map((l) => l.trim().split(/\s+/))
|
|
40
|
+
.find((p) => p[1]?.replace(/^\*/, '') === asset)?.[0];
|
|
41
|
+
const actual = crypto.createHash('sha256').update(bin).digest('hex');
|
|
42
|
+
if (!expected || expected.toLowerCase() !== actual)
|
|
43
|
+
throw new Error(`checksum mismatch for ${asset}; not installing`);
|
|
44
|
+
const exe = process.execPath;
|
|
45
|
+
const next = `${exe}.new`;
|
|
46
|
+
fs.writeFileSync(next, bin, { mode: 0o755 });
|
|
47
|
+
if (process.platform === 'win32') {
|
|
48
|
+
// A running .exe cannot be overwritten but can be renamed out of the way.
|
|
49
|
+
const old = `${exe}.old`;
|
|
50
|
+
fs.rmSync(old, { force: true });
|
|
51
|
+
fs.renameSync(exe, old);
|
|
52
|
+
}
|
|
53
|
+
fs.renameSync(next, exe);
|
|
54
|
+
}
|
|
55
|
+
export async function fetchLatestVersion() {
|
|
56
|
+
if (IS_STANDALONE)
|
|
57
|
+
return fetchLatestReleaseVersion();
|
|
58
|
+
const res = await fetch(REGISTRY_URL, {
|
|
59
|
+
headers: { accept: 'application/json' },
|
|
60
|
+
signal: AbortSignal.timeout(15_000),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
throw new Error(`npm registry returned HTTP ${res.status}`);
|
|
64
|
+
const body = (await res.json());
|
|
65
|
+
if (!body.version)
|
|
66
|
+
throw new Error('npm registry response had no version');
|
|
67
|
+
return body.version;
|
|
68
|
+
}
|
|
69
|
+
function runNpmInstall() {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
// Child output goes to stderr so `--json` keeps stdout clean.
|
|
72
|
+
const child = spawn('npm', ['install', '-g', `${PACKAGE_NAME}@latest`], {
|
|
73
|
+
stdio: ['ignore', 2, 2],
|
|
74
|
+
shell: process.platform === 'win32', // npm is npm.cmd on Windows
|
|
75
|
+
});
|
|
76
|
+
child.on('error', reject);
|
|
77
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** Skill files written by `wavespeed skill install` are a snapshot; flag the ones in this directory. */
|
|
81
|
+
function installedSkillRoots(cwd = process.cwd()) {
|
|
82
|
+
return ['.claude', '.agents']
|
|
83
|
+
.map((root) => path.join(root, 'skills', 'wavespeed', 'SKILL.md'))
|
|
84
|
+
.filter((rel) => fs.existsSync(path.join(cwd, rel)));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Other `wavespeed` executables on PATH besides the one running (e.g. an npm
|
|
88
|
+
* install shadowing a standalone one). Upgrading one of them while the other
|
|
89
|
+
* wins on PATH makes the upgrade look like it did nothing.
|
|
90
|
+
*/
|
|
91
|
+
export function otherInstalls(pathEnv = process.env.PATH ?? '', self = process.argv[1] ?? '') {
|
|
92
|
+
const names = process.platform === 'win32' ? ['wavespeed.exe', 'wavespeed.cmd', 'wavespeed.ps1'] : ['wavespeed'];
|
|
93
|
+
const real = (p) => { try {
|
|
94
|
+
return fs.realpathSync(p);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
} };
|
|
99
|
+
const mine = new Set([real(process.execPath), real(self)].filter(Boolean));
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const found = [];
|
|
102
|
+
for (const dir of pathEnv.split(path.delimiter).filter(Boolean)) {
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
const full = path.join(dir, name);
|
|
105
|
+
const r = real(full);
|
|
106
|
+
if (!r || seen.has(r))
|
|
107
|
+
continue;
|
|
108
|
+
seen.add(r);
|
|
109
|
+
// The npm shim and the package entry both count as "this" install.
|
|
110
|
+
if (!mine.has(r) && !(!IS_STANDALONE && r.includes(`${path.sep}@wavespeed${path.sep}cli${path.sep}`)))
|
|
111
|
+
found.push(full);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
function warnOtherInstalls(json) {
|
|
117
|
+
const others = otherInstalls();
|
|
118
|
+
if (others.length && !json) {
|
|
119
|
+
info(chalk.yellow('Note: another wavespeed is also on your PATH, so the version you run may not be the one just upgraded:'));
|
|
120
|
+
for (const o of others)
|
|
121
|
+
info(chalk.gray(' ' + o.replace(os.homedir(), '~')));
|
|
122
|
+
}
|
|
123
|
+
return others;
|
|
124
|
+
}
|
|
125
|
+
export function registerUpgrade(program) {
|
|
126
|
+
program
|
|
127
|
+
.command('upgrade')
|
|
128
|
+
.alias('update')
|
|
129
|
+
.description('Upgrade the CLI to the latest published version')
|
|
130
|
+
.option('--check', 'Only report whether a newer version exists; do not install')
|
|
131
|
+
.option('--json', 'Emit JSON: {current, latest, update_available, upgraded}')
|
|
132
|
+
.action(async (opts) => {
|
|
133
|
+
setJsonMode(!!opts.json);
|
|
134
|
+
const fail = (message) => {
|
|
135
|
+
if (opts.json)
|
|
136
|
+
emitJsonError(message, { current: VERSION });
|
|
137
|
+
else
|
|
138
|
+
console.error(chalk.red('Error: ') + message);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
};
|
|
141
|
+
let latest;
|
|
142
|
+
try {
|
|
143
|
+
latest = await fetchLatestVersion();
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return fail(`Could not check for updates: ${err.message ?? String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
const updateAvailable = isNewer(latest, VERSION);
|
|
149
|
+
let others = [];
|
|
150
|
+
if (!updateAvailable || opts.check) {
|
|
151
|
+
if (opts.json) {
|
|
152
|
+
emitJson({ current: VERSION, latest, update_available: updateAvailable, upgraded: false, other_installs: otherInstalls() });
|
|
153
|
+
}
|
|
154
|
+
else if (!updateAvailable) {
|
|
155
|
+
info(chalk.green(`Already up to date (v${VERSION}).`));
|
|
156
|
+
warnOtherInstalls(opts.json);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
info(`Update available: v${VERSION} → ${chalk.bold('v' + latest)}. Run ${chalk.cyan('wavespeed upgrade')}.`);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
info(chalk.gray(`Upgrading v${VERSION} → v${latest}…`));
|
|
164
|
+
if (IS_STANDALONE) {
|
|
165
|
+
// Installed by a package manager: let it own the upgrade.
|
|
166
|
+
const managed = /[\\/](Cellar|homebrew|Homebrew|WinGet|scoop)[\\/]/.exec(process.execPath);
|
|
167
|
+
if (managed) {
|
|
168
|
+
return fail(`This copy is managed by ${managed[1]}; upgrade it with your package manager (e.g. brew upgrade wavespeed).`);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await replaceExecutable(latest);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
return fail(`Could not replace ${process.execPath}: ${err.message ?? String(err)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
let code;
|
|
179
|
+
try {
|
|
180
|
+
code = await runNpmInstall();
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
return fail(`Could not run npm: ${err.message ?? String(err)}`);
|
|
184
|
+
}
|
|
185
|
+
if (code !== 0) {
|
|
186
|
+
return fail(`npm install -g exited with code ${code}. If it is a permissions error, fix npm's prefix or re-run with sudo: npm install -g ${PACKAGE_NAME}@latest`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
others = warnOtherInstalls(opts.json);
|
|
190
|
+
const skills = installedSkillRoots();
|
|
191
|
+
if (opts.json) {
|
|
192
|
+
emitJson({ current: VERSION, latest, update_available: true, upgraded: true, stale_skill_files: skills, other_installs: others });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
info(chalk.green(`Upgraded to v${latest}.`));
|
|
196
|
+
if (skills.length) {
|
|
197
|
+
info(chalk.gray(' Skill files here are from the old version: ') + chalk.cyan('wavespeed skill install') + chalk.gray(' to refresh.'));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
package/dist/lib/api.js
CHANGED
|
@@ -130,12 +130,9 @@ async function apiPost(path, body, opts = {}) {
|
|
|
130
130
|
export async function fetchBalance() {
|
|
131
131
|
return apiGet('/api/v3/balance');
|
|
132
132
|
}
|
|
133
|
-
// The model's page on the
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
export function modelDocsUrl(modelId) {
|
|
137
|
-
const provider = modelId.split('/')[0];
|
|
138
|
-
return `https://wavespeed.ai/docs/docs-api/${provider}/${modelId.replace('wavespeed-ai/', '').split('/').join('-')}`;
|
|
133
|
+
// The model's page on the site, where it can be tried in the playground.
|
|
134
|
+
export function modelPlaygroundUrl(modelId) {
|
|
135
|
+
return `https://wavespeed.ai/models/${modelId}`;
|
|
139
136
|
}
|
|
140
137
|
export async function fetchPricing(modelId, inputs) {
|
|
141
138
|
// /model/price is the documented pricing endpoint; /model/pricing is a
|
|
@@ -2,24 +2,9 @@
|
|
|
2
2
|
// for the CLI's direct REST calls, matching the convention wavespeed-desktop
|
|
3
3
|
// uses. Calls that go through the `wavespeed` SDK get the same headers from
|
|
4
4
|
// the SDK itself (via the clientName option in client.ts).
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
5
|
import os from 'node:os';
|
|
8
|
-
import {
|
|
6
|
+
import { VERSION } from './version.js';
|
|
9
7
|
export const CLI_CLIENT_NAME = 'wavespeed-cli';
|
|
10
|
-
// Read version from package.json at runtime so a bump in package.json is
|
|
11
|
-
// reflected automatically (same approach as cli.ts).
|
|
12
|
-
function cliVersion() {
|
|
13
|
-
try {
|
|
14
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
// dist/lib/client-headers.js → ../../package.json
|
|
16
|
-
const pkgPath = path.resolve(here, '..', '..', 'package.json');
|
|
17
|
-
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return '0.0.0';
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
8
|
// Lowercase OS name: darwin / linux / windows (win32 is reported as windows).
|
|
24
9
|
function operatingSystem() {
|
|
25
10
|
const platform = os.platform();
|
|
@@ -34,7 +19,7 @@ export function resolveClientName() {
|
|
|
34
19
|
export function clientAttributionHeaders() {
|
|
35
20
|
return {
|
|
36
21
|
'X-Client-Name': resolveClientName(),
|
|
37
|
-
'X-Client-Version':
|
|
22
|
+
'X-Client-Version': VERSION,
|
|
38
23
|
'X-Client-OS': operatingSystem(),
|
|
39
24
|
};
|
|
40
25
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
export const PACKAGE_NAME = '@wavespeed/cli';
|
|
5
|
+
/** True when running as the standalone executable rather than under Node + npm. */
|
|
6
|
+
export const IS_STANDALONE = typeof __WS_VERSION__ === 'string';
|
|
7
|
+
// Otherwise read package.json at runtime so a bump is reflected automatically
|
|
8
|
+
// (no second place to keep in sync).
|
|
9
|
+
export const VERSION = IS_STANDALONE ? __WS_VERSION__ : (() => {
|
|
10
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
// dist/lib/version.js (or src/lib/version.ts) → ../../package.json
|
|
12
|
+
const pkgPath = path.resolve(here, '..', '..', 'package.json');
|
|
13
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
|
|
14
|
+
})();
|
|
15
|
+
/** Parse "1.2.3" (optionally "v"-prefixed / with a -prerelease or +build tail) into numbers. */
|
|
16
|
+
function parse(v) {
|
|
17
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
|
|
18
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
19
|
+
}
|
|
20
|
+
/** True when `latest` is strictly newer than `current`. Unparseable input → false. */
|
|
21
|
+
export function isNewer(latest, current) {
|
|
22
|
+
const a = parse(latest);
|
|
23
|
+
const b = parse(current);
|
|
24
|
+
if (!a || !b)
|
|
25
|
+
return false;
|
|
26
|
+
for (let i = 0; i < 3; i++) {
|
|
27
|
+
if (a[i] !== b[i])
|
|
28
|
+
return a[i] > b[i];
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/** Release asset name for this platform, e.g. wavespeed-linux-x64, wavespeed-windows-x64.exe. */
|
|
33
|
+
export function assetName(platform = process.platform, arch = process.arch) {
|
|
34
|
+
const os = platform === 'win32' ? 'windows' : platform;
|
|
35
|
+
return `wavespeed-${os}-${arch}${platform === 'win32' ? '.exe' : ''}`;
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wavespeed/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.15",
|
|
4
4
|
"description": "WaveSpeed CLI — one command to run any WaveSpeed AI model from your terminal.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^20.14.0",
|
|
63
|
+
"esbuild": "^0.28.2",
|
|
64
|
+
"postject": "^1.0.0-alpha.6",
|
|
63
65
|
"prettier": "^3.3.0",
|
|
64
66
|
"tsx": "^4.19.0",
|
|
65
67
|
"typescript": "^5.5.0",
|