clawd-models 1.0.0
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 +70 -0
- package/bin/clawd-models.js +549 -0
- package/docs/openclaw.example.json +164 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# clawd-models
|
|
2
|
+
|
|
3
|
+
CLI tool to manage OpenClaw model configurations.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node bin/clawd-models.js <command>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
### Core
|
|
20
|
+
| Command | Description |
|
|
21
|
+
|---------|-------------|
|
|
22
|
+
| `init` | Initialize OpenClaw configuration with defaults |
|
|
23
|
+
| `view` | View current configuration file and path |
|
|
24
|
+
| `edit` | Edit configuration in default editor |
|
|
25
|
+
| `import -f <file>` | Import configuration from a JSON config file of `openclaw` |
|
|
26
|
+
| `export` | Export configuration to stdout |
|
|
27
|
+
|
|
28
|
+
### Provider Management
|
|
29
|
+
| Command | Description |
|
|
30
|
+
|---------|-------------|
|
|
31
|
+
| `providers:add -n <name> -u <url>` | Add a new model provider |
|
|
32
|
+
| `providers:remove -n <name>` | Remove a model provider |
|
|
33
|
+
| `providers:list` | List all configured providers |
|
|
34
|
+
|
|
35
|
+
### Model Management
|
|
36
|
+
| Command | Description |
|
|
37
|
+
|---------|-------------|
|
|
38
|
+
| `models:add -p <provider> -i <model-id> --name <name>` | Add a model to a provider |
|
|
39
|
+
| `models:remove -p <provider> -i <model-id>` | Remove a model from a provider |
|
|
40
|
+
| `model:list [--provider <name>]` | List all configured models |
|
|
41
|
+
|
|
42
|
+
Use `agents:set-default` in next section or refer to `openclaw models set` to set a default model for 'main' and all agents.
|
|
43
|
+
|
|
44
|
+
Refer to `openclaw agent --agent main -m <message>` to test the default model and agent.
|
|
45
|
+
|
|
46
|
+
### Agent Management
|
|
47
|
+
| Command | Description |
|
|
48
|
+
|---------|-------------|
|
|
49
|
+
| `agents:add -i <id>` | Add a new agent |
|
|
50
|
+
| `agents:remove -i <id>` | Remove an agent |
|
|
51
|
+
| `agents:list` | List all configured agents |
|
|
52
|
+
| `agents:set-default -a <agent> -m <provider>/<model-id>` | Set default model for an agent type |
|
|
53
|
+
Refer to `openclaw agents` and `openclaw agent --help` for more.
|
|
54
|
+
|
|
55
|
+
### Gateway Management
|
|
56
|
+
| Command | Description |
|
|
57
|
+
|---------|-------------|
|
|
58
|
+
| `gateway:view` | View the Gateway |
|
|
59
|
+
| `gateway:refresh-token` | Refresh(regenerate) gateway auth token |
|
|
60
|
+
Refer to `openclaw gateway status` for more.
|
|
61
|
+
|
|
62
|
+
### Auth Profile Management
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---------|-------------|
|
|
65
|
+
| `auth:add-profile -n <name>` | Add an auth profile |
|
|
66
|
+
| `auth:profiles` | List auth profiles configured |
|
|
67
|
+
|
|
68
|
+
## Configuration Location
|
|
69
|
+
|
|
70
|
+
`~/.openclaw/openclaw.json`
|
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const OPENCLAW_CONFIG_PATH = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
9
|
+
|
|
10
|
+
const CURRENT_VERSION = '2026.2.1';
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('clawd-models')
|
|
14
|
+
.description('CLI tool to manage OpenClaw model configurations')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
// ============ Core Commands ============
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.command('init')
|
|
21
|
+
.description('Initialize OpenClaw configuration with defaults')
|
|
22
|
+
.action(() => {
|
|
23
|
+
if (fs.existsSync(OPENCLAW_CONFIG_PATH)) {
|
|
24
|
+
console.log(`Configuration already exists at ${OPENCLAW_CONFIG_PATH}`);
|
|
25
|
+
console.log('Use "clawd-models edit" to modify or "clawd-models reset" to start fresh.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const initialConfig = {
|
|
30
|
+
meta: {
|
|
31
|
+
lastTouchedVersion: CURRENT_VERSION,
|
|
32
|
+
lastTouchedAt: new Date().toISOString()
|
|
33
|
+
},
|
|
34
|
+
wizard: {
|
|
35
|
+
lastRunAt: null,
|
|
36
|
+
lastRunVersion: null,
|
|
37
|
+
lastRunCommand: null,
|
|
38
|
+
lastRunMode: null
|
|
39
|
+
},
|
|
40
|
+
auth: {
|
|
41
|
+
profiles: {}
|
|
42
|
+
},
|
|
43
|
+
models: {
|
|
44
|
+
mode: 'merge',
|
|
45
|
+
providers: {}
|
|
46
|
+
},
|
|
47
|
+
agents: {
|
|
48
|
+
defaults: {
|
|
49
|
+
model: { primary: null },
|
|
50
|
+
models: {},
|
|
51
|
+
workspace: path.join(os.homedir(), '.openclaw', 'workspace'),
|
|
52
|
+
maxConcurrent: 4,
|
|
53
|
+
subagents: { maxConcurrent: 8 }
|
|
54
|
+
},
|
|
55
|
+
list: []
|
|
56
|
+
},
|
|
57
|
+
messages: {
|
|
58
|
+
ackReactionScope: 'group-mentions'
|
|
59
|
+
},
|
|
60
|
+
commands: {
|
|
61
|
+
native: 'auto',
|
|
62
|
+
nativeSkills: 'auto'
|
|
63
|
+
},
|
|
64
|
+
gateway: {
|
|
65
|
+
port: 18789,
|
|
66
|
+
mode: 'local',
|
|
67
|
+
bind: 'lan',
|
|
68
|
+
auth: {
|
|
69
|
+
mode: 'token',
|
|
70
|
+
token: generateToken()
|
|
71
|
+
},
|
|
72
|
+
tailscale: {
|
|
73
|
+
mode: 'off',
|
|
74
|
+
resetOnExit: false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
saveConfig(initialConfig);
|
|
80
|
+
console.log(`Initialized new configuration at ${OPENCLAW_CONFIG_PATH}`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
program
|
|
84
|
+
.command('view')
|
|
85
|
+
.description('View current configuration')
|
|
86
|
+
.action(() => {
|
|
87
|
+
const config = loadConfig();
|
|
88
|
+
console.log(JSON.stringify(config, null, 2));
|
|
89
|
+
console.log(`\n${OPENCLAW_CONFIG_PATH}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
program
|
|
93
|
+
.command('edit')
|
|
94
|
+
.description('Edit configuration in default editor')
|
|
95
|
+
.action(() => {
|
|
96
|
+
const config = loadConfig();
|
|
97
|
+
config.meta.lastTouchedVersion = CURRENT_VERSION;
|
|
98
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
99
|
+
saveConfig(config);
|
|
100
|
+
|
|
101
|
+
const editor = process.env.EDITOR || 'vi';
|
|
102
|
+
require('child_process').execSync(`${editor} "${OPENCLAW_CONFIG_PATH}"`, { stdio: 'inherit' });
|
|
103
|
+
|
|
104
|
+
console.log(`Configuration updated at ${OPENCLAW_CONFIG_PATH}`);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ============ Provider Commands ============
|
|
108
|
+
|
|
109
|
+
program
|
|
110
|
+
.command('providers:add')
|
|
111
|
+
.description('Add a new model provider')
|
|
112
|
+
.requiredOption('-n, --name <name>', 'Provider name (e.g., qiniu, minimax)')
|
|
113
|
+
.requiredOption('-u, --base-url <url>', 'Base API URL')
|
|
114
|
+
.option('--api <api-type>', 'API type (e.g., openai-completions, anthropic-messages)', 'openai-completions')
|
|
115
|
+
.option('--auth <auth-type>', 'Auth method (e.g., api-key, bearer)', 'api-key')
|
|
116
|
+
.option('--api-key <key>', 'API key for the provider')
|
|
117
|
+
.action((options) => {
|
|
118
|
+
const config = loadConfig();
|
|
119
|
+
config.models.providers = config.models.providers || {};
|
|
120
|
+
|
|
121
|
+
const provider = {
|
|
122
|
+
baseUrl: options.baseUrl,
|
|
123
|
+
api: options.api,
|
|
124
|
+
auth: options.auth,
|
|
125
|
+
models: []
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (options.apiKey) {
|
|
129
|
+
provider.apiKey = options.apiKey;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
config.models.providers[options.name] = provider;
|
|
133
|
+
|
|
134
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
135
|
+
saveConfig(config);
|
|
136
|
+
console.log(`Provider "${options.name}" added. Use "clawd-models models:add" to add models.`);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
program
|
|
140
|
+
.command('providers:remove')
|
|
141
|
+
.description('Remove a model provider')
|
|
142
|
+
.requiredOption('-n, --name <name>', 'Provider name')
|
|
143
|
+
.action((options) => {
|
|
144
|
+
const config = loadConfig();
|
|
145
|
+
if (config.models.providers && config.models.providers[options.name]) {
|
|
146
|
+
delete config.models.providers[options.name];
|
|
147
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
148
|
+
saveConfig(config);
|
|
149
|
+
console.log(`Provider "${options.name}" removed.`);
|
|
150
|
+
} else {
|
|
151
|
+
console.log(`Provider "${options.name}" not found.`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('providers:list')
|
|
157
|
+
.description('List all configured providers')
|
|
158
|
+
.action(() => {
|
|
159
|
+
const config = loadConfig();
|
|
160
|
+
const providers = config.models?.providers || {};
|
|
161
|
+
|
|
162
|
+
if (Object.keys(providers).length === 0) {
|
|
163
|
+
console.log('No providers configured.');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('Configured providers:');
|
|
168
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
169
|
+
console.log(`\n${name}:`);
|
|
170
|
+
console.log(` Base URL: ${provider.baseUrl}`);
|
|
171
|
+
console.log(` API: ${provider.api}`);
|
|
172
|
+
console.log(` Auth: ${provider.auth}`);
|
|
173
|
+
console.log(` Models: ${provider.models?.length || 0}`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ============ Model Commands ============
|
|
178
|
+
|
|
179
|
+
program
|
|
180
|
+
.command('models:add')
|
|
181
|
+
.description('Add a model to a provider')
|
|
182
|
+
.requiredOption('-p, --provider <provider>', 'Provider name')
|
|
183
|
+
.requiredOption('-i, --id <id>', 'Model ID (e.g., minimax/minimax-m2.1)')
|
|
184
|
+
.requiredOption('--name <name>', 'Display name')
|
|
185
|
+
.option('--api <api>', 'API type for model', 'openai-completions')
|
|
186
|
+
.option('--reasoning', 'Model has reasoning capability', false)
|
|
187
|
+
.option('--input <types>', 'Input types (comma-separated: text,image,audio,video)', 'text')
|
|
188
|
+
.option('--input-cost <cost>', 'Input cost per 1M tokens')
|
|
189
|
+
.option('--output-cost <cost>', 'Output cost per 1M tokens')
|
|
190
|
+
.option('--cache-read <cost>', 'Cache read cost per 1M tokens')
|
|
191
|
+
.option('--cache-write <cost>', 'Cache write cost per 1M tokens')
|
|
192
|
+
.option('--context <tokens>', 'Context window size', '200000')
|
|
193
|
+
.option('--max-tokens <tokens>', 'Max output tokens', '8192')
|
|
194
|
+
.action((options) => {
|
|
195
|
+
const config = loadConfig();
|
|
196
|
+
|
|
197
|
+
if (!config.models.providers || !config.models.providers[options.provider]) {
|
|
198
|
+
console.log(`Provider "${options.provider}" not found. Add it first with "clawd-models providers:add".`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const provider = config.models.providers[options.provider];
|
|
203
|
+
provider.models = provider.models || [];
|
|
204
|
+
|
|
205
|
+
const model = {
|
|
206
|
+
id: options.id,
|
|
207
|
+
name: options.name,
|
|
208
|
+
api: options.api,
|
|
209
|
+
reasoning: options.reasoning,
|
|
210
|
+
input: options.input.split(',').map(s => s.trim()),
|
|
211
|
+
cost: {
|
|
212
|
+
input: parseInt(options.inputCost) || 0,
|
|
213
|
+
output: parseInt(options.outputCost) || 0,
|
|
214
|
+
cacheRead: parseInt(options.cacheRead) || 0,
|
|
215
|
+
cacheWrite: parseInt(options.cacheWrite) || 0
|
|
216
|
+
},
|
|
217
|
+
contextWindow: parseInt(options.context),
|
|
218
|
+
maxTokens: parseInt(options.maxTokens)
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Check for duplicate
|
|
222
|
+
if (provider.models.find(m => m.id === options.id)) {
|
|
223
|
+
console.log(`Model "${options.id}" already exists in provider "${options.provider}".`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
provider.models.push(model);
|
|
228
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
229
|
+
saveConfig(config);
|
|
230
|
+
console.log(`Model "${options.id}" added to provider "${options.provider}".`);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
program
|
|
234
|
+
.command('models:remove')
|
|
235
|
+
.description('Remove a model from a provider')
|
|
236
|
+
.requiredOption('-p, --provider <provider>', 'Provider name')
|
|
237
|
+
.requiredOption('-i, --id <id>', 'Model ID')
|
|
238
|
+
.action((options) => {
|
|
239
|
+
const config = loadConfig();
|
|
240
|
+
|
|
241
|
+
if (!config.models.providers || !config.models.providers[options.provider]) {
|
|
242
|
+
console.log(`Provider "${options.provider}" not found.`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const provider = config.models.providers[options.provider];
|
|
247
|
+
const idx = provider.models?.findIndex(m => m.id === options.id);
|
|
248
|
+
|
|
249
|
+
if (idx === undefined || idx === -1) {
|
|
250
|
+
console.log(`Model "${options.id}" not found in provider "${options.provider}".`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
provider.models.splice(idx, 1);
|
|
255
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
256
|
+
saveConfig(config);
|
|
257
|
+
console.log(`Model "${options.id}" removed from provider "${options.provider}".`);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
program
|
|
261
|
+
.command('models:list')
|
|
262
|
+
.description('List all configured models')
|
|
263
|
+
.option('--provider <provider>', 'Filter by provider')
|
|
264
|
+
.action((options) => {
|
|
265
|
+
const config = loadConfig();
|
|
266
|
+
const providers = config.models?.providers || {};
|
|
267
|
+
|
|
268
|
+
if (options.provider) {
|
|
269
|
+
const provider = providers[options.provider];
|
|
270
|
+
if (!provider) {
|
|
271
|
+
console.log(`Provider "${options.provider}" not found.`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
console.log(`Models in "${options.provider}":`);
|
|
275
|
+
for (const model of (provider.models || [])) {
|
|
276
|
+
console.log(` ${model.id} (${model.name})`);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('All configured models:');
|
|
282
|
+
for (const [pname, provider] of Object.entries(providers)) {
|
|
283
|
+
console.log(`\n${pname}:`);
|
|
284
|
+
for (const model of (provider.models || [])) {
|
|
285
|
+
console.log(` ${model.id} (${model.name})`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// ============ Agent Commands ============
|
|
291
|
+
|
|
292
|
+
program
|
|
293
|
+
.command('agents:add')
|
|
294
|
+
.description('Add a new agent')
|
|
295
|
+
.requiredOption('-i, --id <id>', 'Agent ID')
|
|
296
|
+
.option('--name <name>', 'Display name')
|
|
297
|
+
.option('--model <model>', 'Default model (format: provider/model-id)')
|
|
298
|
+
.option('--workspace <path>', 'Workspace directory')
|
|
299
|
+
.option('--agent-dir <path>', 'Agent directory')
|
|
300
|
+
.action((options) => {
|
|
301
|
+
const config = loadConfig();
|
|
302
|
+
config.agents = config.agents || { defaults: {}, list: [] };
|
|
303
|
+
|
|
304
|
+
const existing = config.agents.list.find(a => a.id === options.id);
|
|
305
|
+
if (existing) {
|
|
306
|
+
console.log(`Agent "${options.id}" already exists.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const agent = { id: options.id };
|
|
311
|
+
if (options.name) agent.name = options.name;
|
|
312
|
+
if (options.model) agent.model = options.model;
|
|
313
|
+
if (options.workspace) agent.workspace = options.workspace;
|
|
314
|
+
if (options.agentDir) agent.agentDir = options.agentDir;
|
|
315
|
+
|
|
316
|
+
config.agents.list.push(agent);
|
|
317
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
318
|
+
saveConfig(config);
|
|
319
|
+
console.log(`Agent "${options.id}" added.`);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
program
|
|
323
|
+
.command('agents:remove')
|
|
324
|
+
.description('Remove an agent')
|
|
325
|
+
.requiredOption('-i, --id <id>', 'Agent ID')
|
|
326
|
+
.action((options) => {
|
|
327
|
+
const config = loadConfig();
|
|
328
|
+
if (!config.agents?.list) {
|
|
329
|
+
console.log('No agents configured.');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const idx = config.agents.list.findIndex(a => a.id === options.id);
|
|
334
|
+
if (idx === -1) {
|
|
335
|
+
console.log(`Agent "${options.id}" not found.`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
config.agents.list.splice(idx, 1);
|
|
340
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
341
|
+
saveConfig(config);
|
|
342
|
+
console.log(`Agent "${options.id}" removed.`);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
program
|
|
346
|
+
.command('agents:list')
|
|
347
|
+
.description('List all configured agents')
|
|
348
|
+
.action(() => {
|
|
349
|
+
const config = loadConfig();
|
|
350
|
+
const agents = config.agents?.list || [];
|
|
351
|
+
const defaults = config.agents?.defaults || {};
|
|
352
|
+
|
|
353
|
+
if (agents.length === 0) {
|
|
354
|
+
console.log('No agents configured.');
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
console.log('Configured agents:');
|
|
359
|
+
for (const agent of agents) {
|
|
360
|
+
console.log(`\n${agent.id}:`);
|
|
361
|
+
if (agent.name) console.log(` Name: ${agent.name}`);
|
|
362
|
+
|
|
363
|
+
if (agent.id === 'main' && defaults.model?.primary) {
|
|
364
|
+
console.log(` Model: ${defaults.model.primary}`);
|
|
365
|
+
} else if (agent.model) {
|
|
366
|
+
console.log(` Model: ${agent.model}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (agent.id === 'main') {
|
|
370
|
+
if (defaults.workspace) console.log(` Workspace: ${defaults.workspace}`);
|
|
371
|
+
if (defaults.maxConcurrent) console.log(` Max Concurrent: ${defaults.maxConcurrent}`);
|
|
372
|
+
if (defaults.subagents?.maxConcurrent) console.log(` Subagents Max Concurrent: ${defaults.subagents.maxConcurrent}`);
|
|
373
|
+
} else {
|
|
374
|
+
if (agent.workspace) console.log(` Workspace: ${agent.workspace}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (agent.agentDir) console.log(` Agent Dir: ${agent.agentDir}`);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
program
|
|
382
|
+
.command('agents:set-default')
|
|
383
|
+
.description('Set default model for an agent type')
|
|
384
|
+
.requiredOption('-a, --agent <agent>', 'Agent type (e.g., main, code)')
|
|
385
|
+
.requiredOption('-m, --model <model>', 'Model ID')
|
|
386
|
+
.action((options) => {
|
|
387
|
+
const config = loadConfig();
|
|
388
|
+
config.agents = config.agents || { defaults: {}, list: [] };
|
|
389
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
390
|
+
config.agents.defaults.model = config.agents.defaults.model || {};
|
|
391
|
+
config.agents.defaults.model.primary = options.model;
|
|
392
|
+
|
|
393
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
394
|
+
saveConfig(config);
|
|
395
|
+
console.log(`Default model for agent "${options.agent}" set to "${options.model}".`);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// ============ Gateway Commands ============
|
|
399
|
+
|
|
400
|
+
program
|
|
401
|
+
.command('gateway:view')
|
|
402
|
+
.description('View gateway configuration')
|
|
403
|
+
.action(() => {
|
|
404
|
+
const config = loadConfig();
|
|
405
|
+
const gw = config.gateway || {};
|
|
406
|
+
console.log(`Port: ${gw.port || 18789}`);
|
|
407
|
+
console.log(`Mode: ${gw.mode || 'local'}`);
|
|
408
|
+
console.log(`Bind: ${gw.bind || 'lan'}`);
|
|
409
|
+
console.log(`Auth Mode: ${gw.auth?.mode || 'token'}`);
|
|
410
|
+
if (gw.auth?.token) {
|
|
411
|
+
console.log(`Token: ${gw.auth.token.slice(0, 8)}...${gw.auth.token.slice(-4)}`);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
program
|
|
416
|
+
.command('gateway:refresh-token')
|
|
417
|
+
.description('Refresh gateway auth token')
|
|
418
|
+
.action(() => {
|
|
419
|
+
const config = loadConfig();
|
|
420
|
+
config.gateway = config.gateway || {};
|
|
421
|
+
config.gateway.auth = config.gateway.auth || { mode: 'token' };
|
|
422
|
+
config.gateway.auth.token = generateToken();
|
|
423
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
424
|
+
saveConfig(config);
|
|
425
|
+
console.log('Gateway auth token refreshed.');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// ============ Auth Profile Commands ============
|
|
429
|
+
|
|
430
|
+
program
|
|
431
|
+
.command('auth:profiles')
|
|
432
|
+
.description('List all auth profiles (configured and supported)')
|
|
433
|
+
.action(() => {
|
|
434
|
+
const config = loadConfig();
|
|
435
|
+
const providers = config.models?.providers || {};
|
|
436
|
+
const profiles = config.auth?.profiles || {};
|
|
437
|
+
|
|
438
|
+
// Show configured profiles
|
|
439
|
+
console.log('Configured in auth:');
|
|
440
|
+
if (Object.keys(profiles).length === 0) {
|
|
441
|
+
console.log(' (none)');
|
|
442
|
+
} else {
|
|
443
|
+
for (const [name, profile] of Object.entries(profiles)) {
|
|
444
|
+
console.log(` [configured] ${name}: provider=${profile.provider}, mode=${profile.mode}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Show supported profiles from providers
|
|
449
|
+
console.log('\nSupported (from providers):');
|
|
450
|
+
const providerNames = Object.keys(providers);
|
|
451
|
+
if (providerNames.length === 0) {
|
|
452
|
+
console.log(' (none)');
|
|
453
|
+
} else {
|
|
454
|
+
for (const [pname, provider] of Object.entries(providers)) {
|
|
455
|
+
const authMode = provider.auth || 'api-key';
|
|
456
|
+
const isConfigured = profiles[`${pname}:default`] ? '[default]' : '[provider]';
|
|
457
|
+
console.log(` ${isConfigured} ${pname}:default: provider=${pname}, mode=${authMode}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
program
|
|
463
|
+
.command('auth:add-profile')
|
|
464
|
+
.description('Add an auth profile')
|
|
465
|
+
.requiredOption('-n, --name <name>', 'Profile name (e.g., minimax:default)')
|
|
466
|
+
.requiredOption('-p, --provider <provider>', 'Auth provider')
|
|
467
|
+
.requiredOption('-m, --mode <mode>', 'Auth mode (api_key, bearer)')
|
|
468
|
+
.action((options) => {
|
|
469
|
+
const config = loadConfig();
|
|
470
|
+
config.auth = config.auth || { profiles: {} };
|
|
471
|
+
|
|
472
|
+
config.auth.profiles[options.name] = {
|
|
473
|
+
provider: options.provider,
|
|
474
|
+
mode: options.mode
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
config.meta.lastTouchedAt = new Date().toISOString();
|
|
478
|
+
saveConfig(config);
|
|
479
|
+
console.log(`Auth profile "${options.name}" added.`);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// ============ Import/Export ============
|
|
483
|
+
|
|
484
|
+
program
|
|
485
|
+
.command('import')
|
|
486
|
+
.description('Import configuration from a JSON file')
|
|
487
|
+
.requiredOption('-f, --file <file>', 'Path to JSON file')
|
|
488
|
+
.action((options) => {
|
|
489
|
+
if (!fs.existsSync(options.file)) {
|
|
490
|
+
console.error(`File not found: ${options.file}`);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const imported = fs.readFileSync(options.file, 'utf8');
|
|
495
|
+
const config = JSON.parse(imported);
|
|
496
|
+
|
|
497
|
+
config.meta = {
|
|
498
|
+
lastTouchedVersion: CURRENT_VERSION,
|
|
499
|
+
lastTouchedAt: new Date().toISOString()
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
saveConfig(config);
|
|
503
|
+
console.log(`Configuration imported from ${options.file}`);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
program
|
|
507
|
+
.command('export')
|
|
508
|
+
.description('Export configuration to stdout')
|
|
509
|
+
.action(() => {
|
|
510
|
+
const config = loadConfig();
|
|
511
|
+
console.log(JSON.stringify(config, null, 2));
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// ============ Helpers ============
|
|
515
|
+
|
|
516
|
+
function loadConfig() {
|
|
517
|
+
try {
|
|
518
|
+
if (fs.existsSync(OPENCLAW_CONFIG_PATH)) {
|
|
519
|
+
const content = fs.readFileSync(OPENCLAW_CONFIG_PATH, 'utf8');
|
|
520
|
+
return JSON.parse(content);
|
|
521
|
+
}
|
|
522
|
+
return {};
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.error(`Error loading configuration: ${error.message}`);
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function saveConfig(config) {
|
|
530
|
+
try {
|
|
531
|
+
const dir = path.dirname(OPENCLAW_CONFIG_PATH);
|
|
532
|
+
fs.ensureDirSync(dir);
|
|
533
|
+
fs.writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
534
|
+
} catch (error) {
|
|
535
|
+
console.error(`Error saving configuration: ${error.message}`);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function generateToken() {
|
|
541
|
+
const chars = 'abcdef0123456789';
|
|
542
|
+
let token = '';
|
|
543
|
+
for (let i = 0; i < 40; i++) {
|
|
544
|
+
token += chars[Math.floor(Math.random() * chars.length)];
|
|
545
|
+
}
|
|
546
|
+
return token;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
program.parse();
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"lastTouchedVersion": "2026.2.1",
|
|
4
|
+
"lastTouchedAt": "2026-02-09T04:36:41.216Z"
|
|
5
|
+
},
|
|
6
|
+
"wizard": {
|
|
7
|
+
"lastRunAt": "2026-02-04T04:45:03.494Z",
|
|
8
|
+
"lastRunVersion": "2026.2.1",
|
|
9
|
+
"lastRunCommand": "configure",
|
|
10
|
+
"lastRunMode": "remote"
|
|
11
|
+
},
|
|
12
|
+
"auth": {
|
|
13
|
+
"profiles": {
|
|
14
|
+
"minimax:default": {
|
|
15
|
+
"provider": "minimax",
|
|
16
|
+
"mode": "api_key"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"models": {
|
|
21
|
+
"mode": "merge",
|
|
22
|
+
"providers": {
|
|
23
|
+
"qiniu": {
|
|
24
|
+
"baseUrl": "https://api.qnaigc.com/v1",
|
|
25
|
+
"apiKey": "sk-b870e3bd48248a8eec6*****",
|
|
26
|
+
"auth": "api-key",
|
|
27
|
+
"api": "openai-completions",
|
|
28
|
+
"models": [
|
|
29
|
+
{
|
|
30
|
+
"id": "xiaomi/mimo-v2-flash",
|
|
31
|
+
"name": "Mi M2.1",
|
|
32
|
+
"api": "openai-completions",
|
|
33
|
+
"reasoning": false,
|
|
34
|
+
"input": [
|
|
35
|
+
"text"
|
|
36
|
+
],
|
|
37
|
+
"cost": {
|
|
38
|
+
"input": 15,
|
|
39
|
+
"output": 60,
|
|
40
|
+
"cacheRead": 2,
|
|
41
|
+
"cacheWrite": 10
|
|
42
|
+
},
|
|
43
|
+
"contextWindow": 200000,
|
|
44
|
+
"maxTokens": 8192
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "minimax/minimax-m2.1",
|
|
48
|
+
"name": "Qiniu Minimax M2.1",
|
|
49
|
+
"api": "openai-completions",
|
|
50
|
+
"reasoning": false,
|
|
51
|
+
"input": [
|
|
52
|
+
"text"
|
|
53
|
+
],
|
|
54
|
+
"cost": {
|
|
55
|
+
"input": 15,
|
|
56
|
+
"output": 60,
|
|
57
|
+
"cacheRead": 2,
|
|
58
|
+
"cacheWrite": 10
|
|
59
|
+
},
|
|
60
|
+
"contextWindow": 200000,
|
|
61
|
+
"maxTokens": 8192
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "meituan/longcat-flash-lite",
|
|
65
|
+
"name": "Qiniu Pony Alpha",
|
|
66
|
+
"api": "openai-completions",
|
|
67
|
+
"reasoning": false,
|
|
68
|
+
"input": [
|
|
69
|
+
"text"
|
|
70
|
+
],
|
|
71
|
+
"cost": {
|
|
72
|
+
"input": 15,
|
|
73
|
+
"output": 60,
|
|
74
|
+
"cacheRead": 2,
|
|
75
|
+
"cacheWrite": 10
|
|
76
|
+
},
|
|
77
|
+
"contextWindow": 200000,
|
|
78
|
+
"maxTokens": 8192
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"minimax": {
|
|
83
|
+
"baseUrl": "https://api.minimax.io/anthropic",
|
|
84
|
+
"api": "anthropic-messages",
|
|
85
|
+
"auth": "api-key",
|
|
86
|
+
"models": [
|
|
87
|
+
{
|
|
88
|
+
"id": "MiniMax-M2.1",
|
|
89
|
+
"name": "MiniMax M2.1",
|
|
90
|
+
"reasoning": false,
|
|
91
|
+
"input": [
|
|
92
|
+
"text"
|
|
93
|
+
],
|
|
94
|
+
"cost": {
|
|
95
|
+
"input": 15,
|
|
96
|
+
"output": 60,
|
|
97
|
+
"cacheRead": 2,
|
|
98
|
+
"cacheWrite": 10
|
|
99
|
+
},
|
|
100
|
+
"contextWindow": 200000,
|
|
101
|
+
"maxTokens": 8192
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"agents": {
|
|
108
|
+
"defaults": {
|
|
109
|
+
"model": {
|
|
110
|
+
"primary": "qiniu/xiaomi/mimo-v2-flash"
|
|
111
|
+
},
|
|
112
|
+
"models": {
|
|
113
|
+
"qiniu/xiaomi/mimo-v2-flash": {},
|
|
114
|
+
"qiniu/minimax/minimax-m2.1": {},
|
|
115
|
+
"qiniu/meituan/longcat-flash-lite": {}
|
|
116
|
+
},
|
|
117
|
+
"workspace": "/home/teric/dev/bot/openclaw/workspace",
|
|
118
|
+
"maxConcurrent": 4,
|
|
119
|
+
"subagents": {
|
|
120
|
+
"maxConcurrent": 8
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"list": [
|
|
124
|
+
{
|
|
125
|
+
"id": "main"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "abc",
|
|
129
|
+
"name": "abc",
|
|
130
|
+
"model": "qiniu/minimax/minimax-m2.1",
|
|
131
|
+
"workspace": "/home/teric/dev/bot/openclaw/workspace-abc",
|
|
132
|
+
"agentDir": "/home/teric/.openclaw/agents/abc/agent",
|
|
133
|
+
"model": "qiniu/xiaomi/mimo-v2-flash"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": "pony",
|
|
137
|
+
"name": "pony",
|
|
138
|
+
"model": "qiniu/meituan/longcat-flash-lite",
|
|
139
|
+
"workspace": "/home/teric/.openclaw/workspace-pony",
|
|
140
|
+
"agentDir": "/home/teric/.openclaw/agents/pony/agent"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"messages": {
|
|
145
|
+
"ackReactionScope": "group-mentions"
|
|
146
|
+
},
|
|
147
|
+
"commands": {
|
|
148
|
+
"native": "auto",
|
|
149
|
+
"nativeSkills": "auto"
|
|
150
|
+
},
|
|
151
|
+
"gateway": {
|
|
152
|
+
"port": 18789,
|
|
153
|
+
"mode": "local",
|
|
154
|
+
"bind": "lan",
|
|
155
|
+
"auth": {
|
|
156
|
+
"mode": "token",
|
|
157
|
+
"token": "5ce63aeb6791a87a261de88503d88ce58c54372bf77c6160"
|
|
158
|
+
},
|
|
159
|
+
"tailscale": {
|
|
160
|
+
"mode": "off",
|
|
161
|
+
"resetOnExit": false
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawd-models",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to manage OpenClaw model configurations",
|
|
5
|
+
"main": "bin/clawd-models.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clawd-models": "./bin/clawd-models.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["openclaw", "models", "third-party", "providers", "configuration", "cli"],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22"
|
|
15
|
+
},
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"commander": "^12.0.0",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"os": "^0.1.2",
|
|
22
|
+
"path": "^0.12.7"
|
|
23
|
+
}
|
|
24
|
+
}
|