@waniwani/cli 0.0.34 → 0.0.36
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 +151 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,33 +14,31 @@ import { Command } from "commander";
|
|
|
14
14
|
// src/lib/config.ts
|
|
15
15
|
import { existsSync } from "fs";
|
|
16
16
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
17
|
-
import { homedir } from "os";
|
|
18
17
|
import { join } from "path";
|
|
19
18
|
import { z } from "zod";
|
|
20
19
|
var LOCAL_CONFIG_DIR = ".waniwani";
|
|
21
20
|
var CONFIG_FILE_NAME = "settings.json";
|
|
22
21
|
var LOCAL_DIR = join(process.cwd(), LOCAL_CONFIG_DIR);
|
|
23
22
|
var LOCAL_FILE = join(LOCAL_DIR, CONFIG_FILE_NAME);
|
|
24
|
-
var GLOBAL_DIR = join(homedir(), LOCAL_CONFIG_DIR);
|
|
25
|
-
var GLOBAL_FILE = join(GLOBAL_DIR, CONFIG_FILE_NAME);
|
|
26
23
|
var DEFAULT_API_URL = "https://app.waniwani.ai";
|
|
27
24
|
var ConfigSchema = z.object({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
// Settings
|
|
26
|
+
sessionId: z.string().nullable().default(null),
|
|
27
|
+
mcpId: z.string().nullable().default(null),
|
|
28
|
+
apiUrl: z.string().default(DEFAULT_API_URL),
|
|
29
|
+
// Auth (merged from auth.json)
|
|
30
|
+
accessToken: z.string().nullable().default(null),
|
|
31
|
+
refreshToken: z.string().nullable().default(null),
|
|
32
|
+
expiresAt: z.string().nullable().default(null),
|
|
33
|
+
clientId: z.string().nullable().default(null)
|
|
33
34
|
});
|
|
34
35
|
var Config = class {
|
|
35
36
|
dir;
|
|
36
37
|
file;
|
|
37
38
|
cache = null;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.dir = useLocal ? LOCAL_DIR : GLOBAL_DIR;
|
|
42
|
-
this.file = useLocal ? LOCAL_FILE : GLOBAL_FILE;
|
|
43
|
-
this.scope = useLocal ? "local" : "global";
|
|
39
|
+
constructor() {
|
|
40
|
+
this.dir = LOCAL_DIR;
|
|
41
|
+
this.file = LOCAL_FILE;
|
|
44
42
|
}
|
|
45
43
|
async load() {
|
|
46
44
|
if (!this.cache) {
|
|
@@ -59,12 +57,34 @@ var Config = class {
|
|
|
59
57
|
await mkdir(this.dir, { recursive: true });
|
|
60
58
|
await writeFile(this.file, JSON.stringify(data, null, " "));
|
|
61
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Ensure the .waniwani directory exists in cwd.
|
|
62
|
+
* Used by login to create config before saving tokens.
|
|
63
|
+
*/
|
|
64
|
+
async ensureConfigDir() {
|
|
65
|
+
await mkdir(this.dir, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if a .waniwani config directory exists in cwd.
|
|
69
|
+
*/
|
|
70
|
+
hasConfig() {
|
|
71
|
+
return existsSync(this.dir);
|
|
72
|
+
}
|
|
73
|
+
// --- Settings methods ---
|
|
62
74
|
async getMcpId() {
|
|
63
|
-
return (await this.load()).
|
|
75
|
+
return (await this.load()).mcpId;
|
|
64
76
|
}
|
|
65
77
|
async setMcpId(id) {
|
|
66
78
|
const data = await this.load();
|
|
67
|
-
data.
|
|
79
|
+
data.mcpId = id;
|
|
80
|
+
await this.save(data);
|
|
81
|
+
}
|
|
82
|
+
async getSessionId() {
|
|
83
|
+
return (await this.load()).sessionId;
|
|
84
|
+
}
|
|
85
|
+
async setSessionId(id) {
|
|
86
|
+
const data = await this.load();
|
|
87
|
+
data.sessionId = id;
|
|
68
88
|
await this.save(data);
|
|
69
89
|
}
|
|
70
90
|
async getApiUrl() {
|
|
@@ -74,9 +94,42 @@ var Config = class {
|
|
|
74
94
|
async clear() {
|
|
75
95
|
await this.save(ConfigSchema.parse({}));
|
|
76
96
|
}
|
|
97
|
+
// --- Auth methods ---
|
|
98
|
+
async getAccessToken() {
|
|
99
|
+
return (await this.load()).accessToken;
|
|
100
|
+
}
|
|
101
|
+
async getRefreshToken() {
|
|
102
|
+
return (await this.load()).refreshToken;
|
|
103
|
+
}
|
|
104
|
+
async getClientId() {
|
|
105
|
+
return (await this.load()).clientId;
|
|
106
|
+
}
|
|
107
|
+
async setTokens(accessToken, refreshToken, expiresIn, clientId) {
|
|
108
|
+
const expiresAt = new Date(Date.now() + expiresIn * 1e3).toISOString();
|
|
109
|
+
const data = await this.load();
|
|
110
|
+
data.accessToken = accessToken;
|
|
111
|
+
data.refreshToken = refreshToken;
|
|
112
|
+
data.expiresAt = expiresAt;
|
|
113
|
+
if (clientId) {
|
|
114
|
+
data.clientId = clientId;
|
|
115
|
+
}
|
|
116
|
+
await this.save(data);
|
|
117
|
+
}
|
|
118
|
+
async clearAuth() {
|
|
119
|
+
const data = await this.load();
|
|
120
|
+
data.accessToken = null;
|
|
121
|
+
data.refreshToken = null;
|
|
122
|
+
data.expiresAt = null;
|
|
123
|
+
data.clientId = null;
|
|
124
|
+
await this.save(data);
|
|
125
|
+
}
|
|
126
|
+
async isTokenExpired() {
|
|
127
|
+
const data = await this.load();
|
|
128
|
+
if (!data.expiresAt) return true;
|
|
129
|
+
return new Date(data.expiresAt).getTime() - 5 * 60 * 1e3 < Date.now();
|
|
130
|
+
}
|
|
77
131
|
};
|
|
78
132
|
var config = new Config();
|
|
79
|
-
var globalConfig = new Config(true);
|
|
80
133
|
async function initConfigAt(dir, overrides = {}) {
|
|
81
134
|
const configDir = join(dir, LOCAL_CONFIG_DIR);
|
|
82
135
|
const configPath = join(configDir, CONFIG_FILE_NAME);
|
|
@@ -241,82 +294,29 @@ import { Command as Command3 } from "commander";
|
|
|
241
294
|
import ora from "ora";
|
|
242
295
|
|
|
243
296
|
// src/lib/auth.ts
|
|
244
|
-
import { access, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
245
|
-
import { homedir as homedir2 } from "os";
|
|
246
|
-
import { join as join3 } from "path";
|
|
247
|
-
import { z as z2 } from "zod";
|
|
248
|
-
var CONFIG_DIR = join3(homedir2(), ".waniwani");
|
|
249
|
-
var AUTH_FILE = join3(CONFIG_DIR, "auth.json");
|
|
250
|
-
var AuthStoreSchema = z2.object({
|
|
251
|
-
accessToken: z2.string().nullable().default(null),
|
|
252
|
-
refreshToken: z2.string().nullable().default(null),
|
|
253
|
-
expiresAt: z2.string().nullable().default(null),
|
|
254
|
-
clientId: z2.string().nullable().default(null)
|
|
255
|
-
});
|
|
256
|
-
async function ensureConfigDir() {
|
|
257
|
-
await mkdir2(CONFIG_DIR, { recursive: true });
|
|
258
|
-
}
|
|
259
|
-
async function readAuthStore() {
|
|
260
|
-
await ensureConfigDir();
|
|
261
|
-
try {
|
|
262
|
-
await access(AUTH_FILE);
|
|
263
|
-
const content = await readFile2(AUTH_FILE, "utf-8");
|
|
264
|
-
return AuthStoreSchema.parse(JSON.parse(content));
|
|
265
|
-
} catch {
|
|
266
|
-
return AuthStoreSchema.parse({});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
async function writeAuthStore(store) {
|
|
270
|
-
await ensureConfigDir();
|
|
271
|
-
await writeFile2(AUTH_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
272
|
-
}
|
|
273
297
|
var AuthManager = class {
|
|
274
|
-
storeCache = null;
|
|
275
|
-
async getStore() {
|
|
276
|
-
if (!this.storeCache) {
|
|
277
|
-
this.storeCache = await readAuthStore();
|
|
278
|
-
}
|
|
279
|
-
return this.storeCache;
|
|
280
|
-
}
|
|
281
|
-
async saveStore(store) {
|
|
282
|
-
this.storeCache = store;
|
|
283
|
-
await writeAuthStore(store);
|
|
284
|
-
}
|
|
285
298
|
async isLoggedIn() {
|
|
286
|
-
const
|
|
287
|
-
return !!
|
|
299
|
+
const token = await config.getAccessToken();
|
|
300
|
+
return !!token;
|
|
288
301
|
}
|
|
289
302
|
async getAccessToken() {
|
|
290
|
-
|
|
291
|
-
return store.accessToken;
|
|
303
|
+
return config.getAccessToken();
|
|
292
304
|
}
|
|
293
305
|
async getRefreshToken() {
|
|
294
|
-
|
|
295
|
-
return store.refreshToken;
|
|
306
|
+
return config.getRefreshToken();
|
|
296
307
|
}
|
|
297
308
|
async setTokens(accessToken, refreshToken, expiresIn, clientId) {
|
|
298
|
-
|
|
299
|
-
const store = await this.getStore();
|
|
300
|
-
store.accessToken = accessToken;
|
|
301
|
-
store.refreshToken = refreshToken;
|
|
302
|
-
store.expiresAt = expiresAt;
|
|
303
|
-
if (clientId) {
|
|
304
|
-
store.clientId = clientId;
|
|
305
|
-
}
|
|
306
|
-
await this.saveStore(store);
|
|
309
|
+
return config.setTokens(accessToken, refreshToken, expiresIn, clientId);
|
|
307
310
|
}
|
|
308
311
|
async clear() {
|
|
309
|
-
|
|
310
|
-
await this.saveStore(emptyStore);
|
|
312
|
+
return config.clearAuth();
|
|
311
313
|
}
|
|
312
314
|
async isTokenExpired() {
|
|
313
|
-
|
|
314
|
-
if (!store.expiresAt) return true;
|
|
315
|
-
return new Date(store.expiresAt).getTime() - 5 * 60 * 1e3 < Date.now();
|
|
315
|
+
return config.isTokenExpired();
|
|
316
316
|
}
|
|
317
317
|
async tryRefreshToken() {
|
|
318
|
-
const
|
|
319
|
-
const
|
|
318
|
+
const refreshToken = await config.getRefreshToken();
|
|
319
|
+
const clientId = await config.getClientId();
|
|
320
320
|
if (!refreshToken || !clientId) return false;
|
|
321
321
|
try {
|
|
322
322
|
const apiUrl = await config.getApiUrl();
|
|
@@ -468,7 +468,10 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
468
468
|
text-align: center;
|
|
469
469
|
}
|
|
470
470
|
.logo {
|
|
471
|
-
|
|
471
|
+
font-size: 1.75rem;
|
|
472
|
+
font-weight: 700;
|
|
473
|
+
color: #1e293b;
|
|
474
|
+
letter-spacing: -0.025em;
|
|
472
475
|
}
|
|
473
476
|
.icon-circle {
|
|
474
477
|
width: 80px;
|
|
@@ -501,9 +504,7 @@ async function waitForCallback(expectedState, timeoutMs = 3e5) {
|
|
|
501
504
|
<div class="blob blob-2"></div>
|
|
502
505
|
<div class="blob blob-3"></div>
|
|
503
506
|
<div class="container">
|
|
504
|
-
<
|
|
505
|
-
<text x="0" y="32" font-family="system-ui" font-size="28" font-weight="bold" fill="#1e293b">WaniWani</text>
|
|
506
|
-
</svg>
|
|
507
|
+
<span class="logo">WaniWani</span>
|
|
507
508
|
<div class="icon-circle">
|
|
508
509
|
${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>'}
|
|
509
510
|
</div>
|
|
@@ -699,6 +700,7 @@ var loginCommand = new Command3("login").description("Log in to WaniWani").optio
|
|
|
699
700
|
apiUrl
|
|
700
701
|
// RFC 8707 resource parameter
|
|
701
702
|
);
|
|
703
|
+
await config.ensureConfigDir();
|
|
702
704
|
await auth.setTokens(
|
|
703
705
|
tokenResponse.access_token,
|
|
704
706
|
tokenResponse.refresh_token,
|
|
@@ -861,16 +863,14 @@ async function requireMcpId(mcpId) {
|
|
|
861
863
|
}
|
|
862
864
|
return configMcpId;
|
|
863
865
|
}
|
|
864
|
-
async function requireSessionId(
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
);
|
|
868
|
-
if (!repository.activeSandbox) {
|
|
866
|
+
async function requireSessionId() {
|
|
867
|
+
const sessionId = await config.getSessionId();
|
|
868
|
+
if (!sessionId) {
|
|
869
869
|
throw new McpError(
|
|
870
870
|
"No active session. Run 'waniwani mcp dev' to start development."
|
|
871
871
|
);
|
|
872
872
|
}
|
|
873
|
-
return
|
|
873
|
+
return sessionId;
|
|
874
874
|
}
|
|
875
875
|
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
876
876
|
".png",
|
|
@@ -940,6 +940,7 @@ var deleteCommand = new Command5("delete").description("Delete the MCP (includes
|
|
|
940
940
|
spinner.succeed("MCP deleted");
|
|
941
941
|
if (await config.getMcpId() === mcpId) {
|
|
942
942
|
await config.setMcpId(null);
|
|
943
|
+
await config.setSessionId(null);
|
|
943
944
|
}
|
|
944
945
|
if (json) {
|
|
945
946
|
formatOutput({ deleted: mcpId }, true);
|
|
@@ -959,37 +960,26 @@ import ora3 from "ora";
|
|
|
959
960
|
|
|
960
961
|
// src/lib/sync.ts
|
|
961
962
|
import { existsSync as existsSync3 } from "fs";
|
|
962
|
-
import { mkdir as
|
|
963
|
-
import { dirname, join as
|
|
963
|
+
import { mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
|
|
964
|
+
import { dirname, join as join3, relative } from "path";
|
|
964
965
|
import ignore from "ignore";
|
|
965
966
|
var PROJECT_DIR = ".waniwani";
|
|
966
|
-
var SETTINGS_FILE = "settings.json";
|
|
967
967
|
async function findProjectRoot(startDir) {
|
|
968
968
|
let current = startDir;
|
|
969
969
|
const root = dirname(current);
|
|
970
970
|
while (current !== root) {
|
|
971
|
-
if (existsSync3(
|
|
971
|
+
if (existsSync3(join3(current, PROJECT_DIR))) {
|
|
972
972
|
return current;
|
|
973
973
|
}
|
|
974
974
|
const parent = dirname(current);
|
|
975
975
|
if (parent === current) break;
|
|
976
976
|
current = parent;
|
|
977
977
|
}
|
|
978
|
-
if (existsSync3(
|
|
978
|
+
if (existsSync3(join3(current, PROJECT_DIR))) {
|
|
979
979
|
return current;
|
|
980
980
|
}
|
|
981
981
|
return null;
|
|
982
982
|
}
|
|
983
|
-
async function loadProjectMcpId(projectRoot) {
|
|
984
|
-
const settingsPath = join4(projectRoot, PROJECT_DIR, SETTINGS_FILE);
|
|
985
|
-
try {
|
|
986
|
-
const content = await readFile3(settingsPath, "utf-8");
|
|
987
|
-
const settings = JSON.parse(content);
|
|
988
|
-
return settings.mcp?.id ?? null;
|
|
989
|
-
} catch {
|
|
990
|
-
return null;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
983
|
var DEFAULT_IGNORE_PATTERNS = [
|
|
994
984
|
".waniwani",
|
|
995
985
|
".git",
|
|
@@ -1009,10 +999,10 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
1009
999
|
async function loadIgnorePatterns(projectRoot) {
|
|
1010
1000
|
const ig = ignore();
|
|
1011
1001
|
ig.add(DEFAULT_IGNORE_PATTERNS);
|
|
1012
|
-
const gitignorePath =
|
|
1002
|
+
const gitignorePath = join3(projectRoot, ".gitignore");
|
|
1013
1003
|
if (existsSync3(gitignorePath)) {
|
|
1014
1004
|
try {
|
|
1015
|
-
const content = await
|
|
1005
|
+
const content = await readFile2(gitignorePath, "utf-8");
|
|
1016
1006
|
ig.add(content);
|
|
1017
1007
|
} catch {
|
|
1018
1008
|
}
|
|
@@ -1025,7 +1015,7 @@ async function collectFiles(projectRoot) {
|
|
|
1025
1015
|
async function walk(dir) {
|
|
1026
1016
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1027
1017
|
for (const entry of entries) {
|
|
1028
|
-
const fullPath =
|
|
1018
|
+
const fullPath = join3(dir, entry.name);
|
|
1029
1019
|
const relativePath = relative(projectRoot, fullPath);
|
|
1030
1020
|
if (ig.ignores(relativePath)) {
|
|
1031
1021
|
continue;
|
|
@@ -1034,7 +1024,7 @@ async function collectFiles(projectRoot) {
|
|
|
1034
1024
|
await walk(fullPath);
|
|
1035
1025
|
} else if (entry.isFile()) {
|
|
1036
1026
|
try {
|
|
1037
|
-
const content = await
|
|
1027
|
+
const content = await readFile2(fullPath);
|
|
1038
1028
|
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
1039
1029
|
files.push({
|
|
1040
1030
|
path: relativePath,
|
|
@@ -1055,20 +1045,20 @@ async function pullFilesFromGithub(mcpId, targetDir) {
|
|
|
1055
1045
|
);
|
|
1056
1046
|
const writtenFiles = [];
|
|
1057
1047
|
for (const file of result.files) {
|
|
1058
|
-
const localPath =
|
|
1048
|
+
const localPath = join3(targetDir, file.path);
|
|
1059
1049
|
const dir = dirname(localPath);
|
|
1060
|
-
await
|
|
1050
|
+
await mkdir2(dir, { recursive: true });
|
|
1061
1051
|
if (file.encoding === "base64") {
|
|
1062
|
-
await
|
|
1052
|
+
await writeFile2(localPath, Buffer.from(file.content, "base64"));
|
|
1063
1053
|
} else {
|
|
1064
|
-
await
|
|
1054
|
+
await writeFile2(localPath, file.content, "utf8");
|
|
1065
1055
|
}
|
|
1066
1056
|
writtenFiles.push(file.path);
|
|
1067
1057
|
}
|
|
1068
1058
|
return { count: writtenFiles.length, files: writtenFiles };
|
|
1069
1059
|
}
|
|
1070
1060
|
async function collectSingleFile(projectRoot, filePath) {
|
|
1071
|
-
const fullPath =
|
|
1061
|
+
const fullPath = join3(projectRoot, filePath);
|
|
1072
1062
|
const relativePath = relative(projectRoot, fullPath);
|
|
1073
1063
|
if (!existsSync3(fullPath)) {
|
|
1074
1064
|
return null;
|
|
@@ -1078,7 +1068,7 @@ async function collectSingleFile(projectRoot, filePath) {
|
|
|
1078
1068
|
if (!fileStat.isFile()) {
|
|
1079
1069
|
return null;
|
|
1080
1070
|
}
|
|
1081
|
-
const content = await
|
|
1071
|
+
const content = await readFile2(fullPath);
|
|
1082
1072
|
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
1083
1073
|
return {
|
|
1084
1074
|
path: relativePath,
|
|
@@ -1152,9 +1142,6 @@ var devCommand = new Command7("dev").description("Start live development with sa
|
|
|
1152
1142
|
);
|
|
1153
1143
|
}
|
|
1154
1144
|
let mcpId = options.mcpId;
|
|
1155
|
-
if (!mcpId) {
|
|
1156
|
-
mcpId = await loadProjectMcpId(projectRoot);
|
|
1157
|
-
}
|
|
1158
1145
|
if (!mcpId) {
|
|
1159
1146
|
mcpId = await config.getMcpId();
|
|
1160
1147
|
}
|
|
@@ -1182,6 +1169,7 @@ var devCommand = new Command7("dev").description("Start live development with sa
|
|
|
1182
1169
|
}
|
|
1183
1170
|
sessionId = existing.id;
|
|
1184
1171
|
}
|
|
1172
|
+
await config.setSessionId(sessionId);
|
|
1185
1173
|
spinner.text = "Syncing files to sandbox...";
|
|
1186
1174
|
const files = await collectFiles(projectRoot);
|
|
1187
1175
|
if (files.length > 0) {
|
|
@@ -1270,8 +1258,8 @@ var listCommand = new Command8("list").description("List files in the MCP sandbo
|
|
|
1270
1258
|
const globalOptions = command.optsWithGlobals();
|
|
1271
1259
|
const json = globalOptions.json ?? false;
|
|
1272
1260
|
try {
|
|
1273
|
-
|
|
1274
|
-
const sessionId = await requireSessionId(
|
|
1261
|
+
await requireMcpId(options.mcpId);
|
|
1262
|
+
const sessionId = await requireSessionId();
|
|
1275
1263
|
const spinner = ora5(`Listing ${path}...`).start();
|
|
1276
1264
|
const result = await api.get(
|
|
1277
1265
|
`/api/mcp/sessions/${sessionId}/files/list?path=${encodeURIComponent(path)}`
|
|
@@ -1308,15 +1296,15 @@ function formatSize(bytes) {
|
|
|
1308
1296
|
}
|
|
1309
1297
|
|
|
1310
1298
|
// src/commands/mcp/file/read.ts
|
|
1311
|
-
import { writeFile as
|
|
1299
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
1312
1300
|
import { Command as Command9 } from "commander";
|
|
1313
1301
|
import ora6 from "ora";
|
|
1314
1302
|
var readCommand = new Command9("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) => {
|
|
1315
1303
|
const globalOptions = command.optsWithGlobals();
|
|
1316
1304
|
const json = globalOptions.json ?? false;
|
|
1317
1305
|
try {
|
|
1318
|
-
|
|
1319
|
-
const sessionId = await requireSessionId(
|
|
1306
|
+
await requireMcpId(options.mcpId);
|
|
1307
|
+
const sessionId = await requireSessionId();
|
|
1320
1308
|
const encoding = options.base64 ? "base64" : "utf8";
|
|
1321
1309
|
const spinner = ora6(`Reading ${path}...`).start();
|
|
1322
1310
|
const result = await api.get(
|
|
@@ -1328,7 +1316,7 @@ var readCommand = new Command9("read").description("Read a file from the MCP san
|
|
|
1328
1316
|
}
|
|
1329
1317
|
if (options.output) {
|
|
1330
1318
|
const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
|
|
1331
|
-
await
|
|
1319
|
+
await writeFile3(options.output, buffer);
|
|
1332
1320
|
if (json) {
|
|
1333
1321
|
formatOutput({ path, savedTo: options.output }, true);
|
|
1334
1322
|
} else {
|
|
@@ -1349,15 +1337,15 @@ var readCommand = new Command9("read").description("Read a file from the MCP san
|
|
|
1349
1337
|
});
|
|
1350
1338
|
|
|
1351
1339
|
// src/commands/mcp/file/write.ts
|
|
1352
|
-
import { readFile as
|
|
1340
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1353
1341
|
import { Command as Command10 } from "commander";
|
|
1354
1342
|
import ora7 from "ora";
|
|
1355
1343
|
var writeCommand = new Command10("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) => {
|
|
1356
1344
|
const globalOptions = command.optsWithGlobals();
|
|
1357
1345
|
const json = globalOptions.json ?? false;
|
|
1358
1346
|
try {
|
|
1359
|
-
|
|
1360
|
-
const sessionId = await requireSessionId(
|
|
1347
|
+
await requireMcpId(options.mcpId);
|
|
1348
|
+
const sessionId = await requireSessionId();
|
|
1361
1349
|
let content;
|
|
1362
1350
|
let encoding = "utf8";
|
|
1363
1351
|
if (options.content) {
|
|
@@ -1366,7 +1354,7 @@ var writeCommand = new Command10("write").description("Write a file to the MCP s
|
|
|
1366
1354
|
encoding = "base64";
|
|
1367
1355
|
}
|
|
1368
1356
|
} else if (options.file) {
|
|
1369
|
-
const fileBuffer = await
|
|
1357
|
+
const fileBuffer = await readFile3(options.file);
|
|
1370
1358
|
if (options.base64) {
|
|
1371
1359
|
content = fileBuffer.toString("base64");
|
|
1372
1360
|
encoding = "base64";
|
|
@@ -1402,32 +1390,31 @@ var writeCommand = new Command10("write").description("Write a file to the MCP s
|
|
|
1402
1390
|
var fileCommand = new Command11("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
|
|
1403
1391
|
|
|
1404
1392
|
// src/commands/mcp/init.ts
|
|
1405
|
-
import { existsSync as existsSync4 } from "fs";
|
|
1406
|
-
import { readFile as
|
|
1407
|
-
import { join as
|
|
1393
|
+
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
1394
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1395
|
+
import { join as join4 } from "path";
|
|
1408
1396
|
import { Command as Command12 } from "commander";
|
|
1409
|
-
import { execa } from "execa";
|
|
1410
1397
|
import ora8 from "ora";
|
|
1411
1398
|
async function loadParentConfig(cwd) {
|
|
1412
|
-
const parentConfigPath =
|
|
1399
|
+
const parentConfigPath = join4(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
1413
1400
|
if (!existsSync4(parentConfigPath)) {
|
|
1414
1401
|
return null;
|
|
1415
1402
|
}
|
|
1416
1403
|
try {
|
|
1417
|
-
const content = await
|
|
1404
|
+
const content = await readFile4(parentConfigPath, "utf-8");
|
|
1418
1405
|
const config2 = JSON.parse(content);
|
|
1419
|
-
const {
|
|
1406
|
+
const { mcpId: _, sessionId: __, ...rest } = config2;
|
|
1420
1407
|
return rest;
|
|
1421
1408
|
} catch {
|
|
1422
1409
|
return null;
|
|
1423
1410
|
}
|
|
1424
1411
|
}
|
|
1425
|
-
var initCommand = new Command12("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").
|
|
1412
|
+
var initCommand = new Command12("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").action(async (name, _options, command) => {
|
|
1426
1413
|
const globalOptions = command.optsWithGlobals();
|
|
1427
1414
|
const json = globalOptions.json ?? false;
|
|
1428
1415
|
try {
|
|
1429
1416
|
const cwd = process.cwd();
|
|
1430
|
-
const projectDir =
|
|
1417
|
+
const projectDir = join4(cwd, name);
|
|
1431
1418
|
if (existsSync4(projectDir)) {
|
|
1432
1419
|
if (json) {
|
|
1433
1420
|
formatOutput(
|
|
@@ -1443,32 +1430,22 @@ var initCommand = new Command12("init").description("Create a new MCP project").
|
|
|
1443
1430
|
process.exit(1);
|
|
1444
1431
|
}
|
|
1445
1432
|
const spinner = ora8("Creating MCP...").start();
|
|
1446
|
-
const result = await api.post(
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
);
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
...parentConfig,
|
|
1457
|
-
mcp: {
|
|
1458
|
-
id: result.repository.id,
|
|
1459
|
-
name: result.repository.name
|
|
1460
|
-
}
|
|
1461
|
-
});
|
|
1462
|
-
spinner.succeed("MCP project created");
|
|
1463
|
-
} else {
|
|
1464
|
-
spinner.succeed("MCP created");
|
|
1465
|
-
}
|
|
1433
|
+
const result = await api.post("/api/mcp/repositories", {
|
|
1434
|
+
name
|
|
1435
|
+
});
|
|
1436
|
+
mkdirSync(projectDir, { recursive: true });
|
|
1437
|
+
const parentConfig = await loadParentConfig(cwd);
|
|
1438
|
+
await initConfigAt(projectDir, {
|
|
1439
|
+
...parentConfig,
|
|
1440
|
+
mcpId: result.id
|
|
1441
|
+
});
|
|
1442
|
+
spinner.succeed("MCP project created");
|
|
1466
1443
|
if (json) {
|
|
1467
1444
|
formatOutput(
|
|
1468
1445
|
{
|
|
1469
1446
|
success: true,
|
|
1470
|
-
projectDir
|
|
1471
|
-
mcpId: result.
|
|
1447
|
+
projectDir,
|
|
1448
|
+
mcpId: result.id
|
|
1472
1449
|
},
|
|
1473
1450
|
true
|
|
1474
1451
|
);
|
|
@@ -1476,24 +1453,10 @@ var initCommand = new Command12("init").description("Create a new MCP project").
|
|
|
1476
1453
|
console.log();
|
|
1477
1454
|
formatSuccess(`MCP "${name}" created!`, false);
|
|
1478
1455
|
console.log();
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
" waniwani mcp dev # Start live preview with file watching"
|
|
1484
|
-
);
|
|
1485
|
-
console.log(" waniwani mcp push # Deploy to production");
|
|
1486
|
-
} else {
|
|
1487
|
-
console.log("Clone your repository:");
|
|
1488
|
-
console.log(` ${result.cloneCommand}`);
|
|
1489
|
-
console.log(` cd ${name}`);
|
|
1490
|
-
console.log();
|
|
1491
|
-
console.log("Then start developing:");
|
|
1492
|
-
console.log(
|
|
1493
|
-
" waniwani mcp dev # Start live preview with file watching"
|
|
1494
|
-
);
|
|
1495
|
-
console.log(" waniwani mcp push # Deploy to production");
|
|
1496
|
-
}
|
|
1456
|
+
console.log("Next steps:");
|
|
1457
|
+
console.log(` cd ${name}`);
|
|
1458
|
+
console.log(" waniwani mcp sync # Pull template files");
|
|
1459
|
+
console.log(" waniwani mcp dev # Start developing");
|
|
1497
1460
|
}
|
|
1498
1461
|
} catch (error) {
|
|
1499
1462
|
handleError(error, json);
|
|
@@ -1579,8 +1542,8 @@ var logsCommand = new Command14("logs").description("Stream logs from the MCP se
|
|
|
1579
1542
|
process.on("SIGINT", cleanup);
|
|
1580
1543
|
process.on("SIGTERM", cleanup);
|
|
1581
1544
|
try {
|
|
1582
|
-
|
|
1583
|
-
const sessionId = await requireSessionId(
|
|
1545
|
+
await requireMcpId(options.mcpId);
|
|
1546
|
+
const sessionId = await requireSessionId();
|
|
1584
1547
|
const token = await auth.getAccessToken();
|
|
1585
1548
|
if (!token) {
|
|
1586
1549
|
throw new AuthError(
|
|
@@ -1715,8 +1678,8 @@ var runCommandCommand = new Command15("run-command").description("Run a command
|
|
|
1715
1678
|
const globalOptions = command.optsWithGlobals();
|
|
1716
1679
|
const json = globalOptions.json ?? false;
|
|
1717
1680
|
try {
|
|
1718
|
-
|
|
1719
|
-
const sessionId = await requireSessionId(
|
|
1681
|
+
await requireMcpId(options.mcpId);
|
|
1682
|
+
const sessionId = await requireSessionId();
|
|
1720
1683
|
const timeout = options.timeout ? Number.parseInt(options.timeout, 10) : void 0;
|
|
1721
1684
|
const spinner = ora11(`Running: ${cmd} ${args.join(" ")}`.trim()).start();
|
|
1722
1685
|
const result = await api.post(
|
|
@@ -1847,8 +1810,8 @@ var stopCommand = new Command17("stop").description("Stop the development enviro
|
|
|
1847
1810
|
const globalOptions = command.optsWithGlobals();
|
|
1848
1811
|
const json = globalOptions.json ?? false;
|
|
1849
1812
|
try {
|
|
1850
|
-
|
|
1851
|
-
const sessionId = await requireSessionId(
|
|
1813
|
+
await requireMcpId(options.mcpId);
|
|
1814
|
+
const sessionId = await requireSessionId();
|
|
1852
1815
|
const spinner = ora13("Stopping development environment...").start();
|
|
1853
1816
|
try {
|
|
1854
1817
|
await api.post(`/api/mcp/sessions/${sessionId}/server`, {
|
|
@@ -1857,6 +1820,7 @@ var stopCommand = new Command17("stop").description("Stop the development enviro
|
|
|
1857
1820
|
} catch {
|
|
1858
1821
|
}
|
|
1859
1822
|
await api.delete(`/api/mcp/sessions/${sessionId}`);
|
|
1823
|
+
await config.setSessionId(null);
|
|
1860
1824
|
spinner.succeed("Development environment stopped");
|
|
1861
1825
|
if (json) {
|
|
1862
1826
|
formatOutput({ stopped: true }, true);
|
|
@@ -1910,7 +1874,7 @@ var syncCommand = new Command18("sync").description("Pull files from GitHub to l
|
|
|
1910
1874
|
// src/commands/mcp/use.ts
|
|
1911
1875
|
import { Command as Command19 } from "commander";
|
|
1912
1876
|
import ora15 from "ora";
|
|
1913
|
-
var useCommand = new Command19("use").description("Select an MCP to use for subsequent commands").argument("<name>", "Name of the MCP to use").
|
|
1877
|
+
var useCommand = new Command19("use").description("Select an MCP to use for subsequent commands").argument("<name>", "Name of the MCP to use").action(async (name, _options, command) => {
|
|
1914
1878
|
const globalOptions = command.optsWithGlobals();
|
|
1915
1879
|
const json = globalOptions.json ?? false;
|
|
1916
1880
|
try {
|
|
@@ -1925,12 +1889,12 @@ var useCommand = new Command19("use").description("Select an MCP to use for subs
|
|
|
1925
1889
|
`MCP "${name}" not found. Run 'waniwani mcp list' to see available MCPs.`
|
|
1926
1890
|
);
|
|
1927
1891
|
}
|
|
1928
|
-
|
|
1929
|
-
await
|
|
1892
|
+
await config.setMcpId(mcp.id);
|
|
1893
|
+
await config.setSessionId(null);
|
|
1930
1894
|
if (json) {
|
|
1931
|
-
formatOutput({ selected: mcp
|
|
1895
|
+
formatOutput({ selected: mcp }, true);
|
|
1932
1896
|
} else {
|
|
1933
|
-
formatSuccess(`Now using MCP "${name}"
|
|
1897
|
+
formatSuccess(`Now using MCP "${name}"`, false);
|
|
1934
1898
|
console.log();
|
|
1935
1899
|
console.log(` MCP ID: ${mcp.id}`);
|
|
1936
1900
|
console.log();
|