@wavespeed/cli 0.4.17 → 0.4.18

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
@@ -141,7 +141,7 @@ wavespeed logout Clear stored API key
141
141
  wavespeed status Show masked key, base URL, useful links
142
142
  wavespeed config [--default-model …] View / update CLI defaults
143
143
  wavespeed upgrade [--check] [--json] Upgrade to the latest version (--check only reports)
144
- wavespeed setup [agent] Point claude, codex, opencode, openclaw, hermes or cline at the WaveSpeed LLM gateway (--dry-run, --undo, --model)
144
+ wavespeed setup [agent] Point claude, codex, opencode, openclaw, hermes, cline, qwen or miniagent at the WaveSpeed LLM gateway (--dry-run, --undo, --model)
145
145
 
146
146
  # generation
147
147
  wavespeed run [model|alias] -p "…" Run any model or alias (uses defaultModel if omitted)
@@ -8,7 +8,7 @@ import JSON5 from 'json5';
8
8
  import { getApiKey, userConfig } from '../lib/config.js';
9
9
  import { clientAttributionHeaders } from '../lib/client-headers.js';
10
10
  import { emitJson, emitJsonError, info, setJsonMode } from '../lib/log.js';
11
- import { DEFAULT_MODEL, KEY_ENV, LLM_BASE_URL, applyCodexToml, applyHermesYaml, applyJson, claudeEdits, openclawEdits, opencodeEdits, revertCodexToml, revertHermesYaml, revertJson, validateModel, } from '../lib/setup-agents.js';
11
+ import { DEFAULT_MODEL, KEY_ENV, LLM_BASE_URL, applyCodexToml, applyHermesYaml, applyJson, applyYamlScalars, claudeEdits, miniAgentValues, openclawEdits, opencodeEdits, qwenEdits, revertCodexToml, revertHermesYaml, revertJson, revertYamlScalars, validateModel, } from '../lib/setup-agents.js';
12
12
  const home = os.homedir();
