@waniwani/cli 0.0.37 → 0.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +447 -447
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -868,7 +868,7 @@ async function requireSessionId() {
|
|
|
868
868
|
const sessionId = await config.getSessionId();
|
|
869
869
|
if (!sessionId) {
|
|
870
870
|
throw new McpError(
|
|
871
|
-
"No active session. Run 'waniwani mcp
|
|
871
|
+
"No active session. Run 'waniwani mcp preview' to start development."
|
|
872
872
|
);
|
|
873
873
|
}
|
|
874
874
|
return sessionId;
|
|
@@ -954,172 +954,138 @@ var deleteCommand = new Command5("delete").description("Delete the MCP (includes
|
|
|
954
954
|
}
|
|
955
955
|
});
|
|
956
956
|
|
|
957
|
-
// src/commands/mcp/
|
|
958
|
-
import {
|
|
957
|
+
// src/commands/mcp/file/index.ts
|
|
958
|
+
import { Command as Command9 } from "commander";
|
|
959
|
+
|
|
960
|
+
// src/commands/mcp/file/list.ts
|
|
961
|
+
import chalk5 from "chalk";
|
|
959
962
|
import { Command as Command6 } from "commander";
|
|
960
963
|
import ora3 from "ora";
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
if (
|
|
973
|
-
|
|
964
|
+
var listCommand = new Command6("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) => {
|
|
965
|
+
const globalOptions = command.optsWithGlobals();
|
|
966
|
+
const json = globalOptions.json ?? false;
|
|
967
|
+
try {
|
|
968
|
+
await requireMcpId(options.mcpId);
|
|
969
|
+
const sessionId = await requireSessionId();
|
|
970
|
+
const spinner = ora3(`Listing ${path}...`).start();
|
|
971
|
+
const result = await api.get(
|
|
972
|
+
`/api/mcp/sessions/${sessionId}/files/list?path=${encodeURIComponent(path)}`
|
|
973
|
+
);
|
|
974
|
+
spinner.stop();
|
|
975
|
+
if (json) {
|
|
976
|
+
formatOutput(result, true);
|
|
977
|
+
} else {
|
|
978
|
+
console.log(chalk5.bold(`
|
|
979
|
+
Directory: ${result.path}
|
|
980
|
+
`));
|
|
981
|
+
if (result.entries.length === 0) {
|
|
982
|
+
console.log(" (empty)");
|
|
983
|
+
} else {
|
|
984
|
+
const rows = result.entries.map((entry) => {
|
|
985
|
+
const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
|
|
986
|
+
const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
|
|
987
|
+
return [name, size];
|
|
988
|
+
});
|
|
989
|
+
formatTable(["Name", "Size"], rows, false);
|
|
990
|
+
}
|
|
991
|
+
console.log();
|
|
974
992
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
if (existsSync3(join3(current, PROJECT_DIR))) {
|
|
980
|
-
return current;
|
|
993
|
+
} catch (error) {
|
|
994
|
+
handleError(error, json);
|
|
995
|
+
process.exit(1);
|
|
981
996
|
}
|
|
982
|
-
|
|
997
|
+
});
|
|
998
|
+
function formatSize(bytes) {
|
|
999
|
+
if (bytes === void 0) return "";
|
|
1000
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1001
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1002
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
983
1003
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const gitignorePath = join3(projectRoot, ".gitignore");
|
|
1004
|
-
if (existsSync3(gitignorePath)) {
|
|
1005
|
-
try {
|
|
1006
|
-
const content = await readFile2(gitignorePath, "utf-8");
|
|
1007
|
-
ig.add(content);
|
|
1008
|
-
} catch {
|
|
1004
|
+
|
|
1005
|
+
// src/commands/mcp/file/read.ts
|
|
1006
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
1007
|
+
import { Command as Command7 } from "commander";
|
|
1008
|
+
import ora4 from "ora";
|
|
1009
|
+
var readCommand = new Command7("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) => {
|
|
1010
|
+
const globalOptions = command.optsWithGlobals();
|
|
1011
|
+
const json = globalOptions.json ?? false;
|
|
1012
|
+
try {
|
|
1013
|
+
await requireMcpId(options.mcpId);
|
|
1014
|
+
const sessionId = await requireSessionId();
|
|
1015
|
+
const encoding = options.base64 ? "base64" : "utf8";
|
|
1016
|
+
const spinner = ora4(`Reading ${path}...`).start();
|
|
1017
|
+
const result = await api.get(
|
|
1018
|
+
`/api/mcp/sessions/${sessionId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
|
|
1019
|
+
);
|
|
1020
|
+
spinner.stop();
|
|
1021
|
+
if (!result.exists) {
|
|
1022
|
+
throw new McpError(`File not found: ${path}`);
|
|
1009
1023
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
1018
|
-
for (const entry of entries) {
|
|
1019
|
-
const fullPath = join3(dir, entry.name);
|
|
1020
|
-
const relativePath = relative(projectRoot, fullPath);
|
|
1021
|
-
if (ig.ignores(relativePath)) {
|
|
1022
|
-
continue;
|
|
1023
|
-
}
|
|
1024
|
-
if (entry.isDirectory()) {
|
|
1025
|
-
await walk(fullPath);
|
|
1026
|
-
} else if (entry.isFile()) {
|
|
1027
|
-
try {
|
|
1028
|
-
const content = await readFile2(fullPath);
|
|
1029
|
-
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
1030
|
-
files.push({
|
|
1031
|
-
path: relativePath,
|
|
1032
|
-
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
1033
|
-
encoding: isBinary ? "base64" : "utf8"
|
|
1034
|
-
});
|
|
1035
|
-
} catch {
|
|
1036
|
-
}
|
|
1024
|
+
if (options.output) {
|
|
1025
|
+
const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
|
|
1026
|
+
await writeFile2(options.output, buffer);
|
|
1027
|
+
if (json) {
|
|
1028
|
+
formatOutput({ path, savedTo: options.output }, true);
|
|
1029
|
+
} else {
|
|
1030
|
+
formatSuccess(`Saved to ${options.output}`, false);
|
|
1037
1031
|
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
await walk(projectRoot);
|
|
1041
|
-
return files;
|
|
1042
|
-
}
|
|
1043
|
-
async function pullFilesFromGithub(mcpId, targetDir) {
|
|
1044
|
-
const result = await api.get(
|
|
1045
|
-
`/api/mcp/repositories/${mcpId}/files`
|
|
1046
|
-
);
|
|
1047
|
-
const writtenFiles = [];
|
|
1048
|
-
for (const file of result.files) {
|
|
1049
|
-
const localPath = join3(targetDir, file.path);
|
|
1050
|
-
const dir = dirname(localPath);
|
|
1051
|
-
await mkdir2(dir, { recursive: true });
|
|
1052
|
-
if (file.encoding === "base64") {
|
|
1053
|
-
await writeFile2(localPath, Buffer.from(file.content, "base64"));
|
|
1032
|
+
} else if (json) {
|
|
1033
|
+
formatOutput(result, true);
|
|
1054
1034
|
} else {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
return { count: writtenFiles.length, files: writtenFiles };
|
|
1060
|
-
}
|
|
1061
|
-
async function collectSingleFile(projectRoot, filePath) {
|
|
1062
|
-
const fullPath = join3(projectRoot, filePath);
|
|
1063
|
-
const relativePath = relative(projectRoot, fullPath);
|
|
1064
|
-
if (!existsSync3(fullPath)) {
|
|
1065
|
-
return null;
|
|
1066
|
-
}
|
|
1067
|
-
try {
|
|
1068
|
-
const fileStat = await stat(fullPath);
|
|
1069
|
-
if (!fileStat.isFile()) {
|
|
1070
|
-
return null;
|
|
1035
|
+
process.stdout.write(result.content);
|
|
1036
|
+
if (!result.content.endsWith("\n")) {
|
|
1037
|
+
process.stdout.write("\n");
|
|
1038
|
+
}
|
|
1071
1039
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
path: relativePath,
|
|
1076
|
-
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
1077
|
-
encoding: isBinary ? "base64" : "utf8"
|
|
1078
|
-
};
|
|
1079
|
-
} catch {
|
|
1080
|
-
return null;
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
handleError(error, json);
|
|
1042
|
+
process.exit(1);
|
|
1081
1043
|
}
|
|
1082
|
-
}
|
|
1044
|
+
});
|
|
1083
1045
|
|
|
1084
|
-
// src/commands/mcp/
|
|
1085
|
-
|
|
1046
|
+
// src/commands/mcp/file/write.ts
|
|
1047
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1048
|
+
import { Command as Command8 } from "commander";
|
|
1049
|
+
import ora5 from "ora";
|
|
1050
|
+
var writeCommand = new Command8("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) => {
|
|
1086
1051
|
const globalOptions = command.optsWithGlobals();
|
|
1087
1052
|
const json = globalOptions.json ?? false;
|
|
1088
1053
|
try {
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1054
|
+
await requireMcpId(options.mcpId);
|
|
1055
|
+
const sessionId = await requireSessionId();
|
|
1056
|
+
let content;
|
|
1057
|
+
let encoding = "utf8";
|
|
1058
|
+
if (options.content) {
|
|
1059
|
+
content = options.content;
|
|
1060
|
+
if (options.base64) {
|
|
1061
|
+
encoding = "base64";
|
|
1062
|
+
}
|
|
1063
|
+
} else if (options.file) {
|
|
1064
|
+
const fileBuffer = await readFile2(options.file);
|
|
1065
|
+
if (options.base64) {
|
|
1066
|
+
content = fileBuffer.toString("base64");
|
|
1067
|
+
encoding = "base64";
|
|
1068
|
+
} else {
|
|
1069
|
+
content = fileBuffer.toString("utf8");
|
|
1070
|
+
}
|
|
1071
|
+
} else {
|
|
1092
1072
|
throw new CLIError(
|
|
1093
|
-
"
|
|
1094
|
-
"
|
|
1073
|
+
"Either --content or --file is required",
|
|
1074
|
+
"MISSING_CONTENT"
|
|
1095
1075
|
);
|
|
1096
1076
|
}
|
|
1097
|
-
|
|
1098
|
-
if (!message) {
|
|
1099
|
-
message = await input({
|
|
1100
|
-
message: "Commit message:",
|
|
1101
|
-
validate: (value) => value.trim() ? true : "Commit message is required"
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
const spinner = ora3("Collecting files...").start();
|
|
1105
|
-
const files = await collectFiles(projectRoot);
|
|
1106
|
-
if (files.length === 0) {
|
|
1107
|
-
spinner.fail("No files to deploy");
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
spinner.text = `Pushing ${files.length} files to GitHub...`;
|
|
1077
|
+
const spinner = ora5(`Writing ${path}...`).start();
|
|
1111
1078
|
const result = await api.post(
|
|
1112
|
-
`/api/mcp/
|
|
1113
|
-
{
|
|
1079
|
+
`/api/mcp/sessions/${sessionId}/files`,
|
|
1080
|
+
{
|
|
1081
|
+
files: [{ path, content, encoding }]
|
|
1082
|
+
}
|
|
1114
1083
|
);
|
|
1115
|
-
spinner.succeed(`
|
|
1084
|
+
spinner.succeed(`Wrote ${path}`);
|
|
1116
1085
|
if (json) {
|
|
1117
1086
|
formatOutput(result, true);
|
|
1118
1087
|
} else {
|
|
1119
|
-
|
|
1120
|
-
formatSuccess("Files pushed to GitHub!", false);
|
|
1121
|
-
console.log();
|
|
1122
|
-
console.log("Deployment will start automatically via webhook.");
|
|
1088
|
+
formatSuccess(`File written: ${path}`, false);
|
|
1123
1089
|
}
|
|
1124
1090
|
} catch (error) {
|
|
1125
1091
|
handleError(error, json);
|
|
@@ -1127,296 +1093,36 @@ var deployCommand = new Command6("deploy").description("Push local files to GitH
|
|
|
1127
1093
|
}
|
|
1128
1094
|
});
|
|
1129
1095
|
|
|
1130
|
-
// src/commands/mcp/
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
}
|
|
1155
|
-
const spinner = ora4("Starting development environment...").start();
|
|
1156
|
-
spinner.text = "Starting session...";
|
|
1157
|
-
let sessionId;
|
|
1158
|
-
try {
|
|
1159
|
-
const sessionResponse = await api.post(
|
|
1160
|
-
`/api/mcp/repositories/${mcpId}/session`,
|
|
1161
|
-
{}
|
|
1162
|
-
);
|
|
1163
|
-
sessionId = sessionResponse.sandbox.id;
|
|
1164
|
-
} catch {
|
|
1165
|
-
const existing = await api.get(
|
|
1166
|
-
`/api/mcp/repositories/${mcpId}/session`
|
|
1167
|
-
);
|
|
1168
|
-
if (!existing) {
|
|
1169
|
-
throw new CLIError("Failed to start session", "SESSION_ERROR");
|
|
1170
|
-
}
|
|
1171
|
-
sessionId = existing.id;
|
|
1172
|
-
}
|
|
1173
|
-
await config.setSessionId(sessionId);
|
|
1174
|
-
spinner.text = "Syncing files to sandbox...";
|
|
1175
|
-
const files = await collectFiles(projectRoot);
|
|
1176
|
-
if (files.length > 0) {
|
|
1177
|
-
await api.post(
|
|
1178
|
-
`/api/mcp/sessions/${sessionId}/files`,
|
|
1179
|
-
{ files }
|
|
1180
|
-
);
|
|
1181
|
-
}
|
|
1182
|
-
spinner.text = "Starting MCP server...";
|
|
1183
|
-
const serverResult = await api.post(
|
|
1184
|
-
`/api/mcp/sessions/${sessionId}/server`,
|
|
1185
|
-
{ action: "start" }
|
|
1186
|
-
);
|
|
1187
|
-
spinner.succeed("Development environment ready");
|
|
1188
|
-
console.log();
|
|
1189
|
-
formatSuccess("Live preview started!", false);
|
|
1190
|
-
console.log();
|
|
1191
|
-
console.log(` Preview URL: ${serverResult.previewUrl}`);
|
|
1192
|
-
console.log();
|
|
1193
|
-
console.log(` MCP Inspector:`);
|
|
1194
|
-
console.log(
|
|
1195
|
-
` npx @anthropic-ai/mcp-inspector@latest "${serverResult.previewUrl}/mcp"`
|
|
1196
|
-
);
|
|
1197
|
-
console.log();
|
|
1198
|
-
if (options.watch !== false) {
|
|
1199
|
-
console.log("Watching for file changes... (Ctrl+C to stop)");
|
|
1200
|
-
console.log();
|
|
1201
|
-
const ig = await loadIgnorePatterns(projectRoot);
|
|
1202
|
-
const watcher = watch(projectRoot, {
|
|
1203
|
-
ignored: (path) => {
|
|
1204
|
-
const relative2 = path.replace(`${projectRoot}/`, "");
|
|
1205
|
-
if (relative2 === path) return false;
|
|
1206
|
-
return ig.ignores(relative2);
|
|
1207
|
-
},
|
|
1208
|
-
persistent: true,
|
|
1209
|
-
ignoreInitial: true,
|
|
1210
|
-
awaitWriteFinish: {
|
|
1211
|
-
stabilityThreshold: 100,
|
|
1212
|
-
pollInterval: 50
|
|
1213
|
-
}
|
|
1214
|
-
});
|
|
1215
|
-
const syncFile = async (filePath) => {
|
|
1216
|
-
const relativePath = filePath.replace(`${projectRoot}/`, "");
|
|
1217
|
-
const file = await collectSingleFile(projectRoot, relativePath);
|
|
1218
|
-
if (file) {
|
|
1219
|
-
try {
|
|
1220
|
-
await api.post(
|
|
1221
|
-
`/api/mcp/sessions/${sessionId}/files`,
|
|
1222
|
-
{ files: [file] }
|
|
1223
|
-
);
|
|
1224
|
-
console.log(` Synced: ${relativePath}`);
|
|
1225
|
-
} catch {
|
|
1226
|
-
console.error(` Failed to sync: ${relativePath}`);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
};
|
|
1230
|
-
watcher.on("add", syncFile);
|
|
1231
|
-
watcher.on("change", syncFile);
|
|
1232
|
-
watcher.on("unlink", (filePath) => {
|
|
1233
|
-
const relativePath = filePath.replace(`${projectRoot}/`, "");
|
|
1234
|
-
console.log(` Deleted: ${relativePath}`);
|
|
1235
|
-
});
|
|
1236
|
-
process.on("SIGINT", async () => {
|
|
1237
|
-
console.log();
|
|
1238
|
-
console.log("Stopping development environment...");
|
|
1239
|
-
await watcher.close();
|
|
1240
|
-
process.exit(0);
|
|
1241
|
-
});
|
|
1242
|
-
await new Promise(() => {
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
} catch (error) {
|
|
1246
|
-
handleError(error, json);
|
|
1247
|
-
process.exit(1);
|
|
1248
|
-
}
|
|
1249
|
-
});
|
|
1250
|
-
|
|
1251
|
-
// src/commands/mcp/file/index.ts
|
|
1252
|
-
import { Command as Command11 } from "commander";
|
|
1253
|
-
|
|
1254
|
-
// src/commands/mcp/file/list.ts
|
|
1255
|
-
import chalk5 from "chalk";
|
|
1256
|
-
import { Command as Command8 } from "commander";
|
|
1257
|
-
import ora5 from "ora";
|
|
1258
|
-
var listCommand = new Command8("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) => {
|
|
1259
|
-
const globalOptions = command.optsWithGlobals();
|
|
1260
|
-
const json = globalOptions.json ?? false;
|
|
1261
|
-
try {
|
|
1262
|
-
await requireMcpId(options.mcpId);
|
|
1263
|
-
const sessionId = await requireSessionId();
|
|
1264
|
-
const spinner = ora5(`Listing ${path}...`).start();
|
|
1265
|
-
const result = await api.get(
|
|
1266
|
-
`/api/mcp/sessions/${sessionId}/files/list?path=${encodeURIComponent(path)}`
|
|
1267
|
-
);
|
|
1268
|
-
spinner.stop();
|
|
1269
|
-
if (json) {
|
|
1270
|
-
formatOutput(result, true);
|
|
1271
|
-
} else {
|
|
1272
|
-
console.log(chalk5.bold(`
|
|
1273
|
-
Directory: ${result.path}
|
|
1274
|
-
`));
|
|
1275
|
-
if (result.entries.length === 0) {
|
|
1276
|
-
console.log(" (empty)");
|
|
1277
|
-
} else {
|
|
1278
|
-
const rows = result.entries.map((entry) => {
|
|
1279
|
-
const name = entry.type === "directory" ? chalk5.blue(`${entry.name}/`) : entry.name;
|
|
1280
|
-
const size = entry.type === "directory" ? chalk5.gray("<dir>") : formatSize(entry.size);
|
|
1281
|
-
return [name, size];
|
|
1282
|
-
});
|
|
1283
|
-
formatTable(["Name", "Size"], rows, false);
|
|
1284
|
-
}
|
|
1285
|
-
console.log();
|
|
1286
|
-
}
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
handleError(error, json);
|
|
1289
|
-
process.exit(1);
|
|
1290
|
-
}
|
|
1291
|
-
});
|
|
1292
|
-
function formatSize(bytes) {
|
|
1293
|
-
if (bytes === void 0) return "";
|
|
1294
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
1295
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1296
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
// src/commands/mcp/file/read.ts
|
|
1300
|
-
import { writeFile as writeFile3 } from "fs/promises";
|
|
1301
|
-
import { Command as Command9 } from "commander";
|
|
1302
|
-
import ora6 from "ora";
|
|
1303
|
-
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) => {
|
|
1304
|
-
const globalOptions = command.optsWithGlobals();
|
|
1305
|
-
const json = globalOptions.json ?? false;
|
|
1306
|
-
try {
|
|
1307
|
-
await requireMcpId(options.mcpId);
|
|
1308
|
-
const sessionId = await requireSessionId();
|
|
1309
|
-
const encoding = options.base64 ? "base64" : "utf8";
|
|
1310
|
-
const spinner = ora6(`Reading ${path}...`).start();
|
|
1311
|
-
const result = await api.get(
|
|
1312
|
-
`/api/mcp/sessions/${sessionId}/files?path=${encodeURIComponent(path)}&encoding=${encoding}`
|
|
1313
|
-
);
|
|
1314
|
-
spinner.stop();
|
|
1315
|
-
if (!result.exists) {
|
|
1316
|
-
throw new McpError(`File not found: ${path}`);
|
|
1317
|
-
}
|
|
1318
|
-
if (options.output) {
|
|
1319
|
-
const buffer = result.encoding === "base64" ? Buffer.from(result.content, "base64") : Buffer.from(result.content, "utf8");
|
|
1320
|
-
await writeFile3(options.output, buffer);
|
|
1321
|
-
if (json) {
|
|
1322
|
-
formatOutput({ path, savedTo: options.output }, true);
|
|
1323
|
-
} else {
|
|
1324
|
-
formatSuccess(`Saved to ${options.output}`, false);
|
|
1325
|
-
}
|
|
1326
|
-
} else if (json) {
|
|
1327
|
-
formatOutput(result, true);
|
|
1328
|
-
} else {
|
|
1329
|
-
process.stdout.write(result.content);
|
|
1330
|
-
if (!result.content.endsWith("\n")) {
|
|
1331
|
-
process.stdout.write("\n");
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
handleError(error, json);
|
|
1336
|
-
process.exit(1);
|
|
1337
|
-
}
|
|
1338
|
-
});
|
|
1339
|
-
|
|
1340
|
-
// src/commands/mcp/file/write.ts
|
|
1341
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
1342
|
-
import { Command as Command10 } from "commander";
|
|
1343
|
-
import ora7 from "ora";
|
|
1344
|
-
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) => {
|
|
1345
|
-
const globalOptions = command.optsWithGlobals();
|
|
1346
|
-
const json = globalOptions.json ?? false;
|
|
1347
|
-
try {
|
|
1348
|
-
await requireMcpId(options.mcpId);
|
|
1349
|
-
const sessionId = await requireSessionId();
|
|
1350
|
-
let content;
|
|
1351
|
-
let encoding = "utf8";
|
|
1352
|
-
if (options.content) {
|
|
1353
|
-
content = options.content;
|
|
1354
|
-
if (options.base64) {
|
|
1355
|
-
encoding = "base64";
|
|
1356
|
-
}
|
|
1357
|
-
} else if (options.file) {
|
|
1358
|
-
const fileBuffer = await readFile3(options.file);
|
|
1359
|
-
if (options.base64) {
|
|
1360
|
-
content = fileBuffer.toString("base64");
|
|
1361
|
-
encoding = "base64";
|
|
1362
|
-
} else {
|
|
1363
|
-
content = fileBuffer.toString("utf8");
|
|
1364
|
-
}
|
|
1365
|
-
} else {
|
|
1366
|
-
throw new CLIError(
|
|
1367
|
-
"Either --content or --file is required",
|
|
1368
|
-
"MISSING_CONTENT"
|
|
1369
|
-
);
|
|
1370
|
-
}
|
|
1371
|
-
const spinner = ora7(`Writing ${path}...`).start();
|
|
1372
|
-
const result = await api.post(
|
|
1373
|
-
`/api/mcp/sessions/${sessionId}/files`,
|
|
1374
|
-
{
|
|
1375
|
-
files: [{ path, content, encoding }]
|
|
1376
|
-
}
|
|
1377
|
-
);
|
|
1378
|
-
spinner.succeed(`Wrote ${path}`);
|
|
1379
|
-
if (json) {
|
|
1380
|
-
formatOutput(result, true);
|
|
1381
|
-
} else {
|
|
1382
|
-
formatSuccess(`File written: ${path}`, false);
|
|
1383
|
-
}
|
|
1384
|
-
} catch (error) {
|
|
1385
|
-
handleError(error, json);
|
|
1386
|
-
process.exit(1);
|
|
1387
|
-
}
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
|
-
// src/commands/mcp/file/index.ts
|
|
1391
|
-
var fileCommand = new Command11("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
|
|
1392
|
-
|
|
1393
|
-
// src/commands/mcp/init.ts
|
|
1394
|
-
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
1395
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
1396
|
-
import { join as join4 } from "path";
|
|
1397
|
-
import { Command as Command12 } from "commander";
|
|
1398
|
-
import ora8 from "ora";
|
|
1399
|
-
async function loadParentConfig(cwd) {
|
|
1400
|
-
const parentConfigPath = join4(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
1401
|
-
if (!existsSync4(parentConfigPath)) {
|
|
1402
|
-
return null;
|
|
1403
|
-
}
|
|
1404
|
-
try {
|
|
1405
|
-
const content = await readFile4(parentConfigPath, "utf-8");
|
|
1406
|
-
const config2 = JSON.parse(content);
|
|
1407
|
-
const { mcpId: _, sessionId: __, ...rest } = config2;
|
|
1408
|
-
return rest;
|
|
1409
|
-
} catch {
|
|
1410
|
-
return null;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
var initCommand = new Command12("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").action(async (name, _options, command) => {
|
|
1096
|
+
// src/commands/mcp/file/index.ts
|
|
1097
|
+
var fileCommand = new Command9("file").description("File operations in MCP sandbox").addCommand(readCommand).addCommand(writeCommand).addCommand(listCommand);
|
|
1098
|
+
|
|
1099
|
+
// src/commands/mcp/init.ts
|
|
1100
|
+
import { existsSync as existsSync3, mkdirSync } from "fs";
|
|
1101
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1102
|
+
import { join as join3 } from "path";
|
|
1103
|
+
import { Command as Command10 } from "commander";
|
|
1104
|
+
import ora6 from "ora";
|
|
1105
|
+
async function loadParentConfig(cwd) {
|
|
1106
|
+
const parentConfigPath = join3(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
1107
|
+
if (!existsSync3(parentConfigPath)) {
|
|
1108
|
+
return null;
|
|
1109
|
+
}
|
|
1110
|
+
try {
|
|
1111
|
+
const content = await readFile3(parentConfigPath, "utf-8");
|
|
1112
|
+
const config2 = JSON.parse(content);
|
|
1113
|
+
const { mcpId: _, sessionId: __, ...rest } = config2;
|
|
1114
|
+
return rest;
|
|
1115
|
+
} catch {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
var initCommand = new Command10("init").description("Create a new MCP project").argument("<name>", "Name for the MCP project").action(async (name, _options, command) => {
|
|
1414
1120
|
const globalOptions = command.optsWithGlobals();
|
|
1415
1121
|
const json = globalOptions.json ?? false;
|
|
1416
1122
|
try {
|
|
1417
1123
|
const cwd = process.cwd();
|
|
1418
|
-
const projectDir =
|
|
1419
|
-
if (
|
|
1124
|
+
const projectDir = join3(cwd, name);
|
|
1125
|
+
if (existsSync3(projectDir)) {
|
|
1420
1126
|
if (json) {
|
|
1421
1127
|
formatOutput(
|
|
1422
1128
|
{
|
|
@@ -1430,7 +1136,7 @@ var initCommand = new Command12("init").description("Create a new MCP project").
|
|
|
1430
1136
|
}
|
|
1431
1137
|
process.exit(1);
|
|
1432
1138
|
}
|
|
1433
|
-
const spinner =
|
|
1139
|
+
const spinner = ora6("Creating MCP...").start();
|
|
1434
1140
|
const result = await api.post("/api/mcp/repositories", {
|
|
1435
1141
|
name
|
|
1436
1142
|
});
|
|
@@ -1457,7 +1163,7 @@ var initCommand = new Command12("init").description("Create a new MCP project").
|
|
|
1457
1163
|
console.log("Next steps:");
|
|
1458
1164
|
console.log(` cd ${name}`);
|
|
1459
1165
|
console.log(" waniwani mcp sync # Pull template files");
|
|
1460
|
-
console.log(" waniwani mcp
|
|
1166
|
+
console.log(" waniwani mcp preview # Start developing");
|
|
1461
1167
|
}
|
|
1462
1168
|
} catch (error) {
|
|
1463
1169
|
handleError(error, json);
|
|
@@ -1467,13 +1173,13 @@ var initCommand = new Command12("init").description("Create a new MCP project").
|
|
|
1467
1173
|
|
|
1468
1174
|
// src/commands/mcp/list.ts
|
|
1469
1175
|
import chalk6 from "chalk";
|
|
1470
|
-
import { Command as
|
|
1471
|
-
import
|
|
1472
|
-
var listCommand2 = new
|
|
1176
|
+
import { Command as Command11 } from "commander";
|
|
1177
|
+
import ora7 from "ora";
|
|
1178
|
+
var listCommand2 = new Command11("list").description("List all MCPs in your organization").action(async (_, command) => {
|
|
1473
1179
|
const globalOptions = command.optsWithGlobals();
|
|
1474
1180
|
const json = globalOptions.json ?? false;
|
|
1475
1181
|
try {
|
|
1476
|
-
const spinner =
|
|
1182
|
+
const spinner = ora7("Fetching MCPs...").start();
|
|
1477
1183
|
const mcps = await api.get(
|
|
1478
1184
|
"/api/mcp/repositories"
|
|
1479
1185
|
);
|
|
@@ -1527,10 +1233,10 @@ var listCommand2 = new Command13("list").description("List all MCPs in your orga
|
|
|
1527
1233
|
|
|
1528
1234
|
// src/commands/mcp/logs.ts
|
|
1529
1235
|
import chalk7 from "chalk";
|
|
1530
|
-
import { Command as
|
|
1531
|
-
import
|
|
1532
|
-
var logsCommand = new
|
|
1533
|
-
const globalOptions = command.optsWithGlobals();
|
|
1236
|
+
import { Command as Command12 } from "commander";
|
|
1237
|
+
import ora8 from "ora";
|
|
1238
|
+
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) => {
|
|
1239
|
+
const globalOptions = command.optsWithGlobals();
|
|
1534
1240
|
const json = globalOptions.json ?? false;
|
|
1535
1241
|
let reader;
|
|
1536
1242
|
const cleanup = () => {
|
|
@@ -1553,7 +1259,7 @@ var logsCommand = new Command14("logs").description("Stream logs from the MCP se
|
|
|
1553
1259
|
}
|
|
1554
1260
|
let cmdId = cmdIdArg;
|
|
1555
1261
|
if (!cmdId) {
|
|
1556
|
-
const spinner =
|
|
1262
|
+
const spinner = ora8("Getting server status...").start();
|
|
1557
1263
|
const status = await api.post(
|
|
1558
1264
|
`/api/mcp/sessions/${sessionId}/server`,
|
|
1559
1265
|
{ action: "status" }
|
|
@@ -1561,7 +1267,7 @@ var logsCommand = new Command14("logs").description("Stream logs from the MCP se
|
|
|
1561
1267
|
spinner.stop();
|
|
1562
1268
|
if (!status.running || !status.cmdId) {
|
|
1563
1269
|
throw new McpError(
|
|
1564
|
-
"No server is running. Run 'waniwani mcp
|
|
1270
|
+
"No server is running. Run 'waniwani mcp preview' first."
|
|
1565
1271
|
);
|
|
1566
1272
|
}
|
|
1567
1273
|
cmdId = status.cmdId;
|
|
@@ -1668,6 +1374,300 @@ Error: ${event.error}`));
|
|
|
1668
1374
|
}
|
|
1669
1375
|
});
|
|
1670
1376
|
|
|
1377
|
+
// src/commands/mcp/preview.ts
|
|
1378
|
+
import { watch } from "chokidar";
|
|
1379
|
+
import { Command as Command13 } from "commander";
|
|
1380
|
+
import ora9 from "ora";
|
|
1381
|
+
|
|
1382
|
+
// src/lib/sync.ts
|
|
1383
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1384
|
+
import { mkdir as mkdir2, readdir, readFile as readFile4, stat, writeFile as writeFile3 } from "fs/promises";
|
|
1385
|
+
import { dirname, join as join4, relative } from "path";
|
|
1386
|
+
import ignore from "ignore";
|
|
1387
|
+
var PROJECT_DIR = ".waniwani";
|
|
1388
|
+
async function findProjectRoot(startDir) {
|
|
1389
|
+
let current = startDir;
|
|
1390
|
+
const root = dirname(current);
|
|
1391
|
+
while (current !== root) {
|
|
1392
|
+
if (existsSync4(join4(current, PROJECT_DIR))) {
|
|
1393
|
+
return current;
|
|
1394
|
+
}
|
|
1395
|
+
const parent = dirname(current);
|
|
1396
|
+
if (parent === current) break;
|
|
1397
|
+
current = parent;
|
|
1398
|
+
}
|
|
1399
|
+
if (existsSync4(join4(current, PROJECT_DIR))) {
|
|
1400
|
+
return current;
|
|
1401
|
+
}
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
1405
|
+
".waniwani",
|
|
1406
|
+
".git",
|
|
1407
|
+
"node_modules",
|
|
1408
|
+
".env",
|
|
1409
|
+
".env.*",
|
|
1410
|
+
".DS_Store",
|
|
1411
|
+
"*.log",
|
|
1412
|
+
".cache",
|
|
1413
|
+
"dist",
|
|
1414
|
+
"coverage",
|
|
1415
|
+
".turbo",
|
|
1416
|
+
".next",
|
|
1417
|
+
".nuxt",
|
|
1418
|
+
".vercel"
|
|
1419
|
+
];
|
|
1420
|
+
async function loadIgnorePatterns(projectRoot) {
|
|
1421
|
+
const ig = ignore();
|
|
1422
|
+
ig.add(DEFAULT_IGNORE_PATTERNS);
|
|
1423
|
+
const gitignorePath = join4(projectRoot, ".gitignore");
|
|
1424
|
+
if (existsSync4(gitignorePath)) {
|
|
1425
|
+
try {
|
|
1426
|
+
const content = await readFile4(gitignorePath, "utf-8");
|
|
1427
|
+
ig.add(content);
|
|
1428
|
+
} catch {
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
return ig;
|
|
1432
|
+
}
|
|
1433
|
+
async function collectFiles(projectRoot) {
|
|
1434
|
+
const ig = await loadIgnorePatterns(projectRoot);
|
|
1435
|
+
const files = [];
|
|
1436
|
+
async function walk(dir) {
|
|
1437
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
1438
|
+
for (const entry of entries) {
|
|
1439
|
+
const fullPath = join4(dir, entry.name);
|
|
1440
|
+
const relativePath = relative(projectRoot, fullPath);
|
|
1441
|
+
if (ig.ignores(relativePath)) {
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
if (entry.isDirectory()) {
|
|
1445
|
+
await walk(fullPath);
|
|
1446
|
+
} else if (entry.isFile()) {
|
|
1447
|
+
try {
|
|
1448
|
+
const content = await readFile4(fullPath);
|
|
1449
|
+
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
1450
|
+
files.push({
|
|
1451
|
+
path: relativePath,
|
|
1452
|
+
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
1453
|
+
encoding: isBinary ? "base64" : "utf8"
|
|
1454
|
+
});
|
|
1455
|
+
} catch {
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
await walk(projectRoot);
|
|
1461
|
+
return files;
|
|
1462
|
+
}
|
|
1463
|
+
async function pullFilesFromGithub(mcpId, targetDir) {
|
|
1464
|
+
const result = await api.get(
|
|
1465
|
+
`/api/mcp/repositories/${mcpId}/files`
|
|
1466
|
+
);
|
|
1467
|
+
const writtenFiles = [];
|
|
1468
|
+
for (const file of result.files) {
|
|
1469
|
+
const localPath = join4(targetDir, file.path);
|
|
1470
|
+
const dir = dirname(localPath);
|
|
1471
|
+
await mkdir2(dir, { recursive: true });
|
|
1472
|
+
if (file.encoding === "base64") {
|
|
1473
|
+
await writeFile3(localPath, Buffer.from(file.content, "base64"));
|
|
1474
|
+
} else {
|
|
1475
|
+
await writeFile3(localPath, file.content, "utf8");
|
|
1476
|
+
}
|
|
1477
|
+
writtenFiles.push(file.path);
|
|
1478
|
+
}
|
|
1479
|
+
return { count: writtenFiles.length, files: writtenFiles };
|
|
1480
|
+
}
|
|
1481
|
+
async function collectSingleFile(projectRoot, filePath) {
|
|
1482
|
+
const fullPath = join4(projectRoot, filePath);
|
|
1483
|
+
const relativePath = relative(projectRoot, fullPath);
|
|
1484
|
+
if (!existsSync4(fullPath)) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
const fileStat = await stat(fullPath);
|
|
1489
|
+
if (!fileStat.isFile()) {
|
|
1490
|
+
return null;
|
|
1491
|
+
}
|
|
1492
|
+
const content = await readFile4(fullPath);
|
|
1493
|
+
const isBinary = isBinaryPath(fullPath) || detectBinary(content);
|
|
1494
|
+
return {
|
|
1495
|
+
path: relativePath,
|
|
1496
|
+
content: isBinary ? content.toString("base64") : content.toString("utf8"),
|
|
1497
|
+
encoding: isBinary ? "base64" : "utf8"
|
|
1498
|
+
};
|
|
1499
|
+
} catch {
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// src/commands/mcp/preview.ts
|
|
1505
|
+
var previewCommand = new Command13("preview").description("Start live development with sandbox and file watching").option("--mcp-id <id>", "Specific MCP ID").option("--no-watch", "Skip file watching").option("--no-logs", "Don't stream logs to terminal").action(async (options, command) => {
|
|
1506
|
+
const globalOptions = command.optsWithGlobals();
|
|
1507
|
+
const json = globalOptions.json ?? false;
|
|
1508
|
+
try {
|
|
1509
|
+
const projectRoot = await findProjectRoot(process.cwd());
|
|
1510
|
+
if (!projectRoot) {
|
|
1511
|
+
throw new CLIError(
|
|
1512
|
+
"Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
|
|
1513
|
+
"NOT_IN_PROJECT"
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
let mcpId = options.mcpId;
|
|
1517
|
+
if (!mcpId) {
|
|
1518
|
+
mcpId = await config.getMcpId();
|
|
1519
|
+
}
|
|
1520
|
+
if (!mcpId) {
|
|
1521
|
+
throw new CLIError(
|
|
1522
|
+
"No MCP found. Run 'waniwani mcp init <name>' or use --mcp-id.",
|
|
1523
|
+
"NO_MCP"
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
const spinner = ora9("Starting development environment...").start();
|
|
1527
|
+
spinner.text = "Starting session...";
|
|
1528
|
+
let sessionId;
|
|
1529
|
+
try {
|
|
1530
|
+
const sessionResponse = await api.post(
|
|
1531
|
+
`/api/mcp/repositories/${mcpId}/session`,
|
|
1532
|
+
{}
|
|
1533
|
+
);
|
|
1534
|
+
sessionId = sessionResponse.sandbox.id;
|
|
1535
|
+
} catch {
|
|
1536
|
+
const existing = await api.get(
|
|
1537
|
+
`/api/mcp/repositories/${mcpId}/session`
|
|
1538
|
+
);
|
|
1539
|
+
if (!existing) {
|
|
1540
|
+
throw new CLIError("Failed to start session", "SESSION_ERROR");
|
|
1541
|
+
}
|
|
1542
|
+
sessionId = existing.id;
|
|
1543
|
+
}
|
|
1544
|
+
await config.setSessionId(sessionId);
|
|
1545
|
+
spinner.text = "Syncing files to sandbox...";
|
|
1546
|
+
const files = await collectFiles(projectRoot);
|
|
1547
|
+
if (files.length > 0) {
|
|
1548
|
+
await api.post(
|
|
1549
|
+
`/api/mcp/sessions/${sessionId}/files`,
|
|
1550
|
+
{ files }
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
spinner.text = "Starting MCP server...";
|
|
1554
|
+
const serverResult = await api.post(
|
|
1555
|
+
`/api/mcp/sessions/${sessionId}/server`,
|
|
1556
|
+
{ action: "start" }
|
|
1557
|
+
);
|
|
1558
|
+
spinner.succeed("Development environment ready");
|
|
1559
|
+
console.log();
|
|
1560
|
+
formatSuccess("Live preview started!", false);
|
|
1561
|
+
console.log();
|
|
1562
|
+
console.log(` Preview URL: ${serverResult.previewUrl}`);
|
|
1563
|
+
console.log();
|
|
1564
|
+
console.log(` MCP Inspector:`);
|
|
1565
|
+
console.log(
|
|
1566
|
+
` npx @anthropic-ai/mcp-inspector@latest "${serverResult.previewUrl}/mcp"`
|
|
1567
|
+
);
|
|
1568
|
+
console.log();
|
|
1569
|
+
if (options.watch !== false) {
|
|
1570
|
+
console.log("Watching for file changes... (Ctrl+C to stop)");
|
|
1571
|
+
console.log();
|
|
1572
|
+
const ig = await loadIgnorePatterns(projectRoot);
|
|
1573
|
+
const watcher = watch(projectRoot, {
|
|
1574
|
+
ignored: (path) => {
|
|
1575
|
+
const relative2 = path.replace(`${projectRoot}/`, "");
|
|
1576
|
+
if (relative2 === path) return false;
|
|
1577
|
+
return ig.ignores(relative2);
|
|
1578
|
+
},
|
|
1579
|
+
persistent: true,
|
|
1580
|
+
ignoreInitial: true,
|
|
1581
|
+
awaitWriteFinish: {
|
|
1582
|
+
stabilityThreshold: 100,
|
|
1583
|
+
pollInterval: 50
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
const syncFile = async (filePath) => {
|
|
1587
|
+
const relativePath = filePath.replace(`${projectRoot}/`, "");
|
|
1588
|
+
const file = await collectSingleFile(projectRoot, relativePath);
|
|
1589
|
+
if (file) {
|
|
1590
|
+
try {
|
|
1591
|
+
await api.post(
|
|
1592
|
+
`/api/mcp/sessions/${sessionId}/files`,
|
|
1593
|
+
{ files: [file] }
|
|
1594
|
+
);
|
|
1595
|
+
console.log(` Synced: ${relativePath}`);
|
|
1596
|
+
} catch {
|
|
1597
|
+
console.error(` Failed to sync: ${relativePath}`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
watcher.on("add", syncFile);
|
|
1602
|
+
watcher.on("change", syncFile);
|
|
1603
|
+
watcher.on("unlink", (filePath) => {
|
|
1604
|
+
const relativePath = filePath.replace(`${projectRoot}/`, "");
|
|
1605
|
+
console.log(` Deleted: ${relativePath}`);
|
|
1606
|
+
});
|
|
1607
|
+
process.on("SIGINT", async () => {
|
|
1608
|
+
console.log();
|
|
1609
|
+
console.log("Stopping development environment...");
|
|
1610
|
+
await watcher.close();
|
|
1611
|
+
process.exit(0);
|
|
1612
|
+
});
|
|
1613
|
+
await new Promise(() => {
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
handleError(error, json);
|
|
1618
|
+
process.exit(1);
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
// src/commands/mcp/publish.ts
|
|
1623
|
+
import { input } from "@inquirer/prompts";
|
|
1624
|
+
import { Command as Command14 } from "commander";
|
|
1625
|
+
import ora10 from "ora";
|
|
1626
|
+
var publishCommand = new Command14("publish").description("Push local files to GitHub and trigger deployment").option("-m, --message <msg>", "Commit message").option("--mcp-id <id>", "Specific MCP ID").action(async (options, command) => {
|
|
1627
|
+
const globalOptions = command.optsWithGlobals();
|
|
1628
|
+
const json = globalOptions.json ?? false;
|
|
1629
|
+
try {
|
|
1630
|
+
const mcpId = await requireMcpId(options.mcpId);
|
|
1631
|
+
const projectRoot = await findProjectRoot(process.cwd());
|
|
1632
|
+
if (!projectRoot) {
|
|
1633
|
+
throw new CLIError(
|
|
1634
|
+
"Not in a WaniWani project. Run 'waniwani mcp init <name>' first.",
|
|
1635
|
+
"NOT_IN_PROJECT"
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
let message = options.message;
|
|
1639
|
+
if (!message) {
|
|
1640
|
+
message = await input({
|
|
1641
|
+
message: "Commit message:",
|
|
1642
|
+
validate: (value) => value.trim() ? true : "Commit message is required"
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
const spinner = ora10("Collecting files...").start();
|
|
1646
|
+
const files = await collectFiles(projectRoot);
|
|
1647
|
+
if (files.length === 0) {
|
|
1648
|
+
spinner.fail("No files to deploy");
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
spinner.text = `Pushing ${files.length} files to GitHub...`;
|
|
1652
|
+
const result = await api.post(
|
|
1653
|
+
`/api/mcp/repositories/${mcpId}/deploy`,
|
|
1654
|
+
{ files, message }
|
|
1655
|
+
);
|
|
1656
|
+
spinner.succeed(`Pushed to GitHub (${result.commitSha.slice(0, 7)})`);
|
|
1657
|
+
if (json) {
|
|
1658
|
+
formatOutput(result, true);
|
|
1659
|
+
} else {
|
|
1660
|
+
console.log();
|
|
1661
|
+
formatSuccess("Files pushed to GitHub!", false);
|
|
1662
|
+
console.log();
|
|
1663
|
+
console.log("Deployment will start automatically via webhook.");
|
|
1664
|
+
}
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
handleError(error, json);
|
|
1667
|
+
process.exit(1);
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
1671
|
// src/commands/mcp/run-command.ts
|
|
1672
1672
|
import chalk8 from "chalk";
|
|
1673
1673
|
import { Command as Command15 } from "commander";
|
|
@@ -1793,7 +1793,7 @@ var statusCommand = new Command16("status").description("Show current MCP status
|
|
|
1793
1793
|
formatList(items, false);
|
|
1794
1794
|
console.log();
|
|
1795
1795
|
if (!result.activeSandbox) {
|
|
1796
|
-
console.log("Start development: waniwani mcp
|
|
1796
|
+
console.log("Start development: waniwani mcp preview");
|
|
1797
1797
|
} else if (!serverStatus?.running) {
|
|
1798
1798
|
console.log("View logs: waniwani mcp logs");
|
|
1799
1799
|
}
|
|
@@ -1828,7 +1828,7 @@ var stopCommand = new Command17("stop").description("Stop the development enviro
|
|
|
1828
1828
|
} else {
|
|
1829
1829
|
formatSuccess("Sandbox stopped.", false);
|
|
1830
1830
|
console.log();
|
|
1831
|
-
console.log("Start again: waniwani mcp
|
|
1831
|
+
console.log("Start again: waniwani mcp preview");
|
|
1832
1832
|
}
|
|
1833
1833
|
} catch (error) {
|
|
1834
1834
|
handleError(error, json);
|
|
@@ -1900,7 +1900,7 @@ var useCommand = new Command19("use").description("Select an MCP to use for subs
|
|
|
1900
1900
|
console.log(` MCP ID: ${mcp.id}`);
|
|
1901
1901
|
console.log();
|
|
1902
1902
|
console.log("Next steps:");
|
|
1903
|
-
console.log(" waniwani mcp
|
|
1903
|
+
console.log(" waniwani mcp preview # Start live preview");
|
|
1904
1904
|
console.log(" waniwani mcp status # Check status");
|
|
1905
1905
|
}
|
|
1906
1906
|
} catch (error) {
|
|
@@ -1910,7 +1910,7 @@ var useCommand = new Command19("use").description("Select an MCP to use for subs
|
|
|
1910
1910
|
});
|
|
1911
1911
|
|
|
1912
1912
|
// src/commands/mcp/index.ts
|
|
1913
|
-
var mcpCommand = new Command20("mcp").description("MCP management commands").addCommand(initCommand).addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(
|
|
1913
|
+
var mcpCommand = new Command20("mcp").description("MCP management commands").addCommand(initCommand).addCommand(listCommand2).addCommand(useCommand).addCommand(statusCommand).addCommand(previewCommand).addCommand(stopCommand).addCommand(logsCommand).addCommand(syncCommand).addCommand(publishCommand).addCommand(deleteCommand).addCommand(fileCommand).addCommand(runCommandCommand);
|
|
1914
1914
|
|
|
1915
1915
|
// src/commands/org/index.ts
|
|
1916
1916
|
import { Command as Command23 } from "commander";
|