@wavespeed/cli 0.2.3 → 0.2.5

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.
@@ -33,6 +33,10 @@ async function readClipboardKey() {
33
33
  async function validateKey(apiKey, baseUrl) {
34
34
  // The platform doesn't expose a dedicated /whoami yet, so we ping a known
35
35
  // authenticated endpoint and treat anything that isn't 401/403 as "key works".
36
+ // Note this proves the key is live, not that it can run models: /balance is
37
+ // deliberately open to every key regardless of the creator's org role, so a
38
+ // Billing-created key logs in here and only fails at `run`, which now
39
+ // surfaces the API's explanation of why.
36
40
  let res = null;
37
41
  try {
38
42
  res = await fetch(`${baseUrl}/api/v3/balance`, {
package/dist/lib/api.js CHANGED
@@ -47,7 +47,7 @@ export async function fetchModels(opts = {}) {
47
47
  headers: { Authorization: `Bearer ${apiKey}` },
48
48
  });
49
49
  if (!res.ok) {
50
- throw new Error(`GET /api/v3/models failed: ${res.status} ${res.statusText}`);
50
+ throw await httpError(res, 'GET', '/api/v3/models');
51
51
  }
52
52
  const json = (await res.json());
53
53
  if (json.code !== 200) {
@@ -72,10 +72,29 @@ function authHeaders() {
72
72
  throw new Error('No API key configured. Run `wavespeed login`.');
73
73
  return { Authorization: `Bearer ${apiKey}` };
74
74
  }
75
+ // Non-2xx responses still carry the platform's error envelope, and for
76
+ // permission failures that body is the whole point: it names the role that
77
+ // created the key and a role whose key would work. Throwing only the status
78
+ // line turned every 403 into an unactionable "403 Forbidden".
79
+ async function httpError(res, method, path) {
80
+ let detail = '';
81
+ try {
82
+ const body = (await res.json());
83
+ if (body?.message) {
84
+ detail = body.error_code ? `${body.message} [${body.error_code}]` : body.message;
85
+ }
86
+ }
87
+ catch {
88
+ /* non-JSON body (gateway error page, empty response) — fall back below */
89
+ }
90
+ if (detail)
91
+ return new Error(detail);
92
+ return new Error(`${method} ${path} failed: ${res.status} ${res.statusText}`);
93
+ }
75
94
  async function apiGet(path) {
76
95
  const res = await fetch(`${getBaseUrl()}${path}`, { headers: authHeaders() });
77
96
  if (!res.ok)
78
- throw new Error(`GET ${path} failed: ${res.status} ${res.statusText}`);
97
+ throw await httpError(res, 'GET', path);
79
98
  const json = (await res.json());
80
99
  if (json.code !== 200)
81
100
  throw new Error(json.message || `API returned code ${json.code}`);
@@ -88,7 +107,7 @@ async function apiPost(path, body) {
88
107
  body: JSON.stringify(body),
89
108
  });
90
109
  if (!res.ok)
91
- throw new Error(`POST ${path} failed: ${res.status} ${res.statusText}`);
110
+ throw await httpError(res, 'POST', path);
92
111
  const json = (await res.json());
93
112
  if (json.code !== 200)
94
113
  throw new Error(json.message || `API returned code ${json.code}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavespeed/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "WaveSpeed CLI — one command to run any WaveSpeed AI model from your terminal.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -56,7 +56,7 @@
56
56
  "conf": "^13.0.0",
57
57
  "open": "^10.1.0",
58
58
  "ora": "^8.1.0",
59
- "wavespeed": "^0.2.1"
59
+ "wavespeed": "^0.2.6"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^20.14.0",