@waniwani/cli 0.0.43 → 0.0.45
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 +307 -114
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -762,7 +762,7 @@ var loginCommand = new Command3("login").description("Log in to WaniWani").optio
|
|
|
762
762
|
|
|
763
763
|
// src/commands/logout.ts
|
|
764
764
|
import { Command as Command4 } from "commander";
|
|
765
|
-
var logoutCommand = new Command4("logout").description("Log out from WaniWani").action(async (
|
|
765
|
+
var logoutCommand = new Command4("logout").description("Log out from WaniWani").action(async (_options, command) => {
|
|
766
766
|
const globalOptions = command.optsWithGlobals();
|
|
767
767
|
const json = globalOptions.json ?? false;
|
|
768
768
|
try {
|
|
@@ -790,10 +790,10 @@ var logoutCommand = new Command4("logout").description("Log out from WaniWani").
|
|
|
790
790
|
import { Command as Command21 } from "commander";
|
|
791
791
|
|
|
792
792
|
// src/commands/mcp/clone.ts
|
|
793
|
-
import { execSync } from "child_process";
|
|
793
|
+
import { execFileSync as execFileSync2, execSync } from "child_process";
|
|
794
794
|
import { existsSync as existsSync3 } from "fs";
|
|
795
795
|
import { readFile as readFile2 } from "fs/promises";
|
|
796
|
-
import { join as
|
|
796
|
+
import { join as join4 } from "path";
|
|
797
797
|
import { Command as Command5 } from "commander";
|
|
798
798
|
import ora2 from "ora";
|
|
799
799
|
|
|
@@ -885,9 +885,132 @@ var api = {
|
|
|
885
885
|
getBaseUrl: () => config.getApiUrl()
|
|
886
886
|
};
|
|
887
887
|
|
|
888
|
+
// src/lib/git-auth.ts
|
|
889
|
+
import { execFileSync } from "child_process";
|
|
890
|
+
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
891
|
+
import { tmpdir } from "os";
|
|
892
|
+
import { join as join3 } from "path";
|
|
893
|
+
function getGitHubApiBaseUrl(remoteUrl) {
|
|
894
|
+
try {
|
|
895
|
+
const parsed = new URL(remoteUrl);
|
|
896
|
+
return parsed.hostname === "github.com" || parsed.hostname === "www.github.com" ? "https://api.github.com" : `${parsed.protocol}//${parsed.host}/api/v3`;
|
|
897
|
+
} catch {
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
function parseCloneUrlAuth(cloneUrl) {
|
|
902
|
+
try {
|
|
903
|
+
const parsed = new URL(cloneUrl);
|
|
904
|
+
const username = decodeURIComponent(parsed.username);
|
|
905
|
+
const password = decodeURIComponent(parsed.password);
|
|
906
|
+
if (username && password) {
|
|
907
|
+
parsed.username = "";
|
|
908
|
+
parsed.password = "";
|
|
909
|
+
const remoteUrl = parsed.toString();
|
|
910
|
+
return {
|
|
911
|
+
cloneUrl,
|
|
912
|
+
remoteUrl,
|
|
913
|
+
credentials: { username, password },
|
|
914
|
+
githubApiBaseUrl: getGitHubApiBaseUrl(remoteUrl)
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
} catch {
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
cloneUrl,
|
|
921
|
+
remoteUrl: cloneUrl,
|
|
922
|
+
credentials: null,
|
|
923
|
+
githubApiBaseUrl: null
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
async function getGitAuthContext(mcpId) {
|
|
927
|
+
try {
|
|
928
|
+
const gitAuth = await api.get(
|
|
929
|
+
`/api/mcp/repositories/${mcpId}/git-auth`
|
|
930
|
+
);
|
|
931
|
+
const parsedRemote = new URL(gitAuth.remoteUrl);
|
|
932
|
+
parsedRemote.username = gitAuth.username;
|
|
933
|
+
parsedRemote.password = gitAuth.token;
|
|
934
|
+
const cloneUrl = parsedRemote.toString();
|
|
935
|
+
return {
|
|
936
|
+
cloneUrl,
|
|
937
|
+
remoteUrl: gitAuth.remoteUrl,
|
|
938
|
+
credentials: {
|
|
939
|
+
username: gitAuth.username,
|
|
940
|
+
password: gitAuth.token
|
|
941
|
+
},
|
|
942
|
+
githubApiBaseUrl: getGitHubApiBaseUrl(gitAuth.remoteUrl)
|
|
943
|
+
};
|
|
944
|
+
} catch (error) {
|
|
945
|
+
if (error instanceof ApiError && error.statusCode !== 404) {
|
|
946
|
+
throw error;
|
|
947
|
+
}
|
|
948
|
+
const { cloneUrl } = await api.get(
|
|
949
|
+
`/api/mcp/repositories/${mcpId}/clone-url`
|
|
950
|
+
);
|
|
951
|
+
return parseCloneUrlAuth(cloneUrl);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
function runGitWithCredentials(args, options) {
|
|
955
|
+
const { cwd, stdio = "ignore", credentials = null } = options ?? {};
|
|
956
|
+
if (!credentials) {
|
|
957
|
+
execFileSync("git", args, { cwd, stdio });
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
const dir = mkdtempSync(join3(tmpdir(), "waniwani-askpass-"));
|
|
961
|
+
const askpassPath = join3(dir, "askpass.sh");
|
|
962
|
+
try {
|
|
963
|
+
writeFileSync(
|
|
964
|
+
askpassPath,
|
|
965
|
+
`#!/bin/sh
|
|
966
|
+
case "$1" in
|
|
967
|
+
*sername*) printf '%s\\n' "$WANIWANI_GIT_USERNAME" ;;
|
|
968
|
+
*assword*) printf '%s\\n' "$WANIWANI_GIT_PASSWORD" ;;
|
|
969
|
+
*) printf '\\n' ;;
|
|
970
|
+
esac
|
|
971
|
+
`,
|
|
972
|
+
"utf-8"
|
|
973
|
+
);
|
|
974
|
+
chmodSync(askpassPath, 448);
|
|
975
|
+
execFileSync("git", ["-c", "credential.helper=", ...args], {
|
|
976
|
+
cwd,
|
|
977
|
+
stdio,
|
|
978
|
+
env: {
|
|
979
|
+
...process.env,
|
|
980
|
+
GIT_ASKPASS: askpassPath,
|
|
981
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
982
|
+
WANIWANI_GIT_USERNAME: credentials.username,
|
|
983
|
+
WANIWANI_GIT_PASSWORD: credentials.password
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
} finally {
|
|
987
|
+
rmSync(dir, { recursive: true, force: true });
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
var REVOKE_TIMEOUT_MS = 5e3;
|
|
991
|
+
async function revokeGitHubInstallationToken(auth2) {
|
|
992
|
+
if (!auth2.credentials?.password || !auth2.githubApiBaseUrl) return;
|
|
993
|
+
const controller = new AbortController();
|
|
994
|
+
const timeout = setTimeout(() => controller.abort(), REVOKE_TIMEOUT_MS);
|
|
995
|
+
try {
|
|
996
|
+
await fetch(`${auth2.githubApiBaseUrl}/installation/token`, {
|
|
997
|
+
method: "DELETE",
|
|
998
|
+
headers: {
|
|
999
|
+
Accept: "application/vnd.github+json",
|
|
1000
|
+
Authorization: `Bearer ${auth2.credentials.password}`,
|
|
1001
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
1002
|
+
},
|
|
1003
|
+
signal: controller.signal
|
|
1004
|
+
});
|
|
1005
|
+
} catch {
|
|
1006
|
+
} finally {
|
|
1007
|
+
clearTimeout(timeout);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
888
1011
|
// src/commands/mcp/clone.ts
|
|
889
1012
|
async function loadParentConfig(cwd) {
|
|
890
|
-
const parentConfigPath =
|
|
1013
|
+
const parentConfigPath = join4(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
891
1014
|
if (!existsSync3(parentConfigPath)) {
|
|
892
1015
|
return null;
|
|
893
1016
|
}
|
|
@@ -910,92 +1033,103 @@ function checkGitInstalled() {
|
|
|
910
1033
|
);
|
|
911
1034
|
}
|
|
912
1035
|
}
|
|
913
|
-
var cloneCommand = new Command5("clone").description("Clone an existing MCP project to a local directory").argument("<name>", "Name of the MCP to clone").argument("[directory]", "Directory to clone into (defaults to MCP name)").action(
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1036
|
+
var cloneCommand = new Command5("clone").description("Clone an existing MCP project to a local directory").argument("<name>", "Name of the MCP to clone").argument("[directory]", "Directory to clone into (defaults to MCP name)").action(
|
|
1037
|
+
async (name, directory, _options, command) => {
|
|
1038
|
+
const globalOptions = command.optsWithGlobals();
|
|
1039
|
+
const json = globalOptions.json ?? false;
|
|
1040
|
+
try {
|
|
1041
|
+
const cwd = process.cwd();
|
|
1042
|
+
const dirName = directory ?? name;
|
|
1043
|
+
const projectDir = join4(cwd, dirName);
|
|
1044
|
+
if (existsSync3(projectDir)) {
|
|
1045
|
+
if (json) {
|
|
1046
|
+
formatOutput(
|
|
1047
|
+
{
|
|
1048
|
+
success: false,
|
|
1049
|
+
error: `Directory "${dirName}" already exists`
|
|
1050
|
+
},
|
|
1051
|
+
true
|
|
1052
|
+
);
|
|
1053
|
+
} else {
|
|
1054
|
+
console.error(`Error: Directory "${dirName}" already exists`);
|
|
1055
|
+
}
|
|
1056
|
+
process.exit(1);
|
|
1057
|
+
}
|
|
1058
|
+
checkGitInstalled();
|
|
1059
|
+
const spinner = ora2("Fetching MCPs...").start();
|
|
1060
|
+
const mcps = await api.get(
|
|
1061
|
+
"/api/mcp/repositories"
|
|
1062
|
+
);
|
|
1063
|
+
const mcp = mcps.find((m) => m.name === name);
|
|
1064
|
+
if (!mcp) {
|
|
1065
|
+
spinner.fail("MCP not found");
|
|
1066
|
+
throw new McpError(
|
|
1067
|
+
`MCP "${name}" not found. Run 'waniwani mcp list' to see available MCPs.`
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
spinner.text = "Cloning repository...";
|
|
1071
|
+
const gitAuth = await getGitAuthContext(mcp.id);
|
|
1072
|
+
try {
|
|
1073
|
+
runGitWithCredentials(["clone", gitAuth.remoteUrl, projectDir], {
|
|
1074
|
+
stdio: "ignore",
|
|
1075
|
+
credentials: gitAuth.credentials
|
|
1076
|
+
});
|
|
1077
|
+
} catch {
|
|
1078
|
+
spinner.fail("Failed to clone repository");
|
|
1079
|
+
throw new CLIError(
|
|
1080
|
+
"Failed to clone repository. Ensure git is configured correctly.",
|
|
1081
|
+
"CLONE_FAILED"
|
|
1082
|
+
);
|
|
1083
|
+
} finally {
|
|
1084
|
+
await revokeGitHubInstallationToken(gitAuth);
|
|
1085
|
+
}
|
|
1086
|
+
execFileSync2(
|
|
1087
|
+
"git",
|
|
1088
|
+
["remote", "set-url", "origin", mcp.githubCloneUrl],
|
|
1089
|
+
{
|
|
1090
|
+
cwd: projectDir,
|
|
1091
|
+
stdio: "ignore"
|
|
1092
|
+
}
|
|
1093
|
+
);
|
|
1094
|
+
const parentConfig = await loadParentConfig(cwd);
|
|
1095
|
+
await initConfigAt(projectDir, {
|
|
1096
|
+
...parentConfig,
|
|
1097
|
+
mcpId: mcp.id
|
|
1098
|
+
});
|
|
1099
|
+
spinner.succeed("Repository cloned");
|
|
921
1100
|
if (json) {
|
|
922
1101
|
formatOutput(
|
|
923
1102
|
{
|
|
924
|
-
success:
|
|
925
|
-
|
|
1103
|
+
success: true,
|
|
1104
|
+
projectDir,
|
|
1105
|
+
mcpId: mcp.id
|
|
926
1106
|
},
|
|
927
1107
|
true
|
|
928
1108
|
);
|
|
929
1109
|
} else {
|
|
930
|
-
console.
|
|
1110
|
+
console.log();
|
|
1111
|
+
formatSuccess(`MCP "${name}" cloned!`, false);
|
|
1112
|
+
console.log();
|
|
1113
|
+
console.log("Next steps:");
|
|
1114
|
+
console.log(` cd ${dirName}`);
|
|
1115
|
+
console.log(" waniwani mcp preview # Start developing");
|
|
931
1116
|
}
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
handleError(error, json);
|
|
932
1119
|
process.exit(1);
|
|
933
1120
|
}
|
|
934
|
-
checkGitInstalled();
|
|
935
|
-
const spinner = ora2("Fetching MCPs...").start();
|
|
936
|
-
const mcps = await api.get(
|
|
937
|
-
"/api/mcp/repositories"
|
|
938
|
-
);
|
|
939
|
-
const mcp = mcps.find((m) => m.name === name);
|
|
940
|
-
if (!mcp) {
|
|
941
|
-
spinner.fail("MCP not found");
|
|
942
|
-
throw new McpError(
|
|
943
|
-
`MCP "${name}" not found. Run 'waniwani mcp list' to see available MCPs.`
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
spinner.text = "Cloning repository...";
|
|
947
|
-
const { cloneUrl } = await api.get(
|
|
948
|
-
`/api/mcp/repositories/${mcp.id}/clone-url`
|
|
949
|
-
);
|
|
950
|
-
try {
|
|
951
|
-
execSync(`git clone "${cloneUrl}" "${projectDir}"`, {
|
|
952
|
-
stdio: "ignore"
|
|
953
|
-
});
|
|
954
|
-
} catch {
|
|
955
|
-
spinner.fail("Failed to clone repository");
|
|
956
|
-
throw new CLIError(
|
|
957
|
-
"Failed to clone repository. Ensure git is configured correctly.",
|
|
958
|
-
"CLONE_FAILED"
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
const parentConfig = await loadParentConfig(cwd);
|
|
962
|
-
await initConfigAt(projectDir, {
|
|
963
|
-
...parentConfig,
|
|
964
|
-
mcpId: mcp.id
|
|
965
|
-
});
|
|
966
|
-
spinner.succeed("Repository cloned");
|
|
967
|
-
if (json) {
|
|
968
|
-
formatOutput(
|
|
969
|
-
{
|
|
970
|
-
success: true,
|
|
971
|
-
projectDir,
|
|
972
|
-
mcpId: mcp.id
|
|
973
|
-
},
|
|
974
|
-
true
|
|
975
|
-
);
|
|
976
|
-
} else {
|
|
977
|
-
console.log();
|
|
978
|
-
formatSuccess(`MCP "${name}" cloned!`, false);
|
|
979
|
-
console.log();
|
|
980
|
-
console.log("Next steps:");
|
|
981
|
-
console.log(` cd ${dirName}`);
|
|
982
|
-
console.log(" waniwani mcp preview # Start developing");
|
|
983
|
-
}
|
|
984
|
-
} catch (error) {
|
|
985
|
-
handleError(error, json);
|
|
986
|
-
process.exit(1);
|
|
987
1121
|
}
|
|
988
|
-
|
|
1122
|
+
);
|
|
989
1123
|
|
|
990
1124
|
// src/commands/mcp/create.ts
|
|
991
|
-
import { execSync as execSync2 } from "child_process";
|
|
1125
|
+
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
992
1126
|
import { existsSync as existsSync4 } from "fs";
|
|
993
1127
|
import { readFile as readFile3 } from "fs/promises";
|
|
994
|
-
import { join as
|
|
1128
|
+
import { join as join5 } from "path";
|
|
995
1129
|
import { Command as Command6 } from "commander";
|
|
996
1130
|
import ora3 from "ora";
|
|
997
1131
|
async function loadParentConfig2(cwd) {
|
|
998
|
-
const parentConfigPath =
|
|
1132
|
+
const parentConfigPath = join5(cwd, LOCAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
999
1133
|
if (!existsSync4(parentConfigPath)) {
|
|
1000
1134
|
return null;
|
|
1001
1135
|
}
|
|
@@ -1023,7 +1157,7 @@ var createCommand = new Command6("create").description("Create a new MCP project
|
|
|
1023
1157
|
const json = globalOptions.json ?? false;
|
|
1024
1158
|
try {
|
|
1025
1159
|
const cwd = process.cwd();
|
|
1026
|
-
const projectDir =
|
|
1160
|
+
const projectDir = join5(cwd, name);
|
|
1027
1161
|
if (existsSync4(projectDir)) {
|
|
1028
1162
|
if (json) {
|
|
1029
1163
|
formatOutput(
|
|
@@ -1044,12 +1178,11 @@ var createCommand = new Command6("create").description("Create a new MCP project
|
|
|
1044
1178
|
name
|
|
1045
1179
|
});
|
|
1046
1180
|
spinner.text = "Cloning repository...";
|
|
1047
|
-
const
|
|
1048
|
-
`/api/mcp/repositories/${result.id}/clone-url`
|
|
1049
|
-
);
|
|
1181
|
+
const gitAuth = await getGitAuthContext(result.id);
|
|
1050
1182
|
try {
|
|
1051
|
-
|
|
1052
|
-
stdio: "ignore"
|
|
1183
|
+
runGitWithCredentials(["clone", gitAuth.remoteUrl, projectDir], {
|
|
1184
|
+
stdio: "ignore",
|
|
1185
|
+
credentials: gitAuth.credentials
|
|
1053
1186
|
});
|
|
1054
1187
|
} catch {
|
|
1055
1188
|
spinner.fail("Failed to clone repository");
|
|
@@ -1057,7 +1190,17 @@ var createCommand = new Command6("create").description("Create a new MCP project
|
|
|
1057
1190
|
`Failed to clone repository. Ensure git is configured correctly.`,
|
|
1058
1191
|
"CLONE_FAILED"
|
|
1059
1192
|
);
|
|
1193
|
+
} finally {
|
|
1194
|
+
await revokeGitHubInstallationToken(gitAuth);
|
|
1060
1195
|
}
|
|
1196
|
+
execFileSync3(
|
|
1197
|
+
"git",
|
|
1198
|
+
["remote", "set-url", "origin", result.githubCloneUrl],
|
|
1199
|
+
{
|
|
1200
|
+
cwd: projectDir,
|
|
1201
|
+
stdio: "ignore"
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1061
1204
|
const parentConfig = await loadParentConfig2(cwd);
|
|
1062
1205
|
await initConfigAt(projectDir, {
|
|
1063
1206
|
...parentConfig,
|
|
@@ -1340,7 +1483,7 @@ var fileCommand = new Command11("file").description("File operations in MCP sand
|
|
|
1340
1483
|
import chalk6 from "chalk";
|
|
1341
1484
|
import { Command as Command12 } from "commander";
|
|
1342
1485
|
import ora8 from "ora";
|
|
1343
|
-
var listCommand2 = new Command12("list").description("List all MCPs in your organization").action(async (
|
|
1486
|
+
var listCommand2 = new Command12("list").description("List all MCPs in your organization").action(async (_options, command) => {
|
|
1344
1487
|
const globalOptions = command.optsWithGlobals();
|
|
1345
1488
|
const json = globalOptions.json ?? false;
|
|
1346
1489
|
try {
|
|
@@ -1544,24 +1687,44 @@ import { watch } from "chokidar";
|
|
|
1544
1687
|
import { Command as Command14 } from "commander";
|
|
1545
1688
|
import ora10 from "ora";
|
|
1546
1689
|
|
|
1690
|
+
// src/lib/async.ts
|
|
1691
|
+
async function withTimeout(promise, timeoutMs, options) {
|
|
1692
|
+
let timer;
|
|
1693
|
+
try {
|
|
1694
|
+
return await Promise.race([
|
|
1695
|
+
promise,
|
|
1696
|
+
new Promise((resolve) => {
|
|
1697
|
+
timer = setTimeout(() => {
|
|
1698
|
+
options?.onTimeout?.();
|
|
1699
|
+
resolve(null);
|
|
1700
|
+
}, timeoutMs);
|
|
1701
|
+
})
|
|
1702
|
+
]);
|
|
1703
|
+
} finally {
|
|
1704
|
+
if (timer) {
|
|
1705
|
+
clearTimeout(timer);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1547
1710
|
// src/lib/sync.ts
|
|
1548
1711
|
import { existsSync as existsSync5 } from "fs";
|
|
1549
1712
|
import { mkdir as mkdir2, readdir, readFile as readFile5, stat, writeFile as writeFile3 } from "fs/promises";
|
|
1550
|
-
import { dirname, join as
|
|
1713
|
+
import { dirname, join as join6, relative } from "path";
|
|
1551
1714
|
import ignore from "ignore";
|
|
1552
1715
|
var PROJECT_DIR = ".waniwani";
|
|
1553
1716
|
async function findProjectRoot(startDir) {
|
|
1554
1717
|
let current = startDir;
|
|
1555
1718
|
const root = dirname(current);
|
|
1556
1719
|
while (current !== root) {
|
|
1557
|
-
if (existsSync5(
|
|
1720
|
+
if (existsSync5(join6(current, PROJECT_DIR))) {
|
|
1558
1721
|
return current;
|
|
1559
1722
|
}
|
|
1560
1723
|
const parent = dirname(current);
|
|
1561
1724
|
if (parent === current) break;
|
|
1562
1725
|
current = parent;
|
|
1563
1726
|
}
|
|
1564
|
-
if (existsSync5(
|
|
1727
|
+
if (existsSync5(join6(current, PROJECT_DIR))) {
|
|
1565
1728
|
return current;
|
|
1566
1729
|
}
|
|
1567
1730
|
return null;
|
|
@@ -1585,7 +1748,7 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
1585
1748
|
async function loadIgnorePatterns(projectRoot) {
|
|
1586
1749
|
const ig = ignore();
|
|
1587
1750
|
ig.add(DEFAULT_IGNORE_PATTERNS);
|
|
1588
|
-
const gitignorePath =
|
|
1751
|
+
const gitignorePath = join6(projectRoot, ".gitignore");
|
|
1589
1752
|
if (existsSync5(gitignorePath)) {
|
|
1590
1753
|
try {
|
|
1591
1754
|
const content = await readFile5(gitignorePath, "utf-8");
|
|
@@ -1601,7 +1764,7 @@ async function collectFiles(projectRoot) {
|
|
|
1601
1764
|
async function walk(dir) {
|
|
1602
1765
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1603
1766
|
for (const entry of entries) {
|
|
1604
|
-
const fullPath =
|
|
1767
|
+
const fullPath = join6(dir, entry.name);
|
|
1605
1768
|
const relativePath = relative(projectRoot, fullPath);
|
|
1606
1769
|
if (ig.ignores(relativePath)) {
|
|
1607
1770
|
continue;
|
|
@@ -1631,7 +1794,7 @@ async function pullFilesFromGithub(mcpId, targetDir) {
|
|
|
1631
1794
|
);
|
|
1632
1795
|
const writtenFiles = [];
|
|
1633
1796
|
for (const file of result.files) {
|
|
1634
|
-
const localPath =
|
|
1797
|
+
const localPath = join6(targetDir, file.path);
|
|
1635
1798
|
const dir = dirname(localPath);
|
|
1636
1799
|
await mkdir2(dir, { recursive: true });
|
|
1637
1800
|
if (file.encoding === "base64") {
|
|
@@ -1644,7 +1807,7 @@ async function pullFilesFromGithub(mcpId, targetDir) {
|
|
|
1644
1807
|
return { count: writtenFiles.length, files: writtenFiles };
|
|
1645
1808
|
}
|
|
1646
1809
|
async function collectSingleFile(projectRoot, filePath) {
|
|
1647
|
-
const fullPath =
|
|
1810
|
+
const fullPath = join6(projectRoot, filePath);
|
|
1648
1811
|
const relativePath = relative(projectRoot, fullPath);
|
|
1649
1812
|
if (!existsSync5(fullPath)) {
|
|
1650
1813
|
return null;
|
|
@@ -1667,6 +1830,8 @@ async function collectSingleFile(projectRoot, filePath) {
|
|
|
1667
1830
|
}
|
|
1668
1831
|
|
|
1669
1832
|
// src/commands/mcp/preview.ts
|
|
1833
|
+
var SHUTDOWN_MAX_WAIT_MS = 3e3;
|
|
1834
|
+
var SHUTDOWN_STEP_TIMEOUT_MS = 1200;
|
|
1670
1835
|
var previewCommand = new Command14("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) => {
|
|
1671
1836
|
const globalOptions = command.optsWithGlobals();
|
|
1672
1837
|
const json = globalOptions.json ?? false;
|
|
@@ -1767,11 +1932,51 @@ var previewCommand = new Command14("preview").description("Start live developmen
|
|
|
1767
1932
|
const relativePath = filePath.replace(`${projectRoot}/`, "");
|
|
1768
1933
|
console.log(` Deleted: ${relativePath}`);
|
|
1769
1934
|
});
|
|
1770
|
-
|
|
1935
|
+
const stopSessionBestEffort = async () => {
|
|
1936
|
+
await withTimeout(
|
|
1937
|
+
api.post(`/api/mcp/sessions/${sessionId}/server`, { action: "stop" }).catch(() => void 0),
|
|
1938
|
+
SHUTDOWN_STEP_TIMEOUT_MS
|
|
1939
|
+
);
|
|
1940
|
+
await withTimeout(
|
|
1941
|
+
api.delete(`/api/mcp/sessions/${sessionId}`).catch(() => void 0),
|
|
1942
|
+
SHUTDOWN_STEP_TIMEOUT_MS
|
|
1943
|
+
);
|
|
1944
|
+
await withTimeout(
|
|
1945
|
+
config.setSessionId(null).catch(() => void 0),
|
|
1946
|
+
SHUTDOWN_STEP_TIMEOUT_MS
|
|
1947
|
+
);
|
|
1948
|
+
};
|
|
1949
|
+
let shuttingDown = false;
|
|
1950
|
+
const gracefulShutdown = async () => {
|
|
1951
|
+
if (shuttingDown) return;
|
|
1952
|
+
shuttingDown = true;
|
|
1771
1953
|
console.log();
|
|
1772
1954
|
console.log("Stopping development environment...");
|
|
1773
|
-
|
|
1774
|
-
|
|
1955
|
+
try {
|
|
1956
|
+
await withTimeout(
|
|
1957
|
+
(async () => {
|
|
1958
|
+
await withTimeout(
|
|
1959
|
+
watcher.close().catch(() => void 0),
|
|
1960
|
+
SHUTDOWN_STEP_TIMEOUT_MS
|
|
1961
|
+
);
|
|
1962
|
+
await stopSessionBestEffort();
|
|
1963
|
+
})(),
|
|
1964
|
+
SHUTDOWN_MAX_WAIT_MS,
|
|
1965
|
+
{
|
|
1966
|
+
onTimeout: () => {
|
|
1967
|
+
console.log("Shutdown timed out, forcing exit.");
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
} finally {
|
|
1972
|
+
process.exit(0);
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
process.once("SIGINT", () => {
|
|
1976
|
+
void gracefulShutdown();
|
|
1977
|
+
});
|
|
1978
|
+
process.once("SIGTERM", () => {
|
|
1979
|
+
void gracefulShutdown();
|
|
1775
1980
|
});
|
|
1776
1981
|
await new Promise(() => {
|
|
1777
1982
|
});
|
|
@@ -1783,7 +1988,7 @@ var previewCommand = new Command14("preview").description("Start live developmen
|
|
|
1783
1988
|
});
|
|
1784
1989
|
|
|
1785
1990
|
// src/commands/mcp/publish.ts
|
|
1786
|
-
import {
|
|
1991
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1787
1992
|
import { input } from "@inquirer/prompts";
|
|
1788
1993
|
import { Command as Command15 } from "commander";
|
|
1789
1994
|
import ora11 from "ora";
|
|
@@ -1800,7 +2005,7 @@ var publishCommand = new Command15("publish").description("Push local files to G
|
|
|
1800
2005
|
);
|
|
1801
2006
|
}
|
|
1802
2007
|
try {
|
|
1803
|
-
|
|
2008
|
+
execFileSync4("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1804
2009
|
cwd: projectRoot,
|
|
1805
2010
|
stdio: "ignore"
|
|
1806
2011
|
});
|
|
@@ -1810,7 +2015,7 @@ var publishCommand = new Command15("publish").description("Push local files to G
|
|
|
1810
2015
|
"NOT_GIT_REPO"
|
|
1811
2016
|
);
|
|
1812
2017
|
}
|
|
1813
|
-
const status =
|
|
2018
|
+
const status = execFileSync4("git", ["status", "--porcelain"], {
|
|
1814
2019
|
cwd: projectRoot,
|
|
1815
2020
|
encoding: "utf-8"
|
|
1816
2021
|
}).trim();
|
|
@@ -1830,36 +2035,24 @@ var publishCommand = new Command15("publish").description("Push local files to G
|
|
|
1830
2035
|
});
|
|
1831
2036
|
}
|
|
1832
2037
|
const spinner = ora11("Publishing...").start();
|
|
1833
|
-
const
|
|
1834
|
-
`/api/mcp/repositories/${mcpId}/clone-url`
|
|
1835
|
-
);
|
|
2038
|
+
const gitAuth = await getGitAuthContext(mcpId);
|
|
1836
2039
|
spinner.text = "Committing changes...";
|
|
1837
|
-
|
|
1838
|
-
|
|
2040
|
+
execFileSync4("git", ["add", "-A"], { cwd: projectRoot, stdio: "ignore" });
|
|
2041
|
+
execFileSync4("git", ["commit", "-m", message], {
|
|
1839
2042
|
cwd: projectRoot,
|
|
1840
2043
|
stdio: "ignore"
|
|
1841
2044
|
});
|
|
1842
2045
|
spinner.text = "Pushing to GitHub...";
|
|
1843
|
-
const originalUrl = execSync3("git remote get-url origin", {
|
|
1844
|
-
cwd: projectRoot,
|
|
1845
|
-
encoding: "utf-8"
|
|
1846
|
-
}).trim();
|
|
1847
2046
|
try {
|
|
1848
|
-
|
|
2047
|
+
runGitWithCredentials(["push", gitAuth.remoteUrl, "HEAD"], {
|
|
1849
2048
|
cwd: projectRoot,
|
|
1850
|
-
stdio: "ignore"
|
|
1851
|
-
|
|
1852
|
-
execSync3("git push origin HEAD", {
|
|
1853
|
-
cwd: projectRoot,
|
|
1854
|
-
stdio: "ignore"
|
|
2049
|
+
stdio: "ignore",
|
|
2050
|
+
credentials: gitAuth.credentials
|
|
1855
2051
|
});
|
|
1856
2052
|
} finally {
|
|
1857
|
-
|
|
1858
|
-
cwd: projectRoot,
|
|
1859
|
-
stdio: "ignore"
|
|
1860
|
-
});
|
|
2053
|
+
await revokeGitHubInstallationToken(gitAuth);
|
|
1861
2054
|
}
|
|
1862
|
-
const commitSha =
|
|
2055
|
+
const commitSha = execFileSync4("git", ["rev-parse", "HEAD"], {
|
|
1863
2056
|
cwd: projectRoot,
|
|
1864
2057
|
encoding: "utf-8"
|
|
1865
2058
|
}).trim();
|
|
@@ -2129,7 +2322,7 @@ import { Command as Command24 } from "commander";
|
|
|
2129
2322
|
import chalk10 from "chalk";
|
|
2130
2323
|
import { Command as Command22 } from "commander";
|
|
2131
2324
|
import ora17 from "ora";
|
|
2132
|
-
var listCommand3 = new Command22("list").description("List your organizations").action(async (
|
|
2325
|
+
var listCommand3 = new Command22("list").description("List your organizations").action(async (_options, command) => {
|
|
2133
2326
|
const globalOptions = command.optsWithGlobals();
|
|
2134
2327
|
const json = globalOptions.json ?? false;
|
|
2135
2328
|
try {
|
|
@@ -2181,7 +2374,7 @@ var listCommand3 = new Command22("list").description("List your organizations").
|
|
|
2181
2374
|
// src/commands/org/switch.ts
|
|
2182
2375
|
import { Command as Command23 } from "commander";
|
|
2183
2376
|
import ora18 from "ora";
|
|
2184
|
-
var switchCommand = new Command23("switch").description("Switch to a different organization").argument("<name>", "Name or slug of the organization to switch to").action(async (name,
|
|
2377
|
+
var switchCommand = new Command23("switch").description("Switch to a different organization").argument("<name>", "Name or slug of the organization to switch to").action(async (name, _options, command) => {
|
|
2185
2378
|
const globalOptions = command.optsWithGlobals();
|
|
2186
2379
|
const json = globalOptions.json ?? false;
|
|
2187
2380
|
try {
|