@waniwani/cli 0.0.6 → 0.0.12
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 +15 -11
- package/dist/index.js +364 -223
- package/dist/index.js.map +1 -1
- package/package.json +58 -59
package/dist/index.js
CHANGED
|
@@ -1,117 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
5
|
-
|
|
6
|
-
// src/commands/login.ts
|
|
7
|
-
import { spawn } from "child_process";
|
|
8
|
-
import { createServer } from "http";
|
|
9
|
-
import chalk3 from "chalk";
|
|
10
|
-
import { Command } from "commander";
|
|
11
|
-
import ora from "ora";
|
|
4
|
+
import { Command as Command16 } from "commander";
|
|
12
5
|
|
|
13
|
-
// src/
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
16
9
|
import { join } from "path";
|
|
17
|
-
import {
|
|
18
|
-
var CONFIG_DIR = join(homedir(), ".waniwani");
|
|
19
|
-
var AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
20
|
-
var AuthStoreSchema = z.object({
|
|
21
|
-
accessToken: z.string().nullable().default(null),
|
|
22
|
-
refreshToken: z.string().nullable().default(null),
|
|
23
|
-
expiresAt: z.string().nullable().default(null)
|
|
24
|
-
});
|
|
25
|
-
var API_BASE_URL = process.env.WANIWANI_API_URL || "https://waniwani.com";
|
|
26
|
-
async function ensureConfigDir() {
|
|
27
|
-
await mkdir(CONFIG_DIR, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
async function readAuthStore() {
|
|
30
|
-
await ensureConfigDir();
|
|
31
|
-
try {
|
|
32
|
-
await access(AUTH_FILE);
|
|
33
|
-
const content = await readFile(AUTH_FILE, "utf-8");
|
|
34
|
-
return AuthStoreSchema.parse(JSON.parse(content));
|
|
35
|
-
} catch {
|
|
36
|
-
return AuthStoreSchema.parse({});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
async function writeAuthStore(store) {
|
|
40
|
-
await ensureConfigDir();
|
|
41
|
-
await writeFile(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
42
|
-
}
|
|
43
|
-
var AuthManager = class {
|
|
44
|
-
storeCache = null;
|
|
45
|
-
async getStore() {
|
|
46
|
-
if (!this.storeCache) {
|
|
47
|
-
this.storeCache = await readAuthStore();
|
|
48
|
-
}
|
|
49
|
-
return this.storeCache;
|
|
50
|
-
}
|
|
51
|
-
async saveStore(store) {
|
|
52
|
-
this.storeCache = store;
|
|
53
|
-
await writeAuthStore(store);
|
|
54
|
-
}
|
|
55
|
-
async isLoggedIn() {
|
|
56
|
-
const store = await this.getStore();
|
|
57
|
-
return !!store.accessToken;
|
|
58
|
-
}
|
|
59
|
-
async getAccessToken() {
|
|
60
|
-
const store = await this.getStore();
|
|
61
|
-
return store.accessToken;
|
|
62
|
-
}
|
|
63
|
-
async getRefreshToken() {
|
|
64
|
-
const store = await this.getStore();
|
|
65
|
-
return store.refreshToken;
|
|
66
|
-
}
|
|
67
|
-
async setTokens(accessToken, refreshToken, expiresIn) {
|
|
68
|
-
const expiresAt = new Date(Date.now() + expiresIn * 1e3).toISOString();
|
|
69
|
-
const store = await this.getStore();
|
|
70
|
-
store.accessToken = accessToken;
|
|
71
|
-
store.refreshToken = refreshToken;
|
|
72
|
-
store.expiresAt = expiresAt;
|
|
73
|
-
await this.saveStore(store);
|
|
74
|
-
}
|
|
75
|
-
async clear() {
|
|
76
|
-
const emptyStore = AuthStoreSchema.parse({});
|
|
77
|
-
await this.saveStore(emptyStore);
|
|
78
|
-
}
|
|
79
|
-
async isTokenExpired() {
|
|
80
|
-
const store = await this.getStore();
|
|
81
|
-
if (!store.expiresAt) return true;
|
|
82
|
-
return new Date(store.expiresAt).getTime() - 5 * 60 * 1e3 < Date.now();
|
|
83
|
-
}
|
|
84
|
-
async tryRefreshToken() {
|
|
85
|
-
const refreshToken = await this.getRefreshToken();
|
|
86
|
-
if (!refreshToken) return false;
|
|
87
|
-
try {
|
|
88
|
-
const response = await fetch(`${API_BASE_URL}/api/auth/oauth2/token`, {
|
|
89
|
-
method: "POST",
|
|
90
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
91
|
-
body: new URLSearchParams({
|
|
92
|
-
grant_type: "refresh_token",
|
|
93
|
-
refresh_token: refreshToken,
|
|
94
|
-
client_id: "waniwani-cli"
|
|
95
|
-
}).toString()
|
|
96
|
-
});
|
|
97
|
-
if (!response.ok) {
|
|
98
|
-
await this.clear();
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
const data = await response.json();
|
|
102
|
-
await this.setTokens(
|
|
103
|
-
data.access_token,
|
|
104
|
-
data.refresh_token,
|
|
105
|
-
data.expires_in
|
|
106
|
-
);
|
|
107
|
-
return true;
|
|
108
|
-
} catch {
|
|
109
|
-
await this.clear();
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
var auth = new AuthManager();
|
|
10
|
+
import { Command } from "commander";
|
|
115
11
|
|
|
116
12
|
// src/lib/errors.ts
|
|
117
13
|
import chalk from "chalk";
|
|
@@ -237,8 +133,297 @@ function prettyPrint(data, indent = 0) {
|
|
|
237
133
|
}
|
|
238
134
|
}
|
|
239
135
|
|
|
136
|
+
// src/commands/init.ts
|
|
137
|
+
var PROJECT_CONFIG_DIR = ".waniwani";
|
|
138
|
+
var PROJECT_CONFIG_FILE = "settings.local.json";
|
|
139
|
+
var initCommand = new Command("init").description("Initialize WaniWani project config in current directory").action(async (_, command) => {
|
|
140
|
+
const globalOptions = command.optsWithGlobals();
|
|
141
|
+
const json = globalOptions.json ?? false;
|
|
142
|
+
try {
|
|
143
|
+
const cwd = process.cwd();
|
|
144
|
+
const configDir = join(cwd, PROJECT_CONFIG_DIR);
|
|
145
|
+
const configPath = join(configDir, PROJECT_CONFIG_FILE);
|
|
146
|
+
if (existsSync(configDir)) {
|
|
147
|
+
if (json) {
|
|
148
|
+
formatOutput(
|
|
149
|
+
{ initialized: false, message: "Already initialized" },
|
|
150
|
+
true
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
console.log("Project already initialized (.waniwani/ exists)");
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await mkdir(configDir, { recursive: true });
|
|
158
|
+
const defaultConfig = {
|
|
159
|
+
mcpId: null,
|
|
160
|
+
defaults: {}
|
|
161
|
+
};
|
|
162
|
+
await writeFile(
|
|
163
|
+
configPath,
|
|
164
|
+
JSON.stringify(defaultConfig, null, " "),
|
|
165
|
+
"utf-8"
|
|
166
|
+
);
|
|
167
|
+
if (json) {
|
|
168
|
+
formatOutput({ initialized: true, path: configDir }, true);
|
|
169
|
+
} else {
|
|
170
|
+
formatSuccess("Initialized WaniWani project config", false);
|
|
171
|
+
console.log();
|
|
172
|
+
console.log(` Created: ${PROJECT_CONFIG_DIR}/${PROJECT_CONFIG_FILE}`);
|
|
173
|
+
console.log();
|
|
174
|
+
console.log("Now run:");
|
|
175
|
+
console.log(' waniwani mcp create "my-mcp"');
|
|
176
|
+
console.log(" waniwani mcp use <name>");
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
handleError(error, json);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// src/commands/login.ts
|
|
185
|
+
import { spawn } from "child_process";
|
|
186
|
+
import { createServer } from "http";
|
|
187
|
+
import chalk3 from "chalk";
|
|
188
|
+
import { Command as Command2 } from "commander";
|
|
189
|
+
import ora from "ora";
|
|
190
|
+
|
|
191
|
+
// src/lib/auth.ts
|
|
192
|
+
import { access, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
193
|
+
import { homedir as homedir2 } from "os";
|
|
194
|
+
import { join as join4 } from "path";
|
|
195
|
+
import { z as z3 } from "zod";
|
|
196
|
+
|
|
197
|
+
// src/lib/config.ts
|
|
198
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
199
|
+
import { homedir } from "os";
|
|
200
|
+
import { join as join3 } from "path";
|
|
201
|
+
import { z as z2 } from "zod";
|
|
202
|
+
|
|
203
|
+
// src/lib/project-config.ts
|
|
204
|
+
import { existsSync as existsSync2 } from "fs";
|
|
205
|
+
import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
|
|
206
|
+
import { join as join2 } from "path";
|
|
207
|
+
import { z } from "zod";
|
|
208
|
+
var PROJECT_CONFIG_DIR2 = ".waniwani";
|
|
209
|
+
var PROJECT_CONFIG_FILE2 = "settings.local.json";
|
|
210
|
+
var ProjectConfigSchema = z.object({
|
|
211
|
+
mcpId: z.string().optional(),
|
|
212
|
+
defaults: z.object({
|
|
213
|
+
model: z.string().optional(),
|
|
214
|
+
maxSteps: z.number().optional()
|
|
215
|
+
}).optional()
|
|
216
|
+
});
|
|
217
|
+
var projectConfigCache;
|
|
218
|
+
function getProjectConfigPath() {
|
|
219
|
+
return join2(process.cwd(), PROJECT_CONFIG_DIR2, PROJECT_CONFIG_FILE2);
|
|
220
|
+
}
|
|
221
|
+
function getProjectConfigDir() {
|
|
222
|
+
return join2(process.cwd(), PROJECT_CONFIG_DIR2);
|
|
223
|
+
}
|
|
224
|
+
function hasProjectConfig() {
|
|
225
|
+
return existsSync2(getProjectConfigDir());
|
|
226
|
+
}
|
|
227
|
+
async function loadProjectConfig() {
|
|
228
|
+
if (projectConfigCache !== void 0) {
|
|
229
|
+
return projectConfigCache;
|
|
230
|
+
}
|
|
231
|
+
const configPath = getProjectConfigPath();
|
|
232
|
+
try {
|
|
233
|
+
const content = await readFile(configPath, "utf-8");
|
|
234
|
+
projectConfigCache = ProjectConfigSchema.parse(JSON.parse(content));
|
|
235
|
+
return projectConfigCache;
|
|
236
|
+
} catch {
|
|
237
|
+
projectConfigCache = null;
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function saveProjectConfig(config2) {
|
|
242
|
+
const configDir = getProjectConfigDir();
|
|
243
|
+
const configPath = getProjectConfigPath();
|
|
244
|
+
await mkdir2(configDir, { recursive: true });
|
|
245
|
+
const existing = await loadProjectConfig() ?? {};
|
|
246
|
+
const merged = ProjectConfigSchema.parse({ ...existing, ...config2 });
|
|
247
|
+
await writeFile2(configPath, JSON.stringify(merged, null, " "), "utf-8");
|
|
248
|
+
projectConfigCache = merged;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/lib/config.ts
|
|
252
|
+
var CONFIG_DIR = join3(homedir(), ".waniwani");
|
|
253
|
+
var CONFIG_FILE = join3(CONFIG_DIR, "config.json");
|
|
254
|
+
var DEFAULT_API_URL = "https://app.waniwani.ai";
|
|
255
|
+
var ConfigSchema = z2.object({
|
|
256
|
+
defaults: z2.object({ model: z2.string(), maxSteps: z2.number() }).default({ model: "claude-sonnet-4-20250514", maxSteps: 10 }),
|
|
257
|
+
activeMcpId: z2.string().nullable().default(null),
|
|
258
|
+
apiUrl: z2.string().nullable().default(null)
|
|
259
|
+
});
|
|
260
|
+
var cache = null;
|
|
261
|
+
async function load() {
|
|
262
|
+
if (cache) return cache;
|
|
263
|
+
try {
|
|
264
|
+
cache = ConfigSchema.parse(
|
|
265
|
+
JSON.parse(await readFile2(CONFIG_FILE, "utf-8"))
|
|
266
|
+
);
|
|
267
|
+
} catch {
|
|
268
|
+
cache = ConfigSchema.parse({});
|
|
269
|
+
}
|
|
270
|
+
return cache;
|
|
271
|
+
}
|
|
272
|
+
async function save(cfg) {
|
|
273
|
+
cache = cfg;
|
|
274
|
+
await mkdir3(CONFIG_DIR, { recursive: true });
|
|
275
|
+
await writeFile3(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
276
|
+
}
|
|
277
|
+
async function update(fn) {
|
|
278
|
+
const cfg = await load();
|
|
279
|
+
fn(cfg);
|
|
280
|
+
await save(cfg);
|
|
281
|
+
}
|
|
282
|
+
var config = {
|
|
283
|
+
getDefaults: async () => {
|
|
284
|
+
const cfg = await load();
|
|
285
|
+
return cfg.defaults;
|
|
286
|
+
},
|
|
287
|
+
getEffectiveDefaults: async () => {
|
|
288
|
+
const global = await load();
|
|
289
|
+
const project = await loadProjectConfig();
|
|
290
|
+
return { ...global.defaults, ...project?.defaults };
|
|
291
|
+
},
|
|
292
|
+
setDefaults: (defaults) => {
|
|
293
|
+
return update((cfg) => {
|
|
294
|
+
cfg.defaults = { ...cfg.defaults, ...defaults };
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
getActiveMcpId: async () => {
|
|
298
|
+
const cfg = await load();
|
|
299
|
+
return cfg.activeMcpId;
|
|
300
|
+
},
|
|
301
|
+
getEffectiveMcpId: async () => {
|
|
302
|
+
const project = await loadProjectConfig();
|
|
303
|
+
if (project?.mcpId) return project.mcpId;
|
|
304
|
+
const cfg = await load();
|
|
305
|
+
return cfg.activeMcpId;
|
|
306
|
+
},
|
|
307
|
+
setActiveMcpId: (id) => {
|
|
308
|
+
return update((cfg) => {
|
|
309
|
+
cfg.activeMcpId = id;
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
getApiUrl: async () => {
|
|
313
|
+
if (process.env.WANIWANI_API_URL) return process.env.WANIWANI_API_URL;
|
|
314
|
+
const cfg = await load();
|
|
315
|
+
return cfg.apiUrl || DEFAULT_API_URL;
|
|
316
|
+
},
|
|
317
|
+
setApiUrl: (url) => {
|
|
318
|
+
return update((cfg) => {
|
|
319
|
+
cfg.apiUrl = url;
|
|
320
|
+
});
|
|
321
|
+
},
|
|
322
|
+
clear: () => {
|
|
323
|
+
return save(ConfigSchema.parse({}));
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/lib/auth.ts
|
|
328
|
+
var CONFIG_DIR2 = join4(homedir2(), ".waniwani");
|
|
329
|
+
var AUTH_FILE = join4(CONFIG_DIR2, "auth.json");
|
|
330
|
+
var AuthStoreSchema = z3.object({
|
|
331
|
+
accessToken: z3.string().nullable().default(null),
|
|
332
|
+
refreshToken: z3.string().nullable().default(null),
|
|
333
|
+
expiresAt: z3.string().nullable().default(null)
|
|
334
|
+
});
|
|
335
|
+
async function ensureConfigDir() {
|
|
336
|
+
await mkdir4(CONFIG_DIR2, { recursive: true });
|
|
337
|
+
}
|
|
338
|
+
async function readAuthStore() {
|
|
339
|
+
await ensureConfigDir();
|
|
340
|
+
try {
|
|
341
|
+
await access(AUTH_FILE);
|
|
342
|
+
const content = await readFile3(AUTH_FILE, "utf-8");
|
|
343
|
+
return AuthStoreSchema.parse(JSON.parse(content));
|
|
344
|
+
} catch {
|
|
345
|
+
return AuthStoreSchema.parse({});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async function writeAuthStore(store) {
|
|
349
|
+
await ensureConfigDir();
|
|
350
|
+
await writeFile4(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
351
|
+
}
|
|
352
|
+
var AuthManager = class {
|
|
353
|
+
storeCache = null;
|
|
354
|
+
async getStore() {
|
|
355
|
+
if (!this.storeCache) {
|
|
356
|
+
this.storeCache = await readAuthStore();
|
|
357
|
+
}
|
|
358
|
+
return this.storeCache;
|
|
359
|
+
}
|
|
360
|
+
async saveStore(store) {
|
|
361
|
+
this.storeCache = store;
|
|
362
|
+
await writeAuthStore(store);
|
|
363
|
+
}
|
|
364
|
+
async isLoggedIn() {
|
|
365
|
+
const store = await this.getStore();
|
|
366
|
+
return !!store.accessToken;
|
|
367
|
+
}
|
|
368
|
+
async getAccessToken() {
|
|
369
|
+
const store = await this.getStore();
|
|
370
|
+
return store.accessToken;
|
|
371
|
+
}
|
|
372
|
+
async getRefreshToken() {
|
|
373
|
+
const store = await this.getStore();
|
|
374
|
+
return store.refreshToken;
|
|
375
|
+
}
|
|
376
|
+
async setTokens(accessToken, refreshToken, expiresIn) {
|
|
377
|
+
const expiresAt = new Date(Date.now() + expiresIn * 1e3).toISOString();
|
|
378
|
+
const store = await this.getStore();
|
|
379
|
+
store.accessToken = accessToken;
|
|
380
|
+
store.refreshToken = refreshToken;
|
|
381
|
+
store.expiresAt = expiresAt;
|
|
382
|
+
await this.saveStore(store);
|
|
383
|
+
}
|
|
384
|
+
async clear() {
|
|
385
|
+
const emptyStore = AuthStoreSchema.parse({});
|
|
386
|
+
await this.saveStore(emptyStore);
|
|
387
|
+
}
|
|
388
|
+
async isTokenExpired() {
|
|
389
|
+
const store = await this.getStore();
|
|
390
|
+
if (!store.expiresAt) return true;
|
|
391
|
+
return new Date(store.expiresAt).getTime() - 5 * 60 * 1e3 < Date.now();
|
|
392
|
+
}
|
|
393
|
+
async tryRefreshToken() {
|
|
394
|
+
const refreshToken = await this.getRefreshToken();
|
|
395
|
+
if (!refreshToken) return false;
|
|
396
|
+
try {
|
|
397
|
+
const apiUrl = await config.getApiUrl();
|
|
398
|
+
const response = await fetch(`${apiUrl}/api/auth/oauth2/token`, {
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
401
|
+
body: new URLSearchParams({
|
|
402
|
+
grant_type: "refresh_token",
|
|
403
|
+
refresh_token: refreshToken,
|
|
404
|
+
client_id: "waniwani-cli"
|
|
405
|
+
}).toString()
|
|
406
|
+
});
|
|
407
|
+
if (!response.ok) {
|
|
408
|
+
await this.clear();
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const data = await response.json();
|
|
412
|
+
await this.setTokens(
|
|
413
|
+
data.access_token,
|
|
414
|
+
data.refresh_token,
|
|
415
|
+
data.expires_in
|
|
416
|
+
);
|
|
417
|
+
return true;
|
|
418
|
+
} catch {
|
|
419
|
+
await this.clear();
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
var auth = new AuthManager();
|
|
425
|
+
|
|
240
426
|
// src/commands/login.ts
|
|
241
|
-
var API_BASE_URL2 = process.env.WANIWANI_API_URL || "https://waniwani.com";
|
|
242
427
|
var CALLBACK_PORT = 54321;
|
|
243
428
|
var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
|
|
244
429
|
var CLIENT_ID = "waniwani-cli";
|
|
@@ -362,7 +547,8 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
362
547
|
});
|
|
363
548
|
}
|
|
364
549
|
async function exchangeCodeForToken(code, codeVerifier) {
|
|
365
|
-
const
|
|
550
|
+
const apiUrl = await config.getApiUrl();
|
|
551
|
+
const response = await fetch(`${apiUrl}/api/auth/oauth2/token`, {
|
|
366
552
|
method: "POST",
|
|
367
553
|
headers: {
|
|
368
554
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
@@ -384,7 +570,7 @@ async function exchangeCodeForToken(code, codeVerifier) {
|
|
|
384
570
|
}
|
|
385
571
|
return response.json();
|
|
386
572
|
}
|
|
387
|
-
var loginCommand = new
|
|
573
|
+
var loginCommand = new Command2("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
|
|
388
574
|
const globalOptions = command.optsWithGlobals();
|
|
389
575
|
const json = globalOptions.json ?? false;
|
|
390
576
|
try {
|
|
@@ -406,7 +592,8 @@ var loginCommand = new Command("login").description("Log in to WaniWani").option
|
|
|
406
592
|
const codeVerifier = generateCodeVerifier();
|
|
407
593
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
408
594
|
const state = generateState();
|
|
409
|
-
const
|
|
595
|
+
const apiUrl = await config.getApiUrl();
|
|
596
|
+
const authUrl = new URL(`${apiUrl}/oauth/authorize`);
|
|
410
597
|
authUrl.searchParams.set("client_id", CLIENT_ID);
|
|
411
598
|
authUrl.searchParams.set("redirect_uri", CALLBACK_URL);
|
|
412
599
|
authUrl.searchParams.set("response_type", "code");
|
|
@@ -456,78 +643,8 @@ var loginCommand = new Command("login").description("Log in to WaniWani").option
|
|
|
456
643
|
});
|
|
457
644
|
|
|
458
645
|
// src/commands/logout.ts
|
|
459
|
-
import { Command as
|
|
460
|
-
|
|
461
|
-
// src/lib/config.ts
|
|
462
|
-
import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
463
|
-
import { homedir as homedir2 } from "os";
|
|
464
|
-
import { join as join2 } from "path";
|
|
465
|
-
import { z as z2 } from "zod";
|
|
466
|
-
var CONFIG_DIR2 = join2(homedir2(), ".waniwani");
|
|
467
|
-
var CONFIG_FILE = join2(CONFIG_DIR2, "config.json");
|
|
468
|
-
var ConfigSchema = z2.object({
|
|
469
|
-
defaults: z2.object({
|
|
470
|
-
model: z2.string().default("claude-sonnet-4-20250514"),
|
|
471
|
-
maxSteps: z2.number().default(10)
|
|
472
|
-
}).default(() => ({ model: "claude-sonnet-4-20250514", maxSteps: 10 })),
|
|
473
|
-
activeMcpId: z2.string().nullable().default(null)
|
|
474
|
-
});
|
|
475
|
-
async function ensureConfigDir2() {
|
|
476
|
-
await mkdir2(CONFIG_DIR2, { recursive: true });
|
|
477
|
-
}
|
|
478
|
-
async function readConfig() {
|
|
479
|
-
await ensureConfigDir2();
|
|
480
|
-
try {
|
|
481
|
-
await access2(CONFIG_FILE);
|
|
482
|
-
const content = await readFile2(CONFIG_FILE, "utf-8");
|
|
483
|
-
return ConfigSchema.parse(JSON.parse(content));
|
|
484
|
-
} catch {
|
|
485
|
-
return ConfigSchema.parse({});
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
async function writeConfig(config2) {
|
|
489
|
-
await ensureConfigDir2();
|
|
490
|
-
await writeFile2(CONFIG_FILE, JSON.stringify(config2, null, 2), "utf-8");
|
|
491
|
-
}
|
|
492
|
-
var ConfigManager = class {
|
|
493
|
-
configCache = null;
|
|
494
|
-
async getConfig() {
|
|
495
|
-
if (!this.configCache) {
|
|
496
|
-
this.configCache = await readConfig();
|
|
497
|
-
}
|
|
498
|
-
return this.configCache;
|
|
499
|
-
}
|
|
500
|
-
async saveConfig(config2) {
|
|
501
|
-
this.configCache = config2;
|
|
502
|
-
await writeConfig(config2);
|
|
503
|
-
}
|
|
504
|
-
async getDefaults() {
|
|
505
|
-
const config2 = await this.getConfig();
|
|
506
|
-
return config2.defaults;
|
|
507
|
-
}
|
|
508
|
-
async setDefaults(defaults) {
|
|
509
|
-
const config2 = await this.getConfig();
|
|
510
|
-
config2.defaults = { ...config2.defaults, ...defaults };
|
|
511
|
-
await this.saveConfig(config2);
|
|
512
|
-
}
|
|
513
|
-
async getActiveMcpId() {
|
|
514
|
-
const config2 = await this.getConfig();
|
|
515
|
-
return config2.activeMcpId;
|
|
516
|
-
}
|
|
517
|
-
async setActiveMcpId(id) {
|
|
518
|
-
const config2 = await this.getConfig();
|
|
519
|
-
config2.activeMcpId = id;
|
|
520
|
-
await this.saveConfig(config2);
|
|
521
|
-
}
|
|
522
|
-
async clear() {
|
|
523
|
-
const emptyConfig = ConfigSchema.parse({});
|
|
524
|
-
await this.saveConfig(emptyConfig);
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
var config = new ConfigManager();
|
|
528
|
-
|
|
529
|
-
// src/commands/logout.ts
|
|
530
|
-
var logoutCommand = new Command2("logout").description("Log out from WaniWani").action(async (_, command) => {
|
|
646
|
+
import { Command as Command3 } from "commander";
|
|
647
|
+
var logoutCommand = new Command3("logout").description("Log out from WaniWani").action(async (_, command) => {
|
|
531
648
|
const globalOptions = command.optsWithGlobals();
|
|
532
649
|
const json = globalOptions.json ?? false;
|
|
533
650
|
try {
|
|
@@ -553,14 +670,13 @@ var logoutCommand = new Command2("logout").description("Log out from WaniWani").
|
|
|
553
670
|
});
|
|
554
671
|
|
|
555
672
|
// src/commands/mcp/index.ts
|
|
556
|
-
import { Command as
|
|
673
|
+
import { Command as Command11 } from "commander";
|
|
557
674
|
|
|
558
675
|
// src/commands/mcp/create.ts
|
|
559
|
-
import { Command as
|
|
676
|
+
import { Command as Command4 } from "commander";
|
|
560
677
|
import ora2 from "ora";
|
|
561
678
|
|
|
562
679
|
// src/lib/api.ts
|
|
563
|
-
var API_BASE_URL3 = process.env.WANIWANI_API_URL || "https://waniwani.com";
|
|
564
680
|
var ApiError = class extends CLIError {
|
|
565
681
|
constructor(message, code, statusCode, details) {
|
|
566
682
|
super(message, code, details);
|
|
@@ -587,7 +703,8 @@ async function request(method, path, options) {
|
|
|
587
703
|
}
|
|
588
704
|
headers.Authorization = `Bearer ${token}`;
|
|
589
705
|
}
|
|
590
|
-
const
|
|
706
|
+
const baseUrl = await config.getApiUrl();
|
|
707
|
+
const url = `${baseUrl}${path}`;
|
|
591
708
|
const response = await fetch(url, {
|
|
592
709
|
method,
|
|
593
710
|
headers,
|
|
@@ -624,11 +741,11 @@ var api = {
|
|
|
624
741
|
get: (path, options) => request("GET", path, options),
|
|
625
742
|
post: (path, body, options) => request("POST", path, { body, ...options }),
|
|
626
743
|
delete: (path, options) => request("DELETE", path, options),
|
|
627
|
-
getBaseUrl: () =>
|
|
744
|
+
getBaseUrl: () => config.getApiUrl()
|
|
628
745
|
};
|
|
629
746
|
|
|
630
747
|
// src/commands/mcp/create.ts
|
|
631
|
-
var createCommand = new
|
|
748
|
+
var createCommand = new Command4("create").description("Create a new MCP sandbox from template").argument("<name>", "Name for the MCP project").option("--global", "Save to global config instead of project config").action(async (name, options, command) => {
|
|
632
749
|
const globalOptions = command.optsWithGlobals();
|
|
633
750
|
const json = globalOptions.json ?? false;
|
|
634
751
|
try {
|
|
@@ -637,12 +754,24 @@ var createCommand = new Command3("create").description("Create a new MCP sandbox
|
|
|
637
754
|
name
|
|
638
755
|
});
|
|
639
756
|
spinner.succeed("MCP sandbox created");
|
|
640
|
-
|
|
757
|
+
const useProjectConfig = !options.global && hasProjectConfig();
|
|
758
|
+
if (useProjectConfig) {
|
|
759
|
+
await saveProjectConfig({ mcpId: result.id });
|
|
760
|
+
} else {
|
|
761
|
+
await config.setActiveMcpId(result.id);
|
|
762
|
+
}
|
|
641
763
|
if (json) {
|
|
642
|
-
formatOutput(
|
|
764
|
+
formatOutput(
|
|
765
|
+
{ ...result, scope: useProjectConfig ? "project" : "global" },
|
|
766
|
+
true
|
|
767
|
+
);
|
|
643
768
|
} else {
|
|
769
|
+
const scope = useProjectConfig ? "(saved to project)" : "(saved globally)";
|
|
644
770
|
console.log();
|
|
645
|
-
formatSuccess(
|
|
771
|
+
formatSuccess(
|
|
772
|
+
`MCP sandbox "${name}" created successfully! ${scope}`,
|
|
773
|
+
false
|
|
774
|
+
);
|
|
646
775
|
console.log();
|
|
647
776
|
console.log(` MCP ID: ${result.id}`);
|
|
648
777
|
console.log(` Sandbox ID: ${result.sandboxId}`);
|
|
@@ -660,9 +789,9 @@ var createCommand = new Command3("create").description("Create a new MCP sandbox
|
|
|
660
789
|
});
|
|
661
790
|
|
|
662
791
|
// src/commands/mcp/deploy.ts
|
|
663
|
-
import { Command as
|
|
792
|
+
import { Command as Command5 } from "commander";
|
|
664
793
|
import ora3 from "ora";
|
|
665
|
-
var deployCommand = new
|
|
794
|
+
var deployCommand = new Command5("deploy").description("Deploy MCP server to GitHub + Vercel from sandbox").option("--repo <name>", "GitHub repository name").option("--org <name>", "GitHub organization").option("--private", "Create private repository").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
666
795
|
const globalOptions = command.optsWithGlobals();
|
|
667
796
|
const json = globalOptions.json ?? false;
|
|
668
797
|
try {
|
|
@@ -708,9 +837,9 @@ var deployCommand = new Command4("deploy").description("Deploy MCP server to Git
|
|
|
708
837
|
|
|
709
838
|
// src/commands/mcp/list.ts
|
|
710
839
|
import chalk4 from "chalk";
|
|
711
|
-
import { Command as
|
|
840
|
+
import { Command as Command6 } from "commander";
|
|
712
841
|
import ora4 from "ora";
|
|
713
|
-
var listCommand = new
|
|
842
|
+
var listCommand = new Command6("list").description("List all MCPs in your organization").option("--all", "Include stopped/expired MCPs").action(async (options, command) => {
|
|
714
843
|
const globalOptions = command.optsWithGlobals();
|
|
715
844
|
const json = globalOptions.json ?? false;
|
|
716
845
|
try {
|
|
@@ -768,9 +897,9 @@ var listCommand = new Command5("list").description("List all MCPs in your organi
|
|
|
768
897
|
|
|
769
898
|
// src/commands/mcp/status.ts
|
|
770
899
|
import chalk5 from "chalk";
|
|
771
|
-
import { Command as
|
|
900
|
+
import { Command as Command7 } from "commander";
|
|
772
901
|
import ora5 from "ora";
|
|
773
|
-
var statusCommand = new
|
|
902
|
+
var statusCommand = new Command7("status").description("Show current MCP sandbox status").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
774
903
|
const globalOptions = command.optsWithGlobals();
|
|
775
904
|
const json = globalOptions.json ?? false;
|
|
776
905
|
try {
|
|
@@ -810,9 +939,9 @@ var statusCommand = new Command6("status").description("Show current MCP sandbox
|
|
|
810
939
|
});
|
|
811
940
|
|
|
812
941
|
// src/commands/mcp/stop.ts
|
|
813
|
-
import { Command as
|
|
942
|
+
import { Command as Command8 } from "commander";
|
|
814
943
|
import ora6 from "ora";
|
|
815
|
-
var stopCommand = new
|
|
944
|
+
var stopCommand = new Command8("stop").description("Stop and clean up the MCP sandbox").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
816
945
|
const globalOptions = command.optsWithGlobals();
|
|
817
946
|
const json = globalOptions.json ?? false;
|
|
818
947
|
try {
|
|
@@ -842,9 +971,9 @@ var stopCommand = new Command7("stop").description("Stop and clean up the MCP sa
|
|
|
842
971
|
|
|
843
972
|
// src/commands/mcp/test.ts
|
|
844
973
|
import chalk6 from "chalk";
|
|
845
|
-
import { Command as
|
|
974
|
+
import { Command as Command9 } from "commander";
|
|
846
975
|
import ora7 from "ora";
|
|
847
|
-
var testCommand = new
|
|
976
|
+
var testCommand = new Command9("test").description("Test MCP tools via the sandbox").argument("[tool]", "Tool name to test (lists tools if omitted)").argument("[args...]", "JSON arguments for the tool").option("--mcp-id <id>", "Specific MCP ID").action(
|
|
848
977
|
async (tool, args, options, command) => {
|
|
849
978
|
const globalOptions = command.optsWithGlobals();
|
|
850
979
|
const json = globalOptions.json ?? false;
|
|
@@ -932,9 +1061,9 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
|
|
|
932
1061
|
);
|
|
933
1062
|
|
|
934
1063
|
// src/commands/mcp/use.ts
|
|
935
|
-
import { Command as
|
|
1064
|
+
import { Command as Command10 } from "commander";
|
|
936
1065
|
import ora8 from "ora";
|
|
937
|
-
var useCommand = new
|
|
1066
|
+
var useCommand = new Command10("use").description("Select an MCP to use for subsequent commands").argument("<name>", "Name of the MCP to use").option("--global", "Save to global config instead of project config").action(async (name, options, command) => {
|
|
938
1067
|
const globalOptions = command.optsWithGlobals();
|
|
939
1068
|
const json = globalOptions.json ?? false;
|
|
940
1069
|
try {
|
|
@@ -952,11 +1081,20 @@ var useCommand = new Command9("use").description("Select an MCP to use for subse
|
|
|
952
1081
|
`MCP "${name}" is ${mcp.status}. Only active MCPs can be used.`
|
|
953
1082
|
);
|
|
954
1083
|
}
|
|
955
|
-
|
|
1084
|
+
const useProjectConfig = !options.global && hasProjectConfig();
|
|
1085
|
+
if (useProjectConfig) {
|
|
1086
|
+
await saveProjectConfig({ mcpId: mcp.id });
|
|
1087
|
+
} else {
|
|
1088
|
+
await config.setActiveMcpId(mcp.id);
|
|
1089
|
+
}
|
|
956
1090
|
if (json) {
|
|
957
|
-
formatOutput(
|
|
1091
|
+
formatOutput(
|
|
1092
|
+
{ selected: mcp, scope: useProjectConfig ? "project" : "global" },
|
|
1093
|
+
true
|
|
1094
|
+
);
|
|
958
1095
|
} else {
|
|
959
|
-
|
|
1096
|
+
const scope = useProjectConfig ? "(project)" : "(global)";
|
|
1097
|
+
formatSuccess(`Now using MCP "${name}" ${scope}`, false);
|
|
960
1098
|
console.log();
|
|
961
1099
|
console.log(` MCP ID: ${mcp.id}`);
|
|
962
1100
|
console.log(` Preview URL: ${mcp.previewUrl}`);
|
|
@@ -973,16 +1111,16 @@ var useCommand = new Command9("use").description("Select an MCP to use for subse
|
|
|
973
1111
|
});
|
|
974
1112
|
|
|
975
1113
|
// src/commands/mcp/index.ts
|
|
976
|
-
var mcpCommand = new
|
|
1114
|
+
var mcpCommand = new Command11("mcp").description("MCP sandbox management commands").addCommand(createCommand).addCommand(listCommand).addCommand(useCommand).addCommand(statusCommand).addCommand(stopCommand).addCommand(testCommand).addCommand(deployCommand);
|
|
977
1115
|
|
|
978
1116
|
// src/commands/org/index.ts
|
|
979
|
-
import { Command as
|
|
1117
|
+
import { Command as Command14 } from "commander";
|
|
980
1118
|
|
|
981
1119
|
// src/commands/org/list.ts
|
|
982
1120
|
import chalk7 from "chalk";
|
|
983
|
-
import { Command as
|
|
1121
|
+
import { Command as Command12 } from "commander";
|
|
984
1122
|
import ora9 from "ora";
|
|
985
|
-
var listCommand2 = new
|
|
1123
|
+
var listCommand2 = new Command12("list").description("List your organizations").action(async (_, command) => {
|
|
986
1124
|
const globalOptions = command.optsWithGlobals();
|
|
987
1125
|
const json = globalOptions.json ?? false;
|
|
988
1126
|
try {
|
|
@@ -1032,9 +1170,9 @@ var listCommand2 = new Command11("list").description("List your organizations").
|
|
|
1032
1170
|
});
|
|
1033
1171
|
|
|
1034
1172
|
// src/commands/org/switch.ts
|
|
1035
|
-
import { Command as
|
|
1173
|
+
import { Command as Command13 } from "commander";
|
|
1036
1174
|
import ora10 from "ora";
|
|
1037
|
-
var switchCommand = new
|
|
1175
|
+
var switchCommand = new Command13("switch").description("Switch to a different organization").argument("<name>", "Name or slug of the organization to switch to").action(async (name, _, command) => {
|
|
1038
1176
|
const globalOptions = command.optsWithGlobals();
|
|
1039
1177
|
const json = globalOptions.json ?? false;
|
|
1040
1178
|
try {
|
|
@@ -1071,22 +1209,22 @@ var switchCommand = new Command12("switch").description("Switch to a different o
|
|
|
1071
1209
|
});
|
|
1072
1210
|
|
|
1073
1211
|
// src/commands/org/index.ts
|
|
1074
|
-
var orgCommand = new
|
|
1212
|
+
var orgCommand = new Command14("org").description("Organization management commands").addCommand(listCommand2).addCommand(switchCommand);
|
|
1075
1213
|
|
|
1076
1214
|
// src/commands/task.ts
|
|
1077
1215
|
import chalk8 from "chalk";
|
|
1078
|
-
import { Command as
|
|
1216
|
+
import { Command as Command15 } from "commander";
|
|
1079
1217
|
import ora11 from "ora";
|
|
1080
|
-
var taskCommand = new
|
|
1218
|
+
var taskCommand = new Command15("task").description("Send a task to Claude running in the sandbox").argument("<prompt>", "Task description/prompt").option("--mcp-id <id>", "Specific MCP ID").option("--model <model>", "Claude model to use").option("--max-steps <n>", "Maximum tool use steps").action(async (prompt, options, command) => {
|
|
1081
1219
|
const globalOptions = command.optsWithGlobals();
|
|
1082
1220
|
const json = globalOptions.json ?? false;
|
|
1083
1221
|
try {
|
|
1084
1222
|
let mcpId = options.mcpId;
|
|
1085
1223
|
if (!mcpId) {
|
|
1086
|
-
mcpId = await config.
|
|
1224
|
+
mcpId = await config.getEffectiveMcpId();
|
|
1087
1225
|
if (!mcpId) {
|
|
1088
1226
|
throw new McpError(
|
|
1089
|
-
"No active MCP. Run 'waniwani
|
|
1227
|
+
"No active MCP. Run 'waniwani init' then 'waniwani mcp use <name>'."
|
|
1090
1228
|
);
|
|
1091
1229
|
}
|
|
1092
1230
|
}
|
|
@@ -1096,7 +1234,9 @@ var taskCommand = new Command14("task").description("Send a task to Claude runni
|
|
|
1096
1234
|
"Not logged in. Run 'waniwani login' to authenticate."
|
|
1097
1235
|
);
|
|
1098
1236
|
}
|
|
1099
|
-
const
|
|
1237
|
+
const defaults = await config.getEffectiveDefaults();
|
|
1238
|
+
const model = options.model ?? defaults.model;
|
|
1239
|
+
const maxSteps = options.maxSteps ? Number.parseInt(options.maxSteps, 10) : defaults.maxSteps;
|
|
1100
1240
|
if (!json) {
|
|
1101
1241
|
console.log();
|
|
1102
1242
|
console.log(chalk8.bold("Task:"), prompt);
|
|
@@ -1113,7 +1253,7 @@ var taskCommand = new Command14("task").description("Send a task to Claude runni
|
|
|
1113
1253
|
},
|
|
1114
1254
|
body: JSON.stringify({
|
|
1115
1255
|
prompt,
|
|
1116
|
-
model
|
|
1256
|
+
model,
|
|
1117
1257
|
maxSteps
|
|
1118
1258
|
})
|
|
1119
1259
|
});
|
|
@@ -1222,9 +1362,10 @@ var taskCommand = new Command14("task").description("Send a task to Claude runni
|
|
|
1222
1362
|
|
|
1223
1363
|
// src/cli.ts
|
|
1224
1364
|
var version = "0.1.0";
|
|
1225
|
-
var program = new
|
|
1365
|
+
var program = new Command16().name("waniwani").description("WaniWani CLI for MCP development workflow").version(version).option("--json", "Output results as JSON").option("--verbose", "Enable verbose logging");
|
|
1226
1366
|
program.addCommand(loginCommand);
|
|
1227
1367
|
program.addCommand(logoutCommand);
|
|
1368
|
+
program.addCommand(initCommand);
|
|
1228
1369
|
program.addCommand(mcpCommand);
|
|
1229
1370
|
program.addCommand(taskCommand);
|
|
1230
1371
|
program.addCommand(orgCommand);
|