13
13
  const AGENTS = [
14
14
  { id: 'claude', name: 'Claude Code', file: path.join(home, '.claude', 'settings.json'), command: 'claude', kind: 'json', keyInFile: true },
@@ -16,6 +16,8 @@ const AGENTS = [
16
16
  { id: 'opencode', name: 'OpenCode', file: path.join(home, '.config', 'opencode', 'opencode.json'), command: 'opencode', kind: 'json', keyInFile: false },
17
17
  { id: 'openclaw', name: 'OpenClaw', file: path.join(home, '.openclaw', 'openclaw.json'), command: 'openclaw', kind: 'json5', keyInFile: false },
18
18
  { id: 'hermes', name: 'Hermes', file: path.join(home, '.hermes', 'config.yaml'), command: 'hermes', kind: 'yaml', keyInFile: false },
19
+ { id: 'qwen', name: 'Qwen Code', file: path.join(home, '.qwen', 'settings.json'), command: 'qwen', kind: 'json', keyInFile: false },
20
+ { id: 'miniagent', name: 'Mini-Agent', file: path.join(home, '.mini-agent', 'config', 'config.yaml'), command: 'mini-agent', kind: 'yamlscalars', keyInFile: true },
19
21
  // Cline's CLI keeps provider settings (including the key) in this file; we write it only through `cline auth`.
20
22
  { id: 'cline', name: 'Cline', file: path.join(home, '.cline', 'data', 'settings', 'providers.json'), command: 'cline', kind: 'cline', keyInFile: true },
21
23
  ];
@@ -82,7 +84,7 @@ function envHint() {
82
84
  export function registerSetup(program) {
83
85
  program
84
86
  .command('setup [agent]')
85
- .description('Point a coding agent (claude, codex, opencode, openclaw, hermes, cline) at the WaveSpeed LLM gateway')
87
+ .description('Point a coding agent (claude, codex, opencode, openclaw, hermes, cline, qwen, miniagent) at the WaveSpeed LLM gateway')
86
88
  .option('--model <id>', `Model to use (default: ${DEFAULT_MODEL})`)
87
89
  .option('--dry-run', 'Show what would change without writing anything')
88
90
  .option('--undo', "Restore the agent's config to what it was before setup")
@@ -142,6 +144,16 @@ export function registerSetup(program) {
142
144
  forget();
143
145
  }
144
146
  }
147
+ else if (agent.kind === 'yamlscalars') {
148
+ const after = revertYamlScalars(fs.readFileSync(state.file, 'utf8'), state.previous);
149
+ if (!opts.dryRun) {
150
+ if (after.trim() === '')
151
+ fs.rmSync(state.file, { force: true });
152
+ else
153
+ fs.writeFileSync(state.file, after);
154
+ forget();
155
+ }
156
+ }
145
157
  else if (agent.kind === 'yaml') {
146
158
  const after = revertHermesYaml(fs.readFileSync(state.file, 'utf8'), state.previous);
147
159
  if (!opts.dryRun) {
@@ -188,6 +200,11 @@ export function registerSetup(program) {
188
200
  next = r.text;
189
201
  previous = r.previous;
190
202
  }
203
+ else if (agent.kind === 'yamlscalars') {
204
+ const r = applyYamlScalars(fs.existsSync(agent.file) ? fs.readFileSync(agent.file, 'utf8') : '', miniAgentValues(model, apiKey));
205
+ next = r.text;
206
+ previous = r.previous;
207
+ }
191
208
  else if (agent.kind === 'cline') {
192
209
  previous = { existed: fs.existsSync(agent.file) };
193
210
  if (!onPath('cline'))
@@ -195,7 +212,7 @@ export function registerSetup(program) {
195
212
  }
196
213
  else {
197
214
  const base = readJson(agent.file, agent.kind === 'json5');
198
- const edits = agent.id === 'claude' ? claudeEdits(model, apiKey) : agent.id === 'openclaw' ? openclawEdits(model, base) : opencodeEdits(model);
215
+ const edits = agent.id === 'claude' ? claudeEdits(model, apiKey) : agent.id === 'openclaw' ? openclawEdits(model, base) : agent.id === 'qwen' ? qwenEdits(model) : opencodeEdits(model);
199
216
  const r = applyJson(base, edits);
200
217
  previous = r.previous;
201
218
  next = JSON.stringify(r.doc, null, 2) + '\n';
@@ -109,6 +109,66 @@ export function openclawEdits(model, doc) {
109
109
  edits.push({ path: ['agents', 'defaults', 'models', `wavespeed/${m}`], value: {} });
110
110
  return edits;
111
111
  }
112
+ export function qwenEdits(model) {
113
+ const m = validateModel(model);
114
+ return [
115
+ { path: ['modelProviders', 'wavespeed'], value: [{ id: m, name: m, envKey: KEY_ENV, baseUrl: LLM_BASE_URL }] },
116
+ { path: ['providerProtocol', 'wavespeed'], value: 'openai' },
117
+ { path: ['security', 'auth', 'selectedType'], value: 'openai' },
118
+ { path: ['model', 'name'], value: m },
119
+ ];
120
+ }
121
+ // ---------- Mini-Agent (flat YAML scalars; the key lives in the file) ----------
122
+ export function miniAgentValues(model, apiKey) {
123
+ return { api_key: apiKey, api_base: LLM_BASE_URL, model: validateModel(model), provider: 'openai' };
124
+ }
125
+ /** Set top-level `key: "value"` pairs in place. Returns the raw text each key held before (null = absent). */
126
+ export function applyYamlScalars(input, values) {
127
+ let text = input;
128
+ if (text !== '' && !text.endsWith('\n'))
129
+ text += '\n';
130
+ const doc = parseDocument(text);
131
+ if (doc.errors.length)
132
+ throw new Error(`config.yaml is not valid YAML: ${doc.errors[0].message}`);
133
+ const root = doc.contents;
134
+ // null (not undefined) marks "absent": the state is stored as JSON, which drops undefined values.
135
+ const previous = {};
136
+ const edits = [];
137
+ let tail = '';
138
+ for (const [key, value] of Object.entries(values)) {
139
+ const node = root?.get?.(key, true);
140
+ const quoted = JSON.stringify(value);
141
+ if (node?.range) {
142
+ previous[key] = text.slice(node.range[0], node.range[1]);
143
+ edits.push({ start: node.range[0], end: node.range[1], text: quoted });
144
+ }
145
+ else {
146
+ previous[key] = null;
147
+ tail += `${key}: ${quoted}\n`;
148
+ }
149
+ }
150
+ return { text: [...edits].sort((a, b) => b.start - a.start).reduce((t, e) => t.slice(0, e.start) + e.text + t.slice(e.end), text) + tail, previous };
151
+ }
152
+ export function revertYamlScalars(input, previous) {
153
+ let text = input;
154
+ const doc = parseDocument(text);
155
+ const root = doc.contents;
156
+ const edits = [];
157
+ for (const [key, raw] of Object.entries(previous)) {
158
+ const node = root?.get?.(key, true);
159
+ if (!node?.range)
160
+ continue;
161
+ if (raw !== null) {
162
+ edits.push({ start: node.range[0], end: node.range[1], text: raw });
163
+ }
164
+ else {
165
+ const pair = root.items.find((p) => p.key?.value === key);
166
+ const ks = pair.key.range[0];
167
+ edits.push({ start: lineStart(text, ks), end: lineEnd(text, node.range[1]), text: '' });
168
+ }
169
+ }
170
+ return [...edits].sort((a, b) => b.start - a.start).reduce((t, e) => t.slice(0, e.start) + e.text + t.slice(e.end), text);
171
+ }
112
172
  const lineStart = (t, off) => t.lastIndexOf('\n', off - 1) + 1;
113
173
  const lineEnd = (t, off) => {
114
174
  const i = t.indexOf('\n', off);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavespeed/cli",
3
- "version": "0.4.17",
3
+ "version": "0.4.18",
4
4
  "description": "WaveSpeed CLI — one command to run any WaveSpeed AI model from your terminal.",
5
5
  "publishConfig": {
6
6
  "access": "public"