@waniwani/cli 0.0.25 → 0.0.28
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/dist/index.js +907 -444
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,209 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
5
|
-
|
|
6
|
-
// src/commands/init.ts
|
|
7
|
-
import { existsSync } from "fs";
|
|
8
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
import { Command } from "commander";
|
|
4
|
+
import { Command as Command25 } from "commander";
|
|
11
5
|
|
|
12
|
-
// src/
|
|
13
|
-
import
|
|
14
|
-
import { ZodError } from "zod";
|
|
15
|
-
var CLIError = class extends Error {
|
|
16
|
-
constructor(message, code, details) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.code = code;
|
|
19
|
-
this.details = details;
|
|
20
|
-
this.name = "CLIError";
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
var AuthError = class extends CLIError {
|
|
24
|
-
constructor(message, details) {
|
|
25
|
-
super(message, "AUTH_ERROR", details);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
var SandboxError = class extends CLIError {
|
|
29
|
-
constructor(message, details) {
|
|
30
|
-
super(message, "SANDBOX_ERROR", details);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
var McpError = class extends CLIError {
|
|
34
|
-
constructor(message, details) {
|
|
35
|
-
super(message, "MCP_ERROR", details);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
function handleError(error, json) {
|
|
39
|
-
if (error instanceof ZodError) {
|
|
40
|
-
const message = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
41
|
-
outputError("VALIDATION_ERROR", `Invalid input: ${message}`, json);
|
|
42
|
-
} else if (error instanceof CLIError) {
|
|
43
|
-
outputError(error.code, error.message, json, error.details);
|
|
44
|
-
} else if (error instanceof Error) {
|
|
45
|
-
outputError("UNKNOWN_ERROR", error.message, json);
|
|
46
|
-
} else {
|
|
47
|
-
outputError("UNKNOWN_ERROR", String(error), json);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function outputError(code, message, json, details) {
|
|
51
|
-
if (json) {
|
|
52
|
-
console.error(
|
|
53
|
-
JSON.stringify({ success: false, error: { code, message, details } })
|
|
54
|
-
);
|
|
55
|
-
} else {
|
|
56
|
-
console.error(chalk.red(`Error [${code}]:`), message);
|
|
57
|
-
if (details) {
|
|
58
|
-
console.error(chalk.gray("Details:"), JSON.stringify(details, null, 2));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// src/lib/output.ts
|
|
6
|
+
// src/commands/dev.ts
|
|
7
|
+
import { relative as relative2 } from "path";
|
|
64
8
|
import chalk2 from "chalk";
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
68
|
-
} else {
|
|
69
|
-
prettyPrint(data);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function formatSuccess(message, json) {
|
|
73
|
-
if (json) {
|
|
74
|
-
console.log(JSON.stringify({ success: true, message }));
|
|
75
|
-
} else {
|
|
76
|
-
console.log(chalk2.green("\u2713"), message);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function formatTable(headers, rows, json) {
|
|
80
|
-
if (json) {
|
|
81
|
-
const data = rows.map(
|
|
82
|
-
(row) => Object.fromEntries(headers.map((header, i) => [header, row[i]]))
|
|
83
|
-
);
|
|
84
|
-
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
85
|
-
} else {
|
|
86
|
-
const colWidths = headers.map(
|
|
87
|
-
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
|
|
88
|
-
);
|
|
89
|
-
const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
|
|
90
|
-
const formatRow = (row) => row.map((cell, i) => ` ${(cell || "").padEnd(colWidths[i])} `).join("|");
|
|
91
|
-
console.log(chalk2.cyan(formatRow(headers)));
|
|
92
|
-
console.log(separator);
|
|
93
|
-
for (const row of rows) {
|
|
94
|
-
console.log(formatRow(row));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function formatList(items, json) {
|
|
99
|
-
if (json) {
|
|
100
|
-
const data = Object.fromEntries(
|
|
101
|
-
items.map((item) => [item.label, item.value])
|
|
102
|
-
);
|
|
103
|
-
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
104
|
-
} else {
|
|
105
|
-
const maxLabelLength = Math.max(...items.map((i) => i.label.length));
|
|
106
|
-
items.forEach((item) => {
|
|
107
|
-
console.log(
|
|
108
|
-
`${chalk2.gray(item.label.padEnd(maxLabelLength))} ${chalk2.white(item.value)}`
|
|
109
|
-
);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
function prettyPrint(data, indent = 0) {
|
|
114
|
-
const prefix = " ".repeat(indent);
|
|
115
|
-
if (Array.isArray(data)) {
|
|
116
|
-
data.forEach((item, index) => {
|
|
117
|
-
console.log(`${prefix}${chalk2.gray(`[${index}]`)}`);
|
|
118
|
-
prettyPrint(item, indent + 1);
|
|
119
|
-
});
|
|
120
|
-
} else if (typeof data === "object" && data !== null) {
|
|
121
|
-
for (const [key, value] of Object.entries(data)) {
|
|
122
|
-
if (typeof value === "object" && value !== null) {
|
|
123
|
-
console.log(`${prefix}${chalk2.gray(key)}:`);
|
|
124
|
-
prettyPrint(value, indent + 1);
|
|
125
|
-
} else {
|
|
126
|
-
console.log(
|
|
127
|
-
`${prefix}${chalk2.gray(key)}: ${chalk2.white(String(value))}`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
console.log(`${prefix}${chalk2.white(String(data))}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
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";
|
|
9
|
+
import chokidar from "chokidar";
|
|
10
|
+
import { Command } from "commander";
|
|
189
11
|
import ora from "ora";
|
|
190
12
|
|
|
191
13
|
// src/lib/auth.ts
|
|
192
|
-
import { access, mkdir as
|
|
14
|
+
import { access, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
193
15
|
import { homedir as homedir2 } from "os";
|
|
194
|
-
import { join as
|
|
16
|
+
import { join as join2 } from "path";
|
|
195
17
|
import { z as z2 } from "zod";
|
|
196
18
|
|
|
197
19
|
// src/lib/config.ts
|
|
198
|
-
import { existsSync
|
|
199
|
-
import { mkdir
|
|
20
|
+
import { existsSync } from "fs";
|
|
21
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
200
22
|
import { homedir } from "os";
|
|
201
|
-
import { join
|
|
23
|
+
import { join } from "path";
|
|
202
24
|
import { z } from "zod";
|
|
203
|
-
var LOCAL_DIR =
|
|
204
|
-
var LOCAL_FILE =
|
|
205
|
-
var GLOBAL_DIR =
|
|
206
|
-
var GLOBAL_FILE =
|
|
25
|
+
var LOCAL_DIR = join(process.cwd(), ".waniwani");
|
|
26
|
+
var LOCAL_FILE = join(LOCAL_DIR, "settings.json");
|
|
27
|
+
var GLOBAL_DIR = join(homedir(), ".waniwani");
|
|
28
|
+
var GLOBAL_FILE = join(GLOBAL_DIR, "settings.json");
|
|
207
29
|
var DEFAULT_API_URL = "https://app.waniwani.ai";
|
|
208
30
|
var ConfigSchema = z.object({
|
|
209
31
|
defaults: z.object({ model: z.string(), maxSteps: z.number() }).default({ model: "claude-sonnet-4-20250514", maxSteps: 10 }),
|
|
@@ -216,7 +38,7 @@ var Config = class {
|
|
|
216
38
|
cache = null;
|
|
217
39
|
scope;
|
|
218
40
|
constructor(forceGlobal = false) {
|
|
219
|
-
const useLocal = !forceGlobal &&
|
|
41
|
+
const useLocal = !forceGlobal && existsSync(LOCAL_DIR);
|
|
220
42
|
this.dir = useLocal ? LOCAL_DIR : GLOBAL_DIR;
|
|
221
43
|
this.file = useLocal ? LOCAL_FILE : GLOBAL_FILE;
|
|
222
44
|
this.scope = useLocal ? "local" : "global";
|
|
@@ -235,8 +57,8 @@ var Config = class {
|
|
|
235
57
|
}
|
|
236
58
|
async save(data) {
|
|
237
59
|
this.cache = data;
|
|
238
|
-
await
|
|
239
|
-
await
|
|
60
|
+
await mkdir(this.dir, { recursive: true });
|
|
61
|
+
await writeFile(this.file, JSON.stringify(data, null, " "));
|
|
240
62
|
}
|
|
241
63
|
async getDefaults() {
|
|
242
64
|
return (await this.load()).defaults;
|
|
@@ -271,8 +93,8 @@ var config = new Config();
|
|
|
271
93
|
var globalConfig = new Config(true);
|
|
272
94
|
|
|
273
95
|
// src/lib/auth.ts
|
|
274
|
-
var CONFIG_DIR =
|
|
275
|
-
var AUTH_FILE =
|
|
96
|
+
var CONFIG_DIR = join2(homedir2(), ".waniwani");
|
|
97
|
+
var AUTH_FILE = join2(CONFIG_DIR, "auth.json");
|
|
276
98
|
var AuthStoreSchema = z2.object({
|
|
277
99
|
accessToken: z2.string().nullable().default(null),
|
|
278
100
|
refreshToken: z2.string().nullable().default(null),
|
|
@@ -280,7 +102,7 @@ var AuthStoreSchema = z2.object({
|
|
|
280
102
|
clientId: z2.string().nullable().default(null)
|
|
281
103
|
});
|
|
282
104
|
async function ensureConfigDir() {
|
|
283
|
-
await
|
|
105
|
+
await mkdir2(CONFIG_DIR, { recursive: true });
|
|
284
106
|
}
|
|
285
107
|
async function readAuthStore() {
|
|
286
108
|
await ensureConfigDir();
|
|
@@ -294,7 +116,7 @@ async function readAuthStore() {
|
|
|
294
116
|
}
|
|
295
117
|
async function writeAuthStore(store) {
|
|
296
118
|
await ensureConfigDir();
|
|
297
|
-
await
|
|
119
|
+
await writeFile2(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
298
120
|
}
|
|
299
121
|
var AuthManager = class {
|
|
300
122
|
storeCache = null;
|
|
@@ -359,22 +181,612 @@ var AuthManager = class {
|
|
|
359
181
|
await this.clear();
|
|
360
182
|
return false;
|
|
361
183
|
}
|
|
362
|
-
const data = await response.json();
|
|
363
|
-
await this.setTokens(
|
|
364
|
-
data.access_token,
|
|
365
|
-
data.refresh_token,
|
|
366
|
-
data.expires_in
|
|
184
|
+
const data = await response.json();
|
|
185
|
+
await this.setTokens(
|
|
186
|
+
data.access_token,
|
|
187
|
+
data.refresh_token,
|
|
188
|
+
data.expires_in
|
|
189
|
+
);
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
await this.clear();
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
var auth = new AuthManager();
|
|
198
|
+
|
|
199
|
+
// src/lib/errors.ts
|
|
200
|
+
import chalk from "chalk";
|
|
201
|
+
import { ZodError } from "zod";
|
|
202
|
+
var CLIError = class extends Error {
|
|
203
|
+
constructor(message, code, details) {
|
|
204
|
+
super(message);
|
|
205
|
+
this.code = code;
|
|
206
|
+
this.details = details;
|
|
207
|
+
this.name = "CLIError";
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
var AuthError = class extends CLIError {
|
|
211
|
+
constructor(message, details) {
|
|
212
|
+
super(message, "AUTH_ERROR", details);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var SandboxError = class extends CLIError {
|
|
216
|
+
constructor(message, details) {
|
|
217
|
+
super(message, "SANDBOX_ERROR", details);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
var McpError = class extends CLIError {
|
|
221
|
+
constructor(message, details) {
|
|
222
|
+
super(message, "MCP_ERROR", details);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function handleError(error, json) {
|
|
226
|
+
if (error instanceof ZodError) {
|
|
227
|
+
const message = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
228
|
+
outputError("VALIDATION_ERROR", `Invalid input: ${message}`, json);
|
|
229
|
+
} else if (error instanceof CLIError) {
|
|
230
|
+
outputError(error.code, error.message, json, error.details);
|
|
231
|
+
} else if (error instanceof Error) {
|
|
232
|
+
outputError("UNKNOWN_ERROR", error.message, json);
|
|
233
|
+
} else {
|
|
234
|
+
outputError("UNKNOWN_ERROR", String(error), json);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function outputError(code, message, json, details) {
|
|
238
|
+
if (json) {
|
|
239
|
+
console.error(
|
|
240
|
+
JSON.stringify({ success: false, error: { code, message, details } })
|
|
241
|
+
);
|
|
242
|
+
} else {
|
|
243
|
+
console.error(chalk.red(`Error [${code}]:`), message);
|
|
244
|
+
if (details) {
|
|
245
|
+
console.error(chalk.gray("Details:"), JSON.stringify(details, null, 2));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/lib/api.ts
|
|
251
|
+
var ApiError = class extends CLIError {
|
|
252
|
+
constructor(message, code, statusCode, details) {
|
|
253
|
+
super(message, code, details);
|
|
254
|
+
this.statusCode = statusCode;
|
|
255
|
+
this.name = "ApiError";
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
async function request(method, path, options) {
|
|
259
|
+
const {
|
|
260
|
+
body,
|
|
261
|
+
requireAuth = true,
|
|
262
|
+
headers: extraHeaders = {}
|
|
263
|
+
} = options || {};
|
|
264
|
+
const headers = {
|
|
265
|
+
"Content-Type": "application/json",
|
|
266
|
+
...extraHeaders
|
|
267
|
+
};
|
|
268
|
+
if (requireAuth) {
|
|
269
|
+
const token = await auth.getAccessToken();
|
|
270
|
+
if (!token) {
|
|
271
|
+
throw new AuthError(
|
|
272
|
+
"Not logged in. Run 'waniwani login' to authenticate."
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
headers.Authorization = `Bearer ${token}`;
|
|
276
|
+
}
|
|
277
|
+
const baseUrl = await config.getApiUrl();
|
|
278
|
+
const url = `${baseUrl}${path}`;
|
|
279
|
+
const response = await fetch(url, {
|
|
280
|
+
method,
|
|
281
|
+
headers,
|
|
282
|
+
body: body ? JSON.stringify(body) : void 0
|
|
283
|
+
});
|
|
284
|
+
if (response.status === 204) {
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
let data;
|
|
288
|
+
let rawBody;
|
|
289
|
+
try {
|
|
290
|
+
rawBody = await response.text();
|
|
291
|
+
data = JSON.parse(rawBody);
|
|
292
|
+
} catch {
|
|
293
|
+
throw new ApiError(
|
|
294
|
+
rawBody || `Request failed with status ${response.status}`,
|
|
295
|
+
"API_ERROR",
|
|
296
|
+
response.status,
|
|
297
|
+
{ statusText: response.statusText }
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
if (!response.ok || data.error) {
|
|
301
|
+
const errorMessage = data.error?.message || data.message || data.error || rawBody || `Request failed with status ${response.status}`;
|
|
302
|
+
const errorCode = data.error?.code || data.code || "API_ERROR";
|
|
303
|
+
const errorDetails = {
|
|
304
|
+
...data.error?.details,
|
|
305
|
+
statusText: response.statusText,
|
|
306
|
+
...data.error ? {} : { rawResponse: data }
|
|
307
|
+
};
|
|
308
|
+
const error = {
|
|
309
|
+
code: errorCode,
|
|
310
|
+
message: errorMessage,
|
|
311
|
+
details: errorDetails
|
|
312
|
+
};
|
|
313
|
+
if (response.status === 401) {
|
|
314
|
+
const refreshed = await auth.tryRefreshToken();
|
|
315
|
+
if (refreshed) {
|
|
316
|
+
return request(method, path, options);
|
|
317
|
+
}
|
|
318
|
+
throw new AuthError(
|
|
319
|
+
"Session expired. Run 'waniwani login' to re-authenticate."
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
throw new ApiError(
|
|
323
|
+
error.message,
|
|
324
|
+
error.code,
|
|
325
|
+
response.status,
|
|
326
|
+
error.details
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
return data.data;
|
|
330
|
+
}
|
|
331
|
+
var api = {
|
|
332
|
+
get: (path, options) => request("GET", path, options),
|
|
333
|
+
post: (path, body, options) => request("POST", path, { body, ...options }),
|
|
334
|
+
delete: (path, options) => request("DELETE", path, options),
|
|
335
|
+
getBaseUrl: () => config.getApiUrl()
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// src/lib/sync.ts
|
|
339
|
+
import { existsSync as existsSync2 } from "fs";
|
|
340
|
+
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
341
|
+
import { dirname, join as join3, relative } from "path";
|
|
342
|
+
import ignore from "ignore";
|
|
343
|
+
|
|
344
|
+
// src/lib/utils.ts
|
|
345
|
+
function debounce(fn, delay) {
|
|
346
|
+
let timeoutId;
|
|
347
|
+
return (...args) => {
|
|
348
|
+
clearTimeout(timeoutId);
|
|
349
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
353
|
+
".png",
|
|
354
|
+
".jpg",
|
|
355
|
+
".jpeg",
|
|
356
|
+
".gif",
|
|
357
|
+
".ico",
|
|
358
|
+
".webp",
|
|
359
|
+
".svg",
|
|
360
|
+
".woff",
|
|
361
|
+
".woff2",
|
|
362
|
+
".ttf",
|
|
363
|
+
".eot",
|
|
364
|
+
".otf",
|
|
365
|
+
".zip",
|
|
366
|
+
".tar",
|
|
367
|
+
".gz",
|
|
368
|
+
".pdf",
|
|
369
|
+
".exe",
|
|
370
|
+
".dll",
|
|
371
|
+
".so",
|
|
372
|
+
".dylib",
|
|
373
|
+
".bin",
|
|
374
|
+
".mp3",
|
|
375
|
+
".mp4",
|
|
376
|
+
".wav",
|
|
377
|
+
".ogg",
|
|
378
|
+
".webm"
|
|
379
|
+
]);
|
|
380
|
+
function isBinaryPath(filePath) {
|
|
381
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
382
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
383
|
+
}
|
|
384
|
+
function detectBinary(buffer) {
|
|
385
|
+
const sample = buffer.subarray(0, 8192);
|
|
386
|
+
return sample.includes(0);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/lib/sync.ts
|
|
390
|
+
var PROJECT_DIR = ".waniwani";
|
|
391
|
+
var SETTINGS_FILE = "settings.json";
|
|
392
|
+
async function findProjectRoot(startDir) {
|
|
393
|
+
let current = startDir;
|
|
394
|
+
const root = dirname(current);
|
|
395
|
+
while (current !== root) {
|
|
396
|
+
if (existsSync2(join3(current, PROJECT_DIR))) {
|
|
397
|
+
return current;
|
|
398
|
+
}
|
|
399
|
+
const parent = dirname(current);
|
|
400
|
+
if (parent === current) break;
|
|
401
|
+
current = parent;
|
|
402
|
+
}
|
|
403
|
+
if (existsSync2(join3(current, PROJECT_DIR))) {
|
|
404
|
+
return current;
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
async function loadProjectMcpId(projectRoot) {
|
|
409
|
+
const settingsPath = join3(projectRoot, PROJECT_DIR, SETTINGS_FILE);
|
|
410
|
+
try {
|
|
411
|
+
const content = await readFile3(settingsPath, "utf-8");
|
|
412
|
+
const settings = JSON.parse(content);
|
|
413
|
+
return settings.mcpId ?? null;
|
|
414
|
+
} catch {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
419
|
+
".waniwani",
|
|
420
|
+
".git",
|
|
421
|
+
"node_modules",
|
|
422
|
+
".env",
|
|
423
|
+
".env.*",
|
|
424
|
+
".DS_Store",
|
|
425
|
+
"*.log",
|
|
426
|
+
".cache",
|
|
427
|
+
"dist",
|
|
428
|
+
"coverage",
|
|
429
|
+
".turbo",
|
|
430
|
+
".next",
|
|
431
|
+
".nuxt",
|
|
432
|
+
".vercel"
|
|
433
|
+
];
|
|
434
|
+
async function loadIgnorePatterns(projectRoot) {
|
|
435
|
+
const ig = ignore();
|
|
436
|
+
ig.add(DEFAULT_IGNORE_PATTERNS);
|
|
437
|
+
const gitignorePath = join3(projectRoot, ".gitignore");
|
|
438
|
+
if (existsSync2(gitignorePath)) {
|
|
439
|
+
try {
|
|
440
|
+
const content = await readFile3(gitignorePath, "utf-8");
|
|
441
|
+
ig.add(content);
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return ig;
|
|
446
|
+
}
|
|
447
|
+
async function collectFiles(projectRoot) {
|
|
448
|
+
const ig = await loadIgnorePatterns(projectRoot);
|
|
449
|
+
const files = [];
|
|
450
|
+
async function walk(dir) {
|
|
451
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
452
|
+
for (const entry of entries) {
|
|
453
|
+
const fullPath = join3(dir, entry.name);
|
|
454
|
+
const relativePath = relative(projectRoot, fullPath);
|
|
455
|
+
if (ig.ignores(relativePath)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (entry.isDirectory()) {
|
|
459
|
+
await walk(fullPath);
|
|
460
|
+
} else if (entry.isFile()) {
|
|
461
|
+
try {
|
|
462
|
+
const content = await readFile3(fullPath);
|
|
463
|
+
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
464
|
+
files.push({
|
|
465
|
+
path: relativePath,
|
|
466
|
+
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
467
|
+
encoding: isBinary ? "base64" : "utf8"
|
|
468
|
+
});
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
await walk(projectRoot);
|
|
475
|
+
return files;
|
|
476
|
+
}
|
|
477
|
+
async function collectSingleFile(projectRoot, filePath) {
|
|
478
|
+
const fullPath = join3(projectRoot, filePath);
|
|
479
|
+
const relativePath = relative(projectRoot, fullPath);
|
|
480
|
+
if (!existsSync2(fullPath)) {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const fileStat = await stat(fullPath);
|
|
485
|
+
if (!fileStat.isFile()) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
const content = await readFile3(fullPath);
|
|
489
|
+
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
490
|
+
return {
|
|
491
|
+
path: relativePath,
|
|
492
|
+
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
493
|
+
encoding: isBinary ? "base64" : "utf8"
|
|
494
|
+
};
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/commands/dev.ts
|
|
501
|
+
var BATCH_SIZE = 50;
|
|
502
|
+
var DEFAULT_DEBOUNCE_MS = 300;
|
|
503
|
+
var devCommand = new Command("dev").description("Watch and sync files to MCP sandbox").option("--no-initial-sync", "Skip initial sync of all files").option(
|
|
504
|
+
"--debounce <ms>",
|
|
505
|
+
"Debounce delay in milliseconds",
|
|
506
|
+
String(DEFAULT_DEBOUNCE_MS)
|
|
507
|
+
).action(async (options, command) => {
|
|
508
|
+
const globalOptions = command.optsWithGlobals();
|
|
509
|
+
const json = globalOptions.json ?? false;
|
|
510
|
+
try {
|
|
511
|
+
const cwd = process.cwd();
|
|
512
|
+
const projectRoot = await findProjectRoot(cwd);
|
|
513
|
+
if (!projectRoot) {
|
|
514
|
+
throw new CLIError(
|
|
515
|
+
"Not in a WaniWani project. Run 'waniwani init <name>' first.",
|
|
516
|
+
"NOT_IN_PROJECT"
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
const mcpId = await loadProjectMcpId(projectRoot);
|
|
520
|
+
if (!mcpId) {
|
|
521
|
+
throw new CLIError(
|
|
522
|
+
"No MCP ID found in project config. Run 'waniwani init <name>' first.",
|
|
523
|
+
"NO_MCP_ID"
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
if (options.initialSync !== false) {
|
|
527
|
+
const spinner = ora("Initial sync...").start();
|
|
528
|
+
const files = await collectFiles(projectRoot);
|
|
529
|
+
if (files.length > 0) {
|
|
530
|
+
const totalBatches = Math.ceil(files.length / BATCH_SIZE);
|
|
531
|
+
let synced = 0;
|
|
532
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
533
|
+
const batch = files.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE);
|
|
534
|
+
spinner.text = `Syncing (${i + 1}/${totalBatches})...`;
|
|
535
|
+
const result = await api.post(
|
|
536
|
+
`/api/mcp/sandboxes/${mcpId}/files`,
|
|
537
|
+
{
|
|
538
|
+
files: batch.map((f) => ({
|
|
539
|
+
path: f.path,
|
|
540
|
+
content: f.content,
|
|
541
|
+
encoding: f.encoding
|
|
542
|
+
}))
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
synced += result.written.length;
|
|
546
|
+
}
|
|
547
|
+
spinner.succeed(`Initial sync complete (${synced} files)`);
|
|
548
|
+
} else {
|
|
549
|
+
spinner.info("No files to sync");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const ig = await loadIgnorePatterns(projectRoot);
|
|
553
|
+
const debounceMs = Number.parseInt(options.debounce, 10) || DEFAULT_DEBOUNCE_MS;
|
|
554
|
+
const syncFile = debounce(async (filePath) => {
|
|
555
|
+
const relativePath = relative2(projectRoot, filePath);
|
|
556
|
+
if (ig.ignores(relativePath)) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const file = await collectSingleFile(projectRoot, relativePath);
|
|
560
|
+
if (!file) {
|
|
561
|
+
console.log(chalk2.yellow("Skipped:"), relativePath);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
await api.post(
|
|
566
|
+
`/api/mcp/sandboxes/${mcpId}/files`,
|
|
567
|
+
{
|
|
568
|
+
files: [
|
|
569
|
+
{
|
|
570
|
+
path: file.path,
|
|
571
|
+
content: file.content,
|
|
572
|
+
encoding: file.encoding
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
}
|
|
576
|
+
);
|
|
577
|
+
console.log(chalk2.green("Synced:"), relativePath);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
console.log(chalk2.red("Failed:"), relativePath);
|
|
580
|
+
if (globalOptions.verbose) {
|
|
581
|
+
console.error(error);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}, debounceMs);
|
|
585
|
+
console.log();
|
|
586
|
+
console.log(chalk2.bold("Watching for changes..."));
|
|
587
|
+
console.log(chalk2.dim("Press Ctrl+C to stop"));
|
|
588
|
+
console.log();
|
|
589
|
+
const watcher = chokidar.watch(projectRoot, {
|
|
590
|
+
ignored: (path) => {
|
|
591
|
+
const relativePath = relative2(projectRoot, path);
|
|
592
|
+
return ig.ignores(relativePath);
|
|
593
|
+
},
|
|
594
|
+
persistent: true,
|
|
595
|
+
ignoreInitial: true,
|
|
596
|
+
awaitWriteFinish: {
|
|
597
|
+
stabilityThreshold: 100,
|
|
598
|
+
pollInterval: 100
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
watcher.on("add", (path) => syncFile(path)).on("change", (path) => syncFile(path)).on("unlink", (path) => {
|
|
602
|
+
const relativePath = relative2(projectRoot, path);
|
|
603
|
+
console.log(chalk2.yellow("Deleted (local only):"), relativePath);
|
|
604
|
+
});
|
|
605
|
+
const cleanup = () => {
|
|
606
|
+
console.log();
|
|
607
|
+
console.log(chalk2.dim("Stopping watcher..."));
|
|
608
|
+
watcher.close().then(() => {
|
|
609
|
+
process.exit(0);
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
process.on("SIGINT", cleanup);
|
|
613
|
+
process.on("SIGTERM", cleanup);
|
|
614
|
+
} catch (error) {
|
|
615
|
+
handleError(error, json);
|
|
616
|
+
process.exit(1);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// src/commands/init.ts
|
|
621
|
+
import { existsSync as existsSync3 } from "fs";
|
|
622
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
623
|
+
import { join as join4 } from "path";
|
|
624
|
+
import { Command as Command2 } from "commander";
|
|
625
|
+
import degit from "degit";
|
|
626
|
+
import ora2 from "ora";
|
|
627
|
+
|
|
628
|
+
// src/lib/output.ts
|
|
629
|
+
import chalk3 from "chalk";
|
|
630
|
+
function formatOutput(data, json) {
|
|
631
|
+
if (json) {
|
|
632
|
+
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
633
|
+
} else {
|
|
634
|
+
prettyPrint(data);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function formatSuccess(message, json) {
|
|
638
|
+
if (json) {
|
|
639
|
+
console.log(JSON.stringify({ success: true, message }));
|
|
640
|
+
} else {
|
|
641
|
+
console.log(chalk3.green("\u2713"), message);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function formatTable(headers, rows, json) {
|
|
645
|
+
if (json) {
|
|
646
|
+
const data = rows.map(
|
|
647
|
+
(row) => Object.fromEntries(headers.map((header, i) => [header, row[i]]))
|
|
648
|
+
);
|
|
649
|
+
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
650
|
+
} else {
|
|
651
|
+
const colWidths = headers.map(
|
|
652
|
+
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length))
|
|
653
|
+
);
|
|
654
|
+
const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
|
|
655
|
+
const formatRow = (row) => row.map((cell, i) => ` ${(cell || "").padEnd(colWidths[i])} `).join("|");
|
|
656
|
+
console.log(chalk3.cyan(formatRow(headers)));
|
|
657
|
+
console.log(separator);
|
|
658
|
+
for (const row of rows) {
|
|
659
|
+
console.log(formatRow(row));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function formatList(items, json) {
|
|
664
|
+
if (json) {
|
|
665
|
+
const data = Object.fromEntries(
|
|
666
|
+
items.map((item) => [item.label, item.value])
|
|
667
|
+
);
|
|
668
|
+
console.log(JSON.stringify({ success: true, data }, null, 2));
|
|
669
|
+
} else {
|
|
670
|
+
const maxLabelLength = Math.max(...items.map((i) => i.label.length));
|
|
671
|
+
items.forEach((item) => {
|
|
672
|
+
console.log(
|
|
673
|
+
`${chalk3.gray(item.label.padEnd(maxLabelLength))} ${chalk3.white(item.value)}`
|
|
674
|
+
);
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function prettyPrint(data, indent = 0) {
|
|
679
|
+
const prefix = " ".repeat(indent);
|
|
680
|
+
if (Array.isArray(data)) {
|
|
681
|
+
data.forEach((item, index) => {
|
|
682
|
+
console.log(`${prefix}${chalk3.gray(`[${index}]`)}`);
|
|
683
|
+
prettyPrint(item, indent + 1);
|
|
684
|
+
});
|
|
685
|
+
} else if (typeof data === "object" && data !== null) {
|
|
686
|
+
for (const [key, value] of Object.entries(data)) {
|
|
687
|
+
if (typeof value === "object" && value !== null) {
|
|
688
|
+
console.log(`${prefix}${chalk3.gray(key)}:`);
|
|
689
|
+
prettyPrint(value, indent + 1);
|
|
690
|
+
} else {
|
|
691
|
+
console.log(
|
|
692
|
+
`${prefix}${chalk3.gray(key)}: ${chalk3.white(String(value))}`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
} else {
|
|
697
|
+
console.log(`${prefix}${chalk3.white(String(data))}`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/commands/init.ts
|
|
702
|
+
var PROJECT_CONFIG_DIR = ".waniwani";
|
|
703
|
+
var PROJECT_CONFIG_FILE = "settings.json";
|
|
704
|
+
var initCommand = new Command2("init").description("Create a new MCP project from template").argument("<name>", "Name for the MCP project").action(async (name, _, command) => {
|
|
705
|
+
const globalOptions = command.optsWithGlobals();
|
|
706
|
+
const json = globalOptions.json ?? false;
|
|
707
|
+
try {
|
|
708
|
+
const cwd = process.cwd();
|
|
709
|
+
const projectDir = join4(cwd, name);
|
|
710
|
+
if (existsSync3(projectDir)) {
|
|
711
|
+
if (json) {
|
|
712
|
+
formatOutput(
|
|
713
|
+
{
|
|
714
|
+
success: false,
|
|
715
|
+
error: `Directory "${name}" already exists`
|
|
716
|
+
},
|
|
717
|
+
true
|
|
718
|
+
);
|
|
719
|
+
} else {
|
|
720
|
+
console.error(`Error: Directory "${name}" already exists`);
|
|
721
|
+
}
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
const spinner = ora2("Creating MCP sandbox...").start();
|
|
725
|
+
const result = await api.post("/api/mcp/sandboxes", {
|
|
726
|
+
name
|
|
727
|
+
});
|
|
728
|
+
spinner.text = "Cloning template...";
|
|
729
|
+
const templateRef = result.templateBranch ? `${result.templateGitUrl}#${result.templateBranch}` : result.templateGitUrl;
|
|
730
|
+
const emitter = degit(templateRef, {
|
|
731
|
+
cache: false,
|
|
732
|
+
force: true,
|
|
733
|
+
verbose: false
|
|
734
|
+
});
|
|
735
|
+
await emitter.clone(projectDir);
|
|
736
|
+
spinner.text = "Setting up project config...";
|
|
737
|
+
const configDir = join4(projectDir, PROJECT_CONFIG_DIR);
|
|
738
|
+
const configPath = join4(configDir, PROJECT_CONFIG_FILE);
|
|
739
|
+
await mkdir3(configDir, { recursive: true });
|
|
740
|
+
const projectConfig = {
|
|
741
|
+
mcpId: result.id,
|
|
742
|
+
defaults: {
|
|
743
|
+
model: "claude-sonnet-4-20250514",
|
|
744
|
+
maxSteps: 10
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
await writeFile3(
|
|
748
|
+
configPath,
|
|
749
|
+
JSON.stringify(projectConfig, null, " "),
|
|
750
|
+
"utf-8"
|
|
751
|
+
);
|
|
752
|
+
spinner.succeed("MCP project created");
|
|
753
|
+
if (json) {
|
|
754
|
+
formatOutput(
|
|
755
|
+
{
|
|
756
|
+
success: true,
|
|
757
|
+
projectDir,
|
|
758
|
+
mcpId: result.id,
|
|
759
|
+
sandboxId: result.sandboxId,
|
|
760
|
+
previewUrl: result.previewUrl
|
|
761
|
+
},
|
|
762
|
+
true
|
|
367
763
|
);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
764
|
+
} else {
|
|
765
|
+
console.log();
|
|
766
|
+
formatSuccess(`MCP project "${name}" created!`, false);
|
|
767
|
+
console.log();
|
|
768
|
+
console.log(` Project: ${projectDir}`);
|
|
769
|
+
console.log(` MCP ID: ${result.id}`);
|
|
770
|
+
console.log(` Preview URL: ${result.previewUrl}`);
|
|
771
|
+
console.log();
|
|
772
|
+
console.log("Next steps:");
|
|
773
|
+
console.log(` cd ${name}`);
|
|
774
|
+
console.log(" waniwani push # Sync files to sandbox");
|
|
775
|
+
console.log(" waniwani dev # Watch mode with auto-sync");
|
|
776
|
+
console.log(' waniwani task "..." # Send tasks to Claude');
|
|
372
777
|
}
|
|
778
|
+
} catch (error) {
|
|
779
|
+
handleError(error, json);
|
|
780
|
+
process.exit(1);
|
|
373
781
|
}
|
|
374
|
-
};
|
|
375
|
-
var auth = new AuthManager();
|
|
782
|
+
});
|
|
376
783
|
|
|
377
784
|
// src/commands/login.ts
|
|
785
|
+
import { spawn } from "child_process";
|
|
786
|
+
import { createServer } from "http";
|
|
787
|
+
import chalk4 from "chalk";
|
|
788
|
+
import { Command as Command3 } from "commander";
|
|
789
|
+
import ora3 from "ora";
|
|
378
790
|
var CALLBACK_PORT = 54321;
|
|
379
791
|
var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
|
|
380
792
|
var CLIENT_NAME = "waniwani-cli";
|
|
@@ -438,13 +850,106 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
438
850
|
sockets.clear();
|
|
439
851
|
server?.close();
|
|
440
852
|
};
|
|
441
|
-
const htmlResponse = (title, message,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
853
|
+
const htmlResponse = (title, message, isSuccess) => `<!DOCTYPE html>
|
|
854
|
+
<html>
|
|
855
|
+
<head>
|
|
856
|
+
<meta charset="UTF-8">
|
|
857
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
858
|
+
<title>${title} - WaniWani</title>
|
|
859
|
+
<style>
|
|
860
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
861
|
+
body {
|
|
862
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
863
|
+
min-height: 100vh;
|
|
864
|
+
display: flex;
|
|
865
|
+
align-items: center;
|
|
866
|
+
justify-content: center;
|
|
867
|
+
background: #fafafa;
|
|
868
|
+
position: relative;
|
|
869
|
+
overflow: hidden;
|
|
870
|
+
}
|
|
871
|
+
.blob {
|
|
872
|
+
position: absolute;
|
|
873
|
+
border-radius: 50%;
|
|
874
|
+
filter: blur(60px);
|
|
875
|
+
pointer-events: none;
|
|
876
|
+
}
|
|
877
|
+
.blob-1 {
|
|
878
|
+
top: 0;
|
|
879
|
+
left: 25%;
|
|
880
|
+
width: 24rem;
|
|
881
|
+
height: 24rem;
|
|
882
|
+
background: linear-gradient(to bottom right, rgba(253, 224, 71, 0.3), rgba(251, 146, 60, 0.3));
|
|
883
|
+
}
|
|
884
|
+
.blob-2 {
|
|
885
|
+
bottom: 0;
|
|
886
|
+
right: 25%;
|
|
887
|
+
width: 24rem;
|
|
888
|
+
height: 24rem;
|
|
889
|
+
background: linear-gradient(to bottom right, rgba(134, 239, 172, 0.3), rgba(52, 211, 153, 0.3));
|
|
890
|
+
}
|
|
891
|
+
.blob-3 {
|
|
892
|
+
top: 50%;
|
|
893
|
+
left: 50%;
|
|
894
|
+
transform: translate(-50%, -50%);
|
|
895
|
+
width: 40rem;
|
|
896
|
+
height: 40rem;
|
|
897
|
+
background: linear-gradient(to bottom right, rgba(255, 237, 213, 0.2), rgba(254, 249, 195, 0.2));
|
|
898
|
+
}
|
|
899
|
+
.container {
|
|
900
|
+
display: flex;
|
|
901
|
+
flex-direction: column;
|
|
902
|
+
align-items: center;
|
|
903
|
+
gap: 2rem;
|
|
904
|
+
padding: 2rem;
|
|
905
|
+
z-index: 10;
|
|
906
|
+
text-align: center;
|
|
907
|
+
}
|
|
908
|
+
.logo {
|
|
909
|
+
height: 40px;
|
|
910
|
+
}
|
|
911
|
+
.icon-circle {
|
|
912
|
+
width: 80px;
|
|
913
|
+
height: 80px;
|
|
914
|
+
border-radius: 50%;
|
|
915
|
+
display: flex;
|
|
916
|
+
align-items: center;
|
|
917
|
+
justify-content: center;
|
|
918
|
+
background: ${isSuccess ? "rgba(52, 211, 153, 0.15)" : "rgba(239, 68, 68, 0.15)"};
|
|
919
|
+
}
|
|
920
|
+
.icon {
|
|
921
|
+
width: 40px;
|
|
922
|
+
height: 40px;
|
|
923
|
+
color: ${isSuccess ? "#10b981" : "#ef4444"};
|
|
924
|
+
}
|
|
925
|
+
h1 {
|
|
926
|
+
font-size: 2rem;
|
|
927
|
+
font-weight: 700;
|
|
928
|
+
color: #1e293b;
|
|
929
|
+
}
|
|
930
|
+
p {
|
|
931
|
+
font-size: 1.125rem;
|
|
932
|
+
color: #64748b;
|
|
933
|
+
max-width: 400px;
|
|
934
|
+
}
|
|
935
|
+
</style>
|
|
936
|
+
</head>
|
|
937
|
+
<body>
|
|
938
|
+
<div class="blob blob-1"></div>
|
|
939
|
+
<div class="blob blob-2"></div>
|
|
940
|
+
<div class="blob blob-3"></div>
|
|
941
|
+
<div class="container">
|
|
942
|
+
<svg class="logo" viewBox="0 0 248 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
943
|
+
<text x="0" y="32" font-family="system-ui" font-size="28" font-weight="bold" fill="#1e293b">WaniWani</text>
|
|
944
|
+
</svg>
|
|
945
|
+
<div class="icon-circle">
|
|
946
|
+
${isSuccess ? '<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"></path></svg>' : '<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>'}
|
|
947
|
+
</div>
|
|
948
|
+
<h1>${title}</h1>
|
|
949
|
+
<p>${message}</p>
|
|
950
|
+
</div>
|
|
951
|
+
</body>
|
|
952
|
+
</html>`;
|
|
448
953
|
try {
|
|
449
954
|
server = createServer((req, res) => {
|
|
450
955
|
const url = new URL(
|
|
@@ -458,7 +963,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
458
963
|
res.setHeader("Content-Type", "text/html");
|
|
459
964
|
if (error) {
|
|
460
965
|
res.statusCode = 400;
|
|
461
|
-
res.end(htmlResponse("Login Failed", `Error: ${error}`,
|
|
966
|
+
res.end(htmlResponse("Login Failed", `Error: ${error}`, false));
|
|
462
967
|
cleanup();
|
|
463
968
|
reject(new CLIError(`OAuth error: ${error}`, "OAUTH_ERROR"));
|
|
464
969
|
return;
|
|
@@ -469,7 +974,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
469
974
|
htmlResponse(
|
|
470
975
|
"Login Failed",
|
|
471
976
|
"Invalid state parameter. Please try again.",
|
|
472
|
-
|
|
977
|
+
false
|
|
473
978
|
)
|
|
474
979
|
);
|
|
475
980
|
cleanup();
|
|
@@ -482,7 +987,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
482
987
|
htmlResponse(
|
|
483
988
|
"Login Failed",
|
|
484
989
|
"No authorization code received.",
|
|
485
|
-
|
|
990
|
+
false
|
|
486
991
|
)
|
|
487
992
|
);
|
|
488
993
|
cleanup();
|
|
@@ -494,7 +999,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
494
999
|
htmlResponse(
|
|
495
1000
|
"Login Successful!",
|
|
496
1001
|
"You can close this window and return to the terminal.",
|
|
497
|
-
|
|
1002
|
+
true
|
|
498
1003
|
)
|
|
499
1004
|
);
|
|
500
1005
|
setTimeout(() => {
|
|
@@ -556,7 +1061,7 @@ async function exchangeCodeForToken(code, codeVerifier, clientId, resource) {
|
|
|
556
1061
|
}
|
|
557
1062
|
return response.json();
|
|
558
1063
|
}
|
|
559
|
-
var loginCommand = new
|
|
1064
|
+
var loginCommand = new Command3("login").description("Log in to WaniWani").option("--no-browser", "Don't open the browser automatically").action(async (options, command) => {
|
|
560
1065
|
const globalOptions = command.optsWithGlobals();
|
|
561
1066
|
const json = globalOptions.json ?? false;
|
|
562
1067
|
try {
|
|
@@ -568,14 +1073,14 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
|
|
|
568
1073
|
formatOutput({ alreadyLoggedIn: true, refreshed: true }, true);
|
|
569
1074
|
} else {
|
|
570
1075
|
console.log(
|
|
571
|
-
|
|
1076
|
+
chalk4.green("Session refreshed. You're still logged in.")
|
|
572
1077
|
);
|
|
573
1078
|
}
|
|
574
1079
|
return;
|
|
575
1080
|
}
|
|
576
1081
|
if (!json) {
|
|
577
1082
|
console.log(
|
|
578
|
-
|
|
1083
|
+
chalk4.yellow("Session expired. Starting new login flow...")
|
|
579
1084
|
);
|
|
580
1085
|
}
|
|
581
1086
|
await auth.clear();
|
|
@@ -584,7 +1089,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
|
|
|
584
1089
|
formatOutput({ alreadyLoggedIn: true }, true);
|
|
585
1090
|
} else {
|
|
586
1091
|
console.log(
|
|
587
|
-
|
|
1092
|
+
chalk4.yellow(
|
|
588
1093
|
"Already logged in. Use 'waniwani logout' to log out first."
|
|
589
1094
|
)
|
|
590
1095
|
);
|
|
@@ -593,9 +1098,9 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
|
|
|
593
1098
|
}
|
|
594
1099
|
}
|
|
595
1100
|
if (!json) {
|
|
596
|
-
console.log(
|
|
1101
|
+
console.log(chalk4.bold("\nWaniWani CLI Login\n"));
|
|
597
1102
|
}
|
|
598
|
-
const spinner =
|
|
1103
|
+
const spinner = ora3("Registering client...").start();
|
|
599
1104
|
const { client_id: clientId } = await registerClient();
|
|
600
1105
|
spinner.text = "Preparing authentication...";
|
|
601
1106
|
const codeVerifier = generateCodeVerifier();
|
|
@@ -615,7 +1120,7 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
|
|
|
615
1120
|
console.log("Opening browser for authentication...\n");
|
|
616
1121
|
console.log(`If the browser doesn't open, visit:
|
|
617
1122
|
`);
|
|
618
|
-
console.log(
|
|
1123
|
+
console.log(chalk4.cyan(` ${authUrl.toString()}`));
|
|
619
1124
|
console.log();
|
|
620
1125
|
}
|
|
621
1126
|
const callbackPromise = waitForCallback(state);
|
|
@@ -661,8 +1166,8 @@ var loginCommand = new Command2("login").description("Log in to WaniWani").optio
|
|
|
661
1166
|
});
|
|
662
1167
|
|
|
663
1168
|
// src/commands/logout.ts
|
|
664
|
-
import { Command as
|
|
665
|
-
var logoutCommand = new
|
|
1169
|
+
import { Command as Command4 } from "commander";
|
|
1170
|
+
var logoutCommand = new Command4("logout").description("Log out from WaniWani").action(async (_, command) => {
|
|
666
1171
|
const globalOptions = command.optsWithGlobals();
|
|
667
1172
|
const json = globalOptions.json ?? false;
|
|
668
1173
|
try {
|
|
@@ -689,134 +1194,9 @@ var logoutCommand = new Command3("logout").description("Log out from WaniWani").
|
|
|
689
1194
|
// src/commands/mcp/index.ts
|
|
690
1195
|
import { Command as Command19 } from "commander";
|
|
691
1196
|
|
|
692
|
-
// src/commands/mcp/create.ts
|
|
693
|
-
import { Command as Command4 } from "commander";
|
|
694
|
-
import ora2 from "ora";
|
|
695
|
-
|
|
696
|
-
// src/lib/api.ts
|
|
697
|
-
var ApiError = class extends CLIError {
|
|
698
|
-
constructor(message, code, statusCode, details) {
|
|
699
|
-
super(message, code, details);
|
|
700
|
-
this.statusCode = statusCode;
|
|
701
|
-
this.name = "ApiError";
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
async function request(method, path, options) {
|
|
705
|
-
const {
|
|
706
|
-
body,
|
|
707
|
-
requireAuth = true,
|
|
708
|
-
headers: extraHeaders = {}
|
|
709
|
-
} = options || {};
|
|
710
|
-
const headers = {
|
|
711
|
-
"Content-Type": "application/json",
|
|
712
|
-
...extraHeaders
|
|
713
|
-
};
|
|
714
|
-
if (requireAuth) {
|
|
715
|
-
const token = await auth.getAccessToken();
|
|
716
|
-
if (!token) {
|
|
717
|
-
throw new AuthError(
|
|
718
|
-
"Not logged in. Run 'waniwani login' to authenticate."
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
headers.Authorization = `Bearer ${token}`;
|
|
722
|
-
}
|
|
723
|
-
const baseUrl = await config.getApiUrl();
|
|
724
|
-
const url = `${baseUrl}${path}`;
|
|
725
|
-
const response = await fetch(url, {
|
|
726
|
-
method,
|
|
727
|
-
headers,
|
|
728
|
-
body: body ? JSON.stringify(body) : void 0
|
|
729
|
-
});
|
|
730
|
-
if (response.status === 204) {
|
|
731
|
-
return void 0;
|
|
732
|
-
}
|
|
733
|
-
let data;
|
|
734
|
-
let rawBody;
|
|
735
|
-
try {
|
|
736
|
-
rawBody = await response.text();
|
|
737
|
-
data = JSON.parse(rawBody);
|
|
738
|
-
} catch {
|
|
739
|
-
throw new ApiError(
|
|
740
|
-
rawBody || `Request failed with status ${response.status}`,
|
|
741
|
-
"API_ERROR",
|
|
742
|
-
response.status,
|
|
743
|
-
{ statusText: response.statusText }
|
|
744
|
-
);
|
|
745
|
-
}
|
|
746
|
-
if (!response.ok || data.error) {
|
|
747
|
-
const errorMessage = data.error?.message || data.message || data.error || rawBody || `Request failed with status ${response.status}`;
|
|
748
|
-
const errorCode = data.error?.code || data.code || "API_ERROR";
|
|
749
|
-
const errorDetails = {
|
|
750
|
-
...data.error?.details,
|
|
751
|
-
statusText: response.statusText,
|
|
752
|
-
...data.error ? {} : { rawResponse: data }
|
|
753
|
-
};
|
|
754
|
-
const error = {
|
|
755
|
-
code: errorCode,
|
|
756
|
-
message: errorMessage,
|
|
757
|
-
details: errorDetails
|
|
758
|
-
};
|
|
759
|
-
if (response.status === 401) {
|
|
760
|
-
const refreshed = await auth.tryRefreshToken();
|
|
761
|
-
if (refreshed) {
|
|
762
|
-
return request(method, path, options);
|
|
763
|
-
}
|
|
764
|
-
throw new AuthError(
|
|
765
|
-
"Session expired. Run 'waniwani login' to re-authenticate."
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
throw new ApiError(
|
|
769
|
-
error.message,
|
|
770
|
-
error.code,
|
|
771
|
-
response.status,
|
|
772
|
-
error.details
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
return data.data;
|
|
776
|
-
}
|
|
777
|
-
var api = {
|
|
778
|
-
get: (path, options) => request("GET", path, options),
|
|
779
|
-
post: (path, body, options) => request("POST", path, { body, ...options }),
|
|
780
|
-
delete: (path, options) => request("DELETE", path, options),
|
|
781
|
-
getBaseUrl: () => config.getApiUrl()
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
// src/commands/mcp/create.ts
|
|
785
|
-
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) => {
|
|
786
|
-
const globalOptions = command.optsWithGlobals();
|
|
787
|
-
const json = globalOptions.json ?? false;
|
|
788
|
-
try {
|
|
789
|
-
const spinner = ora2("Creating MCP sandbox...").start();
|
|
790
|
-
const result = await api.post("/api/mcp/sandboxes", {
|
|
791
|
-
name
|
|
792
|
-
});
|
|
793
|
-
spinner.succeed("MCP sandbox created");
|
|
794
|
-
const cfg = options.global ? globalConfig : config;
|
|
795
|
-
await cfg.setMcpId(result.id);
|
|
796
|
-
if (json) {
|
|
797
|
-
formatOutput({ ...result, scope: cfg.scope }, true);
|
|
798
|
-
} else {
|
|
799
|
-
console.log();
|
|
800
|
-
formatSuccess(`MCP sandbox "${name}" created! (${cfg.scope})`, false);
|
|
801
|
-
console.log();
|
|
802
|
-
console.log(` MCP ID: ${result.id}`);
|
|
803
|
-
console.log(` Sandbox ID: ${result.sandboxId}`);
|
|
804
|
-
console.log(` Preview URL: ${result.previewUrl}`);
|
|
805
|
-
console.log();
|
|
806
|
-
console.log(`Next steps:`);
|
|
807
|
-
console.log(` waniwani task "Add a tool that does X"`);
|
|
808
|
-
console.log(` waniwani mcp test`);
|
|
809
|
-
console.log(` waniwani mcp deploy`);
|
|
810
|
-
}
|
|
811
|
-
} catch (error) {
|
|
812
|
-
handleError(error, json);
|
|
813
|
-
process.exit(1);
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
|
|
817
1197
|
// src/commands/mcp/delete.ts
|
|
818
1198
|
import { Command as Command5 } from "commander";
|
|
819
|
-
import
|
|
1199
|
+
import ora4 from "ora";
|
|
820
1200
|
var deleteCommand = new Command5("delete").description("Delete the MCP sandbox").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
821
1201
|
const globalOptions = command.optsWithGlobals();
|
|
822
1202
|
const json = globalOptions.json ?? false;
|
|
@@ -828,7 +1208,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
|
|
|
828
1208
|
throw new McpError("No active MCP. Use --mcp-id to specify one.");
|
|
829
1209
|
}
|
|
830
1210
|
}
|
|
831
|
-
const spinner =
|
|
1211
|
+
const spinner = ora4("Deleting MCP sandbox...").start();
|
|
832
1212
|
await api.delete(`/api/mcp/sandboxes/${mcpId}`);
|
|
833
1213
|
spinner.succeed("MCP sandbox deleted");
|
|
834
1214
|
if (await config.getMcpId() === mcpId) {
|
|
@@ -847,7 +1227,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP sandbox")
|
|
|
847
1227
|
|
|
848
1228
|
// src/commands/mcp/deploy.ts
|
|
849
1229
|
import { Command as Command6 } from "commander";
|
|
850
|
-
import
|
|
1230
|
+
import ora5 from "ora";
|
|
851
1231
|
var deployCommand = new Command6("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) => {
|
|
852
1232
|
const globalOptions = command.optsWithGlobals();
|
|
853
1233
|
const json = globalOptions.json ?? false;
|
|
@@ -861,7 +1241,7 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
|
|
|
861
1241
|
);
|
|
862
1242
|
}
|
|
863
1243
|
}
|
|
864
|
-
const spinner =
|
|
1244
|
+
const spinner = ora5("Deploying to GitHub...").start();
|
|
865
1245
|
const result = await api.post(
|
|
866
1246
|
`/api/admin/mcps/${mcpId}/deploy`,
|
|
867
1247
|
{
|
|
@@ -896,9 +1276,9 @@ var deployCommand = new Command6("deploy").description("Deploy MCP server to Git
|
|
|
896
1276
|
import { Command as Command10 } from "commander";
|
|
897
1277
|
|
|
898
1278
|
// src/commands/mcp/file/list.ts
|
|
899
|
-
import
|
|
1279
|
+
import chalk5 from "chalk";
|
|
900
1280
|
import { Command as Command7 } from "commander";
|
|
901
|
-
import
|
|
1281
|
+
import ora6 from "ora";
|
|
902
1282
|
var listCommand = new Command7("list").description("List files in the MCP sandbox").argument("[path]", "Directory path (defaults to /app)", "/app").option("--mcp-id <id>", "Specific MCP ID").action(async (path, options, command) => {
|
|
903
1283
|
const globalOptions = command.optsWithGlobals();
|
|
904
1284
|
const json = globalOptions.json ?? false;
|
|
@@ -912,7 +1292,7 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
|
|
|
912
1292
|
);
|
|
913
1293
|
}
|
|
914
1294
|
}
|
|
915
|
-
const spinner =
|
|
1295
|
+
const spinner = ora6(`Listing ${path}...`).start();
|
|
916
1296
|
const result = await api.get(
|
|
917
1297
|
`/api/mcp/sandboxes/${mcpId}/files/list?path=${encodeURIComponent(path)}`
|
|
918
1298
|
);
|
|
@@ -920,15 +1300,15 @@ var listCommand = new Command7("list").description("List files in the MCP sandbo
|
|
|
920
1300
|
if (json) {
|
|
921
1301
|
formatOutput(result, true);
|
|
922
1302
|
} else {
|
|
923
|
-
console.log(
|
|
1303
|
+
console.log(chalk5.bold(`
|
|
924
1304
|
Directory: ${result.path}
|
|
925
1305
|
`));
|
|
926
1306
|
if (result.entries.length === 0) {
|
|
927
1307
|
console.log(" (empty)");
|
|
928
1308
|
} else {
|
|
929
1309
|
const rows = result.entries.map((entry) => {
|
|
930
|
-
const name = entry.type === "directory" ?
|
|
931
|
-
const size = entry.type === "directory" ?
|
|
1310
|
+
const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
|
|
1311
|
+
const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
|
|
932
1312
|
return [name, size];
|
|
933
1313
|
});
|
|
934
1314
|
formatTable(["Name", "Size"], rows, false);
|
|
@@ -950,7 +1330,7 @@ function formatSize(bytes) {
|
|
|
950
1330
|
// src/commands/mcp/file/read.ts
|
|
951
1331
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
952
1332
|
import { Command as Command8 } from "commander";
|
|
953
|
-
import
|
|
1333
|
+
import ora7 from "ora";
|
|
954
1334
|
var readCommand = new Command8("read").description("Read a file from the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--output <file>", "Write to local file instead of stdout").option("--base64", "Output as base64 (for binary files)").action(async (path, options, command) => {
|
|
955
1335
|
const globalOptions = command.optsWithGlobals();
|
|
956
1336
|
const json = globalOptions.json ?? false;
|
|
@@ -965,7 +1345,7 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
|
|
|
965
1345
|
}
|
|
966
1346
|
}
|
|
967
1347
|
const encoding = options.base64 ? "base64" : "utf8";
|
|
968
|
-
const spinner =
|
|
1348
|
+
const spinner = ora7(`Reading ${path}...`).start();
|
|
969
1349
|
const result = await api.get(
|
|
970
1350
|
`/api/mcp/sandboxes/${mcpId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
|
|
971
1351
|
);
|
|
@@ -996,9 +1376,9 @@ var readCommand = new Command8("read").description("Read a file from the MCP san
|
|
|
996
1376
|
});
|
|
997
1377
|
|
|
998
1378
|
// src/commands/mcp/file/write.ts
|
|
999
|
-
import { readFile as
|
|
1379
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1000
1380
|
import { Command as Command9 } from "commander";
|
|
1001
|
-
import
|
|
1381
|
+
import ora8 from "ora";
|
|
1002
1382
|
var writeCommand = new Command9("write").description("Write a file to the MCP sandbox").argument("<path>", "Path in sandbox (e.g., /app/src/index.ts)").option("--mcp-id <id>", "Specific MCP ID").option("--content <content>", "Content to write").option("--file <localFile>", "Local file to upload").option("--base64", "Treat content as base64 encoded").action(async (path, options, command) => {
|
|
1003
1383
|
const globalOptions = command.optsWithGlobals();
|
|
1004
1384
|
const json = globalOptions.json ?? false;
|
|
@@ -1020,7 +1400,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
|
|
|
1020
1400
|
encoding = "base64";
|
|
1021
1401
|
}
|
|
1022
1402
|
} else if (options.file) {
|
|
1023
|
-
const fileBuffer = await
|
|
1403
|
+
const fileBuffer = await readFile4(options.file);
|
|
1024
1404
|
if (options.base64) {
|
|
1025
1405
|
content = fileBuffer.toString("base64");
|
|
1026
1406
|
encoding = "base64";
|
|
@@ -1033,7 +1413,7 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
|
|
|
1033
1413
|
"MISSING_CONTENT"
|
|
1034
1414
|
);
|
|
1035
1415
|
}
|
|
1036
|
-
const spinner =
|
|
1416
|
+
const spinner = ora8(`Writing ${path}...`).start();
|
|
1037
1417
|
const result = await api.post(
|
|
1038
1418
|
`/api/mcp/sandboxes/${mcpId}/files`,
|
|
1039
1419
|
{
|
|
@@ -1056,14 +1436,14 @@ var writeCommand = new Command9("write").description("Write a file to the MCP sa
|
|
|
1056
1436
|
var fileCommand = new Command10("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
|
|
1057
1437
|
|
|
1058
1438
|
// src/commands/mcp/list.ts
|
|
1059
|
-
import
|
|
1439
|
+
import chalk6 from "chalk";
|
|
1060
1440
|
import { Command as Command11 } from "commander";
|
|
1061
|
-
import
|
|
1441
|
+
import ora9 from "ora";
|
|
1062
1442
|
var listCommand2 = new Command11("list").description("List all MCPs in your organization").option("--all", "Include stopped/expired MCPs").action(async (options, command) => {
|
|
1063
1443
|
const globalOptions = command.optsWithGlobals();
|
|
1064
1444
|
const json = globalOptions.json ?? false;
|
|
1065
1445
|
try {
|
|
1066
|
-
const spinner =
|
|
1446
|
+
const spinner = ora9("Fetching MCPs...").start();
|
|
1067
1447
|
const mcps = await api.get(
|
|
1068
1448
|
`/api/mcp/sandboxes${options.all ? "?all=true" : ""}`
|
|
1069
1449
|
);
|
|
@@ -1086,12 +1466,12 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
|
|
|
1086
1466
|
console.log("\nCreate a new MCP sandbox: waniwani mcp create <name>");
|
|
1087
1467
|
return;
|
|
1088
1468
|
}
|
|
1089
|
-
console.log(
|
|
1469
|
+
console.log(chalk6.bold("\nMCPs:\n"));
|
|
1090
1470
|
const rows = mcps.map((m) => {
|
|
1091
1471
|
const isActive = m.id === activeMcpId;
|
|
1092
|
-
const statusColor = m.status === "active" ?
|
|
1472
|
+
const statusColor = m.status === "active" ? chalk6.green : m.status === "stopped" ? chalk6.red : chalk6.yellow;
|
|
1093
1473
|
return [
|
|
1094
|
-
isActive ?
|
|
1474
|
+
isActive ? chalk6.cyan(`* ${m.id.slice(0, 8)}`) : ` ${m.id.slice(0, 8)}`,
|
|
1095
1475
|
m.name,
|
|
1096
1476
|
statusColor(m.status),
|
|
1097
1477
|
m.previewUrl,
|
|
@@ -1105,7 +1485,7 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
|
|
|
1105
1485
|
);
|
|
1106
1486
|
console.log();
|
|
1107
1487
|
if (activeMcpId) {
|
|
1108
|
-
console.log(`Active MCP: ${
|
|
1488
|
+
console.log(`Active MCP: ${chalk6.cyan(activeMcpId.slice(0, 8))}`);
|
|
1109
1489
|
}
|
|
1110
1490
|
console.log("\nSelect an MCP: waniwani mcp use <name>");
|
|
1111
1491
|
}
|
|
@@ -1116,9 +1496,9 @@ var listCommand2 = new Command11("list").description("List all MCPs in your orga
|
|
|
1116
1496
|
});
|
|
1117
1497
|
|
|
1118
1498
|
// src/commands/mcp/logs.ts
|
|
1119
|
-
import
|
|
1499
|
+
import chalk7 from "chalk";
|
|
1120
1500
|
import { Command as Command12 } from "commander";
|
|
1121
|
-
import
|
|
1501
|
+
import ora10 from "ora";
|
|
1122
1502
|
var logsCommand = new Command12("logs").description("Stream logs from the MCP server").argument("[cmdId]", "Command ID (defaults to running server)").option("--mcp-id <id>", "Specific MCP ID").option("-f, --follow", "Keep streaming logs (default)", true).option("--no-follow", "Fetch logs and exit").action(async (cmdIdArg, options, command) => {
|
|
1123
1503
|
const globalOptions = command.optsWithGlobals();
|
|
1124
1504
|
const json = globalOptions.json ?? false;
|
|
@@ -1150,7 +1530,7 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
|
|
|
1150
1530
|
}
|
|
1151
1531
|
let cmdId = cmdIdArg;
|
|
1152
1532
|
if (!cmdId) {
|
|
1153
|
-
const spinner =
|
|
1533
|
+
const spinner = ora10("Getting server status...").start();
|
|
1154
1534
|
const status = await api.post(
|
|
1155
1535
|
`/api/mcp/sandboxes/${mcpId}/server`,
|
|
1156
1536
|
{ action: "status" }
|
|
@@ -1167,8 +1547,8 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
|
|
|
1167
1547
|
const streamParam = options.follow ? "?stream=true" : "";
|
|
1168
1548
|
const url = `${baseUrl}/api/mcp/sandboxes/${mcpId}/commands/${cmdId}${streamParam}`;
|
|
1169
1549
|
if (!json) {
|
|
1170
|
-
console.log(
|
|
1171
|
-
console.log(
|
|
1550
|
+
console.log(chalk7.gray(`Streaming logs for command ${cmdId}...`));
|
|
1551
|
+
console.log(chalk7.gray("Press Ctrl+C to stop\n"));
|
|
1172
1552
|
}
|
|
1173
1553
|
const response = await fetch(url, {
|
|
1174
1554
|
method: "GET",
|
|
@@ -1192,10 +1572,10 @@ var logsCommand = new Command12("logs").description("Stream logs from the MCP se
|
|
|
1192
1572
|
process.stdout.write(data.stdout);
|
|
1193
1573
|
}
|
|
1194
1574
|
if (data.stderr) {
|
|
1195
|
-
process.stderr.write(
|
|
1575
|
+
process.stderr.write(chalk7.red(data.stderr));
|
|
1196
1576
|
}
|
|
1197
1577
|
if (data.exitCode !== void 0) {
|
|
1198
|
-
console.log(
|
|
1578
|
+
console.log(chalk7.gray(`
|
|
1199
1579
|
Exit code: ${data.exitCode}`));
|
|
1200
1580
|
}
|
|
1201
1581
|
}
|
|
@@ -1234,18 +1614,18 @@ Exit code: ${data.exitCode}`));
|
|
|
1234
1614
|
if (event.stream === "stdout") {
|
|
1235
1615
|
process.stdout.write(event.data);
|
|
1236
1616
|
} else if (event.stream === "stderr") {
|
|
1237
|
-
process.stderr.write(
|
|
1617
|
+
process.stderr.write(chalk7.red(event.data));
|
|
1238
1618
|
}
|
|
1239
1619
|
}
|
|
1240
1620
|
if (event.exitCode !== void 0) {
|
|
1241
|
-
const exitColor = event.exitCode === 0 ?
|
|
1621
|
+
const exitColor = event.exitCode === 0 ? chalk7.green : chalk7.red;
|
|
1242
1622
|
console.log(
|
|
1243
1623
|
exitColor(`
|
|
1244
1624
|
Process exited with code ${event.exitCode}`)
|
|
1245
1625
|
);
|
|
1246
1626
|
}
|
|
1247
1627
|
if (event.error) {
|
|
1248
|
-
console.error(
|
|
1628
|
+
console.error(chalk7.red(`
|
|
1249
1629
|
Error: ${event.error}`));
|
|
1250
1630
|
}
|
|
1251
1631
|
} catch {
|
|
@@ -1266,9 +1646,9 @@ Error: ${event.error}`));
|
|
|
1266
1646
|
});
|
|
1267
1647
|
|
|
1268
1648
|
// src/commands/mcp/run-command.ts
|
|
1269
|
-
import
|
|
1649
|
+
import chalk8 from "chalk";
|
|
1270
1650
|
import { Command as Command13 } from "commander";
|
|
1271
|
-
import
|
|
1651
|
+
import ora11 from "ora";
|
|
1272
1652
|
var runCommandCommand = new Command13("run-command").description("Run a command in the MCP sandbox").argument("<command>", "Command to run").argument("[args...]", "Command arguments").option("--mcp-id <id>", "Specific MCP ID").option("--cwd <path>", "Working directory").option(
|
|
1273
1653
|
"--timeout <ms>",
|
|
1274
1654
|
"Command timeout in milliseconds (default: 30000, max: 300000)"
|
|
@@ -1286,7 +1666,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
|
|
|
1286
1666
|
}
|
|
1287
1667
|
}
|
|
1288
1668
|
const timeout = options.timeout ? Number.parseInt(options.timeout, 10) : void 0;
|
|
1289
|
-
const spinner =
|
|
1669
|
+
const spinner = ora11(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
|
|
1290
1670
|
const result = await api.post(
|
|
1291
1671
|
`/api/mcp/sandboxes/${mcpId}/commands`,
|
|
1292
1672
|
{
|
|
@@ -1301,7 +1681,7 @@ var runCommandCommand = new Command13("run-command").description("Run a command
|
|
|
1301
1681
|
formatOutput(result, true);
|
|
1302
1682
|
} else {
|
|
1303
1683
|
const cmdLine = [cmd, ...args].join(" ");
|
|
1304
|
-
console.log(
|
|
1684
|
+
console.log(chalk8.gray(`$ ${cmdLine}`));
|
|
1305
1685
|
console.log();
|
|
1306
1686
|
if (result.stdout) {
|
|
1307
1687
|
process.stdout.write(result.stdout);
|
|
@@ -1310,16 +1690,16 @@ var runCommandCommand = new Command13("run-command").description("Run a command
|
|
|
1310
1690
|
}
|
|
1311
1691
|
}
|
|
1312
1692
|
if (result.stderr) {
|
|
1313
|
-
process.stderr.write(
|
|
1693
|
+
process.stderr.write(chalk8.red(result.stderr));
|
|
1314
1694
|
if (!result.stderr.endsWith("\n")) {
|
|
1315
1695
|
process.stderr.write("\n");
|
|
1316
1696
|
}
|
|
1317
1697
|
}
|
|
1318
1698
|
console.log();
|
|
1319
|
-
const exitColor = result.exitCode === 0 ?
|
|
1699
|
+
const exitColor = result.exitCode === 0 ? chalk8.green : chalk8.red;
|
|
1320
1700
|
console.log(
|
|
1321
1701
|
exitColor(`Exit code: ${result.exitCode}`),
|
|
1322
|
-
|
|
1702
|
+
chalk8.gray(`(${(result.duration / 1e3).toFixed(2)}s)`)
|
|
1323
1703
|
);
|
|
1324
1704
|
}
|
|
1325
1705
|
if (result.exitCode !== 0) {
|
|
@@ -1332,9 +1712,9 @@ var runCommandCommand = new Command13("run-command").description("Run a command
|
|
|
1332
1712
|
});
|
|
1333
1713
|
|
|
1334
1714
|
// src/commands/mcp/start.ts
|
|
1335
|
-
import
|
|
1715
|
+
import chalk9 from "chalk";
|
|
1336
1716
|
import { Command as Command14 } from "commander";
|
|
1337
|
-
import
|
|
1717
|
+
import ora12 from "ora";
|
|
1338
1718
|
var startCommand = new Command14("start").description("Start the MCP server (npm run dev)").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
1339
1719
|
const globalOptions = command.optsWithGlobals();
|
|
1340
1720
|
const json = globalOptions.json ?? false;
|
|
@@ -1348,7 +1728,7 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
|
|
|
1348
1728
|
);
|
|
1349
1729
|
}
|
|
1350
1730
|
}
|
|
1351
|
-
const spinner =
|
|
1731
|
+
const spinner = ora12("Starting MCP server...").start();
|
|
1352
1732
|
const result = await api.post(
|
|
1353
1733
|
`/api/mcp/sandboxes/${mcpId}/server`,
|
|
1354
1734
|
{ action: "start" }
|
|
@@ -1361,13 +1741,13 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
|
|
|
1361
1741
|
formatList(
|
|
1362
1742
|
[
|
|
1363
1743
|
{ label: "Command ID", value: result.cmdId },
|
|
1364
|
-
{ label: "Preview URL", value:
|
|
1744
|
+
{ label: "Preview URL", value: chalk9.cyan(result.previewUrl) }
|
|
1365
1745
|
],
|
|
1366
1746
|
false
|
|
1367
1747
|
);
|
|
1368
1748
|
console.log();
|
|
1369
1749
|
console.log(
|
|
1370
|
-
|
|
1750
|
+
chalk9.gray("Run 'waniwani mcp logs' to stream server output")
|
|
1371
1751
|
);
|
|
1372
1752
|
}
|
|
1373
1753
|
} catch (error) {
|
|
@@ -1377,9 +1757,9 @@ var startCommand = new Command14("start").description("Start the MCP server (npm
|
|
|
1377
1757
|
});
|
|
1378
1758
|
|
|
1379
1759
|
// src/commands/mcp/status.ts
|
|
1380
|
-
import
|
|
1760
|
+
import chalk10 from "chalk";
|
|
1381
1761
|
import { Command as Command15 } from "commander";
|
|
1382
|
-
import
|
|
1762
|
+
import ora13 from "ora";
|
|
1383
1763
|
var statusCommand = new Command15("status").description("Show current MCP sandbox status").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
1384
1764
|
const globalOptions = command.optsWithGlobals();
|
|
1385
1765
|
const json = globalOptions.json ?? false;
|
|
@@ -1393,7 +1773,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
|
|
|
1393
1773
|
);
|
|
1394
1774
|
}
|
|
1395
1775
|
}
|
|
1396
|
-
const spinner =
|
|
1776
|
+
const spinner = ora13("Fetching MCP status...").start();
|
|
1397
1777
|
const [result, serverStatus] = await Promise.all([
|
|
1398
1778
|
api.get(`/api/mcp/sandboxes/${mcpId}`),
|
|
1399
1779
|
api.post(`/api/mcp/sandboxes/${mcpId}/server`, {
|
|
@@ -1408,9 +1788,9 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
|
|
|
1408
1788
|
if (json) {
|
|
1409
1789
|
formatOutput({ ...result, server: serverStatus }, true);
|
|
1410
1790
|
} else {
|
|
1411
|
-
const statusColor = result.status === "active" ?
|
|
1791
|
+
const statusColor = result.status === "active" ? chalk10.green : chalk10.red;
|
|
1412
1792
|
const serverRunning = serverStatus.running;
|
|
1413
|
-
const serverStatusColor = serverRunning ?
|
|
1793
|
+
const serverStatusColor = serverRunning ? chalk10.green : chalk10.yellow;
|
|
1414
1794
|
formatList(
|
|
1415
1795
|
[
|
|
1416
1796
|
{ label: "MCP ID", value: result.id },
|
|
@@ -1437,7 +1817,7 @@ var statusCommand = new Command15("status").description("Show current MCP sandbo
|
|
|
1437
1817
|
|
|
1438
1818
|
// src/commands/mcp/stop.ts
|
|
1439
1819
|
import { Command as Command16 } from "commander";
|
|
1440
|
-
import
|
|
1820
|
+
import ora14 from "ora";
|
|
1441
1821
|
var stopCommand = new Command16("stop").description("Stop the MCP server process").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
1442
1822
|
const globalOptions = command.optsWithGlobals();
|
|
1443
1823
|
const json = globalOptions.json ?? false;
|
|
@@ -1451,7 +1831,7 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
|
|
|
1451
1831
|
);
|
|
1452
1832
|
}
|
|
1453
1833
|
}
|
|
1454
|
-
const spinner =
|
|
1834
|
+
const spinner = ora14("Stopping MCP server...").start();
|
|
1455
1835
|
const result = await api.post(
|
|
1456
1836
|
`/api/mcp/sandboxes/${mcpId}/server`,
|
|
1457
1837
|
{ action: "stop" }
|
|
@@ -1473,9 +1853,9 @@ var stopCommand = new Command16("stop").description("Stop the MCP server process
|
|
|
1473
1853
|
});
|
|
1474
1854
|
|
|
1475
1855
|
// src/commands/mcp/test.ts
|
|
1476
|
-
import
|
|
1856
|
+
import chalk11 from "chalk";
|
|
1477
1857
|
import { Command as Command17 } from "commander";
|
|
1478
|
-
import
|
|
1858
|
+
import ora15 from "ora";
|
|
1479
1859
|
var testCommand = new Command17("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(
|
|
1480
1860
|
async (tool, args, options, command) => {
|
|
1481
1861
|
const globalOptions = command.optsWithGlobals();
|
|
@@ -1491,7 +1871,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
|
|
|
1491
1871
|
}
|
|
1492
1872
|
}
|
|
1493
1873
|
if (!tool) {
|
|
1494
|
-
const spinner =
|
|
1874
|
+
const spinner = ora15("Fetching available tools...").start();
|
|
1495
1875
|
const result = await api.post(
|
|
1496
1876
|
`/api/mcp/sandboxes/${mcpId}/test`,
|
|
1497
1877
|
{ action: "list" }
|
|
@@ -1504,7 +1884,7 @@ var testCommand = new Command17("test").description("Test MCP tools via the sand
|
|
|
1504
1884
|
if (tools.length === 0) {
|
|
1505
1885
|
console.log("No tools available.");
|
|
1506
1886
|
} else {
|
|
1507
|
-
console.log(
|
|
1887
|
+
console.log(chalk11.bold("\nAvailable Tools:\n"));
|
|
1508
1888
|
formatTable(
|
|
1509
1889
|
["Name", "Description"],
|
|
1510
1890
|
tools.map((t) => [t.name, t.description || "No description"]),
|
|
@@ -1527,7 +1907,7 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
|
|
|
1527
1907
|
);
|
|
1528
1908
|
}
|
|
1529
1909
|
}
|
|
1530
|
-
const spinner =
|
|
1910
|
+
const spinner = ora15(`Calling tool "${tool}"...`).start();
|
|
1531
1911
|
const startTime = Date.now();
|
|
1532
1912
|
const result = await api.post(
|
|
1533
1913
|
`/api/mcp/sandboxes/${mcpId}/test`,
|
|
@@ -1548,11 +1928,11 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
|
|
|
1548
1928
|
if (json) {
|
|
1549
1929
|
formatOutput(output, true);
|
|
1550
1930
|
} else {
|
|
1551
|
-
console.log(
|
|
1552
|
-
console.log(
|
|
1553
|
-
console.log(
|
|
1554
|
-
console.log(
|
|
1555
|
-
console.log(
|
|
1931
|
+
console.log(chalk11.bold("\nTool Result:\n"));
|
|
1932
|
+
console.log(chalk11.gray("Tool:"), tool);
|
|
1933
|
+
console.log(chalk11.gray("Input:"), JSON.stringify(toolArgs));
|
|
1934
|
+
console.log(chalk11.gray("Duration:"), `${duration}ms`);
|
|
1935
|
+
console.log(chalk11.gray("Result:"));
|
|
1556
1936
|
console.log(JSON.stringify(result.result, null, 2));
|
|
1557
1937
|
}
|
|
1558
1938
|
}
|
|
@@ -1565,12 +1945,12 @@ Test a tool: waniwani mcp test <tool-name> '{"arg": "value"}'`
|
|
|
1565
1945
|
|
|
1566
1946
|
// src/commands/mcp/use.ts
|
|
1567
1947
|
import { Command as Command18 } from "commander";
|
|
1568
|
-
import
|
|
1948
|
+
import ora16 from "ora";
|
|
1569
1949
|
var useCommand = new Command18("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) => {
|
|
1570
1950
|
const globalOptions = command.optsWithGlobals();
|
|
1571
1951
|
const json = globalOptions.json ?? false;
|
|
1572
1952
|
try {
|
|
1573
|
-
const spinner =
|
|
1953
|
+
const spinner = ora16("Fetching MCPs...").start();
|
|
1574
1954
|
const mcps = await api.get("/api/admin/mcps");
|
|
1575
1955
|
spinner.stop();
|
|
1576
1956
|
const mcp = mcps.find((m) => m.name === name);
|
|
@@ -1606,20 +1986,20 @@ var useCommand = new Command18("use").description("Select an MCP to use for subs
|
|
|
1606
1986
|
});
|
|
1607
1987
|
|
|
1608
1988
|
// src/commands/mcp/index.ts
|
|
1609
|
-
var mcpCommand = new Command19("mcp").description("MCP sandbox management commands").addCommand(
|
|
1989
|
+
var mcpCommand = new Command19("mcp").description("MCP sandbox management commands").addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(startCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(deleteCommand).addCommand(testCommand).addCommand(deployCommand).addCommand(fileCommand).addCommand(runCommandCommand);
|
|
1610
1990
|
|
|
1611
1991
|
// src/commands/org/index.ts
|
|
1612
1992
|
import { Command as Command22 } from "commander";
|
|
1613
1993
|
|
|
1614
1994
|
// src/commands/org/list.ts
|
|
1615
|
-
import
|
|
1995
|
+
import chalk12 from "chalk";
|
|
1616
1996
|
import { Command as Command20 } from "commander";
|
|
1617
|
-
import
|
|
1997
|
+
import ora17 from "ora";
|
|
1618
1998
|
var listCommand3 = new Command20("list").description("List your organizations").action(async (_, command) => {
|
|
1619
1999
|
const globalOptions = command.optsWithGlobals();
|
|
1620
2000
|
const json = globalOptions.json ?? false;
|
|
1621
2001
|
try {
|
|
1622
|
-
const spinner =
|
|
2002
|
+
const spinner = ora17("Fetching organizations...").start();
|
|
1623
2003
|
const result = await api.get("/api/oauth/orgs");
|
|
1624
2004
|
spinner.stop();
|
|
1625
2005
|
const { orgs, activeOrgId } = result;
|
|
@@ -1639,11 +2019,11 @@ var listCommand3 = new Command20("list").description("List your organizations").
|
|
|
1639
2019
|
console.log("No organizations found.");
|
|
1640
2020
|
return;
|
|
1641
2021
|
}
|
|
1642
|
-
console.log(
|
|
2022
|
+
console.log(chalk12.bold("\nOrganizations:\n"));
|
|
1643
2023
|
const rows = orgs.map((o) => {
|
|
1644
2024
|
const isActive = o.id === activeOrgId;
|
|
1645
2025
|
return [
|
|
1646
|
-
isActive ?
|
|
2026
|
+
isActive ? chalk12.cyan(`* ${o.name}`) : ` ${o.name}`,
|
|
1647
2027
|
o.slug,
|
|
1648
2028
|
o.role
|
|
1649
2029
|
];
|
|
@@ -1653,7 +2033,7 @@ var listCommand3 = new Command20("list").description("List your organizations").
|
|
|
1653
2033
|
if (activeOrgId) {
|
|
1654
2034
|
const activeOrg = orgs.find((o) => o.id === activeOrgId);
|
|
1655
2035
|
if (activeOrg) {
|
|
1656
|
-
console.log(`Active organization: ${
|
|
2036
|
+
console.log(`Active organization: ${chalk12.cyan(activeOrg.name)}`);
|
|
1657
2037
|
}
|
|
1658
2038
|
}
|
|
1659
2039
|
console.log("\nSwitch organization: waniwani org switch <name>");
|
|
@@ -1666,12 +2046,12 @@ var listCommand3 = new Command20("list").description("List your organizations").
|
|
|
1666
2046
|
|
|
1667
2047
|
// src/commands/org/switch.ts
|
|
1668
2048
|
import { Command as Command21 } from "commander";
|
|
1669
|
-
import
|
|
2049
|
+
import ora18 from "ora";
|
|
1670
2050
|
var switchCommand = new Command21("switch").description("Switch to a different organization").argument("<name>", "Name or slug of the organization to switch to").action(async (name, _, command) => {
|
|
1671
2051
|
const globalOptions = command.optsWithGlobals();
|
|
1672
2052
|
const json = globalOptions.json ?? false;
|
|
1673
2053
|
try {
|
|
1674
|
-
const spinner =
|
|
2054
|
+
const spinner = ora18("Fetching organizations...").start();
|
|
1675
2055
|
const { orgs } = await api.get("/api/oauth/orgs");
|
|
1676
2056
|
const org = orgs.find((o) => o.name === name || o.slug === name);
|
|
1677
2057
|
if (!org) {
|
|
@@ -1706,11 +2086,92 @@ var switchCommand = new Command21("switch").description("Switch to a different o
|
|
|
1706
2086
|
// src/commands/org/index.ts
|
|
1707
2087
|
var orgCommand = new Command22("org").description("Organization management commands").addCommand(listCommand3).addCommand(switchCommand);
|
|
1708
2088
|
|
|
1709
|
-
// src/commands/
|
|
1710
|
-
import
|
|
2089
|
+
// src/commands/push.ts
|
|
2090
|
+
import chalk13 from "chalk";
|
|
1711
2091
|
import { Command as Command23 } from "commander";
|
|
1712
|
-
import
|
|
1713
|
-
var
|
|
2092
|
+
import ora19 from "ora";
|
|
2093
|
+
var BATCH_SIZE2 = 50;
|
|
2094
|
+
var pushCommand = new Command23("push").description("Sync local files to MCP sandbox").option("--dry-run", "Show what would be synced without uploading").action(async (options, command) => {
|
|
2095
|
+
const globalOptions = command.optsWithGlobals();
|
|
2096
|
+
const json = globalOptions.json ?? false;
|
|
2097
|
+
try {
|
|
2098
|
+
const cwd = process.cwd();
|
|
2099
|
+
const projectRoot = await findProjectRoot(cwd);
|
|
2100
|
+
if (!projectRoot) {
|
|
2101
|
+
throw new CLIError(
|
|
2102
|
+
"Not in a WaniWani project. Run 'waniwani init <name>' first.",
|
|
2103
|
+
"NOT_IN_PROJECT"
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
const mcpId = await loadProjectMcpId(projectRoot);
|
|
2107
|
+
if (!mcpId) {
|
|
2108
|
+
throw new CLIError(
|
|
2109
|
+
"No MCP ID found in project config. Run 'waniwani init <name>' first.",
|
|
2110
|
+
"NO_MCP_ID"
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
const spinner = ora19("Collecting files...").start();
|
|
2114
|
+
const files = await collectFiles(projectRoot);
|
|
2115
|
+
if (files.length === 0) {
|
|
2116
|
+
spinner.info("No files to sync");
|
|
2117
|
+
if (json) {
|
|
2118
|
+
formatOutput({ synced: 0, files: [] }, true);
|
|
2119
|
+
}
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
spinner.text = `Found ${files.length} files`;
|
|
2123
|
+
if (options.dryRun) {
|
|
2124
|
+
spinner.stop();
|
|
2125
|
+
console.log();
|
|
2126
|
+
console.log(chalk13.bold("Files that would be synced:"));
|
|
2127
|
+
for (const file of files) {
|
|
2128
|
+
console.log(` ${file.path}`);
|
|
2129
|
+
}
|
|
2130
|
+
console.log();
|
|
2131
|
+
console.log(`Total: ${files.length} files`);
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
const allWritten = [];
|
|
2135
|
+
const totalBatches = Math.ceil(files.length / BATCH_SIZE2);
|
|
2136
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
2137
|
+
const batch = files.slice(i * BATCH_SIZE2, (i + 1) * BATCH_SIZE2);
|
|
2138
|
+
spinner.text = `Syncing files (${i + 1}/${totalBatches})...`;
|
|
2139
|
+
const result = await api.post(
|
|
2140
|
+
`/api/mcp/sandboxes/${mcpId}/files`,
|
|
2141
|
+
{
|
|
2142
|
+
files: batch.map((f) => ({
|
|
2143
|
+
path: f.path,
|
|
2144
|
+
content: f.content,
|
|
2145
|
+
encoding: f.encoding
|
|
2146
|
+
}))
|
|
2147
|
+
}
|
|
2148
|
+
);
|
|
2149
|
+
allWritten.push(...result.written);
|
|
2150
|
+
}
|
|
2151
|
+
spinner.succeed(`Synced ${allWritten.length} files`);
|
|
2152
|
+
if (json) {
|
|
2153
|
+
formatOutput(
|
|
2154
|
+
{
|
|
2155
|
+
synced: allWritten.length,
|
|
2156
|
+
files: allWritten
|
|
2157
|
+
},
|
|
2158
|
+
true
|
|
2159
|
+
);
|
|
2160
|
+
} else {
|
|
2161
|
+
console.log();
|
|
2162
|
+
formatSuccess(`${allWritten.length} files synced to sandbox`, false);
|
|
2163
|
+
}
|
|
2164
|
+
} catch (error) {
|
|
2165
|
+
handleError(error, json);
|
|
2166
|
+
process.exit(1);
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
|
|
2170
|
+
// src/commands/task.ts
|
|
2171
|
+
import chalk14 from "chalk";
|
|
2172
|
+
import { Command as Command24 } from "commander";
|
|
2173
|
+
import ora20 from "ora";
|
|
2174
|
+
var taskCommand = new Command24("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) => {
|
|
1714
2175
|
const globalOptions = command.optsWithGlobals();
|
|
1715
2176
|
const json = globalOptions.json ?? false;
|
|
1716
2177
|
try {
|
|
@@ -1734,10 +2195,10 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
|
|
|
1734
2195
|
const maxSteps = options.maxSteps ? Number.parseInt(options.maxSteps, 10) : defaults.maxSteps;
|
|
1735
2196
|
if (!json) {
|
|
1736
2197
|
console.log();
|
|
1737
|
-
console.log(
|
|
2198
|
+
console.log(chalk14.bold("Task:"), prompt);
|
|
1738
2199
|
console.log();
|
|
1739
2200
|
}
|
|
1740
|
-
const spinner =
|
|
2201
|
+
const spinner = ora20("Starting task...").start();
|
|
1741
2202
|
const baseUrl = await api.getBaseUrl();
|
|
1742
2203
|
const response = await fetch(
|
|
1743
2204
|
`${baseUrl}/api/mcp/sandboxes/${mcpId}/task`,
|
|
@@ -1791,7 +2252,7 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
|
|
|
1791
2252
|
const event = parsed;
|
|
1792
2253
|
steps.push({ type: "text", text: event.content });
|
|
1793
2254
|
if (!json && event.content) {
|
|
1794
|
-
console.log(
|
|
2255
|
+
console.log(chalk14.white(event.content));
|
|
1795
2256
|
}
|
|
1796
2257
|
} else if (parsed.type === "tool_call") {
|
|
1797
2258
|
const event = parsed;
|
|
@@ -1802,24 +2263,24 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
|
|
|
1802
2263
|
output: event.output
|
|
1803
2264
|
});
|
|
1804
2265
|
if (!json) {
|
|
1805
|
-
console.log(
|
|
2266
|
+
console.log(chalk14.cyan(`> Using tool: ${event.tool}`));
|
|
1806
2267
|
if (event.input?.command) {
|
|
1807
|
-
console.log(
|
|
2268
|
+
console.log(chalk14.gray(` $ ${event.input.command}`));
|
|
1808
2269
|
}
|
|
1809
2270
|
if (event.output) {
|
|
1810
2271
|
const outputLines = event.output.split("\n");
|
|
1811
2272
|
if (outputLines.length > 10) {
|
|
1812
2273
|
console.log(
|
|
1813
|
-
|
|
2274
|
+
chalk14.gray(outputLines.slice(0, 5).join("\n"))
|
|
1814
2275
|
);
|
|
1815
2276
|
console.log(
|
|
1816
|
-
|
|
2277
|
+
chalk14.gray(
|
|
1817
2278
|
` ... (${outputLines.length - 10} more lines)`
|
|
1818
2279
|
)
|
|
1819
2280
|
);
|
|
1820
|
-
console.log(
|
|
2281
|
+
console.log(chalk14.gray(outputLines.slice(-5).join("\n")));
|
|
1821
2282
|
} else {
|
|
1822
|
-
console.log(
|
|
2283
|
+
console.log(chalk14.gray(event.output));
|
|
1823
2284
|
}
|
|
1824
2285
|
}
|
|
1825
2286
|
console.log();
|
|
@@ -1846,12 +2307,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
|
|
|
1846
2307
|
} else {
|
|
1847
2308
|
console.log();
|
|
1848
2309
|
console.log(
|
|
1849
|
-
|
|
2310
|
+
chalk14.green("\u2713"),
|
|
1850
2311
|
`Task completed in ${finalStepCount} steps.`
|
|
1851
2312
|
);
|
|
1852
2313
|
if (maxStepsReached) {
|
|
1853
2314
|
console.log(
|
|
1854
|
-
|
|
2315
|
+
chalk14.yellow("\u26A0"),
|
|
1855
2316
|
"Maximum steps reached. Task may be incomplete."
|
|
1856
2317
|
);
|
|
1857
2318
|
}
|
|
@@ -1864,10 +2325,12 @@ var taskCommand = new Command23("task").description("Send a task to Claude runni
|
|
|
1864
2325
|
|
|
1865
2326
|
// src/cli.ts
|
|
1866
2327
|
var version = "0.1.0";
|
|
1867
|
-
var program = new
|
|
2328
|
+
var program = new Command25().name("waniwani").description("WaniWani CLI for MCP development workflow").version(version).option("--json", "Output results as JSON").option("--verbose", "Enable verbose logging");
|
|
1868
2329
|
program.addCommand(loginCommand);
|
|
1869
2330
|
program.addCommand(logoutCommand);
|
|
1870
2331
|
program.addCommand(initCommand);
|
|
2332
|
+
program.addCommand(pushCommand);
|
|
2333
|
+
program.addCommand(devCommand);
|
|
1871
2334
|
program.addCommand(mcpCommand);
|
|
1872
2335
|
program.addCommand(taskCommand);
|
|
1873
2336
|
program.addCommand(orgCommand);
|